diff options
Diffstat (limited to 'drivers/usb')
344 files changed, 6797 insertions, 2793 deletions
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 0dd85d2635b9..c6b9ad12e8fe 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -597,7 +597,7 @@ static int cxacru_start_wait_urb(struct urb *urb, struct completion *done, timer_setup_on_stack(&timer.timer, cxacru_timeout_kill, 0); mod_timer(&timer.timer, jiffies + msecs_to_jiffies(CMD_TIMEOUT)); wait_for_completion(done); - del_timer_sync(&timer.timer); + timer_delete_sync(&timer.timer); destroy_timer_on_stack(&timer.timer); if (actual_length) @@ -1131,7 +1131,10 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, struct cxacru_data *instance; struct usb_device *usb_dev = interface_to_usbdev(intf); struct usb_host_endpoint *cmd_ep = usb_dev->ep_in[CXACRU_EP_CMD]; - struct usb_endpoint_descriptor *in, *out; + static const u8 ep_addrs[] = { + CXACRU_EP_CMD + USB_DIR_IN, + CXACRU_EP_CMD + USB_DIR_OUT, + 0}; int ret; /* instance init */ @@ -1179,13 +1182,11 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, } if (usb_endpoint_xfer_int(&cmd_ep->desc)) - ret = usb_find_common_endpoints(intf->cur_altsetting, - NULL, NULL, &in, &out); + ret = usb_check_int_endpoints(intf, ep_addrs); else - ret = usb_find_common_endpoints(intf->cur_altsetting, - &in, &out, NULL, NULL); + ret = usb_check_bulk_endpoints(intf, ep_addrs); - if (ret) { + if (!ret) { usb_err(usbatm_instance, "cxacru_bind: interface has incorrect endpoints\n"); ret = -ENODEV; goto fail; diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 973548b5c15c..27e3d35ee7dd 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -612,7 +612,7 @@ static void speedtch_handle_int(struct urb *int_urb) } if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) { - del_timer(&instance->status_check_timer); + timer_delete(&instance->status_check_timer); atm_info(usbatm, "DSL line goes up\n"); } else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) { atm_info(usbatm, "DSL line goes down\n"); @@ -688,7 +688,7 @@ static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_de atm_dbg(usbatm, "%s entered\n", __func__); - del_timer_sync(&instance->status_check_timer); + timer_delete_sync(&instance->status_check_timer); /* * Since resubmit_timer and int_urb can schedule themselves and @@ -697,14 +697,14 @@ static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_de instance->int_urb = NULL; /* signal shutdown */ mb(); usb_kill_urb(int_urb); - del_timer_sync(&instance->resubmit_timer); + timer_delete_sync(&instance->resubmit_timer); /* * At this point, speedtch_handle_int and speedtch_resubmit_int * can run or be running, but instance->int_urb == NULL means that * they will not reschedule */ usb_kill_urb(int_urb); - del_timer_sync(&instance->resubmit_timer); + timer_delete_sync(&instance->resubmit_timer); usb_free_urb(int_urb); flush_work(&instance->status_check_work); diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index e8e43c38aa1b..cd0f7b4bd82a 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -808,7 +808,7 @@ static int check_dsp_e4(const u8 *dsp, int len) if (l > len) return 1; - /* zero is zero regardless endianes */ + /* zero is zero regardless endianness */ } while (blockidx->NotLastBlock); } @@ -1276,7 +1276,7 @@ static void uea_set_bulk_timeout(struct uea_softc *sc, u32 dsrate) sc->stats.phy.dsrate == dsrate) return; - /* Original timming (1Mbit/s) from ADI (used in windows driver) */ + /* Original timing (1Mbit/s) from ADI (used in windows driver) */ timeout = (dsrate <= 1024*1024) ? 0 : 1; ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL); uea_info(INS_TO_USBDEV(sc), "setting new timeout %d%s\n", @@ -1972,7 +1972,7 @@ static void uea_dispatch_cmv_e1(struct uea_softc *sc, struct intr_pkt *intr) if (cmv->bDirection != E1_MODEMTOHOST) goto bad1; - /* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to + /* FIXME : ADI930 reply wrong preamble (func = 2, sub = 2) to * the first MEMACCESS cmv. Ignore it... */ if (cmv->bFunction != dsc->function) { diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 2da6615fbb6f..a6a05e85ef8c 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -1158,7 +1158,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, if (i >= num_rcv_urbs) list_add_tail(&urb->urb_list, &channel->list); - vdbg(&intf->dev, "%s: alloced buffer 0x%p buf size %u urb 0x%p", + vdbg(&intf->dev, "%s: allocated buffer 0x%p buf size %u urb 0x%p", __func__, urb->transfer_buffer, urb->transfer_buffer_length, urb); } @@ -1237,8 +1237,8 @@ void usbatm_usb_disconnect(struct usb_interface *intf) for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++) usb_kill_urb(instance->urbs[i]); - del_timer_sync(&instance->rx_channel.delay); - del_timer_sync(&instance->tx_channel.delay); + timer_delete_sync(&instance->rx_channel.delay); + timer_delete_sync(&instance->tx_channel.delay); /* turn usbatm_[rt]x_process into something close to a no-op */ /* no need to take the spinlock */ diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c index bb9d5d7ffefc..8f38e2c5369a 100644 --- a/drivers/usb/c67x00/c67x00-drv.c +++ b/drivers/usb/c67x00/c67x00-drv.c @@ -201,7 +201,7 @@ static void c67x00_drv_remove(struct platform_device *pdev) static struct platform_driver c67x00_driver = { .probe = c67x00_drv_probe, - .remove_new = c67x00_drv_remove, + .remove = c67x00_drv_remove, .driver = { .name = "c67x00", }, diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index fd1beb10bba7..694aa1457739 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -3468,7 +3468,7 @@ __must_hold(&cdns->lock) return 0; } -static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated) +static int cdns3_gadget_resume(struct cdns *cdns, bool lost_power) { struct cdns3_device *priv_dev = cdns->gadget_dev; @@ -3476,7 +3476,7 @@ static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated) return 0; cdns3_gadget_config(priv_dev); - if (hibernated) + if (lost_power) writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf); return 0; diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 281de47e2a3b..a2f041e1707c 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -422,7 +422,7 @@ MODULE_DEVICE_TABLE(of, cdns_imx_of_match); static struct platform_driver cdns_imx_driver = { .probe = cdns_imx_probe, - .remove_new = cdns_imx_remove, + .remove = cdns_imx_remove, .driver = { .name = "cdns3-imx", .of_match_table = cdns_imx_of_match, diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c index 591d149de8f3..3b3b3dc75f35 100644 --- a/drivers/usb/cdns3/cdns3-pci-wrap.c +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c @@ -37,8 +37,6 @@ struct cdns3_wrap { #define PCI_DRIVER_NAME "cdns3-pci-usbss" #define PLAT_DRIVER_NAME "cdns-usb3" -#define PCI_DEVICE_ID_CDNS_USB3 0x0100 - static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev) { struct pci_dev *func; @@ -189,7 +187,7 @@ static void cdns3_pci_remove(struct pci_dev *pdev) } static const struct pci_device_id cdns3_pci_ids[] = { - { PCI_VDEVICE(CDNS, PCI_DEVICE_ID_CDNS_USB3) }, + { PCI_VDEVICE(CDNS, PCI_DEVICE_ID_CDNS_USBSS) }, { 0, } }; diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 3ef8e3c872a3..59ec505e198a 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -327,7 +327,7 @@ MODULE_DEVICE_TABLE(of, of_cdns3_match); static struct platform_driver cdns3_driver = { .probe = cdns3_plat_probe, - .remove_new = cdns3_plat_remove, + .remove = cdns3_plat_remove, .driver = { .name = "cdns-usb3", .of_match_table = of_match_ptr(of_cdns3_match), diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c index c04d196abd87..2ff7f2b48cc2 100644 --- a/drivers/usb/cdns3/cdns3-starfive.c +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -230,7 +230,7 @@ MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); static struct platform_driver cdns_starfive_driver = { .probe = cdns_starfive_probe, - .remove_new = cdns_starfive_remove, + .remove = cdns_starfive_remove, .driver = { .name = "cdns3-starfive", .of_match_table = cdns_starfive_of_match, diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index cfabc12ee0e3..302ebf6d8e53 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -58,6 +58,7 @@ struct cdns_ti { unsigned vbus_divider:1; struct clk *usb2_refclk; struct clk *lpm_clk; + int usb2_refclk_rate_code; }; static const int cdns_ti_rate_table[] = { /* in KHZ */ @@ -98,15 +99,50 @@ static const struct of_dev_auxdata cdns_ti_auxdata[] = { {}, }; +static void cdns_ti_reset_and_init_hw(struct cdns_ti *data) +{ + u32 reg; + + /* assert RESET */ + reg = cdns_ti_readl(data, USBSS_W1); + reg &= ~USBSS_W1_PWRUP_RST; + cdns_ti_writel(data, USBSS_W1, reg); + + /* set static config */ + reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG); + reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK; + reg |= data->usb2_refclk_rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT; + + reg &= ~USBSS1_STATIC_VBUS_SEL_MASK; + if (data->vbus_divider) + reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT; + + cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg); + reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG); + + /* set USB2_ONLY mode if requested */ + reg = cdns_ti_readl(data, USBSS_W1); + if (data->usb2_only) + reg |= USBSS_W1_USB2_ONLY; + + /* set default modestrap */ + reg |= USBSS_W1_MODESTRAP_SEL; + reg &= ~USBSS_W1_MODESTRAP_MASK; + reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT; + cdns_ti_writel(data, USBSS_W1, reg); + + /* de-assert RESET */ + reg |= USBSS_W1_PWRUP_RST; + cdns_ti_writel(data, USBSS_W1, reg); +} + static int cdns_ti_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = pdev->dev.of_node; struct cdns_ti *data; - int error; - u32 reg; - int rate_code, i; unsigned long rate; + int error, i; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -146,7 +182,17 @@ static int cdns_ti_probe(struct platform_device *pdev) return -EINVAL; } - rate_code = i; + data->usb2_refclk_rate_code = i; + data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); + data->usb2_only = device_property_read_bool(dev, "ti,usb2-only"); + + /* + * The call below to pm_runtime_get_sync() MIGHT reset hardware, if it + * detects it as uninitialised. We want to enforce a reset at probe, + * and so do it manually here. This means the first runtime_resume() + * will be a no-op. + */ + cdns_ti_reset_and_init_hw(data); pm_runtime_enable(dev); error = pm_runtime_get_sync(dev); @@ -155,40 +201,6 @@ static int cdns_ti_probe(struct platform_device *pdev) goto err; } - /* assert RESET */ - reg = cdns_ti_readl(data, USBSS_W1); - reg &= ~USBSS_W1_PWRUP_RST; - cdns_ti_writel(data, USBSS_W1, reg); - - /* set static config */ - reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG); - reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK; - reg |= rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT; - - reg &= ~USBSS1_STATIC_VBUS_SEL_MASK; - data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); - if (data->vbus_divider) - reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT; - - cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg); - reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG); - - /* set USB2_ONLY mode if requested */ - reg = cdns_ti_readl(data, USBSS_W1); - data->usb2_only = device_property_read_bool(dev, "ti,usb2-only"); - if (data->usb2_only) - reg |= USBSS_W1_USB2_ONLY; - - /* set default modestrap */ - reg |= USBSS_W1_MODESTRAP_SEL; - reg &= ~USBSS_W1_MODESTRAP_MASK; - reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT; - cdns_ti_writel(data, USBSS_W1, reg); - - /* de-assert RESET */ - reg |= USBSS_W1_PWRUP_RST; - cdns_ti_writel(data, USBSS_W1, reg); - error = of_platform_populate(node, NULL, cdns_ti_auxdata, dev); if (error) { dev_err(dev, "failed to create children: %d\n", error); @@ -224,6 +236,24 @@ static void cdns_ti_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); } +static int cdns_ti_runtime_resume(struct device *dev) +{ + const u32 mask = USBSS_W1_PWRUP_RST | USBSS_W1_MODESTRAP_SEL; + struct cdns_ti *data = dev_get_drvdata(dev); + u32 w1; + + w1 = cdns_ti_readl(data, USBSS_W1); + if ((w1 & mask) != mask) + cdns_ti_reset_and_init_hw(data); + + return 0; +} + +static const struct dev_pm_ops cdns_ti_pm_ops = { + RUNTIME_PM_OPS(NULL, cdns_ti_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + static const struct of_device_id cdns_ti_of_match[] = { { .compatible = "ti,j721e-usb", }, { .compatible = "ti,am64-usb", }, @@ -233,10 +263,11 @@ MODULE_DEVICE_TABLE(of, cdns_ti_of_match); static struct platform_driver cdns_ti_driver = { .probe = cdns_ti_probe, - .remove_new = cdns_ti_remove, + .remove = cdns_ti_remove, .driver = { .name = "cdns3-ti", .of_match_table = cdns_ti_of_match, + .pm = pm_ptr(&cdns_ti_pm_ops), }, }; diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 4a3f0f958256..87f310841735 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <linux/log2.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/pci.h> #include <linux/irq.h> #include <linux/dmi.h> @@ -1671,12 +1672,12 @@ static int cdnsp_gadget_init_endpoints(struct cdnsp_device *pdev) "CTRL: %s, INT: %s, BULK: %s, ISOC %s, " "SupDir IN: %s, OUT: %s\n", pep->name, 1024, - (pep->endpoint.caps.type_control) ? "yes" : "no", - (pep->endpoint.caps.type_int) ? "yes" : "no", - (pep->endpoint.caps.type_bulk) ? "yes" : "no", - (pep->endpoint.caps.type_iso) ? "yes" : "no", - (pep->endpoint.caps.dir_in) ? "yes" : "no", - (pep->endpoint.caps.dir_out) ? "yes" : "no"); + str_yes_no(pep->endpoint.caps.type_control), + str_yes_no(pep->endpoint.caps.type_int), + str_yes_no(pep->endpoint.caps.type_bulk), + str_yes_no(pep->endpoint.caps.type_iso), + str_yes_no(pep->endpoint.caps.dir_in), + str_yes_no(pep->endpoint.caps.dir_out)); INIT_LIST_HEAD(&pep->pending_list); } @@ -1973,7 +1974,7 @@ static int cdnsp_gadget_suspend(struct cdns *cdns, bool do_wakeup) return 0; } -static int cdnsp_gadget_resume(struct cdns *cdns, bool hibernated) +static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power) { struct cdnsp_device *pdev = cdns->gadget_dev; enum usb_device_speed max_speed; diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c index 2d05368a6745..a51144504ff3 100644 --- a/drivers/usb/cdns3/cdnsp-pci.c +++ b/drivers/usb/cdns3/cdnsp-pci.c @@ -28,12 +28,6 @@ #define PCI_DRIVER_NAME "cdns-pci-usbssp" #define PLAT_DRIVER_NAME "cdns-usbssp" -#define PCI_DEVICE_ID_CDNS_USB3 0x0100 -#define PCI_DEVICE_ID_CDNS_UDC 0x0200 - -#define PCI_CLASS_SERIAL_USB_CDNS_USB3 (PCI_CLASS_SERIAL_USB << 8 | 0x80) -#define PCI_CLASS_SERIAL_USB_CDNS_UDC PCI_CLASS_SERIAL_USB_DEVICE - static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) { /* @@ -41,10 +35,10 @@ static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) * Platform has two function. The fist keeps resources for * Host/Device while the secon keeps resources for DRD/OTG. */ - if (pdev->device == PCI_DEVICE_ID_CDNS_UDC) - return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USB3, NULL); - if (pdev->device == PCI_DEVICE_ID_CDNS_USB3) - return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_UDC, NULL); + if (pdev->device == PCI_DEVICE_ID_CDNS_USBSSP) + return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USBSS, NULL); + if (pdev->device == PCI_DEVICE_ID_CDNS_USBSS) + return pci_get_device(pdev->vendor, PCI_DEVICE_ID_CDNS_USBSSP, NULL); return NULL; } @@ -221,12 +215,12 @@ static const struct dev_pm_ops cdnsp_pci_pm_ops = { }; static const struct pci_device_id cdnsp_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC), - .class = PCI_CLASS_SERIAL_USB_CDNS_UDC }, - { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_UDC), - .class = PCI_CLASS_SERIAL_USB_CDNS_USB3 }, - { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB3), - .class = PCI_CLASS_SERIAL_USB_CDNS_USB3 }, + { 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), + .class = PCI_CLASS_SERIAL_USB_CDNS }, + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USBSS), + .class = PCI_CLASS_SERIAL_USB_CDNS }, { 0, } }; diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 465e9267b49c..1243a5cea91b 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -524,14 +524,13 @@ EXPORT_SYMBOL_GPL(cdns_suspend); int cdns_resume(struct cdns *cdns) { + bool power_lost = cdns_power_is_lost(cdns); enum usb_role real_role; bool role_changed = false; int ret = 0; - if (cdns_power_is_lost(cdns)) { - if (cdns->role_sw) { - cdns->role = cdns_role_get(cdns->role_sw); - } else { + if (power_lost) { + if (!cdns->role_sw) { real_role = cdns_hw_role_state_machine(cdns); if (real_role != cdns->role) { ret = cdns_hw_role_switch(cdns); @@ -553,7 +552,7 @@ int cdns_resume(struct cdns *cdns) } if (cdns->roles[cdns->role]->resume) - cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns)); + cdns->roles[cdns->role]->resume(cdns, power_lost); return 0; } diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h index 57d47348dc19..921cccf1ca9d 100644 --- a/drivers/usb/cdns3/core.h +++ b/drivers/usb/cdns3/core.h @@ -30,7 +30,7 @@ struct cdns_role_driver { int (*start)(struct cdns *cdns); void (*stop)(struct cdns *cdns); int (*suspend)(struct cdns *cdns, bool do_wakeup); - int (*resume)(struct cdns *cdns, bool hibernated); + int (*resume)(struct cdns *cdns, bool lost_power); const char *name; #define CDNS_ROLE_STATE_INACTIVE 0 #define CDNS_ROLE_STATE_ACTIVE 1 diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c index 7ba760ee62e3..f0df114c2b53 100644 --- a/drivers/usb/cdns3/host.c +++ b/drivers/usb/cdns3/host.c @@ -138,6 +138,16 @@ static void cdns_host_exit(struct cdns *cdns) cdns_drd_host_off(cdns); } +static int cdns_host_resume(struct cdns *cdns, bool power_lost) +{ + struct usb_hcd *hcd = platform_get_drvdata(cdns->host_dev); + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); + + priv->power_lost = power_lost; + + return 0; +} + int cdns_host_init(struct cdns *cdns) { struct cdns_role_driver *rdrv; @@ -148,6 +158,7 @@ int cdns_host_init(struct cdns *cdns) rdrv->start = __cdns_host_init; rdrv->stop = cdns_host_exit; + rdrv->resume = cdns_host_resume; rdrv->state = CDNS_ROLE_STATE_INACTIVE; rdrv->name = "host"; diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 2a38e1eb6546..97437de52ef6 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -25,6 +25,7 @@ #define TD_PAGE_COUNT 5 #define CI_HDRC_PAGE_SIZE 4096ul /* page size for TD's */ #define ENDPT_MAX 32 +#define CI_MAX_REQ_SIZE (4 * CI_HDRC_PAGE_SIZE) #define CI_MAX_BUF_SIZE (TD_PAGE_COUNT * CI_HDRC_PAGE_SIZE) /****************************************************************************** @@ -260,6 +261,7 @@ struct ci_hdrc { bool b_sess_valid_event; bool imx28_write_fix; bool has_portsc_pec_bug; + bool has_short_pkt_limit; bool supports_runtime_pm; bool in_lpm; bool wakeup_int; diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index c64ab0e07ea0..1a7fc638213e 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -342,6 +342,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) struct ci_hdrc_platform_data pdata = { .name = dev_name(&pdev->dev), .capoffset = DEF_CAPOFFSET, + .flags = CI_HDRC_HAS_SHORT_PKT_LIMIT, .notify_event = ci_hdrc_imx_notify_event, }; int ret; @@ -369,25 +370,29 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) data->pinctrl = devm_pinctrl_get(dev); if (PTR_ERR(data->pinctrl) == -ENODEV) data->pinctrl = NULL; - else if (IS_ERR(data->pinctrl)) - return dev_err_probe(dev, PTR_ERR(data->pinctrl), + else if (IS_ERR(data->pinctrl)) { + ret = dev_err_probe(dev, PTR_ERR(data->pinctrl), "pinctrl get failed\n"); + goto err_put; + } data->hsic_pad_regulator = devm_regulator_get_optional(dev, "hsic"); if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { /* no pad regulator is needed */ data->hsic_pad_regulator = NULL; - } else if (IS_ERR(data->hsic_pad_regulator)) - return dev_err_probe(dev, PTR_ERR(data->hsic_pad_regulator), + } else if (IS_ERR(data->hsic_pad_regulator)) { + ret = dev_err_probe(dev, PTR_ERR(data->hsic_pad_regulator), "Get HSIC pad regulator error\n"); + goto err_put; + } if (data->hsic_pad_regulator) { ret = regulator_enable(data->hsic_pad_regulator); if (ret) { dev_err(dev, "Failed to enable HSIC pad regulator\n"); - return ret; + goto err_put; } } } @@ -401,13 +406,14 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) dev_err(dev, "pinctrl_hsic_idle lookup failed, err=%ld\n", PTR_ERR(pinctrl_hsic_idle)); - return PTR_ERR(pinctrl_hsic_idle); + ret = PTR_ERR(pinctrl_hsic_idle); + goto err_put; } ret = pinctrl_select_state(data->pinctrl, pinctrl_hsic_idle); if (ret) { dev_err(dev, "hsic_idle select failed, err=%d\n", ret); - return ret; + goto err_put; } data->pinctrl_hsic_active = pinctrl_lookup_state(data->pinctrl, @@ -416,7 +422,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) dev_err(dev, "pinctrl_hsic_active lookup failed, err=%ld\n", PTR_ERR(data->pinctrl_hsic_active)); - return PTR_ERR(data->pinctrl_hsic_active); + ret = PTR_ERR(data->pinctrl_hsic_active); + goto err_put; } } @@ -526,6 +533,8 @@ disable_hsic_regulator: if (pdata.flags & CI_HDRC_PMQOS) cpu_latency_qos_remove_request(&data->pm_qos_req); data->ci_pdev = NULL; +err_put: + put_device(data->usbmisc_data->dev); return ret; } @@ -550,6 +559,7 @@ static void ci_hdrc_imx_remove(struct platform_device *pdev) if (data->hsic_pad_regulator) regulator_disable(data->hsic_pad_regulator); } + put_device(data->usbmisc_data->dev); } static void ci_hdrc_imx_shutdown(struct platform_device *pdev) @@ -675,7 +685,7 @@ static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { }; static struct platform_driver ci_hdrc_imx_driver = { .probe = ci_hdrc_imx_probe, - .remove_new = ci_hdrc_imx_remove, + .remove = ci_hdrc_imx_remove, .shutdown = ci_hdrc_imx_shutdown, .driver = { .name = "imx_usb", diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index 1661639cd2eb..3ab3daa78e34 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -292,7 +292,7 @@ MODULE_DEVICE_TABLE(of, msm_ci_dt_match); static struct platform_driver ci_hdrc_msm_driver = { .probe = ci_hdrc_msm_probe, - .remove_new = ci_hdrc_msm_remove, + .remove = ci_hdrc_msm_remove, .driver = { .name = "msm_hsusb", .of_match_table = msm_ci_dt_match, diff --git a/drivers/usb/chipidea/ci_hdrc_npcm.c b/drivers/usb/chipidea/ci_hdrc_npcm.c index 3e5e05dbda89..e52a2b05cbe2 100644 --- a/drivers/usb/chipidea/ci_hdrc_npcm.c +++ b/drivers/usb/chipidea/ci_hdrc_npcm.c @@ -98,7 +98,7 @@ MODULE_DEVICE_TABLE(of, npcm_udc_dt_match); static struct platform_driver npcm_udc_driver = { .probe = npcm_udc_probe, - .remove_new = npcm_udc_remove, + .remove = npcm_udc_remove, .driver = { .name = "npcm_udc", .of_match_table = npcm_udc_dt_match, diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 9538d425f0a0..372788f0f970 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -406,7 +406,7 @@ static struct platform_driver tegra_usb_driver = { .pm = pm_ptr(&tegra_usb_pm), }, .probe = tegra_usb_probe, - .remove_new = tegra_usb_remove, + .remove = tegra_usb_remove, }; module_platform_driver(tegra_usb_driver); diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index 97379f653b06..8ffa1e95d8e8 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -116,7 +116,7 @@ static void ci_hdrc_usb2_remove(struct platform_device *pdev) static struct platform_driver ci_hdrc_usb2_driver = { .probe = ci_hdrc_usb2_probe, - .remove_new = ci_hdrc_usb2_remove, + .remove = ci_hdrc_usb2_remove, .driver = { .name = "chipidea-usb2", .of_match_table = ci_hdrc_usb2_of_match, diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 835bf2428dc6..694b4a8e4e1d 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -765,7 +765,7 @@ static int ci_get_platdata(struct device *dev, ext_id = ERR_PTR(-ENODEV); ext_vbus = ERR_PTR(-ENODEV); - if (of_property_read_bool(dev->of_node, "extcon")) { + if (of_property_present(dev->of_node, "extcon")) { /* Each one of them is not mandatory */ ext_vbus = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) @@ -1076,6 +1076,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) CI_HDRC_SUPPORTS_RUNTIME_PM); ci->has_portsc_pec_bug = !!(ci->platdata->flags & CI_HDRC_HAS_PORTSC_PEC_MISSED); + ci->has_short_pkt_limit = !!(ci->platdata->flags & + CI_HDRC_HAS_SHORT_PKT_LIMIT); platform_set_drvdata(pdev, ci); ret = hw_device_init(ci, base); @@ -1495,7 +1497,7 @@ static const struct dev_pm_ops ci_pm_ops = { static struct platform_driver ci_hdrc_driver = { .probe = ci_hdrc_probe, - .remove_new = ci_hdrc_remove, + .remove = ci_hdrc_remove, .driver = { .name = "ci_hdrc", .pm = &ci_pm_ops, diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 0cce19208370..ced6076a8248 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -13,6 +13,7 @@ #include <linux/usb/hcd.h> #include <linux/usb/chipidea.h> #include <linux/regulator/consumer.h> +#include <linux/string_choices.h> #include <linux/pinctrl/consumer.h> #include "../host/ehci.h" @@ -56,7 +57,7 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) if (ret) { dev_err(dev, "Failed to %s vbus regulator, ret=%d\n", - enable ? "enable" : "disable", ret); + str_enable_disable(enable), ret); return ret; } priv->enabled = enable; @@ -256,8 +257,14 @@ static int ci_ehci_hub_control( struct device *dev = hcd->self.controller; struct ci_hdrc *ci = dev_get_drvdata(dev); - port_index = wIndex & 0xff; - port_index -= (port_index > 0); + /* + * Avoid out-of-bounds values while calculating the port index + * from wIndex. The compiler doesn't like pointers to invalid + * addresses, even if they are never used. + */ + port_index = (wIndex - 1) & 0xff; + if (port_index >= HCS_N_PORTS_MAX) + port_index = 0; status_reg = &ehci->regs->port_status[port_index]; spin_lock_irqsave(&ehci->lock, flags); diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index c17516c29b63..a093544482d5 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -424,8 +424,7 @@ static enum hrtimer_restart ci_otg_hrtimer_func(struct hrtimer *t) /* Initialize timers */ static int ci_otg_init_timers(struct ci_hdrc *ci) { - hrtimer_init(&ci->otg_fsm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - ci->otg_fsm_hrtimer.function = ci_otg_hrtimer_func; + hrtimer_setup(&ci->otg_fsm_hrtimer, ci_otg_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); return 0; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 69ef3cd8d4f8..8a9b31fd5c89 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -10,6 +10,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/dmapool.h> +#include <linux/dma-direct.h> #include <linux/err.h> #include <linux/irqreturn.h> #include <linux/kernel.h> @@ -540,6 +541,126 @@ static int prepare_td_for_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) return ret; } +/* + * Verify if the scatterlist is valid by iterating each sg entry. + * Return invalid sg entry index which is less than num_sgs. + */ +static int sglist_get_invalid_entry(struct device *dma_dev, u8 dir, + struct usb_request *req) +{ + int i; + struct scatterlist *s = req->sg; + + if (req->num_sgs == 1) + return 1; + + dir = dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + + for (i = 0; i < req->num_sgs; i++, s = sg_next(s)) { + /* Only small sg (generally last sg) may be bounced. If + * that happens. we can't ensure the addr is page-aligned + * after dma map. + */ + if (dma_kmalloc_needs_bounce(dma_dev, s->length, dir)) + break; + + /* Make sure each sg start address (except first sg) is + * page-aligned and end address (except last sg) is also + * page-aligned. + */ + if (i == 0) { + if (!IS_ALIGNED(s->offset + s->length, + CI_HDRC_PAGE_SIZE)) + break; + } else { + if (s->offset) + break; + if (!sg_is_last(s) && !IS_ALIGNED(s->length, + CI_HDRC_PAGE_SIZE)) + break; + } + } + + return i; +} + +static int sglist_do_bounce(struct ci_hw_req *hwreq, int index, + bool copy, unsigned int *bounced) +{ + void *buf; + int i, ret, nents, num_sgs; + unsigned int rest, rounded; + struct scatterlist *sg, *src, *dst; + + nents = index + 1; + ret = sg_alloc_table(&hwreq->sgt, nents, GFP_KERNEL); + if (ret) + return ret; + + sg = src = hwreq->req.sg; + num_sgs = hwreq->req.num_sgs; + rest = hwreq->req.length; + dst = hwreq->sgt.sgl; + + for (i = 0; i < index; i++) { + memcpy(dst, src, sizeof(*src)); + rest -= src->length; + src = sg_next(src); + dst = sg_next(dst); + } + + /* create one bounce buffer */ + rounded = round_up(rest, CI_HDRC_PAGE_SIZE); + buf = kmalloc(rounded, GFP_KERNEL); + if (!buf) { + sg_free_table(&hwreq->sgt); + return -ENOMEM; + } + + sg_set_buf(dst, buf, rounded); + + hwreq->req.sg = hwreq->sgt.sgl; + hwreq->req.num_sgs = nents; + hwreq->sgt.sgl = sg; + hwreq->sgt.nents = num_sgs; + + if (copy) + sg_copy_to_buffer(src, num_sgs - index, buf, rest); + + *bounced = rest; + + return 0; +} + +static void sglist_do_debounce(struct ci_hw_req *hwreq, bool copy) +{ + void *buf; + int i, nents, num_sgs; + struct scatterlist *sg, *src, *dst; + + sg = hwreq->req.sg; + num_sgs = hwreq->req.num_sgs; + src = sg_last(sg, num_sgs); + buf = sg_virt(src); + + if (copy) { + dst = hwreq->sgt.sgl; + for (i = 0; i < num_sgs - 1; i++) + dst = sg_next(dst); + + nents = hwreq->sgt.nents - num_sgs + 1; + sg_copy_from_buffer(dst, nents, buf, sg_dma_len(src)); + } + + hwreq->req.sg = hwreq->sgt.sgl; + hwreq->req.num_sgs = hwreq->sgt.nents; + hwreq->sgt.sgl = sg; + hwreq->sgt.nents = num_sgs; + + kfree(buf); + sg_free_table(&hwreq->sgt); +} + /** * _hardware_enqueue: configures a request at hardware level * @hwep: endpoint @@ -552,6 +673,8 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) struct ci_hdrc *ci = hwep->ci; int ret = 0; struct td_node *firstnode, *lastnode; + unsigned int bounced_size; + struct scatterlist *sg; /* don't queue twice */ if (hwreq->req.status == -EALREADY) @@ -559,11 +682,29 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) hwreq->req.status = -EALREADY; + if (hwreq->req.num_sgs && hwreq->req.length && + ci->has_short_pkt_limit) { + ret = sglist_get_invalid_entry(ci->dev->parent, hwep->dir, + &hwreq->req); + if (ret < hwreq->req.num_sgs) { + ret = sglist_do_bounce(hwreq, ret, hwep->dir == TX, + &bounced_size); + if (ret) + return ret; + } + } + ret = usb_gadget_map_request_by_dev(ci->dev->parent, &hwreq->req, hwep->dir); if (ret) return ret; + if (hwreq->sgt.sgl) { + /* We've mapped a bigger buffer, now recover the actual size */ + sg = sg_last(hwreq->req.sg, hwreq->req.num_sgs); + sg_dma_len(sg) = min(sg_dma_len(sg), bounced_size); + } + if (hwreq->req.num_mapped_sgs) ret = prepare_td_for_sg(hwep, hwreq); else @@ -612,10 +753,17 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) do { hw_write(ci, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW); tmp_stat = hw_read(ci, OP_ENDPTSTAT, BIT(n)); - } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW)); + } while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW) && tmp_stat); hw_write(ci, OP_USBCMD, USBCMD_ATDTW, 0); if (tmp_stat) goto done; + + /* OP_ENDPTSTAT will be clear by HW when the endpoint met + * err. This dTD don't push to dQH if current dTD point is + * not the last one in previous request. + */ + if (hwep->qh.ptr->curr != cpu_to_le32(prevlastnode->dma)) + goto done; } /* QH configuration */ @@ -676,6 +824,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) unsigned remaining_length; unsigned actual = hwreq->req.length; struct ci_hdrc *ci = hwep->ci; + bool is_isoc = hwep->type == USB_ENDPOINT_XFER_ISOC; if (hwreq->req.status != -EALREADY) return -EINVAL; @@ -689,7 +838,7 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) int n = hw_ep_bit(hwep->num, hwep->dir); if (ci->rev == CI_REVISION_24 || - ci->rev == CI_REVISION_22) + ci->rev == CI_REVISION_22 || is_isoc) if (!hw_read(ci, OP_ENDPTSTAT, BIT(n))) reprime_dtd(ci, hwep, node); hwreq->req.status = -EALREADY; @@ -708,11 +857,15 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) hwreq->req.status = -EPROTO; break; } else if ((TD_STATUS_TR_ERR & hwreq->req.status)) { - hwreq->req.status = -EILSEQ; - break; + if (is_isoc) { + hwreq->req.status = 0; + } else { + hwreq->req.status = -EILSEQ; + break; + } } - if (remaining_length) { + if (remaining_length && !is_isoc) { if (hwep->dir == TX) { hwreq->req.status = -EPROTO; break; @@ -733,6 +886,10 @@ static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq) usb_gadget_unmap_request_by_dev(hwep->ci->dev->parent, &hwreq->req, hwep->dir); + /* sglist bounced */ + if (hwreq->sgt.sgl) + sglist_do_debounce(hwreq, hwep->dir == RX); + hwreq->req.actual += actual; if (hwreq->req.status) @@ -960,6 +1117,12 @@ static int _ep_queue(struct usb_ep *ep, struct usb_request *req, return -EMSGSIZE; } + if (ci->has_short_pkt_limit && + hwreq->req.length > CI_MAX_REQ_SIZE) { + dev_err(hwep->ci->dev, "request length too big (max 16KB)\n"); + return -EMSGSIZE; + } + /* first nuke then test link, e.g. previous status has not sent */ if (!list_empty(&hwreq->queue)) { dev_err(hwep->ci->dev, "request already in queue\n"); @@ -1574,6 +1737,9 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) usb_gadget_unmap_request(&hwep->ci->gadget, req, hwep->dir); + if (hwreq->sgt.sgl) + sglist_do_debounce(hwreq, false); + req->status = -ECONNRESET; if (hwreq->req.complete != NULL) { @@ -2063,7 +2229,7 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci) } } - if (USBi_UI & intr) + if ((USBi_UI | USBi_UEI) & intr) isr_tr_complete_handler(ci); if ((USBi_SLI & intr) && !(ci->suspended)) { diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index 5193df1e18c7..c8a47389a46b 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h @@ -69,11 +69,13 @@ struct td_node { * @req: request structure for gadget drivers * @queue: link to QH list * @tds: link to TD list + * @sgt: hold original sglist when bounce sglist */ struct ci_hw_req { struct usb_request req; struct list_head queue; struct list_head tds; + struct sg_table sgt; }; #ifdef CONFIG_USB_CHIPIDEA_UDC diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 173c78afd502..6243d8005f5d 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -440,7 +440,7 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data) else if (data->oc_pol_configured) reg &= ~MX6_BM_OVER_CUR_POLARITY; } - /* If the polarity is not set keep it as setup by the bootlader */ + /* If the polarity is not set keep it as setup by the bootloader */ if (data->pwr_pol == 1) reg |= MX6_BM_PWR_POLARITY; writel(reg, usbmisc->base + data->index * 4); @@ -645,7 +645,7 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) else if (data->oc_pol_configured) reg &= ~MX6_BM_OVER_CUR_POLARITY; } - /* If the polarity is not set keep it as setup by the bootlader */ + /* If the polarity is not set keep it as setup by the bootloader */ if (data->pwr_pol == 1) reg |= MX6_BM_PWR_POLARITY; writel(reg, usbmisc->base); @@ -939,7 +939,7 @@ static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data) else if (data->oc_pol_configured) reg &= ~MX6_BM_OVER_CUR_POLARITY; } - /* If the polarity is not set keep it as setup by the bootlader */ + /* If the polarity is not set keep it as setup by the bootloader */ if (data->pwr_pol == 1) reg |= MX6_BM_PWR_POLARITY; @@ -1185,7 +1185,7 @@ int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup) if (usbmisc->ops->hsic_set_clk && data->hsic) ret = usbmisc->ops->hsic_set_clk(data, false); if (ret) { - dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret); + dev_err(data->dev, "hsic_set_clk failed, ret=%d\n", ret); return ret; } @@ -1224,7 +1224,7 @@ int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup) if (usbmisc->ops->hsic_set_clk && data->hsic) ret = usbmisc->ops->hsic_set_clk(data, true); if (ret) { - dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret); + dev_err(data->dev, "hsic_set_clk failed, ret=%d\n", ret); goto hsic_set_clk_fail; } @@ -1285,6 +1285,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .compatible = "fsl,imx7ulp-usbmisc", .data = &imx7ulp_usbmisc_ops, }, + { + .compatible = "fsl,imx8ulp-usbmisc", + .data = &imx7ulp_usbmisc_ops, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 6b37d1c47fce..c2ecfa3c8349 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -371,7 +371,7 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) static void acm_ctrl_irq(struct urb *urb) { struct acm *acm = urb->context; - struct usb_cdc_notification *dr = urb->transfer_buffer; + struct usb_cdc_notification *dr; unsigned int current_size = urb->actual_length; unsigned int expected_size, copy_size, alloc_size; int retval; @@ -398,14 +398,25 @@ static void acm_ctrl_irq(struct urb *urb) usb_mark_last_busy(acm->dev); - if (acm->nb_index) + if (acm->nb_index == 0) { + /* + * The first chunk of a message must contain at least the + * notification header with the length field, otherwise we + * can't get an expected_size. + */ + if (current_size < sizeof(struct usb_cdc_notification)) { + dev_dbg(&acm->control->dev, "urb too short\n"); + goto exit; + } + dr = urb->transfer_buffer; + } else { dr = (struct usb_cdc_notification *)acm->notification_buffer; - + } /* size = notification-header + (optional) data */ expected_size = sizeof(struct usb_cdc_notification) + le16_to_cpu(dr->wLength); - if (current_size < expected_size) { + if (acm->nb_index != 0 || current_size < expected_size) { /* notification is transmitted fragmented, reassemble */ if (acm->nb_size < expected_size) { u8 *new_buffer; @@ -1727,13 +1738,16 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, - { USB_DEVICE(0x045b, 0x023c), /* Renesas USB Download mode */ + { USB_DEVICE(0x045b, 0x023c), /* Renesas R-Car H3 USB Download mode */ + .driver_info = DISABLE_ECHO, /* Don't echo banner */ + }, + { USB_DEVICE(0x045b, 0x0247), /* Renesas R-Car D3 USB Download mode */ .driver_info = DISABLE_ECHO, /* Don't echo banner */ }, - { USB_DEVICE(0x045b, 0x0248), /* Renesas USB Download mode */ + { USB_DEVICE(0x045b, 0x0248), /* Renesas R-Car M3-N USB Download mode */ .driver_info = DISABLE_ECHO, /* Don't echo banner */ }, - { USB_DEVICE(0x045b, 0x024D), /* Renesas USB Download mode */ + { USB_DEVICE(0x045b, 0x024D), /* Renesas R-Car E3 USB Download mode */ .driver_info = DISABLE_ECHO, /* Don't echo banner */ }, { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 5a2e43331064..e2527faa6592 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -87,7 +87,7 @@ /* Get two-int array: [0]=vendor ID, [1]=product ID: */ #define LPIOC_GET_VID_PID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_VID_PID, len) /* Perform class specific soft reset */ -#define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0); +#define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0) /* * A DEVICE_ID string may include the printer's serial number. @@ -1337,11 +1337,12 @@ static int usblp_set_protocol(struct usblp *usblp, int protocol) if (protocol < USBLP_FIRST_PROTOCOL || protocol > USBLP_LAST_PROTOCOL) return -EINVAL; + alts = usblp->protocol[protocol].alt_setting; + if (alts < 0) + return -EINVAL; + /* Don't unnecessarily set the interface if there's a single alt. */ if (usblp->intf->num_altsetting > 1) { - alts = usblp->protocol[protocol].alt_setting; - if (alts < 0) - return -EINVAL; r = usb_set_interface(usblp->dev, usblp->ifnum, alts); if (r < 0) { printk(KERN_ERR "usblp: can't set desired altsetting %d on interface %d\n", diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index b7bea1015d7c..fc0845f681be 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -41,6 +41,12 @@ const char *usb_ep_type_string(int ep_type) } EXPORT_SYMBOL_GPL(usb_ep_type_string); +/** + * usb_otg_state_string() - returns human readable name of OTG state. + * @state: the OTG state to return the human readable name of. If it's not + * any of the states defined in usb_otg_state enum, 'UNDEFINED' will be + * returned. + */ const char *usb_otg_state_string(enum usb_otg_state state) { static const char *const names[] = { @@ -179,6 +185,14 @@ static const char *const usb_dr_modes[] = { [USB_DR_MODE_OTG] = "otg", }; +/** + * usb_get_dr_mode_from_string() - Get dual role mode for given string + * @str: String to find the corresponding dual role mode for + * + * This function performs a lookup for the given string and returns the + * corresponding enum usb_dr_mode. If no match for the string could be found, + * 'USB_DR_MODE_UNKNOWN' is returned. + */ static enum usb_dr_mode usb_get_dr_mode_from_string(const char *str) { int ret; @@ -415,6 +429,9 @@ EXPORT_SYMBOL_GPL(usb_of_get_companion_dev); struct dentry *usb_debug_root; EXPORT_SYMBOL_GPL(usb_debug_root); +DEFINE_MUTEX(usb_dynids_lock); +EXPORT_SYMBOL_GPL(usb_dynids_lock); + static int __init usb_common_init(void) { usb_debug_root = debugfs_create_dir("usb", NULL); diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index 501e8bc9738e..1e36be2a28fd 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -19,6 +19,7 @@ #include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/regulator/consumer.h> +#include <linux/string_choices.h> #include <linux/usb/role.h> #define USB_GPIO_DEB_MS 20 /* ms */ @@ -111,7 +112,7 @@ static void usb_conn_detect_cable(struct work_struct *work) if (info->vbus) dev_dbg(info->dev, "vbus regulator is %s\n", - regulator_is_enabled(info->vbus) ? "enabled" : "disabled"); + str_enabled_disabled(regulator_is_enabled(info->vbus))); power_supply_changed(info->charger); } @@ -157,7 +158,7 @@ static int usb_conn_psy_register(struct usb_conn_info *info) struct device *dev = info->dev; struct power_supply_desc *desc = &info->desc; struct power_supply_config cfg = { - .of_node = dev->of_node, + .fwnode = dev_fwnode(dev), }; desc->name = "usb-charger"; @@ -340,7 +341,7 @@ MODULE_DEVICE_TABLE(of, usb_conn_dt_match); static struct platform_driver usb_conn_driver = { .probe = usb_conn_probe, - .remove_new = usb_conn_remove, + .remove = usb_conn_remove, .driver = { .name = "usb-conn-gpio", .pm = &usb_conn_pm_ops, diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 880d52c0949d..13bd4ec4ea5f 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -9,6 +9,7 @@ #include <linux/usb/quirks.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/device.h> #include <asm/byteorder.h> #include "usb.h" @@ -18,12 +19,6 @@ #define USB_MAXCONFIG 8 /* Arbitrary limit */ - -static inline const char *plural(int n) -{ - return (n == 1 ? "" : "s"); -} - static int find_next_descriptor(unsigned char *buffer, int size, int dt1, int dt2, int *num_skipped) { @@ -69,6 +64,37 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev, memcpy(&ep->ssp_isoc_ep_comp, desc, USB_DT_SSP_ISOC_EP_COMP_SIZE); } +static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev, + int cfgno, int inum, int asnum, struct usb_host_endpoint *ep, + unsigned char *buffer, int size) +{ + struct usb_eusb2_isoc_ep_comp_descriptor *desc; + struct usb_descriptor_header *h; + + /* + * eUSB2 isochronous endpoint companion descriptor for this endpoint + * shall be declared before the next endpoint or interface descriptor + */ + while (size >= USB_DT_EUSB2_ISOC_EP_COMP_SIZE) { + h = (struct usb_descriptor_header *)buffer; + + if (h->bDescriptorType == USB_DT_EUSB2_ISOC_ENDPOINT_COMP) { + desc = (struct usb_eusb2_isoc_ep_comp_descriptor *)buffer; + ep->eusb2_isoc_ep_comp = *desc; + return; + } + if (h->bDescriptorType == USB_DT_ENDPOINT || + h->bDescriptorType == USB_DT_INTERFACE) + break; + + buffer += h->bLength; + size -= h->bLength; + } + + dev_notice(ddev, "No eUSB2 isoc ep %d companion for config %d interface %d altsetting %d\n", + ep->desc.bEndpointAddress, cfgno, inum, asnum); +} + static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, int inum, int asnum, struct usb_host_endpoint *ep, unsigned char *buffer, int size) @@ -263,8 +289,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int n, i, j, retval; unsigned int maxp; const unsigned short *maxpacket_maxes; + u16 bcdUSB; d = (struct usb_endpoint_descriptor *) buffer; + bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB); buffer += d->bLength; size -= d->bLength; @@ -414,15 +442,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, /* * Validate the wMaxPacketSize field. - * Some devices have isochronous endpoints in altsetting 0; - * the USB-2 spec requires such endpoints to have wMaxPacketSize = 0 - * (see the end of section 5.6.3), so don't warn about them. + * eUSB2 devices (see USB 2.0 Double Isochronous IN ECN 9.6.6 Endpoint) + * and devices with isochronous endpoints in altsetting 0 (see USB 2.0 + * end of section 5.6.3) have wMaxPacketSize = 0. + * So don't warn about those. */ maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize); - if (maxp == 0 && !(usb_endpoint_xfer_isoc(d) && asnum == 0)) { + + if (maxp == 0 && bcdUSB != 0x0220 && + !(usb_endpoint_xfer_isoc(d) && asnum == 0)) dev_notice(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n", cfgno, inum, asnum, d->bEndpointAddress); - } /* Find the highest legal maxpacket size for this endpoint */ i = 0; /* additional transactions per microframe */ @@ -470,6 +500,12 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, maxp); } + /* Parse a possible eUSB2 periodic endpoint companion descriptor */ + if (bcdUSB == 0x0220 && d->wMaxPacketSize == 0 && + (usb_endpoint_xfer_isoc(d) || usb_endpoint_xfer_int(d))) + usb_parse_eusb2_isoc_endpoint_companion(ddev, cfgno, inum, asnum, + endpoint, buffer, size); + /* Parse a possible SuperSpeed endpoint companion descriptor */ if (udev->speed >= USB_SPEED_SUPER) usb_parse_ss_endpoint_companion(ddev, cfgno, @@ -484,7 +520,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, retval = buffer - buffer0 + i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", - n, plural(n), "endpoint"); + n, str_plural(n), "endpoint"); return retval; skip_to_next_endpoint_or_interface_descriptor: @@ -563,7 +599,7 @@ static int usb_parse_interface(struct device *ddev, int cfgno, alt->extralen = i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", - n, plural(n), "interface"); + n, str_plural(n), "interface"); buffer += i; size -= i; @@ -605,7 +641,7 @@ static int usb_parse_interface(struct device *ddev, int cfgno, dev_notice(ddev, "config %d interface %d altsetting %d has %d " "endpoint descriptor%s, different from the interface " "descriptor's value: %d\n", - cfgno, inum, asnum, n, plural(n), num_ep_orig); + cfgno, inum, asnum, n, str_plural(n), num_ep_orig); return buffer - buffer0; skip_to_next_interface_descriptor: @@ -664,7 +700,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, if (size2 < sizeof(struct usb_descriptor_header)) { dev_notice(ddev, "config %d descriptor has %d excess " "byte%s, ignoring\n", - cfgno, size2, plural(size2)); + cfgno, size2, str_plural(size2)); break; } @@ -754,7 +790,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, if (n != nintf) dev_notice(ddev, "config %d has %d interface%s, different from " "the descriptor's value: %d\n", - cfgno, n, plural(n), nintf_orig); + cfgno, n, str_plural(n), nintf_orig); else if (n == 0) dev_notice(ddev, "config %d has no interfaces?\n", cfgno); config->desc.bNumInterfaces = nintf = n; @@ -798,7 +834,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, config->extralen = i; if (n > 0) dev_dbg(ddev, "skipped %d descriptor%s after %s\n", - n, plural(n), "configuration"); + n, str_plural(n), "configuration"); buffer += i; size -= i; @@ -924,7 +960,7 @@ int usb_get_configuration(struct usb_device *dev) result = -EINVAL; goto err; } - length = max((int) le16_to_cpu(desc->wTotalLength), + length = max_t(int, le16_to_cpu(desc->wTotalLength), USB_DT_CONFIG_SIZE); /* Now that we know the length, get the whole thing */ diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 3beb6a862e80..f6ce6e26e0d4 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -238,6 +238,9 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma) dma_addr_t dma_handle = DMA_MAPPING_ERROR; int ret; + if (!(file->f_mode & FMODE_WRITE)) + return -EPERM; + ret = usbfs_increase_memory_usage(size + sizeof(struct usb_memory)); if (ret) goto error; @@ -1295,7 +1298,7 @@ static int do_proc_bulk(struct usb_dev_state *ps, return ret; len1 = bulk->len; - if (len1 < 0 || len1 >= (INT_MAX - sizeof(struct urb))) + if (len1 >= (INT_MAX - sizeof(struct urb))) return -EINVAL; if (bulk->ep & USB_DIR_IN) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0c3f12daac79..460d4dde5994 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -95,9 +95,9 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids, } } - spin_lock(&dynids->lock); + mutex_lock(&usb_dynids_lock); list_add_tail(&dynid->node, &dynids->list); - spin_unlock(&dynids->lock); + mutex_unlock(&usb_dynids_lock); retval = driver_attach(driver); @@ -116,6 +116,7 @@ ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf) struct usb_dynid *dynid; size_t count = 0; + guard(mutex)(&usb_dynids_lock); list_for_each_entry(dynid, &dynids->list, node) if (dynid->id.bInterfaceClass != 0) count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x %02x\n", @@ -160,7 +161,7 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf, if (fields < 2) return -EINVAL; - spin_lock(&usb_driver->dynids.lock); + guard(mutex)(&usb_dynids_lock); list_for_each_entry_safe(dynid, n, &usb_driver->dynids.list, node) { struct usb_device_id *id = &dynid->id; @@ -171,7 +172,6 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf, break; } } - spin_unlock(&usb_driver->dynids.lock); return count; } @@ -220,27 +220,24 @@ static void usb_free_dynids(struct usb_driver *usb_drv) { struct usb_dynid *dynid, *n; - spin_lock(&usb_drv->dynids.lock); + guard(mutex)(&usb_dynids_lock); list_for_each_entry_safe(dynid, n, &usb_drv->dynids.list, node) { list_del(&dynid->node); kfree(dynid); } - spin_unlock(&usb_drv->dynids.lock); } static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *intf, - struct usb_driver *drv) + const struct usb_driver *drv) { struct usb_dynid *dynid; - spin_lock(&drv->dynids.lock); + guard(mutex)(&usb_dynids_lock); list_for_each_entry(dynid, &drv->dynids.list, node) { if (usb_match_one_id(intf, &dynid->id)) { - spin_unlock(&drv->dynids.lock); return &dynid->id; } } - spin_unlock(&drv->dynids.lock); return NULL; } @@ -853,7 +850,7 @@ const struct usb_device_id *usb_device_match_id(struct usb_device *udev, EXPORT_SYMBOL_GPL(usb_device_match_id); bool usb_driver_applicable(struct usb_device *udev, - struct usb_device_driver *udrv) + const struct usb_device_driver *udrv) { if (udrv->id_table && udrv->match) return usb_device_match_id(udev, udrv->id_table) != NULL && @@ -873,7 +870,7 @@ static int usb_device_match(struct device *dev, const struct device_driver *drv) /* devices and interfaces are handled separately */ if (is_usb_device(dev)) { struct usb_device *udev; - struct usb_device_driver *udrv; + const struct usb_device_driver *udrv; /* interface drivers never match devices */ if (!is_usb_device_driver(drv)) @@ -893,7 +890,7 @@ static int usb_device_match(struct device *dev, const struct device_driver *drv) } else if (is_usb_interface(dev)) { struct usb_interface *intf; - struct usb_driver *usb_drv; + const struct usb_driver *usb_drv; const struct usb_device_id *id; /* device drivers never match interfaces */ @@ -1076,7 +1073,6 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, new_driver->driver.owner = owner; new_driver->driver.mod_name = mod_name; new_driver->driver.dev_groups = new_driver->dev_groups; - spin_lock_init(&new_driver->dynids.lock); INIT_LIST_HEAD(&new_driver->dynids.list); retval = driver_register(&new_driver->driver); @@ -1090,15 +1086,14 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner, pr_info("%s: registered new interface driver %s\n", usbcore_name, new_driver->name); -out: - return retval; + return 0; out_newid: driver_unregister(&new_driver->driver); - +out: pr_err("%s: error %d registering interface driver %s\n", usbcore_name, retval, new_driver->name); - goto out; + return retval; } EXPORT_SYMBOL_GPL(usb_register_driver); diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 4b38b87a1343..e48399401608 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/spinlock.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/usb.h> #include "usb.h" @@ -39,7 +40,7 @@ static ssize_t field##_show(struct device *dev, \ char *buf) \ { \ struct ep_device *ep = to_ep_device(dev); \ - return sprintf(buf, format_string, ep->desc->field); \ + return sysfs_emit(buf, format_string, ep->desc->field); \ } \ static DEVICE_ATTR_RO(field) @@ -52,7 +53,7 @@ static ssize_t wMaxPacketSize_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ep_device *ep = to_ep_device(dev); - return sprintf(buf, "%04x\n", usb_endpoint_maxp(ep->desc)); + return sysfs_emit(buf, "%04x\n", usb_endpoint_maxp(ep->desc)); } static DEVICE_ATTR_RO(wMaxPacketSize); @@ -76,7 +77,7 @@ static ssize_t type_show(struct device *dev, struct device_attribute *attr, type = "Interrupt"; break; } - return sprintf(buf, "%s\n", type); + return sysfs_emit(buf, "%s\n", type); } static DEVICE_ATTR_RO(type); @@ -95,7 +96,7 @@ static ssize_t interval_show(struct device *dev, struct device_attribute *attr, interval /= 1000; } - return sprintf(buf, "%d%cs\n", interval, unit); + return sysfs_emit(buf, "%d%cs\n", interval, unit); } static DEVICE_ATTR_RO(interval); @@ -111,7 +112,7 @@ static ssize_t direction_show(struct device *dev, struct device_attribute *attr, direction = "in"; else direction = "out"; - return sprintf(buf, "%s\n", direction); + return sysfs_emit(buf, "%s\n", direction); } static DEVICE_ATTR_RO(direction); diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index b134bff5c3fe..9c6ae5e1198b 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -21,14 +21,10 @@ #include <linux/usb.h> #include <linux/usb/hcd.h> +#include <linux/string_choices.h> #include <uapi/linux/usb/audio.h> #include "usb.h" -static inline const char *plural(int n) -{ - return (n == 1 ? "" : "s"); -} - static int is_rndis(struct usb_interface_descriptor *desc) { return desc->bInterfaceClass == USB_CLASS_COMM @@ -194,18 +190,18 @@ int usb_choose_configuration(struct usb_device *udev) if (insufficient_power > 0) dev_info(&udev->dev, "rejected %d configuration%s " "due to insufficient available bus power\n", - insufficient_power, plural(insufficient_power)); + insufficient_power, str_plural(insufficient_power)); if (best) { i = best->desc.bConfigurationValue; dev_dbg(&udev->dev, "configuration #%d chosen from %d choice%s\n", - i, num_configs, plural(num_configs)); + i, num_configs, str_plural(num_configs)); } else { i = -1; dev_warn(&udev->dev, "no configuration chosen from %d choice%s\n", - num_configs, plural(num_configs)); + num_configs, str_plural(num_configs)); } return i; } diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index a08f3f228e6d..56b534f59907 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -422,7 +422,12 @@ static int suspend_common(struct device *dev, pm_message_t msg) bool do_wakeup; int retval; - do_wakeup = PMSG_IS_AUTO(msg) ? true : device_may_wakeup(dev); + if (PMSG_IS_AUTO(msg)) + do_wakeup = true; + else if (PMSG_NO_WAKEUP(msg)) + do_wakeup = false; + else + do_wakeup = device_may_wakeup(dev); /* Root hub suspend should have stopped all downstream traffic, * and all bus master traffic. And done so for both the interface @@ -521,6 +526,11 @@ static int hcd_pci_suspend(struct device *dev) return suspend_common(dev, PMSG_SUSPEND); } +static int hcd_pci_freeze(struct device *dev) +{ + return suspend_common(dev, PMSG_FREEZE); +} + static int hcd_pci_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -590,6 +600,7 @@ static int hcd_pci_restore(struct device *dev) #else #define hcd_pci_suspend NULL +#define hcd_pci_freeze NULL #define hcd_pci_suspend_noirq NULL #define hcd_pci_poweroff_late NULL #define hcd_pci_resume_noirq NULL @@ -624,7 +635,7 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = { .suspend_noirq = hcd_pci_suspend_noirq, .resume_noirq = hcd_pci_resume_noirq, .resume = hcd_pci_resume, - .freeze = hcd_pci_suspend, + .freeze = hcd_pci_freeze, .freeze_noirq = check_root_hub_suspended, .thaw_noirq = NULL, .thaw = hcd_pci_resume, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 500dc35e6477..a63c793bac21 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -415,7 +415,7 @@ ascii2desc(char const *s, u8 *buf, unsigned len) static unsigned rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len) { - char buf[100]; + char buf[160]; char const *s; static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04}; @@ -842,7 +842,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) } else { /* Status URB */ if (!hcd->uses_new_polling) - del_timer (&hcd->rh_timer); + timer_delete(&hcd->rh_timer); if (urb == hcd->status_urb) { hcd->status_urb = NULL; usb_hcd_unlink_urb_from_ep(hcd, urb); @@ -1609,7 +1609,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) if (retval == 0) retval = -EINPROGRESS; else if (retval != -EIDRM && retval != -EBUSY) - dev_dbg(&udev->dev, "hcd_unlink_urb %pK fail %d\n", + dev_dbg(&udev->dev, "hcd_unlink_urb %p fail %d\n", urb, retval); usb_put_dev(udev); } @@ -1786,7 +1786,7 @@ rescan: /* kick hcd */ unlink1(hcd, urb, -ESHUTDOWN); dev_dbg (hcd->self.controller, - "shutdown urb %pK ep%d%s-%s\n", + "shutdown urb %p ep%d%s-%s\n", urb, usb_endpoint_num(&ep->desc), is_in ? "in" : "out", usb_ep_type_string(usb_endpoint_type(&ep->desc))); @@ -2768,14 +2768,14 @@ static void usb_stop_hcd(struct usb_hcd *hcd) { hcd->rh_pollable = 0; clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - del_timer_sync(&hcd->rh_timer); + timer_delete_sync(&hcd->rh_timer); hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; /* In case the HCD restarted the timer, stop it again. */ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - del_timer_sync(&hcd->rh_timer); + timer_delete_sync(&hcd->rh_timer); } /** @@ -2794,8 +2794,14 @@ int usb_add_hcd(struct usb_hcd *hcd, int retval; struct usb_device *rhdev; struct usb_hcd *shared_hcd; + int skip_phy_initialization; - if (!hcd->skip_phy_initialization) { + if (usb_hcd_is_primary_hcd(hcd)) + skip_phy_initialization = hcd->skip_phy_initialization; + else + skip_phy_initialization = hcd->primary_hcd->skip_phy_initialization; + + if (!skip_phy_initialization) { if (usb_hcd_is_primary_hcd(hcd)) { hcd->phy_roothub = usb_phy_roothub_alloc(hcd->self.sysdev); if (IS_ERR(hcd->phy_roothub)) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4b93c0bd1d4b..0e1dd6ef60a7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -18,6 +18,7 @@ #include <linux/sched/mm.h> #include <linux/list.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/kcov.h> #include <linux/ioctl.h> #include <linux/usb.h> @@ -1384,7 +1385,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) } /* Stop hub_wq and related activity */ - del_timer_sync(&hub->irq_urb_retry); + timer_delete_sync(&hub->irq_urb_retry); usb_kill_urb(hub->urb); if (hub->has_indicators) cancel_delayed_work_sync(&hub->leds); @@ -1496,7 +1497,7 @@ static int hub_configure(struct usb_hub *hub, maxchild = hub->descriptor->bNbrPorts; dev_info(hub_dev, "%d port%s detected\n", maxchild, - (maxchild == 1) ? "" : "s"); + str_plural(maxchild)); hub->ports = kcalloc(maxchild, sizeof(struct usb_port *), GFP_KERNEL); if (!hub->ports) { @@ -1848,6 +1849,17 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) hdev = interface_to_usbdev(intf); /* + * The USB 2.0 spec prohibits hubs from having more than one + * configuration or interface, and we rely on this prohibition. + * Refuse to accept a device that violates it. + */ + if (hdev->descriptor.bNumConfigurations > 1 || + hdev->actconfig->desc.bNumInterfaces > 1) { + dev_err(&intf->dev, "Invalid hub with more than one config or interface\n"); + return -EINVAL; + } + + /* * Set default autosuspend delay as 0 to speedup bus suspend, * based on the below considerations: * @@ -2663,13 +2675,13 @@ int usb_new_device(struct usb_device *udev) err = sysfs_create_link(&udev->dev.kobj, &port_dev->dev.kobj, "port"); if (err) - goto fail; + goto out_del_dev; err = sysfs_create_link(&port_dev->dev.kobj, &udev->dev.kobj, "device"); if (err) { sysfs_remove_link(&udev->dev.kobj, "port"); - goto fail; + goto out_del_dev; } if (!test_and_set_bit(port1, hub->child_usage_bits)) @@ -2683,6 +2695,8 @@ int usb_new_device(struct usb_device *udev) pm_runtime_put_sync_autosuspend(&udev->dev); return err; +out_del_dev: + device_del(&udev->dev); fail: usb_set_device_state(udev, USB_STATE_NOTATTACHED); pm_runtime_disable(&udev->dev); @@ -4137,14 +4151,14 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev, break; default: dev_warn(&udev->dev, "%s: Can't %s non-U1 or U2 state.\n", - __func__, enable ? "enable" : "disable"); + __func__, str_enable_disable(enable)); return -EINVAL; } if (udev->state != USB_STATE_CONFIGURED) { dev_dbg(&udev->dev, "%s: Can't %s %s state " "for unconfigured device.\n", - __func__, enable ? "enable" : "disable", + __func__, str_enable_disable(enable), usb3_lpm_names[state]); return 0; } @@ -4170,8 +4184,7 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev, } if (ret < 0) { dev_warn(&udev->dev, "%s of device-initiated %s failed.\n", - enable ? "Enable" : "Disable", - usb3_lpm_names[state]); + str_enable_disable(enable), usb3_lpm_names[state]); return -EBUSY; } return 0; @@ -4695,9 +4708,6 @@ void usb_ep0_reinit(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_ep0_reinit); -#define usb_sndaddr0pipe() (PIPE_CONTROL << 30) -#define usb_rcvaddr0pipe() ((PIPE_CONTROL << 30) | USB_DIR_IN) - static int hub_set_address(struct usb_device *udev, int devnum) { int retval; @@ -4721,7 +4731,7 @@ static int hub_set_address(struct usb_device *udev, int devnum) if (hcd->driver->address_device) retval = hcd->driver->address_device(hcd, udev, timeout_ms); else - retval = usb_control_msg(udev, usb_sndaddr0pipe(), + retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_ADDRESS, 0, devnum, 0, NULL, 0, timeout_ms); if (retval == 0) { @@ -4802,7 +4812,7 @@ static int get_bMaxPacketSize0(struct usb_device *udev, for (i = 0; i < GET_MAXPACKET0_TRIES; ++i) { /* Start with invalid values in case the transfer fails */ buf->bDescriptorType = buf->bMaxPacketSize0 = 0; - rc = usb_control_msg(udev, usb_rcvaddr0pipe(), + rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, 0, buf, size, @@ -6054,6 +6064,36 @@ void usb_hub_cleanup(void) } /* usb_hub_cleanup() */ /** + * hub_hc_release_resources - clear resources used by host controller + * @udev: pointer to device being released + * + * Context: task context, might sleep + * + * Function releases the host controller resources in correct order before + * making any operation on resuming usb device. The host controller resources + * allocated for devices in tree should be released starting from the last + * usb device in tree toward the root hub. This function is used only during + * resuming device when usb device require reinitialization – that is, when + * flag udev->reset_resume is set. + * + * This call is synchronous, and may not be used in an interrupt context. + */ +static void hub_hc_release_resources(struct usb_device *udev) +{ + struct usb_hub *hub = usb_hub_to_struct_hub(udev); + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + int i; + + /* Release up resources for all children before this device */ + for (i = 0; i < udev->maxchild; i++) + if (hub->ports[i]->child) + hub_hc_release_resources(hub->ports[i]->child); + + if (hcd->driver->reset_device) + hcd->driver->reset_device(hcd, udev); +} + +/** * usb_reset_and_verify_device - perform a USB port reset to reinitialize a device * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * @@ -6117,6 +6157,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev) bos = udev->bos; udev->bos = NULL; + if (udev->reset_resume) + hub_hc_release_resources(udev); + mutex_lock(hcd->address0_mutex); for (i = 0; i < PORT_INIT_TRIES; ++i) { diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c index 85c999f71ad7..5e3c515991f3 100644 --- a/drivers/usb/core/ledtrig-usbport.c +++ b/drivers/usb/core/ledtrig-usbport.c @@ -10,6 +10,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/slab.h> +#include <linux/sysfs.h> #include <linux/usb.h> #include <linux/usb/of.h> @@ -87,7 +88,7 @@ static ssize_t usbport_trig_port_show(struct device *dev, struct usbport_trig_port, attr); - return sprintf(buf, "%d\n", port->observed) + 1; + return sysfs_emit(buf, "%d\n", port->observed) + 1; } static ssize_t usbport_trig_port_store(struct device *dev, diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index e7da2fca11a4..f54198171b6a 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -9,6 +9,8 @@ #include <linux/kstrtox.h> #include <linux/slab.h> +#include <linux/string_choices.h> +#include <linux/sysfs.h> #include <linux/pm_qos.h> #include <linux/component.h> #include <linux/usb/of.h> @@ -24,7 +26,7 @@ static ssize_t early_stop_show(struct device *dev, { struct usb_port *port_dev = to_usb_port(dev); - return sysfs_emit(buf, "%s\n", port_dev->early_stop ? "yes" : "no"); + return sysfs_emit(buf, "%s\n", str_yes_no(port_dev->early_stop)); } static ssize_t early_stop_store(struct device *dev, struct device_attribute *attr, @@ -166,7 +168,7 @@ static ssize_t location_show(struct device *dev, { struct usb_port *port_dev = to_usb_port(dev); - return sprintf(buf, "0x%08x\n", port_dev->location); + return sysfs_emit(buf, "0x%08x\n", port_dev->location); } static DEVICE_ATTR_RO(location); @@ -191,7 +193,7 @@ static ssize_t connect_type_show(struct device *dev, break; } - return sprintf(buf, "%s\n", result); + return sysfs_emit(buf, "%s\n", result); } static DEVICE_ATTR_RO(connect_type); @@ -210,7 +212,7 @@ static ssize_t over_current_count_show(struct device *dev, { struct usb_port *port_dev = to_usb_port(dev); - return sprintf(buf, "%u\n", port_dev->over_current_count); + return sysfs_emit(buf, "%u\n", port_dev->over_current_count); } static DEVICE_ATTR_RO(over_current_count); @@ -219,7 +221,7 @@ static ssize_t quirks_show(struct device *dev, { struct usb_port *port_dev = to_usb_port(dev); - return sprintf(buf, "%08x\n", port_dev->quirks); + return sysfs_emit(buf, "%08x\n", port_dev->quirks); } static ssize_t quirks_store(struct device *dev, struct device_attribute *attr, @@ -254,7 +256,7 @@ static ssize_t usb3_lpm_permit_show(struct device *dev, p = "0"; } - return sprintf(buf, "%s\n", p); + return sysfs_emit(buf, "%s\n", p); } static ssize_t usb3_lpm_permit_store(struct device *dev, @@ -452,10 +454,11 @@ static int usb_port_runtime_suspend(struct device *dev) static void usb_port_shutdown(struct device *dev) { struct usb_port *port_dev = to_usb_port(dev); + struct usb_device *udev = port_dev->child; - if (port_dev->child) { - usb_disable_usb2_hardware_lpm(port_dev->child); - usb_unlocked_disable_lpm(port_dev->child); + if (udev && !udev->port_is_suspended) { + usb_disable_usb2_hardware_lpm(udev); + usb_unlocked_disable_lpm(udev); } } diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 13171454f959..8efbacc5bc34 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -341,6 +341,10 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x0638, 0x0a13), .driver_info = USB_QUIRK_STRING_FETCH_255 }, + /* Prolific Single-LUN Mass Storage Card Reader */ + { USB_DEVICE(0x067b, 0x2731), .driver_info = USB_QUIRK_DELAY_INIT | + USB_QUIRK_NO_LPM }, + /* Saitek Cyborg Gold Joystick */ { USB_DEVICE(0x06a3, 0x0006), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, @@ -394,6 +398,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Kingston DataTraveler 3.0 */ { USB_DEVICE(0x0951, 0x1666), .driver_info = USB_QUIRK_NO_LPM }, + /* TOSHIBA TransMemory-Mx */ + { USB_DEVICE(0x0930, 0x1408), .driver_info = USB_QUIRK_NO_LPM }, + /* NVIDIA Jetson devices in Force Recovery mode */ { USB_DEVICE(0x0955, 0x7018), .driver_info = USB_QUIRK_RESET_RESUME }, { USB_DEVICE(0x0955, 0x7019), .driver_info = USB_QUIRK_RESET_RESUME }, @@ -432,6 +439,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x0c45, 0x7056), .driver_info = USB_QUIRK_IGNORE_REMOTE_WAKEUP }, + /* Sony Xperia XZ1 Compact (lilac) smartphone in fastboot mode */ + { USB_DEVICE(0x0fce, 0x0dde), .driver_info = USB_QUIRK_NO_LPM }, + /* Action Semiconductor flash disk */ { USB_DEVICE(0x10d6, 0x2200), .driver_info = USB_QUIRK_STRING_FETCH_255 }, @@ -522,6 +532,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Blackmagic Design UltraStudio SDI */ { USB_DEVICE(0x1edb, 0xbd4f), .driver_info = USB_QUIRK_NO_LPM }, + /* Teclast disk */ + { USB_DEVICE(0x1f75, 0x0917), .driver_info = USB_QUIRK_NO_LPM }, + /* Hauppauge HVR-950q */ { USB_DEVICE(0x2040, 0x7200), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 61b6d978892c..23f3cb1989f4 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -854,7 +854,7 @@ static const struct attribute_group dev_string_attr_grp = { static ssize_t descriptors_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -890,11 +890,11 @@ descriptors_read(struct file *filp, struct kobject *kobj, } return count - nleft; } -static BIN_ATTR_RO(descriptors, 18 + 65535); /* dev descr + max-size raw descriptor */ +static const BIN_ATTR_RO(descriptors, 18 + 65535); /* dev descr + max-size raw descriptor */ static ssize_t bos_descriptors_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -913,19 +913,19 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj, } return n; } -static BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */ +static const BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */ /* When modifying this list, be sure to modify dev_bin_attrs_are_visible() * accordingly. */ -static struct bin_attribute *dev_bin_attrs[] = { +static const struct bin_attribute *const dev_bin_attrs[] = { &bin_attr_descriptors, &bin_attr_bos_descriptors, NULL }; static umode_t dev_bin_attrs_are_visible(struct kobject *kobj, - struct bin_attribute *a, int n) + const struct bin_attribute *a, int n) { struct device *dev = kobj_to_dev(kobj); struct usb_device *udev = to_usb_device(dev); @@ -944,7 +944,7 @@ static umode_t dev_bin_attrs_are_visible(struct kobject *kobj, } static const struct attribute_group dev_bin_attr_grp = { - .bin_attrs = dev_bin_attrs, + .bin_attrs_new = dev_bin_attrs, .is_bin_visible = dev_bin_attrs_are_visible, }; diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 7576920e2d5a..5e52a35486af 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -376,7 +376,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (!urb || !urb->complete) return -EINVAL; if (urb->hcpriv) { - WARN_ONCE(1, "URB %pK submitted while active\n", urb); + WARN_ONCE(1, "URB %p submitted while active\n", urb); return -EBUSY; } diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 03c22114214b..935c0efea0b6 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -213,8 +213,7 @@ usb_acpi_get_connect_type(struct usb_port *port_dev, acpi_handle *handle) * no connectable, the port would be not used. */ - status = acpi_get_physical_device_location(handle, &pld); - if (ACPI_SUCCESS(status) && pld) + if (acpi_get_physical_device_location(handle, &pld) && pld) port_dev->location = USB_ACPI_LOCATION_VALID | pld->group_token << 8 | pld->group_position; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index b8324ea05b20..a9b37aeb515b 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -75,7 +75,7 @@ extern int usb_match_device(struct usb_device *dev, extern const struct usb_device_id *usb_device_match_id(struct usb_device *udev, const struct usb_device_id *id); extern bool usb_driver_applicable(struct usb_device *udev, - struct usb_device_driver *udrv); + const struct usb_device_driver *udrv); extern void usb_forced_unbind_intf(struct usb_interface *intf); extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev); diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index c131719367ec..d7af55a8eced 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -21,7 +21,7 @@ config USB_DWC2 if USB_DWC2 choice - bool "DWC2 Mode Selection" + prompt "DWC2 Mode Selection" default USB_DWC2_DUAL_ROLE if (USB && USB_GADGET) default USB_DWC2_HOST if (USB && !USB_GADGET) default USB_DWC2_PERIPHERAL if (!USB && USB_GADGET) diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 9919ab725d54..c3d24312db0f 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -43,6 +43,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) /* Backup global regs */ gr = &hsotg->gr_backup; + gr->gintsts = dwc2_readl(hsotg, GINTSTS); gr->gotgctl = dwc2_readl(hsotg, GOTGCTL); gr->gintmsk = dwc2_readl(hsotg, GINTMSK); gr->gahbcfg = dwc2_readl(hsotg, GAHBCFG); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 2bd74f3033ed..34127b890b2a 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -667,6 +667,7 @@ struct dwc2_hw_params { /** * struct dwc2_gregs_backup - Holds global registers state before * entering partial power down + * @gintsts: Backup of GINTSTS register * @gotgctl: Backup of GOTGCTL register * @gintmsk: Backup of GINTMSK register * @gahbcfg: Backup of GAHBCFG register @@ -683,6 +684,7 @@ struct dwc2_hw_params { * @valid: True if registers values backuped. */ struct dwc2_gregs_backup { + u32 gintsts; u32 gotgctl; u32 gintmsk; u32 gahbcfg; @@ -1127,6 +1129,9 @@ struct dwc2_hsotg { #define DWC2_FS_IOT_ID 0x55310000 #define DWC2_HS_IOT_ID 0x55320000 +#define DWC2_RESTORE_DCTL BIT(0) +#define DWC2_RESTORE_DCFG BIT(1) + #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) union dwc2_hcd_internal_flags { u32 d32; @@ -1420,7 +1425,7 @@ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); #define dwc2_is_device_connected(hsotg) (hsotg->connected) #define dwc2_is_device_enabled(hsotg) (hsotg->enabled) int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg); -int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup); +int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, unsigned int flags); int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg); int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset); @@ -1435,6 +1440,9 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg); int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg); void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg); void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg); +int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg); +int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg, + unsigned int flags); static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) { hsotg->fifo_map = 0; } #else @@ -1459,7 +1467,7 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, - int remote_wakeup) + unsigned int flags) { return 0; } static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) { return 0; } @@ -1482,6 +1490,11 @@ static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {} static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {} +static inline int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg, + unsigned int flags) +{ return 0; } static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) {} #endif @@ -1505,6 +1518,8 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg); void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup); bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2); +int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg); +int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg); static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) { schedule_work(&hsotg->phy_reset_work); } #else @@ -1544,6 +1559,10 @@ static inline void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup) {} static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2) { return false; } +static inline int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg) +{ return 0; } static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {} #endif diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index e7bf9cc635be..300ea4969f0c 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -4615,6 +4615,7 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) spin_lock_irqsave(&hsotg->lock, flags); hsotg->driver = NULL; + hsotg->gadget.dev.of_node = NULL; hsotg->gadget.speed = USB_SPEED_UNKNOWN; hsotg->enabled = 0; @@ -5203,11 +5204,11 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) * if controller power were disabled. * * @hsotg: Programming view of the DWC_otg controller - * @remote_wakeup: Indicates whether resume is initiated by Device or Host. + * @flags: Defines which registers should be restored. * * Return: 0 if successful, negative error code otherwise */ -int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup) +int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, unsigned int flags) { struct dwc2_dregs_backup *dr; int i; @@ -5223,7 +5224,10 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup) } dr->valid = false; - if (!remote_wakeup) + if (flags & DWC2_RESTORE_DCFG) + dwc2_writel(hsotg, dr->dcfg, DCFG); + + if (flags & DWC2_RESTORE_DCTL) dwc2_writel(hsotg, dr->dctl, DCTL); dwc2_writel(hsotg, dr->daintmsk, DAINTMSK); @@ -5309,6 +5313,49 @@ void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK)); } +int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg) +{ + int ret; + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + + return 0; +} + +int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg, + unsigned int flags) +{ + int ret; + + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + ret = dwc2_restore_device_registers(hsotg, flags); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore device registers\n", + __func__); + return ret; + } + + return 0; +} + /** * dwc2_gadget_enter_hibernation() - Put controller in Hibernation. * @@ -5326,18 +5373,9 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) /* Change to L2(suspend) state */ hsotg->lx_state = DWC2_L2; dev_dbg(hsotg->dev, "Start of hibernation completed\n"); - ret = dwc2_backup_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup global registers\n", - __func__); - return ret; - } - ret = dwc2_backup_device_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup device registers\n", - __func__); + ret = dwc2_gadget_backup_critical_registers(hsotg); + if (ret) return ret; - } gpwrdn = GPWRDN_PWRDNRSTN; udelay(10); @@ -5414,6 +5452,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, u32 gpwrdn; u32 dctl; int ret = 0; + unsigned int flags = 0; struct dwc2_gregs_backup *gr; struct dwc2_dregs_backup *dr; @@ -5476,6 +5515,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, dctl = dwc2_readl(hsotg, DCTL); dctl |= DCTL_PWRONPRGDONE; dwc2_writel(hsotg, dctl, DCTL); + flags |= DWC2_RESTORE_DCTL; } /* Wait for interrupts which must be cleared */ mdelay(2); @@ -5483,20 +5523,9 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, dwc2_writel(hsotg, 0xffffffff, GINTSTS); /* Restore global registers */ - ret = dwc2_restore_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore registers\n", - __func__); - return ret; - } - - /* Restore device registers */ - ret = dwc2_restore_device_registers(hsotg, rem_wakeup); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore device registers\n", - __func__); + ret = dwc2_gadget_restore_critical_registers(hsotg, flags); + if (ret) return ret; - } if (rem_wakeup) { mdelay(10); @@ -5530,19 +5559,9 @@ int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "Entering device partial power down started.\n"); /* Backup all registers */ - ret = dwc2_backup_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup global registers\n", - __func__); - return ret; - } - - ret = dwc2_backup_device_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup device registers\n", - __func__); + ret = dwc2_gadget_backup_critical_registers(hsotg); + if (ret) return ret; - } /* * Clear any pending interrupts since dwc2 will not be able to @@ -5589,11 +5608,8 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, { u32 pcgcctl; u32 dctl; - struct dwc2_dregs_backup *dr; int ret = 0; - dr = &hsotg->dr_backup; - dev_dbg(hsotg->dev, "Exiting device partial Power Down started.\n"); pcgcctl = dwc2_readl(hsotg, PCGCTL); @@ -5610,21 +5626,10 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg, udelay(100); if (restore) { - ret = dwc2_restore_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore registers\n", - __func__); - return ret; - } - /* Restore DCFG */ - dwc2_writel(hsotg, dr->dcfg, DCFG); - - ret = dwc2_restore_device_registers(hsotg, 0); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore device registers\n", - __func__); + ret = dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL | + DWC2_RESTORE_DCFG); + if (ret) return ret; - } } /* Set the Power-On Programming done bit */ diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index cb54390e7de4..60ef8092259a 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3546,11 +3546,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, port_status |= USB_PORT_STAT_C_OVERCURRENT << 16; } - if (!hsotg->flags.b.port_connect_status) { + if (dwc2_is_device_mode(hsotg)) { /* - * The port is disconnected, which means the core is - * either in device mode or it soon will be. Just - * return 0's for the remainder of the port status + * Just return 0's for the remainder of the port status * since the port register can't be read if the core * is in device mode. */ @@ -3620,13 +3618,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, if (wvalue != USB_PORT_FEAT_TEST && (!windex || windex > 1)) goto error; - if (!hsotg->flags.b.port_connect_status) { + if (dwc2_is_device_mode(hsotg)) { /* - * The port is disconnected, which means the core is - * either in device mode or it soon will be. Just - * return without doing anything since the port - * register can't be written if the core is in device - * mode. + * Just return 0's for the remainder of the port status + * since the port register can't be read if the core + * is in device mode. */ break; } @@ -4349,7 +4345,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) if (hsotg->bus_suspended) goto skip_power_saving; - if (hsotg->flags.b.port_connect_status == 0) + if (!(dwc2_read_hprt0(hsotg) & HPRT0_CONNSTS)) goto skip_power_saving; switch (hsotg->params.power_down) { @@ -4431,6 +4427,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) * Power Down mode. */ if (hprt0 & HPRT0_CONNSTS) { + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); hsotg->lx_state = DWC2_L0; goto unlock; } @@ -5084,7 +5081,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) cancel_work_sync(&hsotg->phy_reset_work); - del_timer(&hsotg->wkp_timer); + timer_delete(&hsotg->wkp_timer); } static void dwc2_hcd_release(struct dwc2_hsotg *hsotg) @@ -5477,6 +5474,49 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) return 0; } +int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg) +{ + int ret; + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + ret = dwc2_backup_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup host registers\n", + __func__); + return ret; + } + + return 0; +} + +int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg) +{ + int ret; + + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + + ret = dwc2_restore_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore host registers\n", + __func__); + return ret; + } + + return 0; +} + /** * dwc2_host_enter_hibernation() - Put controller in Hibernation. * @@ -5492,18 +5532,9 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) u32 gpwrdn; dev_dbg(hsotg->dev, "Preparing host for hibernation\n"); - ret = dwc2_backup_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup global registers\n", - __func__); - return ret; - } - ret = dwc2_backup_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup host registers\n", - __func__); + ret = dwc2_host_backup_critical_registers(hsotg); + if (ret) return ret; - } /* Enter USB Suspend Mode */ hprt0 = dwc2_readl(hsotg, HPRT0); @@ -5697,20 +5728,9 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, dwc2_writel(hsotg, 0xffffffff, GINTSTS); /* Restore global registers */ - ret = dwc2_restore_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore registers\n", - __func__); - return ret; - } - - /* Restore host registers */ - ret = dwc2_restore_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore host registers\n", - __func__); + ret = dwc2_host_restore_critical_registers(hsotg); + if (ret) return ret; - } if (rem_wakeup) { dwc2_hcd_rem_wakeup(hsotg); @@ -5777,19 +5797,9 @@ int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg) dev_warn(hsotg->dev, "Suspend wasn't generated\n"); /* Backup all registers */ - ret = dwc2_backup_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup global registers\n", - __func__); - return ret; - } - - ret = dwc2_backup_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to backup host registers\n", - __func__); + ret = dwc2_host_backup_critical_registers(hsotg); + if (ret) return ret; - } /* * Clear any pending interrupts since dwc2 will not be able to @@ -5858,19 +5868,9 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg, udelay(100); if (restore) { - ret = dwc2_restore_global_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore registers\n", - __func__); - return ret; - } - - ret = dwc2_restore_host_registers(hsotg); - if (ret) { - dev_err(hsotg->dev, "%s: failed to restore host registers\n", - __func__); + ret = dwc2_host_restore_critical_registers(hsotg); + if (ret) return ret; - } } /* Drive resume signaling and exit suspend mode on the port. */ diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 238c6fd50e75..b0098792dd22 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -1302,7 +1302,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) } /* Cancel pending unreserve; if canceled OK, unreserve was pending */ - if (del_timer(&qh->unreserve_timer)) + if (timer_delete(&qh->unreserve_timer)) WARN_ON(!qh->unreserve_pending); /* @@ -1459,8 +1459,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, /* Initialize QH */ qh->hsotg = hsotg; timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0); - hrtimer_init(&qh->wait_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - qh->wait_timer.function = &dwc2_wait_timer_fn; + hrtimer_setup(&qh->wait_timer, &dwc2_wait_timer_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL); qh->ep_type = ep_type; qh->ep_is_in = ep_is_in; @@ -1615,7 +1614,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { /* Make sure any unreserve work is finished. */ - if (del_timer_sync(&qh->unreserve_timer)) { + if (timer_delete_sync(&qh->unreserve_timer)) { unsigned long flags; spin_lock_irqsave(&hsotg->lock, flags); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index c1b7209b9483..12b4dc07d08a 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -685,6 +685,14 @@ static int __maybe_unused dwc2_suspend(struct device *dev) regulator_disable(dwc2->usb33d); } + if (is_device_mode) + ret = dwc2_gadget_backup_critical_registers(dwc2); + else + ret = dwc2_host_backup_critical_registers(dwc2); + + if (ret) + return ret; + if (dwc2->ll_hw_enabled && (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) { ret = __dwc2_lowlevel_hw_disable(dwc2); @@ -694,6 +702,24 @@ static int __maybe_unused dwc2_suspend(struct device *dev) return ret; } +static int dwc2_restore_critical_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_gregs_backup *gr; + + gr = &hsotg->gr_backup; + + if (!gr->valid) { + dev_err(hsotg->dev, "No valid register backup, failed to restore\n"); + return -EINVAL; + } + + if (gr->gintsts & GINTSTS_CURMODE_HOST) + return dwc2_host_restore_critical_registers(hsotg); + + return dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL | + DWC2_RESTORE_DCFG); +} + static int __maybe_unused dwc2_resume(struct device *dev) { struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); @@ -706,6 +732,18 @@ static int __maybe_unused dwc2_resume(struct device *dev) } dwc2->phy_off_for_suspend = false; + /* + * During suspend it's possible that the power domain for the + * DWC2 controller is disabled and all register values get lost. + * In case the GUSBCFG register is not initialized, it's clear the + * registers must be restored. + */ + if (!(dwc2_readl(dwc2, GUSBCFG) & GUSBCFG_TOUTCAL_MASK)) { + ret = dwc2_restore_critical_registers(dwc2); + if (ret) + return ret; + } + if (dwc2->params.activate_stm_id_vb_detection) { unsigned long flags; u32 ggpio, gotgctl; @@ -756,7 +794,7 @@ static struct platform_driver dwc2_platform_driver = { .pm = &dwc2_dev_pm_ops, }, .probe = dwc2_driver_probe, - .remove_new = dwc2_driver_remove, + .remove = dwc2_driver_remove, .shutdown = dwc2_driver_shutdown, }; diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 31078f3d41b8..310d182e10b5 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -23,7 +23,7 @@ config USB_DWC3_ULPI controller. choice - bool "DWC3 Mode Selection" + prompt "DWC3 Mode Selection" default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET) default USB_DWC3_HOST if (USB && !USB_GADGET) default USB_DWC3_GADGET if (!USB && USB_GADGET) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 98114c2827c0..66a08b527165 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -131,11 +131,24 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable) } } -void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy) { + unsigned int hw_mode; u32 reg; reg = dwc3_readl(dwc->regs, DWC3_GCTL); + + /* + * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and + * GUSB2PHYCFG.SUSPHY should be cleared during mode switching, + * and they can be set after core initialization. + */ + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && !ignore_susphy) { + if (DWC3_GCTL_PRTCAP(reg) != mode) + dwc3_enable_susphy(dwc, false); + } + reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); reg |= DWC3_GCTL_PRTCAPDIR(mode); dwc3_writel(dwc->regs, DWC3_GCTL, reg); @@ -216,7 +229,7 @@ static void __dwc3_set_mode(struct work_struct *work) spin_lock_irqsave(&dwc->lock, flags); - dwc3_set_prtcap(dwc, desired_dr_role); + dwc3_set_prtcap(dwc, desired_dr_role, false); spin_unlock_irqrestore(&dwc->lock, flags); @@ -658,16 +671,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index) */ reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX; - /* - * Above DWC_usb3.0 1.94a, it is recommended to set - * DWC3_GUSB3PIPECTL_SUSPHY to '0' during coreConsultant configuration. - * So default value will be '0' when the core is reset. Application - * needs to set it to '1' after the core initialization is completed. - * - * Similarly for DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be - * cleared after power-on reset, and it can be set after core - * initialization. - */ + /* Ensure the GUSB3PIPECTL.SUSPENDENABLE is cleared prior to phy init. */ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; if (dwc->u2ss_inp3_quirk) @@ -747,15 +751,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index) break; } - /* - * Above DWC_usb3.0 1.94a, it is recommended to set - * DWC3_GUSB2PHYCFG_SUSPHY to '0' during coreConsultant configuration. - * So default value will be '0' when the core is reset. Application - * needs to set it to '1' after the core initialization is completed. - * - * Similarly for DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared - * after power-on reset, and it can be set after core initialization. - */ + /* Ensure the GUSB2PHYCFG.SUSPHY is cleared prior to phy init. */ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; if (dwc->dis_enblslpm_quirk) @@ -830,6 +826,25 @@ static int dwc3_phy_init(struct dwc3 *dwc) goto err_exit_usb3_phy; } + /* + * Above DWC_usb3.0 1.94a, it is recommended to set + * DWC3_GUSB3PIPECTL_SUSPHY and DWC3_GUSB2PHYCFG_SUSPHY to '0' during + * coreConsultant configuration. So default value will be '0' when the + * core is reset. Application needs to set it to '1' after the core + * initialization is completed. + * + * Certain phy requires to be in P0 power state during initialization. + * Make sure GUSB3PIPECTL.SUSPENDENABLE and GUSB2PHYCFG.SUSPHY are clear + * prior to phy init to maintain in the P0 state. + * + * After phy initialization, some phy operations can only be executed + * while in lower P states. Ensure GUSB3PIPECTL.SUSPENDENABLE and + * GUSB2PHYCFG.SUSPHY are set soon after initialization to avoid + * blocking phy ops. + */ + if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) + dwc3_enable_susphy(dwc, true); + return 0; err_exit_usb3_phy: @@ -1409,7 +1424,7 @@ static int dwc3_core_init(struct dwc3 *dwc) /* * When configured in HOST mode, after issuing U3/L2 exit controller - * fails to send proper CRC checksum in CRC5 feild. Because of this + * fails to send proper CRC checksum in CRC5 field. Because of this * behaviour Transaction Error is generated, resulting in reset and * re-enumeration of usb device attached. All the termsel, xcvrsel, * opmode becomes 0 during end of resume. Enabling bit 10 of GUCTL1 @@ -1470,9 +1485,33 @@ static int dwc3_core_init(struct dwc3 *dwc) if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && (DWC3_IP_IS(DWC31)) && dwc->maximum_speed == USB_SPEED_SUPER) { - reg = dwc3_readl(dwc->regs, DWC3_LLUCTL); - reg |= DWC3_LLUCTL_FORCE_GEN1; - dwc3_writel(dwc->regs, DWC3_LLUCTL, reg); + int i; + + for (i = 0; i < dwc->num_usb3_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_LLUCTL(i)); + reg |= DWC3_LLUCTL_FORCE_GEN1; + dwc3_writel(dwc->regs, DWC3_LLUCTL(i), reg); + } + } + + /* + * STAR 9001346572: This issue affects DWC_usb31 versions 1.80a and + * prior. When an active endpoint not currently cached in the host + * controller is chosen to be cached to the same index as an endpoint + * receiving NAKs, the endpoint receiving NAKs enters continuous + * retry mode. This prevents it from being evicted from the host + * controller cache, blocking the new endpoint from being cached and + * serviced. + * + * To resolve this, for controller versions 1.70a and 1.80a, set the + * GUCTL3 bit[16] (USB2.0 Internal Retry Disable) to 1. This bit + * disables the USB2.0 internal retry feature. The GUCTL3[16] register + * function is available only from version 1.70a. + */ + if (DWC3_VER_IS_WITHIN(DWC31, 170A, 180A)) { + reg = dwc3_readl(dwc->regs, DWC3_GUCTL3); + reg |= DWC3_GUCTL3_USB20_RETRY_DISABLE; + dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); } return 0; @@ -1564,7 +1603,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, false); if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, false); @@ -1576,7 +1615,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) return dev_err_probe(dev, ret, "failed to initialize gadget\n"); break; case USB_DR_MODE_HOST: - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, false); if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, true); @@ -1621,7 +1660,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) } /* de-assert DRVVBUS for HOST and OTG mode */ - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true); } static void dwc3_get_software_properties(struct dwc3 *dwc) @@ -1660,8 +1699,6 @@ static void dwc3_get_properties(struct dwc3 *dwc) u8 tx_thr_num_pkt_prd = 0; u8 tx_max_burst_prd = 0; u8 tx_fifo_resize_max_num; - const char *usb_psy_name; - int ret; /* default to highest possible threshold */ lpm_nyet_threshold = 0xf; @@ -1696,13 +1733,6 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->sys_wakeup = device_may_wakeup(dwc->sysdev); - ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name); - if (ret >= 0) { - dwc->usb_psy = power_supply_get_by_name(usb_psy_name); - if (!dwc->usb_psy) - dev_err(dev, "couldn't get usb power supply\n"); - } - dwc->has_lpm_erratum = device_property_read_bool(dev, "snps,has-lpm-erratum"); device_property_read_u8(dev, "snps,lpm-nyet-threshold", @@ -1820,8 +1850,6 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd; dwc->tx_max_burst_prd = tx_max_burst_prd; - dwc->imod_interval = 0; - dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num; } @@ -1839,21 +1867,19 @@ static void dwc3_check_params(struct dwc3 *dwc) unsigned int hwparam_gen = DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3); - /* Check for proper value of imod_interval */ - if (dwc->imod_interval && !dwc3_has_imod(dwc)) { - dev_warn(dwc->dev, "Interrupt moderation not supported\n"); - dwc->imod_interval = 0; - } - /* + * Enable IMOD for all supporting controllers. + * + * Particularly, DWC_usb3 v3.00a must enable this feature for + * the following reason: + * * Workaround for STAR 9000961433 which affects only version * 3.00a of the DWC_usb3 core. This prevents the controller * interrupt from being masked while handling events. IMOD * allows us to work around this issue. Enable it for the * affected version. */ - if (!dwc->imod_interval && - DWC3_VER_IS(DWC3, 300A)) + if (dwc3_has_imod((dwc))) dwc->imod_interval = 1; /* Check the maximum_speed parameter */ @@ -1941,7 +1967,7 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) struct extcon_dev *edev = NULL; const char *name; - if (device_property_read_bool(dev, "extcon")) + if (device_property_present(dev, "extcon")) return extcon_get_edev_by_phandle(dev, 0); /* @@ -2105,6 +2131,23 @@ static int dwc3_get_num_ports(struct dwc3 *dwc) return 0; } +static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc) +{ + struct power_supply *usb_psy; + const char *usb_psy_name; + int ret; + + ret = device_property_read_string(dwc->dev, "usb-psy-name", &usb_psy_name); + if (ret < 0) + return NULL; + + usb_psy = power_supply_get_by_name(usb_psy_name); + if (!usb_psy) + return ERR_PTR(-EPROBE_DEFER); + + return usb_psy; +} + static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -2161,6 +2204,10 @@ static int dwc3_probe(struct platform_device *pdev) dwc3_get_software_properties(dwc); + dwc->usb_psy = dwc3_get_usb_power_supply(dwc); + if (IS_ERR(dwc->usb_psy)) + return dev_err_probe(dev, PTR_ERR(dwc->usb_psy), "couldn't get usb power supply\n"); + dwc->reset = devm_reset_control_array_get_optional_shared(dev); if (IS_ERR(dwc->reset)) { ret = PTR_ERR(dwc->reset); @@ -2421,7 +2468,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) if (ret) return ret; - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true); dwc3_gadget_resume(dwc); break; case DWC3_GCTL_PRTCAP_HOST: @@ -2429,7 +2476,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) ret = dwc3_core_init_for_resume(dwc); if (ret) return ret; - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST, true); break; } /* Restore GUSB2PHYCFG bits that were modified in suspend */ @@ -2458,7 +2505,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) if (ret) return ret; - dwc3_set_prtcap(dwc, dwc->current_dr_role); + dwc3_set_prtcap(dwc, dwc->current_dr_role, true); dwc3_otg_init(dwc); if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) { @@ -2585,12 +2632,15 @@ static int dwc3_resume(struct device *dev) pinctrl_pm_select_default_state(dev); pm_runtime_disable(dev); - pm_runtime_set_active(dev); + ret = pm_runtime_set_active(dev); + if (ret) + goto out; ret = dwc3_resume_common(dwc, PMSG_RESUME); if (ret) pm_runtime_set_suspended(dev); +out: pm_runtime_enable(dev); return ret; @@ -2651,7 +2701,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match); static struct platform_driver dwc3_driver = { .probe = dwc3_probe, - .remove_new = dwc3_remove, + .remove = dwc3_remove, .driver = { .name = "dwc3", .of_match_table = of_match_ptr(of_dwc3_match), diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index eab81dfdcc35..aaa39e663f60 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -81,7 +81,7 @@ #define DWC3_GSNPSREV_MASK 0xffff #define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16) -/* DWC3 registers memory space boundries */ +/* DWC3 registers memory space boundaries */ #define DWC3_XHCI_REGS_START 0x0 #define DWC3_XHCI_REGS_END 0x7fff #define DWC3_GLOBALS_REGS_START 0xc100 @@ -179,7 +179,7 @@ #define DWC3_OEVTEN 0xcc0C #define DWC3_OSTS 0xcc10 -#define DWC3_LLUCTL 0xd024 +#define DWC3_LLUCTL(n) (0xd024 + ((n) * 0x80)) /* Bit fields */ @@ -425,6 +425,7 @@ /* Global User Control Register 3 */ #define DWC3_GUCTL3_SPLITDISABLE BIT(14) +#define DWC3_GUCTL3_USB20_RETRY_DISABLE BIT(16) /* Device Configuration Register */ #define DWC3_DCFG_NUMLANES(n) (((n) & 0x3) << 30) /* DWC_usb32 only */ @@ -464,6 +465,7 @@ #define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) /* These apply for core versions 1.94a and later */ +#define DWC3_DCTL_NYET_THRES_MASK (0xf << 20) #define DWC3_DCTL_NYET_THRES(n) (((n) & 0xf) << 20) #define DWC3_DCTL_KEEP_CONNECT BIT(19) @@ -715,6 +717,7 @@ struct dwc3_event_buffer { /** * struct dwc3_ep - device side endpoint representation * @endpoint: usb endpoint + * @nostream_work: work for handling bulk NoStream * @cancelled_list: list of cancelled requests for this endpoint * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint @@ -741,6 +744,7 @@ struct dwc3_event_buffer { */ struct dwc3_ep { struct usb_ep endpoint; + struct delayed_work nostream_work; struct list_head cancelled_list; struct list_head pending_list; struct list_head started_list; @@ -763,7 +767,7 @@ struct dwc3_ep { #define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7) #define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8) #define DWC3_EP_FORCE_RESTART_STREAM BIT(9) -#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10) +#define DWC3_EP_STREAM_PRIMED BIT(10) #define DWC3_EP_PENDING_CLEAR_STALL BIT(11) #define DWC3_EP_TXFIFO_RESIZED BIT(12) #define DWC3_EP_DELAY_STOP BIT(13) @@ -915,6 +919,7 @@ struct dwc3_hwparams { #define DWC3_MODE(n) ((n) & 0x7) /* HWPARAMS1 */ +#define DWC3_SPRAM_TYPE(n) (((n) >> 23) & 1) #define DWC3_NUM_INT(n) (((n) & (0x3f << 15)) >> 15) /* HWPARAMS3 */ @@ -925,6 +930,9 @@ struct dwc3_hwparams { #define DWC3_NUM_IN_EPS(p) (((p)->hwparams3 & \ (DWC3_NUM_IN_EPS_MASK)) >> 18) +/* HWPARAMS6 */ +#define DWC3_RAM0_DEPTH(n) (((n) & (0xffff0000)) >> 16) + /* HWPARAMS7 */ #define DWC3_RAM1_DEPTH(n) ((n) & 0xffff) @@ -937,18 +945,14 @@ struct dwc3_hwparams { * @request: struct usb_request to be transferred * @list: a list_head used for request queueing * @dep: struct dwc3_ep owning this request - * @sg: pointer to first incomplete sg * @start_sg: pointer to the sg which should be queued next * @num_pending_sgs: counter to pending sgs - * @num_queued_sgs: counter to the number of sgs which already got queued * @remaining: amount of data remaining * @status: internal dwc3 request status tracking * @epnum: endpoint number to which this request refers * @trb: pointer to struct dwc3_trb * @trb_dma: DMA address of @trb * @num_trbs: number of TRBs used by this request - * @needs_extra_trb: true when request needs one extra TRB (either due to ZLP - * or unaligned OUT) * @direction: IN or OUT direction flag * @mapped: true when request has been dma-mapped */ @@ -956,11 +960,9 @@ struct dwc3_request { struct usb_request request; struct list_head list; struct dwc3_ep *dep; - struct scatterlist *sg; struct scatterlist *start_sg; unsigned int num_pending_sgs; - unsigned int num_queued_sgs; unsigned int remaining; unsigned int status; @@ -978,7 +980,6 @@ struct dwc3_request { unsigned int num_trbs; - unsigned int needs_extra_trb:1; unsigned int direction:1; unsigned int mapped:1; }; @@ -1557,7 +1558,7 @@ struct dwc3_gadget_ep_cmd_params { #define DWC3_HAS_OTG BIT(3) /* prototypes */ -void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy); void dwc3_set_mode(struct dwc3 *dwc, u32 mode); u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index d76ae676783c..7977860932b1 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -173,7 +173,7 @@ void dwc3_otg_init(struct dwc3 *dwc) * block "Initialize GCTL for OTG operation". */ /* GCTL.PrtCapDir=2'b11 */ - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true); /* GUSB2PHYCFG0.SusPHY=0 */ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; @@ -556,7 +556,7 @@ int dwc3_drd_init(struct dwc3 *dwc) dwc3_drd_update(dwc); } else { - dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG); + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true); /* use OTG block to get ID event */ irq = dwc3_otg_get_irq(dwc); diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index fad151e78fd6..9db8f3ca493d 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -108,6 +108,9 @@ #define DWC3_AM62_AUTOSUSPEND_DELAY 100 +#define USBSS_DEBUG_CFG_OFF 0x0 +#define USBSS_DEBUG_CFG_DISABLED 0x7 + struct dwc3_am62 { struct device *dev; void __iomem *usbss; @@ -117,6 +120,7 @@ struct dwc3_am62 { unsigned int offset; unsigned int vbus_divider; u32 wakeup_stat; + void __iomem *phy_regs; }; static const int dwc3_ti_rate_table[] = { /* in KHZ */ @@ -149,11 +153,11 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62) { struct device *dev = am62->dev; struct device_node *node = dev->of_node; - struct of_phandle_args args; struct regmap *syscon; int ret; - syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-phy-pll-refclk"); + syscon = syscon_regmap_lookup_by_phandle_args(node, "ti,syscon-phy-pll-refclk", + 1, &am62->offset); if (IS_ERR(syscon)) { dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n"); return PTR_ERR(syscon); @@ -161,13 +165,6 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62) am62->syscon = syscon; - ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1, - 0, &args); - if (ret) - return ret; - - am62->offset = args.args[0]; - /* Core voltage. PHY_CORE_VOLTAGE bit Recommended to be 0 always */ ret = regmap_update_bits(am62->syscon, am62->offset, PHY_CORE_VOLTAGE_MASK, 0); if (ret) { @@ -184,15 +181,47 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62) return 0; } +static int dwc3_ti_init(struct dwc3_am62 *am62) +{ + int ret; + u32 reg; + + /* Read the syscon property and set the rate code */ + ret = phy_syscon_pll_refclk(am62); + if (ret) + return ret; + + /* Workaround Errata i2409 */ + if (am62->phy_regs) { + reg = readl(am62->phy_regs + USB_PHY_PLL_REG12); + reg |= USB_PHY_PLL_LDO_REF_EN | USB_PHY_PLL_LDO_REF_EN_EN; + writel(reg, am62->phy_regs + USB_PHY_PLL_REG12); + } + + /* VBUS divider select */ + reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG); + if (am62->vbus_divider) + reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT; + + dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg); + + clk_prepare_enable(am62->usb2_refclk); + + /* Set mode valid bit to indicate role is valid */ + reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL); + reg |= USBSS_MODE_VALID; + dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg); + + return 0; +} + static int dwc3_ti_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = pdev->dev.of_node; struct dwc3_am62 *am62; unsigned long rate; - void __iomem *phy; int i, ret; - u32 reg; am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL); if (!am62) @@ -228,29 +257,17 @@ static int dwc3_ti_probe(struct platform_device *pdev) am62->rate_code = i; - /* Read the syscon property and set the rate code */ - ret = phy_syscon_pll_refclk(am62); - if (ret) - return ret; - - /* Workaround Errata i2409 */ - phy = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(phy)) { + am62->phy_regs = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(am62->phy_regs)) { dev_err(dev, "can't map PHY IOMEM resource. Won't apply i2409 fix.\n"); - phy = NULL; - } else { - reg = readl(phy + USB_PHY_PLL_REG12); - reg |= USB_PHY_PLL_LDO_REF_EN | USB_PHY_PLL_LDO_REF_EN_EN; - writel(reg, phy + USB_PHY_PLL_REG12); + am62->phy_regs = NULL; } - /* VBUS divider select */ am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); - reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG); - if (am62->vbus_divider) - reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT; - dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg); + ret = dwc3_ti_init(am62); + if (ret) + return ret; pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -258,7 +275,6 @@ static int dwc3_ti_probe(struct platform_device *pdev) * Don't ignore its dependencies with its children */ pm_suspend_ignore_children(dev, false); - clk_prepare_enable(am62->usb2_refclk); pm_runtime_get_noresume(dev); ret = of_platform_populate(node, NULL, NULL, dev); @@ -267,11 +283,6 @@ static int dwc3_ti_probe(struct platform_device *pdev) goto err_pm_disable; } - /* Set mode valid bit to indicate role is valid */ - reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL); - reg |= USBSS_MODE_VALID; - dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg); - /* Device has capability to wakeup system from sleep */ device_set_wakeup_capable(dev, true); ret = device_wakeup_enable(dev); @@ -309,6 +320,7 @@ static void dwc3_ti_remove(struct platform_device *pdev) pm_runtime_put_sync(dev); pm_runtime_disable(dev); + pm_runtime_dont_use_autosuspend(dev); pm_runtime_set_suspended(dev); } @@ -338,6 +350,9 @@ static int dwc3_ti_suspend_common(struct device *dev) dwc3_ti_writel(am62, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR); } + /* just to track if module resets on suspend */ + dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_DISABLED); + clk_disable_unprepare(am62->usb2_refclk); return 0; @@ -348,7 +363,14 @@ static int dwc3_ti_resume_common(struct device *dev) struct dwc3_am62 *am62 = dev_get_drvdata(dev); u32 reg; - clk_prepare_enable(am62->usb2_refclk); + reg = dwc3_ti_readl(am62, USBSS_DEBUG_CFG); + if (reg != USBSS_DEBUG_CFG_DISABLED) { + /* lost power/context */ + dwc3_ti_init(am62); + } else { + dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_OFF); + clk_prepare_enable(am62->usb2_refclk); + } if (device_may_wakeup(dev)) { /* Clear wakeup config enable bits */ @@ -377,7 +399,7 @@ MODULE_DEVICE_TABLE(of, dwc3_ti_of_match); static struct platform_driver dwc3_ti_driver = { .probe = dwc3_ti_probe, - .remove_new = dwc3_ti_remove, + .remove = dwc3_ti_remove, .driver = { .name = "dwc3-am62", .pm = DEV_PM_OPS, diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 9a6e988d165a..de686b9e6404 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -163,6 +163,12 @@ static const struct dwc3_exynos_driverdata exynos7_drvdata = { .suspend_clk_idx = 1, }; +static const struct dwc3_exynos_driverdata exynos7870_drvdata = { + .clk_names = { "bus_early", "ref", "ctrl" }, + .num_clks = 3, + .suspend_clk_idx = -1, +}; + static const struct dwc3_exynos_driverdata exynos850_drvdata = { .clk_names = { "bus_early", "ref" }, .num_clks = 2, @@ -186,6 +192,9 @@ static const struct of_device_id exynos_dwc3_match[] = { .compatible = "samsung,exynos7-dwusb3", .data = &exynos7_drvdata, }, { + .compatible = "samsung,exynos7870-dwusb3", + .data = &exynos7870_drvdata, + }, { .compatible = "samsung,exynos850-dwusb3", .data = &exynos850_drvdata, }, { @@ -243,7 +252,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(dwc3_exynos_dev_pm_ops, static struct platform_driver dwc3_exynos_driver = { .probe = dwc3_exynos_probe, - .remove_new = dwc3_exynos_remove, + .remove = dwc3_exynos_remove, .driver = { .name = "exynos-dwc3", .of_match_table = exynos_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 64c0cd1995aa..3edc5aca76f9 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -129,6 +129,16 @@ static void dwc3_imx8mp_wakeup_disable(struct dwc3_imx8mp *dwc3_imx) writel(val, dwc3_imx->hsio_blk_base + USB_WAKEUP_CTRL); } +static const struct property_entry dwc3_imx8mp_properties[] = { + PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk"), + PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk"), + {}, +}; + +static const struct software_node dwc3_imx8mp_swnode = { + .properties = dwc3_imx8mp_properties, +}; + static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx) { struct dwc3_imx8mp *dwc3_imx = _dwc3_imx; @@ -148,17 +158,6 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx) return IRQ_HANDLED; } -static int dwc3_imx8mp_set_software_node(struct device *dev) -{ - struct property_entry props[3] = { 0 }; - int prop_idx = 0; - - props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk"); - props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk"); - - return device_create_managed_software_node(dev, props, NULL); -} - static int dwc3_imx8mp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -221,17 +220,17 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) if (err < 0) goto disable_rpm; - err = dwc3_imx8mp_set_software_node(dev); + err = device_add_software_node(dev, &dwc3_imx8mp_swnode); if (err) { err = -ENODEV; - dev_err(dev, "failed to create software node\n"); + dev_err(dev, "failed to add software node\n"); goto disable_rpm; } err = of_platform_populate(node, NULL, NULL, dev); if (err) { dev_err(&pdev->dev, "failed to create dwc3 core\n"); - goto disable_rpm; + goto remove_swnode; } dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np); @@ -255,6 +254,8 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) depopulate: of_platform_depopulate(dev); +remove_swnode: + device_remove_software_node(dev); disable_rpm: pm_runtime_disable(dev); pm_runtime_put_noidle(dev); @@ -268,6 +269,7 @@ static void dwc3_imx8mp_remove(struct platform_device *pdev) pm_runtime_get_sync(dev); of_platform_depopulate(dev); + device_remove_software_node(dev); pm_runtime_disable(dev); pm_runtime_put_noidle(dev); @@ -400,7 +402,7 @@ MODULE_DEVICE_TABLE(of, dwc3_imx8mp_of_match); static struct platform_driver dwc3_imx8mp_driver = { .probe = dwc3_imx8mp_probe, - .remove_new = dwc3_imx8mp_remove, + .remove = dwc3_imx8mp_remove, .driver = { .name = "imx8mp-dwc3", .pm = pm_ptr(&dwc3_imx8mp_dev_pm_ops), diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 8899348b6276..7ee1610162b9 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -208,7 +208,7 @@ MODULE_DEVICE_TABLE(of, kdwc3_of_match); static struct platform_driver kdwc3_driver = { .probe = kdwc3_probe, - .remove_new = kdwc3_remove, + .remove = kdwc3_remove, .driver = { .name = "keystone-dwc3", .of_match_table = kdwc3_of_match, diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 2c07c038b584..7d80bf7b18b0 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -968,7 +968,7 @@ MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); static struct platform_driver dwc3_meson_g12a_driver = { .probe = dwc3_meson_g12a_probe, - .remove_new = dwc3_meson_g12a_remove, + .remove = dwc3_meson_g12a_remove, .driver = { .name = "dwc3-meson-g12a", .of_match_table = dwc3_meson_g12a_match, diff --git a/drivers/usb/dwc3/dwc3-octeon.c b/drivers/usb/dwc3/dwc3-octeon.c index 1a3b205367fd..42bfc14ae0c4 100644 --- a/drivers/usb/dwc3/dwc3-octeon.c +++ b/drivers/usb/dwc3/dwc3-octeon.c @@ -520,7 +520,7 @@ MODULE_DEVICE_TABLE(of, dwc3_octeon_of_match); static struct platform_driver dwc3_octeon_driver = { .probe = dwc3_octeon_probe, - .remove_new = dwc3_octeon_remove, + .remove = dwc3_octeon_remove, .driver = { .name = "dwc3-octeon", .of_match_table = dwc3_octeon_of_match, diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index be7be00ecb34..a4954a21be93 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -180,7 +180,7 @@ MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); static struct platform_driver dwc3_of_simple_driver = { .probe = dwc3_of_simple_probe, - .remove_new = dwc3_of_simple_remove, + .remove = dwc3_of_simple_remove, .shutdown = dwc3_of_simple_shutdown, .driver = { .name = "dwc3-of-simple", diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 2a11fc0ee84f..fe74d11bb629 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -416,7 +416,7 @@ static int dwc3_omap_extcon_register(struct dwc3_omap *omap) struct device_node *node = omap->dev->of_node; struct extcon_dev *edev; - if (of_property_read_bool(node, "extcon")) { + if (of_property_present(node, "extcon")) { edev = extcon_get_edev_by_phandle(omap->dev, 0); if (IS_ERR(edev)) { dev_vdbg(omap->dev, "couldn't get extcon device\n"); @@ -457,7 +457,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) struct dwc3_omap *omap; struct device *dev = &pdev->dev; - struct regulator *vbus_reg = NULL; + struct regulator *vbus_reg; int ret; int irq; @@ -483,12 +483,11 @@ static int dwc3_omap_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - if (of_property_read_bool(node, "vbus-supply")) { - vbus_reg = devm_regulator_get(dev, "vbus"); - if (IS_ERR(vbus_reg)) { - dev_err(dev, "vbus init failed\n"); - return PTR_ERR(vbus_reg); - } + vbus_reg = devm_regulator_get_optional(dev, "vbus"); + if (IS_ERR(vbus_reg)) { + if (PTR_ERR(vbus_reg) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(vbus_reg), "vbus init failed\n"); + vbus_reg = NULL; } omap->dev = dev; @@ -611,7 +610,7 @@ static const struct dev_pm_ops dwc3_omap_dev_pm_ops = { static struct platform_driver dwc3_omap_driver = { .probe = dwc3_omap_probe, - .remove_new = dwc3_omap_remove, + .remove = dwc3_omap_remove, .driver = { .name = "omap-dwc3", .of_match_table = of_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 052852f80146..54a4ee2b90b7 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -148,11 +148,21 @@ static const struct property_entry dwc3_pci_intel_byt_properties[] = { {} }; +/* + * Intel Merrifield SoC uses these endpoints for tracing and they cannot + * be re-allocated if being used because the side band flow control signals + * are hard wired to certain endpoints: + * - 1 High BW Bulk IN (IN#1) (RTIT) + * - 1 1KB BW Bulk IN (IN#8) + 1 1KB BW Bulk OUT (Run Control) (OUT#8) + */ +static const u8 dwc3_pci_mrfld_reserved_endpoints[] = { 3, 16, 17 }; + static const struct property_entry dwc3_pci_mrfld_properties[] = { PROPERTY_ENTRY_STRING("dr_mode", "otg"), PROPERTY_ENTRY_STRING("linux,extcon-name", "mrfld_bcove_pwrsrc"), PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"), PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"), + PROPERTY_ENTRY_U8_ARRAY("snps,reserved-endpoints", dwc3_pci_mrfld_reserved_endpoints), PROPERTY_ENTRY_BOOL("snps,usb2-gadget-lpm-disable"), PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), {} diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index c1d4b52f25b0..58683bb672e9 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -161,7 +161,7 @@ static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom) struct extcon_dev *host_edev; int ret; - if (!of_property_read_bool(dev->of_node, "extcon")) + if (!of_property_present(dev->of_node, "extcon")) return 0; qcom->edev = extcon_get_edev_by_phandle(dev, 0); @@ -921,7 +921,7 @@ MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match); static struct platform_driver dwc3_qcom_driver = { .probe = dwc3_qcom_probe, - .remove_new = dwc3_qcom_remove, + .remove = dwc3_qcom_remove, .driver = { .name = "dwc3-qcom", .pm = &dwc3_qcom_dev_pm_ops, diff --git a/drivers/usb/dwc3/dwc3-rtk.c b/drivers/usb/dwc3/dwc3-rtk.c index e9c8b032c72c..56c53e0c0257 100644 --- a/drivers/usb/dwc3/dwc3-rtk.c +++ b/drivers/usb/dwc3/dwc3-rtk.c @@ -441,7 +441,7 @@ static const struct dev_pm_ops dwc3_rtk_dev_pm_ops = { static struct platform_driver dwc3_rtk_driver = { .probe = dwc3_rtk_probe, - .remove_new = dwc3_rtk_remove, + .remove = dwc3_rtk_remove, .driver = { .name = "rtk-dwc3", .of_match_table = rtk_dwc3_match, diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index 2841021f3557..5d513decaacd 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -225,7 +225,7 @@ static int st_dwc3_probe(struct platform_device *pdev) dwc3_data->syscfg_reg_off = res->start; - dev_vdbg(dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n", + dev_vdbg(dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n", dwc3_data->glue_base, dwc3_data->syscfg_reg_off); struct device_node *child __free(device_node) = of_get_compatible_child(node, @@ -309,7 +309,6 @@ static void st_dwc3_remove(struct platform_device *pdev) reset_control_assert(dwc3_data->rstc_rst); } -#ifdef CONFIG_PM_SLEEP static int st_dwc3_suspend(struct device *dev) { struct st_dwc3 *dwc3_data = dev_get_drvdata(dev); @@ -343,9 +342,8 @@ static int st_dwc3_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume); static const struct of_device_id st_dwc3_match[] = { { .compatible = "st,stih407-dwc3" }, @@ -356,11 +354,11 @@ MODULE_DEVICE_TABLE(of, st_dwc3_match); static struct platform_driver st_dwc3_driver = { .probe = st_dwc3_probe, - .remove_new = st_dwc3_remove, + .remove = st_dwc3_remove, .driver = { .name = "usb-st-dwc3", .of_match_table = st_dwc3_match, - .pm = &st_dwc3_dev_pm_ops, + .pm = pm_sleep_ptr(&st_dwc3_dev_pm_ops), }, }; diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index b5e5be424ce9..a33a42ba0249 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -121,8 +121,11 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) * in use but the usb3-phy entry is missing from the device tree. * Therefore, skip these operations in this case. */ - if (!priv_data->usb3_phy) + if (!priv_data->usb3_phy) { + /* Deselect the PIPE Clock Select bit in FPD PIPE Clock register */ + writel(PIPE_CLK_DESELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); goto skip_usb3_phy; + } crst = devm_reset_control_get_exclusive(dev, "usb_crst"); if (IS_ERR(crst)) { @@ -420,7 +423,7 @@ static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = { static struct platform_driver dwc3_xlnx_driver = { .probe = dwc3_xlnx_probe, - .remove_new = dwc3_xlnx_remove, + .remove = dwc3_xlnx_remove, .driver = { .name = "dwc3-xilinx", .of_match_table = dwc3_xlnx_of_match, diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index c9533a99e47c..666ac432f52d 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -145,7 +145,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, * Unfortunately we have uncovered a limitation wrt the Data Phase. * * Section 9.4 says we can wait for the XferNotReady(DATA) event to - * come before issueing Start Transfer command, but if we do, we will + * come before issuing Start Transfer command, but if we do, we will * miss situations where the host starts another SETUP phase instead of * the DATA phase. Such cases happen at least on TD.7.6 of the Link * Layer Compliance Suite. @@ -232,7 +232,7 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) /* stall is always issued on EP0 */ dep = dwc->eps[0]; __dwc3_gadget_ep_set_halt(dep, 1, false); - dep->flags &= DWC3_EP_RESOURCE_ALLOCATED; + dep->flags &= DWC3_EP_RESOURCE_ALLOCATED | DWC3_EP_TRANSFER_STARTED; dep->flags |= DWC3_EP_ENABLED; dwc->delayed_status = false; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4959c26d3b71..47e73c4ed62d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -197,7 +197,6 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, list_del(&req->list); req->remaining = 0; - req->needs_extra_trb = false; req->num_trbs = 0; if (req->request.status == -EINPROGRESS) @@ -548,6 +547,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep) int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index) { struct dwc3_gadget_ep_cmd_params params; + struct dwc3_ep *dep; u32 cmd; int i; int ret; @@ -564,8 +564,13 @@ int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index) return ret; /* Reset resource allocation flags */ - for (i = resource_index; i < dwc->num_eps && dwc->eps[i]; i++) - dwc->eps[i]->flags &= ~DWC3_EP_RESOURCE_ALLOCATED; + for (i = resource_index; i < dwc->num_eps; i++) { + dep = dwc->eps[i]; + if (!dep) + continue; + + dep->flags &= ~DWC3_EP_RESOURCE_ALLOCATED; + } return 0; } @@ -688,6 +693,44 @@ static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult) } /** + * dwc3_gadget_calc_ram_depth - calculates the ram depth for txfifo + * @dwc: pointer to the DWC3 context + */ +static int dwc3_gadget_calc_ram_depth(struct dwc3 *dwc) +{ + int ram_depth; + int fifo_0_start; + bool is_single_port_ram; + + /* Check supporting RAM type by HW */ + is_single_port_ram = DWC3_SPRAM_TYPE(dwc->hwparams.hwparams1); + + /* + * If a single port RAM is utilized, then allocate TxFIFOs from + * RAM0. otherwise, allocate them from RAM1. + */ + ram_depth = is_single_port_ram ? DWC3_RAM0_DEPTH(dwc->hwparams.hwparams6) : + DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); + + /* + * In a single port RAM configuration, the available RAM is shared + * between the RX and TX FIFOs. This means that the txfifo can begin + * at a non-zero address. + */ + if (is_single_port_ram) { + u32 reg; + + /* Check if TXFIFOs start at non-zero addr */ + reg = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(reg); + + ram_depth -= (fifo_0_start >> 16); + } + + return ram_depth; +} + +/** * dwc3_gadget_clear_tx_fifos - Clears txfifo allocation * @dwc: pointer to the DWC3 context * @@ -714,9 +757,11 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc) dwc->last_fifo_depth = fifo_depth; /* Clear existing TXFIFO for all IN eps except ep0 */ - for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM); - num += 2) { + for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM); num += 2) { dep = dwc->eps[num]; + if (!dep) + continue; + /* Don't change TXFRAMNUM on usb31 version */ size = DWC3_IP_IS(DWC3) ? 0 : dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) & @@ -753,7 +798,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; int fifo_0_start; - int ram1_depth; + int ram_depth; int fifo_size; int min_depth; int num_in_ep; @@ -773,17 +818,32 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) if (dep->flags & DWC3_EP_TXFIFO_RESIZED) return 0; - ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); - - if ((dep->endpoint.maxburst > 1 && - usb_endpoint_xfer_bulk(dep->endpoint.desc)) || - usb_endpoint_xfer_isoc(dep->endpoint.desc)) - num_fifos = 3; + ram_depth = dwc3_gadget_calc_ram_depth(dwc); - if (dep->endpoint.maxburst > 6 && - (usb_endpoint_xfer_bulk(dep->endpoint.desc) || - usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31)) - num_fifos = dwc->tx_fifo_resize_max_num; + switch (dwc->gadget->speed) { + case USB_SPEED_SUPER_PLUS: + case USB_SPEED_SUPER: + if (usb_endpoint_xfer_bulk(dep->endpoint.desc) || + usb_endpoint_xfer_isoc(dep->endpoint.desc)) + num_fifos = min_t(unsigned int, + dep->endpoint.maxburst, + dwc->tx_fifo_resize_max_num); + break; + case USB_SPEED_HIGH: + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + num_fifos = min_t(unsigned int, + usb_endpoint_maxp_mult(dep->endpoint.desc) + 1, + dwc->tx_fifo_resize_max_num); + break; + } + fallthrough; + case USB_SPEED_FULL: + if (usb_endpoint_xfer_bulk(dep->endpoint.desc)) + num_fifos = 2; + break; + default: + break; + } /* FIFO size for a single buffer */ fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1); @@ -794,7 +854,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) /* Reserve at least one FIFO for the number of IN EPs */ min_depth = num_in_ep * (fifo + 1); - remaining = ram1_depth - min_depth - dwc->last_fifo_depth; + remaining = ram_depth - min_depth - dwc->last_fifo_depth; remaining = max_t(int, 0, remaining); /* * We've already reserved 1 FIFO per EP, so check what we can fit in @@ -820,9 +880,9 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size); /* Check fifo size allocation doesn't exceed available RAM size. */ - if (dwc->last_fifo_depth >= ram1_depth) { + if (dwc->last_fifo_depth >= ram_depth) { dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n", - dwc->last_fifo_depth, ram1_depth, + dwc->last_fifo_depth, ram_depth, dep->endpoint.name, fifo_size); if (DWC3_IP_IS(DWC3)) fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size); @@ -944,8 +1004,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) /* * All stream eps will reinitiate stream on NoStream - * rejection until we can determine that the host can - * prime after the first transfer. + * rejection. * * However, if the controller is capable of * TXF_FLUSH_BYPASS, then IN direction endpoints will @@ -1177,11 +1236,14 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) * pending to be processed by the driver. */ if (dep->trb_enqueue == dep->trb_dequeue) { + struct dwc3_request *req; + /* - * If there is any request remained in the started_list at - * this point, that means there is no TRB available. + * If there is any request remained in the started_list with + * active TRBs at this point, then there is no TRB available. */ - if (!list_empty(&dep->started_list)) + req = next_request(&dep->started_list); + if (req && req->num_trbs) return 0; return DWC3_TRB_NUM - 1; @@ -1384,6 +1446,7 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep, unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); unsigned int rem = req->request.length % maxp; unsigned int num_trbs = 1; + bool needs_extra_trb; if (dwc3_needs_extra_trb(dep, req)) num_trbs++; @@ -1391,15 +1454,15 @@ static int dwc3_prepare_last_sg(struct dwc3_ep *dep, if (dwc3_calc_trbs_left(dep) < num_trbs) return 0; - req->needs_extra_trb = num_trbs > 1; + needs_extra_trb = num_trbs > 1; /* Prepare a normal TRB */ if (req->direction || req->request.length) dwc3_prepare_one_trb(dep, req, entry_length, - req->needs_extra_trb, node, false, false); + needs_extra_trb, node, false, false); /* Prepare extra TRBs for ZLP and MPS OUT transfer alignment */ - if ((!req->direction && !req->request.length) || req->needs_extra_trb) + if ((!req->direction && !req->request.length) || needs_extra_trb) dwc3_prepare_one_trb(dep, req, req->direction ? 0 : maxp - rem, false, 1, true, false); @@ -1414,8 +1477,8 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, struct scatterlist *s; int i; unsigned int length = req->request.length; - unsigned int remaining = req->request.num_mapped_sgs - - req->num_queued_sgs; + unsigned int remaining = req->num_pending_sgs; + unsigned int num_queued_sgs = req->request.num_mapped_sgs - remaining; unsigned int num_trbs = req->num_trbs; bool needs_extra_trb = dwc3_needs_extra_trb(dep, req); @@ -1423,7 +1486,7 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, * If we resume preparing the request, then get the remaining length of * the request and resume where we left off. */ - for_each_sg(req->request.sg, s, req->num_queued_sgs, i) + for_each_sg(req->request.sg, s, num_queued_sgs, i) length -= sg_dma_len(s); for_each_sg(sg, s, remaining, i) { @@ -1488,7 +1551,6 @@ static int dwc3_prepare_trbs_sg(struct dwc3_ep *dep, if (!last_sg) req->start_sg = sg_next(s); - req->num_queued_sgs++; req->num_pending_sgs--; /* @@ -1569,9 +1631,7 @@ static int dwc3_prepare_trbs(struct dwc3_ep *dep) if (ret) return ret; - req->sg = req->request.sg; - req->start_sg = req->sg; - req->num_queued_sgs = 0; + req->start_sg = req->request.sg; req->num_pending_sgs = req->request.num_mapped_sgs; if (req->num_pending_sgs > 0) { @@ -1919,12 +1979,12 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) return -ESHUTDOWN; } - if (WARN(req->dep != dep, "request %pK belongs to '%s'\n", + if (WARN(req->dep != dep, "request %p belongs to '%s'\n", &req->request, req->dep->name)) return -EINVAL; if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED, - "%s: request %pK already in flight\n", + "%s: request %p already in flight\n", dep->name, &req->request)) return -EINVAL; @@ -2113,7 +2173,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, } } - dev_err(dwc->dev, "request %pK was not queued to %s\n", + dev_err(dwc->dev, "request %p was not queued to %s\n", request, ep->name); ret = -EINVAL; out: @@ -2577,10 +2637,38 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) { u32 reg; u32 timeout = 2000; + u32 saved_config = 0; if (pm_runtime_suspended(dwc->dev)) return 0; + /* + * When operating in USB 2.0 speeds (HS/FS), ensure that + * GUSB2PHYCFG.ENBLSLPM and GUSB2PHYCFG.SUSPHY are cleared before starting + * or stopping the controller. This resolves timeout issues that occur + * during frequent role switches between host and device modes. + * + * Save and clear these settings, then restore them after completing the + * controller start or stop sequence. + * + * This solution was discovered through experimentation as it is not + * mentioned in the dwc3 programming guide. It has been tested on an + * Exynos platforms. + */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + if (reg & DWC3_GUSB2PHYCFG_SUSPHY) { + saved_config |= DWC3_GUSB2PHYCFG_SUSPHY; + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + } + + if (reg & DWC3_GUSB2PHYCFG_ENBLSLPM) { + saved_config |= DWC3_GUSB2PHYCFG_ENBLSLPM; + reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; + } + + if (saved_config) + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) { @@ -2608,6 +2696,12 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) reg &= DWC3_DSTS_DEVCTRLHLT; } while (--timeout && !(!is_on ^ !reg)); + if (saved_config) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= saved_config; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } + if (!timeout) return -ETIMEDOUT; @@ -2687,6 +2781,8 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) __dwc3_gadget_stop(dwc); spin_unlock_irqrestore(&dwc->lock, flags); + usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED); + return ret; } @@ -3075,7 +3171,7 @@ static int dwc3_gadget_check_config(struct usb_gadget *g) struct dwc3 *dwc = gadget_to_dwc(g); struct usb_ep *ep; int fifo_size = 0; - int ram1_depth; + int ram_depth; int ep_num = 0; if (!dwc->do_fifo_resize) @@ -3098,8 +3194,8 @@ static int dwc3_gadget_check_config(struct usb_gadget *g) fifo_size += dwc->max_cfg_eps; /* Check if we can fit a single fifo per endpoint */ - ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7); - if (fifo_size > ram1_depth) + ram_depth = dwc3_gadget_calc_ram_depth(dwc); + if (fifo_size > ram_depth) return -ENOMEM; return 0; @@ -3245,6 +3341,50 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep) return dwc3_alloc_trb_pool(dep); } +#define nostream_work_to_dep(w) (container_of(to_delayed_work(w), struct dwc3_ep, nostream_work)) +static void dwc3_nostream_work(struct work_struct *work) +{ + struct dwc3_ep *dep = nostream_work_to_dep(work); + struct dwc3 *dwc = dep->dwc; + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + if (dep->flags & DWC3_EP_STREAM_PRIMED) + goto out; + + if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) || + (!DWC3_MST_CAPABLE(&dwc->hwparams) && + !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE))) + goto out; + /* + * If the host rejects a stream due to no active stream, by the + * USB and xHCI spec, the endpoint will be put back to idle + * state. When the host is ready (buffer added/updated), it will + * prime the endpoint to inform the usb device controller. This + * triggers the device controller to issue ERDY to restart the + * stream. However, some hosts don't follow this and keep the + * endpoint in the idle state. No prime will come despite host + * streams are updated, and the device controller will not be + * triggered to generate ERDY to move the next stream data. To + * workaround this and maintain compatibility with various + * hosts, force to reinitiate the stream until the host is ready + * instead of waiting for the host to prime the endpoint. + */ + if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) { + unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME; + + dwc3_send_gadget_generic_command(dwc, cmd, dep->number); + } else { + dep->flags |= DWC3_EP_DELAY_START; + dwc3_stop_active_transfer(dep, true, true); + spin_unlock_irqrestore(&dwc->lock, flags); + return; + } +out: + dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM; + spin_unlock_irqrestore(&dwc->lock, flags); +} + static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) { struct dwc3_ep *dep; @@ -3290,20 +3430,60 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) INIT_LIST_HEAD(&dep->pending_list); INIT_LIST_HEAD(&dep->started_list); INIT_LIST_HEAD(&dep->cancelled_list); + INIT_DELAYED_WORK(&dep->nostream_work, dwc3_nostream_work); dwc3_debugfs_create_endpoint_dir(dep); return 0; } +static int dwc3_gadget_get_reserved_endpoints(struct dwc3 *dwc, const char *propname, + u8 *eps, u8 num) +{ + u8 count; + int ret; + + if (!device_property_present(dwc->dev, propname)) + return 0; + + ret = device_property_count_u8(dwc->dev, propname); + if (ret < 0) + return ret; + count = ret; + + ret = device_property_read_u8_array(dwc->dev, propname, eps, min(num, count)); + if (ret) + return ret; + + return count; +} + static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total) { + const char *propname = "snps,reserved-endpoints"; u8 epnum; + u8 reserved_eps[DWC3_ENDPOINTS_NUM]; + u8 count; + u8 num; + int ret; INIT_LIST_HEAD(&dwc->gadget->ep_list); + ret = dwc3_gadget_get_reserved_endpoints(dwc, propname, + reserved_eps, ARRAY_SIZE(reserved_eps)); + if (ret < 0) { + dev_err(dwc->dev, "failed to read %s\n", propname); + return ret; + } + count = ret; + for (epnum = 0; epnum < total; epnum++) { - int ret; + for (num = 0; num < count; num++) { + if (epnum == reserved_eps[num]) + break; + } + if (num < count) + continue; ret = dwc3_gadget_init_endpoint(dwc, epnum); if (ret) @@ -3416,20 +3596,16 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep, int status) { struct dwc3_trb *trb; - struct scatterlist *sg = req->sg; - struct scatterlist *s; - unsigned int num_queued = req->num_queued_sgs; + unsigned int num_completed_trbs = req->num_trbs; unsigned int i; int ret = 0; - for_each_sg(sg, s, num_queued, i) { + for (i = 0; i < num_completed_trbs; i++) { trb = &dep->trb_pool[dep->trb_dequeue]; - req->sg = sg_next(s); - req->num_queued_sgs--; - ret = dwc3_gadget_ep_reclaim_completed_trb(dep, req, - trb, event, status, true); + trb, event, status, + !!(trb->ctrl & DWC3_TRB_CTRL_CHN)); if (ret) break; } @@ -3437,19 +3613,9 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep, return ret; } -static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep, - struct dwc3_request *req, const struct dwc3_event_depevt *event, - int status) -{ - struct dwc3_trb *trb = &dep->trb_pool[dep->trb_dequeue]; - - return dwc3_gadget_ep_reclaim_completed_trb(dep, req, trb, - event, status, false); -} - static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req) { - return req->num_pending_sgs == 0 && req->num_queued_sgs == 0; + return req->num_pending_sgs == 0 && req->num_trbs == 0; } static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, @@ -3459,24 +3625,13 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep, int request_status; int ret; - if (req->request.num_mapped_sgs) - ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event, - status); - else - ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, - status); + ret = dwc3_gadget_ep_reclaim_trb_sg(dep, req, event, status); req->request.actual = req->request.length - req->remaining; if (!dwc3_gadget_ep_request_completed(req)) goto out; - if (req->needs_extra_trb) { - ret = dwc3_gadget_ep_reclaim_trb_linear(dep, req, event, - status); - req->needs_extra_trb = false; - } - /* * The event status only reflects the status of the TRB with IOC set. * For the requests that don't set interrupt on completion, the driver @@ -3595,6 +3750,8 @@ out: for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { dep = dwc->eps[i]; + if (!dep) + continue; if (!(dep->flags & DWC3_EP_ENABLED)) continue; @@ -3714,66 +3871,27 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep, static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep, const struct dwc3_event_depevt *event) { - struct dwc3 *dwc = dep->dwc; - if (event->status == DEPEVT_STREAMEVT_FOUND) { - dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED; - goto out; + cancel_delayed_work(&dep->nostream_work); + dep->flags |= DWC3_EP_STREAM_PRIMED; + dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM; + return; } /* Note: NoStream rejection event param value is 0 and not 0xFFFF */ switch (event->parameters) { case DEPEVT_STREAM_PRIME: - /* - * If the host can properly transition the endpoint state from - * idle to prime after a NoStream rejection, there's no need to - * force restarting the endpoint to reinitiate the stream. To - * simplify the check, assume the host follows the USB spec if - * it primed the endpoint more than once. - */ - if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) { - if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED) - dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM; - else - dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED; - } - + cancel_delayed_work(&dep->nostream_work); + dep->flags |= DWC3_EP_STREAM_PRIMED; + dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM; break; case DEPEVT_STREAM_NOSTREAM: - if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) || - !(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) || - (!DWC3_MST_CAPABLE(&dwc->hwparams) && - !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE))) - break; - - /* - * If the host rejects a stream due to no active stream, by the - * USB and xHCI spec, the endpoint will be put back to idle - * state. When the host is ready (buffer added/updated), it will - * prime the endpoint to inform the usb device controller. This - * triggers the device controller to issue ERDY to restart the - * stream. However, some hosts don't follow this and keep the - * endpoint in the idle state. No prime will come despite host - * streams are updated, and the device controller will not be - * triggered to generate ERDY to move the next stream data. To - * workaround this and maintain compatibility with various - * hosts, force to reinitiate the stream until the host is ready - * instead of waiting for the host to prime the endpoint. - */ - if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) { - unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME; - - dwc3_send_gadget_generic_command(dwc, cmd, dep->number); - } else { - dep->flags |= DWC3_EP_DELAY_START; - dwc3_stop_active_transfer(dep, true, true); - return; - } + dep->flags &= ~DWC3_EP_STREAM_PRIMED; + if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) + queue_delayed_work(system_wq, &dep->nostream_work, + msecs_to_jiffies(100)); break; } - -out: - dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM; } static void dwc3_endpoint_interrupt(struct dwc3 *dwc, @@ -3783,6 +3901,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, u8 epnum = event->endpoint_number; dep = dwc->eps[epnum]; + if (!dep) { + dev_warn(dwc->dev, "spurious event, endpoint %u is not allocated\n", epnum); + return; + } if (!(dep->flags & DWC3_EP_ENABLED)) { if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED)) @@ -4167,8 +4289,10 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) WARN_ONCE(DWC3_VER_IS_PRIOR(DWC3, 240A) && dwc->has_lpm_erratum, "LPM Erratum not available on dwc3 revisions < 2.40a\n"); - if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A)) + if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A)) { + reg &= ~DWC3_DCTL_NYET_THRES_MASK; reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold); + } dwc3_gadget_dctl_write_safe(dwc, reg); } else { @@ -4430,14 +4554,18 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt) dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_SIZE(evt->length)); + evt->flags &= ~DWC3_EVENT_PENDING; + /* + * Add an explicit write memory barrier to make sure that the update of + * clearing DWC3_EVENT_PENDING is observed in dwc3_check_event_buf() + */ + wmb(); + if (dwc->imod_interval) { dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval); } - /* Keep the clearing of DWC3_EVENT_PENDING at the end */ - evt->flags &= ~DWC3_EVENT_PENDING; - return ret; } diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index e0533cee6870..b48e108fc8fe 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -35,7 +35,7 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) u32 reg; int i; - /* xhci regs is not mapped yet, do it temperary here */ + /* xhci regs are not mapped yet, do it temporarily here */ if (dwc->xhci_resources[0].start) { xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); if (!xhci_regs) { diff --git a/drivers/usb/fotg210/fotg210-core.c b/drivers/usb/fotg210/fotg210-core.c index 0655afe7f977..7fb4d4715e9f 100644 --- a/drivers/usb/fotg210/fotg210-core.c +++ b/drivers/usb/fotg210/fotg210-core.c @@ -13,6 +13,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regmap.h> +#include <linux/string_choices.h> #include <linux/usb.h> #include <linux/usb/otg.h> @@ -119,8 +120,8 @@ void fotg210_vbus(struct fotg210 *fotg, bool enable) ret = regmap_update_bits(fotg->map, GEMINI_GLOBAL_MISC_CTRL, mask, val); if (ret) dev_err(fotg->dev, "failed to %s VBUS\n", - enable ? "enable" : "disable"); - dev_info(fotg->dev, "%s: %s VBUS\n", __func__, enable ? "enable" : "disable"); + str_enable_disable(enable)); + dev_info(fotg->dev, "%s: %s VBUS\n", __func__, str_enable_disable(enable)); } static int fotg210_probe(struct platform_device *pdev) @@ -195,7 +196,7 @@ static struct platform_driver fotg210_driver = { .of_match_table = of_match_ptr(fotg210_of_match), }, .probe = fotg210_probe, - .remove_new = fotg210_remove, + .remove = fotg210_remove, }; static int __init fotg210_init(void) diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c index 3d404d19a205..64c4965a160f 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/fotg210/fotg210-hcd.c @@ -4901,8 +4901,7 @@ static int hcd_fotg210_init(struct usb_hcd *hcd) */ fotg210->need_io_watchdog = 1; - hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - fotg210->hrtimer.function = fotg210_hrtimer_func; + hrtimer_setup(&fotg210->hrtimer, fotg210_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT; hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 566ff0b1282a..76521555e3c1 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -211,6 +211,8 @@ config USB_F_MIDI config USB_F_MIDI2 tristate + select SND_UMP + select SND_UMP_LEGACY_RAWMIDI config USB_F_HID tristate @@ -445,8 +447,6 @@ config USB_CONFIGFS_F_MIDI2 depends on USB_CONFIGFS depends on SND select USB_LIBCOMPOSITE - select SND_UMP - select SND_UMP_LEGACY_RAWMIDI select USB_F_MIDI2 help The MIDI 2.0 function driver provides the generic emulated diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f25dd2cb5d03..869ad99afb48 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1050,10 +1050,11 @@ static int set_config(struct usb_composite_dev *cdev, else usb_gadget_set_remote_wakeup(gadget, 0); done: - if (power <= USB_SELF_POWER_VBUS_MAX_DRAW) - usb_gadget_set_selfpowered(gadget); - else + if (power > USB_SELF_POWER_VBUS_MAX_DRAW || + (c && !(c->bmAttributes & USB_CONFIG_ATT_SELFPOWER))) usb_gadget_clear_selfpowered(gadget); + else + usb_gadget_set_selfpowered(gadget); usb_gadget_vbus_draw(gadget, power); if (result >= 0 && cdev->delayed_status) @@ -1844,7 +1845,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) cdev->desc.bcdUSB = cpu_to_le16(0x0200); } - value = min(w_length, (u16) sizeof cdev->desc); + value = min_t(u16, w_length, sizeof(cdev->desc)); memcpy(req->buf, &cdev->desc, value); break; case USB_DT_DEVICE_QUALIFIER: @@ -1863,19 +1864,19 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_CONFIG: value = config_desc(cdev, w_value); if (value >= 0) - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); break; case USB_DT_STRING: value = get_string(cdev, req->buf, w_index, w_value & 0xff); if (value >= 0) - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); break; case USB_DT_BOS: if (gadget_is_superspeed(gadget) || gadget->lpm_capable || cdev->use_webusb) { value = bos_desc(cdev); - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); } break; case USB_DT_OTG: @@ -1930,7 +1931,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) *(u8 *)req->buf = cdev->config->bConfigurationValue; else *(u8 *)req->buf = 0; - value = min(w_length, (u16) 1); + value = min_t(u16, w_length, 1); break; /* function drivers must handle get/set altsetting */ @@ -1976,7 +1977,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (value < 0) break; *((u8 *)req->buf) = value; - value = min(w_length, (u16) 1); + value = min_t(u16, w_length, 1); break; case USB_REQ_GET_STATUS: if (gadget_is_otg(gadget) && gadget->hnp_polling_support && @@ -2111,8 +2112,20 @@ unknown: memset(buf, 0, w_length); buf[5] = 0x01; switch (ctrl->bRequestType & USB_RECIP_MASK) { + /* + * The Microsoft CompatID OS Descriptor Spec(w_index = 0x4) and + * Extended Prop OS Desc Spec(w_index = 0x5) state that the + * HighByte of wValue is the InterfaceNumber and the LowByte is + * the PageNumber. This high/low byte ordering is incorrectly + * documented in the Spec. USB analyzer output on the below + * request packets show the high/low byte inverted i.e LowByte + * is the InterfaceNumber and the HighByte is the PageNumber. + * Since we dont support >64KB CompatID/ExtendedProp descriptors, + * PageNumber is set to 0. Hence verify that the HighByte is 0 + * for below two cases. + */ case USB_RECIP_DEVICE: - if (w_index != 0x4 || (w_value & 0xff)) + if (w_index != 0x4 || (w_value >> 8)) break; buf[6] = w_index; /* Number of ext compat interfaces */ @@ -2128,9 +2141,9 @@ unknown: } break; case USB_RECIP_INTERFACE: - if (w_index != 0x5 || (w_value & 0xff)) + if (w_index != 0x5 || (w_value >> 8)) break; - interface = w_value >> 8; + interface = w_value & 0xFF; if (interface >= MAX_CONFIG_INTERFACES || !os_desc_cfg->interface[interface]) break; @@ -2603,7 +2616,10 @@ void composite_suspend(struct usb_gadget *gadget) cdev->suspended = 1; - usb_gadget_set_selfpowered(gadget); + if (cdev->config && + cdev->config->bmAttributes & USB_CONFIG_ATT_SELFPOWER) + usb_gadget_set_selfpowered(gadget); + usb_gadget_vbus_draw(gadget, 2); } @@ -2637,8 +2653,11 @@ void composite_resume(struct usb_gadget *gadget) else maxpower = min(maxpower, 900U); - if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW) + if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW || + !(cdev->config->bmAttributes & USB_CONFIG_ATT_SELFPOWER)) usb_gadget_clear_selfpowered(gadget); + else + usb_gadget_set_selfpowered(gadget); usb_gadget_vbus_draw(gadget, maxpower); } else { diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index b1f625245713..95f144a54ed9 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -57,11 +57,11 @@ EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf); * usb_gadget_config_buf - builts a complete configuration descriptor * @config: Header for the descriptor, including characteristics such * as power requirements and number of interfaces. - * @desc: Null-terminated vector of pointers to the descriptors (interface, - * endpoint, etc) defining all functions in this device configuration. * @buf: Buffer for the resulting configuration descriptor. * @length: Length of buffer. If this is not big enough to hold the * entire configuration descriptor, an error code will be returned. + * @desc: Null-terminated vector of pointers to the descriptors (interface, + * endpoint, etc) defining all functions in this device configuration. * * This copies descriptors into the response buffer, building a descriptor * for that configuration. It returns the buffer length or a negative diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index c82a6a0fba93..fba2a56dae97 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -827,11 +827,15 @@ static ssize_t gadget_string_s_store(struct config_item *item, const char *page, { struct gadget_string *string = to_gadget_string(item); int size = min(sizeof(string->string), len + 1); + ssize_t cpy_len; if (len > USB_MAX_STRING_LEN) return -EINVAL; - return strscpy(string->string, page, size); + cpy_len = strscpy(string->string, page, size); + if (cpy_len > 0 && string->string[cpy_len - 1] == '\n') + string->string[cpy_len - 1] = 0; + return len; } CONFIGFS_ATTR(gadget_string_, s); @@ -1184,7 +1188,7 @@ static ssize_t os_desc_qw_sign_store(struct config_item *item, const char *page, struct gadget_info *gi = os_desc_item_to_gadget_info(item); int res, l; - l = min((int)len, OS_STRING_QW_SIGN_LEN >> 1); + l = min_t(int, len, OS_STRING_QW_SIGN_LEN >> 1); if (page[l - 1] == '\n') --l; diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 87917a7d4a9b..7ce1637276f0 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -41,6 +41,10 @@ obj-$(CONFIG_USB_F_UAC1_LEGACY) += usb_f_uac1_legacy.o usb_f_uac2-y := f_uac2.o obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o +ifneq ($(CONFIG_TRACING),) + CFLAGS_uvc_trace.o := -I$(src) + usb_f_uvc-y += uvc_trace.o +endif obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o usb_f_midi-y := f_midi.o obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 6cb7771e8a69..80841de845b0 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/etherdevice.h> +#include <linux/string_choices.h> #include "u_ether.h" #include "u_ether_configfs.h" @@ -387,8 +388,7 @@ static void ecm_do_notify(struct f_ecm *ecm) event->wLength = 0; req->length = sizeof *event; - DBG(cdev, "notify connect %s\n", - ecm->is_open ? "true" : "false"); + DBG(cdev, "notify connect %s\n", str_true_false(ecm->is_open)); ecm->notify_state = ECM_NOTIFY_SPEED; break; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 2920f8000bbd..2dea9e42a0f8 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -49,7 +49,7 @@ #define DMABUF_ENQUEUE_TIMEOUT_MS 5000 -MODULE_IMPORT_NS(DMA_BUF); +MODULE_IMPORT_NS("DMA_BUF"); /* Reference counter handling */ static void ffs_data_get(struct ffs_data *ffs); @@ -456,7 +456,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf, } /* FFS_SETUP_PENDING and not stall */ - len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength)); + len = min_t(size_t, len, le16_to_cpu(ffs->ev.setup.wLength)); spin_unlock_irq(&ffs->ev.waitq.lock); @@ -590,7 +590,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, /* unlocks spinlock */ return __ffs_ep0_read_events(ffs, buf, - min(n, (size_t)ffs->ev.count)); + min_t(size_t, n, ffs->ev.count)); case FFS_SETUP_PENDING: if (ffs->ev.setup.bRequestType & USB_DIR_IN) { @@ -599,7 +599,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, goto done_mutex; } - len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength)); + len = min_t(size_t, len, le16_to_cpu(ffs->ev.setup.wLength)); spin_unlock_irq(&ffs->ev.waitq.lock); @@ -2285,7 +2285,7 @@ static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev) struct usb_gadget_strings **lang; int first_id; - if (WARN_ON(ffs->state != FFS_ACTIVE + if ((ffs->state != FFS_ACTIVE || test_and_set_bit(FFS_FL_BOUND, &ffs->flags))) return -EBADFD; diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 08e0d1c511e8..94d478b6bcd3 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -500,7 +500,7 @@ static int fsg_setup(struct usb_function *f, *(u8 *)req->buf = _fsg_common_get_max_lun(fsg->common); /* Respond with data/status */ - req->length = min((u16)1, w_length); + req->length = min_t(u16, 1, w_length); return ep0_queue(fsg->common); } @@ -655,7 +655,7 @@ static int do_read(struct fsg_common *common) * And don't try to read past the end of the file. */ amount = min(amount_left, FSG_BUFLEN); - amount = min((loff_t)amount, + amount = min_t(loff_t, amount, curlun->file_length - file_offset); /* Wait for the next buffer to become available */ @@ -1005,7 +1005,7 @@ static int do_verify(struct fsg_common *common) * And don't try to read past the end of the file. */ amount = min(amount_left, FSG_BUFLEN); - amount = min((loff_t)amount, + amount = min_t(loff_t, amount, curlun->file_length - file_offset); if (amount == 0) { curlun->sense_data = @@ -2142,8 +2142,8 @@ static int do_scsi_command(struct fsg_common *common) * of Posix locks. */ case FORMAT_UNIT: - case RELEASE: - case RESERVE: + case RELEASE_6: + case RESERVE_6: case SEND_DIAGNOSTIC: default: @@ -2167,7 +2167,7 @@ unknown_cmnd: if (reply == -EINVAL) reply = 0; /* Error reply length */ if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { - reply = min((u32)reply, common->data_size_from_cmnd); + reply = min_t(u32, reply, common->data_size_from_cmnd); bh->inreq->length = reply; bh->state = BUF_STATE_FULL; common->residue -= reply; diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 1067847cc079..da82598fcef8 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -283,7 +283,7 @@ f_midi_complete(struct usb_ep *ep, struct usb_request *req) /* Our transmit completed. See if there's more to go. * f_midi_transmit eats req, don't queue it again. */ req->length = 0; - f_midi_transmit(midi); + queue_work(system_highpri_wq, &midi->work); return; } break; @@ -819,9 +819,9 @@ static int f_midi_register_card(struct f_midi *midi) goto fail; } - strcpy(card->driver, f_midi_longname); - strcpy(card->longname, f_midi_longname); - strcpy(card->shortname, f_midi_shortname); + strscpy(card->driver, f_midi_longname); + strscpy(card->longname, f_midi_longname); + strscpy(card->shortname, f_midi_shortname); /* Set up rawmidi */ snd_component_add(card, "MIDI"); @@ -833,7 +833,7 @@ static int f_midi_register_card(struct f_midi *midi) } midi->rmidi = rmidi; midi->in_last_port = 0; - strcpy(rmidi->name, card->shortname); + strscpy(rmidi->name, card->shortname); rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; @@ -907,6 +907,15 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) status = -ENODEV; + /* + * Reset wMaxPacketSize with maximum packet size of FS bulk transfer before + * endpoint claim. This ensures that the wMaxPacketSize does not exceed the + * limit during bind retries where configured dwc3 TX/RX FIFO's maxpacket + * size of 512 bytes for IN/OUT endpoints in support HS speed only. + */ + bulk_in_desc.wMaxPacketSize = cpu_to_le16(64); + bulk_out_desc.wMaxPacketSize = cpu_to_le16(64); + /* allocate instance-specific endpoints */ midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc); if (!midi->in_ep) @@ -1000,11 +1009,11 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) } /* configure the endpoint descriptors ... */ - ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports); - ms_out_desc.bNumEmbMIDIJack = midi->in_ports; + ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports); + ms_out_desc.bNumEmbMIDIJack = midi->out_ports; - ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports); - ms_in_desc.bNumEmbMIDIJack = midi->out_ports; + ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports); + ms_in_desc.bNumEmbMIDIJack = midi->in_ports; /* ... and add them to the list */ endpoint_descriptor_index = i; diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index 8285df9ed6fd..12e866fb311d 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -1285,10 +1285,8 @@ static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf, if (alt == 0) op_mode = MIDI_OP_MODE_MIDI1; - else if (alt == 1) - op_mode = MIDI_OP_MODE_MIDI2; else - op_mode = MIDI_OP_MODE_UNSET; + op_mode = MIDI_OP_MODE_MIDI2; if (midi2->operation_mode == op_mode) return 0; @@ -1593,7 +1591,11 @@ static int f_midi2_create_card(struct f_midi2 *midi2) fb->info.midi_ci_version = b->midi_ci_version; fb->info.ui_hint = reverse_dir(b->ui_hint); fb->info.sysex8_streams = b->sysex8_streams; - fb->info.flags |= b->is_midi1; + if (b->is_midi1 < 2) + fb->info.flags |= b->is_midi1; + else + fb->info.flags |= SNDRV_UMP_BLOCK_IS_MIDI1 | + SNDRV_UMP_BLOCK_IS_LOWSPEED; strscpy(fb->info.name, ump_fb_name(b), sizeof(fb->info.name)); } diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 8e761249d672..58b0dd575af3 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -17,6 +17,7 @@ #include <linux/device.h> #include <linux/etherdevice.h> #include <linux/crc32.h> +#include <linux/string_choices.h> #include <linux/usb/cdc.h> @@ -558,7 +559,7 @@ static void ncm_do_notify(struct f_ncm *ncm) req->length = sizeof *event; DBG(cdev, "notify connect %s\n", - ncm->is_open ? "true" : "false"); + str_true_false(ncm->is_open)); ncm->notify_state = NCM_NOTIFY_NONE; break; @@ -1558,8 +1559,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ncm->port.open = ncm_open; ncm->port.close = ncm_close; - hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - ncm->task_timer.function = ncm_tx_timeout; + hrtimer_setup(&ncm->task_timer, ncm_tx_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n", ncm->port.in_ep->name, ncm->port.out_ep->name, diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 15bb3aa12aa8..5a2e1237f85c 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -12,6 +12,7 @@ #include <linux/string.h> #include <linux/configfs.h> #include <linux/ctype.h> +#include <linux/delay.h> #include <linux/usb/ch9.h> #include <linux/usb/composite.h> #include <linux/usb/gadget.h> @@ -50,7 +51,7 @@ static int bot_enqueue_cmd_cbw(struct f_uas *fu) if (fu->flags & USBG_BOT_CMD_PEND) return 0; - ret = usb_ep_queue(fu->ep_out, fu->cmd.req, GFP_ATOMIC); + ret = usb_ep_queue(fu->ep_out, fu->cmd[0].req, GFP_ATOMIC); if (!ret) fu->flags |= USBG_BOT_CMD_PEND; return ret; @@ -62,10 +63,11 @@ static void bot_status_complete(struct usb_ep *ep, struct usb_request *req) struct f_uas *fu = cmd->fu; transport_generic_free_cmd(&cmd->se_cmd, 0); - if (req->status < 0) { - pr_err("ERR %s(%d)\n", __func__, __LINE__); + if (req->status == -ESHUTDOWN) return; - } + + if (req->status < 0) + pr_err("ERR %s(%d)\n", __func__, __LINE__); /* CSW completed, wait for next CBW */ bot_enqueue_cmd_cbw(fu); @@ -136,7 +138,7 @@ static void bot_send_bad_status(struct usbg_cmd *cmd) } req->complete = bot_err_compl; req->context = cmd; - req->buf = fu->cmd.buf; + req->buf = fu->cmd[0].buf; usb_ep_queue(ep, req, GFP_KERNEL); } else { bot_enqueue_sense_code(fu, cmd); @@ -196,6 +198,11 @@ static void bot_read_compl(struct usb_ep *ep, struct usb_request *req) if (req->status < 0) pr_err("ERR %s(%d)\n", __func__, __LINE__); + if (req->status == -ESHUTDOWN) { + transport_generic_free_cmd(&cmd->se_cmd, 0); + return; + } + bot_send_status(cmd, true); } @@ -244,11 +251,8 @@ static int usbg_prepare_w_request(struct usbg_cmd *, struct usb_request *); static int bot_send_write_request(struct usbg_cmd *cmd) { struct f_uas *fu = cmd->fu; - struct se_cmd *se_cmd = &cmd->se_cmd; - struct usb_gadget *gadget = fuas_to_gadget(fu); int ret; - init_completion(&cmd->write_complete); cmd->fu = fu; if (!cmd->data_len) { @@ -256,22 +260,6 @@ static int bot_send_write_request(struct usbg_cmd *cmd) return -EINVAL; } - if (!gadget->sg_supported) { - cmd->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL); - if (!cmd->data_buf) - return -ENOMEM; - - fu->bot_req_out->buf = cmd->data_buf; - } else { - fu->bot_req_out->buf = NULL; - fu->bot_req_out->num_sgs = se_cmd->t_data_nents; - fu->bot_req_out->sg = se_cmd->t_data_sg; - } - - fu->bot_req_out->complete = usbg_data_write_cmpl; - fu->bot_req_out->length = se_cmd->data_length; - fu->bot_req_out->context = cmd; - ret = usbg_prepare_w_request(cmd, fu->bot_req_out); if (ret) goto cleanup; @@ -279,8 +267,6 @@ static int bot_send_write_request(struct usbg_cmd *cmd) if (ret) pr_err("%s(%d)\n", __func__, __LINE__); - wait_for_completion(&cmd->write_complete); - target_execute_cmd(se_cmd); cleanup: return ret; } @@ -292,14 +278,31 @@ static void bot_cmd_complete(struct usb_ep *ep, struct usb_request *req) struct f_uas *fu = req->context; int ret; + if (req->status == -ESHUTDOWN) + return; + fu->flags &= ~USBG_BOT_CMD_PEND; - if (req->status < 0) + if (req->status < 0) { + struct usb_gadget *gadget = fuas_to_gadget(fu); + + dev_err(&gadget->dev, "BOT command req err (%d)\n", req->status); + bot_enqueue_cmd_cbw(fu); return; + } ret = bot_submit_command(fu, req->buf, req->actual); - if (ret) + if (ret) { pr_err("%s(%d): %d\n", __func__, __LINE__, ret); + if (!(fu->flags & USBG_BOT_WEDGED)) + usb_ep_set_wedge(fu->ep_in); + + fu->flags |= USBG_BOT_WEDGED; + bot_enqueue_cmd_cbw(fu); + } else if (fu->flags & USBG_BOT_WEDGED) { + fu->flags &= ~USBG_BOT_WEDGED; + usb_ep_clear_halt(fu->ep_in); + } } static int bot_prepare_reqs(struct f_uas *fu) @@ -314,8 +317,8 @@ static int bot_prepare_reqs(struct f_uas *fu) if (!fu->bot_req_out) goto err_out; - fu->cmd.req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL); - if (!fu->cmd.req) + fu->cmd[0].req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL); + if (!fu->cmd[0].req) goto err_cmd; fu->bot_status.req = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL); @@ -327,27 +330,27 @@ static int bot_prepare_reqs(struct f_uas *fu) fu->bot_status.req->complete = bot_status_complete; fu->bot_status.csw.Signature = cpu_to_le32(US_BULK_CS_SIGN); - fu->cmd.buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL); - if (!fu->cmd.buf) + fu->cmd[0].buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL); + if (!fu->cmd[0].buf) goto err_buf; - fu->cmd.req->complete = bot_cmd_complete; - fu->cmd.req->buf = fu->cmd.buf; - fu->cmd.req->length = fu->ep_out->maxpacket; - fu->cmd.req->context = fu; + fu->cmd[0].req->complete = bot_cmd_complete; + fu->cmd[0].req->buf = fu->cmd[0].buf; + fu->cmd[0].req->length = fu->ep_out->maxpacket; + fu->cmd[0].req->context = fu; ret = bot_enqueue_cmd_cbw(fu); if (ret) goto err_queue; return 0; err_queue: - kfree(fu->cmd.buf); - fu->cmd.buf = NULL; + kfree(fu->cmd[0].buf); + fu->cmd[0].buf = NULL; err_buf: usb_ep_free_request(fu->ep_in, fu->bot_status.req); err_sts: - usb_ep_free_request(fu->ep_out, fu->cmd.req); - fu->cmd.req = NULL; + usb_ep_free_request(fu->ep_out, fu->cmd[0].req); + fu->cmd[0].req = NULL; err_cmd: usb_ep_free_request(fu->ep_out, fu->bot_req_out); fu->bot_req_out = NULL; @@ -372,16 +375,16 @@ static void bot_cleanup_old_alt(struct f_uas *fu) usb_ep_free_request(fu->ep_in, fu->bot_req_in); usb_ep_free_request(fu->ep_out, fu->bot_req_out); - usb_ep_free_request(fu->ep_out, fu->cmd.req); + usb_ep_free_request(fu->ep_out, fu->cmd[0].req); usb_ep_free_request(fu->ep_in, fu->bot_status.req); - kfree(fu->cmd.buf); + kfree(fu->cmd[0].buf); fu->bot_req_in = NULL; fu->bot_req_out = NULL; - fu->cmd.req = NULL; + fu->cmd[0].req = NULL; fu->bot_status.req = NULL; - fu->cmd.buf = NULL; + fu->cmd[0].buf = NULL; } static void bot_set_alt(struct f_uas *fu) @@ -441,14 +444,10 @@ static int usbg_bot_setup(struct usb_function *f, pr_err("No LUNs configured?\n"); return -EINVAL; } - /* - * If 4 LUNs are present we return 3 i.e. LUN 0..3 can be - * accessed. The upper limit is 0xf - */ luns--; - if (luns > 0xf) { + if (luns > US_BULK_MAX_LUN_LIMIT) { pr_info_once("Limiting the number of luns to 16\n"); - luns = 0xf; + luns = US_BULK_MAX_LUN_LIMIT; } ret_lun = cdev->req->buf; *ret_lun = luns; @@ -457,6 +456,11 @@ static int usbg_bot_setup(struct usb_function *f, case US_BULK_RESET_REQUEST: /* XXX maybe we should remove previous requests for IN + OUT */ + if (fu->flags & USBG_BOT_WEDGED) { + fu->flags &= ~USBG_BOT_WEDGED; + usb_ep_clear_halt(fu->ep_in); + } + bot_enqueue_cmd_cbw(fu); return 0; } @@ -465,6 +469,45 @@ static int usbg_bot_setup(struct usb_function *f, /* Start uas.c code */ +static int tcm_to_uasp_response(enum tcm_tmrsp_table code) +{ + switch (code) { + case TMR_FUNCTION_FAILED: + return RC_TMF_FAILED; + case TMR_FUNCTION_COMPLETE: + case TMR_TASK_DOES_NOT_EXIST: + return RC_TMF_COMPLETE; + case TMR_LUN_DOES_NOT_EXIST: + return RC_INCORRECT_LUN; + case TMR_FUNCTION_REJECTED: + case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED: + default: + return RC_TMF_NOT_SUPPORTED; + } +} + +static unsigned char uasp_to_tcm_func(int code) +{ + switch (code) { + case TMF_ABORT_TASK: + return TMR_ABORT_TASK; + case TMF_ABORT_TASK_SET: + return TMR_ABORT_TASK_SET; + case TMF_CLEAR_TASK_SET: + return TMR_CLEAR_TASK_SET; + case TMF_LOGICAL_UNIT_RESET: + return TMR_LUN_RESET; + case TMF_CLEAR_ACA: + return TMR_CLEAR_ACA; + case TMF_I_T_NEXUS_RESET: + case TMF_QUERY_TASK: + case TMF_QUERY_TASK_SET: + case TMF_QUERY_ASYNC_EVENT: + default: + return TMR_UNKNOWN; + } +} + static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream) { /* We have either all three allocated or none */ @@ -482,10 +525,14 @@ static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream) static void uasp_free_cmdreq(struct f_uas *fu) { - usb_ep_free_request(fu->ep_cmd, fu->cmd.req); - kfree(fu->cmd.buf); - fu->cmd.req = NULL; - fu->cmd.buf = NULL; + int i; + + for (i = 0; i < USBG_NUM_CMDS; i++) { + usb_ep_free_request(fu->ep_cmd, fu->cmd[i].req); + kfree(fu->cmd[i].buf); + fu->cmd[i].req = NULL; + fu->cmd[i].buf = NULL; + } } static void uasp_cleanup_old_alt(struct f_uas *fu) @@ -500,7 +547,7 @@ static void uasp_cleanup_old_alt(struct f_uas *fu) usb_ep_disable(fu->ep_status); usb_ep_disable(fu->ep_cmd); - for (i = 0; i < UASP_SS_EP_COMP_NUM_STREAMS; i++) + for (i = 0; i < USBG_NUM_CMDS; i++) uasp_cleanup_one_stream(fu, &fu->stream[i]); uasp_free_cmdreq(fu); } @@ -512,7 +559,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd) struct se_cmd *se_cmd = &cmd->se_cmd; struct f_uas *fu = cmd->fu; struct usb_gadget *gadget = fuas_to_gadget(fu); - struct uas_stream *stream = cmd->stream; + struct uas_stream *stream = &fu->stream[se_cmd->map_tag]; if (!gadget->sg_supported) { cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC); @@ -532,6 +579,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd) } stream->req_in->is_last = 1; + stream->req_in->stream_id = cmd->tag; stream->req_in->complete = uasp_status_data_cmpl; stream->req_in->length = se_cmd->data_length; stream->req_in->context = cmd; @@ -544,7 +592,7 @@ static void uasp_prepare_status(struct usbg_cmd *cmd) { struct se_cmd *se_cmd = &cmd->se_cmd; struct sense_iu *iu = &cmd->sense_iu; - struct uas_stream *stream = cmd->stream; + struct uas_stream *stream = &cmd->fu->stream[se_cmd->map_tag]; cmd->state = UASP_QUEUE_COMMAND; iu->iu_id = IU_ID_STATUS; @@ -556,20 +604,76 @@ static void uasp_prepare_status(struct usbg_cmd *cmd) iu->len = cpu_to_be16(se_cmd->scsi_sense_length); iu->status = se_cmd->scsi_status; stream->req_status->is_last = 1; + stream->req_status->stream_id = cmd->tag; stream->req_status->context = cmd; stream->req_status->length = se_cmd->scsi_sense_length + 16; stream->req_status->buf = iu; stream->req_status->complete = uasp_status_data_cmpl; } +static void uasp_prepare_response(struct usbg_cmd *cmd) +{ + struct se_cmd *se_cmd = &cmd->se_cmd; + struct response_iu *rsp_iu = &cmd->response_iu; + struct uas_stream *stream = &cmd->fu->stream[se_cmd->map_tag]; + + cmd->state = UASP_QUEUE_COMMAND; + rsp_iu->iu_id = IU_ID_RESPONSE; + rsp_iu->tag = cpu_to_be16(cmd->tag); + + if (cmd->tmr_rsp != RC_RESPONSE_UNKNOWN) + rsp_iu->response_code = cmd->tmr_rsp; + else + rsp_iu->response_code = + tcm_to_uasp_response(se_cmd->se_tmr_req->response); + + /* + * The UASP driver must support all the task management functions listed + * in Table 20 of UAS-r04. To remain compliant while indicate that the + * TMR did not go through, report RC_TMF_FAILED instead of + * RC_TMF_NOT_SUPPORTED and print a warning to the user. + */ + switch (cmd->tmr_func) { + case TMF_ABORT_TASK: + case TMF_ABORT_TASK_SET: + case TMF_CLEAR_TASK_SET: + case TMF_LOGICAL_UNIT_RESET: + case TMF_CLEAR_ACA: + case TMF_I_T_NEXUS_RESET: + case TMF_QUERY_TASK: + case TMF_QUERY_TASK_SET: + case TMF_QUERY_ASYNC_EVENT: + if (rsp_iu->response_code == RC_TMF_NOT_SUPPORTED) { + struct usb_gadget *gadget = fuas_to_gadget(cmd->fu); + + dev_warn(&gadget->dev, "TMF function %d not supported\n", + cmd->tmr_func); + rsp_iu->response_code = RC_TMF_FAILED; + } + break; + default: + break; + } + + stream->req_status->is_last = 1; + stream->req_status->stream_id = cmd->tag; + stream->req_status->context = cmd; + stream->req_status->length = sizeof(struct response_iu); + stream->req_status->buf = rsp_iu; + stream->req_status->complete = uasp_status_data_cmpl; +} + +static void usbg_release_cmd(struct se_cmd *se_cmd); +static int uasp_send_tm_response(struct usbg_cmd *cmd); + static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req) { struct usbg_cmd *cmd = req->context; - struct uas_stream *stream = cmd->stream; struct f_uas *fu = cmd->fu; + struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag]; int ret; - if (req->status < 0) + if (req->status == -ESHUTDOWN) goto cleanup; switch (cmd->state) { @@ -600,8 +704,37 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req) break; case UASP_QUEUE_COMMAND: - transport_generic_free_cmd(&cmd->se_cmd, 0); - usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC); + /* + * Overlapped command detected and cancelled. + * So send overlapped attempted status. + */ + if (cmd->tmr_rsp == RC_OVERLAPPED_TAG && + req->status == -ECONNRESET) { + uasp_send_tm_response(cmd); + return; + } + + hash_del(&stream->node); + + /* + * If no command submitted to target core here, just free the + * bitmap index. This is for the cases where f_tcm handles + * status response instead of the target core. + */ + if (cmd->tmr_rsp != RC_OVERLAPPED_TAG && + cmd->tmr_rsp != RC_RESPONSE_UNKNOWN) { + struct se_session *se_sess; + + se_sess = fu->tpg->tpg_nexus->tvn_se_sess; + sbitmap_queue_clear(&se_sess->sess_tag_pool, + cmd->se_cmd.map_tag, + cmd->se_cmd.map_cpu); + } else { + transport_generic_free_cmd(&cmd->se_cmd, 0); + } + + usb_ep_queue(fu->ep_cmd, cmd->req, GFP_ATOMIC); + complete(&stream->cmd_completion); break; default: @@ -610,27 +743,38 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req) return; cleanup: + hash_del(&stream->node); transport_generic_free_cmd(&cmd->se_cmd, 0); } static int uasp_send_status_response(struct usbg_cmd *cmd) { struct f_uas *fu = cmd->fu; - struct uas_stream *stream = cmd->stream; + struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag]; struct sense_iu *iu = &cmd->sense_iu; iu->tag = cpu_to_be16(cmd->tag); - stream->req_status->complete = uasp_status_data_cmpl; - stream->req_status->context = cmd; cmd->fu = fu; uasp_prepare_status(cmd); return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC); } +static int uasp_send_tm_response(struct usbg_cmd *cmd) +{ + struct f_uas *fu = cmd->fu; + struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag]; + struct response_iu *iu = &cmd->response_iu; + + iu->tag = cpu_to_be16(cmd->tag); + cmd->fu = fu; + uasp_prepare_response(cmd); + return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC); +} + static int uasp_send_read_response(struct usbg_cmd *cmd) { struct f_uas *fu = cmd->fu; - struct uas_stream *stream = cmd->stream; + struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag]; struct sense_iu *iu = &cmd->sense_iu; int ret; @@ -674,11 +818,10 @@ static int uasp_send_write_request(struct usbg_cmd *cmd) { struct f_uas *fu = cmd->fu; struct se_cmd *se_cmd = &cmd->se_cmd; - struct uas_stream *stream = cmd->stream; + struct uas_stream *stream = &fu->stream[se_cmd->map_tag]; struct sense_iu *iu = &cmd->sense_iu; int ret; - init_completion(&cmd->write_complete); cmd->fu = fu; iu->tag = cpu_to_be16(cmd->tag); @@ -710,36 +853,31 @@ static int uasp_send_write_request(struct usbg_cmd *cmd) pr_err("%s(%d)\n", __func__, __LINE__); } - wait_for_completion(&cmd->write_complete); - target_execute_cmd(se_cmd); cleanup: return ret; } -static int usbg_submit_command(struct f_uas *, void *, unsigned int); +static int usbg_submit_command(struct f_uas *, struct usb_request *); static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req) { struct f_uas *fu = req->context; - int ret; - if (req->status < 0) + if (req->status == -ESHUTDOWN) return; - ret = usbg_submit_command(fu, req->buf, req->actual); - /* - * Once we tune for performance enqueue the command req here again so - * we can receive a second command while we processing this one. Pay - * attention to properly sync STAUS endpoint with DATA IN + OUT so you - * don't break HS. - */ - if (!ret) + if (req->status < 0) { + usb_ep_queue(fu->ep_cmd, req, GFP_ATOMIC); return; - usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC); + } + + usbg_submit_command(fu, req); } static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream) { + init_completion(&stream->cmd_completion); + stream->req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL); if (!stream->req_in) goto out; @@ -764,66 +902,48 @@ out: return -ENOMEM; } -static int uasp_alloc_cmd(struct f_uas *fu) +static int uasp_alloc_cmd(struct f_uas *fu, int i) { - fu->cmd.req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL); - if (!fu->cmd.req) + fu->cmd[i].req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL); + if (!fu->cmd[i].req) goto err; - fu->cmd.buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL); - if (!fu->cmd.buf) + fu->cmd[i].buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL); + if (!fu->cmd[i].buf) goto err_buf; - fu->cmd.req->complete = uasp_cmd_complete; - fu->cmd.req->buf = fu->cmd.buf; - fu->cmd.req->length = fu->ep_cmd->maxpacket; - fu->cmd.req->context = fu; + fu->cmd[i].req->complete = uasp_cmd_complete; + fu->cmd[i].req->buf = fu->cmd[i].buf; + fu->cmd[i].req->length = fu->ep_cmd->maxpacket; + fu->cmd[i].req->context = fu; return 0; err_buf: - usb_ep_free_request(fu->ep_cmd, fu->cmd.req); + usb_ep_free_request(fu->ep_cmd, fu->cmd[i].req); err: return -ENOMEM; } -static void uasp_setup_stream_res(struct f_uas *fu, int max_streams) -{ - int i; - - for (i = 0; i < max_streams; i++) { - struct uas_stream *s = &fu->stream[i]; - - s->req_in->stream_id = i + 1; - s->req_out->stream_id = i + 1; - s->req_status->stream_id = i + 1; - } -} - static int uasp_prepare_reqs(struct f_uas *fu) { int ret; int i; - int max_streams; - if (fu->flags & USBG_USE_STREAMS) - max_streams = UASP_SS_EP_COMP_NUM_STREAMS; - else - max_streams = 1; - - for (i = 0; i < max_streams; i++) { + for (i = 0; i < USBG_NUM_CMDS; i++) { ret = uasp_alloc_stream_res(fu, &fu->stream[i]); if (ret) goto err_cleanup; } - ret = uasp_alloc_cmd(fu); - if (ret) - goto err_free_stream; - uasp_setup_stream_res(fu, max_streams); + for (i = 0; i < USBG_NUM_CMDS; i++) { + ret = uasp_alloc_cmd(fu, i); + if (ret) + goto err_free_stream; - ret = usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC); - if (ret) - goto err_free_stream; + ret = usb_ep_queue(fu->ep_cmd, fu->cmd[i].req, GFP_ATOMIC); + if (ret) + goto err_free_stream; + } return 0; @@ -914,6 +1034,8 @@ static int get_cmd_dir(const unsigned char *cdb) case READ_TOC: case READ_FORMAT_CAPACITIES: case REQUEST_SENSE: + case ATA_12: + case ATA_16: ret = DMA_FROM_DEVICE; break; @@ -957,7 +1079,18 @@ static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req) struct usbg_cmd *cmd = req->context; struct se_cmd *se_cmd = &cmd->se_cmd; - if (req->status < 0) { + cmd->state = UASP_QUEUE_COMMAND; + + if (req->status == -ESHUTDOWN) { + struct uas_stream *stream = &cmd->fu->stream[se_cmd->map_tag]; + + hash_del(&stream->node); + target_put_sess_cmd(se_cmd); + transport_generic_free_cmd(&cmd->se_cmd, 0); + return; + } + + if (req->status) { pr_err("%s() state %d transfer failed\n", __func__, cmd->state); goto cleanup; } @@ -969,11 +1102,22 @@ static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req) se_cmd->data_length); } - complete(&cmd->write_complete); + cmd->flags |= USBG_CMD_PENDING_DATA_WRITE; + queue_work(cmd->fu->tpg->workqueue, &cmd->work); return; cleanup: - transport_generic_free_cmd(&cmd->se_cmd, 0); + target_put_sess_cmd(se_cmd); + + /* Command was aborted due to overlapped tag */ + if (cmd->state == UASP_QUEUE_COMMAND && + cmd->tmr_rsp == RC_OVERLAPPED_TAG) { + uasp_send_tm_response(cmd); + return; + } + + transport_send_check_condition_and_sense(se_cmd, + TCM_CHECK_CONDITION_ABORT_CMD, 0); } static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req) @@ -995,9 +1139,12 @@ static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req) } req->is_last = 1; + req->stream_id = cmd->tag; req->complete = usbg_data_write_cmpl; req->length = se_cmd->data_length; req->context = cmd; + + cmd->state = UASP_SEND_STATUS; return 0; } @@ -1037,36 +1184,153 @@ static int usbg_send_read_response(struct se_cmd *se_cmd) return uasp_send_read_response(cmd); } -static void usbg_cmd_work(struct work_struct *work) +static void usbg_aborted_task(struct se_cmd *se_cmd); + +static void usbg_submit_tmr(struct usbg_cmd *cmd) +{ + struct se_session *se_sess; + struct se_cmd *se_cmd; + int flags = TARGET_SCF_ACK_KREF; + + se_cmd = &cmd->se_cmd; + se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess; + + target_submit_tmr(se_cmd, se_sess, + cmd->response_iu.add_response_info, + cmd->unpacked_lun, NULL, uasp_to_tcm_func(cmd->tmr_func), + GFP_ATOMIC, cmd->tag, flags); +} + +static void usbg_submit_cmd(struct usbg_cmd *cmd) { - struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work); struct se_cmd *se_cmd; struct tcm_usbg_nexus *tv_nexus; struct usbg_tpg *tpg; int dir, flags = (TARGET_SCF_UNKNOWN_SIZE | TARGET_SCF_ACK_KREF); + /* + * Note: each command will spawn its own process, and each stage of the + * command is processed sequentially. Should this no longer be the case, + * locking is needed. + */ + if (cmd->flags & USBG_CMD_PENDING_DATA_WRITE) { + target_execute_cmd(&cmd->se_cmd); + cmd->flags &= ~USBG_CMD_PENDING_DATA_WRITE; + return; + } + se_cmd = &cmd->se_cmd; tpg = cmd->fu->tpg; tv_nexus = tpg->tpg_nexus; dir = get_cmd_dir(cmd->cmd_buf); - if (dir < 0) { - __target_init_cmd(se_cmd, - tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo, - tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE, - cmd->prio_attr, cmd->sense_iu.sense, - cmd->unpacked_lun, NULL); + if (dir < 0) goto out; - } target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun, 0, cmd->prio_attr, dir, flags); + return; out: + __target_init_cmd(se_cmd, + tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo, + tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE, + cmd->prio_attr, cmd->sense_iu.sense, + cmd->unpacked_lun, NULL); transport_send_check_condition_and_sense(se_cmd, - TCM_UNSUPPORTED_SCSI_OPCODE, 1); - transport_generic_free_cmd(&cmd->se_cmd, 0); + TCM_UNSUPPORTED_SCSI_OPCODE, 0); +} + +static void usbg_cmd_work(struct work_struct *work) +{ + struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work); + + /* + * Failure is detected by f_tcm here. Skip submitting the command to the + * target core if we already know the failing response and send the usb + * response to the host directly. + */ + if (cmd->tmr_rsp != RC_RESPONSE_UNKNOWN) + goto skip; + + if (cmd->tmr_func) + usbg_submit_tmr(cmd); + else + usbg_submit_cmd(cmd); + + return; + +skip: + if (cmd->tmr_rsp == RC_OVERLAPPED_TAG) { + struct f_uas *fu = cmd->fu; + struct se_session *se_sess; + struct uas_stream *stream = NULL; + struct hlist_node *tmp; + struct usbg_cmd *active_cmd = NULL; + + se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess; + + hash_for_each_possible_safe(fu->stream_hash, stream, tmp, node, cmd->tag) { + int i = stream - &fu->stream[0]; + + active_cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[i]; + if (active_cmd->tag == cmd->tag) + break; + } + + /* Sanity check */ + if (!stream || (active_cmd && active_cmd->tag != cmd->tag)) { + usbg_submit_command(cmd->fu, cmd->req); + return; + } + + reinit_completion(&stream->cmd_completion); + + /* + * A UASP command consists of the command, data, and status + * stages, each operating sequentially from different endpoints. + * + * Each USB endpoint operates independently, and depending on + * hardware implementation, a completion callback for a transfer + * from one endpoint may not reflect the order of completion on + * the wire. This is particularly true for devices with + * endpoints that have independent interrupts and event buffers. + * + * The driver must still detect misbehaving hosts and respond + * with an overlap status. To reduce false overlap failures, + * allow the active and matching stream ID a brief 1ms to + * complete before responding with an overlap command failure. + * Overlap failure should be rare. + */ + wait_for_completion_timeout(&stream->cmd_completion, msecs_to_jiffies(1)); + + /* If the previous stream is completed, retry the command. */ + if (!hash_hashed(&stream->node)) { + usbg_submit_command(cmd->fu, cmd->req); + return; + } + + /* + * The command isn't submitted to the target core, so we're safe + * to remove the bitmap index from the session tag pool. + */ + sbitmap_queue_clear(&se_sess->sess_tag_pool, + cmd->se_cmd.map_tag, + cmd->se_cmd.map_cpu); + + /* + * Overlap command tag detected. Cancel any pending transfer of + * the command submitted to target core. + */ + active_cmd->tmr_rsp = RC_OVERLAPPED_TAG; + usbg_aborted_task(&active_cmd->se_cmd); + + /* Send the response after the transfer is aborted. */ + return; + } + + uasp_send_tm_response(cmd); } static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu, @@ -1084,6 +1348,7 @@ static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu, memset(cmd, 0, sizeof(*cmd)); cmd->se_cmd.map_tag = tag; cmd->se_cmd.map_cpu = cpu; + cmd->se_cmd.cpuid = cpu; cmd->se_cmd.tag = cmd->tag = scsi_tag; cmd->fu = fu; @@ -1092,50 +1357,82 @@ static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu, static void usbg_release_cmd(struct se_cmd *); -static int usbg_submit_command(struct f_uas *fu, - void *cmdbuf, unsigned int len) +static int usbg_submit_command(struct f_uas *fu, struct usb_request *req) { - struct command_iu *cmd_iu = cmdbuf; + struct iu *iu = req->buf; struct usbg_cmd *cmd; struct usbg_tpg *tpg = fu->tpg; struct tcm_usbg_nexus *tv_nexus; + struct uas_stream *stream; + struct hlist_node *tmp; + struct command_iu *cmd_iu; u32 cmd_len; u16 scsi_tag; - if (cmd_iu->iu_id != IU_ID_COMMAND) { - pr_err("Unsupported type %d\n", cmd_iu->iu_id); - return -EINVAL; - } - tv_nexus = tpg->tpg_nexus; if (!tv_nexus) { pr_err("Missing nexus, ignoring command\n"); return -EINVAL; } - cmd_len = (cmd_iu->len & ~0x3) + 16; - if (cmd_len > USBG_MAX_CMD) - return -EINVAL; - - scsi_tag = be16_to_cpup(&cmd_iu->tag); + scsi_tag = be16_to_cpup(&iu->tag); cmd = usbg_get_cmd(fu, tv_nexus, scsi_tag); if (IS_ERR(cmd)) { pr_err("usbg_get_cmd failed\n"); return -ENOMEM; } - memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len); - if (fu->flags & USBG_USE_STREAMS) { - if (cmd->tag > UASP_SS_EP_COMP_NUM_STREAMS) - goto err; - if (!cmd->tag) - cmd->stream = &fu->stream[0]; - else - cmd->stream = &fu->stream[cmd->tag - 1]; - } else { - cmd->stream = &fu->stream[0]; + cmd->req = req; + cmd->fu = fu; + cmd->tag = scsi_tag; + cmd->se_cmd.tag = scsi_tag; + cmd->tmr_func = 0; + cmd->tmr_rsp = RC_RESPONSE_UNKNOWN; + cmd->flags = 0; + + cmd_iu = (struct command_iu *)iu; + + /* Command and Task Management IUs share the same LUN offset */ + cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun); + + if (iu->iu_id != IU_ID_COMMAND && iu->iu_id != IU_ID_TASK_MGMT) { + cmd->tmr_rsp = RC_INVALID_INFO_UNIT; + goto skip; + } + + hash_for_each_possible_safe(fu->stream_hash, stream, tmp, node, scsi_tag) { + struct usbg_cmd *active_cmd; + struct se_session *se_sess; + int i = stream - &fu->stream[0]; + + se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess; + active_cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[i]; + + if (active_cmd->tag == scsi_tag) { + cmd->tmr_rsp = RC_OVERLAPPED_TAG; + goto skip; + } } + stream = &fu->stream[cmd->se_cmd.map_tag]; + hash_add(fu->stream_hash, &stream->node, scsi_tag); + + if (iu->iu_id == IU_ID_TASK_MGMT) { + struct task_mgmt_iu *tm_iu; + + tm_iu = (struct task_mgmt_iu *)iu; + cmd->tmr_func = tm_iu->function; + goto skip; + } + + cmd_len = (cmd_iu->len & ~0x3) + 16; + if (cmd_len > USBG_MAX_CMD) { + target_free_tag(tv_nexus->tvn_se_sess, &cmd->se_cmd); + hash_del(&stream->node); + return -EINVAL; + } + memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len); + switch (cmd_iu->prio_attr & 0x7) { case UAS_HEAD_TAG: cmd->prio_attr = TCM_HEAD_TAG; @@ -1155,15 +1452,11 @@ static int usbg_submit_command(struct f_uas *fu, break; } - cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun); - +skip: INIT_WORK(&cmd->work, usbg_cmd_work); queue_work(tpg->workqueue, &cmd->work); return 0; -err: - usbg_release_cmd(&cmd->se_cmd); - return -EINVAL; } static void bot_cmd_work(struct work_struct *work) @@ -1172,30 +1465,40 @@ static void bot_cmd_work(struct work_struct *work) struct se_cmd *se_cmd; struct tcm_usbg_nexus *tv_nexus; struct usbg_tpg *tpg; + int flags = TARGET_SCF_ACK_KREF; int dir; + /* + * Note: each command will spawn its own process, and each stage of the + * command is processed sequentially. Should this no longer be the case, + * locking is needed. + */ + if (cmd->flags & USBG_CMD_PENDING_DATA_WRITE) { + target_execute_cmd(&cmd->se_cmd); + cmd->flags &= ~USBG_CMD_PENDING_DATA_WRITE; + return; + } + se_cmd = &cmd->se_cmd; tpg = cmd->fu->tpg; tv_nexus = tpg->tpg_nexus; dir = get_cmd_dir(cmd->cmd_buf); - if (dir < 0) { - __target_init_cmd(se_cmd, - tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo, - tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE, - cmd->prio_attr, cmd->sense_iu.sense, - cmd->unpacked_lun, NULL); + if (dir < 0) goto out; - } target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun, - cmd->data_len, cmd->prio_attr, dir, 0); + cmd->data_len, cmd->prio_attr, dir, flags); return; out: + __target_init_cmd(se_cmd, + tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo, + tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE, + cmd->prio_attr, cmd->sense_iu.sense, + cmd->unpacked_lun, NULL); transport_send_check_condition_and_sense(se_cmd, - TCM_UNSUPPORTED_SCSI_OPCODE, 1); - transport_generic_free_cmd(&cmd->se_cmd, 0); + TCM_UNSUPPORTED_SCSI_OPCODE, 0); } static int bot_submit_command(struct f_uas *fu, @@ -1239,6 +1542,7 @@ static int bot_submit_command(struct f_uas *fu, cmd->is_read = cbw->Flags & US_BULK_FLAG_IN ? 1 : 0; cmd->data_len = le32_to_cpu(cbw->DataTransferLength); cmd->se_cmd.tag = le32_to_cpu(cmd->bot_tag); + cmd->flags = 0; INIT_WORK(&cmd->work, bot_cmd_work); queue_work(tpg->workqueue, &cmd->work); @@ -1275,16 +1579,38 @@ static void usbg_release_cmd(struct se_cmd *se_cmd) se_cmd); struct se_session *se_sess = se_cmd->se_sess; + cmd->tag = 0; kfree(cmd->data_buf); target_free_tag(se_sess, se_cmd); } static void usbg_queue_tm_rsp(struct se_cmd *se_cmd) { + struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, se_cmd); + + uasp_send_tm_response(cmd); } static void usbg_aborted_task(struct se_cmd *se_cmd) { + struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, se_cmd); + struct f_uas *fu = cmd->fu; + struct usb_gadget *gadget = fuas_to_gadget(fu); + struct uas_stream *stream = &fu->stream[se_cmd->map_tag]; + int ret = 0; + + if (stream->req_out->status == -EINPROGRESS) + ret = usb_ep_dequeue(fu->ep_out, stream->req_out); + else if (stream->req_in->status == -EINPROGRESS) + ret = usb_ep_dequeue(fu->ep_in, stream->req_in); + else if (stream->req_status->status == -EINPROGRESS) + ret = usb_ep_dequeue(fu->ep_status, stream->req_status); + + if (ret) + dev_err(&gadget->dev, "Failed to abort cmd tag %d, (%d)\n", + cmd->tag, ret); + + cmd->state = UASP_QUEUE_COMMAND; } static const char *usbg_check_wwn(const char *name) @@ -1355,7 +1681,8 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn, goto unref_dep; mutex_init(&tpg->tpg_mutex); atomic_set(&tpg->tpg_port_count, 0); - tpg->workqueue = alloc_workqueue("tcm_usb_gadget", 0, 1); + tpg->workqueue = alloc_workqueue("tcm_usb_gadget", + WQ_UNBOUND, WQ_UNBOUND_MAX_ACTIVE); if (!tpg->workqueue) goto free_tpg; @@ -1746,7 +2073,7 @@ static struct usb_endpoint_descriptor uasp_ss_bi_desc = { static struct usb_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = { .bLength = sizeof(uasp_bi_ep_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - .bMaxBurst = 0, + .bMaxBurst = 15, .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS, .wBytesPerInterval = 0, }; @@ -1754,7 +2081,7 @@ static struct usb_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = { static struct usb_ss_ep_comp_descriptor bot_bi_ep_comp_desc = { .bLength = sizeof(bot_bi_ep_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - .bMaxBurst = 0, + .bMaxBurst = 15, }; static struct usb_endpoint_descriptor uasp_bo_desc = { @@ -1789,12 +2116,14 @@ static struct usb_endpoint_descriptor uasp_ss_bo_desc = { static struct usb_ss_ep_comp_descriptor uasp_bo_ep_comp_desc = { .bLength = sizeof(uasp_bo_ep_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 15, .bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS, }; static struct usb_ss_ep_comp_descriptor bot_bo_ep_comp_desc = { .bLength = sizeof(bot_bo_ep_comp_desc), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + .bMaxBurst = 15, }; static struct usb_endpoint_descriptor uasp_status_desc = { @@ -1971,43 +2300,39 @@ static int tcm_bind(struct usb_configuration *c, struct usb_function *f) bot_intf_desc.bInterfaceNumber = iface; uasp_intf_desc.bInterfaceNumber = iface; fu->iface = iface; - ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bi_desc, - &uasp_bi_ep_comp_desc); + ep = usb_ep_autoconfig(gadget, &uasp_fs_bi_desc); if (!ep) goto ep_fail; fu->ep_in = ep; - ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bo_desc, - &uasp_bo_ep_comp_desc); + ep = usb_ep_autoconfig(gadget, &uasp_fs_bo_desc); if (!ep) goto ep_fail; fu->ep_out = ep; - ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_desc, - &uasp_status_in_ep_comp_desc); + ep = usb_ep_autoconfig(gadget, &uasp_fs_status_desc); if (!ep) goto ep_fail; fu->ep_status = ep; - ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_cmd_desc, - &uasp_cmd_comp_desc); + ep = usb_ep_autoconfig(gadget, &uasp_fs_cmd_desc); if (!ep) goto ep_fail; fu->ep_cmd = ep; /* Assume endpoint addresses are the same for both speeds */ - uasp_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress; - uasp_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress; + uasp_bi_desc.bEndpointAddress = uasp_fs_bi_desc.bEndpointAddress; + uasp_bo_desc.bEndpointAddress = uasp_fs_bo_desc.bEndpointAddress; uasp_status_desc.bEndpointAddress = - uasp_ss_status_desc.bEndpointAddress; - uasp_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress; + uasp_fs_status_desc.bEndpointAddress; + uasp_cmd_desc.bEndpointAddress = uasp_fs_cmd_desc.bEndpointAddress; - uasp_fs_bi_desc.bEndpointAddress = uasp_ss_bi_desc.bEndpointAddress; - uasp_fs_bo_desc.bEndpointAddress = uasp_ss_bo_desc.bEndpointAddress; - uasp_fs_status_desc.bEndpointAddress = - uasp_ss_status_desc.bEndpointAddress; - uasp_fs_cmd_desc.bEndpointAddress = uasp_ss_cmd_desc.bEndpointAddress; + uasp_ss_bi_desc.bEndpointAddress = uasp_fs_bi_desc.bEndpointAddress; + uasp_ss_bo_desc.bEndpointAddress = uasp_fs_bo_desc.bEndpointAddress; + uasp_ss_status_desc.bEndpointAddress = + uasp_fs_status_desc.bEndpointAddress; + uasp_ss_cmd_desc.bEndpointAddress = uasp_fs_cmd_desc.bEndpointAddress; ret = usb_assign_descriptors(f, uasp_fs_function_desc, uasp_hs_function_desc, uasp_ss_function_desc, @@ -2051,9 +2376,14 @@ static void tcm_delayed_set_alt(struct work_struct *wq) static int tcm_get_alt(struct usb_function *f, unsigned intf) { - if (intf == bot_intf_desc.bInterfaceNumber) + struct f_uas *fu = to_f_uas(f); + + if (fu->iface != intf) + return -EOPNOTSUPP; + + if (fu->flags & USBG_IS_BOT) return USB_G_ALT_INT_BBB; - if (intf == uasp_intf_desc.bInterfaceNumber) + else if (fu->flags & USBG_IS_UAS) return USB_G_ALT_INT_UAS; return -EOPNOTSUPP; @@ -2063,6 +2393,9 @@ static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_uas *fu = to_f_uas(f); + if (fu->iface != intf) + return -EOPNOTSUPP; + if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) { struct guas_setup_wq *work; @@ -2271,6 +2604,8 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi) fu->function.disable = tcm_disable; fu->function.free_func = tcm_free; fu->tpg = tpg_instances[i].tpg; + + hash_init(fu->stream_hash); mutex_unlock(&tpg_instances_lock); return &fu->function; diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index ce5b77f89190..9b324821c93b 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1185,6 +1185,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) uac2->as_in_alt = 0; } + std_ac_if_desc.bNumEndpoints = 0; if (FUOUT_EN(uac2_opts) || FUIN_EN(uac2_opts)) { uac2->int_ep = usb_ep_autoconfig(gadget, &fs_ep_int_desc); if (!uac2->int_ep) { diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 40187b7112e7..aa6ab666741a 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -465,7 +465,7 @@ uvc_register_video(struct uvc_device *uvc) memcpy(mem, desc, (desc)->bLength); \ *(dst)++ = mem; \ mem += (desc)->bLength; \ - } while (0); + } while (0) #define UVC_COPY_DESCRIPTORS(mem, dst, src) \ do { \ @@ -991,6 +991,8 @@ static void uvc_function_unbind(struct usb_configuration *c, uvcg_info(f, "%s()\n", __func__); + kthread_cancel_work_sync(&video->hw_submit); + if (video->async_wq) destroy_workqueue(video->async_wq); diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h index ced5d2b09234..11ac785d5eee 100644 --- a/drivers/usb/gadget/function/storage_common.h +++ b/drivers/usb/gadget/function/storage_common.h @@ -131,7 +131,7 @@ static inline bool fsg_lun_is_open(struct fsg_lun *curlun) #define FSG_BUFLEN ((u32)16384) /* Maximal number of LUNs supported in mass storage function */ -#define FSG_MAX_LUNS 16 +#define FSG_MAX_LUNS (US_BULK_MAX_LUN_LIMIT + 1) enum fsg_buffer_state { BUF_STATE_SENDING = -2, diff --git a/drivers/usb/gadget/function/tcm.h b/drivers/usb/gadget/function/tcm.h index 3cd565794ad7..009974d81d66 100644 --- a/drivers/usb/gadget/function/tcm.h +++ b/drivers/usb/gadget/function/tcm.h @@ -4,6 +4,7 @@ #include <linux/kref.h> /* #include <linux/usb/uas.h> */ +#include <linux/hashtable.h> #include <linux/usb/composite.h> #include <linux/usb/uas.h> #include <linux/usb/storage.h> @@ -13,9 +14,11 @@ #define USBG_NAMELEN 32 #define fuas_to_gadget(f) (f->function.config->cdev->gadget) -#define UASP_SS_EP_COMP_LOG_STREAMS 4 +#define UASP_SS_EP_COMP_LOG_STREAMS 5 #define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS) +#define USBG_NUM_CMDS (UASP_SS_EP_COMP_NUM_STREAMS + 1) + enum { USB_G_STR_INT_UAS = 0, USB_G_STR_INT_BBB, @@ -24,7 +27,7 @@ enum { #define USB_G_ALT_INT_BBB 0 #define USB_G_ALT_INT_UAS 1 -#define USB_G_DEFAULT_SESSION_TAGS 128 +#define USB_G_DEFAULT_SESSION_TAGS USBG_NUM_CMDS struct tcm_usbg_nexus { struct se_session *tvn_se_sess; @@ -72,15 +75,23 @@ struct usbg_cmd { struct se_cmd se_cmd; void *data_buf; /* used if no sg support available */ struct f_uas *fu; - struct completion write_complete; struct kref ref; + struct usb_request *req; + + u32 flags; +#define USBG_CMD_PENDING_DATA_WRITE BIT(0) + /* UAS only */ u16 tag; u16 prio_attr; struct sense_iu sense_iu; + struct response_iu response_iu; enum uas_state state; - struct uas_stream *stream; + + int tmr_func; + int tmr_rsp; +#define RC_RESPONSE_UNKNOWN 0xff /* BOT only */ __le32 bot_tag; @@ -93,6 +104,9 @@ struct uas_stream { struct usb_request *req_in; struct usb_request *req_out; struct usb_request *req_status; + + struct completion cmd_completion; + struct hlist_node node; }; struct usbg_cdb { @@ -116,15 +130,17 @@ struct f_uas { #define USBG_USE_STREAMS (1 << 2) #define USBG_IS_BOT (1 << 3) #define USBG_BOT_CMD_PEND (1 << 4) +#define USBG_BOT_WEDGED (1 << 5) - struct usbg_cdb cmd; + struct usbg_cdb cmd[USBG_NUM_CMDS]; struct usb_ep *ep_in; struct usb_ep *ep_out; /* UAS */ struct usb_ep *ep_status; struct usb_ep *ep_cmd; - struct uas_stream stream[UASP_SS_EP_COMP_NUM_STREAMS]; + struct uas_stream stream[USBG_NUM_CMDS]; + DECLARE_HASHTABLE(stream_hash, UASP_SS_EP_COMP_LOG_STREAMS); /* BOT */ struct bot_status bot_status; diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index 09e2838917e2..f58590bf5e02 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -1052,8 +1052,8 @@ void gether_suspend(struct gether *link) * There is a transfer in progress. So we trigger a remote * wakeup to inform the host. */ - ether_wakeup_host(dev->port_usb); - return; + if (!ether_wakeup_host(dev->port_usb)) + return; } spin_lock_irqsave(&dev->lock, flags); link->is_suspend = true; diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 0a8c05b2746b..36fff45e8c9b 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -21,6 +21,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/export.h> #include <linux/module.h> #include <linux/console.h> @@ -579,9 +580,12 @@ static int gs_start_io(struct gs_port *port) * we didn't in gs_start_tx() */ tty_wakeup(port->port.tty); } else { - gs_free_requests(ep, head, &port->read_allocated); - gs_free_requests(port->port_usb->in, &port->write_pool, - &port->write_allocated); + /* Free reqs only if we are still connected */ + if (port->port_usb) { + gs_free_requests(ep, head, &port->read_allocated); + gs_free_requests(port->port_usb->in, &port->write_pool, + &port->write_allocated); + } status = -EIO; } @@ -1542,7 +1546,7 @@ static int __init userial_init(void) pr_debug("%s: registered %d ttyGS* device%s\n", __func__, MAX_U_SERIAL_PORTS, - (MAX_U_SERIAL_PORTS == 1) ? "" : "s"); + str_plural(MAX_U_SERIAL_PORTS)); return status; fail: diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index cb35687b11e7..6f44dd732315 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -71,6 +71,11 @@ extern unsigned int uvc_gadget_trace_param; #define UVCG_REQUEST_HEADER_LEN 12 +#define UVCG_REQ_MAX_INT_COUNT 16 +#define UVCG_REQ_MAX_ZERO_COUNT (2 * UVCG_REQ_MAX_INT_COUNT) + +#define UVCG_STREAMING_MIN_BUFFERS 2 + /* ------------------------------------------------------------------------ * Structures */ @@ -91,16 +96,24 @@ struct uvc_video { struct work_struct pump; struct workqueue_struct *async_wq; + struct kthread_worker *kworker; + struct kthread_work hw_submit; + + atomic_t queued; + /* Frame parameters */ u8 bpp; u32 fcc; unsigned int width; unsigned int height; unsigned int imagesize; + unsigned int interval; struct mutex mutex; /* protects frame parameters */ unsigned int uvc_num_requests; + unsigned int reqs_per_frame; + /* Requests */ bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 6fac696ea846..f131943254a4 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1566,11 +1566,13 @@ static const struct uvcg_config_group_type uvcg_control_grp_type = { /* ----------------------------------------------------------------------------- * streaming/uncompressed * streaming/mjpeg + * streaming/framebased */ static const char * const uvcg_format_names[] = { "uncompressed", "mjpeg", + "framebased", }; static struct uvcg_color_matching * @@ -1777,6 +1779,9 @@ static int uvcg_streaming_header_allow_link(struct config_item *src, target_fmt = container_of(to_config_group(target), struct uvcg_format, group); + if (!target_fmt) + goto out; + uvcg_format_set_indices(to_config_group(target)); format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); @@ -1816,6 +1821,9 @@ static void uvcg_streaming_header_drop_link(struct config_item *src, target_fmt = container_of(to_config_group(target), struct uvcg_format, group); + if (!target_fmt) + goto out; + list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry) if (format_ptr->fmt == target_fmt) { list_del(&format_ptr->entry); @@ -1826,6 +1834,7 @@ static void uvcg_streaming_header_drop_link(struct config_item *src, --target_fmt->linked; +out: mutex_unlock(&opts->lock); mutex_unlock(su_mutex); } @@ -2022,6 +2031,7 @@ UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, 32); UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, 32); UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, 32); UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, 32); +UVCG_FRAME_ATTR(dw_bytes_perline, dwBytesPerLine, 32); #undef UVCG_FRAME_ATTR @@ -2035,7 +2045,7 @@ static ssize_t uvcg_frame_dw_frame_interval_show(struct config_item *item, int result, i; char *pg = page; - mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -2105,7 +2115,7 @@ end: UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval); -static struct configfs_attribute *uvcg_frame_attrs[] = { +static struct configfs_attribute *uvcg_frame_attrs1[] = { &uvcg_frame_attr_b_frame_index, &uvcg_frame_attr_bm_capabilities, &uvcg_frame_attr_w_width, @@ -2118,12 +2128,31 @@ static struct configfs_attribute *uvcg_frame_attrs[] = { NULL, }; -static const struct config_item_type uvcg_frame_type = { +static struct configfs_attribute *uvcg_frame_attrs2[] = { + &uvcg_frame_attr_b_frame_index, + &uvcg_frame_attr_bm_capabilities, + &uvcg_frame_attr_w_width, + &uvcg_frame_attr_w_height, + &uvcg_frame_attr_dw_min_bit_rate, + &uvcg_frame_attr_dw_max_bit_rate, + &uvcg_frame_attr_dw_default_frame_interval, + &uvcg_frame_attr_dw_frame_interval, + &uvcg_frame_attr_dw_bytes_perline, + NULL, +}; + +static const struct config_item_type uvcg_frame_type1 = { .ct_item_ops = &uvcg_config_item_ops, - .ct_attrs = uvcg_frame_attrs, + .ct_attrs = uvcg_frame_attrs1, .ct_owner = THIS_MODULE, }; +static const struct config_item_type uvcg_frame_type2 = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_attrs = uvcg_frame_attrs2, + .ct_owner = THIS_MODULE, +}; + static struct config_item *uvcg_frame_make(struct config_group *group, const char *name) { @@ -2145,6 +2174,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, h->frame.dw_max_bit_rate = 55296000; h->frame.dw_max_video_frame_buffer_size = 460800; h->frame.dw_default_frame_interval = 666666; + h->frame.dw_bytes_perline = 0; opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); @@ -2157,6 +2187,9 @@ static struct config_item *uvcg_frame_make(struct config_group *group, } else if (fmt->type == UVCG_MJPEG) { h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG; h->fmt_type = UVCG_MJPEG; + } else if (fmt->type == UVCG_FRAMEBASED) { + h->frame.b_descriptor_subtype = UVC_VS_FRAME_FRAME_BASED; + h->fmt_type = UVCG_FRAMEBASED; } else { mutex_unlock(&opts->lock); kfree(h); @@ -2175,7 +2208,10 @@ static struct config_item *uvcg_frame_make(struct config_group *group, ++fmt->num_frames; mutex_unlock(&opts->lock); - config_item_init_type_name(&h->item, name, &uvcg_frame_type); + if (fmt->type == UVCG_FRAMEBASED) + config_item_init_type_name(&h->item, name, &uvcg_frame_type2); + else + config_item_init_type_name(&h->item, name, &uvcg_frame_type1); return &h->item; } @@ -2215,9 +2251,6 @@ static void uvcg_format_set_indices(struct config_group *fmt) list_for_each_entry(ci, &fmt->cg_children, ci_entry) { struct uvcg_frame *frm; - if (ci->ci_type != &uvcg_frame_type) - continue; - frm = to_uvcg_frame(ci); frm->frame.b_frame_index = i++; } @@ -2678,6 +2711,251 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = { }; /* ----------------------------------------------------------------------------- + * streaming/framebased/<NAME> + */ + +static struct configfs_group_operations uvcg_framebased_group_ops = { + .make_item = uvcg_frame_make, + .drop_item = uvcg_frame_drop, +}; + +#define UVCG_FRAMEBASED_ATTR_RO(cname, aname, bits) \ +static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +UVC_ATTR_RO(uvcg_framebased_, cname, aname) + +#define UVCG_FRAMEBASED_ATTR(cname, aname, bits) \ +static ssize_t uvcg_framebased_##cname##_show(struct config_item *item, \ + char *page) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int result; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\ + mutex_unlock(&opts->lock); \ + \ + mutex_unlock(su_mutex); \ + return result; \ +} \ + \ +static ssize_t \ +uvcg_framebased_##cname##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct uvcg_framebased *u = to_uvcg_framebased(item); \ + struct f_uvc_opts *opts; \ + struct config_item *opts_item; \ + struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ + int ret; \ + u8 num; \ + \ + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ + \ + opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ + opts = to_f_uvc_opts(opts_item); \ + \ + mutex_lock(&opts->lock); \ + if (u->fmt.linked || opts->refcnt) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + ret = kstrtou8(page, 0, &num); \ + if (ret) \ + goto end; \ + \ + if (num > 255) { \ + ret = -EINVAL; \ + goto end; \ + } \ + u->desc.aname = num; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + mutex_unlock(su_mutex); \ + return ret; \ +} \ + \ +UVC_ATTR(uvcg_framebased_, cname, aname) + +UVCG_FRAMEBASED_ATTR_RO(b_format_index, bFormatIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_bits_per_pixel, bBitsPerPixel, 8); +UVCG_FRAMEBASED_ATTR(b_default_frame_index, bDefaultFrameIndex, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8); +UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8); +UVCG_FRAMEBASED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8); + +#undef UVCG_FRAMEBASED_ATTR +#undef UVCG_FRAMEBASED_ATTR_RO + +static ssize_t uvcg_framebased_guid_format_show(struct config_item *item, + char *page) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat)); + mutex_unlock(&opts->lock); + + mutex_unlock(su_mutex); + + return sizeof(ch->desc.guidFormat); +} + +static ssize_t uvcg_framebased_guid_format_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *ch = to_uvcg_framebased(item); + struct f_uvc_opts *opts; + struct config_item *opts_item; + struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; + int ret; + + mutex_lock(su_mutex); /* for navigating configfs hierarchy */ + + opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; + opts = to_f_uvc_opts(opts_item); + + mutex_lock(&opts->lock); + if (ch->fmt.linked || opts->refcnt) { + ret = -EBUSY; + goto end; + } + + memcpy(ch->desc.guidFormat, page, + min(sizeof(ch->desc.guidFormat), len)); + ret = sizeof(ch->desc.guidFormat); + +end: + mutex_unlock(&opts->lock); + mutex_unlock(su_mutex); + return ret; +} + +UVC_ATTR(uvcg_framebased_, guid_format, guidFormat); + +static inline ssize_t +uvcg_framebased_bma_controls_show(struct config_item *item, char *page) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_show(&u->fmt, page); +} + +static inline ssize_t +uvcg_framebased_bma_controls_store(struct config_item *item, + const char *page, size_t len) +{ + struct uvcg_framebased *u = to_uvcg_framebased(item); + + return uvcg_format_bma_controls_store(&u->fmt, page, len); +} + +UVC_ATTR(uvcg_framebased_, bma_controls, bmaControls); + +static struct configfs_attribute *uvcg_framebased_attrs[] = { + &uvcg_framebased_attr_b_format_index, + &uvcg_framebased_attr_b_default_frame_index, + &uvcg_framebased_attr_b_bits_per_pixel, + &uvcg_framebased_attr_b_aspect_ratio_x, + &uvcg_framebased_attr_b_aspect_ratio_y, + &uvcg_framebased_attr_bm_interface_flags, + &uvcg_framebased_attr_bma_controls, + &uvcg_framebased_attr_guid_format, + NULL, +}; + +static const struct config_item_type uvcg_framebased_type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_group_ops, + .ct_attrs = uvcg_framebased_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *uvcg_framebased_make(struct config_group *group, + const char *name) +{ + static char guid[] = { /*Declear frame based as H264 format*/ + 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 + }; + struct uvcg_framebased *h; + + h = kzalloc(sizeof(*h), GFP_KERNEL); + if (!h) + return ERR_PTR(-ENOMEM); + + h->desc.bLength = UVC_DT_FORMAT_FRAMEBASED_SIZE; + h->desc.bDescriptorType = USB_DT_CS_INTERFACE; + h->desc.bDescriptorSubType = UVC_VS_FORMAT_FRAME_BASED; + memcpy(h->desc.guidFormat, guid, sizeof(guid)); + h->desc.bBitsPerPixel = 0; + h->desc.bDefaultFrameIndex = 1; + h->desc.bAspectRatioX = 0; + h->desc.bAspectRatioY = 0; + h->desc.bmInterfaceFlags = 0; + h->desc.bCopyProtect = 0; + h->desc.bVariableSize = 1; + + INIT_LIST_HEAD(&h->fmt.frames); + h->fmt.type = UVCG_FRAMEBASED; + config_group_init_type_name(&h->fmt.group, name, + &uvcg_framebased_type); + + return &h->fmt.group; +} + +static struct configfs_group_operations uvcg_framebased_grp_ops = { + .make_group = uvcg_framebased_make, +}; + +static const struct uvcg_config_group_type uvcg_framebased_grp_type = { + .type = { + .ct_item_ops = &uvcg_config_item_ops, + .ct_group_ops = &uvcg_framebased_grp_ops, + .ct_owner = THIS_MODULE, + }, + .name = "framebased", +}; + +/* ----------------------------------------------------------------------------- * streaming/color_matching/default */ @@ -2912,6 +3190,7 @@ static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, if (ret) return ret; grp = &f->fmt->group; + j = 0; list_for_each_entry(item, &grp->cg_children, ci_entry) { frm = to_uvcg_frame(item); ret = fun(frm, priv2, priv3, j++, UVCG_FRAME); @@ -2965,6 +3244,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, container_of(fmt, struct uvcg_mjpeg, fmt); *size += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, fmt); + + *size += sizeof(f->desc); } else { return -EINVAL; } @@ -2975,6 +3259,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, int sz = sizeof(frm->dw_frame_interval); *size += sizeof(frm->frame); + /* + * framebased has duplicate member with uncompressed and + * mjpeg, so minus it + */ + *size -= sizeof(u32); *size += frm->frame.b_frame_interval_type * sz; } break; @@ -2991,6 +3280,27 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, return 0; } +static int __uvcg_copy_framebased_desc(void *dest, struct uvcg_frame *frm, + int sz) +{ + struct uvc_frame_framebased *desc = dest; + + desc->bLength = frm->frame.b_length; + desc->bDescriptorType = frm->frame.b_descriptor_type; + desc->bDescriptorSubType = frm->frame.b_descriptor_subtype; + desc->bFrameIndex = frm->frame.b_frame_index; + desc->bmCapabilities = frm->frame.bm_capabilities; + desc->wWidth = frm->frame.w_width; + desc->wHeight = frm->frame.w_height; + desc->dwMinBitRate = frm->frame.dw_min_bit_rate; + desc->dwMaxBitRate = frm->frame.dw_max_bit_rate; + desc->dwDefaultFrameInterval = frm->frame.dw_default_frame_interval; + desc->bFrameIntervalType = frm->frame.b_frame_interval_type; + desc->dwBytesPerLine = frm->frame.dw_bytes_perline; + + return 0; +} + /* * Fill an array of streaming descriptors. * @@ -3045,6 +3355,15 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, m->desc.bNumFrameDescriptors = fmt->num_frames; memcpy(*dest, &m->desc, sizeof(m->desc)); *dest += sizeof(m->desc); + } else if (fmt->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *f = + container_of(fmt, struct uvcg_framebased, + fmt); + + f->desc.bFormatIndex = n + 1; + f->desc.bNumFrameDescriptors = fmt->num_frames; + memcpy(*dest, &f->desc, sizeof(f->desc)); + *dest += sizeof(f->desc); } else { return -EINVAL; } @@ -3054,8 +3373,11 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, struct uvcg_frame *frm = priv1; struct uvc_descriptor_header *h = *dest; - sz = sizeof(frm->frame); - memcpy(*dest, &frm->frame, sz); + sz = sizeof(frm->frame) - 4; + if (frm->fmt_type != UVCG_FRAMEBASED) + memcpy(*dest, &frm->frame, sz); + else + __uvcg_copy_framebased_desc(*dest, frm, sz); *dest += sz; sz = frm->frame.b_frame_interval_type * sizeof(*frm->dw_frame_interval); @@ -3066,7 +3388,10 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, frm->frame.b_frame_interval_type); else if (frm->fmt_type == UVCG_MJPEG) h->bLength = UVC_DT_FRAME_MJPEG_SIZE( - frm->frame.b_frame_interval_type); + frm->frame.b_frame_interval_type); + else if (frm->fmt_type == UVCG_FRAMEBASED) + h->bLength = UVC_DT_FRAME_FRAMEBASED_SIZE( + frm->frame.b_frame_interval_type); } break; case UVCG_COLOR_MATCHING: { @@ -3285,6 +3610,7 @@ static const struct uvcg_config_group_type uvcg_streaming_grp_type = { &uvcg_streaming_header_grp_type, &uvcg_uncompressed_grp_type, &uvcg_mjpeg_grp_type, + &uvcg_framebased_grp_type, &uvcg_color_matching_grp_type, &uvcg_streaming_class_grp_type, NULL, diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index c6a690158138..2f78cd4f396f 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -49,6 +49,7 @@ container_of(group_ptr, struct uvcg_color_matching, group) enum uvcg_format_type { UVCG_UNCOMPRESSED = 0, UVCG_MJPEG, + UVCG_FRAMEBASED, }; struct uvcg_format { @@ -105,6 +106,7 @@ struct uvcg_frame { u32 dw_max_video_frame_buffer_size; u32 dw_default_frame_interval; u8 b_frame_interval_type; + u32 dw_bytes_perline; } __attribute__((packed)) frame; u32 *dw_frame_interval; }; @@ -143,6 +145,20 @@ static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) } /* ----------------------------------------------------------------------------- + * streaming/framebased/<NAME> + */ + +struct uvcg_framebased { + struct uvcg_format fmt; + struct uvc_format_framebased desc; +}; + +static inline struct uvcg_framebased *to_uvcg_framebased(struct config_item *item) +{ + return container_of(to_uvcg_format(item), struct uvcg_framebased, fmt); +} + +/* ----------------------------------------------------------------------------- * control/extensions/<NAME> */ diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 0aa3d7e1f3cc..9a1bbd79ff5a 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -21,6 +21,7 @@ #include <media/videobuf2-vmalloc.h> #include "uvc.h" +#include "uvc_video.h" /* ------------------------------------------------------------------------ * Video buffers queue management. @@ -44,33 +45,23 @@ static int uvc_queue_setup(struct vb2_queue *vq, { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); struct uvc_video *video = container_of(queue, struct uvc_video, queue); - unsigned int req_size; - unsigned int nreq; if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) *nbuffers = UVC_MAX_VIDEO_BUFFERS; + if (*nbuffers < UVCG_STREAMING_MIN_BUFFERS) + *nbuffers = UVCG_STREAMING_MIN_BUFFERS; *nplanes = 1; sizes[0] = video->imagesize; - req_size = video->ep->maxpacket - * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult); - - /* We divide by two, to increase the chance to run - * into fewer requests for smaller framesizes. - */ - nreq = DIV_ROUND_UP(DIV_ROUND_UP(sizes[0], 2), req_size); - nreq = clamp(nreq, 4U, 64U); - video->uvc_num_requests = nreq; - return 0; } static int uvc_buffer_prepare(struct vb2_buffer *vb) { struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + struct uvc_video *video = container_of(queue, struct uvc_video, queue); struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct uvc_buffer *buf = container_of(vbuf, struct uvc_buffer, buf); @@ -91,10 +82,15 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) buf->mem = vb2_plane_vaddr(vb, 0); } buf->length = vb2_plane_size(vb, 0); - if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { buf->bytesused = 0; - else + } else { buf->bytesused = vb2_get_plane_payload(vb, 0); + buf->req_payload_size = + DIV_ROUND_UP(buf->bytesused + + (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN), + video->reqs_per_frame); + } return 0; } @@ -126,8 +122,6 @@ static const struct vb2_ops uvc_queue_qops = { .queue_setup = uvc_queue_setup, .buf_prepare = uvc_buffer_prepare, .buf_queue = uvc_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, }; int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2_buf_type type, diff --git a/drivers/usb/gadget/function/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h index 41f87b917f6b..b54becc570a3 100644 --- a/drivers/usb/gadget/function/uvc_queue.h +++ b/drivers/usb/gadget/function/uvc_queue.h @@ -39,6 +39,8 @@ struct uvc_buffer { unsigned int offset; unsigned int length; unsigned int bytesused; + /* req_payload_size: only used with isoc */ + unsigned int req_payload_size; }; #define UVC_QUEUE_DISCONNECTED (1 << 0) diff --git a/drivers/usb/gadget/function/uvc_trace.c b/drivers/usb/gadget/function/uvc_trace.c new file mode 100644 index 000000000000..d384f6d8221a --- /dev/null +++ b/drivers/usb/gadget/function/uvc_trace.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * trace.c - USB UVC Gadget Trace Support + * + * Copyright (C) 2024 Pengutronix e.K. + * + * Author: Michael Grzeschik <m.grzeschik@pengutronix.de> + */ + +#define CREATE_TRACE_POINTS +#include "uvc_trace.h" diff --git a/drivers/usb/gadget/function/uvc_trace.h b/drivers/usb/gadget/function/uvc_trace.h new file mode 100644 index 000000000000..04c33cf43cc2 --- /dev/null +++ b/drivers/usb/gadget/function/uvc_trace.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * trace.h - USB UVC Gadget Trace Support + * + * Copyright (C) 2024 Pengutronix e.K. + * + * Author: Michael Grzeschik <m.grzeschik@pengutronix.de> + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM uvcg + +#if !defined(__UVCG_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __UVCG_TRACE_H + +#include <linux/types.h> +#include <linux/tracepoint.h> +#include <linux/usb/gadget.h> +#include <asm/byteorder.h> + +DECLARE_EVENT_CLASS(uvcg_video_req, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued), + TP_STRUCT__entry( + __field(struct usb_request *, req) + __field(u32, length) + __field(u32, queued) + ), + TP_fast_assign( + __entry->req = req; + __entry->length = req->length; + __entry->queued = queued; + ), + TP_printk("req %p length %u queued %u", + __entry->req, + __entry->length, + __entry->queued) +); + +DEFINE_EVENT(uvcg_video_req, uvcg_video_complete, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued) +); + +DEFINE_EVENT(uvcg_video_req, uvcg_video_queue, + TP_PROTO(struct usb_request *req, u32 queued), + TP_ARGS(req, queued) +); + +#endif /* __UVCG_TRACE_H */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE uvc_trace + +#include <trace/define_trace.h> diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index de1736f834e6..fc9a8d31a1e9 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -31,14 +31,23 @@ static const struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat) { char guid[16] = UVC_GUID_FORMAT_MJPEG; const struct uvc_format_desc *format; - struct uvcg_uncompressed *unc; if (uformat->type == UVCG_UNCOMPRESSED) { + struct uvcg_uncompressed *unc; + unc = to_uvcg_uncompressed(&uformat->group.cg_item); if (!unc) return ERR_PTR(-EINVAL); memcpy(guid, unc->desc.guidFormat, sizeof(guid)); + } else if (uformat->type == UVCG_FRAMEBASED) { + struct uvcg_framebased *unc; + + unc = to_uvcg_framebased(&uformat->group.cg_item); + if (!unc) + return ERR_PTR(-EINVAL); + + memcpy(guid, unc->desc.guidFormat, sizeof(guid)); } format = uvc_format_by_guid(guid); @@ -314,6 +323,56 @@ uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) return ret; } +static int uvc_v4l2_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct v4l2_fract timeperframe; + + if (!V4L2_TYPE_IS_OUTPUT(parm->type)) + return -EINVAL; + + /* Return the actual frame period. */ + timeperframe.numerator = video->interval; + timeperframe.denominator = 10000000; + v4l2_simplify_fraction(&timeperframe.numerator, + &timeperframe.denominator, 8, 333); + + uvcg_dbg(&uvc->func, "Getting frame interval of %u/%u (%u)\n", + timeperframe.numerator, timeperframe.denominator, + video->interval); + + parm->parm.output.timeperframe = timeperframe; + parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + + return 0; +} + +static int uvc_v4l2_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + struct video_device *vdev = video_devdata(file); + struct uvc_device *uvc = video_get_drvdata(vdev); + struct uvc_video *video = &uvc->video; + struct v4l2_fract timeperframe; + + if (!V4L2_TYPE_IS_OUTPUT(parm->type)) + return -EINVAL; + + timeperframe = parm->parm.output.timeperframe; + + video->interval = v4l2_fraction_to_interval(timeperframe.numerator, + timeperframe.denominator); + + uvcg_dbg(&uvc->func, "Setting frame interval to %u/%u (%u)\n", + timeperframe.numerator, timeperframe.denominator, + video->interval); + + return 0; +} + static int uvc_v4l2_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival) @@ -496,6 +555,9 @@ uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) if (ret < 0) return ret; + if (uvc->state != UVC_STATE_STREAMING) + return 0; + uvc->state = UVC_STATE_CONNECTED; uvc_function_setup_continue(uvc, 1); return 0; @@ -587,6 +649,8 @@ const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { .vidioc_dqbuf = uvc_v4l2_dqbuf, .vidioc_streamon = uvc_v4l2_streamon, .vidioc_streamoff = uvc_v4l2_streamoff, + .vidioc_s_parm = uvc_v4l2_s_parm, + .vidioc_g_parm = uvc_v4l2_g_parm, .vidioc_subscribe_event = uvc_v4l2_subscribe_event, .vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event, .vidioc_default = uvc_v4l2_ioctl_default, diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 57a851151225..fb77b0b21790 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -19,6 +19,7 @@ #include "uvc.h" #include "uvc_queue.h" #include "uvc_video.h" +#include "uvc_trace.h" /* -------------------------------------------------------------------------- * Video codecs @@ -78,7 +79,7 @@ uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf, /* Copy video data to the USB buffer. */ mem = buf->mem + queue->buf_used; - nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used); + nbytes = min_t(unsigned int, len, buf->bytesused - queue->buf_used); memcpy(data, mem, nbytes); queue->buf_used += nbytes; @@ -104,7 +105,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, } /* Process video data. */ - len = min((int)(video->max_payload_size - video->payload_size), len); + len = min_t(int, video->max_payload_size - video->payload_size, len); ret = uvc_video_encode_data(video, buf, mem, len); video->payload_size += ret; @@ -136,7 +137,7 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, unsigned int pending = buf->bytesused - video->queue.buf_used; struct uvc_request *ureq = req->context; struct scatterlist *sg, *iter; - unsigned int len = video->req_size; + unsigned int len = buf->req_payload_size; unsigned int sg_left, part = 0; unsigned int i; int header_len; @@ -146,15 +147,15 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, /* Init the header. */ header_len = uvc_video_encode_header(video, buf, ureq->header, - video->req_size); + buf->req_payload_size); sg_set_buf(sg, ureq->header, header_len); len -= header_len; if (pending <= len) len = pending; - req->length = (len == pending) ? - len + header_len : video->req_size; + req->length = (len == pending) ? len + header_len : + buf->req_payload_size; /* Init the pending sgs with payload */ sg = sg_next(sg); @@ -202,7 +203,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, { void *mem = req->buf; struct uvc_request *ureq = req->context; - int len = video->req_size; + int len = buf->req_payload_size; int ret; /* Add the header. */ @@ -214,7 +215,7 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, ret = uvc_video_encode_data(video, buf, mem, len); len -= ret; - req->length = video->req_size - len; + req->length = buf->req_payload_size - len; if (buf->bytesused == video->queue.buf_used || video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) { @@ -269,6 +270,10 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) } } + atomic_inc(&video->queued); + + trace_uvcg_video_queue(req, atomic_read(&video->queued)); + return ret; } @@ -304,7 +309,7 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video, */ if (list_empty(&video->req_free) || ureq->last_buf || !(video->req_int_count % - DIV_ROUND_UP(video->uvc_num_requests, 4))) { + min(DIV_ROUND_UP(video->uvc_num_requests, 4), UVCG_REQ_MAX_INT_COUNT))) { video->req_int_count = 0; req->no_interrupt = 0; } else { @@ -322,50 +327,6 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video, return 0; } -/* - * Must only be called from uvcg_video_enable - since after that we only want to - * queue requests to the endpoint from the uvc_video_complete complete handler. - * This function is needed in order to 'kick start' the flow of requests from - * gadget driver to the usb controller. - */ -static void uvc_video_ep_queue_initial_requests(struct uvc_video *video) -{ - struct usb_request *req = NULL; - unsigned long flags = 0; - unsigned int count = 0; - int ret = 0; - - /* - * We only queue half of the free list since we still want to have - * some free usb_requests in the free list for the video_pump async_wq - * thread to encode uvc buffers into. Otherwise we could get into a - * situation where the free list does not have any usb requests to - * encode into - we always end up queueing 0 length requests to the - * end point. - */ - unsigned int half_list_size = video->uvc_num_requests / 2; - - spin_lock_irqsave(&video->req_lock, flags); - /* - * Take these requests off the free list and queue them all to the - * endpoint. Since we queue 0 length requests with the req_lock held, - * there isn't any 'data' race involved here with the complete handler. - */ - while (count < half_list_size) { - req = list_first_entry(&video->req_free, struct usb_request, - list); - list_del(&req->list); - req->length = 0; - ret = uvcg_video_ep_queue(video, req); - if (ret < 0) { - uvcg_queue_cancel(&video->queue, 0); - break; - } - count++; - } - spin_unlock_irqrestore(&video->req_lock, flags); -} - static void uvc_video_complete(struct usb_ep *ep, struct usb_request *req) { @@ -373,12 +334,10 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) struct uvc_video *video = ureq->video; struct uvc_video_queue *queue = &video->queue; struct uvc_buffer *last_buf; - struct usb_request *to_queue = req; unsigned long flags; - bool is_bulk = video->max_payload_size; - int ret = 0; spin_lock_irqsave(&video->req_lock, flags); + atomic_dec(&video->queued); if (!video->is_enabled) { /* * When is_enabled is false, uvcg_video_disable() ensures @@ -438,51 +397,87 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) return; } + list_add_tail(&req->list, &video->req_free); /* - * Here we check whether any request is available in the ready - * list. If it is, queue it to the ep and add the current - * usb_request to the req_free list - for video_pump to fill in. - * Otherwise, just use the current usb_request to queue a 0 - * length request to the ep. Since we always add to the req_free - * list if we dequeue from the ready list, there will never - * be a situation where the req_free list is completely out of - * requests and cannot recover. + * Queue work to the wq as well since it is possible that a + * buffer may not have been completely encoded with the set of + * in-flight usb requests for whih the complete callbacks are + * firing. + * In that case, if we do not queue work to the worker thread, + * the buffer will never be marked as complete - and therefore + * not be returned to userpsace. As a result, + * dequeue -> queue -> dequeue flow of uvc buffers will not + * happen. Since there are is a new free request wake up the pump. */ - to_queue->length = 0; - if (!list_empty(&video->req_ready)) { - to_queue = list_first_entry(&video->req_ready, - struct usb_request, list); - list_del(&to_queue->list); - list_add_tail(&req->list, &video->req_free); + queue_work(video->async_wq, &video->pump); + + trace_uvcg_video_complete(req, atomic_read(&video->queued)); + + spin_unlock_irqrestore(&video->req_lock, flags); + + kthread_queue_work(video->kworker, &video->hw_submit); +} + +static void uvcg_video_hw_submit(struct kthread_work *work) +{ + struct uvc_video *video = container_of(work, struct uvc_video, hw_submit); + bool is_bulk = video->max_payload_size; + unsigned long flags; + struct usb_request *req; + int ret = 0; + + while (true) { + if (!video->ep->enabled) + return; + spin_lock_irqsave(&video->req_lock, flags); /* - * Queue work to the wq as well since it is possible that a - * buffer may not have been completely encoded with the set of - * in-flight usb requests for whih the complete callbacks are - * firing. - * In that case, if we do not queue work to the worker thread, - * the buffer will never be marked as complete - and therefore - * not be returned to userpsace. As a result, - * dequeue -> queue -> dequeue flow of uvc buffers will not - * happen. + * Here we check whether any request is available in the ready + * list. If it is, queue it to the ep and add the current + * usb_request to the req_free list - for video_pump to fill in. + * Otherwise, just use the current usb_request to queue a 0 + * length request to the ep. Since we always add to the req_free + * list if we dequeue from the ready list, there will never + * be a situation where the req_free list is completely out of + * requests and cannot recover. */ - queue_work(video->async_wq, &video->pump); - } - /* - * Queue to the endpoint. The actual queueing to ep will - * only happen on one thread - the async_wq for bulk endpoints - * and this thread for isoc endpoints. - */ - ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk); - if (ret < 0) { + if (!list_empty(&video->req_ready)) { + req = list_first_entry(&video->req_ready, + struct usb_request, list); + } else { + if (list_empty(&video->req_free) || + (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT)) { + spin_unlock_irqrestore(&video->req_lock, flags); + + return; + } + req = list_first_entry(&video->req_free, struct usb_request, + list); + req->length = 0; + } + list_del(&req->list); + /* - * Endpoint error, but the stream is still enabled. - * Put request back in req_free for it to be cleaned - * up later. + * Queue to the endpoint. The actual queueing to ep will + * only happen on one thread - the async_wq for bulk endpoints + * and this thread for isoc endpoints. */ - list_add_tail(&to_queue->list, &video->req_free); - } + ret = uvcg_video_usb_req_queue(video, req, !is_bulk); + if (ret < 0) { + /* + * Endpoint error, but the stream is still enabled. + * Put request back in req_free for it to be cleaned + * up later. + */ + list_add_tail(&req->list, &video->req_free); + /* + * There is a new free request - wake up the pump. + */ + queue_work(video->async_wq, &video->pump); - spin_unlock_irqrestore(&video->req_lock, flags); + } + + spin_unlock_irqrestore(&video->req_lock, flags); + } } static int @@ -496,23 +491,71 @@ uvc_video_free_requests(struct uvc_video *video) INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); INIT_LIST_HEAD(&video->req_ready); - video->req_size = 0; return 0; } +static void +uvc_video_prep_requests(struct uvc_video *video) +{ + struct uvc_device *uvc = container_of(video, struct uvc_device, video); + struct usb_composite_dev *cdev = uvc->func.config->cdev; + unsigned int interval_duration = video->ep->desc->bInterval * 1250; + unsigned int max_req_size, req_size, header_size; + unsigned int nreq; + + max_req_size = video->ep->maxpacket + * max_t(unsigned int, video->ep->maxburst, 1) + * (video->ep->mult); + + if (!usb_endpoint_xfer_isoc(video->ep->desc)) { + video->req_size = max_req_size; + video->reqs_per_frame = video->uvc_num_requests = + DIV_ROUND_UP(video->imagesize, max_req_size); + + return; + } + + if (cdev->gadget->speed < USB_SPEED_HIGH) + interval_duration = video->ep->desc->bInterval * 10000; + + nreq = DIV_ROUND_UP(video->interval, interval_duration); + + header_size = nreq * UVCG_REQUEST_HEADER_LEN; + + req_size = DIV_ROUND_UP(video->imagesize + header_size, nreq); + + if (req_size > max_req_size) { + /* The prepared interval length and expected buffer size + * is not possible to stream with the currently configured + * isoc bandwidth. Fallback to the maximum. + */ + req_size = max_req_size; + } + video->req_size = req_size; + + /* We need to compensate the amount of requests to be + * allocated with the maximum amount of zero length requests. + * Since it is possible that hw_submit will initially + * enqueue some zero length requests and we then will not be + * able to fully encode one frame. + */ + video->uvc_num_requests = nreq + UVCG_REQ_MAX_ZERO_COUNT; + video->reqs_per_frame = nreq; +} + static int uvc_video_alloc_requests(struct uvc_video *video) { struct uvc_request *ureq; - unsigned int req_size; unsigned int i; int ret = -ENOMEM; - BUG_ON(video->req_size); - - req_size = video->ep->maxpacket - * max_t(unsigned int, video->ep->maxburst, 1) - * (video->ep->mult); + /* + * calculate in uvc_video_prep_requests + * - video->uvc_num_requests + * - video->req_size + */ + uvc_video_prep_requests(video); for (i = 0; i < video->uvc_num_requests; i++) { ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL); @@ -523,7 +566,7 @@ uvc_video_alloc_requests(struct uvc_video *video) list_add_tail(&ureq->list, &video->ureqs); - ureq->req_buffer = kmalloc(req_size, GFP_KERNEL); + ureq->req_buffer = kmalloc(video->req_size, GFP_KERNEL); if (ureq->req_buffer == NULL) goto error; @@ -541,12 +584,10 @@ uvc_video_alloc_requests(struct uvc_video *video) list_add_tail(&ureq->req->list, &video->req_free); /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ sg_alloc_table(&ureq->sgt, - DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN, + DIV_ROUND_UP(video->req_size - UVCG_REQUEST_HEADER_LEN, PAGE_SIZE) + 2, GFP_KERNEL); } - video->req_size = req_size; - return 0; error: @@ -699,7 +740,6 @@ uvcg_video_disable(struct uvc_video *video) INIT_LIST_HEAD(&video->ureqs); INIT_LIST_HEAD(&video->req_free); INIT_LIST_HEAD(&video->req_ready); - video->req_size = 0; spin_unlock_irqrestore(&video->req_lock, flags); /* @@ -752,7 +792,9 @@ int uvcg_video_enable(struct uvc_video *video) video->req_int_count = 0; - uvc_video_ep_queue_initial_requests(video); + atomic_set(&video->queued, 0); + + kthread_queue_work(video->kworker, &video->hw_submit); queue_work(video->async_wq, &video->pump); return ret; @@ -775,12 +817,24 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) if (!video->async_wq) return -EINVAL; + /* Allocate a kthread for asynchronous hw submit handler. */ + video->kworker = kthread_run_worker(0, "UVCG"); + if (IS_ERR(video->kworker)) { + uvcg_err(&video->uvc->func, "failed to create UVCG kworker\n"); + return PTR_ERR(video->kworker); + } + + kthread_init_work(&video->hw_submit, uvcg_video_hw_submit); + + sched_set_fifo(video->kworker->task); + video->uvc = uvc; video->fcc = V4L2_PIX_FMT_YUYV; video->bpp = 16; video->width = 320; video->height = 240; video->imagesize = 320 * 240 * 2; + video->interval = 666666; /* Initialize the video buffers queue. */ uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c index 4ca67b2f8f24..3684546e8801 100644 --- a/drivers/usb/gadget/legacy/hid.c +++ b/drivers/usb/gadget/legacy/hid.c @@ -261,7 +261,7 @@ static struct usb_composite_driver hidg_driver = { }; static struct platform_driver hidg_plat_driver = { - .remove_new = hidg_plat_driver_remove, + .remove = hidg_plat_driver_remove, .driver = { .name = "hidg", }, diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 9c7381661016..b6a30d88a800 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -20,6 +20,7 @@ #include <linux/uaccess.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/poll.h> #include <linux/kthread.h> #include <linux/aio.h> @@ -1182,7 +1183,7 @@ ep0_fasync (int f, struct file *fd, int on) { struct dev_data *dev = fd->private_data; // caller must F_SETOWN before signal delivery happens - VDEBUG (dev, "%s %s\n", __func__, on ? "on" : "off"); + VDEBUG(dev, "%s %s\n", __func__, str_on_off(on)); return fasync_helper (f, fd, on, &dev->fasync); } diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c index 112fd18d8c99..20165e1582d9 100644 --- a/drivers/usb/gadget/legacy/raw_gadget.c +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -782,7 +782,7 @@ static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value) if (ret < 0) goto free; - length = min(io.length, (unsigned int)ret); + length = min_t(unsigned int, io.length, ret); if (copy_to_user((void __user *)(value + sizeof(io)), data, length)) ret = -EFAULT; else @@ -1168,7 +1168,7 @@ static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value) if (ret < 0) goto free; - length = min(io.length, (unsigned int)ret); + length = min_t(unsigned int, io.length, ret); if (copy_to_user((void __user *)(value + sizeof(io)), data, length)) ret = -EFAULT; else diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c index e25e0d8dd387..a05785bdeb30 100644 --- a/drivers/usb/gadget/legacy/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -194,7 +194,7 @@ static void zero_suspend(struct usb_composite_dev *cdev) static void zero_resume(struct usb_composite_dev *cdev) { DBG(cdev, "%s\n", __func__); - del_timer(&autoresume_timer); + timer_delete(&autoresume_timer); } /*-------------------------------------------------------------------------*/ @@ -398,7 +398,7 @@ err_put_func_inst_ss: static int zero_unbind(struct usb_composite_dev *cdev) { - del_timer_sync(&autoresume_timer); + timer_delete_sync(&autoresume_timer); if (!IS_ERR_OR_NULL(func_ss)) usb_put_function(func_ss); usb_put_function_instance(func_inst_ss); diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index f60a019bb173..f2685f89b3e5 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -428,7 +428,7 @@ MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids); static struct platform_driver ast_vhub_driver = { .probe = ast_vhub_probe, - .remove_new = ast_vhub_remove, + .remove = ast_vhub_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = ast_vhub_dt_ids, diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 573109ca5b79..a09f72772e6e 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -548,6 +548,9 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx) d->vhub = vhub; d->index = idx; d->name = devm_kasprintf(parent, GFP_KERNEL, "port%d", idx+1); + if (!d->name) + return -ENOMEM; + d->regs = vhub->regs + 0x100 + 0x10 * idx; ast_vhub_init_ep0(vhub, &d->ep0, d); diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c index a63e4af60a56..02fe1a08d575 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c @@ -22,6 +22,7 @@ #include <linux/usb/gadget.h> #include <linux/of.h> #include <linux/regmap.h> +#include <linux/string_choices.h> #include <linux/dma-mapping.h> #include <linux/bcd.h> #include <linux/version.h> @@ -219,7 +220,7 @@ static int ast_vhub_hub_dev_feature(struct ast_vhub_ep *ep, if (wValue == USB_DEVICE_REMOTE_WAKEUP) { ep->vhub->wakeup_en = is_set; EPDBG(ep, "Hub remote wakeup %s\n", - is_set ? "enabled" : "disabled"); + str_enabled_disabled(is_set)); return std_req_complete; } diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c index f4781e611aaa..353bfb1ff0a1 100644 --- a/drivers/usb/gadget/udc/aspeed_udc.c +++ b/drivers/usb/gadget/udc/aspeed_udc.c @@ -156,7 +156,7 @@ #define AST_EP_DMA_DESC_PID_DATA1 (2 << 14) #define AST_EP_DMA_DESC_PID_MDATA (3 << 14) #define EP_DESC1_IN_LEN(x) ((x) & 0x1fff) -#define AST_EP_DMA_DESC_MAX_LEN (7680) /* Max packet length for trasmit in 1 desc */ +#define AST_EP_DMA_DESC_MAX_LEN (7680) /* Max packet length for transmit in 1 desc */ struct ast_udc_request { struct usb_request req; @@ -1590,7 +1590,7 @@ MODULE_DEVICE_TABLE(of, ast_udc_of_dt_ids); static struct platform_driver ast_udc_driver = { .probe = ast_udc_probe, - .remove_new = ast_udc_remove, + .remove = ast_udc_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = ast_udc_of_dt_ids, diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index e3bf17a98b38..aa4c61094dc6 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/ioport.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/errno.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -131,7 +132,7 @@ static void proc_ep_show(struct seq_file *s, struct at91_ep *ep) seq_printf(s, "csr %08x rxbytes=%d %s %s %s" EIGHTBITS "\n", csr, (csr & 0x07ff0000) >> 16, - (csr & (1 << 15)) ? "enabled" : "disabled", + str_enabled_disabled(csr & (1 << 15)), (csr & (1 << 11)) ? "DATA1" : "DATA0", types[(csr & 0x700) >> 8], @@ -2002,7 +2003,7 @@ static int at91udc_resume(struct platform_device *pdev) static struct platform_driver at91_udc_driver = { .probe = at91udc_probe, - .remove_new = at91udc_remove, + .remove = at91udc_remove, .shutdown = at91udc_shutdown, .suspend = at91udc_suspend, .resume = at91udc_resume, diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 4928eba19327..0c6f2ad81d37 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2444,7 +2444,7 @@ static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume); static struct platform_driver udc_driver = { .probe = usba_udc_probe, - .remove_new = usba_udc_remove, + .remove = usba_udc_remove, .driver = { .name = "atmel_usba_udc", .pm = &usba_udc_pm_ops, diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index da7011d906e0..502612a5650e 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2367,7 +2367,7 @@ static void bcm63xx_udc_remove(struct platform_device *pdev) static struct platform_driver bcm63xx_udc_driver = { .probe = bcm63xx_udc_probe, - .remove_new = bcm63xx_udc_remove, + .remove = bcm63xx_udc_remove, .driver = { .name = DRV_MODULE_NAME, }, diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 5149e2b7f050..5c3d8b64c0e7 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -648,7 +648,7 @@ static struct platform_driver bdc_driver = { .of_match_table = bdc_of_match, }, .probe = bdc_probe, - .remove_new = bdc_remove, + .remove = bdc_remove, }; module_platform_driver(bdc_driver); diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c index 62fce42ef2da..7e69944ef18a 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c @@ -29,6 +29,7 @@ #include <linux/pm_runtime.h> #include <linux/interrupt.h> #include <linux/property.h> +#include <linux/string_choices.h> #include <linux/dmapool.h> #include <linux/iopoll.h> @@ -2233,12 +2234,12 @@ static int cdns2_init_eps(struct cdns2_device *pdev) dev_dbg(pdev->dev, "Init %s, SupType: CTRL: %s, INT: %s, " "BULK: %s, ISOC %s, SupDir IN: %s, OUT: %s\n", pep->name, - (pep->endpoint.caps.type_control) ? "yes" : "no", - (pep->endpoint.caps.type_int) ? "yes" : "no", - (pep->endpoint.caps.type_bulk) ? "yes" : "no", - (pep->endpoint.caps.type_iso) ? "yes" : "no", - (pep->endpoint.caps.dir_in) ? "yes" : "no", - (pep->endpoint.caps.dir_out) ? "yes" : "no"); + str_yes_no(pep->endpoint.caps.type_control), + str_yes_no(pep->endpoint.caps.type_int), + str_yes_no(pep->endpoint.caps.type_bulk), + str_yes_no(pep->endpoint.caps.type_iso), + str_yes_no(pep->endpoint.caps.dir_in), + str_yes_no(pep->endpoint.caps.dir_out)); INIT_LIST_HEAD(&pep->pending_list); INIT_LIST_HEAD(&pep->deferred_list); diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c index b1a8f772467c..e589593b4cbf 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c @@ -15,7 +15,6 @@ #include "cdns2-gadget.h" #define PCI_DRIVER_NAME "cdns-pci-usbhs" -#define PCI_DEVICE_ID_CDNS_USB2 0x0120 #define PCI_BAR_DEV 0 #define PCI_DEV_FN_DEVICE 0 @@ -113,7 +112,7 @@ static const struct dev_pm_ops cdns2_pci_pm_ops = { }; static const struct pci_device_id cdns2_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB2), + { PCI_DEVICE(PCI_VENDOR_ID_CDNS, PCI_DEVICE_ID_CDNS_USB), .class = PCI_CLASS_SERIAL_USB_DEVICE }, { 0, } }; diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index a6f46364be65..4b3d5075621a 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1543,8 +1543,8 @@ void usb_del_gadget(struct usb_gadget *gadget) kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); sysfs_remove_link(&udc->dev.kobj, "gadget"); - flush_work(&gadget->work); device_del(&gadget->dev); + flush_work(&gadget->work); ida_free(&gadget_id_numbers, gadget->id_number); cancel_work_sync(&udc->vbus_work); device_unregister(&udc->dev); diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 081ac7683c0b..4f1b5db51dda 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -28,6 +28,7 @@ #include <linux/delay.h> #include <linux/ioport.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/hrtimer.h> @@ -80,7 +81,7 @@ module_param_named(num, mod_data.num, uint, S_IRUGO); MODULE_PARM_DESC(num, "number of emulated controllers"); /*-------------------------------------------------------------------------*/ -/* gadget side driver data structres */ +/* gadget side driver data structures */ struct dummy_ep { struct list_head queue; unsigned long last_io; /* jiffies timestamp */ @@ -625,7 +626,7 @@ static int dummy_enable(struct usb_ep *_ep, desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", usb_ep_type_string(usb_endpoint_type(desc)), - max, ep->stream_en ? "enabled" : "disabled"); + max, str_enabled_disabled(ep->stream_en)); /* at this point real hardware should be NAKing transfers * to that endpoint, until a buffer is queued to it. @@ -1152,7 +1153,7 @@ static int dummy_udc_resume(struct platform_device *pdev) static struct platform_driver dummy_udc_driver = { .probe = dummy_udc_probe, - .remove_new = dummy_udc_remove, + .remove = dummy_udc_remove, .suspend = dummy_udc_suspend, .resume = dummy_udc_resume, .driver = { @@ -2478,8 +2479,7 @@ static DEVICE_ATTR_RO(urbs); static int dummy_start_ss(struct dummy_hcd *dum_hcd) { - hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - dum_hcd->timer.function = dummy_timer; + hrtimer_setup(&dum_hcd->timer, dummy_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); dum_hcd->rh_state = DUMMY_RH_RUNNING; dum_hcd->stream_en_ep = 0; INIT_LIST_HEAD(&dum_hcd->urbp_list); @@ -2508,8 +2508,7 @@ static int dummy_start(struct usb_hcd *hcd) return dummy_start_ss(dum_hcd); spin_lock_init(&dum_hcd->dum->lock); - hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); - dum_hcd->timer.function = dummy_timer; + hrtimer_setup(&dum_hcd->timer, dummy_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); dum_hcd->rh_state = DUMMY_RH_RUNNING; INIT_LIST_HEAD(&dum_hcd->urbp_list); @@ -2769,7 +2768,7 @@ static int dummy_hcd_resume(struct platform_device *pdev) static struct platform_driver dummy_hcd_driver = { .probe = dummy_hcd_probe, - .remove_new = dummy_hcd_remove, + .remove = dummy_hcd_remove, .suspend = dummy_hcd_suspend, .resume = dummy_hcd_resume, .driver = { diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index 4e88681a79b6..aacfde06387c 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -511,7 +511,7 @@ static int qe_ep_register_init(struct qe_udc *udc, unsigned char pipe_num) out_8(&epparam->tbmr, rtfcr); tmp = (u16)(ep->ep.maxpacket + USB_CRC_SIZE); - /* MRBLR must be divisble by 4 */ + /* MRBLR must be divisible by 4 */ tmp = (u16)(((tmp >> 2) << 2) + 4); out_be16(&epparam->mrblr, tmp); @@ -1413,7 +1413,7 @@ static int ep_txframe_handle(struct qe_ep *ep) return 0; } -/* confirm the already trainsmited bd */ +/* confirm the already transmitted bd */ static int qe_ep_txconf(struct qe_ep *ep) { struct qe_bd __iomem *bd; @@ -2196,7 +2196,7 @@ static int tx_irq(struct qe_udc *udc) } -/* setup packect's rx is handle in the function too */ +/* setup packet's rx is handle in the function too */ static void rx_irq(struct qe_udc *udc) { struct qe_ep *ep; @@ -2704,7 +2704,7 @@ static struct platform_driver udc_driver = { .of_match_table = qe_udc_match, }, .probe = qe_udc_probe, - .remove_new = qe_udc_remove, + .remove = qe_udc_remove, #ifdef CONFIG_PM .suspend = qe_udc_suspend, .resume = qe_udc_resume, diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 0cabd4eee6ac..4dea8bc30cf6 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -22,6 +22,7 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/init.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -1181,7 +1182,7 @@ static int fsl_vbus_session(struct usb_gadget *gadget, int is_active) udc = container_of(gadget, struct fsl_udc, gadget); spin_lock_irqsave(&udc->lock, flags); - dev_vdbg(&gadget->dev, "VBUS %s\n", is_active ? "on" : "off"); + dev_vdbg(&gadget->dev, "VBUS %s\n", str_on_off(is_active)); udc->vbus_active = (is_active != 0); if (can_pullup(udc)) fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), @@ -2685,7 +2686,7 @@ MODULE_DEVICE_TABLE(of, fsl_udc_dt_ids); static struct platform_driver udc_driver = { .probe = fsl_udc_probe, - .remove_new = fsl_udc_remove, + .remove = fsl_udc_remove, .id_table = fsl_udc_devtype, /* these suspend and resume are not usb suspend and resume */ .suspend = fsl_udc_suspend, diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index 873265634ccc..5e94a99b3e53 100644 --- a/drivers/usb/gadget/udc/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1297,7 +1297,7 @@ static void init_controller(struct fusb300 *fusb300) reg |= val; iowrite32(reg, fusb300->reg + FUSB300_OFFSET_HSCR); - /*set u1 u2 timmer*/ + /*set u1 u2 timer*/ fusb300_set_u2_timeout(fusb300, 0xff); fusb300_set_u1_timeout(fusb300, 0xff); @@ -1507,7 +1507,7 @@ clean_up: static struct platform_driver fusb300_driver = { .probe = fusb300_probe, - .remove_new = fusb300_remove, + .remove = fusb300_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index fb901be5dac1..bf5b3c964c18 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -2249,7 +2249,7 @@ static struct platform_driver gr_driver = { .of_match_table = gr_match, }, .probe = gr_probe, - .remove_new = gr_remove, + .remove = gr_remove, }; module_platform_driver(gr_driver); diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 3bfd889ed56a..89d6daf2bda7 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -3249,7 +3249,7 @@ MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match); static struct platform_driver lpc32xx_udc_driver = { .probe = lpc32xx_udc_probe, - .remove_new = lpc32xx_udc_remove, + .remove = lpc32xx_udc_remove, .shutdown = lpc32xx_udc_shutdown, .suspend = lpc32xx_udc_suspend, .resume = lpc32xx_udc_resume, diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index bfaa5291e6c8..a938b2af0944 100644 --- a/drivers/usb/gadget/udc/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1688,7 +1688,7 @@ clean_up: /*-------------------------------------------------------------------------*/ static struct platform_driver m66592_driver = { .probe = m66592_probe, - .remove_new = m66592_remove, + .remove = m66592_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index e1dd5cf25d08..062f43e146aa 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -2047,7 +2047,7 @@ static void mv_u3d_shutdown(struct platform_device *dev) static struct platform_driver mv_u3d_driver = { .probe = mv_u3d_probe, - .remove_new = mv_u3d_remove, + .remove = mv_u3d_remove, .shutdown = mv_u3d_shutdown, .driver = { .name = "mv-u3d", diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 71012b282891..ff103e6b3048 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -2409,7 +2409,7 @@ static void mv_udc_shutdown(struct platform_device *pdev) static struct platform_driver udc_driver = { .probe = mv_udc_probe, - .remove_new = mv_udc_remove, + .remove = mv_udc_remove, .shutdown = mv_udc_shutdown, .driver = { .name = "mv-udc", diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 9230db57dab7..7ecddbf5c90d 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -2097,7 +2097,7 @@ static irqreturn_t net2272_irq(int irq, void *_dev) } /* check dma interrupts */ #endif - /* Platform/devcice interrupt handler */ + /* Platform/device interrupt handler */ #if !defined(PLX_PCI_RDK) net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1)); net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0)); @@ -2685,7 +2685,7 @@ net2272_plat_remove(struct platform_device *pdev) static struct platform_driver net2272_plat_driver = { .probe = net2272_plat_probe, - .remove_new = net2272_plat_remove, + .remove = net2272_plat_remove, .driver = { .name = driver_name, }, diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 61a45e4657d5..c93ea210c17c 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -18,6 +18,7 @@ #include <linux/errno.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/timer.h> #include <linux/list.h> #include <linux/interrupt.h> @@ -251,7 +252,7 @@ static int omap_ep_disable(struct usb_ep *_ep) ep->has_dma = 0; omap_writew(UDC_SET_HALT, UDC_CTRL); list_del_init(&ep->iso); - del_timer(&ep->timer); + timer_delete(&ep->timer); spin_unlock_irqrestore(&ep->udc->lock, flags); @@ -576,13 +577,13 @@ static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status) static void next_out_dma(struct omap_ep *ep, struct omap_req *req) { - unsigned packets = req->req.length - req->req.actual; + unsigned int packets = req->req.length - req->req.actual; int dma_trigger = 0; u16 w; /* set up this DMA transfer, enable the fifo, start */ packets /= ep->ep.maxpacket; - packets = min(packets, (unsigned)UDC_RXN_TC + 1); + packets = min_t(unsigned int, packets, UDC_RXN_TC + 1); req->dma_bytes = packets * ep->ep.maxpacket; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, ep->ep.maxpacket >> 1, packets, @@ -1252,7 +1253,7 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active) udc = container_of(gadget, struct omap_udc, gadget); spin_lock_irqsave(&udc->lock, flags); - VDBG("VBUS %s\n", is_active ? "on" : "off"); + VDBG("VBUS %s\n", str_on_off(is_active)); udc->vbus_active = (is_active != 0); if (cpu_is_omap15xx()) { /* "software" detect, ignored if !VBUS_MODE_1510 */ @@ -2980,7 +2981,7 @@ static int omap_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .probe = omap_udc_probe, - .remove_new = omap_udc_remove, + .remove = omap_udc_remove, .suspend = omap_udc_suspend, .resume = omap_udc_resume, .driver = { diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 7c96fc9f680f..24eb1ae78e45 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -1503,7 +1503,7 @@ reset_gadget(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) ep->stopped = 1; nuke(ep, -ESHUTDOWN); } - del_timer_sync(&dev->timer); + timer_delete_sync(&dev->timer); /* report reset; the driver is already quiesced */ if (driver) @@ -1530,7 +1530,7 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) ep->stopped = 1; nuke(ep, -ESHUTDOWN); } - del_timer_sync(&dev->timer); + timer_delete_sync(&dev->timer); /* report disconnect; the driver is already quiesced */ if (driver) @@ -1607,14 +1607,14 @@ static void handle_ep0 (struct pxa25x_udc *dev) if (udccs0 & UDCCS0_SST) { nuke(ep, -EPIPE); udc_ep0_set_UDCCS(dev, UDCCS0_SST); - del_timer(&dev->timer); + timer_delete(&dev->timer); ep0_idle(dev); } /* previous request unfinished? non-error iff back-to-back ... */ if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) { nuke(ep, 0); - del_timer(&dev->timer); + timer_delete(&dev->timer); ep0_idle(dev); } @@ -2474,7 +2474,7 @@ static int pxa25x_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .shutdown = pxa25x_udc_shutdown, .probe = pxa25x_udc_probe, - .remove_new = pxa25x_udc_remove, + .remove = pxa25x_udc_remove, .suspend = pxa25x_udc_suspend, .resume = pxa25x_udc_resume, .driver = { diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 1a6317e4b2a3..897f53601b5b 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -20,6 +20,7 @@ #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/prefetch.h> #include <linux/byteorder/generic.h> #include <linux/platform_data/pxa2xx_udc.h> @@ -1083,7 +1084,7 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, is_first_req = list_empty(&ep->queue); ep_dbg(ep, "queue req %p(first=%s), len %d buf %p\n", - _req, is_first_req ? "yes" : "no", + _req, str_yes_no(is_first_req), _req->length, _req->buf); if (!ep->enabled) { @@ -2355,18 +2356,19 @@ 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); - unsigned long gpio_flags; if (mach) { - gpio_flags = mach->gpio_pullup_inverted ? GPIOF_ACTIVE_LOW : 0; gpio = mach->gpio_pullup; if (gpio_is_valid(gpio)) { retval = devm_gpio_request_one(&pdev->dev, gpio, - gpio_flags, + 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 { @@ -2538,7 +2540,7 @@ static struct platform_driver udc_driver = { .of_match_table = of_match_ptr(udc_pxa_dt_ids), }, .probe = pxa_udc_probe, - .remove_new = pxa_udc_remove, + .remove = pxa_udc_remove, .shutdown = pxa_udc_shutdown, #ifdef CONFIG_PM .suspend = pxa_udc_suspend, diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index db4a10a979f9..6c1d15b2250c 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1810,7 +1810,7 @@ static void r8a66597_remove(struct platform_device *pdev) struct r8a66597 *r8a66597 = platform_get_drvdata(pdev); usb_del_gadget_udc(&r8a66597->gadget); - del_timer_sync(&r8a66597->timer); + timer_delete_sync(&r8a66597->timer); r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); if (r8a66597->pdata->on_chip) { @@ -1965,7 +1965,7 @@ clean_up2: /*-------------------------------------------------------------------------*/ static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, - .remove_new = r8a66597_remove, + .remove = r8a66597_remove, .driver = { .name = udc_name, }, diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 3b01734ce1b7..89b304cf6d03 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -310,7 +310,7 @@ struct renesas_usb3_request { struct list_head queue; }; -#define USB3_EP_NAME_SIZE 8 +#define USB3_EP_NAME_SIZE 16 struct renesas_usb3_ep { struct usb_ep ep; struct renesas_usb3 *usb3; @@ -3013,7 +3013,7 @@ static SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend, static struct platform_driver renesas_usb3_driver = { .probe = renesas_usb3_probe, - .remove_new = renesas_usb3_remove, + .remove = renesas_usb3_remove, .driver = { .name = udc_name, .pm = &renesas_usb3_pm_ops, diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c index 657f265ac7cc..14f4b2cf05a4 100644 --- a/drivers/usb/gadget/udc/renesas_usbf.c +++ b/drivers/usb/gadget/udc/renesas_usbf.c @@ -2482,7 +2482,7 @@ static int usbf_handle_ep0_setup(struct usbf_ep *ep0) ep0->delayed_status = 0; if ((crq.ctrlreq.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) { - /* This is not a USB standard request -> delelate */ + /* This is not a USB standard request -> delegate */ goto delegate; } @@ -3381,7 +3381,7 @@ static struct platform_driver udc_driver = { .of_match_table = usbf_match, }, .probe = usbf_probe, - .remove_new = usbf_remove, + .remove = usbf_remove, }; module_platform_driver(udc_driver); diff --git a/drivers/usb/gadget/udc/rzv2m_usb3drd.c b/drivers/usb/gadget/udc/rzv2m_usb3drd.c index 36f4ff00d22f..4692eae89f44 100644 --- a/drivers/usb/gadget/udc/rzv2m_usb3drd.c +++ b/drivers/usb/gadget/udc/rzv2m_usb3drd.c @@ -127,7 +127,7 @@ static struct platform_driver rzv2m_usb3drd_driver = { .of_match_table = rzv2m_usb3drd_of_match, }, .probe = rzv2m_usb3drd_probe, - .remove_new = rzv2m_usb3drd_remove, + .remove = rzv2m_usb3drd_remove, }; module_platform_driver(rzv2m_usb3drd_driver); diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index cd89532adec2..373942ceb076 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -2707,7 +2707,7 @@ static irqreturn_t udc_control_in_isr(struct udc *dev) /* write fifo */ udc_txfifo_write(ep, &req->req); - /* lengh bytes transferred */ + /* length bytes transferred */ len = req->req.length - req->req.actual; if (len > ep->ep.maxpacket) len = ep->ep.maxpacket; @@ -3035,12 +3035,12 @@ void udc_remove(struct udc *dev) stop_timer++; if (timer_pending(&udc_timer)) wait_for_completion(&on_exit); - del_timer_sync(&udc_timer); + timer_delete_sync(&udc_timer); /* remove pollstall timer */ stop_pollstall_timer++; if (timer_pending(&udc_pollstall_timer)) wait_for_completion(&on_pollstall_exit); - del_timer_sync(&udc_pollstall_timer); + timer_delete_sync(&udc_pollstall_timer); udc = NULL; } EXPORT_SYMBOL_GPL(udc_remove); diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index ba5a06690507..db842a6de643 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -309,7 +309,7 @@ MODULE_DEVICE_TABLE(of, of_udc_match); static struct platform_driver udc_plat_driver = { .probe = udc_plat_probe, - .remove_new = udc_plat_remove, + .remove = udc_plat_remove, .driver = { .name = "snps-udc-plat", .of_match_table = of_udc_match, diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 7aa46d426f31..c7fdbc55fb0b 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -4071,7 +4071,7 @@ static const struct dev_pm_ops tegra_xudc_pm_ops = { static struct platform_driver tegra_xudc_driver = { .probe = tegra_xudc_probe, - .remove_new = tegra_xudc_remove, + .remove = tegra_xudc_remove, .driver = { .name = "tegra-xudc", .pm = &tegra_xudc_pm_ops, diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index ebc45565c33e..ae2aeb271897 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -2258,7 +2258,7 @@ static struct platform_driver xudc_driver = { .pm = &xudc_pm_ops, }, .probe = xudc_probe, - .remove_new = xudc_remove, + .remove = xudc_remove, }; module_platform_driver(xudc_driver); diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 75f6f99f8173..37a2f1b61cba 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -55,7 +55,7 @@ usb_gadget_get_string (const struct usb_gadget_strings *table, int id, u8 *buf) return -EINVAL; /* string descriptors have length, tag, then UTF16-LE text */ - len = min((size_t)USB_MAX_STRING_LEN, strlen(s->s)); + len = min_t(size_t, USB_MAX_STRING_LEN, strlen(s->s)); len = utf8s_to_utf16s(s->s, len, UTF16_LITTLE_ENDIAN, (wchar_t *) &buf[2], USB_MAX_STRING_LEN); if (len < 0) diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c index 7558cc4d90cc..519386255886 100644 --- a/drivers/usb/host/bcma-hcd.c +++ b/drivers/usb/host/bcma-hcd.c @@ -25,7 +25,6 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/usb/ehci_pdriver.h> #include <linux/usb/ohci_pdriver.h> diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 6a6e1c510b28..65747270fd88 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -220,7 +220,7 @@ static SIMPLE_DEV_PM_OPS(ehci_atmel_pm_ops, ehci_atmel_drv_suspend, static struct platform_driver ehci_atmel_driver = { .probe = ehci_atmel_drv_probe, - .remove_new = ehci_atmel_drv_remove, + .remove = ehci_atmel_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "atmel-ehci", diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c index 68cad0620f1a..888e8f6670d2 100644 --- a/drivers/usb/host/ehci-brcm.c +++ b/drivers/usb/host/ehci-brcm.c @@ -250,7 +250,7 @@ MODULE_DEVICE_TABLE(of, brcm_ehci_of_match); static struct platform_driver ehci_brcm_driver = { .probe = ehci_brcm_probe, - .remove_new = ehci_brcm_remove, + .remove = ehci_brcm_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ehci-brcm", diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index e3a961d3f5fc..d2a5bedf736a 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -288,7 +288,7 @@ MODULE_DEVICE_TABLE(of, exynos_ehci_match); static struct platform_driver exynos_ehci_driver = { .probe = exynos_ehci_probe, - .remove_new = exynos_ehci_remove, + .remove = exynos_ehci_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "exynos-ehci", diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 5b1ce394a417..26f13278d4d6 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -706,7 +706,7 @@ static void fsl_ehci_drv_remove(struct platform_device *pdev) static struct platform_driver ehci_fsl_driver = { .probe = fsl_ehci_drv_probe, - .remove_new = fsl_ehci_drv_remove, + .remove = fsl_ehci_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = DRV_NAME, diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index 14150e4d3382..bd9762eaa135 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -168,7 +168,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match); static struct platform_driver ehci_grlib_driver = { .probe = ehci_hcd_grlib_probe, - .remove_new = ehci_hcd_grlib_remove, + .remove = ehci_hcd_grlib_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "grlib-ehci", diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index cbc0b86fcc36..6d1d190c914d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -466,8 +466,7 @@ static int ehci_init(struct usb_hcd *hcd) */ ehci->need_io_watchdog = 1; - hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - ehci->hrtimer.function = ehci_hrtimer_func; + hrtimer_setup(&ehci->hrtimer, ehci_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT; hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); @@ -547,7 +546,7 @@ static int ehci_init(struct usb_hcd *hcd) * make problems: throughput reduction (!), data errors... */ if (park) { - park = min(park, (unsigned) 3); + park = min_t(unsigned int, park, 3); temp |= CMD_PARK; temp |= park << 8; } diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 2f1fc7eb8b72..cbabbe107172 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -279,7 +279,7 @@ static const struct of_device_id ehci_mv_dt_ids[] = { static struct platform_driver ehci_mv_driver = { .probe = mv_ehci_probe, - .remove_new = mv_ehci_remove, + .remove = mv_ehci_remove, .shutdown = mv_ehci_shutdown, .driver = { .name = "mv-ehci", diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c index 3d3317a1a0b3..f1c7034c1e80 100644 --- a/drivers/usb/host/ehci-npcm7xx.c +++ b/drivers/usb/host/ehci-npcm7xx.c @@ -122,7 +122,7 @@ MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table); static struct platform_driver npcm7xx_ehci_hcd_driver = { .probe = npcm7xx_ehci_hcd_drv_probe, - .remove_new = npcm7xx_ehci_hcd_drv_remove, + .remove = npcm7xx_ehci_hcd_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "npcm7xx-ehci", diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index b24f371a46f3..db4a1acb27da 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -264,7 +264,7 @@ MODULE_DEVICE_TABLE(of, omap_ehci_dt_ids); static struct platform_driver ehci_hcd_omap_driver = { .probe = ehci_hcd_omap_probe, - .remove_new = ehci_hcd_omap_remove, + .remove = ehci_hcd_omap_remove, .shutdown = usb_hcd_platform_shutdown, /*.suspend = ehci_hcd_omap_suspend, */ /*.resume = ehci_hcd_omap_resume, */ diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index ad145a54ca74..34abff8669f8 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -352,7 +352,7 @@ MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids); static struct platform_driver ehci_orion_driver = { .probe = ehci_orion_drv_probe, - .remove_new = ehci_orion_drv_remove, + .remove = ehci_orion_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "orion-ehci", diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 98b073185e1c..150d2542cef0 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -224,7 +224,7 @@ static void quirk_poll_init(struct ehci_platform_priv *priv) static void quirk_poll_end(struct ehci_platform_priv *priv) { - del_timer_sync(&priv->poll_timer); + timer_delete_sync(&priv->poll_timer); cancel_delayed_work(&priv->poll_work); } @@ -508,7 +508,7 @@ static SIMPLE_DEV_PM_OPS(ehci_platform_pm_ops, ehci_platform_suspend, static struct platform_driver ehci_platform_driver = { .id_table = ehci_platform_table, .probe = ehci_platform_probe, - .remove_new = ehci_platform_remove, + .remove = ehci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ehci-platform", diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 7fd83e806ae4..8063b9d3aebd 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -230,7 +230,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match); static struct platform_driver ehci_hcd_ppc_of_driver = { .probe = ehci_hcd_ppc_of_probe, - .remove_new = ehci_hcd_ppc_of_remove, + .remove = ehci_hcd_ppc_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ppc-of-ehci", diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index d31d9506e41a..2d23690d72c5 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -119,8 +119,12 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) if (IS_ERR(priv->iclk)) priv->iclk = NULL; - clk_enable(priv->fclk); - clk_enable(priv->iclk); + ret = clk_enable(priv->fclk); + if (ret) + goto fail_request_resource; + ret = clk_enable(priv->iclk); + if (ret) + goto fail_iclk; ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret != 0) { @@ -136,6 +140,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) fail_add_hcd: clk_disable(priv->iclk); +fail_iclk: clk_disable(priv->fclk); fail_request_resource: @@ -169,7 +174,7 @@ static void ehci_hcd_sh_shutdown(struct platform_device *pdev) static struct platform_driver ehci_hcd_sh_driver = { .probe = ehci_hcd_sh_probe, - .remove_new = ehci_hcd_sh_remove, + .remove = ehci_hcd_sh_remove, .shutdown = ehci_hcd_sh_shutdown, .driver = { .name = "sh_ehci", diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index d0e94e4c9fe2..e96710192d6b 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -105,7 +105,9 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) /* registers start at offset 0x0 */ hcd_to_ehci(hcd)->caps = hcd->regs; - clk_prepare_enable(sehci->clk); + retval = clk_prepare_enable(sehci->clk); + if (retval) + goto err_put_hcd; retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto err_stop_ehci; @@ -130,8 +132,7 @@ static void spear_ehci_hcd_drv_remove(struct platform_device *pdev) usb_remove_hcd(hcd); - if (sehci->clk) - clk_disable_unprepare(sehci->clk); + clk_disable_unprepare(sehci->clk); usb_put_hcd(hcd); } @@ -143,7 +144,7 @@ MODULE_DEVICE_TABLE(of, spear_ehci_id_table); static struct platform_driver spear_ehci_hcd_driver = { .probe = spear_ehci_hcd_drv_probe, - .remove_new = spear_ehci_hcd_drv_remove, + .remove = spear_ehci_hcd_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "spear-ehci", diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c index 2dbb0d86daaa..58867d816af7 100644 --- a/drivers/usb/host/ehci-st.c +++ b/drivers/usb/host/ehci-st.c @@ -320,7 +320,7 @@ MODULE_DEVICE_TABLE(of, st_ehci_ids); static struct platform_driver ehci_platform_driver = { .probe = st_ehci_platform_probe, - .remove_new = st_ehci_platform_remove, + .remove = st_ehci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "st-ehci", diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index a2112c28f631..1d16cfefabd7 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -220,7 +220,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_of_match); static struct platform_driver ehci_hcd_xilinx_of_driver = { .probe = ehci_hcd_xilinx_of_probe, - .remove_new = ehci_hcd_xilinx_of_remove, + .remove = ehci_hcd_xilinx_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xilinx-of-ehci", diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 9a1b5224f239..22a0942f0bce 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -791,7 +791,7 @@ static struct platform_driver of_fhci_driver = { .of_match_table = of_fhci_match, }, .probe = of_fhci_probe, - .remove_new = of_fhci_remove, + .remove = of_fhci_remove, }; module_platform_driver(of_fhci_driver); diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index a45ede80edfc..c3acd410ce94 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -158,7 +158,7 @@ static int add_packet(struct fhci_usb *usb, struct ed *ed, struct td *td) struct packet *pkt; u8 *data = NULL; - /* calcalate data address,len and toggle and then add the transaction */ + /* calculate data address,len and toggle and then add the transaction */ if (td->toggle == USB_TD_TOGGLE_CARRY) td->toggle = ed->toggle_carry; @@ -679,7 +679,7 @@ static void process_done_list(unsigned long data) DECLARE_TASKLET_OLD(fhci_tasklet, process_done_list); -/* transfer complted callback */ +/* transfer completed callback */ u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci) { if (!fhci->process_done_task->state) diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 6cdc3d805c32..4e67b9471986 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -362,7 +362,7 @@ static struct platform_driver fsl_usb2_mph_dr_driver = { .of_match_table = fsl_usb2_mph_dr_of_match, }, .probe = fsl_usb2_mph_dr_of_probe, - .remove_new = fsl_usb2_mph_dr_of_remove, + .remove = fsl_usb2_mph_dr_of_remove, }; module_platform_driver(fsl_usb2_mph_dr_driver); diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index a82d8926e922..71c22c4bd163 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1684,7 +1684,7 @@ MODULE_ALIAS("platform:isp116x-hcd"); static struct platform_driver isp116x_driver = { .probe = isp116x_probe, - .remove_new = isp116x_remove, + .remove = isp116x_remove, .suspend = isp116x_suspend, .resume = isp116x_resume, .driver = { diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 31059c8f94e6..954fc5ad565b 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2357,7 +2357,7 @@ static void isp1362_hc_stop(struct usb_hcd *hcd) pr_debug("%s:\n", __func__); - del_timer_sync(&hcd->rh_timer); + timer_delete_sync(&hcd->rh_timer); spin_lock_irqsave(&isp1362_hcd->lock, flags); @@ -2757,7 +2757,7 @@ static int isp1362_resume(struct platform_device *pdev) static struct platform_driver isp1362_driver = { .probe = isp1362_probe, - .remove_new = isp1362_remove, + .remove = isp1362_remove, .suspend = isp1362_suspend, .resume = isp1362_resume, diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 9fe4f48b1898..dcf31a592f5d 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -779,11 +779,17 @@ max3421_check_unlink(struct usb_hcd *hcd) retval = 1; dev_dbg(&spi->dev, "%s: URB %p unlinked=%d", __func__, urb, urb->unlinked); - usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock_irqrestore(&max3421_hcd->lock, - flags); - usb_hcd_giveback_urb(hcd, urb, 0); - spin_lock_irqsave(&max3421_hcd->lock, flags); + if (urb == max3421_hcd->curr_urb) { + max3421_hcd->urb_done = 1; + max3421_hcd->hien &= ~(BIT(MAX3421_HI_HXFRDN_BIT) | + BIT(MAX3421_HI_RCVDAV_BIT)); + } else { + usb_hcd_unlink_urb_from_ep(hcd, urb); + spin_unlock_irqrestore(&max3421_hcd->lock, + flags); + usb_hcd_giveback_urb(hcd, urb, 0); + spin_lock_irqsave(&max3421_hcd->lock, flags); + } } } } @@ -1940,6 +1946,12 @@ max3421_remove(struct spi_device *spi) usb_put_hcd(hcd); } +static const struct spi_device_id max3421_spi_ids[] = { + { "max3421" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, max3421_spi_ids); + static const struct of_device_id max3421_of_match_table[] = { { .compatible = "maxim,max3421", }, {}, @@ -1949,6 +1961,7 @@ MODULE_DEVICE_TABLE(of, max3421_of_match_table); static struct spi_driver max3421_driver = { .probe = max3421_probe, .remove = max3421_remove, + .id_table = max3421_spi_ids, .driver = { .name = "max3421-hcd", .of_match_table = max3421_of_match_table, diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c index 19d5777f5db2..361d33b0c4d2 100644 --- a/drivers/usb/host/octeon-hcd.c +++ b/drivers/usb/host/octeon-hcd.c @@ -3346,7 +3346,7 @@ static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case USB_PORT_FEAT_INDICATOR: dev_dbg(dev, " INDICATOR\n"); - /* Port inidicator not supported */ + /* Port indicator not supported */ break; case USB_PORT_FEAT_C_CONNECTION: dev_dbg(dev, " C_CONNECTION\n"); @@ -3711,8 +3711,8 @@ static struct platform_driver octeon_usb_driver = { .name = "octeon-hcd", .of_match_table = octeon_usb_match, }, - .probe = octeon_usb_probe, - .remove_new = octeon_usb_remove, + .probe = octeon_usb_probe, + .remove = octeon_usb_remove, }; static int __init octeon_usb_driver_init(void) diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index f691cd98a574..5df793dcb25d 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -685,7 +685,7 @@ static SIMPLE_DEV_PM_OPS(ohci_hcd_at91_pm_ops, ohci_hcd_at91_drv_suspend, static struct platform_driver ohci_hcd_at91_driver = { .probe = ohci_hcd_at91_drv_probe, - .remove_new = ohci_hcd_at91_drv_remove, + .remove = ohci_hcd_at91_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "at91_ohci", diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index d2b67da76762..3c5ca2d7c92e 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -531,7 +531,7 @@ static const struct ohci_driver_overrides da8xx_overrides __initconst = { */ static struct platform_driver ohci_hcd_da8xx_driver = { .probe = ohci_da8xx_probe, - .remove_new = ohci_da8xx_remove, + .remove = ohci_da8xx_remove, .shutdown = usb_hcd_platform_shutdown, #ifdef CONFIG_PM .suspend = ohci_da8xx_suspend, diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 1379e03644b2..cc5cb0900988 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -262,7 +262,7 @@ MODULE_DEVICE_TABLE(of, exynos_ohci_match); static struct platform_driver exynos_ohci_driver = { .probe = exynos_ohci_probe, - .remove_new = exynos_ohci_remove, + .remove = exynos_ohci_remove, .shutdown = exynos_ohci_shutdown, .driver = { .name = "exynos-ohci", diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 9b24181fee60..c7784bf8101d 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1003,7 +1003,7 @@ static void ohci_stop (struct usb_hcd *hcd) if (quirk_nec(ohci)) flush_work(&ohci->nec_work); - del_timer_sync(&ohci->io_watchdog); + timer_delete_sync(&ohci->io_watchdog); ohci->prev_frame_no = IO_WATCHDOG_OFF; ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 90cee192e96d..b3d734ab6201 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -315,7 +315,7 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) spin_unlock_irq (&ohci->lock); if (rc == 0) { - del_timer_sync(&ohci->io_watchdog); + timer_delete_sync(&ohci->io_watchdog); ohci->prev_frame_no = IO_WATCHDOG_OFF; } return rc; diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 5b775e1ea527..24d5a1dc5056 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -254,7 +254,7 @@ static struct platform_driver ohci_hcd_nxp_driver = { .of_match_table = of_match_ptr(ohci_hcd_nxp_match), }, .probe = ohci_hcd_nxp_probe, - .remove_new = ohci_hcd_nxp_remove, + .remove = ohci_hcd_nxp_remove, }; static int __init ohci_nxp_init(void) diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 21a6f6c55e07..f6e56c4b9914 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -152,7 +152,7 @@ static int ohci_omap_reset(struct usb_hcd *hcd) rh &= ~RH_A_NOCP; - /* gpio9 for overcurrent detction */ + /* gpio9 for overcurrent detection */ omap_cfg_reg(W8_1610_GPIO9); /* for paranoia's sake: disable USB.PUEN */ @@ -390,7 +390,7 @@ static int ohci_omap_resume(struct platform_device *dev) */ static struct platform_driver ohci_hcd_omap_driver = { .probe = ohci_hcd_omap_probe, - .remove_new = ohci_hcd_omap_remove, + .remove = ohci_hcd_omap_remove, .shutdown = usb_hcd_platform_shutdown, #ifdef CONFIG_PM .suspend = ohci_omap_suspend, diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 4a75507325dd..f47ae12cde6a 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -344,7 +344,7 @@ static const struct dev_pm_ops ohci_platform_pm_ops = { static struct platform_driver ohci_platform_driver = { .id_table = ohci_platform_table, .probe = ohci_platform_probe, - .remove_new = ohci_platform_remove, + .remove = ohci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ohci-platform", diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index a6be061f8653..acd0a0e398a4 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -219,7 +219,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_ppc_of_match); static struct platform_driver ohci_hcd_ppc_of_driver = { .probe = ohci_hcd_ppc_of_probe, - .remove_new = ohci_hcd_ppc_of_remove, + .remove = ohci_hcd_ppc_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ppc-of-ohci", diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 3348c25ddb18..45d026e85168 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -569,7 +569,7 @@ static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = { static struct platform_driver ohci_hcd_pxa27x_driver = { .probe = ohci_hcd_pxa27x_probe, - .remove_new = ohci_hcd_pxa27x_remove, + .remove = ohci_hcd_pxa27x_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "pxa27x-ohci", diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index c5c9b4cbcb9a..66d970854357 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -457,7 +457,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_s3c2410_dt_ids); static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_probe, - .remove_new = ohci_hcd_s3c2410_remove, + .remove = ohci_hcd_s3c2410_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "s3c2410-ohci", diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 4b39e9d6f33a..843a5378764e 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -252,7 +252,7 @@ static int ohci_sm501_resume(struct platform_device *pdev) */ static struct platform_driver ohci_hcd_sm501_driver = { .probe = ohci_hcd_sm501_drv_probe, - .remove_new = ohci_hcd_sm501_drv_remove, + .remove = ohci_hcd_sm501_drv_remove, .shutdown = usb_hcd_platform_shutdown, .suspend = ohci_sm501_suspend, .resume = ohci_sm501_resume, diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 993f347c5c28..d7131e5a4477 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -157,7 +157,7 @@ MODULE_DEVICE_TABLE(of, spear_ohci_id_table); /* Driver definition to register with the platform bus */ static struct platform_driver spear_ohci_hcd_driver = { .probe = spear_ohci_hcd_drv_probe, - .remove_new = spear_ohci_hcd_drv_remove, + .remove = spear_ohci_hcd_drv_remove, #ifdef CONFIG_PM .suspend = spear_ohci_hcd_drv_suspend, .resume = spear_ohci_hcd_drv_resume, diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c index 214342013f7e..d1656fce5400 100644 --- a/drivers/usb/host/ohci-st.c +++ b/drivers/usb/host/ohci-st.c @@ -298,7 +298,7 @@ MODULE_DEVICE_TABLE(of, st_ohci_platform_ids); static struct platform_driver ohci_platform_driver = { .probe = st_ohci_platform_probe, - .remove_new = st_ohci_platform_remove, + .remove = st_ohci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "st-ohci", diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index ca3859463ba1..d75b1b9b4db0 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -15,6 +15,7 @@ #include <linux/ioport.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/errno.h> #include <linux/timer.h> #include <linux/list.h> @@ -885,7 +886,7 @@ static int oxu_buf_alloc(struct oxu_hcd *oxu, struct ehci_qtd *qtd, int len) int a_blocks; /* blocks allocated */ int i, j; - /* Don't allocte bigger than supported */ + /* Don't allocate bigger than supported */ if (len > BUFFER_SIZE * BUFFER_NUM) { oxu_err(oxu, "buffer too big (%d)\n", len); return -ENOMEM; @@ -902,7 +903,7 @@ static int oxu_buf_alloc(struct oxu_hcd *oxu, struct ehci_qtd *qtd, int len) /* Find a suitable available data buffer */ for (i = 0; i < BUFFER_NUM; - i += max(a_blocks, (int)oxu->db_used[i])) { + i += max_t(int, a_blocks, oxu->db_used[i])) { /* Check all the required blocks are available */ for (j = 0; j < a_blocks; j++) @@ -1126,7 +1127,7 @@ static void ehci_mem_cleanup(struct oxu_hcd *oxu) qh_put(oxu->async); oxu->async = NULL; - del_timer(&oxu->urb_timer); + timer_delete(&oxu->urb_timer); oxu->periodic = NULL; @@ -2756,7 +2757,7 @@ static void ehci_port_power(struct oxu_hcd *oxu, int is_on) if (!HCS_PPC(oxu->hcs_params)) return; - oxu_dbg(oxu, "...power%s ports...\n", is_on ? "up" : "down"); + oxu_dbg(oxu, "...power%s ports...\n", str_up_down(is_on)); for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; ) { if (is_on) oxu_hub_control(oxu_to_hcd(oxu), SetPortFeature, @@ -3040,7 +3041,7 @@ static int oxu_hcd_init(struct usb_hcd *hcd) * make problems: throughput reduction (!), data errors... */ if (park) { - park = min(park, (unsigned) 3); + park = min_t(unsigned int, park, 3); temp |= CMD_PARK; temp |= park << 8; } @@ -3153,7 +3154,7 @@ static void oxu_stop(struct usb_hcd *hcd) ehci_port_power(oxu, 0); /* no more interrupts ... */ - del_timer_sync(&oxu->watchdog); + timer_delete_sync(&oxu->watchdog); spin_lock_irq(&oxu->lock); if (HC_IS_RUNNING(hcd->state)) @@ -3886,7 +3887,7 @@ static int oxu_bus_suspend(struct usb_hcd *hcd) spin_unlock_irq(&oxu->lock); /* turn off now-idle HC */ - del_timer_sync(&oxu->watchdog); + timer_delete_sync(&oxu->watchdog); spin_lock_irq(&oxu->lock); ehci_halt(oxu); hcd->state = HC_STATE_SUSPENDED; @@ -4289,7 +4290,7 @@ static int oxu_drv_resume(struct device *dev) static struct platform_driver oxu_driver = { .probe = oxu_drv_probe, - .remove_new = oxu_drv_remove, + .remove = oxu_drv_remove, .shutdown = oxu_drv_shutdown, .suspend = oxu_drv_suspend, .resume = oxu_drv_resume, diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 1f9c1b1435d8..0404489c2f6a 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -958,6 +958,15 @@ static void quirk_usb_disable_ehci(struct pci_dev *pdev) * booting from USB disk or using a usb keyboard */ hcc_params = readl(base + EHCI_HCC_PARAMS); + + /* LS7A EHCI controller doesn't have extended capabilities, the + * EECP (EHCI Extended Capabilities Pointer) field of HCCPARAMS + * register should be 0x0 but it reads as 0xa0. So clear it to + * avoid error messages on boot. + */ + if (pdev->vendor == PCI_VENDOR_ID_LOONGSON && pdev->device == 0x7a14) + hcc_params &= ~(0xffL << 8); + offset = (hcc_params >> 8) & 0xff; while (offset && --count) { pci_read_config_dword(pdev, offset, &cap); diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 6576515a29cd..67e472116d11 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -759,7 +759,7 @@ static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597, struct r8a66597_pipe_info *info = &pipe->info; unsigned short mbw = mbw_value(r8a66597); - /* pipe dma is only for external controlles */ + /* pipe dma is only for external controllers */ if (r8a66597->pdata->on_chip) return; @@ -1336,7 +1336,7 @@ static void packet_read(struct r8a66597 *r8a66597, u16 pipenum) buf = (void *)urb->transfer_buffer + urb->actual_length; urb_len = urb->transfer_buffer_length - urb->actual_length; } - bufsize = min(urb_len, (int) td->maxpacket); + bufsize = min_t(int, urb_len, td->maxpacket); if (rcv_len <= bufsize) { size = rcv_len; } else { @@ -2384,7 +2384,7 @@ static void r8a66597_remove(struct platform_device *pdev) struct r8a66597 *r8a66597 = platform_get_drvdata(pdev); struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597); - del_timer_sync(&r8a66597->rh_timer); + timer_delete_sync(&r8a66597->rh_timer); usb_remove_hcd(hcd); iounmap(r8a66597->reg); if (r8a66597->pdata->on_chip) @@ -2510,7 +2510,7 @@ clean_up: static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, - .remove_new = r8a66597_remove, + .remove = r8a66597_remove, .driver = { .name = hcd_name, .pm = R8A66597_DEV_PM_OPS, diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 92f2d1238448..718b1b7fe366 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -48,6 +48,7 @@ #include <linux/usb/hcd.h> #include <linux/platform_device.h> #include <linux/prefetch.h> +#include <linux/string_choices.h> #include <linux/debugfs.h> #include <linux/seq_file.h> @@ -98,7 +99,7 @@ static void port_power(struct sl811 *sl811, int is_on) if (sl811->board && sl811->board->port_power) { /* switch VBUS, at 500mA unless hub power budget gets set */ dev_dbg(hcd->self.controller, "power %s\n", - is_on ? "on" : "off"); + str_on_off(is_on)); sl811->board->port_power(hcd->self.controller, is_on); } @@ -1514,7 +1515,7 @@ sl811h_stop(struct usb_hcd *hcd) struct sl811 *sl811 = hcd_to_sl811(hcd); unsigned long flags; - del_timer_sync(&hcd->rh_timer); + timer_delete_sync(&hcd->rh_timer); spin_lock_irqsave(&sl811->lock, flags); port_power(sl811, 0); @@ -1784,7 +1785,7 @@ sl811h_resume(struct platform_device *dev) /* this driver is exported so sl811_cs can depend on it */ struct platform_driver sl811h_driver = { .probe = sl811h_probe, - .remove_new = sl811h_remove, + .remove = sl811h_remove, .suspend = sl811h_suspend, .resume = sl811h_resume, diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index cfebb833668e..8a1f6d1b5b56 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -184,7 +184,7 @@ MODULE_DEVICE_TABLE(of, uhci_hcd_grlib_of_match); static struct platform_driver uhci_grlib_driver = { .probe = uhci_hcd_grlib_probe, - .remove_new = uhci_hcd_grlib_remove, + .remove = uhci_hcd_grlib_remove, .shutdown = uhci_hcd_grlib_shutdown, .driver = { .name = "grlib-uhci", diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index fd2408b553cf..14e6dfef16c6 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -716,7 +716,7 @@ static void uhci_stop(struct usb_hcd *hcd) spin_unlock_irq(&uhci->lock); synchronize_irq(hcd->irq); - del_timer_sync(&uhci->fsbr_timer); + timer_delete_sync(&uhci->fsbr_timer); release_uhci(uhci); } diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 3dec5dd3a0d5..a7c934404ebc 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -184,7 +184,7 @@ MODULE_DEVICE_TABLE(of, platform_uhci_ids); static struct platform_driver uhci_platform_driver = { .probe = uhci_hcd_platform_probe, - .remove_new = uhci_hcd_platform_remove, + .remove = uhci_hcd_platform_remove, .shutdown = uhci_hcd_platform_shutdown, .driver = { .name = "platform-uhci", diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 35fcb826152c..45a8256a665f 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -84,7 +84,7 @@ static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp) uhci_fsbr_on(uhci); else if (uhci->fsbr_expiring) { uhci->fsbr_expiring = 0; - del_timer(&uhci->fsbr_timer); + timer_delete(&uhci->fsbr_timer); } } } diff --git a/drivers/usb/host/xen-hcd.c b/drivers/usb/host/xen-hcd.c index 46fdab940092..05943f2213e4 100644 --- a/drivers/usb/host/xen-hcd.c +++ b/drivers/usb/host/xen-hcd.c @@ -327,7 +327,7 @@ static int xenhcd_bus_suspend(struct usb_hcd *hcd) } spin_unlock_irq(&info->lock); - del_timer_sync(&info->watchdog); + timer_delete_sync(&info->watchdog); return ret; } @@ -1307,7 +1307,7 @@ static void xenhcd_stop(struct usb_hcd *hcd) { struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); - del_timer_sync(&info->watchdog); + timer_delete_sync(&info->watchdog); spin_lock_irq(&info->lock); /* cancel all urbs */ hcd->state = HC_STATE_HALT; diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index 9e94cebf4a56..f6b9a00a0ab9 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -83,3 +83,9 @@ #define HCC2_CIC(p) ((p) & (1 << 5)) /* true: HC support Extended TBC Capability, Isoc burst count > 65535 */ #define HCC2_ETC(p) ((p) & (1 << 6)) +/* true: HC support Extended TBC TRB Status Capability */ +#define HCC2_ETC_TSC(p) ((p) & (1 << 7)) +/* true: HC support Get/Set Extended Property Capability */ +#define HCC2_GSC(p) ((p) & (1 << 8)) +/* true: HC support Virtualization Based Trusted I/O Capability */ +#define HCC2_VTC(p) ((p) & (1 << 9)) diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index 241d7aa1fbc2..fd7895b24367 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -248,8 +248,9 @@ xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1, trb->generic.field[2] = cpu_to_le32(field3); trb->generic.field[3] = cpu_to_le32(field4); - trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic); - + 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)) { @@ -471,7 +472,7 @@ xhci_dbc_ring_alloc(struct device *dev, enum xhci_ring_type type, gfp_t flags) trb->link.control = cpu_to_le32(LINK_TOGGLE | TRB_TYPE(TRB_LINK)); } INIT_LIST_HEAD(&ring->td_list); - xhci_initialize_ring_info(ring, 1); + xhci_initialize_ring_info(ring); return ring; dma_fail: kfree(seg); @@ -747,7 +748,7 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) return; } - trace_xhci_dbc_handle_transfer(ring, &req->trb->generic); + trace_xhci_dbc_handle_transfer(ring, &req->trb->generic, req->trb_dma); switch (comp_code) { case COMP_SUCCESS: @@ -898,7 +899,9 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) */ rmb(); - trace_xhci_dbc_handle_event(dbc->ring_evt, &evt->generic); + trace_xhci_dbc_handle_event(dbc->ring_evt, &evt->generic, + xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg, + dbc->ring_evt->dequeue)); switch (le32_to_cpu(evt->event_cmd.flags) & TRB_TYPE_BITMASK) { case TRB_TYPE(TRB_PORT_STATUS): @@ -954,7 +957,7 @@ static void xhci_dbc_handle_events(struct work_struct *work) /* set fast poll rate if there are pending data transfers */ if (!list_empty(&dbc->eps[BULK_OUT].list_pending) || !list_empty(&dbc->eps[BULK_IN].list_pending)) - poll_interval = 1; + poll_interval = 0; break; default: dev_info(dbc->dev, "stop handling dbc events\n"); diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c index d719c16ea30b..60ed753c85bb 100644 --- a/drivers/usb/host/xhci-dbgtty.c +++ b/drivers/usb/host/xhci-dbgtty.c @@ -110,15 +110,74 @@ static void dbc_start_rx(struct dbc_port *port) } } +/* + * Queue received data to tty buffer and push it. + * + * Returns nr of remaining bytes that didn't fit tty buffer, i.e. 0 if all + * bytes sucessfullt moved. In case of error returns negative errno. + * Call with lock held + */ +static int dbc_rx_push_buffer(struct dbc_port *port, struct dbc_request *req) +{ + char *packet = req->buf; + unsigned int n, size = req->actual; + int count; + + if (!req->actual) + return 0; + + /* if n_read is set then request was partially moved to tty buffer */ + n = port->n_read; + if (n) { + packet += n; + size -= n; + } + + count = tty_insert_flip_string(&port->port, packet, size); + if (count) + tty_flip_buffer_push(&port->port); + if (count != size) { + port->n_read += count; + return size - count; + } + + port->n_read = 0; + return 0; +} + static void dbc_read_complete(struct xhci_dbc *dbc, struct dbc_request *req) { unsigned long flags; struct dbc_port *port = dbc_to_port(dbc); + struct tty_struct *tty; + int untransferred; + + tty = port->port.tty; spin_lock_irqsave(&port->port_lock, flags); + + /* + * Only defer copyig data to tty buffer in case: + * - !list_empty(&port->read_queue), there are older pending data + * - tty is throttled + * - failed to copy all data to buffer, defer remaining part + */ + + if (list_empty(&port->read_queue) && tty && !tty_throttled(tty)) { + untransferred = dbc_rx_push_buffer(port, req); + if (untransferred == 0) { + list_add_tail(&req->list_pool, &port->read_pool); + if (req->status != -ESHUTDOWN) + dbc_start_rx(port); + goto out; + } + } + + /* defer moving data from req to tty buffer to a tasklet */ list_add_tail(&req->list_pool, &port->read_queue); tasklet_schedule(&port->push); +out: spin_unlock_irqrestore(&port->port_lock, flags); } @@ -331,10 +390,10 @@ static void dbc_rx_push(struct tasklet_struct *t) struct dbc_request *req; struct tty_struct *tty; unsigned long flags; - bool do_push = false; bool disconnect = false; struct dbc_port *port = from_tasklet(port, t, push); struct list_head *queue = &port->read_queue; + int untransferred; spin_lock_irqsave(&port->port_lock, flags); tty = port->port.tty; @@ -356,42 +415,15 @@ static void dbc_rx_push(struct tasklet_struct *t) break; } - if (req->actual) { - char *packet = req->buf; - unsigned int n, size = req->actual; - int count; - - n = port->n_read; - if (n) { - packet += n; - size -= n; - } - - count = tty_insert_flip_string(&port->port, packet, - size); - if (count) - do_push = true; - if (count != size) { - port->n_read += count; - break; - } - port->n_read = 0; - } + untransferred = dbc_rx_push_buffer(port, req); + if (untransferred > 0) + break; list_move_tail(&req->list_pool, &port->read_pool); } - if (do_push) - tty_flip_buffer_push(&port->port); - - if (!list_empty(queue) && tty) { - if (!tty_throttled(tty)) { - if (do_push) - tasklet_schedule(&port->push); - else - pr_warn("ttyDBC0: RX not scheduled?\n"); - } - } + if (!list_empty(queue)) + tasklet_schedule(&port->push); if (!disconnect) dbc_start_rx(port); diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index f8ba15e7c225..1f5ef174abea 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -214,14 +214,11 @@ static void xhci_ring_dump_segment(struct seq_file *s, static int xhci_ring_trb_show(struct seq_file *s, void *unused) { - int i; struct xhci_ring *ring = *(struct xhci_ring **)s->private; struct xhci_segment *seg = ring->first_seg; - for (i = 0; i < ring->num_segs; i++) { + xhci_for_each_ring_seg(ring->first_seg, seg) xhci_ring_dump_segment(s, seg); - seg = seg->next; - } return 0; } @@ -235,16 +232,7 @@ static struct xhci_file_map ring_files[] = { static int xhci_ring_open(struct inode *inode, struct file *file) { - int i; - struct xhci_file_map *f_map; - const char *file_name = file_dentry(file)->d_iname; - - for (i = 0; i < ARRAY_SIZE(ring_files); i++) { - f_map = &ring_files[i]; - - if (strcmp(f_map->name, file_name) == 0) - break; - } + const struct xhci_file_map *f_map = debugfs_get_aux(file); return single_open(file, f_map->show, inode->i_private); } @@ -291,12 +279,13 @@ static int xhci_endpoint_context_show(struct seq_file *s, void *unused) for (ep_index = 0; ep_index < 31; ep_index++) { ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); dma = dev->out_ctx->dma + (ep_index + 1) * CTX_SIZE(xhci->hcc_params); - seq_printf(s, "%pad: %s\n", &dma, + seq_printf(s, "%pad: %s, virt_state:%#x\n", &dma, xhci_decode_ep_context(str, le32_to_cpu(ep_ctx->ep_info), le32_to_cpu(ep_ctx->ep_info2), le64_to_cpu(ep_ctx->deq), - le32_to_cpu(ep_ctx->tx_info))); + le32_to_cpu(ep_ctx->tx_info)), + dev->eps[ep_index].ep_state); } return 0; @@ -320,16 +309,7 @@ static struct xhci_file_map context_files[] = { static int xhci_context_open(struct inode *inode, struct file *file) { - int i; - struct xhci_file_map *f_map; - const char *file_name = file_dentry(file)->d_iname; - - for (i = 0; i < ARRAY_SIZE(context_files); i++) { - f_map = &context_files[i]; - - if (strcmp(f_map->name, file_name) == 0) - break; - } + const struct xhci_file_map *f_map = debugfs_get_aux(file); return single_open(file, f_map->show, inode->i_private); } @@ -412,7 +392,8 @@ static void xhci_debugfs_create_files(struct xhci_hcd *xhci, int i; for (i = 0; i < nentries; i++) - debugfs_create_file(files[i].name, 0444, parent, data, fops); + debugfs_create_file_aux(files[i].name, 0444, parent, + data, &files[i], fops); } static struct dentry *xhci_debugfs_create_ring_dir(struct xhci_hcd *xhci, diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c index f9a4a4b0eb57..02396c8721dc 100644 --- a/drivers/usb/host/xhci-histb.c +++ b/drivers/usb/host/xhci-histb.c @@ -355,7 +355,7 @@ static int __maybe_unused xhci_histb_resume(struct device *dev) if (!device_may_wakeup(dev)) xhci_histb_host_enable(histb); - return xhci_resume(xhci, PMSG_RESUME); + return xhci_resume(xhci, false, false); } static const struct dev_pm_ops xhci_histb_pm_ops = { @@ -373,7 +373,7 @@ MODULE_DEVICE_TABLE(of, histb_xhci_of_match); static struct platform_driver histb_xhci_driver = { .probe = xhci_histb_probe, - .remove_new = xhci_histb_remove, + .remove = xhci_histb_remove, .driver = { .name = "xhci-histb", .pm = DEV_PM_OPS, diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 8d774f19271e..c0f226584a40 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/unaligned.h> #include <linux/bitfield.h> +#include <linux/pci.h> #include "xhci.h" #include "xhci-trace.h" @@ -770,9 +771,16 @@ static int xhci_exit_test_mode(struct xhci_hcd *xhci) enum usb_link_tunnel_mode xhci_port_is_tunneled(struct xhci_hcd *xhci, struct xhci_port *port) { + struct usb_hcd *hcd; void __iomem *base; u32 offset; + /* Don't try and probe this capability for non-Intel hosts */ + hcd = xhci_to_hcd(xhci); + if (!dev_is_pci(hcd->self.controller) || + to_pci_dev(hcd->self.controller)->vendor != PCI_VENDOR_ID_INTEL) + return USB_LINK_UNKNOWN; + base = &xhci->cap_regs->hc_capbase; offset = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_INTEL_SPR_SHADOW); @@ -918,7 +926,7 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) { xhci->port_status_u0 |= 1 << wIndex; if (xhci->port_status_u0 == all_ports_seen_u0) { - del_timer_sync(&xhci->comp_mode_recovery_timer); + timer_delete_sync(&xhci->comp_mode_recovery_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "All USB3 ports have entered U0 already!"); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, @@ -946,9 +954,9 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, } /* did port event handler already start resume timing? */ if (!port->resume_timestamp) { - /* If not, maybe we are in a host initated resume? */ + /* If not, maybe we are in a host initiated resume? */ if (test_bit(wIndex, &bus_state->resuming_ports)) { - /* Host initated resume doesn't time the resume + /* Host initiated resume doesn't time the resume * signalling using resume_done[]. * It manually sets RESUME state, sleeps 20ms * and sets U0 state. This should probably be @@ -1924,7 +1932,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) /* resume already initiated */ break; default: - /* not in a resumeable state, ignore it */ + /* not in a resumable state, ignore it */ clear_bit(port_index, &bus_state->bus_suspended); break; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index d2900197a49e..d698095fc88d 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -27,14 +27,12 @@ * "All components of all Command and Transfer TRBs shall be initialized to '0'" */ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, - unsigned int cycle_state, unsigned int max_packet, unsigned int num, gfp_t flags) { struct xhci_segment *seg; dma_addr_t dma; - int i; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; seg = kzalloc_node(sizeof(*seg), flags, dev_to_node(dev)); @@ -56,11 +54,6 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, return NULL; } } - /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */ - if (cycle_state == 0) { - for (i = 0; i < TRBS_PER_SEGMENT; i++) - seg->trbs[i].link.control = cpu_to_le32(TRB_CYCLE); - } seg->num = num; seg->dma = dma; seg->next = NULL; @@ -78,82 +71,104 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) kfree(seg); } -static void xhci_free_segments_for_ring(struct xhci_hcd *xhci, - struct xhci_segment *first) +static void xhci_ring_segments_free(struct xhci_hcd *xhci, struct xhci_ring *ring) { - struct xhci_segment *seg; + struct xhci_segment *seg, *next; - seg = first->next; - while (seg && seg != first) { - struct xhci_segment *next = seg->next; + ring->last_seg->next = NULL; + seg = ring->first_seg; + + while (seg) { + next = seg->next; xhci_segment_free(xhci, seg); seg = next; } - xhci_segment_free(xhci, first); } /* - * Make the prev segment point to the next segment. + * Only for transfer and command rings where driver is the producer, not for + * event rings. * - * Change the last TRB in the prev segment to be a Link TRB which points to the + * Change the last TRB in the segment to be a Link TRB which points to the * DMA address of the next segment. The caller needs to set any Link TRB * related flags, such as End TRB, Toggle Cycle, and no snoop. */ -static void xhci_link_segments(struct xhci_segment *prev, - struct xhci_segment *next, - enum xhci_ring_type type, bool chain_links) +static void xhci_set_link_trb(struct xhci_segment *seg, bool chain_links) { + union xhci_trb *trb; u32 val; - if (!prev || !next) + if (!seg || !seg->next) return; - prev->next = next; - if (type != TYPE_EVENT) { - prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = - cpu_to_le64(next->dma); - /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ - val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); - val &= ~TRB_TYPE_BITMASK; - val |= TRB_TYPE(TRB_LINK); - if (chain_links) - val |= TRB_CHAIN; - prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); - } + trb = &seg->trbs[TRBS_PER_SEGMENT - 1]; + + /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ + val = le32_to_cpu(trb->link.control); + val &= ~TRB_TYPE_BITMASK; + val |= TRB_TYPE(TRB_LINK); + if (chain_links) + val |= TRB_CHAIN; + trb->link.control = cpu_to_le32(val); + trb->link.segment_ptr = cpu_to_le64(seg->next->dma); +} + +static void xhci_initialize_ring_segments(struct xhci_hcd *xhci, struct xhci_ring *ring) +{ + struct xhci_segment *seg; + bool chain_links; + + if (ring->type == TYPE_EVENT) + return; + + chain_links = xhci_link_chain_quirk(xhci, ring->type); + xhci_for_each_ring_seg(ring->first_seg, seg) + xhci_set_link_trb(seg, chain_links); + + /* See section 4.9.2.1 and 6.4.4.1 */ + ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE); } /* - * Link the ring to the new segments. + * Link the src ring segments to the dst ring. * Set Toggle Cycle for the new ring if needed. */ -static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring, - struct xhci_segment *first, struct xhci_segment *last, - unsigned int num_segs) +static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *src, struct xhci_ring *dst) { - struct xhci_segment *next, *seg; + struct xhci_segment *seg; bool chain_links; - if (!ring || !first || !last) + if (!src || !dst) return; - chain_links = xhci_link_chain_quirk(xhci, ring->type); + /* If the cycle state is 0, set the cycle bit to 1 for all the TRBs */ + if (dst->cycle_state == 0) { + xhci_for_each_ring_seg(src->first_seg, seg) { + for (int i = 0; i < TRBS_PER_SEGMENT; i++) + seg->trbs[i].link.control |= cpu_to_le32(TRB_CYCLE); + } + } - next = ring->enq_seg->next; - xhci_link_segments(ring->enq_seg, first, ring->type, chain_links); - xhci_link_segments(last, next, ring->type, chain_links); - ring->num_segs += num_segs; + src->last_seg->next = dst->enq_seg->next; + dst->enq_seg->next = src->first_seg; + if (dst->type != TYPE_EVENT) { + chain_links = xhci_link_chain_quirk(xhci, dst->type); + xhci_set_link_trb(dst->enq_seg, chain_links); + xhci_set_link_trb(src->last_seg, chain_links); + } + dst->num_segs += src->num_segs; - if (ring->enq_seg == ring->last_seg) { - if (ring->type != TYPE_EVENT) { - ring->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control + if (dst->enq_seg == dst->last_seg) { + if (dst->type != TYPE_EVENT) + dst->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control &= ~cpu_to_le32(LINK_TOGGLE); - last->trbs[TRBS_PER_SEGMENT-1].link.control - |= cpu_to_le32(LINK_TOGGLE); - } - ring->last_seg = last; + + dst->last_seg = src->last_seg; + } else if (dst->type != TYPE_EVENT) { + src->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control &= ~cpu_to_le32(LINK_TOGGLE); } - for (seg = ring->enq_seg; seg != ring->last_seg; seg = seg->next) + for (seg = dst->enq_seg; seg != dst->last_seg; seg = seg->next) seg->next->num = seg->num + 1; } @@ -224,7 +239,6 @@ static int xhci_update_stream_segment_mapping( struct radix_tree_root *trb_address_map, struct xhci_ring *ring, struct xhci_segment *first_seg, - struct xhci_segment *last_seg, gfp_t mem_flags) { struct xhci_segment *seg; @@ -234,28 +248,22 @@ static int xhci_update_stream_segment_mapping( if (WARN_ON_ONCE(trb_address_map == NULL)) return 0; - seg = first_seg; - do { + xhci_for_each_ring_seg(first_seg, seg) { ret = xhci_insert_segment_mapping(trb_address_map, ring, seg, mem_flags); if (ret) goto remove_streams; - if (seg == last_seg) - return 0; - seg = seg->next; - } while (seg != first_seg); + } return 0; remove_streams: failed_seg = seg; - seg = first_seg; - do { + xhci_for_each_ring_seg(first_seg, seg) { xhci_remove_segment_mapping(trb_address_map, seg); if (seg == failed_seg) return ret; - seg = seg->next; - } while (seg != first_seg); + } return ret; } @@ -267,17 +275,14 @@ static void xhci_remove_stream_mapping(struct xhci_ring *ring) if (WARN_ON_ONCE(ring->trb_address_map == NULL)) return; - seg = ring->first_seg; - do { + xhci_for_each_ring_seg(ring->first_seg, seg) xhci_remove_segment_mapping(ring->trb_address_map, seg); - seg = seg->next; - } while (seg != ring->first_seg); } static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags) { return xhci_update_stream_segment_mapping(ring->trb_address_map, ring, - ring->first_seg, ring->last_seg, mem_flags); + ring->first_seg, mem_flags); } /* XXX: Do we need the hcd structure in all these functions? */ @@ -291,14 +296,13 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) if (ring->first_seg) { if (ring->type == TYPE_STREAM) xhci_remove_stream_mapping(ring); - xhci_free_segments_for_ring(xhci, ring->first_seg); + xhci_ring_segments_free(xhci, ring); } kfree(ring); } -void xhci_initialize_ring_info(struct xhci_ring *ring, - unsigned int cycle_state) +void xhci_initialize_ring_info(struct xhci_ring *ring) { /* The ring is empty, so the enqueue pointer == dequeue pointer */ ring->enqueue = ring->first_seg->trbs; @@ -312,7 +316,7 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, * New rings are initialized with cycle state equal to 1; if we are * handling ring expansion, set the cycle state equal to the old ring. */ - ring->cycle_state = cycle_state; + ring->cycle_state = 1; /* * Each segment has a link TRB, and leave an extra TRB for SW @@ -323,46 +327,36 @@ void xhci_initialize_ring_info(struct xhci_ring *ring, EXPORT_SYMBOL_GPL(xhci_initialize_ring_info); /* Allocate segments and link them for a ring */ -static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, - struct xhci_segment **first, - struct xhci_segment **last, - unsigned int num_segs, - unsigned int cycle_state, - enum xhci_ring_type type, - unsigned int max_packet, - gfp_t flags) +static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, struct xhci_ring *ring, gfp_t flags) { struct xhci_segment *prev; unsigned int num = 0; - bool chain_links; - chain_links = xhci_link_chain_quirk(xhci, type); - - prev = xhci_segment_alloc(xhci, cycle_state, max_packet, num, flags); + prev = xhci_segment_alloc(xhci, ring->bounce_buf_len, num, flags); if (!prev) return -ENOMEM; num++; - *first = prev; - while (num < num_segs) { + ring->first_seg = prev; + while (num < ring->num_segs) { struct xhci_segment *next; - next = xhci_segment_alloc(xhci, cycle_state, max_packet, num, - flags); + next = xhci_segment_alloc(xhci, ring->bounce_buf_len, num, flags); if (!next) goto free_segments; - xhci_link_segments(prev, next, type, chain_links); + prev->next = next; prev = next; num++; } - xhci_link_segments(prev, *first, type, chain_links); - *last = prev; + ring->last_seg = prev; + ring->last_seg->next = ring->first_seg; return 0; free_segments: - xhci_free_segments_for_ring(xhci, *first); + ring->last_seg = prev; + xhci_ring_segments_free(xhci, ring); return -ENOMEM; } @@ -373,9 +367,8 @@ free_segments: * Set the end flag and the cycle toggle bit on the last segment. * See section 4.9.1 and figures 15 and 16. */ -struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, - unsigned int num_segs, unsigned int cycle_state, - enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, + enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) { struct xhci_ring *ring; int ret; @@ -392,18 +385,12 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, if (num_segs == 0) return ring; - ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg, &ring->last_seg, num_segs, - cycle_state, type, max_packet, flags); + ret = xhci_alloc_segments_for_ring(xhci, ring, flags); if (ret) goto fail; - /* Only event ring does not use link TRB */ - if (type != TYPE_EVENT) { - /* See section 4.9.2.1 and 6.4.4.1 */ - ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= - cpu_to_le32(LINK_TOGGLE); - } - xhci_initialize_ring_info(ring, cycle_state); + xhci_initialize_ring_segments(xhci, ring); + xhci_initialize_ring_info(ring); trace_xhci_ring_alloc(ring); return ring; @@ -427,23 +414,29 @@ void xhci_free_endpoint_ring(struct xhci_hcd *xhci, int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, unsigned int num_new_segs, gfp_t flags) { - struct xhci_segment *first; - struct xhci_segment *last; - int ret; + struct xhci_ring new_ring; + int ret; - ret = xhci_alloc_segments_for_ring(xhci, &first, &last, num_new_segs, ring->cycle_state, - ring->type, ring->bounce_buf_len, flags); + if (num_new_segs == 0) + return 0; + + new_ring.num_segs = num_new_segs; + new_ring.bounce_buf_len = ring->bounce_buf_len; + new_ring.type = ring->type; + ret = xhci_alloc_segments_for_ring(xhci, &new_ring, flags); if (ret) return -ENOMEM; + xhci_initialize_ring_segments(xhci, &new_ring); + if (ring->type == TYPE_STREAM) { - ret = xhci_update_stream_segment_mapping(ring->trb_address_map, - ring, first, last, flags); + ret = xhci_update_stream_segment_mapping(ring->trb_address_map, ring, + new_ring.first_seg, flags); if (ret) goto free_segments; } - xhci_link_rings(xhci, ring, first, last, num_new_segs); + xhci_link_rings(xhci, &new_ring, ring); trace_xhci_ring_expansion(ring); xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion, "ring expansion succeed, now has %d segments", @@ -452,7 +445,7 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, return 0; free_segments: - xhci_free_segments_for_ring(xhci, first); + xhci_ring_segments_free(xhci, &new_ring); return ret; } @@ -642,8 +635,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { stream_info->stream_rings[cur_stream] = - xhci_ring_alloc(xhci, 2, 1, TYPE_STREAM, max_packet, - mem_flags); + xhci_ring_alloc(xhci, 2, TYPE_STREAM, max_packet, mem_flags); cur_ring = stream_info->stream_rings[cur_stream]; if (!cur_ring) goto cleanup_rings; @@ -658,6 +650,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n", cur_stream, addr); ret = xhci_update_stream_mapping(cur_ring, mem_flags); + + trace_xhci_alloc_stream_info_ctx(stream_info, cur_stream); if (ret) { xhci_ring_free(xhci, cur_ring); stream_info->stream_rings[cur_stream] = NULL; @@ -984,7 +978,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, } /* Allocate endpoint 0 ring */ - dev->eps[0].ring = xhci_ring_alloc(xhci, 2, 1, TYPE_CTRL, 0, flags); + dev->eps[0].ring = xhci_ring_alloc(xhci, 2, TYPE_CTRL, 0, flags); if (!dev->eps[0].ring) goto fail; @@ -1461,7 +1455,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, /* Set up the endpoint ring */ virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags); + xhci_ring_alloc(xhci, 2, ring_type, max_packet, mem_flags); if (!virt_dev->eps[ep_index].new_ring) return -ENOMEM; @@ -1959,7 +1953,6 @@ no_bw: xhci->interrupters = NULL; xhci->page_size = 0; - xhci->page_shift = 0; xhci->usb2_rhub.bus_state.bus_suspended = 0; xhci->usb3_rhub.bus_state.bus_suspended = 0; } @@ -2271,7 +2264,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags) if (!ir) return NULL; - ir->event_ring = xhci_ring_alloc(xhci, segs, 1, TYPE_EVENT, 0, flags); + ir->event_ring = xhci_ring_alloc(xhci, segs, TYPE_EVENT, 0, flags); if (!ir->event_ring) { xhci_warn(xhci, "Failed to allocate interrupter event ring\n"); kfree(ir); @@ -2378,6 +2371,22 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs, } EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter); +static void xhci_hcd_page_size(struct xhci_hcd *xhci) +{ + u32 page_size; + + page_size = readl(&xhci->op_regs->page_size) & XHCI_PAGE_SIZE_MASK; + if (!is_power_of_2(page_size)) { + xhci_warn(xhci, "Invalid page size register = 0x%x\n", page_size); + /* Fallback to 4K page size, since that's common */ + page_size = 1; + } + + xhci->page_size = page_size << 12; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "HCD page size set to %iK", + xhci->page_size >> 10); +} + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { struct xhci_interrupter *ir; @@ -2385,7 +2394,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) dma_addr_t dma; unsigned int val, val2; u64 val_64; - u32 page_size, temp; + u32 temp; int i; INIT_LIST_HEAD(&xhci->cmd_list); @@ -2394,20 +2403,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout); init_completion(&xhci->cmd_ring_stop_completion); - page_size = readl(&xhci->op_regs->page_size); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Supported page size register = 0x%x", page_size); - i = ffs(page_size); - if (i < 16) - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Supported page size of %iK", (1 << (i+12)) / 1024); - else - xhci_warn(xhci, "WARN: no supported page size\n"); - /* Use 4K pages, since that's common and the minimum the HC supports */ - xhci->page_shift = 12; - xhci->page_size = 1 << xhci->page_shift; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "HCD page size set to %iK", xhci->page_size / 1024); + xhci_hcd_page_size(xhci); /* * Program the Number of Device Slots Enabled field in the CONFIG @@ -2443,7 +2439,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * and our use of dma addresses in the trb_address_map radix tree needs * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need. */ - if (xhci->quirks & XHCI_ZHAOXIN_TRB_FETCH) + if (xhci->quirks & XHCI_TRB_OVERFETCH) + /* Buggy HC prefetches beyond segment bounds - allocate dummy space at the end */ xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, TRB_SEGMENT_SIZE * 2, TRB_SEGMENT_SIZE * 2, xhci->page_size * 2); else @@ -2473,7 +2470,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) goto fail; /* Set up the command ring to have one segments for now. */ - xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, 0, flags); + xhci->cmd_ring = xhci_ring_alloc(xhci, 1, TYPE_COMMAND, 0, flags); if (!xhci->cmd_ring) goto fail; xhci_dbg_trace(xhci, trace_xhci_dbg_init, @@ -2518,11 +2515,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) ir->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; - /* - * XXX: Might need to set the Interrupter Moderation Register to - * something other than the default (~1ms minimum between interrupts). - * See section 5.5.1.2. - */ for (i = 0; i < MAX_HC_SLOTS; i++) xhci->devs[i] = NULL; diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 3252e3d2d79c..208558cf822d 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -746,10 +746,10 @@ static int __maybe_unused xhci_mtk_suspend(struct device *dev) xhci_dbg(xhci, "%s: stop port polling\n", __func__); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - del_timer_sync(&hcd->rh_timer); + timer_delete_sync(&hcd->rh_timer); if (shared_hcd) { clear_bit(HCD_FLAG_POLL_RH, &shared_hcd->flags); - del_timer_sync(&shared_hcd->rh_timer); + timer_delete_sync(&shared_hcd->rh_timer); } ret = xhci_mtk_host_disable(mtk); @@ -853,7 +853,7 @@ MODULE_DEVICE_TABLE(of, mtk_xhci_of_match); static struct platform_driver mtk_xhci_driver = { .probe = xhci_mtk_probe, - .remove_new = xhci_mtk_remove, + .remove = xhci_mtk_remove, .driver = { .name = "xhci-mtk", .pm = DEV_PM_OPS, diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c index 87f1597a0e5a..257e4d79971f 100644 --- a/drivers/usb/host/xhci-mvebu.c +++ b/drivers/usb/host/xhci-mvebu.c @@ -73,13 +73,3 @@ int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd) return 0; } - -int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd) -{ - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - - /* Without reset on resume, the HC won't work at all */ - xhci->quirks |= XHCI_RESET_ON_RESUME; - - return 0; -} diff --git a/drivers/usb/host/xhci-mvebu.h b/drivers/usb/host/xhci-mvebu.h index 3be021793cc8..9d26e22c4842 100644 --- a/drivers/usb/host/xhci-mvebu.h +++ b/drivers/usb/host/xhci-mvebu.h @@ -12,16 +12,10 @@ struct usb_hcd; #if IS_ENABLED(CONFIG_USB_XHCI_MVEBU) int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd); -int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd); #else static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd) { return 0; } - -static inline int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd) -{ - return 0; -} #endif #endif /* __LINUX_XHCI_MVEBU_H */ diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c index 65fc9319d5e7..620f8f0febb8 100644 --- a/drivers/usb/host/xhci-pci-renesas.c +++ b/drivers/usb/host/xhci-pci-renesas.c @@ -661,5 +661,5 @@ module_pci_driver(xhci_renesas_pci_driver); MODULE_DESCRIPTION("Renesas xHCI PCI Host Controller Driver"); MODULE_FIRMWARE(RENESAS_FW_NAME); -MODULE_IMPORT_NS(xhci); +MODULE_IMPORT_NS("xhci"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index cb07cee9ed0c..0c481cbc8f08 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -28,18 +28,20 @@ #define SPARSE_CNTL_ENABLE 0xC12C /* Device for a quirk */ -#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 -#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000 +#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73 +#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000 #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1009 0x1009 #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1100 0x1100 #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400 -#define PCI_VENDOR_ID_ETRON 0x1b6f -#define PCI_DEVICE_ID_EJ168 0x7023 -#define PCI_DEVICE_ID_EJ188 0x7052 +#define PCI_VENDOR_ID_ETRON 0x1b6f +#define PCI_DEVICE_ID_ETRON_EJ168 0x7023 +#define PCI_DEVICE_ID_ETRON_EJ188 0x7052 -#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 -#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 +#define PCI_DEVICE_ID_VIA_VL805 0x3483 + +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31 +#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI 0x9cb1 #define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5 #define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f @@ -82,9 +84,6 @@ #define PCI_DEVICE_ID_ASMEDIA_3042_XHCI 0x3042 #define PCI_DEVICE_ID_ASMEDIA_3242_XHCI 0x3242 -#define PCI_DEVICE_ID_CADENCE 0x17CD -#define PCI_DEVICE_ID_CADENCE_SSP 0x0200 - static const char hcd_name[] = "xhci_hcd"; static struct hc_driver __read_mostly xhci_pci_hc_driver; @@ -150,14 +149,11 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) hcd->irq = 0; /* - * calculate number of MSI-X vectors supported. - * - HCS_MAX_INTRS: the max number of interrupts the host can handle, - * with max number of interrupters based on the xhci HCSPARAMS1. - * - num_online_cpus: maximum MSI-X vectors per CPUs core. - * Add additional 1 vector to ensure always available interrupt. + * Calculate number of MSI/MSI-X vectors supported. + * - max_interrupters: the max number of interrupts requested, capped to xhci HCSPARAMS1. + * - num_online_cpus: one vector per CPUs core, with at least one overall. */ - xhci->nvecs = min(num_online_cpus() + 1, - HCS_MAX_INTRS(xhci->hcs_params1)); + xhci->nvecs = min(num_online_cpus() + 1, xhci->max_interrupters); /* TODO: Check with MSI Soc for sysdev */ xhci->nvecs = pci_alloc_irq_vectors(pdev, 1, xhci->nvecs, @@ -395,14 +391,12 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_EJ168) { - xhci->quirks |= XHCI_RESET_ON_RESUME; - xhci->quirks |= XHCI_BROKEN_STREAMS; - } - if (pdev->vendor == PCI_VENDOR_ID_ETRON && - pdev->device == PCI_DEVICE_ID_EJ188) { + (pdev->device == PCI_DEVICE_ID_ETRON_EJ168 || + pdev->device == PCI_DEVICE_ID_ETRON_EJ188)) { + xhci->quirks |= XHCI_ETRON_HOST; xhci->quirks |= XHCI_RESET_ON_RESUME; xhci->quirks |= XHCI_BROKEN_STREAMS; + xhci->quirks |= XHCI_NO_SOFT_RETRY; } if (pdev->vendor == PCI_VENDOR_ID_RENESAS && @@ -426,8 +420,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == 0x3432) xhci->quirks |= XHCI_BROKEN_STREAMS; - if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483) + if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == PCI_DEVICE_ID_VIA_VL805) { xhci->quirks |= XHCI_LPM_SUPPORT; + xhci->quirks |= XHCI_TRB_OVERFETCH; + } if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && pdev->device == PCI_DEVICE_ID_ASMEDIA_1042_XHCI) { @@ -475,15 +471,15 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->device == 0x9202) { xhci->quirks |= XHCI_RESET_ON_RESUME; - xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; + xhci->quirks |= XHCI_TRB_OVERFETCH; } if (pdev->device == 0x9203) - xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; + xhci->quirks |= XHCI_TRB_OVERFETCH; } - if (pdev->vendor == PCI_DEVICE_ID_CADENCE && - pdev->device == PCI_DEVICE_ID_CADENCE_SSP) + if (pdev->vendor == PCI_VENDOR_ID_CDNS && + pdev->device == PCI_DEVICE_ID_CDNS_USBSSP) xhci->quirks |= XHCI_CDNS_SCTX_QUIRK; /* xHC spec requires PCI devices to support D3hot and D3cold */ @@ -646,6 +642,9 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) dma_set_max_seg_size(&dev->dev, UINT_MAX); + if (device_property_read_bool(&dev->dev, "ti,pwron-active-high")) + pci_clear_and_set_config_dword(dev, 0xE0, 0, 1 << 22); + return 0; put_usb3_hcd: @@ -656,10 +655,10 @@ put_runtime_pm: pm_runtime_put_noidle(&dev->dev); return retval; } -EXPORT_SYMBOL_NS_GPL(xhci_pci_common_probe, xhci); +EXPORT_SYMBOL_NS_GPL(xhci_pci_common_probe, "xhci"); -static const struct pci_device_id pci_ids_reject[] = { - /* handled by xhci-pci-renesas */ +/* handled by xhci-pci-renesas if enabled */ +static const struct pci_device_id pci_ids_renesas[] = { { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0014) }, { PCI_DEVICE(PCI_VENDOR_ID_RENESAS, 0x0015) }, { /* end: all zeroes */ } @@ -667,7 +666,8 @@ static const struct pci_device_id pci_ids_reject[] = { static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { - if (pci_match_id(pci_ids_reject, dev)) + if (IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS) && + pci_match_id(pci_ids_renesas, dev)) return -ENODEV; return xhci_pci_common_probe(dev, id); @@ -700,7 +700,7 @@ void xhci_pci_remove(struct pci_dev *dev) if (set_power_d3) pci_set_power_state(dev, PCI_D3hot); } -EXPORT_SYMBOL_NS_GPL(xhci_pci_remove, xhci); +EXPORT_SYMBOL_NS_GPL(xhci_pci_remove, "xhci"); /* * In some Intel xHCI controllers, in order to get D3 working, @@ -807,8 +807,10 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) { - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + bool power_lost = msg.event == PM_EVENT_RESTORE; + bool is_auto_resume = msg.event == PM_EVENT_AUTO_RESUME; reset_control_reset(xhci->reset); @@ -839,7 +841,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) if (xhci->quirks & XHCI_PME_STUCK_QUIRK) xhci_pme_quirk(hcd); - return xhci_resume(xhci, msg); + return xhci_resume(xhci, power_lost, is_auto_resume); } static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index ecaa75718e59..3155e3a842da 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -106,7 +106,7 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada = { }; static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = { - .init_quirk = xhci_mvebu_a3700_init_quirk, + .quirks = XHCI_RESET_ON_RESUME, }; static const struct xhci_plat_priv xhci_plat_brcm = { @@ -290,7 +290,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node); - if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT)) + if ((priv && (priv->quirks & XHCI_SKIP_PHY_INIT)) || + (xhci->quirks & XHCI_SKIP_PHY_INIT)) hcd->skip_phy_initialization = 1; if (priv && (priv->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK)) @@ -329,6 +330,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s usb3_hcd->can_do_streams = 1; if (xhci->shared_hcd) { + xhci->shared_hcd->rsrc_start = hcd->rsrc_start; + xhci->shared_hcd->rsrc_len = hcd->rsrc_len; ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); if (ret) goto put_usb3_hcd; @@ -476,9 +479,10 @@ static int xhci_plat_suspend(struct device *dev) return 0; } -static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg) +static int xhci_plat_resume_common(struct device *dev, bool power_lost) { struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd); int ret; @@ -498,7 +502,7 @@ static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg) if (ret) goto disable_clks; - ret = xhci_resume(xhci, pmsg); + ret = xhci_resume(xhci, power_lost || priv->power_lost, false); if (ret) goto disable_clks; @@ -519,12 +523,12 @@ disable_clks: static int xhci_plat_resume(struct device *dev) { - return xhci_plat_resume_common(dev, PMSG_RESUME); + return xhci_plat_resume_common(dev, false); } static int xhci_plat_restore(struct device *dev) { - return xhci_plat_resume_common(dev, PMSG_RESTORE); + return xhci_plat_resume_common(dev, true); } static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev) @@ -545,7 +549,7 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - return xhci_resume(xhci, PMSG_AUTO_RESUME); + return xhci_resume(xhci, false, true); } const struct dev_pm_ops xhci_plat_pm_ops = { @@ -566,6 +570,7 @@ EXPORT_SYMBOL_GPL(xhci_plat_pm_ops); static const struct acpi_device_id usb_xhci_acpi_match[] = { /* XHCI-compliant USB Controller */ { "PNP0D10", }, + { "PNP0D15", }, { } }; MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); @@ -573,7 +578,7 @@ MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); static struct platform_driver usb_generic_xhci_driver = { .probe = xhci_generic_plat_probe, - .remove_new = xhci_plat_remove, + .remove = xhci_plat_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xhci-hcd", diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index 6475130eac4b..fe4f95e690fa 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -15,6 +15,7 @@ struct usb_hcd; struct xhci_plat_priv { const char *firmware_name; unsigned long long quirks; + bool power_lost; void (*plat_start)(struct usb_hcd *); int (*init_quirk)(struct usb_hcd *); int (*suspend_quirk)(struct usb_hcd *); diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 8b357647728c..1cc082a3b793 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -274,7 +274,7 @@ static int xhci_renesas_probe(struct platform_device *pdev) static struct platform_driver usb_xhci_renesas_driver = { .probe = xhci_renesas_probe, - .remove_new = xhci_plat_remove, + .remove = xhci_plat_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xhci-renesas-hcd", diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 928b93ad1ee8..5d64c297721c 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -52,8 +52,10 @@ * endpoint rings; it generates events on the event ring for these. */ +#include <linux/jiffies.h> #include <linux/scatterlist.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/dma-mapping.h> #include "xhci.h" #include "xhci-trace.h" @@ -145,10 +147,8 @@ static void trb_to_noop(union xhci_trb *trb, u32 noop_type) * TRB is in a new segment. This does not skip over link TRBs, and it does not * effect the ring dequeue or enqueue pointers. */ -static void next_trb(struct xhci_hcd *xhci, - struct xhci_ring *ring, - struct xhci_segment **seg, - union xhci_trb **trb) +static void next_trb(struct xhci_segment **seg, + union xhci_trb **trb) { if (trb_is_link(*trb) || last_trb_on_seg(*seg, *trb)) { *seg = (*seg)->next; @@ -169,13 +169,16 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) if (ring->type == TYPE_EVENT) { if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) { ring->dequeue++; - goto out; + return; } if (last_trb_on_ring(ring, ring->deq_seg, ring->dequeue)) ring->cycle_state ^= 1; ring->deq_seg = ring->deq_seg->next; ring->dequeue = ring->deq_seg->trbs; - goto out; + + trace_xhci_inc_deq(ring); + + return; } /* All other rings have link trbs */ @@ -190,18 +193,61 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) ring->deq_seg = ring->deq_seg->next; ring->dequeue = ring->deq_seg->trbs; + trace_xhci_inc_deq(ring); + if (link_trb_count++ > ring->num_segs) { xhci_warn(xhci, "Ring is an endless link TRB loop\n"); break; } } -out: - trace_xhci_inc_deq(ring); - return; } /* + * If enqueue points at a link TRB, follow links until an ordinary TRB is reached. + * Toggle the cycle bit of passed link TRBs and optionally chain them. + */ +static void inc_enq_past_link(struct xhci_hcd *xhci, struct xhci_ring *ring, u32 chain) +{ + unsigned int link_trb_count = 0; + + while (trb_is_link(ring->enqueue)) { + + /* + * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit + * set, but other sections talk about dealing with the chain bit set. This was + * fixed in the 0.96 specification errata, but we have to assume that all 0.95 + * xHCI hardware can't handle the chain bit being cleared on a link TRB. + * + * On 0.95 and some 0.96 HCs the chain bit is set once at segment initalization + * and never changed here. On all others, modify it as requested by the caller. + */ + if (!xhci_link_chain_quirk(xhci, ring->type)) { + ring->enqueue->link.control &= cpu_to_le32(~TRB_CHAIN); + ring->enqueue->link.control |= cpu_to_le32(chain); + } + + /* Give this link TRB to the hardware */ + wmb(); + ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE); + + /* Toggle the cycle bit after the last ring segment. */ + if (link_trb_toggles_cycle(ring->enqueue)) + ring->cycle_state ^= 1; + + ring->enq_seg = ring->enq_seg->next; + ring->enqueue = ring->enq_seg->trbs; + + trace_xhci_inc_enq(ring); + + if (link_trb_count++ > ring->num_segs) { + xhci_warn(xhci, "Link TRB loop at enqueue\n"); + break; + } + } +} + +/* * See Cycle bit rules. SW is the consumer for the event ring only. * * If we've just enqueued a TRB that is in the middle of a TD (meaning the @@ -209,11 +255,6 @@ out: * If we've enqueued the last TRB in a TD, make sure the following link TRBs * have their chain bit cleared (so that each Link TRB is a separate TD). * - * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit - * set, but other sections talk about dealing with the chain bit set. This was - * fixed in the 0.96 specification errata, but we have to assume that all 0.95 - * xHCI hardware can't handle the chain bit being cleared on a link TRB. - * * @more_trbs_coming: Will you enqueue more TRBs before calling * prepare_transfer()? */ @@ -221,8 +262,6 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool more_trbs_coming) { u32 chain; - union xhci_trb *next; - unsigned int link_trb_count = 0; chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN; @@ -231,48 +270,67 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, return; } - next = ++(ring->enqueue); - - /* Update the dequeue pointer further if that was a link TRB */ - while (trb_is_link(next)) { + ring->enqueue++; - /* - * If the caller doesn't plan on enqueueing more TDs before - * ringing the doorbell, then we don't want to give the link TRB - * to the hardware just yet. We'll give the link TRB back in - * prepare_ring() just before we enqueue the TD at the top of - * the ring. - */ - if (!chain && !more_trbs_coming) - break; + /* + * If we are in the middle of a TD or the caller plans to enqueue more + * TDs as one transfer (eg. control), traverse any link TRBs right now. + * Otherwise, enqueue can stay on a link until the next prepare_ring(). + * This avoids enqueue entering deq_seg and simplifies ring expansion. + */ + if (trb_is_link(ring->enqueue) && (chain || more_trbs_coming)) + inc_enq_past_link(xhci, ring, chain); +} - /* If we're not dealing with 0.95 hardware or isoc rings on - * AMD 0.96 host, carry over the chain bit of the previous TRB - * (which may mean the chain bit is cleared). - */ - if (!xhci_link_chain_quirk(xhci, ring->type)) { - next->link.control &= cpu_to_le32(~TRB_CHAIN); - next->link.control |= cpu_to_le32(chain); - } - /* Give this link TRB to the hardware */ - wmb(); - next->link.control ^= cpu_to_le32(TRB_CYCLE); +/* + * If the suspect DMA address is a TRB in this TD, this function returns that + * TRB's segment. Otherwise it returns 0. + */ +static struct xhci_segment *trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma) +{ + dma_addr_t start_dma; + dma_addr_t end_seg_dma; + dma_addr_t end_trb_dma; + struct xhci_segment *cur_seg; - /* Toggle the cycle bit after the last ring segment. */ - if (link_trb_toggles_cycle(next)) - ring->cycle_state ^= 1; + start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb); + cur_seg = td->start_seg; - ring->enq_seg = ring->enq_seg->next; - ring->enqueue = ring->enq_seg->trbs; - next = ring->enqueue; + do { + if (start_dma == 0) + return NULL; + /* We may get an event for a Link TRB in the middle of a TD */ + end_seg_dma = xhci_trb_virt_to_dma(cur_seg, + &cur_seg->trbs[TRBS_PER_SEGMENT - 1]); + /* If the end TRB isn't in this segment, this is set to 0 */ + end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb); - if (link_trb_count++ > ring->num_segs) { - xhci_warn(xhci, "%s: Ring link TRB loop\n", __func__); - break; + if (end_trb_dma > 0) { + /* The end TRB is in this segment, so suspect should be here */ + if (start_dma <= end_trb_dma) { + if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma) + return cur_seg; + } else { + /* Case for one segment with + * a TD wrapped around to the top + */ + if ((suspect_dma >= start_dma && + suspect_dma <= end_seg_dma) || + (suspect_dma >= cur_seg->dma && + suspect_dma <= end_trb_dma)) + return cur_seg; + } + return NULL; } - } + /* Might still be somewhere in this segment */ + if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma) + return cur_seg; - trace_xhci_inc_enq(ring); + cur_seg = cur_seg->next; + start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]); + } while (cur_seg != td->start_seg); + + return NULL; } /* @@ -421,12 +479,13 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, if ((xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue) && !(xhci->xhc_state & XHCI_STATE_DYING)) { xhci->current_cmd = cur_cmd; - xhci_mod_cmd_timer(xhci); + if (cur_cmd) + xhci_mod_cmd_timer(xhci); xhci_ring_cmd_db(xhci); } } -/* Must be called with xhci->lock held, releases and aquires lock back */ +/* Must be called with xhci->lock held, releases and acquires lock back */ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) { struct xhci_segment *new_seg = xhci->cmd_ring->deq_seg; @@ -446,9 +505,9 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) * avoiding corrupting the command ring pointer in case the command ring * is stopped by the time the upper dword is written. */ - next_trb(xhci, NULL, &new_seg, &new_deq); + next_trb(&new_seg, &new_deq); if (trb_is_link(new_deq)) - next_trb(xhci, NULL, &new_seg, &new_deq); + next_trb(&new_seg, &new_deq); crcr = xhci_trb_virt_to_dma(new_seg, new_deq); xhci_write_64(xhci, crcr | CMD_RING_ABORT, &xhci->op_regs->cmd_ring); @@ -502,8 +561,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, * pointer command pending because the device can choose to start any * stream once the endpoint is on the HW schedule. */ - if ((ep_state & EP_STOP_CMD_PENDING) || (ep_state & SET_DEQ_PENDING) || - (ep_state & EP_HALTED) || (ep_state & EP_CLEARING_TT)) + if (ep_state & (EP_STOP_CMD_PENDING | SET_DEQ_PENDING | EP_HALTED | + EP_CLEARING_TT | EP_STALLED)) return; trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id)); @@ -660,8 +719,8 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, /* * We want to find the pointer, segment and cycle state of the new trb - * (the one after current TD's last_trb). We know the cycle state at - * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are + * (the one after current TD's end_trb). We know the cycle state at + * hw_dequeue, so walk the ring until both hw_dequeue and end_trb are * found. */ do { @@ -671,14 +730,14 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, if (td_last_trb_found) break; } - if (new_deq == td->last_trb) + if (new_deq == td->end_trb) td_last_trb_found = true; if (cycle_found && trb_is_link(new_deq) && link_trb_toggles_cycle(new_deq)) new_cycle ^= 0x1; - next_trb(xhci, ep_ring, &new_seg, &new_deq); + next_trb(&new_seg, &new_deq); /* Search wrapped around, bail out */ if (new_deq == ep->ring->dequeue) { @@ -740,23 +799,22 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, * (The last TRB actually points to the ring enqueue pointer, which is not part * of this TD.) This is used to remove partially enqueued isoc TDs from a ring. */ -static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, - struct xhci_td *td, bool flip_cycle) +static void td_to_noop(struct xhci_td *td, bool flip_cycle) { struct xhci_segment *seg = td->start_seg; - union xhci_trb *trb = td->first_trb; + union xhci_trb *trb = td->start_trb; while (1) { trb_to_noop(trb, TRB_TR_NOOP); /* flip cycle if asked to */ - if (flip_cycle && trb != td->first_trb && trb != td->last_trb) + if (flip_cycle && trb != td->start_trb && trb != td->end_trb) trb->generic.field[3] ^= cpu_to_le32(TRB_CYCLE); - if (trb == td->last_trb) + if (trb == td->end_trb) break; - next_trb(xhci, ep_ring, &seg, &trb); + next_trb(&seg, &trb); } } @@ -799,7 +857,7 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, dma_unmap_single(dev, seg->bounce_dma, ring->bounce_buf_len, DMA_FROM_DEVICE); - /* for in tranfers we need to copy the data from bounce to sg */ + /* for in transfers we need to copy the data from bounce to sg */ if (urb->num_sgs) { len = sg_pcopy_from_buffer(urb->sg, urb->num_sgs, seg->bounce_buf, seg->bounce_len, seg->bounce_offs); @@ -814,8 +872,8 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci, seg->bounce_offs = 0; } -static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, - struct xhci_ring *ep_ring, int status) +static void xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, + struct xhci_ring *ep_ring, int status) { struct urb *urb = NULL; @@ -858,10 +916,18 @@ static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, status = 0; xhci_giveback_urb_in_irq(xhci, td, status); } - - return 0; } +/* Give back previous TD and move on to the next TD. */ +static void xhci_dequeue_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_ring *ring, + u32 status) +{ + ring->dequeue = td->end_trb; + ring->deq_seg = td->end_seg; + inc_deq(xhci, ring); + + xhci_td_cleanup(xhci, td, ring, status); +} /* Complete the cancelled URBs we unlinked from td_list. */ static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep) @@ -972,13 +1038,20 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) unsigned int slot_id = ep->vdev->slot_id; int err; + /* + * This is not going to work if the hardware is changing its dequeue + * pointers as we look at them. Completion handler will call us later. + */ + if (ep->ep_state & SET_DEQ_PENDING) + return 0; + xhci = ep->xhci; list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) { xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Removing canceled TD starting at 0x%llx (dma) in stream %u URB %p", (unsigned long long)xhci_trb_virt_to_dma( - td->start_seg, td->first_trb), + td->start_seg, td->start_trb), td->urb->stream_id, td->urb); list_del_init(&td->td_list); ring = xhci_urb_to_transfer_ring(xhci, td->urb); @@ -997,7 +1070,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) td->urb->stream_id); hw_deq &= ~0xf; - if (td->cancel_status == TD_HALTED || trb_in_td(xhci, td, hw_deq, false)) { + if (td->cancel_status == TD_HALTED || trb_in_td(td, hw_deq)) { switch (td->cancel_status) { case TD_CLEARED: /* TD is already no-op */ case TD_CLEARING_CACHE: /* set TR deq command already queued */ @@ -1020,16 +1093,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) "Found multiple active URBs %p and %p in stream %u?\n", td->urb, cached_td->urb, td->urb->stream_id); - td_to_noop(xhci, ring, cached_td, false); + td_to_noop(cached_td, false); cached_td->cancel_status = TD_CLEARED; } - td_to_noop(xhci, ring, td, false); + td_to_noop(td, false); td->cancel_status = TD_CLEARING_CACHE; cached_td = td; break; } } else { - td_to_noop(xhci, ring, td, false); + td_to_noop(td, false); td->cancel_status = TD_CLEARED; } } @@ -1054,7 +1127,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) continue; xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n", td->urb); - td_to_noop(xhci, ring, td, false); + td_to_noop(td, false); td->cancel_status = TD_CLEARED; } } @@ -1062,6 +1135,19 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) } /* + * Erase queued TDs from transfer ring(s) and give back those the xHC didn't + * stop on. If necessary, queue commands to move the xHC off cancelled TDs it + * stopped on. Those will be given back later when the commands complete. + * + * Call under xhci->lock on a stopped endpoint. + */ +void xhci_process_cancelled_tds(struct xhci_virt_ep *ep) +{ + xhci_invalidate_cancelled_tds(ep); + xhci_giveback_invalidated_tds(ep); +} + +/* * Returns the TD the endpoint ring halted on. * Only call for non-running rings without streams. */ @@ -1074,7 +1160,7 @@ static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep) hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0); hw_deq &= ~0xf; td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list); - if (trb_in_td(ep->xhci, td, hw_deq, false)) + if (trb_in_td(td, hw_deq)) return td; } return NULL; @@ -1134,7 +1220,14 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, */ switch (GET_EP_CTX_STATE(ep_ctx)) { case EP_STATE_HALTED: - xhci_dbg(xhci, "Stop ep completion raced with stall, reset ep\n"); + xhci_dbg(xhci, "Stop ep completion raced with stall\n"); + /* + * If the halt happened before Stop Endpoint failed, its transfer event + * should have already been handled and Reset Endpoint should be pending. + */ + if (ep->ep_state & EP_HALTED) + goto reset_done; + if (ep->ep_state & EP_HAS_STREAMS) { reset_type = EP_SOFT_RESET; } else { @@ -1145,22 +1238,45 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, } /* reset ep, reset handler cleans up cancelled tds */ err = xhci_handle_halted_endpoint(xhci, ep, td, reset_type); + xhci_dbg(xhci, "Stop ep completion resetting ep, status %d\n", err); if (err) break; +reset_done: + /* Reset EP handler will clean up cancelled TDs */ ep->ep_state &= ~EP_STOP_CMD_PENDING; return; case EP_STATE_STOPPED: /* - * NEC uPD720200 sometimes sets this state and fails with - * Context Error while continuing to process TRBs. - * Be conservative and trust EP_CTX_STATE on other chips. + * Per xHCI 4.6.9, Stop Endpoint command on a Stopped + * EP is a Context State Error, and EP stays Stopped. + * + * But maybe it failed on Halted, and somebody ran Reset + * Endpoint later. EP state is now Stopped and EP_HALTED + * still set because Reset EP handler will run after us. */ - if (!(xhci->quirks & XHCI_NEC_HOST)) + if (ep->ep_state & EP_HALTED) break; + /* + * On some HCs EP state remains Stopped for some tens of + * us to a few ms or more after a doorbell ring, and any + * new Stop Endpoint fails without aborting the restart. + * This handler may run quickly enough to still see this + * Stopped state, but it will soon change to Running. + * + * Assume this bug on unexpected Stop Endpoint failures. + * Keep retrying until the EP starts and stops again. + */ fallthrough; case EP_STATE_RUNNING: /* Race, HW handled stop ep cmd before ep was running */ - xhci_dbg(xhci, "Stop ep completion ctx error, ep is running\n"); + xhci_dbg(xhci, "Stop ep completion ctx error, ctx_state %d\n", + GET_EP_CTX_STATE(ep_ctx)); + /* + * Don't retry forever if we guessed wrong or a defective HC never starts + * the EP or says 'Running' but fails the command. We must give back TDs. + */ + if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100))) + break; command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) { @@ -1285,43 +1401,6 @@ void xhci_hc_died(struct xhci_hcd *xhci) usb_hc_died(xhci_to_hcd(xhci)); } -static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, - struct xhci_virt_device *dev, - struct xhci_ring *ep_ring, - unsigned int ep_index) -{ - union xhci_trb *dequeue_temp; - - dequeue_temp = ep_ring->dequeue; - - /* If we get two back-to-back stalls, and the first stalled transfer - * ends just before a link TRB, the dequeue pointer will be left on - * the link TRB by the code in the while loop. So we have to update - * the dequeue pointer one segment further, or we'll jump off - * the segment into la-la-land. - */ - if (trb_is_link(ep_ring->dequeue)) { - ep_ring->deq_seg = ep_ring->deq_seg->next; - ep_ring->dequeue = ep_ring->deq_seg->trbs; - } - - while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) { - /* We have more usable TRBs */ - ep_ring->dequeue++; - if (trb_is_link(ep_ring->dequeue)) { - if (ep_ring->dequeue == - dev->eps[ep_index].queued_deq_ptr) - break; - ep_ring->deq_seg = ep_ring->deq_seg->next; - ep_ring->dequeue = ep_ring->deq_seg->trbs; - } - if (ep_ring->dequeue == dequeue_temp) { - xhci_dbg(xhci, "Unable to find new dequeue pointer\n"); - break; - } - } -} - /* * When we get a completion for a Set Transfer Ring Dequeue Pointer command, * we need to clear the set deq pending flag in the endpoint ring state, so that @@ -1338,8 +1417,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, struct xhci_virt_ep *ep; struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx; + struct xhci_stream_ctx *stream_ctx; struct xhci_td *td, *tmp_td; - bool deferred = false; ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2])); @@ -1360,6 +1439,11 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, trace_xhci_handle_cmd_set_deq(slot_ctx); trace_xhci_handle_cmd_set_deq_ep(ep_ctx); + if (ep->ep_state & EP_HAS_STREAMS) { + stream_ctx = &ep->stream_info->stream_ctx_array[stream_id]; + trace_xhci_handle_cmd_set_deq_stream(ep->stream_info, stream_id); + } + if (cmd_comp_code != COMP_SUCCESS) { unsigned int ep_state; unsigned int slot_state; @@ -1396,9 +1480,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, u64 deq; /* 4.6.10 deq ptr is written to the stream ctx for streams */ if (ep->ep_state & EP_HAS_STREAMS) { - struct xhci_stream_ctx *ctx = - &ep->stream_info->stream_ctx_array[stream_id]; - deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK; + deq = le64_to_cpu(stream_ctx->stream_ring) & SCTX_DEQ_MASK; /* * Cadence xHCI controllers store some endpoint state @@ -1410,8 +1492,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, * To fix this issue driver must clear Rsvd0 field. */ if (xhci->quirks & XHCI_CDNS_SCTX_QUIRK) { - ctx->reserved[0] = 0; - ctx->reserved[1] = 0; + stream_ctx->reserved[0] = 0; + stream_ctx->reserved[1] = 0; } } else { deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK; @@ -1423,8 +1505,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, /* Update the ring's dequeue segment and dequeue pointer * to reflect the new position. */ - update_ring_for_set_deq_completion(xhci, ep->vdev, - ep_ring, ep_index); + ep_ring->deq_seg = ep->queued_deq_seg; + ep_ring->dequeue = ep->queued_deq_ptr; } else { xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n"); xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n", @@ -1440,8 +1522,6 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n", __func__, td->urb); xhci_td_cleanup(ep->xhci, td, ep_ring, td->status); - } else if (td->cancel_status == TD_CLEARING_CACHE_DEFERRED) { - deferred = true; } else { xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n", __func__, td->urb, td->cancel_status); @@ -1452,11 +1532,15 @@ cleanup: ep->queued_deq_seg = NULL; ep->queued_deq_ptr = NULL; - if (deferred) { - /* We have more streams to clear */ + /* Check for deferred or newly cancelled TDs */ + if (!list_empty(&ep->cancelled_td_list)) { xhci_dbg(ep->xhci, "%s: Pending TDs to clear, continuing with invalidation\n", __func__); xhci_invalidate_cancelled_tds(ep); + /* Try to restart the endpoint if all is done */ + ring_doorbell_for_active_rings(xhci, slot_id, ep_index); + /* Start giving back any TDs invalidated above */ + xhci_giveback_invalidated_tds(ep); } else { /* Restart any rings with pending URBs */ xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__); @@ -1599,12 +1683,13 @@ static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci, NEC_FW_MINOR(le32_to_cpu(event->status))); } -static void xhci_complete_del_and_free_cmd(struct xhci_command *cmd, u32 status) +static void xhci_complete_del_and_free_cmd(struct xhci_command *cmd, u32 comp_code, u32 comp_param) { list_del(&cmd->cmd_list); if (cmd->completion) { - cmd->status = status; + cmd->status = comp_code; + cmd->comp_param = comp_param; complete(cmd->completion); } else { kfree(cmd); @@ -1616,7 +1701,7 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci) struct xhci_command *cur_cmd, *tmp_cmd; xhci->current_cmd = NULL; list_for_each_entry_safe(cur_cmd, tmp_cmd, &xhci->cmd_list, cmd_list) - xhci_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED); + xhci_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED, 0); } void xhci_handle_command_timeout(struct work_struct *work) @@ -1701,6 +1786,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_event_cmd *event) { unsigned int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); + u32 status = le32_to_cpu(event->status); u64 cmd_dma; dma_addr_t cmd_dequeue_dma; u32 cmd_comp_code; @@ -1716,7 +1802,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, cmd_dma = le64_to_cpu(event->cmd_trb); cmd_trb = xhci->cmd_ring->dequeue; - trace_xhci_handle_command(xhci->cmd_ring, &cmd_trb->generic); + trace_xhci_handle_command(xhci->cmd_ring, &cmd_trb->generic, cmd_dma); cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status)); @@ -1829,7 +1915,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, } event_handled: - xhci_complete_del_and_free_cmd(cmd, cmd_comp_code); + xhci_complete_del_and_free_cmd(cmd, cmd_comp_code, COMP_PARAM(status)); inc_deq(xhci, xhci->cmd_ring); } @@ -2062,67 +2148,6 @@ cleanup: spin_lock(&xhci->lock); } -/* - * If the suspect DMA address is a TRB in this TD, this function returns that - * TRB's segment. Otherwise it returns 0. - */ -struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_addr_t suspect_dma, - bool debug) -{ - dma_addr_t start_dma; - dma_addr_t end_seg_dma; - dma_addr_t end_trb_dma; - struct xhci_segment *cur_seg; - - start_dma = xhci_trb_virt_to_dma(td->start_seg, td->first_trb); - cur_seg = td->start_seg; - - do { - if (start_dma == 0) - return NULL; - /* We may get an event for a Link TRB in the middle of a TD */ - end_seg_dma = xhci_trb_virt_to_dma(cur_seg, - &cur_seg->trbs[TRBS_PER_SEGMENT - 1]); - /* If the end TRB isn't in this segment, this is set to 0 */ - end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->last_trb); - - if (debug) - xhci_warn(xhci, - "Looking for event-dma %016llx trb-start %016llx trb-end %016llx seg-start %016llx seg-end %016llx\n", - (unsigned long long)suspect_dma, - (unsigned long long)start_dma, - (unsigned long long)end_trb_dma, - (unsigned long long)cur_seg->dma, - (unsigned long long)end_seg_dma); - - if (end_trb_dma > 0) { - /* The end TRB is in this segment, so suspect should be here */ - if (start_dma <= end_trb_dma) { - if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma) - return cur_seg; - } else { - /* Case for one segment with - * a TD wrapped around to the top - */ - if ((suspect_dma >= start_dma && - suspect_dma <= end_seg_dma) || - (suspect_dma >= cur_seg->dma && - suspect_dma <= end_trb_dma)) - return cur_seg; - } - return NULL; - } else { - /* Might still be somewhere in this segment */ - if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma) - return cur_seg; - } - cur_seg = cur_seg->next; - start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]); - } while (cur_seg != td->start_seg); - - return NULL; -} - static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_virt_ep *ep) { @@ -2184,9 +2209,9 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code) return 0; } -static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - u32 trb_comp_code) +static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + u32 trb_comp_code) { struct xhci_ep_ctx *ep_ctx; @@ -2201,7 +2226,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, * stopped TDs. A stopped TD may be restarted, so don't update * the ring dequeue pointer or take this TD off any lists yet. */ - return 0; + return; case COMP_USB_TRANSACTION_ERROR: case COMP_BABBLE_DETECTED_ERROR: case COMP_SPLIT_TRANSACTION_ERROR: @@ -2226,8 +2251,8 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, !list_empty(&td->cancelled_td_list)) { xhci_dbg(xhci, "Already resolving halted ep for 0x%llx\n", (unsigned long long)xhci_trb_virt_to_dma( - td->start_seg, td->first_trb)); - return 0; + td->start_seg, td->start_trb)); + return; } /* endpoint not halted, don't reset it */ break; @@ -2235,7 +2260,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, /* Almost same procedure as for STALL_ERROR below */ xhci_clear_hub_tt_buffer(xhci, td, ep); xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); - return 0; + return; case COMP_STALL_ERROR: /* * xhci internal endpoint state will go to a "halt" state for @@ -2252,28 +2277,22 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); - return 0; /* xhci_handle_halted_endpoint marked td cancelled */ + return; /* xhci_handle_halted_endpoint marked td cancelled */ default: break; } - /* Update ring dequeue pointer */ - ep_ring->dequeue = td->last_trb; - ep_ring->deq_seg = td->last_trb_seg; - inc_deq(xhci, ep_ring); - - return xhci_td_cleanup(xhci, td, ep_ring, td->status); + xhci_dequeue_td(xhci, td, ep_ring, td->status); } -/* sum trb lengths from ring dequeue up to stop_trb, _excluding_ stop_trb */ -static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring, - union xhci_trb *stop_trb) +/* sum trb lengths from the first trb up to stop_trb, _excluding_ stop_trb */ +static u32 sum_trb_lengths(struct xhci_td *td, union xhci_trb *stop_trb) { u32 sum; - union xhci_trb *trb = ring->dequeue; - struct xhci_segment *seg = ring->deq_seg; + union xhci_trb *trb = td->start_trb; + struct xhci_segment *seg = td->start_seg; - for (sum = 0; trb != stop_trb; next_trb(xhci, ring, &seg, &trb)) { + for (sum = 0; trb != stop_trb; next_trb(&seg, &trb)) { if (!trb_is_noop(trb) && !trb_is_link(trb)) sum += TRB_LEN(le32_to_cpu(trb->generic.field[2])); } @@ -2283,9 +2302,9 @@ static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring, /* * Process control tds, update urb status and actual_length. */ -static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) +static void process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct xhci_ep_ctx *ep_ctx; u32 trb_comp_code; @@ -2364,7 +2383,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->urb_length_set = true; td->urb->actual_length = requested - remaining; xhci_dbg(xhci, "Waiting for status stage event\n"); - return 0; + return; } /* at status stage */ @@ -2372,15 +2391,15 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->urb->actual_length = requested; finish_td: - return finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code); } /* * Process isochronous tds, update urb packet status and actual_length. */ -static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) +static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct urb_priv *urb_priv; int idx; @@ -2425,7 +2444,13 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, fallthrough; case COMP_ISOCH_BUFFER_OVERRUN: frame->status = -EOVERFLOW; - if (ep_trb != td->last_trb) + if (ep_trb != td->end_trb) + td->error_mid_td = true; + break; + case COMP_MISSED_SERVICE_ERROR: + frame->status = -EXDEV; + sum_trbs_for_length = true; + if (ep_trb != td->end_trb) td->error_mid_td = true; break; case COMP_INCOMPATIBLE_DEVICE_ERROR: @@ -2435,14 +2460,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, case COMP_USB_TRANSACTION_ERROR: frame->status = -EPROTO; sum_trbs_for_length = true; - if (ep_trb != td->last_trb) + if (ep_trb != td->end_trb) td->error_mid_td = true; break; case COMP_STOPPED: sum_trbs_for_length = true; break; case COMP_STOPPED_SHORT_PACKET: - /* field normally containing residue now contains tranferred */ + /* field normally containing residue now contains transferred */ frame->status = short_framestatus; requested = remaining; break; @@ -2462,7 +2487,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, goto finish_td; if (sum_trbs_for_length) - frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) + + frame->actual_length = sum_trb_lengths(td, ep_trb) + ep_trb_len - remaining; else frame->actual_length = requested; @@ -2471,17 +2496,16 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, finish_td: /* Don't give back TD yet if we encountered an error mid TD */ - if (td->error_mid_td && ep_trb != td->last_trb) { + if (td->error_mid_td && ep_trb != td->end_trb) { xhci_dbg(xhci, "Error mid isoc TD, wait for final completion event\n"); td->urb_length_set = true; - return 0; + return; } - - return finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code); } -static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, - struct xhci_virt_ep *ep, int status) +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; @@ -2497,20 +2521,15 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, /* calc actual length */ frame->actual_length = 0; - /* Update ring dequeue pointer */ - ep->ring->dequeue = td->last_trb; - ep->ring->deq_seg = td->last_trb_seg; - inc_deq(xhci, ep->ring); - - return xhci_td_cleanup(xhci, td, ep->ring, status); + xhci_dequeue_td(xhci, td, ep->ring, status); } /* * Process bulk and interrupt tds, update urb status and actual_length. */ -static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) +static void process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_ring *ep_ring, struct xhci_td *td, + union xhci_trb *ep_trb, struct xhci_transfer_event *event) { struct xhci_slot_ctx *slot_ctx; u32 trb_comp_code; @@ -2526,7 +2545,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, case COMP_SUCCESS: ep->err_count = 0; /* handle success with untransferred data as short packet */ - if (ep_trb != td->last_trb || remaining) { + if (ep_trb != td->end_trb || remaining) { xhci_warn(xhci, "WARN Successful completion on short TX\n"); xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n", td->urb->ep->desc.bEndpointAddress, @@ -2542,7 +2561,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, goto finish_td; case COMP_STOPPED_LENGTH_INVALID: /* stopped on ep trb with invalid length, exclude it */ - td->urb->actual_length = sum_trb_lengths(xhci, ep_ring, ep_trb); + td->urb->actual_length = sum_trb_lengths(td, ep_trb); goto finish_td; case COMP_USB_TRANSACTION_ERROR: if (xhci->quirks & XHCI_NO_SOFT_RETRY || @@ -2553,17 +2572,20 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->status = 0; xhci_handle_halted_endpoint(xhci, ep, td, EP_SOFT_RESET); - return 0; + return; + case COMP_STALL_ERROR: + ep->ep_state |= EP_STALLED; + break; default: /* do nothing */ break; } - if (ep_trb == td->last_trb) + if (ep_trb == td->end_trb) td->urb->actual_length = requested - remaining; else td->urb->actual_length = - sum_trb_lengths(xhci, ep_ring, ep_trb) + + sum_trb_lengths(td, ep_trb) + ep_trb_len - remaining; finish_td: if (remaining > requested) { @@ -2572,7 +2594,7 @@ finish_td: td->urb->actual_length = 0; } - return finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code); } /* Transfer events which don't point to a transfer TRB, see xhci 4.17.4 */ @@ -2602,6 +2624,22 @@ static int handle_transferless_tx_event(struct xhci_hcd *xhci, struct xhci_virt_ return 0; } +static bool xhci_spurious_success_tx_event(struct xhci_hcd *xhci, + struct xhci_ring *ring) +{ + switch (ring->old_trb_comp_code) { + case COMP_SHORT_PACKET: + return xhci->quirks & XHCI_SPURIOUS_SUCCESS; + case COMP_USB_TRANSACTION_ERROR: + case COMP_BABBLE_DETECTED_ERROR: + case COMP_ISOCH_BUFFER_OVERRUN: + return xhci->quirks & XHCI_ETRON_HOST && + ring->type == TYPE_ISOC; + default: + return false; + } +} + /* * If this function returns an error condition, it means it got a Transfer * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. @@ -2622,6 +2660,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, int status = -EINPROGRESS; struct xhci_ep_ctx *ep_ctx; u32 trb_comp_code; + bool ring_xrun_event = false; slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; @@ -2655,8 +2694,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, case COMP_SUCCESS: if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) { trb_comp_code = COMP_SHORT_PACKET; - xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u with last td short %d\n", - slot_id, ep_index, ep_ring->last_td_was_short); + xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u with last td comp code %d\n", + slot_id, ep_index, ep_ring->old_trb_comp_code); } break; case COMP_SHORT_PACKET: @@ -2728,14 +2767,12 @@ static int handle_tx_event(struct xhci_hcd *xhci, * Underrun Event for OUT Isoch endpoint. */ xhci_dbg(xhci, "Underrun event on slot %u ep %u\n", slot_id, ep_index); - if (ep->skip) - break; - return 0; + ring_xrun_event = true; + break; case COMP_RING_OVERRUN: xhci_dbg(xhci, "Overrun event on slot %u ep %u\n", slot_id, ep_index); - if (ep->skip) - break; - return 0; + ring_xrun_event = true; + break; case COMP_MISSED_SERVICE_ERROR: /* * When encounter missed service error, one or more isoc tds @@ -2745,9 +2782,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, */ ep->skip = true; xhci_dbg(xhci, - "Miss service interval error for slot %u ep %u, set skip flag\n", - slot_id, ep_index); - return 0; + "Miss service interval error for slot %u ep %u, set skip flag%s\n", + slot_id, ep_index, ep_trb_dma ? ", skip now" : ""); + break; case COMP_NO_PING_RESPONSE_ERROR: ep->skip = true; xhci_dbg(xhci, @@ -2790,14 +2827,15 @@ static int handle_tx_event(struct xhci_hcd *xhci, */ td = list_first_entry_or_null(&ep_ring->td_list, struct xhci_td, td_list); - if (td && td->error_mid_td && !trb_in_td(xhci, td, ep_trb_dma, false)) { + if (td && td->error_mid_td && !trb_in_td(td, ep_trb_dma)) { xhci_dbg(xhci, "Missing TD completion event after mid TD error\n"); - ep_ring->dequeue = td->last_trb; - ep_ring->deq_seg = td->last_trb_seg; - inc_deq(xhci, ep_ring); - xhci_td_cleanup(xhci, td, ep_ring, td->status); + xhci_dequeue_td(xhci, td, ep_ring, td->status); } + /* If the TRB pointer is NULL, missed TDs will be skipped on the next event */ + if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !ep_trb_dma) + return 0; + if (list_empty(&ep_ring->td_list)) { /* * Don't print wanings if ring is empty due to a stopped endpoint generating an @@ -2807,7 +2845,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, */ if (trb_comp_code != COMP_STOPPED && trb_comp_code != COMP_STOPPED_LENGTH_INVALID && - !ep_ring->last_td_was_short) { + !ring_xrun_event && + !xhci_spurious_success_tx_event(xhci, ep_ring)) { xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n", slot_id, ep_index); } @@ -2821,14 +2860,31 @@ static int handle_tx_event(struct xhci_hcd *xhci, td_list); /* Is this a TRB in the currently executing TD? */ - ep_seg = trb_in_td(xhci, td, ep_trb_dma, false); + ep_seg = trb_in_td(td, ep_trb_dma); if (!ep_seg) { if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { + /* this event is unlikely to match any TD, don't skip them all */ + if (trb_comp_code == COMP_STOPPED_LENGTH_INVALID) + return 0; + skip_isoc_td(xhci, td, ep, status); - if (!list_empty(&ep_ring->td_list)) + + if (!list_empty(&ep_ring->td_list)) { + if (ring_xrun_event) { + /* + * If we are here, we are on xHCI 1.0 host with no + * idea how many TDs were missed or where the xrun + * occurred. New TDs may have been added after the + * xrun, so skip only one TD to be safe. + */ + xhci_dbg(xhci, "Skipped one TD for slot %u ep %u", + slot_id, ep_index); + return 0; + } continue; + } xhci_dbg(xhci, "All TDs skipped for slot %u ep %u. Clear skip flag.\n", slot_id, ep_index); @@ -2837,6 +2893,10 @@ static int handle_tx_event(struct xhci_hcd *xhci, goto check_endpoint_halted; } + /* TD was queued after xrun, maybe xrun was on a link, don't panic yet */ + if (ring_xrun_event) + return 0; + /* * Skip the Force Stopped Event. The 'ep_trb' of FSE is not in the current * TD pointed by 'ep_ring->dequeue' because that the hardware dequeue @@ -2851,21 +2911,17 @@ static int handle_tx_event(struct xhci_hcd *xhci, /* * Some hosts give a spurious success event after a short - * transfer. Ignore it. + * transfer or error on last TRB. Ignore it. */ - if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) && - ep_ring->last_td_was_short) { - ep_ring->last_td_was_short = false; + if (xhci_spurious_success_tx_event(xhci, ep_ring)) { + xhci_dbg(xhci, "Spurious event dma %pad, comp_code %u after %u\n", + &ep_trb_dma, trb_comp_code, ep_ring->old_trb_comp_code); + ep_ring->old_trb_comp_code = trb_comp_code; return 0; } /* HC is busted, give up! */ - xhci_err(xhci, - "ERROR Transfer event TRB DMA ptr not part of current TD ep_index %d comp_code %u\n", - ep_index, trb_comp_code); - trb_in_td(xhci, td, ep_trb_dma, true); - - return -ESHUTDOWN; + goto debug_finding_td; } if (ep->skip) { @@ -2883,13 +2939,14 @@ static int handle_tx_event(struct xhci_hcd *xhci, */ } while (ep->skip); - if (trb_comp_code == COMP_SHORT_PACKET) - ep_ring->last_td_was_short = true; - else - ep_ring->last_td_was_short = false; + ep_ring->old_trb_comp_code = trb_comp_code; + + /* Get out if a TD was queued at enqueue after the xrun occurred */ + if (ring_xrun_event) + return 0; ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)]; - trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb); + trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma); /* * No-op TRB could trigger interrupts in a case where a URB was killed @@ -2918,6 +2975,17 @@ check_endpoint_halted: return 0; +debug_finding_td: + xhci_err(xhci, "Event dma %pad for ep %d status %d not part of TD at %016llx - %016llx\n", + &ep_trb_dma, ep_index, trb_comp_code, + (unsigned long long)xhci_trb_virt_to_dma(td->start_seg, td->start_trb), + (unsigned long long)xhci_trb_virt_to_dma(td->end_seg, td->end_trb)); + + xhci_for_each_ring_seg(ep_ring->first_seg, ep_seg) + xhci_warn(xhci, "Ring seg %u dma %pad\n", ep_seg->num, &ep_seg->dma); + + return -ESHUTDOWN; + err_out: xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n", (unsigned long long) xhci_trb_virt_to_dma( @@ -2939,7 +3007,9 @@ static int xhci_handle_event_trb(struct xhci_hcd *xhci, struct xhci_interrupter { u32 trb_type; - trace_xhci_handle_event(ir->event_ring, &event->generic); + trace_xhci_handle_event(ir->event_ring, &event->generic, + xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue)); /* * Barrier between reading the TRB_CYCLE (valid) flag before, and any @@ -3162,7 +3232,8 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, wmb(); trb->field[3] = cpu_to_le32(field4); - trace_xhci_queue_trb(ring, trb); + trace_xhci_queue_trb(ring, trb, + xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue)); inc_enq(xhci, ring, more_trbs_coming); } @@ -3174,7 +3245,6 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, u32 ep_state, unsigned int num_trbs, gfp_t mem_flags) { - unsigned int link_trb_count = 0; unsigned int new_segs = 0; /* Make sure the endpoint has been added to xHC schedule */ @@ -3222,33 +3292,9 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, } } - while (trb_is_link(ep_ring->enqueue)) { - /* If we're not dealing with 0.95 hardware or isoc rings - * on AMD 0.96 host, clear the chain bit. - */ - if (!xhci_link_chain_quirk(xhci, ep_ring->type)) - ep_ring->enqueue->link.control &= - cpu_to_le32(~TRB_CHAIN); - else - ep_ring->enqueue->link.control |= - cpu_to_le32(TRB_CHAIN); - - wmb(); - ep_ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE); - - /* Toggle the cycle bit after the last ring segment. */ - if (link_trb_toggles_cycle(ep_ring->enqueue)) - ep_ring->cycle_state ^= 1; - - ep_ring->enq_seg = ep_ring->enq_seg->next; - ep_ring->enqueue = ep_ring->enq_seg->trbs; - - /* prevent infinite loop if all first trbs are link trbs */ - if (link_trb_count++ > ep_ring->num_segs) { - xhci_warn(xhci, "Ring is an endless link TRB loop\n"); - return -EINVAL; - } - } + /* Ensure that new TRBs won't overwrite a link */ + if (trb_is_link(ep_ring->enqueue)) + inc_enq_past_link(xhci, ep_ring, 0); if (last_trb_on_seg(ep_ring->enq_seg, ep_ring->enqueue)) { xhci_warn(xhci, "Missing link TRB at end of ring segment\n"); @@ -3302,7 +3348,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, /* Add this TD to the tail of the endpoint ring's TD list */ list_add_tail(&td->td_list, &ep_ring->td_list); td->start_seg = ep_ring->enq_seg; - td->first_trb = ep_ring->enqueue; + td->start_trb = ep_ring->enqueue; return 0; } @@ -3400,8 +3446,8 @@ static void check_interval(struct urb *urb, struct xhci_ep_ctx *ep_ctx) if (xhci_interval != ep_interval) { dev_dbg_ratelimited(&urb->dev->dev, "Driver uses different interval (%d microframe%s) than xHCI (%d microframe%s)\n", - ep_interval, ep_interval == 1 ? "" : "s", - xhci_interval, xhci_interval == 1 ? "" : "s"); + ep_interval, str_plural(ep_interval), + xhci_interval, str_plural(xhci_interval)); urb->interval = xhci_interval; /* Convert back to frames for LS/FS devices */ if (urb->dev->speed == USB_SPEED_LOW || @@ -3641,8 +3687,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field &= ~TRB_CHAIN; field |= TRB_IOC; more_trbs_coming = false; - td->last_trb = ring->enqueue; - td->last_trb_seg = ring->enq_seg; + td->end_trb = ring->enqueue; + td->end_seg = ring->enq_seg; if (xhci_urb_suitable_for_idt(urb)) { memcpy(&send_addr, urb->transfer_buffer, trb_buff_len); @@ -3690,8 +3736,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, 1, urb, 1, mem_flags); - urb_priv->td[1].last_trb = ring->enqueue; - urb_priv->td[1].last_trb_seg = ring->enq_seg; + urb_priv->td[1].end_trb = ring->enqueue; + urb_priv->td[1].end_seg = ring->enq_seg; field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC; queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field); } @@ -3727,6 +3773,20 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (!urb->setup_packet) return -EINVAL; + if ((xhci->quirks & XHCI_ETRON_HOST) && + urb->dev->speed >= USB_SPEED_SUPER) { + /* + * If next available TRB is the Link TRB in the ring segment then + * enqueue a No Op TRB, this can prevent the Setup and Data Stage + * TRB to be breaked by the Link TRB. + */ + if (trb_is_link(ep_ring->enqueue + 1)) { + field = TRB_TYPE(TRB_TR_NOOP) | ep_ring->cycle_state; + queue_trb(xhci, ep_ring, false, 0, 0, + TRB_INTR_TARGET(0), field); + } + } + /* 1 TRB for setup, 1 for status */ num_trbs = 2; /* @@ -3815,8 +3875,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } /* Save the DMA address of the last TRB in the TD */ - td->last_trb = ep_ring->enqueue; - td->last_trb_seg = ep_ring->enq_seg; + td->end_trb = ep_ring->enqueue; + td->end_seg = ep_ring->enq_seg; /* Queue status TRB - see Table 7 and sections 4.11.2.2 and 6.4.1.2.3 */ /* If the device sent data, the status stage is an OUT transfer */ @@ -4101,8 +4161,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field |= TRB_CHAIN; } else { more_trbs_coming = false; - td->last_trb = ep_ring->enqueue; - td->last_trb_seg = ep_ring->enq_seg; + td->end_trb = ep_ring->enqueue; + td->end_seg = ep_ring->enq_seg; field |= TRB_IOC; if (trb_block_event_intr(xhci, num_tds, i, ir)) field |= TRB_BEI; @@ -4168,14 +4228,14 @@ cleanup: /* Use the first TD as a temporary variable to turn the TDs we've queued * into No-ops with a software-owned cycle bit. That way the hardware * won't accidentally start executing bogus TDs when we partially - * overwrite them. td->first_trb and td->start_seg are already set. + * overwrite them. td->start_trb and td->start_seg are already set. */ - urb_priv->td[0].last_trb = ep_ring->enqueue; + urb_priv->td[0].end_trb = ep_ring->enqueue; /* Every TRB except the first & last will have its cycle bit flipped. */ - td_to_noop(xhci, ep_ring, &urb_priv->td[0], true); + td_to_noop(&urb_priv->td[0], true); /* Reset the ring enqueue back to the first TRB and its cycle bit. */ - ep_ring->enqueue = urb_priv->td[0].first_trb; + ep_ring->enqueue = urb_priv->td[0].start_trb; ep_ring->enq_seg = urb_priv->td[0].start_seg; ep_ring->cycle_state = start_cycle; usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb); diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 76f228e7443c..b5c362c2051d 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -26,6 +26,7 @@ #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/usb/otg.h> #include <linux/usb/phy.h> #include <linux/usb/role.h> @@ -724,7 +725,7 @@ static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra, if (err < 0) { dev_err(dev, "failed to %s LFPS detection on USB3#%u: %d\n", - enable ? "enable" : "disable", port, err); + str_enable_disable(enable), port, err); rsp.cmd = MBOX_CMD_NAK; } else { rsp.cmd = MBOX_CMD_ACK; @@ -1349,7 +1350,7 @@ static void tegra_xhci_id_work(struct work_struct *work) u32 status; int ret; - dev_dbg(tegra->dev, "host mode %s\n", tegra->host_mode ? "on" : "off"); + dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode)); mutex_lock(&tegra->lock); @@ -1667,7 +1668,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto put_padctl; } - if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) { + if (!of_property_present(pdev->dev.of_node, "power-domains")) { tegra->host_rst = devm_reset_control_get(&pdev->dev, "xusb_host"); if (IS_ERR(tegra->host_rst)) { @@ -2161,11 +2162,11 @@ static void tegra_xhci_program_utmi_power_lp0_exit(struct tegra_xusb *tegra) } } -static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime) +static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool is_auto_resume) { struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); struct device *dev = tegra->dev; - bool wakeup = runtime ? true : device_may_wakeup(dev); + bool wakeup = is_auto_resume ? true : device_may_wakeup(dev); unsigned int i; int err; u32 usbcmd; @@ -2231,11 +2232,11 @@ out: return err; } -static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime) +static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool is_auto_resume) { struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); struct device *dev = tegra->dev; - bool wakeup = runtime ? true : device_may_wakeup(dev); + bool wakeup = is_auto_resume ? true : device_may_wakeup(dev); unsigned int i; u32 usbcmd; int err; @@ -2286,7 +2287,7 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime) if (wakeup) tegra_xhci_disable_phy_sleepwalk(tegra); - err = xhci_resume(xhci, runtime ? PMSG_AUTO_RESUME : PMSG_RESUME); + err = xhci_resume(xhci, false, is_auto_resume); if (err < 0) { dev_err(tegra->dev, "failed to resume XHCI: %d\n", err); goto disable_phy; @@ -2664,7 +2665,7 @@ MODULE_DEVICE_TABLE(of, tegra_xusb_of_match); static struct platform_driver tegra_xusb_driver = { .probe = tegra_xusb_probe, - .remove_new = tegra_xusb_remove, + .remove = tegra_xusb_remove, .shutdown = tegra_xusb_shutdown, .driver = { .name = "tegra-xusb", diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 24405315ffe6..bfb5c5c17012 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -108,9 +108,10 @@ DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx, ); DECLARE_EVENT_CLASS(xhci_log_trb, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb), + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma), TP_STRUCT__entry( + __field(dma_addr_t, dma) __field(u32, type) __field(u32, field0) __field(u32, field1) @@ -118,51 +119,54 @@ DECLARE_EVENT_CLASS(xhci_log_trb, __field(u32, field3) ), TP_fast_assign( + __entry->dma = dma; __entry->type = ring->type; __entry->field0 = le32_to_cpu(trb->field[0]); __entry->field1 = le32_to_cpu(trb->field[1]); __entry->field2 = le32_to_cpu(trb->field[2]); __entry->field3 = le32_to_cpu(trb->field[3]); ), - TP_printk("%s: %s", xhci_ring_type_string(__entry->type), + TP_printk("%s: @%pad %s", + xhci_ring_type_string(__entry->type), &__entry->dma, xhci_decode_trb(__get_buf(XHCI_MSG_MAX), XHCI_MSG_MAX, __entry->field0, __entry->field1, __entry->field2, __entry->field3) ) ); DEFINE_EVENT(xhci_log_trb, xhci_handle_event, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_handle_command, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_handle_transfer, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_queue_trb, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) + ); DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_event, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_transfer, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DEFINE_EVENT(xhci_log_trb, xhci_dbc_gadget_ep_queue, - TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), - TP_ARGS(ring, trb) + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb, dma_addr_t dma), + TP_ARGS(ring, trb, dma) ); DECLARE_EVENT_CLASS(xhci_log_free_virt_dev, @@ -310,6 +314,37 @@ DEFINE_EVENT(xhci_log_urb, xhci_urb_dequeue, TP_ARGS(urb) ); +DECLARE_EVENT_CLASS(xhci_log_stream_ctx, + TP_PROTO(struct xhci_stream_info *info, unsigned int stream_id), + TP_ARGS(info, stream_id), + TP_STRUCT__entry( + __field(unsigned int, stream_id) + __field(u64, stream_ring) + __field(dma_addr_t, ctx_array_dma) + + ), + TP_fast_assign( + __entry->stream_id = stream_id; + __entry->stream_ring = le64_to_cpu(info->stream_ctx_array[stream_id].stream_ring); + __entry->ctx_array_dma = info->ctx_array_dma + stream_id * 16; + + ), + TP_printk("stream %u ctx @%pad: SCT %llu deq %llx", __entry->stream_id, + &__entry->ctx_array_dma, CTX_TO_SCT(__entry->stream_ring), + __entry->stream_ring + ) +); + +DEFINE_EVENT(xhci_log_stream_ctx, xhci_alloc_stream_info_ctx, + TP_PROTO(struct xhci_stream_info *info, unsigned int stream_id), + TP_ARGS(info, stream_id) +); + +DEFINE_EVENT(xhci_log_stream_ctx, xhci_handle_cmd_set_deq_stream, + TP_PROTO(struct xhci_stream_info *info, unsigned int stream_id), + TP_ARGS(info, stream_id) +); + DECLARE_EVENT_CLASS(xhci_log_ep_ctx, TP_PROTO(struct xhci_ep_ctx *ctx), TP_ARGS(ctx), @@ -454,8 +489,6 @@ DECLARE_EVENT_CLASS(xhci_log_ring, __field(void *, ring) __field(dma_addr_t, enq) __field(dma_addr_t, deq) - __field(dma_addr_t, enq_seg) - __field(dma_addr_t, deq_seg) __field(unsigned int, num_segs) __field(unsigned int, stream_id) __field(unsigned int, cycle_state) @@ -466,17 +499,15 @@ DECLARE_EVENT_CLASS(xhci_log_ring, __entry->type = ring->type; __entry->num_segs = ring->num_segs; __entry->stream_id = ring->stream_id; - __entry->enq_seg = ring->enq_seg->dma; - __entry->deq_seg = ring->deq_seg->dma; __entry->cycle_state = ring->cycle_state; __entry->bounce_buf_len = ring->bounce_buf_len; __entry->enq = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); __entry->deq = xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue); ), - TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d bounce %d cycle %d", + TP_printk("%s %p: enq %pad deq %pad segs %d stream %d bounce %d cycle %d", xhci_ring_type_string(__entry->type), __entry->ring, - &__entry->enq, &__entry->enq_seg, - &__entry->deq, &__entry->deq_seg, + &__entry->enq, + &__entry->deq, __entry->num_segs, __entry->stream_id, __entry->bounce_buf_len, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 899c0effb5d3..0452b8d65832 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -8,6 +8,7 @@ * Some code borrowed from the Linux EHCI driver. */ +#include <linux/jiffies.h> #include <linux/pci.h> #include <linux/iommu.h> #include <linux/iopoll.h> @@ -16,6 +17,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/dmi.h> #include <linux/dma-mapping.h> @@ -40,15 +42,15 @@ MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring) { - struct xhci_segment *seg = ring->first_seg; + struct xhci_segment *seg; if (!td || !td->start_seg) return false; - do { + + xhci_for_each_ring_seg(ring->first_seg, seg) { if (seg == td->start_seg) return true; - seg = seg->next; - } while (seg && seg != ring->first_seg); + } return false; } @@ -473,14 +475,7 @@ static int xhci_init(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_init"); spin_lock_init(&xhci->lock); - if (xhci->hci_version == 0x95 && link_quirk) { - xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, - "QUIRK: Not clearing Link TRB chain bits."); - xhci->quirks |= XHCI_LINK_TRB_QUIRK; - } else { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "xHCI doesn't need link TRB QUIRK"); - } + retval = xhci_mem_init(xhci, GFP_KERNEL); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_init"); @@ -632,7 +627,7 @@ void xhci_stop(struct usb_hcd *hcd) /* Deleting Compliance Mode Recovery Timer */ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && (!(xhci_all_ports_seen_u0(xhci)))) { - del_timer_sync(&xhci->comp_mode_recovery_timer); + timer_delete_sync(&xhci->comp_mode_recovery_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "%s: compliance mode recovery timer deleted", __func__); @@ -677,11 +672,11 @@ void xhci_shutdown(struct usb_hcd *hcd) xhci_dbg(xhci, "%s: stopping usb%d port polling.\n", __func__, hcd->self.busnum); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - del_timer_sync(&hcd->rh_timer); + timer_delete_sync(&hcd->rh_timer); if (xhci->shared_hcd) { clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); - del_timer_sync(&xhci->shared_hcd->rh_timer); + timer_delete_sync(&xhci->shared_hcd->rh_timer); } spin_lock_irq(&xhci->lock); @@ -785,16 +780,14 @@ static void xhci_clear_command_ring(struct xhci_hcd *xhci) struct xhci_segment *seg; ring = xhci->cmd_ring; - seg = ring->deq_seg; - do { - memset(seg->trbs, 0, - sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1)); - seg->trbs[TRBS_PER_SEGMENT - 1].link.control &= - cpu_to_le32(~TRB_CYCLE); - seg = seg->next; - } while (seg != ring->deq_seg); - - xhci_initialize_ring_info(ring, 1); + xhci_for_each_ring_seg(ring->first_seg, seg) { + /* erase all TRBs before the link */ + memset(seg->trbs, 0, sizeof(union xhci_trb) * (TRBS_PER_SEGMENT - 1)); + /* clear link cycle bit */ + seg->trbs[TRBS_PER_SEGMENT - 1].link.control &= cpu_to_le32(~TRB_CYCLE); + } + + xhci_initialize_ring_info(ring); /* * Reset the hardware dequeue pointer. * Yes, this will need to be re-written after resume, but we're paranoid @@ -915,10 +908,10 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) xhci_dbg(xhci, "%s: stopping usb%d port polling.\n", __func__, hcd->self.busnum); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - del_timer_sync(&hcd->rh_timer); + timer_delete_sync(&hcd->rh_timer); if (xhci->shared_hcd) { clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); - del_timer_sync(&xhci->shared_hcd->rh_timer); + timer_delete_sync(&xhci->shared_hcd->rh_timer); } if (xhci->quirks & XHCI_SUSPEND_DELAY) @@ -985,7 +978,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) */ if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && (!(xhci_all_ports_seen_u0(xhci)))) { - del_timer_sync(&xhci->comp_mode_recovery_timer); + timer_delete_sync(&xhci->comp_mode_recovery_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "%s: compliance mode recovery timer deleted", __func__); @@ -1001,16 +994,14 @@ EXPORT_SYMBOL_GPL(xhci_suspend); * This is called when the machine transition from S3/S4 mode. * */ -int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) +int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume) { - bool hibernated = (msg.event == PM_EVENT_RESTORE); u32 command, temp = 0; struct usb_hcd *hcd = xhci_to_hcd(xhci); int retval = 0; bool comp_timer_running = false; bool pending_portevent = false; bool suspended_usb3_devs = false; - bool reinit_xhc = false; if (!hcd->state) return 0; @@ -1029,10 +1020,10 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) spin_lock_irq(&xhci->lock); - if (hibernated || xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend) - reinit_xhc = true; + if (xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend) + power_lost = true; - if (!reinit_xhc) { + if (!power_lost) { /* * Some controllers might lose power during suspend, so wait * for controller not ready bit to clear, just as in xHC init. @@ -1072,15 +1063,15 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) /* re-initialize the HC on Restore Error, or Host Controller Error */ if ((temp & (STS_SRE | STS_HCE)) && !(xhci->xhc_state & XHCI_STATE_REMOVING)) { - reinit_xhc = true; - if (!xhci->broken_suspend) + if (!power_lost) xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp); + power_lost = true; } - if (reinit_xhc) { + if (power_lost) { if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !(xhci_all_ports_seen_u0(xhci))) { - del_timer_sync(&xhci->comp_mode_recovery_timer); + timer_delete_sync(&xhci->comp_mode_recovery_timer); xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "Compliance Mode Recovery Timer deleted!"); } @@ -1175,8 +1166,7 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) pending_portevent = xhci_pending_portevent(xhci); - if (suspended_usb3_devs && !pending_portevent && - msg.event == PM_EVENT_AUTO_RESUME) { + if (suspended_usb3_devs && !pending_portevent && is_auto_resume) { msleep(120); pending_portevent = xhci_pending_portevent(xhci); } @@ -1615,6 +1605,11 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag goto free_priv; } + /* Class driver might not be aware ep halted due to async URB giveback */ + if (*ep_state & EP_STALLED) + dev_dbg(&urb->dev->dev, "URB %p queued before clearing halt\n", + urb); + switch (usb_endpoint_type(&urb->ep->desc)) { case USB_ENDPOINT_XFER_CONTROL: @@ -1756,7 +1751,7 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) urb->ep->desc.bEndpointAddress, (unsigned long long) xhci_trb_virt_to_dma( urb_priv->td[i].start_seg, - urb_priv->td[i].first_trb)); + urb_priv->td[i].start_trb)); for (; i < urb_priv->num_tds; i++) { td = &urb_priv->td[i]; @@ -1768,15 +1763,27 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) } } - /* Queue a stop endpoint command, but only if this is - * the first cancellation to be handled. - */ - if (!(ep->ep_state & EP_STOP_CMD_PENDING)) { + /* These completion handlers will sort out cancelled TDs for us */ + if (ep->ep_state & (EP_STOP_CMD_PENDING | EP_HALTED | SET_DEQ_PENDING)) { + xhci_dbg(xhci, "Not queuing Stop Endpoint on slot %d ep %d in state 0x%x\n", + urb->dev->slot_id, ep_index, ep->ep_state); + goto done; + } + + /* In these cases no commands are pending but the endpoint is stopped */ + if (ep->ep_state & (EP_CLEARING_TT | EP_STALLED)) { + /* and cancelled TDs can be given back right away */ + xhci_dbg(xhci, "Invalidating TDs instantly on slot %d ep %d in state 0x%x\n", + urb->dev->slot_id, ep_index, ep->ep_state); + xhci_process_cancelled_tds(ep); + } else { + /* Otherwise, queue a new Stop Endpoint command */ command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) { ret = -ENOMEM; goto done; } + ep->stop_time = jiffies; ep->ep_state |= EP_STOP_CMD_PENDING; xhci_queue_stop_endpoint(xhci, command, urb->dev->slot_id, ep_index, 0); @@ -2794,6 +2801,51 @@ static int xhci_reserve_bandwidth(struct xhci_hcd *xhci, return -ENOMEM; } +/* + * Synchronous XHCI stop endpoint helper. Issues the stop endpoint command and + * waits for the command completion before returning. This does not call + * xhci_handle_cmd_stop_ep(), which has additional handling for 'context error' + * cases, along with transfer ring cleanup. + * + * xhci_stop_endpoint_sync() is intended to be utilized by clients that manage + * their own transfer ring, such as offload situations. + */ +int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int suspend, + gfp_t gfp_flags) +{ + struct xhci_command *command; + unsigned long flags; + int ret; + + command = xhci_alloc_command(xhci, true, gfp_flags); + if (!command) + return -ENOMEM; + + spin_lock_irqsave(&xhci->lock, flags); + ret = xhci_queue_stop_endpoint(xhci, command, ep->vdev->slot_id, + ep->ep_index, suspend); + if (ret < 0) { + spin_unlock_irqrestore(&xhci->lock, flags); + goto out; + } + + xhci_ring_cmd_db(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); + + wait_for_completion(command->completion); + + /* No handling for COMP_CONTEXT_STATE_ERROR done at command completion*/ + if (command->status == COMP_COMMAND_ABORTED || + command->status == COMP_COMMAND_RING_STOPPED) { + xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n"); + ret = -ETIME; + } +out: + xhci_free_command(xhci, command); + + return ret; +} +EXPORT_SYMBOL_GPL(xhci_stop_endpoint_sync); /* Issue a configure endpoint command or evaluate context command * and wait for it to finish. @@ -3157,8 +3209,11 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd, ep = &vdev->eps[ep_index]; - /* Bail out if toggle is already being cleared by a endpoint reset */ spin_lock_irqsave(&xhci->lock, flags); + + ep->ep_state &= ~EP_STALLED; + + /* Bail out if toggle is already being cleared by a endpoint reset */ if (ep->ep_state & EP_HARD_CLEAR_TOGGLE) { ep->ep_state &= ~EP_HARD_CLEAR_TOGGLE; spin_unlock_irqrestore(&xhci->lock, flags); @@ -3692,6 +3747,8 @@ void xhci_free_device_endpoint_resources(struct xhci_hcd *xhci, xhci->num_active_eps); } +static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev); + /* * This submits a Reset Device Command, which will set the device state to 0, * set the device address to 0, and disable all the endpoints except the default @@ -3762,6 +3819,23 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, SLOT_STATE_DISABLED) return 0; + if (xhci->quirks & XHCI_ETRON_HOST) { + /* + * Obtaining a new device slot to inform the xHCI host that + * the USB device has been reset. + */ + ret = xhci_disable_slot(xhci, udev->slot_id); + xhci_free_virt_device(xhci, udev->slot_id); + if (!ret) { + ret = xhci_alloc_dev(hcd, udev); + if (ret == 1) + ret = 0; + else + ret = -EINVAL; + } + return ret; + } + trace_xhci_discover_or_reset_device(slot_ctx); xhci_dbg(xhci, "Resetting device with slot ID %u\n", slot_id); @@ -4459,7 +4533,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, hlpm_addr = ports[port_num]->addr + PORTHLPMC; xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", - enable ? "enable" : "disable", port_num + 1); + str_enable_disable(enable), port_num + 1); if (enable) { /* Host supports BESL timeout instead of HIRD */ @@ -4690,8 +4764,8 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci, */ if (timeout_ns <= USB3_LPM_U1_MAX_TIMEOUT) return timeout_ns; - dev_dbg(&udev->dev, "Hub-initiated U1 disabled " - "due to long timeout %llu ms\n", timeout_ns); + dev_dbg(&udev->dev, "Hub-initiated U1 disabled due to long timeout %lluus\n", + timeout_ns); return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1); } @@ -4748,8 +4822,8 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci, */ if (timeout_ns <= USB3_LPM_U2_MAX_TIMEOUT) return timeout_ns; - dev_dbg(&udev->dev, "Hub-initiated U2 disabled " - "due to long timeout %llu ms\n", timeout_ns); + dev_dbg(&udev->dev, "Hub-initiated U2 disabled due to long timeout %lluus\n", + timeout_ns * 256); return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U2); } @@ -5251,6 +5325,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x96) xhci->quirks |= XHCI_SPURIOUS_SUCCESS; + if (xhci->hci_version == 0x95 && link_quirk) { + xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits"); + xhci->quirks |= XHCI_LINK_TRB_QUIRK; + } + /* Make sure the HC is halted. */ retval = xhci_halt(xhci); if (retval) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f0fb696d5619..37860f1e3aba 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -211,6 +211,9 @@ struct xhci_op_regs { #define CONFIG_CIE (1 << 9) /* bits 10:31 - reserved and should be preserved */ +/* bits 15:0 - HCD page shift bit */ +#define XHCI_PAGE_SIZE_MASK 0xffff + /** * struct xhci_intr_reg - Interrupt Register Set * @irq_pending: IMAN - Interrupt Management Register. Used to enable @@ -529,6 +532,7 @@ struct xhci_command { /* Input context for changing device state */ struct xhci_container_ctx *in_ctx; u32 status; + u32 comp_param; int slot_id; /* If completion is null, no one is waiting on this command * and the structure can be freed after the command completes. @@ -554,6 +558,7 @@ struct xhci_stream_ctx { /* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */ #define SCT_FOR_CTX(p) (((p) & 0x7) << 1) +#define CTX_TO_SCT(p) (((p) >> 1) & 0x7) /* Secondary stream array type, dequeue pointer is to a transfer ring */ #define SCT_SEC_TR 0 /* Primary stream array type, dequeue pointer is to a transfer ring */ @@ -659,7 +664,7 @@ struct xhci_virt_ep { unsigned int err_count; unsigned int ep_state; #define SET_DEQ_PENDING (1 << 0) -#define EP_HALTED (1 << 1) /* For stall handling */ +#define EP_HALTED (1 << 1) /* Halted host ep handling */ #define EP_STOP_CMD_PENDING (1 << 2) /* For URB cancellation */ /* Transitioning the endpoint to using streams, don't enqueue URBs */ #define EP_GETTING_STREAMS (1 << 3) @@ -670,6 +675,7 @@ struct xhci_virt_ep { #define EP_SOFT_CLEAR_TOGGLE (1 << 7) /* usb_hub_clear_tt_buffer is in progress */ #define EP_CLEARING_TT (1 << 8) +#define EP_STALLED (1 << 9) /* For stall handling */ /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; struct xhci_hcd *xhci; @@ -690,6 +696,7 @@ struct xhci_virt_ep { /* Bandwidth checking storage */ struct xhci_bw_info bw_info; struct list_head bw_endpoint_list; + unsigned long stop_time; /* Isoch Frame ID checking storage */ int next_frame_id; /* Use new Isoch TRB layout needed for extended TBC support */ @@ -957,6 +964,9 @@ struct xhci_event_cmd { __le32 flags; }; +/* status bitmasks */ +#define COMP_PARAM(p) ((p) & 0xffffff) /* Command Completion Parameter */ + /* Address device - disable SetAddress */ #define TRB_BSR (1<<9) @@ -1023,9 +1033,6 @@ enum xhci_setup_dev { /* Interrupter Target - which MSI-X vector to target the completion event at */ #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) -/* Total burst count field, Rsvdz on xhci 1.1 with Extended TBC enabled (ETE) */ -#define TRB_TBC(p) (((p) & 0x3) << 7) -#define TRB_TLBPC(p) (((p) & 0xf) << 16) /* Cycle bit - indicates TRB ownership by HC or HCD */ #define TRB_CYCLE (1<<0) @@ -1059,6 +1066,12 @@ enum xhci_setup_dev { /* Isochronous TRB specific fields */ #define TRB_SIA (1<<31) #define TRB_FRAME_ID(p) (((p) & 0x7ff) << 20) +#define GET_FRAME_ID(p) (((p) >> 20) & 0x7ff) +/* Total burst count field, Rsvdz on xhci 1.1 with Extended TBC enabled (ETE) */ +#define TRB_TBC(p) (((p) & 0x3) << 7) +#define GET_TBC(p) (((p) >> 7) & 0x3) +#define TRB_TLBPC(p) (((p) & 0xf) << 16) +#define GET_TLBPC(p) (((p) >> 16) & 0xf) /* TRB cache size for xHC with TRB cache */ #define TRB_CACHE_SIZE_HS 8 @@ -1259,6 +1272,9 @@ static inline const char *xhci_trb_type_string(u8 type) #define AVOID_BEI_INTERVAL_MIN 8 #define AVOID_BEI_INTERVAL_MAX 32 +#define xhci_for_each_ring_seg(head, seg) \ + for (seg = head; seg != NULL; seg = (seg->next != head ? seg->next : NULL)) + struct xhci_segment { union xhci_trb *trbs; /* private to HCD */ @@ -1287,9 +1303,9 @@ struct xhci_td { enum xhci_cancelled_td_status cancel_status; struct urb *urb; struct xhci_segment *start_seg; - union xhci_trb *first_trb; - union xhci_trb *last_trb; - struct xhci_segment *last_trb_seg; + union xhci_trb *start_trb; + struct xhci_segment *end_seg; + union xhci_trb *end_trb; struct xhci_segment *bounce_seg; /* actual_length of the URB has already been set */ bool urb_length_set; @@ -1359,7 +1375,7 @@ struct xhci_ring { unsigned int num_trbs_free; /* used only by xhci DbC */ unsigned int bounce_buf_len; enum xhci_ring_type type; - bool last_td_was_short; + u32 old_trb_comp_code; struct radix_tree_root *trb_address_map; }; @@ -1502,10 +1518,7 @@ struct xhci_hcd { u16 max_interrupters; /* imod_interval in ns (I * 250ns) */ u32 imod_interval; - /* 4KB min, 128MB max */ - int page_size; - /* Valid values are 12 to 20, inclusive */ - int page_shift; + u32 page_size; /* MSI-X/MSI vectors */ int nvecs; /* optional clocks */ @@ -1620,10 +1633,11 @@ struct xhci_hcd { #define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(42) #define XHCI_SUSPEND_RESUME_CLKS BIT_ULL(43) #define XHCI_RESET_TO_DEFAULT BIT_ULL(44) -#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45) +#define XHCI_TRB_OVERFETCH BIT_ULL(45) #define XHCI_ZHAOXIN_HOST BIT_ULL(46) #define XHCI_WRITE_64_HI_LO BIT_ULL(47) #define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48) +#define XHCI_ETRON_HOST BIT_ULL(49) unsigned int num_active_eps; unsigned int limit_active_eps; @@ -1746,11 +1760,20 @@ static inline void xhci_write_64(struct xhci_hcd *xhci, } -/* Link TRB chain should always be set on 0.95 hosts, and AMD 0.96 ISOC rings */ +/* + * Reportedly, some chapters of v0.95 spec said that Link TRB always has its chain bit set. + * Other chapters and later specs say that it should only be set if the link is inside a TD + * which continues from the end of one segment to the next segment. + * + * Some 0.95 hardware was found to misbehave if any link TRB doesn't have the chain bit set. + * + * 0.96 hardware from AMD and NEC was found to ignore unchained isochronous link TRBs when + * "resynchronizing the pipe" after a Missed Service Error. + */ static inline bool xhci_link_chain_quirk(struct xhci_hcd *xhci, enum xhci_ring_type type) { return (xhci->quirks & XHCI_LINK_TRB_QUIRK) || - (type == TYPE_ISOC && (xhci->quirks & XHCI_AMD_0x96_HOST)); + (type == TYPE_ISOC && (xhci->quirks & (XHCI_AMD_0x96_HOST | XHCI_NEC_HOST))); } /* xHCI debugging */ @@ -1788,14 +1811,12 @@ void xhci_slot_copy(struct xhci_hcd *xhci, int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *udev, struct usb_host_endpoint *ep, gfp_t mem_flags); -struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, - unsigned int num_segs, unsigned int cycle_state, +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, enum xhci_ring_type type, unsigned int max_packet, gfp_t flags); void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring); int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, unsigned int num_trbs, gfp_t flags); -void xhci_initialize_ring_info(struct xhci_ring *ring, - unsigned int cycle_state); +void xhci_initialize_ring_info(struct xhci_ring *ring); void xhci_free_endpoint_ring(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, unsigned int ep_index); @@ -1859,7 +1880,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); int xhci_ext_cap_init(struct xhci_hcd *xhci); int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup); -int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg); +int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume); irqreturn_t xhci_irq(struct usb_hcd *hcd); irqreturn_t xhci_msi_irq(int irq, void *hcd); @@ -1873,8 +1894,6 @@ int xhci_set_interrupter_moderation(struct xhci_interrupter *ir, /* xHCI ring, segment, TRB, and TD functions */ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); -struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, - dma_addr_t suspect_dma, bool debug); int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code); void xhci_ring_cmd_db(struct xhci_hcd *xhci); int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd, @@ -1913,6 +1932,9 @@ 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); +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); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, @@ -2070,7 +2092,6 @@ static inline const char *xhci_decode_trb(char *str, size_t size, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_NORMAL: - case TRB_ISOC: case TRB_EVENT_DATA: case TRB_TR_NOOP: snprintf(str, size, @@ -2087,7 +2108,25 @@ static inline const char *xhci_decode_trb(char *str, size_t size, field3 & TRB_ENT ? 'E' : 'e', field3 & TRB_CYCLE ? 'C' : 'c'); break; - + case TRB_ISOC: + snprintf(str, size, + "Buffer %08x%08x length %d TD size/TBC %d intr %d type '%s' TBC %u TLBPC %u frame_id %u flags %c:%c:%c:%c:%c:%c:%c:%c:%c", + field1, field0, TRB_LEN(field2), GET_TD_SIZE(field2), + GET_INTR_TARGET(field2), + xhci_trb_type_string(type), + GET_TBC(field3), + GET_TLBPC(field3), + GET_FRAME_ID(field3), + field3 & TRB_SIA ? 'S' : 's', + field3 & TRB_BEI ? 'B' : 'b', + field3 & TRB_IDT ? 'I' : 'i', + field3 & TRB_IOC ? 'I' : 'i', + field3 & TRB_CHAIN ? 'C' : 'c', + field3 & TRB_NO_SNOOP ? 'S' : 's', + field3 & TRB_ISP ? 'I' : 'i', + field3 & TRB_ENT ? 'E' : 'e', + field3 & TRB_CYCLE ? 'C' : 'c'); + break; case TRB_CMD_NOOP: case TRB_ENABLE_SLOT: snprintf(str, size, diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 9f758241d9d3..934ec5310fb9 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -322,7 +322,7 @@ static inline void mts_urb_abort(struct mts_desc* desc) { usb_kill_urb( desc->urb ); } -static int mts_slave_alloc (struct scsi_device *s) +static int mts_sdev_init (struct scsi_device *s) { s->inquiry_len = 0x24; return 0; @@ -626,7 +626,7 @@ static const struct scsi_host_template mts_scsi_host_template = { .this_id = -1, .emulated = 1, .dma_alignment = 511, - .slave_alloc = mts_slave_alloc, + .sdev_init = mts_sdev_init, .max_sectors= 256, /* 128 K */ }; diff --git a/drivers/usb/isp1760/Kconfig b/drivers/usb/isp1760/Kconfig index 2ed2b73291d1..e19178f3cdd3 100644 --- a/drivers/usb/isp1760/Kconfig +++ b/drivers/usb/isp1760/Kconfig @@ -26,7 +26,7 @@ config USB_ISP1761_UDC if USB_ISP1760 choice - bool "ISP1760 Mode Selection" + prompt "ISP1760 Mode Selection" default USB_ISP1760_DUAL_ROLE if (USB && USB_GADGET) default USB_ISP1760_HOST_ROLE if (USB && !USB_GADGET) default USB_ISP1760_GADGET_ROLE if (!USB && USB_GADGET) diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c index add2d2e3b61b..8dcd9cc22413 100644 --- a/drivers/usb/isp1760/isp1760-hcd.c +++ b/drivers/usb/isp1760/isp1760-hcd.c @@ -2458,7 +2458,7 @@ static void isp1760_stop(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - del_timer(&errata2_timer); + timer_delete(&errata2_timer); isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1, NULL, 0); diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c index fe1e3985419a..a64190addba6 100644 --- a/drivers/usb/isp1760/isp1760-if.c +++ b/drivers/usb/isp1760/isp1760-if.c @@ -263,7 +263,7 @@ MODULE_DEVICE_TABLE(of, isp1760_of_match); static struct platform_driver isp1760_plat_driver = { .probe = isp1760_plat_probe, - .remove_new = isp1760_plat_remove, + .remove = isp1760_plat_remove, .driver = { .name = "isp1760", .of_match_table = of_match_ptr(isp1760_of_match), diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index 5cafd23345ca..2af89ee28baa 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -1145,7 +1145,7 @@ static void isp1760_udc_disconnect(struct isp1760_udc *udc) if (udc->driver->disconnect) udc->driver->disconnect(&udc->gadget); - del_timer(&udc->vbus_timer); + timer_delete(&udc->vbus_timer); /* TODO Reset all endpoints ? */ } @@ -1314,7 +1314,7 @@ static int isp1760_udc_stop(struct usb_gadget *gadget) dev_dbg(udc->isp->dev, "%s\n", __func__); - del_timer_sync(&udc->vbus_timer); + timer_delete_sync(&udc->vbus_timer); isp1760_reg_write(udc->regs, mode_reg, 0); diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index 6fb5140e29b9..225863321dc4 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -27,6 +27,8 @@ static struct usb_class_driver chaoskey_class; static int chaoskey_rng_read(struct hwrng *rng, void *data, size_t max, bool wait); +static DEFINE_MUTEX(chaoskey_list_lock); + #define usb_dbg(usb_if, format, arg...) \ dev_dbg(&(usb_if)->dev, format, ## arg) @@ -233,6 +235,7 @@ static void chaoskey_disconnect(struct usb_interface *interface) usb_deregister_dev(interface, &chaoskey_class); usb_set_intfdata(interface, NULL); + mutex_lock(&chaoskey_list_lock); mutex_lock(&dev->lock); dev->present = false; @@ -244,6 +247,7 @@ static void chaoskey_disconnect(struct usb_interface *interface) } else mutex_unlock(&dev->lock); + mutex_unlock(&chaoskey_list_lock); usb_dbg(interface, "disconnect done"); } @@ -251,6 +255,7 @@ static int chaoskey_open(struct inode *inode, struct file *file) { struct chaoskey *dev; struct usb_interface *interface; + int rv = 0; /* get the interface from minor number and driver information */ interface = usb_find_interface(&chaoskey_driver, iminor(inode)); @@ -266,18 +271,23 @@ static int chaoskey_open(struct inode *inode, struct file *file) } file->private_data = dev; + mutex_lock(&chaoskey_list_lock); mutex_lock(&dev->lock); - ++dev->open; + if (dev->present) + ++dev->open; + else + rv = -ENODEV; mutex_unlock(&dev->lock); + mutex_unlock(&chaoskey_list_lock); - usb_dbg(interface, "open success"); - return 0; + return rv; } static int chaoskey_release(struct inode *inode, struct file *file) { struct chaoskey *dev = file->private_data; struct usb_interface *interface; + int rv = 0; if (dev == NULL) return -ENODEV; @@ -286,14 +296,15 @@ static int chaoskey_release(struct inode *inode, struct file *file) usb_dbg(interface, "release"); + mutex_lock(&chaoskey_list_lock); mutex_lock(&dev->lock); usb_dbg(interface, "open count at release is %d", dev->open); if (dev->open <= 0) { usb_dbg(interface, "invalid open count (%d)", dev->open); - mutex_unlock(&dev->lock); - return -ENODEV; + rv = -ENODEV; + goto bail; } --dev->open; @@ -302,13 +313,15 @@ static int chaoskey_release(struct inode *inode, struct file *file) if (dev->open == 0) { mutex_unlock(&dev->lock); chaoskey_free(dev); - } else - mutex_unlock(&dev->lock); - } else - mutex_unlock(&dev->lock); - + goto destruction; + } + } +bail: + mutex_unlock(&dev->lock); +destruction: + mutex_unlock(&chaoskey_list_lock); usb_dbg(interface, "release success"); - return 0; + return rv; } static void chaos_read_callback(struct urb *urb) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 6d28467ce352..365c10069345 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -277,28 +277,45 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer, struct iowarrior *dev; int read_idx; int offset; + int retval; dev = file->private_data; + if (file->f_flags & O_NONBLOCK) { + retval = mutex_trylock(&dev->mutex); + if (!retval) + return -EAGAIN; + } else { + retval = mutex_lock_interruptible(&dev->mutex); + if (retval) + return -ERESTARTSYS; + } + /* verify that the device wasn't unplugged */ - if (!dev || !dev->present) - return -ENODEV; + if (!dev->present) { + retval = -ENODEV; + goto exit; + } dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n", dev->minor, count); /* read count must be packet size (+ time stamp) */ if ((count != dev->report_size) - && (count != (dev->report_size + 1))) - return -EINVAL; + && (count != (dev->report_size + 1))) { + retval = -EINVAL; + goto exit; + } /* repeat until no buffer overrun in callback handler occur */ do { atomic_set(&dev->overflow_flag, 0); if ((read_idx = read_index(dev)) == -1) { /* queue empty */ - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto exit; + } else { //next line will return when there is either new data, or the device is unplugged int r = wait_event_interruptible(dev->read_wait, @@ -309,28 +326,37 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer, -1)); if (r) { //we were interrupted by a signal - return -ERESTART; + retval = -ERESTART; + goto exit; } if (!dev->present) { //The device was unplugged - return -ENODEV; + retval = -ENODEV; + goto exit; } if (read_idx == -1) { // Can this happen ??? - return 0; + retval = 0; + goto exit; } } } offset = read_idx * (dev->report_size + 1); if (copy_to_user(buffer, dev->read_queue + offset, count)) { - return -EFAULT; + retval = -EFAULT; + goto exit; } } while (atomic_read(&dev->overflow_flag)); read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx; atomic_set(&dev->read_idx, read_idx); + mutex_unlock(&dev->mutex); return count; + +exit: + mutex_unlock(&dev->mutex); + return retval; } /* @@ -885,7 +911,6 @@ error: static void iowarrior_disconnect(struct usb_interface *interface) { struct iowarrior *dev = usb_get_intfdata(interface); - int minor = dev->minor; usb_deregister_dev(interface, &iowarrior_class); @@ -909,9 +934,6 @@ static void iowarrior_disconnect(struct usb_interface *interface) mutex_unlock(&dev->mutex); iowarrior_delete(dev); } - - dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n", - minor - IOWARRIOR_MINOR_BASE); } /* usb specific object needed to register this driver with the usb subsystem */ diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c index 75dfdca04ff1..75ac3c6aa92d 100644 --- a/drivers/usb/misc/onboard_usb_dev.c +++ b/drivers/usb/misc/onboard_usb_dev.c @@ -407,8 +407,10 @@ static int onboard_dev_probe(struct platform_device *pdev) } if (of_device_is_compatible(pdev->dev.of_node, "usb424,2744") || - of_device_is_compatible(pdev->dev.of_node, "usb424,5744")) + of_device_is_compatible(pdev->dev.of_node, "usb424,5744")) { err = onboard_dev_5744_i2c_init(client); + onboard_dev->always_powered_in_suspend = true; + } put_device(&client->dev); if (err < 0) @@ -473,7 +475,7 @@ static const struct dev_pm_ops __maybe_unused onboard_dev_pm_ops = { static struct platform_driver onboard_dev_driver = { .probe = onboard_dev_probe, - .remove_new = onboard_dev_remove, + .remove = onboard_dev_remove, .driver = { .name = "onboard-usb-dev", diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h index 317b3eb99c02..933797a7e084 100644 --- a/drivers/usb/misc/onboard_usb_dev.h +++ b/drivers/usb/misc/onboard_usb_dev.h @@ -23,6 +23,13 @@ static const struct onboard_dev_pdata microchip_usb424_data = { .is_hub = true, }; +static const struct onboard_dev_pdata microchip_usb2514_data = { + .reset_us = 1, + .num_supplies = 2, + .supply_names = { "vdd", "vdda" }, + .is_hub = true, +}; + static const struct onboard_dev_pdata microchip_usb5744_data = { .reset_us = 0, .power_on_delay_us = 10000, @@ -96,7 +103,7 @@ static const struct onboard_dev_pdata xmos_xvf3500_data = { static const struct of_device_id onboard_dev_match[] = { { .compatible = "usb424,2412", .data = µchip_usb424_data, }, - { .compatible = "usb424,2514", .data = µchip_usb424_data, }, + { .compatible = "usb424,2514", .data = µchip_usb2514_data, }, { .compatible = "usb424,2517", .data = µchip_usb424_data, }, { .compatible = "usb424,2744", .data = µchip_usb5744_data, }, { .compatible = "usb424,5744", .data = µchip_usb5744_data, }, diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c index 19906301a4eb..83079c414b4f 100644 --- a/drivers/usb/misc/qcom_eud.c +++ b/drivers/usb/misc/qcom_eud.c @@ -239,7 +239,7 @@ MODULE_DEVICE_TABLE(of, eud_dt_match); static struct platform_driver eud_driver = { .probe = eud_probe, - .remove_new = eud_remove, + .remove = eud_remove, .driver = { .name = "qcom_eud", .dev_groups = eud_groups, diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index 01ceafc4ab78..c562630d862c 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c @@ -332,14 +332,11 @@ static int ljca_send(struct ljca_adapter *adap, u8 type, u8 cmd, ret = usb_bulk_msg(adap->usb_dev, adap->tx_pipe, header, msg_len, &transferred, LJCA_WRITE_TIMEOUT_MS); - - usb_autopm_put_interface(adap->intf); - if (ret < 0) - goto out; + goto out_put; if (transferred != msg_len) { ret = -EIO; - goto out; + goto out_put; } if (ack) { @@ -347,11 +344,14 @@ static int ljca_send(struct ljca_adapter *adap, u8 type, u8 cmd, timeout); if (!ret) { ret = -ETIMEDOUT; - goto out; + goto out_put; } } ret = adap->actual_length; +out_put: + usb_autopm_put_interface(adap->intf); + out: spin_lock_irqsave(&adap->lock, flags); adap->ex_buf = NULL; @@ -372,7 +372,7 @@ int ljca_transfer(struct ljca_client *client, u8 cmd, const u8 *obuf, obuf, obuf_len, ibuf, ibuf_len, true, LJCA_WRITE_ACK_TIMEOUT_MS); } -EXPORT_SYMBOL_NS_GPL(ljca_transfer, LJCA); +EXPORT_SYMBOL_NS_GPL(ljca_transfer, "LJCA"); int ljca_transfer_noack(struct ljca_client *client, u8 cmd, const u8 *obuf, u8 obuf_len) @@ -380,7 +380,7 @@ int ljca_transfer_noack(struct ljca_client *client, u8 cmd, const u8 *obuf, return ljca_send(client->adapter, client->type, cmd, obuf, obuf_len, NULL, 0, false, LJCA_WRITE_ACK_TIMEOUT_MS); } -EXPORT_SYMBOL_NS_GPL(ljca_transfer_noack, LJCA); +EXPORT_SYMBOL_NS_GPL(ljca_transfer_noack, "LJCA"); int ljca_register_event_cb(struct ljca_client *client, ljca_event_cb_t event_cb, void *context) @@ -404,7 +404,7 @@ int ljca_register_event_cb(struct ljca_client *client, ljca_event_cb_t event_cb, return 0; } -EXPORT_SYMBOL_NS_GPL(ljca_register_event_cb, LJCA); +EXPORT_SYMBOL_NS_GPL(ljca_register_event_cb, "LJCA"); void ljca_unregister_event_cb(struct ljca_client *client) { @@ -417,7 +417,7 @@ void ljca_unregister_event_cb(struct ljca_client *client) spin_unlock_irqrestore(&client->event_cb_lock, flags); } -EXPORT_SYMBOL_NS_GPL(ljca_unregister_event_cb, LJCA); +EXPORT_SYMBOL_NS_GPL(ljca_unregister_event_cb, "LJCA"); static int ljca_match_device_ids(struct acpi_device *adev, void *data) { @@ -811,6 +811,14 @@ static int ljca_probe(struct usb_interface *interface, if (ret) goto err_free; + /* + * This works around problems with ov2740 initialization on some + * Lenovo platforms. The autosuspend delay, has to be smaller than + * the delay after setting the reset_gpio line in ov2740_resume(). + * Otherwise the sensor randomly fails to initialize. + */ + pm_runtime_set_autosuspend_delay(&usb_dev->dev, 10); + usb_enable_autosuspend(usb_dev); return 0; diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index e24cdb667307..4fb453ca5450 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -636,10 +636,8 @@ static int usb251xb_probe(struct usb251xb *hub) if (np && usb_data) { err = usb251xb_get_ofdata(hub, usb_data); - if (err) { - dev_err(dev, "failed to get ofdata: %d\n", err); - return err; - } + if (err) + return dev_err_probe(dev, err, "failed to get ofdata\n"); } /* diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 3b33e4878c60..322e59381b78 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -423,7 +423,7 @@ static struct platform_driver usb3503_platform_driver = { .pm = pm_ptr(&usb3503_platform_pm_ops), }, .probe = usb3503_platform_probe, - .remove_new = usb3503_platform_remove, + .remove = usb3503_platform_remove, }; static int __init usb3503_init(void) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index caf65f8294db..853a5f082a70 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -626,7 +626,7 @@ static int perform_sglist( mod_timer(&timeout.timer, jiffies + msecs_to_jiffies(SIMPLE_IO_TIMEOUT)); usb_sg_wait(req); - if (!del_timer_sync(&timeout.timer)) + if (!timer_delete_sync(&timeout.timer)) retval = -ETIMEDOUT; else retval = req->status; @@ -2021,7 +2021,8 @@ static struct urb *iso_alloc_urb( for (i = 0; i < packets; i++) { /* here, only the last packet will be short */ - urb->iso_frame_desc[i].length = min((unsigned) bytes, maxp); + urb->iso_frame_desc[i].length = min_t(unsigned int, + bytes, maxp); bytes -= urb->iso_frame_desc[i].length; urb->iso_frame_desc[i].offset = maxp * i; diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 6aebc736a80c..70dff0db5354 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -441,7 +441,10 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer, if (count == 0) goto error; - mutex_lock(&dev->io_mutex); + retval = mutex_lock_interruptible(&dev->io_mutex); + if (retval < 0) + return -EINTR; + if (dev->disconnected) { /* already disconnected */ mutex_unlock(&dev->io_mutex); retval = -ENODEV; diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index afb71c18415d..c93b43f5bc46 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -823,7 +823,7 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf, ep = MON_OFF2HDR(rp, rp->b_out); if (rp->b_read < hdrbytes) { - step_len = min(nbytes, (size_t)(hdrbytes - rp->b_read)); + step_len = min_t(size_t, nbytes, hdrbytes - rp->b_read); ptr = ((char *)ep) + rp->b_read; if (step_len && copy_to_user(buf, ptr, step_len)) { mutex_unlock(&rp->fetch_lock); diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig index bf98fd36341d..d281da1cdbcc 100644 --- a/drivers/usb/mtu3/Kconfig +++ b/drivers/usb/mtu3/Kconfig @@ -21,7 +21,7 @@ config USB_MTU3 if USB_MTU3 choice - bool "MTU3 Mode Selection" + prompt "MTU3 Mode Selection" default USB_MTU3_DUAL_ROLE if (USB && USB_GADGET) default USB_MTU3_HOST if (USB && !USB_GADGET) default USB_MTU3_GADGET if (!USB && USB_GADGET) diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c index f0de99858353..c003049bafbf 100644 --- a/drivers/usb/mtu3/mtu3_debugfs.c +++ b/drivers/usb/mtu3/mtu3_debugfs.c @@ -7,6 +7,7 @@ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> */ +#include <linux/string_choices.h> #include <linux/uaccess.h> #include "mtu3.h" @@ -256,16 +257,7 @@ static const struct mtu3_file_map mtu3_ep_files[] = { static int mtu3_ep_open(struct inode *inode, struct file *file) { - const char *file_name = file_dentry(file)->d_iname; - const struct mtu3_file_map *f_map; - int i; - - for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) { - f_map = &mtu3_ep_files[i]; - - if (strcmp(f_map->name, file_name) == 0) - break; - } + const struct mtu3_file_map *f_map = debugfs_get_aux(file); return single_open(file, f_map->show, inode->i_private); } @@ -288,17 +280,8 @@ static const struct debugfs_reg32 mtu3_prb_regs[] = { static int mtu3_probe_show(struct seq_file *sf, void *unused) { - const char *file_name = file_dentry(sf->file)->d_iname; struct mtu3 *mtu = sf->private; - const struct debugfs_reg32 *regs; - int i; - - for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) { - regs = &mtu3_prb_regs[i]; - - if (strcmp(regs->name, file_name) == 0) - break; - } + const struct debugfs_reg32 *regs = debugfs_get_aux(sf->file); seq_printf(sf, "0x%04x - 0x%08x\n", (u32)regs->offset, mtu3_readl(mtu->ippc_base, (u32)regs->offset)); @@ -314,13 +297,11 @@ static int mtu3_probe_open(struct inode *inode, struct file *file) static ssize_t mtu3_probe_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) { - const char *file_name = file_dentry(file)->d_iname; struct seq_file *sf = file->private_data; struct mtu3 *mtu = sf->private; - const struct debugfs_reg32 *regs; + const struct debugfs_reg32 *regs = debugfs_get_aux(file); char buf[32]; u32 val; - int i; if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; @@ -328,12 +309,6 @@ static ssize_t mtu3_probe_write(struct file *file, const char __user *ubuf, if (kstrtou32(buf, 0, &val)) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) { - regs = &mtu3_prb_regs[i]; - - if (strcmp(regs->name, file_name) == 0) - break; - } mtu3_writel(mtu->ippc_base, (u32)regs->offset, val); return count; @@ -358,8 +333,8 @@ static void mtu3_debugfs_create_prb_files(struct mtu3 *mtu) for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) { regs = &mtu3_prb_regs[i]; - debugfs_create_file(regs->name, 0644, dir_prb, - mtu, &mtu3_probe_fops); + debugfs_create_file_aux(regs->name, 0644, dir_prb, + mtu, regs, &mtu3_probe_fops); } mtu3_debugfs_regset(mtu, mtu->ippc_base, mtu3_prb_regs, @@ -379,8 +354,8 @@ static void mtu3_debugfs_create_ep_dir(struct mtu3_ep *mep, for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) { files = &mtu3_ep_files[i]; - debugfs_create_file(files->name, 0444, dir_ep, - mep, &mtu3_ep_fops); + debugfs_create_file_aux(files->name, 0444, dir_ep, + mep, files, &mtu3_ep_fops); } } @@ -479,7 +454,7 @@ static int ssusb_vbus_show(struct seq_file *sf, void *unused) struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; seq_printf(sf, "vbus state: %s\n(echo on/off)\n", - regulator_is_enabled(otg_sx->vbus) ? "on" : "off"); + str_on_off(regulator_is_enabled(otg_sx->vbus))); return 0; } diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c index 8191b7ed3852..ffa5b9401dad 100644 --- a/drivers/usb/mtu3/mtu3_dr.c +++ b/drivers/usb/mtu3/mtu3_dr.c @@ -7,6 +7,7 @@ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> */ +#include <linux/string_choices.h> #include "mtu3.h" #include "mtu3_dr.h" #include "mtu3_debug.h" @@ -109,7 +110,7 @@ int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on) if (!vbus) return 0; - dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off"); + dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, str_on_off(is_on)); if (is_on) { ret = regulator_enable(vbus); diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index ad0eeac4332d..bf73fbc29976 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -7,6 +7,7 @@ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> */ +#include <linux/string_choices.h> #include "mtu3.h" #include "mtu3_trace.h" @@ -490,7 +491,7 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on) unsigned long flags; dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__, - is_on ? "on" : "off", mtu->is_active ? "" : "in"); + str_on_off(is_on), mtu->is_active ? "" : "in"); pm_runtime_get_sync(mtu->dev); diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 6858ed9fc3b2..7b5a431acb56 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -307,7 +307,7 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) if (otg_sx->role_sw_used || otg_sx->manual_drd_enabled) goto out; - if (of_property_read_bool(node, "extcon")) { + if (of_property_present(node, "extcon")) { otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0); if (IS_ERR(otg_sx->edev)) { return dev_err_probe(dev, PTR_ERR(otg_sx->edev), @@ -621,7 +621,7 @@ MODULE_DEVICE_TABLE(of, mtu3_of_match); static struct platform_driver mtu3_driver = { .probe = mtu3_probe, - .remove_new = mtu3_remove, + .remove = mtu3_remove, .driver = { .name = MTU3_DRIVER_NAME, .pm = DEV_PM_OPS, diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 9a8cf3de0617..9e45d12b81d3 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -29,7 +29,7 @@ config USB_MUSB_HDRC if USB_MUSB_HDRC choice - bool "MUSB Mode Selection" + prompt "MUSB Mode Selection" default USB_MUSB_DUAL_ROLE if (USB && USB_GADGET) default USB_MUSB_HOST if (USB && !USB_GADGET) default USB_MUSB_GADGET if (!USB && USB_GADGET) diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 953094c1930c..eebb24ab3ec8 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -21,6 +21,7 @@ #include <linux/of_platform.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> +#include <linux/string_choices.h> #include <linux/dma-mapping.h> #include <linux/usb/usb_phy_generic.h> @@ -203,7 +204,7 @@ static void __maybe_unused da8xx_musb_try_idle(struct musb *musb, unsigned long musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) { dev_dbg(musb->controller, "%s active, deleting timer\n", usb_otg_state_string(musb->xceiv->otg->state)); - del_timer(&musb->dev_timer); + timer_delete(&musb->dev_timer); last_timer = jiffies; return; } @@ -289,7 +290,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) MUSB_HST_MODE(musb); musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; portstate(musb->port1_status |= USB_PORT_STAT_POWER); - del_timer(&musb->dev_timer); + timer_delete(&musb->dev_timer); } else if (!(musb->int_usb & MUSB_INTR_BABBLE)) { /* * When babble condition happens, drvvbus interrupt @@ -306,7 +307,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) } dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", - drvvbus ? "on" : "off", + str_on_off(drvvbus), usb_otg_state_string(musb->xceiv->otg->state), err ? " ERROR" : "", devctl); @@ -418,7 +419,7 @@ static int da8xx_musb_exit(struct musb *musb) { struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent); - del_timer_sync(&musb->dev_timer); + timer_delete_sync(&musb->dev_timer); phy_power_off(glue->phy); phy_exit(glue->phy); @@ -633,7 +634,7 @@ MODULE_DEVICE_TABLE(of, da8xx_id_table); static struct platform_driver da8xx_driver = { .probe = da8xx_probe, - .remove_new = da8xx_remove, + .remove = da8xx_remove, .driver = { .name = "musb-da8xx", .pm = &da8xx_pm_ops, diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index b38df9226278..df56c972986f 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -59,7 +59,7 @@ static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci) return IRQ_NONE; } -static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = { +static const struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, }, @@ -205,7 +205,7 @@ static const struct musb_hdrc_platform_data jz4740_musb_pdata = { .platform_ops = &jz4740_musb_ops, }; -static struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = { +static const struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -325,7 +325,7 @@ MODULE_DEVICE_TABLE(of, jz4740_musb_of_match); static struct platform_driver jz4740_driver = { .probe = jz4740_probe, - .remove_new = jz4740_remove, + .remove = jz4740_remove, .driver = { .name = "musb-jz4740", .of_match_table = jz4740_musb_of_match, diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c index 63c86c046b98..c6cbe718b1da 100644 --- a/drivers/usb/musb/mediatek.c +++ b/drivers/usb/musb/mediatek.c @@ -365,7 +365,7 @@ static const struct musb_platform_ops mtk_musb_ops = { #define MTK_MUSB_MAX_EP_NUM 8 #define MTK_MUSB_RAM_BITS 11 -static struct musb_fifo_cfg mtk_musb_mode_cfg[] = { +static const struct musb_fifo_cfg mtk_musb_mode_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -523,7 +523,7 @@ MODULE_DEVICE_TABLE(of, mtk_musb_match); static struct platform_driver mtk_musb_driver = { .probe = mtk_musb_probe, - .remove_new = mtk_musb_remove, + .remove = mtk_musb_remove, .driver = { .name = "musb-mtk", .of_match_table = of_match_ptr(mtk_musb_match), diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c index 00e13214aa76..020348a98514 100644 --- a/drivers/usb/musb/mpfs.c +++ b/drivers/usb/musb/mpfs.c @@ -29,7 +29,7 @@ struct mpfs_glue { struct clk *clk; }; -static struct musb_fifo_cfg mpfs_musb_mode_cfg[] = { +static const struct musb_fifo_cfg mpfs_musb_mode_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -165,7 +165,7 @@ static void __maybe_unused mpfs_musb_try_idle(struct musb *musb, unsigned long t musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) { dev_dbg(musb->controller, "%s active, deleting timer\n", usb_otg_state_string(musb->xceiv->otg->state)); - del_timer(&musb->dev_timer); + timer_delete(&musb->dev_timer); last_timer = jiffies; return; } @@ -232,7 +232,7 @@ static int mpfs_musb_init(struct musb *musb) static int mpfs_musb_exit(struct musb *musb) { - del_timer_sync(&musb->dev_timer); + timer_delete_sync(&musb->dev_timer); return 0; } @@ -369,7 +369,7 @@ MODULE_DEVICE_TABLE(of, mpfs_id_table); static struct platform_driver mpfs_musb_driver = { .probe = mpfs_probe, - .remove_new = mpfs_remove, + .remove = mpfs_remove, .driver = { .name = "mpfs-musb", .of_match_table = of_match_ptr(mpfs_id_table) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b24adb5b399f..cbbb27178024 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -72,6 +72,7 @@ #include <linux/kobject.h> #include <linux/prefetch.h> #include <linux/platform_device.h> +#include <linux/string_choices.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/dma-mapping.h> @@ -920,7 +921,7 @@ b_host: musb_set_state(musb, OTG_STATE_B_HOST); if (musb->hcd) musb->hcd->self.is_b_host = 1; - del_timer(&musb->otg_timer); + timer_delete(&musb->otg_timer); break; default: if ((devctl & MUSB_DEVCTL_VBUS) @@ -1014,7 +1015,7 @@ static void musb_handle_intr_reset(struct musb *musb) + msecs_to_jiffies(TA_WAIT_BCON(musb))); break; case OTG_STATE_A_PERIPHERAL: - del_timer(&musb->otg_timer); + timer_delete(&musb->otg_timer); musb_g_reset(musb); break; case OTG_STATE_B_WAIT_ACON: @@ -1270,7 +1271,7 @@ MODULE_PARM_DESC(fifo_mode, "initial endpoint configuration"); */ /* mode 0 - fits in 2KB */ -static struct musb_fifo_cfg mode_0_cfg[] = { +static const struct musb_fifo_cfg mode_0_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, }, @@ -1279,7 +1280,7 @@ static struct musb_fifo_cfg mode_0_cfg[] = { }; /* mode 1 - fits in 4KB */ -static struct musb_fifo_cfg mode_1_cfg[] = { +static const struct musb_fifo_cfg mode_1_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, .mode = BUF_DOUBLE, }, @@ -1288,7 +1289,7 @@ static struct musb_fifo_cfg mode_1_cfg[] = { }; /* mode 2 - fits in 4KB */ -static struct musb_fifo_cfg mode_2_cfg[] = { +static const struct musb_fifo_cfg mode_2_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1298,7 +1299,7 @@ static struct musb_fifo_cfg mode_2_cfg[] = { }; /* mode 3 - fits in 4KB */ -static struct musb_fifo_cfg mode_3_cfg[] = { +static const struct musb_fifo_cfg mode_3_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1308,7 +1309,7 @@ static struct musb_fifo_cfg mode_3_cfg[] = { }; /* mode 4 - fits in 16KB */ -static struct musb_fifo_cfg mode_4_cfg[] = { +static const struct musb_fifo_cfg mode_4_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1339,7 +1340,7 @@ static struct musb_fifo_cfg mode_4_cfg[] = { }; /* mode 5 - fits in 8KB */ -static struct musb_fifo_cfg mode_5_cfg[] = { +static const struct musb_fifo_cfg mode_5_cfg[] = { { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, @@ -1387,7 +1388,7 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, /* expect hw_ep has already been zero-initialized */ - size = ffs(max(maxpacket, (u16) 8)) - 1; + size = ffs(max_t(u16, maxpacket, 8)) - 1; maxpacket = 1 << size; c_size = size - 3; @@ -1446,7 +1447,7 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep, return offset + (maxpacket << ((c_size & MUSB_FIFOSZ_DPB) ? 1 : 0)); } -static struct musb_fifo_cfg ep0_cfg = { +static const struct musb_fifo_cfg ep0_cfg = { .style = FIFO_RXTX, .maxpacket = 64, }; @@ -1937,7 +1938,7 @@ vbus_show(struct device *dev, struct device_attribute *attr, char *buf) pm_runtime_put_sync(dev); return sprintf(buf, "Vbus %s, timeout %lu msec\n", - vbus ? "on" : "off", val); + str_on_off(vbus), val); } static DEVICE_ATTR_RW(vbus); @@ -2953,7 +2954,7 @@ static struct platform_driver musb_driver = { .dev_groups = musb_groups, }, .probe = musb_probe, - .remove_new = musb_remove, + .remove = musb_remove, }; module_platform_driver(musb_driver); diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 9589243e8951..4cde3abb7006 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -760,8 +760,8 @@ cppi41_dma_controller_create(struct musb *musb, void __iomem *base) if (!controller) goto kzalloc_fail; - hrtimer_init(&controller->early_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - controller->early_tx.function = cppi41_recheck_tx_req; + hrtimer_setup(&controller->early_tx, cppi41_recheck_tx_req, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); INIT_LIST_HEAD(&controller->early_tx_list); controller->controller.channel_alloc = cppi41_dma_channel_allocate; diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 9c7a8bbc0542..e5e813f97fac 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -24,6 +24,7 @@ #include <linux/usb/usb_phy_generic.h> #include <linux/platform_data/usb-omap.h> #include <linux/sizes.h> +#include <linux/string_choices.h> #include <linux/of.h> #include <linux/of_address.h> @@ -200,7 +201,7 @@ static void dsps_musb_disable(struct musb *musb) musb_writel(reg_base, wrp->coreintr_clear, wrp->usb_bitmap); musb_writel(reg_base, wrp->epintr_clear, wrp->txep_bitmap | wrp->rxep_bitmap); - del_timer_sync(&musb->dev_timer); + timer_delete_sync(&musb->dev_timer); } /* Caller must take musb->lock */ @@ -214,7 +215,7 @@ static int dsps_check_status(struct musb *musb, void *unused) int skip_session = 0; if (glue->vbus_irq) - del_timer(&musb->dev_timer); + timer_delete(&musb->dev_timer); /* * We poll because DSPS IP's won't expose several OTG-critical @@ -378,7 +379,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci) /* NOTE: this must complete power-on within 100 ms. */ dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", - drvvbus ? "on" : "off", + str_on_off(drvvbus), usb_otg_state_string(musb->xceiv->otg->state), err ? " ERROR" : "", devctl); @@ -498,7 +499,7 @@ static int dsps_musb_exit(struct musb *musb) struct device *dev = musb->controller; struct dsps_glue *glue = dev_get_drvdata(dev->parent); - del_timer_sync(&musb->dev_timer); + timer_delete_sync(&musb->dev_timer); phy_power_off(musb->phy); phy_exit(musb->phy); debugfs_remove_recursive(glue->dbgfs_root); @@ -982,7 +983,7 @@ static int dsps_suspend(struct device *dev) return ret; } - del_timer_sync(&musb->dev_timer); + timer_delete_sync(&musb->dev_timer); mbase = musb->ctrl_base; glue->context.control = musb_readl(mbase, wrp->control); @@ -1032,7 +1033,7 @@ static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume); static struct platform_driver dsps_usbss_driver = { .probe = dsps_probe, - .remove_new = dsps_remove, + .remove = dsps_remove, .driver = { .name = "musb-dsps", .pm = &dsps_pm_ops, diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index bdf13911a1e5..6869c58367f2 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/smp.h> #include <linux/spinlock.h> +#include <linux/string_choices.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/slab.h> @@ -1161,12 +1162,19 @@ void musb_free_request(struct usb_ep *ep, struct usb_request *req) */ void musb_ep_restart(struct musb *musb, struct musb_request *req) { + u16 csr; + void __iomem *epio = req->ep->hw_ep->regs; + trace_musb_req_start(req); musb_ep_select(musb->mregs, req->epnum); - if (req->tx) + if (req->tx) { txstate(musb, req); - else - rxstate(musb, req); + } else { + csr = musb_readw(epio, MUSB_RXCSR); + csr |= MUSB_RXCSR_FLUSHFIFO | MUSB_RXCSR_P_WZC_BITS; + musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, csr); + } } static int musb_ep_restart_resume_work(struct musb *musb, void *data) @@ -1599,7 +1607,7 @@ static void musb_pullup(struct musb *musb, int is_on) /* FIXME if on, HdrcStart; if off, HdrcStop */ musb_dbg(musb, "gadget D+ pullup %s", - is_on ? "on" : "off"); + str_on_off(is_on)); musb_writeb(musb->mregs, MUSB_POWER, power); } diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 6d7336727388..f0786f8fbb25 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -533,7 +533,7 @@ static void ep0_txstate(struct musb *musb) /* load the data */ fifo_src = (u8 *) request->buf + request->actual; - fifo_count = min((unsigned) MUSB_EP0_FIFOSIZE, + fifo_count = min_t(unsigned, MUSB_EP0_FIFOSIZE, request->length - request->actual); musb_write_fifo(&musb->endpoints[0], fifo_count, fifo_src); request->actual += fifo_count; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index bc4507781167..6b4481a867c5 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -13,6 +13,7 @@ #include <linux/delay.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/errno.h> #include <linux/list.h> #include <linux/dma-mapping.h> @@ -798,10 +799,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum, } if (can_bulk_split(musb, qh->type)) - load_count = min((u32) hw_ep->max_packet_sz_tx, - len); + load_count = min_t(u32, hw_ep->max_packet_sz_tx, len); else - load_count = min((u32) packet_sz, len); + load_count = min_t(u32, packet_sz, len); if (dma_channel && musb_tx_dma_program(dma_controller, hw_ep, qh, urb, offset, len)) @@ -1029,7 +1029,7 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) + urb->actual_length); musb_dbg(musb, "Sending %d byte%s to ep0 fifo %p", fifo_count, - (fifo_count == 1) ? "" : "s", + str_plural(fifo_count), fifo_dest); musb_write_fifo(hw_ep, fifo_count, fifo_dest); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index b4a4c1df4e0d..2970967a4fd2 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -608,7 +608,7 @@ MODULE_DEVICE_TABLE(of, omap2430_id_table); static struct platform_driver omap2430_driver = { .probe = omap2430_probe, - .remove_new = omap2430_remove, + .remove = omap2430_remove, .driver = { .name = "musb-omap2430", .pm = DEV_PM_OPS, diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index 05b6e7e52e02..a6bd3e968cc7 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -629,7 +629,7 @@ static const struct musb_platform_ops sunxi_musb_ops = { #define SUNXI_MUSB_RAM_BITS 11 /* Allwinner OTG supports up to 5 endpoints */ -static struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = { +static const struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = { MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512), MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512), MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512), @@ -643,7 +643,7 @@ static struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = { }; /* H3/V3s OTG supports only 4 endpoints */ -static struct musb_fifo_cfg sunxi_musb_mode_cfg_4eps[] = { +static const struct musb_fifo_cfg sunxi_musb_mode_cfg_4eps[] = { MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512), MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512), MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512), @@ -857,7 +857,7 @@ MODULE_DEVICE_TABLE(of, sunxi_musb_match); static struct platform_driver sunxi_musb_driver = { .probe = sunxi_musb_probe, - .remove_new = sunxi_musb_remove, + .remove = sunxi_musb_remove, .driver = { .name = "musb-sunxi", .of_match_table = sunxi_musb_match, diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 461587629bf2..abd2472da7f7 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -525,7 +525,7 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) && (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON))) { dev_dbg(musb->controller, "%s active, deleting timer\n", usb_otg_state_string(musb->xceiv->otg->state)); - del_timer(&musb->dev_timer); + timer_delete(&musb->dev_timer); last_timer = jiffies; return; } @@ -875,7 +875,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) } if (int_src & TUSB_INT_SRC_USB_IP_CONN) - del_timer(&musb->dev_timer); + timer_delete(&musb->dev_timer); /* OTG state change reports (annoyingly) not issued by Mentor core */ if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG @@ -984,7 +984,7 @@ static void tusb_musb_disable(struct musb *musb) musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff); musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff); - del_timer(&musb->dev_timer); + timer_delete(&musb->dev_timer); if (is_dma_capable() && !dma_off) { printk(KERN_WARNING "%s %s: dma still active\n", @@ -1174,7 +1174,7 @@ static int tusb_musb_exit(struct musb *musb) { struct tusb6010_glue *glue = dev_get_drvdata(musb->controller->parent); - del_timer_sync(&musb->dev_timer); + timer_delete_sync(&musb->dev_timer); the_musb = NULL; gpiod_set_value(glue->enable, 0); @@ -1290,7 +1290,7 @@ static void tusb_remove(struct platform_device *pdev) static struct platform_driver tusb_driver = { .probe = tusb_probe, - .remove_new = tusb_remove, + .remove = tusb_remove, .driver = { .name = "musb-tusb", }, diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index c8d9d2a1d2f0..8c2a43d992f5 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -355,7 +355,7 @@ MODULE_DEVICE_TABLE(of, ux500_match); static struct platform_driver ux500_driver = { .probe = ux500_probe, - .remove_new = ux500_remove, + .remove = ux500_remove, .driver = { .name = "musb-ux500", .pm = &ux500_pm_ops, diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 408f47e39025..6a98aeeeae31 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -987,7 +987,7 @@ MODULE_DEVICE_TABLE(platform, ab8500_usb_devtype); static struct platform_driver ab8500_usb_driver = { .probe = ab8500_usb_probe, - .remove_new = ab8500_usb_remove, + .remove = ab8500_usb_remove, .id_table = ab8500_usb_devtype, .driver = { .name = "abx5x0-usb", diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 6db88e00f127..ca9353dad71a 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -133,7 +133,7 @@ MODULE_DEVICE_TABLE(of, am335x_phy_ids); static struct platform_driver am335x_phy_driver = { .probe = am335x_phy_probe, - .remove_new = am335x_phy_remove, + .remove = am335x_phy_remove, .driver = { .name = "am335x-phy-driver", .pm = &am335x_pm_ops, diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index c5c6b818998e..40ac68e52cee 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -12,6 +12,7 @@ #include <linux/kernel.h> #include <linux/delay.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/proc_fs.h> #include <linux/errno.h> #include <linux/interrupt.h> @@ -529,7 +530,7 @@ int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) if (!otg->gadget || !otg->gadget->dev.parent) return -ENODEV; - VDBG("gadget %s\n", on ? "on" : "off"); + VDBG("gadget %s\n", str_on_off(on)); dev = otg->gadget->dev.parent; if (on) { @@ -1002,7 +1003,7 @@ static void fsl_otg_remove(struct platform_device *pdev) struct platform_driver fsl_otg_driver = { .probe = fsl_otg_probe, - .remove_new = fsl_otg_remove, + .remove = fsl_otg_remove, .driver = { .name = driver_name, }, diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index e7d50e0a1612..8423be59ec0f 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -212,7 +212,7 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop) if (of_property_read_u32(node, "clock-frequency", &clk_rate)) clk_rate = 0; - needs_clk = of_property_read_bool(node, "clocks"); + needs_clk = of_property_present(node, "clocks"); } nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); @@ -345,7 +345,7 @@ MODULE_DEVICE_TABLE(of, nop_xceiv_dt_ids); static struct platform_driver usb_phy_generic_driver = { .probe = usb_phy_generic_probe, - .remove_new = usb_phy_generic_remove, + .remove = usb_phy_generic_remove, .driver = { .name = "usb_phy_generic", .of_match_table = nop_xceiv_dt_ids, diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index 5428b2b67de1..ce09e789afd8 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -385,7 +385,7 @@ static struct platform_driver gpio_vbus_driver = { .of_match_table = gpio_vbus_of_match, }, .probe = gpio_vbus_probe, - .remove_new = gpio_vbus_remove, + .remove = gpio_vbus_remove, }; module_platform_driver(gpio_vbus_driver); diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index 993d7525a102..f9b5c411aee4 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", 0 }, + { "isp1301" }, { } }; MODULE_DEVICE_TABLE(i2c, isp1301_id); diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index bd9a98ad1b30..51155c5513d3 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -103,7 +103,7 @@ MODULE_DEVICE_TABLE(of, keystone_usbphy_ids); static struct platform_driver keystone_usbphy_driver = { .probe = keystone_usbphy_probe, - .remove_new = keystone_usbphy_remove, + .remove = keystone_usbphy_remove, .driver = { .name = "keystone-usbphy", .of_match_table = keystone_usbphy_ids, diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index df7c27474a75..638fba58420c 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -15,6 +15,7 @@ #include <linux/clk.h> #include <linux/workqueue.h> #include <linux/platform_device.h> +#include <linux/string_choices.h> #include <linux/usb.h> #include <linux/usb/ch9.h> @@ -109,7 +110,7 @@ static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id) timer = &mvotg->otg_ctrl.timer[id]; if (timer_pending(timer)) - del_timer(timer); + timer_delete(timer); return 0; } @@ -217,7 +218,7 @@ static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on) if (!otg->gadget) return; - dev_info(mvotg->phy.dev, "gadget %s\n", on ? "on" : "off"); + dev_info(mvotg->phy.dev, "gadget %s\n", str_on_off(on)); if (on) usb_gadget_vbus_connect(otg->gadget); @@ -867,7 +868,7 @@ static int mv_otg_resume(struct platform_device *pdev) static struct platform_driver mv_otg_driver = { .probe = mv_otg_probe, - .remove_new = mv_otg_remove, + .remove = mv_otg_remove, .driver = { .name = driver_name, .dev_groups = mv_otg_groups, diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index cc4156c1b148..7069dd3f4d0d 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -769,11 +769,9 @@ static int mxs_phy_probe(struct platform_device *pdev) return PTR_ERR(base); clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, - "can't get the clock, err=%ld", PTR_ERR(clk)); - return PTR_ERR(clk); - } + if (IS_ERR(clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk), + "can't get the clock\n"); mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL); if (!mxs_phy) @@ -952,7 +950,7 @@ static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend, static struct platform_driver mxs_phy_driver = { .probe = mxs_phy_probe, - .remove_new = mxs_phy_remove, + .remove = mxs_phy_remove, .driver = { .name = DRIVER_NAME, .of_match_table = mxs_phy_dt_ids, diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index 5cac31c6029b..88607d0edb01 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -18,6 +18,7 @@ #include <linux/extcon-provider.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/string_choices.h> #include <linux/usb/otg.h> #include <linux/mfd/retu.h> #include <linux/usb/gadget.h> @@ -63,7 +64,7 @@ static ssize_t vbus_show(struct device *device, struct device_attribute *attr, char *buf) { struct tahvo_usb *tu = dev_get_drvdata(device); - return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off"); + return sprintf(buf, "%s\n", str_on_off(tu->vbus_state)); } static DEVICE_ATTR_RO(vbus); @@ -424,7 +425,7 @@ static void tahvo_usb_remove(struct platform_device *pdev) static struct platform_driver tahvo_usb_driver = { .probe = tahvo_usb_probe, - .remove_new = tahvo_usb_remove, + .remove = tahvo_usb_remove, .driver = { .name = "tahvo-usb", .dev_groups = tahvo_groups, diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 4ea47e6f835b..bee222967f6b 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1495,7 +1495,7 @@ static void tegra_usb_phy_remove(struct platform_device *pdev) static struct platform_driver tegra_usb_phy_driver = { .probe = tegra_usb_phy_probe, - .remove_new = tegra_usb_phy_remove, + .remove = tegra_usb_phy_remove, .driver = { .name = "tegra-phy", .of_match_table = tegra_usb_phy_id_table, diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index da09cff55abc..49d79c1257f3 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -432,7 +432,7 @@ MODULE_DEVICE_TABLE(of, twl6030_usb_id_table); static struct platform_driver twl6030_usb_driver = { .probe = twl6030_usb_probe, - .remove_new = twl6030_usb_remove, + .remove = twl6030_usb_remove, .driver = { .name = "twl6030_usb", .of_match_table = of_match_ptr(twl6030_usb_id_table), diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c index e683a37e3a7a..4df63e67bb37 100644 --- a/drivers/usb/phy/phy-ulpi.c +++ b/drivers/usb/phy/phy-ulpi.c @@ -256,29 +256,6 @@ static void otg_ulpi_init(struct usb_phy *phy, struct usb_otg *otg, } struct usb_phy * -otg_ulpi_create(struct usb_phy_io_ops *ops, - unsigned int flags) -{ - struct usb_phy *phy; - struct usb_otg *otg; - - phy = kzalloc(sizeof(*phy), GFP_KERNEL); - if (!phy) - return NULL; - - otg = kzalloc(sizeof(*otg), GFP_KERNEL); - if (!otg) { - kfree(phy); - return NULL; - } - - otg_ulpi_init(phy, otg, ops, flags); - - return phy; -} -EXPORT_SYMBOL_GPL(otg_ulpi_create); - -struct usb_phy * devm_otg_ulpi_create(struct device *dev, struct usb_phy_io_ops *ops, unsigned int flags) diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 06f789097989..e1435bc59662 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -346,13 +346,6 @@ static void devm_usb_phy_release2(struct device *dev, void *_res) usb_put_phy(res->phy); } -static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) -{ - struct usb_phy **phy = res; - - return *phy == match_data; -} - static void usb_charger_init(struct usb_phy *usb_phy) { usb_phy->chg_type = UNKNOWN_TYPE; @@ -365,7 +358,7 @@ static int usb_add_extcon(struct usb_phy *x) { int ret; - if (of_property_read_bool(x->dev->of_node, "extcon")) { + if (of_property_present(x->dev->of_node, "extcon")) { x->edev = extcon_get_edev_by_phandle(x->dev, 0); if (IS_ERR(x->edev)) return PTR_ERR(x->edev); @@ -615,25 +608,6 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle); /** - * devm_usb_put_phy - release the USB PHY - * @dev: device that wants to release this phy - * @phy: the phy returned by devm_usb_get_phy() - * - * destroys the devres associated with this phy and invokes usb_put_phy - * to release the phy. - * - * For use by USB host and peripheral drivers. - */ -void devm_usb_put_phy(struct device *dev, struct usb_phy *phy) -{ - int r; - - r = devres_release(dev, devm_usb_phy_release, devm_usb_phy_match, phy); - dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n"); -} -EXPORT_SYMBOL_GPL(devm_usb_put_phy); - -/** * usb_put_phy - release the USB PHY * @x: the phy returned by usb_get_phy() * diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index edc43f169d49..4b35ef216125 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -312,8 +312,10 @@ static int usbhsc_clk_get(struct device *dev, struct usbhs_priv *priv) priv->clks[1] = of_clk_get(dev_of_node(dev), 1); if (PTR_ERR(priv->clks[1]) == -ENOENT) priv->clks[1] = NULL; - else if (IS_ERR(priv->clks[1])) + else if (IS_ERR(priv->clks[1])) { + clk_put(priv->clks[0]); return PTR_ERR(priv->clks[1]); + } return 0; } @@ -632,7 +634,7 @@ static int usbhs_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - if (of_property_read_bool(dev_of_node(dev), "extcon")) { + if (of_property_present(dev_of_node(dev), "extcon")) { priv->edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(priv->edev)) return PTR_ERR(priv->edev); @@ -779,6 +781,8 @@ static void usbhs_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "usb remove\n"); + flush_delayed_work(&priv->notify_hotplug_work); + /* power off */ if (!usbhs_get_dparam(priv, runtime_pwctrl)) usbhsc_power_ctrl(priv, 0); @@ -835,7 +839,7 @@ static struct platform_driver renesas_usbhs_driver = { .of_match_table = usbhs_of_match, }, .probe = usbhs_probe, - .remove_new = usbhs_remove, + .remove = usbhs_remove, }; module_platform_driver(renesas_usbhs_driver); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 105132ae87ac..e8e5723f5412 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -1094,7 +1094,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) goto usbhs_mod_gadget_probe_err_gpriv; } - gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED); + gpriv->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_UNDEFINED); dev_info(dev, "%stransceiver found\n", !IS_ERR(gpriv->transceiver) ? "" : "no "); diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index c58a12c147f4..30482d4cf826 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -387,8 +387,11 @@ usb_role_switch_register(struct device *parent, dev_set_name(&sw->dev, "%s-role-switch", desc->name ? desc->name : dev_name(parent)); + sw->registered = true; + ret = device_register(&sw->dev); if (ret) { + sw->registered = false; put_device(&sw->dev); return ERR_PTR(ret); } @@ -399,8 +402,6 @@ usb_role_switch_register(struct device *parent, dev_warn(&sw->dev, "failed to add component\n"); } - sw->registered = true; - /* TODO: Symlinks for the host port and the device controller. */ return sw; diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index e5c6c413a075..cd2e026dcb22 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -217,7 +217,7 @@ static struct platform_driver intel_xhci_usb_driver = { }, .id_table = intel_xhci_usb_table, .probe = intel_xhci_usb_probe, - .remove_new = intel_xhci_usb_remove, + .remove = intel_xhci_usb_remove, }; module_platform_driver(intel_xhci_usb_driver); diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index d200e2c29a8f..2fea1b1db4a2 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -136,12 +136,11 @@ static void free_dynids(struct usb_serial_driver *drv) { struct usb_dynid *dynid, *n; - spin_lock(&drv->dynids.lock); + guard(mutex)(&usb_dynids_lock); list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { list_del(&dynid->node); kfree(dynid); } - spin_unlock(&drv->dynids.lock); } const struct bus_type usb_serial_bus_type = { @@ -157,7 +156,6 @@ int usb_serial_bus_register(struct usb_serial_driver *driver) int retval; driver->driver.bus = &usb_serial_bus_type; - spin_lock_init(&driver->dynids.lock); INIT_LIST_HEAD(&driver->dynids.list); retval = driver_register(&driver->driver); diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index d10e4c4848a0..7cc36f84821f 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -63,6 +63,7 @@ #define CH341_REG_DIVISOR 0x13 #define CH341_REG_LCR 0x18 #define CH341_REG_LCR2 0x25 +#define CH341_REG_FLOW_CTL 0x27 #define CH341_NBREAK_BITS 0x01 @@ -77,6 +78,9 @@ #define CH341_LCR_CS6 0x01 #define CH341_LCR_CS5 0x00 +#define CH341_FLOW_CTL_NONE 0x00 +#define CH341_FLOW_CTL_RTSCTS 0x01 + #define CH341_QUIRK_LIMITED_PRESCALER BIT(0) #define CH341_QUIRK_SIMULATE_BREAK BIT(1) @@ -478,6 +482,28 @@ err_kill_interrupt_urb: return r; } +static void ch341_set_flow_control(struct tty_struct *tty, + struct usb_serial_port *port, + const struct ktermios *old_termios) +{ + u16 flow_ctl; + int r; + + if (C_CRTSCTS(tty)) + flow_ctl = CH341_FLOW_CTL_RTSCTS; + else + flow_ctl = CH341_FLOW_CTL_NONE; + + r = ch341_control_out(port->serial->dev, + CH341_REQ_WRITE_REG, + (CH341_REG_FLOW_CTL << 8) | CH341_REG_FLOW_CTL, + (flow_ctl << 8) | flow_ctl); + if (r < 0 && old_termios) { + tty->termios.c_cflag &= ~CRTSCTS; + tty->termios.c_cflag |= (old_termios->c_cflag & CRTSCTS); + } +} + /* Old_termios contains the original termios settings and * tty->termios contains the new setting to be used. */ @@ -546,6 +572,8 @@ static void ch341_set_termios(struct tty_struct *tty, spin_unlock_irqrestore(&priv->lock, flags); ch341_set_handshake(port->serial->dev, priv->mcr); + + ch341_set_flow_control(tty, port, old_termios); } /* @@ -632,13 +660,12 @@ restore: static int ch341_break_ctl(struct tty_struct *tty, int break_state) { - const uint16_t ch341_break_reg = - ((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK; + const u16 ch341_break_reg = (CH341_REG_LCR << 8) | CH341_REG_BREAK; struct usb_serial_port *port = tty->driver_data; struct ch341_private *priv = usb_get_serial_port_data(port); + u16 reg_contents; + u8 break_reg[2]; int r; - uint16_t reg_contents; - uint8_t break_reg[2]; if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK) return ch341_simulate_break(tty, break_state); diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index c24101f0a07a..9960ac2b10b7 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -223,6 +223,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */ { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */ { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */ + { USB_DEVICE(0x1B93, 0x1013) }, /* Phoenix Contact UPS Device */ { USB_DEVICE(0x1BA4, 0x0002) }, /* Silicon Labs 358x factory default */ { USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */ { USB_DEVICE(0x1D6F, 0x0010) }, /* Seluxit ApS RF Dongle */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index c6f17d732b95..9b34e23b7091 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1079,6 +1079,20 @@ static const struct usb_device_id id_table_combined[] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, /* GMC devices */ { USB_DEVICE(GMC_VID, GMC_Z216C_PID) }, + /* Altera USB Blaster 3 */ + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_6022_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_6025_PID, 2) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_6026_PID, 2) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_6026_PID, 3) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_6029_PID, 2) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602A_PID, 2) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602A_PID, 3) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602C_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602D_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602D_PID, 2) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 2) }, + { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 3) }, { } /* Terminating entry */ }; @@ -1443,9 +1457,11 @@ static void get_serial_info(struct tty_struct *tty, struct serial_struct *ss) struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); + mutex_lock(&priv->cfg_lock); ss->flags = priv->flags; ss->baud_base = priv->baud_base; ss->custom_divisor = priv->custom_divisor; + mutex_unlock(&priv->cfg_lock); } static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss) diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 5ee60ba2a73c..52be47d684ea 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -1612,3 +1612,16 @@ */ #define GMC_VID 0x1cd7 #define GMC_Z216C_PID 0x0217 /* GMC Z216C Adapter IR-USB */ + +/* + * Altera USB Blaster 3 (http://www.altera.com). + */ +#define ALTERA_VID 0x09fb +#define ALTERA_UB3_6022_PID 0x6022 +#define ALTERA_UB3_6025_PID 0x6025 +#define ALTERA_UB3_6026_PID 0x6026 +#define ALTERA_UB3_6029_PID 0x6029 +#define ALTERA_UB3_602A_PID 0x602a +#define ALTERA_UB3_602C_PID 0x602c +#define ALTERA_UB3_602D_PID 0x602d +#define ALTERA_UB3_602E_PID 0x602e diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 28c71d99e857..1fffda7647f9 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1129,7 +1129,7 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port, spin_lock_irqsave(&edge_port->ep_lock, flags); /* calculate number of bytes to put in fifo */ - copySize = min((unsigned int)count, + copySize = min_t(unsigned int, count, (edge_port->txCredits - fifo->count)); dev_dbg(&port->dev, "%s of %d byte(s) Fifo room %d -- will copy %d bytes\n", diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index ca3da79afd23..93710b762893 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -66,29 +66,16 @@ #define MOS_WDR_TIMEOUT 5000 /* default urb timeout */ -#define MOS_PORT1 0x0200 -#define MOS_PORT2 0x0300 -#define MOS_VENREG 0x0000 -#define MOS_MAX_PORT 0x02 -#define MOS_WRITE 0x0E -#define MOS_READ 0x0D - /* Requests */ #define MCS_RD_RTYPE 0xC0 #define MCS_WR_RTYPE 0x40 #define MCS_RDREQ 0x0D #define MCS_WRREQ 0x0E -#define MCS_CTRL_TIMEOUT 500 #define VENDOR_READ_LENGTH (0x01) -#define MAX_NAME_LEN 64 - #define ZLP_REG1 0x3A /* Zero_Flag_Reg1 58 */ #define ZLP_REG5 0x3E /* Zero_Flag_Reg5 62 */ -/* For higher baud Rates use TIOCEXBAUD */ -#define TIOCEXBAUD 0x5462 - /* * Vendor id and device id defines * diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 9ba5584061c8..5cd26dac2069 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -619,13 +619,6 @@ static void option_instat_callback(struct urb *urb); /* Luat Air72*U series based on UNISOC UIS8910 uses UNISOC's vendor ID */ #define LUAT_PRODUCT_AIR720U 0x4e00 -/* MeiG Smart Technology products */ -#define MEIGSMART_VENDOR_ID 0x2dee -/* MeiG Smart SRM825L based on Qualcomm 315 */ -#define MEIGSMART_PRODUCT_SRM825L 0x4d22 -/* MeiG Smart SLM320 based on UNISOC UIS8910 */ -#define MEIGSMART_PRODUCT_SLM320 0x4d41 - /* Device flags */ /* Highest interface number which can be used with NCTRL() and RSVD() */ @@ -1365,23 +1358,23 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(2) | RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1063, 0xff), /* Telit LN920 (ECM) */ .driver_info = NCTRL(0) | RSVD(1) }, - { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1070, 0xff), /* Telit FN990 (rmnet) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1070, 0xff), /* Telit FN990A (rmnet) */ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, - { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1071, 0xff), /* Telit FN990 (MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1071, 0xff), /* Telit FN990A (MBIM) */ .driver_info = NCTRL(0) | RSVD(1) }, - { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1072, 0xff), /* Telit FN990 (RNDIS) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1072, 0xff), /* Telit FN990A (RNDIS) */ .driver_info = NCTRL(2) | RSVD(3) }, - { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff), /* Telit FN990 (ECM) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1073, 0xff), /* Telit FN990A (ECM) */ .driver_info = NCTRL(0) | RSVD(1) }, - { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff), /* Telit FN990 (PCIe) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff), /* Telit FN990A (PCIe) */ .driver_info = RSVD(0) }, - { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1080, 0xff), /* Telit FE990 (rmnet) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1080, 0xff), /* Telit FE990A (rmnet) */ .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, - { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1081, 0xff), /* Telit FE990 (MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1081, 0xff), /* Telit FE990A (MBIM) */ .driver_info = NCTRL(0) | RSVD(1) }, - { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1082, 0xff), /* Telit FE990 (RNDIS) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1082, 0xff), /* Telit FE990A (RNDIS) */ .driver_info = NCTRL(2) | RSVD(3) }, - { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1083, 0xff), /* Telit FE990 (ECM) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1083, 0xff), /* Telit FE990A (ECM) */ .driver_info = NCTRL(0) | RSVD(1) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(3) }, @@ -1395,6 +1388,44 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10aa, 0xff), /* Telit FN920C04 (MBIM) */ .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x30), /* Telit FE990B (rmnet) */ + .driver_info = NCTRL(5) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b1, 0xff, 0xff, 0x30), /* Telit FE990B (MBIM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b1, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b1, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b2, 0xff, 0xff, 0x30), /* Telit FE990B (RNDIS) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b2, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b2, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b3, 0xff, 0xff, 0x30), /* Telit FE990B (ECM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b3, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b3, 0xff, 0xff, 0x60) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c0, 0xff), /* Telit FE910C04 (rmnet) */ + .driver_info = RSVD(0) | NCTRL(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c4, 0xff), /* Telit FE910C04 (rmnet) */ + .driver_info = RSVD(0) | NCTRL(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c8, 0xff), /* Telit FE910C04 (rmnet) */ + .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x30), /* Telit FN990B (rmnet) */ + .driver_info = NCTRL(5) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d1, 0xff, 0xff, 0x30), /* Telit FN990B (MBIM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d1, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d1, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d2, 0xff, 0xff, 0x30), /* Telit FN990B (RNDIS) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d2, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d2, 0xff, 0xff, 0x60) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d3, 0xff, 0xff, 0x30), /* Telit FN990B (ECM) */ + .driver_info = NCTRL(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d3, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d3, 0xff, 0xff, 0x60) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910), .driver_info = NCTRL(0) | RSVD(1) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM), @@ -2247,6 +2278,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(2) }, { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x7127, 0xff, 0x00, 0x00), .driver_info = NCTRL(2) | NCTRL(3) | NCTRL(4) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x7129, 0xff, 0x00, 0x00), /* MediaTek T7XX */ + .driver_info = NCTRL(2) | NCTRL(3) | NCTRL(4) }, { USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) }, { USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MPL200), .driver_info = RSVD(1) | RSVD(4) }, @@ -2337,6 +2370,14 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0a05, 0xff) }, /* Fibocom FM650-CN (NCM mode) */ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0a06, 0xff) }, /* Fibocom FM650-CN (RNDIS mode) */ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0a07, 0xff) }, /* Fibocom FM650-CN (MBIM mode) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d41, 0xff, 0, 0) }, /* MeiG Smart SLM320 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d57, 0xff, 0, 0) }, /* MeiG Smart SLM770A */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0, 0) }, /* MeiG Smart SRM815 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0x10, 0x02) }, /* MeiG Smart SLM828 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0x10, 0x03) }, /* MeiG Smart SLM828 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM815 and SRM825L */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825L */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d22, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825L */ { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */ @@ -2375,16 +2416,29 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0116, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Golbal EDU */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0116, 0xff, 0x00, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0116, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010a, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WRD for WWAN Ready */ + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010a, 0xff, 0x00, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010a, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010b, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for WWAN Ready */ + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010b, 0xff, 0x00, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010b, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010c, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WRD for WWAN Ready */ + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010c, 0xff, 0x00, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010c, 0xff, 0xff, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010d, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for WWAN Ready */ + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010d, 0xff, 0x00, 0x40) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x010d, 0xff, 0xff, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(OPPO_VENDOR_ID, OPPO_PRODUCT_R11, 0xff, 0xff, 0x30) }, { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x30) }, { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, TOZED_PRODUCT_LT70C, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, LUAT_PRODUCT_AIR720U, 0xff, 0, 0) }, - { USB_DEVICE_AND_INTERFACE_INFO(MEIGSMART_VENDOR_ID, MEIGSMART_PRODUCT_SLM320, 0xff, 0, 0) }, - { USB_DEVICE_AND_INTERFACE_INFO(MEIGSMART_VENDOR_ID, MEIGSMART_PRODUCT_SRM825L, 0xff, 0xff, 0x30) }, - { USB_DEVICE_AND_INTERFACE_INFO(MEIGSMART_VENDOR_ID, MEIGSMART_PRODUCT_SRM825L, 0xff, 0xff, 0x40) }, - { USB_DEVICE_AND_INTERFACE_INFO(MEIGSMART_VENDOR_ID, MEIGSMART_PRODUCT_SRM825L, 0xff, 0xff, 0x60) }, + { USB_DEVICE_INTERFACE_CLASS(0x1bbb, 0x0530, 0xff), /* TCL IK512 MBIM */ + .driver_info = NCTRL(1) }, + { USB_DEVICE_INTERFACE_CLASS(0x1bbb, 0x0640, 0xff), /* TCL IK512 ECM */ + .driver_info = NCTRL(3) }, + { USB_DEVICE_INTERFACE_CLASS(0x2949, 0x8700, 0xff) }, /* Neoway N723-EA */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ad41363e3cea..010688dd9e49 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -31,6 +31,7 @@ #define PL2303_QUIRK_UART_STATE_IDX0 BIT(0) #define PL2303_QUIRK_LEGACY BIT(1) #define PL2303_QUIRK_ENDPOINT_HACK BIT(2) +#define PL2303_QUIRK_NO_BREAK_GETLINE BIT(3) static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID), @@ -467,6 +468,25 @@ static int pl2303_detect_type(struct usb_serial *serial) return -ENODEV; } +static bool pl2303_is_hxd_clone(struct usb_serial *serial) +{ + struct usb_device *udev = serial->dev; + unsigned char *buf; + int ret; + + buf = kmalloc(7, GFP_KERNEL); + if (!buf) + return false; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, + 0, 0, buf, 7, 100); + + kfree(buf); + + return ret == -EPIPE; +} + static int pl2303_startup(struct usb_serial *serial) { struct pl2303_serial_private *spriv; @@ -489,6 +509,9 @@ static int pl2303_startup(struct usb_serial *serial) spriv->quirks = (unsigned long)usb_get_serial_data(serial); spriv->quirks |= spriv->type->quirks; + if (type == TYPE_HXD && pl2303_is_hxd_clone(serial)) + spriv->quirks |= PL2303_QUIRK_NO_BREAK_GETLINE; + usb_set_serial_data(serial, spriv); if (type != TYPE_HXN) { @@ -725,9 +748,18 @@ static void pl2303_encode_baud_rate(struct tty_struct *tty, static int pl2303_get_line_request(struct usb_serial_port *port, unsigned char buf[7]) { - struct usb_device *udev = port->serial->dev; + struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); + struct usb_device *udev = serial->dev; int ret; + if (spriv->quirks & PL2303_QUIRK_NO_BREAK_GETLINE) { + struct pl2303_private *priv = usb_get_serial_port_data(port); + + memcpy(buf, priv->line_settings, 7); + return 0; + } + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE, 0, 0, buf, 7, 100); @@ -1064,9 +1096,13 @@ static int pl2303_carrier_raised(struct usb_serial_port *port) static int pl2303_set_break(struct usb_serial_port *port, bool enable) { struct usb_serial *serial = port->serial; + struct pl2303_serial_private *spriv = usb_get_serial_data(serial); u16 state; int result; + if (spriv->quirks & PL2303_QUIRK_NO_BREAK_GETLINE) + return -ENOTTY; + if (enable) state = BREAK_ON; else diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index a317bdbd00ad..72fe83a6c978 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -503,7 +503,7 @@ static void qt2_process_read_urb(struct urb *urb) newport = *(ch + 3); - if (newport > serial->num_ports) { + if (newport >= serial->num_ports) { dev_err(&port->dev, "%s - port change to invalid port: %i\n", __func__, newport); diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 64a2e0bb5723..741e68e46139 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -421,7 +421,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, unsigned long flags; unsigned char *buffer; struct urb *urb; - size_t writesize = min((size_t)count, (size_t)MAX_TRANSFER); + size_t writesize = min_t(size_t, count, MAX_TRANSFER); int retval = 0; /* verify that we actually have some data to write */ diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index df6a2ae0bf42..7266558d823a 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -706,14 +706,12 @@ static const struct usb_device_id *match_dynamic_id(struct usb_interface *intf, { struct usb_dynid *dynid; - spin_lock(&drv->dynids.lock); + guard(mutex)(&usb_dynids_lock); list_for_each_entry(dynid, &drv->dynids.list, node) { if (usb_match_one_id(intf, &dynid->id)) { - spin_unlock(&drv->dynids.lock); return &dynid->id; } } - spin_unlock(&drv->dynids.lock); return NULL; } diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index d17b60a644ef..4be1d617d63d 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -3,8 +3,7 @@ # USB Storage driver configuration # -comment "NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may" -comment "also be needed; see USB_STORAGE Help for more info" +comment "NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; see USB_STORAGE Help for more info" config USB_STORAGE tristate "USB Mass Storage support" diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index 46635fa4a340..28db337f190b 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -8,7 +8,7 @@ ccflags-y := -I $(srctree)/drivers/scsi -ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=USB_STORAGE +ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"USB_STORAGE"' obj-$(CONFIG_USB_UAS) += uas.o obj-$(CONFIG_USB_STORAGE) += usb-storage.o diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index a9d3c58ce7d9..e01f3a42bde4 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -36,7 +36,7 @@ MODULE_DESCRIPTION("Driver for Alauda-based card readers"); MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); /* * Status bytes @@ -174,7 +174,7 @@ struct alauda_card_info { unsigned char zoneshift; /* 1<<zs blocks per zone */ }; -static struct alauda_card_info alauda_card_ids[] = { +static const struct alauda_card_info alauda_card_ids[] = { /* NAND flash */ { 0x6e, 20, 8, 4, 8}, /* 1 MB */ { 0xe8, 20, 8, 4, 8}, /* 1 MB */ @@ -200,7 +200,7 @@ static struct alauda_card_info alauda_card_ids[] = { { 0,} }; -static struct alauda_card_info *alauda_card_find_id(unsigned char id) +static const struct alauda_card_info *alauda_card_find_id(unsigned char id) { int i; @@ -383,7 +383,7 @@ static int alauda_init_media(struct us_data *us) { unsigned char *data = us->iobuf; int ready = 0; - struct alauda_card_info *media_info; + const struct alauda_card_info *media_info; unsigned int num_zones; while (ready == 0) { @@ -1132,7 +1132,7 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) int rc; struct alauda_info *info = (struct alauda_info *) us->extra; unsigned char *ptr = us->iobuf; - static unsigned char inquiry_response[36] = { + static const unsigned char inquiry_response[36] = { 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 }; diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c index 30dfd0082474..2fce5f95be51 100644 --- a/drivers/usb/storage/cypress_atacb.c +++ b/drivers/usb/storage/cypress_atacb.c @@ -22,7 +22,7 @@ MODULE_DESCRIPTION("SAT support for Cypress USB/ATA bridges with ATACB"); MODULE_AUTHOR("Matthieu Castet <castet.matthieu@free.fr>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); /* * The table of devices diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 3ea5601d16b8..9ba369483c9b 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -54,7 +54,7 @@ MODULE_DESCRIPTION("Driver for Datafab USB Compact Flash reader"); MODULE_AUTHOR("Jimmie Mayfield <mayfield+datafab@sackheads.org>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); struct datafab_info { unsigned long sectors; /* total sector count */ @@ -319,7 +319,7 @@ static int datafab_determine_lun(struct us_data *us, // // There might be a better way of doing this? - static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; + static const unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; unsigned char *command = us->iobuf; unsigned char *buf; int count = 0, rc; @@ -384,7 +384,7 @@ static int datafab_id_device(struct us_data *us, // to the ATA spec, 'Sector Count' isn't used but the Windows driver // sets this bit so we do too... // - static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; + static const unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 }; unsigned char *command = us->iobuf; unsigned char *reply; int rc; @@ -437,16 +437,16 @@ static int datafab_handle_mode_sense(struct us_data *us, struct scsi_cmnd * srb, int sense_6) { - static unsigned char rw_err_page[12] = { + static const unsigned char rw_err_page[12] = { 0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0 }; - static unsigned char cache_page[12] = { + static const unsigned char cache_page[12] = { 0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - static unsigned char rbac_page[12] = { + static const unsigned char rbac_page[12] = { 0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0 }; - static unsigned char timer_page[8] = { + static const unsigned char timer_page[8] = { 0x1C, 0x6, 0, 0, 0, 0 }; unsigned char pc, page_code; @@ -550,7 +550,7 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) int rc; unsigned long block, blocks; unsigned char *ptr = us->iobuf; - static unsigned char inquiry_reply[8] = { + static const unsigned char inquiry_reply[8] = { 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 }; diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index 576be66ad962..dda610f689b7 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -58,8 +58,8 @@ void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb) case INQUIRY: what = "INQUIRY"; break; case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; case MODE_SELECT: what = "MODE_SELECT"; break; - case RESERVE: what = "RESERVE"; break; - case RELEASE: what = "RELEASE"; break; + case RESERVE_6: what = "RESERVE"; break; + case RELEASE_6: what = "RELEASE"; break; case COPY: what = "COPY"; break; case ERASE: what = "ERASE"; break; case MODE_SENSE: what = "MODE_SENSE"; break; diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index a4bfbecbf16c..ce91fb105975 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -26,7 +26,7 @@ MODULE_DESCRIPTION("Driver for ENE UB6250 reader"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); MODULE_FIRMWARE(SD_INIT1_FIRMWARE); MODULE_FIRMWARE(SD_INIT2_FIRMWARE); MODULE_FIRMWARE(SD_RW_FIRMWARE); @@ -737,7 +737,7 @@ static int sd_scsi_write(struct us_data *us, struct scsi_cmnd *srb) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = blenByte; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xF0; bcb->CDB[5] = (unsigned char)(bnByte); bcb->CDB[4] = (unsigned char)(bnByte>>8); @@ -1163,7 +1163,7 @@ static int ms_read_copyblock(struct us_data *us, u16 oldphy, u16 newphy, memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = 0x200*len; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xF0; bcb->CDB[1] = 0x08; bcb->CDB[4] = (unsigned char)(oldphy); @@ -1759,7 +1759,7 @@ static int ms_scsi_write(struct us_data *us, struct scsi_cmnd *srb) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = blenByte; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xF0; bcb->CDB[1] = 0x04; bcb->CDB[5] = (unsigned char)(bn); @@ -1931,7 +1931,7 @@ static int ene_load_bincode(struct us_data *us, unsigned char flag) memset(bcb, 0, sizeof(struct bulk_cb_wrap)); bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = sd_fw->size; - bcb->Flags = 0x00; + bcb->Flags = US_BULK_FLAG_OUT; bcb->CDB[0] = 0xEF; result = ene_send_scsi_cmd(us, FDIR_WRITE, buf, 0); diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index cab27ba7a32a..a075620907b4 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -29,7 +29,7 @@ MODULE_DESCRIPTION("Driver for Freecom USB/IDE adaptor"); MODULE_AUTHOR("David Brown <usb-storage@davidb.org>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); #ifdef CONFIG_USB_STORAGE_DEBUG static void pdump(struct us_data *us, void *ibuffer, int length); diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index f8f9ce8dc710..b243bd5521a6 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -54,7 +54,7 @@ int usb_stor_ucr61s2b_init(struct us_data *us) struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap*) us->iobuf; int res; unsigned int partial; - static char init_string[] = "\xec\x0a\x06\x00$PCCHIPS"; + static const char init_string[] = "\xec\x0a\x06\x00$PCCHIPS"; usb_stor_dbg(us, "Sending UCR-61S2B initialization packet...\n"); diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index f2254eb3c0d7..a1669c35bad5 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -53,7 +53,7 @@ MODULE_DESCRIPTION("Driver for In-System Design, Inc. ISD200 ASIC"); MODULE_AUTHOR("Björn Stenberg <bjorn@haxx.se>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); static int isd200_Initialization(struct us_data *us); diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 0e71a8f33c2b..089c6f8ac85f 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -51,7 +51,7 @@ MODULE_DESCRIPTION("Driver for Lexar \"Jumpshot\" Compact Flash reader"); MODULE_AUTHOR("Jimmie Mayfield <mayfield+usb@sackheads.org>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); /* * The table of devices @@ -367,16 +367,16 @@ static int jumpshot_handle_mode_sense(struct us_data *us, struct scsi_cmnd * srb, int sense_6) { - static unsigned char rw_err_page[12] = { + static const unsigned char rw_err_page[12] = { 0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0 }; - static unsigned char cache_page[12] = { + static const unsigned char cache_page[12] = { 0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - static unsigned char rbac_page[12] = { + static const unsigned char rbac_page[12] = { 0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0 }; - static unsigned char timer_page[8] = { + static const unsigned char timer_page[8] = { 0x1C, 0x6, 0, 0, 0, 0 }; unsigned char pc, page_code; @@ -477,7 +477,7 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) int rc; unsigned long block, blocks; unsigned char *ptr = us->iobuf; - static unsigned char inquiry_response[8] = { + static const unsigned char inquiry_response[8] = { 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 }; diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c index d6a5e54f2ca8..341d6839548a 100644 --- a/drivers/usb/storage/karma.c +++ b/drivers/usb/storage/karma.c @@ -23,7 +23,7 @@ MODULE_DESCRIPTION("Driver for Rio Karma"); MODULE_AUTHOR("Bob Copeland <me@bobcopeland.com>, Keith Bennett <keith@mcs.st-and.ac.uk>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); #define RIO_PREFIX "RIOP\x00" #define RIO_PREFIX_LEN 5 diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index f97cf6cadb8e..5a8a1ffda0ec 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -25,7 +25,7 @@ MODULE_DESCRIPTION("Maxtor USB OneTouch hard drive button driver"); MODULE_AUTHOR("Nick Sillik <n.sillik@temple.edu>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); #define ONETOUCH_PKT_LEN 0x02 #define ONETOUCH_BUTTON KEY_PROG1 diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 0c423916d7bf..b387863c245f 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -35,7 +35,7 @@ MODULE_DESCRIPTION("Driver for Realtek USB Card Reader"); MODULE_AUTHOR("wwang <wei_wang@realsil.com.cn>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); static int auto_delink_en = 1; module_param(auto_delink_en, int, S_IRUGO | S_IWUSR); @@ -191,7 +191,7 @@ MODULE_DEVICE_TABLE(usb, realtek_cr_ids); .initFunction = init_function, \ } -static struct us_unusual_dev realtek_cr_unusual_dev_list[] = { +static const struct us_unusual_dev realtek_cr_unusual_dev_list[] = { # include "unusual_realtek.h" {} /* Terminating entry */ }; @@ -212,7 +212,7 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun, /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(buf_len); - bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0; + bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT; bcb->Tag = ++us->tag; bcb->Lun = lun; bcb->Length = cmd_len; @@ -301,7 +301,7 @@ static int rts51x_bulk_transport_special(struct us_data *us, u8 lun, /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(buf_len); - bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : 0; + bcb->Flags = (dir == DMA_FROM_DEVICE) ? US_BULK_FLAG_IN : US_BULK_FLAG_OUT; bcb->Tag = ++us->tag; bcb->Lun = lun; bcb->Length = cmd_len; @@ -797,10 +797,10 @@ static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) { struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra); static int card_first_show = 1; - static u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0, + static const u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0x3A, 0, 0, 0, 0, 0 }; - static u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0, + static const u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0x24, 0, 0, 0, 0, 0 }; int ret; @@ -934,7 +934,7 @@ static void realtek_cr_destructor(void *extra) #ifdef CONFIG_REALTEK_AUTOPM if (ss_en) { - del_timer(&chip->rts51x_suspend_timer); + timer_delete(&chip->rts51x_suspend_timer); chip->timer_expires = 0; } #endif diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 8c8b5e6041cc..d2f476e48d0c 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -64,7 +64,7 @@ static const char* host_info(struct Scsi_Host *host) return us->scsi_name; } -static int slave_alloc (struct scsi_device *sdev) +static int sdev_init (struct scsi_device *sdev) { struct us_data *us = host_to_us(sdev->host); @@ -88,7 +88,7 @@ static int slave_alloc (struct scsi_device *sdev) return 0; } -static int device_configure(struct scsi_device *sdev, struct queue_limits *lim) +static int sdev_configure(struct scsi_device *sdev, struct queue_limits *lim) { struct us_data *us = host_to_us(sdev->host); struct device *dev = us->pusb_dev->bus->sysdev; @@ -127,7 +127,7 @@ static int device_configure(struct scsi_device *sdev, struct queue_limits *lim) lim->max_hw_sectors, dma_max_mapping_size(dev) >> SECTOR_SHIFT); /* - * We can't put these settings in slave_alloc() because that gets + * We can't put these settings in sdev_init() because that gets * called before the device type is known. Consequently these * settings can't be overridden via the scsi devinfo mechanism. */ @@ -592,12 +592,9 @@ static ssize_t max_sectors_store(struct device *dev, struct device_attribute *at if (sscanf(buf, "%hu", &ms) <= 0) return -EINVAL; - blk_mq_freeze_queue(sdev->request_queue); lim = queue_limits_start_update(sdev->request_queue); lim.max_hw_sectors = ms; - ret = queue_limits_commit_update(sdev->request_queue, &lim); - blk_mq_unfreeze_queue(sdev->request_queue); - + ret = queue_limits_commit_update_frozen(sdev->request_queue, &lim); if (ret) return ret; return count; @@ -637,8 +634,8 @@ static const struct scsi_host_template usb_stor_host_template = { /* unknown initiator id */ .this_id = -1, - .slave_alloc = slave_alloc, - .device_configure = device_configure, + .sdev_init = sdev_init, + .sdev_configure = sdev_configure, .target_alloc = target_alloc, /* lots of sg segments can be handled */ diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 03d1b9c69ea1..e66b920e99e2 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -47,7 +47,7 @@ MODULE_DESCRIPTION("Driver for SanDisk SDDR-09 SmartMedia reader"); MODULE_AUTHOR("Andries Brouwer <aeb@cwi.nl>, Robert Baruch <autophile@starband.net>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); static int usb_stor_sddr09_dpcm_init(struct us_data *us); static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us); @@ -144,7 +144,7 @@ static inline char *nand_flash_manufacturer(int manuf_id) { * 256 MB NAND flash has a 5-byte ID with 2nd byte 0xaa, 0xba, 0xca or 0xda. */ -static struct nand_flash_dev nand_flash_ids[] = { +static const struct nand_flash_dev nand_flash_ids[] = { /* NAND flash */ { 0x6e, 20, 8, 4, 8, 2}, /* 1 MB */ { 0xe8, 20, 8, 4, 8, 2}, /* 1 MB */ @@ -169,7 +169,7 @@ static struct nand_flash_dev nand_flash_ids[] = { { 0,} }; -static struct nand_flash_dev * +static const struct nand_flash_dev * nand_find_id(unsigned char id) { int i; @@ -752,7 +752,7 @@ sddr09_read_data(struct us_data *us, // a bounce buffer and move the data a piece at a time between the // bounce buffer and the actual transfer buffer. - len = min(sectors, (unsigned int) info->blocksize) * info->pagesize; + len = min_t(unsigned int, sectors, info->blocksize) * info->pagesize; buffer = kmalloc(len, GFP_NOIO); if (!buffer) return -ENOMEM; @@ -997,7 +997,7 @@ sddr09_write_data(struct us_data *us, * at a time between the bounce buffer and the actual transfer buffer. */ - len = min(sectors, (unsigned int) info->blocksize) * info->pagesize; + len = min_t(unsigned int, sectors, info->blocksize) * info->pagesize; buffer = kmalloc(len, GFP_NOIO); if (!buffer) { kfree(blockbuffer); @@ -1133,9 +1133,9 @@ sddr09_reset(struct us_data *us) { } #endif -static struct nand_flash_dev * +static const struct nand_flash_dev * sddr09_get_cardinfo(struct us_data *us, unsigned char flags) { - struct nand_flash_dev *cardinfo; + const struct nand_flash_dev *cardinfo; unsigned char deviceID[4]; char blurbtxt[256]; int result; @@ -1545,12 +1545,12 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) struct sddr09_card_info *info; - static unsigned char inquiry_response[8] = { + static const unsigned char inquiry_response[8] = { 0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00 }; /* note: no block descriptor support */ - static unsigned char mode_page_01[19] = { + static const unsigned char mode_page_01[19] = { 0x00, 0x0F, 0x00, 0x0, 0x0, 0x0, 0x00, 0x01, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 @@ -1584,7 +1584,7 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) } if (srb->cmnd[0] == READ_CAPACITY) { - struct nand_flash_dev *cardinfo; + const struct nand_flash_dev *cardinfo; sddr09_get_wp(us, info); /* read WP bit */ diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index b8227478a7ad..b323f0a36260 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -29,7 +29,7 @@ MODULE_DESCRIPTION("Driver for SanDisk SDDR-55 SmartMedia reader"); MODULE_AUTHOR("Simon Munton"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); /* * The table of devices @@ -206,7 +206,7 @@ static int sddr55_read_data(struct us_data *us, // a bounce buffer and move the data a piece at a time between the // bounce buffer and the actual transfer buffer. - len = min((unsigned int) sectors, (unsigned int) info->blocksize >> + len = min_t(unsigned int, sectors, info->blocksize >> info->smallpageshift) * PAGESIZE; buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) @@ -224,7 +224,7 @@ static int sddr55_read_data(struct us_data *us, // Read as many sectors as possible in this block - pages = min((unsigned int) sectors << info->smallpageshift, + pages = min_t(unsigned int, sectors << info->smallpageshift, info->blocksize - page); len = pages << info->pageshift; @@ -333,7 +333,7 @@ static int sddr55_write_data(struct us_data *us, // a bounce buffer and move the data a piece at a time between the // bounce buffer and the actual transfer buffer. - len = min((unsigned int) sectors, (unsigned int) info->blocksize >> + len = min_t(unsigned int, sectors, info->blocksize >> info->smallpageshift) * PAGESIZE; buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) @@ -351,7 +351,7 @@ static int sddr55_write_data(struct us_data *us, // Write as many sectors as possible in this block - pages = min((unsigned int) sectors << info->smallpageshift, + pages = min_t(unsigned int, sectors << info->smallpageshift, info->blocksize - page); len = pages << info->pageshift; @@ -775,11 +775,11 @@ static void sddr55_card_info_destructor(void *extra) { static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) { int result; - static unsigned char inquiry_response[8] = { + static const unsigned char inquiry_response[8] = { 0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00 }; // write-protected for now, no block descriptor support - static unsigned char mode_page_01[20] = { + static const unsigned char mode_page_01[20] = { 0x0, 0x12, 0x00, 0x80, 0x0, 0x0, 0x0, 0x0, 0x01, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index e7c224b7c464..27faa0ead11d 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -32,6 +32,7 @@ #include <linux/errno.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/cdrom.h> #include <scsi/scsi.h> @@ -48,7 +49,7 @@ MODULE_DESCRIPTION("Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable"); MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>, Robert Baruch <autophile@starband.net>"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); /* Supported device types */ #define USBAT_DEV_HP8200 0x01 @@ -651,8 +652,7 @@ static int usbat_hp8200e_rw_block_test(struct us_data *us, return USB_STOR_TRANSPORT_FAILED; usb_stor_dbg(us, "Redoing %s\n", - direction == DMA_TO_DEVICE - ? "write" : "read"); + str_write_read(direction == DMA_TO_DEVICE)); } else if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -1683,7 +1683,7 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) struct usbat_info *info = (struct usbat_info *) (us->extra); unsigned long block, blocks; unsigned char *ptr = us->iobuf; - static unsigned char inquiry_response[36] = { + static const unsigned char inquiry_response[36] = { 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 }; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 7449e379077a..1aa1bd26c81f 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -528,7 +528,7 @@ static void last_sector_hacks(struct us_data *us, struct scsi_cmnd *srb) u32 sector; /* To Report "Medium Error: Record Not Found */ - static unsigned char record_not_found[18] = { + static const unsigned char record_not_found[18] = { [0] = 0x70, /* current error */ [2] = MEDIUM_ERROR, /* = 0x03 */ [7] = 0x0a, /* additional length */ @@ -1087,13 +1087,9 @@ int usb_stor_Bulk_max_lun(struct us_data *us) usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n", result, us->iobuf[0]); - /* - * If we have a successful request, return the result if valid. The - * CBW LUN field is 4 bits wide, so the value reported by the device - * should fit into that. - */ + /* If we have a successful request, return the result if valid. */ if (result > 0) { - if (us->iobuf[0] < 16) { + if (us->iobuf[0] <= US_BULK_MAX_LUN_LIMIT) { return us->iobuf[0]; } else { dev_info(&us->pusb_intf->dev, @@ -1133,7 +1129,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(transfer_length); bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? - US_BULK_FLAG_IN : 0; + US_BULK_FLAG_IN : US_BULK_FLAG_OUT; bcb->Tag = ++us->tag; bcb->Lun = srb->device->lun; if (us->fflags & US_FL_SCM_MULT_TARG) diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 03043d567fa1..4ed0dc19afe0 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -817,7 +817,7 @@ static int uas_target_alloc(struct scsi_target *starget) return 0; } -static int uas_slave_alloc(struct scsi_device *sdev) +static int uas_sdev_init(struct scsi_device *sdev) { struct uas_dev_info *devinfo = (struct uas_dev_info *)sdev->host->hostdata; @@ -832,8 +832,8 @@ static int uas_slave_alloc(struct scsi_device *sdev) return 0; } -static int uas_device_configure(struct scsi_device *sdev, - struct queue_limits *lim) +static int uas_sdev_configure(struct scsi_device *sdev, + struct queue_limits *lim) { struct uas_dev_info *devinfo = sdev->hostdata; @@ -905,8 +905,8 @@ static const struct scsi_host_template uas_host_template = { .name = "uas", .queuecommand = uas_queuecommand, .target_alloc = uas_target_alloc, - .slave_alloc = uas_slave_alloc, - .device_configure = uas_device_configure, + .sdev_init = uas_sdev_init, + .sdev_configure = uas_sdev_configure, .eh_abort_handler = uas_eh_abort_handler, .eh_device_reset_handler = uas_eh_device_reset_handler, .this_id = -1, @@ -1289,6 +1289,6 @@ module_exit(uas_exit); MODULE_DESCRIPTION("USB Attached SCSI driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(USB_STORAGE); +MODULE_IMPORT_NS("USB_STORAGE"); MODULE_AUTHOR( "Hans de Goede <hdegoede@redhat.com>, Matthew Wilcox and Sarah Sharp"); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index e5ad23d86833..54f0b1c83317 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -255,6 +255,13 @@ UNUSUAL_DEV( 0x0421, 0x06aa, 0x1110, 0x1110, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_MAX_SECTORS_64 ), +/* Added by Lubomir Rintel <lkundrak@v3.sk>, a very fine chap */ +UNUSUAL_DEV( 0x0421, 0x06c2, 0x0000, 0x0406, + "Nokia", + "Nokia 208", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + #ifdef NO_SDDR09 UNUSUAL_DEV( 0x0436, 0x0005, 0x0100, 0x0100, "Microtech", diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig index 1a6b5e872b0d..7867fa7c405d 100644 --- a/drivers/usb/typec/altmodes/Kconfig +++ b/drivers/usb/typec/altmodes/Kconfig @@ -23,4 +23,13 @@ config TYPEC_NVIDIA_ALTMODE To compile this driver as a module, choose M here: the module will be called typec_nvidia. +config TYPEC_TBT_ALTMODE + tristate "Thunderbolt3 Alternate Mode driver" + help + Select this option if you have Thunderbolt3 hardware on your + system. + + To compile this driver as a module, choose M here: the + module will be called typec_thunderbolt. + endmenu diff --git a/drivers/usb/typec/altmodes/Makefile b/drivers/usb/typec/altmodes/Makefile index 45717548b396..508a68351bd2 100644 --- a/drivers/usb/typec/altmodes/Makefile +++ b/drivers/usb/typec/altmodes/Makefile @@ -4,3 +4,5 @@ obj-$(CONFIG_TYPEC_DP_ALTMODE) += typec_displayport.o typec_displayport-y := displayport.o obj-$(CONFIG_TYPEC_NVIDIA_ALTMODE) += typec_nvidia.o typec_nvidia-y := nvidia.o +obj-$(CONFIG_TYPEC_TBT_ALTMODE) += typec_thunderbolt.o +typec_thunderbolt-y := thunderbolt.o diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 92cc1b136120..ac84a6d64c2f 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -252,7 +252,7 @@ static void dp_altmode_work(struct work_struct *work) case DP_STATE_ENTER: ret = typec_altmode_enter(dp->alt, NULL); if (ret && ret != -EBUSY) - dev_err(&dp->alt->dev, "failed to enter mode\n"); + dev_err(&dp->alt->dev, "failed to enter mode: %d\n", ret); break; case DP_STATE_ENTER_PRIME: ret = typec_cable_altmode_enter(dp->alt, TYPEC_PLUG_SOP_P, NULL); @@ -729,7 +729,7 @@ int dp_altmode_probe(struct typec_altmode *alt) /* FIXME: Port can only be DFP_U. */ - /* Make sure we have compatiple pin configurations */ + /* Make sure we have compatible pin configurations */ if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) & DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) && !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) & @@ -791,7 +791,7 @@ void dp_altmode_remove(struct typec_altmode *alt) EXPORT_SYMBOL_GPL(dp_altmode_remove); static const struct typec_device_id dp_typec_id[] = { - { USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE }, + { USB_TYPEC_DP_SID }, { }, }; MODULE_DEVICE_TABLE(typec, dp_typec_id); diff --git a/drivers/usb/typec/altmodes/nvidia.c b/drivers/usb/typec/altmodes/nvidia.c index fe70b36f078f..2b77d931e494 100644 --- a/drivers/usb/typec/altmodes/nvidia.c +++ b/drivers/usb/typec/altmodes/nvidia.c @@ -24,7 +24,7 @@ static void nvidia_altmode_remove(struct typec_altmode *alt) } static const struct typec_device_id nvidia_typec_id[] = { - { USB_TYPEC_NVIDIA_VLINK_SID, TYPEC_ANY_MODE }, + { USB_TYPEC_NVIDIA_VLINK_SID }, { }, }; MODULE_DEVICE_TABLE(typec, nvidia_typec_id); diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c new file mode 100644 index 000000000000..6eadf7835f8f --- /dev/null +++ b/drivers/usb/typec/altmodes/thunderbolt.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Typec-C Thunderbolt3 Alternate Mode driver + * + * Copyright (C) 2019 Intel Corporation + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + */ + +#include <linux/lockdep.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/usb/pd_vdo.h> +#include <linux/usb/typec_altmode.h> +#include <linux/usb/typec_tbt.h> + +enum tbt_state { + TBT_STATE_IDLE, + TBT_STATE_SOP_P_ENTER, + TBT_STATE_SOP_PP_ENTER, + TBT_STATE_ENTER, + TBT_STATE_EXIT, + TBT_STATE_SOP_PP_EXIT, + TBT_STATE_SOP_P_EXIT +}; + +struct tbt_altmode { + enum tbt_state state; + struct typec_cable *cable; + struct typec_altmode *alt; + struct typec_altmode *plug[2]; + u32 enter_vdo; + + struct work_struct work; + struct mutex lock; /* device lock */ +}; + +static bool tbt_ready(struct typec_altmode *alt); + +static int tbt_enter_mode(struct tbt_altmode *tbt) +{ + struct typec_altmode *plug = tbt->plug[TYPEC_PLUG_SOP_P]; + u32 vdo; + + vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1); + vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0; + vdo |= TBT_MODE; + + if (plug) { + if (typec_cable_is_active(tbt->cable)) + vdo |= TBT_ENTER_MODE_ACTIVE_CABLE; + + vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo)); + vdo |= plug->vdo & TBT_CABLE_ROUNDED; + vdo |= plug->vdo & TBT_CABLE_OPTICAL; + vdo |= plug->vdo & TBT_CABLE_RETIMER; + vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING; + } else { + vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE); + } + + tbt->enter_vdo = vdo; + return typec_altmode_enter(tbt->alt, &vdo); +} + +static void tbt_altmode_work(struct work_struct *work) +{ + struct tbt_altmode *tbt = container_of(work, struct tbt_altmode, work); + int ret; + + mutex_lock(&tbt->lock); + + switch (tbt->state) { + case TBT_STATE_SOP_P_ENTER: + ret = typec_cable_altmode_enter(tbt->alt, TYPEC_PLUG_SOP_P, NULL); + if (ret) { + dev_dbg(&tbt->plug[TYPEC_PLUG_SOP_P]->dev, + "failed to enter mode (%d)\n", ret); + goto disable_plugs; + } + break; + case TBT_STATE_SOP_PP_ENTER: + ret = typec_cable_altmode_enter(tbt->alt, TYPEC_PLUG_SOP_PP, NULL); + if (ret) { + dev_dbg(&tbt->plug[TYPEC_PLUG_SOP_PP]->dev, + "failed to enter mode (%d)\n", ret); + goto disable_plugs; + } + break; + case TBT_STATE_ENTER: + ret = tbt_enter_mode(tbt); + if (ret) + dev_dbg(&tbt->alt->dev, "failed to enter mode (%d)\n", + ret); + break; + case TBT_STATE_EXIT: + typec_altmode_exit(tbt->alt); + break; + case TBT_STATE_SOP_PP_EXIT: + typec_cable_altmode_exit(tbt->alt, TYPEC_PLUG_SOP_PP); + break; + case TBT_STATE_SOP_P_EXIT: + typec_cable_altmode_exit(tbt->alt, TYPEC_PLUG_SOP_P); + break; + default: + break; + } + + tbt->state = TBT_STATE_IDLE; + + mutex_unlock(&tbt->lock); + return; + +disable_plugs: + for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) { + if (tbt->plug[i]) + typec_altmode_put_plug(tbt->plug[i]); + + tbt->plug[i] = NULL; + } + + tbt->state = TBT_STATE_ENTER; + schedule_work(&tbt->work); + mutex_unlock(&tbt->lock); +} + +/* + * If SOP' is available, enter that first (which will trigger a VDM response + * that will enter SOP" if available and then the port). If entering SOP' fails, + * stop attempting to enter either cable altmode (probably not supported) and + * directly enter the port altmode. + */ +static int tbt_enter_modes_ordered(struct typec_altmode *alt) +{ + struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt); + int ret = 0; + + lockdep_assert_held(&tbt->lock); + + if (!tbt_ready(tbt->alt)) + return -ENODEV; + + if (tbt->plug[TYPEC_PLUG_SOP_P]) { + ret = typec_cable_altmode_enter(alt, TYPEC_PLUG_SOP_P, NULL); + if (ret < 0) { + for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) { + if (tbt->plug[i]) + typec_altmode_put_plug(tbt->plug[i]); + + tbt->plug[i] = NULL; + } + } else { + return ret; + } + } + + return tbt_enter_mode(tbt); +} + +static int tbt_cable_altmode_vdm(struct typec_altmode *alt, + enum typec_plug_index sop, const u32 hdr, + const u32 *vdo, int count) +{ + struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt); + int cmd_type = PD_VDO_CMDT(hdr); + int cmd = PD_VDO_CMD(hdr); + + mutex_lock(&tbt->lock); + + if (tbt->state != TBT_STATE_IDLE) { + mutex_unlock(&tbt->lock); + return -EBUSY; + } + + switch (cmd_type) { + case CMDT_RSP_ACK: + switch (cmd) { + case CMD_ENTER_MODE: + /* + * Following the order described in USB Type-C Spec + * R2.0 Section 6.7.3: SOP', SOP", then port. + */ + if (sop == TYPEC_PLUG_SOP_P) { + if (tbt->plug[TYPEC_PLUG_SOP_PP]) + tbt->state = TBT_STATE_SOP_PP_ENTER; + else + tbt->state = TBT_STATE_ENTER; + } else if (sop == TYPEC_PLUG_SOP_PP) + tbt->state = TBT_STATE_ENTER; + + break; + case CMD_EXIT_MODE: + /* Exit in opposite order: Port, SOP", then SOP'. */ + if (sop == TYPEC_PLUG_SOP_PP) + tbt->state = TBT_STATE_SOP_P_EXIT; + break; + } + break; + default: + break; + } + + if (tbt->state != TBT_STATE_IDLE) + schedule_work(&tbt->work); + + mutex_unlock(&tbt->lock); + return 0; +} + +static int tbt_altmode_vdm(struct typec_altmode *alt, + const u32 hdr, const u32 *vdo, int count) +{ + struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt); + struct typec_thunderbolt_data data; + int cmd_type = PD_VDO_CMDT(hdr); + int cmd = PD_VDO_CMD(hdr); + + mutex_lock(&tbt->lock); + + if (tbt->state != TBT_STATE_IDLE) { + mutex_unlock(&tbt->lock); + return -EBUSY; + } + + switch (cmd_type) { + case CMDT_RSP_ACK: + /* Port altmode is last to enter and first to exit. */ + switch (cmd) { + case CMD_ENTER_MODE: + memset(&data, 0, sizeof(data)); + + data.device_mode = tbt->alt->vdo; + data.enter_vdo = tbt->enter_vdo; + if (tbt->plug[TYPEC_PLUG_SOP_P]) + data.cable_mode = tbt->plug[TYPEC_PLUG_SOP_P]->vdo; + + typec_altmode_notify(alt, TYPEC_STATE_MODAL, &data); + break; + case CMD_EXIT_MODE: + if (tbt->plug[TYPEC_PLUG_SOP_PP]) + tbt->state = TBT_STATE_SOP_PP_EXIT; + else if (tbt->plug[TYPEC_PLUG_SOP_P]) + tbt->state = TBT_STATE_SOP_P_EXIT; + break; + } + break; + case CMDT_RSP_NAK: + switch (cmd) { + case CMD_ENTER_MODE: + dev_warn(&alt->dev, "Enter Mode refused\n"); + break; + default: + break; + } + break; + default: + break; + } + + if (tbt->state != TBT_STATE_IDLE) + schedule_work(&tbt->work); + + mutex_unlock(&tbt->lock); + + return 0; +} + +static int tbt_altmode_activate(struct typec_altmode *alt, int activate) +{ + struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt); + int ret; + + mutex_lock(&tbt->lock); + + if (activate) + ret = tbt_enter_modes_ordered(alt); + else + ret = typec_altmode_exit(alt); + + mutex_unlock(&tbt->lock); + + return ret; +} + +static const struct typec_altmode_ops tbt_altmode_ops = { + .vdm = tbt_altmode_vdm, + .activate = tbt_altmode_activate +}; + +static const struct typec_cable_ops tbt_cable_ops = { + .vdm = tbt_cable_altmode_vdm, +}; + +static int tbt_altmode_probe(struct typec_altmode *alt) +{ + struct tbt_altmode *tbt; + + tbt = devm_kzalloc(&alt->dev, sizeof(*tbt), GFP_KERNEL); + if (!tbt) + return -ENOMEM; + + INIT_WORK(&tbt->work, tbt_altmode_work); + mutex_init(&tbt->lock); + tbt->alt = alt; + + alt->desc = "Thunderbolt3"; + typec_altmode_set_drvdata(alt, tbt); + typec_altmode_set_ops(alt, &tbt_altmode_ops); + + if (tbt_ready(alt)) { + if (tbt->plug[TYPEC_PLUG_SOP_P]) + tbt->state = TBT_STATE_SOP_P_ENTER; + else if (tbt->plug[TYPEC_PLUG_SOP_PP]) + tbt->state = TBT_STATE_SOP_PP_ENTER; + else + tbt->state = TBT_STATE_ENTER; + schedule_work(&tbt->work); + } + + return 0; +} + +static void tbt_altmode_remove(struct typec_altmode *alt) +{ + struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt); + + for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) { + if (tbt->plug[i]) + typec_altmode_put_plug(tbt->plug[i]); + } + + if (tbt->cable) + typec_cable_put(tbt->cable); +} + +static bool tbt_ready(struct typec_altmode *alt) +{ + struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt); + struct typec_altmode *plug; + + if (tbt->cable) + return true; + + /* Thunderbolt 3 requires a cable with eMarker */ + tbt->cable = typec_cable_get(typec_altmode2port(tbt->alt)); + if (!tbt->cable) + return false; + + /* We accept systems without SOP' or SOP''. This means the port altmode + * driver will be responsible for properly ordering entry/exit. + */ + for (int i = 0; i < TYPEC_PLUG_SOP_PP + 1; i++) { + plug = typec_altmode_get_plug(tbt->alt, i); + if (!plug) + continue; + + if (plug->svid != USB_TYPEC_TBT_SID) + break; + + plug->desc = "Thunderbolt3"; + plug->cable_ops = &tbt_cable_ops; + typec_altmode_set_drvdata(plug, tbt); + + tbt->plug[i] = plug; + } + + return true; +} + +static const struct typec_device_id tbt_typec_id[] = { + { USB_TYPEC_TBT_SID }, + { } +}; +MODULE_DEVICE_TABLE(typec, tbt_typec_id); + +static struct typec_altmode_driver tbt_altmode_driver = { + .id_table = tbt_typec_id, + .probe = tbt_altmode_probe, + .remove = tbt_altmode_remove, + .driver = { + .name = "typec-thunderbolt", + } +}; +module_typec_altmode_driver(tbt_altmode_driver); + +MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Thunderbolt3 USB Type-C Alternate Mode"); diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index d1e7c487ddfb..0ae0a5ee3fae 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -290,6 +290,8 @@ struct anx7411_data { struct power_supply *psy; struct power_supply_desc psy_desc; struct device *dev; + struct fwnode_handle *switch_node; + struct fwnode_handle *mux_node; }; static u8 snk_identity[] = { @@ -1021,6 +1023,16 @@ static void anx7411_port_unregister_altmodes(struct typec_altmode **adev) } } +static void anx7411_port_unregister(struct typec_params *typecp) +{ + fwnode_handle_put(typecp->caps.fwnode); + anx7411_port_unregister_altmodes(typecp->port_amode); + if (typecp->port) + typec_unregister_port(typecp->port); + if (typecp->role_sw) + usb_role_switch_put(typecp->role_sw); +} + static int anx7411_usb_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) { @@ -1089,6 +1101,7 @@ static void anx7411_unregister_mux(struct anx7411_data *ctx) if (ctx->typec.typec_mux) { typec_mux_unregister(ctx->typec.typec_mux); ctx->typec.typec_mux = NULL; + fwnode_handle_put(ctx->mux_node); } } @@ -1097,6 +1110,7 @@ static void anx7411_unregister_switch(struct anx7411_data *ctx) if (ctx->typec.typec_switch) { typec_switch_unregister(ctx->typec.typec_switch); ctx->typec.typec_switch = NULL; + fwnode_handle_put(ctx->switch_node); } } @@ -1104,28 +1118,29 @@ static int anx7411_typec_switch_probe(struct anx7411_data *ctx, struct device *dev) { int ret; - struct device_node *node; - node = of_get_child_by_name(dev->of_node, "orientation_switch"); - if (!node) + ctx->switch_node = device_get_named_child_node(dev, "orientation_switch"); + if (!ctx->switch_node) return 0; - ret = anx7411_register_switch(ctx, dev, &node->fwnode); + ret = anx7411_register_switch(ctx, dev, ctx->switch_node); if (ret) { dev_err(dev, "failed register switch"); + fwnode_handle_put(ctx->switch_node); return ret; } - node = of_get_child_by_name(dev->of_node, "mode_switch"); - if (!node) { + ctx->mux_node = device_get_named_child_node(dev, "mode_switch"); + if (!ctx->mux_node) { dev_err(dev, "no typec mux exist"); ret = -ENODEV; goto unregister_switch; } - ret = anx7411_register_mux(ctx, dev, &node->fwnode); + ret = anx7411_register_mux(ctx, dev, ctx->mux_node); if (ret) { dev_err(dev, "failed register mode switch"); + fwnode_handle_put(ctx->mux_node); ret = -ENODEV; goto unregister_switch; } @@ -1154,34 +1169,34 @@ static int anx7411_typec_port_probe(struct anx7411_data *ctx, ret = fwnode_property_read_string(fwnode, "power-role", &buf); if (ret) { dev_err(dev, "power-role not found: %d\n", ret); - return ret; + goto put_fwnode; } ret = typec_find_port_power_role(buf); if (ret < 0) - return ret; + goto put_fwnode; cap->type = ret; ret = fwnode_property_read_string(fwnode, "data-role", &buf); if (ret) { dev_err(dev, "data-role not found: %d\n", ret); - return ret; + goto put_fwnode; } ret = typec_find_port_data_role(buf); if (ret < 0) - return ret; + goto put_fwnode; cap->data = ret; ret = fwnode_property_read_string(fwnode, "try-power-role", &buf); if (ret) { dev_err(dev, "try-power-role not found: %d\n", ret); - return ret; + goto put_fwnode; } ret = typec_find_power_role(buf); if (ret < 0) - return ret; + goto put_fwnode; cap->prefer_role = ret; /* Get source pdos */ @@ -1193,7 +1208,7 @@ static int anx7411_typec_port_probe(struct anx7411_data *ctx, typecp->src_pdo_nr); if (ret < 0) { dev_err(dev, "source cap validate failed: %d\n", ret); - return -EINVAL; + goto put_fwnode; } typecp->caps_flags |= HAS_SOURCE_CAP; @@ -1207,7 +1222,7 @@ static int anx7411_typec_port_probe(struct anx7411_data *ctx, typecp->sink_pdo_nr); if (ret < 0) { dev_err(dev, "sink cap validate failed: %d\n", ret); - return -EINVAL; + goto put_fwnode; } for (i = 0; i < typecp->sink_pdo_nr; i++) { @@ -1251,13 +1266,21 @@ static int anx7411_typec_port_probe(struct anx7411_data *ctx, ret = PTR_ERR(ctx->typec.port); ctx->typec.port = NULL; dev_err(dev, "Failed to register type c port %d\n", ret); - return ret; + goto put_usb_role_switch; } typec_port_register_altmodes(ctx->typec.port, NULL, ctx, ctx->typec.port_amode, MAX_ALTMODE); return 0; + +put_usb_role_switch: + if (ctx->typec.role_sw) + usb_role_switch_put(ctx->typec.role_sw); +put_fwnode: + fwnode_handle_put(fwnode); + + return ret; } static int anx7411_typec_check_connection(struct anx7411_data *ctx) @@ -1523,8 +1546,7 @@ free_wq: destroy_workqueue(plat->workqueue); free_typec_port: - typec_unregister_port(plat->typec.port); - anx7411_port_unregister_altmodes(plat->typec.port_amode); + anx7411_port_unregister(&plat->typec); free_typec_switch: anx7411_unregister_switch(plat); @@ -1548,17 +1570,11 @@ static void anx7411_i2c_remove(struct i2c_client *client) i2c_unregister_device(plat->spi_client); - if (plat->typec.role_sw) - usb_role_switch_put(plat->typec.role_sw); - anx7411_unregister_mux(plat); anx7411_unregister_switch(plat); - if (plat->typec.port) - typec_unregister_port(plat->typec.port); - - anx7411_port_unregister_altmodes(plat->typec.port_amode); + anx7411_port_unregister(&plat->typec); } static const struct i2c_device_id anx7411_id[] = { diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index aa879253d3b8..ae90688d23e4 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -454,8 +454,7 @@ static int typec_match(struct device *dev, const struct device_driver *driver) const struct typec_device_id *id; for (id = drv->id_table; id->svid; id++) - if (id->svid == altmode->svid && - (id->mode == TYPEC_ANY_MODE || id->mode == altmode->mode)) + if (id->svid == altmode->svid) return 1; return 0; } @@ -470,8 +469,7 @@ static int typec_uevent(const struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "MODE=%u", altmode->mode)) return -ENOMEM; - return add_uevent_var(env, "MODALIAS=typec:id%04Xm%02X", - altmode->svid, altmode->mode); + return add_uevent_var(env, "MODALIAS=typec:id%04X", altmode->svid); } static int typec_altmode_create_links(struct altmode *alt) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 58f40156de56..9c76c3d0c6cf 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -10,6 +10,7 @@ #include <linux/mutex.h> #include <linux/property.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/usb/pd_vdo.h> #include <linux/usb/typec_mux.h> #include <linux/usb/typec_retimer.h> @@ -219,24 +220,31 @@ static ssize_t usb_power_delivery_revision_show(struct device *dev, char *buf); static DEVICE_ATTR_RO(usb_power_delivery_revision); +static const char * const usb_modes[] = { + [USB_MODE_NONE] = "none", + [USB_MODE_USB2] = "usb2", + [USB_MODE_USB3] = "usb3", + [USB_MODE_USB4] = "usb4" +}; + /* ------------------------------------------------------------------------- */ /* Alternate Modes */ -static int altmode_match(struct device *dev, void *data) +static int altmode_match(struct device *dev, const void *data) { struct typec_altmode *adev = to_typec_altmode(dev); - struct typec_device_id *id = data; + const struct typec_device_id *id = data; if (!is_typec_altmode(dev)) return 0; - return ((adev->svid == id->svid) && (adev->mode == id->mode)); + return (adev->svid == id->svid); } static void typec_altmode_set_partner(struct altmode *altmode) { struct typec_altmode *adev = &altmode->adev; - struct typec_device_id id = { adev->svid, adev->mode, }; + struct typec_device_id id = { adev->svid }; struct typec_port *port = typec_altmode2port(adev); struct altmode *partner; struct device *dev; @@ -354,7 +362,7 @@ active_show(struct device *dev, struct device_attribute *attr, char *buf) { struct typec_altmode *alt = to_typec_altmode(dev); - return sprintf(buf, "%s\n", alt->active ? "yes" : "no"); + return sprintf(buf, "%s\n", str_yes_no(alt->active)); } static ssize_t active_store(struct device *dev, struct device_attribute *attr, @@ -451,7 +459,8 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj, struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj)); if (attr == &dev_attr_active.attr) - if (!adev->ops || !adev->ops->activate) + if (!is_typec_port(adev->dev.parent) && + (!adev->ops || !adev->ops->activate)) return 0444; return attr->mode; @@ -556,7 +565,7 @@ typec_register_altmode(struct device *parent, if (is_port) { alt->attrs[3] = &dev_attr_supported_roles.attr; - alt->adev.active = true; /* Enabled by default */ + alt->adev.active = !desc->inactive; /* Enabled by default */ } sprintf(alt->group_name, "mode%d", desc->mode); @@ -614,6 +623,75 @@ EXPORT_SYMBOL_GPL(typec_unregister_altmode); /* ------------------------------------------------------------------------- */ /* Type-C Partners */ +/** + * typec_partner_set_usb_mode - Assign active USB Mode for the partner + * @partner: USB Type-C partner + * @mode: USB Mode (USB2, USB3 or USB4) + * + * The port drivers can use this function to assign the active USB Mode to + * @partner. The USB Mode can change for example due to Data Reset. + */ +void typec_partner_set_usb_mode(struct typec_partner *partner, enum usb_mode mode) +{ + if (!partner || partner->usb_mode == mode) + return; + + partner->usb_capability |= BIT(mode - 1); + partner->usb_mode = mode; + sysfs_notify(&partner->dev.kobj, NULL, "usb_mode"); +} +EXPORT_SYMBOL_GPL(typec_partner_set_usb_mode); + +static ssize_t +usb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct typec_partner *partner = to_typec_partner(dev); + int len = 0; + int i; + + for (i = USB_MODE_USB2; i < USB_MODE_USB4 + 1; i++) { + if (!(BIT(i - 1) & partner->usb_capability)) + continue; + + if (i == partner->usb_mode) + len += sysfs_emit_at(buf, len, "[%s] ", usb_modes[i]); + else + len += sysfs_emit_at(buf, len, "%s ", usb_modes[i]); + } + + sysfs_emit_at(buf, len - 1, "\n"); + + return len; +} + +static ssize_t usb_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct typec_partner *partner = to_typec_partner(dev); + struct typec_port *port = to_typec_port(dev->parent); + int mode; + int ret; + + if (!port->ops || !port->ops->enter_usb_mode) + return -EOPNOTSUPP; + + mode = sysfs_match_string(usb_modes, buf); + if (mode < 0) + return mode; + + if (mode == partner->usb_mode) + return size; + + ret = port->ops->enter_usb_mode(port, mode); + if (ret) + return ret; + + typec_partner_set_usb_mode(partner, mode); + + return size; +} +static DEVICE_ATTR_RW(usb_mode); + static ssize_t accessory_mode_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -630,7 +708,7 @@ static ssize_t supports_usb_power_delivery_show(struct device *dev, { struct typec_partner *p = to_typec_partner(dev); - return sprintf(buf, "%s\n", p->usb_pd ? "yes" : "no"); + return sprintf(buf, "%s\n", str_yes_no(p->usb_pd)); } static DEVICE_ATTR_RO(supports_usb_power_delivery); @@ -660,6 +738,7 @@ static struct attribute *typec_partner_attrs[] = { &dev_attr_supports_usb_power_delivery.attr, &dev_attr_number_of_alternate_modes.attr, &dev_attr_type.attr, + &dev_attr_usb_mode.attr, &dev_attr_usb_power_delivery_revision.attr, NULL }; @@ -667,6 +746,14 @@ static struct attribute *typec_partner_attrs[] = { static umode_t typec_partner_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { struct typec_partner *partner = to_typec_partner(kobj_to_dev(kobj)); + struct typec_port *port = to_typec_port(partner->dev.parent); + + if (attr == &dev_attr_usb_mode.attr) { + if (!partner->usb_capability) + return 0; + if (!port->ops || !port->ops->enter_usb_mode) + return 0444; + } if (attr == &dev_attr_number_of_alternate_modes.attr) { if (partner->num_altmodes < 0) @@ -740,10 +827,33 @@ static void typec_partner_unlink_device(struct typec_partner *partner, struct de */ int typec_partner_set_identity(struct typec_partner *partner) { - if (!partner->identity) + u8 usb_capability = partner->usb_capability; + struct device *dev = &partner->dev; + struct usb_pd_identity *id; + + id = get_pd_identity(dev); + if (!id) return -EINVAL; - typec_report_identity(&partner->dev); + if (to_typec_port(dev->parent)->data_role == TYPEC_HOST) { + u32 devcap = PD_VDO_UFP_DEVCAP(id->vdo[0]); + + if (devcap & (DEV_USB2_CAPABLE | DEV_USB2_BILLBOARD)) + usb_capability |= USB_CAPABILITY_USB2; + if (devcap & DEV_USB3_CAPABLE) + usb_capability |= USB_CAPABILITY_USB3; + if (devcap & DEV_USB4_CAPABLE) + usb_capability |= USB_CAPABILITY_USB4; + } else { + usb_capability = PD_VDO_DFP_HOSTCAP(id->vdo[0]); + } + + if (partner->usb_capability != usb_capability) { + partner->usb_capability = usb_capability; + sysfs_notify(&dev->kobj, NULL, "usb_mode"); + } + + typec_report_identity(dev); return 0; } EXPORT_SYMBOL_GPL(typec_partner_set_identity); @@ -913,6 +1023,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner->usb_pd = desc->usb_pd; partner->accessory = desc->accessory; partner->num_altmodes = -1; + partner->usb_capability = desc->usb_capability; partner->pd_revision = desc->pd_revision; partner->svdm_version = port->cap->svdm_version; partner->attach = desc->attach; @@ -932,6 +1043,15 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner->dev.type = &typec_partner_dev_type; dev_set_name(&partner->dev, "%s-partner", dev_name(&port->dev)); + if (port->usb2_dev) { + partner->usb_capability |= USB_CAPABILITY_USB2; + partner->usb_mode = USB_MODE_USB2; + } + if (port->usb3_dev) { + partner->usb_capability |= USB_CAPABILITY_USB2 | USB_CAPABILITY_USB3; + partner->usb_mode = USB_MODE_USB3; + } + ret = device_register(&partner->dev); if (ret) { dev_err(&port->dev, "failed to register partner (%d)\n", ret); @@ -1164,11 +1284,6 @@ const struct device_type typec_cable_dev_type = { .release = typec_cable_release, }; -static int cable_match(struct device *dev, void *data) -{ - return is_typec_cable(dev); -} - /** * typec_cable_get - Get a reference to the USB Type-C cable * @port: The USB Type-C Port the cable is connected to @@ -1180,7 +1295,8 @@ struct typec_cable *typec_cable_get(struct typec_port *port) { struct device *dev; - dev = device_find_child(&port->dev, NULL, cable_match); + dev = device_find_child(&port->dev, &typec_cable_dev_type, + device_match_type); if (!dev) return NULL; @@ -1293,6 +1409,67 @@ EXPORT_SYMBOL_GPL(typec_unregister_cable); /* USB Type-C ports */ /** + * typec_port_set_usb_mode - Set the operational USB mode for the port + * @port: USB Type-C port + * @mode: USB Mode (USB2, USB3 or USB4) + * + * @mode will be used with the next Enter_USB message. Existing connections are + * not affected. + */ +void typec_port_set_usb_mode(struct typec_port *port, enum usb_mode mode) +{ + port->usb_mode = mode; +} +EXPORT_SYMBOL_GPL(typec_port_set_usb_mode); + +static ssize_t +usb_capability_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct typec_port *port = to_typec_port(dev); + int len = 0; + int i; + + for (i = USB_MODE_USB2; i < USB_MODE_USB4 + 1; i++) { + if (!(BIT(i - 1) & port->cap->usb_capability)) + continue; + + if (i == port->usb_mode) + len += sysfs_emit_at(buf, len, "[%s] ", usb_modes[i]); + else + len += sysfs_emit_at(buf, len, "%s ", usb_modes[i]); + } + + sysfs_emit_at(buf, len - 1, "\n"); + + return len; +} + +static ssize_t +usb_capability_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct typec_port *port = to_typec_port(dev); + int ret = 0; + int mode; + + if (!port->ops || !port->ops->default_usb_mode_set) + return -EOPNOTSUPP; + + mode = sysfs_match_string(usb_modes, buf); + if (mode < 0) + return mode; + + ret = port->ops->default_usb_mode_set(port, mode); + if (ret) + return ret; + + port->usb_mode = mode; + + return size; +} +static DEVICE_ATTR_RW(usb_capability); + +/** * typec_port_set_usb_power_delivery - Assign USB PD for port. * @port: USB Type-C port. * @pd: USB PD instance. @@ -1679,7 +1856,7 @@ static ssize_t vconn_source_show(struct device *dev, struct typec_port *port = to_typec_port(dev); return sprintf(buf, "%s\n", - port->vconn_role == TYPEC_SOURCE ? "yes" : "no"); + str_yes_no(port->vconn_role == TYPEC_SOURCE)); } static DEVICE_ATTR_RW(vconn_source); @@ -1760,6 +1937,7 @@ static struct attribute *typec_attrs[] = { &dev_attr_vconn_source.attr, &dev_attr_port_type.attr, &dev_attr_orientation.attr, + &dev_attr_usb_capability.attr, NULL, }; @@ -1793,6 +1971,11 @@ static umode_t typec_attr_is_visible(struct kobject *kobj, if (port->cap->orientation_aware) return 0444; return 0; + } else if (attr == &dev_attr_usb_capability.attr) { + if (!port->cap->usb_capability) + return 0; + if (!port->ops || !port->ops->default_usb_mode_set) + return 0444; } return attr->mode; @@ -1843,16 +2026,12 @@ const struct device_type typec_port_dev_type = { /* --------------------------------------- */ /* Driver callbacks to report role updates */ -static int partner_match(struct device *dev, void *data) -{ - return is_typec_partner(dev); -} - static struct typec_partner *typec_get_partner(struct typec_port *port) { struct device *dev; - dev = device_find_child(&port->dev, NULL, partner_match); + dev = device_find_child(&port->dev, &typec_partner_dev_type, + device_match_type); if (!dev) return NULL; @@ -1864,13 +2043,18 @@ static void typec_partner_attach(struct typec_connector *con, struct device *dev struct typec_port *port = container_of(con, struct typec_port, con); struct typec_partner *partner = typec_get_partner(port); struct usb_device *udev = to_usb_device(dev); + enum usb_mode usb_mode; - if (udev->speed < USB_SPEED_SUPER) + if (udev->speed < USB_SPEED_SUPER) { + usb_mode = USB_MODE_USB2; port->usb2_dev = dev; - else + } else { + usb_mode = USB_MODE_USB3; port->usb3_dev = dev; + } if (partner) { + typec_partner_set_usb_mode(partner, usb_mode); typec_partner_link_device(partner, dev); put_device(&partner->dev); } @@ -1980,7 +2164,9 @@ void typec_set_pwr_opmode(struct typec_port *port, sysfs_notify(&port->dev.kobj, NULL, "power_operation_mode"); kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); - partner_dev = device_find_child(&port->dev, NULL, partner_match); + partner_dev = device_find_child(&port->dev, + &typec_partner_dev_type, + device_match_type); if (partner_dev) { struct typec_partner *partner = to_typec_partner(partner_dev); @@ -2144,7 +2330,9 @@ int typec_get_negotiated_svdm_version(struct typec_port *port) enum usb_pd_svdm_ver svdm_version; struct device *partner_dev; - partner_dev = device_find_child(&port->dev, NULL, partner_match); + partner_dev = device_find_child(&port->dev, + &typec_partner_dev_type, + device_match_type); if (!partner_dev) return -ENODEV; @@ -2171,7 +2359,8 @@ int typec_get_cable_svdm_version(struct typec_port *port) enum usb_pd_svdm_ver svdm_version; struct device *cable_dev; - cable_dev = device_find_child(&port->dev, NULL, cable_match); + cable_dev = device_find_child(&port->dev, &typec_cable_dev_type, + device_match_type); if (!cable_dev) return -ENODEV; @@ -2433,6 +2622,13 @@ struct typec_port *typec_register_port(struct device *parent, port->con.attach = typec_partner_attach; port->con.deattach = typec_partner_deattach; + if (cap->usb_capability & USB_CAPABILITY_USB4) + port->usb_mode = USB_MODE_USB4; + else if (cap->usb_capability & USB_CAPABILITY_USB3) + port->usb_mode = USB_MODE_USB3; + else if (cap->usb_capability & USB_CAPABILITY_USB2) + port->usb_mode = USB_MODE_USB2; + device_initialize(&port->dev); port->dev.class = &typec_class; port->dev.parent = parent; diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index 7485cdb9dd20..b3076a24ad2e 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -35,6 +35,8 @@ struct typec_partner { int num_altmodes; u16 pd_revision; /* 0300H = "3.0" */ enum usb_pd_svdm_ver svdm_version; + enum usb_mode usb_mode; + u8 usb_capability; struct usb_power_delivery *pd; @@ -55,6 +57,7 @@ struct typec_port { enum typec_role vconn_role; enum typec_pwr_opmode pwr_opmode; enum typec_port_type port_type; + enum usb_mode usb_mode; struct mutex port_type_lock; enum typec_orientation orientation; diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index fb1242e82ffd..3ecc688dda82 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -16,10 +16,17 @@ #include <linux/delay.h> #include <linux/workqueue.h> +#define HD3SS3220_REG_CN_STAT 0x08 #define HD3SS3220_REG_CN_STAT_CTRL 0x09 #define HD3SS3220_REG_GEN_CTRL 0x0A #define HD3SS3220_REG_DEV_REV 0xA0 +/* Register HD3SS3220_REG_CN_STAT */ +#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_MASK (BIT(7) | BIT(6)) +#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_DEFAULT 0x00 +#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_MID BIT(6) +#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_HIGH BIT(7) + /* Register HD3SS3220_REG_CN_STAT_CTRL*/ #define HD3SS3220_REG_CN_STAT_CTRL_ATTACHED_STATE_MASK (BIT(7) | BIT(6)) #define HD3SS3220_REG_CN_STAT_CTRL_AS_DFP BIT(6) @@ -28,10 +35,16 @@ #define HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS BIT(4) /* Register HD3SS3220_REG_GEN_CTRL*/ +#define HD3SS3220_REG_GEN_CTRL_DISABLE_TERM BIT(0) #define HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK (BIT(2) | BIT(1)) #define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT 0x00 #define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK BIT(1) #define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC (BIT(2) | BIT(1)) +#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_MASK (BIT(5) | BIT(4)) +#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DEFAULT 0x00 +#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DFP BIT(5) +#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_UFP BIT(4) +#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DRP (BIT(5) | BIT(4)) struct hd3ss3220 { struct device *dev; @@ -43,8 +56,96 @@ struct hd3ss3220 { bool poll; }; -static int hd3ss3220_set_source_pref(struct hd3ss3220 *hd3ss3220, int src_pref) +static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode) +{ + int current_mode; + + switch (power_opmode) { + case TYPEC_PWR_MODE_USB: + current_mode = HD3SS3220_REG_CN_STAT_CURRENT_MODE_DEFAULT; + break; + case TYPEC_PWR_MODE_1_5A: + current_mode = HD3SS3220_REG_CN_STAT_CURRENT_MODE_MID; + break; + case TYPEC_PWR_MODE_3_0A: + current_mode = HD3SS3220_REG_CN_STAT_CURRENT_MODE_HIGH; + break; + case TYPEC_PWR_MODE_PD: /* Power delivery not supported */ + default: + dev_err(hd3ss3220->dev, "bad power operation mode: %d\n", power_opmode); + return -EINVAL; + } + + return regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT, + HD3SS3220_REG_CN_STAT_CURRENT_MODE_MASK, + current_mode); +} + +static int hd3ss3220_set_port_type(struct hd3ss3220 *hd3ss3220, int type) +{ + int mode_select, err; + + switch (type) { + case TYPEC_PORT_SRC: + mode_select = HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DFP; + break; + case TYPEC_PORT_SNK: + mode_select = HD3SS3220_REG_GEN_CTRL_MODE_SELECT_UFP; + break; + case TYPEC_PORT_DRP: + mode_select = HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DRP; + break; + default: + dev_err(hd3ss3220->dev, "bad port type: %d\n", type); + return -EINVAL; + } + + /* Disable termination before changing MODE_SELECT as required by datasheet */ + err = regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL, + HD3SS3220_REG_GEN_CTRL_DISABLE_TERM, + HD3SS3220_REG_GEN_CTRL_DISABLE_TERM); + if (err < 0) { + dev_err(hd3ss3220->dev, "Failed to disable port for mode change: %d\n", err); + return err; + } + + err = regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL, + HD3SS3220_REG_GEN_CTRL_MODE_SELECT_MASK, + mode_select); + if (err < 0) { + dev_err(hd3ss3220->dev, "Failed to change mode: %d\n", err); + regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL, + HD3SS3220_REG_GEN_CTRL_DISABLE_TERM, 0); + return err; + } + + err = regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL, + HD3SS3220_REG_GEN_CTRL_DISABLE_TERM, 0); + if (err < 0) + dev_err(hd3ss3220->dev, "Failed to re-enable port after mode change: %d\n", err); + + return err; +} + +static int hd3ss3220_set_source_pref(struct hd3ss3220 *hd3ss3220, int prefer_role) { + int src_pref; + + switch (prefer_role) { + case TYPEC_NO_PREFERRED_ROLE: + src_pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT; + break; + case TYPEC_SINK: + src_pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK; + break; + case TYPEC_SOURCE: + src_pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC; + break; + default: + dev_err(hd3ss3220->dev, "bad role preference: %d\n", prefer_role); + return -EINVAL; + } + return regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL, HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK, src_pref); @@ -76,31 +177,23 @@ static enum usb_role hd3ss3220_get_attached_state(struct hd3ss3220 *hd3ss3220) return attached_state; } -static int hd3ss3220_dr_set(struct typec_port *port, enum typec_data_role role) +static int hd3ss3220_try_role(struct typec_port *port, int role) { struct hd3ss3220 *hd3ss3220 = typec_get_drvdata(port); - enum usb_role role_val; - int pref, ret = 0; - if (role == TYPEC_HOST) { - role_val = USB_ROLE_HOST; - pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC; - } else { - role_val = USB_ROLE_DEVICE; - pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK; - } - - ret = hd3ss3220_set_source_pref(hd3ss3220, pref); - usleep_range(10, 100); + return hd3ss3220_set_source_pref(hd3ss3220, role); +} - usb_role_switch_set_role(hd3ss3220->role_sw, role_val); - typec_set_data_role(hd3ss3220->port, role); +static int hd3ss3220_port_type_set(struct typec_port *port, enum typec_port_type type) +{ + struct hd3ss3220 *hd3ss3220 = typec_get_drvdata(port); - return ret; + return hd3ss3220_set_port_type(hd3ss3220, type); } static const struct typec_operations hd3ss3220_ops = { - .dr_set = hd3ss3220_dr_set + .try_role = hd3ss3220_try_role, + .port_type_set = hd3ss3220_port_type_set, }; static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220) @@ -108,9 +201,6 @@ static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220) enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220); usb_role_switch_set_role(hd3ss3220->role_sw, role_state); - if (role_state == USB_ROLE_NONE) - hd3ss3220_set_source_pref(hd3ss3220, - HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT); switch (role_state) { case USB_ROLE_HOST: @@ -162,6 +252,67 @@ static irqreturn_t hd3ss3220_irq_handler(int irq, void *data) return hd3ss3220_irq(hd3ss3220); } +static int hd3ss3220_configure_power_opmode(struct hd3ss3220 *hd3ss3220, + struct fwnode_handle *connector) +{ + /* + * Supported power operation mode can be configured through device tree + */ + const char *cap_str; + int ret, power_opmode; + + ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str); + if (ret) + return 0; + + power_opmode = typec_find_pwr_opmode(cap_str); + return hd3ss3220_set_power_opmode(hd3ss3220, power_opmode); +} + +static int hd3ss3220_configure_port_type(struct hd3ss3220 *hd3ss3220, + struct fwnode_handle *connector, + struct typec_capability *cap) +{ + /* + * Port type can be configured through device tree + */ + const char *cap_str; + int ret; + + ret = fwnode_property_read_string(connector, "power-role", &cap_str); + if (ret) + return 0; + + ret = typec_find_port_power_role(cap_str); + if (ret < 0) + return ret; + + cap->type = ret; + return hd3ss3220_set_port_type(hd3ss3220, cap->type); +} + +static int hd3ss3220_configure_source_pref(struct hd3ss3220 *hd3ss3220, + struct fwnode_handle *connector, + struct typec_capability *cap) +{ + /* + * Preferred role can be configured through device tree + */ + const char *cap_str; + int ret; + + ret = fwnode_property_read_string(connector, "try-power-role", &cap_str); + if (ret) + return 0; + + ret = typec_find_power_role(cap_str); + if (ret < 0) + return ret; + + cap->prefer_role = ret; + return hd3ss3220_set_source_pref(hd3ss3220, cap->prefer_role); +} + static const struct regmap_config config = { .reg_bits = 8, .val_bits = 8, @@ -188,8 +339,6 @@ static int hd3ss3220_probe(struct i2c_client *client) if (IS_ERR(hd3ss3220->regmap)) return PTR_ERR(hd3ss3220->regmap); - hd3ss3220_set_source_pref(hd3ss3220, - HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT); /* For backward compatibility check the connector child node first */ connector = device_get_named_child_node(hd3ss3220->dev, "connector"); if (connector) { @@ -217,12 +366,24 @@ static int hd3ss3220_probe(struct i2c_client *client) typec_cap.ops = &hd3ss3220_ops; typec_cap.fwnode = connector; + ret = hd3ss3220_configure_source_pref(hd3ss3220, connector, &typec_cap); + if (ret < 0) + goto err_put_role; + + ret = hd3ss3220_configure_port_type(hd3ss3220, connector, &typec_cap); + if (ret < 0) + goto err_put_role; + hd3ss3220->port = typec_register_port(&client->dev, &typec_cap); if (IS_ERR(hd3ss3220->port)) { ret = PTR_ERR(hd3ss3220->port); goto err_put_role; } + ret = hd3ss3220_configure_power_opmode(hd3ss3220, connector); + if (ret < 0) + goto err_unreg_port; + hd3ss3220_set_role(hd3ss3220); ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, &data); if (ret < 0) diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index ce7db6ad3057..6dd8f961b593 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -56,6 +56,16 @@ config TYPEC_MUX_NB7VPQ904M Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C redriver chip found on some devices with a Type-C port. +config TYPEC_MUX_PS883X + tristate "Parade PS883x Type-C retimer driver" + depends on I2C + depends on DRM || DRM=n + select DRM_AUX_BRIDGE if DRM_BRIDGE && OF + select REGMAP_I2C + help + Say Y or M if your system has a Parade PS883x Type-C retimer chip + found on some devices with a Type-C port. + config TYPEC_MUX_PTN36502 tristate "NXP PTN36502 Type-C redriver driver" depends on I2C @@ -66,6 +76,15 @@ config TYPEC_MUX_PTN36502 Say Y or M if your system has a NXP PTN36502 Type-C redriver chip found on some devices with a Type-C port. +config TYPEC_MUX_TUSB1046 + tristate "TI TUSB1046 Type-C crosspoint switch driver" + depends on I2C + help + Driver for the Texas Instruments TUSB1046-DCI crosspoint switch. + Supports flipping USB-C SuperSpeed lanes to adapt to orientation + changes, as well as muxing DisplayPort and sideband signals to a + common Type-C connector. + config TYPEC_MUX_WCD939X_USBSS tristate "Qualcomm WCD939x USBSS Analog Audio Switch driver" depends on I2C diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index bb96f30267af..b4f599eb5053 100644 --- a/drivers/usb/typec/mux/Makefile +++ b/drivers/usb/typec/mux/Makefile @@ -6,5 +6,7 @@ obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o obj-$(CONFIG_TYPEC_MUX_IT5205) += it5205.o obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M) += nb7vpq904m.o +obj-$(CONFIG_TYPEC_MUX_PS883X) += ps883x.o obj-$(CONFIG_TYPEC_MUX_PTN36502) += ptn36502.o +obj-$(CONFIG_TYPEC_MUX_TUSB1046) += tusb1046.o obj-$(CONFIG_TYPEC_MUX_WCD939X_USBSS) += wcd939x-usbss.o diff --git a/drivers/usb/typec/mux/gpio-sbu-mux.c b/drivers/usb/typec/mux/gpio-sbu-mux.c index 8902102c05a8..1834f1a2dd9d 100644 --- a/drivers/usb/typec/mux/gpio-sbu-mux.c +++ b/drivers/usb/typec/mux/gpio-sbu-mux.c @@ -159,7 +159,7 @@ MODULE_DEVICE_TABLE(of, gpio_sbu_mux_match); static struct platform_driver gpio_sbu_mux_driver = { .probe = gpio_sbu_mux_probe, - .remove_new = gpio_sbu_mux_remove, + .remove = gpio_sbu_mux_remove, .driver = { .name = "gpio_sbu_mux", .of_match_table = gpio_sbu_mux_match, diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 46b4c5c3a6be..65dda9183e6f 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -718,7 +718,7 @@ DEFINE_SHOW_ATTRIBUTE(port_iom_status); static void pmc_mux_port_debugfs_init(struct pmc_usb_port *port) { struct dentry *debugfs_dir; - char name[6]; + char name[8]; snprintf(name, sizeof(name), "port%d", port->usb3_port - 1); @@ -828,7 +828,7 @@ static struct platform_driver pmc_usb_driver = { .acpi_match_table = ACPI_PTR(pmc_usb_acpi_ids), }, .probe = pmc_usb_probe, - .remove_new = pmc_usb_remove, + .remove = pmc_usb_remove, }; static int __init pmc_usb_init(void) diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c new file mode 100644 index 000000000000..ad59babf7cce --- /dev/null +++ b/drivers/usb/typec/mux/ps883x.c @@ -0,0 +1,466 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Parade ps883x usb retimer driver + * + * Copyright (C) 2024 Linaro Ltd. + */ + +#include <drm/bridge/aux-bridge.h> +#include <linux/clk.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/usb/typec_altmode.h> +#include <linux/usb/typec_dp.h> +#include <linux/usb/typec_mux.h> +#include <linux/usb/typec_retimer.h> + +#define REG_USB_PORT_CONN_STATUS_0 0x00 + +#define CONN_STATUS_0_CONNECTION_PRESENT BIT(0) +#define CONN_STATUS_0_ORIENTATION_REVERSED BIT(1) +#define CONN_STATUS_0_USB_3_1_CONNECTED BIT(5) + +#define REG_USB_PORT_CONN_STATUS_1 0x01 + +#define CONN_STATUS_1_DP_CONNECTED BIT(0) +#define CONN_STATUS_1_DP_SINK_REQUESTED BIT(1) +#define CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D BIT(2) +#define CONN_STATUS_1_DP_HPD_LEVEL BIT(7) + +#define REG_USB_PORT_CONN_STATUS_2 0x02 + +struct ps883x_retimer { + struct i2c_client *client; + struct gpio_desc *reset_gpio; + struct regmap *regmap; + struct typec_switch_dev *sw; + struct typec_retimer *retimer; + struct clk *xo_clk; + struct regulator *vdd_supply; + struct regulator *vdd33_supply; + struct regulator *vdd33_cap_supply; + struct regulator *vddat_supply; + struct regulator *vddar_supply; + struct regulator *vddio_supply; + + struct typec_switch *typec_switch; + struct typec_mux *typec_mux; + + struct mutex lock; /* protect non-concurrent retimer & switch */ + + enum typec_orientation orientation; + unsigned long mode; + unsigned int svid; +}; + +static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, + int cfg1, int cfg2) +{ + struct device *dev = &retimer->client->dev; + int ret; + + ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0); + if (ret) { + dev_err(dev, "failed to write conn_status_0: %d\n", ret); + return ret; + } + + ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_1, cfg1); + if (ret) { + dev_err(dev, "failed to write conn_status_1: %d\n", ret); + return ret; + } + + ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_2, cfg2); + if (ret) { + dev_err(dev, "failed to write conn_status_2: %d\n", ret); + return ret; + } + + return 0; +} + +static int ps883x_set(struct ps883x_retimer *retimer) +{ + int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT; + int cfg1 = 0x00; + int cfg2 = 0x00; + + if (retimer->orientation == TYPEC_ORIENTATION_NONE || + retimer->mode == TYPEC_STATE_SAFE) { + return ps883x_configure(retimer, cfg0, cfg1, cfg2); + } + + if (retimer->mode != TYPEC_STATE_USB && retimer->svid != USB_TYPEC_DP_SID) + return -EINVAL; + + if (retimer->orientation == TYPEC_ORIENTATION_REVERSE) + cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED; + + switch (retimer->mode) { + case TYPEC_STATE_USB: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + break; + + case TYPEC_DP_STATE_C: + cfg1 = CONN_STATUS_1_DP_CONNECTED | + CONN_STATUS_1_DP_SINK_REQUESTED | + CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D | + CONN_STATUS_1_DP_HPD_LEVEL; + break; + + case TYPEC_DP_STATE_D: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + cfg1 = CONN_STATUS_1_DP_CONNECTED | + CONN_STATUS_1_DP_SINK_REQUESTED | + CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D | + CONN_STATUS_1_DP_HPD_LEVEL; + break; + + case TYPEC_DP_STATE_E: + cfg1 = CONN_STATUS_1_DP_CONNECTED | + CONN_STATUS_1_DP_HPD_LEVEL; + break; + + default: + return -EOPNOTSUPP; + } + + return ps883x_configure(retimer, cfg0, cfg1, cfg2); +} + +static int ps883x_sw_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct ps883x_retimer *retimer = typec_switch_get_drvdata(sw); + int ret = 0; + + ret = typec_switch_set(retimer->typec_switch, orientation); + if (ret) + return ret; + + mutex_lock(&retimer->lock); + + if (retimer->orientation != orientation) { + retimer->orientation = orientation; + + ret = ps883x_set(retimer); + } + + mutex_unlock(&retimer->lock); + + return ret; +} + +static int ps883x_retimer_set(struct typec_retimer *rtmr, + struct typec_retimer_state *state) +{ + struct ps883x_retimer *retimer = typec_retimer_get_drvdata(rtmr); + struct typec_mux_state mux_state; + int ret = 0; + + mutex_lock(&retimer->lock); + + if (state->mode != retimer->mode) { + retimer->mode = state->mode; + + if (state->alt) + retimer->svid = state->alt->svid; + else + retimer->svid = 0; + + ret = ps883x_set(retimer); + } + + mutex_unlock(&retimer->lock); + + if (ret) + return ret; + + mux_state.alt = state->alt; + mux_state.data = state->data; + mux_state.mode = state->mode; + + return typec_mux_set(retimer->typec_mux, &mux_state); +} + +static int ps883x_enable_vregs(struct ps883x_retimer *retimer) +{ + struct device *dev = &retimer->client->dev; + int ret; + + ret = regulator_enable(retimer->vdd33_supply); + if (ret) { + dev_err(dev, "cannot enable VDD 3.3V regulator: %d\n", ret); + return ret; + } + + ret = regulator_enable(retimer->vdd33_cap_supply); + if (ret) { + dev_err(dev, "cannot enable VDD 3.3V CAP regulator: %d\n", ret); + goto err_vdd33_disable; + } + + usleep_range(4000, 10000); + + ret = regulator_enable(retimer->vdd_supply); + if (ret) { + dev_err(dev, "cannot enable VDD regulator: %d\n", ret); + goto err_vdd33_cap_disable; + } + + ret = regulator_enable(retimer->vddar_supply); + if (ret) { + dev_err(dev, "cannot enable VDD AR regulator: %d\n", ret); + goto err_vdd_disable; + } + + ret = regulator_enable(retimer->vddat_supply); + if (ret) { + dev_err(dev, "cannot enable VDD AT regulator: %d\n", ret); + goto err_vddar_disable; + } + + ret = regulator_enable(retimer->vddio_supply); + if (ret) { + dev_err(dev, "cannot enable VDD IO regulator: %d\n", ret); + goto err_vddat_disable; + } + + return 0; + +err_vddat_disable: + regulator_disable(retimer->vddat_supply); +err_vddar_disable: + regulator_disable(retimer->vddar_supply); +err_vdd_disable: + regulator_disable(retimer->vdd_supply); +err_vdd33_cap_disable: + regulator_disable(retimer->vdd33_cap_supply); +err_vdd33_disable: + regulator_disable(retimer->vdd33_supply); + + return ret; +} + +static void ps883x_disable_vregs(struct ps883x_retimer *retimer) +{ + regulator_disable(retimer->vddio_supply); + regulator_disable(retimer->vddat_supply); + regulator_disable(retimer->vddar_supply); + regulator_disable(retimer->vdd_supply); + regulator_disable(retimer->vdd33_cap_supply); + regulator_disable(retimer->vdd33_supply); +} + +static int ps883x_get_vregs(struct ps883x_retimer *retimer) +{ + struct device *dev = &retimer->client->dev; + + retimer->vdd_supply = devm_regulator_get(dev, "vdd"); + if (IS_ERR(retimer->vdd_supply)) + return dev_err_probe(dev, PTR_ERR(retimer->vdd_supply), + "failed to get VDD\n"); + + retimer->vdd33_supply = devm_regulator_get(dev, "vdd33"); + if (IS_ERR(retimer->vdd33_supply)) + return dev_err_probe(dev, PTR_ERR(retimer->vdd33_supply), + "failed to get VDD 3.3V\n"); + + retimer->vdd33_cap_supply = devm_regulator_get(dev, "vdd33-cap"); + if (IS_ERR(retimer->vdd33_cap_supply)) + return dev_err_probe(dev, PTR_ERR(retimer->vdd33_cap_supply), + "failed to get VDD CAP 3.3V\n"); + + retimer->vddat_supply = devm_regulator_get(dev, "vddat"); + if (IS_ERR(retimer->vddat_supply)) + return dev_err_probe(dev, PTR_ERR(retimer->vddat_supply), + "failed to get VDD AT\n"); + + retimer->vddar_supply = devm_regulator_get(dev, "vddar"); + if (IS_ERR(retimer->vddar_supply)) + return dev_err_probe(dev, PTR_ERR(retimer->vddar_supply), + "failed to get VDD AR\n"); + + retimer->vddio_supply = devm_regulator_get(dev, "vddio"); + if (IS_ERR(retimer->vddio_supply)) + return dev_err_probe(dev, PTR_ERR(retimer->vddio_supply), + "failed to get VDD IO\n"); + + return 0; +} + +static const struct regmap_config ps883x_retimer_regmap = { + .max_register = 0x1f, + .reg_bits = 8, + .val_bits = 8, +}; + +static int ps883x_retimer_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct typec_switch_desc sw_desc = { }; + struct typec_retimer_desc rtmr_desc = { }; + struct ps883x_retimer *retimer; + unsigned int val; + int ret; + + retimer = devm_kzalloc(dev, sizeof(*retimer), GFP_KERNEL); + if (!retimer) + return -ENOMEM; + + retimer->client = client; + + mutex_init(&retimer->lock); + + retimer->regmap = devm_regmap_init_i2c(client, &ps883x_retimer_regmap); + if (IS_ERR(retimer->regmap)) + return dev_err_probe(dev, PTR_ERR(retimer->regmap), + "failed to allocate register map\n"); + + ret = ps883x_get_vregs(retimer); + if (ret) + return ret; + + retimer->xo_clk = devm_clk_get(dev, NULL); + if (IS_ERR(retimer->xo_clk)) + return dev_err_probe(dev, PTR_ERR(retimer->xo_clk), + "failed to get xo clock\n"); + + retimer->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); + if (IS_ERR(retimer->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(retimer->reset_gpio), + "failed to get reset gpio\n"); + + retimer->typec_switch = typec_switch_get(dev); + if (IS_ERR(retimer->typec_switch)) + return dev_err_probe(dev, PTR_ERR(retimer->typec_switch), + "failed to acquire orientation-switch\n"); + + retimer->typec_mux = typec_mux_get(dev); + if (IS_ERR(retimer->typec_mux)) { + ret = dev_err_probe(dev, PTR_ERR(retimer->typec_mux), + "failed to acquire mode-mux\n"); + goto err_switch_put; + } + + ret = drm_aux_bridge_register(dev); + if (ret) + goto err_mux_put; + + ret = ps883x_enable_vregs(retimer); + if (ret) + goto err_mux_put; + + ret = clk_prepare_enable(retimer->xo_clk); + if (ret) { + dev_err(dev, "failed to enable XO: %d\n", ret); + goto err_vregs_disable; + } + + /* skip resetting if already configured */ + if (regmap_test_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, + CONN_STATUS_0_CONNECTION_PRESENT) == 1) { + gpiod_direction_output(retimer->reset_gpio, 0); + } else { + gpiod_direction_output(retimer->reset_gpio, 1); + + /* VDD IO supply enable to reset release delay */ + usleep_range(4000, 14000); + + gpiod_set_value(retimer->reset_gpio, 0); + + /* firmware initialization delay */ + msleep(60); + + /* make sure device is accessible */ + ret = regmap_read(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, + &val); + if (ret) { + dev_err(dev, "failed to read conn_status_0: %d\n", ret); + if (ret == -ENXIO) + ret = -EIO; + goto err_clk_disable; + } + } + + sw_desc.drvdata = retimer; + sw_desc.fwnode = dev_fwnode(dev); + sw_desc.set = ps883x_sw_set; + + retimer->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(retimer->sw)) { + ret = PTR_ERR(retimer->sw); + dev_err(dev, "failed to register typec switch: %d\n", ret); + goto err_clk_disable; + } + + rtmr_desc.drvdata = retimer; + rtmr_desc.fwnode = dev_fwnode(dev); + rtmr_desc.set = ps883x_retimer_set; + + retimer->retimer = typec_retimer_register(dev, &rtmr_desc); + if (IS_ERR(retimer->retimer)) { + ret = PTR_ERR(retimer->retimer); + dev_err(dev, "failed to register typec retimer: %d\n", ret); + goto err_switch_unregister; + } + + return 0; + +err_switch_unregister: + typec_switch_unregister(retimer->sw); +err_clk_disable: + clk_disable_unprepare(retimer->xo_clk); +err_vregs_disable: + gpiod_set_value(retimer->reset_gpio, 1); + ps883x_disable_vregs(retimer); +err_mux_put: + typec_mux_put(retimer->typec_mux); +err_switch_put: + typec_switch_put(retimer->typec_switch); + + return ret; +} + +static void ps883x_retimer_remove(struct i2c_client *client) +{ + struct ps883x_retimer *retimer = i2c_get_clientdata(client); + + typec_retimer_unregister(retimer->retimer); + typec_switch_unregister(retimer->sw); + + gpiod_set_value(retimer->reset_gpio, 1); + + clk_disable_unprepare(retimer->xo_clk); + + ps883x_disable_vregs(retimer); + + typec_mux_put(retimer->typec_mux); + typec_switch_put(retimer->typec_switch); +} + +static const struct of_device_id ps883x_retimer_of_table[] = { + { .compatible = "parade,ps8830" }, + { } +}; +MODULE_DEVICE_TABLE(of, ps883x_retimer_of_table); + +static struct i2c_driver ps883x_retimer_driver = { + .driver = { + .name = "ps883x_retimer", + .of_match_table = ps883x_retimer_of_table, + }, + .probe = ps883x_retimer_probe, + .remove = ps883x_retimer_remove, +}; + +module_i2c_driver(ps883x_retimer_driver); + +MODULE_DESCRIPTION("Parade ps883x Type-C Retimer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/mux/tusb1046.c b/drivers/usb/typec/mux/tusb1046.c new file mode 100644 index 000000000000..b4f45c217b59 --- /dev/null +++ b/drivers/usb/typec/mux/tusb1046.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the TUSB1046-DCI USB Type-C crosspoint switch + * + * Copyright (C) 2024 Bootlin + */ + +#include <linux/bits.h> +#include <linux/i2c.h> +#include <linux/usb/typec_mux.h> +#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> +#include <linux/mutex.h> + +#define TUSB1046_REG_GENERAL 0xa + +/* General register bits */ +#define TUSB1046_GENERAL_FLIPSEL BIT(2) +#define TUSB1046_GENERAL_CTLSEL GENMASK(1, 0) + +/* Mux modes */ +#define TUSB1046_CTLSEL_DISABLED 0x0 +#define TUSB1046_CTLSEL_USB3 0x1 +#define TUSB1046_CTLSEL_4LANE_DP 0x2 +#define TUSB1046_CTLSEL_USB3_AND_2LANE_DP 0x3 + +struct tusb1046_priv { + struct i2c_client *client; + struct typec_switch_dev *sw; + struct typec_mux_dev *mux; + + /* Lock General register during accesses */ + struct mutex general_reg_lock; +}; + +static int tusb1046_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct tusb1046_priv *priv = typec_mux_get_drvdata(mux); + struct i2c_client *client = priv->client; + struct device *dev = &client->dev; + int mode, val, ret = 0; + + if (state->mode >= TYPEC_STATE_MODAL && + state->alt->svid != USB_TYPEC_DP_SID) + return -EINVAL; + + dev_dbg(dev, "mux mode requested: %lu\n", state->mode); + + mutex_lock(&priv->general_reg_lock); + + val = i2c_smbus_read_byte_data(client, TUSB1046_REG_GENERAL); + if (val < 0) { + dev_err(dev, "failed to read ctlsel status, err %d\n", val); + ret = val; + goto out_unlock; + } + + switch (state->mode) { + case TYPEC_STATE_USB: + mode = TUSB1046_CTLSEL_USB3; + break; + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: + mode = TUSB1046_CTLSEL_4LANE_DP; + break; + case TYPEC_DP_STATE_D: + mode = TUSB1046_CTLSEL_USB3_AND_2LANE_DP; + break; + case TYPEC_STATE_SAFE: + default: + mode = TUSB1046_CTLSEL_DISABLED; + break; + } + + val &= ~TUSB1046_GENERAL_CTLSEL; + val |= mode; + + ret = i2c_smbus_write_byte_data(client, TUSB1046_REG_GENERAL, val); + +out_unlock: + mutex_unlock(&priv->general_reg_lock); + return ret; +} + +static int tusb1046_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct tusb1046_priv *priv = typec_switch_get_drvdata(sw); + struct i2c_client *client = priv->client; + struct device *dev = &client->dev; + int val, ret = 0; + + dev_dbg(dev, "setting USB3.0 lane flip for orientation %d\n", orientation); + + mutex_lock(&priv->general_reg_lock); + + val = i2c_smbus_read_byte_data(client, TUSB1046_REG_GENERAL); + if (val < 0) { + dev_err(dev, "failed to read flipsel status, err %d\n", val); + ret = val; + goto out_unlock; + } + + if (orientation == TYPEC_ORIENTATION_REVERSE) + val |= TUSB1046_GENERAL_FLIPSEL; + else + val &= ~TUSB1046_GENERAL_FLIPSEL; + + ret = i2c_smbus_write_byte_data(client, TUSB1046_REG_GENERAL, val); + +out_unlock: + mutex_unlock(&priv->general_reg_lock); + return ret; +} + +static int tusb1046_i2c_probe(struct i2c_client *client) +{ + struct typec_switch_desc sw_desc = { }; + struct typec_mux_desc mux_desc = { }; + struct device *dev = &client->dev; + struct tusb1046_priv *priv; + int ret = 0; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return dev_err_probe(dev, -ENOMEM, "failed to allocate driver data\n"); + + priv->client = client; + + mutex_init(&priv->general_reg_lock); + + sw_desc.drvdata = priv; + sw_desc.fwnode = dev_fwnode(dev); + sw_desc.set = tusb1046_switch_set; + + priv->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(priv->sw)) { + ret = dev_err_probe(dev, PTR_ERR(priv->sw), "failed to register type-c switch\n"); + goto err_destroy_mutex; + } + + mux_desc.drvdata = priv; + mux_desc.fwnode = dev_fwnode(dev); + mux_desc.set = tusb1046_mux_set; + + priv->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(priv->mux)) { + ret = dev_err_probe(dev, PTR_ERR(priv->mux), "failed to register type-c mux\n"); + goto err_unregister_switch; + } + + i2c_set_clientdata(client, priv); + + return 0; + +err_unregister_switch: + typec_switch_unregister(priv->sw); +err_destroy_mutex: + mutex_destroy(&priv->general_reg_lock); + return ret; +} + +static void tusb1046_i2c_remove(struct i2c_client *client) +{ + struct tusb1046_priv *priv = i2c_get_clientdata(client); + + typec_switch_unregister(priv->sw); + typec_mux_unregister(priv->mux); + mutex_destroy(&priv->general_reg_lock); +} + +static const struct of_device_id tusb1046_match_table[] = { + {.compatible = "ti,tusb1046"}, + {}, +}; + +static struct i2c_driver tusb1046_driver = { + .driver = { + .name = "tusb1046", + .of_match_table = tusb1046_match_table, + }, + .probe = tusb1046_i2c_probe, + .remove = tusb1046_i2c_remove, +}; + +module_i2c_driver(tusb1046_driver); + +MODULE_DESCRIPTION("TUSB1046 USB Type-C switch driver"); +MODULE_AUTHOR("Romain Gantois <romain.gantois@bootlin.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index f3140fc04c12..6d85b25b40bc 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -633,9 +633,8 @@ MODULE_DEVICE_TABLE(of, stusb160x_of_match); static int stusb160x_probe(struct i2c_client *client) { + const struct regmap_config *regmap_config; struct stusb160x *chip; - const struct of_device_id *match; - struct regmap_config *regmap_config; struct fwnode_handle *fwnode; int ret; @@ -645,8 +644,8 @@ static int stusb160x_probe(struct i2c_client *client) i2c_set_clientdata(client, chip); - match = i2c_of_match_device(stusb160x_of_match, client); - regmap_config = (struct regmap_config *)match->data; + regmap_config = i2c_get_match_data(client); + chip->regmap = devm_regmap_init_i2c(client, regmap_config); if (IS_ERR(chip->regmap)) { ret = PTR_ERR(chip->regmap); diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index e2fe479e16ad..f15c63d3a8f4 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/string.h> +#include <linux/string_choices.h> #include <linux/types.h> #include <linux/usb.h> #include <linux/usb/typec.h> @@ -733,7 +734,7 @@ static int tcpm_set_vconn(struct tcpc_dev *dev, bool on) mutex_lock(&chip->lock); if (chip->vconn_on == on) { - fusb302_log(chip, "vconn is already %s", on ? "On" : "Off"); + fusb302_log(chip, "vconn is already %s", str_on_off(on)); goto done; } if (on) { @@ -746,7 +747,7 @@ static int tcpm_set_vconn(struct tcpc_dev *dev, bool on) if (ret < 0) goto done; chip->vconn_on = on; - fusb302_log(chip, "vconn := %s", on ? "On" : "Off"); + fusb302_log(chip, "vconn := %s", str_on_off(on)); done: mutex_unlock(&chip->lock); @@ -761,7 +762,7 @@ static int tcpm_set_vbus(struct tcpc_dev *dev, bool on, bool charge) mutex_lock(&chip->lock); if (chip->vbus_on == on) { - fusb302_log(chip, "vbus is already %s", on ? "On" : "Off"); + fusb302_log(chip, "vbus is already %s", str_on_off(on)); } else { if (on) ret = regulator_enable(chip->vbus); @@ -769,15 +770,14 @@ static int tcpm_set_vbus(struct tcpc_dev *dev, bool on, bool charge) ret = regulator_disable(chip->vbus); if (ret < 0) { fusb302_log(chip, "cannot %s vbus regulator, ret=%d", - on ? "enable" : "disable", ret); + str_enable_disable(on), ret); goto done; } chip->vbus_on = on; - fusb302_log(chip, "vbus := %s", on ? "On" : "Off"); + fusb302_log(chip, "vbus := %s", str_on_off(on)); } if (chip->charge_on == charge) - fusb302_log(chip, "charge is already %s", - charge ? "On" : "Off"); + fusb302_log(chip, "charge is already %s", str_on_off(charge)); else chip->charge_on = charge; @@ -854,16 +854,16 @@ static int tcpm_set_pd_rx(struct tcpc_dev *dev, bool on) ret = fusb302_pd_set_auto_goodcrc(chip, on); if (ret < 0) { fusb302_log(chip, "cannot turn %s auto GCRC, ret=%d", - on ? "on" : "off", ret); + str_on_off(on), ret); goto done; } ret = fusb302_pd_set_interrupts(chip, on); if (ret < 0) { fusb302_log(chip, "cannot turn %s pd interrupts, ret=%d", - on ? "on" : "off", ret); + str_on_off(on), ret); goto done; } - fusb302_log(chip, "pd := %s", on ? "on" : "off"); + fusb302_log(chip, "pd := %s", str_on_off(on)); done: mutex_unlock(&chip->lock); @@ -1531,7 +1531,7 @@ static void fusb302_irq_work(struct work_struct *work) if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) { vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK); fusb302_log(chip, "IRQ: VBUS_OK, vbus=%s", - vbus_present ? "On" : "Off"); + str_on_off(vbus_present)); if (vbus_present != chip->vbus_present) { chip->vbus_present = vbus_present; tcpm_vbus_change(chip->tcpm_port); @@ -1562,7 +1562,7 @@ static void fusb302_irq_work(struct work_struct *work) if ((interrupt & FUSB_REG_INTERRUPT_COMP_CHNG) && intr_comp_chng) { comp_result = !!(status0 & FUSB_REG_STATUS0_COMP); fusb302_log(chip, "IRQ: COMP_CHNG, comp=%s", - comp_result ? "true" : "false"); + str_true_false(comp_result)); if (comp_result) { /* cc level > Rd_threshold, detach */ chip->cc1 = TYPEC_CC_OPEN; diff --git a/drivers/usb/typec/tcpm/maxim_contaminant.c b/drivers/usb/typec/tcpm/maxim_contaminant.c index 22163d8f9eb0..0cdda06592fd 100644 --- a/drivers/usb/typec/tcpm/maxim_contaminant.c +++ b/drivers/usb/typec/tcpm/maxim_contaminant.c @@ -135,7 +135,7 @@ static int max_contaminant_read_resistance_kohm(struct max_tcpci_chip *chip, mv = max_contaminant_read_adc_mv(chip, channel, sleep_msec, raw, true); if (mv < 0) - return ret; + return mv; /* OVP enable */ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCOVPDIS, 0); @@ -157,7 +157,7 @@ static int max_contaminant_read_resistance_kohm(struct max_tcpci_chip *chip, mv = max_contaminant_read_adc_mv(chip, channel, sleep_msec, raw, true); if (mv < 0) - return ret; + return mv; /* Disable current source */ ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, SBURPCTRL, 0); if (ret < 0) diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c index b80eb2d78d88..3766790c1548 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c @@ -163,7 +163,7 @@ static struct platform_driver qcom_pmic_typec_driver = { .of_match_table = qcom_pmic_typec_table, }, .probe = qcom_pmic_typec_probe, - .remove_new = qcom_pmic_typec_remove, + .remove = qcom_pmic_typec_remove, }; module_platform_driver(qcom_pmic_typec_driver); 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 726423684bae..18303b34594b 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c @@ -12,6 +12,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/usb/pd.h> #include <linux/usb/tcpm.h> #include "qcom_pmic_typec.h" @@ -418,7 +419,7 @@ static int qcom_pmic_typec_pdphy_set_pd_rx(struct tcpc_dev *tcpc, bool on) spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags); - dev_dbg(pmic_typec_pdphy->dev, "set_pd_rx: %s\n", on ? "on" : "off"); + dev_dbg(pmic_typec_pdphy->dev, "set_pd_rx: %s\n", str_on_off(on)); return ret; } 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 df79059cda67..8fac171778da 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 @@ -12,6 +12,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/usb/pd.h> #include <linux/usb/tcpm.h> #include "qcom_pmic_typec.h" @@ -38,7 +39,7 @@ static int qcom_pmic_typec_pdphy_stub_set_pd_rx(struct tcpc_dev *tcpc, bool on) struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc); struct device *dev = tcpm->dev; - dev_dbg(dev, "set_pd_rx: %s\n", on ? "on" : "off"); + dev_dbg(dev, "set_pd_rx: %s\n", str_on_off(on)); return 0; } 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 c37dede62e12..4fc83dcfae64 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c @@ -13,6 +13,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include <linux/usb/tcpm.h> #include <linux/usb/typec_mux.h> #include <linux/workqueue.h> @@ -562,7 +563,8 @@ done: spin_unlock_irqrestore(&pmic_typec_port->lock, flags); dev_dbg(dev, "set_vconn: orientation %d control 0x%08x state %s cc %s vconn %s\n", - orientation, value, on ? "on" : "off", misc_to_vconn(misc), misc_to_cc(misc)); + orientation, value, str_on_off(on), misc_to_vconn(misc), + misc_to_cc(misc)); return ret; } diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index ed32583829be..19ab6647af70 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -27,6 +27,7 @@ #define VPPS_NEW_MIN_PERCENT 95 #define VPPS_VALID_MIN_MV 100 #define VSINKDISCONNECT_PD_MIN_PERCENT 90 +#define VPPS_SHUTDOWN_MIN_PERCENT 85 struct tcpci { struct device *dev; @@ -282,7 +283,7 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc, if (cc2 == TYPEC_CC_RD) /* Role control would have the Rp setting when DRP was enabled */ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP); - else + else if (cc2 >= TYPEC_CC_RP_DEF) reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD); } else { reg &= ~TCPC_ROLE_CTRL_CC1; @@ -290,7 +291,7 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc, if (cc1 == TYPEC_CC_RD) /* Role control would have the Rp setting when DRP was enabled */ reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP); - else + else if (cc1 >= TYPEC_CC_RP_DEF) reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD); } } @@ -366,7 +367,8 @@ static int tcpci_enable_auto_vbus_discharge(struct tcpc_dev *dev, bool enable) } static int tcpci_set_auto_vbus_discharge_threshold(struct tcpc_dev *dev, enum typec_pwr_opmode mode, - bool pps_active, u32 requested_vbus_voltage_mv) + bool pps_active, u32 requested_vbus_voltage_mv, + u32 apdo_min_voltage_mv) { struct tcpci *tcpci = tcpc_to_tcpci(dev); unsigned int pwr_ctrl, threshold = 0; @@ -388,9 +390,12 @@ static int tcpci_set_auto_vbus_discharge_threshold(struct tcpc_dev *dev, enum ty threshold = AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV; } else if (mode == TYPEC_PWR_MODE_PD) { if (pps_active) - threshold = ((VPPS_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) - - VSINKPD_MIN_IR_DROP_MV - VPPS_VALID_MIN_MV) * - VSINKDISCONNECT_PD_MIN_PERCENT / 100; + /* + * To prevent disconnect when the source is in Current Limit Mode. + * Set the threshold to the lowest possible voltage vPpsShutdown (min) + */ + threshold = VPPS_SHUTDOWN_MIN_PERCENT * apdo_min_voltage_mv / 100 - + VSINKPD_MIN_IR_DROP_MV; else threshold = ((VSRC_NEW_MIN_PERCENT * requested_vbus_voltage_mv / 100) - VSINKPD_MIN_IR_DROP_MV - VSRC_VALID_MIN_MV) * @@ -700,7 +705,7 @@ static int tcpci_init(struct tcpc_dev *tcpc) tcpci->alert_mask = reg; - return tcpci_write16(tcpci, TCPC_ALERT_MASK, reg); + return 0; } irqreturn_t tcpci_irq(struct tcpci *tcpci) @@ -923,22 +928,27 @@ static int tcpci_probe(struct i2c_client *client) chip->data.set_orientation = err; + chip->tcpci = tcpci_register_port(&client->dev, &chip->data); + if (IS_ERR(chip->tcpci)) + return PTR_ERR(chip->tcpci); + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, _tcpci_irq, IRQF_SHARED | IRQF_ONESHOT, dev_name(&client->dev), chip); if (err < 0) - return err; + goto unregister_port; - /* - * Disable irq while registering port. If irq is configured as an edge - * irq this allow to keep track and process the irq as soon as it is enabled. - */ - disable_irq(client->irq); - chip->tcpci = tcpci_register_port(&client->dev, &chip->data); - enable_irq(client->irq); + /* Enable chip interrupts at last */ + err = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, chip->tcpci->alert_mask); + if (err < 0) + goto unregister_port; - return PTR_ERR_OR_ZERO(chip->tcpci); + return 0; + +unregister_port: + tcpci_unregister_port(chip->tcpci); + return err; } static void tcpci_remove(struct i2c_client *client) diff --git a/drivers/usb/typec/tcpm/tcpci_mt6360.c b/drivers/usb/typec/tcpm/tcpci_mt6360.c index 02b7fd302265..881ffacbfd77 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6360.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6360.c @@ -221,7 +221,7 @@ static struct platform_driver mt6360_tcpc_driver = { .of_match_table = mt6360_tcpc_of_id, }, .probe = mt6360_tcpc_probe, - .remove_new = mt6360_tcpc_remove, + .remove = mt6360_tcpc_remove, }; module_platform_driver(mt6360_tcpc_driver); diff --git a/drivers/usb/typec/tcpm/tcpci_mt6370.c b/drivers/usb/typec/tcpm/tcpci_mt6370.c index 9cda1005ef01..ed822f438a09 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6370.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c @@ -11,7 +11,6 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/pm_wakeup.h> #include <linux/pm_wakeirq.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -196,7 +195,7 @@ static struct platform_driver mt6370_tcpc_driver = { .of_match_table = mt6370_tcpc_devid_table, }, .probe = mt6370_tcpc_probe, - .remove_new = mt6370_tcpc_remove, + .remove = mt6370_tcpc_remove, }; module_platform_driver(mt6370_tcpc_driver); diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index 64f6dd0dc660..88c50b984e8a 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -334,6 +334,11 @@ static int rt1711h_probe(struct i2c_client *client) { int ret; struct rt1711h_chip *chip; + const u16 alert_mask = TCPC_ALERT_TX_SUCCESS | TCPC_ALERT_TX_DISCARDED | + TCPC_ALERT_TX_FAILED | TCPC_ALERT_RX_HARD_RST | + TCPC_ALERT_RX_STATUS | TCPC_ALERT_POWER_STATUS | + TCPC_ALERT_CC_STATUS | TCPC_ALERT_RX_BUF_OVF | + TCPC_ALERT_FAULT; chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -382,6 +387,12 @@ static int rt1711h_probe(struct i2c_client *client) dev_name(chip->dev), chip); if (ret < 0) return ret; + + /* Enable alert interrupts */ + ret = rt1711h_write16(chip, TCPC_ALERT_MASK, alert_mask); + if (ret < 0) + return ret; + enable_irq_wake(client->irq); return 0; diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 7ae341a40342..a99db4e025cd 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -21,6 +21,7 @@ #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/string_choices.h> #include <linux/usb.h> #include <linux/usb/pd.h> #include <linux/usb/pd_ado.h> @@ -185,7 +186,8 @@ S(UNSTRUCTURED_VDMS), \ S(STRUCTURED_VDMS), \ S(COUNTRY_INFO), \ - S(COUNTRY_CODES) + S(COUNTRY_CODES), \ + S(REVISION_INFORMATION) #define GENERATE_ENUM(e) e #define GENERATE_STRING(s) #s @@ -225,6 +227,7 @@ enum pd_msg_request { PD_MSG_CTRL_NOT_SUPP, PD_MSG_DATA_SINK_CAP, PD_MSG_DATA_SOURCE_CAP, + PD_MSG_DATA_REV, }; enum adev_actions { @@ -310,6 +313,25 @@ struct pd_data { unsigned int operating_snk_mw; }; +struct pd_revision_info { + u8 rev_major; + u8 rev_minor; + u8 ver_major; + u8 ver_minor; +}; + +/* + * @sink_wait_cap_time: Deadline (in ms) for tTypeCSinkWaitCap timer + * @ps_src_wait_off_time: Deadline (in ms) for tPSSourceOff timer + * @cc_debounce_time: Deadline (in ms) for tCCDebounce timer + */ +struct pd_timings { + u32 sink_wait_cap_time; + u32 ps_src_off_time; + u32 cc_debounce_time; + u32 snk_bc12_cmpletion_time; +}; + struct tcpm_port { struct device *dev; @@ -552,6 +574,12 @@ struct tcpm_port { */ unsigned int message_id_prime; unsigned int rx_msgid_prime; + + /* Timer deadline values configured at runtime */ + struct pd_timings timings; + + /* Indicates maximum (revision, version) supported */ + struct pd_revision_info pd_rev; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -865,8 +893,8 @@ static int tcpm_enable_auto_vbus_discharge(struct tcpm_port *port, bool enable) if (port->tcpc->enable_auto_vbus_discharge) { ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, enable); - tcpm_log_force(port, "%s vbus discharge ret:%d", enable ? "enable" : "disable", - ret); + tcpm_log_force(port, "%s vbus discharge ret:%d", + str_enable_disable(enable), ret); if (!ret) port->auto_vbus_discharge_enabled = enable; } @@ -1219,6 +1247,24 @@ static u32 tcpm_forge_legacy_pdo(struct tcpm_port *port, u32 pdo, enum typec_rol } } +static int tcpm_pd_send_revision(struct tcpm_port *port) +{ + struct pd_message msg; + u32 rmdo; + + memset(&msg, 0, sizeof(msg)); + rmdo = RMDO(port->pd_rev.rev_major, port->pd_rev.rev_minor, + port->pd_rev.ver_major, port->pd_rev.ver_minor); + msg.payload[0] = cpu_to_le32(rmdo); + msg.header = PD_HEADER_LE(PD_DATA_REVISION, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + 1); + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + static int tcpm_pd_send_source_caps(struct tcpm_port *port) { struct pd_message msg; @@ -2928,10 +2974,12 @@ static int tcpm_set_auto_vbus_discharge_threshold(struct tcpm_port *port, return 0; ret = port->tcpc->set_auto_vbus_discharge_threshold(port->tcpc, mode, pps_active, - requested_vbus_voltage); + requested_vbus_voltage, + port->pps_data.min_volt); tcpm_log_force(port, - "set_auto_vbus_discharge_threshold mode:%d pps_active:%c vbus:%u ret:%d", - mode, pps_active ? 'y' : 'n', requested_vbus_voltage, ret); + "set_auto_vbus_discharge_threshold mode:%d pps_active:%c vbus:%u pps_apdo_min_volt:%u ret:%d", + mode, pps_active ? 'y' : 'n', requested_vbus_voltage, + port->pps_data.min_volt, ret); return ret; } @@ -3522,6 +3570,17 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); break; + case PD_CTRL_GET_REVISION: + if (port->negotiated_rev >= PD_REV30 && port->pd_rev.rev_major) + tcpm_pd_handle_msg(port, PD_MSG_DATA_REV, + REVISION_INFORMATION); + else + tcpm_pd_handle_msg(port, + port->negotiated_rev < PD_REV30 ? + PD_MSG_CTRL_REJECT : + PD_MSG_CTRL_NOT_SUPP, + NONE_AMS); + break; default: tcpm_pd_handle_msg(port, port->negotiated_rev < PD_REV30 ? @@ -3766,6 +3825,14 @@ static bool tcpm_send_queued_message(struct tcpm_port *port) tcpm_ams_finish(port); } break; + case PD_MSG_DATA_REV: + ret = tcpm_pd_send_revision(port); + if (ret) + tcpm_log(port, + "Unable to send revision msg, ret=%d", + ret); + tcpm_ams_finish(port); + break; default: break; } @@ -4375,7 +4442,7 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port) static void tcpm_set_partner_usb_comm_capable(struct tcpm_port *port, bool capable) { - tcpm_log(port, "Setting usb_comm capable %s", capable ? "true" : "false"); + tcpm_log(port, "Setting usb_comm capable %s", str_true_false(capable)); if (port->tcpc->set_partner_usb_comm_capable) port->tcpc->set_partner_usb_comm_capable(port->tcpc, capable); @@ -4640,15 +4707,15 @@ static void run_state_machine(struct tcpm_port *port) case SRC_ATTACH_WAIT: if (tcpm_port_is_debug(port)) tcpm_set_state(port, DEBUG_ACC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); else if (tcpm_port_is_audio(port)) tcpm_set_state(port, AUDIO_ACC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); else if (tcpm_port_is_source(port) && port->vbus_vsafe0v) tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case SNK_TRY: @@ -4699,7 +4766,7 @@ static void run_state_machine(struct tcpm_port *port) } break; case SRC_TRYWAIT_DEBOUNCE: - tcpm_set_state(port, SRC_ATTACHED, PD_T_CC_DEBOUNCE); + tcpm_set_state(port, SRC_ATTACHED, port->timings.cc_debounce_time); break; case SRC_TRYWAIT_UNATTACHED: tcpm_set_state(port, SNK_UNATTACHED, 0); @@ -4757,7 +4824,7 @@ static void run_state_machine(struct tcpm_port *port) port->caps_count = 0; port->pd_capable = true; tcpm_set_state_cond(port, SRC_SEND_CAPABILITIES_TIMEOUT, - PD_T_SEND_SOURCE_CAP); + PD_T_SENDER_RESPONSE); } break; case SRC_SEND_CAPABILITIES_TIMEOUT: @@ -4902,7 +4969,7 @@ static void run_state_machine(struct tcpm_port *port) (port->cc1 != TYPEC_CC_OPEN && port->cc2 == TYPEC_CC_OPEN)) tcpm_set_state(port, SNK_DEBOUNCED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); else if (tcpm_port_is_disconnected(port)) tcpm_set_state(port, SNK_UNATTACHED, PD_T_PD_DEBOUNCE); @@ -4942,7 +5009,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_TRYWAIT: tcpm_set_cc(port, TYPEC_CC_RD); - tcpm_set_state(port, SNK_TRYWAIT_VBUS, PD_T_CC_DEBOUNCE); + tcpm_set_state(port, SNK_TRYWAIT_VBUS, port->timings.cc_debounce_time); break; case SNK_TRYWAIT_VBUS: /* @@ -4965,7 +5032,16 @@ static void run_state_machine(struct tcpm_port *port) if (ret < 0) tcpm_set_state(port, SNK_UNATTACHED, 0); else - tcpm_set_state(port, SNK_STARTUP, 0); + /* + * For Type C port controllers that use Battery Charging + * Detection (based on BCv1.2 spec) to detect USB + * charger type, add a delay of "snk_bc12_cmpletion_time" + * before transitioning to SNK_STARTUP to allow BC1.2 + * detection to complete before PD is eventually enabled + * in later states. + */ + tcpm_set_state(port, SNK_STARTUP, + port->timings.snk_bc12_cmpletion_time); break; case SNK_STARTUP: opmode = tcpm_get_pwr_opmode(port->polarity ? @@ -5015,7 +5091,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SNK_DISCOVERY_DEBOUNCE: tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE_DONE, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case SNK_DISCOVERY_DEBOUNCE_DONE: if (!tcpm_port_is_disconnected(port) && @@ -5041,15 +5117,16 @@ static void run_state_machine(struct tcpm_port *port) */ if (port->vbus_never_low) { port->vbus_never_low = false; - tcpm_set_state(port, SNK_SOFT_RESET, - PD_T_SINK_WAIT_CAP); + upcoming_state = SNK_SOFT_RESET; } else { if (!port->self_powered) upcoming_state = SNK_WAIT_CAPABILITIES_TIMEOUT; else upcoming_state = hard_reset_state(port); - tcpm_set_state(port, upcoming_state, PD_T_SINK_WAIT_CAP); } + + tcpm_set_state(port, upcoming_state, + port->timings.sink_wait_cap_time); break; case SNK_WAIT_CAPABILITIES_TIMEOUT: /* @@ -5070,7 +5147,8 @@ static void run_state_machine(struct tcpm_port *port) if (tcpm_pd_send_control(port, PD_CTRL_GET_SOURCE_CAP, TCPC_TX_SOP)) tcpm_set_state_cond(port, hard_reset_state(port), 0); else - tcpm_set_state(port, hard_reset_state(port), PD_T_SINK_WAIT_CAP); + tcpm_set_state(port, hard_reset_state(port), + port->timings.sink_wait_cap_time); break; case SNK_NEGOTIATE_CAPABILITIES: port->pd_capable = true; @@ -5207,7 +5285,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ACC_UNATTACHED, 0); break; case AUDIO_ACC_DEBOUNCE: - tcpm_set_state(port, ACC_UNATTACHED, PD_T_CC_DEBOUNCE); + tcpm_set_state(port, ACC_UNATTACHED, port->timings.cc_debounce_time); break; /* Hard_Reset states */ @@ -5274,7 +5352,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); break; case SNK_HARD_RESET_SINK_OFF: - /* Do not discharge/disconnect during hard reseet */ + /* Do not discharge/disconnect during hard reset */ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, false, 0); memset(&port->pps_data, 0, sizeof(port->pps_data)); tcpm_set_vconn(port, false); @@ -5424,7 +5502,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ERROR_RECOVERY, 0); break; case FR_SWAP_SNK_SRC_TRANSITION_TO_OFF: - tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_OFF); + tcpm_set_state(port, ERROR_RECOVERY, port->timings.ps_src_off_time); break; case FR_SWAP_SNK_SRC_NEW_SINK_READY: if (port->vbus_source) @@ -5479,7 +5557,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_cc(port, TYPEC_CC_RD); /* allow CC debounce */ tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED: /* @@ -5513,8 +5591,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB, port->pps_data.active, 0); tcpm_set_charge(port, false); - tcpm_set_state(port, hard_reset_state(port), - PD_T_PS_SOURCE_OFF); + tcpm_set_state(port, ERROR_RECOVERY, port->timings.ps_src_off_time); break; case PR_SWAP_SNK_SRC_SOURCE_ON: tcpm_enable_auto_vbus_discharge(port, true); @@ -5670,7 +5747,7 @@ static void run_state_machine(struct tcpm_port *port) case PORT_RESET_WAIT_OFF: tcpm_set_state(port, tcpm_default_state(port), - port->vbus_present ? PD_T_PS_SOURCE_OFF : 0); + port->vbus_present ? port->timings.ps_src_off_time : 0); break; /* AMS intermediate state */ @@ -6070,7 +6147,7 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port) break; case SNK_ATTACH_WAIT: case SNK_DEBOUNCED: - /* Do nothing, as TCPM is still waiting for vbus to reaach VSAFE5V to connect */ + /* Do nothing, as TCPM is still waiting for vbus to reach VSAFE5V to connect */ break; case SNK_NEGOTIATE_CAPABILITIES: @@ -6161,7 +6238,7 @@ static void _tcpm_pd_vbus_vsafe0v(struct tcpm_port *port) case SRC_ATTACH_WAIT: if (tcpm_port_is_source(port)) tcpm_set_state(port, tcpm_try_snk(port) ? SNK_TRY : SRC_ATTACHED, - PD_T_CC_DEBOUNCE); + port->timings.cc_debounce_time); break; case SRC_STARTUP: case SRC_SEND_CAPABILITIES: @@ -7010,7 +7087,9 @@ static void tcpm_port_unregister_pd(struct tcpm_port *port) static int tcpm_port_register_pd(struct tcpm_port *port) { - struct usb_power_delivery_desc desc = { port->typec_caps.pd_revision }; + u16 pd_revision = port->typec_caps.pd_revision; + u16 pd_version = port->pd_rev.ver_major << 8 | port->pd_rev.ver_minor; + struct usb_power_delivery_desc desc = { pd_revision, pd_version }; struct usb_power_delivery_capabilities *cap; int ret, i; @@ -7057,6 +7136,34 @@ err_unregister: return ret; } +static void tcpm_fw_get_timings(struct tcpm_port *port, struct fwnode_handle *fwnode) +{ + int ret; + u32 val; + + ret = fwnode_property_read_u32(fwnode, "sink-wait-cap-time-ms", &val); + if (!ret) + port->timings.sink_wait_cap_time = val; + else + port->timings.sink_wait_cap_time = PD_T_SINK_WAIT_CAP; + + ret = fwnode_property_read_u32(fwnode, "ps-source-off-time-ms", &val); + if (!ret) + port->timings.ps_src_off_time = val; + else + port->timings.ps_src_off_time = PD_T_PS_SOURCE_OFF; + + ret = fwnode_property_read_u32(fwnode, "cc-debounce-time-ms", &val); + if (!ret) + port->timings.cc_debounce_time = val; + else + port->timings.cc_debounce_time = PD_T_CC_DEBOUNCE; + + ret = fwnode_property_read_u32(fwnode, "sink-bc12-completion-time-ms", &val); + if (!ret) + port->timings.snk_bc12_cmpletion_time = val; +} + static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { struct fwnode_handle *capabilities, *child, *caps = NULL; @@ -7277,6 +7384,29 @@ static int tcpm_fw_get_snk_vdos(struct tcpm_port *port, struct fwnode_handle *fw return 0; } +static void tcpm_fw_get_pd_revision(struct tcpm_port *port, struct fwnode_handle *fwnode) +{ + int ret; + u8 val[4]; + + ret = fwnode_property_count_u8(fwnode, "pd-revision"); + if (!ret || ret != 4) { + tcpm_log(port, "Unable to find pd-revision property or incorrect array size"); + return; + } + + ret = fwnode_property_read_u8_array(fwnode, "pd-revision", val, 4); + if (ret) { + tcpm_log(port, "Failed to parse pd-revision, ret:(%d)", ret); + return; + } + + port->pd_rev.rev_major = val[0]; + port->pd_rev.rev_minor = val[1]; + port->pd_rev.ver_major = val[2]; + port->pd_rev.ver_minor = val[3]; +} + /* Power Supply access to expose source power information */ enum tcpm_psy_online_states { TCPM_PSY_OFFLINE = 0, @@ -7371,7 +7501,7 @@ static int tcpm_psy_get_input_power_limit(struct tcpm_port *port, src_mv = pdo_fixed_voltage(pdo); src_ma = pdo_max_current(pdo); tmp = src_mv * src_ma; - max_src_uw = tmp > max_src_uw ? tmp : max_src_uw; + max_src_uw = max(tmp, max_src_uw); } } @@ -7581,7 +7711,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) mutex_init(&port->lock); mutex_init(&port->swap_lock); - port->wq = kthread_create_worker(0, dev_name(dev)); + port->wq = kthread_run_worker(0, dev_name(dev)); if (IS_ERR(port->wq)) return ERR_CAST(port->wq); sched_set_fifo(port->wq->task); @@ -7591,14 +7721,14 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) kthread_init_work(&port->event_work, tcpm_pd_event_handler); kthread_init_work(&port->enable_frs, tcpm_enable_frs_work); kthread_init_work(&port->send_discover_work, tcpm_send_discover_work); - hrtimer_init(&port->state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - port->state_machine_timer.function = state_machine_timer_handler; - hrtimer_init(&port->vdm_state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - port->vdm_state_machine_timer.function = vdm_state_machine_timer_handler; - hrtimer_init(&port->enable_frs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - port->enable_frs_timer.function = enable_frs_timer_handler; - hrtimer_init(&port->send_discover_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - port->send_discover_timer.function = send_discover_timer_handler; + hrtimer_setup(&port->state_machine_timer, state_machine_timer_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hrtimer_setup(&port->vdm_state_machine_timer, vdm_state_machine_timer_handler, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_setup(&port->enable_frs_timer, enable_frs_timer_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + hrtimer_setup(&port->send_discover_timer, send_discover_timer_handler, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); spin_lock_init(&port->pd_event_lock); @@ -7614,10 +7744,19 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) if (err < 0) goto out_destroy_wq; + tcpm_fw_get_timings(port, tcpc->fwnode); + tcpm_fw_get_pd_revision(port, tcpc->fwnode); + port->try_role = port->typec_caps.prefer_role; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ - port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ + + if (port->pd_rev.rev_major) + port->typec_caps.pd_revision = port->pd_rev.rev_major << 8 | + port->pd_rev.rev_minor; + else + port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ + port->typec_caps.svdm_version = SVDM_VER_2_0; port->typec_caps.driver_data = port; port->typec_caps.ops = &tcpm_ops; diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index cf719307b3f6..759c982bb16a 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -621,10 +621,6 @@ static int wcove_typec_probe(struct platform_device *pdev) if (irq < 0) return irq; - irq = regmap_irq_get_virq(pmic->irq_chip_data_chgr, irq); - if (irq < 0) - return irq; - ret = guid_parse(WCOVE_DSM_UUID, &wcove->guid); if (ret) return ret; @@ -691,7 +687,7 @@ static struct platform_driver wcove_typec_driver = { .name = "bxt_wcove_usbc", }, .probe = wcove_typec_probe, - .remove_new = wcove_typec_remove, + .remove = wcove_typec_remove, }; module_platform_driver(wcove_typec_driver); diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index 680e1b87b152..75559601fe8f 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -69,6 +69,19 @@ config UCSI_PMIC_GLINK To compile the driver as a module, choose M here: the module will be called ucsi_glink. +config CROS_EC_UCSI + tristate "UCSI Driver for ChromeOS EC" + depends on MFD_CROS_EC_DEV + depends on CROS_USBPD_NOTIFY + depends on !EXTCON_TCSS_CROS_EC + default MFD_CROS_EC_DEV + help + This driver enables UCSI support for a ChromeOS EC. The EC is + expected to implement a PPM. + + To compile the driver as a module, choose M here: the module + will be called cros_ec_ucsi. + config UCSI_LENOVO_YOGA_C630 tristate "UCSI Interface Driver for Lenovo Yoga C630" depends on EC_LENOVO_YOGA_C630 diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index aed41d23887b..be98a879104d 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -21,4 +21,5 @@ obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o obj-$(CONFIG_UCSI_PMIC_GLINK) += ucsi_glink.o +obj-$(CONFIG_CROS_EC_UCSI) += cros_ec_ucsi.o obj-$(CONFIG_UCSI_LENOVO_YOGA_C630) += ucsi_yoga_c630.o diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c new file mode 100644 index 000000000000..4ec1c6d22310 --- /dev/null +++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI driver for ChromeOS EC + * + * Copyright 2024 Google LLC. + */ + +#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/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_usbpd_notify.h> +#include <linux/platform_data/cros_ec_proto.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include "ucsi.h" + +/* + * Maximum size in bytes of a UCSI message between AP and EC + */ +#define MAX_EC_DATA_SIZE 256 + +/* + * Maximum time in milliseconds the cros_ec_ucsi driver + * will wait for a response to a command or and ack. + */ +#define WRITE_TMO_MS 5000 + +/* Number of times to attempt recovery from a write timeout before giving up. */ +#define WRITE_TMO_CTR_MAX 5 + +struct cros_ucsi_data { + struct device *dev; + struct ucsi *ucsi; + + struct cros_ec_device *ec; + struct notifier_block nb; + struct work_struct work; + struct delayed_work write_tmo; + int tmo_counter; + + struct completion complete; + unsigned long flags; +}; + +static int cros_ucsi_read(struct ucsi *ucsi, unsigned int offset, void *val, + size_t val_len) +{ + struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi); + struct ec_params_ucsi_ppm_get req = { + .offset = offset, + .size = val_len, + }; + int ret; + + if (val_len > MAX_EC_DATA_SIZE) { + dev_err(udata->dev, "Can't read %zu bytes. Too big.\n", val_len); + return -EINVAL; + } + + ret = cros_ec_cmd(udata->ec, 0, EC_CMD_UCSI_PPM_GET, + &req, sizeof(req), val, val_len); + if (ret < 0) { + dev_warn(udata->dev, "Failed to send EC message UCSI_PPM_GET: error=%d\n", ret); + return ret; + } + return 0; +} + +static int cros_ucsi_read_version(struct ucsi *ucsi, u16 *version) +{ + return cros_ucsi_read(ucsi, UCSI_VERSION, version, sizeof(*version)); +} + +static int cros_ucsi_read_cci(struct ucsi *ucsi, u32 *cci) +{ + return cros_ucsi_read(ucsi, UCSI_CCI, cci, sizeof(*cci)); +} + +static int cros_ucsi_read_message_in(struct ucsi *ucsi, void *val, + size_t val_len) +{ + return cros_ucsi_read(ucsi, UCSI_MESSAGE_IN, val, val_len); +} + +static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd) +{ + struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi); + u8 ec_buf[sizeof(struct ec_params_ucsi_ppm_set) + sizeof(cmd)]; + struct ec_params_ucsi_ppm_set *req = (struct ec_params_ucsi_ppm_set *) ec_buf; + int ret; + + req->offset = UCSI_CONTROL; + memcpy(req->data, &cmd, sizeof(cmd)); + ret = cros_ec_cmd(udata->ec, 0, EC_CMD_UCSI_PPM_SET, + req, sizeof(ec_buf), NULL, 0); + if (ret < 0) { + dev_warn(udata->dev, "Failed to send EC message UCSI_PPM_SET: error=%d\n", ret); + return ret; + } + return 0; +} + +static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci, + void *data, size_t size) +{ + struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi); + int ret; + + ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size); + switch (ret) { + case -EBUSY: + /* EC may return -EBUSY if CCI.busy is set. + * Convert this to a timeout. + */ + case -ETIMEDOUT: + /* Schedule recovery attempt when we timeout + * or tried to send a command while still busy. + */ + cancel_delayed_work_sync(&udata->write_tmo); + schedule_delayed_work(&udata->write_tmo, + msecs_to_jiffies(WRITE_TMO_MS)); + break; + case 0: + /* Successful write. Cancel any pending recovery work. */ + cancel_delayed_work_sync(&udata->write_tmo); + break; + } + + return ret; +} + +static const struct ucsi_operations cros_ucsi_ops = { + .read_version = cros_ucsi_read_version, + .read_cci = cros_ucsi_read_cci, + .read_message_in = cros_ucsi_read_message_in, + .async_control = cros_ucsi_async_control, + .sync_control = cros_ucsi_sync_control, +}; + +static void cros_ucsi_work(struct work_struct *work) +{ + struct cros_ucsi_data *udata = container_of(work, struct cros_ucsi_data, work); + u32 cci; + + if (cros_ucsi_read_cci(udata->ucsi, &cci)) + return; + + ucsi_notify_common(udata->ucsi, cci); +} + +static void cros_ucsi_write_timeout(struct work_struct *work) +{ + struct cros_ucsi_data *udata = + container_of(work, struct cros_ucsi_data, write_tmo.work); + u32 cci; + u64 cmd; + + if (cros_ucsi_read(udata->ucsi, UCSI_CCI, &cci, sizeof(cci))) { + dev_err(udata->dev, + "Reading CCI failed; no write timeout recovery possible.\n"); + return; + } + + if (cci & UCSI_CCI_BUSY) { + udata->tmo_counter++; + + if (udata->tmo_counter <= WRITE_TMO_CTR_MAX) + schedule_delayed_work(&udata->write_tmo, + msecs_to_jiffies(WRITE_TMO_MS)); + else + dev_err(udata->dev, + "PPM unresponsive - too many write timeouts.\n"); + + return; + } + + /* No longer busy means we can reset our timeout counter. */ + udata->tmo_counter = 0; + + /* Need to ack previous command which may have timed out. */ + if (cci & UCSI_CCI_COMMAND_COMPLETE) { + cmd = UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE; + cros_ucsi_async_control(udata->ucsi, cmd); + + /* Check again after a few seconds that the system has + * recovered to make sure our async write above was successful. + */ + schedule_delayed_work(&udata->write_tmo, + msecs_to_jiffies(WRITE_TMO_MS)); + return; + } + + /* We recovered from a previous timeout. Treat this as a recovery from + * suspend and call resume. + */ + ucsi_resume(udata->ucsi); +} + +static int cros_ucsi_event(struct notifier_block *nb, + unsigned long host_event, void *_notify) +{ + struct cros_ucsi_data *udata = container_of(nb, struct cros_ucsi_data, nb); + + if (host_event & PD_EVENT_INIT) { + /* Late init event received from ChromeOS EC. Treat this as a + * system resume to re-enable communication with the PPM. + */ + dev_dbg(udata->dev, "Late PD init received\n"); + ucsi_resume(udata->ucsi); + } + + if (host_event & PD_EVENT_PPM) { + dev_dbg(udata->dev, "UCSI notification received\n"); + flush_work(&udata->work); + schedule_work(&udata->work); + } + + return NOTIFY_OK; +} + +static void cros_ucsi_destroy(struct cros_ucsi_data *udata) +{ + cros_usbpd_unregister_notify(&udata->nb); + cancel_delayed_work_sync(&udata->write_tmo); + cancel_work_sync(&udata->work); + ucsi_destroy(udata->ucsi); +} + +static int cros_ucsi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cros_ec_dev *ec_data = dev_get_drvdata(dev->parent); + struct cros_ucsi_data *udata; + int ret; + + udata = devm_kzalloc(dev, sizeof(*udata), GFP_KERNEL); + if (!udata) + return -ENOMEM; + + udata->dev = dev; + + udata->ec = ec_data->ec_dev; + if (!udata->ec) + return dev_err_probe(dev, -ENODEV, "couldn't find parent EC device\n"); + + platform_set_drvdata(pdev, udata); + + INIT_WORK(&udata->work, cros_ucsi_work); + INIT_DELAYED_WORK(&udata->write_tmo, cros_ucsi_write_timeout); + init_completion(&udata->complete); + + udata->ucsi = ucsi_create(dev, &cros_ucsi_ops); + if (IS_ERR(udata->ucsi)) + return dev_err_probe(dev, PTR_ERR(udata->ucsi), "failed to allocate UCSI instance\n"); + + ucsi_set_drvdata(udata->ucsi, udata); + + udata->nb.notifier_call = cros_ucsi_event; + ret = cros_usbpd_register_notify(&udata->nb); + if (ret) { + dev_err_probe(dev, ret, "failed to register notifier\n"); + ucsi_destroy(udata->ucsi); + return ret; + } + + ret = ucsi_register(udata->ucsi); + if (ret) { + dev_err_probe(dev, ret, "failed to register UCSI\n"); + cros_ucsi_destroy(udata); + return ret; + } + + return 0; +} + +static void cros_ucsi_remove(struct platform_device *dev) +{ + struct cros_ucsi_data *udata = platform_get_drvdata(dev); + + ucsi_unregister(udata->ucsi); + cros_ucsi_destroy(udata); +} + +static int __maybe_unused cros_ucsi_suspend(struct device *dev) +{ + struct cros_ucsi_data *udata = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&udata->write_tmo); + cancel_work_sync(&udata->work); + + return 0; +} + +static void __maybe_unused cros_ucsi_complete(struct device *dev) +{ + struct cros_ucsi_data *udata = dev_get_drvdata(dev); + + ucsi_resume(udata->ucsi); +} + +/* + * UCSI protocol is also used on ChromeOS platforms which reply on + * cros_ec_lpc.c driver for communication with embedded controller (EC). + * On such platforms communication with the EC is not available until + * the .complete() callback of the cros_ec_lpc driver is executed. + * For this reason we delay ucsi_resume() until the .complete() stage + * otherwise UCSI SET_NOTIFICATION_ENABLE command will fail and we won't + * receive any UCSI notifications from the EC where PPM is implemented. + */ +static const struct dev_pm_ops cros_ucsi_pm_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = cros_ucsi_suspend, + .complete = cros_ucsi_complete, +#endif +}; + +static const struct platform_device_id cros_ucsi_id[] = { + { KBUILD_MODNAME, 0 }, + {} +}; +MODULE_DEVICE_TABLE(platform, cros_ucsi_id); + +static struct platform_driver cros_ucsi_driver = { + .driver = { + .name = KBUILD_MODNAME, + .pm = &cros_ucsi_pm_ops, + }, + .id_table = cros_ucsi_id, + .probe = cros_ucsi_probe, + .remove = cros_ucsi_remove, +}; + +module_platform_driver(cros_ucsi_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("UCSI driver for ChromeOS EC"); diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index f67733cecfdf..eae2b18a2d8a 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -28,10 +28,12 @@ static int ucsi_cmd(void *data, u64 val) ucsi->debugfs->status = 0; switch (UCSI_COMMAND(val)) { - case UCSI_SET_UOM: + case UCSI_SET_CCOM: case UCSI_SET_UOR: case UCSI_SET_PDR: case UCSI_CONNECTOR_RESET: + case UCSI_SET_SINK_PATH: + case UCSI_SET_NEW_CAM: ret = ucsi_send_command(ucsi, val, NULL, 0); break; case UCSI_GET_CAPABILITY: @@ -41,6 +43,9 @@ static int ucsi_cmd(void *data, u64 val) case UCSI_GET_PDOS: case UCSI_GET_CABLE_PROPERTY: case UCSI_GET_CONNECTOR_STATUS: + case UCSI_GET_ERROR_STATUS: + case UCSI_GET_CAM_CS: + case UCSI_GET_LPM_PPM_INFO: ret = ucsi_send_command(ucsi, val, &ucsi->debugfs->response, sizeof(ucsi->debugfs->response)); diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c index 1c631c7855a9..62ac69730405 100644 --- a/drivers/usb/typec/ucsi/psy.c +++ b/drivers/usb/typec/ucsi/psy.c @@ -55,8 +55,8 @@ static int ucsi_psy_get_online(struct ucsi_connector *con, union power_supply_propval *val) { val->intval = UCSI_PSY_OFFLINE; - if (con->status.flags & UCSI_CONSTAT_CONNECTED && - (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK) + if (UCSI_CONSTAT(con, CONNECTED) && + (UCSI_CONSTAT(con, PWR_DIR) == TYPEC_SINK)) val->intval = UCSI_PSY_FIXED_ONLINE; return 0; } @@ -66,7 +66,7 @@ static int ucsi_psy_get_voltage_min(struct ucsi_connector *con, { u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: pdo = con->src_pdos[0]; val->intval = pdo_fixed_voltage(pdo) * 1000; @@ -89,7 +89,7 @@ static int ucsi_psy_get_voltage_max(struct ucsi_connector *con, { u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: if (con->num_pdos > 0) { pdo = con->src_pdos[con->num_pdos - 1]; @@ -117,7 +117,7 @@ static int ucsi_psy_get_voltage_now(struct ucsi_connector *con, int index; u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: index = rdo_index(con->rdo); if (index > 0) { @@ -145,7 +145,7 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con, { u32 pdo; - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: if (con->num_pdos > 0) { pdo = con->src_pdos[con->num_pdos - 1]; @@ -173,9 +173,7 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con, static int ucsi_psy_get_current_now(struct ucsi_connector *con, union power_supply_propval *val) { - u16 flags = con->status.flags; - - if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD) + if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) val->intval = rdo_op_current(con->rdo) * 1000; else val->intval = 0; @@ -185,11 +183,9 @@ static int ucsi_psy_get_current_now(struct ucsi_connector *con, static int ucsi_psy_get_usb_type(struct ucsi_connector *con, union power_supply_propval *val) { - u16 flags = con->status.flags; - val->intval = POWER_SUPPLY_USB_TYPE_C; - if (flags & UCSI_CONSTAT_CONNECTED && - UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD) + if (UCSI_CONSTAT(con, CONNECTED) && + UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) val->intval = POWER_SUPPLY_USB_TYPE_PD; return 0; @@ -197,18 +193,18 @@ static int ucsi_psy_get_usb_type(struct ucsi_connector *con, static int ucsi_psy_get_charge_type(struct ucsi_connector *con, union power_supply_propval *val) { - if (!(con->status.flags & UCSI_CONSTAT_CONNECTED)) { + if (!(UCSI_CONSTAT(con, CONNECTED))) { val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; return 0; } /* The Battery Charging Cabability Status field is only valid in sink role. */ - if ((con->status.flags & UCSI_CONSTAT_PWR_DIR) != TYPEC_SINK) { + if (UCSI_CONSTAT(con, PWR_DIR) != TYPEC_SINK) { val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; return 0; } - switch (UCSI_CONSTAT_BC_STATUS(con->status.pwr_status)) { + switch (UCSI_CONSTAT(con, BC_STATUS)) { case UCSI_CONSTAT_BC_NOMINAL_CHARGING: val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; break; diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c index cb62ad835761..596a9542d401 100644 --- a/drivers/usb/typec/ucsi/trace.c +++ b/drivers/usb/typec/ucsi/trace.c @@ -12,7 +12,7 @@ static const char * const ucsi_cmd_strs[] = { [UCSI_SET_NOTIFICATION_ENABLE] = "SET_NOTIFICATION_ENABLE", [UCSI_GET_CAPABILITY] = "GET_CAPABILITY", [UCSI_GET_CONNECTOR_CAPABILITY] = "GET_CONNECTOR_CAPABILITY", - [UCSI_SET_UOM] = "SET_UOM", + [UCSI_SET_CCOM] = "SET_CCOM", [UCSI_SET_UOR] = "SET_UOR", [UCSI_SET_PDM] = "SET_PDM", [UCSI_SET_PDR] = "SET_PDR", diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h index a0d3a934d3d9..41701dee7056 100644 --- a/drivers/usb/typec/ucsi/trace.h +++ b/drivers/usb/typec/ucsi/trace.h @@ -40,8 +40,8 @@ DEFINE_EVENT(ucsi_log_command, ucsi_reset_ppm, ); DECLARE_EVENT_CLASS(ucsi_log_connector_status, - TP_PROTO(int port, struct ucsi_connector_status *status), - TP_ARGS(port, status), + TP_PROTO(int port, struct ucsi_connector *con), + TP_ARGS(port, con), TP_STRUCT__entry( __field(int, port) __field(u16, change) @@ -55,14 +55,14 @@ DECLARE_EVENT_CLASS(ucsi_log_connector_status, ), TP_fast_assign( __entry->port = port - 1; - __entry->change = status->change; - __entry->opmode = UCSI_CONSTAT_PWR_OPMODE(status->flags); - __entry->connected = !!(status->flags & UCSI_CONSTAT_CONNECTED); - __entry->pwr_dir = !!(status->flags & UCSI_CONSTAT_PWR_DIR); - __entry->partner_flags = UCSI_CONSTAT_PARTNER_FLAGS(status->flags); - __entry->partner_type = UCSI_CONSTAT_PARTNER_TYPE(status->flags); - __entry->request_data_obj = status->request_data_obj; - __entry->bc_status = UCSI_CONSTAT_BC_STATUS(status->pwr_status); + __entry->change = UCSI_CONSTAT(con, CHANGE); + __entry->opmode = UCSI_CONSTAT(con, PWR_OPMODE); + __entry->connected = UCSI_CONSTAT(con, CONNECTED); + __entry->pwr_dir = UCSI_CONSTAT(con, PWR_DIR); + __entry->partner_flags = UCSI_CONSTAT(con, PARTNER_FLAGS); + __entry->partner_type = UCSI_CONSTAT(con, PARTNER_TYPE); + __entry->request_data_obj = UCSI_CONSTAT(con, RDO); + __entry->bc_status = UCSI_CONSTAT(con, BC_STATUS); ), TP_printk("port%d status: change=%04x, opmode=%x, connected=%d, " "sourcing=%d, partner_flags=%x, partner_type=%x, " @@ -73,13 +73,13 @@ DECLARE_EVENT_CLASS(ucsi_log_connector_status, ); DEFINE_EVENT(ucsi_log_connector_status, ucsi_connector_change, - TP_PROTO(int port, struct ucsi_connector_status *status), - TP_ARGS(port, status) + TP_PROTO(int port, struct ucsi_connector *con), + TP_ARGS(port, con) ); DEFINE_EVENT(ucsi_log_connector_status, ucsi_register_port, - TP_PROTO(int port, struct ucsi_connector_status *status), - TP_ARGS(port, status) + TP_PROTO(int port, struct ucsi_connector *con), + TP_ARGS(port, con) ); DECLARE_EVENT_CLASS(ucsi_log_register_altmode, diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index e0f3925e401b..e8c7e9dc4930 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -25,7 +25,7 @@ * difficult to estimate the time it takes for the system to process the command * before it is actually passed to the PPM. */ -#define UCSI_TIMEOUT_MS 5000 +#define UCSI_TIMEOUT_MS 10000 /* * UCSI_SWAP_TIMEOUT_MS - Timeout for role swap requests @@ -46,16 +46,17 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci)); if (cci & UCSI_CCI_ACK_COMPLETE && - test_bit(ACK_PENDING, &ucsi->flags)) + test_and_clear_bit(ACK_PENDING, &ucsi->flags)) complete(&ucsi->complete); if (cci & UCSI_CCI_COMMAND_COMPLETE && - test_bit(COMMAND_PENDING, &ucsi->flags)) + test_and_clear_bit(COMMAND_PENDING, &ucsi->flags)) complete(&ucsi->complete); } EXPORT_SYMBOL_GPL(ucsi_notify_common); -int ucsi_sync_control_common(struct ucsi *ucsi, u64 command) +int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, + void *data, size_t size) { bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI; int ret; @@ -65,6 +66,8 @@ int ucsi_sync_control_common(struct ucsi *ucsi, u64 command) else set_bit(COMMAND_PENDING, &ucsi->flags); + reinit_completion(&ucsi->complete); + ret = ucsi->ops->async_control(ucsi, command); if (ret) goto out_clear_bit; @@ -78,6 +81,13 @@ out_clear_bit: else clear_bit(COMMAND_PENDING, &ucsi->flags); + if (!ret && cci) + ret = ucsi->ops->read_cci(ucsi, cci); + + if (!ret && data && + (*cci & UCSI_CCI_COMMAND_COMPLETE)) + ret = ucsi->ops->read_message_in(ucsi, data, size); + return ret; } EXPORT_SYMBOL_GPL(ucsi_sync_control_common); @@ -93,7 +103,7 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack) ctrl |= UCSI_ACK_CONNECTOR_CHANGE; } - return ucsi->ops->sync_control(ucsi, ctrl); + return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0); } static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, @@ -106,9 +116,7 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, if (size > UCSI_MAX_DATA_LENGTH(ucsi)) return -EINVAL; - ret = ucsi->ops->sync_control(ucsi, command); - if (ucsi->ops->read_cci(ucsi, cci)) - return -EIO; + ret = ucsi->ops->sync_control(ucsi, command, cci, data, size); if (*cci & UCSI_CCI_BUSY) return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY; @@ -125,9 +133,6 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci, else err = 0; - if (!err && data && UCSI_CCI_LENGTH(*cci)) - err = ucsi->ops->read_message_in(ucsi, data, size); - /* * Don't ACK connection change if there was an error. */ @@ -648,6 +653,18 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) } } +static int ucsi_get_connector_status(struct ucsi_connector *con, bool conn_ack) +{ + u64 command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); + size_t size = min(sizeof(con->status), + UCSI_MAX_DATA_LENGTH(con->ucsi)); + int ret; + + ret = ucsi_send_command_common(con->ucsi, command, &con->status, size, conn_ack); + + return ret < 0 ? ret : 0; +} + static int ucsi_read_pdos(struct ucsi_connector *con, enum typec_role role, int is_partner, u32 *pdos, int offset, int num_pdos) @@ -658,8 +675,7 @@ static int ucsi_read_pdos(struct ucsi_connector *con, if (is_partner && ucsi->quirks & UCSI_NO_PARTNER_PDOS && - ((con->status.flags & UCSI_CONSTAT_PWR_DIR) || - !is_source(role))) + (UCSI_CONSTAT(con, PWR_DIR) || !is_source(role))) return 0; command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num); @@ -973,6 +989,7 @@ static void ucsi_unregister_cable(struct ucsi_connector *con) static int ucsi_check_connector_capability(struct ucsi_connector *con) { + u64 pd_revision; u64 command; int ret; @@ -986,17 +1003,17 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con) return ret; } - typec_partner_set_pd_revision(con->partner, - UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(con->cap.flags)); + pd_revision = UCSI_CONCAP(con, PARTNER_PD_REVISION_V2_1); + typec_partner_set_pd_revision(con->partner, UCSI_SPEC_REVISION_TO_BCD(pd_revision)); return ret; } static void ucsi_pwr_opmode_change(struct ucsi_connector *con) { - switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { case UCSI_CONSTAT_PWR_OPMODE_PD: - con->rdo = con->status.request_data_obj; + con->rdo = UCSI_CONSTAT(con, RDO); typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD); ucsi_partner_task(con, ucsi_get_src_pdos, 30, 0); ucsi_partner_task(con, ucsi_check_altmodes, 30, HZ); @@ -1020,7 +1037,7 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con) static int ucsi_register_partner(struct ucsi_connector *con) { - u8 pwr_opmode = UCSI_CONSTAT_PWR_OPMODE(con->status.flags); + u8 pwr_opmode = UCSI_CONSTAT(con, PWR_OPMODE); struct typec_partner_desc desc; struct typec_partner *partner; @@ -1029,7 +1046,7 @@ static int ucsi_register_partner(struct ucsi_connector *con) memset(&desc, 0, sizeof(desc)); - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_DEBUG: desc.accessory = TYPEC_ACCESSORY_DEBUG; break; @@ -1047,6 +1064,11 @@ static int ucsi_register_partner(struct ucsi_connector *con) desc.identity = &con->partner_identity; desc.usb_pd = pwr_opmode == UCSI_CONSTAT_PWR_OPMODE_PD; + if (con->ucsi->version >= UCSI_VERSION_2_1) { + u64 pd_revision = UCSI_CONCAP(con, PARTNER_PD_REVISION_V2_1); + desc.pd_revision = UCSI_SPEC_REVISION_TO_BCD(pd_revision); + } + partner = typec_register_partner(con->port, &desc); if (IS_ERR(partner)) { dev_err(con->ucsi->dev, @@ -1057,6 +1079,13 @@ static int ucsi_register_partner(struct ucsi_connector *con) con->partner = partner; + if (con->ucsi->version >= UCSI_VERSION_3_0 && + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN4)) + typec_partner_set_usb_mode(partner, USB_MODE_USB4); + else if (con->ucsi->version >= UCSI_VERSION_2_0 && + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN3)) + typec_partner_set_usb_mode(partner, USB_MODE_USB4); + return 0; } @@ -1081,7 +1110,7 @@ static void ucsi_partner_change(struct ucsi_connector *con) enum usb_role u_role = USB_ROLE_NONE; int ret; - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_UFP: case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: u_role = USB_ROLE_HOST; @@ -1097,8 +1126,8 @@ static void ucsi_partner_change(struct ucsi_connector *con) break; } - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + if (UCSI_CONSTAT(con, CONNECTED)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_DEBUG: typec_set_mode(con->port, TYPEC_MODE_DEBUG); break; @@ -1106,14 +1135,13 @@ static void ucsi_partner_change(struct ucsi_connector *con) typec_set_mode(con->port, TYPEC_MODE_AUDIO); break; default: - if (UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) == - UCSI_CONSTAT_PARTNER_FLAG_USB) + if (UCSI_CONSTAT(con, PARTNER_FLAG_USB)) typec_set_mode(con->port, TYPEC_STATE_USB); } } /* Only notify USB controller if partner supports USB data */ - if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) + if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) u_role = USB_ROLE_NONE; ret = usb_role_switch_set_role(con->usb_role_sw, u_role); @@ -1124,21 +1152,18 @@ static void ucsi_partner_change(struct ucsi_connector *con) static int ucsi_check_connection(struct ucsi_connector *con) { - u8 prev_flags = con->status.flags; - u64 command; + u8 prev_state = UCSI_CONSTAT(con, CONNECTED); int ret; - command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(con->ucsi, command, &con->status, sizeof(con->status)); - if (ret < 0) { + ret = ucsi_get_connector_status(con, false); + if (ret) { dev_err(con->ucsi->dev, "GET_CONNECTOR_STATUS failed (%d)\n", ret); return ret; } - if (con->status.flags == prev_flags) - return 0; - - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { + if (UCSI_CONSTAT(con, CONNECTED)) { + if (prev_state) + return 0; ucsi_register_partner(con); ucsi_pwr_opmode_change(con); ucsi_partner_change(con); @@ -1194,7 +1219,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) work); struct ucsi *ucsi = con->ucsi; enum typec_role role; - u64 command; + u16 change; int ret; mutex_lock(&con->lock); @@ -1203,25 +1228,23 @@ static void ucsi_handle_connector_change(struct work_struct *work) dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n", __func__); - command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - - ret = ucsi_send_command_common(ucsi, command, &con->status, - sizeof(con->status), true); - if (ret < 0) { + ret = ucsi_get_connector_status(con, true); + if (ret) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", __func__, ret); clear_bit(EVENT_PENDING, &con->ucsi->flags); goto out_unlock; } - trace_ucsi_connector_change(con->num, &con->status); + trace_ucsi_connector_change(con->num, con); if (ucsi->ops->connector_status) ucsi->ops->connector_status(con); - role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); + change = UCSI_CONSTAT(con, CHANGE); + role = UCSI_CONSTAT(con, PWR_DIR); - if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) { + if (change & UCSI_CONSTAT_POWER_DIR_CHANGE) { typec_set_pwr_role(con->port, role); /* Complete pending power role swap */ @@ -1229,12 +1252,12 @@ static void ucsi_handle_connector_change(struct work_struct *work) complete(&con->complete); } - if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) { + if (change & UCSI_CONSTAT_CONNECT_CHANGE) { typec_set_pwr_role(con->port, role); ucsi_port_psy_changed(con); ucsi_partner_change(con); - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { + if (UCSI_CONSTAT(con, CONNECTED)) { ucsi_register_partner(con); ucsi_partner_task(con, ucsi_check_connection, 1, HZ); if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) @@ -1242,8 +1265,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) if (con->ucsi->cap.features & UCSI_CAP_CABLE_DETAILS) ucsi_partner_task(con, ucsi_check_cable, 1, HZ); - if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) == - UCSI_CONSTAT_PWR_OPMODE_PD) { + if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) { ucsi_partner_task(con, ucsi_register_partner_pdos, 1, HZ); ucsi_partner_task(con, ucsi_check_connector_capability, 1, HZ); } @@ -1252,11 +1274,10 @@ static void ucsi_handle_connector_change(struct work_struct *work) } } - if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE || - con->status.change & UCSI_CONSTAT_POWER_LEVEL_CHANGE) + if (change & (UCSI_CONSTAT_POWER_OPMODE_CHANGE | UCSI_CONSTAT_POWER_LEVEL_CHANGE)) ucsi_pwr_opmode_change(con); - if (con->partner && con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) { + if (con->partner && (change & UCSI_CONSTAT_PARTNER_CHANGE)) { ucsi_partner_change(con); /* Complete pending data role swap */ @@ -1264,10 +1285,10 @@ static void ucsi_handle_connector_change(struct work_struct *work) complete(&con->complete); } - if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) + if (change & UCSI_CONSTAT_CAM_CHANGE) ucsi_partner_task(con, ucsi_check_altmodes, 1, HZ); - if (con->status.change & UCSI_CONSTAT_BC_CHANGE) + if (change & UCSI_CONSTAT_BC_CHANGE) ucsi_port_psy_changed(con); out_unlock: @@ -1328,7 +1349,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi) mutex_lock(&ucsi->ppm_lock); - ret = ucsi->ops->read_cci(ucsi, &cci); + ret = ucsi->ops->poll_cci(ucsi, &cci); if (ret < 0) goto out; @@ -1346,7 +1367,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi) tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS); do { - ret = ucsi->ops->read_cci(ucsi, &cci); + ret = ucsi->ops->poll_cci(ucsi, &cci); if (ret < 0) goto out; if (cci & UCSI_CCI_COMMAND_COMPLETE) @@ -1375,7 +1396,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi) /* Give the PPM time to process a reset before reading CCI */ msleep(20); - ret = ucsi->ops->read_cci(ucsi, &cci); + ret = ucsi->ops->poll_cci(ucsi, &cci); if (ret) goto out; @@ -1427,7 +1448,7 @@ static int ucsi_dr_swap(struct typec_port *port, enum typec_data_role role) goto out_unlock; } - partner_type = UCSI_CONSTAT_PARTNER_TYPE(con->status.flags); + partner_type = UCSI_CONSTAT(con, PARTNER_TYPE); if ((partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP && role == TYPEC_DEVICE) || (partner_type == UCSI_CONSTAT_PARTNER_TYPE_UFP && @@ -1471,7 +1492,7 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) goto out_unlock; } - cur_role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); + cur_role = UCSI_CONSTAT(con, PWR_DIR); if (cur_role == role) goto out_unlock; @@ -1494,8 +1515,7 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role) mutex_lock(&con->lock); /* Something has gone wrong while swapping the role */ - if (UCSI_CONSTAT_PWR_OPMODE(con->status.flags) != - UCSI_CONSTAT_PWR_OPMODE_PD) { + if (UCSI_CONSTAT(con, PWR_OPMODE) != UCSI_CONSTAT_PWR_OPMODE_PD) { ucsi_reset_connector(con, true); ret = -EPROTO; } @@ -1563,19 +1583,18 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) if (ret < 0) goto out_unlock; - if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP) + if (UCSI_CONCAP(con, OPMODE_DRP)) cap->data = TYPEC_PORT_DRD; - else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DFP) + else if (UCSI_CONCAP(con, OPMODE_DFP)) cap->data = TYPEC_PORT_DFP; - else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_UFP) + else if (UCSI_CONCAP(con, OPMODE_UFP)) cap->data = TYPEC_PORT_UFP; - if ((con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) && - (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER)) + if (UCSI_CONCAP(con, PROVIDER) && UCSI_CONCAP(con, CONSUMER)) cap->type = TYPEC_PORT_DRP; - else if (con->cap.flags & UCSI_CONCAP_FLAG_PROVIDER) + else if (UCSI_CONCAP(con, PROVIDER)) cap->type = TYPEC_PORT_SRC; - else if (con->cap.flags & UCSI_CONCAP_FLAG_CONSUMER) + else if (UCSI_CONCAP(con, CONSUMER)) cap->type = TYPEC_PORT_SNK; cap->revision = ucsi->cap.typec_version; @@ -1583,11 +1602,18 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) cap->svdm_version = SVDM_VER_2_0; cap->prefer_role = TYPEC_NO_PREFERRED_ROLE; - if (con->cap.op_mode & UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY) + if (UCSI_CONCAP(con, OPMODE_AUDIO_ACCESSORY)) *accessory++ = TYPEC_ACCESSORY_AUDIO; - if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY) + if (UCSI_CONCAP(con, OPMODE_DEBUG_ACCESSORY)) *accessory = TYPEC_ACCESSORY_DEBUG; + if (UCSI_CONCAP_USB2_SUPPORT(con)) + cap->usb_capability |= USB_CAPABILITY_USB2; + if (UCSI_CONCAP_USB3_SUPPORT(con)) + cap->usb_capability |= USB_CAPABILITY_USB3; + if (UCSI_CONCAP_USB4_SUPPORT(con)) + cap->usb_capability |= USB_CAPABILITY_USB4; + cap->driver_data = con; cap->ops = &ucsi_ops; @@ -1617,19 +1643,16 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) } /* Get the status */ - command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); - ret = ucsi_send_command(ucsi, command, &con->status, sizeof(con->status)); - if (ret < 0) { + ret = ucsi_get_connector_status(con, false); + if (ret) { dev_err(ucsi->dev, "con%d: failed to get status\n", con->num); - ret = 0; goto out; } - ret = 0; /* ucsi_send_command() returns length on success */ if (ucsi->ops->connector_status) ucsi->ops->connector_status(con); - switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + switch (UCSI_CONSTAT(con, PARTNER_TYPE)) { case UCSI_CONSTAT_PARTNER_TYPE_UFP: case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP: u_role = USB_ROLE_HOST; @@ -1646,9 +1669,8 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) } /* Check if there is already something connected */ - if (con->status.flags & UCSI_CONSTAT_CONNECTED) { - typec_set_pwr_role(con->port, - !!(con->status.flags & UCSI_CONSTAT_PWR_DIR)); + if (UCSI_CONSTAT(con, CONNECTED)) { + typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR)); ucsi_register_partner(con); ucsi_pwr_opmode_change(con); ucsi_port_psy_changed(con); @@ -1659,7 +1681,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) } /* Only notify USB controller if partner supports USB data */ - if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) + if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) u_role = USB_ROLE_NONE; ret = usb_role_switch_set_role(con->usb_role_sw, u_role); @@ -1669,16 +1691,14 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) ret = 0; } - if (con->partner && - UCSI_CONSTAT_PWR_OPMODE(con->status.flags) == - UCSI_CONSTAT_PWR_OPMODE_PD) { + if (con->partner && UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) { ucsi_register_device_pdos(con); ucsi_get_src_pdos(con); ucsi_check_altmodes(con); ucsi_check_connector_capability(con); } - trace_ucsi_register_port(con->num, &con->status); + trace_ucsi_register_port(con->num, con); out: fwnode_handle_put(cap->fwnode); @@ -1761,7 +1781,8 @@ static int ucsi_init(struct ucsi *ucsi) /* Get PPM capabilities */ command = UCSI_GET_CAPABILITY; - ret = ucsi_send_command(ucsi, command, &ucsi->cap, sizeof(ucsi->cap)); + ret = ucsi_send_command(ucsi, command, &ucsi->cap, + BITS_TO_BYTES(UCSI_GET_CAPABILITY_SIZE)); if (ret < 0) goto err_reset; @@ -1807,11 +1828,11 @@ static int ucsi_init(struct ucsi *ucsi) err_unregister: for (con = connector; con->port; con++) { + if (con->wq) + destroy_workqueue(con->wq); ucsi_unregister_partner(con); ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); ucsi_unregister_port_psy(con); - if (con->wq) - destroy_workqueue(con->wq); usb_power_delivery_unregister_capabilities(con->port_sink_caps); con->port_sink_caps = NULL; @@ -1911,8 +1932,8 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops) struct ucsi *ucsi; if (!ops || - !ops->read_version || !ops->read_cci || !ops->read_message_in || - !ops->sync_control || !ops->async_control) + !ops->read_version || !ops->read_cci || !ops->poll_cci || + !ops->read_message_in || !ops->sync_control || !ops->async_control) return ERR_PTR(-EINVAL); ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL); @@ -1995,10 +2016,6 @@ void ucsi_unregister(struct ucsi *ucsi) for (i = 0; i < ucsi->cap.num_connectors; i++) { cancel_work_sync(&ucsi->connector[i].work); - ucsi_unregister_partner(&ucsi->connector[i]); - ucsi_unregister_altmodes(&ucsi->connector[i], - UCSI_RECIPIENT_CON); - ucsi_unregister_port_psy(&ucsi->connector[i]); if (ucsi->connector[i].wq) { struct ucsi_work *uwork; @@ -2014,6 +2031,11 @@ void ucsi_unregister(struct ucsi *ucsi) destroy_workqueue(ucsi->connector[i].wq); } + ucsi_unregister_partner(&ucsi->connector[i]); + ucsi_unregister_altmodes(&ucsi->connector[i], + UCSI_RECIPIENT_CON); + ucsi_unregister_port_psy(&ucsi->connector[i]); + usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_sink_caps); ucsi->connector[i].port_sink_caps = NULL; usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_source_caps); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 1cf5aad4c23a..3a2c1762bec1 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -4,6 +4,7 @@ #define __DRIVER_USB_TYPEC_UCSI_H #include <linux/bitops.h> +#include <linux/bitmap.h> #include <linux/completion.h> #include <linux/device.h> #include <linux/power_supply.h> @@ -29,6 +30,7 @@ struct dentry; #define UCSIv2_MESSAGE_OUT 272 /* UCSI versions */ +#define UCSI_VERSION_1_0 0x0100 #define UCSI_VERSION_1_1 0x0110 #define UCSI_VERSION_1_2 0x0120 #define UCSI_VERSION_2_0 0x0200 @@ -60,6 +62,7 @@ struct dentry; * struct ucsi_operations - UCSI I/O operations * @read_version: Read implemented UCSI version * @read_cci: Read CCI register + * @poll_cci: Read CCI register while polling with notifications disabled * @read_message_in: Read message data from UCSI * @sync_control: Blocking control operation * @async_control: Non-blocking control operation @@ -74,8 +77,10 @@ struct dentry; struct ucsi_operations { int (*read_version)(struct ucsi *ucsi, u16 *version); int (*read_cci)(struct ucsi *ucsi, u32 *cci); + int (*poll_cci)(struct ucsi *ucsi, u32 *cci); int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len); - int (*sync_control)(struct ucsi *ucsi, u64 command); + int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci, + void *data, size_t size); int (*async_control)(struct ucsi *ucsi, u64 command); bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig, struct ucsi_altmode *updated); @@ -95,26 +100,33 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); /* -------------------------------------------------------------------------- */ /* Commands */ -#define UCSI_PPM_RESET 0x01 -#define UCSI_CANCEL 0x02 -#define UCSI_CONNECTOR_RESET 0x03 -#define UCSI_ACK_CC_CI 0x04 -#define UCSI_SET_NOTIFICATION_ENABLE 0x05 -#define UCSI_GET_CAPABILITY 0x06 -#define UCSI_GET_CONNECTOR_CAPABILITY 0x07 -#define UCSI_SET_UOM 0x08 -#define UCSI_SET_UOR 0x09 -#define UCSI_SET_PDM 0x0a -#define UCSI_SET_PDR 0x0b -#define UCSI_GET_ALTERNATE_MODES 0x0c -#define UCSI_GET_CAM_SUPPORTED 0x0d -#define UCSI_GET_CURRENT_CAM 0x0e -#define UCSI_SET_NEW_CAM 0x0f -#define UCSI_GET_PDOS 0x10 -#define UCSI_GET_CABLE_PROPERTY 0x11 -#define UCSI_GET_CONNECTOR_STATUS 0x12 -#define UCSI_GET_ERROR_STATUS 0x13 -#define UCSI_GET_PD_MESSAGE 0x15 +#define UCSI_PPM_RESET 0x01 +#define UCSI_CANCEL 0x02 +#define UCSI_CONNECTOR_RESET 0x03 +#define UCSI_ACK_CC_CI 0x04 +#define UCSI_SET_NOTIFICATION_ENABLE 0x05 +#define UCSI_GET_CAPABILITY 0x06 +#define UCSI_GET_CAPABILITY_SIZE 128 +#define UCSI_GET_CONNECTOR_CAPABILITY 0x07 +#define UCSI_GET_CONNECTOR_CAPABILITY_SIZE 32 +#define UCSI_SET_CCOM 0x08 +#define UCSI_SET_UOR 0x09 +#define UCSI_SET_PDM 0x0a +#define UCSI_SET_PDR 0x0b +#define UCSI_GET_ALTERNATE_MODES 0x0c +#define UCSI_GET_CAM_SUPPORTED 0x0d +#define UCSI_GET_CURRENT_CAM 0x0e +#define UCSI_SET_NEW_CAM 0x0f +#define UCSI_GET_PDOS 0x10 +#define UCSI_GET_CABLE_PROPERTY 0x11 +#define UCSI_GET_CABLE_PROPERTY_SIZE 64 +#define UCSI_GET_CONNECTOR_STATUS 0x12 +#define UCSI_GET_CONNECTOR_STATUS_SIZE 152 +#define UCSI_GET_ERROR_STATUS 0x13 +#define UCSI_GET_PD_MESSAGE 0x15 +#define UCSI_GET_CAM_CS 0x18 +#define UCSI_SET_SINK_PATH 0x1c +#define UCSI_GET_LPM_PPM_INFO 0x22 #define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16) #define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff) @@ -126,7 +138,6 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_CONNECTOR_RESET_HARD_VER_1_0 BIT(23) /* Deprecated in v1.1 */ #define UCSI_CONNECTOR_RESET_DATA_VER_2_0 BIT(23) /* Redefined in v2.0 */ - /* ACK_CC_CI bits */ #define UCSI_ACK_CONNECTOR_CHANGE BIT(16) #define UCSI_ACK_COMMAND_COMPLETE BIT(17) @@ -250,42 +261,6 @@ struct ucsi_capability { u16 typec_version; } __packed; -/* Data structure filled by PPM in response to GET_CONNECTOR_CAPABILITY cmd. */ -struct ucsi_connector_capability { - u8 op_mode; -#define UCSI_CONCAP_OPMODE_DFP BIT(0) -#define UCSI_CONCAP_OPMODE_UFP BIT(1) -#define UCSI_CONCAP_OPMODE_DRP BIT(2) -#define UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY BIT(3) -#define UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY BIT(4) -#define UCSI_CONCAP_OPMODE_USB2 BIT(5) -#define UCSI_CONCAP_OPMODE_USB3 BIT(6) -#define UCSI_CONCAP_OPMODE_ALT_MODE BIT(7) - u32 flags; -#define UCSI_CONCAP_FLAG_PROVIDER BIT(0) -#define UCSI_CONCAP_FLAG_CONSUMER BIT(1) -#define UCSI_CONCAP_FLAG_SWAP_TO_DFP BIT(2) -#define UCSI_CONCAP_FLAG_SWAP_TO_UFP BIT(3) -#define UCSI_CONCAP_FLAG_SWAP_TO_SRC BIT(4) -#define UCSI_CONCAP_FLAG_SWAP_TO_SINK BIT(5) -#define UCSI_CONCAP_FLAG_EX_OP_MODE(_f_) \ - (((_f_) & GENMASK(13, 6)) >> 6) -#define UCSI_CONCAP_EX_OP_MODE_USB4_GEN2 BIT(0) -#define UCSI_CONCAP_EX_OP_MODE_EPR_SRC BIT(1) -#define UCSI_CONCAP_EX_OP_MODE_EPR_SINK BIT(2) -#define UCSI_CONCAP_EX_OP_MODE_USB4_GEN3 BIT(3) -#define UCSI_CONCAP_EX_OP_MODE_USB4_GEN4 BIT(4) -#define UCSI_CONCAP_FLAG_MISC_CAPS(_f_) \ - (((_f_) & GENMASK(17, 14)) >> 14) -#define UCSI_CONCAP_MISC_CAP_FW_UPDATE BIT(0) -#define UCSI_CONCAP_MISC_CAP_SECURITY BIT(1) -#define UCSI_CONCAP_FLAG_REV_CURR_PROT_SUPPORT BIT(18) -#define UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV(_f_) \ - (((_f_) & GENMASK(20, 19)) >> 19) -#define UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV_AS_BCD(_f_) \ - UCSI_SPEC_REVISION_TO_BCD(UCSI_CONCAP_FLAG_PARTNER_PD_MAJOR_REV(_f_)) -} __packed; - struct ucsi_altmode { u16 svid; u32 mid; @@ -311,49 +286,143 @@ struct ucsi_cable_property { u8 latency; } __packed; -/* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */ -struct ucsi_connector_status { - u16 change; -#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1) -#define UCSI_CONSTAT_POWER_OPMODE_CHANGE BIT(2) -#define UCSI_CONSTAT_PDOS_CHANGE BIT(5) -#define UCSI_CONSTAT_POWER_LEVEL_CHANGE BIT(6) -#define UCSI_CONSTAT_PD_RESET_COMPLETE BIT(7) -#define UCSI_CONSTAT_CAM_CHANGE BIT(8) -#define UCSI_CONSTAT_BC_CHANGE BIT(9) -#define UCSI_CONSTAT_PARTNER_CHANGE BIT(11) -#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12) -#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14) -#define UCSI_CONSTAT_ERROR BIT(15) - u16 flags; -#define UCSI_CONSTAT_PWR_OPMODE(_f_) ((_f_) & GENMASK(2, 0)) +/* Get Connector Capability Fields. */ +#define UCSI_CONCAP_OPMODE UCSI_DECLARE_BITFIELD(0, 0, 8) +#define UCSI_CONCAP_OPMODE_DFP UCSI_DECLARE_BITFIELD(0, 0, 1) +#define UCSI_CONCAP_OPMODE_UFP UCSI_DECLARE_BITFIELD(0, 1, 1) +#define UCSI_CONCAP_OPMODE_DRP UCSI_DECLARE_BITFIELD(0, 2, 1) +#define UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY UCSI_DECLARE_BITFIELD(0, 3, 1) +#define UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY UCSI_DECLARE_BITFIELD(0, 4, 1) +#define UCSI_CONCAP_OPMODE_USB2 UCSI_DECLARE_BITFIELD(0, 5, 1) +#define UCSI_CONCAP_OPMODE_USB3 UCSI_DECLARE_BITFIELD(0, 6, 1) +#define UCSI_CONCAP_OPMODE_ALT_MODE UCSI_DECLARE_BITFIELD(0, 7, 1) +#define UCSI_CONCAP_PROVIDER UCSI_DECLARE_BITFIELD(0, 8, 1) +#define UCSI_CONCAP_CONSUMER UCSI_DECLARE_BITFIELD(0, 9, 1) +#define UCSI_CONCAP_SWAP_TO_DFP_V1_1 UCSI_DECLARE_BITFIELD_V1_1(10, 1) +#define UCSI_CONCAP_SWAP_TO_UFP_V1_1 UCSI_DECLARE_BITFIELD_V1_1(11, 1) +#define UCSI_CONCAP_SWAP_TO_SRC_V1_1 UCSI_DECLARE_BITFIELD_V1_1(12, 1) +#define UCSI_CONCAP_SWAP_TO_SNK_V1_1 UCSI_DECLARE_BITFIELD_V1_1(13, 1) +#define UCSI_CONCAP_EXT_OPMODE_V2_0 UCSI_DECLARE_BITFIELD_V2_0(14, 8) +#define UCSI_CONCAP_EXT_OPMODE_USB4_GEN2_V2_0 UCSI_DECLARE_BITFIELD_V2_0(14, 1) +#define UCSI_CONCAP_EXT_OPMODE_EPR_SRC_V2_0 UCSI_DECLARE_BITFIELD_V2_0(15, 1) +#define UCSI_CONCAP_EXT_OPMODE_EPR_SINK_V2_0 UCSI_DECLARE_BITFIELD_V2_0(16, 1) +#define UCSI_CONCAP_EXT_OPMODE_USB4_GEN3_V2_0 UCSI_DECLARE_BITFIELD_V2_0(17, 1) +#define UCSI_CONCAP_EXT_OPMODE_USB4_GEN4_V2_0 UCSI_DECLARE_BITFIELD_V2_0(18, 1) +#define UCSI_CONCAP_MISC_V2_0 UCSI_DECLARE_BITFIELD_V2_0(22, 4) +#define UCSI_CONCAP_MISC_FW_UPDATE_V2_0 UCSI_DECLARE_BITFIELD_V2_0(22, 1) +#define UCSI_CONCAP_MISC_SECURITY_V2_0 UCSI_DECLARE_BITFIELD_V2_0(23, 1) +#define UCSI_CONCAP_REV_CURR_PROT_SUPPORT_V2_0 UCSI_DECLARE_BITFIELD_V2_0(26, 1) +#define UCSI_CONCAP_PARTNER_PD_REVISION_V2_1 UCSI_DECLARE_BITFIELD_V2_1(27, 2) + +/* Helpers for USB capability checks. */ +#define UCSI_CONCAP_USB2_SUPPORT(_con_) UCSI_CONCAP((_con_), OPMODE_USB2) +#define UCSI_CONCAP_USB3_SUPPORT(_con_) UCSI_CONCAP((_con_), OPMODE_USB3) +#define UCSI_CONCAP_USB4_SUPPORT(_con_) \ + ((_con_)->ucsi->version >= UCSI_VERSION_2_0 && \ + (UCSI_CONCAP((_con_), EXT_OPMODE_USB4_GEN2_V2_0) | \ + UCSI_CONCAP((_con_), EXT_OPMODE_USB4_GEN3_V2_0) | \ + UCSI_CONCAP((_con_), EXT_OPMODE_USB4_GEN4_V2_0))) + +/* Get Connector Status Fields. */ +#define UCSI_CONSTAT_CHANGE UCSI_DECLARE_BITFIELD(0, 0, 16) +#define UCSI_CONSTAT_PWR_OPMODE UCSI_DECLARE_BITFIELD(0, 16, 3) #define UCSI_CONSTAT_PWR_OPMODE_NONE 0 #define UCSI_CONSTAT_PWR_OPMODE_DEFAULT 1 #define UCSI_CONSTAT_PWR_OPMODE_BC 2 #define UCSI_CONSTAT_PWR_OPMODE_PD 3 #define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5 4 #define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0 5 -#define UCSI_CONSTAT_CONNECTED BIT(3) -#define UCSI_CONSTAT_PWR_DIR BIT(4) -#define UCSI_CONSTAT_PARTNER_FLAGS(_f_) (((_f_) & GENMASK(12, 5)) >> 5) -#define UCSI_CONSTAT_PARTNER_FLAG_USB 1 -#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE 2 -#define UCSI_CONSTAT_PARTNER_TYPE(_f_) (((_f_) & GENMASK(15, 13)) >> 13) +#define UCSI_CONSTAT_CONNECTED UCSI_DECLARE_BITFIELD(0, 19, 1) +#define UCSI_CONSTAT_PWR_DIR UCSI_DECLARE_BITFIELD(0, 20, 1) +#define UCSI_CONSTAT_PARTNER_FLAGS UCSI_DECLARE_BITFIELD(0, 21, 8) +#define UCSI_CONSTAT_PARTNER_FLAG_USB UCSI_DECLARE_BITFIELD(0, 21, 1) +#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE UCSI_DECLARE_BITFIELD(0, 22, 1) +#define UCSI_CONSTAT_PARTNER_FLAG_USB4_GEN3 UCSI_DECLARE_BITFIELD(0, 23, 1) +#define UCSI_CONSTAT_PARTNER_FLAG_USB4_GEN4 UCSI_DECLARE_BITFIELD(0, 24, 1) +#define UCSI_CONSTAT_PARTNER_TYPE UCSI_DECLARE_BITFIELD(0, 29, 3) #define UCSI_CONSTAT_PARTNER_TYPE_DFP 1 #define UCSI_CONSTAT_PARTNER_TYPE_UFP 2 -#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */ -#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */ +#define UCSI_CONSTAT_PARTNER_TYPE_CABLE 3 /* Powered Cable */ +#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP 4 /* Powered Cable */ #define UCSI_CONSTAT_PARTNER_TYPE_DEBUG 5 #define UCSI_CONSTAT_PARTNER_TYPE_AUDIO 6 - u32 request_data_obj; - - u8 pwr_status; -#define UCSI_CONSTAT_BC_STATUS(_p_) ((_p_) & GENMASK(1, 0)) +#define UCSI_CONSTAT_RDO UCSI_DECLARE_BITFIELD(0, 32, 32) +#define UCSI_CONSTAT_BC_STATUS UCSI_DECLARE_BITFIELD(0, 64, 2) #define UCSI_CONSTAT_BC_NOT_CHARGING 0 #define UCSI_CONSTAT_BC_NOMINAL_CHARGING 1 #define UCSI_CONSTAT_BC_SLOW_CHARGING 2 #define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3 -} __packed; +#define UCSI_CONSTAT_PD_VERSION_V1_2 UCSI_DECLARE_BITFIELD_V1_2(70, 16) + +/* Connector Status Change Bits. */ +#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1) +#define UCSI_CONSTAT_POWER_OPMODE_CHANGE BIT(2) +#define UCSI_CONSTAT_PDOS_CHANGE BIT(5) +#define UCSI_CONSTAT_POWER_LEVEL_CHANGE BIT(6) +#define UCSI_CONSTAT_PD_RESET_COMPLETE BIT(7) +#define UCSI_CONSTAT_CAM_CHANGE BIT(8) +#define UCSI_CONSTAT_BC_CHANGE BIT(9) +#define UCSI_CONSTAT_PARTNER_CHANGE BIT(11) +#define UCSI_CONSTAT_POWER_DIR_CHANGE BIT(12) +#define UCSI_CONSTAT_CONNECT_CHANGE BIT(14) +#define UCSI_CONSTAT_ERROR BIT(15) + +#define UCSI_DECLARE_BITFIELD_V1_1(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_1_1, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V1_2(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_1_2, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V2_0(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_2_0, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V2_1(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_2_1, (_offset_), (_size_)) +#define UCSI_DECLARE_BITFIELD_V3_0(_offset_, _size_) \ + UCSI_DECLARE_BITFIELD(UCSI_VERSION_3_0, (_offset_), (_size_)) + +#define UCSI_DECLARE_BITFIELD(_ver_, _offset_, _size_) \ +(struct ucsi_bitfield) { \ + .version = _ver_, \ + .offset = _offset_, \ + .size = _size_, \ +} + +struct ucsi_bitfield { + const u16 version; + const u8 offset; + const u8 size; +}; + +/** + * ucsi_bitfield_read - Read a field from UCSI command response + * @_map_: UCSI command response + * @_field_: The field offset in the response data structure + * @_ver_: UCSI version where the field was introduced + * + * Reads the fields in the command responses by first checking that the field is + * valid with the UCSI interface version that is used in the system. + * @_ver_ is the minimum UCSI version for the @_field_. If the UCSI interface is + * older than @_ver_, a warning is generated. + * + * Caveats: + * - Removed fields are not checked - @_ver_ is just the minimum UCSI version. + * + * Returns the value of @_field_, or 0 when the UCSI interface is older than + * @_ver_. + */ +#define ucsi_bitfield_read(_map_, _field_, _ver_) \ +({ \ + struct ucsi_bitfield f = (_field_); \ + WARN((_ver_) < f.version, \ + "Access to unsupported field at offset 0x%x (need version %04x)", \ + f.offset, f.version) ? 0 : \ + bitmap_read((_map_), f.offset, f.size); \ +}) + +/* Helpers to access cached command responses. */ +#define UCSI_CONCAP(_con_, _field_) \ + ucsi_bitfield_read((_con_)->cap, UCSI_CONCAP_##_field_, (_con_)->ucsi->version) + +#define UCSI_CONSTAT(_con_, _field_) \ + ucsi_bitfield_read((_con_)->status, UCSI_CONSTAT_##_field_, (_con_)->ucsi->version) /* -------------------------------------------------------------------------- */ @@ -433,8 +502,10 @@ struct ucsi_connector { struct typec_capability typec_cap; - struct ucsi_connector_status status; - struct ucsi_connector_capability cap; + /* Cached command responses. */ + DECLARE_BITMAP(cap, UCSI_GET_CONNECTOR_CAPABILITY_SIZE); + DECLARE_BITMAP(status, UCSI_GET_CONNECTOR_STATUS_SIZE); + struct power_supply *psy; struct power_supply_desc psy_desc; u32 rdo; @@ -463,7 +534,8 @@ void ucsi_altmode_update_active(struct ucsi_connector *con); int ucsi_resume(struct ucsi *ucsi); void ucsi_notify_common(struct ucsi *ucsi, u32 cci); -int ucsi_sync_control_common(struct ucsi *ucsi, u64 command); +int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci, + void *data, size_t size); #if IS_ENABLED(CONFIG_POWER_SUPPLY) int ucsi_register_port_psy(struct ucsi_connector *con); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 7a5dff8d9cc6..6b92f296e985 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -59,18 +59,13 @@ static int ucsi_acpi_read_version(struct ucsi *ucsi, u16 *version) static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci) { struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - int ret; - - ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); - if (ret) - return ret; memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci)); return 0; } -static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) +static int ucsi_acpi_poll_cci(struct ucsi *ucsi, u32 *cci) { struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); int ret; @@ -79,6 +74,13 @@ static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_le if (ret) return ret; + return ucsi_acpi_read_cci(ucsi, cci); +} + +static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) +{ + struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); + memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len); return 0; @@ -97,66 +99,34 @@ static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command) static const struct ucsi_operations ucsi_acpi_ops = { .read_version = ucsi_acpi_read_version, .read_cci = ucsi_acpi_read_cci, + .poll_cci = ucsi_acpi_poll_cci, .read_message_in = ucsi_acpi_read_message_in, .sync_control = ucsi_sync_control_common, .async_control = ucsi_acpi_async_control }; -static int -ucsi_zenbook_read_cci(struct ucsi *ucsi, u32 *cci) -{ - struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - int ret; - - if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) { - ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ); - if (ret) - return ret; - } - - memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci)); - - return 0; -} - -static int -ucsi_zenbook_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) -{ - struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - - /* UCSI_MESSAGE_IN is never read for PPM_RESET, return stored data */ - memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len); - - return 0; -} - -static const struct ucsi_operations ucsi_zenbook_ops = { - .read_version = ucsi_acpi_read_version, - .read_cci = ucsi_zenbook_read_cci, - .read_message_in = ucsi_zenbook_read_message_in, - .sync_control = ucsi_sync_control_common, - .async_control = ucsi_acpi_async_control -}; - -static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) +static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, + void *val, size_t len) { u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE | UCSI_CONSTAT_PDOS_CHANGE; struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - struct ucsi_connector_status *status; int ret; - ret = ucsi_acpi_read_message_in(ucsi, val, val_len); + ret = ucsi_sync_control_common(ucsi, command, cci, val, len); if (ret < 0) return ret; + if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS && + ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) && + ua->cmd & UCSI_GET_PDOS_SRC_PDOS) + ua->check_bogus_event = true; + if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS && ua->check_bogus_event) { - status = (struct ucsi_connector_status *)val; - /* Clear the bogus change */ - if (status->change == bogus_change) - status->change = 0; + if (*(u16 *)val == bogus_change) + *(u16 *)val = 0; ua->check_bogus_event = false; } @@ -164,27 +134,11 @@ static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_le return ret; } -static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command) -{ - struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); - int ret; - - ret = ucsi_sync_control_common(ucsi, command); - if (ret < 0) - return ret; - - if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS && - ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) && - ua->cmd & UCSI_GET_PDOS_SRC_PDOS) - ua->check_bogus_event = true; - - return ret; -} - static const struct ucsi_operations ucsi_gram_ops = { .read_version = ucsi_acpi_read_version, .read_cci = ucsi_acpi_read_cci, - .read_message_in = ucsi_gram_read_message_in, + .poll_cci = ucsi_acpi_poll_cci, + .read_message_in = ucsi_acpi_read_message_in, .sync_control = ucsi_gram_sync_control, .async_control = ucsi_acpi_async_control }; @@ -192,13 +146,6 @@ static const struct ucsi_operations ucsi_gram_ops = { static const struct dmi_system_id ucsi_acpi_quirks[] = { { .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), - }, - .driver_data = (void *)&ucsi_zenbook_ops, - }, - { - .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"), DMI_MATCH(DMI_PRODUCT_FAMILY, "LG gram PC"), DMI_MATCH(DMI_PRODUCT_NAME, "90Q"), @@ -320,7 +267,7 @@ static struct platform_driver ucsi_acpi_platform_driver = { .acpi_match_table = ACPI_PTR(ucsi_acpi_match), }, .probe = ucsi_acpi_probe, - .remove_new = ucsi_acpi_remove, + .remove = ucsi_acpi_remove, }; module_platform_driver(ucsi_acpi_platform_driver); diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index bccfc03b5986..f01e4ef6619d 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -222,7 +222,6 @@ struct ucsi_ccg { u16 fw_build; struct work_struct pm_work; - u64 last_cmd_sent; bool has_multiple_dp; struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES]; struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES]; @@ -538,9 +537,10 @@ static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc, * first and then vdo=0x3 */ static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc, - struct ucsi_altmode *alt) + struct ucsi_altmode *alt, + u64 command) { - switch (UCSI_ALTMODE_OFFSET(uc->last_cmd_sent)) { + switch (UCSI_ALTMODE_OFFSET(command)) { case NVIDIA_FTB_DP_OFFSET: if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DBG_VDO) alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DP_VDO | @@ -578,37 +578,11 @@ static int ucsi_ccg_read_cci(struct ucsi *ucsi, u32 *cci) static int ucsi_ccg_read_message_in(struct ucsi *ucsi, void *val, size_t val_len) { struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); - struct ucsi_capability *cap; - struct ucsi_altmode *alt; spin_lock(&uc->op_lock); memcpy(val, uc->op_data.message_in, val_len); spin_unlock(&uc->op_lock); - switch (UCSI_COMMAND(uc->last_cmd_sent)) { - case UCSI_GET_CURRENT_CAM: - if (uc->has_multiple_dp) - ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)val); - break; - case UCSI_GET_ALTERNATE_MODES: - if (UCSI_ALTMODE_RECIPIENT(uc->last_cmd_sent) == - UCSI_RECIPIENT_SOP) { - alt = val; - if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID) - ucsi_ccg_nvidia_altmode(uc, alt); - } - break; - case UCSI_GET_CAPABILITY: - if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) { - cap = val; - cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS; - } - break; - default: - break; - } - uc->last_cmd_sent = 0; - return 0; } @@ -628,7 +602,8 @@ static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command) return ccg_write(uc, reg, (u8 *)&command, sizeof(command)); } -static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command) +static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci, + void *data, size_t size) { struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); struct ucsi_connector *con; @@ -638,18 +613,45 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command) mutex_lock(&uc->lock); pm_runtime_get_sync(uc->dev); - uc->last_cmd_sent = command; - - if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM && + if (UCSI_COMMAND(command) == UCSI_SET_NEW_CAM && uc->has_multiple_dp) { - con_index = (uc->last_cmd_sent >> 16) & + con_index = (command >> 16) & UCSI_CMD_CONNECTOR_MASK; + if (con_index == 0) { + ret = -EINVAL; + goto err_put; + } con = &uc->ucsi->connector[con_index - 1]; ucsi_ccg_update_set_new_cam_cmd(uc, con, &command); } - ret = ucsi_sync_control_common(ucsi, command); + ret = ucsi_sync_control_common(ucsi, command, cci, data, size); + switch (UCSI_COMMAND(command)) { + case UCSI_GET_CURRENT_CAM: + if (uc->has_multiple_dp) + ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)data); + break; + case UCSI_GET_ALTERNATE_MODES: + if (UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_SOP) { + struct ucsi_altmode *alt = data; + + if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID) + ucsi_ccg_nvidia_altmode(uc, alt, command); + } + break; + case UCSI_GET_CAPABILITY: + if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) { + struct ucsi_capability *cap = data; + + cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS; + } + break; + default: + break; + } + +err_put: pm_runtime_put_sync(uc->dev); mutex_unlock(&uc->lock); @@ -659,6 +661,7 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command) static const struct ucsi_operations ucsi_ccg_ops = { .read_version = ucsi_ccg_read_version, .read_cci = ucsi_ccg_read_cci, + .poll_cci = ucsi_ccg_read_cci, .read_message_in = ucsi_ccg_read_message_in, .sync_control = ucsi_ccg_sync_control, .async_control = ucsi_ccg_async_control, @@ -1385,22 +1388,35 @@ static ssize_t do_flash_store(struct device *dev, if (!flash) return n; - if (uc->fw_build == 0x0) { - dev_err(dev, "fail to flash FW due to missing FW build info\n"); - return -EINVAL; - } - schedule_work(&uc->work); return n; } +static umode_t ucsi_ccg_attrs_is_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev)); + + if (!uc->fw_build) + return 0; + + return attr->mode; +} + static DEVICE_ATTR_WO(do_flash); static struct attribute *ucsi_ccg_attrs[] = { &dev_attr_do_flash.attr, NULL, }; -ATTRIBUTE_GROUPS(ucsi_ccg); +static struct attribute_group ucsi_ccg_attr_group = { + .attrs = ucsi_ccg_attrs, + .is_visible = ucsi_ccg_attrs_is_visible, +}; +static const struct attribute_group *ucsi_ccg_groups[] = { + &ucsi_ccg_attr_group, + NULL, +}; static int ucsi_ccg_probe(struct i2c_client *client) { @@ -1427,11 +1443,10 @@ static int ucsi_ccg_probe(struct i2c_client *client) uc->fw_build = CCG_FW_BUILD_NVIDIA_TEGRA; else if (!strcmp(fw_name, "nvidia,gpu")) uc->fw_build = CCG_FW_BUILD_NVIDIA; + if (!uc->fw_build) + dev_err(uc->dev, "failed to get FW build information\n"); } - if (!uc->fw_build) - dev_err(uc->dev, "failed to get FW build information\n"); - /* reset ccg device and initialize ucsi */ status = ucsi_ccg_init(uc); if (status < 0) { diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 03c0fa8edc8d..8af79101a2fc 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -172,12 +172,12 @@ static int pmic_glink_ucsi_async_control(struct ucsi *__ucsi, u64 command) static void pmic_glink_ucsi_update_connector(struct ucsi_connector *con) { struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi); - int i; - for (i = 0; i < PMIC_GLINK_MAX_PORTS; i++) { - if (ucsi->port_orientation[i]) - con->typec_cap.orientation_aware = true; - } + if (con->num > PMIC_GLINK_MAX_PORTS || + !ucsi->port_orientation[con->num - 1]) + return; + + con->typec_cap.orientation_aware = true; } static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con) @@ -185,7 +185,12 @@ static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con) struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(con->ucsi); int orientation; - if (con->num >= PMIC_GLINK_MAX_PORTS || + if (!UCSI_CONSTAT(con, CONNECTED)) { + typec_set_orientation(con->port, TYPEC_ORIENTATION_NONE); + return; + } + + if (con->num > PMIC_GLINK_MAX_PORTS || !ucsi->port_orientation[con->num - 1]) return; @@ -201,6 +206,7 @@ static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con) static const struct ucsi_operations pmic_glink_ucsi_ops = { .read_version = pmic_glink_ucsi_read_version, .read_cci = pmic_glink_ucsi_read_cci, + .poll_cci = pmic_glink_ucsi_read_cci, .read_message_in = pmic_glink_ucsi_read_message_in, .sync_control = ucsi_sync_control_common, .async_control = pmic_glink_ucsi_async_control, @@ -322,7 +328,6 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, struct pmic_glink_ucsi *ucsi; struct device *dev = &adev->dev; const struct of_device_id *match; - struct fwnode_handle *fwnode; int ret; ucsi = devm_kzalloc(dev, sizeof(*ucsi), GFP_KERNEL); @@ -354,14 +359,13 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, ucsi_set_drvdata(ucsi->ucsi, ucsi); - device_for_each_child_node(dev, fwnode) { + device_for_each_child_node_scoped(dev, fwnode) { struct gpio_desc *desc; u32 port; ret = fwnode_property_read_u32(fwnode, "reg", &port); if (ret < 0) { dev_err(dev, "missing reg property of %pOFn\n", fwnode); - fwnode_handle_put(fwnode); return ret; } @@ -376,11 +380,10 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev, if (!desc) continue; - if (IS_ERR(desc)) { - fwnode_handle_put(fwnode); + if (IS_ERR(desc)) return dev_err_probe(dev, PTR_ERR(desc), "unable to acquire orientation gpio\n"); - } + ucsi->port_orientation[port] = desc; } diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c index 6923fad31d79..57ef7d83a412 100644 --- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c +++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c @@ -424,6 +424,7 @@ static irqreturn_t ucsi_stm32g0_irq_handler(int irq, void *data) static const struct ucsi_operations ucsi_stm32g0_ops = { .read_version = ucsi_stm32g0_read_version, .read_cci = ucsi_stm32g0_read_cci, + .poll_cci = ucsi_stm32g0_read_cci, .read_message_in = ucsi_stm32g0_read_message_in, .sync_control = ucsi_sync_control_common, .async_control = ucsi_stm32g0_async_control, diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c index f3a5e24ea84d..d33e3f2dd1d8 100644 --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c @@ -71,9 +71,10 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command) return yoga_c630_ec_ucsi_write(uec->ec, (u8*)&command); } -const struct ucsi_operations yoga_c630_ucsi_ops = { +static const struct ucsi_operations yoga_c630_ucsi_ops = { .read_version = yoga_c630_ucsi_read_version, .read_cci = yoga_c630_ucsi_read_cci, + .poll_cci = yoga_c630_ucsi_read_cci, .read_message_in = yoga_c630_ucsi_read_message_in, .sync_control = ucsi_sync_control_common, .async_control = yoga_c630_ucsi_async_control, diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index 6338d818bc8b..9aa30ef76f3b 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -269,7 +269,7 @@ static int stub_recv_cmd_unlink(struct stub_device *sdev, return 0; } - usbip_dbg_stub_rx("seqnum %d is not pending\n", + usbip_dbg_stub_rx("seqnum %u is not pending\n", pdu->u.cmd_unlink.seqnum); /* diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index b1c2f6781cb3..7eb2e074012a 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -201,7 +201,7 @@ static int stub_send_ret_submit(struct stub_device *sdev) /* 1. setup usbip_header */ setup_ret_submit_pdu(&pdu_header, urb); - usbip_dbg_stub_tx("setup txdata seqnum: %d\n", + usbip_dbg_stub_tx("setup txdata seqnum: %u\n", pdu_header.base.seqnum); if (priv->sgl) { diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 8dac1edc74d4..e70fba9f55d6 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -11,6 +11,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/string_choices.h> #include "usbip_common.h" #include "vhci.h" @@ -675,7 +676,7 @@ static void vhci_tx_urb(struct urb *urb, struct vhci_device *vdev) spin_lock_irqsave(&vdev->priv_lock, flags); - priv->seqnum = atomic_inc_return(&vhci_hcd->seqnum); + priv->seqnum = (u32)atomic_inc_return(&vhci_hcd->seqnum); if (priv->seqnum == 0xffff) dev_info(&urb->dev->dev, "seqnum max\n"); @@ -1161,12 +1162,8 @@ static int vhci_setup(struct usb_hcd *hcd) hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS; } - /* - * Support SG. - * sg_tablesize is an arbitrary value to alleviate memory pressure - * on the host. - */ - hcd->self.sg_tablesize = 32; + /* accept arbitrarily long scatter-gather lists */ + hcd->self.sg_tablesize = ~0; hcd->self.no_sg_constraint = 1; return 0; @@ -1453,7 +1450,7 @@ static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state) if (connected > 0) { dev_info(&pdev->dev, "We have %d active connection%s. Do not suspend.\n", - connected, (connected == 1 ? "" : "s")); + connected, str_plural(connected)); ret = -EBUSY; } else { dev_info(&pdev->dev, "suspend vhci_hcd"); @@ -1487,7 +1484,7 @@ static int vhci_hcd_resume(struct platform_device *pdev) static struct platform_driver vhci_driver = { .probe = vhci_hcd_probe, - .remove_new = vhci_hcd_remove, + .remove = vhci_hcd_remove, .suspend = vhci_hcd_suspend, .resume = vhci_hcd_resume, .driver = { diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index 7f2d1c241559..a75f4a898a41 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -66,7 +66,7 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev, spin_unlock_irqrestore(&vdev->priv_lock, flags); if (!urb) { - pr_err("cannot find a urb of seqnum %u max seqnum %d\n", + pr_err("cannot find a urb of seqnum %u max seqnum %u\n", pdu->base.seqnum, atomic_read(&vhci_hcd->seqnum)); usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); @@ -162,10 +162,10 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev, * already received the result of its submit result and gave * back the URB. */ - pr_info("the urb (seqnum %d) was already given back\n", + pr_info("the urb (seqnum %u) was already given back\n", pdu->base.seqnum); } else { - usbip_dbg_vhci_rx("now giveback urb %d\n", pdu->base.seqnum); + usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum); /* If unlink is successful, status is -ECONNRESET */ urb->status = pdu->u.ret_unlink.status; diff --git a/drivers/usb/usbip/vudc_main.c b/drivers/usb/usbip/vudc_main.c index 8bee553e4894..993e721cb840 100644 --- a/drivers/usb/usbip/vudc_main.c +++ b/drivers/usb/usbip/vudc_main.c @@ -19,7 +19,7 @@ MODULE_PARM_DESC(num, "number of emulated controllers"); static struct platform_driver vudc_driver = { .probe = vudc_probe, - .remove_new = vudc_remove, + .remove = vudc_remove, .driver = { .name = GADGET_NAME, .dev_groups = vudc_groups, diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c index 907a43a00896..2aae3edfc813 100644 --- a/drivers/usb/usbip/vudc_sysfs.c +++ b/drivers/usb/usbip/vudc_sysfs.c @@ -67,7 +67,7 @@ out: * Exposes device descriptor from the gadget driver. */ static ssize_t dev_desc_read(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *out, + const struct bin_attribute *attr, char *out, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); @@ -88,7 +88,7 @@ unlock: spin_unlock_irqrestore(&udc->lock, flags); return ret; } -static BIN_ATTR_RO(dev_desc, sizeof(struct usb_device_descriptor)); +static const BIN_ATTR_RO(dev_desc, sizeof(struct usb_device_descriptor)); static ssize_t usbip_sockfd_store(struct device *dev, struct device_attribute *attr, @@ -252,14 +252,14 @@ static struct attribute *dev_attrs[] = { NULL, }; -static struct bin_attribute *dev_bin_attrs[] = { +static const struct bin_attribute *const dev_bin_attrs[] = { &bin_attr_dev_desc, NULL, }; static const struct attribute_group vudc_attr_group = { .attrs = dev_attrs, - .bin_attrs = dev_bin_attrs, + .bin_attrs_new = dev_bin_attrs, }; const struct attribute_group *vudc_groups[] = { diff --git a/drivers/usb/usbip/vudc_tx.c b/drivers/usb/usbip/vudc_tx.c index 3ccb17c3e840..30c11bf9f4e7 100644 --- a/drivers/usb/usbip/vudc_tx.c +++ b/drivers/usb/usbip/vudc_tx.c @@ -107,7 +107,7 @@ static int v_send_ret_submit(struct vudc *udc, struct urbp *urb_p) /* 1. setup usbip_header */ setup_ret_submit_pdu(&pdu_header, urb_p); - usbip_dbg_stub_tx("setup txdata seqnum: %d\n", + usbip_dbg_stub_tx("setup txdata seqnum: %u\n", pdu_header.base.seqnum); usbip_header_correct_endian(&pdu_header, 1); |
