diff options
Diffstat (limited to 'drivers/usb')
470 files changed, 18089 insertions, 21721 deletions
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 949eca0adebe..6f3c86149887 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_FHCI_HCD) += host/ obj-$(CONFIG_USB_XHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ -obj-$(CONFIG_USB_ISP1362_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_FSL_USB2) += host/ obj-$(CONFIG_USB_FOTG210_HCD) += host/ diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 47d06af33747..f1900c567ba4 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -582,7 +582,7 @@ struct cxacru_timer { static void cxacru_timeout_kill(struct timer_list *t) { - struct cxacru_timer *timer = from_timer(timer, t, timer); + struct cxacru_timer *timer = timer_container_of(timer, t, timer); usb_unlink_urb(timer->urb); } @@ -597,8 +597,8 @@ 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); - destroy_timer_on_stack(&timer.timer); + timer_delete_sync(&timer.timer); + timer_destroy_on_stack(&timer.timer); if (actual_length) *actual_length = urb->actual_length; @@ -980,25 +980,60 @@ cleanup: return ret; } -static void cxacru_upload_firmware(struct cxacru_data *instance, - const struct firmware *fw, - const struct firmware *bp) + +static int cxacru_find_firmware(struct cxacru_data *instance, + char *phase, const struct firmware **fw_p) { - int ret; + struct usbatm_data *usbatm = instance->usbatm; + struct device *dev = &usbatm->usb_intf->dev; + char buf[16]; + + sprintf(buf, "cxacru-%s.bin", phase); + usb_dbg(usbatm, "cxacru_find_firmware: looking for %s\n", buf); + + if (request_firmware(fw_p, buf, dev)) { + usb_dbg(usbatm, "no stage %s firmware found\n", phase); + return -ENOENT; + } + + usb_info(usbatm, "found firmware %s\n", buf); + + return 0; +} + +static int cxacru_heavy_init(struct usbatm_data *usbatm_instance, + struct usb_interface *usb_intf) +{ + const struct firmware *fw, *bp; + struct cxacru_data *instance = usbatm_instance->driver_data; struct usbatm_data *usbatm = instance->usbatm; struct usb_device *usb_dev = usbatm->usb_dev; __le16 signature[] = { usb_dev->descriptor.idVendor, usb_dev->descriptor.idProduct }; __le32 val; + int ret; - usb_dbg(usbatm, "%s\n", __func__); + ret = cxacru_find_firmware(instance, "fw", &fw); + if (ret) { + usb_warn(usbatm_instance, "firmware (cxacru-fw.bin) unavailable (system misconfigured?)\n"); + return ret; + } + + if (instance->modem_type->boot_rom_patch) { + ret = cxacru_find_firmware(instance, "bp", &bp); + if (ret) { + usb_warn(usbatm_instance, "boot ROM patch (cxacru-bp.bin) unavailable (system misconfigured?)\n"); + release_firmware(fw); + return ret; + } + } /* FirmwarePllFClkValue */ val = cpu_to_le32(instance->modem_type->pll_f_clk); ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLFCLK_ADDR, (u8 *) &val, 4); if (ret) { usb_err(usbatm, "FirmwarePllFClkValue failed: %d\n", ret); - return; + goto done; } /* FirmwarePllBClkValue */ @@ -1006,7 +1041,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance, ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, PLLBCLK_ADDR, (u8 *) &val, 4); if (ret) { usb_err(usbatm, "FirmwarePllBClkValue failed: %d\n", ret); - return; + goto done; } /* Enable SDRAM */ @@ -1014,7 +1049,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance, ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SDRAMEN_ADDR, (u8 *) &val, 4); if (ret) { usb_err(usbatm, "Enable SDRAM failed: %d\n", ret); - return; + goto done; } /* Firmware */ @@ -1022,7 +1057,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance, ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, FW_ADDR, fw->data, fw->size); if (ret) { usb_err(usbatm, "Firmware upload failed: %d\n", ret); - return; + goto done; } /* Boot ROM patch */ @@ -1031,7 +1066,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance, ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_ADDR, bp->data, bp->size); if (ret) { usb_err(usbatm, "Boot ROM patching failed: %d\n", ret); - return; + goto done; } } @@ -1039,7 +1074,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance, ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, SIG_ADDR, (u8 *) signature, 4); if (ret) { usb_err(usbatm, "Signature storing failed: %d\n", ret); - return; + goto done; } usb_info(usbatm, "starting device\n"); @@ -1051,7 +1086,7 @@ static void cxacru_upload_firmware(struct cxacru_data *instance, } if (ret) { usb_err(usbatm, "Passing control to firmware failed: %d\n", ret); - return; + goto done; } /* Delay to allow firmware to start up. */ @@ -1065,53 +1100,10 @@ static void cxacru_upload_firmware(struct cxacru_data *instance, ret = cxacru_cm(instance, CM_REQUEST_CARD_GET_STATUS, NULL, 0, NULL, 0); if (ret < 0) { usb_err(usbatm, "modem failed to initialize: %d\n", ret); - return; - } -} - -static int cxacru_find_firmware(struct cxacru_data *instance, - char *phase, const struct firmware **fw_p) -{ - struct usbatm_data *usbatm = instance->usbatm; - struct device *dev = &usbatm->usb_intf->dev; - char buf[16]; - - sprintf(buf, "cxacru-%s.bin", phase); - usb_dbg(usbatm, "cxacru_find_firmware: looking for %s\n", buf); - - if (request_firmware(fw_p, buf, dev)) { - usb_dbg(usbatm, "no stage %s firmware found\n", phase); - return -ENOENT; - } - - usb_info(usbatm, "found firmware %s\n", buf); - - return 0; -} - -static int cxacru_heavy_init(struct usbatm_data *usbatm_instance, - struct usb_interface *usb_intf) -{ - const struct firmware *fw, *bp; - struct cxacru_data *instance = usbatm_instance->driver_data; - int ret = cxacru_find_firmware(instance, "fw", &fw); - - if (ret) { - usb_warn(usbatm_instance, "firmware (cxacru-fw.bin) unavailable (system misconfigured?)\n"); - return ret; + goto done; } - if (instance->modem_type->boot_rom_patch) { - ret = cxacru_find_firmware(instance, "bp", &bp); - if (ret) { - usb_warn(usbatm_instance, "boot ROM patch (cxacru-bp.bin) unavailable (system misconfigured?)\n"); - release_firmware(fw); - return ret; - } - } - - cxacru_upload_firmware(instance, fw, bp); - +done: if (instance->modem_type->boot_rom_patch) release_firmware(bp); release_firmware(fw); @@ -1138,7 +1130,7 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, int ret; /* instance init */ - instance = kzalloc(sizeof(*instance), GFP_KERNEL); + instance = kzalloc_obj(*instance); if (!instance) return -ENOMEM; diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 973548b5c15c..9d28c652bcd8 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -13,6 +13,7 @@ #include <linux/device.h> #include <linux/errno.h> #include <linux/firmware.h> +#include <linux/hex.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -559,8 +560,9 @@ static void speedtch_check_status(struct work_struct *work) static void speedtch_status_poll(struct timer_list *t) { - struct speedtch_instance_data *instance = from_timer(instance, t, - status_check_timer); + struct speedtch_instance_data *instance = timer_container_of(instance, + t, + status_check_timer); schedule_work(&instance->status_check_work); @@ -573,8 +575,9 @@ static void speedtch_status_poll(struct timer_list *t) static void speedtch_resubmit_int(struct timer_list *t) { - struct speedtch_instance_data *instance = from_timer(instance, t, - resubmit_timer); + struct speedtch_instance_data *instance = timer_container_of(instance, + t, + resubmit_timer); struct urb *int_urb = instance->int_urb; int ret; @@ -612,7 +615,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 +691,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 +700,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); @@ -801,7 +804,7 @@ static int speedtch_bind(struct usbatm_data *usbatm, } } - instance = kzalloc(sizeof(*instance), GFP_KERNEL); + instance = kzalloc_obj(*instance); if (!instance) { ret = -ENOMEM; diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index cd0f7b4bd82a..f3ae72feb5bf 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/crc32.h> +#include <linux/hex.h> #include <linux/usb.h> #include <linux/firmware.h> #include <linux/ctype.h> @@ -2515,7 +2516,7 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, return ret; } - sc = kzalloc(sizeof(struct uea_softc), GFP_KERNEL); + sc = kzalloc_obj(struct uea_softc); if (!sc) return -ENOMEM; diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index d1e622bb1406..9600e1ec0993 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -804,7 +804,7 @@ static int usbatm_atm_open(struct atm_vcc *vcc) goto fail; } - new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL); + new = kzalloc_obj(struct usbatm_vcc_data); if (!new) { ret = -ENOMEM; goto fail; @@ -993,7 +993,7 @@ static int usbatm_heavy_init(struct usbatm_data *instance) static void usbatm_tasklet_schedule(struct timer_list *t) { - struct usbatm_channel *channel = from_timer(channel, t, delay); + struct usbatm_channel *channel = timer_container_of(channel, t, delay); tasklet_schedule(&channel->tasklet); } @@ -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 8f38e2c5369a..7ca6682c96c4 100644 --- a/drivers/usb/c67x00/c67x00-drv.c +++ b/drivers/usb/c67x00/c67x00-drv.c @@ -121,7 +121,7 @@ static int c67x00_drv_probe(struct platform_device *pdev) if (!pdata) return -ENODEV; - c67x00 = kzalloc(sizeof(*c67x00), GFP_KERNEL); + c67x00 = kzalloc_obj(*c67x00); if (!c67x00) return -ENOMEM; diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c index a09fa68a6ce7..a832f5696f2a 100644 --- a/drivers/usb/c67x00/c67x00-sched.c +++ b/drivers/usb/c67x00/c67x00-sched.c @@ -246,7 +246,7 @@ c67x00_ep_data_alloc(struct c67x00_hcd *c67x00, struct urb *urb) } /* Allocate and initialize a new c67x00 endpoint data structure */ - ep_data = kzalloc(sizeof(*ep_data), GFP_ATOMIC); + ep_data = kzalloc_obj(*ep_data, GFP_ATOMIC); if (!ep_data) return NULL; @@ -349,7 +349,7 @@ int c67x00_urb_enqueue(struct usb_hcd *hcd, int port = get_root_port(urb->dev)-1; /* Allocate and initialize urb private data */ - urbp = kzalloc(sizeof(*urbp), mem_flags); + urbp = kzalloc_obj(*urbp, mem_flags); if (!urbp) { ret = -ENOMEM; goto err_urbp; @@ -574,7 +574,7 @@ static int c67x00_create_td(struct c67x00_hcd *c67x00, struct urb *urb, || usb_pipeint(urb->pipe))) return -EMSGSIZE; /* Not really an error, but expected */ - td = kzalloc(sizeof(*td), GFP_ATOMIC); + td = kzalloc_obj(*td, GFP_ATOMIC); if (!td) return -ENOMEM; diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index fd1beb10bba7..8382231af357 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -903,7 +903,7 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req) buf = priv_req->aligned_buf; if (!buf || priv_req->request.length > buf->size) { - buf = kzalloc(sizeof(*buf), GFP_ATOMIC); + buf = kzalloc_obj(*buf, GFP_ATOMIC); if (!buf) return -ENOMEM; @@ -1963,6 +1963,7 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data) unsigned int bit; unsigned long reg; + local_bh_disable(); spin_lock_irqsave(&priv_dev->lock, flags); reg = readl(&priv_dev->regs->usb_ists); @@ -2004,6 +2005,7 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data) irqend: writel(~0, &priv_dev->regs->ep_ien); spin_unlock_irqrestore(&priv_dev->lock, flags); + local_bh_enable(); return ret; } @@ -2313,7 +2315,7 @@ struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep, struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); struct cdns3_request *priv_req; - priv_req = kzalloc(sizeof(*priv_req), gfp_flags); + priv_req = kzalloc_obj(*priv_req, gfp_flags); if (!priv_req) return NULL; @@ -2587,6 +2589,9 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep, struct cdns3_request *priv_req; int ret = 0; + if (!ep->desc) + return -ESHUTDOWN; + request->actual = 0; request->status = -EINPROGRESS; priv_req = to_cdns3_request(request); @@ -3249,7 +3254,6 @@ static void cdns3_gadget_exit(struct cdns *cdns) priv_dev = cdns->gadget_dev; - pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); usb_del_gadget(&priv_dev->gadget); @@ -3286,7 +3290,7 @@ static int cdns3_gadget_start(struct cdns *cdns) u32 max_speed; int ret; - priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL); + priv_dev = kzalloc_obj(*priv_dev); if (!priv_dev) return -ENOMEM; @@ -3427,6 +3431,7 @@ static int __cdns3_gadget_init(struct cdns *cdns) ret = cdns3_gadget_start(cdns); if (ret) { pm_runtime_put_sync(cdns->dev); + cdns_drd_gadget_off(cdns); return ret; } @@ -3468,7 +3473,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 +3481,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-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c index 3b3b3dc75f35..eb5760f75b9d 100644 --- a/drivers/usb/cdns3/cdns3-pci-wrap.c +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c @@ -97,11 +97,9 @@ static int cdns3_pci_probe(struct pci_dev *pdev, if (pci_is_enabled(func)) { wrap = pci_get_drvdata(func); } else { - wrap = kzalloc(sizeof(*wrap), GFP_KERNEL); - if (!wrap) { - pci_disable_device(pdev); + wrap = kzalloc_obj(*wrap); + if (!wrap) return -ENOMEM; - } } res = wrap->dev_res; @@ -160,7 +158,6 @@ static int cdns3_pci_probe(struct pci_dev *pdev, /* register platform device */ wrap->plat_dev = platform_device_register_full(&plat_info); if (IS_ERR(wrap->plat_dev)) { - pci_disable_device(pdev); err = PTR_ERR(wrap->plat_dev); kfree(wrap); return err; diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 59ec505e198a..735df88774e4 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -179,8 +179,6 @@ err_phy3_init: /** * cdns3_plat_remove() - unbind drd driver and clean up * @pdev: Pointer to Linux platform device - * - * Returns 0 on success otherwise negative errno */ static void cdns3_plat_remove(struct platform_device *pdev) { diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index 040bb91e9c01..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", }, @@ -237,6 +267,7 @@ static struct platform_driver cdns_ti_driver = { .driver = { .name = "cdns3-ti", .of_match_table = cdns_ti_of_match, + .pm = pm_ptr(&cdns_ti_pm_ops), }, }; diff --git a/drivers/usb/cdns3/cdns3-trace.h b/drivers/usb/cdns3/cdns3-trace.h index c4e542f1b9b7..21a6a2ce7a3d 100644 --- a/drivers/usb/cdns3/cdns3-trace.h +++ b/drivers/usb/cdns3/cdns3-trace.h @@ -283,39 +283,6 @@ TRACE_EVENT(cdns3_ep0_queue, __entry->length) ); -DECLARE_EVENT_CLASS(cdns3_stream_split_transfer_len, - TP_PROTO(struct cdns3_request *req), - TP_ARGS(req), - TP_STRUCT__entry( - __string(name, req->priv_ep->name) - __field(struct cdns3_request *, req) - __field(unsigned int, length) - __field(unsigned int, actual) - __field(unsigned int, stream_id) - ), - TP_fast_assign( - __assign_str(name); - __entry->req = req; - __entry->actual = req->request.length; - __entry->length = req->request.actual; - __entry->stream_id = req->request.stream_id; - ), - TP_printk("%s: req: %p,request length: %u actual length: %u SID: %u", - __get_str(name), __entry->req, __entry->length, - __entry->actual, __entry->stream_id) -); - -DEFINE_EVENT(cdns3_stream_split_transfer_len, cdns3_stream_transfer_split, - TP_PROTO(struct cdns3_request *req), - TP_ARGS(req) -); - -DEFINE_EVENT(cdns3_stream_split_transfer_len, - cdns3_stream_transfer_split_next_part, - TP_PROTO(struct cdns3_request *req), - TP_ARGS(req) -); - DECLARE_EVENT_CLASS(cdns3_log_aligned_request, TP_PROTO(struct cdns3_request *priv_req), TP_ARGS(priv_req), @@ -354,34 +321,6 @@ DEFINE_EVENT(cdns3_log_aligned_request, cdns3_prepare_aligned_request, TP_ARGS(req) ); -DECLARE_EVENT_CLASS(cdns3_log_map_request, - TP_PROTO(struct cdns3_request *priv_req), - TP_ARGS(priv_req), - TP_STRUCT__entry( - __string(name, priv_req->priv_ep->name) - __field(struct usb_request *, req) - __field(void *, buf) - __field(dma_addr_t, dma) - ), - TP_fast_assign( - __assign_str(name); - __entry->req = &priv_req->request; - __entry->buf = priv_req->request.buf; - __entry->dma = priv_req->request.dma; - ), - TP_printk("%s: req: %p, req buf %p, dma %p", - __get_str(name), __entry->req, __entry->buf, &__entry->dma - ) -); -DEFINE_EVENT(cdns3_log_map_request, cdns3_map_request, - TP_PROTO(struct cdns3_request *req), - TP_ARGS(req) -); -DEFINE_EVENT(cdns3_log_map_request, cdns3_mapped_request, - TP_PROTO(struct cdns3_request *req), - TP_ARGS(req) -); - DECLARE_EVENT_CLASS(cdns3_log_trb, TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb), TP_ARGS(priv_ep, trb), diff --git a/drivers/usb/cdns3/cdnsp-debug.h b/drivers/usb/cdns3/cdnsp-debug.h index cd138acdcce1..86860686d836 100644 --- a/drivers/usb/cdns3/cdnsp-debug.h +++ b/drivers/usb/cdns3/cdnsp-debug.h @@ -327,12 +327,13 @@ static inline const char *cdnsp_decode_trb(char *str, size_t size, u32 field0, case TRB_RESET_EP: case TRB_HALT_ENDPOINT: ret = scnprintf(str, size, - "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c", + "%s: ep%d%s(%d) ctx %08x%08x slot %ld flags %c %c", cdnsp_trb_type_string(type), ep_num, ep_id % 2 ? "out" : "in", TRB_TO_EP_INDEX(field3), field1, field0, TRB_TO_SLOT_ID(field3), - field3 & TRB_CYCLE ? 'C' : 'c'); + field3 & TRB_CYCLE ? 'C' : 'c', + field3 & TRB_ESP ? 'P' : 'p'); break; case TRB_STOP_RING: ret = scnprintf(str, size, diff --git a/drivers/usb/cdns3/cdnsp-ep0.c b/drivers/usb/cdns3/cdnsp-ep0.c index f317d3c84781..5cd9b898ce97 100644 --- a/drivers/usb/cdns3/cdnsp-ep0.c +++ b/drivers/usb/cdns3/cdnsp-ep0.c @@ -414,6 +414,7 @@ static int cdnsp_ep0_std_request(struct cdnsp_device *pdev, void cdnsp_setup_analyze(struct cdnsp_device *pdev) { struct usb_ctrlrequest *ctrl = &pdev->setup; + struct cdnsp_ep *pep; int ret = -EINVAL; u16 len; @@ -427,10 +428,21 @@ void cdnsp_setup_analyze(struct cdnsp_device *pdev) goto out; } + pep = &pdev->eps[0]; + /* Restore the ep0 to Stopped/Running state. */ - if (pdev->eps[0].ep_state & EP_HALTED) { - trace_cdnsp_ep0_halted("Restore to normal state"); - cdnsp_halt_endpoint(pdev, &pdev->eps[0], 0); + if (pep->ep_state & EP_HALTED) { + if (GET_EP_CTX_STATE(pep->out_ctx) == EP_STATE_HALTED) + cdnsp_halt_endpoint(pdev, pep, 0); + + /* + * Halt Endpoint Command for SSP2 for ep0 preserve current + * endpoint state and driver has to synchronize the + * software endpoint state with endpoint output context + * state. + */ + pep->ep_state &= ~EP_HALTED; + pep->ep_state |= EP_STOPPED; } /* diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c index 97edf767ecee..6b3815f8a6e5 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.c +++ b/drivers/usb/cdns3/cdnsp-gadget.c @@ -29,7 +29,8 @@ unsigned int cdnsp_port_speed(unsigned int port_status) { /*Detect gadget speed based on PORTSC register*/ - if (DEV_SUPERSPEEDPLUS(port_status)) + if (DEV_SUPERSPEEDPLUS(port_status) || + DEV_SSP_GEN1x2(port_status) || DEV_SSP_GEN2x2(port_status)) return USB_SPEED_SUPER_PLUS; else if (DEV_SUPERSPEED(port_status)) return USB_SPEED_SUPER; @@ -139,6 +140,26 @@ static void cdnsp_clear_port_change_bit(struct cdnsp_device *pdev, (portsc & PORT_CHANGE_BITS), port_regs); } +static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev) +{ + struct cdns *cdns = dev_get_drvdata(pdev->dev); + __le32 __iomem *reg; + void __iomem *base; + u32 offset = 0; + u32 val; + + if (!cdns->override_apb_timeout) + return; + + base = &pdev->cap_regs->hc_capbase; + offset = cdnsp_find_next_ext_cap(base, offset, D_XEC_PRE_REGS_CAP); + reg = base + offset + REG_CHICKEN_BITS_3_OFFSET; + + val = le32_to_cpu(readl(reg)); + val = CHICKEN_APB_TIMEOUT_SET(val, cdns->override_apb_timeout); + writel(cpu_to_le32(val), reg); +} + static void cdnsp_set_chicken_bits_2(struct cdnsp_device *pdev, u32 bit) { __le32 __iomem *reg; @@ -527,6 +548,7 @@ int cdnsp_wait_for_cmd_compl(struct cdnsp_device *pdev) dma_addr_t cmd_deq_dma; union cdnsp_trb *event; u32 cycle_state; + u32 retry = 10; int ret, val; u64 cmd_dma; u32 flags; @@ -558,8 +580,23 @@ int cdnsp_wait_for_cmd_compl(struct cdnsp_device *pdev) flags = le32_to_cpu(event->event_cmd.flags); /* Check the owner of the TRB. */ - if ((flags & TRB_CYCLE) != cycle_state) + if ((flags & TRB_CYCLE) != cycle_state) { + /* + * Give some extra time to get chance controller + * to finish command before returning error code. + * Checking CMD_RING_BUSY is not sufficient because + * this bit is cleared to '0' when the Command + * Descriptor has been executed by controller + * and not when command completion event has + * be added to event ring. + */ + if (retry--) { + udelay(20); + continue; + } + return -EINVAL; + } cmd_dma = le64_to_cpu(event->event_cmd.cmd_trb); @@ -1065,7 +1102,7 @@ static struct usb_request *cdnsp_gadget_ep_alloc_request(struct usb_ep *ep, struct cdnsp_ep *pep = to_cdnsp_ep(ep); struct cdnsp_request *preq; - preq = kzalloc(sizeof(*preq), gfp_flags); + preq = kzalloc_obj(*preq, gfp_flags); if (!preq) return NULL; @@ -1773,6 +1810,8 @@ static void cdnsp_get_rev_cap(struct cdnsp_device *pdev) reg += cdnsp_find_next_ext_cap(reg, 0, RTL_REV_CAP); pdev->rev_cap = reg; + pdev->rtl_revision = readl(&pdev->rev_cap->rtl_revision); + dev_info(pdev->dev, "Rev: %08x/%08x, eps: %08x, buff: %08x/%08x\n", readl(&pdev->rev_cap->ctrl_revision), readl(&pdev->rev_cap->rtl_revision), @@ -1798,6 +1837,15 @@ static int cdnsp_gen_setup(struct cdnsp_device *pdev) pdev->hci_version = HC_VERSION(pdev->hcc_params); pdev->hcc_params = readl(&pdev->cap_regs->hcc_params); + /* + * Override the APB timeout value to give the controller more time for + * enabling UTMI clock and synchronizing APB and UTMI clock domains. + * This fix is platform specific and is required to fixes issue with + * reading incorrect value from PORTSC register after resuming + * from L1 state. + */ + cdnsp_set_apb_timeout_value(pdev); + cdnsp_get_rev_cap(pdev); /* Make sure the Device Controller is halted. */ @@ -1857,7 +1905,7 @@ static int __cdnsp_gadget_init(struct cdns *cdns) cdns_drd_gadget_on(cdns); - pdev = kzalloc(sizeof(*pdev), GFP_KERNEL); + pdev = kzalloc_obj(*pdev); if (!pdev) return -ENOMEM; @@ -1928,7 +1976,10 @@ static int __cdnsp_gadget_init(struct cdns *cdns) return 0; del_gadget: - usb_del_gadget_udc(&pdev->gadget); + usb_del_gadget(&pdev->gadget); + cdnsp_gadget_free_endpoints(pdev); + usb_put_gadget(&pdev->gadget); + goto halt_pdev; free_endpoints: cdnsp_gadget_free_endpoints(pdev); halt_pdev: @@ -1948,10 +1999,10 @@ static void cdnsp_gadget_exit(struct cdns *cdns) struct cdnsp_device *pdev = cdns->gadget_dev; devm_free_irq(pdev->dev, cdns->dev_irq, pdev); - pm_runtime_mark_last_busy(cdns->dev); pm_runtime_put_autosuspend(cdns->dev); - usb_del_gadget_udc(&pdev->gadget); + usb_del_gadget(&pdev->gadget); cdnsp_gadget_free_endpoints(pdev); + usb_put_gadget(&pdev->gadget); cdnsp_mem_cleanup(pdev); kfree(pdev); cdns->gadget_dev = NULL; @@ -1974,7 +2025,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-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h index 84887dfea763..a91cca509db0 100644 --- a/drivers/usb/cdns3/cdnsp-gadget.h +++ b/drivers/usb/cdns3/cdnsp-gadget.h @@ -285,11 +285,15 @@ struct cdnsp_port_regs { #define XDEV_HS (0x3 << 10) #define XDEV_SS (0x4 << 10) #define XDEV_SSP (0x5 << 10) +#define XDEV_SSP1x2 (0x6 << 10) +#define XDEV_SSP2x2 (0x7 << 10) #define DEV_UNDEFSPEED(p) (((p) & DEV_SPEED_MASK) == (0x0 << 10)) #define DEV_FULLSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_FS) #define DEV_HIGHSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_HS) #define DEV_SUPERSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_SS) #define DEV_SUPERSPEEDPLUS(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP) +#define DEV_SSP_GEN1x2(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP1x2) +#define DEV_SSP_GEN2x2(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP2x2) #define DEV_SUPERSPEED_ANY(p) (((p) & DEV_SPEED_MASK) >= XDEV_SS) #define DEV_PORT_SPEED(p) (((p) >> 10) & 0x0f) /* Port Link State Write Strobe - set this when changing link state */ @@ -520,6 +524,9 @@ struct cdnsp_rev_cap { #define REG_CHICKEN_BITS_2_OFFSET 0x48 #define CHICKEN_XDMA_2_TP_CACHE_DIS BIT(28) +#define REG_CHICKEN_BITS_3_OFFSET 0x4C +#define CHICKEN_APB_TIMEOUT_SET(p, val) (((p) & ~GENMASK(21, 0)) | (val)) + /* XBUF Extended Capability ID. */ #define XBUF_CAP_ID 0xCB #define XBUF_RX_TAG_MASK_0_OFFSET 0x1C @@ -980,6 +987,12 @@ enum cdnsp_setup_dev { #define STREAM_ID_FOR_TRB(p) ((((p)) << 16) & GENMASK(31, 16)) #define SCT_FOR_TRB(p) (((p) << 1) & 0x7) +/* + * Halt Endpoint Command TRB field. + * The ESP bit only exists in the SSP2 controller. + */ +#define TRB_ESP BIT(9) + /* Link TRB specific fields. */ #define TRB_TC BIT(1) @@ -1357,6 +1370,7 @@ struct cdnsp_port { * @rev_cap: Controller Capabilities Registers. * @hcs_params1: Cached register copies of read-only HCSPARAMS1 * @hcc_params: Cached register copies of read-only HCCPARAMS1 + * @rtl_revision: Cached controller rtl revision. * @setup: Temporary buffer for setup packet. * @ep0_preq: Internal allocated request used during enumeration. * @ep0_stage: ep0 stage during enumeration process. @@ -1411,6 +1425,8 @@ struct cdnsp_device { __u32 hcs_params1; __u32 hcs_params3; __u32 hcc_params; + #define RTL_REVISION_NEW_LPM 0x2700 + __u32 rtl_revision; /* Lock used in interrupt thread context. */ spinlock_t lock; struct usb_ctrlrequest setup; diff --git a/drivers/usb/cdns3/cdnsp-mem.c b/drivers/usb/cdns3/cdnsp-mem.c index 97866bfb2da9..a2a1b21f2ef8 100644 --- a/drivers/usb/cdns3/cdnsp-mem.c +++ b/drivers/usb/cdns3/cdnsp-mem.c @@ -35,7 +35,7 @@ static struct cdnsp_segment *cdnsp_segment_alloc(struct cdnsp_device *pdev, dma_addr_t dma; int i; - seg = kzalloc(sizeof(*seg), flags); + seg = kzalloc_obj(*seg, flags); if (!seg) return NULL; @@ -376,7 +376,7 @@ static struct cdnsp_ring *cdnsp_ring_alloc(struct cdnsp_device *pdev, struct cdnsp_ring *ring; int ret; - ring = kzalloc(sizeof *(ring), flags); + ring = kzalloc_obj(*(ring), flags); if (!ring) return NULL; @@ -575,9 +575,8 @@ int cdnsp_alloc_stream_info(struct cdnsp_device *pdev, stream_info->num_stream_ctxs = num_stream_ctxs; /* Initialize the array of virtual pointers to stream rings. */ - stream_info->stream_rings = kcalloc(num_streams, - sizeof(struct cdnsp_ring *), - GFP_ATOMIC); + stream_info->stream_rings = kzalloc_objs(struct cdnsp_ring *, + num_streams, GFP_ATOMIC); if (!stream_info->stream_rings) return -ENOMEM; diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c index a51144504ff3..566d94e49102 100644 --- a/drivers/usb/cdns3/cdnsp-pci.c +++ b/drivers/usb/cdns3/cdnsp-pci.c @@ -28,6 +28,8 @@ #define PCI_DRIVER_NAME "cdns-pci-usbssp" #define PLAT_DRIVER_NAME "cdns-usbssp" +#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20 + static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev) { /* @@ -80,10 +82,10 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, if (pci_is_enabled(func)) { cdnsp = pci_get_drvdata(func); } else { - cdnsp = kzalloc(sizeof(*cdnsp), GFP_KERNEL); + cdnsp = kzalloc_obj(*cdnsp); if (!cdnsp) { ret = -ENOMEM; - goto disable_pci; + goto put_pci; } } @@ -139,6 +141,14 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, cdnsp->otg_irq = pdev->irq; } + /* + * Cadence PCI based platform require some longer timeout for APB + * to fixes domain clock synchronization issue after resuming + * controller from L1 state. + */ + cdnsp->override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE; + pci_set_drvdata(pdev, cdnsp); + if (pci_is_enabled(func)) { cdnsp->dev = dev; cdnsp->gadget_init = cdnsp_gadget_init; @@ -148,8 +158,6 @@ static int cdnsp_pci_probe(struct pci_dev *pdev, goto free_cdnsp; } - pci_set_drvdata(pdev, cdnsp); - device_wakeup_enable(&pdev->dev); if (pci_dev_run_wake(pdev)) pm_runtime_put_noidle(&pdev->dev); @@ -160,9 +168,6 @@ free_cdnsp: if (!pci_is_enabled(func)) kfree(cdnsp); -disable_pci: - pci_disable_device(pdev); - put_pci: pci_dev_put(func); diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index 46852529499d..0758f171f73e 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -308,7 +308,8 @@ static bool cdnsp_ring_ep_doorbell(struct cdnsp_device *pdev, writel(db_value, reg_addr); - cdnsp_force_l0_go(pdev); + if (pdev->rtl_revision < RTL_REVISION_NEW_LPM) + cdnsp_force_l0_go(pdev); /* Doorbell was set. */ return true; @@ -771,7 +772,9 @@ static int cdnsp_update_port_id(struct cdnsp_device *pdev, u32 port_id) } if (port_id != old_port) { - cdnsp_disable_slot(pdev); + if (pdev->slot_id) + cdnsp_disable_slot(pdev); + pdev->active_port = port; cdnsp_enable_slot(pdev); } @@ -2482,7 +2485,8 @@ void cdnsp_queue_halt_endpoint(struct cdnsp_device *pdev, unsigned int ep_index) { cdnsp_queue_command(pdev, 0, 0, 0, TRB_TYPE(TRB_HALT_ENDPOINT) | SLOT_ID_FOR_TRB(pdev->slot_id) | - EP_ID_FOR_TRB(ep_index)); + EP_ID_FOR_TRB(ep_index) | + (!ep_index ? TRB_ESP : 0)); } void cdnsp_force_header_wakeup(struct cdnsp_device *pdev, int intf_num) diff --git a/drivers/usb/cdns3/cdnsp-trace.h b/drivers/usb/cdns3/cdnsp-trace.h index f2bcf77a5d0a..9b33a736c3de 100644 --- a/drivers/usb/cdns3/cdnsp-trace.h +++ b/drivers/usb/cdns3/cdnsp-trace.h @@ -178,11 +178,6 @@ DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_set_config, TP_ARGS(msg) ); -DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep0_halted, - TP_PROTO(char *msg), - TP_ARGS(msg) -); - DEFINE_EVENT(cdnsp_log_simple, cdnsp_ep_halt, TP_PROTO(char *msg), TP_ARGS(msg) @@ -399,11 +394,6 @@ DEFINE_EVENT(cdnsp_log_trb, cdnsp_cmd_timeout, TP_ARGS(ring, trb) ); -DEFINE_EVENT(cdnsp_log_trb, cdnsp_defered_event, - TP_PROTO(struct cdnsp_ring *ring, struct cdnsp_generic_trb *trb), - TP_ARGS(ring, trb) -); - DECLARE_EVENT_CLASS(cdnsp_log_pdev, TP_PROTO(struct cdnsp_device *pdev), TP_ARGS(pdev), @@ -433,16 +423,6 @@ DEFINE_EVENT(cdnsp_log_pdev, cdnsp_alloc_priv_device, TP_ARGS(vdev) ); -DEFINE_EVENT(cdnsp_log_pdev, cdnsp_free_priv_device, - TP_PROTO(struct cdnsp_device *vdev), - TP_ARGS(vdev) -); - -DEFINE_EVENT(cdnsp_log_pdev, cdnsp_setup_device, - TP_PROTO(struct cdnsp_device *vdev), - TP_ARGS(vdev) -); - DEFINE_EVENT(cdnsp_log_pdev, cdnsp_setup_addressable_priv_device, TP_PROTO(struct cdnsp_device *vdev), TP_ARGS(vdev) @@ -575,11 +555,6 @@ DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_stop_ep, TP_ARGS(ctx) ); -DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_flush_ep, - TP_PROTO(struct cdnsp_ep_ctx *ctx), - TP_ARGS(ctx) -); - DEFINE_EVENT(cdnsp_log_ep_ctx, cdnsp_handle_cmd_set_deq_ep, TP_PROTO(struct cdnsp_ep_ctx *ctx), TP_ARGS(ctx) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 98980a23e1c2..f0e32227c0b7 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -524,11 +524,12 @@ 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 (power_lost) { if (!cdns->role_sw) { real_role = cdns_hw_role_state_machine(cdns); if (real_role != cdns->role) { @@ -550,8 +551,8 @@ int cdns_resume(struct cdns *cdns) } } - if (cdns->roles[cdns->role]->resume) - cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns)); + if (!role_changed && cdns->roles[cdns->role]->resume) + 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..801be9e61340 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 @@ -79,6 +79,8 @@ struct cdns3_platform_data { * @pdata: platform data from glue layer * @lock: spinlock structure * @xhci_plat_data: xhci private data structure pointer + * @override_apb_timeout: hold value of APB timeout. For value 0 the default + * value in CHICKEN_BITS_3 will be preserved. * @gadget_init: pointer to gadget initialization function */ struct cdns { @@ -117,6 +119,7 @@ struct cdns { struct cdns3_platform_data *pdata; spinlock_t lock; struct xhci_plat_priv *xhci_plat_data; + u32 override_apb_timeout; int (*gadget_init)(struct cdns *cdns); }; 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_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 1a7fc638213e..56d2ba824a0b 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -1,11 +1,13 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2025 NXP * Copyright (C) 2012 Marek Vasut <marex@denx.de> * on behalf of DENX Software Engineering GmbH */ #include <linux/module.h> +#include <linux/irq.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> @@ -77,6 +79,14 @@ static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = { CI_HDRC_HAS_PORTSC_PEC_MISSED, }; +static const struct ci_hdrc_imx_platform_flag imx95_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | CI_HDRC_OUT_BAND_WAKEUP, +}; + +static const struct ci_hdrc_imx_platform_flag s32g_usb_data = { + .flags = CI_HDRC_DISABLE_HOST_STREAMING, +}; + static const struct of_device_id ci_hdrc_imx_dt_ids[] = { { .compatible = "fsl,imx23-usb", .data = &imx23_usb_data}, { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, @@ -88,6 +98,8 @@ static const struct of_device_id ci_hdrc_imx_dt_ids[] = { { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data}, { .compatible = "fsl,imx7ulp-usb", .data = &imx7ulp_usb_data}, { .compatible = "fsl,imx8ulp-usb", .data = &imx8ulp_usb_data}, + { .compatible = "fsl,imx95-usb", .data = &imx95_usb_data}, + { .compatible = "nxp,s32g2-usb", .data = &s32g_usb_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); @@ -98,6 +110,7 @@ struct ci_hdrc_imx_data { struct clk *clk; struct clk *clk_wakeup; struct imx_usbmisc_data *usbmisc_data; + int wakeup_irq; bool supports_runtime_pm; bool override_phy_control; bool in_lpm; @@ -329,6 +342,12 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) if (ci->usb_phy) schedule_work(&ci->usb_phy->chg_work); break; + case CI_HDRC_CONTROLLER_PULLUP_EVENT: + if (ci->role == CI_ROLE_GADGET && + ci->gadget.speed == USB_SPEED_HIGH) + imx_usbmisc_pullup(data->usbmisc_data, + ci->gadget.connected); + break; default: break; } @@ -336,6 +355,23 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event) return ret; } +static irqreturn_t ci_wakeup_irq_handler(int irq, void *data) +{ + struct ci_hdrc_imx_data *imx_data = data; + + disable_irq_nosync(irq); + pm_runtime_resume(&imx_data->ci_pdev->dev); + + return IRQ_HANDLED; +} + +static void ci_hdrc_imx_disable_regulator(void *arg) +{ + struct ci_hdrc_imx_data *data = arg; + + regulator_disable(data->hsic_pad_regulator); +} + static int ci_hdrc_imx_probe(struct platform_device *pdev) { struct ci_hdrc_imx_data *data; @@ -349,6 +385,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) const struct ci_hdrc_imx_platform_flag *imx_platform_flag; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; + const char *irq_name; imx_platform_flag = of_device_get_match_data(&pdev->dev); @@ -394,6 +431,13 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) "Failed to enable HSIC pad regulator\n"); goto err_put; } + ret = devm_add_action_or_reset(dev, + ci_hdrc_imx_disable_regulator, data); + if (ret) { + dev_err(dev, + "Failed to add regulator devm action\n"); + goto err_put; + } } } @@ -432,11 +476,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) ret = imx_get_clks(dev); if (ret) - goto disable_hsic_regulator; + goto qos_remove_request; ret = imx_prepare_enable_clks(dev); if (ret) - goto disable_hsic_regulator; + goto qos_remove_request; ret = clk_prepare_enable(data->clk_wakeup); if (ret) @@ -470,16 +514,36 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) { pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL; data->override_phy_control = true; - usb_phy_init(pdata.usb_phy); + ret = usb_phy_init(pdata.usb_phy); + if (ret) { + dev_err(dev, "Failed to init phy\n"); + goto err_clk; + } } if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) data->supports_runtime_pm = true; + data->wakeup_irq = platform_get_irq_optional(pdev, 1); + if (data->wakeup_irq > 0) { + irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", pdata.name); + if (!irq_name) { + dev_err_probe(dev, -ENOMEM, "failed to create irq_name\n"); + goto err_clk; + } + + ret = devm_request_threaded_irq(dev, data->wakeup_irq, + NULL, ci_wakeup_irq_handler, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + irq_name, data); + if (ret) + goto err_clk; + } + ret = imx_usbmisc_init(data->usbmisc_data); if (ret) { dev_err(dev, "usbmisc init failed, ret=%d\n", ret); - goto err_clk; + goto phy_shutdown; } data->ci_pdev = ci_hdrc_add_device(dev, @@ -488,7 +552,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) if (IS_ERR(data->ci_pdev)) { ret = PTR_ERR(data->ci_pdev); dev_err_probe(dev, ret, "ci_hdrc_add_device failed\n"); - goto err_clk; + goto phy_shutdown; } if (data->usbmisc_data) { @@ -522,19 +586,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) disable_device: ci_hdrc_remove_device(data->ci_pdev); +phy_shutdown: + if (data->override_phy_control) + usb_phy_shutdown(data->phy); err_clk: clk_disable_unprepare(data->clk_wakeup); err_wakeup_clk: imx_disable_unprepare_clks(dev); -disable_hsic_regulator: - if (data->hsic_pad_regulator) - /* don't overwrite original ret (cf. EPROBE_DEFER) */ - regulator_disable(data->hsic_pad_regulator); +qos_remove_request: 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); + if (data->usbmisc_data) + put_device(data->usbmisc_data->dev); return ret; } @@ -556,10 +621,9 @@ static void ci_hdrc_imx_remove(struct platform_device *pdev) clk_disable_unprepare(data->clk_wakeup); if (data->plat_data->flags & CI_HDRC_PMQOS) cpu_latency_qos_remove_request(&data->pm_qos_req); - if (data->hsic_pad_regulator) - regulator_disable(data->hsic_pad_regulator); } - put_device(data->usbmisc_data->dev); + if (data->usbmisc_data) + put_device(data->usbmisc_data->dev); } static void ci_hdrc_imx_shutdown(struct platform_device *pdev) @@ -584,6 +648,10 @@ static int imx_controller_suspend(struct device *dev, } imx_disable_unprepare_clks(dev); + + if (data->wakeup_irq > 0) + enable_irq(data->wakeup_irq); + if (data->plat_data->flags & CI_HDRC_PMQOS) cpu_latency_qos_remove_request(&data->pm_qos_req); @@ -608,6 +676,10 @@ static int imx_controller_resume(struct device *dev, if (data->plat_data->flags & CI_HDRC_PMQOS) cpu_latency_qos_add_request(&data->pm_qos_req, 0); + if (data->wakeup_irq > 0 && + !irqd_irq_disabled(irq_get_irq_data(data->wakeup_irq))) + disable_irq_nosync(data->wakeup_irq); + ret = imx_prepare_enable_clks(dev); if (ret) return ret; @@ -643,6 +715,14 @@ static int ci_hdrc_imx_suspend(struct device *dev) return ret; pinctrl_pm_select_sleep_state(dev); + + if (data->wakeup_irq > 0 && device_may_wakeup(dev)) { + enable_irq_wake(data->wakeup_irq); + + if (data->plat_data->flags & CI_HDRC_OUT_BAND_WAKEUP) + device_set_out_band_wakeup(dev); + } + return ret; } @@ -651,6 +731,9 @@ static int ci_hdrc_imx_resume(struct device *dev) struct ci_hdrc_imx_data *data = dev_get_drvdata(dev); int ret; + if (data->wakeup_irq > 0 && device_may_wakeup(dev)) + disable_irq_wake(data->wakeup_irq); + pinctrl_pm_select_default_state(dev); ret = imx_controller_resume(dev, PMSG_RESUME); if (!ret && data->supports_runtime_pm) { diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 88b8da79d518..cb95c84d0322 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -37,5 +37,6 @@ int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data); int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect); int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup); int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup); +int imx_usbmisc_pullup(struct imx_usbmisc_data *data, bool on); #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */ diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 694b4a8e4e1d..7cfabb04a4fb 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -27,6 +27,7 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/pm_runtime.h> +#include <linux/pm_domain.h> #include <linux/pinctrl/consumer.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -543,30 +544,31 @@ static irqreturn_t ci_irq_handler(int irq, void *data) if (ret == IRQ_HANDLED) return ret; } - } - /* - * Handle id change interrupt, it indicates device/host function - * switch. - */ - if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { - ci->id_event = true; - /* Clear ID change irq status */ - hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); - ci_otg_queue_work(ci); - return IRQ_HANDLED; - } + /* + * Handle id change interrupt, it indicates device/host function + * switch. + */ + if ((otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) { + ci->id_event = true; + /* Clear ID change irq status */ + hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); + } - /* - * Handle vbus change interrupt, it indicates device connection - * and disconnection events. - */ - if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { - ci->b_sess_valid_event = true; - /* Clear BSV irq */ - hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); - ci_otg_queue_work(ci); - return IRQ_HANDLED; + /* + * Handle vbus change interrupt, it indicates device connection + * and disconnection events. + */ + if ((otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) { + ci->b_sess_valid_event = true; + /* Clear BSV irq */ + hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); + } + + if (ci->id_event || ci->b_sess_valid_event) { + ci_otg_queue_work(ci); + return IRQ_HANDLED; + } } /* Handle device/host interrupt */ @@ -617,28 +619,13 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw, struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw); struct ci_hdrc_cable *cable; - if (role == USB_ROLE_HOST) { - cable = &ci->platdata->id_extcon; - cable->changed = true; - cable->connected = true; - cable = &ci->platdata->vbus_extcon; - cable->changed = true; - cable->connected = false; - } else if (role == USB_ROLE_DEVICE) { - cable = &ci->platdata->id_extcon; - cable->changed = true; - cable->connected = false; - cable = &ci->platdata->vbus_extcon; - cable->changed = true; - cable->connected = true; - } else { - cable = &ci->platdata->id_extcon; - cable->changed = true; - cable->connected = false; - cable = &ci->platdata->vbus_extcon; - cable->changed = true; - cable->connected = false; - } + cable = &ci->platdata->id_extcon; + cable->changed = true; + cable->connected = (role == USB_ROLE_HOST); + + cable = &ci->platdata->vbus_extcon; + cable->changed = true; + cable->connected = (role == USB_ROLE_DEVICE); ci_irq(ci); return 0; @@ -915,6 +902,8 @@ struct platform_device *ci_hdrc_add_device(struct device *dev, if (ret) goto err; + dev_pm_domain_detach(&pdev->dev, false); + return pdev; err: @@ -1372,7 +1361,6 @@ static int ci_controller_resume(struct device *dev) ci->in_lpm = false; if (ci->wakeup_int) { ci->wakeup_int = false; - pm_runtime_mark_last_busy(ci->dev); pm_runtime_put_autosuspend(ci->dev); enable_irq(ci->irq); if (ci_otg_is_fsm_mode(ci)) diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 647e98f4e351..fecc7d7e2f0d 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -130,6 +130,9 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci) void ci_handle_vbus_change(struct ci_hdrc *ci) { + if (ci->role != CI_ROLE_GADGET) + return; + if (!ci->is_otg) { if (ci->platdata->flags & CI_HDRC_FORCE_VBUS_ACTIVE_ALWAYS) usb_gadget_vbus_connect(&ci->gadget); @@ -187,8 +190,8 @@ void ci_handle_id_switch(struct ci_hdrc *ci) ci_role_stop(ci); - if (role == CI_ROLE_GADGET && - IS_ERR(ci->platdata->vbus_extcon.edev)) + if (role == CI_ROLE_GADGET && !ci->role_switch && + IS_ERR(ci->platdata->vbus_extcon.edev)) /* * Wait vbus lower than OTGSC_BSV before connecting * to host. If connecting status is from an external diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index c17516c29b63..929536dc96ec 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; } @@ -630,7 +629,6 @@ int ci_otg_fsm_work(struct ci_hdrc *ci) ci_otg_queue_work(ci); } } else if (ci->fsm.otg->state == OTG_STATE_A_HOST) { - pm_runtime_mark_last_busy(ci->dev); pm_runtime_put_autosuspend(ci->dev); return 0; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 8a9b31fd5c89..f2de86d0ce40 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -360,8 +360,8 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq, { int i; u32 temp; - struct td_node *lastnode, *node = kzalloc(sizeof(struct td_node), - GFP_ATOMIC); + struct td_node *lastnode, *node = kzalloc_obj(struct td_node, + GFP_ATOMIC); if (node == NULL) return -ENOMEM; @@ -931,6 +931,13 @@ __acquires(hwep->lock) list_del_init(&hwreq->queue); hwreq->req.status = -ESHUTDOWN; + /* Unmap DMA and clean up bounce buffers before giving back */ + usb_gadget_unmap_request_by_dev(hwep->ci->dev->parent, + &hwreq->req, hwep->dir); + + if (hwreq->sgt.sgl) + sglist_do_debounce(hwreq, false); + if (hwreq->req.complete != NULL) { spin_unlock(hwep->lock); usb_gadget_giveback_request(&hwep->ep, &hwreq->req); @@ -1638,7 +1645,7 @@ static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) if (ep == NULL) return NULL; - hwreq = kzalloc(sizeof(struct ci_hw_req), gfp_flags); + hwreq = kzalloc_obj(struct ci_hw_req, gfp_flags); if (hwreq != NULL) { INIT_LIST_HEAD(&hwreq->queue); INIT_LIST_HEAD(&hwreq->tds); @@ -1970,6 +1977,11 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on) hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS); else hw_write(ci, OP_USBCMD, USBCMD_RS, 0); + + if (ci->platdata->notify_event) { + _gadget->connected = is_on; + ci->platdata->notify_event(ci, CI_HDRC_CONTROLLER_PULLUP_EVENT); + } pm_runtime_put_sync(ci->dev); return 0; @@ -2374,6 +2386,10 @@ static void udc_suspend(struct ci_hdrc *ci) */ if (hw_read(ci, OP_ENDPTLISTADDR, ~0) == 0) hw_write(ci, OP_ENDPTLISTADDR, ~0, ~0); + + if (ci->gadget.connected && + (!ci->suspended || !device_may_wakeup(ci->dev))) + usb_gadget_disconnect(&ci->gadget); } static void udc_resume(struct ci_hdrc *ci, bool power_lost) @@ -2384,6 +2400,9 @@ static void udc_resume(struct ci_hdrc *ci, bool power_lost) OTGSC_BSVIS | OTGSC_BSVIE); if (ci->vbus_active) usb_gadget_vbus_disconnect(&ci->gadget); + } else if (ci->vbus_active && ci->driver && + !ci->gadget.connected) { + usb_gadget_connect(&ci->gadget); } /* Restore value 0 if it was set for power lost check */ diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 1394881fde5f..bb027d2bd700 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2025 NXP */ #include <linux/module.h> @@ -139,6 +140,34 @@ #define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \ MX6_BM_ID_WAKEUP | MX6SX_BM_DPDM_WAKEUP_EN) +/* + * HSIO Block Control Register + */ + +#define BLKCTL_USB_WAKEUP_CTRL 0x0 +#define BLKCTL_OTG_WAKE_ENABLE BIT(31) +#define BLKCTL_OTG_VBUS_SESSVALID BIT(4) +#define BLKCTL_OTG_ID_WAKEUP_EN BIT(2) +#define BLKCTL_OTG_VBUS_WAKEUP_EN BIT(1) +#define BLKCTL_OTG_DPDM_WAKEUP_EN BIT(0) + +#define BLKCTL_WAKEUP_SOURCE (BLKCTL_OTG_WAKE_ENABLE | \ + BLKCTL_OTG_ID_WAKEUP_EN | \ + BLKCTL_OTG_VBUS_WAKEUP_EN | \ + BLKCTL_OTG_DPDM_WAKEUP_EN) + +#define S32G_WAKEUP_IE BIT(0) +#define S32G_CORE_IE BIT(1) +#define S32G_PWRFLTEN BIT(7) +#define S32G_WAKEUPCTRL BIT(10) +#define S32G_WAKEUPEN BIT(11) + +/* Workaround errata ERR050474 (handle packages that aren't 4 byte aligned) */ +#define S32G_UCMALLBE BIT(15) + +#define S32G_WAKEUP_BITS (S32G_WAKEUP_IE | S32G_CORE_IE | S32G_WAKEUPEN | \ + S32G_WAKEUPCTRL) + struct usbmisc_ops { /* It's called once when probe a usb device */ int (*init)(struct imx_usbmisc_data *data); @@ -154,11 +183,15 @@ struct usbmisc_ops { int (*charger_detection)(struct imx_usbmisc_data *data); /* It's called when system resume from usb power lost */ int (*power_lost_check)(struct imx_usbmisc_data *data); + /* It's called when device controller changed pullup status */ + void (*pullup)(struct imx_usbmisc_data *data, bool on); + /* It's called during suspend/resume to save power */ void (*vbus_comparator_on)(struct imx_usbmisc_data *data, bool on); }; struct imx_usbmisc { void __iomem *base; + void __iomem *blkctl; spinlock_t lock; const struct usbmisc_ops *ops; }; @@ -440,7 +473,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); @@ -597,6 +630,57 @@ static int usbmisc_vf610_init(struct imx_usbmisc_data *data) return 0; } +static int usbmisc_s32g_set_wakeup(struct imx_usbmisc_data *data, bool enabled) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&usbmisc->lock, flags); + + reg = readl(usbmisc->base); + if (enabled) + reg |= S32G_WAKEUP_BITS; + else + reg &= ~S32G_WAKEUP_BITS; + + writel(reg, usbmisc->base); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; +} + +static int usbmisc_s32g_init(struct imx_usbmisc_data *data, u32 extra_flags) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&usbmisc->lock, flags); + + reg = readl(usbmisc->base); + + reg |= S32G_PWRFLTEN; + reg |= extra_flags; + + writel(reg, usbmisc->base); + + spin_unlock_irqrestore(&usbmisc->lock, flags); + usbmisc_s32g_set_wakeup(data, false); + + return 0; +} + +static int usbmisc_s32g2_init(struct imx_usbmisc_data *data) +{ + return usbmisc_s32g_init(data, S32G_UCMALLBE); +} + +static int usbmisc_s32g3_init(struct imx_usbmisc_data *data) +{ + return usbmisc_s32g_init(data, 0); +} + static int usbmisc_imx7d_set_wakeup (struct imx_usbmisc_data *data, bool enabled) { @@ -645,7 +729,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 +1023,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; @@ -978,6 +1062,34 @@ static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data) return 0; } +static void usbmisc_imx7d_pullup(struct imx_usbmisc_data *data, bool on) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + + if (on) + return; + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK; + val |= MX7D_USBNC_USB_CTRL2_OPMODE(1); + val |= MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN; + writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + /* Last for at least 1 micro-frame to let host see disconnect signal */ + usleep_range(125, 150); + + spin_lock_irqsave(&usbmisc->lock, flags); + val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK; + val |= MX7D_USBNC_USB_CTRL2_OPMODE(0); + val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN; + writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2); + spin_unlock_irqrestore(&usbmisc->lock, flags); +} + static int usbmisc_imx7d_power_lost_check(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); @@ -1016,6 +1128,49 @@ static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data) return 0; } +static int usbmisc_s32g_power_lost_check(struct imx_usbmisc_data *data) +{ + return 1; +} + +static u32 usbmisc_blkctl_wakeup_setting(struct imx_usbmisc_data *data) +{ + u32 wakeup_setting = BLKCTL_WAKEUP_SOURCE; + + if (data->ext_id || data->available_role != USB_DR_MODE_OTG) + wakeup_setting &= ~BLKCTL_OTG_ID_WAKEUP_EN; + + if (data->ext_vbus || data->available_role == USB_DR_MODE_HOST) + wakeup_setting &= ~BLKCTL_OTG_VBUS_WAKEUP_EN; + + /* Select session valid as VBUS wakeup source */ + wakeup_setting |= BLKCTL_OTG_VBUS_SESSVALID; + + return wakeup_setting; +} + +static int usbmisc_imx95_set_wakeup(struct imx_usbmisc_data *data, bool enabled) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + + if (!usbmisc->blkctl) + return 0; + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->blkctl + BLKCTL_USB_WAKEUP_CTRL); + val &= ~BLKCTL_WAKEUP_SOURCE; + + if (enabled) + val |= usbmisc_blkctl_wakeup_setting(data); + + writel(val, usbmisc->blkctl + BLKCTL_USB_WAKEUP_CTRL); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; +} + static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, @@ -1057,6 +1212,7 @@ static const struct usbmisc_ops imx7d_usbmisc_ops = { .set_wakeup = usbmisc_imx7d_set_wakeup, .charger_detection = imx7d_charger_detection, .power_lost_check = usbmisc_imx7d_power_lost_check, + .pullup = usbmisc_imx7d_pullup, .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on, }; @@ -1068,6 +1224,35 @@ static const struct usbmisc_ops imx7ulp_usbmisc_ops = { .power_lost_check = usbmisc_imx7d_power_lost_check, }; +static const struct usbmisc_ops imx94_usbmisc_ops = { + .init = usbmisc_imx7d_init, + .set_wakeup = usbmisc_imx95_set_wakeup, + .charger_detection = imx7d_charger_detection, + .power_lost_check = usbmisc_imx7d_power_lost_check, + .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on, +}; + +static const struct usbmisc_ops imx95_usbmisc_ops = { + .init = usbmisc_imx7d_init, + .set_wakeup = usbmisc_imx95_set_wakeup, + .charger_detection = imx7d_charger_detection, + .power_lost_check = usbmisc_imx7d_power_lost_check, + .pullup = usbmisc_imx7d_pullup, + .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on, +}; + +static const struct usbmisc_ops s32g2_usbmisc_ops = { + .init = usbmisc_s32g2_init, + .set_wakeup = usbmisc_s32g_set_wakeup, + .power_lost_check = usbmisc_s32g_power_lost_check, +}; + +static const struct usbmisc_ops s32g3_usbmisc_ops = { + .init = usbmisc_s32g3_init, + .set_wakeup = usbmisc_s32g_set_wakeup, + .power_lost_check = usbmisc_s32g_power_lost_check, +}; + static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); @@ -1162,6 +1347,21 @@ int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect) } EXPORT_SYMBOL_GPL(imx_usbmisc_charger_detection); +int imx_usbmisc_pullup(struct imx_usbmisc_data *data, bool on) +{ + struct imx_usbmisc *usbmisc; + + if (!data) + return 0; + + usbmisc = dev_get_drvdata(data->dev); + if (usbmisc->ops->pullup) + usbmisc->ops->pullup(data, on); + + return 0; +} +EXPORT_SYMBOL_GPL(imx_usbmisc_pullup); + int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup) { struct imx_usbmisc *usbmisc; @@ -1185,7 +1385,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 +1424,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; } @@ -1289,6 +1489,22 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .compatible = "fsl,imx8ulp-usbmisc", .data = &imx7ulp_usbmisc_ops, }, + { + .compatible = "fsl,imx94-usbmisc", + .data = &imx94_usbmisc_ops, + }, + { + .compatible = "fsl,imx95-usbmisc", + .data = &imx95_usbmisc_ops, + }, + { + .compatible = "nxp,s32g2-usbmisc", + .data = &s32g2_usbmisc_ops, + }, + { + .compatible = "nxp,s32g3-usbmisc", + .data = &s32g3_usbmisc_ops, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); @@ -1296,6 +1512,7 @@ MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); static int usbmisc_imx_probe(struct platform_device *pdev) { struct imx_usbmisc *data; + struct resource *res; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -1307,6 +1524,15 @@ static int usbmisc_imx_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + data->blkctl = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->blkctl)) + return PTR_ERR(data->blkctl); + } else if (device_is_compatible(&pdev->dev, "fsl,imx95-usbmisc")) { + dev_warn(&pdev->dev, "wakeup setting is missing\n"); + } + data->ops = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, data); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index c2ecfa3c8349..54059e4fc6ed 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -114,6 +114,8 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, int retval; retval = usb_autopm_get_interface(acm->control); +#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */ +#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */ if (retval) return retval; @@ -710,12 +712,14 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty) set_bit(TTY_NO_WRITE_SPLIT, &tty->flags); acm->control->needs_remote_wakeup = 1; - acm->ctrlurb->dev = acm->dev; - retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL); - if (retval) { - dev_err(&acm->control->dev, - "%s - usb_submit_urb(ctrl irq) failed\n", __func__); - goto error_submit_urb; + if (!(acm->quirks & ALWAYS_POLL_CTRL)) { + acm->ctrlurb->dev = acm->dev; + retval = usb_submit_urb(acm->ctrlurb, GFP_KERNEL); + if (retval) { + dev_err(&acm->control->dev, + "%s - usb_submit_urb(ctrl irq) failed\n", __func__); + goto error_submit_urb; + } } acm_tty_set_termios(tty, NULL); @@ -788,6 +792,14 @@ static void acm_port_shutdown(struct tty_port *port) acm_unpoison_urbs(acm); + if (acm->quirks & ALWAYS_POLL_CTRL) { + acm->ctrlurb->dev = acm->dev; + if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) + dev_dbg(&acm->control->dev, + "ctrl polling restart failed after port close\n"); + /* port_shutdown() cleared DTR/RTS; restore them */ + acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS); + } } static void acm_tty_cleanup(struct tty_struct *tty) @@ -1225,6 +1237,12 @@ static int acm_probe(struct usb_interface *intf, if (!data_interface || !control_interface) return -ENODEV; goto skip_normal_probe; + } else if (quirks == NO_UNION_12) { + data_interface = usb_ifnum_to_if(usb_dev, 2); + control_interface = usb_ifnum_to_if(usb_dev, 1); + if (!data_interface || !control_interface) + return -ENODEV; + goto skip_normal_probe; } /* normal probing*/ @@ -1322,6 +1340,9 @@ skip_normal_probe: dev_dbg(&intf->dev, "Your device has switched interfaces.\n"); swap(control_interface, data_interface); + } else if (quirks & VENDOR_CLASS_DATA_IFACE) { + dev_dbg(&intf->dev, + "Vendor-specific data interface class, continuing.\n"); } else { return -EINVAL; } @@ -1350,7 +1371,7 @@ skip_normal_probe: made_compressed_probe: dev_dbg(&intf->dev, "interfaces are valid\n"); - acm = kzalloc(sizeof(struct acm), GFP_KERNEL); + acm = kzalloc_obj(struct acm); if (!acm) return -ENOMEM; @@ -1379,6 +1400,8 @@ made_compressed_probe: acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities; if (quirks & NO_CAP_LINE) acm->ctrl_caps &= ~USB_CDC_CAP_LINE; + if (quirks & MISSING_CAP_BRK) + acm->ctrl_caps |= USB_CDC_CAP_BRK; acm->ctrlsize = ctrlsize; acm->readsize = readsize; acm->rx_buflimit = num_rx_buf; @@ -1475,7 +1498,7 @@ made_compressed_probe: if (!acm->country_codes) goto skip_countries; acm->country_code_size = cfd->bLength - 4; - memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, + memcpy(acm->country_codes, cfd->wCountryCodes, cfd->bLength - 4); acm->country_rel_date = cfd->iCountryCodeRelDate; @@ -1514,12 +1537,21 @@ skip_countries: acm->line.bDataBits = 8; acm_set_line(acm, &acm->line); + if (quirks & ALWAYS_POLL_CTRL) + acm_set_control(acm, USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS); + if (!acm->combined_interfaces) { rv = usb_driver_claim_interface(&acm_driver, data_interface, acm); if (rv) goto err_remove_files; } + if (quirks & CLEAR_HALT_CONDITIONS) { + /* errors intentionally ignored */ + usb_clear_halt(usb_dev, acm->in); + usb_clear_halt(usb_dev, acm->out); + } + tty_dev = tty_port_register_device(&acm->port, acm_tty_driver, minor, &control_interface->dev); if (IS_ERR(tty_dev)) { @@ -1527,13 +1559,15 @@ skip_countries: goto err_release_data_interface; } - if (quirks & CLEAR_HALT_CONDITIONS) { - usb_clear_halt(usb_dev, acm->in); - usb_clear_halt(usb_dev, acm->out); - } - dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); + if (acm->quirks & ALWAYS_POLL_CTRL) { + acm->ctrlurb->dev = acm->dev; + if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) + dev_warn(&intf->dev, + "failed to start persistent ctrl polling\n"); + } + return 0; err_release_data_interface: @@ -1571,7 +1605,6 @@ err_put_port: static void acm_disconnect(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata(intf); - struct tty_struct *tty; int i; /* sibling interface is already cleaning up */ @@ -1598,11 +1631,7 @@ static void acm_disconnect(struct usb_interface *intf) usb_set_intfdata(acm->data, NULL); mutex_unlock(&acm->mutex); - tty = tty_port_tty_get(&acm->port); - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } + tty_port_tty_vhangup(&acm->port); cancel_delayed_work_sync(&acm->dwork); @@ -1665,7 +1694,7 @@ static int acm_resume(struct usb_interface *intf) acm_unpoison_urbs(acm); - if (tty_port_initialized(&acm->port)) { + if (tty_port_initialized(&acm->port) || (acm->quirks & ALWAYS_POLL_CTRL)) { rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC); for (;;) { @@ -1750,6 +1779,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x045b, 0x024D), /* Renesas R-Car E3 USB Download mode */ .driver_info = DISABLE_ECHO, /* Don't echo banner */ }, + { USB_DEVICE(0x04b8, 0x0d12), /* EPSON HMD Com&Sens */ + .driver_info = NO_UNION_12, /* union descriptor is garbage */ + }, { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, @@ -2006,6 +2038,23 @@ static const struct usb_device_id acm_ids[] = { .driver_info = IGNORE_DEVICE, }, + /* CH343 supports CAP_BRK, but doesn't advertise it */ + { USB_DEVICE(0x1a86, 0x55d3), .driver_info = MISSING_CAP_BRK, }, + + /* + * Lenovo Yoga Book 9 14IAH10 (83KJ) — INGENIC 17EF:6161 touchscreen + * composite device. The CDC ACM control interface (0) uses a standard + * Union descriptor, but the data interface (1) is declared as vendor- + * specific class (0xff) with no CDC data descriptors, so cdc-acm would + * normally reject it. The firmware also requires continuous polling of + * the notification endpoint (EP 0x82) to suppress a 20-second watchdog + * reset; ALWAYS_POLL_CTRL keeps the ctrlurb active even when no TTY is + * open. Match only the control interface by class to avoid probing the + * vendor-specific data interface. + */ + { USB_DEVICE_INTERFACE_CLASS(0x17ef, 0x6161, USB_CLASS_COMM), + .driver_info = VENDOR_CLASS_DATA_IFACE | ALWAYS_POLL_CTRL }, + /* control interfaces without any protocol set */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTO_NONE) }, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 759ac15631d3..25fd5329a878 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -113,3 +113,5 @@ struct acm { #define CLEAR_HALT_CONDITIONS BIT(5) #define SEND_ZERO_PACKET BIT(6) #define DISABLE_ECHO BIT(7) +#define MISSING_CAP_BRK BIT(8) +#define NO_UNION_12 BIT(9) diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 86ee39db013f..7556c0dac908 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -92,7 +92,6 @@ struct wdm_device { u16 wMaxCommand; u16 wMaxPacketSize; __le16 inum; - int reslength; int length; int read; int count; @@ -214,6 +213,11 @@ static void wdm_in_callback(struct urb *urb) if (desc->rerr == 0 && status != -EPIPE) desc->rerr = status; + if (length == 0) { + dev_dbg(&desc->intf->dev, "received ZLP\n"); + goto skip_zlp; + } + if (length + desc->length > desc->wMaxCommand) { /* The buffer would overflow */ set_bit(WDM_OVERFLOW, &desc->flags); @@ -221,19 +225,20 @@ static void wdm_in_callback(struct urb *urb) /* we may already be in overflow */ if (!test_bit(WDM_OVERFLOW, &desc->flags)) { memmove(desc->ubuf + desc->length, desc->inbuf, length); - desc->length += length; - desc->reslength = length; + smp_wmb(); /* against wdm_read() */ + WRITE_ONCE(desc->length, desc->length + length); } } skip_error: if (desc->rerr) { /* - * Since there was an error, userspace may decide to not read - * any data after poll'ing. + * If there was a ZLP or an error, userspace may decide to not + * read any data after poll'ing. * We should respond to further attempts from the device to send * data, so that we can get unstuck. */ +skip_zlp: schedule_work(&desc->service_outs_intr); } else { set_bit(WDM_READ, &desc->flags); @@ -529,6 +534,7 @@ static ssize_t wdm_read return -ERESTARTSYS; cntr = READ_ONCE(desc->length); + smp_rmb(); /* against wdm_in_callback() */ if (cntr == 0) { desc->read = 0; retry: @@ -585,15 +591,6 @@ retry: goto retry; } - if (!desc->reslength) { /* zero length read */ - dev_dbg(&desc->intf->dev, "zero length - clearing WDM_READ\n"); - clear_bit(WDM_READ, &desc->flags); - rv = service_outstanding_interrupt(desc); - spin_unlock_irq(&desc->iuspin); - if (rv < 0) - goto err; - goto retry; - } cntr = desc->length; spin_unlock_irq(&desc->iuspin); } @@ -726,7 +723,7 @@ static int wdm_open(struct inode *inode, struct file *file) rv = -EBUSY; goto out; } - + smp_rmb(); /* ordered against wdm_wwan_port_stop() */ rv = usb_autopm_get_interface(desc->intf); if (rv < 0) { dev_err(&desc->intf->dev, "Error autopm - %d\n", rv); @@ -829,6 +826,7 @@ static struct usb_class_driver wdm_class = { static int wdm_wwan_port_start(struct wwan_port *port) { struct wdm_device *desc = wwan_port_get_drvdata(port); + int rv; /* The interface is both exposed via the WWAN framework and as a * legacy usbmisc chardev. If chardev is already open, just fail @@ -848,7 +846,15 @@ static int wdm_wwan_port_start(struct wwan_port *port) wwan_port_txon(port); /* Start getting events */ - return usb_submit_urb(desc->validity, GFP_KERNEL); + rv = usb_submit_urb(desc->validity, GFP_KERNEL); + if (rv < 0) { + wwan_port_txoff(port); + desc->manage_power(desc->intf, 0); + /* this must be last lest we race with chardev open */ + clear_bit(WDM_WWAN_IN_USE, &desc->flags); + } + + return rv; } static void wdm_wwan_port_stop(struct wwan_port *port) @@ -859,8 +865,10 @@ static void wdm_wwan_port_stop(struct wwan_port *port) poison_urbs(desc); desc->manage_power(desc->intf, 0); clear_bit(WDM_READ, &desc->flags); - clear_bit(WDM_WWAN_IN_USE, &desc->flags); unpoison_urbs(desc); + smp_wmb(); /* ordered against wdm_open() */ + /* this must be last lest we open a poisoned device */ + clear_bit(WDM_WWAN_IN_USE, &desc->flags); } static void wdm_wwan_port_tx_complete(struct urb *urb) @@ -868,7 +876,7 @@ static void wdm_wwan_port_tx_complete(struct urb *urb) struct sk_buff *skb = urb->context; struct wdm_device *desc = skb_shinfo(skb)->destructor_arg; - usb_autopm_put_interface(desc->intf); + usb_autopm_put_interface_async(desc->intf); wwan_port_txon(desc->wwanp); kfree_skb(skb); } @@ -898,7 +906,7 @@ static int wdm_wwan_port_tx(struct wwan_port *port, struct sk_buff *skb) req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE); req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND; req->wValue = 0; - req->wIndex = desc->inum; + req->wIndex = desc->inum; /* already converted */ req->wLength = cpu_to_le16(skb->len); skb_shinfo(skb)->destructor_arg = desc; @@ -1005,7 +1013,7 @@ static void service_interrupt_work(struct work_struct *work) spin_lock_irq(&desc->iuspin); service_outstanding_interrupt(desc); - if (!desc->resp_count) { + if (!desc->resp_count && (desc->length || desc->rerr)) { set_bit(WDM_READ, &desc->flags); wake_up(&desc->wait); } @@ -1021,7 +1029,7 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor int rv = -ENOMEM; struct wdm_device *desc; - desc = kzalloc(sizeof(struct wdm_device), GFP_KERNEL); + desc = kzalloc_obj(struct wdm_device); if (!desc) goto out; INIT_LIST_HEAD(&desc->device_list); @@ -1044,10 +1052,10 @@ static int wdm_create(struct usb_interface *intf, struct usb_endpoint_descriptor desc->wMaxPacketSize = usb_endpoint_maxp(ep); - desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + desc->orq = kmalloc_obj(struct usb_ctrlrequest); if (!desc->orq) goto err; - desc->irq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + desc->irq = kmalloc_obj(struct usb_ctrlrequest); if (!desc->irq) goto err; diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index e2527faa6592..746414763da5 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -34,6 +34,7 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/minmax.h> #include <linux/sched/signal.h> #include <linux/signal.h> #include <linux/poll.h> @@ -366,7 +367,8 @@ static int usblp_check_status(struct usblp *usblp, int err) int error; mutex_lock(&usblp->mut); - if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) { + error = usblp_read_status(usblp, usblp->statusbuf); + if (error < 0) { mutex_unlock(&usblp->mut); printk_ratelimited(KERN_ERR "usblp%d: error %d reading printer status\n", @@ -751,14 +753,16 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t rv = -EINTR; goto raise_biglock; } - if ((rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK))) < 0) + rv = usblp_wwait(usblp, !!(file->f_flags & O_NONBLOCK)); + if (rv < 0) goto raise_wait; while (writecount < count) { /* * Step 1: Submit next block. */ - if ((transfer_length = count - writecount) > USBLP_BUF_SIZE) + transfer_length = count - writecount; + if (transfer_length > USBLP_BUF_SIZE) transfer_length = USBLP_BUF_SIZE; rv = -ENOMEM; @@ -776,7 +780,9 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t spin_lock_irq(&usblp->lock); usblp->wcomplete = 0; spin_unlock_irq(&usblp->lock); - if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) { + + rv = usb_submit_urb(writeurb, GFP_KERNEL); + if (rv < 0) { usblp->wstatus = 0; spin_lock_irq(&usblp->lock); usblp->no_paper = 0; @@ -857,22 +863,24 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t len, lo goto done; } - if ((avail = usblp->rstatus) < 0) { + avail = usblp->rstatus; + if (avail < 0) { printk(KERN_ERR "usblp%d: error %d reading from printer\n", - usblp->minor, (int)avail); + usblp->minor, (int)avail); usblp_submit_read(usblp); count = -EIO; goto done; } - count = len < avail - usblp->readcount ? len : avail - usblp->readcount; + count = min_t(ssize_t, len, avail - usblp->readcount); if (count != 0 && copy_to_user(buffer, usblp->readbuf + usblp->readcount, count)) { count = -EFAULT; goto done; } - if ((usblp->readcount += count) == avail) { + usblp->readcount += count; + if (usblp->readcount == avail) { if (usblp_submit_read(usblp) < 0) { /* We don't want to leak USB return codes into errno. */ if (count == 0) @@ -973,7 +981,8 @@ static int usblp_rwait_and_lock(struct usblp *usblp, int nonblock) break; } set_current_state(TASK_INTERRUPTIBLE); - if ((rc = usblp_rtest(usblp, nonblock)) < 0) { + rc = usblp_rtest(usblp, nonblock); + if (rc < 0) { mutex_unlock(&usblp->mut); break; } @@ -1031,7 +1040,8 @@ static int usblp_submit_read(struct usblp *usblp) usblp->readcount = 0; /* XXX Why here? */ usblp->rcomplete = 0; spin_unlock_irqrestore(&usblp->lock, flags); - if ((rc = usb_submit_urb(urb, GFP_KERNEL)) < 0) { + rc = usb_submit_urb(urb, GFP_KERNEL); + if (rc < 0) { dev_dbg(&usblp->intf->dev, "error submitting urb (%d)\n", rc); spin_lock_irqsave(&usblp->lock, flags); usblp->rstatus = rc; @@ -1132,7 +1142,7 @@ static int usblp_probe(struct usb_interface *intf, /* Malloc and start initializing usblp structure so we can use it * directly. */ - usblp = kzalloc(sizeof(struct usblp), GFP_KERNEL); + usblp = kzalloc_obj(struct usblp); if (!usblp) { retval = -ENOMEM; goto abort_ret; @@ -1150,7 +1160,8 @@ static int usblp_probe(struct usb_interface *intf, /* Malloc device ID string buffer to the largest expected length, * since we can re-query it on an ioctl and a dynamic string * could change in length. */ - if (!(usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL))) { + usblp->device_id_string = kmalloc(USBLP_DEVICE_ID_SIZE, GFP_KERNEL); + if (!usblp->device_id_string) { retval = -ENOMEM; goto abort; } @@ -1160,13 +1171,14 @@ static int usblp_probe(struct usb_interface *intf, * malloc both regardless of bidirectionality, because the * alternate setting can be changed later via an ioctl. */ - if (!(usblp->readbuf = kmalloc(USBLP_BUF_SIZE_IN, GFP_KERNEL))) { + usblp->readbuf = kmalloc(USBLP_BUF_SIZE_IN, GFP_KERNEL); + if (!usblp->readbuf) { retval = -ENOMEM; goto abort; } /* Allocate buffer for printer status */ - usblp->statusbuf = kmalloc(STATUS_BUF_SIZE, GFP_KERNEL); + usblp->statusbuf = kzalloc(STATUS_BUF_SIZE, GFP_KERNEL); if (!usblp->statusbuf) { retval = -ENOMEM; goto abort; @@ -1365,6 +1377,7 @@ static int usblp_cache_device_id_string(struct usblp *usblp) { int err, length; + memset(usblp->device_id_string, 0, USBLP_DEVICE_ID_SIZE); err = usblp_get_id(usblp, 0, usblp->device_id_string, USBLP_DEVICE_ID_SIZE - 1); if (err < 0) { dev_dbg(&usblp->intf->dev, diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 34e46ef308ab..bd9347804dec 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -172,7 +172,7 @@ static int usbtmc_open(struct inode *inode, struct file *filp) return -ENODEV; } - file_data = kzalloc(sizeof(*file_data), GFP_KERNEL); + file_data = kzalloc_obj(*file_data); if (!file_data) return -ENOMEM; @@ -254,6 +254,9 @@ static int usbtmc_release(struct inode *inode, struct file *file) list_del(&file_data->file_elem); spin_unlock_irq(&file_data->data->dev_lock); + + /* flush anchored URBs */ + usbtmc_draw_down(file_data); mutex_unlock(&file_data->data->io_mutex); kref_put(&file_data->data->kref, usbtmc_delete); @@ -482,6 +485,8 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb) u8 *buffer; u8 tag; int rv; + long wait_rv; + unsigned long expire; dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n", data->iin_ep_present); @@ -511,16 +516,18 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb) } if (data->iin_ep_present) { - rv = wait_event_interruptible_timeout( + expire = msecs_to_jiffies(file_data->timeout); + wait_rv = wait_event_interruptible_timeout( data->waitq, atomic_read(&data->iin_data_valid) != 0, - file_data->timeout); - if (rv < 0) { - dev_dbg(dev, "wait interrupted %d\n", rv); + expire); + if (wait_rv < 0) { + dev_dbg(dev, "wait interrupted %ld\n", wait_rv); + rv = wait_rv; goto exit; } - if (rv == 0) { + if (wait_rv == 0) { dev_dbg(dev, "wait timed out\n"); rv = -ETIMEDOUT; goto exit; @@ -539,6 +546,8 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb) dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)*stb, rv); + rv = 0; + exit: /* bump interrupt bTag */ data->iin_bTag += 1; @@ -559,14 +568,15 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data, rv = usbtmc_get_stb(file_data, &stb); - if (rv > 0) { - srq_asserted = atomic_xchg(&file_data->srq_asserted, - srq_asserted); - if (srq_asserted) - stb |= 0x40; /* Set RQS bit */ + if (rv < 0) + return rv; + + srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted); + if (srq_asserted) + stb |= 0x40; /* Set RQS bit */ + + rv = put_user(stb, (__u8 __user *)arg); - rv = put_user(stb, (__u8 __user *)arg); - } return rv; } @@ -602,9 +612,9 @@ static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data, { struct usbtmc_device_data *data = file_data->data; struct device *dev = &data->intf->dev; - int rv; u32 timeout; unsigned long expire; + long wait_rv; if (!data->iin_ep_present) { dev_dbg(dev, "no interrupt endpoint present\n"); @@ -618,25 +628,24 @@ static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data, mutex_unlock(&data->io_mutex); - rv = wait_event_interruptible_timeout( - data->waitq, - atomic_read(&file_data->srq_asserted) != 0 || - atomic_read(&file_data->closing), - expire); + wait_rv = wait_event_interruptible_timeout( + data->waitq, + atomic_read(&file_data->srq_asserted) != 0 || + atomic_read(&file_data->closing), + expire); mutex_lock(&data->io_mutex); /* Note! disconnect or close could be called in the meantime */ if (atomic_read(&file_data->closing) || data->zombie) - rv = -ENODEV; + return -ENODEV; - if (rv < 0) { - /* dev can be invalid now! */ - pr_debug("%s - wait interrupted %d\n", __func__, rv); - return rv; + if (wait_rv < 0) { + dev_dbg(dev, "%s - wait interrupted %ld\n", __func__, wait_rv); + return wait_rv; } - if (rv == 0) { + if (wait_rv == 0) { dev_dbg(dev, "%s - wait timed out\n", __func__); return -ETIMEDOUT; } @@ -721,7 +730,7 @@ static int usbtmc488_ioctl_trigger(struct usbtmc_file_data *file_data) buffer[1] = data->bTag; buffer[2] = ~data->bTag; - retval = usb_bulk_msg(data->usb_dev, + retval = usb_bulk_msg_killable(data->usb_dev, usb_sndbulkpipe(data->usb_dev, data->bulk_out), buffer, USBTMC_HEADER_SIZE, @@ -830,6 +839,7 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data, unsigned long expire; int bufcount = 1; int again = 0; + long wait_rv; /* mutex already locked */ @@ -942,19 +952,24 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data, if (!(flags & USBTMC_FLAG_ASYNC)) { dev_dbg(dev, "%s: before wait time %lu\n", __func__, expire); - retval = wait_event_interruptible_timeout( + wait_rv = wait_event_interruptible_timeout( file_data->wait_bulk_in, usbtmc_do_transfer(file_data), expire); - dev_dbg(dev, "%s: wait returned %d\n", - __func__, retval); + dev_dbg(dev, "%s: wait returned %ld\n", + __func__, wait_rv); - if (retval <= 0) { - if (retval == 0) - retval = -ETIMEDOUT; + if (wait_rv < 0) { + retval = wait_rv; goto error; } + + if (wait_rv == 0) { + retval = -ETIMEDOUT; + goto error; + } + } urb = usb_get_from_anchor(&file_data->in_anchor); @@ -1335,7 +1350,7 @@ static int send_request_dev_dep_msg_in(struct usbtmc_file_data *file_data, buffer[11] = 0; /* Reserved */ /* Send bulk URB */ - retval = usb_bulk_msg(data->usb_dev, + retval = usb_bulk_msg_killable(data->usb_dev, usb_sndbulkpipe(data->usb_dev, data->bulk_out), buffer, USBTMC_HEADER_SIZE, @@ -1380,7 +1395,10 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, if (!buffer) return -ENOMEM; - mutex_lock(&data->io_mutex); + retval = mutex_lock_interruptible(&data->io_mutex); + if (retval < 0) + goto exit_nolock; + if (data->zombie) { retval = -ENODEV; goto exit; @@ -1404,7 +1422,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, actual = 0; /* Send bulk URB */ - retval = usb_bulk_msg(data->usb_dev, + retval = usb_bulk_msg_killable(data->usb_dev, usb_rcvbulkpipe(data->usb_dev, data->bulk_in), buffer, bufsize, &actual, @@ -1503,6 +1521,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf, exit: mutex_unlock(&data->io_mutex); +exit_nolock: kfree(buffer); return retval; } @@ -1920,10 +1939,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data, u8 *buffer = NULL; int rv; unsigned int is_in, pipe; - unsigned long res; - res = copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest)); - if (res) + if (copy_from_user(&request, arg, sizeof(struct usbtmc_ctrlrequest))) return -EFAULT; if (request.req.wLength > USBTMC_BUFSIZE) @@ -1940,9 +1957,8 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data, if (!is_in) { /* Send control data to device */ - res = copy_from_user(buffer, request.data, - request.req.wLength); - if (res) { + if (copy_from_user(buffer, request.data, + request.req.wLength)) { rv = -EFAULT; goto exit; } @@ -1968,8 +1984,7 @@ static int usbtmc_ioctl_request(struct usbtmc_device_data *data, if (rv && is_in) { /* Read control data from device */ - res = copy_to_user(request.data, buffer, rv); - if (res) + if (copy_to_user(request.data, buffer, rv)) rv = -EFAULT; } @@ -2186,7 +2201,7 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case USBTMC_IOCTL_GET_STB: retval = usbtmc_get_stb(file_data, &tmp_byte); - if (retval > 0) + if (!retval) retval = put_user(tmp_byte, (__u8 __user *)arg); break; @@ -2366,7 +2381,7 @@ static int usbtmc_probe(struct usb_interface *intf, dev_dbg(&intf->dev, "%s called\n", __func__); - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = kzalloc_obj(*data); if (!data) return -ENOMEM; diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 4a2ee447b213..9b69148128e5 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -286,12 +286,15 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi) ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); ret = ulpi_of_register(ulpi); - if (ret) + if (ret) { + kfree(ulpi); return ret; + } ret = ulpi_read_id(ulpi); if (ret) { of_node_put(ulpi->dev.of_node); + kfree(ulpi); return ret; } @@ -324,17 +327,16 @@ struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi *ulpi; int ret; - ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL); + ulpi = kzalloc_obj(*ulpi); if (!ulpi) return ERR_PTR(-ENOMEM); ulpi->ops = ops; ret = ulpi_register(dev, ulpi); - if (ret) { - kfree(ulpi); + if (ret) return ERR_PTR(ret); - } + return ulpi; } diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index aa710b50791b..421c3af38e06 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -21,6 +21,9 @@ #include <linux/regulator/consumer.h> #include <linux/string_choices.h> #include <linux/usb/role.h> +#include <linux/idr.h> + +static DEFINE_IDA(usb_conn_ida); #define USB_GPIO_DEB_MS 20 /* ms */ #define USB_GPIO_DEB_US ((USB_GPIO_DEB_MS) * 1000) /* us */ @@ -30,6 +33,7 @@ struct usb_conn_info { struct device *dev; + int conn_id; /* store the IDA-allocated ID */ struct usb_role_switch *role_sw; enum usb_role last_role; struct regulator *vbus; @@ -158,10 +162,20 @@ 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"; + info->conn_id = ida_alloc(&usb_conn_ida, GFP_KERNEL); + if (info->conn_id < 0) + return info->conn_id; + + desc->name = devm_kasprintf(dev, GFP_KERNEL, "usb-charger-%d", + info->conn_id); + if (!desc->name) { + ida_free(&usb_conn_ida, info->conn_id); + return -ENOMEM; + } + desc->properties = usb_charger_properties; desc->num_properties = ARRAY_SIZE(usb_charger_properties); desc->get_property = usb_charger_get_property; @@ -169,8 +183,10 @@ static int usb_conn_psy_register(struct usb_conn_info *info) cfg.drv_data = info; info->charger = devm_power_supply_register(dev, desc, &cfg); - if (IS_ERR(info->charger)) - dev_err(dev, "Unable to register charger\n"); + if (IS_ERR(info->charger)) { + dev_err(dev, "Unable to register charger %d\n", info->conn_id); + ida_free(&usb_conn_ida, info->conn_id); + } return PTR_ERR_OR_ZERO(info->charger); } @@ -278,6 +294,9 @@ static void usb_conn_remove(struct platform_device *pdev) cancel_delayed_work_sync(&info->dw_det); + if (info->charger) + ida_free(&usb_conn_ida, info->conn_id); + if (info->last_role == USB_ROLE_HOST && info->vbus) regulator_disable(info->vbus); diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index ac006abd13b3..60ea76160122 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -3,12 +3,16 @@ # Makefile for USB Core files and filesystem # +# define_trace.h needs to know how to find our header +CFLAGS_trace.o := -I$(src) + usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o usbcore-y += devio.o notify.o generic.o quirks.o devices.o -usbcore-y += phy.o port.o +usbcore-y += phy.o port.o trace.o usbcore-$(CONFIG_OF) += of.o +usbcore-$(CONFIG_USB_XHCI_SIDEBAND) += offload.o usbcore-$(CONFIG_USB_PCI) += hcd-pci.o usbcore-$(CONFIG_ACPI) += usb-acpi.o diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index f7bf8d1de3ad..417140b012bb 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -54,8 +54,8 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev, * follows the SuperSpeed Endpoint Companion descriptor */ desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer; - if (desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP || - size < USB_DT_SSP_ISOC_EP_COMP_SIZE) { + if (size < USB_DT_SSP_ISOC_EP_COMP_SIZE || + desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP) { dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion" "for config %d interface %d altsetting %d ep %d.\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); @@ -64,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) @@ -76,8 +107,14 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, */ desc = (struct usb_ss_ep_comp_descriptor *) buffer; - if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP || - size < USB_DT_SS_EP_COMP_SIZE) { + if (size < USB_DT_SS_EP_COMP_SIZE) { + dev_notice(ddev, + "invalid SuperSpeed endpoint companion descriptor " + "of length %d, skipping\n", size); + return; + } + + if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) { dev_notice(ddev, "No SuperSpeed endpoint companion for config %d " " interface %d altsetting %d ep %d: " "using minimum values\n", @@ -258,8 +295,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; @@ -274,7 +313,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, goto skip_to_next_endpoint_or_interface_descriptor; } - i = d->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + i = usb_endpoint_num(d); if (i == 0) { dev_notice(ddev, "config %d interface %d altsetting %d has an " "invalid descriptor for endpoint zero, skipping\n", @@ -409,15 +448,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 */ @@ -465,6 +506,12 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, maxp); } + /* Parse a possible eUSB2 periodic endpoint companion descriptor */ + if (udev->speed == USB_SPEED_HIGH && bcdUSB == 0x0220 && + !le16_to_cpu(d->wMaxPacketSize) && usb_endpoint_is_isoc_in(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, @@ -776,7 +823,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, nalts[i] = j = USB_MAXALTSETTING; } - intfc = kzalloc(struct_size(intfc, altsetting, j), GFP_KERNEL); + intfc = kzalloc_flex(*intfc, altsetting, j); config->intf_cache[i] = intfc; if (!intfc) return -ENOMEM; @@ -880,7 +927,11 @@ int usb_get_configuration(struct usb_device *dev) dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG; } - if (ncfg < 1) { + if (ncfg < 1 && dev->quirks & USB_QUIRK_FORCE_ONE_CONFIG) { + dev_info(ddev, "Device claims zero configurations, forcing to 1\n"); + dev->descriptor.bNumConfigurations = 1; + ncfg = 1; + } else if (ncfg < 1) { dev_err(ddev, "no configurations\n"); return -EINVAL; } @@ -993,7 +1044,12 @@ int usb_get_bos_descriptor(struct usb_device *dev) __u8 cap_type; int ret; - bos = kzalloc(sizeof(*bos), GFP_KERNEL); + if (dev->quirks & USB_QUIRK_NO_BOS) { + dev_dbg(ddev, "skipping BOS descriptor\n"); + return -ENOMSG; + } + + bos = kzalloc_obj(*bos); if (!bos) return -ENOMEM; @@ -1014,7 +1070,7 @@ int usb_get_bos_descriptor(struct usb_device *dev) if (total_len < length) return -EINVAL; - dev->bos = kzalloc(sizeof(*dev->bos), GFP_KERNEL); + dev->bos = kzalloc_obj(*dev->bos); if (!dev->bos) return -ENOMEM; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index f6ce6e26e0d4..e191934623c7 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -245,7 +245,7 @@ static int usbdev_mmap(struct file *file, struct vm_area_struct *vma) if (ret) goto error; - usbm = kzalloc(sizeof(struct usb_memory), GFP_KERNEL); + usbm = kzalloc_obj(struct usb_memory); if (!usbm) { ret = -ENOMEM; goto error_decrease_mem; @@ -402,7 +402,7 @@ static struct async *alloc_async(unsigned int numisoframes) { struct async *as; - as = kzalloc(sizeof(struct async), GFP_KERNEL); + as = kzalloc_obj(struct async); if (!as) return NULL; as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL); @@ -970,7 +970,7 @@ static int parse_usbdevfs_streams(struct usb_dev_state *ps, if (num_streams_ret && (num_streams < 2 || num_streams > 65536)) return -EINVAL; - eps = kmalloc_array(num_eps, sizeof(*eps), GFP_KERNEL); + eps = kmalloc_objs(*eps, num_eps); if (!eps) return -ENOMEM; @@ -1039,7 +1039,7 @@ static int usbdev_open(struct inode *inode, struct file *file) int ret; ret = -ENOMEM; - ps = kzalloc(sizeof(struct usb_dev_state), GFP_KERNEL); + ps = kzalloc_obj(struct usb_dev_state); if (!ps) goto out_free_ps; @@ -1196,7 +1196,7 @@ static int do_proc_control(struct usb_dev_state *ps, urb = usb_alloc_urb(0, GFP_NOIO); if (!urb) goto done; - dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); + dr = kmalloc_obj(struct usb_ctrlrequest, GFP_NOIO); if (!dr) goto done; @@ -1670,7 +1670,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb /* min 8 byte setup packet */ if (uurb->buffer_length < 8) return -EINVAL; - dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + dr = kmalloc_obj(struct usb_ctrlrequest); if (!dr) return -ENOMEM; if (copy_from_user(dr, uurb->buffer, 8)) { @@ -1805,9 +1805,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb as->mem_usage = u; if (num_sgs) { - as->urb->sg = kmalloc_array(num_sgs, - sizeof(struct scatterlist), - GFP_KERNEL | __GFP_NOWARN); + as->urb->sg = kmalloc_objs(struct scatterlist, num_sgs, + GFP_KERNEL | __GFP_NOWARN); if (!as->urb->sg) { ret = -ENOMEM; goto error; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 460d4dde5994..f63004417058 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -57,7 +57,7 @@ ssize_t usb_store_new_id(struct usb_dynids *dynids, if (fields < 2) return -EINVAL; - dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); + dynid = kzalloc_obj(*dynid); if (!dynid) return -ENOMEM; @@ -119,11 +119,11 @@ ssize_t usb_show_dynids(struct usb_dynids *dynids, char *buf) 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", + count += sysfs_emit_at(buf, count, "%04x %04x %02x\n", dynid->id.idVendor, dynid->id.idProduct, dynid->id.bInterfaceClass); else - count += scnprintf(&buf[count], PAGE_SIZE - count, "%04x %04x\n", + count += sysfs_emit_at(buf, count, "%04x %04x\n", dynid->id.idVendor, dynid->id.idProduct); return count; } @@ -332,10 +332,10 @@ static int usb_probe_interface(struct device *dev) return error; if (udev->authorized == 0) { - dev_err(&intf->dev, "Device is not authorized for usage\n"); + dev_info(&intf->dev, "Device is not authorized for usage\n"); return error; } else if (intf->authorized == 0) { - dev_err(&intf->dev, "Interface %d is not authorized for usage\n", + dev_info(&intf->dev, "Interface %d is not authorized for usage\n", intf->altsetting->desc.bInterfaceNumber); return error; } @@ -1415,16 +1415,34 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) int status = 0; int i = 0, n = 0; struct usb_interface *intf; + bool offload_active = false; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) goto done; + usb_offload_set_pm_locked(udev, true); + if (msg.event == PM_EVENT_SUSPEND && usb_offload_check(udev)) { + dev_dbg(&udev->dev, "device offloaded, skip suspend.\n"); + offload_active = true; + } + /* Suspend all the interfaces and then udev itself */ if (udev->actconfig) { n = udev->actconfig->desc.bNumInterfaces; for (i = n - 1; i >= 0; --i) { intf = udev->actconfig->interface[i]; + /* + * Don't suspend interfaces with remote wakeup while + * the controller is active. This preserves pending + * interrupt urbs, allowing interrupt events to be + * handled during system suspend. + */ + if (offload_active && intf->needs_remote_wakeup) { + dev_dbg(&intf->dev, + "device offloaded, skip suspend.\n"); + continue; + } status = usb_suspend_interface(udev, intf, msg); /* Ignore errors during system sleep transitions */ @@ -1435,7 +1453,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) } } if (status == 0) { - status = usb_suspend_device(udev, msg); + if (!offload_active) + status = usb_suspend_device(udev, msg); /* * Ignore errors from non-root-hub devices during @@ -1480,13 +1499,17 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) */ } else { udev->can_submit = 0; - for (i = 0; i < 16; ++i) { - usb_hcd_flush_endpoint(udev, udev->ep_out[i]); - usb_hcd_flush_endpoint(udev, udev->ep_in[i]); + if (!offload_active) { + for (i = 0; i < 16; ++i) { + usb_hcd_flush_endpoint(udev, udev->ep_out[i]); + usb_hcd_flush_endpoint(udev, udev->ep_in[i]); + } } } done: + if (status != 0) + usb_offload_set_pm_locked(udev, false); dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); return status; } @@ -1516,21 +1539,40 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) int status = 0; int i; struct usb_interface *intf; + bool offload_active = false; if (udev->state == USB_STATE_NOTATTACHED) { status = -ENODEV; goto done; } udev->can_submit = 1; + if (msg.event == PM_EVENT_RESUME) + offload_active = usb_offload_check(udev); /* Resume the device */ - if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume) - status = usb_resume_device(udev, msg); + if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume) { + if (!offload_active) + status = usb_resume_device(udev, msg); + else + dev_dbg(&udev->dev, + "device offloaded, skip resume.\n"); + } /* Resume the interfaces */ if (status == 0 && udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; + /* + * Interfaces with remote wakeup aren't suspended + * while the controller is active. This preserves + * pending interrupt urbs, allowing interrupt events + * to be handled during system suspend. + */ + if (offload_active && intf->needs_remote_wakeup) { + dev_dbg(&intf->dev, + "device offloaded, skip resume.\n"); + continue; + } usb_resume_interface(udev, intf, msg, udev->reset_resume); } @@ -1539,6 +1581,7 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg) done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); + usb_offload_set_pm_locked(udev, false); if (!status) udev->reset_resume = 0; return status; @@ -1723,8 +1766,6 @@ int usb_autoresume_device(struct usb_device *udev) dev_vdbg(&udev->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&udev->dev.power.usage_count), status); - if (status > 0) - status = 0; return status; } @@ -1774,13 +1815,11 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface); void usb_autopm_put_interface_async(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); - int status; usb_mark_last_busy(udev); - status = pm_runtime_put(&intf->dev); - dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", - __func__, atomic_read(&intf->dev.power.usage_count), - status); + pm_runtime_put(&intf->dev); + dev_vdbg(&intf->dev, "%s: cnt %d\n", + __func__, atomic_read(&intf->dev.power.usage_count)); } EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async); @@ -1829,8 +1868,6 @@ int usb_autopm_get_interface(struct usb_interface *intf) dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&intf->dev.power.usage_count), status); - if (status > 0) - status = 0; return status; } EXPORT_SYMBOL_GPL(usb_autopm_get_interface); diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index e48399401608..e00eaf9e22cd 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -26,14 +26,6 @@ struct ep_device { #define to_ep_device(_dev) \ container_of(_dev, struct ep_device, dev) -struct ep_attribute { - struct attribute attr; - ssize_t (*show)(struct usb_device *, - struct usb_endpoint_descriptor *, char *); -}; -#define to_ep_attribute(_attr) \ - container_of(_attr, struct ep_attribute, attr) - #define usb_ep_attr(field, format_string) \ static ssize_t field##_show(struct device *dev, \ struct device_attribute *attr, \ @@ -154,7 +146,7 @@ int usb_create_ep_devs(struct device *parent, struct ep_device *ep_dev; int retval; - ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL); + ep_dev = kzalloc_obj(*ep_dev); if (!ep_dev) { retval = -ENOMEM; goto exit; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 9c6ae5e1198b..a48994e11ef3 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -243,7 +243,7 @@ int usb_generic_driver_probe(struct usb_device *udev) * with the driver core and lets interface drivers bind to them. */ if (udev->authorized == 0) - dev_err(&udev->dev, "Device is not authorized for usage\n"); + dev_info(&udev->dev, "Device is not authorized for usage\n"); else { c = usb_choose_configuration(udev); if (c >= 0) { diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 56b534f59907..cd223475917e 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -210,7 +210,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct hc_driver *driver) hcd->amd_resume_bug = usb_hcd_amd_resume_bug(dev, driver); if (driver->flags & HCD_MEMORY) { - /* EHCI, OHCI */ + /* XHCI, EHCI, OHCI */ hcd->rsrc_start = pci_resource_start(dev, 0); hcd->rsrc_len = pci_resource_len(dev, 0); if (!devm_request_mem_region(&dev->dev, hcd->rsrc_start, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index a75cf1f6d741..89221f1ce769 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -77,10 +77,6 @@ /*-------------------------------------------------------------------------*/ -/* Keep track of which host controller drivers are loaded */ -unsigned long usb_hcds_loaded; -EXPORT_SYMBOL_GPL(usb_hcds_loaded); - /* host controllers we manage */ DEFINE_IDR (usb_bus_idr); EXPORT_SYMBOL_GPL (usb_bus_idr); @@ -775,7 +771,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status); /* timer callback */ static void rh_timer_func (struct timer_list *t) { - struct usb_hcd *_hcd = from_timer(_hcd, t, rh_timer); + struct usb_hcd *_hcd = timer_container_of(_hcd, t, rh_timer); usb_hcd_poll_rh_status(_hcd); } @@ -842,7 +838,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); @@ -1342,29 +1338,35 @@ void usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; if (IS_ENABLED(CONFIG_HAS_DMA) && - (urb->transfer_flags & URB_DMA_MAP_SG)) + (urb->transfer_flags & URB_DMA_MAP_SG)) { dma_unmap_sg(hcd->self.sysdev, urb->sg, urb->num_sgs, dir); - else if (IS_ENABLED(CONFIG_HAS_DMA) && - (urb->transfer_flags & URB_DMA_MAP_PAGE)) + } else if (IS_ENABLED(CONFIG_HAS_DMA) && + (urb->transfer_flags & URB_DMA_MAP_PAGE)) { dma_unmap_page(hcd->self.sysdev, urb->transfer_dma, urb->transfer_buffer_length, dir); - else if (IS_ENABLED(CONFIG_HAS_DMA) && - (urb->transfer_flags & URB_DMA_MAP_SINGLE)) + } else if (IS_ENABLED(CONFIG_HAS_DMA) && + (urb->transfer_flags & URB_DMA_MAP_SINGLE)) { dma_unmap_single(hcd->self.sysdev, urb->transfer_dma, urb->transfer_buffer_length, dir); - else if (urb->transfer_flags & URB_MAP_LOCAL) + } else if (urb->transfer_flags & URB_MAP_LOCAL) { hcd_free_coherent(urb->dev->bus, &urb->transfer_dma, &urb->transfer_buffer, urb->transfer_buffer_length, dir); + } else if ((urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) && urb->sgt) { + dma_sync_sgtable_for_cpu(hcd->self.sysdev, urb->sgt, dir); + if (dir == DMA_FROM_DEVICE) + invalidate_kernel_vmap_range(urb->transfer_buffer, + urb->transfer_buffer_length); + } /* Make it safe to call this routine more than once */ urb->transfer_flags &= ~(URB_DMA_MAP_SG | URB_DMA_MAP_PAGE | @@ -1425,8 +1427,15 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, } dir = usb_urb_dir_in(urb) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; - if (urb->transfer_buffer_length != 0 - && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { + if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) { + if (!urb->sgt) + return 0; + + if (dir == DMA_TO_DEVICE) + flush_kernel_vmap_range(urb->transfer_buffer, + urb->transfer_buffer_length); + dma_sync_sgtable_for_device(hcd->self.sysdev, urb->sgt, dir); + } else if (urb->transfer_buffer_length != 0) { if (hcd->localmem_pool) { ret = hcd_alloc_coherent( urb->dev->bus, mem_flags, @@ -1609,7 +1618,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); } @@ -1623,7 +1632,6 @@ static void __usb_hcd_giveback_urb(struct urb *urb) struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus); struct usb_anchor *anchor = urb->anchor; int status = urb->unlinked; - unsigned long flags; urb->hcpriv = NULL; if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) && @@ -1641,14 +1649,13 @@ static void __usb_hcd_giveback_urb(struct urb *urb) /* pass ownership to the completion handler */ urb->status = status; /* - * Only collect coverage in the softirq context and disable interrupts - * to avoid scenarios with nested remote coverage collection sections - * that KCOV does not support. - * See the comment next to kcov_remote_start_usb_softirq() for details. + * This function can be called in task context inside another remote + * coverage collection section, but kcov doesn't support that kind of + * recursion yet. Only collect coverage in softirq context for now. */ - flags = kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum); + kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum); urb->complete(urb); - kcov_remote_stop_softirq(flags); + kcov_remote_stop_softirq(); usb_anchor_resume_wakeups(anchor); atomic_dec(&urb->use_count); @@ -1706,10 +1713,10 @@ static void usb_giveback_urb_bh(struct work_struct *work) * @urb: urb being returned to the USB device driver. * @status: completion status code for the URB. * - * Context: atomic. The completion callback is invoked in caller's context. - * For HCDs with HCD_BH flag set, the completion callback is invoked in BH - * context (except for URBs submitted to the root hub which always complete in - * caller's context). + * Context: atomic. The completion callback is invoked either in a work queue + * (BH) context or in the caller's context, depending on whether the HCD_BH + * flag is set in the @hcd structure, except that URBs submitted to the + * root hub always complete in BH context. * * This hands the URB from HCD to its USB device driver, using its * completion function. The HCD has freed all per-urb resources @@ -1786,7 +1793,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))); @@ -2153,7 +2160,7 @@ static struct urb *request_single_step_set_feature_urb( urb->complete = usb_ehset_completion; urb->status = -EINPROGRESS; urb->actual_length = 0; - urb->transfer_flags = URB_DIR_IN; + urb->transfer_flags = URB_DIR_IN | URB_NO_TRANSFER_DMA_MAP; usb_get_urb(urb); atomic_inc(&urb->use_count); atomic_inc(&urb->dev->urbnum); @@ -2186,7 +2193,7 @@ int ehset_single_step_set_feature(struct usb_hcd *hcd, int port) if (!buf) return -ENOMEM; - dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + dr = kmalloc_obj(struct usb_ctrlrequest); if (!dr) { kfree(buf); return -ENOMEM; @@ -2217,9 +2224,15 @@ int ehset_single_step_set_feature(struct usb_hcd *hcd, int port) /* Complete remaining DATA and STATUS stages using the same URB */ urb->status = -EINPROGRESS; + urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP; usb_get_urb(urb); atomic_inc(&urb->use_count); atomic_inc(&urb->dev->urbnum); + if (map_urb_for_dma(hcd, urb, GFP_KERNEL)) { + usb_put_urb(urb); + goto out1; + } + retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 0); if (!retval && !wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) { @@ -2390,7 +2403,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) if (hcd->rh_registered) { pm_wakeup_event(&hcd->self.root_hub->dev, 0); set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); - queue_work(pm_wq, &hcd->wakeup_work); + queue_work(system_freezable_wq, &hcd->wakeup_work); } spin_unlock_irqrestore (&hcd_root_hub_lock, flags); } @@ -2554,16 +2567,14 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver, if (!hcd) return NULL; if (primary_hcd == NULL) { - hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex), - GFP_KERNEL); + hcd->address0_mutex = kmalloc_obj(*hcd->address0_mutex); if (!hcd->address0_mutex) { kfree(hcd); dev_dbg(dev, "hcd address0 mutex alloc failed\n"); return NULL; } mutex_init(hcd->address0_mutex); - hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex), - GFP_KERNEL); + hcd->bandwidth_mutex = kmalloc_obj(*hcd->bandwidth_mutex); if (!hcd->bandwidth_mutex) { kfree(hcd->address0_mutex); kfree(hcd); @@ -2679,18 +2690,18 @@ static void hcd_release(struct kref *kref) kfree(hcd); } -struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd) +struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd) { if (hcd) - kref_get (&hcd->kref); + kref_get(&hcd->kref); return hcd; } EXPORT_SYMBOL_GPL(usb_get_hcd); -void usb_put_hcd (struct usb_hcd *hcd) +void usb_put_hcd(struct usb_hcd *hcd) { if (hcd) - kref_put (&hcd->kref, hcd_release); + kref_put(&hcd->kref, hcd_release); } EXPORT_SYMBOL_GPL(usb_put_hcd); @@ -2768,14 +2779,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); } /** diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index dcba4281ea48..24960ba9caa9 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -28,6 +28,7 @@ #include <linux/usb/otg.h> #include <linux/usb/quirks.h> #include <linux/workqueue.h> +#include <linux/minmax.h> #include <linux/mutex.h> #include <linux/random.h> #include <linux/pm_qos.h> @@ -40,6 +41,7 @@ #include "hub.h" #include "phy.h" #include "otg_productlist.h" +#include "trace.h" #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define USB_VENDOR_SMSC 0x0424 @@ -68,6 +70,12 @@ */ #define USB_SHORT_SET_ADDRESS_REQ_TIMEOUT 500 /* ms */ +/* + * Give SS hubs 200ms time after wake to train downstream links before + * assuming no port activity and allowing hub to runtime suspend back. + */ +#define USB_SS_PORT_U0_WAKE_TIME 200 /* ms */ + /* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ @@ -271,10 +279,7 @@ static void usb_set_lpm_pel(struct usb_device *udev, * device and the parent hub into U0. The exit latency is the bigger of * the device exit latency or the hub exit latency. */ - if (udev_exit_latency > hub_exit_latency) - first_link_pel = udev_exit_latency * 1000; - else - first_link_pel = hub_exit_latency * 1000; + first_link_pel = max(udev_exit_latency, hub_exit_latency) * 1000; /* * When the hub starts to receive the LFPS, there is a slight delay for @@ -288,10 +293,7 @@ static void usb_set_lpm_pel(struct usb_device *udev, * According to figure C-7 in the USB 3.0 spec, the PEL for this device * is the greater of the two exit latencies. */ - if (first_link_pel > hub_pel) - udev_lpm_params->pel = first_link_pel; - else - udev_lpm_params->pel = hub_pel; + udev_lpm_params->pel = max(first_link_pel, hub_pel); } /* @@ -697,7 +699,7 @@ static void hub_resubmit_irq_urb(struct usb_hub *hub) static void hub_retry_irq_urb(struct timer_list *t) { - struct usb_hub *hub = from_timer(hub, t, irq_urb_retry); + struct usb_hub *hub = timer_container_of(hub, t, irq_urb_retry); hub_resubmit_irq_urb(hub); } @@ -927,7 +929,7 @@ int usb_hub_clear_tt_buffer(struct urb *urb) * since each TT has "at least two" buffers that can need it (and * there can be many TTs per hub). even if they're uncommon. */ - clear = kmalloc(sizeof *clear, GFP_ATOMIC); + clear = kmalloc_obj(*clear, GFP_ATOMIC); if (clear == NULL) { dev_err(&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); /* FIXME recover somehow ... RESET_TT? */ @@ -1095,6 +1097,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) goto init2; goto init3; } + hub_get(hub); /* The superspeed hub except for root hub has to use Hub Depth @@ -1343,6 +1346,17 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) device_unlock(&hdev->dev); } + if (type == HUB_RESUME && hub_is_superspeed(hub->hdev)) { + /* give usb3 downstream links training time after hub resume */ + usb_autopm_get_interface_no_resume( + to_usb_interface(hub->intfdev)); + + queue_delayed_work(system_power_efficient_wq, + &hub->post_resume_work, + msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME)); + return; + } + hub_put(hub); } @@ -1361,6 +1375,14 @@ static void hub_init_func3(struct work_struct *ws) hub_activate(hub, HUB_INIT3); } +static void hub_post_resume(struct work_struct *ws) +{ + struct usb_hub *hub = container_of(ws, struct usb_hub, post_resume_work.work); + + usb_autopm_put_interface_async(to_usb_interface(hub->intfdev)); + hub_put(hub); +} + enum hub_quiescing_type { HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND }; @@ -1385,7 +1407,8 @@ 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); + flush_delayed_work(&hub->post_resume_work); usb_kill_urb(hub->urb); if (hub->has_indicators) cancel_delayed_work_sync(&hub->leds); @@ -1438,20 +1461,20 @@ static int hub_configure(struct usb_hub *hub, unsigned full_load; unsigned maxchild; - hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL); + hub->buffer = kmalloc_obj(*hub->buffer); if (!hub->buffer) { ret = -ENOMEM; goto fail; } - hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL); + hub->status = kmalloc_obj(*hub->status); if (!hub->status) { ret = -ENOMEM; goto fail; } mutex_init(&hub->status_mutex); - hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL); + hub->descriptor = kzalloc_obj(*hub->descriptor); if (!hub->descriptor) { ret = -ENOMEM; goto fail; @@ -1499,7 +1522,7 @@ static int hub_configure(struct usb_hub *hub, dev_info(hub_dev, "%d port%s detected\n", maxchild, str_plural(maxchild)); - hub->ports = kcalloc(maxchild, sizeof(struct usb_port *), GFP_KERNEL); + hub->ports = kzalloc_objs(struct usb_port *, maxchild); if (!hub->ports) { ret = -ENOMEM; goto fail; @@ -1935,7 +1958,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) /* We found a hub */ dev_info(&intf->dev, "USB hub found\n"); - hub = kzalloc(sizeof(*hub), GFP_KERNEL); + hub = kzalloc_obj(*hub); if (!hub) return -ENOMEM; @@ -1944,6 +1967,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) hub->hdev = hdev; INIT_DELAYED_WORK(&hub->leds, led_work); INIT_DELAYED_WORK(&hub->init_work, NULL); + INIT_DELAYED_WORK(&hub->post_resume_work, hub_post_resume); INIT_WORK(&hub->events, hub_event); INIT_LIST_HEAD(&hub->onboard_devs); spin_lock_init(&hub->irq_urb_lock); @@ -2119,6 +2143,21 @@ static void update_port_device_state(struct usb_device *udev) } } +static void update_usb_device_state(struct usb_device *udev, + enum usb_device_state new_state) +{ + if (udev->state == USB_STATE_SUSPENDED && + new_state != USB_STATE_SUSPENDED) + udev->active_duration -= jiffies; + else if (new_state == USB_STATE_SUSPENDED && + udev->state != USB_STATE_SUSPENDED) + udev->active_duration += jiffies; + + udev->state = new_state; + update_port_device_state(udev); + trace_usb_set_device_state(udev); +} + static void recursively_mark_NOTATTACHED(struct usb_device *udev) { struct usb_hub *hub = usb_hub_to_struct_hub(udev); @@ -2128,10 +2167,7 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev) if (hub->ports[i]->child) recursively_mark_NOTATTACHED(hub->ports[i]->child); } - if (udev->state == USB_STATE_SUSPENDED) - udev->active_duration -= jiffies; - udev->state = USB_STATE_NOTATTACHED; - update_port_device_state(udev); + update_usb_device_state(udev, USB_STATE_NOTATTACHED); } /** @@ -2181,14 +2217,7 @@ void usb_set_device_state(struct usb_device *udev, else wakeup = 0; } - if (udev->state == USB_STATE_SUSPENDED && - new_state != USB_STATE_SUSPENDED) - udev->active_duration -= jiffies; - else if (new_state == USB_STATE_SUSPENDED && - udev->state != USB_STATE_SUSPENDED) - udev->active_duration += jiffies; - udev->state = new_state; - update_port_device_state(udev); + update_usb_device_state(udev, new_state); } else recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); @@ -2337,6 +2366,9 @@ void usb_disconnect(struct usb_device **pdev) usb_remove_ep_devs(&udev->ep0); usb_unlock_device(udev); + if (udev->usb4_link) + device_link_del(udev->usb4_link); + /* Unregister the device. The device driver is responsible * for de-configuring the device and invoking the remove-device * notifier chain (used by usbfs and possibly others). @@ -4110,7 +4142,7 @@ static int usb_req_set_sel(struct usb_device *udev) * which may be initiated by an error path of a mass storage driver. * Therefore, use GFP_NOIO. */ - sel_values = kmalloc(sizeof *(sel_values), GFP_NOIO); + sel_values = kmalloc_obj(*(sel_values), GFP_NOIO); if (!sel_values) return -ENOMEM; @@ -4160,7 +4192,7 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev, "for unconfigured device.\n", __func__, str_enable_disable(enable), usb3_lpm_names[state]); - return 0; + return -EINVAL; } if (enable) { @@ -4234,9 +4266,9 @@ static int usb_set_lpm_timeout(struct usb_device *udev, } /* - * Don't allow device intiated U1/U2 if the system exit latency + one bus - * interval is greater than the minimum service interval of any active - * periodic endpoint. See USB 3.2 section 9.4.9 + * Don't allow device intiated U1/U2 if device isn't in the configured state, + * or the system exit latency + one bus interval is greater than the minimum + * service interval of any active periodic endpoint. See USB 3.2 section 9.4.9 */ static bool usb_device_may_initiate_lpm(struct usb_device *udev, enum usb3_link_state state) @@ -4244,7 +4276,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev, unsigned int sel; /* us */ int i, j; - if (!udev->lpm_devinit_allow) + if (!udev->lpm_devinit_allow || !udev->actconfig) return false; if (state == USB3_LPM_U1) @@ -4292,7 +4324,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev, * driver know about it. If that call fails, it should be harmless, and just * take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency. */ -static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, +static int usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, enum usb3_link_state state) { int timeout; @@ -4301,7 +4333,7 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, /* Skip if the device BOS descriptor couldn't be read */ if (!udev->bos) - return; + return -EINVAL; u1_mel = udev->bos->ss_cap->bU1devExitLat; u2_mel = udev->bos->ss_cap->bU2DevExitLat; @@ -4312,7 +4344,7 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, */ if ((state == USB3_LPM_U1 && u1_mel == 0) || (state == USB3_LPM_U2 && u2_mel == 0)) - return; + return -EINVAL; /* We allow the host controller to set the U1/U2 timeout internally * first, so that it can change its schedule to account for the @@ -4323,13 +4355,13 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, /* xHCI host controller doesn't want to enable this LPM state. */ if (timeout == 0) - return; + return -EINVAL; if (timeout < 0) { dev_warn(&udev->dev, "Could not enable %s link state, " "xHCI error %i.\n", usb3_lpm_names[state], timeout); - return; + return timeout; } if (usb_set_lpm_timeout(udev, state, timeout)) { @@ -4338,29 +4370,15 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, * host know that this link state won't be enabled. */ hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state); - return; - } - - /* Only a configured device will accept the Set Feature - * U1/U2_ENABLE - */ - if (udev->actconfig && - usb_device_may_initiate_lpm(udev, state)) { - if (usb_set_device_initiated_lpm(udev, state, true)) { - /* - * Request to enable device initiated U1/U2 failed, - * better to turn off lpm in this case. - */ - usb_set_lpm_timeout(udev, state, 0); - hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state); - return; - } + return -EBUSY; } if (state == USB3_LPM_U1) udev->usb3_lpm_u1_enabled = 1; else if (state == USB3_LPM_U2) udev->usb3_lpm_u2_enabled = 1; + + return 0; } /* * Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated @@ -4393,8 +4411,6 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev, if (usb_set_lpm_timeout(udev, state, 0)) return -EBUSY; - usb_set_device_initiated_lpm(udev, state, false); - if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state)) dev_warn(&udev->dev, "Could not disable xHCI %s timeout, " "bus schedule bandwidth may be impacted.\n", @@ -4424,6 +4440,7 @@ static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev, int usb_disable_lpm(struct usb_device *udev) { struct usb_hcd *hcd; + int err; if (!udev || !udev->parent || udev->speed < USB_SPEED_SUPER || @@ -4441,14 +4458,19 @@ int usb_disable_lpm(struct usb_device *udev) /* If LPM is enabled, attempt to disable it. */ if (usb_disable_link_state(hcd, udev, USB3_LPM_U1)) - goto enable_lpm; + goto disable_failed; if (usb_disable_link_state(hcd, udev, USB3_LPM_U2)) - goto enable_lpm; + goto disable_failed; + + err = usb_set_device_initiated_lpm(udev, USB3_LPM_U1, false); + if (!err) + usb_set_device_initiated_lpm(udev, USB3_LPM_U2, false); return 0; -enable_lpm: - usb_enable_lpm(udev); +disable_failed: + udev->lpm_disable_count--; + return -EBUSY; } EXPORT_SYMBOL_GPL(usb_disable_lpm); @@ -4509,10 +4531,24 @@ void usb_enable_lpm(struct usb_device *udev) port_dev = hub->ports[udev->portnum - 1]; if (port_dev->usb3_lpm_u1_permit) - usb_enable_link_state(hcd, udev, USB3_LPM_U1); + if (usb_enable_link_state(hcd, udev, USB3_LPM_U1)) + return; if (port_dev->usb3_lpm_u2_permit) - usb_enable_link_state(hcd, udev, USB3_LPM_U2); + if (usb_enable_link_state(hcd, udev, USB3_LPM_U2)) + return; + + /* + * Enable device initiated U1/U2 with a SetFeature(U1/U2_ENABLE) request + * if system exit latency is short enough and device is configured + */ + if (usb_device_may_initiate_lpm(udev, USB3_LPM_U1)) { + if (usb_set_device_initiated_lpm(udev, USB3_LPM_U1, true)) + return; + + if (usb_device_may_initiate_lpm(udev, USB3_LPM_U2)) + usb_set_device_initiated_lpm(udev, USB3_LPM_U2, true); + } } EXPORT_SYMBOL_GPL(usb_enable_lpm); @@ -4708,8 +4744,6 @@ void usb_ep0_reinit(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_ep0_reinit); -#define usb_sndaddr0pipe() (PIPE_CONTROL << 30) - static int hub_set_address(struct usb_device *udev, int devnum) { int retval; @@ -4733,7 +4767,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) { @@ -5202,7 +5236,7 @@ check_highspeed(struct usb_hub *hub, struct usb_device *udev, int port1) if (udev->quirks & USB_QUIRK_DEVICE_QUALIFIER) return; - qual = kmalloc(sizeof *qual, GFP_KERNEL); + qual = kmalloc_obj(*qual); if (qual == NULL) return; @@ -5718,6 +5752,7 @@ static void port_event(struct usb_hub *hub, int port1) struct usb_device *hdev = hub->hdev; u16 portstatus, portchange; int i = 0; + int err; connect_change = test_bit(port1, hub->change_bits); clear_bit(port1, hub->event_bits); @@ -5814,8 +5849,11 @@ static void port_event(struct usb_hub *hub, int port1) } else if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION) || udev->state == USB_STATE_NOTATTACHED) { dev_dbg(&port_dev->dev, "do warm reset, port only\n"); - if (hub_port_reset(hub, port1, NULL, - HUB_BH_RESET_TIME, true) < 0) + err = hub_port_reset(hub, port1, NULL, + HUB_BH_RESET_TIME, true); + if (!udev && err == -ENOTCONN) + connect_change = 0; + else if (err < 0) hub_port_disable(hub, port1, 1); } else { dev_dbg(&port_dev->dev, "do warm reset, full device\n"); @@ -6040,7 +6078,7 @@ int usb_hub_init(void) * device was gone before the EHCI controller had handed its port * over to the companion full-speed controller. */ - hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE, 0); + hub_wq = alloc_workqueue("usb_hub_wq", WQ_FREEZABLE | WQ_PERCPU, 0); if (hub_wq) return 0; @@ -6135,6 +6173,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev) struct usb_hub *parent_hub; struct usb_hcd *hcd = bus_to_hcd(udev->bus); struct usb_device_descriptor descriptor; + struct usb_interface *intf; struct usb_host_bos *bos; int i, j, ret = 0; int port1 = udev->portnum; @@ -6192,6 +6231,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev) if (!udev->actconfig) goto done; + /* + * Some devices can't handle setting default altsetting 0 with a + * Set-Interface request. Disable host-side endpoints of those + * interfaces here. Enable and reset them back after host has set + * its internal endpoint structures during usb_hcd_alloc_bandwith() + */ + for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { + intf = udev->actconfig->interface[i]; + if (intf->cur_altsetting->desc.bAlternateSetting == 0) + usb_disable_interface(udev, intf, true); + } + mutex_lock(hcd->bandwidth_mutex); ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL); if (ret < 0) { @@ -6223,12 +6274,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev) */ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { struct usb_host_config *config = udev->actconfig; - struct usb_interface *intf = config->interface[i]; struct usb_interface_descriptor *desc; + intf = config->interface[i]; desc = &intf->cur_altsetting->desc; if (desc->bAlternateSetting == 0) { - usb_disable_interface(udev, intf, true); usb_enable_interface(udev, intf, true); ret = 0; } else { diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index e6ae73f8a95d..9ebc5ef54a32 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -70,6 +70,7 @@ struct usb_hub { u8 indicator[USB_MAXCHILDREN]; struct delayed_work leds; struct delayed_work init_work; + struct delayed_work post_resume_work; struct work_struct events; spinlock_t irq_urb_lock; struct timer_list irq_urb_retry; diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c index 5e3c515991f3..8881644777a7 100644 --- a/drivers/usb/core/ledtrig-usbport.c +++ b/drivers/usb/core/ledtrig-usbport.c @@ -190,7 +190,7 @@ static int usbport_trig_add_port(struct usbport_trig_data *usbport_data, size_t len; int err; - port = kzalloc(sizeof(*port), GFP_KERNEL); + port = kzalloc_obj(*port); if (!port) { err = -ENOMEM; goto err_out; @@ -305,7 +305,7 @@ static int usbport_trig_activate(struct led_classdev *led_cdev) struct usbport_trig_data *usbport_data; int err; - usbport_data = kzalloc(sizeof(*usbport_data), GFP_KERNEL); + usbport_data = kzalloc_obj(*usbport_data); if (!usbport_data) return -ENOMEM; usbport_data->led_cdev = led_cdev; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index d2b2787be409..75e2bfd744a9 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -42,16 +42,19 @@ static void usb_api_blocking_completion(struct urb *urb) /* - * Starts urb and waits for completion or timeout. Note that this call - * is NOT interruptible. Many device driver i/o requests should be - * interruptible and therefore these drivers should implement their - * own interruptible routines. + * Starts urb and waits for completion or timeout. + * Whether or not the wait is killable depends on the flag passed in. + * For example, compare usb_bulk_msg() and usb_bulk_msg_killable(). + * + * For non-killable waits, we enforce a maximum limit on the timeout value. */ -static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) +static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length, + bool killable) { struct api_context ctx; unsigned long expire; int retval; + long rc; init_completion(&ctx.done); urb->context = &ctx; @@ -60,13 +63,24 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) if (unlikely(retval)) goto out; - expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; - if (!wait_for_completion_timeout(&ctx.done, expire)) { + if (!killable && (timeout <= 0 || timeout > USB_MAX_SYNCHRONOUS_TIMEOUT)) + timeout = USB_MAX_SYNCHRONOUS_TIMEOUT; + expire = (timeout > 0) ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; + if (killable) + rc = wait_for_completion_killable_timeout(&ctx.done, expire); + else + rc = wait_for_completion_timeout(&ctx.done, expire); + if (rc <= 0) { usb_kill_urb(urb); - retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status); + if (ctx.status != -ENOENT) + retval = ctx.status; + else if (rc == 0) + retval = -ETIMEDOUT; + else + retval = rc; dev_dbg(&urb->dev->dev, - "%s timed out on ep%d%s len=%u/%u\n", + "%s timed out or killed on ep%d%s len=%u/%u\n", current->comm, usb_endpoint_num(&urb->ep->desc), usb_urb_dir_in(urb) ? "in" : "out", @@ -100,7 +114,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev, usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data, len, usb_api_blocking_completion, NULL); - retv = usb_start_wait_urb(urb, timeout, &length); + retv = usb_start_wait_urb(urb, timeout, &length, false); if (retv < 0) return retv; else @@ -117,8 +131,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev, * @index: USB message index value * @data: pointer to the data to send * @size: length in bytes of the data to send - * @timeout: time in msecs to wait for the message to complete before timing - * out (if 0 the wait is forever) + * @timeout: time in msecs to wait for the message to complete before timing out * * Context: task context, might sleep. * @@ -141,7 +154,7 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, struct usb_ctrlrequest *dr; int ret; - dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); + dr = kmalloc_obj(struct usb_ctrlrequest, GFP_NOIO); if (!dr) return -ENOMEM; @@ -173,8 +186,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg); * @index: USB message index value * @driver_data: pointer to the data to send * @size: length in bytes of the data to send - * @timeout: time in msecs to wait for the message to complete before timing - * out (if 0 the wait is forever) + * @timeout: time in msecs to wait for the message to complete before timing out * @memflags: the flags for memory allocation for buffers * * Context: !in_interrupt () @@ -232,8 +244,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_send); * @index: USB message index value * @driver_data: pointer to the data to be filled in by the message * @size: length in bytes of the data to be received - * @timeout: time in msecs to wait for the message to complete before timing - * out (if 0 the wait is forever) + * @timeout: time in msecs to wait for the message to complete before timing out * @memflags: the flags for memory allocation for buffers * * Context: !in_interrupt () @@ -304,8 +315,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_recv); * @len: length in bytes of the data to send * @actual_length: pointer to a location to put the actual length transferred * in bytes - * @timeout: time in msecs to wait for the message to complete before - * timing out (if 0 the wait is forever) + * @timeout: time in msecs to wait for the message to complete before timing out * * Context: task context, might sleep. * @@ -337,8 +347,7 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg); * @len: length in bytes of the data to send * @actual_length: pointer to a location to put the actual length transferred * in bytes - * @timeout: time in msecs to wait for the message to complete before - * timing out (if 0 the wait is forever) + * @timeout: time in msecs to wait for the message to complete before timing out * * Context: task context, might sleep. * @@ -385,10 +394,59 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, usb_fill_bulk_urb(urb, usb_dev, pipe, data, len, usb_api_blocking_completion, NULL); - return usb_start_wait_urb(urb, timeout, actual_length); + return usb_start_wait_urb(urb, timeout, actual_length, false); } EXPORT_SYMBOL_GPL(usb_bulk_msg); +/** + * usb_bulk_msg_killable - Builds a bulk urb, sends it off and waits for completion in a killable state + * @usb_dev: pointer to the usb device to send the message to + * @pipe: endpoint "pipe" to send the message to + * @data: pointer to the data to send + * @len: length in bytes of the data to send + * @actual_length: pointer to a location to put the actual length transferred + * in bytes + * @timeout: time in msecs to wait for the message to complete before + * timing out (if <= 0, the wait is as long as possible) + * + * Context: task context, might sleep. + * + * This function is just like usb_blk_msg(), except that it waits in a + * killable state and there is no limit on the timeout length. + * + * Return: + * If successful, 0. Otherwise a negative error number. The number of actual + * bytes transferred will be stored in the @actual_length parameter. + * + */ +int usb_bulk_msg_killable(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout) +{ + struct urb *urb; + struct usb_host_endpoint *ep; + + ep = usb_pipe_endpoint(usb_dev, pipe); + if (!ep || len < 0) + return -EINVAL; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_INT) { + pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30); + usb_fill_int_urb(urb, usb_dev, pipe, data, len, + usb_api_blocking_completion, NULL, + ep->desc.bInterval); + } else + usb_fill_bulk_urb(urb, usb_dev, pipe, data, len, + usb_api_blocking_completion, NULL); + + return usb_start_wait_urb(urb, timeout, actual_length, true); +} +EXPORT_SYMBOL_GPL(usb_bulk_msg_killable); + /*-------------------------------------------------------------------*/ static void sg_clean(struct usb_sg_request *io) @@ -526,7 +584,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, } /* initialize all the urbs we'll use */ - io->urbs = kmalloc_array(io->entries, sizeof(*io->urbs), mem_flags); + io->urbs = kmalloc_objs(*io->urbs, io->entries, mem_flags); if (!io->urbs) goto nomem; @@ -1005,7 +1063,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) } EXPORT_SYMBOL_GPL(usb_string); -/* one UTF-8-encoded 16-bit character has at most three bytes */ +/* one 16-bit character, when UTF-8-encoded, has at most three bytes */ #define MAX_USB_STRING_SIZE (127 * 3 + 1) /** @@ -1026,16 +1084,18 @@ char *usb_cache_string(struct usb_device *udev, int index) return NULL; buf = kmalloc(MAX_USB_STRING_SIZE, GFP_NOIO); - if (buf) { - len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE); - if (len > 0) { - smallbuf = kmalloc(++len, GFP_NOIO); - if (!smallbuf) - return buf; - memcpy(smallbuf, buf, len); - } + if (!buf) + return NULL; + + len = usb_string(udev, index, buf, MAX_USB_STRING_SIZE); + if (len <= 0) { kfree(buf); + return NULL; } + + smallbuf = krealloc(buf, len + 1, GFP_NOIO); + if (unlikely(!smallbuf)) + return buf; return smallbuf; } EXPORT_SYMBOL_GPL(usb_cache_string); @@ -1058,7 +1118,7 @@ struct usb_device_descriptor *usb_get_device_descriptor(struct usb_device *udev) struct usb_device_descriptor *desc; int ret; - desc = kmalloc(sizeof(*desc), GFP_NOIO); + desc = kmalloc_obj(*desc, GFP_NOIO); if (!desc) return ERR_PTR(-ENOMEM); @@ -2028,15 +2088,13 @@ int usb_set_configuration(struct usb_device *dev, int configuration) n = nintf = 0; if (cp) { nintf = cp->desc.bNumInterfaces; - new_interfaces = kmalloc_array(nintf, sizeof(*new_interfaces), - GFP_NOIO); + new_interfaces = kmalloc_objs(*new_interfaces, nintf, GFP_NOIO); if (!new_interfaces) return -ENOMEM; for (; n < nintf; ++n) { - new_interfaces[n] = kzalloc( - sizeof(struct usb_interface), - GFP_NOIO); + new_interfaces[n] = kzalloc_obj(struct usb_interface, + GFP_NOIO); if (!new_interfaces[n]) { ret = -ENOMEM; free_interfaces: @@ -2289,7 +2347,7 @@ int usb_driver_set_configuration(struct usb_device *udev, int config) { struct set_config_request *req; - req = kmalloc(sizeof(*req), GFP_KERNEL); + req = kmalloc_obj(*req); if (!req) return -ENOMEM; req->udev = udev; @@ -2431,7 +2489,7 @@ int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, break; case USB_CDC_MBIM_EXTENDED_TYPE: if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) - break; + goto next_desc; hdr->usb_cdc_mbim_extended_desc = (struct usb_cdc_mbim_extended_desc *)buffer; break; diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index 763e4122ed5b..a6ee20d8774e 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -79,17 +79,13 @@ EXPORT_SYMBOL_GPL(usb_of_has_combined_node); static bool usb_of_has_devices_or_graph(const struct usb_device *hub) { const struct device_node *np = hub->dev.of_node; - struct device_node *child; if (of_graph_is_present(np)) return true; - for_each_child_of_node(np, child) { - if (of_property_present(child, "reg")) { - of_node_put(child); + for_each_child_of_node_scoped(np, child) + if (of_property_present(child, "reg")) return true; - } - } return false; } diff --git a/drivers/usb/core/offload.c b/drivers/usb/core/offload.c new file mode 100644 index 000000000000..9db3cfedd29c --- /dev/null +++ b/drivers/usb/core/offload.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * offload.c - USB offload related functions + * + * Copyright (c) 2025, Google LLC. + * + * Author: Guan-Yu Lin + */ + +#include <linux/usb.h> + +#include "usb.h" + +/** + * usb_offload_get - increment the offload_usage of a USB device + * @udev: the USB device to increment its offload_usage + * + * Incrementing the offload_usage of a usb_device indicates that offload is + * enabled on this usb_device; that is, another entity is actively handling USB + * transfers. This information allows the USB driver to adjust its power + * management policy based on offload activity. + * + * Return: 0 on success. A negative error code otherwise. + */ +int usb_offload_get(struct usb_device *udev) +{ + int ret = 0; + + if (!usb_get_dev(udev)) + return -ENODEV; + + if (pm_runtime_get_if_active(&udev->dev) != 1) { + ret = -EBUSY; + goto err_rpm; + } + + spin_lock(&udev->offload_lock); + + if (udev->offload_pm_locked) { + ret = -EAGAIN; + goto err; + } + + udev->offload_usage++; + +err: + spin_unlock(&udev->offload_lock); + pm_runtime_put_autosuspend(&udev->dev); +err_rpm: + usb_put_dev(udev); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_offload_get); + +/** + * usb_offload_put - drop the offload_usage of a USB device + * @udev: the USB device to drop its offload_usage + * + * The inverse operation of usb_offload_get, which drops the offload_usage of + * a USB device. This information allows the USB driver to adjust its power + * management policy based on offload activity. + * + * Return: 0 on success. A negative error code otherwise. + */ +int usb_offload_put(struct usb_device *udev) +{ + int ret = 0; + + if (!usb_get_dev(udev)) + return -ENODEV; + + if (pm_runtime_get_if_active(&udev->dev) != 1) { + ret = -EBUSY; + goto err_rpm; + } + + spin_lock(&udev->offload_lock); + + if (udev->offload_pm_locked) { + ret = -EAGAIN; + goto err; + } + + /* Drop the count when it wasn't 0, ignore the operation otherwise. */ + if (udev->offload_usage) + udev->offload_usage--; + +err: + spin_unlock(&udev->offload_lock); + pm_runtime_put_autosuspend(&udev->dev); +err_rpm: + usb_put_dev(udev); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_offload_put); + +/** + * usb_offload_check - check offload activities on a USB device + * @udev: the USB device to check its offload activity. + * + * Check if there are any offload activity on the USB device right now. This + * information could be used for power management or other forms of resource + * management. + * + * The caller must hold @udev's device lock. In addition, the caller should + * ensure the device itself and the downstream usb devices are all marked as + * "offload_pm_locked" to ensure the correctness of the return value. + * + * Returns true on any offload activity, false otherwise. + */ +bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex) +{ + struct usb_device *child; + bool active = false; + int port1; + + if (udev->offload_usage) + return true; + + usb_hub_for_each_child(udev, port1, child) { + usb_lock_device(child); + active = usb_offload_check(child); + usb_unlock_device(child); + + if (active) + break; + } + + return active; +} +EXPORT_SYMBOL_GPL(usb_offload_check); + +/** + * usb_offload_set_pm_locked - set the PM lock state of a USB device + * @udev: the USB device to modify + * @locked: the new lock state + * + * Setting @locked to true prevents offload_usage from being modified. This + * ensures that offload activities cannot be started or stopped during critical + * power management transitions, maintaining a stable state for the duration + * of the transition. + */ +void usb_offload_set_pm_locked(struct usb_device *udev, bool locked) +{ + spin_lock(&udev->offload_lock); + udev->offload_pm_locked = locked; + spin_unlock(&udev->offload_lock); +} +EXPORT_SYMBOL_GPL(usb_offload_set_pm_locked); diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c index faa20054ad5a..4d966cc9cdc9 100644 --- a/drivers/usb/core/phy.c +++ b/drivers/usb/core/phy.c @@ -114,7 +114,7 @@ EXPORT_SYMBOL_GPL(usb_phy_roothub_alloc); struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev) { struct usb_phy_roothub *phy_roothub; - int num_phys; + int num_phys, usb2_phy_index; if (!IS_ENABLED(CONFIG_GENERIC_PHY)) return NULL; @@ -124,6 +124,16 @@ struct usb_phy_roothub *usb_phy_roothub_alloc_usb3_phy(struct device *dev) if (num_phys <= 0) return NULL; + /* + * If 'usb2-phy' is not present, usb_phy_roothub_alloc() added + * all PHYs to the primary HCD's phy_roothub already, so skip + * adding 'usb3-phy' here to avoid double use of that. + */ + usb2_phy_index = of_property_match_string(dev->of_node, "phy-names", + "usb2-phy"); + if (usb2_phy_index < 0) + return NULL; + phy_roothub = devm_kzalloc(dev, sizeof(*phy_roothub), GFP_KERNEL); if (!phy_roothub) return ERR_PTR(-ENOMEM); @@ -200,16 +210,10 @@ int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub, list_for_each_entry(roothub_entry, head, list) { err = phy_set_mode(roothub_entry->phy, mode); if (err) - goto err_out; + return err; } return 0; - -err_out: - list_for_each_entry_continue_reverse(roothub_entry, head, list) - phy_power_off(roothub_entry->phy); - - return err; } EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode); diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index f54198171b6a..b1364f0c384c 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -21,6 +21,20 @@ static int usb_port_block_power_off; static const struct attribute_group *port_dev_group[]; +static bool usb_port_allow_power_off(struct usb_device *hdev, + struct usb_hub *hub, + struct usb_port *port_dev) +{ + if (hub_is_port_power_switchable(hub)) + return true; + + if (!IS_ENABLED(CONFIG_ACPI)) + return false; + + return port_dev->connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED && + usb_acpi_power_manageable(hdev, port_dev->portnum - 1); +} + static ssize_t early_stop_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -141,6 +155,7 @@ static ssize_t disable_store(struct device *dev, struct device_attribute *attr, usb_disconnect(&port_dev->child); rc = usb_hub_set_port_power(hdev, hub, port1, !disabled); + msleep(2 * hub_power_on_good_delay(hub)); if (disabled) { usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION); @@ -739,11 +754,11 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) struct usb_device *hdev = hub->hdev; int retval; - port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL); + port_dev = kzalloc_obj(*port_dev); if (!port_dev) return -ENOMEM; - port_dev->req = kzalloc(sizeof(*(port_dev->req)), GFP_KERNEL); + port_dev->req = kzalloc_obj(*(port_dev->req)); if (!port_dev->req) { kfree(port_dev); return -ENOMEM; @@ -805,10 +820,10 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) device_enable_async_suspend(&port_dev->dev); /* - * Keep hidden the ability to enable port-poweroff if the hub - * does not support power switching. + * Keep hidden the ability to enable port-poweroff if neither the + * USB hub nor platform firmware can manage downstream port power. */ - if (!hub_is_port_power_switchable(hub)) + if (!usb_port_allow_power_off(hdev, hub, port_dev)) return 0; /* Attempt to let userspace take over the policy. */ diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 8efbacc5bc34..0ffdaefba508 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -61,8 +61,7 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp) quirk_list = NULL; } - quirk_list = kcalloc(quirk_count, sizeof(struct quirk_entry), - GFP_KERNEL); + quirk_list = kzalloc_objs(struct quirk_entry, quirk_count); if (!quirk_list) { quirk_count = 0; mutex_unlock(&quirk_mutex); @@ -141,6 +140,8 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp) case 'p': flags |= USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT; break; + case 'q': + flags |= USB_QUIRK_FORCE_ONE_CONFIG; /* Ignore unrecognized flag characters */ } } @@ -208,6 +209,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* HP v222w 16GB Mini USB Drive */ { USB_DEVICE(0x03f0, 0x3f40), .driver_info = USB_QUIRK_DELAY_INIT }, + /* Huawei 4G LTE module ME906S */ + { USB_DEVICE(0x03f0, 0xa31d), .driver_info = + USB_QUIRK_DISCONNECT_SUSPEND }, + /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, @@ -227,7 +232,8 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME }, /* Logitech HD Webcam C270 */ - { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME }, + { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME | + USB_QUIRK_NO_LPM}, /* Logitech HD Pro Webcams C920, C920-C, C922, C925e and C930e */ { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT }, @@ -369,6 +375,16 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x0781, 0x5583), .driver_info = USB_QUIRK_NO_LPM }, { USB_DEVICE(0x0781, 0x5591), .driver_info = USB_QUIRK_NO_LPM }, + /* SanDisk Corp. SanDisk 3.2Gen1 */ + { USB_DEVICE(0x0781, 0x5596), .driver_info = USB_QUIRK_DELAY_INIT }, + { USB_DEVICE(0x0781, 0x55a3), .driver_info = USB_QUIRK_DELAY_INIT }, + + /* SanDisk Extreme 55AE */ + { USB_DEVICE(0x0781, 0x55ae), .driver_info = USB_QUIRK_NO_LPM }, + + /* Avermedia Live Gamer Ultra 2.1 (GC553G2) - BOS descriptor fetch hangs at SuperSpeed Plus */ + { USB_DEVICE(0x07ca, 0x2553), .driver_info = USB_QUIRK_NO_BOS }, + /* Realforce 87U Keyboard */ { USB_DEVICE(0x0853, 0x011b), .driver_info = USB_QUIRK_NO_LPM }, @@ -383,6 +399,10 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x0904, 0x6103), .driver_info = USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL }, + /* Silicon Motion Flash Drive */ + { USB_DEVICE(0x090c, 0x1000), .driver_info = USB_QUIRK_DELAY_INIT }, + { USB_DEVICE(0x090c, 0x2000), .driver_info = USB_QUIRK_DELAY_INIT }, + /* Sound Devices USBPre2 */ { USB_DEVICE(0x0926, 0x0202), .driver_info = USB_QUIRK_ENDPOINT_IGNORE }, @@ -426,6 +446,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x0b05, 0x17e0), .driver_info = USB_QUIRK_IGNORE_REMOTE_WAKEUP }, + /* ASUS TUF 4K PRO - BOS descriptor fetch hangs at SuperSpeed Plus */ + { USB_DEVICE(0x0b05, 0x1ab9), .driver_info = USB_QUIRK_NO_BOS }, + /* Realtek Semiconductor Corp. Mass Storage Device (Multicard Reader)*/ { USB_DEVICE(0x0bda, 0x0151), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, @@ -439,6 +462,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x0c45, 0x7056), .driver_info = USB_QUIRK_IGNORE_REMOTE_WAKEUP }, + /* Elgato 4K X - BOS descriptor fetch hangs at SuperSpeed Plus */ + { USB_DEVICE(0x0fd9, 0x009b), .driver_info = USB_QUIRK_NO_BOS }, + /* Sony Xperia XZ1 Compact (lilac) smartphone in fastboot mode */ { USB_DEVICE(0x0fce, 0x0dde), .driver_info = USB_QUIRK_NO_LPM }, @@ -456,6 +482,8 @@ static const struct usb_device_id usb_quirk_list[] = { /* Huawei 4G LTE module */ { USB_DEVICE(0x12d1, 0x15bb), .driver_info = USB_QUIRK_DISCONNECT_SUSPEND }, + { USB_DEVICE(0x12d1, 0x15c1), .driver_info = + USB_QUIRK_DISCONNECT_SUSPEND }, { USB_DEVICE(0x12d1, 0x15c3), .driver_info = USB_QUIRK_DISCONNECT_SUSPEND }, @@ -465,6 +493,8 @@ static const struct usb_device_id usb_quirk_list[] = { /* Razer - Razer Blade Keyboard */ { USB_DEVICE(0x1532, 0x0116), .driver_info = USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + /* Razer - Razer Kiyo Pro Webcam */ + { USB_DEVICE(0x1532, 0x0e05), .driver_info = USB_QUIRK_NO_LPM }, /* Lenovo ThinkPad OneLink+ Dock twin hub controllers (VIA Labs VL812) */ { USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME }, @@ -539,6 +569,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x2040, 0x7200), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* VLI disk */ + { USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM }, + /* Raydium Touchscreen */ { USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM }, @@ -546,6 +579,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM }, + /* UGREEN 35871 - BOS descriptor fetch hangs at SuperSpeed Plus */ + { USB_DEVICE(0x2b89, 0x5871), .driver_info = USB_QUIRK_NO_BOS }, + /* APTIV AUTOMOTIVE HUB */ { USB_DEVICE(0x2c48, 0x0132), .driver_info = USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT }, @@ -556,12 +592,18 @@ static const struct usb_device_id usb_quirk_list[] = { /* Alcor Link AK9563 SC Reader used in 2022 Lenovo ThinkPads */ { USB_DEVICE(0x2ce3, 0x9563), .driver_info = USB_QUIRK_NO_LPM }, + /* ezcap401 - BOS descriptor fetch hangs at SuperSpeed Plus */ + { USB_DEVICE(0x32ed, 0x0401), .driver_info = USB_QUIRK_NO_BOS }, + /* DELL USB GEN2 */ { USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME }, /* VCOM device */ { USB_DEVICE(0x4296, 0x7570), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Noji-MCS SmartCard Reader */ + { USB_DEVICE(0x5131, 0x2007), .driver_info = USB_QUIRK_FORCE_ONE_CONFIG }, + /* INTEL VALUE SSD */ { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME }, @@ -723,7 +765,7 @@ void usb_detect_quirks(struct usb_device *udev) udev->quirks ^= usb_detect_dynamic_quirks(udev); if (udev->quirks) - dev_dbg(&udev->dev, "USB quirks for this device: %x\n", + dev_dbg(&udev->dev, "USB quirks for this device: 0x%x\n", udev->quirks); #ifdef CONFIG_USB_DEFAULT_PERSIST diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 23f3cb1989f4..a07866f1060c 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -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_new = dev_bin_attrs, + .bin_attrs = dev_bin_attrs, .is_bin_visible = dev_bin_attrs_are_visible, }; diff --git a/drivers/usb/core/trace.c b/drivers/usb/core/trace.c new file mode 100644 index 000000000000..607bcf639d27 --- /dev/null +++ b/drivers/usb/core/trace.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Google LLC + */ +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/usb/core/trace.h b/drivers/usb/core/trace.h new file mode 100644 index 000000000000..903e57dc273a --- /dev/null +++ b/drivers/usb/core/trace.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2025 Google LLC + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM usbcore + +#if !defined(_USB_CORE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _USB_CORE_TRACE_H + +#include <linux/types.h> +#include <linux/tracepoint.h> +#include <linux/usb.h> + +DECLARE_EVENT_CLASS(usb_core_log_usb_device, + TP_PROTO(struct usb_device *udev), + TP_ARGS(udev), + TP_STRUCT__entry( + __string(name, dev_name(&udev->dev)) + __field(enum usb_device_speed, speed) + __field(enum usb_device_state, state) + __field(unsigned short, bus_mA) + __field(unsigned, authorized) + ), + TP_fast_assign( + __assign_str(name); + __entry->speed = udev->speed; + __entry->state = udev->state; + __entry->bus_mA = udev->bus_mA; + __entry->authorized = udev->authorized; + ), + TP_printk("usb %s speed %s state %s %dmA [%s]", + __get_str(name), + usb_speed_string(__entry->speed), + usb_state_string(__entry->state), + __entry->bus_mA, + __entry->authorized ? "authorized" : "unauthorized") +); + +DEFINE_EVENT(usb_core_log_usb_device, usb_set_device_state, + TP_PROTO(struct usb_device *udev), + TP_ARGS(udev) +); + +DEFINE_EVENT(usb_core_log_usb_device, usb_alloc_dev, + TP_PROTO(struct usb_device *udev), + TP_ARGS(udev) +); + + +#endif /* _USB_CORE_TRACE_H */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include <trace/define_trace.h> diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 7576920e2d5a..c06b44ca507b 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -72,8 +72,7 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags) { struct urb *urb; - urb = kmalloc(struct_size(urb, iso_frame_desc, iso_packets), - mem_flags); + urb = kmalloc_flex(*urb, iso_frame_desc, iso_packets, mem_flags); if (!urb) return NULL; usb_init_urb(urb); @@ -372,11 +371,12 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) struct usb_host_endpoint *ep; int is_out; unsigned int allowed; + bool is_eusb2_isoch_double; 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; } @@ -434,7 +434,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) return -ENODEV; max = usb_endpoint_maxp(&ep->desc); - if (max <= 0) { + is_eusb2_isoch_double = usb_endpoint_is_hs_isoc_double(dev, ep); + if (!max && !is_eusb2_isoch_double) { dev_dbg(&dev->dev, "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n", usb_endpoint_num(&ep->desc), is_out ? "out" : "in", @@ -467,9 +468,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) max = le32_to_cpu(isoc_ep_comp->dwBytesPerInterval); } - /* "high bandwidth" mode, 1-3 packets/uframe? */ - if (dev->speed == USB_SPEED_HIGH) - max *= usb_endpoint_maxp_mult(&ep->desc); + /* High speed, 1-3 packets/uframe, max 6 for eUSB2 double bw */ + if (dev->speed == USB_SPEED_HIGH) { + if (is_eusb2_isoch_double) + max = le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval); + else + max *= usb_endpoint_maxp_mult(&ep->desc); + } if (urb->number_of_packets <= 0) return -EINVAL; @@ -500,7 +505,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) /* Check that the pipe's type matches the endpoint's type */ if (usb_pipe_type_check(urb->dev, urb->pipe)) - dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n", + dev_warn_once(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n", usb_pipetype(urb->pipe), pipetypes[xfertype]); /* Check against a simple/standard policy */ @@ -597,10 +602,9 @@ EXPORT_SYMBOL_GPL(usb_submit_urb); * code). * * Drivers should not call this routine or related routines, such as - * usb_kill_urb() or usb_unlink_anchored_urbs(), after their disconnect - * method has returned. The disconnect function should synchronize with - * a driver's I/O routines to insure that all URB-related activity has - * completed before it returns. + * usb_kill_urb(), after their disconnect method has returned. The + * disconnect function should synchronize with a driver's I/O routines + * to insure that all URB-related activity has completed before it returns. * * This request is asynchronous, however the HCD might call the ->complete() * callback during unlink. Therefore when drivers call usb_unlink_urb(), they @@ -890,28 +894,6 @@ void usb_unpoison_anchored_urbs(struct usb_anchor *anchor) spin_unlock_irqrestore(&anchor->lock, flags); } EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs); -/** - * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse - * @anchor: anchor the requests are bound to - * - * this allows all outstanding URBs to be unlinked starting - * from the back of the queue. This function is asynchronous. - * The unlinking is just triggered. It may happen after this - * function has returned. - * - * This routine should not be called by a driver after its disconnect - * method has returned. - */ -void usb_unlink_anchored_urbs(struct usb_anchor *anchor) -{ - struct urb *victim; - - while ((victim = usb_get_from_anchor(anchor)) != NULL) { - usb_unlink_urb(victim); - usb_put_urb(victim); - } -} -EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs); /** * usb_anchor_suspend_wakeups diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 935c0efea0b6..489dbdc96f94 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -157,7 +157,7 @@ EXPORT_SYMBOL_GPL(usb_acpi_set_power_state); */ static int usb_acpi_add_usb4_devlink(struct usb_device *udev) { - const struct device_link *link; + struct device_link *link; struct usb_port *port_dev; struct usb_hub *hub; @@ -165,6 +165,8 @@ static int usb_acpi_add_usb4_devlink(struct usb_device *udev) return 0; hub = usb_hub_to_struct_hub(udev->parent); + if (!hub) + return 0; port_dev = hub->ports[udev->portnum - 1]; struct fwnode_handle *nhi_fwnode __free(fwnode_handle) = @@ -186,6 +188,8 @@ static int usb_acpi_add_usb4_devlink(struct usb_device *udev) dev_dbg(&port_dev->dev, "Created device link from %s to %s\n", dev_name(&port_dev->child->dev), dev_name(nhi_fwnode->dev)); + udev->usb4_link = link; + return 0; } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 0b4685aad2d5..df166cafe106 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -46,6 +46,7 @@ #include <linux/dma-mapping.h> #include "hub.h" +#include "trace.h" const char *usbcore_name = "usbcore"; @@ -647,7 +648,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_hcd *usb_hcd = bus_to_hcd(bus); unsigned raw_port = port1; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) return NULL; @@ -670,6 +671,8 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, set_dev_node(&dev->dev, dev_to_node(bus->sysdev)); dev->state = USB_STATE_ATTACHED; dev->lpm_disable_count = 1; + spin_lock_init(&dev->offload_lock); + dev->offload_usage = 0; atomic_set(&dev->urbnum, 0); INIT_LIST_HEAD(&dev->ep0.urb_list); @@ -695,15 +698,16 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, device_set_of_node_from_dev(&dev->dev, bus->sysdev); dev_set_name(&dev->dev, "usb%d", bus->busnum); } else { + int n; + /* match any labeling on the hubs; it's one-based */ if (parent->devpath[0] == '0') { - snprintf(dev->devpath, sizeof dev->devpath, - "%d", port1); + n = snprintf(dev->devpath, sizeof(dev->devpath), "%d", port1); /* Root ports are not counted in route string */ dev->route = 0; } else { - snprintf(dev->devpath, sizeof dev->devpath, - "%s.%d", parent->devpath, port1); + n = snprintf(dev->devpath, sizeof(dev->devpath), "%s.%d", + parent->devpath, port1); /* Route string assumes hubs have less than 16 ports */ if (port1 < 15) dev->route = parent->route + @@ -712,6 +716,11 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, dev->route = parent->route + (15 << ((parent->level - 1)*4)); } + if (n >= sizeof(dev->devpath)) { + usb_put_hcd(bus_to_hcd(bus)); + usb_put_dev(dev); + return NULL; + } dev->dev.parent = &parent->dev; dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath); @@ -739,6 +748,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, #endif dev->authorized = usb_dev_authorized(dev, usb_hcd); + trace_usb_alloc_dev(dev); return dev; } EXPORT_SYMBOL_GPL(usb_alloc_dev); @@ -1024,6 +1034,136 @@ void usb_free_coherent(struct usb_device *dev, size_t size, void *addr, } EXPORT_SYMBOL_GPL(usb_free_coherent); +/** + * usb_alloc_noncoherent - allocate dma-noncoherent buffer for URB_NO_xxx_DMA_MAP + * @dev: device the buffer will be used with + * @size: requested buffer size + * @mem_flags: affect whether allocation may block + * @dma: used to return DMA address of buffer + * @dir: DMA transfer direction + * @table: used to return sg_table of allocated memory + * + * To explicit manage the memory ownership for the kernel vs the device by + * USB core, the user needs save sg_table to urb->sgt. Then USB core will + * do DMA sync for CPU and device properly. + * + * When the buffer is no longer used, free it with usb_free_noncoherent(). + * + * Return: Either null (indicating no buffer could be allocated), or the + * cpu-space pointer to a buffer that may be used to perform DMA to the + * specified device. Such cpu-space buffers are returned along with the DMA + * address (through the pointer provided). + */ +void *usb_alloc_noncoherent(struct usb_device *dev, size_t size, + gfp_t mem_flags, dma_addr_t *dma, + enum dma_data_direction dir, + struct sg_table **table) +{ + struct device *dmadev; + struct sg_table *sgt; + void *buffer; + + if (!dev || !dev->bus) + return NULL; + + dmadev = bus_to_hcd(dev->bus)->self.sysdev; + + sgt = dma_alloc_noncontiguous(dmadev, size, dir, mem_flags, 0); + if (!sgt) + return NULL; + + buffer = dma_vmap_noncontiguous(dmadev, size, sgt); + if (!buffer) { + dma_free_noncontiguous(dmadev, size, sgt, dir); + return NULL; + } + + *table = sgt; + *dma = sg_dma_address(sgt->sgl); + + return buffer; +} +EXPORT_SYMBOL_GPL(usb_alloc_noncoherent); + +/** + * usb_free_noncoherent - free memory allocated with usb_alloc_noncoherent() + * @dev: device the buffer was used with + * @size: requested buffer size + * @addr: CPU address of buffer + * @dir: DMA transfer direction + * @table: describe the allocated and DMA mapped memory, + * + * This reclaims an I/O buffer, letting it be reused. The memory must have + * been allocated using usb_alloc_noncoherent(), and the parameters must match + * those provided in that allocation request. + */ +void usb_free_noncoherent(struct usb_device *dev, size_t size, + void *addr, enum dma_data_direction dir, + struct sg_table *table) +{ + struct device *dmadev; + + if (!dev || !dev->bus) + return; + if (!addr) + return; + + dmadev = bus_to_hcd(dev->bus)->self.sysdev; + dma_vunmap_noncontiguous(dmadev, addr); + dma_free_noncontiguous(dmadev, size, table, dir); +} +EXPORT_SYMBOL_GPL(usb_free_noncoherent); + +/** + * usb_endpoint_max_periodic_payload - Get maximum payload bytes per service + * interval + * @udev: The USB device + * @ep: The endpoint + * + * Returns: the maximum number of bytes isochronous or interrupt endpoint @ep + * can transfer during a service interval, or 0 for other endpoints. + */ +u32 usb_endpoint_max_periodic_payload(struct usb_device *udev, + const struct usb_host_endpoint *ep) +{ + if (!usb_endpoint_xfer_isoc(&ep->desc) && + !usb_endpoint_xfer_int(&ep->desc)) + return 0; + + switch (udev->speed) { + case USB_SPEED_SUPER_PLUS: + if (USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) + return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval); + fallthrough; + case USB_SPEED_SUPER: + return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); + default: + if (usb_endpoint_is_hs_isoc_double(udev, ep)) + return le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval); + return usb_endpoint_maxp(&ep->desc) * usb_endpoint_maxp_mult(&ep->desc); + } +} +EXPORT_SYMBOL_GPL(usb_endpoint_max_periodic_payload); + +/** + * usb_endpoint_is_hs_isoc_double - Tell whether an endpoint uses USB 2 + * Isochronous Double IN Bandwidth + * @udev: The USB device + * @ep: The endpoint + * + * Returns: true if an endpoint @ep conforms to USB 2 Isochronous Double IN + * Bandwidth ECN, false otherwise. + */ +bool usb_endpoint_is_hs_isoc_double(struct usb_device *udev, + const struct usb_host_endpoint *ep) +{ + return ep->eusb2_isoc_ep_comp.bDescriptorType && + le16_to_cpu(udev->descriptor.bcdUSB) == 0x220 && + usb_endpoint_is_isoc_in(&ep->desc) && + !le16_to_cpu(ep->desc.wMaxPacketSize); +} +EXPORT_SYMBOL_GPL(usb_endpoint_is_hs_isoc_double); + /* * Notifications of device and interface registration */ diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 9919ab725d54..f375c5185bfe 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); @@ -577,6 +578,7 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg) { switch (hsotg->dr_mode) { case USB_DR_MODE_HOST: + dwc2_force_mode(hsotg, true); /* * NOTE: This is required for some rockchip soc based * platforms on their host-only dwc2. 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 bd4c788f03bc..c8b02c27d27d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -397,7 +397,7 @@ static struct usb_request *dwc2_hsotg_ep_alloc_request(struct usb_ep *ep, { struct dwc2_hsotg_req *req; - req = kzalloc(sizeof(*req), flags); + req = kzalloc_obj(*req, flags); if (!req) return NULL; @@ -4049,7 +4049,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, return -EINVAL; } - ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + ep_type = usb_endpoint_type(desc); mps = usb_endpoint_maxp(desc); mc = usb_endpoint_maxp_mult(desc); @@ -4604,6 +4604,14 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) if (!hsotg) return -ENODEV; + /* Exit clock gating when driver is stopped. */ + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && + hsotg->bus_suspended && !hsotg->params.no_clock_gating) { + spin_lock_irqsave(&hsotg->lock, flags); + dwc2_gadget_exit_clock_gating(hsotg, 0); + spin_unlock_irqrestore(&hsotg->lock, flags); + } + /* all endpoints should be shutdown */ for (ep = 1; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) @@ -5204,11 +5212,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; @@ -5224,7 +5232,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); @@ -5310,6 +5321,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. * @@ -5327,18 +5381,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); @@ -5346,20 +5391,34 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) { /* ULPI interface */ gpwrdn |= GPWRDN_ULPI_LATCH_EN_DURING_HIB_ENTRY; - } - dwc2_writel(hsotg, gpwrdn, GPWRDN); - udelay(10); + dwc2_writel(hsotg, gpwrdn, GPWRDN); + udelay(10); - /* Suspend the Phy Clock */ - pcgcctl = dwc2_readl(hsotg, PCGCTL); - pcgcctl |= PCGCTL_STOPPCLK; - dwc2_writel(hsotg, pcgcctl, PCGCTL); - udelay(10); + /* Suspend the Phy Clock */ + pcgcctl = dwc2_readl(hsotg, PCGCTL); + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(10); - gpwrdn = dwc2_readl(hsotg, GPWRDN); - gpwrdn |= GPWRDN_PMUACTV; - dwc2_writel(hsotg, gpwrdn, GPWRDN); - udelay(10); + gpwrdn = dwc2_readl(hsotg, GPWRDN); + gpwrdn |= GPWRDN_PMUACTV; + dwc2_writel(hsotg, gpwrdn, GPWRDN); + udelay(10); + } else { + /* UTMI+ Interface */ + dwc2_writel(hsotg, gpwrdn, GPWRDN); + udelay(10); + + gpwrdn = dwc2_readl(hsotg, GPWRDN); + gpwrdn |= GPWRDN_PMUACTV; + dwc2_writel(hsotg, gpwrdn, GPWRDN); + udelay(10); + + pcgcctl = dwc2_readl(hsotg, PCGCTL); + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(hsotg, pcgcctl, PCGCTL); + udelay(10); + } /* Set flag to indicate that we are in hibernation */ hsotg->hibernated = 1; @@ -5415,6 +5474,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; @@ -5477,6 +5537,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); @@ -5484,20 +5545,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); @@ -5531,19 +5581,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 @@ -5590,11 +5630,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); @@ -5611,21 +5648,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 8c3941ecaaf5..1a763ad4f721 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -3227,7 +3227,7 @@ host: static void dwc2_wakeup_detected(struct timer_list *t) { - struct dwc2_hsotg *hsotg = from_timer(hsotg, t, wkp_timer); + struct dwc2_hsotg *hsotg = timer_container_of(hsotg, t, wkp_timer); u32 hprt0; dev_dbg(hsotg->dev, "%s()\n", __func__); @@ -3823,7 +3823,7 @@ static struct dwc2_hcd_urb *dwc2_hcd_urb_alloc(struct dwc2_hsotg *hsotg, { struct dwc2_hcd_urb *urb; - urb = kzalloc(struct_size(urb, iso_descs, iso_desc_count), mem_flags); + urb = kzalloc_flex(*urb, iso_descs, iso_desc_count, mem_flags); if (urb) urb->packet_count = iso_desc_count; return urb; @@ -4743,7 +4743,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, qh_allocated = true; } - qtd = kzalloc(sizeof(*qtd), mem_flags); + qtd = kzalloc_obj(*qtd, mem_flags); if (!qtd) { retval = -ENOMEM; goto fail1; @@ -5081,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) @@ -5218,7 +5218,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) memset(&hsotg->hc_ptr_array[0], 0, sizeof(hsotg->hc_ptr_array)); for (i = 0; i < num_channels; i++) { - channel = kzalloc(sizeof(*channel), GFP_KERNEL); + channel = kzalloc_obj(*channel); if (!channel) goto error3; channel->hc_num = i; @@ -5474,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. * @@ -5489,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); @@ -5694,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__); + ret = dwc2_host_restore_critical_registers(hsotg); + if (ret) 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__); - return ret; - } if (rem_wakeup) { dwc2_hcd_rem_wakeup(hsotg); @@ -5774,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 @@ -5855,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..4f00ff1dc3e1 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -1215,7 +1215,7 @@ static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) */ static void dwc2_unreserve_timer_fn(struct timer_list *t) { - struct dwc2_qh *qh = from_timer(qh, t, unreserve_timer); + struct dwc2_qh *qh = timer_container_of(qh, t, unreserve_timer); struct dwc2_hsotg *hsotg = qh->hsotg; unsigned long flags; @@ -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; @@ -1586,7 +1585,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, return NULL; /* Allocate memory */ - qh = kzalloc(sizeof(*qh), mem_flags); + qh = kzalloc_obj(*qh, mem_flags); if (!qh) return NULL; @@ -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/params.c b/drivers/usb/dwc2/params.c index 4d73fae80b12..091bfcfef753 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -334,7 +334,7 @@ const struct of_device_id dwc2_of_match_table[] = { .data = dwc2_set_amlogic_a1_params }, { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params }, { .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params }, - { .compatible = "sophgo,cv1800-usb", + { .compatible = "sophgo,cv1800b-usb", .data = dwc2_set_cv1800_params }, { .compatible = "st,stm32f4x9-fsotg", .data = dwc2_set_stm32f4x9_fsotg_params }, @@ -1029,11 +1029,33 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) return 0; } +static int dwc2_limit_speed(struct dwc2_hsotg *hsotg) +{ + enum usb_device_speed usb_speed; + + usb_speed = usb_get_maximum_speed(hsotg->dev); + switch (usb_speed) { + case USB_SPEED_LOW: + dev_err(hsotg->dev, "Maximum speed cannot be forced to low-speed\n"); + return -EINVAL; + case USB_SPEED_FULL: + if (hsotg->params.speed == DWC2_SPEED_PARAM_LOW) + break; + hsotg->params.speed = DWC2_SPEED_PARAM_FULL; + break; + default: + break; + } + + return 0; +} + typedef void (*set_params_cb)(struct dwc2_hsotg *data); int dwc2_init_params(struct dwc2_hsotg *hsotg) { set_params_cb set_params; + int ret; dwc2_set_default_params(hsotg); dwc2_get_device_properties(hsotg); @@ -1051,6 +1073,10 @@ int dwc2_init_params(struct dwc2_hsotg *hsotg) } } + ret = dwc2_limit_speed(hsotg); + if (ret) + return ret; + dwc2_check_params(hsotg); return 0; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 91c80a92d9b8..ef0d73077034 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -369,8 +369,11 @@ static void dwc2_driver_shutdown(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); - dwc2_disable_global_interrupts(hsotg); - synchronize_irq(hsotg->irq); + if (hsotg->ll_hw_enabled) { + dwc2_disable_global_interrupts(hsotg); + synchronize_irq(hsotg->irq); + dwc2_lowlevel_hw_disable(hsotg); + } } /** @@ -646,9 +649,13 @@ error: static int __maybe_unused dwc2_suspend(struct device *dev) { struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); - bool is_device_mode = dwc2_is_device_mode(dwc2); + bool is_device_mode; int ret = 0; + if (!dwc2->ll_hw_enabled) + return 0; + + is_device_mode = dwc2_is_device_mode(dwc2); if (is_device_mode) dwc2_hsotg_suspend(dwc2); @@ -685,6 +692,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,11 +709,32 @@ 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); int ret = 0; + if (!dwc2->ll_hw_enabled) + return 0; + if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) { ret = __dwc2_lowlevel_hw_enable(dwc2); if (ret) @@ -706,6 +742,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; diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 310d182e10b5..18169727a413 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -150,6 +150,18 @@ config USB_DWC3_IMX8MP functionality. Say 'Y' or 'M' if you have one such device. +config USB_DWC3_IMX + tristate "NXP iMX Platform" + depends on OF && COMMON_CLK + depends on (ARCH_MXC && ARM64) || COMPILE_TEST + default USB_DWC3 + help + NXP iMX SoC use DesignWare Core IP for USB2/3 + functionality. + This driver also handles the wakeup feature outside + of DesignWare Core. + Say 'Y' or 'M' if you have one such device. + config USB_DWC3_XILINX tristate "Xilinx Platforms" depends on (ARCH_ZYNQMP || COMPILE_TEST) && OF @@ -189,4 +201,37 @@ config USB_DWC3_RTK or dual-role mode. Say 'Y' or 'M' if you have such device. +config USB_DWC3_GENERIC_PLAT + tristate "DWC3 Generic Platform Driver" + depends on OF && COMMON_CLK + default USB_DWC3 + help + Support USB3 functionality in simple SoC integrations. + Currently supports SpacemiT DWC USB3. Platforms using + dwc3-of-simple can easily switch to dwc3-generic by flattening + the dwc3 child node in the device tree. + Say 'Y' or 'M' here if your platform integrates DWC3 in a similar way. + +config USB_DWC3_APPLE + tristate "Apple Silicon DWC3 Platform Driver" + depends on OF && ARCH_APPLE + default USB_DWC3 + select USB_ROLE_SWITCH + help + Support Apple Silicon SoCs with DesignWare Core USB3 IP. + The DesignWare Core USB3 IP has to be used in dual-role + mode on these machines. + Say 'Y' or 'M' if you have such device. + +config USB_DWC3_GOOGLE + tristate "Google Platform" + help + Support the DesignWare Core USB3 IP found on Google Tensor SoCs, + starting with the G5 generation (Laguna). This driver includes + support for hibernation in host mode. + Say 'Y' or 'M' if you have one such device. + + To compile this driver as a module, choose M here: the + module will be called dwc3-google.ko. + endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 124eda2522d9..f37971197203 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -43,6 +43,7 @@ endif ## obj-$(CONFIG_USB_DWC3_AM62) += dwc3-am62.o +obj-$(CONFIG_USB_DWC3_APPLE) += dwc3-apple.o obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o @@ -52,7 +53,11 @@ obj-$(CONFIG_USB_DWC3_MESON_G12A) += dwc3-meson-g12a.o obj-$(CONFIG_USB_DWC3_OF_SIMPLE) += dwc3-of-simple.o obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom.o +obj-$(CONFIG_USB_DWC3_QCOM) += dwc3-qcom-legacy.o obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o +obj-$(CONFIG_USB_DWC3_IMX) += dwc3-imx.o obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o +obj-$(CONFIG_USB_DWC3_GENERIC_PLAT) += dwc3-generic-plat.o +obj-$(CONFIG_USB_DWC3_GOOGLE) += dwc3-google.o diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 66a08b527165..65213896de99 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -25,7 +25,9 @@ #include <linux/of.h> #include <linux/of_graph.h> #include <linux/acpi.h> +#include <linux/pci.h> #include <linux/pinctrl/consumer.h> +#include <linux/pinctrl/devinfo.h> #include <linux/reset.h> #include <linux/bitfield.h> @@ -36,6 +38,7 @@ #include "core.h" #include "gadget.h" +#include "glue.h" #include "io.h" #include "debug.h" @@ -111,32 +114,33 @@ void dwc3_enable_susphy(struct dwc3 *dwc, bool enable) int i; for (i = 0; i < dwc->num_usb3_ports; i++) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i)); + reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(i)); if (enable && !dwc->dis_u3_susphy_quirk) reg |= DWC3_GUSB3PIPECTL_SUSPHY; else reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg); + dwc3_writel(dwc, DWC3_GUSB3PIPECTL(i), reg); } for (i = 0; i < dwc->num_usb2_ports; i++) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i)); if (enable && !dwc->dis_u2_susphy_quirk) reg |= DWC3_GUSB2PHYCFG_SUSPHY; else reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg); } } +EXPORT_SYMBOL_GPL(dwc3_enable_susphy); 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); + reg = dwc3_readl(dwc, DWC3_GCTL); /* * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE and @@ -151,10 +155,12 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy) reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); reg |= DWC3_GCTL_PRTCAPDIR(mode); - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + dwc3_writel(dwc, DWC3_GCTL, reg); dwc->current_dr_role = mode; + trace_dwc3_set_prtcap(dwc, mode); } +EXPORT_SYMBOL_GPL(dwc3_set_prtcap); static void __dwc3_set_mode(struct work_struct *work) { @@ -210,9 +216,9 @@ static void __dwc3_set_mode(struct work_struct *work) if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) || DWC3_VER_IS_PRIOR(DWC31, 190A)) && desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) { - reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg = dwc3_readl(dwc, DWC3_GCTL); reg |= DWC3_GCTL_CORESOFTRESET; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + dwc3_writel(dwc, DWC3_GCTL, reg); /* * Wait for internal clocks to synchronized. DWC_usb31 and @@ -222,9 +228,9 @@ static void __dwc3_set_mode(struct work_struct *work) */ msleep(100); - reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg = dwc3_readl(dwc, DWC3_GCTL); reg &= ~DWC3_GCTL_CORESOFTRESET; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + dwc3_writel(dwc, DWC3_GCTL, reg); } spin_lock_irqsave(&dwc->lock, flags); @@ -248,9 +254,9 @@ static void __dwc3_set_mode(struct work_struct *work) phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); if (dwc->dis_split_quirk) { - reg = dwc3_readl(dwc->regs, DWC3_GUCTL3); + reg = dwc3_readl(dwc, DWC3_GUCTL3); reg |= DWC3_GUCTL3_SPLITDISABLE; - dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); + dwc3_writel(dwc, DWC3_GUCTL3, reg); } } break; @@ -277,7 +283,6 @@ static void __dwc3_set_mode(struct work_struct *work) } out: - pm_runtime_mark_last_busy(dwc->dev); pm_runtime_put_autosuspend(dwc->dev); mutex_unlock(&dwc->mutex); } @@ -301,11 +306,11 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type) struct dwc3 *dwc = dep->dwc; u32 reg; - dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE, - DWC3_GDBGFIFOSPACE_NUM(dep->number) | - DWC3_GDBGFIFOSPACE_TYPE(type)); + dwc3_writel(dwc, DWC3_GDBGFIFOSPACE, + DWC3_GDBGFIFOSPACE_NUM(dep->number) | + DWC3_GDBGFIFOSPACE_TYPE(type)); - reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE); + reg = dwc3_readl(dwc, DWC3_GDBGFIFOSPACE); return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg); } @@ -327,7 +332,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc) if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) return 0; - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); reg |= DWC3_DCTL_CSFTRST; reg &= ~DWC3_DCTL_RUN_STOP; dwc3_gadget_dctl_write_safe(dwc, reg); @@ -342,7 +347,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc) retries = 10; do { - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); if (!(reg & DWC3_DCTL_CSFTRST)) goto done; @@ -382,12 +387,12 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc) if (dwc->fladj == 0) return; - reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + reg = dwc3_readl(dwc, DWC3_GFLADJ); dft = reg & DWC3_GFLADJ_30MHZ_MASK; if (dft != dwc->fladj) { reg &= ~DWC3_GFLADJ_30MHZ_MASK; reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; - dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); + dwc3_writel(dwc, DWC3_GFLADJ, reg); } } @@ -419,10 +424,10 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) return; } - reg = dwc3_readl(dwc->regs, DWC3_GUCTL); + reg = dwc3_readl(dwc, DWC3_GUCTL); reg &= ~DWC3_GUCTL_REFCLKPER_MASK; reg |= FIELD_PREP(DWC3_GUCTL_REFCLKPER_MASK, period); - dwc3_writel(dwc->regs, DWC3_GUCTL, reg); + dwc3_writel(dwc, DWC3_GUCTL, reg); if (DWC3_VER_IS_PRIOR(DWC3, 250A)) return; @@ -450,7 +455,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) */ decr = 480000000 / rate; - reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + reg = dwc3_readl(dwc, DWC3_GFLADJ); reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK & ~DWC3_GFLADJ_240MHZDECR & ~DWC3_GFLADJ_240MHZDECR_PLS1; @@ -461,7 +466,7 @@ static void dwc3_ref_clk_period(struct dwc3 *dwc) if (dwc->gfladj_refclk_lpm_sel) reg |= DWC3_GFLADJ_REFCLK_LPM_SEL; - dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); + dwc3_writel(dwc, DWC3_GFLADJ, reg); } /** @@ -564,16 +569,16 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc) evt = dwc->ev_buf; evt->lpos = 0; - dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), - lower_32_bits(evt->dma)); - dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), - upper_32_bits(evt->dma)); - dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), - DWC3_GEVNTSIZ_SIZE(evt->length)); + dwc3_writel(dwc, DWC3_GEVNTADRLO(0), + lower_32_bits(evt->dma)); + dwc3_writel(dwc, DWC3_GEVNTADRHI(0), + upper_32_bits(evt->dma)); + dwc3_writel(dwc, DWC3_GEVNTSIZ(0), + DWC3_GEVNTSIZ_SIZE(evt->length)); /* Clear any stale event */ - reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg); + reg = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0)); + dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), reg); return 0; } @@ -588,7 +593,7 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc) * Exynos platforms may not be able to access event buffer if the * controller failed to halt on dwc3_core_exit(). */ - reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg = dwc3_readl(dwc, DWC3_DSTS); if (!(reg & DWC3_DSTS_DEVCTRLHLT)) return; @@ -596,14 +601,14 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc) evt->lpos = 0; - dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0); - dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0); - dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK + dwc3_writel(dwc, DWC3_GEVNTADRLO(0), 0); + dwc3_writel(dwc, DWC3_GEVNTADRHI(0), 0); + dwc3_writel(dwc, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(0)); /* Clear any stale event */ - reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), reg); + reg = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0)); + dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), reg); } static void dwc3_core_num_eps(struct dwc3 *dwc) @@ -617,18 +622,18 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) { struct dwc3_hwparams *parms = &dwc->hwparams; - parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0); - parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1); - parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2); - parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3); - parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4); - parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5); - parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); - parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); - parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); + parms->hwparams0 = dwc3_readl(dwc, DWC3_GHWPARAMS0); + parms->hwparams1 = dwc3_readl(dwc, DWC3_GHWPARAMS1); + parms->hwparams2 = dwc3_readl(dwc, DWC3_GHWPARAMS2); + parms->hwparams3 = dwc3_readl(dwc, DWC3_GHWPARAMS3); + parms->hwparams4 = dwc3_readl(dwc, DWC3_GHWPARAMS4); + parms->hwparams5 = dwc3_readl(dwc, DWC3_GHWPARAMS5); + parms->hwparams6 = dwc3_readl(dwc, DWC3_GHWPARAMS6); + parms->hwparams7 = dwc3_readl(dwc, DWC3_GHWPARAMS7); + parms->hwparams8 = dwc3_readl(dwc, DWC3_GHWPARAMS8); if (DWC3_IP_IS(DWC32)) - parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9); + parms->hwparams9 = dwc3_readl(dwc, DWC3_GHWPARAMS9); } static void dwc3_config_soc_bus(struct dwc3 *dwc) @@ -636,10 +641,10 @@ static void dwc3_config_soc_bus(struct dwc3 *dwc) if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) { u32 reg; - reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0); + reg = dwc3_readl(dwc, DWC3_GSBUSCFG0); reg &= ~DWC3_GSBUSCFG0_REQINFO(~0); reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo); - dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg); + dwc3_writel(dwc, DWC3_GSBUSCFG0, reg); } } @@ -663,7 +668,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index) { u32 reg; - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(index)); + reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(index)); /* * Make sure UX_EXIT_PX is cleared as that causes issues with some @@ -701,7 +706,7 @@ static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index) if (dwc->dis_del_phy_power_chg_quirk) reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(index), reg); + dwc3_writel(dwc, DWC3_GUSB3PIPECTL(index), reg); return 0; } @@ -710,7 +715,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index) { u32 reg; - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index)); /* Select the HS PHY interface */ switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { @@ -722,7 +727,7 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index) } else if (dwc->hsphy_interface && !strncmp(dwc->hsphy_interface, "ulpi", 4)) { reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg); } else { /* Relying on default value. */ if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) @@ -772,11 +777,29 @@ static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index) if (dwc->ulpi_ext_vbus_drv) reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg); return 0; } +static void dwc3_ulpi_setup(struct dwc3 *dwc) +{ + int index; + u32 reg; + + /* Don't do anything if there is no ULPI PHY */ + if (!dwc->ulpi) + return; + + if (dwc->enable_usb2_transceiver_delay) { + for (index = 0; index < dwc->num_usb2_ports; index++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); + reg |= DWC3_GUSB2PHYCFG_XCVRDLY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + } + } +} + /** * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * @dwc: Pointer to our controller context structure @@ -972,7 +995,7 @@ static void dwc3_clk_disable(struct dwc3 *dwc) clk_disable_unprepare(dwc->bus_clk); } -static void dwc3_core_exit(struct dwc3 *dwc) +void dwc3_core_exit(struct dwc3 *dwc) { dwc3_event_buffers_cleanup(dwc); dwc3_phy_power_off(dwc); @@ -980,20 +1003,23 @@ static void dwc3_core_exit(struct dwc3 *dwc) dwc3_clk_disable(dwc); reset_control_assert(dwc->reset); } +EXPORT_SYMBOL_GPL(dwc3_core_exit); static bool dwc3_core_is_valid(struct dwc3 *dwc) { u32 reg; - reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + reg = dwc3_readl(dwc, DWC3_GSNPSID); dwc->ip = DWC3_GSNPS_ID(reg); + if (dwc->ip == DWC4_IP) + dwc->ip = DWC32_IP; /* This should read as U3 followed by revision number */ if (DWC3_IP_IS(DWC3)) { dwc->revision = reg; } else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) { - dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); - dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE); + dwc->revision = dwc3_readl(dwc, DWC3_VER_NUMBER); + dwc->version_type = dwc3_readl(dwc, DWC3_VER_TYPE); } else { return false; } @@ -1007,7 +1033,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) unsigned int hw_mode; u32 reg; - reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg = dwc3_readl(dwc, DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN_MASK; hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); power_opt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1); @@ -1085,7 +1111,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc) if (DWC3_VER_IS_PRIOR(DWC3, 190A)) reg |= DWC3_GCTL_U2RSTECN; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + dwc3_writel(dwc, DWC3_GCTL, reg); } static int dwc3_core_get_phy(struct dwc3 *dwc); @@ -1105,7 +1131,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) int ret; int i; - cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0); + cfg = dwc3_readl(dwc, DWC3_GSBUSCFG0); /* * Handle property "snps,incr-burst-type-adjustment". @@ -1180,7 +1206,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) break; } - dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg); + dwc3_writel(dwc, DWC3_GSBUSCFG0, cfg); } static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc) @@ -1205,12 +1231,12 @@ static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc) * (3x or more) to be within the requirement. */ scale = DIV_ROUND_UP(clk_get_rate(dwc->susp_clk), 16000); - reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg = dwc3_readl(dwc, DWC3_GCTL); if ((reg & DWC3_GCTL_PWRDNSCALE_MASK) < DWC3_GCTL_PWRDNSCALE(scale) || (reg & DWC3_GCTL_PWRDNSCALE_MASK) > DWC3_GCTL_PWRDNSCALE(scale*3)) { reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK); reg |= DWC3_GCTL_PWRDNSCALE(scale); - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + dwc3_writel(dwc, DWC3_GCTL, reg); } } @@ -1233,7 +1259,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc) tx_maxburst = dwc->tx_max_burst_prd; if (rx_thr_num && rx_maxburst) { - reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); + reg = dwc3_readl(dwc, DWC3_GRXTHRCFG); reg |= DWC31_RXTHRNUMPKTSEL_PRD; reg &= ~DWC31_RXTHRNUMPKT_PRD(~0); @@ -1242,11 +1268,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc) reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0); reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst); - dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); + dwc3_writel(dwc, DWC3_GRXTHRCFG, reg); } if (tx_thr_num && tx_maxburst) { - reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG); + reg = dwc3_readl(dwc, DWC3_GTXTHRCFG); reg |= DWC31_TXTHRNUMPKTSEL_PRD; reg &= ~DWC31_TXTHRNUMPKT_PRD(~0); @@ -1255,7 +1281,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc) reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0); reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst); - dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg); + dwc3_writel(dwc, DWC3_GTXTHRCFG, reg); } } @@ -1266,7 +1292,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc) if (DWC3_IP_IS(DWC3)) { if (rx_thr_num && rx_maxburst) { - reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); + reg = dwc3_readl(dwc, DWC3_GRXTHRCFG); reg |= DWC3_GRXTHRCFG_PKTCNTSEL; reg &= ~DWC3_GRXTHRCFG_RXPKTCNT(~0); @@ -1275,11 +1301,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc) reg &= ~DWC3_GRXTHRCFG_MAXRXBURSTSIZE(~0); reg |= DWC3_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst); - dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); + dwc3_writel(dwc, DWC3_GRXTHRCFG, reg); } if (tx_thr_num && tx_maxburst) { - reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG); + reg = dwc3_readl(dwc, DWC3_GTXTHRCFG); reg |= DWC3_GTXTHRCFG_PKTCNTSEL; reg &= ~DWC3_GTXTHRCFG_TXPKTCNT(~0); @@ -1288,11 +1314,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc) reg &= ~DWC3_GTXTHRCFG_MAXTXBURSTSIZE(~0); reg |= DWC3_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst); - dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg); + dwc3_writel(dwc, DWC3_GTXTHRCFG, reg); } } else { if (rx_thr_num && rx_maxburst) { - reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); + reg = dwc3_readl(dwc, DWC3_GRXTHRCFG); reg |= DWC31_GRXTHRCFG_PKTCNTSEL; reg &= ~DWC31_GRXTHRCFG_RXPKTCNT(~0); @@ -1301,11 +1327,11 @@ static void dwc3_config_threshold(struct dwc3 *dwc) reg &= ~DWC31_GRXTHRCFG_MAXRXBURSTSIZE(~0); reg |= DWC31_GRXTHRCFG_MAXRXBURSTSIZE(rx_maxburst); - dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); + dwc3_writel(dwc, DWC3_GRXTHRCFG, reg); } if (tx_thr_num && tx_maxburst) { - reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG); + reg = dwc3_readl(dwc, DWC3_GTXTHRCFG); reg |= DWC31_GTXTHRCFG_PKTCNTSEL; reg &= ~DWC31_GTXTHRCFG_TXPKTCNT(~0); @@ -1314,7 +1340,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc) reg &= ~DWC31_GTXTHRCFG_MAXTXBURSTSIZE(~0); reg |= DWC31_GTXTHRCFG_MAXTXBURSTSIZE(tx_maxburst); - dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg); + dwc3_writel(dwc, DWC3_GTXTHRCFG, reg); } } } @@ -1325,7 +1351,7 @@ static void dwc3_config_threshold(struct dwc3 *dwc) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_core_init(struct dwc3 *dwc) +int dwc3_core_init(struct dwc3 *dwc) { unsigned int hw_mode; u32 reg; @@ -1333,12 +1359,6 @@ static int dwc3_core_init(struct dwc3 *dwc) hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); - /* - * Write Linux Version Code to our GUID register so it's easy to figure - * out which kernel version a bug was found. - */ - dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE); - ret = dwc3_phy_setup(dwc); if (ret) return ret; @@ -1355,6 +1375,8 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc->ulpi_ready = true; } + dwc3_ulpi_setup(dwc); + if (!dwc->phys_ready) { ret = dwc3_core_get_phy(dwc); if (ret) @@ -1370,6 +1392,12 @@ static int dwc3_core_init(struct dwc3 *dwc) if (ret) goto err_exit_phy; + /* + * Write Linux Version Code to our GUID register so it's easy to figure + * out which kernel version a bug was found. + */ + dwc3_writel(dwc, DWC3_GUID, LINUX_VERSION_CODE); + dwc3_core_setup_global_control(dwc); dwc3_core_num_eps(dwc); @@ -1402,9 +1430,9 @@ static int dwc3_core_init(struct dwc3 *dwc) * DWC_usb31 controller. */ if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) { - reg = dwc3_readl(dwc->regs, DWC3_GUCTL2); + reg = dwc3_readl(dwc, DWC3_GUCTL2); reg |= DWC3_GUCTL2_RST_ACTBITLATER; - dwc3_writel(dwc->regs, DWC3_GUCTL2, reg); + dwc3_writel(dwc, DWC3_GUCTL2, reg); } /* @@ -1417,9 +1445,9 @@ static int dwc3_core_init(struct dwc3 *dwc) * setting GUCTL2[19] by default; instead, use GUCTL2[19] = 0. */ if (DWC3_VER_IS(DWC3, 320A)) { - reg = dwc3_readl(dwc->regs, DWC3_GUCTL2); + reg = dwc3_readl(dwc, DWC3_GUCTL2); reg &= ~DWC3_GUCTL2_LC_TIMER; - dwc3_writel(dwc->regs, DWC3_GUCTL2, reg); + dwc3_writel(dwc, DWC3_GUCTL2, reg); } /* @@ -1432,13 +1460,13 @@ static int dwc3_core_init(struct dwc3 *dwc) * legacy ULPI PHYs. */ if (dwc->resume_hs_terminations) { - reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); + reg = dwc3_readl(dwc, DWC3_GUCTL1); reg |= DWC3_GUCTL1_RESUME_OPMODE_HS_HOST; - dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); + dwc3_writel(dwc, DWC3_GUCTL1, reg); } if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) { - reg = dwc3_readl(dwc->regs, DWC3_GUCTL1); + reg = dwc3_readl(dwc, DWC3_GUCTL1); /* * Enable hardware control of sending remote wakeup @@ -1473,24 +1501,20 @@ static int dwc3_core_init(struct dwc3 *dwc) reg &= ~DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK; } - dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); + dwc3_writel(dwc, DWC3_GUCTL1, reg); } dwc3_config_threshold(dwc); - /* - * Modify this for all supported Super Speed ports when - * multiport support is added. - */ if (hw_mode != DWC3_GHWPARAMS0_MODE_GADGET && (DWC3_IP_IS(DWC31)) && dwc->maximum_speed == USB_SPEED_SUPER) { int i; for (i = 0; i < dwc->num_usb3_ports; i++) { - reg = dwc3_readl(dwc->regs, DWC3_LLUCTL(i)); + reg = dwc3_readl(dwc, DWC3_LLUCTL(i)); reg |= DWC3_LLUCTL_FORCE_GEN1; - dwc3_writel(dwc->regs, DWC3_LLUCTL(i), reg); + dwc3_writel(dwc, DWC3_LLUCTL(i), reg); } } @@ -1509,9 +1533,9 @@ static int dwc3_core_init(struct dwc3 *dwc) * 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_readl(dwc, DWC3_GUCTL3); reg |= DWC3_GUCTL3_USB20_RETRY_DISABLE; - dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); + dwc3_writel(dwc, DWC3_GUCTL3, reg); } return 0; @@ -1525,6 +1549,7 @@ err_exit_ulpi: return ret; } +EXPORT_SYMBOL_GPL(dwc3_core_init); static int dwc3_core_get_phy(struct dwc3 *dwc) { @@ -1663,14 +1688,24 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE, true); } -static void dwc3_get_software_properties(struct dwc3 *dwc) +static void dwc3_get_software_properties(struct dwc3 *dwc, + const struct dwc3_properties *properties) { struct device *tmpdev; u16 gsbuscfg0_reqinfo; int ret; + if (properties->needs_full_reinit) + dwc->needs_full_reinit = true; + dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED; + if (properties->gsbuscfg0_reqinfo != + DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) { + dwc->gsbuscfg0_reqinfo = properties->gsbuscfg0_reqinfo; + return; + } + /* * Iterate over all parent nodes for finding swnode properties * and non-DT (non-ABI) properties. @@ -1699,6 +1734,7 @@ 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; + u16 num_hc_interrupters; /* default to highest possible threshold */ lpm_nyet_threshold = 0xf; @@ -1719,6 +1755,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) */ tx_fifo_resize_max_num = 6; + /* default to a single XHCI interrupter */ + num_hc_interrupters = 1; + dwc->maximum_speed = usb_get_maximum_speed(dev); dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev); dwc->dr_mode = usb_get_dr_mode(dev); @@ -1765,6 +1804,12 @@ static void dwc3_get_properties(struct dwc3 *dwc) &tx_thr_num_pkt_prd); device_property_read_u8(dev, "snps,tx-max-burst-prd", &tx_max_burst_prd); + device_property_read_u16(dev, "num-hc-interrupters", + &num_hc_interrupters); + /* DWC3 core allowed to have a max of 8 interrupters */ + if (num_hc_interrupters > 8) + num_hc_interrupters = 8; + dwc->do_fifo_resize = device_property_read_bool(dev, "tx-fifo-resize"); if (dwc->do_fifo_resize) @@ -1851,6 +1896,8 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->tx_max_burst_prd = tx_max_burst_prd; dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num; + + dwc->num_hc_interrupters = num_hc_interrupters; } /* check whether the core supports IMOD */ @@ -2131,6 +2178,20 @@ static int dwc3_get_num_ports(struct dwc3 *dwc) return 0; } +static void dwc3_vbus_draw_work(struct work_struct *work) +{ + struct dwc3 *dwc = container_of(work, struct dwc3, vbus_draw_work); + union power_supply_propval val = {0}; + int ret; + + val.intval = 1000 * (dwc->current_limit); + ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); + + if (ret < 0) + dev_dbg(dwc->dev, "Error (%d) setting vbus draw (%d mA)\n", + ret, dwc->current_limit); +} + static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc) { struct power_supply *usb_psy; @@ -2145,30 +2206,20 @@ static struct power_supply *dwc3_get_usb_power_supply(struct dwc3 *dwc) if (!usb_psy) return ERR_PTR(-EPROBE_DEFER); + INIT_WORK(&dwc->vbus_draw_work, dwc3_vbus_draw_work); return usb_psy; } -static int dwc3_probe(struct platform_device *pdev) +int dwc3_core_probe(const struct dwc3_probe_data *data) { - struct device *dev = &pdev->dev; - struct resource *res, dwc_res; + struct dwc3 *dwc = data->dwc; + struct device *dev = dwc->dev; + struct resource dwc_res; unsigned int hw_mode; void __iomem *regs; - struct dwc3 *dwc; + struct resource *res = data->res; int ret; - dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL); - if (!dwc) - return -ENOMEM; - - dwc->dev = dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "missing memory resource\n"); - return -ENODEV; - } - dwc->xhci_resources[0].start = res->start; dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + DWC3_XHCI_REGS_END; @@ -2202,21 +2253,23 @@ static int dwc3_probe(struct platform_device *pdev) dwc3_get_properties(dwc); - dwc3_get_software_properties(dwc); + dwc3_get_software_properties(dwc, &data->properties); 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); - goto err_put_psy; - } + if (!data->ignore_clocks_and_resets) { + dwc->reset = devm_reset_control_array_get_optional_shared(dev); + if (IS_ERR(dwc->reset)) { + ret = PTR_ERR(dwc->reset); + goto err_put_psy; + } - ret = dwc3_get_clocks(dwc); - if (ret) - goto err_put_psy; + ret = dwc3_get_clocks(dwc); + if (ret) + goto err_put_psy; + } ret = reset_control_deassert(dwc->reset); if (ret) @@ -2232,10 +2285,10 @@ static int dwc3_probe(struct platform_device *pdev) goto err_disable_clks; } - platform_set_drvdata(pdev, dwc); + dev_set_drvdata(dev, dwc); dwc3_cache_hwparams(dwc); - if (!dwc->sysdev_is_parent && + if (!dev_is_pci(dwc->sysdev) && DWC3_GHWPARAMS0_AWIDTH(dwc->hwparams.hwparams0) == 64) { ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64)); if (ret) @@ -2293,9 +2346,11 @@ static int dwc3_probe(struct platform_device *pdev) dwc3_check_params(dwc); dwc3_debugfs_init(dwc); - ret = dwc3_core_init_mode(dwc); - if (ret) - goto err_exit_debugfs; + if (!data->skip_core_init_mode) { + ret = dwc3_core_init_mode(dwc); + if (ret) + goto err_exit_debugfs; + } pm_runtime_put(dev); @@ -2327,12 +2382,37 @@ err_put_psy: return ret; } +EXPORT_SYMBOL_GPL(dwc3_core_probe); -static void dwc3_remove(struct platform_device *pdev) +static int dwc3_probe(struct platform_device *pdev) { - struct dwc3 *dwc = platform_get_drvdata(pdev); + struct dwc3_probe_data probe_data = {}; + struct resource *res; + struct dwc3 *dwc; - pm_runtime_get_sync(&pdev->dev); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing memory resource\n"); + return -ENODEV; + } + + dwc = devm_kzalloc(&pdev->dev, sizeof(*dwc), GFP_KERNEL); + if (!dwc) + return -ENOMEM; + + dwc->dev = &pdev->dev; + dwc->glue_ops = NULL; + + probe_data.dwc = dwc; + probe_data.res = res; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + + return dwc3_core_probe(&probe_data); +} + +void dwc3_core_remove(struct dwc3 *dwc) +{ + pm_runtime_get_sync(dwc->dev); dwc3_core_exit_mode(dwc); dwc3_debugfs_exit(dwc); @@ -2340,21 +2420,29 @@ static void dwc3_remove(struct platform_device *pdev) dwc3_core_exit(dwc); dwc3_ulpi_exit(dwc); - pm_runtime_allow(&pdev->dev); - pm_runtime_disable(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); - pm_runtime_put_noidle(&pdev->dev); + pm_runtime_allow(dwc->dev); + pm_runtime_disable(dwc->dev); + pm_runtime_dont_use_autosuspend(dwc->dev); + pm_runtime_put_noidle(dwc->dev); /* * HACK: Clear the driver data, which is currently accessed by parent * glue drivers, before allowing the parent to suspend. */ - platform_set_drvdata(pdev, NULL); - pm_runtime_set_suspended(&pdev->dev); + dev_set_drvdata(dwc->dev, NULL); + pm_runtime_set_suspended(dwc->dev); dwc3_free_event_buffers(dwc); - if (dwc->usb_psy) + if (dwc->usb_psy) { + cancel_work_sync(&dwc->vbus_draw_work); power_supply_put(dwc->usb_psy); + } +} +EXPORT_SYMBOL_GPL(dwc3_core_remove); + +static void dwc3_remove(struct platform_device *pdev) +{ + dwc3_core_remove(platform_get_drvdata(pdev)); } #ifdef CONFIG_PM @@ -2388,11 +2476,12 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) { u32 reg; int i; + int ret; if (!pm_runtime_suspended(dwc->dev) && !PMSG_IS_AUTO(msg)) { - dwc->susphy_state = (dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)) & + dwc->susphy_state = (dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)) & DWC3_GUSB2PHYCFG_SUSPHY) || - (dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)) & + (dwc3_readl(dwc, DWC3_GUSB3PIPECTL(0)) & DWC3_GUSB3PIPECTL_SUSPHY); /* * TI AM62 platform requires SUSPHY to be @@ -2406,12 +2495,15 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) case DWC3_GCTL_PRTCAP_DEVICE: if (pm_runtime_suspended(dwc->dev)) break; - dwc3_gadget_suspend(dwc); + ret = dwc3_gadget_suspend(dwc); + if (ret) + return ret; synchronize_irq(dwc->irq_gadget); dwc3_core_exit(dwc); break; case DWC3_GCTL_PRTCAP_HOST: - if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) { + if (!PMSG_IS_AUTO(msg) && + (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) { dwc3_core_exit(dwc); break; } @@ -2420,10 +2512,10 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) if (dwc->dis_u2_susphy_quirk || dwc->dis_enblslpm_quirk) { for (i = 0; i < dwc->num_usb2_ports; i++) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i)); reg |= DWC3_GUSB2PHYCFG_ENBLSLPM | DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg); } /* Give some time for USB2 PHY to suspend */ @@ -2441,7 +2533,9 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) break; if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) { - dwc3_gadget_suspend(dwc); + ret = dwc3_gadget_suspend(dwc); + if (ret) + return ret; synchronize_irq(dwc->irq_gadget); } @@ -2472,7 +2566,8 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) dwc3_gadget_resume(dwc); break; case DWC3_GCTL_PRTCAP_HOST: - if (!PMSG_IS_AUTO(msg) && !device_may_wakeup(dwc->dev)) { + if (!PMSG_IS_AUTO(msg) && + (!device_may_wakeup(dwc->dev) || dwc->needs_full_reinit)) { ret = dwc3_core_init_for_resume(dwc); if (ret) return ret; @@ -2481,14 +2576,14 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) } /* Restore GUSB2PHYCFG bits that were modified in suspend */ for (i = 0; i < dwc->num_usb2_ports; i++) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(i)); if (dwc->dis_u2_susphy_quirk) reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; if (dwc->dis_enblslpm_quirk) reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(i), reg); } for (i = 0; i < dwc->num_usb2_ports; i++) @@ -2544,9 +2639,8 @@ static int dwc3_runtime_checks(struct dwc3 *dwc) return 0; } -static int dwc3_runtime_suspend(struct device *dev) +int dwc3_runtime_suspend(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); int ret; if (dwc3_runtime_checks(dwc)) @@ -2558,10 +2652,11 @@ static int dwc3_runtime_suspend(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(dwc3_runtime_suspend); -static int dwc3_runtime_resume(struct device *dev) +int dwc3_runtime_resume(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); + struct device *dev = dwc->dev; int ret; ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME); @@ -2571,7 +2666,7 @@ static int dwc3_runtime_resume(struct device *dev) switch (dwc->current_dr_role) { case DWC3_GCTL_PRTCAP_DEVICE: if (dwc->pending_events) { - pm_runtime_put(dwc->dev); + pm_runtime_put(dev); dwc->pending_events = false; enable_irq(dwc->irq_gadget); } @@ -2586,10 +2681,11 @@ static int dwc3_runtime_resume(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(dwc3_runtime_resume); -static int dwc3_runtime_idle(struct device *dev) +int dwc3_runtime_idle(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); + struct device *dev = dwc->dev; switch (dwc->current_dr_role) { case DWC3_GCTL_PRTCAP_DEVICE: @@ -2602,17 +2698,32 @@ static int dwc3_runtime_idle(struct device *dev) break; } - pm_runtime_mark_last_busy(dev); pm_runtime_autosuspend(dev); return 0; } +EXPORT_SYMBOL_GPL(dwc3_runtime_idle); + +static int dwc3_plat_runtime_suspend(struct device *dev) +{ + return dwc3_runtime_suspend(dev_get_drvdata(dev)); +} + +static int dwc3_plat_runtime_resume(struct device *dev) +{ + return dwc3_runtime_resume(dev_get_drvdata(dev)); +} + +static int dwc3_plat_runtime_idle(struct device *dev) +{ + return dwc3_runtime_idle(dev_get_drvdata(dev)); +} #endif /* CONFIG_PM */ #ifdef CONFIG_PM_SLEEP -static int dwc3_suspend(struct device *dev) +int dwc3_pm_suspend(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); + struct device *dev = dwc->dev; int ret; ret = dwc3_suspend_common(dwc, PMSG_SUSPEND); @@ -2623,10 +2734,11 @@ static int dwc3_suspend(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(dwc3_pm_suspend); -static int dwc3_resume(struct device *dev) +int dwc3_pm_resume(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); + struct device *dev = dwc->dev; int ret = 0; pinctrl_pm_select_default_state(dev); @@ -2645,34 +2757,73 @@ out: return ret; } +EXPORT_SYMBOL_GPL(dwc3_pm_resume); -static void dwc3_complete(struct device *dev) +void dwc3_pm_complete(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); u32 reg; if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->dis_split_quirk) { - reg = dwc3_readl(dwc->regs, DWC3_GUCTL3); + reg = dwc3_readl(dwc, DWC3_GUCTL3); reg |= DWC3_GUCTL3_SPLITDISABLE; - dwc3_writel(dwc->regs, DWC3_GUCTL3, reg); + dwc3_writel(dwc, DWC3_GUCTL3, reg); } } +EXPORT_SYMBOL_GPL(dwc3_pm_complete); + +int dwc3_pm_prepare(struct dwc3 *dwc) +{ + struct device *dev = dwc->dev; + + /* + * Indicate to the PM core that it may safely leave the device in + * runtime suspend if runtime-suspended already in device mode. + */ + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE && + pm_runtime_suspended(dev) && + !dev_pinctrl(dev)) + return 1; + + return 0; +} +EXPORT_SYMBOL_GPL(dwc3_pm_prepare); + +static int dwc3_plat_suspend(struct device *dev) +{ + return dwc3_pm_suspend(dev_get_drvdata(dev)); +} + +static int dwc3_plat_resume(struct device *dev) +{ + return dwc3_pm_resume(dev_get_drvdata(dev)); +} + +static void dwc3_plat_complete(struct device *dev) +{ + dwc3_pm_complete(dev_get_drvdata(dev)); +} + +static int dwc3_plat_prepare(struct device *dev) +{ + return dwc3_pm_prepare(dev_get_drvdata(dev)); +} #else -#define dwc3_complete NULL +#define dwc3_plat_complete NULL +#define dwc3_plat_prepare NULL #endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops dwc3_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) - .complete = dwc3_complete, - + SET_SYSTEM_SLEEP_PM_OPS(dwc3_plat_suspend, dwc3_plat_resume) + .complete = dwc3_plat_complete, + .prepare = dwc3_plat_prepare, /* * Runtime suspend halts the controller on disconnection. It relies on * platforms with custom connection notification to start the controller * again. */ - SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume, - dwc3_runtime_idle) + SET_RUNTIME_PM_OPS(dwc3_plat_runtime_suspend, dwc3_plat_runtime_resume, + dwc3_plat_runtime_idle) }; #ifdef CONFIG_OF diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index aaa39e663f60..e0dee9d28740 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -165,10 +165,10 @@ #define DWC3_DCFG1 0xc740 /* DWC_usb32 only */ #define DWC3_DEP_BASE(n) (0xc800 + ((n) * 0x10)) -#define DWC3_DEPCMDPAR2 0x00 -#define DWC3_DEPCMDPAR1 0x04 -#define DWC3_DEPCMDPAR0 0x08 -#define DWC3_DEPCMD 0x0c +#define DWC3_DEPCMDPAR2(n) (DWC3_DEP_BASE(n) + 0x00) +#define DWC3_DEPCMDPAR1(n) (DWC3_DEP_BASE(n) + 0x04) +#define DWC3_DEPCMDPAR0(n) (DWC3_DEP_BASE(n) + 0x08) +#define DWC3_DEPCMD(n) (DWC3_DEP_BASE(n) + 0x0c) #define DWC3_DEV_IMOD(n) (0xca00 + ((n) * 0x4)) @@ -302,6 +302,7 @@ #define DWC3_GUSB2PHYCFG_SUSPHY BIT(6) #define DWC3_GUSB2PHYCFG_ULPI_UTMI BIT(4) #define DWC3_GUSB2PHYCFG_ENBLSLPM BIT(8) +#define DWC3_GUSB2PHYCFG_XCVRDLY BIT(9) #define DWC3_GUSB2PHYCFG_PHYIF(n) (n << 3) #define DWC3_GUSB2PHYCFG_PHYIF_MASK DWC3_GUSB2PHYCFG_PHYIF(1) #define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10) @@ -749,8 +750,6 @@ struct dwc3_ep { struct list_head pending_list; struct list_head started_list; - void __iomem *regs; - struct dwc3_trb *trb_pool; dma_addr_t trb_pool_dma; struct dwc3 *dwc; @@ -993,6 +992,17 @@ struct dwc3_scratchpad_array { }; /** + * struct dwc3_glue_ops - The ops indicate the notifications that + * need to be passed on to glue layer + * @pre_set_role: Notify glue of role switch notifications + * @pre_run_stop: Notify run stop enable/disable information to glue + */ +struct dwc3_glue_ops { + void (*pre_set_role)(struct dwc3 *dwc, enum usb_role role); + void (*pre_run_stop)(struct dwc3 *dwc, bool is_on); +}; + +/** * struct dwc3 - representation of our controller * @drd_work: workqueue used for role swapping * @ep0_trb: trb which is used for the ctrl_req @@ -1012,6 +1022,7 @@ struct dwc3_scratchpad_array { * @eps: endpoint array * @gadget: device side representation of the peripheral controller * @gadget_driver: pointer to the gadget driver + * @glue_ops: Vendor callbacks for flattened device implementations. * @bus_clk: clock for accessing the registers * @ref_clk: reference clock * @susp_clk: clock used when the SS phy is in low power (S3) state @@ -1048,6 +1059,8 @@ struct dwc3_scratchpad_array { * @role_switch_default_mode: default operation mode of controller while * usb role is USB_ROLE_NONE. * @usb_psy: pointer to power supply interface. + * @vbus_draw_work: Work to set the vbus drawing limit + * @current_limit: How much current to draw from vbus, in milliAmperes. * @usb2_phy: pointer to USB2 PHY * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to array of USB2 PHYs @@ -1083,6 +1096,7 @@ struct dwc3_scratchpad_array { * @tx_max_burst_prd: max periodic ESS transmit burst size * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize * @clear_stall_protocol: endpoint number that requires a delayed status phase + * @num_hc_interrupters: number of host controller interrupters * @hsphy_interface: "utmi" or "ulpi" * @connected: true when we're connected to a host, false otherwise * @softconnect: true when gadget connect is called, false when disconnect runs @@ -1106,6 +1120,8 @@ struct dwc3_scratchpad_array { * @usb3_lpm_capable: set if hadrware supports Link Power Management * @usb2_lpm_disable: set to disable usb2 lpm for host * @usb2_gadget_lpm_disable: set to disable usb2 lpm for gadget + * @needs_full_reinit: set to indicate the core may lose power and need full + * initialization during system pm * @disable_scramble_quirk: set if we enable the disable scramble quirk * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk @@ -1136,7 +1152,7 @@ struct dwc3_scratchpad_array { * VBUS with an external supply. * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed * instances in park mode. - * @parkmode_disable_hs_quirk: set if we need to disable all HishSpeed + * @parkmode_disable_hs_quirk: set if we need to disable all HighSpeed * instances in park mode. * @gfladj_refclk_lpm_sel: set if we need to enable SOF/ITP counter * running based on ref_clk @@ -1148,6 +1164,8 @@ struct dwc3_scratchpad_array { * 3 - Reserved * @dis_metastability_quirk: set to disable metastability quirk. * @dis_split_quirk: set to disable split boundary. + * @enable_usb2_transceiver_delay: Set to insert a delay before the + * assertion of the TxValid signal during a HS Chirp. * @sys_wakeup: set if the device may do system wakeup. * @wakeup_configured: set if the device is configured for remote wakeup. * @suspended: set to track suspend event due to U3/L2. @@ -1164,6 +1182,9 @@ struct dwc3_scratchpad_array { * @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO, * DATWRREQINFO, and DESWRREQINFO value passed from * glue driver. + * @wakeup_pending_funcs: Indicates whether any interface has requested for + * function wakeup in bitmap format where bit position + * represents interface_id. */ struct dwc3 { struct work_struct drd_work; @@ -1193,6 +1214,8 @@ struct dwc3 { struct usb_gadget *gadget; struct usb_gadget_driver *gadget_driver; + const struct dwc3_glue_ops *glue_ops; + struct clk *bus_clk; struct clk *ref_clk; struct clk *susp_clk; @@ -1228,6 +1251,8 @@ struct dwc3 { enum usb_dr_mode role_switch_default_mode; struct power_supply *usb_psy; + struct work_struct vbus_draw_work; + unsigned int current_limit; u32 fladj; u32 ref_clk_per; @@ -1247,6 +1272,7 @@ struct dwc3 { #define DWC3_IP 0x5533 #define DWC31_IP 0x3331 #define DWC32_IP 0x3332 +#define DWC4_IP 0x3430 u32 revision; @@ -1330,6 +1356,7 @@ struct dwc3 { u8 tx_max_burst_prd; u8 tx_fifo_resize_max_num; u8 clear_stall_protocol; + u16 num_hc_interrupters; const char *hsphy_interface; @@ -1351,6 +1378,7 @@ struct dwc3 { unsigned usb3_lpm_capable:1; unsigned usb2_lpm_disable:1; unsigned usb2_gadget_lpm_disable:1; + unsigned needs_full_reinit:1; unsigned disable_scramble_quirk:1; unsigned u2exit_lfps_quirk:1; @@ -1381,6 +1409,7 @@ struct dwc3 { unsigned dis_metastability_quirk:1; unsigned dis_split_quirk:1; + unsigned enable_usb2_transceiver_delay:1; unsigned async_callbacks:1; unsigned sys_wakeup:1; unsigned wakeup_configured:1; @@ -1394,6 +1423,7 @@ struct dwc3 { int num_ep_resized; struct dentry *debug_root; u32 gsbuscfg0_reqinfo; + u32 wakeup_pending_funcs; }; #define INCRX_BURST_MODE 0 @@ -1608,6 +1638,18 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc); int dwc3_core_soft_reset(struct dwc3 *dwc); void dwc3_enable_susphy(struct dwc3 *dwc, bool enable); +static inline void dwc3_pre_set_role(struct dwc3 *dwc, enum usb_role role) +{ + if (dwc->glue_ops && dwc->glue_ops->pre_set_role) + dwc->glue_ops->pre_set_role(dwc, role); +} + +static inline void dwc3_pre_run_stop(struct dwc3 *dwc, bool is_on) +{ + if (dwc->glue_ops && dwc->glue_ops->pre_run_stop) + dwc->glue_ops->pre_run_stop(dwc, is_on); +} + #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_host_init(struct dwc3 *dwc); void dwc3_host_exit(struct dwc3 *dwc); diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 09d703852a92..6e1cdcdce7cc 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -14,6 +14,24 @@ #include "core.h" /** + * dwc3_mode_string - returns mode name + * @mode: GCTL.PrtCapDir value + */ +static inline const char *dwc3_mode_string(u32 mode) +{ + switch (mode) { + case DWC3_GCTL_PRTCAP_HOST: + return "host"; + case DWC3_GCTL_PRTCAP_DEVICE: + return "device"; + case DWC3_GCTL_PRTCAP_OTG: + return "otg"; + default: + return "UNKNOWN"; + } +} + +/** * dwc3_gadget_ep_cmd_string - returns endpoint command string * @cmd: command code */ diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index ebf03468fac4..65d909e4b785 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -36,23 +36,19 @@ #define dump_ep_register_set(n) \ { \ .name = "DEPCMDPAR2("__stringify(n)")", \ - .offset = DWC3_DEP_BASE(n) + \ - DWC3_DEPCMDPAR2, \ + .offset = DWC3_DEPCMDPAR2(n), \ }, \ { \ .name = "DEPCMDPAR1("__stringify(n)")", \ - .offset = DWC3_DEP_BASE(n) + \ - DWC3_DEPCMDPAR1, \ + .offset = DWC3_DEPCMDPAR1(n), \ }, \ { \ .name = "DEPCMDPAR0("__stringify(n)")", \ - .offset = DWC3_DEP_BASE(n) + \ - DWC3_DEPCMDPAR0, \ + .offset = DWC3_DEPCMDPAR0(n), \ }, \ { \ .name = "DEPCMD("__stringify(n)")", \ - .offset = DWC3_DEP_BASE(n) + \ - DWC3_DEPCMD, \ + .offset = DWC3_DEPCMD(n), \ } @@ -300,14 +296,14 @@ static void dwc3_host_lsp(struct seq_file *s) reg = DWC3_GDBGLSPMUX_HOSTSELECT(sel); - dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); - val = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg); + val = dwc3_readl(dwc, DWC3_GDBGLSP); seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", sel, val); if (dbc_enabled && sel < 256) { reg |= DWC3_GDBGLSPMUX_ENDBC; - dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); - val = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg); + val = dwc3_readl(dwc, DWC3_GDBGLSP); seq_printf(s, "GDBGLSP_DBC[%d] = 0x%08x\n", sel, val); } } @@ -320,8 +316,8 @@ static void dwc3_gadget_lsp(struct seq_file *s) for (i = 0; i < 16; i++) { reg = DWC3_GDBGLSPMUX_DEVSELECT(i); - dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); - reg = dwc3_readl(dwc->regs, DWC3_GDBGLSP); + dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg); + reg = dwc3_readl(dwc, DWC3_GDBGLSP); seq_printf(s, "GDBGLSP[%d] = 0x%08x\n", i, reg); } } @@ -339,7 +335,7 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused) return ret; spin_lock_irqsave(&dwc->lock, flags); - reg = dwc3_readl(dwc->regs, DWC3_GSTS); + reg = dwc3_readl(dwc, DWC3_GSTS); current_mode = DWC3_GSTS_CURMOD(reg); switch (current_mode) { @@ -402,6 +398,7 @@ static int dwc3_mode_show(struct seq_file *s, void *unused) struct dwc3 *dwc = s->private; unsigned long flags; u32 reg; + u32 mode; int ret; ret = pm_runtime_resume_and_get(dwc->dev); @@ -409,21 +406,18 @@ static int dwc3_mode_show(struct seq_file *s, void *unused) return ret; spin_lock_irqsave(&dwc->lock, flags); - reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg = dwc3_readl(dwc, DWC3_GCTL); spin_unlock_irqrestore(&dwc->lock, flags); - switch (DWC3_GCTL_PRTCAP(reg)) { + mode = DWC3_GCTL_PRTCAP(reg); + switch (mode) { case DWC3_GCTL_PRTCAP_HOST: - seq_puts(s, "host\n"); - break; case DWC3_GCTL_PRTCAP_DEVICE: - seq_puts(s, "device\n"); - break; case DWC3_GCTL_PRTCAP_OTG: - seq_puts(s, "otg\n"); + seq_printf(s, "%s\n", dwc3_mode_string(mode)); break; default: - seq_printf(s, "UNKNOWN %08x\n", DWC3_GCTL_PRTCAP(reg)); + seq_printf(s, "UNKNOWN %08x\n", mode); } pm_runtime_put_sync(dwc->dev); @@ -484,7 +478,7 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused) return ret; spin_lock_irqsave(&dwc->lock, flags); - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); reg &= DWC3_DCTL_TSTCTRL_MASK; reg >>= 1; spin_unlock_irqrestore(&dwc->lock, flags); @@ -583,7 +577,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) return ret; spin_lock_irqsave(&dwc->lock, flags); - reg = dwc3_readl(dwc->regs, DWC3_GSTS); + reg = dwc3_readl(dwc, DWC3_GSTS); if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) { seq_puts(s, "Not available\n"); spin_unlock_irqrestore(&dwc->lock, flags); @@ -591,7 +585,7 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused) return 0; } - reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg = dwc3_readl(dwc, DWC3_DSTS); state = DWC3_DSTS_USBLNKST(reg); speed = reg & DWC3_DSTS_CONNECTSPD; @@ -645,14 +639,14 @@ static ssize_t dwc3_link_state_write(struct file *file, return ret; spin_lock_irqsave(&dwc->lock, flags); - reg = dwc3_readl(dwc->regs, DWC3_GSTS); + reg = dwc3_readl(dwc, DWC3_GSTS); if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) { spin_unlock_irqrestore(&dwc->lock, flags); pm_runtime_put_sync(dwc->dev); return -EINVAL; } - reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg = dwc3_readl(dwc, DWC3_DSTS); speed = reg & DWC3_DSTS_CONNECTSPD; if (speed < DWC3_DSTS_SUPERSPEED && @@ -948,10 +942,10 @@ static int dwc3_ep_info_register_show(struct seq_file *s, void *unused) spin_lock_irqsave(&dwc->lock, flags); reg = DWC3_GDBGLSPMUX_EPSELECT(dep->number); - dwc3_writel(dwc->regs, DWC3_GDBGLSPMUX, reg); + dwc3_writel(dwc, DWC3_GDBGLSPMUX, reg); - lower_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO0); - upper_32_bits = dwc3_readl(dwc->regs, DWC3_GDBGEPINFO1); + lower_32_bits = dwc3_readl(dwc, DWC3_GDBGEPINFO0); + upper_32_bits = dwc3_readl(dwc, DWC3_GDBGEPINFO1); ep_info = ((u64)upper_32_bits << 32) | lower_32_bits; seq_printf(s, "0x%016llx\n", ep_info); @@ -1009,7 +1003,7 @@ void dwc3_debugfs_init(struct dwc3 *dwc) { struct dentry *root; - dwc->regset = kzalloc(sizeof(*dwc->regset), GFP_KERNEL); + dwc->regset = kzalloc_obj(*dwc->regset); if (!dwc->regset) return; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 7977860932b1..f3e37d383627 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -18,25 +18,25 @@ static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask) { - u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN); + u32 reg = dwc3_readl(dwc, DWC3_OEVTEN); reg &= ~(disable_mask); - dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); + dwc3_writel(dwc, DWC3_OEVTEN, reg); } static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask) { - u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN); + u32 reg = dwc3_readl(dwc, DWC3_OEVTEN); reg |= (enable_mask); - dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); + dwc3_writel(dwc, DWC3_OEVTEN, reg); } static void dwc3_otg_clear_events(struct dwc3 *dwc) { - u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT); + u32 reg = dwc3_readl(dwc, DWC3_OEVT); - dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); + dwc3_writel(dwc, DWC3_OEVTEN, reg); } #define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \ @@ -72,18 +72,18 @@ static irqreturn_t dwc3_otg_irq(int irq, void *_dwc) struct dwc3 *dwc = _dwc; irqreturn_t ret = IRQ_NONE; - reg = dwc3_readl(dwc->regs, DWC3_OEVT); + reg = dwc3_readl(dwc, DWC3_OEVT); if (reg) { /* ignore non OTG events, we can't disable them in OEVTEN */ if (!(reg & DWC3_OTG_ALL_EVENTS)) { - dwc3_writel(dwc->regs, DWC3_OEVT, reg); + dwc3_writel(dwc, DWC3_OEVT, reg); return IRQ_NONE; } if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST && !(reg & DWC3_OEVT_DEVICEMODE)) dwc->otg_restart_host = true; - dwc3_writel(dwc->regs, DWC3_OEVT, reg); + dwc3_writel(dwc, DWC3_OEVT, reg); ret = IRQ_WAKE_THREAD; } @@ -100,23 +100,23 @@ static void dwc3_otgregs_init(struct dwc3 *dwc) * the signal outputs sent to the PHY, the OTG FSM logic of the * core and also the resets to the VBUS filters inside the core. */ - reg = dwc3_readl(dwc->regs, DWC3_OCFG); + reg = dwc3_readl(dwc, DWC3_OCFG); reg |= DWC3_OCFG_SFTRSTMASK; - dwc3_writel(dwc->regs, DWC3_OCFG, reg); + dwc3_writel(dwc, DWC3_OCFG, reg); /* Disable hibernation for simplicity */ - reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg = dwc3_readl(dwc, DWC3_GCTL); reg &= ~DWC3_GCTL_GBLHIBERNATIONEN; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + dwc3_writel(dwc, DWC3_GCTL, reg); /* * Initialize OTG registers as per * Figure 11-4 OTG Driver Overall Programming Flow */ /* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */ - reg = dwc3_readl(dwc->regs, DWC3_OCFG); + reg = dwc3_readl(dwc, DWC3_OCFG); reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP); - dwc3_writel(dwc->regs, DWC3_OCFG, reg); + dwc3_writel(dwc, DWC3_OCFG, reg); /* OEVT = FFFF */ dwc3_otg_clear_events(dwc); /* OEVTEN = 0 */ @@ -127,11 +127,11 @@ static void dwc3_otgregs_init(struct dwc3 *dwc) * OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0, * OCTL.HNPReq = 0 */ - reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg = dwc3_readl(dwc, DWC3_OCTL); reg |= DWC3_OCTL_PERIMODE; reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_HNPREQ); - dwc3_writel(dwc->regs, DWC3_OCTL, reg); + dwc3_writel(dwc, DWC3_OCTL, reg); } static int dwc3_otg_get_irq(struct dwc3 *dwc) @@ -175,9 +175,9 @@ void dwc3_otg_init(struct dwc3 *dwc) /* GCTL.PrtCapDir=2'b11 */ dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG, true); /* GUSB2PHYCFG0.SusPHY=0 */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)); reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg); /* Initialize OTG registers */ dwc3_otgregs_init(dwc); @@ -203,17 +203,17 @@ void dwc3_otg_host_init(struct dwc3 *dwc) * OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0, * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0 */ - reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg = dwc3_readl(dwc, DWC3_OCTL); reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN); - dwc3_writel(dwc->regs, DWC3_OCTL, reg); + dwc3_writel(dwc, DWC3_OCTL, reg); /* * OCFG.DisPrtPwrCutoff = 0/1 */ - reg = dwc3_readl(dwc->regs, DWC3_OCFG); + reg = dwc3_readl(dwc, DWC3_OCFG); reg &= ~DWC3_OCFG_DISPWRCUTTOFF; - dwc3_writel(dwc->regs, DWC3_OCFG, reg); + dwc3_writel(dwc, DWC3_OCFG, reg); /* * OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP @@ -229,15 +229,15 @@ void dwc3_otg_host_init(struct dwc3 *dwc) /* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */ if (!dwc->dis_u2_susphy_quirk) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)); reg |= DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg); } /* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */ - reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg = dwc3_readl(dwc, DWC3_OCTL); reg |= DWC3_OCTL_PRTPWRCTL; - dwc3_writel(dwc->regs, DWC3_OCTL, reg); + dwc3_writel(dwc, DWC3_OCTL, reg); } /* should be called after Host controller driver is stopped */ @@ -258,9 +258,9 @@ static void dwc3_otg_host_exit(struct dwc3 *dwc) */ /* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */ - reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg = dwc3_readl(dwc, DWC3_OCTL); reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL); - dwc3_writel(dwc->regs, DWC3_OCTL, reg); + dwc3_writel(dwc, DWC3_OCTL, reg); } /* should be called before the gadget controller driver is started */ @@ -274,27 +274,27 @@ static void dwc3_otg_device_init(struct dwc3 *dwc) * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1 * but we keep them 0 for simple dual-role operation. */ - reg = dwc3_readl(dwc->regs, DWC3_OCFG); + reg = dwc3_readl(dwc, DWC3_OCFG); /* OCFG.OTGSftRstMsk = 0/1 */ reg |= DWC3_OCFG_SFTRSTMASK; - dwc3_writel(dwc->regs, DWC3_OCFG, reg); + dwc3_writel(dwc, DWC3_OCFG, reg); /* * OCTL.PeriMode = 1 * OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0 * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0 */ - reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg = dwc3_readl(dwc, DWC3_OCTL); reg |= DWC3_OCTL_PERIMODE; reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ | DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN); - dwc3_writel(dwc->regs, DWC3_OCTL, reg); + dwc3_writel(dwc, DWC3_OCTL, reg); /* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */ dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN); /* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */ if (!dwc->dis_u2_susphy_quirk) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)); reg |= DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg); } /* GCTL.GblHibernationEn = 0. Already 0. */ } @@ -319,10 +319,10 @@ static void dwc3_otg_device_exit(struct dwc3 *dwc) DWC3_OEVTEN_BDEVBHOSTENDEN); /* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */ - reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg = dwc3_readl(dwc, DWC3_OCTL); reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ); reg |= DWC3_OCTL_PERIMODE; - dwc3_writel(dwc->regs, DWC3_OCTL, reg); + dwc3_writel(dwc, DWC3_OCTL, reg); } void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) @@ -341,7 +341,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) return; if (!ignore_idstatus) { - reg = dwc3_readl(dwc->regs, DWC3_OSTS); + reg = dwc3_readl(dwc, DWC3_OSTS); id = !!(reg & DWC3_OSTS_CONIDSTS); dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE : @@ -381,6 +381,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) dwc3_otgregs_init(dwc); dwc3_otg_host_init(dwc); spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_pre_set_role(dwc, USB_ROLE_HOST); ret = dwc3_host_init(dwc); if (ret) { dev_err(dwc->dev, "failed to initialize host\n"); @@ -406,6 +407,7 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) otg_set_vbus(dwc->usb2_phy->otg, false); if (dwc->usb2_generic_phy[0]) phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE); + dwc3_pre_set_role(dwc, USB_ROLE_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) dev_err(dwc->dev, "failed to initialize peripheral\n"); @@ -433,10 +435,12 @@ static int dwc3_drd_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb); + u32 mode = event ? DWC3_GCTL_PRTCAP_HOST : DWC3_GCTL_PRTCAP_DEVICE; + enum usb_role role = mode == DWC3_GCTL_PRTCAP_HOST ? + USB_ROLE_HOST : USB_ROLE_DEVICE; - dwc3_set_mode(dwc, event ? - DWC3_GCTL_PRTCAP_HOST : - DWC3_GCTL_PRTCAP_DEVICE); + dwc3_pre_set_role(dwc, role); + dwc3_set_mode(dwc, mode); return NOTIFY_DONE; } @@ -464,6 +468,7 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, break; } + dwc3_pre_set_role(dwc, role); dwc3_set_mode(dwc, mode); return 0; } @@ -514,6 +519,7 @@ static int dwc3_setup_role_switch(struct dwc3 *dwc) dwc3_role_switch.set = dwc3_usb_role_switch_set; dwc3_role_switch.get = dwc3_usb_role_switch_get; dwc3_role_switch.driver_data = dwc; + dwc3_role_switch.allow_userspace_control = true; dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch); if (IS_ERR(dwc->role_sw)) return PTR_ERR(dwc->role_sw); diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index c158364bc03e..e11d7643f966 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -153,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); @@ -165,14 +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; - - of_node_put(args.np); - 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) { @@ -300,7 +292,6 @@ static int dwc3_ti_probe(struct platform_device *pdev) /* Setting up autosuspend */ pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/usb/dwc3/dwc3-apple.c b/drivers/usb/dwc3/dwc3-apple.c new file mode 100644 index 000000000000..40c3ccfddb67 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-apple.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Apple Silicon DWC3 Glue driver + * Copyright (C) The Asahi Linux Contributors + * + * Based on: + * - dwc3-qcom.c Copyright (c) 2018, The Linux Foundation. All rights reserved. + * - dwc3-of-simple.c Copyright (c) 2015 Texas Instruments Incorporated - https://www.ti.com + */ + +#include <linux/of.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/reset.h> + +#include "glue.h" + +/* + * This platform requires a very specific sequence of operations to bring up dwc3 and its USB3 PHY: + * + * 1) The PHY itself has to be brought up; for this we need to know the mode (USB3, + * USB3+DisplayPort, USB4, etc) and the lane orientation. This happens through typec_mux_set. + * 2) DWC3 has to be brought up but we must not touch the gadget area or start xhci yet. + * 3) The PHY bring-up has to be finalized and dwc3's PIPE interface has to be switched to the + * USB3 PHY, this is done inside phy_set_mode. + * 4) We can now initialize xhci or gadget mode. + * + * We can switch 1 and 2 but 3 has to happen after (1 and 2) and 4 has to happen after 3. + * + * And then to bring this all down again: + * + * 1) DWC3 has to exit host or gadget mode and must no longer touch those registers + * 2) The PHY has to switch dwc3's PIPE interface back to the dummy backend + * 3) The PHY itself can be shut down, this happens from typec_mux_set + * + * We also can't transition the PHY from one mode to another while dwc3 is up and running (this is + * slightly wrong, some transitions are possible, others aren't but because we have no documentation + * for this I'd rather play it safe). + * + * After both the PHY and dwc3 are initialized we will only ever see a single "new device connected" + * event. If we just keep them running only the first device plugged in will ever work. XHCI's port + * status register actually does show the correct state but no interrupt ever comes in. In gadget + * mode we don't even get a USBDisconnected event and everything looks like there's still something + * connected on the other end. + * This can be partially explained because the USB2 D+/D- lines are connected through a stateful + * eUSB2 repeater which in turn is controlled by a variant of the TI TPS6598x USB PD chip which + * resets the repeater out-of-band everytime the CC lines are (dis)connected. This then requires a + * PHY reset to make sure the PHY and the eUSB2 repeater state are synchronized again. + * + * And to make this all extra fun: If we get the order of some of this wrong either the port is just + * broken until a phy+dwc3 reset, or it's broken until a full SoC reset (likely because we can't + * reset some parts of the PHY), or some watchdog kicks in after a few seconds and forces a full SoC + * reset (mostly seen this with USB4/Thunderbolt but there's clearly some watchdog that hates + * invalid states). + * + * Hence there's really no good way to keep dwc3 fully up and running after we disconnect a cable + * because then we can't shut down the PHY anymore. And if we kept the PHY running in whatever mode + * it was until the next cable is connected we'd need to tear it all down and bring it back up again + * anyway to detect and use the next device. + * + * Instead, we just shut down everything when a cable is disconnected and transition to + * DWC3_APPLE_NO_CABLE. + * During initial probe we don't have any information about the connected cable and can't bring up + * the PHY properly and thus also can't fully bring up dwc3. Instead, we just keep everything off + * and defer the first dwc3 probe until we get the first cable connected event. Until then we stay + * in DWC3_APPLE_PROBE_PENDING. + * Once a cable is connected we then keep track of the controller mode here by transitioning to + * DWC3_APPLE_HOST or DWC3_APPLE_DEVICE. + */ +enum dwc3_apple_state { + DWC3_APPLE_PROBE_PENDING, /* Before first cable connection, dwc3_core_probe not called */ + DWC3_APPLE_NO_CABLE, /* No cable connected, dwc3 suspended after dwc3_core_exit */ + DWC3_APPLE_HOST, /* Cable connected, dwc3 in host mode */ + DWC3_APPLE_DEVICE, /* Cable connected, dwc3 in device mode */ +}; + +/** + * struct dwc3_apple - Apple-specific DWC3 USB controller + * @dwc: Core DWC3 structure + * @dev: Pointer to the device structure + * @mmio_resource: Resource to be passed to dwc3_core_probe + * @apple_regs: Apple-specific DWC3 registers + * @reset: Reset control + * @role_sw: USB role switch + * @lock: Mutex for synchronizing access + * @state: Current state of the controller, see documentation for the enum for details + */ +struct dwc3_apple { + struct dwc3 dwc; + + struct device *dev; + struct resource *mmio_resource; + void __iomem *apple_regs; + + struct reset_control *reset; + struct usb_role_switch *role_sw; + + struct mutex lock; + + enum dwc3_apple_state state; +}; + +#define to_dwc3_apple(d) container_of((d), struct dwc3_apple, dwc) + +/* + * Apple Silicon dwc3 vendor-specific registers + * + * These registers were identified by tracing XNU's memory access patterns and correlating them with + * debug output over serial to determine their names. We don't exactly know what these do but + * without these USB3 devices sometimes don't work. + */ +#define APPLE_DWC3_REGS_START 0xcd00 +#define APPLE_DWC3_REGS_END 0xcdff + +#define APPLE_DWC3_CIO_LFPS_OFFSET 0xcd38 +#define APPLE_DWC3_CIO_LFPS_OFFSET_VALUE 0xf800f80 + +#define APPLE_DWC3_CIO_BW_NGT_OFFSET 0xcd3c +#define APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE 0xfc00fc0 + +#define APPLE_DWC3_CIO_LINK_TIMER 0xcd40 +#define APPLE_DWC3_CIO_PENDING_HP_TIMER GENMASK(23, 16) +#define APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE 0x14 +#define APPLE_DWC3_CIO_PM_LC_TIMER GENMASK(15, 8) +#define APPLE_DWC3_CIO_PM_LC_TIMER_VALUE 0xa +#define APPLE_DWC3_CIO_PM_ENTRY_TIMER GENMASK(7, 0) +#define APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE 0x10 + +static inline void dwc3_apple_writel(struct dwc3_apple *appledwc, u32 offset, u32 value) +{ + writel(value, appledwc->apple_regs + offset - APPLE_DWC3_REGS_START); +} + +static inline u32 dwc3_apple_readl(struct dwc3_apple *appledwc, u32 offset) +{ + return readl(appledwc->apple_regs + offset - APPLE_DWC3_REGS_START); +} + +static inline void dwc3_apple_mask(struct dwc3_apple *appledwc, u32 offset, u32 mask, u32 value) +{ + u32 reg; + + reg = dwc3_apple_readl(appledwc, offset); + reg &= ~mask; + reg |= value; + dwc3_apple_writel(appledwc, offset, reg); +} + +static void dwc3_apple_setup_cio(struct dwc3_apple *appledwc) +{ + dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_LFPS_OFFSET, APPLE_DWC3_CIO_LFPS_OFFSET_VALUE); + dwc3_apple_writel(appledwc, APPLE_DWC3_CIO_BW_NGT_OFFSET, + APPLE_DWC3_CIO_BW_NGT_OFFSET_VALUE); + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PENDING_HP_TIMER, + FIELD_PREP(APPLE_DWC3_CIO_PENDING_HP_TIMER, + APPLE_DWC3_CIO_PENDING_HP_TIMER_VALUE)); + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER, + FIELD_PREP(APPLE_DWC3_CIO_PM_LC_TIMER, APPLE_DWC3_CIO_PM_LC_TIMER_VALUE)); + dwc3_apple_mask(appledwc, APPLE_DWC3_CIO_LINK_TIMER, APPLE_DWC3_CIO_PM_ENTRY_TIMER, + FIELD_PREP(APPLE_DWC3_CIO_PM_ENTRY_TIMER, + APPLE_DWC3_CIO_PM_ENTRY_TIMER_VALUE)); +} + +static void dwc3_apple_set_ptrcap(struct dwc3_apple *appledwc, u32 mode) +{ + guard(spinlock_irqsave)(&appledwc->dwc.lock); + dwc3_set_prtcap(&appledwc->dwc, mode, false); +} + +static int dwc3_apple_core_probe(struct dwc3_apple *appledwc) +{ + struct dwc3_probe_data probe_data = {}; + int ret; + + lockdep_assert_held(&appledwc->lock); + WARN_ON_ONCE(appledwc->state != DWC3_APPLE_PROBE_PENDING); + + appledwc->dwc.dev = appledwc->dev; + probe_data.dwc = &appledwc->dwc; + probe_data.res = appledwc->mmio_resource; + probe_data.ignore_clocks_and_resets = true; + probe_data.skip_core_init_mode = true; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + + ret = dwc3_core_probe(&probe_data); + if (ret) + return ret; + + appledwc->state = DWC3_APPLE_NO_CABLE; + return 0; +} + +static int dwc3_apple_core_init(struct dwc3_apple *appledwc) +{ + int ret; + + lockdep_assert_held(&appledwc->lock); + + switch (appledwc->state) { + case DWC3_APPLE_PROBE_PENDING: + ret = dwc3_apple_core_probe(appledwc); + if (ret) + dev_err(appledwc->dev, "Failed to probe DWC3 Core, err=%d\n", ret); + break; + case DWC3_APPLE_NO_CABLE: + ret = dwc3_core_init(&appledwc->dwc); + if (ret) + dev_err(appledwc->dev, "Failed to initialize DWC3 Core, err=%d\n", ret); + break; + default: + /* Unreachable unless there's a bug in this driver */ + WARN_ON_ONCE(1); + ret = -EINVAL; + break; + } + + return ret; +} + +static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state) +{ + int ret, ret_reset; + + lockdep_assert_held(&appledwc->lock); + + /* + * The USB2 PHY on this platform must be configured for host or device mode while it is + * still powered off and before dwc3 tries to access it. Otherwise, the new configuration + * will sometimes only take affect after the *next* time dwc3 is brought up which causes + * the connected device to just not work. + * The USB3 PHY must be configured later after dwc3 has already been initialized. + */ + switch (state) { + case DWC3_APPLE_HOST: + phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_HOST); + break; + case DWC3_APPLE_DEVICE: + phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_DEVICE); + break; + default: + /* Unreachable unless there's a bug in this driver */ + return -EINVAL; + } + + ret = reset_control_deassert(appledwc->reset); + if (ret) { + dev_err(appledwc->dev, "Failed to deassert reset, err=%d\n", ret); + return ret; + } + + ret = dwc3_apple_core_init(appledwc); + if (ret) + goto reset_assert; + + /* + * Now that the core is initialized and already went through dwc3_core_soft_reset we can + * configure some unknown Apple-specific settings and then bring up xhci or gadget mode. + */ + dwc3_apple_setup_cio(appledwc); + + switch (state) { + case DWC3_APPLE_HOST: + appledwc->dwc.dr_mode = USB_DR_MODE_HOST; + dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST); + /* + * This platform requires SUSPHY to be enabled here already in order to properly + * configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY + * has already been configured to the correct mode earlier. + */ + dwc3_enable_susphy(&appledwc->dwc, true); + phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_HOST); + ret = dwc3_host_init(&appledwc->dwc); + if (ret) { + dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret); + goto core_exit; + } + + break; + case DWC3_APPLE_DEVICE: + appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL; + dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE); + /* + * This platform requires SUSPHY to be enabled here already in order to properly + * configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY + * has already been configured to the correct mode earlier. + */ + dwc3_enable_susphy(&appledwc->dwc, true); + phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_DEVICE); + ret = dwc3_gadget_init(&appledwc->dwc); + if (ret) { + dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret); + goto core_exit; + } + break; + default: + /* Unreachable unless there's a bug in this driver */ + WARN_ON_ONCE(1); + ret = -EINVAL; + goto core_exit; + } + + appledwc->state = state; + return 0; + +core_exit: + dwc3_core_exit(&appledwc->dwc); +reset_assert: + ret_reset = reset_control_assert(appledwc->reset); + if (ret_reset) + dev_warn(appledwc->dev, "Failed to assert reset, err=%d\n", ret_reset); + + return ret; +} + +static int dwc3_apple_exit(struct dwc3_apple *appledwc) +{ + int ret = 0; + + lockdep_assert_held(&appledwc->lock); + + switch (appledwc->state) { + case DWC3_APPLE_PROBE_PENDING: + case DWC3_APPLE_NO_CABLE: + /* Nothing to do if we're already off */ + return 0; + case DWC3_APPLE_DEVICE: + dwc3_gadget_exit(&appledwc->dwc); + break; + case DWC3_APPLE_HOST: + dwc3_host_exit(&appledwc->dwc); + break; + } + + /* + * This platform requires SUSPHY to be enabled in order to properly power down the PHY + * and switch dwc3's PIPE interface back to a dummy PHY (i.e. no USB3 support and USB2 via + * a different PHY connected through ULPI). + */ + dwc3_enable_susphy(&appledwc->dwc, true); + dwc3_core_exit(&appledwc->dwc); + appledwc->state = DWC3_APPLE_NO_CABLE; + + ret = reset_control_assert(appledwc->reset); + if (ret) { + dev_err(appledwc->dev, "Failed to assert reset, err=%d\n", ret); + return ret; + } + + return 0; +} + +static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, enum usb_role role) +{ + struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw); + int ret; + + guard(mutex)(&appledwc->lock); + + /* + * Skip role switches if appledwc is already in the desired state. The + * USB-C port controller on M2 and M1/M2 Pro/Max/Ultra devices issues + * additional interrupts which results in usb_role_switch_set_role() + * calls with the current role. + * Ignore those calls here to ensure the USB-C port controller and + * appledwc are in a consistent state. + * This matches the behaviour in __dwc3_set_mode(). + * Do no handle USB_ROLE_NONE for DWC3_APPLE_NO_CABLE and + * DWC3_APPLE_PROBE_PENDING since that is no-op anyway. + */ + if (appledwc->state == DWC3_APPLE_HOST && role == USB_ROLE_HOST) + return 0; + if (appledwc->state == DWC3_APPLE_DEVICE && role == USB_ROLE_DEVICE) + return 0; + + /* + * We need to tear all of dwc3 down and re-initialize it every time a cable is + * connected or disconnected or when the mode changes. See the documentation for enum + * dwc3_apple_state for details. + */ + ret = dwc3_apple_exit(appledwc); + if (ret) + return ret; + + switch (role) { + case USB_ROLE_NONE: + /* Nothing to do if no cable is connected */ + return 0; + case USB_ROLE_HOST: + return dwc3_apple_init(appledwc, DWC3_APPLE_HOST); + case USB_ROLE_DEVICE: + return dwc3_apple_init(appledwc, DWC3_APPLE_DEVICE); + default: + dev_err(appledwc->dev, "Invalid target role: %d\n", role); + return -EINVAL; + } +} + +static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw) +{ + struct dwc3_apple *appledwc = usb_role_switch_get_drvdata(sw); + + guard(mutex)(&appledwc->lock); + + switch (appledwc->state) { + case DWC3_APPLE_HOST: + return USB_ROLE_HOST; + case DWC3_APPLE_DEVICE: + return USB_ROLE_DEVICE; + case DWC3_APPLE_NO_CABLE: + case DWC3_APPLE_PROBE_PENDING: + return USB_ROLE_NONE; + default: + /* Unreachable unless there's a bug in this driver */ + dev_err(appledwc->dev, "Invalid internal state: %d\n", appledwc->state); + return USB_ROLE_NONE; + } +} + +static int dwc3_apple_setup_role_switch(struct dwc3_apple *appledwc) +{ + struct usb_role_switch_desc dwc3_role_switch = { NULL }; + + dwc3_role_switch.fwnode = dev_fwnode(appledwc->dev); + dwc3_role_switch.set = dwc3_usb_role_switch_set; + dwc3_role_switch.get = dwc3_usb_role_switch_get; + dwc3_role_switch.driver_data = appledwc; + appledwc->role_sw = usb_role_switch_register(appledwc->dev, &dwc3_role_switch); + if (IS_ERR(appledwc->role_sw)) + return PTR_ERR(appledwc->role_sw); + + return 0; +} + +static int dwc3_apple_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3_apple *appledwc; + int ret; + + appledwc = devm_kzalloc(&pdev->dev, sizeof(*appledwc), GFP_KERNEL); + if (!appledwc) + return -ENOMEM; + + appledwc->dev = &pdev->dev; + mutex_init(&appledwc->lock); + + appledwc->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(appledwc->reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(appledwc->reset), + "Failed to get reset control\n"); + + ret = reset_control_assert(appledwc->reset); + if (ret) { + dev_err(&pdev->dev, "Failed to assert reset, err=%d\n", ret); + return ret; + } + + appledwc->mmio_resource = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dwc3-core"); + if (!appledwc->mmio_resource) { + dev_err(dev, "Failed to get DWC3 MMIO\n"); + return -EINVAL; + } + + appledwc->apple_regs = devm_platform_ioremap_resource_byname(pdev, "dwc3-apple"); + if (IS_ERR(appledwc->apple_regs)) + return dev_err_probe(dev, PTR_ERR(appledwc->apple_regs), + "Failed to map Apple-specific MMIO\n"); + + /* + * On this platform, DWC3 can only be brought up after parts of the PHY have been + * initialized with knowledge of the target mode and cable orientation from typec_set_mux. + * Since this has not happened here we cannot setup DWC3 yet and instead defer this until + * the first cable is connected. See the documentation for enum dwc3_apple_state for + * details. + */ + appledwc->state = DWC3_APPLE_PROBE_PENDING; + ret = dwc3_apple_setup_role_switch(appledwc); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to setup role switch\n"); + + return 0; +} + +static void dwc3_apple_remove(struct platform_device *pdev) +{ + struct dwc3 *dwc = platform_get_drvdata(pdev); + struct dwc3_apple *appledwc = to_dwc3_apple(dwc); + + guard(mutex)(&appledwc->lock); + + usb_role_switch_unregister(appledwc->role_sw); + + /* + * If we're still in DWC3_APPLE_PROBE_PENDING we never got any cable connected event and + * dwc3_core_probe was never called and there's hence no need to call dwc3_core_remove. + * dwc3_apple_exit can be called unconditionally because it checks the state itself. + */ + dwc3_apple_exit(appledwc); + if (appledwc->state != DWC3_APPLE_PROBE_PENDING) + dwc3_core_remove(&appledwc->dwc); +} + +static const struct of_device_id dwc3_apple_of_match[] = { + { .compatible = "apple,t8103-dwc3" }, + {} +}; +MODULE_DEVICE_TABLE(of, dwc3_apple_of_match); + +static struct platform_driver dwc3_apple_driver = { + .probe = dwc3_apple_probe, + .remove = dwc3_apple_remove, + .driver = { + .name = "dwc3-apple", + .of_match_table = dwc3_apple_of_match, + }, +}; + +module_platform_driver(dwc3_apple_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sven Peter <sven@kernel.org>"); +MODULE_DESCRIPTION("DesignWare DWC3 Apple Silicon Glue Driver"); diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index f5d963fae9e0..e934f94e8fd8 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -145,6 +145,12 @@ static void dwc3_exynos_remove(struct platform_device *pdev) regulator_disable(exynos->vdd10); } +static const struct dwc3_exynos_driverdata exynos2200_drvdata = { + .clk_names = { "link_aclk" }, + .num_clks = 1, + .suspend_clk_idx = -1, +}; + static const struct dwc3_exynos_driverdata exynos5250_drvdata = { .clk_names = { "usbdrd30" }, .num_clks = 1, @@ -163,6 +169,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, @@ -175,8 +187,17 @@ static const struct dwc3_exynos_driverdata gs101_drvdata = { .suspend_clk_idx = 1, }; +static const struct dwc3_exynos_driverdata exynosautov920_drvdata = { + .clk_names = { "ref", "susp_clk"}, + .num_clks = 2, + .suspend_clk_idx = 1, +}; + static const struct of_device_id exynos_dwc3_match[] = { { + .compatible = "samsung,exynos2200-dwusb3", + .data = &exynos2200_drvdata, + }, { .compatible = "samsung,exynos5250-dwusb3", .data = &exynos5250_drvdata, }, { @@ -186,9 +207,15 @@ 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, }, { + .compatible = "samsung,exynosautov920-dwusb3", + .data = &exynosautov920_drvdata, + }, { .compatible = "google,gs101-dwusb3", .data = &gs101_drvdata, }, { diff --git a/drivers/usb/dwc3/dwc3-generic-plat.c b/drivers/usb/dwc3/dwc3-generic-plat.c new file mode 100644 index 000000000000..ca69ac0eb07c --- /dev/null +++ b/drivers/usb/dwc3/dwc3-generic-plat.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * dwc3-generic-plat.c - DesignWare USB3 generic platform driver + * + * Copyright (C) 2025 Ze Huang <huang.ze@linux.dev> + * + * Inspired by dwc3-qcom.c and dwc3-of-simple.c + */ + +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> +#include <linux/regulator/consumer.h> +#include <linux/usb/otg.h> +#include "glue.h" + +#define EIC7700_HSP_BUS_FILTER_EN BIT(0) +#define EIC7700_HSP_BUS_CLKEN_GM BIT(9) +#define EIC7700_HSP_BUS_CLKEN_GS BIT(16) +#define EIC7700_HSP_AXI_LP_XM_CSYSREQ BIT(0) +#define EIC7700_HSP_AXI_LP_XS_CSYSREQ BIT(16) + +struct dwc3_generic { + struct device *dev; + struct dwc3 dwc; + struct clk_bulk_data *clks; + int num_clocks; + struct reset_control *resets; +}; + +struct dwc3_generic_config { + int (*init)(struct dwc3_generic *dwc3g); + struct dwc3_properties properties; +}; + +#define to_dwc3_generic(d) container_of((d), struct dwc3_generic, dwc) + +static void dwc3_generic_reset_control_assert(void *data) +{ + reset_control_assert(data); +} + +static int dwc3_eic7700_init(struct dwc3_generic *dwc3g) +{ + struct device *dev = dwc3g->dev; + struct regmap *regmap; + u32 hsp_usb_axi_lp; + u32 hsp_usb_bus; + u32 args[2]; + u32 val; + + regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, + "eswin,hsp-sp-csr", + ARRAY_SIZE(args), args); + if (IS_ERR(regmap)) { + dev_err(dev, "No hsp-sp-csr phandle specified\n"); + return PTR_ERR(regmap); + } + + hsp_usb_bus = args[0]; + hsp_usb_axi_lp = args[1]; + + regmap_read(regmap, hsp_usb_bus, &val); + regmap_write(regmap, hsp_usb_bus, val | EIC7700_HSP_BUS_FILTER_EN | + EIC7700_HSP_BUS_CLKEN_GM | EIC7700_HSP_BUS_CLKEN_GS); + + regmap_write(regmap, hsp_usb_axi_lp, EIC7700_HSP_AXI_LP_XM_CSYSREQ | + EIC7700_HSP_AXI_LP_XS_CSYSREQ); + return 0; +} + +static int dwc3_spacemit_k1_init(struct dwc3_generic *dwc3g) +{ + struct device *dev = dwc3g->dev; + + if (usb_get_dr_mode(dev) == USB_DR_MODE_HOST) { + int ret = devm_regulator_get_enable_optional(dev, "vbus"); + + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable VBUS\n"); + } + + return 0; +} + +static int dwc3_generic_probe(struct platform_device *pdev) +{ + const struct dwc3_generic_config *plat_config; + struct dwc3_probe_data probe_data = {}; + struct device *dev = &pdev->dev; + struct dwc3_generic *dwc3g; + struct resource *res; + int ret; + + dwc3g = devm_kzalloc(dev, sizeof(*dwc3g), GFP_KERNEL); + if (!dwc3g) + return -ENOMEM; + + dwc3g->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing memory resource\n"); + return -ENODEV; + } + + dwc3g->resets = devm_reset_control_array_get_optional_exclusive(dev); + if (IS_ERR(dwc3g->resets)) + return dev_err_probe(dev, PTR_ERR(dwc3g->resets), "failed to get resets\n"); + + ret = reset_control_assert(dwc3g->resets); + if (ret) + return dev_err_probe(dev, ret, "failed to assert resets\n"); + + /* Not strict timing, just for safety */ + udelay(2); + + ret = reset_control_deassert(dwc3g->resets); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert resets\n"); + + ret = devm_add_action_or_reset(dev, dwc3_generic_reset_control_assert, dwc3g->resets); + if (ret) + return ret; + + ret = devm_clk_bulk_get_all_enabled(dwc3g->dev, &dwc3g->clks); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get clocks\n"); + + dwc3g->num_clocks = ret; + dwc3g->dwc.dev = dev; + probe_data.dwc = &dwc3g->dwc; + probe_data.res = res; + probe_data.ignore_clocks_and_resets = true; + + plat_config = of_device_get_match_data(dev); + if (!plat_config) { + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + goto core_probe; + } + + probe_data.properties = plat_config->properties; + if (plat_config->init) { + ret = plat_config->init(dwc3g); + if (ret) + return dev_err_probe(dev, ret, + "failed to init platform\n"); + } + +core_probe: + ret = dwc3_core_probe(&probe_data); + if (ret) + return dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); + + return 0; +} + +static void dwc3_generic_remove(struct platform_device *pdev) +{ + struct dwc3 *dwc = platform_get_drvdata(pdev); + + dwc3_core_remove(dwc); +} + +static int dwc3_generic_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_generic *dwc3g = to_dwc3_generic(dwc); + int ret; + + ret = dwc3_pm_suspend(dwc); + if (ret) + return ret; + + clk_bulk_disable_unprepare(dwc3g->num_clocks, dwc3g->clks); + + return 0; +} + +static int dwc3_generic_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_generic *dwc3g = to_dwc3_generic(dwc); + int ret; + + ret = clk_bulk_prepare_enable(dwc3g->num_clocks, dwc3g->clks); + if (ret) + return ret; + + ret = dwc3_pm_resume(dwc); + if (ret) + return ret; + + return 0; +} + +static int dwc3_generic_runtime_suspend(struct device *dev) +{ + return dwc3_runtime_suspend(dev_get_drvdata(dev)); +} + +static int dwc3_generic_runtime_resume(struct device *dev) +{ + return dwc3_runtime_resume(dev_get_drvdata(dev)); +} + +static int dwc3_generic_runtime_idle(struct device *dev) +{ + return dwc3_runtime_idle(dev_get_drvdata(dev)); +} + +static const struct dev_pm_ops dwc3_generic_dev_pm_ops = { + SYSTEM_SLEEP_PM_OPS(dwc3_generic_suspend, dwc3_generic_resume) + RUNTIME_PM_OPS(dwc3_generic_runtime_suspend, dwc3_generic_runtime_resume, + dwc3_generic_runtime_idle) +}; + +static const struct dwc3_generic_config spacemit_k1_dwc3 = { + .init = dwc3_spacemit_k1_init, + .properties = DWC3_DEFAULT_PROPERTIES, +}; + +static const struct dwc3_generic_config fsl_ls1028_dwc3 = { + .properties.gsbuscfg0_reqinfo = 0x2222, +}; + +static const struct dwc3_generic_config eic7700_dwc3 = { + .init = dwc3_eic7700_init, + .properties = DWC3_DEFAULT_PROPERTIES, +}; + +static const struct of_device_id dwc3_generic_of_match[] = { + { .compatible = "spacemit,k1-dwc3", &spacemit_k1_dwc3}, + { .compatible = "spacemit,k3-dwc3", }, + { .compatible = "fsl,ls1028a-dwc3", &fsl_ls1028_dwc3}, + { .compatible = "eswin,eic7700-dwc3", &eic7700_dwc3}, + { .compatible = "starfive,jhb100-dwc3", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dwc3_generic_of_match); + +static struct platform_driver dwc3_generic_driver = { + .probe = dwc3_generic_probe, + .remove = dwc3_generic_remove, + .driver = { + .name = "dwc3-generic-plat", + .of_match_table = dwc3_generic_of_match, + .pm = pm_ptr(&dwc3_generic_dev_pm_ops), + }, +}; +module_platform_driver(dwc3_generic_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DesignWare USB3 generic platform driver"); diff --git a/drivers/usb/dwc3/dwc3-google.c b/drivers/usb/dwc3/dwc3-google.c new file mode 100644 index 000000000000..4ca567ec01d0 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-google.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dwc3-google.c - Google DWC3 Specific Glue Layer + * + * Copyright (c) 2025, Google LLC + * Author: Roy Luo <royluo@google.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include "core.h" +#include "glue.h" + +/* HOST CFG registers */ +#define HC_STATUS_OFFSET 0x0 +#define HC_STATUS_CURRENT_POWER_STATE_U2PMU GENMASK(1, 0) +#define HC_STATUS_CURRENT_POWER_STATE_U3PMU GENMASK(4, 3) + +#define HOST_CFG1_OFFSET 0x4 +#define HOST_CFG1_PME_EN BIT(3) +#define HOST_CFG1_PM_POWER_STATE_REQUEST GENMASK(5, 4) +#define HOST_CFG1_PM_POWER_STATE_D0 0x0 +#define HOST_CFG1_PM_POWER_STATE_D3 0x3 + +/* USBINT registers */ +#define USBINT_CFG1_OFFSET 0x0 +#define USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK BIT(2) +#define USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK BIT(3) +#define USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN BIT(8) +#define USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN BIT(9) +#define USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR BIT(14) +#define USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR BIT(15) + +#define USBINT_STATUS_OFFSET 0x4 +#define USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW BIT(2) +#define USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW BIT(3) + +#define USBCS_TOP_CTRL_CFG1_OFFSET 0xc +#define USBCS_TOP_CTRL_CFG1_USB2ONLY_MODE BIT(5) + +#define DWC3_GOOGLE_MAX_RESETS 4 + +struct dwc3_google { + struct device *dev; + struct dwc3 dwc; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control_bulk_data rsts[DWC3_GOOGLE_MAX_RESETS]; + int num_rsts; + struct reset_control *non_sticky_rst; + struct device *usb_psw_pd; + struct device_link *usb_psw_pd_dl; + struct notifier_block usb_psw_pd_nb; + struct device *usb_top_pd; + struct device_link *usb_top_pd_dl; + struct regmap *usb_cfg_regmap; + unsigned int host_cfg_offset; + unsigned int usbint_cfg_offset; + int hs_pme_irq; + int ss_pme_irq; + bool is_usb2only; + bool is_hibernation; +}; + +#define to_dwc3_google(d) container_of_const((d), struct dwc3_google, dwc) + +static int dwc3_google_rst_init(struct dwc3_google *google) +{ + int ret; + + google->num_rsts = 4; + google->rsts[0].id = "non_sticky"; + google->rsts[1].id = "sticky"; + google->rsts[2].id = "drd_bus"; + google->rsts[3].id = "top"; + + ret = devm_reset_control_bulk_get_exclusive(google->dev, + google->num_rsts, + google->rsts); + + if (ret < 0) + return ret; + + google->non_sticky_rst = google->rsts[0].rstc; + + return 0; +} + +static int dwc3_google_set_pmu_state(struct dwc3_google *google, int state) +{ + u32 reg; + int ret; + + regmap_read(google->usb_cfg_regmap, + google->host_cfg_offset + HOST_CFG1_OFFSET, ®); + + reg &= ~HOST_CFG1_PM_POWER_STATE_REQUEST; + reg |= (FIELD_PREP(HOST_CFG1_PM_POWER_STATE_REQUEST, state) | + HOST_CFG1_PME_EN); + regmap_write(google->usb_cfg_regmap, + google->host_cfg_offset + HOST_CFG1_OFFSET, reg); + + ret = regmap_read_poll_timeout(google->usb_cfg_regmap, + google->host_cfg_offset + HC_STATUS_OFFSET, reg, + (FIELD_GET(HC_STATUS_CURRENT_POWER_STATE_U2PMU, + reg) == state && + FIELD_GET(HC_STATUS_CURRENT_POWER_STATE_U3PMU, + reg) == state), + 10, 10000); + + if (ret) + dev_err(google->dev, "failed to set PMU state %d\n", state); + + return ret; +} + +/* + * Clear pme interrupts and report their status. + * The hardware requires write-1 then write-0 sequence to clear the interrupt bits. + */ +static u32 dwc3_google_clear_pme_irqs(struct dwc3_google *google) +{ + u32 irq_status, reg_set, reg_clear; + + regmap_read(google->usb_cfg_regmap, + google->usbint_cfg_offset + USBINT_STATUS_OFFSET, &irq_status); + + irq_status &= (USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW | + USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW); + if (!irq_status) + return irq_status; + + regmap_read(google->usb_cfg_regmap, + google->usbint_cfg_offset + USBINT_CFG1_OFFSET, ®_set); + + reg_clear = reg_set; + if (irq_status & USBINT_STATUS_USBDRD_PME_GEN_U2P_INTR_STS_RAW) { + reg_set |= USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR; + reg_clear &= ~USBINT_CFG1_USBDRD_PME_GEN_U2_INTR_CLR; + } + if (irq_status & USBINT_STATUS_USBDRD_PME_GEN_U3P_INTR_STS_RAW) { + reg_set |= USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR; + reg_clear &= ~USBINT_CFG1_USBDRD_PME_GEN_U3_INTR_CLR; + } + + regmap_write(google->usb_cfg_regmap, + google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg_set); + regmap_write(google->usb_cfg_regmap, + google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg_clear); + + return irq_status; +} + +static void dwc3_google_enable_pme_irq(struct dwc3_google *google) +{ + u32 reg; + + regmap_read(google->usb_cfg_regmap, + google->usbint_cfg_offset + USBINT_CFG1_OFFSET, ®); + reg &= ~(USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK | + USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK); + reg |= (USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN | + USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN); + regmap_write(google->usb_cfg_regmap, + google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg); + + enable_irq(google->hs_pme_irq); + enable_irq(google->ss_pme_irq); + enable_irq_wake(google->hs_pme_irq); + enable_irq_wake(google->ss_pme_irq); +} + +static void dwc3_google_disable_pme_irq(struct dwc3_google *google) +{ + u32 reg; + + regmap_read(google->usb_cfg_regmap, + google->usbint_cfg_offset + USBINT_CFG1_OFFSET, ®); + reg &= ~(USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_INT_EN | + USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_INT_EN); + reg |= (USBINT_CFG1_USBDRD_PME_GEN_U2P_INTR_MSK | + USBINT_CFG1_USBDRD_PME_GEN_U3P_INTR_MSK); + regmap_write(google->usb_cfg_regmap, + google->usbint_cfg_offset + USBINT_CFG1_OFFSET, reg); + + disable_irq_wake(google->hs_pme_irq); + disable_irq_wake(google->ss_pme_irq); + disable_irq_nosync(google->hs_pme_irq); + disable_irq_nosync(google->ss_pme_irq); +} + +static irqreturn_t dwc3_google_resume_irq(int irq, void *data) +{ + struct dwc3_google *google = data; + struct dwc3 *dwc = &google->dwc; + u32 irq_status, dr_role; + + irq_status = dwc3_google_clear_pme_irqs(google); + dr_role = dwc->current_dr_role; + + if (!irq_status || !google->is_hibernation || + dr_role != DWC3_GCTL_PRTCAP_HOST) { + dev_dbg(google->dev, "spurious pme irq %d, hibernation %d, dr_role %u\n", + irq, google->is_hibernation, dr_role); + return IRQ_HANDLED; + } + + if (dwc->xhci) + pm_runtime_resume(&dwc->xhci->dev); + + return IRQ_HANDLED; +} + +static int dwc3_google_request_irq(struct dwc3_google *google, struct platform_device *pdev, + const char *irq_name, const char *req_name) +{ + int ret; + int irq; + + irq = platform_get_irq_byname(pdev, irq_name); + if (irq < 0) + return irq; + + irq_set_status_flags(irq, IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(google->dev, irq, NULL, + dwc3_google_resume_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + req_name, google); + if (ret < 0) { + dev_err(google->dev, "failed to request irq %s\n", req_name); + return ret; + } + + return irq; +} + +static int dwc3_google_usb_psw_pd_notifier(struct notifier_block *nb, unsigned long action, void *d) +{ + struct dwc3_google *google = container_of(nb, struct dwc3_google, usb_psw_pd_nb); + int ret; + + if (!google->is_hibernation) + return NOTIFY_OK; + + if (action == GENPD_NOTIFY_OFF) { + dev_dbg(google->dev, "enter D3 power state\n"); + dwc3_google_set_pmu_state(google, HOST_CFG1_PM_POWER_STATE_D3); + ret = reset_control_assert(google->non_sticky_rst); + if (ret) + dev_err(google->dev, "non sticky reset assert failed: %d\n", ret); + } else if (action == GENPD_NOTIFY_ON) { + dev_dbg(google->dev, "enter D0 power state\n"); + dwc3_google_clear_pme_irqs(google); + ret = reset_control_deassert(google->non_sticky_rst); + if (ret) + dev_err(google->dev, "non sticky reset deassert failed: %d\n", ret); + dwc3_google_set_pmu_state(google, HOST_CFG1_PM_POWER_STATE_D0); + } + + return NOTIFY_OK; +} + +static void dwc3_google_pm_domain_deinit(struct dwc3_google *google) +{ + if (google->usb_top_pd_dl) + device_link_del(google->usb_top_pd_dl); + + if (!IS_ERR_OR_NULL(google->usb_top_pd)) { + device_set_wakeup_capable(google->usb_top_pd, false); + dev_pm_domain_detach(google->usb_top_pd, true); + } + + if (google->usb_psw_pd_dl) + device_link_del(google->usb_psw_pd_dl); + + if (!IS_ERR_OR_NULL(google->usb_psw_pd)) { + dev_pm_genpd_remove_notifier(google->usb_psw_pd); + dev_pm_domain_detach(google->usb_psw_pd, true); + } +} + +static int dwc3_google_pm_domain_init(struct dwc3_google *google) +{ + int ret; + + /* + * Establish PM RUNTIME link between dwc dev and its power domain usb_psw_pd, + * register notifier block to handle hibernation. + */ + google->usb_psw_pd = dev_pm_domain_attach_by_name(google->dev, "psw"); + if (IS_ERR_OR_NULL(google->usb_psw_pd)) { + dev_err(google->dev, "failed to get psw pd"); + ret = google->usb_psw_pd ? PTR_ERR(google->usb_psw_pd) : -ENODATA; + return ret; + } + + google->usb_psw_pd_nb.notifier_call = dwc3_google_usb_psw_pd_notifier; + ret = dev_pm_genpd_add_notifier(google->usb_psw_pd, &google->usb_psw_pd_nb); + if (ret) { + dev_err(google->dev, "failed to add psw pd notifier"); + goto err; + } + + google->usb_psw_pd_dl = device_link_add(google->dev, google->usb_psw_pd, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!google->usb_psw_pd_dl) { + dev_err(google->usb_psw_pd, "failed to add device link"); + ret = -ENODEV; + goto err; + } + + /* + * usb_top_pd is the parent power domain of usb_psw_pd. Keeping usb_top_pd on + * while usb_psw_pd is off places the controller in a power-gated state, + * essential for hibernation. Acquire a handle to usb_top_pd and sets it as + * wakeup-capable to allow the domain to be left on during system suspend. + */ + google->usb_top_pd = dev_pm_domain_attach_by_name(google->dev, "top"); + if (IS_ERR_OR_NULL(google->usb_top_pd)) { + dev_err(google->dev, "failed to get top pd"); + ret = google->usb_top_pd ? PTR_ERR(google->usb_top_pd) : -ENODATA; + goto err; + } + device_set_wakeup_capable(google->usb_top_pd, true); + + google->usb_top_pd_dl = device_link_add(google->dev, google->usb_top_pd, + DL_FLAG_STATELESS); + if (!google->usb_top_pd_dl) { + dev_err(google->usb_top_pd, "failed to add device link"); + ret = -ENODEV; + goto err; + } + + return 0; + +err: + dwc3_google_pm_domain_deinit(google); + + return ret; +} + +static void dwc3_google_program_usb2only(struct dwc3_google *google) +{ + u32 reg; + + regmap_read(google->usb_cfg_regmap, + google->usbint_cfg_offset + USBCS_TOP_CTRL_CFG1_OFFSET, ®); + reg |= USBCS_TOP_CTRL_CFG1_USB2ONLY_MODE; + regmap_write(google->usb_cfg_regmap, + google->usbint_cfg_offset + USBCS_TOP_CTRL_CFG1_OFFSET, reg); +} + +static int dwc3_google_probe(struct platform_device *pdev) +{ + struct dwc3_probe_data probe_data = {}; + struct device *dev = &pdev->dev; + struct dwc3_google *google; + struct resource *res; + int ret; + u32 args[2]; + + google = devm_kzalloc(&pdev->dev, sizeof(*google), GFP_KERNEL); + if (!google) + return -ENOMEM; + + google->dev = &pdev->dev; + + ret = dwc3_google_pm_domain_init(google); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to init pdom\n"); + + google->usb_cfg_regmap = + syscon_regmap_lookup_by_phandle_args(dev->of_node, + "google,usb-cfg-csr", + ARRAY_SIZE(args), args); + if (IS_ERR(google->usb_cfg_regmap)) { + ret = dev_err_probe(dev, PTR_ERR(google->usb_cfg_regmap), + "invalid usb cfg csr\n"); + goto err_deinit_pdom; + } + + google->host_cfg_offset = args[0]; + google->usbint_cfg_offset = args[1]; + + if (device_property_match_string(dev, "phy-names", "usb3-phy") < 0) { + google->is_usb2only = true; + dwc3_google_program_usb2only(google); + } + + ret = devm_clk_bulk_get_all_enabled(dev, &google->clks); + if (ret < 0) { + ret = dev_err_probe(dev, ret, "failed to get and enable clks\n"); + goto err_deinit_pdom; + } + google->num_clks = ret; + + ret = dwc3_google_rst_init(google); + if (ret) { + ret = dev_err_probe(dev, ret, "failed to get resets\n"); + goto err_deinit_pdom; + } + + ret = reset_control_bulk_deassert(google->num_rsts, google->rsts); + if (ret) { + ret = dev_err_probe(dev, ret, "failed to deassert rsts\n"); + goto err_deinit_pdom; + } + + ret = dwc3_google_request_irq(google, pdev, "hs_pme", "USB HS wakeup"); + if (ret < 0) { + ret = dev_err_probe(dev, ret, "failed to request hs pme irq"); + goto err_reset_assert; + } + google->hs_pme_irq = ret; + + ret = dwc3_google_request_irq(google, pdev, "ss_pme", "USB SS wakeup"); + if (ret < 0) { + ret = dev_err_probe(dev, ret, "failed to request ss pme irq"); + goto err_reset_assert; + } + google->ss_pme_irq = ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = dev_err_probe(dev, -ENODEV, "invalid memory\n"); + goto err_reset_assert; + } + + device_init_wakeup(dev, true); + + google->dwc.dev = dev; + probe_data.dwc = &google->dwc; + probe_data.res = res; + probe_data.ignore_clocks_and_resets = true; + ret = dwc3_core_probe(&probe_data); + if (ret) { + ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); + goto err_reset_assert; + } + + return 0; + +err_reset_assert: + reset_control_bulk_assert(google->num_rsts, google->rsts); + +err_deinit_pdom: + dwc3_google_pm_domain_deinit(google); + + return ret; +} + +static void dwc3_google_remove(struct platform_device *pdev) +{ + struct dwc3 *dwc = platform_get_drvdata(pdev); + struct dwc3_google *google = to_dwc3_google(dwc); + + dwc3_core_remove(&google->dwc); + + reset_control_bulk_assert(google->num_rsts, google->rsts); + + dwc3_google_pm_domain_deinit(google); +} + +static int dwc3_google_suspend(struct dwc3_google *google, pm_message_t msg) +{ + if (pm_runtime_suspended(google->dev)) + return 0; + + if (google->dwc.current_dr_role == DWC3_GCTL_PRTCAP_HOST) { + /* + * Follow dwc3_suspend_common() guidelines for deciding between + * a full teardown and hibernation. + */ + if (PMSG_IS_AUTO(msg) || device_may_wakeup(google->dev)) { + dev_dbg(google->dev, "enter hibernation"); + pm_runtime_get_sync(google->usb_top_pd); + device_wakeup_enable(google->usb_top_pd); + dwc3_google_enable_pme_irq(google); + google->is_hibernation = true; + return 0; + } + } + + reset_control_bulk_assert(google->num_rsts, google->rsts); + clk_bulk_disable_unprepare(google->num_clks, google->clks); + + return 0; +} + +static int dwc3_google_resume(struct dwc3_google *google, pm_message_t msg) +{ + int ret; + + if (google->is_hibernation) { + dev_dbg(google->dev, "exit hibernation"); + dwc3_google_disable_pme_irq(google); + device_wakeup_disable(google->usb_top_pd); + pm_runtime_put_sync(google->usb_top_pd); + google->is_hibernation = false; + return 0; + } + + if (google->is_usb2only) + dwc3_google_program_usb2only(google); + + ret = clk_bulk_prepare_enable(google->num_clks, google->clks); + if (ret) + return ret; + + ret = reset_control_bulk_deassert(google->num_rsts, google->rsts); + if (ret) { + clk_bulk_disable_unprepare(google->num_clks, google->clks); + return ret; + } + + return 0; +} + +static int dwc3_google_pm_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_google *google = to_dwc3_google(dwc); + int ret; + + ret = dwc3_pm_suspend(&google->dwc); + if (ret) + return ret; + + return dwc3_google_suspend(google, PMSG_SUSPEND); +} + +static int dwc3_google_pm_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_google *google = to_dwc3_google(dwc); + int ret; + + ret = dwc3_google_resume(google, PMSG_RESUME); + if (ret) + return ret; + + return dwc3_pm_resume(&google->dwc); +} + +static void dwc3_google_complete(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + + dwc3_pm_complete(dwc); +} + +static int dwc3_google_prepare(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + + return dwc3_pm_prepare(dwc); +} + +static int dwc3_google_runtime_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_google *google = to_dwc3_google(dwc); + int ret; + + ret = dwc3_runtime_suspend(&google->dwc); + if (ret) + return ret; + + return dwc3_google_suspend(google, PMSG_AUTO_SUSPEND); +} + +static int dwc3_google_runtime_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_google *google = to_dwc3_google(dwc); + int ret; + + ret = dwc3_google_resume(google, PMSG_AUTO_RESUME); + if (ret) + return ret; + + return dwc3_runtime_resume(&google->dwc); +} + +static int dwc3_google_runtime_idle(struct device *dev) +{ + return dwc3_runtime_idle(dev_get_drvdata(dev)); +} + +static const struct dev_pm_ops dwc3_google_dev_pm_ops = { + SYSTEM_SLEEP_PM_OPS(dwc3_google_pm_suspend, dwc3_google_pm_resume) + RUNTIME_PM_OPS(dwc3_google_runtime_suspend, dwc3_google_runtime_resume, + dwc3_google_runtime_idle) + .complete = pm_sleep_ptr(dwc3_google_complete), + .prepare = pm_sleep_ptr(dwc3_google_prepare), +}; + +static const struct of_device_id dwc3_google_of_match[] = { + { .compatible = "google,lga-dwc3" }, + { } +}; +MODULE_DEVICE_TABLE(of, dwc3_google_of_match); + +static struct platform_driver dwc3_google_driver = { + .probe = dwc3_google_probe, + .remove = dwc3_google_remove, + .driver = { + .name = "dwc3-google", + .pm = pm_ptr(&dwc3_google_dev_pm_ops), + .of_match_table = dwc3_google_of_match, + }, +}; + +module_platform_driver(dwc3_google_driver); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DesignWare DWC3 Google Glue Driver"); diff --git a/drivers/usb/dwc3/dwc3-imx.c b/drivers/usb/dwc3/dwc3-imx.c new file mode 100644 index 000000000000..973a486b544d --- /dev/null +++ b/drivers/usb/dwc3/dwc3-imx.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dwc3-imx.c - NXP i.MX Soc USB3 Specific Glue layer + * + * Copyright 2026 NXP + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#include "core.h" +#include "glue.h" + +/* USB wakeup registers */ +#define USB_WAKEUP_CTRL 0x00 + +/* Global wakeup interrupt enable, also used to clear interrupt */ +#define USB_WAKEUP_EN BIT(31) +/* Wakeup from connect or disconnect, only for superspeed */ +#define USB_WAKEUP_SS_CONN BIT(5) +/* 0 select vbus_valid, 1 select sessvld */ +#define USB_WAKEUP_VBUS_SRC_SESS_VAL BIT(4) +/* Enable signal for wake up from u3 state */ +#define USB_WAKEUP_U3_EN BIT(3) +/* Enable signal for wake up from id change */ +#define USB_WAKEUP_ID_EN BIT(2) +/* Enable signal for wake up from vbus change */ +#define USB_WAKEUP_VBUS_EN BIT(1) +/* Enable signal for wake up from dp/dm change */ +#define USB_WAKEUP_DPDM_EN BIT(0) + +#define USB_WAKEUP_EN_MASK GENMASK(5, 0) + +/* USB glue registers */ +#define USB_CTRL0 0x00 +#define USB_CTRL1 0x04 + +#define USB_CTRL0_PORTPWR_EN BIT(12) /* 1 - PPC enabled (default) */ +#define USB_CTRL0_USB3_FIXED BIT(22) /* 1 - USB3 permanent attached */ +#define USB_CTRL0_USB2_FIXED BIT(23) /* 1 - USB2 permanent attached */ + +#define USB_CTRL1_OC_POLARITY BIT(16) /* 0 - HIGH / 1 - LOW */ +#define USB_CTRL1_PWR_POLARITY BIT(17) /* 0 - HIGH / 1 - LOW */ + +struct dwc3_imx { + struct dwc3 dwc; + struct device *dev; + void __iomem *blkctl_base; + void __iomem *glue_base; + struct clk *hsio_clk; + struct clk *suspend_clk; + int irq; + bool pm_suspended; + bool wakeup_pending; + unsigned permanent_attached:1; + unsigned disable_pwr_ctrl:1; + unsigned overcur_active_low:1; + unsigned power_active_low:1; +}; + +#define to_dwc3_imx(d) container_of((d), struct dwc3_imx, dwc) + +static void dwc3_imx_get_property(struct dwc3_imx *dwc_imx) +{ + struct device *dev = dwc_imx->dev; + + dwc_imx->permanent_attached = + device_property_read_bool(dev, "fsl,permanently-attached"); + dwc_imx->disable_pwr_ctrl = + device_property_read_bool(dev, "fsl,disable-port-power-control"); + dwc_imx->overcur_active_low = + device_property_read_bool(dev, "fsl,over-current-active-low"); + dwc_imx->power_active_low = + device_property_read_bool(dev, "fsl,power-active-low"); +} + +static void dwc3_imx_configure_glue(struct dwc3_imx *dwc_imx) +{ + u32 value; + + if (!dwc_imx->glue_base) + return; + + value = readl(dwc_imx->glue_base + USB_CTRL0); + + if (dwc_imx->permanent_attached) + value |= USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED; + else + value &= ~(USB_CTRL0_USB2_FIXED | USB_CTRL0_USB3_FIXED); + + if (dwc_imx->disable_pwr_ctrl) + value &= ~USB_CTRL0_PORTPWR_EN; + else + value |= USB_CTRL0_PORTPWR_EN; + + writel(value, dwc_imx->glue_base + USB_CTRL0); + + value = readl(dwc_imx->glue_base + USB_CTRL1); + if (dwc_imx->overcur_active_low) + value |= USB_CTRL1_OC_POLARITY; + else + value &= ~USB_CTRL1_OC_POLARITY; + + if (dwc_imx->power_active_low) + value |= USB_CTRL1_PWR_POLARITY; + else + value &= ~USB_CTRL1_PWR_POLARITY; + + writel(value, dwc_imx->glue_base + USB_CTRL1); +} + +static void dwc3_imx_wakeup_enable(struct dwc3_imx *dwc_imx, pm_message_t msg) +{ + struct dwc3 *dwc = &dwc_imx->dwc; + u32 val; + + val = readl(dwc_imx->blkctl_base + USB_WAKEUP_CTRL); + + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->xhci) { + val |= USB_WAKEUP_EN | USB_WAKEUP_DPDM_EN; + if (PMSG_IS_AUTO(msg)) + val |= USB_WAKEUP_SS_CONN | USB_WAKEUP_U3_EN; + } else { + val |= USB_WAKEUP_EN | USB_WAKEUP_VBUS_EN | + USB_WAKEUP_VBUS_SRC_SESS_VAL; + } + + writel(val, dwc_imx->blkctl_base + USB_WAKEUP_CTRL); +} + +static void dwc3_imx_wakeup_disable(struct dwc3_imx *dwc_imx) +{ + u32 val; + + val = readl(dwc_imx->blkctl_base + USB_WAKEUP_CTRL); + val &= ~(USB_WAKEUP_EN | USB_WAKEUP_EN_MASK); + writel(val, dwc_imx->blkctl_base + USB_WAKEUP_CTRL); +} + +static irqreturn_t dwc3_imx_interrupt(int irq, void *data) +{ + struct dwc3_imx *dwc_imx = data; + struct dwc3 *dwc = &dwc_imx->dwc; + + if (!dwc_imx->pm_suspended) + return IRQ_HANDLED; + + disable_irq_nosync(dwc_imx->irq); + dwc_imx->wakeup_pending = true; + + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST && dwc->xhci) + pm_runtime_resume(&dwc->xhci->dev); + else if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) + pm_runtime_get(dwc->dev); + + return IRQ_HANDLED; +} + +static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role) +{ + if (role == USB_ROLE_HOST) + /* + * For xhci host, we need disable dwc core auto + * suspend, because during this auto suspend delay(5s), + * xhci host RUN_STOP is cleared and wakeup is not + * enabled, if device is inserted, xhci host can't + * response the connection. + */ + pm_runtime_dont_use_autosuspend(dwc->dev); + else + pm_runtime_use_autosuspend(dwc->dev); +} + +static struct dwc3_glue_ops dwc3_imx_glue_ops = { + .pre_set_role = dwc3_imx_pre_set_role, +}; + +static const struct property_entry dwc3_imx_properties[] = { + PROPERTY_ENTRY_BOOL("xhci-missing-cas-quirk"), + PROPERTY_ENTRY_BOOL("xhci-skip-phy-init-quirk"), + {}, +}; + +static const struct software_node dwc3_imx_swnode = { + .properties = dwc3_imx_properties, +}; + +static int dwc3_imx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3_imx *dwc_imx; + struct dwc3 *dwc; + struct resource *res; + const char *irq_name; + struct dwc3_probe_data probe_data = {}; + int ret, irq; + + dwc_imx = devm_kzalloc(dev, sizeof(*dwc_imx), GFP_KERNEL); + if (!dwc_imx) + return -ENOMEM; + + platform_set_drvdata(pdev, dwc_imx); + dwc_imx->dev = dev; + + dwc3_imx_get_property(dwc_imx); + + dwc_imx->blkctl_base = devm_platform_ioremap_resource_byname(pdev, "blkctl"); + if (IS_ERR(dwc_imx->blkctl_base)) + return PTR_ERR(dwc_imx->blkctl_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "glue"); + if (!res) { + dev_warn(dev, "Base address for glue layer missing\n"); + } else { + dwc_imx->glue_base = devm_ioremap_resource(dev, res); + if (IS_ERR(dwc_imx->glue_base)) + return PTR_ERR(dwc_imx->glue_base); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); + if (!res) + return dev_err_probe(dev, -ENODEV, "missing core memory resource\n"); + + dwc_imx->hsio_clk = devm_clk_get_enabled(dev, "hsio"); + if (IS_ERR(dwc_imx->hsio_clk)) + return dev_err_probe(dev, PTR_ERR(dwc_imx->hsio_clk), + "Failed to get hsio clk\n"); + + dwc_imx->suspend_clk = devm_clk_get_enabled(dev, "suspend"); + if (IS_ERR(dwc_imx->suspend_clk)) + return dev_err_probe(dev, PTR_ERR(dwc_imx->suspend_clk), + "Failed to get suspend clk\n"); + + irq = platform_get_irq_byname(pdev, "wakeup"); + if (irq < 0) + return irq; + dwc_imx->irq = irq; + + irq_name = devm_kasprintf(dev, GFP_KERNEL, "%s:wakeup", dev_name(dev)); + if (!irq_name) + return dev_err_probe(dev, -ENOMEM, "failed to create irq_name\n"); + + ret = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx_interrupt, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + irq_name, dwc_imx); + if (ret) + return dev_err_probe(dev, ret, "failed to request IRQ #%d\n", irq); + + ret = device_add_software_node(dev, &dwc3_imx_swnode); + if (ret) + return dev_err_probe(dev, ret, "failed to add software node\n"); + + dwc3_imx_configure_glue(dwc_imx); + + dwc = &dwc_imx->dwc; + dwc->dev = dev; + dwc->glue_ops = &dwc3_imx_glue_ops; + + probe_data.res = res; + probe_data.dwc = dwc; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + probe_data.properties.needs_full_reinit = true; + + ret = dwc3_core_probe(&probe_data); + if (ret) { + device_remove_software_node(dev); + return ret; + } + + device_set_wakeup_capable(dev, true); + return 0; +} + +static void dwc3_imx_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dwc3 *dwc = dev_get_drvdata(dev); + + dwc3_core_remove(dwc); + device_remove_software_node(dev); +} + +static void dwc3_imx_suspend(struct dwc3_imx *dwc_imx, pm_message_t msg) +{ + if (dwc_imx->pm_suspended) + return; + + if (PMSG_IS_AUTO(msg) || device_may_wakeup(dwc_imx->dev)) + dwc3_imx_wakeup_enable(dwc_imx, msg); + + enable_irq(dwc_imx->irq); + dwc_imx->pm_suspended = true; +} + +static void dwc3_imx_resume(struct dwc3_imx *dwc_imx, pm_message_t msg) +{ + struct dwc3 *dwc = &dwc_imx->dwc; + + if (!dwc_imx->pm_suspended) + return; + + dwc_imx->pm_suspended = false; + if (!dwc_imx->wakeup_pending) + disable_irq_nosync(dwc_imx->irq); + + dwc3_imx_wakeup_disable(dwc_imx); + + /* Upon power loss any previous configuration is lost, restore it */ + dwc3_imx_configure_glue(dwc_imx); + + if (dwc_imx->wakeup_pending) { + dwc_imx->wakeup_pending = false; + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) + pm_runtime_put_autosuspend(dwc->dev); + else + /* + * Add wait for xhci switch from suspend + * clock to normal clock to detect connection. + */ + usleep_range(9000, 10000); + } +} + +static int dwc3_imx_runtime_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + int ret; + + ret = dwc3_runtime_suspend(dwc); + if (ret) + return ret; + + dwc3_imx_suspend(dwc_imx, PMSG_AUTO_SUSPEND); + return 0; +} + +static int dwc3_imx_runtime_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + + dwc3_imx_resume(dwc_imx, PMSG_AUTO_RESUME); + return dwc3_runtime_resume(dwc); +} + +static int dwc3_imx_runtime_idle(struct device *dev) +{ + return dwc3_runtime_idle(dev_get_drvdata(dev)); +} + +static int dwc3_imx_pm_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + int ret; + + ret = dwc3_pm_suspend(dwc); + if (ret) + return ret; + + dwc3_imx_suspend(dwc_imx, PMSG_SUSPEND); + + if (device_may_wakeup(dev)) { + enable_irq_wake(dwc_imx->irq); + device_set_out_band_wakeup(dev); + } else { + clk_disable_unprepare(dwc_imx->suspend_clk); + } + + clk_disable_unprepare(dwc_imx->hsio_clk); + + return 0; +} + +static int dwc3_imx_pm_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_imx *dwc_imx = to_dwc3_imx(dwc); + int ret; + + if (device_may_wakeup(dwc_imx->dev)) { + disable_irq_wake(dwc_imx->irq); + } else { + ret = clk_prepare_enable(dwc_imx->suspend_clk); + if (ret) + return ret; + } + + ret = clk_prepare_enable(dwc_imx->hsio_clk); + if (ret) { + clk_disable_unprepare(dwc_imx->suspend_clk); + return ret; + } + + dwc3_imx_resume(dwc_imx, PMSG_RESUME); + + ret = dwc3_pm_resume(dwc); + if (ret) + return ret; + + return 0; +} + +static void dwc3_imx_complete(struct device *dev) +{ + dwc3_pm_complete(dev_get_drvdata(dev)); +} + +static int dwc3_imx_prepare(struct device *dev) +{ + return dwc3_pm_prepare(dev_get_drvdata(dev)); +} + +static const struct dev_pm_ops dwc3_imx_dev_pm_ops = { + SYSTEM_SLEEP_PM_OPS(dwc3_imx_pm_suspend, dwc3_imx_pm_resume) + RUNTIME_PM_OPS(dwc3_imx_runtime_suspend, dwc3_imx_runtime_resume, + dwc3_imx_runtime_idle) + .complete = pm_sleep_ptr(dwc3_imx_complete), + .prepare = pm_sleep_ptr(dwc3_imx_prepare), +}; + +static const struct of_device_id dwc3_imx_of_match[] = { + { .compatible = "nxp,imx8mp-dwc3", }, + {}, +}; +MODULE_DEVICE_TABLE(of, dwc3_imx_of_match); + +static struct platform_driver dwc3_imx_driver = { + .probe = dwc3_imx_probe, + .remove = dwc3_imx_remove, + .driver = { + .name = "imx-dwc3", + .pm = pm_ptr(&dwc3_imx_dev_pm_ops), + .of_match_table = dwc3_imx_of_match, + }, +}; + +module_platform_driver(dwc3_imx_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DesignWare USB3 i.MX Glue Layer"); diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 3edc5aca76f9..1cf96540b66e 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -51,7 +51,7 @@ struct dwc3_imx8mp { struct device *dev; - struct platform_device *dwc3; + struct platform_device *dwc3_pdev; void __iomem *hsio_blk_base; void __iomem *glue_base; struct clk *hsio_clk; @@ -100,7 +100,7 @@ static void imx8mp_configure_glue(struct dwc3_imx8mp *dwc3_imx) static void dwc3_imx8mp_wakeup_enable(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg) { - struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3); + struct dwc3 *dwc3 = platform_get_drvdata(dwc3_imx->dwc3_pdev); u32 val; if (!dwc3) @@ -142,7 +142,7 @@ static const struct software_node dwc3_imx8mp_swnode = { static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx) { struct dwc3_imx8mp *dwc3_imx = _dwc3_imx; - struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3); + struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3_pdev); if (!dwc3_imx->pm_suspended) return IRQ_HANDLED; @@ -158,11 +158,31 @@ static irqreturn_t dwc3_imx8mp_interrupt(int irq, void *_dwc3_imx) return IRQ_HANDLED; } +static void dwc3_imx_pre_set_role(struct dwc3 *dwc, enum usb_role role) +{ + if (role == USB_ROLE_HOST) + /* + * For xhci host, we need disable dwc core auto + * suspend, because during this auto suspend delay(5s), + * xhci host RUN_STOP is cleared and wakeup is not + * enabled, if device is inserted, xhci host can't + * response the connection. + */ + pm_runtime_dont_use_autosuspend(dwc->dev); + else + pm_runtime_use_autosuspend(dwc->dev); +} + +struct dwc3_glue_ops dwc3_imx_glue_ops = { + .pre_set_role = dwc3_imx_pre_set_role, +}; + static int dwc3_imx8mp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; struct dwc3_imx8mp *dwc3_imx; + struct dwc3 *dwc3; struct resource *res; int err, irq; @@ -233,18 +253,29 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) goto remove_swnode; } - dwc3_imx->dwc3 = of_find_device_by_node(dwc3_np); - if (!dwc3_imx->dwc3) { + dwc3_imx->dwc3_pdev = of_find_device_by_node(dwc3_np); + if (!dwc3_imx->dwc3_pdev) { dev_err(dev, "failed to get dwc3 platform device\n"); err = -ENODEV; goto depopulate; } + dwc3 = platform_get_drvdata(dwc3_imx->dwc3_pdev); + if (!dwc3) { + err = dev_err_probe(dev, -EPROBE_DEFER, "failed to get dwc3 platform data\n"); + goto put_dwc3; + } + + dwc3->glue_ops = &dwc3_imx_glue_ops; + + if (dwc3->dr_mode == USB_DR_MODE_HOST) + pm_runtime_dont_use_autosuspend(dwc3->dev); + err = devm_request_threaded_irq(dev, irq, NULL, dwc3_imx8mp_interrupt, IRQF_ONESHOT, dev_name(dev), dwc3_imx); if (err) { dev_err(dev, "failed to request IRQ #%d --> %d\n", irq, err); - goto depopulate; + goto put_dwc3; } device_set_wakeup_capable(dev, true); @@ -252,6 +283,8 @@ static int dwc3_imx8mp_probe(struct platform_device *pdev) return 0; +put_dwc3: + put_device(&dwc3_imx->dwc3_pdev->dev); depopulate: of_platform_depopulate(dev); remove_swnode: @@ -265,8 +298,11 @@ disable_rpm: static void dwc3_imx8mp_remove(struct platform_device *pdev) { + struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + put_device(&dwc3_imx->dwc3_pdev->dev); + pm_runtime_get_sync(dev); of_platform_depopulate(dev); device_remove_software_node(dev); @@ -291,7 +327,7 @@ static int dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg) static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg) { - struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3); + struct dwc3 *dwc = platform_get_drvdata(dwc3_imx->dwc3_pdev); int ret = 0; if (!dwc3_imx->pm_suspended) @@ -307,7 +343,6 @@ static int dwc3_imx8mp_resume(struct dwc3_imx8mp *dwc3_imx, pm_message_t msg) if (dwc3_imx->wakeup_pending) { dwc3_imx->wakeup_pending = false; if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) { - pm_runtime_mark_last_busy(dwc->dev); pm_runtime_put_autosuspend(dwc->dev); } else { /* @@ -329,10 +364,15 @@ static int dwc3_imx8mp_pm_suspend(struct device *dev) ret = dwc3_imx8mp_suspend(dwc3_imx, PMSG_SUSPEND); - if (device_may_wakeup(dwc3_imx->dev)) + if (device_may_wakeup(dwc3_imx->dev)) { enable_irq_wake(dwc3_imx->irq); - else + + if (device_is_compatible(dev, "fsl,imx95-dwc3")) + device_set_out_band_wakeup(dev); + + } else { clk_disable_unprepare(dwc3_imx->suspend_clk); + } clk_disable_unprepare(dwc3_imx->hsio_clk); dev_dbg(dev, "dwc3 imx8mp pm suspend.\n"); diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 7d80bf7b18b0..55e144ba8cfc 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -837,6 +837,9 @@ static void dwc3_meson_g12a_remove(struct platform_device *pdev) usb_role_switch_unregister(priv->role_switch); + put_device(priv->switch_desc.udc); + put_device(priv->switch_desc.usb2_port); + of_platform_depopulate(dev); for (i = 0 ; i < PHY_COUNT ; ++i) { diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index a4954a21be93..c116143335d9 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -70,11 +70,11 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) simple->num_clocks = ret; ret = clk_bulk_prepare_enable(simple->num_clocks, simple->clks); if (ret) - goto err_resetc_assert; + goto err_clk_put_all; ret = of_platform_populate(np, NULL, NULL, dev); if (ret) - goto err_clk_put; + goto err_clk_disable; pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -82,8 +82,9 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) return 0; -err_clk_put: +err_clk_disable: clk_bulk_disable_unprepare(simple->num_clocks, simple->clks); +err_clk_put_all: clk_bulk_put_all(simple->num_clocks, simple->clks); err_resetc_assert: diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 052852f80146..6c1cbb722ca8 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -21,39 +21,42 @@ #include <linux/acpi.h> #include <linux/delay.h> +#define PCI_DEVICE_ID_INTEL_CMLLP 0x02ee +#define PCI_DEVICE_ID_INTEL_CMLH 0x06ee +#define PCI_DEVICE_ID_INTEL_BXT 0x0aaa #define PCI_DEVICE_ID_INTEL_BYT 0x0f37 #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e -#define PCI_DEVICE_ID_INTEL_BSW 0x22b7 -#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30 -#define PCI_DEVICE_ID_INTEL_SPTH 0xa130 -#define PCI_DEVICE_ID_INTEL_BXT 0x0aaa #define PCI_DEVICE_ID_INTEL_BXT_M 0x1aaa -#define PCI_DEVICE_ID_INTEL_APL 0x5aaa -#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0 -#define PCI_DEVICE_ID_INTEL_CMLLP 0x02ee -#define PCI_DEVICE_ID_INTEL_CMLH 0x06ee +#define PCI_DEVICE_ID_INTEL_BSW 0x22b7 #define PCI_DEVICE_ID_INTEL_GLK 0x31aa -#define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee -#define PCI_DEVICE_ID_INTEL_CNPH 0xa36e -#define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0 #define PCI_DEVICE_ID_INTEL_ICLLP 0x34ee -#define PCI_DEVICE_ID_INTEL_EHL 0x4b7e -#define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee #define PCI_DEVICE_ID_INTEL_TGPH 0x43ee -#define PCI_DEVICE_ID_INTEL_JSP 0x4dee #define PCI_DEVICE_ID_INTEL_ADL 0x460e -#define PCI_DEVICE_ID_INTEL_ADL_PCH 0x51ee #define PCI_DEVICE_ID_INTEL_ADLN 0x465e +#define PCI_DEVICE_ID_INTEL_EHL 0x4b7e +#define PCI_DEVICE_ID_INTEL_WCL 0x4d7e +#define PCI_DEVICE_ID_INTEL_JSP 0x4dee +#define PCI_DEVICE_ID_INTEL_ADL_PCH 0x51ee #define PCI_DEVICE_ID_INTEL_ADLN_PCH 0x54ee -#define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1 -#define PCI_DEVICE_ID_INTEL_RPL 0xa70e +#define PCI_DEVICE_ID_INTEL_APL 0x5aaa +#define PCI_DEVICE_ID_INTEL_NVLS_PCH 0x6e6f +#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e #define PCI_DEVICE_ID_INTEL_RPLS 0x7a61 +#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e +#define PCI_DEVICE_ID_INTEL_ADLS 0x7ae1 #define PCI_DEVICE_ID_INTEL_MTLM 0x7eb1 #define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1 #define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f -#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e -#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e #define PCI_DEVICE_ID_INTEL_TGL 0x9a15 +#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30 +#define PCI_DEVICE_ID_INTEL_CNPLP 0x9dee +#define PCI_DEVICE_ID_INTEL_TGPLP 0xa0ee +#define PCI_DEVICE_ID_INTEL_SPTH 0xa130 +#define PCI_DEVICE_ID_INTEL_KBP 0xa2b0 +#define PCI_DEVICE_ID_INTEL_CNPH 0xa36e +#define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0 +#define PCI_DEVICE_ID_INTEL_RPL 0xa70e +#define PCI_DEVICE_ID_INTEL_NVLH 0xd37f #define PCI_DEVICE_ID_INTEL_PTLH 0xe332 #define PCI_DEVICE_ID_INTEL_PTLH_PCH 0xe37e #define PCI_DEVICE_ID_INTEL_PTLU 0xe432 @@ -148,11 +151,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"), {} @@ -311,7 +324,6 @@ static void dwc3_pci_resume_work(struct work_struct *work) return; } - pm_runtime_mark_last_busy(&dwc3->dev); pm_runtime_put_sync_autosuspend(&dwc3->dev); } #endif @@ -401,39 +413,42 @@ static void dwc3_pci_remove(struct pci_dev *pci) } static const struct pci_device_id dwc3_pci_id_table[] = { - { PCI_DEVICE_DATA(INTEL, BSW, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, BYT, &dwc3_pci_intel_byt_swnode) }, - { PCI_DEVICE_DATA(INTEL, MRFLD, &dwc3_pci_intel_mrfld_swnode) }, { PCI_DEVICE_DATA(INTEL, CMLLP, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, CMLH, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, SPTLP, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, SPTH, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, BXT, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, BYT, &dwc3_pci_intel_byt_swnode) }, + { PCI_DEVICE_DATA(INTEL, MRFLD, &dwc3_pci_intel_mrfld_swnode) }, { PCI_DEVICE_DATA(INTEL, BXT_M, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, APL, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, KBP, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, BSW, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, GLK, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, CNPLP, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, CNPH, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, CNPV, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, ICLLP, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, EHL, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, TGPLP, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, TGPH, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, JSP, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, ADL, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, ADL_PCH, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, ADLN, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, EHL, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, WCL, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, JSP, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, ADL_PCH, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, ADLN_PCH, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, ADLS, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, RPL, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, APL, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, NVLS_PCH, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, RPLS, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, ADLS, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, MTLM, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, MTLP, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) }, - { PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, SPTLP, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, CNPLP, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, TGPLP, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, SPTH, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, KBP, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, CNPH, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, CNPV, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, RPL, &dwc3_pci_intel_swnode) }, + { PCI_DEVICE_DATA(INTEL, NVLH, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, PTLH, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, PTLH_PCH, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, PTLU, &dwc3_pci_intel_swnode) }, diff --git a/drivers/usb/dwc3/dwc3-qcom-legacy.c b/drivers/usb/dwc3/dwc3-qcom-legacy.c new file mode 100644 index 000000000000..d3fad0fcfdac --- /dev/null +++ b/drivers/usb/dwc3/dwc3-qcom-legacy.c @@ -0,0 +1,935 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * Inspired by dwc3-of-simple.c + */ + +#include <linux/cleanup.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/clk.h> +#include <linux/irq.h> +#include <linux/of_clk.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/extcon.h> +#include <linux/interconnect.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> +#include <linux/usb/of.h> +#include <linux/reset.h> +#include <linux/iopoll.h> +#include <linux/usb/hcd.h> +#include <linux/usb.h> +#include "core.h" + +/* USB QSCRATCH Hardware registers */ +#define QSCRATCH_HS_PHY_CTRL 0x10 +#define UTMI_OTG_VBUS_VALID BIT(20) +#define SW_SESSVLD_SEL BIT(28) + +#define QSCRATCH_SS_PHY_CTRL 0x30 +#define LANE0_PWR_PRESENT BIT(24) + +#define QSCRATCH_GENERAL_CFG 0x08 +#define PIPE_UTMI_CLK_SEL BIT(0) +#define PIPE3_PHYSTATUS_SW BIT(3) +#define PIPE_UTMI_CLK_DIS BIT(8) + +#define PWR_EVNT_LPM_IN_L2_MASK BIT(4) +#define PWR_EVNT_LPM_OUT_L2_MASK BIT(5) + +#define SDM845_QSCRATCH_BASE_OFFSET 0xf8800 +#define SDM845_QSCRATCH_SIZE 0x400 +#define SDM845_DWC3_CORE_SIZE 0xcd00 + +/* Interconnect path bandwidths in MBps */ +#define USB_MEMORY_AVG_HS_BW MBps_to_icc(240) +#define USB_MEMORY_PEAK_HS_BW MBps_to_icc(700) +#define USB_MEMORY_AVG_SS_BW MBps_to_icc(1000) +#define USB_MEMORY_PEAK_SS_BW MBps_to_icc(2500) +#define APPS_USB_AVG_BW 0 +#define APPS_USB_PEAK_BW MBps_to_icc(40) + +/* Qualcomm SoCs with multiport support has up to 4 ports */ +#define DWC3_QCOM_MAX_PORTS 4 + +static const u32 pwr_evnt_irq_stat_reg[DWC3_QCOM_MAX_PORTS] = { + 0x58, + 0x1dc, + 0x228, + 0x238, +}; + +struct dwc3_qcom_port { + int qusb2_phy_irq; + int dp_hs_phy_irq; + int dm_hs_phy_irq; + int ss_phy_irq; + enum usb_device_speed usb2_speed; +}; + +struct dwc3_qcom { + struct device *dev; + void __iomem *qscratch_base; + struct platform_device *dwc3; + struct clk **clks; + int num_clocks; + struct reset_control *resets; + struct dwc3_qcom_port ports[DWC3_QCOM_MAX_PORTS]; + u8 num_ports; + + struct extcon_dev *edev; + struct extcon_dev *host_edev; + struct notifier_block vbus_nb; + struct notifier_block host_nb; + + enum usb_dr_mode mode; + bool is_suspended; + bool pm_suspended; + struct icc_path *icc_path_ddr; + struct icc_path *icc_path_apps; +}; + +static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg |= val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static inline void dwc3_qcom_clrbits(void __iomem *base, u32 offset, u32 val) +{ + u32 reg; + + reg = readl(base + offset); + reg &= ~val; + writel(reg, base + offset); + + /* ensure that above write is through */ + readl(base + offset); +} + +static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable) +{ + if (enable) { + dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL, + LANE0_PWR_PRESENT); + dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_HS_PHY_CTRL, + UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL); + } else { + dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL, + LANE0_PWR_PRESENT); + dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_HS_PHY_CTRL, + UTMI_OTG_VBUS_VALID | SW_SESSVLD_SEL); + } +} + +static int dwc3_qcom_vbus_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, vbus_nb); + + /* enable vbus override for device mode */ + dwc3_qcom_vbus_override_enable(qcom, event); + qcom->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST; + + return NOTIFY_DONE; +} + +static int dwc3_qcom_host_notifier(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, host_nb); + + /* disable vbus override in host mode */ + dwc3_qcom_vbus_override_enable(qcom, !event); + qcom->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL; + + return NOTIFY_DONE; +} + +static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom) +{ + struct device *dev = qcom->dev; + struct extcon_dev *host_edev; + int ret; + + if (!of_property_present(dev->of_node, "extcon")) + return 0; + + qcom->edev = extcon_get_edev_by_phandle(dev, 0); + if (IS_ERR(qcom->edev)) + return dev_err_probe(dev, PTR_ERR(qcom->edev), + "Failed to get extcon\n"); + + qcom->vbus_nb.notifier_call = dwc3_qcom_vbus_notifier; + + qcom->host_edev = extcon_get_edev_by_phandle(dev, 1); + if (IS_ERR(qcom->host_edev)) + qcom->host_edev = NULL; + + ret = devm_extcon_register_notifier(dev, qcom->edev, EXTCON_USB, + &qcom->vbus_nb); + if (ret < 0) { + dev_err(dev, "VBUS notifier register failed\n"); + return ret; + } + + if (qcom->host_edev) + host_edev = qcom->host_edev; + else + host_edev = qcom->edev; + + qcom->host_nb.notifier_call = dwc3_qcom_host_notifier; + ret = devm_extcon_register_notifier(dev, host_edev, EXTCON_USB_HOST, + &qcom->host_nb); + if (ret < 0) { + dev_err(dev, "Host notifier register failed\n"); + return ret; + } + + /* Update initial VBUS override based on extcon state */ + if (extcon_get_state(qcom->edev, EXTCON_USB) || + !extcon_get_state(host_edev, EXTCON_USB_HOST)) + dwc3_qcom_vbus_notifier(&qcom->vbus_nb, true, qcom->edev); + else + dwc3_qcom_vbus_notifier(&qcom->vbus_nb, false, qcom->edev); + + return 0; +} + +static int dwc3_qcom_interconnect_enable(struct dwc3_qcom *qcom) +{ + int ret; + + ret = icc_enable(qcom->icc_path_ddr); + if (ret) + return ret; + + ret = icc_enable(qcom->icc_path_apps); + if (ret) + icc_disable(qcom->icc_path_ddr); + + return ret; +} + +static int dwc3_qcom_interconnect_disable(struct dwc3_qcom *qcom) +{ + int ret; + + ret = icc_disable(qcom->icc_path_ddr); + if (ret) + return ret; + + ret = icc_disable(qcom->icc_path_apps); + if (ret) + icc_enable(qcom->icc_path_ddr); + + return ret; +} + +/** + * dwc3_qcom_interconnect_init() - Get interconnect path handles + * and set bandwidth. + * @qcom: Pointer to the concerned usb core. + * + */ +static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom) +{ + enum usb_device_speed max_speed; + struct device *dev = qcom->dev; + int ret; + + qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr"); + if (IS_ERR(qcom->icc_path_ddr)) { + return dev_err_probe(dev, PTR_ERR(qcom->icc_path_ddr), + "failed to get usb-ddr path\n"); + } + + qcom->icc_path_apps = of_icc_get(dev, "apps-usb"); + if (IS_ERR(qcom->icc_path_apps)) { + ret = dev_err_probe(dev, PTR_ERR(qcom->icc_path_apps), + "failed to get apps-usb path\n"); + goto put_path_ddr; + } + + max_speed = usb_get_maximum_speed(&qcom->dwc3->dev); + if (max_speed >= USB_SPEED_SUPER || max_speed == USB_SPEED_UNKNOWN) { + ret = icc_set_bw(qcom->icc_path_ddr, + USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW); + } else { + ret = icc_set_bw(qcom->icc_path_ddr, + USB_MEMORY_AVG_HS_BW, USB_MEMORY_PEAK_HS_BW); + } + if (ret) { + dev_err(dev, "failed to set bandwidth for usb-ddr path: %d\n", ret); + goto put_path_apps; + } + + ret = icc_set_bw(qcom->icc_path_apps, APPS_USB_AVG_BW, APPS_USB_PEAK_BW); + if (ret) { + dev_err(dev, "failed to set bandwidth for apps-usb path: %d\n", ret); + goto put_path_apps; + } + + return 0; + +put_path_apps: + icc_put(qcom->icc_path_apps); +put_path_ddr: + icc_put(qcom->icc_path_ddr); + return ret; +} + +/** + * dwc3_qcom_interconnect_exit() - Release interconnect path handles + * @qcom: Pointer to the concerned usb core. + * + * This function is used to release interconnect path handle. + */ +static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom) +{ + icc_put(qcom->icc_path_ddr); + icc_put(qcom->icc_path_apps); +} + +/* Only usable in contexts where the role can not change. */ +static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom) +{ + struct dwc3 *dwc; + + /* + * FIXME: Fix this layering violation. + */ + dwc = platform_get_drvdata(qcom->dwc3); + + /* Core driver may not have probed yet. */ + if (!dwc) + return false; + + return dwc->xhci; +} + +static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom, int port_index) +{ + struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3); + struct usb_device *udev; + struct usb_hcd __maybe_unused *hcd; + + /* + * FIXME: Fix this layering violation. + */ + hcd = platform_get_drvdata(dwc->xhci); + +#ifdef CONFIG_USB + udev = usb_hub_find_child(hcd->self.root_hub, port_index + 1); +#else + udev = NULL; +#endif + if (!udev) + return USB_SPEED_UNKNOWN; + + return udev->speed; +} + +static void dwc3_qcom_enable_wakeup_irq(int irq, unsigned int polarity) +{ + if (!irq) + return; + + if (polarity) + irq_set_irq_type(irq, polarity); + + enable_irq(irq); + enable_irq_wake(irq); +} + +static void dwc3_qcom_disable_wakeup_irq(int irq) +{ + if (!irq) + return; + + disable_irq_wake(irq); + disable_irq_nosync(irq); +} + +static void dwc3_qcom_disable_port_interrupts(struct dwc3_qcom_port *port) +{ + dwc3_qcom_disable_wakeup_irq(port->qusb2_phy_irq); + + if (port->usb2_speed == USB_SPEED_LOW) { + dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq); + } else if ((port->usb2_speed == USB_SPEED_HIGH) || + (port->usb2_speed == USB_SPEED_FULL)) { + dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq); + } else { + dwc3_qcom_disable_wakeup_irq(port->dp_hs_phy_irq); + dwc3_qcom_disable_wakeup_irq(port->dm_hs_phy_irq); + } + + dwc3_qcom_disable_wakeup_irq(port->ss_phy_irq); +} + +static void dwc3_qcom_enable_port_interrupts(struct dwc3_qcom_port *port) +{ + dwc3_qcom_enable_wakeup_irq(port->qusb2_phy_irq, 0); + + /* + * Configure DP/DM line interrupts based on the USB2 device attached to + * the root hub port. When HS/FS device is connected, configure the DP line + * as falling edge to detect both disconnect and remote wakeup scenarios. When + * LS device is connected, configure DM line as falling edge to detect both + * disconnect and remote wakeup. When no device is connected, configure both + * DP and DM lines as rising edge to detect HS/HS/LS device connect scenario. + */ + + if (port->usb2_speed == USB_SPEED_LOW) { + dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq, + IRQ_TYPE_EDGE_FALLING); + } else if ((port->usb2_speed == USB_SPEED_HIGH) || + (port->usb2_speed == USB_SPEED_FULL)) { + dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq, + IRQ_TYPE_EDGE_FALLING); + } else { + dwc3_qcom_enable_wakeup_irq(port->dp_hs_phy_irq, + IRQ_TYPE_EDGE_RISING); + dwc3_qcom_enable_wakeup_irq(port->dm_hs_phy_irq, + IRQ_TYPE_EDGE_RISING); + } + + dwc3_qcom_enable_wakeup_irq(port->ss_phy_irq, 0); +} + +static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) +{ + int i; + + for (i = 0; i < qcom->num_ports; i++) + dwc3_qcom_disable_port_interrupts(&qcom->ports[i]); +} + +static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom) +{ + int i; + + for (i = 0; i < qcom->num_ports; i++) + dwc3_qcom_enable_port_interrupts(&qcom->ports[i]); +} + +static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup) +{ + u32 val; + int i, ret; + + if (qcom->is_suspended) + return 0; + + for (i = 0; i < qcom->num_ports; i++) { + val = readl(qcom->qscratch_base + pwr_evnt_irq_stat_reg[i]); + if (!(val & PWR_EVNT_LPM_IN_L2_MASK)) + dev_err(qcom->dev, "port-%d HS-PHY not in L2\n", i + 1); + } + + for (i = qcom->num_clocks - 1; i >= 0; i--) + clk_disable_unprepare(qcom->clks[i]); + + ret = dwc3_qcom_interconnect_disable(qcom); + if (ret) + dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret); + + /* + * The role is stable during suspend as role switching is done from a + * freezable workqueue. + */ + if (dwc3_qcom_is_host(qcom) && wakeup) { + for (i = 0; i < qcom->num_ports; i++) + qcom->ports[i].usb2_speed = dwc3_qcom_read_usb2_speed(qcom, i); + dwc3_qcom_enable_interrupts(qcom); + } + + qcom->is_suspended = true; + + return 0; +} + +static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup) +{ + int ret; + int i; + + if (!qcom->is_suspended) + return 0; + + if (dwc3_qcom_is_host(qcom) && wakeup) + dwc3_qcom_disable_interrupts(qcom); + + for (i = 0; i < qcom->num_clocks; i++) { + ret = clk_prepare_enable(qcom->clks[i]); + if (ret < 0) { + while (--i >= 0) + clk_disable_unprepare(qcom->clks[i]); + return ret; + } + } + + ret = dwc3_qcom_interconnect_enable(qcom); + if (ret) + dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret); + + /* Clear existing events from PHY related to L2 in/out */ + for (i = 0; i < qcom->num_ports; i++) { + dwc3_qcom_setbits(qcom->qscratch_base, + pwr_evnt_irq_stat_reg[i], + PWR_EVNT_LPM_IN_L2_MASK | PWR_EVNT_LPM_OUT_L2_MASK); + } + + qcom->is_suspended = false; + + return 0; +} + +static irqreturn_t qcom_dwc3_resume_irq(int irq, void *data) +{ + struct dwc3_qcom *qcom = data; + struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3); + + /* If pm_suspended then let pm_resume take care of resuming h/w */ + if (qcom->pm_suspended) + return IRQ_HANDLED; + + /* + * This is safe as role switching is done from a freezable workqueue + * and the wakeup interrupts are disabled as part of resume. + */ + if (dwc3_qcom_is_host(qcom)) + pm_runtime_resume(&dwc->xhci->dev); + + return IRQ_HANDLED; +} + +static void dwc3_qcom_select_utmi_clk(struct dwc3_qcom *qcom) +{ + /* Configure dwc3 to use UTMI clock as PIPE clock not present */ + dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG, + PIPE_UTMI_CLK_DIS); + + usleep_range(100, 1000); + + dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG, + PIPE_UTMI_CLK_SEL | PIPE3_PHYSTATUS_SW); + + usleep_range(100, 1000); + + dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG, + PIPE_UTMI_CLK_DIS); +} + +static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq, + const char *name) +{ + int ret; + + /* Keep wakeup interrupts disabled until suspend */ + ret = devm_request_threaded_irq(qcom->dev, irq, NULL, + qcom_dwc3_resume_irq, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + name, qcom); + if (ret) + dev_err(qcom->dev, "failed to request irq %s: %d\n", name, ret); + + return ret; +} + +static int dwc3_qcom_setup_port_irq(struct platform_device *pdev, int port_index, bool is_multiport) +{ + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); + const char *irq_name; + int irq; + int ret; + + if (is_multiport) + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_%d", port_index + 1); + else + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dp_hs_phy_irq"); + if (!irq_name) + return -ENOMEM; + + irq = platform_get_irq_byname_optional(pdev, irq_name); + if (irq > 0) { + ret = dwc3_qcom_request_irq(qcom, irq, irq_name); + if (ret) + return ret; + qcom->ports[port_index].dp_hs_phy_irq = irq; + } + + if (is_multiport) + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_%d", port_index + 1); + else + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "dm_hs_phy_irq"); + if (!irq_name) + return -ENOMEM; + + irq = platform_get_irq_byname_optional(pdev, irq_name); + if (irq > 0) { + ret = dwc3_qcom_request_irq(qcom, irq, irq_name); + if (ret) + return ret; + qcom->ports[port_index].dm_hs_phy_irq = irq; + } + + if (is_multiport) + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_%d", port_index + 1); + else + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "ss_phy_irq"); + if (!irq_name) + return -ENOMEM; + + irq = platform_get_irq_byname_optional(pdev, irq_name); + if (irq > 0) { + ret = dwc3_qcom_request_irq(qcom, irq, irq_name); + if (ret) + return ret; + qcom->ports[port_index].ss_phy_irq = irq; + } + + if (is_multiport) + return 0; + + irq = platform_get_irq_byname_optional(pdev, "qusb2_phy"); + if (irq > 0) { + ret = dwc3_qcom_request_irq(qcom, irq, "qusb2_phy"); + if (ret) + return ret; + qcom->ports[port_index].qusb2_phy_irq = irq; + } + + return 0; +} + +static int dwc3_qcom_find_num_ports(struct platform_device *pdev) +{ + char irq_name[14]; + int port_num; + int irq; + + irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1"); + if (irq <= 0) + return 1; + + for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) { + sprintf(irq_name, "dp_hs_phy_%d", port_num); + + irq = platform_get_irq_byname_optional(pdev, irq_name); + if (irq <= 0) + return port_num - 1; + } + + return DWC3_QCOM_MAX_PORTS; +} + +static int dwc3_qcom_setup_irq(struct platform_device *pdev) +{ + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); + bool is_multiport; + int ret; + int i; + + qcom->num_ports = dwc3_qcom_find_num_ports(pdev); + is_multiport = (qcom->num_ports > 1); + + for (i = 0; i < qcom->num_ports; i++) { + ret = dwc3_qcom_setup_port_irq(pdev, i, is_multiport); + if (ret) + return ret; + } + + return 0; +} + +static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count) +{ + struct device *dev = qcom->dev; + struct device_node *np = dev->of_node; + int i; + + if (!np || !count) + return 0; + + if (count < 0) + return count; + + qcom->num_clocks = count; + + qcom->clks = devm_kcalloc(dev, qcom->num_clocks, + sizeof(struct clk *), GFP_KERNEL); + if (!qcom->clks) + return -ENOMEM; + + for (i = 0; i < qcom->num_clocks; i++) { + struct clk *clk; + int ret; + + clk = of_clk_get(np, i); + if (IS_ERR(clk)) { + while (--i >= 0) + clk_put(qcom->clks[i]); + return PTR_ERR(clk); + } + + ret = clk_prepare_enable(clk); + if (ret < 0) { + while (--i >= 0) { + clk_disable_unprepare(qcom->clks[i]); + clk_put(qcom->clks[i]); + } + clk_put(clk); + + return ret; + } + + qcom->clks[i] = clk; + } + + return 0; +} + +static int dwc3_qcom_of_register_core(struct platform_device *pdev) +{ + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + int ret; + + struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np, + "snps,dwc3"); + if (!dwc3_np) { + dev_err(dev, "failed to find dwc3 core child\n"); + return -ENODEV; + } + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + dev_err(dev, "failed to register dwc3 core - %d\n", ret); + return ret; + } + + qcom->dwc3 = of_find_device_by_node(dwc3_np); + if (!qcom->dwc3) { + ret = -ENODEV; + dev_err(dev, "failed to get dwc3 platform device\n"); + of_platform_depopulate(dev); + } + + return ret; +} + +static int dwc3_qcom_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct dwc3_qcom *qcom; + int ret, i; + bool ignore_pipe_clk; + bool wakeup_source; + + qcom = devm_kzalloc(&pdev->dev, sizeof(*qcom), GFP_KERNEL); + if (!qcom) + return -ENOMEM; + + platform_set_drvdata(pdev, qcom); + qcom->dev = &pdev->dev; + + qcom->resets = devm_reset_control_array_get_optional_exclusive(dev); + if (IS_ERR(qcom->resets)) { + return dev_err_probe(&pdev->dev, PTR_ERR(qcom->resets), + "failed to get resets\n"); + } + + ret = reset_control_assert(qcom->resets); + if (ret) { + dev_err(&pdev->dev, "failed to assert resets, err=%d\n", ret); + return ret; + } + + usleep_range(10, 1000); + + ret = reset_control_deassert(qcom->resets); + if (ret) { + dev_err(&pdev->dev, "failed to deassert resets, err=%d\n", ret); + goto reset_assert; + } + + ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np)); + if (ret) { + dev_err_probe(dev, ret, "failed to get clocks\n"); + goto reset_assert; + } + + qcom->qscratch_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(qcom->qscratch_base)) { + ret = PTR_ERR(qcom->qscratch_base); + goto clk_disable; + } + + ret = dwc3_qcom_setup_irq(pdev); + if (ret) { + dev_err(dev, "failed to setup IRQs, err=%d\n", ret); + goto clk_disable; + } + + /* + * Disable pipe_clk requirement if specified. Used when dwc3 + * operates without SSPHY and only HS/FS/LS modes are supported. + */ + ignore_pipe_clk = device_property_read_bool(dev, + "qcom,select-utmi-as-pipe-clk"); + if (ignore_pipe_clk) + dwc3_qcom_select_utmi_clk(qcom); + + ret = dwc3_qcom_of_register_core(pdev); + if (ret) { + dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret); + goto clk_disable; + } + + ret = dwc3_qcom_interconnect_init(qcom); + if (ret) + goto depopulate; + + qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev); + + /* enable vbus override for device mode */ + if (qcom->mode != USB_DR_MODE_HOST) + dwc3_qcom_vbus_override_enable(qcom, true); + + /* register extcon to override sw_vbus on Vbus change later */ + ret = dwc3_qcom_register_extcon(qcom); + if (ret) + goto interconnect_exit; + + wakeup_source = of_property_read_bool(dev->of_node, "wakeup-source"); + device_init_wakeup(&pdev->dev, wakeup_source); + device_init_wakeup(&qcom->dwc3->dev, wakeup_source); + + qcom->is_suspended = false; + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_forbid(dev); + + return 0; + +interconnect_exit: + dwc3_qcom_interconnect_exit(qcom); +depopulate: + of_platform_depopulate(&pdev->dev); + platform_device_put(qcom->dwc3); +clk_disable: + for (i = qcom->num_clocks - 1; i >= 0; i--) { + clk_disable_unprepare(qcom->clks[i]); + clk_put(qcom->clks[i]); + } +reset_assert: + reset_control_assert(qcom->resets); + + return ret; +} + +static void dwc3_qcom_remove(struct platform_device *pdev) +{ + struct dwc3_qcom *qcom = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int i; + + of_platform_depopulate(&pdev->dev); + platform_device_put(qcom->dwc3); + + for (i = qcom->num_clocks - 1; i >= 0; i--) { + clk_disable_unprepare(qcom->clks[i]); + clk_put(qcom->clks[i]); + } + qcom->num_clocks = 0; + + dwc3_qcom_interconnect_exit(qcom); + reset_control_assert(qcom->resets); + + pm_runtime_allow(dev); + pm_runtime_disable(dev); +} + +static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev) +{ + struct dwc3_qcom *qcom = dev_get_drvdata(dev); + bool wakeup = device_may_wakeup(dev); + int ret; + + ret = dwc3_qcom_suspend(qcom, wakeup); + if (ret) + return ret; + + qcom->pm_suspended = true; + + return 0; +} + +static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev) +{ + struct dwc3_qcom *qcom = dev_get_drvdata(dev); + bool wakeup = device_may_wakeup(dev); + int ret; + + ret = dwc3_qcom_resume(qcom, wakeup); + if (ret) + return ret; + + qcom->pm_suspended = false; + + return 0; +} + +static int __maybe_unused dwc3_qcom_runtime_suspend(struct device *dev) +{ + struct dwc3_qcom *qcom = dev_get_drvdata(dev); + + return dwc3_qcom_suspend(qcom, true); +} + +static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev) +{ + struct dwc3_qcom *qcom = dev_get_drvdata(dev); + + return dwc3_qcom_resume(qcom, true); +} + +static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_qcom_pm_suspend, dwc3_qcom_pm_resume) + SET_RUNTIME_PM_OPS(dwc3_qcom_runtime_suspend, dwc3_qcom_runtime_resume, + NULL) +}; + +static const struct of_device_id dwc3_qcom_of_match[] = { + { .compatible = "qcom,dwc3" }, + { } +}; +MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match); + +static struct platform_driver dwc3_qcom_driver = { + .probe = dwc3_qcom_probe, + .remove = dwc3_qcom_remove, + .driver = { + .name = "dwc3-qcom-legacy", + .pm = &dwc3_qcom_dev_pm_ops, + .of_match_table = dwc3_qcom_of_match, + }, +}; + +module_platform_driver(dwc3_qcom_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DesignWare DWC3 QCOM legacy glue Driver"); diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 58683bb672e9..f43f73ac36ff 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -4,7 +4,6 @@ * Inspired by dwc3-of-simple.c */ -#include <linux/cleanup.h> #include <linux/io.h> #include <linux/of.h> #include <linux/clk.h> @@ -12,9 +11,7 @@ #include <linux/of_clk.h> #include <linux/module.h> #include <linux/kernel.h> -#include <linux/extcon.h> #include <linux/interconnect.h> -#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> #include <linux/usb/of.h> @@ -23,6 +20,7 @@ #include <linux/usb/hcd.h> #include <linux/usb.h> #include "core.h" +#include "glue.h" /* USB QSCRATCH Hardware registers */ #define QSCRATCH_HS_PHY_CTRL 0x10 @@ -73,25 +71,24 @@ struct dwc3_qcom_port { struct dwc3_qcom { struct device *dev; void __iomem *qscratch_base; - struct platform_device *dwc3; - struct clk **clks; + struct dwc3 dwc; + struct clk_bulk_data *clks; int num_clocks; struct reset_control *resets; struct dwc3_qcom_port ports[DWC3_QCOM_MAX_PORTS]; u8 num_ports; - struct extcon_dev *edev; - struct extcon_dev *host_edev; - struct notifier_block vbus_nb; - struct notifier_block host_nb; - enum usb_dr_mode mode; bool is_suspended; bool pm_suspended; struct icc_path *icc_path_ddr; struct icc_path *icc_path_apps; + + enum usb_role current_role; }; +#define to_dwc3_qcom(d) container_of((d), struct dwc3_qcom, dwc) + static inline void dwc3_qcom_setbits(void __iomem *base, u32 offset, u32 val) { u32 reg; @@ -131,80 +128,6 @@ static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable) } } -static int dwc3_qcom_vbus_notifier(struct notifier_block *nb, - unsigned long event, void *ptr) -{ - struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, vbus_nb); - - /* enable vbus override for device mode */ - dwc3_qcom_vbus_override_enable(qcom, event); - qcom->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST; - - return NOTIFY_DONE; -} - -static int dwc3_qcom_host_notifier(struct notifier_block *nb, - unsigned long event, void *ptr) -{ - struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, host_nb); - - /* disable vbus override in host mode */ - dwc3_qcom_vbus_override_enable(qcom, !event); - qcom->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL; - - return NOTIFY_DONE; -} - -static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom) -{ - struct device *dev = qcom->dev; - struct extcon_dev *host_edev; - int ret; - - if (!of_property_present(dev->of_node, "extcon")) - return 0; - - qcom->edev = extcon_get_edev_by_phandle(dev, 0); - if (IS_ERR(qcom->edev)) - return dev_err_probe(dev, PTR_ERR(qcom->edev), - "Failed to get extcon\n"); - - qcom->vbus_nb.notifier_call = dwc3_qcom_vbus_notifier; - - qcom->host_edev = extcon_get_edev_by_phandle(dev, 1); - if (IS_ERR(qcom->host_edev)) - qcom->host_edev = NULL; - - ret = devm_extcon_register_notifier(dev, qcom->edev, EXTCON_USB, - &qcom->vbus_nb); - if (ret < 0) { - dev_err(dev, "VBUS notifier register failed\n"); - return ret; - } - - if (qcom->host_edev) - host_edev = qcom->host_edev; - else - host_edev = qcom->edev; - - qcom->host_nb.notifier_call = dwc3_qcom_host_notifier; - ret = devm_extcon_register_notifier(dev, host_edev, EXTCON_USB_HOST, - &qcom->host_nb); - if (ret < 0) { - dev_err(dev, "Host notifier register failed\n"); - return ret; - } - - /* Update initial VBUS override based on extcon state */ - if (extcon_get_state(qcom->edev, EXTCON_USB) || - !extcon_get_state(host_edev, EXTCON_USB_HOST)) - dwc3_qcom_vbus_notifier(&qcom->vbus_nb, true, qcom->edev); - else - dwc3_qcom_vbus_notifier(&qcom->vbus_nb, false, qcom->edev); - - return 0; -} - static int dwc3_qcom_interconnect_enable(struct dwc3_qcom *qcom) { int ret; @@ -260,7 +183,7 @@ static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom) goto put_path_ddr; } - max_speed = usb_get_maximum_speed(&qcom->dwc3->dev); + max_speed = usb_get_maximum_speed(qcom->dwc.dev); if (max_speed >= USB_SPEED_SUPER || max_speed == USB_SPEED_UNKNOWN) { ret = icc_set_bw(qcom->icc_path_ddr, USB_MEMORY_AVG_SS_BW, USB_MEMORY_PEAK_SS_BW); @@ -303,25 +226,14 @@ static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom) /* Only usable in contexts where the role can not change. */ static bool dwc3_qcom_is_host(struct dwc3_qcom *qcom) { - struct dwc3 *dwc; - - /* - * FIXME: Fix this layering violation. - */ - dwc = platform_get_drvdata(qcom->dwc3); - - /* Core driver may not have probed yet. */ - if (!dwc) - return false; - - return dwc->xhci; + return qcom->dwc.xhci; } static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom, int port_index) { - struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3); struct usb_device *udev; struct usb_hcd __maybe_unused *hcd; + struct dwc3 *dwc = &qcom->dwc; /* * FIXME: Fix this layering violation. @@ -436,9 +348,7 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup) if (!(val & PWR_EVNT_LPM_IN_L2_MASK)) dev_err(qcom->dev, "port-%d HS-PHY not in L2\n", i + 1); } - - for (i = qcom->num_clocks - 1; i >= 0; i--) - clk_disable_unprepare(qcom->clks[i]); + clk_bulk_disable_unprepare(qcom->num_clocks, qcom->clks); ret = dwc3_qcom_interconnect_disable(qcom); if (ret) @@ -470,14 +380,9 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup) if (dwc3_qcom_is_host(qcom) && wakeup) dwc3_qcom_disable_interrupts(qcom); - for (i = 0; i < qcom->num_clocks; i++) { - ret = clk_prepare_enable(qcom->clks[i]); - if (ret < 0) { - while (--i >= 0) - clk_disable_unprepare(qcom->clks[i]); - return ret; - } - } + ret = clk_bulk_prepare_enable(qcom->num_clocks, qcom->clks); + if (ret < 0) + return ret; ret = dwc3_qcom_interconnect_enable(qcom); if (ret) @@ -498,7 +403,7 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup) static irqreturn_t qcom_dwc3_resume_irq(int irq, void *data) { struct dwc3_qcom *qcom = data; - struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3); + struct dwc3 *dwc = &qcom->dwc; /* If pm_suspended then let pm_resume take care of resuming h/w */ if (qcom->pm_suspended) @@ -547,9 +452,10 @@ static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq, return ret; } -static int dwc3_qcom_setup_port_irq(struct platform_device *pdev, int port_index, bool is_multiport) +static int dwc3_qcom_setup_port_irq(struct dwc3_qcom *qcom, + struct platform_device *pdev, + int port_index, bool is_multiport) { - struct dwc3_qcom *qcom = platform_get_drvdata(pdev); const char *irq_name; int irq; int ret; @@ -620,23 +526,22 @@ static int dwc3_qcom_find_num_ports(struct platform_device *pdev) int irq; irq = platform_get_irq_byname_optional(pdev, "dp_hs_phy_1"); - if (irq <= 0) + if (irq < 0) return 1; for (port_num = 2; port_num <= DWC3_QCOM_MAX_PORTS; port_num++) { sprintf(irq_name, "dp_hs_phy_%d", port_num); irq = platform_get_irq_byname_optional(pdev, irq_name); - if (irq <= 0) + if (irq < 0) return port_num - 1; } return DWC3_QCOM_MAX_PORTS; } -static int dwc3_qcom_setup_irq(struct platform_device *pdev) +static int dwc3_qcom_setup_irq(struct dwc3_qcom *qcom, struct platform_device *pdev) { - struct dwc3_qcom *qcom = platform_get_drvdata(pdev); bool is_multiport; int ret; int i; @@ -645,7 +550,7 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) is_multiport = (qcom->num_ports > 1); for (i = 0; i < qcom->num_ports; i++) { - ret = dwc3_qcom_setup_port_irq(pdev, i, is_multiport); + ret = dwc3_qcom_setup_port_irq(qcom, pdev, i, is_multiport); if (ret) return ret; } @@ -653,89 +558,63 @@ static int dwc3_qcom_setup_irq(struct platform_device *pdev) return 0; } -static int dwc3_qcom_clk_init(struct dwc3_qcom *qcom, int count) +static void dwc3_qcom_set_role_notifier(struct dwc3 *dwc, enum usb_role next_role) { - struct device *dev = qcom->dev; - struct device_node *np = dev->of_node; - int i; - - if (!np || !count) - return 0; - - if (count < 0) - return count; + struct dwc3_qcom *qcom = to_dwc3_qcom(dwc); - qcom->num_clocks = count; - - qcom->clks = devm_kcalloc(dev, qcom->num_clocks, - sizeof(struct clk *), GFP_KERNEL); - if (!qcom->clks) - return -ENOMEM; + if (qcom->current_role == next_role) + return; - for (i = 0; i < qcom->num_clocks; i++) { - struct clk *clk; - int ret; - - clk = of_clk_get(np, i); - if (IS_ERR(clk)) { - while (--i >= 0) - clk_put(qcom->clks[i]); - return PTR_ERR(clk); - } - - ret = clk_prepare_enable(clk); - if (ret < 0) { - while (--i >= 0) { - clk_disable_unprepare(qcom->clks[i]); - clk_put(qcom->clks[i]); - } - clk_put(clk); + if (pm_runtime_resume_and_get(qcom->dev)) { + dev_dbg(qcom->dev, "Failed to resume device\n"); + return; + } - return ret; - } + if (qcom->current_role == USB_ROLE_DEVICE) + dwc3_qcom_vbus_override_enable(qcom, false); + else if (qcom->current_role != USB_ROLE_DEVICE) + dwc3_qcom_vbus_override_enable(qcom, true); - qcom->clks[i] = clk; - } + pm_runtime_mark_last_busy(qcom->dev); + pm_runtime_put_sync(qcom->dev); - return 0; + /* + * Current role changes via usb_role_switch_set_role callback protected + * internally by mutex lock. + */ + qcom->current_role = next_role; } -static int dwc3_qcom_of_register_core(struct platform_device *pdev) +static void dwc3_qcom_run_stop_notifier(struct dwc3 *dwc, bool is_on) { - struct dwc3_qcom *qcom = platform_get_drvdata(pdev); - struct device_node *np = pdev->dev.of_node; - struct device *dev = &pdev->dev; - int ret; + struct dwc3_qcom *qcom = to_dwc3_qcom(dwc); - struct device_node *dwc3_np __free(device_node) = of_get_compatible_child(np, - "snps,dwc3"); - if (!dwc3_np) { - dev_err(dev, "failed to find dwc3 core child\n"); - return -ENODEV; - } - - ret = of_platform_populate(np, NULL, NULL, dev); - if (ret) { - dev_err(dev, "failed to register dwc3 core - %d\n", ret); - return ret; - } - - qcom->dwc3 = of_find_device_by_node(dwc3_np); - if (!qcom->dwc3) { - ret = -ENODEV; - dev_err(dev, "failed to get dwc3 platform device\n"); - of_platform_depopulate(dev); - } + /* + * When autosuspend is enabled and controller goes to suspend + * after removing UDC from userspace, the next UDC write needs + * setting of QSCRATCH VBUS_VALID to "1" to generate a connect + * done event. + */ + if (!is_on) + return; - return ret; + dwc3_qcom_vbus_override_enable(qcom, true); + pm_runtime_mark_last_busy(qcom->dev); } +struct dwc3_glue_ops dwc3_qcom_glue_ops = { + .pre_set_role = dwc3_qcom_set_role_notifier, + .pre_run_stop = dwc3_qcom_run_stop_notifier, +}; + static int dwc3_qcom_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + struct dwc3_probe_data probe_data = {}; struct device *dev = &pdev->dev; struct dwc3_qcom *qcom; - int ret, i; + struct resource res; + struct resource *r; + int ret; bool ignore_pipe_clk; bool wakeup_source; @@ -743,7 +622,6 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (!qcom) return -ENOMEM; - platform_set_drvdata(pdev, qcom); qcom->dev = &pdev->dev; qcom->resets = devm_reset_control_array_get_optional_exclusive(dev); @@ -752,6 +630,11 @@ static int dwc3_qcom_probe(struct platform_device *pdev) "failed to get resets\n"); } + ret = devm_clk_bulk_get_all(&pdev->dev, &qcom->clks); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get clocks\n"); + qcom->num_clocks = ret; + ret = reset_control_assert(qcom->resets); if (ret) { dev_err(&pdev->dev, "failed to assert resets, err=%d\n", ret); @@ -763,22 +646,29 @@ static int dwc3_qcom_probe(struct platform_device *pdev) ret = reset_control_deassert(qcom->resets); if (ret) { dev_err(&pdev->dev, "failed to deassert resets, err=%d\n", ret); - goto reset_assert; + return ret; } - ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np)); - if (ret) { - dev_err_probe(dev, ret, "failed to get clocks\n"); - goto reset_assert; + ret = clk_bulk_prepare_enable(qcom->num_clocks, qcom->clks); + if (ret < 0) + return ret; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + ret = -EINVAL; + goto clk_disable; } + res = *r; + res.end = res.start + SDM845_QSCRATCH_BASE_OFFSET; - qcom->qscratch_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(qcom->qscratch_base)) { - ret = PTR_ERR(qcom->qscratch_base); + qcom->qscratch_base = devm_ioremap(dev, res.end, SDM845_QSCRATCH_SIZE); + if (!qcom->qscratch_base) { + dev_err(dev, "failed to map qscratch region\n"); + ret = -ENOMEM; goto clk_disable; } - ret = dwc3_qcom_setup_irq(pdev); + ret = dwc3_qcom_setup_irq(qcom, pdev); if (ret) { dev_err(dev, "failed to setup IRQs, err=%d\n", ret); goto clk_disable; @@ -793,82 +683,79 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (ignore_pipe_clk) dwc3_qcom_select_utmi_clk(qcom); - ret = dwc3_qcom_of_register_core(pdev); - if (ret) { - dev_err(dev, "failed to register DWC3 Core, err=%d\n", ret); + qcom->mode = usb_get_dr_mode(dev); + + if (qcom->mode == USB_DR_MODE_HOST) { + qcom->current_role = USB_ROLE_HOST; + } else if (qcom->mode == USB_DR_MODE_PERIPHERAL) { + qcom->current_role = USB_ROLE_DEVICE; + dwc3_qcom_vbus_override_enable(qcom, true); + } else { + if ((device_property_read_bool(dev, "usb-role-switch")) && + (usb_get_role_switch_default_mode(dev) == USB_DR_MODE_HOST)) + qcom->current_role = USB_ROLE_HOST; + else + qcom->current_role = USB_ROLE_DEVICE; + } + + qcom->dwc.glue_ops = &dwc3_qcom_glue_ops; + + qcom->dwc.dev = dev; + probe_data.dwc = &qcom->dwc; + probe_data.res = &res; + probe_data.ignore_clocks_and_resets = true; + probe_data.properties = DWC3_DEFAULT_PROPERTIES; + ret = dwc3_core_probe(&probe_data); + if (ret) { + ret = dev_err_probe(dev, ret, "failed to register DWC3 Core\n"); goto clk_disable; } ret = dwc3_qcom_interconnect_init(qcom); if (ret) - goto depopulate; - - qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev); - - /* enable vbus override for device mode */ - if (qcom->mode != USB_DR_MODE_HOST) - dwc3_qcom_vbus_override_enable(qcom, true); - - /* register extcon to override sw_vbus on Vbus change later */ - ret = dwc3_qcom_register_extcon(qcom); - if (ret) - goto interconnect_exit; + goto remove_core; wakeup_source = of_property_read_bool(dev->of_node, "wakeup-source"); device_init_wakeup(&pdev->dev, wakeup_source); - device_init_wakeup(&qcom->dwc3->dev, wakeup_source); qcom->is_suspended = false; - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - pm_runtime_forbid(dev); return 0; -interconnect_exit: - dwc3_qcom_interconnect_exit(qcom); -depopulate: - of_platform_depopulate(&pdev->dev); - platform_device_put(qcom->dwc3); +remove_core: + dwc3_core_remove(&qcom->dwc); clk_disable: - for (i = qcom->num_clocks - 1; i >= 0; i--) { - clk_disable_unprepare(qcom->clks[i]); - clk_put(qcom->clks[i]); - } -reset_assert: - reset_control_assert(qcom->resets); + clk_bulk_disable_unprepare(qcom->num_clocks, qcom->clks); return ret; } static void dwc3_qcom_remove(struct platform_device *pdev) { - struct dwc3_qcom *qcom = platform_get_drvdata(pdev); - struct device *dev = &pdev->dev; - int i; + struct dwc3 *dwc = platform_get_drvdata(pdev); + struct dwc3_qcom *qcom = to_dwc3_qcom(dwc); - of_platform_depopulate(&pdev->dev); - platform_device_put(qcom->dwc3); - - for (i = qcom->num_clocks - 1; i >= 0; i--) { - clk_disable_unprepare(qcom->clks[i]); - clk_put(qcom->clks[i]); - } - qcom->num_clocks = 0; + if (pm_runtime_resume_and_get(qcom->dev) < 0) + return; + dwc3_core_remove(&qcom->dwc); + clk_bulk_disable_unprepare(qcom->num_clocks, qcom->clks); dwc3_qcom_interconnect_exit(qcom); - reset_control_assert(qcom->resets); - pm_runtime_allow(dev); - pm_runtime_disable(dev); + pm_runtime_put_noidle(qcom->dev); } -static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev) +static int dwc3_qcom_pm_suspend(struct device *dev) { - struct dwc3_qcom *qcom = dev_get_drvdata(dev); + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_qcom *qcom = to_dwc3_qcom(dwc); bool wakeup = device_may_wakeup(dev); int ret; + ret = dwc3_pm_suspend(&qcom->dwc); + if (ret) + return ret; + ret = dwc3_qcom_suspend(qcom, wakeup); if (ret) return ret; @@ -878,9 +765,10 @@ static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev) return 0; } -static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev) +static int dwc3_qcom_pm_resume(struct device *dev) { - struct dwc3_qcom *qcom = dev_get_drvdata(dev); + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_qcom *qcom = to_dwc3_qcom(dwc); bool wakeup = device_may_wakeup(dev); int ret; @@ -890,31 +778,68 @@ static int __maybe_unused dwc3_qcom_pm_resume(struct device *dev) qcom->pm_suspended = false; + ret = dwc3_pm_resume(&qcom->dwc); + if (ret) + return ret; + return 0; } -static int __maybe_unused dwc3_qcom_runtime_suspend(struct device *dev) +static void dwc3_qcom_complete(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + + dwc3_pm_complete(dwc); +} + +static int dwc3_qcom_prepare(struct device *dev) { - struct dwc3_qcom *qcom = dev_get_drvdata(dev); + struct dwc3 *dwc = dev_get_drvdata(dev); + + return dwc3_pm_prepare(dwc); +} + +static int dwc3_qcom_runtime_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_qcom *qcom = to_dwc3_qcom(dwc); + int ret; + + ret = dwc3_runtime_suspend(&qcom->dwc); + if (ret) + return ret; return dwc3_qcom_suspend(qcom, true); } -static int __maybe_unused dwc3_qcom_runtime_resume(struct device *dev) +static int dwc3_qcom_runtime_resume(struct device *dev) { - struct dwc3_qcom *qcom = dev_get_drvdata(dev); + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_qcom *qcom = to_dwc3_qcom(dwc); + int ret; + + ret = dwc3_qcom_resume(qcom, true); + if (ret) + return ret; - return dwc3_qcom_resume(qcom, true); + return dwc3_runtime_resume(&qcom->dwc); +} + +static int dwc3_qcom_runtime_idle(struct device *dev) +{ + return dwc3_runtime_idle(dev_get_drvdata(dev)); } static const struct dev_pm_ops dwc3_qcom_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dwc3_qcom_pm_suspend, dwc3_qcom_pm_resume) - SET_RUNTIME_PM_OPS(dwc3_qcom_runtime_suspend, dwc3_qcom_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(dwc3_qcom_pm_suspend, dwc3_qcom_pm_resume) + RUNTIME_PM_OPS(dwc3_qcom_runtime_suspend, dwc3_qcom_runtime_resume, + dwc3_qcom_runtime_idle) + .complete = pm_sleep_ptr(dwc3_qcom_complete), + .prepare = pm_sleep_ptr(dwc3_qcom_prepare), }; static const struct of_device_id dwc3_qcom_of_match[] = { - { .compatible = "qcom,dwc3" }, + { .compatible = "qcom,snps-dwc3" }, { } }; MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match); @@ -922,9 +847,10 @@ MODULE_DEVICE_TABLE(of, dwc3_qcom_of_match); static struct platform_driver dwc3_qcom_driver = { .probe = dwc3_qcom_probe, .remove = dwc3_qcom_remove, + .shutdown = dwc3_qcom_remove, .driver = { .name = "dwc3-qcom", - .pm = &dwc3_qcom_dev_pm_ops, + .pm = pm_ptr(&dwc3_qcom_dev_pm_ops), .of_match_table = dwc3_qcom_of_match, }, }; diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index ef7c43008946..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, diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index a33a42ba0249..f41b0da5e89d 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -32,6 +32,9 @@ #define XLNX_USB_TRAFFIC_ROUTE_CONFIG 0x005C #define XLNX_USB_TRAFFIC_ROUTE_FPD 0x1 +/* USB 2.0 IP Register */ +#define XLNX_USB2_TRAFFIC_ROUTE_CONFIG 0x0044 + #define XLNX_USB_FPD_PIPE_CLK 0x7c #define PIPE_CLK_DESELECT 1 #define PIPE_CLK_SELECT 0 @@ -66,6 +69,23 @@ static void dwc3_xlnx_mask_phy_rst(struct dwc3_xlnx *priv_data, bool mask) writel(reg, priv_data->regs + XLNX_USB_PHY_RST_EN); } +static void dwc3_xlnx_set_coherency(struct dwc3_xlnx *priv_data, u32 coherency_offset) +{ + struct device *dev = priv_data->dev; + u32 reg; + + /* + * This routes the USB DMA traffic to go through FPD path instead + * of reaching DDR directly. This traffic routing is needed to + * make SMMU and CCI work with USB DMA. + */ + if (of_dma_is_coherent(dev->of_node) || device_iommu_mapped(dev)) { + reg = readl(priv_data->regs + coherency_offset); + reg |= XLNX_USB_TRAFFIC_ROUTE_FPD; + writel(reg, priv_data->regs + coherency_offset); + } +} + static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data) { struct device *dev = priv_data->dev; @@ -92,6 +112,7 @@ static int dwc3_xlnx_init_versal(struct dwc3_xlnx *priv_data) } dwc3_xlnx_mask_phy_rst(priv_data, true); + dwc3_xlnx_set_coherency(priv_data, XLNX_USB2_TRAFFIC_ROUTE_CONFIG); return 0; } @@ -102,7 +123,6 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) struct reset_control *crst, *hibrst, *apbrst; struct gpio_desc *reset_gpio; int ret = 0; - u32 reg; priv_data->usb3_phy = devm_phy_optional_get(dev, "usb3-phy"); if (IS_ERR(priv_data->usb3_phy)) { @@ -112,21 +132,6 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) goto err; } - /* - * The following core resets are not required unless a USB3 PHY - * is used, and the subsequent register settings are not required - * unless a core reset is performed (they should be set properly - * by the first-stage boot loader, but may be reverted by a core - * reset). They may also break the configuration if USB3 is actually - * 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) { - /* 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)) { ret = PTR_ERR(crst); @@ -151,22 +156,31 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) goto err; } - ret = reset_control_assert(crst); - if (ret < 0) { - dev_err(dev, "Failed to assert core reset\n"); - goto err; - } - - ret = reset_control_assert(hibrst); - if (ret < 0) { - dev_err(dev, "Failed to assert hibernation reset\n"); - goto err; - } - - ret = reset_control_assert(apbrst); - if (ret < 0) { - dev_err(dev, "Failed to assert APB reset\n"); - goto err; + /* + * Asserting the core resets is not required unless a USB3 PHY is used. + * They may also break the configuration if USB3 is actually in use but + * the usb3-phy entry is missing from the device tree. Therefore, skip + * a full reset cycle and just deassert the resets if the phy is + * absent. + */ + if (priv_data->usb3_phy) { + ret = reset_control_assert(crst); + if (ret < 0) { + dev_err(dev, "Failed to assert core reset\n"); + goto err; + } + + ret = reset_control_assert(hibrst); + if (ret < 0) { + dev_err(dev, "Failed to assert hibernation reset\n"); + goto err; + } + + ret = reset_control_assert(apbrst); + if (ret < 0) { + dev_err(dev, "Failed to assert APB reset\n"); + goto err; + } } ret = phy_init(priv_data->usb3_phy); @@ -181,11 +195,15 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) goto err; } - /* Set PIPE Power Present signal in FPD Power Present Register*/ - writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); - - /* Set the PIPE Clock Select bit in FPD PIPE Clock register */ - writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); + if (priv_data->usb3_phy) { + /* Set PIPE Power Present signal in FPD Power Present Register*/ + writel(FPD_POWER_PRSNT_OPTION, priv_data->regs + XLNX_USB_FPD_POWER_PRSNT); + /* Set the PIPE Clock Select bit in FPD PIPE Clock register */ + writel(PIPE_CLK_SELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); + } else { + /* Deselect the PIPE Clock Select bit in FPD PIPE Clock register */ + writel(PIPE_CLK_DESELECT, priv_data->regs + XLNX_USB_FPD_PIPE_CLK); + } ret = reset_control_deassert(crst); if (ret < 0) { @@ -205,33 +223,20 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) goto err; } -skip_usb3_phy: /* ulpi reset via gpio-modepin or gpio-framework driver */ - reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpio)) { return dev_err_probe(dev, PTR_ERR(reset_gpio), "Failed to request reset GPIO\n"); } if (reset_gpio) { - /* Toggle ulpi to reset the phy. */ - gpiod_set_value_cansleep(reset_gpio, 1); usleep_range(5000, 10000); gpiod_set_value_cansleep(reset_gpio, 0); usleep_range(5000, 10000); } - /* - * This routes the USB DMA traffic to go through FPD path instead - * of reaching DDR directly. This traffic routing is needed to - * make SMMU and CCI work with USB DMA. - */ - if (of_dma_is_coherent(dev->of_node) || device_iommu_mapped(dev)) { - reg = readl(priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG); - reg |= XLNX_USB_TRAFFIC_ROUTE_FPD; - writel(reg, priv_data->regs + XLNX_USB_TRAFFIC_ROUTE_CONFIG); - } - + dwc3_xlnx_set_coherency(priv_data, XLNX_USB_TRAFFIC_ROUTE_CONFIG); err: return ret; } @@ -375,7 +380,6 @@ static int __maybe_unused dwc3_xlnx_runtime_resume(struct device *dev) static int __maybe_unused dwc3_xlnx_runtime_idle(struct device *dev) { - pm_runtime_mark_last_busy(dev); pm_runtime_autosuspend(dev); return 0; @@ -424,6 +428,7 @@ static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = { static struct platform_driver dwc3_xlnx_driver = { .probe = dwc3_xlnx_probe, .remove = dwc3_xlnx_remove, + .shutdown = 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 666ac432f52d..bfe616194dfa 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -94,6 +94,7 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, req->request.actual = 0; req->request.status = -EINPROGRESS; req->epnum = dep->number; + req->status = DWC3_REQUEST_STATUS_QUEUED; list_add_tail(&req->list, &dep->pending_list); @@ -288,7 +289,9 @@ void dwc3_ep0_out_start(struct dwc3 *dwc) dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8, DWC3_TRBCTL_CONTROL_SETUP, false); ret = dwc3_ep0_start_trans(dep); - WARN_ON(ret < 0); + if (ret < 0) + dev_err(dwc->dev, "ep0 out start transfer failed: %d\n", ret); + for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) { struct dwc3_ep *dwc3_ep; @@ -358,7 +361,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, if ((dwc->speed == DWC3_DSTS_SUPERSPEED) || (dwc->speed == DWC3_DSTS_SUPERSPEED_PLUS)) { - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); if (reg & DWC3_DCTL_INITU1ENA) usb_status |= 1 << USB_DEV_STAT_U1_ENABLED; if (reg & DWC3_DCTL_INITU2ENA) @@ -414,12 +417,12 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state, if (set && dwc->dis_u1_entry_quirk) return -EINVAL; - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); if (set) reg |= DWC3_DCTL_INITU1ENA; else reg &= ~DWC3_DCTL_INITU1ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_writel(dwc, DWC3_DCTL, reg); return 0; } @@ -438,12 +441,12 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state, if (set && dwc->dis_u2_entry_quirk) return -EINVAL; - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); if (set) reg |= DWC3_DCTL_INITU2ENA; else reg &= ~DWC3_DCTL_INITU2ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_writel(dwc, DWC3_DCTL, reg); return 0; } @@ -609,10 +612,10 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) return -EINVAL; } - reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg = dwc3_readl(dwc, DWC3_DCFG); reg &= ~(DWC3_DCFG_DEVADDR_MASK); reg |= DWC3_DCFG_DEVADDR(addr); - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + dwc3_writel(dwc, DWC3_DCFG, reg); if (addr) usb_gadget_set_state(dwc->gadget, USB_STATE_ADDRESS); @@ -669,12 +672,12 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) * Enable transition to U1/U2 state when * nothing is pending from application. */ - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); if (!dwc->dis_u1_entry_quirk) reg |= DWC3_DCTL_ACCEPTU1ENA; if (!dwc->dis_u2_entry_quirk) reg |= DWC3_DCTL_ACCEPTU2ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_writel(dwc, DWC3_DCTL, reg); } break; @@ -714,7 +717,7 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) dwc->u2sel = le16_to_cpu(timing.u2sel); dwc->u2pel = le16_to_cpu(timing.u2pel); - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); if (reg & DWC3_DCTL_INITU2ENA) param = dwc->u2pel; if (reg & DWC3_DCTL_INITU1ENA) @@ -830,7 +833,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected) goto out; - trace_dwc3_ctrl_req(ctrl); + trace_dwc3_ctrl_req(dwc, ctrl); len = le16_to_cpu(ctrl->wLength); if (!len) { @@ -1061,7 +1064,9 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, ret = dwc3_ep0_start_trans(dep); } - WARN_ON(ret < 0); + if (ret < 0) + dev_err(dwc->dev, + "ep0 data phase start transfer failed: %d\n", ret); } static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) @@ -1078,7 +1083,12 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep) static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep) { - WARN_ON(dwc3_ep0_start_control_status(dep)); + int ret; + + ret = dwc3_ep0_start_control_status(dep); + if (ret) + dev_err(dwc->dev, + "ep0 status phase start transfer failed: %d\n", ret); } static void dwc3_ep0_do_control_status(struct dwc3 *dwc, @@ -1121,7 +1131,10 @@ void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); memset(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); - WARN_ON_ONCE(ret); + if (ret) + dev_err_ratelimited(dwc->dev, + "ep0 data phase end transfer failed: %d\n", ret); + dep->resource_index = 0; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 89a4dc8ebf94..3d4ca68e584c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -42,7 +42,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) { u32 reg; - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; switch (mode) { @@ -73,7 +73,7 @@ int dwc3_gadget_get_link_state(struct dwc3 *dwc) { u32 reg; - reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg = dwc3_readl(dwc, DWC3_DSTS); return DWC3_DSTS_USBLNKST(reg); } @@ -97,7 +97,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) */ if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) { while (--retries) { - reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg = dwc3_readl(dwc, DWC3_DSTS); if (reg & DWC3_DSTS_DCNRD) udelay(5); else @@ -108,15 +108,15 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) return -ETIMEDOUT; } - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; /* set no action before sending new link state change */ - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_writel(dwc, DWC3_DCTL, reg); /* set requested state */ reg |= DWC3_DCTL_ULSTCHNGREQ(state); - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_writel(dwc, DWC3_DCTL, reg); /* * The following code is racy when called from dwc3_gadget_wakeup, @@ -128,7 +128,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) /* wait for a change in DSTS */ retries = 10000; while (--retries) { - reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg = dwc3_readl(dwc, DWC3_DSTS); if (DWC3_DSTS_USBLNKST(reg) == state) return 0; @@ -228,6 +228,13 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, { struct dwc3 *dwc = dep->dwc; + /* + * The request might have been processed and completed while the + * spinlock was released. Skip processing if already completed. + */ + if (req->status == DWC3_REQUEST_STATUS_COMPLETED) + return; + dwc3_gadget_del_and_unmap_request(dep, req, status); req->status = DWC3_REQUEST_STATUS_COMPLETED; @@ -253,11 +260,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd, int ret = 0; u32 reg; - dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); - dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); + dwc3_writel(dwc, DWC3_DGCMDPAR, param); + dwc3_writel(dwc, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); do { - reg = dwc3_readl(dwc->regs, DWC3_DGCMD); + reg = dwc3_readl(dwc, DWC3_DGCMD); if (!(reg & DWC3_DGCMD_CMDACT)) { status = DWC3_DGCMD_STATUS(reg); if (status) @@ -271,13 +278,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd, status = -ETIMEDOUT; } - trace_dwc3_gadget_generic_cmd(cmd, param, status); + trace_dwc3_gadget_generic_cmd(dwc, cmd, param, status); return ret; } -static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async); - /** * dwc3_send_gadget_ep_cmd - issue an endpoint command * @dep: the endpoint to which the command is going to be issued @@ -315,6 +320,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, int cmd_status = 0; int ret = -EINVAL; + u8 epnum = dep->number; /* * When operating in USB 2.0 speeds (HS/FS), if GUSB2PHYCFG.ENBLSLPM or @@ -328,7 +334,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, */ if (dwc->gadget->speed <= USB_SPEED_HIGH || DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)); if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) { saved_config |= DWC3_GUSB2PHYCFG_SUSPHY; reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; @@ -340,7 +346,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, } if (saved_config) - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg); } /* @@ -350,9 +356,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, * improve performance. */ if (DWC3_DEPCMD_CMD(cmd) != DWC3_DEPCMD_UPDATETRANSFER) { - dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0); - dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1); - dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2); + dwc3_writel(dwc, DWC3_DEPCMDPAR0(epnum), params->param0); + dwc3_writel(dwc, DWC3_DEPCMDPAR1(epnum), params->param1); + dwc3_writel(dwc, DWC3_DEPCMDPAR2(epnum), params->param2); } /* @@ -376,7 +382,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, else cmd |= DWC3_DEPCMD_CMDACT; - dwc3_writel(dep->regs, DWC3_DEPCMD, cmd); + dwc3_writel(dwc, DWC3_DEPCMD(epnum), cmd); if (!(cmd & DWC3_DEPCMD_CMDACT) || (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_ENDTRANSFER && @@ -386,7 +392,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd, } do { - reg = dwc3_readl(dep->regs, DWC3_DEPCMD); + reg = dwc3_readl(dwc, DWC3_DEPCMD(epnum)); if (!(reg & DWC3_DEPCMD_CMDACT)) { cmd_status = DWC3_DEPCMD_STATUS(reg); @@ -442,9 +448,9 @@ skip_status: mdelay(1); if (saved_config) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)); reg |= saved_config; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg); } return ret; @@ -547,6 +553,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; @@ -563,8 +570,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; } @@ -715,7 +727,7 @@ static int dwc3_gadget_calc_ram_depth(struct dwc3 *dwc) u32 reg; /* Check if TXFIFOs start at non-zero addr */ - reg = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + reg = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0)); fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(reg); ram_depth -= (fifo_0_start >> 16); @@ -743,7 +755,7 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc) /* Read ep0IN related TXFIFO size */ dep = dwc->eps[1]; - size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + size = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0)); if (DWC3_IP_IS(DWC3)) fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size); else @@ -751,15 +763,17 @@ 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)) & + dwc3_readl(dwc, DWC3_GTXFIFOSIZ(num >> 1)) & DWC31_GTXFIFOSIZ_TXFRAMNUM; - dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size); + dwc3_writel(dwc, DWC3_GTXFIFOSIZ(num >> 1), size); dep->flags &= ~DWC3_EP_TXFIFO_RESIZED; } dwc->num_ep_resized = 0; @@ -862,7 +876,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) fifo_size++; /* Check if TXFIFOs start at non-zero addr */ - tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)); + tmp = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(0)); fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp); fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16)); @@ -885,7 +899,7 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep) return -ENOMEM; } - dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size); + dwc3_writel(dwc, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size); dep->flags |= DWC3_EP_TXFIFO_RESIZED; dwc->num_ep_resized++; @@ -918,11 +932,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) if (ret) return ret; - if (!(dep->flags & DWC3_EP_RESOURCE_ALLOCATED)) { - ret = dwc3_gadget_set_xfer_resource(dep); - if (ret) - return ret; - } + ret = dwc3_gadget_set_xfer_resource(dep); + if (ret) + return ret; if (!(dep->flags & DWC3_EP_ENABLED)) { struct dwc3_trb *trb_st_hw; @@ -931,9 +943,9 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action) dep->type = usb_endpoint_type(desc); dep->flags |= DWC3_EP_ENABLED; - reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg = dwc3_readl(dwc, DWC3_DALEPENA); reg |= DWC3_DALEPENA_EP(dep->number); - dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + dwc3_writel(dwc, DWC3_DALEPENA, reg); dep->trb_dequeue = 0; dep->trb_enqueue = 0; @@ -1068,9 +1080,9 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) if (dep->flags & DWC3_EP_STALL) __dwc3_gadget_ep_set_halt(dep, 0, false); - reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg = dwc3_readl(dwc, DWC3_DALEPENA); reg &= ~DWC3_DALEPENA_EP(dep->number); - dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + dwc3_writel(dwc, DWC3_DALEPENA, reg); dwc3_remove_requests(dwc, dep, -ESHUTDOWN); @@ -1176,7 +1188,7 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, struct dwc3_request *req; struct dwc3_ep *dep = to_dwc3_ep(ep); - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -1676,7 +1688,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) * transfer, there's no need to update the transfer. */ if (!ret && !starting) - return ret; + return 0; req = next_request(&dep->started_list); if (!req) { @@ -1731,7 +1743,7 @@ static int __dwc3_gadget_get_frame(struct dwc3 *dwc) { u32 reg; - reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg = dwc3_readl(dwc, DWC3_DSTS); return DWC3_DSTS_SOFFN(reg); } @@ -1768,7 +1780,11 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int dep->flags |= DWC3_EP_DELAY_STOP; return 0; } - WARN_ON_ONCE(ret); + + if (ret) + dev_err_ratelimited(dep->dwc->dev, + "end transfer failed: %d\n", ret); + dep->resource_index = 0; if (!interrupt) @@ -1971,12 +1987,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; @@ -2165,7 +2181,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: @@ -2335,13 +2351,13 @@ static void dwc3_gadget_enable_linksts_evts(struct dwc3 *dwc, bool set) if (DWC3_VER_IS_PRIOR(DWC3, 250A)) return; - reg = dwc3_readl(dwc->regs, DWC3_DEVTEN); + reg = dwc3_readl(dwc, DWC3_DEVTEN); if (set) reg |= DWC3_DEVTEN_ULSTCNGEN; else reg &= ~DWC3_DEVTEN_ULSTCNGEN; - dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); + dwc3_writel(dwc, DWC3_DEVTEN, reg); } static int dwc3_gadget_get_frame(struct usb_gadget *g) @@ -2351,10 +2367,8 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g) return __dwc3_gadget_get_frame(dwc); } -static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async) +static int __dwc3_gadget_wakeup(struct dwc3 *dwc) { - int retries; - int ret; u32 reg; @@ -2366,7 +2380,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async) * * We can check that via USB Link State bits in DSTS register. */ - reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg = dwc3_readl(dwc, DWC3_DSTS); link_state = DWC3_DSTS_USBLNKST(reg); @@ -2382,8 +2396,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async) return -EINVAL; } - if (async) - dwc3_gadget_enable_linksts_evts(dwc, true); + dwc3_gadget_enable_linksts_evts(dwc, true); ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV); if (ret < 0) { @@ -2395,34 +2408,15 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async) /* Recent versions do this automatically */ if (DWC3_VER_IS_PRIOR(DWC3, 194A)) { /* write zeroes to Link Change Request */ - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_writel(dwc, DWC3_DCTL, reg); } /* * Since link status change events are enabled we will receive - * an U0 event when wakeup is successful. So bail out. + * an U0 event when wakeup is successful. */ - if (async) - return 0; - - /* poll until Link State changes to ON */ - retries = 20000; - - while (retries--) { - reg = dwc3_readl(dwc->regs, DWC3_DSTS); - - /* in HS, means ON */ - if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0) - break; - } - - if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) { - dev_err(dwc->dev, "failed to send remote wakeup\n"); - return -EINVAL; - } - return 0; } @@ -2443,7 +2437,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) spin_unlock_irqrestore(&dwc->lock, flags); return -EINVAL; } - ret = __dwc3_gadget_wakeup(dwc, true); + ret = __dwc3_gadget_wakeup(dwc); spin_unlock_irqrestore(&dwc->lock, flags); @@ -2471,14 +2465,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id) */ link_state = dwc3_gadget_get_link_state(dwc); if (link_state == DWC3_LINK_STATE_U3) { - ret = __dwc3_gadget_wakeup(dwc, false); - if (ret) { - spin_unlock_irqrestore(&dwc->lock, flags); - return -EINVAL; - } - dwc3_resume_gadget(dwc); - dwc->suspended = false; - dwc->link_state = DWC3_LINK_STATE_U0; + dwc->wakeup_pending_funcs |= BIT(intf_id); + ret = __dwc3_gadget_wakeup(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + return ret; } ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION, @@ -2540,7 +2530,7 @@ static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc) if (ssp_rate == USB_SSP_GEN_UNKNOWN) ssp_rate = dwc->max_ssp_rate; - reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg = dwc3_readl(dwc, DWC3_DCFG); reg &= ~DWC3_DCFG_SPEED_MASK; reg &= ~DWC3_DCFG_NUMLANES(~0); @@ -2553,7 +2543,7 @@ static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc) dwc->max_ssp_rate != USB_SSP_GEN_2x1) reg |= DWC3_DCFG_NUMLANES(1); - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + dwc3_writel(dwc, DWC3_DCFG, reg); } static void __dwc3_gadget_set_speed(struct dwc3 *dwc) @@ -2571,7 +2561,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc) return; } - reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg = dwc3_readl(dwc, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); /* @@ -2622,7 +2612,7 @@ static void __dwc3_gadget_set_speed(struct dwc3 *dwc) speed < USB_SPEED_SUPER_PLUS) reg &= ~DWC3_DCFG_NUMLANES(~0); - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + dwc3_writel(dwc, DWC3_DCFG, reg); } static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) @@ -2647,7 +2637,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) * mentioned in the dwc3 programming guide. It has been tested on an * Exynos platforms. */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)); if (reg & DWC3_GUSB2PHYCFG_SUSPHY) { saved_config |= DWC3_GUSB2PHYCFG_SUSPHY; reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; @@ -2659,9 +2649,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) } if (saved_config) - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg); - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); if (is_on) { if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) { reg &= ~DWC3_DCTL_TRGTULST_MASK; @@ -2680,18 +2670,19 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) dwc->pullups_connected = false; } + dwc3_pre_run_stop(dwc, is_on); dwc3_gadget_dctl_write_safe(dwc, reg); do { usleep_range(1000, 2000); - reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg = dwc3_readl(dwc, DWC3_DSTS); reg &= DWC3_DSTS_DEVCTRLHLT; } while (--timeout && !(!is_on ^ !reg)); if (saved_config) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)); reg |= saved_config; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYCFG(0), reg); } if (!timeout) @@ -2867,13 +2858,13 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc) if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) reg |= DWC3_DEVTEN_U3L2L1SUSPEN; - dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); + dwc3_writel(dwc, DWC3_DEVTEN, reg); } static void dwc3_gadget_disable_irq(struct dwc3 *dwc) { /* mask all interrupts */ - dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); + dwc3_writel(dwc, DWC3_DEVTEN, 0x00); } static irqreturn_t dwc3_interrupt(int irq, void *_dwc); @@ -2914,10 +2905,10 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc) nump = min_t(u32, nump, 16); /* update NumP */ - reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg = dwc3_readl(dwc, DWC3_DCFG); reg &= ~DWC3_DCFG_NUMP_MASK; reg |= nump << DWC3_DCFG_NUMP_SHIFT; - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + dwc3_writel(dwc, DWC3_DCFG, reg); } static int __dwc3_gadget_start(struct dwc3 *dwc) @@ -2931,10 +2922,10 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) * the core supports IMOD, disable it. */ if (dwc->imod_interval) { - dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval); - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); + dwc3_writel(dwc, DWC3_DEV_IMOD(0), dwc->imod_interval); + dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); } else if (dwc3_has_imod(dwc)) { - dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0); + dwc3_writel(dwc, DWC3_DEV_IMOD(0), 0); } /* @@ -2944,13 +2935,13 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) * This way, we maximize the chances that we'll be able to get several * bursts of data without going through any sort of endpoint throttling. */ - reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); + reg = dwc3_readl(dwc, DWC3_GRXTHRCFG); if (DWC3_IP_IS(DWC3)) reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL; else reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL; - dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); + dwc3_writel(dwc, DWC3_GRXTHRCFG, reg); dwc3_gadget_setup_nump(dwc); @@ -2961,15 +2952,15 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) * ACK with NumP=0 and PP=0 (for IN direction). This slightly improves * the stream performance. */ - reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg = dwc3_readl(dwc, DWC3_DCFG); reg |= DWC3_DCFG_IGNSTRMPP; - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + dwc3_writel(dwc, DWC3_DCFG, reg); /* Enable MST by default if the device is capable of MST */ if (DWC3_MST_CAPABLE(&dwc->hwparams)) { - reg = dwc3_readl(dwc->regs, DWC3_DCFG1); + reg = dwc3_readl(dwc, DWC3_DCFG1); reg &= ~DWC3_DCFG1_DIS_MST_ENH; - dwc3_writel(dwc->regs, DWC3_DCFG1, reg); + dwc3_writel(dwc, DWC3_DCFG1, reg); } /* Start with SuperSpeed Default */ @@ -3133,8 +3124,6 @@ static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g, static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) { struct dwc3 *dwc = gadget_to_dwc(g); - union power_supply_propval val = {0}; - int ret; if (dwc->usb2_phy) return usb_phy_set_power(dwc->usb2_phy, mA); @@ -3142,10 +3131,10 @@ static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA) if (!dwc->usb_psy) return -EOPNOTSUPP; - val.intval = 1000 * mA; - ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); + dwc->current_limit = mA; + schedule_work(&dwc->vbus_draw_work); - return ret; + return 0; } /** @@ -3249,7 +3238,7 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep) /* MDWIDTH is represented in bits, we need it in bytes */ mdwidth /= 8; - size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1)); + size = dwc3_readl(dwc, DWC3_GTXFIFOSIZ(dep->number >> 1)); if (DWC3_IP_IS(DWC3)) size = DWC3_GTXFIFOSIZ_TXFDEP(size); else @@ -3298,7 +3287,7 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep) mdwidth /= 8; /* All OUT endpoints share a single RxFIFO space */ - size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0)); + size = dwc3_readl(dwc, DWC3_GRXFIFOSIZ(0)); if (DWC3_IP_IS(DWC3)) size = DWC3_GRXFIFOSIZ_RXFDEP(size); else @@ -3384,14 +3373,13 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) int ret; u8 num = epnum >> 1; - dep = kzalloc(sizeof(*dep), GFP_KERNEL); + dep = kzalloc_obj(*dep); if (!dep) return -ENOMEM; dep->dwc = dwc; dep->number = epnum; dep->direction = direction; - dep->regs = dwc->regs + DWC3_DEP_BASE(epnum); dwc->eps[epnum] = dep; dep->combo_num = 0; dep->start_cmd_status = 0; @@ -3429,14 +3417,53 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum) 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) @@ -3478,7 +3505,7 @@ static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, struct dwc3_request *req, struct dwc3_trb *trb, - const struct dwc3_event_depevt *event, int status, int chain) + const struct dwc3_event_depevt *event, int status) { unsigned int count; @@ -3497,7 +3524,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, * We're going to do that here to avoid problems of HW trying * to use bogus TRBs for transfers. */ - if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO)) + if (trb->ctrl & DWC3_TRB_CTRL_HWO) trb->ctrl &= ~DWC3_TRB_CTRL_HWO; /* @@ -3530,7 +3557,8 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) return 1; - if (event->status & DEPEVT_STATUS_SHORT && !chain) + if (event->status & DEPEVT_STATUS_SHORT && + !(trb->ctrl & DWC3_TRB_CTRL_CHN)) return 1; if ((trb->ctrl & DWC3_TRB_CTRL_ISP_IMI) && @@ -3557,8 +3585,7 @@ static int dwc3_gadget_ep_reclaim_trb_sg(struct dwc3_ep *dep, trb = &dep->trb_pool[dep->trb_dequeue]; ret = dwc3_gadget_ep_reclaim_completed_trb(dep, req, - trb, event, status, - !!(trb->ctrl & DWC3_TRB_CTRL_CHN)); + trb, event, status); if (ret) break; } @@ -3703,6 +3730,8 @@ out: for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { dep = dwc->eps[i]; + if (!dep) + continue; if (!(dep->flags & DWC3_EP_ENABLED)) continue; @@ -3711,9 +3740,9 @@ out: return no_started_trb; } - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); reg |= dwc->u1u2; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_writel(dwc, DWC3_DCTL, reg); dwc->u1u2 = 0; } @@ -3758,6 +3787,15 @@ static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep, static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep, const struct dwc3_event_depevt *event) { + /* + * During a device-initiated disconnect, a late xferNotReady event can + * be generated after the End Transfer command resets the event filter, + * but before the controller is halted. Ignore it to prevent a new + * transfer from starting. + */ + if (!dep->dwc->connected) + return; + dwc3_gadget_endpoint_frame_from_event(dep, event); /* @@ -3839,7 +3877,7 @@ static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep, case DEPEVT_STREAM_NOSTREAM: dep->flags &= ~DWC3_EP_STREAM_PRIMED; if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) - queue_delayed_work(system_wq, &dep->nostream_work, + queue_delayed_work(system_percpu_wq, &dep->nostream_work, msecs_to_jiffies(100)); break; } @@ -3852,6 +3890,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)) @@ -4016,7 +4058,9 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) dep->flags &= ~DWC3_EP_STALL; ret = dwc3_send_clear_stall_ep_cmd(dep); - WARN_ON_ONCE(ret); + if (ret) + dev_err_ratelimited(dwc->dev, + "failed to clear STALL on %s\n", dep->name); } } @@ -4028,7 +4072,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RX_DET); - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; reg &= ~DWC3_DCTL_INITU2ENA; dwc3_gadget_dctl_write_safe(dwc, reg); @@ -4117,7 +4161,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc3_stop_active_transfers(dwc); dwc->connected = true; - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; dwc3_gadget_dctl_write_safe(dwc, reg); dwc->test_mode = false; @@ -4126,9 +4170,9 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc3_clear_stall_all_ep(dwc); /* Reset device address to zero */ - reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg = dwc3_readl(dwc, DWC3_DCFG); reg &= ~(DWC3_DCFG_DEVADDR_MASK); - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + dwc3_writel(dwc, DWC3_DCFG, reg); } static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) @@ -4142,7 +4186,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) if (!dwc->softconnect) return; - reg = dwc3_readl(dwc->regs, DWC3_DSTS); + reg = dwc3_readl(dwc, DWC3_DSTS); speed = reg & DWC3_DSTS_CONNECTSPD; dwc->speed = speed; @@ -4217,11 +4261,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) !dwc->usb2_gadget_lpm_disable && (speed != DWC3_DSTS_SUPERSPEED) && (speed != DWC3_DSTS_SUPERSPEED_PLUS)) { - reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg = dwc3_readl(dwc, DWC3_DCFG); reg |= DWC3_DCFG_LPM_CAP; - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + dwc3_writel(dwc, DWC3_DCFG, reg); - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold | @@ -4244,12 +4288,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) dwc3_gadget_dctl_write_safe(dwc, reg); } else { if (dwc->usb2_gadget_lpm_disable) { - reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg = dwc3_readl(dwc, DWC3_DCFG); reg &= ~DWC3_DCFG_LPM_CAP; - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + dwc3_writel(dwc, DWC3_DCFG, reg); } - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); reg &= ~DWC3_DCTL_HIRD_THRES_MASK; dwc3_gadget_dctl_write_safe(dwc, reg); } @@ -4300,6 +4344,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, { enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; unsigned int pwropt; + int ret; + int intf_id; /* * WORKAROUND: DWC3 < 2.50a have an issue when configured without @@ -4353,7 +4399,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, switch (dwc->link_state) { case DWC3_LINK_STATE_U1: case DWC3_LINK_STATE_U2: - reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg = dwc3_readl(dwc, DWC3_DCTL); u1u2 = reg & (DWC3_DCTL_INITU2ENA | DWC3_DCTL_ACCEPTU2ENA | DWC3_DCTL_INITU1ENA @@ -4375,7 +4421,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, switch (next) { case DWC3_LINK_STATE_U0: - if (dwc->gadget->wakeup_armed) { + if (dwc->gadget->wakeup_armed || dwc->wakeup_pending_funcs) { dwc3_gadget_enable_linksts_evts(dwc, false); dwc3_resume_gadget(dwc); dwc->suspended = false; @@ -4398,6 +4444,18 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, } dwc->link_state = next; + + /* Proceed with func wakeup if any interfaces that has requested */ + while (dwc->wakeup_pending_funcs && (next == DWC3_LINK_STATE_U0)) { + intf_id = ffs(dwc->wakeup_pending_funcs) - 1; + ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION, + DWC3_DGCMDPAR_DN_FUNC_WAKE | + DWC3_DGCMDPAR_INTF_SEL(intf_id)); + if (ret) + dev_err(dwc->dev, "Failed to send DN wake for intf %d\n", intf_id); + + dwc->wakeup_pending_funcs &= ~BIT(intf_id); + } } static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc, @@ -4498,7 +4556,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt) ret = IRQ_HANDLED; /* Unmask interrupt */ - dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), + dwc3_writel(dwc, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_SIZE(evt->length)); evt->flags &= ~DWC3_EVENT_PENDING; @@ -4509,8 +4567,8 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt) 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); + dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); + dwc3_writel(dwc, DWC3_DEV_IMOD(0), dwc->imod_interval); } return ret; @@ -4559,16 +4617,22 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) if (evt->flags & DWC3_EVENT_PENDING) return IRQ_HANDLED; - count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); + count = dwc3_readl(dwc, DWC3_GEVNTCOUNT(0)); count &= DWC3_GEVNTCOUNT_MASK; if (!count) return IRQ_NONE; + if (count > evt->length) { + dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n", + count, evt->length); + return IRQ_NONE; + } + evt->count = count; evt->flags |= DWC3_EVENT_PENDING; /* Mask interrupt */ - dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), + dwc3_writel(dwc, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length)); amount = min(count, evt->length - evt->lpos); @@ -4577,7 +4641,7 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) if (amount < count) memcpy(evt->cache, evt->buf, count - amount); - dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count); + dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), count); return IRQ_WAKE_THREAD; } @@ -4664,7 +4728,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) } init_completion(&dwc->ep0_in_setup); - dwc->gadget = kzalloc(sizeof(struct usb_gadget), GFP_KERNEL); + dwc->gadget = kzalloc_obj(struct usb_gadget); if (!dwc->gadget) { ret = -ENOMEM; goto err3; @@ -4751,6 +4815,7 @@ err1: err0: return ret; } +EXPORT_SYMBOL_GPL(dwc3_gadget_init); /* -------------------------------------------------------------------------- */ @@ -4759,7 +4824,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc) if (!dwc->gadget) return; - dwc3_enable_susphy(dwc, false); + dwc3_enable_susphy(dwc, true); usb_del_gadget(dwc->gadget); dwc3_gadget_free_endpoints(dwc); usb_put_gadget(dwc->gadget); @@ -4769,6 +4834,7 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dma_free_coherent(dwc->sysdev, sizeof(*dwc->ep0_trb) * 2, dwc->ep0_trb, dwc->ep0_trb_addr); } +EXPORT_SYMBOL_GPL(dwc3_gadget_exit); int dwc3_gadget_suspend(struct dwc3 *dwc) { @@ -4776,26 +4842,22 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) int ret; ret = dwc3_gadget_soft_disconnect(dwc); - if (ret) - goto err; - - spin_lock_irqsave(&dwc->lock, flags); - if (dwc->gadget_driver) - dwc3_disconnect_gadget(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - - return 0; - -err: /* * Attempt to reset the controller's state. Likely no * communication can be established until the host * performs a port reset. */ - if (dwc->softconnect) + if (ret && dwc->softconnect) { dwc3_gadget_soft_connect(dwc); + return -EAGAIN; + } - return ret; + spin_lock_irqsave(&dwc->lock, flags); + if (dwc->gadget_driver) + dwc3_disconnect_gadget(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; } int dwc3_gadget_resume(struct dwc3 *dwc) diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index d73e735e4081..45f113b3c146 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -132,7 +132,7 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep) { u32 res_id; - res_id = dwc3_readl(dep->regs, DWC3_DEPCMD); + res_id = dwc3_readl(dep->dwc, DWC3_DEPCMD(dep->number)); dep->resource_index = DWC3_DEPCMD_GET_RSC_IDX(res_id); } @@ -147,7 +147,7 @@ static inline void dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep) static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value) { value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, value); + dwc3_writel(dwc, DWC3_DCTL, value); } #endif /* __DRIVERS_USB_DWC3_GADGET_H */ diff --git a/drivers/usb/dwc3/glue.h b/drivers/usb/dwc3/glue.h new file mode 100644 index 000000000000..d738e1739ae0 --- /dev/null +++ b/drivers/usb/dwc3/glue.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * glue.h - DesignWare USB3 DRD glue header + */ + +#ifndef __DRIVERS_USB_DWC3_GLUE_H +#define __DRIVERS_USB_DWC3_GLUE_H + +#include <linux/types.h> +#include "core.h" + +/** + * dwc3_properties: DWC3 core properties + * @gsbuscfg0_reqinfo: Value to be programmed in the GSBUSCFG0.REQINFO field + * @needs_full_reinit: indicate the controller may not remain power during system + * pm and need full initialization + */ +struct dwc3_properties { + u32 gsbuscfg0_reqinfo; + unsigned needs_full_reinit:1; +}; + +#define DWC3_DEFAULT_PROPERTIES ((struct dwc3_properties){ \ + .gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED, \ + }) + +/** + * dwc3_probe_data: Initialization parameters passed to dwc3_core_probe() + * @dwc: Reference to dwc3 context structure + * @res: resource for the DWC3 core mmio region + * @ignore_clocks_and_resets: clocks and resets defined for the device should + * be ignored by the DWC3 core, as they are managed by the glue + * @skip_core_init_mode: Skip the finial initialization of the target mode, as + * it must be managed by the glue + * @properties: dwc3 software manage properties + */ +struct dwc3_probe_data { + struct dwc3 *dwc; + struct resource *res; + bool ignore_clocks_and_resets; + bool skip_core_init_mode; + struct dwc3_properties properties; +}; + +/** + * dwc3_core_probe - Initialize the core dwc3 driver + * @data: Initialization and configuration parameters for the controller + * + * Initializes the DesignWare USB3 core driver by setting up resources, + * registering interrupts, performing hardware setup, and preparing + * the controller for operation in the appropriate mode (host, gadget, + * or OTG). This is the main initialization function called by glue + * layer drivers to set up the core controller. + * + * Return: 0 on success, negative error code on failure + */ +int dwc3_core_probe(const struct dwc3_probe_data *data); + +/** + * dwc3_core_remove - Deinitialize and remove the core dwc3 driver + * @dwc: Pointer to DWC3 controller context + * + * Cleans up resources and disables the dwc3 core driver. This should be called + * during driver removal or when the glue layer needs to shut down the + * controller completely. + */ +void dwc3_core_remove(struct dwc3 *dwc); + +/* + * The following callbacks are provided for glue drivers to call from their + * own pm callbacks provided in struct dev_pm_ops. Glue drivers can perform + * platform-specific work before or after calling these functions and delegate + * the core suspend/resume operations to the core driver. + */ +int dwc3_runtime_suspend(struct dwc3 *dwc); +int dwc3_runtime_resume(struct dwc3 *dwc); +int dwc3_runtime_idle(struct dwc3 *dwc); +int dwc3_pm_suspend(struct dwc3 *dwc); +int dwc3_pm_resume(struct dwc3 *dwc); +void dwc3_pm_complete(struct dwc3 *dwc); +int dwc3_pm_prepare(struct dwc3 *dwc); + + +/* All of the following functions must only be used with skip_core_init_mode */ + +/** + * dwc3_core_init - Initialize DWC3 core hardware + * @dwc: Pointer to DWC3 controller context + * + * Configures and initializes the core hardware, usually done by dwc3_core_probe. + * This function is provided for platforms that use skip_core_init_mode and need + * to finalize the core initialization after some platform-specific setup. + * It must only be called when using skip_core_init_mode and before + * dwc3_host_init or dwc3_gadget_init. + * + * Return: 0 on success, negative error code on failure + */ +int dwc3_core_init(struct dwc3 *dwc); + +/** + * dwc3_core_exit - Shut down DWC3 core hardware + * @dwc: Pointer to DWC3 controller context + * + * Disables and cleans up the core hardware state. This is usually handled + * internally by dwc3 and must only be called when using skip_core_init_mode + * and only after dwc3_core_init. Afterwards, dwc3_core_init may be called + * again. + */ +void dwc3_core_exit(struct dwc3 *dwc); + +/** + * dwc3_host_init - Initialize host mode operation + * @dwc: Pointer to DWC3 controller context + * + * Initializes the controller for USB host mode operation, usually done by + * dwc3_core_probe or from within the dwc3 USB role switch callback. + * This function is provided for platforms that use skip_core_init_mode and need + * to finalize the host initialization after some platform-specific setup. + * It must not be called before dwc3_core_init or when skip_core_init_mode is + * not used. It must also not be called when gadget or host mode has already + * been initialized. + * + * Return: 0 on success, negative error code on failure + */ +int dwc3_host_init(struct dwc3 *dwc); + +/** + * dwc3_host_exit - Shut down host mode operation + * @dwc: Pointer to DWC3 controller context + * + * Disables and cleans up host mode resources, usually done by + * the dwc3 USB role switch callback before switching controller mode. + * It must only be called when skip_core_init_mode is used and only after + * dwc3_host_init. + */ +void dwc3_host_exit(struct dwc3 *dwc); + +/** + * dwc3_gadget_init - Initialize gadget mode operation + * @dwc: Pointer to DWC3 controller context + * + * Initializes the controller for USB gadget mode operation, usually done by + * dwc3_core_probe or from within the dwc3 USB role switch callback. This + * function is provided for platforms that use skip_core_init_mode and need to + * finalize the gadget initialization after some platform-specific setup. + * It must not be called before dwc3_core_init or when skip_core_init_mode is + * not used. It must also not be called when gadget or host mode has already + * been initialized. + * + * Return: 0 on success, negative error code on failure + */ +int dwc3_gadget_init(struct dwc3 *dwc); + +/** + * dwc3_gadget_exit - Shut down gadget mode operation + * @dwc: Pointer to DWC3 controller context + * + * Disables and cleans up gadget mode resources, usually done by + * the dwc3 USB role switch callback before switching controller mode. + * It must only be called when skip_core_init_mode is used and only after + * dwc3_gadget_init. + */ +void dwc3_gadget_exit(struct dwc3 *dwc); + +/** + * dwc3_enable_susphy - Control SUSPHY status for all USB ports + * @dwc: Pointer to DWC3 controller context + * @enable: True to enable SUSPHY, false to disable + * + * Enables or disables the USB3 PHY SUSPEND and USB2 PHY SUSPHY feature for + * all available ports. + * This is usually handled by the dwc3 core code and should only be used + * when skip_core_init_mode is used and the glue layer needs to manage SUSPHY + * settings itself, e.g., due to platform-specific requirements during mode + * switches. + */ +void dwc3_enable_susphy(struct dwc3 *dwc, bool enable); + +/** + * dwc3_set_prtcap - Set the USB controller PRTCAP mode + * @dwc: Pointer to DWC3 controller context + * @mode: Target mode, must be one of DWC3_GCTL_PRTCAP_{HOST,DEVICE,OTG} + * @ignore_susphy: If true, skip disabling the SUSPHY and keep the current state + * + * Updates PRTCAP of the controller and current_dr_role inside the dwc3 + * structure. For DRD controllers, this also disables SUSPHY unless explicitly + * told to skip via the ignore_susphy parameter. + * + * This is usually handled by the dwc3 core code and should only be used + * when skip_core_init_mode is used and the glue layer needs to manage mode + * transitions itself due to platform-specific requirements. It must be called + * with the correct mode before calling dwc3_host_init or dwc3_gadget_init. + */ +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy); + +#endif diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index b48e108fc8fe..96b588bd08cd 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -37,7 +37,10 @@ static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) /* 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 (dwc->xhci_resources[0].flags & IORESOURCE_MEM_NONPOSTED) + xhci_regs = ioremap_np(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); + else + xhci_regs = ioremap(dwc->xhci_resources[0].start, DWC3_XHCI_REGS_END); if (!xhci_regs) { dev_err(dwc->dev, "Failed to ioremap xhci_regs\n"); return; @@ -182,6 +185,9 @@ int dwc3_host_init(struct dwc3 *dwc) if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A)) props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped"); + props[prop_idx++] = PROPERTY_ENTRY_U16("num-hc-interrupters", + dwc->num_hc_interrupters); + if (prop_idx) { ret = device_create_managed_software_node(&xhci->dev, props, NULL); if (ret) { @@ -214,13 +220,15 @@ err: platform_device_put(xhci); return ret; } +EXPORT_SYMBOL_GPL(dwc3_host_init); void dwc3_host_exit(struct dwc3 *dwc) { if (dwc->sys_wakeup) device_init_wakeup(&dwc->xhci->dev, false); - dwc3_enable_susphy(dwc, false); + dwc3_enable_susphy(dwc, true); platform_device_unregister(dwc->xhci); dwc->xhci = NULL; } +EXPORT_SYMBOL_GPL(dwc3_host_exit); diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index 1e96ea339d48..cad9a2ae1547 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -16,9 +16,10 @@ #include "debug.h" #include "core.h" -static inline u32 dwc3_readl(void __iomem *base, u32 offset) +static inline u32 dwc3_readl(struct dwc3 *dwc, u32 offset) { u32 value; + void __iomem *base = dwc->regs; /* * We requested the mem region starting from the Globals address @@ -32,13 +33,15 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset) * documentation, so we revert it back to the proper addresses, the * same way they are described on SNPS documentation */ - trace_dwc3_readl(base - DWC3_GLOBALS_REGS_START, offset, value); + trace_dwc3_readl(dwc, base - DWC3_GLOBALS_REGS_START, offset, value); return value; } -static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) +static inline void dwc3_writel(struct dwc3 *dwc, u32 offset, u32 value) { + void __iomem *base = dwc->regs; + /* * We requested the mem region starting from the Globals address * space, see dwc3_probe in core.c. @@ -51,7 +54,7 @@ static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) * documentation, so we revert it back to the proper addresses, the * same way they are described on SNPS documentation */ - trace_dwc3_writel(base - DWC3_GLOBALS_REGS_START, offset, value); + trace_dwc3_writel(dwc, base - DWC3_GLOBALS_REGS_START, offset, value); } #endif /* __DRIVERS_USB_DWC3_IO_H */ diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index bdeb1aaf65d8..5253da23d8b0 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -19,47 +19,71 @@ #include "core.h" #include "debug.h" +DECLARE_EVENT_CLASS(dwc3_log_set_prtcap, + TP_PROTO(struct dwc3 *dwc, u32 mode), + TP_ARGS(dwc, mode), + TP_STRUCT__entry( + __field(phys_addr_t, base_address) + __field(u32, mode) + ), + TP_fast_assign( + __entry->base_address = dwc->xhci_resources[0].start; + __entry->mode = mode; + ), + TP_printk("%pa: mode %s", &__entry->base_address, dwc3_mode_string(__entry->mode)) +); + +DEFINE_EVENT(dwc3_log_set_prtcap, dwc3_set_prtcap, + TP_PROTO(struct dwc3 *dwc, u32 mode), + TP_ARGS(dwc, mode) +); + DECLARE_EVENT_CLASS(dwc3_log_io, - TP_PROTO(void *base, u32 offset, u32 value), - TP_ARGS(base, offset, value), + TP_PROTO(struct dwc3 *dwc, void *base, u32 offset, u32 value), + TP_ARGS(dwc, base, offset, value), TP_STRUCT__entry( + __field(phys_addr_t, base_address) __field(void *, base) __field(u32, offset) __field(u32, value) ), TP_fast_assign( + __entry->base_address = dwc->xhci_resources[0].start; __entry->base = base; __entry->offset = offset; __entry->value = value; ), - TP_printk("addr %p offset %04x value %08x", + TP_printk("%pa: addr %p offset %04x value %08x", + &__entry->base_address, __entry->base + __entry->offset, __entry->offset, __entry->value) ); DEFINE_EVENT(dwc3_log_io, dwc3_readl, - TP_PROTO(void __iomem *base, u32 offset, u32 value), - TP_ARGS(base, offset, value) + TP_PROTO(struct dwc3 *dwc, void __iomem *base, u32 offset, u32 value), + TP_ARGS(dwc, base, offset, value) ); DEFINE_EVENT(dwc3_log_io, dwc3_writel, - TP_PROTO(void __iomem *base, u32 offset, u32 value), - TP_ARGS(base, offset, value) + TP_PROTO(struct dwc3 *dwc, void __iomem *base, u32 offset, u32 value), + TP_ARGS(dwc, base, offset, value) ); DECLARE_EVENT_CLASS(dwc3_log_event, TP_PROTO(u32 event, struct dwc3 *dwc), TP_ARGS(event, dwc), TP_STRUCT__entry( + __field(phys_addr_t, base_address) __field(u32, event) __field(u32, ep0state) ), TP_fast_assign( + __entry->base_address = dwc->xhci_resources[0].start; __entry->event = event; __entry->ep0state = dwc->ep0state; ), - TP_printk("event (%08x): %s", __entry->event, + TP_printk("%pa: event (%08x): %s", &__entry->base_address, __entry->event, dwc3_decode_event(__get_buf(DWC3_MSG_MAX), DWC3_MSG_MAX, __entry->event, __entry->ep0state)) ); @@ -70,9 +94,10 @@ DEFINE_EVENT(dwc3_log_event, dwc3_event, ); DECLARE_EVENT_CLASS(dwc3_log_ctrl, - TP_PROTO(struct usb_ctrlrequest *ctrl), - TP_ARGS(ctrl), + TP_PROTO(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl), + TP_ARGS(dwc, ctrl), TP_STRUCT__entry( + __field(phys_addr_t, base_address) __field(__u8, bRequestType) __field(__u8, bRequest) __field(__u16, wValue) @@ -80,13 +105,15 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl, __field(__u16, wLength) ), TP_fast_assign( + __entry->base_address = dwc->xhci_resources[0].start; __entry->bRequestType = ctrl->bRequestType; __entry->bRequest = ctrl->bRequest; __entry->wValue = le16_to_cpu(ctrl->wValue); __entry->wIndex = le16_to_cpu(ctrl->wIndex); __entry->wLength = le16_to_cpu(ctrl->wLength); ), - TP_printk("%s", usb_decode_ctrl(__get_buf(DWC3_MSG_MAX), DWC3_MSG_MAX, + TP_printk("%pa: %s", &__entry->base_address, usb_decode_ctrl(__get_buf(DWC3_MSG_MAX), + DWC3_MSG_MAX, __entry->bRequestType, __entry->bRequest, __entry->wValue, __entry->wIndex, __entry->wLength) @@ -94,14 +121,15 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl, ); DEFINE_EVENT(dwc3_log_ctrl, dwc3_ctrl_req, - TP_PROTO(struct usb_ctrlrequest *ctrl), - TP_ARGS(ctrl) + TP_PROTO(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl), + TP_ARGS(dwc, ctrl) ); DECLARE_EVENT_CLASS(dwc3_log_request, TP_PROTO(struct dwc3_request *req), TP_ARGS(req), TP_STRUCT__entry( + __field(phys_addr_t, base_address) __string(name, req->dep->name) __field(struct dwc3_request *, req) __field(unsigned int, actual) @@ -112,7 +140,7 @@ DECLARE_EVENT_CLASS(dwc3_log_request, __field(int, no_interrupt) ), TP_fast_assign( - __assign_str(name); + __entry->base_address = req->dep->dwc->xhci_resources[0].start; __entry->req = req; __entry->actual = req->request.actual; __entry->length = req->request.length; @@ -121,8 +149,10 @@ DECLARE_EVENT_CLASS(dwc3_log_request, __entry->short_not_ok = req->request.short_not_ok; __entry->no_interrupt = req->request.no_interrupt; ), - TP_printk("%s: req %p length %u/%u %s%s%s ==> %d", - __get_str(name), __entry->req, __entry->actual, __entry->length, + TP_printk("%pa: %s: req %p length %u/%u %s%s%s ==> %d", + &__entry->base_address, + __get_str(name), __entry->req, + __entry->actual, __entry->length, __entry->zero ? "Z" : "z", __entry->short_not_ok ? "S" : "s", __entry->no_interrupt ? "i" : "I", @@ -156,28 +186,30 @@ DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback, ); DECLARE_EVENT_CLASS(dwc3_log_generic_cmd, - TP_PROTO(unsigned int cmd, u32 param, int status), - TP_ARGS(cmd, param, status), + TP_PROTO(struct dwc3 *dwc, unsigned int cmd, u32 param, int status), + TP_ARGS(dwc, cmd, param, status), TP_STRUCT__entry( + __field(phys_addr_t, base_address) __field(unsigned int, cmd) __field(u32, param) __field(int, status) ), TP_fast_assign( + __entry->base_address = dwc->xhci_resources[0].start; __entry->cmd = cmd; __entry->param = param; __entry->status = status; ), - TP_printk("cmd '%s' [%x] param %08x --> status: %s", - dwc3_gadget_generic_cmd_string(__entry->cmd), + TP_printk("%pa: cmd '%s' [%x] param %08x --> status: %s", + &__entry->base_address, dwc3_gadget_generic_cmd_string(__entry->cmd), __entry->cmd, __entry->param, dwc3_gadget_generic_cmd_status_string(__entry->status) ) ); DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd, - TP_PROTO(unsigned int cmd, u32 param, int status), - TP_ARGS(cmd, param, status) + TP_PROTO(struct dwc3 *dwc, unsigned int cmd, u32 param, int status), + TP_ARGS(dwc, cmd, param, status) ); DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, @@ -185,6 +217,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, struct dwc3_gadget_ep_cmd_params *params, int cmd_status), TP_ARGS(dep, cmd, params, cmd_status), TP_STRUCT__entry( + __field(phys_addr_t, base_address) __string(name, dep->name) __field(unsigned int, cmd) __field(u32, param0) @@ -193,6 +226,7 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, __field(int, cmd_status) ), TP_fast_assign( + __entry->base_address = dep->dwc->xhci_resources[0].start; __assign_str(name); __entry->cmd = cmd; __entry->param0 = params->param0; @@ -200,8 +234,9 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, __entry->param2 = params->param2; __entry->cmd_status = cmd_status; ), - TP_printk("%s: cmd '%s' [%x] params %08x %08x %08x --> status: %s", - __get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd), + TP_printk("%pa: %s: cmd '%s' [%x] params %08x %08x %08x --> status: %s", + &__entry->base_address, __get_str(name), + dwc3_gadget_ep_cmd_string(__entry->cmd), __entry->cmd, __entry->param0, __entry->param1, __entry->param2, dwc3_ep_cmd_status_string(__entry->cmd_status) @@ -218,6 +253,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, TP_PROTO(struct dwc3_ep *dep, struct dwc3_trb *trb), TP_ARGS(dep, trb), TP_STRUCT__entry( + __field(phys_addr_t, base_address) __string(name, dep->name) __field(struct dwc3_trb *, trb) __field(u32, bpl) @@ -229,6 +265,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, __field(u32, dequeue) ), TP_fast_assign( + __entry->base_address = dep->dwc->xhci_resources[0].start; __assign_str(name); __entry->trb = trb; __entry->bpl = trb->bpl; @@ -239,8 +276,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, __entry->enqueue = dep->trb_enqueue; __entry->dequeue = dep->trb_dequeue; ), - TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x sofn %08x (%c%c%c%c:%c%c:%s)", - __get_str(name), __entry->trb, __entry->enqueue, + TP_printk("%pa: %s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x sofn %08x (%c%c%c%c:%c%c:%s)", + &__entry->base_address, __get_str(name), __entry->trb, __entry->enqueue, __entry->dequeue, __entry->bph, __entry->bpl, ({char *s; int pcm = ((__entry->size >> 24) & 3) + 1; @@ -290,6 +327,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep, TP_PROTO(struct dwc3_ep *dep), TP_ARGS(dep), TP_STRUCT__entry( + __field(phys_addr_t, base_address) __string(name, dep->name) __field(unsigned int, maxpacket) __field(unsigned int, maxpacket_limit) @@ -301,6 +339,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ep, __field(u8, trb_dequeue) ), TP_fast_assign( + __entry->base_address = dep->dwc->xhci_resources[0].start; __assign_str(name); __entry->maxpacket = dep->endpoint.maxpacket; __entry->maxpacket_limit = dep->endpoint.maxpacket_limit; @@ -311,8 +350,8 @@ DECLARE_EVENT_CLASS(dwc3_log_ep, __entry->trb_enqueue = dep->trb_enqueue; __entry->trb_dequeue = dep->trb_dequeue; ), - TP_printk("%s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c", - __get_str(name), __entry->maxpacket, + TP_printk("%pa: %s: mps %d/%d streams %d burst %d ring %d/%d flags %c:%c%c%c%c:%c", + &__entry->base_address, __get_str(name), __entry->maxpacket, __entry->maxpacket_limit, __entry->max_streams, __entry->maxburst, __entry->trb_enqueue, __entry->trb_dequeue, diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c index f23f4c9a557e..a256b7f5d78b 100644 --- a/drivers/usb/dwc3/ulpi.c +++ b/drivers/usb/dwc3/ulpi.c @@ -10,10 +10,13 @@ #include <linux/delay.h> #include <linux/time64.h> #include <linux/ulpi/regs.h> +#include <linux/ulpi/driver.h> #include "core.h" #include "io.h" +#define USB_VENDOR_MICROCHIP 0x0424 + #define DWC3_ULPI_ADDR(a) \ ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ @@ -33,13 +36,13 @@ static int dwc3_ulpi_busyloop(struct dwc3 *dwc, u8 addr, bool read) if (read) ns += DWC3_ULPI_BASE_DELAY; - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(0)); if (reg & DWC3_GUSB2PHYCFG_SUSPHY) usleep_range(1000, 1200); while (count--) { ndelay(ns); - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYACC(0)); if (reg & DWC3_GUSB2PHYACC_DONE) return 0; cpu_relax(); @@ -55,13 +58,13 @@ static int dwc3_ulpi_read(struct device *dev, u8 addr) int ret; reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); - dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYACC(0), reg); ret = dwc3_ulpi_busyloop(dwc, addr, true); if (ret) return ret; - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); + reg = dwc3_readl(dwc, DWC3_GUSB2PHYACC(0)); return DWC3_GUSB2PHYACC_DATA(reg); } @@ -73,7 +76,7 @@ static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val) reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); reg |= DWC3_GUSB2PHYACC_WRITE | val; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); + dwc3_writel(dwc, DWC3_GUSB2PHYACC(0), reg); return dwc3_ulpi_busyloop(dwc, addr, false); } @@ -83,6 +86,26 @@ static const struct ulpi_ops dwc3_ulpi_ops = { .write = dwc3_ulpi_write, }; +static void dwc3_ulpi_detect_config(struct dwc3 *dwc) +{ + struct ulpi *ulpi = dwc->ulpi; + + switch (ulpi->id.vendor) { + case USB_VENDOR_MICROCHIP: + switch (ulpi->id.product) { + case 0x0009: + /* Microchip USB3340 ULPI PHY */ + dwc->enable_usb2_transceiver_delay = true; + break; + default: + break; + } + break; + default: + break; + } +} + int dwc3_ulpi_init(struct dwc3 *dwc) { /* Register the interface */ @@ -92,6 +115,8 @@ int dwc3_ulpi_init(struct dwc3 *dwc) return PTR_ERR(dwc->ulpi); } + dwc3_ulpi_detect_config(dwc); + return 0; } diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c index 341408410ed9..41118bba9197 100644 --- a/drivers/usb/early/xhci-dbc.c +++ b/drivers/usb/early/xhci-dbc.c @@ -681,6 +681,10 @@ int __init early_xdbc_setup_hardware(void) xdbc.table_base = NULL; xdbc.out_buf = NULL; + + early_iounmap(xdbc.xhci_base, xdbc.xhci_length); + xdbc.xhci_base = NULL; + xdbc.xhci_length = 0; } return ret; diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c index 3d404d19a205..1a48329a4e08 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/fotg210/fotg210-hcd.c @@ -515,7 +515,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) unsigned i; __hc32 tag; - seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC); + seen = kmalloc_objs(*seen, DBG_SCHED_LIMIT, GFP_ATOMIC); if (!seen) return 0; @@ -738,7 +738,7 @@ static struct debug_buffer { struct debug_buffer *buf; - buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); + buf = kzalloc_obj(struct debug_buffer); if (buf) { buf->bus = bus; @@ -1847,7 +1847,7 @@ static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, struct fotg210_qh *qh; dma_addr_t dma; - qh = kzalloc(sizeof(*qh), GFP_ATOMIC); + qh = kzalloc_obj(*qh, GFP_ATOMIC); if (!qh) goto done; qh->hw = (struct fotg210_qh_hw *) @@ -3899,7 +3899,7 @@ static struct fotg210_iso_stream *iso_stream_alloc(gfp_t mem_flags) { struct fotg210_iso_stream *stream; - stream = kzalloc(sizeof(*stream), mem_flags); + stream = kzalloc_obj(*stream, mem_flags); if (likely(stream != NULL)) { INIT_LIST_HEAD(&stream->td_list); INIT_LIST_HEAD(&stream->free_list); @@ -4007,7 +4007,7 @@ static struct fotg210_iso_sched *iso_sched_alloc(unsigned packets, { struct fotg210_iso_sched *iso_sched; - iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags); + iso_sched = kzalloc_flex(*iso_sched, packet, packets, mem_flags); if (likely(iso_sched != NULL)) INIT_LIST_HEAD(&iso_sched->td_list); @@ -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); @@ -5626,11 +5625,6 @@ int __init fotg210_hcd_init(void) if (usb_disabled()) return -ENODEV; - set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); - if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || - test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) - pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); - pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n", hcd_name, sizeof(struct fotg210_qh), sizeof(struct fotg210_qtd), @@ -5644,5 +5638,4 @@ int __init fotg210_hcd_init(void) void __exit fotg210_hcd_cleanup(void) { debugfs_remove(fotg210_debug_root); - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); } diff --git a/drivers/usb/fotg210/fotg210-udc.c b/drivers/usb/fotg210/fotg210-udc.c index 0bae12e34f9a..d9e024873a42 100644 --- a/drivers/usb/fotg210/fotg210-udc.c +++ b/drivers/usb/fotg210/fotg210-udc.c @@ -244,7 +244,7 @@ static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, { struct fotg210_request *req; - req = kzalloc(sizeof(struct fotg210_request), gfp_flags); + req = kzalloc_obj(struct fotg210_request, gfp_flags); if (!req) return NULL; @@ -1183,7 +1183,7 @@ int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg) return irq; /* initialize udc */ - fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); + fotg210 = kzalloc_obj(struct fotg210_udc); if (fotg210 == NULL) return -ENOMEM; @@ -1207,7 +1207,7 @@ int fotg210_udc_probe(struct platform_device *pdev, struct fotg210 *fotg) ret = -ENOMEM; for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { - fotg210->ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); + fotg210->ep[i] = kzalloc_obj(struct fotg210_ep); if (!fotg210->ep[i]) goto err_alloc; } diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 869ad99afb48..a902184bdf82 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1011,7 +1011,7 @@ static int set_config(struct usb_composite_dev *cdev, ep = (struct usb_endpoint_descriptor *)*descriptors; addr = ((ep->bEndpointAddress & 0x80) >> 3) - | (ep->bEndpointAddress & 0x0f); + | usb_endpoint_num(ep); set_bit(addr, f->endpoints); } @@ -1194,30 +1194,6 @@ static void remove_config(struct usb_composite_dev *cdev, } } -/** - * usb_remove_config() - remove a configuration from a device. - * @cdev: wraps the USB gadget - * @config: the configuration - * - * Drivers must call usb_gadget_disconnect before calling this function - * to disconnect the device from the host and make sure the host will not - * try to enumerate the device while we are changing the config list. - */ -void usb_remove_config(struct usb_composite_dev *cdev, - struct usb_configuration *config) -{ - unsigned long flags; - - spin_lock_irqsave(&cdev->lock, flags); - - if (cdev->config == config) - reset_config(cdev); - - spin_unlock_irqrestore(&cdev->lock, flags); - - remove_config(cdev, config); -} - /*-------------------------------------------------------------------------*/ /* We support strings in multiple languages ... string descriptor zero @@ -2011,15 +1987,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (f->get_status) { status = f->get_status(f); + if (status < 0) break; - } else { - /* Set D0 and D1 bits based on func wakeup capability */ - if (f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP) { - status |= USB_INTRF_STAT_FUNC_RW_CAP; - if (f->func_wakeup_armed) - status |= USB_INTRF_STAT_FUNC_RW; - } + + /* if D5 is not set, then device is not wakeup capable */ + if (!(f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP)) + status &= ~(USB_INTRF_STAT_FUNC_RW_CAP | USB_INTRF_STAT_FUNC_RW); } put_unaligned_le16(status & 0x0000ffff, req->buf); @@ -2491,6 +2465,11 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, if (!cdev->os_desc_req->buf) { ret = -ENOMEM; usb_ep_free_request(ep0, cdev->os_desc_req); + /* + * Set os_desc_req to NULL so that composite_dev_cleanup() + * will not try to free it again. + */ + cdev->os_desc_req = NULL; goto end; } cdev->os_desc_req->context = cdev; @@ -2552,7 +2531,7 @@ static int composite_bind(struct usb_gadget *gadget, struct usb_composite_driver *composite = to_cdriver(gdriver); int status = -ENOMEM; - cdev = kzalloc(sizeof *cdev, GFP_KERNEL); + cdev = kzalloc_obj(*cdev); if (!cdev) return status; diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index 95f144a54ed9..256364d4b941 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -54,59 +54,6 @@ usb_descriptor_fillbuf(void *buf, unsigned buflen, 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. - * @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 - * status code. The config.wTotalLength field is set to match the length - * of the result, but other descriptor fields (including power usage and - * interface count) must be set by the caller. - * - * Gadget drivers could use this when constructing a config descriptor - * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the - * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed. - */ -int usb_gadget_config_buf( - const struct usb_config_descriptor *config, - void *buf, - unsigned length, - const struct usb_descriptor_header **desc -) -{ - struct usb_config_descriptor *cp = buf; - int len; - - /* config descriptor first */ - if (length < USB_DT_CONFIG_SIZE || !desc) - return -EINVAL; - *cp = *config; - - /* then interface/endpoint/class/vendor/... */ - len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf, - length - USB_DT_CONFIG_SIZE, desc); - if (len < 0) - return len; - len += USB_DT_CONFIG_SIZE; - if (len > 0xffff) - return -EINVAL; - - /* patch up the config descriptor */ - cp->bLength = USB_DT_CONFIG_SIZE; - cp->bDescriptorType = USB_DT_CONFIG; - cp->wTotalLength = cpu_to_le16(len); - cp->bmAttributes |= USB_CONFIG_ATT_ONE; - return len; -} -EXPORT_SYMBOL_GPL(usb_gadget_config_buf); - -/** * usb_copy_descriptors - copy a vector of USB descriptors * @src: null-terminated vector to copy * Context: initialization code, which may sleep diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index fba2a56dae97..183a25f65ac8 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -409,7 +409,7 @@ static void gadget_info_attr_release(struct config_item *item) kfree(gi); } -static struct configfs_item_operations gadget_root_item_ops = { +static const struct configfs_item_operations gadget_root_item_ops = { .release = gadget_info_attr_release, }; @@ -514,7 +514,7 @@ static void config_usb_cfg_unlink( WARN(1, "Unable to locate function to unbind\n"); } -static struct configfs_item_operations gadget_config_item_ops = { +static const struct configfs_item_operations gadget_config_item_ops = { .release = gadget_config_attr_release, .allow_link = config_usb_cfg_link, .drop_link = config_usb_cfg_unlink, @@ -663,7 +663,7 @@ static void function_drop( config_item_put(item); } -static struct configfs_group_operations functions_ops = { +static const struct configfs_group_operations functions_ops = { .make_group = &function_make, .drop_item = &function_drop, }; @@ -727,7 +727,7 @@ static struct config_group *config_desc_make( if (ret) return ERR_PTR(ret); - cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + cfg = kzalloc_obj(*cfg); if (!cfg) return ERR_PTR(-ENOMEM); cfg->c.label = kstrdup(buf, GFP_KERNEL); @@ -766,7 +766,7 @@ static void config_desc_drop( config_item_put(item); } -static struct configfs_group_operations config_desc_ops = { +static const struct configfs_group_operations config_desc_ops = { .make_group = &config_desc_make, .drop_item = &config_desc_drop, }; @@ -799,7 +799,7 @@ static void gadget_language_attr_release(struct config_item *item) kfree(gs); } -static struct configfs_item_operations gadget_language_langid_item_ops = { +static const struct configfs_item_operations gadget_language_langid_item_ops = { .release = gadget_language_attr_release, }; @@ -852,7 +852,7 @@ static void gadget_string_release(struct config_item *item) kfree(string); } -static struct configfs_item_operations gadget_string_item_ops = { +static const struct configfs_item_operations gadget_string_item_ops = { .release = gadget_string_release, }; @@ -870,7 +870,7 @@ static struct config_item *gadget_language_string_make(struct config_group *grou language = to_gadget_language(&group->cg_item); - string = kzalloc(sizeof(*string), GFP_KERNEL); + string = kzalloc_obj(*string); if (!string) return ERR_PTR(-ENOMEM); @@ -901,7 +901,7 @@ static void gadget_language_string_drop(struct config_group *group, string->usb_string.id = i++; } -static struct configfs_group_operations gadget_language_langid_group_ops = { +static const struct configfs_group_operations gadget_language_langid_group_ops = { .make_item = gadget_language_string_make, .drop_item = gadget_language_string_drop, }; @@ -922,7 +922,7 @@ static struct config_group *gadget_language_make(struct config_group *group, int langs = 0; int ret; - new = kzalloc(sizeof(*new), GFP_KERNEL); + new = kzalloc_obj(*new); if (!new) return ERR_PTR(-ENOMEM); @@ -960,7 +960,7 @@ static void gadget_language_drop(struct config_group *group, config_item_put(item); } -static struct configfs_group_operations gadget_language_group_ops = { +static const struct configfs_group_operations gadget_language_group_ops = { .make_group = &gadget_language_make, .drop_item = &gadget_language_drop, }; @@ -1065,6 +1065,8 @@ static ssize_t webusb_landingPage_store(struct config_item *item, const char *pa unsigned int bytes_to_strip = 0; int l = len; + if (!len) + return len; if (page[l - 1] == '\n') { --l; ++bytes_to_strip; @@ -1188,6 +1190,8 @@ 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; + if (!len) + return len; l = min_t(int, len, OS_STRING_QW_SIGN_LEN >> 1); if (page[l - 1] == '\n') --l; @@ -1262,7 +1266,7 @@ static void os_desc_unlink(struct config_item *os_desc_ci, mutex_unlock(&gi->lock); } -static struct configfs_item_operations os_desc_ops = { +static const struct configfs_item_operations os_desc_ops = { .allow_link = os_desc_link, .drop_link = os_desc_unlink, }; @@ -1387,7 +1391,7 @@ static void usb_os_desc_ext_prop_release(struct config_item *item) kfree(ext_prop); /* frees a whole chunk */ } -static struct configfs_item_operations ext_prop_ops = { +static const struct configfs_item_operations ext_prop_ops = { .release = usb_os_desc_ext_prop_release, }; @@ -1452,7 +1456,7 @@ static void ext_prop_drop(struct config_group *group, struct config_item *item) config_item_put(item); } -static struct configfs_group_operations interf_grp_ops = { +static const struct configfs_group_operations interf_grp_ops = { .make_item = &ext_prop_make, .drop_item = &ext_prop_drop, }; @@ -1625,8 +1629,7 @@ configfs_attach_gadget_strings(struct gadget_info *gi) if (!nlangs) return NULL; - gadget_strings = kcalloc(nlangs + 1, /* including NULL terminator */ - sizeof(struct usb_gadget_strings *), GFP_KERNEL); + gadget_strings = kzalloc_objs(struct usb_gadget_strings *, nlangs + 1)/* including NULL terminator */; if (!gadget_strings) return ERR_PTR(-ENOMEM); @@ -1641,8 +1644,8 @@ configfs_attach_gadget_strings(struct gadget_info *gi) goto cleanup; } - stringtab = kcalloc(language->nstrings + 1, sizeof(struct usb_string), - GFP_KERNEL); + stringtab = kzalloc_objs(struct usb_string, + language->nstrings + 1); if (!stringtab) { us = ERR_PTR(-ENOMEM); goto cleanup; @@ -1746,6 +1749,8 @@ static int configfs_composite_bind(struct usb_gadget *gadget, cdev->use_os_string = true; cdev->b_vendor_code = gi->b_vendor_code; memcpy(cdev->qw_sign, gi->qw_sign, OS_STRING_QW_SIGN_LEN); + } else { + cdev->use_os_string = false; } if (gadget_is_otg(gadget) && !otg_desc[0]) { @@ -1986,7 +1991,7 @@ static struct config_group *gadgets_make( { struct gadget_info *gi; - gi = kzalloc(sizeof(*gi), GFP_KERNEL); + gi = kzalloc_obj(*gi); if (!gi) return ERR_PTR(-ENOMEM); @@ -2055,7 +2060,7 @@ static void gadgets_drop(struct config_group *group, struct config_item *item) config_item_put(item); } -static struct configfs_group_operations gadgets_ops = { +static const struct configfs_group_operations gadgets_ops = { .make_group = &gadgets_make, .drop_item = &gadgets_drop, }; diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index ed5a92c474e5..30016b805bfd 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -158,7 +158,7 @@ struct usb_ep *usb_ep_autoconfig( if (!ep) return NULL; - type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + type = usb_endpoint_type(desc); /* report (variable) full speed bulk maxpacket */ if (type == USB_ENDPOINT_XFER_BULK) { diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 7061720b9732..c628d02bfb1d 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -11,12 +11,15 @@ /* #define VERBOSE_DEBUG */ +#include <linux/cleanup.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/usb/gadget.h> + #include "u_serial.h" @@ -613,6 +616,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) struct usb_string *us; int status; struct usb_ep *ep; + struct usb_request *request __free(free_usb_request) = NULL; /* REVISIT might want instance-specific strings to help * distinguish instances ... @@ -630,7 +634,7 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; acm->ctrl_id = status; acm_iad_descriptor.bFirstInterface = status; @@ -639,43 +643,41 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; acm->data_id = status; acm_data_interface_desc.bInterfaceNumber = status; acm_union_desc.bSlaveInterface0 = status; acm_call_mgmt_descriptor.bDataInterface = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc); if (!ep) - goto fail; + return -ENODEV; acm->port.in = ep; ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc); if (!ep) - goto fail; + return -ENODEV; acm->port.out = ep; ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc); if (!ep) - goto fail; + return -ENODEV; acm->notify = ep; acm_iad_descriptor.bFunctionProtocol = acm->bInterfaceProtocol; acm_control_interface_desc.bInterfaceProtocol = acm->bInterfaceProtocol; /* allocate notification */ - acm->notify_req = gs_alloc_req(ep, - sizeof(struct usb_cdc_notification) + 2, - GFP_KERNEL); - if (!acm->notify_req) - goto fail; + request = gs_alloc_req(ep, + sizeof(struct usb_cdc_notification) + 2, + GFP_KERNEL); + if (!request) + return -ENODEV; - acm->notify_req->complete = acm_cdc_notify_complete; - acm->notify_req->context = acm; + request->complete = acm_cdc_notify_complete; + request->context = acm; /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -692,7 +694,9 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function, acm_ss_function, acm_ss_function); if (status) - goto fail; + return status; + + acm->notify_req = no_free_ptr(request); dev_dbg(&cdev->gadget->dev, "acm ttyGS%d: IN/%s OUT/%s NOTIFY/%s\n", @@ -700,14 +704,6 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) acm->port.in->name, acm->port.out->name, acm->notify->name); return 0; - -fail: - if (acm->notify_req) - gs_free_req(acm->notify, acm->notify_req); - - ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); - - return status; } static void acm_unbind(struct usb_configuration *c, struct usb_function *f) @@ -752,7 +748,7 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi) struct f_serial_opts *opts; struct f_acm *acm; - acm = kzalloc(sizeof(*acm), GFP_KERNEL); + acm = kzalloc_obj(*acm); if (!acm) return ERR_PTR(-ENOMEM); @@ -797,7 +793,7 @@ static void acm_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations acm_item_ops = { +static const struct configfs_item_operations acm_item_ops = { .release = acm_attr_release, }; @@ -886,7 +882,7 @@ static struct usb_function_instance *acm_alloc_instance(void) struct f_serial_opts *opts; int ret; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); opts->protocol = USB_CDC_ACM_PROTO_AT_V25TER; diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 80841de845b0..e495bac4efeb 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -8,6 +8,7 @@ /* #define VERBOSE_DEBUG */ +#include <linux/cleanup.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/module.h> @@ -15,6 +16,8 @@ #include <linux/etherdevice.h> #include <linux/string_choices.h> +#include <linux/usb/gadget.h> + #include "u_ether.h" #include "u_ether_configfs.h" #include "u_ecm.h" @@ -678,24 +681,26 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_ecm_opts *ecm_opts; + struct net_device *net __free(detach_gadget) = NULL; + struct usb_request *request __free(free_usb_request) = NULL; if (!can_support_ecm(cdev->gadget)) return -EINVAL; ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); - mutex_lock(&ecm_opts->lock); - - gether_set_gadget(ecm_opts->net, cdev->gadget); - - if (!ecm_opts->bound) { - status = gether_register_netdev(ecm_opts->net); - ecm_opts->bound = true; - } - - mutex_unlock(&ecm_opts->lock); - if (status) - return status; + scoped_guard(mutex, &ecm_opts->lock) + if (ecm_opts->bind_count == 0 && !ecm_opts->bound) { + if (!device_is_registered(&ecm_opts->net->dev)) { + gether_set_gadget(ecm_opts->net, cdev->gadget); + status = gether_register_netdev(ecm_opts->net); + } else + status = gether_attach_gadget(ecm_opts->net, cdev->gadget); + + if (status) + return status; + net = ecm_opts->net; + } ecm_string_defs[1].s = ecm->ethaddr; @@ -711,7 +716,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; ecm->ctrl_id = status; ecm_iad_descriptor.bFirstInterface = status; @@ -720,24 +725,22 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; ecm->data_id = status; ecm_data_nop_intf.bInterfaceNumber = status; ecm_data_intf.bInterfaceNumber = status; ecm_union_desc.bSlaveInterface0 = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc); if (!ep) - goto fail; + return -ENODEV; ecm->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc); if (!ep) - goto fail; + return -ENODEV; ecm->port.out_ep = ep; /* NOTE: a status/notification endpoint is *OPTIONAL* but we @@ -746,20 +749,18 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) */ ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc); if (!ep) - goto fail; + return -ENODEV; ecm->notify = ep; - status = -ENOMEM; - /* allocate notification request and buffer */ - ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (!ecm->notify_req) - goto fail; - ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL); - if (!ecm->notify_req->buf) - goto fail; - ecm->notify_req->context = ecm; - ecm->notify_req->complete = ecm_notify_complete; + request = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!request) + return -ENOMEM; + request->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL); + if (!request->buf) + return -ENOMEM; + request->context = ecm; + request->complete = ecm_notify_complete; /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -778,7 +779,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function, ecm_ss_function, ecm_ss_function); if (status) - goto fail; + return status; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code @@ -788,20 +789,15 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm->port.open = ecm_open; ecm->port.close = ecm_close; + ecm->notify_req = no_free_ptr(request); + + ecm_opts->bind_count++; + retain_and_null_ptr(net); + DBG(cdev, "CDC Ethernet: IN/%s OUT/%s NOTIFY/%s\n", ecm->port.in_ep->name, ecm->port.out_ep->name, ecm->notify->name); return 0; - -fail: - if (ecm->notify_req) { - kfree(ecm->notify_req->buf); - usb_ep_free_request(ecm->notify, ecm->notify_req); - } - - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item) @@ -844,7 +840,7 @@ static void ecm_free_inst(struct usb_function_instance *f) struct f_ecm_opts *opts; opts = container_of(f, struct f_ecm_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -855,7 +851,7 @@ static struct usb_function_instance *ecm_alloc_inst(void) { struct f_ecm_opts *opts; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); mutex_init(&opts->lock); @@ -892,6 +888,12 @@ static void ecm_resume(struct usb_function *f) gether_resume(&ecm->port); } +static int ecm_get_status(struct usb_function *f) +{ + return (f->func_wakeup_armed ? USB_INTRF_STAT_FUNC_RW : 0) | + USB_INTRF_STAT_FUNC_RW_CAP; +} + static void ecm_free(struct usb_function *f) { struct f_ecm *ecm; @@ -908,9 +910,12 @@ static void ecm_free(struct usb_function *f) static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_ecm *ecm = func_to_ecm(f); + struct f_ecm_opts *ecm_opts; DBG(c->cdev, "ecm unbind\n"); + ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst); + usb_free_all_descriptors(f); if (atomic_read(&ecm->notify_count)) { @@ -920,6 +925,10 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) kfree(ecm->notify_req->buf); usb_ep_free_request(ecm->notify, ecm->notify_req); + + ecm_opts->bind_count--; + if (ecm_opts->bind_count == 0 && !ecm_opts->bound) + gether_detach_gadget(ecm_opts->net); } static struct usb_function *ecm_alloc(struct usb_function_instance *fi) @@ -929,7 +938,7 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi) int status; /* allocate and initialize one new instance */ - ecm = kzalloc(sizeof(*ecm), GFP_KERNEL); + ecm = kzalloc_obj(*ecm); if (!ecm) return ERR_PTR(-ENOMEM); @@ -960,6 +969,7 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi) ecm->port.func.disable = ecm_disable; ecm->port.func.free_func = ecm_free; ecm->port.func.suspend = ecm_suspend; + ecm->port.func.get_status = ecm_get_status; ecm->port.func.resume = ecm_resume; return &ecm->port.func; diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c index 6de81ea17274..ac37d7c1d168 100644 --- a/drivers/usb/gadget/function/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -7,6 +7,7 @@ * Copyright (C) 2009 EF Johnson Technologies */ +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> @@ -251,24 +252,22 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_eem_opts *eem_opts; + struct net_device *net __free(detach_gadget) = NULL; eem_opts = container_of(f->fi, struct f_eem_opts, func_inst); - /* - * in drivers/usb/gadget/configfs.c:configfs_composite_bind() - * configurations are bound in sequence with list_for_each_entry, - * in each configuration its functions are bound in sequence - * with list_for_each_entry, so we assume no race condition - * with regard to eem_opts->bound access - */ - if (!eem_opts->bound) { - mutex_lock(&eem_opts->lock); - gether_set_gadget(eem_opts->net, cdev->gadget); - status = gether_register_netdev(eem_opts->net); - mutex_unlock(&eem_opts->lock); - if (status) - return status; - eem_opts->bound = true; - } + + scoped_guard(mutex, &eem_opts->lock) + if (eem_opts->bind_count == 0 && !eem_opts->bound) { + if (!device_is_registered(&eem_opts->net->dev)) { + gether_set_gadget(eem_opts->net, cdev->gadget); + status = gether_register_netdev(eem_opts->net); + } else + status = gether_attach_gadget(eem_opts->net, cdev->gadget); + + if (status) + return status; + net = eem_opts->net; + } us = usb_gstrings_attach(cdev, eem_strings, ARRAY_SIZE(eem_string_defs)); @@ -279,21 +278,19 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; eem->ctrl_id = status; eem_intf.bInterfaceNumber = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc); if (!ep) - goto fail; + return -ENODEV; eem->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc); if (!ep) - goto fail; + return -ENODEV; eem->port.out_ep = ep; /* support all relevant hardware speeds... we expect that when @@ -309,16 +306,14 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function, eem_ss_function, eem_ss_function); if (status) - goto fail; + return status; + + eem_opts->bind_count++; + retain_and_null_ptr(net); DBG(cdev, "CDC Ethernet (EEM): IN/%s OUT/%s\n", eem->port.in_ep->name, eem->port.out_ep->name); return 0; - -fail: - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req) @@ -462,7 +457,7 @@ static int eem_unwrap(struct gether *port, goto next; } - ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + ctx = kmalloc_obj(*ctx); if (!ctx) { kfree(req->buf); usb_ep_free_request(ep, req); @@ -477,8 +472,13 @@ static int eem_unwrap(struct gether *port, req->complete = eem_cmd_complete; req->zero = 1; req->context = ctx; - if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC)) + if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC)) { DBG(cdev, "echo response queue fail\n"); + kfree(ctx); + kfree(req->buf); + usb_ep_free_request(ep, req); + dev_kfree_skb_any(skb2); + } break; case 1: /* echo response */ @@ -592,7 +592,7 @@ static void eem_free_inst(struct usb_function_instance *f) struct f_eem_opts *opts; opts = container_of(f, struct f_eem_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -603,7 +603,7 @@ static struct usb_function_instance *eem_alloc_inst(void) { struct f_eem_opts *opts; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); mutex_init(&opts->lock); @@ -635,9 +635,17 @@ static void eem_free(struct usb_function *f) static void eem_unbind(struct usb_configuration *c, struct usb_function *f) { + struct f_eem_opts *opts; + DBG(c->cdev, "eem unbind\n"); + opts = container_of(f->fi, struct f_eem_opts, func_inst); + usb_free_all_descriptors(f); + + opts->bind_count--; + if (opts->bind_count == 0 && !opts->bound) + gether_detach_gadget(opts->net); } static struct usb_function *eem_alloc(struct usb_function_instance *fi) @@ -646,7 +654,7 @@ static struct usb_function *eem_alloc(struct usb_function_instance *fi) struct f_eem_opts *opts; /* allocate and initialize one new instance */ - eem = kzalloc(sizeof(*eem), GFP_KERNEL); + eem = kzalloc_obj(*eem); if (!eem) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 2dea9e42a0f8..002c3441bea3 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -59,7 +59,6 @@ static struct ffs_data *__must_check ffs_data_new(const char *dev_name) __attribute__((malloc)); /* Opened counter handling. */ -static void ffs_data_opened(struct ffs_data *ffs); static void ffs_data_closed(struct ffs_data *ffs); /* Called with ffs->mutex held; take over ownership of data. */ @@ -160,8 +159,6 @@ struct ffs_epfile { struct ffs_data *ffs; struct ffs_ep *ep; /* P: ffs->eps_lock */ - struct dentry *dentry; - /* * Buffer for holding data from partial reads which may happen since * we’re rounding user read requests to a multiple of a max packet size. @@ -271,11 +268,11 @@ struct ffs_desc_helper { }; static int __must_check ffs_epfiles_create(struct ffs_data *ffs); -static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count); +static void ffs_epfiles_destroy(struct super_block *sb, + struct ffs_epfile *epfiles, unsigned count); -static struct dentry * -ffs_sb_create_file(struct super_block *sb, const char *name, void *data, - const struct file_operations *fops); +static int ffs_sb_create_file(struct super_block *sb, const char *name, + void *data, const struct file_operations *fops); /* Devices management *******************************************************/ @@ -638,15 +635,26 @@ done_mutex: return ret; } + +static void ffs_data_reset(struct ffs_data *ffs); + static int ffs_ep0_open(struct inode *inode, struct file *file) { - struct ffs_data *ffs = inode->i_private; + struct ffs_data *ffs = inode->i_sb->s_fs_info; - if (ffs->state == FFS_CLOSING) + spin_lock_irq(&ffs->eps_lock); + if (ffs->state == FFS_CLOSING) { + spin_unlock_irq(&ffs->eps_lock); return -EBUSY; - + } + if (!ffs->opened++ && ffs->state == FFS_DEACTIVATED) { + ffs->state = FFS_CLOSING; + spin_unlock_irq(&ffs->eps_lock); + ffs_data_reset(ffs); + } else { + spin_unlock_irq(&ffs->eps_lock); + } file->private_data = ffs; - ffs_data_opened(ffs); return stream_open(inode, file); } @@ -806,7 +814,7 @@ static void *ffs_build_sg_list(struct sg_table *sgt, size_t sz) return NULL; n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT; - pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL); + pages = kvmalloc_objs(struct page *, n_pages); if (!pages) { vfree(vaddr); @@ -854,7 +862,6 @@ static void ffs_user_copy_worker(struct work_struct *work) work); int ret = io_data->status; bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD; - unsigned long flags; if (io_data->read && ret > 0) { kthread_use_mm(io_data->mm); @@ -867,10 +874,7 @@ static void ffs_user_copy_worker(struct work_struct *work) if (io_data->ffs->ffs_eventfd && !kiocb_has_eventfd) eventfd_signal(io_data->ffs->ffs_eventfd); - spin_lock_irqsave(&io_data->ffs->eps_lock, flags); usb_ep_free_request(io_data->ep, io_data->req); - io_data->req = NULL; - spin_unlock_irqrestore(&io_data->ffs->eps_lock, flags); if (io_data->read) kfree(io_data->to_free); @@ -953,7 +957,7 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile, data_len, ret); data_len -= ret; - buf = kmalloc(struct_size(buf, storage, data_len), GFP_KERNEL); + buf = kmalloc_flex(*buf, storage, data_len); if (!buf) return -ENOMEM; buf->length = data_len; @@ -1197,33 +1201,41 @@ error: static int ffs_epfile_open(struct inode *inode, struct file *file) { - struct ffs_epfile *epfile = inode->i_private; + struct ffs_data *ffs = inode->i_sb->s_fs_info; + struct ffs_epfile *epfile; - if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) + spin_lock_irq(&ffs->eps_lock); + if (!ffs->opened) { + spin_unlock_irq(&ffs->eps_lock); return -ENODEV; + } + /* + * we want the state to be FFS_ACTIVE; FFS_ACTIVE alone is + * not enough, though - we might have been through FFS_CLOSING + * and back to FFS_ACTIVE, with our file already removed. + */ + epfile = smp_load_acquire(&inode->i_private); + if (unlikely(ffs->state != FFS_ACTIVE || !epfile)) { + spin_unlock_irq(&ffs->eps_lock); + return -ENODEV; + } + ffs->opened++; + spin_unlock_irq(&ffs->eps_lock); file->private_data = epfile; - ffs_data_opened(epfile->ffs); - return stream_open(inode, file); } static int ffs_aio_cancel(struct kiocb *kiocb) { struct ffs_io_data *io_data = kiocb->private; - struct ffs_epfile *epfile = kiocb->ki_filp->private_data; - unsigned long flags; int value; - spin_lock_irqsave(&epfile->ffs->eps_lock, flags); - if (io_data && io_data->ep && io_data->req) value = usb_ep_dequeue(io_data->ep, io_data->req); else value = -EINVAL; - spin_unlock_irqrestore(&epfile->ffs->eps_lock, flags); - return value; } @@ -1233,7 +1245,7 @@ static ssize_t ffs_epfile_write_iter(struct kiocb *kiocb, struct iov_iter *from) ssize_t res; if (!is_sync_kiocb(kiocb)) { - p = kzalloc(sizeof(io_data), GFP_KERNEL); + p = kzalloc_obj(io_data); if (!p) return -ENOMEM; p->aio = true; @@ -1268,7 +1280,7 @@ static ssize_t ffs_epfile_read_iter(struct kiocb *kiocb, struct iov_iter *to) ssize_t res; if (!is_sync_kiocb(kiocb)) { - p = kzalloc(sizeof(io_data), GFP_KERNEL); + p = kzalloc_obj(io_data); if (!p) return -ENOMEM; p->aio = true; @@ -1316,9 +1328,7 @@ static void ffs_dmabuf_release(struct kref *ref) struct dma_buf *dmabuf = attach->dmabuf; pr_vdebug("FFS DMABUF release\n"); - dma_resv_lock(dmabuf->resv, NULL); - dma_buf_unmap_attachment(attach, priv->sgt, priv->dir); - dma_resv_unlock(dmabuf->resv); + dma_buf_unmap_attachment_unlocked(attach, priv->sgt, priv->dir); dma_buf_detach(attach->dmabuf, attach); dma_buf_put(dmabuf); @@ -1342,7 +1352,7 @@ static void ffs_dmabuf_put(struct dma_buf_attachment *attach) static int ffs_epfile_release(struct inode *inode, struct file *file) { - struct ffs_epfile *epfile = inode->i_private; + struct ffs_epfile *epfile = file->private_data; struct ffs_dmabuf_priv *priv, *tmp; struct ffs_data *ffs = epfile->ffs; @@ -1493,13 +1503,13 @@ static int ffs_dmabuf_attach(struct file *file, int fd) goto err_dmabuf_put; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) { err = -ENOMEM; goto err_dmabuf_detach; } - dir = epfile->in ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + dir = epfile->in ? DMA_TO_DEVICE : DMA_FROM_DEVICE; err = ffs_dma_resv_lock(dmabuf, nonblock); if (err) @@ -1629,7 +1639,7 @@ static int ffs_dmabuf_transfer(struct file *file, /* Make sure we don't have writers */ timeout = nonblock ? 0 : msecs_to_jiffies(DMABUF_ENQUEUE_TIMEOUT_MS); retl = dma_resv_wait_timeout(dmabuf->resv, - dma_resv_usage_rw(epfile->in), + dma_resv_usage_rw(!epfile->in), true, timeout); if (retl == 0) retl = -EBUSY; @@ -1642,7 +1652,7 @@ static int ffs_dmabuf_transfer(struct file *file, if (ret) goto err_resv_unlock; - fence = kmalloc(sizeof(*fence), GFP_KERNEL); + fence = kmalloc_obj(*fence); if (!fence) { ret = -ENOMEM; goto err_resv_unlock; @@ -1674,7 +1684,7 @@ static int ffs_dmabuf_transfer(struct file *file, dma_fence_init(&fence->base, &ffs_dmabuf_fence_ops, &priv->lock, priv->context, seqno); - resv_dir = epfile->in ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ; + resv_dir = epfile->in ? DMA_RESV_USAGE_READ : DMA_RESV_USAGE_WRITE; dma_resv_add_fence(dmabuf->resv, &fence->base, resv_dir); dma_resv_unlock(dmabuf->resv); @@ -1734,10 +1744,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, { int fd; - if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) { - ret = -EFAULT; - break; - } + if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) + return -EFAULT; return ffs_dmabuf_attach(file, fd); } @@ -1745,10 +1753,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, { int fd; - if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) { - ret = -EFAULT; - break; - } + if (copy_from_user(&fd, (void __user *)value, sizeof(fd))) + return -EFAULT; return ffs_dmabuf_detach(file, fd); } @@ -1756,10 +1762,8 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, { struct usb_ffs_dmabuf_transfer_req req; - if (copy_from_user(&req, (void __user *)value, sizeof(req))) { - ret = -EFAULT; - break; - } + if (copy_from_user(&req, (void __user *)value, sizeof(req))) + return -EFAULT; return ffs_dmabuf_transfer(file, &req); } @@ -1876,32 +1880,32 @@ ffs_sb_make_inode(struct super_block *sb, void *data, } /* Create "regular" file */ -static struct dentry *ffs_sb_create_file(struct super_block *sb, - const char *name, void *data, - const struct file_operations *fops) +static int ffs_sb_create_file(struct super_block *sb, const char *name, + void *data, const struct file_operations *fops) { struct ffs_data *ffs = sb->s_fs_info; struct dentry *dentry; struct inode *inode; - dentry = d_alloc_name(sb->s_root, name); - if (!dentry) - return NULL; - inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms); - if (!inode) { - dput(dentry); - return NULL; + if (!inode) + return -ENOMEM; + dentry = simple_start_creating(sb->s_root, name); + if (IS_ERR(dentry)) { + iput(inode); + return PTR_ERR(dentry); } - d_add(dentry, inode); - return dentry; + d_make_persistent(dentry, inode); + + simple_done_creating(dentry); + return 0; } /* Super block */ static const struct super_operations ffs_sb_operations = { .statfs = simple_statfs, - .drop_inode = generic_delete_inode, + .drop_inode = inode_just_drop, }; struct ffs_sb_fill_data { @@ -1938,10 +1942,7 @@ static int ffs_sb_fill(struct super_block *sb, struct fs_context *fc) return -ENOMEM; /* EP0 file */ - if (!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations)) - return -ENOMEM; - - return 0; + return ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations); } enum { @@ -2066,7 +2067,7 @@ static int ffs_fs_init_fs_context(struct fs_context *fc) { struct ffs_sb_fill_data *ctx; - ctx = kzalloc(sizeof(struct ffs_sb_fill_data), GFP_KERNEL); + ctx = kzalloc_obj(struct ffs_sb_fill_data); if (!ctx) return -ENOMEM; @@ -2084,9 +2085,16 @@ static int ffs_fs_init_fs_context(struct fs_context *fc) static void ffs_fs_kill_sb(struct super_block *sb) { - kill_litter_super(sb); - if (sb->s_fs_info) - ffs_data_closed(sb->s_fs_info); + kill_anon_super(sb); + if (sb->s_fs_info) { + struct ffs_data *ffs = sb->s_fs_info; + ffs->state = FFS_CLOSING; + ffs_data_reset(ffs); + // no configfs accesses from that point on, + // so no further schedule_work() is possible + cancel_work_sync(&ffs->reset_work); + ffs_data_put(ffs); + } } static struct file_system_type ffs_fs_type = { @@ -2124,23 +2132,12 @@ static void functionfs_cleanup(void) /* ffs_data and ffs_function construction and destruction code **************/ static void ffs_data_clear(struct ffs_data *ffs); -static void ffs_data_reset(struct ffs_data *ffs); static void ffs_data_get(struct ffs_data *ffs) { refcount_inc(&ffs->ref); } -static void ffs_data_opened(struct ffs_data *ffs) -{ - refcount_inc(&ffs->ref); - if (atomic_add_return(1, &ffs->opened) == 1 && - ffs->state == FFS_DEACTIVATED) { - ffs->state = FFS_CLOSING; - ffs_data_reset(ffs); - } -} - static void ffs_data_put(struct ffs_data *ffs) { if (refcount_dec_and_test(&ffs->ref)) { @@ -2158,40 +2155,35 @@ static void ffs_data_put(struct ffs_data *ffs) static void ffs_data_closed(struct ffs_data *ffs) { - struct ffs_epfile *epfiles; - unsigned long flags; - - if (atomic_dec_and_test(&ffs->opened)) { - if (ffs->no_disconnect) { - ffs->state = FFS_DEACTIVATED; - spin_lock_irqsave(&ffs->eps_lock, flags); - epfiles = ffs->epfiles; - ffs->epfiles = NULL; - spin_unlock_irqrestore(&ffs->eps_lock, - flags); - - if (epfiles) - ffs_epfiles_destroy(epfiles, - ffs->eps_count); - - if (ffs->setup_state == FFS_SETUP_PENDING) - __ffs_ep0_stall(ffs); - } else { - ffs->state = FFS_CLOSING; - ffs_data_reset(ffs); - } + spin_lock_irq(&ffs->eps_lock); + if (--ffs->opened) { // not the last opener? + spin_unlock_irq(&ffs->eps_lock); + return; } - if (atomic_read(&ffs->opened) < 0) { + if (ffs->no_disconnect) { + struct ffs_epfile *epfiles; + + ffs->state = FFS_DEACTIVATED; + epfiles = ffs->epfiles; + ffs->epfiles = NULL; + spin_unlock_irq(&ffs->eps_lock); + + if (epfiles) + ffs_epfiles_destroy(ffs->sb, epfiles, + ffs->eps_count); + + if (ffs->setup_state == FFS_SETUP_PENDING) + __ffs_ep0_stall(ffs); + } else { ffs->state = FFS_CLOSING; + spin_unlock_irq(&ffs->eps_lock); ffs_data_reset(ffs); } - - ffs_data_put(ffs); } static struct ffs_data *ffs_data_new(const char *dev_name) { - struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL); + struct ffs_data *ffs = kzalloc_obj(*ffs); if (!ffs) return NULL; @@ -2202,7 +2194,7 @@ static struct ffs_data *ffs_data_new(const char *dev_name) } refcount_set(&ffs->ref, 1); - atomic_set(&ffs->opened, 0); + ffs->opened = 0; ffs->state = FFS_READ_DESCRIPTORS; mutex_init(&ffs->mutex); spin_lock_init(&ffs->eps_lock); @@ -2236,7 +2228,7 @@ static void ffs_data_clear(struct ffs_data *ffs) * copy of epfile will save us from use-after-free. */ if (epfiles) { - ffs_epfiles_destroy(epfiles, ffs->eps_count); + ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count); ffs->epfiles = NULL; } @@ -2254,6 +2246,7 @@ static void ffs_data_reset(struct ffs_data *ffs) { ffs_data_clear(ffs); + spin_lock_irq(&ffs->eps_lock); ffs->raw_descs_data = NULL; ffs->raw_descs = NULL; ffs->raw_strings = NULL; @@ -2277,6 +2270,7 @@ static void ffs_data_reset(struct ffs_data *ffs) ffs->ms_os_descs_ext_prop_count = 0; ffs->ms_os_descs_ext_prop_name_len = 0; ffs->ms_os_descs_ext_prop_data_len = 0; + spin_unlock_irq(&ffs->eps_lock); } @@ -2333,9 +2327,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs) { struct ffs_epfile *epfile, *epfiles; unsigned i, count; + int err; count = ffs->eps_count; - epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL); + epfiles = kzalloc_objs(*epfiles, count); if (!epfiles) return -ENOMEM; @@ -2349,12 +2344,11 @@ static int ffs_epfiles_create(struct ffs_data *ffs) sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]); else sprintf(epfile->name, "ep%u", i); - epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name, - epfile, - &ffs_epfile_operations); - if (!epfile->dentry) { - ffs_epfiles_destroy(epfiles, i - 1); - return -ENOMEM; + err = ffs_sb_create_file(ffs->sb, epfile->name, + epfile, &ffs_epfile_operations); + if (err) { + ffs_epfiles_destroy(ffs->sb, epfiles, i - 1); + return err; } } @@ -2362,17 +2356,20 @@ static int ffs_epfiles_create(struct ffs_data *ffs) return 0; } -static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) +static void clear_one(struct dentry *dentry) +{ + smp_store_release(&dentry->d_inode->i_private, NULL); +} + +static void ffs_epfiles_destroy(struct super_block *sb, + struct ffs_epfile *epfiles, unsigned count) { struct ffs_epfile *epfile = epfiles; + struct dentry *root = sb->s_root; for (; count; --count, ++epfile) { BUG_ON(mutex_is_locked(&epfile->mutex)); - if (epfile->dentry) { - d_delete(epfile->dentry); - dput(epfile->dentry); - epfile->dentry = NULL; - } + simple_remove_by_name(root, epfile->name, clear_one); } kfree(epfiles); @@ -2418,7 +2415,12 @@ static int ffs_func_eps_enable(struct ffs_function *func) ep = func->eps; epfile = ffs->epfiles; count = ffs->eps_count; - while(count--) { + if (!epfile) { + ret = -ENOMEM; + goto done; + } + + while (count--) { ep->ep->driver_data = ep; ret = config_ep_by_speed(func->gadget, &func->function, ep->ep); @@ -2442,6 +2444,7 @@ static int ffs_func_eps_enable(struct ffs_function *func) } wake_up_interruptible(&ffs->wait); +done: spin_unlock_irqrestore(&func->ffs->eps_lock, flags); return ret; @@ -3295,7 +3298,7 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep, if (ffs_ep->descs[ep_desc_id]) { pr_err("two %sspeed descriptors for EP %d\n", speed_names[ep_desc_id], - ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + usb_endpoint_num(ds)); return -EINVAL; } ffs_ep->descs[ep_desc_id] = ds; @@ -3735,6 +3738,7 @@ static int ffs_func_set_alt(struct usb_function *f, { struct ffs_function *func = ffs_func_from_usb(f); struct ffs_data *ffs = func->ffs; + unsigned long flags; int ret = 0, intf; if (alt > MAX_ALT_SETTINGS) @@ -3747,12 +3751,15 @@ static int ffs_func_set_alt(struct usb_function *f, if (ffs->func) ffs_func_eps_disable(ffs->func); + spin_lock_irqsave(&ffs->eps_lock, flags); if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; + spin_unlock_irqrestore(&ffs->eps_lock, flags); INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return -ENODEV; } + spin_unlock_irqrestore(&ffs->eps_lock, flags); if (ffs->state != FFS_ACTIVE) return -ENODEV; @@ -3770,16 +3777,20 @@ static void ffs_func_disable(struct usb_function *f) { struct ffs_function *func = ffs_func_from_usb(f); struct ffs_data *ffs = func->ffs; + unsigned long flags; if (ffs->func) ffs_func_eps_disable(ffs->func); + spin_lock_irqsave(&ffs->eps_lock, flags); if (ffs->state == FFS_DEACTIVATED) { ffs->state = FFS_CLOSING; + spin_unlock_irqrestore(&ffs->eps_lock, flags); INIT_WORK(&ffs->reset_work, ffs_reset_work); schedule_work(&ffs->reset_work); return; } + spin_unlock_irqrestore(&ffs->eps_lock, flags); if (ffs->state == FFS_ACTIVE) { ffs->func = NULL; @@ -3983,7 +3994,7 @@ static void ffs_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations ffs_item_ops = { +static const struct configfs_item_operations ffs_item_ops = { .release = ffs_attr_release, }; @@ -4020,7 +4031,7 @@ static struct usb_function_instance *ffs_alloc_inst(void) struct f_fs_opts *opts; struct ffs_dev *dev; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); @@ -4096,7 +4107,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi) { struct ffs_function *func; - func = kzalloc(sizeof(*func), GFP_KERNEL); + func = kzalloc_obj(*func); if (!func) return ERR_PTR(-ENOMEM); @@ -4127,7 +4138,7 @@ static struct ffs_dev *_ffs_alloc_dev(void) if (_ffs_get_single_dev()) return ERR_PTR(-EBUSY); - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 740311c4fa24..c5a12a6760ea 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -62,6 +62,9 @@ struct f_hidg { unsigned short report_desc_length; char *report_desc; unsigned short report_length; + unsigned char interval; + bool interval_user_set; + /* * use_out_ep - if true, the OUT Endpoint (interrupt out method) * will be used to receive reports from the host @@ -75,6 +78,7 @@ struct f_hidg { /* recv report */ spinlock_t read_spinlock; wait_queue_head_t read_queue; + bool disabled; /* recv report - interrupt out only (use_out_ep == 1) */ struct list_head completed_out_req; unsigned int qlen; @@ -102,7 +106,7 @@ struct f_hidg { struct list_head report_list; struct device dev; - struct cdev cdev; + struct cdev *cdev; struct usb_function func; struct usb_ep *in_ep; @@ -144,8 +148,8 @@ static struct hid_descriptor hidg_desc = { .bcdHID = cpu_to_le16(0x0101), .bCountryCode = 0x00, .bNumDescriptors = 0x1, - /*.desc[0].bDescriptorType = DYNAMIC */ - /*.desc[0].wDescriptorLenght = DYNAMIC */ + /*.rpt_desc.bDescriptorType = DYNAMIC */ + /*.rpt_desc.wDescriptorLength = DYNAMIC */ }; /* Super-Speed Support */ @@ -156,10 +160,7 @@ static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ - .bInterval = 4, /* FIXME: Add this field in the - * HID gadget configuration? - * (struct hidg_func_descriptor) - */ + /*.bInterval = DYNAMIC */ }; static struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = { @@ -177,10 +178,7 @@ static struct usb_endpoint_descriptor hidg_ss_out_ep_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ - .bInterval = 4, /* FIXME: Add this field in the - * HID gadget configuration? - * (struct hidg_func_descriptor) - */ + /*.bInterval = DYNAMIC */ }; static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = { @@ -218,10 +216,7 @@ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ - .bInterval = 4, /* FIXME: Add this field in the - * HID gadget configuration? - * (struct hidg_func_descriptor) - */ + /* .bInterval = DYNAMIC */ }; static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { @@ -230,10 +225,7 @@ static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ - .bInterval = 4, /* FIXME: Add this field in the - * HID gadget configuration? - * (struct hidg_func_descriptor) - */ + /*.bInterval = DYNAMIC */ }; static struct usb_descriptor_header *hidg_hs_descriptors_intout[] = { @@ -259,10 +251,7 @@ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ - .bInterval = 10, /* FIXME: Add this field in the - * HID gadget configuration? - * (struct hidg_func_descriptor) - */ + /*.bInterval = DYNAMIC */ }; static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { @@ -271,10 +260,7 @@ static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_INT, /*.wMaxPacketSize = DYNAMIC */ - .bInterval = 10, /* FIXME: Add this field in the - * HID gadget configuration? - * (struct hidg_func_descriptor) - */ + /*.bInterval = DYNAMIC */ }; static struct usb_descriptor_header *hidg_fs_descriptors_intout[] = { @@ -329,7 +315,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer, spin_lock_irqsave(&hidg->read_spinlock, flags); -#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req)) +#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req) || hidg->disabled) /* wait for at least one buffer to complete */ while (!READ_COND_INTOUT) { @@ -343,6 +329,11 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer, spin_lock_irqsave(&hidg->read_spinlock, flags); } + if (hidg->disabled) { + spin_unlock_irqrestore(&hidg->read_spinlock, flags); + return -ESHUTDOWN; + } + /* pick the first one */ list = list_first_entry(&hidg->completed_out_req, struct f_hidg_req_list, list); @@ -387,7 +378,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer, return count; } -#define READ_COND_SSREPORT (hidg->set_report_buf != NULL) +#define READ_COND_SSREPORT (hidg->set_report_buf != NULL || hidg->disabled) static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer, size_t count, loff_t *ptr) @@ -520,7 +511,7 @@ try_again: } req->status = 0; - req->zero = 0; + req->zero = 1; req->length = count; req->complete = f_hidg_req_complete; req->context = hidg; @@ -659,7 +650,7 @@ static int f_hidg_get_report(struct file *file, struct usb_hidg_report __user *b struct report_entry *ptr; __u8 report_id; - entry = kmalloc(sizeof(*entry), GFP_KERNEL); + entry = kmalloc_obj(*entry); if (!entry) return -ENOMEM; @@ -758,8 +749,9 @@ static int f_hidg_release(struct inode *inode, struct file *fd) static int f_hidg_open(struct inode *inode, struct file *fd) { + struct kobject *parent = inode->i_cdev->kobj.parent; struct f_hidg *hidg = - container_of(inode->i_cdev, struct f_hidg, cdev); + container_of(parent, struct f_hidg, dev.kobj); fd->private_data = hidg; @@ -784,7 +776,7 @@ static void hidg_intout_complete(struct usb_ep *ep, struct usb_request *req) switch (req->status) { case 0: - req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC); + req_list = kzalloc_obj(*req_list, GFP_ATOMIC); if (!req_list) { ERROR(cdev, "Unable to allocate mem for req_list\n"); goto free_req; @@ -939,8 +931,8 @@ static int hidg_setup(struct usb_function *f, struct hid_descriptor hidg_desc_copy = hidg_desc; VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n"); - hidg_desc_copy.desc[0].bDescriptorType = HID_DT_REPORT; - hidg_desc_copy.desc[0].wDescriptorLength = + hidg_desc_copy.rpt_desc.bDescriptorType = HID_DT_REPORT; + hidg_desc_copy.rpt_desc.wDescriptorLength = cpu_to_le16(hidg->report_desc_length); length = min_t(unsigned short, length, @@ -976,7 +968,7 @@ stall: return -EOPNOTSUPP; respond: - req->zero = 0; + req->zero = 1; req->length = length; status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); if (status < 0) @@ -1012,6 +1004,11 @@ static void hidg_disable(struct usb_function *f) } spin_unlock_irqrestore(&hidg->get_report_spinlock, flags); + spin_lock_irqsave(&hidg->read_spinlock, flags); + hidg->disabled = true; + spin_unlock_irqrestore(&hidg->read_spinlock, flags); + wake_up(&hidg->read_queue); + spin_lock_irqsave(&hidg->write_spinlock, flags); if (!hidg->write_pending) { free_ep_req(hidg->in_ep, hidg->req); @@ -1097,6 +1094,10 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) } } + spin_lock_irqsave(&hidg->read_spinlock, flags); + hidg->disabled = false; + spin_unlock_irqrestore(&hidg->read_spinlock, flags); + if (hidg->in_ep != NULL) { spin_lock_irqsave(&hidg->write_spinlock, flags); hidg->req = req_in; @@ -1202,6 +1203,18 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); hidg_ss_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); + + /* IN endpoints: FS default=10ms, HS default=4µ-frame; user override if set */ + if (!hidg->interval_user_set) { + hidg_fs_in_ep_desc.bInterval = 10; + hidg_hs_in_ep_desc.bInterval = 4; + hidg_ss_in_ep_desc.bInterval = 4; + } else { + hidg_fs_in_ep_desc.bInterval = hidg->interval; + hidg_hs_in_ep_desc.bInterval = hidg->interval; + hidg_ss_in_ep_desc.bInterval = hidg->interval; + } + hidg_ss_out_comp_desc.wBytesPerInterval = cpu_to_le16(hidg->report_length); hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); @@ -1210,8 +1223,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) * We can use hidg_desc struct here but we should not relay * that its content won't change after returning from this function. */ - hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT; - hidg_desc.desc[0].wDescriptorLength = + hidg_desc.rpt_desc.bDescriptorType = HID_DT_REPORT; + hidg_desc.rpt_desc.wDescriptorLength = cpu_to_le16(hidg->report_desc_length); hidg_hs_in_ep_desc.bEndpointAddress = @@ -1224,54 +1237,61 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) hidg_ss_out_ep_desc.bEndpointAddress = hidg_fs_out_ep_desc.bEndpointAddress; - if (hidg->use_out_ep) + if (hidg->use_out_ep) { + /* OUT endpoints: same defaults (FS=10, HS=4) unless user set */ + if (!hidg->interval_user_set) { + hidg_fs_out_ep_desc.bInterval = 10; + hidg_hs_out_ep_desc.bInterval = 4; + hidg_ss_out_ep_desc.bInterval = 4; + } else { + hidg_fs_out_ep_desc.bInterval = hidg->interval; + hidg_hs_out_ep_desc.bInterval = hidg->interval; + hidg_ss_out_ep_desc.bInterval = hidg->interval; + } status = usb_assign_descriptors(f, - hidg_fs_descriptors_intout, - hidg_hs_descriptors_intout, - hidg_ss_descriptors_intout, - hidg_ss_descriptors_intout); - else + hidg_fs_descriptors_intout, + hidg_hs_descriptors_intout, + hidg_ss_descriptors_intout, + hidg_ss_descriptors_intout); + } else { status = usb_assign_descriptors(f, hidg_fs_descriptors_ssreport, hidg_hs_descriptors_ssreport, hidg_ss_descriptors_ssreport, hidg_ss_descriptors_ssreport); - + } if (status) goto fail; - spin_lock_init(&hidg->write_spinlock); hidg->write_pending = 1; hidg->req = NULL; - spin_lock_init(&hidg->read_spinlock); - spin_lock_init(&hidg->get_report_spinlock); - init_waitqueue_head(&hidg->write_queue); - init_waitqueue_head(&hidg->read_queue); - init_waitqueue_head(&hidg->get_queue); - init_waitqueue_head(&hidg->get_id_queue); - INIT_LIST_HEAD(&hidg->completed_out_req); - INIT_LIST_HEAD(&hidg->report_list); INIT_WORK(&hidg->work, get_report_workqueue_handler); hidg->workqueue = alloc_workqueue("report_work", - WQ_FREEZABLE | - WQ_MEM_RECLAIM, + WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU, 1); if (!hidg->workqueue) { status = -ENOMEM; - goto fail; + goto fail_free_descs; } /* create char device */ - cdev_init(&hidg->cdev, &f_hidg_fops); - status = cdev_device_add(&hidg->cdev, &hidg->dev); + hidg->cdev = cdev_alloc(); + if (!hidg->cdev) { + status = -ENOMEM; + goto fail_free_all; + } + hidg->cdev->ops = &f_hidg_fops; + + status = cdev_device_add(hidg->cdev, &hidg->dev); if (status) - goto fail_free_descs; + goto fail_free_all; return 0; -fail_free_descs: +fail_free_all: destroy_workqueue(hidg->workqueue); +fail_free_descs: usb_free_all_descriptors(f); fail: ERROR(f->config->cdev, "hidg_bind FAILED\n"); @@ -1310,7 +1330,7 @@ static void hid_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations hidg_item_ops = { +static const struct configfs_item_operations hidg_item_ops = { .release = hid_attr_release, }; @@ -1408,6 +1428,53 @@ end: CONFIGFS_ATTR(f_hid_opts_, report_desc); +static ssize_t f_hid_opts_interval_show(struct config_item *item, char *page) +{ + struct f_hid_opts *opts = to_f_hid_opts(item); + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d\n", opts->interval); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_hid_opts_interval_store(struct config_item *item, + const char *page, size_t len) +{ + struct f_hid_opts *opts = to_f_hid_opts(item); + int ret; + unsigned int tmp; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + /* parse into a wider type first */ + ret = kstrtouint(page, 0, &tmp); + if (ret) + goto end; + + /* range-check against unsigned char max */ + if (tmp > 255) { + ret = -EINVAL; + goto end; + } + + opts->interval = (unsigned char)tmp; + opts->interval_user_set = true; + ret = len; + +end: + mutex_unlock(&opts->lock); + return ret; +} + +CONFIGFS_ATTR(f_hid_opts_, interval); + static ssize_t f_hid_opts_dev_show(struct config_item *item, char *page) { struct f_hid_opts *opts = to_f_hid_opts(item); @@ -1422,6 +1489,7 @@ static struct configfs_attribute *hid_attrs[] = { &f_hid_opts_attr_protocol, &f_hid_opts_attr_no_out_endpoint, &f_hid_opts_attr_report_length, + &f_hid_opts_attr_interval, &f_hid_opts_attr_report_desc, &f_hid_opts_attr_dev, NULL, @@ -1464,10 +1532,14 @@ static struct usb_function_instance *hidg_alloc_inst(void) struct usb_function_instance *ret; int status = 0; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); mutex_init(&opts->lock); + + opts->interval = 4; + opts->interval_user_set = false; + opts->func_inst.free_func_inst = hidg_free_inst; ret = &opts->func_inst; @@ -1514,7 +1586,7 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_hidg *hidg = func_to_hidg(f); - cdev_device_del(&hidg->cdev, &hidg->dev); + cdev_device_del(hidg->cdev, &hidg->dev); destroy_workqueue(hidg->workqueue); usb_free_all_descriptors(f); } @@ -1526,7 +1598,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) int ret; /* allocate and initialize one new instance */ - hidg = kzalloc(sizeof(*hidg), GFP_KERNEL); + hidg = kzalloc_obj(*hidg); if (!hidg) return ERR_PTR(-ENOMEM); @@ -1534,6 +1606,16 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) mutex_lock(&opts->lock); + spin_lock_init(&hidg->write_spinlock); + spin_lock_init(&hidg->read_spinlock); + spin_lock_init(&hidg->get_report_spinlock); + init_waitqueue_head(&hidg->write_queue); + init_waitqueue_head(&hidg->read_queue); + init_waitqueue_head(&hidg->get_queue); + init_waitqueue_head(&hidg->get_id_queue); + INIT_LIST_HEAD(&hidg->completed_out_req); + INIT_LIST_HEAD(&hidg->report_list); + device_initialize(&hidg->dev); hidg->dev.release = hidg_release; hidg->dev.class = &hidg_class; @@ -1546,6 +1628,8 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) hidg->bInterfaceProtocol = opts->protocol; hidg->report_length = opts->report_length; hidg->report_desc_length = opts->report_desc_length; + hidg->interval = opts->interval; + hidg->interval_user_set = opts->interval_user_set; if (opts->report_desc) { hidg->report_desc = kmemdup(opts->report_desc, opts->report_desc_length, diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 49b009a7d5d7..d2d07fb49e70 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -425,7 +425,7 @@ static struct usb_function *loopback_alloc(struct usb_function_instance *fi) struct f_loopback *loop; struct f_lb_opts *lb_opts; - loop = kzalloc(sizeof *loop, GFP_KERNEL); + loop = kzalloc_obj(*loop); if (!loop) return ERR_PTR(-ENOMEM); @@ -464,7 +464,7 @@ static void lb_attr_release(struct config_item *item) usb_put_function_instance(&lb_opts->func_inst); } -static struct configfs_item_operations lb_item_ops = { +static const struct configfs_item_operations lb_item_ops = { .release = lb_attr_release, }; @@ -568,7 +568,7 @@ static struct usb_function_instance *loopback_alloc_instance(void) { struct f_lb_opts *lb_opts; - lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL); + lb_opts = kzalloc_obj(*lb_opts); if (!lb_opts) return ERR_PTR(-ENOMEM); mutex_init(&lb_opts->lock); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 2eae8fc2e0db..b7b06cb79ff5 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -180,6 +180,7 @@ #include <linux/kthread.h> #include <linux/sched/signal.h> #include <linux/limits.h> +#include <linux/overflow.h> #include <linux/pagemap.h> #include <linux/rwsem.h> #include <linux/slab.h> @@ -1853,8 +1854,15 @@ static int check_command_size_in_blocks(struct fsg_common *common, int cmnd_size, enum data_direction data_dir, unsigned int mask, int needs_medium, const char *name) { - if (common->curlun) - common->data_size_from_cmnd <<= common->curlun->blkbits; + if (common->curlun) { + if (check_shl_overflow(common->data_size_from_cmnd, + common->curlun->blkbits, + &common->data_size_from_cmnd)) { + common->phase_error = 1; + return -EINVAL; + } + } + return check_command(common, cmnd_size, data_dir, mask, needs_medium, name); } @@ -2142,8 +2150,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: @@ -2699,7 +2707,7 @@ static void fsg_lun_release(struct device *dev) static struct fsg_common *fsg_common_setup(struct fsg_common *common) { if (!common) { - common = kzalloc(sizeof(*common), GFP_KERNEL); + common = kzalloc_obj(*common); if (!common) return ERR_PTR(-ENOMEM); common->free_storage_on_release = 1; @@ -2740,7 +2748,7 @@ int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n) struct fsg_buffhd *bh, *buffhds; int i; - buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL); + buffhds = kzalloc_objs(*buffhds, n); if (!buffhds) return -ENOMEM; @@ -2887,7 +2895,7 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, return -EINVAL; } - lun = kzalloc(sizeof(*lun), GFP_KERNEL); + lun = kzalloc_obj(*lun); if (!lun) return -ENOMEM; @@ -3153,7 +3161,7 @@ static void fsg_lun_attr_release(struct config_item *item) kfree(lun_opts); } -static struct configfs_item_operations fsg_lun_item_ops = { +static const struct configfs_item_operations fsg_lun_item_ops = { .release = fsg_lun_attr_release, }; @@ -3311,7 +3319,7 @@ static struct config_group *fsg_lun_make(struct config_group *group, goto out; } - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) { ret = -ENOMEM; goto out; @@ -3369,7 +3377,7 @@ static void fsg_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations fsg_item_ops = { +static const struct configfs_item_operations fsg_item_ops = { .release = fsg_attr_release, }; @@ -3462,7 +3470,7 @@ static struct configfs_attribute *fsg_attrs[] = { NULL, }; -static struct configfs_group_operations fsg_group_ops = { +static const struct configfs_group_operations fsg_group_ops = { .make_group = fsg_lun_make, .drop_item = fsg_lun_drop, }; @@ -3489,7 +3497,7 @@ static struct usb_function_instance *fsg_alloc_inst(void) struct fsg_lun_config config; int rc; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); mutex_init(&opts->lock); @@ -3554,7 +3562,7 @@ static struct usb_function *fsg_alloc(struct usb_function_instance *fi) struct fsg_common *common = opts->common; struct fsg_dev *fsg; - fsg = kzalloc(sizeof(*fsg), GFP_KERNEL); + fsg = kzalloc_obj(*fsg); if (unlikely(!fsg)) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h index 3b8c4ce2a40a..82ecd3fedb3a 100644 --- a/drivers/usb/gadget/function/f_mass_storage.h +++ b/drivers/usb/gadget/function/f_mass_storage.h @@ -110,7 +110,7 @@ struct fsg_config { }; static inline struct fsg_opts * -fsg_opts_from_func_inst(const struct usb_function_instance *fi) +fsg_opts_from_func_inst(struct usb_function_instance *fi) { return container_of(fi, struct fsg_opts, func_inst); } diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index da82598fcef8..4d9e4bd700d8 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -875,6 +875,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) struct usb_composite_dev *cdev = c->cdev; struct f_midi *midi = func_to_midi(f); struct usb_string *us; + struct f_midi_opts *opts; int status, n, jack = 1, i = 0, endpoint_descriptor_index = 0; midi->gadget = cdev->gadget; @@ -883,6 +884,10 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) if (status < 0) goto fail_register; + opts = container_of(f->fi, struct f_midi_opts, func_inst); + if (opts->interface_string) + midi_string_defs[STRING_FUNC_IDX].s = opts->interface_string; + /* maybe allocate device-global string ID */ us = usb_gstrings_attach(c->cdev, midi_strings, ARRAY_SIZE(midi_string_defs)); @@ -926,8 +931,7 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) goto fail; /* allocate temporary function list */ - midi_function = kcalloc((MAX_PORTS * 4) + 11, sizeof(*midi_function), - GFP_KERNEL); + midi_function = kzalloc_objs(*midi_function, (MAX_PORTS * 4) + 11); if (!midi_function) { status = -ENOMEM; goto fail; @@ -1086,7 +1090,7 @@ static void midi_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations midi_item_ops = { +static const struct configfs_item_operations midi_item_ops = { .release = midi_attr_release, }; @@ -1178,59 +1182,60 @@ end: \ \ CONFIGFS_ATTR(f_midi_opts_, name); +#define F_MIDI_OPT_STRING(name) \ +static ssize_t f_midi_opts_##name##_show(struct config_item *item, char *page) \ +{ \ + struct f_midi_opts *opts = to_f_midi_opts(item); \ + ssize_t result; \ + \ + mutex_lock(&opts->lock); \ + if (opts->name) { \ + result = strscpy(page, opts->name, PAGE_SIZE); \ + } else { \ + page[0] = 0; \ + result = 0; \ + } \ + \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_midi_opts_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct f_midi_opts *opts = to_f_midi_opts(item); \ + int ret; \ + char *c; \ + \ + mutex_lock(&opts->lock); \ + if (opts->refcnt > 1) { \ + ret = -EBUSY; \ + goto end; \ + } \ + \ + c = kstrndup(page, len, GFP_KERNEL); \ + if (!c) { \ + ret = -ENOMEM; \ + goto end; \ + } \ + kfree(opts->name); \ + opts->name = c; \ + ret = len; \ +end: \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(f_midi_opts_, name) + F_MIDI_OPT_SIGNED(index, true, SNDRV_CARDS); F_MIDI_OPT(buflen, false, 0); F_MIDI_OPT(qlen, false, 0); F_MIDI_OPT(in_ports, true, MAX_PORTS); F_MIDI_OPT(out_ports, true, MAX_PORTS); - -static ssize_t f_midi_opts_id_show(struct config_item *item, char *page) -{ - struct f_midi_opts *opts = to_f_midi_opts(item); - ssize_t result; - - mutex_lock(&opts->lock); - if (opts->id) { - result = strscpy(page, opts->id, PAGE_SIZE); - } else { - page[0] = 0; - result = 0; - } - - mutex_unlock(&opts->lock); - - return result; -} - -static ssize_t f_midi_opts_id_store(struct config_item *item, - const char *page, size_t len) -{ - struct f_midi_opts *opts = to_f_midi_opts(item); - int ret; - char *c; - - mutex_lock(&opts->lock); - if (opts->refcnt > 1) { - ret = -EBUSY; - goto end; - } - - c = kstrndup(page, len, GFP_KERNEL); - if (!c) { - ret = -ENOMEM; - goto end; - } - if (opts->id_allocated) - kfree(opts->id); - opts->id = c; - opts->id_allocated = true; - ret = len; -end: - mutex_unlock(&opts->lock); - return ret; -} - -CONFIGFS_ATTR(f_midi_opts_, id); +F_MIDI_OPT_STRING(id); +F_MIDI_OPT_STRING(interface_string); static struct configfs_attribute *midi_attrs[] = { &f_midi_opts_attr_index, @@ -1239,6 +1244,7 @@ static struct configfs_attribute *midi_attrs[] = { &f_midi_opts_attr_in_ports, &f_midi_opts_attr_out_ports, &f_midi_opts_attr_id, + &f_midi_opts_attr_interface_string, NULL, }; @@ -1262,8 +1268,8 @@ static void f_midi_free_inst(struct usb_function_instance *f) mutex_unlock(&opts->lock); if (free) { - if (opts->id_allocated) - kfree(opts->id); + kfree(opts->id); + kfree(opts->interface_string); kfree(opts); } } @@ -1272,14 +1278,15 @@ static struct usb_function_instance *f_midi_alloc_inst(void) { struct f_midi_opts *opts; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); mutex_init(&opts->lock); opts->func_inst.free_func_inst = f_midi_free_inst; opts->index = SNDRV_DEFAULT_IDX1; - opts->id = SNDRV_DEFAULT_STR1; + opts->id = NULL; + opts->interface_string = NULL; opts->buflen = 512; opts->qlen = 32; opts->in_ports = 1; @@ -1353,8 +1360,7 @@ static struct usb_function *f_midi_alloc(struct usb_function_instance *fi) } /* allocate and initialize one new instance */ - midi = kzalloc(struct_size(midi, in_ports_array, opts->in_ports), - GFP_KERNEL); + midi = kzalloc_flex(*midi, in_ports_array, opts->in_ports); if (!midi) { status = -ENOMEM; goto setup_fail; diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index 12e866fb311d..19fdac024343 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -475,7 +475,7 @@ static void reply_ump_stream_ep_info(struct f_midi2_ep *ep) /* reply a UMP EP device info */ static void reply_ump_stream_ep_device(struct f_midi2_ep *ep) { - struct snd_ump_stream_msg_devince_info rep = { + struct snd_ump_stream_msg_device_info rep = { .type = UMP_MSG_TYPE_STREAM, .status = UMP_STREAM_MSG_STATUS_DEVICE_INFO, .manufacture_id = ep->info.manufacturer, @@ -1187,8 +1187,7 @@ static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep, return -ENODEV; usb_ep->complete = complete; - usb_ep->reqs = kcalloc(midi2->info.num_reqs, sizeof(*usb_ep->reqs), - GFP_KERNEL); + usb_ep->reqs = kzalloc_objs(*usb_ep->reqs, midi2->info.num_reqs); if (!usb_ep->reqs) return -ENOMEM; for (i = 0; i < midi2->info.num_reqs; i++) { @@ -1542,9 +1541,9 @@ static int f_midi2_create_card(struct f_midi2 *midi2) return err; midi2->card = card; - strcpy(card->driver, "f_midi2"); - strcpy(card->shortname, "MIDI 2.0 Gadget"); - strcpy(card->longname, "MIDI 2.0 Gadget"); + strscpy(card->driver, "f_midi2"); + strscpy(card->shortname, "MIDI 2.0 Gadget"); + strscpy(card->longname, "MIDI 2.0 Gadget"); id = 0; for (i = 0; i < midi2->num_eps; i++) { @@ -1599,6 +1598,7 @@ static int f_midi2_create_card(struct f_midi2 *midi2) strscpy(fb->info.name, ump_fb_name(b), sizeof(fb->info.name)); } + snd_ump_update_group_attrs(ump); } for (i = 0; i < midi2->num_eps; i++) { @@ -1736,9 +1736,12 @@ static int f_midi2_create_usb_configs(struct f_midi2 *midi2, case USB_SPEED_HIGH: midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(512); midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(512); - for (i = 0; i < midi2->num_eps; i++) + for (i = 0; i < midi2->num_eps; i++) { midi2_midi2_ep_out_desc[i].wMaxPacketSize = cpu_to_le16(512); + midi2_midi2_ep_in_desc[i].wMaxPacketSize = + cpu_to_le16(512); + } fallthrough; case USB_SPEED_FULL: midi1_in_eps = midi2_midi1_ep_in_descs; @@ -1747,9 +1750,12 @@ static int f_midi2_create_usb_configs(struct f_midi2 *midi2, case USB_SPEED_SUPER: midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(1024); midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(1024); - for (i = 0; i < midi2->num_eps; i++) + for (i = 0; i < midi2->num_eps; i++) { midi2_midi2_ep_out_desc[i].wMaxPacketSize = cpu_to_le16(1024); + midi2_midi2_ep_in_desc[i].wMaxPacketSize = + cpu_to_le16(1024); + } midi1_in_eps = midi2_midi1_ep_in_ss_descs; midi1_out_eps = midi2_midi1_ep_out_ss_descs; break; @@ -2309,7 +2315,7 @@ static void f_midi2_block_opts_release(struct config_item *item) kfree(opts); } -static struct configfs_item_operations f_midi2_block_item_ops = { +static const struct configfs_item_operations f_midi2_block_item_ops = { .release = f_midi2_block_opts_release, }; @@ -2333,7 +2339,7 @@ static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts, goto out; } - block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL); + block_opts = kzalloc_obj(*block_opts); if (!block_opts) { ret = -ENOMEM; goto out; @@ -2472,11 +2478,11 @@ static void f_midi2_ep_opts_release(struct config_item *item) kfree(opts); } -static struct configfs_item_operations f_midi2_ep_item_ops = { +static const struct configfs_item_operations f_midi2_ep_item_ops = { .release = f_midi2_ep_opts_release, }; -static struct configfs_group_operations f_midi2_ep_group_ops = { +static const struct configfs_group_operations f_midi2_ep_group_ops = { .make_group = f_midi2_opts_block_make, .drop_item = f_midi2_opts_block_drop, }; @@ -2495,7 +2501,7 @@ static int f_midi2_ep_opts_create(struct f_midi2_opts *opts, { struct f_midi2_ep_opts *ep_opts; - ep_opts = kzalloc(sizeof(*ep_opts), GFP_KERNEL); + ep_opts = kzalloc_obj(*ep_opts); if (!ep_opts) return -ENOMEM; @@ -2611,11 +2617,11 @@ static void f_midi2_opts_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations f_midi2_item_ops = { +static const struct configfs_item_operations f_midi2_item_ops = { .release = f_midi2_opts_release, }; -static struct configfs_group_operations f_midi2_group_ops = { +static const struct configfs_group_operations f_midi2_group_ops = { .make_group = f_midi2_opts_ep_make, .drop_item = f_midi2_opts_ep_drop, }; @@ -2645,7 +2651,7 @@ static struct usb_function_instance *f_midi2_alloc_inst(void) struct f_midi2_block_opts *block_opts; int ret; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); @@ -2806,7 +2812,7 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) struct f_midi2_block *bp; int i, num_eps, blk; - midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL); + midi2 = kzalloc_obj(*midi2); if (!midi2) return ERR_PTR(-ENOMEM); @@ -2848,8 +2854,8 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi) } } - midi2->string_defs = kcalloc(midi2->total_blocks + 1, - sizeof(*midi2->string_defs), GFP_KERNEL); + midi2->string_defs = kzalloc_objs(*midi2->string_defs, + midi2->total_blocks + 1); if (!midi2->string_defs) { do_f_midi2_free(midi2, opts); return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index f60576a65ca6..c5bf8a448d64 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -11,6 +11,7 @@ * Copyright (C) 2008 Nokia Corporation */ +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/module.h> @@ -20,6 +21,7 @@ #include <linux/string_choices.h> #include <linux/usb/cdc.h> +#include <linux/usb/gadget.h> #include "u_ether.h" #include "u_ether_configfs.h" @@ -1208,8 +1210,8 @@ parse_ntb: block_len = get_ncm(&tmp, opts->block_length); /* (d)wBlockLength */ - if (block_len > ntb_max) { - INFO(port->func.config->cdev, "OUT size exceeded\n"); + if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max)) { + INFO(port->func.config->cdev, "Bad block length: %#X\n", block_len); goto err; } @@ -1436,39 +1438,42 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_ncm_opts *ncm_opts; + struct usb_os_desc_table *os_desc_table __free(kfree) = NULL; + struct net_device *net __free(detach_gadget) = NULL; + struct usb_request *request __free(free_usb_request) = NULL; + if (!can_support_ecm(cdev->gadget)) return -EINVAL; ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); if (cdev->use_os_string) { - f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), - GFP_KERNEL); - if (!f->os_desc_table) + os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL); + if (!os_desc_table) return -ENOMEM; - f->os_desc_n = 1; - f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc; } - mutex_lock(&ncm_opts->lock); - gether_set_gadget(ncm_opts->net, cdev->gadget); - if (!ncm_opts->bound) { - ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN); - status = gether_register_netdev(ncm_opts->net); - } - mutex_unlock(&ncm_opts->lock); - - if (status) - goto fail; + scoped_guard(mutex, &ncm_opts->lock) + if (ncm_opts->bind_count == 0) { + if (!device_is_registered(&ncm_opts->net->dev)) { + ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN); + gether_set_gadget(ncm_opts->net, cdev->gadget); + status = gether_register_netdev(ncm_opts->net); + } else + status = gether_attach_gadget(ncm_opts->net, cdev->gadget); + + if (status) + return status; + net = ncm_opts->net; + } - ncm_opts->bound = true; + ncm_string_defs[1].s = ncm->ethaddr; us = usb_gstrings_attach(cdev, ncm_strings, ARRAY_SIZE(ncm_string_defs)); - if (IS_ERR(us)) { - status = PTR_ERR(us); - goto fail; - } + if (IS_ERR(us)) + return PTR_ERR(us); + ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id; ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id; ncm_data_intf.iInterface = us[STRING_DATA_IDX].id; @@ -1478,20 +1483,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; ncm->ctrl_id = status; ncm_iad_desc.bFirstInterface = status; ncm_control_intf.bInterfaceNumber = status; ncm_union_desc.bMasterInterface0 = status; - if (cdev->use_os_string) - f->os_desc_table[0].if_id = - ncm_iad_desc.bFirstInterface; - status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; ncm->data_id = status; ncm_data_nop_intf.bInterfaceNumber = status; @@ -1500,35 +1501,31 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size); - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_in_desc); if (!ep) - goto fail; + return -ENODEV; ncm->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc); if (!ep) - goto fail; + return -ENODEV; ncm->port.out_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc); if (!ep) - goto fail; + return -ENODEV; ncm->notify = ep; - status = -ENOMEM; - /* allocate notification request and buffer */ - ncm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (!ncm->notify_req) - goto fail; - ncm->notify_req->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL); - if (!ncm->notify_req->buf) - goto fail; - ncm->notify_req->context = ncm; - ncm->notify_req->complete = ncm_notify_complete; + request = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!request) + return -ENOMEM; + request->buf = kmalloc(NCM_STATUS_BYTECOUNT, GFP_KERNEL); + if (!request->buf) + return -ENOMEM; + request->context = ncm; + request->complete = ncm_notify_complete; /* * support all relevant hardware speeds... we expect that when @@ -1548,7 +1545,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function, ncm_ss_function, ncm_ss_function); if (status) - goto fail; + return status; /* * NOTE: all that is done without knowing or caring about @@ -1559,26 +1556,23 @@ 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); + + if (cdev->use_os_string) { + os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc; + os_desc_table[0].if_id = ncm_iad_desc.bFirstInterface; + f->os_desc_table = no_free_ptr(os_desc_table); + f->os_desc_n = 1; + } + ncm->notify_req = no_free_ptr(request); + + ncm_opts->bind_count++; + retain_and_null_ptr(net); DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n", ncm->port.in_ep->name, ncm->port.out_ep->name, ncm->notify->name); return 0; - -fail: - kfree(f->os_desc_table); - f->os_desc_n = 0; - - if (ncm->notify_req) { - kfree(ncm->notify_req->buf); - usb_ep_free_request(ncm->notify, ncm->notify_req); - } - - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item) @@ -1666,7 +1660,7 @@ static void ncm_free_inst(struct usb_function_instance *f) struct f_ncm_opts *opts; opts = container_of(f, struct f_ncm_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -1681,7 +1675,7 @@ static struct usb_function_instance *ncm_alloc_inst(void) char *names[1]; struct config_group *ncm_interf_group; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id; @@ -1729,9 +1723,12 @@ static void ncm_free(struct usb_function *f) static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_ncm *ncm = func_to_ncm(f); + struct f_ncm_opts *ncm_opts; DBG(c->cdev, "ncm unbind\n"); + ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst); + hrtimer_cancel(&ncm->task_timer); kfree(f->os_desc_table); @@ -1747,6 +1744,10 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) kfree(ncm->notify_req->buf); usb_ep_free_request(ncm->notify, ncm->notify_req); + + ncm_opts->bind_count--; + if (ncm_opts->bind_count == 0) + gether_detach_gadget(ncm_opts->net); } static struct usb_function *ncm_alloc(struct usb_function_instance *fi) @@ -1772,7 +1773,6 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi) mutex_unlock(&opts->lock); return ERR_PTR(-EINVAL); } - ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr; spin_lock_init(&ncm->lock); ncm_reset_values(ncm); diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index 1305e2326cdf..715bb0decd3b 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -390,7 +390,7 @@ static void obex_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations obex_item_ops = { +static const struct configfs_item_operations obex_item_ops = { .release = obex_attr_release, }; @@ -426,7 +426,7 @@ static struct usb_function_instance *obex_alloc_inst(void) struct f_serial_opts *opts; int ret; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); @@ -461,7 +461,7 @@ static struct usb_function *obex_alloc(struct usb_function_instance *fi) struct f_serial_opts *opts; /* allocate and initialize one new instance */ - obex = kzalloc(sizeof(*obex), GFP_KERNEL); + obex = kzalloc_obj(*obex); if (!obex) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index 0aa9e8224cae..b1ee9a7c2e94 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -333,6 +333,15 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) if (unlikely(!skb)) break; + if (unlikely(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS)) { + /* Frame count from host exceeds frags[] capacity */ + dev_kfree_skb_any(skb); + if (fp->rx.skb == skb) + fp->rx.skb = NULL; + dev->stats.rx_length_errors++; + break; + } + if (skb->len == 0) { /* First fragment */ skb->protocol = htons(ETH_P_PHONET); skb_reset_mac_header(skb); @@ -585,7 +594,7 @@ static void phonet_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations phonet_item_ops = { +static const struct configfs_item_operations phonet_item_ops = { .release = phonet_attr_release, }; @@ -623,7 +632,7 @@ static struct usb_function_instance *phonet_alloc_inst(void) { struct f_phonet_opts *opts; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); @@ -669,7 +678,7 @@ static struct usb_function *phonet_alloc(struct usb_function_instance *fi) struct f_phonet *fp; struct f_phonet_opts *opts; - fp = kzalloc(struct_size(fp, out_reqv, phonet_rxq_size), GFP_KERNEL); + fp = kzalloc_flex(*fp, out_reqv, phonet_rxq_size); if (!fp) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index d295ade8fa67..e4f7828ae75d 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -1220,7 +1220,7 @@ static void printer_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations printer_item_ops = { +static const struct configfs_item_operations printer_item_ops = { .release = printer_attr_release, }; @@ -1374,7 +1374,7 @@ static struct usb_function_instance *gprinter_alloc_inst(void) struct usb_function_instance *ret; int status = 0; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); @@ -1482,7 +1482,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENOENT); } - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) { mutex_unlock(&opts->lock); return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 7cec19d65fb5..7de1c5f8e326 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -11,6 +11,7 @@ /* #define VERBOSE_DEBUG */ +#include <linux/cleanup.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/module.h> @@ -19,6 +20,8 @@ #include <linux/atomic.h> +#include <linux/usb/gadget.h> + #include "u_ether.h" #include "u_ether_configfs.h" #include "u_rndis.h" @@ -662,6 +665,9 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_rndis_opts *rndis_opts; + struct usb_os_desc_table *os_desc_table __free(kfree) = NULL; + struct net_device *net __free(detach_gadget) = NULL; + struct usb_request *request __free(free_usb_request) = NULL; if (!can_support_rndis(c)) return -EINVAL; @@ -669,39 +675,33 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst); if (cdev->use_os_string) { - f->os_desc_table = kzalloc(sizeof(*f->os_desc_table), - GFP_KERNEL); - if (!f->os_desc_table) + os_desc_table = kzalloc_obj(*os_desc_table); + if (!os_desc_table) return -ENOMEM; - f->os_desc_n = 1; - f->os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc; } - rndis_iad_descriptor.bFunctionClass = rndis_opts->class; - rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; - rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; - - /* - * in drivers/usb/gadget/configfs.c:configfs_composite_bind() - * configurations are bound in sequence with list_for_each_entry, - * in each configuration its functions are bound in sequence - * with list_for_each_entry, so we assume no race condition - * with regard to rndis_opts->bound access - */ - if (!rndis_opts->bound) { - gether_set_gadget(rndis_opts->net, cdev->gadget); - status = gether_register_netdev(rndis_opts->net); - if (status) - goto fail; - rndis_opts->bound = true; + scoped_guard(mutex, &rndis_opts->lock) { + rndis_iad_descriptor.bFunctionClass = rndis_opts->class; + rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; + rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; + + if (rndis_opts->bind_count == 0 && !rndis_opts->borrowed_net) { + if (!device_is_registered(&rndis_opts->net->dev)) { + gether_set_gadget(rndis_opts->net, cdev->gadget); + status = gether_register_netdev(rndis_opts->net); + } else + status = gether_attach_gadget(rndis_opts->net, cdev->gadget); + + if (status) + return status; + net = rndis_opts->net; + } } us = usb_gstrings_attach(cdev, rndis_strings, ARRAY_SIZE(rndis_string_defs)); - if (IS_ERR(us)) { - status = PTR_ERR(us); - goto fail; - } + if (IS_ERR(us)) + return PTR_ERR(us); rndis_control_intf.iInterface = us[0].id; rndis_data_intf.iInterface = us[1].id; rndis_iad_descriptor.iFunction = us[2].id; @@ -709,36 +709,30 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; rndis->ctrl_id = status; rndis_iad_descriptor.bFirstInterface = status; rndis_control_intf.bInterfaceNumber = status; rndis_union_desc.bMasterInterface0 = status; - if (cdev->use_os_string) - f->os_desc_table[0].if_id = - rndis_iad_descriptor.bFirstInterface; - status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; rndis->data_id = status; rndis_data_intf.bInterfaceNumber = status; rndis_union_desc.bSlaveInterface0 = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc); if (!ep) - goto fail; + return -ENODEV; rndis->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); if (!ep) - goto fail; + return -ENODEV; rndis->port.out_ep = ep; /* NOTE: a status/notification endpoint is, strictly speaking, @@ -747,21 +741,19 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) */ ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc); if (!ep) - goto fail; + return -ENODEV; rndis->notify = ep; - status = -ENOMEM; - /* allocate notification request and buffer */ - rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL); - if (!rndis->notify_req) - goto fail; - rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); - if (!rndis->notify_req->buf) - goto fail; - rndis->notify_req->length = STATUS_BYTECOUNT; - rndis->notify_req->context = rndis; - rndis->notify_req->complete = rndis_response_complete; + request = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!request) + return -ENOMEM; + request->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL); + if (!request->buf) + return -ENOMEM; + request->length = STATUS_BYTECOUNT; + request->context = rndis; + request->complete = rndis_response_complete; /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -778,7 +770,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function, eth_ss_function, eth_ss_function); if (status) - goto fail; + return status; rndis->port.open = rndis_open; rndis->port.close = rndis_close; @@ -789,10 +781,22 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) if (rndis->manufacturer && rndis->vendorID && rndis_set_param_vendor(rndis->params, rndis->vendorID, rndis->manufacturer)) { - status = -EINVAL; - goto fail_free_descs; + usb_free_all_descriptors(f); + return -EINVAL; } + if (cdev->use_os_string) { + os_desc_table[0].os_desc = &rndis_opts->rndis_os_desc; + os_desc_table[0].if_id = rndis_iad_descriptor.bFirstInterface; + f->os_desc_table = no_free_ptr(os_desc_table); + f->os_desc_n = 1; + + } + rndis->notify_req = no_free_ptr(request); + + rndis_opts->bind_count++; + retain_and_null_ptr(net); + /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code * until we're activated via set_alt(). @@ -802,21 +806,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->port.in_ep->name, rndis->port.out_ep->name, rndis->notify->name); return 0; - -fail_free_descs: - usb_free_all_descriptors(f); -fail: - kfree(f->os_desc_table); - f->os_desc_n = 0; - - if (rndis->notify_req) { - kfree(rndis->notify_req->buf); - usb_ep_free_request(rndis->notify, rndis->notify_req); - } - - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) @@ -824,11 +813,11 @@ void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net) struct f_rndis_opts *opts; opts = container_of(f, struct f_rndis_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); - opts->borrowed_net = opts->bound = true; + opts->borrowed_net = true; opts->net = net; } EXPORT_SYMBOL_GPL(rndis_borrow_net); @@ -886,7 +875,7 @@ static void rndis_free_inst(struct usb_function_instance *f) opts = container_of(f, struct f_rndis_opts, func_inst); if (!opts->borrowed_net) { - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -903,7 +892,7 @@ static struct usb_function_instance *rndis_alloc_inst(void) char *names[1]; struct config_group *rndis_interf_group; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); opts->rndis_os_desc.ext_compat_id = opts->rndis_ext_compat_id; @@ -955,6 +944,9 @@ static void rndis_free(struct usb_function *f) static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rndis *rndis = func_to_rndis(f); + struct f_rndis_opts *rndis_opts; + + rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst); kfree(f->os_desc_table); f->os_desc_n = 0; @@ -962,6 +954,10 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) kfree(rndis->notify_req->buf); usb_ep_free_request(rndis->notify, rndis->notify_req); + + rndis_opts->bind_count--; + if (rndis_opts->bind_count == 0 && !rndis_opts->borrowed_net) + gether_detach_gadget(rndis_opts->net); } static struct usb_function *rndis_alloc(struct usb_function_instance *fi) @@ -971,7 +967,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) struct rndis_params *params; /* allocate and initialize one new instance */ - rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); + rndis = kzalloc_obj(*rndis); if (!rndis) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 8f7e7a2b2ff2..ae8813c67bc7 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -260,7 +260,7 @@ static void serial_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations serial_item_ops = { +static const struct configfs_item_operations serial_item_ops = { .release = serial_attr_release, }; @@ -317,7 +317,7 @@ static struct usb_function_instance *gser_alloc_inst(void) struct f_serial_opts *opts; int ret; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); @@ -364,13 +364,19 @@ static void gser_suspend(struct usb_function *f) gserial_suspend(&gser->port); } +static int gser_get_status(struct usb_function *f) +{ + return (f->func_wakeup_armed ? USB_INTRF_STAT_FUNC_RW : 0) | + USB_INTRF_STAT_FUNC_RW_CAP; +} + static struct usb_function *gser_alloc(struct usb_function_instance *fi) { struct f_gser *gser; struct f_serial_opts *opts; /* allocate and initialize one new instance */ - gser = kzalloc(sizeof(*gser), GFP_KERNEL); + gser = kzalloc_obj(*gser); if (!gser) return ERR_PTR(-ENOMEM); @@ -387,6 +393,7 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi) gser->port.func.free_func = gser_free; gser->port.func.resume = gser_resume; gser->port.func.suspend = gser_suspend; + gser->port.func.get_status = gser_get_status; return &gser->port.func; } diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index ec5fd25020fd..d2b707e41669 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -46,6 +46,7 @@ struct f_sourcesink { unsigned isoc_mult; unsigned isoc_maxburst; unsigned buflen; + unsigned bulk_maxburst; unsigned bulk_qlen; unsigned iso_qlen; }; @@ -328,6 +329,12 @@ sourcesink_bind(struct usb_configuration *c, struct usb_function *f) source_sink_intf_alt0.bInterfaceNumber = id; source_sink_intf_alt1.bInterfaceNumber = id; + if (ss->bulk_maxburst > 15) + ss->bulk_maxburst = 15; + + ss_source_comp_desc.bMaxBurst = ss->bulk_maxburst; + ss_sink_comp_desc.bMaxBurst = ss->bulk_maxburst; + /* allocate bulk endpoints */ ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc); if (!ss->in_ep) { @@ -837,7 +844,7 @@ static struct usb_function *source_sink_alloc_func( struct f_sourcesink *ss; struct f_ss_opts *ss_opts; - ss = kzalloc(sizeof(*ss), GFP_KERNEL); + ss = kzalloc_obj(*ss); if (!ss) return ERR_PTR(-ENOMEM); @@ -853,6 +860,7 @@ static struct usb_function *source_sink_alloc_func( ss->isoc_mult = ss_opts->isoc_mult; ss->isoc_maxburst = ss_opts->isoc_maxburst; ss->buflen = ss_opts->bulk_buflen; + ss->bulk_maxburst = ss_opts->bulk_maxburst; ss->bulk_qlen = ss_opts->bulk_qlen; ss->iso_qlen = ss_opts->iso_qlen; @@ -882,7 +890,7 @@ static void ss_attr_release(struct config_item *item) usb_put_function_instance(&ss_opts->func_inst); } -static struct configfs_item_operations ss_item_ops = { +static const struct configfs_item_operations ss_item_ops = { .release = ss_attr_release, }; @@ -1101,6 +1109,49 @@ end: CONFIGFS_ATTR(f_ss_opts_, isoc_maxburst); +static ssize_t f_ss_opts_bulk_maxburst_show(struct config_item *item, char *page) +{ + struct f_ss_opts *opts = to_f_ss_opts(item); + int result; + + mutex_lock(&opts->lock); + result = sysfs_emit(page, "%u\n", opts->bulk_maxburst); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t f_ss_opts_bulk_maxburst_store(struct config_item *item, + const char *page, size_t len) +{ + struct f_ss_opts *opts = to_f_ss_opts(item); + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + if (num > 15) { + ret = -EINVAL; + goto end; + } + + opts->bulk_maxburst = num; + ret = len; +end: + mutex_unlock(&opts->lock); + return ret; +} + +CONFIGFS_ATTR(f_ss_opts_, bulk_maxburst); + static ssize_t f_ss_opts_bulk_buflen_show(struct config_item *item, char *page) { struct f_ss_opts *opts = to_f_ss_opts(item); @@ -1222,6 +1273,7 @@ static struct configfs_attribute *ss_attrs[] = { &f_ss_opts_attr_isoc_mult, &f_ss_opts_attr_isoc_maxburst, &f_ss_opts_attr_bulk_buflen, + &f_ss_opts_attr_bulk_maxburst, &f_ss_opts_attr_bulk_qlen, &f_ss_opts_attr_iso_qlen, NULL, @@ -1245,7 +1297,7 @@ static struct usb_function_instance *source_sink_alloc_inst(void) { struct f_ss_opts *ss_opts; - ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL); + ss_opts = kzalloc_obj(*ss_opts); if (!ss_opts) return ERR_PTR(-ENOMEM); mutex_init(&ss_opts->lock); diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index ea3fdd842462..6e3265b8a3a0 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -6,6 +6,7 @@ * Copyright (C) 2008 Nokia Corporation */ +#include <linux/cleanup.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/module.h> @@ -298,25 +299,22 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) struct usb_ep *ep; struct f_gether_opts *gether_opts; + struct net_device *net __free(detach_gadget) = NULL; gether_opts = container_of(f->fi, struct f_gether_opts, func_inst); - /* - * in drivers/usb/gadget/configfs.c:configfs_composite_bind() - * configurations are bound in sequence with list_for_each_entry, - * in each configuration its functions are bound in sequence - * with list_for_each_entry, so we assume no race condition - * with regard to gether_opts->bound access - */ - if (!gether_opts->bound) { - mutex_lock(&gether_opts->lock); - gether_set_gadget(gether_opts->net, cdev->gadget); - status = gether_register_netdev(gether_opts->net); - mutex_unlock(&gether_opts->lock); - if (status) - return status; - gether_opts->bound = true; - } + scoped_guard(mutex, &gether_opts->lock) + if (gether_opts->bind_count == 0 && !gether_opts->bound) { + if (!device_is_registered(&gether_opts->net->dev)) { + gether_set_gadget(gether_opts->net, cdev->gadget); + status = gether_register_netdev(gether_opts->net); + } else + status = gether_attach_gadget(gether_opts->net, cdev->gadget); + + if (status) + return status; + net = gether_opts->net; + } us = usb_gstrings_attach(cdev, geth_strings, ARRAY_SIZE(geth_string_defs)); @@ -329,20 +327,18 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) - goto fail; + return status; subset_data_intf.bInterfaceNumber = status; - status = -ENODEV; - /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc); if (!ep) - goto fail; + return -ENODEV; geth->port.in_ep = ep; ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc); if (!ep) - goto fail; + return -ENODEV; geth->port.out_ep = ep; /* support all relevant hardware speeds... we expect that when @@ -360,21 +356,19 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function, ss_eth_function, ss_eth_function); if (status) - goto fail; + return status; /* NOTE: all that is done without knowing or caring about * the network link ... which is unavailable to this code * until we're activated via set_alt(). */ + gether_opts->bind_count++; + retain_and_null_ptr(net); + DBG(cdev, "CDC Subset: IN/%s OUT/%s\n", geth->port.in_ep->name, geth->port.out_ep->name); return 0; - -fail: - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); - - return status; } static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item) @@ -417,7 +411,7 @@ static void geth_free_inst(struct usb_function_instance *f) struct f_gether_opts *opts; opts = container_of(f, struct f_gether_opts, func_inst); - if (opts->bound) + if (device_is_registered(&opts->net->dev)) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); @@ -428,7 +422,7 @@ static struct usb_function_instance *geth_alloc_inst(void) { struct f_gether_opts *opts; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); mutex_init(&opts->lock); @@ -449,15 +443,28 @@ static struct usb_function_instance *geth_alloc_inst(void) static void geth_free(struct usb_function *f) { struct f_gether *eth; + struct f_gether_opts *opts; + + opts = container_of(f->fi, struct f_gether_opts, func_inst); eth = func_to_geth(f); + scoped_guard(mutex, &opts->lock) + opts->refcnt--; kfree(eth); } static void geth_unbind(struct usb_configuration *c, struct usb_function *f) { + struct f_gether_opts *opts; + + opts = container_of(f->fi, struct f_gether_opts, func_inst); + geth_string_defs[0].id = 0; usb_free_all_descriptors(f); + + opts->bind_count--; + if (opts->bind_count == 0 && !opts->bound) + gether_detach_gadget(opts->net); } static struct usb_function *geth_alloc(struct usb_function_instance *fi) @@ -467,7 +474,7 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi) int status; /* allocate and initialize one new instance */ - geth = kzalloc(sizeof(*geth), GFP_KERNEL); + geth = kzalloc_obj(*geth); if (!geth) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 5a2e1237f85c..34d9f49e9987 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -1222,12 +1222,19 @@ static void usbg_submit_cmd(struct usbg_cmd *cmd) se_cmd = &cmd->se_cmd; tpg = cmd->fu->tpg; tv_nexus = tpg->tpg_nexus; + if (!tv_nexus) { + struct usb_gadget *gadget = fuas_to_gadget(cmd->fu); + + dev_err(&gadget->dev, "Missing nexus, ignoring command\n"); + return; + } + dir = get_cmd_dir(cmd->cmd_buf); 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->sense_iu.sense, cmd->unpacked_lun, cmd->data_len, cmd->prio_attr, dir, flags); return; @@ -1389,6 +1396,7 @@ static int usbg_submit_command(struct f_uas *fu, struct usb_request *req) cmd->tmr_func = 0; cmd->tmr_rsp = RC_RESPONSE_UNKNOWN; cmd->flags = 0; + cmd->data_len = 0; cmd_iu = (struct command_iu *)iu; @@ -1482,6 +1490,13 @@ static void bot_cmd_work(struct work_struct *work) se_cmd = &cmd->se_cmd; tpg = cmd->fu->tpg; tv_nexus = tpg->tpg_nexus; + if (!tv_nexus) { + struct usb_gadget *gadget = fuas_to_gadget(cmd->fu); + + dev_err(&gadget->dev, "Missing nexus, ignoring command\n"); + return; + } + dir = get_cmd_dir(cmd->cmd_buf); if (dir < 0) goto out; @@ -1641,14 +1656,14 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn, struct usbg_tport *tport = container_of(wwn, struct usbg_tport, tport_wwn); struct usbg_tpg *tpg; - unsigned long tpgt; + u16 tpgt; int ret; struct f_tcm_opts *opts; unsigned i; if (strstr(name, "tpgt_") != name) return ERR_PTR(-EINVAL); - if (kstrtoul(name + 5, 0, &tpgt) || tpgt > UINT_MAX) + if (kstrtou16(name + 5, 0, &tpgt)) return ERR_PTR(-EINVAL); ret = -ENODEV; mutex_lock(&tpg_instances_lock); @@ -1675,7 +1690,7 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn, goto unlock_dep; } - tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL); + tpg = kzalloc_obj(struct usbg_tpg); ret = -ENOMEM; if (!tpg) goto unref_dep; @@ -1767,7 +1782,7 @@ static struct se_wwn *usbg_make_tport( if (!wnn_name) return ERR_PTR(-EINVAL); - tport = kzalloc(sizeof(struct usbg_tport), GFP_KERNEL); + tport = kzalloc_obj(struct usbg_tport); if (!(tport)) return ERR_PTR(-ENOMEM); @@ -1860,7 +1875,7 @@ static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name) goto out_unlock; } - tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL); + tv_nexus = kzalloc_obj(*tv_nexus); if (!tv_nexus) { ret = -ENOMEM; goto out_unlock; @@ -2015,6 +2030,7 @@ static const struct target_core_fabric_ops usbg_ops = { .tfc_wwn_attrs = usbg_wwn_attrs, .tfc_tpg_base_attrs = usbg_base_attrs, + .default_compl_type = TARGET_QUEUE_COMPL, .default_submit_type = TARGET_DIRECT_SUBMIT, .direct_submit_supp = 1, }; @@ -2399,7 +2415,7 @@ static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) { struct guas_setup_wq *work; - work = kmalloc(sizeof(*work), GFP_ATOMIC); + work = kmalloc_obj(*work, GFP_ATOMIC); if (!work) return -ENOMEM; INIT_WORK(&work->work, tcm_delayed_set_alt); @@ -2446,7 +2462,7 @@ static void tcm_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations tcm_item_ops = { +static const struct configfs_item_operations tcm_item_ops = { .release = tcm_attr_release, }; @@ -2534,7 +2550,7 @@ static struct usb_function_instance *tcm_alloc_inst(void) int i; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); @@ -2589,7 +2605,7 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENODEV); } - fu = kzalloc(sizeof(*fu), GFP_KERNEL); + fu = kzalloc_obj(*fu); if (!fu) { mutex_unlock(&tpg_instances_lock); return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index c87e74afc881..85c502e98f57 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -451,7 +451,7 @@ static int audio_notify(struct g_audio *audio, int unit_id, int cs) goto err_dec_int_count; } - msg = kmalloc(sizeof(*msg), GFP_ATOMIC); + msg = kmalloc_obj(*msg, GFP_ATOMIC); if (msg == NULL) { ret = -ENOMEM; goto err_free_request; @@ -1512,7 +1512,7 @@ static void f_uac1_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations f_uac1_item_ops = { +static const struct configfs_item_operations f_uac1_item_ops = { .release = f_uac1_attr_release, }; @@ -1634,7 +1634,7 @@ static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ int result; \ \ mutex_lock(&opts->lock); \ - result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \ + result = sysfs_emit(page, "%s", opts->name); \ mutex_unlock(&opts->lock); \ \ return result; \ @@ -1748,7 +1748,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void) { struct f_uac1_opts *opts; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); @@ -1831,7 +1831,7 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) struct f_uac1_opts *opts; /* allocate and initialize one new instance */ - uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL); + uac1 = kzalloc_obj(*uac1); if (!uac1) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c index 49cf5aae90ca..5d201a2e30e7 100644 --- a/drivers/usb/gadget/function/f_uac1_legacy.c +++ b/drivers/usb/gadget/function/f_uac1_legacy.c @@ -251,7 +251,7 @@ static struct f_audio_buf *f_audio_buffer_alloc(int buf_size) { struct f_audio_buf *copy_buf; - copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC); + copy_buf = kzalloc_obj(*copy_buf, GFP_ATOMIC); if (!copy_buf) return ERR_PTR(-ENOMEM); @@ -360,19 +360,46 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) { struct f_audio *audio = req->context; - int status = req->status; - u32 data = 0; struct usb_ep *out_ep = audio->out_ep; - switch (status) { - - case 0: /* normal completion? */ - if (ep == out_ep) + switch (req->status) { + case 0: + if (ep == out_ep) { f_audio_out_ep_complete(ep, req); - else if (audio->set_con) { - memcpy(&data, req->buf, req->length); - audio->set_con->set(audio->set_con, audio->set_cmd, - le16_to_cpu(data)); + } else if (audio->set_con) { + struct usb_audio_control *con = audio->set_con; + u8 type = con->type; + u32 data; + bool valid_request = false; + + switch (type) { + case UAC_FU_MUTE: { + u8 value; + + if (req->actual == sizeof(value)) { + memcpy(&value, req->buf, sizeof(value)); + data = value; + valid_request = true; + } + break; + } + case UAC_FU_VOLUME: { + __le16 value; + + if (req->actual == sizeof(value)) { + memcpy(&value, req->buf, sizeof(value)); + data = le16_to_cpu(value); + valid_request = true; + } + break; + } + } + + if (valid_request) + con->set(con, audio->set_cmd, data); + else + usb_ep_set_halt(ep); + audio->set_con = NULL; } break; @@ -812,7 +839,7 @@ static void f_uac1_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations f_uac1_item_ops = { +static const struct configfs_item_operations f_uac1_item_ops = { .release = f_uac1_attr_release, }; @@ -942,7 +969,7 @@ static struct usb_function_instance *f_audio_alloc_inst(void) { struct f_uac1_legacy_opts *opts; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); @@ -985,7 +1012,7 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) struct f_uac1_legacy_opts *opts; /* allocate and initialize one new instance */ - audio = kzalloc(sizeof(*audio), GFP_KERNEL); + audio = kzalloc_obj(*audio); if (!audio) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 9b324821c93b..897787d0803c 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1388,7 +1388,7 @@ afunc_notify(struct g_audio *agdev, int unit_id, int cs) goto err_dec_int_count; } - msg = kzalloc(sizeof(*msg), GFP_ATOMIC); + msg = kzalloc_obj(*msg, GFP_ATOMIC); if (msg == NULL) { ret = -ENOMEM; goto err_free_request; @@ -1874,7 +1874,7 @@ static void f_uac2_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations f_uac2_item_ops = { +static const struct configfs_item_operations f_uac2_item_ops = { .release = f_uac2_attr_release, }; @@ -2052,7 +2052,7 @@ static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \ int result; \ \ mutex_lock(&opts->lock); \ - result = scnprintf(page, sizeof(opts->name), "%s", opts->name); \ + result = sysfs_emit(page, "%s", opts->name); \ mutex_unlock(&opts->lock); \ \ return result; \ @@ -2189,7 +2189,7 @@ static struct usb_function_instance *afunc_alloc_inst(void) { struct f_uac2_opts *opts; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); @@ -2278,7 +2278,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi) struct f_uac2 *uac2; struct f_uac2_opts *opts; - uac2 = kzalloc(sizeof(*uac2), GFP_KERNEL); + uac2 = kzalloc_obj(*uac2); if (uac2 == NULL) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index aa6ab666741a..8d404d88391c 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -362,6 +362,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) return ret; usb_ep_enable(uvc->video.ep); + uvc->video.max_req_size = uvc->video.ep->maxpacket + * max_t(unsigned int, uvc->video.ep->maxburst, 1) + * (uvc->video.ep->mult); + memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMON; v4l2_event_queue(&uvc->vdev, &v4l2_event); @@ -409,6 +413,12 @@ uvc_function_disconnect(struct uvc_device *uvc) { int ret; + guard(mutex)(&uvc->lock); + if (uvc->func_unbound) { + dev_dbg(&uvc->vdev.dev, "skipping function deactivate (unbound)\n"); + return; + } + if ((ret = usb_function_deactivate(&uvc->func)) < 0) uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret); } @@ -427,6 +437,15 @@ static ssize_t function_name_show(struct device *dev, static DEVICE_ATTR_RO(function_name); +static void uvc_vdev_release(struct video_device *vdev) +{ + struct uvc_device *uvc = video_get_drvdata(vdev); + + /* Signal uvc_function_unbind() that the video device has been released */ + if (uvc->vdev_release_done) + complete(uvc->vdev_release_done); +} + static int uvc_register_video(struct uvc_device *uvc) { @@ -439,7 +458,7 @@ uvc_register_video(struct uvc_device *uvc) uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev; uvc->vdev.fops = &uvc_v4l2_fops; uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops; - uvc->vdev.release = video_device_release_empty; + uvc->vdev.release = uvc_vdev_release; uvc->vdev.vfl_dir = VFL_DIR_TX; uvc->vdev.lock = &uvc->video.mutex; uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; @@ -655,6 +674,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) int ret = -EINVAL; uvcg_info(f, "%s()\n", __func__); + scoped_guard(mutex, &uvc->lock) + uvc->func_unbound = false; opts = fi_to_f_uvc_opts(f->fi); /* Sanity check the streaming endpoint module parameters. */ @@ -883,7 +904,7 @@ static struct usb_function_instance *uvc_alloc_inst(void) struct uvc_descriptor_header **ctl_cls; int ret; - opts = kzalloc(sizeof(*opts), GFP_KERNEL); + opts = kzalloc_obj(*opts); if (!opts) return ERR_PTR(-ENOMEM); opts->func_inst.free_func_inst = uvc_free_inst; @@ -984,12 +1005,19 @@ static void uvc_free(struct usb_function *f) static void uvc_function_unbind(struct usb_configuration *c, struct usb_function *f) { + DECLARE_COMPLETION_ONSTACK(vdev_release_done); struct usb_composite_dev *cdev = c->cdev; struct uvc_device *uvc = to_uvc(f); struct uvc_video *video = &uvc->video; long wait_ret = 1; + bool connected; uvcg_info(f, "%s()\n", __func__); + scoped_guard(mutex, &uvc->lock) { + uvc->func_unbound = true; + uvc->vdev_release_done = &vdev_release_done; + connected = uvc->func_connected; + } kthread_cancel_work_sync(&video->hw_submit); @@ -1002,7 +1030,7 @@ static void uvc_function_unbind(struct usb_configuration *c, * though the video device removal uevent. Allow some time for the * application to close out before things get deleted. */ - if (uvc->func_connected) { + if (connected) { uvcg_dbg(f, "waiting for clean disconnect\n"); wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue, uvc->func_connected == false, msecs_to_jiffies(500)); @@ -1013,7 +1041,10 @@ static void uvc_function_unbind(struct usb_configuration *c, video_unregister_device(&uvc->vdev); v4l2_device_unregister(&uvc->v4l2_dev); - if (uvc->func_connected) { + scoped_guard(mutex, &uvc->lock) + connected = uvc->func_connected; + + if (connected) { /* * Wait for the release to occur to ensure there are no longer any * pending operations that may cause panics when resources are cleaned @@ -1025,6 +1056,10 @@ static void uvc_function_unbind(struct usb_configuration *c, uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret); } + /* Wait for the video device to be released */ + wait_for_completion(&vdev_release_done); + uvc->vdev_release_done = NULL; + usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); @@ -1038,11 +1073,13 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi) struct uvc_descriptor_header **strm_cls; struct config_item *streaming, *header, *h; - uvc = kzalloc(sizeof(*uvc), GFP_KERNEL); + uvc = kzalloc_obj(*uvc); if (uvc == NULL) return ERR_PTR(-ENOMEM); mutex_init(&uvc->video.mutex); + mutex_init(&uvc->lock); + uvc->func_unbound = true; uvc->state = UVC_STATE_DISCONNECTED; init_waitqueue_head(&uvc->func_connected_queue); opts = fi_to_f_uvc_opts(fi); diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h index 98b8462ad538..7bd66004821f 100644 --- a/drivers/usb/gadget/function/g_zero.h +++ b/drivers/usb/gadget/function/g_zero.h @@ -34,6 +34,7 @@ struct f_ss_opts { unsigned isoc_mult; unsigned isoc_maxburst; unsigned bulk_buflen; + unsigned bulk_maxburst; unsigned bulk_qlen; unsigned iso_qlen; diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index afd75d72412c..3da54a7d7aba 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -892,7 +892,7 @@ struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v) return ERR_PTR(-ENODEV); } - params = kzalloc(sizeof(*params), GFP_KERNEL); + params = kzalloc_obj(*params); if (!params) { rndis_put_nr(i); diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index ca8dbec65f73..e53f2927b539 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -1191,7 +1191,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, if (!g_audio) return -EINVAL; - uac = kzalloc(sizeof(*uac), GFP_KERNEL); + uac = kzalloc_obj(*uac); if (!uac) return -ENOMEM; g_audio->uac = uac; @@ -1209,9 +1209,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, prm->max_psize = g_audio->out_ep_maxpsize; prm->srate = params->c_srates[0]; - prm->reqs = kcalloc(params->req_number, - sizeof(struct usb_request *), - GFP_KERNEL); + prm->reqs = kzalloc_objs(struct usb_request *, + params->req_number); if (!prm->reqs) { err = -ENOMEM; goto fail; @@ -1234,9 +1233,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, prm->max_psize = g_audio->in_ep_maxpsize; prm->srate = params->p_srates[0]; - prm->reqs = kcalloc(params->req_number, - sizeof(struct usb_request *), - GFP_KERNEL); + prm->reqs = kzalloc_objs(struct usb_request *, + params->req_number); if (!prm->reqs) { err = -ENOMEM; goto fail; diff --git a/drivers/usb/gadget/function/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h index 77cfb89932be..7f666b9dea02 100644 --- a/drivers/usb/gadget/function/u_ecm.h +++ b/drivers/usb/gadget/function/u_ecm.h @@ -15,17 +15,26 @@ #include <linux/usb/composite.h> +/** + * struct f_ecm_opts - ECM function options + * @func_inst: USB function instance. + * @net: The net_device associated with the ECM function. + * @bound: True if the net_device is shared and pre-registered during the + * legacy composite driver's bind phase (e.g., multi.c). If false, + * the ECM function will register the net_device during its own + * bind phase. + * @bind_count: Tracks the number of configurations the ECM function is + * bound to, preventing double-registration of the @net device. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_ecm_opts { struct usb_function_instance func_inst; struct net_device *net; bool bound; + int bind_count; - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ struct mutex lock; int refcnt; }; diff --git a/drivers/usb/gadget/function/u_eem.h b/drivers/usb/gadget/function/u_eem.h index 3bd85dfcd71c..78ef55815219 100644 --- a/drivers/usb/gadget/function/u_eem.h +++ b/drivers/usb/gadget/function/u_eem.h @@ -15,17 +15,26 @@ #include <linux/usb/composite.h> +/** + * struct f_eem_opts - EEM function options + * @func_inst: USB function instance. + * @net: The net_device associated with the EEM function. + * @bound: True if the net_device is shared and pre-registered during the + * legacy composite driver's bind phase (e.g., multi.c). If false, + * the EEM function will register the net_device during its own + * bind phase. + * @bind_count: Tracks the number of configurations the EEM function is + * bound to, preventing double-registration of the @net device. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_eem_opts { struct usb_function_instance func_inst; struct net_device *net; bool bound; + int bind_count; - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ struct mutex lock; int refcnt; }; diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index f58590bf5e02..59d85d6a84a8 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -16,6 +16,7 @@ #include <linux/ctype.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> +#include <linux/hex.h> #include <linux/if_vlan.h> #include <linux/string_helpers.h> #include <linux/usb/composite.h> @@ -112,8 +113,10 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p) strscpy(p->driver, "g_ether", sizeof(p->driver)); strscpy(p->version, UETH__VERSION, sizeof(p->version)); - strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); - strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); + if (dev->gadget) { + strscpy(p->fw_version, dev->gadget->name, sizeof(p->fw_version)); + strscpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof(p->bus_info)); + } } /* REVISIT can also support: @@ -896,6 +899,28 @@ void gether_set_gadget(struct net_device *net, struct usb_gadget *g) } EXPORT_SYMBOL_GPL(gether_set_gadget); +int gether_attach_gadget(struct net_device *net, struct usb_gadget *g) +{ + int ret; + + ret = device_move(&net->dev, &g->dev, DPM_ORDER_DEV_AFTER_PARENT); + if (ret) + return ret; + + gether_set_gadget(net, g); + return 0; +} +EXPORT_SYMBOL_GPL(gether_attach_gadget); + +void gether_detach_gadget(struct net_device *net) +{ + struct eth_dev *dev = netdev_priv(net); + + device_move(&net->dev, NULL, DPM_ORDER_NONE); + dev->gadget = NULL; +} +EXPORT_SYMBOL_GPL(gether_detach_gadget); + int gether_set_dev_addr(struct net_device *net, const char *dev_addr) { struct eth_dev *dev; @@ -1200,6 +1225,11 @@ void gether_disconnect(struct gether *link) DBG(dev, "%s\n", __func__); + spin_lock(&dev->lock); + dev->port_usb = NULL; + link->is_suspend = false; + spin_unlock(&dev->lock); + netif_stop_queue(dev->net); netif_carrier_off(dev->net); @@ -1237,11 +1267,6 @@ void gether_disconnect(struct gether *link) dev->header_len = 0; dev->unwrap = NULL; dev->wrap = NULL; - - spin_lock(&dev->lock); - dev->port_usb = NULL; - link->is_suspend = false; - spin_unlock(&dev->lock); } EXPORT_SYMBOL_GPL(gether_disconnect); diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h index 34be220cef77..c85a1cf3c115 100644 --- a/drivers/usb/gadget/function/u_ether.h +++ b/drivers/usb/gadget/function/u_ether.h @@ -151,6 +151,32 @@ static inline struct net_device *gether_setup_default(void) void gether_set_gadget(struct net_device *net, struct usb_gadget *g); /** + * gether_attach_gadget - Reparent net_device to the gadget device. + * @net: The network device to reparent. + * @g: The target USB gadget device to parent to. + * + * This function moves the network device to be a child of the USB gadget + * device in the device hierarchy. This is typically done when the function + * is bound to a configuration. + * + * Returns 0 on success, or a negative error code on failure. + */ +int gether_attach_gadget(struct net_device *net, struct usb_gadget *g); + +/** + * gether_detach_gadget - Detach net_device from its gadget parent. + * @net: The network device to detach. + * + * This function moves the network device to be a child of the virtual + * devices parent, effectively detaching it from the USB gadget device + * hierarchy. This is typically done when the function is unbound + * from a configuration but the instance is not yet freed. + */ +void gether_detach_gadget(struct net_device *net); + +DEFINE_FREE(detach_gadget, struct net_device *, if (_T) gether_detach_gadget(_T)) + +/** * gether_set_dev_addr - initialize an ethernet-over-usb link with eth address * @net: device representing this link * @dev_addr: eth address of this device diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h index f558c3139ebe..51f0d79e5eca 100644 --- a/drivers/usb/gadget/function/u_ether_configfs.h +++ b/drivers/usb/gadget/function/u_ether_configfs.h @@ -21,7 +21,7 @@ usb_put_function_instance(&opts->func_inst); \ } \ \ - static struct configfs_item_operations _f_##_item_ops = { \ + static const struct configfs_item_operations _f_##_item_ops = { \ .release = _f_##_attr_release, \ } diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h index 4b3365f23fd7..6a80182aadd7 100644 --- a/drivers/usb/gadget/function/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -176,7 +176,7 @@ struct ffs_data { /* reference counter */ refcount_t ref; /* how many files are opened (EP0 and others) */ - atomic_t opened; + int opened; /* EP0 state */ enum ffs_state state; diff --git a/drivers/usb/gadget/function/u_gether.h b/drivers/usb/gadget/function/u_gether.h index 2f7a373ed449..e7b6b51f69c1 100644 --- a/drivers/usb/gadget/function/u_gether.h +++ b/drivers/usb/gadget/function/u_gether.h @@ -15,17 +15,25 @@ #include <linux/usb/composite.h> +/** + * struct f_gether_opts - subset function options + * @func_inst: USB function instance. + * @net: The net_device associated with the subset function. + * @bound: True if the net_device is shared and pre-registered during the + * legacy composite driver's bind phase (e.g., multi.c). If false, + * the subset function will register the net_device during its own + * bind phase. + * @bind_count: Tracks the number of configurations the subset function is + * bound to, preventing double-registration of the @net device. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_gether_opts { struct usb_function_instance func_inst; struct net_device *net; bool bound; - - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ + int bind_count; struct mutex lock; int refcnt; }; diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h index 84bb70292855..a9ed9720caee 100644 --- a/drivers/usb/gadget/function/u_hid.h +++ b/drivers/usb/gadget/function/u_hid.h @@ -25,6 +25,8 @@ struct f_hid_opts { unsigned short report_desc_length; unsigned char *report_desc; bool report_desc_alloc; + unsigned char interval; + bool interval_user_set; /* * Protect the data form concurrent access by read/write diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h index 2e400b495cb8..41cb8aa73f09 100644 --- a/drivers/usb/gadget/function/u_midi.h +++ b/drivers/usb/gadget/function/u_midi.h @@ -19,7 +19,7 @@ struct f_midi_opts { struct usb_function_instance func_inst; int index; char *id; - bool id_allocated; + char *interface_string; unsigned int in_ports; unsigned int out_ports; unsigned int buflen; diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index 49ec095cdb4b..ce2f6358688a 100644 --- a/drivers/usb/gadget/function/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h @@ -15,20 +15,29 @@ #include <linux/usb/composite.h> +/** + * struct f_ncm_opts - NCM function options + * @func_inst: USB function instance. + * @net: The net_device associated with the NCM function. + * @bind_count: Tracks the number of configurations the NCM function is + * bound to, preventing double-registration of the @net device. + * @ncm_interf_group: ConfigFS group for NCM interface. + * @ncm_os_desc: USB OS descriptor for NCM. + * @ncm_ext_compat_id: Extended compatibility ID. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + * @max_segment_size: Maximum segment size. + */ struct f_ncm_opts { struct usb_function_instance func_inst; struct net_device *net; - bool bound; + int bind_count; struct config_group *ncm_interf_group; struct usb_os_desc ncm_os_desc; char ncm_ext_compat_id[16]; - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ + struct mutex lock; int refcnt; diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index a8c409b2f52f..4e64619714dc 100644 --- a/drivers/usb/gadget/function/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h @@ -15,12 +15,34 @@ #include <linux/usb/composite.h> +/** + * struct f_rndis_opts - RNDIS function options + * @func_inst: USB function instance. + * @vendor_id: Vendor ID. + * @manufacturer: Manufacturer string. + * @net: The net_device associated with the RNDIS function. + * @bind_count: Tracks the number of configurations the RNDIS function is + * bound to, preventing double-registration of the @net device. + * @borrowed_net: True if the net_device is shared and pre-registered during + * the legacy composite driver's bind phase (e.g., multi.c). + * If false, the RNDIS function will register the net_device + * during its own bind phase. + * @rndis_interf_group: ConfigFS group for RNDIS interface. + * @rndis_os_desc: USB OS descriptor for RNDIS. + * @rndis_ext_compat_id: Extended compatibility ID. + * @class: USB class. + * @subclass: USB subclass. + * @protocol: USB protocol. + * @lock: Protects the data from concurrent access by configfs read/write + * and create symlink/remove symlink operations. + * @refcnt: Reference counter for the function instance. + */ struct f_rndis_opts { struct usb_function_instance func_inst; u32 vendor_id; const char *manufacturer; struct net_device *net; - bool bound; + int bind_count; bool borrowed_net; struct config_group *rndis_interf_group; @@ -30,13 +52,6 @@ struct f_rndis_opts { u8 class; u8 subclass; u8 protocol; - - /* - * Read/write access to configfs attributes is handled by configfs. - * - * This is to protect the data from concurrent access by read/write - * and create symlink/remove symlink. - */ struct mutex lock; int refcnt; }; diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 36fff45e8c9b..cdd1dfc666c4 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -295,8 +295,8 @@ __acquires(&port->port_lock) break; } - if (do_tty_wake && port->port.tty) - tty_wakeup(port->port.tty); + if (do_tty_wake) + tty_port_tty_wakeup(&port->port); return status; } @@ -544,20 +544,16 @@ static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head, static int gs_start_io(struct gs_port *port) { struct list_head *head = &port->read_pool; - struct usb_ep *ep; + struct usb_ep *ep = port->port_usb->out; int status; unsigned started; - if (!port->port_usb || !port->port.tty) - return -EIO; - /* Allocate RX and TX I/O buffers. We can't easily do this much * earlier (with GFP_KERNEL) because the requests are coupled to * endpoints, as are the packet sizes we'll be using. Different * configurations may use different endpoints with a given port; * and high speed vs full speed changes packet sizes too. */ - ep = port->port_usb->out; status = gs_alloc_requests(ep, head, gs_read_complete, &port->read_allocated); if (status) @@ -578,7 +574,7 @@ static int gs_start_io(struct gs_port *port) gs_start_tx(port); /* Unblock any pending writes into our circular buffer, in case * we didn't in gs_start_tx() */ - tty_wakeup(port->port.tty); + tty_port_tty_wakeup(&port->port); } else { /* Free reqs only if we are still connected */ if (port->port_usb) { @@ -592,6 +588,17 @@ static int gs_start_io(struct gs_port *port) return status; } +static int gserial_wakeup_host(struct gserial *gser) +{ + struct usb_function *func = &gser->func; + struct usb_gadget *gadget = func->config->cdev->gadget; + + if (func->func_suspended) + return usb_func_wakeup(func); + else + return usb_gadget_wakeup(gadget); +} + /*-------------------------------------------------------------------------*/ /* TTY Driver */ @@ -746,6 +753,8 @@ static ssize_t gs_write(struct tty_struct *tty, const u8 *buf, size_t count) { struct gs_port *port = tty->driver_data; unsigned long flags; + int ret = 0; + struct gserial *gser = port->port_usb; pr_vdebug("gs_write: ttyGS%d (%p) writing %zu bytes\n", port->port_num, tty, count); @@ -753,6 +762,17 @@ static ssize_t gs_write(struct tty_struct *tty, const u8 *buf, size_t count) spin_lock_irqsave(&port->port_lock, flags); if (count) count = kfifo_in(&port->port_write_buf, buf, count); + + if (port->suspended) { + spin_unlock_irqrestore(&port->port_lock, flags); + ret = gserial_wakeup_host(gser); + if (ret) { + pr_debug("ttyGS%d: Remote wakeup failed:%d\n", port->port_num, ret); + return count; + } + spin_lock_irqsave(&port->port_lock, flags); + } + /* treat count == 0 as flush_chars() */ if (port->port_usb) gs_start_tx(port); @@ -781,10 +801,22 @@ static void gs_flush_chars(struct tty_struct *tty) { struct gs_port *port = tty->driver_data; unsigned long flags; + int ret = 0; + struct gserial *gser = port->port_usb; pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty); spin_lock_irqsave(&port->port_lock, flags); + if (port->suspended) { + spin_unlock_irqrestore(&port->port_lock, flags); + ret = gserial_wakeup_host(gser); + if (ret) { + pr_debug("ttyGS%d: Remote wakeup failed:%d\n", port->port_num, ret); + return; + } + spin_lock_irqsave(&port->port_lock, flags); + } + if (port->port_usb) gs_start_tx(port); spin_unlock_irqrestore(&port->port_lock, flags); @@ -1050,11 +1082,11 @@ static int gs_console_init(struct gs_port *port) if (port->console) return 0; - cons = kzalloc(sizeof(*port->console), GFP_KERNEL); + cons = kzalloc_obj(*port->console); if (!cons) return -ENOMEM; - strcpy(cons->console.name, "ttyGS"); + strscpy(cons->console.name, "ttyGS"); cons->console.write = gs_console_write; cons->console.device = gs_console_device; cons->console.flags = CON_PRINTBUFFER; @@ -1183,7 +1215,7 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) goto out; } - port = kzalloc(sizeof(struct gs_port), GFP_KERNEL); + port = kzalloc_obj(struct gs_port); if (port == NULL) { ret = -ENOMEM; goto out; @@ -1464,6 +1496,14 @@ void gserial_suspend(struct gserial *gser) return; } + if (port->write_busy || port->write_started) { + /* Wakeup to host if there are ongoing transfers */ + spin_unlock_irqrestore(&serial_port_lock, flags); + if (!gserial_wakeup_host(gser)) + return; + spin_lock_irqsave(&serial_port_lock, flags); + } + spin_lock(&port->port_lock); spin_unlock(&serial_port_lock); port->suspended = true; diff --git a/drivers/usb/gadget/function/u_uac1_legacy.c b/drivers/usb/gadget/function/u_uac1_legacy.c index dd21c251542c..01016102fa17 100644 --- a/drivers/usb/gadget/function/u_uac1_legacy.c +++ b/drivers/usb/gadget/function/u_uac1_legacy.c @@ -106,7 +106,7 @@ static int playback_default_hw_params(struct gaudio_snd_dev *snd) snd->channels = 2; snd->rate = 48000; - params = kzalloc(sizeof(*params), GFP_KERNEL); + params = kzalloc_obj(*params); if (!params) return -ENOMEM; diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 6f44dd732315..7abfdd5e1eef 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -107,7 +107,7 @@ struct uvc_video { unsigned int width; unsigned int height; unsigned int imagesize; - unsigned int interval; + unsigned int interval; /* in 100ns units */ struct mutex mutex; /* protects frame parameters */ unsigned int uvc_num_requests; @@ -117,6 +117,7 @@ struct uvc_video { /* Requests */ bool is_enabled; /* tracks whether video stream is enabled */ unsigned int req_size; + unsigned int max_req_size; struct list_head ureqs; /* all uvc_requests allocated by uvc_video */ /* USB requests that the video pump thread can encode into */ @@ -154,6 +155,9 @@ struct uvc_device { enum uvc_state state; struct usb_function func; struct uvc_video video; + struct completion *vdev_release_done; + struct mutex lock; /* protects func_unbound and func_connected */ + bool func_unbound; bool func_connected; wait_queue_head_t func_connected_queue; @@ -196,6 +200,11 @@ struct uvc_file_handle { #define to_uvc_file_handle(handle) \ container_of(handle, struct uvc_file_handle, vfh) +static inline struct uvc_file_handle *file_to_uvc_file_handle(struct file *filp) +{ + return container_of(file_to_v4l2_fh(filp), struct uvc_file_handle, vfh); +} + /* ------------------------------------------------------------------------ * Functions */ diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index f131943254a4..70a1415ea401 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -12,6 +12,7 @@ #include "uvc_configfs.h" +#include <linux/hex.h> #include <linux/sort.h> #include <linux/usb/uvc.h> #include <linux/usb/video.h> @@ -127,7 +128,7 @@ static void uvcg_config_item_release(struct config_item *item) kfree(group); } -static struct configfs_item_operations uvcg_config_item_ops = { +static const struct configfs_item_operations uvcg_config_item_ops = { .release = uvcg_config_item_release, }; @@ -157,7 +158,7 @@ static int uvcg_config_create_group(struct config_group *parent, { struct config_group *group; - group = kzalloc(sizeof(*group), GFP_KERNEL); + group = kzalloc_obj(*group); if (!group) return -ENOMEM; @@ -269,7 +270,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group, { struct uvcg_control_header *h; - h = kzalloc(sizeof(*h), GFP_KERNEL); + h = kzalloc_obj(*h); if (!h) return ERR_PTR(-ENOMEM); @@ -284,7 +285,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group, return &h->item; } -static struct configfs_group_operations uvcg_control_header_grp_ops = { +static const struct configfs_group_operations uvcg_control_header_grp_ops = { .make_item = uvcg_control_header_make, }; @@ -1232,7 +1233,7 @@ static void uvcg_extension_drop_link(struct config_item *src, struct config_item mutex_unlock(su_mutex); } -static struct configfs_item_operations uvcg_extension_item_ops = { +static const struct configfs_item_operations uvcg_extension_item_ops = { .release = uvcg_extension_release, .allow_link = uvcg_extension_allow_link, .drop_link = uvcg_extension_drop_link, @@ -1272,7 +1273,7 @@ static struct config_item *uvcg_extension_make(struct config_group *group, const opts_item = group->cg_item.ci_parent->ci_parent; opts = to_f_uvc_opts(opts_item); - xu = kzalloc(sizeof(*xu), GFP_KERNEL); + xu = kzalloc_obj(*xu); if (!xu) return ERR_PTR(-ENOMEM); @@ -1297,7 +1298,7 @@ static struct config_item *uvcg_extension_make(struct config_group *group, const return &xu->item; } -static struct configfs_group_operations uvcg_extensions_grp_ops = { +static const struct configfs_group_operations uvcg_extensions_grp_ops = { .make_item = uvcg_extension_make, .drop_item = uvcg_extension_drop, }; @@ -1413,7 +1414,7 @@ out: mutex_unlock(su_mutex); } -static struct configfs_item_operations uvcg_control_class_item_ops = { +static const struct configfs_item_operations uvcg_control_class_item_ops = { .release = uvcg_config_item_release, .allow_link = uvcg_control_class_allow_link, .drop_link = uvcg_control_class_drop_link, @@ -1436,7 +1437,7 @@ static int uvcg_control_class_create_children(struct config_group *parent) for (i = 0; i < ARRAY_SIZE(names); ++i) { struct uvcg_control_class_group *group; - group = kzalloc(sizeof(*group), GFP_KERNEL); + group = kzalloc_obj(*group); if (!group) return -ENOMEM; @@ -1663,7 +1664,7 @@ static void uvcg_format_drop_link(struct config_item *src, struct config_item *t mutex_unlock(su_mutex); } -static struct configfs_item_operations uvcg_format_item_operations = { +static const struct configfs_item_operations uvcg_format_item_operations = { .release = uvcg_config_item_release, .allow_link = uvcg_format_allow_link, .drop_link = uvcg_format_drop_link, @@ -1784,7 +1785,7 @@ static int uvcg_streaming_header_allow_link(struct config_item *src, uvcg_format_set_indices(to_config_group(target)); - format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); + format_ptr = kzalloc_obj(*format_ptr); if (!format_ptr) { ret = -ENOMEM; goto out; @@ -1839,7 +1840,7 @@ out: mutex_unlock(su_mutex); } -static struct configfs_item_operations uvcg_streaming_header_item_ops = { +static const struct configfs_item_operations uvcg_streaming_header_item_ops = { .release = uvcg_config_item_release, .allow_link = uvcg_streaming_header_allow_link, .drop_link = uvcg_streaming_header_drop_link, @@ -1898,7 +1899,7 @@ static struct config_item { struct uvcg_streaming_header *h; - h = kzalloc(sizeof(*h), GFP_KERNEL); + h = kzalloc_obj(*h); if (!h) return ERR_PTR(-ENOMEM); @@ -1913,7 +1914,7 @@ static struct config_item return &h->item; } -static struct configfs_group_operations uvcg_streaming_header_grp_ops = { +static const struct configfs_group_operations uvcg_streaming_header_grp_ops = { .make_item = uvcg_streaming_header_make, }; @@ -2162,7 +2163,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, struct config_item *opts_item; struct uvcg_frame_ptr *frame_ptr; - h = kzalloc(sizeof(*h), GFP_KERNEL); + h = kzalloc_obj(*h); if (!h) return ERR_PTR(-ENOMEM); @@ -2196,7 +2197,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group, return ERR_PTR(-EINVAL); } - frame_ptr = kzalloc(sizeof(*frame_ptr), GFP_KERNEL); + frame_ptr = kzalloc_obj(*frame_ptr); if (!frame_ptr) { mutex_unlock(&opts->lock); kfree(h); @@ -2260,7 +2261,7 @@ static void uvcg_format_set_indices(struct config_group *fmt) * streaming/uncompressed/<NAME> */ -static struct configfs_group_operations uvcg_uncompressed_group_ops = { +static const struct configfs_group_operations uvcg_uncompressed_group_ops = { .make_item = uvcg_frame_make, .drop_item = uvcg_frame_drop, }; @@ -2482,7 +2483,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group, if (!color_match) return ERR_PTR(-EINVAL); - h = kzalloc(sizeof(*h), GFP_KERNEL); + h = kzalloc_obj(*h); if (!h) return ERR_PTR(-ENOMEM); @@ -2507,7 +2508,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group, return &h->fmt.group; } -static struct configfs_group_operations uvcg_uncompressed_grp_ops = { +static const struct configfs_group_operations uvcg_uncompressed_grp_ops = { .make_group = uvcg_uncompressed_make, }; @@ -2524,7 +2525,7 @@ static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = { * streaming/mjpeg/<NAME> */ -static struct configfs_group_operations uvcg_mjpeg_group_ops = { +static const struct configfs_group_operations uvcg_mjpeg_group_ops = { .make_item = uvcg_frame_make, .drop_item = uvcg_frame_drop, }; @@ -2674,7 +2675,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group, if (!color_match) return ERR_PTR(-EINVAL); - h = kzalloc(sizeof(*h), GFP_KERNEL); + h = kzalloc_obj(*h); if (!h) return ERR_PTR(-ENOMEM); @@ -2697,7 +2698,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group, return &h->fmt.group; } -static struct configfs_group_operations uvcg_mjpeg_grp_ops = { +static const struct configfs_group_operations uvcg_mjpeg_grp_ops = { .make_group = uvcg_mjpeg_make, }; @@ -2714,7 +2715,7 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = { * streaming/framebased/<NAME> */ -static struct configfs_group_operations uvcg_framebased_group_ops = { +static const struct configfs_group_operations uvcg_framebased_group_ops = { .make_item = uvcg_frame_make, .drop_item = uvcg_frame_drop, }; @@ -2916,9 +2917,16 @@ static struct config_group *uvcg_framebased_make(struct config_group *group, 'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }; + struct uvcg_color_matching *color_match; + struct config_item *streaming; struct uvcg_framebased *h; - h = kzalloc(sizeof(*h), GFP_KERNEL); + streaming = group->cg_item.ci_parent; + color_match = uvcg_format_get_default_color_match(streaming); + if (!color_match) + return ERR_PTR(-EINVAL); + + h = kzalloc_obj(*h); if (!h) return ERR_PTR(-ENOMEM); @@ -2936,13 +2944,16 @@ static struct config_group *uvcg_framebased_make(struct config_group *group, INIT_LIST_HEAD(&h->fmt.frames); h->fmt.type = UVCG_FRAMEBASED; + + h->fmt.color_matching = color_match; + color_match->refcnt++; 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 = { +static const struct configfs_group_operations uvcg_framebased_grp_ops = { .make_group = uvcg_framebased_make, }; @@ -3045,7 +3056,7 @@ static void uvcg_color_matching_release(struct config_item *item) kfree(color_match); } -static struct configfs_item_operations uvcg_color_matching_item_ops = { +static const struct configfs_item_operations uvcg_color_matching_item_ops = { .release = uvcg_color_matching_release, }; @@ -3064,7 +3075,7 @@ static struct config_group *uvcg_color_matching_make(struct config_group *group, { struct uvcg_color_matching *color_match; - color_match = kzalloc(sizeof(*color_match), GFP_KERNEL); + color_match = kzalloc_obj(*color_match); if (!color_match) return ERR_PTR(-ENOMEM); @@ -3078,7 +3089,7 @@ static struct config_group *uvcg_color_matching_make(struct config_group *group, return &color_match->group; } -static struct configfs_group_operations uvcg_color_matching_grp_group_ops = { +static const struct configfs_group_operations uvcg_color_matching_grp_group_ops = { .make_group = uvcg_color_matching_make, }; @@ -3086,7 +3097,7 @@ static int uvcg_color_matching_create_children(struct config_group *parent) { struct uvcg_color_matching *color_match; - color_match = kzalloc(sizeof(*color_match), GFP_KERNEL); + color_match = kzalloc_obj(*color_match); if (!color_match) return -ENOMEM; @@ -3519,7 +3530,7 @@ out: mutex_unlock(su_mutex); } -static struct configfs_item_operations uvcg_streaming_class_item_ops = { +static const struct configfs_item_operations uvcg_streaming_class_item_ops = { .release = uvcg_config_item_release, .allow_link = uvcg_streaming_class_allow_link, .drop_link = uvcg_streaming_class_drop_link, @@ -3542,7 +3553,7 @@ static int uvcg_streaming_class_create_children(struct config_group *parent) for (i = 0; i < ARRAY_SIZE(names); ++i) { struct uvcg_streaming_class_group *group; - group = kzalloc(sizeof(*group), GFP_KERNEL); + group = kzalloc_obj(*group); if (!group) return -ENOMEM; @@ -3687,7 +3698,7 @@ static void uvc_func_drop_link(struct config_item *src, struct config_item *tgt) mutex_unlock(&opts->lock); } -static struct configfs_item_operations uvc_func_item_ops = { +static const struct configfs_item_operations uvc_func_item_ops = { .release = uvc_func_item_release, .allow_link = uvc_func_allow_link, .drop_link = uvc_func_drop_link, diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index 2f78cd4f396f..9391614135e9 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -74,10 +74,12 @@ static inline struct uvcg_format *to_uvcg_format(struct config_item *item) struct uvcg_streaming_header { struct config_item item; - struct uvc_input_header_descriptor desc; unsigned linked; struct list_head formats; unsigned num_fmt; + + /* Must be last --ends in a flexible-array member. */ + struct uvc_input_header_descriptor desc; }; static inline struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item) diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 5eaeae3e2441..586e5524c171 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -86,10 +86,17 @@ static int uvc_buffer_prepare(struct vb2_buffer *vb) buf->bytesused = 0; } 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); + + if (video->reqs_per_frame != 0) { + buf->req_payload_size = + DIV_ROUND_UP(buf->bytesused + + (video->reqs_per_frame * UVCG_REQUEST_HEADER_LEN), + video->reqs_per_frame); + if (buf->req_payload_size > video->req_size) + buf->req_payload_size = video->req_size; + } else { + buf->req_payload_size = video->max_req_size; + } } return 0; @@ -122,8 +129,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, @@ -177,7 +182,15 @@ int uvcg_alloc_buffers(struct uvc_video_queue *queue, { int ret; +retry: ret = vb2_reqbufs(&queue->queue, rb); + if (ret < 0 && queue->use_sg) { + uvc_trace(UVC_TRACE_IOCTL, + "failed to alloc buffer with sg enabled, try non-sg mode\n"); + queue->use_sg = 0; + queue->queue.mem_ops = &vb2_vmalloc_memops; + goto retry; + } return ret ? ret : rb->count; } diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index fc9a8d31a1e9..514e5930b9ca 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -574,6 +574,8 @@ uvc_v4l2_subscribe_event(struct v4l2_fh *fh, if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) return -EINVAL; + guard(mutex)(&uvc->lock); + if (sub->type == UVC_EVENT_SETUP && uvc->func_connected) return -EBUSY; @@ -595,7 +597,8 @@ static void uvc_v4l2_disable(struct uvc_device *uvc) uvc_function_disconnect(uvc); uvcg_video_disable(&uvc->video); uvcg_free_buffers(&uvc->video.queue); - uvc->func_connected = false; + scoped_guard(mutex, &uvc->lock) + uvc->func_connected = false; wake_up_interruptible(&uvc->func_connected_queue); } @@ -667,15 +670,14 @@ uvc_v4l2_open(struct file *file) struct uvc_device *uvc = video_get_drvdata(vdev); struct uvc_file_handle *handle; - handle = kzalloc(sizeof(*handle), GFP_KERNEL); + handle = kzalloc_obj(*handle); if (handle == NULL) return -ENOMEM; v4l2_fh_init(&handle->vfh, vdev); - v4l2_fh_add(&handle->vfh); + v4l2_fh_add(&handle->vfh, file); handle->device = &uvc->video; - file->private_data = &handle->vfh; return 0; } @@ -685,7 +687,7 @@ uvc_v4l2_release(struct file *file) { struct video_device *vdev = video_devdata(file); struct uvc_device *uvc = video_get_drvdata(vdev); - struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); + struct uvc_file_handle *handle = file_to_uvc_file_handle(file); struct uvc_video *video = handle->device; mutex_lock(&video->mutex); @@ -693,8 +695,7 @@ uvc_v4l2_release(struct file *file) uvc_v4l2_disable(uvc); mutex_unlock(&video->mutex); - file->private_data = NULL; - v4l2_fh_del(&handle->vfh); + v4l2_fh_del(&handle->vfh, file); v4l2_fh_exit(&handle->vfh); kfree(handle); diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index fb77b0b21790..2f9700b3f1b6 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -499,13 +499,11 @@ 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 interval_duration; 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); + max_req_size = video->max_req_size; if (!usb_endpoint_xfer_isoc(video->ep->desc)) { video->req_size = max_req_size; @@ -515,8 +513,11 @@ uvc_video_prep_requests(struct uvc_video *video) return; } + interval_duration = 1 << (video->ep->desc->bInterval - 1); if (cdev->gadget->speed < USB_SPEED_HIGH) - interval_duration = video->ep->desc->bInterval * 10000; + interval_duration *= 10000; + else + interval_duration *= 1250; nreq = DIV_ROUND_UP(video->interval, interval_duration); @@ -558,7 +559,7 @@ uvc_video_alloc_requests(struct uvc_video *video) uvc_video_prep_requests(video); for (i = 0; i < video->uvc_num_requests; i++) { - ureq = kzalloc(sizeof(struct uvc_request), GFP_KERNEL); + ureq = kzalloc_obj(struct uvc_request); if (ureq == NULL) goto error; @@ -837,7 +838,6 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) video->interval = 666666; /* Initialize the video buffers queue. */ - uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, + return uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex); - return 0; } diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index d70fb5bc2357..03032ea6f701 100644 --- a/drivers/usb/gadget/legacy/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -298,7 +298,7 @@ static int dbgp_bind(struct usb_gadget *gadget, dbgp.req->length = DBGP_REQ_EP0_LEN; #ifdef CONFIG_USB_G_DBGP_SERIAL - dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL); + dbgp.serial = kzalloc_obj(struct gserial); if (!dbgp.serial) { stp = 3; err = -ENOMEM; diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index a9544fea8723..dc59e625aca8 100644 --- a/drivers/usb/gadget/legacy/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -188,7 +188,7 @@ static int __init gfs_init(void) /* * Allocate in one chunk for easier maintenance */ - f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL); + f_ffs[0] = kzalloc_objs(*f_ffs[0], func_num * N_CONF); if (!f_ffs[0]) { ret = -ENOMEM; goto no_func; @@ -196,7 +196,7 @@ static int __init gfs_init(void) for (i = 1; i < N_CONF; ++i) f_ffs[i] = f_ffs[0] + i * func_num; - fi_ffs = kcalloc(func_num, sizeof(*fi_ffs), GFP_KERNEL); + fi_ffs = kzalloc_objs(*fi_ffs, func_num); if (!fi_ffs) { ret = -ENOMEM; goto no_func; diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c index 3684546e8801..89722413e5fd 100644 --- a/drivers/usb/gadget/legacy/hid.c +++ b/drivers/usb/gadget/legacy/hid.c @@ -227,7 +227,7 @@ static int hidg_plat_driver_probe(struct platform_device *pdev) return -ENODEV; } - entry = kzalloc(sizeof(*entry), GFP_KERNEL); + entry = kzalloc_obj(*entry); if (!entry) return -ENOMEM; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index b6a30d88a800..d87a8ab51510 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -150,7 +150,6 @@ struct dev_data { void *buf; wait_queue_head_t wait; struct super_block *sb; - struct dentry *dentry; /* except this scratch i/o buffer for ep0 */ u8 rbuf[RBUF_SIZE]; @@ -174,7 +173,7 @@ static struct dev_data *dev_new (void) { struct dev_data *dev; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) return NULL; dev->state = STATE_DEV_DISABLED; @@ -208,7 +207,6 @@ struct ep_data { struct usb_endpoint_descriptor desc, hs_desc; struct list_head epfiles; wait_queue_head_t wait; - struct dentry *dentry; }; static inline void get_ep (struct ep_data *data) @@ -616,7 +614,7 @@ ep_read_iter(struct kiocb *iocb, struct iov_iter *to) if (value >= 0 && (copy_to_iter(buf, value, to) != value)) value = -EFAULT; } else { - struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL); + struct kiocb_priv *priv = kzalloc_obj(*priv); value = -ENOMEM; if (!priv) goto fail; @@ -684,7 +682,7 @@ ep_write_iter(struct kiocb *iocb, struct iov_iter *from) } else if (is_sync_kiocb(iocb)) { value = ep_io(epdata, buf, len); } else { - struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL); + struct kiocb_priv *priv = kzalloc_obj(*priv); value = -ENOMEM; if (priv) { value = ep_aio(iocb, priv, epdata, buf, len); @@ -1561,18 +1559,12 @@ static void destroy_ep_files (struct dev_data *dev) spin_lock_irq (&dev->lock); while (!list_empty(&dev->epfiles)) { struct ep_data *ep; - struct inode *parent; - struct dentry *dentry; /* break link to FS */ ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles); list_del_init (&ep->epfiles); spin_unlock_irq (&dev->lock); - dentry = ep->dentry; - ep->dentry = NULL; - parent = d_inode(dentry->d_parent); - /* break link to controller */ mutex_lock(&ep->lock); if (ep->state == STATE_EP_ENABLED) @@ -1583,13 +1575,11 @@ static void destroy_ep_files (struct dev_data *dev) mutex_unlock(&ep->lock); wake_up (&ep->wait); - put_ep (ep); /* break link to dcache */ - inode_lock(parent); - d_delete (dentry); - dput (dentry); - inode_unlock(parent); + simple_remove_by_name(dev->sb->s_root, ep->name, NULL); + + put_ep (ep); spin_lock_irq (&dev->lock); } @@ -1597,25 +1587,25 @@ static void destroy_ep_files (struct dev_data *dev) } -static struct dentry * -gadgetfs_create_file (struct super_block *sb, char const *name, +static int gadgetfs_create_file (struct super_block *sb, char const *name, void *data, const struct file_operations *fops); static int activate_ep_files (struct dev_data *dev) { struct usb_ep *ep; struct ep_data *data; + int err; gadget_for_each_ep (ep, dev->gadget) { - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = kzalloc_obj(*data); if (!data) goto enomem0; data->state = STATE_EP_DISABLED; mutex_init(&data->lock); init_waitqueue_head (&data->wait); - strncpy (data->name, ep->name, sizeof (data->name) - 1); + strscpy(data->name, ep->name); refcount_set (&data->count, 1); data->dev = dev; get_dev (dev); @@ -1627,9 +1617,9 @@ static int activate_ep_files (struct dev_data *dev) if (!data->req) goto enomem1; - data->dentry = gadgetfs_create_file (dev->sb, data->name, + err = gadgetfs_create_file (dev->sb, data->name, data, &ep_io_operations); - if (!data->dentry) + if (err) goto enomem2; list_add_tail (&data->epfiles, &dev->epfiles); } @@ -1993,30 +1983,32 @@ gadgetfs_make_inode (struct super_block *sb, /* creates in fs root directory, so non-renamable and non-linkable. * so inode and dentry are paired, until device reconfig. */ -static struct dentry * -gadgetfs_create_file (struct super_block *sb, char const *name, +static int gadgetfs_create_file (struct super_block *sb, char const *name, void *data, const struct file_operations *fops) { struct dentry *dentry; struct inode *inode; - dentry = d_alloc_name(sb->s_root, name); - if (!dentry) - return NULL; - inode = gadgetfs_make_inode (sb, data, fops, S_IFREG | (default_perm & S_IRWXUGO)); - if (!inode) { - dput(dentry); - return NULL; + if (!inode) + return -ENOMEM; + + dentry = simple_start_creating(sb->s_root, name); + if (IS_ERR(dentry)) { + iput(inode); + return PTR_ERR(dentry); } - d_add (dentry, inode); - return dentry; + + d_make_persistent(dentry, inode); + + simple_done_creating(dentry); + return 0; } static const struct super_operations gadget_fs_operations = { .statfs = simple_statfs, - .drop_inode = generic_delete_inode, + .drop_inode = inode_just_drop, }; static int @@ -2064,8 +2056,8 @@ gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc) goto Enomem; dev->sb = sb; - dev->dentry = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations); - if (!dev->dentry) { + rc = gadgetfs_create_file(sb, CHIP, dev, &ep0_operations); + if (rc) { put_dev(dev); goto Enomem; } @@ -2107,7 +2099,7 @@ static void gadgetfs_kill_sb (struct super_block *sb) { mutex_lock(&sb_mutex); - kill_litter_super (sb); + kill_anon_super (sb); if (the_device) { put_dev (the_device); the_device = NULL; diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c index 20165e1582d9..4febf8dac7ca 100644 --- a/drivers/usb/gadget/legacy/raw_gadget.c +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -40,6 +40,7 @@ MODULE_LICENSE("GPL"); static DEFINE_IDA(driver_id_numbers); #define DRIVER_DRIVER_NAME_LENGTH_MAX 32 +#define USB_RAW_IO_LENGTH_MAX KMALLOC_MAX_SIZE #define RAW_EVENT_QUEUE_SIZE 16 @@ -189,7 +190,7 @@ static struct raw_dev *dev_new(void) { struct raw_dev *dev; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) return NULL; /* Matches kref_put() in raw_release(). */ @@ -667,7 +668,7 @@ static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr, return ERR_PTR(-EINVAL); if (!usb_raw_io_flags_valid(io->flags)) return ERR_PTR(-EINVAL); - if (io->length > PAGE_SIZE) + if (io->length > USB_RAW_IO_LENGTH_MAX) return ERR_PTR(-EINVAL); if (get_from_user) data = memdup_user(ptr + sizeof(*io), io->length); @@ -1250,7 +1251,7 @@ static int raw_ioctl_eps_info(struct raw_dev *dev, unsigned long value) struct usb_raw_eps_info *info; struct raw_ep *ep; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc_obj(*info); if (!info) { ret = -ENOMEM; goto out; diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c index e25e0d8dd387..08a21bd0c2ba 100644 --- a/drivers/usb/gadget/legacy/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -147,6 +147,12 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; +static struct usb_function *func_lb; +static struct usb_function_instance *func_inst_lb; + +static struct usb_function *func_ss; +static struct usb_function_instance *func_inst_ss; + /*-------------------------------------------------------------------------*/ static struct timer_list autoresume_timer; @@ -156,6 +162,7 @@ static void zero_autoresume(struct timer_list *unused) { struct usb_composite_dev *cdev = autoresume_cdev; struct usb_gadget *g = cdev->gadget; + int status; /* unconfigured devices can't issue wakeups */ if (!cdev->config) @@ -165,10 +172,18 @@ static void zero_autoresume(struct timer_list *unused) * more significant than just a timer firing; likely * because of some direct user request. */ - if (g->speed != USB_SPEED_UNKNOWN) { - int status = usb_gadget_wakeup(g); - INFO(cdev, "%s --> %d\n", __func__, status); + if (g->speed == USB_SPEED_UNKNOWN) + return; + + if (g->speed >= USB_SPEED_SUPER) { + if (loopdefault) + status = usb_func_wakeup(func_lb); + else + status = usb_func_wakeup(func_ss); + } else { + status = usb_gadget_wakeup(g); } + INFO(cdev, "%s --> %d\n", __func__, status); } static void zero_suspend(struct usb_composite_dev *cdev) @@ -194,7 +209,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); } /*-------------------------------------------------------------------------*/ @@ -206,9 +221,6 @@ static struct usb_configuration loopback_driver = { /* .iConfiguration = DYNAMIC */ }; -static struct usb_function *func_ss; -static struct usb_function_instance *func_inst_ss; - static int ss_config_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl) { @@ -248,9 +260,6 @@ module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)"); -static struct usb_function *func_lb; -static struct usb_function_instance *func_inst_lb; - module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(qlen, "depth of loopback queue"); @@ -398,7 +407,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/Kconfig b/drivers/usb/gadget/udc/Kconfig index aae1787320d4..26460340fbc9 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -102,12 +102,6 @@ config USB_FSL_USB2 dynamically linked module called "fsl_usb2_udc" and force all gadget drivers to also be dynamically linked. -config USB_FUSB300 - tristate "Faraday FUSB300 USB Peripheral Controller" - depends on !PHYS_ADDR_T_64BIT && HAS_DMA - help - Faraday usb device controller FUSB300 driver - config USB_GR_UDC tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver" depends on HAS_DMA @@ -228,21 +222,6 @@ config USB_PXA27X dynamically linked module called "pxa27x_udc" and force all gadget drivers to also be dynamically linked. -config USB_MV_UDC - tristate "Marvell USB2.0 Device Controller" - depends on HAS_DMA - help - Marvell Socs (including PXA and MMP series) include a high speed - USB2.0 OTG controller, which can be configured as high speed or - full speed USB peripheral. - -config USB_MV_U3D - depends on HAS_DMA - tristate "MARVELL PXA2128 USB 3.0 controller" - help - MARVELL PXA2128 Processor series include a super speed USB3.0 device - controller, which support super speed USB peripheral. - config USB_SNP_CORE depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT) depends on HAS_DMA @@ -326,29 +305,6 @@ config USB_FSL_QE Set CONFIG_USB_GADGET to "m" to build this driver as a dynamically linked module called "fsl_qe_udc". -config USB_NET2272 - depends on HAS_IOMEM - tristate "PLX NET2272" - help - PLX NET2272 is a USB peripheral controller which supports - both full and high speed USB 2.0 data transfers. - - It has three configurable endpoints, as well as endpoint zero - (for control transfer). - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "net2272" and force all - gadget drivers to also be dynamically linked. - -config USB_NET2272_DMA - bool "Support external DMA controller" - depends on USB_NET2272 && HAS_DMA - help - The NET2272 part can optionally support an external DMA - controller, but your board has to have support in the - driver itself. - - If unsure, say "N" here. The driver works fine in PIO mode. - config USB_NET2280 tristate "NetChip NET228x / PLX USB3x8x" depends on USB_PCI diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index b52f93e9c61d..1b9b1a4f9c57 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -9,7 +9,6 @@ udc-core-y := core.o trace.o # obj-$(CONFIG_USB_GADGET) += udc-core.o obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o -obj-$(CONFIG_USB_NET2272) += net2272.o obj-$(CONFIG_USB_NET2280) += net2280.o obj-$(CONFIG_USB_SNP_CORE) += snps_udc_core.o obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc_pci.o @@ -31,10 +30,6 @@ obj-$(CONFIG_USB_RENESAS_USBF) += renesas_usbf.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o obj-$(CONFIG_USB_EG20T) += pch_udc.o -obj-$(CONFIG_USB_MV_UDC) += mv_udc.o -mv_udc-y := mv_udc_core.o -obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o -obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o obj-$(CONFIG_USB_GR_UDC) += gr_udc.o obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c index a36913ae31f9..cd767b1e3fe7 100644 --- a/drivers/usb/gadget/udc/amd5536udc_pci.c +++ b/drivers/usb/gadget/udc/amd5536udc_pci.c @@ -95,7 +95,7 @@ static int udc_pci_probe( } /* init */ - dev = kzalloc(sizeof(struct udc), GFP_KERNEL); + dev = kzalloc_obj(struct udc); if (!dev) return -ENOMEM; diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index f2685f89b3e5..4a8b9ff8368f 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -23,6 +23,7 @@ #include <linux/of.h> #include <linux/regmap.h> #include <linux/dma-mapping.h> +#include <linux/reset.h> #include "vhub.h" @@ -77,7 +78,7 @@ struct usb_request *ast_vhub_alloc_request(struct usb_ep *u_ep, { struct ast_vhub_req *req; - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; return &req->req; @@ -280,6 +281,8 @@ static void ast_vhub_remove(struct platform_device *pdev) if (vhub->clk) clk_disable_unprepare(vhub->clk); + reset_control_assert(vhub->rst); + spin_unlock_irqrestore(&vhub->lock, flags); if (vhub->ep0_bufs) @@ -294,6 +297,7 @@ static void ast_vhub_remove(struct platform_device *pdev) static int ast_vhub_probe(struct platform_device *pdev) { enum usb_device_speed max_speed; + const u64 *dma_mask_ptr; struct ast_vhub *vhub; struct resource *res; int i, rc = 0; @@ -348,6 +352,16 @@ static int ast_vhub_probe(struct platform_device *pdev) goto err; } + vhub->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); + if (IS_ERR(vhub->rst)) { + rc = PTR_ERR(vhub->rst); + goto err; + } + + rc = reset_control_deassert(vhub->rst); + if (rc) + goto err; + /* Check if we need to limit the HW to USB1 */ max_speed = usb_get_maximum_speed(&pdev->dev); if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH) @@ -370,6 +384,12 @@ static int ast_vhub_probe(struct platform_device *pdev) goto err; } + dma_mask_ptr = (u64 *)of_device_get_match_data(&pdev->dev); + if (dma_mask_ptr) { + rc = dma_coerce_mask_and_coherent(&pdev->dev, *dma_mask_ptr); + if (rc) + goto err; + } /* * Allocate DMA buffers for all EP0s in one chunk, * one per port and one for the vHub itself @@ -412,15 +432,25 @@ static int ast_vhub_probe(struct platform_device *pdev) return rc; } +static const u64 dma_mask_32 = DMA_BIT_MASK(32); +static const u64 dma_mask_64 = DMA_BIT_MASK(64); + static const struct of_device_id ast_vhub_dt_ids[] = { { .compatible = "aspeed,ast2400-usb-vhub", + .data = &dma_mask_32, }, { .compatible = "aspeed,ast2500-usb-vhub", + .data = &dma_mask_32, }, { .compatible = "aspeed,ast2600-usb-vhub", + .data = &dma_mask_32, + }, + { + .compatible = "aspeed,ast2700-usb-vhub", + .data = &dma_mask_64, }, { } }; diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index 573109ca5b79..2ecd049dacc2 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); @@ -557,7 +560,7 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx) * endpoint 0. */ d->max_epns = min_t(u32, vhub->max_epns, 30); - d->epns = kcalloc(d->max_epns, sizeof(*d->epns), GFP_KERNEL); + d->epns = kzalloc_objs(*d->epns, d->max_epns); if (!d->epns) return -ENOMEM; @@ -566,7 +569,7 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx) * named "parent" devices for each port so we create a sub device * here for that purpose */ - d->port_dev = kzalloc(sizeof(struct device), GFP_KERNEL); + d->port_dev = kzalloc_obj(struct device); if (!d->port_dev) { rc = -ENOMEM; goto fail_alloc; diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h index 6b9dfa6e10eb..aca2050e2db0 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h +++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h @@ -388,6 +388,7 @@ struct ast_vhub { spinlock_t lock; struct work_struct wake_work; struct clk *clk; + struct reset_control *rst; /* EP0 DMA buffers allocated in one chunk */ void *ep0_bufs; diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c index 353bfb1ff0a1..7fc6696b7694 100644 --- a/drivers/usb/gadget/udc/aspeed_udc.c +++ b/drivers/usb/gadget/udc/aspeed_udc.c @@ -452,7 +452,7 @@ static struct usb_request *ast_udc_ep_alloc_request(struct usb_ep *_ep, struct ast_udc_ep *ep = to_ast_ep(_ep); struct ast_udc_request *req; - req = kzalloc(sizeof(struct ast_udc_request), gfp_flags); + req = kzalloc_obj(struct ast_udc_request, gfp_flags); if (!req) { EP_DBG(ep, "request allocation failed\n"); return NULL; diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index aa4c61094dc6..5aa360ba4f03 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -585,7 +585,7 @@ at91_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { struct at91_request *req; - req = kzalloc(sizeof (struct at91_request), gfp_flags); + req = kzalloc_obj(struct at91_request, gfp_flags); if (!req) return NULL; @@ -1541,7 +1541,7 @@ static void at91_vbus_timer_work(struct work_struct *work) static void at91_vbus_timer(struct timer_list *t) { - struct at91_udc *udc = from_timer(udc, t, vbus_timer); + struct at91_udc *udc = timer_container_of(udc, t, vbus_timer); /* * If we are polling vbus it is likely that the gpio is on an diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 0c6f2ad81d37..72a2f95ff018 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -40,7 +40,7 @@ static int queue_dbg_open(struct inode *inode, struct file *file) struct usba_request *req, *req_copy; struct list_head *queue_data; - queue_data = kmalloc(sizeof(*queue_data), GFP_KERNEL); + queue_data = kmalloc_obj(*queue_data); if (!queue_data) return -ENOMEM; INIT_LIST_HEAD(queue_data); @@ -702,7 +702,7 @@ usba_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) DBG(DBG_GADGET, "ep_alloc_request: %p, 0x%x\n", _ep, gfp_flags); - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index 502612a5650e..c4f9ea45bdbb 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -1115,7 +1115,7 @@ static struct usb_request *bcm63xx_udc_alloc_request(struct usb_ep *ep, { struct bcm63xx_req *breq; - breq = kzalloc(sizeof(*breq), mem_flags); + breq = kzalloc_obj(*breq, mem_flags); if (!breq) return NULL; return &breq->req; diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 5c3d8b64c0e7..438201dc96ca 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -35,8 +35,8 @@ static int poll_oip(struct bdc *bdc, u32 usec) u32 status; int ret; - ret = readl_poll_timeout(bdc->regs + BDC_BDCSC, status, - (BDC_CSTS(status) != BDC_OIP), 10, usec); + ret = readl_poll_timeout_atomic(bdc->regs + BDC_BDCSC, status, + (BDC_CSTS(status) != BDC_OIP), 10, usec); if (ret) dev_err(bdc->dev, "operation timedout BDCSC: 0x%08x\n", status); else @@ -397,8 +397,7 @@ static int bdc_mem_alloc(struct bdc *bdc) "ieps:%d eops:%d num_eps:%d\n", num_ieps, num_oeps, bdc->num_eps); /* allocate array of ep pointers */ - bdc->bdc_ep_array = kcalloc(bdc->num_eps, sizeof(struct bdc_ep *), - GFP_KERNEL); + bdc->bdc_ep_array = kzalloc_objs(struct bdc_ep *, bdc->num_eps); if (!bdc->bdc_ep_array) goto fail; diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index f995cfa9b99e..a7a22e5ec47b 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -138,16 +138,15 @@ static int ep_bd_list_alloc(struct bdc_ep *ep) __func__, ep, num_tabs); /* Allocate memory for table array */ - ep->bd_list.bd_table_array = kcalloc(num_tabs, - sizeof(struct bd_table *), - GFP_ATOMIC); + ep->bd_list.bd_table_array = kzalloc_objs(struct bd_table *, num_tabs, + GFP_ATOMIC); if (!ep->bd_list.bd_table_array) return -ENOMEM; /* Allocate memory for each table */ for (index = 0; index < num_tabs; index++) { /* Allocate memory for bd_table structure */ - bd_table = kzalloc(sizeof(*bd_table), GFP_ATOMIC); + bd_table = kzalloc_obj(*bd_table, GFP_ATOMIC); if (!bd_table) goto fail; @@ -1648,6 +1647,10 @@ void bdc_sr_xsf(struct bdc *bdc, struct bdc_sr *sreport) u8 ep_num; ep_num = (le32_to_cpu(sreport->offset[3])>>4) & 0x1f; + if (ep_num >= bdc->num_eps) { + dev_err(bdc->dev, "xsf for invalid ep %u\n", ep_num); + return; + } ep = bdc->bdc_ep_array[ep_num]; if (!ep || !(ep->flags & BDC_EP_ENABLED)) { dev_err(bdc->dev, "xsf for ep not enabled\n"); @@ -1829,7 +1832,7 @@ static struct usb_request *bdc_gadget_alloc_request(struct usb_ep *_ep, struct bdc_req *req; struct bdc_ep *ep; - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -1946,7 +1949,7 @@ static int init_ep(struct bdc *bdc, u32 epnum, u32 dir) struct bdc_ep *ep; dev_dbg(bdc->dev, "%s epnum=%d dir=%d\n", __func__, epnum, dir); - ep = kzalloc(sizeof(*ep), GFP_KERNEL); + ep = kzalloc_obj(*ep); if (!ep) return -ENOMEM; diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c index 7e69944ef18a..308d3c468ab1 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c @@ -1505,7 +1505,7 @@ struct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep, struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep); struct cdns2_request *preq; - preq = kzalloc(sizeof(*preq), gfp_flags); + preq = kzalloc_obj(*preq, gfp_flags); if (!preq) return NULL; @@ -2415,7 +2415,6 @@ int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated) void cdns2_gadget_remove(struct cdns2_device *pdev) { - pm_runtime_mark_last_busy(pdev->dev); pm_runtime_put_autosuspend(pdev->dev); usb_del_gadget(&pdev->gadget); diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-trace.h b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h index ade1752956b1..f4df0e2ff853 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-trace.h +++ b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h @@ -47,16 +47,6 @@ DEFINE_EVENT(cdns2_log_enable_disable, cdns2_pullup, TP_ARGS(set) ); -DEFINE_EVENT(cdns2_log_enable_disable, cdns2_lpm, - TP_PROTO(int set), - TP_ARGS(set) -); - -DEFINE_EVENT(cdns2_log_enable_disable, cdns2_may_wakeup, - TP_PROTO(int set), - TP_ARGS(set) -); - DECLARE_EVENT_CLASS(cdns2_log_simple, TP_PROTO(char *msg), TP_ARGS(msg), @@ -79,11 +69,6 @@ DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_status_stage, TP_ARGS(msg) ); -DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_set_config, - TP_PROTO(char *msg), - TP_ARGS(msg) -); - DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_setup, TP_PROTO(char *msg), TP_ARGS(msg) @@ -340,11 +325,6 @@ DEFINE_EVENT(cdns2_log_request, cdns2_free_request, TP_ARGS(preq) ); -DEFINE_EVENT(cdns2_log_request, cdns2_ep_queue, - TP_PROTO(struct cdns2_request *preq), - TP_ARGS(preq) -); - DEFINE_EVENT(cdns2_log_request, cdns2_request_dequeue, TP_PROTO(struct cdns2_request *preq), TP_ARGS(preq) @@ -355,50 +335,6 @@ DEFINE_EVENT(cdns2_log_request, cdns2_request_giveback, TP_ARGS(preq) ); -TRACE_EVENT(cdns2_ep0_enqueue, - TP_PROTO(struct cdns2_device *dev_priv, struct usb_request *request), - TP_ARGS(dev_priv, request), - TP_STRUCT__entry( - __field(int, dir) - __field(int, length) - ), - TP_fast_assign( - __entry->dir = dev_priv->eps[0].dir; - __entry->length = request->length; - ), - TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out", - __entry->length) -); - -DECLARE_EVENT_CLASS(cdns2_log_map_request, - TP_PROTO(struct cdns2_request *priv_req), - TP_ARGS(priv_req), - TP_STRUCT__entry( - __string(name, priv_req->pep->name) - __field(struct usb_request *, req) - __field(void *, buf) - __field(dma_addr_t, dma) - ), - TP_fast_assign( - __assign_str(name); - __entry->req = &priv_req->request; - __entry->buf = priv_req->request.buf; - __entry->dma = priv_req->request.dma; - ), - TP_printk("%s: req: %p, req buf %p, dma %p", - __get_str(name), __entry->req, __entry->buf, &__entry->dma - ) -); - -DEFINE_EVENT(cdns2_log_map_request, cdns2_map_request, - TP_PROTO(struct cdns2_request *req), - TP_ARGS(req) -); -DEFINE_EVENT(cdns2_log_map_request, cdns2_mapped_request, - TP_PROTO(struct cdns2_request *req), - TP_ARGS(req) -); - DECLARE_EVENT_CLASS(cdns2_log_trb, TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb), TP_ARGS(pep, trb), @@ -507,11 +443,6 @@ DEFINE_EVENT(cdns2_log_ep, cdns2_gadget_ep_disable, TP_ARGS(pep) ); -DEFINE_EVENT(cdns2_log_ep, cdns2_iso_out_ep_disable, - TP_PROTO(struct cdns2_endpoint *pep), - TP_ARGS(pep) -); - DEFINE_EVENT(cdns2_log_ep, cdns2_ep_busy_try_halt_again, TP_PROTO(struct cdns2_endpoint *pep), TP_ARGS(pep) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 4b3d5075621a..e8861eaad907 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -194,6 +194,9 @@ struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, req = ep->ops->alloc_request(ep, gfp_flags); + if (req) + req->ep = ep; + trace_usb_ep_alloc_request(ep, req, req ? 0 : -ENOMEM); return req; @@ -1123,8 +1126,14 @@ static void usb_gadget_state_work(struct work_struct *work) void usb_gadget_set_state(struct usb_gadget *gadget, enum usb_device_state state) { + unsigned long flags; + + spin_lock_irqsave(&gadget->state_lock, flags); gadget->state = state; - schedule_work(&gadget->work); + if (!gadget->teardown) + schedule_work(&gadget->work); + spin_unlock_irqrestore(&gadget->state_lock, flags); + trace_usb_gadget_set_state(gadget, 0); } EXPORT_SYMBOL_GPL(usb_gadget_set_state); @@ -1257,8 +1266,9 @@ static inline void usb_gadget_udc_stop_locked(struct usb_udc *udc) * @speed: The maximum speed to allowed to run * * This call is issued by the UDC Class driver before calling - * usb_gadget_udc_start() in order to make sure that we don't try to - * connect on speeds the gadget driver doesn't support. + * usb_gadget_udc_start_locked() in order to make sure that + * we don't try to connect on speeds the gadget driver + * doesn't support. */ static inline void usb_gadget_udc_set_speed(struct usb_udc *udc, enum usb_device_speed speed) @@ -1357,6 +1367,8 @@ static void usb_udc_nop_release(struct device *dev) void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget, void (*release)(struct device *dev)) { + spin_lock_init(&gadget->state_lock); + gadget->teardown = false; INIT_WORK(&gadget->work, usb_gadget_state_work); gadget->dev.parent = parent; @@ -1382,7 +1394,7 @@ int usb_add_gadget(struct usb_gadget *gadget) struct usb_udc *udc; int ret = -ENOMEM; - udc = kzalloc(sizeof(*udc), GFP_KERNEL); + udc = kzalloc_obj(*udc); if (!udc) goto error; @@ -1531,6 +1543,7 @@ EXPORT_SYMBOL_GPL(usb_add_gadget_udc); void usb_del_gadget(struct usb_gadget *gadget) { struct usb_udc *udc = gadget->udc; + unsigned long flags; if (!udc) return; @@ -1544,6 +1557,13 @@ void usb_del_gadget(struct usb_gadget *gadget) kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); sysfs_remove_link(&udc->dev.kobj, "gadget"); device_del(&gadget->dev); + /* + * Set the teardown flag before flushing the work to prevent new work + * from being scheduled while we are cleaning up. + */ + spin_lock_irqsave(&gadget->state_lock, flags); + gadget->teardown = true; + spin_unlock_irqrestore(&gadget->state_lock, flags); flush_work(&gadget->work); ida_free(&gadget_id_numbers, gadget->id_number); cancel_work_sync(&udc->vbus_work); @@ -1570,7 +1590,7 @@ static int gadget_match_driver(struct device *dev, const struct device_driver *d { struct usb_gadget *gadget = dev_to_usb_gadget(dev); struct usb_udc *udc = gadget->udc; - struct usb_gadget_driver *driver = container_of(drv, + const struct usb_gadget_driver *driver = container_of(drv, struct usb_gadget_driver, driver); /* If the driver specifies a udc_name, it must match the UDC's name */ diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index bda08c5ba7c0..f094491b1041 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -462,8 +462,13 @@ static void set_link_state(struct dummy_hcd *dum_hcd) /* Report reset and disconnect events to the driver */ if (dum->ints_enabled && (disconnect || reset)) { - stop_activity(dum); ++dum->callback_usage; + /* + * stop_activity() can drop dum->lock, so it must + * not come between the dum->ints_enabled test + * and the ++dum->callback_usage. + */ + stop_activity(dum); spin_unlock(&dum->lock); if (reset) usb_gadget_udc_reset(&dum->gadget, dum->driver); @@ -623,7 +628,7 @@ static int dummy_enable(struct usb_ep *_ep, dev_dbg(udc_dev(dum), "enabled %s (ep%d%s-%s) maxpacket %d stream %s\n", _ep->name, - desc->bEndpointAddress & 0x0f, + usb_endpoint_num(desc), (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", usb_ep_type_string(usb_endpoint_type(desc)), max, str_enabled_disabled(ep->stream_en)); @@ -666,7 +671,7 @@ static struct usb_request *dummy_alloc_request(struct usb_ep *_ep, if (!_ep) return NULL; - req = kzalloc(sizeof(*req), mem_flags); + req = kzalloc_obj(*req, mem_flags); if (!req) return NULL; INIT_LIST_HEAD(&req->queue); @@ -765,8 +770,7 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req) if (!dum->driver) return -ESHUTDOWN; - local_irq_save(flags); - spin_lock(&dum->lock); + spin_lock_irqsave(&dum->lock, flags); list_for_each_entry(iter, &ep->queue, queue) { if (&iter->req != _req) continue; @@ -776,15 +780,16 @@ static int dummy_dequeue(struct usb_ep *_ep, struct usb_request *_req) retval = 0; break; } - spin_unlock(&dum->lock); if (retval == 0) { dev_dbg(udc_dev(dum), "dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); + spin_unlock(&dum->lock); usb_gadget_giveback_request(_ep, _req); + spin_lock(&dum->lock); } - local_irq_restore(flags); + spin_unlock_irqrestore(&dum->lock, flags); return retval; } @@ -908,21 +913,6 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value) spin_lock_irqsave(&dum->lock, flags); dum->pullup = (value != 0); set_link_state(dum_hcd); - if (value == 0) { - /* - * Emulate synchronize_irq(): wait for callbacks to finish. - * This seems to be the best place to emulate the call to - * synchronize_irq() that's in usb_gadget_remove_driver(). - * Doing it in dummy_udc_stop() would be too late since it - * is called after the unbind callback and unbind shouldn't - * be invoked until all the other callbacks are finished. - */ - while (dum->callback_usage > 0) { - spin_unlock_irqrestore(&dum->lock, flags); - usleep_range(1000, 2000); - spin_lock_irqsave(&dum->lock, flags); - } - } spin_unlock_irqrestore(&dum->lock, flags); usb_hcd_poll_rh_status(dummy_hcd_to_hcd(dum_hcd)); @@ -945,6 +935,20 @@ static void dummy_udc_async_callbacks(struct usb_gadget *_gadget, bool enable) spin_lock_irq(&dum->lock); dum->ints_enabled = enable; + if (!enable) { + /* + * Emulate synchronize_irq(): wait for callbacks to finish. + * This has to happen after emulated interrupts are disabled + * (dum->ints_enabled is clear) and before the unbind callback, + * just like the call to synchronize_irq() in + * gadget/udc/core:gadget_unbind_driver(). + */ + while (dum->callback_usage > 0) { + spin_unlock_irq(&dum->lock); + usleep_range(1000, 2000); + spin_lock_irq(&dum->lock); + } + } spin_unlock_irq(&dum->lock); } @@ -1270,7 +1274,7 @@ static int dummy_urb_enqueue( unsigned long flags; int rc; - urbp = kmalloc(sizeof *urbp, mem_flags); + urbp = kmalloc_obj(*urbp, mem_flags); if (!urbp) return -ENOMEM; urbp->urb = urb; @@ -1534,6 +1538,12 @@ top: /* rescan to continue with any other queued i/o */ if (rescan) goto top; + + /* request not fully transferred; stop iterating to + * preserve data ordering across queued requests. + */ + if (req->req.actual < req->req.length) + break; } return sent; } @@ -1787,7 +1797,8 @@ static int handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb, */ static enum hrtimer_restart dummy_timer(struct hrtimer *t) { - struct dummy_hcd *dum_hcd = from_timer(dum_hcd, t, timer); + struct dummy_hcd *dum_hcd = timer_container_of(dum_hcd, t, + timer); struct dummy *dum = dum_hcd->dum; struct urbp *urbp, *tmp; unsigned long flags; @@ -2479,8 +2490,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); @@ -2509,8 +2519,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); @@ -2820,7 +2829,7 @@ static int __init dummy_hcd_init(void) } } for (i = 0; i < mod_data.num; i++) { - dum[i] = kzalloc(sizeof(struct dummy), GFP_KERNEL); + dum[i] = kzalloc_obj(struct dummy); if (!dum[i]) { retval = -ENOMEM; goto err_add_pdata; diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index aacfde06387c..bf87285ad13c 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -417,7 +417,7 @@ static int qe_ep_rxbd_update(struct qe_ep *ep) bd = ep->rxbase; - ep->rxframe = kmalloc(sizeof(*ep->rxframe), GFP_ATOMIC); + ep->rxframe = kmalloc_obj(*ep->rxframe, GFP_ATOMIC); if (!ep->rxframe) return -ENOMEM; @@ -666,7 +666,7 @@ static int qe_ep_init(struct qe_udc *udc, } if ((ep->tm == USBP_TM_CTL) || (ep->dir == USB_DIR_IN)) { - ep->txframe = kmalloc(sizeof(*ep->txframe), GFP_ATOMIC); + ep->txframe = kmalloc_obj(*ep->txframe, GFP_ATOMIC); if (!ep->txframe) goto en_done2; qe_frame_init(ep->txframe); @@ -1670,7 +1670,7 @@ static struct usb_request *qe_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { struct qe_req *req; - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -2344,7 +2344,7 @@ static struct qe_udc *qe_udc_config(struct platform_device *ofdev) u64 size; u32 offset; - udc = kzalloc(sizeof(*udc), GFP_KERNEL); + udc = kzalloc_obj(*udc); if (!udc) goto cleanup; diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 4dea8bc30cf6..600ce8cc0fef 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -678,7 +678,7 @@ fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { struct fsl_req *req; - req = kzalloc(sizeof *req, gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -2250,7 +2250,7 @@ static int struct_udc_setup(struct fsl_udc *udc, pdata = dev_get_platdata(&pdev->dev); udc->phy_mode = pdata->phy_mode; - udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL); + udc->eps = kzalloc_objs(struct fsl_ep, udc->max_ep); if (!udc->eps) { dev_err(&udc->gadget.dev, "kmalloc udc endpoint status failed\n"); goto eps_alloc_failed; @@ -2369,7 +2369,7 @@ static int fsl_udc_probe(struct platform_device *pdev) unsigned int i; u32 dccparams; - udc_controller = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); + udc_controller = kzalloc_obj(struct fsl_udc); if (udc_controller == NULL) return -ENOMEM; diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c deleted file mode 100644 index 5e94a99b3e53..000000000000 --- a/drivers/usb/gadget/udc/fusb300_udc.c +++ /dev/null @@ -1,1516 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Fusb300 UDC (USB gadget) - * - * Copyright (C) 2010 Faraday Technology Corp. - * - * Author : Yuan-hsin Chen <yhchen@faraday-tech.com> - */ -#include <linux/dma-mapping.h> -#include <linux/err.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> - -#include "fusb300_udc.h" - -MODULE_DESCRIPTION("FUSB300 USB gadget driver"); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Yuan-Hsin Chen, Feng-Hsin Chiang <john453@faraday-tech.com>"); -MODULE_ALIAS("platform:fusb300_udc"); - -#define DRIVER_VERSION "20 October 2010" - -static const char udc_name[] = "fusb300_udc"; -static const char * const fusb300_ep_name[] = { - "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7", "ep8", "ep9", - "ep10", "ep11", "ep12", "ep13", "ep14", "ep15" -}; - -static void done(struct fusb300_ep *ep, struct fusb300_request *req, - int status); - -static void fusb300_enable_bit(struct fusb300 *fusb300, u32 offset, - u32 value) -{ - u32 reg = ioread32(fusb300->reg + offset); - - reg |= value; - iowrite32(reg, fusb300->reg + offset); -} - -static void fusb300_disable_bit(struct fusb300 *fusb300, u32 offset, - u32 value) -{ - u32 reg = ioread32(fusb300->reg + offset); - - reg &= ~value; - iowrite32(reg, fusb300->reg + offset); -} - - -static void fusb300_ep_setting(struct fusb300_ep *ep, - struct fusb300_ep_info info) -{ - ep->epnum = info.epnum; - ep->type = info.type; -} - -static int fusb300_ep_release(struct fusb300_ep *ep) -{ - if (!ep->epnum) - return 0; - ep->epnum = 0; - ep->stall = 0; - ep->wedged = 0; - return 0; -} - -static void fusb300_set_fifo_entry(struct fusb300 *fusb300, - u32 ep) -{ - u32 val = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); - - val &= ~FUSB300_EPSET1_FIFOENTRY_MSK; - val |= FUSB300_EPSET1_FIFOENTRY(FUSB300_FIFO_ENTRY_NUM); - iowrite32(val, fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); -} - -static void fusb300_set_start_entry(struct fusb300 *fusb300, - u8 ep) -{ - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); - u32 start_entry = fusb300->fifo_entry_num * FUSB300_FIFO_ENTRY_NUM; - - reg &= ~FUSB300_EPSET1_START_ENTRY_MSK ; - reg |= FUSB300_EPSET1_START_ENTRY(start_entry); - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); - if (fusb300->fifo_entry_num == FUSB300_MAX_FIFO_ENTRY) { - fusb300->fifo_entry_num = 0; - fusb300->addrofs = 0; - pr_err("fifo entry is over the maximum number!\n"); - } else - fusb300->fifo_entry_num++; -} - -/* set fusb300_set_start_entry first before fusb300_set_epaddrofs */ -static void fusb300_set_epaddrofs(struct fusb300 *fusb300, - struct fusb300_ep_info info) -{ - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); - - reg &= ~FUSB300_EPSET2_ADDROFS_MSK; - reg |= FUSB300_EPSET2_ADDROFS(fusb300->addrofs); - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); - fusb300->addrofs += (info.maxpacket + 7) / 8 * FUSB300_FIFO_ENTRY_NUM; -} - -static void ep_fifo_setting(struct fusb300 *fusb300, - struct fusb300_ep_info info) -{ - fusb300_set_fifo_entry(fusb300, info.epnum); - fusb300_set_start_entry(fusb300, info.epnum); - fusb300_set_epaddrofs(fusb300, info); -} - -static void fusb300_set_eptype(struct fusb300 *fusb300, - struct fusb300_ep_info info) -{ - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); - - reg &= ~FUSB300_EPSET1_TYPE_MSK; - reg |= FUSB300_EPSET1_TYPE(info.type); - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); -} - -static void fusb300_set_epdir(struct fusb300 *fusb300, - struct fusb300_ep_info info) -{ - u32 reg; - - if (!info.dir_in) - return; - reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); - reg &= ~FUSB300_EPSET1_DIR_MSK; - reg |= FUSB300_EPSET1_DIRIN; - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); -} - -static void fusb300_set_ep_active(struct fusb300 *fusb300, - u8 ep) -{ - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); - - reg |= FUSB300_EPSET1_ACTEN; - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(ep)); -} - -static void fusb300_set_epmps(struct fusb300 *fusb300, - struct fusb300_ep_info info) -{ - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); - - reg &= ~FUSB300_EPSET2_MPS_MSK; - reg |= FUSB300_EPSET2_MPS(info.maxpacket); - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET2(info.epnum)); -} - -static void fusb300_set_interval(struct fusb300 *fusb300, - struct fusb300_ep_info info) -{ - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); - - reg &= ~FUSB300_EPSET1_INTERVAL(0x7); - reg |= FUSB300_EPSET1_INTERVAL(info.interval); - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); -} - -static void fusb300_set_bwnum(struct fusb300 *fusb300, - struct fusb300_ep_info info) -{ - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); - - reg &= ~FUSB300_EPSET1_BWNUM(0x3); - reg |= FUSB300_EPSET1_BWNUM(info.bw_num); - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET1(info.epnum)); -} - -static void set_ep_reg(struct fusb300 *fusb300, - struct fusb300_ep_info info) -{ - fusb300_set_eptype(fusb300, info); - fusb300_set_epdir(fusb300, info); - fusb300_set_epmps(fusb300, info); - - if (info.interval) - fusb300_set_interval(fusb300, info); - - if (info.bw_num) - fusb300_set_bwnum(fusb300, info); - - fusb300_set_ep_active(fusb300, info.epnum); -} - -static int config_ep(struct fusb300_ep *ep, - const struct usb_endpoint_descriptor *desc) -{ - struct fusb300 *fusb300 = ep->fusb300; - struct fusb300_ep_info info; - - ep->ep.desc = desc; - - info.interval = 0; - info.addrofs = 0; - info.bw_num = 0; - - info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; - info.maxpacket = usb_endpoint_maxp(desc); - info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - - if ((info.type == USB_ENDPOINT_XFER_INT) || - (info.type == USB_ENDPOINT_XFER_ISOC)) { - info.interval = desc->bInterval; - if (info.type == USB_ENDPOINT_XFER_ISOC) - info.bw_num = usb_endpoint_maxp_mult(desc); - } - - ep_fifo_setting(fusb300, info); - - set_ep_reg(fusb300, info); - - fusb300_ep_setting(ep, info); - - fusb300->ep[info.epnum] = ep; - - return 0; -} - -static int fusb300_enable(struct usb_ep *_ep, - const struct usb_endpoint_descriptor *desc) -{ - struct fusb300_ep *ep; - - ep = container_of(_ep, struct fusb300_ep, ep); - - if (ep->fusb300->reenum) { - ep->fusb300->fifo_entry_num = 0; - ep->fusb300->addrofs = 0; - ep->fusb300->reenum = 0; - } - - return config_ep(ep, desc); -} - -static int fusb300_disable(struct usb_ep *_ep) -{ - struct fusb300_ep *ep; - struct fusb300_request *req; - unsigned long flags; - - ep = container_of(_ep, struct fusb300_ep, ep); - - BUG_ON(!ep); - - while (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, struct fusb300_request, queue); - spin_lock_irqsave(&ep->fusb300->lock, flags); - done(ep, req, -ECONNRESET); - spin_unlock_irqrestore(&ep->fusb300->lock, flags); - } - - return fusb300_ep_release(ep); -} - -static struct usb_request *fusb300_alloc_request(struct usb_ep *_ep, - gfp_t gfp_flags) -{ - struct fusb300_request *req; - - req = kzalloc(sizeof(struct fusb300_request), gfp_flags); - if (!req) - return NULL; - INIT_LIST_HEAD(&req->queue); - - return &req->req; -} - -static void fusb300_free_request(struct usb_ep *_ep, struct usb_request *_req) -{ - struct fusb300_request *req; - - req = container_of(_req, struct fusb300_request, req); - kfree(req); -} - -static int enable_fifo_int(struct fusb300_ep *ep) -{ - struct fusb300 *fusb300 = ep->fusb300; - - if (ep->epnum) { - fusb300_enable_bit(fusb300, FUSB300_OFFSET_IGER0, - FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum)); - } else { - pr_err("can't enable_fifo_int ep0\n"); - return -EINVAL; - } - - return 0; -} - -static int disable_fifo_int(struct fusb300_ep *ep) -{ - struct fusb300 *fusb300 = ep->fusb300; - - if (ep->epnum) { - fusb300_disable_bit(fusb300, FUSB300_OFFSET_IGER0, - FUSB300_IGER0_EEPn_FIFO_INT(ep->epnum)); - } else { - pr_err("can't disable_fifo_int ep0\n"); - return -EINVAL; - } - - return 0; -} - -static void fusb300_set_cxlen(struct fusb300 *fusb300, u32 length) -{ - u32 reg; - - reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR); - reg &= ~FUSB300_CSR_LEN_MSK; - reg |= FUSB300_CSR_LEN(length); - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_CSR); -} - -/* write data to cx fifo */ -static void fusb300_wrcxf(struct fusb300_ep *ep, - struct fusb300_request *req) -{ - int i = 0; - u8 *tmp; - u32 data; - struct fusb300 *fusb300 = ep->fusb300; - u32 length = req->req.length - req->req.actual; - - tmp = req->req.buf + req->req.actual; - - if (length > SS_CTL_MAX_PACKET_SIZE) { - fusb300_set_cxlen(fusb300, SS_CTL_MAX_PACKET_SIZE); - for (i = (SS_CTL_MAX_PACKET_SIZE >> 2); i > 0; i--) { - data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 | - *(tmp + 3) << 24; - iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); - tmp += 4; - } - req->req.actual += SS_CTL_MAX_PACKET_SIZE; - } else { /* length is less than max packet size */ - fusb300_set_cxlen(fusb300, length); - for (i = length >> 2; i > 0; i--) { - data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16 | - *(tmp + 3) << 24; - printk(KERN_DEBUG " 0x%x\n", data); - iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); - tmp = tmp + 4; - } - switch (length % 4) { - case 1: - data = *tmp; - printk(KERN_DEBUG " 0x%x\n", data); - iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); - break; - case 2: - data = *tmp | *(tmp + 1) << 8; - printk(KERN_DEBUG " 0x%x\n", data); - iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); - break; - case 3: - data = *tmp | *(tmp + 1) << 8 | *(tmp + 2) << 16; - printk(KERN_DEBUG " 0x%x\n", data); - iowrite32(data, fusb300->reg + FUSB300_OFFSET_CXPORT); - break; - default: - break; - } - req->req.actual += length; - } -} - -static void fusb300_set_epnstall(struct fusb300 *fusb300, u8 ep) -{ - fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep), - FUSB300_EPSET0_STL); -} - -static void fusb300_clear_epnstall(struct fusb300 *fusb300, u8 ep) -{ - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); - - if (reg & FUSB300_EPSET0_STL) { - printk(KERN_DEBUG "EP%d stall... Clear!!\n", ep); - reg |= FUSB300_EPSET0_STL_CLR; - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); - } -} - -static void ep0_queue(struct fusb300_ep *ep, struct fusb300_request *req) -{ - if (ep->fusb300->ep0_dir) { /* if IN */ - if (req->req.length) { - fusb300_wrcxf(ep, req); - } else - printk(KERN_DEBUG "%s : req->req.length = 0x%x\n", - __func__, req->req.length); - if ((req->req.length == req->req.actual) || - (req->req.actual < ep->ep.maxpacket)) - done(ep, req, 0); - } else { /* OUT */ - if (!req->req.length) - done(ep, req, 0); - else - fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER1, - FUSB300_IGER1_CX_OUT_INT); - } -} - -static int fusb300_queue(struct usb_ep *_ep, struct usb_request *_req, - gfp_t gfp_flags) -{ - struct fusb300_ep *ep; - struct fusb300_request *req; - unsigned long flags; - int request = 0; - - ep = container_of(_ep, struct fusb300_ep, ep); - req = container_of(_req, struct fusb300_request, req); - - if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - spin_lock_irqsave(&ep->fusb300->lock, flags); - - if (list_empty(&ep->queue)) - request = 1; - - list_add_tail(&req->queue, &ep->queue); - - req->req.actual = 0; - req->req.status = -EINPROGRESS; - - if (ep->ep.desc == NULL) /* ep0 */ - ep0_queue(ep, req); - else if (request && !ep->stall) - enable_fifo_int(ep); - - spin_unlock_irqrestore(&ep->fusb300->lock, flags); - - return 0; -} - -static int fusb300_dequeue(struct usb_ep *_ep, struct usb_request *_req) -{ - struct fusb300_ep *ep; - struct fusb300_request *req; - unsigned long flags; - - ep = container_of(_ep, struct fusb300_ep, ep); - req = container_of(_req, struct fusb300_request, req); - - spin_lock_irqsave(&ep->fusb300->lock, flags); - if (!list_empty(&ep->queue)) - done(ep, req, -ECONNRESET); - spin_unlock_irqrestore(&ep->fusb300->lock, flags); - - return 0; -} - -static int fusb300_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) -{ - struct fusb300_ep *ep; - struct fusb300 *fusb300; - unsigned long flags; - int ret = 0; - - ep = container_of(_ep, struct fusb300_ep, ep); - - fusb300 = ep->fusb300; - - spin_lock_irqsave(&ep->fusb300->lock, flags); - - if (!list_empty(&ep->queue)) { - ret = -EAGAIN; - goto out; - } - - if (value) { - fusb300_set_epnstall(fusb300, ep->epnum); - ep->stall = 1; - if (wedge) - ep->wedged = 1; - } else { - fusb300_clear_epnstall(fusb300, ep->epnum); - ep->stall = 0; - ep->wedged = 0; - } - -out: - spin_unlock_irqrestore(&ep->fusb300->lock, flags); - return ret; -} - -static int fusb300_set_halt(struct usb_ep *_ep, int value) -{ - return fusb300_set_halt_and_wedge(_ep, value, 0); -} - -static int fusb300_set_wedge(struct usb_ep *_ep) -{ - return fusb300_set_halt_and_wedge(_ep, 1, 1); -} - -static void fusb300_fifo_flush(struct usb_ep *_ep) -{ -} - -static const struct usb_ep_ops fusb300_ep_ops = { - .enable = fusb300_enable, - .disable = fusb300_disable, - - .alloc_request = fusb300_alloc_request, - .free_request = fusb300_free_request, - - .queue = fusb300_queue, - .dequeue = fusb300_dequeue, - - .set_halt = fusb300_set_halt, - .fifo_flush = fusb300_fifo_flush, - .set_wedge = fusb300_set_wedge, -}; - -/*****************************************************************************/ -static void fusb300_clear_int(struct fusb300 *fusb300, u32 offset, - u32 value) -{ - iowrite32(value, fusb300->reg + offset); -} - -static void fusb300_reset(void) -{ -} - -static void fusb300_set_cxstall(struct fusb300 *fusb300) -{ - fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR, - FUSB300_CSR_STL); -} - -static void fusb300_set_cxdone(struct fusb300 *fusb300) -{ - fusb300_enable_bit(fusb300, FUSB300_OFFSET_CSR, - FUSB300_CSR_DONE); -} - -/* read data from cx fifo */ -static void fusb300_rdcxf(struct fusb300 *fusb300, - u8 *buffer, u32 length) -{ - int i = 0; - u8 *tmp; - u32 data; - - tmp = buffer; - - for (i = (length >> 2); i > 0; i--) { - data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); - printk(KERN_DEBUG " 0x%x\n", data); - *tmp = data & 0xFF; - *(tmp + 1) = (data >> 8) & 0xFF; - *(tmp + 2) = (data >> 16) & 0xFF; - *(tmp + 3) = (data >> 24) & 0xFF; - tmp = tmp + 4; - } - - switch (length % 4) { - case 1: - data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); - printk(KERN_DEBUG " 0x%x\n", data); - *tmp = data & 0xFF; - break; - case 2: - data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); - printk(KERN_DEBUG " 0x%x\n", data); - *tmp = data & 0xFF; - *(tmp + 1) = (data >> 8) & 0xFF; - break; - case 3: - data = ioread32(fusb300->reg + FUSB300_OFFSET_CXPORT); - printk(KERN_DEBUG " 0x%x\n", data); - *tmp = data & 0xFF; - *(tmp + 1) = (data >> 8) & 0xFF; - *(tmp + 2) = (data >> 16) & 0xFF; - break; - default: - break; - } -} - -static void fusb300_rdfifo(struct fusb300_ep *ep, - struct fusb300_request *req, - u32 length) -{ - int i = 0; - u8 *tmp; - u32 data, reg; - struct fusb300 *fusb300 = ep->fusb300; - - tmp = req->req.buf + req->req.actual; - req->req.actual += length; - - if (req->req.actual > req->req.length) - printk(KERN_DEBUG "req->req.actual > req->req.length\n"); - - for (i = (length >> 2); i > 0; i--) { - data = ioread32(fusb300->reg + - FUSB300_OFFSET_EPPORT(ep->epnum)); - *tmp = data & 0xFF; - *(tmp + 1) = (data >> 8) & 0xFF; - *(tmp + 2) = (data >> 16) & 0xFF; - *(tmp + 3) = (data >> 24) & 0xFF; - tmp = tmp + 4; - } - - switch (length % 4) { - case 1: - data = ioread32(fusb300->reg + - FUSB300_OFFSET_EPPORT(ep->epnum)); - *tmp = data & 0xFF; - break; - case 2: - data = ioread32(fusb300->reg + - FUSB300_OFFSET_EPPORT(ep->epnum)); - *tmp = data & 0xFF; - *(tmp + 1) = (data >> 8) & 0xFF; - break; - case 3: - data = ioread32(fusb300->reg + - FUSB300_OFFSET_EPPORT(ep->epnum)); - *tmp = data & 0xFF; - *(tmp + 1) = (data >> 8) & 0xFF; - *(tmp + 2) = (data >> 16) & 0xFF; - break; - default: - break; - } - - do { - reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1); - reg &= FUSB300_IGR1_SYNF0_EMPTY_INT; - if (i) - printk(KERN_INFO "sync fifo is not empty!\n"); - i++; - } while (!reg); -} - -static u8 fusb300_get_epnstall(struct fusb300 *fusb300, u8 ep) -{ - u8 value; - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPSET0(ep)); - - value = reg & FUSB300_EPSET0_STL; - - return value; -} - -static u8 fusb300_get_cxstall(struct fusb300 *fusb300) -{ - u8 value; - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_CSR); - - value = (reg & FUSB300_CSR_STL) >> 1; - - return value; -} - -static void request_error(struct fusb300 *fusb300) -{ - fusb300_set_cxstall(fusb300); - printk(KERN_DEBUG "request error!!\n"); -} - -static void get_status(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) -__releases(fusb300->lock) -__acquires(fusb300->lock) -{ - u8 ep; - u16 status = 0; - u16 w_index = ctrl->wIndex; - - switch (ctrl->bRequestType & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - status = 1 << USB_DEVICE_SELF_POWERED; - break; - case USB_RECIP_INTERFACE: - status = 0; - break; - case USB_RECIP_ENDPOINT: - ep = w_index & USB_ENDPOINT_NUMBER_MASK; - if (ep) { - if (fusb300_get_epnstall(fusb300, ep)) - status = 1 << USB_ENDPOINT_HALT; - } else { - if (fusb300_get_cxstall(fusb300)) - status = 0; - } - break; - - default: - request_error(fusb300); - return; /* exit */ - } - - fusb300->ep0_data = cpu_to_le16(status); - fusb300->ep0_req->buf = &fusb300->ep0_data; - fusb300->ep0_req->length = 2; - - spin_unlock(&fusb300->lock); - fusb300_queue(fusb300->gadget.ep0, fusb300->ep0_req, GFP_KERNEL); - spin_lock(&fusb300->lock); -} - -static void set_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) -{ - u8 ep; - - switch (ctrl->bRequestType & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - fusb300_set_cxdone(fusb300); - break; - case USB_RECIP_INTERFACE: - fusb300_set_cxdone(fusb300); - break; - case USB_RECIP_ENDPOINT: { - u16 w_index = le16_to_cpu(ctrl->wIndex); - - ep = w_index & USB_ENDPOINT_NUMBER_MASK; - if (ep) - fusb300_set_epnstall(fusb300, ep); - else - fusb300_set_cxstall(fusb300); - fusb300_set_cxdone(fusb300); - } - break; - default: - request_error(fusb300); - break; - } -} - -static void fusb300_clear_seqnum(struct fusb300 *fusb300, u8 ep) -{ - fusb300_enable_bit(fusb300, FUSB300_OFFSET_EPSET0(ep), - FUSB300_EPSET0_CLRSEQNUM); -} - -static void clear_feature(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) -{ - struct fusb300_ep *ep = - fusb300->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; - - switch (ctrl->bRequestType & USB_RECIP_MASK) { - case USB_RECIP_DEVICE: - fusb300_set_cxdone(fusb300); - break; - case USB_RECIP_INTERFACE: - fusb300_set_cxdone(fusb300); - break; - case USB_RECIP_ENDPOINT: - if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { - if (ep->wedged) { - fusb300_set_cxdone(fusb300); - break; - } - if (ep->stall) { - ep->stall = 0; - fusb300_clear_seqnum(fusb300, ep->epnum); - fusb300_clear_epnstall(fusb300, ep->epnum); - if (!list_empty(&ep->queue)) - enable_fifo_int(ep); - } - } - fusb300_set_cxdone(fusb300); - break; - default: - request_error(fusb300); - break; - } -} - -static void fusb300_set_dev_addr(struct fusb300 *fusb300, u16 addr) -{ - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_DAR); - - reg &= ~FUSB300_DAR_DRVADDR_MSK; - reg |= FUSB300_DAR_DRVADDR(addr); - - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_DAR); -} - -static void set_address(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) -{ - if (ctrl->wValue >= 0x0100) - request_error(fusb300); - else { - fusb300_set_dev_addr(fusb300, ctrl->wValue); - fusb300_set_cxdone(fusb300); - } -} - -#define UVC_COPY_DESCRIPTORS(mem, src) \ - do { \ - const struct usb_descriptor_header * const *__src; \ - for (__src = src; *__src; ++__src) { \ - memcpy(mem, *__src, (*__src)->bLength); \ - mem += (*__src)->bLength; \ - } \ - } while (0) - -static int setup_packet(struct fusb300 *fusb300, struct usb_ctrlrequest *ctrl) -{ - u8 *p = (u8 *)ctrl; - u8 ret = 0; - u8 i = 0; - - fusb300_rdcxf(fusb300, p, 8); - fusb300->ep0_dir = ctrl->bRequestType & USB_DIR_IN; - fusb300->ep0_length = ctrl->wLength; - - /* check request */ - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { - switch (ctrl->bRequest) { - case USB_REQ_GET_STATUS: - get_status(fusb300, ctrl); - break; - case USB_REQ_CLEAR_FEATURE: - clear_feature(fusb300, ctrl); - break; - case USB_REQ_SET_FEATURE: - set_feature(fusb300, ctrl); - break; - case USB_REQ_SET_ADDRESS: - set_address(fusb300, ctrl); - break; - case USB_REQ_SET_CONFIGURATION: - fusb300_enable_bit(fusb300, FUSB300_OFFSET_DAR, - FUSB300_DAR_SETCONFG); - /* clear sequence number */ - for (i = 1; i <= FUSB300_MAX_NUM_EP; i++) - fusb300_clear_seqnum(fusb300, i); - fusb300->reenum = 1; - ret = 1; - break; - default: - ret = 1; - break; - } - } else - ret = 1; - - return ret; -} - -static void done(struct fusb300_ep *ep, struct fusb300_request *req, - int status) -{ - list_del_init(&req->queue); - - /* don't modify queue heads during completion callback */ - if (ep->fusb300->gadget.speed == USB_SPEED_UNKNOWN) - req->req.status = -ESHUTDOWN; - else - req->req.status = status; - - spin_unlock(&ep->fusb300->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&ep->fusb300->lock); - - if (ep->epnum) { - disable_fifo_int(ep); - if (!list_empty(&ep->queue)) - enable_fifo_int(ep); - } else - fusb300_set_cxdone(ep->fusb300); -} - -static void fusb300_fill_idma_prdtbl(struct fusb300_ep *ep, dma_addr_t d, - u32 len) -{ - u32 value; - u32 reg; - - /* wait SW owner */ - do { - reg = ioread32(ep->fusb300->reg + - FUSB300_OFFSET_EPPRD_W0(ep->epnum)); - reg &= FUSB300_EPPRD0_H; - } while (reg); - - iowrite32(d, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W1(ep->epnum)); - - value = FUSB300_EPPRD0_BTC(len) | FUSB300_EPPRD0_H | - FUSB300_EPPRD0_F | FUSB300_EPPRD0_L | FUSB300_EPPRD0_I; - iowrite32(value, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W0(ep->epnum)); - - iowrite32(0x0, ep->fusb300->reg + FUSB300_OFFSET_EPPRD_W2(ep->epnum)); - - fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_EPPRDRDY, - FUSB300_EPPRDR_EP_PRD_RDY(ep->epnum)); -} - -static void fusb300_wait_idma_finished(struct fusb300_ep *ep) -{ - u32 reg; - - do { - reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR1); - if ((reg & FUSB300_IGR1_VBUS_CHG_INT) || - (reg & FUSB300_IGR1_WARM_RST_INT) || - (reg & FUSB300_IGR1_HOT_RST_INT) || - (reg & FUSB300_IGR1_USBRST_INT) - ) - goto IDMA_RESET; - reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGR0); - reg &= FUSB300_IGR0_EPn_PRD_INT(ep->epnum); - } while (!reg); - - fusb300_clear_int(ep->fusb300, FUSB300_OFFSET_IGR0, - FUSB300_IGR0_EPn_PRD_INT(ep->epnum)); - return; - -IDMA_RESET: - reg = ioread32(ep->fusb300->reg + FUSB300_OFFSET_IGER0); - reg &= ~FUSB300_IGER0_EEPn_PRD_INT(ep->epnum); - iowrite32(reg, ep->fusb300->reg + FUSB300_OFFSET_IGER0); -} - -static void fusb300_set_idma(struct fusb300_ep *ep, - struct fusb300_request *req) -{ - int ret; - - ret = usb_gadget_map_request(&ep->fusb300->gadget, - &req->req, DMA_TO_DEVICE); - if (ret) - return; - - fusb300_enable_bit(ep->fusb300, FUSB300_OFFSET_IGER0, - FUSB300_IGER0_EEPn_PRD_INT(ep->epnum)); - - fusb300_fill_idma_prdtbl(ep, req->req.dma, req->req.length); - /* check idma is done */ - fusb300_wait_idma_finished(ep); - - usb_gadget_unmap_request(&ep->fusb300->gadget, - &req->req, DMA_TO_DEVICE); -} - -static void in_ep_fifo_handler(struct fusb300_ep *ep) -{ - struct fusb300_request *req = list_entry(ep->queue.next, - struct fusb300_request, queue); - - if (req->req.length) - fusb300_set_idma(ep, req); - done(ep, req, 0); -} - -static void out_ep_fifo_handler(struct fusb300_ep *ep) -{ - struct fusb300 *fusb300 = ep->fusb300; - struct fusb300_request *req = list_entry(ep->queue.next, - struct fusb300_request, queue); - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_EPFFR(ep->epnum)); - u32 length = reg & FUSB300_FFR_BYCNT; - - fusb300_rdfifo(ep, req, length); - - /* finish out transfer */ - if ((req->req.length == req->req.actual) || (length < ep->ep.maxpacket)) - done(ep, req, 0); -} - -static void check_device_mode(struct fusb300 *fusb300) -{ - u32 reg = ioread32(fusb300->reg + FUSB300_OFFSET_GCR); - - switch (reg & FUSB300_GCR_DEVEN_MSK) { - case FUSB300_GCR_DEVEN_SS: - fusb300->gadget.speed = USB_SPEED_SUPER; - break; - case FUSB300_GCR_DEVEN_HS: - fusb300->gadget.speed = USB_SPEED_HIGH; - break; - case FUSB300_GCR_DEVEN_FS: - fusb300->gadget.speed = USB_SPEED_FULL; - break; - default: - fusb300->gadget.speed = USB_SPEED_UNKNOWN; - break; - } - printk(KERN_INFO "dev_mode = %d\n", (reg & FUSB300_GCR_DEVEN_MSK)); -} - - -static void fusb300_ep0out(struct fusb300 *fusb300) -{ - struct fusb300_ep *ep = fusb300->ep[0]; - u32 reg; - - if (!list_empty(&ep->queue)) { - struct fusb300_request *req; - - req = list_first_entry(&ep->queue, - struct fusb300_request, queue); - if (req->req.length) - fusb300_rdcxf(ep->fusb300, req->req.buf, - req->req.length); - done(ep, req, 0); - reg = ioread32(fusb300->reg + FUSB300_OFFSET_IGER1); - reg &= ~FUSB300_IGER1_CX_OUT_INT; - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_IGER1); - } else - pr_err("%s : empty queue\n", __func__); -} - -static void fusb300_ep0in(struct fusb300 *fusb300) -{ - struct fusb300_request *req; - struct fusb300_ep *ep = fusb300->ep[0]; - - if ((!list_empty(&ep->queue)) && (fusb300->ep0_dir)) { - req = list_entry(ep->queue.next, - struct fusb300_request, queue); - if (req->req.length) - fusb300_wrcxf(ep, req); - if ((req->req.length - req->req.actual) < ep->ep.maxpacket) - done(ep, req, 0); - } else - fusb300_set_cxdone(fusb300); -} - -static void fusb300_grp2_handler(void) -{ -} - -static void fusb300_grp3_handler(void) -{ -} - -static void fusb300_grp4_handler(void) -{ -} - -static void fusb300_grp5_handler(void) -{ -} - -static irqreturn_t fusb300_irq(int irq, void *_fusb300) -{ - struct fusb300 *fusb300 = _fusb300; - u32 int_grp1 = ioread32(fusb300->reg + FUSB300_OFFSET_IGR1); - u32 int_grp1_en = ioread32(fusb300->reg + FUSB300_OFFSET_IGER1); - u32 int_grp0 = ioread32(fusb300->reg + FUSB300_OFFSET_IGR0); - u32 int_grp0_en = ioread32(fusb300->reg + FUSB300_OFFSET_IGER0); - struct usb_ctrlrequest ctrl; - u8 in; - u32 reg; - int i; - - spin_lock(&fusb300->lock); - - int_grp1 &= int_grp1_en; - int_grp0 &= int_grp0_en; - - if (int_grp1 & FUSB300_IGR1_WARM_RST_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_WARM_RST_INT); - printk(KERN_INFO"fusb300_warmreset\n"); - fusb300_reset(); - } - - if (int_grp1 & FUSB300_IGR1_HOT_RST_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_HOT_RST_INT); - printk(KERN_INFO"fusb300_hotreset\n"); - fusb300_reset(); - } - - if (int_grp1 & FUSB300_IGR1_USBRST_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_USBRST_INT); - fusb300_reset(); - } - /* COMABT_INT has a highest priority */ - - if (int_grp1 & FUSB300_IGR1_CX_COMABT_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_CX_COMABT_INT); - printk(KERN_INFO"fusb300_ep0abt\n"); - } - - if (int_grp1 & FUSB300_IGR1_VBUS_CHG_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_VBUS_CHG_INT); - printk(KERN_INFO"fusb300_vbus_change\n"); - } - - if (int_grp1 & FUSB300_IGR1_U3_EXIT_FAIL_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U3_EXIT_FAIL_INT); - } - - if (int_grp1 & FUSB300_IGR1_U2_EXIT_FAIL_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U2_EXIT_FAIL_INT); - } - - if (int_grp1 & FUSB300_IGR1_U1_EXIT_FAIL_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U1_EXIT_FAIL_INT); - } - - if (int_grp1 & FUSB300_IGR1_U2_ENTRY_FAIL_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U2_ENTRY_FAIL_INT); - } - - if (int_grp1 & FUSB300_IGR1_U1_ENTRY_FAIL_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U1_ENTRY_FAIL_INT); - } - - if (int_grp1 & FUSB300_IGR1_U3_EXIT_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U3_EXIT_INT); - printk(KERN_INFO "FUSB300_IGR1_U3_EXIT_INT\n"); - } - - if (int_grp1 & FUSB300_IGR1_U2_EXIT_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U2_EXIT_INT); - printk(KERN_INFO "FUSB300_IGR1_U2_EXIT_INT\n"); - } - - if (int_grp1 & FUSB300_IGR1_U1_EXIT_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U1_EXIT_INT); - printk(KERN_INFO "FUSB300_IGR1_U1_EXIT_INT\n"); - } - - if (int_grp1 & FUSB300_IGR1_U3_ENTRY_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U3_ENTRY_INT); - printk(KERN_INFO "FUSB300_IGR1_U3_ENTRY_INT\n"); - fusb300_enable_bit(fusb300, FUSB300_OFFSET_SSCR1, - FUSB300_SSCR1_GO_U3_DONE); - } - - if (int_grp1 & FUSB300_IGR1_U2_ENTRY_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U2_ENTRY_INT); - printk(KERN_INFO "FUSB300_IGR1_U2_ENTRY_INT\n"); - } - - if (int_grp1 & FUSB300_IGR1_U1_ENTRY_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_U1_ENTRY_INT); - printk(KERN_INFO "FUSB300_IGR1_U1_ENTRY_INT\n"); - } - - if (int_grp1 & FUSB300_IGR1_RESM_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_RESM_INT); - printk(KERN_INFO "fusb300_resume\n"); - } - - if (int_grp1 & FUSB300_IGR1_SUSP_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_SUSP_INT); - printk(KERN_INFO "fusb300_suspend\n"); - } - - if (int_grp1 & FUSB300_IGR1_HS_LPM_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_HS_LPM_INT); - printk(KERN_INFO "fusb300_HS_LPM_INT\n"); - } - - if (int_grp1 & FUSB300_IGR1_DEV_MODE_CHG_INT) { - fusb300_clear_int(fusb300, FUSB300_OFFSET_IGR1, - FUSB300_IGR1_DEV_MODE_CHG_INT); - check_device_mode(fusb300); - } - - if (int_grp1 & FUSB300_IGR1_CX_COMFAIL_INT) { - fusb300_set_cxstall(fusb300); - printk(KERN_INFO "fusb300_ep0fail\n"); - } - - if (int_grp1 & FUSB300_IGR1_CX_SETUP_INT) { - printk(KERN_INFO "fusb300_ep0setup\n"); - if (setup_packet(fusb300, &ctrl)) { - spin_unlock(&fusb300->lock); - if (fusb300->driver->setup(&fusb300->gadget, &ctrl) < 0) - fusb300_set_cxstall(fusb300); - spin_lock(&fusb300->lock); - } - } - - if (int_grp1 & FUSB300_IGR1_CX_CMDEND_INT) - printk(KERN_INFO "fusb300_cmdend\n"); - - - if (int_grp1 & FUSB300_IGR1_CX_OUT_INT) { - printk(KERN_INFO "fusb300_cxout\n"); - fusb300_ep0out(fusb300); - } - - if (int_grp1 & FUSB300_IGR1_CX_IN_INT) { - printk(KERN_INFO "fusb300_cxin\n"); - fusb300_ep0in(fusb300); - } - - if (int_grp1 & FUSB300_IGR1_INTGRP5) - fusb300_grp5_handler(); - - if (int_grp1 & FUSB300_IGR1_INTGRP4) - fusb300_grp4_handler(); - - if (int_grp1 & FUSB300_IGR1_INTGRP3) - fusb300_grp3_handler(); - - if (int_grp1 & FUSB300_IGR1_INTGRP2) - fusb300_grp2_handler(); - - if (int_grp0) { - for (i = 1; i < FUSB300_MAX_NUM_EP; i++) { - if (int_grp0 & FUSB300_IGR0_EPn_FIFO_INT(i)) { - reg = ioread32(fusb300->reg + - FUSB300_OFFSET_EPSET1(i)); - in = (reg & FUSB300_EPSET1_DIRIN) ? 1 : 0; - if (in) - in_ep_fifo_handler(fusb300->ep[i]); - else - out_ep_fifo_handler(fusb300->ep[i]); - } - } - } - - spin_unlock(&fusb300->lock); - - return IRQ_HANDLED; -} - -static void fusb300_set_u2_timeout(struct fusb300 *fusb300, - u32 time) -{ - u32 reg; - - reg = ioread32(fusb300->reg + FUSB300_OFFSET_TT); - reg &= ~0xff; - reg |= FUSB300_SSCR2_U2TIMEOUT(time); - - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_TT); -} - -static void fusb300_set_u1_timeout(struct fusb300 *fusb300, - u32 time) -{ - u32 reg; - - reg = ioread32(fusb300->reg + FUSB300_OFFSET_TT); - reg &= ~(0xff << 8); - reg |= FUSB300_SSCR2_U1TIMEOUT(time); - - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_TT); -} - -static void init_controller(struct fusb300 *fusb300) -{ - u32 reg; - u32 mask = 0; - u32 val = 0; - - /* split on */ - mask = val = FUSB300_AHBBCR_S0_SPLIT_ON | FUSB300_AHBBCR_S1_SPLIT_ON; - reg = ioread32(fusb300->reg + FUSB300_OFFSET_AHBCR); - reg &= ~mask; - reg |= val; - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_AHBCR); - - /* enable high-speed LPM */ - mask = val = FUSB300_HSCR_HS_LPM_PERMIT; - reg = ioread32(fusb300->reg + FUSB300_OFFSET_HSCR); - reg &= ~mask; - reg |= val; - iowrite32(reg, fusb300->reg + FUSB300_OFFSET_HSCR); - - /*set u1 u2 timer*/ - fusb300_set_u2_timeout(fusb300, 0xff); - fusb300_set_u1_timeout(fusb300, 0xff); - - /* enable all grp1 interrupt */ - iowrite32(0xcfffff9f, fusb300->reg + FUSB300_OFFSET_IGER1); -} -/*------------------------------------------------------------------------*/ -static int fusb300_udc_start(struct usb_gadget *g, - struct usb_gadget_driver *driver) -{ - struct fusb300 *fusb300 = to_fusb300(g); - - /* hook up the driver */ - fusb300->driver = driver; - - return 0; -} - -static int fusb300_udc_stop(struct usb_gadget *g) -{ - struct fusb300 *fusb300 = to_fusb300(g); - - init_controller(fusb300); - fusb300->driver = NULL; - - return 0; -} -/*--------------------------------------------------------------------------*/ - -static int fusb300_udc_pullup(struct usb_gadget *_gadget, int is_active) -{ - return 0; -} - -static const struct usb_gadget_ops fusb300_gadget_ops = { - .pullup = fusb300_udc_pullup, - .udc_start = fusb300_udc_start, - .udc_stop = fusb300_udc_stop, -}; - -static void fusb300_remove(struct platform_device *pdev) -{ - struct fusb300 *fusb300 = platform_get_drvdata(pdev); - int i; - - usb_del_gadget_udc(&fusb300->gadget); - iounmap(fusb300->reg); - free_irq(platform_get_irq(pdev, 0), fusb300); - free_irq(platform_get_irq(pdev, 1), fusb300); - - fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req); - for (i = 0; i < FUSB300_MAX_NUM_EP; i++) - kfree(fusb300->ep[i]); - kfree(fusb300); -} - -static int fusb300_probe(struct platform_device *pdev) -{ - struct resource *res, *ires, *ires1; - void __iomem *reg = NULL; - struct fusb300 *fusb300 = NULL; - struct fusb300_ep *_ep[FUSB300_MAX_NUM_EP]; - int ret = 0; - int i; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - pr_err("platform_get_resource error.\n"); - goto clean_up; - } - - ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!ires) { - ret = -ENODEV; - dev_err(&pdev->dev, - "platform_get_resource IORESOURCE_IRQ error.\n"); - goto clean_up; - } - - ires1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); - if (!ires1) { - ret = -ENODEV; - dev_err(&pdev->dev, - "platform_get_resource IORESOURCE_IRQ 1 error.\n"); - goto clean_up; - } - - reg = ioremap(res->start, resource_size(res)); - if (reg == NULL) { - ret = -ENOMEM; - pr_err("ioremap error.\n"); - goto clean_up; - } - - /* initialize udc */ - fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL); - if (fusb300 == NULL) { - ret = -ENOMEM; - goto clean_up; - } - - for (i = 0; i < FUSB300_MAX_NUM_EP; i++) { - _ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL); - if (_ep[i] == NULL) { - ret = -ENOMEM; - goto clean_up; - } - fusb300->ep[i] = _ep[i]; - } - - spin_lock_init(&fusb300->lock); - - platform_set_drvdata(pdev, fusb300); - - fusb300->gadget.ops = &fusb300_gadget_ops; - - fusb300->gadget.max_speed = USB_SPEED_HIGH; - fusb300->gadget.name = udc_name; - fusb300->reg = reg; - - ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED, - udc_name, fusb300); - if (ret < 0) { - pr_err("request_irq error (%d)\n", ret); - goto clean_up; - } - - ret = request_irq(ires1->start, fusb300_irq, - IRQF_SHARED, udc_name, fusb300); - if (ret < 0) { - pr_err("request_irq1 error (%d)\n", ret); - goto err_request_irq1; - } - - INIT_LIST_HEAD(&fusb300->gadget.ep_list); - - for (i = 0; i < FUSB300_MAX_NUM_EP ; i++) { - struct fusb300_ep *ep = fusb300->ep[i]; - - if (i != 0) { - INIT_LIST_HEAD(&fusb300->ep[i]->ep.ep_list); - list_add_tail(&fusb300->ep[i]->ep.ep_list, - &fusb300->gadget.ep_list); - } - ep->fusb300 = fusb300; - INIT_LIST_HEAD(&ep->queue); - ep->ep.name = fusb300_ep_name[i]; - ep->ep.ops = &fusb300_ep_ops; - usb_ep_set_maxpacket_limit(&ep->ep, HS_BULK_MAX_PACKET_SIZE); - - if (i == 0) { - ep->ep.caps.type_control = true; - } else { - ep->ep.caps.type_iso = true; - ep->ep.caps.type_bulk = true; - ep->ep.caps.type_int = true; - } - - ep->ep.caps.dir_in = true; - ep->ep.caps.dir_out = true; - } - usb_ep_set_maxpacket_limit(&fusb300->ep[0]->ep, HS_CTL_MAX_PACKET_SIZE); - fusb300->ep[0]->epnum = 0; - fusb300->gadget.ep0 = &fusb300->ep[0]->ep; - INIT_LIST_HEAD(&fusb300->gadget.ep0->ep_list); - - fusb300->ep0_req = fusb300_alloc_request(&fusb300->ep[0]->ep, - GFP_KERNEL); - if (fusb300->ep0_req == NULL) { - ret = -ENOMEM; - goto err_alloc_request; - } - - init_controller(fusb300); - ret = usb_add_gadget_udc(&pdev->dev, &fusb300->gadget); - if (ret) - goto err_add_udc; - - dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); - - return 0; - -err_add_udc: - fusb300_free_request(&fusb300->ep[0]->ep, fusb300->ep0_req); - -err_alloc_request: - free_irq(ires1->start, fusb300); - -err_request_irq1: - free_irq(ires->start, fusb300); - -clean_up: - if (fusb300) { - if (fusb300->ep0_req) - fusb300_free_request(&fusb300->ep[0]->ep, - fusb300->ep0_req); - for (i = 0; i < FUSB300_MAX_NUM_EP; i++) - kfree(fusb300->ep[i]); - kfree(fusb300); - } - if (reg) - iounmap(reg); - - return ret; -} - -static struct platform_driver fusb300_driver = { - .probe = fusb300_probe, - .remove = fusb300_remove, - .driver = { - .name = udc_name, - }, -}; - -module_platform_driver(fusb300_driver); diff --git a/drivers/usb/gadget/udc/fusb300_udc.h b/drivers/usb/gadget/udc/fusb300_udc.h deleted file mode 100644 index eb3d6d379ba7..000000000000 --- a/drivers/usb/gadget/udc/fusb300_udc.h +++ /dev/null @@ -1,675 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Fusb300 UDC (USB gadget) - * - * Copyright (C) 2010 Faraday Technology Corp. - * - * Author : Yuan-hsin Chen <yhchen@faraday-tech.com> - */ - - -#ifndef __FUSB300_UDC_H__ -#define __FUSB300_UDC_H__ - -#include <linux/kernel.h> - -#define FUSB300_OFFSET_GCR 0x00 -#define FUSB300_OFFSET_GTM 0x04 -#define FUSB300_OFFSET_DAR 0x08 -#define FUSB300_OFFSET_CSR 0x0C -#define FUSB300_OFFSET_CXPORT 0x10 -#define FUSB300_OFFSET_EPSET0(n) (0x20 + (n - 1) * 0x30) -#define FUSB300_OFFSET_EPSET1(n) (0x24 + (n - 1) * 0x30) -#define FUSB300_OFFSET_EPSET2(n) (0x28 + (n - 1) * 0x30) -#define FUSB300_OFFSET_EPFFR(n) (0x2c + (n - 1) * 0x30) -#define FUSB300_OFFSET_EPSTRID(n) (0x40 + (n - 1) * 0x30) -#define FUSB300_OFFSET_HSPTM 0x300 -#define FUSB300_OFFSET_HSCR 0x304 -#define FUSB300_OFFSET_SSCR0 0x308 -#define FUSB300_OFFSET_SSCR1 0x30C -#define FUSB300_OFFSET_TT 0x310 -#define FUSB300_OFFSET_DEVNOTF 0x314 -#define FUSB300_OFFSET_DNC1 0x318 -#define FUSB300_OFFSET_CS 0x31C -#define FUSB300_OFFSET_SOF 0x324 -#define FUSB300_OFFSET_EFCS 0x328 -#define FUSB300_OFFSET_IGR0 0x400 -#define FUSB300_OFFSET_IGR1 0x404 -#define FUSB300_OFFSET_IGR2 0x408 -#define FUSB300_OFFSET_IGR3 0x40C -#define FUSB300_OFFSET_IGR4 0x410 -#define FUSB300_OFFSET_IGR5 0x414 -#define FUSB300_OFFSET_IGER0 0x420 -#define FUSB300_OFFSET_IGER1 0x424 -#define FUSB300_OFFSET_IGER2 0x428 -#define FUSB300_OFFSET_IGER3 0x42C -#define FUSB300_OFFSET_IGER4 0x430 -#define FUSB300_OFFSET_IGER5 0x434 -#define FUSB300_OFFSET_DMAHMER 0x500 -#define FUSB300_OFFSET_EPPRDRDY 0x504 -#define FUSB300_OFFSET_DMAEPMR 0x508 -#define FUSB300_OFFSET_DMAENR 0x50C -#define FUSB300_OFFSET_DMAAPR 0x510 -#define FUSB300_OFFSET_AHBCR 0x514 -#define FUSB300_OFFSET_EPPRD_W0(n) (0x520 + (n - 1) * 0x10) -#define FUSB300_OFFSET_EPPRD_W1(n) (0x524 + (n - 1) * 0x10) -#define FUSB300_OFFSET_EPPRD_W2(n) (0x528 + (n - 1) * 0x10) -#define FUSB300_OFFSET_EPRD_PTR(n) (0x52C + (n - 1) * 0x10) -#define FUSB300_OFFSET_BUFDBG_START 0x800 -#define FUSB300_OFFSET_BUFDBG_END 0xBFC -#define FUSB300_OFFSET_EPPORT(n) (0x1010 + (n - 1) * 0x10) - -/* - * * Global Control Register (offset = 000H) - * */ -#define FUSB300_GCR_SF_RST (1 << 8) -#define FUSB300_GCR_VBUS_STATUS (1 << 7) -#define FUSB300_GCR_FORCE_HS_SUSP (1 << 6) -#define FUSB300_GCR_SYNC_FIFO1_CLR (1 << 5) -#define FUSB300_GCR_SYNC_FIFO0_CLR (1 << 4) -#define FUSB300_GCR_FIFOCLR (1 << 3) -#define FUSB300_GCR_GLINTEN (1 << 2) -#define FUSB300_GCR_DEVEN_FS 0x3 -#define FUSB300_GCR_DEVEN_HS 0x2 -#define FUSB300_GCR_DEVEN_SS 0x1 -#define FUSB300_GCR_DEVDIS 0x0 -#define FUSB300_GCR_DEVEN_MSK 0x3 - - -/* - * *Global Test Mode (offset = 004H) - * */ -#define FUSB300_GTM_TST_DIS_SOFGEN (1 << 16) -#define FUSB300_GTM_TST_CUR_EP_ENTRY(n) ((n & 0xF) << 12) -#define FUSB300_GTM_TST_EP_ENTRY(n) ((n & 0xF) << 8) -#define FUSB300_GTM_TST_EP_NUM(n) ((n & 0xF) << 4) -#define FUSB300_GTM_TST_FIFO_DEG (1 << 1) -#define FUSB300_GTM_TSTMODE (1 << 0) - -/* - * * Device Address Register (offset = 008H) - * */ -#define FUSB300_DAR_SETCONFG (1 << 7) -#define FUSB300_DAR_DRVADDR(x) (x & 0x7F) -#define FUSB300_DAR_DRVADDR_MSK 0x7F - -/* - * *Control Transfer Configuration and Status Register - * (CX_Config_Status, offset = 00CH) - * */ -#define FUSB300_CSR_LEN(x) ((x & 0xFFFF) << 8) -#define FUSB300_CSR_LEN_MSK (0xFFFF << 8) -#define FUSB300_CSR_EMP (1 << 4) -#define FUSB300_CSR_FUL (1 << 3) -#define FUSB300_CSR_CLR (1 << 2) -#define FUSB300_CSR_STL (1 << 1) -#define FUSB300_CSR_DONE (1 << 0) - -/* - * * EPn Setting 0 (EPn_SET0, offset = 020H+(n-1)*30H, n=1~15 ) - * */ -#define FUSB300_EPSET0_STL_CLR (1 << 3) -#define FUSB300_EPSET0_CLRSEQNUM (1 << 2) -#define FUSB300_EPSET0_STL (1 << 0) - -/* - * * EPn Setting 1 (EPn_SET1, offset = 024H+(n-1)*30H, n=1~15) - * */ -#define FUSB300_EPSET1_START_ENTRY(x) ((x & 0xFF) << 24) -#define FUSB300_EPSET1_START_ENTRY_MSK (0xFF << 24) -#define FUSB300_EPSET1_FIFOENTRY(x) ((x & 0x1F) << 12) -#define FUSB300_EPSET1_FIFOENTRY_MSK (0x1f << 12) -#define FUSB300_EPSET1_INTERVAL(x) ((x & 0x7) << 6) -#define FUSB300_EPSET1_BWNUM(x) ((x & 0x3) << 4) -#define FUSB300_EPSET1_TYPEISO (1 << 2) -#define FUSB300_EPSET1_TYPEBLK (2 << 2) -#define FUSB300_EPSET1_TYPEINT (3 << 2) -#define FUSB300_EPSET1_TYPE(x) ((x & 0x3) << 2) -#define FUSB300_EPSET1_TYPE_MSK (0x3 << 2) -#define FUSB300_EPSET1_DIROUT (0 << 1) -#define FUSB300_EPSET1_DIRIN (1 << 1) -#define FUSB300_EPSET1_DIR(x) ((x & 0x1) << 1) -#define FUSB300_EPSET1_DIRIN (1 << 1) -#define FUSB300_EPSET1_DIR_MSK ((0x1) << 1) -#define FUSB300_EPSET1_ACTDIS 0 -#define FUSB300_EPSET1_ACTEN 1 - -/* - * *EPn Setting 2 (EPn_SET2, offset = 028H+(n-1)*30H, n=1~15) - * */ -#define FUSB300_EPSET2_ADDROFS(x) ((x & 0x7FFF) << 16) -#define FUSB300_EPSET2_ADDROFS_MSK (0x7fff << 16) -#define FUSB300_EPSET2_MPS(x) (x & 0x7FF) -#define FUSB300_EPSET2_MPS_MSK 0x7FF - -/* - * * EPn FIFO Register (offset = 2cH+(n-1)*30H) - * */ -#define FUSB300_FFR_RST (1 << 31) -#define FUSB300_FF_FUL (1 << 30) -#define FUSB300_FF_EMPTY (1 << 29) -#define FUSB300_FFR_BYCNT 0x1FFFF - -/* - * *EPn Stream ID (EPn_STR_ID, offset = 040H+(n-1)*30H, n=1~15) - * */ -#define FUSB300_STRID_STREN (1 << 16) -#define FUSB300_STRID_STRID(x) (x & 0xFFFF) - -/* - * *HS PHY Test Mode (offset = 300H) - * */ -#define FUSB300_HSPTM_TSTPKDONE (1 << 4) -#define FUSB300_HSPTM_TSTPKT (1 << 3) -#define FUSB300_HSPTM_TSTSET0NAK (1 << 2) -#define FUSB300_HSPTM_TSTKSTA (1 << 1) -#define FUSB300_HSPTM_TSTJSTA (1 << 0) - -/* - * *HS Control Register (offset = 304H) - * */ -#define FUSB300_HSCR_HS_LPM_PERMIT (1 << 8) -#define FUSB300_HSCR_HS_LPM_RMWKUP (1 << 7) -#define FUSB300_HSCR_CAP_LPM_RMWKUP (1 << 6) -#define FUSB300_HSCR_HS_GOSUSP (1 << 5) -#define FUSB300_HSCR_HS_GORMWKU (1 << 4) -#define FUSB300_HSCR_CAP_RMWKUP (1 << 3) -#define FUSB300_HSCR_IDLECNT_0MS 0 -#define FUSB300_HSCR_IDLECNT_1MS 1 -#define FUSB300_HSCR_IDLECNT_2MS 2 -#define FUSB300_HSCR_IDLECNT_3MS 3 -#define FUSB300_HSCR_IDLECNT_4MS 4 -#define FUSB300_HSCR_IDLECNT_5MS 5 -#define FUSB300_HSCR_IDLECNT_6MS 6 -#define FUSB300_HSCR_IDLECNT_7MS 7 - -/* - * * SS Controller Register 0 (offset = 308H) - * */ -#define FUSB300_SSCR0_MAX_INTERVAL(x) ((x & 0x7) << 4) -#define FUSB300_SSCR0_U2_FUN_EN (1 << 1) -#define FUSB300_SSCR0_U1_FUN_EN (1 << 0) - -/* - * * SS Controller Register 1 (offset = 30CH) - * */ -#define FUSB300_SSCR1_GO_U3_DONE (1 << 8) -#define FUSB300_SSCR1_TXDEEMPH_LEVEL (1 << 7) -#define FUSB300_SSCR1_DIS_SCRMB (1 << 6) -#define FUSB300_SSCR1_FORCE_RECOVERY (1 << 5) -#define FUSB300_SSCR1_U3_WAKEUP_EN (1 << 4) -#define FUSB300_SSCR1_U2_EXIT_EN (1 << 3) -#define FUSB300_SSCR1_U1_EXIT_EN (1 << 2) -#define FUSB300_SSCR1_U2_ENTRY_EN (1 << 1) -#define FUSB300_SSCR1_U1_ENTRY_EN (1 << 0) - -/* - * *SS Controller Register 2 (offset = 310H) - * */ -#define FUSB300_SSCR2_SS_TX_SWING (1 << 25) -#define FUSB300_SSCR2_FORCE_LINKPM_ACCEPT (1 << 24) -#define FUSB300_SSCR2_U2_INACT_TIMEOUT(x) ((x & 0xFF) << 16) -#define FUSB300_SSCR2_U1TIMEOUT(x) ((x & 0xFF) << 8) -#define FUSB300_SSCR2_U2TIMEOUT(x) (x & 0xFF) - -/* - * *SS Device Notification Control (DEV_NOTF, offset = 314H) - * */ -#define FUSB300_DEVNOTF_CONTEXT0(x) ((x & 0xFFFFFF) << 8) -#define FUSB300_DEVNOTF_TYPE_DIS 0 -#define FUSB300_DEVNOTF_TYPE_FUNCWAKE 1 -#define FUSB300_DEVNOTF_TYPE_LTM 2 -#define FUSB300_DEVNOTF_TYPE_BUSINT_ADJMSG 3 - -/* - * *BFM Arbiter Priority Register (BFM_ARB offset = 31CH) - * */ -#define FUSB300_BFMARB_ARB_M1 (1 << 3) -#define FUSB300_BFMARB_ARB_M0 (1 << 2) -#define FUSB300_BFMARB_ARB_S1 (1 << 1) -#define FUSB300_BFMARB_ARB_S0 1 - -/* - * *Vendor Specific IO Control Register (offset = 320H) - * */ -#define FUSB300_VSIC_VCTLOAD_N (1 << 8) -#define FUSB300_VSIC_VCTL(x) (x & 0x3F) - -/* - * *SOF Mask Timer (offset = 324H) - * */ -#define FUSB300_SOF_MASK_TIMER_HS 0x044c -#define FUSB300_SOF_MASK_TIMER_FS 0x2710 - -/* - * *Error Flag and Control Status (offset = 328H) - * */ -#define FUSB300_EFCS_PM_STATE_U3 3 -#define FUSB300_EFCS_PM_STATE_U2 2 -#define FUSB300_EFCS_PM_STATE_U1 1 -#define FUSB300_EFCS_PM_STATE_U0 0 - -/* - * *Interrupt Group 0 Register (offset = 400H) - * */ -#define FUSB300_IGR0_EP15_PRD_INT (1 << 31) -#define FUSB300_IGR0_EP14_PRD_INT (1 << 30) -#define FUSB300_IGR0_EP13_PRD_INT (1 << 29) -#define FUSB300_IGR0_EP12_PRD_INT (1 << 28) -#define FUSB300_IGR0_EP11_PRD_INT (1 << 27) -#define FUSB300_IGR0_EP10_PRD_INT (1 << 26) -#define FUSB300_IGR0_EP9_PRD_INT (1 << 25) -#define FUSB300_IGR0_EP8_PRD_INT (1 << 24) -#define FUSB300_IGR0_EP7_PRD_INT (1 << 23) -#define FUSB300_IGR0_EP6_PRD_INT (1 << 22) -#define FUSB300_IGR0_EP5_PRD_INT (1 << 21) -#define FUSB300_IGR0_EP4_PRD_INT (1 << 20) -#define FUSB300_IGR0_EP3_PRD_INT (1 << 19) -#define FUSB300_IGR0_EP2_PRD_INT (1 << 18) -#define FUSB300_IGR0_EP1_PRD_INT (1 << 17) -#define FUSB300_IGR0_EPn_PRD_INT(n) (1 << (n + 16)) - -#define FUSB300_IGR0_EP15_FIFO_INT (1 << 15) -#define FUSB300_IGR0_EP14_FIFO_INT (1 << 14) -#define FUSB300_IGR0_EP13_FIFO_INT (1 << 13) -#define FUSB300_IGR0_EP12_FIFO_INT (1 << 12) -#define FUSB300_IGR0_EP11_FIFO_INT (1 << 11) -#define FUSB300_IGR0_EP10_FIFO_INT (1 << 10) -#define FUSB300_IGR0_EP9_FIFO_INT (1 << 9) -#define FUSB300_IGR0_EP8_FIFO_INT (1 << 8) -#define FUSB300_IGR0_EP7_FIFO_INT (1 << 7) -#define FUSB300_IGR0_EP6_FIFO_INT (1 << 6) -#define FUSB300_IGR0_EP5_FIFO_INT (1 << 5) -#define FUSB300_IGR0_EP4_FIFO_INT (1 << 4) -#define FUSB300_IGR0_EP3_FIFO_INT (1 << 3) -#define FUSB300_IGR0_EP2_FIFO_INT (1 << 2) -#define FUSB300_IGR0_EP1_FIFO_INT (1 << 1) -#define FUSB300_IGR0_EPn_FIFO_INT(n) (1 << n) - -/* - * *Interrupt Group 1 Register (offset = 404H) - * */ -#define FUSB300_IGR1_INTGRP5 (1 << 31) -#define FUSB300_IGR1_VBUS_CHG_INT (1 << 30) -#define FUSB300_IGR1_SYNF1_EMPTY_INT (1 << 29) -#define FUSB300_IGR1_SYNF0_EMPTY_INT (1 << 28) -#define FUSB300_IGR1_U3_EXIT_FAIL_INT (1 << 27) -#define FUSB300_IGR1_U2_EXIT_FAIL_INT (1 << 26) -#define FUSB300_IGR1_U1_EXIT_FAIL_INT (1 << 25) -#define FUSB300_IGR1_U2_ENTRY_FAIL_INT (1 << 24) -#define FUSB300_IGR1_U1_ENTRY_FAIL_INT (1 << 23) -#define FUSB300_IGR1_U3_EXIT_INT (1 << 22) -#define FUSB300_IGR1_U2_EXIT_INT (1 << 21) -#define FUSB300_IGR1_U1_EXIT_INT (1 << 20) -#define FUSB300_IGR1_U3_ENTRY_INT (1 << 19) -#define FUSB300_IGR1_U2_ENTRY_INT (1 << 18) -#define FUSB300_IGR1_U1_ENTRY_INT (1 << 17) -#define FUSB300_IGR1_HOT_RST_INT (1 << 16) -#define FUSB300_IGR1_WARM_RST_INT (1 << 15) -#define FUSB300_IGR1_RESM_INT (1 << 14) -#define FUSB300_IGR1_SUSP_INT (1 << 13) -#define FUSB300_IGR1_HS_LPM_INT (1 << 12) -#define FUSB300_IGR1_USBRST_INT (1 << 11) -#define FUSB300_IGR1_DEV_MODE_CHG_INT (1 << 9) -#define FUSB300_IGR1_CX_COMABT_INT (1 << 8) -#define FUSB300_IGR1_CX_COMFAIL_INT (1 << 7) -#define FUSB300_IGR1_CX_CMDEND_INT (1 << 6) -#define FUSB300_IGR1_CX_OUT_INT (1 << 5) -#define FUSB300_IGR1_CX_IN_INT (1 << 4) -#define FUSB300_IGR1_CX_SETUP_INT (1 << 3) -#define FUSB300_IGR1_INTGRP4 (1 << 2) -#define FUSB300_IGR1_INTGRP3 (1 << 1) -#define FUSB300_IGR1_INTGRP2 (1 << 0) - -/* - * *Interrupt Group 2 Register (offset = 408H) - * */ -#define FUSB300_IGR2_EP6_STR_ACCEPT_INT (1 << 29) -#define FUSB300_IGR2_EP6_STR_RESUME_INT (1 << 28) -#define FUSB300_IGR2_EP6_STR_REQ_INT (1 << 27) -#define FUSB300_IGR2_EP6_STR_NOTRDY_INT (1 << 26) -#define FUSB300_IGR2_EP6_STR_PRIME_INT (1 << 25) -#define FUSB300_IGR2_EP5_STR_ACCEPT_INT (1 << 24) -#define FUSB300_IGR2_EP5_STR_RESUME_INT (1 << 23) -#define FUSB300_IGR2_EP5_STR_REQ_INT (1 << 22) -#define FUSB300_IGR2_EP5_STR_NOTRDY_INT (1 << 21) -#define FUSB300_IGR2_EP5_STR_PRIME_INT (1 << 20) -#define FUSB300_IGR2_EP4_STR_ACCEPT_INT (1 << 19) -#define FUSB300_IGR2_EP4_STR_RESUME_INT (1 << 18) -#define FUSB300_IGR2_EP4_STR_REQ_INT (1 << 17) -#define FUSB300_IGR2_EP4_STR_NOTRDY_INT (1 << 16) -#define FUSB300_IGR2_EP4_STR_PRIME_INT (1 << 15) -#define FUSB300_IGR2_EP3_STR_ACCEPT_INT (1 << 14) -#define FUSB300_IGR2_EP3_STR_RESUME_INT (1 << 13) -#define FUSB300_IGR2_EP3_STR_REQ_INT (1 << 12) -#define FUSB300_IGR2_EP3_STR_NOTRDY_INT (1 << 11) -#define FUSB300_IGR2_EP3_STR_PRIME_INT (1 << 10) -#define FUSB300_IGR2_EP2_STR_ACCEPT_INT (1 << 9) -#define FUSB300_IGR2_EP2_STR_RESUME_INT (1 << 8) -#define FUSB300_IGR2_EP2_STR_REQ_INT (1 << 7) -#define FUSB300_IGR2_EP2_STR_NOTRDY_INT (1 << 6) -#define FUSB300_IGR2_EP2_STR_PRIME_INT (1 << 5) -#define FUSB300_IGR2_EP1_STR_ACCEPT_INT (1 << 4) -#define FUSB300_IGR2_EP1_STR_RESUME_INT (1 << 3) -#define FUSB300_IGR2_EP1_STR_REQ_INT (1 << 2) -#define FUSB300_IGR2_EP1_STR_NOTRDY_INT (1 << 1) -#define FUSB300_IGR2_EP1_STR_PRIME_INT (1 << 0) - -#define FUSB300_IGR2_EP_STR_ACCEPT_INT(n) (1 << (5 * n - 1)) -#define FUSB300_IGR2_EP_STR_RESUME_INT(n) (1 << (5 * n - 2)) -#define FUSB300_IGR2_EP_STR_REQ_INT(n) (1 << (5 * n - 3)) -#define FUSB300_IGR2_EP_STR_NOTRDY_INT(n) (1 << (5 * n - 4)) -#define FUSB300_IGR2_EP_STR_PRIME_INT(n) (1 << (5 * n - 5)) - -/* - * *Interrupt Group 3 Register (offset = 40CH) - * */ -#define FUSB300_IGR3_EP12_STR_ACCEPT_INT (1 << 29) -#define FUSB300_IGR3_EP12_STR_RESUME_INT (1 << 28) -#define FUSB300_IGR3_EP12_STR_REQ_INT (1 << 27) -#define FUSB300_IGR3_EP12_STR_NOTRDY_INT (1 << 26) -#define FUSB300_IGR3_EP12_STR_PRIME_INT (1 << 25) -#define FUSB300_IGR3_EP11_STR_ACCEPT_INT (1 << 24) -#define FUSB300_IGR3_EP11_STR_RESUME_INT (1 << 23) -#define FUSB300_IGR3_EP11_STR_REQ_INT (1 << 22) -#define FUSB300_IGR3_EP11_STR_NOTRDY_INT (1 << 21) -#define FUSB300_IGR3_EP11_STR_PRIME_INT (1 << 20) -#define FUSB300_IGR3_EP10_STR_ACCEPT_INT (1 << 19) -#define FUSB300_IGR3_EP10_STR_RESUME_INT (1 << 18) -#define FUSB300_IGR3_EP10_STR_REQ_INT (1 << 17) -#define FUSB300_IGR3_EP10_STR_NOTRDY_INT (1 << 16) -#define FUSB300_IGR3_EP10_STR_PRIME_INT (1 << 15) -#define FUSB300_IGR3_EP9_STR_ACCEPT_INT (1 << 14) -#define FUSB300_IGR3_EP9_STR_RESUME_INT (1 << 13) -#define FUSB300_IGR3_EP9_STR_REQ_INT (1 << 12) -#define FUSB300_IGR3_EP9_STR_NOTRDY_INT (1 << 11) -#define FUSB300_IGR3_EP9_STR_PRIME_INT (1 << 10) -#define FUSB300_IGR3_EP8_STR_ACCEPT_INT (1 << 9) -#define FUSB300_IGR3_EP8_STR_RESUME_INT (1 << 8) -#define FUSB300_IGR3_EP8_STR_REQ_INT (1 << 7) -#define FUSB300_IGR3_EP8_STR_NOTRDY_INT (1 << 6) -#define FUSB300_IGR3_EP8_STR_PRIME_INT (1 << 5) -#define FUSB300_IGR3_EP7_STR_ACCEPT_INT (1 << 4) -#define FUSB300_IGR3_EP7_STR_RESUME_INT (1 << 3) -#define FUSB300_IGR3_EP7_STR_REQ_INT (1 << 2) -#define FUSB300_IGR3_EP7_STR_NOTRDY_INT (1 << 1) -#define FUSB300_IGR3_EP7_STR_PRIME_INT (1 << 0) - -#define FUSB300_IGR3_EP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1)) -#define FUSB300_IGR3_EP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2)) -#define FUSB300_IGR3_EP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3)) -#define FUSB300_IGR3_EP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4)) -#define FUSB300_IGR3_EP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5)) - -/* - * *Interrupt Group 4 Register (offset = 410H) - * */ -#define FUSB300_IGR4_EP15_RX0_INT (1 << 31) -#define FUSB300_IGR4_EP14_RX0_INT (1 << 30) -#define FUSB300_IGR4_EP13_RX0_INT (1 << 29) -#define FUSB300_IGR4_EP12_RX0_INT (1 << 28) -#define FUSB300_IGR4_EP11_RX0_INT (1 << 27) -#define FUSB300_IGR4_EP10_RX0_INT (1 << 26) -#define FUSB300_IGR4_EP9_RX0_INT (1 << 25) -#define FUSB300_IGR4_EP8_RX0_INT (1 << 24) -#define FUSB300_IGR4_EP7_RX0_INT (1 << 23) -#define FUSB300_IGR4_EP6_RX0_INT (1 << 22) -#define FUSB300_IGR4_EP5_RX0_INT (1 << 21) -#define FUSB300_IGR4_EP4_RX0_INT (1 << 20) -#define FUSB300_IGR4_EP3_RX0_INT (1 << 19) -#define FUSB300_IGR4_EP2_RX0_INT (1 << 18) -#define FUSB300_IGR4_EP1_RX0_INT (1 << 17) -#define FUSB300_IGR4_EP_RX0_INT(x) (1 << (x + 16)) -#define FUSB300_IGR4_EP15_STR_ACCEPT_INT (1 << 14) -#define FUSB300_IGR4_EP15_STR_RESUME_INT (1 << 13) -#define FUSB300_IGR4_EP15_STR_REQ_INT (1 << 12) -#define FUSB300_IGR4_EP15_STR_NOTRDY_INT (1 << 11) -#define FUSB300_IGR4_EP15_STR_PRIME_INT (1 << 10) -#define FUSB300_IGR4_EP14_STR_ACCEPT_INT (1 << 9) -#define FUSB300_IGR4_EP14_STR_RESUME_INT (1 << 8) -#define FUSB300_IGR4_EP14_STR_REQ_INT (1 << 7) -#define FUSB300_IGR4_EP14_STR_NOTRDY_INT (1 << 6) -#define FUSB300_IGR4_EP14_STR_PRIME_INT (1 << 5) -#define FUSB300_IGR4_EP13_STR_ACCEPT_INT (1 << 4) -#define FUSB300_IGR4_EP13_STR_RESUME_INT (1 << 3) -#define FUSB300_IGR4_EP13_STR_REQ_INT (1 << 2) -#define FUSB300_IGR4_EP13_STR_NOTRDY_INT (1 << 1) -#define FUSB300_IGR4_EP13_STR_PRIME_INT (1 << 0) - -#define FUSB300_IGR4_EP_STR_ACCEPT_INT(n) (1 << (5 * (n - 12) - 1)) -#define FUSB300_IGR4_EP_STR_RESUME_INT(n) (1 << (5 * (n - 12) - 2)) -#define FUSB300_IGR4_EP_STR_REQ_INT(n) (1 << (5 * (n - 12) - 3)) -#define FUSB300_IGR4_EP_STR_NOTRDY_INT(n) (1 << (5 * (n - 12) - 4)) -#define FUSB300_IGR4_EP_STR_PRIME_INT(n) (1 << (5 * (n - 12) - 5)) - -/* - * *Interrupt Group 5 Register (offset = 414H) - * */ -#define FUSB300_IGR5_EP_STL_INT(n) (1 << n) - -/* - * *Interrupt Enable Group 0 Register (offset = 420H) - * */ -#define FUSB300_IGER0_EEP15_PRD_INT (1 << 31) -#define FUSB300_IGER0_EEP14_PRD_INT (1 << 30) -#define FUSB300_IGER0_EEP13_PRD_INT (1 << 29) -#define FUSB300_IGER0_EEP12_PRD_INT (1 << 28) -#define FUSB300_IGER0_EEP11_PRD_INT (1 << 27) -#define FUSB300_IGER0_EEP10_PRD_INT (1 << 26) -#define FUSB300_IGER0_EEP9_PRD_INT (1 << 25) -#define FUSB300_IGER0_EP8_PRD_INT (1 << 24) -#define FUSB300_IGER0_EEP7_PRD_INT (1 << 23) -#define FUSB300_IGER0_EEP6_PRD_INT (1 << 22) -#define FUSB300_IGER0_EEP5_PRD_INT (1 << 21) -#define FUSB300_IGER0_EEP4_PRD_INT (1 << 20) -#define FUSB300_IGER0_EEP3_PRD_INT (1 << 19) -#define FUSB300_IGER0_EEP2_PRD_INT (1 << 18) -#define FUSB300_IGER0_EEP1_PRD_INT (1 << 17) -#define FUSB300_IGER0_EEPn_PRD_INT(n) (1 << (n + 16)) - -#define FUSB300_IGER0_EEP15_FIFO_INT (1 << 15) -#define FUSB300_IGER0_EEP14_FIFO_INT (1 << 14) -#define FUSB300_IGER0_EEP13_FIFO_INT (1 << 13) -#define FUSB300_IGER0_EEP12_FIFO_INT (1 << 12) -#define FUSB300_IGER0_EEP11_FIFO_INT (1 << 11) -#define FUSB300_IGER0_EEP10_FIFO_INT (1 << 10) -#define FUSB300_IGER0_EEP9_FIFO_INT (1 << 9) -#define FUSB300_IGER0_EEP8_FIFO_INT (1 << 8) -#define FUSB300_IGER0_EEP7_FIFO_INT (1 << 7) -#define FUSB300_IGER0_EEP6_FIFO_INT (1 << 6) -#define FUSB300_IGER0_EEP5_FIFO_INT (1 << 5) -#define FUSB300_IGER0_EEP4_FIFO_INT (1 << 4) -#define FUSB300_IGER0_EEP3_FIFO_INT (1 << 3) -#define FUSB300_IGER0_EEP2_FIFO_INT (1 << 2) -#define FUSB300_IGER0_EEP1_FIFO_INT (1 << 1) -#define FUSB300_IGER0_EEPn_FIFO_INT(n) (1 << n) - -/* - * *Interrupt Enable Group 1 Register (offset = 424H) - * */ -#define FUSB300_IGER1_EINT_GRP5 (1 << 31) -#define FUSB300_IGER1_VBUS_CHG_INT (1 << 30) -#define FUSB300_IGER1_SYNF1_EMPTY_INT (1 << 29) -#define FUSB300_IGER1_SYNF0_EMPTY_INT (1 << 28) -#define FUSB300_IGER1_U3_EXIT_FAIL_INT (1 << 27) -#define FUSB300_IGER1_U2_EXIT_FAIL_INT (1 << 26) -#define FUSB300_IGER1_U1_EXIT_FAIL_INT (1 << 25) -#define FUSB300_IGER1_U2_ENTRY_FAIL_INT (1 << 24) -#define FUSB300_IGER1_U1_ENTRY_FAIL_INT (1 << 23) -#define FUSB300_IGER1_U3_EXIT_INT (1 << 22) -#define FUSB300_IGER1_U2_EXIT_INT (1 << 21) -#define FUSB300_IGER1_U1_EXIT_INT (1 << 20) -#define FUSB300_IGER1_U3_ENTRY_INT (1 << 19) -#define FUSB300_IGER1_U2_ENTRY_INT (1 << 18) -#define FUSB300_IGER1_U1_ENTRY_INT (1 << 17) -#define FUSB300_IGER1_HOT_RST_INT (1 << 16) -#define FUSB300_IGER1_WARM_RST_INT (1 << 15) -#define FUSB300_IGER1_RESM_INT (1 << 14) -#define FUSB300_IGER1_SUSP_INT (1 << 13) -#define FUSB300_IGER1_LPM_INT (1 << 12) -#define FUSB300_IGER1_HS_RST_INT (1 << 11) -#define FUSB300_IGER1_EDEV_MODE_CHG_INT (1 << 9) -#define FUSB300_IGER1_CX_COMABT_INT (1 << 8) -#define FUSB300_IGER1_CX_COMFAIL_INT (1 << 7) -#define FUSB300_IGER1_CX_CMDEND_INT (1 << 6) -#define FUSB300_IGER1_CX_OUT_INT (1 << 5) -#define FUSB300_IGER1_CX_IN_INT (1 << 4) -#define FUSB300_IGER1_CX_SETUP_INT (1 << 3) -#define FUSB300_IGER1_INTGRP4 (1 << 2) -#define FUSB300_IGER1_INTGRP3 (1 << 1) -#define FUSB300_IGER1_INTGRP2 (1 << 0) - -/* - * *Interrupt Enable Group 2 Register (offset = 428H) - * */ -#define FUSB300_IGER2_EEP_STR_ACCEPT_INT(n) (1 << (5 * n - 1)) -#define FUSB300_IGER2_EEP_STR_RESUME_INT(n) (1 << (5 * n - 2)) -#define FUSB300_IGER2_EEP_STR_REQ_INT(n) (1 << (5 * n - 3)) -#define FUSB300_IGER2_EEP_STR_NOTRDY_INT(n) (1 << (5 * n - 4)) -#define FUSB300_IGER2_EEP_STR_PRIME_INT(n) (1 << (5 * n - 5)) - -/* - * *Interrupt Enable Group 3 Register (offset = 42CH) - * */ - -#define FUSB300_IGER3_EEP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1)) -#define FUSB300_IGER3_EEP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2)) -#define FUSB300_IGER3_EEP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3)) -#define FUSB300_IGER3_EEP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4)) -#define FUSB300_IGER3_EEP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5)) - -/* - * *Interrupt Enable Group 4 Register (offset = 430H) - * */ - -#define FUSB300_IGER4_EEP_RX0_INT(n) (1 << (n + 16)) -#define FUSB300_IGER4_EEP_STR_ACCEPT_INT(n) (1 << (5 * (n - 6) - 1)) -#define FUSB300_IGER4_EEP_STR_RESUME_INT(n) (1 << (5 * (n - 6) - 2)) -#define FUSB300_IGER4_EEP_STR_REQ_INT(n) (1 << (5 * (n - 6) - 3)) -#define FUSB300_IGER4_EEP_STR_NOTRDY_INT(n) (1 << (5 * (n - 6) - 4)) -#define FUSB300_IGER4_EEP_STR_PRIME_INT(n) (1 << (5 * (n - 6) - 5)) - -/* EP PRD Ready (EP_PRD_RDY, offset = 504H) */ - -#define FUSB300_EPPRDR_EP15_PRD_RDY (1 << 15) -#define FUSB300_EPPRDR_EP14_PRD_RDY (1 << 14) -#define FUSB300_EPPRDR_EP13_PRD_RDY (1 << 13) -#define FUSB300_EPPRDR_EP12_PRD_RDY (1 << 12) -#define FUSB300_EPPRDR_EP11_PRD_RDY (1 << 11) -#define FUSB300_EPPRDR_EP10_PRD_RDY (1 << 10) -#define FUSB300_EPPRDR_EP9_PRD_RDY (1 << 9) -#define FUSB300_EPPRDR_EP8_PRD_RDY (1 << 8) -#define FUSB300_EPPRDR_EP7_PRD_RDY (1 << 7) -#define FUSB300_EPPRDR_EP6_PRD_RDY (1 << 6) -#define FUSB300_EPPRDR_EP5_PRD_RDY (1 << 5) -#define FUSB300_EPPRDR_EP4_PRD_RDY (1 << 4) -#define FUSB300_EPPRDR_EP3_PRD_RDY (1 << 3) -#define FUSB300_EPPRDR_EP2_PRD_RDY (1 << 2) -#define FUSB300_EPPRDR_EP1_PRD_RDY (1 << 1) -#define FUSB300_EPPRDR_EP_PRD_RDY(n) (1 << n) - -/* AHB Bus Control Register (offset = 514H) */ -#define FUSB300_AHBBCR_S1_SPLIT_ON (1 << 17) -#define FUSB300_AHBBCR_S0_SPLIT_ON (1 << 16) -#define FUSB300_AHBBCR_S1_1entry (0 << 12) -#define FUSB300_AHBBCR_S1_4entry (3 << 12) -#define FUSB300_AHBBCR_S1_8entry (5 << 12) -#define FUSB300_AHBBCR_S1_16entry (7 << 12) -#define FUSB300_AHBBCR_S0_1entry (0 << 8) -#define FUSB300_AHBBCR_S0_4entry (3 << 8) -#define FUSB300_AHBBCR_S0_8entry (5 << 8) -#define FUSB300_AHBBCR_S0_16entry (7 << 8) -#define FUSB300_AHBBCR_M1_BURST_SINGLE (0 << 4) -#define FUSB300_AHBBCR_M1_BURST_INCR (1 << 4) -#define FUSB300_AHBBCR_M1_BURST_INCR4 (3 << 4) -#define FUSB300_AHBBCR_M1_BURST_INCR8 (5 << 4) -#define FUSB300_AHBBCR_M1_BURST_INCR16 (7 << 4) -#define FUSB300_AHBBCR_M0_BURST_SINGLE 0 -#define FUSB300_AHBBCR_M0_BURST_INCR 1 -#define FUSB300_AHBBCR_M0_BURST_INCR4 3 -#define FUSB300_AHBBCR_M0_BURST_INCR8 5 -#define FUSB300_AHBBCR_M0_BURST_INCR16 7 -#define FUSB300_IGER5_EEP_STL_INT(n) (1 << n) - -/* WORD 0 Data Structure of PRD Table */ -#define FUSB300_EPPRD0_M (1 << 30) -#define FUSB300_EPPRD0_O (1 << 29) -/* The finished prd */ -#define FUSB300_EPPRD0_F (1 << 28) -#define FUSB300_EPPRD0_I (1 << 27) -#define FUSB300_EPPRD0_A (1 << 26) -/* To decide HW point to first prd at next time */ -#define FUSB300_EPPRD0_L (1 << 25) -#define FUSB300_EPPRD0_H (1 << 24) -#define FUSB300_EPPRD0_BTC(n) (n & 0xFFFFFF) - -/*----------------------------------------------------------------------*/ -#define FUSB300_MAX_NUM_EP 16 - -#define FUSB300_FIFO_ENTRY_NUM 8 -#define FUSB300_MAX_FIFO_ENTRY 8 - -#define SS_CTL_MAX_PACKET_SIZE 0x200 -#define SS_BULK_MAX_PACKET_SIZE 0x400 -#define SS_INT_MAX_PACKET_SIZE 0x400 -#define SS_ISO_MAX_PACKET_SIZE 0x400 - -#define HS_BULK_MAX_PACKET_SIZE 0x200 -#define HS_CTL_MAX_PACKET_SIZE 0x40 -#define HS_INT_MAX_PACKET_SIZE 0x400 -#define HS_ISO_MAX_PACKET_SIZE 0x400 - -struct fusb300_ep_info { - u8 epnum; - u8 type; - u8 interval; - u8 dir_in; - u16 maxpacket; - u16 addrofs; - u16 bw_num; -}; - -struct fusb300_request { - - struct usb_request req; - struct list_head queue; -}; - - -struct fusb300_ep { - struct usb_ep ep; - struct fusb300 *fusb300; - - struct list_head queue; - unsigned stall:1; - unsigned wedged:1; - unsigned use_dma:1; - - unsigned char epnum; - unsigned char type; -}; - -struct fusb300 { - spinlock_t lock; - void __iomem *reg; - - unsigned long irq_trigger; - - struct usb_gadget gadget; - struct usb_gadget_driver *driver; - - struct fusb300_ep *ep[FUSB300_MAX_NUM_EP]; - - struct usb_request *ep0_req; /* for internal request */ - __le16 ep0_data; - u32 ep0_length; /* for internal request */ - u8 ep0_dir; /* 0/0x80 out/in */ - - u8 fifo_entry_num; /* next start fifo entry */ - u32 addrofs; /* next fifo address offset */ - u8 reenum; /* if re-enumeration */ -}; - -#define to_fusb300(g) (container_of((g), struct fusb300, gadget)) - -#endif diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index b860c2e76449..db42a5e3e805 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -273,7 +273,7 @@ goku_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) if (!_ep) return NULL; - req = kzalloc(sizeof *req, gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -1755,7 +1755,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) } /* alloc, and start init */ - dev = kzalloc (sizeof *dev, GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) { retval = -ENOMEM; goto err; diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index bf5b3c964c18..cdcbfae140e9 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -347,7 +347,7 @@ static struct usb_request *gr_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { struct gr_request *req; - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 89d6daf2bda7..044c31869cfb 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -1629,7 +1629,7 @@ static int lpc32xx_ep_enable(struct usb_ep *_ep, return -ESHUTDOWN; } - tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + tmp = usb_endpoint_type(desc); switch (tmp) { case USB_ENDPOINT_XFER_CONTROL: return -EINVAL; @@ -1705,7 +1705,7 @@ static struct usb_request *lpc32xx_ep_alloc_request(struct usb_ep *_ep, { struct lpc32xx_request *req; - req = kzalloc(sizeof(struct lpc32xx_request), gfp_flags); + req = kzalloc_obj(struct lpc32xx_request, gfp_flags); if (!req) return NULL; @@ -3020,7 +3020,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) pdev->dev.dma_mask = &lpc32xx_usbd_dmamask; retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (retval) - return retval; + goto err_put_client; udc->board = &lpc32xx_usbddata; @@ -3038,28 +3038,32 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) /* Get IRQs */ for (i = 0; i < 4; i++) { udc->udp_irq[i] = platform_get_irq(pdev, i); - if (udc->udp_irq[i] < 0) - return udc->udp_irq[i]; + if (udc->udp_irq[i] < 0) { + retval = udc->udp_irq[i]; + goto err_put_client; + } } udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(udc->udp_baseaddr)) { dev_err(udc->dev, "IO map failure\n"); - return PTR_ERR(udc->udp_baseaddr); + retval = PTR_ERR(udc->udp_baseaddr); + goto err_put_client; } /* Get USB device clock */ udc->usb_slv_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(udc->usb_slv_clk)) { dev_err(udc->dev, "failed to acquire USB device clock\n"); - return PTR_ERR(udc->usb_slv_clk); + retval = PTR_ERR(udc->usb_slv_clk); + goto err_put_client; } /* Enable USB device clock */ retval = clk_prepare_enable(udc->usb_slv_clk); if (retval < 0) { dev_err(udc->dev, "failed to start USB device clock\n"); - return retval; + goto err_put_client; } /* Setup deferred workqueue data */ @@ -3080,7 +3084,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (!udc->udca_v_base) { dev_err(udc->dev, "error getting UDCA region\n"); retval = -ENOMEM; - goto i2c_fail; + goto err_disable_clk; } udc->udca_p_base = dma_handle; dev_dbg(udc->dev, "DMA buffer(0x%x bytes), P:0x%08x, V:0x%p\n", @@ -3093,7 +3097,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (!udc->dd_cache) { dev_err(udc->dev, "error getting DD DMA region\n"); retval = -ENOMEM; - goto dma_alloc_fail; + goto err_free_dma; } /* Clear USB peripheral and initialize gadget endpoints */ @@ -3107,14 +3111,14 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (retval < 0) { dev_err(udc->dev, "LP request irq %d failed\n", udc->udp_irq[IRQ_USB_LP]); - goto irq_req_fail; + goto err_destroy_pool; } retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_HP], lpc32xx_usb_hp_irq, 0, "udc_hp", udc); if (retval < 0) { dev_err(udc->dev, "HP request irq %d failed\n", udc->udp_irq[IRQ_USB_HP]); - goto irq_req_fail; + goto err_destroy_pool; } retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_DEVDMA], @@ -3122,7 +3126,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (retval < 0) { dev_err(udc->dev, "DEV request irq %d failed\n", udc->udp_irq[IRQ_USB_DEVDMA]); - goto irq_req_fail; + goto err_destroy_pool; } /* The transceiver interrupt is used for VBUS detection and will @@ -3133,7 +3137,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (retval < 0) { dev_err(udc->dev, "VBUS request irq %d failed\n", udc->udp_irq[IRQ_USB_ATX]); - goto irq_req_fail; + goto err_destroy_pool; } /* Initialize wait queue */ @@ -3142,7 +3146,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) retval = usb_add_gadget_udc(dev, &udc->gadget); if (retval < 0) - goto add_gadget_fail; + goto err_destroy_pool; dev_set_drvdata(dev, udc); device_init_wakeup(dev, 1); @@ -3154,14 +3158,16 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) dev_info(udc->dev, "%s version %s\n", driver_name, DRIVER_VERSION); return 0; -add_gadget_fail: -irq_req_fail: +err_destroy_pool: dma_pool_destroy(udc->dd_cache); -dma_alloc_fail: +err_free_dma: dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, udc->udca_v_base, udc->udca_p_base); -i2c_fail: +err_disable_clk: clk_disable_unprepare(udc->usb_slv_clk); +err_put_client: + put_device(&udc->isp1301_i2c_client->dev); + dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval); return retval; @@ -3190,6 +3196,8 @@ static void lpc32xx_udc_remove(struct platform_device *pdev) udc->udca_v_base, udc->udca_p_base); clk_disable_unprepare(udc->usb_slv_clk); + + put_device(&udc->isp1301_i2c_client->dev); } #ifdef CONFIG_PM diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index a938b2af0944..d77c11c4eb38 100644 --- a/drivers/usb/gadget/udc/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -359,7 +359,7 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep, ep->pipenum = pipenum; ep->ep.maxpacket = usb_endpoint_maxp(desc); m66592->pipenum2ep[pipenum] = ep; - m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep; + m66592->epaddr2ep[usb_endpoint_num(desc)] = ep; INIT_LIST_HEAD(&ep->queue); } @@ -391,7 +391,7 @@ static int alloc_pipe_config(struct m66592_ep *ep, BUG_ON(ep->pipenum); - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: if (m66592->bulk >= M66592_MAX_NUM_BULK) { if (m66592->isochronous >= M66592_MAX_NUM_ISOC) { @@ -433,7 +433,7 @@ static int alloc_pipe_config(struct m66592_ep *ep, } ep->type = info.type; - info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + info.epnum = usb_endpoint_num(desc); info.maxpacket = usb_endpoint_maxp(desc); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) @@ -1261,7 +1261,7 @@ static irqreturn_t m66592_irq(int irq, void *_m66592) static void m66592_timer(struct timer_list *t) { - struct m66592 *m66592 = from_timer(m66592, t, timer); + struct m66592 *m66592 = timer_container_of(m66592, t, timer); unsigned long flags; u16 tmp; @@ -1330,7 +1330,7 @@ static struct usb_request *m66592_alloc_request(struct usb_ep *_ep, { struct m66592_request *req; - req = kzalloc(sizeof(struct m66592_request), gfp_flags); + req = kzalloc_obj(struct m66592_request, gfp_flags); if (!req) return NULL; @@ -1571,7 +1571,7 @@ static int m66592_probe(struct platform_device *pdev) } /* initialize ucd */ - m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL); + m66592 = kzalloc_obj(struct m66592); if (m66592 == NULL) { ret = -ENOMEM; goto clean_up; diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c index 7349ea774adf..11fd61cba5af 100644 --- a/drivers/usb/gadget/udc/max3420_udc.c +++ b/drivers/usb/gadget/udc/max3420_udc.c @@ -1007,7 +1007,7 @@ static struct usb_request *max3420_alloc_request(struct usb_ep *_ep, struct max3420_ep *ep = to_max3420_ep(_ep); struct max3420_req *req; - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; diff --git a/drivers/usb/gadget/udc/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h deleted file mode 100644 index 66b84f792f64..000000000000 --- a/drivers/usb/gadget/udc/mv_u3d.h +++ /dev/null @@ -1,317 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - */ - -#ifndef __MV_U3D_H -#define __MV_U3D_H - -#define MV_U3D_EP_CONTEXT_ALIGNMENT 32 -#define MV_U3D_TRB_ALIGNMENT 16 -#define MV_U3D_DMA_BOUNDARY 4096 -#define MV_U3D_EP0_MAX_PKT_SIZE 512 - -/* ep0 transfer state */ -#define MV_U3D_WAIT_FOR_SETUP 0 -#define MV_U3D_DATA_STATE_XMIT 1 -#define MV_U3D_DATA_STATE_NEED_ZLP 2 -#define MV_U3D_WAIT_FOR_OUT_STATUS 3 -#define MV_U3D_DATA_STATE_RECV 4 -#define MV_U3D_STATUS_STAGE 5 - -#define MV_U3D_EP_MAX_LENGTH_TRANSFER 0x10000 - -/* USB3 Interrupt Status */ -#define MV_U3D_USBINT_SETUP 0x00000001 -#define MV_U3D_USBINT_RX_COMPLETE 0x00000002 -#define MV_U3D_USBINT_TX_COMPLETE 0x00000004 -#define MV_U3D_USBINT_UNDER_RUN 0x00000008 -#define MV_U3D_USBINT_RXDESC_ERR 0x00000010 -#define MV_U3D_USBINT_TXDESC_ERR 0x00000020 -#define MV_U3D_USBINT_RX_TRB_COMPLETE 0x00000040 -#define MV_U3D_USBINT_TX_TRB_COMPLETE 0x00000080 -#define MV_U3D_USBINT_VBUS_VALID 0x00010000 -#define MV_U3D_USBINT_STORAGE_CMD_FULL 0x00020000 -#define MV_U3D_USBINT_LINK_CHG 0x01000000 - -/* USB3 Interrupt Enable */ -#define MV_U3D_INTR_ENABLE_SETUP 0x00000001 -#define MV_U3D_INTR_ENABLE_RX_COMPLETE 0x00000002 -#define MV_U3D_INTR_ENABLE_TX_COMPLETE 0x00000004 -#define MV_U3D_INTR_ENABLE_UNDER_RUN 0x00000008 -#define MV_U3D_INTR_ENABLE_RXDESC_ERR 0x00000010 -#define MV_U3D_INTR_ENABLE_TXDESC_ERR 0x00000020 -#define MV_U3D_INTR_ENABLE_RX_TRB_COMPLETE 0x00000040 -#define MV_U3D_INTR_ENABLE_TX_TRB_COMPLETE 0x00000080 -#define MV_U3D_INTR_ENABLE_RX_BUFFER_ERR 0x00000100 -#define MV_U3D_INTR_ENABLE_VBUS_VALID 0x00010000 -#define MV_U3D_INTR_ENABLE_STORAGE_CMD_FULL 0x00020000 -#define MV_U3D_INTR_ENABLE_LINK_CHG 0x01000000 -#define MV_U3D_INTR_ENABLE_PRIME_STATUS 0x02000000 - -/* USB3 Link Change */ -#define MV_U3D_LINK_CHANGE_LINK_UP 0x00000001 -#define MV_U3D_LINK_CHANGE_SUSPEND 0x00000002 -#define MV_U3D_LINK_CHANGE_RESUME 0x00000004 -#define MV_U3D_LINK_CHANGE_WRESET 0x00000008 -#define MV_U3D_LINK_CHANGE_HRESET 0x00000010 -#define MV_U3D_LINK_CHANGE_VBUS_INVALID 0x00000020 -#define MV_U3D_LINK_CHANGE_INACT 0x00000040 -#define MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0 0x00000080 -#define MV_U3D_LINK_CHANGE_U1 0x00000100 -#define MV_U3D_LINK_CHANGE_U2 0x00000200 -#define MV_U3D_LINK_CHANGE_U3 0x00000400 - -/* bridge setting */ -#define MV_U3D_BRIDGE_SETTING_VBUS_VALID (1 << 16) - -/* Command Register Bit Masks */ -#define MV_U3D_CMD_RUN_STOP 0x00000001 -#define MV_U3D_CMD_CTRL_RESET 0x00000002 - -/* ep control register */ -#define MV_U3D_EPXCR_EP_TYPE_CONTROL 0 -#define MV_U3D_EPXCR_EP_TYPE_ISOC 1 -#define MV_U3D_EPXCR_EP_TYPE_BULK 2 -#define MV_U3D_EPXCR_EP_TYPE_INT 3 -#define MV_U3D_EPXCR_EP_ENABLE_SHIFT 4 -#define MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT 12 -#define MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT 16 -#define MV_U3D_USB_BULK_BURST_OUT 6 -#define MV_U3D_USB_BULK_BURST_IN 14 - -#define MV_U3D_EPXCR_EP_FLUSH (1 << 7) -#define MV_U3D_EPXCR_EP_HALT (1 << 1) -#define MV_U3D_EPXCR_EP_INIT (1) - -/* TX/RX Status Register */ -#define MV_U3D_XFERSTATUS_COMPLETE_SHIFT 24 -#define MV_U3D_COMPLETE_INVALID 0 -#define MV_U3D_COMPLETE_SUCCESS 1 -#define MV_U3D_COMPLETE_BUFF_ERR 2 -#define MV_U3D_COMPLETE_SHORT_PACKET 3 -#define MV_U3D_COMPLETE_TRB_ERR 5 -#define MV_U3D_XFERSTATUS_TRB_LENGTH_MASK (0xFFFFFF) - -#define MV_U3D_USB_LINK_BYPASS_VBUS 0x8 - -#define MV_U3D_LTSSM_PHY_INIT_DONE 0x80000000 -#define MV_U3D_LTSSM_NEVER_GO_COMPLIANCE 0x40000000 - -#define MV_U3D_USB3_OP_REGS_OFFSET 0x100 -#define MV_U3D_USB3_PHY_OFFSET 0xB800 - -#define DCS_ENABLE 0x1 - -/* timeout */ -#define MV_U3D_RESET_TIMEOUT 10000 -#define MV_U3D_FLUSH_TIMEOUT 100000 -#define MV_U3D_OWN_TIMEOUT 10000 -#define LOOPS_USEC_SHIFT 4 -#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT) -#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT) - -/* ep direction */ -#define MV_U3D_EP_DIR_IN 1 -#define MV_U3D_EP_DIR_OUT 0 -#define mv_u3d_ep_dir(ep) (((ep)->ep_num == 0) ? \ - ((ep)->u3d->ep0_dir) : ((ep)->direction)) - -/* usb capability registers */ -struct mv_u3d_cap_regs { - u32 rsvd[5]; - u32 dboff; /* doorbell register offset */ - u32 rtsoff; /* runtime register offset */ - u32 vuoff; /* vendor unique register offset */ -}; - -/* operation registers */ -struct mv_u3d_op_regs { - u32 usbcmd; /* Command register */ - u32 rsvd1[11]; - u32 dcbaapl; /* Device Context Base Address low register */ - u32 dcbaaph; /* Device Context Base Address high register */ - u32 rsvd2[243]; - u32 portsc; /* port status and control register*/ - u32 portlinkinfo; /* port link info register*/ - u32 rsvd3[9917]; - u32 doorbell; /* doorbell register */ -}; - -/* control endpoint enable registers */ -struct epxcr { - u32 epxoutcr0; /* ep out control 0 register */ - u32 epxoutcr1; /* ep out control 1 register */ - u32 epxincr0; /* ep in control 0 register */ - u32 epxincr1; /* ep in control 1 register */ -}; - -/* transfer status registers */ -struct xferstatus { - u32 curdeqlo; /* current TRB pointer low */ - u32 curdeqhi; /* current TRB pointer high */ - u32 statuslo; /* transfer status low */ - u32 statushi; /* transfer status high */ -}; - -/* vendor unique control registers */ -struct mv_u3d_vuc_regs { - u32 ctrlepenable; /* control endpoint enable register */ - u32 setuplock; /* setup lock register */ - u32 endcomplete; /* endpoint transfer complete register */ - u32 intrcause; /* interrupt cause register */ - u32 intrenable; /* interrupt enable register */ - u32 trbcomplete; /* TRB complete register */ - u32 linkchange; /* link change register */ - u32 rsvd1[5]; - u32 trbunderrun; /* TRB underrun register */ - u32 rsvd2[43]; - u32 bridgesetting; /* bridge setting register */ - u32 rsvd3[7]; - struct xferstatus txst[16]; /* TX status register */ - struct xferstatus rxst[16]; /* RX status register */ - u32 ltssm; /* LTSSM control register */ - u32 pipe; /* PIPE control register */ - u32 linkcr0; /* link control 0 register */ - u32 linkcr1; /* link control 1 register */ - u32 rsvd6[60]; - u32 mib0; /* MIB0 counter register */ - u32 usblink; /* usb link control register */ - u32 ltssmstate; /* LTSSM state register */ - u32 linkerrorcause; /* link error cause register */ - u32 rsvd7[60]; - u32 devaddrtiebrkr; /* device address and tie breaker */ - u32 itpinfo0; /* ITP info 0 register */ - u32 itpinfo1; /* ITP info 1 register */ - u32 rsvd8[61]; - struct epxcr epcr[16]; /* ep control register */ - u32 rsvd9[64]; - u32 phyaddr; /* PHY address register */ - u32 phydata; /* PHY data register */ -}; - -/* Endpoint context structure */ -struct mv_u3d_ep_context { - u32 rsvd0; - u32 rsvd1; - u32 trb_addr_lo; /* TRB address low 32 bit */ - u32 trb_addr_hi; /* TRB address high 32 bit */ - u32 rsvd2; - u32 rsvd3; - struct usb_ctrlrequest setup_buffer; /* setup data buffer */ -}; - -/* TRB control data structure */ -struct mv_u3d_trb_ctrl { - u32 own:1; /* owner of TRB */ - u32 rsvd1:3; - u32 chain:1; /* associate this TRB with the - next TRB on the Ring */ - u32 ioc:1; /* interrupt on complete */ - u32 rsvd2:4; - u32 type:6; /* TRB type */ -#define TYPE_NORMAL 1 -#define TYPE_DATA 3 -#define TYPE_LINK 6 - u32 dir:1; /* Working at data stage of control endpoint - operation. 0 is OUT and 1 is IN. */ - u32 rsvd3:15; -}; - -/* TRB data structure - * For multiple TRB, all the TRBs' physical address should be continuous. - */ -struct mv_u3d_trb_hw { - u32 buf_addr_lo; /* data buffer address low 32 bit */ - u32 buf_addr_hi; /* data buffer address high 32 bit */ - u32 trb_len; /* transfer length */ - struct mv_u3d_trb_ctrl ctrl; /* TRB control data */ -}; - -/* TRB structure */ -struct mv_u3d_trb { - struct mv_u3d_trb_hw *trb_hw; /* point to the trb_hw structure */ - dma_addr_t trb_dma; /* dma address for this trb_hw */ - struct list_head trb_list; /* trb list */ -}; - -/* device data structure */ -struct mv_u3d { - struct usb_gadget gadget; - struct usb_gadget_driver *driver; - spinlock_t lock; /* device lock */ - struct completion *done; - struct device *dev; - int irq; - - /* usb controller registers */ - struct mv_u3d_cap_regs __iomem *cap_regs; - struct mv_u3d_op_regs __iomem *op_regs; - struct mv_u3d_vuc_regs __iomem *vuc_regs; - void __iomem *phy_regs; - - unsigned int max_eps; - struct mv_u3d_ep_context *ep_context; - size_t ep_context_size; - dma_addr_t ep_context_dma; - - struct dma_pool *trb_pool; /* for TRB data structure */ - struct mv_u3d_ep *eps; - - struct mv_u3d_req *status_req; /* ep0 status request */ - struct usb_ctrlrequest local_setup_buff; /* store setup data*/ - - unsigned int resume_state; /* USB state to resume */ - unsigned int usb_state; /* USB current state */ - unsigned int ep0_state; /* Endpoint zero state */ - unsigned int ep0_dir; - - unsigned int dev_addr; /* device address */ - - unsigned int errors; - - unsigned softconnect:1; - unsigned vbus_active:1; /* vbus is active or not */ - unsigned remote_wakeup:1; /* support remote wakeup */ - unsigned clock_gating:1; /* clock gating or not */ - unsigned active:1; /* udc is active or not */ - unsigned vbus_valid_detect:1; /* udc vbus detection */ - - struct mv_usb_addon_irq *vbus; - unsigned int power; - - struct clk *clk; -}; - -/* endpoint data structure */ -struct mv_u3d_ep { - struct usb_ep ep; - struct mv_u3d *u3d; - struct list_head queue; /* ep request queued hardware */ - struct list_head req_list; /* list of ep request */ - struct mv_u3d_ep_context *ep_context; /* ep context */ - u32 direction; - char name[14]; - u32 processing; /* there is ep request - queued on haredware */ - spinlock_t req_lock; /* ep lock */ - unsigned wedge:1; - unsigned enabled:1; - unsigned ep_type:2; - unsigned ep_num:8; -}; - -/* request data structure */ -struct mv_u3d_req { - struct usb_request req; - struct mv_u3d_ep *ep; - struct list_head queue; /* ep requst queued on hardware */ - struct list_head list; /* ep request list */ - struct list_head trb_list; /* trb list of a request */ - - struct mv_u3d_trb *trb_head; /* point to first trb of a request */ - unsigned trb_count; /* TRB number in the chain */ - unsigned chain; /* TRB chain or not */ -}; - -#endif diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c deleted file mode 100644 index 062f43e146aa..000000000000 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ /dev/null @@ -1,2062 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - */ - -#include <linux/module.h> -#include <linux/dma-mapping.h> -#include <linux/dmapool.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/timer.h> -#include <linux/list.h> -#include <linux/notifier.h> -#include <linux/interrupt.h> -#include <linux/moduleparam.h> -#include <linux/device.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> -#include <linux/pm.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/platform_device.h> -#include <linux/platform_data/mv_usb.h> -#include <linux/clk.h> - -#include "mv_u3d.h" - -#define DRIVER_DESC "Marvell PXA USB3.0 Device Controller driver" - -static const char driver_name[] = "mv_u3d"; - -static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status); -static void mv_u3d_stop_activity(struct mv_u3d *u3d, - struct usb_gadget_driver *driver); - -/* for endpoint 0 operations */ -static const struct usb_endpoint_descriptor mv_u3d_ep0_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0, - .bmAttributes = USB_ENDPOINT_XFER_CONTROL, - .wMaxPacketSize = MV_U3D_EP0_MAX_PKT_SIZE, -}; - -static void mv_u3d_ep0_reset(struct mv_u3d *u3d) -{ - struct mv_u3d_ep *ep; - u32 epxcr; - int i; - - for (i = 0; i < 2; i++) { - ep = &u3d->eps[i]; - ep->u3d = u3d; - - /* ep0 ep context, ep0 in and out share the same ep context */ - ep->ep_context = &u3d->ep_context[1]; - } - - /* reset ep state machine */ - /* reset ep0 out */ - epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); - epxcr |= MV_U3D_EPXCR_EP_INIT; - iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0); - udelay(5); - epxcr &= ~MV_U3D_EPXCR_EP_INIT; - iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr0); - - epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE - << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) - | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) - | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) - | MV_U3D_EPXCR_EP_TYPE_CONTROL); - iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxoutcr1); - - /* reset ep0 in */ - epxcr = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); - epxcr |= MV_U3D_EPXCR_EP_INIT; - iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0); - udelay(5); - epxcr &= ~MV_U3D_EPXCR_EP_INIT; - iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr0); - - epxcr = ((MV_U3D_EP0_MAX_PKT_SIZE - << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) - | (1 << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) - | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) - | MV_U3D_EPXCR_EP_TYPE_CONTROL); - iowrite32(epxcr, &u3d->vuc_regs->epcr[0].epxincr1); -} - -static void mv_u3d_ep0_stall(struct mv_u3d *u3d) -{ - u32 tmp; - dev_dbg(u3d->dev, "%s\n", __func__); - - /* set TX and RX to stall */ - tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); - tmp |= MV_U3D_EPXCR_EP_HALT; - iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); - - tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); - tmp |= MV_U3D_EPXCR_EP_HALT; - iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); - - /* update ep0 state */ - u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; - u3d->ep0_dir = MV_U3D_EP_DIR_OUT; -} - -static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index, - struct mv_u3d_req *curr_req) -{ - struct mv_u3d_trb *curr_trb; - int actual, remaining_length = 0; - int direction, ep_num; - int retval = 0; - u32 tmp, status, length; - - direction = index % 2; - ep_num = index / 2; - - actual = curr_req->req.length; - - while (!list_empty(&curr_req->trb_list)) { - curr_trb = list_entry(curr_req->trb_list.next, - struct mv_u3d_trb, trb_list); - if (!curr_trb->trb_hw->ctrl.own) { - dev_err(u3d->dev, "%s, TRB own error!\n", - u3d->eps[index].name); - return 1; - } - - curr_trb->trb_hw->ctrl.own = 0; - if (direction == MV_U3D_EP_DIR_OUT) - tmp = ioread32(&u3d->vuc_regs->rxst[ep_num].statuslo); - else - tmp = ioread32(&u3d->vuc_regs->txst[ep_num].statuslo); - - status = tmp >> MV_U3D_XFERSTATUS_COMPLETE_SHIFT; - length = tmp & MV_U3D_XFERSTATUS_TRB_LENGTH_MASK; - - if (status == MV_U3D_COMPLETE_SUCCESS || - (status == MV_U3D_COMPLETE_SHORT_PACKET && - direction == MV_U3D_EP_DIR_OUT)) { - remaining_length += length; - actual -= remaining_length; - } else { - dev_err(u3d->dev, - "complete_tr error: ep=%d %s: error = 0x%x\n", - index >> 1, direction ? "SEND" : "RECV", - status); - retval = -EPROTO; - } - - list_del_init(&curr_trb->trb_list); - } - if (retval) - return retval; - - curr_req->req.actual = actual; - return 0; -} - -/* - * mv_u3d_done() - retire a request; caller blocked irqs - * @status : request status to be set, only works when - * request is still in progress. - */ -static -void mv_u3d_done(struct mv_u3d_ep *ep, struct mv_u3d_req *req, int status) - __releases(&ep->udc->lock) - __acquires(&ep->udc->lock) -{ - struct mv_u3d *u3d = (struct mv_u3d *)ep->u3d; - - dev_dbg(u3d->dev, "mv_u3d_done: remove req->queue\n"); - /* Removed the req from ep queue */ - list_del_init(&req->queue); - - /* req.status should be set as -EINPROGRESS in ep_queue() */ - if (req->req.status == -EINPROGRESS) - req->req.status = status; - else - status = req->req.status; - - /* Free trb for the request */ - if (!req->chain) - dma_pool_free(u3d->trb_pool, - req->trb_head->trb_hw, req->trb_head->trb_dma); - else { - dma_unmap_single(ep->u3d->gadget.dev.parent, - (dma_addr_t)req->trb_head->trb_dma, - req->trb_count * sizeof(struct mv_u3d_trb_hw), - DMA_BIDIRECTIONAL); - kfree(req->trb_head->trb_hw); - } - kfree(req->trb_head); - - usb_gadget_unmap_request(&u3d->gadget, &req->req, mv_u3d_ep_dir(ep)); - - if (status && (status != -ESHUTDOWN)) { - dev_dbg(u3d->dev, "complete %s req %p stat %d len %u/%u", - ep->ep.name, &req->req, status, - req->req.actual, req->req.length); - } - - spin_unlock(&ep->u3d->lock); - - usb_gadget_giveback_request(&ep->ep, &req->req); - - spin_lock(&ep->u3d->lock); -} - -static int mv_u3d_queue_trb(struct mv_u3d_ep *ep, struct mv_u3d_req *req) -{ - u32 tmp, direction; - struct mv_u3d *u3d; - struct mv_u3d_ep_context *ep_context; - int retval = 0; - - u3d = ep->u3d; - direction = mv_u3d_ep_dir(ep); - - /* ep0 in and out share the same ep context slot 1*/ - if (ep->ep_num == 0) - ep_context = &(u3d->ep_context[1]); - else - ep_context = &(u3d->ep_context[ep->ep_num * 2 + direction]); - - /* check if the pipe is empty or not */ - if (!list_empty(&ep->queue)) { - dev_err(u3d->dev, "add trb to non-empty queue!\n"); - retval = -ENOMEM; - WARN_ON(1); - } else { - ep_context->rsvd0 = cpu_to_le32(1); - ep_context->rsvd1 = 0; - - /* Configure the trb address and set the DCS bit. - * Both DCS bit and own bit in trb should be set. - */ - ep_context->trb_addr_lo = - cpu_to_le32(req->trb_head->trb_dma | DCS_ENABLE); - ep_context->trb_addr_hi = 0; - - /* Ensure that updates to the EP Context will - * occure before Ring Bell. - */ - wmb(); - - /* ring bell the ep */ - if (ep->ep_num == 0) - tmp = 0x1; - else - tmp = ep->ep_num * 2 - + ((direction == MV_U3D_EP_DIR_OUT) ? 0 : 1); - - iowrite32(tmp, &u3d->op_regs->doorbell); - } - return retval; -} - -static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req, - unsigned *length, dma_addr_t *dma) -{ - u32 temp; - unsigned int direction; - struct mv_u3d_trb *trb; - struct mv_u3d_trb_hw *trb_hw; - struct mv_u3d *u3d; - - /* how big will this transfer be? */ - *length = req->req.length - req->req.actual; - BUG_ON(*length > (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER); - - u3d = req->ep->u3d; - - trb = kzalloc(sizeof(*trb), GFP_ATOMIC); - if (!trb) - return NULL; - - /* - * Be careful that no _GFP_HIGHMEM is set, - * or we can not use dma_to_virt - * cannot use GFP_KERNEL in spin lock - */ - trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma); - if (!trb_hw) { - kfree(trb); - dev_err(u3d->dev, - "%s, dma_pool_alloc fail\n", __func__); - return NULL; - } - trb->trb_dma = *dma; - trb->trb_hw = trb_hw; - - /* initialize buffer page pointers */ - temp = (u32)(req->req.dma + req->req.actual); - - trb_hw->buf_addr_lo = cpu_to_le32(temp); - trb_hw->buf_addr_hi = 0; - trb_hw->trb_len = cpu_to_le32(*length); - trb_hw->ctrl.own = 1; - - if (req->ep->ep_num == 0) - trb_hw->ctrl.type = TYPE_DATA; - else - trb_hw->ctrl.type = TYPE_NORMAL; - - req->req.actual += *length; - - direction = mv_u3d_ep_dir(req->ep); - if (direction == MV_U3D_EP_DIR_IN) - trb_hw->ctrl.dir = 1; - else - trb_hw->ctrl.dir = 0; - - /* Enable interrupt for the last trb of a request */ - if (!req->req.no_interrupt) - trb_hw->ctrl.ioc = 1; - - trb_hw->ctrl.chain = 0; - - wmb(); - return trb; -} - -static int mv_u3d_build_trb_chain(struct mv_u3d_req *req, unsigned *length, - struct mv_u3d_trb *trb, int *is_last) -{ - u32 temp; - unsigned int direction; - struct mv_u3d *u3d; - - /* how big will this transfer be? */ - *length = min(req->req.length - req->req.actual, - (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER); - - u3d = req->ep->u3d; - - trb->trb_dma = 0; - - /* initialize buffer page pointers */ - temp = (u32)(req->req.dma + req->req.actual); - - trb->trb_hw->buf_addr_lo = cpu_to_le32(temp); - trb->trb_hw->buf_addr_hi = 0; - trb->trb_hw->trb_len = cpu_to_le32(*length); - trb->trb_hw->ctrl.own = 1; - - if (req->ep->ep_num == 0) - trb->trb_hw->ctrl.type = TYPE_DATA; - else - trb->trb_hw->ctrl.type = TYPE_NORMAL; - - req->req.actual += *length; - - direction = mv_u3d_ep_dir(req->ep); - if (direction == MV_U3D_EP_DIR_IN) - trb->trb_hw->ctrl.dir = 1; - else - trb->trb_hw->ctrl.dir = 0; - - /* zlp is needed if req->req.zero is set */ - if (req->req.zero) { - if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) - *is_last = 1; - else - *is_last = 0; - } else if (req->req.length == req->req.actual) - *is_last = 1; - else - *is_last = 0; - - /* Enable interrupt for the last trb of a request */ - if (*is_last && !req->req.no_interrupt) - trb->trb_hw->ctrl.ioc = 1; - - if (*is_last) - trb->trb_hw->ctrl.chain = 0; - else { - trb->trb_hw->ctrl.chain = 1; - dev_dbg(u3d->dev, "chain trb\n"); - } - - wmb(); - - return 0; -} - -/* generate TRB linked list for a request - * usb controller only supports continous trb chain, - * that trb structure physical address should be continous. - */ -static int mv_u3d_req_to_trb(struct mv_u3d_req *req) -{ - unsigned count; - int is_last; - struct mv_u3d_trb *trb; - struct mv_u3d_trb_hw *trb_hw; - struct mv_u3d *u3d; - dma_addr_t dma; - unsigned length; - unsigned trb_num; - - u3d = req->ep->u3d; - - INIT_LIST_HEAD(&req->trb_list); - - length = req->req.length - req->req.actual; - /* normally the request transfer length is less than 16KB. - * we use buil_trb_one() to optimize it. - */ - if (length <= (unsigned)MV_U3D_EP_MAX_LENGTH_TRANSFER) { - trb = mv_u3d_build_trb_one(req, &count, &dma); - list_add_tail(&trb->trb_list, &req->trb_list); - req->trb_head = trb; - req->trb_count = 1; - req->chain = 0; - } else { - trb_num = length / MV_U3D_EP_MAX_LENGTH_TRANSFER; - if (length % MV_U3D_EP_MAX_LENGTH_TRANSFER) - trb_num++; - - trb = kcalloc(trb_num, sizeof(*trb), GFP_ATOMIC); - if (!trb) - return -ENOMEM; - - trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC); - if (!trb_hw) { - kfree(trb); - return -ENOMEM; - } - - do { - trb->trb_hw = trb_hw; - if (mv_u3d_build_trb_chain(req, &count, - trb, &is_last)) { - dev_err(u3d->dev, - "%s, mv_u3d_build_trb_chain fail\n", - __func__); - return -EIO; - } - - list_add_tail(&trb->trb_list, &req->trb_list); - req->trb_count++; - trb++; - trb_hw++; - } while (!is_last); - - req->trb_head = list_entry(req->trb_list.next, - struct mv_u3d_trb, trb_list); - req->trb_head->trb_dma = dma_map_single(u3d->gadget.dev.parent, - req->trb_head->trb_hw, - trb_num * sizeof(*trb_hw), - DMA_BIDIRECTIONAL); - if (dma_mapping_error(u3d->gadget.dev.parent, - req->trb_head->trb_dma)) { - kfree(req->trb_head->trb_hw); - kfree(req->trb_head); - return -EFAULT; - } - - req->chain = 1; - } - - return 0; -} - -static int -mv_u3d_start_queue(struct mv_u3d_ep *ep) -{ - struct mv_u3d *u3d = ep->u3d; - struct mv_u3d_req *req; - int ret; - - if (!list_empty(&ep->req_list) && !ep->processing) - req = list_entry(ep->req_list.next, struct mv_u3d_req, list); - else - return 0; - - ep->processing = 1; - - /* set up dma mapping */ - ret = usb_gadget_map_request(&u3d->gadget, &req->req, - mv_u3d_ep_dir(ep)); - if (ret) - goto break_processing; - - req->req.status = -EINPROGRESS; - req->req.actual = 0; - req->trb_count = 0; - - /* build trbs */ - ret = mv_u3d_req_to_trb(req); - if (ret) { - dev_err(u3d->dev, "%s, mv_u3d_req_to_trb fail\n", __func__); - goto break_processing; - } - - /* and push them to device queue */ - ret = mv_u3d_queue_trb(ep, req); - if (ret) - goto break_processing; - - /* irq handler advances the queue */ - list_add_tail(&req->queue, &ep->queue); - - return 0; - -break_processing: - ep->processing = 0; - return ret; -} - -static int mv_u3d_ep_enable(struct usb_ep *_ep, - const struct usb_endpoint_descriptor *desc) -{ - struct mv_u3d *u3d; - struct mv_u3d_ep *ep; - u16 max = 0; - unsigned maxburst = 0; - u32 epxcr, direction; - - if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) - return -EINVAL; - - ep = container_of(_ep, struct mv_u3d_ep, ep); - u3d = ep->u3d; - - if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - direction = mv_u3d_ep_dir(ep); - max = le16_to_cpu(desc->wMaxPacketSize); - - if (!_ep->maxburst) - _ep->maxburst = 1; - maxburst = _ep->maxburst; - - /* Set the max burst size */ - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { - case USB_ENDPOINT_XFER_BULK: - if (maxburst > 16) { - dev_dbg(u3d->dev, - "max burst should not be greater " - "than 16 on bulk ep\n"); - maxburst = 1; - _ep->maxburst = maxburst; - } - dev_dbg(u3d->dev, - "maxburst: %d on bulk %s\n", maxburst, ep->name); - break; - case USB_ENDPOINT_XFER_CONTROL: - /* control transfer only supports maxburst as one */ - maxburst = 1; - _ep->maxburst = maxburst; - break; - case USB_ENDPOINT_XFER_INT: - if (maxburst != 1) { - dev_dbg(u3d->dev, - "max burst should be 1 on int ep " - "if transfer size is not 1024\n"); - maxburst = 1; - _ep->maxburst = maxburst; - } - break; - case USB_ENDPOINT_XFER_ISOC: - if (maxburst != 1) { - dev_dbg(u3d->dev, - "max burst should be 1 on isoc ep " - "if transfer size is not 1024\n"); - maxburst = 1; - _ep->maxburst = maxburst; - } - break; - default: - goto en_done; - } - - ep->ep.maxpacket = max; - ep->ep.desc = desc; - ep->enabled = 1; - - /* Enable the endpoint for Rx or Tx and set the endpoint type */ - if (direction == MV_U3D_EP_DIR_OUT) { - epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); - epxcr |= MV_U3D_EPXCR_EP_INIT; - iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); - udelay(5); - epxcr &= ~MV_U3D_EPXCR_EP_INIT; - iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); - - epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) - | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) - | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) - | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); - iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); - } else { - epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); - epxcr |= MV_U3D_EPXCR_EP_INIT; - iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); - udelay(5); - epxcr &= ~MV_U3D_EPXCR_EP_INIT; - iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); - - epxcr = ((max << MV_U3D_EPXCR_MAX_PACKET_SIZE_SHIFT) - | ((maxburst - 1) << MV_U3D_EPXCR_MAX_BURST_SIZE_SHIFT) - | (1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) - | (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); - iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1); - } - - return 0; -en_done: - return -EINVAL; -} - -static int mv_u3d_ep_disable(struct usb_ep *_ep) -{ - struct mv_u3d *u3d; - struct mv_u3d_ep *ep; - u32 epxcr, direction; - unsigned long flags; - - if (!_ep) - return -EINVAL; - - ep = container_of(_ep, struct mv_u3d_ep, ep); - if (!ep->ep.desc) - return -EINVAL; - - u3d = ep->u3d; - - direction = mv_u3d_ep_dir(ep); - - /* nuke all pending requests (does flush) */ - spin_lock_irqsave(&u3d->lock, flags); - mv_u3d_nuke(ep, -ESHUTDOWN); - spin_unlock_irqrestore(&u3d->lock, flags); - - /* Disable the endpoint for Rx or Tx and reset the endpoint type */ - if (direction == MV_U3D_EP_DIR_OUT) { - epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); - epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) - | USB_ENDPOINT_XFERTYPE_MASK); - iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr1); - } else { - epxcr = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr1); - epxcr &= ~((1 << MV_U3D_EPXCR_EP_ENABLE_SHIFT) - | USB_ENDPOINT_XFERTYPE_MASK); - iowrite32(epxcr, &u3d->vuc_regs->epcr[ep->ep_num].epxincr1); - } - - ep->enabled = 0; - - ep->ep.desc = NULL; - return 0; -} - -static struct usb_request * -mv_u3d_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) -{ - struct mv_u3d_req *req; - - req = kzalloc(sizeof *req, gfp_flags); - if (!req) - return NULL; - - INIT_LIST_HEAD(&req->queue); - - return &req->req; -} - -static void mv_u3d_free_request(struct usb_ep *_ep, struct usb_request *_req) -{ - struct mv_u3d_req *req = container_of(_req, struct mv_u3d_req, req); - - kfree(req); -} - -static void mv_u3d_ep_fifo_flush(struct usb_ep *_ep) -{ - struct mv_u3d *u3d; - u32 direction; - struct mv_u3d_ep *ep = container_of(_ep, struct mv_u3d_ep, ep); - unsigned int loops; - u32 tmp; - - /* if endpoint is not enabled, cannot flush endpoint */ - if (!ep->enabled) - return; - - u3d = ep->u3d; - direction = mv_u3d_ep_dir(ep); - - /* ep0 need clear bit after flushing fifo. */ - if (!ep->ep_num) { - if (direction == MV_U3D_EP_DIR_OUT) { - tmp = ioread32(&u3d->vuc_regs->epcr[0].epxoutcr0); - tmp |= MV_U3D_EPXCR_EP_FLUSH; - iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); - udelay(10); - tmp &= ~MV_U3D_EPXCR_EP_FLUSH; - iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxoutcr0); - } else { - tmp = ioread32(&u3d->vuc_regs->epcr[0].epxincr0); - tmp |= MV_U3D_EPXCR_EP_FLUSH; - iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); - udelay(10); - tmp &= ~MV_U3D_EPXCR_EP_FLUSH; - iowrite32(tmp, &u3d->vuc_regs->epcr[0].epxincr0); - } - return; - } - - if (direction == MV_U3D_EP_DIR_OUT) { - tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); - tmp |= MV_U3D_EPXCR_EP_FLUSH; - iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); - - /* Wait until flushing completed */ - loops = LOOPS(MV_U3D_FLUSH_TIMEOUT); - while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0) & - MV_U3D_EPXCR_EP_FLUSH) { - /* - * EP_FLUSH bit should be cleared to indicate this - * operation is complete - */ - if (loops == 0) { - dev_dbg(u3d->dev, - "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num, - direction ? "in" : "out"); - return; - } - loops--; - udelay(LOOPS_USEC); - } - } else { /* EP_DIR_IN */ - tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); - tmp |= MV_U3D_EPXCR_EP_FLUSH; - iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); - - /* Wait until flushing completed */ - loops = LOOPS(MV_U3D_FLUSH_TIMEOUT); - while (ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0) & - MV_U3D_EPXCR_EP_FLUSH) { - /* - * EP_FLUSH bit should be cleared to indicate this - * operation is complete - */ - if (loops == 0) { - dev_dbg(u3d->dev, - "EP FLUSH TIMEOUT for ep%d%s\n", ep->ep_num, - direction ? "in" : "out"); - return; - } - loops--; - udelay(LOOPS_USEC); - } - } -} - -/* queues (submits) an I/O request to an endpoint */ -static int -mv_u3d_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) -{ - struct mv_u3d_ep *ep; - struct mv_u3d_req *req; - struct mv_u3d *u3d; - unsigned long flags; - int is_first_req = 0; - - if (unlikely(!_ep || !_req)) - return -EINVAL; - - ep = container_of(_ep, struct mv_u3d_ep, ep); - u3d = ep->u3d; - - req = container_of(_req, struct mv_u3d_req, req); - - if (!ep->ep_num - && u3d->ep0_state == MV_U3D_STATUS_STAGE - && !_req->length) { - dev_dbg(u3d->dev, "ep0 status stage\n"); - u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; - return 0; - } - - dev_dbg(u3d->dev, "%s: %s, req: 0x%p\n", - __func__, _ep->name, req); - - /* catch various bogus parameters */ - if (!req->req.complete || !req->req.buf - || !list_empty(&req->queue)) { - dev_err(u3d->dev, - "%s, bad params, _req: 0x%p," - "req->req.complete: 0x%p, req->req.buf: 0x%p," - "list_empty: 0x%x\n", - __func__, _req, - req->req.complete, req->req.buf, - list_empty(&req->queue)); - return -EINVAL; - } - if (unlikely(!ep->ep.desc)) { - dev_err(u3d->dev, "%s, bad ep\n", __func__); - return -EINVAL; - } - if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { - if (req->req.length > ep->ep.maxpacket) - return -EMSGSIZE; - } - - if (!u3d->driver || u3d->gadget.speed == USB_SPEED_UNKNOWN) { - dev_err(u3d->dev, - "bad params of driver/speed\n"); - return -ESHUTDOWN; - } - - req->ep = ep; - - /* Software list handles usb request. */ - spin_lock_irqsave(&ep->req_lock, flags); - is_first_req = list_empty(&ep->req_list); - list_add_tail(&req->list, &ep->req_list); - spin_unlock_irqrestore(&ep->req_lock, flags); - if (!is_first_req) { - dev_dbg(u3d->dev, "list is not empty\n"); - return 0; - } - - dev_dbg(u3d->dev, "call mv_u3d_start_queue from usb_ep_queue\n"); - spin_lock_irqsave(&u3d->lock, flags); - mv_u3d_start_queue(ep); - spin_unlock_irqrestore(&u3d->lock, flags); - return 0; -} - -/* dequeues (cancels, unlinks) an I/O request from an endpoint */ -static int mv_u3d_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) -{ - struct mv_u3d_ep *ep; - struct mv_u3d_req *req = NULL, *iter; - struct mv_u3d *u3d; - struct mv_u3d_ep_context *ep_context; - struct mv_u3d_req *next_req; - - unsigned long flags; - int ret = 0; - - if (!_ep || !_req) - return -EINVAL; - - ep = container_of(_ep, struct mv_u3d_ep, ep); - u3d = ep->u3d; - - spin_lock_irqsave(&ep->u3d->lock, flags); - - /* make sure it's actually queued on this endpoint */ - list_for_each_entry(iter, &ep->queue, queue) { - if (&iter->req != _req) - continue; - req = iter; - break; - } - if (!req) { - ret = -EINVAL; - goto out; - } - - /* The request is in progress, or completed but not dequeued */ - if (ep->queue.next == &req->queue) { - _req->status = -ECONNRESET; - mv_u3d_ep_fifo_flush(_ep); - - /* The request isn't the last request in this ep queue */ - if (req->queue.next != &ep->queue) { - dev_dbg(u3d->dev, - "it is the last request in this ep queue\n"); - ep_context = ep->ep_context; - next_req = list_entry(req->queue.next, - struct mv_u3d_req, queue); - - /* Point first TRB of next request to the EP context. */ - iowrite32((unsigned long) next_req->trb_head, - &ep_context->trb_addr_lo); - } else { - struct mv_u3d_ep_context *ep_context; - ep_context = ep->ep_context; - ep_context->trb_addr_lo = 0; - ep_context->trb_addr_hi = 0; - } - - } else - WARN_ON(1); - - mv_u3d_done(ep, req, -ECONNRESET); - - /* remove the req from the ep req list */ - if (!list_empty(&ep->req_list)) { - struct mv_u3d_req *curr_req; - curr_req = list_entry(ep->req_list.next, - struct mv_u3d_req, list); - if (curr_req == req) { - list_del_init(&req->list); - ep->processing = 0; - } - } - -out: - spin_unlock_irqrestore(&ep->u3d->lock, flags); - return ret; -} - -static void -mv_u3d_ep_set_stall(struct mv_u3d *u3d, u8 ep_num, u8 direction, int stall) -{ - u32 tmp; - struct mv_u3d_ep *ep = u3d->eps; - - dev_dbg(u3d->dev, "%s\n", __func__); - if (direction == MV_U3D_EP_DIR_OUT) { - tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); - if (stall) - tmp |= MV_U3D_EPXCR_EP_HALT; - else - tmp &= ~MV_U3D_EPXCR_EP_HALT; - iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxoutcr0); - } else { - tmp = ioread32(&u3d->vuc_regs->epcr[ep->ep_num].epxincr0); - if (stall) - tmp |= MV_U3D_EPXCR_EP_HALT; - else - tmp &= ~MV_U3D_EPXCR_EP_HALT; - iowrite32(tmp, &u3d->vuc_regs->epcr[ep->ep_num].epxincr0); - } -} - -static int mv_u3d_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge) -{ - struct mv_u3d_ep *ep; - unsigned long flags; - int status = 0; - struct mv_u3d *u3d; - - ep = container_of(_ep, struct mv_u3d_ep, ep); - u3d = ep->u3d; - if (!ep->ep.desc) { - status = -EINVAL; - goto out; - } - - if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { - status = -EOPNOTSUPP; - goto out; - } - - /* - * Attempt to halt IN ep will fail if any transfer requests - * are still queue - */ - if (halt && (mv_u3d_ep_dir(ep) == MV_U3D_EP_DIR_IN) - && !list_empty(&ep->queue)) { - status = -EAGAIN; - goto out; - } - - spin_lock_irqsave(&ep->u3d->lock, flags); - mv_u3d_ep_set_stall(u3d, ep->ep_num, mv_u3d_ep_dir(ep), halt); - if (halt && wedge) - ep->wedge = 1; - else if (!halt) - ep->wedge = 0; - spin_unlock_irqrestore(&ep->u3d->lock, flags); - - if (ep->ep_num == 0) - u3d->ep0_dir = MV_U3D_EP_DIR_OUT; -out: - return status; -} - -static int mv_u3d_ep_set_halt(struct usb_ep *_ep, int halt) -{ - return mv_u3d_ep_set_halt_wedge(_ep, halt, 0); -} - -static int mv_u3d_ep_set_wedge(struct usb_ep *_ep) -{ - return mv_u3d_ep_set_halt_wedge(_ep, 1, 1); -} - -static const struct usb_ep_ops mv_u3d_ep_ops = { - .enable = mv_u3d_ep_enable, - .disable = mv_u3d_ep_disable, - - .alloc_request = mv_u3d_alloc_request, - .free_request = mv_u3d_free_request, - - .queue = mv_u3d_ep_queue, - .dequeue = mv_u3d_ep_dequeue, - - .set_wedge = mv_u3d_ep_set_wedge, - .set_halt = mv_u3d_ep_set_halt, - .fifo_flush = mv_u3d_ep_fifo_flush, -}; - -static void mv_u3d_controller_stop(struct mv_u3d *u3d) -{ - u32 tmp; - - if (!u3d->clock_gating && u3d->vbus_valid_detect) - iowrite32(MV_U3D_INTR_ENABLE_VBUS_VALID, - &u3d->vuc_regs->intrenable); - else - iowrite32(0, &u3d->vuc_regs->intrenable); - iowrite32(~0x0, &u3d->vuc_regs->endcomplete); - iowrite32(~0x0, &u3d->vuc_regs->trbunderrun); - iowrite32(~0x0, &u3d->vuc_regs->trbcomplete); - iowrite32(~0x0, &u3d->vuc_regs->linkchange); - iowrite32(0x1, &u3d->vuc_regs->setuplock); - - /* Reset the RUN bit in the command register to stop USB */ - tmp = ioread32(&u3d->op_regs->usbcmd); - tmp &= ~MV_U3D_CMD_RUN_STOP; - iowrite32(tmp, &u3d->op_regs->usbcmd); - dev_dbg(u3d->dev, "after u3d_stop, USBCMD 0x%x\n", - ioread32(&u3d->op_regs->usbcmd)); -} - -static void mv_u3d_controller_start(struct mv_u3d *u3d) -{ - u32 usbintr; - u32 temp; - - /* enable link LTSSM state machine */ - temp = ioread32(&u3d->vuc_regs->ltssm); - temp |= MV_U3D_LTSSM_PHY_INIT_DONE; - iowrite32(temp, &u3d->vuc_regs->ltssm); - - /* Enable interrupts */ - usbintr = MV_U3D_INTR_ENABLE_LINK_CHG | MV_U3D_INTR_ENABLE_TXDESC_ERR | - MV_U3D_INTR_ENABLE_RXDESC_ERR | MV_U3D_INTR_ENABLE_TX_COMPLETE | - MV_U3D_INTR_ENABLE_RX_COMPLETE | MV_U3D_INTR_ENABLE_SETUP | - (u3d->vbus_valid_detect ? MV_U3D_INTR_ENABLE_VBUS_VALID : 0); - iowrite32(usbintr, &u3d->vuc_regs->intrenable); - - /* Enable ctrl ep */ - iowrite32(0x1, &u3d->vuc_regs->ctrlepenable); - - /* Set the Run bit in the command register */ - iowrite32(MV_U3D_CMD_RUN_STOP, &u3d->op_regs->usbcmd); - dev_dbg(u3d->dev, "after u3d_start, USBCMD 0x%x\n", - ioread32(&u3d->op_regs->usbcmd)); -} - -static int mv_u3d_controller_reset(struct mv_u3d *u3d) -{ - unsigned int loops; - u32 tmp; - - /* Stop the controller */ - tmp = ioread32(&u3d->op_regs->usbcmd); - tmp &= ~MV_U3D_CMD_RUN_STOP; - iowrite32(tmp, &u3d->op_regs->usbcmd); - - /* Reset the controller to get default values */ - iowrite32(MV_U3D_CMD_CTRL_RESET, &u3d->op_regs->usbcmd); - - /* wait for reset to complete */ - loops = LOOPS(MV_U3D_RESET_TIMEOUT); - while (ioread32(&u3d->op_regs->usbcmd) & MV_U3D_CMD_CTRL_RESET) { - if (loops == 0) { - dev_err(u3d->dev, - "Wait for RESET completed TIMEOUT\n"); - return -ETIMEDOUT; - } - loops--; - udelay(LOOPS_USEC); - } - - /* Configure the Endpoint Context Address */ - iowrite32(u3d->ep_context_dma, &u3d->op_regs->dcbaapl); - iowrite32(0, &u3d->op_regs->dcbaaph); - - return 0; -} - -static int mv_u3d_enable(struct mv_u3d *u3d) -{ - struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); - int retval; - - if (u3d->active) - return 0; - - if (!u3d->clock_gating) { - u3d->active = 1; - return 0; - } - - dev_dbg(u3d->dev, "enable u3d\n"); - clk_enable(u3d->clk); - if (pdata->phy_init) { - retval = pdata->phy_init(u3d->phy_regs); - if (retval) { - dev_err(u3d->dev, - "init phy error %d\n", retval); - clk_disable(u3d->clk); - return retval; - } - } - u3d->active = 1; - - return 0; -} - -static void mv_u3d_disable(struct mv_u3d *u3d) -{ - struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); - if (u3d->clock_gating && u3d->active) { - dev_dbg(u3d->dev, "disable u3d\n"); - if (pdata->phy_deinit) - pdata->phy_deinit(u3d->phy_regs); - clk_disable(u3d->clk); - u3d->active = 0; - } -} - -static int mv_u3d_vbus_session(struct usb_gadget *gadget, int is_active) -{ - struct mv_u3d *u3d; - unsigned long flags; - int retval = 0; - - u3d = container_of(gadget, struct mv_u3d, gadget); - - spin_lock_irqsave(&u3d->lock, flags); - - u3d->vbus_active = (is_active != 0); - dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n", - __func__, u3d->softconnect, u3d->vbus_active); - /* - * 1. external VBUS detect: we can disable/enable clock on demand. - * 2. UDC VBUS detect: we have to enable clock all the time. - * 3. No VBUS detect: we have to enable clock all the time. - */ - if (u3d->driver && u3d->softconnect && u3d->vbus_active) { - retval = mv_u3d_enable(u3d); - if (retval == 0) { - /* - * after clock is disabled, we lost all the register - * context. We have to re-init registers - */ - mv_u3d_controller_reset(u3d); - mv_u3d_ep0_reset(u3d); - mv_u3d_controller_start(u3d); - } - } else if (u3d->driver && u3d->softconnect) { - if (!u3d->active) - goto out; - - /* stop all the transfer in queue*/ - mv_u3d_stop_activity(u3d, u3d->driver); - mv_u3d_controller_stop(u3d); - mv_u3d_disable(u3d); - } - -out: - spin_unlock_irqrestore(&u3d->lock, flags); - return retval; -} - -/* constrain controller's VBUS power usage - * This call is used by gadget drivers during SET_CONFIGURATION calls, - * reporting how much power the device may consume. For example, this - * could affect how quickly batteries are recharged. - * - * Returns zero on success, else negative errno. - */ -static int mv_u3d_vbus_draw(struct usb_gadget *gadget, unsigned mA) -{ - struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget); - - u3d->power = mA; - - return 0; -} - -static int mv_u3d_pullup(struct usb_gadget *gadget, int is_on) -{ - struct mv_u3d *u3d = container_of(gadget, struct mv_u3d, gadget); - unsigned long flags; - int retval = 0; - - spin_lock_irqsave(&u3d->lock, flags); - - dev_dbg(u3d->dev, "%s: softconnect %d, vbus_active %d\n", - __func__, u3d->softconnect, u3d->vbus_active); - u3d->softconnect = (is_on != 0); - if (u3d->driver && u3d->softconnect && u3d->vbus_active) { - retval = mv_u3d_enable(u3d); - if (retval == 0) { - /* - * after clock is disabled, we lost all the register - * context. We have to re-init registers - */ - mv_u3d_controller_reset(u3d); - mv_u3d_ep0_reset(u3d); - mv_u3d_controller_start(u3d); - } - } else if (u3d->driver && u3d->vbus_active) { - /* stop all the transfer in queue*/ - mv_u3d_stop_activity(u3d, u3d->driver); - mv_u3d_controller_stop(u3d); - mv_u3d_disable(u3d); - } - - spin_unlock_irqrestore(&u3d->lock, flags); - - return retval; -} - -static int mv_u3d_start(struct usb_gadget *g, - struct usb_gadget_driver *driver) -{ - struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget); - struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); - unsigned long flags; - - if (u3d->driver) - return -EBUSY; - - spin_lock_irqsave(&u3d->lock, flags); - - if (!u3d->clock_gating) { - clk_enable(u3d->clk); - if (pdata->phy_init) - pdata->phy_init(u3d->phy_regs); - } - - /* hook up the driver ... */ - u3d->driver = driver; - - u3d->ep0_dir = USB_DIR_OUT; - - spin_unlock_irqrestore(&u3d->lock, flags); - - u3d->vbus_valid_detect = 1; - - return 0; -} - -static int mv_u3d_stop(struct usb_gadget *g) -{ - struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget); - struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev); - unsigned long flags; - - u3d->vbus_valid_detect = 0; - spin_lock_irqsave(&u3d->lock, flags); - - /* enable clock to access controller register */ - clk_enable(u3d->clk); - if (pdata->phy_init) - pdata->phy_init(u3d->phy_regs); - - mv_u3d_controller_stop(u3d); - /* stop all usb activities */ - u3d->gadget.speed = USB_SPEED_UNKNOWN; - mv_u3d_stop_activity(u3d, NULL); - mv_u3d_disable(u3d); - - if (pdata->phy_deinit) - pdata->phy_deinit(u3d->phy_regs); - clk_disable(u3d->clk); - - spin_unlock_irqrestore(&u3d->lock, flags); - - u3d->driver = NULL; - - return 0; -} - -/* device controller usb_gadget_ops structure */ -static const struct usb_gadget_ops mv_u3d_ops = { - /* notify controller that VBUS is powered or not */ - .vbus_session = mv_u3d_vbus_session, - - /* constrain controller's VBUS power usage */ - .vbus_draw = mv_u3d_vbus_draw, - - .pullup = mv_u3d_pullup, - .udc_start = mv_u3d_start, - .udc_stop = mv_u3d_stop, -}; - -static int mv_u3d_eps_init(struct mv_u3d *u3d) -{ - struct mv_u3d_ep *ep; - char name[14]; - int i; - - /* initialize ep0, ep0 in/out use eps[1] */ - ep = &u3d->eps[1]; - ep->u3d = u3d; - strscpy(ep->name, "ep0"); - ep->ep.name = ep->name; - ep->ep.ops = &mv_u3d_ep_ops; - ep->wedge = 0; - usb_ep_set_maxpacket_limit(&ep->ep, MV_U3D_EP0_MAX_PKT_SIZE); - ep->ep.caps.type_control = true; - ep->ep.caps.dir_in = true; - ep->ep.caps.dir_out = true; - ep->ep_num = 0; - ep->ep.desc = &mv_u3d_ep0_desc; - INIT_LIST_HEAD(&ep->queue); - INIT_LIST_HEAD(&ep->req_list); - ep->ep_type = USB_ENDPOINT_XFER_CONTROL; - - /* add ep0 ep_context */ - ep->ep_context = &u3d->ep_context[1]; - - /* initialize other endpoints */ - for (i = 2; i < u3d->max_eps * 2; i++) { - ep = &u3d->eps[i]; - if (i & 1) { - snprintf(name, sizeof(name), "ep%din", i >> 1); - ep->direction = MV_U3D_EP_DIR_IN; - ep->ep.caps.dir_in = true; - } else { - snprintf(name, sizeof(name), "ep%dout", i >> 1); - ep->direction = MV_U3D_EP_DIR_OUT; - ep->ep.caps.dir_out = true; - } - ep->u3d = u3d; - strscpy(ep->name, name); - ep->ep.name = ep->name; - - ep->ep.caps.type_iso = true; - ep->ep.caps.type_bulk = true; - ep->ep.caps.type_int = true; - - ep->ep.ops = &mv_u3d_ep_ops; - usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); - ep->ep_num = i / 2; - - INIT_LIST_HEAD(&ep->queue); - list_add_tail(&ep->ep.ep_list, &u3d->gadget.ep_list); - - INIT_LIST_HEAD(&ep->req_list); - spin_lock_init(&ep->req_lock); - ep->ep_context = &u3d->ep_context[i]; - } - - return 0; -} - -/* delete all endpoint requests, called with spinlock held */ -static void mv_u3d_nuke(struct mv_u3d_ep *ep, int status) -{ - /* endpoint fifo flush */ - mv_u3d_ep_fifo_flush(&ep->ep); - - while (!list_empty(&ep->queue)) { - struct mv_u3d_req *req = NULL; - req = list_entry(ep->queue.next, struct mv_u3d_req, queue); - mv_u3d_done(ep, req, status); - } -} - -/* stop all USB activities */ -static -void mv_u3d_stop_activity(struct mv_u3d *u3d, struct usb_gadget_driver *driver) -{ - struct mv_u3d_ep *ep; - - mv_u3d_nuke(&u3d->eps[1], -ESHUTDOWN); - - list_for_each_entry(ep, &u3d->gadget.ep_list, ep.ep_list) { - mv_u3d_nuke(ep, -ESHUTDOWN); - } - - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock(&u3d->lock); - driver->disconnect(&u3d->gadget); - spin_lock(&u3d->lock); - } -} - -static void mv_u3d_irq_process_error(struct mv_u3d *u3d) -{ - /* Increment the error count */ - u3d->errors++; - dev_err(u3d->dev, "%s\n", __func__); -} - -static void mv_u3d_irq_process_link_change(struct mv_u3d *u3d) -{ - u32 linkchange; - - linkchange = ioread32(&u3d->vuc_regs->linkchange); - iowrite32(linkchange, &u3d->vuc_regs->linkchange); - - dev_dbg(u3d->dev, "linkchange: 0x%x\n", linkchange); - - if (linkchange & MV_U3D_LINK_CHANGE_LINK_UP) { - dev_dbg(u3d->dev, "link up: ltssm state: 0x%x\n", - ioread32(&u3d->vuc_regs->ltssmstate)); - - u3d->usb_state = USB_STATE_DEFAULT; - u3d->ep0_dir = MV_U3D_EP_DIR_OUT; - u3d->ep0_state = MV_U3D_WAIT_FOR_SETUP; - - /* set speed */ - u3d->gadget.speed = USB_SPEED_SUPER; - } - - if (linkchange & MV_U3D_LINK_CHANGE_SUSPEND) { - dev_dbg(u3d->dev, "link suspend\n"); - u3d->resume_state = u3d->usb_state; - u3d->usb_state = USB_STATE_SUSPENDED; - } - - if (linkchange & MV_U3D_LINK_CHANGE_RESUME) { - dev_dbg(u3d->dev, "link resume\n"); - u3d->usb_state = u3d->resume_state; - u3d->resume_state = 0; - } - - if (linkchange & MV_U3D_LINK_CHANGE_WRESET) { - dev_dbg(u3d->dev, "warm reset\n"); - u3d->usb_state = USB_STATE_POWERED; - } - - if (linkchange & MV_U3D_LINK_CHANGE_HRESET) { - dev_dbg(u3d->dev, "hot reset\n"); - u3d->usb_state = USB_STATE_DEFAULT; - } - - if (linkchange & MV_U3D_LINK_CHANGE_INACT) - dev_dbg(u3d->dev, "inactive\n"); - - if (linkchange & MV_U3D_LINK_CHANGE_DISABLE_AFTER_U0) - dev_dbg(u3d->dev, "ss.disabled\n"); - - if (linkchange & MV_U3D_LINK_CHANGE_VBUS_INVALID) { - dev_dbg(u3d->dev, "vbus invalid\n"); - u3d->usb_state = USB_STATE_ATTACHED; - u3d->vbus_valid_detect = 1; - /* if external vbus detect is not supported, - * we handle it here. - */ - if (!u3d->vbus) { - spin_unlock(&u3d->lock); - mv_u3d_vbus_session(&u3d->gadget, 0); - spin_lock(&u3d->lock); - } - } -} - -static void mv_u3d_ch9setaddress(struct mv_u3d *u3d, - struct usb_ctrlrequest *setup) -{ - u32 tmp; - - if (u3d->usb_state != USB_STATE_DEFAULT) { - dev_err(u3d->dev, - "%s, cannot setaddr in this state (%d)\n", - __func__, u3d->usb_state); - goto err; - } - - u3d->dev_addr = (u8)setup->wValue; - - dev_dbg(u3d->dev, "%s: 0x%x\n", __func__, u3d->dev_addr); - - if (u3d->dev_addr > 127) { - dev_err(u3d->dev, - "%s, u3d address is wrong (out of range)\n", __func__); - u3d->dev_addr = 0; - goto err; - } - - /* update usb state */ - u3d->usb_state = USB_STATE_ADDRESS; - - /* set the new address */ - tmp = ioread32(&u3d->vuc_regs->devaddrtiebrkr); - tmp &= ~0x7F; - tmp |= (u32)u3d->dev_addr; - iowrite32(tmp, &u3d->vuc_regs->devaddrtiebrkr); - - return; -err: - mv_u3d_ep0_stall(u3d); -} - -static int mv_u3d_is_set_configuration(struct usb_ctrlrequest *setup) -{ - if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) - if (setup->bRequest == USB_REQ_SET_CONFIGURATION) - return 1; - - return 0; -} - -static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num, - struct usb_ctrlrequest *setup) - __releases(&u3c->lock) - __acquires(&u3c->lock) -{ - bool delegate = false; - - mv_u3d_nuke(&u3d->eps[ep_num * 2 + MV_U3D_EP_DIR_IN], -ESHUTDOWN); - - dev_dbg(u3d->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", - setup->bRequestType, setup->bRequest, - setup->wValue, setup->wIndex, setup->wLength); - - /* We process some stardard setup requests here */ - if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { - switch (setup->bRequest) { - case USB_REQ_GET_STATUS: - delegate = true; - break; - - case USB_REQ_SET_ADDRESS: - mv_u3d_ch9setaddress(u3d, setup); - break; - - case USB_REQ_CLEAR_FEATURE: - delegate = true; - break; - - case USB_REQ_SET_FEATURE: - delegate = true; - break; - - default: - delegate = true; - } - } else - delegate = true; - - /* delegate USB standard requests to the gadget driver */ - if (delegate) { - /* USB requests handled by gadget */ - if (setup->wLength) { - /* DATA phase from gadget, STATUS phase from u3d */ - u3d->ep0_dir = (setup->bRequestType & USB_DIR_IN) - ? MV_U3D_EP_DIR_IN : MV_U3D_EP_DIR_OUT; - spin_unlock(&u3d->lock); - if (u3d->driver->setup(&u3d->gadget, - &u3d->local_setup_buff) < 0) { - dev_err(u3d->dev, "setup error!\n"); - mv_u3d_ep0_stall(u3d); - } - spin_lock(&u3d->lock); - } else { - /* no DATA phase, STATUS phase from gadget */ - u3d->ep0_dir = MV_U3D_EP_DIR_IN; - u3d->ep0_state = MV_U3D_STATUS_STAGE; - spin_unlock(&u3d->lock); - if (u3d->driver->setup(&u3d->gadget, - &u3d->local_setup_buff) < 0) - mv_u3d_ep0_stall(u3d); - spin_lock(&u3d->lock); - } - - if (mv_u3d_is_set_configuration(setup)) { - dev_dbg(u3d->dev, "u3d configured\n"); - u3d->usb_state = USB_STATE_CONFIGURED; - } - } -} - -static void mv_u3d_get_setup_data(struct mv_u3d *u3d, u8 ep_num, u8 *buffer_ptr) -{ - struct mv_u3d_ep_context *epcontext; - - epcontext = &u3d->ep_context[ep_num * 2 + MV_U3D_EP_DIR_IN]; - - /* Copy the setup packet to local buffer */ - memcpy(buffer_ptr, (u8 *) &epcontext->setup_buffer, 8); -} - -static void mv_u3d_irq_process_setup(struct mv_u3d *u3d) -{ - u32 tmp, i; - /* Process all Setup packet received interrupts */ - tmp = ioread32(&u3d->vuc_regs->setuplock); - if (tmp) { - for (i = 0; i < u3d->max_eps; i++) { - if (tmp & (1 << i)) { - mv_u3d_get_setup_data(u3d, i, - (u8 *)(&u3d->local_setup_buff)); - mv_u3d_handle_setup_packet(u3d, i, - &u3d->local_setup_buff); - } - } - } - - iowrite32(tmp, &u3d->vuc_regs->setuplock); -} - -static void mv_u3d_irq_process_tr_complete(struct mv_u3d *u3d) -{ - u32 tmp, bit_pos; - int i, ep_num = 0, direction = 0; - struct mv_u3d_ep *curr_ep; - struct mv_u3d_req *curr_req, *temp_req; - int status; - - tmp = ioread32(&u3d->vuc_regs->endcomplete); - - dev_dbg(u3d->dev, "tr_complete: ep: 0x%x\n", tmp); - if (!tmp) - return; - iowrite32(tmp, &u3d->vuc_regs->endcomplete); - - for (i = 0; i < u3d->max_eps * 2; i++) { - ep_num = i >> 1; - direction = i % 2; - - bit_pos = 1 << (ep_num + 16 * direction); - - if (!(bit_pos & tmp)) - continue; - - if (i == 0) - curr_ep = &u3d->eps[1]; - else - curr_ep = &u3d->eps[i]; - - /* remove req out of ep request list after completion */ - dev_dbg(u3d->dev, "tr comp: check req_list\n"); - spin_lock(&curr_ep->req_lock); - if (!list_empty(&curr_ep->req_list)) { - struct mv_u3d_req *req; - req = list_entry(curr_ep->req_list.next, - struct mv_u3d_req, list); - list_del_init(&req->list); - curr_ep->processing = 0; - } - spin_unlock(&curr_ep->req_lock); - - /* process the req queue until an uncomplete request */ - list_for_each_entry_safe(curr_req, temp_req, - &curr_ep->queue, queue) { - status = mv_u3d_process_ep_req(u3d, i, curr_req); - if (status) - break; - /* write back status to req */ - curr_req->req.status = status; - - /* ep0 request completion */ - if (ep_num == 0) { - mv_u3d_done(curr_ep, curr_req, 0); - break; - } else { - mv_u3d_done(curr_ep, curr_req, status); - } - } - - dev_dbg(u3d->dev, "call mv_u3d_start_queue from ep complete\n"); - mv_u3d_start_queue(curr_ep); - } -} - -static irqreturn_t mv_u3d_irq(int irq, void *dev) -{ - struct mv_u3d *u3d = (struct mv_u3d *)dev; - u32 status, intr; - u32 bridgesetting; - u32 trbunderrun; - - spin_lock(&u3d->lock); - - status = ioread32(&u3d->vuc_regs->intrcause); - intr = ioread32(&u3d->vuc_regs->intrenable); - status &= intr; - - if (status == 0) { - spin_unlock(&u3d->lock); - dev_err(u3d->dev, "irq error!\n"); - return IRQ_NONE; - } - - if (status & MV_U3D_USBINT_VBUS_VALID) { - bridgesetting = ioread32(&u3d->vuc_regs->bridgesetting); - if (bridgesetting & MV_U3D_BRIDGE_SETTING_VBUS_VALID) { - /* write vbus valid bit of bridge setting to clear */ - bridgesetting = MV_U3D_BRIDGE_SETTING_VBUS_VALID; - iowrite32(bridgesetting, &u3d->vuc_regs->bridgesetting); - dev_dbg(u3d->dev, "vbus valid\n"); - - u3d->usb_state = USB_STATE_POWERED; - u3d->vbus_valid_detect = 0; - /* if external vbus detect is not supported, - * we handle it here. - */ - if (!u3d->vbus) { - spin_unlock(&u3d->lock); - mv_u3d_vbus_session(&u3d->gadget, 1); - spin_lock(&u3d->lock); - } - } else - dev_err(u3d->dev, "vbus bit is not set\n"); - } - - /* RX data is already in the 16KB FIFO.*/ - if (status & MV_U3D_USBINT_UNDER_RUN) { - trbunderrun = ioread32(&u3d->vuc_regs->trbunderrun); - dev_err(u3d->dev, "under run, ep%d\n", trbunderrun); - iowrite32(trbunderrun, &u3d->vuc_regs->trbunderrun); - mv_u3d_irq_process_error(u3d); - } - - if (status & (MV_U3D_USBINT_RXDESC_ERR | MV_U3D_USBINT_TXDESC_ERR)) { - /* write one to clear */ - iowrite32(status & (MV_U3D_USBINT_RXDESC_ERR - | MV_U3D_USBINT_TXDESC_ERR), - &u3d->vuc_regs->intrcause); - dev_err(u3d->dev, "desc err 0x%x\n", status); - mv_u3d_irq_process_error(u3d); - } - - if (status & MV_U3D_USBINT_LINK_CHG) - mv_u3d_irq_process_link_change(u3d); - - if (status & MV_U3D_USBINT_TX_COMPLETE) - mv_u3d_irq_process_tr_complete(u3d); - - if (status & MV_U3D_USBINT_RX_COMPLETE) - mv_u3d_irq_process_tr_complete(u3d); - - if (status & MV_U3D_USBINT_SETUP) - mv_u3d_irq_process_setup(u3d); - - spin_unlock(&u3d->lock); - return IRQ_HANDLED; -} - -static void mv_u3d_remove(struct platform_device *dev) -{ - struct mv_u3d *u3d = platform_get_drvdata(dev); - - BUG_ON(u3d == NULL); - - usb_del_gadget_udc(&u3d->gadget); - - /* free memory allocated in probe */ - dma_pool_destroy(u3d->trb_pool); - - if (u3d->ep_context) - dma_free_coherent(&dev->dev, u3d->ep_context_size, - u3d->ep_context, u3d->ep_context_dma); - - kfree(u3d->eps); - - if (u3d->irq) - free_irq(u3d->irq, u3d); - - if (u3d->cap_regs) - iounmap(u3d->cap_regs); - u3d->cap_regs = NULL; - - kfree(u3d->status_req); - - clk_put(u3d->clk); - - kfree(u3d); -} - -static int mv_u3d_probe(struct platform_device *dev) -{ - struct mv_u3d *u3d; - struct mv_usb_platform_data *pdata = dev_get_platdata(&dev->dev); - int retval = 0; - struct resource *r; - size_t size; - - if (!dev_get_platdata(&dev->dev)) { - dev_err(&dev->dev, "missing platform_data\n"); - retval = -ENODEV; - goto err_pdata; - } - - u3d = kzalloc(sizeof(*u3d), GFP_KERNEL); - if (!u3d) { - retval = -ENOMEM; - goto err_alloc_private; - } - - spin_lock_init(&u3d->lock); - - platform_set_drvdata(dev, u3d); - - u3d->dev = &dev->dev; - u3d->vbus = pdata->vbus; - - u3d->clk = clk_get(&dev->dev, NULL); - if (IS_ERR(u3d->clk)) { - retval = PTR_ERR(u3d->clk); - goto err_get_clk; - } - - r = platform_get_resource_byname(dev, IORESOURCE_MEM, "capregs"); - if (!r) { - dev_err(&dev->dev, "no I/O memory resource defined\n"); - retval = -ENODEV; - goto err_get_cap_regs; - } - - u3d->cap_regs = (struct mv_u3d_cap_regs __iomem *) - ioremap(r->start, resource_size(r)); - if (!u3d->cap_regs) { - dev_err(&dev->dev, "failed to map I/O memory\n"); - retval = -EBUSY; - goto err_map_cap_regs; - } else { - dev_dbg(&dev->dev, "cap_regs address: 0x%lx/0x%lx\n", - (unsigned long) r->start, - (unsigned long) u3d->cap_regs); - } - - /* we will access controller register, so enable the u3d controller */ - retval = clk_enable(u3d->clk); - if (retval) { - dev_err(&dev->dev, "clk_enable error %d\n", retval); - goto err_u3d_enable; - } - - if (pdata->phy_init) { - retval = pdata->phy_init(u3d->phy_regs); - if (retval) { - dev_err(&dev->dev, "init phy error %d\n", retval); - clk_disable(u3d->clk); - goto err_phy_init; - } - } - - u3d->op_regs = (struct mv_u3d_op_regs __iomem *)(u3d->cap_regs - + MV_U3D_USB3_OP_REGS_OFFSET); - - u3d->vuc_regs = (struct mv_u3d_vuc_regs __iomem *)(u3d->cap_regs - + ioread32(&u3d->cap_regs->vuoff)); - - u3d->max_eps = 16; - - /* - * some platform will use usb to download image, it may not disconnect - * usb gadget before loading kernel. So first stop u3d here. - */ - mv_u3d_controller_stop(u3d); - iowrite32(0xFFFFFFFF, &u3d->vuc_regs->intrcause); - - if (pdata->phy_deinit) - pdata->phy_deinit(u3d->phy_regs); - clk_disable(u3d->clk); - - size = u3d->max_eps * sizeof(struct mv_u3d_ep_context) * 2; - size = (size + MV_U3D_EP_CONTEXT_ALIGNMENT - 1) - & ~(MV_U3D_EP_CONTEXT_ALIGNMENT - 1); - u3d->ep_context = dma_alloc_coherent(&dev->dev, size, - &u3d->ep_context_dma, GFP_KERNEL); - if (!u3d->ep_context) { - dev_err(&dev->dev, "allocate ep context memory failed\n"); - retval = -ENOMEM; - goto err_alloc_ep_context; - } - u3d->ep_context_size = size; - - /* create TRB dma_pool resource */ - u3d->trb_pool = dma_pool_create("u3d_trb", - &dev->dev, - sizeof(struct mv_u3d_trb_hw), - MV_U3D_TRB_ALIGNMENT, - MV_U3D_DMA_BOUNDARY); - - if (!u3d->trb_pool) { - retval = -ENOMEM; - goto err_alloc_trb_pool; - } - - size = u3d->max_eps * sizeof(struct mv_u3d_ep) * 2; - u3d->eps = kzalloc(size, GFP_KERNEL); - if (!u3d->eps) { - retval = -ENOMEM; - goto err_alloc_eps; - } - - /* initialize ep0 status request structure */ - u3d->status_req = kzalloc(sizeof(struct mv_u3d_req) + 8, GFP_KERNEL); - if (!u3d->status_req) { - retval = -ENOMEM; - goto err_alloc_status_req; - } - INIT_LIST_HEAD(&u3d->status_req->queue); - - /* allocate a small amount of memory to get valid address */ - u3d->status_req->req.buf = (char *)u3d->status_req - + sizeof(struct mv_u3d_req); - u3d->status_req->req.dma = virt_to_phys(u3d->status_req->req.buf); - - u3d->resume_state = USB_STATE_NOTATTACHED; - u3d->usb_state = USB_STATE_ATTACHED; - u3d->ep0_dir = MV_U3D_EP_DIR_OUT; - u3d->remote_wakeup = 0; - - r = platform_get_resource(dev, IORESOURCE_IRQ, 0); - if (!r) { - dev_err(&dev->dev, "no IRQ resource defined\n"); - retval = -ENODEV; - goto err_get_irq; - } - u3d->irq = r->start; - - /* initialize gadget structure */ - u3d->gadget.ops = &mv_u3d_ops; /* usb_gadget_ops */ - u3d->gadget.ep0 = &u3d->eps[1].ep; /* gadget ep0 */ - INIT_LIST_HEAD(&u3d->gadget.ep_list); /* ep_list */ - u3d->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ - - /* the "gadget" abstracts/virtualizes the controller */ - u3d->gadget.name = driver_name; /* gadget name */ - - mv_u3d_eps_init(u3d); - - if (request_irq(u3d->irq, mv_u3d_irq, - IRQF_SHARED, driver_name, u3d)) { - u3d->irq = 0; - dev_err(&dev->dev, "Request irq %d for u3d failed\n", - u3d->irq); - retval = -ENODEV; - goto err_request_irq; - } - - /* external vbus detection */ - if (u3d->vbus) { - u3d->clock_gating = 1; - dev_err(&dev->dev, "external vbus detection\n"); - } - - if (!u3d->clock_gating) - u3d->vbus_active = 1; - - /* enable usb3 controller vbus detection */ - u3d->vbus_valid_detect = 1; - - retval = usb_add_gadget_udc(&dev->dev, &u3d->gadget); - if (retval) - goto err_unregister; - - dev_dbg(&dev->dev, "successful probe usb3 device %s clock gating.\n", - u3d->clock_gating ? "with" : "without"); - - return 0; - -err_unregister: - free_irq(u3d->irq, u3d); -err_get_irq: -err_request_irq: - kfree(u3d->status_req); -err_alloc_status_req: - kfree(u3d->eps); -err_alloc_eps: - dma_pool_destroy(u3d->trb_pool); -err_alloc_trb_pool: - dma_free_coherent(&dev->dev, u3d->ep_context_size, - u3d->ep_context, u3d->ep_context_dma); -err_alloc_ep_context: -err_phy_init: -err_u3d_enable: - iounmap(u3d->cap_regs); -err_map_cap_regs: -err_get_cap_regs: - clk_put(u3d->clk); -err_get_clk: - kfree(u3d); -err_alloc_private: -err_pdata: - return retval; -} - -#ifdef CONFIG_PM_SLEEP -static int mv_u3d_suspend(struct device *dev) -{ - struct mv_u3d *u3d = dev_get_drvdata(dev); - - /* - * only cable is unplugged, usb can suspend. - * So do not care about clock_gating == 1, it is handled by - * vbus session. - */ - if (!u3d->clock_gating) { - mv_u3d_controller_stop(u3d); - - spin_lock_irq(&u3d->lock); - /* stop all usb activities */ - mv_u3d_stop_activity(u3d, u3d->driver); - spin_unlock_irq(&u3d->lock); - - mv_u3d_disable(u3d); - } - - return 0; -} - -static int mv_u3d_resume(struct device *dev) -{ - struct mv_u3d *u3d = dev_get_drvdata(dev); - int retval; - - if (!u3d->clock_gating) { - retval = mv_u3d_enable(u3d); - if (retval) - return retval; - - if (u3d->driver && u3d->softconnect) { - mv_u3d_controller_reset(u3d); - mv_u3d_ep0_reset(u3d); - mv_u3d_controller_start(u3d); - } - } - - return 0; -} -#endif - -static SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume); - -static void mv_u3d_shutdown(struct platform_device *dev) -{ - struct mv_u3d *u3d = platform_get_drvdata(dev); - u32 tmp; - - tmp = ioread32(&u3d->op_regs->usbcmd); - tmp &= ~MV_U3D_CMD_RUN_STOP; - iowrite32(tmp, &u3d->op_regs->usbcmd); -} - -static struct platform_driver mv_u3d_driver = { - .probe = mv_u3d_probe, - .remove = mv_u3d_remove, - .shutdown = mv_u3d_shutdown, - .driver = { - .name = "mv-u3d", - .pm = &mv_u3d_pm_ops, - }, -}; - -module_platform_driver(mv_u3d_driver); -MODULE_ALIAS("platform:mv-u3d"); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Yu Xu <yuxu@marvell.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/udc/mv_udc.h b/drivers/usb/gadget/udc/mv_udc.h deleted file mode 100644 index b3f759c0962c..000000000000 --- a/drivers/usb/gadget/udc/mv_udc.h +++ /dev/null @@ -1,309 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - */ - -#ifndef __MV_UDC_H -#define __MV_UDC_H - -#define VUSBHS_MAX_PORTS 8 - -#define DQH_ALIGNMENT 2048 -#define DTD_ALIGNMENT 64 -#define DMA_BOUNDARY 4096 - -#define EP_DIR_IN 1 -#define EP_DIR_OUT 0 - -#define DMA_ADDR_INVALID (~(dma_addr_t)0) - -#define EP0_MAX_PKT_SIZE 64 -/* ep0 transfer state */ -#define WAIT_FOR_SETUP 0 -#define DATA_STATE_XMIT 1 -#define DATA_STATE_NEED_ZLP 2 -#define WAIT_FOR_OUT_STATUS 3 -#define DATA_STATE_RECV 4 - -#define CAPLENGTH_MASK (0xff) -#define DCCPARAMS_DEN_MASK (0x1f) - -#define HCSPARAMS_PPC (0x10) - -/* Frame Index Register Bit Masks */ -#define USB_FRINDEX_MASKS 0x3fff - -/* Command Register Bit Masks */ -#define USBCMD_RUN_STOP (0x00000001) -#define USBCMD_CTRL_RESET (0x00000002) -#define USBCMD_SETUP_TRIPWIRE_SET (0x00002000) -#define USBCMD_SETUP_TRIPWIRE_CLEAR (~USBCMD_SETUP_TRIPWIRE_SET) - -#define USBCMD_ATDTW_TRIPWIRE_SET (0x00004000) -#define USBCMD_ATDTW_TRIPWIRE_CLEAR (~USBCMD_ATDTW_TRIPWIRE_SET) - -/* bit 15,3,2 are for frame list size */ -#define USBCMD_FRAME_SIZE_1024 (0x00000000) /* 000 */ -#define USBCMD_FRAME_SIZE_512 (0x00000004) /* 001 */ -#define USBCMD_FRAME_SIZE_256 (0x00000008) /* 010 */ -#define USBCMD_FRAME_SIZE_128 (0x0000000C) /* 011 */ -#define USBCMD_FRAME_SIZE_64 (0x00008000) /* 100 */ -#define USBCMD_FRAME_SIZE_32 (0x00008004) /* 101 */ -#define USBCMD_FRAME_SIZE_16 (0x00008008) /* 110 */ -#define USBCMD_FRAME_SIZE_8 (0x0000800C) /* 111 */ - -#define EPCTRL_TX_ALL_MASK (0xFFFF0000) -#define EPCTRL_RX_ALL_MASK (0x0000FFFF) - -#define EPCTRL_TX_DATA_TOGGLE_RST (0x00400000) -#define EPCTRL_TX_EP_STALL (0x00010000) -#define EPCTRL_RX_EP_STALL (0x00000001) -#define EPCTRL_RX_DATA_TOGGLE_RST (0x00000040) -#define EPCTRL_RX_ENABLE (0x00000080) -#define EPCTRL_TX_ENABLE (0x00800000) -#define EPCTRL_CONTROL (0x00000000) -#define EPCTRL_ISOCHRONOUS (0x00040000) -#define EPCTRL_BULK (0x00080000) -#define EPCTRL_INT (0x000C0000) -#define EPCTRL_TX_TYPE (0x000C0000) -#define EPCTRL_RX_TYPE (0x0000000C) -#define EPCTRL_DATA_TOGGLE_INHIBIT (0x00000020) -#define EPCTRL_TX_EP_TYPE_SHIFT (18) -#define EPCTRL_RX_EP_TYPE_SHIFT (2) - -#define EPCOMPLETE_MAX_ENDPOINTS (16) - -/* endpoint list address bit masks */ -#define USB_EP_LIST_ADDRESS_MASK 0xfffff800 - -#define PORTSCX_W1C_BITS 0x2a -#define PORTSCX_PORT_RESET 0x00000100 -#define PORTSCX_PORT_POWER 0x00001000 -#define PORTSCX_FORCE_FULL_SPEED_CONNECT 0x01000000 -#define PORTSCX_PAR_XCVR_SELECT 0xC0000000 -#define PORTSCX_PORT_FORCE_RESUME 0x00000040 -#define PORTSCX_PORT_SUSPEND 0x00000080 -#define PORTSCX_PORT_SPEED_FULL 0x00000000 -#define PORTSCX_PORT_SPEED_LOW 0x04000000 -#define PORTSCX_PORT_SPEED_HIGH 0x08000000 -#define PORTSCX_PORT_SPEED_MASK 0x0C000000 - -/* USB MODE Register Bit Masks */ -#define USBMODE_CTRL_MODE_IDLE 0x00000000 -#define USBMODE_CTRL_MODE_DEVICE 0x00000002 -#define USBMODE_CTRL_MODE_HOST 0x00000003 -#define USBMODE_CTRL_MODE_RSV 0x00000001 -#define USBMODE_SETUP_LOCK_OFF 0x00000008 -#define USBMODE_STREAM_DISABLE 0x00000010 - -/* USB STS Register Bit Masks */ -#define USBSTS_INT 0x00000001 -#define USBSTS_ERR 0x00000002 -#define USBSTS_PORT_CHANGE 0x00000004 -#define USBSTS_FRM_LST_ROLL 0x00000008 -#define USBSTS_SYS_ERR 0x00000010 -#define USBSTS_IAA 0x00000020 -#define USBSTS_RESET 0x00000040 -#define USBSTS_SOF 0x00000080 -#define USBSTS_SUSPEND 0x00000100 -#define USBSTS_HC_HALTED 0x00001000 -#define USBSTS_RCL 0x00002000 -#define USBSTS_PERIODIC_SCHEDULE 0x00004000 -#define USBSTS_ASYNC_SCHEDULE 0x00008000 - - -/* Interrupt Enable Register Bit Masks */ -#define USBINTR_INT_EN (0x00000001) -#define USBINTR_ERR_INT_EN (0x00000002) -#define USBINTR_PORT_CHANGE_DETECT_EN (0x00000004) - -#define USBINTR_ASYNC_ADV_AAE (0x00000020) -#define USBINTR_ASYNC_ADV_AAE_ENABLE (0x00000020) -#define USBINTR_ASYNC_ADV_AAE_DISABLE (0xFFFFFFDF) - -#define USBINTR_RESET_EN (0x00000040) -#define USBINTR_SOF_UFRAME_EN (0x00000080) -#define USBINTR_DEVICE_SUSPEND (0x00000100) - -#define USB_DEVICE_ADDRESS_MASK (0xfe000000) -#define USB_DEVICE_ADDRESS_BIT_SHIFT (25) - -struct mv_cap_regs { - u32 caplength_hciversion; - u32 hcsparams; /* HC structural parameters */ - u32 hccparams; /* HC Capability Parameters*/ - u32 reserved[5]; - u32 dciversion; /* DC version number and reserved 16 bits */ - u32 dccparams; /* DC Capability Parameters */ -}; - -struct mv_op_regs { - u32 usbcmd; /* Command register */ - u32 usbsts; /* Status register */ - u32 usbintr; /* Interrupt enable */ - u32 frindex; /* Frame index */ - u32 reserved1[1]; - u32 deviceaddr; /* Device Address */ - u32 eplistaddr; /* Endpoint List Address */ - u32 ttctrl; /* HOST TT status and control */ - u32 burstsize; /* Programmable Burst Size */ - u32 txfilltuning; /* Host Transmit Pre-Buffer Packet Tuning */ - u32 reserved[4]; - u32 epnak; /* Endpoint NAK */ - u32 epnaken; /* Endpoint NAK Enable */ - u32 configflag; /* Configured Flag register */ - u32 portsc[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */ - u32 otgsc; - u32 usbmode; /* USB Host/Device mode */ - u32 epsetupstat; /* Endpoint Setup Status */ - u32 epprime; /* Endpoint Initialize */ - u32 epflush; /* Endpoint De-initialize */ - u32 epstatus; /* Endpoint Status */ - u32 epcomplete; /* Endpoint Interrupt On Complete */ - u32 epctrlx[16]; /* Endpoint Control, where x = 0.. 15 */ - u32 mcr; /* Mux Control */ - u32 isr; /* Interrupt Status */ - u32 ier; /* Interrupt Enable */ -}; - -struct mv_udc { - struct usb_gadget gadget; - struct usb_gadget_driver *driver; - spinlock_t lock; - struct completion *done; - struct platform_device *dev; - int irq; - - struct mv_cap_regs __iomem *cap_regs; - struct mv_op_regs __iomem *op_regs; - void __iomem *phy_regs; - unsigned int max_eps; - struct mv_dqh *ep_dqh; - size_t ep_dqh_size; - dma_addr_t ep_dqh_dma; - - struct dma_pool *dtd_pool; - struct mv_ep *eps; - - struct mv_dtd *dtd_head; - struct mv_dtd *dtd_tail; - unsigned int dtd_entries; - - struct mv_req *status_req; - struct usb_ctrlrequest local_setup_buff; - - unsigned int resume_state; /* USB state to resume */ - unsigned int usb_state; /* USB current state */ - unsigned int ep0_state; /* Endpoint zero state */ - unsigned int ep0_dir; - - unsigned int dev_addr; - unsigned int test_mode; - - int errors; - unsigned softconnect:1, - vbus_active:1, - remote_wakeup:1, - softconnected:1, - force_fs:1, - clock_gating:1, - active:1, - stopped:1; /* stop bit is setted */ - - struct work_struct vbus_work; - struct workqueue_struct *qwork; - - struct usb_phy *transceiver; - - struct mv_usb_platform_data *pdata; - - /* some SOC has mutiple clock sources for USB*/ - struct clk *clk; -}; - -/* endpoint data structure */ -struct mv_ep { - struct usb_ep ep; - struct mv_udc *udc; - struct list_head queue; - struct mv_dqh *dqh; - u32 direction; - char name[14]; - unsigned stopped:1, - wedge:1, - ep_type:2, - ep_num:8; -}; - -/* request data structure */ -struct mv_req { - struct usb_request req; - struct mv_dtd *dtd, *head, *tail; - struct mv_ep *ep; - struct list_head queue; - unsigned int test_mode; - unsigned dtd_count; - unsigned mapped:1; -}; - -#define EP_QUEUE_HEAD_MULT_POS 30 -#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000 -#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16 -#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) -#define EP_QUEUE_HEAD_IOS 0x00008000 -#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001 -#define EP_QUEUE_HEAD_IOC 0x00008000 -#define EP_QUEUE_HEAD_MULTO 0x00000C00 -#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040 -#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080 -#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF -#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0 -#define EP_QUEUE_FRINDEX_MASK 0x000007FF -#define EP_MAX_LENGTH_TRANSFER 0x4000 - -struct mv_dqh { - /* Bits 16..26 Bit 15 is Interrupt On Setup */ - u32 max_packet_length; - u32 curr_dtd_ptr; /* Current dTD Pointer */ - u32 next_dtd_ptr; /* Next dTD Pointer */ - /* Total bytes (16..30), IOC (15), INT (8), STS (0-7) */ - u32 size_ioc_int_sts; - u32 buff_ptr0; /* Buffer pointer Page 0 (12-31) */ - u32 buff_ptr1; /* Buffer pointer Page 1 (12-31) */ - u32 buff_ptr2; /* Buffer pointer Page 2 (12-31) */ - u32 buff_ptr3; /* Buffer pointer Page 3 (12-31) */ - u32 buff_ptr4; /* Buffer pointer Page 4 (12-31) */ - u32 reserved1; - /* 8 bytes of setup data that follows the Setup PID */ - u8 setup_buffer[8]; - u32 reserved2[4]; -}; - - -#define DTD_NEXT_TERMINATE (0x00000001) -#define DTD_IOC (0x00008000) -#define DTD_STATUS_ACTIVE (0x00000080) -#define DTD_STATUS_HALTED (0x00000040) -#define DTD_STATUS_DATA_BUFF_ERR (0x00000020) -#define DTD_STATUS_TRANSACTION_ERR (0x00000008) -#define DTD_RESERVED_FIELDS (0x00007F00) -#define DTD_ERROR_MASK (0x68) -#define DTD_ADDR_MASK (0xFFFFFFE0) -#define DTD_PACKET_SIZE 0x7FFF0000 -#define DTD_LENGTH_BIT_POS (16) - -struct mv_dtd { - u32 dtd_next; - u32 size_ioc_sts; - u32 buff_ptr0; /* Buffer pointer Page 0 */ - u32 buff_ptr1; /* Buffer pointer Page 1 */ - u32 buff_ptr2; /* Buffer pointer Page 2 */ - u32 buff_ptr3; /* Buffer pointer Page 3 */ - u32 buff_ptr4; /* Buffer pointer Page 4 */ - u32 scratch_ptr; - /* 32 bytes */ - dma_addr_t td_dma; /* dma address for this td */ - struct mv_dtd *next_dtd_virt; -}; - -#endif diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c deleted file mode 100644 index ff103e6b3048..000000000000 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ /dev/null @@ -1,2426 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - * Author: Chao Xie <chao.xie@marvell.com> - * Neil Zhang <zhangwm@marvell.com> - */ - -#include <linux/module.h> -#include <linux/pci.h> -#include <linux/dma-mapping.h> -#include <linux/dmapool.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/err.h> -#include <linux/timer.h> -#include <linux/list.h> -#include <linux/interrupt.h> -#include <linux/moduleparam.h> -#include <linux/device.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> -#include <linux/usb/otg.h> -#include <linux/pm.h> -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/platform_data/mv_usb.h> -#include <linux/unaligned.h> - -#include "mv_udc.h" - -#define DRIVER_DESC "Marvell PXA USB Device Controller driver" - -#define ep_dir(ep) (((ep)->ep_num == 0) ? \ - ((ep)->udc->ep0_dir) : ((ep)->direction)) - -/* timeout value -- usec */ -#define RESET_TIMEOUT 10000 -#define FLUSH_TIMEOUT 10000 -#define EPSTATUS_TIMEOUT 10000 -#define PRIME_TIMEOUT 10000 -#define READSAFE_TIMEOUT 1000 - -#define LOOPS_USEC_SHIFT 1 -#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT) -#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT) - -static DECLARE_COMPLETION(release_done); - -static const char driver_name[] = "mv_udc"; - -static void nuke(struct mv_ep *ep, int status); -static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver); - -/* for endpoint 0 operations */ -static const struct usb_endpoint_descriptor mv_ep0_desc = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0, - .bmAttributes = USB_ENDPOINT_XFER_CONTROL, - .wMaxPacketSize = EP0_MAX_PKT_SIZE, -}; - -static void ep0_reset(struct mv_udc *udc) -{ - struct mv_ep *ep; - u32 epctrlx; - int i = 0; - - /* ep0 in and out */ - for (i = 0; i < 2; i++) { - ep = &udc->eps[i]; - ep->udc = udc; - - /* ep0 dQH */ - ep->dqh = &udc->ep_dqh[i]; - - /* configure ep0 endpoint capabilities in dQH */ - ep->dqh->max_packet_length = - (EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) - | EP_QUEUE_HEAD_IOS; - - ep->dqh->next_dtd_ptr = EP_QUEUE_HEAD_NEXT_TERMINATE; - - epctrlx = readl(&udc->op_regs->epctrlx[0]); - if (i) { /* TX */ - epctrlx |= EPCTRL_TX_ENABLE - | (USB_ENDPOINT_XFER_CONTROL - << EPCTRL_TX_EP_TYPE_SHIFT); - - } else { /* RX */ - epctrlx |= EPCTRL_RX_ENABLE - | (USB_ENDPOINT_XFER_CONTROL - << EPCTRL_RX_EP_TYPE_SHIFT); - } - - writel(epctrlx, &udc->op_regs->epctrlx[0]); - } -} - -/* protocol ep0 stall, will automatically be cleared on new transaction */ -static void ep0_stall(struct mv_udc *udc) -{ - u32 epctrlx; - - /* set TX and RX to stall */ - epctrlx = readl(&udc->op_regs->epctrlx[0]); - epctrlx |= EPCTRL_RX_EP_STALL | EPCTRL_TX_EP_STALL; - writel(epctrlx, &udc->op_regs->epctrlx[0]); - - /* update ep0 state */ - udc->ep0_state = WAIT_FOR_SETUP; - udc->ep0_dir = EP_DIR_OUT; -} - -static int process_ep_req(struct mv_udc *udc, int index, - struct mv_req *curr_req) -{ - struct mv_dtd *curr_dtd; - struct mv_dqh *curr_dqh; - int actual, remaining_length; - int i, direction; - int retval = 0; - u32 errors; - u32 bit_pos; - - curr_dqh = &udc->ep_dqh[index]; - direction = index % 2; - - curr_dtd = curr_req->head; - actual = curr_req->req.length; - - for (i = 0; i < curr_req->dtd_count; i++) { - if (curr_dtd->size_ioc_sts & DTD_STATUS_ACTIVE) { - dev_dbg(&udc->dev->dev, "%s, dTD not completed\n", - udc->eps[index].name); - return 1; - } - - errors = curr_dtd->size_ioc_sts & DTD_ERROR_MASK; - if (!errors) { - remaining_length = - (curr_dtd->size_ioc_sts & DTD_PACKET_SIZE) - >> DTD_LENGTH_BIT_POS; - actual -= remaining_length; - - if (remaining_length) { - if (direction) { - dev_dbg(&udc->dev->dev, - "TX dTD remains data\n"); - retval = -EPROTO; - break; - } else - break; - } - } else { - dev_info(&udc->dev->dev, - "complete_tr error: ep=%d %s: error = 0x%x\n", - index >> 1, direction ? "SEND" : "RECV", - errors); - if (errors & DTD_STATUS_HALTED) { - /* Clear the errors and Halt condition */ - curr_dqh->size_ioc_int_sts &= ~errors; - retval = -EPIPE; - } else if (errors & DTD_STATUS_DATA_BUFF_ERR) { - retval = -EPROTO; - } else if (errors & DTD_STATUS_TRANSACTION_ERR) { - retval = -EILSEQ; - } - } - if (i != curr_req->dtd_count - 1) - curr_dtd = (struct mv_dtd *)curr_dtd->next_dtd_virt; - } - if (retval) - return retval; - - if (direction == EP_DIR_OUT) - bit_pos = 1 << curr_req->ep->ep_num; - else - bit_pos = 1 << (16 + curr_req->ep->ep_num); - - while (curr_dqh->curr_dtd_ptr == curr_dtd->td_dma) { - if (curr_dtd->dtd_next == EP_QUEUE_HEAD_NEXT_TERMINATE) { - while (readl(&udc->op_regs->epstatus) & bit_pos) - udelay(1); - break; - } - udelay(1); - } - - curr_req->req.actual = actual; - - return 0; -} - -/* - * done() - retire a request; caller blocked irqs - * @status : request status to be set, only works when - * request is still in progress. - */ -static void done(struct mv_ep *ep, struct mv_req *req, int status) - __releases(&ep->udc->lock) - __acquires(&ep->udc->lock) -{ - struct mv_udc *udc = NULL; - unsigned char stopped = ep->stopped; - struct mv_dtd *curr_td, *next_td; - int j; - - udc = (struct mv_udc *)ep->udc; - /* Removed the req from fsl_ep->queue */ - list_del_init(&req->queue); - - /* req.status should be set as -EINPROGRESS in ep_queue() */ - if (req->req.status == -EINPROGRESS) - req->req.status = status; - else - status = req->req.status; - - /* Free dtd for the request */ - next_td = req->head; - for (j = 0; j < req->dtd_count; j++) { - curr_td = next_td; - if (j != req->dtd_count - 1) - next_td = curr_td->next_dtd_virt; - dma_pool_free(udc->dtd_pool, curr_td, curr_td->td_dma); - } - - usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep)); - - if (status && (status != -ESHUTDOWN)) - dev_info(&udc->dev->dev, "complete %s req %p stat %d len %u/%u", - ep->ep.name, &req->req, status, - req->req.actual, req->req.length); - - ep->stopped = 1; - - spin_unlock(&ep->udc->lock); - - usb_gadget_giveback_request(&ep->ep, &req->req); - - spin_lock(&ep->udc->lock); - ep->stopped = stopped; -} - -static int queue_dtd(struct mv_ep *ep, struct mv_req *req) -{ - struct mv_udc *udc; - struct mv_dqh *dqh; - u32 bit_pos, direction; - u32 usbcmd, epstatus; - unsigned int loops; - int retval = 0; - - udc = ep->udc; - direction = ep_dir(ep); - dqh = &(udc->ep_dqh[ep->ep_num * 2 + direction]); - bit_pos = 1 << (((direction == EP_DIR_OUT) ? 0 : 16) + ep->ep_num); - - /* check if the pipe is empty */ - if (!(list_empty(&ep->queue))) { - struct mv_req *lastreq; - lastreq = list_entry(ep->queue.prev, struct mv_req, queue); - lastreq->tail->dtd_next = - req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; - - wmb(); - - if (readl(&udc->op_regs->epprime) & bit_pos) - goto done; - - loops = LOOPS(READSAFE_TIMEOUT); - while (1) { - /* start with setting the semaphores */ - usbcmd = readl(&udc->op_regs->usbcmd); - usbcmd |= USBCMD_ATDTW_TRIPWIRE_SET; - writel(usbcmd, &udc->op_regs->usbcmd); - - /* read the endpoint status */ - epstatus = readl(&udc->op_regs->epstatus) & bit_pos; - - /* - * Reread the ATDTW semaphore bit to check if it is - * cleared. When hardware see a hazard, it will clear - * the bit or else we remain set to 1 and we can - * proceed with priming of endpoint if not already - * primed. - */ - if (readl(&udc->op_regs->usbcmd) - & USBCMD_ATDTW_TRIPWIRE_SET) - break; - - loops--; - if (loops == 0) { - dev_err(&udc->dev->dev, - "Timeout for ATDTW_TRIPWIRE...\n"); - retval = -ETIME; - goto done; - } - udelay(LOOPS_USEC); - } - - /* Clear the semaphore */ - usbcmd = readl(&udc->op_regs->usbcmd); - usbcmd &= USBCMD_ATDTW_TRIPWIRE_CLEAR; - writel(usbcmd, &udc->op_regs->usbcmd); - - if (epstatus) - goto done; - } - - /* Write dQH next pointer and terminate bit to 0 */ - dqh->next_dtd_ptr = req->head->td_dma - & EP_QUEUE_HEAD_NEXT_POINTER_MASK; - - /* clear active and halt bit, in case set from a previous error */ - dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED); - - /* Ensure that updates to the QH will occur before priming. */ - wmb(); - - /* Prime the Endpoint */ - writel(bit_pos, &udc->op_regs->epprime); - -done: - return retval; -} - -static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length, - dma_addr_t *dma, int *is_last) -{ - struct mv_dtd *dtd; - struct mv_udc *udc; - struct mv_dqh *dqh; - u32 temp, mult = 0; - - /* how big will this transfer be? */ - if (usb_endpoint_xfer_isoc(req->ep->ep.desc)) { - dqh = req->ep->dqh; - mult = (dqh->max_packet_length >> EP_QUEUE_HEAD_MULT_POS) - & 0x3; - *length = min(req->req.length - req->req.actual, - (unsigned)(mult * req->ep->ep.maxpacket)); - } else - *length = min(req->req.length - req->req.actual, - (unsigned)EP_MAX_LENGTH_TRANSFER); - - udc = req->ep->udc; - - /* - * Be careful that no _GFP_HIGHMEM is set, - * or we can not use dma_to_virt - */ - dtd = dma_pool_alloc(udc->dtd_pool, GFP_ATOMIC, dma); - if (dtd == NULL) - return dtd; - - dtd->td_dma = *dma; - /* initialize buffer page pointers */ - temp = (u32)(req->req.dma + req->req.actual); - dtd->buff_ptr0 = cpu_to_le32(temp); - temp &= ~0xFFF; - dtd->buff_ptr1 = cpu_to_le32(temp + 0x1000); - dtd->buff_ptr2 = cpu_to_le32(temp + 0x2000); - dtd->buff_ptr3 = cpu_to_le32(temp + 0x3000); - dtd->buff_ptr4 = cpu_to_le32(temp + 0x4000); - - req->req.actual += *length; - - /* zlp is needed if req->req.zero is set */ - if (req->req.zero) { - if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) - *is_last = 1; - else - *is_last = 0; - } else if (req->req.length == req->req.actual) - *is_last = 1; - else - *is_last = 0; - - /* Fill in the transfer size; set active bit */ - temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); - - /* Enable interrupt for the last dtd of a request */ - if (*is_last && !req->req.no_interrupt) - temp |= DTD_IOC; - - temp |= mult << 10; - - dtd->size_ioc_sts = temp; - - mb(); - - return dtd; -} - -/* generate dTD linked list for a request */ -static int req_to_dtd(struct mv_req *req) -{ - unsigned count; - int is_last, is_first = 1; - struct mv_dtd *dtd, *last_dtd = NULL; - dma_addr_t dma; - - do { - dtd = build_dtd(req, &count, &dma, &is_last); - if (dtd == NULL) - return -ENOMEM; - - if (is_first) { - is_first = 0; - req->head = dtd; - } else { - last_dtd->dtd_next = dma; - last_dtd->next_dtd_virt = dtd; - } - last_dtd = dtd; - req->dtd_count++; - } while (!is_last); - - /* set terminate bit to 1 for the last dTD */ - dtd->dtd_next = DTD_NEXT_TERMINATE; - - req->tail = dtd; - - return 0; -} - -static int mv_ep_enable(struct usb_ep *_ep, - const struct usb_endpoint_descriptor *desc) -{ - struct mv_udc *udc; - struct mv_ep *ep; - struct mv_dqh *dqh; - u16 max = 0; - u32 bit_pos, epctrlx, direction; - const unsigned char zlt = 1; - unsigned char ios, mult; - unsigned long flags; - - ep = container_of(_ep, struct mv_ep, ep); - udc = ep->udc; - - if (!_ep || !desc - || desc->bDescriptorType != USB_DT_ENDPOINT) - return -EINVAL; - - if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - direction = ep_dir(ep); - max = usb_endpoint_maxp(desc); - - /* - * disable HW zero length termination select - * driver handles zero length packet through req->req.zero - */ - bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); - - /* Check if the Endpoint is Primed */ - if ((readl(&udc->op_regs->epprime) & bit_pos) - || (readl(&udc->op_regs->epstatus) & bit_pos)) { - dev_info(&udc->dev->dev, - "ep=%d %s: Init ERROR: ENDPTPRIME=0x%x," - " ENDPTSTATUS=0x%x, bit_pos=0x%x\n", - (unsigned)ep->ep_num, direction ? "SEND" : "RECV", - (unsigned)readl(&udc->op_regs->epprime), - (unsigned)readl(&udc->op_regs->epstatus), - (unsigned)bit_pos); - goto en_done; - } - - /* Set the max packet length, interrupt on Setup and Mult fields */ - ios = 0; - mult = 0; - switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { - case USB_ENDPOINT_XFER_BULK: - case USB_ENDPOINT_XFER_INT: - break; - case USB_ENDPOINT_XFER_CONTROL: - ios = 1; - break; - case USB_ENDPOINT_XFER_ISOC: - /* Calculate transactions needed for high bandwidth iso */ - mult = usb_endpoint_maxp_mult(desc); - /* 3 transactions at most */ - if (mult > 3) - goto en_done; - break; - default: - goto en_done; - } - - spin_lock_irqsave(&udc->lock, flags); - /* Get the endpoint queue head address */ - dqh = ep->dqh; - dqh->max_packet_length = (max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) - | (mult << EP_QUEUE_HEAD_MULT_POS) - | (zlt ? EP_QUEUE_HEAD_ZLT_SEL : 0) - | (ios ? EP_QUEUE_HEAD_IOS : 0); - dqh->next_dtd_ptr = 1; - dqh->size_ioc_int_sts = 0; - - ep->ep.maxpacket = max; - ep->ep.desc = desc; - ep->stopped = 0; - - /* Enable the endpoint for Rx or Tx and set the endpoint type */ - epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); - if (direction == EP_DIR_IN) { - epctrlx &= ~EPCTRL_TX_ALL_MASK; - epctrlx |= EPCTRL_TX_ENABLE | EPCTRL_TX_DATA_TOGGLE_RST - | ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - << EPCTRL_TX_EP_TYPE_SHIFT); - } else { - epctrlx &= ~EPCTRL_RX_ALL_MASK; - epctrlx |= EPCTRL_RX_ENABLE | EPCTRL_RX_DATA_TOGGLE_RST - | ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - << EPCTRL_RX_EP_TYPE_SHIFT); - } - writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); - - /* - * Implement Guideline (GL# USB-7) The unused endpoint type must - * be programmed to bulk. - */ - epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); - if ((epctrlx & EPCTRL_RX_ENABLE) == 0) { - epctrlx |= (USB_ENDPOINT_XFER_BULK - << EPCTRL_RX_EP_TYPE_SHIFT); - writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); - } - - epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); - if ((epctrlx & EPCTRL_TX_ENABLE) == 0) { - epctrlx |= (USB_ENDPOINT_XFER_BULK - << EPCTRL_TX_EP_TYPE_SHIFT); - writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); - } - - spin_unlock_irqrestore(&udc->lock, flags); - - return 0; -en_done: - return -EINVAL; -} - -static int mv_ep_disable(struct usb_ep *_ep) -{ - struct mv_udc *udc; - struct mv_ep *ep; - struct mv_dqh *dqh; - u32 epctrlx, direction; - unsigned long flags; - - ep = container_of(_ep, struct mv_ep, ep); - if ((_ep == NULL) || !ep->ep.desc) - return -EINVAL; - - udc = ep->udc; - - /* Get the endpoint queue head address */ - dqh = ep->dqh; - - spin_lock_irqsave(&udc->lock, flags); - - direction = ep_dir(ep); - - /* Reset the max packet length and the interrupt on Setup */ - dqh->max_packet_length = 0; - - /* Disable the endpoint for Rx or Tx and reset the endpoint type */ - epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); - epctrlx &= ~((direction == EP_DIR_IN) - ? (EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE) - : (EPCTRL_RX_ENABLE | EPCTRL_RX_TYPE)); - writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); - - /* nuke all pending requests (does flush) */ - nuke(ep, -ESHUTDOWN); - - ep->ep.desc = NULL; - ep->stopped = 1; - - spin_unlock_irqrestore(&udc->lock, flags); - - return 0; -} - -static struct usb_request * -mv_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) -{ - struct mv_req *req; - - req = kzalloc(sizeof *req, gfp_flags); - if (!req) - return NULL; - - req->req.dma = DMA_ADDR_INVALID; - INIT_LIST_HEAD(&req->queue); - - return &req->req; -} - -static void mv_free_request(struct usb_ep *_ep, struct usb_request *_req) -{ - struct mv_req *req = NULL; - - req = container_of(_req, struct mv_req, req); - - if (_req) - kfree(req); -} - -static void mv_ep_fifo_flush(struct usb_ep *_ep) -{ - struct mv_udc *udc; - u32 bit_pos, direction; - struct mv_ep *ep; - unsigned int loops; - - if (!_ep) - return; - - ep = container_of(_ep, struct mv_ep, ep); - if (!ep->ep.desc) - return; - - udc = ep->udc; - direction = ep_dir(ep); - - if (ep->ep_num == 0) - bit_pos = (1 << 16) | 1; - else if (direction == EP_DIR_OUT) - bit_pos = 1 << ep->ep_num; - else - bit_pos = 1 << (16 + ep->ep_num); - - loops = LOOPS(EPSTATUS_TIMEOUT); - do { - unsigned int inter_loops; - - if (loops == 0) { - dev_err(&udc->dev->dev, - "TIMEOUT for ENDPTSTATUS=0x%x, bit_pos=0x%x\n", - (unsigned)readl(&udc->op_regs->epstatus), - (unsigned)bit_pos); - return; - } - /* Write 1 to the Flush register */ - writel(bit_pos, &udc->op_regs->epflush); - - /* Wait until flushing completed */ - inter_loops = LOOPS(FLUSH_TIMEOUT); - while (readl(&udc->op_regs->epflush)) { - /* - * ENDPTFLUSH bit should be cleared to indicate this - * operation is complete - */ - if (inter_loops == 0) { - dev_err(&udc->dev->dev, - "TIMEOUT for ENDPTFLUSH=0x%x," - "bit_pos=0x%x\n", - (unsigned)readl(&udc->op_regs->epflush), - (unsigned)bit_pos); - return; - } - inter_loops--; - udelay(LOOPS_USEC); - } - loops--; - } while (readl(&udc->op_regs->epstatus) & bit_pos); -} - -/* queues (submits) an I/O request to an endpoint */ -static int -mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) -{ - struct mv_ep *ep = container_of(_ep, struct mv_ep, ep); - struct mv_req *req = container_of(_req, struct mv_req, req); - struct mv_udc *udc = ep->udc; - unsigned long flags; - int retval; - - /* catch various bogus parameters */ - if (!_req || !req->req.complete || !req->req.buf - || !list_empty(&req->queue)) { - dev_err(&udc->dev->dev, "%s, bad params", __func__); - return -EINVAL; - } - if (unlikely(!_ep || !ep->ep.desc)) { - dev_err(&udc->dev->dev, "%s, bad ep", __func__); - return -EINVAL; - } - - udc = ep->udc; - if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - req->ep = ep; - - /* map virtual address to hardware */ - retval = usb_gadget_map_request(&udc->gadget, _req, ep_dir(ep)); - if (retval) - return retval; - - req->req.status = -EINPROGRESS; - req->req.actual = 0; - req->dtd_count = 0; - - spin_lock_irqsave(&udc->lock, flags); - - /* build dtds and push them to device queue */ - if (!req_to_dtd(req)) { - retval = queue_dtd(ep, req); - if (retval) { - spin_unlock_irqrestore(&udc->lock, flags); - dev_err(&udc->dev->dev, "Failed to queue dtd\n"); - goto err_unmap_dma; - } - } else { - spin_unlock_irqrestore(&udc->lock, flags); - dev_err(&udc->dev->dev, "Failed to dma_pool_alloc\n"); - retval = -ENOMEM; - goto err_unmap_dma; - } - - /* Update ep0 state */ - if (ep->ep_num == 0) - udc->ep0_state = DATA_STATE_XMIT; - - /* irq handler advances the queue */ - list_add_tail(&req->queue, &ep->queue); - spin_unlock_irqrestore(&udc->lock, flags); - - return 0; - -err_unmap_dma: - usb_gadget_unmap_request(&udc->gadget, _req, ep_dir(ep)); - - return retval; -} - -static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req) -{ - struct mv_dqh *dqh = ep->dqh; - u32 bit_pos; - - /* Write dQH next pointer and terminate bit to 0 */ - dqh->next_dtd_ptr = req->head->td_dma - & EP_QUEUE_HEAD_NEXT_POINTER_MASK; - - /* clear active and halt bit, in case set from a previous error */ - dqh->size_ioc_int_sts &= ~(DTD_STATUS_ACTIVE | DTD_STATUS_HALTED); - - /* Ensure that updates to the QH will occure before priming. */ - wmb(); - - bit_pos = 1 << (((ep_dir(ep) == EP_DIR_OUT) ? 0 : 16) + ep->ep_num); - - /* Prime the Endpoint */ - writel(bit_pos, &ep->udc->op_regs->epprime); -} - -/* dequeues (cancels, unlinks) an I/O request from an endpoint */ -static int mv_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) -{ - struct mv_ep *ep = container_of(_ep, struct mv_ep, ep); - struct mv_req *req = NULL, *iter; - struct mv_udc *udc = ep->udc; - unsigned long flags; - int stopped, ret = 0; - u32 epctrlx; - - if (!_ep || !_req) - return -EINVAL; - - spin_lock_irqsave(&ep->udc->lock, flags); - stopped = ep->stopped; - - /* Stop the ep before we deal with the queue */ - ep->stopped = 1; - epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); - if (ep_dir(ep) == EP_DIR_IN) - epctrlx &= ~EPCTRL_TX_ENABLE; - else - epctrlx &= ~EPCTRL_RX_ENABLE; - writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); - - /* make sure it's actually queued on this endpoint */ - list_for_each_entry(iter, &ep->queue, queue) { - if (&iter->req != _req) - continue; - req = iter; - break; - } - if (!req) { - ret = -EINVAL; - goto out; - } - - /* The request is in progress, or completed but not dequeued */ - if (ep->queue.next == &req->queue) { - _req->status = -ECONNRESET; - mv_ep_fifo_flush(_ep); /* flush current transfer */ - - /* The request isn't the last request in this ep queue */ - if (req->queue.next != &ep->queue) { - struct mv_req *next_req; - - next_req = list_entry(req->queue.next, - struct mv_req, queue); - - /* Point the QH to the first TD of next request */ - mv_prime_ep(ep, next_req); - } else { - struct mv_dqh *qh; - - qh = ep->dqh; - qh->next_dtd_ptr = 1; - qh->size_ioc_int_sts = 0; - } - - /* The request hasn't been processed, patch up the TD chain */ - } else { - struct mv_req *prev_req; - - prev_req = list_entry(req->queue.prev, struct mv_req, queue); - writel(readl(&req->tail->dtd_next), - &prev_req->tail->dtd_next); - - } - - done(ep, req, -ECONNRESET); - - /* Enable EP */ -out: - epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); - if (ep_dir(ep) == EP_DIR_IN) - epctrlx |= EPCTRL_TX_ENABLE; - else - epctrlx |= EPCTRL_RX_ENABLE; - writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); - ep->stopped = stopped; - - spin_unlock_irqrestore(&ep->udc->lock, flags); - return ret; -} - -static void ep_set_stall(struct mv_udc *udc, u8 ep_num, u8 direction, int stall) -{ - u32 epctrlx; - - epctrlx = readl(&udc->op_regs->epctrlx[ep_num]); - - if (stall) { - if (direction == EP_DIR_IN) - epctrlx |= EPCTRL_TX_EP_STALL; - else - epctrlx |= EPCTRL_RX_EP_STALL; - } else { - if (direction == EP_DIR_IN) { - epctrlx &= ~EPCTRL_TX_EP_STALL; - epctrlx |= EPCTRL_TX_DATA_TOGGLE_RST; - } else { - epctrlx &= ~EPCTRL_RX_EP_STALL; - epctrlx |= EPCTRL_RX_DATA_TOGGLE_RST; - } - } - writel(epctrlx, &udc->op_regs->epctrlx[ep_num]); -} - -static int ep_is_stall(struct mv_udc *udc, u8 ep_num, u8 direction) -{ - u32 epctrlx; - - epctrlx = readl(&udc->op_regs->epctrlx[ep_num]); - - if (direction == EP_DIR_OUT) - return (epctrlx & EPCTRL_RX_EP_STALL) ? 1 : 0; - else - return (epctrlx & EPCTRL_TX_EP_STALL) ? 1 : 0; -} - -static int mv_ep_set_halt_wedge(struct usb_ep *_ep, int halt, int wedge) -{ - struct mv_ep *ep; - unsigned long flags; - int status = 0; - struct mv_udc *udc; - - ep = container_of(_ep, struct mv_ep, ep); - udc = ep->udc; - if (!_ep || !ep->ep.desc) { - status = -EINVAL; - goto out; - } - - if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { - status = -EOPNOTSUPP; - goto out; - } - - /* - * Attempt to halt IN ep will fail if any transfer requests - * are still queue - */ - if (halt && (ep_dir(ep) == EP_DIR_IN) && !list_empty(&ep->queue)) { - status = -EAGAIN; - goto out; - } - - spin_lock_irqsave(&ep->udc->lock, flags); - ep_set_stall(udc, ep->ep_num, ep_dir(ep), halt); - if (halt && wedge) - ep->wedge = 1; - else if (!halt) - ep->wedge = 0; - spin_unlock_irqrestore(&ep->udc->lock, flags); - - if (ep->ep_num == 0) { - udc->ep0_state = WAIT_FOR_SETUP; - udc->ep0_dir = EP_DIR_OUT; - } -out: - return status; -} - -static int mv_ep_set_halt(struct usb_ep *_ep, int halt) -{ - return mv_ep_set_halt_wedge(_ep, halt, 0); -} - -static int mv_ep_set_wedge(struct usb_ep *_ep) -{ - return mv_ep_set_halt_wedge(_ep, 1, 1); -} - -static const struct usb_ep_ops mv_ep_ops = { - .enable = mv_ep_enable, - .disable = mv_ep_disable, - - .alloc_request = mv_alloc_request, - .free_request = mv_free_request, - - .queue = mv_ep_queue, - .dequeue = mv_ep_dequeue, - - .set_wedge = mv_ep_set_wedge, - .set_halt = mv_ep_set_halt, - .fifo_flush = mv_ep_fifo_flush, /* flush fifo */ -}; - -static int udc_clock_enable(struct mv_udc *udc) -{ - return clk_prepare_enable(udc->clk); -} - -static void udc_clock_disable(struct mv_udc *udc) -{ - clk_disable_unprepare(udc->clk); -} - -static void udc_stop(struct mv_udc *udc) -{ - u32 tmp; - - /* Disable interrupts */ - tmp = readl(&udc->op_regs->usbintr); - tmp &= ~(USBINTR_INT_EN | USBINTR_ERR_INT_EN | - USBINTR_PORT_CHANGE_DETECT_EN | USBINTR_RESET_EN); - writel(tmp, &udc->op_regs->usbintr); - - udc->stopped = 1; - - /* Reset the Run the bit in the command register to stop VUSB */ - tmp = readl(&udc->op_regs->usbcmd); - tmp &= ~USBCMD_RUN_STOP; - writel(tmp, &udc->op_regs->usbcmd); -} - -static void udc_start(struct mv_udc *udc) -{ - u32 usbintr; - - usbintr = USBINTR_INT_EN | USBINTR_ERR_INT_EN - | USBINTR_PORT_CHANGE_DETECT_EN - | USBINTR_RESET_EN | USBINTR_DEVICE_SUSPEND; - /* Enable interrupts */ - writel(usbintr, &udc->op_regs->usbintr); - - udc->stopped = 0; - - /* Set the Run bit in the command register */ - writel(USBCMD_RUN_STOP, &udc->op_regs->usbcmd); -} - -static int udc_reset(struct mv_udc *udc) -{ - unsigned int loops; - u32 tmp, portsc; - - /* Stop the controller */ - tmp = readl(&udc->op_regs->usbcmd); - tmp &= ~USBCMD_RUN_STOP; - writel(tmp, &udc->op_regs->usbcmd); - - /* Reset the controller to get default values */ - writel(USBCMD_CTRL_RESET, &udc->op_regs->usbcmd); - - /* wait for reset to complete */ - loops = LOOPS(RESET_TIMEOUT); - while (readl(&udc->op_regs->usbcmd) & USBCMD_CTRL_RESET) { - if (loops == 0) { - dev_err(&udc->dev->dev, - "Wait for RESET completed TIMEOUT\n"); - return -ETIMEDOUT; - } - loops--; - udelay(LOOPS_USEC); - } - - /* set controller to device mode */ - tmp = readl(&udc->op_regs->usbmode); - tmp |= USBMODE_CTRL_MODE_DEVICE; - - /* turn setup lockout off, require setup tripwire in usbcmd */ - tmp |= USBMODE_SETUP_LOCK_OFF; - - writel(tmp, &udc->op_regs->usbmode); - - writel(0x0, &udc->op_regs->epsetupstat); - - /* Configure the Endpoint List Address */ - writel(udc->ep_dqh_dma & USB_EP_LIST_ADDRESS_MASK, - &udc->op_regs->eplistaddr); - - portsc = readl(&udc->op_regs->portsc[0]); - if (readl(&udc->cap_regs->hcsparams) & HCSPARAMS_PPC) - portsc &= (~PORTSCX_W1C_BITS | ~PORTSCX_PORT_POWER); - - if (udc->force_fs) - portsc |= PORTSCX_FORCE_FULL_SPEED_CONNECT; - else - portsc &= (~PORTSCX_FORCE_FULL_SPEED_CONNECT); - - writel(portsc, &udc->op_regs->portsc[0]); - - tmp = readl(&udc->op_regs->epctrlx[0]); - tmp &= ~(EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL); - writel(tmp, &udc->op_regs->epctrlx[0]); - - return 0; -} - -static int mv_udc_enable_internal(struct mv_udc *udc) -{ - int retval; - - if (udc->active) - return 0; - - dev_dbg(&udc->dev->dev, "enable udc\n"); - retval = udc_clock_enable(udc); - if (retval) - return retval; - - if (udc->pdata->phy_init) { - retval = udc->pdata->phy_init(udc->phy_regs); - if (retval) { - dev_err(&udc->dev->dev, - "init phy error %d\n", retval); - udc_clock_disable(udc); - return retval; - } - } - udc->active = 1; - - return 0; -} - -static int mv_udc_enable(struct mv_udc *udc) -{ - if (udc->clock_gating) - return mv_udc_enable_internal(udc); - - return 0; -} - -static void mv_udc_disable_internal(struct mv_udc *udc) -{ - if (udc->active) { - dev_dbg(&udc->dev->dev, "disable udc\n"); - if (udc->pdata->phy_deinit) - udc->pdata->phy_deinit(udc->phy_regs); - udc_clock_disable(udc); - udc->active = 0; - } -} - -static void mv_udc_disable(struct mv_udc *udc) -{ - if (udc->clock_gating) - mv_udc_disable_internal(udc); -} - -static int mv_udc_get_frame(struct usb_gadget *gadget) -{ - struct mv_udc *udc; - u16 retval; - - if (!gadget) - return -ENODEV; - - udc = container_of(gadget, struct mv_udc, gadget); - - retval = readl(&udc->op_regs->frindex) & USB_FRINDEX_MASKS; - - return retval; -} - -/* Tries to wake up the host connected to this gadget */ -static int mv_udc_wakeup(struct usb_gadget *gadget) -{ - struct mv_udc *udc = container_of(gadget, struct mv_udc, gadget); - u32 portsc; - - /* Remote wakeup feature not enabled by host */ - if (!udc->remote_wakeup) - return -ENOTSUPP; - - portsc = readl(&udc->op_regs->portsc); - /* not suspended? */ - if (!(portsc & PORTSCX_PORT_SUSPEND)) - return 0; - /* trigger force resume */ - portsc |= PORTSCX_PORT_FORCE_RESUME; - writel(portsc, &udc->op_regs->portsc[0]); - return 0; -} - -static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active) -{ - struct mv_udc *udc; - unsigned long flags; - int retval = 0; - - udc = container_of(gadget, struct mv_udc, gadget); - spin_lock_irqsave(&udc->lock, flags); - - udc->vbus_active = (is_active != 0); - - dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n", - __func__, udc->softconnect, udc->vbus_active); - - if (udc->driver && udc->softconnect && udc->vbus_active) { - retval = mv_udc_enable(udc); - if (retval == 0) { - /* Clock is disabled, need re-init registers */ - udc_reset(udc); - ep0_reset(udc); - udc_start(udc); - } - } else if (udc->driver && udc->softconnect) { - if (!udc->active) - goto out; - - /* stop all the transfer in queue*/ - stop_activity(udc, udc->driver); - udc_stop(udc); - mv_udc_disable(udc); - } - -out: - spin_unlock_irqrestore(&udc->lock, flags); - return retval; -} - -static int mv_udc_pullup(struct usb_gadget *gadget, int is_on) -{ - struct mv_udc *udc; - unsigned long flags; - int retval = 0; - - udc = container_of(gadget, struct mv_udc, gadget); - spin_lock_irqsave(&udc->lock, flags); - - udc->softconnect = (is_on != 0); - - dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n", - __func__, udc->softconnect, udc->vbus_active); - - if (udc->driver && udc->softconnect && udc->vbus_active) { - retval = mv_udc_enable(udc); - if (retval == 0) { - /* Clock is disabled, need re-init registers */ - udc_reset(udc); - ep0_reset(udc); - udc_start(udc); - } - } else if (udc->driver && udc->vbus_active) { - /* stop all the transfer in queue*/ - stop_activity(udc, udc->driver); - udc_stop(udc); - mv_udc_disable(udc); - } - - spin_unlock_irqrestore(&udc->lock, flags); - return retval; -} - -static int mv_udc_start(struct usb_gadget *, struct usb_gadget_driver *); -static int mv_udc_stop(struct usb_gadget *); -/* device controller usb_gadget_ops structure */ -static const struct usb_gadget_ops mv_ops = { - - /* returns the current frame number */ - .get_frame = mv_udc_get_frame, - - /* tries to wake up the host connected to this gadget */ - .wakeup = mv_udc_wakeup, - - /* notify controller that VBUS is powered or not */ - .vbus_session = mv_udc_vbus_session, - - /* D+ pullup, software-controlled connect/disconnect to USB host */ - .pullup = mv_udc_pullup, - .udc_start = mv_udc_start, - .udc_stop = mv_udc_stop, -}; - -static int eps_init(struct mv_udc *udc) -{ - struct mv_ep *ep; - char name[14]; - int i; - - /* initialize ep0 */ - ep = &udc->eps[0]; - ep->udc = udc; - strncpy(ep->name, "ep0", sizeof(ep->name)); - ep->ep.name = ep->name; - ep->ep.ops = &mv_ep_ops; - ep->wedge = 0; - ep->stopped = 0; - usb_ep_set_maxpacket_limit(&ep->ep, EP0_MAX_PKT_SIZE); - ep->ep.caps.type_control = true; - ep->ep.caps.dir_in = true; - ep->ep.caps.dir_out = true; - ep->ep_num = 0; - ep->ep.desc = &mv_ep0_desc; - INIT_LIST_HEAD(&ep->queue); - - ep->ep_type = USB_ENDPOINT_XFER_CONTROL; - - /* initialize other endpoints */ - for (i = 2; i < udc->max_eps * 2; i++) { - ep = &udc->eps[i]; - if (i % 2) { - snprintf(name, sizeof(name), "ep%din", i / 2); - ep->direction = EP_DIR_IN; - ep->ep.caps.dir_in = true; - } else { - snprintf(name, sizeof(name), "ep%dout", i / 2); - ep->direction = EP_DIR_OUT; - ep->ep.caps.dir_out = true; - } - ep->udc = udc; - strncpy(ep->name, name, sizeof(ep->name)); - ep->ep.name = ep->name; - - ep->ep.caps.type_iso = true; - ep->ep.caps.type_bulk = true; - ep->ep.caps.type_int = true; - - ep->ep.ops = &mv_ep_ops; - ep->stopped = 0; - usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0); - ep->ep_num = i / 2; - - INIT_LIST_HEAD(&ep->queue); - list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); - - ep->dqh = &udc->ep_dqh[i]; - } - - return 0; -} - -/* delete all endpoint requests, called with spinlock held */ -static void nuke(struct mv_ep *ep, int status) -{ - /* called with spinlock held */ - ep->stopped = 1; - - /* endpoint fifo flush */ - mv_ep_fifo_flush(&ep->ep); - - while (!list_empty(&ep->queue)) { - struct mv_req *req = NULL; - req = list_entry(ep->queue.next, struct mv_req, queue); - done(ep, req, status); - } -} - -static void gadget_reset(struct mv_udc *udc, struct usb_gadget_driver *driver) -{ - struct mv_ep *ep; - - nuke(&udc->eps[0], -ESHUTDOWN); - - list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { - nuke(ep, -ESHUTDOWN); - } - - /* report reset; the driver is already quiesced */ - if (driver) { - spin_unlock(&udc->lock); - usb_gadget_udc_reset(&udc->gadget, driver); - spin_lock(&udc->lock); - } -} -/* stop all USB activities */ -static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver) -{ - struct mv_ep *ep; - - nuke(&udc->eps[0], -ESHUTDOWN); - - list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { - nuke(ep, -ESHUTDOWN); - } - - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock(&udc->lock); - driver->disconnect(&udc->gadget); - spin_lock(&udc->lock); - } -} - -static int mv_udc_start(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - struct mv_udc *udc; - int retval = 0; - unsigned long flags; - - udc = container_of(gadget, struct mv_udc, gadget); - - if (udc->driver) - return -EBUSY; - - spin_lock_irqsave(&udc->lock, flags); - - /* hook up the driver ... */ - udc->driver = driver; - - udc->usb_state = USB_STATE_ATTACHED; - udc->ep0_state = WAIT_FOR_SETUP; - udc->ep0_dir = EP_DIR_OUT; - - spin_unlock_irqrestore(&udc->lock, flags); - - if (udc->transceiver) { - retval = otg_set_peripheral(udc->transceiver->otg, - &udc->gadget); - if (retval) { - dev_err(&udc->dev->dev, - "unable to register peripheral to otg\n"); - udc->driver = NULL; - return retval; - } - } - - /* When boot with cable attached, there will be no vbus irq occurred */ - if (udc->qwork) - queue_work(udc->qwork, &udc->vbus_work); - - return 0; -} - -static int mv_udc_stop(struct usb_gadget *gadget) -{ - struct mv_udc *udc; - unsigned long flags; - - udc = container_of(gadget, struct mv_udc, gadget); - - spin_lock_irqsave(&udc->lock, flags); - - mv_udc_enable(udc); - udc_stop(udc); - - /* stop all usb activities */ - udc->gadget.speed = USB_SPEED_UNKNOWN; - stop_activity(udc, NULL); - mv_udc_disable(udc); - - spin_unlock_irqrestore(&udc->lock, flags); - - /* unbind gadget driver */ - udc->driver = NULL; - - return 0; -} - -static void mv_set_ptc(struct mv_udc *udc, u32 mode) -{ - u32 portsc; - - portsc = readl(&udc->op_regs->portsc[0]); - portsc |= mode << 16; - writel(portsc, &udc->op_regs->portsc[0]); -} - -static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req) -{ - struct mv_ep *mvep = container_of(ep, struct mv_ep, ep); - struct mv_req *req = container_of(_req, struct mv_req, req); - struct mv_udc *udc; - unsigned long flags; - - udc = mvep->udc; - - dev_info(&udc->dev->dev, "switch to test mode %d\n", req->test_mode); - - spin_lock_irqsave(&udc->lock, flags); - if (req->test_mode) { - mv_set_ptc(udc, req->test_mode); - req->test_mode = 0; - } - spin_unlock_irqrestore(&udc->lock, flags); -} - -static int -udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) -{ - int retval = 0; - struct mv_req *req; - struct mv_ep *ep; - - ep = &udc->eps[0]; - udc->ep0_dir = direction; - udc->ep0_state = WAIT_FOR_OUT_STATUS; - - req = udc->status_req; - - /* fill in the request structure */ - if (empty == false) { - *((u16 *) req->req.buf) = cpu_to_le16(status); - req->req.length = 2; - } else - req->req.length = 0; - - req->ep = ep; - req->req.status = -EINPROGRESS; - req->req.actual = 0; - if (udc->test_mode) { - req->req.complete = prime_status_complete; - req->test_mode = udc->test_mode; - udc->test_mode = 0; - } else - req->req.complete = NULL; - req->dtd_count = 0; - - if (req->req.dma == DMA_ADDR_INVALID) { - req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, - req->req.buf, req->req.length, - ep_dir(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = 1; - } - - /* prime the data phase */ - if (!req_to_dtd(req)) { - retval = queue_dtd(ep, req); - if (retval) { - dev_err(&udc->dev->dev, - "Failed to queue dtd when prime status\n"); - goto out; - } - } else{ /* no mem */ - retval = -ENOMEM; - dev_err(&udc->dev->dev, - "Failed to dma_pool_alloc when prime status\n"); - goto out; - } - - list_add_tail(&req->queue, &ep->queue); - - return 0; -out: - usb_gadget_unmap_request(&udc->gadget, &req->req, ep_dir(ep)); - - return retval; -} - -static void mv_udc_testmode(struct mv_udc *udc, u16 index) -{ - if (index <= USB_TEST_FORCE_ENABLE) { - udc->test_mode = index; - if (udc_prime_status(udc, EP_DIR_IN, 0, true)) - ep0_stall(udc); - } else - dev_err(&udc->dev->dev, - "This test mode(%d) is not supported\n", index); -} - -static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup) -{ - udc->dev_addr = (u8)setup->wValue; - - /* update usb state */ - udc->usb_state = USB_STATE_ADDRESS; - - if (udc_prime_status(udc, EP_DIR_IN, 0, true)) - ep0_stall(udc); -} - -static void ch9getstatus(struct mv_udc *udc, u8 ep_num, - struct usb_ctrlrequest *setup) -{ - u16 status = 0; - int retval; - - if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) - != (USB_DIR_IN | USB_TYPE_STANDARD)) - return; - - if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) { - status = 1 << USB_DEVICE_SELF_POWERED; - status |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; - } else if ((setup->bRequestType & USB_RECIP_MASK) - == USB_RECIP_INTERFACE) { - /* get interface status */ - status = 0; - } else if ((setup->bRequestType & USB_RECIP_MASK) - == USB_RECIP_ENDPOINT) { - u8 ep_num, direction; - - ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK; - direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK) - ? EP_DIR_IN : EP_DIR_OUT; - status = ep_is_stall(udc, ep_num, direction) - << USB_ENDPOINT_HALT; - } - - retval = udc_prime_status(udc, EP_DIR_IN, status, false); - if (retval) - ep0_stall(udc); - else - udc->ep0_state = DATA_STATE_XMIT; -} - -static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) -{ - u8 ep_num; - u8 direction; - struct mv_ep *ep; - - if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) - == ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) { - switch (setup->wValue) { - case USB_DEVICE_REMOTE_WAKEUP: - udc->remote_wakeup = 0; - break; - default: - goto out; - } - } else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) - == ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) { - switch (setup->wValue) { - case USB_ENDPOINT_HALT: - ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK; - direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK) - ? EP_DIR_IN : EP_DIR_OUT; - if (setup->wValue != 0 || setup->wLength != 0 - || ep_num > udc->max_eps) - goto out; - ep = &udc->eps[ep_num * 2 + direction]; - if (ep->wedge == 1) - break; - spin_unlock(&udc->lock); - ep_set_stall(udc, ep_num, direction, 0); - spin_lock(&udc->lock); - break; - default: - goto out; - } - } else - goto out; - - if (udc_prime_status(udc, EP_DIR_IN, 0, true)) - ep0_stall(udc); -out: - return; -} - -static void ch9setfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) -{ - u8 ep_num; - u8 direction; - - if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) - == ((USB_TYPE_STANDARD | USB_RECIP_DEVICE))) { - switch (setup->wValue) { - case USB_DEVICE_REMOTE_WAKEUP: - udc->remote_wakeup = 1; - break; - case USB_DEVICE_TEST_MODE: - if (setup->wIndex & 0xFF - || udc->gadget.speed != USB_SPEED_HIGH) - ep0_stall(udc); - - if (udc->usb_state != USB_STATE_CONFIGURED - && udc->usb_state != USB_STATE_ADDRESS - && udc->usb_state != USB_STATE_DEFAULT) - ep0_stall(udc); - - mv_udc_testmode(udc, (setup->wIndex >> 8)); - goto out; - default: - goto out; - } - } else if ((setup->bRequestType & (USB_TYPE_MASK | USB_RECIP_MASK)) - == ((USB_TYPE_STANDARD | USB_RECIP_ENDPOINT))) { - switch (setup->wValue) { - case USB_ENDPOINT_HALT: - ep_num = setup->wIndex & USB_ENDPOINT_NUMBER_MASK; - direction = (setup->wIndex & USB_ENDPOINT_DIR_MASK) - ? EP_DIR_IN : EP_DIR_OUT; - if (setup->wValue != 0 || setup->wLength != 0 - || ep_num > udc->max_eps) - goto out; - spin_unlock(&udc->lock); - ep_set_stall(udc, ep_num, direction, 1); - spin_lock(&udc->lock); - break; - default: - goto out; - } - } else - goto out; - - if (udc_prime_status(udc, EP_DIR_IN, 0, true)) - ep0_stall(udc); -out: - return; -} - -static void handle_setup_packet(struct mv_udc *udc, u8 ep_num, - struct usb_ctrlrequest *setup) - __releases(&ep->udc->lock) - __acquires(&ep->udc->lock) -{ - bool delegate = false; - - nuke(&udc->eps[ep_num * 2 + EP_DIR_OUT], -ESHUTDOWN); - - dev_dbg(&udc->dev->dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", - setup->bRequestType, setup->bRequest, - setup->wValue, setup->wIndex, setup->wLength); - /* We process some standard setup requests here */ - if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { - switch (setup->bRequest) { - case USB_REQ_GET_STATUS: - ch9getstatus(udc, ep_num, setup); - break; - - case USB_REQ_SET_ADDRESS: - ch9setaddress(udc, setup); - break; - - case USB_REQ_CLEAR_FEATURE: - ch9clearfeature(udc, setup); - break; - - case USB_REQ_SET_FEATURE: - ch9setfeature(udc, setup); - break; - - default: - delegate = true; - } - } else - delegate = true; - - /* delegate USB standard requests to the gadget driver */ - if (delegate == true) { - /* USB requests handled by gadget */ - if (setup->wLength) { - /* DATA phase from gadget, STATUS phase from udc */ - udc->ep0_dir = (setup->bRequestType & USB_DIR_IN) - ? EP_DIR_IN : EP_DIR_OUT; - spin_unlock(&udc->lock); - if (udc->driver->setup(&udc->gadget, - &udc->local_setup_buff) < 0) - ep0_stall(udc); - spin_lock(&udc->lock); - udc->ep0_state = (setup->bRequestType & USB_DIR_IN) - ? DATA_STATE_XMIT : DATA_STATE_RECV; - } else { - /* no DATA phase, IN STATUS phase from gadget */ - udc->ep0_dir = EP_DIR_IN; - spin_unlock(&udc->lock); - if (udc->driver->setup(&udc->gadget, - &udc->local_setup_buff) < 0) - ep0_stall(udc); - spin_lock(&udc->lock); - udc->ep0_state = WAIT_FOR_OUT_STATUS; - } - } -} - -/* complete DATA or STATUS phase of ep0 prime status phase if needed */ -static void ep0_req_complete(struct mv_udc *udc, - struct mv_ep *ep0, struct mv_req *req) -{ - u32 new_addr; - - if (udc->usb_state == USB_STATE_ADDRESS) { - /* set the new address */ - new_addr = (u32)udc->dev_addr; - writel(new_addr << USB_DEVICE_ADDRESS_BIT_SHIFT, - &udc->op_regs->deviceaddr); - } - - done(ep0, req, 0); - - switch (udc->ep0_state) { - case DATA_STATE_XMIT: - /* receive status phase */ - if (udc_prime_status(udc, EP_DIR_OUT, 0, true)) - ep0_stall(udc); - break; - case DATA_STATE_RECV: - /* send status phase */ - if (udc_prime_status(udc, EP_DIR_IN, 0 , true)) - ep0_stall(udc); - break; - case WAIT_FOR_OUT_STATUS: - udc->ep0_state = WAIT_FOR_SETUP; - break; - case WAIT_FOR_SETUP: - dev_err(&udc->dev->dev, "unexpect ep0 packets\n"); - break; - default: - ep0_stall(udc); - break; - } -} - -static void get_setup_data(struct mv_udc *udc, u8 ep_num, u8 *buffer_ptr) -{ - u32 temp; - struct mv_dqh *dqh; - - dqh = &udc->ep_dqh[ep_num * 2 + EP_DIR_OUT]; - - /* Clear bit in ENDPTSETUPSTAT */ - writel((1 << ep_num), &udc->op_regs->epsetupstat); - - /* while a hazard exists when setup package arrives */ - do { - /* Set Setup Tripwire */ - temp = readl(&udc->op_regs->usbcmd); - writel(temp | USBCMD_SETUP_TRIPWIRE_SET, &udc->op_regs->usbcmd); - - /* Copy the setup packet to local buffer */ - memcpy(buffer_ptr, (u8 *) dqh->setup_buffer, 8); - } while (!(readl(&udc->op_regs->usbcmd) & USBCMD_SETUP_TRIPWIRE_SET)); - - /* Clear Setup Tripwire */ - temp = readl(&udc->op_regs->usbcmd); - writel(temp & ~USBCMD_SETUP_TRIPWIRE_SET, &udc->op_regs->usbcmd); -} - -static void irq_process_tr_complete(struct mv_udc *udc) -{ - u32 tmp, bit_pos; - int i, ep_num = 0, direction = 0; - struct mv_ep *curr_ep; - struct mv_req *curr_req, *temp_req; - int status; - - /* - * We use separate loops for ENDPTSETUPSTAT and ENDPTCOMPLETE - * because the setup packets are to be read ASAP - */ - - /* Process all Setup packet received interrupts */ - tmp = readl(&udc->op_regs->epsetupstat); - - if (tmp) { - for (i = 0; i < udc->max_eps; i++) { - if (tmp & (1 << i)) { - get_setup_data(udc, i, - (u8 *)(&udc->local_setup_buff)); - handle_setup_packet(udc, i, - &udc->local_setup_buff); - } - } - } - - /* Don't clear the endpoint setup status register here. - * It is cleared as a setup packet is read out of the buffer - */ - - /* Process non-setup transaction complete interrupts */ - tmp = readl(&udc->op_regs->epcomplete); - - if (!tmp) - return; - - writel(tmp, &udc->op_regs->epcomplete); - - for (i = 0; i < udc->max_eps * 2; i++) { - ep_num = i >> 1; - direction = i % 2; - - bit_pos = 1 << (ep_num + 16 * direction); - - if (!(bit_pos & tmp)) - continue; - - if (i == 1) - curr_ep = &udc->eps[0]; - else - curr_ep = &udc->eps[i]; - /* process the req queue until an uncomplete request */ - list_for_each_entry_safe(curr_req, temp_req, - &curr_ep->queue, queue) { - status = process_ep_req(udc, i, curr_req); - if (status) - break; - - /* write back status to req */ - curr_req->req.status = status; - - /* ep0 request completion */ - if (ep_num == 0) { - ep0_req_complete(udc, curr_ep, curr_req); - break; - } else { - done(curr_ep, curr_req, status); - } - } - } -} - -static void irq_process_reset(struct mv_udc *udc) -{ - u32 tmp; - unsigned int loops; - - udc->ep0_dir = EP_DIR_OUT; - udc->ep0_state = WAIT_FOR_SETUP; - udc->remote_wakeup = 0; /* default to 0 on reset */ - - /* The address bits are past bit 25-31. Set the address */ - tmp = readl(&udc->op_regs->deviceaddr); - tmp &= ~(USB_DEVICE_ADDRESS_MASK); - writel(tmp, &udc->op_regs->deviceaddr); - - /* Clear all the setup token semaphores */ - tmp = readl(&udc->op_regs->epsetupstat); - writel(tmp, &udc->op_regs->epsetupstat); - - /* Clear all the endpoint complete status bits */ - tmp = readl(&udc->op_regs->epcomplete); - writel(tmp, &udc->op_regs->epcomplete); - - /* wait until all endptprime bits cleared */ - loops = LOOPS(PRIME_TIMEOUT); - while (readl(&udc->op_regs->epprime) & 0xFFFFFFFF) { - if (loops == 0) { - dev_err(&udc->dev->dev, - "Timeout for ENDPTPRIME = 0x%x\n", - readl(&udc->op_regs->epprime)); - break; - } - loops--; - udelay(LOOPS_USEC); - } - - /* Write 1s to the Flush register */ - writel((u32)~0, &udc->op_regs->epflush); - - if (readl(&udc->op_regs->portsc[0]) & PORTSCX_PORT_RESET) { - dev_info(&udc->dev->dev, "usb bus reset\n"); - udc->usb_state = USB_STATE_DEFAULT; - /* reset all the queues, stop all USB activities */ - gadget_reset(udc, udc->driver); - } else { - dev_info(&udc->dev->dev, "USB reset portsc 0x%x\n", - readl(&udc->op_regs->portsc)); - - /* - * re-initialize - * controller reset - */ - udc_reset(udc); - - /* reset all the queues, stop all USB activities */ - stop_activity(udc, udc->driver); - - /* reset ep0 dQH and endptctrl */ - ep0_reset(udc); - - /* enable interrupt and set controller to run state */ - udc_start(udc); - - udc->usb_state = USB_STATE_ATTACHED; - } -} - -static void handle_bus_resume(struct mv_udc *udc) -{ - udc->usb_state = udc->resume_state; - udc->resume_state = 0; - - /* report resume to the driver */ - if (udc->driver) { - if (udc->driver->resume) { - spin_unlock(&udc->lock); - udc->driver->resume(&udc->gadget); - spin_lock(&udc->lock); - } - } -} - -static void irq_process_suspend(struct mv_udc *udc) -{ - udc->resume_state = udc->usb_state; - udc->usb_state = USB_STATE_SUSPENDED; - - if (udc->driver->suspend) { - spin_unlock(&udc->lock); - udc->driver->suspend(&udc->gadget); - spin_lock(&udc->lock); - } -} - -static void irq_process_port_change(struct mv_udc *udc) -{ - u32 portsc; - - portsc = readl(&udc->op_regs->portsc[0]); - if (!(portsc & PORTSCX_PORT_RESET)) { - /* Get the speed */ - u32 speed = portsc & PORTSCX_PORT_SPEED_MASK; - switch (speed) { - case PORTSCX_PORT_SPEED_HIGH: - udc->gadget.speed = USB_SPEED_HIGH; - break; - case PORTSCX_PORT_SPEED_FULL: - udc->gadget.speed = USB_SPEED_FULL; - break; - case PORTSCX_PORT_SPEED_LOW: - udc->gadget.speed = USB_SPEED_LOW; - break; - default: - udc->gadget.speed = USB_SPEED_UNKNOWN; - break; - } - } - - if (portsc & PORTSCX_PORT_SUSPEND) { - udc->resume_state = udc->usb_state; - udc->usb_state = USB_STATE_SUSPENDED; - if (udc->driver->suspend) { - spin_unlock(&udc->lock); - udc->driver->suspend(&udc->gadget); - spin_lock(&udc->lock); - } - } - - if (!(portsc & PORTSCX_PORT_SUSPEND) - && udc->usb_state == USB_STATE_SUSPENDED) { - handle_bus_resume(udc); - } - - if (!udc->resume_state) - udc->usb_state = USB_STATE_DEFAULT; -} - -static void irq_process_error(struct mv_udc *udc) -{ - /* Increment the error count */ - udc->errors++; -} - -static irqreturn_t mv_udc_irq(int irq, void *dev) -{ - struct mv_udc *udc = (struct mv_udc *)dev; - u32 status, intr; - - /* Disable ISR when stopped bit is set */ - if (udc->stopped) - return IRQ_NONE; - - spin_lock(&udc->lock); - - status = readl(&udc->op_regs->usbsts); - intr = readl(&udc->op_regs->usbintr); - status &= intr; - - if (status == 0) { - spin_unlock(&udc->lock); - return IRQ_NONE; - } - - /* Clear all the interrupts occurred */ - writel(status, &udc->op_regs->usbsts); - - if (status & USBSTS_ERR) - irq_process_error(udc); - - if (status & USBSTS_RESET) - irq_process_reset(udc); - - if (status & USBSTS_PORT_CHANGE) - irq_process_port_change(udc); - - if (status & USBSTS_INT) - irq_process_tr_complete(udc); - - if (status & USBSTS_SUSPEND) - irq_process_suspend(udc); - - spin_unlock(&udc->lock); - - return IRQ_HANDLED; -} - -static irqreturn_t mv_udc_vbus_irq(int irq, void *dev) -{ - struct mv_udc *udc = (struct mv_udc *)dev; - - /* polling VBUS and init phy may cause too much time*/ - if (udc->qwork) - queue_work(udc->qwork, &udc->vbus_work); - - return IRQ_HANDLED; -} - -static void mv_udc_vbus_work(struct work_struct *work) -{ - struct mv_udc *udc; - unsigned int vbus; - - udc = container_of(work, struct mv_udc, vbus_work); - if (!udc->pdata->vbus) - return; - - vbus = udc->pdata->vbus->poll(); - dev_info(&udc->dev->dev, "vbus is %d\n", vbus); - - if (vbus == VBUS_HIGH) - mv_udc_vbus_session(&udc->gadget, 1); - else if (vbus == VBUS_LOW) - mv_udc_vbus_session(&udc->gadget, 0); -} - -/* release device structure */ -static void gadget_release(struct device *_dev) -{ - struct mv_udc *udc; - - udc = dev_get_drvdata(_dev); - - complete(udc->done); -} - -static void mv_udc_remove(struct platform_device *pdev) -{ - struct mv_udc *udc; - - udc = platform_get_drvdata(pdev); - - usb_del_gadget_udc(&udc->gadget); - - if (udc->qwork) - destroy_workqueue(udc->qwork); - - /* free memory allocated in probe */ - dma_pool_destroy(udc->dtd_pool); - - if (udc->ep_dqh) - dma_free_coherent(&pdev->dev, udc->ep_dqh_size, - udc->ep_dqh, udc->ep_dqh_dma); - - mv_udc_disable(udc); - - /* free dev, wait for the release() finished */ - wait_for_completion(udc->done); -} - -static int mv_udc_probe(struct platform_device *pdev) -{ - struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct mv_udc *udc; - int retval = 0; - struct resource *r; - size_t size; - - if (pdata == NULL) { - dev_err(&pdev->dev, "missing platform_data\n"); - return -ENODEV; - } - - udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL); - if (udc == NULL) - return -ENOMEM; - - udc->done = &release_done; - udc->pdata = dev_get_platdata(&pdev->dev); - spin_lock_init(&udc->lock); - - udc->dev = pdev; - - if (pdata->mode == MV_USB_MODE_OTG) { - udc->transceiver = devm_usb_get_phy(&pdev->dev, - USB_PHY_TYPE_USB2); - if (IS_ERR(udc->transceiver)) { - retval = PTR_ERR(udc->transceiver); - - if (retval == -ENXIO) - return retval; - - udc->transceiver = NULL; - return -EPROBE_DEFER; - } - } - - /* udc only have one sysclk. */ - udc->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(udc->clk)) - return PTR_ERR(udc->clk); - - r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs"); - if (r == NULL) { - dev_err(&pdev->dev, "no I/O memory resource defined\n"); - return -ENODEV; - } - - udc->cap_regs = (struct mv_cap_regs __iomem *) - devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (udc->cap_regs == NULL) { - dev_err(&pdev->dev, "failed to map I/O memory\n"); - return -EBUSY; - } - - r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs"); - if (r == NULL) { - dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); - return -ENODEV; - } - - udc->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (udc->phy_regs == NULL) { - dev_err(&pdev->dev, "failed to map phy I/O memory\n"); - return -EBUSY; - } - - /* we will acces controller register, so enable the clk */ - retval = mv_udc_enable_internal(udc); - if (retval) - return retval; - - udc->op_regs = - (struct mv_op_regs __iomem *)((unsigned long)udc->cap_regs - + (readl(&udc->cap_regs->caplength_hciversion) - & CAPLENGTH_MASK)); - udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK; - - /* - * some platform will use usb to download image, it may not disconnect - * usb gadget before loading kernel. So first stop udc here. - */ - udc_stop(udc); - writel(0xFFFFFFFF, &udc->op_regs->usbsts); - - size = udc->max_eps * sizeof(struct mv_dqh) *2; - size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1); - udc->ep_dqh = dma_alloc_coherent(&pdev->dev, size, - &udc->ep_dqh_dma, GFP_KERNEL); - - if (udc->ep_dqh == NULL) { - dev_err(&pdev->dev, "allocate dQH memory failed\n"); - retval = -ENOMEM; - goto err_disable_clock; - } - udc->ep_dqh_size = size; - - /* create dTD dma_pool resource */ - udc->dtd_pool = dma_pool_create("mv_dtd", - &pdev->dev, - sizeof(struct mv_dtd), - DTD_ALIGNMENT, - DMA_BOUNDARY); - - if (!udc->dtd_pool) { - retval = -ENOMEM; - goto err_free_dma; - } - - size = udc->max_eps * sizeof(struct mv_ep) *2; - udc->eps = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (udc->eps == NULL) { - retval = -ENOMEM; - goto err_destroy_dma; - } - - /* initialize ep0 status request structure */ - udc->status_req = devm_kzalloc(&pdev->dev, sizeof(struct mv_req), - GFP_KERNEL); - if (!udc->status_req) { - retval = -ENOMEM; - goto err_destroy_dma; - } - INIT_LIST_HEAD(&udc->status_req->queue); - - /* allocate a small amount of memory to get valid address */ - udc->status_req->req.buf = devm_kzalloc(&pdev->dev, 8, GFP_KERNEL); - if (!udc->status_req->req.buf) { - retval = -ENOMEM; - goto err_destroy_dma; - } - udc->status_req->req.dma = DMA_ADDR_INVALID; - - udc->resume_state = USB_STATE_NOTATTACHED; - udc->usb_state = USB_STATE_POWERED; - udc->ep0_dir = EP_DIR_OUT; - udc->remote_wakeup = 0; - - r = platform_get_resource(udc->dev, IORESOURCE_IRQ, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no IRQ resource defined\n"); - retval = -ENODEV; - goto err_destroy_dma; - } - udc->irq = r->start; - if (devm_request_irq(&pdev->dev, udc->irq, mv_udc_irq, - IRQF_SHARED, driver_name, udc)) { - dev_err(&pdev->dev, "Request irq %d for UDC failed\n", - udc->irq); - retval = -ENODEV; - goto err_destroy_dma; - } - - /* initialize gadget structure */ - udc->gadget.ops = &mv_ops; /* usb_gadget_ops */ - udc->gadget.ep0 = &udc->eps[0].ep; /* gadget ep0 */ - INIT_LIST_HEAD(&udc->gadget.ep_list); /* ep_list */ - udc->gadget.speed = USB_SPEED_UNKNOWN; /* speed */ - udc->gadget.max_speed = USB_SPEED_HIGH; /* support dual speed */ - - /* the "gadget" abstracts/virtualizes the controller */ - udc->gadget.name = driver_name; /* gadget name */ - - eps_init(udc); - - /* VBUS detect: we can disable/enable clock on demand.*/ - if (udc->transceiver) - udc->clock_gating = 1; - else if (pdata->vbus) { - udc->clock_gating = 1; - retval = devm_request_threaded_irq(&pdev->dev, - pdata->vbus->irq, NULL, - mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc); - if (retval) { - dev_info(&pdev->dev, - "Can not request irq for VBUS, " - "disable clock gating\n"); - udc->clock_gating = 0; - } - - udc->qwork = create_singlethread_workqueue("mv_udc_queue"); - if (!udc->qwork) { - dev_err(&pdev->dev, "cannot create workqueue\n"); - retval = -ENOMEM; - goto err_destroy_dma; - } - - INIT_WORK(&udc->vbus_work, mv_udc_vbus_work); - } - - /* - * When clock gating is supported, we can disable clk and phy. - * If not, it means that VBUS detection is not supported, we - * have to enable vbus active all the time to let controller work. - */ - if (udc->clock_gating) - mv_udc_disable_internal(udc); - else - udc->vbus_active = 1; - - retval = usb_add_gadget_udc_release(&pdev->dev, &udc->gadget, - gadget_release); - if (retval) - goto err_create_workqueue; - - platform_set_drvdata(pdev, udc); - dev_info(&pdev->dev, "successful probe UDC device %s clock gating.\n", - udc->clock_gating ? "with" : "without"); - - return 0; - -err_create_workqueue: - if (udc->qwork) - destroy_workqueue(udc->qwork); -err_destroy_dma: - dma_pool_destroy(udc->dtd_pool); -err_free_dma: - dma_free_coherent(&pdev->dev, udc->ep_dqh_size, - udc->ep_dqh, udc->ep_dqh_dma); -err_disable_clock: - mv_udc_disable_internal(udc); - - return retval; -} - -#ifdef CONFIG_PM -static int mv_udc_suspend(struct device *dev) -{ - struct mv_udc *udc; - - udc = dev_get_drvdata(dev); - - /* if OTG is enabled, the following will be done in OTG driver*/ - if (udc->transceiver) - return 0; - - if (udc->pdata->vbus && udc->pdata->vbus->poll) - if (udc->pdata->vbus->poll() == VBUS_HIGH) { - dev_info(&udc->dev->dev, "USB cable is connected!\n"); - return -EAGAIN; - } - - /* - * only cable is unplugged, udc can suspend. - * So do not care about clock_gating == 1. - */ - if (!udc->clock_gating) { - udc_stop(udc); - - spin_lock_irq(&udc->lock); - /* stop all usb activities */ - stop_activity(udc, udc->driver); - spin_unlock_irq(&udc->lock); - - mv_udc_disable_internal(udc); - } - - return 0; -} - -static int mv_udc_resume(struct device *dev) -{ - struct mv_udc *udc; - int retval; - - udc = dev_get_drvdata(dev); - - /* if OTG is enabled, the following will be done in OTG driver*/ - if (udc->transceiver) - return 0; - - if (!udc->clock_gating) { - retval = mv_udc_enable_internal(udc); - if (retval) - return retval; - - if (udc->driver && udc->softconnect) { - udc_reset(udc); - ep0_reset(udc); - udc_start(udc); - } - } - - return 0; -} - -static const struct dev_pm_ops mv_udc_pm_ops = { - .suspend = mv_udc_suspend, - .resume = mv_udc_resume, -}; -#endif - -static void mv_udc_shutdown(struct platform_device *pdev) -{ - struct mv_udc *udc; - u32 mode; - - udc = platform_get_drvdata(pdev); - /* reset controller mode to IDLE */ - mv_udc_enable(udc); - mode = readl(&udc->op_regs->usbmode); - mode &= ~3; - writel(mode, &udc->op_regs->usbmode); - mv_udc_disable(udc); -} - -static struct platform_driver udc_driver = { - .probe = mv_udc_probe, - .remove = mv_udc_remove, - .shutdown = mv_udc_shutdown, - .driver = { - .name = "mv-udc", -#ifdef CONFIG_PM - .pm = &mv_udc_pm_ops, -#endif - }, -}; - -module_platform_driver(udc_driver); -MODULE_ALIAS("platform:mv-udc"); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c deleted file mode 100644 index 7ecddbf5c90d..000000000000 --- a/drivers/usb/gadget/udc/net2272.c +++ /dev/null @@ -1,2723 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for PLX NET2272 USB device controller - * - * Copyright (C) 2005-2006 PLX Technology, Inc. - * Copyright (C) 2006-2011 Analog Devices, Inc. - */ - -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/ioport.h> -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/pci.h> -#include <linux/platform_device.h> -#include <linux/prefetch.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/timer.h> -#include <linux/usb.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> - -#include <asm/byteorder.h> -#include <linux/unaligned.h> - -#include "net2272.h" - -#define DRIVER_DESC "PLX NET2272 USB Peripheral Controller" - -static const char driver_name[] = "net2272"; -static const char driver_vers[] = "2006 October 17/mainline"; -static const char driver_desc[] = DRIVER_DESC; - -static const char ep0name[] = "ep0"; -static const char * const ep_name[] = { - ep0name, - "ep-a", "ep-b", "ep-c", -}; - -#ifdef CONFIG_USB_NET2272_DMA -/* - * use_dma: the NET2272 can use an external DMA controller. - * Note that since there is no generic DMA api, some functions, - * notably request_dma, start_dma, and cancel_dma will need to be - * modified for your platform's particular dma controller. - * - * If use_dma is disabled, pio will be used instead. - */ -static bool use_dma = false; -module_param(use_dma, bool, 0644); - -/* - * dma_ep: selects the endpoint for use with dma (1=ep-a, 2=ep-b) - * The NET2272 can only use dma for a single endpoint at a time. - * At some point this could be modified to allow either endpoint - * to take control of dma as it becomes available. - * - * Note that DMA should not be used on OUT endpoints unless it can - * be guaranteed that no short packets will arrive on an IN endpoint - * while the DMA operation is pending. Otherwise the OUT DMA will - * terminate prematurely (See NET2272 Errata 630-0213-0101) - */ -static ushort dma_ep = 1; -module_param(dma_ep, ushort, 0644); - -/* - * dma_mode: net2272 dma mode setting (see LOCCTL1 definition): - * mode 0 == Slow DREQ mode - * mode 1 == Fast DREQ mode - * mode 2 == Burst mode - */ -static ushort dma_mode = 2; -module_param(dma_mode, ushort, 0644); -#else -#define use_dma 0 -#define dma_ep 1 -#define dma_mode 2 -#endif - -/* - * fifo_mode: net2272 buffer configuration: - * mode 0 == ep-{a,b,c} 512db each - * mode 1 == ep-a 1k, ep-{b,c} 512db - * mode 2 == ep-a 1k, ep-b 1k, ep-c 512db - * mode 3 == ep-a 1k, ep-b disabled, ep-c 512db - */ -static ushort fifo_mode; -module_param(fifo_mode, ushort, 0644); - -/* - * enable_suspend: When enabled, the driver will respond to - * USB suspend requests by powering down the NET2272. Otherwise, - * USB suspend requests will be ignored. This is acceptable for - * self-powered devices. For bus powered devices set this to 1. - */ -static ushort enable_suspend; -module_param(enable_suspend, ushort, 0644); - -static void assert_out_naking(struct net2272_ep *ep, const char *where) -{ - u8 tmp; - -#ifndef DEBUG - return; -#endif - - tmp = net2272_ep_read(ep, EP_STAT0); - if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { - dev_dbg(ep->dev->dev, "%s %s %02x !NAK\n", - ep->ep.name, where, tmp); - net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS); - } -} -#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__) - -static void stop_out_naking(struct net2272_ep *ep) -{ - u8 tmp = net2272_ep_read(ep, EP_STAT0); - - if ((tmp & (1 << NAK_OUT_PACKETS)) != 0) - net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS); -} - -#define PIPEDIR(bAddress) (usb_pipein(bAddress) ? "in" : "out") - -static char *type_string(u8 bmAttributes) -{ - switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { - case USB_ENDPOINT_XFER_BULK: return "bulk"; - case USB_ENDPOINT_XFER_ISOC: return "iso"; - case USB_ENDPOINT_XFER_INT: return "intr"; - default: return "control"; - } -} - -static char *buf_state_string(unsigned state) -{ - switch (state) { - case BUFF_FREE: return "free"; - case BUFF_VALID: return "valid"; - case BUFF_LCL: return "local"; - case BUFF_USB: return "usb"; - default: return "unknown"; - } -} - -static char *dma_mode_string(void) -{ - if (!use_dma) - return "PIO"; - switch (dma_mode) { - case 0: return "SLOW DREQ"; - case 1: return "FAST DREQ"; - case 2: return "BURST"; - default: return "invalid"; - } -} - -static void net2272_dequeue_all(struct net2272_ep *); -static int net2272_kick_dma(struct net2272_ep *, struct net2272_request *); -static int net2272_fifo_status(struct usb_ep *); - -static const struct usb_ep_ops net2272_ep_ops; - -/*---------------------------------------------------------------------------*/ - -static int -net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) -{ - struct net2272 *dev; - struct net2272_ep *ep; - u32 max; - u8 tmp; - unsigned long flags; - - ep = container_of(_ep, struct net2272_ep, ep); - if (!_ep || !desc || ep->desc || _ep->name == ep0name - || desc->bDescriptorType != USB_DT_ENDPOINT) - return -EINVAL; - dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - max = usb_endpoint_maxp(desc); - - spin_lock_irqsave(&dev->lock, flags); - _ep->maxpacket = max; - ep->desc = desc; - - /* net2272_ep_reset() has already been called */ - ep->stopped = 0; - ep->wedged = 0; - - /* set speed-dependent max packet */ - net2272_ep_write(ep, EP_MAXPKT0, max & 0xff); - net2272_ep_write(ep, EP_MAXPKT1, (max & 0xff00) >> 8); - - /* set type, direction, address; reset fifo counters */ - net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH); - tmp = usb_endpoint_type(desc); - if (usb_endpoint_xfer_bulk(desc)) { - /* catch some particularly blatant driver bugs */ - if ((dev->gadget.speed == USB_SPEED_HIGH && max != 512) || - (dev->gadget.speed == USB_SPEED_FULL && max > 64)) { - spin_unlock_irqrestore(&dev->lock, flags); - return -ERANGE; - } - } - ep->is_iso = usb_endpoint_xfer_isoc(desc) ? 1 : 0; - tmp <<= ENDPOINT_TYPE; - tmp |= ((desc->bEndpointAddress & 0x0f) << ENDPOINT_NUMBER); - tmp |= usb_endpoint_dir_in(desc) << ENDPOINT_DIRECTION; - tmp |= (1 << ENDPOINT_ENABLE); - - /* for OUT transfers, block the rx fifo until a read is posted */ - ep->is_in = usb_endpoint_dir_in(desc); - if (!ep->is_in) - net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS); - - net2272_ep_write(ep, EP_CFG, tmp); - - /* enable irqs */ - tmp = (1 << ep->num) | net2272_read(dev, IRQENB0); - net2272_write(dev, IRQENB0, tmp); - - tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) - | net2272_ep_read(ep, EP_IRQENB); - net2272_ep_write(ep, EP_IRQENB, tmp); - - tmp = desc->bEndpointAddress; - dev_dbg(dev->dev, "enabled %s (ep%d%s-%s) max %04x cfg %02x\n", - _ep->name, tmp & 0x0f, PIPEDIR(tmp), - type_string(desc->bmAttributes), max, - net2272_ep_read(ep, EP_CFG)); - - spin_unlock_irqrestore(&dev->lock, flags); - return 0; -} - -static void net2272_ep_reset(struct net2272_ep *ep) -{ - u8 tmp; - - ep->desc = NULL; - INIT_LIST_HEAD(&ep->queue); - - usb_ep_set_maxpacket_limit(&ep->ep, ~0); - ep->ep.ops = &net2272_ep_ops; - - /* disable irqs, endpoint */ - net2272_ep_write(ep, EP_IRQENB, 0); - - /* init to our chosen defaults, notably so that we NAK OUT - * packets until the driver queues a read. - */ - tmp = (1 << NAK_OUT_PACKETS_MODE) | (1 << ALT_NAK_OUT_PACKETS); - net2272_ep_write(ep, EP_RSPSET, tmp); - - tmp = (1 << INTERRUPT_MODE) | (1 << HIDE_STATUS_PHASE); - if (ep->num != 0) - tmp |= (1 << ENDPOINT_TOGGLE) | (1 << ENDPOINT_HALT); - - net2272_ep_write(ep, EP_RSPCLR, tmp); - - /* scrub most status bits, and flush any fifo state */ - net2272_ep_write(ep, EP_STAT0, - (1 << DATA_IN_TOKEN_INTERRUPT) - | (1 << DATA_OUT_TOKEN_INTERRUPT) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)); - - net2272_ep_write(ep, EP_STAT1, - (1 << TIMEOUT) - | (1 << USB_OUT_ACK_SENT) - | (1 << USB_OUT_NAK_SENT) - | (1 << USB_IN_ACK_RCVD) - | (1 << USB_IN_NAK_SENT) - | (1 << USB_STALL_SENT) - | (1 << LOCAL_OUT_ZLP) - | (1 << BUFFER_FLUSH)); - - /* fifo size is handled separately */ -} - -static int net2272_disable(struct usb_ep *_ep) -{ - struct net2272_ep *ep; - unsigned long flags; - - ep = container_of(_ep, struct net2272_ep, ep); - if (!_ep || !ep->desc || _ep->name == ep0name) - return -EINVAL; - - spin_lock_irqsave(&ep->dev->lock, flags); - net2272_dequeue_all(ep); - net2272_ep_reset(ep); - - dev_vdbg(ep->dev->dev, "disabled %s\n", _ep->name); - - spin_unlock_irqrestore(&ep->dev->lock, flags); - return 0; -} - -/*---------------------------------------------------------------------------*/ - -static struct usb_request * -net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) -{ - struct net2272_request *req; - - if (!_ep) - return NULL; - - req = kzalloc(sizeof(*req), gfp_flags); - if (!req) - return NULL; - - INIT_LIST_HEAD(&req->queue); - - return &req->req; -} - -static void -net2272_free_request(struct usb_ep *_ep, struct usb_request *_req) -{ - struct net2272_request *req; - - if (!_ep || !_req) - return; - - req = container_of(_req, struct net2272_request, req); - WARN_ON(!list_empty(&req->queue)); - kfree(req); -} - -static void -net2272_done(struct net2272_ep *ep, struct net2272_request *req, int status) -{ - struct net2272 *dev; - unsigned stopped = ep->stopped; - - if (ep->num == 0) { - if (ep->dev->protocol_stall) { - ep->stopped = 1; - set_halt(ep); - } - allow_status(ep); - } - - list_del_init(&req->queue); - - if (req->req.status == -EINPROGRESS) - req->req.status = status; - else - status = req->req.status; - - dev = ep->dev; - if (use_dma && ep->dma) - usb_gadget_unmap_request(&dev->gadget, &req->req, - ep->is_in); - - if (status && status != -ESHUTDOWN) - dev_vdbg(dev->dev, "complete %s req %p stat %d len %u/%u buf %p\n", - ep->ep.name, &req->req, status, - req->req.actual, req->req.length, req->req.buf); - - /* don't modify queue heads during completion callback */ - ep->stopped = 1; - spin_unlock(&dev->lock); - usb_gadget_giveback_request(&ep->ep, &req->req); - spin_lock(&dev->lock); - ep->stopped = stopped; -} - -static int -net2272_write_packet(struct net2272_ep *ep, u8 *buf, - struct net2272_request *req, unsigned max) -{ - u16 __iomem *ep_data = net2272_reg_addr(ep->dev, EP_DATA); - u16 *bufp; - unsigned length, count; - u8 tmp; - - length = min(req->req.length - req->req.actual, max); - req->req.actual += length; - - dev_vdbg(ep->dev->dev, "write packet %s req %p max %u len %u avail %u\n", - ep->ep.name, req, max, length, - (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0)); - - count = length; - bufp = (u16 *)buf; - - while (likely(count >= 2)) { - /* no byte-swap required; chip endian set during init */ - writew(*bufp++, ep_data); - count -= 2; - } - buf = (u8 *)bufp; - - /* write final byte by placing the NET2272 into 8-bit mode */ - if (unlikely(count)) { - tmp = net2272_read(ep->dev, LOCCTL); - net2272_write(ep->dev, LOCCTL, tmp & ~(1 << DATA_WIDTH)); - writeb(*buf, ep_data); - net2272_write(ep->dev, LOCCTL, tmp); - } - return length; -} - -/* returns: 0: still running, 1: completed, negative: errno */ -static int -net2272_write_fifo(struct net2272_ep *ep, struct net2272_request *req) -{ - u8 *buf; - unsigned count, max; - int status; - - dev_vdbg(ep->dev->dev, "write_fifo %s actual %d len %d\n", - ep->ep.name, req->req.actual, req->req.length); - - /* - * Keep loading the endpoint until the final packet is loaded, - * or the endpoint buffer is full. - */ - top: - /* - * Clear interrupt status - * - Packet Transmitted interrupt will become set again when the - * host successfully takes another packet - */ - net2272_ep_write(ep, EP_STAT0, (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)); - while (!(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_FULL))) { - buf = req->req.buf + req->req.actual; - prefetch(buf); - - /* force pagesel */ - net2272_ep_read(ep, EP_STAT0); - - max = (net2272_ep_read(ep, EP_AVAIL1) << 8) | - (net2272_ep_read(ep, EP_AVAIL0)); - - if (max < ep->ep.maxpacket) - max = (net2272_ep_read(ep, EP_AVAIL1) << 8) - | (net2272_ep_read(ep, EP_AVAIL0)); - - count = net2272_write_packet(ep, buf, req, max); - /* see if we are done */ - if (req->req.length == req->req.actual) { - /* validate short or zlp packet */ - if (count < ep->ep.maxpacket) - set_fifo_bytecount(ep, 0); - net2272_done(ep, req, 0); - - if (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, - struct net2272_request, - queue); - status = net2272_kick_dma(ep, req); - - if (status < 0) - if ((net2272_ep_read(ep, EP_STAT0) - & (1 << BUFFER_EMPTY))) - goto top; - } - return 1; - } - net2272_ep_write(ep, EP_STAT0, (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)); - } - return 0; -} - -static void -net2272_out_flush(struct net2272_ep *ep) -{ - ASSERT_OUT_NAKING(ep); - - net2272_ep_write(ep, EP_STAT0, (1 << DATA_OUT_TOKEN_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT)); - net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH); -} - -static int -net2272_read_packet(struct net2272_ep *ep, u8 *buf, - struct net2272_request *req, unsigned avail) -{ - u16 __iomem *ep_data = net2272_reg_addr(ep->dev, EP_DATA); - unsigned is_short; - u16 *bufp; - - req->req.actual += avail; - - dev_vdbg(ep->dev->dev, "read packet %s req %p len %u avail %u\n", - ep->ep.name, req, avail, - (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0)); - - is_short = (avail < ep->ep.maxpacket); - - if (unlikely(avail == 0)) { - /* remove any zlp from the buffer */ - (void)readw(ep_data); - return is_short; - } - - /* Ensure we get the final byte */ - if (unlikely(avail % 2)) - avail++; - bufp = (u16 *)buf; - - do { - *bufp++ = readw(ep_data); - avail -= 2; - } while (avail); - - /* - * To avoid false endpoint available race condition must read - * ep stat0 twice in the case of a short transfer - */ - if (net2272_ep_read(ep, EP_STAT0) & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) - net2272_ep_read(ep, EP_STAT0); - - return is_short; -} - -static int -net2272_read_fifo(struct net2272_ep *ep, struct net2272_request *req) -{ - u8 *buf; - unsigned is_short; - int count; - int tmp; - int cleanup = 0; - - dev_vdbg(ep->dev->dev, "read_fifo %s actual %d len %d\n", - ep->ep.name, req->req.actual, req->req.length); - - top: - do { - buf = req->req.buf + req->req.actual; - prefetchw(buf); - - count = (net2272_ep_read(ep, EP_AVAIL1) << 8) - | net2272_ep_read(ep, EP_AVAIL0); - - net2272_ep_write(ep, EP_STAT0, - (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) | - (1 << DATA_PACKET_RECEIVED_INTERRUPT)); - - tmp = req->req.length - req->req.actual; - - if (count > tmp) { - if ((tmp % ep->ep.maxpacket) != 0) { - dev_err(ep->dev->dev, - "%s out fifo %d bytes, expected %d\n", - ep->ep.name, count, tmp); - cleanup = 1; - } - count = (tmp > 0) ? tmp : 0; - } - - is_short = net2272_read_packet(ep, buf, req, count); - - /* completion */ - if (unlikely(cleanup || is_short || - req->req.actual == req->req.length)) { - - if (cleanup) { - net2272_out_flush(ep); - net2272_done(ep, req, -EOVERFLOW); - } else - net2272_done(ep, req, 0); - - /* re-initialize endpoint transfer registers - * otherwise they may result in erroneous pre-validation - * for subsequent control reads - */ - if (unlikely(ep->num == 0)) { - net2272_ep_write(ep, EP_TRANSFER2, 0); - net2272_ep_write(ep, EP_TRANSFER1, 0); - net2272_ep_write(ep, EP_TRANSFER0, 0); - } - - if (!list_empty(&ep->queue)) { - int status; - - req = list_entry(ep->queue.next, - struct net2272_request, queue); - status = net2272_kick_dma(ep, req); - if ((status < 0) && - !(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_EMPTY))) - goto top; - } - return 1; - } - } while (!(net2272_ep_read(ep, EP_STAT0) & (1 << BUFFER_EMPTY))); - - return 0; -} - -static void -net2272_pio_advance(struct net2272_ep *ep) -{ - struct net2272_request *req; - - if (unlikely(list_empty(&ep->queue))) - return; - - req = list_entry(ep->queue.next, struct net2272_request, queue); - (ep->is_in ? net2272_write_fifo : net2272_read_fifo)(ep, req); -} - -/* returns 0 on success, else negative errno */ -static int -net2272_request_dma(struct net2272 *dev, unsigned ep, u32 buf, - unsigned len, unsigned dir) -{ - dev_vdbg(dev->dev, "request_dma ep %d buf %08x len %d dir %d\n", - ep, buf, len, dir); - - /* The NET2272 only supports a single dma channel */ - if (dev->dma_busy) - return -EBUSY; - /* - * EP_TRANSFER (used to determine the number of bytes received - * in an OUT transfer) is 24 bits wide; don't ask for more than that. - */ - if ((dir == 1) && (len > 0x1000000)) - return -EINVAL; - - dev->dma_busy = 1; - - /* initialize platform's dma */ -#ifdef CONFIG_USB_PCI - /* NET2272 addr, buffer addr, length, etc. */ - switch (dev->dev_id) { - case PCI_DEVICE_ID_RDK1: - /* Setup PLX 9054 DMA mode */ - writel((1 << LOCAL_BUS_WIDTH) | - (1 << TA_READY_INPUT_ENABLE) | - (0 << LOCAL_BURST_ENABLE) | - (1 << DONE_INTERRUPT_ENABLE) | - (1 << LOCAL_ADDRESSING_MODE) | - (1 << DEMAND_MODE) | - (1 << DMA_EOT_ENABLE) | - (1 << FAST_SLOW_TERMINATE_MODE_SELECT) | - (1 << DMA_CHANNEL_INTERRUPT_SELECT), - dev->rdk1.plx9054_base_addr + DMAMODE0); - - writel(0x100000, dev->rdk1.plx9054_base_addr + DMALADR0); - writel(buf, dev->rdk1.plx9054_base_addr + DMAPADR0); - writel(len, dev->rdk1.plx9054_base_addr + DMASIZ0); - writel((dir << DIRECTION_OF_TRANSFER) | - (1 << INTERRUPT_AFTER_TERMINAL_COUNT), - dev->rdk1.plx9054_base_addr + DMADPR0); - writel((1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE) | - readl(dev->rdk1.plx9054_base_addr + INTCSR), - dev->rdk1.plx9054_base_addr + INTCSR); - - break; - } -#endif - - net2272_write(dev, DMAREQ, - (0 << DMA_BUFFER_VALID) | - (1 << DMA_REQUEST_ENABLE) | - (1 << DMA_CONTROL_DACK) | - (dev->dma_eot_polarity << EOT_POLARITY) | - (dev->dma_dack_polarity << DACK_POLARITY) | - (dev->dma_dreq_polarity << DREQ_POLARITY) | - ((ep >> 1) << DMA_ENDPOINT_SELECT)); - - (void) net2272_read(dev, SCRATCH); - - return 0; -} - -static void -net2272_start_dma(struct net2272 *dev) -{ - /* start platform's dma controller */ -#ifdef CONFIG_USB_PCI - switch (dev->dev_id) { - case PCI_DEVICE_ID_RDK1: - writeb((1 << CHANNEL_ENABLE) | (1 << CHANNEL_START), - dev->rdk1.plx9054_base_addr + DMACSR0); - break; - } -#endif -} - -/* returns 0 on success, else negative errno */ -static int -net2272_kick_dma(struct net2272_ep *ep, struct net2272_request *req) -{ - unsigned size; - u8 tmp; - - if (!use_dma || (ep->num < 1) || (ep->num > 2) || !ep->dma) - return -EINVAL; - - /* don't use dma for odd-length transfers - * otherwise, we'd need to deal with the last byte with pio - */ - if (req->req.length & 1) - return -EINVAL; - - dev_vdbg(ep->dev->dev, "kick_dma %s req %p dma %08llx\n", - ep->ep.name, req, (unsigned long long) req->req.dma); - - net2272_ep_write(ep, EP_RSPSET, 1 << ALT_NAK_OUT_PACKETS); - - /* The NET2272 can only use DMA on one endpoint at a time */ - if (ep->dev->dma_busy) - return -EBUSY; - - /* Make sure we only DMA an even number of bytes (we'll use - * pio to complete the transfer) - */ - size = req->req.length; - size &= ~1; - - /* device-to-host transfer */ - if (ep->is_in) { - /* initialize platform's dma controller */ - if (net2272_request_dma(ep->dev, ep->num, req->req.dma, size, 0)) - /* unable to obtain DMA channel; return error and use pio mode */ - return -EBUSY; - req->req.actual += size; - - /* host-to-device transfer */ - } else { - tmp = net2272_ep_read(ep, EP_STAT0); - - /* initialize platform's dma controller */ - if (net2272_request_dma(ep->dev, ep->num, req->req.dma, size, 1)) - /* unable to obtain DMA channel; return error and use pio mode */ - return -EBUSY; - - if (!(tmp & (1 << BUFFER_EMPTY))) - ep->not_empty = 1; - else - ep->not_empty = 0; - - - /* allow the endpoint's buffer to fill */ - net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS); - - /* this transfer completed and data's already in the fifo - * return error so pio gets used. - */ - if (tmp & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { - - /* deassert dreq */ - net2272_write(ep->dev, DMAREQ, - (0 << DMA_BUFFER_VALID) | - (0 << DMA_REQUEST_ENABLE) | - (1 << DMA_CONTROL_DACK) | - (ep->dev->dma_eot_polarity << EOT_POLARITY) | - (ep->dev->dma_dack_polarity << DACK_POLARITY) | - (ep->dev->dma_dreq_polarity << DREQ_POLARITY) | - ((ep->num >> 1) << DMA_ENDPOINT_SELECT)); - - return -EBUSY; - } - } - - /* Don't use per-packet interrupts: use dma interrupts only */ - net2272_ep_write(ep, EP_IRQENB, 0); - - net2272_start_dma(ep->dev); - - return 0; -} - -static void net2272_cancel_dma(struct net2272 *dev) -{ -#ifdef CONFIG_USB_PCI - switch (dev->dev_id) { - case PCI_DEVICE_ID_RDK1: - writeb(0, dev->rdk1.plx9054_base_addr + DMACSR0); - writeb(1 << CHANNEL_ABORT, dev->rdk1.plx9054_base_addr + DMACSR0); - while (!(readb(dev->rdk1.plx9054_base_addr + DMACSR0) & - (1 << CHANNEL_DONE))) - continue; /* wait for dma to stabalize */ - - /* dma abort generates an interrupt */ - writeb(1 << CHANNEL_CLEAR_INTERRUPT, - dev->rdk1.plx9054_base_addr + DMACSR0); - break; - } -#endif - - dev->dma_busy = 0; -} - -/*---------------------------------------------------------------------------*/ - -static int -net2272_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) -{ - struct net2272_request *req; - struct net2272_ep *ep; - struct net2272 *dev; - unsigned long flags; - int status = -1; - u8 s; - - req = container_of(_req, struct net2272_request, req); - if (!_req || !_req->complete || !_req->buf - || !list_empty(&req->queue)) - return -EINVAL; - ep = container_of(_ep, struct net2272_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return -EINVAL; - dev = ep->dev; - if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - /* set up dma mapping in case the caller didn't */ - if (use_dma && ep->dma) { - status = usb_gadget_map_request(&dev->gadget, _req, - ep->is_in); - if (status) - return status; - } - - dev_vdbg(dev->dev, "%s queue req %p, len %d buf %p dma %08llx %s\n", - _ep->name, _req, _req->length, _req->buf, - (unsigned long long) _req->dma, _req->zero ? "zero" : "!zero"); - - spin_lock_irqsave(&dev->lock, flags); - - _req->status = -EINPROGRESS; - _req->actual = 0; - - /* kickstart this i/o queue? */ - if (list_empty(&ep->queue) && !ep->stopped) { - /* maybe there's no control data, just status ack */ - if (ep->num == 0 && _req->length == 0) { - net2272_done(ep, req, 0); - dev_vdbg(dev->dev, "%s status ack\n", ep->ep.name); - goto done; - } - - /* Return zlp, don't let it block subsequent packets */ - s = net2272_ep_read(ep, EP_STAT0); - if (s & (1 << BUFFER_EMPTY)) { - /* Buffer is empty check for a blocking zlp, handle it */ - if ((s & (1 << NAK_OUT_PACKETS)) && - net2272_ep_read(ep, EP_STAT1) & (1 << LOCAL_OUT_ZLP)) { - dev_dbg(dev->dev, "WARNING: returning ZLP short packet termination!\n"); - /* - * Request is going to terminate with a short packet ... - * hope the client is ready for it! - */ - status = net2272_read_fifo(ep, req); - /* clear short packet naking */ - net2272_ep_write(ep, EP_STAT0, (1 << NAK_OUT_PACKETS)); - goto done; - } - } - - /* try dma first */ - status = net2272_kick_dma(ep, req); - - if (status < 0) { - /* dma failed (most likely in use by another endpoint) - * fallback to pio - */ - status = 0; - - if (ep->is_in) - status = net2272_write_fifo(ep, req); - else { - s = net2272_ep_read(ep, EP_STAT0); - if ((s & (1 << BUFFER_EMPTY)) == 0) - status = net2272_read_fifo(ep, req); - } - - if (unlikely(status != 0)) { - if (status > 0) - status = 0; - req = NULL; - } - } - } - if (likely(req)) - list_add_tail(&req->queue, &ep->queue); - - if (likely(!list_empty(&ep->queue))) - net2272_ep_write(ep, EP_RSPCLR, 1 << ALT_NAK_OUT_PACKETS); - done: - spin_unlock_irqrestore(&dev->lock, flags); - - return 0; -} - -/* dequeue ALL requests */ -static void -net2272_dequeue_all(struct net2272_ep *ep) -{ - struct net2272_request *req; - - /* called with spinlock held */ - ep->stopped = 1; - - while (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, - struct net2272_request, - queue); - net2272_done(ep, req, -ESHUTDOWN); - } -} - -/* dequeue JUST ONE request */ -static int -net2272_dequeue(struct usb_ep *_ep, struct usb_request *_req) -{ - struct net2272_ep *ep; - struct net2272_request *req = NULL, *iter; - unsigned long flags; - int stopped; - - ep = container_of(_ep, struct net2272_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0) || !_req) - return -EINVAL; - - spin_lock_irqsave(&ep->dev->lock, flags); - stopped = ep->stopped; - ep->stopped = 1; - - /* make sure it's still queued on this endpoint */ - list_for_each_entry(iter, &ep->queue, queue) { - if (&iter->req != _req) - continue; - req = iter; - break; - } - if (!req) { - ep->stopped = stopped; - spin_unlock_irqrestore(&ep->dev->lock, flags); - return -EINVAL; - } - - /* queue head may be partially complete */ - if (ep->queue.next == &req->queue) { - dev_dbg(ep->dev->dev, "unlink (%s) pio\n", _ep->name); - net2272_done(ep, req, -ECONNRESET); - } - ep->stopped = stopped; - - spin_unlock_irqrestore(&ep->dev->lock, flags); - return 0; -} - -/*---------------------------------------------------------------------------*/ - -static int -net2272_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) -{ - struct net2272_ep *ep; - unsigned long flags; - int ret = 0; - - ep = container_of(_ep, struct net2272_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return -EINVAL; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - if (ep->desc /* not ep0 */ && usb_endpoint_xfer_isoc(ep->desc)) - return -EINVAL; - - spin_lock_irqsave(&ep->dev->lock, flags); - if (!list_empty(&ep->queue)) - ret = -EAGAIN; - else if (ep->is_in && value && net2272_fifo_status(_ep) != 0) - ret = -EAGAIN; - else { - dev_vdbg(ep->dev->dev, "%s %s %s\n", _ep->name, - value ? "set" : "clear", - wedged ? "wedge" : "halt"); - /* set/clear */ - if (value) { - if (ep->num == 0) - ep->dev->protocol_stall = 1; - else - set_halt(ep); - if (wedged) - ep->wedged = 1; - } else { - clear_halt(ep); - ep->wedged = 0; - } - } - spin_unlock_irqrestore(&ep->dev->lock, flags); - - return ret; -} - -static int -net2272_set_halt(struct usb_ep *_ep, int value) -{ - return net2272_set_halt_and_wedge(_ep, value, 0); -} - -static int -net2272_set_wedge(struct usb_ep *_ep) -{ - if (!_ep || _ep->name == ep0name) - return -EINVAL; - return net2272_set_halt_and_wedge(_ep, 1, 1); -} - -static int -net2272_fifo_status(struct usb_ep *_ep) -{ - struct net2272_ep *ep; - u16 avail; - - ep = container_of(_ep, struct net2272_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return -ENODEV; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return -ESHUTDOWN; - - avail = net2272_ep_read(ep, EP_AVAIL1) << 8; - avail |= net2272_ep_read(ep, EP_AVAIL0); - if (avail > ep->fifo_size) - return -EOVERFLOW; - if (ep->is_in) - avail = ep->fifo_size - avail; - return avail; -} - -static void -net2272_fifo_flush(struct usb_ep *_ep) -{ - struct net2272_ep *ep; - - ep = container_of(_ep, struct net2272_ep, ep); - if (!_ep || (!ep->desc && ep->num != 0)) - return; - if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) - return; - - net2272_ep_write(ep, EP_STAT1, 1 << BUFFER_FLUSH); -} - -static const struct usb_ep_ops net2272_ep_ops = { - .enable = net2272_enable, - .disable = net2272_disable, - - .alloc_request = net2272_alloc_request, - .free_request = net2272_free_request, - - .queue = net2272_queue, - .dequeue = net2272_dequeue, - - .set_halt = net2272_set_halt, - .set_wedge = net2272_set_wedge, - .fifo_status = net2272_fifo_status, - .fifo_flush = net2272_fifo_flush, -}; - -/*---------------------------------------------------------------------------*/ - -static int -net2272_get_frame(struct usb_gadget *_gadget) -{ - struct net2272 *dev; - unsigned long flags; - u16 ret; - - if (!_gadget) - return -ENODEV; - dev = container_of(_gadget, struct net2272, gadget); - spin_lock_irqsave(&dev->lock, flags); - - ret = net2272_read(dev, FRAME1) << 8; - ret |= net2272_read(dev, FRAME0); - - spin_unlock_irqrestore(&dev->lock, flags); - return ret; -} - -static int -net2272_wakeup(struct usb_gadget *_gadget) -{ - struct net2272 *dev; - u8 tmp; - unsigned long flags; - - if (!_gadget) - return 0; - dev = container_of(_gadget, struct net2272, gadget); - - spin_lock_irqsave(&dev->lock, flags); - tmp = net2272_read(dev, USBCTL0); - if (tmp & (1 << IO_WAKEUP_ENABLE)) - net2272_write(dev, USBCTL1, (1 << GENERATE_RESUME)); - - spin_unlock_irqrestore(&dev->lock, flags); - - return 0; -} - -static int -net2272_set_selfpowered(struct usb_gadget *_gadget, int value) -{ - if (!_gadget) - return -ENODEV; - - _gadget->is_selfpowered = (value != 0); - - return 0; -} - -static int -net2272_pullup(struct usb_gadget *_gadget, int is_on) -{ - struct net2272 *dev; - u8 tmp; - unsigned long flags; - - if (!_gadget) - return -ENODEV; - dev = container_of(_gadget, struct net2272, gadget); - - spin_lock_irqsave(&dev->lock, flags); - tmp = net2272_read(dev, USBCTL0); - dev->softconnect = (is_on != 0); - if (is_on) - tmp |= (1 << USB_DETECT_ENABLE); - else - tmp &= ~(1 << USB_DETECT_ENABLE); - net2272_write(dev, USBCTL0, tmp); - spin_unlock_irqrestore(&dev->lock, flags); - - return 0; -} - -static int net2272_start(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver); -static int net2272_stop(struct usb_gadget *_gadget); -static void net2272_async_callbacks(struct usb_gadget *_gadget, bool enable); - -static const struct usb_gadget_ops net2272_ops = { - .get_frame = net2272_get_frame, - .wakeup = net2272_wakeup, - .set_selfpowered = net2272_set_selfpowered, - .pullup = net2272_pullup, - .udc_start = net2272_start, - .udc_stop = net2272_stop, - .udc_async_callbacks = net2272_async_callbacks, -}; - -/*---------------------------------------------------------------------------*/ - -static ssize_t -registers_show(struct device *_dev, struct device_attribute *attr, char *buf) -{ - struct net2272 *dev; - char *next; - unsigned size, t; - unsigned long flags; - u8 t1, t2; - int i; - const char *s; - - dev = dev_get_drvdata(_dev); - next = buf; - size = PAGE_SIZE; - spin_lock_irqsave(&dev->lock, flags); - - /* Main Control Registers */ - t = scnprintf(next, size, "%s version %s," - "chiprev %02x, locctl %02x\n" - "irqenb0 %02x irqenb1 %02x " - "irqstat0 %02x irqstat1 %02x\n", - driver_name, driver_vers, dev->chiprev, - net2272_read(dev, LOCCTL), - net2272_read(dev, IRQENB0), - net2272_read(dev, IRQENB1), - net2272_read(dev, IRQSTAT0), - net2272_read(dev, IRQSTAT1)); - size -= t; - next += t; - - /* DMA */ - t1 = net2272_read(dev, DMAREQ); - t = scnprintf(next, size, "\ndmareq %02x: %s %s%s%s%s\n", - t1, ep_name[(t1 & 0x01) + 1], - t1 & (1 << DMA_CONTROL_DACK) ? "dack " : "", - t1 & (1 << DMA_REQUEST_ENABLE) ? "reqenb " : "", - t1 & (1 << DMA_REQUEST) ? "req " : "", - t1 & (1 << DMA_BUFFER_VALID) ? "valid " : ""); - size -= t; - next += t; - - /* USB Control Registers */ - t1 = net2272_read(dev, USBCTL1); - if (t1 & (1 << VBUS_PIN)) { - if (t1 & (1 << USB_HIGH_SPEED)) - s = "high speed"; - else if (dev->gadget.speed == USB_SPEED_UNKNOWN) - s = "powered"; - else - s = "full speed"; - } else - s = "not attached"; - t = scnprintf(next, size, - "usbctl0 %02x usbctl1 %02x addr 0x%02x (%s)\n", - net2272_read(dev, USBCTL0), t1, - net2272_read(dev, OURADDR), s); - size -= t; - next += t; - - /* Endpoint Registers */ - for (i = 0; i < 4; ++i) { - struct net2272_ep *ep; - - ep = &dev->ep[i]; - if (i && !ep->desc) - continue; - - t1 = net2272_ep_read(ep, EP_CFG); - t2 = net2272_ep_read(ep, EP_RSPSET); - t = scnprintf(next, size, - "\n%s\tcfg %02x rsp (%02x) %s%s%s%s%s%s%s%s" - "irqenb %02x\n", - ep->ep.name, t1, t2, - (t2 & (1 << ALT_NAK_OUT_PACKETS)) ? "NAK " : "", - (t2 & (1 << HIDE_STATUS_PHASE)) ? "hide " : "", - (t2 & (1 << AUTOVALIDATE)) ? "auto " : "", - (t2 & (1 << INTERRUPT_MODE)) ? "interrupt " : "", - (t2 & (1 << CONTROL_STATUS_PHASE_HANDSHAKE)) ? "status " : "", - (t2 & (1 << NAK_OUT_PACKETS_MODE)) ? "NAKmode " : "", - (t2 & (1 << ENDPOINT_TOGGLE)) ? "DATA1 " : "DATA0 ", - (t2 & (1 << ENDPOINT_HALT)) ? "HALT " : "", - net2272_ep_read(ep, EP_IRQENB)); - size -= t; - next += t; - - t = scnprintf(next, size, - "\tstat0 %02x stat1 %02x avail %04x " - "(ep%d%s-%s)%s\n", - net2272_ep_read(ep, EP_STAT0), - net2272_ep_read(ep, EP_STAT1), - (net2272_ep_read(ep, EP_AVAIL1) << 8) | net2272_ep_read(ep, EP_AVAIL0), - t1 & 0x0f, - ep->is_in ? "in" : "out", - type_string(t1 >> 5), - ep->stopped ? "*" : ""); - size -= t; - next += t; - - t = scnprintf(next, size, - "\tep_transfer %06x\n", - ((net2272_ep_read(ep, EP_TRANSFER2) & 0xff) << 16) | - ((net2272_ep_read(ep, EP_TRANSFER1) & 0xff) << 8) | - ((net2272_ep_read(ep, EP_TRANSFER0) & 0xff))); - size -= t; - next += t; - - t1 = net2272_ep_read(ep, EP_BUFF_STATES) & 0x03; - t2 = (net2272_ep_read(ep, EP_BUFF_STATES) >> 2) & 0x03; - t = scnprintf(next, size, - "\tbuf-a %s buf-b %s\n", - buf_state_string(t1), - buf_state_string(t2)); - size -= t; - next += t; - } - - spin_unlock_irqrestore(&dev->lock, flags); - - return PAGE_SIZE - size; -} -static DEVICE_ATTR_RO(registers); - -/*---------------------------------------------------------------------------*/ - -static void -net2272_set_fifo_mode(struct net2272 *dev, int mode) -{ - u8 tmp; - - tmp = net2272_read(dev, LOCCTL) & 0x3f; - tmp |= (mode << 6); - net2272_write(dev, LOCCTL, tmp); - - INIT_LIST_HEAD(&dev->gadget.ep_list); - - /* always ep-a, ep-c ... maybe not ep-b */ - list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list); - - switch (mode) { - case 0: - list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); - dev->ep[1].fifo_size = dev->ep[2].fifo_size = 512; - break; - case 1: - list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); - dev->ep[1].fifo_size = 1024; - dev->ep[2].fifo_size = 512; - break; - case 2: - list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); - dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024; - break; - case 3: - dev->ep[1].fifo_size = 1024; - break; - } - - /* ep-c is always 2 512 byte buffers */ - list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); - dev->ep[3].fifo_size = 512; -} - -/*---------------------------------------------------------------------------*/ - -static void -net2272_usb_reset(struct net2272 *dev) -{ - dev->gadget.speed = USB_SPEED_UNKNOWN; - - net2272_cancel_dma(dev); - - net2272_write(dev, IRQENB0, 0); - net2272_write(dev, IRQENB1, 0); - - /* clear irq state */ - net2272_write(dev, IRQSTAT0, 0xff); - net2272_write(dev, IRQSTAT1, ~(1 << SUSPEND_REQUEST_INTERRUPT)); - - net2272_write(dev, DMAREQ, - (0 << DMA_BUFFER_VALID) | - (0 << DMA_REQUEST_ENABLE) | - (1 << DMA_CONTROL_DACK) | - (dev->dma_eot_polarity << EOT_POLARITY) | - (dev->dma_dack_polarity << DACK_POLARITY) | - (dev->dma_dreq_polarity << DREQ_POLARITY) | - ((dma_ep >> 1) << DMA_ENDPOINT_SELECT)); - - net2272_cancel_dma(dev); - net2272_set_fifo_mode(dev, (fifo_mode <= 3) ? fifo_mode : 0); - - /* Set the NET2272 ep fifo data width to 16-bit mode and for correct byte swapping - * note that the higher level gadget drivers are expected to convert data to little endian. - * Enable byte swap for your local bus/cpu if needed by setting BYTE_SWAP in LOCCTL here - */ - net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) | (1 << DATA_WIDTH)); - net2272_write(dev, LOCCTL1, (dma_mode << DMA_MODE)); -} - -static void -net2272_usb_reinit(struct net2272 *dev) -{ - int i; - - /* basic endpoint init */ - for (i = 0; i < 4; ++i) { - struct net2272_ep *ep = &dev->ep[i]; - - ep->ep.name = ep_name[i]; - ep->dev = dev; - ep->num = i; - ep->not_empty = 0; - - if (use_dma && ep->num == dma_ep) - ep->dma = 1; - - if (i > 0 && i <= 3) - ep->fifo_size = 512; - else - ep->fifo_size = 64; - net2272_ep_reset(ep); - - if (i == 0) { - ep->ep.caps.type_control = true; - } else { - ep->ep.caps.type_iso = true; - ep->ep.caps.type_bulk = true; - ep->ep.caps.type_int = true; - } - - ep->ep.caps.dir_in = true; - ep->ep.caps.dir_out = true; - } - usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64); - - dev->gadget.ep0 = &dev->ep[0].ep; - dev->ep[0].stopped = 0; - INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); -} - -static void -net2272_ep0_start(struct net2272 *dev) -{ - struct net2272_ep *ep0 = &dev->ep[0]; - - net2272_ep_write(ep0, EP_RSPSET, - (1 << NAK_OUT_PACKETS_MODE) | - (1 << ALT_NAK_OUT_PACKETS)); - net2272_ep_write(ep0, EP_RSPCLR, - (1 << HIDE_STATUS_PHASE) | - (1 << CONTROL_STATUS_PHASE_HANDSHAKE)); - net2272_write(dev, USBCTL0, - (dev->softconnect << USB_DETECT_ENABLE) | - (1 << USB_ROOT_PORT_WAKEUP_ENABLE) | - (1 << IO_WAKEUP_ENABLE)); - net2272_write(dev, IRQENB0, - (1 << SETUP_PACKET_INTERRUPT_ENABLE) | - (1 << ENDPOINT_0_INTERRUPT_ENABLE) | - (1 << DMA_DONE_INTERRUPT_ENABLE)); - net2272_write(dev, IRQENB1, - (1 << VBUS_INTERRUPT_ENABLE) | - (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) | - (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE)); -} - -/* when a driver is successfully registered, it will receive - * control requests including set_configuration(), which enables - * non-control requests. then usb traffic follows until a - * disconnect is reported. then a host may connect again, or - * the driver might get unbound. - */ -static int net2272_start(struct usb_gadget *_gadget, - struct usb_gadget_driver *driver) -{ - struct net2272 *dev; - unsigned i; - - if (!driver || !driver->setup || - driver->max_speed != USB_SPEED_HIGH) - return -EINVAL; - - dev = container_of(_gadget, struct net2272, gadget); - - for (i = 0; i < 4; ++i) - dev->ep[i].irqs = 0; - /* hook up the driver ... */ - dev->softconnect = 1; - dev->driver = driver; - - /* ... then enable host detection and ep0; and we're ready - * for set_configuration as well as eventual disconnect. - */ - net2272_ep0_start(dev); - - return 0; -} - -static void -stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver) -{ - int i; - - /* don't disconnect if it's not connected */ - if (dev->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; - - /* stop hardware; prevent new request submissions; - * and kill any outstanding requests. - */ - net2272_usb_reset(dev); - for (i = 0; i < 4; ++i) - net2272_dequeue_all(&dev->ep[i]); - - /* report disconnect; the driver is already quiesced */ - if (dev->async_callbacks && driver) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - } - - net2272_usb_reinit(dev); -} - -static int net2272_stop(struct usb_gadget *_gadget) -{ - struct net2272 *dev; - unsigned long flags; - - dev = container_of(_gadget, struct net2272, gadget); - - spin_lock_irqsave(&dev->lock, flags); - stop_activity(dev, NULL); - spin_unlock_irqrestore(&dev->lock, flags); - - dev->driver = NULL; - - return 0; -} - -static void net2272_async_callbacks(struct usb_gadget *_gadget, bool enable) -{ - struct net2272 *dev = container_of(_gadget, struct net2272, gadget); - - spin_lock_irq(&dev->lock); - dev->async_callbacks = enable; - spin_unlock_irq(&dev->lock); -} - -/*---------------------------------------------------------------------------*/ -/* handle ep-a/ep-b dma completions */ -static void -net2272_handle_dma(struct net2272_ep *ep) -{ - struct net2272_request *req; - unsigned len; - int status; - - if (!list_empty(&ep->queue)) - req = list_entry(ep->queue.next, - struct net2272_request, queue); - else - req = NULL; - - dev_vdbg(ep->dev->dev, "handle_dma %s req %p\n", ep->ep.name, req); - - /* Ensure DREQ is de-asserted */ - net2272_write(ep->dev, DMAREQ, - (0 << DMA_BUFFER_VALID) - | (0 << DMA_REQUEST_ENABLE) - | (1 << DMA_CONTROL_DACK) - | (ep->dev->dma_eot_polarity << EOT_POLARITY) - | (ep->dev->dma_dack_polarity << DACK_POLARITY) - | (ep->dev->dma_dreq_polarity << DREQ_POLARITY) - | (ep->dma << DMA_ENDPOINT_SELECT)); - - ep->dev->dma_busy = 0; - - net2272_ep_write(ep, EP_IRQENB, - (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) - | net2272_ep_read(ep, EP_IRQENB)); - - /* device-to-host transfer completed */ - if (ep->is_in) { - /* validate a short packet or zlp if necessary */ - if ((req->req.length % ep->ep.maxpacket != 0) || - req->req.zero) - set_fifo_bytecount(ep, 0); - - net2272_done(ep, req, 0); - if (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, - struct net2272_request, queue); - status = net2272_kick_dma(ep, req); - if (status < 0) - net2272_pio_advance(ep); - } - - /* host-to-device transfer completed */ - } else { - /* terminated with a short packet? */ - if (net2272_read(ep->dev, IRQSTAT0) & - (1 << DMA_DONE_INTERRUPT)) { - /* abort system dma */ - net2272_cancel_dma(ep->dev); - } - - /* EP_TRANSFER will contain the number of bytes - * actually received. - * NOTE: There is no overflow detection on EP_TRANSFER: - * We can't deal with transfers larger than 2^24 bytes! - */ - len = (net2272_ep_read(ep, EP_TRANSFER2) << 16) - | (net2272_ep_read(ep, EP_TRANSFER1) << 8) - | (net2272_ep_read(ep, EP_TRANSFER0)); - - if (ep->not_empty) - len += 4; - - req->req.actual += len; - - /* get any remaining data */ - net2272_pio_advance(ep); - } -} - -/*---------------------------------------------------------------------------*/ - -static void -net2272_handle_ep(struct net2272_ep *ep) -{ - struct net2272_request *req; - u8 stat0, stat1; - - if (!list_empty(&ep->queue)) - req = list_entry(ep->queue.next, - struct net2272_request, queue); - else - req = NULL; - - /* ack all, and handle what we care about */ - stat0 = net2272_ep_read(ep, EP_STAT0); - stat1 = net2272_ep_read(ep, EP_STAT1); - ep->irqs++; - - dev_vdbg(ep->dev->dev, "%s ack ep_stat0 %02x, ep_stat1 %02x, req %p\n", - ep->ep.name, stat0, stat1, req ? &req->req : NULL); - - net2272_ep_write(ep, EP_STAT0, stat0 & - ~((1 << NAK_OUT_PACKETS) - | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT))); - net2272_ep_write(ep, EP_STAT1, stat1); - - /* data packet(s) received (in the fifo, OUT) - * direction must be validated, otherwise control read status phase - * could be interpreted as a valid packet - */ - if (!ep->is_in && (stat0 & (1 << DATA_PACKET_RECEIVED_INTERRUPT))) - net2272_pio_advance(ep); - /* data packet(s) transmitted (IN) */ - else if (stat0 & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) - net2272_pio_advance(ep); -} - -static struct net2272_ep * -net2272_get_ep_by_addr(struct net2272 *dev, u16 wIndex) -{ - struct net2272_ep *ep; - - if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) - return &dev->ep[0]; - - list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { - u8 bEndpointAddress; - - if (!ep->desc) - continue; - bEndpointAddress = ep->desc->bEndpointAddress; - if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) - continue; - if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) - return ep; - } - return NULL; -} - -/* - * USB Test Packet: - * JKJKJKJK * 9 - * JJKKJJKK * 8 - * JJJJKKKK * 8 - * JJJJJJJKKKKKKK * 8 - * JJJJJJJK * 8 - * {JKKKKKKK * 10}, JK - */ -static const u8 net2272_test_packet[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, - 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, - 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFD, 0x7E -}; - -static void -net2272_set_test_mode(struct net2272 *dev, int mode) -{ - int i; - - /* Disable all net2272 interrupts: - * Nothing but a power cycle should stop the test. - */ - net2272_write(dev, IRQENB0, 0x00); - net2272_write(dev, IRQENB1, 0x00); - - /* Force tranceiver to high-speed */ - net2272_write(dev, XCVRDIAG, 1 << FORCE_HIGH_SPEED); - - net2272_write(dev, PAGESEL, 0); - net2272_write(dev, EP_STAT0, 1 << DATA_PACKET_TRANSMITTED_INTERRUPT); - net2272_write(dev, EP_RSPCLR, - (1 << CONTROL_STATUS_PHASE_HANDSHAKE) - | (1 << HIDE_STATUS_PHASE)); - net2272_write(dev, EP_CFG, 1 << ENDPOINT_DIRECTION); - net2272_write(dev, EP_STAT1, 1 << BUFFER_FLUSH); - - /* wait for status phase to complete */ - while (!(net2272_read(dev, EP_STAT0) & - (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))) - ; - - /* Enable test mode */ - net2272_write(dev, USBTEST, mode); - - /* load test packet */ - if (mode == USB_TEST_PACKET) { - /* switch to 8 bit mode */ - net2272_write(dev, LOCCTL, net2272_read(dev, LOCCTL) & - ~(1 << DATA_WIDTH)); - - for (i = 0; i < sizeof(net2272_test_packet); ++i) - net2272_write(dev, EP_DATA, net2272_test_packet[i]); - - /* Validate test packet */ - net2272_write(dev, EP_TRANSFER0, 0); - } -} - -static void -net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat) -{ - struct net2272_ep *ep; - u8 num, scratch; - - /* starting a control request? */ - if (unlikely(stat & (1 << SETUP_PACKET_INTERRUPT))) { - union { - u8 raw[8]; - struct usb_ctrlrequest r; - } u; - int tmp = 0; - struct net2272_request *req; - - if (dev->gadget.speed == USB_SPEED_UNKNOWN) { - if (net2272_read(dev, USBCTL1) & (1 << USB_HIGH_SPEED)) - dev->gadget.speed = USB_SPEED_HIGH; - else - dev->gadget.speed = USB_SPEED_FULL; - dev_dbg(dev->dev, "%s\n", - usb_speed_string(dev->gadget.speed)); - } - - ep = &dev->ep[0]; - ep->irqs++; - - /* make sure any leftover interrupt state is cleared */ - stat &= ~(1 << ENDPOINT_0_INTERRUPT); - while (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, - struct net2272_request, queue); - net2272_done(ep, req, - (req->req.actual == req->req.length) ? 0 : -EPROTO); - } - ep->stopped = 0; - dev->protocol_stall = 0; - net2272_ep_write(ep, EP_STAT0, - (1 << DATA_IN_TOKEN_INTERRUPT) - | (1 << DATA_OUT_TOKEN_INTERRUPT) - | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) - | (1 << DATA_PACKET_RECEIVED_INTERRUPT) - | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)); - net2272_ep_write(ep, EP_STAT1, - (1 << TIMEOUT) - | (1 << USB_OUT_ACK_SENT) - | (1 << USB_OUT_NAK_SENT) - | (1 << USB_IN_ACK_RCVD) - | (1 << USB_IN_NAK_SENT) - | (1 << USB_STALL_SENT) - | (1 << LOCAL_OUT_ZLP)); - - /* - * Ensure Control Read pre-validation setting is beyond maximum size - * - Control Writes can leave non-zero values in EP_TRANSFER. If - * an EP0 transfer following the Control Write is a Control Read, - * the NET2272 sees the non-zero EP_TRANSFER as an unexpected - * pre-validation count. - * - Setting EP_TRANSFER beyond the maximum EP0 transfer size ensures - * the pre-validation count cannot cause an unexpected validatation - */ - net2272_write(dev, PAGESEL, 0); - net2272_write(dev, EP_TRANSFER2, 0xff); - net2272_write(dev, EP_TRANSFER1, 0xff); - net2272_write(dev, EP_TRANSFER0, 0xff); - - u.raw[0] = net2272_read(dev, SETUP0); - u.raw[1] = net2272_read(dev, SETUP1); - u.raw[2] = net2272_read(dev, SETUP2); - u.raw[3] = net2272_read(dev, SETUP3); - u.raw[4] = net2272_read(dev, SETUP4); - u.raw[5] = net2272_read(dev, SETUP5); - u.raw[6] = net2272_read(dev, SETUP6); - u.raw[7] = net2272_read(dev, SETUP7); - /* - * If you have a big endian cpu make sure le16_to_cpus - * performs the proper byte swapping here... - */ - le16_to_cpus(&u.r.wValue); - le16_to_cpus(&u.r.wIndex); - le16_to_cpus(&u.r.wLength); - - /* ack the irq */ - net2272_write(dev, IRQSTAT0, 1 << SETUP_PACKET_INTERRUPT); - stat ^= (1 << SETUP_PACKET_INTERRUPT); - - /* watch control traffic at the token level, and force - * synchronization before letting the status phase happen. - */ - ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; - if (ep->is_in) { - scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) - | (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE) - | (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE); - stop_out_naking(ep); - } else - scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) - | (1 << DATA_OUT_TOKEN_INTERRUPT_ENABLE) - | (1 << DATA_IN_TOKEN_INTERRUPT_ENABLE); - net2272_ep_write(ep, EP_IRQENB, scratch); - - if ((u.r.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) - goto delegate; - switch (u.r.bRequest) { - case USB_REQ_GET_STATUS: { - struct net2272_ep *e; - u16 status = 0; - - switch (u.r.bRequestType & USB_RECIP_MASK) { - case USB_RECIP_ENDPOINT: - e = net2272_get_ep_by_addr(dev, u.r.wIndex); - if (!e || u.r.wLength > 2) - goto do_stall; - if (net2272_ep_read(e, EP_RSPSET) & (1 << ENDPOINT_HALT)) - status = cpu_to_le16(1); - else - status = cpu_to_le16(0); - - /* don't bother with a request object! */ - net2272_ep_write(&dev->ep[0], EP_IRQENB, 0); - writew(status, net2272_reg_addr(dev, EP_DATA)); - set_fifo_bytecount(&dev->ep[0], 0); - allow_status(ep); - dev_vdbg(dev->dev, "%s stat %02x\n", - ep->ep.name, status); - goto next_endpoints; - case USB_RECIP_DEVICE: - if (u.r.wLength > 2) - goto do_stall; - if (dev->gadget.is_selfpowered) - status = (1 << USB_DEVICE_SELF_POWERED); - - /* don't bother with a request object! */ - net2272_ep_write(&dev->ep[0], EP_IRQENB, 0); - writew(status, net2272_reg_addr(dev, EP_DATA)); - set_fifo_bytecount(&dev->ep[0], 0); - allow_status(ep); - dev_vdbg(dev->dev, "device stat %02x\n", status); - goto next_endpoints; - case USB_RECIP_INTERFACE: - if (u.r.wLength > 2) - goto do_stall; - - /* don't bother with a request object! */ - net2272_ep_write(&dev->ep[0], EP_IRQENB, 0); - writew(status, net2272_reg_addr(dev, EP_DATA)); - set_fifo_bytecount(&dev->ep[0], 0); - allow_status(ep); - dev_vdbg(dev->dev, "interface status %02x\n", status); - goto next_endpoints; - } - - break; - } - case USB_REQ_CLEAR_FEATURE: { - struct net2272_ep *e; - - if (u.r.bRequestType != USB_RECIP_ENDPOINT) - goto delegate; - if (u.r.wValue != USB_ENDPOINT_HALT || - u.r.wLength != 0) - goto do_stall; - e = net2272_get_ep_by_addr(dev, u.r.wIndex); - if (!e) - goto do_stall; - if (e->wedged) { - dev_vdbg(dev->dev, "%s wedged, halt not cleared\n", - ep->ep.name); - } else { - dev_vdbg(dev->dev, "%s clear halt\n", ep->ep.name); - clear_halt(e); - } - allow_status(ep); - goto next_endpoints; - } - case USB_REQ_SET_FEATURE: { - struct net2272_ep *e; - - if (u.r.bRequestType == USB_RECIP_DEVICE) { - if (u.r.wIndex != NORMAL_OPERATION) - net2272_set_test_mode(dev, (u.r.wIndex >> 8)); - allow_status(ep); - dev_vdbg(dev->dev, "test mode: %d\n", u.r.wIndex); - goto next_endpoints; - } else if (u.r.bRequestType != USB_RECIP_ENDPOINT) - goto delegate; - if (u.r.wValue != USB_ENDPOINT_HALT || - u.r.wLength != 0) - goto do_stall; - e = net2272_get_ep_by_addr(dev, u.r.wIndex); - if (!e) - goto do_stall; - set_halt(e); - allow_status(ep); - dev_vdbg(dev->dev, "%s set halt\n", ep->ep.name); - goto next_endpoints; - } - case USB_REQ_SET_ADDRESS: { - net2272_write(dev, OURADDR, u.r.wValue & 0xff); - allow_status(ep); - break; - } - default: - delegate: - dev_vdbg(dev->dev, "setup %02x.%02x v%04x i%04x " - "ep_cfg %08x\n", - u.r.bRequestType, u.r.bRequest, - u.r.wValue, u.r.wIndex, - net2272_ep_read(ep, EP_CFG)); - if (dev->async_callbacks) { - spin_unlock(&dev->lock); - tmp = dev->driver->setup(&dev->gadget, &u.r); - spin_lock(&dev->lock); - } - } - - /* stall ep0 on error */ - if (tmp < 0) { - do_stall: - dev_vdbg(dev->dev, "req %02x.%02x protocol STALL; stat %d\n", - u.r.bRequestType, u.r.bRequest, tmp); - dev->protocol_stall = 1; - } - /* endpoint dma irq? */ - } else if (stat & (1 << DMA_DONE_INTERRUPT)) { - net2272_cancel_dma(dev); - net2272_write(dev, IRQSTAT0, 1 << DMA_DONE_INTERRUPT); - stat &= ~(1 << DMA_DONE_INTERRUPT); - num = (net2272_read(dev, DMAREQ) & (1 << DMA_ENDPOINT_SELECT)) - ? 2 : 1; - - ep = &dev->ep[num]; - net2272_handle_dma(ep); - } - - next_endpoints: - /* endpoint data irq? */ - scratch = stat & 0x0f; - stat &= ~0x0f; - for (num = 0; scratch; num++) { - u8 t; - - /* does this endpoint's FIFO and queue need tending? */ - t = 1 << num; - if ((scratch & t) == 0) - continue; - scratch ^= t; - - ep = &dev->ep[num]; - net2272_handle_ep(ep); - } - - /* some interrupts we can just ignore */ - stat &= ~(1 << SOF_INTERRUPT); - - if (stat) - dev_dbg(dev->dev, "unhandled irqstat0 %02x\n", stat); -} - -static void -net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat) -{ - u8 tmp, mask; - - /* after disconnect there's nothing else to do! */ - tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT); - mask = (1 << USB_HIGH_SPEED) | (1 << USB_FULL_SPEED); - - if (stat & tmp) { - bool reset = false; - bool disconnect = false; - - /* - * Ignore disconnects and resets if the speed hasn't been set. - * VBUS can bounce and there's always an initial reset. - */ - net2272_write(dev, IRQSTAT1, tmp); - if (dev->gadget.speed != USB_SPEED_UNKNOWN) { - if ((stat & (1 << VBUS_INTERRUPT)) && - (net2272_read(dev, USBCTL1) & - (1 << VBUS_PIN)) == 0) { - disconnect = true; - dev_dbg(dev->dev, "disconnect %s\n", - dev->driver->driver.name); - } else if ((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) && - (net2272_read(dev, USBCTL1) & mask) - == 0) { - reset = true; - dev_dbg(dev->dev, "reset %s\n", - dev->driver->driver.name); - } - - if (disconnect || reset) { - stop_activity(dev, dev->driver); - net2272_ep0_start(dev); - if (dev->async_callbacks) { - spin_unlock(&dev->lock); - if (reset) - usb_gadget_udc_reset(&dev->gadget, dev->driver); - else - (dev->driver->disconnect)(&dev->gadget); - spin_lock(&dev->lock); - } - return; - } - } - stat &= ~tmp; - - if (!stat) - return; - } - - tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT); - if (stat & tmp) { - net2272_write(dev, IRQSTAT1, tmp); - if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) { - if (dev->async_callbacks && dev->driver->suspend) - dev->driver->suspend(&dev->gadget); - if (!enable_suspend) { - stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT); - dev_dbg(dev->dev, "Suspend disabled, ignoring\n"); - } - } else { - if (dev->async_callbacks && dev->driver->resume) - dev->driver->resume(&dev->gadget); - } - stat &= ~tmp; - } - - /* clear any other status/irqs */ - if (stat) - net2272_write(dev, IRQSTAT1, stat); - - /* some status we can just ignore */ - stat &= ~((1 << CONTROL_STATUS_INTERRUPT) - | (1 << SUSPEND_REQUEST_INTERRUPT) - | (1 << RESUME_INTERRUPT)); - if (!stat) - return; - else - dev_dbg(dev->dev, "unhandled irqstat1 %02x\n", stat); -} - -static irqreturn_t net2272_irq(int irq, void *_dev) -{ - struct net2272 *dev = _dev; -#if defined(PLX_PCI_RDK) || defined(PLX_PCI_RDK2) - u32 intcsr; -#endif -#if defined(PLX_PCI_RDK) - u8 dmareq; -#endif - spin_lock(&dev->lock); -#if defined(PLX_PCI_RDK) - intcsr = readl(dev->rdk1.plx9054_base_addr + INTCSR); - - if ((intcsr & LOCAL_INTERRUPT_TEST) == LOCAL_INTERRUPT_TEST) { - writel(intcsr & ~(1 << PCI_INTERRUPT_ENABLE), - dev->rdk1.plx9054_base_addr + INTCSR); - net2272_handle_stat1_irqs(dev, net2272_read(dev, IRQSTAT1)); - net2272_handle_stat0_irqs(dev, net2272_read(dev, IRQSTAT0)); - intcsr = readl(dev->rdk1.plx9054_base_addr + INTCSR); - writel(intcsr | (1 << PCI_INTERRUPT_ENABLE), - dev->rdk1.plx9054_base_addr + INTCSR); - } - if ((intcsr & DMA_CHANNEL_0_TEST) == DMA_CHANNEL_0_TEST) { - writeb((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)), - dev->rdk1.plx9054_base_addr + DMACSR0); - - dmareq = net2272_read(dev, DMAREQ); - if (dmareq & 0x01) - net2272_handle_dma(&dev->ep[2]); - else - net2272_handle_dma(&dev->ep[1]); - } -#endif -#if defined(PLX_PCI_RDK2) - /* see if PCI int for us by checking irqstat */ - intcsr = readl(dev->rdk2.fpga_base_addr + RDK2_IRQSTAT); - if (!(intcsr & (1 << NET2272_PCI_IRQ))) { - spin_unlock(&dev->lock); - return IRQ_NONE; - } - /* check dma interrupts */ -#endif - /* 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)); -#endif - spin_unlock(&dev->lock); - - return IRQ_HANDLED; -} - -static int net2272_present(struct net2272 *dev) -{ - /* - * Quick test to see if CPU can communicate properly with the NET2272. - * Verifies connection using writes and reads to write/read and - * read-only registers. - * - * This routine is strongly recommended especially during early bring-up - * of new hardware, however for designs that do not apply Power On System - * Tests (POST) it may discarded (or perhaps minimized). - */ - unsigned int ii; - u8 val, refval; - - /* Verify NET2272 write/read SCRATCH register can write and read */ - refval = net2272_read(dev, SCRATCH); - for (ii = 0; ii < 0x100; ii += 7) { - net2272_write(dev, SCRATCH, ii); - val = net2272_read(dev, SCRATCH); - if (val != ii) { - dev_dbg(dev->dev, - "%s: write/read SCRATCH register test failed: " - "wrote:0x%2.2x, read:0x%2.2x\n", - __func__, ii, val); - return -EINVAL; - } - } - /* To be nice, we write the original SCRATCH value back: */ - net2272_write(dev, SCRATCH, refval); - - /* Verify NET2272 CHIPREV register is read-only: */ - refval = net2272_read(dev, CHIPREV_2272); - for (ii = 0; ii < 0x100; ii += 7) { - net2272_write(dev, CHIPREV_2272, ii); - val = net2272_read(dev, CHIPREV_2272); - if (val != refval) { - dev_dbg(dev->dev, - "%s: write/read CHIPREV register test failed: " - "wrote 0x%2.2x, read:0x%2.2x expected:0x%2.2x\n", - __func__, ii, val, refval); - return -EINVAL; - } - } - - /* - * Verify NET2272's "NET2270 legacy revision" register - * - NET2272 has two revision registers. The NET2270 legacy revision - * register should read the same value, regardless of the NET2272 - * silicon revision. The legacy register applies to NET2270 - * firmware being applied to the NET2272. - */ - val = net2272_read(dev, CHIPREV_LEGACY); - if (val != NET2270_LEGACY_REV) { - /* - * Unexpected legacy revision value - * - Perhaps the chip is a NET2270? - */ - dev_dbg(dev->dev, - "%s: WARNING: UNEXPECTED NET2272 LEGACY REGISTER VALUE:\n" - " - CHIPREV_LEGACY: expected 0x%2.2x, got:0x%2.2x. (Not NET2272?)\n", - __func__, NET2270_LEGACY_REV, val); - return -EINVAL; - } - - /* - * Verify NET2272 silicon revision - * - This revision register is appropriate for the silicon version - * of the NET2272 - */ - val = net2272_read(dev, CHIPREV_2272); - switch (val) { - case CHIPREV_NET2272_R1: - /* - * NET2272 Rev 1 has DMA related errata: - * - Newer silicon (Rev 1A or better) required - */ - dev_dbg(dev->dev, - "%s: Rev 1 detected: newer silicon recommended for DMA support\n", - __func__); - break; - case CHIPREV_NET2272_R1A: - break; - default: - /* NET2272 silicon version *may* not work with this firmware */ - dev_dbg(dev->dev, - "%s: unexpected silicon revision register value: " - " CHIPREV_2272: 0x%2.2x\n", - __func__, val); - /* - * Return Success, even though the chip rev is not an expected value - * - Older, pre-built firmware can attempt to operate on newer silicon - * - Often, new silicon is perfectly compatible - */ - } - - /* Success: NET2272 checks out OK */ - return 0; -} - -static void -net2272_gadget_release(struct device *_dev) -{ - struct net2272 *dev = container_of(_dev, struct net2272, gadget.dev); - - kfree(dev); -} - -/*---------------------------------------------------------------------------*/ - -static void -net2272_remove(struct net2272 *dev) -{ - if (dev->added) - usb_del_gadget(&dev->gadget); - free_irq(dev->irq, dev); - iounmap(dev->base_addr); - device_remove_file(dev->dev, &dev_attr_registers); - - dev_info(dev->dev, "unbind\n"); -} - -static struct net2272 *net2272_probe_init(struct device *dev, unsigned int irq) -{ - struct net2272 *ret; - - if (!irq) { - dev_dbg(dev, "No IRQ!\n"); - return ERR_PTR(-ENODEV); - } - - /* alloc, and start init */ - ret = kzalloc(sizeof(*ret), GFP_KERNEL); - if (!ret) - return ERR_PTR(-ENOMEM); - - spin_lock_init(&ret->lock); - ret->irq = irq; - ret->dev = dev; - ret->gadget.ops = &net2272_ops; - ret->gadget.max_speed = USB_SPEED_HIGH; - - /* the "gadget" abstracts/virtualizes the controller */ - ret->gadget.name = driver_name; - usb_initialize_gadget(dev, &ret->gadget, net2272_gadget_release); - - return ret; -} - -static int -net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) -{ - int ret; - - /* See if there... */ - if (net2272_present(dev)) { - dev_warn(dev->dev, "2272 not found!\n"); - ret = -ENODEV; - goto err; - } - - net2272_usb_reset(dev); - net2272_usb_reinit(dev); - - ret = request_irq(dev->irq, net2272_irq, irqflags, driver_name, dev); - if (ret) { - dev_err(dev->dev, "request interrupt %i failed\n", dev->irq); - goto err; - } - - dev->chiprev = net2272_read(dev, CHIPREV_2272); - - /* done */ - dev_info(dev->dev, "%s\n", driver_desc); - dev_info(dev->dev, "irq %i, mem %p, chip rev %04x, dma %s\n", - dev->irq, dev->base_addr, dev->chiprev, - dma_mode_string()); - dev_info(dev->dev, "version: %s\n", driver_vers); - - ret = device_create_file(dev->dev, &dev_attr_registers); - if (ret) - goto err_irq; - - ret = usb_add_gadget(&dev->gadget); - if (ret) - goto err_add_udc; - dev->added = 1; - - return 0; - -err_add_udc: - device_remove_file(dev->dev, &dev_attr_registers); - err_irq: - free_irq(dev->irq, dev); - err: - return ret; -} - -#ifdef CONFIG_USB_PCI - -/* - * wrap this driver around the specified device, but - * don't respond over USB until a gadget driver binds to us - */ - -static int -net2272_rdk1_probe(struct pci_dev *pdev, struct net2272 *dev) -{ - unsigned long resource, len, tmp; - void __iomem *mem_mapped_addr[4]; - int ret, i; - - /* - * BAR 0 holds PLX 9054 config registers - * BAR 1 is i/o memory; unused here - * BAR 2 holds EPLD config registers - * BAR 3 holds NET2272 registers - */ - - /* Find and map all address spaces */ - for (i = 0; i < 4; ++i) { - if (i == 1) - continue; /* BAR1 unused */ - - resource = pci_resource_start(pdev, i); - len = pci_resource_len(pdev, i); - - if (!request_mem_region(resource, len, driver_name)) { - dev_dbg(dev->dev, "controller already in use\n"); - ret = -EBUSY; - goto err; - } - - mem_mapped_addr[i] = ioremap(resource, len); - if (mem_mapped_addr[i] == NULL) { - release_mem_region(resource, len); - dev_dbg(dev->dev, "can't map memory\n"); - ret = -EFAULT; - goto err; - } - } - - dev->rdk1.plx9054_base_addr = mem_mapped_addr[0]; - dev->rdk1.epld_base_addr = mem_mapped_addr[2]; - dev->base_addr = mem_mapped_addr[3]; - - /* Set PLX 9054 bus width (16 bits) */ - tmp = readl(dev->rdk1.plx9054_base_addr + LBRD1); - writel((tmp & ~(3 << MEMORY_SPACE_LOCAL_BUS_WIDTH)) | W16_BIT, - dev->rdk1.plx9054_base_addr + LBRD1); - - /* Enable PLX 9054 Interrupts */ - writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) | - (1 << PCI_INTERRUPT_ENABLE) | - (1 << LOCAL_INTERRUPT_INPUT_ENABLE), - dev->rdk1.plx9054_base_addr + INTCSR); - - writeb((1 << CHANNEL_CLEAR_INTERRUPT | (0 << CHANNEL_ENABLE)), - dev->rdk1.plx9054_base_addr + DMACSR0); - - /* reset */ - writeb((1 << EPLD_DMA_ENABLE) | - (1 << DMA_CTL_DACK) | - (1 << DMA_TIMEOUT_ENABLE) | - (1 << USER) | - (0 << MPX_MODE) | - (1 << BUSWIDTH) | - (1 << NET2272_RESET), - dev->base_addr + EPLD_IO_CONTROL_REGISTER); - - mb(); - writeb(readb(dev->base_addr + EPLD_IO_CONTROL_REGISTER) & - ~(1 << NET2272_RESET), - dev->base_addr + EPLD_IO_CONTROL_REGISTER); - udelay(200); - - return 0; - - err: - while (--i >= 0) { - if (i == 1) - continue; /* BAR1 unused */ - iounmap(mem_mapped_addr[i]); - release_mem_region(pci_resource_start(pdev, i), - pci_resource_len(pdev, i)); - } - - return ret; -} - -static int -net2272_rdk2_probe(struct pci_dev *pdev, struct net2272 *dev) -{ - unsigned long resource, len; - void __iomem *mem_mapped_addr[2]; - int ret, i; - - /* - * BAR 0 holds FGPA config registers - * BAR 1 holds NET2272 registers - */ - - /* Find and map all address spaces, bar2-3 unused in rdk 2 */ - for (i = 0; i < 2; ++i) { - resource = pci_resource_start(pdev, i); - len = pci_resource_len(pdev, i); - - if (!request_mem_region(resource, len, driver_name)) { - dev_dbg(dev->dev, "controller already in use\n"); - ret = -EBUSY; - goto err; - } - - mem_mapped_addr[i] = ioremap(resource, len); - if (mem_mapped_addr[i] == NULL) { - release_mem_region(resource, len); - dev_dbg(dev->dev, "can't map memory\n"); - ret = -EFAULT; - goto err; - } - } - - dev->rdk2.fpga_base_addr = mem_mapped_addr[0]; - dev->base_addr = mem_mapped_addr[1]; - - mb(); - /* Set 2272 bus width (16 bits) and reset */ - writel((1 << CHIP_RESET), dev->rdk2.fpga_base_addr + RDK2_LOCCTLRDK); - udelay(200); - writel((1 << BUS_WIDTH), dev->rdk2.fpga_base_addr + RDK2_LOCCTLRDK); - /* Print fpga version number */ - dev_info(dev->dev, "RDK2 FPGA version %08x\n", - readl(dev->rdk2.fpga_base_addr + RDK2_FPGAREV)); - /* Enable FPGA Interrupts */ - writel((1 << NET2272_PCI_IRQ), dev->rdk2.fpga_base_addr + RDK2_IRQENB); - - return 0; - - err: - while (--i >= 0) { - iounmap(mem_mapped_addr[i]); - release_mem_region(pci_resource_start(pdev, i), - pci_resource_len(pdev, i)); - } - - return ret; -} - -static int -net2272_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct net2272 *dev; - int ret; - - dev = net2272_probe_init(&pdev->dev, pdev->irq); - if (IS_ERR(dev)) - return PTR_ERR(dev); - dev->dev_id = pdev->device; - - if (pci_enable_device(pdev) < 0) { - ret = -ENODEV; - goto err_put; - } - - pci_set_master(pdev); - - switch (pdev->device) { - case PCI_DEVICE_ID_RDK1: ret = net2272_rdk1_probe(pdev, dev); break; - case PCI_DEVICE_ID_RDK2: ret = net2272_rdk2_probe(pdev, dev); break; - default: BUG(); - } - if (ret) - goto err_pci; - - ret = net2272_probe_fin(dev, 0); - if (ret) - goto err_pci; - - pci_set_drvdata(pdev, dev); - - return 0; - - err_pci: - pci_disable_device(pdev); - err_put: - usb_put_gadget(&dev->gadget); - - return ret; -} - -static void -net2272_rdk1_remove(struct pci_dev *pdev, struct net2272 *dev) -{ - int i; - - /* disable PLX 9054 interrupts */ - writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) & - ~(1 << PCI_INTERRUPT_ENABLE), - dev->rdk1.plx9054_base_addr + INTCSR); - - /* clean up resources allocated during probe() */ - iounmap(dev->rdk1.plx9054_base_addr); - iounmap(dev->rdk1.epld_base_addr); - - for (i = 0; i < 4; ++i) { - if (i == 1) - continue; /* BAR1 unused */ - release_mem_region(pci_resource_start(pdev, i), - pci_resource_len(pdev, i)); - } -} - -static void -net2272_rdk2_remove(struct pci_dev *pdev, struct net2272 *dev) -{ - int i; - - /* disable fpga interrupts - writel(readl(dev->rdk1.plx9054_base_addr + INTCSR) & - ~(1 << PCI_INTERRUPT_ENABLE), - dev->rdk1.plx9054_base_addr + INTCSR); - */ - - /* clean up resources allocated during probe() */ - iounmap(dev->rdk2.fpga_base_addr); - - for (i = 0; i < 2; ++i) - release_mem_region(pci_resource_start(pdev, i), - pci_resource_len(pdev, i)); -} - -static void -net2272_pci_remove(struct pci_dev *pdev) -{ - struct net2272 *dev = pci_get_drvdata(pdev); - - net2272_remove(dev); - - switch (pdev->device) { - case PCI_DEVICE_ID_RDK1: net2272_rdk1_remove(pdev, dev); break; - case PCI_DEVICE_ID_RDK2: net2272_rdk2_remove(pdev, dev); break; - default: BUG(); - } - - pci_disable_device(pdev); - - usb_put_gadget(&dev->gadget); -} - -/* Table of matching PCI IDs */ -static struct pci_device_id pci_ids[] = { - { /* RDK 1 card */ - .class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe), - .class_mask = 0, - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_RDK1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { /* RDK 2 card */ - .class = ((PCI_CLASS_BRIDGE_OTHER << 8) | 0xfe), - .class_mask = 0, - .vendor = PCI_VENDOR_ID_PLX, - .device = PCI_DEVICE_ID_RDK2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { } -}; -MODULE_DEVICE_TABLE(pci, pci_ids); - -static struct pci_driver net2272_pci_driver = { - .name = driver_name, - .id_table = pci_ids, - - .probe = net2272_pci_probe, - .remove = net2272_pci_remove, -}; - -static int net2272_pci_register(void) -{ - return pci_register_driver(&net2272_pci_driver); -} - -static void net2272_pci_unregister(void) -{ - pci_unregister_driver(&net2272_pci_driver); -} - -#else -static inline int net2272_pci_register(void) { return 0; } -static inline void net2272_pci_unregister(void) { } -#endif - -/*---------------------------------------------------------------------------*/ - -static int -net2272_plat_probe(struct platform_device *pdev) -{ - struct net2272 *dev; - int ret; - unsigned int irqflags; - resource_size_t base, len; - struct resource *iomem, *iomem_bus, *irq_res; - - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - iomem_bus = platform_get_resource(pdev, IORESOURCE_BUS, 0); - if (!irq_res || !iomem) { - dev_err(&pdev->dev, "must provide irq/base addr"); - return -EINVAL; - } - - dev = net2272_probe_init(&pdev->dev, irq_res->start); - if (IS_ERR(dev)) - return PTR_ERR(dev); - - irqflags = 0; - if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE) - irqflags |= IRQF_TRIGGER_RISING; - if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE) - irqflags |= IRQF_TRIGGER_FALLING; - if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL) - irqflags |= IRQF_TRIGGER_HIGH; - if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL) - irqflags |= IRQF_TRIGGER_LOW; - - base = iomem->start; - len = resource_size(iomem); - if (iomem_bus) - dev->base_shift = iomem_bus->start; - - if (!request_mem_region(base, len, driver_name)) { - dev_dbg(dev->dev, "get request memory region!\n"); - ret = -EBUSY; - goto err; - } - dev->base_addr = ioremap(base, len); - if (!dev->base_addr) { - dev_dbg(dev->dev, "can't map memory\n"); - ret = -EFAULT; - goto err_req; - } - - ret = net2272_probe_fin(dev, irqflags); - if (ret) - goto err_io; - - platform_set_drvdata(pdev, dev); - dev_info(&pdev->dev, "running in 16-bit, %sbyte swap local bus mode\n", - (net2272_read(dev, LOCCTL) & (1 << BYTE_SWAP)) ? "" : "no "); - - return 0; - - err_io: - iounmap(dev->base_addr); - err_req: - release_mem_region(base, len); - err: - usb_put_gadget(&dev->gadget); - - return ret; -} - -static void -net2272_plat_remove(struct platform_device *pdev) -{ - struct net2272 *dev = platform_get_drvdata(pdev); - - net2272_remove(dev); - - release_mem_region(pdev->resource[0].start, - resource_size(&pdev->resource[0])); - - usb_put_gadget(&dev->gadget); -} - -static struct platform_driver net2272_plat_driver = { - .probe = net2272_plat_probe, - .remove = net2272_plat_remove, - .driver = { - .name = driver_name, - }, - /* FIXME .suspend, .resume */ -}; -MODULE_ALIAS("platform:net2272"); - -static int __init net2272_init(void) -{ - int ret; - - ret = net2272_pci_register(); - if (ret) - return ret; - ret = platform_driver_register(&net2272_plat_driver); - if (ret) - goto err_pci; - return ret; - -err_pci: - net2272_pci_unregister(); - return ret; -} -module_init(net2272_init); - -static void __exit net2272_cleanup(void) -{ - net2272_pci_unregister(); - platform_driver_unregister(&net2272_plat_driver); -} -module_exit(net2272_cleanup); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("PLX Technology, Inc."); -MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h deleted file mode 100644 index a9994f737588..000000000000 --- a/drivers/usb/gadget/udc/net2272.h +++ /dev/null @@ -1,584 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * PLX NET2272 high/full speed USB device controller - * - * Copyright (C) 2005-2006 PLX Technology, Inc. - * Copyright (C) 2006-2011 Analog Devices, Inc. - */ - -#ifndef __NET2272_H__ -#define __NET2272_H__ - -/* Main Registers */ -#define REGADDRPTR 0x00 -#define REGDATA 0x01 -#define IRQSTAT0 0x02 -#define ENDPOINT_0_INTERRUPT 0 -#define ENDPOINT_A_INTERRUPT 1 -#define ENDPOINT_B_INTERRUPT 2 -#define ENDPOINT_C_INTERRUPT 3 -#define VIRTUALIZED_ENDPOINT_INTERRUPT 4 -#define SETUP_PACKET_INTERRUPT 5 -#define DMA_DONE_INTERRUPT 6 -#define SOF_INTERRUPT 7 -#define IRQSTAT1 0x03 -#define CONTROL_STATUS_INTERRUPT 1 -#define VBUS_INTERRUPT 2 -#define SUSPEND_REQUEST_INTERRUPT 3 -#define SUSPEND_REQUEST_CHANGE_INTERRUPT 4 -#define RESUME_INTERRUPT 5 -#define ROOT_PORT_RESET_INTERRUPT 6 -#define RESET_STATUS 7 -#define PAGESEL 0x04 -#define DMAREQ 0x1c -#define DMA_ENDPOINT_SELECT 0 -#define DREQ_POLARITY 1 -#define DACK_POLARITY 2 -#define EOT_POLARITY 3 -#define DMA_CONTROL_DACK 4 -#define DMA_REQUEST_ENABLE 5 -#define DMA_REQUEST 6 -#define DMA_BUFFER_VALID 7 -#define SCRATCH 0x1d -#define IRQENB0 0x20 -#define ENDPOINT_0_INTERRUPT_ENABLE 0 -#define ENDPOINT_A_INTERRUPT_ENABLE 1 -#define ENDPOINT_B_INTERRUPT_ENABLE 2 -#define ENDPOINT_C_INTERRUPT_ENABLE 3 -#define VIRTUALIZED_ENDPOINT_INTERRUPT_ENABLE 4 -#define SETUP_PACKET_INTERRUPT_ENABLE 5 -#define DMA_DONE_INTERRUPT_ENABLE 6 -#define SOF_INTERRUPT_ENABLE 7 -#define IRQENB1 0x21 -#define VBUS_INTERRUPT_ENABLE 2 -#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 -#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 4 -#define RESUME_INTERRUPT_ENABLE 5 -#define ROOT_PORT_RESET_INTERRUPT_ENABLE 6 -#define LOCCTL 0x22 -#define DATA_WIDTH 0 -#define LOCAL_CLOCK_OUTPUT 1 -#define LOCAL_CLOCK_OUTPUT_OFF 0 -#define LOCAL_CLOCK_OUTPUT_3_75MHZ 1 -#define LOCAL_CLOCK_OUTPUT_7_5MHZ 2 -#define LOCAL_CLOCK_OUTPUT_15MHZ 3 -#define LOCAL_CLOCK_OUTPUT_30MHZ 4 -#define LOCAL_CLOCK_OUTPUT_60MHZ 5 -#define DMA_SPLIT_BUS_MODE 4 -#define BYTE_SWAP 5 -#define BUFFER_CONFIGURATION 6 -#define BUFFER_CONFIGURATION_EPA512_EPB512 0 -#define BUFFER_CONFIGURATION_EPA1024_EPB512 1 -#define BUFFER_CONFIGURATION_EPA1024_EPB1024 2 -#define BUFFER_CONFIGURATION_EPA1024DB 3 -#define CHIPREV_LEGACY 0x23 -#define NET2270_LEGACY_REV 0x40 -#define LOCCTL1 0x24 -#define DMA_MODE 0 -#define SLOW_DREQ 0 -#define FAST_DREQ 1 -#define BURST_MODE 2 -#define DMA_DACK_ENABLE 2 -#define CHIPREV_2272 0x25 -#define CHIPREV_NET2272_R1 0x10 -#define CHIPREV_NET2272_R1A 0x11 -/* USB Registers */ -#define USBCTL0 0x18 -#define IO_WAKEUP_ENABLE 1 -#define USB_DETECT_ENABLE 3 -#define USB_ROOT_PORT_WAKEUP_ENABLE 5 -#define USBCTL1 0x19 -#define VBUS_PIN 0 -#define USB_FULL_SPEED 1 -#define USB_HIGH_SPEED 2 -#define GENERATE_RESUME 3 -#define VIRTUAL_ENDPOINT_ENABLE 4 -#define FRAME0 0x1a -#define FRAME1 0x1b -#define OURADDR 0x30 -#define FORCE_IMMEDIATE 7 -#define USBDIAG 0x31 -#define FORCE_TRANSMIT_CRC_ERROR 0 -#define PREVENT_TRANSMIT_BIT_STUFF 1 -#define FORCE_RECEIVE_ERROR 2 -#define FAST_TIMES 4 -#define USBTEST 0x32 -#define TEST_MODE_SELECT 0 -#define NORMAL_OPERATION 0 -#define XCVRDIAG 0x33 -#define FORCE_FULL_SPEED 2 -#define FORCE_HIGH_SPEED 3 -#define OPMODE 4 -#define NORMAL_OPERATION 0 -#define NON_DRIVING 1 -#define DISABLE_BITSTUFF_AND_NRZI_ENCODE 2 -#define LINESTATE 6 -#define SE0_STATE 0 -#define J_STATE 1 -#define K_STATE 2 -#define SE1_STATE 3 -#define VIRTOUT0 0x34 -#define VIRTOUT1 0x35 -#define VIRTIN0 0x36 -#define VIRTIN1 0x37 -#define SETUP0 0x40 -#define SETUP1 0x41 -#define SETUP2 0x42 -#define SETUP3 0x43 -#define SETUP4 0x44 -#define SETUP5 0x45 -#define SETUP6 0x46 -#define SETUP7 0x47 -/* Endpoint Registers (Paged via PAGESEL) */ -#define EP_DATA 0x05 -#define EP_STAT0 0x06 -#define DATA_IN_TOKEN_INTERRUPT 0 -#define DATA_OUT_TOKEN_INTERRUPT 1 -#define DATA_PACKET_TRANSMITTED_INTERRUPT 2 -#define DATA_PACKET_RECEIVED_INTERRUPT 3 -#define SHORT_PACKET_TRANSFERRED_INTERRUPT 4 -#define NAK_OUT_PACKETS 5 -#define BUFFER_EMPTY 6 -#define BUFFER_FULL 7 -#define EP_STAT1 0x07 -#define TIMEOUT 0 -#define USB_OUT_ACK_SENT 1 -#define USB_OUT_NAK_SENT 2 -#define USB_IN_ACK_RCVD 3 -#define USB_IN_NAK_SENT 4 -#define USB_STALL_SENT 5 -#define LOCAL_OUT_ZLP 6 -#define BUFFER_FLUSH 7 -#define EP_TRANSFER0 0x08 -#define EP_TRANSFER1 0x09 -#define EP_TRANSFER2 0x0a -#define EP_IRQENB 0x0b -#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0 -#define DATA_OUT_TOKEN_INTERRUPT_ENABLE 1 -#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2 -#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3 -#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 4 -#define EP_AVAIL0 0x0c -#define EP_AVAIL1 0x0d -#define EP_RSPCLR 0x0e -#define EP_RSPSET 0x0f -#define ENDPOINT_HALT 0 -#define ENDPOINT_TOGGLE 1 -#define NAK_OUT_PACKETS_MODE 2 -#define CONTROL_STATUS_PHASE_HANDSHAKE 3 -#define INTERRUPT_MODE 4 -#define AUTOVALIDATE 5 -#define HIDE_STATUS_PHASE 6 -#define ALT_NAK_OUT_PACKETS 7 -#define EP_MAXPKT0 0x28 -#define EP_MAXPKT1 0x29 -#define ADDITIONAL_TRANSACTION_OPPORTUNITIES 3 -#define NONE_ADDITIONAL_TRANSACTION 0 -#define ONE_ADDITIONAL_TRANSACTION 1 -#define TWO_ADDITIONAL_TRANSACTION 2 -#define EP_CFG 0x2a -#define ENDPOINT_NUMBER 0 -#define ENDPOINT_DIRECTION 4 -#define ENDPOINT_TYPE 5 -#define ENDPOINT_ENABLE 7 -#define EP_HBW 0x2b -#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 0 -#define DATA0_PID 0 -#define DATA1_PID 1 -#define DATA2_PID 2 -#define MDATA_PID 3 -#define EP_BUFF_STATES 0x2c -#define BUFFER_A_STATE 0 -#define BUFFER_B_STATE 2 -#define BUFF_FREE 0 -#define BUFF_VALID 1 -#define BUFF_LCL 2 -#define BUFF_USB 3 - -/*---------------------------------------------------------------------------*/ - -#define PCI_DEVICE_ID_RDK1 0x9054 - -/* PCI-RDK EPLD Registers */ -#define RDK_EPLD_IO_REGISTER1 0x00000000 -#define RDK_EPLD_USB_RESET 0 -#define RDK_EPLD_USB_POWERDOWN 1 -#define RDK_EPLD_USB_WAKEUP 2 -#define RDK_EPLD_USB_EOT 3 -#define RDK_EPLD_DPPULL 4 -#define RDK_EPLD_IO_REGISTER2 0x00000004 -#define RDK_EPLD_BUSWIDTH 0 -#define RDK_EPLD_USER 2 -#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3 -#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4 -#define RDK_EPLD_STATUS_REGISTER 0x00000008 -#define RDK_EPLD_USB_LRESET 0 -#define RDK_EPLD_REVISION_REGISTER 0x0000000c - -/* PCI-RDK PLX 9054 Registers */ -#define INTCSR 0x68 -#define PCI_INTERRUPT_ENABLE 8 -#define LOCAL_INTERRUPT_INPUT_ENABLE 11 -#define LOCAL_INPUT_INTERRUPT_ACTIVE 15 -#define LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE 18 -#define LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE 19 -#define DMA_CHANNEL_0_INTERRUPT_ACTIVE 21 -#define DMA_CHANNEL_1_INTERRUPT_ACTIVE 22 -#define CNTRL 0x6C -#define RELOAD_CONFIGURATION_REGISTERS 29 -#define PCI_ADAPTER_SOFTWARE_RESET 30 -#define DMAMODE0 0x80 -#define LOCAL_BUS_WIDTH 0 -#define INTERNAL_WAIT_STATES 2 -#define TA_READY_INPUT_ENABLE 6 -#define LOCAL_BURST_ENABLE 8 -#define SCATTER_GATHER_MODE 9 -#define DONE_INTERRUPT_ENABLE 10 -#define LOCAL_ADDRESSING_MODE 11 -#define DEMAND_MODE 12 -#define DMA_EOT_ENABLE 14 -#define FAST_SLOW_TERMINATE_MODE_SELECT 15 -#define DMA_CHANNEL_INTERRUPT_SELECT 17 -#define DMAPADR0 0x84 -#define DMALADR0 0x88 -#define DMASIZ0 0x8c -#define DMADPR0 0x90 -#define DESCRIPTOR_LOCATION 0 -#define END_OF_CHAIN 1 -#define INTERRUPT_AFTER_TERMINAL_COUNT 2 -#define DIRECTION_OF_TRANSFER 3 -#define DMACSR0 0xa8 -#define CHANNEL_ENABLE 0 -#define CHANNEL_START 1 -#define CHANNEL_ABORT 2 -#define CHANNEL_CLEAR_INTERRUPT 3 -#define CHANNEL_DONE 4 -#define DMATHR 0xb0 -#define LBRD1 0xf8 -#define MEMORY_SPACE_LOCAL_BUS_WIDTH 0 -#define W8_BIT 0 -#define W16_BIT 1 - -/* Special OR'ing of INTCSR bits */ -#define LOCAL_INTERRUPT_TEST \ - ((1 << LOCAL_INPUT_INTERRUPT_ACTIVE) | \ - (1 << LOCAL_INTERRUPT_INPUT_ENABLE)) - -#define DMA_CHANNEL_0_TEST \ - ((1 << DMA_CHANNEL_0_INTERRUPT_ACTIVE) | \ - (1 << LOCAL_DMA_CHANNEL_0_INTERRUPT_ENABLE)) - -#define DMA_CHANNEL_1_TEST \ - ((1 << DMA_CHANNEL_1_INTERRUPT_ACTIVE) | \ - (1 << LOCAL_DMA_CHANNEL_1_INTERRUPT_ENABLE)) - -/* EPLD Registers */ -#define RDK_EPLD_IO_REGISTER1 0x00000000 -#define RDK_EPLD_USB_RESET 0 -#define RDK_EPLD_USB_POWERDOWN 1 -#define RDK_EPLD_USB_WAKEUP 2 -#define RDK_EPLD_USB_EOT 3 -#define RDK_EPLD_DPPULL 4 -#define RDK_EPLD_IO_REGISTER2 0x00000004 -#define RDK_EPLD_BUSWIDTH 0 -#define RDK_EPLD_USER 2 -#define RDK_EPLD_RESET_INTERRUPT_ENABLE 3 -#define RDK_EPLD_DMA_TIMEOUT_ENABLE 4 -#define RDK_EPLD_STATUS_REGISTER 0x00000008 -#define RDK_EPLD_USB_LRESET 0 -#define RDK_EPLD_REVISION_REGISTER 0x0000000c - -#define EPLD_IO_CONTROL_REGISTER 0x400 -#define NET2272_RESET 0 -#define BUSWIDTH 1 -#define MPX_MODE 3 -#define USER 4 -#define DMA_TIMEOUT_ENABLE 5 -#define DMA_CTL_DACK 6 -#define EPLD_DMA_ENABLE 7 -#define EPLD_DMA_CONTROL_REGISTER 0x800 -#define SPLIT_DMA_MODE 0 -#define SPLIT_DMA_DIRECTION 1 -#define SPLIT_DMA_ENABLE 2 -#define SPLIT_DMA_INTERRUPT_ENABLE 3 -#define SPLIT_DMA_INTERRUPT 4 -#define EPLD_DMA_MODE 5 -#define EPLD_DMA_CONTROLLER_ENABLE 7 -#define SPLIT_DMA_ADDRESS_LOW 0xc00 -#define SPLIT_DMA_ADDRESS_HIGH 0x1000 -#define SPLIT_DMA_BYTE_COUNT_LOW 0x1400 -#define SPLIT_DMA_BYTE_COUNT_HIGH 0x1800 -#define EPLD_REVISION_REGISTER 0x1c00 -#define SPLIT_DMA_RAM 0x4000 -#define DMA_RAM_SIZE 0x1000 - -/*---------------------------------------------------------------------------*/ - -#define PCI_DEVICE_ID_RDK2 0x3272 - -/* PCI-RDK version 2 registers */ - -/* Main Control Registers */ - -#define RDK2_IRQENB 0x00 -#define RDK2_IRQSTAT 0x04 -#define PB7 23 -#define PB6 22 -#define PB5 21 -#define PB4 20 -#define PB3 19 -#define PB2 18 -#define PB1 17 -#define PB0 16 -#define GP3 23 -#define GP2 23 -#define GP1 23 -#define GP0 23 -#define DMA_RETRY_ABORT 6 -#define DMA_PAUSE_DONE 5 -#define DMA_ABORT_DONE 4 -#define DMA_OUT_FIFO_TRANSFER_DONE 3 -#define DMA_LOCAL_DONE 2 -#define DMA_PCI_DONE 1 -#define NET2272_PCI_IRQ 0 - -#define RDK2_LOCCTLRDK 0x08 -#define CHIP_RESET 3 -#define SPLIT_DMA 2 -#define MULTIPLEX_MODE 1 -#define BUS_WIDTH 0 - -#define RDK2_GPIOCTL 0x10 -#define GP3_OUT_ENABLE 7 -#define GP2_OUT_ENABLE 6 -#define GP1_OUT_ENABLE 5 -#define GP0_OUT_ENABLE 4 -#define GP3_DATA 3 -#define GP2_DATA 2 -#define GP1_DATA 1 -#define GP0_DATA 0 - -#define RDK2_LEDSW 0x14 -#define LED3 27 -#define LED2 26 -#define LED1 25 -#define LED0 24 -#define PBUTTON 16 -#define DIPSW 0 - -#define RDK2_DIAG 0x18 -#define RDK2_FAST_TIMES 2 -#define FORCE_PCI_SERR 1 -#define FORCE_PCI_INT 0 -#define RDK2_FPGAREV 0x1C - -/* Dma Control registers */ -#define RDK2_DMACTL 0x80 -#define ADDR_HOLD 24 -#define RETRY_COUNT 16 /* 23:16 */ -#define FIFO_THRESHOLD 11 /* 15:11 */ -#define MEM_WRITE_INVALIDATE 10 -#define READ_MULTIPLE 9 -#define READ_LINE 8 -#define RDK2_DMA_MODE 6 /* 7:6 */ -#define CONTROL_DACK 5 -#define EOT_ENABLE 4 -#define EOT_POLARITY 3 -#define DACK_POLARITY 2 -#define DREQ_POLARITY 1 -#define DMA_ENABLE 0 - -#define RDK2_DMASTAT 0x84 -#define GATHER_COUNT 12 /* 14:12 */ -#define FIFO_COUNT 6 /* 11:6 */ -#define FIFO_FLUSH 5 -#define FIFO_TRANSFER 4 -#define PAUSE_DONE 3 -#define ABORT_DONE 2 -#define DMA_ABORT 1 -#define DMA_START 0 - -#define RDK2_DMAPCICOUNT 0x88 -#define DMA_DIRECTION 31 -#define DMA_PCI_BYTE_COUNT 0 /* 0:23 */ - -#define RDK2_DMALOCCOUNT 0x8C /* 0:23 dma local byte count */ - -#define RDK2_DMAADDR 0x90 /* 2:31 PCI bus starting address */ - -/*---------------------------------------------------------------------------*/ - -#define REG_INDEXED_THRESHOLD (1 << 5) - -/* DRIVER DATA STRUCTURES and UTILITIES */ -struct net2272_ep { - struct usb_ep ep; - struct net2272 *dev; - unsigned long irqs; - - /* analogous to a host-side qh */ - struct list_head queue; - const struct usb_endpoint_descriptor *desc; - unsigned num:8, - fifo_size:12, - stopped:1, - wedged:1, - is_in:1, - is_iso:1, - dma:1, - not_empty:1; -}; - -struct net2272 { - /* each device provides one gadget, several endpoints */ - struct usb_gadget gadget; - struct device *dev; - unsigned short dev_id; - - spinlock_t lock; - struct net2272_ep ep[4]; - struct usb_gadget_driver *driver; - unsigned protocol_stall:1, - softconnect:1, - wakeup:1, - added:1, - async_callbacks:1, - dma_eot_polarity:1, - dma_dack_polarity:1, - dma_dreq_polarity:1, - dma_busy:1; - u16 chiprev; - u8 pagesel; - - unsigned int irq; - unsigned short fifo_mode; - - unsigned int base_shift; - u16 __iomem *base_addr; - union { -#ifdef CONFIG_USB_PCI - struct { - void __iomem *plx9054_base_addr; - void __iomem *epld_base_addr; - } rdk1; - struct { - /* Bar0, Bar1 is base_addr both mem-mapped */ - void __iomem *fpga_base_addr; - } rdk2; -#endif - }; -}; - -static void __iomem * -net2272_reg_addr(struct net2272 *dev, unsigned int reg) -{ - return dev->base_addr + (reg << dev->base_shift); -} - -static void -net2272_write(struct net2272 *dev, unsigned int reg, u8 value) -{ - if (reg >= REG_INDEXED_THRESHOLD) { - /* - * Indexed register; use REGADDRPTR/REGDATA - * - Save and restore REGADDRPTR. This prevents REGADDRPTR from - * changes between other code sections, but it is time consuming. - * - Performance tips: either do not save and restore REGADDRPTR (if it - * is safe) or do save/restore operations only in critical sections. - u8 tmp = readb(dev->base_addr + REGADDRPTR); - */ - writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR)); - writeb(value, net2272_reg_addr(dev, REGDATA)); - /* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */ - } else - writeb(value, net2272_reg_addr(dev, reg)); -} - -static u8 -net2272_read(struct net2272 *dev, unsigned int reg) -{ - u8 ret; - - if (reg >= REG_INDEXED_THRESHOLD) { - /* - * Indexed register; use REGADDRPTR/REGDATA - * - Save and restore REGADDRPTR. This prevents REGADDRPTR from - * changes between other code sections, but it is time consuming. - * - Performance tips: either do not save and restore REGADDRPTR (if it - * is safe) or do save/restore operations only in critical sections. - u8 tmp = readb(dev->base_addr + REGADDRPTR); - */ - writeb((u8)reg, net2272_reg_addr(dev, REGADDRPTR)); - ret = readb(net2272_reg_addr(dev, REGDATA)); - /* writeb(tmp, net2272_reg_addr(dev, REGADDRPTR)); */ - } else - ret = readb(net2272_reg_addr(dev, reg)); - - return ret; -} - -static void -net2272_ep_write(struct net2272_ep *ep, unsigned int reg, u8 value) -{ - struct net2272 *dev = ep->dev; - - if (dev->pagesel != ep->num) { - net2272_write(dev, PAGESEL, ep->num); - dev->pagesel = ep->num; - } - net2272_write(dev, reg, value); -} - -static u8 -net2272_ep_read(struct net2272_ep *ep, unsigned int reg) -{ - struct net2272 *dev = ep->dev; - - if (dev->pagesel != ep->num) { - net2272_write(dev, PAGESEL, ep->num); - dev->pagesel = ep->num; - } - return net2272_read(dev, reg); -} - -static void allow_status(struct net2272_ep *ep) -{ - /* ep0 only */ - net2272_ep_write(ep, EP_RSPCLR, - (1 << CONTROL_STATUS_PHASE_HANDSHAKE) | - (1 << ALT_NAK_OUT_PACKETS) | - (1 << NAK_OUT_PACKETS_MODE)); - ep->stopped = 1; -} - -static void set_halt(struct net2272_ep *ep) -{ - /* ep0 and bulk/intr endpoints */ - net2272_ep_write(ep, EP_RSPCLR, 1 << CONTROL_STATUS_PHASE_HANDSHAKE); - net2272_ep_write(ep, EP_RSPSET, 1 << ENDPOINT_HALT); -} - -static void clear_halt(struct net2272_ep *ep) -{ - /* ep0 and bulk/intr endpoints */ - net2272_ep_write(ep, EP_RSPCLR, - (1 << ENDPOINT_HALT) | (1 << ENDPOINT_TOGGLE)); -} - -/* count (<= 4) bytes in the next fifo write will be valid */ -static void set_fifo_bytecount(struct net2272_ep *ep, unsigned count) -{ - /* net2272_ep_write will truncate to u8 for us */ - net2272_ep_write(ep, EP_TRANSFER2, count >> 16); - net2272_ep_write(ep, EP_TRANSFER1, count >> 8); - net2272_ep_write(ep, EP_TRANSFER0, count); -} - -struct net2272_request { - struct usb_request req; - struct list_head queue; - unsigned mapped:1, - valid:1; -}; - -#endif diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index b2903e4bbf54..d02765bd49ce 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -203,13 +203,13 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) } /* erratum 0119 workaround ties up an endpoint number */ - if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) { + if (usb_endpoint_num(desc) == EP_DONTUSE) { ret = -EDOM; goto print_err; } if (dev->quirks & PLX_PCIE) { - if ((desc->bEndpointAddress & 0x0f) >= 0x0c) { + if (usb_endpoint_num(desc) >= 0x0c) { ret = -EDOM; goto print_err; } @@ -255,7 +255,7 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) else tmp &= ~USB3380_EP_CFG_MASK_OUT; } - type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + type = usb_endpoint_type(desc); if (type == USB_ENDPOINT_XFER_INT) { /* erratum 0105 workaround prevents hs NYET */ if (dev->chiprev == 0100 && @@ -554,7 +554,7 @@ static struct usb_request } ep = container_of(_ep, struct net2280_ep, ep); - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -1334,7 +1334,7 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) retval = -ESHUTDOWN; goto print_err; } - if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) + if (ep->desc /* not ep0 */ && usb_endpoint_type(ep->desc) == USB_ENDPOINT_XFER_ISOC) { retval = -EINVAL; goto print_err; @@ -3625,7 +3625,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) int retval, i; /* alloc, and start init */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (dev == NULL) { retval = -ENOMEM; goto done; diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 8902abe3ca76..f3ca79cece1b 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -252,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); @@ -267,7 +267,7 @@ omap_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { struct omap_req *req; - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -733,8 +733,6 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) if (status == 0) { omap_writew(reg, UDC_TXDMA_CFG); /* EMIFF or SDRC */ - omap_set_dma_src_burst_mode(ep->lch, - OMAP_DMA_DATA_BURST_4); omap_set_dma_src_data_pack(ep->lch, 1); /* TIPB */ omap_set_dma_dest_params(ep->lch, @@ -756,8 +754,6 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) UDC_DATA_DMA, 0, 0); /* EMIFF or SDRC */ - omap_set_dma_dest_burst_mode(ep->lch, - OMAP_DMA_DATA_BURST_4); omap_set_dma_dest_data_pack(ep->lch, 1); } } @@ -1860,7 +1856,7 @@ static irqreturn_t omap_udc_irq(int irq, void *_udc) static void pio_out_timer(struct timer_list *t) { - struct omap_ep *ep = from_timer(ep, t, timer); + struct omap_ep *ep = timer_container_of(ep, t, timer); unsigned long flags; u16 stat_flg; @@ -2627,7 +2623,7 @@ omap_udc_setup(struct platform_device *odev, struct usb_phy *xceiv) /* UDC_PULLUP_EN gates the chip clock */ /* OTG_SYSCON_1 |= DEV_IDLE_EN; */ - udc = kzalloc(sizeof(*udc), GFP_KERNEL); + udc = kzalloc_obj(*udc); if (!udc) return -ENOMEM; diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 169f72665739..0a6886428739 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -988,7 +988,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep, pch_udc_ep_fifo_flush(ep, ep->in); /* Configure the endpoint */ val = ep->num << UDC_CSR_NE_NUM_SHIFT | ep->in << UDC_CSR_NE_DIR_SHIFT | - ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) << + (usb_endpoint_type(desc) << UDC_CSR_NE_TYPE_SHIFT) | (cfg->cur_cfg << UDC_CSR_NE_CFG_SHIFT) | (cfg->cur_intf << UDC_CSR_NE_INTF_SHIFT) | @@ -1717,7 +1717,7 @@ static struct usb_request *pch_udc_alloc_request(struct usb_ep *usbep, if (!usbep) return NULL; ep = container_of(usbep, struct pch_udc_ep, ep); - req = kzalloc(sizeof *req, gfp); + req = kzalloc_obj(*req, gfp); if (!req) return NULL; req->req.dma = DMA_ADDR_INVALID; diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 1e9998024aaa..b3d58d7c3a77 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -508,7 +508,7 @@ pxa25x_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) { struct pxa25x_request *req; - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -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) @@ -1574,7 +1574,7 @@ static inline void clear_ep_state (struct pxa25x_udc *dev) static void udc_watchdog(struct timer_list *t) { - struct pxa25x_udc *dev = from_timer(dev, t, timer); + struct pxa25x_udc *dev = timer_container_of(dev, t, timer); local_irq_disable(); if (dev->ep0state == EP0_STALL @@ -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); } @@ -2348,15 +2348,14 @@ static int pxa25x_udc_probe(struct platform_device *pdev) dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2); if (gpio_is_valid(dev->mach->gpio_pullup)) { - retval = devm_gpio_request(&pdev->dev, dev->mach->gpio_pullup, - "pca25x_udc GPIO PULLUP"); + retval = devm_gpio_request_one(&pdev->dev, dev->mach->gpio_pullup, + GPIOF_OUT_INIT_LOW, "pca25x_udc GPIO PULLUP"); if (retval) { dev_dbg(&pdev->dev, "can't get pullup gpio %d, err: %d\n", dev->mach->gpio_pullup, retval); goto err; } - gpio_direction_output(dev->mach->gpio_pullup, 0); } timer_setup(&dev->timer, udc_watchdog, 0); diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 897f53601b5b..1abea0d48c35 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -573,7 +573,7 @@ pxa_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { struct pxa27x_request *req; - req = kzalloc(sizeof *req, gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -1462,7 +1462,7 @@ static int pxa_udc_wakeup(struct usb_gadget *_gadget) return 0; } -static void udc_enable(struct pxa_udc *udc); +static int udc_enable(struct pxa_udc *udc); static void udc_disable(struct pxa_udc *udc); /** @@ -1519,14 +1519,20 @@ static int should_disable_udc(struct pxa_udc *udc) static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active) { struct pxa_udc *udc = to_gadget_udc(_gadget); + int ret; if (!udc->gpiod && !udc->udc_command) return -EOPNOTSUPP; dplus_pullup(udc, is_active); - if (should_enable_udc(udc)) - udc_enable(udc); + if (should_enable_udc(udc)) { + ret = udc_enable(udc); + if (ret) { + dplus_pullup(udc, !is_active); + return ret; + } + } if (should_disable_udc(udc)) udc_disable(udc); return 0; @@ -1545,10 +1551,16 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active) static int pxa_udc_vbus_session(struct usb_gadget *_gadget, int is_active) { struct pxa_udc *udc = to_gadget_udc(_gadget); + int ret; udc->vbus_sensed = is_active; - if (should_enable_udc(udc)) - udc_enable(udc); + if (should_enable_udc(udc)) { + ret = udc_enable(udc); + if (ret) { + udc->vbus_sensed = !is_active; + return ret; + } + } if (should_disable_udc(udc)) udc_disable(udc); @@ -1691,12 +1703,18 @@ static void udc_init_data(struct pxa_udc *dev) * Enables the udc device : enables clocks, udc interrupts, control endpoint * interrupts, sets usb as UDC client and setups endpoints. */ -static void udc_enable(struct pxa_udc *udc) +static int udc_enable(struct pxa_udc *udc) { + int ret; + if (udc->enabled) - return; + return 0; - clk_enable(udc->clk); + ret = clk_enable(udc->clk); + if (ret) { + dev_err(udc->dev, "clk_enable failed: %d\n", ret); + return ret; + } udc_writel(udc, UDCICR0, 0); udc_writel(udc, UDCICR1, 0); udc_clear_mask_UDCCR(udc, UDCCR_UDE); @@ -1726,6 +1744,8 @@ static void udc_enable(struct pxa_udc *udc) pio_irq_enable(&udc->pxa_ep[0]); udc->enabled = 1; + + return 0; } /** @@ -1761,10 +1781,16 @@ static int pxa27x_udc_start(struct usb_gadget *g, } } - if (should_enable_udc(udc)) - udc_enable(udc); + if (should_enable_udc(udc)) { + retval = udc_enable(udc); + if (retval) + goto fail_enable; + } return 0; +fail_enable: + if (!IS_ERR_OR_NULL(udc->transceiver)) + otg_set_peripheral(udc->transceiver->otg, NULL); fail: udc->driver = NULL; return retval; @@ -2430,10 +2456,16 @@ static int pxa_udc_probe(struct platform_device *pdev) goto err_add_gadget; pxa_init_debugfs(udc); - if (should_enable_udc(udc)) - udc_enable(udc); + if (should_enable_udc(udc)) { + retval = udc_enable(udc); + if (retval) + goto err_enable; + } return 0; +err_enable: + usb_del_gadget_udc(&udc->gadget); + pxa_cleanup_debugfs(udc); err_add_gadget: if (!IS_ERR_OR_NULL(udc->transceiver)) usb_unregister_notifier(udc->transceiver, &pxa27x_udc_phy); @@ -2509,13 +2541,19 @@ static int pxa_udc_resume(struct platform_device *_dev) { struct pxa_udc *udc = platform_get_drvdata(_dev); struct pxa_ep *ep; + int ret; ep = &udc->pxa_ep[0]; udc_ep_writel(ep, UDCCSR, udc->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME)); dplus_pullup(udc, udc->pullup_resume); - if (should_enable_udc(udc)) - udc_enable(udc); + if (should_enable_udc(udc)) { + ret = udc_enable(udc); + if (ret) { + dplus_pullup(udc, !udc->pullup_resume); + return ret; + } + } /* * We do not handle OTG yet. * diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index ff6f846b1d41..e7a5d8553c0e 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1516,7 +1516,7 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) static void r8a66597_timer(struct timer_list *t) { - struct r8a66597 *r8a66597 = from_timer(r8a66597, t, timer); + struct r8a66597 *r8a66597 = timer_container_of(r8a66597, t, timer); unsigned long flags; u16 tmp; @@ -1580,7 +1580,7 @@ static struct usb_request *r8a66597_alloc_request(struct usb_ep *_ep, { struct r8a66597_request *req; - req = kzalloc(sizeof(struct r8a66597_request), gfp_flags); + req = kzalloc_obj(struct r8a66597_request, gfp_flags); if (!req) return NULL; @@ -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) { diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 89b304cf6d03..2c9c3e935a5e 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -1669,6 +1669,10 @@ static bool usb3_std_req_get_status(struct renesas_usb3 *usb3, break; case USB_RECIP_ENDPOINT: num = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; + if (num >= usb3->num_usb3_eps) { + stall = true; + break; + } usb3_ep = usb3_get_ep(usb3, num); if (usb3_ep->halt) status |= 1 << USB_ENDPOINT_HALT; @@ -1781,7 +1785,8 @@ static bool usb3_std_req_feature_endpoint(struct renesas_usb3 *usb3, struct renesas_usb3_ep *usb3_ep; struct renesas_usb3_request *usb3_req; - if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) + if ((le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT) || + (num >= usb3->num_usb3_eps)) return true; /* stall */ usb3_ep = usb3_get_ep(usb3, num); @@ -2265,7 +2270,7 @@ static struct usb_request *__renesas_usb3_ep_alloc_request(gfp_t gfp_flags) { struct renesas_usb3_request *usb3_req; - usb3_req = kzalloc(sizeof(struct renesas_usb3_request), gfp_flags); + usb3_req = kzalloc_obj(struct renesas_usb3_request, gfp_flags); if (!usb3_req) return NULL; @@ -2397,8 +2402,7 @@ static int renesas_usb3_stop(struct usb_gadget *gadget) rzv2m_usb3drd_reset(usb3_to_dev(usb3)->parent, false); renesas_usb3_stop_controller(usb3); - if (usb3->phy) - phy_exit(usb3->phy); + phy_exit(usb3->phy); pm_runtime_put(usb3_to_dev(usb3)); @@ -2658,6 +2662,7 @@ static void renesas_usb3_remove(struct platform_device *pdev) struct renesas_usb3 *usb3 = platform_get_drvdata(pdev); debugfs_remove_recursive(usb3->dentry); + put_device(usb3->host_dev); device_remove_file(&pdev->dev, &dev_attr_role); cancel_work_sync(&usb3->role_work); @@ -2974,7 +2979,6 @@ err_alloc_prd: return ret; } -#ifdef CONFIG_PM_SLEEP static int renesas_usb3_suspend(struct device *dev) { struct renesas_usb3 *usb3 = dev_get_drvdata(dev); @@ -2984,8 +2988,7 @@ static int renesas_usb3_suspend(struct device *dev) return 0; renesas_usb3_stop_controller(usb3); - if (usb3->phy) - phy_exit(usb3->phy); + phy_exit(usb3->phy); pm_runtime_put(dev); return 0; @@ -3006,17 +3009,16 @@ static int renesas_usb3_resume(struct device *dev) return 0; } -#endif -static SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend, - renesas_usb3_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend, + renesas_usb3_resume); static struct platform_driver renesas_usb3_driver = { .probe = renesas_usb3_probe, .remove = renesas_usb3_remove, .driver = { .name = udc_name, - .pm = &renesas_usb3_pm_ops, + .pm = pm_sleep_ptr(&renesas_usb3_pm_ops), .of_match_table = usb3_of_match, }, }; @@ -3025,4 +3027,3 @@ module_platform_driver(renesas_usb3_driver); MODULE_DESCRIPTION("Renesas USB3.0 Peripheral driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>"); -MODULE_ALIAS("platform:renesas_usb3"); diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c index 14f4b2cf05a4..5d510665da1f 100644 --- a/drivers/usb/gadget/udc/renesas_usbf.c +++ b/drivers/usb/gadget/udc/renesas_usbf.c @@ -2081,7 +2081,7 @@ static struct usb_request *usbf_ep_alloc_request(struct usb_ep *_ep, if (!_ep) return NULL; - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -3262,7 +3262,9 @@ static int usbf_probe(struct platform_device *pdev) if (IS_ERR(udc->regs)) return PTR_ERR(udc->regs); - devm_pm_runtime_enable(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; ret = pm_runtime_resume_and_get(&pdev->dev); if (ret < 0) return ret; diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index 1f8a99d2a643..0e0db68e0b27 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -523,7 +523,7 @@ udc_alloc_request(struct usb_ep *usbep, gfp_t gfp) ep = container_of(usbep, struct udc_ep, ep); VDBG(ep->dev, "udc_alloc_req(): ep%d\n", ep->num); - req = kzalloc(sizeof(struct udc_request), gfp); + req = kzalloc_obj(struct udc_request, gfp); if (!req) return NULL; @@ -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); @@ -3151,7 +3151,7 @@ int udc_probe(struct udc *dev) tmp, dev->phys_addr, dev->chiprev, (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1"); - strcpy(tmp, UDC_DRIVER_VERSION_STRING); + strscpy(tmp, UDC_DRIVER_VERSION_STRING); if (dev->chiprev == UDC_HSA0_REV) { dev_err(dev->dev, "chip revision is A0; too old\n"); retval = -ENODEV; diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index c7fdbc55fb0b..e9d33be02866 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -502,6 +502,7 @@ struct tegra_xudc { struct clk_bulk_data *clks; bool device_mode; + bool current_device_mode; struct work_struct usb_role_sw_work; struct phy **usb3_phy; @@ -715,6 +716,8 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc) phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_DEVICE); + + xudc->current_device_mode = true; } static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc) @@ -725,6 +728,8 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc) dev_dbg(xudc->dev, "device mode off\n"); + xudc->current_device_mode = false; + connected = !!(xudc_readl(xudc, PORTSC) & PORTSC_CCS); reinit_completion(&xudc->disconnect_complete); @@ -807,8 +812,7 @@ static void tegra_xudc_update_data_role(struct tegra_xudc *xudc, return; } - xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true : - false; + xudc->device_mode = usbphy->last_event == USB_EVENT_VBUS; phy_index = tegra_xudc_get_phy_index(xudc, usbphy); dev_dbg(xudc->dev, "%s(): current phy index is %d\n", __func__, @@ -1554,12 +1558,6 @@ static int __tegra_xudc_ep_set_halt(struct tegra_xudc_ep *ep, bool halt) return -ENOTSUPP; } - if (!!(xudc_readl(xudc, EP_HALT) & BIT(ep->index)) == halt) { - dev_dbg(xudc->dev, "EP %u already %s\n", ep->index, - halt ? "halted" : "not halted"); - return 0; - } - if (halt) { ep_halt(xudc, ep->index); } else { @@ -1749,6 +1747,10 @@ static int __tegra_xudc_ep_disable(struct tegra_xudc_ep *ep) val = xudc_readl(xudc, CTRL); val &= ~CTRL_RUN; xudc_writel(xudc, val, CTRL); + + val = xudc_readl(xudc, ST); + if (val & ST_RC) + xudc_writel(xudc, ST_RC, ST); } dev_info(xudc->dev, "ep %u disabled\n", ep->index); @@ -1904,7 +1906,7 @@ tegra_xudc_ep_alloc_request(struct usb_ep *usb_ep, gfp_t gfp) { struct tegra_xudc_request *req; - req = kzalloc(sizeof(*req), gfp); + req = kzalloc_obj(*req, gfp); if (!req) return NULL; @@ -3390,17 +3392,18 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc) { u32 val, imod; + val = xudc_readl(xudc, BLCG); if (xudc->soc->has_ipfs) { - val = xudc_readl(xudc, BLCG); val |= BLCG_ALL; val &= ~(BLCG_DFPCI | BLCG_UFPCI | BLCG_FE | BLCG_COREPLL_PWRDN); val |= BLCG_IOPLL_0_PWRDN; val |= BLCG_IOPLL_1_PWRDN; val |= BLCG_IOPLL_2_PWRDN; - - xudc_writel(xudc, val, BLCG); + } else { + val &= ~BLCG_COREPLL_PWRDN; } + xudc_writel(xudc, val, BLCG); if (xudc->soc->port_speed_quirk) tegra_xudc_limit_port_speed(xudc); @@ -3951,6 +3954,7 @@ static void tegra_xudc_remove(struct platform_device *pdev) static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc) { unsigned long flags; + u32 val; dev_dbg(xudc->dev, "entering ELPG\n"); @@ -3963,6 +3967,10 @@ static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc) spin_unlock_irqrestore(&xudc->lock, flags); + val = xudc_readl(xudc, BLCG); + val |= BLCG_COREPLL_PWRDN; + xudc_writel(xudc, val, BLCG); + clk_bulk_disable_unprepare(xudc->soc->num_clks, xudc->clks); regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies); @@ -4040,10 +4048,10 @@ static int __maybe_unused tegra_xudc_resume(struct device *dev) spin_lock_irqsave(&xudc->lock, flags); xudc->suspended = false; + if (xudc->device_mode != xudc->current_device_mode) + schedule_work(&xudc->usb_role_sw_work); spin_unlock_irqrestore(&xudc->lock, flags); - schedule_work(&xudc->usb_role_sw_work); - pm_runtime_enable(dev); return 0; diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h index 4e334298b0e8..fa3e6ddf0a12 100644 --- a/drivers/usb/gadget/udc/trace.h +++ b/drivers/usb/gadget/udc/trace.h @@ -81,6 +81,11 @@ DECLARE_EVENT_CLASS(udc_log_gadget, __entry->ret) ); +DEFINE_EVENT(udc_log_gadget, usb_gadget_set_state, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + DEFINE_EVENT(udc_log_gadget, usb_gadget_frame_number, TP_PROTO(struct usb_gadget *g, int ret), TP_ARGS(g, ret) diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index ae2aeb271897..bef06fe7543b 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -813,10 +813,10 @@ static int __xudc_ep_enable(struct xusb_ep *ep, ep->is_in = ((desc->bEndpointAddress & USB_DIR_IN) != 0); /* Bit 3...0:endpoint number */ - ep->epnumber = (desc->bEndpointAddress & 0x0f); + ep->epnumber = usb_endpoint_num(desc); ep->desc = desc; ep->ep_usb.desc = desc; - tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + tmp = usb_endpoint_type(desc); ep->ep_usb.maxpacket = maxpacket = le16_to_cpu(desc->wMaxPacketSize); switch (tmp) { @@ -970,7 +970,7 @@ static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep, struct xusb_ep *ep = to_xusb_ep(_ep); struct xusb_req *req; - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -2178,8 +2178,6 @@ fail: /** * xudc_remove - Releases the resources allocated during the initialization. * @pdev: pointer to the platform device structure. - * - * Return: 0 always */ static void xudc_remove(struct platform_device *pdev) { diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index d011d6c753ed..0a277a07cf70 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -93,7 +93,7 @@ config USB_XHCI_RCAR default ARCH_RENESAS help Say 'Y' to enable the support for the xHCI host controller - found in Renesas R-Car ARM SoCs. + found in Renesas R-Car and RZ/G3E alike ARM SoCs. config USB_XHCI_RZV2M bool "xHCI support for Renesas RZ/V2M SoC" @@ -104,6 +104,15 @@ config USB_XHCI_RZV2M Say 'Y' to enable the support for the xHCI host controller found in Renesas RZ/V2M SoC. +config USB_XHCI_SIDEBAND + bool "xHCI support for sideband" + help + Say 'Y' to enable the support for the xHCI sideband capability. + Provide a mechanism for a sideband datapath for payload associated + with audio class endpoints. This allows for an audio DSP to use + xHCI USB endpoints directly, allowing CPU to sleep while playing + audio. + config USB_XHCI_TEGRA tristate "xHCI support for NVIDIA Tegra SoCs" depends on PHY_TEGRA_XUSB @@ -225,7 +234,7 @@ config USB_EHCI_HCD_OMAP tristate "EHCI support for OMAP3 and later chips" depends on ARCH_OMAP || COMPILE_TEST depends on NOP_USB_XCEIV - default y + default ARCH_OMAP help Enables support for the on-chip EHCI controller on OMAP3 and later chips. @@ -374,18 +383,6 @@ config USB_ISP116X_HCD To compile this driver as a module, choose M here: the module will be called isp116x-hcd. -config USB_ISP1362_HCD - tristate "ISP1362 HCD support" - depends on HAS_IOPORT - depends on COMPILE_TEST # nothing uses this - help - Supports the Philips ISP1362 chip as a host controller - - This driver does not support isochronous transfers. - - To compile this driver as a module, choose M here: the - module will be called isp1362-hcd. - config USB_MAX3421_HCD tristate "MAX3421 HCD (USB-over-SPI) support" depends on USB && SPI @@ -607,7 +604,7 @@ config USB_UHCI_ASPEED config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" - depends on OF_GPIO && QE_GPIO && QUICC_ENGINE + depends on QE_GPIO && QUICC_ENGINE select FSL_GTM select QE_USB help diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index be4e5245c52f..a07e7ba9cd53 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -32,6 +32,10 @@ endif xhci-rcar-hcd-y += xhci-rcar.o xhci-rcar-hcd-$(CONFIG_USB_XHCI_RZV2M) += xhci-rzv2m.o +ifneq ($(CONFIG_USB_XHCI_SIDEBAND),) + xhci-hcd-y += xhci-sideband.o +endif + obj-$(CONFIG_USB_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o @@ -48,7 +52,6 @@ obj-$(CONFIG_USB_EHCI_BRCMSTB) += ehci-brcm.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o -obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c index 888e8f6670d2..5e3156f94cc6 100644 --- a/drivers/usb/host/ehci-brcm.c +++ b/drivers/usb/host/ehci-brcm.c @@ -31,8 +31,8 @@ static inline void ehci_brcm_wait_for_sof(struct ehci_hcd *ehci, u32 delay) int res; /* Wait for next microframe (every 125 usecs) */ - res = readl_relaxed_poll_timeout(&ehci->regs->frame_index, val, - val != frame_idx, 1, 130); + res = readl_relaxed_poll_timeout_atomic(&ehci->regs->frame_index, + val, val != frame_idx, 1, 130); if (res) ehci_err(ehci, "Error waiting for SOF\n"); udelay(delay); diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 435001128221..3cf743c81eb4 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -629,7 +629,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) unsigned i; __hc32 tag; - seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC); + seen = kmalloc_objs(*seen, DBG_SCHED_LIMIT, GFP_ATOMIC); if (!seen) return 0; seen_count = 0; @@ -917,7 +917,7 @@ static struct debug_buffer *alloc_buffer(struct usb_bus *bus, { struct debug_buffer *buf; - buf = kzalloc(sizeof(*buf), GFP_KERNEL); + buf = kzalloc_obj(*buf); if (buf) { buf->bus = bus; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 26f13278d4d6..6ed2fa5418a4 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -410,15 +410,13 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) return retval; } -struct ehci_fsl { - struct ehci_hcd ehci; - -#ifdef CONFIG_PM +struct ehci_fsl_priv { /* Saved USB PHY settings, need to restore after deep sleep. */ u32 usb_ctrl; -#endif }; +#define hcd_to_ehci_fsl_priv(h) ((struct ehci_fsl_priv *) hcd_to_ehci(h)->priv) + #ifdef CONFIG_PM #ifdef CONFIG_PPC_MPC512x @@ -566,17 +564,10 @@ static inline int ehci_fsl_mpc512x_drv_resume(struct device *dev) } #endif /* CONFIG_PPC_MPC512x */ -static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd) -{ - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - - return container_of(ehci, struct ehci_fsl, ehci); -} - static int ehci_fsl_drv_suspend(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); + struct ehci_fsl_priv *priv = hcd_to_ehci_fsl_priv(hcd); void __iomem *non_ehci = hcd->regs; if (of_device_is_compatible(dev->parent->of_node, @@ -589,14 +580,14 @@ static int ehci_fsl_drv_suspend(struct device *dev) if (!fsl_deep_sleep()) return 0; - ehci_fsl->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL); + priv->usb_ctrl = ioread32be(non_ehci + FSL_SOC_USB_CTRL); return 0; } static int ehci_fsl_drv_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); - struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); + struct ehci_fsl_priv *priv = hcd_to_ehci_fsl_priv(hcd); struct ehci_hcd *ehci = hcd_to_ehci(hcd); void __iomem *non_ehci = hcd->regs; @@ -612,7 +603,7 @@ static int ehci_fsl_drv_resume(struct device *dev) usb_root_hub_lost_power(hcd->self.root_hub); /* Restore USB PHY settings and enable the controller. */ - iowrite32be(ehci_fsl->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL); + iowrite32be(priv->usb_ctrl, non_ehci + FSL_SOC_USB_CTRL); ehci_reset(ehci); ehci_fsl_reinit(ehci); @@ -671,7 +662,7 @@ static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port) #endif /* CONFIG_USB_OTG */ static const struct ehci_driver_overrides ehci_fsl_overrides __initconst = { - .extra_priv_size = sizeof(struct ehci_fsl), + .extra_priv_size = sizeof(struct ehci_fsl_priv), .reset = ehci_fsl_setup, }; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 6de79ac5e6a4..3c46bb18c7f3 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); @@ -1355,12 +1354,6 @@ static int __init ehci_hcd_init(void) if (usb_disabled()) return -ENODEV; - set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); - if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || - test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) - printk(KERN_WARNING "Warning! ehci_hcd should always be loaded" - " before uhci_hcd and ohci_hcd, not after\n"); - pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd sitd %zd\n", hcd_name, sizeof(struct ehci_qh), sizeof(struct ehci_qtd), @@ -1391,7 +1384,6 @@ clean0: debugfs_remove(ehci_debug_root); ehci_debug_root = NULL; #endif - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); return retval; } module_init(ehci_hcd_init); @@ -1405,6 +1397,5 @@ static void __exit ehci_hcd_cleanup(void) #ifdef CONFIG_DYNAMIC_DEBUG debugfs_remove(ehci_debug_root); #endif - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); } module_exit(ehci_hcd_cleanup); diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 4c6c08b675b5..a2d4499e67e0 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -69,7 +69,7 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) struct ehci_qh *qh; dma_addr_t dma; - qh = kzalloc(sizeof *qh, GFP_ATOMIC); + qh = kzalloc_obj(*qh, GFP_ATOMIC); if (!qh) goto done; qh->hw = (struct ehci_qh_hw *) diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 34abff8669f8..eaaa49712a8c 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -12,7 +12,6 @@ #include <linux/clk.h> #include <linux/platform_data/usb-ehci-orion.h> #include <linux/of.h> -#include <linux/phy/phy.h> #include <linux/usb.h> #include <linux/usb/hcd.h> #include <linux/io.h> @@ -60,7 +59,6 @@ struct orion_ehci_hcd { struct clk *clk; - struct phy *phy; }; static struct hc_driver __read_mostly ehci_orion_hc_driver; @@ -276,13 +274,6 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) goto err_put_hcd; } - priv->phy = devm_phy_optional_get(&pdev->dev, "usb"); - if (IS_ERR(priv->phy)) { - err = PTR_ERR(priv->phy); - if (err != -ENOSYS) - goto err_dis_clk; - } - /* * (Re-)program MBUS remapping windows if we are asked to. */ diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 889dc4426271..bd3a63555594 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -21,7 +21,6 @@ static const char hcd_name[] = "ehci-pci"; /* defined here to avoid adding to pci_ids.h for single instance use */ #define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70 -#define PCI_VENDOR_ID_ASPEED 0x1a03 #define PCI_DEVICE_ID_ASPEED_EHCI 0x2603 /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index cdf41886e8ca..f61f095cedab 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -27,6 +27,7 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/reset.h> #include <linux/sys_soc.h> @@ -111,8 +112,7 @@ static void ehci_platform_power_off(struct platform_device *dev) int clk; for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) - if (priv->clks[clk]) - clk_disable_unprepare(priv->clks[clk]); + clk_disable_unprepare(priv->clks[clk]); } static struct hc_driver __read_mostly ehci_platform_hc_driver; @@ -198,7 +198,8 @@ static void quirk_poll_work(struct work_struct *work) static void quirk_poll_timer(struct timer_list *t) { - struct ehci_platform_priv *priv = from_timer(priv, t, poll_timer); + struct ehci_platform_priv *priv = timer_container_of(priv, t, + poll_timer); struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd, priv); @@ -224,7 +225,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); } @@ -238,9 +239,11 @@ static int ehci_platform_probe(struct platform_device *dev) struct usb_hcd *hcd; struct resource *res_mem; struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); + const struct of_device_id *match; struct ehci_platform_priv *priv; struct ehci_hcd *ehci; int err, irq, clk = 0; + bool dma_mask_64; if (usb_disabled()) return -ENODEV; @@ -252,8 +255,13 @@ static int ehci_platform_probe(struct platform_device *dev) if (!pdata) pdata = &ehci_platform_defaults; + dma_mask_64 = pdata->dma_mask_64; + match = of_match_device(dev->dev.driver->of_match_table, &dev->dev); + if (match && match->data) + dma_mask_64 = true; + err = dma_coerce_mask_and_coherent(&dev->dev, - pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); + dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); if (err) { dev_err(&dev->dev, "Error: DMA mask configuration failed\n"); return err; @@ -297,7 +305,9 @@ static int ehci_platform_probe(struct platform_device *dev) if (of_device_is_compatible(dev->dev.of_node, "aspeed,ast2500-ehci") || of_device_is_compatible(dev->dev.of_node, - "aspeed,ast2600-ehci")) + "aspeed,ast2600-ehci") || + of_device_is_compatible(dev->dev.of_node, + "aspeed,ast2700-ehci")) ehci->is_aspeed = 1; if (soc_device_match(quirk_poll_match)) @@ -444,6 +454,17 @@ static int __maybe_unused ehci_platform_suspend(struct device *dev) if (pdata->power_suspend) pdata->power_suspend(pdev); + ret = reset_control_assert(priv->rsts); + if (ret) { + if (pdata->power_on) + pdata->power_on(pdev); + + ehci_resume(hcd, false); + + if (priv->quirk_poll) + quirk_poll_init(priv); + } + return ret; } @@ -454,11 +475,18 @@ static int __maybe_unused ehci_platform_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); struct device *companion_dev; + int err; + + err = reset_control_deassert(priv->rsts); + if (err) + return err; if (pdata->power_on) { - int err = pdata->power_on(pdev); - if (err < 0) + err = pdata->power_on(pdev); + if (err < 0) { + reset_control_assert(priv->rsts); return err; + } } companion_dev = usb_of_get_companion_dev(hcd->self.controller); @@ -484,6 +512,7 @@ static const struct of_device_id vt8500_ehci_ids[] = { { .compatible = "wm,prizm-ehci", }, { .compatible = "generic-ehci", }, { .compatible = "cavium,octeon-6335-ehci", }, + { .compatible = "aspeed,ast2700-ehci", .data = (void *)1 }, {} }; MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 7e834587e7de..a241337c9af8 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -117,9 +117,8 @@ static struct ehci_tt *find_tt(struct usb_device *udev) if (utt->multi) { tt_index = utt->hcpriv; if (!tt_index) { /* Create the index array */ - tt_index = kcalloc(utt->hub->maxchild, - sizeof(*tt_index), - GFP_ATOMIC); + tt_index = kzalloc_objs(*tt_index, utt->hub->maxchild, + GFP_ATOMIC); if (!tt_index) return ERR_PTR(-ENOMEM); utt->hcpriv = tt_index; @@ -137,7 +136,7 @@ static struct ehci_tt *find_tt(struct usb_device *udev) struct ehci_hcd *ehci = hcd_to_ehci(bus_to_hcd(udev->bus)); - tt = kzalloc(sizeof(*tt), GFP_ATOMIC); + tt = kzalloc_obj(*tt, GFP_ATOMIC); if (!tt) { if (allocated_index) { utt->hcpriv = NULL; @@ -1003,7 +1002,7 @@ iso_stream_alloc(gfp_t mem_flags) { struct ehci_iso_stream *stream; - stream = kzalloc(sizeof(*stream), mem_flags); + stream = kzalloc_obj(*stream, mem_flags); if (likely(stream != NULL)) { INIT_LIST_HEAD(&stream->td_list); INIT_LIST_HEAD(&stream->free_list); @@ -1167,7 +1166,7 @@ iso_sched_alloc(unsigned packets, gfp_t mem_flags) { struct ehci_iso_sched *iso_sched; - iso_sched = kzalloc(struct_size(iso_sched, packet, packets), mem_flags); + iso_sched = kzalloc_flex(*iso_sched, packet, packets, mem_flags); if (likely(iso_sched != NULL)) INIT_LIST_HEAD(&iso_sched->td_list); diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c index 8f75cb7b197c..5e6b545c30e6 100644 --- a/drivers/usb/host/ehci-sysfs.c +++ b/drivers/usb/host/ehci-sysfs.c @@ -12,21 +12,17 @@ static ssize_t companion_show(struct device *dev, char *buf) { struct ehci_hcd *ehci; - int nports, index, n; - int count = PAGE_SIZE; - char *ptr = buf; + int nports, index; + int len = 0; ehci = hcd_to_ehci(dev_get_drvdata(dev)); nports = HCS_N_PORTS(ehci->hcs_params); for (index = 0; index < nports; ++index) { - if (test_bit(index, &ehci->companion_ports)) { - n = scnprintf(ptr, count, "%d\n", index + 1); - ptr += n; - count -= n; - } + if (test_bit(index, &ehci->companion_ports)) + len += sysfs_emit_at(buf, len, "%d\n", index + 1); } - return ptr - buf; + return len; } /* @@ -70,11 +66,9 @@ static ssize_t uframe_periodic_max_show(struct device *dev, char *buf) { struct ehci_hcd *ehci; - int n; ehci = hcd_to_ehci(dev_get_drvdata(dev)); - n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max); - return n; + return sysfs_emit(buf, "%d\n", ehci->uframe_periodic_max); } diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 22a0942f0bce..71e785f445a3 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -193,7 +193,7 @@ static int fhci_mem_init(struct fhci_hcd *fhci) { int i; - fhci->hc_list = kzalloc(sizeof(*fhci->hc_list), GFP_KERNEL); + fhci->hc_list = kzalloc_obj(*fhci->hc_list); if (!fhci->hc_list) goto err; @@ -203,7 +203,7 @@ static int fhci_mem_init(struct fhci_hcd *fhci) INIT_LIST_HEAD(&fhci->hc_list->intr_list); INIT_LIST_HEAD(&fhci->hc_list->done_list); - fhci->vroot_hub = kzalloc(sizeof(*fhci->vroot_hub), GFP_KERNEL); + fhci->vroot_hub = kzalloc_obj(*fhci->vroot_hub); if (!fhci->vroot_hub) goto err; @@ -217,7 +217,7 @@ static int fhci_mem_init(struct fhci_hcd *fhci) for (i = 0; i < MAX_TDS; i++) { struct td *td; - td = kmalloc(sizeof(*td), GFP_KERNEL); + td = kmalloc_obj(*td); if (!td) goto err; fhci_recycle_empty_td(fhci, td); @@ -225,7 +225,7 @@ static int fhci_mem_init(struct fhci_hcd *fhci) for (i = 0; i < MAX_EDS; i++) { struct ed *ed; - ed = kmalloc(sizeof(*ed), GFP_KERNEL); + ed = kmalloc_obj(*ed); if (!ed) goto err; fhci_recycle_empty_ed(fhci, ed); @@ -264,7 +264,7 @@ static int fhci_usb_init(struct fhci_hcd *fhci) usb->max_frame_usage = FRAME_TIME_USAGE; usb->sw_transaction_time = SW_FIX_TIME_BETWEEN_TRANSACTION; - usb->actual_frame = kzalloc(sizeof(*usb->actual_frame), GFP_KERNEL); + usb->actual_frame = kzalloc_obj(*usb->actual_frame); if (!usb->actual_frame) { fhci_usb_free(usb); return -ENOMEM; @@ -306,7 +306,7 @@ static struct fhci_usb *fhci_create_lld(struct fhci_hcd *fhci) struct fhci_usb *usb; /* allocate memory for SCC data structure */ - usb = kzalloc(sizeof(*usb), GFP_KERNEL); + usb = kzalloc_obj(*usb); if (!usb) return NULL; @@ -426,16 +426,11 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, } /* allocate the private part of the URB */ - urb_priv = kzalloc(sizeof(*urb_priv), mem_flags); + urb_priv = kzalloc_flex(*urb_priv, tds, size, mem_flags); if (!urb_priv) return -ENOMEM; - /* allocate the private part of the URB */ - urb_priv->tds = kcalloc(size, sizeof(*urb_priv->tds), mem_flags); - if (!urb_priv->tds) { - kfree(urb_priv); - return -ENOMEM; - } + urb_priv->num_of_tds = size; spin_lock_irqsave(&fhci->lock, flags); @@ -444,8 +439,6 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, goto err; /* fill the private part of the URB */ - urb_priv->num_of_tds = size; - urb->status = -EINPROGRESS; urb->actual_length = 0; urb->error_count = 0; @@ -453,10 +446,8 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, fhci_queue_urb(fhci, urb); err: - if (ret) { - kfree(urb_priv->tds); + if (ret) kfree(urb_priv); - } spin_unlock_irqrestore(&fhci->lock, flags); return ret; } diff --git a/drivers/usb/host/fhci-mem.c b/drivers/usb/host/fhci-mem.c index 658aedc6adc1..d5951fc1964a 100644 --- a/drivers/usb/host/fhci-mem.c +++ b/drivers/usb/host/fhci-mem.c @@ -42,7 +42,7 @@ static struct td *get_empty_td(struct fhci_hcd *fhci) td = list_entry(fhci->empty_tds.next, struct td, node); list_del(fhci->empty_tds.next); } else { - td = kmalloc(sizeof(*td), GFP_ATOMIC); + td = kmalloc_obj(*td, GFP_ATOMIC); if (!td) fhci_err(fhci, "No memory to allocate to TD\n"); else @@ -66,7 +66,7 @@ struct ed *fhci_get_empty_ed(struct fhci_hcd *fhci) ed = list_entry(fhci->empty_eds.next, struct ed, node); list_del(fhci->empty_eds.next); } else { - ed = kmalloc(sizeof(*ed), GFP_ATOMIC); + ed = kmalloc_obj(*ed, GFP_ATOMIC); if (!ed) fhci_err(fhci, "No memory to allocate to ED\n"); else diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c index d98fd9e1af0b..d7482dc0604e 100644 --- a/drivers/usb/host/fhci-tds.c +++ b/drivers/usb/host/fhci-tds.c @@ -161,7 +161,7 @@ u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem, return -EINVAL; } - ep = kzalloc(sizeof(*ep), GFP_KERNEL); + ep = kzalloc_obj(*ep); if (!ep) return -ENOMEM; @@ -183,7 +183,7 @@ u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem, struct packet *pkt; u8 *buff; - pkt = kmalloc(sizeof(*pkt), GFP_KERNEL); + pkt = kmalloc_obj(*pkt); if (!pkt) { err_for = "frame"; goto err; diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h index 1f57b0989485..e221b28e8608 100644 --- a/drivers/usb/host/fhci.h +++ b/drivers/usb/host/fhci.h @@ -387,9 +387,10 @@ struct urb_priv { int tds_cnt; int state; - struct td **tds; struct ed *ed; struct timer_list time_out; + + struct td *tds[] __counted_by(num_of_tds); }; struct endpoint { diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 4e67b9471986..edfb63543029 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -327,8 +327,7 @@ static void fsl_usb2_mpc5121_exit(struct platform_device *pdev) pdata->regs = NULL; - if (pdata->clk) - clk_disable_unprepare(pdata->clk); + clk_disable_unprepare(pdata->clk); } static struct fsl_usb2_platform_data fsl_usb2_mpc5121_pd = { diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 71c22c4bd163..b5e9056895e4 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -703,7 +703,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, } /* avoid all allocations within spinlocks: request or endpoint */ if (!hep->hcpriv) { - ep = kzalloc(sizeof *ep, mem_flags); + ep = kzalloc_obj(*ep, mem_flags); if (!ep) return -ENOMEM; } diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c deleted file mode 100644 index 2d3a082cb52f..000000000000 --- a/drivers/usb/host/isp1362-hcd.c +++ /dev/null @@ -1,2769 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ISP1362 HCD (Host Controller Driver) for USB. - * - * Copyright (C) 2005 Lothar Wassmann <LW@KARO-electronics.de> - * - * Derived from the SL811 HCD, rewritten for ISP116x. - * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee> - * - * Portions: - * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) - * Copyright (C) 2004 David Brownell - */ - -/* - * The ISP1362 chip requires a large delay (300ns and 462ns) between - * accesses to the address and data register. - * The following timing options exist: - * - * 1. Configure your memory controller to add such delays if it can (the best) - * 2. Implement platform-specific delay function possibly - * combined with configuring the memory controller; see - * include/linux/usb_isp1362.h for more info. - * 3. Use ndelay (easiest, poorest). - * - * Use the corresponding macros USE_PLATFORM_DELAY and USE_NDELAY in the - * platform specific section of isp1362.h to select the appropriate variant. - * - * Also note that according to the Philips "ISP1362 Errata" document - * Rev 1.00 from 27 May data corruption may occur when the #WR signal - * is reasserted (even with #CS deasserted) within 132ns after a - * write cycle to any controller register. If the hardware doesn't - * implement the recommended fix (gating the #WR with #CS) software - * must ensure that no further write cycle (not necessarily to the chip!) - * is issued by the CPU within this interval. - - * For PXA25x this can be ensured by using VLIO with the maximum - * recovery time (MSCx = 0x7f8c) with a memory clock of 99.53 MHz. - */ - -#undef ISP1362_DEBUG - -/* - * The PXA255 UDC apparently doesn't handle GET_STATUS, GET_CONFIG and - * GET_INTERFACE requests correctly when the SETUP and DATA stages of the - * requests are carried out in separate frames. This will delay any SETUP - * packets until the start of the next frame so that this situation is - * unlikely to occur (and makes usbtest happy running with a PXA255 target - * device). - */ -#undef BUGGY_PXA2XX_UDC_USBTEST - -#undef PTD_TRACE -#undef URB_TRACE -#undef VERBOSE -#undef REGISTERS - -/* This enables a memory test on the ISP1362 chip memory to make sure the - * chip access timing is correct. - */ -#undef CHIP_BUFFER_TEST - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/list.h> -#include <linux/interrupt.h> -#include <linux/usb.h> -#include <linux/usb/isp1362.h> -#include <linux/usb/hcd.h> -#include <linux/platform_device.h> -#include <linux/pm.h> -#include <linux/io.h> -#include <linux/bitmap.h> -#include <linux/prefetch.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> - -#include <asm/irq.h> -#include <asm/byteorder.h> -#include <linux/unaligned.h> - -static int dbg_level; -#ifdef ISP1362_DEBUG -module_param(dbg_level, int, 0644); -#else -module_param(dbg_level, int, 0); -#endif - -#include "../core/usb.h" -#include "isp1362.h" - - -#define DRIVER_VERSION "2005-04-04" -#define DRIVER_DESC "ISP1362 USB Host Controller Driver" - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - -static const char hcd_name[] = "isp1362-hcd"; - -static void isp1362_hc_stop(struct usb_hcd *hcd); -static int isp1362_hc_start(struct usb_hcd *hcd); - -/*-------------------------------------------------------------------------*/ - -/* - * When called from the interrupthandler only isp1362_hcd->irqenb is modified, - * since the interrupt handler will write isp1362_hcd->irqenb to HCuPINT upon - * completion. - * We don't need a 'disable' counterpart, since interrupts will be disabled - * only by the interrupt handler. - */ -static inline void isp1362_enable_int(struct isp1362_hcd *isp1362_hcd, u16 mask) -{ - if ((isp1362_hcd->irqenb | mask) == isp1362_hcd->irqenb) - return; - if (mask & ~isp1362_hcd->irqenb) - isp1362_write_reg16(isp1362_hcd, HCuPINT, mask & ~isp1362_hcd->irqenb); - isp1362_hcd->irqenb |= mask; - if (isp1362_hcd->irq_active) - return; - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); -} - -/*-------------------------------------------------------------------------*/ - -static inline struct isp1362_ep_queue *get_ptd_queue(struct isp1362_hcd *isp1362_hcd, - u16 offset) -{ - struct isp1362_ep_queue *epq = NULL; - - if (offset < isp1362_hcd->istl_queue[1].buf_start) - epq = &isp1362_hcd->istl_queue[0]; - else if (offset < isp1362_hcd->intl_queue.buf_start) - epq = &isp1362_hcd->istl_queue[1]; - else if (offset < isp1362_hcd->atl_queue.buf_start) - epq = &isp1362_hcd->intl_queue; - else if (offset < isp1362_hcd->atl_queue.buf_start + - isp1362_hcd->atl_queue.buf_size) - epq = &isp1362_hcd->atl_queue; - - if (epq) - DBG(1, "%s: PTD $%04x is on %s queue\n", __func__, offset, epq->name); - else - pr_warn("%s: invalid PTD $%04x\n", __func__, offset); - - return epq; -} - -static inline int get_ptd_offset(struct isp1362_ep_queue *epq, u8 index) -{ - int offset; - - if (index * epq->blk_size > epq->buf_size) { - pr_warn("%s: Bad %s index %d(%d)\n", - __func__, epq->name, index, - epq->buf_size / epq->blk_size); - return -EINVAL; - } - offset = epq->buf_start + index * epq->blk_size; - DBG(3, "%s: %s PTD[%02x] # %04x\n", __func__, epq->name, index, offset); - - return offset; -} - -/*-------------------------------------------------------------------------*/ - -static inline u16 max_transfer_size(struct isp1362_ep_queue *epq, size_t size, - int mps) -{ - u16 xfer_size = min_t(size_t, MAX_XFER_SIZE, size); - - xfer_size = min_t(size_t, xfer_size, epq->buf_avail * epq->blk_size - PTD_HEADER_SIZE); - if (xfer_size < size && xfer_size % mps) - xfer_size -= xfer_size % mps; - - return xfer_size; -} - -static int claim_ptd_buffers(struct isp1362_ep_queue *epq, - struct isp1362_ep *ep, u16 len) -{ - int ptd_offset = -EINVAL; - int num_ptds = ((len + PTD_HEADER_SIZE - 1) / epq->blk_size) + 1; - int found; - - BUG_ON(len > epq->buf_size); - - if (!epq->buf_avail) - return -ENOMEM; - - if (ep->num_ptds) - pr_err("%s: %s len %d/%d num_ptds %d buf_map %08lx skip_map %08lx\n", __func__, - epq->name, len, epq->blk_size, num_ptds, epq->buf_map, epq->skip_map); - BUG_ON(ep->num_ptds != 0); - - found = bitmap_find_next_zero_area(&epq->buf_map, epq->buf_count, 0, - num_ptds, 0); - if (found >= epq->buf_count) - return -EOVERFLOW; - - DBG(1, "%s: Found %d PTDs[%d] for %d/%d byte\n", __func__, - num_ptds, found, len, (int)(epq->blk_size - PTD_HEADER_SIZE)); - ptd_offset = get_ptd_offset(epq, found); - WARN_ON(ptd_offset < 0); - ep->ptd_offset = ptd_offset; - ep->num_ptds += num_ptds; - epq->buf_avail -= num_ptds; - BUG_ON(epq->buf_avail > epq->buf_count); - ep->ptd_index = found; - bitmap_set(&epq->buf_map, found, num_ptds); - DBG(1, "%s: Done %s PTD[%d] $%04x, avail %d count %d claimed %d %08lx:%08lx\n", - __func__, epq->name, ep->ptd_index, ep->ptd_offset, - epq->buf_avail, epq->buf_count, num_ptds, epq->buf_map, epq->skip_map); - - return found; -} - -static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1362_ep *ep) -{ - int last = ep->ptd_index + ep->num_ptds; - - if (last > epq->buf_count) - pr_err("%s: ep %p req %d len %d %s PTD[%d] $%04x num_ptds %d buf_count %d buf_avail %d buf_map %08lx skip_map %08lx\n", - __func__, ep, ep->num_req, ep->length, epq->name, ep->ptd_index, - ep->ptd_offset, ep->num_ptds, epq->buf_count, epq->buf_avail, - epq->buf_map, epq->skip_map); - BUG_ON(last > epq->buf_count); - - bitmap_clear(&epq->buf_map, ep->ptd_index, ep->num_ptds); - bitmap_set(&epq->skip_map, ep->ptd_index, ep->num_ptds); - epq->buf_avail += ep->num_ptds; - epq->ptd_count--; - - BUG_ON(epq->buf_avail > epq->buf_count); - BUG_ON(epq->ptd_count > epq->buf_count); - - DBG(1, "%s: Done %s PTDs $%04x released %d avail %d count %d\n", - __func__, epq->name, - ep->ptd_offset, ep->num_ptds, epq->buf_avail, epq->buf_count); - DBG(1, "%s: buf_map %08lx skip_map %08lx\n", __func__, - epq->buf_map, epq->skip_map); - - ep->num_ptds = 0; - ep->ptd_offset = -EINVAL; - ep->ptd_index = -EINVAL; -} - -/*-------------------------------------------------------------------------*/ - -/* - Set up PTD's. -*/ -static void prepare_ptd(struct isp1362_hcd *isp1362_hcd, struct urb *urb, - struct isp1362_ep *ep, struct isp1362_ep_queue *epq, - u16 fno) -{ - struct ptd *ptd; - int toggle; - int dir; - u16 len; - size_t buf_len = urb->transfer_buffer_length - urb->actual_length; - - DBG(3, "%s: %s ep %p\n", __func__, epq->name, ep); - - ptd = &ep->ptd; - - ep->data = (unsigned char *)urb->transfer_buffer + urb->actual_length; - - switch (ep->nextpid) { - case USB_PID_IN: - toggle = usb_gettoggle(urb->dev, ep->epnum, 0); - dir = PTD_DIR_IN; - if (usb_pipecontrol(urb->pipe)) { - len = min_t(size_t, ep->maxpacket, buf_len); - } else if (usb_pipeisoc(urb->pipe)) { - len = min_t(size_t, urb->iso_frame_desc[fno].length, MAX_XFER_SIZE); - ep->data = urb->transfer_buffer + urb->iso_frame_desc[fno].offset; - } else - len = max_transfer_size(epq, buf_len, ep->maxpacket); - DBG(1, "%s: IN len %d/%d/%d from URB\n", __func__, len, ep->maxpacket, - (int)buf_len); - break; - case USB_PID_OUT: - toggle = usb_gettoggle(urb->dev, ep->epnum, 1); - dir = PTD_DIR_OUT; - if (usb_pipecontrol(urb->pipe)) - len = min_t(size_t, ep->maxpacket, buf_len); - else if (usb_pipeisoc(urb->pipe)) - len = min_t(size_t, urb->iso_frame_desc[0].length, MAX_XFER_SIZE); - else - len = max_transfer_size(epq, buf_len, ep->maxpacket); - if (len == 0) - pr_info("%s: Sending ZERO packet: %d\n", __func__, - urb->transfer_flags & URB_ZERO_PACKET); - DBG(1, "%s: OUT len %d/%d/%d from URB\n", __func__, len, ep->maxpacket, - (int)buf_len); - break; - case USB_PID_SETUP: - toggle = 0; - dir = PTD_DIR_SETUP; - len = sizeof(struct usb_ctrlrequest); - DBG(1, "%s: SETUP len %d\n", __func__, len); - ep->data = urb->setup_packet; - break; - case USB_PID_ACK: - toggle = 1; - len = 0; - dir = (urb->transfer_buffer_length && usb_pipein(urb->pipe)) ? - PTD_DIR_OUT : PTD_DIR_IN; - DBG(1, "%s: ACK len %d\n", __func__, len); - break; - default: - toggle = dir = len = 0; - pr_err("%s@%d: ep->nextpid %02x\n", __func__, __LINE__, ep->nextpid); - BUG_ON(1); - } - - ep->length = len; - if (!len) - ep->data = NULL; - - ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle); - ptd->mps = PTD_MPS(ep->maxpacket) | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) | - PTD_EP(ep->epnum); - ptd->len = PTD_LEN(len) | PTD_DIR(dir); - ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe)); - - if (usb_pipeint(urb->pipe)) { - ptd->faddr |= PTD_SF_INT(ep->branch); - ptd->faddr |= PTD_PR(ep->interval ? __ffs(ep->interval) : 0); - } - if (usb_pipeisoc(urb->pipe)) - ptd->faddr |= PTD_SF_ISO(fno); - - DBG(1, "%s: Finished\n", __func__); -} - -static void isp1362_write_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, - struct isp1362_ep_queue *epq) -{ - struct ptd *ptd = &ep->ptd; - int len = PTD_GET_DIR(ptd) == PTD_DIR_IN ? 0 : ep->length; - - prefetch(ptd); - isp1362_write_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE); - if (len) - isp1362_write_buffer(isp1362_hcd, ep->data, - ep->ptd_offset + PTD_HEADER_SIZE, len); - - dump_ptd(ptd); - dump_ptd_out_data(ptd, ep->data); -} - -static void isp1362_read_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, - struct isp1362_ep_queue *epq) -{ - struct ptd *ptd = &ep->ptd; - int act_len; - - WARN_ON(list_empty(&ep->active)); - BUG_ON(ep->ptd_offset < 0); - - list_del_init(&ep->active); - DBG(1, "%s: ep %p removed from active list %p\n", __func__, ep, &epq->active); - - prefetchw(ptd); - isp1362_read_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE); - dump_ptd(ptd); - act_len = PTD_GET_COUNT(ptd); - if (PTD_GET_DIR(ptd) != PTD_DIR_IN || act_len == 0) - return; - if (act_len > ep->length) - pr_err("%s: ep %p PTD $%04x act_len %d ep->length %d\n", __func__, ep, - ep->ptd_offset, act_len, ep->length); - BUG_ON(act_len > ep->length); - /* Only transfer the amount of data that has actually been overwritten - * in the chip buffer. We don't want any data that doesn't belong to the - * transfer to leak out of the chip to the callers transfer buffer! - */ - prefetchw(ep->data); - isp1362_read_buffer(isp1362_hcd, ep->data, - ep->ptd_offset + PTD_HEADER_SIZE, act_len); - dump_ptd_in_data(ptd, ep->data); -} - -/* - * INT PTDs will stay in the chip until data is available. - * This function will remove a PTD from the chip when the URB is dequeued. - * Must be called with the spinlock held and IRQs disabled - */ -static void remove_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep) - -{ - int index; - struct isp1362_ep_queue *epq; - - DBG(1, "%s: ep %p PTD[%d] $%04x\n", __func__, ep, ep->ptd_index, ep->ptd_offset); - BUG_ON(ep->ptd_offset < 0); - - epq = get_ptd_queue(isp1362_hcd, ep->ptd_offset); - BUG_ON(!epq); - - /* put ep in remove_list for cleanup */ - WARN_ON(!list_empty(&ep->remove_list)); - list_add_tail(&ep->remove_list, &isp1362_hcd->remove_list); - /* let SOF interrupt handle the cleanup */ - isp1362_enable_int(isp1362_hcd, HCuPINT_SOF); - - index = ep->ptd_index; - if (index < 0) - /* ISO queues don't have SKIP registers */ - return; - - DBG(1, "%s: Disabling PTD[%02x] $%04x %08lx|%08x\n", __func__, - index, ep->ptd_offset, epq->skip_map, 1 << index); - - /* prevent further processing of PTD (will be effective after next SOF) */ - epq->skip_map |= 1 << index; - if (epq == &isp1362_hcd->atl_queue) { - DBG(2, "%s: ATLSKIP = %08x -> %08lx\n", __func__, - isp1362_read_reg32(isp1362_hcd, HCATLSKIP), epq->skip_map); - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, epq->skip_map); - if (~epq->skip_map == 0) - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); - } else if (epq == &isp1362_hcd->intl_queue) { - DBG(2, "%s: INTLSKIP = %08x -> %08lx\n", __func__, - isp1362_read_reg32(isp1362_hcd, HCINTLSKIP), epq->skip_map); - isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, epq->skip_map); - if (~epq->skip_map == 0) - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); - } -} - -/* - Take done or failed requests out of schedule. Give back - processed urbs. -*/ -static void finish_request(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, - struct urb *urb, int status) - __releases(isp1362_hcd->lock) - __acquires(isp1362_hcd->lock) -{ - urb->hcpriv = NULL; - ep->error_count = 0; - - if (usb_pipecontrol(urb->pipe)) - ep->nextpid = USB_PID_SETUP; - - URB_DBG("%s: req %d FA %d ep%d%s %s: len %d/%d %s stat %d\n", __func__, - ep->num_req, usb_pipedevice(urb->pipe), - usb_pipeendpoint(urb->pipe), - !usb_pipein(urb->pipe) ? "out" : "in", - usb_pipecontrol(urb->pipe) ? "ctrl" : - usb_pipeint(urb->pipe) ? "int" : - usb_pipebulk(urb->pipe) ? "bulk" : - "iso", - urb->actual_length, urb->transfer_buffer_length, - !(urb->transfer_flags & URB_SHORT_NOT_OK) ? - "short_ok" : "", urb->status); - - - usb_hcd_unlink_urb_from_ep(isp1362_hcd_to_hcd(isp1362_hcd), urb); - spin_unlock(&isp1362_hcd->lock); - usb_hcd_giveback_urb(isp1362_hcd_to_hcd(isp1362_hcd), urb, status); - spin_lock(&isp1362_hcd->lock); - - /* take idle endpoints out of the schedule right away */ - if (!list_empty(&ep->hep->urb_list)) - return; - - /* async deschedule */ - if (!list_empty(&ep->schedule)) { - list_del_init(&ep->schedule); - return; - } - - - if (ep->interval) { - /* periodic deschedule */ - DBG(1, "deschedule qh%d/%p branch %d load %d bandwidth %d -> %d\n", ep->interval, - ep, ep->branch, ep->load, - isp1362_hcd->load[ep->branch], - isp1362_hcd->load[ep->branch] - ep->load); - isp1362_hcd->load[ep->branch] -= ep->load; - ep->branch = PERIODIC_SIZE; - } -} - -/* - * Analyze transfer results, handle partial transfers and errors -*/ -static void postproc_ep(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep) -{ - struct urb *urb = get_urb(ep); - struct usb_device *udev; - struct ptd *ptd; - int short_ok; - u16 len; - int urbstat = -EINPROGRESS; - u8 cc; - - DBG(2, "%s: ep %p req %d\n", __func__, ep, ep->num_req); - - udev = urb->dev; - ptd = &ep->ptd; - cc = PTD_GET_CC(ptd); - if (cc == PTD_NOTACCESSED) { - pr_err("%s: req %d PTD %p Untouched by ISP1362\n", __func__, - ep->num_req, ptd); - cc = PTD_DEVNOTRESP; - } - - short_ok = !(urb->transfer_flags & URB_SHORT_NOT_OK); - len = urb->transfer_buffer_length - urb->actual_length; - - /* Data underrun is special. For allowed underrun - we clear the error and continue as normal. For - forbidden underrun we finish the DATA stage - immediately while for control transfer, - we do a STATUS stage. - */ - if (cc == PTD_DATAUNDERRUN) { - if (short_ok) { - DBG(1, "%s: req %d Allowed data underrun short_%sok %d/%d/%d byte\n", - __func__, ep->num_req, short_ok ? "" : "not_", - PTD_GET_COUNT(ptd), ep->maxpacket, len); - cc = PTD_CC_NOERROR; - urbstat = 0; - } else { - DBG(1, "%s: req %d Data Underrun %s nextpid %02x short_%sok %d/%d/%d byte\n", - __func__, ep->num_req, - usb_pipein(urb->pipe) ? "IN" : "OUT", ep->nextpid, - short_ok ? "" : "not_", - PTD_GET_COUNT(ptd), ep->maxpacket, len); - /* save the data underrun error code for later and - * proceed with the status stage - */ - urb->actual_length += PTD_GET_COUNT(ptd); - if (usb_pipecontrol(urb->pipe)) { - ep->nextpid = USB_PID_ACK; - BUG_ON(urb->actual_length > urb->transfer_buffer_length); - - if (urb->status == -EINPROGRESS) - urb->status = cc_to_error[PTD_DATAUNDERRUN]; - } else { - usb_settoggle(udev, ep->epnum, ep->nextpid == USB_PID_OUT, - PTD_GET_TOGGLE(ptd)); - urbstat = cc_to_error[PTD_DATAUNDERRUN]; - } - goto out; - } - } - - if (cc != PTD_CC_NOERROR) { - if (++ep->error_count >= 3 || cc == PTD_CC_STALL || cc == PTD_DATAOVERRUN) { - urbstat = cc_to_error[cc]; - DBG(1, "%s: req %d nextpid %02x, status %d, error %d, error_count %d\n", - __func__, ep->num_req, ep->nextpid, urbstat, cc, - ep->error_count); - } - goto out; - } - - switch (ep->nextpid) { - case USB_PID_OUT: - if (PTD_GET_COUNT(ptd) != ep->length) - pr_err("%s: count=%d len=%d\n", __func__, - PTD_GET_COUNT(ptd), ep->length); - BUG_ON(PTD_GET_COUNT(ptd) != ep->length); - urb->actual_length += ep->length; - BUG_ON(urb->actual_length > urb->transfer_buffer_length); - usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd)); - if (urb->actual_length == urb->transfer_buffer_length) { - DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__, - ep->num_req, len, ep->maxpacket, urbstat); - if (usb_pipecontrol(urb->pipe)) { - DBG(3, "%s: req %d %s Wait for ACK\n", __func__, - ep->num_req, - usb_pipein(urb->pipe) ? "IN" : "OUT"); - ep->nextpid = USB_PID_ACK; - } else { - if (len % ep->maxpacket || - !(urb->transfer_flags & URB_ZERO_PACKET)) { - urbstat = 0; - DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n", - __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT", - urbstat, len, ep->maxpacket, urb->actual_length); - } - } - } - break; - case USB_PID_IN: - len = PTD_GET_COUNT(ptd); - BUG_ON(len > ep->length); - urb->actual_length += len; - BUG_ON(urb->actual_length > urb->transfer_buffer_length); - usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd)); - /* if transfer completed or (allowed) data underrun */ - if ((urb->transfer_buffer_length == urb->actual_length) || - len % ep->maxpacket) { - DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__, - ep->num_req, len, ep->maxpacket, urbstat); - if (usb_pipecontrol(urb->pipe)) { - DBG(3, "%s: req %d %s Wait for ACK\n", __func__, - ep->num_req, - usb_pipein(urb->pipe) ? "IN" : "OUT"); - ep->nextpid = USB_PID_ACK; - } else { - urbstat = 0; - DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n", - __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT", - urbstat, len, ep->maxpacket, urb->actual_length); - } - } - break; - case USB_PID_SETUP: - if (urb->transfer_buffer_length == urb->actual_length) { - ep->nextpid = USB_PID_ACK; - } else if (usb_pipeout(urb->pipe)) { - usb_settoggle(udev, 0, 1, 1); - ep->nextpid = USB_PID_OUT; - } else { - usb_settoggle(udev, 0, 0, 1); - ep->nextpid = USB_PID_IN; - } - break; - case USB_PID_ACK: - DBG(3, "%s: req %d got ACK %d -> 0\n", __func__, ep->num_req, - urbstat); - WARN_ON(urbstat != -EINPROGRESS); - urbstat = 0; - ep->nextpid = 0; - break; - default: - BUG_ON(1); - } - - out: - if (urbstat != -EINPROGRESS) { - DBG(2, "%s: Finishing ep %p req %d urb %p status %d\n", __func__, - ep, ep->num_req, urb, urbstat); - finish_request(isp1362_hcd, ep, urb, urbstat); - } -} - -static void finish_unlinks(struct isp1362_hcd *isp1362_hcd) -{ - struct isp1362_ep *ep; - struct isp1362_ep *tmp; - - list_for_each_entry_safe(ep, tmp, &isp1362_hcd->remove_list, remove_list) { - struct isp1362_ep_queue *epq = - get_ptd_queue(isp1362_hcd, ep->ptd_offset); - int index = ep->ptd_index; - - BUG_ON(epq == NULL); - if (index >= 0) { - DBG(1, "%s: remove PTD[%d] $%04x\n", __func__, index, ep->ptd_offset); - BUG_ON(ep->num_ptds == 0); - release_ptd_buffers(epq, ep); - } - if (!list_empty(&ep->hep->urb_list)) { - struct urb *urb = get_urb(ep); - - DBG(1, "%s: Finishing req %d ep %p from remove_list\n", __func__, - ep->num_req, ep); - finish_request(isp1362_hcd, ep, urb, -ESHUTDOWN); - } - WARN_ON(list_empty(&ep->active)); - if (!list_empty(&ep->active)) { - list_del_init(&ep->active); - DBG(1, "%s: ep %p removed from active list\n", __func__, ep); - } - list_del_init(&ep->remove_list); - DBG(1, "%s: ep %p removed from remove_list\n", __func__, ep); - } - DBG(1, "%s: Done\n", __func__); -} - -static inline void enable_atl_transfers(struct isp1362_hcd *isp1362_hcd, int count) -{ - if (count > 0) { - if (count < isp1362_hcd->atl_queue.ptd_count) - isp1362_write_reg16(isp1362_hcd, HCATLDTC, count); - isp1362_enable_int(isp1362_hcd, HCuPINT_ATL); - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, isp1362_hcd->atl_queue.skip_map); - isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); - } else - isp1362_enable_int(isp1362_hcd, HCuPINT_SOF); -} - -static inline void enable_intl_transfers(struct isp1362_hcd *isp1362_hcd) -{ - isp1362_enable_int(isp1362_hcd, HCuPINT_INTL); - isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); - isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, isp1362_hcd->intl_queue.skip_map); -} - -static inline void enable_istl_transfers(struct isp1362_hcd *isp1362_hcd, int flip) -{ - isp1362_enable_int(isp1362_hcd, flip ? HCuPINT_ISTL1 : HCuPINT_ISTL0); - isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, flip ? - HCBUFSTAT_ISTL1_FULL : HCBUFSTAT_ISTL0_FULL); -} - -static int submit_req(struct isp1362_hcd *isp1362_hcd, struct urb *urb, - struct isp1362_ep *ep, struct isp1362_ep_queue *epq) -{ - int index; - - prepare_ptd(isp1362_hcd, urb, ep, epq, 0); - index = claim_ptd_buffers(epq, ep, ep->length); - if (index == -ENOMEM) { - DBG(1, "%s: req %d No free %s PTD available: %d, %08lx:%08lx\n", __func__, - ep->num_req, epq->name, ep->num_ptds, epq->buf_map, epq->skip_map); - return index; - } else if (index == -EOVERFLOW) { - DBG(1, "%s: req %d Not enough space for %d byte %s PTD %d %08lx:%08lx\n", - __func__, ep->num_req, ep->length, epq->name, ep->num_ptds, - epq->buf_map, epq->skip_map); - return index; - } else - BUG_ON(index < 0); - list_add_tail(&ep->active, &epq->active); - DBG(1, "%s: ep %p req %d len %d added to active list %p\n", __func__, - ep, ep->num_req, ep->length, &epq->active); - DBG(1, "%s: Submitting %s PTD $%04x for ep %p req %d\n", __func__, epq->name, - ep->ptd_offset, ep, ep->num_req); - isp1362_write_ptd(isp1362_hcd, ep, epq); - __clear_bit(ep->ptd_index, &epq->skip_map); - - return 0; -} - -static void start_atl_transfers(struct isp1362_hcd *isp1362_hcd) -{ - int ptd_count = 0; - struct isp1362_ep_queue *epq = &isp1362_hcd->atl_queue; - struct isp1362_ep *ep; - int defer = 0; - - if (atomic_read(&epq->finishing)) { - DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); - return; - } - - list_for_each_entry(ep, &isp1362_hcd->async, schedule) { - struct urb *urb = get_urb(ep); - int ret; - - if (!list_empty(&ep->active)) { - DBG(2, "%s: Skipping active %s ep %p\n", __func__, epq->name, ep); - continue; - } - - DBG(1, "%s: Processing %s ep %p req %d\n", __func__, epq->name, - ep, ep->num_req); - - ret = submit_req(isp1362_hcd, urb, ep, epq); - if (ret == -ENOMEM) { - defer = 1; - break; - } else if (ret == -EOVERFLOW) { - defer = 1; - continue; - } -#ifdef BUGGY_PXA2XX_UDC_USBTEST - defer = ep->nextpid == USB_PID_SETUP; -#endif - ptd_count++; - } - - /* Avoid starving of endpoints */ - if (isp1362_hcd->async.next != isp1362_hcd->async.prev) { - DBG(2, "%s: Cycling ASYNC schedule %d\n", __func__, ptd_count); - list_move(&isp1362_hcd->async, isp1362_hcd->async.next); - } - if (ptd_count || defer) - enable_atl_transfers(isp1362_hcd, defer ? 0 : ptd_count); - - epq->ptd_count += ptd_count; - if (epq->ptd_count > epq->stat_maxptds) { - epq->stat_maxptds = epq->ptd_count; - DBG(0, "%s: max_ptds: %d\n", __func__, epq->stat_maxptds); - } -} - -static void start_intl_transfers(struct isp1362_hcd *isp1362_hcd) -{ - int ptd_count = 0; - struct isp1362_ep_queue *epq = &isp1362_hcd->intl_queue; - struct isp1362_ep *ep; - - if (atomic_read(&epq->finishing)) { - DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); - return; - } - - list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) { - struct urb *urb = get_urb(ep); - int ret; - - if (!list_empty(&ep->active)) { - DBG(1, "%s: Skipping active %s ep %p\n", __func__, - epq->name, ep); - continue; - } - - DBG(1, "%s: Processing %s ep %p req %d\n", __func__, - epq->name, ep, ep->num_req); - ret = submit_req(isp1362_hcd, urb, ep, epq); - if (ret == -ENOMEM) - break; - else if (ret == -EOVERFLOW) - continue; - ptd_count++; - } - - if (ptd_count) { - static int last_count; - - if (ptd_count != last_count) { - DBG(0, "%s: ptd_count: %d\n", __func__, ptd_count); - last_count = ptd_count; - } - enable_intl_transfers(isp1362_hcd); - } - - epq->ptd_count += ptd_count; - if (epq->ptd_count > epq->stat_maxptds) - epq->stat_maxptds = epq->ptd_count; -} - -static inline int next_ptd(struct isp1362_ep_queue *epq, struct isp1362_ep *ep) -{ - u16 ptd_offset = ep->ptd_offset; - int num_ptds = (ep->length + PTD_HEADER_SIZE + (epq->blk_size - 1)) / epq->blk_size; - - DBG(2, "%s: PTD offset $%04x + %04x => %d * %04x -> $%04x\n", __func__, ptd_offset, - ep->length, num_ptds, epq->blk_size, ptd_offset + num_ptds * epq->blk_size); - - ptd_offset += num_ptds * epq->blk_size; - if (ptd_offset < epq->buf_start + epq->buf_size) - return ptd_offset; - else - return -ENOMEM; -} - -static void start_iso_transfers(struct isp1362_hcd *isp1362_hcd) -{ - int ptd_count = 0; - int flip = isp1362_hcd->istl_flip; - struct isp1362_ep_queue *epq; - int ptd_offset; - struct isp1362_ep *ep; - struct isp1362_ep *tmp; - u16 fno = isp1362_read_reg32(isp1362_hcd, HCFMNUM); - - fill2: - epq = &isp1362_hcd->istl_queue[flip]; - if (atomic_read(&epq->finishing)) { - DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); - return; - } - - if (!list_empty(&epq->active)) - return; - - ptd_offset = epq->buf_start; - list_for_each_entry_safe(ep, tmp, &isp1362_hcd->isoc, schedule) { - struct urb *urb = get_urb(ep); - s16 diff = fno - (u16)urb->start_frame; - - DBG(1, "%s: Processing %s ep %p\n", __func__, epq->name, ep); - - if (diff > urb->number_of_packets) { - /* time frame for this URB has elapsed */ - finish_request(isp1362_hcd, ep, urb, -EOVERFLOW); - continue; - } else if (diff < -1) { - /* URB is not due in this frame or the next one. - * Comparing with '-1' instead of '0' accounts for double - * buffering in the ISP1362 which enables us to queue the PTD - * one frame ahead of time - */ - } else if (diff == -1) { - /* submit PTD's that are due in the next frame */ - prepare_ptd(isp1362_hcd, urb, ep, epq, fno); - if (ptd_offset + PTD_HEADER_SIZE + ep->length > - epq->buf_start + epq->buf_size) { - pr_err("%s: Not enough ISO buffer space for %d byte PTD\n", - __func__, ep->length); - continue; - } - ep->ptd_offset = ptd_offset; - list_add_tail(&ep->active, &epq->active); - - ptd_offset = next_ptd(epq, ep); - if (ptd_offset < 0) { - pr_warn("%s: req %d No more %s PTD buffers available\n", - __func__, ep->num_req, epq->name); - break; - } - } - } - list_for_each_entry(ep, &epq->active, active) { - if (epq->active.next == &ep->active) - ep->ptd.mps |= PTD_LAST_MSK; - isp1362_write_ptd(isp1362_hcd, ep, epq); - ptd_count++; - } - - if (ptd_count) - enable_istl_transfers(isp1362_hcd, flip); - - epq->ptd_count += ptd_count; - if (epq->ptd_count > epq->stat_maxptds) - epq->stat_maxptds = epq->ptd_count; - - /* check, whether the second ISTL buffer may also be filled */ - if (!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & - (flip ? HCBUFSTAT_ISTL0_FULL : HCBUFSTAT_ISTL1_FULL))) { - fno++; - ptd_count = 0; - flip = 1 - flip; - goto fill2; - } -} - -static void finish_transfers(struct isp1362_hcd *isp1362_hcd, unsigned long done_map, - struct isp1362_ep_queue *epq) -{ - struct isp1362_ep *ep; - struct isp1362_ep *tmp; - - if (list_empty(&epq->active)) { - DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name); - return; - } - - DBG(1, "%s: Finishing %s transfers %08lx\n", __func__, epq->name, done_map); - - atomic_inc(&epq->finishing); - list_for_each_entry_safe(ep, tmp, &epq->active, active) { - int index = ep->ptd_index; - - DBG(1, "%s: Checking %s PTD[%02x] $%04x\n", __func__, epq->name, - index, ep->ptd_offset); - - BUG_ON(index < 0); - if (__test_and_clear_bit(index, &done_map)) { - isp1362_read_ptd(isp1362_hcd, ep, epq); - epq->free_ptd = index; - BUG_ON(ep->num_ptds == 0); - release_ptd_buffers(epq, ep); - - DBG(1, "%s: ep %p req %d removed from active list\n", __func__, - ep, ep->num_req); - if (!list_empty(&ep->remove_list)) { - list_del_init(&ep->remove_list); - DBG(1, "%s: ep %p removed from remove list\n", __func__, ep); - } - DBG(1, "%s: Postprocessing %s ep %p req %d\n", __func__, epq->name, - ep, ep->num_req); - postproc_ep(isp1362_hcd, ep); - } - if (!done_map) - break; - } - if (done_map) - pr_warn("%s: done_map not clear: %08lx:%08lx\n", - __func__, done_map, epq->skip_map); - atomic_dec(&epq->finishing); -} - -static void finish_iso_transfers(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep_queue *epq) -{ - struct isp1362_ep *ep; - struct isp1362_ep *tmp; - - if (list_empty(&epq->active)) { - DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name); - return; - } - - DBG(1, "%s: Finishing %s transfers\n", __func__, epq->name); - - atomic_inc(&epq->finishing); - list_for_each_entry_safe(ep, tmp, &epq->active, active) { - DBG(1, "%s: Checking PTD $%04x\n", __func__, ep->ptd_offset); - - isp1362_read_ptd(isp1362_hcd, ep, epq); - DBG(1, "%s: Postprocessing %s ep %p\n", __func__, epq->name, ep); - postproc_ep(isp1362_hcd, ep); - } - WARN_ON(epq->blk_size != 0); - atomic_dec(&epq->finishing); -} - -static irqreturn_t isp1362_irq(struct usb_hcd *hcd) -{ - int handled = 0; - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - u16 irqstat; - u16 svc_mask; - - spin_lock(&isp1362_hcd->lock); - - BUG_ON(isp1362_hcd->irq_active++); - - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); - - irqstat = isp1362_read_reg16(isp1362_hcd, HCuPINT); - DBG(3, "%s: got IRQ %04x:%04x\n", __func__, irqstat, isp1362_hcd->irqenb); - - /* only handle interrupts that are currently enabled */ - irqstat &= isp1362_hcd->irqenb; - isp1362_write_reg16(isp1362_hcd, HCuPINT, irqstat); - svc_mask = irqstat; - - if (irqstat & HCuPINT_SOF) { - isp1362_hcd->irqenb &= ~HCuPINT_SOF; - isp1362_hcd->irq_stat[ISP1362_INT_SOF]++; - handled = 1; - svc_mask &= ~HCuPINT_SOF; - DBG(3, "%s: SOF\n", __func__); - isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM); - if (!list_empty(&isp1362_hcd->remove_list)) - finish_unlinks(isp1362_hcd); - if (!list_empty(&isp1362_hcd->async) && !(irqstat & HCuPINT_ATL)) { - if (list_empty(&isp1362_hcd->atl_queue.active)) { - start_atl_transfers(isp1362_hcd); - } else { - isp1362_enable_int(isp1362_hcd, HCuPINT_ATL); - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, - isp1362_hcd->atl_queue.skip_map); - isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); - } - } - } - - if (irqstat & HCuPINT_ISTL0) { - isp1362_hcd->irq_stat[ISP1362_INT_ISTL0]++; - handled = 1; - svc_mask &= ~HCuPINT_ISTL0; - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL0_FULL); - DBG(1, "%s: ISTL0\n", __func__); - WARN_ON((int)!!isp1362_hcd->istl_flip); - WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & - HCBUFSTAT_ISTL0_ACTIVE); - WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & - HCBUFSTAT_ISTL0_DONE)); - isp1362_hcd->irqenb &= ~HCuPINT_ISTL0; - } - - if (irqstat & HCuPINT_ISTL1) { - isp1362_hcd->irq_stat[ISP1362_INT_ISTL1]++; - handled = 1; - svc_mask &= ~HCuPINT_ISTL1; - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL1_FULL); - DBG(1, "%s: ISTL1\n", __func__); - WARN_ON(!(int)isp1362_hcd->istl_flip); - WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & - HCBUFSTAT_ISTL1_ACTIVE); - WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & - HCBUFSTAT_ISTL1_DONE)); - isp1362_hcd->irqenb &= ~HCuPINT_ISTL1; - } - - if (irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) { - WARN_ON((irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) == - (HCuPINT_ISTL0 | HCuPINT_ISTL1)); - finish_iso_transfers(isp1362_hcd, - &isp1362_hcd->istl_queue[isp1362_hcd->istl_flip]); - start_iso_transfers(isp1362_hcd); - isp1362_hcd->istl_flip = 1 - isp1362_hcd->istl_flip; - } - - if (irqstat & HCuPINT_INTL) { - u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE); - u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCINTLSKIP); - isp1362_hcd->irq_stat[ISP1362_INT_INTL]++; - - DBG(2, "%s: INTL\n", __func__); - - svc_mask &= ~HCuPINT_INTL; - - isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, skip_map | done_map); - if (~(done_map | skip_map) == 0) - /* All PTDs are finished, disable INTL processing entirely */ - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); - - handled = 1; - WARN_ON(!done_map); - if (done_map) { - DBG(3, "%s: INTL done_map %08x\n", __func__, done_map); - finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue); - start_intl_transfers(isp1362_hcd); - } - } - - if (irqstat & HCuPINT_ATL) { - u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE); - u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCATLSKIP); - isp1362_hcd->irq_stat[ISP1362_INT_ATL]++; - - DBG(2, "%s: ATL\n", __func__); - - svc_mask &= ~HCuPINT_ATL; - - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, skip_map | done_map); - if (~(done_map | skip_map) == 0) - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); - if (done_map) { - DBG(3, "%s: ATL done_map %08x\n", __func__, done_map); - finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue); - start_atl_transfers(isp1362_hcd); - } - handled = 1; - } - - if (irqstat & HCuPINT_OPR) { - u32 intstat = isp1362_read_reg32(isp1362_hcd, HCINTSTAT); - isp1362_hcd->irq_stat[ISP1362_INT_OPR]++; - - svc_mask &= ~HCuPINT_OPR; - DBG(2, "%s: OPR %08x:%08x\n", __func__, intstat, isp1362_hcd->intenb); - intstat &= isp1362_hcd->intenb; - if (intstat & OHCI_INTR_UE) { - pr_err("Unrecoverable error\n"); - /* FIXME: do here reset or cleanup or whatever */ - } - if (intstat & OHCI_INTR_RHSC) { - isp1362_hcd->rhstatus = isp1362_read_reg32(isp1362_hcd, HCRHSTATUS); - isp1362_hcd->rhport[0] = isp1362_read_reg32(isp1362_hcd, HCRHPORT1); - isp1362_hcd->rhport[1] = isp1362_read_reg32(isp1362_hcd, HCRHPORT2); - } - if (intstat & OHCI_INTR_RD) { - pr_info("%s: RESUME DETECTED\n", __func__); - isp1362_show_reg(isp1362_hcd, HCCONTROL); - usb_hcd_resume_root_hub(hcd); - } - isp1362_write_reg32(isp1362_hcd, HCINTSTAT, intstat); - irqstat &= ~HCuPINT_OPR; - handled = 1; - } - - if (irqstat & HCuPINT_SUSP) { - isp1362_hcd->irq_stat[ISP1362_INT_SUSP]++; - handled = 1; - svc_mask &= ~HCuPINT_SUSP; - - pr_info("%s: SUSPEND IRQ\n", __func__); - } - - if (irqstat & HCuPINT_CLKRDY) { - isp1362_hcd->irq_stat[ISP1362_INT_CLKRDY]++; - handled = 1; - isp1362_hcd->irqenb &= ~HCuPINT_CLKRDY; - svc_mask &= ~HCuPINT_CLKRDY; - pr_info("%s: CLKRDY IRQ\n", __func__); - } - - if (svc_mask) - pr_err("%s: Unserviced interrupt(s) %04x\n", __func__, svc_mask); - - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); - isp1362_hcd->irq_active--; - spin_unlock(&isp1362_hcd->lock); - - return IRQ_RETVAL(handled); -} - -/*-------------------------------------------------------------------------*/ - -#define MAX_PERIODIC_LOAD 900 /* out of 1000 usec */ -static int balance(struct isp1362_hcd *isp1362_hcd, u16 interval, u16 load) -{ - int i, branch = -ENOSPC; - - /* search for the least loaded schedule branch of that interval - * which has enough bandwidth left unreserved. - */ - for (i = 0; i < interval; i++) { - if (branch < 0 || isp1362_hcd->load[branch] > isp1362_hcd->load[i]) { - int j; - - for (j = i; j < PERIODIC_SIZE; j += interval) { - if ((isp1362_hcd->load[j] + load) > MAX_PERIODIC_LOAD) { - pr_err("%s: new load %d load[%02x] %d max %d\n", __func__, - load, j, isp1362_hcd->load[j], MAX_PERIODIC_LOAD); - break; - } - } - if (j < PERIODIC_SIZE) - continue; - branch = i; - } - } - return branch; -} - -/* NB! ALL the code above this point runs with isp1362_hcd->lock - held, irqs off -*/ - -/*-------------------------------------------------------------------------*/ - -static int isp1362_urb_enqueue(struct usb_hcd *hcd, - struct urb *urb, - gfp_t mem_flags) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - struct usb_device *udev = urb->dev; - unsigned int pipe = urb->pipe; - int is_out = !usb_pipein(pipe); - int type = usb_pipetype(pipe); - int epnum = usb_pipeendpoint(pipe); - struct usb_host_endpoint *hep = urb->ep; - struct isp1362_ep *ep = NULL; - unsigned long flags; - int retval = 0; - - DBG(3, "%s: urb %p\n", __func__, urb); - - if (type == PIPE_ISOCHRONOUS) { - pr_err("Isochronous transfers not supported\n"); - return -ENOSPC; - } - - URB_DBG("%s: FA %d ep%d%s %s: len %d %s%s\n", __func__, - usb_pipedevice(pipe), epnum, - is_out ? "out" : "in", - usb_pipecontrol(pipe) ? "ctrl" : - usb_pipeint(pipe) ? "int" : - usb_pipebulk(pipe) ? "bulk" : - "iso", - urb->transfer_buffer_length, - (urb->transfer_flags & URB_ZERO_PACKET) ? "ZERO_PACKET " : "", - !(urb->transfer_flags & URB_SHORT_NOT_OK) ? - "short_ok" : ""); - - /* avoid all allocations within spinlocks: request or endpoint */ - if (!hep->hcpriv) { - ep = kzalloc(sizeof *ep, mem_flags); - if (!ep) - return -ENOMEM; - } - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - /* don't submit to a dead or disabled port */ - if (!((isp1362_hcd->rhport[0] | isp1362_hcd->rhport[1]) & - USB_PORT_STAT_ENABLE) || - !HC_IS_RUNNING(hcd->state)) { - kfree(ep); - retval = -ENODEV; - goto fail_not_linked; - } - - retval = usb_hcd_link_urb_to_ep(hcd, urb); - if (retval) { - kfree(ep); - goto fail_not_linked; - } - - if (hep->hcpriv) { - ep = hep->hcpriv; - } else { - INIT_LIST_HEAD(&ep->schedule); - INIT_LIST_HEAD(&ep->active); - INIT_LIST_HEAD(&ep->remove_list); - ep->udev = usb_get_dev(udev); - ep->hep = hep; - ep->epnum = epnum; - ep->maxpacket = usb_maxpacket(udev, urb->pipe); - ep->ptd_offset = -EINVAL; - ep->ptd_index = -EINVAL; - usb_settoggle(udev, epnum, is_out, 0); - - if (type == PIPE_CONTROL) - ep->nextpid = USB_PID_SETUP; - else if (is_out) - ep->nextpid = USB_PID_OUT; - else - ep->nextpid = USB_PID_IN; - - switch (type) { - case PIPE_ISOCHRONOUS: - case PIPE_INTERRUPT: - if (urb->interval > PERIODIC_SIZE) - urb->interval = PERIODIC_SIZE; - ep->interval = urb->interval; - ep->branch = PERIODIC_SIZE; - ep->load = usb_calc_bus_time(udev->speed, !is_out, - type == PIPE_ISOCHRONOUS, - usb_maxpacket(udev, pipe)) / 1000; - break; - } - hep->hcpriv = ep; - } - ep->num_req = isp1362_hcd->req_serial++; - - /* maybe put endpoint into schedule */ - switch (type) { - case PIPE_CONTROL: - case PIPE_BULK: - if (list_empty(&ep->schedule)) { - DBG(1, "%s: Adding ep %p req %d to async schedule\n", - __func__, ep, ep->num_req); - list_add_tail(&ep->schedule, &isp1362_hcd->async); - } - break; - case PIPE_ISOCHRONOUS: - case PIPE_INTERRUPT: - urb->interval = ep->interval; - - /* urb submitted for already existing EP */ - if (ep->branch < PERIODIC_SIZE) - break; - - retval = balance(isp1362_hcd, ep->interval, ep->load); - if (retval < 0) { - pr_err("%s: balance returned %d\n", __func__, retval); - goto fail; - } - ep->branch = retval; - retval = 0; - isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM); - DBG(1, "%s: Current frame %04x branch %02x start_frame %04x(%04x)\n", - __func__, isp1362_hcd->fmindex, ep->branch, - ((isp1362_hcd->fmindex + PERIODIC_SIZE - 1) & - ~(PERIODIC_SIZE - 1)) + ep->branch, - (isp1362_hcd->fmindex & (PERIODIC_SIZE - 1)) + ep->branch); - - if (list_empty(&ep->schedule)) { - if (type == PIPE_ISOCHRONOUS) { - u16 frame = isp1362_hcd->fmindex; - - frame += max_t(u16, 8, ep->interval); - frame &= ~(ep->interval - 1); - frame |= ep->branch; - if (frame_before(frame, isp1362_hcd->fmindex)) - frame += ep->interval; - urb->start_frame = frame; - - DBG(1, "%s: Adding ep %p to isoc schedule\n", __func__, ep); - list_add_tail(&ep->schedule, &isp1362_hcd->isoc); - } else { - DBG(1, "%s: Adding ep %p to periodic schedule\n", __func__, ep); - list_add_tail(&ep->schedule, &isp1362_hcd->periodic); - } - } else - DBG(1, "%s: ep %p already scheduled\n", __func__, ep); - - DBG(2, "%s: load %d bandwidth %d -> %d\n", __func__, - ep->load / ep->interval, isp1362_hcd->load[ep->branch], - isp1362_hcd->load[ep->branch] + ep->load); - isp1362_hcd->load[ep->branch] += ep->load; - } - - urb->hcpriv = hep; - ALIGNSTAT(isp1362_hcd, urb->transfer_buffer); - - switch (type) { - case PIPE_CONTROL: - case PIPE_BULK: - start_atl_transfers(isp1362_hcd); - break; - case PIPE_INTERRUPT: - start_intl_transfers(isp1362_hcd); - break; - case PIPE_ISOCHRONOUS: - start_iso_transfers(isp1362_hcd); - break; - default: - BUG(); - } - fail: - if (retval) - usb_hcd_unlink_urb_from_ep(hcd, urb); - - - fail_not_linked: - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (retval) - DBG(0, "%s: urb %p failed with %d\n", __func__, urb, retval); - return retval; -} - -static int isp1362_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - struct usb_host_endpoint *hep; - unsigned long flags; - struct isp1362_ep *ep; - int retval = 0; - - DBG(3, "%s: urb %p\n", __func__, urb); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - retval = usb_hcd_check_unlink_urb(hcd, urb, status); - if (retval) - goto done; - - hep = urb->hcpriv; - - if (!hep) { - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - return -EIDRM; - } - - ep = hep->hcpriv; - if (ep) { - /* In front of queue? */ - if (ep->hep->urb_list.next == &urb->urb_list) { - if (!list_empty(&ep->active)) { - DBG(1, "%s: urb %p ep %p req %d active PTD[%d] $%04x\n", __func__, - urb, ep, ep->num_req, ep->ptd_index, ep->ptd_offset); - /* disable processing and queue PTD for removal */ - remove_ptd(isp1362_hcd, ep); - urb = NULL; - } - } - if (urb) { - DBG(1, "%s: Finishing ep %p req %d\n", __func__, ep, - ep->num_req); - finish_request(isp1362_hcd, ep, urb, status); - } else - DBG(1, "%s: urb %p active; wait4irq\n", __func__, urb); - } else { - pr_warn("%s: No EP in URB %p\n", __func__, urb); - retval = -EINVAL; - } -done: - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - DBG(3, "%s: exit\n", __func__); - - return retval; -} - -static void isp1362_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) -{ - struct isp1362_ep *ep = hep->hcpriv; - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - - DBG(1, "%s: ep %p\n", __func__, ep); - if (!ep) - return; - spin_lock_irqsave(&isp1362_hcd->lock, flags); - if (!list_empty(&hep->urb_list)) { - if (!list_empty(&ep->active) && list_empty(&ep->remove_list)) { - DBG(1, "%s: Removing ep %p req %d PTD[%d] $%04x\n", __func__, - ep, ep->num_req, ep->ptd_index, ep->ptd_offset); - remove_ptd(isp1362_hcd, ep); - pr_info("%s: Waiting for Interrupt to clean up\n", __func__); - } - } - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - /* Wait for interrupt to clear out active list */ - while (!list_empty(&ep->active)) - msleep(1); - - DBG(1, "%s: Freeing EP %p\n", __func__, ep); - - usb_put_dev(ep->udev); - kfree(ep); - hep->hcpriv = NULL; -} - -static int isp1362_get_frame(struct usb_hcd *hcd) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - u32 fmnum; - unsigned long flags; - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - fmnum = isp1362_read_reg32(isp1362_hcd, HCFMNUM); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - return (int)fmnum; -} - -/*-------------------------------------------------------------------------*/ - -/* Adapted from ohci-hub.c */ -static int isp1362_hub_status_data(struct usb_hcd *hcd, char *buf) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - int ports, i, changed = 0; - unsigned long flags; - - if (!HC_IS_RUNNING(hcd->state)) - return -ESHUTDOWN; - - /* Report no status change now, if we are scheduled to be - called later */ - if (timer_pending(&hcd->rh_timer)) - return 0; - - ports = isp1362_hcd->rhdesca & RH_A_NDP; - BUG_ON(ports > 2); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - /* init status */ - if (isp1362_hcd->rhstatus & (RH_HS_LPSC | RH_HS_OCIC)) - buf[0] = changed = 1; - else - buf[0] = 0; - - for (i = 0; i < ports; i++) { - u32 status = isp1362_hcd->rhport[i]; - - if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | - RH_PS_OCIC | RH_PS_PRSC)) { - changed = 1; - buf[0] |= 1 << (i + 1); - continue; - } - - if (!(status & RH_PS_CCS)) - continue; - } - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - return changed; -} - -static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd, - struct usb_hub_descriptor *desc) -{ - u32 reg = isp1362_hcd->rhdesca; - - DBG(3, "%s: enter\n", __func__); - - desc->bDescriptorType = USB_DT_HUB; - desc->bDescLength = 9; - desc->bHubContrCurrent = 0; - desc->bNbrPorts = reg & 0x3; - /* Power switching, device type, overcurrent. */ - desc->wHubCharacteristics = cpu_to_le16((reg >> 8) & - (HUB_CHAR_LPSM | - HUB_CHAR_COMPOUND | - HUB_CHAR_OCPM)); - DBG(0, "%s: hubcharacteristics = %02x\n", __func__, - desc->wHubCharacteristics); - desc->bPwrOn2PwrGood = (reg >> 24) & 0xff; - /* ports removable, and legacy PortPwrCtrlMask */ - desc->u.hs.DeviceRemovable[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1; - desc->u.hs.DeviceRemovable[1] = ~0; - - DBG(3, "%s: exit\n", __func__); -} - -/* Adapted from ohci-hub.c */ -static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, - u16 wIndex, char *buf, u16 wLength) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - int retval = 0; - unsigned long flags; - unsigned long t1; - int ports = isp1362_hcd->rhdesca & RH_A_NDP; - u32 tmp = 0; - - switch (typeReq) { - case ClearHubFeature: - DBG(0, "ClearHubFeature: "); - switch (wValue) { - case C_HUB_OVER_CURRENT: - DBG(0, "C_HUB_OVER_CURRENT\n"); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_OCIC); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - break; - case C_HUB_LOCAL_POWER: - DBG(0, "C_HUB_LOCAL_POWER\n"); - break; - default: - goto error; - } - break; - case SetHubFeature: - DBG(0, "SetHubFeature: "); - switch (wValue) { - case C_HUB_OVER_CURRENT: - case C_HUB_LOCAL_POWER: - DBG(0, "C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n"); - break; - default: - goto error; - } - break; - case GetHubDescriptor: - DBG(0, "GetHubDescriptor\n"); - isp1362_hub_descriptor(isp1362_hcd, (struct usb_hub_descriptor *)buf); - break; - case GetHubStatus: - DBG(0, "GetHubStatus\n"); - put_unaligned(cpu_to_le32(0), (__le32 *) buf); - break; - case GetPortStatus: -#ifndef VERBOSE - DBG(0, "GetPortStatus\n"); -#endif - if (!wIndex || wIndex > ports) - goto error; - tmp = isp1362_hcd->rhport[--wIndex]; - put_unaligned(cpu_to_le32(tmp), (__le32 *) buf); - break; - case ClearPortFeature: - DBG(0, "ClearPortFeature: "); - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - - switch (wValue) { - case USB_PORT_FEAT_ENABLE: - DBG(0, "USB_PORT_FEAT_ENABLE\n"); - tmp = RH_PS_CCS; - break; - case USB_PORT_FEAT_C_ENABLE: - DBG(0, "USB_PORT_FEAT_C_ENABLE\n"); - tmp = RH_PS_PESC; - break; - case USB_PORT_FEAT_SUSPEND: - DBG(0, "USB_PORT_FEAT_SUSPEND\n"); - tmp = RH_PS_POCI; - break; - case USB_PORT_FEAT_C_SUSPEND: - DBG(0, "USB_PORT_FEAT_C_SUSPEND\n"); - tmp = RH_PS_PSSC; - break; - case USB_PORT_FEAT_POWER: - DBG(0, "USB_PORT_FEAT_POWER\n"); - tmp = RH_PS_LSDA; - - break; - case USB_PORT_FEAT_C_CONNECTION: - DBG(0, "USB_PORT_FEAT_C_CONNECTION\n"); - tmp = RH_PS_CSC; - break; - case USB_PORT_FEAT_C_OVER_CURRENT: - DBG(0, "USB_PORT_FEAT_C_OVER_CURRENT\n"); - tmp = RH_PS_OCIC; - break; - case USB_PORT_FEAT_C_RESET: - DBG(0, "USB_PORT_FEAT_C_RESET\n"); - tmp = RH_PS_PRSC; - break; - default: - goto error; - } - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, tmp); - isp1362_hcd->rhport[wIndex] = - isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - break; - case SetPortFeature: - DBG(0, "SetPortFeature: "); - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - DBG(0, "USB_PORT_FEAT_SUSPEND\n"); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PSS); - isp1362_hcd->rhport[wIndex] = - isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - break; - case USB_PORT_FEAT_POWER: - DBG(0, "USB_PORT_FEAT_POWER\n"); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PPS); - isp1362_hcd->rhport[wIndex] = - isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - break; - case USB_PORT_FEAT_RESET: - DBG(0, "USB_PORT_FEAT_RESET\n"); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - t1 = jiffies + msecs_to_jiffies(USB_RESET_WIDTH); - while (time_before(jiffies, t1)) { - /* spin until any current reset finishes */ - for (;;) { - tmp = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); - if (!(tmp & RH_PS_PRS)) - break; - udelay(500); - } - if (!(tmp & RH_PS_CCS)) - break; - /* Reset lasts 10ms (claims datasheet) */ - isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, (RH_PS_PRS)); - - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - msleep(10); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - } - - isp1362_hcd->rhport[wIndex] = isp1362_read_reg32(isp1362_hcd, - HCRHPORT1 + wIndex); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - break; - default: - goto error; - } - break; - - default: - error: - /* "protocol stall" on error */ - DBG(0, "PROTOCOL STALL\n"); - retval = -EPIPE; - } - - return retval; -} - -#ifdef CONFIG_PM -static int isp1362_bus_suspend(struct usb_hcd *hcd) -{ - int status = 0; - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - - if (time_before(jiffies, isp1362_hcd->next_statechange)) - msleep(5); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); - switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) { - case OHCI_USB_RESUME: - DBG(0, "%s: resume/suspend?\n", __func__); - isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS; - isp1362_hcd->hc_control |= OHCI_USB_RESET; - isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); - fallthrough; - case OHCI_USB_RESET: - status = -EBUSY; - pr_warn("%s: needs reinit!\n", __func__); - goto done; - case OHCI_USB_SUSPEND: - pr_warn("%s: already suspended?\n", __func__); - goto done; - } - DBG(0, "%s: suspend root hub\n", __func__); - - /* First stop any processing */ - hcd->state = HC_STATE_QUIESCING; - if (!list_empty(&isp1362_hcd->atl_queue.active) || - !list_empty(&isp1362_hcd->intl_queue.active) || - !list_empty(&isp1362_hcd->istl_queue[0] .active) || - !list_empty(&isp1362_hcd->istl_queue[1] .active)) { - int limit; - - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0); - isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0); - isp1362_write_reg16(isp1362_hcd, HCBUFSTAT, 0); - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); - isp1362_write_reg32(isp1362_hcd, HCINTSTAT, OHCI_INTR_SF); - - DBG(0, "%s: stopping schedules ...\n", __func__); - limit = 2000; - while (limit > 0) { - udelay(250); - limit -= 250; - if (isp1362_read_reg32(isp1362_hcd, HCINTSTAT) & OHCI_INTR_SF) - break; - } - mdelay(7); - if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ATL) { - u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE); - finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue); - } - if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_INTL) { - u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE); - finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue); - } - if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL0) - finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[0]); - if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL1) - finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[1]); - } - DBG(0, "%s: HCINTSTAT: %08x\n", __func__, - isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); - isp1362_write_reg32(isp1362_hcd, HCINTSTAT, - isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); - - /* Suspend hub */ - isp1362_hcd->hc_control = OHCI_USB_SUSPEND; - isp1362_show_reg(isp1362_hcd, HCCONTROL); - isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); - isp1362_show_reg(isp1362_hcd, HCCONTROL); - -#if 1 - isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); - if ((isp1362_hcd->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_SUSPEND) { - pr_err("%s: controller won't suspend %08x\n", __func__, - isp1362_hcd->hc_control); - status = -EBUSY; - } else -#endif - { - /* no resumes until devices finish suspending */ - isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(5); - } -done: - if (status == 0) { - hcd->state = HC_STATE_SUSPENDED; - DBG(0, "%s: HCD suspended: %08x\n", __func__, - isp1362_read_reg32(isp1362_hcd, HCCONTROL)); - } - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - return status; -} - -static int isp1362_bus_resume(struct usb_hcd *hcd) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - u32 port; - unsigned long flags; - int status = -EINPROGRESS; - - if (time_before(jiffies, isp1362_hcd->next_statechange)) - msleep(5); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); - pr_info("%s: HCCONTROL: %08x\n", __func__, isp1362_hcd->hc_control); - if (hcd->state == HC_STATE_RESUMING) { - pr_warn("%s: duplicate resume\n", __func__); - status = 0; - } else - switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) { - case OHCI_USB_SUSPEND: - DBG(0, "%s: resume root hub\n", __func__); - isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS; - isp1362_hcd->hc_control |= OHCI_USB_RESUME; - isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); - break; - case OHCI_USB_RESUME: - /* HCFS changes sometime after INTR_RD */ - DBG(0, "%s: remote wakeup\n", __func__); - break; - case OHCI_USB_OPER: - DBG(0, "%s: odd resume\n", __func__); - status = 0; - hcd->self.root_hub->dev.power.power_state = PMSG_ON; - break; - default: /* RESET, we lost power */ - DBG(0, "%s: root hub hardware reset\n", __func__); - status = -EBUSY; - } - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (status == -EBUSY) { - DBG(0, "%s: Restarting HC\n", __func__); - isp1362_hc_stop(hcd); - return isp1362_hc_start(hcd); - } - if (status != -EINPROGRESS) - return status; - spin_lock_irqsave(&isp1362_hcd->lock, flags); - port = isp1362_read_reg32(isp1362_hcd, HCRHDESCA) & RH_A_NDP; - while (port--) { - u32 stat = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + port); - - /* force global, not selective, resume */ - if (!(stat & RH_PS_PSS)) { - DBG(0, "%s: Not Resuming RH port %d\n", __func__, port); - continue; - } - DBG(0, "%s: Resuming RH port %d\n", __func__, port); - isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + port, RH_PS_POCI); - } - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - /* Some controllers (lucent) need extra-long delays */ - hcd->state = HC_STATE_RESUMING; - mdelay(20 /* usb 11.5.1.10 */ + 15); - - isp1362_hcd->hc_control = OHCI_USB_OPER; - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_show_reg(isp1362_hcd, HCCONTROL); - isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - /* TRSMRCY */ - msleep(10); - - /* keep it alive for ~5x suspend + resume costs */ - isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(250); - - hcd->self.root_hub->dev.power.power_state = PMSG_ON; - hcd->state = HC_STATE_RUNNING; - return 0; -} -#else -#define isp1362_bus_suspend NULL -#define isp1362_bus_resume NULL -#endif - -/*-------------------------------------------------------------------------*/ - -static void dump_irq(struct seq_file *s, char *label, u16 mask) -{ - seq_printf(s, "%-15s %04x%s%s%s%s%s%s\n", label, mask, - mask & HCuPINT_CLKRDY ? " clkrdy" : "", - mask & HCuPINT_SUSP ? " susp" : "", - mask & HCuPINT_OPR ? " opr" : "", - mask & HCuPINT_EOT ? " eot" : "", - mask & HCuPINT_ATL ? " atl" : "", - mask & HCuPINT_SOF ? " sof" : ""); -} - -static void dump_int(struct seq_file *s, char *label, u32 mask) -{ - seq_printf(s, "%-15s %08x%s%s%s%s%s%s%s\n", label, mask, - mask & OHCI_INTR_MIE ? " MIE" : "", - mask & OHCI_INTR_RHSC ? " rhsc" : "", - mask & OHCI_INTR_FNO ? " fno" : "", - mask & OHCI_INTR_UE ? " ue" : "", - mask & OHCI_INTR_RD ? " rd" : "", - mask & OHCI_INTR_SF ? " sof" : "", - mask & OHCI_INTR_SO ? " so" : ""); -} - -static void dump_ctrl(struct seq_file *s, char *label, u32 mask) -{ - seq_printf(s, "%-15s %08x%s%s%s\n", label, mask, - mask & OHCI_CTRL_RWC ? " rwc" : "", - mask & OHCI_CTRL_RWE ? " rwe" : "", - ({ - char *hcfs; - switch (mask & OHCI_CTRL_HCFS) { - case OHCI_USB_OPER: - hcfs = " oper"; - break; - case OHCI_USB_RESET: - hcfs = " reset"; - break; - case OHCI_USB_RESUME: - hcfs = " resume"; - break; - case OHCI_USB_SUSPEND: - hcfs = " suspend"; - break; - default: - hcfs = " ?"; - } - hcfs; - })); -} - -static void dump_regs(struct seq_file *s, struct isp1362_hcd *isp1362_hcd) -{ - seq_printf(s, "HCREVISION [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCREVISION), - isp1362_read_reg32(isp1362_hcd, HCREVISION)); - seq_printf(s, "HCCONTROL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCONTROL), - isp1362_read_reg32(isp1362_hcd, HCCONTROL)); - seq_printf(s, "HCCMDSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCMDSTAT), - isp1362_read_reg32(isp1362_hcd, HCCMDSTAT)); - seq_printf(s, "HCINTSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTSTAT), - isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); - seq_printf(s, "HCINTENB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTENB), - isp1362_read_reg32(isp1362_hcd, HCINTENB)); - seq_printf(s, "HCFMINTVL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMINTVL), - isp1362_read_reg32(isp1362_hcd, HCFMINTVL)); - seq_printf(s, "HCFMREM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMREM), - isp1362_read_reg32(isp1362_hcd, HCFMREM)); - seq_printf(s, "HCFMNUM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMNUM), - isp1362_read_reg32(isp1362_hcd, HCFMNUM)); - seq_printf(s, "HCLSTHRESH [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCLSTHRESH), - isp1362_read_reg32(isp1362_hcd, HCLSTHRESH)); - seq_printf(s, "HCRHDESCA [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCA), - isp1362_read_reg32(isp1362_hcd, HCRHDESCA)); - seq_printf(s, "HCRHDESCB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCB), - isp1362_read_reg32(isp1362_hcd, HCRHDESCB)); - seq_printf(s, "HCRHSTATUS [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHSTATUS), - isp1362_read_reg32(isp1362_hcd, HCRHSTATUS)); - seq_printf(s, "HCRHPORT1 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT1), - isp1362_read_reg32(isp1362_hcd, HCRHPORT1)); - seq_printf(s, "HCRHPORT2 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT2), - isp1362_read_reg32(isp1362_hcd, HCRHPORT2)); - seq_printf(s, "\n"); - seq_printf(s, "HCHWCFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCHWCFG), - isp1362_read_reg16(isp1362_hcd, HCHWCFG)); - seq_printf(s, "HCDMACFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCDMACFG), - isp1362_read_reg16(isp1362_hcd, HCDMACFG)); - seq_printf(s, "HCXFERCTR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCXFERCTR), - isp1362_read_reg16(isp1362_hcd, HCXFERCTR)); - seq_printf(s, "HCuPINT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINT), - isp1362_read_reg16(isp1362_hcd, HCuPINT)); - seq_printf(s, "HCuPINTENB [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINTENB), - isp1362_read_reg16(isp1362_hcd, HCuPINTENB)); - seq_printf(s, "HCCHIPID [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCCHIPID), - isp1362_read_reg16(isp1362_hcd, HCCHIPID)); - seq_printf(s, "HCSCRATCH [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCSCRATCH), - isp1362_read_reg16(isp1362_hcd, HCSCRATCH)); - seq_printf(s, "HCBUFSTAT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCBUFSTAT), - isp1362_read_reg16(isp1362_hcd, HCBUFSTAT)); - seq_printf(s, "HCDIRADDR [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCDIRADDR), - isp1362_read_reg32(isp1362_hcd, HCDIRADDR)); -#if 0 - seq_printf(s, "HCDIRDATA [%02x] %04x\n", ISP1362_REG_NO(HCDIRDATA), - isp1362_read_reg16(isp1362_hcd, HCDIRDATA)); -#endif - seq_printf(s, "HCISTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLBUFSZ), - isp1362_read_reg16(isp1362_hcd, HCISTLBUFSZ)); - seq_printf(s, "HCISTLRATE [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLRATE), - isp1362_read_reg16(isp1362_hcd, HCISTLRATE)); - seq_printf(s, "\n"); - seq_printf(s, "HCINTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBUFSZ), - isp1362_read_reg16(isp1362_hcd, HCINTLBUFSZ)); - seq_printf(s, "HCINTLBLKSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBLKSZ), - isp1362_read_reg16(isp1362_hcd, HCINTLBLKSZ)); - seq_printf(s, "HCINTLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLDONE), - isp1362_read_reg32(isp1362_hcd, HCINTLDONE)); - seq_printf(s, "HCINTLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLSKIP), - isp1362_read_reg32(isp1362_hcd, HCINTLSKIP)); - seq_printf(s, "HCINTLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLLAST), - isp1362_read_reg32(isp1362_hcd, HCINTLLAST)); - seq_printf(s, "HCINTLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLCURR), - isp1362_read_reg16(isp1362_hcd, HCINTLCURR)); - seq_printf(s, "\n"); - seq_printf(s, "HCATLBUFSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBUFSZ), - isp1362_read_reg16(isp1362_hcd, HCATLBUFSZ)); - seq_printf(s, "HCATLBLKSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBLKSZ), - isp1362_read_reg16(isp1362_hcd, HCATLBLKSZ)); -#if 0 - seq_printf(s, "HCATLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDONE), - isp1362_read_reg32(isp1362_hcd, HCATLDONE)); -#endif - seq_printf(s, "HCATLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLSKIP), - isp1362_read_reg32(isp1362_hcd, HCATLSKIP)); - seq_printf(s, "HCATLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLLAST), - isp1362_read_reg32(isp1362_hcd, HCATLLAST)); - seq_printf(s, "HCATLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLCURR), - isp1362_read_reg16(isp1362_hcd, HCATLCURR)); - seq_printf(s, "\n"); - seq_printf(s, "HCATLDTC [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTC), - isp1362_read_reg16(isp1362_hcd, HCATLDTC)); - seq_printf(s, "HCATLDTCTO [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTCTO), - isp1362_read_reg16(isp1362_hcd, HCATLDTCTO)); -} - -static int isp1362_show(struct seq_file *s, void *unused) -{ - struct isp1362_hcd *isp1362_hcd = s->private; - struct isp1362_ep *ep; - int i; - - seq_printf(s, "%s\n%s version %s\n", - isp1362_hcd_to_hcd(isp1362_hcd)->product_desc, hcd_name, DRIVER_VERSION); - - /* collect statistics to help estimate potential win for - * DMA engines that care about alignment (PXA) - */ - seq_printf(s, "alignment: 16b/%ld 8b/%ld 4b/%ld 2b/%ld 1b/%ld\n", - isp1362_hcd->stat16, isp1362_hcd->stat8, isp1362_hcd->stat4, - isp1362_hcd->stat2, isp1362_hcd->stat1); - seq_printf(s, "max # ptds in ATL fifo: %d\n", isp1362_hcd->atl_queue.stat_maxptds); - seq_printf(s, "max # ptds in INTL fifo: %d\n", isp1362_hcd->intl_queue.stat_maxptds); - seq_printf(s, "max # ptds in ISTL fifo: %d\n", - max(isp1362_hcd->istl_queue[0] .stat_maxptds, - isp1362_hcd->istl_queue[1] .stat_maxptds)); - - /* FIXME: don't show the following in suspended state */ - spin_lock_irq(&isp1362_hcd->lock); - - dump_irq(s, "hc_irq_enable", isp1362_read_reg16(isp1362_hcd, HCuPINTENB)); - dump_irq(s, "hc_irq_status", isp1362_read_reg16(isp1362_hcd, HCuPINT)); - dump_int(s, "ohci_int_enable", isp1362_read_reg32(isp1362_hcd, HCINTENB)); - dump_int(s, "ohci_int_status", isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); - dump_ctrl(s, "ohci_control", isp1362_read_reg32(isp1362_hcd, HCCONTROL)); - - for (i = 0; i < NUM_ISP1362_IRQS; i++) - if (isp1362_hcd->irq_stat[i]) - seq_printf(s, "%-15s: %d\n", - ISP1362_INT_NAME(i), isp1362_hcd->irq_stat[i]); - - dump_regs(s, isp1362_hcd); - list_for_each_entry(ep, &isp1362_hcd->async, schedule) { - struct urb *urb; - - seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep, ep->epnum, - ({ - char *s; - switch (ep->nextpid) { - case USB_PID_IN: - s = "in"; - break; - case USB_PID_OUT: - s = "out"; - break; - case USB_PID_SETUP: - s = "setup"; - break; - case USB_PID_ACK: - s = "status"; - break; - default: - s = "?"; - break; - } - s;}), ep->maxpacket) ; - list_for_each_entry(urb, &ep->hep->urb_list, urb_list) { - seq_printf(s, " urb%p, %d/%d\n", urb, - urb->actual_length, - urb->transfer_buffer_length); - } - } - if (!list_empty(&isp1362_hcd->async)) - seq_printf(s, "\n"); - dump_ptd_queue(&isp1362_hcd->atl_queue); - - seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); - - list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) { - seq_printf(s, "branch:%2d load:%3d PTD[%d] $%04x:\n", ep->branch, - isp1362_hcd->load[ep->branch], ep->ptd_index, ep->ptd_offset); - - seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", - ep->interval, ep, - (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ", - ep->udev->devnum, ep->epnum, - (ep->epnum == 0) ? "" : - ((ep->nextpid == USB_PID_IN) ? - "in" : "out"), ep->maxpacket); - } - dump_ptd_queue(&isp1362_hcd->intl_queue); - - seq_printf(s, "ISO:\n"); - - list_for_each_entry(ep, &isp1362_hcd->isoc, schedule) { - seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", - ep->interval, ep, - (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ", - ep->udev->devnum, ep->epnum, - (ep->epnum == 0) ? "" : - ((ep->nextpid == USB_PID_IN) ? - "in" : "out"), ep->maxpacket); - } - - spin_unlock_irq(&isp1362_hcd->lock); - seq_printf(s, "\n"); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(isp1362); - -/* expect just one isp1362_hcd per system */ -static void create_debug_file(struct isp1362_hcd *isp1362_hcd) -{ - debugfs_create_file("isp1362", S_IRUGO, usb_debug_root, isp1362_hcd, - &isp1362_fops); -} - -static void remove_debug_file(struct isp1362_hcd *isp1362_hcd) -{ - debugfs_lookup_and_remove("isp1362", usb_debug_root); -} - -/*-------------------------------------------------------------------------*/ - -static void __isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd) -{ - int tmp = 20; - - isp1362_write_reg16(isp1362_hcd, HCSWRES, HCSWRES_MAGIC); - isp1362_write_reg32(isp1362_hcd, HCCMDSTAT, OHCI_HCR); - while (--tmp) { - mdelay(1); - if (!(isp1362_read_reg32(isp1362_hcd, HCCMDSTAT) & OHCI_HCR)) - break; - } - if (!tmp) - pr_err("Software reset timeout\n"); -} - -static void isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd) -{ - unsigned long flags; - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - __isp1362_sw_reset(isp1362_hcd); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); -} - -static int isp1362_mem_config(struct usb_hcd *hcd) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - u32 total; - u16 istl_size = ISP1362_ISTL_BUFSIZE; - u16 intl_blksize = ISP1362_INTL_BLKSIZE + PTD_HEADER_SIZE; - u16 intl_size = ISP1362_INTL_BUFFERS * intl_blksize; - u16 atl_blksize = ISP1362_ATL_BLKSIZE + PTD_HEADER_SIZE; - u16 atl_buffers = (ISP1362_BUF_SIZE - (istl_size + intl_size)) / atl_blksize; - u16 atl_size; - int i; - - WARN_ON(istl_size & 3); - WARN_ON(atl_blksize & 3); - WARN_ON(intl_blksize & 3); - WARN_ON(atl_blksize < PTD_HEADER_SIZE); - WARN_ON(intl_blksize < PTD_HEADER_SIZE); - - BUG_ON((unsigned)ISP1362_INTL_BUFFERS > 32); - if (atl_buffers > 32) - atl_buffers = 32; - atl_size = atl_buffers * atl_blksize; - total = atl_size + intl_size + istl_size; - dev_info(hcd->self.controller, "ISP1362 Memory usage:\n"); - dev_info(hcd->self.controller, " ISTL: 2 * %4d: %4d @ $%04x:$%04x\n", - istl_size / 2, istl_size, 0, istl_size / 2); - dev_info(hcd->self.controller, " INTL: %4d * (%3zu+8): %4d @ $%04x\n", - ISP1362_INTL_BUFFERS, intl_blksize - PTD_HEADER_SIZE, - intl_size, istl_size); - dev_info(hcd->self.controller, " ATL : %4d * (%3zu+8): %4d @ $%04x\n", - atl_buffers, atl_blksize - PTD_HEADER_SIZE, - atl_size, istl_size + intl_size); - dev_info(hcd->self.controller, " USED/FREE: %4d %4d\n", total, - ISP1362_BUF_SIZE - total); - - if (total > ISP1362_BUF_SIZE) { - dev_err(hcd->self.controller, "%s: Memory requested: %d, available %d\n", - __func__, total, ISP1362_BUF_SIZE); - return -ENOMEM; - } - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - for (i = 0; i < 2; i++) { - isp1362_hcd->istl_queue[i].buf_start = i * istl_size / 2, - isp1362_hcd->istl_queue[i].buf_size = istl_size / 2; - isp1362_hcd->istl_queue[i].blk_size = 4; - INIT_LIST_HEAD(&isp1362_hcd->istl_queue[i].active); - snprintf(isp1362_hcd->istl_queue[i].name, - sizeof(isp1362_hcd->istl_queue[i].name), "ISTL%d", i); - DBG(3, "%s: %5s buf $%04x %d\n", __func__, - isp1362_hcd->istl_queue[i].name, - isp1362_hcd->istl_queue[i].buf_start, - isp1362_hcd->istl_queue[i].buf_size); - } - isp1362_write_reg16(isp1362_hcd, HCISTLBUFSZ, istl_size / 2); - - isp1362_hcd->intl_queue.buf_start = istl_size; - isp1362_hcd->intl_queue.buf_size = intl_size; - isp1362_hcd->intl_queue.buf_count = ISP1362_INTL_BUFFERS; - isp1362_hcd->intl_queue.blk_size = intl_blksize; - isp1362_hcd->intl_queue.buf_avail = isp1362_hcd->intl_queue.buf_count; - isp1362_hcd->intl_queue.skip_map = ~0; - INIT_LIST_HEAD(&isp1362_hcd->intl_queue.active); - - isp1362_write_reg16(isp1362_hcd, HCINTLBUFSZ, - isp1362_hcd->intl_queue.buf_size); - isp1362_write_reg16(isp1362_hcd, HCINTLBLKSZ, - isp1362_hcd->intl_queue.blk_size - PTD_HEADER_SIZE); - isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0); - isp1362_write_reg32(isp1362_hcd, HCINTLLAST, - 1 << (ISP1362_INTL_BUFFERS - 1)); - - isp1362_hcd->atl_queue.buf_start = istl_size + intl_size; - isp1362_hcd->atl_queue.buf_size = atl_size; - isp1362_hcd->atl_queue.buf_count = atl_buffers; - isp1362_hcd->atl_queue.blk_size = atl_blksize; - isp1362_hcd->atl_queue.buf_avail = isp1362_hcd->atl_queue.buf_count; - isp1362_hcd->atl_queue.skip_map = ~0; - INIT_LIST_HEAD(&isp1362_hcd->atl_queue.active); - - isp1362_write_reg16(isp1362_hcd, HCATLBUFSZ, - isp1362_hcd->atl_queue.buf_size); - isp1362_write_reg16(isp1362_hcd, HCATLBLKSZ, - isp1362_hcd->atl_queue.blk_size - PTD_HEADER_SIZE); - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0); - isp1362_write_reg32(isp1362_hcd, HCATLLAST, - 1 << (atl_buffers - 1)); - - snprintf(isp1362_hcd->atl_queue.name, - sizeof(isp1362_hcd->atl_queue.name), "ATL"); - snprintf(isp1362_hcd->intl_queue.name, - sizeof(isp1362_hcd->intl_queue.name), "INTL"); - DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__, - isp1362_hcd->intl_queue.name, - isp1362_hcd->intl_queue.buf_start, - ISP1362_INTL_BUFFERS, isp1362_hcd->intl_queue.blk_size, - isp1362_hcd->intl_queue.buf_size); - DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__, - isp1362_hcd->atl_queue.name, - isp1362_hcd->atl_queue.buf_start, - atl_buffers, isp1362_hcd->atl_queue.blk_size, - isp1362_hcd->atl_queue.buf_size); - - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - return 0; -} - -static int isp1362_hc_reset(struct usb_hcd *hcd) -{ - int ret = 0; - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long t; - unsigned long timeout = 100; - unsigned long flags; - int clkrdy = 0; - - pr_debug("%s:\n", __func__); - - if (isp1362_hcd->board && isp1362_hcd->board->reset) { - isp1362_hcd->board->reset(hcd->self.controller, 1); - msleep(20); - if (isp1362_hcd->board->clock) - isp1362_hcd->board->clock(hcd->self.controller, 1); - isp1362_hcd->board->reset(hcd->self.controller, 0); - } else - isp1362_sw_reset(isp1362_hcd); - - /* chip has been reset. First we need to see a clock */ - t = jiffies + msecs_to_jiffies(timeout); - while (!clkrdy && time_before_eq(jiffies, t)) { - spin_lock_irqsave(&isp1362_hcd->lock, flags); - clkrdy = isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_CLKRDY; - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (!clkrdy) - msleep(4); - } - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_CLKRDY); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (!clkrdy) { - pr_err("Clock not ready after %lums\n", timeout); - ret = -ENODEV; - } - return ret; -} - -static void isp1362_hc_stop(struct usb_hcd *hcd) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - u32 tmp; - - pr_debug("%s:\n", __func__); - - del_timer_sync(&hcd->rh_timer); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); - - /* Switch off power for all ports */ - tmp = isp1362_read_reg32(isp1362_hcd, HCRHDESCA); - tmp &= ~(RH_A_NPS | RH_A_PSM); - isp1362_write_reg32(isp1362_hcd, HCRHDESCA, tmp); - isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS); - - /* Reset the chip */ - if (isp1362_hcd->board && isp1362_hcd->board->reset) - isp1362_hcd->board->reset(hcd->self.controller, 1); - else - __isp1362_sw_reset(isp1362_hcd); - - if (isp1362_hcd->board && isp1362_hcd->board->clock) - isp1362_hcd->board->clock(hcd->self.controller, 0); - - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); -} - -#ifdef CHIP_BUFFER_TEST -static int isp1362_chip_test(struct isp1362_hcd *isp1362_hcd) -{ - int ret = 0; - u16 *ref; - unsigned long flags; - - ref = kmalloc(2 * ISP1362_BUF_SIZE, GFP_KERNEL); - if (ref) { - int offset; - u16 *tst = &ref[ISP1362_BUF_SIZE / 2]; - - for (offset = 0; offset < ISP1362_BUF_SIZE / 2; offset++) { - ref[offset] = ~offset; - tst[offset] = offset; - } - - for (offset = 0; offset < 4; offset++) { - int j; - - for (j = 0; j < 8; j++) { - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_buffer(isp1362_hcd, (u8 *)ref + offset, 0, j); - isp1362_read_buffer(isp1362_hcd, (u8 *)tst + offset, 0, j); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - if (memcmp(ref, tst, j)) { - ret = -ENODEV; - pr_err("%s: memory check with %d byte offset %d failed\n", - __func__, j, offset); - dump_data((u8 *)ref + offset, j); - dump_data((u8 *)tst + offset, j); - } - } - } - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_buffer(isp1362_hcd, ref, 0, ISP1362_BUF_SIZE); - isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - if (memcmp(ref, tst, ISP1362_BUF_SIZE)) { - ret = -ENODEV; - pr_err("%s: memory check failed\n", __func__); - dump_data((u8 *)tst, ISP1362_BUF_SIZE / 2); - } - - for (offset = 0; offset < 256; offset++) { - int test_size = 0; - - yield(); - - memset(tst, 0, ISP1362_BUF_SIZE); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); - isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (memcmp(tst, tst + (ISP1362_BUF_SIZE / (2 * sizeof(*tst))), - ISP1362_BUF_SIZE / 2)) { - pr_err("%s: Failed to clear buffer\n", __func__); - dump_data((u8 *)tst, ISP1362_BUF_SIZE); - break; - } - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_buffer(isp1362_hcd, ref, offset * 2, PTD_HEADER_SIZE); - isp1362_write_buffer(isp1362_hcd, ref + PTD_HEADER_SIZE / sizeof(*ref), - offset * 2 + PTD_HEADER_SIZE, test_size); - isp1362_read_buffer(isp1362_hcd, tst, offset * 2, - PTD_HEADER_SIZE + test_size); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) { - dump_data(((u8 *)ref) + offset, PTD_HEADER_SIZE + test_size); - dump_data((u8 *)tst, PTD_HEADER_SIZE + test_size); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_read_buffer(isp1362_hcd, tst, offset * 2, - PTD_HEADER_SIZE + test_size); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) { - ret = -ENODEV; - pr_err("%s: memory check with offset %02x failed\n", - __func__, offset); - break; - } - pr_warn("%s: memory check with offset %02x ok after second read\n", - __func__, offset); - } - } - kfree(ref); - } - return ret; -} -#endif - -static int isp1362_hc_start(struct usb_hcd *hcd) -{ - int ret; - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - struct isp1362_platform_data *board = isp1362_hcd->board; - u16 hwcfg; - u16 chipid; - unsigned long flags; - - pr_debug("%s:\n", __func__); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - if ((chipid & HCCHIPID_MASK) != HCCHIPID_MAGIC) { - pr_err("%s: Invalid chip ID %04x\n", __func__, chipid); - return -ENODEV; - } - -#ifdef CHIP_BUFFER_TEST - ret = isp1362_chip_test(isp1362_hcd); - if (ret) - return -ENODEV; -#endif - spin_lock_irqsave(&isp1362_hcd->lock, flags); - /* clear interrupt status and disable all interrupt sources */ - isp1362_write_reg16(isp1362_hcd, HCuPINT, 0xff); - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); - - /* HW conf */ - hwcfg = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1); - if (board->sel15Kres) - hwcfg |= HCHWCFG_PULLDOWN_DS2 | - ((MAX_ROOT_PORTS > 1) ? HCHWCFG_PULLDOWN_DS1 : 0); - if (board->clknotstop) - hwcfg |= HCHWCFG_CLKNOTSTOP; - if (board->oc_enable) - hwcfg |= HCHWCFG_ANALOG_OC; - if (board->int_act_high) - hwcfg |= HCHWCFG_INT_POL; - if (board->int_edge_triggered) - hwcfg |= HCHWCFG_INT_TRIGGER; - if (board->dreq_act_high) - hwcfg |= HCHWCFG_DREQ_POL; - if (board->dack_act_high) - hwcfg |= HCHWCFG_DACK_POL; - isp1362_write_reg16(isp1362_hcd, HCHWCFG, hwcfg); - isp1362_show_reg(isp1362_hcd, HCHWCFG); - isp1362_write_reg16(isp1362_hcd, HCDMACFG, 0); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - ret = isp1362_mem_config(hcd); - if (ret) - return ret; - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - /* Root hub conf */ - isp1362_hcd->rhdesca = 0; - if (board->no_power_switching) - isp1362_hcd->rhdesca |= RH_A_NPS; - if (board->power_switching_mode) - isp1362_hcd->rhdesca |= RH_A_PSM; - if (board->potpg) - isp1362_hcd->rhdesca |= (board->potpg << 24) & RH_A_POTPGT; - else - isp1362_hcd->rhdesca |= (25 << 24) & RH_A_POTPGT; - - isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca & ~RH_A_OCPM); - isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca | RH_A_OCPM); - isp1362_hcd->rhdesca = isp1362_read_reg32(isp1362_hcd, HCRHDESCA); - - isp1362_hcd->rhdescb = RH_B_PPCM; - isp1362_write_reg32(isp1362_hcd, HCRHDESCB, isp1362_hcd->rhdescb); - isp1362_hcd->rhdescb = isp1362_read_reg32(isp1362_hcd, HCRHDESCB); - - isp1362_read_reg32(isp1362_hcd, HCFMINTVL); - isp1362_write_reg32(isp1362_hcd, HCFMINTVL, (FSMP(FI) << 16) | FI); - isp1362_write_reg32(isp1362_hcd, HCLSTHRESH, LSTHRESH); - - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - isp1362_hcd->hc_control = OHCI_USB_OPER; - hcd->state = HC_STATE_RUNNING; - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - /* Set up interrupts */ - isp1362_hcd->intenb = OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE; - isp1362_hcd->intenb |= OHCI_INTR_RD; - isp1362_hcd->irqenb = HCuPINT_OPR | HCuPINT_SUSP; - isp1362_write_reg32(isp1362_hcd, HCINTENB, isp1362_hcd->intenb); - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); - - /* Go operational */ - isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); - /* enable global power */ - isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC | RH_HS_DRWE); - - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static const struct hc_driver isp1362_hc_driver = { - .description = hcd_name, - .product_desc = "ISP1362 Host Controller", - .hcd_priv_size = sizeof(struct isp1362_hcd), - - .irq = isp1362_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - .reset = isp1362_hc_reset, - .start = isp1362_hc_start, - .stop = isp1362_hc_stop, - - .urb_enqueue = isp1362_urb_enqueue, - .urb_dequeue = isp1362_urb_dequeue, - .endpoint_disable = isp1362_endpoint_disable, - - .get_frame_number = isp1362_get_frame, - - .hub_status_data = isp1362_hub_status_data, - .hub_control = isp1362_hub_control, - .bus_suspend = isp1362_bus_suspend, - .bus_resume = isp1362_bus_resume, -}; - -/*-------------------------------------------------------------------------*/ - -static void isp1362_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - - remove_debug_file(isp1362_hcd); - DBG(0, "%s: Removing HCD\n", __func__); - usb_remove_hcd(hcd); - DBG(0, "%s: put_hcd\n", __func__); - usb_put_hcd(hcd); - DBG(0, "%s: Done\n", __func__); -} - -static int isp1362_probe(struct platform_device *pdev) -{ - struct usb_hcd *hcd; - struct isp1362_hcd *isp1362_hcd; - struct resource *data, *irq_res; - void __iomem *addr_reg; - void __iomem *data_reg; - int irq; - int retval = 0; - unsigned int irq_flags = 0; - - if (usb_disabled()) - return -ENODEV; - - /* basic sanity checks first. board-specific init logic should - * have initialized this the three resources and probably board - * specific platform_data. we don't probe for IRQs, and do only - * minimal sanity checking. - */ - if (pdev->num_resources < 3) - return -ENODEV; - - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq_res) - return -ENODEV; - - irq = irq_res->start; - - addr_reg = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(addr_reg)) - return PTR_ERR(addr_reg); - - data_reg = devm_platform_get_and_ioremap_resource(pdev, 0, &data); - if (IS_ERR(data_reg)) - return PTR_ERR(data_reg); - - /* allocate and initialize hcd */ - hcd = usb_create_hcd(&isp1362_hc_driver, &pdev->dev, dev_name(&pdev->dev)); - if (!hcd) - return -ENOMEM; - - hcd->rsrc_start = data->start; - isp1362_hcd = hcd_to_isp1362_hcd(hcd); - isp1362_hcd->data_reg = data_reg; - isp1362_hcd->addr_reg = addr_reg; - - isp1362_hcd->next_statechange = jiffies; - spin_lock_init(&isp1362_hcd->lock); - INIT_LIST_HEAD(&isp1362_hcd->async); - INIT_LIST_HEAD(&isp1362_hcd->periodic); - INIT_LIST_HEAD(&isp1362_hcd->isoc); - INIT_LIST_HEAD(&isp1362_hcd->remove_list); - isp1362_hcd->board = dev_get_platdata(&pdev->dev); -#if USE_PLATFORM_DELAY - if (!isp1362_hcd->board->delay) { - dev_err(hcd->self.controller, "No platform delay function given\n"); - retval = -ENODEV; - goto err; - } -#endif - - if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE) - irq_flags |= IRQF_TRIGGER_RISING; - if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE) - irq_flags |= IRQF_TRIGGER_FALLING; - if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL) - irq_flags |= IRQF_TRIGGER_HIGH; - if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL) - irq_flags |= IRQF_TRIGGER_LOW; - - retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED); - if (retval != 0) - goto err; - device_wakeup_enable(hcd->self.controller); - - dev_info(&pdev->dev, "%s, irq %d\n", hcd->product_desc, irq); - - create_debug_file(isp1362_hcd); - - return 0; - - err: - usb_put_hcd(hcd); - - return retval; -} - -#ifdef CONFIG_PM -static int isp1362_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - int retval = 0; - - DBG(0, "%s: Suspending device\n", __func__); - - if (state.event == PM_EVENT_FREEZE) { - DBG(0, "%s: Suspending root hub\n", __func__); - retval = isp1362_bus_suspend(hcd); - } else { - DBG(0, "%s: Suspending RH ports\n", __func__); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - } - if (retval == 0) - pdev->dev.power.power_state = state; - return retval; -} - -static int isp1362_resume(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - - DBG(0, "%s: Resuming\n", __func__); - - if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { - DBG(0, "%s: Resume RH ports\n", __func__); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - return 0; - } - - pdev->dev.power.power_state = PMSG_ON; - - return isp1362_bus_resume(isp1362_hcd_to_hcd(isp1362_hcd)); -} -#else -#define isp1362_suspend NULL -#define isp1362_resume NULL -#endif - -static struct platform_driver isp1362_driver = { - .probe = isp1362_probe, - .remove = isp1362_remove, - - .suspend = isp1362_suspend, - .resume = isp1362_resume, - .driver = { - .name = hcd_name, - }, -}; - -module_platform_driver(isp1362_driver); diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h deleted file mode 100644 index 74ca4be24723..000000000000 --- a/drivers/usb/host/isp1362.h +++ /dev/null @@ -1,914 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * ISP1362 HCD (Host Controller Driver) for USB. - * - * COPYRIGHT (C) by L. Wassmann <LW@KARO-electronics.de> - */ - -/* ------------------------------------------------------------------------- */ - -#define MAX_ROOT_PORTS 2 - -#define USE_32BIT 0 - -/* These options are mutually exclusive */ -#define USE_PLATFORM_DELAY 0 -#define USE_NDELAY 0 - -#define DUMMY_DELAY_ACCESS do {} while (0) - -/* ------------------------------------------------------------------------- */ - -#define USB_RESET_WIDTH 50 -#define MAX_XFER_SIZE 1023 - -/* Buffer sizes */ -#define ISP1362_BUF_SIZE 4096 -#define ISP1362_ISTL_BUFSIZE 512 -#define ISP1362_INTL_BLKSIZE 64 -#define ISP1362_INTL_BUFFERS 16 -#define ISP1362_ATL_BLKSIZE 64 - -#define ISP1362_REG_WRITE_OFFSET 0x80 - -#define REG_WIDTH_16 0x000 -#define REG_WIDTH_32 0x100 -#define REG_WIDTH_MASK 0x100 -#define REG_NO_MASK 0x0ff - -#ifdef ISP1362_DEBUG -typedef const unsigned int isp1362_reg_t; - -#define REG_ACCESS_R 0x200 -#define REG_ACCESS_W 0x400 -#define REG_ACCESS_RW 0x600 -#define REG_ACCESS_MASK 0x600 - -#define ISP1362_REG_NO(r) ((r) & REG_NO_MASK) - -#define ISP1362_REG(name, addr, width, rw) \ -static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw)) - -#define REG_ACCESS_TEST(r) BUG_ON(((r) & ISP1362_REG_WRITE_OFFSET) && !((r) & REG_ACCESS_W)) -#define REG_WIDTH_TEST(r, w) BUG_ON(((r) & REG_WIDTH_MASK) != (w)) -#else -typedef const unsigned char isp1362_reg_t; -#define ISP1362_REG_NO(r) (r) - -#define ISP1362_REG(name, addr, width, rw) \ -static isp1362_reg_t __maybe_unused ISP1362_REG_##name = addr - -#define REG_ACCESS_TEST(r) do {} while (0) -#define REG_WIDTH_TEST(r, w) do {} while (0) -#endif - -/* OHCI compatible registers */ -/* - * Note: Some of the ISP1362 'OHCI' registers implement only - * a subset of the bits defined in the OHCI spec. - * - * Bitmasks for the individual bits of these registers are defined in "ohci.h" - */ -ISP1362_REG(HCREVISION, 0x00, REG_WIDTH_32, REG_ACCESS_R); -ISP1362_REG(HCCONTROL, 0x01, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCCMDSTAT, 0x02, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCINTSTAT, 0x03, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCINTENB, 0x04, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCINTDIS, 0x05, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCFMINTVL, 0x0d, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCFMREM, 0x0e, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCFMNUM, 0x0f, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCLSTHRESH, 0x11, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCRHDESCA, 0x12, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCRHDESCB, 0x13, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCRHSTATUS, 0x14, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCRHPORT1, 0x15, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCRHPORT2, 0x16, REG_WIDTH_32, REG_ACCESS_RW); - -/* Philips ISP1362 specific registers */ -ISP1362_REG(HCHWCFG, 0x20, REG_WIDTH_16, REG_ACCESS_RW); -#define HCHWCFG_DISABLE_SUSPEND (1 << 15) -#define HCHWCFG_GLOBAL_PWRDOWN (1 << 14) -#define HCHWCFG_PULLDOWN_DS2 (1 << 13) -#define HCHWCFG_PULLDOWN_DS1 (1 << 12) -#define HCHWCFG_CLKNOTSTOP (1 << 11) -#define HCHWCFG_ANALOG_OC (1 << 10) -#define HCHWCFG_ONEINT (1 << 9) -#define HCHWCFG_DACK_MODE (1 << 8) -#define HCHWCFG_ONEDMA (1 << 7) -#define HCHWCFG_DACK_POL (1 << 6) -#define HCHWCFG_DREQ_POL (1 << 5) -#define HCHWCFG_DBWIDTH_MASK (0x03 << 3) -#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK) -#define HCHWCFG_INT_POL (1 << 2) -#define HCHWCFG_INT_TRIGGER (1 << 1) -#define HCHWCFG_INT_ENABLE (1 << 0) - -ISP1362_REG(HCDMACFG, 0x21, REG_WIDTH_16, REG_ACCESS_RW); -#define HCDMACFG_CTR_ENABLE (1 << 7) -#define HCDMACFG_BURST_LEN_MASK (0x03 << 5) -#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK) -#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0) -#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1) -#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2) -#define HCDMACFG_DMA_ENABLE (1 << 4) -#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1) -#define HCDMACFG_BUF_TYPE(n) (((n) << 1) & HCDMACFG_BUF_TYPE_MASK) -#define HCDMACFG_BUF_ISTL0 HCDMACFG_BUF_TYPE(0) -#define HCDMACFG_BUF_ISTL1 HCDMACFG_BUF_TYPE(1) -#define HCDMACFG_BUF_INTL HCDMACFG_BUF_TYPE(2) -#define HCDMACFG_BUF_ATL HCDMACFG_BUF_TYPE(3) -#define HCDMACFG_BUF_DIRECT HCDMACFG_BUF_TYPE(4) -#define HCDMACFG_DMA_RW_SELECT (1 << 0) - -ISP1362_REG(HCXFERCTR, 0x22, REG_WIDTH_16, REG_ACCESS_RW); - -ISP1362_REG(HCuPINT, 0x24, REG_WIDTH_16, REG_ACCESS_RW); -#define HCuPINT_SOF (1 << 0) -#define HCuPINT_ISTL0 (1 << 1) -#define HCuPINT_ISTL1 (1 << 2) -#define HCuPINT_EOT (1 << 3) -#define HCuPINT_OPR (1 << 4) -#define HCuPINT_SUSP (1 << 5) -#define HCuPINT_CLKRDY (1 << 6) -#define HCuPINT_INTL (1 << 7) -#define HCuPINT_ATL (1 << 8) -#define HCuPINT_OTG (1 << 9) - -ISP1362_REG(HCuPINTENB, 0x25, REG_WIDTH_16, REG_ACCESS_RW); -/* same bit definitions apply as for HCuPINT */ - -ISP1362_REG(HCCHIPID, 0x27, REG_WIDTH_16, REG_ACCESS_R); -#define HCCHIPID_MASK 0xff00 -#define HCCHIPID_MAGIC 0x3600 - -ISP1362_REG(HCSCRATCH, 0x28, REG_WIDTH_16, REG_ACCESS_RW); - -ISP1362_REG(HCSWRES, 0x29, REG_WIDTH_16, REG_ACCESS_W); -#define HCSWRES_MAGIC 0x00f6 - -ISP1362_REG(HCBUFSTAT, 0x2c, REG_WIDTH_16, REG_ACCESS_RW); -#define HCBUFSTAT_ISTL0_FULL (1 << 0) -#define HCBUFSTAT_ISTL1_FULL (1 << 1) -#define HCBUFSTAT_INTL_ACTIVE (1 << 2) -#define HCBUFSTAT_ATL_ACTIVE (1 << 3) -#define HCBUFSTAT_RESET_HWPP (1 << 4) -#define HCBUFSTAT_ISTL0_ACTIVE (1 << 5) -#define HCBUFSTAT_ISTL1_ACTIVE (1 << 6) -#define HCBUFSTAT_ISTL0_DONE (1 << 8) -#define HCBUFSTAT_ISTL1_DONE (1 << 9) -#define HCBUFSTAT_PAIRED_PTDPP (1 << 10) - -ISP1362_REG(HCDIRADDR, 0x32, REG_WIDTH_32, REG_ACCESS_RW); -#define HCDIRADDR_ADDR_MASK 0x0000ffff -#define HCDIRADDR_ADDR(n) (((n) << 0) & HCDIRADDR_ADDR_MASK) -#define HCDIRADDR_COUNT_MASK 0xffff0000 -#define HCDIRADDR_COUNT(n) (((n) << 16) & HCDIRADDR_COUNT_MASK) -ISP1362_REG(HCDIRDATA, 0x45, REG_WIDTH_16, REG_ACCESS_RW); - -ISP1362_REG(HCISTLBUFSZ, 0x30, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCISTL0PORT, 0x40, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCISTL1PORT, 0x42, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCISTLRATE, 0x47, REG_WIDTH_16, REG_ACCESS_RW); - -ISP1362_REG(HCINTLBUFSZ, 0x33, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCINTLPORT, 0x43, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCINTLBLKSZ, 0x53, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCINTLDONE, 0x17, REG_WIDTH_32, REG_ACCESS_R); -ISP1362_REG(HCINTLSKIP, 0x18, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCINTLLAST, 0x19, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCINTLCURR, 0x1a, REG_WIDTH_16, REG_ACCESS_R); - -ISP1362_REG(HCATLBUFSZ, 0x34, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCATLPORT, 0x44, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCATLBLKSZ, 0x54, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCATLDONE, 0x1b, REG_WIDTH_32, REG_ACCESS_R); -ISP1362_REG(HCATLSKIP, 0x1c, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCATLLAST, 0x1d, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCATLCURR, 0x1e, REG_WIDTH_16, REG_ACCESS_R); - -ISP1362_REG(HCATLDTC, 0x51, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCATLDTCTO, 0x52, REG_WIDTH_16, REG_ACCESS_RW); - - -ISP1362_REG(OTGCONTROL, 0x62, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(OTGSTATUS, 0x67, REG_WIDTH_16, REG_ACCESS_R); -ISP1362_REG(OTGINT, 0x68, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(OTGINTENB, 0x69, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(OTGTIMER, 0x6A, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(OTGALTTMR, 0x6C, REG_WIDTH_16, REG_ACCESS_RW); - -/* Philips transfer descriptor, cpu-endian */ -struct ptd { - u16 count; -#define PTD_COUNT_MSK (0x3ff << 0) -#define PTD_TOGGLE_MSK (1 << 10) -#define PTD_ACTIVE_MSK (1 << 11) -#define PTD_CC_MSK (0xf << 12) - u16 mps; -#define PTD_MPS_MSK (0x3ff << 0) -#define PTD_SPD_MSK (1 << 10) -#define PTD_LAST_MSK (1 << 11) -#define PTD_EP_MSK (0xf << 12) - u16 len; -#define PTD_LEN_MSK (0x3ff << 0) -#define PTD_DIR_MSK (3 << 10) -#define PTD_DIR_SETUP (0) -#define PTD_DIR_OUT (1) -#define PTD_DIR_IN (2) - u16 faddr; -#define PTD_FA_MSK (0x7f << 0) -/* PTD Byte 7: [StartingFrame (if ISO PTD) | StartingFrame[0..4], PollingRate[0..2] (if INT PTD)] */ -#define PTD_SF_ISO_MSK (0xff << 8) -#define PTD_SF_INT_MSK (0x1f << 8) -#define PTD_PR_MSK (0x07 << 13) -} __attribute__ ((packed, aligned(2))); -#define PTD_HEADER_SIZE sizeof(struct ptd) - -/* ------------------------------------------------------------------------- */ -/* Copied from ohci.h: */ -/* - * Hardware transfer status codes -- CC from PTD - */ -#define PTD_CC_NOERROR 0x00 -#define PTD_CC_CRC 0x01 -#define PTD_CC_BITSTUFFING 0x02 -#define PTD_CC_DATATOGGLEM 0x03 -#define PTD_CC_STALL 0x04 -#define PTD_DEVNOTRESP 0x05 -#define PTD_PIDCHECKFAIL 0x06 -#define PTD_UNEXPECTEDPID 0x07 -#define PTD_DATAOVERRUN 0x08 -#define PTD_DATAUNDERRUN 0x09 - /* 0x0A, 0x0B reserved for hardware */ -#define PTD_BUFFEROVERRUN 0x0C -#define PTD_BUFFERUNDERRUN 0x0D - /* 0x0E, 0x0F reserved for HCD */ -#define PTD_NOTACCESSED 0x0F - - -/* map OHCI TD status codes (CC) to errno values */ -static const int cc_to_error[16] = { - /* No Error */ 0, - /* CRC Error */ -EILSEQ, - /* Bit Stuff */ -EPROTO, - /* Data Togg */ -EILSEQ, - /* Stall */ -EPIPE, - /* DevNotResp */ -ETIMEDOUT, - /* PIDCheck */ -EPROTO, - /* UnExpPID */ -EPROTO, - /* DataOver */ -EOVERFLOW, - /* DataUnder */ -EREMOTEIO, - /* (for hw) */ -EIO, - /* (for hw) */ -EIO, - /* BufferOver */ -ECOMM, - /* BuffUnder */ -ENOSR, - /* (for HCD) */ -EALREADY, - /* (for HCD) */ -EALREADY -}; - - -/* - * HcControl (control) register masks - */ -#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ -#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ -#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ - -/* pre-shifted values for HCFS */ -# define OHCI_USB_RESET (0 << 6) -# define OHCI_USB_RESUME (1 << 6) -# define OHCI_USB_OPER (2 << 6) -# define OHCI_USB_SUSPEND (3 << 6) - -/* - * HcCommandStatus (cmdstatus) register masks - */ -#define OHCI_HCR (1 << 0) /* host controller reset */ -#define OHCI_SOC (3 << 16) /* scheduling overrun count */ - -/* - * masks used with interrupt registers: - * HcInterruptStatus (intrstatus) - * HcInterruptEnable (intrenable) - * HcInterruptDisable (intrdisable) - */ -#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ -#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ -#define OHCI_INTR_SF (1 << 2) /* start frame */ -#define OHCI_INTR_RD (1 << 3) /* resume detect */ -#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ -#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ -#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ -#define OHCI_INTR_OC (1 << 30) /* ownership change */ -#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ - -/* roothub.portstatus [i] bits */ -#define RH_PS_CCS 0x00000001 /* current connect status */ -#define RH_PS_PES 0x00000002 /* port enable status*/ -#define RH_PS_PSS 0x00000004 /* port suspend status */ -#define RH_PS_POCI 0x00000008 /* port over current indicator */ -#define RH_PS_PRS 0x00000010 /* port reset status */ -#define RH_PS_PPS 0x00000100 /* port power status */ -#define RH_PS_LSDA 0x00000200 /* low speed device attached */ -#define RH_PS_CSC 0x00010000 /* connect status change */ -#define RH_PS_PESC 0x00020000 /* port enable status change */ -#define RH_PS_PSSC 0x00040000 /* port suspend status change */ -#define RH_PS_OCIC 0x00080000 /* over current indicator change */ -#define RH_PS_PRSC 0x00100000 /* port reset status change */ - -/* roothub.status bits */ -#define RH_HS_LPS 0x00000001 /* local power status */ -#define RH_HS_OCI 0x00000002 /* over current indicator */ -#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ -#define RH_HS_LPSC 0x00010000 /* local power status change */ -#define RH_HS_OCIC 0x00020000 /* over current indicator change */ -#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -#define FI 0x2edf /* 12000 bits per frame (-1) */ -#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7)) -#define LSTHRESH 0x628 /* lowspeed bit threshold */ - -/* ------------------------------------------------------------------------- */ - -/* PTD accessor macros. */ -#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0) -#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK) -#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10) -#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK) -#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11) -#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK) -#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12) -#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK) -#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0) -#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK) -#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10) -#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK) -#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11) -#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK) -#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12) -#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK) -#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0) -#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK) -#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10) -#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK) -#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0) -#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK) -#define PTD_GET_SF_INT(p) (((p)->faddr & PTD_SF_INT_MSK) >> 8) -#define PTD_SF_INT(v) (((v) << 8) & PTD_SF_INT_MSK) -#define PTD_GET_SF_ISO(p) (((p)->faddr & PTD_SF_ISO_MSK) >> 8) -#define PTD_SF_ISO(v) (((v) << 8) & PTD_SF_ISO_MSK) -#define PTD_GET_PR(p) (((p)->faddr & PTD_PR_MSK) >> 13) -#define PTD_PR(v) (((v) << 13) & PTD_PR_MSK) - -#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ -#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) - -struct isp1362_ep { - struct usb_host_endpoint *hep; - struct usb_device *udev; - - /* philips transfer descriptor */ - struct ptd ptd; - - u8 maxpacket; - u8 epnum; - u8 nextpid; - u16 error_count; - u16 length; /* of current packet */ - s16 ptd_offset; /* buffer offset in ISP1362 where - PTD has been stored - (for access thru HCDIRDATA) */ - int ptd_index; - int num_ptds; - void *data; /* to databuf */ - /* queue of active EPs (the ones transmitted to the chip) */ - struct list_head active; - - /* periodic schedule */ - u8 branch; - u16 interval; - u16 load; - u16 last_iso; - - /* async schedule */ - struct list_head schedule; /* list of all EPs that need processing */ - struct list_head remove_list; - int num_req; -}; - -struct isp1362_ep_queue { - struct list_head active; /* list of PTDs currently processed by HC */ - atomic_t finishing; - unsigned long buf_map; - unsigned long skip_map; - int free_ptd; - u16 buf_start; - u16 buf_size; - u16 blk_size; /* PTD buffer block size for ATL and INTL */ - u8 buf_count; - u8 buf_avail; - char name[16]; - - /* for statistical tracking */ - u8 stat_maxptds; /* Max # of ptds seen simultaneously in fifo */ - u8 ptd_count; /* number of ptds submitted to this queue */ -}; - -struct isp1362_hcd { - spinlock_t lock; - void __iomem *addr_reg; - void __iomem *data_reg; - - struct isp1362_platform_data *board; - - unsigned long stat1, stat2, stat4, stat8, stat16; - - /* HC registers */ - u32 intenb; /* "OHCI" interrupts */ - u16 irqenb; /* uP interrupts */ - - /* Root hub registers */ - u32 rhdesca; - u32 rhdescb; - u32 rhstatus; - u32 rhport[MAX_ROOT_PORTS]; - unsigned long next_statechange; - - /* HC control reg shadow copy */ - u32 hc_control; - - /* async schedule: control, bulk */ - struct list_head async; - - /* periodic schedule: int */ - u16 load[PERIODIC_SIZE]; - struct list_head periodic; - u16 fmindex; - - /* periodic schedule: isochronous */ - struct list_head isoc; - unsigned int istl_flip:1; - unsigned int irq_active:1; - - /* Schedules for the current frame */ - struct isp1362_ep_queue atl_queue; - struct isp1362_ep_queue intl_queue; - struct isp1362_ep_queue istl_queue[2]; - - /* list of PTDs retrieved from HC */ - struct list_head remove_list; - enum { - ISP1362_INT_SOF, - ISP1362_INT_ISTL0, - ISP1362_INT_ISTL1, - ISP1362_INT_EOT, - ISP1362_INT_OPR, - ISP1362_INT_SUSP, - ISP1362_INT_CLKRDY, - ISP1362_INT_INTL, - ISP1362_INT_ATL, - ISP1362_INT_OTG, - NUM_ISP1362_IRQS - } IRQ_NAMES; - unsigned int irq_stat[NUM_ISP1362_IRQS]; - int req_serial; -}; - -static inline const char *ISP1362_INT_NAME(int n) -{ - switch (n) { - case ISP1362_INT_SOF: return "SOF"; - case ISP1362_INT_ISTL0: return "ISTL0"; - case ISP1362_INT_ISTL1: return "ISTL1"; - case ISP1362_INT_EOT: return "EOT"; - case ISP1362_INT_OPR: return "OPR"; - case ISP1362_INT_SUSP: return "SUSP"; - case ISP1362_INT_CLKRDY: return "CLKRDY"; - case ISP1362_INT_INTL: return "INTL"; - case ISP1362_INT_ATL: return "ATL"; - case ISP1362_INT_OTG: return "OTG"; - default: return "unknown"; - } -} - -static inline void ALIGNSTAT(struct isp1362_hcd *isp1362_hcd, void *ptr) -{ - unsigned long p = (unsigned long)ptr; - if (!(p & 0xf)) - isp1362_hcd->stat16++; - else if (!(p & 0x7)) - isp1362_hcd->stat8++; - else if (!(p & 0x3)) - isp1362_hcd->stat4++; - else if (!(p & 0x1)) - isp1362_hcd->stat2++; - else - isp1362_hcd->stat1++; -} - -static inline struct isp1362_hcd *hcd_to_isp1362_hcd(struct usb_hcd *hcd) -{ - return (struct isp1362_hcd *) (hcd->hcd_priv); -} - -static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd) -{ - return container_of((void *)isp1362_hcd, struct usb_hcd, hcd_priv); -} - -#define frame_before(f1, f2) ((s16)((u16)f1 - (u16)f2) < 0) - -/* - * ISP1362 HW Interface - */ - -#define DBG(level, fmt...) \ - do { \ - if (dbg_level > level) \ - pr_debug(fmt); \ - } while (0) - -#ifdef VERBOSE -# define VDBG(fmt...) DBG(3, fmt) -#else -# define VDBG(fmt...) do {} while (0) -#endif - -#ifdef REGISTERS -# define RDBG(fmt...) DBG(1, fmt) -#else -# define RDBG(fmt...) do {} while (0) -#endif - -#ifdef URB_TRACE -#define URB_DBG(fmt...) DBG(0, fmt) -#else -#define URB_DBG(fmt...) do {} while (0) -#endif - - -#if USE_PLATFORM_DELAY -#if USE_NDELAY -#error USE_PLATFORM_DELAY and USE_NDELAY defined simultaneously. -#endif -#define isp1362_delay(h, d) (h)->board->delay(isp1362_hcd_to_hcd(h)->self.controller, d) -#elif USE_NDELAY -#define isp1362_delay(h, d) ndelay(d) -#else -#define isp1362_delay(h, d) do {} while (0) -#endif - -#define get_urb(ep) ({ \ - BUG_ON(list_empty(&ep->hep->urb_list)); \ - container_of(ep->hep->urb_list.next, struct urb, urb_list); \ -}) - -/* basic access functions for ISP1362 chip registers */ -/* NOTE: The contents of the address pointer register cannot be read back! The driver must ensure, - * that all register accesses are performed with interrupts disabled, since the interrupt - * handler has no way of restoring the previous state. - */ -static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t reg) -{ - REG_ACCESS_TEST(reg); - DUMMY_DELAY_ACCESS; - writew(ISP1362_REG_NO(reg), isp1362_hcd->addr_reg); - DUMMY_DELAY_ACCESS; - isp1362_delay(isp1362_hcd, 1); -} - -static void isp1362_write_data16(struct isp1362_hcd *isp1362_hcd, u16 val) -{ - DUMMY_DELAY_ACCESS; - writew(val, isp1362_hcd->data_reg); -} - -static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd) -{ - u16 val; - - DUMMY_DELAY_ACCESS; - val = readw(isp1362_hcd->data_reg); - - return val; -} - -static void isp1362_write_data32(struct isp1362_hcd *isp1362_hcd, u32 val) -{ -#if USE_32BIT - DUMMY_DELAY_ACCESS; - writel(val, isp1362_hcd->data_reg); -#else - DUMMY_DELAY_ACCESS; - writew((u16)val, isp1362_hcd->data_reg); - DUMMY_DELAY_ACCESS; - writew(val >> 16, isp1362_hcd->data_reg); -#endif -} - -static u32 isp1362_read_data32(struct isp1362_hcd *isp1362_hcd) -{ - u32 val; - -#if USE_32BIT - DUMMY_DELAY_ACCESS; - val = readl(isp1362_hcd->data_reg); -#else - DUMMY_DELAY_ACCESS; - val = (u32)readw(isp1362_hcd->data_reg); - DUMMY_DELAY_ACCESS; - val |= (u32)readw(isp1362_hcd->data_reg) << 16; -#endif - return val; -} - -/* use readsw/writesw to access the fifo whenever possible */ -/* assume HCDIRDATA or XFERCTR & addr_reg have been set up */ -static void isp1362_read_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len) -{ - u8 *dp = buf; - u16 data; - - if (!len) - return; - - RDBG("%s: Reading %d byte from fifo to mem @ %p\n", __func__, len, buf); -#if USE_32BIT - if (len >= 4) { - RDBG("%s: Using readsl for %d dwords\n", __func__, len >> 2); - readsl(isp1362_hcd->data_reg, dp, len >> 2); - dp += len & ~3; - len &= 3; - } -#endif - if (len >= 2) { - RDBG("%s: Using readsw for %d words\n", __func__, len >> 1); - insw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1); - dp += len & ~1; - len &= 1; - } - - BUG_ON(len & ~1); - if (len > 0) { - data = isp1362_read_data16(isp1362_hcd); - RDBG("%s: Reading trailing byte %02x to mem @ %08x\n", __func__, - (u8)data, (u32)dp); - *dp = (u8)data; - } -} - -static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len) -{ - u8 *dp = buf; - u16 data; - - if (!len) - return; - - if ((unsigned long)dp & 0x1) { - /* not aligned */ - for (; len > 1; len -= 2) { - data = *dp++; - data |= *dp++ << 8; - isp1362_write_data16(isp1362_hcd, data); - } - if (len) - isp1362_write_data16(isp1362_hcd, *dp); - return; - } - - RDBG("%s: Writing %d byte to fifo from memory @%p\n", __func__, len, buf); -#if USE_32BIT - if (len >= 4) { - RDBG("%s: Using writesl for %d dwords\n", __func__, len >> 2); - writesl(isp1362_hcd->data_reg, dp, len >> 2); - dp += len & ~3; - len &= 3; - } -#endif - if (len >= 2) { - RDBG("%s: Using writesw for %d words\n", __func__, len >> 1); - outsw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1); - dp += len & ~1; - len &= 1; - } - - BUG_ON(len & ~1); - if (len > 0) { - /* finally write any trailing byte; we don't need to care - * about the high byte of the last word written - */ - data = (u16)*dp; - RDBG("%s: Sending trailing byte %02x from mem @ %08x\n", __func__, - data, (u32)dp); - isp1362_write_data16(isp1362_hcd, data); - } -} - -#define isp1362_read_reg16(d, r) ({ \ - u16 __v; \ - REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \ - isp1362_write_addr(d, ISP1362_REG_##r); \ - __v = isp1362_read_data16(d); \ - RDBG("%s: Read %04x from %s[%02x]\n", __func__, __v, #r, \ - ISP1362_REG_NO(ISP1362_REG_##r)); \ - __v; \ -}) - -#define isp1362_read_reg32(d, r) ({ \ - u32 __v; \ - REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \ - isp1362_write_addr(d, ISP1362_REG_##r); \ - __v = isp1362_read_data32(d); \ - RDBG("%s: Read %08x from %s[%02x]\n", __func__, __v, #r, \ - ISP1362_REG_NO(ISP1362_REG_##r)); \ - __v; \ -}) - -#define isp1362_write_reg16(d, r, v) { \ - REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \ - isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \ - isp1362_write_data16(d, (u16)(v)); \ - RDBG("%s: Wrote %04x to %s[%02x]\n", __func__, (u16)(v), #r, \ - ISP1362_REG_NO(ISP1362_REG_##r)); \ -} - -#define isp1362_write_reg32(d, r, v) { \ - REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \ - isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \ - isp1362_write_data32(d, (u32)(v)); \ - RDBG("%s: Wrote %08x to %s[%02x]\n", __func__, (u32)(v), #r, \ - ISP1362_REG_NO(ISP1362_REG_##r)); \ -} - -#define isp1362_set_mask16(d, r, m) { \ - u16 __v; \ - __v = isp1362_read_reg16(d, r); \ - if ((__v | m) != __v) \ - isp1362_write_reg16(d, r, __v | m); \ -} - -#define isp1362_clr_mask16(d, r, m) { \ - u16 __v; \ - __v = isp1362_read_reg16(d, r); \ - if ((__v & ~m) != __v) \ - isp1362_write_reg16(d, r, __v & ~m); \ -} - -#define isp1362_set_mask32(d, r, m) { \ - u32 __v; \ - __v = isp1362_read_reg32(d, r); \ - if ((__v | m) != __v) \ - isp1362_write_reg32(d, r, __v | m); \ -} - -#define isp1362_clr_mask32(d, r, m) { \ - u32 __v; \ - __v = isp1362_read_reg32(d, r); \ - if ((__v & ~m) != __v) \ - isp1362_write_reg32(d, r, __v & ~m); \ -} - -#define isp1362_show_reg(d, r) { \ - if ((ISP1362_REG_##r & REG_WIDTH_MASK) == REG_WIDTH_32) \ - DBG(0, "%-12s[%02x]: %08x\n", #r, \ - ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg32(d, r)); \ - else \ - DBG(0, "%-12s[%02x]: %04x\n", #r, \ - ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \ -} - -static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len) -{ - len = (len + 1) & ~1; - - isp1362_clr_mask16(isp1362_hcd, HCDMACFG, HCDMACFG_CTR_ENABLE); - isp1362_write_reg32(isp1362_hcd, HCDIRADDR, - HCDIRADDR_ADDR(offset) | HCDIRADDR_COUNT(len)); -} - -static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) -{ - isp1362_write_diraddr(isp1362_hcd, offset, len); - - DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %p\n", - __func__, len, offset, buf); - - isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); - - isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA); - - isp1362_read_fifo(isp1362_hcd, buf, len); - isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); -} - -static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) -{ - isp1362_write_diraddr(isp1362_hcd, offset, len); - - DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %p\n", - __func__, len, offset, buf); - - isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); - - isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA | ISP1362_REG_WRITE_OFFSET); - isp1362_write_fifo(isp1362_hcd, buf, len); - - isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); -} - -static void __attribute__((unused)) dump_data(char *buf, int len) -{ - if (dbg_level > 0) { - int k; - int lf = 0; - - for (k = 0; k < len; ++k) { - if (!lf) - DBG(0, "%04x:", k); - printk(" %02x", ((u8 *) buf)[k]); - lf = 1; - if (!k) - continue; - if (k % 16 == 15) { - printk("\n"); - lf = 0; - continue; - } - if (k % 8 == 7) - printk(" "); - if (k % 4 == 3) - printk(" "); - } - if (lf) - printk("\n"); - } -} - -#if defined(PTD_TRACE) - -static void dump_ptd(struct ptd *ptd) -{ - DBG(0, "EP %p: CC=%x EP=%d DIR=%x CNT=%d LEN=%d MPS=%d TGL=%x ACT=%x FA=%d SPD=%x SF=%x PR=%x LST=%x\n", - container_of(ptd, struct isp1362_ep, ptd), - PTD_GET_CC(ptd), PTD_GET_EP(ptd), PTD_GET_DIR(ptd), - PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd), - PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd), PTD_GET_FA(ptd), - PTD_GET_SPD(ptd), PTD_GET_SF_INT(ptd), PTD_GET_PR(ptd), PTD_GET_LAST(ptd)); - DBG(0, " %04x %04x %04x %04x\n", ptd->count, ptd->mps, ptd->len, ptd->faddr); -} - -static void dump_ptd_out_data(struct ptd *ptd, u8 *buf) -{ - if (dbg_level > 0) { - if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) { - DBG(0, "--out->\n"); - dump_data(buf, PTD_GET_LEN(ptd)); - } - } -} - -static void dump_ptd_in_data(struct ptd *ptd, u8 *buf) -{ - if (dbg_level > 0) { - if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) { - DBG(0, "<--in--\n"); - dump_data(buf, PTD_GET_COUNT(ptd)); - } - DBG(0, "-----\n"); - } -} - -static void dump_ptd_queue(struct isp1362_ep_queue *epq) -{ - struct isp1362_ep *ep; - int dbg = dbg_level; - - dbg_level = 1; - list_for_each_entry(ep, &epq->active, active) { - dump_ptd(&ep->ptd); - dump_data(ep->data, ep->length); - } - dbg_level = dbg; -} -#else -#define dump_ptd(ptd) do {} while (0) -#define dump_ptd_in_data(ptd, buf) do {} while (0) -#define dump_ptd_out_data(ptd, buf) do {} while (0) -#define dump_ptd_data(ptd, buf) do {} while (0) -#define dump_ptd_queue(epq) do {} while (0) -#endif diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 0881fdd1823e..0e17c988d36a 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1523,7 +1523,7 @@ max3421_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) max3421_ep = urb->ep->hcpriv; if (!max3421_ep) { /* gets freed in max3421_endpoint_disable: */ - max3421_ep = kzalloc(sizeof(struct max3421_ep), GFP_ATOMIC); + max3421_ep = kzalloc_obj(struct max3421_ep, GFP_ATOMIC); if (!max3421_ep) { retval = -ENOMEM; goto out; @@ -1878,10 +1878,10 @@ max3421_probe(struct spi_device *spi) INIT_LIST_HEAD(&max3421_hcd->ep_list); spi_set_drvdata(spi, max3421_hcd); - max3421_hcd->tx = kmalloc(sizeof(*max3421_hcd->tx), GFP_KERNEL); + max3421_hcd->tx = kmalloc_obj(*max3421_hcd->tx); if (!max3421_hcd->tx) goto error; - max3421_hcd->rx = kmalloc(sizeof(*max3421_hcd->rx), GFP_KERNEL); + max3421_hcd->rx = kmalloc_obj(*max3421_hcd->rx); if (!max3421_hcd->rx) goto error; @@ -1916,7 +1916,7 @@ error: if (hcd) { kfree(max3421_hcd->tx); kfree(max3421_hcd->rx); - if (max3421_hcd->spi_thread) + if (!IS_ERR_OR_NULL(max3421_hcd->spi_thread)) kthread_stop(max3421_hcd->spi_thread); usb_put_hcd(hcd); } @@ -1946,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", }, {}, @@ -1955,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 361d33b0c4d2..34ce771fa0f5 100644 --- a/drivers/usb/host/octeon-hcd.c +++ b/drivers/usb/host/octeon-hcd.c @@ -1098,7 +1098,7 @@ static struct cvmx_usb_pipe *cvmx_usb_open_pipe(struct octeon_hcd *usb, { struct cvmx_usb_pipe *pipe; - pipe = kzalloc(sizeof(*pipe), GFP_ATOMIC); + pipe = kzalloc_obj(*pipe, GFP_ATOMIC); if (!pipe) return NULL; if ((device_speed == CVMX_USB_SPEED_HIGH) && @@ -2138,7 +2138,7 @@ static struct cvmx_usb_transaction *cvmx_usb_submit_transaction( if (unlikely(pipe->transfer_type != type)) return NULL; - transaction = kzalloc(sizeof(*transaction), GFP_ATOMIC); + transaction = kzalloc_obj(*transaction, GFP_ATOMIC); if (unlikely(!transaction)) return NULL; @@ -3180,9 +3180,8 @@ static int octeon_usb_urb_enqueue(struct usb_hcd *hcd, * Allocate a structure to use for our private list of * isochronous packets. */ - iso_packet = kmalloc_array(urb->number_of_packets, - sizeof(struct cvmx_usb_iso_packet), - GFP_ATOMIC); + iso_packet = kmalloc_objs(struct cvmx_usb_iso_packet, + urb->number_of_packets, GFP_ATOMIC); if (iso_packet) { int i; /* Fill the list with the data from the URB */ diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 5df793dcb25d..12fdb18934cf 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -193,7 +193,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, if (irq < 0) return irq; - hcd = usb_create_hcd(driver, dev, "at91"); + hcd = usb_create_hcd(driver, dev, dev_name(dev)); if (!hcd) return -ENOMEM; ohci_at91 = hcd_to_ohci_at91_priv(hcd); diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 3c5ca2d7c92e..0938c0e7a8b6 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -18,7 +18,6 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> -#include <linux/platform_data/usb-davinci.h> #include <linux/regulator/consumer.h> #include <linux/usb.h> #include <linux/usb/hcd.h> @@ -166,17 +165,6 @@ static int ohci_da8xx_has_oci(struct usb_hcd *hcd) return 0; } -static int ohci_da8xx_has_potpgt(struct usb_hcd *hcd) -{ - struct device *dev = hcd->self.controller; - struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); - - if (hub && hub->potpgt) - return 1; - - return 0; -} - static int ohci_da8xx_regulator_event(struct notifier_block *nb, unsigned long event, void *data) { @@ -228,7 +216,6 @@ static int ohci_da8xx_register_notify(struct usb_hcd *hcd) static int ohci_da8xx_reset(struct usb_hcd *hcd) { struct device *dev = hcd->self.controller; - struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); int result; u32 rh_a; @@ -266,10 +253,6 @@ static int ohci_da8xx_reset(struct usb_hcd *hcd) rh_a &= ~RH_A_NOCP; rh_a |= RH_A_OCPM; } - if (ohci_da8xx_has_potpgt(hcd)) { - rh_a &= ~RH_A_POTPGT; - rh_a |= hub->potpgt << 24; - } ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); return result; diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 76bc8d56325d..9e0e06bbc570 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -492,7 +492,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) char *next; unsigned i; - seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC); + seen = kmalloc_objs(*seen, DBG_SCHED_LIMIT, GFP_ATOMIC); if (!seen) return 0; seen_count = 0; @@ -667,7 +667,7 @@ static struct debug_buffer *alloc_buffer(struct ohci_hcd *ohci, { struct debug_buffer *buf; - buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); + buf = kzalloc_obj(struct debug_buffer); if (buf) { buf->ohci = ohci; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 9b24181fee60..129468b1efc2 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -190,7 +190,7 @@ static int ohci_urb_enqueue ( } /* allocate the private part of the URB */ - urb_priv = kzalloc(struct_size(urb_priv, td, size), mem_flags); + urb_priv = kzalloc_flex(*urb_priv, td, size, mem_flags); if (!urb_priv) return -ENOMEM; INIT_LIST_HEAD (&urb_priv->pending); @@ -746,7 +746,8 @@ static int ohci_start(struct usb_hcd *hcd) */ static void io_watchdog_func(struct timer_list *t) { - struct ohci_hcd *ohci = from_timer(ohci, t, io_watchdog); + struct ohci_hcd *ohci = timer_container_of(ohci, t, + io_watchdog); bool takeback_all_pending = false; u32 status; u32 head; @@ -1003,7 +1004,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); @@ -1281,7 +1282,6 @@ static int __init ohci_hcd_mod_init(void) pr_debug ("%s: block sizes: ed %zd td %zd\n", hcd_name, sizeof (struct ed), sizeof (struct td)); - set_bit(USB_OHCI_LOADED, &usb_hcds_loaded); ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root); @@ -1331,7 +1331,6 @@ static int __init ohci_hcd_mod_init(void) debugfs_remove(ohci_debug_root); ohci_debug_root = NULL; - clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded); return retval; } module_init(ohci_hcd_mod_init); @@ -1351,7 +1350,6 @@ static void __exit ohci_hcd_mod_exit(void) ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif debugfs_remove(ohci_debug_root); - clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded); } module_exit(ohci_hcd_mod_exit); 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 24d5a1dc5056..7663f2aa35e9 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -169,13 +169,13 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) - goto fail_disable; + goto err_put_client; dev_dbg(&pdev->dev, "%s: " DRIVER_DESC " (nxp)\n", hcd_name); if (usb_disabled()) { dev_err(&pdev->dev, "USB is disabled\n"); ret = -ENODEV; - goto fail_disable; + goto err_put_client; } /* Enable USB host clock */ @@ -183,7 +183,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) if (IS_ERR(usb_host_clk)) { dev_err(&pdev->dev, "failed to acquire and start USB OHCI clock\n"); ret = PTR_ERR(usb_host_clk); - goto fail_disable; + goto err_put_client; } isp1301_configure(); @@ -192,13 +192,13 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) if (!hcd) { dev_err(&pdev->dev, "Failed to allocate HC buffer\n"); ret = -ENOMEM; - goto fail_disable; + goto err_put_client; } hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(hcd->regs)) { ret = PTR_ERR(hcd->regs); - goto fail_resource; + goto err_put_hcd; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); @@ -206,7 +206,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = -ENXIO; - goto fail_resource; + goto err_put_hcd; } ohci_nxp_start_hc(); @@ -220,9 +220,10 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) } ohci_nxp_stop_hc(); -fail_resource: +err_put_hcd: usb_put_hcd(hcd); -fail_disable: +err_put_client: + put_device(&isp1301_i2c_client->dev); isp1301_i2c_client = NULL; return ret; } @@ -234,6 +235,7 @@ static void ohci_hcd_nxp_remove(struct platform_device *pdev) usb_remove_hcd(hcd); ohci_nxp_stop_hc(); usb_put_hcd(hcd); + put_device(&isp1301_i2c_client->dev); isp1301_i2c_client = NULL; } diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 900ea0d368e0..9f0a6b27e47c 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -165,6 +165,25 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd) return 0; } +static int ohci_quirk_loongson(struct usb_hcd *hcd) +{ + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + + /* + * Loongson's LS7A OHCI controller (rev 0x02) has a + * flaw. MMIO register with offset 0x60/64 is treated + * as legacy PS2-compatible keyboard/mouse interface. + * Since OHCI only use 4KB BAR resource, LS7A OHCI's + * 32KB BAR is wrapped around (the 2nd 4KB BAR space + * is the same as the 1st 4KB internally). So add 4KB + * offset (0x1000) to the OHCI registers as a quirk. + */ + if (pdev->revision == 0x2) + hcd->regs += SZ_4K; /* SZ_4K = 0x1000 */ + + return 0; +} + static int ohci_quirk_qemu(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci(hcd); @@ -225,6 +244,10 @@ static const struct pci_device_id ohci_pci_quirks[] = { .driver_data = (unsigned long)ohci_quirk_amd700, }, { + PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x7a24), + .driver_data = (unsigned long)ohci_quirk_loongson, + }, + { .vendor = PCI_VENDOR_ID_APPLE, .device = 0x003f, .subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET, diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index f47ae12cde6a..c801527d5bd2 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -69,8 +69,7 @@ static void ohci_platform_power_off(struct platform_device *dev) int clk; for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) - if (priv->clks[clk]) - clk_disable_unprepare(priv->clks[clk]); + clk_disable_unprepare(priv->clks[clk]); } static struct hc_driver __read_mostly ohci_platform_hc_driver; @@ -271,6 +270,7 @@ static int ohci_platform_suspend(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev->platform_data; struct platform_device *pdev = to_platform_device(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); bool do_wakeup = device_may_wakeup(dev); int ret; @@ -281,6 +281,14 @@ static int ohci_platform_suspend(struct device *dev) if (pdata->power_suspend) pdata->power_suspend(pdev); + ret = reset_control_assert(priv->resets); + if (ret) { + if (pdata->power_on) + pdata->power_on(pdev); + + ohci_resume(hcd, false); + } + return ret; } @@ -289,11 +297,19 @@ static int ohci_platform_resume_common(struct device *dev, bool hibernated) struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = to_platform_device(dev); + struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); + int err; + + err = reset_control_deassert(priv->resets); + if (err) + return err; if (pdata->power_on) { - int err = pdata->power_on(pdev); - if (err < 0) + err = pdata->power_on(pdev); + if (err < 0) { + reset_control_assert(priv->resets); return err; + } } ohci_resume(hcd, hibernated); @@ -376,3 +392,4 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("Hauke Mehrtens"); MODULE_AUTHOR("Alan Stern"); MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: ehci_platform"); diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index 66d970854357..e623e24d3f8e 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -448,13 +448,6 @@ static const struct dev_pm_ops ohci_hcd_s3c2410_pm_ops = { .resume = ohci_hcd_s3c2410_drv_resume, }; -static const struct of_device_id ohci_hcd_s3c2410_dt_ids[] = { - { .compatible = "samsung,s3c2410-ohci" }, - { /* sentinel */ } -}; - -MODULE_DEVICE_TABLE(of, ohci_hcd_s3c2410_dt_ids); - static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_probe, .remove = ohci_hcd_s3c2410_remove, @@ -462,7 +455,6 @@ static struct platform_driver ohci_hcd_s3c2410_driver = { .driver = { .name = "s3c2410-ohci", .pm = &ohci_hcd_s3c2410_pm_ops, - .of_match_table = ohci_hcd_s3c2410_dt_ids, }, }; diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index d7131e5a4477..6843d7cb3f9f 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -103,8 +103,7 @@ static void spear_ohci_hcd_drv_remove(struct platform_device *pdev) struct spear_ohci *sohci_p = to_spear_ohci(hcd); usb_remove_hcd(hcd); - if (sohci_p->clk) - clk_disable_unprepare(sohci_p->clk); + clk_disable_unprepare(sohci_p->clk); usb_put_hcd(hcd); } diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index fce800ba4c61..ca96067e6d6d 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -1127,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; @@ -1149,7 +1149,7 @@ static int ehci_mem_init(struct oxu_hcd *oxu, gfp_t flags) for (i = 0; i < QTD_NUM; i++) oxu->qtd_used[i] = 0; - oxu->murb_pool = kcalloc(MURB_NUM, sizeof(struct oxu_murb), flags); + oxu->murb_pool = kzalloc_objs(struct oxu_murb, MURB_NUM, flags); if (!oxu->murb_pool) goto fail; @@ -2955,7 +2955,7 @@ static irqreturn_t oxu_irq(struct usb_hcd *hcd) static void oxu_watchdog(struct timer_list *t) { - struct oxu_hcd *oxu = from_timer(oxu, t, watchdog); + struct oxu_hcd *oxu = timer_container_of(oxu, t, watchdog); unsigned long flags; spin_lock_irqsave(&oxu->lock, flags); @@ -3154,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)) @@ -3887,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; diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index a44992e2561b..0a26489a40d0 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -337,7 +337,7 @@ static int make_r8a66597_device(struct r8a66597 *r8a66597, struct r8a66597_device *dev; int usb_address = urb->setup_packet[2]; /* urb->pipe is address 0 */ - dev = kzalloc(sizeof(struct r8a66597_device), GFP_ATOMIC); + dev = kzalloc_obj(struct r8a66597_device, GFP_ATOMIC); if (dev == NULL) return -ENOMEM; @@ -1720,7 +1720,8 @@ static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port) static void r8a66597_interval_timer(struct timer_list *t) { - struct r8a66597_timers *timers = from_timer(timers, t, interval); + struct r8a66597_timers *timers = timer_container_of(timers, t, + interval); struct r8a66597 *r8a66597 = timers->r8a66597; unsigned long flags; u16 pipenum; @@ -1744,7 +1745,7 @@ static void r8a66597_interval_timer(struct timer_list *t) static void r8a66597_td_timer(struct timer_list *t) { - struct r8a66597_timers *timers = from_timer(timers, t, td); + struct r8a66597_timers *timers = timer_container_of(timers, t, td); struct r8a66597 *r8a66597 = timers->r8a66597; unsigned long flags; u16 pipenum; @@ -1798,7 +1799,7 @@ static void r8a66597_td_timer(struct timer_list *t) static void r8a66597_timer(struct timer_list *t) { - struct r8a66597 *r8a66597 = from_timer(r8a66597, t, rh_timer); + struct r8a66597 *r8a66597 = timer_container_of(r8a66597, t, rh_timer); unsigned long flags; int port; @@ -1858,7 +1859,7 @@ static struct r8a66597_td *r8a66597_make_td(struct r8a66597 *r8a66597, struct r8a66597_td *td; u16 pipenum; - td = kzalloc(sizeof(struct r8a66597_td), GFP_ATOMIC); + td = kzalloc_obj(struct r8a66597_td, GFP_ATOMIC); if (td == NULL) return NULL; @@ -1900,8 +1901,7 @@ static int r8a66597_urb_enqueue(struct usb_hcd *hcd, goto error_not_linked; if (!hep->hcpriv) { - hep->hcpriv = kzalloc(sizeof(struct r8a66597_pipe), - GFP_ATOMIC); + hep->hcpriv = kzalloc_obj(struct r8a66597_pipe, GFP_ATOMIC); if (!hep->hcpriv) { ret = -ENOMEM; goto error; @@ -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) diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index fa2e4badd288..4ae47edd4b8b 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -814,7 +814,7 @@ static int sl811h_urb_enqueue( /* avoid all allocations within spinlocks */ if (!hep->hcpriv) { - ep = kzalloc(sizeof *ep, mem_flags); + ep = kzalloc_obj(*ep, mem_flags); if (ep == NULL) return -ENOMEM; } @@ -1124,7 +1124,7 @@ sl811h_hub_descriptor ( static void sl811h_timer(struct timer_list *t) { - struct sl811 *sl811 = from_timer(sl811, t, timer); + struct sl811 *sl811 = timer_container_of(sl811, t, timer); unsigned long flags; u8 irqstat; u8 signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE; @@ -1515,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); @@ -1748,6 +1748,7 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state) break; case PM_EVENT_SUSPEND: case PM_EVENT_HIBERNATE: + case PM_EVENT_POWEROFF: case PM_EVENT_PRETHAW: /* explicitly discard hw state */ port_power(sl811, 0); break; diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 16d157013018..ada91ca33f65 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -178,7 +178,7 @@ static int sl811_cs_probe(struct pcmcia_device *link) { local_info_t *local; - local = kzalloc(sizeof(local_info_t), GFP_KERNEL); + local = kzalloc_obj(local_info_t); if (!local) return -ENOMEM; local->p_dev = link; diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index c4e67c4b51f6..e91ec6a4fa74 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -561,7 +561,7 @@ static int uhci_debug_open(struct inode *inode, struct file *file) struct uhci_debug *up; unsigned long flags; - up = kmalloc(sizeof(*up), GFP_KERNEL); + up = kmalloc_obj(*up); if (!up) return -ENOMEM; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index fd2408b553cf..433d64a5fab5 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -603,8 +603,7 @@ static int uhci_start(struct usb_hcd *hcd) goto err_alloc_frame; } - uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu), - GFP_KERNEL); + uhci->frame_cpu = kzalloc_objs(*uhci->frame_cpu, UHCI_NUMFRAMES); if (!uhci->frame_cpu) goto err_alloc_frame_cpu; @@ -716,7 +715,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); } @@ -867,8 +866,6 @@ static int __init uhci_hcd_init(void) if (usb_disabled()) return -ENODEV; - set_bit(USB_UHCI_LOADED, &usb_hcds_loaded); - #ifdef CONFIG_DYNAMIC_DEBUG errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL); if (!errbuf) @@ -912,8 +909,6 @@ up_failed: errbuf_failed: #endif - - clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded); return retval; } @@ -930,7 +925,6 @@ static void __exit uhci_hcd_cleanup(void) #ifdef CONFIG_DYNAMIC_DEBUG kfree(errbuf); #endif - clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded); } module_init(uhci_hcd_init); diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 13ee2a6144b2..4326d1f3ca76 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -445,6 +445,7 @@ struct uhci_hcd { short load[MAX_PHASE]; /* Periodic allocations */ struct clk *clk; /* (optional) clock source */ + struct reset_control *rsts; /* (optional) clock reset */ /* Reset host controller */ void (*reset_hc) (struct uhci_hcd *uhci); diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index a7c934404ebc..f4419d4526c4 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -11,6 +11,7 @@ #include <linux/of.h> #include <linux/device.h> #include <linux/platform_device.h> +#include <linux/reset.h> static int uhci_platform_init(struct usb_hcd *hcd) { @@ -67,6 +68,7 @@ static const struct hc_driver uhci_platform_hc_driver = { static int uhci_hcd_platform_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + bool dma_mask_64 = false; struct usb_hcd *hcd; struct uhci_hcd *uhci; struct resource *res; @@ -80,7 +82,11 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) * Since shared usb code relies on it, set it here for now. * Once we have dma capability bindings this can go away. */ - ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (of_device_get_match_data(&pdev->dev)) + dma_mask_64 = true; + + ret = dma_coerce_mask_and_coherent(&pdev->dev, + dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); if (ret) return ret; @@ -113,7 +119,8 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) } if (of_device_is_compatible(np, "aspeed,ast2400-uhci") || of_device_is_compatible(np, "aspeed,ast2500-uhci") || - of_device_is_compatible(np, "aspeed,ast2600-uhci")) { + of_device_is_compatible(np, "aspeed,ast2600-uhci") || + of_device_is_compatible(np, "aspeed,ast2700-uhci")) { uhci->is_aspeed = 1; dev_info(&pdev->dev, "Enabled Aspeed implementation workarounds\n"); @@ -121,7 +128,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) } /* Get and enable clock if any specified */ - uhci->clk = devm_clk_get(&pdev->dev, NULL); + uhci->clk = devm_clk_get_optional(&pdev->dev, NULL); if (IS_ERR(uhci->clk)) { ret = PTR_ERR(uhci->clk); goto err_rmr; @@ -132,17 +139,28 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) goto err_rmr; } + uhci->rsts = devm_reset_control_array_get_optional_shared(&pdev->dev); + if (IS_ERR(uhci->rsts)) { + ret = PTR_ERR(uhci->rsts); + goto err_clk; + } + ret = reset_control_deassert(uhci->rsts); + if (ret) + goto err_clk; + ret = platform_get_irq(pdev, 0); if (ret < 0) - goto err_clk; + goto err_reset; ret = usb_add_hcd(hcd, ret, IRQF_SHARED); if (ret) - goto err_clk; + goto err_reset; device_wakeup_enable(hcd->self.controller); return 0; +err_reset: + reset_control_assert(uhci->rsts); err_clk: clk_disable_unprepare(uhci->clk); err_rmr: @@ -156,6 +174,7 @@ static void uhci_hcd_platform_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); struct uhci_hcd *uhci = hcd_to_uhci(hcd); + reset_control_assert(uhci->rsts); clk_disable_unprepare(uhci->clk); usb_remove_hcd(hcd); usb_put_hcd(hcd); @@ -178,6 +197,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op) static const struct of_device_id platform_uhci_ids[] = { { .compatible = "generic-uhci", }, { .compatible = "platform-uhci", }, + { .compatible = "aspeed,ast2700-uhci", .data = (void *)1 }, {} }; MODULE_DEVICE_TABLE(of, platform_uhci_ids); @@ -191,3 +211,4 @@ static struct platform_driver uhci_platform_driver = { .of_match_table = platform_uhci_ids, }, }; +MODULE_SOFTDEP("pre: ehci_platform"); diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 35fcb826152c..9480d4ff0111 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -84,14 +84,14 @@ 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); } } } static void uhci_fsbr_timeout(struct timer_list *t) { - struct uhci_hcd *uhci = from_timer(uhci, t, fsbr_timer); + struct uhci_hcd *uhci = timer_container_of(uhci, t, fsbr_timer); unsigned long flags; spin_lock_irqsave(&uhci->lock, flags); diff --git a/drivers/usb/host/xen-hcd.c b/drivers/usb/host/xen-hcd.c index 46fdab940092..0a94d302911a 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; } @@ -1258,7 +1258,7 @@ static void xenhcd_disconnect(struct xenbus_device *dev) static void xenhcd_watchdog(struct timer_list *timer) { - struct xenhcd_info *info = from_timer(info, timer, watchdog); + struct xenhcd_info *info = timer_container_of(info, timer, watchdog); unsigned long flags; spin_lock_irqsave(&info->lock, flags); @@ -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; @@ -1388,7 +1388,7 @@ static int xenhcd_get_frame(struct usb_hcd *hcd) return 0; } -static struct hc_driver xenhcd_usb20_hc_driver = { +static const struct hc_driver xenhcd_usb20_hc_driver = { .description = "xen-hcd", .product_desc = "Xen USB2.0 Virtual Host Controller", .hcd_priv_size = sizeof(struct xenhcd_info), @@ -1413,7 +1413,7 @@ static struct hc_driver xenhcd_usb20_hc_driver = { #endif }; -static struct hc_driver xenhcd_usb11_hc_driver = { +static const struct hc_driver xenhcd_usb11_hc_driver = { .description = "xen-hcd", .product_desc = "Xen USB1.1 Virtual Host Controller", .hcd_priv_size = sizeof(struct xenhcd_info), diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h index f6b9a00a0ab9..2f59b6ab1e45 100644 --- a/drivers/usb/host/xhci-caps.h +++ b/drivers/usb/host/xhci-caps.h @@ -1,91 +1,120 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * xHCI Host Controller Capability Registers. + * xHCI Specification Section 5.3, Revision 1.2. + */ + +#include <linux/bits.h> -/* hc_capbase bitmasks */ -/* bits 7:0 - how long is the Capabilities register */ -#define HC_LENGTH(p) XHCI_HC_LENGTH(p) -/* bits 31:16 */ +/* hc_capbase - bitmasks */ +/* bits 7:0 - Capability Registers Length */ +#define HC_LENGTH(p) ((p) & 0xff) +/* bits 15:8 - Rsvd */ +/* bits 31:16 - Host Controller Interface Version Number */ #define HC_VERSION(p) (((p) >> 16) & 0xffff) /* HCSPARAMS1 - hcs_params1 - bitmasks */ -/* bits 0:7, Max Device Slots */ +/* bits 7:0 - Number of Device Slots */ #define HCS_MAX_SLOTS(p) (((p) >> 0) & 0xff) #define HCS_SLOTS_MASK 0xff -/* bits 8:18, Max Interrupters */ +/* bits 18:8 - Number of Interrupters, max values is 1024 */ #define HCS_MAX_INTRS(p) (((p) >> 8) & 0x7ff) -/* bits 24:31, Max Ports - max value is 0x7F = 127 ports */ -#define HCS_MAX_PORTS(p) (((p) >> 24) & 0x7f) +/* bits 31:24, Max Ports - max value is 255 */ +#define HCS_MAX_PORTS(p) (((p) >> 24) & 0xff) /* HCSPARAMS2 - hcs_params2 - bitmasks */ -/* bits 0:3, frames or uframes that SW needs to queue transactions - * ahead of the HW to meet periodic deadlines */ -#define HCS_IST(p) (((p) >> 0) & 0xf) -/* bits 4:7, max number of Event Ring segments */ +/* + * bits 3:0 - Isochronous Scheduling Threshold, frames or uframes that SW + * needs to queue transactions ahead of the HW to meet periodic deadlines. + * - Bits 2:0: Threshold value + * - Bit 3: Unit indicator + * - '1': Threshold in Frames + * - '0': Threshold in Microframes (uframes) + * Note: 1 Frame = 8 Microframes + * xHCI specification section 5.3.4. + */ +#define HCS_IST_VALUE(p) ((p) & 0x7) +#define HCS_IST_UNIT BIT(3) +/* bits 7:4 - Event Ring Segment Table Max, 2^(n) */ #define HCS_ERST_MAX(p) (((p) >> 4) & 0xf) -/* bits 21:25 Hi 5 bits of Scratchpad buffers SW must allocate for the HW */ -/* bit 26 Scratchpad restore - for save/restore HW state - not used yet */ -/* bits 27:31 Lo 5 bits of Scratchpad buffers SW must allocate for the HW */ -#define HCS_MAX_SCRATCHPAD(p) ((((p) >> 16) & 0x3e0) | (((p) >> 27) & 0x1f)) +/* bits 20:8 - Rsvd */ +/* bits 25:21 - Max Scratchpad Buffers (Hi), 5 Most significant bits */ +#define HCS_MAX_SP_HI(p) (((p) >> 21) & 0x1f) +/* bit 26 - Scratchpad restore, for save/restore HW state */ +/* bits 31:27 - Max Scratchpad Buffers (Lo), 5 Least significant bits */ +#define HCS_MAX_SP_LO(p) (((p) >> 27) & 0x1f) +#define HCS_MAX_SCRATCHPAD(p) (HCS_MAX_SP_HI(p) << 5 | HCS_MAX_SP_LO(p)) /* HCSPARAMS3 - hcs_params3 - bitmasks */ -/* bits 0:7, Max U1 to U0 latency for the roothub ports */ +/* bits 7:0 - U1 Device Exit Latency, Max U1 to U0 latency for the roothub ports */ #define HCS_U1_LATENCY(p) (((p) >> 0) & 0xff) -/* bits 16:31, Max U2 to U0 latency for the roothub ports */ +/* bits 15:8 - Rsvd */ +/* bits 31:16 - U2 Device Exit Latency, Max U2 to U0 latency for the roothub ports */ #define HCS_U2_LATENCY(p) (((p) >> 16) & 0xffff) -/* HCCPARAMS - hcc_params - bitmasks */ -/* true: HC can use 64-bit address pointers */ -#define HCC_64BIT_ADDR(p) ((p) & (1 << 0)) -/* true: HC can do bandwidth negotiation */ -#define HCC_BANDWIDTH_NEG(p) ((p) & (1 << 1)) -/* true: HC uses 64-byte Device Context structures - * FIXME 64-byte context structures aren't supported yet. - */ -#define HCC_64BYTE_CONTEXT(p) ((p) & (1 << 2)) -/* true: HC has port power switches */ -#define HCC_PPC(p) ((p) & (1 << 3)) -/* true: HC has port indicators */ -#define HCS_INDICATOR(p) ((p) & (1 << 4)) -/* true: HC has Light HC Reset Capability */ -#define HCC_LIGHT_RESET(p) ((p) & (1 << 5)) -/* true: HC supports latency tolerance messaging */ -#define HCC_LTC(p) ((p) & (1 << 6)) -/* true: no secondary Stream ID Support */ -#define HCC_NSS(p) ((p) & (1 << 7)) -/* true: HC supports Stopped - Short Packet */ -#define HCC_SPC(p) ((p) & (1 << 9)) -/* true: HC has Contiguous Frame ID Capability */ -#define HCC_CFC(p) ((p) & (1 << 11)) -/* Max size for Primary Stream Arrays - 2^(n+1), where n is bits 12:15 */ +/* HCCPARAMS1 - hcc_params - bitmasks */ +/* bit 0 - 64-bit Addressing Capability */ +#define HCC_64BIT_ADDR BIT(0) +/* bit 1 - BW Negotiation Capability */ +#define HCC_BANDWIDTH_NEG BIT(1) +/* bit 2 - Context Size */ +#define HCC_64BYTE_CONTEXT BIT(2) +#define CTX_SIZE(_hcc) (_hcc & HCC_64BYTE_CONTEXT ? 64 : 32) +/* bit 3 - Port Power Control */ +#define HCC_PPC BIT(3) +/* bit 4 - Port Indicators */ +#define HCS_INDICATOR BIT(4) +/* bit 5 - Light HC Reset Capability */ +#define HCC_LIGHT_RESET BIT(5) +/* bit 6 - Latency Tolerance Messaging Capability */ +#define HCC_LTC BIT(6) +/* bit 7 - No Secondary Stream ID Support */ +#define HCC_NSS BIT(7) +/* bit 8 - Parse All Event Data */ +/* bit 9 - Short Packet Capability */ +#define HCC_SPC BIT(9) +/* bit 10 - Stopped EDTLA Capability */ +/* bit 11 - Contiguous Frame ID Capability */ +#define HCC_CFC BIT(11) +/* bits 15:12 - Max size for Primary Stream Arrays, 2^(n+1) */ #define HCC_MAX_PSA(p) (1 << ((((p) >> 12) & 0xf) + 1)) -/* Extended Capabilities pointer from PCI base - section 5.3.6 */ -#define HCC_EXT_CAPS(p) XHCI_HCC_EXT_CAPS(p) - -#define CTX_SIZE(_hcc) (HCC_64BYTE_CONTEXT(_hcc) ? 64 : 32) +/* bits 31:16 - xHCI Extended Capabilities Pointer, from PCI base: 2^(n) */ +#define HCC_EXT_CAPS(p) (((p) >> 16) & 0xffff) -/* db_off bitmask - bits 0:1 reserved */ -#define DBOFF_MASK (~0x3) +/* DBOFF - db_off - bitmasks */ +/* bits 1:0 - Rsvd */ +/* bits 31:2 - Doorbell Array Offset */ +#define DBOFF_MASK (0xfffffffc) -/* run_regs_off bitmask - bits 0:4 reserved */ +/* RTSOFF - run_regs_off - bitmasks */ +/* bits 4:0 - Rsvd */ +/* bits 31:5 - Runtime Register Space Offse */ #define RTSOFF_MASK (~0x1f) /* HCCPARAMS2 - hcc_params2 - bitmasks */ -/* true: HC supports U3 entry Capability */ -#define HCC2_U3C(p) ((p) & (1 << 0)) -/* true: HC supports Configure endpoint command Max exit latency too large */ -#define HCC2_CMC(p) ((p) & (1 << 1)) -/* true: HC supports Force Save context Capability */ -#define HCC2_FSC(p) ((p) & (1 << 2)) -/* true: HC supports Compliance Transition Capability */ -#define HCC2_CTC(p) ((p) & (1 << 3)) -/* true: HC support Large ESIT payload Capability > 48k */ -#define HCC2_LEC(p) ((p) & (1 << 4)) -/* true: HC support Configuration Information Capability */ -#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)) +/* bit 0 - U3 Entry Capability */ +#define HCC2_U3C BIT(0) +/* bit 1 - Configure Endpoint Command Max Exit Latency Too Large Capability */ +#define HCC2_CMC BIT(1) +/* bit 2 - Force Save Context Capabilitu */ +#define HCC2_FSC BIT(2) +/* bit 3 - Compliance Transition Capability, false: compliance is enabled by default */ +#define HCC2_CTC BIT(3) +/* bit 4 - Large ESIT Payload Capability, true: HC support ESIT payload > 48k */ +#define HCC2_LEC BIT(4) +/* bit 5 - Configuration Information Capability */ +#define HCC2_CIC BIT(5) +/* bit 6 - Extended TBC Capability, true: Isoc burst count > 65535 */ +#define HCC2_ETC BIT(6) +/* bit 7 - Extended TBC TRB Status Capability */ +#define HCC2_ETC_TSC BIT(7) +/* bit 8 - Get/Set Extended Property Capability */ +#define HCC2_GSC BIT(8) +/* bit 9 - Virtualization Based Trusted I/O Capability */ +#define HCC2_VTC BIT(9) +/* bit 10 - Rsvd */ +/* bit 11 - HC support Double BW on a eUSB2 HS ISOC EP */ +#define HCC2_EUSB2_DIC BIT(11) +/* bit 12 - HC support eUSB2V2 capability */ +#define HCC2_E2V2C BIT(12) +/* bits 31:13 - Rsvd */ diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index fd7895b24367..7e6f7d72f03e 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -29,6 +29,12 @@ #include "xhci-trace.h" #include "xhci-dbgcap.h" +static const struct dbc_str dbc_str_default = { + .manufacturer = "Linux Foundation", + .product = "Linux USB Debug Target", + .serial = "0001", +}; + static void dbc_free_ctx(struct device *dev, struct xhci_container_ctx *ctx) { if (!ctx) @@ -52,89 +58,107 @@ static void dbc_ring_free(struct device *dev, struct xhci_ring *ring) kfree(ring); } -static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings) +static void xhci_dbc_init_ep_contexts(struct xhci_dbc *dbc) +{ + struct xhci_ep_ctx *ep_ctx; + unsigned int max_burst; + dma_addr_t deq; + + max_burst = DBC_CTRL_MAXBURST(readl(&dbc->regs->control)); + + /* Populate bulk out endpoint context: */ + ep_ctx = dbc_bulkout_ctx(dbc); + deq = dbc_bulkout_enq(dbc); + ep_ctx->ep_info = 0; + ep_ctx->ep_info2 = dbc_epctx_info2(BULK_OUT_EP, 1024, max_burst); + ep_ctx->deq = cpu_to_le64(deq | dbc->ring_out->cycle_state); + + /* Populate bulk in endpoint context: */ + ep_ctx = dbc_bulkin_ctx(dbc); + deq = dbc_bulkin_enq(dbc); + ep_ctx->ep_info = 0; + ep_ctx->ep_info2 = dbc_epctx_info2(BULK_IN_EP, 1024, max_burst); + ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state); +} + +static u8 get_str_desc_len(const char *desc) +{ + return ((struct usb_string_descriptor *)desc)->bLength; +} + +static u32 dbc_prepare_info_context_str_len(struct dbc_str_descs *descs) +{ + u32 len; + + len = get_str_desc_len(descs->serial); + len <<= 8; + len += get_str_desc_len(descs->product); + len <<= 8; + len += get_str_desc_len(descs->manufacturer); + len <<= 8; + len += get_str_desc_len(descs->string0); + + return len; +} + +static int xhci_dbc_populate_str_desc(char *desc, const char *src) { struct usb_string_descriptor *s_desc; - u32 string_length; + int len; - /* Serial string: */ - s_desc = (struct usb_string_descriptor *)strings->serial; - utf8s_to_utf16s(DBC_STRING_SERIAL, strlen(DBC_STRING_SERIAL), - UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, - DBC_MAX_STRING_LENGTH); + s_desc = (struct usb_string_descriptor *)desc; - s_desc->bLength = (strlen(DBC_STRING_SERIAL) + 1) * 2; + /* len holds number of 2 byte UTF-16 characters */ + len = utf8s_to_utf16s(src, strlen(src), UTF16_LITTLE_ENDIAN, + (wchar_t *)s_desc->wData, USB_MAX_STRING_LEN * 2); + if (len < 0) + return len; + + s_desc->bLength = len * 2 + 2; s_desc->bDescriptorType = USB_DT_STRING; - string_length = s_desc->bLength; - string_length <<= 8; - /* Product string: */ - s_desc = (struct usb_string_descriptor *)strings->product; - utf8s_to_utf16s(DBC_STRING_PRODUCT, strlen(DBC_STRING_PRODUCT), - UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, - DBC_MAX_STRING_LENGTH); + return s_desc->bLength; +} - s_desc->bLength = (strlen(DBC_STRING_PRODUCT) + 1) * 2; - s_desc->bDescriptorType = USB_DT_STRING; - string_length += s_desc->bLength; - string_length <<= 8; +static void xhci_dbc_populate_str_descs(struct dbc_str_descs *str_descs, + struct dbc_str *str) +{ + /* Serial string: */ + xhci_dbc_populate_str_desc(str_descs->serial, str->serial); - /* Manufacture string: */ - s_desc = (struct usb_string_descriptor *)strings->manufacturer; - utf8s_to_utf16s(DBC_STRING_MANUFACTURER, - strlen(DBC_STRING_MANUFACTURER), - UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, - DBC_MAX_STRING_LENGTH); + /* Product string: */ + xhci_dbc_populate_str_desc(str_descs->product, str->product); - s_desc->bLength = (strlen(DBC_STRING_MANUFACTURER) + 1) * 2; - s_desc->bDescriptorType = USB_DT_STRING; - string_length += s_desc->bLength; - string_length <<= 8; + /* Manufacturer string: */ + xhci_dbc_populate_str_desc(str_descs->manufacturer, str->manufacturer); /* String0: */ - strings->string0[0] = 4; - strings->string0[1] = USB_DT_STRING; - strings->string0[2] = 0x09; - strings->string0[3] = 0x04; - string_length += 4; - - return string_length; + str_descs->string0[0] = 4; + str_descs->string0[1] = USB_DT_STRING; + str_descs->string0[2] = 0x09; + str_descs->string0[3] = 0x04; } -static void xhci_dbc_init_contexts(struct xhci_dbc *dbc, u32 string_length) +static void xhci_dbc_init_contexts(struct xhci_dbc *dbc) { struct dbc_info_context *info; - struct xhci_ep_ctx *ep_ctx; u32 dev_info; - dma_addr_t deq, dma; - unsigned int max_burst; + dma_addr_t dma; if (!dbc) return; /* Populate info Context: */ info = (struct dbc_info_context *)dbc->ctx->bytes; - dma = dbc->string_dma; + dma = dbc->str_descs_dma; info->string0 = cpu_to_le64(dma); - info->manufacturer = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH); - info->product = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2); - info->serial = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3); - info->length = cpu_to_le32(string_length); - - /* Populate bulk out endpoint context: */ - ep_ctx = dbc_bulkout_ctx(dbc); - max_burst = DBC_CTRL_MAXBURST(readl(&dbc->regs->control)); - deq = dbc_bulkout_enq(dbc); - ep_ctx->ep_info = 0; - ep_ctx->ep_info2 = dbc_epctx_info2(BULK_OUT_EP, 1024, max_burst); - ep_ctx->deq = cpu_to_le64(deq | dbc->ring_out->cycle_state); + info->manufacturer = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN); + info->product = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 2); + info->serial = cpu_to_le64(dma + USB_MAX_STRING_DESC_LEN * 3); + info->length = cpu_to_le32(dbc_prepare_info_context_str_len(dbc->str_descs)); - /* Populate bulk in endpoint context: */ - ep_ctx = dbc_bulkin_ctx(dbc); - deq = dbc_bulkin_enq(dbc); - ep_ctx->ep_info = 0; - ep_ctx->ep_info2 = dbc_epctx_info2(BULK_IN_EP, 1024, max_burst); - ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state); + /* Populate bulk in and out endpoint contexts: */ + xhci_dbc_init_ep_contexts(dbc); /* Set DbC context and info registers: */ lo_hi_writeq(dbc->ctx->dma, &dbc->regs->dccp); @@ -214,7 +238,7 @@ dbc_alloc_request(struct xhci_dbc *dbc, unsigned int direction, gfp_t flags) if (!dbc) return NULL; - req = kzalloc(sizeof(*req), flags); + req = kzalloc_obj(*req, flags); if (!req) return NULL; @@ -365,7 +389,7 @@ int dbc_ep_queue(struct dbc_request *req) ret = dbc_ep_do_queue(req); spin_unlock_irqrestore(&dbc->lock, flags); - mod_delayed_work(system_wq, &dbc->event_work, 0); + mod_delayed_work(system_percpu_wq, &dbc->event_work, 0); trace_xhci_dbc_queue_request(req); @@ -422,7 +446,7 @@ dbc_alloc_ctx(struct device *dev, gfp_t flags) { struct xhci_container_ctx *ctx; - ctx = kzalloc(sizeof(*ctx), flags); + ctx = kzalloc_obj(*ctx, flags); if (!ctx) return NULL; @@ -436,6 +460,42 @@ dbc_alloc_ctx(struct device *dev, gfp_t flags) return ctx; } +static void xhci_dbc_ring_init(struct xhci_ring *ring) +{ + struct xhci_segment *seg = ring->first_seg; + + /* clear all trbs on ring in case of old ring */ + memset(seg->trbs, 0, TRB_SEGMENT_SIZE); + + /* Only event ring does not use link TRB */ + if (ring->type != TYPE_EVENT) { + union xhci_trb *trb = &seg->trbs[TRBS_PER_SEGMENT - 1]; + + trb->link.segment_ptr = cpu_to_le64(ring->first_seg->dma); + trb->link.control = cpu_to_le32(LINK_TOGGLE | TRB_TYPE(TRB_LINK)); + } + xhci_initialize_ring_info(ring); +} + +static int xhci_dbc_reinit_ep_rings(struct xhci_dbc *dbc) +{ + struct xhci_ring *in_ring = dbc->eps[BULK_IN].ring; + struct xhci_ring *out_ring = dbc->eps[BULK_OUT].ring; + + if (!in_ring || !out_ring || !dbc->ctx) { + dev_warn(dbc->dev, "Can't re-init unallocated endpoints\n"); + return -ENODEV; + } + + xhci_dbc_ring_init(in_ring); + xhci_dbc_ring_init(out_ring); + + /* set ep context enqueue, dequeue, and cycle to initial values */ + xhci_dbc_init_ep_contexts(dbc); + + return 0; +} + static struct xhci_ring * xhci_dbc_ring_alloc(struct device *dev, enum xhci_ring_type type, gfp_t flags) { @@ -443,14 +503,14 @@ xhci_dbc_ring_alloc(struct device *dev, enum xhci_ring_type type, gfp_t flags) struct xhci_segment *seg; dma_addr_t dma; - ring = kzalloc(sizeof(*ring), flags); + ring = kzalloc_obj(*ring, flags); if (!ring) return NULL; ring->num_segs = 1; ring->type = type; - seg = kzalloc(sizeof(*seg), flags); + seg = kzalloc_obj(*seg, flags); if (!seg) goto seg_fail; @@ -464,15 +524,10 @@ xhci_dbc_ring_alloc(struct device *dev, enum xhci_ring_type type, gfp_t flags) seg->dma = dma; - /* Only event ring does not use link TRB */ - if (type != TYPE_EVENT) { - union xhci_trb *trb = &seg->trbs[TRBS_PER_SEGMENT - 1]; - - trb->link.segment_ptr = cpu_to_le64(dma); - trb->link.control = cpu_to_le32(LINK_TOGGLE | TRB_TYPE(TRB_LINK)); - } INIT_LIST_HEAD(&ring->td_list); - xhci_initialize_ring_info(ring); + + xhci_dbc_ring_init(ring); + return ring; dma_fail: kfree(seg); @@ -485,7 +540,6 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags) { int ret; dma_addr_t deq; - u32 string_length; struct device *dev = dbc->dev; /* Allocate various rings for events and transfers: */ @@ -512,11 +566,11 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags) goto ctx_fail; /* Allocate the string table: */ - dbc->string_size = sizeof(*dbc->string); - dbc->string = dma_alloc_coherent(dev, dbc->string_size, - &dbc->string_dma, flags); - if (!dbc->string) - goto string_fail; + dbc->str_descs_size = sizeof(*dbc->str_descs); + dbc->str_descs = dma_alloc_coherent(dev, dbc->str_descs_size, + &dbc->str_descs_dma, flags); + if (!dbc->str_descs) + goto str_descs_fail; /* Setup ERST register: */ writel(dbc->erst.num_entries, &dbc->regs->ersts); @@ -526,16 +580,16 @@ static int xhci_dbc_mem_init(struct xhci_dbc *dbc, gfp_t flags) dbc->ring_evt->dequeue); lo_hi_writeq(deq, &dbc->regs->erdp); - /* Setup strings and contexts: */ - string_length = xhci_dbc_populate_strings(dbc->string); - xhci_dbc_init_contexts(dbc, string_length); + /* Setup string descriptors and contexts: */ + xhci_dbc_populate_str_descs(dbc->str_descs, &dbc->str); + xhci_dbc_init_contexts(dbc); xhci_dbc_eps_init(dbc); dbc->state = DS_INITIALIZED; return 0; -string_fail: +str_descs_fail: dbc_free_ctx(dev, dbc->ctx); dbc->ctx = NULL; ctx_fail: @@ -560,8 +614,8 @@ static void xhci_dbc_mem_cleanup(struct xhci_dbc *dbc) xhci_dbc_eps_exit(dbc); - dma_free_coherent(dbc->dev, dbc->string_size, dbc->string, dbc->string_dma); - dbc->string = NULL; + dma_free_coherent(dbc->dev, dbc->str_descs_size, dbc->str_descs, dbc->str_descs_dma); + dbc->str_descs = NULL; dbc_free_ctx(dbc->dev, dbc->ctx); dbc->ctx = NULL; @@ -637,7 +691,7 @@ static int xhci_dbc_start(struct xhci_dbc *dbc) return ret; } - return mod_delayed_work(system_wq, &dbc->event_work, + return mod_delayed_work(system_percpu_wq, &dbc->event_work, msecs_to_jiffies(dbc->poll_interval)); } @@ -652,6 +706,10 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) case DS_DISABLED: return; case DS_CONFIGURED: + spin_lock(&dbc->lock); + xhci_dbc_flush_requests(dbc); + spin_unlock(&dbc->lock); + if (dbc->driver->disconnect) dbc->driver->disconnect(dbc); break; @@ -823,6 +881,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) { dma_addr_t deq; union xhci_trb *evt; + enum evtreturn ret = EVT_DONE; u32 ctrl, portsc; bool update_erdp = false; @@ -847,7 +906,8 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) dev_info(dbc->dev, "DbC configured\n"); portsc = readl(&dbc->regs->portsc); writel(portsc, &dbc->regs->portsc); - return EVT_GSER; + ret = EVT_GSER; + break; } return EVT_DONE; @@ -859,7 +919,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) dev_info(dbc->dev, "DbC cable unplugged\n"); dbc->state = DS_ENABLED; xhci_dbc_flush_requests(dbc); - + xhci_dbc_reinit_ep_rings(dbc); return EVT_DISC; } @@ -869,7 +929,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) writel(portsc, &dbc->regs->portsc); dbc->state = DS_ENABLED; xhci_dbc_flush_requests(dbc); - + xhci_dbc_reinit_ep_rings(dbc); return EVT_DISC; } @@ -909,6 +969,8 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) break; case TRB_TYPE(TRB_TRANSFER): dbc_handle_xfer_event(dbc, evt); + if (ret != EVT_GSER) + ret = EVT_XFER_DONE; break; default: break; @@ -927,7 +989,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) lo_hi_writeq(deq, &dbc->regs->erdp); } - return EVT_DONE; + return ret; } static void xhci_dbc_handle_events(struct work_struct *work) @@ -936,6 +998,7 @@ static void xhci_dbc_handle_events(struct work_struct *work) struct xhci_dbc *dbc; unsigned long flags; unsigned int poll_interval; + unsigned long busypoll_timelimit; dbc = container_of(to_delayed_work(work), struct xhci_dbc, event_work); poll_interval = dbc->poll_interval; @@ -954,17 +1017,27 @@ static void xhci_dbc_handle_events(struct work_struct *work) dbc->driver->disconnect(dbc); break; case EVT_DONE: - /* set fast poll rate if there are pending data transfers */ + /* + * Set fast poll rate if there are pending out transfers, or + * a transfer was recently processed + */ + busypoll_timelimit = dbc->xfer_timestamp + + msecs_to_jiffies(DBC_XFER_INACTIVITY_TIMEOUT); + if (!list_empty(&dbc->eps[BULK_OUT].list_pending) || - !list_empty(&dbc->eps[BULK_IN].list_pending)) + time_is_after_jiffies(busypoll_timelimit)) poll_interval = 0; break; + case EVT_XFER_DONE: + dbc->xfer_timestamp = jiffies; + poll_interval = 0; + break; default: dev_info(dbc->dev, "stop handling dbc events\n"); return; } - mod_delayed_work(system_wq, &dbc->event_work, + mod_delayed_work(system_percpu_wq, &dbc->event_work, msecs_to_jiffies(poll_interval)); } @@ -1137,6 +1210,108 @@ static ssize_t dbc_bcdDevice_store(struct device *dev, return size; } +static ssize_t dbc_manufacturer_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + + return sysfs_emit(buf, "%s\n", dbc->str.manufacturer); +} + +static ssize_t dbc_manufacturer_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + size_t len; + + if (dbc->state != DS_DISABLED) + return -EBUSY; + + len = strcspn(buf, "\n"); + if (!len) + return -EINVAL; + + if (len > USB_MAX_STRING_LEN) + return -E2BIG; + + memcpy(dbc->str.manufacturer, buf, len); + dbc->str.manufacturer[len] = '\0'; + + return size; +} + +static ssize_t dbc_product_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + + return sysfs_emit(buf, "%s\n", dbc->str.product); +} + +static ssize_t dbc_product_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + size_t len; + + if (dbc->state != DS_DISABLED) + return -EBUSY; + + len = strcspn(buf, "\n"); + if (!len) + return -EINVAL; + + if (len > USB_MAX_STRING_LEN) + return -E2BIG; + + memcpy(dbc->str.product, buf, len); + dbc->str.product[len] = '\0'; + + return size; +} + +static ssize_t dbc_serial_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + + return sysfs_emit(buf, "%s\n", dbc->str.serial); +} + +static ssize_t dbc_serial_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct xhci_hcd *xhci = hcd_to_xhci(dev_get_drvdata(dev)); + struct xhci_dbc *dbc = xhci->dbc; + size_t len; + + if (dbc->state != DS_DISABLED) + return -EBUSY; + + len = strcspn(buf, "\n"); + if (!len) + return -EINVAL; + + if (len > USB_MAX_STRING_LEN) + return -E2BIG; + + memcpy(dbc->str.serial, buf, len); + dbc->str.serial[len] = '\0'; + + return size; +} + static ssize_t dbc_bInterfaceProtocol_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1215,7 +1390,7 @@ static ssize_t dbc_poll_interval_ms_store(struct device *dev, dbc->poll_interval = value; - mod_delayed_work(system_wq, &dbc->event_work, 0); + mod_delayed_work(system_percpu_wq, &dbc->event_work, 0); return size; } @@ -1224,6 +1399,9 @@ static DEVICE_ATTR_RW(dbc); static DEVICE_ATTR_RW(dbc_idVendor); static DEVICE_ATTR_RW(dbc_idProduct); static DEVICE_ATTR_RW(dbc_bcdDevice); +static DEVICE_ATTR_RW(dbc_serial); +static DEVICE_ATTR_RW(dbc_product); +static DEVICE_ATTR_RW(dbc_manufacturer); static DEVICE_ATTR_RW(dbc_bInterfaceProtocol); static DEVICE_ATTR_RW(dbc_poll_interval_ms); @@ -1232,6 +1410,9 @@ static struct attribute *dbc_dev_attrs[] = { &dev_attr_dbc_idVendor.attr, &dev_attr_dbc_idProduct.attr, &dev_attr_dbc_bcdDevice.attr, + &dev_attr_dbc_serial.attr, + &dev_attr_dbc_product.attr, + &dev_attr_dbc_manufacturer.attr, &dev_attr_dbc_bInterfaceProtocol.attr, &dev_attr_dbc_poll_interval_ms.attr, NULL @@ -1244,7 +1425,7 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver * struct xhci_dbc *dbc; int ret; - dbc = kzalloc(sizeof(*dbc), GFP_KERNEL); + dbc = kzalloc_obj(*dbc); if (!dbc) return NULL; @@ -1257,6 +1438,9 @@ xhci_alloc_dbc(struct device *dev, void __iomem *base, const struct dbc_driver * dbc->bInterfaceProtocol = DBC_PROTOCOL; dbc->poll_interval = DBC_POLL_INTERVAL_DEFAULT; + /* initialize serial, product and manufacturer with default values */ + dbc->str = dbc_str_default; + if (readl(&dbc->regs->control) & DBC_CTRL_DBC_ENABLE) goto err; @@ -1333,8 +1517,15 @@ int xhci_dbc_suspend(struct xhci_hcd *xhci) if (!dbc) return 0; - if (dbc->state == DS_CONFIGURED) + switch (dbc->state) { + case DS_ENABLED: + case DS_CONNECTED: + case DS_CONFIGURED: dbc->resume_required = 1; + break; + default: + break; + } xhci_dbc_stop(dbc); diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index 9dc8f4d8077c..20ae4e7617f2 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -47,10 +47,6 @@ struct dbc_info_context { #define DBC_DOOR_BELL_TARGET(p) (((p) & 0xff) << 8) #define DBC_MAX_PACKET 1024 -#define DBC_MAX_STRING_LENGTH 64 -#define DBC_STRING_MANUFACTURER "Linux Foundation" -#define DBC_STRING_PRODUCT "Linux USB Debug Target" -#define DBC_STRING_SERIAL "0001" #define DBC_CONTEXT_SIZE 64 /* @@ -63,11 +59,31 @@ struct dbc_info_context { #define DBC_PORTSC_LINK_CHANGE BIT(22) #define DBC_PORTSC_CONFIG_CHANGE BIT(23) +/* + * The maximum length of a string descriptor is 255, because the bLength + * field in the usb_string_descriptor struct is __u8. In practice the + * maximum length is 254, because a string descriptor consists of a 2 byte + * header followed by UTF-16 characters (2 bytes each). This allows for + * only 126 characters (code points) in the string, which is where + * USB_MAX_STRING_LEN comes from. + */ +#define USB_MAX_STRING_DESC_LEN 254 + struct dbc_str_descs { - char string0[DBC_MAX_STRING_LENGTH]; - char manufacturer[DBC_MAX_STRING_LENGTH]; - char product[DBC_MAX_STRING_LENGTH]; - char serial[DBC_MAX_STRING_LENGTH]; + char string0[USB_MAX_STRING_DESC_LEN]; + char manufacturer[USB_MAX_STRING_DESC_LEN]; + char product[USB_MAX_STRING_DESC_LEN]; + char serial[USB_MAX_STRING_DESC_LEN]; +}; + +/* + * NULL terminated UTF-8 strings used to create UTF-16 strings + * (with maxiumum USB_MAX_STRING_LEN 2 byte characters). + */ +struct dbc_str { + char manufacturer[USB_MAX_STRING_LEN+1]; + char product[USB_MAX_STRING_LEN+1]; + char serial[USB_MAX_STRING_LEN+1]; }; #define DBC_PROTOCOL 1 /* GNU Remote Debug Command */ @@ -96,6 +112,7 @@ struct dbc_ep { #define DBC_WRITE_BUF_SIZE 8192 #define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */ #define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */ +#define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */ /* * Private structure for DbC hardware state: */ @@ -113,6 +130,7 @@ struct dbc_port { unsigned int tx_boundary; bool registered; + bool tx_running; }; struct dbc_driver { @@ -131,9 +149,10 @@ struct xhci_dbc { struct xhci_erst erst; struct xhci_container_ctx *ctx; - struct dbc_str_descs *string; - dma_addr_t string_dma; - size_t string_size; + struct dbc_str_descs *str_descs; + dma_addr_t str_descs_dma; + size_t str_descs_size; + struct dbc_str str; u16 idVendor; u16 idProduct; u16 bcdDevice; @@ -142,6 +161,7 @@ struct xhci_dbc { enum dbc_state state; struct delayed_work event_work; unsigned int poll_interval; /* ms */ + unsigned long xfer_timestamp; unsigned resume_required:1; struct dbc_ep eps[2]; @@ -187,6 +207,7 @@ struct dbc_request { enum evtreturn { EVT_ERR = -1, EVT_DONE, + EVT_XFER_DONE, EVT_GSER, EVT_DISC, }; diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c index 60ed753c85bb..2e7384c6b6ec 100644 --- a/drivers/usb/host/xhci-dbgtty.c +++ b/drivers/usb/host/xhci-dbgtty.c @@ -47,7 +47,7 @@ dbc_kfifo_to_req(struct dbc_port *port, char *packet) return len; } -static int dbc_start_tx(struct dbc_port *port) +static int dbc_do_start_tx(struct dbc_port *port) __releases(&port->port_lock) __acquires(&port->port_lock) { @@ -57,6 +57,8 @@ static int dbc_start_tx(struct dbc_port *port) bool do_tty_wake = false; struct list_head *pool = &port->write_pool; + port->tx_running = true; + while (!list_empty(pool)) { req = list_entry(pool->next, struct dbc_request, list_pool); len = dbc_kfifo_to_req(port, req->buf); @@ -77,12 +79,25 @@ static int dbc_start_tx(struct dbc_port *port) } } + port->tx_running = false; + if (do_tty_wake && port->port.tty) tty_wakeup(port->port.tty); return status; } +/* must be called with port->port_lock held */ +static int dbc_start_tx(struct dbc_port *port) +{ + lockdep_assert_held(&port->port_lock); + + if (port->tx_running) + return -EBUSY; + + return dbc_do_start_tx(port); +} + static void dbc_start_rx(struct dbc_port *port) __releases(&port->port_lock) __acquires(&port->port_lock) @@ -535,6 +550,12 @@ static void xhci_dbc_tty_unregister_device(struct xhci_dbc *dbc) if (!port->registered) return; + /* + * Hang up the TTY. This wakes up any blocked + * writers and causes subsequent writes to fail. + */ + tty_port_tty_vhangup(&port->port); + tty_unregister_device(dbc_tty_driver, port->minor); xhci_dbc_tty_exit_port(port); port->registered = false; @@ -563,7 +584,7 @@ int xhci_dbc_tty_probe(struct device *dev, void __iomem *base, struct xhci_hcd * if (!dbc_tty_driver) return -ENODEV; - port = kzalloc(sizeof(*port), GFP_KERNEL); + port = kzalloc_obj(*port); if (!port) return -ENOMEM; @@ -617,6 +638,7 @@ int dbc_tty_init(void) dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL; dbc_tty_driver->init_termios = tty_std_termios; + dbc_tty_driver->init_termios.c_lflag &= ~ECHO; dbc_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; dbc_tty_driver->init_termios.c_ispeed = 9600; diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index 1f5ef174abea..d07276192256 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -87,7 +87,7 @@ static struct xhci_regset *xhci_debugfs_alloc_regset(struct xhci_hcd *xhci) { struct xhci_regset *regset; - regset = kzalloc(sizeof(*regset), GFP_KERNEL); + regset = kzalloc_obj(*regset); if (!regset) return NULL; @@ -329,7 +329,7 @@ static int xhci_portsc_show(struct seq_file *s, void *unused) u32 portsc; char str[XHCI_MSG_MAX]; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); seq_printf(s, "%s\n", xhci_decode_portsc(str, portsc)); return 0; @@ -355,11 +355,11 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf, if (!strncmp(buf, "compliance", 10)) { /* If CTC is clear, compliance is enabled by default */ - if (!HCC2_CTC(xhci->hcc_params2)) + if (!(xhci->hcc_params2 & HCC2_CTC)) return count; spin_lock_irqsave(&xhci->lock, flags); /* compliance mode can only be enabled on ports in RxDetect */ - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); if ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT) { spin_unlock_irqrestore(&xhci->lock, flags); return -EPERM; @@ -367,7 +367,7 @@ static ssize_t xhci_port_write(struct file *file, const char __user *ubuf, portsc = xhci_port_state_to_neutral(portsc); portsc &= ~PORT_PLS_MASK; portsc |= PORT_LINK_STROBE | XDEV_COMP_MODE; - writel(portsc, port->addr); + xhci_portsc_writel(port, portsc); spin_unlock_irqrestore(&xhci->lock, flags); } else { return -EINVAL; @@ -383,6 +383,47 @@ static const struct file_operations port_fops = { .release = single_release, }; +static int xhci_portli_show(struct seq_file *s, void *unused) +{ + struct xhci_port *port = s->private; + struct xhci_hcd *xhci; + u32 portli; + + portli = readl(&port->port_reg->portli); + + /* port without protocol capability isn't added to a roothub */ + if (!port->rhub) { + seq_printf(s, "0x%08x\n", portli); + return 0; + } + + xhci = hcd_to_xhci(port->rhub->hcd); + + /* PORTLI fields are valid if port is a USB3 or eUSB2V2 port */ + if (port->rhub == &xhci->usb3_rhub) + seq_printf(s, "0x%08x LEC=%u RLC=%u TLC=%u\n", portli, + PORT_LEC(portli), PORT_RX_LANES(portli), PORT_TX_LANES(portli)); + else if (xhci->hcc_params2 & HCC2_E2V2C) + seq_printf(s, "0x%08x RDR=%u TDR=%u\n", portli, + PORTLI_RDR(portli), PORTLI_TDR(portli)); + else + seq_printf(s, "0x%08x RsvdP\n", portli); + + return 0; +} + +static int xhci_portli_open(struct inode *inode, struct file *file) +{ + return single_open(file, xhci_portli_show, inode->i_private); +} + +static const struct file_operations portli_fops = { + .open = xhci_portli_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static void xhci_debugfs_create_files(struct xhci_hcd *xhci, struct xhci_file_map *files, size_t nentries, void *data, @@ -435,7 +476,7 @@ void xhci_debugfs_create_endpoint(struct xhci_hcd *xhci, if (spriv->eps[ep_index]) return; - epriv = kzalloc(sizeof(*epriv), GFP_KERNEL); + epriv = kzalloc_obj(*epriv); if (!epriv) return; @@ -575,7 +616,7 @@ void xhci_debugfs_create_slot(struct xhci_hcd *xhci, int slot_id) struct xhci_slot_priv *priv; struct xhci_virt_device *dev = xhci->devs[slot_id]; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return; @@ -613,22 +654,126 @@ void xhci_debugfs_remove_slot(struct xhci_hcd *xhci, int slot_id) static void xhci_debugfs_create_ports(struct xhci_hcd *xhci, struct dentry *parent) { - unsigned int num_ports; char port_name[8]; struct xhci_port *port; struct dentry *dir; - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - parent = debugfs_create_dir("ports", parent); - while (num_ports--) { - scnprintf(port_name, sizeof(port_name), "port%02d", - num_ports + 1); + for (int i = 0; i < xhci->max_ports; i++) { + scnprintf(port_name, sizeof(port_name), "port%02d", i + 1); dir = debugfs_create_dir(port_name, parent); - port = &xhci->hw_ports[num_ports]; + port = &xhci->hw_ports[i]; debugfs_create_file("portsc", 0644, dir, port, &port_fops); + debugfs_create_file("portli", 0444, dir, port, &portli_fops); + } +} + +static int xhci_port_bw_show(struct xhci_hcd *xhci, u8 dev_speed, + struct seq_file *s) +{ + unsigned int i; + int ret; + struct xhci_container_ctx *ctx; + struct usb_hcd *hcd = xhci_to_hcd(xhci); + struct device *dev = hcd->self.controller; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) + return ret; + + ctx = xhci_alloc_port_bw_ctx(xhci, 0); + if (!ctx) { + pm_runtime_put_sync(dev); + return -ENOMEM; + } + + /* get roothub port bandwidth */ + ret = xhci_get_port_bandwidth(xhci, ctx, dev_speed); + if (ret) + goto err_out; + + /* print all roothub ports available bandwidth + * refer to xhci rev1_2 protocol 6.2.6 , byte 0 is reserved + */ + for (i = 1; i <= xhci->max_ports; i++) + seq_printf(s, "port[%d] available bw: %d%%.\n", i, + ctx->bytes[i]); +err_out: + if (ret == -EIO) { + seq_puts(s, "Get Port Bandwidth failed\n"); + ret = 0; } + pm_runtime_put_sync(dev); + xhci_free_port_bw_ctx(xhci, ctx); + return ret; +} + +static int xhci_ss_bw_show(struct seq_file *s, void *unused) +{ + int ret; + struct xhci_hcd *xhci = (struct xhci_hcd *)s->private; + + ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_SS), s); + return ret; +} + +static int xhci_hs_bw_show(struct seq_file *s, void *unused) +{ + int ret; + struct xhci_hcd *xhci = (struct xhci_hcd *)s->private; + + ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_HS), s); + return ret; +} + +static int xhci_fs_bw_show(struct seq_file *s, void *unused) +{ + int ret; + struct xhci_hcd *xhci = (struct xhci_hcd *)s->private; + + ret = xhci_port_bw_show(xhci, DEV_PORT_SPEED(XDEV_FS), s); + return ret; +} + +static struct xhci_file_map bw_context_files[] = { + {"SS_BW", xhci_ss_bw_show, }, + {"HS_BW", xhci_hs_bw_show, }, + {"FS_BW", xhci_fs_bw_show, }, +}; + +static int bw_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(bw_context_files); i++) { + f_map = &bw_context_files[i]; + + if (strcmp(f_map->name, file_name) == 0) + break; + } + + return single_open(file, f_map->show, inode->i_private); +} + +static const struct file_operations bw_fops = { + .open = bw_context_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void xhci_debugfs_create_bandwidth(struct xhci_hcd *xhci, + struct dentry *parent) +{ + parent = debugfs_create_dir("port_bandwidth", parent); + + xhci_debugfs_create_files(xhci, bw_context_files, + ARRAY_SIZE(bw_context_files), + xhci, + parent, &bw_fops); } void xhci_debugfs_init(struct xhci_hcd *xhci) @@ -681,6 +826,8 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) xhci->debugfs_slots = debugfs_create_dir("devices", xhci->debugfs_root); xhci_debugfs_create_ports(xhci, xhci->debugfs_root); + + xhci_debugfs_create_bandwidth(xhci, xhci->debugfs_root); } void xhci_debugfs_exit(struct xhci_hcd *xhci) diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c index 8a7d46dae62c..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 = { diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 69c278b64084..bacd0ddd0d09 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -110,7 +110,7 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, ss_cap->bU2DevExitLat = 0; /* set later */ reg = readl(&xhci->cap_regs->hcc_params); - if (HCC_LTC(reg)) + if (reg & HCC_LTC) ss_cap->bmAttributes |= USB_LTM_SUPPORT; if ((xhci->quirks & XHCI_LPM_SUPPORT)) { @@ -263,7 +263,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, desc->bNbrPorts = ports; temp = 0; /* Bits 1:0 - support per-port power switching, or power always on */ - if (HCC_PPC(xhci->hcc_params)) + if (xhci->hcc_params & HCC_PPC) temp |= HUB_CHAR_INDV_PORT_LPSM; else temp |= HUB_CHAR_NO_LPSM; @@ -299,7 +299,7 @@ static void xhci_usb2_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, */ memset(port_removable, 0, sizeof(port_removable)); for (i = 0; i < ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); /* If a device is removable, PORTSC reports a 0, same as in the * hub descriptor DeviceRemovable bits. */ @@ -356,7 +356,7 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, port_removable = 0; /* bit 0 is reserved, bit 1 is for port 1, etc. */ for (i = 0; i < ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); if (portsc & PORT_DEV_REMOVE) port_removable |= 1 << (i + 1); } @@ -375,11 +375,11 @@ static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, } -static unsigned int xhci_port_speed(unsigned int port_status) +static unsigned int xhci_port_speed(int portsc) { - if (DEV_LOWSPEED(port_status)) + if (DEV_LOWSPEED(portsc)) return USB_PORT_STAT_LOW_SPEED; - if (DEV_HIGHSPEED(port_status)) + if (DEV_HIGHSPEED(portsc)) return USB_PORT_STAT_HIGH_SPEED; /* * FIXME: Yes, we should check for full speed, but the core uses that as @@ -429,9 +429,9 @@ static unsigned int xhci_port_speed(unsigned int port_status) /** * xhci_port_state_to_neutral() - Clean up read portsc value back into writeable - * @state: u32 port value read from portsc register to be cleanup up + * @portsc: u32 port value read from portsc register to be cleanup up * - * Given a port state, this function returns a value that would result in the + * Given a portsc, this function returns a value that would result in the * port being in the same state, if the value was written to the port status * control register. * Save Read Only (RO) bits and save read/write bits where @@ -442,10 +442,10 @@ static unsigned int xhci_port_speed(unsigned int port_status) * changing port state. */ -u32 xhci_port_state_to_neutral(u32 state) +u32 xhci_port_state_to_neutral(u32 portsc) { /* Save read-only status and port state */ - return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS); + return (portsc & XHCI_PORT_RO) | (portsc & XHCI_PORT_RWS); } EXPORT_SYMBOL_GPL(xhci_port_state_to_neutral); @@ -566,19 +566,19 @@ static void xhci_disable_port(struct xhci_hcd *xhci, struct xhci_port *port) return; } - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); portsc = xhci_port_state_to_neutral(portsc); /* Write 1 to disable the port */ - writel(portsc | PORT_PE, port->addr); + xhci_portsc_writel(port, portsc | PORT_PE); - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n", hcd->self.busnum, port->hcd_portnum + 1, portsc); } -static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, - u16 wIndex, __le32 __iomem *addr, u32 port_status) +static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, struct xhci_port *port, + u32 portsc) { char *port_change_bit; u32 status; @@ -621,11 +621,11 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, return; } /* Change bits are all write 1 to clear */ - writel(port_status | status, addr); - port_status = readl(addr); + xhci_portsc_writel(port, portsc | status); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n", - wIndex + 1, port_change_bit, port_status); + port->hcd_portnum + 1, port_change_bit, portsc); } struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd) @@ -650,7 +650,7 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, u32 temp; hcd = port->rhub->hcd; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n", hcd->self.busnum, port->hcd_portnum + 1, on ? "ON" : "OFF", temp); @@ -659,11 +659,11 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, if (on) { /* Power on */ - writel(temp | PORT_POWER, port->addr); - readl(port->addr); + xhci_portsc_writel(port, temp | PORT_POWER); + xhci_portsc_readl(port); } else { /* Power off */ - writel(temp & ~PORT_POWER, port->addr); + xhci_portsc_writel(port, temp & ~PORT_POWER); } spin_unlock_irqrestore(&xhci->lock, *flags); @@ -675,24 +675,23 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct xhci_port *port, spin_lock_irqsave(&xhci->lock, *flags); } -static void xhci_port_set_test_mode(struct xhci_hcd *xhci, - u16 test_mode, u16 wIndex) +static void xhci_port_set_test_mode(struct xhci_hcd *xhci, u16 test_mode, int portnum) { u32 temp; struct xhci_port *port; /* xhci only supports test mode for usb2 ports */ - port = xhci->usb2_rhub.ports[wIndex]; - temp = readl(port->addr + PORTPMSC); + port = xhci->usb2_rhub.ports[portnum]; + temp = readl(&port->port_reg->portpmsc); temp |= test_mode << PORT_TEST_MODE_SHIFT; - writel(temp, port->addr + PORTPMSC); + writel(temp, &port->port_reg->portpmsc); xhci->test_mode = test_mode; if (test_mode == USB_TEST_FORCE_ENABLE) xhci_start(xhci); } -static int xhci_enter_test_mode(struct xhci_hcd *xhci, - u16 test_mode, u16 wIndex, unsigned long *flags) +static int xhci_enter_test_mode(struct xhci_hcd *xhci, u16 test_mode, int portnum, + unsigned long *flags) __must_hold(&xhci->lock) { int i, retval; @@ -700,12 +699,11 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, /* Disable all Device Slots */ xhci_dbg(xhci, "Disable all slots\n"); spin_unlock_irqrestore(&xhci->lock, *flags); - for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + for (i = 1; i <= xhci->max_slots; i++) { if (!xhci->devs[i]) continue; - retval = xhci_disable_slot(xhci, i); - xhci_free_virt_device(xhci, i); + retval = xhci_disable_and_free_slot(xhci, i); if (retval) xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n", i, retval); @@ -727,10 +725,8 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, /* Disable runtime PM for test mode */ pm_runtime_forbid(xhci_to_hcd(xhci)->self.controller); /* Set PORTPMSC.PTC field to enter selected test mode */ - /* Port is selected by wIndex. port_id = wIndex + 1 */ - xhci_dbg(xhci, "Enter Test Mode: %d, Port_id=%d\n", - test_mode, wIndex + 1); - xhci_port_set_test_mode(xhci, test_mode, wIndex); + xhci_dbg(xhci, "Enter Test Mode: %u, Port_id=%d\n", test_mode, portnum + 1); + xhci_port_set_test_mode(xhci, test_mode, portnum); return retval; } @@ -802,11 +798,11 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, u32 temp; u32 portsc; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); temp = xhci_port_state_to_neutral(portsc); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | link_state; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); xhci_dbg(xhci, "Set port %d-%d link state, portsc: 0x%x, write 0x%x", port->rhub->hcd->self.busnum, port->hcd_portnum + 1, @@ -818,7 +814,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, { u32 temp; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); temp = xhci_port_state_to_neutral(temp); if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT) @@ -836,7 +832,7 @@ static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, else temp &= ~PORT_WKOC_E; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); } /* Test and clear port RWC bit */ @@ -845,62 +841,46 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, struct xhci_port *port, { u32 temp; - temp = readl(port->addr); + temp = xhci_portsc_readl(port); if (temp & port_bit) { temp = xhci_port_state_to_neutral(temp); temp |= port_bit; - writel(temp, port->addr); + xhci_portsc_writel(port, temp); } } /* Updates Link Status for super Speed port */ -static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, - u32 *status, u32 status_reg) +static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, u32 *status, u32 portsc) { - u32 pls = status_reg & PORT_PLS_MASK; + u32 pls = portsc & PORT_PLS_MASK; - /* When the CAS bit is set then warm reset - * should be performed on port + /* + * CAS indicates that a warm reset is required, it may be set in any + * link state and is only present on roothubs. */ - if (status_reg & PORT_CAS) { - /* The CAS bit can be set while the port is - * in any link state. - * Only roothubs have CAS bit, so we - * pretend to be in compliance mode - * unless we're already in compliance - * or the inactive state. + if (portsc & PORT_CAS) { + /* + * If not already in Compliance or Inactive state, + * report Compliance Mode so the hub logic triggers a warm reset. */ - if (pls != USB_SS_PORT_LS_COMP_MOD && - pls != USB_SS_PORT_LS_SS_INACTIVE) { + if (pls != XDEV_COMP_MODE && pls != XDEV_INACTIVE) pls = USB_SS_PORT_LS_COMP_MOD; - } - /* Return also connection bit - - * hub state machine resets port - * when this bit is set. - */ - pls |= USB_PORT_STAT_CONNECTION; - } else { + + /* Signal a connection change to force a reset */ + *status |= USB_PORT_STAT_CONNECTION; + } else if (pls == XDEV_RESUME) { /* - * Resume state is an xHCI internal state. Do not report it to - * usb core, instead, pretend to be U3, thus usb core knows - * it's not ready for transfer. + * Resume is an internal xHCI-only state and must not be exposed + * to usbcore. Report it as U3 so transfers are blocked. */ - if (pls == XDEV_RESUME) { - *status |= USB_SS_PORT_LS_U3; - return; - } - + pls = USB_SS_PORT_LS_U3; + } else if (pls == XDEV_COMP_MODE) { /* - * If CAS bit isn't set but the Port is already at - * Compliance Mode, fake a connection so the USB core - * notices the Compliance state and resets the port. - * This resolves an issue generated by the SN65LVPE502CP - * in which sometimes the port enters compliance mode - * caused by a delay on the host-device negotiation. + * Some hardware may enter Compliance Mode without CAS. + * Fake a connection event so usbcore notices and resets the port. */ - if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && - (pls == USB_SS_PORT_LS_COMP_MOD)) - pls |= USB_PORT_STAT_CONNECTION; + if (xhci->quirks & XHCI_COMP_MODE_QUIRK) + *status |= USB_PORT_STAT_CONNECTION; } /* update status field */ @@ -914,19 +894,18 @@ static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, * the compliance mode timer is deleted. A port won't enter * compliance mode if it has previously entered U0. */ -static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, - u16 wIndex) +static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 portsc, int portnum) { u32 all_ports_seen_u0 = ((1 << xhci->usb3_rhub.num_ports) - 1); - bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0); + bool port_in_u0 = ((portsc & PORT_PLS_MASK) == XDEV_U0); if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK)) return; if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) { - xhci->port_status_u0 |= 1 << wIndex; + xhci->port_status_u0 |= 1 << portnum; 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, @@ -942,12 +921,12 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, struct xhci_bus_state *bus_state; struct xhci_hcd *xhci; struct usb_hcd *hcd; - u32 wIndex; + int portnum; hcd = port->rhub->hcd; bus_state = &port->rhub->bus_state; xhci = hcd_to_xhci(hcd); - wIndex = port->hcd_portnum; + portnum = port->hcd_portnum; if ((portsc & PORT_RESET) || !(portsc & PORT_PE)) { return -EINVAL; @@ -955,7 +934,7 @@ 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 initiated resume? */ - if (test_bit(wIndex, &bus_state->resuming_ports)) { + if (test_bit(portnum, &bus_state->resuming_ports)) { /* Host initiated resume doesn't time the resume * signalling using resume_done[]. * It manually sets RESUME state, sleeps 20ms @@ -969,20 +948,20 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, unsigned long timeout = jiffies + msecs_to_jiffies(USB_RESUME_TIMEOUT); - set_bit(wIndex, &bus_state->resuming_ports); + set_bit(portnum, &bus_state->resuming_ports); port->resume_timestamp = timeout; mod_timer(&hcd->rh_timer, timeout); - usb_hcd_start_port_resume(&hcd->self, wIndex); + usb_hcd_start_port_resume(&hcd->self, portnum); } /* Has resume been signalled for USB_RESUME_TIME yet? */ } else if (time_after_eq(jiffies, port->resume_timestamp)) { int time_left; xhci_dbg(xhci, "resume USB2 port %d-%d\n", - hcd->self.busnum, wIndex + 1); + hcd->self.busnum, portnum + 1); port->resume_timestamp = 0; - clear_bit(wIndex, &bus_state->resuming_ports); + clear_bit(portnum, &bus_state->resuming_ports); reinit_completion(&port->rexit_done); port->rexit_active = true; @@ -1003,10 +982,10 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, } xhci_ring_device(xhci, port->slot_id); } else { - int port_status = readl(port->addr); + int port_status = xhci_portsc_readl(port); xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", - hcd->self.busnum, wIndex + 1, port_status); + hcd->self.busnum, portnum + 1, port_status); /* * keep rexit_active set if U0 transition failed so we * know to report PORT_STAT_SUSPEND status back to @@ -1015,21 +994,21 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, */ } - usb_hcd_end_port_resume(&hcd->self, wIndex); - bus_state->port_c_suspend |= 1 << wIndex; - bus_state->suspended_ports &= ~(1 << wIndex); + usb_hcd_end_port_resume(&hcd->self, portnum); + bus_state->port_c_suspend |= 1 << portnum; + bus_state->suspended_ports &= ~(1 << portnum); } return 0; } -static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li) +static u32 xhci_get_ext_port_status(u32 portsc, u32 port_li) { u32 ext_stat = 0; int speed_id; /* only support rx and tx lane counts of 1 in usb3.1 spec */ - speed_id = DEV_PORT_SPEED(raw_port_status); + speed_id = DEV_PORT_SPEED(portsc); ext_stat |= speed_id; /* bits 3:0, RX speed id */ ext_stat |= speed_id << 4; /* bits 7:4, TX speed id */ @@ -1154,10 +1133,8 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, * - Stop the Synopsys redriver Compliance Mode polling. * - Drop and reacquire the xHCI lock, in order to wait for port resume. */ -static u32 xhci_get_port_status(struct usb_hcd *hcd, - struct xhci_bus_state *bus_state, - u16 wIndex, u32 raw_port_status, - unsigned long *flags) +static u32 xhci_get_port_status(struct usb_hcd *hcd, struct xhci_bus_state *bus_state, + int portnum, u32 portsc, unsigned long *flags) __releases(&xhci->lock) __acquires(&xhci->lock) { @@ -1166,38 +1143,37 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, struct xhci_port *port; rhub = xhci_get_rhub(hcd); - port = rhub->ports[wIndex]; + port = rhub->ports[portnum]; /* common wPortChange bits */ - if (raw_port_status & PORT_CSC) + if (portsc & PORT_CSC) status |= USB_PORT_STAT_C_CONNECTION << 16; - if (raw_port_status & PORT_PEC) + if (portsc & PORT_PEC) status |= USB_PORT_STAT_C_ENABLE << 16; - if ((raw_port_status & PORT_OCC)) + if (portsc & PORT_OCC) status |= USB_PORT_STAT_C_OVERCURRENT << 16; - if ((raw_port_status & PORT_RC)) + if (portsc & PORT_RC) status |= USB_PORT_STAT_C_RESET << 16; /* common wPortStatus bits */ - if (raw_port_status & PORT_CONNECT) { + if (portsc & PORT_CONNECT) { status |= USB_PORT_STAT_CONNECTION; - status |= xhci_port_speed(raw_port_status); + status |= xhci_port_speed(portsc); } - if (raw_port_status & PORT_PE) + if (portsc & PORT_PE) status |= USB_PORT_STAT_ENABLE; - if (raw_port_status & PORT_OC) + if (portsc & PORT_OC) status |= USB_PORT_STAT_OVERCURRENT; - if (raw_port_status & PORT_RESET) + if (portsc & PORT_RESET) status |= USB_PORT_STAT_RESET; /* USB2 and USB3 specific bits, including Port Link State */ if (hcd->speed >= HCD_USB3) - xhci_get_usb3_port_status(port, &status, raw_port_status); + xhci_get_usb3_port_status(port, &status, portsc); else - xhci_get_usb2_port_status(port, &status, raw_port_status, - flags); + xhci_get_usb2_port_status(port, &status, portsc, flags); - if (bus_state->port_c_suspend & (1 << wIndex)) + if (bus_state->port_c_suspend & (1 << portnum)) status |= USB_PORT_STAT_C_SUSPEND << 16; return status; @@ -1209,23 +1185,23 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports; unsigned long flags; - u32 temp, status; + u32 portsc, portpmsc, status; int retval = 0; struct xhci_bus_state *bus_state; - u16 link_state = 0; - u16 wake_mask = 0; - u16 timeout = 0; - u16 test_mode = 0; + u16 link_state; + u16 wake_mask; + u8 timeout; + u8 test_mode; + u8 desc_type; struct xhci_hub *rhub; struct xhci_port **ports; struct xhci_port *port; - int portnum1; + int portnum; rhub = xhci_get_rhub(hcd); ports = rhub->ports; max_ports = rhub->num_ports; bus_state = &rhub->bus_state; - portnum1 = wIndex & 0xff; spin_lock_irqsave(&xhci->lock, flags); switch (typeReq) { @@ -1234,13 +1210,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, memset(buf, 0, 4); break; case GetHubDescriptor: + desc_type = (wValue & 0xff00) >> 8; /* Check to make sure userspace is asking for the USB 3.0 hub * descriptor for the USB 3.0 roothub. If not, we stall the * endpoint, like external hubs do. */ if (hcd->speed >= HCD_USB3 && - (wLength < USB_DT_SS_HUB_SIZE || - wValue != (USB_DT_SS_HUB << 8))) { + (wLength < USB_DT_SS_HUB_SIZE || desc_type != USB_DT_SS_HUB)) { xhci_dbg(xhci, "Wrong hub descriptor type for " "USB 3.0 roothub.\n"); goto error; @@ -1249,7 +1225,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, (struct usb_hub_descriptor *) buf); break; case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - if ((wValue & 0xff00) != (USB_DT_BOS << 8)) + desc_type = (wValue & 0xff00) >> 8; + if (desc_type != USB_DT_BOS) goto error; if (hcd->speed < HCD_USB3) @@ -1259,29 +1236,28 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, spin_unlock_irqrestore(&xhci->lock, flags); return retval; case GetPortStatus: - if (!portnum1 || portnum1 > max_ports) + portnum = (wIndex & 0xff) - 1; + if (!in_range(portnum, 0, max_ports)) goto error; - wIndex--; - port = ports[portnum1 - 1]; - temp = readl(port->addr); - if (temp == ~(u32)0) { + port = ports[portnum]; + portsc = xhci_portsc_readl(port); + if (portsc == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; break; } - trace_xhci_get_port_status(port, temp); - status = xhci_get_port_status(hcd, bus_state, wIndex, temp, - &flags); + trace_xhci_get_port_status(port, portsc); + status = xhci_get_port_status(hcd, bus_state, portnum, portsc, &flags); if (status == 0xffffffff) goto error; xhci_dbg(xhci, "Get port status %d-%d read: 0x%x, return 0x%x", - hcd->self.busnum, portnum1, temp, status); + hcd->self.busnum, portnum + 1, portsc, status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); /* if USB 3.1 extended port status return additional 4 bytes */ - if (wValue == 0x02) { + if (wValue == HUB_EXT_PORT_STATUS) { u32 port_li; if (hcd->speed < HCD_USB31 || wLength != 8) { @@ -1289,39 +1265,29 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, retval = -EINVAL; break; } - port_li = readl(port->addr + PORTLI); - status = xhci_get_ext_port_status(temp, port_li); + port_li = readl(&port->port_reg->portli); + status = xhci_get_ext_port_status(portsc, port_li); put_unaligned_le32(status, &buf[4]); } break; case SetPortFeature: - if (wValue == USB_PORT_FEAT_LINK_STATE) - link_state = (wIndex & 0xff00) >> 3; - if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK) - wake_mask = wIndex & 0xff00; - if (wValue == USB_PORT_FEAT_TEST) - test_mode = (wIndex & 0xff00) >> 8; - /* The MSB of wIndex is the U1/U2 timeout */ - timeout = (wIndex & 0xff00) >> 8; - - wIndex &= 0xff; - if (!portnum1 || portnum1 > max_ports) + portnum = (wIndex & 0xff) - 1; + if (!in_range(portnum, 0, max_ports)) goto error; - port = ports[portnum1 - 1]; - wIndex--; - temp = readl(port->addr); - if (temp == ~(u32)0) { + port = ports[portnum]; + portsc = xhci_portsc_readl(port); + if (portsc == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; break; } - temp = xhci_port_state_to_neutral(temp); + portsc = xhci_port_state_to_neutral(portsc); /* FIXME: What new port features do we need to support? */ switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = readl(port->addr); - if ((temp & PORT_PLS_MASK) != XDEV_U0) { + portsc = xhci_portsc_readl(port); + if ((portsc & PORT_PLS_MASK) != XDEV_U0) { /* Resume the port to U0 first */ xhci_set_link_state(xhci, port, XDEV_U0); spin_unlock_irqrestore(&xhci->lock, flags); @@ -1332,11 +1298,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ - temp = readl(port->addr); - if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) - || (temp & PORT_PLS_MASK) >= XDEV_U3) { + portsc = xhci_portsc_readl(port); + if ((portsc & PORT_PE) == 0 || (portsc & PORT_RESET) || + (portsc & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n", - hcd->self.busnum, portnum1); + hcd->self.busnum, portnum + 1); goto error; } @@ -1355,34 +1321,35 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, msleep(10); /* wait device to enter */ spin_lock_irqsave(&xhci->lock, flags); - temp = readl(port->addr); - bus_state->suspended_ports |= 1 << wIndex; + portsc = xhci_portsc_readl(port); + bus_state->suspended_ports |= 1 << portnum; break; case USB_PORT_FEAT_LINK_STATE: - temp = readl(port->addr); + link_state = (wIndex & 0xff00) >> 3; + portsc = xhci_portsc_readl(port); /* Disable port */ if (link_state == USB_SS_PORT_LS_SS_DISABLED) { xhci_dbg(xhci, "Disable port %d-%d\n", - hcd->self.busnum, portnum1); - temp = xhci_port_state_to_neutral(temp); + hcd->self.busnum, portnum + 1); + portsc = xhci_port_state_to_neutral(portsc); /* * Clear all change bits, so that we get a new * connection event. */ - temp |= PORT_CSC | PORT_PEC | PORT_WRC | - PORT_OCC | PORT_RC | PORT_PLC | - PORT_CEC; - writel(temp | PORT_PE, port->addr); - temp = readl(port->addr); + portsc |= PORT_CSC | PORT_PEC | PORT_WRC | + PORT_OCC | PORT_RC | PORT_PLC | + PORT_CEC; + xhci_portsc_writel(port, portsc | PORT_PE); + portsc = xhci_portsc_readl(port); break; } /* Put link in RxDetect (enable port) */ if (link_state == USB_SS_PORT_LS_RX_DETECT) { xhci_dbg(xhci, "Enable port %d-%d\n", - hcd->self.busnum, portnum1); - xhci_set_link_state(xhci, port, link_state); - temp = readl(port->addr); + hcd->self.busnum, portnum + 1); + xhci_set_link_state(xhci, port, XDEV_RXDETECT); + portsc = xhci_portsc_readl(port); break; } @@ -1401,32 +1368,32 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * automatically entered as on 1.0 and prior. */ if (link_state == USB_SS_PORT_LS_COMP_MOD) { - if (!HCC2_CTC(xhci->hcc_params2)) { + if (!(xhci->hcc_params2 & HCC2_CTC)) { xhci_dbg(xhci, "CTC flag is 0, port already supports entering compliance mode\n"); break; } - if ((temp & PORT_CONNECT)) { + if ((portsc & PORT_CONNECT)) { xhci_warn(xhci, "Can't set compliance mode when port is connected\n"); goto error; } xhci_dbg(xhci, "Enable compliance mode transition for port %d-%d\n", - hcd->self.busnum, portnum1); - xhci_set_link_state(xhci, port, link_state); + hcd->self.busnum, portnum + 1); + xhci_set_link_state(xhci, port, XDEV_COMP_MODE); - temp = readl(port->addr); + portsc = xhci_portsc_readl(port); break; } /* Port must be enabled */ - if (!(temp & PORT_PE)) { + if (!(portsc & PORT_PE)) { retval = -ENODEV; break; } /* Can't set port link state above '3' (U3) */ if (link_state > USB_SS_PORT_LS_U3) { xhci_warn(xhci, "Cannot set port %d-%d link state %d\n", - hcd->self.busnum, portnum1, link_state); + hcd->self.busnum, portnum + 1, link_state); goto error; } @@ -1438,7 +1405,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * completion */ if (link_state == USB_SS_PORT_LS_U0) { - u32 pls = temp & PORT_PLS_MASK; + u32 pls = portsc & PORT_PLS_MASK; bool wait_u0 = false; /* already in U0 */ @@ -1451,7 +1418,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, reinit_completion(&port->u3exit_done); } if (pls <= XDEV_U3) /* U1, U2, U3 */ - xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U0); + xhci_set_link_state(xhci, port, XDEV_U0); if (!wait_u0) { if (pls > XDEV_U3) goto error; @@ -1461,9 +1428,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (!wait_for_completion_timeout(&port->u3exit_done, msecs_to_jiffies(500))) xhci_dbg(xhci, "missing U0 port change event for port %d-%d\n", - hcd->self.busnum, portnum1); + hcd->self.busnum, portnum + 1); spin_lock_irqsave(&xhci->lock, flags); - temp = readl(port->addr); + portsc = xhci_portsc_readl(port); break; } @@ -1477,17 +1444,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_stop_device(xhci, port->slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); } - xhci_set_link_state(xhci, port, USB_SS_PORT_LS_U3); + xhci_set_link_state(xhci, port, XDEV_U3); spin_unlock_irqrestore(&xhci->lock, flags); while (retries--) { usleep_range(4000, 8000); - temp = readl(port->addr); - if ((temp & PORT_PLS_MASK) == XDEV_U3) + portsc = xhci_portsc_readl(port); + if ((portsc & PORT_PLS_MASK) == XDEV_U3) break; } spin_lock_irqsave(&xhci->lock, flags); - temp = readl(port->addr); - bus_state->suspended_ports |= 1 << wIndex; + portsc = xhci_portsc_readl(port); + bus_state->suspended_ports |= 1 << portnum; } break; case USB_PORT_FEAT_POWER: @@ -1500,93 +1467,98 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_set_port_power(xhci, port, true, &flags); break; case USB_PORT_FEAT_RESET: - temp = (temp | PORT_RESET); - writel(temp, port->addr); + portsc |= PORT_RESET; + xhci_portsc_writel(port, portsc); - temp = readl(port->addr); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "set port reset, actual port %d-%d status = 0x%x\n", - hcd->self.busnum, portnum1, temp); + hcd->self.busnum, portnum + 1, portsc); break; case USB_PORT_FEAT_REMOTE_WAKE_MASK: + wake_mask = wIndex & 0xff00; xhci_set_remote_wake_mask(xhci, port, wake_mask); - temp = readl(port->addr); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "set port remote wake mask, actual port %d-%d status = 0x%x\n", - hcd->self.busnum, portnum1, temp); + hcd->self.busnum, portnum + 1, portsc); break; case USB_PORT_FEAT_BH_PORT_RESET: - temp |= PORT_WR; - writel(temp, port->addr); - temp = readl(port->addr); + portsc |= PORT_WR; + xhci_portsc_writel(port, portsc); + portsc = xhci_portsc_readl(port); break; case USB_PORT_FEAT_U1_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(port->addr + PORTPMSC); - temp &= ~PORT_U1_TIMEOUT_MASK; - temp |= PORT_U1_TIMEOUT(timeout); - writel(temp, port->addr + PORTPMSC); + + timeout = (wIndex & 0xff00) >> 8; + portpmsc = readl(&port->port_reg->portpmsc); + portpmsc &= ~PORT_U1_TIMEOUT_MASK; + portpmsc |= PORT_U1_TIMEOUT(timeout); + writel(portpmsc, &port->port_reg->portpmsc); break; case USB_PORT_FEAT_U2_TIMEOUT: if (hcd->speed < HCD_USB3) goto error; - temp = readl(port->addr + PORTPMSC); - temp &= ~PORT_U2_TIMEOUT_MASK; - temp |= PORT_U2_TIMEOUT(timeout); - writel(temp, port->addr + PORTPMSC); + + timeout = (wIndex & 0xff00) >> 8; + portpmsc = readl(&port->port_reg->portpmsc); + portpmsc &= ~PORT_U2_TIMEOUT_MASK; + portpmsc |= PORT_U2_TIMEOUT(timeout); + writel(portpmsc, &port->port_reg->portpmsc); break; case USB_PORT_FEAT_TEST: /* 4.19.6 Port Test Modes (USB2 Test Mode) */ if (hcd->speed != HCD_USB2) goto error; + + test_mode = (wIndex & 0xff00) >> 8; if (test_mode > USB_TEST_FORCE_ENABLE || test_mode < USB_TEST_J) goto error; - retval = xhci_enter_test_mode(xhci, test_mode, wIndex, - &flags); + retval = xhci_enter_test_mode(xhci, test_mode, portnum, &flags); break; default: goto error; } /* unblock any posted writes */ - temp = readl(port->addr); + portsc = xhci_portsc_readl(port); break; case ClearPortFeature: - if (!portnum1 || portnum1 > max_ports) + portnum = (wIndex & 0xff) - 1; + if (!in_range(portnum, 0, max_ports)) goto error; - port = ports[portnum1 - 1]; - - wIndex--; - temp = readl(port->addr); - if (temp == ~(u32)0) { + port = ports[portnum]; + portsc = xhci_portsc_readl(port); + if (portsc == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; break; } /* FIXME: What new port features do we need to support? */ - temp = xhci_port_state_to_neutral(temp); + portsc = xhci_port_state_to_neutral(portsc); switch (wValue) { case USB_PORT_FEAT_SUSPEND: - temp = readl(port->addr); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n"); - xhci_dbg(xhci, "PORTSC %04x\n", temp); - if (temp & PORT_RESET) + xhci_dbg(xhci, "PORTSC %04x\n", portsc); + if (portsc & PORT_RESET) goto error; - if ((temp & PORT_PLS_MASK) == XDEV_U3) { - if ((temp & PORT_PE) == 0) + if ((portsc & PORT_PLS_MASK) == XDEV_U3) { + if ((portsc & PORT_PE) == 0) goto error; - set_bit(wIndex, &bus_state->resuming_ports); - usb_hcd_start_port_resume(&hcd->self, wIndex); + set_bit(portnum, &bus_state->resuming_ports); + usb_hcd_start_port_resume(&hcd->self, portnum); xhci_set_link_state(xhci, port, XDEV_RESUME); spin_unlock_irqrestore(&xhci->lock, flags); msleep(USB_RESUME_TIMEOUT); spin_lock_irqsave(&xhci->lock, flags); xhci_set_link_state(xhci, port, XDEV_U0); - clear_bit(wIndex, &bus_state->resuming_ports); - usb_hcd_end_port_resume(&hcd->self, wIndex); + clear_bit(portnum, &bus_state->resuming_ports); + usb_hcd_end_port_resume(&hcd->self, portnum); } - bus_state->port_c_suspend |= 1 << wIndex; + bus_state->port_c_suspend |= 1 << portnum; if (!port->slot_id) { xhci_dbg(xhci, "slot_id is zero\n"); @@ -1595,7 +1567,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_ring_device(xhci, port->slot_id); break; case USB_PORT_FEAT_C_SUSPEND: - bus_state->port_c_suspend &= ~(1 << wIndex); + bus_state->port_c_suspend &= ~(1 << portnum); fallthrough; case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_BH_PORT_RESET: @@ -1604,8 +1576,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_PORT_LINK_STATE: case USB_PORT_FEAT_C_PORT_CONFIG_ERROR: - xhci_clear_port_change_bit(xhci, wValue, wIndex, - port->addr, temp); + xhci_clear_port_change_bit(xhci, wValue, port, portsc); break; case USB_PORT_FEAT_ENABLE: xhci_disable_port(xhci, port); @@ -1672,7 +1643,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) * SS devices are only visible to roothub after link training completes. * Keep polling roothubs for a grace period after xHC start */ - if (xhci->run_graceperiod) { + if (hcd->speed >= HCD_USB3 && xhci->run_graceperiod) { if (time_before(jiffies, xhci->run_graceperiod)) status = 1; else @@ -1683,7 +1654,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) /* For each port, did anything change? If so, set that bit in buf. */ for (i = 0; i < max_ports; i++) { - temp = readl(ports[i]->addr); + temp = xhci_portsc_readl(ports[i]); if (temp == ~(u32)0) { xhci_hc_died(xhci); retval = -ENODEV; @@ -1752,7 +1723,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) u32 t1, t2; int retries = 10; retry: - t1 = readl(ports[port_index]->addr); + t1 = xhci_portsc_readl(ports[port_index]); t2 = xhci_port_state_to_neutral(t1); portsc_buf[port_index] = 0; @@ -1830,7 +1801,7 @@ retry: spin_lock_irqsave(&xhci->lock, flags); } } - writel(portsc_buf[port_index], ports[port_index]->addr); + xhci_portsc_writel(ports[port_index], portsc_buf[port_index]); } hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10); @@ -1851,7 +1822,7 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port) { u32 portsc; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); /* if any of these are set we are not stuck */ if (portsc & (PORT_CONNECT | PORT_CAS)) @@ -1864,9 +1835,9 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port) /* clear wakeup/change bits, and do a warm port reset */ portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); portsc |= PORT_WR; - writel(portsc, port->addr); + xhci_portsc_writel(port, portsc); /* flush write */ - readl(port->addr); + xhci_portsc_readl(port); return true; } @@ -1878,9 +1849,10 @@ int xhci_bus_resume(struct usb_hcd *hcd) int max_ports, port_index; int sret; u32 next_state; - u32 temp, portsc; + u32 portsc; struct xhci_hub *rhub; struct xhci_port **ports; + bool disabled_irq = false; rhub = xhci_get_rhub(hcd); ports = rhub->ports; @@ -1896,20 +1868,23 @@ int xhci_bus_resume(struct usb_hcd *hcd) return -ESHUTDOWN; } - /* delay the irqs */ - temp = readl(&xhci->op_regs->command); - temp &= ~CMD_EIE; - writel(temp, &xhci->op_regs->command); - /* bus specific resume for ports we suspended at bus_suspend */ - if (hcd->speed >= HCD_USB3) + if (hcd->speed >= HCD_USB3) { next_state = XDEV_U0; - else + } else { next_state = XDEV_RESUME; - + if (bus_state->bus_suspended) { + /* + * prevent port event interrupts from interfering + * with usb2 port resume process + */ + xhci_disable_interrupter(xhci, xhci->interrupters[0]); + disabled_irq = true; + } + } port_index = max_ports; while (port_index--) { - portsc = readl(ports[port_index]->addr); + portsc = xhci_portsc_readl(ports[port_index]); /* warm reset CAS limited ports stuck in polling/compliance */ if ((xhci->quirks & XHCI_MISSING_CAS) && @@ -1939,7 +1914,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) } /* disable wake for all ports, write new link state if needed */ portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); - writel(portsc, ports[port_index]->addr); + xhci_portsc_writel(ports[port_index], portsc); } /* USB2 specific resume signaling delay and U0 link state transition */ @@ -1960,7 +1935,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) /* poll for U0 link state complete, both USB2 and USB3 */ for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) { - sret = xhci_handshake(ports[port_index]->addr, PORT_PLC, + sret = xhci_handshake(&ports[port_index]->port_reg->portsc, PORT_PLC, PORT_PLC, 10 * 1000); if (sret) { xhci_warn(xhci, "port %d-%d resume PLC timeout\n", @@ -1974,11 +1949,9 @@ int xhci_bus_resume(struct usb_hcd *hcd) (void) readl(&xhci->op_regs->command); bus_state->next_statechange = jiffies + msecs_to_jiffies(5); - /* re-enable irqs */ - temp = readl(&xhci->op_regs->command); - temp |= CMD_EIE; - writel(temp, &xhci->op_regs->command); - temp = readl(&xhci->op_regs->command); + /* re-enable interrupter */ + if (disabled_irq) + xhci_enable_interrupter(xhci->interrupters[0]); spin_unlock_irqrestore(&xhci->lock, flags); return 0; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index fdf0c1008225..997fe90f54e5 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -129,6 +129,13 @@ static void xhci_initialize_ring_segments(struct xhci_hcd *xhci, struct xhci_rin ring->last_seg->trbs[TRBS_PER_SEGMENT - 1].link.control |= cpu_to_le32(LINK_TOGGLE); } +void xhci_ring_init(struct xhci_hcd *xhci, struct xhci_ring *ring) +{ + xhci_initialize_ring_segments(xhci, ring); + xhci_initialize_ring_info(ring); + trace_xhci_ring_alloc(ring); +} + /* * Link the src ring segments to the dst ring. * Set Toggle Cycle for the new ring if needed. @@ -389,9 +396,6 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, if (ret) goto fail; - xhci_initialize_ring_segments(xhci, ring); - xhci_initialize_ring_info(ring); - trace_xhci_ring_alloc(ring); return ring; fail: @@ -463,7 +467,7 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, return NULL; ctx->type = type; - ctx->size = HCC_64BYTE_CONTEXT(xhci->hcc_params) ? 2048 : 1024; + ctx->size = xhci->hcc_params & HCC_64BYTE_CONTEXT ? 2048 : 1024; if (type == XHCI_CTX_TYPE_INPUT) ctx->size += CTX_SIZE(xhci->hcc_params); @@ -484,6 +488,35 @@ void xhci_free_container_ctx(struct xhci_hcd *xhci, kfree(ctx); } +struct xhci_container_ctx *xhci_alloc_port_bw_ctx(struct xhci_hcd *xhci, + gfp_t flags) +{ + struct xhci_container_ctx *ctx; + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + + ctx = kzalloc_node(sizeof(*ctx), flags, dev_to_node(dev)); + if (!ctx) + return NULL; + + ctx->size = GET_PORT_BW_ARRAY_SIZE; + + ctx->bytes = dma_pool_zalloc(xhci->port_bw_pool, flags, &ctx->dma); + if (!ctx->bytes) { + kfree(ctx); + return NULL; + } + return ctx; +} + +void xhci_free_port_bw_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx) +{ + if (!ctx) + return; + dma_pool_free(xhci->port_bw_pool, ctx->bytes, ctx->dma); + kfree(ctx); +} + struct xhci_input_control_ctx *xhci_get_input_control_ctx( struct xhci_container_ctx *ctx) { @@ -639,6 +672,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, cur_ring = stream_info->stream_rings[cur_stream]; if (!cur_ring) goto cleanup_rings; + + xhci_ring_init(xhci, cur_ring); cur_ring->stream_id = cur_stream; cur_ring->trb_address_map = &stream_info->trb_address_map; /* Set deq ptr, cycle bit, and stream context type */ @@ -836,21 +871,20 @@ free_tts: * will be manipulated by the configure endpoint, allocate device, or update * hub functions while this function is removing the TT entries from the list. */ -void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) +void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, + int slot_id) { - struct xhci_virt_device *dev; int i; int old_active_eps = 0; /* Slot ID 0 is reserved */ - if (slot_id == 0 || !xhci->devs[slot_id]) + if (slot_id == 0 || !dev) return; - dev = xhci->devs[slot_id]; - - xhci->dcbaa->dev_context_ptrs[slot_id] = 0; - if (!dev) - return; + /* If device ctx array still points to _this_ device, clear it */ + if (dev->out_ctx && + xhci->dcbaa->dev_context_ptrs[slot_id] == cpu_to_le64(dev->out_ctx->dma)) + xhci->dcbaa->dev_context_ptrs[slot_id] = 0; trace_xhci_free_virt_device(dev); @@ -891,8 +925,9 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) dev->udev->slot_id = 0; if (dev->rhub_port && dev->rhub_port->slot_id == slot_id) dev->rhub_port->slot_id = 0; - kfree(xhci->devs[slot_id]); - xhci->devs[slot_id] = NULL; + if (xhci->devs[slot_id] == dev) + xhci->devs[slot_id] = NULL; + kfree(dev); } /* @@ -901,7 +936,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) * that tt_info, then free the child first. Recursive. * We can't rely on udev at this point to find child-parent relationships. */ -static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) +void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id) { struct xhci_virt_device *vdev; struct list_head *tt_list_head; @@ -922,7 +957,7 @@ static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_i /* is this a hub device that added a tt_info to the tts list */ if (tt_info->slot_id == slot_id) { /* are any devices using this tt_info? */ - for (i = 1; i < HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + for (i = 1; i < xhci->max_slots; i++) { vdev = xhci->devs[i]; if (vdev && (vdev->tt_info == tt_info)) xhci_free_virt_devices_depth_first( @@ -933,7 +968,7 @@ static void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_i out: /* we are now at a leaf device */ xhci_debugfs_remove_slot(xhci, slot_id); - xhci_free_virt_device(xhci, slot_id); + xhci_free_virt_device(xhci, xhci->devs[slot_id], slot_id); } int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, @@ -948,7 +983,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, return 0; } - dev = kzalloc(sizeof(*dev), flags); + dev = kzalloc_obj(*dev, flags); if (!dev) return 0; @@ -959,14 +994,14 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, if (!dev->out_ctx) goto fail; - xhci_dbg(xhci, "Slot %d output ctx = 0x%pad (dma)\n", slot_id, &dev->out_ctx->dma); + xhci_dbg(xhci, "Slot %d output ctx = %pad (dma)\n", slot_id, &dev->out_ctx->dma); /* Allocate the (input) device context for address device command */ dev->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, flags); if (!dev->in_ctx) goto fail; - xhci_dbg(xhci, "Slot %d input ctx = 0x%pad (dma)\n", slot_id, &dev->in_ctx->dma); + xhci_dbg(xhci, "Slot %d input ctx = %pad (dma)\n", slot_id, &dev->in_ctx->dma); /* Initialize the cancellation and bandwidth list for each ep */ for (i = 0; i < 31; i++) { @@ -982,6 +1017,8 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, if (!dev->eps[0].ring) goto fail; + xhci_ring_init(xhci, dev->eps[0].ring); + dev->udev = udev; /* Point to output device context in dcbaa. */ @@ -1166,6 +1203,8 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud ep0_ctx->deq = cpu_to_le64(dev->eps[0].ring->first_seg->dma | dev->eps[0].ring->cycle_state); + ep0_ctx->tx_info = cpu_to_le32(EP_AVG_TRB_LENGTH(8)); + trace_xhci_setup_addressable_virt_device(dev); /* Steps 7 and 8 were done in xhci_alloc_virt_device() */ @@ -1299,18 +1338,33 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, return interval; } -/* The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps. +/* + * xHCs without LEC use the "Mult" field in the endpoint context for SuperSpeed + * isoc eps, and High speed isoc eps that support bandwidth doubling. Standard * High speed endpoint descriptors can define "the number of additional * transaction opportunities per microframe", but that goes in the Max Burst * endpoint context field. */ -static u32 xhci_get_endpoint_mult(struct usb_device *udev, - struct usb_host_endpoint *ep) +static u32 xhci_get_endpoint_mult(struct xhci_hcd *xhci, + struct usb_device *udev, + struct usb_host_endpoint *ep) { - if (udev->speed < USB_SPEED_SUPER || - !usb_endpoint_xfer_isoc(&ep->desc)) - return 0; - return ep->ss_ep_comp.bmAttributes; + bool lec; + + /* xHCI 1.1 with LEC set does not use mult field, except intel eUSB2 */ + lec = xhci->hci_version > 0x100 && (xhci->hcc_params2 & HCC2_LEC); + + /* eUSB2 double isoc bw devices are the only USB2 devices using mult */ + if (usb_endpoint_is_hs_isoc_double(udev, ep) && + (!lec || xhci->quirks & XHCI_INTEL_HOST)) + return 1; + + /* SuperSpeed isoc transfers on hosts without LEC uses mult field */ + if (udev->speed >= USB_SPEED_SUPER && + usb_endpoint_xfer_isoc(&ep->desc) && !lec) + return ep->ss_ep_comp.bmAttributes; + + return 0; } static u32 xhci_get_endpoint_max_burst(struct usb_device *udev, @@ -1322,8 +1376,16 @@ static u32 xhci_get_endpoint_max_burst(struct usb_device *udev, if (udev->speed == USB_SPEED_HIGH && (usb_endpoint_xfer_isoc(&ep->desc) || - usb_endpoint_xfer_int(&ep->desc))) + usb_endpoint_xfer_int(&ep->desc))) { + /* + * USB 2 Isochronous Double IN Bandwidth ECN uses fixed burst + * size and max packets bits 12:11 are invalid. + */ + if (usb_endpoint_is_hs_isoc_double(udev, ep)) + return 2; + return usb_endpoint_maxp_mult(&ep->desc) - 1; + } return 0; } @@ -1347,36 +1409,6 @@ static u32 xhci_get_endpoint_type(struct usb_host_endpoint *ep) return 0; } -/* Return the maximum endpoint service interval time (ESIT) payload. - * Basically, this is the maxpacket size, multiplied by the burst size - * and mult size. - */ -static u32 xhci_get_max_esit_payload(struct usb_device *udev, - struct usb_host_endpoint *ep) -{ - int max_burst; - int max_packet; - - /* Only applies for interrupt or isochronous endpoints */ - if (usb_endpoint_xfer_control(&ep->desc) || - usb_endpoint_xfer_bulk(&ep->desc)) - return 0; - - /* SuperSpeedPlus Isoc ep sending over 48k per esit */ - if ((udev->speed >= USB_SPEED_SUPER_PLUS) && - USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) - return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval); - - /* SuperSpeed or SuperSpeedPlus Isoc ep with less than 48k per esit */ - if (udev->speed >= USB_SPEED_SUPER) - return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); - - max_packet = usb_endpoint_maxp(&ep->desc); - max_burst = usb_endpoint_maxp_mult(&ep->desc); - /* A 0 in max burst means 1 transfer per ESIT */ - return max_packet * max_burst; -} - /* Set up an endpoint with one ring segment. Do not allocate stream rings. * Drivers will have to call usb_alloc_streams() to do that. */ @@ -1408,18 +1440,28 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, ring_type = usb_endpoint_type(&ep->desc); + /* Ensure host supports double isoc bandwidth for eUSB2 devices */ + if (usb_endpoint_is_hs_isoc_double(udev, ep) && !(xhci->hcc_params2 & HCC2_EUSB2_DIC)) { + dev_dbg(&udev->dev, "Double Isoc Bandwidth not supported by xhci\n"); + return -EINVAL; + } + /* * Get values to fill the endpoint context, mostly from ep descriptor. * The average TRB buffer lengt for bulk endpoints is unclear as we * have no clue on scatter gather list entry size. For Isoc and Int, * set it to max available. See xHCI 1.1 spec 4.14.1.1 for details. */ - max_esit_payload = xhci_get_max_esit_payload(udev, ep); + max_esit_payload = usb_endpoint_max_periodic_payload(udev, ep); interval = xhci_get_endpoint_interval(udev, ep); /* Periodic endpoint bInterval limit quirk */ if (usb_endpoint_xfer_int(&ep->desc) || usb_endpoint_xfer_isoc(&ep->desc)) { + if ((xhci->quirks & XHCI_LIMIT_ENDPOINT_INTERVAL_9) && + interval >= 9) { + interval = 8; + } if ((xhci->quirks & XHCI_LIMIT_ENDPOINT_INTERVAL_7) && udev->speed >= USB_SPEED_HIGH && interval >= 7) { @@ -1427,8 +1469,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, } } - mult = xhci_get_endpoint_mult(udev, ep); - max_packet = usb_endpoint_maxp(&ep->desc); + mult = xhci_get_endpoint_mult(xhci, udev, ep); + max_packet = xhci_usb_endpoint_maxp(udev, ep); max_burst = xhci_get_endpoint_max_burst(udev, ep); avg_trb_len = max_esit_payload; @@ -1449,9 +1491,6 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, /* xHCI 1.0 and 1.1 indicates that ctrl ep avg TRB Length should be 8 */ if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version >= 0x100) avg_trb_len = 8; - /* xhci 1.1 with LEC support doesn't use mult field, use RsvdZ */ - if ((xhci->hci_version > 0x100) && HCC2_LEC(xhci->hcc_params2)) - mult = 0; /* Set up the endpoint ring */ virt_dev->eps[ep_index].new_ring = @@ -1461,6 +1500,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, virt_dev->eps[ep_index].skip = false; ep_ring = virt_dev->eps[ep_index].new_ring; + xhci_ring_init(xhci, ep_ring); /* Fill the endpoint context */ ep_ctx->ep_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) | @@ -1802,10 +1842,10 @@ xhci_remove_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) */ if (ir->ir_set) { tmp = readl(&ir->ir_set->erst_size); - tmp &= ERST_SIZE_MASK; + tmp &= ~ERST_SIZE_MASK; writel(tmp, &ir->ir_set->erst_size); - xhci_write_64(xhci, ERST_EHB, &ir->ir_set->erst_dequeue); + xhci_update_erst_dequeue(xhci, ir, true); } } @@ -1848,6 +1888,11 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup return; } + /* + * Cleanup secondary interrupter to ensure there are no pending events. + * This also updates event ring dequeue pointer back to the start. + */ + xhci_skip_sec_intr_events(xhci, ir->event_ring, ir); intr_num = ir->intr_num; xhci_remove_interrupter(xhci, ir); @@ -1859,10 +1904,36 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup } EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter); +/* Cleanup roothub bandwidth data */ +void xhci_rh_bw_cleanup(struct xhci_hcd *xhci) +{ + struct xhci_root_port_bw_info *rh_bw; + struct xhci_tt_bw_info *tt_info, *tt_next; + struct list_head *eps, *ep, *ep_next; + + for (int i = 0; i < xhci->max_ports; i++) { + rh_bw = &xhci->rh_bw[i]; + + /* Clear and free all TT bandwidth entries */ + list_for_each_entry_safe(tt_info, tt_next, &rh_bw->tts, tt_list) { + list_del(&tt_info->tt_list); + kfree(tt_info); + } + + /* Clear per-interval endpoint lists */ + for (int j = 0; j < XHCI_MAX_INTERVAL; j++) { + eps = &rh_bw->bw_table.interval_bw[j].endpoints; + + list_for_each_safe(ep, ep_next, eps) + list_del_init(ep); + } + } +} + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - int i, j, num_ports; + int i; cancel_delayed_work_sync(&xhci->cmd_timer); @@ -1881,17 +1952,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed command ring"); xhci_cleanup_command_queue(xhci); - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - for (i = 0; i < num_ports && xhci->rh_bw; i++) { - struct xhci_interval_bw_table *bwt = &xhci->rh_bw[i].bw_table; - for (j = 0; j < XHCI_MAX_INTERVAL; j++) { - struct list_head *ep = &bwt->interval_bw[j].endpoints; - while (!list_empty(ep)) - list_del_init(ep->next); - } - } - - for (i = HCS_MAX_SLOTS(xhci->hcs_params1); i > 0; i--) + for (i = xhci->max_slots; i > 0; i--) xhci_free_virt_devices_depth_first(xhci, i); dma_pool_destroy(xhci->segment_pool); @@ -1907,6 +1968,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed small stream array pool"); + dma_pool_destroy(xhci->port_bw_pool); + xhci->port_bw_pool = NULL; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Freed xhci port bw array pool"); + dma_pool_destroy(xhci->medium_streams_pool); xhci->medium_streams_pool = NULL; xhci_dbg_trace(xhci, trace_xhci_dbg_init, @@ -1919,18 +1985,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) scratchpad_free(xhci); - if (!xhci->rh_bw) - goto no_bw; - - for (i = 0; i < num_ports; i++) { - struct xhci_tt_bw_info *tt, *n; - list_for_each_entry_safe(tt, n, &xhci->rh_bw[i].tts, tt_list) { - list_del(&tt->tt_list); - kfree(tt); - } - } + if (xhci->rh_bw) + xhci_rh_bw_cleanup(xhci); -no_bw: xhci->cmd_ring_reserved_trbs = 0; xhci->usb2_rhub.num_ports = 0; xhci->usb3_rhub.num_ports = 0; @@ -1952,8 +2009,6 @@ no_bw: xhci->port_caps = NULL; 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; } @@ -2110,21 +2165,34 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, /* FIXME: Should we disable ports not in the Extended Capabilities? */ } -static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, - struct xhci_hub *rhub, gfp_t flags) +static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, struct xhci_hub *rhub, + unsigned int max_ports, gfp_t flags) { int port_index = 0; int i; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - if (!rhub->num_ports) + if (!rhub->num_ports) { + xhci_info(xhci, "USB%u root hub has no ports\n", rhub->maj_rev); return; + } + + /* + * Place limits on the number of roothub ports so that the hub + * descriptors aren't longer than the USB core will allocate. + */ + if (rhub->num_ports > max_ports) { + xhci->usb3_rhub.num_ports = max_ports; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Limiting USB%u root hub ports to %u", + rhub->maj_rev, max_ports); + } + rhub->ports = kcalloc_node(rhub->num_ports, sizeof(*rhub->ports), flags, dev_to_node(dev)); if (!rhub->ports) return; - for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { + for (i = 0; i < xhci->max_ports; i++) { if (xhci->hw_ports[i].rhub != rhub || xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY) continue; @@ -2147,32 +2215,28 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) { void __iomem *base; u32 offset; - unsigned int num_ports; int i, j; int cap_count = 0; u32 cap_start; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - num_ports = HCS_MAX_PORTS(xhci->hcs_params1); - xhci->hw_ports = kcalloc_node(num_ports, sizeof(*xhci->hw_ports), - flags, dev_to_node(dev)); + xhci->hw_ports = kcalloc_node(xhci->max_ports, sizeof(*xhci->hw_ports), + flags, dev_to_node(dev)); if (!xhci->hw_ports) return -ENOMEM; - for (i = 0; i < num_ports; i++) { - xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base + - NUM_PORT_REGS * i; + for (i = 0; i < xhci->max_ports; i++) { + xhci->hw_ports[i].port_reg = &xhci->op_regs->port_regs[i]; xhci->hw_ports[i].hw_portnum = i; init_completion(&xhci->hw_ports[i].rexit_done); init_completion(&xhci->hw_ports[i].u3exit_done); } - xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags, - dev_to_node(dev)); + xhci->rh_bw = kcalloc_node(xhci->max_ports, sizeof(*xhci->rh_bw), flags, dev_to_node(dev)); if (!xhci->rh_bw) return -ENOMEM; - for (i = 0; i < num_ports; i++) { + for (i = 0; i < xhci->max_ports; i++) { struct xhci_interval_bw_table *bw_table; INIT_LIST_HEAD(&xhci->rh_bw[i].tts); @@ -2204,9 +2268,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) offset = cap_start; while (offset) { - xhci_add_in_port(xhci, num_ports, base + offset, cap_count); - if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == - num_ports) + xhci_add_in_port(xhci, xhci->max_ports, base + offset, cap_count); + if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == xhci->max_ports) break; offset = xhci_find_next_ext_cap(base, offset, XHCI_EXT_CAPS_PROTOCOL); @@ -2219,30 +2282,8 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) "Found %u USB 2.0 ports and %u USB 3.0 ports.", xhci->usb2_rhub.num_ports, xhci->usb3_rhub.num_ports); - /* Place limits on the number of roothub ports so that the hub - * descriptors aren't longer than the USB core will allocate. - */ - if (xhci->usb3_rhub.num_ports > USB_SS_MAXPORTS) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Limiting USB 3.0 roothub ports to %u.", - USB_SS_MAXPORTS); - xhci->usb3_rhub.num_ports = USB_SS_MAXPORTS; - } - if (xhci->usb2_rhub.num_ports > USB_MAXCHILDREN) { - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Limiting USB 2.0 roothub ports to %u.", - USB_MAXCHILDREN); - xhci->usb2_rhub.num_ports = USB_MAXCHILDREN; - } - - if (!xhci->usb2_rhub.num_ports) - xhci_info(xhci, "USB2 root hub has no ports\n"); - - if (!xhci->usb3_rhub.num_ports) - xhci_info(xhci, "USB3 root hub has no ports\n"); - - xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, flags); - xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, flags); + xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, USB_MAXCHILDREN, flags); + xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, USB_SS_MAXPORTS, flags); return 0; } @@ -2283,37 +2324,25 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int segs, gfp_t flags) return ir; } -static int -xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, - unsigned int intr_num) +void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num) { + struct xhci_interrupter *ir; u64 erst_base; u32 erst_size; - if (intr_num >= xhci->max_interrupters) { - xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n", - intr_num, xhci->max_interrupters); - return -EINVAL; - } - - if (xhci->interrupters[intr_num]) { - xhci_warn(xhci, "Interrupter %d\n already set up", intr_num); - return -EINVAL; - } - - xhci->interrupters[intr_num] = ir; + ir = xhci->interrupters[intr_num]; ir->intr_num = intr_num; ir->ir_set = &xhci->run_regs->ir_set[intr_num]; /* set ERST count with the number of entries in the segment table */ erst_size = readl(&ir->ir_set->erst_size); - erst_size &= ERST_SIZE_MASK; + erst_size &= ~ERST_SIZE_MASK; erst_size |= ir->event_ring->num_segs; writel(erst_size, &ir->ir_set->erst_size); erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); - erst_base &= ERST_BASE_RSVDP; - erst_base |= ir->erst.erst_dma_addr & ~ERST_BASE_RSVDP; + erst_base &= ~ERST_BASE_ADDRESS_MASK; + erst_base |= ir->erst.erst_dma_addr & ERST_BASE_ADDRESS_MASK; if (xhci->quirks & XHCI_WRITE_64_HI_LO) hi_lo_writeq(erst_base, &ir->ir_set->erst_base); else @@ -2321,36 +2350,45 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, /* Set the event ring dequeue address of this interrupter */ xhci_set_hc_event_deq(xhci, ir); - - return 0; } struct xhci_interrupter * xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs, - u32 imod_interval) + u32 imod_interval, unsigned int intr_num) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_interrupter *ir; unsigned int i; int err = -ENOSPC; - if (!xhci->interrupters || xhci->max_interrupters <= 1) + if (!xhci->interrupters || xhci->max_interrupters <= 1 || + intr_num >= xhci->max_interrupters) return NULL; ir = xhci_alloc_interrupter(xhci, segs, GFP_KERNEL); if (!ir) return NULL; - spin_lock_irq(&xhci->lock); + xhci_ring_init(xhci, ir->event_ring); - /* Find available secondary interrupter, interrupter 0 is reserved for primary */ - for (i = 1; i < xhci->max_interrupters; i++) { - if (xhci->interrupters[i] == NULL) { - err = xhci_add_interrupter(xhci, ir, i); - break; + spin_lock_irq(&xhci->lock); + if (!intr_num) { + /* Find available secondary interrupter, interrupter 0 is reserved for primary */ + for (i = 1; i < xhci->max_interrupters; i++) { + if (!xhci->interrupters[i]) { + xhci->interrupters[i] = ir; + xhci_add_interrupter(xhci, i); + err = 0; + break; + } + } + } else { + if (!xhci->interrupters[intr_num]) { + xhci->interrupters[intr_num] = ir; + xhci_add_interrupter(xhci, intr_num); + err = 0; } } - spin_unlock_irq(&xhci->lock); if (err) { @@ -2360,13 +2398,10 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs, return NULL; } - err = xhci_set_interrupter_moderation(ir, imod_interval); - if (err) - xhci_warn(xhci, "Failed to set interrupter %d moderation to %uns\n", - i, imod_interval); + xhci_set_interrupter_moderation(ir, imod_interval); xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n", - i, xhci->max_interrupters); + ir->intr_num, xhci->max_interrupters); return ir; } @@ -2374,61 +2409,23 @@ EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter); int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { - struct xhci_interrupter *ir; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; dma_addr_t dma; - unsigned int val, val2; - u64 val_64; - u32 page_size, temp; - int i; - - INIT_LIST_HEAD(&xhci->cmd_list); - - /* init command timeout work */ - INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout); - init_completion(&xhci->cmd_ring_stop_completion); - - page_size = readl(&xhci->op_regs->page_size); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "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); - /* - * Program the Number of Device Slots Enabled field in the CONFIG - * register with the max value of slots the HC can handle. - */ - val = HCS_MAX_SLOTS(readl(&xhci->cap_regs->hcs_params1)); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// xHC can handle at most %d device slots.", val); - val2 = readl(&xhci->op_regs->config_reg); - val |= (val2 & ~HCS_SLOTS_MASK); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Setting Max device slots reg = 0x%x.", val); - writel(val, &xhci->op_regs->config_reg); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__); /* * xHCI section 5.4.6 - Device Context array must be * "physically contiguous and 64-byte (cache line) aligned". */ - xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, - flags); + xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, flags); if (!xhci->dcbaa) goto fail; + xhci->dcbaa->dma = dma; xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Device context base array address = 0x%pad (DMA), %p (virt)", - &xhci->dcbaa->dma, xhci->dcbaa); - xhci_write_64(xhci, dma, &xhci->op_regs->dcbaa_ptr); + "Device context base array address = %pad (DMA), %p (virt)", + &xhci->dcbaa->dma, xhci->dcbaa); /* * Initialize the ring segment pool. The ring must be a contiguous @@ -2444,92 +2441,69 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) else xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size); + if (!xhci->segment_pool) + goto fail; /* See Table 46 and Note on Figure 55 */ - xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, - 2112, 64, xhci->page_size); - if (!xhci->segment_pool || !xhci->device_pool) + xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, 2112, 64, + xhci->page_size); + if (!xhci->device_pool) goto fail; - /* Linear stream context arrays don't have any boundary restrictions, + /* + * Linear stream context arrays don't have any boundary restrictions, * and only need to be 16-byte aligned. */ - xhci->small_streams_pool = - dma_pool_create("xHCI 256 byte stream ctx arrays", - dev, SMALL_STREAM_ARRAY_SIZE, 16, 0); - xhci->medium_streams_pool = - dma_pool_create("xHCI 1KB stream ctx arrays", - dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0); - /* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE - * will be allocated with dma_alloc_coherent() + xhci->small_streams_pool = dma_pool_create("xHCI 256 byte stream ctx arrays", + dev, SMALL_STREAM_ARRAY_SIZE, 16, 0); + if (!xhci->small_streams_pool) + goto fail; + + /* + * Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE will be + * allocated with dma_alloc_coherent(). */ - if (!xhci->small_streams_pool || !xhci->medium_streams_pool) + xhci->medium_streams_pool = dma_pool_create("xHCI 1KB stream ctx arrays", + dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0); + if (!xhci->medium_streams_pool) + goto fail; + + /* + * refer to xhci rev1_2 protocol 5.3.3 max ports is 255. + * refer to xhci rev1_2 protocol 6.4.3.14 port bandwidth buffer need + * to be 16-byte aligned. + */ + xhci->port_bw_pool = dma_pool_create("xHCI 256 port bw ctx arrays", + dev, GET_PORT_BW_ARRAY_SIZE, 16, 0); + if (!xhci->port_bw_pool) goto fail; /* Set up the command ring to have one segments for now. */ 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, - "Allocated command ring at %p", xhci->cmd_ring); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "First segment DMA is 0x%pad", - &xhci->cmd_ring->first_seg->dma); - - /* Set the address in the Command Ring Control register */ - val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); - val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) | - (xhci->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) | - xhci->cmd_ring->cycle_state; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Setting command ring address to 0x%016llx", val_64); - xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring); - - /* Reserve one command ring TRB for disabling LPM. - * Since the USB core grabs the shared usb_bus bandwidth mutex before - * disabling LPM, we only need to reserve one TRB for all devices. - */ - xhci->cmd_ring_reserved_trbs++; - val = readl(&xhci->cap_regs->db_off); - val &= DBOFF_MASK; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Doorbell array is located at offset 0x%x from cap regs base addr", - val); - xhci->dba = (void __iomem *) xhci->cap_regs + val; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocated command ring at %p", xhci->cmd_ring); /* Allocate and set up primary interrupter 0 with an event ring. */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Allocating primary event ring"); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring"); xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters), flags, dev_to_node(dev)); - - ir = xhci_alloc_interrupter(xhci, 0, flags); - if (!ir) + if (!xhci->interrupters) goto fail; - if (xhci_add_interrupter(xhci, ir, 0)) + xhci->interrupters[0] = xhci_alloc_interrupter(xhci, 0, flags); + if (!xhci->interrupters[0]) goto fail; - ir->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; - - for (i = 0; i < MAX_HC_SLOTS; i++) - xhci->devs[i] = NULL; - if (scratchpad_alloc(xhci, flags)) goto fail; + if (xhci_setup_port_arrays(xhci, flags)) goto fail; - /* Enable USB 3.0 device notifications for function remote wake, which - * is necessary for allowing USB 3.0 devices to do remote wakeup from - * U3 (device suspend). - */ - temp = readl(&xhci->op_regs->dev_notification); - temp &= ~DEV_NOTE_MASK; - temp |= DEV_NOTE_FWAKE; - writel(temp, &xhci->op_regs->dev_notification); - + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished %s", __func__); return 0; fail: diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c index 27eb384a3963..583623cd4d8f 100644 --- a/drivers/usb/host/xhci-mtk-sch.c +++ b/drivers/usb/host/xhci-mtk-sch.c @@ -179,8 +179,7 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev) if (utt->multi) { tt_index = utt->hcpriv; if (!tt_index) { /* Create the index array */ - tt_index = kcalloc(utt->hub->maxchild, - sizeof(*tt_index), GFP_KERNEL); + tt_index = kzalloc_objs(*tt_index, utt->hub->maxchild); if (!tt_index) return ERR_PTR(-ENOMEM); utt->hcpriv = tt_index; @@ -193,7 +192,7 @@ static struct mu3h_sch_tt *find_tt(struct usb_device *udev) tt = *ptt; if (!tt) { /* Create the mu3h_sch_tt */ - tt = kzalloc(sizeof(*tt), GFP_KERNEL); + tt = kzalloc_obj(*tt); if (!tt) { if (allocated_index) { utt->hcpriv = NULL; @@ -264,7 +263,7 @@ create_sch_ep(struct xhci_hcd_mtk *mtk, struct usb_device *udev, else len = 1; - sch_ep = kzalloc(struct_size(sch_ep, bw_budget_table, len), GFP_KERNEL); + sch_ep = kzalloc_flex(*sch_ep, bw_budget_table, len); if (!sch_ep) return ERR_PTR(-ENOMEM); @@ -891,7 +890,7 @@ int xhci_mtk_sch_init(struct xhci_hcd_mtk *mtk) /* ss IN and OUT are separated */ num_usb_bus = xhci->usb3_rhub.num_ports * 2 + xhci->usb2_rhub.num_ports; - sch_array = kcalloc(num_usb_bus, sizeof(*sch_array), GFP_KERNEL); + sch_array = kzalloc_objs(*sch_array, num_usb_bus); if (sch_array == NULL) return -ENOMEM; diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 904831344440..06043c7c3100 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -670,7 +670,6 @@ static int xhci_mtk_probe(struct platform_device *pdev) } device_enable_async_suspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); pm_runtime_forbid(dev); @@ -746,10 +745,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); diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index f5e2bd66bb1b..2274f5995171 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -21,7 +21,7 @@ /* support at most 64 ep, use 32 size hash table */ #define SCH_EP_HASH_BITS 5 -/** +/* * To simplify scheduler algorithm, set a upper limit for ESIT, * if a synchromous ep's ESIT is larger than @XHCI_MTK_MAX_ESIT, * round down to the limit value, that means allocating more @@ -34,6 +34,7 @@ #define XHCI_MTK_FRAMES_CNT (XHCI_MTK_MAX_ESIT / UFRAMES_PER_FRAME) /** + * struct mu3h_sch_tt - TT scheduling data * @fs_bus_bw_out: save bandwidth used by FS/LS OUT eps in each uframes * @fs_bus_bw_in: save bandwidth used by FS/LS IN eps in each uframes * @ls_bus_bw: save bandwidth used by LS eps in each uframes @@ -51,7 +52,7 @@ struct mu3h_sch_tt { }; /** - * struct mu3h_sch_bw_info: schedule information for bandwidth domain + * struct mu3h_sch_bw_info - schedule information for bandwidth domain * * @bus_bw: array to keep track of bandwidth already used at each uframes * @@ -63,7 +64,7 @@ struct mu3h_sch_bw_info { }; /** - * struct mu3h_sch_ep_info: schedule information for endpoint + * struct mu3h_sch_ep_info - schedule information for endpoint * * @esit: unit is 125us, equal to 2 << Interval field in ep-context * @num_esit: number of @esit in a period @@ -77,6 +78,7 @@ struct mu3h_sch_bw_info { * @ep_type: endpoint type * @maxpkt: max packet size of endpoint * @ep: address of usb_host_endpoint struct + * @speed: usb device speed * @allocated: the bandwidth is aready allocated from bus_bw * @offset: which uframe of the interval that transfer should be * scheduled first time within the interval @@ -125,7 +127,7 @@ struct mu3h_sch_ep_info { #define MU3C_U2_PORT_MAX 5 /** - * struct mu3c_ippc_regs: MTK ssusb ip port control registers + * struct mu3c_ippc_regs - MTK ssusb ip port control registers * @ip_pw_ctr0~3: ip power and clock control registers * @ip_pw_sts1~2: ip power and clock status registers * @ip_xhci_cap: ip xHCI capability register diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c index 87f1597a0e5a..f91c5004fade 100644 --- a/drivers/usb/host/xhci-mvebu.c +++ b/drivers/usb/host/xhci-mvebu.c @@ -30,7 +30,7 @@ static void xhci_mvebu_mbus_config(void __iomem *base, writel(0, base + USB3_WIN_BASE(win)); } - /* Program each DRAM CS in a seperate window */ + /* Program each DRAM CS in a separate window */ for (win = 0; win < dram->num_cs; win++) { const struct mbus_dram_window *cs = &dram->cs[win]; @@ -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 620f8f0febb8..86df80399c9f 100644 --- a/drivers/usb/host/xhci-pci-renesas.c +++ b/drivers/usb/host/xhci-pci-renesas.c @@ -47,8 +47,9 @@ #define RENESAS_ROM_ERASE_MAGIC 0x5A65726F #define RENESAS_ROM_WRITE_MAGIC 0x53524F4D -#define RENESAS_RETRY 10000 -#define RENESAS_DELAY 10 +#define RENESAS_RETRY 50000 /* 50000 * RENESAS_DELAY ~= 500ms */ +#define RENESAS_CHIP_ERASE_RETRY 500000 /* 500000 * RENESAS_DELAY ~= 5s */ +#define RENESAS_DELAY 10 #define RENESAS_FW_NAME "renesas_usb_fw.mem" @@ -407,7 +408,7 @@ static void renesas_rom_erase(struct pci_dev *pdev) /* sleep a bit while ROM is erased */ msleep(20); - for (i = 0; i < RENESAS_RETRY; i++) { + for (i = 0; i < RENESAS_CHIP_ERASE_RETRY; i++) { retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status); status &= RENESAS_ROM_STATUS_ERASE; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 54460d11f7ee..585b2f3117b0 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -71,12 +71,22 @@ #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI 0x15ec #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI 0x15f0 +#define PCI_DEVICE_ID_AMD_ARIEL_TYPEC_XHCI 0x13ed +#define PCI_DEVICE_ID_AMD_ARIEL_TYPEA_XHCI 0x13ee +#define PCI_DEVICE_ID_AMD_STARSHIP_XHCI 0x148c +#define PCI_DEVICE_ID_AMD_FIREFLIGHT_15D4_XHCI 0x15d4 +#define PCI_DEVICE_ID_AMD_FIREFLIGHT_15D5_XHCI 0x15d5 +#define PCI_DEVICE_ID_AMD_RAVEN_15E0_XHCI 0x15e0 +#define PCI_DEVICE_ID_AMD_RAVEN_15E1_XHCI 0x15e1 +#define PCI_DEVICE_ID_AMD_RAVEN2_XHCI 0x15e5 #define PCI_DEVICE_ID_AMD_RENOIR_XHCI 0x1639 #define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9 #define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba #define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb #define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc +#define PCI_DEVICE_ID_ATI_NAVI10_7316_XHCI 0x7316 + #define PCI_DEVICE_ID_ASMEDIA_1042_XHCI 0x1042 #define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142 #define PCI_DEVICE_ID_ASMEDIA_1142_XHCI 0x1242 @@ -280,6 +290,21 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_NEC) xhci->quirks |= XHCI_NEC_HOST; + if (pdev->vendor == PCI_VENDOR_ID_AMD && + (pdev->device == PCI_DEVICE_ID_AMD_ARIEL_TYPEC_XHCI || + pdev->device == PCI_DEVICE_ID_AMD_ARIEL_TYPEA_XHCI || + pdev->device == PCI_DEVICE_ID_AMD_STARSHIP_XHCI || + pdev->device == PCI_DEVICE_ID_AMD_FIREFLIGHT_15D4_XHCI || + pdev->device == PCI_DEVICE_ID_AMD_FIREFLIGHT_15D5_XHCI || + pdev->device == PCI_DEVICE_ID_AMD_RAVEN_15E0_XHCI || + pdev->device == PCI_DEVICE_ID_AMD_RAVEN_15E1_XHCI || + pdev->device == PCI_DEVICE_ID_AMD_RAVEN2_XHCI)) + xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_9; + + if (pdev->vendor == PCI_VENDOR_ID_ATI && + pdev->device == PCI_DEVICE_ID_ATI_NAVI10_7316_XHCI) + xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_9; + if (pdev->vendor == PCI_VENDOR_ID_AMD && xhci->hci_version == 0x96) xhci->quirks |= XHCI_AMD_0x96_HOST; @@ -557,6 +582,8 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (!usb_hcd_is_primary_hcd(hcd)) return 0; + xhci->allow_single_roothub = 1; + if (xhci->quirks & XHCI_PME_STUCK_QUIRK) xhci_pme_acpi_rtd3_enable(pdev); @@ -585,7 +612,7 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) { int retval; struct xhci_hcd *xhci; - struct usb_hcd *hcd; + struct usb_hcd *hcd, *usb3_hcd; struct reset_control *reset; reset = devm_reset_control_get_optional_exclusive(&dev->dev, NULL); @@ -611,26 +638,31 @@ int xhci_pci_common_probe(struct pci_dev *dev, const struct pci_device_id *id) hcd = dev_get_drvdata(&dev->dev); xhci = hcd_to_xhci(hcd); xhci->reset = reset; - xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev, - pci_name(dev), hcd); - if (!xhci->shared_hcd) { - retval = -ENOMEM; - goto dealloc_usb2_hcd; - } - retval = xhci_ext_cap_init(xhci); - if (retval) - goto put_usb3_hcd; + if (!xhci_has_one_roothub(xhci)) { + xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev, + pci_name(dev), hcd); + if (!xhci->shared_hcd) { + retval = -ENOMEM; + goto dealloc_usb2_hcd; + } - retval = usb_add_hcd(xhci->shared_hcd, dev->irq, - IRQF_SHARED); - if (retval) - goto put_usb3_hcd; - /* Roothub already marked as USB 3.0 speed */ + retval = xhci_ext_cap_init(xhci); + if (retval) + goto put_usb3_hcd; + + retval = usb_add_hcd(xhci->shared_hcd, dev->irq, IRQF_SHARED); + if (retval) + goto put_usb3_hcd; + } else { + retval = xhci_ext_cap_init(xhci); + if (retval) + goto dealloc_usb2_hcd; + } - if (!(xhci->quirks & XHCI_BROKEN_STREAMS) && - HCC_MAX_PSA(xhci->hcc_params) >= 4) - xhci->shared_hcd->can_do_streams = 1; + usb3_hcd = xhci_get_usb3_hcd(xhci); + if (usb3_hcd && !(xhci->quirks & XHCI_BROKEN_STREAMS) && HCC_MAX_PSA(xhci->hcc_params) >= 4) + usb3_hcd->can_do_streams = 1; /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ pm_runtime_put_noidle(&dev->dev); @@ -807,8 +839,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 +873,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) @@ -862,9 +896,9 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup) if (!(xhci->quirks & XHCI_RESET_TO_DEFAULT)) return 0; - for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { + for (i = 0; i < xhci->max_ports; i++) { port = &xhci->hw_ports[i]; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); if ((portsc & PORT_PLS_MASK) != XDEV_U3) continue; @@ -885,7 +919,7 @@ static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup) xhci_dbg(xhci, "port %d-%d in U3 without wakeup, disable it\n", port->rhub->hcd->self.busnum, port->hcd_portnum + 1); portsc = xhci_port_state_to_neutral(portsc); - writel(portsc | PORT_PE, port->addr); + xhci_portsc_writel(port, portsc | PORT_PE); } return 0; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index d85ffa9ffaa7..074d9c731639 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -20,6 +20,7 @@ #include <linux/acpi.h> #include <linux/usb/of.h> #include <linux/reset.h> +#include <linux/usb/xhci-sideband.h> #include "xhci.h" #include "xhci-plat.h" @@ -74,6 +75,16 @@ static int xhci_priv_resume_quirk(struct usb_hcd *hcd) return priv->resume_quirk(hcd); } +static int xhci_priv_post_resume_quirk(struct usb_hcd *hcd) +{ + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); + + if (!priv->post_resume_quirk) + return 0; + + return priv->post_resume_quirk(hcd); +} + static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) { struct xhci_plat_priv *priv = xhci_to_priv(xhci); @@ -106,7 +117,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 = { @@ -152,7 +163,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s int ret; int irq; struct xhci_plat_priv *priv = NULL; - bool of_match; + const struct of_device_id *of_match; if (usb_disabled()) return -ENODEV; @@ -171,6 +182,7 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s return ret; pm_runtime_set_active(&pdev->dev); + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_enable(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); @@ -267,6 +279,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s device_property_read_u32(tmpdev, "imod-interval-ns", &xhci->imod_interval); + device_property_read_u16(tmpdev, "num-hc-interrupters", + &xhci->max_interrupters); } /* @@ -326,7 +340,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s } usb3_hcd = xhci_get_usb3_hcd(xhci); - if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4) + if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 && + !(xhci->quirks & XHCI_BROKEN_STREAMS)) usb3_hcd->can_do_streams = 1; if (xhci->shared_hcd) { @@ -451,7 +466,7 @@ void xhci_plat_remove(struct platform_device *dev) } EXPORT_SYMBOL_GPL(xhci_plat_remove); -static int xhci_plat_suspend(struct device *dev) +static int xhci_plat_suspend_common(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -479,9 +494,29 @@ 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_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); + + if (xhci_sideband_check(hcd)) { + priv->sideband_at_suspend = 1; + dev_dbg(dev, "sideband instance active, skip suspend.\n"); + return 0; + } + + return xhci_plat_suspend_common(dev); +} + +static int xhci_plat_freeze(struct device *dev) +{ + return xhci_plat_suspend_common(dev); +} + +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; @@ -501,7 +536,11 @@ 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; + + ret = xhci_priv_post_resume_quirk(hcd); if (ret) goto disable_clks; @@ -522,12 +561,26 @@ disable_clks: static int xhci_plat_resume(struct device *dev) { - return xhci_plat_resume_common(dev, PMSG_RESUME); + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); + + if (priv->sideband_at_suspend) { + priv->sideband_at_suspend = 0; + dev_dbg(dev, "sideband instance active, skip resume.\n"); + return 0; + } + + return xhci_plat_resume_common(dev, false); +} + +static int xhci_plat_thaw(struct device *dev) +{ + 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) @@ -548,15 +601,15 @@ 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 = { .suspend = pm_sleep_ptr(xhci_plat_suspend), .resume = pm_sleep_ptr(xhci_plat_resume), - .freeze = pm_sleep_ptr(xhci_plat_suspend), - .thaw = pm_sleep_ptr(xhci_plat_resume), - .poweroff = pm_sleep_ptr(xhci_plat_suspend), + .freeze = pm_sleep_ptr(xhci_plat_freeze), + .thaw = pm_sleep_ptr(xhci_plat_thaw), + .poweroff = pm_sleep_ptr(xhci_plat_freeze), .restore = pm_sleep_ptr(xhci_plat_restore), SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index 6475130eac4b..00751d851831 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -15,10 +15,13 @@ struct usb_hcd; struct xhci_plat_priv { const char *firmware_name; unsigned long long quirks; + bool power_lost; + unsigned sideband_at_suspend:1; void (*plat_start)(struct usb_hcd *); int (*init_quirk)(struct usb_hcd *); int (*suspend_quirk)(struct usb_hcd *); int (*resume_quirk)(struct usb_hcd *); + int (*post_resume_quirk)(struct usb_hcd *); }; #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv) diff --git a/drivers/usb/host/xhci-port.h b/drivers/usb/host/xhci-port.h index f19efb966d18..889b5fb0fcd8 100644 --- a/drivers/usb/host/xhci-port.h +++ b/drivers/usb/host/xhci-port.h @@ -144,9 +144,14 @@ #define PORT_TEST_MODE_SHIFT 28 /* USB3 Protocol PORTLI Port Link Information */ +#define PORT_LEC(p) ((p) & 0xffff) #define PORT_RX_LANES(p) (((p) >> 16) & 0xf) #define PORT_TX_LANES(p) (((p) >> 20) & 0xf) +/* eUSB2v2 protocol PORTLI Port Link information, RsvdP for normal USB2 */ +#define PORTLI_RDR(p) ((p) & 0xf) +#define PORTLI_TDR(p) (((p) >> 4) & 0xf) + /* USB2 Protocol PORTHLPMC */ #define PORT_HIRDM(p)((p) & 3) #define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2) diff --git a/drivers/usb/host/xhci-rcar-regs.h b/drivers/usb/host/xhci-rcar-regs.h new file mode 100644 index 000000000000..5ecbda858be0 --- /dev/null +++ b/drivers/usb/host/xhci-rcar-regs.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __XHCI_RCAR_H +#define __XHCI_RCAR_H + +/*** Register Offset ***/ +#define RCAR_USB3_AXH_STA 0x104 /* AXI Host Control Status */ +#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */ +#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */ +#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */ + +#define RCAR_USB3_LCLK 0xa44 /* LCLK Select */ +#define RCAR_USB3_CONF1 0xa48 /* USB3.0 Configuration1 */ +#define RCAR_USB3_CONF2 0xa5c /* USB3.0 Configuration2 */ +#define RCAR_USB3_CONF3 0xaa8 /* USB3.0 Configuration3 */ +#define RCAR_USB3_RX_POL 0xab0 /* USB3.0 RX Polarity */ +#define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */ + +/*** Register Settings ***/ +/* AXI Host Control Status */ +#define RCAR_USB3_AXH_STA_B3_PLL_ACTIVE 0x00010000 +#define RCAR_USB3_AXH_STA_B2_PLL_ACTIVE 0x00000001 +#define RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK (RCAR_USB3_AXH_STA_B3_PLL_ACTIVE | \ + RCAR_USB3_AXH_STA_B2_PLL_ACTIVE) + +/* Interrupt Enable */ +#define RCAR_USB3_INT_XHC_ENA 0x00000001 +#define RCAR_USB3_INT_PME_ENA 0x00000002 +#define RCAR_USB3_INT_HSE_ENA 0x00000004 +#define RCAR_USB3_INT_ENA_VAL (RCAR_USB3_INT_XHC_ENA | \ + RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA) + +/* FW Download Control & Status */ +#define RCAR_USB3_DL_CTRL_ENABLE 0x00000001 +#define RCAR_USB3_DL_CTRL_FW_SUCCESS 0x00000010 +#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 0x00000100 + +/* LCLK Select */ +#define RCAR_USB3_LCLK_ENA_VAL 0x01030001 + +/* USB3.0 Configuration */ +#define RCAR_USB3_CONF1_VAL 0x00030204 +#define RCAR_USB3_CONF2_VAL 0x00030300 +#define RCAR_USB3_CONF3_VAL 0x13802007 + +/* USB3.0 Polarity */ +#define RCAR_USB3_RX_POL_VAL BIT(21) +#define RCAR_USB3_TX_POL_VAL BIT(4) + +#endif /* __XHCI_RCAR_H */ diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 1cc082a3b793..8a993ee21c87 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -11,9 +11,12 @@ #include <linux/platform_device.h> #include <linux/of.h> #include <linux/usb/phy.h> +#include <linux/reset.h> #include "xhci.h" #include "xhci-plat.h" +#include "xhci-rcar-regs.h" +#include "xhci-rzg3e-regs.h" #include "xhci-rzv2m.h" #define XHCI_RCAR_FIRMWARE_NAME_V1 "r8a779x_usb3_v1.dlmem" @@ -29,50 +32,6 @@ MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V1); MODULE_FIRMWARE(XHCI_RCAR_FIRMWARE_NAME_V3); -/*** Register Offset ***/ -#define RCAR_USB3_AXH_STA 0x104 /* AXI Host Control Status */ -#define RCAR_USB3_INT_ENA 0x224 /* Interrupt Enable */ -#define RCAR_USB3_DL_CTRL 0x250 /* FW Download Control & Status */ -#define RCAR_USB3_FW_DATA0 0x258 /* FW Data0 */ - -#define RCAR_USB3_LCLK 0xa44 /* LCLK Select */ -#define RCAR_USB3_CONF1 0xa48 /* USB3.0 Configuration1 */ -#define RCAR_USB3_CONF2 0xa5c /* USB3.0 Configuration2 */ -#define RCAR_USB3_CONF3 0xaa8 /* USB3.0 Configuration3 */ -#define RCAR_USB3_RX_POL 0xab0 /* USB3.0 RX Polarity */ -#define RCAR_USB3_TX_POL 0xab8 /* USB3.0 TX Polarity */ - -/*** Register Settings ***/ -/* AXI Host Control Status */ -#define RCAR_USB3_AXH_STA_B3_PLL_ACTIVE 0x00010000 -#define RCAR_USB3_AXH_STA_B2_PLL_ACTIVE 0x00000001 -#define RCAR_USB3_AXH_STA_PLL_ACTIVE_MASK (RCAR_USB3_AXH_STA_B3_PLL_ACTIVE | \ - RCAR_USB3_AXH_STA_B2_PLL_ACTIVE) - -/* Interrupt Enable */ -#define RCAR_USB3_INT_XHC_ENA 0x00000001 -#define RCAR_USB3_INT_PME_ENA 0x00000002 -#define RCAR_USB3_INT_HSE_ENA 0x00000004 -#define RCAR_USB3_INT_ENA_VAL (RCAR_USB3_INT_XHC_ENA | \ - RCAR_USB3_INT_PME_ENA | RCAR_USB3_INT_HSE_ENA) - -/* FW Download Control & Status */ -#define RCAR_USB3_DL_CTRL_ENABLE 0x00000001 -#define RCAR_USB3_DL_CTRL_FW_SUCCESS 0x00000010 -#define RCAR_USB3_DL_CTRL_FW_SET_DATA0 0x00000100 - -/* LCLK Select */ -#define RCAR_USB3_LCLK_ENA_VAL 0x01030001 - -/* USB3.0 Configuration */ -#define RCAR_USB3_CONF1_VAL 0x00030204 -#define RCAR_USB3_CONF2_VAL 0x00030300 -#define RCAR_USB3_CONF3_VAL 0x13802007 - -/* USB3.0 Polarity */ -#define RCAR_USB3_RX_POL_VAL BIT(21) -#define RCAR_USB3_TX_POL_VAL BIT(4) - static void xhci_rcar_start_gen2(struct usb_hcd *hcd) { /* LCLK Select */ @@ -110,6 +69,48 @@ static void xhci_rcar_start(struct usb_hcd *hcd) } } +static void xhci_rzg3e_start(struct usb_hcd *hcd) +{ + u32 int_en; + + if (hcd->regs) { + /* Update the controller initial setting */ + writel(0x03130200, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(0)); + writel(0x00160200, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(1)); + writel(0x03150000, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(2)); + writel(0x03130200, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(3)); + writel(0x00180000, hcd->regs + RZG3E_USB3_HOST_U3P0PIPESC(4)); + + /* Interrupt Enable */ + int_en = readl(hcd->regs + RZG3E_USB3_HOST_INTEN); + int_en |= RZG3E_USB3_HOST_INTEN_ENA; + writel(int_en, hcd->regs + RZG3E_USB3_HOST_INTEN); + } +} + +static int xhci_rzg3e_resume(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + return reset_control_deassert(xhci->reset); +} + +static int xhci_rzg3e_post_resume(struct usb_hcd *hcd) +{ + xhci_rzg3e_start(hcd); + + return 0; +} + +static int xhci_rzg3e_suspend(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + reset_control_assert(xhci->reset); + + return 0; +} + static int xhci_rcar_download_firmware(struct usb_hcd *hcd) { struct device *dev = hcd->self.controller; @@ -233,6 +234,14 @@ static const struct xhci_plat_priv xhci_plat_renesas_rzv2m = { .plat_start = xhci_rzv2m_start, }; +static const struct xhci_plat_priv xhci_plat_renesas_rzg3e = { + .quirks = XHCI_NO_64BIT_SUPPORT | XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS, + .plat_start = xhci_rzg3e_start, + .suspend_quirk = xhci_rzg3e_suspend, + .resume_quirk = xhci_rzg3e_resume, + .post_resume_quirk = xhci_rzg3e_post_resume, +}; + static const struct of_device_id usb_xhci_of_match[] = { { .compatible = "renesas,xhci-r8a7790", @@ -250,6 +259,9 @@ static const struct of_device_id usb_xhci_of_match[] = { .compatible = "renesas,xhci-r8a7796", .data = &xhci_plat_renesas_rcar_gen3, }, { + .compatible = "renesas,r9a09g047-xhci", + .data = &xhci_plat_renesas_rzg3e, + }, { .compatible = "renesas,rcar-gen2-xhci", .data = &xhci_plat_renesas_rcar_gen2, }, { diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 965bffce301e..e47e644b296e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -82,6 +82,23 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, return seg->dma + (segment_offset * sizeof(*trb)); } +static union xhci_trb *xhci_dma_to_trb(struct xhci_segment *start_seg, + dma_addr_t dma, + struct xhci_segment **match_seg) +{ + struct xhci_segment *seg; + + xhci_for_each_ring_seg(start_seg, seg) { + if (in_range(dma, seg->dma, TRB_SEGMENT_SIZE)) { + if (match_seg) + *match_seg = seg; + return &seg->trbs[(dma - seg->dma) / sizeof(union xhci_trb)]; + } + } + + return NULL; +} + static bool trb_is_noop(union xhci_trb *trb) { return TRB_TYPE_NOOP_LE32(trb->generic.field[3]); @@ -128,11 +145,11 @@ static void inc_td_cnt(struct urb *urb) urb_priv->num_tds_done++; } -static void trb_to_noop(union xhci_trb *trb, u32 noop_type) +static void trb_to_noop(union xhci_trb *trb, u32 noop_type, bool unchain_links) { if (trb_is_link(trb)) { - /* unchain chained link TRBs */ - trb->link.control &= cpu_to_le32(~TRB_CHAIN); + if (unchain_links) + trb->link.control &= cpu_to_le32(~TRB_CHAIN); } else { trb->generic.field[0] = 0; trb->generic.field[1] = 0; @@ -143,6 +160,11 @@ static void trb_to_noop(union xhci_trb *trb, u32 noop_type) } } +static unsigned int trb_to_pos(struct xhci_segment *seg, union xhci_trb *trb) +{ + return seg->num * TRBS_PER_SEGMENT + (trb - seg->trbs); +} + /* Updates trb to point to the next TRB in the ring, and updates seg if the next * TRB is in a new segment. This does not skip over link TRBs, and it does not * effect the ring dequeue or enqueue pointers. @@ -204,6 +226,50 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) } /* + * 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 @@ -211,11 +277,6 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) * 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()? */ @@ -223,8 +284,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; @@ -233,48 +292,46 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, return; } - next = ++(ring->enqueue); + ring->enqueue++; - /* Update the dequeue pointer further if that was a link TRB */ - while (trb_is_link(next)) { + /* + * 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 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; +static bool dma_in_range(dma_addr_t dma, + struct xhci_segment *start_seg, union xhci_trb *start_trb, + struct xhci_segment *end_seg, union xhci_trb *end_trb) +{ + unsigned int pos, start, end; + struct xhci_segment *pos_seg; + union xhci_trb *pos_trb = xhci_dma_to_trb(start_seg, dma, &pos_seg); - /* 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); + /* Is the trb dma address even part of the whole ring? */ + if (!pos_trb) + return false; - /* Toggle the cycle bit after the last ring segment. */ - if (link_trb_toggles_cycle(next)) - ring->cycle_state ^= 1; + pos = trb_to_pos(pos_seg, pos_trb); + start = trb_to_pos(start_seg, start_trb); + end = trb_to_pos(end_seg, end_trb); - ring->enq_seg = ring->enq_seg->next; - ring->enqueue = ring->enq_seg->trbs; - next = ring->enqueue; + /* end position is smaller than start, search range wraps around */ + if (end < start) + return !(pos > end && pos < start); - trace_xhci_inc_enq(ring); + return (pos >= start && pos <= end); +} - if (link_trb_count++ > ring->num_segs) { - xhci_warn(xhci, "%s: Ring link TRB loop\n", __func__); - break; - } - } +/* If the suspect DMA address is a TRB in this TD, this function returns true */ +static bool trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma) +{ + return dma_in_range(suspect_dma, td->start_seg, td->start_trb, + td->end_seg, td->end_trb); } /* @@ -378,7 +435,7 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci) static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci) { - return mod_delayed_work(system_wq, &xhci->cmd_timer, + return mod_delayed_work(system_percpu_wq, &xhci->cmd_timer, msecs_to_jiffies(xhci->current_cmd->timeout_ms)); } @@ -409,7 +466,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, xhci_dbg(xhci, "Turn aborted command %p to no-op\n", i_cmd->command_trb); - trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP); + trb_to_noop(i_cmd->command_trb, TRB_CMD_NOOP, false); /* * caller waiting for completion is called when command @@ -462,9 +519,8 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags) * In the future we should distinguish between -ENODEV and -ETIMEDOUT * and try to recover a -ETIMEDOUT with a host controller reset. */ - ret = xhci_handshake_check_state(xhci, &xhci->op_regs->cmd_ring, - CMD_RING_RUNNING, 0, 5 * 1000 * 1000, - XHCI_STATE_REMOVING); + ret = xhci_handshake(&xhci->op_regs->cmd_ring, + CMD_RING_RUNNING, 0, 5 * 1000 * 1000); if (ret < 0) { xhci_err(xhci, "Abort failed to stop command ring: %d\n", ret); xhci_halt(xhci); @@ -643,7 +699,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, int new_cycle; dma_addr_t addr; u64 hw_dequeue; - bool cycle_found = false; + bool hw_dequeue_found = false; bool td_last_trb_found = false; u32 trb_sct = 0; int ret; @@ -656,28 +712,27 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, return -ENODEV; } - hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id); + hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id) & TR_DEQ_PTR_MASK; new_seg = ep_ring->deq_seg; new_deq = ep_ring->dequeue; - new_cycle = hw_dequeue & 0x1; + new_cycle = le32_to_cpu(td->end_trb->generic.field[3]) & TRB_CYCLE; /* - * We want to find the pointer, segment and cycle state of the new trb - * (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. + * Walk the ring until both the next TRB and hw_dequeue are found (don't + * move hw_dequeue back if it went forward due to a HW bug). Cycle state + * is loaded from a known good TRB, track later toggles to maintain it. */ do { - if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq) - == (dma_addr_t)(hw_dequeue & ~0xf)) { - cycle_found = true; + if (!hw_dequeue_found && xhci_trb_virt_to_dma(new_seg, new_deq) + == (dma_addr_t)hw_dequeue) { + hw_dequeue_found = true; if (td_last_trb_found) break; } if (new_deq == td->end_trb) td_last_trb_found = true; - if (cycle_found && trb_is_link(new_deq) && + if (td_last_trb_found && trb_is_link(new_deq) && link_trb_toggles_cycle(new_deq)) new_cycle ^= 0x1; @@ -689,7 +744,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, return -EINVAL; } - } while (!cycle_found || !td_last_trb_found); + } while (!hw_dequeue_found || !td_last_trb_found); /* Don't update the ring cycle state for the producer (us). */ addr = xhci_trb_virt_to_dma(new_seg, new_deq); @@ -700,7 +755,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, } if ((ep->ep_state & SET_DEQ_PENDING)) { - xhci_warn(xhci, "Set TR Deq already pending, don't submit for 0x%pad\n", + xhci_warn(xhci, "Set TR Deq already pending, don't submit for %pad\n", &addr); return -EBUSY; } @@ -708,7 +763,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci, /* This function gets called from contexts where it cannot sleep */ cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!cmd) { - xhci_warn(xhci, "Can't alloc Set TR Deq cmd 0x%pad\n", &addr); + xhci_warn(xhci, "Can't alloc Set TR Deq cmd %pad\n", &addr); return -ENOMEM; } @@ -743,13 +798,18 @@ 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_td *td, bool flip_cycle) +static void td_to_noop(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, + struct xhci_td *td, bool flip_cycle) { + bool unchain_links; struct xhci_segment *seg = td->start_seg; union xhci_trb *trb = td->start_trb; + /* link TRBs should now be unchained, but some old HCs expect otherwise */ + unchain_links = !xhci_link_chain_quirk(xhci, ep->ring ? ep->ring->type : TYPE_STREAM); + while (1) { - trb_to_noop(trb, TRB_TR_NOOP); + trb_to_noop(trb, TRB_TR_NOOP, unchain_links); /* flip cycle if asked to */ if (flip_cycle && trb != td->start_trb && trb != td->end_trb) @@ -1012,9 +1072,9 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep) */ hw_deq = xhci_get_hw_deq(xhci, ep->vdev, ep->ep_index, td->urb->stream_id); - hw_deq &= ~0xf; + hw_deq &= TR_DEQ_PTR_MASK; - 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 */ @@ -1037,16 +1097,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(cached_td, false); + td_to_noop(xhci, ep, cached_td, false); cached_td->cancel_status = TD_CLEARED; } - td_to_noop(td, false); + td_to_noop(xhci, ep, td, false); td->cancel_status = TD_CLEARING_CACHE; cached_td = td; break; } } else { - td_to_noop(td, false); + td_to_noop(xhci, ep, td, false); td->cancel_status = TD_CLEARED; } } @@ -1071,7 +1131,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(td, false); + td_to_noop(xhci, ep, td, false); td->cancel_status = TD_CLEARED; } } @@ -1102,9 +1162,9 @@ static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep) if (!list_empty(&ep->ring->td_list)) { /* Not streams compatible */ hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0); - hw_deq &= ~0xf; + hw_deq &= TR_DEQ_PTR_MASK; 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; @@ -1164,7 +1224,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 { @@ -1175,8 +1242,11 @@ 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: @@ -1198,8 +1268,9 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, * 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, on - * chips where this is known to help. Wait for 100ms. + * Keep retrying until the EP starts and stops again or + * up to a timeout (a defective HC may never start, or a + * driver bug may cause stopping an already stopped EP). */ if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100))) break; @@ -1309,18 +1380,21 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci, */ void xhci_hc_died(struct xhci_hcd *xhci) { + bool notify; int i, j; if (xhci->xhc_state & XHCI_STATE_DYING) return; - xhci_err(xhci, "xHCI host controller not responding, assume dead\n"); + notify = !(xhci->xhc_state & XHCI_STATE_REMOVING); + if (notify) + xhci_err(xhci, "xHCI host controller not responding, assume dead\n"); xhci->xhc_state |= XHCI_STATE_DYING; xhci_cleanup_command_queue(xhci); /* return any pending urbs, remove may be waiting for them */ - for (i = 0; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { + for (i = 0; i <= xhci->max_slots; i++) { if (!xhci->devs[i]) continue; for (j = 0; j < 31; j++) @@ -1328,47 +1402,10 @@ void xhci_hc_died(struct xhci_hcd *xhci) } /* inform usb core hc died if PCI remove isn't already handling it */ - if (!(xhci->xhc_state & XHCI_STATE_REMOVING)) + if (notify) 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 @@ -1448,7 +1485,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) { - deq = le64_to_cpu(stream_ctx->stream_ring) & SCTX_DEQ_MASK; + deq = le64_to_cpu(stream_ctx->stream_ring) & TR_DEQ_PTR_MASK; /* * Cadence xHCI controllers store some endpoint state @@ -1464,7 +1501,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, stream_ctx->reserved[1] = 0; } } else { - deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK; + deq = le64_to_cpu(ep_ctx->deq) & TR_DEQ_PTR_MASK; } xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Successful Set TR Deq Ptr cmd, deq = @%08llx", deq); @@ -1473,8 +1510,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", @@ -1559,7 +1596,8 @@ static void xhci_handle_cmd_enable_slot(int slot_id, struct xhci_command *comman command->slot_id = 0; } -static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id) +static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id, + u32 cmd_comp_code) { struct xhci_virt_device *virt_dev; struct xhci_slot_ctx *slot_ctx; @@ -1574,6 +1612,10 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id) if (xhci->quirks & XHCI_EP_LIMIT_QUIRK) /* Delete default control endpoint resources */ xhci_free_device_endpoint_resources(xhci, virt_dev, true); + if (cmd_comp_code == COMP_SUCCESS) { + xhci->dcbaa->dev_context_ptrs[slot_id] = 0; + xhci->devs[slot_id] = NULL; + } } static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id) @@ -1823,7 +1865,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, xhci_handle_cmd_enable_slot(slot_id, cmd, cmd_comp_code); break; case TRB_DISABLE_SLOT: - xhci_handle_cmd_disable_slot(xhci, slot_id); + xhci_handle_cmd_disable_slot(xhci, slot_id, cmd_comp_code); break; case TRB_CONFIG_EP: if (!cmd->completion) @@ -1867,6 +1909,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, case TRB_NEC_GET_FW: xhci_handle_cmd_nec_get_fw(xhci, event); break; + case TRB_GET_BW: + break; default: /* Skip over unknown commands on the event ring */ xhci_info(xhci, "INFO unknown command type %d\n", cmd_type); @@ -1947,10 +1991,10 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci) static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) { + struct xhci_virt_device *vdev = NULL; struct usb_hcd *hcd; u32 port_id; u32 portsc, cmd_reg; - int max_ports; unsigned int hcd_portnum; struct xhci_bus_state *bus_state; bool bogus_port_status = false; @@ -1962,9 +2006,8 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) "WARN: xHC returned failed port status event\n"); port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0])); - max_ports = HCS_MAX_PORTS(xhci->hcs_params1); - if ((port_id <= 0) || (port_id > max_ports)) { + if ((port_id <= 0) || (port_id > xhci->max_ports)) { xhci_warn(xhci, "Port change event with invalid port ID %d\n", port_id); return; @@ -1978,6 +2021,9 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) goto cleanup; } + if (port->slot_id) + vdev = xhci->devs[port->slot_id]; + /* We might get interrupts after shared_hcd is removed */ if (port->rhub == &xhci->usb3_rhub && xhci->shared_hcd == NULL) { xhci_dbg(xhci, "ignore port event for removed USB3 hcd\n"); @@ -1988,7 +2034,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) hcd = port->rhub->hcd; bus_state = &port->rhub->bus_state; hcd_portnum = port->hcd_portnum; - portsc = readl(port->addr); + portsc = xhci_portsc_readl(port); xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n", hcd->self.busnum, hcd_portnum + 1, port_id, portsc); @@ -2000,10 +2046,11 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) usb_hcd_resume_root_hub(hcd); } - if (hcd->speed >= HCD_USB3 && - (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) { - if (port->slot_id && xhci->devs[port->slot_id]) - xhci->devs[port->slot_id]->flags |= VDEV_PORT_ERROR; + if (vdev && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) { + if (!(portsc & PORT_RESET)) + vdev->flags |= VDEV_PORT_ERROR; + } else if (vdev && portsc & PORT_RC) { + vdev->flags &= ~VDEV_PORT_ERROR; } if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) { @@ -2061,7 +2108,7 @@ static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) * so the roothub behavior is consistent with external * USB 3.0 hub behavior. */ - if (port->slot_id && xhci->devs[port->slot_id]) + if (vdev) xhci_ring_device(xhci, port->slot_id); if (bus_state->port_remote_wakeup & (1 << hcd_portnum)) { xhci_test_and_clear_bit(xhci, port, PORT_PLC); @@ -2116,67 +2163,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->start_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->end_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) { @@ -2202,24 +2188,31 @@ static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td, * External device side is also halted in functional stall cases. Class driver * will clear the device halt with a CLEAR_FEATURE(ENDPOINT_HALT) request later. */ -static bool xhci_halted_host_endpoint(struct xhci_ep_ctx *ep_ctx, unsigned int comp_code) +static bool xhci_halted_host_endpoint(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx, + unsigned int comp_code) { - /* Stall halts both internal and device side endpoint */ - if (comp_code == COMP_STALL_ERROR) - return true; + int ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); - /* TRB completion codes that may require internal halt cleanup */ - if (comp_code == COMP_USB_TRANSACTION_ERROR || - comp_code == COMP_BABBLE_DETECTED_ERROR || - comp_code == COMP_SPLIT_TRANSACTION_ERROR) + switch (comp_code) { + case COMP_STALL_ERROR: + /* on xHCI this always halts, including protocol stall */ + return true; + case COMP_BABBLE_DETECTED_ERROR: /* * The 0.95 spec says a babbling control endpoint is not halted. * The 0.96 spec says it is. Some HW claims to be 0.95 * compliant, but it halts the control endpoint anyway. * Check endpoint context if endpoint is halted. */ - if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED) - return true; + if (xhci->hci_version <= 0x95 && ep_type == CTRL_EP) + return GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED; + + fallthrough; + case COMP_USB_TRANSACTION_ERROR: + case COMP_SPLIT_TRANSACTION_ERROR: + /* these errors halt all non-isochronous endpoints */ + return ep_type != ISOC_IN_EP && ep_type != ISOC_OUT_EP; + } return false; } @@ -2256,41 +2249,9 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, * the ring dequeue pointer or take this TD off any lists yet. */ return; - case COMP_USB_TRANSACTION_ERROR: - case COMP_BABBLE_DETECTED_ERROR: - case COMP_SPLIT_TRANSACTION_ERROR: - /* - * If endpoint context state is not halted we might be - * racing with a reset endpoint command issued by a unsuccessful - * stop endpoint completion (context error). In that case the - * td should be on the cancelled list, and EP_HALTED flag set. - * - * Or then it's not halted due to the 0.95 spec stating that a - * babbling control endpoint should not halt. The 0.96 spec - * again says it should. Some HW claims to be 0.95 compliant, - * but it halts the control endpoint anyway. - */ - if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_HALTED) { - /* - * If EP_HALTED is set and TD is on the cancelled list - * the TD and dequeue pointer will be handled by reset - * ep command completion - */ - if ((ep->ep_state & EP_HALTED) && - !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->start_trb)); - return; - } - /* endpoint not halted, don't reset it */ - break; - } - /* 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; - case COMP_STALL_ERROR: + } + + if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) { /* * xhci internal endpoint state will go to a "halt" state for * any stall, including default control pipe protocol stall. @@ -2301,14 +2262,12 @@ static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, * stall later. Hub TT buffer should only be cleared for FS/LS * devices behind HS hubs for functional stalls. */ - if (ep->ep_index != 0) + if (!(ep->ep_index == 0 && trb_comp_code == COMP_STALL_ERROR)) xhci_clear_hub_tt_buffer(xhci, td, ep); xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); return; /* xhci_handle_halted_endpoint marked td cancelled */ - default: - break; } xhci_dequeue_td(xhci, td, ep_ring, td->status); @@ -2385,7 +2344,7 @@ static void process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, case COMP_STOPPED_LENGTH_INVALID: goto finish_td; default: - if (!xhci_halted_host_endpoint(ep_ctx, trb_comp_code)) + if (!xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) break; xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n", trb_comp_code, ep->ep_index); @@ -2476,6 +2435,12 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, 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: case COMP_STALL_ERROR: frame->status = -EPROTO; @@ -2644,6 +2609,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. @@ -2659,11 +2640,11 @@ static int handle_tx_event(struct xhci_hcd *xhci, int ep_index; struct xhci_td *td = NULL; dma_addr_t ep_trb_dma; - struct xhci_segment *ep_seg; union xhci_trb *ep_trb; 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; @@ -2689,6 +2670,9 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (!ep_ring) return handle_transferless_tx_event(xhci, ep, trb_comp_code); + /* find the transfer trb this events points to */ + ep_trb = xhci_dma_to_trb(ep_ring->deq_seg, ep_trb_dma, NULL); + /* Look for common error cases */ switch (trb_comp_code) { /* Skip codes that require special handling depending on @@ -2697,8 +2681,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: @@ -2770,14 +2754,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 @@ -2787,9 +2769,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, @@ -2832,11 +2814,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"); 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 @@ -2846,7 +2832,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); } @@ -2859,15 +2846,30 @@ static int handle_tx_event(struct xhci_hcd *xhci, td = list_first_entry(&ep_ring->td_list, struct xhci_td, td_list); - /* Is this a TRB in the currently executing TD? */ - ep_seg = trb_in_td(xhci, td, ep_trb_dma, false); - - if (!ep_seg) { + /* Is this TRB not part of the currently executing TD? */ + if (!trb_in_td(td, ep_trb_dma)) { 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); @@ -2876,6 +2878,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 @@ -2890,21 +2896,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 = 0; 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) { @@ -2922,12 +2924,12 @@ 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, ep_trb_dma); /* @@ -2952,11 +2954,19 @@ static int handle_tx_event(struct xhci_hcd *xhci, return 0; check_endpoint_halted: - if (xhci_halted_host_endpoint(ep_ctx, trb_comp_code)) + if (xhci_halted_host_endpoint(xhci, ep_ctx, trb_comp_code)) xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET); 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)); + + return -ESHUTDOWN; + err_out: xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n", (unsigned long long) xhci_trb_virt_to_dma( @@ -3025,9 +3035,9 @@ static int xhci_handle_event_trb(struct xhci_hcd *xhci, struct xhci_interrupter * - When all events have finished * - To avoid "Event Ring Full Error" condition */ -static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, - struct xhci_interrupter *ir, - bool clear_ehb) +void xhci_update_erst_dequeue(struct xhci_hcd *xhci, + struct xhci_interrupter *ir, + bool clear_ehb) { u64 temp_64; dma_addr_t deq; @@ -3058,11 +3068,14 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir) { if (!ir->ip_autoclear) { - u32 irq_pending; + u32 iman; - irq_pending = readl(&ir->ir_set->irq_pending); - irq_pending |= IMAN_IP; - writel(irq_pending, &ir->ir_set->irq_pending); + iman = readl(&ir->ir_set->iman); + iman |= IMAN_IP; + writel(iman, &ir->ir_set->iman); + + /* Read operation to guarantee the write has been flushed from posted buffers */ + readl(&ir->ir_set->iman); } } @@ -3070,10 +3083,11 @@ static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir) * Handle all OS-owned events on an interrupter event ring. It may drop * and reaquire xhci->lock between event processing. */ -static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir) +static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir, + bool skip_events) { int event_loop = 0; - int err; + int err = 0; u64 temp; xhci_clear_interrupt_pending(ir); @@ -3096,7 +3110,8 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir /* Process all OS owned event TRBs on this event ring */ while (unhandled_event_trb(ir->event_ring)) { - err = xhci_handle_event_trb(xhci, ir, ir->event_ring->dequeue); + if (!skip_events) + err = xhci_handle_event_trb(xhci, ir, ir->event_ring->dequeue); /* * If half a segment of events have been handled in one go then @@ -3124,6 +3139,37 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir } /* + * Move the event ring dequeue pointer to skip events kept in the secondary + * event ring. This is used to ensure that pending events in the ring are + * acknowledged, so the xHCI HCD can properly enter suspend/resume. The + * secondary ring is typically maintained by an external component. + */ +void xhci_skip_sec_intr_events(struct xhci_hcd *xhci, + struct xhci_ring *ring, struct xhci_interrupter *ir) +{ + union xhci_trb *current_trb; + u64 erdp_reg; + dma_addr_t deq; + + /* disable irq, ack pending interrupt and ack all pending events */ + xhci_disable_interrupter(xhci, ir); + + /* last acked event trb is in erdp reg */ + erdp_reg = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + deq = (dma_addr_t)(erdp_reg & ERST_PTR_MASK); + if (!deq) { + xhci_err(xhci, "event ring handling not required\n"); + return; + } + + current_trb = ir->event_ring->dequeue; + /* read cycle state of the last acked trb to find out CCS */ + ring->cycle_state = le32_to_cpu(current_trb->event_cmd.flags) & TRB_CYCLE; + + xhci_handle_events(xhci, ir, true); +} + +/* * xHCI spec says we can get an interrupt, and if the HC has an error condition, * we might get bad data out of the event ring. Section 4.10.2.7 has a list of * indicators of an event TRB error, but we check the status *first* to be safe. @@ -3149,6 +3195,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) if (status & STS_HCE) { xhci_warn(xhci, "WARNING: Host Controller Error\n"); + xhci_halt(xhci); goto out; } @@ -3161,13 +3208,12 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) /* * Clear the op reg interrupt status first, * so we can receive interrupts from other MSI-X interrupters. - * Write 1 to clear the interrupt status. + * USBSTS bits are write 1 to clear. */ - status |= STS_EINT; - writel(status, &xhci->op_regs->status); + writel(STS_EINT, &xhci->op_regs->status); /* This is the handler of the primary interrupter */ - xhci_handle_events(xhci, xhci->interrupters[0]); + xhci_handle_events(xhci, xhci->interrupters[0], false); out: spin_unlock(&xhci->lock); @@ -3216,7 +3262,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 */ @@ -3264,33 +3309,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"); @@ -3508,7 +3529,7 @@ static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred, if ((xhci->quirks & XHCI_MTK_HOST) && (xhci->hci_version < 0x100)) trb_buff_len = 0; - maxp = usb_endpoint_maxp(&urb->ep->desc); + maxp = xhci_usb_endpoint_maxp(urb->dev, urb->ep); total_packet_count = DIV_ROUND_UP(td_total_len, maxp); /* Queueing functions don't count the current TRB into transferred */ @@ -3525,7 +3546,7 @@ static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len, u32 new_buff_len; size_t len; - max_pkt = usb_endpoint_maxp(&urb->ep->desc); + max_pkt = xhci_usb_endpoint_maxp(urb->dev, urb->ep); unalign = (enqd_len + *trb_buff_len) % max_pkt; /* we got lucky, last normal TRB data on segment is packet aligned */ @@ -3776,7 +3797,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, * 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)) { + if (last_trb_on_seg(ep_ring->enq_seg, 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); @@ -3945,6 +3966,16 @@ static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, return total_packet_count - 1; } +/* Returns the Isochronous Scheduling Threshold in Microframes. 1 Frame is 8 Microframes. */ +static int xhci_ist_microframes(struct xhci_hcd *xhci) +{ + int ist = HCS_IST_VALUE(xhci->hcs_params2); + + if (xhci->hcs_params2 & HCS_IST_UNIT) + ist *= 8; + return ist; +} + /* * Calculates Frame ID field of the isochronous TRB identifies the * target frame that the Interval associated with this Isochronous @@ -3964,17 +3995,7 @@ static int xhci_get_isoc_frame_id(struct xhci_hcd *xhci, else start_frame = (urb->start_frame + index * urb->interval) >> 3; - /* Isochronous Scheduling Threshold (IST, bits 0~3 in HCSPARAMS2): - * - * If bit [3] of IST is cleared to '0', software can add a TRB no - * later than IST[2:0] Microframes before that TRB is scheduled to - * be executed. - * If bit [3] of IST is set to '1', software can add a TRB no later - * than IST[2:0] Frames before that TRB is scheduled to be executed. - */ - ist = HCS_IST(xhci->hcs_params2) & 0x7; - if (HCS_IST(xhci->hcs_params2) & (1 << 3)) - ist <<= 3; + ist = xhci_ist_microframes(xhci); /* Software shall not schedule an Isoch TD with a Frame ID value that * is less than the Start Frame ID or greater than the End Frame ID, @@ -4096,7 +4117,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, addr = start_addr + urb->iso_frame_desc[i].offset; td_len = urb->iso_frame_desc[i].length; td_remain_len = td_len; - max_pkt = usb_endpoint_maxp(&urb->ep->desc); + max_pkt = xhci_usb_endpoint_maxp(urb->dev, urb->ep); total_pkt_count = DIV_ROUND_UP(td_len, max_pkt); /* A zero-length transfer still involves at least one packet. */ @@ -4119,7 +4140,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* use SIA as default, if frame id is used overwrite it */ sia_frame_id = TRB_SIA; if (!(urb->transfer_flags & URB_ISO_ASAP) && - HCC_CFC(xhci->hcc_params)) { + (xhci->hcc_params & HCC_CFC)) { frame_id = xhci_get_isoc_frame_id(xhci, urb, i); if (frame_id >= 0) sia_frame_id = TRB_FRAME_ID(frame_id); @@ -4203,7 +4224,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } /* store the next frame id */ - if (HCC_CFC(xhci->hcc_params)) + if (xhci->hcc_params & HCC_CFC) xep->next_frame_id = urb->start_frame + num_tds * urb->interval; if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) { @@ -4228,7 +4249,7 @@ cleanup: */ urb_priv->td[0].end_trb = ep_ring->enqueue; /* Every TRB except the first & last will have its cycle bit flipped. */ - td_to_noop(&urb_priv->td[0], true); + td_to_noop(xhci, xep, &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].start_trb; @@ -4282,7 +4303,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, check_interval(urb, ep_ctx); /* Calculate the start frame and put it in urb->start_frame. */ - if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) { + if ((xhci->hcc_params & HCC_CFC) && !list_empty(&ep_ring->td_list)) { if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) { urb->start_frame = xep->next_frame_id; goto skip_start_over; @@ -4295,9 +4316,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, * Round up to the next frame and consider the time before trb really * gets scheduled by hardare. */ - ist = HCS_IST(xhci->hcs_params2) & 0x7; - if (HCS_IST(xhci->hcs_params2) & (1 << 3)) - ist <<= 3; + ist = xhci_ist_microframes(xhci); start_frame += ist + XHCI_CFC_DELAY; start_frame = roundup(start_frame, 8); @@ -4338,7 +4357,8 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, if ((xhci->xhc_state & XHCI_STATE_DYING) || (xhci->xhc_state & XHCI_STATE_HALTED)) { - xhci_dbg(xhci, "xHCI dying or halted, can't queue_command\n"); + xhci_dbg(xhci, "xHCI dying or halted, can't queue_command. state: 0x%x\n", + xhci->xhc_state); return -ESHUTDOWN; } @@ -4414,6 +4434,17 @@ int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, command_must_succeed); } +/* Queue a get root hub port bandwidth command TRB */ +int xhci_queue_get_port_bw(struct xhci_hcd *xhci, + struct xhci_command *cmd, dma_addr_t in_ctx_ptr, + u8 dev_speed, bool command_must_succeed) +{ + return queue_command(xhci, cmd, lower_32_bits(in_ctx_ptr), + upper_32_bits(in_ctx_ptr), 0, + TRB_TYPE(TRB_GET_BW) | DEV_SPEED_FOR_TRB(dev_speed), + command_must_succeed); +} + /* Queue an evaluate context command TRB */ int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd, dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed) diff --git a/drivers/usb/host/xhci-rzg3e-regs.h b/drivers/usb/host/xhci-rzg3e-regs.h new file mode 100644 index 000000000000..7a244a47b882 --- /dev/null +++ b/drivers/usb/host/xhci-rzg3e-regs.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __XHCI_RZG3E_H +#define __XHCI_RZG3E_H + +#define RZG3E_USB3_HOST_INTEN 0x1044 /* Interrupt Enable */ +#define RZG3E_USB3_HOST_U3P0PIPESC(x) (0x10c0 + (x) * 4) /* PIPE Status and Control Register */ + +#define RZG3E_USB3_HOST_INTEN_XHC BIT(0) +#define RZG3E_USB3_HOST_INTEN_HSE BIT(2) +#define RZG3E_USB3_HOST_INTEN_ENA (RZG3E_USB3_HOST_INTEN_XHC | RZG3E_USB3_HOST_INTEN_HSE) + +#endif /* __XHCI_RZG3E_H */ diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c new file mode 100644 index 000000000000..23153e136d4b --- /dev/null +++ b/drivers/usb/host/xhci-sideband.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * xHCI host controller sideband support + * + * Copyright (c) 2023-2025, Intel Corporation. + * + * Author: Mathias Nyman + */ + +#include <linux/usb/xhci-sideband.h> +#include <linux/dma-direct.h> + +#include "xhci.h" + +/* sideband internal helpers */ +static struct sg_table * +xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring) +{ + struct xhci_segment *seg; + struct sg_table *sgt; + unsigned int n_pages; + struct page **pages; + struct device *dev; + size_t sz; + int i; + + dev = xhci_to_hcd(sb->xhci)->self.sysdev; + sz = ring->num_segs * TRB_SEGMENT_SIZE; + n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT; + pages = kvmalloc_objs(struct page *, n_pages); + if (!pages) + return NULL; + + sgt = kzalloc_obj(*sgt); + if (!sgt) { + kvfree(pages); + return NULL; + } + + seg = ring->first_seg; + if (!seg) + goto err; + /* + * Rings can potentially have multiple segments, create an array that + * carries page references to allocated segments. Utilize the + * sg_alloc_table_from_pages() to create the sg table, and to ensure + * that page links are created. + */ + for (i = 0; i < ring->num_segs; i++) { + dma_get_sgtable(dev, sgt, seg->trbs, seg->dma, + TRB_SEGMENT_SIZE); + pages[i] = sg_page(sgt->sgl); + sg_free_table(sgt); + seg = seg->next; + } + + if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) + goto err; + + /* + * Save first segment dma address to sg dma_address field for the sideband + * client to have access to the IOVA of the ring. + */ + sg_dma_address(sgt->sgl) = ring->first_seg->dma; + + return sgt; + +err: + kvfree(pages); + kfree(sgt); + + return NULL; +} + +/* Caller must hold sb->mutex */ +static void +__xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep) +{ + lockdep_assert_held(&sb->mutex); + + /* + * Issue a stop endpoint command when an endpoint is removed. + * The stop ep cmd handler will handle the ring cleanup. + */ + xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL); + + ep->sideband = NULL; + sb->eps[ep->ep_index] = NULL; +} + +/* Caller must hold sb->mutex */ +static void +__xhci_sideband_remove_interrupter(struct xhci_sideband *sb) +{ + lockdep_assert_held(&sb->mutex); + + if (!sb->ir) + return; + + xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir); + sb->ir = NULL; +} + +/* sideband api functions */ + +/** + * xhci_sideband_notify_ep_ring_free - notify client of xfer ring free + * @sb: sideband instance for this usb device + * @ep_index: usb endpoint index + * + * Notifies the xHCI sideband client driver of a xHCI transfer ring free + * routine. This will allow for the client to ensure that all transfers + * are completed. + * + * The callback should be synchronous, as the ring free happens after. + */ +void xhci_sideband_notify_ep_ring_free(struct xhci_sideband *sb, + unsigned int ep_index) +{ + struct xhci_sideband_event evt; + + evt.type = XHCI_SIDEBAND_XFER_RING_FREE; + evt.evt_data = &ep_index; + + if (sb->notify_client) + sb->notify_client(sb->intf, &evt); +} +EXPORT_SYMBOL_GPL(xhci_sideband_notify_ep_ring_free); + +/** + * xhci_sideband_add_endpoint - add endpoint to sideband access list + * @sb: sideband instance for this usb device + * @host_ep: usb host endpoint + * + * Adds an endpoint to the list of sideband accessed endpoints for this usb + * device. + * After an endpoint is added the sideband client can get the endpoint transfer + * ring buffer by calling xhci_sideband_endpoint_buffer() + * + * Return: 0 on success, negative error otherwise. + */ +int +xhci_sideband_add_endpoint(struct xhci_sideband *sb, + struct usb_host_endpoint *host_ep) +{ + struct xhci_virt_ep *ep; + unsigned int ep_index; + + guard(mutex)(&sb->mutex); + + if (!sb->vdev) + return -ENODEV; + + ep_index = xhci_get_endpoint_index(&host_ep->desc); + ep = &sb->vdev->eps[ep_index]; + + if (ep->ep_state & EP_HAS_STREAMS) + return -EINVAL; + + /* + * Note, we don't know the DMA mask of the audio DSP device, if its + * smaller than for xhci it won't be able to access the endpoint ring + * buffer. This could be solved by not allowing the audio class driver + * to add the endpoint the normal way, but instead offload it immediately, + * and let this function add the endpoint and allocate the ring buffer + * with the smallest common DMA mask + */ + if (sb->eps[ep_index] || ep->sideband) + return -EBUSY; + + ep->sideband = sb; + sb->eps[ep_index] = ep; + + return 0; +} +EXPORT_SYMBOL_GPL(xhci_sideband_add_endpoint); + +/** + * xhci_sideband_remove_endpoint - remove endpoint from sideband access list + * @sb: sideband instance for this usb device + * @host_ep: usb host endpoint + * + * Removes an endpoint from the list of sideband accessed endpoints for this usb + * device. + * sideband client should no longer touch the endpoint transfer buffer after + * calling this. + * + * Return: 0 on success, negative error otherwise. + */ +int +xhci_sideband_remove_endpoint(struct xhci_sideband *sb, + struct usb_host_endpoint *host_ep) +{ + struct xhci_virt_ep *ep; + unsigned int ep_index; + + guard(mutex)(&sb->mutex); + + ep_index = xhci_get_endpoint_index(&host_ep->desc); + ep = sb->eps[ep_index]; + + if (!ep || !ep->sideband || ep->sideband != sb) + return -ENODEV; + + __xhci_sideband_remove_endpoint(sb, ep); + + return 0; +} +EXPORT_SYMBOL_GPL(xhci_sideband_remove_endpoint); + +int +xhci_sideband_stop_endpoint(struct xhci_sideband *sb, + struct usb_host_endpoint *host_ep) +{ + struct xhci_virt_ep *ep; + unsigned int ep_index; + + ep_index = xhci_get_endpoint_index(&host_ep->desc); + ep = sb->eps[ep_index]; + + if (!ep || !ep->sideband || ep->sideband != sb) + return -EINVAL; + + return xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(xhci_sideband_stop_endpoint); + +/** + * xhci_sideband_get_endpoint_buffer - gets the endpoint transfer buffer address + * @sb: sideband instance for this usb device + * @host_ep: usb host endpoint + * + * Returns the address of the endpoint buffer where xHC controller reads queued + * transfer TRBs from. This is the starting address of the ringbuffer where the + * sideband client should write TRBs to. + * + * Caller needs to free the returned sg_table + * + * Return: struct sg_table * if successful. NULL otherwise. + */ +struct sg_table * +xhci_sideband_get_endpoint_buffer(struct xhci_sideband *sb, + struct usb_host_endpoint *host_ep) +{ + struct xhci_virt_ep *ep; + unsigned int ep_index; + + ep_index = xhci_get_endpoint_index(&host_ep->desc); + ep = sb->eps[ep_index]; + + if (!ep || !ep->ring || !ep->sideband || ep->sideband != sb) + return NULL; + + return xhci_ring_to_sgtable(sb, ep->ring); +} +EXPORT_SYMBOL_GPL(xhci_sideband_get_endpoint_buffer); + +/** + * xhci_sideband_get_event_buffer - return the event buffer for this device + * @sb: sideband instance for this usb device + * + * If a secondary xhci interupter is set up for this usb device then this + * function returns the address of the event buffer where xHC writes + * the transfer completion events. + * + * Caller needs to free the returned sg_table + * + * Return: struct sg_table * if successful. NULL otherwise. + */ +struct sg_table * +xhci_sideband_get_event_buffer(struct xhci_sideband *sb) +{ + if (!sb || !sb->ir) + return NULL; + + return xhci_ring_to_sgtable(sb, sb->ir->event_ring); +} +EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer); + +/** + * xhci_sideband_check - check the existence of active sidebands + * @hcd: the host controller driver associated with the target host controller + * + * Allow other drivers, such as usb controller driver, to check if there are + * any sideband activity on the host controller. This information could be used + * for power management or other forms of resource management. The caller should + * ensure downstream usb devices are all marked as "offload_pm_locked" to ensure + * the correctness of the return value. + * + * Returns true on any active sideband existence, false otherwise. + */ +bool xhci_sideband_check(struct usb_hcd *hcd) +{ + struct usb_device *udev = hcd->self.root_hub; + bool active; + + usb_lock_device(udev); + active = usb_offload_check(udev); + usb_unlock_device(udev); + + return active; +} +EXPORT_SYMBOL_GPL(xhci_sideband_check); + +/** + * xhci_sideband_create_interrupter - creates a new interrupter for this sideband + * @sb: sideband instance for this usb device + * @num_seg: number of event ring segments to allocate + * @ip_autoclear: IP autoclearing support such as MSI implemented + * + * Sets up a xhci interrupter that can be used for this sideband accessed usb + * device. Transfer events for this device can be routed to this interrupters + * event ring by setting the 'Interrupter Target' field correctly when queueing + * the transfer TRBs. + * Once this interrupter is created the interrupter target ID can be obtained + * by calling xhci_sideband_interrupter_id() + * + * Returns 0 on success, negative error otherwise + */ +int +xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg, + bool ip_autoclear, u32 imod_interval, int intr_num) +{ + if (!sb || !sb->xhci) + return -ENODEV; + + guard(mutex)(&sb->mutex); + + if (!sb->vdev) + return -ENODEV; + + if (sb->ir) + return -EBUSY; + + sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci), + num_seg, imod_interval, + intr_num); + if (!sb->ir) + return -ENOMEM; + + sb->ir->ip_autoclear = ip_autoclear; + + return 0; +} +EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter); + +/** + * xhci_sideband_remove_interrupter - remove the interrupter from a sideband + * @sb: sideband instance for this usb device + * + * Removes a registered interrupt for a sideband. This would allow for other + * sideband users to utilize this interrupter. + */ +void +xhci_sideband_remove_interrupter(struct xhci_sideband *sb) +{ + if (!sb) + return; + + guard(mutex)(&sb->mutex); + + __xhci_sideband_remove_interrupter(sb); +} +EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter); + +/** + * xhci_sideband_interrupter_id - return the interrupter target id + * @sb: sideband instance for this usb device + * + * If a secondary xhci interrupter is set up for this usb device then this + * function returns the ID used by the interrupter. The sideband client + * needs to write this ID to the 'Interrupter Target' field of the transfer TRBs + * it queues on the endpoints transfer ring to ensure transfer completion event + * are written by xHC to the correct interrupter event ring. + * + * Returns interrupter id on success, negative error othgerwise + */ +int +xhci_sideband_interrupter_id(struct xhci_sideband *sb) +{ + if (!sb || !sb->ir) + return -ENODEV; + + return sb->ir->intr_num; +} +EXPORT_SYMBOL_GPL(xhci_sideband_interrupter_id); + +/** + * xhci_sideband_register - register a sideband for a usb device + * @intf: usb interface associated with the sideband device + * + * Allows for clients to utilize XHCI interrupters and fetch transfer and event + * ring parameters for executing data transfers. + * + * Return: pointer to a new xhci_sideband instance if successful. NULL otherwise. + */ +struct xhci_sideband * +xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type, + int (*notify_client)(struct usb_interface *intf, + struct xhci_sideband_event *evt)) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_virt_device *vdev; + struct xhci_sideband *sb; + + /* + * Make sure the usb device is connected to a xhci controller. Fail + * registration if the type is anything other than XHCI_SIDEBAND_VENDOR, + * as this is the only type that is currently supported by xhci-sideband. + */ + if (!udev->slot_id || type != XHCI_SIDEBAND_VENDOR) + return NULL; + + sb = kzalloc_node(sizeof(*sb), GFP_KERNEL, dev_to_node(hcd->self.sysdev)); + if (!sb) + return NULL; + + mutex_init(&sb->mutex); + + /* check this device isn't already controlled via sideband */ + spin_lock_irq(&xhci->lock); + + vdev = xhci->devs[udev->slot_id]; + + if (!vdev || vdev->sideband) { + xhci_warn(xhci, "XHCI sideband for slot %d already in use\n", + udev->slot_id); + spin_unlock_irq(&xhci->lock); + kfree(sb); + return NULL; + } + + sb->xhci = xhci; + sb->vdev = vdev; + sb->intf = intf; + sb->type = type; + sb->notify_client = notify_client; + vdev->sideband = sb; + + spin_unlock_irq(&xhci->lock); + + return sb; +} +EXPORT_SYMBOL_GPL(xhci_sideband_register); + +/** + * xhci_sideband_unregister - unregister sideband access to a usb device + * @sb: sideband instance to be unregistered + * + * Unregisters sideband access to a usb device and frees the sideband + * instance. + * After this the endpoint and interrupter event buffers should no longer + * be accessed via sideband. The xhci driver can now take over handling + * the buffers. + */ +void +xhci_sideband_unregister(struct xhci_sideband *sb) +{ + struct xhci_virt_device *vdev; + struct xhci_hcd *xhci; + int i; + + if (!sb) + return; + + xhci = sb->xhci; + + scoped_guard(mutex, &sb->mutex) { + vdev = sb->vdev; + if (!vdev) + return; + + for (i = 0; i < EP_CTX_PER_DEV; i++) + if (sb->eps[i]) + __xhci_sideband_remove_endpoint(sb, sb->eps[i]); + + __xhci_sideband_remove_interrupter(sb); + + sb->vdev = NULL; + } + + spin_lock_irq(&xhci->lock); + sb->xhci = NULL; + vdev->sideband = NULL; + spin_unlock_irq(&xhci->lock); + + kfree(sb); +} +EXPORT_SYMBOL_GPL(xhci_sideband_unregister); +MODULE_DESCRIPTION("xHCI sideband driver for secondary interrupter management"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 22dc86fb5254..d2214d309e96 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -155,6 +155,8 @@ #define FW_IOCTL_TYPE_SHIFT 24 #define FW_IOCTL_CFGTBL_READ 17 +#define WAKE_IRQ_START_INDEX 2 + struct tegra_xusb_fw_header { __le32 boot_loadaddr_in_imem; __le32 boot_codedfi_offset; @@ -228,6 +230,7 @@ struct tegra_xusb_soc { unsigned int num_supplies; const struct tegra_xusb_phy_type *phy_types; unsigned int num_types; + unsigned int max_num_wakes; const struct tegra_xusb_context_soc *context; struct { @@ -263,6 +266,7 @@ struct tegra_xusb { int xhci_irq; int mbox_irq; int padctl_irq; + int *wake_irqs; void __iomem *ipfs_base; void __iomem *fpci_base; @@ -313,6 +317,7 @@ struct tegra_xusb { bool suspended; struct tegra_xusb_context context; u8 lp0_utmi_pad_mask; + int num_wakes; }; static struct hc_driver __read_mostly tegra_xhci_hc_driver; @@ -1352,18 +1357,15 @@ static void tegra_xhci_id_work(struct work_struct *work) dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode)); - mutex_lock(&tegra->lock); - if (tegra->host_mode) phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST); else phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); - mutex_unlock(&tegra->lock); - tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(tegra->padctl, tegra->otg_usb2_port); + pm_runtime_get_sync(tegra->dev); if (tegra->host_mode) { /* switch to host mode */ if (tegra->otg_usb3_port >= 0) { @@ -1400,6 +1402,7 @@ static void tegra_xhci_id_work(struct work_struct *work) tegra_xhci_set_port_power(tegra, true, false); } + pm_runtime_put_autosuspend(tegra->dev); } #if IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP) @@ -1479,7 +1482,7 @@ static int tegra_xhci_id_notify(struct notifier_block *nb, tegra->otg_usb2_port = tegra_xusb_get_usb2_port(tegra, usbphy); - tegra->host_mode = (usbphy->last_event == USB_EVENT_ID) ? true : false; + tegra->host_mode = usbphy->last_event == USB_EVENT_ID; schedule_work(&tegra->id_work); @@ -1534,6 +1537,47 @@ static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra) otg_set_host(tegra->usbphy[i]->otg, NULL); } +static int tegra_xusb_setup_wakeup(struct platform_device *pdev, struct tegra_xusb *tegra) +{ + unsigned int i; + + if (tegra->soc->max_num_wakes == 0) + return 0; + + tegra->wake_irqs = devm_kcalloc(tegra->dev, + tegra->soc->max_num_wakes, + sizeof(*tegra->wake_irqs), GFP_KERNEL); + if (!tegra->wake_irqs) + return -ENOMEM; + + /* + * USB wake events are independent of each other, so it is not necessary for a platform + * to utilize all wake-up events supported for a given device. The USB host can operate + * even if wake-up events are not defined or fail to be configured. Therefore, we only + * return critical errors, such as -ENOMEM. + */ + for (i = 0; i < tegra->soc->max_num_wakes; i++) { + struct irq_data *data; + + tegra->wake_irqs[i] = platform_get_irq_optional(pdev, i + WAKE_IRQ_START_INDEX); + if (tegra->wake_irqs[i] < 0) + break; + + data = irq_get_irq_data(tegra->wake_irqs[i]); + if (!data) { + dev_warn(tegra->dev, "get wake event %d irq data fail\n", i); + break; + } + + irq_set_irq_type(tegra->wake_irqs[i], irqd_get_trigger_type(data)); + } + + tegra->num_wakes = i; + dev_dbg(tegra->dev, "setup %d wake events\n", tegra->num_wakes); + + return 0; +} + static int tegra_xusb_probe(struct platform_device *pdev) { struct tegra_xusb *tegra; @@ -1584,6 +1628,10 @@ static int tegra_xusb_probe(struct platform_device *pdev) if (tegra->mbox_irq < 0) return tegra->mbox_irq; + err = tegra_xusb_setup_wakeup(pdev, tegra); + if (err) + return err; + tegra->padctl = tegra_xusb_padctl_get(&pdev->dev); if (IS_ERR(tegra->padctl)) return PTR_ERR(tegra->padctl); @@ -1966,7 +2014,7 @@ static bool xhci_hub_ports_suspended(struct xhci_hub *hub) u32 value; for (i = 0; i < hub->num_ports; i++) { - value = readl(hub->ports[i]->addr); + value = xhci_portsc_readl(hub->ports[i]); if ((value & PORT_PE) == 0) continue; @@ -2092,7 +2140,7 @@ static void tegra_xhci_enable_phy_sleepwalk_wake(struct tegra_xusb *tegra) if (!is_host_mode_phy(tegra, i, j)) continue; - portsc = readl(rhub->ports[index]->addr); + portsc = xhci_portsc_readl(rhub->ports[index]); speed = tegra_xhci_portsc_to_speed(tegra, portsc); tegra_xusb_padctl_enable_phy_sleepwalk(padctl, phy, speed); tegra_xusb_padctl_enable_phy_wake(padctl, phy); @@ -2162,11 +2210,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; @@ -2187,7 +2235,7 @@ static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime) for (i = 0; i < xhci->usb2_rhub.num_ports; i++) { if (!xhci->usb2_rhub.ports[i]) continue; - portsc = readl(xhci->usb2_rhub.ports[i]->addr); + portsc = xhci_portsc_readl(xhci->usb2_rhub.ports[i]); tegra->lp0_utmi_pad_mask &= ~BIT(i); if (((portsc & PORT_PLS_MASK) == XDEV_U3) || ((portsc & DEV_SPEED_MASK) == XDEV_FS)) tegra->lp0_utmi_pad_mask |= BIT(i); @@ -2232,11 +2280,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; @@ -2287,7 +2335,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; @@ -2352,8 +2400,13 @@ out: pm_runtime_disable(dev); if (device_may_wakeup(dev)) { + unsigned int i; + if (enable_irq_wake(tegra->padctl_irq)) dev_err(dev, "failed to enable padctl wakes\n"); + + for (i = 0; i < tegra->num_wakes; i++) + enable_irq_wake(tegra->wake_irqs[i]); } } @@ -2381,8 +2434,13 @@ static __maybe_unused int tegra_xusb_resume(struct device *dev) } if (device_may_wakeup(dev)) { + unsigned int i; + if (disable_irq_wake(tegra->padctl_irq)) dev_err(dev, "failed to disable padctl wakes\n"); + + for (i = 0; i < tegra->num_wakes; i++) + disable_irq_wake(tegra->wake_irqs[i]); } tegra->suspended = false; mutex_unlock(&tegra->lock); @@ -2504,7 +2562,9 @@ static const struct tegra_xusb_soc tegra124_soc = { .smi_intr = XUSB_CFG_ARU_SMI_INTR, }, }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) || IS_ENABLED(CONFIG_ARCH_TEGRA_132_SOC) MODULE_FIRMWARE("nvidia/tegra124/xusb.bin"); +#endif static const char * const tegra210_supply_names[] = { "dvddio-pex", @@ -2542,11 +2602,15 @@ static const struct tegra_xusb_soc tegra210_soc = { .smi_intr = XUSB_CFG_ARU_SMI_INTR, }, }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) MODULE_FIRMWARE("nvidia/tegra210/xusb.bin"); +#endif static const char * const tegra186_supply_names[] = { }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) MODULE_FIRMWARE("nvidia/tegra186/xusb.bin"); +#endif static const struct tegra_xusb_phy_type tegra186_phy_types[] = { { .name = "usb3", .num = 3, }, @@ -2619,7 +2683,9 @@ static const struct tegra_xusb_soc tegra194_soc = { }, .lpm_support = true, }; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) MODULE_FIRMWARE("nvidia/tegra194/xusb.bin"); +#endif static const struct tegra_xusb_soc_ops tegra234_ops = { .mbox_reg_readl = &bar2_readl, @@ -2633,6 +2699,7 @@ static const struct tegra_xusb_soc tegra234_soc = { .num_supplies = ARRAY_SIZE(tegra194_supply_names), .phy_types = tegra194_phy_types, .num_types = ARRAY_SIZE(tegra194_phy_types), + .max_num_wakes = 7, .context = &tegra186_xusb_context, .ports = { .usb3 = { .offset = 0, .count = 4, }, @@ -2709,7 +2776,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, while (i--) { if (!test_bit(i, &bus_state->resuming_ports)) continue; - portsc = readl(ports[i]->addr); + portsc = xhci_portsc_readl(ports[i]); if ((portsc & PORT_PLS_MASK) == XDEV_RESUME) tegra_phy_xusb_utmi_pad_power_on( tegra_xusb_get_phy(tegra, "usb2", (int) i)); @@ -2727,7 +2794,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, if (!index || index > rhub->num_ports) return -EPIPE; ports = rhub->ports; - portsc = readl(ports[port]->addr); + portsc = xhci_portsc_readl(ports[port]); if (portsc & PORT_CONNECT) tegra_phy_xusb_utmi_pad_power_on(phy); } @@ -2746,7 +2813,7 @@ static int tegra_xhci_hub_control(struct usb_hcd *hcd, u16 type_req, u16 value, if ((type_req == ClearPortFeature) && (value == USB_PORT_FEAT_C_CONNECTION)) { ports = rhub->ports; - portsc = readl(ports[port]->addr); + portsc = xhci_portsc_readl(ports[port]); if (!(portsc & PORT_CONNECT)) { /* We don't suspend the PAD while HNP role swap happens on the OTG * port diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index bfb5c5c17012..724cba2dbb78 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -71,29 +71,20 @@ DEFINE_EVENT(xhci_log_msg, xhci_dbg_ring_expansion, ); DECLARE_EVENT_CLASS(xhci_log_ctx, - TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, - unsigned int ep_num), - TP_ARGS(xhci, ctx, ep_num), + TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx), + TP_ARGS(xhci, ctx), TP_STRUCT__entry( __field(int, ctx_64) __field(unsigned, ctx_type) __field(dma_addr_t, ctx_dma) __field(u8 *, ctx_va) - __field(unsigned, ctx_ep_num) - __dynamic_array(u32, ctx_data, - ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 8) * - ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1)) ), TP_fast_assign( - __entry->ctx_64 = HCC_64BYTE_CONTEXT(xhci->hcc_params); + __entry->ctx_64 = xhci->hcc_params & HCC_64BYTE_CONTEXT; __entry->ctx_type = ctx->type; __entry->ctx_dma = ctx->dma; __entry->ctx_va = ctx->bytes; - __entry->ctx_ep_num = ep_num; - memcpy(__get_dynamic_array(ctx_data), ctx->bytes, - ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) * - ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1)); ), TP_printk("ctx_64=%d, ctx_type=%u, ctx_dma=@%llx, ctx_va=@%p", __entry->ctx_64, __entry->ctx_type, @@ -102,9 +93,8 @@ DECLARE_EVENT_CLASS(xhci_log_ctx, ); DEFINE_EVENT(xhci_log_ctx, xhci_address_ctx, - TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, - unsigned int ep_num), - TP_ARGS(xhci, ctx, ep_num) + TP_PROTO(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx), + TP_ARGS(xhci, ctx) ); DECLARE_EVENT_CLASS(xhci_log_trb, @@ -541,23 +531,23 @@ DEFINE_EVENT(xhci_log_ring, xhci_inc_deq, ); DECLARE_EVENT_CLASS(xhci_log_portsc, - TP_PROTO(struct xhci_port *port, u32 portsc), - TP_ARGS(port, portsc), - TP_STRUCT__entry( - __field(u32, busnum) - __field(u32, portnum) - __field(u32, portsc) - ), - TP_fast_assign( - __entry->busnum = port->rhub->hcd->self.busnum; - __entry->portnum = port->hcd_portnum; - __entry->portsc = portsc; - ), - TP_printk("port %d-%d: %s", - __entry->busnum, - __entry->portnum, - xhci_decode_portsc(__get_buf(XHCI_MSG_MAX), __entry->portsc) - ) + TP_PROTO(struct xhci_port *port, u32 portsc), + TP_ARGS(port, portsc), + TP_STRUCT__entry( + __field(u32, busnum) + __field(u32, portnum) + __field(u32, portsc) + ), + TP_fast_assign( + __entry->busnum = port->rhub->hcd->self.busnum; + __entry->portnum = port->hcd_portnum + 1; + __entry->portsc = portsc; + ), + TP_printk("port %d-%d: %s", + __entry->busnum, + __entry->portnum, + xhci_decode_portsc(__get_buf(XHCI_MSG_MAX), __entry->portsc) + ) ); DEFINE_EVENT(xhci_log_portsc, xhci_handle_port_status, @@ -575,6 +565,11 @@ DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data, TP_ARGS(port, portsc) ); +DEFINE_EVENT(xhci_log_portsc, xhci_portsc_writel, + TP_PROTO(struct xhci_port *port, u32 portsc), + TP_ARGS(port, portsc) +); + DECLARE_EVENT_CLASS(xhci_log_doorbell, TP_PROTO(u32 slot, u32 doorbell), TP_ARGS(slot, doorbell), diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1a90ebc8a30e..a54f5b57f205 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -20,6 +20,7 @@ #include <linux/string_choices.h> #include <linux/dmi.h> #include <linux/dma-mapping.h> +#include <linux/usb/xhci-sideband.h> #include "xhci.h" #include "xhci-trace.h" @@ -40,6 +41,19 @@ static unsigned long long quirks; module_param(quirks, ullong, S_IRUGO); MODULE_PARM_DESC(quirks, "Bit flags for quirks to be enabled as default"); +void xhci_portsc_writel(struct xhci_port *port, u32 val) +{ + trace_xhci_portsc_writel(port, val); + writel(val, &port->port_reg->portsc); +} +EXPORT_SYMBOL_GPL(xhci_portsc_writel); + +u32 xhci_portsc_readl(struct xhci_port *port) +{ + return readl(&port->port_reg->portsc); +} +EXPORT_SYMBOL_GPL(xhci_portsc_readl); + static bool td_on_ring(struct xhci_td *td, struct xhci_ring *ring) { struct xhci_segment *seg; @@ -84,29 +98,6 @@ int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us) } /* - * xhci_handshake_check_state - same as xhci_handshake but takes an additional - * exit_state parameter, and bails out with an error immediately when xhc_state - * has exit_state flag set. - */ -int xhci_handshake_check_state(struct xhci_hcd *xhci, void __iomem *ptr, - u32 mask, u32 done, int usec, unsigned int exit_state) -{ - u32 result; - int ret; - - ret = readl_poll_timeout_atomic(ptr, result, - (result & mask) == done || - result == U32_MAX || - xhci->xhc_state & exit_state, - 1, usec); - - if (result == U32_MAX || xhci->xhc_state & exit_state) - return -ENODEV; - - return ret; -} - -/* * Disable interrupts and begin the xHCI halting process. */ void xhci_quiesce(struct xhci_hcd *xhci) @@ -143,7 +134,8 @@ int xhci_halt(struct xhci_hcd *xhci) ret = xhci_handshake(&xhci->op_regs->status, STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC); if (ret) { - xhci_warn(xhci, "Host halt failed, %d\n", ret); + if (!(xhci->xhc_state & XHCI_STATE_DYING)) + xhci_warn(xhci, "Host halt failed, %d\n", ret); return ret; } @@ -202,7 +194,8 @@ int xhci_reset(struct xhci_hcd *xhci, u64 timeout_us) state = readl(&xhci->op_regs->status); if (state == ~(u32)0) { - xhci_warn(xhci, "Host not accessible, reset failed.\n"); + if (!(xhci->xhc_state & XHCI_STATE_DYING)) + xhci_warn(xhci, "Host not accessible, reset failed.\n"); return -ENODEV; } @@ -226,8 +219,7 @@ int xhci_reset(struct xhci_hcd *xhci, u64 timeout_us) if (xhci->quirks & XHCI_INTEL_HOST) udelay(1000); - ret = xhci_handshake_check_state(xhci, &xhci->op_regs->command, - CMD_RESET, 0, timeout_us, XHCI_STATE_REMOVING); + ret = xhci_handshake(&xhci->op_regs->command, CMD_RESET, 0, timeout_us); if (ret) return ret; @@ -258,7 +250,6 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) struct iommu_domain *domain; int err, i; u64 val; - u32 intrs; /* * Some Renesas controllers get into a weird state if they are @@ -299,10 +290,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) if (upper_32_bits(val)) xhci_write_64(xhci, 0, &xhci->op_regs->cmd_ring); - intrs = min_t(u32, HCS_MAX_INTRS(xhci->hcs_params1), - ARRAY_SIZE(xhci->run_regs->ir_set)); - - for (i = 0; i < intrs; i++) { + for (i = 0; i < xhci->max_interrupters; i++) { struct xhci_intr_reg __iomem *ir; ir = &xhci->run_regs->ir_set[i]; @@ -322,28 +310,38 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) xhci_info(xhci, "Fault detected\n"); } -static int xhci_enable_interrupter(struct xhci_interrupter *ir) +int xhci_enable_interrupter(struct xhci_interrupter *ir) { u32 iman; if (!ir || !ir->ir_set) return -EINVAL; - iman = readl(&ir->ir_set->irq_pending); - writel(ER_IRQ_ENABLE(iman), &ir->ir_set->irq_pending); + iman = readl(&ir->ir_set->iman); + iman &= ~IMAN_IP; + iman |= IMAN_IE; + writel(iman, &ir->ir_set->iman); + /* Read operation to guarantee the write has been flushed from posted buffers */ + readl(&ir->ir_set->iman); return 0; } -static int xhci_disable_interrupter(struct xhci_interrupter *ir) +int xhci_disable_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { u32 iman; if (!ir || !ir->ir_set) return -EINVAL; - iman = readl(&ir->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(iman), &ir->ir_set->irq_pending); + iman = readl(&ir->ir_set->iman); + iman &= ~IMAN_IP; + iman &= ~IMAN_IE; + writel(iman, &ir->ir_set->iman); + + iman = readl(&ir->ir_set->iman); + if (iman & IMAN_IP) + xhci_dbg(xhci, "%s: Interrupt pending\n", __func__); return 0; } @@ -354,13 +352,16 @@ int xhci_set_interrupter_moderation(struct xhci_interrupter *ir, { u32 imod; - if (!ir || !ir->ir_set || imod_interval > U16_MAX * 250) + if (!ir || !ir->ir_set) return -EINVAL; - imod = readl(&ir->ir_set->irq_control); - imod &= ~ER_IRQ_INTERVAL_MASK; - imod |= (imod_interval / 250) & ER_IRQ_INTERVAL_MASK; - writel(imod, &ir->ir_set->irq_control); + /* IMODI value in IMOD register is in 250ns increments */ + imod_interval = umin(imod_interval / 250, IMODI_MASK); + + imod = readl(&ir->ir_set->imod); + imod &= ~IMODI_MASK; + imod |= imod_interval; + writel(imod, &ir->ir_set->imod); return 0; } @@ -373,7 +374,7 @@ static void compliance_mode_recovery(struct timer_list *t) u32 temp; int i; - xhci = from_timer(xhci, t, comp_mode_recovery_timer); + xhci = timer_container_of(xhci, t, comp_mode_recovery_timer); rhub = &xhci->usb3_rhub; hcd = rhub->hcd; @@ -381,7 +382,7 @@ static void compliance_mode_recovery(struct timer_list *t) return; for (i = 0; i < rhub->num_ports; i++) { - temp = readl(rhub->ports[i]->addr); + temp = xhci_portsc_readl(rhub->ports[i]); if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) { /* * Compliance Mode Detected. Letting USB Core @@ -460,24 +461,115 @@ static int xhci_all_ports_seen_u0(struct xhci_hcd *xhci) return (xhci->port_status_u0 == ((1 << xhci->usb3_rhub.num_ports) - 1)); } +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); +} + +static void xhci_enable_max_dev_slots(struct xhci_hcd *xhci) +{ + u32 config_reg; + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xHC can handle at most %d device slots", + xhci->max_slots); + + config_reg = readl(&xhci->op_regs->config_reg); + config_reg &= ~HCS_SLOTS_MASK; + config_reg |= xhci->max_slots; + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting Max device slots reg = 0x%x", + config_reg); + writel(config_reg, &xhci->op_regs->config_reg); +} + +static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) +{ + dma_addr_t deq_dma; + u64 crcr; + + deq_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, xhci->cmd_ring->dequeue); + deq_dma &= CMD_RING_PTR_MASK; + + crcr = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); + crcr &= ~CMD_RING_PTR_MASK; + crcr |= deq_dma; + + crcr &= ~CMD_RING_CYCLE; + crcr |= xhci->cmd_ring->cycle_state; + + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Setting command ring address to 0x%llx", crcr); + xhci_write_64(xhci, crcr, &xhci->op_regs->cmd_ring); +} + +static void xhci_set_doorbell_ptr(struct xhci_hcd *xhci) +{ + u32 offset; + + offset = readl(&xhci->cap_regs->db_off) & DBOFF_MASK; + xhci->dba = (void __iomem *)xhci->cap_regs + offset; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Doorbell array is located at offset 0x%x from cap regs base addr", offset); +} /* - * Initialize memory for HCD and xHC (one-time init). - * - * Program the PAGESIZE register, initialize the device context array, create - * device contexts (?), set up a command ring segment (or two?), create event - * ring (one for now). + * Enable USB 3.0 device notifications for function remote wake, which is necessary + * for allowing USB 3.0 devices to do remote wakeup from U3 (device suspend). */ -static int xhci_init(struct usb_hcd *hcd) +static void xhci_set_dev_notifications(struct xhci_hcd *xhci) +{ + u32 dev_notf; + + dev_notf = readl(&xhci->op_regs->dev_notification); + dev_notf &= ~DEV_NOTE_MASK; + dev_notf |= DEV_NOTE_FWAKE; + writel(dev_notf, &xhci->op_regs->dev_notification); +} + +/* Setup basic xHCI registers */ +static void xhci_init(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int retval; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "xhci_init"); - spin_lock_init(&xhci->lock); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Starting %s", __func__); - retval = xhci_mem_init(xhci, GFP_KERNEL); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_init"); + /* Set the Number of Device Slots Enabled to the maximum supported value */ + xhci_enable_max_dev_slots(xhci); + + /* Initialize the Command ring */ + xhci_ring_init(xhci, xhci->cmd_ring); + /* + * Reserve one command ring TRB for disabling LPM. + * Since the USB core grabs the shared usb_bus bandwidth mutex before + * disabling LPM, we only need to reserve one TRB for all devices. + */ + xhci->cmd_ring_reserved_trbs = 1; + /* Set the address in the Command Ring Control register */ + xhci_set_cmd_ring_deq(xhci); + + /* Set Device Context Base Address Array pointer */ + xhci_write_64(xhci, xhci->dcbaa->dma, &xhci->op_regs->dcbaa_ptr); + + /* Set Doorbell array pointer */ + xhci_set_doorbell_ptr(xhci); + + /* Set USB 3.0 device notifications for function remote wake */ + xhci_set_dev_notifications(xhci); + + /* Initialize the Primary interrupter */ + xhci_ring_init(xhci, xhci->interrupters[0]->event_ring); + xhci_add_interrupter(xhci, 0); + xhci->interrupters[0]->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; /* Initializing Compliance Mode Recovery Data If Needed */ if (xhci_compliance_mode_recovery_timer_quirk_check()) { @@ -485,7 +577,7 @@ static int xhci_init(struct usb_hcd *hcd) compliance_mode_recovery_timer_init(xhci); } - return retval; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished %s", __func__); } /*-------------------------------------------------------------------------*/ @@ -627,7 +719,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__); @@ -640,7 +732,7 @@ void xhci_stop(struct usb_hcd *hcd) "// Disabling event ring interrupts"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - xhci_disable_interrupter(ir); + xhci_disable_interrupter(xhci, ir); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -672,11 +764,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); @@ -719,8 +811,8 @@ static void xhci_save_registers(struct xhci_hcd *xhci) ir->s3_erst_size = readl(&ir->ir_set->erst_size); ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); - ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); - ir->s3_irq_control = readl(&ir->ir_set->irq_control); + ir->s3_iman = readl(&ir->ir_set->iman); + ir->s3_imod = readl(&ir->ir_set->imod); } } @@ -743,28 +835,11 @@ static void xhci_restore_registers(struct xhci_hcd *xhci) writel(ir->s3_erst_size, &ir->ir_set->erst_size); xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); - writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); - writel(ir->s3_irq_control, &ir->ir_set->irq_control); + writel(ir->s3_iman, &ir->ir_set->iman); + writel(ir->s3_imod, &ir->ir_set->imod); } } -static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) -{ - u64 val_64; - - /* step 2: initialize command ring buffer */ - val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); - val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) | - (xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, - xhci->cmd_ring->dequeue) & - (u64) ~CMD_RING_RSVD_BITS) | - xhci->cmd_ring->cycle_state; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Setting command ring address to 0x%llx", - (long unsigned long) val_64); - xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring); -} - /* * The whole command ring must be cleared to zero when we suspend the host. * @@ -818,7 +893,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci, spin_lock_irqsave(&xhci->lock, flags); for (i = 0; i < rhub->num_ports; i++) { - portsc = readl(rhub->ports[i]->addr); + portsc = xhci_portsc_readl(rhub->ports[i]); t1 = xhci_port_state_to_neutral(portsc); t2 = t1; @@ -831,7 +906,7 @@ static void xhci_disable_hub_port_wake(struct xhci_hcd *xhci, t2 |= PORT_CSC; if (t1 != t2) { - writel(t2, rhub->ports[i]->addr); + xhci_portsc_writel(rhub->ports[i], t2); xhci_dbg(xhci, "config port %d-%d wake bits, portsc: 0x%x, write: 0x%x\n", rhub->hcd->self.busnum, i + 1, portsc, t2); } @@ -858,7 +933,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) port_index = xhci->usb2_rhub.num_ports; ports = xhci->usb2_rhub.ports; while (port_index--) { - portsc = readl(ports[port_index]->addr); + portsc = xhci_portsc_readl(ports[port_index]); if (portsc & PORT_CHANGE_MASK || (portsc & PORT_PLS_MASK) == XDEV_RESUME) return true; @@ -866,7 +941,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) port_index = xhci->usb3_rhub.num_ports; ports = xhci->usb3_rhub.ports; while (port_index--) { - portsc = readl(ports[port_index]->addr); + portsc = xhci_portsc_readl(ports[port_index]); if (portsc & (PORT_CHANGE_MASK | PORT_CAS) || (portsc & PORT_PLS_MASK) == XDEV_RESUME) return true; @@ -882,11 +957,11 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) */ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) { - int rc = 0; + int err; unsigned int delay = XHCI_MAX_HALT_USEC * 2; struct usb_hcd *hcd = xhci_to_hcd(xhci); u32 command; - u32 res; + u32 usbsts; if (!hcd->state) return 0; @@ -908,10 +983,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) @@ -932,11 +1007,10 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) /* Some chips from Fresco Logic need an extraordinary delay */ delay *= (xhci->quirks & XHCI_SLOW_SUSPEND) ? 10 : 1; - if (xhci_handshake(&xhci->op_regs->status, - STS_HALT, STS_HALT, delay)) { - xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n"); - spin_unlock_irq(&xhci->lock); - return -ETIMEDOUT; + err = xhci_handshake(&xhci->op_regs->status, STS_HALT, STS_HALT, delay); + if (err) { + xhci_warn(xhci, "Clearing Run/Stop bit failed %d\n", err); + goto handshake_error; } xhci_clear_command_ring(xhci); @@ -947,28 +1021,34 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) command = readl(&xhci->op_regs->command); command |= CMD_CSS; writel(command, &xhci->op_regs->command); + + err = xhci_handshake(&xhci->op_regs->status, STS_SAVE, 0, 20 * USEC_PER_MSEC); + usbsts = readl(&xhci->op_regs->status); xhci->broken_suspend = 0; - if (xhci_handshake(&xhci->op_regs->status, - STS_SAVE, 0, 20 * 1000)) { - /* - * AMD SNPS xHC 3.0 occasionally does not clear the - * SSS bit of USBSTS and when driver tries to poll - * to see if the xHC clears BIT(8) which never happens - * and driver assumes that controller is not responding - * and times out. To workaround this, its good to check - * if SRE and HCE bits are not set (as per xhci - * Section 5.4.2) and bypass the timeout. - */ - res = readl(&xhci->op_regs->status); - if ((xhci->quirks & XHCI_SNPS_BROKEN_SUSPEND) && - (((res & STS_SRE) == 0) && - ((res & STS_HCE) == 0))) { - xhci->broken_suspend = 1; - } else { - xhci_warn(xhci, "WARN: xHC save state timeout\n"); - spin_unlock_irq(&xhci->lock); - return -ETIMEDOUT; + if (err) { + /* + * AMD SNPS xHC 3.0 occasionally does not clear the + * SSS bit of USBSTS and when driver tries to poll + * to see if the xHC clears BIT(8) which never happens + * and driver assumes that controller is not responding + * and times out. To workaround this, its good to check + * if SRE and HCE bits are not set (as per xhci + * Section 5.4.2) and bypass the timeout. + */ + if (!(xhci->quirks & XHCI_SNPS_BROKEN_SUSPEND)) { + xhci_warn(xhci, "Controller Save State failed %d\n", err); + goto handshake_error; + } + + if (usbsts & (STS_SRE | STS_HCE)) { + xhci_warn(xhci, "Controller Save State failed, USBSTS 0x%08x\n", usbsts); + goto handshake_error; } + + xhci_dbg(xhci, "SNPS broken suspend, save state unreliable\n"); + xhci->broken_suspend = 1; + } else if (usbsts & STS_SRE) { + xhci_warn(xhci, "Suspend Save Error (SRE), USBSTS 0x%08x\n", usbsts); } spin_unlock_irq(&xhci->lock); @@ -978,13 +1058,17 @@ 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__); } - return rc; + return 0; + +handshake_error: + spin_unlock_irq(&xhci->lock); + return -ETIMEDOUT; } EXPORT_SYMBOL_GPL(xhci_suspend); @@ -994,16 +1078,15 @@ 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); + struct xhci_segment *seg; int retval = 0; - bool comp_timer_running = false; bool pending_portevent = false; bool suspended_usb3_devs = false; - bool reinit_xhc = false; + bool reset_registers = false; if (!hcd->state) return 0; @@ -1022,10 +1105,11 @@ 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 (!reinit_xhc) { + if (power_lost || xhci->broken_suspend || xhci->quirks & XHCI_RESET_ON_RESUME) { + xhci_dbg(xhci, "HC state lost, performing host controller reset\n"); + reset_registers = true; + } else { + xhci_dbg(xhci, "HC state intact, continuing without reset\n"); /* * Some controllers might lose power during suspend, so wait * for controller not ready bit to clear, just as in xHC init. @@ -1058,22 +1142,19 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) spin_unlock_irq(&xhci->lock); return -ETIMEDOUT; } - } - temp = readl(&xhci->op_regs->status); - - /* 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) + /* re-initialize the HC on Restore Error, or Host Controller Error */ + temp = readl(&xhci->op_regs->status); + if ((temp & (STS_SRE | STS_HCE)) && !(xhci->xhc_state & XHCI_STATE_REMOVING)) { xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp); + reset_registers = true; + } } - if (reinit_xhc) { + if (reset_registers) { 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!"); } @@ -1086,32 +1167,41 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) xhci_dbg(xhci, "Stop HCD\n"); xhci_halt(xhci); xhci_zero_64b_regs(xhci); - retval = xhci_reset(xhci, XHCI_RESET_LONG_USEC); + if (xhci->xhc_state & XHCI_STATE_REMOVING) + retval = -ENODEV; + else + retval = xhci_reset(xhci, XHCI_RESET_LONG_USEC); spin_unlock_irq(&xhci->lock); if (retval) return retval; - xhci_dbg(xhci, "// Disabling event ring interrupts\n"); - temp = readl(&xhci->op_regs->status); - writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - xhci_disable_interrupter(xhci->interrupters[0]); + cancel_delayed_work_sync(&xhci->cmd_timer); + + /* Delete all remaining commands */ + xhci_cleanup_command_queue(xhci); + + /* Clear data which is re-initilized during runtime */ + xhci_for_each_ring_seg(xhci->interrupters[0]->event_ring->first_seg, seg) + memset(seg->trbs, 0, sizeof(union xhci_trb) * TRBS_PER_SEGMENT); + + for (int i = xhci->max_slots; i > 0; i--) + xhci_free_virt_devices_depth_first(xhci, i); + + xhci_rh_bw_cleanup(xhci); + + xhci->cmd_ring_reserved_trbs = 0; + xhci_for_each_ring_seg(xhci->cmd_ring->first_seg, seg) + memset(seg->trbs, 0, sizeof(union xhci_trb) * TRBS_PER_SEGMENT); - xhci_dbg(xhci, "cleaning up memory\n"); - xhci_mem_cleanup(xhci); xhci_debugfs_exit(xhci); - xhci_dbg(xhci, "xhci_stop completed - status = %x\n", - readl(&xhci->op_regs->status)); - /* USB core calls the PCI reinit and start functions twice: + xhci_init(hcd); + + /* + * USB core calls the PCI reinit and start functions twice: * first with the primary HCD, and then with the secondary HCD. * If we don't do the same, the host will never be started. */ - xhci_dbg(xhci, "Initialize the xhci_hcd\n"); - retval = xhci_init(hcd); - if (retval) - return retval; - comp_timer_running = true; - xhci_dbg(xhci, "Start the primary HCD\n"); retval = xhci_run(hcd); if (!retval && xhci->shared_hcd) { @@ -1155,41 +1245,36 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) xhci_dbc_resume(xhci); - if (retval == 0) { - /* - * Resume roothubs only if there are pending events. - * USB 3 devices resend U3 LFPS wake after a 100ms delay if - * the first wake signalling failed, give it that chance if - * there are suspended USB 3 devices. - */ - if (xhci->usb3_rhub.bus_state.suspended_ports || - xhci->usb3_rhub.bus_state.bus_suspended) - suspended_usb3_devs = true; + /* + * Resume roothubs only if there are pending events. + * USB 3 devices resend U3 LFPS wake after a 100ms delay if + * the first wake signalling failed, give it that chance if + * there are suspended USB 3 devices. + */ + if (xhci->usb3_rhub.bus_state.suspended_ports || xhci->usb3_rhub.bus_state.bus_suspended) + suspended_usb3_devs = true; + pending_portevent = xhci_pending_portevent(xhci); + if (suspended_usb3_devs && !pending_portevent && is_auto_resume) { + msleep(120); pending_portevent = xhci_pending_portevent(xhci); + } - if (suspended_usb3_devs && !pending_portevent && - msg.event == PM_EVENT_AUTO_RESUME) { - msleep(120); - pending_portevent = xhci_pending_portevent(xhci); - } - - if (pending_portevent) { - if (xhci->shared_hcd) - usb_hcd_resume_root_hub(xhci->shared_hcd); - usb_hcd_resume_root_hub(hcd); - } + if (pending_portevent) { + if (xhci->shared_hcd) + usb_hcd_resume_root_hub(xhci->shared_hcd); + usb_hcd_resume_root_hub(hcd); } -done: + /* * If system is subject to the Quirk, Compliance Mode Timer needs to * be re-initialized Always after a system resume. Ports are subject * to suffer the Compliance Mode issue again. It doesn't matter if * ports have entered previously to U0 before system's suspension. */ - if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running) + if (xhci->quirks & XHCI_COMP_MODE_QUIRK) compliance_mode_recovery_timer_init(xhci); - +done: if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL) usb_asmedia_modifyflowcontrol(to_pci_dev(hcd->self.controller)); @@ -1258,7 +1343,7 @@ static bool xhci_urb_temp_buffer_required(struct usb_hcd *hcd, struct scatterlist *tail_sg; tail_sg = urb->sg; - max_pkt = usb_endpoint_maxp(&urb->ep->desc); + max_pkt = xhci_usb_endpoint_maxp(urb->dev, urb->ep); if (!urb->num_sgs) return ret; @@ -1362,6 +1447,7 @@ static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb) * xhci_get_endpoint_index - Used for passing endpoint bitmasks between the core and * HCDs. Find the index for an endpoint given its descriptor. Use the return * value to right shift 1 for the bitmask. + * @desc: USB endpoint descriptor to determine index for * * Index = (epnum * 2) + direction - 1, * where direction = 0 for OUT, 1 for IN. @@ -1555,7 +1641,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag else num_tds = 1; - urb_priv = kzalloc(struct_size(urb_priv, td, num_tds), mem_flags); + urb_priv = kzalloc_flex(*urb_priv, td, num_tds, mem_flags); if (!urb_priv) return -ENOMEM; @@ -2812,16 +2898,25 @@ int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int gfp_t gfp_flags) { struct xhci_command *command; + struct xhci_ep_ctx *ep_ctx; unsigned long flags; - int ret; + int ret = -ENODEV; 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); + + /* make sure endpoint exists and is running before stopping it */ + if (ep->ring) { + ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index); + if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) + 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; @@ -2845,6 +2940,20 @@ out: } EXPORT_SYMBOL_GPL(xhci_stop_endpoint_sync); +/* + * xhci_usb_endpoint_maxp - get endpoint max packet size + * @host_ep: USB host endpoint to be checked + * + * Returns max packet from the correct descriptor + */ +int xhci_usb_endpoint_maxp(struct usb_device *udev, + struct usb_host_endpoint *host_ep) +{ + if (usb_endpoint_is_hs_isoc_double(udev, host_ep)) + return le16_to_cpu(host_ep->eusb2_isoc_ep_comp.wMaxPacketSize); + return usb_endpoint_maxp(&host_ep->desc); +} + /* Issue a configure endpoint command or evaluate context command * and wait for it to finish. */ @@ -3092,6 +3201,49 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) } EXPORT_SYMBOL_GPL(xhci_reset_bandwidth); +/* + * Get the available bandwidth of the ports under the xhci roothub. + * EIO means the command failed: command not implemented or unsupported + * speed (TRB Error), some ASMedia complete with Parameter Error when + * querying the root hub (slot_id = 0), or other error or timeout. + */ +int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, + u8 dev_speed) +{ + struct xhci_command *cmd; + unsigned long flags; + int ret; + + if (!ctx || !xhci) + return -EINVAL; + + cmd = xhci_alloc_command(xhci, true, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->in_ctx = ctx; + + /* get xhci port bandwidth, refer to xhci rev1_2 protocol 4.6.15 */ + spin_lock_irqsave(&xhci->lock, flags); + + ret = xhci_queue_get_port_bw(xhci, cmd, ctx->dma, dev_speed, 0); + if (ret) { + spin_unlock_irqrestore(&xhci->lock, flags); + goto err_out; + } + xhci_ring_cmd_db(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); + + wait_for_completion(cmd->completion); + if (cmd->status != COMP_SUCCESS) + ret = -EIO; +err_out: + kfree(cmd->completion); + kfree(cmd); + + return ret; +} + static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci, struct xhci_container_ctx *in_ctx, struct xhci_container_ctx *out_ctx, @@ -3140,7 +3292,6 @@ rescan: xhci_dbg(xhci, "endpoint disable with ep_state 0x%x\n", ep->ep_state); done: - host_ep->hcpriv = NULL; spin_unlock_irqrestore(&xhci->lock, flags); } @@ -3819,8 +3970,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, * 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); + ret = xhci_disable_and_free_slot(xhci, udev->slot_id); if (!ret) { ret = xhci_alloc_dev(hcd, udev); if (ret == 1) @@ -3879,6 +4029,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, xhci_get_slot_state(xhci, virt_dev->out_ctx)); xhci_dbg(xhci, "Not freeing device rings.\n"); /* Don't treat this as an error. May change my mind later. */ + virt_dev->flags = 0; ret = 0; goto command_cleanup; case COMP_SUCCESS: @@ -3914,6 +4065,8 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, } if (ep->ring) { + if (ep->sideband) + xhci_sideband_notify_ep_ring_free(ep->sideband, i); xhci_debugfs_remove_endpoint(xhci, virt_dev, i); xhci_free_endpoint_ring(xhci, virt_dev, i); } @@ -3975,7 +4128,7 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) xhci_disable_slot(xhci, udev->slot_id); spin_lock_irqsave(&xhci->lock, flags); - xhci_free_virt_device(xhci, udev->slot_id); + xhci_free_virt_device(xhci, virt_dev, udev->slot_id); spin_unlock_irqrestore(&xhci->lock, flags); } @@ -3999,7 +4152,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id) if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) || (xhci->xhc_state & XHCI_STATE_HALTED)) { spin_unlock_irqrestore(&xhci->lock, flags); - kfree(command); + xhci_free_command(xhci, command); return -ENODEV; } @@ -4007,7 +4160,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id) slot_id); if (ret) { spin_unlock_irqrestore(&xhci->lock, flags); - kfree(command); + xhci_free_command(xhci, command); return ret; } xhci_ring_cmd_db(xhci); @@ -4024,6 +4177,16 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id) return 0; } +int xhci_disable_and_free_slot(struct xhci_hcd *xhci, u32 slot_id) +{ + struct xhci_virt_device *vdev = xhci->devs[slot_id]; + int ret; + + ret = xhci_disable_slot(xhci, slot_id); + xhci_free_virt_device(xhci, vdev, slot_id); + return ret; +} + /* * Checks if we have enough host controller resources for the default control * endpoint. @@ -4082,8 +4245,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) xhci_err(xhci, "Error while assigning device slot ID: %s\n", xhci_trb_comp_code_string(command->status)); xhci_err(xhci, "Max number of devices this xHCI host supports is %u.\n", - HCS_MAX_SLOTS( - readl(&xhci->cap_regs->hcs_params1))); + xhci->max_slots); xhci_free_command(xhci, command); return 0; } @@ -4130,8 +4292,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) return 1; disable_slot: - xhci_disable_slot(xhci, udev->slot_id); - xhci_free_virt_device(xhci, udev->slot_id); + xhci_disable_and_free_slot(xhci, udev->slot_id); return 0; } @@ -4227,8 +4388,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); ctrl_ctx->drop_flags = 0; - trace_xhci_address_ctx(xhci, virt_dev->in_ctx, - le32_to_cpu(slot_ctx->dev_info) >> 27); + trace_xhci_address_ctx(xhci, virt_dev->in_ctx); trace_xhci_address_ctrl_ctx(ctrl_ctx); spin_lock_irqsave(&xhci->lock, flags); @@ -4267,8 +4427,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, dev_warn(&udev->dev, "Device not responding to setup %s.\n", act); mutex_unlock(&xhci->mutex); - ret = xhci_disable_slot(xhci, udev->slot_id); - xhci_free_virt_device(xhci, udev->slot_id); + ret = xhci_disable_and_free_slot(xhci, udev->slot_id); if (!ret) { if (xhci_alloc_dev(hcd, udev) == 1) xhci_setup_addressable_virt_dev(xhci, udev); @@ -4289,7 +4448,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, xhci_err(xhci, "ERROR: unexpected setup %s command completion code 0x%x.\n", act, command->status); - trace_xhci_address_ctx(xhci, virt_dev->out_ctx, 1); + trace_xhci_address_ctx(xhci, virt_dev->out_ctx); ret = -EINVAL; break; } @@ -4307,14 +4466,12 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, xhci_dbg_trace(xhci, trace_xhci_dbg_address, "Output Context DMA address = %#08llx", (unsigned long long)virt_dev->out_ctx->dma); - trace_xhci_address_ctx(xhci, virt_dev->in_ctx, - le32_to_cpu(slot_ctx->dev_info) >> 27); + trace_xhci_address_ctx(xhci, virt_dev->in_ctx); /* * USB core uses address 1 for the roothubs, so we add one to the * address given back to us by the HC. */ - trace_xhci_address_ctx(xhci, virt_dev->out_ctx, - le32_to_cpu(slot_ctx->dev_info) >> 27); + trace_xhci_address_ctx(xhci, virt_dev->out_ctx); /* Zero the input context control for later use */ ctrl_ctx->add_flags = 0; ctrl_ctx->drop_flags = 0; @@ -4498,7 +4655,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_port **ports; - __le32 __iomem *pm_addr, *hlpm_addr; + struct xhci_port_regs __iomem *port_reg; u32 pm_val, hlpm_val, field; unsigned int port_num; unsigned long flags; @@ -4523,9 +4680,8 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, ports = xhci->usb2_rhub.ports; port_num = udev->portnum - 1; - pm_addr = ports[port_num]->addr + PORTPMSC; - pm_val = readl(pm_addr); - hlpm_addr = ports[port_num]->addr + PORTHLPMC; + port_reg = ports[port_num]->port_reg; + pm_val = readl(&port_reg->portpmsc); xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", str_enable_disable(enable), port_num + 1); @@ -4554,30 +4710,30 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, spin_lock_irqsave(&xhci->lock, flags); hlpm_val = xhci_calculate_usb2_hw_lpm_params(udev); - writel(hlpm_val, hlpm_addr); + writel(hlpm_val, &port_reg->porthlmpc); /* flush write */ - readl(hlpm_addr); + readl(&port_reg->porthlmpc); } else { hird = xhci_calculate_hird_besl(xhci, udev); } pm_val &= ~PORT_HIRD_MASK; pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id); - writel(pm_val, pm_addr); - pm_val = readl(pm_addr); + writel(pm_val, &port_reg->portpmsc); + pm_val = readl(&port_reg->portpmsc); pm_val |= PORT_HLE; - writel(pm_val, pm_addr); + writel(pm_val, &port_reg->portpmsc); /* flush write */ - readl(pm_addr); + readl(&port_reg->portpmsc); } else { pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK); - writel(pm_val, pm_addr); + writel(pm_val, &port_reg->portpmsc); /* flush write */ - readl(pm_addr); + readl(&port_reg->portpmsc); if (udev->usb2_hw_lpm_besl_capable) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_change_max_exit_latency(xhci, udev, 0); - readl_poll_timeout(ports[port_num]->addr, pm_val, + readl_poll_timeout(&ports[port_num]->port_reg->portsc, pm_val, (pm_val & PORT_PLS_MASK) == XDEV_U0, 100, 10000); return 0; @@ -4759,8 +4915,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); } @@ -4817,8 +4973,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); } @@ -5270,6 +5426,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) */ struct device *dev = hcd->self.sysdev; int retval; + u32 hcs_params1; /* Accept arbitrarily long scatter-gather lists */ hcd->self.sg_tablesize = ~0; @@ -5295,7 +5452,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->run_regs = hcd->regs + (readl(&xhci->cap_regs->run_regs_off) & RTSOFF_MASK); /* Cache read-only capability registers */ - xhci->hcs_params1 = readl(&xhci->cap_regs->hcs_params1); + hcs_params1 = readl(&xhci->cap_regs->hcs_params1); xhci->hcs_params2 = readl(&xhci->cap_regs->hcs_params2); xhci->hcs_params3 = readl(&xhci->cap_regs->hcs_params3); xhci->hci_version = HC_VERSION(readl(&xhci->cap_regs->hc_capbase)); @@ -5303,10 +5460,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); + xhci->max_slots = HCS_MAX_SLOTS(hcs_params1); + xhci->max_ports = min(HCS_MAX_PORTS(hcs_params1), MAX_HC_PORTS); /* xhci-plat or xhci-pci might have set max_interrupters already */ - if ((!xhci->max_interrupters) || - xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1)) - xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1); + if (!xhci->max_interrupters) + xhci->max_interrupters = min(HCS_MAX_INTRS(hcs_params1), MAX_HC_INTRS); + else if (xhci->max_interrupters > HCS_MAX_INTRS(hcs_params1)) + xhci->max_interrupters = HCS_MAX_INTRS(hcs_params1); xhci->quirks |= quirks; @@ -5351,7 +5511,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Set dma_mask and coherent_dma_mask to 64-bits, * if xHC supports 64-bit addressing */ - if (HCC_64BIT_ADDR(xhci->hcc_params) && + if ((xhci->hcc_params & HCC_64BIT_ADDR) && !dma_set_mask(dev, DMA_BIT_MASK(64))) { xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); @@ -5367,12 +5527,21 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); } - xhci_dbg(xhci, "Calling HCD init\n"); - /* Initialize HCD and host controller data structures. */ - retval = xhci_init(hcd); + spin_lock_init(&xhci->lock); + INIT_LIST_HEAD(&xhci->cmd_list); + INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout); + init_completion(&xhci->cmd_ring_stop_completion); + xhci_hcd_page_size(xhci); + + memset(xhci->devs, 0, MAX_HC_SLOTS * sizeof(*xhci->devs)); + + /* Allocate xHCI data structures */ + retval = xhci_mem_init(xhci, GFP_KERNEL); if (retval) return retval; - xhci_dbg(xhci, "Called HCD init\n"); + + /* Initialize HCD and host controller data structures */ + xhci_init(hcd); if (xhci_hcd_is_usb3(hcd)) xhci_hcd_init_usb3_data(xhci, hcd); @@ -5525,8 +5694,8 @@ static int __init xhci_hcd_init(void) BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8); BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 8*32/8); BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8); - /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */ - BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8); + /* xhci_run_regs has eight fields and embeds 1024 xhci_intr_regs */ + BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*1024)*32/8); if (usb_disabled()) return -ENODEV; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 779b01dee068..aeecd301f207 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -12,6 +12,7 @@ #ifndef __LINUX_XHCI_HCD_H #define __LINUX_XHCI_HCD_H +#include <linux/bits.h> #include <linux/usb.h> #include <linux/timer.h> #include <linux/kernel.h> @@ -34,8 +35,16 @@ /* Max number of USB devices for any host controller - limit in section 6.1 */ #define MAX_HC_SLOTS 256 -/* Section 5.3.3 - MaxPorts */ +/* + * Max Number of Ports. xHCI specification section 5.3.3 + * Valid values are in the range of 1 to 255. + */ #define MAX_HC_PORTS 127 +/* + * Max number of Interrupter Register Sets. xHCI specification section 5.3.3 + * Valid values are in the range of 1 to 1024. + */ +#define MAX_HC_INTRS 128 /* * xHCI register interface. @@ -66,13 +75,19 @@ struct xhci_cap_regs { /* Reserved up to (CAPLENGTH - 0x1C) */ }; -/* Number of registers per port */ -#define NUM_PORT_REGS 4 - -#define PORTSC 0 -#define PORTPMSC 1 -#define PORTLI 2 -#define PORTHLPMC 3 +/* + * struct xhci_port_regs - Host Controller USB Port Register Set. xHCI spec 5.4.8 + * @portsc: Port Status and Control + * @portpmsc: Port Power Management Status and Control + * @portli: Port Link Info + * @porthlmpc: Port Hardware LPM Control + */ +struct xhci_port_regs { + __le32 portsc; + __le32 portpmsc; + __le32 portli; + __le32 porthlmpc; +}; /** * struct xhci_op_regs - xHCI Host Controller Operational Registers. @@ -85,16 +100,7 @@ struct xhci_cap_regs { * @cmd_ring: CRP - 64-bit Command Ring Pointer * @dcbaa_ptr: DCBAAP - 64-bit Device Context Base Address Array Pointer * @config_reg: CONFIG - Configure Register - * @port_status_base: PORTSCn - base address for Port Status and Control - * Each port has a Port Status and Control register, - * followed by a Port Power Management Status and Control - * register, a Port Link Info register, and a reserved - * register. - * @port_power_base: PORTPMSCn - base address for - * Port Power Management Status and Control - * @port_link_base: PORTLIn - base address for Port Link Info (current - * Link PM state and control) for USB 2.1 and USB 3.0 - * devices. + * @port_regs: Port Register Sets, from 1 to MaxPorts (defined by HCSPARAMS1). */ struct xhci_op_regs { __le32 command; @@ -110,13 +116,7 @@ struct xhci_op_regs { __le32 config_reg; /* rsvd: offset 0x3C-3FF */ __le32 reserved4[241]; - /* port 1 registers, which serve as a base address for other ports */ - __le32 port_status_base; - __le32 port_power_base; - __le32 port_link_base; - __le32 reserved5; - /* registers for ports 2-255 */ - __le32 reserved6[NUM_PORT_REGS*254]; + struct xhci_port_regs port_regs[]; }; /* USBCMD - USB command - command bitmasks */ @@ -126,17 +126,17 @@ struct xhci_op_regs { * PCI config regs). HC does NOT drive a USB reset on the downstream ports. * The xHCI driver must reinitialize the xHC after setting this bit. */ -#define CMD_RESET (1 << 1) +#define CMD_RESET BIT(1) /* Event Interrupt Enable - a '1' allows interrupts from the host controller */ #define CMD_EIE XHCI_CMD_EIE /* Host System Error Interrupt Enable - get out-of-band signal for HC errors */ #define CMD_HSEIE XHCI_CMD_HSEIE /* bits 4:6 are reserved (and should be preserved on writes). */ /* light reset (port status stays unchanged) - reset completed when this is 0 */ -#define CMD_LRESET (1 << 7) +#define CMD_LRESET BIT(7) /* host controller save/restore state. */ -#define CMD_CSS (1 << 8) -#define CMD_CRS (1 << 9) +#define CMD_CSS BIT(8) +#define CMD_CRS BIT(9) /* Enable Wrap Event - '1' means xHC generates an event when MFINDEX wraps. */ #define CMD_EWE XHCI_CMD_EWE /* MFINDEX power management - '1' means xHC can stop MFINDEX counter if all root @@ -144,38 +144,34 @@ struct xhci_op_regs { * '0' means the xHC can power it off if all ports are in the disconnect, * disabled, or powered-off state. */ -#define CMD_PM_INDEX (1 << 11) +#define CMD_PM_INDEX BIT(11) /* bit 14 Extended TBC Enable, changes Isoc TRB fields to support larger TBC */ -#define CMD_ETE (1 << 14) +#define CMD_ETE BIT(14) /* bits 15:31 are reserved (and should be preserved on writes). */ #define XHCI_RESET_LONG_USEC (10 * 1000 * 1000) #define XHCI_RESET_SHORT_USEC (250 * 1000) -/* IMAN - Interrupt Management Register */ -#define IMAN_IE (1 << 1) -#define IMAN_IP (1 << 0) - /* USBSTS - USB status - status bitmasks */ /* HC not running - set to 1 when run/stop bit is cleared. */ #define STS_HALT XHCI_STS_HALT /* serious error, e.g. PCI parity error. The HC will clear the run/stop bit. */ -#define STS_FATAL (1 << 2) +#define STS_FATAL BIT(2) /* event interrupt - clear this prior to clearing any IP flags in IR set*/ -#define STS_EINT (1 << 3) +#define STS_EINT BIT(3) /* port change detect */ -#define STS_PORT (1 << 4) +#define STS_PORT BIT(4) /* bits 5:7 reserved and zeroed */ /* save state status - '1' means xHC is saving state */ -#define STS_SAVE (1 << 8) +#define STS_SAVE BIT(8) /* restore state status - '1' means xHC is restoring state */ -#define STS_RESTORE (1 << 9) +#define STS_RESTORE BIT(9) /* true: save or restore error */ -#define STS_SRE (1 << 10) +#define STS_SRE BIT(10) /* true: Controller Not Ready to accept doorbell or op reg writes after reset */ #define STS_CNR XHCI_STS_CNR /* true: internal Host Controller Error - SW needs to reset and reinitialize */ -#define STS_HCE (1 << 12) +#define STS_HCE BIT(12) /* bits 13:31 reserved and should be preserved */ /* @@ -184,42 +180,43 @@ struct xhci_op_regs { * notification type that matches a bit set in this bit field. */ #define DEV_NOTE_MASK (0xffff) -#define ENABLE_DEV_NOTE(x) (1 << (x)) /* Most of the device notification types should only be used for debug. * SW does need to pay attention to function wake notifications. */ -#define DEV_NOTE_FWAKE ENABLE_DEV_NOTE(1) +#define DEV_NOTE_FWAKE BIT(1) /* CRCR - Command Ring Control Register - cmd_ring bitmasks */ -/* bit 0 is the command ring cycle state */ +/* bit 0 - Cycle bit indicates the ownership of the command ring */ +#define CMD_RING_CYCLE BIT(0) /* stop ring operation after completion of the currently executing command */ -#define CMD_RING_PAUSE (1 << 1) +#define CMD_RING_PAUSE BIT(1) /* stop ring immediately - abort the currently executing command */ -#define CMD_RING_ABORT (1 << 2) +#define CMD_RING_ABORT BIT(2) /* true: command ring is running */ -#define CMD_RING_RUNNING (1 << 3) -/* bits 4:5 reserved and should be preserved */ -/* Command Ring pointer - bit mask for the lower 32 bits. */ -#define CMD_RING_RSVD_BITS (0x3f) +#define CMD_RING_RUNNING BIT(3) +/* bits 63:6 - Command Ring pointer */ +#define CMD_RING_PTR_MASK GENMASK_ULL(63, 6) /* CONFIG - Configure Register - config_reg bitmasks */ /* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */ #define MAX_DEVS(p) ((p) & 0xff) /* bit 8: U3 Entry Enabled, assert PLC when root port enters U3, xhci 1.1 */ -#define CONFIG_U3E (1 << 8) +#define CONFIG_U3E BIT(8) /* bit 9: Configuration Information Enable, xhci 1.1 */ -#define CONFIG_CIE (1 << 9) +#define CONFIG_CIE BIT(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 + * struct xhci_intr_reg - Interrupt Register Set, v1.2 section 5.5.2. + * @iman: IMAN - Interrupt Management Register. Used to enable * interrupts and check for pending interrupts. - * @irq_control: IMOD - Interrupt Moderation Register. - * Used to throttle interrupts. - * @erst_size: Number of segments in the Event Ring Segment Table (ERST). - * @erst_base: ERST base address. - * @erst_dequeue: Event ring dequeue pointer. + * @imod: IMOD - Interrupt Moderation Register. Used to throttle interrupts. + * @erst_size: ERSTSZ - Number of segments in the Event Ring Segment Table (ERST). + * @erst_base: ERSTBA - Event ring segment table base address. + * @erst_dequeue: ERDP - Event ring dequeue pointer. * * Each interrupter (defined by a MSI-X vector) has an event ring and an Event * Ring Segment Table (ERST) associated with it. The event ring is comprised of @@ -229,48 +226,51 @@ struct xhci_op_regs { * updates the dequeue pointer. */ struct xhci_intr_reg { - __le32 irq_pending; - __le32 irq_control; + __le32 iman; + __le32 imod; __le32 erst_size; __le32 rsvd; __le64 erst_base; __le64 erst_dequeue; }; -/* irq_pending bitmasks */ -#define ER_IRQ_PENDING(p) ((p) & 0x1) -/* bits 2:31 need to be preserved */ -/* THIS IS BUGGY - FIXME - IP IS WRITE 1 TO CLEAR */ -#define ER_IRQ_CLEAR(p) ((p) & 0xfffffffe) -#define ER_IRQ_ENABLE(p) ((ER_IRQ_CLEAR(p)) | 0x2) -#define ER_IRQ_DISABLE(p) ((ER_IRQ_CLEAR(p)) & ~(0x2)) - -/* irq_control bitmasks */ -/* Minimum interval between interrupts (in 250ns intervals). The interval - * between interrupts will be longer if there are no events on the event ring. - * Default is 4000 (1 ms). +/* iman bitmasks */ +/* bit 0 - Interrupt Pending (IP), whether there is an interrupt pending. Write-1-to-clear. */ +#define IMAN_IP BIT(0) +/* bit 1 - Interrupt Enable (IE), whether the interrupter is capable of generating an interrupt */ +#define IMAN_IE BIT(1) + +/* imod bitmasks */ +/* + * bits 15:0 - Interrupt Moderation Interval, the minimum interval between interrupts + * (in 250ns intervals). The interval between interrupts will be longer if there are no + * events on the event ring. Default is 4000 (1 ms). */ -#define ER_IRQ_INTERVAL_MASK (0xffff) -/* Counter used to count down the time to the next interrupt - HW use only */ -#define ER_IRQ_COUNTER_MASK (0xffff << 16) +#define IMODI_MASK (0xffff) +/* bits 31:16 - Interrupt Moderation Counter, used to count down the time to the next interrupt */ +#define IMODC_MASK (0xffff << 16) /* erst_size bitmasks */ -/* Preserve bits 16:31 of erst_size */ -#define ERST_SIZE_MASK (0xffff << 16) +/* bits 15:0 - Event Ring Segment Table Size, number of ERST entries */ +#define ERST_SIZE_MASK (0xffff) /* erst_base bitmasks */ -#define ERST_BASE_RSVDP (GENMASK_ULL(5, 0)) +/* bits 63:6 - Event Ring Segment Table Base Address Register */ +#define ERST_BASE_ADDRESS_MASK GENMASK_ULL(63, 6) /* erst_dequeue bitmasks */ -/* Dequeue ERST Segment Index (DESI) - Segment number (or alias) - * where the current dequeue pointer lies. This is an optional HW hint. +/* + * bits 2:0 - Dequeue ERST Segment Index (DESI), is the segment number (or alias) where the + * current dequeue pointer lies. This is an optional HW hint. */ #define ERST_DESI_MASK (0x7) -/* Event Handler Busy (EHB) - is the event ring scheduled to be serviced by +/* + * bit 3 - Event Handler Busy (EHB), whether the event ring is scheduled to be serviced by * a work queue (or delayed service routine)? */ -#define ERST_EHB (1 << 3) -#define ERST_PTR_MASK (GENMASK_ULL(63, 4)) +#define ERST_EHB BIT(3) +/* bits 63:4 - Event Ring Dequeue Pointer */ +#define ERST_PTR_MASK GENMASK_ULL(63, 4) /** * struct xhci_run_regs @@ -284,7 +284,7 @@ struct xhci_intr_reg { struct xhci_run_regs { __le32 microframe_index; __le32 rsvd[7]; - struct xhci_intr_reg ir_set[128]; + struct xhci_intr_reg ir_set[1024]; }; /** @@ -357,15 +357,15 @@ struct xhci_slot_ctx { #define GET_DEV_SPEED(n) (((n) & DEV_SPEED) >> 20) /* bit 24 reserved */ /* Is this LS/FS device connected through a HS hub? - bit 25 */ -#define DEV_MTT (0x1 << 25) +#define DEV_MTT BIT(25) /* Set if the device is a hub - bit 26 */ -#define DEV_HUB (0x1 << 26) +#define DEV_HUB BIT(26) /* Index of the last valid endpoint context in this device context - 27:31 */ #define LAST_CTX_MASK (0x1f << 27) #define LAST_CTX(p) ((p) << 27) #define LAST_CTX_TO_EP_NUM(p) (((p) >> 27) - 1) -#define SLOT_FLAG (1 << 0) -#define EP0_FLAG (1 << 1) +#define SLOT_FLAG BIT(0) +#define EP0_FLAG BIT(1) /* dev_info2 bitmasks */ /* Max Exit Latency (ms) - worst case time to wake up all links in dev path */ @@ -464,7 +464,7 @@ struct xhci_ep_ctx { #define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) #define CTX_TO_EP_MAXPSTREAMS(p) (((p) & EP_MAXPSTREAMS_MASK) >> 10) /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ -#define EP_HAS_LSA (1 << 15) +#define EP_HAS_LSA BIT(15) /* hosts with LEC=1 use bits 31:24 as ESIT high bits. */ #define CTX_TO_MAX_ESIT_PAYLOAD_HI(p) (((p) >> 24) & 0xff) @@ -499,8 +499,9 @@ struct xhci_ep_ctx { #define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) /* deq bitmasks */ -#define EP_CTX_CYCLE_MASK (1 << 0) -#define SCTX_DEQ_MASK (~0xfL) +#define EP_CTX_CYCLE_MASK BIT(0) +/* bits 63:4 - TR Dequeue Pointer */ +#define TR_DEQ_PTR_MASK GENMASK_ULL(63, 4) /** @@ -586,6 +587,7 @@ struct xhci_stream_info { #define SMALL_STREAM_ARRAY_SIZE 256 #define MEDIUM_STREAM_ARRAY_SIZE 1024 +#define GET_PORT_BW_ARRAY_SIZE 256 /* Some Intel xHCI host controllers need software to keep track of the bus * bandwidth. Keep track of endpoint info here. Each root port is allocated @@ -660,18 +662,18 @@ struct xhci_virt_ep { struct xhci_ring *new_ring; unsigned int err_count; unsigned int ep_state; -#define SET_DEQ_PENDING (1 << 0) -#define EP_HALTED (1 << 1) /* For stall handling */ -#define EP_STOP_CMD_PENDING (1 << 2) /* For URB cancellation */ +#define SET_DEQ_PENDING BIT(0) +#define EP_HALTED BIT(1) /* For stall handling */ +#define EP_STOP_CMD_PENDING BIT(2) /* For URB cancellation */ /* Transitioning the endpoint to using streams, don't enqueue URBs */ -#define EP_GETTING_STREAMS (1 << 3) -#define EP_HAS_STREAMS (1 << 4) +#define EP_GETTING_STREAMS BIT(3) +#define EP_HAS_STREAMS BIT(4) /* Transitioning the endpoint to not using streams, don't enqueue URBs */ -#define EP_GETTING_NO_STREAMS (1 << 5) -#define EP_HARD_CLEAR_TOGGLE (1 << 6) -#define EP_SOFT_CLEAR_TOGGLE (1 << 7) +#define EP_GETTING_NO_STREAMS BIT(5) +#define EP_HARD_CLEAR_TOGGLE BIT(6) +#define EP_SOFT_CLEAR_TOGGLE BIT(7) /* usb_hub_clear_tt_buffer is in progress */ -#define EP_CLEARING_TT (1 << 8) +#define EP_CLEARING_TT BIT(8) /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; struct xhci_hcd *xhci; @@ -697,6 +699,8 @@ struct xhci_virt_ep { int next_frame_id; /* Use new Isoch TRB layout needed for extended TBC support */ bool use_extended_tbc; + /* set if this endpoint is controlled via sideband access*/ + struct xhci_sideband *sideband; }; enum xhci_overhead_type { @@ -759,6 +763,8 @@ struct xhci_virt_device { u16 current_mel; /* Used for the debugfs interfaces. */ void *debugfs_private; + /* set if this endpoint is controlled via sideband access*/ + struct xhci_sideband *sideband; }; /* @@ -794,7 +800,6 @@ struct xhci_device_context_array { /* private xHCD pointers */ dma_addr_t dma; }; -/* TODO: write function to set the 64-bit device DMA address */ /* * TODO: change this to be dynamically sized at HC mem init time since the HC * might not be able to handle the maximum number of devices possible. @@ -950,7 +955,7 @@ struct xhci_link_trb { }; /* control bitfields */ -#define LINK_TOGGLE (0x1<<1) +#define LINK_TOGGLE BIT(1) /* Command completion event TRB */ struct xhci_event_cmd { @@ -964,13 +969,13 @@ struct xhci_event_cmd { #define COMP_PARAM(p) ((p) & 0xffffff) /* Command Completion Parameter */ /* Address device - disable SetAddress */ -#define TRB_BSR (1<<9) +#define TRB_BSR BIT(9) /* Configure Endpoint - Deconfigure */ -#define TRB_DC (1<<9) +#define TRB_DC BIT(9) /* Stop Ring - Transfer State Preserve */ -#define TRB_TSP (1<<9) +#define TRB_TSP BIT(9) enum xhci_ep_reset_type { EP_HARD_RESET, @@ -999,6 +1004,9 @@ enum xhci_setup_dev { /* bits 16:23 are the virtual function ID */ /* bits 24:31 are the slot ID */ +/* bits 19:16 are the dev speed */ +#define DEV_SPEED_FOR_TRB(p) ((p) << 16) + /* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */ #define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23) #define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23) @@ -1010,13 +1018,13 @@ enum xhci_setup_dev { #define SCT_FOR_TRB(p) (((p) & 0x7) << 1) /* Link TRB specific fields */ -#define TRB_TC (1<<1) +#define TRB_TC BIT(1) /* Port Status Change Event TRB fields */ /* Port ID - bits 31:24 */ #define GET_PORT_ID(p) (((p) & (0xff << 24)) >> 24) -#define EVENT_DATA (1 << 2) +#define EVENT_DATA BIT(2) /* Normal TRB fields */ /* transfer_len bitmasks - bits 0:16 */ @@ -1031,36 +1039,36 @@ enum xhci_setup_dev { #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) /* Cycle bit - indicates TRB ownership by HC or HCD */ -#define TRB_CYCLE (1<<0) +#define TRB_CYCLE BIT(0) /* * Force next event data TRB to be evaluated before task switch. * Used to pass OS data back after a TD completes. */ -#define TRB_ENT (1<<1) +#define TRB_ENT BIT(1) /* Interrupt on short packet */ -#define TRB_ISP (1<<2) +#define TRB_ISP BIT(2) /* Set PCIe no snoop attribute */ -#define TRB_NO_SNOOP (1<<3) +#define TRB_NO_SNOOP BIT(3) /* Chain multiple TRBs into a TD */ -#define TRB_CHAIN (1<<4) +#define TRB_CHAIN BIT(4) /* Interrupt on completion */ -#define TRB_IOC (1<<5) +#define TRB_IOC BIT(5) /* The buffer pointer contains immediate data */ -#define TRB_IDT (1<<6) +#define TRB_IDT BIT(6) /* TDs smaller than this might use IDT */ #define TRB_IDT_MAX_SIZE 8 /* Block Event Interrupt */ -#define TRB_BEI (1<<9) +#define TRB_BEI BIT(9) /* Control transfer TRB specific fields */ -#define TRB_DIR_IN (1<<16) +#define TRB_DIR_IN BIT(16) #define TRB_TX_TYPE(p) ((p) << 16) #define TRB_DATA_OUT 2 #define TRB_DATA_IN 3 /* Isochronous TRB specific fields */ -#define TRB_SIA (1<<31) +#define TRB_SIA BIT(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) */ @@ -1371,7 +1379,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; }; @@ -1444,8 +1452,8 @@ struct xhci_interrupter { bool ip_autoclear; u32 isoc_bei_interval; /* For interrupter registers save and restore over suspend/resume */ - u32 s3_irq_pending; - u32 s3_irq_control; + u32 s3_iman; + u32 s3_imod; u32 s3_erst_size; u64 s3_erst_base; u64 s3_erst_dequeue; @@ -1465,7 +1473,7 @@ struct xhci_port_cap { }; struct xhci_port { - __le32 __iomem *addr; + struct xhci_port_regs __iomem *port_reg; int hw_portnum; int hcd_portnum; struct xhci_hub *rhub; @@ -1501,7 +1509,6 @@ struct xhci_hcd { struct xhci_doorbell_array __iomem *dba; /* Cached register copies of read-only HC data */ - __u32 hcs_params1; __u32 hcs_params2; __u32 hcs_params3; __u32 hcc_params; @@ -1512,12 +1519,11 @@ struct xhci_hcd { /* packed release number */ u16 hci_version; u16 max_interrupters; + u8 max_slots; + u8 max_ports; /* 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 */ @@ -1530,9 +1536,9 @@ struct xhci_hcd { struct xhci_interrupter **interrupters; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; -#define CMD_RING_STATE_RUNNING (1 << 0) -#define CMD_RING_STATE_ABORTED (1 << 1) -#define CMD_RING_STATE_STOPPED (1 << 2) +#define CMD_RING_STATE_RUNNING BIT(0) +#define CMD_RING_STATE_ABORTED BIT(1) +#define CMD_RING_STATE_STOPPED BIT(2) struct list_head cmd_list; unsigned int cmd_ring_reserved_trbs; struct delayed_work cmd_timer; @@ -1554,6 +1560,7 @@ struct xhci_hcd { struct dma_pool *device_pool; struct dma_pool *segment_pool; struct dma_pool *small_streams_pool; + struct dma_pool *port_bw_pool; struct dma_pool *medium_streams_pool; /* Host controller watchdog timer structures */ @@ -1572,9 +1579,9 @@ struct xhci_hcd { * * There are no reports of xHCI host controllers that display this issue. */ -#define XHCI_STATE_DYING (1 << 0) -#define XHCI_STATE_HALTED (1 << 1) -#define XHCI_STATE_REMOVING (1 << 2) +#define XHCI_STATE_DYING BIT(0) +#define XHCI_STATE_HALTED BIT(1) +#define XHCI_STATE_REMOVING BIT(2) unsigned long long quirks; #define XHCI_LINK_TRB_QUIRK BIT_ULL(0) #define XHCI_RESET_EP_QUIRK BIT_ULL(1) /* Deprecated */ @@ -1637,6 +1644,7 @@ struct xhci_hcd { #define XHCI_WRITE_64_HI_LO BIT_ULL(47) #define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48) #define XHCI_ETRON_HOST BIT_ULL(49) +#define XHCI_LIMIT_ENDPOINT_INTERVAL_9 BIT_ULL(50) unsigned int num_active_eps; unsigned int limit_active_eps; @@ -1759,11 +1767,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 */ @@ -1775,7 +1792,8 @@ void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), /* xHCI memory management */ void xhci_mem_cleanup(struct xhci_hcd *xhci); int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags); -void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id); +void xhci_free_virt_device(struct xhci_hcd *xhci, struct xhci_virt_device *dev, int slot_id); +void xhci_free_virt_devices_depth_first(struct xhci_hcd *xhci, int slot_id); int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_device *udev, gfp_t flags); int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev); void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci, @@ -1787,6 +1805,7 @@ void xhci_update_tt_active_eps(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, int old_active_eps); void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info); +void xhci_rh_bw_cleanup(struct xhci_hcd *xhci); void xhci_update_bw_info(struct xhci_hcd *xhci, struct xhci_container_ctx *in_ctx, struct xhci_input_control_ctx *ctrl_ctx, @@ -1807,6 +1826,7 @@ 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); +void xhci_ring_init(struct xhci_hcd *xhci, struct xhci_ring *ring); void xhci_free_endpoint_ring(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, unsigned int ep_index); @@ -1837,17 +1857,22 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type, gfp_t flags); void xhci_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); +struct xhci_container_ctx *xhci_alloc_port_bw_ctx(struct xhci_hcd *xhci, + gfp_t flags); +void xhci_free_port_bw_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx); struct xhci_interrupter * xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs, - u32 imod_interval); + u32 imod_interval, unsigned int intr_num); void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir); +void xhci_skip_sec_intr_events(struct xhci_hcd *xhci, + struct xhci_ring *ring, + struct xhci_interrupter *ir); /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); int xhci_handshake(void __iomem *ptr, u32 mask, u32 done, u64 timeout_us); -int xhci_handshake_check_state(struct xhci_hcd *xhci, void __iomem *ptr, - u32 mask, u32 done, int usec, unsigned int exit_state); void xhci_quiesce(struct xhci_hcd *xhci); int xhci_halt(struct xhci_hcd *xhci); int xhci_start(struct xhci_hcd *xhci); @@ -1867,10 +1892,11 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev); int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); +int xhci_disable_and_free_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); @@ -1881,11 +1907,11 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci, struct usb_tt *tt, gfp_t mem_flags); int xhci_set_interrupter_moderation(struct xhci_interrupter *ir, u32 imod_interval); +int xhci_enable_interrupter(struct xhci_interrupter *ir); +int xhci_disable_interrupter(struct xhci_hcd *xhci, 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, @@ -1907,6 +1933,11 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, struct xhci_command *cmd, dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed); +int xhci_queue_get_port_bw(struct xhci_hcd *xhci, + struct xhci_command *cmd, dma_addr_t in_ctx_ptr, + u8 dev_speed, bool command_must_succeed); +int xhci_get_port_bandwidth(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, + u8 dev_speed); int xhci_queue_evaluate_context(struct xhci_hcd *xhci, struct xhci_command *cmd, dma_addr_t in_ctx_ptr, u32 slot_id, bool command_must_succeed); int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd, @@ -1927,6 +1958,14 @@ 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); +void xhci_update_erst_dequeue(struct xhci_hcd *xhci, + struct xhci_interrupter *ir, + bool clear_ehb); +void xhci_add_interrupter(struct xhci_hcd *xhci, unsigned int intr_num); +int xhci_usb_endpoint_maxp(struct usb_device *udev, + struct usb_host_endpoint *host_ep); +void xhci_portsc_writel(struct xhci_port *port, u32 val); +u32 xhci_portsc_readl(struct xhci_port *port); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, @@ -2365,25 +2404,48 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc) if (portsc == ~(u32)0) return str; - ret += sprintf(str + ret, "%s %s %s Link:%s PortSpeed:%d ", - portsc & PORT_POWER ? "Powered" : "Powered-off", - portsc & PORT_CONNECT ? "Connected" : "Not-connected", - portsc & PORT_PE ? "Enabled" : "Disabled", - xhci_portsc_link_state_string(portsc), - DEV_PORT_SPEED(portsc)); + ret += sprintf(str + ret, "Speed=%d ", DEV_PORT_SPEED(portsc)); + ret += sprintf(str + ret, "Link=%s ", xhci_portsc_link_state_string(portsc)); + /* RO/ROS: Read-only */ + if (portsc & PORT_CONNECT) + ret += sprintf(str + ret, "CCS "); if (portsc & PORT_OC) - ret += sprintf(str + ret, "OverCurrent "); + ret += sprintf(str + ret, "OCA "); /* No set for USB2 ports */ + if (portsc & PORT_CAS) + ret += sprintf(str + ret, "CAS "); + if (portsc & PORT_DEV_REMOVE) + ret += sprintf(str + ret, "DR "); + + /* RWS; writing 1 sets the bit, writing 0 clears the bit. */ + if (portsc & PORT_POWER) + ret += sprintf(str + ret, "PP "); + if (portsc & PORT_WKCONN_E) + ret += sprintf(str + ret, "WCE "); + if (portsc & PORT_WKDISC_E) + ret += sprintf(str + ret, "WDE "); + if (portsc & PORT_WKOC_E) + ret += sprintf(str + ret, "WOE "); + + /* RW; writing 1 sets the bit, writing 0 clears the bit */ + if (portsc & PORT_LINK_STROBE) + ret += sprintf(str + ret, "LWS "); /* LWS 0 write is ignored */ + + /* RW1S; writing 1 sets the bit, writing 0 has no effect */ if (portsc & PORT_RESET) - ret += sprintf(str + ret, "In-Reset "); + ret += sprintf(str + ret, "PR "); + if (portsc & PORT_WR) + ret += sprintf(str + ret, "WPR "); /* RsvdZ for USB2 ports */ - ret += sprintf(str + ret, "Change: "); + /* RW1CS; writing 1 clears the bit, writing 0 has no effect. */ + if (portsc & PORT_PE) + ret += sprintf(str + ret, "PED "); if (portsc & PORT_CSC) ret += sprintf(str + ret, "CSC "); if (portsc & PORT_PEC) - ret += sprintf(str + ret, "PEC "); + ret += sprintf(str + ret, "PEC "); /* No set for USB3 ports */ if (portsc & PORT_WRC) - ret += sprintf(str + ret, "WRC "); + ret += sprintf(str + ret, "WRC "); /* RsvdZ for USB2 ports */ if (portsc & PORT_OCC) ret += sprintf(str + ret, "OCC "); if (portsc & PORT_RC) @@ -2391,17 +2453,7 @@ static inline const char *xhci_decode_portsc(char *str, u32 portsc) if (portsc & PORT_PLC) ret += sprintf(str + ret, "PLC "); if (portsc & PORT_CEC) - ret += sprintf(str + ret, "CEC "); - if (portsc & PORT_CAS) - ret += sprintf(str + ret, "CAS "); - - ret += sprintf(str + ret, "Wake: "); - if (portsc & PORT_WKCONN_E) - ret += sprintf(str + ret, "WCE "); - if (portsc & PORT_WKDISC_E) - ret += sprintf(str + ret, "WDE "); - if (portsc & PORT_WKOC_E) - ret += sprintf(str + ret, "WOE "); + ret += sprintf(str + ret, "CEC "); /* RsvdZ for USB2 ports */ return str; } diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 7b7e1554ea20..ca287b770e8c 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -707,7 +707,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l if (signal_pending (current)) { mutex_unlock(&mdc800->io_lock); - return -EINTR; + return len == left ? -EINTR : len-left; } sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left; @@ -730,9 +730,11 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l mutex_unlock(&mdc800->io_lock); return len-left; } - wait_event_timeout(mdc800->download_wait, + retval = wait_event_timeout(mdc800->download_wait, mdc800->downloaded, msecs_to_jiffies(TO_DOWNLOAD_GET_READY)); + if (!retval) + usb_kill_urb(mdc800->download_urb); mdc800->downloaded = 0; if (mdc800->download_urb->status != 0) { @@ -982,7 +984,7 @@ static int __init usb_mdc800_init (void) { int retval = -ENODEV; /* Allocate Memory */ - mdc800=kzalloc (sizeof (struct mdc800_data), GFP_KERNEL); + mdc800=kzalloc_obj(struct mdc800_data); if (!mdc800) goto cleanup_on_fail; diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 934ec5310fb9..1f0b3c44d388 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -55,7 +55,6 @@ * Status: * * Untested with multiple scanners. - * Untested on SMP. * Untested on a bigendian machine. * * History: @@ -170,25 +169,13 @@ static struct usb_driver mts_usb_driver = { #define MTS_VERSION "0.4.3" #define MTS_NAME "microtek usb (rev " MTS_VERSION "): " -#define MTS_WARNING(x...) \ - printk( KERN_WARNING MTS_NAME x ) -#define MTS_ERROR(x...) \ - printk( KERN_ERR MTS_NAME x ) -#define MTS_INT_ERROR(x...) \ - MTS_ERROR(x) -#define MTS_MESSAGE(x...) \ - printk( KERN_INFO MTS_NAME x ) - #if defined MTS_DO_DEBUG #define MTS_DEBUG(x...) \ printk( KERN_DEBUG MTS_NAME x ) -#define MTS_DEBUG_GOT_HERE() \ - MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __func__ ) #define MTS_DEBUG_INT() \ - do { MTS_DEBUG_GOT_HERE(); \ - MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \ + do { MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \ MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",transfer->status,(int)context->data_length, (int)transfer->actual_length ); \ mts_debug_dump(context->instance);\ } while(0) @@ -197,7 +184,6 @@ static struct usb_driver mts_usb_driver = { #define MTS_NUL_STATEMENT do { } while(0) #define MTS_DEBUG(x...) MTS_NUL_STATEMENT -#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT #define MTS_DEBUG_INT() MTS_NUL_STATEMENT #endif @@ -316,7 +302,6 @@ static inline void mts_debug_dump(struct mts_desc* dummy) #endif static inline void mts_urb_abort(struct mts_desc* desc) { - MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); usb_kill_urb( desc->urb ); @@ -332,8 +317,6 @@ static int mts_scsi_abort(struct scsi_cmnd *srb) { struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); - MTS_DEBUG_GOT_HERE(); - mts_urb_abort(desc); return FAILED; @@ -344,7 +327,6 @@ static int mts_scsi_host_reset(struct scsi_cmnd *srb) struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); int result; - MTS_DEBUG_GOT_HERE(); mts_debug_dump(desc); result = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf); @@ -355,8 +337,8 @@ static int mts_scsi_host_reset(struct scsi_cmnd *srb) return result ? FAILED : SUCCESS; } -static int -mts_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *srb); +static enum scsi_qc_status mts_scsi_queuecommand(struct Scsi_Host *shost, + struct scsi_cmnd *srb); static void mts_transfer_cleanup( struct urb *transfer ); static void mts_do_sg(struct urb * transfer); @@ -386,7 +368,8 @@ void mts_int_submit_urb (struct urb* transfer, res = usb_submit_urb( transfer, GFP_ATOMIC ); if ( unlikely(res) ) { - MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res ); + dev_err(&context->instance->usb_dev->dev, + "could not submit URB! Error was %d\n",(int)res ); set_host_byte(context->srb, DID_ERROR); mts_transfer_cleanup(transfer); } @@ -452,12 +435,9 @@ static void mts_command_done( struct urb *transfer ) if ( unlikely(status) ) { if (status == -ENOENT) { /* We are being killed */ - MTS_DEBUG_GOT_HERE(); set_host_byte(context->srb, DID_ABORT); } else { /* A genuine error has occurred */ - MTS_DEBUG_GOT_HERE(); - set_host_byte(context->srb, DID_ERROR); } mts_transfer_cleanup(transfer); @@ -523,8 +503,6 @@ mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc) { int pipe; - MTS_DEBUG_GOT_HERE(); - desc->context.instance = desc; desc->context.srb = srb; @@ -559,13 +537,12 @@ mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc) desc->context.data_pipe = pipe; } -static int mts_scsi_queuecommand_lck(struct scsi_cmnd *srb) +static enum scsi_qc_status mts_scsi_queuecommand_lck(struct scsi_cmnd *srb) { mts_scsi_cmnd_callback callback = scsi_done; struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); int res; - MTS_DEBUG_GOT_HERE(); mts_show_command(srb); mts_debug_dump(desc); @@ -601,7 +578,7 @@ static int mts_scsi_queuecommand_lck(struct scsi_cmnd *srb) res=usb_submit_urb(desc->urb, GFP_ATOMIC); if(unlikely(res)){ - MTS_ERROR("error %d submitting URB\n",(int)res); + dev_err(&desc->usb_dev->dev, "error %d submitting URB\n",(int)res); set_host_byte(srb, DID_ERROR); if(likely(callback != NULL)) @@ -666,64 +643,52 @@ static int mts_usb_probe(struct usb_interface *intf, /* the current altsetting on the interface we're probing */ struct usb_host_interface *altsetting; - MTS_DEBUG_GOT_HERE(); MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev ); MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n", le16_to_cpu(dev->descriptor.idProduct), le16_to_cpu(dev->descriptor.idVendor) ); - MTS_DEBUG_GOT_HERE(); - /* the current altsetting on the interface we're probing */ altsetting = intf->cur_altsetting; - /* Check if the config is sane */ if ( altsetting->desc.bNumEndpoints != MTS_EP_TOTAL ) { - MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n", + dev_warn(&dev->dev, "expecting %d got %d endpoints! Bailing out.\n", (int)MTS_EP_TOTAL, (int)altsetting->desc.bNumEndpoints ); return -ENODEV; } for( i = 0; i < altsetting->desc.bNumEndpoints; i++ ) { - if ((altsetting->endpoint[i].desc.bmAttributes & - USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { - - MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n", - (int)altsetting->endpoint[i].desc.bEndpointAddress ); - } else { - if (altsetting->endpoint[i].desc.bEndpointAddress & - USB_DIR_IN) - *ep_in_current++ - = altsetting->endpoint[i].desc.bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; - else { - if ( ep_out != -1 ) { - MTS_WARNING( "can only deal with one output endpoints. Bailing out." ); - return -ENODEV; - } - - ep_out = altsetting->endpoint[i].desc.bEndpointAddress & - USB_ENDPOINT_NUMBER_MASK; + if (usb_endpoint_is_bulk_in(&altsetting->endpoint[i].desc)) { + *ep_in_current++ = usb_endpoint_num(&altsetting->endpoint[i].desc); + } else if (usb_endpoint_is_bulk_out(&altsetting->endpoint[i].desc)) { + if (ep_out == -1) { + ep_out = usb_endpoint_num(&altsetting->endpoint[i].desc); + } else { + dev_warn(&dev->dev, "can only deal with bulk endpoints; endpoint %d is not bulk.\n", + usb_endpoint_num(&altsetting->endpoint[i].desc)); + return -ENODEV; } + } else { + dev_warn(&dev->dev, "can only deal with bulk endpoints; endpoint %d is not bulk.\n", + usb_endpoint_num(&altsetting->endpoint[i].desc)); } - } if (ep_in_current != &ep_in_set[2]) { - MTS_WARNING("couldn't find two input bulk endpoints. Bailing out.\n"); + dev_warn(&dev->dev, "couldn't find two input bulk endpoints. Bailing out.\n"); return -ENODEV; } if ( ep_out == -1 ) { - MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" ); + dev_warn(&dev->dev, "couldn't find an output bulk endpoint. Bailing out.\n" ); return -ENODEV; } - new_desc = kzalloc(sizeof(struct mts_desc), GFP_KERNEL); + new_desc = kzalloc_obj(struct mts_desc); if (!new_desc) goto out; @@ -744,15 +709,15 @@ static int mts_usb_probe(struct usb_interface *intf, new_desc->ep_image = ep_in_set[1]; if ( new_desc->ep_out != MTS_EP_OUT ) - MTS_WARNING( "will this work? Command EP is not usually %d\n", + dev_warn(&dev->dev, "will this work? Command EP is not usually %d\n", (int)new_desc->ep_out ); if ( new_desc->ep_response != MTS_EP_RESPONSE ) - MTS_WARNING( "will this work? Response EP is not usually %d\n", + dev_warn(&dev->dev, "will this work? Response EP is not usually %d\n", (int)new_desc->ep_response ); if ( new_desc->ep_image != MTS_EP_IMAGE ) - MTS_WARNING( "will this work? Image data EP is not usually %d\n", + dev_warn(&dev->dev, "will this work? Image data EP is not usually %d\n", (int)new_desc->ep_image ); new_desc->host = scsi_host_alloc(&mts_scsi_host_template, diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c index add2d2e3b61b..6c52c6f59d69 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); @@ -2572,15 +2572,15 @@ int isp1760_hcd_register(struct isp1760_hcd *priv, struct resource *mem, priv->hcd = hcd; - priv->atl_slots = kcalloc(mem_layout->slot_num, - sizeof(struct isp1760_slotinfo), GFP_KERNEL); + priv->atl_slots = kzalloc_objs(struct isp1760_slotinfo, + mem_layout->slot_num); if (!priv->atl_slots) { ret = -ENOMEM; goto put_hcd; } - priv->int_slots = kcalloc(mem_layout->slot_num, - sizeof(struct isp1760_slotinfo), GFP_KERNEL); + priv->int_slots = kzalloc_objs(struct isp1760_slotinfo, + mem_layout->slot_num); if (!priv->int_slots) { ret = -ENOMEM; goto free_atl_slots; diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index 5cafd23345ca..e45a54c4a560 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -878,7 +878,7 @@ static struct usb_request *isp1760_ep_alloc_request(struct usb_ep *ep, { struct isp1760_request *req; - req = kzalloc(sizeof(*req), gfp_flags); + req = kzalloc_obj(*req, gfp_flags); if (!req) return NULL; @@ -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); @@ -1423,7 +1423,7 @@ static irqreturn_t isp1760_udc_irq(int irq, void *dev) static void isp1760_udc_vbus_poll(struct timer_list *t) { - struct isp1760_udc *udc = from_timer(udc, t, vbus_timer); + struct isp1760_udc *udc = timer_container_of(udc, t, vbus_timer); unsigned long flags; spin_lock_irqsave(&udc->lock, flags); diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 6497c4e81e95..0b56b773dbdf 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -147,6 +147,7 @@ config USB_APPLEDISPLAY config USB_QCOM_EUD tristate "QCOM Embedded USB Debugger(EUD) Driver" depends on ARCH_QCOM || COMPILE_TEST + select QCOM_SCM select USB_ROLE_SWITCH help This module enables support for Qualcomm Technologies, Inc. @@ -178,6 +179,21 @@ config USB_LJCA This driver can also be built as a module. If so, the module will be called usb-ljca. +config USB_USBIO + tristate "Intel USBIO Bridge support" + depends on USB && ACPI + depends on X86 || COMPILE_TEST + select AUXILIARY_BUS + help + This adds support for Intel USBIO drivers. + This enables the USBIO bridge driver module in charge to talk + to the USB device. Additional drivers such as GPIO_USBIO and + I2C_USBIO must be enabled in order to use the device's full + functionality. + + This driver can also be built as a module. If so, the module + will be called usbio. + source "drivers/usb/misc/sisusbvga/Kconfig" config USB_LD @@ -231,8 +247,8 @@ config USB_EHSET_TEST_FIXTURE VID/PID pairs. This driver then initiates a corresponding test mode on the downstream port to which the test fixture is attached. - See <http://www.usb.org/developers/onthego/EHSET_v1.01.pdf> for more - information. + See <https://www.usb.org/sites/default/files/EHSET_v1.01%281%29.pdf> + for more information. config USB_ISIGHTFW tristate "iSight firmware loading support" diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 0cd5bc8f52fe..494ab0377f35 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_USB_EMI62) += emi62.o obj-$(CONFIG_USB_EZUSB_FX2) += ezusb.o obj-$(CONFIG_APPLE_MFI_FASTCHARGE) += apple-mfi-fastcharge.o obj-$(CONFIG_USB_LJCA) += usb-ljca.o +obj-$(CONFIG_USB_USBIO) += usbio.o obj-$(CONFIG_USB_IDMOUSE) += idmouse.o obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index ed6a19254d2f..369d0d2ee2be 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -657,7 +657,7 @@ static int adu_probe(struct usb_interface *interface, int res; /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL); + dev = kzalloc_obj(struct adu_device); if (!dev) return -ENOMEM; diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c index ac8695195c13..af266e19f2fd 100644 --- a/drivers/usb/misc/apple-mfi-fastcharge.c +++ b/drivers/usb/misc/apple-mfi-fastcharge.c @@ -44,6 +44,7 @@ MODULE_DEVICE_TABLE(usb, mfi_fc_id_table); struct mfi_device { struct usb_device *udev; struct power_supply *battery; + struct power_supply_desc battery_desc; int charge_type; }; @@ -133,7 +134,6 @@ static int apple_mfi_fc_set_property(struct power_supply *psy, ret = -EINVAL; } - pm_runtime_mark_last_busy(&mfi->udev->dev); pm_runtime_put_autosuspend(&mfi->udev->dev); return ret; @@ -178,32 +178,48 @@ static int mfi_fc_probe(struct usb_device *udev) { struct power_supply_config battery_cfg = {}; struct mfi_device *mfi = NULL; + char *battery_name; int err; if (!mfi_fc_match(udev)) return -ENODEV; - mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL); + mfi = kzalloc_obj(struct mfi_device); if (!mfi) return -ENOMEM; + battery_name = kasprintf(GFP_KERNEL, "apple_mfi_fastcharge_%d-%d", + udev->bus->busnum, udev->devnum); + if (!battery_name) { + err = -ENOMEM; + goto err_free_mfi; + } + + mfi->battery_desc = apple_mfi_fc_desc; + mfi->battery_desc.name = battery_name; + battery_cfg.drv_data = mfi; mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; mfi->battery = power_supply_register(&udev->dev, - &apple_mfi_fc_desc, + &mfi->battery_desc, &battery_cfg); if (IS_ERR(mfi->battery)) { dev_err(&udev->dev, "Can't register battery\n"); err = PTR_ERR(mfi->battery); - kfree(mfi); - return err; + goto err_free_name; } - mfi->udev = usb_get_dev(udev); + mfi->udev = udev; dev_set_drvdata(&udev->dev, mfi); return 0; + +err_free_name: + kfree(battery_name); +err_free_mfi: + kfree(mfi); + return err; } static void mfi_fc_disconnect(struct usb_device *udev) @@ -213,8 +229,8 @@ static void mfi_fc_disconnect(struct usb_device *udev) mfi = dev_get_drvdata(&udev->dev); if (mfi->battery) power_supply_unregister(mfi->battery); + kfree(mfi->battery_desc.name); dev_set_drvdata(&udev->dev, NULL); - usb_put_dev(mfi->udev); kfree(mfi); } diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index 62b5a30edc42..16883592f7fc 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/hid.h> #include <linux/usb.h> #include <linux/backlight.h> #include <linux/timer.h> @@ -20,9 +21,6 @@ #define APPLE_VENDOR_ID 0x05AC -#define USB_REQ_GET_REPORT 0x01 -#define USB_REQ_SET_REPORT 0x09 - #define ACD_USB_TIMEOUT 250 #define ACD_USB_EDID 0x0302 @@ -140,7 +138,7 @@ static int appledisplay_bl_update_status(struct backlight_device *bd) retval = usb_control_msg( pdata->udev, usb_sndctrlpipe(pdata->udev, 0), - USB_REQ_SET_REPORT, + HID_REQ_SET_REPORT, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, ACD_USB_BRIGHTNESS, 0, @@ -163,7 +161,7 @@ static int appledisplay_bl_get_brightness(struct backlight_device *bd) retval = usb_control_msg( pdata->udev, usb_rcvctrlpipe(pdata->udev, 0), - USB_REQ_GET_REPORT, + HID_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, ACD_USB_BRIGHTNESS, 0, @@ -226,7 +224,7 @@ static int appledisplay_probe(struct usb_interface *iface, int_in_endpointAddr = endpoint->bEndpointAddress; /* allocate memory for our device state and initialize it */ - pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL); + pdata = kzalloc_obj(struct appledisplay); if (!pdata) { retval = -ENOMEM; goto error; diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index 225863321dc4..d8016540953f 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -143,7 +143,7 @@ static int chaoskey_probe(struct usb_interface *interface, /* Looks good, allocate and initialize */ - dev = kzalloc(sizeof(struct chaoskey), GFP_KERNEL); + dev = kzalloc_obj(struct chaoskey); if (dev == NULL) goto out; @@ -444,9 +444,19 @@ static ssize_t chaoskey_read(struct file *file, goto bail; mutex_unlock(&dev->rng_lock); - result = mutex_lock_interruptible(&dev->lock); - if (result) - goto bail; + if (file->f_flags & O_NONBLOCK) { + result = mutex_trylock(&dev->lock); + if (result == 0) { + result = -EAGAIN; + goto bail; + } else { + result = 0; + } + } else { + result = mutex_lock_interruptible(&dev->lock); + if (result) + goto bail; + } if (dev->valid == dev->used) { result = _chaoskey_fill(dev); if (result < 0) { diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index 75f5a740cba3..4a7f955ba85b 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -211,11 +211,11 @@ static int cypress_probe(struct usb_interface *interface, int retval = -ENOMEM; /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) goto error_mem; - dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->udev = interface_to_usbdev(interface); /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); @@ -239,8 +239,6 @@ static void cypress_disconnect(struct usb_interface *interface) * device files have been removed */ usb_set_intfdata(interface, NULL); - usb_put_dev(dev->udev); - dev_info(&interface->dev, "Cypress CY7C63xxx device now disconnected\n"); diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 875016dd073c..b183df9826bc 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -307,11 +307,11 @@ static int cytherm_probe(struct usb_interface *interface, struct usb_cytherm *dev; int retval = -ENOMEM; - dev = kzalloc(sizeof(struct usb_cytherm), GFP_KERNEL); + dev = kzalloc_obj(struct usb_cytherm); if (!dev) goto error_mem; - dev->udev = usb_get_dev(udev); + dev->udev = udev; usb_set_intfdata(interface, dev); @@ -334,8 +334,6 @@ static void cytherm_disconnect(struct usb_interface *interface) /* first remove the files, then NULL the pointer */ usb_set_intfdata(interface, NULL); - usb_put_dev(dev->udev); - kfree(dev); dev_info(&interface->dev, "Cypress thermometer now disconnected\n"); diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index ea39243efee3..0f6b3464c2d6 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -330,7 +330,7 @@ static int idmouse_probe(struct usb_interface *interface, return -ENODEV; /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (dev == NULL) return -ENOMEM; diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 365c10069345..22504c0a2841 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -21,6 +21,7 @@ #include <linux/sched.h> #include <linux/mutex.h> #include <linux/poll.h> +#include <linux/hid.h> #include <linux/usb/iowarrior.h> #define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>" @@ -74,7 +75,6 @@ struct iowarrior { struct mutex mutex; /* locks this structure */ struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface; /* the interface for this device */ - unsigned char minor; /* the starting minor number for this device */ struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */ struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */ struct urb *int_in_urb; /* the urb for reading data */ @@ -99,14 +99,13 @@ struct iowarrior { /* globals */ /*--------------*/ -#define USB_REQ_GET_REPORT 0x01 //#if 0 static int usb_get_report(struct usb_device *dev, struct usb_host_interface *inter, unsigned char type, unsigned char id, void *buf, int size) { return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_REPORT, + HID_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, (type << 8) + id, inter->desc.bInterfaceNumber, buf, size, @@ -114,14 +113,12 @@ static int usb_get_report(struct usb_device *dev, } //#endif -#define USB_REQ_SET_REPORT 0x09 - static int usb_set_report(struct usb_interface *intf, unsigned char type, unsigned char id, void *buf, int size) { return usb_control_msg(interface_to_usbdev(intf), usb_sndctrlpipe(interface_to_usbdev(intf), 0), - USB_REQ_SET_REPORT, + HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, (type << 8) + id, intf->cur_altsetting->desc.bInterfaceNumber, buf, @@ -234,8 +231,7 @@ static void iowarrior_write_callback(struct urb *urb) "nonzero write bulk status received: %d\n", status); } /* free up our allocated buffer */ - usb_free_coherent(urb->dev, urb->transfer_buffer_length, - urb->transfer_buffer, urb->transfer_dma); + kfree(urb->transfer_buffer); /* tell a waiting writer the interrupt-out-pipe is available again */ atomic_dec(&dev->write_busy); wake_up_interruptible(&dev->write_wait); @@ -246,7 +242,6 @@ static void iowarrior_write_callback(struct urb *urb) */ static inline void iowarrior_delete(struct iowarrior *dev) { - dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor); kfree(dev->int_in_buffer); usb_free_urb(dev->int_in_urb); kfree(dev->read_queue); @@ -297,9 +292,6 @@ static ssize_t iowarrior_read(struct file *file, char __user *buffer, 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))) { @@ -367,20 +359,21 @@ static ssize_t iowarrior_write(struct file *file, size_t count, loff_t *ppos) { struct iowarrior *dev; - int retval = 0; + int retval; char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */ struct urb *int_out_urb = NULL; dev = file->private_data; - mutex_lock(&dev->mutex); + retval = mutex_lock_interruptible(&dev->mutex); + if (retval < 0) + return -EINTR; + /* verify that the device wasn't unplugged */ if (!dev->present) { retval = -ENODEV; goto exit; } - dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n", - dev->minor, count); /* if count is 0 we're already done */ if (count == 0) { retval = 0; @@ -443,8 +436,7 @@ static ssize_t iowarrior_write(struct file *file, retval = -ENOMEM; goto error_no_urb; } - buf = usb_alloc_coherent(dev->udev, dev->report_size, - GFP_KERNEL, &int_out_urb->transfer_dma); + buf = kmalloc(dev->report_size, GFP_KERNEL); if (!buf) { retval = -ENOMEM; dev_dbg(&dev->interface->dev, @@ -457,7 +449,6 @@ static ssize_t iowarrior_write(struct file *file, buf, dev->report_size, iowarrior_write_callback, dev, dev->int_out_endpoint->bInterval); - int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; if (copy_from_user(buf, user_buffer, count)) { retval = -EFAULT; goto error; @@ -483,8 +474,7 @@ static ssize_t iowarrior_write(struct file *file, goto exit; } error: - usb_free_coherent(dev->udev, dev->report_size, buf, - int_out_urb->transfer_dma); + kfree(buf); error_no_buffer: usb_free_urb(int_out_urb); error_no_urb: @@ -523,9 +513,6 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, goto error_out; } - dev_dbg(&dev->interface->dev, "minor %d, cmd 0x%.4x, arg %ld\n", - dev->minor, cmd, arg); - retval = 0; switch (cmd) { case IOW_WRITE: @@ -671,8 +658,6 @@ static int iowarrior_release(struct inode *inode, struct file *file) if (!dev) return -ENODEV; - dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor); - /* lock our device */ mutex_lock(&dev->mutex); @@ -775,9 +760,10 @@ static int iowarrior_probe(struct usb_interface *interface, struct usb_host_interface *iface_desc; int retval = -ENOMEM; int res; + int minor; /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL); + dev = kzalloc_obj(struct iowarrior); if (!dev) return retval; @@ -890,12 +876,12 @@ static int iowarrior_probe(struct usb_interface *interface, goto error; } - dev->minor = interface->minor; + minor = interface->minor; /* let the user know what node this device is now attached to */ dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d " "now attached to iowarrior%d\n", dev->product_id, dev->chip_serial, - iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE); + iface_desc->desc.bInterfaceNumber, minor - IOWARRIOR_MINOR_BASE); return retval; error: diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index f392d6f84df9..c74f142f6637 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -656,7 +656,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) goto exit; mutex_init(&dev->mutex); diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 379cf01a6e96..052ffc2e71ee 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -748,7 +748,7 @@ static int tower_probe(struct usb_interface *interface, const struct usb_device_ int result; /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) goto exit; diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c index 25ec5666a75e..9b6e10177f80 100644 --- a/drivers/usb/misc/lvstest.c +++ b/drivers/usb/misc/lvstest.c @@ -260,7 +260,7 @@ static ssize_t get_dev_desc_store(struct device *dev, struct usb_device_descriptor *descriptor; int ret; - descriptor = kmalloc(sizeof(*descriptor), GFP_KERNEL); + descriptor = kmalloc_obj(*descriptor); if (!descriptor) return -ENOMEM; diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c index 75ac3c6aa92d..7cdbdfe07a76 100644 --- a/drivers/usb/misc/onboard_usb_dev.c +++ b/drivers/usb/misc/onboard_usb_dev.c @@ -36,9 +36,10 @@ #define USB5744_CMD_CREG_ACCESS 0x99 #define USB5744_CMD_CREG_ACCESS_LSB 0x37 #define USB5744_CREG_MEM_ADDR 0x00 +#define USB5744_CREG_MEM_RD_ADDR 0x04 #define USB5744_CREG_WRITE 0x00 -#define USB5744_CREG_RUNTIMEFLAGS2 0x41 -#define USB5744_CREG_RUNTIMEFLAGS2_LSB 0x1D +#define USB5744_CREG_READ 0x01 +#define USB5744_CREG_RUNTIMEFLAGS2 0x411D #define USB5744_CREG_BYPASS_UDC_SUSPEND BIT(3) static void onboard_dev_attach_usb_driver(struct work_struct *work); @@ -201,7 +202,7 @@ static int onboard_dev_add_usbdev(struct onboard_dev *onboard_dev, goto error; } - node = kzalloc(sizeof(*node), GFP_KERNEL); + node = kzalloc_obj(*node); if (!node) { err = -ENOMEM; goto error; @@ -309,11 +310,88 @@ static void onboard_dev_attach_usb_driver(struct work_struct *work) pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err)); } +#if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744) +static int onboard_dev_5744_i2c_read_byte(struct i2c_client *client, u16 addr, u8 *data) +{ + struct i2c_msg msg[2]; + u8 rd_buf[3]; + int ret; + + u8 wr_buf[7] = {0, USB5744_CREG_MEM_ADDR, 4, + USB5744_CREG_READ, 1, + addr >> 8 & 0xff, + addr & 0xff}; + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = sizeof(wr_buf); + msg[0].buf = wr_buf; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) + return ret; + + wr_buf[0] = USB5744_CMD_CREG_ACCESS; + wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB; + wr_buf[2] = 0; + msg[0].len = 3; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) + return ret; + + wr_buf[0] = 0; + wr_buf[1] = USB5744_CREG_MEM_RD_ADDR; + msg[0].len = 2; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = rd_buf; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) + return ret; + *data = rd_buf[1]; + + return 0; +} + +static int onboard_dev_5744_i2c_write_byte(struct i2c_client *client, u16 addr, u8 data) +{ + struct i2c_msg msg[2]; + int ret; + + u8 wr_buf[8] = {0, USB5744_CREG_MEM_ADDR, 5, + USB5744_CREG_WRITE, 1, + addr >> 8 & 0xff, + addr & 0xff, + data}; + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = sizeof(wr_buf); + msg[0].buf = wr_buf; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) + return ret; + + msg[0].len = 3; + wr_buf[0] = USB5744_CMD_CREG_ACCESS; + wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB; + wr_buf[2] = 0; + + ret = i2c_transfer(client->adapter, msg, 1); + if (ret < 0) + return ret; + + return 0; +} + static int onboard_dev_5744_i2c_init(struct i2c_client *client) { -#if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744) struct device *dev = &client->dev; int ret; + u8 reg; /* * Set BYPASS_UDC_SUSPEND bit to ensure MCU is always enabled @@ -321,20 +399,16 @@ static int onboard_dev_5744_i2c_init(struct i2c_client *client) * The command writes 5 bytes to memory and single data byte in * configuration register. */ - char wr_buf[7] = {USB5744_CREG_MEM_ADDR, 5, - USB5744_CREG_WRITE, 1, - USB5744_CREG_RUNTIMEFLAGS2, - USB5744_CREG_RUNTIMEFLAGS2_LSB, - USB5744_CREG_BYPASS_UDC_SUSPEND}; - - ret = i2c_smbus_write_block_data(client, 0, sizeof(wr_buf), wr_buf); + ret = onboard_dev_5744_i2c_read_byte(client, + USB5744_CREG_RUNTIMEFLAGS2, ®); if (ret) - return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n"); + return dev_err_probe(dev, ret, "CREG_RUNTIMEFLAGS2 read failed\n"); - ret = i2c_smbus_write_word_data(client, USB5744_CMD_CREG_ACCESS, - USB5744_CMD_CREG_ACCESS_LSB); + reg |= USB5744_CREG_BYPASS_UDC_SUSPEND; + ret = onboard_dev_5744_i2c_write_byte(client, + USB5744_CREG_RUNTIMEFLAGS2, reg); if (ret) - return dev_err_probe(dev, ret, "Configuration Register Access Command failed\n"); + return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n"); /* Send SMBus command to boot hub. */ ret = i2c_smbus_write_word_data(client, USB5744_CMD_ATTACH, @@ -343,10 +417,13 @@ static int onboard_dev_5744_i2c_init(struct i2c_client *client) return dev_err_probe(dev, ret, "USB Attach with SMBus command failed\n"); return ret; +} #else +static int onboard_dev_5744_i2c_init(struct i2c_client *client) +{ return -ENODEV; -#endif } +#endif static int onboard_dev_probe(struct platform_device *pdev) { @@ -487,10 +564,14 @@ static struct platform_driver onboard_dev_driver = { /************************** USB driver **************************/ +#define VENDOR_ID_BISON 0x5986 +#define VENDOR_ID_CORECHIPS 0x3431 #define VENDOR_ID_CYPRESS 0x04b4 #define VENDOR_ID_GENESYS 0x05e3 #define VENDOR_ID_MICROCHIP 0x0424 +#define VENDOR_ID_PARADE 0x1da0 #define VENDOR_ID_REALTEK 0x0bda +#define VENDOR_ID_TERMINUS 0x1a40 #define VENDOR_ID_TI 0x0451 #define VENDOR_ID_VIA 0x2109 #define VENDOR_ID_XMOS 0x20B1 @@ -569,8 +650,17 @@ static void onboard_dev_usbdev_disconnect(struct usb_device *udev) } static const struct usb_device_id onboard_dev_id_table[] = { - { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 HUB */ - { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 HUB */ + { USB_DEVICE(VENDOR_ID_BISON, 0x1198) }, /* Bison Electronics Inc. Integrated Camera */ + { USB_DEVICE(VENDOR_ID_CORECHIPS, 0x6241) }, /* SL6341 2.0 HUB */ + { USB_DEVICE(VENDOR_ID_CORECHIPS, 0x6341) }, /* SL6341 3.0 HUB */ + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6500) }, /* CYUSB330x 3.0 HUB */ + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6502) }, /* CYUSB330x 2.0 HUB */ + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6503) }, /* CYUSB33{0,1}x 2.0 HUB, Vendor Mode */ + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB331x 3.0 HUB */ + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB331x 2.0 HUB */ + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6507) }, /* CYUSB332x 2.0 HUB, Vendor Mode */ + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6508) }, /* CYUSB332x 3.0 HUB */ + { USB_DEVICE(VENDOR_ID_CYPRESS, 0x650a) }, /* CYUSB332x 2.0 HUB */ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6570) }, /* CY7C6563x 2.0 HUB */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 HUB */ { USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 HUB */ @@ -580,14 +670,20 @@ static const struct usb_device_id onboard_dev_id_table[] = { { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 HUB */ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2744) }, /* USB5744 USB 2.0 HUB */ { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x5744) }, /* USB5744 USB 3.0 HUB */ + { USB_DEVICE(VENDOR_ID_PARADE, 0x5511) }, /* PS5511 USB 3.2 */ + { USB_DEVICE(VENDOR_ID_PARADE, 0x55a1) }, /* PS5511 USB 2.0 */ { USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 HUB */ { USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 HUB */ { USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 HUB */ { USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 HUB */ + { USB_DEVICE(VENDOR_ID_REALTEK, 0x0179) }, /* RTL8188ETV 2.4GHz WiFi */ + { USB_DEVICE(VENDOR_ID_TERMINUS, 0x0101) }, /* Terminus FE1.1s 2.0 HUB */ { USB_DEVICE(VENDOR_ID_TI, 0x8025) }, /* TI USB8020B 3.0 HUB */ { USB_DEVICE(VENDOR_ID_TI, 0x8027) }, /* TI USB8020B 2.0 HUB */ { USB_DEVICE(VENDOR_ID_TI, 0x8140) }, /* TI USB8041 3.0 HUB */ { USB_DEVICE(VENDOR_ID_TI, 0x8142) }, /* TI USB8041 2.0 HUB */ + { USB_DEVICE(VENDOR_ID_TI, 0x8440) }, /* TI USB8044 3.0 HUB */ + { USB_DEVICE(VENDOR_ID_TI, 0x8442) }, /* TI USB8044 2.0 HUB */ { USB_DEVICE(VENDOR_ID_VIA, 0x0817) }, /* VIA VL817 3.1 HUB */ { USB_DEVICE(VENDOR_ID_VIA, 0x2817) }, /* VIA VL817 2.0 HUB */ { USB_DEVICE(VENDOR_ID_XMOS, 0x0013) }, /* XMOS XVF3500 Voice Processor */ diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h index 317b3eb99c02..ac1aa3e122ad 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, @@ -31,6 +38,13 @@ static const struct onboard_dev_pdata microchip_usb5744_data = { .is_hub = true, }; +static const struct onboard_dev_pdata parade_ps5511_data = { + .reset_us = 500, + .num_supplies = 2, + .supply_names = { "vddd11", "vdd33"}, + .is_hub = true, +}; + static const struct onboard_dev_pdata realtek_rts5411_data = { .reset_us = 0, .num_supplies = 1, @@ -38,6 +52,13 @@ static const struct onboard_dev_pdata realtek_rts5411_data = { .is_hub = true, }; +static const struct onboard_dev_pdata realtek_rtl8188etv_data = { + .reset_us = 0, + .num_supplies = 1, + .supply_names = { "vdd" }, + .is_hub = false, +}; + static const struct onboard_dev_pdata ti_tusb8020b_data = { .reset_us = 3000, .num_supplies = 1, @@ -45,24 +66,24 @@ static const struct onboard_dev_pdata ti_tusb8020b_data = { .is_hub = true, }; -static const struct onboard_dev_pdata ti_tusb8041_data = { - .reset_us = 3000, +static const struct onboard_dev_pdata bison_intcamera_data = { + .reset_us = 1000, .num_supplies = 1, .supply_names = { "vdd" }, - .is_hub = true, + .is_hub = false, }; -static const struct onboard_dev_pdata cypress_hx3_data = { +static const struct onboard_dev_pdata corechips_sl6341_data = { .reset_us = 10000, .num_supplies = 2, - .supply_names = { "vdd", "vdd2" }, + .supply_names = { "vdd1v1", "vdd3v3" }, .is_hub = true, }; -static const struct onboard_dev_pdata cypress_hx2vl_data = { - .reset_us = 1, - .num_supplies = 1, - .supply_names = { "vdd" }, +static const struct onboard_dev_pdata cypress_hx3_data = { + .reset_us = 10000, + .num_supplies = 2, + .supply_names = { "vdd", "vdd2" }, .is_hub = true, }; @@ -80,6 +101,11 @@ static const struct onboard_dev_pdata genesys_gl852g_data = { .is_hub = true, }; +static const struct onboard_dev_pdata usb_a_conn_data = { + .num_supplies = 1, + .supply_names = { "vbus" }, +}; + static const struct onboard_dev_pdata vialab_vl817_data = { .reset_us = 10, .num_supplies = 1, @@ -87,6 +113,13 @@ static const struct onboard_dev_pdata vialab_vl817_data = { .is_hub = true, }; +static const struct onboard_dev_pdata wch_ch334_data = { + .reset_us = 14000, + .num_supplies = 2, + .supply_names = { "vdd33", "v5" }, + .is_hub = true, +}; + static const struct onboard_dev_pdata xmos_xvf3500_data = { .reset_us = 1, .num_supplies = 2, @@ -95,29 +128,40 @@ static const struct onboard_dev_pdata xmos_xvf3500_data = { }; static const struct of_device_id onboard_dev_match[] = { + { .compatible = "usb-a-connector", .data = &usb_a_conn_data, }, { .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, }, { .compatible = "usb451,8025", .data = &ti_tusb8020b_data, }, { .compatible = "usb451,8027", .data = &ti_tusb8020b_data, }, - { .compatible = "usb451,8140", .data = &ti_tusb8041_data, }, - { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, + { .compatible = "usb451,8140", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8142", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8440", .data = &ti_tusb8020b_data, }, + { .compatible = "usb451,8442", .data = &ti_tusb8020b_data, }, { .compatible = "usb4b4,6504", .data = &cypress_hx3_data, }, { .compatible = "usb4b4,6506", .data = &cypress_hx3_data, }, - { .compatible = "usb4b4,6570", .data = &cypress_hx2vl_data, }, + { .compatible = "usb4b4,6570", .data = µchip_usb424_data, }, { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, }, { .compatible = "usb5e3,626", .data = &genesys_gl852g_data, }, + { .compatible = "usbbda,179", .data = &realtek_rtl8188etv_data, }, { .compatible = "usbbda,411", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,414", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,5414", .data = &realtek_rts5411_data, }, + { .compatible = "usb1a40,0101", .data = &vialab_vl817_data, }, + { .compatible = "usb1a86,8091", .data = &wch_ch334_data, }, + { .compatible = "usb1da0,5511", .data = ¶de_ps5511_data, }, + { .compatible = "usb1da0,55a1", .data = ¶de_ps5511_data, }, { .compatible = "usb2109,817", .data = &vialab_vl817_data, }, { .compatible = "usb2109,2817", .data = &vialab_vl817_data, }, { .compatible = "usb20b1,0013", .data = &xmos_xvf3500_data, }, + { .compatible = "usb3431,6241", .data = &corechips_sl6341_data, }, + { .compatible = "usb3431,6341", .data = &corechips_sl6341_data, }, + { .compatible = "usb5986,1198", .data = &bison_intcamera_data, }, {} }; diff --git a/drivers/usb/misc/onboard_usb_dev_pdevs.c b/drivers/usb/misc/onboard_usb_dev_pdevs.c index 722504752ce3..cdb6949e18ec 100644 --- a/drivers/usb/misc/onboard_usb_dev_pdevs.c +++ b/drivers/usb/misc/onboard_usb_dev_pdevs.c @@ -109,7 +109,7 @@ void onboard_dev_create_pdevs(struct usb_device *parent_hub, struct list_head *p goto node_put; } - pdle = kzalloc(sizeof(*pdle), GFP_KERNEL); + pdle = kzalloc_obj(*pdle); if (!pdle) { of_platform_device_destroy(&pdev->dev, NULL); goto node_put; diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c index 83079c414b4f..926419ca560f 100644 --- a/drivers/usb/misc/qcom_eud.c +++ b/drivers/usb/misc/qcom_eud.c @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/usb/role.h> +#include <linux/firmware/qcom/qcom_scm.h> #define EUD_REG_INT1_EN_MASK 0x0024 #define EUD_REG_INT_STATUS_1 0x0044 @@ -34,7 +35,7 @@ struct eud_chip { struct device *dev; struct usb_role_switch *role_sw; void __iomem *base; - void __iomem *mode_mgr; + phys_addr_t mode_mgr; unsigned int int_status; int irq; bool enabled; @@ -43,18 +44,29 @@ struct eud_chip { static int enable_eud(struct eud_chip *priv) { + int ret; + + ret = qcom_scm_io_writel(priv->mode_mgr + EUD_REG_EUD_EN2, 1); + if (ret) + return ret; + writel(EUD_ENABLE, priv->base + EUD_REG_CSR_EUD_EN); writel(EUD_INT_VBUS | EUD_INT_SAFE_MODE, priv->base + EUD_REG_INT1_EN_MASK); - writel(1, priv->mode_mgr + EUD_REG_EUD_EN2); return usb_role_switch_set_role(priv->role_sw, USB_ROLE_DEVICE); } -static void disable_eud(struct eud_chip *priv) +static int disable_eud(struct eud_chip *priv) { + int ret; + + ret = qcom_scm_io_writel(priv->mode_mgr + EUD_REG_EUD_EN2, 0); + if (ret) + return ret; + writel(0, priv->base + EUD_REG_CSR_EUD_EN); - writel(0, priv->mode_mgr + EUD_REG_EUD_EN2); + return 0; } static ssize_t enable_show(struct device *dev, @@ -82,11 +94,12 @@ static ssize_t enable_store(struct device *dev, chip->enabled = enable; else disable_eud(chip); + } else { - disable_eud(chip); + ret = disable_eud(chip); } - return count; + return ret < 0 ? ret : count; } static DEVICE_ATTR_RW(enable); @@ -178,6 +191,7 @@ static void eud_role_switch_release(void *data) static int eud_probe(struct platform_device *pdev) { struct eud_chip *chip; + struct resource *res; int ret; chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); @@ -193,16 +207,16 @@ static int eud_probe(struct platform_device *pdev) ret = devm_add_action_or_reset(chip->dev, eud_role_switch_release, chip); if (ret) - return dev_err_probe(chip->dev, ret, - "failed to add role switch release action\n"); + return ret; chip->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(chip->base)) return PTR_ERR(chip->base); - chip->mode_mgr = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(chip->mode_mgr)) - return PTR_ERR(chip->mode_mgr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -ENODEV; + chip->mode_mgr = res->start; chip->irq = platform_get_irq(pdev, 0); if (chip->irq < 0) diff --git a/drivers/usb/misc/sisusbvga/sisusbvga.c b/drivers/usb/misc/sisusbvga/sisusbvga.c index febf34f9f049..3e75a7c24828 100644 --- a/drivers/usb/misc/sisusbvga/sisusbvga.c +++ b/drivers/usb/misc/sisusbvga/sisusbvga.c @@ -2797,7 +2797,7 @@ static int sisusb_probe(struct usb_interface *intf, dev->devnum); /* Allocate memory for our private */ - sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL); + sisusb = kzalloc_obj(*sisusb); if (!sisusb) return -ENOMEM; diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c index 26baba3ab7d7..37f6b79889a6 100644 --- a/drivers/usb/misc/trancevibrator.c +++ b/drivers/usb/misc/trancevibrator.c @@ -86,13 +86,13 @@ static int tv_probe(struct usb_interface *interface, struct trancevibrator *dev; int retval; - dev = kzalloc(sizeof(struct trancevibrator), GFP_KERNEL); + dev = kzalloc_obj(struct trancevibrator); if (!dev) { retval = -ENOMEM; goto error; } - dev->udev = usb_get_dev(udev); + dev->udev = udev; usb_set_intfdata(interface, dev); return 0; @@ -108,7 +108,6 @@ static void tv_disconnect(struct usb_interface *interface) dev = usb_get_intfdata (interface); usb_set_intfdata(interface, NULL); - usb_put_dev(dev->udev); kfree(dev); } diff --git a/drivers/usb/misc/usb-ljca.c b/drivers/usb/misc/usb-ljca.c index c562630d862c..c60121faa3da 100644 --- a/drivers/usb/misc/usb-ljca.c +++ b/drivers/usb/misc/usb-ljca.c @@ -164,28 +164,39 @@ struct ljca_match_ids_walk_data { struct acpi_device *adev; }; +/* + * ACPI hardware IDs for LJCA client devices. + * + * [1] Some BIOS implementations use these IDs for denoting LJCA client devices + * even though the IDs have been allocated for USBIO. This isn't a problem + * as the usb-ljca driver is probed based on the USB device's vendor and + * product IDs and its client drivers are probed based on auxiliary device + * names, not these ACPI _HIDs. List of such systems: + * + * Dell Precision 5490 + */ static const struct acpi_device_id ljca_gpio_hids[] = { - { "INTC1074" }, - { "INTC1096" }, - { "INTC100B" }, - { "INTC10D1" }, - { "INTC10B5" }, + { "INTC100B" }, /* RPL LJCA GPIO */ + { "INTC1074" }, /* CVF LJCA GPIO */ + { "INTC1096" }, /* ADL LJCA GPIO */ + { "INTC10B5" }, /* LNL LJCA GPIO */ + { "INTC10D1" }, /* MTL (CVF VSC) USBIO GPIO [1] */ {}, }; static const struct acpi_device_id ljca_i2c_hids[] = { - { "INTC1075" }, - { "INTC1097" }, - { "INTC100C" }, - { "INTC10D2" }, + { "INTC100C" }, /* RPL LJCA I2C */ + { "INTC1075" }, /* CVF LJCA I2C */ + { "INTC1097" }, /* ADL LJCA I2C */ + { "INTC10D2" }, /* MTL (CVF VSC) USBIO I2C [1] */ {}, }; static const struct acpi_device_id ljca_spi_hids[] = { - { "INTC1091" }, - { "INTC1098" }, - { "INTC100D" }, - { "INTC10D3" }, + { "INTC100D" }, /* RPL LJCA SPI */ + { "INTC1091" }, /* TGL/ADL LJCA SPI */ + { "INTC1098" }, /* ADL LJCA SPI */ + { "INTC10D3" }, /* MTL (CVF VSC) USBIO SPI [1] */ {}, }; @@ -518,7 +529,7 @@ static int ljca_new_client_device(struct ljca_adapter *adap, u8 type, u8 id, struct ljca_client *client; int ret; - client = kzalloc(sizeof *client, GFP_KERNEL); + client = kzalloc_obj(*client); if (!client) { kfree(data); return -ENOMEM; @@ -586,7 +597,7 @@ static int ljca_enumerate_gpio(struct ljca_adapter *adap) return -EINVAL; /* construct platform data */ - gpio_info = kzalloc(sizeof *gpio_info, GFP_KERNEL); + gpio_info = kzalloc_obj(*gpio_info); if (!gpio_info) return -ENOMEM; gpio_info->num = gpio_num; @@ -619,7 +630,7 @@ static int ljca_enumerate_i2c(struct ljca_adapter *adap) for (i = 0; i < desc->num; i++) { /* construct platform data */ - i2c_info = kzalloc(sizeof *i2c_info, GFP_KERNEL); + i2c_info = kzalloc_obj(*i2c_info); if (!i2c_info) return -ENOMEM; @@ -658,7 +669,7 @@ static int ljca_enumerate_spi(struct ljca_adapter *adap) for (i = 0; i < desc->num; i++) { /* construct platform data */ - spi_info = kzalloc(sizeof *spi_info, GFP_KERNEL); + spi_info = kzalloc_obj(*spi_info); if (!spi_info) return -ENOMEM; @@ -765,7 +776,7 @@ static int ljca_probe(struct usb_interface *interface, init_completion(&adap->cmd_completion); INIT_LIST_HEAD(&adap->client_list); - adap->intf = usb_get_intf(interface); + adap->intf = interface; adap->usb_dev = usb_dev; adap->dev = dev; @@ -776,7 +787,7 @@ static int ljca_probe(struct usb_interface *interface, ret = usb_find_common_endpoints(alt, &ep_in, &ep_out, NULL, NULL); if (ret) { dev_err(dev, "bulk endpoints not found\n"); - goto err_put; + goto err_destroy_mutex; } adap->rx_pipe = usb_rcvbulkpipe(usb_dev, usb_endpoint_num(ep_in)); adap->tx_pipe = usb_sndbulkpipe(usb_dev, usb_endpoint_num(ep_out)); @@ -786,14 +797,14 @@ static int ljca_probe(struct usb_interface *interface, adap->rx_buf = devm_kzalloc(dev, adap->rx_len, GFP_KERNEL); if (!adap->rx_buf) { ret = -ENOMEM; - goto err_put; + goto err_destroy_mutex; } /* alloc rx urb */ adap->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!adap->rx_urb) { ret = -ENOMEM; - goto err_put; + goto err_destroy_mutex; } usb_fill_bulk_urb(adap->rx_urb, usb_dev, adap->rx_pipe, adap->rx_buf, adap->rx_len, ljca_recv, adap); @@ -825,10 +836,7 @@ static int ljca_probe(struct usb_interface *interface, err_free: usb_free_urb(adap->rx_urb); - -err_put: - usb_put_intf(adap->intf); - +err_destroy_mutex: mutex_destroy(&adap->mutex); return ret; @@ -853,8 +861,6 @@ static void ljca_disconnect(struct usb_interface *interface) usb_free_urb(adap->rx_urb); - usb_put_intf(adap->intf); - mutex_destroy(&adap->mutex); } @@ -891,7 +897,7 @@ static struct usb_driver ljca_driver = { }; module_usb_driver(ljca_driver); -MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>"); +MODULE_AUTHOR("Wentong Wu"); MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>"); MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>"); MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB driver"); diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index e24cdb667307..7c0778631bea 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/nls.h> #include <linux/of.h> +#include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -242,15 +243,19 @@ static int usb251xb_check_dev_children(struct device *dev, void *child) static int usb251x_check_gpio_chip(struct usb251xb *hub) { struct gpio_chip *gc = gpiod_to_chip(hub->gpio_reset); - struct i2c_adapter *adap = hub->i2c->adapter; + struct i2c_adapter *adap; int ret; + if (!hub->i2c) + return 0; + if (!hub->gpio_reset) return 0; if (!gc) return -EINVAL; + adap = hub->i2c->adapter; ret = usb251xb_check_dev_children(&adap->dev, gc->parent); if (ret) { dev_err(hub->dev, "Reset GPIO chip is at the same i2c-bus\n"); @@ -271,7 +276,8 @@ static void usb251xb_reset(struct usb251xb *hub) if (!hub->gpio_reset) return; - i2c_lock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT); + if (hub->i2c) + i2c_lock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT); gpiod_set_value_cansleep(hub->gpio_reset, 1); usleep_range(1, 10); /* >=1us RESET_N asserted */ @@ -280,7 +286,8 @@ static void usb251xb_reset(struct usb251xb *hub) /* wait for hub recovery/stabilization */ usleep_range(500, 750); /* >=500us after RESET_N deasserted */ - i2c_unlock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT); + if (hub->i2c) + i2c_unlock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT); } static int usb251xb_connect(struct usb251xb *hub) @@ -289,6 +296,12 @@ static int usb251xb_connect(struct usb251xb *hub) int err, i; char i2c_wb[USB251XB_I2C_REG_SZ]; + if (!hub->i2c) { + usb251xb_reset(hub); + dev_info(dev, "hub is put in default configuration.\n"); + return 0; + } + memset(i2c_wb, 0, USB251XB_I2C_REG_SZ); if (hub->skip_config) { @@ -636,10 +649,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"); } /* @@ -700,18 +711,13 @@ static int usb251xb_i2c_probe(struct i2c_client *i2c) return usb251xb_probe(hub); } -static int __maybe_unused usb251xb_suspend(struct device *dev) +static int usb251xb_suspend(struct usb251xb *hub) { - struct i2c_client *client = to_i2c_client(dev); - struct usb251xb *hub = i2c_get_clientdata(client); - return regulator_disable(hub->vdd); } -static int __maybe_unused usb251xb_resume(struct device *dev) +static int usb251xb_resume(struct usb251xb *hub) { - struct i2c_client *client = to_i2c_client(dev); - struct usb251xb *hub = i2c_get_clientdata(client); int err; err = regulator_enable(hub->vdd); @@ -721,7 +727,23 @@ static int __maybe_unused usb251xb_resume(struct device *dev) return usb251xb_connect(hub); } -static SIMPLE_DEV_PM_OPS(usb251xb_pm_ops, usb251xb_suspend, usb251xb_resume); +static int usb251xb_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct usb251xb *hub = i2c_get_clientdata(client); + + return usb251xb_suspend(hub); +} + +static int usb251xb_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct usb251xb *hub = i2c_get_clientdata(client); + + return usb251xb_resume(hub); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(usb251xb_i2c_pm_ops, usb251xb_i2c_suspend, usb251xb_i2c_resume); static const struct i2c_device_id usb251xb_id[] = { { "usb2422" }, @@ -741,13 +763,71 @@ static struct i2c_driver usb251xb_i2c_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = usb251xb_of_match, - .pm = &usb251xb_pm_ops, + .pm = pm_sleep_ptr(&usb251xb_i2c_pm_ops), }, .probe = usb251xb_i2c_probe, .id_table = usb251xb_id, }; -module_i2c_driver(usb251xb_i2c_driver); +static int usb251xb_plat_probe(struct platform_device *pdev) +{ + struct usb251xb *hub; + + hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL); + if (!hub) + return -ENOMEM; + + platform_set_drvdata(pdev, hub); + hub->dev = &pdev->dev; + + return usb251xb_probe(hub); +} + +static int usb251xb_plat_suspend(struct device *dev) +{ + return usb251xb_suspend(dev_get_drvdata(dev)); +} + +static int usb251xb_plat_resume(struct device *dev) +{ + return usb251xb_resume(dev_get_drvdata(dev)); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(usb251xb_plat_pm_ops, usb251xb_plat_suspend, usb251xb_plat_resume); + +static struct platform_driver usb251xb_plat_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = usb251xb_of_match, + .pm = pm_sleep_ptr(&usb251xb_plat_pm_ops), + }, + .probe = usb251xb_plat_probe, +}; + +static int __init usb251xb_init(void) +{ + int err; + + err = i2c_add_driver(&usb251xb_i2c_driver); + if (err) + return err; + + err = platform_driver_register(&usb251xb_plat_driver); + if (err) { + i2c_del_driver(&usb251xb_i2c_driver); + return err; + } + + return 0; +} +module_init(usb251xb_init); + +static void __exit usb251xb_exit(void) +{ + platform_driver_unregister(&usb251xb_plat_driver); + i2c_del_driver(&usb251xb_i2c_driver); +} +module_exit(usb251xb_exit); MODULE_AUTHOR("Richard Leitner <richard.leitner@skidata.com>"); MODULE_DESCRIPTION("USB251x/xBi USB 2.0 Hub Controller Driver"); diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c new file mode 100644 index 000000000000..02d1e0760f0c --- /dev/null +++ b/drivers/usb/misc/usbio.c @@ -0,0 +1,752 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel USBIO Bridge driver + * + * Copyright (c) 2025 Intel Corporation. + * Copyright (c) 2025 Red Hat, Inc. + */ + +#include <linux/acpi.h> +#include <linux/auxiliary_bus.h> +#include <linux/byteorder/generic.h> +#include <linux/cleanup.h> +#include <linux/completion.h> +#include <linux/dev_printk.h> +#include <linux/device.h> +#include <linux/lockdep.h> +#include <linux/mutex.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/usb/usbio.h> + +/************************************* + * USBIO Bridge Protocol Definitions * + *************************************/ + +/* USBIO Control Commands */ +#define USBIO_CTRLCMD_PROTVER 0 +#define USBIO_CTRLCMD_FWVER 1 +#define USBIO_CTRLCMD_HS 2 +#define USBIO_CTRLCMD_ENUMGPIO 16 +#define USBIO_CTRLCMD_ENUMI2C 17 + +/* USBIO Packet Flags */ +#define USBIO_PKTFLAG_ACK BIT(0) +#define USBIO_PKTFLAG_RSP BIT(1) +#define USBIO_PKTFLAG_CMP BIT(2) +#define USBIO_PKTFLAG_ERR BIT(3) + +#define USBIO_PKTFLAGS_REQRESP (USBIO_PKTFLAG_CMP | USBIO_PKTFLAG_ACK) + +#define USBIO_CTRLXFER_TIMEOUT 0 +#define USBIO_BULKXFER_TIMEOUT 100 + +struct usbio_protver { + u8 ver; +} __packed; + +struct usbio_fwver { + u8 major; + u8 minor; + __le16 patch; + __le16 build; +} __packed; + +/*********************************** + * USBIO Bridge Device Definitions * + ***********************************/ + +/** + * struct usbio_device - the usb device exposing IOs + * + * @dev: the device in the usb interface + * @udev: the detected usb device + * @intf: the usb interface + * @quirks: quirks + * @ctrl_mutex: protects ctrl_buf + * @ctrl_pipe: the control transfer pipe + * @ctrlbuf_len: the size of the control transfer pipe + * @ctrlbuf: the buffer used for control transfers + * @bulk_mutex: protects tx_buf, rx_buf and split bulk-transfers getting interrupted + * @tx_pipe: the bulk out pipe + * @txbuf_len: the size of the bulk out pipe + * @txbuf: the buffer used for bulk out transfers + * @rx_pipe: the bulk in pipe + * @rxbuf_len: the size of the bulk in pipe + * @rxdat_len: the data length at rx buffer + * @rxbuf: the buffer used for bulk in transfers + * @urb: the urb to read bulk pipe + * @done: completion object as request is done + * @cli_list: device's client list + * @nr_gpio_banks: Number of GPIO banks + * @gpios: GPIO bank descriptors + * @nr_gpio_banks: Number of I2C busses + * @gpios: I2C bank descriptors + */ +struct usbio_device { + struct device *dev; + struct usb_device *udev; + struct usb_interface *intf; + unsigned long quirks; + + struct mutex ctrl_mutex; + unsigned int ctrl_pipe; + u16 ctrlbuf_len; + void *ctrlbuf; + + struct mutex bulk_mutex; + unsigned int tx_pipe; + u16 txbuf_len; + void *txbuf; + + unsigned int rx_pipe; + u16 rxbuf_len; + u16 rxdat_len; + void *rxbuf; + struct urb *urb; + + struct completion done; + + struct list_head cli_list; + + unsigned int nr_gpio_banks; + struct usbio_gpio_bank_desc gpios[USBIO_MAX_GPIOBANKS]; + + unsigned int nr_i2c_buses; + struct usbio_i2c_bus_desc i2cs[USBIO_MAX_I2CBUSES]; +}; + +/** + * struct usbio_client - represents a usbio client + * + * @auxdev: auxiliary device object + * @mutex: protects @bridge + * @bridge: usbio bridge who service the client + * @link: usbio bridge clients list member + */ +struct usbio_client { + struct auxiliary_device auxdev; + struct mutex mutex; + struct usbio_device *bridge; + struct list_head link; +}; + +#define adev_to_client(adev) container_of_const(adev, struct usbio_client, auxdev) + +static int usbio_ctrl_msg(struct usbio_device *usbio, u8 type, u8 cmd, + const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len) +{ + u8 request = USB_TYPE_VENDOR | USB_RECIP_DEVICE; + struct usbio_ctrl_packet *cpkt; + unsigned int pipe; + u16 cpkt_len; + int ret; + + lockdep_assert_held(&usbio->ctrl_mutex); + + if ((obuf_len > (usbio->ctrlbuf_len - sizeof(*cpkt))) || + (ibuf_len > (usbio->ctrlbuf_len - sizeof(*cpkt)))) + return -EMSGSIZE; + + /* Prepare Control Packet Header */ + cpkt = usbio->ctrlbuf; + cpkt->header.type = type; + cpkt->header.cmd = cmd; + if (type == USBIO_PKTTYPE_CTRL || ibuf_len) + cpkt->header.flags = USBIO_PKTFLAGS_REQRESP; + else + cpkt->header.flags = USBIO_PKTFLAG_CMP; + cpkt->len = obuf_len; + + /* Copy the data */ + memcpy(cpkt->data, obuf, obuf_len); + + pipe = usb_sndctrlpipe(usbio->udev, usbio->ctrl_pipe); + cpkt_len = sizeof(*cpkt) + obuf_len; + ret = usb_control_msg(usbio->udev, pipe, 0, request | USB_DIR_OUT, 0, 0, + cpkt, cpkt_len, USBIO_CTRLXFER_TIMEOUT); + dev_dbg(usbio->dev, "control out %d hdr %*phN data %*phN\n", ret, + (int)sizeof(*cpkt), cpkt, (int)cpkt->len, cpkt->data); + + if (ret != cpkt_len) { + dev_err(usbio->dev, "USB control out failed: %d\n", ret); + return (ret < 0) ? ret : -EPROTO; + } + + if (!(cpkt->header.flags & USBIO_PKTFLAG_ACK)) + return 0; + + pipe = usb_rcvctrlpipe(usbio->udev, usbio->ctrl_pipe); + cpkt_len = sizeof(*cpkt) + ibuf_len; + ret = usb_control_msg(usbio->udev, pipe, 0, request | USB_DIR_IN, 0, 0, + cpkt, cpkt_len, USBIO_CTRLXFER_TIMEOUT); + dev_dbg(usbio->dev, "control in %d hdr %*phN data %*phN\n", ret, + (int)sizeof(*cpkt), cpkt, (int)cpkt->len, cpkt->data); + + if (ret < sizeof(*cpkt)) { + dev_err(usbio->dev, "USB control in failed: %d\n", ret); + return (ret < 0) ? ret : -EPROTO; + } + + if (cpkt->header.type != type || cpkt->header.cmd != cmd || + !(cpkt->header.flags & USBIO_PKTFLAG_RSP)) { + dev_err(usbio->dev, "Unexpected reply type: %u, cmd: %u, flags: %u\n", + cpkt->header.type, cpkt->header.cmd, cpkt->header.flags); + return -EPROTO; + } + + if (cpkt->header.flags & USBIO_PKTFLAG_ERR) + return -EREMOTEIO; + + if (ibuf_len < cpkt->len) + return -ENOSPC; + + memcpy(ibuf, cpkt->data, cpkt->len); + + return cpkt->len; +} + +int usbio_control_msg(struct auxiliary_device *adev, u8 type, u8 cmd, + const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len) +{ + struct usbio_client *client = adev_to_client(adev); + struct usbio_device *usbio; + int ret; + + guard(mutex)(&client->mutex); + + usbio = client->bridge; + if (!usbio) + return -ENODEV; /* Disconnected */ + + ret = usb_autopm_get_interface(usbio->intf); + if (ret) + return ret; + + mutex_lock(&usbio->ctrl_mutex); + + ret = usbio_ctrl_msg(client->bridge, type, cmd, obuf, obuf_len, ibuf, ibuf_len); + + mutex_unlock(&usbio->ctrl_mutex); + usb_autopm_put_interface(usbio->intf); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(usbio_control_msg, "USBIO"); + +static void usbio_bulk_recv(struct urb *urb) +{ + struct usbio_bulk_packet *bpkt = urb->transfer_buffer; + struct usbio_device *usbio = urb->context; + + if (!urb->status) { + if (bpkt->header.flags & USBIO_PKTFLAG_RSP) { + usbio->rxdat_len = urb->actual_length; + complete(&usbio->done); + } + } else if (urb->status != -ENOENT) { + dev_err(usbio->dev, "Bulk in error %d\n", urb->status); + } + + usb_submit_urb(usbio->urb, GFP_ATOMIC); +} + +int usbio_bulk_msg(struct auxiliary_device *adev, u8 type, u8 cmd, bool last, + const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len) +{ + struct usbio_client *client = adev_to_client(adev); + struct usbio_device *usbio = client->bridge; + struct usbio_bulk_packet *bpkt; + int ret, act = 0; + u16 bpkt_len; + + lockdep_assert_held(&client->mutex); + lockdep_assert_held(&usbio->bulk_mutex); + + if ((obuf_len > (usbio->txbuf_len - sizeof(*bpkt))) || + (ibuf_len > (usbio->txbuf_len - sizeof(*bpkt)))) + return -EMSGSIZE; + + if (ibuf_len) + reinit_completion(&usbio->done); + + /* If no data to send, skip to read */ + if (!obuf_len) + goto read; + + /* Prepare Bulk Packet Header */ + bpkt = usbio->txbuf; + bpkt->header.type = type; + bpkt->header.cmd = cmd; + if (!last) + bpkt->header.flags = 0; + else if (ibuf_len) + bpkt->header.flags = USBIO_PKTFLAGS_REQRESP; + else + bpkt->header.flags = USBIO_PKTFLAG_CMP; + bpkt->len = cpu_to_le16(obuf_len); + + /* Copy the data */ + memcpy(bpkt->data, obuf, obuf_len); + + bpkt_len = sizeof(*bpkt) + obuf_len; + ret = usb_bulk_msg(usbio->udev, usbio->tx_pipe, bpkt, bpkt_len, &act, + USBIO_BULKXFER_TIMEOUT); + dev_dbg(usbio->dev, "bulk out %d hdr %*phN data %*phN\n", act, + (int)sizeof(*bpkt), bpkt, obuf_len, bpkt->data); + + if (ret || act != bpkt_len) { + dev_err(usbio->dev, "Bulk out failed: %d\n", ret); + return ret ?: -EPROTO; + } + + if (!(bpkt->header.flags & USBIO_PKTFLAG_ACK)) + return obuf_len; + +read: + ret = wait_for_completion_timeout(&usbio->done, USBIO_BULKXFER_TIMEOUT); + if (ret <= 0) { + dev_err(usbio->dev, "Bulk in wait failed: %d\n", ret); + return ret ?: -ETIMEDOUT; + } + + act = usbio->rxdat_len; + bpkt = usbio->rxbuf; + bpkt_len = le16_to_cpu(bpkt->len); + dev_dbg(usbio->dev, "bulk in %d hdr %*phN data %*phN\n", act, + (int)sizeof(*bpkt), bpkt, bpkt_len, bpkt->data); + + /* + * Unsupported bulk commands get only an usbio_packet_header with + * the error flag set as reply. Return -EPIPE for this case. + */ + if (act == sizeof(struct usbio_packet_header) && + (bpkt->header.flags & USBIO_PKTFLAG_ERR)) + return -EPIPE; + + if (act < sizeof(*bpkt)) { + dev_err(usbio->dev, "Bulk in short read: %d\n", act); + return -EPROTO; + } + + if (bpkt->header.type != type || bpkt->header.cmd != cmd || + !(bpkt->header.flags & USBIO_PKTFLAG_RSP)) { + dev_err(usbio->dev, + "Unexpected bulk in type 0x%02x cmd 0x%02x flags 0x%02x\n", + bpkt->header.type, bpkt->header.cmd, bpkt->header.flags); + return -EPROTO; + } + + if (bpkt->header.flags & USBIO_PKTFLAG_ERR) + return -EREMOTEIO; + + if (ibuf_len < bpkt_len) + return -ENOSPC; + + memcpy(ibuf, bpkt->data, bpkt_len); + + return bpkt_len; +} +EXPORT_SYMBOL_NS_GPL(usbio_bulk_msg, "USBIO"); + +int usbio_acquire(struct auxiliary_device *adev) +{ + struct usbio_client *client = adev_to_client(adev); + struct usbio_device *usbio; + int ret; + + mutex_lock(&client->mutex); + + usbio = client->bridge; + if (!usbio) { + ret = -ENODEV; /* Disconnected */ + goto err_unlock; + } + + ret = usb_autopm_get_interface(usbio->intf); + if (ret) + goto err_unlock; + + mutex_lock(&usbio->bulk_mutex); + + /* Leave client locked until release to avoid abba deadlock issues */ + return 0; + +err_unlock: + mutex_unlock(&client->mutex); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(usbio_acquire, "USBIO"); + +void usbio_release(struct auxiliary_device *adev) +{ + struct usbio_client *client = adev_to_client(adev); + struct usbio_device *usbio = client->bridge; + + lockdep_assert_held(&client->mutex); + + mutex_unlock(&usbio->bulk_mutex); + usb_autopm_put_interface(usbio->intf); + mutex_unlock(&client->mutex); +} +EXPORT_SYMBOL_NS_GPL(usbio_release, "USBIO"); + +void usbio_get_txrxbuf_len(struct auxiliary_device *adev, u16 *txbuf_len, u16 *rxbuf_len) +{ + struct usbio_client *client = adev_to_client(adev); + struct usbio_device *usbio; + + guard(mutex)(&client->mutex); + + usbio = client->bridge; + if (!usbio) + return; /* Disconnected */ + + *txbuf_len = usbio->txbuf_len; + *rxbuf_len = usbio->rxbuf_len; +} +EXPORT_SYMBOL_NS_GPL(usbio_get_txrxbuf_len, "USBIO"); + +unsigned long usbio_get_quirks(struct auxiliary_device *adev) +{ + struct usbio_client *client = adev_to_client(adev); + struct usbio_device *usbio; + + guard(mutex)(&client->mutex); + + usbio = client->bridge; + if (!usbio) + return 0; /* Disconnected */ + + return usbio->quirks; +} +EXPORT_SYMBOL_NS_GPL(usbio_get_quirks, "USBIO"); + +static void usbio_auxdev_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + struct usbio_client *client = adev_to_client(adev); + + mutex_destroy(&client->mutex); + kfree(client); +} + +static int usbio_add_client(struct usbio_device *usbio, char *name, u8 id, void *data) +{ + struct usbio_client *client; + struct auxiliary_device *adev; + int ret; + + client = kzalloc_obj(*client); + if (!client) + return -ENOMEM; + + mutex_init(&client->mutex); + client->bridge = usbio; + adev = &client->auxdev; + adev->name = name; + adev->id = id; + + adev->dev.parent = usbio->dev; + adev->dev.platform_data = data; + adev->dev.release = usbio_auxdev_release; + + ret = auxiliary_device_init(adev); + if (ret) { + usbio_auxdev_release(&adev->dev); + return ret; + } + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + list_add_tail(&client->link, &usbio->cli_list); + + return 0; +} + +static int usbio_enum_gpios(struct usbio_device *usbio) +{ + struct usbio_gpio_bank_desc *gpio = usbio->gpios; + + dev_dbg(usbio->dev, "GPIO Banks: %d\n", usbio->nr_gpio_banks); + + for (unsigned int i = 0; i < usbio->nr_gpio_banks; i++) + dev_dbg(usbio->dev, "\tBank%d[%d] map: %#08x\n", + gpio[i].id, gpio[i].pins, gpio[i].bmap); + + usbio_add_client(usbio, USBIO_GPIO_CLIENT, 0, gpio); + + return 0; +} + +static int usbio_enum_i2cs(struct usbio_device *usbio) +{ + struct usbio_i2c_bus_desc *i2c = usbio->i2cs; + + dev_dbg(usbio->dev, "I2C Busses: %d\n", usbio->nr_i2c_buses); + + for (unsigned int i = 0; i < usbio->nr_i2c_buses; i++) { + dev_dbg(usbio->dev, "\tBus%d caps: %#02x\n", i2c[i].id, i2c[i].caps); + usbio_add_client(usbio, USBIO_I2C_CLIENT, i, &i2c[i]); + } + + return 0; +} + +static int usbio_suspend(struct usb_interface *intf, pm_message_t msg) +{ + struct usbio_device *usbio = usb_get_intfdata(intf); + + usb_kill_urb(usbio->urb); + + return 0; +} + +static int usbio_resume(struct usb_interface *intf) +{ + struct usbio_device *usbio = usb_get_intfdata(intf); + + return usb_submit_urb(usbio->urb, GFP_KERNEL); +} + +static void usbio_disconnect(struct usb_interface *intf) +{ + struct usbio_device *usbio = usb_get_intfdata(intf); + struct usbio_client *client; + + /* Wakeup any clients waiting for a reply */ + usbio->rxdat_len = 0; + complete(&usbio->done); + + /* Let clients know the bridge is gone */ + list_for_each_entry(client, &usbio->cli_list, link) { + mutex_lock(&client->mutex); + client->bridge = NULL; + mutex_unlock(&client->mutex); + } + + /* From here on clients will no longer touch struct usbio_device */ + usb_kill_urb(usbio->urb); + usb_free_urb(usbio->urb); + + list_for_each_entry_reverse(client, &usbio->cli_list, link) { + auxiliary_device_delete(&client->auxdev); + auxiliary_device_uninit(&client->auxdev); + } +} + +static int usbio_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *ep_in, *ep_out; + struct device *dev = &intf->dev; + struct usbio_protver protver; + struct usbio_device *usbio; + struct usbio_fwver fwver; + int ret; + + usbio = devm_kzalloc(dev, sizeof(*usbio), GFP_KERNEL); + if (!usbio) + return -ENOMEM; + + ret = devm_mutex_init(dev, &usbio->ctrl_mutex); + if (ret) + return ret; + + ret = devm_mutex_init(dev, &usbio->bulk_mutex); + if (ret) + return ret; + + usbio->dev = dev; + usbio->udev = udev; + usbio->intf = intf; + usbio->quirks = id ? id->driver_info : 0; + init_completion(&usbio->done); + INIT_LIST_HEAD(&usbio->cli_list); + usb_set_intfdata(intf, usbio); + + usbio->ctrl_pipe = usb_endpoint_num(&udev->ep0.desc); + usbio->ctrlbuf_len = usb_maxpacket(udev, usbio->ctrl_pipe); + usbio->ctrlbuf = devm_kzalloc(dev, usbio->ctrlbuf_len, GFP_KERNEL); + if (!usbio->ctrlbuf) + return -ENOMEM; + + /* Find the first bulk-in and bulk-out endpoints */ + ret = usb_find_common_endpoints(intf->cur_altsetting, &ep_in, &ep_out, + NULL, NULL); + if (ret) { + dev_err(dev, "Cannot find bulk endpoints: %d\n", ret); + return ret; + } + + usbio->tx_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(ep_out)); + + if (usbio->quirks & USBIO_QUIRK_BULK_MAXP_63) + usbio->txbuf_len = 63; + else + usbio->txbuf_len = usb_endpoint_maxp(ep_out); + + usbio->txbuf = devm_kzalloc(dev, usbio->txbuf_len, GFP_KERNEL); + if (!usbio->txbuf) + return -ENOMEM; + + usbio->rx_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(ep_in)); + + if (usbio->quirks & USBIO_QUIRK_BULK_MAXP_63) + usbio->rxbuf_len = 63; + else + usbio->rxbuf_len = usb_endpoint_maxp(ep_in); + + usbio->rxbuf = devm_kzalloc(dev, usbio->rxbuf_len, GFP_KERNEL); + if (!usbio->rxbuf) + return -ENOMEM; + + usbio->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!usbio->urb) + return -ENOMEM; + + usb_fill_bulk_urb(usbio->urb, udev, usbio->rx_pipe, usbio->rxbuf, + usbio->rxbuf_len, usbio_bulk_recv, usbio); + ret = usb_submit_urb(usbio->urb, GFP_KERNEL); + if (ret) { + dev_err_probe(dev, ret, "Submitting usb urb\n"); + goto err_free_urb; + } + + mutex_lock(&usbio->ctrl_mutex); + + ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_HS, NULL, 0, NULL, 0); + if (ret < 0) + goto err_unlock; + + ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_PROTVER, NULL, 0, + &protver, sizeof(protver)); + if (ret < 0) + goto err_unlock; + + ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_FWVER, NULL, 0, + &fwver, sizeof(fwver)); + if (ret < 0) + goto err_unlock; + + ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_ENUMGPIO, NULL, 0, + usbio->gpios, sizeof(usbio->gpios)); + if (ret < 0 || ret % sizeof(struct usbio_gpio_bank_desc)) { + ret = (ret < 0) ? ret : -EPROTO; + goto err_unlock; + } + usbio->nr_gpio_banks = ret / sizeof(struct usbio_gpio_bank_desc); + + ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_ENUMI2C, NULL, 0, + usbio->i2cs, sizeof(usbio->i2cs)); + if (ret < 0 || ret % sizeof(struct usbio_i2c_bus_desc)) { + ret = (ret < 0) ? ret : -EPROTO; + goto err_unlock; + } + usbio->nr_i2c_buses = ret / sizeof(struct usbio_i2c_bus_desc); + + mutex_unlock(&usbio->ctrl_mutex); + + dev_dbg(dev, "ProtVer(BCD): %02x FwVer: %d.%d.%d.%d\n", + protver.ver, fwver.major, fwver.minor, + le16_to_cpu(fwver.patch), le16_to_cpu(fwver.build)); + + usbio_enum_gpios(usbio); + usbio_enum_i2cs(usbio); + + return 0; + +err_unlock: + mutex_unlock(&usbio->ctrl_mutex); + usb_kill_urb(usbio->urb); +err_free_urb: + usb_free_urb(usbio->urb); + + return ret; +} + +static const struct usb_device_id usbio_table[] = { + { USB_DEVICE(0x2ac1, 0x20c1), /* Lattice NX40 */ + .driver_info = USBIO_QUIRK_I2C_MAX_RW_LEN_52 }, + { USB_DEVICE(0x2ac1, 0x20c9), /* Lattice NX33 */ + .driver_info = USBIO_QUIRK_I2C_NO_INIT_ACK | USBIO_QUIRK_I2C_MAX_RW_LEN_52 | + USBIO_QUIRK_I2C_ALLOW_400KHZ }, + { USB_DEVICE(0x2ac1, 0x20cb) }, /* Lattice NX33U */ + { USB_DEVICE(0x06cb, 0x0701), /* Synaptics Sabre */ + .driver_info = USBIO_QUIRK_BULK_MAXP_63 | USBIO_QUIRK_I2C_USE_CHUNK_LEN }, + { } +}; +MODULE_DEVICE_TABLE(usb, usbio_table); + +static struct usb_driver usbio_driver = { + .name = "usbio-bridge", + .probe = usbio_probe, + .disconnect = usbio_disconnect, + .suspend = usbio_suspend, + .resume = usbio_resume, + .id_table = usbio_table, + .supports_autosuspend = 1, +}; +module_usb_driver(usbio_driver); + +struct usbio_match_ids_walk_data { + struct acpi_device *adev; + const struct acpi_device_id *hids; + unsigned int id; +}; + +static int usbio_match_device_ids(struct acpi_device *adev, void *data) +{ + struct usbio_match_ids_walk_data *wd = data; + unsigned int id = 0; + char *uid; + + if (acpi_match_device_ids(adev, wd->hids)) + return 0; + + uid = acpi_device_uid(adev); + if (uid) { + for (int i = 0; i < strlen(uid); i++) { + if (!kstrtouint(&uid[i], 10, &id)) + break; + } + } + + if (!uid || wd->id == id) { + wd->adev = adev; + return 1; + } + + return 0; +} + +void usbio_acpi_bind(struct auxiliary_device *adev, const struct acpi_device_id *hids) +{ + struct device *dev = &adev->dev; + struct acpi_device *parent; + struct usbio_match_ids_walk_data wd = { + .adev = NULL, + .hids = hids, + .id = adev->id, + }; + + parent = ACPI_COMPANION(dev->parent); + if (!parent) + return; + + acpi_dev_for_each_child(parent, usbio_match_device_ids, &wd); + if (wd.adev) + ACPI_COMPANION_SET(dev, wd.adev); +} +EXPORT_SYMBOL_NS_GPL(usbio_acpi_bind, "USBIO"); + +MODULE_DESCRIPTION("Intel USBIO Bridge driver"); +MODULE_AUTHOR("Israel Cepeda <israel.a.cepeda.lopez@intel.com>"); +MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index bb546f624a45..f794695dea9d 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -323,7 +323,7 @@ static int lcd_probe(struct usb_interface *interface, int retval; /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) return -ENOMEM; diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index 546deff754ba..89d25fcef642 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -308,11 +308,11 @@ static int sevseg_probe(struct usb_interface *interface, struct usb_sevsegdev *mydev; int rc = -ENOMEM; - mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL); + mydev = kzalloc_obj(struct usb_sevsegdev); if (!mydev) goto error_mem; - mydev->udev = usb_get_dev(udev); + mydev->udev = udev; mydev->intf = interface; usb_set_intfdata(interface, mydev); @@ -338,7 +338,6 @@ static void sevseg_disconnect(struct usb_interface *interface) mydev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); - usb_put_dev(mydev->udev); kfree(mydev); dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); } diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 8d379ae835bc..98071b25ac07 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -545,7 +545,7 @@ alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe) if (max == 0) return NULL; - sg = kmalloc_array(nents, sizeof(*sg), GFP_KERNEL); + sg = kmalloc_objs(*sg, nents); if (!sg) return NULL; sg_init_table(sg, nents); @@ -592,7 +592,7 @@ struct sg_timeout { static void sg_timeout(struct timer_list *t) { - struct sg_timeout *timeout = from_timer(timeout, t, timer); + struct sg_timeout *timeout = timer_container_of(timeout, t, timer); usb_sg_cancel(timeout->req); } @@ -626,11 +626,11 @@ 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; - destroy_timer_on_stack(&timeout.timer); + timer_destroy_on_stack(&timeout.timer); /* FIXME check resulting data pattern */ @@ -1221,7 +1221,7 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param) * as with bulk/intr sglists, sglen is the queue depth; it also * controls which subtests run (more tests than sglen) or rerun. */ - urb = kcalloc(param->sglen, sizeof(struct urb *), GFP_KERNEL); + urb = kzalloc_objs(struct urb *, param->sglen); if (!urb) return -ENOMEM; for (i = 0; i < param->sglen; i++) { @@ -1370,7 +1370,7 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param) if (!u) goto cleanup; - reqp = kmalloc(sizeof(*reqp), GFP_KERNEL); + reqp = kmalloc_obj(*reqp); if (!reqp) goto cleanup; reqp->setup = req; @@ -1572,7 +1572,7 @@ static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num, memset(buf, 0, size); /* Allocate and init the urbs we'll queue */ - ctx.urbs = kcalloc(num, sizeof(struct urb *), GFP_KERNEL); + ctx.urbs = kzalloc_objs(struct urb *, num); if (!ctx.urbs) goto free_buf; for (i = 0; i < num; i++) { @@ -2052,7 +2052,7 @@ test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param, if (param->sglen > MAX_SGLEN) return -EINVAL; - urbs = kcalloc(param->sglen, sizeof(*urbs), GFP_KERNEL); + urbs = kzalloc_objs(*urbs, param->sglen); if (!urbs) return -ENOMEM; @@ -2786,7 +2786,7 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) } #endif - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) return -ENOMEM; info = (struct usbtest_info *) id->driver_info; diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index b26c1d382d59..b7d3c44b970e 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -134,7 +134,7 @@ static struct uss720_async_request *submit_async_request(struct parport_uss720_p usbdev = priv->usbdev; if (!usbdev) return NULL; - rq = kzalloc(sizeof(struct uss720_async_request), mem_flags); + rq = kzalloc_obj(struct uss720_async_request, mem_flags); if (!rq) return NULL; kref_init(&rq->ref_count); @@ -147,7 +147,7 @@ static struct uss720_async_request *submit_async_request(struct parport_uss720_p kref_put(&rq->ref_count, destroy_async); return NULL; } - rq->dr = kmalloc(sizeof(*rq->dr), mem_flags); + rq->dr = kmalloc_obj(*rq->dr, mem_flags); if (!rq->dr) { kref_put(&rq->ref_count, destroy_async); return NULL; @@ -677,35 +677,32 @@ static int uss720_probe(struct usb_interface *intf, struct parport_uss720_private *priv; struct parport *pp; unsigned char reg; - int ret; + int ret = -ENODEV; dev_dbg(&intf->dev, "probe: vendor id 0x%x, device id 0x%x\n", le16_to_cpu(usbdev->descriptor.idVendor), le16_to_cpu(usbdev->descriptor.idProduct)); /* our known interfaces have 3 alternate settings */ - if (intf->num_altsetting != 3) { - usb_put_dev(usbdev); - return -ENODEV; - } + if (intf->num_altsetting != 3) + goto bail_out_early; + ret = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2); dev_dbg(&intf->dev, "set interface result %d\n", ret); interface = intf->cur_altsetting; - if (interface->desc.bNumEndpoints < 2) { - usb_put_dev(usbdev); - return -ENODEV; - } + if (interface->desc.bNumEndpoints < 2) + goto bail_out_early; /* * Allocate parport interface */ - priv = kzalloc(sizeof(struct parport_uss720_private), GFP_KERNEL); - if (!priv) { - usb_put_dev(usbdev); - return -ENOMEM; - } + ret = -ENOMEM; + priv = kzalloc_obj(struct parport_uss720_private); + if (!priv) + goto bail_out_early; + priv->pp = NULL; priv->usbdev = usbdev; kref_init(&priv->ref_count); @@ -736,7 +733,7 @@ static int uss720_probe(struct usb_interface *intf, ret = get_1284_register(pp, 0, ®, GFP_KERNEL); dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg); if (ret < 0) - return ret; + goto probe_abort; ret = usb_find_last_int_in_endpoint(interface, &epd); if (!ret) { @@ -752,6 +749,10 @@ probe_abort: kill_all_async_requests_priv(priv); kref_put(&priv->ref_count, destroy_priv); return -ENODEV; + +bail_out_early: + usb_put_dev(usbdev); + return ret; } static void uss720_disconnect(struct usb_interface *intf) diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 70dff0db5354..7a482cdee1e9 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -197,7 +197,7 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_ int res; /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) goto error; kref_init(&dev->kref); @@ -272,6 +272,7 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_ dev->int_buffer, YUREX_BUF_SIZE, yurex_interrupt, dev, 1); dev->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + dev->bbu = -1; if (usb_submit_urb(dev->urb, GFP_KERNEL)) { retval = -EIO; dev_err(&interface->dev, "Could not submitting URB\n"); @@ -280,7 +281,6 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_ /* save our data pointer in this interface device */ usb_set_intfdata(interface, dev); - dev->bbu = -1; /* we can register the device now, as it is ready */ retval = usb_register_dev(interface, &yurex_class); diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index c93b43f5bc46..687f6a8981f3 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -68,18 +68,20 @@ * The magic limit was calculated so that it allows the monitoring * application to pick data once in two ticks. This way, another application, * which presumably drives the bus, gets to hog CPU, yet we collect our data. - * If HZ is 100, a 480 mbit/s bus drives 614 KB every jiffy. USB has an - * enormous overhead built into the bus protocol, so we need about 1000 KB. + * + * Originally, for a 480 Mbit/s bus this required a buffer of about 1 MB. For + * modern 20 Gbps buses, this value increases to over 50 MB. The maximum + * buffer size is set to 64 MiB to accommodate this. * * This is still too much for most cases, where we just snoop a few * descriptor fetches for enumeration. So, the default is a "reasonable" - * amount for systems with HZ=250 and incomplete bus saturation. + * amount for typical, low-throughput use cases. * * XXX What about multi-megabyte URBs which take minutes to transfer? */ -#define BUFF_MAX CHUNK_ALIGN(1200*1024) -#define BUFF_DFL CHUNK_ALIGN(300*1024) -#define BUFF_MIN CHUNK_ALIGN(8*1024) +#define BUFF_MAX CHUNK_ALIGN(64*1024*1024) +#define BUFF_DFL CHUNK_ALIGN(300*1024) +#define BUFF_MIN CHUNK_ALIGN(8*1024) /* * The per-event API header (2 per URB). @@ -692,7 +694,7 @@ static int mon_bin_open(struct inode *inode, struct file *file) return -ENODEV; } - rp = kzalloc(sizeof(struct mon_reader_bin), GFP_KERNEL); + rp = kzalloc_obj(struct mon_reader_bin); if (rp == NULL) { rc = -ENOMEM; goto err_alloc; @@ -1027,8 +1029,7 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg return -EINVAL; size = CHUNK_ALIGN(arg); - vec = kcalloc(size / CHUNK_SIZE, sizeof(struct mon_pgmap), - GFP_KERNEL); + vec = kzalloc_objs(struct mon_pgmap, size / CHUNK_SIZE); if (vec == NULL) { ret = -ENOMEM; break; diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index af852d53aac6..e4fff194fea0 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -273,7 +273,7 @@ static void mon_bus_init(struct usb_bus *ubus) { struct mon_bus *mbus; - mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL); + mbus = kzalloc_obj(struct mon_bus); if (mbus == NULL) goto err_alloc; kref_init(&mbus->ref); diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c index 398e02af6a2b..3ee2e483da57 100644 --- a/drivers/usb/mon/mon_stat.c +++ b/drivers/usb/mon/mon_stat.c @@ -29,7 +29,7 @@ static int mon_stat_open(struct inode *inode, struct file *file) struct mon_bus *mbus; struct snap *sp; - sp = kmalloc(sizeof(struct snap), GFP_KERNEL); + sp = kmalloc_obj(struct snap); if (sp == NULL) return -ENOMEM; diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 68b9b2b41189..4c8e2518e407 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -330,7 +330,7 @@ static int mon_text_open(struct inode *inode, struct file *file) mutex_lock(&mon_lock); mbus = inode->i_private; - rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL); + rp = kzalloc_obj(struct mon_reader_text); if (rp == NULL) { rc = -ENOMEM; goto err_alloc; diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index c11840b9a6f1..ba5a63669e5f 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -65,7 +65,7 @@ struct mtu3_request; #define MTU3_U3_IP_SLOT_DEFAULT 2 #define MTU3_U2_IP_SLOT_DEFAULT 1 -/** +/* * IP TRUNK version * from 0x1003 version, USB3 Gen2 is supported, two changes affect driver: * 1. MAXPKT and MULTI bits layout of TXCSR1 and RXCSR1 are adjusted, @@ -74,9 +74,9 @@ struct mtu3_request; */ #define MTU3_TRUNK_VERS_1003 0x1003 -/** +/* * Normally the device works on HS or SS, to simplify fifo management, - * devide fifo into some 512B parts, use bitmap to manage it; And + * divide fifo into some 512B parts, use bitmap to manage it; And * 128 bits size of bitmap is large enough, that means it can manage * up to 64KB fifo size. * NOTE: MTU3_EP_FIFO_UNIT should be power of two @@ -85,7 +85,7 @@ struct mtu3_request; #define MTU3_FIFO_BIT_SIZE 128 #define MTU3_U2_IP_EP0_FIFO_SIZE 64 -/** +/* * Maximum size of ep0 response buffer for ch9 requests, * the SET_SEL request uses 6 so far, and GET_STATUS is 2 */ @@ -103,6 +103,7 @@ enum mtu3_speed { }; /** + * enum mtu3_g_ep0_state - endpoint 0 states * @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP * without data stage. * @MU3D_EP0_STATE_TX: IN data stage @@ -121,11 +122,12 @@ enum mtu3_g_ep0_state { }; /** - * MTU3_DR_FORCE_NONE: automatically switch host and periperal mode + * enum mtu3_dr_force_mode - indicates host/OTG operating mode + * @MTU3_DR_FORCE_NONE: automatically switch host and peripheral mode * by IDPIN signal. - * MTU3_DR_FORCE_HOST: force to enter host mode and override OTG + * @MTU3_DR_FORCE_HOST: force to enter host mode and override OTG * IDPIN signal. - * MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. + * @MTU3_DR_FORCE_DEVICE: force to enter peripheral mode. */ enum mtu3_dr_force_mode { MTU3_DR_FORCE_NONE = 0, @@ -134,6 +136,7 @@ enum mtu3_dr_force_mode { }; /** + * struct mtu3_fifo_info - HW FIFO description and management data * @base: the base address of fifo * @limit: the bitmap size in bits * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT @@ -145,7 +148,7 @@ struct mtu3_fifo_info { }; /** - * General Purpose Descriptor (GPD): + * struct qmu_gpd - General Purpose Descriptor (GPD): * The format of TX GPD is a little different from RX one. * And the size of GPD is 16 bytes. * @@ -179,11 +182,13 @@ struct qmu_gpd { } __packed; /** -* dma: physical base address of GPD segment -* start: virtual base address of GPD segment -* end: the last GPD element -* enqueue: the first empty GPD to use -* dequeue: the first completed GPD serviced by ISR +* struct mtu3_gpd_ring - GPD ring descriptor +* @dma: physical base address of GPD segment +* @start: virtual base address of GPD segment +* @end: the last GPD element +* @enqueue: the first empty GPD to use +* @dequeue: the first completed GPD serviced by ISR +* * NOTE: the size of GPD ring should be >= 2 */ struct mtu3_gpd_ring { @@ -195,6 +200,7 @@ struct mtu3_gpd_ring { }; /** +* struct otg_switch_mtk - OTG/dual-role switch management * @vbus: vbus 5V used by host mode * @edev: external connector used to detect vbus and iddig changes * @id_nb : notifier for iddig(idpin) detection @@ -222,6 +228,7 @@ struct otg_switch_mtk { }; /** + * struct ssusb_mtk - SuperSpeed USB descriptor (MTK) * @mac_base: register base address of device MAC, exclude xHCI's * @ippc_base: register base address of IP Power and Clock interface (IPPC) * @vusb33: usb3.3V shared by device/host IP @@ -268,6 +275,7 @@ struct ssusb_mtk { }; /** + * struct mtu3_ep - common mtu3 endpoint description * @fifo_size: it is (@slot + 1) * @fifo_seg_size * @fifo_seg_size: it is roundup_pow_of_two(@maxp) */ diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index a3a6282893d0..66dbfe1705d5 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -290,7 +290,7 @@ static void mtu3_csr_init(struct mtu3 *mtu) /* delay about 0.1us from detecting reset to send chirp-K */ mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK); - /* enable automatical HWRW from L1 */ + /* enable automatic HWRW from L1 */ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE); } @@ -613,7 +613,7 @@ static int mtu3_mem_alloc(struct mtu3 *mtu) /* one for ep0, another is reserved */ mtu->num_eps = min(in_ep_num, out_ep_num) + 1; - ep_array = kcalloc(mtu->num_eps * 2, sizeof(*ep_array), GFP_KERNEL); + ep_array = kzalloc_objs(*ep_array, mtu->num_eps * 2); if (ep_array == NULL) return -ENOMEM; diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index bf73fbc29976..da29f467943f 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -235,7 +235,7 @@ struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) struct mtu3_ep *mep = to_mtu3_ep(ep); struct mtu3_request *mreq; - mreq = kzalloc(sizeof(*mreq), gfp_flags); + mreq = kzalloc_obj(*mreq, gfp_flags); if (!mreq) return NULL; diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 7b5a431acb56..cc8a864dbd63 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -431,7 +431,6 @@ static int mtu3_probe(struct platform_device *pdev) } device_enable_async_suspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); pm_runtime_forbid(dev); diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index 3d77408e3133..03f26589b056 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -221,7 +221,7 @@ static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring) return ring->dequeue; } -/* check if a ring is emtpy */ +/* check if a ring is empty */ static bool gpd_ring_empty(struct mtu3_gpd_ring *ring) { struct qmu_gpd *enq = ring->enqueue; diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 9e45d12b81d3..f56929267eaa 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -84,7 +84,8 @@ config USB_MUSB_TUSB6010 config USB_MUSB_OMAP2PLUS tristate "OMAP2430 and onwards" - depends on ARCH_OMAP2PLUS && USB + depends on ARCH_OMAP2PLUS || COMPILE_TEST + depends on USB depends on OMAP_CONTROL_PHY || !OMAP_CONTROL_PHY select GENERIC_PHY diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 26fd71a5f9b2..4209f438ba18 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -123,7 +123,8 @@ static void da8xx_musb_set_vbus(struct musb *musb, int is_on) static void otg_timer(struct timer_list *t) { - struct musb *musb = from_timer(musb, t, dev_timer); + struct musb *musb = timer_container_of(musb, t, + dev_timer); void __iomem *mregs = musb->mregs; u8 devctl; unsigned long flags; @@ -204,7 +205,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; } @@ -290,7 +291,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 @@ -419,7 +420,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); diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index acdeb1117cd3..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, }, diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c index aa988d74b58d..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, }, diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c index 7edc8429b274..587127abd30a 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, }, @@ -91,7 +91,8 @@ static void mpfs_musb_set_vbus(struct musb *musb, int is_on) static void otg_timer(struct timer_list *t) { - struct musb *musb = from_timer(musb, t, dev_timer); + struct musb *musb = timer_container_of(musb, t, + dev_timer); void __iomem *mregs = musb->mregs; u8 devctl; unsigned long flags; @@ -165,7 +166,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 +233,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; } diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 7f349f5e781d..73ac25f53698 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -596,7 +596,7 @@ void musb_load_testpacket(struct musb *musb) */ static void musb_otg_timer_func(struct timer_list *t) { - struct musb *musb = from_timer(musb, t, otg_timer); + struct musb *musb = timer_container_of(musb, t, otg_timer); unsigned long flags; spin_lock_irqsave(&musb->lock, flags); @@ -921,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) @@ -1015,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: @@ -1271,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, }, @@ -1280,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, }, @@ -1289,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, }, @@ -1299,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, }, @@ -1309,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, }, @@ -1340,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, }, @@ -1447,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, }; @@ -1600,7 +1600,7 @@ static int musb_core_init(u16 musb_type, struct musb *musb) /* log core options (read using indexed model) */ reg = musb_read_configdata(mbase); - strcpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); + strscpy(aInfo, (reg & MUSB_CONFIGDATA_UTMIDW) ? "UTMI-16" : "UTMI-8"); if (reg & MUSB_CONFIGDATA_DYNFIFO) { strcat(aInfo, ", dyn FIFOs"); musb->dyn_fifo = true; @@ -2031,7 +2031,6 @@ static void musb_pm_runtime_check_session(struct musb *musb) if (!musb->session) break; trace_musb_state(musb, devctl, "Allow PM on possible host mode disconnect"); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); musb->session = false; return; @@ -2063,7 +2062,6 @@ static void musb_pm_runtime_check_session(struct musb *musb) msecs_to_jiffies(3000)); } else { trace_musb_state(musb, devctl, "Allow PM with no session"); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); } @@ -2090,7 +2088,6 @@ static void musb_irq_work(struct work_struct *data) sysfs_notify(&musb->controller->kobj, NULL, "mode"); } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); } @@ -2564,7 +2561,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb_init_debugfs(musb); musb->is_initialized = 1; - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; @@ -2887,7 +2883,6 @@ static int musb_resume(struct device *dev) error); spin_unlock_irqrestore(&musb->lock, flags); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 9589243e8951..28e958fcc862 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -756,12 +756,12 @@ cppi41_dma_controller_create(struct musb *musb, void __iomem *base) return NULL; } - controller = kzalloc(sizeof(*controller), GFP_KERNEL); + controller = kzalloc_obj(*controller); 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_debugfs.c b/drivers/usb/musb/musb_debugfs.c index 2d623284edf6..5092d62c2062 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -106,7 +106,6 @@ static int musb_regdump_show(struct seq_file *s, void *unused) } } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; } @@ -119,7 +118,6 @@ static int musb_test_mode_show(struct seq_file *s, void *unused) pm_runtime_get_sync(musb->controller); test = musb_readb(musb->mregs, MUSB_TESTMODE); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); if (test == (MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS)) @@ -216,7 +214,6 @@ static ssize_t musb_test_mode_write(struct file *file, musb_writeb(musb->mregs, MUSB_TESTMODE, test); ret: - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return count; } @@ -243,7 +240,6 @@ static int musb_softconnect_show(struct seq_file *s, void *unused) reg = musb_readb(musb->mregs, MUSB_DEVCTL); connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); break; default: @@ -304,7 +300,6 @@ static ssize_t musb_softconnect_write(struct file *file, } } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return count; } diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index f877faf5a930..e3935f18dd56 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -201,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 */ @@ -215,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 @@ -278,7 +278,7 @@ static int dsps_check_status(struct musb *musb, void *unused) static void otg_timer(struct timer_list *t) { - struct musb *musb = from_timer(musb, t, dev_timer); + struct musb *musb = timer_container_of(musb, t, dev_timer); struct device *dev = musb->controller; unsigned long flags; int err; @@ -296,7 +296,6 @@ static void otg_timer(struct timer_list *t) if (err < 0) dev_err(dev, "%s resume work: %i\n", __func__, err); spin_unlock_irqrestore(&musb->lock, flags); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } @@ -499,7 +498,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); @@ -839,7 +838,7 @@ static int dsps_setup_optional_vbus_irq(struct platform_device *pdev, { int error; - glue->vbus_irq = platform_get_irq_byname(pdev, "vbus"); + glue->vbus_irq = platform_get_irq_byname_optional(pdev, "vbus"); if (glue->vbus_irq == -EPROBE_DEFER) return -EPROBE_DEFER; @@ -983,7 +982,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); diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 6869c58367f2..016d3f3fc1e0 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1133,7 +1133,7 @@ struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) struct musb_ep *musb_ep = to_musb_ep(ep); struct musb_request *request; - request = kzalloc(sizeof *request, gfp_flags); + request = kzalloc_obj(*request, gfp_flags); if (!request) return NULL; @@ -1258,7 +1258,6 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, unlock: spin_unlock_irqrestore(&musb->lock, lockflags); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return status; @@ -1642,7 +1641,6 @@ static void musb_gadget_work(struct work_struct *work) spin_lock_irqsave(&musb->lock, flags); musb_pullup(musb, musb->softconnect); spin_unlock_irqrestore(&musb->lock, flags); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); } @@ -1862,7 +1860,6 @@ static int musb_gadget_start(struct usb_gadget *g, if (musb->xceiv && musb->xceiv->last_event == USB_EVENT_ID) musb_platform_set_vbus(musb, 1); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; @@ -1913,9 +1910,9 @@ static int musb_gadget_stop(struct usb_gadget *g) * gadget driver here and have everything work; * that currently misbehaves. */ + usb_gadget_set_state(g, USB_STATE_NOTATTACHED); /* Force check of devctl register for PM runtime */ - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); return 0; @@ -2019,6 +2016,7 @@ void musb_g_disconnect(struct musb *musb) case OTG_STATE_B_PERIPHERAL: case OTG_STATE_B_IDLE: musb_set_state(musb, OTG_STATE_B_IDLE); + usb_gadget_set_state(&musb->g, USB_STATE_NOTATTACHED); break; case OTG_STATE_B_SRP_INIT: break; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 6b4481a867c5..8efd2fa472f9 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2154,7 +2154,7 @@ static int musb_urb_enqueue( * REVISIT consider a dedicated qh kmem_cache, so it's harder * for bugs in other kernel code to break this driver... */ - qh = kzalloc(sizeof *qh, mem_flags); + qh = kzalloc_obj(*qh, mem_flags); if (!qh) { spin_lock_irqsave(&musb->lock, flags); usb_hcd_unlink_urb_from_ep(hcd, urb); diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 7acd1635850d..8c6c643fcb2c 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -395,7 +395,7 @@ dma_controller_alloc(struct musb *musb, void __iomem *base) { struct musb_dma_controller *controller; - controller = kzalloc(sizeof(*controller), GFP_KERNEL); + controller = kzalloc_obj(*controller); if (!controller) return NULL; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 2970967a4fd2..48bb9bfb2204 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -151,7 +151,6 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue) default: dev_dbg(musb->controller, "ID float\n"); } - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); atomic_notifier_call_chain(&musb->xceiv->notifier, musb->xceiv->last_event, NULL); @@ -318,13 +317,11 @@ static int omap2430_probe(struct platform_device *pdev) glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL); if (!glue) - goto err0; + return -ENOMEM; musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); - if (!musb) { - dev_err(&pdev->dev, "failed to allocate musb device\n"); - goto err0; - } + if (!musb) + return -ENOMEM; musb->dev.parent = &pdev->dev; musb->dev.dma_mask = &omap2430_dmamask; @@ -349,15 +346,15 @@ static int omap2430_probe(struct platform_device *pdev) pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) - goto err2; + goto err_put_musb; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) - goto err2; + goto err_put_musb; config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); if (!config) - goto err2; + goto err_put_musb; of_property_read_u32(np, "mode", (u32 *)&pdata->mode); of_property_read_u32(np, "interface-type", @@ -380,7 +377,7 @@ static int omap2430_probe(struct platform_device *pdev) if (!control_pdev) { dev_err(&pdev->dev, "Failed to get control device\n"); ret = -EINVAL; - goto err2; + goto err_put_musb; } glue->control_otghs = &control_pdev->dev; } @@ -400,7 +397,7 @@ static int omap2430_probe(struct platform_device *pdev) ret = platform_device_add_resources(musb, pdev->resource, pdev->num_resources); if (ret) { dev_err(&pdev->dev, "failed to add resources\n"); - goto err2; + goto err_put_control_otghs; } if (populate_irqs) { @@ -413,7 +410,7 @@ static int omap2430_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -EINVAL; - goto err2; + goto err_put_control_otghs; } musb_res[i].start = res->start; @@ -441,14 +438,14 @@ static int omap2430_probe(struct platform_device *pdev) ret = platform_device_add_resources(musb, musb_res, i); if (ret) { dev_err(&pdev->dev, "failed to add IRQ resources\n"); - goto err2; + goto err_put_control_otghs; } } ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); if (ret) { dev_err(&pdev->dev, "failed to add platform_data\n"); - goto err2; + goto err_put_control_otghs; } pm_runtime_enable(glue->dev); @@ -456,18 +453,19 @@ static int omap2430_probe(struct platform_device *pdev) ret = platform_device_add(musb); if (ret) { dev_err(&pdev->dev, "failed to register musb device\n"); - goto err3; + goto err_disable_rpm; } return 0; -err3: +err_disable_rpm: pm_runtime_disable(glue->dev); - -err2: +err_put_control_otghs: + if (!IS_ERR(glue->control_otghs)) + put_device(glue->control_otghs); +err_put_musb: platform_device_put(musb); -err0: return ret; } @@ -477,6 +475,8 @@ static void omap2430_remove(struct platform_device *pdev) platform_device_unregister(glue->musb); pm_runtime_disable(glue->dev); + if (!IS_ERR(glue->control_otghs)) + put_device(glue->control_otghs); } #ifdef CONFIG_PM diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index eac1cde86be3..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), diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 90b760a95e4e..1d9571f24a56 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -457,7 +457,7 @@ static int tusb_musb_vbus_status(struct musb *musb) static void musb_do_idle(struct timer_list *t) { - struct musb *musb = from_timer(musb, t, dev_timer); + struct musb *musb = timer_container_of(musb, t, dev_timer); unsigned long flags; spin_lock_irqsave(&musb->lock, flags); @@ -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); diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 60a93b8bbe3c..36ee61a52e7e 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -596,7 +596,7 @@ tusb_dma_controller_create(struct musb *musb, void __iomem *base) | TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f) | TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2)); - tusb_dma = kzalloc(sizeof(struct tusb_omap_dma), GFP_KERNEL); + tusb_dma = kzalloc_obj(struct tusb_omap_dma); if (!tusb_dma) goto out; @@ -615,13 +615,13 @@ tusb_dma_controller_create(struct musb *musb, void __iomem *base) struct dma_channel *ch; struct tusb_omap_dma_ch *chdat; - ch = kzalloc(sizeof(struct dma_channel), GFP_KERNEL); + ch = kzalloc_obj(struct dma_channel); if (!ch) goto cleanup; dma_channel_pool[i] = ch; - chdat = kzalloc(sizeof(struct tusb_omap_dma_ch), GFP_KERNEL); + chdat = kzalloc_obj(struct tusb_omap_dma_ch); if (!chdat) goto cleanup; diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index d5cf5e8bb1ca..bbb4978c0cd5 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -362,7 +362,7 @@ ux500_dma_controller_create(struct musb *musb, void __iomem *base) struct resource *iomem; int ret; - controller = kzalloc(sizeof(*controller), GFP_KERNEL); + controller = kzalloc_obj(*controller); if (!controller) goto kzalloc_fail; diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 5f629d7cad64..b7acf3966cd7 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -126,18 +126,6 @@ config USB_ISP1301 To compile this driver as a module, choose M here: the module will be called phy-isp1301. -config USB_MV_OTG - tristate "Marvell USB OTG support" - depends on USB_EHCI_MV && USB_MV_UDC && PM && USB_OTG - depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' - select USB_PHY - help - Say Y here if you want to build Marvell USB OTG transceiver - driver in kernel (including PXA and MMP series). This driver - implements role switch between EHCI host driver and gadget driver. - - To compile this driver as a module, choose M here. - config USB_MXS_PHY tristate "Freescale MXS USB PHY support" depends on ARCH_MXC || ARCH_MXS diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index e5d619b4d8f6..ceae46c5341d 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -18,7 +18,6 @@ obj-$(CONFIG_TWL6030_USB) += phy-twl6030-usb.o obj-$(CONFIG_USB_TEGRA_PHY) += phy-tegra-usb.o obj-$(CONFIG_USB_GPIO_VBUS) += phy-gpio-vbus-usb.o obj-$(CONFIG_USB_ISP1301) += phy-isp1301.o -obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o obj-$(CONFIG_USB_ULPI) += phy-ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index 40ac68e52cee..35d79f11b03d 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -779,11 +779,11 @@ static int fsl_otg_conf(struct platform_device *pdev) return 0; /* allocate space to fsl otg device */ - fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL); + fsl_otg_tc = kzalloc_obj(struct fsl_otg); if (!fsl_otg_tc) return -ENOMEM; - fsl_otg_tc->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL); + fsl_otg_tc->phy.otg = kzalloc_obj(struct usb_otg); if (!fsl_otg_tc->phy.otg) { kfree(fsl_otg_tc); return -ENOMEM; @@ -988,6 +988,7 @@ static void fsl_otg_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); + disable_delayed_work_sync(&fsl_otg_dev->otg_event); usb_remove_phy(&fsl_otg_dev->phy); free_irq(fsl_otg_dev->irq, fsl_otg_dev); diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index d70341ae5a92..95bfe7f1b83a 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -345,7 +345,7 @@ inline struct fsl_otg_timer *otg_timer_initializer { struct fsl_otg_timer *timer; - timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL); + timer = kmalloc_obj(struct fsl_otg_timer); if (!timer) return NULL; timer->function = function; diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 8423be59ec0f..de26b302334d 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -20,7 +20,7 @@ #include <linux/slab.h> #include <linux/clk.h> #include <linux/regulator/consumer.h> -#include <linux/of.h> +#include <linux/property.h> #include <linux/gpio/consumer.h> #include <linux/delay.h> @@ -49,15 +49,13 @@ static int nop_set_suspend(struct usb_phy *x, int suspend) int ret = 0; if (suspend) { - if (!IS_ERR(nop->clk)) - clk_disable_unprepare(nop->clk); + clk_disable_unprepare(nop->clk); if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev)) ret = regulator_disable(nop->vcc); } else { if (!IS_ERR(nop->vcc) && !device_may_wakeup(x->dev)) ret = regulator_enable(nop->vcc); - if (!IS_ERR(nop->clk)) - clk_prepare_enable(nop->clk); + clk_prepare_enable(nop->clk); } return ret; @@ -137,11 +135,9 @@ int usb_gen_phy_init(struct usb_phy *phy) dev_err(phy->dev, "Failed to enable power\n"); } - if (!IS_ERR(nop->clk)) { - ret = clk_prepare_enable(nop->clk); - if (ret) - return ret; - } + ret = clk_prepare_enable(nop->clk); + if (ret) + return ret; nop_reset(nop); @@ -155,8 +151,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy) gpiod_set_value_cansleep(nop->gpiod_reset, 1); - if (!IS_ERR(nop->clk)) - clk_disable_unprepare(nop->clk); + clk_disable_unprepare(nop->clk); if (!IS_ERR(nop->vcc)) { if (regulator_disable(nop->vcc)) @@ -202,18 +197,9 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop) { enum usb_phy_type type = USB_PHY_TYPE_USB2; int err = 0; - u32 clk_rate = 0; - bool needs_clk = false; - - if (dev->of_node) { - struct device_node *node = dev->of_node; - - if (of_property_read_u32(node, "clock-frequency", &clk_rate)) - clk_rate = 0; - needs_clk = of_property_present(node, "clocks"); - } + device_property_read_u32(dev, "clock-frequency", &clk_rate); nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS); err = PTR_ERR_OR_ZERO(nop->gpiod_reset); @@ -235,20 +221,16 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop) if (!nop->phy.otg) return -ENOMEM; - nop->clk = devm_clk_get(dev, "main_clk"); - if (IS_ERR(nop->clk)) { - dev_dbg(dev, "Can't get phy clock: %ld\n", - PTR_ERR(nop->clk)); - if (needs_clk) - return PTR_ERR(nop->clk); - } + nop->clk = devm_clk_get_optional(dev, "main_clk"); + if (IS_ERR(nop->clk)) + return dev_err_probe(dev, PTR_ERR(nop->clk), + "Can't get phy clock\n"); - if (!IS_ERR(nop->clk) && clk_rate) { + if (clk_rate) { err = clk_set_rate(nop->clk, clk_rate); - if (err) { - dev_err(dev, "Error setting clock rate\n"); - return err; - } + if (err) + return dev_err_probe(dev, err, + "Error setting clock rate\n"); } nop->vcc = devm_regulator_get_optional(dev, "vcc"); @@ -282,7 +264,6 @@ EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); static int usb_phy_generic_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *dn = dev->of_node; struct usb_phy_generic *nop; int err; @@ -293,17 +274,17 @@ static int usb_phy_generic_probe(struct platform_device *pdev) err = usb_phy_gen_create_phy(dev, nop); if (err) return err; + if (nop->gpiod_vbus) { - err = devm_request_threaded_irq(&pdev->dev, + err = devm_request_threaded_irq(dev, gpiod_to_irq(nop->gpiod_vbus), NULL, nop_gpio_vbus_thread, VBUS_IRQ_FLAGS, "vbus_detect", nop); - if (err) { - dev_err(&pdev->dev, "can't request irq %i, err: %d\n", - gpiod_to_irq(nop->gpiod_vbus), err); - return err; - } + if (err) + return dev_err_probe(dev, err, "can't request irq %i\n", + gpiod_to_irq(nop->gpiod_vbus)); + nop->phy.otg->state = gpiod_get_value(nop->gpiod_vbus) ? OTG_STATE_B_PERIPHERAL : OTG_STATE_B_IDLE; } @@ -312,16 +293,13 @@ static int usb_phy_generic_probe(struct platform_device *pdev) nop->phy.shutdown = usb_gen_phy_shutdown; err = usb_add_phy_dev(&nop->phy); - if (err) { - dev_err(&pdev->dev, "can't register transceiver, err: %d\n", - err); - return err; - } + if (err) + return dev_err_probe(dev, err, "can't register transceiver\n"); platform_set_drvdata(pdev, nop); - device_set_wakeup_capable(&pdev->dev, - of_property_read_bool(dn, "wakeup-source")); + device_set_wakeup_capable(dev, + device_property_read_bool(dev, "wakeup-source")); return 0; } diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index f9b5c411aee4..2940f0c84e1b 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -149,7 +149,12 @@ struct i2c_client *isp1301_get_client(struct device_node *node) return client; /* non-DT: only one ISP1301 chip supported */ - return isp1301_i2c_client; + if (isp1301_i2c_client) { + get_device(&isp1301_i2c_client->dev); + return isp1301_i2c_client; + } + + return NULL; } EXPORT_SYMBOL_GPL(isp1301_get_client); diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c deleted file mode 100644 index 30d6c8840a5e..000000000000 --- a/drivers/usb/phy/phy-mv-usb.c +++ /dev/null @@ -1,881 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2011 Marvell International Ltd. All rights reserved. - * Author: Chao Xie <chao.xie@marvell.com> - * Neil Zhang <zhangwm@marvell.com> - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/uaccess.h> -#include <linux/device.h> -#include <linux/proc_fs.h> -#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> -#include <linux/usb/otg.h> -#include <linux/usb/gadget.h> -#include <linux/usb/hcd.h> -#include <linux/platform_data/mv_usb.h> - -#include "phy-mv-usb.h" - -#define DRIVER_DESC "Marvell USB OTG transceiver driver" - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - -static const char driver_name[] = "mv-otg"; - -static char *state_string[] = { - "undefined", - "b_idle", - "b_srp_init", - "b_peripheral", - "b_wait_acon", - "b_host", - "a_idle", - "a_wait_vrise", - "a_wait_bcon", - "a_host", - "a_suspend", - "a_peripheral", - "a_wait_vfall", - "a_vbus_err" -}; - -static int mv_otg_set_vbus(struct usb_otg *otg, bool on) -{ - struct mv_otg *mvotg = container_of(otg->usb_phy, struct mv_otg, phy); - if (mvotg->pdata->set_vbus == NULL) - return -ENODEV; - - return mvotg->pdata->set_vbus(on); -} - -static int mv_otg_set_host(struct usb_otg *otg, - struct usb_bus *host) -{ - otg->host = host; - - return 0; -} - -static int mv_otg_set_peripheral(struct usb_otg *otg, - struct usb_gadget *gadget) -{ - otg->gadget = gadget; - - return 0; -} - -static void mv_otg_run_state_machine(struct mv_otg *mvotg, - unsigned long delay) -{ - dev_dbg(&mvotg->pdev->dev, "transceiver is updated\n"); - if (!mvotg->qwork) - return; - - queue_delayed_work(mvotg->qwork, &mvotg->work, delay); -} - -static void mv_otg_timer_await_bcon(struct timer_list *t) -{ - struct mv_otg *mvotg = from_timer(mvotg, t, - otg_ctrl.timer[A_WAIT_BCON_TIMER]); - - mvotg->otg_ctrl.a_wait_bcon_timeout = 1; - - dev_info(&mvotg->pdev->dev, "B Device No Response!\n"); - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } -} - -static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id) -{ - struct timer_list *timer; - - if (id >= OTG_TIMER_NUM) - return -EINVAL; - - timer = &mvotg->otg_ctrl.timer[id]; - - if (timer_pending(timer)) - del_timer(timer); - - return 0; -} - -static int mv_otg_set_timer(struct mv_otg *mvotg, unsigned int id, - unsigned long interval) -{ - struct timer_list *timer; - - if (id >= OTG_TIMER_NUM) - return -EINVAL; - - timer = &mvotg->otg_ctrl.timer[id]; - if (timer_pending(timer)) { - dev_err(&mvotg->pdev->dev, "Timer%d is already running\n", id); - return -EBUSY; - } - - timer->expires = jiffies + interval; - add_timer(timer); - - return 0; -} - -static int mv_otg_reset(struct mv_otg *mvotg) -{ - u32 tmp; - int ret; - - /* Stop the controller */ - tmp = readl(&mvotg->op_regs->usbcmd); - tmp &= ~USBCMD_RUN_STOP; - writel(tmp, &mvotg->op_regs->usbcmd); - - /* Reset the controller to get default values */ - writel(USBCMD_CTRL_RESET, &mvotg->op_regs->usbcmd); - - ret = readl_poll_timeout_atomic(&mvotg->op_regs->usbcmd, tmp, - (tmp & USBCMD_CTRL_RESET), 10, 10000); - if (ret < 0) { - dev_err(&mvotg->pdev->dev, - "Wait for RESET completed TIMEOUT\n"); - return ret; - } - - writel(0x0, &mvotg->op_regs->usbintr); - tmp = readl(&mvotg->op_regs->usbsts); - writel(tmp, &mvotg->op_regs->usbsts); - - return 0; -} - -static void mv_otg_init_irq(struct mv_otg *mvotg) -{ - u32 otgsc; - - mvotg->irq_en = OTGSC_INTR_A_SESSION_VALID - | OTGSC_INTR_A_VBUS_VALID; - mvotg->irq_status = OTGSC_INTSTS_A_SESSION_VALID - | OTGSC_INTSTS_A_VBUS_VALID; - - if (mvotg->pdata->vbus == NULL) { - mvotg->irq_en |= OTGSC_INTR_B_SESSION_VALID - | OTGSC_INTR_B_SESSION_END; - mvotg->irq_status |= OTGSC_INTSTS_B_SESSION_VALID - | OTGSC_INTSTS_B_SESSION_END; - } - - if (mvotg->pdata->id == NULL) { - mvotg->irq_en |= OTGSC_INTR_USB_ID; - mvotg->irq_status |= OTGSC_INTSTS_USB_ID; - } - - otgsc = readl(&mvotg->op_regs->otgsc); - otgsc |= mvotg->irq_en; - writel(otgsc, &mvotg->op_regs->otgsc); -} - -static void mv_otg_start_host(struct mv_otg *mvotg, int on) -{ -#ifdef CONFIG_USB - struct usb_otg *otg = mvotg->phy.otg; - struct usb_hcd *hcd; - - if (!otg->host) - return; - - dev_info(&mvotg->pdev->dev, "%s host\n", on ? "start" : "stop"); - - hcd = bus_to_hcd(otg->host); - - if (on) { - usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); - device_wakeup_enable(hcd->self.controller); - } else { - usb_remove_hcd(hcd); - } -#endif /* CONFIG_USB */ -} - -static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on) -{ - struct usb_otg *otg = mvotg->phy.otg; - - if (!otg->gadget) - return; - - dev_info(mvotg->phy.dev, "gadget %s\n", str_on_off(on)); - - if (on) - usb_gadget_vbus_connect(otg->gadget); - else - usb_gadget_vbus_disconnect(otg->gadget); -} - -static void otg_clock_enable(struct mv_otg *mvotg) -{ - clk_prepare_enable(mvotg->clk); -} - -static void otg_clock_disable(struct mv_otg *mvotg) -{ - clk_disable_unprepare(mvotg->clk); -} - -static int mv_otg_enable_internal(struct mv_otg *mvotg) -{ - int retval = 0; - - if (mvotg->active) - return 0; - - dev_dbg(&mvotg->pdev->dev, "otg enabled\n"); - - otg_clock_enable(mvotg); - if (mvotg->pdata->phy_init) { - retval = mvotg->pdata->phy_init(mvotg->phy_regs); - if (retval) { - dev_err(&mvotg->pdev->dev, - "init phy error %d\n", retval); - otg_clock_disable(mvotg); - return retval; - } - } - mvotg->active = 1; - - return 0; - -} - -static int mv_otg_enable(struct mv_otg *mvotg) -{ - if (mvotg->clock_gating) - return mv_otg_enable_internal(mvotg); - - return 0; -} - -static void mv_otg_disable_internal(struct mv_otg *mvotg) -{ - if (mvotg->active) { - dev_dbg(&mvotg->pdev->dev, "otg disabled\n"); - if (mvotg->pdata->phy_deinit) - mvotg->pdata->phy_deinit(mvotg->phy_regs); - otg_clock_disable(mvotg); - mvotg->active = 0; - } -} - -static void mv_otg_disable(struct mv_otg *mvotg) -{ - if (mvotg->clock_gating) - mv_otg_disable_internal(mvotg); -} - -static void mv_otg_update_inputs(struct mv_otg *mvotg) -{ - struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; - u32 otgsc; - - otgsc = readl(&mvotg->op_regs->otgsc); - - if (mvotg->pdata->vbus) { - if (mvotg->pdata->vbus->poll() == VBUS_HIGH) { - otg_ctrl->b_sess_vld = 1; - otg_ctrl->b_sess_end = 0; - } else { - otg_ctrl->b_sess_vld = 0; - otg_ctrl->b_sess_end = 1; - } - } else { - otg_ctrl->b_sess_vld = !!(otgsc & OTGSC_STS_B_SESSION_VALID); - otg_ctrl->b_sess_end = !!(otgsc & OTGSC_STS_B_SESSION_END); - } - - if (mvotg->pdata->id) - otg_ctrl->id = !!mvotg->pdata->id->poll(); - else - otg_ctrl->id = !!(otgsc & OTGSC_STS_USB_ID); - - if (mvotg->pdata->otg_force_a_bus_req && !otg_ctrl->id) - otg_ctrl->a_bus_req = 1; - - otg_ctrl->a_sess_vld = !!(otgsc & OTGSC_STS_A_SESSION_VALID); - otg_ctrl->a_vbus_vld = !!(otgsc & OTGSC_STS_A_VBUS_VALID); - - dev_dbg(&mvotg->pdev->dev, "%s: ", __func__); - dev_dbg(&mvotg->pdev->dev, "id %d\n", otg_ctrl->id); - dev_dbg(&mvotg->pdev->dev, "b_sess_vld %d\n", otg_ctrl->b_sess_vld); - dev_dbg(&mvotg->pdev->dev, "b_sess_end %d\n", otg_ctrl->b_sess_end); - dev_dbg(&mvotg->pdev->dev, "a_vbus_vld %d\n", otg_ctrl->a_vbus_vld); - dev_dbg(&mvotg->pdev->dev, "a_sess_vld %d\n", otg_ctrl->a_sess_vld); -} - -static void mv_otg_update_state(struct mv_otg *mvotg) -{ - struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; - int old_state = mvotg->phy.otg->state; - - switch (old_state) { - case OTG_STATE_UNDEFINED: - mvotg->phy.otg->state = OTG_STATE_B_IDLE; - fallthrough; - case OTG_STATE_B_IDLE: - if (otg_ctrl->id == 0) - mvotg->phy.otg->state = OTG_STATE_A_IDLE; - else if (otg_ctrl->b_sess_vld) - mvotg->phy.otg->state = OTG_STATE_B_PERIPHERAL; - break; - case OTG_STATE_B_PERIPHERAL: - if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0) - mvotg->phy.otg->state = OTG_STATE_B_IDLE; - break; - case OTG_STATE_A_IDLE: - if (otg_ctrl->id) - mvotg->phy.otg->state = OTG_STATE_B_IDLE; - else if (!(otg_ctrl->a_bus_drop) && - (otg_ctrl->a_bus_req || otg_ctrl->a_srp_det)) - mvotg->phy.otg->state = OTG_STATE_A_WAIT_VRISE; - break; - case OTG_STATE_A_WAIT_VRISE: - if (otg_ctrl->a_vbus_vld) - mvotg->phy.otg->state = OTG_STATE_A_WAIT_BCON; - break; - case OTG_STATE_A_WAIT_BCON: - if (otg_ctrl->id || otg_ctrl->a_bus_drop - || otg_ctrl->a_wait_bcon_timeout) { - mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); - mvotg->otg_ctrl.a_wait_bcon_timeout = 0; - mvotg->phy.otg->state = OTG_STATE_A_WAIT_VFALL; - otg_ctrl->a_bus_req = 0; - } else if (!otg_ctrl->a_vbus_vld) { - mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); - mvotg->otg_ctrl.a_wait_bcon_timeout = 0; - mvotg->phy.otg->state = OTG_STATE_A_VBUS_ERR; - } else if (otg_ctrl->b_conn) { - mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER); - mvotg->otg_ctrl.a_wait_bcon_timeout = 0; - mvotg->phy.otg->state = OTG_STATE_A_HOST; - } - break; - case OTG_STATE_A_HOST: - if (otg_ctrl->id || !otg_ctrl->b_conn - || otg_ctrl->a_bus_drop) - mvotg->phy.otg->state = OTG_STATE_A_WAIT_BCON; - else if (!otg_ctrl->a_vbus_vld) - mvotg->phy.otg->state = OTG_STATE_A_VBUS_ERR; - break; - case OTG_STATE_A_WAIT_VFALL: - if (otg_ctrl->id - || (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld) - || otg_ctrl->a_bus_req) - mvotg->phy.otg->state = OTG_STATE_A_IDLE; - break; - case OTG_STATE_A_VBUS_ERR: - if (otg_ctrl->id || otg_ctrl->a_clr_err - || otg_ctrl->a_bus_drop) { - otg_ctrl->a_clr_err = 0; - mvotg->phy.otg->state = OTG_STATE_A_WAIT_VFALL; - } - break; - default: - break; - } -} - -static void mv_otg_work(struct work_struct *work) -{ - struct mv_otg *mvotg; - struct usb_otg *otg; - int old_state; - - mvotg = container_of(to_delayed_work(work), struct mv_otg, work); - -run: - /* work queue is single thread, or we need spin_lock to protect */ - otg = mvotg->phy.otg; - old_state = otg->state; - - if (!mvotg->active) - return; - - mv_otg_update_inputs(mvotg); - mv_otg_update_state(mvotg); - - if (old_state != mvotg->phy.otg->state) { - dev_info(&mvotg->pdev->dev, "change from state %s to %s\n", - state_string[old_state], - state_string[mvotg->phy.otg->state]); - - switch (mvotg->phy.otg->state) { - case OTG_STATE_B_IDLE: - otg->default_a = 0; - if (old_state == OTG_STATE_B_PERIPHERAL) - mv_otg_start_periphrals(mvotg, 0); - mv_otg_reset(mvotg); - mv_otg_disable(mvotg); - usb_phy_set_event(&mvotg->phy, USB_EVENT_NONE); - break; - case OTG_STATE_B_PERIPHERAL: - mv_otg_enable(mvotg); - mv_otg_start_periphrals(mvotg, 1); - usb_phy_set_event(&mvotg->phy, USB_EVENT_ENUMERATED); - break; - case OTG_STATE_A_IDLE: - otg->default_a = 1; - mv_otg_enable(mvotg); - if (old_state == OTG_STATE_A_WAIT_VFALL) - mv_otg_start_host(mvotg, 0); - mv_otg_reset(mvotg); - break; - case OTG_STATE_A_WAIT_VRISE: - mv_otg_set_vbus(otg, 1); - break; - case OTG_STATE_A_WAIT_BCON: - if (old_state != OTG_STATE_A_HOST) - mv_otg_start_host(mvotg, 1); - mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER, - T_A_WAIT_BCON); - /* - * Now, we directly enter A_HOST. So set b_conn = 1 - * here. In fact, it need host driver to notify us. - */ - mvotg->otg_ctrl.b_conn = 1; - break; - case OTG_STATE_A_HOST: - break; - case OTG_STATE_A_WAIT_VFALL: - /* - * Now, we has exited A_HOST. So set b_conn = 0 - * here. In fact, it need host driver to notify us. - */ - mvotg->otg_ctrl.b_conn = 0; - mv_otg_set_vbus(otg, 0); - break; - case OTG_STATE_A_VBUS_ERR: - break; - default: - break; - } - goto run; - } -} - -static irqreturn_t mv_otg_irq(int irq, void *dev) -{ - struct mv_otg *mvotg = dev; - u32 otgsc; - - otgsc = readl(&mvotg->op_regs->otgsc); - writel(otgsc, &mvotg->op_regs->otgsc); - - /* - * if we have vbus, then the vbus detection for B-device - * will be done by mv_otg_inputs_irq(). - */ - if (mvotg->pdata->vbus) - if ((otgsc & OTGSC_STS_USB_ID) && - !(otgsc & OTGSC_INTSTS_USB_ID)) - return IRQ_NONE; - - if ((otgsc & mvotg->irq_status) == 0) - return IRQ_NONE; - - mv_otg_run_state_machine(mvotg, 0); - - return IRQ_HANDLED; -} - -static irqreturn_t mv_otg_inputs_irq(int irq, void *dev) -{ - struct mv_otg *mvotg = dev; - - /* The clock may disabled at this time */ - if (!mvotg->active) { - mv_otg_enable(mvotg); - mv_otg_init_irq(mvotg); - } - - mv_otg_run_state_machine(mvotg, 0); - - return IRQ_HANDLED; -} - -static ssize_t -a_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", - mvotg->otg_ctrl.a_bus_req); -} - -static ssize_t -a_bus_req_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - - if (count > 2) - return -1; - - /* We will use this interface to change to A device */ - if (mvotg->phy.otg->state != OTG_STATE_B_IDLE - && mvotg->phy.otg->state != OTG_STATE_A_IDLE) - return -1; - - /* The clock may disabled and we need to set irq for ID detected */ - mv_otg_enable(mvotg); - mv_otg_init_irq(mvotg); - - if (buf[0] == '1') { - mvotg->otg_ctrl.a_bus_req = 1; - mvotg->otg_ctrl.a_bus_drop = 0; - dev_dbg(&mvotg->pdev->dev, - "User request: a_bus_req = 1\n"); - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - } - - return count; -} - -static DEVICE_ATTR_RW(a_bus_req); - -static ssize_t -a_clr_err_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - if (!mvotg->phy.otg->default_a) - return -1; - - if (count > 2) - return -1; - - if (buf[0] == '1') { - mvotg->otg_ctrl.a_clr_err = 1; - dev_dbg(&mvotg->pdev->dev, - "User request: a_clr_err = 1\n"); - } - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - - return count; -} - -static DEVICE_ATTR_WO(a_clr_err); - -static ssize_t -a_bus_drop_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", - mvotg->otg_ctrl.a_bus_drop); -} - -static ssize_t -a_bus_drop_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct mv_otg *mvotg = dev_get_drvdata(dev); - if (!mvotg->phy.otg->default_a) - return -1; - - if (count > 2) - return -1; - - if (buf[0] == '0') { - mvotg->otg_ctrl.a_bus_drop = 0; - dev_dbg(&mvotg->pdev->dev, - "User request: a_bus_drop = 0\n"); - } else if (buf[0] == '1') { - mvotg->otg_ctrl.a_bus_drop = 1; - mvotg->otg_ctrl.a_bus_req = 0; - dev_dbg(&mvotg->pdev->dev, - "User request: a_bus_drop = 1\n"); - dev_dbg(&mvotg->pdev->dev, - "User request: and a_bus_req = 0\n"); - } - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - - return count; -} - -static DEVICE_ATTR_RW(a_bus_drop); - -static struct attribute *inputs_attrs[] = { - &dev_attr_a_bus_req.attr, - &dev_attr_a_clr_err.attr, - &dev_attr_a_bus_drop.attr, - NULL, -}; - -static const struct attribute_group inputs_attr_group = { - .name = "inputs", - .attrs = inputs_attrs, -}; - -static const struct attribute_group *mv_otg_groups[] = { - &inputs_attr_group, - NULL, -}; - -static void mv_otg_remove(struct platform_device *pdev) -{ - struct mv_otg *mvotg = platform_get_drvdata(pdev); - - if (mvotg->qwork) - destroy_workqueue(mvotg->qwork); - - mv_otg_disable(mvotg); - - usb_remove_phy(&mvotg->phy); -} - -static int mv_otg_probe(struct platform_device *pdev) -{ - struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct mv_otg *mvotg; - struct usb_otg *otg; - struct resource *r; - int retval = 0, i; - - if (pdata == NULL) { - dev_err(&pdev->dev, "failed to get platform data\n"); - return -ENODEV; - } - - mvotg = devm_kzalloc(&pdev->dev, sizeof(*mvotg), GFP_KERNEL); - if (!mvotg) - return -ENOMEM; - - otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); - if (!otg) - return -ENOMEM; - - platform_set_drvdata(pdev, mvotg); - - mvotg->pdev = pdev; - mvotg->pdata = pdata; - - mvotg->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(mvotg->clk)) - return PTR_ERR(mvotg->clk); - - mvotg->qwork = create_singlethread_workqueue("mv_otg_queue"); - if (!mvotg->qwork) { - dev_dbg(&pdev->dev, "cannot create workqueue for OTG\n"); - return -ENOMEM; - } - - INIT_DELAYED_WORK(&mvotg->work, mv_otg_work); - - /* OTG common part */ - mvotg->pdev = pdev; - mvotg->phy.dev = &pdev->dev; - mvotg->phy.otg = otg; - mvotg->phy.label = driver_name; - - otg->state = OTG_STATE_UNDEFINED; - otg->usb_phy = &mvotg->phy; - otg->set_host = mv_otg_set_host; - otg->set_peripheral = mv_otg_set_peripheral; - otg->set_vbus = mv_otg_set_vbus; - - for (i = 0; i < OTG_TIMER_NUM; i++) - timer_setup(&mvotg->otg_ctrl.timer[i], - mv_otg_timer_await_bcon, 0); - - r = platform_get_resource_byname(mvotg->pdev, - IORESOURCE_MEM, "phyregs"); - if (r == NULL) { - dev_err(&pdev->dev, "no phy I/O memory resource defined\n"); - retval = -ENODEV; - goto err_destroy_workqueue; - } - - mvotg->phy_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (mvotg->phy_regs == NULL) { - dev_err(&pdev->dev, "failed to map phy I/O memory\n"); - retval = -EFAULT; - goto err_destroy_workqueue; - } - - r = platform_get_resource_byname(mvotg->pdev, - IORESOURCE_MEM, "capregs"); - if (r == NULL) { - dev_err(&pdev->dev, "no I/O memory resource defined\n"); - retval = -ENODEV; - goto err_destroy_workqueue; - } - - mvotg->cap_regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (mvotg->cap_regs == NULL) { - dev_err(&pdev->dev, "failed to map I/O memory\n"); - retval = -EFAULT; - goto err_destroy_workqueue; - } - - /* we will acces controller register, so enable the udc controller */ - retval = mv_otg_enable_internal(mvotg); - if (retval) { - dev_err(&pdev->dev, "mv otg enable error %d\n", retval); - goto err_destroy_workqueue; - } - - mvotg->op_regs = - (struct mv_otg_regs __iomem *) ((unsigned long) mvotg->cap_regs - + (readl(mvotg->cap_regs) & CAPLENGTH_MASK)); - - if (pdata->id) { - retval = devm_request_threaded_irq(&pdev->dev, pdata->id->irq, - NULL, mv_otg_inputs_irq, - IRQF_ONESHOT, "id", mvotg); - if (retval) { - dev_info(&pdev->dev, - "Failed to request irq for ID\n"); - pdata->id = NULL; - } - } - - if (pdata->vbus) { - mvotg->clock_gating = 1; - retval = devm_request_threaded_irq(&pdev->dev, pdata->vbus->irq, - NULL, mv_otg_inputs_irq, - IRQF_ONESHOT, "vbus", mvotg); - if (retval) { - dev_info(&pdev->dev, - "Failed to request irq for VBUS, " - "disable clock gating\n"); - mvotg->clock_gating = 0; - pdata->vbus = NULL; - } - } - - if (pdata->disable_otg_clock_gating) - mvotg->clock_gating = 0; - - mv_otg_reset(mvotg); - mv_otg_init_irq(mvotg); - - r = platform_get_resource(mvotg->pdev, IORESOURCE_IRQ, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no IRQ resource defined\n"); - retval = -ENODEV; - goto err_disable_clk; - } - - mvotg->irq = r->start; - if (devm_request_irq(&pdev->dev, mvotg->irq, mv_otg_irq, IRQF_SHARED, - driver_name, mvotg)) { - dev_err(&pdev->dev, "Request irq %d for OTG failed\n", - mvotg->irq); - mvotg->irq = 0; - retval = -ENODEV; - goto err_disable_clk; - } - - retval = usb_add_phy(&mvotg->phy, USB_PHY_TYPE_USB2); - if (retval < 0) { - dev_err(&pdev->dev, "can't register transceiver, %d\n", - retval); - goto err_disable_clk; - } - - spin_lock_init(&mvotg->wq_lock); - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 2 * HZ); - spin_unlock(&mvotg->wq_lock); - } - - dev_info(&pdev->dev, - "successful probe OTG device %s clock gating.\n", - mvotg->clock_gating ? "with" : "without"); - - return 0; - -err_disable_clk: - mv_otg_disable_internal(mvotg); -err_destroy_workqueue: - destroy_workqueue(mvotg->qwork); - - return retval; -} - -#ifdef CONFIG_PM -static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct mv_otg *mvotg = platform_get_drvdata(pdev); - - if (mvotg->phy.otg->state != OTG_STATE_B_IDLE) { - dev_info(&pdev->dev, - "OTG state is not B_IDLE, it is %d!\n", - mvotg->phy.otg->state); - return -EAGAIN; - } - - if (!mvotg->clock_gating) - mv_otg_disable_internal(mvotg); - - return 0; -} - -static int mv_otg_resume(struct platform_device *pdev) -{ - struct mv_otg *mvotg = platform_get_drvdata(pdev); - u32 otgsc; - - if (!mvotg->clock_gating) { - mv_otg_enable_internal(mvotg); - - otgsc = readl(&mvotg->op_regs->otgsc); - otgsc |= mvotg->irq_en; - writel(otgsc, &mvotg->op_regs->otgsc); - - if (spin_trylock(&mvotg->wq_lock)) { - mv_otg_run_state_machine(mvotg, 0); - spin_unlock(&mvotg->wq_lock); - } - } - return 0; -} -#endif - -static struct platform_driver mv_otg_driver = { - .probe = mv_otg_probe, - .remove = mv_otg_remove, - .driver = { - .name = driver_name, - .dev_groups = mv_otg_groups, - }, -#ifdef CONFIG_PM - .suspend = mv_otg_suspend, - .resume = mv_otg_resume, -#endif -}; -module_platform_driver(mv_otg_driver); diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 7490f1798b46..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) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index bee222967f6b..00443a7beaeb 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -29,17 +29,26 @@ #include <linux/usb/tegra_usb_phy.h> #include <linux/usb/ulpi.h> +#define USB_TXFILLTUNING 0x154 +#define USB_FIFO_TXFILL_THRES(x) (((x) & 0x1f) << 16) +#define USB_FIFO_TXFILL_MASK 0x1f0000 + #define ULPI_VIEWPORT 0x170 /* PORTSC PTS/PHCD bits, Tegra20 only */ #define TEGRA_USB_PORTSC1 0x184 -#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) -#define TEGRA_USB_PORTSC1_PHCD BIT(23) +#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) +#define TEGRA_USB_PORTSC1_PHCD BIT(23) +#define TEGRA_USB_PORTSC1_WKOC BIT(22) +#define TEGRA_USB_PORTSC1_WKDS BIT(21) +#define TEGRA_USB_PORTSC1_WKCN BIT(20) /* HOSTPC1 PTS/PHCD bits, Tegra30 and above */ +#define TEGRA30_USB_PORTSC1 0x174 #define TEGRA_USB_HOSTPC1_DEVLC 0x1b4 -#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) -#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22) +#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) +#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22) +#define TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC 4 /* Bits of PORTSC1, which will get cleared by writing 1 into them */ #define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) @@ -51,11 +60,12 @@ #define USB_SUSP_CLR BIT(5) #define USB_PHY_CLK_VALID BIT(7) #define UTMIP_RESET BIT(11) -#define UHSIC_RESET BIT(11) #define UTMIP_PHY_ENABLE BIT(12) #define ULPI_PHY_ENABLE BIT(13) #define USB_SUSP_SET BIT(14) +#define UHSIC_RESET BIT(14) #define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) +#define UHSIC_PHY_ENABLE BIT(19) #define USB_PHY_VBUS_SENSORS 0x404 #define B_SESS_VLD_WAKEUP_EN BIT(14) @@ -156,6 +166,58 @@ #define UTMIP_BIAS_CFG1 0x83c #define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) +/* + * Tegra20 has no UTMIP registers on PHY2 and UHSIC registers start from 0x800 + * just where UTMIP registers should have been. This is the case only with Tegra20 + * Tegra30+ have UTMIP registers at 0x800 and UHSIC registers are shifted by 0x400 + * to 0xc00, but register layout is preserved. + */ +#define UHSIC_PLL_CFG1 0x804 +#define UHSIC_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UHSIC_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 14) + +#define UHSIC_HSRX_CFG0 0x808 +#define UHSIC_ELASTIC_UNDERRUN_LIMIT(x) (((x) & 0x1f) << 2) +#define UHSIC_ELASTIC_OVERRUN_LIMIT(x) (((x) & 0x1f) << 8) +#define UHSIC_IDLE_WAIT(x) (((x) & 0x1f) << 13) + +#define UHSIC_HSRX_CFG1 0x80c +#define UHSIC_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) + +#define UHSIC_TX_CFG0 0x810 +#define UHSIC_HS_READY_WAIT_FOR_VALID BIT(9) + +#define UHSIC_MISC_CFG0 0x814 +#define UHSIC_SUSPEND_EXIT_ON_EDGE BIT(7) +#define UHSIC_DETECT_SHORT_CONNECT BIT(8) +#define UHSIC_FORCE_XCVR_MODE BIT(15) +#define UHSIC_DISABLE_BUSRESET BIT(20) + +#define UHSIC_MISC_CFG1 0x818 +#define UHSIC_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 2) + +#define UHSIC_PADS_CFG0 0x81c +#define UHSIC_TX_RTUNEN 0xf000 +#define UHSIC_TX_RTUNE(x) (((x) & 0xf) << 12) + +#define UHSIC_PADS_CFG1 0x820 +#define UHSIC_PD_BG BIT(2) +#define UHSIC_PD_TX BIT(3) +#define UHSIC_PD_TRK BIT(4) +#define UHSIC_PD_RX BIT(5) +#define UHSIC_PD_ZI BIT(6) +#define UHSIC_RX_SEL BIT(7) +#define UHSIC_RPD_DATA BIT(9) +#define UHSIC_RPD_STROBE BIT(10) +#define UHSIC_RPU_DATA BIT(11) +#define UHSIC_RPU_STROBE BIT(12) + +#define UHSIC_CMD_CFG0 0x824 +#define UHSIC_PRETEND_CONNECT_DETECT BIT(5) + +#define UHSIC_STAT_CFG0 0x828 +#define UHSIC_CONNECT_DETECT BIT(0) + /* For Tegra30 and above only, the address is different in Tegra20 */ #define USB_USBMODE 0x1f8 #define USB_USBMODE_MASK (3 << 0) @@ -174,7 +236,8 @@ struct tegra_xtal_freq { u8 enable_delay; u8 stable_count; u8 active_delay; - u8 xtal_freq_count; + u8 utmi_xtal_freq_count; + u16 hsic_xtal_freq_count; u16 debounce; }; @@ -184,7 +247,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .enable_delay = 0x02, .stable_count = 0x2F, .active_delay = 0x04, - .xtal_freq_count = 0x76, + .utmi_xtal_freq_count = 0x76, + .hsic_xtal_freq_count = 0x1CA, .debounce = 0x7530, }, { @@ -192,7 +256,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .enable_delay = 0x02, .stable_count = 0x33, .active_delay = 0x05, - .xtal_freq_count = 0x7F, + .utmi_xtal_freq_count = 0x7F, + .hsic_xtal_freq_count = 0x1F0, .debounce = 0x7EF4, }, { @@ -200,7 +265,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .enable_delay = 0x03, .stable_count = 0x4B, .active_delay = 0x06, - .xtal_freq_count = 0xBB, + .utmi_xtal_freq_count = 0xBB, + .hsic_xtal_freq_count = 0x2DD, .debounce = 0xBB80, }, { @@ -208,7 +274,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .enable_delay = 0x04, .stable_count = 0x66, .active_delay = 0x09, - .xtal_freq_count = 0xFE, + .utmi_xtal_freq_count = 0xFE, + .hsic_xtal_freq_count = 0x3E0, .debounce = 0xFDE8, }, }; @@ -532,7 +599,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val = readl_relaxed(base + UTMIP_PLL_CFG1); val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); - val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | + val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->utmi_xtal_freq_count) | UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); writel_relaxed(val, base + UTMIP_PLL_CFG1); } @@ -711,58 +778,6 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy) return utmip_pad_power_off(phy); } -static void utmi_phy_preresume(struct tegra_usb_phy *phy) -{ - void __iomem *base = phy->regs; - u32 val; - - val = readl_relaxed(base + UTMIP_TX_CFG0); - val |= UTMIP_HS_DISCON_DISABLE; - writel_relaxed(val, base + UTMIP_TX_CFG0); -} - -static void utmi_phy_postresume(struct tegra_usb_phy *phy) -{ - void __iomem *base = phy->regs; - u32 val; - - val = readl_relaxed(base + UTMIP_TX_CFG0); - val &= ~UTMIP_HS_DISCON_DISABLE; - writel_relaxed(val, base + UTMIP_TX_CFG0); -} - -static void utmi_phy_restore_start(struct tegra_usb_phy *phy, - enum tegra_usb_phy_port_speed port_speed) -{ - void __iomem *base = phy->regs; - u32 val; - - val = readl_relaxed(base + UTMIP_MISC_CFG0); - val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); - if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) - val |= UTMIP_DPDM_OBSERVE_SEL_FS_K; - else - val |= UTMIP_DPDM_OBSERVE_SEL_FS_J; - writel_relaxed(val, base + UTMIP_MISC_CFG0); - usleep_range(1, 10); - - val = readl_relaxed(base + UTMIP_MISC_CFG0); - val |= UTMIP_DPDM_OBSERVE; - writel_relaxed(val, base + UTMIP_MISC_CFG0); - usleep_range(10, 100); -} - -static void utmi_phy_restore_end(struct tegra_usb_phy *phy) -{ - void __iomem *base = phy->regs; - u32 val; - - val = readl_relaxed(base + UTMIP_MISC_CFG0); - val &= ~UTMIP_DPDM_OBSERVE; - writel_relaxed(val, base + UTMIP_MISC_CFG0); - usleep_range(10, 100); -} - static int ulpi_phy_power_on(struct tegra_usb_phy *phy) { void __iomem *base = phy->regs; @@ -855,17 +870,170 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy) return 0; } +static u32 tegra_hsic_readl(struct tegra_usb_phy *phy, u32 reg) +{ + void __iomem *base = phy->regs; + u32 shift = phy->soc_config->uhsic_registers_offset; + + return readl_relaxed(base + shift + reg); +} + +static void tegra_hsic_writel(struct tegra_usb_phy *phy, u32 reg, u32 value) +{ + void __iomem *base = phy->regs; + u32 shift = phy->soc_config->uhsic_registers_offset; + + writel_relaxed(value, base + shift + reg); +} + +static int uhsic_phy_power_on(struct tegra_usb_phy *phy) +{ + struct tegra_utmip_config *config = phy->config; + void __iomem *base = phy->regs; + u32 val; + int err = 0; + + val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1); + val &= ~(UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX | + UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE); + val |= UHSIC_RX_SEL; + tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val); + + udelay(2); + + val = readl_relaxed(base + USB_SUSP_CTRL); + val |= UHSIC_RESET; + writel_relaxed(val, base + USB_SUSP_CTRL); + + udelay(30); + + val = readl_relaxed(base + USB_SUSP_CTRL); + val |= UHSIC_PHY_ENABLE; + writel_relaxed(val, base + USB_SUSP_CTRL); + + val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG0); + val &= ~(UHSIC_IDLE_WAIT(~0) | + UHSIC_ELASTIC_UNDERRUN_LIMIT(~0) | + UHSIC_ELASTIC_OVERRUN_LIMIT(~0)); + val |= UHSIC_IDLE_WAIT(config->idle_wait_delay) | + UHSIC_ELASTIC_UNDERRUN_LIMIT(config->elastic_limit) | + UHSIC_ELASTIC_OVERRUN_LIMIT(config->elastic_limit); + tegra_hsic_writel(phy, UHSIC_HSRX_CFG0, val); + + val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG1); + val &= ~UHSIC_HS_SYNC_START_DLY(~0); + val |= UHSIC_HS_SYNC_START_DLY(config->hssync_start_delay); + tegra_hsic_writel(phy, UHSIC_HSRX_CFG1, val); + + val = tegra_hsic_readl(phy, UHSIC_MISC_CFG0); + val |= UHSIC_SUSPEND_EXIT_ON_EDGE; + tegra_hsic_writel(phy, UHSIC_MISC_CFG0, val); + + val = tegra_hsic_readl(phy, UHSIC_MISC_CFG1); + val &= ~UHSIC_PLLU_STABLE_COUNT(~0); + val |= UHSIC_PLLU_STABLE_COUNT(phy->freq->stable_count); + tegra_hsic_writel(phy, UHSIC_MISC_CFG1, val); + + val = tegra_hsic_readl(phy, UHSIC_PLL_CFG1); + val &= ~(UHSIC_XTAL_FREQ_COUNT(~0) | + UHSIC_PLLU_ENABLE_DLY_COUNT(~0)); + val |= UHSIC_XTAL_FREQ_COUNT(phy->freq->hsic_xtal_freq_count) | + UHSIC_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); + tegra_hsic_writel(phy, UHSIC_PLL_CFG1, val); + + val = readl_relaxed(base + USB_SUSP_CTRL); + val &= ~UHSIC_RESET; + writel_relaxed(val, base + USB_SUSP_CTRL); + + udelay(2); + + if (phy->soc_config->requires_usbmode_setup) { + val = readl_relaxed(base + USB_USBMODE); + val &= ~USB_USBMODE_MASK; + if (phy->mode == USB_DR_MODE_HOST) + val |= USB_USBMODE_HOST; + else + val |= USB_USBMODE_DEVICE; + writel_relaxed(val, base + USB_USBMODE); + } + + set_pts(phy, phy->soc_config->uhsic_pts_value); + + val = readl_relaxed(base + USB_TXFILLTUNING); + if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) { + val = USB_FIFO_TXFILL_THRES(0x10); + writel_relaxed(val, base + USB_TXFILLTUNING); + } + + val = readl_relaxed(base + phy->soc_config->portsc1_offset); + val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS | + TEGRA_USB_PORTSC1_WKCN); + writel_relaxed(val, base + phy->soc_config->portsc1_offset); + + val = tegra_hsic_readl(phy, UHSIC_PADS_CFG0); + val &= ~UHSIC_TX_RTUNEN; + val |= UHSIC_TX_RTUNE(phy->soc_config->uhsic_tx_rtune); + tegra_hsic_writel(phy, UHSIC_PADS_CFG0, val); + + err = utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, + USB_PHY_CLK_VALID); + + if (err) + dev_err(phy->u_phy.dev, + "Timeout waiting for PHY to stabilize on enable (HSIC)\n"); + + return err; +} + +static int uhsic_phy_power_off(struct tegra_usb_phy *phy) +{ + void __iomem *base = phy->regs; + u32 val; + + set_phcd(phy, true); + + val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1); + val |= (UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX | + UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE); + tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val); + + val = readl_relaxed(base + USB_SUSP_CTRL); + val |= UHSIC_RESET; + writel_relaxed(val, base + USB_SUSP_CTRL); + + udelay(30); + + val = readl_relaxed(base + USB_SUSP_CTRL); + val &= ~UHSIC_PHY_ENABLE; + writel_relaxed(val, base + USB_SUSP_CTRL); + + return 0; +} + static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) { - int err; + int err = 0; if (phy->powered_on) return 0; - if (phy->is_ulpi_phy) - err = ulpi_phy_power_on(phy); - else + switch (phy->phy_type) { + case USBPHY_INTERFACE_MODE_UTMI: err = utmi_phy_power_on(phy); + break; + + case USBPHY_INTERFACE_MODE_ULPI: + err = ulpi_phy_power_on(phy); + break; + + case USBPHY_INTERFACE_MODE_HSIC: + err = uhsic_phy_power_on(phy); + break; + + default: + break; + } + if (err) return err; @@ -879,15 +1047,28 @@ static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) { - int err; + int err = 0; if (!phy->powered_on) return 0; - if (phy->is_ulpi_phy) - err = ulpi_phy_power_off(phy); - else + switch (phy->phy_type) { + case USBPHY_INTERFACE_MODE_UTMI: err = utmi_phy_power_off(phy); + break; + + case USBPHY_INTERFACE_MODE_ULPI: + err = ulpi_phy_power_off(phy); + break; + + case USBPHY_INTERFACE_MODE_HSIC: + err = uhsic_phy_power_off(phy); + break; + + default: + break; + } + if (err) return err; @@ -906,7 +1087,7 @@ static void tegra_usb_phy_shutdown(struct usb_phy *u_phy) usb_phy_set_wakeup(u_phy, false); tegra_usb_phy_power_off(phy); - if (!phy->is_ulpi_phy) + if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI) utmip_pad_close(phy); regulator_disable(phy->vbus); @@ -1092,7 +1273,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy) goto disable_clk; } - if (!phy->is_ulpi_phy) { + if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI) { err = utmip_pad_open(phy); if (err) goto disable_vbus; @@ -1109,7 +1290,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy) return 0; close_phy: - if (!phy->is_ulpi_phy) + if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI) utmip_pad_close(phy); disable_vbus: @@ -1123,43 +1304,6 @@ disable_clk: return err; } -void tegra_usb_phy_preresume(struct usb_phy *u_phy) -{ - struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); - - if (!phy->is_ulpi_phy) - utmi_phy_preresume(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume); - -void tegra_usb_phy_postresume(struct usb_phy *u_phy) -{ - struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); - - if (!phy->is_ulpi_phy) - utmi_phy_postresume(phy); -} -EXPORT_SYMBOL_GPL(tegra_usb_phy_postresume); - -void tegra_ehci_phy_restore_start(struct usb_phy *u_phy, - enum tegra_usb_phy_port_speed port_speed) -{ - struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); - - if (!phy->is_ulpi_phy) - utmi_phy_restore_start(phy, port_speed); -} -EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_start); - -void tegra_ehci_phy_restore_end(struct usb_phy *u_phy) -{ - struct tegra_usb_phy *phy = to_tegra_usb_phy(u_phy); - - if (!phy->is_ulpi_phy) - utmi_phy_restore_end(phy); -} -EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end); - static int read_utmi_param(struct platform_device *pdev, const char *param, u8 *dest) { @@ -1184,8 +1328,6 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, struct resource *res; int err; - tegra_phy->is_ulpi_phy = false; - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { dev_err(&pdev->dev, "Failed to get UTMI pad regs\n"); @@ -1320,6 +1462,10 @@ static const struct tegra_phy_soc_config tegra20_soc_config = { .requires_usbmode_setup = false, .requires_extra_tuning_parameters = false, .requires_pmc_ao_power_up = false, + .uhsic_registers_offset = 0, + .uhsic_tx_rtune = 0, /* 40 ohm */ + .uhsic_pts_value = 0, /* UTMI */ + .portsc1_offset = TEGRA_USB_PORTSC1, }; static const struct tegra_phy_soc_config tegra30_soc_config = { @@ -1328,6 +1474,10 @@ static const struct tegra_phy_soc_config tegra30_soc_config = { .requires_usbmode_setup = true, .requires_extra_tuning_parameters = true, .requires_pmc_ao_power_up = true, + .uhsic_registers_offset = 0x400, + .uhsic_tx_rtune = 8, /* 50 ohm */ + .uhsic_pts_value = TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC, + .portsc1_offset = TEGRA30_USB_PORTSC1, }; static const struct of_device_id tegra_usb_phy_id_table[] = { @@ -1341,7 +1491,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct tegra_usb_phy *tegra_phy; - enum usb_phy_interface phy_type; struct reset_control *reset; struct gpio_desc *gpiod; struct resource *res; @@ -1403,9 +1552,10 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) return err; } - phy_type = of_usb_get_phy_mode(np); - switch (phy_type) { + tegra_phy->phy_type = of_usb_get_phy_mode(np); + switch (tegra_phy->phy_type) { case USBPHY_INTERFACE_MODE_UTMI: + case USBPHY_INTERFACE_MODE_HSIC: err = utmi_phy_probe(tegra_phy, pdev); if (err) return err; @@ -1430,8 +1580,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) break; case USBPHY_INTERFACE_MODE_ULPI: - tegra_phy->is_ulpi_phy = true; - tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link"); err = PTR_ERR_OR_ZERO(tegra_phy->clk); if (err) { @@ -1471,7 +1619,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) default: dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n", - phy_type); + tegra_phy->phy_type); return -EINVAL; } diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 49d79c1257f3..8c09db750bfd 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -328,9 +328,8 @@ static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled) static int twl6030_usb_probe(struct platform_device *pdev) { - u32 ret; struct twl6030_usb *twl; - int status, err; + int status, err, ret; struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; 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 e1435bc59662..5a9b9353f343 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -646,6 +646,8 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) return -EINVAL; } + INIT_LIST_HEAD(&x->head); + usb_charger_init(x); ret = usb_add_extcon(x); if (ret) @@ -696,6 +698,8 @@ int usb_add_phy_dev(struct usb_phy *x) return -EINVAL; } + INIT_LIST_HEAD(&x->head); + usb_charger_init(x); ret = usb_add_extcon(x); if (ret) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 4b35ef216125..8c93bde4b816 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -580,6 +580,10 @@ static const struct of_device_id usbhs_of_match[] = { .data = &usbhs_rzg2l_plat_info, }, { + .compatible = "renesas,usbhs-r9a09g077", + .data = &usbhs_rzg2l_plat_info, + }, + { .compatible = "renesas,rcar-gen2-usbhs", .data = &usbhs_rcar_gen2_plat_info, }, @@ -685,10 +689,29 @@ static int usbhs_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug); spin_lock_init(usbhs_priv_to_lock(priv)); + /* + * Acquire clocks and enable power management (PM) early in the + * probe process, as the driver accesses registers during + * initialization. Ensure the device is active before proceeding. + */ + pm_runtime_enable(dev); + + ret = usbhsc_clk_get(dev, priv); + if (ret) + goto probe_pm_disable; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto probe_clk_put; + + ret = usbhsc_clk_prepare_enable(priv); + if (ret) + goto probe_pm_put; + /* call pipe and module init */ ret = usbhs_pipe_probe(priv); if (ret < 0) - return ret; + goto probe_clk_dis_unprepare; ret = usbhs_fifo_probe(priv); if (ret < 0) @@ -698,19 +721,15 @@ static int usbhs_probe(struct platform_device *pdev) if (ret < 0) goto probe_end_fifo_exit; - /* dev_set_drvdata should be called after usbhs_mod_init */ + /* platform_set_drvdata() should be called after usbhs_mod_probe() */ platform_set_drvdata(pdev, priv); ret = reset_control_deassert(priv->rsts); if (ret) goto probe_fail_rst; - ret = usbhsc_clk_get(dev, priv); - if (ret) - goto probe_fail_clks; - /* - * deviece reset here because + * device reset here because * USB device might be used in boot loader. */ usbhs_sys_clock_ctrl(priv, 0); @@ -721,7 +740,7 @@ static int usbhs_probe(struct platform_device *pdev) if (ret) { dev_warn(dev, "USB function not selected (GPIO)\n"); ret = -ENOTSUPP; - goto probe_end_mod_exit; + goto probe_assert_rest; } } @@ -735,14 +754,19 @@ static int usbhs_probe(struct platform_device *pdev) ret = usbhs_platform_call(priv, hardware_init, pdev); if (ret < 0) { dev_err(dev, "platform init failed.\n"); - goto probe_end_mod_exit; + goto probe_assert_rest; } /* reset phy for connection */ usbhs_platform_call(priv, phy_reset, pdev); - /* power control */ - pm_runtime_enable(dev); + /* + * Disable the clocks that were enabled earlier in the probe path, + * and let the driver handle the clocks beyond this point. + */ + usbhsc_clk_disable_unprepare(priv); + pm_runtime_put(dev); + if (!usbhs_get_dparam(priv, runtime_pwctrl)) { usbhsc_power_ctrl(priv, 1); usbhs_mod_autonomy_mode(priv); @@ -759,9 +783,7 @@ static int usbhs_probe(struct platform_device *pdev) return ret; -probe_end_mod_exit: - usbhsc_clk_put(priv); -probe_fail_clks: +probe_assert_rest: reset_control_assert(priv->rsts); probe_fail_rst: usbhs_mod_remove(priv); @@ -769,6 +791,14 @@ probe_end_fifo_exit: usbhs_fifo_remove(priv); probe_end_pipe_exit: usbhs_pipe_remove(priv); +probe_clk_dis_unprepare: + usbhsc_clk_disable_unprepare(priv); +probe_pm_put: + pm_runtime_put(dev); +probe_clk_put: + usbhsc_clk_put(priv); +probe_pm_disable: + pm_runtime_disable(dev); dev_info(dev, "probe failed (%d)\n", ret); @@ -783,24 +813,49 @@ static void usbhs_remove(struct platform_device *pdev) flush_delayed_work(&priv->notify_hotplug_work); + usbhs_platform_call(priv, hardware_exit, pdev); + reset_control_assert(priv->rsts); + + /* + * Explicitly free the IRQ to ensure the interrupt handler is + * disabled and synchronized before freeing resources. + * devm_free_irq() calls free_irq() which waits for any running + * ISR to complete, preventing UAF. + */ + devm_free_irq(&pdev->dev, priv->irq, priv); + + usbhs_mod_remove(priv); + usbhs_fifo_remove(priv); + usbhs_pipe_remove(priv); + /* power off */ if (!usbhs_get_dparam(priv, runtime_pwctrl)) usbhsc_power_ctrl(priv, 0); + usbhsc_clk_put(priv); pm_runtime_disable(&pdev->dev); +} - usbhs_platform_call(priv, hardware_exit, pdev); - usbhsc_clk_put(priv); - reset_control_assert(priv->rsts); - usbhs_mod_remove(priv); - usbhs_fifo_remove(priv); - usbhs_pipe_remove(priv); +static void usbhsc_restore(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + + if (!usbhs_get_dparam(priv, runtime_pwctrl)) { + usbhsc_power_ctrl(priv, 1); + usbhs_mod_autonomy_mode(priv); + } + + usbhs_platform_call(priv, phy_reset, pdev); + + usbhsc_schedule_notify_hotplug(pdev); } -static __maybe_unused int usbhsc_suspend(struct device *dev) +static int usbhsc_suspend(struct device *dev) { struct usbhs_priv *priv = dev_get_drvdata(dev); struct usbhs_mod *mod = usbhs_mod_get_current(priv); + int ret; if (mod) { usbhs_mod_call(priv, stop, priv); @@ -810,32 +865,33 @@ static __maybe_unused int usbhsc_suspend(struct device *dev) if (mod || !usbhs_get_dparam(priv, runtime_pwctrl)) usbhsc_power_ctrl(priv, 0); - return 0; + ret = reset_control_assert(priv->rsts); + if (ret) + usbhsc_restore(dev); + + return ret; } -static __maybe_unused int usbhsc_resume(struct device *dev) +static int usbhsc_resume(struct device *dev) { struct usbhs_priv *priv = dev_get_drvdata(dev); - struct platform_device *pdev = usbhs_priv_to_pdev(priv); - - if (!usbhs_get_dparam(priv, runtime_pwctrl)) { - usbhsc_power_ctrl(priv, 1); - usbhs_mod_autonomy_mode(priv); - } + int ret; - usbhs_platform_call(priv, phy_reset, pdev); + ret = reset_control_deassert(priv->rsts); + if (ret) + return ret; - usbhsc_schedule_notify_hotplug(pdev); + usbhsc_restore(dev); return 0; } -static SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume); static struct platform_driver renesas_usbhs_driver = { .driver = { .name = "renesas_usbhs", - .pm = &usbhsc_pm_ops, + .pm = pm_sleep_ptr(&usbhsc_pm_ops), .of_match_table = usbhs_of_match, }, .probe = usbhs_probe, diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 10607e273879..bac6f8fd0055 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -123,7 +123,7 @@ struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt) if (fifo) chan = usbhsf_dma_chan_get(fifo, pkt); if (chan) { - dmaengine_terminate_all(chan); + dmaengine_terminate_sync(chan); usbhsf_dma_unmap(pkt); } else { if (usbhs_pipe_is_dir_in(pipe)) diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index e8e5723f5412..0c7fe109d5c7 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -40,7 +40,6 @@ struct usbhsg_gpriv { struct usb_gadget gadget; struct usbhs_mod mod; - struct usbhsg_uep *uep; int uep_size; struct usb_gadget_driver *driver; @@ -53,6 +52,7 @@ struct usbhsg_gpriv { #define USBHSG_STATUS_WEDGE (1 << 2) #define USBHSG_STATUS_SELF_POWERED (1 << 3) #define USBHSG_STATUS_SOFT_CONNECT (1 << 4) + struct usbhsg_uep uep[] __counted_by(uep_size); }; struct usbhsg_recip_handle { @@ -325,7 +325,7 @@ static void __usbhsg_recip_send_status(struct usbhsg_gpriv *gpriv, } /* alloc recip data buffer */ - buf = kmalloc(sizeof(*buf), GFP_ATOMIC); + buf = kmalloc_obj(*buf, GFP_ATOMIC); if (!buf) { usb_ep_free_request(&dcp->ep, req); return; @@ -661,7 +661,7 @@ static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep, { struct usbhsg_request *ureq; - ureq = kzalloc(sizeof *ureq, gfp_flags); + ureq = kzalloc_obj(*ureq, gfp_flags); if (!ureq) return NULL; @@ -1084,15 +1084,11 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) int i; int ret; - gpriv = kzalloc(sizeof(struct usbhsg_gpriv), GFP_KERNEL); + gpriv = kzalloc_flex(*gpriv, uep, pipe_size); if (!gpriv) return -ENOMEM; - uep = kcalloc(pipe_size, sizeof(struct usbhsg_uep), GFP_KERNEL); - if (!uep) { - ret = -ENOMEM; - goto usbhs_mod_gadget_probe_err_gpriv; - } + gpriv->uep_size = pipe_size; gpriv->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_UNDEFINED); dev_info(dev, "%stransceiver found\n", @@ -1115,8 +1111,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) gpriv->mod.name = "gadget"; gpriv->mod.start = usbhsg_start; gpriv->mod.stop = usbhsg_stop; - gpriv->uep = uep; - gpriv->uep_size = pipe_size; usbhsg_status_init(gpriv); /* @@ -1175,9 +1169,6 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) return 0; err_add_udc: - kfree(gpriv->uep); - -usbhs_mod_gadget_probe_err_gpriv: kfree(gpriv); return ret; @@ -1189,6 +1180,5 @@ void usbhs_mod_gadget_remove(struct usbhs_priv *priv) usb_del_gadget_udc(&gpriv->gadget); - kfree(gpriv->uep); kfree(gpriv); } diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index ae54221011c3..f7ef3a9f82a4 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -158,7 +158,7 @@ static struct usbhsh_request *usbhsh_ureq_alloc(struct usbhsh_hpriv *hpriv, { struct usbhsh_request *ureq; - ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags); + ureq = kzalloc_obj(struct usbhsh_request, mem_flags); if (!ureq) return NULL; @@ -374,7 +374,7 @@ static int usbhsh_endpoint_attach(struct usbhsh_hpriv *hpriv, struct usb_endpoint_descriptor *desc = &ep->desc; unsigned long flags; - uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags); + uep = kzalloc_obj(struct usbhsh_ep, mem_flags); if (!uep) return -ENOMEM; diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 75fff2e4cbc6..e4e897e8256f 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -713,11 +713,13 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, /* make sure pipe is not busy */ ret = usbhsp_pipe_barrier(pipe); if (ret < 0) { + usbhsp_put_pipe(pipe); dev_err(dev, "pipe setup failed %d\n", usbhs_pipe_number(pipe)); return NULL; } if (usbhsp_setup_pipecfg(pipe, is_host, dir_in, &pipecfg)) { + usbhsp_put_pipe(pipe); dev_err(dev, "can't setup pipe\n"); return NULL; } @@ -820,8 +822,7 @@ int usbhs_pipe_probe(struct usbhs_priv *priv) return -EINVAL; } - info->pipe = kcalloc(pipe_size, sizeof(struct usbhs_pipe), - GFP_KERNEL); + info->pipe = kzalloc_objs(struct usbhs_pipe, pipe_size); if (!info->pipe) return -ENOMEM; diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index 30482d4cf826..edec139b68b5 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -139,9 +139,14 @@ static void *usb_role_switch_match(const struct fwnode_handle *fwnode, const cha static struct usb_role_switch * usb_role_switch_is_parent(struct fwnode_handle *fwnode) { - struct fwnode_handle *parent = fwnode_get_parent(fwnode); + struct fwnode_handle *parent; struct device *dev; + if (!fwnode_device_is_compatible(fwnode, "usb-b-connector")) + return NULL; + + parent = fwnode_get_parent(fwnode); + if (!fwnode_property_present(parent, "usb-role-switch")) { fwnode_handle_put(parent); return NULL; @@ -364,7 +369,7 @@ usb_role_switch_register(struct device *parent, if (!desc || !desc->set) return ERR_PTR(-EINVAL); - sw = kzalloc(sizeof(*sw), GFP_KERNEL); + sw = kzalloc_obj(*sw); if (!sw) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 800b04fe37fa..d974da43fba3 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -126,7 +126,7 @@ static int ark3116_port_probe(struct usb_serial_port *port) struct usb_serial *serial = port->serial; struct ark3116_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 44f5b58beec9..38ac910b1082 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -114,7 +114,7 @@ static int belkin_sa_port_probe(struct usb_serial_port *port) struct usb_device *dev = port->serial->dev; struct belkin_sa_private *priv; - priv = kmalloc(sizeof(struct belkin_sa_private), GFP_KERNEL); + priv = kmalloc_obj(struct belkin_sa_private); if (!priv) return -ENOMEM; @@ -435,43 +435,39 @@ static int belkin_sa_tiocmset(struct tty_struct *tty, struct belkin_sa_private *priv = usb_get_serial_port_data(port); unsigned long control_state; unsigned long flags; - int retval; - int rts = 0; - int dtr = 0; + int retval = 0; spin_lock_irqsave(&priv->lock, flags); control_state = priv->control_state; - if (set & TIOCM_RTS) { + if (set & TIOCM_RTS) control_state |= TIOCM_RTS; - rts = 1; - } - if (set & TIOCM_DTR) { + if (set & TIOCM_DTR) control_state |= TIOCM_DTR; - dtr = 1; - } - if (clear & TIOCM_RTS) { + if (clear & TIOCM_RTS) control_state &= ~TIOCM_RTS; - rts = 0; - } - if (clear & TIOCM_DTR) { + if (clear & TIOCM_DTR) control_state &= ~TIOCM_DTR; - dtr = 0; - } priv->control_state = control_state; spin_unlock_irqrestore(&priv->lock, flags); - retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, rts); - if (retval < 0) { - dev_err(&port->dev, "Set RTS error %d\n", retval); - goto exit; + if ((set | clear) & TIOCM_RTS) { + retval = BSA_USB_CMD(BELKIN_SA_SET_RTS_REQUEST, + !!(control_state & TIOCM_RTS)); + if (retval < 0) { + dev_err(&port->dev, "Set RTS error %d\n", retval); + goto exit; + } } - retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, dtr); - if (retval < 0) { - dev_err(&port->dev, "Set DTR error %d\n", retval); - goto exit; + if ((set | clear) & TIOCM_DTR) { + retval = BSA_USB_CMD(BELKIN_SA_SET_DTR_REQUEST, + !!(control_state & TIOCM_DTR)); + if (retval < 0) { + dev_err(&port->dev, "Set DTR error %d\n", retval); + goto exit; + } } exit: return retval; diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 2fea1b1db4a2..9e2a18c0b218 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -17,7 +17,7 @@ static int usb_serial_device_match(struct device *dev, const struct device_driver *drv) { const struct usb_serial_port *port = to_usb_serial_port(dev); - struct usb_serial_driver *driver = to_usb_serial_driver(drv); + const struct usb_serial_driver *driver = to_usb_serial_driver(drv); /* * drivers are already assigned to ports in serial_probe so it's diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 7cc36f84821f..569f4aede215 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -381,7 +381,7 @@ static int ch341_port_probe(struct usb_serial_port *port) struct ch341_private *priv; int r; - priv = kzalloc(sizeof(struct ch341_private), GFP_KERNEL); + priv = kzalloc_obj(struct ch341_private); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index c3ea3a46ed76..29f09190846e 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -133,7 +133,7 @@ static int usb_console_setup(struct console *co, char *options) * the termios structure, then later call set_termios to * configure according to command line arguments */ - tty = kzalloc(sizeof(*tty), GFP_KERNEL); + tty = kzalloc_obj(*tty); if (!tty) { retval = -ENOMEM; goto reset_open_count; diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 9960ac2b10b7..333b8b463704 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -1504,7 +1504,7 @@ static int cp210x_gpio_get(struct gpio_chip *gc, unsigned int gpio) return !!(mask & BIT(gpio)); } -static void cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) +static int cp210x_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) { struct usb_serial *serial = gpiochip_get_data(gc); struct cp210x_serial_private *priv = usb_get_serial_data(serial); @@ -1559,7 +1559,10 @@ out: if (result < 0) { dev_err(&serial->interface->dev, "failed to set GPIO value: %d\n", result); + return result; } + + return 0; } static int cp210x_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio) @@ -1599,9 +1602,8 @@ static int cp210x_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio, struct cp210x_serial_private *priv = usb_get_serial_data(serial); priv->gpio_input &= ~BIT(gpio); - cp210x_gpio_set(gc, gpio, value); - return 0; + return cp210x_gpio_set(gc, gpio, value); } static int cp210x_gpio_set_config(struct gpio_chip *gc, unsigned int gpio, @@ -2004,7 +2006,7 @@ static int cp210x_port_probe(struct usb_serial_port *port) struct usb_serial *serial = port->serial; struct cp210x_port_private *port_priv; - port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL); + port_priv = kzalloc_obj(*port_priv); if (!port_priv) return -ENOMEM; @@ -2161,7 +2163,7 @@ static int cp210x_attach(struct usb_serial *serial) int result; struct cp210x_serial_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 76dd8e7453b5..4e8ceb23c27d 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -101,7 +101,7 @@ static int cyberjack_port_probe(struct usb_serial_port *port) struct cyberjack_private *priv; int result; - priv = kmalloc(sizeof(struct cyberjack_private), GFP_KERNEL); + priv = kmalloc_obj(struct cyberjack_private); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index e29569d65991..afff1a0f4298 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -445,7 +445,7 @@ static int cypress_generic_port_probe(struct usb_serial_port *port) return -ENODEV; } - priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL); + priv = kzalloc_obj(struct cypress_private); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index a06485965412..d515df045c4c 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1209,7 +1209,7 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) { struct digi_port *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; @@ -1231,7 +1231,7 @@ static int digi_startup(struct usb_serial *serial) struct digi_serial *serial_priv; int ret; - serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL); + serial_priv = kzalloc_obj(*serial_priv); if (!serial_priv) return -ENOMEM; diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 530b77fc2f78..9262a2ac97f5 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -70,7 +70,6 @@ MODULE_DEVICE_TABLE(usb, combined_id_table); #define F81232_REGISTER_REQUEST 0xa0 #define F81232_GET_REGISTER 0xc0 #define F81232_SET_REGISTER 0x40 -#define F81534A_ACCESS_REG_RETRY 2 #define SERIAL_BASE_ADDRESS 0x0120 #define RECEIVE_BUFFER_REGISTER (0x00 + SERIAL_BASE_ADDRESS) @@ -824,36 +823,31 @@ static void f81232_lsr_worker(struct work_struct *work) static int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg, u16 size, void *val) { - struct usb_device *dev = interface_to_usbdev(intf); - int retry = F81534A_ACCESS_REG_RETRY; - int status; - - while (retry--) { - status = usb_control_msg_send(dev, - 0, - F81232_REGISTER_REQUEST, - F81232_SET_REGISTER, - reg, - 0, - val, - size, - USB_CTRL_SET_TIMEOUT, - GFP_KERNEL); - if (status) { - status = usb_translate_errors(status); - if (status == -EIO) - continue; - } - - break; - } - - if (status) { - dev_err(&intf->dev, "failed to set register 0x%x: %d\n", - reg, status); - } + return usb_control_msg_send(interface_to_usbdev(intf), + 0, + F81232_REGISTER_REQUEST, + F81232_SET_REGISTER, + reg, + 0, + val, + size, + USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); +} - return status; +static int f81534a_ctrl_get_register(struct usb_interface *intf, u16 reg, + u16 size, void *val) +{ + return usb_control_msg_recv(interface_to_usbdev(intf), + 0, + F81232_REGISTER_REQUEST, + F81232_GET_REGISTER, + reg, + 0, + val, + size, + USB_CTRL_GET_TIMEOUT, + GFP_KERNEL); } static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en) @@ -869,6 +863,29 @@ static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en) * bit 0~11 : Serial port enable bit. */ if (en) { + /* + * The Fintek F81532A/534A/535/536 family relies on the + * F81534A_CTRL_CMD_ENABLE_PORT (116h) register during + * initialization to both determine serial port status and + * control port creation. + * + * If the driver experiences fast load/unload cycles, the + * device state may becomes unstable, resulting in the + * incomplete generation of serial ports. + * + * Performing a dummy read operation on the register prior + * to the initial write command resolves the issue. + * + * This clears the device's stale internal state. Subsequent + * write operations will correctly generate all serial ports. + */ + status = f81534a_ctrl_get_register(intf, + F81534A_CTRL_CMD_ENABLE_PORT, + sizeof(enable), + enable); + if (status) + return status; + enable[0] = 0xff; enable[1] = 0x8f; } diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 9b34e23b7091..af14548fa03d 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -107,26 +107,24 @@ struct ftdi_quirk { }; static int ftdi_jtag_probe(struct usb_serial *serial); -static int ftdi_NDI_device_setup(struct usb_serial *serial); static int ftdi_stmclite_probe(struct usb_serial *serial); static int ftdi_8u2232c_probe(struct usb_serial *serial); -static void ftdi_USB_UIRT_setup(struct ftdi_private *priv); -static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv); +static void ftdi_usb_uirt_setup(struct ftdi_private *priv); +static void ftdi_he_tira1_setup(struct ftdi_private *priv); static const struct ftdi_quirk ftdi_jtag_quirk = { .probe = ftdi_jtag_probe, }; -static const struct ftdi_quirk ftdi_NDI_device_quirk = { - .probe = ftdi_NDI_device_setup, +static const struct ftdi_quirk ftdi_ndi_quirk = { }; -static const struct ftdi_quirk ftdi_USB_UIRT_quirk = { - .port_probe = ftdi_USB_UIRT_setup, +static const struct ftdi_quirk ftdi_usb_uirt_quirk = { + .port_probe = ftdi_usb_uirt_setup, }; -static const struct ftdi_quirk ftdi_HE_TIRA1_quirk = { - .port_probe = ftdi_HE_TIRA1_setup, +static const struct ftdi_quirk ftdi_he_tira1_quirk = { + .port_probe = ftdi_he_tira1_setup, }; static const struct ftdi_quirk ftdi_stmclite_quirk = { @@ -590,9 +588,9 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(OCT_VID, OCT_US101_PID) }, { USB_DEVICE(OCT_VID, OCT_DK201_PID) }, { USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID), - .driver_info = (kernel_ulong_t)&ftdi_HE_TIRA1_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_he_tira1_quirk }, { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID), - .driver_info = (kernel_ulong_t)&ftdi_USB_UIRT_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_usb_uirt_quirk }, { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) }, { USB_DEVICE(FTDI_VID, PROTEGO_R2X0) }, { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) }, @@ -628,10 +626,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_TIAO_UMPA_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_TIAO_UMPA_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_NT_ORIONLXM_PID, 1) }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLX_PLUS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORION_IO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONMX_PID) }, @@ -794,15 +790,17 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID), - .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, + { USB_DEVICE(FTDI_NDI_VID, FTDI_NDI_EMGUIDE_GEMINI_PID), + .driver_info = (kernel_ulong_t)&ftdi_ndi_quirk }, { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, { USB_DEVICE(NOVITUS_VID, NOVITUS_BONO_E_PID) }, { USB_DEVICE(FTDI_VID, RTSYSTEMS_USB_VX8_PID) }, @@ -840,24 +838,18 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) }, - { USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, CYBER_CORTEX_AV_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_PID, 1) }, { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_H_PID, 1) }, - { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FIC_VID, FIC_NEO1973_DEBUG_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_OOCDLINK_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_DEVEL_BOARD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID, 1) }, + { USB_DEVICE(FTDI_VID, FTDI_AXE027_PID) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_TURTELIZER_PID, 1) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_SCU18) }, { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, @@ -899,17 +891,14 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(ATMEL_VID, STK541_PID) }, { USB_DEVICE(DE_VID, STB_PID) }, { USB_DEVICE(DE_VID, WHT_PID) }, - { USB_DEVICE(ADI_VID, ADI_GNICE_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(ADI_VID, ADI_GNICE_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(ADI_VID, ADI_GNICEPLUS_PID, 1) }, { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID, USB_CLASS_VENDOR_SPEC, USB_SUBCLASS_VENDOR_SPEC, 0x00) }, { USB_DEVICE_INTERFACE_NUMBER(ACTEL_VID, MICROSEMI_ARROW_SF2PLUS_BOARD_PID, 2) }, { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, - { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(MARVELL_VID, MARVELL_SHEEVAPLUG_PID, 1) }, { USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) }, { USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) }, { USB_DEVICE(FTDI_VID, PI_C865_PID) }, @@ -932,10 +921,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(PI_VID, PI_1016_PID) }, { USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) }, { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) }, - { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, TI_XDS100V2_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, MARVELL_OPENRD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, TI_XDS100V2_PID, 1) }, { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) }, { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) }, @@ -944,18 +931,14 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) }, { USB_DEVICE(FTDI_VID, MJSG_HD_RADIO_PID) }, { USB_DEVICE(FTDI_VID, MJSG_XM_RADIO_PID) }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_ST_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_ST_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_SLITE_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, XVERVE_SIGNALYZER_SH2_PID, 1) }, { USB_DEVICE(FTDI_VID, XVERVE_SIGNALYZER_SH4_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, SEGWAY_RMP200_PID) }, { USB_DEVICE(FTDI_VID, ACCESIO_COM4SM_PID) }, - { USB_DEVICE(IONICS_VID, IONICS_PLUGCOMPUTER_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(IONICS_VID, IONICS_PLUGCOMPUTER_PID, 1) }, { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_24_MASTER_WING_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_PC_WING_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_USB_DMX_PID) }, @@ -970,15 +953,12 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, { USB_DEVICE(FTDI_VID, FTDI_FHE_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, - { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(ST_VID, ST_STMCLT_2232_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(ST_VID, ST_STMCLT_2232_PID, 1) }, { USB_DEVICE(ST_VID, ST_STMCLT_4232_PID), .driver_info = (kernel_ulong_t)&ftdi_stmclite_quirk }, { USB_DEVICE(FTDI_VID, FTDI_RF_R106) }, - { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID, 1) }, { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) }, /* Crucible Devices */ { USB_DEVICE(FTDI_VID, FTDI_CT_COMET_PID) }, @@ -1053,8 +1033,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) }, { USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) }, { USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) }, - { USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(TI_VID, TI_CC3200_LAUNCHPAD_PID, 1) }, { USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) }, { USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) }, { USB_DEVICE(AIRBUS_DS_VID, AIRBUS_DS_P8GR) }, @@ -1072,11 +1051,10 @@ static const struct usb_device_id id_table_combined[] = { /* U-Blox devices */ { USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ZED_PID) }, { USB_DEVICE(UBLOX_VID, UBLOX_C099F9P_ODIN_PID) }, + { USB_DEVICE_INTERFACE_NUMBER(UBLOX_VID, UBLOX_EVK_M101_PID, 2) }, /* FreeCalypso USB adapters */ - { USB_DEVICE(FTDI_VID, FTDI_FALCONIA_JTAG_BUF_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(FTDI_VID, FTDI_FALCONIA_JTAG_UNBUF_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_FALCONIA_JTAG_BUF_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(FTDI_VID, FTDI_FALCONIA_JTAG_UNBUF_PID, 1) }, /* GMC devices */ { USB_DEVICE(GMC_VID, GMC_Z216C_PID) }, /* Altera USB Blaster 3 */ @@ -1093,6 +1071,8 @@ static const struct usb_device_id id_table_combined[] = { { 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) }, + /* Abacus Electrics */ + { USB_DEVICE(FTDI_VID, ABACUS_OPTICAL_PROBE_PID) }, { } /* Terminating entry */ }; @@ -1266,6 +1246,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, static u32 get_ftdi_divisor(struct tty_struct *tty, struct usb_serial_port *port) { + const struct ftdi_quirk *quirk = usb_get_serial_data(port->serial); struct ftdi_private *priv = usb_get_serial_port_data(port); struct device *dev = &port->dev; u32 div_value = 0; @@ -1325,16 +1306,8 @@ static u32 get_ftdi_divisor(struct tty_struct *tty, case FT232R: case FTX: if (baud <= 3000000) { - u16 product_id = le16_to_cpu( - port->serial->dev->descriptor.idProduct); - if (((product_id == FTDI_NDI_HUC_PID) || - (product_id == FTDI_NDI_SPECTRA_SCU_PID) || - (product_id == FTDI_NDI_FUTURE_2_PID) || - (product_id == FTDI_NDI_FUTURE_3_PID) || - (product_id == FTDI_NDI_AURORA_SCU_PID)) && - (baud == 19200)) { + if (quirk == &ftdi_ndi_quirk && baud == 19200) baud = 1200000; - } div_value = ftdi_232bm_baud_to_divisor(baud); } else { dev_dbg(dev, "%s - Baud rate too high!\n", __func__); @@ -1855,10 +1828,11 @@ static int ftdi_gpio_get(struct gpio_chip *gc, unsigned int gpio) return !!(result & BIT(gpio)); } -static void ftdi_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) +static int ftdi_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) { struct usb_serial_port *port = gpiochip_get_data(gc); struct ftdi_private *priv = usb_get_serial_port_data(port); + int result; mutex_lock(&priv->gpio_lock); @@ -1867,9 +1841,11 @@ static void ftdi_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) else priv->gpio_value &= ~BIT(gpio); - ftdi_set_cbus_pins(port); + result = ftdi_set_cbus_pins(port); mutex_unlock(&priv->gpio_lock); + + return result; } static int ftdi_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask, @@ -1887,19 +1863,22 @@ static int ftdi_gpio_get_multiple(struct gpio_chip *gc, unsigned long *mask, return 0; } -static void ftdi_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, +static int ftdi_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask, unsigned long *bits) { struct usb_serial_port *port = gpiochip_get_data(gc); struct ftdi_private *priv = usb_get_serial_port_data(port); + int result; mutex_lock(&priv->gpio_lock); priv->gpio_value &= ~(*mask); priv->gpio_value |= *bits & *mask; - ftdi_set_cbus_pins(port); + result = ftdi_set_cbus_pins(port); mutex_unlock(&priv->gpio_lock); + + return result; } static int ftdi_gpio_direction_get(struct gpio_chip *gc, unsigned int gpio) @@ -2209,7 +2188,7 @@ static int ftdi_port_probe(struct usb_serial_port *port) struct ftdi_private *priv; int result; - priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL); + priv = kzalloc_obj(struct ftdi_private); if (!priv) return -ENOMEM; @@ -2225,7 +2204,9 @@ static int ftdi_port_probe(struct usb_serial_port *port) goto err_free; ftdi_set_max_packet_size(port); - if (read_latency_timer(port) < 0) + if (quirk == &ftdi_ndi_quirk) + priv->latency = 1; + else if (read_latency_timer(port) < 0) priv->latency = 16; write_latency_timer(port); @@ -2244,20 +2225,22 @@ err_free: return result; } -/* Setup for the USB-UIRT device, which requires hardwired - * baudrate (38400 gets mapped to 312500) */ -/* Called from usbserial:serial_probe */ -static void ftdi_USB_UIRT_setup(struct ftdi_private *priv) +/* + * Setup for the USB-UIRT device, which requires hardwired baudrate + * (38400 gets mapped to 312500). + */ +static void ftdi_usb_uirt_setup(struct ftdi_private *priv) { priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 77; priv->force_baud = 38400; } -/* Setup for the HE-TIRA1 device, which requires hardwired - * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled. */ - -static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) +/* + * Setup for the HE-TIRA1 device, which requires hardwired baudrate + * (38400 gets mapped to 100000) and RTS-CTS enabled. + */ +static void ftdi_he_tira1_setup(struct ftdi_private *priv) { priv->flags |= ASYNC_SPD_CUST; priv->custom_divisor = 240; @@ -2266,39 +2249,6 @@ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) } /* - * Module parameter to control latency timer for NDI FTDI-based USB devices. - * If this value is not set in /etc/modprobe.d/ its value will be set - * to 1ms. - */ -static int ndi_latency_timer = 1; - -/* Setup for the NDI FTDI-based USB devices, which requires hardwired - * baudrate (19200 gets mapped to 1200000). - * - * Called from usbserial:serial_probe. - */ -static int ftdi_NDI_device_setup(struct usb_serial *serial) -{ - struct usb_device *udev = serial->dev; - int latency = ndi_latency_timer; - - if (latency == 0) - latency = 1; - if (latency > 99) - latency = 99; - - dev_dbg(&udev->dev, "%s setting NDI device latency to %d\n", __func__, latency); - dev_info(&udev->dev, "NDI device with a latency value of %d\n", latency); - - /* FIXME: errors are not returned */ - usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - FTDI_SIO_SET_LATENCY_TIMER_REQUEST, - FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE, - latency, 0, NULL, 0, WDR_TIMEOUT); - return 0; -} - -/* * First port on JTAG adaptors such as Olimex arm-usb-ocd or the FIC/OpenMoko * Neo1973 Debug Board is reserved for JTAG interface and can be accessed from * userspace using openocd. @@ -2308,26 +2258,29 @@ static int ftdi_jtag_probe(struct usb_serial *serial) struct usb_interface *intf = serial->interface; int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - if (ifnum == 0) { - dev_info(&intf->dev, "Ignoring interface reserved for JTAG\n"); + if (ifnum == 0) return -ENODEV; - } return 0; } static int ftdi_8u2232c_probe(struct usb_serial *serial) { + struct usb_interface *intf = serial->interface; struct usb_device *udev = serial->dev; + int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - if (udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) - return ftdi_jtag_probe(serial); + if (ifnum == 0) { + if (udev->manufacturer && + !strcmp(udev->manufacturer, "CALAO Systems")) + return -ENODEV; - if (udev->product && - (!strcmp(udev->product, "Arrow USB Blaster") || - !strcmp(udev->product, "BeagleBone/XDS100V2") || - !strcmp(udev->product, "SNAP Connect E10"))) - return ftdi_jtag_probe(serial); + if (udev->product && + (!strcmp(udev->product, "Arrow USB Blaster") || + !strcmp(udev->product, "BeagleBone/XDS100V2") || + !strcmp(udev->product, "SNAP Connect E10"))) + return -ENODEV; + } return 0; } @@ -2344,10 +2297,8 @@ static int ftdi_stmclite_probe(struct usb_serial *serial) struct usb_interface *intf = serial->interface; int ifnum = intf->cur_altsetting->desc.bInterfaceNumber; - if (ifnum < 2) { - dev_info(&intf->dev, "Ignoring interface reserved for JTAG\n"); + if (ifnum < 2) return -ENODEV; - } return 0; } @@ -2924,6 +2875,3 @@ module_usb_serial_driver(serial_drivers, id_table_combined); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); - -module_param(ndi_latency_timer, int, 0644); -MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override"); diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 52be47d684ea..6c76cfebfd0e 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -96,6 +96,8 @@ #define LMI_LM3S_EVAL_BOARD_PID 0xbcd9 #define LMI_LM3S_ICDI_BOARD_PID 0xbcda +#define FTDI_AXE027_PID 0xBD90 /* PICAXE AXE027 USB download cable */ + #define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */ /* OpenDCC (www.opendcc.de) product id */ @@ -204,6 +206,9 @@ #define FTDI_NDI_FUTURE_3_PID 0xDA73 /* NDI future device #3 */ #define FTDI_NDI_AURORA_SCU_PID 0xDA74 /* NDI Aurora SCU */ +#define FTDI_NDI_VID 0x23F2 +#define FTDI_NDI_EMGUIDE_GEMINI_PID 0x0003 /* NDI Emguide Gemini */ + /* * ChamSys Limited (www.chamsys.co.uk) USB wing/interface product IDs */ @@ -443,6 +448,11 @@ #define LINX_FUTURE_2_PID 0xF44C /* Linx future device */ /* + * Abacus Electrics + */ +#define ABACUS_OPTICAL_PROBE_PID 0xf458 /* ABACUS ELECTRICS Optical Probe */ + +/* * Oceanic product ids */ #define FTDI_OCEANIC_PID 0xF460 /* Oceanic dive instrument */ @@ -1606,6 +1616,7 @@ #define UBLOX_VID 0x1546 #define UBLOX_C099F9P_ZED_PID 0x0502 #define UBLOX_C099F9P_ODIN_PID 0x0503 +#define UBLOX_EVK_M101_PID 0x0506 /* * GMC devices diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index b97ba8ed6801..7205483a0115 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -267,7 +267,7 @@ static int pkt_add(struct garmin_data *garmin_data_p, /* process only packets containing data ... */ if (data_length) { - pkt = kmalloc(struct_size(pkt, data, data_length), GFP_ATOMIC); + pkt = kmalloc_flex(*pkt, data, data_length, GFP_ATOMIC); if (!pkt) return 0; @@ -1357,7 +1357,8 @@ static void garmin_unthrottle(struct tty_struct *tty) */ static void timeout_handler(struct timer_list *t) { - struct garmin_data *garmin_data_p = from_timer(garmin_data_p, t, timer); + struct garmin_data *garmin_data_p = timer_container_of(garmin_data_p, + t, timer); /* send the next queued packet to the tty port */ if (garmin_data_p->mode == MODE_NATIVE) @@ -1372,7 +1373,7 @@ static int garmin_port_probe(struct usb_serial_port *port) int status; struct garmin_data *garmin_data_p; - garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL); + garmin_data_p = kzalloc_obj(struct garmin_data); if (!garmin_data_p) return -ENOMEM; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 1fffda7647f9..3f5889145e51 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -73,6 +73,7 @@ static const struct usb_device_id edgeport_4port_id_table[] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_22I) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_4) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_COMPATIBLE) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BLACKBOX_IC135A) }, { } }; @@ -121,6 +122,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8R) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_8RR) }, { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_EDGEPORT_412_8) }, + { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_BLACKBOX_IC135A) }, { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0202) }, { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0203) }, { USB_DEVICE(USB_VENDOR_ID_NCR, NCR_DEVICE_ID_EPIC_0310) }, @@ -470,6 +472,7 @@ static void get_product_info(struct edgeport_serial *edge_serial) case ION_DEVICE_ID_EDGEPORT_2_DIN: case ION_DEVICE_ID_EDGEPORT_4_DIN: case ION_DEVICE_ID_EDGEPORT_16_DUAL_CPU: + case ION_DEVICE_ID_BLACKBOX_IC135A: product_info->IsRS232 = 1; break; @@ -498,7 +501,7 @@ static int get_epic_descriptor(struct edgeport_serial *ep) ep->is_epic = 0; - epic = kmalloc(sizeof(*epic), GFP_KERNEL); + epic = kmalloc_obj(*epic); if (!epic) return -ENOMEM; @@ -2714,7 +2717,7 @@ static int edge_startup(struct usb_serial *serial) dev = serial->dev; /* create our private serial structure */ - edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); + edge_serial = kzalloc_obj(struct edgeport_serial); if (!edge_serial) return -ENOMEM; @@ -2956,7 +2959,7 @@ static int edge_port_probe(struct usb_serial_port *port) { struct edgeport_port *edge_port; - edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL); + edge_port = kzalloc_obj(*edge_port); if (!edge_port) return -ENOMEM; diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 7d0584b2a234..cb55370e036f 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -528,7 +528,7 @@ static int tx_active(struct edgeport_port *port) u8 *lsr; int bytes_left = 0; - oedb = kmalloc(sizeof(*oedb), GFP_KERNEL); + oedb = kmalloc_obj(*oedb); if (!oedb) return -ENOMEM; @@ -680,7 +680,7 @@ static int check_i2c_image(struct edgeport_serial *serial) u8 *buffer; u16 ttype; - rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); + rom_desc = kmalloc_obj(*rom_desc); if (!rom_desc) return -ENOMEM; @@ -759,7 +759,7 @@ static int get_manuf_info(struct edgeport_serial *serial, u8 *buffer) struct edge_ti_manuf_descriptor *desc; struct device *dev = &serial->serial->dev->dev; - rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); + rom_desc = kmalloc_obj(*rom_desc); if (!rom_desc) return -ENOMEM; @@ -1089,7 +1089,7 @@ static int do_download_mode(struct edgeport_serial *serial, * Validate Hardware version number * Read Manufacturing Descriptor from TI Based Edgeport */ - ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); + ti_manuf_desc = kmalloc_obj(*ti_manuf_desc); if (!ti_manuf_desc) return -ENOMEM; @@ -1107,7 +1107,7 @@ static int do_download_mode(struct edgeport_serial *serial, return -EINVAL; } - rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); + rom_desc = kmalloc_obj(*rom_desc); if (!rom_desc) { kfree(ti_manuf_desc); return -ENOMEM; @@ -1123,8 +1123,7 @@ static int do_download_mode(struct edgeport_serial *serial, dev_dbg(dev, "%s - Found Type FIRMWARE (Type 2) record\n", __func__); - firmware_version = kmalloc(sizeof(*firmware_version), - GFP_KERNEL); + firmware_version = kmalloc_obj(*firmware_version); if (!firmware_version) { kfree(rom_desc); kfree(ti_manuf_desc); @@ -1419,7 +1418,7 @@ static int do_boot_mode(struct edgeport_serial *serial, * Validate Hardware version number * Read Manufacturing Descriptor from TI Based Edgeport */ - ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); + ti_manuf_desc = kmalloc_obj(*ti_manuf_desc); if (!ti_manuf_desc) return -ENOMEM; @@ -2219,7 +2218,7 @@ static void change_port_settings(struct tty_struct *tty, unsigned cflag; int status; - config = kmalloc (sizeof (*config), GFP_KERNEL); + config = kmalloc_obj(*config); if (!config) { tty->termios = *old_termios; return; @@ -2458,7 +2457,7 @@ static void edge_heartbeat_work(struct work_struct *work) serial = container_of(work, struct edgeport_serial, heartbeat_work.work); - rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); + rom_desc = kmalloc_obj(*rom_desc); /* Descriptor address request is enough to reset the firmware timer */ if (!rom_desc || !get_descriptor_addr(serial, I2C_DESC_TYPE_ION, @@ -2497,7 +2496,7 @@ static int edge_startup(struct usb_serial *serial) u16 product_id; /* create our private serial structure */ - edge_serial = kzalloc(sizeof(struct edgeport_serial), GFP_KERNEL); + edge_serial = kzalloc_obj(struct edgeport_serial); if (!edge_serial) return -ENOMEM; @@ -2551,7 +2550,7 @@ static int edge_port_probe(struct usb_serial_port *port) struct edgeport_port *edge_port; int ret; - edge_port = kzalloc(sizeof(*edge_port), GFP_KERNEL); + edge_port = kzalloc_obj(*edge_port); if (!edge_port) return -ENOMEM; diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index 9a6f742ad3ab..c82a275e8e76 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -211,6 +211,7 @@ // // Definitions for other product IDs +#define ION_DEVICE_ID_BLACKBOX_IC135A 0x0801 // OEM device (rebranded Edgeport/4) #define ION_DEVICE_ID_MT4X56USB 0x1403 // OEM device #define ION_DEVICE_ID_E5805A 0x1A01 // OEM device (rebranded Edgeport/4) diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index b1e672c2f423..83709d678b3a 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -198,7 +198,7 @@ static int ipw_attach(struct usb_serial *serial) { struct usb_wwan_intf_private *data; - data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); + data = kzalloc_obj(struct usb_wwan_intf_private); if (!data) return -ENOMEM; diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index a106b71e698f..12e928d25ba1 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -126,7 +126,7 @@ irda_usb_find_class_desc(struct usb_serial *serial, unsigned int ifnum) struct usb_irda_cs_descriptor *desc; int ret; - desc = kzalloc(sizeof(*desc), GFP_KERNEL); + desc = kzalloc_obj(*desc); if (!desc) return NULL; diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index c21dcc9b6f05..0ca111b111c7 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -6,7 +6,7 @@ * Copyright (C) 2007 Alain Degreffe (eczema@ecze.com) * - * Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás) + * Original code taken from iuutool (Copyright (C) 2006 Juan Carlos Borrás) * * And tested with help of WB Electronics */ @@ -67,7 +67,7 @@ static int iuu_port_probe(struct usb_serial_port *port) struct iuu_private *priv; int ret; - priv = kzalloc(sizeof(struct iuu_private), GFP_KERNEL); + priv = kzalloc_obj(struct iuu_private); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 9129e0282c24..46448843541a 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -2789,7 +2789,7 @@ static int keyspan_startup(struct usb_serial *serial) } /* Setup private data for serial driver */ - s_priv = kzalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL); + s_priv = kzalloc_obj(struct keyspan_serial_private); if (!s_priv) return -ENOMEM; @@ -2886,7 +2886,7 @@ static int keyspan_port_probe(struct usb_serial_port *port) s_priv = usb_get_serial_data(serial); d_details = s_priv->device_details; - p_priv = kzalloc(sizeof(*p_priv), GFP_KERNEL); + p_priv = kzalloc_obj(*p_priv); if (!p_priv) return -ENOMEM; diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index e98b479593d3..5036600dd334 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -654,7 +654,7 @@ static int keyspan_pda_port_probe(struct usb_serial_port *port) struct keyspan_pda_private *priv; - priv = kmalloc(sizeof(struct keyspan_pda_private), GFP_KERNEL); + priv = kmalloc_obj(struct keyspan_pda_private); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index d36155b6d2bf..ed8531a64768 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -189,7 +189,7 @@ static int klsi_105_port_probe(struct usb_serial_port *port) { struct klsi_105_private *priv; - priv = kmalloc(sizeof(*priv), GFP_KERNEL); + priv = kmalloc_obj(*priv); if (!priv) return -ENOMEM; @@ -378,7 +378,7 @@ static void klsi_105_set_termios(struct tty_struct *tty, unsigned long flags; speed_t baud; - cfg = kmalloc(sizeof(*cfg), GFP_KERNEL); + cfg = kmalloc_obj(*cfg); if (!cfg) return; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 464433be2034..6126afd67a7b 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -109,13 +109,28 @@ struct kobil_private { __u16 device_type; }; +static int kobil_ctrl_send(struct usb_serial_port *port, u8 req, u16 val) +{ + return usb_control_msg(port->serial->dev, + usb_sndctrlpipe(port->serial->dev, 0), + req, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, + val, 0, NULL, 0, KOBIL_TIMEOUT); +} + +static int kobil_ctrl_recv(struct usb_serial_port *port, u8 req, u16 val, void *buf, u16 size) +{ + return usb_control_msg(port->serial->dev, + usb_rcvctrlpipe(port->serial->dev, 0), + req, USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, + val, 0, buf, size, KOBIL_TIMEOUT); +} static int kobil_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct kobil_private *priv; - priv = kmalloc(sizeof(struct kobil_private), GFP_KERNEL); + priv = kmalloc_obj(struct kobil_private); if (!priv) return -ENOMEM; @@ -163,10 +178,10 @@ static void kobil_init_termios(struct tty_struct *tty) static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) { struct device *dev = &port->dev; - int result = 0; struct kobil_private *priv; unsigned char *transfer_buffer; int transfer_buffer_length = 8; + int result; priv = usb_get_serial_port_data(port); @@ -176,16 +191,8 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) return -ENOMEM; /* get hardware version */ - result = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - SUSBCRequest_GetMisc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - SUSBCR_MSC_GetHWVersion, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_recv(port, SUSBCRequest_GetMisc, SUSBCR_MSC_GetHWVersion, + transfer_buffer, transfer_buffer_length); dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result); if (result >= 3) { dev_dbg(dev, "Hardware version: %i.%i.%i\n", transfer_buffer[0], @@ -193,16 +200,8 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) } /* get firmware version */ - result = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - SUSBCRequest_GetMisc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - SUSBCR_MSC_GetFWVersion, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_recv(port, SUSBCRequest_GetMisc, SUSBCR_MSC_GetFWVersion, + transfer_buffer, transfer_buffer_length); dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result); if (result >= 3) { dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0], @@ -212,35 +211,17 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { /* Setting Baudrate, Parity and Stopbits */ - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetBaudRateParityAndStopBits, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | - SUSBCR_SPASB_1StopBit, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_send(port, SUSBCRequest_SetBaudRateParityAndStopBits, + SUSBCR_SBR_9600 | SUSBCR_SPASB_EvenParity | SUSBCR_SPASB_1StopBit); dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result); /* reset all queues */ - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_Misc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_MSC_ResetAllQueues, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_send(port, SUSBCRequest_Misc, SUSBCR_MSC_ResetAllQueues); dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result); } if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || - priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || - priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { /* start reading (Adapter B 'cause PNP string) */ result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result); @@ -291,10 +272,8 @@ static void kobil_write_int_callback(struct urb *urb) static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count) { - int length = 0; - int result = 0; - int todo = 0; struct kobil_private *priv; + int length, todo, result; if (count == 0) { dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__); @@ -318,9 +297,10 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, if (((priv->device_type != KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 2) && (priv->filled >= (priv->buf[1] + 3))) || ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) && (priv->filled > 3) && (priv->filled >= (priv->buf[2] + 4)))) { /* stop reading (except TWIN and KAAN SIM) */ - if ((priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) - || (priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID)) + if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || + priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { usb_kill_urb(port->interrupt_in_urb); + } todo = priv->filled - priv->cur_pos; @@ -347,7 +327,7 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, /* start reading (except TWIN and KAAN SIM) */ if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID || - priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { + priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) { result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result); @@ -373,8 +353,8 @@ static int kobil_tiocmget(struct tty_struct *tty) int transfer_buffer_length = 8; priv = usb_get_serial_port_data(port); - if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID - || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { /* This device doesn't support ioctl calls */ return -EINVAL; } @@ -384,16 +364,8 @@ static int kobil_tiocmget(struct tty_struct *tty) if (!transfer_buffer) return -ENOMEM; - result = usb_control_msg(port->serial->dev, - usb_rcvctrlpipe(port->serial->dev, 0), - SUSBCRequest_GetStatusLineState, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_IN, - 0, - 0, - transfer_buffer, - transfer_buffer_length, - KOBIL_TIMEOUT); - + result = kobil_ctrl_recv(port, SUSBCRequest_GetStatusLineState, 0, + transfer_buffer, transfer_buffer_length); dev_dbg(&port->dev, "Send get_status_line_state URB returns: %i\n", result); if (result < 1) { @@ -418,58 +390,41 @@ static int kobil_tiocmset(struct tty_struct *tty, struct usb_serial_port *port = tty->driver_data; struct device *dev = &port->dev; struct kobil_private *priv; + int dtr, rts; int result; - int dtr = 0; - int rts = 0; + u16 val = 0; - /* FIXME: locking ? */ priv = usb_get_serial_port_data(port); - if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID - || priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { + if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || + priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) { /* This device doesn't support ioctl calls */ return -EINVAL; } - if (set & TIOCM_RTS) - rts = 1; - if (set & TIOCM_DTR) - dtr = 1; - if (clear & TIOCM_RTS) - rts = 0; - if (clear & TIOCM_DTR) - dtr = 0; - - if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) { - if (dtr != 0) - dev_dbg(dev, "%s - Setting DTR\n", __func__); + dtr = (set | clear) & TIOCM_DTR; + rts = (set | clear) & TIOCM_RTS; + + if (dtr && priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) { + if (set & TIOCM_DTR) + val = SUSBCR_SSL_SETDTR; else - dev_dbg(dev, "%s - Clearing DTR\n", __func__); - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetStatusLinesOrQueues, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - ((dtr != 0) ? SUSBCR_SSL_SETDTR : SUSBCR_SSL_CLRDTR), - 0, - NULL, - 0, - KOBIL_TIMEOUT); - } else { - if (rts != 0) - dev_dbg(dev, "%s - Setting RTS\n", __func__); + val = SUSBCR_SSL_CLRDTR; + } else if (rts) { + if (set & TIOCM_RTS) + val = SUSBCR_SSL_SETRTS; else - dev_dbg(dev, "%s - Clearing RTS\n", __func__); - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetStatusLinesOrQueues, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - ((rts != 0) ? SUSBCR_SSL_SETRTS : SUSBCR_SSL_CLRRTS), - 0, - NULL, - 0, - KOBIL_TIMEOUT); + val = SUSBCR_SSL_CLRRTS; } - dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result); - return (result < 0) ? result : 0; + + if (val) { + result = kobil_ctrl_send(port, SUSBCRequest_SetStatusLinesOrQueues, val); + if (result < 0) { + dev_err(dev, "failed to set status lines: %d\n", result); + return result; + } + } + + return 0; } static void kobil_set_termios(struct tty_struct *tty, @@ -478,9 +433,9 @@ static void kobil_set_termios(struct tty_struct *tty, { struct kobil_private *priv; int result; - unsigned short urb_val = 0; int c_cflag = tty->termios.c_cflag; speed_t speed; + u16 val; priv = usb_get_serial_port_data(port); if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID || @@ -493,37 +448,34 @@ static void kobil_set_termios(struct tty_struct *tty, speed = tty_get_baud_rate(tty); switch (speed) { case 1200: - urb_val = SUSBCR_SBR_1200; + val = SUSBCR_SBR_1200; break; default: speed = 9600; fallthrough; case 9600: - urb_val = SUSBCR_SBR_9600; + val = SUSBCR_SBR_9600; break; } - urb_val |= (c_cflag & CSTOPB) ? SUSBCR_SPASB_2StopBits : - SUSBCR_SPASB_1StopBit; + + if (c_cflag & CSTOPB) + val |= SUSBCR_SPASB_2StopBits; + else + val |= SUSBCR_SPASB_1StopBit; + if (c_cflag & PARENB) { if (c_cflag & PARODD) - urb_val |= SUSBCR_SPASB_OddParity; + val |= SUSBCR_SPASB_OddParity; else - urb_val |= SUSBCR_SPASB_EvenParity; - } else - urb_val |= SUSBCR_SPASB_NoParity; + val |= SUSBCR_SPASB_EvenParity; + } else { + val |= SUSBCR_SPASB_NoParity; + } + tty->termios.c_cflag &= ~CMSPAR; tty_encode_baud_rate(tty, speed, speed); - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_SetBaudRateParityAndStopBits, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - urb_val, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); + result = kobil_ctrl_send(port, SUSBCRequest_SetBaudRateParityAndStopBits, val); if (result) { dev_err(&port->dev, "failed to update line settings: %d\n", result); @@ -544,17 +496,7 @@ static int kobil_ioctl(struct tty_struct *tty, switch (cmd) { case TCFLSH: - result = usb_control_msg(port->serial->dev, - usb_sndctrlpipe(port->serial->dev, 0), - SUSBCRequest_Misc, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT | USB_DIR_OUT, - SUSBCR_MSC_ResetAllQueues, - 0, - NULL, - 0, - KOBIL_TIMEOUT - ); - + result = kobil_ctrl_send(port, SUSBCRequest_Misc, SUSBCR_MSC_ResetAllQueues); dev_dbg(&port->dev, "%s - Send reset_all_queues (FLUSH) URB returns: %i\n", __func__, result); diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 2bce8cc03aca..18844b92bd08 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -385,7 +385,7 @@ static int mct_u232_port_probe(struct usb_serial_port *port) return -ENODEV; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 028878292901..bc834cc48550 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -245,7 +245,7 @@ static int metrousb_port_probe(struct usb_serial_port *port) { struct metrousb_private *metro_priv; - metro_priv = kzalloc(sizeof(*metro_priv), GFP_KERNEL); + metro_priv = kzalloc_obj(*metro_priv); if (!metro_priv) return -ENOMEM; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index e59bfa7c8030..94459408e7fb 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -555,7 +555,7 @@ static int mos7715_parport_init(struct usb_serial *serial) struct mos7715_parport *mos_parport; /* allocate and initialize parallel port control struct */ - mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL); + mos_parport = kzalloc_obj(struct mos7715_parport); if (!mos_parport) return -ENOMEM; @@ -1703,7 +1703,7 @@ static int mos7720_port_probe(struct usb_serial_port *port) { struct moschip_port *mos7720_port; - mos7720_port = kzalloc(sizeof(*mos7720_port), GFP_KERNEL); + mos7720_port = kzalloc_obj(*mos7720_port); if (!mos7720_port) return -ENOMEM; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index ca3da79afd23..c0d0c24b074b 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 * @@ -396,7 +383,7 @@ static void mos7840_set_led_sync(struct usb_serial_port *port, __u16 reg, static void mos7840_led_off(struct timer_list *t) { - struct moschip_port *mcs = from_timer(mcs, t, led_timer1); + struct moschip_port *mcs = timer_container_of(mcs, t, led_timer1); /* Turn off LED */ mos7840_set_led_async(mcs, 0x0300, MODEM_CONTROL_REGISTER); @@ -406,7 +393,7 @@ static void mos7840_led_off(struct timer_list *t) static void mos7840_led_flag_off(struct timer_list *t) { - struct moschip_port *mcs = from_timer(mcs, t, led_timer2); + struct moschip_port *mcs = timer_container_of(mcs, t, led_timer2); clear_bit_unlock(MOS7840_FLAG_LED_BUSY, &mcs->flags); } @@ -1545,7 +1532,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) pnum = port->port_number; dev_dbg(&port->dev, "mos7840_startup: configuring port %d\n", pnum); - mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL); + mos7840_port = kzalloc_obj(struct moschip_port); if (!mos7840_port) return -ENOMEM; @@ -1690,8 +1677,7 @@ static int mos7840_port_probe(struct usb_serial_port *port) /* Initialize LED timers */ if (mos7840_port->has_led) { mos7840_port->led_urb = usb_alloc_urb(0, GFP_KERNEL); - mos7840_port->led_dr = kmalloc(sizeof(*mos7840_port->led_dr), - GFP_KERNEL); + mos7840_port->led_dr = kmalloc_obj(*mos7840_port->led_dr); if (!mos7840_port->led_urb || !mos7840_port->led_dr) { status = -ENOMEM; goto error; diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 397ebd5a3e74..aa1e9745f967 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -113,7 +113,7 @@ static int omninet_port_probe(struct usb_serial_port *port) { struct omninet_data *od; - od = kzalloc(sizeof(*od), GFP_KERNEL); + od = kzalloc_obj(*od); if (!od) return -ENOMEM; diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 1ee84ccc4bbd..e2bed477ad57 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -220,7 +220,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, /* The connected devices do not have a bulk write endpoint, * to transmit data to de barcode device the control endpoint is used */ - dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); + dr = kmalloc_obj(struct usb_ctrlrequest, GFP_ATOMIC); if (!dr) goto error_no_dr; @@ -354,7 +354,7 @@ static int opticon_port_probe(struct usb_serial_port *port) { struct opticon_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5cd26dac2069..42e4cecd28ac 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -273,6 +273,7 @@ static void option_instat_callback(struct urb *urb); #define QUECTEL_PRODUCT_EM05CN 0x0312 #define QUECTEL_PRODUCT_EM05G_GR 0x0313 #define QUECTEL_PRODUCT_EM05G_RS 0x0314 +#define QUECTEL_PRODUCT_RG255C 0x0316 #define QUECTEL_PRODUCT_EM12 0x0512 #define QUECTEL_PRODUCT_RM500Q 0x0800 #define QUECTEL_PRODUCT_RM520N 0x0801 @@ -611,11 +612,13 @@ static void option_instat_callback(struct urb *urb); /* Sierra Wireless products */ #define SIERRA_VENDOR_ID 0x1199 #define SIERRA_PRODUCT_EM9191 0x90d3 +#define SIERRA_PRODUCT_EM9291 0x90e3 /* UNISOC (Spreadtrum) products */ #define UNISOC_VENDOR_ID 0x1782 /* TOZED LT70-C based on UNISOC SL8563 uses UNISOC's vendor ID */ #define TOZED_PRODUCT_LT70C 0x4055 +#define UNISOC_PRODUCT_UIS7720 0x4064 /* Luat Air72*U series based on UNISOC UIS8910 uses UNISOC's vendor ID */ #define LUAT_PRODUCT_AIR720U 0x4e00 @@ -1269,6 +1272,9 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0xff, 0x30) }, { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x40) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) }, @@ -1321,7 +1327,18 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(0) | RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1033, 0xff), /* Telit LE910C1-EUX (ECM) */ .driver_info = NCTRL(0) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1034, 0xff), /* Telit LE910C4-WWX (rmnet) */ + .driver_info = RSVD(2) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1035, 0xff) }, /* Telit LE910C4-WWX (ECM) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1036, 0xff) }, /* Telit LE910C4-WWX */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1037, 0xff), /* Telit LE910C4-WWX (rmnet) */ + .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1038, 0xff), /* Telit LE910C4-WWX (rmnet) */ + .driver_info = NCTRL(0) | RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x103b, 0xff), /* Telit LE910C4-WWX */ + .driver_info = NCTRL(0) | NCTRL(1) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x103c, 0xff), /* Telit LE910C4-WWX */ + .driver_info = NCTRL(0) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0), .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1), @@ -1366,8 +1383,16 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(2) | RSVD(3) }, { 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, 0x1074, 0xff), /* Telit FN990A (MBIM) */ + .driver_info = NCTRL(5) | RSVD(6) | RSVD(7) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff), /* Telit FN990A (PCIe) */ .driver_info = RSVD(0) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1077, 0xff), /* Telit FN990A (rmnet + audio) */ + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1078, 0xff), /* Telit FN990A (MBIM + audio) */ + .driver_info = NCTRL(0) | RSVD(1) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1079, 0xff), /* Telit FN990A (RNDIS + audio) */ + .driver_info = NCTRL(2) | RSVD(3) }, { 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 FE990A (MBIM) */ @@ -1378,16 +1403,26 @@ static const struct usb_device_id option_ids[] = { .driver_info = NCTRL(0) | RSVD(1) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a1, 0xff), /* Telit FN20C04 (RNDIS) */ + .driver_info = NCTRL(4) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a2, 0xff), /* Telit FN920C04 (MBIM) */ .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a3, 0xff), /* Telit FN920C04 (ECM) */ + .driver_info = NCTRL(4) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a4, 0xff), /* Telit FN20C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(3) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a6, 0xff), /* Telit FN920C04 (RNDIS) */ + .driver_info = NCTRL(4) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a7, 0xff), /* Telit FN920C04 (MBIM) */ .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a8, 0xff), /* Telit FN920C04 (ECM) */ + .driver_info = NCTRL(4) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a9, 0xff), /* Telit FN20C04 (rmnet) */ .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_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10ab, 0xff), /* Telit FN920C04 (RNDIS) */ + .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) }, @@ -1406,10 +1441,27 @@ static const struct usb_device_id option_ids[] = { { 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, 0x10c1, 0xff), /* Telit FE910C04 (RNDIS) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c2, 0xff), /* Telit FE910C04 (MBIM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c3, 0xff), /* Telit FE910C04 (ECM) */ + .driver_info = NCTRL(4) }, { 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, 0x10c5, 0xff), /* Telit FE910C04 (RNDIS) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c6, 0xff), /* Telit FE910C04 (MBIM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x30), /* Telit FE910C04 (ECM) */ + .driver_info = NCTRL(4) }, + { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10c7, 0xff, 0xff, 0x40) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c8, 0xff), /* Telit FE910C04 (rmnet) */ .driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c9, 0xff), /* Telit FE910C04 (MBIM) */ + .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10cb, 0xff), /* Telit FE910C04 (RNDIS) */ + .driver_info = NCTRL(3) | RSVD(4) | RSVD(5) }, { 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) }, @@ -1461,6 +1513,11 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1231, 0xff), /* Telit LE910Cx (RNDIS) */ .driver_info = NCTRL(2) | RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x1250, 0xff, 0x00, 0x00) }, /* Telit LE910Cx (rmnet) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1251, 0xff) }, /* Telit LE910Cx (RNDIS) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1252, 0xff) }, /* Telit LE910Cx (MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1253, 0xff) }, /* Telit LE910Cx (ECM) */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1254, 0xff) }, /* Telit LE910Cx */ + { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1255, 0xff) }, /* Telit LE910Cx */ { USB_DEVICE(TELIT_VENDOR_ID, 0x1260), .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, { USB_DEVICE(TELIT_VENDOR_ID, 0x1261), @@ -2093,6 +2150,12 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */ .driver_info = RSVD(7) }, + { USB_DEVICE(0x1e0e, 0x9071), /* Simcom SIM8230 RMNET mode */ + .driver_info = RSVD(3) | RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9078, 0xff), /* Simcom SIM8230 ECM mode */ + .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x907b, 0xff), /* Simcom SIM8230 RNDIS mode */ + .driver_info = RSVD(5) }, { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9205, 0xff) }, /* Simcom SIM7070/SIM7080/SIM7090 AT+ECM mode */ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9206, 0xff) }, /* Simcom SIM7070/SIM7080/SIM7090 AT-only mode */ { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200), @@ -2340,8 +2403,14 @@ static const struct usb_device_id option_ids[] = { .driver_info = RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0f0, 0xff), /* Foxconn T99W373 MBIM */ .driver_info = RSVD(3) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe123, 0xff), /* Foxconn T99W760 MBIM */ + .driver_info = RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe145, 0xff), /* Foxconn T99W651 RNDIS */ .driver_info = RSVD(5) | RSVD(6) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe15f, 0xff), /* Foxconn T99W709 */ + .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe167, 0xff), /* Foxconn T99W640 MBIM */ + .driver_info = RSVD(3) }, { USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */ .driver_info = RSVD(4) | RSVD(5) | RSVD(6) }, { USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */ @@ -2378,20 +2447,30 @@ static const struct usb_device_id option_ids[] = { { 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_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */ { 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 */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1406, 0xff) }, /* GosunCn GM500 ECM/NCM */ { USB_DEVICE(0x33f8, 0x0104), /* Rolling RW101-GL (laptop RMNET) */ .driver_info = RSVD(4) | RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0115, 0xff), /* Rolling RW135-GL (laptop MBIM) */ + .driver_info = RSVD(5) }, { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a2, 0xff) }, /* Rolling RW101-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a3, 0xff) }, /* Rolling RW101-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a4, 0xff), /* Rolling RW101-GL (laptop MBIM) */ .driver_info = RSVD(4) }, - { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0115, 0xff), /* Rolling RW135-GL (laptop MBIM) */ - .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a8, 0xff), /* Rolling RW101R-GL (laptop MBIM) */ + .driver_info = RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x01a9, 0xff), /* Rolling RW101R-GL (laptop MBIM) */ + .driver_info = RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0301, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */ .driver_info = RSVD(5) }, + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) }, @@ -2432,7 +2511,10 @@ static const struct usb_device_id option_ids[] = { { 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(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9291, 0xff, 0xff, 0x30) }, + { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9291, 0xff, 0xff, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, TOZED_PRODUCT_LT70C, 0xff, 0, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, UNISOC_PRODUCT_UIS7720, 0xff, 0, 0) }, { USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, LUAT_PRODUCT_AIR720U, 0xff, 0, 0) }, { USB_DEVICE_INTERFACE_CLASS(0x1bbb, 0x0530, 0xff), /* TCL IK512 MBIM */ .driver_info = NCTRL(1) }, @@ -2534,7 +2616,7 @@ static int option_attach(struct usb_serial *serial) struct usb_wwan_intf_private *data; unsigned long device_flags; - data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); + data = kzalloc_obj(struct usb_wwan_intf_private); if (!data) return -ENOMEM; diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 24068368892c..3ef5b5d8ce1a 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -106,7 +106,7 @@ struct oti6858_control_pkt { #define PIN_DTR 0x04 /* output pin */ #define PIN_RI 0x02 /* input pin, active low */ #define PIN_DCD 0x01 /* input pin, active low */ - u8 rx_bytes_avail; /* number of bytes in rx buffer */; + u8 rx_bytes_avail; /* number of bytes in rx buffer */ }; #define OTI6858_CTRL_PKT_SIZE sizeof(struct oti6858_control_pkt) @@ -328,7 +328,7 @@ static int oti6858_port_probe(struct usb_serial_port *port) { struct oti6858_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 010688dd9e49..50dc838e8115 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -458,6 +458,8 @@ static int pl2303_detect_type(struct usb_serial *serial) case 0x605: case 0x700: /* GR */ case 0x705: + case 0x905: /* GT-2AB */ + case 0x1005: /* GC-Q20 */ return TYPE_HXN; } break; @@ -501,7 +503,7 @@ static int pl2303_startup(struct usb_serial *serial) type = ret; dev_dbg(&serial->interface->dev, "device type: %s\n", pl2303_type_data[type].name); - spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); + spriv = kzalloc_obj(*spriv); if (!spriv) return -ENOMEM; @@ -553,7 +555,7 @@ static int pl2303_port_probe(struct usb_serial_port *port) { struct pl2303_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 13c664317a05..1a930dc668e4 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -431,7 +431,7 @@ static int qc_attach(struct usb_serial *serial) struct usb_wwan_intf_private *data; bool sendsetup; - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = kzalloc_obj(*data); if (!data) return -ENOMEM; diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 72fe83a6c978..b05c655b3cc4 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -626,7 +626,7 @@ static int qt2_attach(struct usb_serial *serial) return status; } - serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL); + serial_priv = kzalloc_obj(*serial_priv); if (!serial_priv) return -ENOMEM; @@ -657,7 +657,7 @@ static int qt2_port_probe(struct usb_serial_port *port) struct qt2_port_private *port_priv; u8 bEndpointAddress; - port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL); + port_priv = kzalloc_obj(*port_priv); if (!port_priv) return -ENOMEM; diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 741e68e46139..6e443aacae07 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -828,7 +828,7 @@ static int sierra_startup(struct usb_serial *serial) { struct sierra_intf_private *intfdata; - intfdata = kzalloc(sizeof(*intfdata), GFP_KERNEL); + intfdata = kzalloc_obj(*intfdata); if (!intfdata) return -ENOMEM; @@ -861,7 +861,7 @@ static int sierra_port_probe(struct usb_serial_port *port) const struct sierra_iface_list *himemory_list; u8 ifnum; - portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); + portdata = kzalloc_obj(*portdata); if (!portdata) return -ENOMEM; diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 11077beb7232..2fbb48f19efd 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -145,7 +145,7 @@ static int spcp8x5_port_probe(struct usb_serial_port *port) const struct usb_device_id *id = usb_get_serial_data(port->serial); struct spcp8x5_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index df21009bdf42..da6316410b77 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -326,7 +326,7 @@ static int ssu100_port_probe(struct usb_serial_port *port) { struct ssu100_port_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 58962bcbf9ba..7a5aa39172a8 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -149,7 +149,7 @@ static int symbol_port_probe(struct usb_serial_port *port) { struct symbol_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index a0c244bc77c0..b3591d6d7645 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -519,7 +519,7 @@ static int ti_startup(struct usb_serial *serial) dev->descriptor.bNumConfigurations, dev->actconfig->desc.bConfigurationValue); - tdev = kzalloc(sizeof(struct ti_device), GFP_KERNEL); + tdev = kzalloc_obj(struct ti_device); if (!tdev) return -ENOMEM; @@ -598,7 +598,7 @@ static int ti_port_probe(struct usb_serial_port *port) { struct ti_port *tport; - tport = kzalloc(sizeof(*tport), GFP_KERNEL); + tport = kzalloc_obj(*tport); if (!tport) return -ENOMEM; @@ -729,11 +729,6 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) /* start read urb */ urb = port->read_urb; - if (!urb) { - dev_err(&port->dev, "%s - no read urb\n", __func__); - status = -EINVAL; - goto unlink_int_urb; - } tport->tp_read_urb_state = TI_READ_URB_RUNNING; urb->context = tport; status = usb_submit_urb(urb, GFP_KERNEL); @@ -902,7 +897,7 @@ static void ti_set_termios(struct tty_struct *tty, u16 wbaudrate; u16 wflags = 0; - config = kmalloc(sizeof(*config), GFP_KERNEL); + config = kmalloc_obj(*config); if (!config) return; @@ -1605,29 +1600,29 @@ static int ti_download_firmware(struct ti_device *tdev) if (le16_to_cpu(dev->descriptor.idVendor) == MTS_VENDOR_ID) { switch (le16_to_cpu(dev->descriptor.idProduct)) { case MTS_CDMA_PRODUCT_ID: - strcpy(buf, "mts_cdma.fw"); + strscpy(buf, "mts_cdma.fw"); break; case MTS_GSM_PRODUCT_ID: - strcpy(buf, "mts_gsm.fw"); + strscpy(buf, "mts_gsm.fw"); break; case MTS_EDGE_PRODUCT_ID: - strcpy(buf, "mts_edge.fw"); + strscpy(buf, "mts_edge.fw"); break; case MTS_MT9234MU_PRODUCT_ID: - strcpy(buf, "mts_mt9234mu.fw"); + strscpy(buf, "mts_mt9234mu.fw"); break; case MTS_MT9234ZBA_PRODUCT_ID: - strcpy(buf, "mts_mt9234zba.fw"); + strscpy(buf, "mts_mt9234zba.fw"); break; case MTS_MT9234ZBAOLD_PRODUCT_ID: - strcpy(buf, "mts_mt9234zba.fw"); + strscpy(buf, "mts_mt9234zba.fw"); break; } } if (buf[0] == '\0') { if (tdev->td_is_3410) - strcpy(buf, "ti_3410.fw"); + strscpy(buf, "ti_3410.fw"); else - strcpy(buf, "ti_5052.fw"); + strscpy(buf, "ti_5052.fw"); } status = request_firmware(&fw_p, buf, &dev->dev); } diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c index 15a17bf111f1..78c4d3c19a8e 100644 --- a/drivers/usb/serial/upd78f0730.c +++ b/drivers/usb/serial/upd78f0730.c @@ -161,7 +161,7 @@ static int upd78f0730_port_probe(struct usb_serial_port *port) { struct upd78f0730_port_private *private; - private = kzalloc(sizeof(*private), GFP_KERNEL); + private = kzalloc_obj(*private); if (!private) return -ENOMEM; diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index 2c12449ff60c..a0afaf254d12 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -100,6 +100,11 @@ DEVICE(nokia, NOKIA_IDS); { USB_DEVICE(0x09d7, 0x0100) } /* NovAtel FlexPack GPS */ DEVICE_N(novatel_gps, NOVATEL_IDS, 3); +/* OWON electronic test and measurement equipment driver */ +#define OWON_IDS() \ + { USB_DEVICE(0x5345, 0x1234) } /* HDS200 oscilloscopes and others */ +DEVICE(owon, OWON_IDS); + /* Siemens USB/MPI adapter */ #define SIEMENS_IDS() \ { USB_DEVICE(0x908, 0x0004) } @@ -134,6 +139,7 @@ static struct usb_serial_driver * const serial_drivers[] = { &motorola_tetra_device, &nokia_device, &novatel_gps_device, + &owon_device, &siemens_mpi_device, &suunto_device, &vivopay_device, @@ -153,6 +159,7 @@ static const struct usb_device_id id_table[] = { MOTOROLA_TETRA_IDS(), NOKIA_IDS(), NOVATEL_IDS(), + OWON_IDS(), SIEMENS_IDS(), SUUNTO_IDS(), VIVOPAY_IDS(), diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 7266558d823a..0e072fd87c3d 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -688,7 +688,7 @@ static struct usb_serial *create_serial(struct usb_device *dev, { struct usb_serial *serial; - serial = kzalloc(sizeof(*serial), GFP_KERNEL); + serial = kzalloc_obj(*serial); if (!serial) return NULL; serial->dev = usb_get_dev(dev); @@ -1005,7 +1005,7 @@ static int usb_serial_probe(struct usb_interface *interface, } /* descriptor matches, let's find the endpoints needed */ - epds = kzalloc(sizeof(*epds), GFP_KERNEL); + epds = kzalloc_obj(*epds); if (!epds) { retval = -ENOMEM; goto err_release_sibling; @@ -1059,7 +1059,7 @@ static int usb_serial_probe(struct usb_interface *interface, dev_dbg(ddev, "setting up %d port structure(s)\n", max_endpoints); for (i = 0; i < max_endpoints; ++i) { - port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); + port = kzalloc_obj(struct usb_serial_port); if (!port) { retval = -ENOMEM; goto err_free_epds; @@ -1176,7 +1176,6 @@ static void usb_serial_disconnect(struct usb_interface *interface) struct usb_serial *serial = usb_get_intfdata(interface); struct device *dev = &interface->dev; struct usb_serial_port *port; - struct tty_struct *tty; /* sibling interface is cleaning up */ if (!serial) @@ -1191,11 +1190,7 @@ static void usb_serial_disconnect(struct usb_interface *interface) for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; - tty = tty_port_tty_get(&port->port); - if (tty) { - tty_vhangup(tty); - tty_kref_put(tty); - } + tty_port_tty_vhangup(&port->port); usb_serial_port_poison_urbs(port); wake_up_interruptible(&port->port.delta_msr_wait); cancel_work_sync(&port->work); @@ -1487,7 +1482,7 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers * Suspend/resume support is implemented in the usb-serial core, * so fill in the PM-related fields in udriver. */ - udriver = kzalloc(sizeof(*udriver), GFP_KERNEL); + udriver = kzalloc_obj(*udriver); if (!udriver) return -ENOMEM; diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 0017f6e969e1..e752ffa4dc62 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -449,7 +449,7 @@ int usb_wwan_port_probe(struct usb_serial_port *port) if (!port->bulk_in_size || !port->bulk_out_size) return -ENODEV; - portdata = kzalloc(sizeof(*portdata), GFP_KERNEL); + portdata = kzalloc_obj(*portdata); if (!portdata) return -ENOMEM; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 009faeb2ef55..6e2b52b2b5f8 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -278,8 +278,7 @@ static int whiteheat_attach(struct usb_serial *serial) serial->type->description, hw_info->sw_major_rev, hw_info->sw_minor_rev); - command_info = kmalloc(sizeof(struct whiteheat_command_private), - GFP_KERNEL); + command_info = kmalloc_obj(struct whiteheat_command_private); if (!command_info) goto no_command_private; @@ -330,7 +329,7 @@ static int whiteheat_port_probe(struct usb_serial_port *port) { struct whiteheat_private *info; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc_obj(*info); if (!info) return -ENOMEM; diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c index 4186e791b384..9fc4082de770 100644 --- a/drivers/usb/serial/xr_serial.c +++ b/drivers/usb/serial/xr_serial.c @@ -756,7 +756,7 @@ static void xr_cdc_set_line_coding(struct tty_struct *tty, struct usb_cdc_line_coding *lc; int ret; - lc = kzalloc(sizeof(*lc), GFP_KERNEL); + lc = kzalloc_obj(*lc); if (!lc) return; @@ -1020,7 +1020,7 @@ static int xr_port_probe(struct usb_serial_port *port) type_id = (int)(unsigned long)usb_get_serial_data(port->serial); type = &xr_types[type_id]; - data = kzalloc(sizeof(*data), GFP_KERNEL); + data = kzalloc_obj(*data); if (!data) return -ENOMEM; diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index 6263c4e61678..691fe47009cf 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -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) { @@ -1113,7 +1113,7 @@ static int init_alauda(struct us_data *us) struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting; nand_init_ecc(); - us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO); + us->extra = kzalloc_obj(struct alauda_info, GFP_NOIO); if (!us->extra) return -ENOMEM; @@ -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/datafab.c b/drivers/usb/storage/datafab.c index bbfa2398b170..0ded8ef72efa 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -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,12 +550,12 @@ 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 }; if (!us->extra) { - us->extra = kzalloc(sizeof(struct datafab_info), GFP_NOIO); + us->extra = kzalloc_obj(struct datafab_info, GFP_NOIO); if (!us->extra) return USB_STOR_TRANSPORT_ERROR; 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 ce91fb105975..8770de01a384 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -1119,9 +1119,8 @@ static int ms_lib_alloc_writebuf(struct us_data *us) info->MS_Lib.blkpag = kmalloc_array(info->MS_Lib.PagesPerBlock, info->MS_Lib.BytesPerSector, GFP_KERNEL); - info->MS_Lib.blkext = kmalloc_array(info->MS_Lib.PagesPerBlock, - sizeof(struct ms_lib_type_extdat), - GFP_KERNEL); + info->MS_Lib.blkext = kmalloc_objs(struct ms_lib_type_extdat, + info->MS_Lib.PagesPerBlock); if ((info->MS_Lib.blkpag == NULL) || (info->MS_Lib.blkext == NULL)) { ms_lib_free_writebuf(us); @@ -2336,7 +2335,7 @@ static int ene_ub6250_probe(struct usb_interface *intf, return result; /* FIXME: where should the code alloc extra buf ? */ - us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL); + us->extra = kzalloc_obj(struct ene_ub6250_info); if (!us->extra) return -ENOMEM; us->extra_destructor = ene_ub6250_info_destructor; 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 a1669c35bad5..a4fd5dda3396 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1463,7 +1463,7 @@ static int isd200_init_info(struct us_data *us) { struct isd200_info *info; - info = kzalloc(sizeof(struct isd200_info), GFP_KERNEL); + info = kzalloc_obj(struct isd200_info); if (!info) return -ENOMEM; diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 39ca84d68591..a9c48091491e 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -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,12 +477,12 @@ 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 }; if (!us->extra) { - us->extra = kzalloc(sizeof(struct jumpshot_info), GFP_NOIO); + us->extra = kzalloc_obj(struct jumpshot_info, GFP_NOIO); if (!us->extra) return USB_STOR_TRANSPORT_ERROR; diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c index 341d6839548a..5f199c21bb0c 100644 --- a/drivers/usb/storage/karma.c +++ b/drivers/usb/storage/karma.c @@ -174,7 +174,7 @@ static void rio_karma_destructor(void *extra) static int rio_karma_init(struct us_data *us) { - struct karma_data *data = kzalloc(sizeof(struct karma_data), GFP_NOIO); + struct karma_data *data = kzalloc_obj(struct karma_data, GFP_NOIO); if (!data) return -ENOMEM; diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index 5a8a1ffda0ec..49d281f7bef6 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -183,7 +183,7 @@ static int onetouch_connect_input(struct us_data *ss) maxp = usb_maxpacket(udev, pipe); maxp = min(maxp, ONETOUCH_PKT_LEN); - onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL); + onetouch = kzalloc_obj(struct usb_onetouch); input_dev = input_allocate_device(); if (!onetouch || !input_dev) goto fail1; diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 9033e505db7f..0cff54ad90fa 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -139,8 +139,7 @@ unsigned int usb_stor_access_xfer_buf(unsigned char *buffer, return cnt; while (sg_miter_next(&miter) && cnt < buflen) { - unsigned int len = min_t(unsigned int, miter.length, - buflen - cnt); + unsigned int len = min(miter.length, buflen - cnt); if (dir == FROM_XFER_BUF) memcpy(buffer + cnt, miter.addr, len); diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 2a82ed7b68ea..af038b897c6b 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -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 */ }; @@ -252,7 +252,7 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun, return USB_STOR_TRANSPORT_ERROR; } - residue = bcs->Residue; + residue = le32_to_cpu(bcs->Residue); if (bcs->Tag != us->tag) return USB_STOR_TRANSPORT_ERROR; @@ -260,8 +260,8 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun, * try to compute the actual residue, based on how much data * was really transferred and what the device tells us */ - if (residue) - residue = residue < buf_len ? residue : buf_len; + if (residue > buf_len) + residue = buf_len; if (act_len) *act_len = buf_len - residue; @@ -748,13 +748,14 @@ static void rts51x_modi_suspend_timer(struct rts51x_chip *chip) usb_stor_dbg(us, "state:%d\n", rts51x_get_stat(chip)); - chip->timer_expires = jiffies + msecs_to_jiffies(1000*ss_delay); + chip->timer_expires = jiffies + secs_to_jiffies(ss_delay); mod_timer(&chip->rts51x_suspend_timer, chip->timer_expires); } static void rts51x_suspend_timer_fn(struct timer_list *t) { - struct rts51x_chip *chip = from_timer(chip, t, rts51x_suspend_timer); + struct rts51x_chip *chip = timer_container_of(chip, t, + rts51x_suspend_timer); struct us_data *us = chip->us; switch (rts51x_get_stat(chip)) { @@ -797,10 +798,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 +935,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 @@ -975,7 +976,7 @@ static int init_realtek_cr(struct us_data *us) struct rts51x_chip *chip; int size, i, retval; - chip = kzalloc(sizeof(struct rts51x_chip), GFP_KERNEL); + chip = kzalloc_obj(struct rts51x_chip); if (!chip) return -ENOMEM; diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index d2f476e48d0c..97de28e85562 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -357,7 +357,7 @@ static int target_alloc(struct scsi_target *starget) /* queue a command */ /* This is always called with scsi_lock(host) held */ -static int queuecommand_lck(struct scsi_cmnd *srb) +static enum scsi_qc_status queuecommand_lck(struct scsi_cmnd *srb) { void (*done)(struct scsi_cmnd *) = scsi_done; struct us_data *us = host_to_us(srb->device->host); diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index d21ce3466e25..3d45e1b54c66 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -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; @@ -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; @@ -1232,8 +1232,8 @@ sddr09_read_map(struct us_data *us) { kfree(info->lba_to_pba); kfree(info->pba_to_lba); - info->lba_to_pba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO); - info->pba_to_lba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO); + info->lba_to_pba = kmalloc_objs(int, numblocks, GFP_NOIO); + info->pba_to_lba = kmalloc_objs(int, numblocks, GFP_NOIO); if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) { printk(KERN_WARNING "sddr09_read_map: out of memory\n"); @@ -1428,7 +1428,7 @@ sddr09_common_init(struct us_data *us) { return -EINVAL; } - us->extra = kzalloc(sizeof(struct sddr09_card_info), GFP_NOIO); + us->extra = kzalloc_obj(struct sddr09_card_info, GFP_NOIO); if (!us->extra) return -ENOMEM; us->extra_destructor = sddr09_card_info_destructor; @@ -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 d5cdff30f6f3..e05e824cd4d1 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -469,6 +469,12 @@ static int sddr55_write_data(struct us_data *us, new_pba = (status[3] + (status[4] << 8) + (status[5] << 16)) >> info->blockshift; + /* check if device-reported new_pba is out of range */ + if (new_pba >= (info->capacity >> (info->blockshift + info->pageshift))) { + result = USB_STOR_TRANSPORT_FAILED; + goto leave; + } + /* check status for error */ if (status[0] == 0xff && status[1] == 0x4) { info->pba_to_lba[new_pba] = BAD_BLOCK; @@ -685,8 +691,8 @@ static int sddr55_read_map(struct us_data *us) { kfree(info->lba_to_pba); kfree(info->pba_to_lba); - info->lba_to_pba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO); - info->pba_to_lba = kmalloc_array(numblocks, sizeof(int), GFP_NOIO); + info->lba_to_pba = kmalloc_objs(int, numblocks, GFP_NOIO); + info->pba_to_lba = kmalloc_objs(int, numblocks, GFP_NOIO); if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) { kfree(info->lba_to_pba); @@ -775,11 +781,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 @@ -793,8 +799,7 @@ static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) struct sddr55_card_info *info; if (!us->extra) { - us->extra = kzalloc( - sizeof(struct sddr55_card_info), GFP_NOIO); + us->extra = kzalloc_obj(struct sddr55_card_info, GFP_NOIO); if (!us->extra) return USB_STOR_TRANSPORT_ERROR; us->extra_destructor = sddr55_card_info_destructor; diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index c33cbf177e6f..7e5424268c73 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1454,7 +1454,7 @@ static int init_usbat(struct us_data *us, int devicetype) unsigned char subcountL = USBAT_ATA_LBA_ME; unsigned char *status = us->iobuf; - us->extra = kzalloc(sizeof(struct usbat_info), GFP_NOIO); + us->extra = kzalloc_obj(struct usbat_info, GFP_NOIO); if (!us->extra) return -ENOMEM; @@ -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/sierra_ms.c b/drivers/usb/storage/sierra_ms.c index 177fa6cd143a..c5e3eeeb2144 100644 --- a/drivers/usb/storage/sierra_ms.c +++ b/drivers/usb/storage/sierra_ms.c @@ -100,7 +100,7 @@ static ssize_t truinst_show(struct device *dev, struct device_attribute *attr, if (swi_tru_install == TRU_FORCE_MS) { result = sysfs_emit(buf, "Forced Mass Storage\n"); } else { - swocInfo = kmalloc(sizeof(struct swoc_info), GFP_KERNEL); + swocInfo = kmalloc_obj(struct swoc_info); if (!swocInfo) { sysfs_emit(buf, "Error\n"); return -ENOMEM; @@ -149,8 +149,7 @@ int sierra_ms_init(struct us_data *us) else { usb_stor_dbg(us, "SWIMS: Normal SWoC Logic\n"); - swocInfo = kmalloc(sizeof(struct swoc_info), - GFP_KERNEL); + swocInfo = kmalloc_obj(struct swoc_info); if (!swocInfo) return -ENOMEM; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index e6bc8ecaecbb..9a4bf86e7b6a 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 */ @@ -1200,7 +1200,23 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) US_BULK_CS_WRAP_LEN && bcs->Signature == cpu_to_le32(US_BULK_CS_SIGN)) { + unsigned char buf[US_BULK_CS_WRAP_LEN]; + usb_stor_dbg(us, "Device skipped data phase\n"); + + /* + * Devices skipping data phase might leave CSW data in srb's + * transfer buffer. Zero it to prevent USB protocol leakage. + */ + sg = NULL; + offset = 0; + memset(buf, 0, sizeof(buf)); + if (usb_stor_access_xfer_buf(buf, + US_BULK_CS_WRAP_LEN, srb, &sg, + &offset, TO_XFER_BUF) != + US_BULK_CS_WRAP_LEN) + usb_stor_dbg(us, "Failed to clear CSW data\n"); + scsi_set_resid(srb, transfer_length); goto skipped_data_phase; } diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 4ed0dc19afe0..265162981269 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -309,18 +309,18 @@ static void uas_stat_cmplt(struct urb *urb) int status = urb->status; bool success; - spin_lock_irqsave(&devinfo->lock, flags); - - if (devinfo->resetting) - goto out; - if (status) { if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) dev_err(&urb->dev->dev, "stat urb: status %d\n", status); - goto out; + goto bail; } idx = be16_to_cpup(&iu->tag) - 1; + + spin_lock_irqsave(&devinfo->lock, flags); + + if (devinfo->resetting) + goto out; if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) { dev_err(&urb->dev->dev, "stat urb: no pending cmd for uas-tag %d\n", idx + 1); @@ -375,9 +375,8 @@ static void uas_stat_cmplt(struct urb *urb) default: uas_log_cmd_state(cmnd, "bogus IU", iu->iu_id); } -out: - usb_free_urb(urb); spin_unlock_irqrestore(&devinfo->lock, flags); + usb_free_urb(urb); /* Unlinking of data urbs must be done without holding the lock */ if (data_in_urb) { @@ -388,6 +387,12 @@ out: usb_unlink_urb(data_out_urb); usb_put_urb(data_out_urb); } + return; + +out: + spin_unlock_irqrestore(&devinfo->lock, flags); +bail: + usb_free_urb(urb); } static void uas_data_cmplt(struct urb *urb) @@ -429,8 +434,8 @@ static void uas_data_cmplt(struct urb *urb) } uas_try_complete(cmnd, __func__); out: - usb_free_urb(urb); spin_unlock_irqrestore(&devinfo->lock, flags); + usb_free_urb(urb); } static void uas_cmd_cmplt(struct urb *urb) @@ -475,7 +480,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, if (!urb) goto out; - iu = kzalloc(sizeof(*iu), gfp); + iu = kzalloc_obj(*iu, gfp); if (!iu) goto free; @@ -631,7 +636,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, return 0; } -static int uas_queuecommand_lck(struct scsi_cmnd *cmnd) +static enum scsi_qc_status uas_queuecommand_lck(struct scsi_cmnd *cmnd) { struct scsi_device *sdev = cmnd->device; struct uas_dev_info *devinfo = sdev->hostdata; @@ -698,6 +703,10 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd) * of queueing, no matter how fatal the error */ if (err == -ENODEV) { + if (cmdinfo->state & (COMMAND_INFLIGHT | DATA_IN_URB_INFLIGHT | + DATA_OUT_URB_INFLIGHT)) + goto out; + set_host_byte(cmnd, DID_NO_CONNECT); scsi_done(cmnd); goto zombie; @@ -711,6 +720,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd) uas_add_work(cmnd); } +out: devinfo->cmnd[idx] = cmnd; zombie: spin_unlock_irqrestore(&devinfo->lock, flags); @@ -762,7 +772,7 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd) return FAILED; } -static int uas_eh_device_reset_handler(struct scsi_cmnd *cmnd) +static int uas_eh_host_reset_handler(struct scsi_cmnd *cmnd) { struct scsi_device *sdev = cmnd->device; struct uas_dev_info *devinfo = sdev->hostdata; @@ -908,7 +918,7 @@ static const struct scsi_host_template uas_host_template = { .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, + .eh_host_reset_handler = uas_eh_host_reset_handler, .this_id = -1, .skip_settle_delay = 1, /* @@ -1265,7 +1275,7 @@ static int __init uas_init(void) { int rv; - workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM, 0); + workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM | WQ_PERCPU, 0); if (!workqueue) return -ENOMEM; diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 54f0b1c83317..255968f9ca42 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -934,6 +934,13 @@ UNUSUAL_DEV( 0x05e3, 0x0723, 0x9451, 0x9451, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_SANE_SENSE ), +/* Added by Maël GUERIN <mael.guerin@murena.io> */ +UNUSUAL_DEV( 0x0603, 0x8611, 0x0000, 0xffff, + "Novatek", + "NTK96550-based camera", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BULK_IGNORE_TAG ), + /* * Reported by Hanno Boeck <hanno@gmx.de> * Taken from the Lycoris Kernel @@ -1494,6 +1501,28 @@ UNUSUAL_DEV( 0x0bc2, 0x3332, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_WP_DETECT ), +/* + * Reported by Zenm Chen <zenmchen@gmail.com> + * Ignore driver CD mode, otherwise usb_modeswitch may fail to switch + * the device into Wi-Fi mode. + */ +UNUSUAL_DEV( 0x0bda, 0x1a2b, 0x0000, 0xffff, + "Realtek", + "DISK", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE ), + +/* + * Reported by Zenm Chen <zenmchen@gmail.com> + * Ignore driver CD mode, otherwise usb_modeswitch may fail to switch + * the device into Wi-Fi mode. + */ +UNUSUAL_DEV( 0x0bda, 0xa192, 0x0000, 0xffff, + "Realtek", + "DISK", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE ), + UNUSUAL_DEV( 0x0d49, 0x7310, 0x0000, 0x9999, "Maxtor", "USB to SATA", @@ -2321,10 +2350,11 @@ UNUSUAL_DEV( 0x2027, 0xa001, 0x0000, 0x9999, US_FL_SCM_MULT_TARG ), /* - * Reported by DocMAX <mail@vacharakis.de> - * and Thomas Weißschuh <linux@weissschuh.net> + * Reported by DocMAX <mail@vacharakis.de>, + * Thomas Weißschuh <linux@weissschuh.net> + * and Daniel Brát <danek.brat@gmail.com> */ -UNUSUAL_DEV( 0x2109, 0x0715, 0x9999, 0x9999, +UNUSUAL_DEV( 0x2109, 0x0715, 0x0000, 0x9999, "VIA Labs, Inc.", "VL817 SATA Bridge", USB_SC_DEVICE, USB_PR_DEVICE, NULL, diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 1f8c9b16a0fb..939a98c2d3f7 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -52,6 +52,13 @@ UNUSUAL_DEV(0x059f, 0x1061, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_OPCODES | US_FL_NO_SAME), +/* Reported-by: Zhihong Zhou <zhouzhihong@greatwall.com.cn> */ +UNUSUAL_DEV(0x0781, 0x55e8, 0x0000, 0x9999, + "SanDisk", + "", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + /* Reported-by: Hongling Zeng <zenghongling@kylinos.cn> */ UNUSUAL_DEV(0x090c, 0x2000, 0x0000, 0x9999, "Hiksemi", @@ -83,8 +90,15 @@ UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_LUNS), +/* Reported-by: Oliver Neukum <oneukum@suse.com> */ +UNUSUAL_DEV(0x125f, 0xa94a, 0x0160, 0x0160, + "ADATA", + "Portable HDD CH94", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + /* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */ -UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999, +UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x0309, "Initio Corporation", "INIC-3069", USB_SC_DEVICE, USB_PR_DEVICE, NULL, diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index d36f3b6992bb..fa83fe0defe2 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -537,7 +537,7 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf) usb_set_intfdata(intf, us); /* Allocate the control/setup and DMA-mapped buffers */ - us->cr = kmalloc(sizeof(*us->cr), GFP_KERNEL); + us->cr = kmalloc_obj(*us->cr); if (!us->cr) return -ENOMEM; @@ -1056,13 +1056,20 @@ int usb_stor_probe1(struct us_data **pus, goto BadDevice; /* - * Some USB host controllers can't do DMA; they have to use PIO. - * For such controllers we need to make sure the block layer sets - * up bounce buffers in addressable memory. + * Some USB host controllers can't do DMA: They have to use PIO, or they + * have to use a small dedicated local memory area, or they have other + * restrictions on addressable memory. + * + * We can't support these controllers on highmem systems as we don't + * kmap or bounce buffer. */ - if (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)) || - bus_to_hcd(us->pusb_dev->bus)->localmem_pool) - host->no_highmem = true; + if (IS_ENABLED(CONFIG_HIGHMEM) && + (!hcd_uses_dma(bus_to_hcd(us->pusb_dev->bus)) || + bus_to_hcd(us->pusb_dev->bus)->localmem_pool)) { + dev_warn(&intf->dev, "USB Mass Storage not supported on this host controller\n"); + result = -EINVAL; + goto release; + } /* Get the unusual_devs entries and the descriptors */ result = get_device_info(us, id, unusual_dev); @@ -1081,6 +1088,7 @@ int usb_stor_probe1(struct us_data **pus, BadDevice: usb_stor_dbg(us, "storage_probe() failed\n"); +release: release_everything(us); return result; } diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 7a368fea61bc..8a6a1c663eb6 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC) += typec.o -typec-y := class.o mux.o bus.o pd.o retimer.o +typec-y := class.o mux.o bus.o pd.o retimer.o mode_selection.o typec-$(CONFIG_ACPI) += port-mapper.o obj-$(CONFIG_TYPEC) += altmodes/ obj-$(CONFIG_TYPEC_TCPM) += tcpm/ diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index ac84a6d64c2f..35d9c3086990 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -65,6 +65,13 @@ struct dp_altmode { enum dp_state state; bool hpd; bool pending_hpd; + u32 irq_hpd_count; + /* + * hpd is mandatory for irq_hpd assertion, so irq_hpd also needs its own pending flag if + * both hpd and irq_hpd are asserted in the first Status Update before the pin assignment + * is configured. + */ + bool pending_irq_hpd; struct mutex lock; /* device lock */ struct work_struct work; @@ -93,9 +100,14 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con) { u8 pin_assign = 0; u32 conf; + u32 signal; /* DP Signalling */ - conf = (dp->data.conf & DP_CONF_SIGNALLING_MASK) >> DP_CONF_SIGNALLING_SHIFT; + signal = DP_CAP_DP_SIGNALLING(dp->port->vdo) & DP_CAP_DP_SIGNALLING(dp->alt->vdo); + if (dp->plug_prime) + signal &= DP_CAP_DP_SIGNALLING(dp->plug_prime->vdo); + + conf = signal << DP_CONF_SIGNALLING_SHIFT; switch (con) { case DP_STATUS_CON_DISABLED: @@ -151,6 +163,7 @@ static int dp_altmode_status_update(struct dp_altmode *dp) { bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf); bool hpd = !!(dp->data.status & DP_STATUS_HPD_STATE); + bool irq_hpd = !!(dp->data.status & DP_STATUS_IRQ_HPD); u8 con = DP_STATUS_CONNECTION(dp->data.status); int ret = 0; @@ -170,6 +183,8 @@ static int dp_altmode_status_update(struct dp_altmode *dp) dp->hpd = hpd; dp->pending_hpd = true; } + if (dp->hpd && dp->pending_hpd && irq_hpd) + dp->pending_irq_hpd = true; } } else { drm_connector_oob_hotplug_event(dp->connector_fwnode, @@ -177,6 +192,10 @@ static int dp_altmode_status_update(struct dp_altmode *dp) connector_status_disconnected); dp->hpd = hpd; sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd"); + if (hpd && irq_hpd) { + dp->irq_hpd_count++; + sysfs_notify(&dp->alt->dev.kobj, "displayport", "irq_hpd"); + } } return ret; @@ -196,6 +215,11 @@ static int dp_altmode_configured(struct dp_altmode *dp) connector_status_connected); sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd"); dp->pending_hpd = false; + if (dp->pending_irq_hpd) { + dp->irq_hpd_count++; + sysfs_notify(&dp->alt->dev.kobj, "displayport", "irq_hpd"); + dp->pending_irq_hpd = false; + } } return dp_altmode_notify(dp); @@ -393,6 +417,9 @@ static int dp_altmode_vdm(struct typec_altmode *alt, break; case CMDT_RSP_NAK: switch (cmd) { + case DP_CMD_STATUS_UPDATE: + dp->state = DP_STATE_EXIT; + break; case DP_CMD_CONFIGURE: dp->data.conf = 0; ret = dp_altmode_configured(dp); @@ -673,7 +700,7 @@ static ssize_t pin_assignment_show(struct device *dev, assignments = get_current_pin_assignments(dp); - for (i = 0; assignments; assignments >>= 1, i++) { + for (i = 0; assignments && i < DP_PIN_ASSIGN_MAX; assignments >>= 1, i++) { if (assignments & 1) { if (i == cur) len += sprintf(buf + len, "[%s] ", @@ -703,10 +730,19 @@ static ssize_t hpd_show(struct device *dev, struct device_attribute *attr, char } static DEVICE_ATTR_RO(hpd); +static ssize_t irq_hpd_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dp_altmode *dp = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", dp->irq_hpd_count); +} +static DEVICE_ATTR_RO(irq_hpd); + static struct attribute *displayport_attrs[] = { &dev_attr_configuration.attr, &dev_attr_pin_assignment.attr, &dev_attr_hpd.attr, + &dev_attr_irq_hpd.attr, NULL }; @@ -727,18 +763,24 @@ int dp_altmode_probe(struct typec_altmode *alt) struct fwnode_handle *fwnode; struct dp_altmode *dp; - /* FIXME: Port can only be DFP_U. */ + /* Port can only be DFP_U. */ + if (typec_altmode_get_data_role(alt) != TYPEC_HOST) + return -EPROTO; /* 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) & - DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo))) + DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo))) { + typec_altmode_put_plug(plug); return -ENODEV; + } dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); - if (!dp) + if (!dp) { + typec_altmode_put_plug(plug); return -ENOMEM; + } INIT_WORK(&dp->work, dp_altmode_work); mutex_init(&dp->lock); @@ -767,8 +809,10 @@ int dp_altmode_probe(struct typec_altmode *alt) if (plug) typec_altmode_set_drvdata(plug, dp); - dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; - schedule_work(&dp->work); + if (!alt->mode_selection) { + dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; + schedule_work(&dp->work); + } return 0; } diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c index 1b475b1d98e7..32250b94262a 100644 --- a/drivers/usb/typec/altmodes/thunderbolt.c +++ b/drivers/usb/typec/altmodes/thunderbolt.c @@ -39,28 +39,7 @@ 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); + return typec_altmode_enter(tbt->alt, &tbt->enter_vdo); } static void tbt_altmode_work(struct work_struct *work) @@ -112,7 +91,7 @@ static void tbt_altmode_work(struct work_struct *work) return; disable_plugs: - for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) { + for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) { if (tbt->plug[i]) typec_altmode_put_plug(tbt->plug[i]); @@ -143,7 +122,7 @@ static int tbt_enter_modes_ordered(struct typec_altmode *alt) 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) { + for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) { if (tbt->plug[i]) typec_altmode_put_plug(tbt->plug[i]); @@ -307,7 +286,7 @@ static int tbt_altmode_probe(struct typec_altmode *alt) typec_altmode_set_drvdata(alt, tbt); typec_altmode_set_ops(alt, &tbt_altmode_ops); - if (tbt_ready(alt)) { + if (!alt->mode_selection && 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]) @@ -324,7 +303,7 @@ 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) { + for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) { if (tbt->plug[i]) typec_altmode_put_plug(tbt->plug[i]); } @@ -337,6 +316,7 @@ static bool tbt_ready(struct typec_altmode *alt) { struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt); struct typec_altmode *plug; + u32 vdo; if (tbt->cable) return true; @@ -351,10 +331,10 @@ static bool tbt_ready(struct typec_altmode *alt) */ for (int i = 0; i < TYPEC_PLUG_SOP_PP + 1; i++) { plug = typec_altmode_get_plug(tbt->alt, i); - if (IS_ERR(plug)) + if (!plug) continue; - if (!plug || plug->svid != USB_TYPEC_TBT_SID) + if (plug->svid != USB_TYPEC_TBT_SID) break; plug->desc = "Thunderbolt3"; @@ -364,6 +344,26 @@ static bool tbt_ready(struct typec_altmode *alt) tbt->plug[i] = plug; } + vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1); + vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0; + vdo |= TBT_MODE; + plug = tbt->plug[TYPEC_PLUG_SOP_P]; + + 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 true; } diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 0ae0a5ee3fae..2e8ae1d2faf9 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1516,8 +1516,7 @@ static int anx7411_i2c_probe(struct i2c_client *client) INIT_WORK(&plat->work, anx7411_work_func); plat->workqueue = alloc_workqueue("anx7411_work", - WQ_FREEZABLE | - WQ_MEM_RECLAIM, + WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU, 1); if (!plat->workqueue) { dev_err(dev, "fail to create work queue\n"); diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index ae90688d23e4..e84b134a3381 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -445,14 +445,33 @@ static struct attribute *typec_attrs[] = { &dev_attr_description.attr, NULL }; -ATTRIBUTE_GROUPS(typec); + +static umode_t typec_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + if (is_typec_partner_altmode(kobj_to_dev(kobj))) + return attr->mode; + return 0; +} + +static const struct attribute_group typec_group = { + .is_visible = typec_is_visible, + .attrs = typec_attrs, +}; + +static const struct attribute_group *typec_groups[] = { + &typec_group, + NULL +}; static int typec_match(struct device *dev, const struct device_driver *driver) { - struct typec_altmode_driver *drv = to_altmode_driver(driver); + const struct typec_altmode_driver *drv = to_altmode_driver(driver); struct typec_altmode *altmode = to_typec_altmode(dev); const struct typec_device_id *id; + if (!is_typec_partner_altmode(dev)) + return 0; + for (id = drv->id_table; id->svid; id++) if (id->svid == altmode->svid) return 1; @@ -463,6 +482,9 @@ static int typec_uevent(const struct device *dev, struct kobj_uevent_env *env) { const struct typec_altmode *altmode = to_typec_altmode(dev); + if (!is_typec_partner_altmode(dev)) + return 0; + if (add_uevent_var(env, "SVID=%04X", altmode->svid)) return -ENOMEM; @@ -547,3 +569,4 @@ const struct bus_type typec_bus = { .probe = typec_probe, .remove = typec_remove, }; +EXPORT_SYMBOL_GPL(typec_bus); diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h index 643b8c81786d..7df5deb1dd3a 100644 --- a/drivers/usb/typec/bus.h +++ b/drivers/usb/typec/bus.h @@ -5,7 +5,6 @@ #include <linux/usb/typec_altmode.h> -struct bus_type; struct typec_mux; struct typec_retimer; @@ -28,9 +27,4 @@ struct altmode { #define to_altmode(d) container_of(d, struct altmode, adev) -extern const struct bus_type typec_bus; -extern const struct device_type typec_altmode_dev_type; - -#define is_typec_altmode(_dev_) (_dev_->type == &typec_altmode_dev_type) - #endif /* __USB_TYPEC_ALTMODE_H__ */ diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 9c76c3d0c6cf..0977581ad1b6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -235,7 +235,7 @@ static int altmode_match(struct device *dev, const void *data) struct typec_altmode *adev = to_typec_altmode(dev); const struct typec_device_id *id = data; - if (!is_typec_altmode(dev)) + if (!is_typec_port_altmode(dev)) return 0; return (adev->svid == id->svid); @@ -445,11 +445,88 @@ svid_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(svid); +static int increment_duplicated_priority(struct device *dev, void *data) +{ + if (is_typec_port_altmode(dev)) { + struct typec_altmode **alt_target = (struct typec_altmode **)data; + struct typec_altmode *alt = to_typec_altmode(dev); + + if (alt != *alt_target && alt->priority == (*alt_target)->priority) { + alt->priority++; + *alt_target = alt; + return 1; + } + } + return 0; +} + +static int find_duplicated_priority(struct device *dev, void *data) +{ + if (is_typec_port_altmode(dev)) { + struct typec_altmode **alt_target = (struct typec_altmode **)data; + struct typec_altmode *alt = to_typec_altmode(dev); + + if (alt != *alt_target && alt->priority == (*alt_target)->priority) + return 1; + } + return 0; +} + +static int typec_mode_set_priority(struct typec_altmode *alt, const u8 priority) +{ + struct typec_port *port = to_typec_port(alt->dev.parent); + const u8 old_priority = alt->priority; + int res = 1; + + alt->priority = priority; + while (res) { + res = device_for_each_child(&port->dev, &alt, find_duplicated_priority); + if (res) { + alt->priority++; + if (alt->priority == 0) { + alt->priority = old_priority; + return -EOVERFLOW; + } + } + } + + res = 1; + alt->priority = priority; + while (res) + res = device_for_each_child(&port->dev, &alt, + increment_duplicated_priority); + + return 0; +} + +static ssize_t priority_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 val; + int err = kstrtou8(buf, 10, &val); + + if (!err) + err = typec_mode_set_priority(to_typec_altmode(dev), val); + + if (!err) + return size; + return err; +} + +static ssize_t priority_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", to_typec_altmode(dev)->priority); +} +static DEVICE_ATTR_RW(priority); + static struct attribute *typec_altmode_attrs[] = { &dev_attr_active.attr, &dev_attr_mode.attr, &dev_attr_svid.attr, &dev_attr_vdo.attr, + &dev_attr_priority.attr, NULL }; @@ -457,11 +534,17 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj)); + struct typec_port *port = typec_altmode2port(adev); - if (attr == &dev_attr_active.attr) - if (!is_typec_port(adev->dev.parent) && - (!adev->ops || !adev->ops->activate)) - return 0444; + if (attr == &dev_attr_active.attr) { + if (!is_typec_port(adev->dev.parent)) { + if (!port->mode_control || !adev->ops || !adev->ops->activate) + return 0444; + } + } else if (attr == &dev_attr_priority.attr) { + if (!is_typec_port(adev->dev.parent) || !port->mode_control) + return 0; + } return attr->mode; } @@ -532,22 +615,38 @@ static void typec_altmode_release(struct device *dev) kfree(alt); } -const struct device_type typec_altmode_dev_type = { - .name = "typec_alternate_mode", +const struct device_type typec_port_altmode_dev_type = { + .name = "typec_port_alternate_mode", .groups = typec_altmode_groups, .release = typec_altmode_release, }; +EXPORT_SYMBOL_GPL(typec_port_altmode_dev_type); + +const struct device_type typec_plug_altmode_dev_type = { + .name = "typec_plug_alternate_mode", + .groups = typec_altmode_groups, + .release = typec_altmode_release, +}; +EXPORT_SYMBOL_GPL(typec_plug_altmode_dev_type); + +const struct device_type typec_partner_altmode_dev_type = { + .name = "typec_partner_alternate_mode", + .groups = typec_altmode_groups, + .release = typec_altmode_release, +}; +EXPORT_SYMBOL_GPL(typec_partner_altmode_dev_type); static struct typec_altmode * typec_register_altmode(struct device *parent, - const struct typec_altmode_desc *desc) + const struct typec_altmode_desc *desc, + const struct device_type *type) { unsigned int id = altmode_id_get(parent); bool is_port = is_typec_port(parent); struct altmode *alt; int ret; - alt = kzalloc(sizeof(*alt), GFP_KERNEL); + alt = kzalloc_obj(*alt); if (!alt) { altmode_id_remove(parent, id); return ERR_PTR(-ENOMEM); @@ -556,6 +655,7 @@ typec_register_altmode(struct device *parent, alt->adev.svid = desc->svid; alt->adev.mode = desc->mode; alt->adev.vdo = desc->vdo; + alt->adev.mode_selection = desc->mode_selection; alt->roles = desc->roles; alt->id = id; @@ -575,7 +675,7 @@ typec_register_altmode(struct device *parent, alt->adev.dev.parent = parent; alt->adev.dev.groups = alt->groups; - alt->adev.dev.type = &typec_altmode_dev_type; + alt->adev.dev.type = type; dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id); get_device(alt->adev.dev.parent); @@ -584,13 +684,7 @@ typec_register_altmode(struct device *parent, if (!is_port) typec_altmode_set_partner(alt); - /* The partners are bind to drivers */ - if (is_typec_partner(parent)) - alt->adev.dev.bus = &typec_bus; - - /* Plug alt modes need a class to generate udev events. */ - if (is_typec_plug(parent)) - alt->adev.dev.class = &typec_class; + alt->adev.dev.bus = &typec_bus; ret = device_register(&alt->adev.dev); if (ret) { @@ -963,7 +1057,7 @@ struct typec_altmode * typec_partner_register_altmode(struct typec_partner *partner, const struct typec_altmode_desc *desc) { - return typec_register_altmode(&partner->dev, desc); + return typec_register_altmode(&partner->dev, desc, &typec_partner_altmode_dev_type); } EXPORT_SYMBOL_GPL(typec_partner_register_altmode); @@ -1015,7 +1109,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, struct typec_partner *partner; int ret; - partner = kzalloc(sizeof(*partner), GFP_KERNEL); + partner = kzalloc_obj(*partner); if (!partner) return ERR_PTR(-ENOMEM); @@ -1052,9 +1146,11 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner->usb_mode = USB_MODE_USB3; } + mutex_lock(&port->partner_link_lock); ret = device_register(&partner->dev); if (ret) { dev_err(&port->dev, "failed to register partner (%d)\n", ret); + mutex_unlock(&port->partner_link_lock); put_device(&partner->dev); return ERR_PTR(ret); } @@ -1063,6 +1159,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, typec_partner_link_device(partner, port->usb2_dev); if (port->usb3_dev) typec_partner_link_device(partner, port->usb3_dev); + mutex_unlock(&port->partner_link_lock); return partner; } @@ -1083,12 +1180,18 @@ void typec_unregister_partner(struct typec_partner *partner) port = to_typec_port(partner->dev.parent); - if (port->usb2_dev) + mutex_lock(&port->partner_link_lock); + if (port->usb2_dev) { typec_partner_unlink_device(partner, port->usb2_dev); - if (port->usb3_dev) + port->usb2_dev = NULL; + } + if (port->usb3_dev) { typec_partner_unlink_device(partner, port->usb3_dev); + port->usb3_dev = NULL; + } device_unregister(&partner->dev); + mutex_unlock(&port->partner_link_lock); } EXPORT_SYMBOL_GPL(typec_unregister_partner); @@ -1184,7 +1287,7 @@ struct typec_altmode * typec_plug_register_altmode(struct typec_plug *plug, const struct typec_altmode_desc *desc) { - return typec_register_altmode(&plug->dev, desc); + return typec_register_altmode(&plug->dev, desc, &typec_plug_altmode_dev_type); } EXPORT_SYMBOL_GPL(typec_plug_register_altmode); @@ -1206,7 +1309,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, char name[8]; int ret; - plug = kzalloc(sizeof(*plug), GFP_KERNEL); + plug = kzalloc_obj(*plug); if (!plug) return ERR_PTR(-ENOMEM); @@ -1359,7 +1462,7 @@ struct typec_cable *typec_register_cable(struct typec_port *port, struct typec_cable *cable; int ret; - cable = kzalloc(sizeof(*cable), GFP_KERNEL); + cable = kzalloc_obj(*cable); if (!cable) return ERR_PTR(-ENOMEM); @@ -2041,10 +2144,11 @@ static struct typec_partner *typec_get_partner(struct typec_port *port) 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 typec_partner *partner; struct usb_device *udev = to_usb_device(dev); enum usb_mode usb_mode; + mutex_lock(&port->partner_link_lock); if (udev->speed < USB_SPEED_SUPER) { usb_mode = USB_MODE_USB2; port->usb2_dev = dev; @@ -2053,18 +2157,22 @@ static void typec_partner_attach(struct typec_connector *con, struct device *dev port->usb3_dev = dev; } + partner = typec_get_partner(port); if (partner) { typec_partner_set_usb_mode(partner, usb_mode); typec_partner_link_device(partner, dev); put_device(&partner->dev); } + mutex_unlock(&port->partner_link_lock); } static void typec_partner_deattach(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 typec_partner *partner; + mutex_lock(&port->partner_link_lock); + partner = typec_get_partner(port); if (partner) { typec_partner_unlink_device(partner, dev); put_device(&partner->dev); @@ -2074,6 +2182,7 @@ static void typec_partner_deattach(struct typec_connector *con, struct device *d port->usb2_dev = NULL; else if (port->usb3_dev == dev) port->usb3_dev = NULL; + mutex_unlock(&port->partner_link_lock); } /** @@ -2106,6 +2215,19 @@ void typec_set_data_role(struct typec_port *port, enum typec_data_role role) EXPORT_SYMBOL_GPL(typec_set_data_role); /** + * typec_get_data_role - Get port data role + * @port: The USB Type-C Port to query + * + * This routine is used by the altmode drivers to determine if the port is the + * DFP before issuing Enter Mode + */ +enum typec_data_role typec_get_data_role(struct typec_port *port) +{ + return port->data_role; +} +EXPORT_SYMBOL_GPL(typec_get_data_role); + +/** * typec_set_pwr_role - Report power role change * @port: The USB Type-C Port where the role was changed * @role: The new data role @@ -2454,6 +2576,7 @@ typec_port_register_altmode(struct typec_port *port, struct typec_altmode *adev; struct typec_mux *mux; struct typec_retimer *retimer; + int ret; mux = typec_mux_get(&port->dev); if (IS_ERR(mux)) @@ -2465,13 +2588,19 @@ typec_port_register_altmode(struct typec_port *port, return ERR_CAST(retimer); } - adev = typec_register_altmode(&port->dev, desc); + adev = typec_register_altmode(&port->dev, desc, &typec_port_altmode_dev_type); if (IS_ERR(adev)) { typec_retimer_put(retimer); typec_mux_put(mux); } else { to_altmode(adev)->mux = mux; to_altmode(adev)->retimer = retimer; + + ret = typec_mode_set_priority(adev, 0); + if (ret) { + typec_unregister_altmode(adev); + return ERR_PTR(ret); + } } return adev; @@ -2570,7 +2699,7 @@ struct typec_port *typec_register_port(struct device *parent, int ret; int id; - port = kzalloc(sizeof(*port), GFP_KERNEL); + port = kzalloc_obj(*port); if (!port) return ERR_PTR(-ENOMEM); @@ -2614,6 +2743,7 @@ struct typec_port *typec_register_port(struct device *parent, ida_init(&port->mode_ids); mutex_init(&port->port_type_lock); + mutex_init(&port->partner_link_lock); port->id = id; port->ops = cap->ops; @@ -2665,6 +2795,7 @@ struct typec_port *typec_register_port(struct device *parent, } port->pd = cap->pd; + port->mode_control = !cap->no_mode_control; ret = device_add(&port->dev); if (ret) { diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index b3076a24ad2e..d3435936ee7c 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -9,6 +9,7 @@ struct typec_mux; struct typec_switch; struct usb_device; +struct mode_selection; struct typec_plug { struct device dev; @@ -39,6 +40,7 @@ struct typec_partner { u8 usb_capability; struct usb_power_delivery *pd; + struct mode_selection *sel; void (*attach)(struct typec_partner *partner, struct device *dev); void (*deattach)(struct typec_partner *partner, struct device *dev); @@ -59,8 +61,10 @@ struct typec_port { enum typec_port_type port_type; enum usb_mode usb_mode; struct mutex port_type_lock; + struct mutex partner_link_lock; enum typec_orientation orientation; + bool mode_control; struct typec_switch *sw; struct typec_mux *mux; struct typec_retimer *retimer; diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index 3ecc688dda82..3e39b800e6b5 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -15,6 +15,9 @@ #include <linux/usb/typec.h> #include <linux/delay.h> #include <linux/workqueue.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> +#include <linux/of_graph.h> #define HD3SS3220_REG_CN_STAT 0x08 #define HD3SS3220_REG_CN_STAT_CTRL 0x09 @@ -54,6 +57,11 @@ struct hd3ss3220 { struct delayed_work output_poll_work; enum usb_role role_state; bool poll; + + struct gpio_desc *id_gpiod; + int id_irq; + + struct regulator *vbus; }; static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode) @@ -196,6 +204,23 @@ static const struct typec_operations hd3ss3220_ops = { .port_type_set = hd3ss3220_port_type_set, }; +static void hd3ss3220_regulator_control(struct hd3ss3220 *hd3ss3220, bool on) +{ + int ret; + + if (regulator_is_enabled(hd3ss3220->vbus) == on) + return; + + if (on) + ret = regulator_enable(hd3ss3220->vbus); + else + ret = regulator_disable(hd3ss3220->vbus); + + if (ret) + dev_err(hd3ss3220->dev, + "vbus regulator %s failed: %d\n", on ? "disable" : "enable", ret); +} + static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220) { enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220); @@ -213,6 +238,9 @@ static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220) break; } + if (hd3ss3220->vbus && !hd3ss3220->id_gpiod) + hd3ss3220_regulator_control(hd3ss3220, role_state == USB_ROLE_HOST); + hd3ss3220->role_state = role_state; } @@ -319,13 +347,25 @@ static const struct regmap_config config = { .max_register = 0x0A, }; +static irqreturn_t hd3ss3220_id_isr(int irq, void *dev_id) +{ + struct hd3ss3220 *hd3ss3220 = dev_id; + int id; + + id = gpiod_get_value_cansleep(hd3ss3220->id_gpiod); + hd3ss3220_regulator_control(hd3ss3220, !id); + + return IRQ_HANDLED; +} + static int hd3ss3220_probe(struct i2c_client *client) { struct typec_capability typec_cap = { }; - struct hd3ss3220 *hd3ss3220; struct fwnode_handle *connector, *ep; - int ret; + struct hd3ss3220 *hd3ss3220; + struct regulator *vbus; unsigned int data; + int ret; hd3ss3220 = devm_kzalloc(&client->dev, sizeof(struct hd3ss3220), GFP_KERNEL); @@ -359,6 +399,49 @@ static int hd3ss3220_probe(struct i2c_client *client) goto err_put_fwnode; } + vbus = devm_of_regulator_get_optional(hd3ss3220->dev, + to_of_node(connector), + "vbus"); + if (IS_ERR(vbus) && vbus != ERR_PTR(-ENODEV)) { + ret = PTR_ERR(vbus); + dev_err(hd3ss3220->dev, "failed to get vbus: %d", ret); + goto err_put_fwnode; + } + + hd3ss3220->vbus = (vbus == ERR_PTR(-ENODEV) ? NULL : vbus); + + if (hd3ss3220->vbus) { + hd3ss3220->id_gpiod = devm_gpiod_get_optional(hd3ss3220->dev, + "id", + GPIOD_IN); + if (IS_ERR(hd3ss3220->id_gpiod)) { + ret = PTR_ERR(hd3ss3220->id_gpiod); + goto err_put_fwnode; + } + } + + if (hd3ss3220->id_gpiod) { + hd3ss3220->id_irq = gpiod_to_irq(hd3ss3220->id_gpiod); + if (hd3ss3220->id_irq < 0) { + ret = hd3ss3220->id_irq; + dev_err(hd3ss3220->dev, + "failed to get ID gpio: %d\n", + hd3ss3220->id_irq); + goto err_put_fwnode; + } + + ret = devm_request_threaded_irq(hd3ss3220->dev, + hd3ss3220->id_irq, NULL, + hd3ss3220_id_isr, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(hd3ss3220->dev), hd3ss3220); + if (ret < 0) { + dev_err(hd3ss3220->dev, "failed to get ID irq: %d\n", ret); + goto err_put_fwnode; + } + } + typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; typec_cap.driver_data = hd3ss3220; typec_cap.type = TYPEC_PORT_DRP; diff --git a/drivers/usb/typec/mode_selection.c b/drivers/usb/typec/mode_selection.c new file mode 100644 index 000000000000..cd376f67e445 --- /dev/null +++ b/drivers/usb/typec/mode_selection.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2025 Google LLC. + */ + +#include <linux/types.h> +#include <linux/list_sort.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/usb/typec_altmode.h> + +#include "class.h" + +/** + * struct mode_state - State tracking for a specific Type-C alternate mode + * @svid: Standard or Vendor ID of the Alternate Mode + * @priority: Mode priority + * @error: Outcome of the last attempt to enter the mode + * @list: List head to link this mode state into a prioritized list + */ +struct mode_state { + u16 svid; + u8 priority; + int error; + struct list_head list; +}; + +/** + * struct mode_selection - Manages the selection and state of Alternate Modes + * @mode_list: Prioritized list of available Alternate Modes + * @lock: Mutex to protect mode_list + * @work: Work structure + * @partner: Handle to the Type-C partner device + * @active_svid: svid of currently active mode + * @timeout: Timeout for a mode entry attempt, ms + * @delay: Delay between mode entry/exit attempts, ms + */ +struct mode_selection { + struct list_head mode_list; + /* Protects the mode_list*/ + struct mutex lock; + struct delayed_work work; + struct typec_partner *partner; + u16 active_svid; + unsigned int timeout; + unsigned int delay; +}; + +/** + * struct mode_order - Mode activation tracking + * @svid: Standard or Vendor ID of the Alternate Mode + * @enter: Flag indicating if the driver is currently attempting to enter or + * exit the mode + * @result: Outcome of the attempt to activate the mode + */ +struct mode_order { + u16 svid; + int enter; + int result; +}; + +static int activate_altmode(struct device *dev, void *data) +{ + if (is_typec_partner_altmode(dev)) { + struct typec_altmode *alt = to_typec_altmode(dev); + struct mode_order *order = (struct mode_order *)data; + + if (order->svid == alt->svid) { + if (alt->ops && alt->ops->activate) + order->result = alt->ops->activate(alt, order->enter); + else + order->result = -EOPNOTSUPP; + return 1; + } + } + return 0; +} + +static int mode_selection_activate(struct mode_selection *sel, + const u16 svid, const int enter) + + __must_hold(&sel->lock) +{ + struct mode_order order = {.svid = svid, .enter = enter, .result = -ENODEV}; + + /* + * The port driver may acquire its internal mutex during alternate mode + * activation. Since this is the same mutex that may be held during the + * execution of typec_altmode_state_update(), it is crucial to release + * sel->mutex before activation to avoid potential deadlock. + * Note that sel->mode_list must remain invariant throughout this unlocked + * interval. + */ + mutex_unlock(&sel->lock); + device_for_each_child(&sel->partner->dev, &order, activate_altmode); + mutex_lock(&sel->lock); + + return order.result; +} + +static void mode_list_clean(struct mode_selection *sel) +{ + struct mode_state *ms, *tmp; + + list_for_each_entry_safe(ms, tmp, &sel->mode_list, list) { + list_del(&ms->list); + kfree(ms); + } +} + +/** + * mode_selection_work_fn() - Alternate mode activation task + * @work: work structure + * + * - If the Alternate Mode currently prioritized at the top of the list is already + * active, the entire selection process is considered finished. + * - If a different Alternate Mode is currently active, the system must exit that + * active mode first before attempting any new entry. + * + * The function then checks the result of the attempt to entre the current mode, + * stored in the `ms->error` field: + * - if the attempt FAILED, the mode is deactivated and removed from the list. + * - `ms->error` value of 0 signifies that the mode has not yet been activated. + * + * Once successfully activated, the task is scheduled for subsequent entry after + * a timeout period. The alternate mode driver is expected to call back with the + * actual mode entry result via `typec_altmode_state_update()`. + */ +static void mode_selection_work_fn(struct work_struct *work) +{ + struct mode_selection *sel = container_of(work, + struct mode_selection, work.work); + struct mode_state *ms; + unsigned int delay = sel->delay; + int result; + + guard(mutex)(&sel->lock); + + ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list); + if (!ms) + return; + + if (sel->active_svid == ms->svid) { + dev_dbg(&sel->partner->dev, "%x altmode is active\n", ms->svid); + mode_list_clean(sel); + } else if (sel->active_svid != 0) { + result = mode_selection_activate(sel, sel->active_svid, 0); + if (result) + mode_list_clean(sel); + else + sel->active_svid = 0; + } else if (ms->error) { + dev_err(&sel->partner->dev, "%x: entry error %pe\n", + ms->svid, ERR_PTR(ms->error)); + mode_selection_activate(sel, ms->svid, 0); + list_del(&ms->list); + kfree(ms); + } else { + result = mode_selection_activate(sel, ms->svid, 1); + if (result) { + dev_err(&sel->partner->dev, "%x: activation error %pe\n", + ms->svid, ERR_PTR(result)); + list_del(&ms->list); + kfree(ms); + } else { + delay = sel->timeout; + ms->error = -ETIMEDOUT; + } + } + + if (!list_empty(&sel->mode_list)) + schedule_delayed_work(&sel->work, msecs_to_jiffies(delay)); +} + +void typec_altmode_state_update(struct typec_partner *partner, const u16 svid, + const int error) +{ + struct mode_selection *sel = partner->sel; + struct mode_state *ms; + + if (sel) { + mutex_lock(&sel->lock); + ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list); + if (ms && ms->svid == svid) { + ms->error = error; + if (cancel_delayed_work(&sel->work)) + schedule_delayed_work(&sel->work, 0); + } + if (!error) + sel->active_svid = svid; + else + sel->active_svid = 0; + mutex_unlock(&sel->lock); + } +} +EXPORT_SYMBOL_GPL(typec_altmode_state_update); + +static int compare_priorities(void *priv, + const struct list_head *a, const struct list_head *b) +{ + const struct mode_state *msa = container_of(a, struct mode_state, list); + const struct mode_state *msb = container_of(b, struct mode_state, list); + + if (msa->priority < msb->priority) + return -1; + return 1; +} + +static int altmode_add_to_list(struct device *dev, void *data) +{ + if (is_typec_partner_altmode(dev)) { + struct list_head *list = (struct list_head *)data; + struct typec_altmode *altmode = to_typec_altmode(dev); + const struct typec_altmode *pdev = typec_altmode_get_partner(altmode); + struct mode_state *ms; + + if (pdev && altmode->ops && altmode->ops->activate) { + ms = kzalloc_obj(*ms); + if (!ms) + return -ENOMEM; + ms->svid = pdev->svid; + ms->priority = pdev->priority; + INIT_LIST_HEAD(&ms->list); + list_add_tail(&ms->list, list); + } + } + return 0; +} + +int typec_mode_selection_start(struct typec_partner *partner, + const unsigned int delay, const unsigned int timeout) +{ + struct mode_selection *sel; + int ret; + + if (partner->usb_mode == USB_MODE_USB4) + return -EBUSY; + + if (partner->sel) + return -EALREADY; + + sel = kzalloc_obj(*sel); + if (!sel) + return -ENOMEM; + + INIT_LIST_HEAD(&sel->mode_list); + + ret = device_for_each_child(&partner->dev, &sel->mode_list, + altmode_add_to_list); + + if (ret || list_empty(&sel->mode_list)) { + mode_list_clean(sel); + kfree(sel); + return ret; + } + + list_sort(NULL, &sel->mode_list, compare_priorities); + sel->partner = partner; + sel->delay = delay; + sel->timeout = timeout; + mutex_init(&sel->lock); + INIT_DELAYED_WORK(&sel->work, mode_selection_work_fn); + schedule_delayed_work(&sel->work, msecs_to_jiffies(delay)); + partner->sel = sel; + + return 0; +} +EXPORT_SYMBOL_GPL(typec_mode_selection_start); + +void typec_mode_selection_delete(struct typec_partner *partner) +{ + struct mode_selection *sel = partner->sel; + + if (sel) { + partner->sel = NULL; + cancel_delayed_work_sync(&sel->work); + mode_list_clean(sel); + mutex_destroy(&sel->lock); + kfree(sel); + } +} +EXPORT_SYMBOL_GPL(typec_mode_selection_delete); diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 49926d6e72c7..db5e4a4c0a99 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -35,7 +35,9 @@ static int switch_fwnode_match(struct device *dev, const void *fwnode) static void *typec_switch_match(const struct fwnode_handle *fwnode, const char *id, void *data) { + struct typec_switch_dev **sw_devs = data; struct device *dev; + int i; /* * Device graph (OF graph) does not give any means to identify the @@ -56,6 +58,13 @@ static void *typec_switch_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, switch_fwnode_match); + /* Skip duplicates */ + for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) + if (to_typec_switch_dev(dev) == sw_devs[i]) { + put_device(dev); + return NULL; + } + return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -76,11 +85,12 @@ struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode) int err; int i; - sw = kzalloc(sizeof(*sw), GFP_KERNEL); + sw = kzalloc_obj(*sw); if (!sw) return ERR_PTR(-ENOMEM); - count = fwnode_connection_find_matches(fwnode, "orientation-switch", NULL, + count = fwnode_connection_find_matches(fwnode, "orientation-switch", + (void **)sw_devs, typec_switch_match, (void **)sw_devs, ARRAY_SIZE(sw_devs)); @@ -171,7 +181,7 @@ typec_switch_register(struct device *parent, if (!desc || !desc->set) return ERR_PTR(-EINVAL); - sw_dev = kzalloc(sizeof(*sw_dev), GFP_KERNEL); + sw_dev = kzalloc_obj(*sw_dev); if (!sw_dev) return ERR_PTR(-ENOMEM); @@ -214,7 +224,7 @@ int typec_switch_set(struct typec_switch *sw, sw_dev = sw->sw_devs[i]; ret = sw_dev->set(sw_dev, orientation); - if (ret) + if (ret && ret != -EOPNOTSUPP) return ret; } @@ -265,7 +275,9 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode) static void *typec_mux_match(const struct fwnode_handle *fwnode, const char *id, void *data) { + struct typec_mux_dev **mux_devs = data; struct device *dev; + int i; /* * Device graph (OF graph) does not give any means to identify the @@ -281,6 +293,14 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode, dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); + /* Skip duplicates */ + for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++) + if (to_typec_mux_dev(dev) == mux_devs[i]) { + put_device(dev); + return NULL; + } + + return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -301,12 +321,13 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode) int err; int i; - mux = kzalloc(sizeof(*mux), GFP_KERNEL); + mux = kzalloc_obj(*mux); if (!mux) return ERR_PTR(-ENOMEM); count = fwnode_connection_find_matches(fwnode, "mode-switch", - NULL, typec_mux_match, + (void **)mux_devs, + typec_mux_match, (void **)mux_devs, ARRAY_SIZE(mux_devs)); if (count <= 0) { @@ -378,7 +399,7 @@ int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state) mux_dev = mux->mux_devs[i]; ret = mux_dev->set(mux_dev, state); - if (ret) + if (ret && ret != -EOPNOTSUPP) return ret; } @@ -415,7 +436,7 @@ typec_mux_register(struct device *parent, const struct typec_mux_desc *desc) if (!desc || !desc->set) return ERR_PTR(-EINVAL); - mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL); + mux_dev = kzalloc_obj(*mux_dev); if (!mux_dev) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index 67381b4ef4f6..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 diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index 60879446da93..b4f599eb5053 100644 --- a/drivers/usb/typec/mux/Makefile +++ b/drivers/usb/typec/mux/Makefile @@ -6,6 +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/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c index f71dba8bf07c..c54e42c7e6a1 100644 --- a/drivers/usb/typec/mux/fsa4480.c +++ b/drivers/usb/typec/mux/fsa4480.c @@ -12,6 +12,7 @@ #include <linux/regmap.h> #include <linux/usb/typec_dp.h> #include <linux/usb/typec_mux.h> +#include <linux/regulator/consumer.h> #define FSA4480_DEVICE_ID 0x00 #define FSA4480_DEVICE_ID_VENDOR_ID GENMASK(7, 6) @@ -273,6 +274,10 @@ static int fsa4480_probe(struct i2c_client *client) if (IS_ERR(fsa->regmap)) return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n"); + ret = devm_regulator_get_enable_optional(dev, "vcc"); + if (ret && ret != -ENODEV) + return dev_err_probe(dev, ret, "Failed to get regulator\n"); + ret = regmap_read(fsa->regmap, FSA4480_DEVICE_ID, &val); if (ret) return dev_err_probe(dev, -ENODEV, "FSA4480 not found\n"); diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 65dda9183e6f..1698428654ab 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -754,7 +754,7 @@ static int pmc_usb_probe(struct platform_device *pdev) pmc->ipc = devm_intel_scu_ipc_dev_get(&pdev->dev); if (!pmc->ipc) - return -ENODEV; + return -EPROBE_DEFER; pmc->dev = &pdev->dev; diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c new file mode 100644 index 000000000000..1256252eceed --- /dev/null +++ b/drivers/usb/typec/mux/ps883x.c @@ -0,0 +1,500 @@ +// 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/pd.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> +#include <linux/usb/typec_tbt.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_ACTIVE_CABLE BIT(2) +#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 + +#define CONN_STATUS_2_TBT_CONNECTED BIT(0) +#define CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT BIT(4) +#define CONN_STATUS_2_USB4_CONNECTED BIT(7) + +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; + u8 cfg0; + u8 cfg1; + u8 cfg2; +}; + +static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0, + int cfg1, int cfg2) +{ + struct device *dev = &retimer->client->dev; + int ret; + + if (retimer->cfg0 == cfg0 && retimer->cfg1 == cfg1 && retimer->cfg2 == cfg2) + return 0; + + 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; + } + + retimer->cfg0 = cfg0; + retimer->cfg1 = cfg1; + retimer->cfg2 = cfg2; + + return 0; +} + +static int ps883x_set(struct ps883x_retimer *retimer, struct typec_retimer_state *state) +{ + struct typec_thunderbolt_data *tb_data; + const struct enter_usb_data *eudo_data; + int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT; + int cfg1 = 0x00; + int cfg2 = 0x00; + + if (retimer->orientation == TYPEC_ORIENTATION_REVERSE) + cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED; + + if (state->alt) { + switch (state->alt->svid) { + case USB_TYPEC_DP_SID: + cfg1 |= CONN_STATUS_1_DP_CONNECTED | + CONN_STATUS_1_DP_HPD_LEVEL; + + switch (state->mode) { + case TYPEC_DP_STATE_C: + cfg1 |= CONN_STATUS_1_DP_SINK_REQUESTED | + CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D; + fallthrough; + case TYPEC_DP_STATE_D: + cfg1 |= CONN_STATUS_0_USB_3_1_CONNECTED; + break; + default: /* MODE_E */ + break; + } + break; + case USB_TYPEC_TBT_SID: + tb_data = state->data; + + /* Unconditional */ + cfg2 |= CONN_STATUS_2_TBT_CONNECTED; + + if (tb_data->cable_mode & TBT_CABLE_ACTIVE_PASSIVE) + cfg0 |= CONN_STATUS_0_ACTIVE_CABLE; + + if (tb_data->enter_vdo & TBT_ENTER_MODE_UNI_DIR_LSRX) + cfg2 |= CONN_STATUS_2_TBT_UNIDIR_LSRX_ACT_LT; + break; + default: + dev_err(&retimer->client->dev, "Got unsupported SID: 0x%x\n", + state->alt->svid); + return -EOPNOTSUPP; + } + } else { + switch (state->mode) { + case TYPEC_STATE_SAFE: + /* USB2 pins don't even go through this chip */ + case TYPEC_MODE_USB2: + break; + case TYPEC_STATE_USB: + case TYPEC_MODE_USB3: + cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED; + break; + case TYPEC_MODE_USB4: + eudo_data = state->data; + + cfg2 |= CONN_STATUS_2_USB4_CONNECTED; + + if (FIELD_GET(EUDO_CABLE_TYPE_MASK, eudo_data->eudo) != EUDO_CABLE_TYPE_PASSIVE) + cfg0 |= CONN_STATUS_0_ACTIVE_CABLE; + break; + default: + dev_err(&retimer->client->dev, "Got unsupported mode: %lu\n", + state->mode); + 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 = regmap_assign_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, + CONN_STATUS_0_ORIENTATION_REVERSED, + orientation == TYPEC_ORIENTATION_REVERSE); + if (ret) + dev_err(&retimer->client->dev, "failed to set orientation: %d\n", ret); + } + + 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); + ret = ps883x_set(retimer, state); + 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; + } + + i2c_set_clientdata(client, retimer); + 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 index b4f45c217b59..3c1a4551c2fb 100644 --- a/drivers/usb/typec/mux/tusb1046.c +++ b/drivers/usb/typec/mux/tusb1046.c @@ -129,7 +129,7 @@ static int tusb1046_i2c_probe(struct i2c_client *client) priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) - return dev_err_probe(dev, -ENOMEM, "failed to allocate driver data\n"); + return -ENOMEM; priv->client = client; diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c index d78c04a421bc..766c76d63328 100644 --- a/drivers/usb/typec/pd.c +++ b/drivers/usb/typec/pd.c @@ -360,6 +360,84 @@ static const struct device_type sink_pps_type = { }; /* -------------------------------------------------------------------------- */ +/* Standard Power Range (SPR) Adjustable Voltage Supply (AVS) */ + +static ssize_t +spr_avs_9v_to_15v_max_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umA\n", + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(to_pdo(dev)->pdo)); +} + +static ssize_t +spr_avs_15v_to_20v_max_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umA\n", + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(to_pdo(dev)->pdo)); +} + +static ssize_t +spr_avs_src_peak_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", + pdo_spr_avs_apdo_src_peak_current(to_pdo(dev)->pdo)); +} + +static struct device_attribute spr_avs_9v_to_15v_max_current_attr = { + .attr = { + .name = "maximum_current_9V_to_15V", + .mode = 0444, + }, + .show = spr_avs_9v_to_15v_max_current_show, +}; + +static struct device_attribute spr_avs_15v_to_20v_max_current_attr = { + .attr = { + .name = "maximum_current_15V_to_20V", + .mode = 0444, + }, + .show = spr_avs_15v_to_20v_max_current_show, +}; + +static struct device_attribute spr_avs_src_peak_current_attr = { + .attr = { + .name = "peak_current", + .mode = 0444, + }, + .show = spr_avs_src_peak_current_show, +}; + +static struct attribute *source_spr_avs_attrs[] = { + &spr_avs_9v_to_15v_max_current_attr.attr, + &spr_avs_15v_to_20v_max_current_attr.attr, + &spr_avs_src_peak_current_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(source_spr_avs); + +static const struct device_type source_spr_avs_type = { + .name = "pdo", + .release = pdo_release, + .groups = source_spr_avs_groups, +}; + +static struct attribute *sink_spr_avs_attrs[] = { + &spr_avs_9v_to_15v_max_current_attr.attr, + &spr_avs_15v_to_20v_max_current_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(sink_spr_avs); + +static const struct device_type sink_spr_avs_type = { + .name = "pdo", + .release = pdo_release, + .groups = sink_spr_avs_groups, +}; + +/* -------------------------------------------------------------------------- */ static const char * const supply_name[] = { [PDO_TYPE_FIXED] = "fixed_supply", @@ -368,7 +446,8 @@ static const char * const supply_name[] = { }; static const char * const apdo_supply_name[] = { - [APDO_TYPE_PPS] = "programmable_supply", + [APDO_TYPE_PPS] = "programmable_supply", + [APDO_TYPE_SPR_AVS] = "spr_adjustable_voltage_supply", }; static const struct device_type *source_type[] = { @@ -378,7 +457,8 @@ static const struct device_type *source_type[] = { }; static const struct device_type *source_apdo_type[] = { - [APDO_TYPE_PPS] = &source_pps_type, + [APDO_TYPE_PPS] = &source_pps_type, + [APDO_TYPE_SPR_AVS] = &source_spr_avs_type, }; static const struct device_type *sink_type[] = { @@ -388,7 +468,8 @@ static const struct device_type *sink_type[] = { }; static const struct device_type *sink_apdo_type[] = { - [APDO_TYPE_PPS] = &sink_pps_type, + [APDO_TYPE_PPS] = &sink_pps_type, + [APDO_TYPE_SPR_AVS] = &sink_spr_avs_type, }; /* REVISIT: Export when EPR_*_Capabilities need to be supported. */ @@ -399,7 +480,7 @@ static int add_pdo(struct usb_power_delivery_capabilities *cap, u32 pdo, int pos struct pdo *p; int ret; - p = kzalloc(sizeof(*p), GFP_KERNEL); + p = kzalloc_obj(*p); if (!p) return -ENOMEM; @@ -407,8 +488,12 @@ static int add_pdo(struct usb_power_delivery_capabilities *cap, u32 pdo, int pos p->object_position = position; if (pdo_type(pdo) == PDO_TYPE_APDO) { - /* FIXME: Only PPS supported for now! Skipping others. */ - if (pdo_apdo_type(pdo) > APDO_TYPE_PPS) { + /* + * FIXME: Only PPS, SPR_AVS supported for now! + * Skipping others. + */ + if (pdo_apdo_type(pdo) != APDO_TYPE_PPS && + pdo_apdo_type(pdo) != APDO_TYPE_SPR_AVS) { dev_warn(&cap->dev, "Unknown APDO type. PDO 0x%08x\n", pdo); kfree(p); return 0; @@ -484,7 +569,7 @@ usb_power_delivery_register_capabilities(struct usb_power_delivery *pd, int ret; int i; - cap = kzalloc(sizeof(*cap), GFP_KERNEL); + cap = kzalloc_obj(*cap); if (!cap) return ERR_PTR(-ENOMEM); @@ -612,7 +697,7 @@ usb_power_delivery_register(struct device *parent, struct usb_power_delivery_des struct usb_power_delivery *pd; int ret; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); + pd = kzalloc_obj(*pd); if (!pd) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c index d42da5720a25..cdbb7c11d714 100644 --- a/drivers/usb/typec/port-mapper.c +++ b/drivers/usb/typec/port-mapper.c @@ -8,6 +8,7 @@ #include <linux/acpi.h> #include <linux/component.h> +#include <linux/thunderbolt.h> #include <linux/usb.h> #include "class.h" @@ -36,6 +37,11 @@ struct each_port_arg { struct component_match *match; }; +static int usb4_port_compare(struct device *dev, void *fwnode) +{ + return usb4_usb3_port_match(dev, fwnode); +} + static int typec_port_compare(struct device *dev, void *fwnode) { return device_match_fwnode(dev, fwnode); @@ -51,9 +57,22 @@ static int typec_port_match(struct device *dev, void *data) if (con_adev == adev) return 0; - if (con_adev->pld_crc == adev->pld_crc) + if (con_adev->pld_crc == adev->pld_crc) { + struct fwnode_handle *adev_fwnode = acpi_fwnode_handle(adev); + component_match_add(&arg->port->dev, &arg->match, typec_port_compare, - acpi_fwnode_handle(adev)); + adev_fwnode); + + /* + * If dev is USB 3.x port, it may have reference to the + * USB4 host interface in which case we can also link the + * Type-C port with the USB4 port. + */ + if (fwnode_property_present(adev_fwnode, "usb4-host-interface")) + component_match_add(&arg->port->dev, &arg->match, + usb4_port_compare, adev_fwnode); + } + return 0; } diff --git a/drivers/usb/typec/retimer.c b/drivers/usb/typec/retimer.c index b519fcf358ca..bfd80292b22a 100644 --- a/drivers/usb/typec/retimer.c +++ b/drivers/usb/typec/retimer.c @@ -110,7 +110,7 @@ typec_retimer_register(struct device *parent, const struct typec_retimer_desc *d if (!desc || !desc->set) return ERR_PTR(-EINVAL); - retimer = kzalloc(sizeof(*retimer), GFP_KERNEL); + retimer = kzalloc_obj(*retimer); if (!retimer) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig index 8cdd84ca5d6f..00baa7503d45 100644 --- a/drivers/usb/typec/tcpm/Kconfig +++ b/drivers/usb/typec/tcpm/Kconfig @@ -58,6 +58,8 @@ config TYPEC_FUSB302 tristate "Fairchild FUSB302 Type-C chip driver" depends on I2C depends on EXTCON || !EXTCON + depends on DRM || DRM=n + select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF help The Fairchild FUSB302 Type-C chip driver that works with Type-C Port Controller Manager to provide USB PD and USB diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index f15c63d3a8f4..889c4c29c1b8 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -5,6 +5,7 @@ * Fairchild FUSB302 Type-C Chip Driver */ +#include <drm/bridge/aux-bridge.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/errno.h> @@ -104,6 +105,7 @@ struct fusb302_chip { bool vconn_on; bool vbus_on; bool charge_on; + bool pd_rx_on; bool vbus_present; enum typec_cc_polarity cc_polarity; enum typec_cc_status cc1; @@ -841,6 +843,11 @@ static int tcpm_set_pd_rx(struct tcpc_dev *dev, bool on) int ret = 0; mutex_lock(&chip->lock); + if (chip->pd_rx_on == on) { + fusb302_log(chip, "pd is already %s", str_on_off(on)); + goto done; + } + ret = fusb302_pd_rx_flush(chip); if (ret < 0) { fusb302_log(chip, "cannot flush pd rx buffer, ret=%d", ret); @@ -863,6 +870,8 @@ static int tcpm_set_pd_rx(struct tcpc_dev *dev, bool on) str_on_off(on), ret); goto done; } + + chip->pd_rx_on = on; fusb302_log(chip, "pd := %s", str_on_off(on)); done: mutex_unlock(&chip->lock); @@ -1681,6 +1690,7 @@ static int fusb302_probe(struct i2c_client *client) { struct fusb302_chip *chip; struct i2c_adapter *adapter = client->adapter; + struct auxiliary_device *bridge_dev; struct device *dev = &client->dev; const char *name; int ret = 0; @@ -1739,6 +1749,13 @@ static int fusb302_probe(struct i2c_client *client) goto destroy_workqueue; } + bridge_dev = devm_drm_dp_hpd_bridge_alloc(chip->dev, to_of_node(chip->tcpc_dev.fwnode)); + if (IS_ERR(bridge_dev)) { + ret = PTR_ERR(bridge_dev); + dev_err_probe(chip->dev, ret, "failed to alloc bridge\n"); + goto destroy_workqueue; + } + chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev); if (IS_ERR(chip->tcpm_port)) { fwnode_handle_put(chip->tcpc_dev.fwnode); @@ -1747,9 +1764,9 @@ static int fusb302_probe(struct i2c_client *client) goto destroy_workqueue; } - ret = request_irq(chip->gpio_int_n_irq, fusb302_irq_intn, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "fsc_interrupt_int_n", chip); + ret = request_threaded_irq(chip->gpio_int_n_irq, NULL, fusb302_irq_intn, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + "fsc_interrupt_int_n", chip); if (ret < 0) { dev_err(dev, "cannot request IRQ for GPIO Int_N, ret=%d", ret); goto tcpm_unregister_port; @@ -1757,6 +1774,10 @@ static int fusb302_probe(struct i2c_client *client) enable_irq_wake(chip->gpio_int_n_irq); i2c_set_clientdata(client, chip); + ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev); + if (ret) + return ret; + return ret; tcpm_unregister_port: diff --git a/drivers/usb/typec/tcpm/maxim_contaminant.c b/drivers/usb/typec/tcpm/maxim_contaminant.c index 0cdda06592fd..af8da6dc60ae 100644 --- a/drivers/usb/typec/tcpm/maxim_contaminant.c +++ b/drivers/usb/typec/tcpm/maxim_contaminant.c @@ -188,6 +188,11 @@ static int max_contaminant_read_comparators(struct max_tcpci_chip *chip, u8 *ven if (ret < 0) return ret; + /* Disable low power mode */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL, + FIELD_PREP(CCLPMODESEL, + LOW_POWER_MODE_DISABLE)); + /* Sleep to allow comparators settle */ usleep_range(5000, 6000); ret = regmap_update_bits(regmap, TCPC_TCPC_CTRL, TCPC_TCPC_CTRL_ORIENTATION, PLUG_ORNT_CC1); @@ -324,6 +329,39 @@ static int max_contaminant_enable_dry_detection(struct max_tcpci_chip *chip) return 0; } +static int max_contaminant_enable_toggling(struct max_tcpci_chip *chip) +{ + struct regmap *regmap = chip->data.regmap; + int ret; + + /* Disable dry detection if enabled. */ + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL2, CCLPMODESEL, + FIELD_PREP(CCLPMODESEL, + LOW_POWER_MODE_DISABLE)); + if (ret) + return ret; + + ret = regmap_update_bits(regmap, TCPC_VENDOR_CC_CTRL1, CCCONNDRY, 0); + if (ret) + return ret; + + ret = max_tcpci_write8(chip, TCPC_ROLE_CTRL, TCPC_ROLE_CTRL_DRP | + FIELD_PREP(TCPC_ROLE_CTRL_CC1, + TCPC_ROLE_CTRL_CC_RD) | + FIELD_PREP(TCPC_ROLE_CTRL_CC2, + TCPC_ROLE_CTRL_CC_RD)); + if (ret) + return ret; + + ret = regmap_update_bits(regmap, TCPC_TCPC_CTRL, + TCPC_TCPC_CTRL_EN_LK4CONN_ALRT, + TCPC_TCPC_CTRL_EN_LK4CONN_ALRT); + if (ret) + return ret; + + return max_tcpci_write8(chip, TCPC_COMMAND, TCPC_CMD_LOOK4CONNECTION); +} + bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect_while_debounce, bool *cc_handled) { @@ -340,6 +378,12 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect if (ret < 0) return false; + if (cc_status & TCPC_CC_STATUS_TOGGLING) { + if (chip->contaminant_state == DETECTED) + return true; + return false; + } + if (chip->contaminant_state == NOT_DETECTED || chip->contaminant_state == SINK) { if (!disconnect_while_debounce) msleep(100); @@ -372,6 +416,12 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect max_contaminant_enable_dry_detection(chip); return true; } + + ret = max_contaminant_enable_toggling(chip); + if (ret) + dev_err(chip->dev, + "Failed to enable toggling, ret=%d", + ret); } } else if (chip->contaminant_state == DETECTED) { if (!(cc_status & TCPC_CC_STATUS_TOGGLING)) { @@ -379,6 +429,14 @@ bool max_contaminant_is_contaminant(struct max_tcpci_chip *chip, bool disconnect if (chip->contaminant_state == DETECTED) { max_contaminant_enable_dry_detection(chip); return true; + } else { + ret = max_contaminant_enable_toggling(chip); + if (ret) { + dev_err(chip->dev, + "Failed to enable toggling, ret=%d", + ret); + return true; + } } } } 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 18303b34594b..c8b1463e6e8b 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c @@ -567,7 +567,7 @@ int qcom_pmic_typec_pdphy_probe(struct platform_device *pdev, if (!res->nr_irqs || res->nr_irqs > PMIC_PDPHY_MAX_IRQS) return -EINVAL; - irq_data = devm_kzalloc(dev, sizeof(*irq_data) * res->nr_irqs, + irq_data = devm_kcalloc(dev, res->nr_irqs, sizeof(*irq_data), GFP_KERNEL); if (!irq_data) return -ENOMEM; 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 4fc83dcfae64..8051eaa46991 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c @@ -713,7 +713,7 @@ int qcom_pmic_typec_port_probe(struct platform_device *pdev, if (!res->nr_irqs || res->nr_irqs > PMIC_TYPEC_MAX_IRQS) return -EINVAL; - irq_data = devm_kzalloc(dev, sizeof(*irq_data) * res->nr_irqs, + irq_data = devm_kcalloc(dev, res->nr_irqs, sizeof(*irq_data), GFP_KERNEL); if (!irq_data) return -ENOMEM; diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 19ab6647af70..0148b8f50412 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -7,6 +7,7 @@ #include <linux/bitfield.h> #include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/i2c.h> @@ -17,6 +18,7 @@ #include <linux/usb/tcpci.h> #include <linux/usb/tcpm.h> #include <linux/usb/typec.h> +#include <linux/regulator/consumer.h> #define PD_RETRY_COUNT_DEFAULT 3 #define PD_RETRY_COUNT_3_0_OR_HIGHER 2 @@ -41,6 +43,7 @@ struct tcpci { struct tcpc_dev tcpc; struct tcpci_data *data; + struct gpio_desc *orientation_gpio; }; struct tcpci_chip { @@ -315,6 +318,10 @@ static int tcpci_set_orientation(struct tcpc_dev *tcpc, struct tcpci *tcpci = tcpc_to_tcpci(tcpc); unsigned int reg; + if (tcpci->orientation_gpio) + return gpiod_set_value_cansleep(tcpci->orientation_gpio, + orientation != TYPEC_ORIENTATION_NORMAL); + switch (orientation) { case TYPEC_ORIENTATION_NONE: /* We can't put a single output into high impedance */ @@ -902,9 +909,14 @@ EXPORT_SYMBOL_GPL(tcpci_unregister_port); static int tcpci_probe(struct i2c_client *client) { struct tcpci_chip *chip; + struct gpio_desc *orient_gpio = NULL; int err; u16 val = 0; + err = devm_regulator_get_enable_optional(&client->dev, "vdd"); + if (err && err != -ENODEV) + return dev_err_probe(&client->dev, err, "Failed to get regulator\n"); + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); if (!chip) return -ENOMEM; @@ -926,12 +938,23 @@ static int tcpci_probe(struct i2c_client *client) if (err < 0) return err; + if (err == 0) { + orient_gpio = devm_gpiod_get_optional(&client->dev, "orientation", + GPIOD_OUT_LOW); + if (IS_ERR(orient_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(orient_gpio), + "unable to acquire orientation gpio\n"); + err = !!orient_gpio; + } + chip->data.set_orientation = err; chip->tcpci = tcpci_register_port(&client->dev, &chip->data); if (IS_ERR(chip->tcpci)) return PTR_ERR(chip->tcpci); + chip->tcpci->orientation_gpio = orient_gpio; + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, _tcpci_irq, IRQF_SHARED | IRQF_ONESHOT, @@ -944,6 +967,8 @@ static int tcpci_probe(struct i2c_client *client) if (err < 0) goto unregister_port; + device_set_wakeup_capable(chip->tcpci->dev, true); + return 0; unregister_port: @@ -964,6 +989,36 @@ static void tcpci_remove(struct i2c_client *client) tcpci_unregister_port(chip->tcpci); } +static int tcpci_suspend(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct tcpci_chip *chip = i2c_get_clientdata(i2c); + int ret; + + if (device_may_wakeup(dev)) + ret = enable_irq_wake(i2c->irq); + else + ret = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, 0); + + return ret; +} + +static int tcpci_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct tcpci_chip *chip = i2c_get_clientdata(i2c); + int ret; + + if (device_may_wakeup(dev)) + ret = disable_irq_wake(i2c->irq); + else + ret = tcpci_write16(chip->tcpci, TCPC_ALERT_MASK, chip->tcpci->alert_mask); + + return ret; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(tcpci_pm_ops, tcpci_suspend, tcpci_resume); + static const struct i2c_device_id tcpci_id[] = { { "tcpci" }, { } @@ -982,6 +1037,7 @@ MODULE_DEVICE_TABLE(of, tcpci_of_match); static struct i2c_driver tcpci_i2c_driver = { .driver = { .name = "tcpci", + .pm = pm_sleep_ptr(&tcpci_pm_ops), .of_match_table = of_match_ptr(tcpci_of_match), }, .probe = tcpci_probe, diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/tcpci_maxim.h index 76270d5c2838..b314606eb0f6 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.h +++ b/drivers/usb/typec/tcpm/tcpci_maxim.h @@ -21,6 +21,7 @@ #define CCOVPDIS BIT(6) #define SBURPCTRL BIT(5) #define CCLPMODESEL GENMASK(4, 3) +#define LOW_POWER_MODE_DISABLE 0 #define ULTRA_LOW_POWER_MODE 1 #define CCRPCTRL GENMASK(2, 0) #define UA_1_SRC 1 @@ -59,6 +60,7 @@ struct max_tcpci_chip { struct tcpm_port *port; enum contamiant_state contaminant_state; bool veto_vconn_swap; + struct regulator *vbus_reg; }; static inline int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned int reg, u16 *val) diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index fd1b80593367..c0ee7e6959ed 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -10,6 +10,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/usb/pd.h> #include <linux/usb/tcpci.h> #include <linux/usb/tcpm.h> @@ -35,12 +36,6 @@ */ #define TCPC_RECEIVE_BUFFER_LEN 32 -#define MAX_BUCK_BOOST_SID 0x69 -#define MAX_BUCK_BOOST_OP 0xb9 -#define MAX_BUCK_BOOST_OFF 0 -#define MAX_BUCK_BOOST_SOURCE 0xa -#define MAX_BUCK_BOOST_SINK 0x5 - static const struct regmap_range max_tcpci_tcpci_range[] = { regmap_reg_range(0x00, 0x95) }; @@ -166,7 +161,8 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) return; } - if (count > sizeof(struct pd_message) || count + 1 > TCPC_RECEIVE_BUFFER_LEN) { + if (count > sizeof(struct pd_message) + 1 || + count + 1 > TCPC_RECEIVE_BUFFER_LEN) { dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d\n", count); return; } @@ -201,32 +197,49 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) tcpm_pd_receive(chip->port, &msg, rx_type); } +static int get_vbus_regulator_handle(struct max_tcpci_chip *chip) +{ + if (IS_ERR_OR_NULL(chip->vbus_reg)) { + chip->vbus_reg = devm_regulator_get_exclusive(chip->dev, + "vbus"); + if (IS_ERR_OR_NULL(chip->vbus_reg)) { + dev_err(chip->dev, + "Failed to get vbus regulator handle\n"); + return -ENODEV; + } + } + + return 0; +} + static int max_tcpci_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdata, bool source, bool sink) { struct max_tcpci_chip *chip = tdata_to_max_tcpci(tdata); - u8 buffer_source[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SOURCE}; - u8 buffer_sink[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_SINK}; - u8 buffer_none[2] = {MAX_BUCK_BOOST_OP, MAX_BUCK_BOOST_OFF}; - struct i2c_client *i2c = chip->client; int ret; - struct i2c_msg msgs[] = { - { - .addr = MAX_BUCK_BOOST_SID, - .flags = i2c->flags & I2C_M_TEN, - .len = 2, - .buf = source ? buffer_source : sink ? buffer_sink : buffer_none, - }, - }; - if (source && sink) { dev_err(chip->dev, "Both source and sink set\n"); return -EINVAL; } - ret = i2c_transfer(i2c->adapter, msgs, 1); + ret = get_vbus_regulator_handle(chip); + if (ret) { + /* + * Regulator is not necessary for sink only applications. Return + * success in cases where sink mode is being modified. + */ + return source ? ret : 1; + } - return ret < 0 ? ret : 1; + if (source) { + if (!regulator_is_enabled(chip->vbus_reg)) + ret = regulator_enable(chip->vbus_reg); + } else { + if (regulator_is_enabled(chip->vbus_reg)) + ret = regulator_disable(chip->vbus_reg); + } + + return ret < 0 ? ret : 1; } static void process_power_status(struct max_tcpci_chip *chip) @@ -420,21 +433,6 @@ static irqreturn_t max_tcpci_isr(int irq, void *dev_id) return IRQ_WAKE_THREAD; } -static int max_tcpci_init_alert(struct max_tcpci_chip *chip, struct i2c_client *client) -{ - int ret; - - ret = devm_request_threaded_irq(chip->dev, client->irq, max_tcpci_isr, max_tcpci_irq, - (IRQF_TRIGGER_LOW | IRQF_ONESHOT), dev_name(chip->dev), - chip); - - if (ret < 0) - return ret; - - enable_irq_wake(client->irq); - return 0; -} - static int max_tcpci_start_toggling(struct tcpci *tcpci, struct tcpci_data *tdata, enum typec_cc_status cc) { @@ -531,33 +529,64 @@ static int max_tcpci_probe(struct i2c_client *client) chip->port = tcpci_get_tcpm_port(chip->tcpci); - ret = max_tcpci_init_alert(chip, client); + ret = devm_request_threaded_irq(&client->dev, client->irq, max_tcpci_isr, max_tcpci_irq, + (IRQF_TRIGGER_LOW | IRQF_ONESHOT), dev_name(chip->dev), + chip); if (ret < 0) return dev_err_probe(&client->dev, ret, "IRQ initialization failed\n"); - device_init_wakeup(chip->dev, true); + ret = devm_device_init_wakeup(chip->dev); + if (ret) + return dev_err_probe(chip->dev, ret, "Failed to init wakeup\n"); + return 0; } +#ifdef CONFIG_PM_SLEEP +static int max_tcpci_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret = 0; + + if (client->irq && device_may_wakeup(dev)) + ret = disable_irq_wake(client->irq); + + return ret; +} + +static int max_tcpci_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret = 0; + + if (client->irq && device_may_wakeup(dev)) + ret = enable_irq_wake(client->irq); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(max_tcpci_pm_ops, max_tcpci_suspend, max_tcpci_resume); + static const struct i2c_device_id max_tcpci_id[] = { { "maxtcpc" }, { } }; MODULE_DEVICE_TABLE(i2c, max_tcpci_id); -#ifdef CONFIG_OF static const struct of_device_id max_tcpci_of_match[] = { { .compatible = "maxim,max33359", }, {}, }; MODULE_DEVICE_TABLE(of, max_tcpci_of_match); -#endif static struct i2c_driver max_tcpci_i2c_driver = { .driver = { .name = "maxtcpc", - .of_match_table = of_match_ptr(max_tcpci_of_match), + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = max_tcpci_of_match, + .pm = &max_tcpci_pm_ops, }, .probe = max_tcpci_probe, .id_table = max_tcpci_id, diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index 88c50b984e8a..4b3e4e22a82e 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -18,11 +18,6 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> -#define RT1711H_VID 0x29CF -#define RT1711H_PID 0x1711 -#define RT1711H_DID 0x2171 -#define RT1715_DID 0x2173 - #define RT1711H_PHYCTRL1 0x80 #define RT1711H_PHYCTRL2 0x81 @@ -55,7 +50,6 @@ struct rt1711h_chip_info { u32 rxdz_sel; - u16 did; bool enable_pd30_extended_message; }; @@ -301,35 +295,6 @@ static int rt1711h_sw_reset(struct rt1711h_chip *chip) return 0; } -static int rt1711h_check_revision(struct i2c_client *i2c, struct rt1711h_chip *chip) -{ - int ret; - - ret = i2c_smbus_read_word_data(i2c, TCPC_VENDOR_ID); - if (ret < 0) - return ret; - if (ret != RT1711H_VID) { - dev_err(&i2c->dev, "vid is not correct, 0x%04x\n", ret); - return -ENODEV; - } - ret = i2c_smbus_read_word_data(i2c, TCPC_PRODUCT_ID); - if (ret < 0) - return ret; - if (ret != RT1711H_PID) { - dev_err(&i2c->dev, "pid is not correct, 0x%04x\n", ret); - return -ENODEV; - } - ret = i2c_smbus_read_word_data(i2c, TCPC_BCD_DEV); - if (ret < 0) - return ret; - if (ret != chip->info->did) { - dev_err(&i2c->dev, "did is not correct, 0x%04x\n", ret); - return -ENODEV; - } - dev_dbg(&i2c->dev, "did is 0x%04x\n", ret); - return ret; -} - static int rt1711h_probe(struct i2c_client *client) { int ret; @@ -346,12 +311,6 @@ static int rt1711h_probe(struct i2c_client *client) chip->info = i2c_get_match_data(client); - ret = rt1711h_check_revision(client, chip); - if (ret < 0) { - dev_err(&client->dev, "check vid/pid fail\n"); - return ret; - } - chip->data.regmap = devm_regmap_init_i2c(client, &rt1711h_regmap_config); if (IS_ERR(chip->data.regmap)) @@ -406,16 +365,15 @@ static void rt1711h_remove(struct i2c_client *client) } static const struct rt1711h_chip_info rt1711h = { - .did = RT1711H_DID, }; static const struct rt1711h_chip_info rt1715 = { .rxdz_sel = RT1711H_BMCIO_RXDZSEL, - .did = RT1715_DID, .enable_pd30_extended_message = true, }; static const struct i2c_device_id rt1711h_id[] = { + { "et7304", (kernel_ulong_t)&rt1715 }, { "rt1711h", (kernel_ulong_t)&rt1711h }, { "rt1715", (kernel_ulong_t)&rt1715 }, {} @@ -423,6 +381,7 @@ static const struct i2c_device_id rt1711h_id[] = { MODULE_DEVICE_TABLE(i2c, rt1711h_id); static const struct of_device_id rt1711h_of_match[] = { + { .compatible = "etekmicro,et7304", .data = &rt1715 }, { .compatible = "richtek,rt1711h", .data = &rt1711h }, { .compatible = "richtek,rt1715", .data = &rt1715 }, {} diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 62ca4a0ec55b..55fee96d3342 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -12,6 +12,7 @@ #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/kthread.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/power_supply.h> @@ -61,12 +62,14 @@ S(SNK_WAIT_CAPABILITIES_TIMEOUT), \ S(SNK_NEGOTIATE_CAPABILITIES), \ S(SNK_NEGOTIATE_PPS_CAPABILITIES), \ + S(SNK_NEGOTIATE_SPR_AVS_CAPABILITIES), \ S(SNK_TRANSITION_SINK), \ S(SNK_TRANSITION_SINK_VBUS), \ S(SNK_READY), \ \ S(ACC_UNATTACHED), \ S(DEBUG_ACC_ATTACHED), \ + S(DEBUG_ACC_DEBOUNCE), \ S(AUDIO_ACC_ATTACHED), \ S(AUDIO_ACC_DEBOUNCE), \ \ @@ -187,7 +190,8 @@ S(STRUCTURED_VDMS), \ S(COUNTRY_INFO), \ S(COUNTRY_CODES), \ - S(REVISION_INFORMATION) + S(REVISION_INFORMATION), \ + S(GETTING_SINK_EXTENDED_CAPABILITIES) #define GENERATE_ENUM(e) e #define GENERATE_STRING(s) #s @@ -228,6 +232,7 @@ enum pd_msg_request { PD_MSG_DATA_SINK_CAP, PD_MSG_DATA_SOURCE_CAP, PD_MSG_DATA_REV, + PD_MSG_EXT_SINK_CAP_EXT }; enum adev_actions { @@ -304,6 +309,51 @@ struct pd_pps_data { bool active; }; +enum spr_avs_status { + SPR_AVS_UNKNOWN, + SPR_AVS_NOT_SUPPORTED, + SPR_AVS_SUPPORTED +}; + +static const char * const spr_avs_status_strings[] = { + [SPR_AVS_UNKNOWN] = "Unknown", + [SPR_AVS_SUPPORTED] = "Supported", + [SPR_AVS_NOT_SUPPORTED] = "Not Supported", +}; + +/* + * Standard Power Range Adjustable Voltage Supply (SPR - AVS) data + * @max_current_ma_9v_to_15v: Max current for 9V to 15V range derived from + * source cap & sink cap + * @max_current_ma_15v_to_20v: Max current for 15V to 20V range derived from + * source cap & sink cap + * @req_op_curr_ma: Requested operating current to the port partner acting as source + * @req_out_volt_mv: Requested output voltage to the port partner acting as source + * @max_out_volt_mv: Max SPR voltage supported by the port and the port partner + * @max_current_ma; MAX SPR current supported by the port and the port partner + * @port_partner_src_status: SPR AVS status of port partner acting as source + * @port_partner_src_pdo_index: PDO index of SPR AVS cap of the port partner + * acting as source. Valid only when + * port_partner_src_status is SPR_AVS_SUPPORTED. + * @port_snk_status: SPR AVS status of the local port acting as sink. + * @port_snk_pdo_index: PDO index of SPR AVS cap of local port acting as sink + * @active: True when the local port acting as the sink has negotiated SPR AVS + * with the partner acting as source. + */ +struct pd_spr_avs_data { + u32 max_current_ma_9v_to_15v; + u32 max_current_ma_15v_to_20v; + u32 req_op_curr_ma; + u32 req_out_volt_mv; + u32 max_out_volt_mv; + u32 max_current_ma; + enum spr_avs_status port_partner_src_status; + unsigned int port_partner_src_pdo_index; + enum spr_avs_status port_snk_status; + unsigned int port_snk_pdo_index; + bool active; +}; + struct pd_data { struct usb_power_delivery *pd; struct usb_power_delivery_capabilities *source_cap; @@ -313,6 +363,10 @@ struct pd_data { unsigned int operating_snk_mw; }; +#define PD_CAP_REV10 0x1 +#define PD_CAP_REV20 0x2 +#define PD_CAP_REV30 0x3 + struct pd_revision_info { u8 rev_major; u8 rev_minor; @@ -332,6 +386,47 @@ struct pd_timings { u32 snk_bc12_cmpletion_time; }; +/* Convert microwatt to watt */ +#define UW_TO_W(pow) ((pow) / 1000000) + +/* + * struct pd_identifier - Contains info about PD identifiers + * @vid: Vendor ID (assigned by USB-IF) + * @pid: Product ID (assigned by manufacturer) + * @xid: Value assigned by USB-IF for product + */ +struct pd_identifier { + u16 vid; + u16 pid; + u32 xid; +}; + +/* + * struct sink_caps_ext_data - Sink extended capability data + * @load_step: Indicates the load step slew rate. Value of 0 indicates 150mA/us + * & 1 indicates 500 mA/us + * @load_char: Snk overload characteristics + * @compliance: Types of sources the sink has been tested & certified on + * @modes: Charging caps & power sources supported + * @spr_min_pdp: Sink Minimum PDP for SPR mode (in Watts) + * @spr_op_pdp: Sink Operational PDP for SPR mode (in Watts) + * @spr_max_pdp: Sink Maximum PDP for SPR mode (in Watts) + */ +struct sink_caps_ext_data { + u8 load_step; + u16 load_char; + u8 compliance; + u8 modes; + u8 spr_min_pdp; + u8 spr_op_pdp; + u8 spr_max_pdp; +}; + +enum aug_req_type { + PD_PPS, + PD_SPR_AVS, +}; + struct tcpm_port { struct device *dev; @@ -494,9 +589,14 @@ struct tcpm_port { /* PPS */ struct pd_pps_data pps_data; - struct completion pps_complete; - bool pps_pending; - int pps_status; + + /* SPR AVS */ + struct pd_spr_avs_data spr_avs_data; + + /* Augmented supply request - PPS; SPR_AVS */ + struct completion aug_supply_req_complete; + bool aug_supply_req_pending; + int aug_supply_req_status; /* Alternate mode data */ struct pd_mode_data mode_data; @@ -580,6 +680,9 @@ struct tcpm_port { /* Indicates maximum (revision, version) supported */ struct pd_revision_info pd_rev; + + struct pd_identifier pd_ident; + struct sink_caps_ext_data sink_caps_ext; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -596,6 +699,15 @@ struct pd_rx_event { enum tcpm_transmit_type rx_sop_type; }; +struct altmode_vdm_event { + struct kthread_work work; + struct tcpm_port *port; + u32 header; + int cnt; + enum tcpm_transmit_type tx_sop_type; + u32 data[] __counted_by(cnt); +}; + static const char * const pd_rev[] = { [PD_REV10] = "rev1", [PD_REV20] = "rev2", @@ -620,9 +732,15 @@ static const char * const pd_rev[] = { (tcpm_cc_is_source((port)->cc2) && \ !tcpm_cc_is_source((port)->cc1))) -#define tcpm_port_is_debug(port) \ +#define tcpm_port_is_debug_source(port) \ (tcpm_cc_is_source((port)->cc1) && tcpm_cc_is_source((port)->cc2)) +#define tcpm_port_is_debug_sink(port) \ + (tcpm_cc_is_sink((port)->cc1) && tcpm_cc_is_sink((port)->cc2)) + +#define tcpm_port_is_debug(port) \ + (tcpm_port_is_debug_source(port) || tcpm_port_is_debug_sink(port)) + #define tcpm_port_is_audio(port) \ (tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_audio((port)->cc2)) @@ -710,7 +828,7 @@ static void _tcpm_log(struct tcpm_port *port, const char *fmt, va_list args) if (tcpm_log_full(port)) { port->logbuffer_head = max(port->logbuffer_head - 1, 0); - strcpy(tmpbuffer, "overflow"); + strscpy(tmpbuffer, "overflow"); } if (port->logbuffer_head < 0 || @@ -808,15 +926,28 @@ static void tcpm_log_source_caps(struct tcpm_port *port) case PDO_TYPE_APDO: if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) scnprintf(msg, sizeof(msg), - "%u-%u mV, %u mA", + "PPS %u-%u mV, %u mA", pdo_pps_apdo_min_voltage(pdo), pdo_pps_apdo_max_voltage(pdo), pdo_pps_apdo_max_current(pdo)); + else if (pdo_apdo_type(pdo) == APDO_TYPE_EPR_AVS) + scnprintf(msg, sizeof(msg), + "EPR AVS %u-%u mV %u W peak_current: %u", + pdo_epr_avs_apdo_min_voltage_mv(pdo), + pdo_epr_avs_apdo_max_voltage_mv(pdo), + pdo_epr_avs_apdo_pdp_w(pdo), + pdo_epr_avs_apdo_src_peak_current(pdo)); + else if (pdo_apdo_type(pdo) == APDO_TYPE_SPR_AVS) + scnprintf(msg, sizeof(msg), + "SPR AVS 9-15 V: %u mA 15-20 V: %u mA peak_current: %u", + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(pdo), + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo), + pdo_spr_avs_apdo_src_peak_current(pdo)); else - strcpy(msg, "undefined APDO"); + strscpy(msg, "undefined APDO"); break; default: - strcpy(msg, "undefined"); + strscpy(msg, "undefined"); break; } tcpm_log(port, " PDO %d: type %d, %s", @@ -1151,7 +1282,7 @@ static int tcpm_set_attached_state(struct tcpm_port *port, bool attached) port->data_role); } -static int tcpm_set_roles(struct tcpm_port *port, bool attached, +static int tcpm_set_roles(struct tcpm_port *port, bool attached, int state, enum typec_role role, enum typec_data_role data) { enum typec_orientation orientation; @@ -1188,7 +1319,7 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached, } } - ret = tcpm_mux_set(port, TYPEC_STATE_USB, usb_role, orientation); + ret = tcpm_mux_set(port, state, usb_role, orientation); if (ret < 0) return ret; @@ -1339,6 +1470,64 @@ static int tcpm_pd_send_sink_caps(struct tcpm_port *port) return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); } +static int tcpm_pd_send_sink_cap_ext(struct tcpm_port *port) +{ + u16 operating_snk_watt = port->operating_snk_mw / 1000; + struct sink_caps_ext_data *data = &port->sink_caps_ext; + struct pd_identifier *pd_ident = &port->pd_ident; + struct sink_caps_ext_msg skedb = {0}; + struct pd_message msg; + u8 data_obj_cnt; + + if (!port->self_powered) + data->spr_op_pdp = operating_snk_watt; + + /* + * SPR Sink Minimum PDP indicates the minimum power required to operate + * a sink device in its lowest level of functionality without requiring + * power from the battery. We can use the operating_snk_watt value to + * populate it, as operating_snk_watt indicates device's min operating + * power. + */ + data->spr_min_pdp = operating_snk_watt; + + if (data->spr_op_pdp < data->spr_min_pdp || + data->spr_max_pdp < data->spr_op_pdp) { + tcpm_log(port, + "Invalid PDP values, Min PDP:%u, Op PDP:%u, Max PDP:%u", + data->spr_min_pdp, data->spr_op_pdp, data->spr_max_pdp); + return -EOPNOTSUPP; + } + + memset(&msg, 0, sizeof(msg)); + skedb.vid = cpu_to_le16(pd_ident->vid); + skedb.pid = cpu_to_le16(pd_ident->pid); + skedb.xid = cpu_to_le32(pd_ident->xid); + skedb.skedb_ver = SKEDB_VER_1_0; + skedb.load_step = data->load_step; + skedb.load_char = cpu_to_le16(data->load_char); + skedb.compliance = data->compliance; + skedb.modes = data->modes; + skedb.spr_min_pdp = data->spr_min_pdp; + skedb.spr_op_pdp = data->spr_op_pdp; + skedb.spr_max_pdp = data->spr_max_pdp; + memcpy(msg.ext_msg.data, &skedb, sizeof(skedb)); + msg.ext_msg.header = PD_EXT_HDR_LE(sizeof(skedb), + 0, /* Denotes if request chunk */ + 0, /* Chunk Number */ + 1 /* Chunked */); + + data_obj_cnt = count_chunked_data_objs(sizeof(skedb)); + msg.header = cpu_to_le16(PD_HEADER(PD_EXT_SINK_CAP_EXT, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + data_obj_cnt, + 1 /* Denotes if ext header */)); + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + static void mod_tcpm_delayed_work(struct tcpm_port *port, unsigned int delay_ms) { if (delay_ms) { @@ -1608,18 +1797,59 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, mod_vdm_delayed_work(port, 0); } -static void tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header, - const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type) +static void tcpm_queue_vdm_work(struct kthread_work *work) { - if (port->state != SRC_READY && port->state != SNK_READY && - port->state != SRC_VDM_IDENTITY_REQUEST) - return; + struct altmode_vdm_event *event = container_of(work, + struct altmode_vdm_event, + work); + struct tcpm_port *port = event->port; mutex_lock(&port->lock); - tcpm_queue_vdm(port, header, data, cnt, tx_sop_type); + if (port->state != SRC_READY && port->state != SNK_READY && + port->state != SRC_VDM_IDENTITY_REQUEST) { + tcpm_log_force(port, "dropping altmode_vdm_event"); + goto port_unlock; + } + + tcpm_queue_vdm(port, event->header, event->data, event->cnt, event->tx_sop_type); + +port_unlock: + kfree(event); mutex_unlock(&port->lock); } +static int tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header, + const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type) +{ + struct altmode_vdm_event *event; + int ret = -ENOMEM; + + event = kzalloc_flex(*event, data, cnt); + if (!event) + goto err_event; + + kthread_init_work(&event->work, tcpm_queue_vdm_work); + event->cnt = cnt; + event->port = port; + event->header = header; + memcpy(event->data, data, sizeof(u32) * cnt); + event->tx_sop_type = tx_sop_type; + + ret = kthread_queue_work(port->wq, &event->work); + if (!ret) { + ret = -EBUSY; + goto err_data; + } + + return 0; + +err_data: + kfree(event); +err_event: + tcpm_log_force(port, "failed to queue altmode vdm, err:%d", ret); + return ret; +} + static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt) { u32 vdo = p[VDO_INDEX_IDH]; @@ -1730,7 +1960,7 @@ static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt, /* * PD3.0 Spec 6.4.4.3.2: The SVIDs are returned 2 per VDO (see Table * 6-43), and can be returned maximum 6 VDOs per response (see Figure - * 6-19). If the Respondersupports 12 or more SVID then the Discover + * 6-19). If the Responder supports 12 or more SVID then the Discover * SVIDs Command Shall be executed multiple times until a Discover * SVIDs VDO is returned ending either with a SVID value of 0x0000 in * the last part of the last VDO or with a VDO containing two SVIDs @@ -2361,17 +2591,21 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port, case ADEV_NONE: break; case ADEV_NOTIFY_USB_AND_QUEUE_VDM: - WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, NULL)); - typec_altmode_vdm(adev, p[0], &p[1], cnt); + if (rx_sop_type == TCPC_TX_SOP_PRIME) { + typec_cable_altmode_vdm(adev, TYPEC_PLUG_SOP_P, p[0], &p[1], cnt); + } else { + WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, NULL)); + typec_altmode_vdm(adev, p[0], &p[1], cnt); + } break; case ADEV_QUEUE_VDM: - if (response_tx_sop_type == TCPC_TX_SOP_PRIME) + if (rx_sop_type == TCPC_TX_SOP_PRIME) typec_cable_altmode_vdm(adev, TYPEC_PLUG_SOP_P, p[0], &p[1], cnt); else typec_altmode_vdm(adev, p[0], &p[1], cnt); break; case ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL: - if (response_tx_sop_type == TCPC_TX_SOP_PRIME) { + if (rx_sop_type == TCPC_TX_SOP_PRIME) { if (typec_cable_altmode_vdm(adev, TYPEC_PLUG_SOP_P, p[0], &p[1], cnt)) { int svdm_version = typec_get_cable_svdm_version( @@ -2830,8 +3064,7 @@ static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo) header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); - tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP); - return 0; + return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP); } static int tcpm_altmode_exit(struct typec_altmode *altmode) @@ -2847,8 +3080,7 @@ static int tcpm_altmode_exit(struct typec_altmode *altmode) header = VDO(altmode->svid, 1, svdm_version, CMD_EXIT_MODE); header |= VDO_OPOS(altmode->mode); - tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP); - return 0; + return tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP); } static int tcpm_altmode_vdm(struct typec_altmode *altmode, @@ -2856,9 +3088,7 @@ static int tcpm_altmode_vdm(struct typec_altmode *altmode, { struct tcpm_port *port = typec_altmode_get_drvdata(altmode); - tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP); - - return 0; + return tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP); } static const struct typec_altmode_ops tcpm_altmode_ops = { @@ -2882,8 +3112,7 @@ static int tcpm_cable_altmode_enter(struct typec_altmode *altmode, enum typec_pl header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); header |= VDO_OPOS(altmode->mode); - tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME); - return 0; + return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME); } static int tcpm_cable_altmode_exit(struct typec_altmode *altmode, enum typec_plug_index sop) @@ -2899,8 +3128,7 @@ static int tcpm_cable_altmode_exit(struct typec_altmode *altmode, enum typec_plu header = VDO(altmode->svid, 1, svdm_version, CMD_EXIT_MODE); header |= VDO_OPOS(altmode->mode); - tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP_PRIME); - return 0; + return tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP_PRIME); } static int tcpm_cable_altmode_vdm(struct typec_altmode *altmode, enum typec_plug_index sop, @@ -2908,9 +3136,7 @@ static int tcpm_cable_altmode_vdm(struct typec_altmode *altmode, enum typec_plug { struct tcpm_port *port = typec_altmode_get_drvdata(altmode); - tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP_PRIME); - - return 0; + return tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP_PRIME); } static const struct typec_cable_ops tcpm_cable_ops = { @@ -3120,6 +3346,7 @@ static void tcpm_pd_data_request(struct tcpm_port *port, switch (type) { case PD_DATA_SOURCE_CAP: + port->spr_avs_data.port_partner_src_status = SPR_AVS_UNKNOWN; for (i = 0; i < cnt; i++) port->source_caps[i] = le32_to_cpu(msg->payload[i]); @@ -3291,12 +3518,12 @@ static void tcpm_pd_data_request(struct tcpm_port *port, } } -static void tcpm_pps_complete(struct tcpm_port *port, int result) +static void tcpm_aug_supply_req_complete(struct tcpm_port *port, int result) { - if (port->pps_pending) { - port->pps_status = result; - port->pps_pending = false; - complete(&port->pps_complete); + if (port->aug_supply_req_pending) { + port->aug_supply_req_status = result; + port->aug_supply_req_pending = false; + complete(&port->aug_supply_req_complete); } } @@ -3394,7 +3621,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, /* Revert data back from any requested PPS updates */ port->pps_data.req_out_volt = port->supply_voltage; port->pps_data.req_op_curr = port->current_limit; - port->pps_status = (type == PD_CTRL_WAIT ? + port->aug_supply_req_status = (type == PD_CTRL_WAIT ? -EAGAIN : -EOPNOTSUPP); /* Threshold was relaxed before sending Request. Restore it back. */ @@ -3404,6 +3631,20 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, tcpm_set_state(port, SNK_READY, 0); break; + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: + /* Revert data back from any requested SPR AVS updates */ + port->spr_avs_data.req_out_volt_mv = port->supply_voltage; + port->spr_avs_data.req_op_curr_ma = port->current_limit; + port->aug_supply_req_status = (type == PD_CTRL_WAIT ? + -EAGAIN : -EOPNOTSUPP); + + /* Threshold was relaxed before sending Request. Restore it back. */ + tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD, + port->spr_avs_data.active, + port->supply_voltage); + + tcpm_set_state(port, SNK_READY, 0); + break; case DR_SWAP_SEND: port->swap_status = (type == PD_CTRL_WAIT ? -EAGAIN : -EOPNOTSUPP); @@ -3456,6 +3697,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, switch (port->state) { case SNK_NEGOTIATE_CAPABILITIES: port->pps_data.active = false; + port->spr_avs_data.active = false; tcpm_set_state(port, SNK_TRANSITION_SINK, 0); break; case SNK_NEGOTIATE_PPS_CAPABILITIES: @@ -3468,6 +3710,13 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, power_supply_changed(port->psy); tcpm_set_state(port, SNK_TRANSITION_SINK, 0); break; + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: + port->spr_avs_data.active = true; + port->req_supply_voltage = port->spr_avs_data.req_out_volt_mv; + port->req_current_limit = port->spr_avs_data.req_op_curr_ma; + power_supply_changed(port->psy); + tcpm_set_state(port, SNK_TRANSITION_SINK, 0); + break; case SOFT_RESET_SEND: if (port->ams == SOFT_RESET_AMS) tcpm_ams_finish(port); @@ -3581,6 +3830,19 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); break; + case PD_CTRL_GET_SINK_CAP_EXT: + /* This is an unsupported message if port type is SRC */ + if (port->negotiated_rev >= PD_REV30 && + port->port_type != TYPEC_PORT_SRC) + tcpm_pd_handle_msg(port, PD_MSG_EXT_SINK_CAP_EXT, + GETTING_SINK_EXTENDED_CAPABILITIES); + 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 ? @@ -3729,7 +3991,7 @@ void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg, { struct pd_rx_event *event; - event = kzalloc(sizeof(*event), GFP_ATOMIC); + event = kzalloc_obj(*event, GFP_ATOMIC); if (!event) return; @@ -3833,6 +4095,16 @@ static bool tcpm_send_queued_message(struct tcpm_port *port) ret); tcpm_ams_finish(port); break; + case PD_MSG_EXT_SINK_CAP_EXT: + ret = tcpm_pd_send_sink_cap_ext(port); + if (ret == -EOPNOTSUPP) + tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP); + else if (ret < 0) + tcpm_log(port, + "Unable to transmit sink cap extended, ret=%d", + ret); + tcpm_ams_finish(port); + break; default: break; } @@ -3942,9 +4214,9 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, case PDO_TYPE_APDO: if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) { port->pps_data.supported = true; - port->usb_type = - POWER_SUPPLY_USB_TYPE_PD_PPS; - power_supply_changed(port->psy); + } else if (pdo_apdo_type(pdo) == APDO_TYPE_SPR_AVS) { + port->spr_avs_data.port_partner_src_status = SPR_AVS_SUPPORTED; + port->spr_avs_data.port_partner_src_pdo_index = i; } continue; default: @@ -3982,6 +4254,10 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, min_snk_mv = pdo_min_voltage(pdo); break; case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) == APDO_TYPE_SPR_AVS) { + port->spr_avs_data.port_snk_status = SPR_AVS_SUPPORTED; + port->spr_avs_data.port_snk_pdo_index = j; + } continue; default: tcpm_log(port, "Invalid sink PDO type, ignoring"); @@ -4003,6 +4279,23 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, } } + if (port->spr_avs_data.port_snk_status == SPR_AVS_UNKNOWN) + port->spr_avs_data.port_snk_status = SPR_AVS_NOT_SUPPORTED; + + if (port->spr_avs_data.port_partner_src_status == SPR_AVS_UNKNOWN) + port->spr_avs_data.port_partner_src_status = SPR_AVS_NOT_SUPPORTED; + + if (port->pps_data.supported && + port->spr_avs_data.port_partner_src_status == SPR_AVS_SUPPORTED) + port->usb_type = POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS; + else if (port->pps_data.supported) + port->usb_type = POWER_SUPPLY_USB_TYPE_PD_PPS; + else if (port->spr_avs_data.port_partner_src_status == SPR_AVS_SUPPORTED) + port->usb_type = POWER_SUPPLY_USB_TYPE_PD_SPR_AVS; + + if (port->usb_type != POWER_SUPPLY_USB_TYPE_PD) + power_supply_changed(port->psy); + return ret; } @@ -4053,6 +4346,88 @@ static unsigned int tcpm_pd_select_pps_apdo(struct tcpm_port *port) return src_pdo; } +static int tcpm_pd_select_spr_avs_apdo(struct tcpm_port *port) +{ + u32 req_out_volt_mv, req_op_curr_ma, src_max_curr_ma = 0, source_cap; + u32 snk_max_curr_ma = 0, src_pdo_index, snk_pdo_index, snk_pdo; + + if (port->spr_avs_data.port_snk_status != SPR_AVS_SUPPORTED || + port->spr_avs_data.port_partner_src_status != + SPR_AVS_SUPPORTED) { + tcpm_log(port, "SPR AVS not supported. port:%s partner:%s", + spr_avs_status_strings[port->spr_avs_data.port_snk_status], + spr_avs_status_strings[port->spr_avs_data.port_partner_src_status]); + return -EOPNOTSUPP; + } + + /* Round up to SPR_AVS_VOLT_MV_STEP */ + req_out_volt_mv = port->spr_avs_data.req_out_volt_mv; + if (req_out_volt_mv % SPR_AVS_VOLT_MV_STEP) { + req_out_volt_mv += SPR_AVS_VOLT_MV_STEP - + (req_out_volt_mv % SPR_AVS_VOLT_MV_STEP); + port->spr_avs_data.req_out_volt_mv = req_out_volt_mv; + } + + /* Round up to RDO_SPR_AVS_CURR_MA_STEP */ + req_op_curr_ma = port->spr_avs_data.req_op_curr_ma; + if (req_op_curr_ma % RDO_SPR_AVS_CURR_MA_STEP) { + req_op_curr_ma += RDO_SPR_AVS_CURR_MA_STEP - + (req_op_curr_ma % RDO_SPR_AVS_CURR_MA_STEP); + port->spr_avs_data.req_op_curr_ma = req_op_curr_ma; + } + + src_pdo_index = port->spr_avs_data.port_partner_src_pdo_index; + snk_pdo_index = port->spr_avs_data.port_snk_pdo_index; + source_cap = port->source_caps[src_pdo_index]; + snk_pdo = port->snk_pdo[snk_pdo_index]; + tcpm_log(port, + "SPR AVS src_pdo_index:%d snk_pdo_index:%d req_op_curr_ma roundup:%u req_out_volt_mv roundup:%u", + src_pdo_index, snk_pdo_index, req_op_curr_ma, req_out_volt_mv); + + if (req_out_volt_mv >= SPR_AVS_TIER1_MIN_VOLT_MV && + req_out_volt_mv <= SPR_AVS_TIER1_MAX_VOLT_MV) { + src_max_curr_ma = + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap); + snk_max_curr_ma = + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo); + } else if (req_out_volt_mv > SPR_AVS_TIER1_MAX_VOLT_MV && + req_out_volt_mv <= SPR_AVS_TIER2_MAX_VOLT_MV) { + src_max_curr_ma = + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap); + snk_max_curr_ma = + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo); + } else { + tcpm_log(port, "Invalid SPR AVS req_volt:%umV", req_out_volt_mv); + return -EINVAL; + } + + if (req_op_curr_ma > src_max_curr_ma || + req_op_curr_ma > snk_max_curr_ma) { + tcpm_log(port, + "Invalid SPR AVS request. req_volt:%umV req_curr:%umA src_max_cur:%umA snk_max_cur:%umA", + req_out_volt_mv, req_op_curr_ma, src_max_curr_ma, + snk_max_curr_ma); + return -EINVAL; + } + + /* Max SPR voltage based on both the port and the partner caps */ + if (pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo) && + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap)) + port->spr_avs_data.max_out_volt_mv = SPR_AVS_TIER2_MAX_VOLT_MV; + else + port->spr_avs_data.max_out_volt_mv = SPR_AVS_TIER1_MAX_VOLT_MV; + + /* + * Max SPR AVS curr based on 9V to 15V. This should be higher than or + * equal to 15V to 20V range. + */ + port->spr_avs_data.max_current_ma = + min(pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap), + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo)); + + return src_pdo_index; +} + static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) { unsigned int mv, ma, mw, flags; @@ -4220,13 +4595,74 @@ static int tcpm_pd_build_pps_request(struct tcpm_port *port, u32 *rdo) return 0; } -static int tcpm_pd_send_pps_request(struct tcpm_port *port) +static int tcpm_pd_build_spr_avs_request(struct tcpm_port *port, u32 *rdo) +{ + u32 out_mv, op_ma, flags, snk_pdo_index, source_cap; + unsigned int src_power_mw, snk_power_mw; + int src_pdo_index; + u32 snk_pdo; + + src_pdo_index = tcpm_pd_select_spr_avs_apdo(port); + if (src_pdo_index < 0) + return src_pdo_index; + snk_pdo_index = port->spr_avs_data.port_snk_pdo_index; + source_cap = port->source_caps[src_pdo_index]; + snk_pdo = port->snk_pdo[snk_pdo_index]; + out_mv = port->spr_avs_data.req_out_volt_mv; + op_ma = port->spr_avs_data.req_op_curr_ma; + + flags = RDO_USB_COMM | RDO_NO_SUSPEND; + + /* + * Set capability mismatch when the maximum power needs in the current + * requested AVS voltage tier range is greater than + * port->operating_snk_mw, however, the maximum power offered by the + * source at the current requested AVS voltage tier is less than + * port->operating_sink_mw. + */ + if (out_mv > SPR_AVS_TIER1_MAX_VOLT_MV) { + src_power_mw = + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(source_cap) * + SPR_AVS_TIER2_MAX_VOLT_MV / 1000; + snk_power_mw = + pdo_spr_avs_apdo_15v_to_20v_max_current_ma(snk_pdo) * + SPR_AVS_TIER2_MAX_VOLT_MV / 1000; + } else { + src_power_mw = + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(source_cap) * + SPR_AVS_TIER1_MAX_VOLT_MV / 1000; + snk_power_mw = + pdo_spr_avs_apdo_9v_to_15v_max_current_ma(snk_pdo) * + SPR_AVS_TIER1_MAX_VOLT_MV / 1000; + } + + if (snk_power_mw >= port->operating_snk_mw && + src_power_mw < port->operating_snk_mw) + flags |= RDO_CAP_MISMATCH; + + *rdo = RDO_AVS(src_pdo_index + 1, out_mv, op_ma, flags); + + tcpm_log(port, "Requesting APDO SPR AVS %d: %u mV, %u mA", + src_pdo_index, out_mv, op_ma); + + return 0; +} + +static int tcpm_pd_send_aug_supply_request(struct tcpm_port *port, + enum aug_req_type type) { struct pd_message msg; int ret; u32 rdo; - ret = tcpm_pd_build_pps_request(port, &rdo); + if (type == PD_PPS) { + ret = tcpm_pd_build_pps_request(port, &rdo); + } else if (type == PD_SPR_AVS) { + ret = tcpm_pd_build_spr_avs_request(port, &rdo); + } else { + tcpm_log(port, "Invalid aug_req_type %d", type); + ret = -EOPNOTSUPP; + } if (ret < 0) return ret; @@ -4353,16 +4789,6 @@ static int tcpm_src_attach(struct tcpm_port *port) tcpm_enable_auto_vbus_discharge(port, true); - ret = tcpm_set_roles(port, true, TYPEC_SOURCE, tcpm_data_role_for_source(port)); - if (ret < 0) - return ret; - - if (port->pd_supported) { - ret = port->tcpc->set_pd_rx(port->tcpc, true); - if (ret < 0) - goto out_disable_mux; - } - /* * USB Type-C specification, version 1.2, * chapter 4.5.2.2.8.1 (Attached.SRC Requirements) @@ -4372,13 +4798,24 @@ static int tcpm_src_attach(struct tcpm_port *port) (polarity == TYPEC_POLARITY_CC2 && port->cc1 == TYPEC_CC_RA)) { ret = tcpm_set_vconn(port, true); if (ret < 0) - goto out_disable_pd; + return ret; } ret = tcpm_set_vbus(port, true); if (ret < 0) goto out_disable_vconn; + ret = tcpm_set_roles(port, true, TYPEC_STATE_USB, TYPEC_SOURCE, + tcpm_data_role_for_source(port)); + if (ret < 0) + goto out_disable_vbus; + + if (port->pd_supported) { + ret = port->tcpc->set_pd_rx(port->tcpc, true); + if (ret < 0) + goto out_disable_mux; + } + port->pd_capable = false; port->partner = NULL; @@ -4389,14 +4826,14 @@ static int tcpm_src_attach(struct tcpm_port *port) return 0; -out_disable_vconn: - tcpm_set_vconn(port, false); -out_disable_pd: - if (port->pd_supported) - port->tcpc->set_pd_rx(port->tcpc, false); out_disable_mux: tcpm_mux_set(port, TYPEC_STATE_SAFE, USB_ROLE_NONE, TYPEC_ORIENTATION_NONE); +out_disable_vbus: + tcpm_set_vbus(port, false); +out_disable_vconn: + tcpm_set_vconn(port, false); + return ret; } @@ -4448,6 +4885,14 @@ static void tcpm_set_partner_usb_comm_capable(struct tcpm_port *port, bool capab port->tcpc->set_partner_usb_comm_capable(port->tcpc, capable); } +static void tcpm_partner_source_caps_reset(struct tcpm_port *port) +{ + usb_power_delivery_unregister_capabilities(port->partner_source_caps); + port->partner_source_caps = NULL; + port->spr_avs_data.port_partner_src_status = SPR_AVS_UNKNOWN; + port->spr_avs_data.active = false; +} + static void tcpm_reset_port(struct tcpm_port *port) { tcpm_enable_auto_vbus_discharge(port, false); @@ -4487,8 +4932,7 @@ static void tcpm_reset_port(struct tcpm_port *port) usb_power_delivery_unregister_capabilities(port->partner_sink_caps); port->partner_sink_caps = NULL; - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps = NULL; + tcpm_partner_source_caps_reset(port); usb_power_delivery_unregister(port->partner_pd); port->partner_pd = NULL; } @@ -4528,7 +4972,8 @@ static int tcpm_snk_attach(struct tcpm_port *port) tcpm_enable_auto_vbus_discharge(port, true); - ret = tcpm_set_roles(port, true, TYPEC_SINK, tcpm_data_role_for_sink(port)); + ret = tcpm_set_roles(port, true, TYPEC_STATE_USB, + TYPEC_SINK, tcpm_data_role_for_sink(port)); if (ret < 0) return ret; @@ -4551,12 +4996,24 @@ static void tcpm_snk_detach(struct tcpm_port *port) static int tcpm_acc_attach(struct tcpm_port *port) { int ret; + enum typec_role role; + enum typec_data_role data; + int state = TYPEC_STATE_USB; if (port->attached) return 0; - ret = tcpm_set_roles(port, true, TYPEC_SOURCE, - tcpm_data_role_for_source(port)); + role = tcpm_port_is_sink(port) ? TYPEC_SINK : TYPEC_SOURCE; + data = tcpm_port_is_sink(port) ? tcpm_data_role_for_sink(port) + : tcpm_data_role_for_source(port); + + if (tcpm_port_is_audio(port)) + state = TYPEC_MODE_AUDIO; + + if (tcpm_port_is_debug(port)) + state = TYPEC_MODE_DEBUG; + + ret = tcpm_set_roles(port, true, state, role, data); if (ret < 0) return ret; @@ -4665,6 +5122,25 @@ static void tcpm_set_initial_svdm_version(struct tcpm_port *port) } } +static void tcpm_set_initial_negotiated_rev(struct tcpm_port *port) +{ + switch (port->pd_rev.rev_major) { + case PD_CAP_REV10: + port->negotiated_rev = PD_REV10; + break; + case PD_CAP_REV20: + port->negotiated_rev = PD_REV20; + break; + case PD_CAP_REV30: + port->negotiated_rev = PD_REV30; + break; + default: + port->negotiated_rev = PD_MAX_REV; + break; + } + port->negotiated_rev_prime = port->negotiated_rev; +} + static void run_state_machine(struct tcpm_port *port) { int ret; @@ -4705,7 +5181,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SNK_UNATTACHED, PD_T_DRP_SNK); break; case SRC_ATTACH_WAIT: - if (tcpm_port_is_debug(port)) + if (tcpm_port_is_debug_source(port)) tcpm_set_state(port, DEBUG_ACC_ATTACHED, port->timings.cc_debounce_time); else if (tcpm_port_is_audio(port)) @@ -4782,8 +5258,7 @@ static void run_state_machine(struct tcpm_port *port) typec_set_pwr_opmode(port->typec_port, opmode); port->pwr_opmode = TYPEC_PWR_MODE_USB; port->caps_count = 0; - port->negotiated_rev = PD_MAX_REV; - port->negotiated_rev_prime = PD_MAX_REV; + tcpm_set_initial_negotiated_rev(port); port->message_id = 0; port->message_id_prime = 0; port->rx_msgid = -1; @@ -4949,7 +5424,7 @@ static void run_state_machine(struct tcpm_port *port) case SNK_UNATTACHED: if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); - tcpm_pps_complete(port, -ENOTCONN); + tcpm_aug_supply_req_complete(port, -ENOTCONN); tcpm_snk_detach(port); if (port->potential_contaminant) { tcpm_set_state(port, CHECK_CONTAMINANT, 0); @@ -4964,7 +5439,13 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_UNATTACHED, PD_T_DRP_SRC); break; case SNK_ATTACH_WAIT: - if ((port->cc1 == TYPEC_CC_OPEN && + if (tcpm_port_is_debug_sink(port)) + tcpm_set_state(port, DEBUG_ACC_ATTACHED, + PD_T_CC_DEBOUNCE); + else if (tcpm_port_is_audio(port)) + tcpm_set_state(port, AUDIO_ACC_ATTACHED, + PD_T_CC_DEBOUNCE); + else if ((port->cc1 == TYPEC_CC_OPEN && port->cc2 != TYPEC_CC_OPEN) || (port->cc1 != TYPEC_CC_OPEN && port->cc2 == TYPEC_CC_OPEN)) @@ -4978,6 +5459,12 @@ static void run_state_machine(struct tcpm_port *port) if (tcpm_port_is_disconnected(port)) tcpm_set_state(port, SNK_UNATTACHED, PD_T_PD_DEBOUNCE); + else if (tcpm_port_is_debug_sink(port)) + tcpm_set_state(port, DEBUG_ACC_ATTACHED, + PD_T_CC_DEBOUNCE); + else if (tcpm_port_is_audio(port)) + tcpm_set_state(port, AUDIO_ACC_ATTACHED, + PD_T_CC_DEBOUNCE); else if (port->vbus_present) tcpm_set_state(port, tcpm_try_src(port) ? SRC_TRY @@ -5048,8 +5535,7 @@ static void run_state_machine(struct tcpm_port *port) port->cc2 : port->cc1); typec_set_pwr_opmode(port->typec_port, opmode); port->pwr_opmode = TYPEC_PWR_MODE_USB; - port->negotiated_rev = PD_MAX_REV; - port->negotiated_rev_prime = PD_MAX_REV; + tcpm_set_initial_negotiated_rev(port); port->message_id = 0; port->message_id_prime = 0; port->rx_msgid = -1; @@ -5169,13 +5655,16 @@ static void run_state_machine(struct tcpm_port *port) } break; case SNK_NEGOTIATE_PPS_CAPABILITIES: - ret = tcpm_pd_send_pps_request(port); + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: + ret = tcpm_pd_send_aug_supply_request(port, port->state == + SNK_NEGOTIATE_PPS_CAPABILITIES ? + PD_PPS : PD_SPR_AVS); if (ret < 0) { /* Restore back to the original state */ tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_PD, port->pps_data.active, port->supply_voltage); - port->pps_status = ret; + port->aug_supply_req_status = ret; /* * If this was called due to updates to sink * capabilities, and pps is no longer valid, we should @@ -5191,23 +5680,58 @@ static void run_state_machine(struct tcpm_port *port) } break; case SNK_TRANSITION_SINK: - /* From the USB PD spec: - * "The Sink Shall transition to Sink Standby before a positive or - * negative voltage transition of VBUS. During Sink Standby - * the Sink Shall reduce its power draw to pSnkStdby." - * - * This is not applicable to PPS though as the port can continue - * to draw negotiated power without switching to standby. - */ - if (port->supply_voltage != port->req_supply_voltage && !port->pps_data.active && - port->current_limit * port->supply_voltage / 1000 > PD_P_SNK_STDBY_MW) { - u32 stdby_ma = PD_P_SNK_STDBY_MW * 1000 / port->supply_voltage; + if (port->spr_avs_data.active) { + if (abs(port->req_supply_voltage - port->supply_voltage) > + SPR_AVS_AVS_SMALL_STEP_V * 1000) { + /* + * The Sink Shall reduce its current draw to + * iSnkStdby within tSnkStdby. The reduction to + * iSnkStdby is not required if the voltage + * increase is less than or equal to + * vAvsSmallStep. + */ + tcpm_log(port, + "SPR AVS Setting iSnkstandby. Req vol: %u mV Curr vol: %u mV", + port->req_supply_voltage, + port->supply_voltage); + tcpm_set_current_limit(port, PD_I_SNK_STBY_MA, + port->supply_voltage); + } + /* + * Although tAvsSrcTransSmall is expected to be used + * for voltage transistions smaller than 1V, using + * tAvsSrcTransLarge to be resilient against chargers + * which strictly cannot honor tAvsSrcTransSmall to + * improve interoperability. + */ + tcpm_set_state(port, hard_reset_state(port), + PD_T_AVS_SRC_TRANS_LARGE); + /* + * From the USB PD spec: + * "The Sink Shall transition to Sink Standby before a + * positive ornegative voltage transition of VBUS. + * During Sink Standby the Sink Shall reduce its power + * draw to pSnkStdby." + * + * This is not applicable to PPS though as the port can + * continue to draw negotiated power without switching + * to standby. + */ + } else if (port->supply_voltage != port->req_supply_voltage && + !port->pps_data.active && + (port->current_limit * port->supply_voltage / 1000 > + PD_P_SNK_STDBY_MW)) { + u32 stdby_ma = PD_P_SNK_STDBY_MW * 1000 / + port->supply_voltage; tcpm_log(port, "Setting standby current %u mV @ %u mA", port->supply_voltage, stdby_ma); - tcpm_set_current_limit(port, stdby_ma, port->supply_voltage); + tcpm_set_current_limit(port, stdby_ma, + port->supply_voltage); + tcpm_set_state(port, hard_reset_state(port), + PD_T_PS_TRANSITION); } - fallthrough; + break; case SNK_TRANSITION_SINK_VBUS: tcpm_set_state(port, hard_reset_state(port), PD_T_PS_TRANSITION); @@ -5227,7 +5751,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_typec_connect(port); if (port->pd_capable && port->source_caps[0] & PDO_FIXED_DUAL_ROLE) mod_enable_frs_delayed_work(port, 0); - tcpm_pps_complete(port, port->pps_status); + tcpm_aug_supply_req_complete(port, port->aug_supply_req_status); if (port->ams != NONE_AMS) tcpm_ams_finish(port); @@ -5276,7 +5800,10 @@ static void run_state_machine(struct tcpm_port *port) /* Accessory states */ case ACC_UNATTACHED: tcpm_acc_detach(port); - tcpm_set_state(port, SRC_UNATTACHED, 0); + if (port->port_type == TYPEC_PORT_SRC) + tcpm_set_state(port, SRC_UNATTACHED, 0); + else + tcpm_set_state(port, SNK_UNATTACHED, 0); break; case DEBUG_ACC_ATTACHED: case AUDIO_ACC_ATTACHED: @@ -5284,6 +5811,7 @@ static void run_state_machine(struct tcpm_port *port) if (ret < 0) tcpm_set_state(port, ACC_UNATTACHED, 0); break; + case DEBUG_ACC_DEBOUNCE: case AUDIO_ACC_DEBOUNCE: tcpm_set_state(port, ACC_UNATTACHED, port->timings.cc_debounce_time); break; @@ -5326,7 +5854,7 @@ static void run_state_machine(struct tcpm_port *port) */ tcpm_set_vconn(port, false); tcpm_set_vbus(port, false); - tcpm_set_roles(port, port->self_powered, TYPEC_SOURCE, + tcpm_set_roles(port, port->self_powered, TYPEC_STATE_USB, TYPEC_SOURCE, tcpm_data_role_for_source(port)); /* * If tcpc fails to notify vbus off, TCPM will wait for PD_T_SAFE_0V + @@ -5358,7 +5886,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_vconn(port, false); if (port->pd_capable) tcpm_set_charge(port, false); - tcpm_set_roles(port, port->self_powered, TYPEC_SINK, + tcpm_set_roles(port, port->self_powered, TYPEC_STATE_USB, TYPEC_SINK, tcpm_data_role_for_sink(port)); /* * VBUS may or may not toggle, depending on the adapter. @@ -5410,9 +5938,10 @@ static void run_state_machine(struct tcpm_port *port) port->message_id = 0; port->rx_msgid = -1; /* remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps = NULL; + tcpm_partner_source_caps_reset(port); tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); + port->vdm_sm_running = false; + port->explicit_contract = false; tcpm_ams_finish(port); if (port->pwr_role == TYPEC_SOURCE) { port->upcoming_state = SRC_SEND_CAPABILITIES; @@ -5444,8 +5973,7 @@ static void run_state_machine(struct tcpm_port *port) port->message_id = 0; port->rx_msgid = -1; /* remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps = NULL; + tcpm_partner_source_caps_reset(port); if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET, TCPC_TX_SOP)) tcpm_set_state_cond(port, hard_reset_state(port), 0); else @@ -5482,10 +6010,10 @@ static void run_state_machine(struct tcpm_port *port) case DR_SWAP_CHANGE_DR: tcpm_unregister_altmodes(port); if (port->data_role == TYPEC_HOST) - tcpm_set_roles(port, true, port->pwr_role, + tcpm_set_roles(port, true, TYPEC_STATE_USB, port->pwr_role, TYPEC_DEVICE); else - tcpm_set_roles(port, true, port->pwr_role, + tcpm_set_roles(port, true, TYPEC_STATE_USB, port->pwr_role, TYPEC_HOST); tcpm_ams_finish(port); tcpm_set_state(port, ready_state(port), 0); @@ -5582,8 +6110,7 @@ static void run_state_machine(struct tcpm_port *port) break; case PR_SWAP_SNK_SRC_SINK_OFF: /* will be source, remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps = NULL; + tcpm_partner_source_caps_reset(port); /* * Prevent vbus discharge circuit from turning on during PR_SWAP * as this is not a disconnect. @@ -5731,7 +6258,7 @@ static void run_state_machine(struct tcpm_port *port) break; case ERROR_RECOVERY: tcpm_swap_complete(port, -EPROTO); - tcpm_pps_complete(port, -EPROTO); + tcpm_aug_supply_req_complete(port, -EPROTO); tcpm_set_state(port, PORT_RESET, 0); break; case PORT_RESET: @@ -5840,10 +6367,10 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, switch (port->state) { case TOGGLING: - if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || + if (tcpm_port_is_debug_source(port) || tcpm_port_is_audio(port) || tcpm_port_is_source(port)) tcpm_set_state(port, SRC_ATTACH_WAIT, 0); - else if (tcpm_port_is_sink(port)) + else if (tcpm_port_is_debug_sink(port) || tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_ATTACH_WAIT, 0); break; case CHECK_CONTAMINANT: @@ -5851,9 +6378,11 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, break; case SRC_UNATTACHED: case ACC_UNATTACHED: - if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || + if (tcpm_port_is_debug_source(port) || tcpm_port_is_audio(port) || tcpm_port_is_source(port)) tcpm_set_state(port, SRC_ATTACH_WAIT, 0); + else if (tcpm_port_is_debug_sink(port)) + tcpm_set_state(port, SNK_ATTACH_WAIT, 0); break; case SRC_ATTACH_WAIT: if (tcpm_port_is_disconnected(port) || @@ -5875,7 +6404,8 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, } break; case SNK_UNATTACHED: - if (tcpm_port_is_sink(port)) + if (tcpm_port_is_debug_sink(port) || tcpm_port_is_audio(port) || + tcpm_port_is_sink(port)) tcpm_set_state(port, SNK_ATTACH_WAIT, 0); break; case SNK_ATTACH_WAIT: @@ -5938,7 +6468,12 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, case DEBUG_ACC_ATTACHED: if (cc1 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_OPEN) - tcpm_set_state(port, ACC_UNATTACHED, 0); + tcpm_set_state(port, DEBUG_ACC_DEBOUNCE, 0); + break; + + case DEBUG_ACC_DEBOUNCE: + if (tcpm_port_is_debug(port)) + tcpm_set_state(port, DEBUG_ACC_ATTACHED, 0); break; case SNK_TRY: @@ -5965,7 +6500,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, case SNK_TRY_WAIT_DEBOUNCE: if (!tcpm_port_is_sink(port)) { port->max_wait = 0; - tcpm_set_state(port, SRC_TRYWAIT, 0); + tcpm_set_state(port, SRC_TRYWAIT, PD_T_PD_DEBOUNCE); } break; case SRC_TRY_WAIT: @@ -6699,7 +7234,7 @@ static int tcpm_try_role(struct typec_port *p, int role) return ret; } -static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr) +static int tcpm_aug_set_op_curr(struct tcpm_port *port, u16 req_op_curr_ma) { unsigned int target_mw; int ret; @@ -6707,7 +7242,19 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr) mutex_lock(&port->swap_lock); mutex_lock(&port->lock); - if (!port->pps_data.active) { + if (port->pps_data.active) { + req_op_curr_ma = req_op_curr_ma - + (req_op_curr_ma % RDO_PROG_CURR_MA_STEP); + if (req_op_curr_ma > port->pps_data.max_curr) { + ret = -EINVAL; + goto port_unlock; + } + target_mw = (req_op_curr_ma * port->supply_voltage) / 1000; + if (target_mw < port->operating_snk_mw) { + ret = -EINVAL; + goto port_unlock; + } + } else if (!port->spr_avs_data.active) { ret = -EOPNOTSUPP; goto port_unlock; } @@ -6717,38 +7264,31 @@ static int tcpm_pps_set_op_curr(struct tcpm_port *port, u16 req_op_curr) goto port_unlock; } - if (req_op_curr > port->pps_data.max_curr) { - ret = -EINVAL; - goto port_unlock; - } - - target_mw = (req_op_curr * port->supply_voltage) / 1000; - if (target_mw < port->operating_snk_mw) { - ret = -EINVAL; - goto port_unlock; - } + if (port->pps_data.active) + port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; + else + port->upcoming_state = SNK_NEGOTIATE_SPR_AVS_CAPABILITIES; - port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; ret = tcpm_ams_start(port, POWER_NEGOTIATION); if (ret == -EAGAIN) { port->upcoming_state = INVALID_STATE; goto port_unlock; } - /* Round down operating current to align with PPS valid steps */ - req_op_curr = req_op_curr - (req_op_curr % RDO_PROG_CURR_MA_STEP); - - reinit_completion(&port->pps_complete); - port->pps_data.req_op_curr = req_op_curr; - port->pps_status = 0; - port->pps_pending = true; + reinit_completion(&port->aug_supply_req_complete); + if (port->pps_data.active) + port->pps_data.req_op_curr = req_op_curr_ma; + else + port->spr_avs_data.req_op_curr_ma = req_op_curr_ma; + port->aug_supply_req_status = 0; + port->aug_supply_req_pending = true; mutex_unlock(&port->lock); - if (!wait_for_completion_timeout(&port->pps_complete, - msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) ret = -ETIMEDOUT; else - ret = port->pps_status; + ret = port->aug_supply_req_status; goto swap_unlock; @@ -6760,7 +7300,7 @@ swap_unlock: return ret; } -static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt) +static int tcpm_aug_set_out_volt(struct tcpm_port *port, u16 req_out_volt_mv) { unsigned int target_mw; int ret; @@ -6768,7 +7308,16 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt) mutex_lock(&port->swap_lock); mutex_lock(&port->lock); - if (!port->pps_data.active) { + if (port->pps_data.active) { + req_out_volt_mv = req_out_volt_mv - (req_out_volt_mv % + RDO_PROG_VOLT_MV_STEP); + /* Round down output voltage to align with PPS valid steps */ + target_mw = (port->current_limit * req_out_volt_mv) / 1000; + if (target_mw < port->operating_snk_mw) { + ret = -EINVAL; + goto port_unlock; + } + } else if (!port->spr_avs_data.active) { ret = -EOPNOTSUPP; goto port_unlock; } @@ -6778,33 +7327,31 @@ static int tcpm_pps_set_out_volt(struct tcpm_port *port, u16 req_out_volt) goto port_unlock; } - target_mw = (port->current_limit * req_out_volt) / 1000; - if (target_mw < port->operating_snk_mw) { - ret = -EINVAL; - goto port_unlock; - } + if (port->pps_data.active) + port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; + else + port->upcoming_state = SNK_NEGOTIATE_SPR_AVS_CAPABILITIES; - port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; ret = tcpm_ams_start(port, POWER_NEGOTIATION); if (ret == -EAGAIN) { port->upcoming_state = INVALID_STATE; goto port_unlock; } - /* Round down output voltage to align with PPS valid steps */ - req_out_volt = req_out_volt - (req_out_volt % RDO_PROG_VOLT_MV_STEP); - - reinit_completion(&port->pps_complete); - port->pps_data.req_out_volt = req_out_volt; - port->pps_status = 0; - port->pps_pending = true; + reinit_completion(&port->aug_supply_req_complete); + if (port->pps_data.active) + port->pps_data.req_out_volt = req_out_volt_mv; + else + port->spr_avs_data.req_out_volt_mv = req_out_volt_mv; + port->aug_supply_req_status = 0; + port->aug_supply_req_pending = true; mutex_unlock(&port->lock); - if (!wait_for_completion_timeout(&port->pps_complete, - msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) ret = -ETIMEDOUT; else - ret = port->pps_status; + ret = port->aug_supply_req_status; goto swap_unlock; @@ -6847,9 +7394,9 @@ static int tcpm_pps_activate(struct tcpm_port *port, bool activate) goto port_unlock; } - reinit_completion(&port->pps_complete); - port->pps_status = 0; - port->pps_pending = true; + reinit_completion(&port->aug_supply_req_complete); + port->aug_supply_req_status = 0; + port->aug_supply_req_pending = true; /* Trigger PPS request or move back to standard PDO contract */ if (activate) { @@ -6858,11 +7405,75 @@ static int tcpm_pps_activate(struct tcpm_port *port, bool activate) } mutex_unlock(&port->lock); - if (!wait_for_completion_timeout(&port->pps_complete, - msecs_to_jiffies(PD_PPS_CTRL_TIMEOUT))) + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->aug_supply_req_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + + return ret; +} + +static int tcpm_spr_avs_activate(struct tcpm_port *port, bool activate) +{ + int ret = 0; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (port->spr_avs_data.port_snk_status == SPR_AVS_NOT_SUPPORTED || + port->spr_avs_data.port_partner_src_status == SPR_AVS_NOT_SUPPORTED) { + tcpm_log(port, "SPR_AVS not supported"); + ret = -EOPNOTSUPP; + goto port_unlock; + } + + /* Trying to deactivate SPR AVS when already deactivated so just bail */ + if (!port->spr_avs_data.active && !activate) + goto port_unlock; + + if (port->state != SNK_READY) { + tcpm_log(port, + "SPR_AVS cannot be activated. Port not in SNK_READY"); + ret = -EAGAIN; + goto port_unlock; + } + + if (activate) + port->upcoming_state = SNK_NEGOTIATE_SPR_AVS_CAPABILITIES; + else + port->upcoming_state = SNK_NEGOTIATE_CAPABILITIES; + ret = tcpm_ams_start(port, POWER_NEGOTIATION); + if (ret == -EAGAIN) { + tcpm_log(port, "SPR_AVS cannot be %s. AMS start failed", + activate ? "activated" : "deactivated"); + port->upcoming_state = INVALID_STATE; + goto port_unlock; + } + + reinit_completion(&port->aug_supply_req_complete); + port->aug_supply_req_status = 0; + port->aug_supply_req_pending = true; + + /* Trigger AVS request or move back to standard PDO contract */ + if (activate) { + port->spr_avs_data.req_out_volt_mv = port->supply_voltage; + port->spr_avs_data.req_op_curr_ma = port->current_limit; + } + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->aug_supply_req_complete, + msecs_to_jiffies(PD_AUG_PSY_CTRL_TIMEOUT))) ret = -ETIMEDOUT; else - ret = port->pps_status; + ret = port->aug_supply_req_status; goto swap_unlock; @@ -7018,16 +7629,26 @@ static int tcpm_pd_set(struct typec_port *p, struct usb_power_delivery *pd) break; case SNK_NEGOTIATE_CAPABILITIES: case SNK_NEGOTIATE_PPS_CAPABILITIES: + case SNK_NEGOTIATE_SPR_AVS_CAPABILITIES: case SNK_READY: case SNK_TRANSITION_SINK: case SNK_TRANSITION_SINK_VBUS: - if (port->pps_data.active) + if (port->pps_data.active) { port->upcoming_state = SNK_NEGOTIATE_PPS_CAPABILITIES; - else if (port->pd_capable) + } else if (port->pd_capable) { port->upcoming_state = SNK_NEGOTIATE_CAPABILITIES; - else + if (port->spr_avs_data.active) { + /* + * De-activate AVS and fallback to PD to + * re-evaluate whether AVS is supported in the + * current sink cap set. + */ + port->spr_avs_data.active = false; + port->spr_avs_data.port_snk_status = SPR_AVS_UNKNOWN; + } + } else { break; - + } port->update_sink_caps = true; ret = tcpm_ams_start(port, POWER_NEGOTIATION); @@ -7164,9 +7785,132 @@ static void tcpm_fw_get_timings(struct tcpm_port *port, struct fwnode_handle *fw port->timings.snk_bc12_cmpletion_time = val; } +static void tcpm_fw_get_pd_ident(struct tcpm_port *port) +{ + struct pd_identifier *pd_ident = &port->pd_ident; + u32 *vdo; + + /* First 3 vdo values contain info regarding USB PID, VID & XID */ + if (port->nr_snk_vdo >= 3) + vdo = port->snk_vdo; + else if (port->nr_snk_vdo_v1 >= 3) + vdo = port->snk_vdo_v1; + else + return; + + pd_ident->vid = PD_IDH_VID(vdo[0]); + pd_ident->pid = PD_PRODUCT_PID(vdo[2]); + pd_ident->xid = PD_CSTAT_XID(vdo[1]); + tcpm_log(port, "vid:%#x pid:%#x xid:%#x", + pd_ident->vid, pd_ident->pid, pd_ident->xid); +} + +static void tcpm_parse_snk_pdos(struct tcpm_port *port) +{ + struct sink_caps_ext_data *caps = &port->sink_caps_ext; + u32 max_mv, max_ma; + u8 avs_tier1_pdp, avs_tier2_pdp; + int i, pdo_itr; + u32 *snk_pdos; + + for (i = 0; i < port->pd_count; ++i) { + snk_pdos = port->pd_list[i]->sink_desc.pdo; + for (pdo_itr = 0; pdo_itr < PDO_MAX_OBJECTS && snk_pdos[pdo_itr]; + ++pdo_itr) { + u32 pdo = snk_pdos[pdo_itr]; + u8 curr_snk_pdp = 0; + + switch (pdo_type(pdo)) { + case PDO_TYPE_FIXED: + max_mv = pdo_fixed_voltage(pdo); + max_ma = pdo_fixed_current(pdo); + curr_snk_pdp = UW_TO_W(max_mv * max_ma); + break; + case PDO_TYPE_BATT: + curr_snk_pdp = UW_TO_W(pdo_max_power(pdo)); + break; + case PDO_TYPE_VAR: + max_mv = pdo_max_voltage(pdo); + max_ma = pdo_max_current(pdo); + curr_snk_pdp = UW_TO_W(max_mv * max_ma); + break; + case PDO_TYPE_APDO: + if (pdo_apdo_type(pdo) == APDO_TYPE_PPS) { + max_mv = pdo_pps_apdo_max_voltage(pdo); + max_ma = pdo_pps_apdo_max_current(pdo); + curr_snk_pdp = UW_TO_W(max_mv * max_ma); + caps->modes |= SINK_MODE_PPS; + } else if (pdo_apdo_type(pdo) == + APDO_TYPE_SPR_AVS) { + avs_tier1_pdp = UW_TO_W(SPR_AVS_TIER1_MAX_VOLT_MV + * pdo_spr_avs_apdo_9v_to_15v_max_current_ma(pdo)); + avs_tier2_pdp = UW_TO_W(SPR_AVS_TIER2_MAX_VOLT_MV + * pdo_spr_avs_apdo_15v_to_20v_max_current_ma(pdo)); + curr_snk_pdp = max(avs_tier1_pdp, avs_tier2_pdp); + caps->modes |= SINK_MODE_AVS; + } + break; + default: + tcpm_log(port, "Invalid source PDO type, ignoring"); + continue; + } + + caps->spr_max_pdp = max(caps->spr_max_pdp, + curr_snk_pdp); + } + } +} + +static void tcpm_fw_get_sink_caps_ext(struct tcpm_port *port, + struct fwnode_handle *fwnode) +{ + struct sink_caps_ext_data *caps = &port->sink_caps_ext; + int ret; + u32 val; + + /* + * Load step represents the change in current per usec that a given + * source can tolerate while maintaining Vbus within the vSrcValid + * range. For a sink this represents the "preferred" load-step value. It + * can only have 2 values (150 mA/usec or 500 mA/usec) with 150 mA/usec + * being the default. + */ + ret = fwnode_property_read_u32(fwnode, "sink-load-step", &val); + if (!ret) + caps->load_step = val == 500 ? 1 : 0; + + fwnode_property_read_u16(fwnode, "sink-load-characteristics", + &caps->load_char); + fwnode_property_read_u8(fwnode, "sink-compliance", &caps->compliance); + caps->modes = SINK_MODE_VBUS; + + /* + * As per "6.5.13.14" SPR Sink Operational PDP definition, for battery + * powered devices, this value will correspond to the PDP of the + * charging adapter either shipped or recommended for use with it. For + * batteryless sink devices SPR Operational PDP indicates the power + * required to operate all the device's functional modes. Hence, this + * value may be considered equal to port's operating_snk_mw. As + * operating_sink_mw can change as per the pd set used thus, OP PDP + * is determined when populating Sink Caps Extended Data Block. + */ + if (port->self_powered) { + fwnode_property_read_u32(fwnode, "charging-adapter-pdp-milliwatt", + &val); + caps->spr_op_pdp = (u8)(val / 1000); + caps->modes |= SINK_MODE_BATT; + } + + tcpm_parse_snk_pdos(port); + tcpm_log(port, + "load-step:%#x load-char:%#x compl:%#x op-pdp:%#x max-pdp:%#x", + caps->load_step, caps->load_char, caps->compliance, + caps->spr_op_pdp, caps->spr_max_pdp); +} + static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { - struct fwnode_handle *capabilities, *child, *caps = NULL; + struct fwnode_handle *capabilities, *caps = NULL; unsigned int nr_src_pdo, nr_snk_pdo; const char *opmode_str; u32 *src_pdo, *snk_pdo; @@ -7232,9 +7976,7 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode if (!capabilities) { port->pd_count = 1; } else { - fwnode_for_each_child_node(capabilities, child) - port->pd_count++; - + port->pd_count = fwnode_get_child_node_count(capabilities); if (!port->pd_count) { ret = -ENODATA; goto put_capabilities; @@ -7339,6 +8081,9 @@ static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode } } + if (port->port_type != TYPEC_PORT_SRC) + tcpm_fw_get_sink_caps_ext(port, fwnode); + put_caps: if (caps != fwnode) fwnode_handle_put(caps); @@ -7381,6 +8126,8 @@ static int tcpm_fw_get_snk_vdos(struct tcpm_port *port, struct fwnode_handle *fw return ret; } + tcpm_fw_get_pd_ident(port); + return 0; } @@ -7411,7 +8158,8 @@ static void tcpm_fw_get_pd_revision(struct tcpm_port *port, struct fwnode_handle enum tcpm_psy_online_states { TCPM_PSY_OFFLINE = 0, TCPM_PSY_FIXED_ONLINE, - TCPM_PSY_PROG_ONLINE, + TCPM_PSY_PPS_ONLINE, + TCPM_PSY_SPR_AVS_ONLINE, }; static enum power_supply_property tcpm_psy_props[] = { @@ -7429,7 +8177,9 @@ static int tcpm_psy_get_online(struct tcpm_port *port, { if (port->vbus_charge) { if (port->pps_data.active) - val->intval = TCPM_PSY_PROG_ONLINE; + val->intval = TCPM_PSY_PPS_ONLINE; + else if (port->spr_avs_data.active) + val->intval = TCPM_PSY_SPR_AVS_ONLINE; else val->intval = TCPM_PSY_FIXED_ONLINE; } else { @@ -7444,6 +8194,8 @@ static int tcpm_psy_get_voltage_min(struct tcpm_port *port, { if (port->pps_data.active) val->intval = port->pps_data.min_volt * 1000; + else if (port->spr_avs_data.active) + val->intval = SPR_AVS_TIER1_MIN_VOLT_MV * 1000; else val->intval = port->supply_voltage * 1000; @@ -7455,6 +8207,8 @@ static int tcpm_psy_get_voltage_max(struct tcpm_port *port, { if (port->pps_data.active) val->intval = port->pps_data.max_volt * 1000; + else if (port->spr_avs_data.active) + val->intval = port->spr_avs_data.max_out_volt_mv * 1000; else val->intval = port->supply_voltage * 1000; @@ -7474,6 +8228,8 @@ static int tcpm_psy_get_current_max(struct tcpm_port *port, { if (port->pps_data.active) val->intval = port->pps_data.max_curr * 1000; + else if (port->spr_avs_data.active) + val->intval = port->spr_avs_data.max_current_ma * 1000; else val->intval = port->current_limit * 1000; @@ -7549,17 +8305,41 @@ static int tcpm_psy_get_prop(struct power_supply *psy, return ret; } +static int tcpm_disable_pps_avs(struct tcpm_port *port) +{ + int ret = 0; + + if (port->pps_data.active) + ret = tcpm_pps_activate(port, false); + else if (port->spr_avs_data.active) + ret = tcpm_spr_avs_activate(port, false); + + return ret; +} + static int tcpm_psy_set_online(struct tcpm_port *port, const union power_supply_propval *val) { - int ret; + int ret = 0; switch (val->intval) { case TCPM_PSY_FIXED_ONLINE: - ret = tcpm_pps_activate(port, false); + ret = tcpm_disable_pps_avs(port); + break; + case TCPM_PSY_PPS_ONLINE: + if (port->spr_avs_data.active) + ret = tcpm_spr_avs_activate(port, false); + if (!ret) + ret = tcpm_pps_activate(port, true); break; - case TCPM_PSY_PROG_ONLINE: - ret = tcpm_pps_activate(port, true); + case TCPM_PSY_SPR_AVS_ONLINE: + tcpm_log(port, "request to set AVS online"); + if (port->spr_avs_data.active) + return 0; + ret = tcpm_disable_pps_avs(port); + if (ret) + break; + ret = tcpm_spr_avs_activate(port, true); break; default: ret = -EINVAL; @@ -7588,13 +8368,10 @@ static int tcpm_psy_set_prop(struct power_supply *psy, ret = tcpm_psy_set_online(port, val); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - ret = tcpm_pps_set_out_volt(port, val->intval / 1000); + ret = tcpm_aug_set_out_volt(port, val->intval / 1000); break; case POWER_SUPPLY_PROP_CURRENT_NOW: - if (val->intval > port->pps_data.max_curr * 1000) - ret = -EINVAL; - else - ret = tcpm_pps_set_op_curr(port, val->intval / 1000); + ret = tcpm_aug_set_op_curr(port, val->intval / 1000); break; default: ret = -EINVAL; @@ -7639,7 +8416,9 @@ static int devm_tcpm_psy_register(struct tcpm_port *port) port->psy_desc.type = POWER_SUPPLY_TYPE_USB; port->psy_desc.usb_types = BIT(POWER_SUPPLY_USB_TYPE_C) | BIT(POWER_SUPPLY_USB_TYPE_PD) | - BIT(POWER_SUPPLY_USB_TYPE_PD_PPS); + BIT(POWER_SUPPLY_USB_TYPE_PD_PPS) | + BIT(POWER_SUPPLY_USB_TYPE_PD_PPS_SPR_AVS) | + BIT(POWER_SUPPLY_USB_TYPE_PD_SPR_AVS); port->psy_desc.properties = tcpm_psy_props; port->psy_desc.num_properties = ARRAY_SIZE(tcpm_psy_props); port->psy_desc.get_property = tcpm_psy_get_prop; @@ -7721,20 +8500,20 @@ 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); init_completion(&port->tx_complete); init_completion(&port->swap_complete); - init_completion(&port->pps_complete); + init_completion(&port->aug_supply_req_complete); tcpm_debugfs_init(port); err = tcpm_fw_get_caps(port, tcpc->fwnode); @@ -7764,9 +8543,9 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->partner_desc.identity = &port->partner_ident; - port->role_sw = usb_role_switch_get(port->dev); + port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode); if (!port->role_sw) - port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode); + port->role_sw = usb_role_switch_get(port->dev); if (IS_ERR(port->role_sw)) { err = PTR_ERR(port->role_sw); goto out_destroy_wq; diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 7ee721a877c1..43faec794b95 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -16,6 +16,9 @@ #include <linux/interrupt.h> #include <linux/usb/typec.h> #include <linux/usb/typec_altmode.h> +#include <linux/usb/typec_dp.h> +#include <linux/usb/typec_mux.h> +#include <linux/usb/typec_tbt.h> #include <linux/usb/role.h> #include <linux/workqueue.h> #include <linux/firmware.h> @@ -35,14 +38,18 @@ #define TPS_REG_INT_MASK2 0x17 #define TPS_REG_INT_CLEAR1 0x18 #define TPS_REG_INT_CLEAR2 0x19 -#define TPS_REG_SYSTEM_POWER_STATE 0x20 #define TPS_REG_STATUS 0x1a +#define TPS_REG_SYSTEM_POWER_STATE 0x20 +#define TPS_REG_USB4_STATUS 0x24 #define TPS_REG_SYSTEM_CONF 0x28 #define TPS_REG_CTRL_CONF 0x29 #define TPS_REG_BOOT_STATUS 0x2D #define TPS_REG_POWER_STATUS 0x3f #define TPS_REG_PD_STATUS 0x40 #define TPS_REG_RX_IDENTITY_SOP 0x48 +#define TPS_REG_CF_VID_STATUS 0x5e +#define TPS_REG_DP_SID_STATUS 0x58 +#define TPS_REG_INTEL_VID_STATUS 0x59 #define TPS_REG_DATA_STATUS 0x5f #define TPS_REG_SLEEP_CONF 0x70 @@ -85,10 +92,38 @@ struct tps6598x_rx_identity_reg { struct usb_pd_identity identity; } __packed; +/* TPS_REG_USB4_STATUS */ +struct tps6598x_usb4_status_reg { + u8 mode_status; + __le32 eudo; + __le32 unknown; +} __packed; + +/* TPS_REG_DP_SID_STATUS */ +struct tps6598x_dp_sid_status_reg { + u8 mode_status; + __le32 status_tx; + __le32 status_rx; + __le32 configure; + __le32 mode_data; +} __packed; + +/* TPS_REG_INTEL_VID_STATUS */ +struct tps6598x_intel_vid_status_reg { + u8 mode_status; + __le32 attention_vdo; + __le16 enter_vdo; + __le16 device_mode; + __le16 cable_mode; +} __packed; + /* Standard Task return codes */ #define TPS_TASK_TIMEOUT 1 #define TPS_TASK_REJECTED 3 +/* Debounce delay for mode changes, in milliseconds */ +#define CD321X_DEBOUNCE_DELAY_MS 500 + enum { TPS_MODE_APP, TPS_MODE_BOOT, @@ -112,12 +147,20 @@ struct tps6598x; struct tipd_data { irq_handler_t irq_handler; + u64 irq_mask1; + size_t tps_struct_size; + void (*remove)(struct tps6598x *tps); int (*register_port)(struct tps6598x *tps, struct fwnode_handle *node); + void (*unregister_port)(struct tps6598x *tps); + void (*trace_data_status)(u32 status); void (*trace_power_status)(u16 status); void (*trace_status)(u32 status); int (*apply_patch)(struct tps6598x *tps); int (*init)(struct tps6598x *tps); + int (*switch_power_state)(struct tps6598x *tps, u8 target_state); + bool (*read_data_status)(struct tps6598x *tps); int (*reset)(struct tps6598x *tps); + int (*connect)(struct tps6598x *tps, u32 status); }; struct tps6598x { @@ -139,12 +182,42 @@ struct tps6598x { int wakeup; u32 status; /* status reg */ + u32 data_status; u16 pwr_status; struct delayed_work wq_poll; const struct tipd_data *data; }; +struct cd321x_status { + u32 status; + u32 pwr_status; + u32 data_status; + u32 status_changed; + struct usb_pd_identity partner_identity; + struct tps6598x_dp_sid_status_reg dp_sid_status; + struct tps6598x_intel_vid_status_reg intel_vid_status; + struct tps6598x_usb4_status_reg usb4_status; +}; + +struct cd321x { + struct tps6598x tps; + + struct tps6598x_dp_sid_status_reg dp_sid_status; + struct tps6598x_intel_vid_status_reg intel_vid_status; + struct tps6598x_usb4_status_reg usb4_status; + + struct typec_altmode *port_altmode_dp; + struct typec_altmode *port_altmode_tbt; + + struct typec_mux *mux; + struct typec_mux_state state; + + struct cd321x_status update_status; + struct delayed_work update_work; + struct usb_pd_identity cur_partner_identity; +}; + static enum power_supply_property tps6598x_psy_props[] = { POWER_SUPPLY_PROP_USB_TYPE, POWER_SUPPLY_PROP_ONLINE, @@ -490,7 +563,51 @@ static bool tps6598x_read_data_status(struct tps6598x *tps) dev_err(tps->dev, "failed to read data status: %d\n", ret); return false; } - trace_tps6598x_data_status(data_status); + tps->data_status = data_status; + + if (tps->data->trace_data_status) + tps->data->trace_data_status(data_status); + + return true; +} + +static bool cd321x_read_data_status(struct tps6598x *tps) +{ + struct cd321x *cd321x = container_of(tps, struct cd321x, tps); + int ret; + + ret = tps6598x_read_data_status(tps); + if (!ret) + return false; + + if (tps->data_status & TPS_DATA_STATUS_DP_CONNECTION) { + ret = tps6598x_block_read(tps, TPS_REG_DP_SID_STATUS, + &cd321x->dp_sid_status, sizeof(cd321x->dp_sid_status)); + if (ret) { + dev_err(tps->dev, "Failed to read DP SID Status: %d\n", + ret); + return false; + } + } + + if (tps->data_status & TPS_DATA_STATUS_TBT_CONNECTION) { + ret = tps6598x_block_read(tps, TPS_REG_INTEL_VID_STATUS, + &cd321x->intel_vid_status, sizeof(cd321x->intel_vid_status)); + if (ret) { + dev_err(tps->dev, "Failed to read Intel VID Status: %d\n", ret); + return false; + } + } + + if (tps->data_status & CD321X_DATA_STATUS_USB4_CONNECTION) { + ret = tps6598x_block_read(tps, TPS_REG_USB4_STATUS, + &cd321x->usb4_status, sizeof(cd321x->usb4_status)); + if (ret) { + dev_err(tps->dev, + "Failed to read USB4 Status: %d\n", ret); + return false; + } + } return true; } @@ -526,6 +643,235 @@ static void tps6598x_handle_plug_event(struct tps6598x *tps, u32 status) } } +static void cd321x_typec_update_mode(struct tps6598x *tps, struct cd321x_status *st) +{ + struct cd321x *cd321x = container_of(tps, struct cd321x, tps); + + if (!(st->data_status & TPS_DATA_STATUS_DATA_CONNECTION)) { + if (cd321x->state.mode == TYPEC_STATE_SAFE) + return; + cd321x->state.alt = NULL; + cd321x->state.mode = TYPEC_STATE_SAFE; + cd321x->state.data = NULL; + typec_mux_set(cd321x->mux, &cd321x->state); + } else if (st->data_status & TPS_DATA_STATUS_DP_CONNECTION) { + struct typec_displayport_data dp_data; + unsigned long mode; + + switch (TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT(st->data_status)) { + case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_A: + mode = TYPEC_DP_STATE_A; + break; + case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_B: + mode = TYPEC_DP_STATE_B; + break; + case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_C: + mode = TYPEC_DP_STATE_C; + break; + case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_D: + mode = TYPEC_DP_STATE_D; + break; + case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_E: + mode = TYPEC_DP_STATE_E; + break; + case TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_F: + mode = TYPEC_DP_STATE_F; + break; + default: + dev_err(tps->dev, "Invalid DP pin assignment\n"); + return; + } + + if (cd321x->state.alt == cd321x->port_altmode_dp && + cd321x->state.mode == mode) { + return; + } + + dp_data.status = le32_to_cpu(st->dp_sid_status.status_rx); + dp_data.conf = le32_to_cpu(st->dp_sid_status.configure); + cd321x->state.alt = cd321x->port_altmode_dp; + cd321x->state.data = &dp_data; + cd321x->state.mode = mode; + typec_mux_set(cd321x->mux, &cd321x->state); + } else if (st->data_status & TPS_DATA_STATUS_TBT_CONNECTION) { + struct typec_thunderbolt_data tbt_data; + + if (cd321x->state.alt == cd321x->port_altmode_tbt && + cd321x->state.mode == TYPEC_TBT_MODE) + return; + + tbt_data.cable_mode = le16_to_cpu(st->intel_vid_status.cable_mode); + tbt_data.device_mode = le16_to_cpu(st->intel_vid_status.device_mode); + tbt_data.enter_vdo = le16_to_cpu(st->intel_vid_status.enter_vdo); + cd321x->state.alt = cd321x->port_altmode_tbt; + cd321x->state.mode = TYPEC_TBT_MODE; + cd321x->state.data = &tbt_data; + typec_mux_set(cd321x->mux, &cd321x->state); + } else if (st->data_status & CD321X_DATA_STATUS_USB4_CONNECTION) { + struct enter_usb_data eusb_data; + + if (cd321x->state.alt == NULL && cd321x->state.mode == TYPEC_MODE_USB4) + return; + + eusb_data.eudo = le32_to_cpu(st->usb4_status.eudo); + eusb_data.active_link_training = + !!(st->data_status & TPS_DATA_STATUS_ACTIVE_LINK_TRAIN); + + cd321x->state.alt = NULL; + cd321x->state.data = &eusb_data; + cd321x->state.mode = TYPEC_MODE_USB4; + typec_mux_set(cd321x->mux, &cd321x->state); + } else { + if (cd321x->state.alt == NULL && cd321x->state.mode == TYPEC_STATE_USB) + return; + cd321x->state.alt = NULL; + cd321x->state.mode = TYPEC_STATE_USB; + cd321x->state.data = NULL; + typec_mux_set(cd321x->mux, &cd321x->state); + } + + /* Clear data since it's no longer used after typec_mux_set and points to the stack */ + cd321x->state.data = NULL; +} + +static void cd321x_update_work(struct work_struct *work) +{ + struct cd321x *cd321x = container_of(to_delayed_work(work), + struct cd321x, update_work); + struct tps6598x *tps = &cd321x->tps; + struct cd321x_status st; + + guard(mutex)(&tps->lock); + + st = cd321x->update_status; + cd321x->update_status.status_changed = 0; + + bool old_connected = !!tps->partner; + bool new_connected = st.status & TPS_STATUS_PLUG_PRESENT; + bool was_disconnected = st.status_changed & TPS_STATUS_PLUG_PRESENT; + + bool usb_connection = st.data_status & + (TPS_DATA_STATUS_USB2_CONNECTION | TPS_DATA_STATUS_USB3_CONNECTION); + + enum usb_role old_role = usb_role_switch_get_role(tps->role_sw); + enum usb_role new_role = USB_ROLE_NONE; + enum typec_pwr_opmode pwr_opmode = TYPEC_PWR_MODE_USB; + enum typec_orientation orientation = TYPEC_ORIENTATION_NONE; + + if (usb_connection) { + if (tps->data_status & TPS_DATA_STATUS_USB_DATA_ROLE) + new_role = USB_ROLE_DEVICE; + else + new_role = USB_ROLE_HOST; + } + + if (new_connected) { + pwr_opmode = TPS_POWER_STATUS_PWROPMODE(st.pwr_status); + orientation = TPS_STATUS_TO_UPSIDE_DOWN(st.status) ? + TYPEC_ORIENTATION_REVERSE : TYPEC_ORIENTATION_NORMAL; + } + + bool is_pd = pwr_opmode == TYPEC_PWR_MODE_PD; + bool partner_changed = old_connected && new_connected && + (was_disconnected || + (is_pd && memcmp(&st.partner_identity, + &cd321x->cur_partner_identity, sizeof(struct usb_pd_identity)))); + + /* If we are switching from an active role, transition to USB_ROLE_NONE first */ + if (old_role != USB_ROLE_NONE && (new_role != old_role || was_disconnected)) + usb_role_switch_set_role(tps->role_sw, USB_ROLE_NONE); + + /* Process partner disconnection or change */ + if (!new_connected || partner_changed) { + if (!IS_ERR(tps->partner)) + typec_unregister_partner(tps->partner); + tps->partner = NULL; + } + + /* If there was a disconnection, set PHY to off */ + if (!new_connected || was_disconnected) { + cd321x->state.alt = NULL; + cd321x->state.mode = TYPEC_STATE_SAFE; + cd321x->state.data = NULL; + typec_set_mode(tps->port, TYPEC_STATE_SAFE); + } + + /* Update Type-C properties */ + typec_set_pwr_opmode(tps->port, pwr_opmode); + typec_set_pwr_role(tps->port, TPS_STATUS_TO_TYPEC_PORTROLE(st.status)); + typec_set_vconn_role(tps->port, TPS_STATUS_TO_TYPEC_VCONN(st.status)); + typec_set_orientation(tps->port, orientation); + typec_set_data_role(tps->port, TPS_STATUS_TO_TYPEC_DATAROLE(st.status)); + power_supply_changed(tps->psy); + + /* If the plug is disconnected, we are done */ + if (!new_connected) + return; + + /* Set up partner if we were previously disconnected (or changed). */ + if (!tps->partner) { + struct typec_partner_desc desc; + + desc.usb_pd = is_pd; + desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */ + desc.identity = NULL; + + if (desc.usb_pd) + desc.identity = &st.partner_identity; + + tps->partner = typec_register_partner(tps->port, &desc); + if (IS_ERR(tps->partner)) { + dev_warn(tps->dev, "%s: failed to register partner\n", __func__); + return; + } + + if (desc.identity) { + typec_partner_set_identity(tps->partner); + cd321x->cur_partner_identity = st.partner_identity; + } + } + + /* Update the TypeC MUX/PHY state */ + cd321x_typec_update_mode(tps, &st); + + /* Launch the USB role switch */ + usb_role_switch_set_role(tps->role_sw, new_role); + + power_supply_changed(tps->psy); +} + +static void cd321x_queue_status(struct cd321x *cd321x) +{ + cd321x->update_status.status_changed |= cd321x->update_status.status ^ cd321x->tps.status; + + cd321x->update_status.status = cd321x->tps.status; + cd321x->update_status.pwr_status = cd321x->tps.pwr_status; + cd321x->update_status.data_status = cd321x->tps.data_status; + + cd321x->update_status.partner_identity = cd321x->tps.partner_identity; + cd321x->update_status.dp_sid_status = cd321x->dp_sid_status; + cd321x->update_status.intel_vid_status = cd321x->intel_vid_status; + cd321x->update_status.usb4_status = cd321x->usb4_status; +} + +static int cd321x_connect(struct tps6598x *tps, u32 status) +{ + struct cd321x *cd321x = container_of(tps, struct cd321x, tps); + + tps->status = status; + cd321x_queue_status(cd321x); + + /* + * Cancel pending work if not already running, then requeue after CD321X_DEBOUNCE_DELAY_MS + * regardless since the work function will check for any plug or altmodes changes since + * its last run anyway. + */ + cancel_delayed_work(&cd321x->update_work); + schedule_delayed_work(&cd321x->update_work, msecs_to_jiffies(CD321X_DEBOUNCE_DELAY_MS)); + + return 0; +} + static irqreturn_t cd321x_interrupt(int irq, void *data) { struct tps6598x *tps = data; @@ -545,23 +891,28 @@ static irqreturn_t cd321x_interrupt(int irq, void *data) if (!event) goto err_unlock; + tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event); + if (!tps6598x_read_status(tps, &status)) - goto err_clear_ints; + goto err_unlock; - if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE) + if (event & APPLE_CD_REG_INT_POWER_STATUS_UPDATE) { if (!tps6598x_read_power_status(tps)) - goto err_clear_ints; + goto err_unlock; + if (TPS_POWER_STATUS_PWROPMODE(tps->pwr_status) == TYPEC_PWR_MODE_PD) { + if (tps6598x_read_partner_identity(tps)) { + dev_err(tps->dev, "failed to read partner identity\n"); + tps->partner_identity = (struct usb_pd_identity) {0}; + } + } + } if (event & APPLE_CD_REG_INT_DATA_STATUS_UPDATE) - if (!tps6598x_read_data_status(tps)) - goto err_clear_ints; + if (!tps->data->read_data_status(tps)) + goto err_unlock; - /* Handle plug insert or removal */ - if (event & APPLE_CD_REG_INT_PLUG_EVENT) - tps6598x_handle_plug_event(tps, status); - -err_clear_ints: - tps6598x_write64(tps, TPS_REG_INT_CLEAR1, event); + /* Can be called uncondtionally since it will check for any changes itself */ + cd321x_connect(tps, status); err_unlock: mutex_unlock(&tps->lock); @@ -605,7 +956,7 @@ static irqreturn_t tps25750_interrupt(int irq, void *data) goto err_clear_ints; if (event[0] & TPS_REG_INT_DATA_STATUS_UPDATE) - if (!tps6598x_read_data_status(tps)) + if (!tps->data->read_data_status(tps)) goto err_clear_ints; /* @@ -668,25 +1019,24 @@ static irqreturn_t tps6598x_interrupt(int irq, void *data) if (!(event1[0] | event1[1] | event2[0] | event2[1])) goto err_unlock; + tps6598x_block_write(tps, TPS_REG_INT_CLEAR1, event1, intev_len); + tps6598x_block_write(tps, TPS_REG_INT_CLEAR2, event2, intev_len); + if (!tps6598x_read_status(tps, &status)) - goto err_clear_ints; + goto err_unlock; if ((event1[0] | event2[0]) & TPS_REG_INT_POWER_STATUS_UPDATE) if (!tps6598x_read_power_status(tps)) - goto err_clear_ints; + goto err_unlock; if ((event1[0] | event2[0]) & TPS_REG_INT_DATA_STATUS_UPDATE) - if (!tps6598x_read_data_status(tps)) - goto err_clear_ints; + if (!tps->data->read_data_status(tps)) + goto err_unlock; /* Handle plug insert or removal */ if ((event1[0] | event2[0]) & TPS_REG_INT_PLUG_EVENT) tps6598x_handle_plug_event(tps, status); -err_clear_ints: - tps6598x_block_write(tps, TPS_REG_INT_CLEAR1, event1, intev_len); - tps6598x_block_write(tps, TPS_REG_INT_CLEAR2, event2, intev_len); - err_unlock: mutex_unlock(&tps->lock); @@ -887,6 +1237,94 @@ tps6598x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) return 0; } +static int cd321x_register_port_altmodes(struct cd321x *cd321x) +{ + struct typec_altmode_desc desc; + struct typec_altmode *amode; + + memset(&desc, 0, sizeof(desc)); + desc.svid = USB_TYPEC_DP_SID; + desc.mode = USB_TYPEC_DP_MODE; + desc.vdo = DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D)); + desc.vdo |= DP_CAP_DFP_D; + amode = typec_port_register_altmode(cd321x->tps.port, &desc); + if (IS_ERR(amode)) + return PTR_ERR(amode); + cd321x->port_altmode_dp = amode; + + memset(&desc, 0, sizeof(desc)); + desc.svid = USB_TYPEC_TBT_SID; + desc.mode = TYPEC_ANY_MODE; + amode = typec_port_register_altmode(cd321x->tps.port, &desc); + if (IS_ERR(amode)) { + typec_unregister_altmode(cd321x->port_altmode_dp); + cd321x->port_altmode_dp = NULL; + return PTR_ERR(amode); + } + cd321x->port_altmode_tbt = amode; + + return 0; +} + +static int +cd321x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) +{ + struct cd321x *cd321x = container_of(tps, struct cd321x, tps); + int ret; + + INIT_DELAYED_WORK(&cd321x->update_work, cd321x_update_work); + + ret = tps6598x_register_port(tps, fwnode); + if (ret) + return ret; + + ret = cd321x_register_port_altmodes(cd321x); + if (ret) + goto err_unregister_port; + + cd321x->mux = fwnode_typec_mux_get(fwnode); + if (IS_ERR(cd321x->mux)) { + ret = PTR_ERR(cd321x->mux); + goto err_unregister_altmodes; + } + + cd321x->state.alt = NULL; + cd321x->state.mode = TYPEC_STATE_SAFE; + cd321x->state.data = NULL; + typec_set_mode(tps->port, TYPEC_STATE_SAFE); + + return 0; + +err_unregister_altmodes: + typec_unregister_altmode(cd321x->port_altmode_dp); + typec_unregister_altmode(cd321x->port_altmode_tbt); + cd321x->port_altmode_dp = NULL; + cd321x->port_altmode_tbt = NULL; +err_unregister_port: + typec_unregister_port(tps->port); + return ret; +} + +static void +tps6598x_unregister_port(struct tps6598x *tps) +{ + typec_unregister_port(tps->port); +} + +static void +cd321x_unregister_port(struct tps6598x *tps) +{ + struct cd321x *cd321x = container_of(tps, struct cd321x, tps); + + typec_mux_put(cd321x->mux); + cd321x->mux = NULL; + typec_unregister_altmode(cd321x->port_altmode_dp); + cd321x->port_altmode_dp = NULL; + typec_unregister_altmode(cd321x->port_altmode_tbt); + cd321x->port_altmode_tbt = NULL; + typec_unregister_port(tps->port); +} + static int tps_request_firmware(struct tps6598x *tps, const struct firmware **fw, const char **firmware_name) { @@ -1265,6 +1703,7 @@ tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) typec_cap.data = ret; typec_cap.revision = USB_TYPEC_REV_1_3; typec_cap.pd_revision = 0x300; + typec_cap.orientation_aware = true; typec_cap.driver_data = tps; typec_cap.ops = &tps6598x_ops; typec_cap.fwnode = fwnode; @@ -1292,22 +1731,33 @@ tps25750_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode) return 0; } +static void cd321x_remove(struct tps6598x *tps) +{ + struct cd321x *cd321x = container_of(tps, struct cd321x, tps); + + cancel_delayed_work_sync(&cd321x->update_work); +} + static int tps6598x_probe(struct i2c_client *client) { - struct device_node *np = client->dev.of_node; + const struct tipd_data *data; struct tps6598x *tps; struct fwnode_handle *fwnode; u32 status; u32 vid; int ret; - u64 mask1; - tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + data = i2c_get_match_data(client); + if (!data) + return -EINVAL; + + tps = devm_kzalloc(&client->dev, data->tps_struct_size, GFP_KERNEL); if (!tps) return -ENOMEM; mutex_init(&tps->lock); tps->dev = &client->dev; + tps->data = data; tps->reset = devm_gpiod_get_optional(tps->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(tps->reset)) @@ -1333,28 +1783,12 @@ static int tps6598x_probe(struct i2c_client *client) if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) tps->i2c_protocol = true; - if (np && of_device_is_compatible(np, "apple,cd321x")) { - /* Switch CD321X chips to the correct system power state */ - ret = cd321x_switch_power_state(tps, TPS_SYSTEM_POWER_STATE_S0); + if (tps->data->switch_power_state) { + ret = tps->data->switch_power_state(tps, TPS_SYSTEM_POWER_STATE_S0); if (ret) return ret; - - /* CD321X chips have all interrupts masked initially */ - mask1 = APPLE_CD_REG_INT_POWER_STATUS_UPDATE | - APPLE_CD_REG_INT_DATA_STATUS_UPDATE | - APPLE_CD_REG_INT_PLUG_EVENT; - - } else { - /* Enable power status, data status and plug event interrupts */ - mask1 = TPS_REG_INT_POWER_STATUS_UPDATE | - TPS_REG_INT_DATA_STATUS_UPDATE | - TPS_REG_INT_PLUG_EVENT; } - tps->data = i2c_get_match_data(client); - if (!tps->data) - return -EINVAL; - /* Make sure the controller has application firmware running */ ret = tps6598x_check_mode(tps); if (ret < 0) @@ -1366,7 +1800,7 @@ static int tps6598x_probe(struct i2c_client *client) return ret; } - ret = tps6598x_write64(tps, TPS_REG_INT_MASK1, mask1); + ret = tps6598x_write64(tps, TPS_REG_INT_MASK1, tps->data->irq_mask1); if (ret) goto err_reset_controller; @@ -1401,12 +1835,11 @@ static int tps6598x_probe(struct i2c_client *client) goto err_role_put; if (status & TPS_STATUS_PLUG_PRESENT) { - ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &tps->pwr_status); - if (ret < 0) { - dev_err(tps->dev, "failed to read power status: %d\n", ret); + if (!tps6598x_read_power_status(tps)) goto err_unregister_port; - } - ret = tps6598x_connect(tps, status); + if (!tps->data->read_data_status(tps)) + goto err_unregister_port; + ret = tps->data->connect(tps, status); if (ret) dev_err(&client->dev, "failed to register partner\n"); } @@ -1431,7 +1864,7 @@ static int tps6598x_probe(struct i2c_client *client) tps->wakeup = device_property_read_bool(tps->dev, "wakeup-source"); if (tps->wakeup && client->irq) { - device_init_wakeup(&client->dev, true); + devm_device_init_wakeup(&client->dev); enable_irq_wake(client->irq); } @@ -1440,7 +1873,7 @@ static int tps6598x_probe(struct i2c_client *client) err_disconnect: tps6598x_disconnect(tps, 0); err_unregister_port: - typec_unregister_port(tps->port); + tps->data->unregister_port(tps); err_role_put: usb_role_switch_put(tps->role_sw); err_fwnode_put: @@ -1463,8 +1896,11 @@ static void tps6598x_remove(struct i2c_client *client) else devm_free_irq(tps->dev, client->irq, tps); + if (tps->data->remove) + tps->data->remove(tps); + tps6598x_disconnect(tps, 0); - typec_unregister_port(tps->port); + tps->data->unregister_port(tps); usb_role_switch_put(tps->role_sw); /* Reset PD controller to remove any applied patch */ @@ -1529,31 +1965,57 @@ static const struct dev_pm_ops tps6598x_pm_ops = { static const struct tipd_data cd321x_data = { .irq_handler = cd321x_interrupt, - .register_port = tps6598x_register_port, + .irq_mask1 = APPLE_CD_REG_INT_POWER_STATUS_UPDATE | + APPLE_CD_REG_INT_DATA_STATUS_UPDATE | + APPLE_CD_REG_INT_PLUG_EVENT, + .tps_struct_size = sizeof(struct cd321x), + .remove = cd321x_remove, + .register_port = cd321x_register_port, + .unregister_port = cd321x_unregister_port, + .trace_data_status = trace_cd321x_data_status, .trace_power_status = trace_tps6598x_power_status, .trace_status = trace_tps6598x_status, .init = cd321x_init, + .read_data_status = cd321x_read_data_status, .reset = cd321x_reset, + .switch_power_state = cd321x_switch_power_state, + .connect = cd321x_connect, }; static const struct tipd_data tps6598x_data = { .irq_handler = tps6598x_interrupt, + .irq_mask1 = TPS_REG_INT_POWER_STATUS_UPDATE | + TPS_REG_INT_DATA_STATUS_UPDATE | + TPS_REG_INT_PLUG_EVENT, + .tps_struct_size = sizeof(struct tps6598x), .register_port = tps6598x_register_port, + .unregister_port = tps6598x_unregister_port, + .trace_data_status = trace_tps6598x_data_status, .trace_power_status = trace_tps6598x_power_status, .trace_status = trace_tps6598x_status, .apply_patch = tps6598x_apply_patch, .init = tps6598x_init, + .read_data_status = tps6598x_read_data_status, .reset = tps6598x_reset, + .connect = tps6598x_connect, }; static const struct tipd_data tps25750_data = { .irq_handler = tps25750_interrupt, + .irq_mask1 = TPS_REG_INT_POWER_STATUS_UPDATE | + TPS_REG_INT_DATA_STATUS_UPDATE | + TPS_REG_INT_PLUG_EVENT, + .tps_struct_size = sizeof(struct tps6598x), .register_port = tps25750_register_port, + .unregister_port = tps6598x_unregister_port, + .trace_data_status = trace_tps6598x_data_status, .trace_power_status = trace_tps25750_power_status, .trace_status = trace_tps25750_status, .apply_patch = tps25750_apply_patch, .init = tps25750_init, + .read_data_status = tps6598x_read_data_status, .reset = tps25750_reset, + .connect = tps6598x_connect, }; static const struct of_device_id tps6598x_of_match[] = { diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h index 9b23e9017452..03edbb77bbd6 100644 --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -27,7 +27,7 @@ #define TPS_STATUS_OVERCURRENT BIT(16) #define TPS_STATUS_GOTO_MIN_ACTIVE BIT(26) #define TPS_STATUS_BIST BIT(27) -#define TPS_STATUS_HIGH_VOLAGE_WARNING BIT(28) +#define TPS_STATUS_HIGH_VOLTAGE_WARNING BIT(28) #define TPS_STATUS_HIGH_LOW_VOLTAGE_WARNING BIT(29) #define TPS_STATUS_CONN_STATE_MASK GENMASK(3, 1) @@ -197,6 +197,11 @@ #define TPS_DATA_STATUS_FORCE_LSX BIT(23) #define TPS_DATA_STATUS_POWER_MISMATCH BIT(24) +/* modified TPS_REG_DATA_STATUS bits for CD321x (and likely also TPS65987DDK) */ +#define CD321X_DATA_STATUS_HPD_IRQ BIT(14) +#define CD321X_DATA_STATUS_HPD_LEVEL BIT(15) +#define CD321X_DATA_STATUS_USB4_CONNECTION BIT(23) + #define TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK GENMASK(11, 10) #define TPS_DATA_STATUS_DP_PIN_ASSIGNMENT(x) \ TPS_FIELD_GET(TPS_DATA_STATUS_DP_PIN_ASSIGNMENT_MASK, (x)) diff --git a/drivers/usb/typec/tipd/trace.h b/drivers/usb/typec/tipd/trace.h index 0669cca12ea1..e9e40425138a 100644 --- a/drivers/usb/typec/tipd/trace.h +++ b/drivers/usb/typec/tipd/trace.h @@ -153,7 +153,7 @@ { TPS_STATUS_OVERCURRENT, "OVERCURRENT" }, \ { TPS_STATUS_GOTO_MIN_ACTIVE, "GOTO_MIN_ACTIVE" }, \ { TPS_STATUS_BIST, "BIST" }, \ - { TPS_STATUS_HIGH_VOLAGE_WARNING, "HIGH_VOLAGE_WARNING" }, \ + { TPS_STATUS_HIGH_VOLTAGE_WARNING, "HIGH_VOLTAGE_WARNING" }, \ { TPS_STATUS_HIGH_LOW_VOLTAGE_WARNING, "HIGH_LOW_VOLTAGE_WARNING" }) #define show_tps25750_status_flags(flags) \ @@ -217,6 +217,26 @@ { TPS_DATA_STATUS_FORCE_LSX, "FORCE_LSX" }, \ { TPS_DATA_STATUS_POWER_MISMATCH, "POWER_MISMATCH" }) +#define show_cd321x_data_status_flags(data_status) \ + __print_flags(data_status & TPS_DATA_STATUS_FLAGS_MASK, "|", \ + { TPS_DATA_STATUS_DATA_CONNECTION, "DATA_CONNECTION" }, \ + { TPS_DATA_STATUS_UPSIDE_DOWN, "DATA_UPSIDE_DOWN" }, \ + { TPS_DATA_STATUS_ACTIVE_CABLE, "ACTIVE_CABLE" }, \ + { TPS_DATA_STATUS_USB2_CONNECTION, "USB2_CONNECTION" }, \ + { TPS_DATA_STATUS_USB3_CONNECTION, "USB3_CONNECTION" }, \ + { TPS_DATA_STATUS_USB3_GEN2, "USB3_GEN2" }, \ + { TPS_DATA_STATUS_USB_DATA_ROLE, "USB_DATA_ROLE" }, \ + { TPS_DATA_STATUS_DP_CONNECTION, "DP_CONNECTION" }, \ + { TPS_DATA_STATUS_DP_SINK, "DP_SINK" }, \ + { CD321X_DATA_STATUS_HPD_IRQ, "HPD_IRQ" }, \ + { CD321X_DATA_STATUS_HPD_LEVEL, "HPD_LEVEL" }, \ + { TPS_DATA_STATUS_TBT_CONNECTION, "TBT_CONNECTION" }, \ + { TPS_DATA_STATUS_TBT_TYPE, "TBT_TYPE" }, \ + { TPS_DATA_STATUS_OPTICAL_CABLE, "OPTICAL_CABLE" }, \ + { TPS_DATA_STATUS_ACTIVE_LINK_TRAIN, "ACTIVE_LINK_TRAIN" }, \ + { CD321X_DATA_STATUS_USB4_CONNECTION, "USB4" }, \ + { TPS_DATA_STATUS_POWER_MISMATCH, "POWER_MISMATCH" }) + #define show_data_status_dp_pin_assignment(data_status) \ __print_symbolic(TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT(data_status), \ { TPS_DATA_STATUS_DP_SPEC_PIN_ASSIGNMENT_E, "E" }, \ @@ -388,6 +408,25 @@ TRACE_EVENT(tps6598x_data_status, ) ); +TRACE_EVENT(cd321x_data_status, + TP_PROTO(u32 data_status), + TP_ARGS(data_status), + + TP_STRUCT__entry( + __field(u32, data_status) + ), + + TP_fast_assign( + __entry->data_status = data_status; + ), + + TP_printk("%s%s%s", + show_cd321x_data_status_flags(__entry->data_status), + __entry->data_status & TPS_DATA_STATUS_DP_CONNECTION ? ", DP pinout " : "", + maybe_show_data_status_dp_pin_assignment(__entry->data_status) + ) +); + #endif /* _TPS6598X_TRACE_H_ */ /* This part must be outside protection */ diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index 75559601fe8f..87dd992a4b9e 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -73,7 +73,6 @@ 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 @@ -85,10 +84,24 @@ config CROS_EC_UCSI config UCSI_LENOVO_YOGA_C630 tristate "UCSI Interface Driver for Lenovo Yoga C630" depends on EC_LENOVO_YOGA_C630 + depends on DRM || !DRM + select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF help This driver enables UCSI support on the Lenovo Yoga C630 laptop. To compile the driver as a module, choose M here: the module will be called ucsi_yoga_c630. +config UCSI_HUAWEI_GAOKUN + tristate "UCSI Interface Driver for Huawei Matebook E Go" + depends on EC_HUAWEI_GAOKUN + depends on DRM || !DRM + select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF + help + This driver enables UCSI support on the Huawei Matebook E Go tablet, + which is a sc8280xp-based 2-in-1 tablet. + + To compile the driver as a module, choose M here: the module will be + called ucsi_huawei_gaokun. + endif diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index be98a879104d..c7e38bf01350 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -17,9 +17,14 @@ ifneq ($(CONFIG_TYPEC_DP_ALTMODE),) typec_ucsi-y += displayport.o endif +ifneq ($(CONFIG_TYPEC_TBT_ALTMODE),) + typec_ucsi-y += thunderbolt.o +endif + 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 +obj-$(CONFIG_UCSI_HUAWEI_GAOKUN) += ucsi_huawei_gaokun.o diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c index c605c8616726..251aa7251ce6 100644 --- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c +++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c @@ -5,17 +5,20 @@ * Copyright 2024 Google LLC. */ +#include <linux/acpi.h> #include <linux/container_of.h> #include <linux/dev_printk.h> #include <linux/jiffies.h> #include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_data/cros_ec_commands.h> #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 <linux/usb/typec_altmode.h> #include "ucsi.h" @@ -33,6 +36,11 @@ /* Number of times to attempt recovery from a write timeout before giving up. */ #define WRITE_TMO_CTR_MAX 5 +/* Delay between mode entry/exit attempts, ms */ +static const unsigned int mode_selection_delay = 1000; +/* Timeout for a mode entry attempt, ms */ +static const unsigned int mode_selection_timeout = 4000; + struct cros_ucsi_data { struct device *dev; struct ucsi *ucsi; @@ -105,12 +113,13 @@ static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd) return 0; } -static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd) +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); + ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size); switch (ret) { case -EBUSY: /* EC may return -EBUSY if CCI.busy is set. @@ -133,12 +142,29 @@ static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd) return ret; } +static void cros_ucsi_add_partner_altmodes(struct ucsi_connector *con) +{ + if (!con->typec_cap.no_mode_control) + typec_mode_selection_start(con->partner, + mode_selection_delay, + mode_selection_timeout); +} + +static void cros_ucsi_remove_partner_altmodes(struct ucsi_connector *con) +{ + if (!con->typec_cap.no_mode_control) + typec_mode_selection_delete(con->partner); +} + static const struct ucsi_operations cros_ucsi_ops = { .read_version = cros_ucsi_read_version, .read_cci = cros_ucsi_read_cci, + .poll_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, + .add_partner_altmodes = cros_ucsi_add_partner_altmodes, + .remove_partner_altmodes = cros_ucsi_remove_partner_altmodes, }; static void cros_ucsi_work(struct work_struct *work) @@ -205,12 +231,19 @@ static int cros_ucsi_event(struct notifier_block *nb, { struct cros_ucsi_data *udata = container_of(nb, struct cros_ucsi_data, nb); - if (!(host_event & PD_EVENT_PPM)) - return NOTIFY_OK; + 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); + } - dev_dbg(udata->dev, "UCSI notification received\n"); - flush_work(&udata->work); - schedule_work(&udata->work); + 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; } @@ -226,7 +259,6 @@ static void cros_ucsi_destroy(struct cros_ucsi_data *udata) 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; @@ -234,9 +266,16 @@ static int cros_ucsi_probe(struct platform_device *pdev) if (!udata) return -ENOMEM; + /* ACPI and OF FW nodes for cros_ec_ucsi are children of the ChromeOS EC. If the + * cros_ec_ucsi device has an ACPI or OF FW node, its parent is the ChromeOS EC device. + * Platforms without a FW node for cros_ec_ucsi may add it as a subdevice of cros_ec_dev. + */ udata->dev = dev; + if (is_acpi_device_node(dev->fwnode) || is_of_node(dev->fwnode)) + udata->ec = dev_get_drvdata(dev->parent); + else + udata->ec = ((struct cros_ec_dev *)dev_get_drvdata(dev->parent))->ec_dev; - udata->ec = ec_data->ec_dev; if (!udata->ec) return dev_err_probe(dev, -ENODEV, "couldn't find parent EC device\n"); @@ -317,10 +356,24 @@ static const struct platform_device_id cros_ucsi_id[] = { }; MODULE_DEVICE_TABLE(platform, cros_ucsi_id); +static const struct acpi_device_id cros_ec_ucsi_acpi_device_ids[] = { + { "GOOG0021", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_ec_ucsi_acpi_device_ids); + +static const struct of_device_id cros_ucsi_of_match[] = { + { .compatible = "google,cros-ec-ucsi", }, + {} +}; +MODULE_DEVICE_TABLE(of, cros_ucsi_of_match); + static struct platform_driver cros_ucsi_driver = { .driver = { .name = KBUILD_MODNAME, .pm = &cros_ucsi_pm_ops, + .acpi_match_table = cros_ec_ucsi_acpi_device_ids, + .of_match_table = cros_ucsi_of_match, }, .id_table = cros_ucsi_id, .probe = cros_ucsi_probe, diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index 83ff23086d79..a4b9a6b51649 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -8,6 +8,7 @@ * Gopal Saranya <saranya.gopal@intel.com> */ #include <linux/debugfs.h> +#include <linux/hex.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/types.h> @@ -28,20 +29,30 @@ 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: + case UCSI_SET_USB: + case UCSI_SET_POWER_LEVEL: + case UCSI_READ_POWER_LEVEL: ret = ucsi_send_command(ucsi, val, NULL, 0); break; case UCSI_GET_CAPABILITY: case UCSI_GET_CONNECTOR_CAPABILITY: case UCSI_GET_ALTERNATE_MODES: + case UCSI_GET_CAM_SUPPORTED: case UCSI_GET_CURRENT_CAM: case UCSI_GET_PDOS: case UCSI_GET_CABLE_PROPERTY: case UCSI_GET_CONNECTOR_STATUS: + case UCSI_GET_ERROR_STATUS: + case UCSI_GET_PD_MESSAGE: + case UCSI_GET_ATTENTION_VDO: + case UCSI_GET_CAM_CS: + case UCSI_GET_LPM_PPM_INFO: ret = ucsi_send_command(ucsi, val, &ucsi->debugfs->response, sizeof(ucsi->debugfs->response)); @@ -72,15 +83,45 @@ static int ucsi_resp_show(struct seq_file *s, void *not_used) } DEFINE_SHOW_ATTRIBUTE(ucsi_resp); +static int ucsi_peak_curr_show(struct seq_file *m, void *v) +{ + struct ucsi *ucsi = m->private; + + seq_printf(m, "%u mA\n", ucsi->connector->peak_current); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ucsi_peak_curr); + +static int ucsi_avg_curr_show(struct seq_file *m, void *v) +{ + struct ucsi *ucsi = m->private; + + seq_printf(m, "%u mA\n", ucsi->connector->avg_current); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ucsi_avg_curr); + +static int ucsi_vbus_volt_show(struct seq_file *m, void *v) +{ + struct ucsi *ucsi = m->private; + + seq_printf(m, "%u mV\n", ucsi->connector->vbus_voltage); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ucsi_vbus_volt); + void ucsi_debugfs_register(struct ucsi *ucsi) { - ucsi->debugfs = kzalloc(sizeof(*ucsi->debugfs), GFP_KERNEL); + ucsi->debugfs = kzalloc_obj(*ucsi->debugfs); if (!ucsi->debugfs) return; ucsi->debugfs->dentry = debugfs_create_dir(dev_name(ucsi->dev), ucsi_debugfs_root); debugfs_create_file("command", 0200, ucsi->debugfs->dentry, ucsi, &ucsi_cmd_fops); debugfs_create_file("response", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_resp_fops); + debugfs_create_file("peak_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_peak_curr_fops); + debugfs_create_file("avg_current", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_avg_curr_fops); + debugfs_create_file("vbus_voltage", 0400, ucsi->debugfs->dentry, ucsi, &ucsi_vbus_volt_fops); } void ucsi_debugfs_unregister(struct ucsi *ucsi) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 420af5139c70..8aae80b457d7 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -54,7 +54,8 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) u8 cur = 0; int ret; - mutex_lock(&dp->con->lock); + if (!ucsi_con_mutex_lock(dp->con)) + return -ENOTCONN; if (!dp->override && dp->initialized) { const struct typec_altmode *p = typec_altmode_get_partner(alt); @@ -100,7 +101,7 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) schedule_work(&dp->work); ret = 0; err_unlock: - mutex_unlock(&dp->con->lock); + ucsi_con_mutex_unlock(dp->con); return ret; } @@ -112,7 +113,8 @@ static int ucsi_displayport_exit(struct typec_altmode *alt) u64 command; int ret = 0; - mutex_lock(&dp->con->lock); + if (!ucsi_con_mutex_lock(dp->con)) + return -ENOTCONN; if (!dp->override) { const struct typec_altmode *p = typec_altmode_get_partner(alt); @@ -144,7 +146,7 @@ static int ucsi_displayport_exit(struct typec_altmode *alt) schedule_work(&dp->work); out_unlock: - mutex_unlock(&dp->con->lock); + ucsi_con_mutex_unlock(dp->con); return ret; } @@ -202,20 +204,21 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt, int cmd = PD_VDO_CMD(header); int svdm_version; - mutex_lock(&dp->con->lock); + if (!ucsi_con_mutex_lock(dp->con)) + return -ENOTCONN; if (!dp->override && dp->initialized) { const struct typec_altmode *p = typec_altmode_get_partner(alt); dev_warn(&p->dev, "firmware doesn't support alternate mode overriding\n"); - mutex_unlock(&dp->con->lock); + ucsi_con_mutex_unlock(dp->con); return -EOPNOTSUPP; } svdm_version = typec_altmode_get_svdm_version(alt); if (svdm_version < 0) { - mutex_unlock(&dp->con->lock); + ucsi_con_mutex_unlock(dp->con); return svdm_version; } @@ -259,7 +262,7 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt, break; } - mutex_unlock(&dp->con->lock); + ucsi_con_mutex_unlock(dp->con); return 0; } @@ -296,6 +299,8 @@ void ucsi_displayport_remove_partner(struct typec_altmode *alt) if (!dp) return; + cancel_work_sync(&dp->work); + dp->data.conf = 0; dp->data.status = 0; dp->initialized = false; diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c index 62ac69730405..c8ebab8ba7e7 100644 --- a/drivers/usb/typec/ucsi/psy.c +++ b/drivers/usb/typec/ucsi/psy.c @@ -29,6 +29,7 @@ static enum power_supply_property ucsi_psy_props[] = { POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, }; static int ucsi_psy_get_scope(struct ucsi_connector *con, @@ -51,6 +52,29 @@ static int ucsi_psy_get_scope(struct ucsi_connector *con, return 0; } +static int ucsi_psy_get_status(struct ucsi_connector *con, + union power_supply_propval *val) +{ + bool is_sink = UCSI_CONSTAT(con, PWR_DIR) == TYPEC_SINK; + bool sink_path_enabled = true; + + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + + if (con->ucsi->version >= UCSI_VERSION_2_0) + sink_path_enabled = + UCSI_CONSTAT(con, SINK_PATH_STATUS_V2_0) == + UCSI_CONSTAT_SINK_PATH_ENABLED; + + if (UCSI_CONSTAT(con, CONNECTED)) { + if (is_sink && sink_path_enabled) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (!is_sink) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } + + return 0; +} + static int ucsi_psy_get_online(struct ucsi_connector *con, union power_supply_propval *val) { @@ -88,15 +112,20 @@ static int ucsi_psy_get_voltage_max(struct ucsi_connector *con, union power_supply_propval *val) { u32 pdo; + int max_voltage = 0; 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]; - val->intval = pdo_fixed_voltage(pdo) * 1000; - } else { - val->intval = 0; + for (int i = 0; i < con->num_pdos; i++) { + int pdo_voltage = 0; + + pdo = con->src_pdos[i]; + if (pdo_type(pdo) == PDO_TYPE_FIXED) + pdo_voltage = pdo_fixed_voltage(pdo) * 1000; + max_voltage = (pdo_voltage > max_voltage) ? pdo_voltage + : max_voltage; } + val->intval = max_voltage; break; case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0: case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: @@ -144,15 +173,25 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con, union power_supply_propval *val) { u32 pdo; + int max_current = 0; + + if (!UCSI_CONSTAT(con, CONNECTED)) { + val->intval = 0; + return 0; + } 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]; - val->intval = pdo_max_current(pdo) * 1000; - } else { - val->intval = 0; + for (int i = 0; i < con->num_pdos; i++) { + int pdo_current = 0; + + pdo = con->src_pdos[i]; + if (pdo_type(pdo) == PDO_TYPE_FIXED) + pdo_current = pdo_max_current(pdo) * 1000; + max_current = (pdo_current > max_current) ? pdo_current + : max_current; } + val->intval = max_current; break; case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: val->intval = UCSI_TYPEC_1_5_CURRENT * 1000; @@ -164,7 +203,7 @@ static int ucsi_psy_get_current_max(struct ucsi_connector *con, case UCSI_CONSTAT_PWR_OPMODE_DEFAULT: /* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */ default: - val->intval = 0; + val->intval = UCSI_TYPEC_DEFAULT_CURRENT * 1000; break; } return 0; @@ -173,10 +212,28 @@ 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) { - if (UCSI_CONSTAT(con, PWR_OPMODE) == UCSI_CONSTAT_PWR_OPMODE_PD) - val->intval = rdo_op_current(con->rdo) * 1000; - else + if (!UCSI_CONSTAT(con, CONNECTED)) { val->intval = 0; + return 0; + } + + switch (UCSI_CONSTAT(con, PWR_OPMODE)) { + case UCSI_CONSTAT_PWR_OPMODE_PD: + val->intval = rdo_op_current(con->rdo) * 1000; + break; + case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: + val->intval = UCSI_TYPEC_1_5_CURRENT * 1000; + break; + case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0: + val->intval = UCSI_TYPEC_3_0_CURRENT * 1000; + break; + case UCSI_CONSTAT_PWR_OPMODE_BC: + case UCSI_CONSTAT_PWR_OPMODE_DEFAULT: + /* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */ + default: + val->intval = UCSI_TYPEC_DEFAULT_CURRENT * 1000; + break; + } return 0; } @@ -245,6 +302,8 @@ static int ucsi_psy_get_prop(struct power_supply *psy, return ucsi_psy_get_current_now(con, val); case POWER_SUPPLY_PROP_SCOPE: return ucsi_psy_get_scope(con, val); + case POWER_SUPPLY_PROP_STATUS: + return ucsi_psy_get_status(con, val); default: return -EINVAL; } diff --git a/drivers/usb/typec/ucsi/thunderbolt.c b/drivers/usb/typec/ucsi/thunderbolt.c new file mode 100644 index 000000000000..434d3d8ea5b6 --- /dev/null +++ b/drivers/usb/typec/ucsi/thunderbolt.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI Thunderbolt Alternate Mode Support + * + * Copyright 2026 Google LLC + */ + +#include <linux/usb/typec_tbt.h> +#include <linux/usb/pd_vdo.h> +#include <linux/err.h> +#include <linux/dev_printk.h> +#include <linux/device/devres.h> +#include <linux/gfp_types.h> +#include <linux/types.h> +#include <linux/usb/typec_altmode.h> +#include <linux/workqueue.h> + +#include "ucsi.h" + +/** + * struct ucsi_tbt - Thunderbolt Alternate Mode private data structure + * @con: Pointer to UCSI connector structure + * @alt: Pointer to typec altmode structure + * @work: Work structure + * @cam: An offset into the list of alternate modes supported by the PPM + * @header: VDO header + */ +struct ucsi_tbt { + struct ucsi_connector *con; + struct typec_altmode *alt; + struct work_struct work; + int cam; + u32 header; +}; + +static void ucsi_thunderbolt_work(struct work_struct *work) +{ + struct ucsi_tbt *tbt = container_of(work, struct ucsi_tbt, work); + + if (typec_altmode_vdm(tbt->alt, tbt->header, NULL, 0)) + dev_err(&tbt->alt->dev, "VDM 0x%x failed\n", tbt->header); + + tbt->header = 0; +} + +static int ucsi_thunderbolt_set_altmode(struct ucsi_tbt *tbt, + bool enter, u32 vdo) +{ + int svdm_version; + int cmd; + int ret; + u64 command = UCSI_SET_NEW_CAM | + UCSI_CONNECTOR_NUMBER(tbt->con->num) | + UCSI_SET_NEW_CAM_SET_AM(tbt->cam) | + ((u64)vdo << 32); + + if (enter) + command |= (1 << 23); + + ret = ucsi_send_command(tbt->con->ucsi, command, NULL, 0); + if (ret < 0) + return ret; + + svdm_version = typec_altmode_get_svdm_version(tbt->alt); + if (svdm_version < 0) + return svdm_version; + + if (enter) + cmd = CMD_ENTER_MODE; + else + cmd = CMD_EXIT_MODE; + tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd); + tbt->header |= VDO_OPOS(TYPEC_TBT_MODE); + tbt->header |= VDO_CMDT(CMDT_RSP_ACK); + + schedule_work(&tbt->work); + + return 0; +} + +static int ucsi_thunderbolt_enter(struct typec_altmode *alt, u32 *vdo) +{ + struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt); + struct ucsi_connector *con = tbt->con; + u64 command; + u8 cur = 0; + int ret; + + if (!ucsi_con_mutex_lock(con)) + return -ENOTCONN; + + command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num); + ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur)); + if (ret < 0) { + if (con->ucsi->version > 0x0100) + goto err_unlock; + cur = 0xff; + } + + if (cur != 0xff) { + if (cur >= UCSI_MAX_ALTMODES || con->port_altmode[cur] != alt) + ret = -EBUSY; + else + ret = 0; + goto err_unlock; + } + + ret = ucsi_thunderbolt_set_altmode(tbt, true, *vdo); + ucsi_altmode_update_active(tbt->con); + +err_unlock: + ucsi_con_mutex_unlock(con); + + return ret; +} + +static int ucsi_thunderbolt_exit(struct typec_altmode *alt) +{ + struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt); + int ret; + + if (!ucsi_con_mutex_lock(tbt->con)) + return -ENOTCONN; + + ret = ucsi_thunderbolt_set_altmode(tbt, false, 0); + + ucsi_con_mutex_unlock(tbt->con); + + return ret; +} + +static int ucsi_thunderbolt_vdm(struct typec_altmode *alt, + u32 header, const u32 *data, int count) +{ + struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt); + int cmd_type = PD_VDO_CMDT(header); + int cmd = PD_VDO_CMD(header); + int svdm_version; + + if (!ucsi_con_mutex_lock(tbt->con)) + return -ENOTCONN; + + svdm_version = typec_altmode_get_svdm_version(alt); + if (svdm_version < 0) { + ucsi_con_mutex_unlock(tbt->con); + return svdm_version; + } + + switch (cmd_type) { + case CMDT_INIT: + if (PD_VDO_SVDM_VER(header) < svdm_version) { + svdm_version = PD_VDO_SVDM_VER(header); + typec_partner_set_svdm_version(tbt->con->partner, svdm_version); + } + tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd); + tbt->header |= VDO_OPOS(TYPEC_TBT_MODE); + tbt->header |= VDO_CMDT(CMDT_RSP_ACK); + + schedule_work(&tbt->work); + break; + default: + break; + } + + ucsi_con_mutex_unlock(tbt->con); + + return 0; +} + +static const struct typec_altmode_ops ucsi_thunderbolt_ops = { + .enter = ucsi_thunderbolt_enter, + .exit = ucsi_thunderbolt_exit, + .vdm = ucsi_thunderbolt_vdm, +}; + +struct typec_altmode *ucsi_register_thunderbolt(struct ucsi_connector *con, + bool override, int offset, + struct typec_altmode_desc *desc) +{ + struct typec_altmode *alt; + struct ucsi_tbt *tbt; + + alt = typec_port_register_altmode(con->port, desc); + if (IS_ERR(alt) || !override) + return alt; + + tbt = devm_kzalloc(&alt->dev, sizeof(*tbt), GFP_KERNEL); + if (!tbt) { + typec_unregister_altmode(alt); + return ERR_PTR(-ENOMEM); + } + + tbt->cam = offset; + tbt->con = con; + tbt->alt = alt; + INIT_WORK(&tbt->work, ucsi_thunderbolt_work); + typec_altmode_set_drvdata(alt, tbt); + typec_altmode_set_ops(alt, &ucsi_thunderbolt_ops); + + return alt; +} + +void ucsi_thunderbolt_remove_partner(struct typec_altmode *alt) +{ + struct ucsi_tbt *tbt; + + if (alt) { + tbt = typec_altmode_get_drvdata(alt); + if (tbt) + cancel_work_sync(&tbt->work); + } +} diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c index cb62ad835761..13a38422743a 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", @@ -33,23 +33,6 @@ const char *ucsi_cmd_str(u64 raw_cmd) return ucsi_cmd_strs[(cmd >= ARRAY_SIZE(ucsi_cmd_strs)) ? 0 : cmd]; } -const char *ucsi_cci_str(u32 cci) -{ - if (UCSI_CCI_CONNECTOR(cci)) { - if (cci & UCSI_CCI_ACK_COMPLETE) - return "Event pending (ACK completed)"; - if (cci & UCSI_CCI_COMMAND_COMPLETE) - return "Event pending (command completed)"; - return "Connector Change"; - } - if (cci & UCSI_CCI_ACK_COMPLETE) - return "ACK completed"; - if (cci & UCSI_CCI_COMMAND_COMPLETE) - return "Command completed"; - - return ""; -} - static const char * const ucsi_recipient_strs[] = { [UCSI_RECIPIENT_CON] = "port", [UCSI_RECIPIENT_SOP] = "partner", diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h index 41701dee7056..9554a0207276 100644 --- a/drivers/usb/typec/ucsi/trace.h +++ b/drivers/usb/typec/ucsi/trace.h @@ -10,7 +10,6 @@ #include <linux/usb/typec_altmode.h> const char *ucsi_cmd_str(u64 raw_cmd); -const char *ucsi_cci_str(u32 cci); const char *ucsi_recipient_str(u8 recipient); DECLARE_EVENT_CLASS(ucsi_log_command, diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 2a2915b0a645..5b7ad9e99cb9 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -13,6 +13,7 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/usb/typec_dp.h> +#include <linux/usb/typec_tbt.h> #include "ucsi.h" #include "trace.h" @@ -42,8 +43,14 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) if (cci & UCSI_CCI_BUSY) return; - if (UCSI_CCI_CONNECTOR(cci)) - ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci)); + if (UCSI_CCI_CONNECTOR(cci)) { + if (!ucsi->cap.num_connectors || + UCSI_CCI_CONNECTOR(cci) <= ucsi->cap.num_connectors) + ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci)); + else + dev_err(ucsi->dev, "bogus connector number in CCI: %lu\n", + UCSI_CCI_CONNECTOR(cci)); + } if (cci & UCSI_CCI_ACK_COMPLETE && test_and_clear_bit(ACK_PENDING, &ucsi->flags)) @@ -55,7 +62,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci) } EXPORT_SYMBOL_GPL(ucsi_notify_common); -int ucsi_sync_control_common(struct ucsi *ucsi, u64 command) +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; @@ -80,6 +88,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); @@ -95,7 +110,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, @@ -108,9 +123,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; @@ -127,9 +140,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. */ @@ -231,6 +241,8 @@ static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd, if (cci & UCSI_CCI_ERROR) ret = ucsi_read_error(ucsi, connector_num); + trace_ucsi_run_command(cmd, ret); + mutex_unlock(&ucsi->ppm_lock); return ret; } @@ -289,7 +301,7 @@ static int ucsi_partner_task(struct ucsi_connector *con, if (!con->partner) return 0; - uwork = kzalloc(sizeof(*uwork), GFP_KERNEL); + uwork = kzalloc_obj(*uwork); if (!uwork) return -ENOMEM; @@ -311,6 +323,7 @@ void ucsi_altmode_update_active(struct ucsi_connector *con) { const struct typec_altmode *altmode = NULL; u64 command; + u16 svid = 0; int ret; u8 cur; int i; @@ -332,6 +345,10 @@ void ucsi_altmode_update_active(struct ucsi_connector *con) for (i = 0; con->partner_altmode[i]; i++) typec_altmode_update_active(con->partner_altmode[i], con->partner_altmode[i] == altmode); + + if (altmode) + svid = altmode->svid; + typec_altmode_state_update(con->partner, svid, 0); } static int ucsi_altmode_next_mode(struct typec_altmode **alt, u16 svid) @@ -409,6 +426,9 @@ static int ucsi_register_altmode(struct ucsi_connector *con, alt = ucsi_register_displayport(con, override, i, desc); break; + case USB_TYPEC_TBT_SID: + alt = ucsi_register_thunderbolt(con, override, i, desc); + break; default: alt = typec_port_register_altmode(con->port, desc); break; @@ -528,13 +548,12 @@ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient) * Update the original altmode table as some ppms may report * multiple DP altmodes. */ - if (recipient == UCSI_RECIPIENT_CON) - multi_dp = ucsi->ops->update_altmodes(ucsi, orig, updated); + multi_dp = ucsi->ops->update_altmodes(ucsi, recipient, orig, updated); /* now register altmodes */ for (i = 0; i < max_altmodes; i++) { memset(&desc, 0, sizeof(desc)); - if (multi_dp && recipient == UCSI_RECIPIENT_CON) { + if (multi_dp) { desc.svid = updated[i].svid; desc.vdo = updated[i].mid; } else { @@ -607,6 +626,8 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) desc.vdo = alt[j].mid; desc.svid = alt[j].svid; desc.roles = TYPEC_PORT_DRD; + desc.mode_selection = con->ucsi->ops->add_partner_altmodes && + !con->typec_cap.no_mode_control; ret = ucsi_register_altmode(con, &desc, recipient); if (ret) @@ -638,12 +659,15 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) } while (adev[i]) { - if (recipient == UCSI_RECIPIENT_SOP && - (adev[i]->svid == USB_TYPEC_DP_SID || - (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID && - adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))) { + if (recipient == UCSI_RECIPIENT_SOP) { pdev = typec_altmode_get_partner(adev[i]); - ucsi_displayport_remove_partner((void *)pdev); + + if (adev[i]->svid == USB_TYPEC_DP_SID || + (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID && + adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO)) + ucsi_displayport_remove_partner((void *)pdev); + else if (adev[i]->svid == USB_TYPEC_TBT_SID) + ucsi_thunderbolt_remove_partner((void *)pdev); } typec_unregister_altmode(adev[i]); adev[i++] = NULL; @@ -829,6 +853,8 @@ static int ucsi_check_altmodes(struct ucsi_connector *con) if (con->partner_altmode[0]) { num_partner_am = ucsi_get_num_altmode(con->partner_altmode); typec_partner_set_num_altmodes(con->partner, num_partner_am); + if (con->ucsi->ops->add_partner_altmodes) + con->ucsi->ops->add_partner_altmodes(con); ucsi_altmode_update_active(con); return 0; } else { @@ -1006,6 +1032,28 @@ static int ucsi_check_connector_capability(struct ucsi_connector *con) return ret; } +static void ucsi_orientation(struct ucsi_connector *con) +{ + if (con->ucsi->version < UCSI_VERSION_2_0) + return; + + if (!UCSI_CONSTAT(con, CONNECTED)) { + typec_set_orientation(con->port, TYPEC_ORIENTATION_NONE); + return; + } + + switch (UCSI_CONSTAT(con, ORIENTATION)) { + case UCSI_CONSTAT_ORIENTATION_NORMAL: + typec_set_orientation(con->port, TYPEC_ORIENTATION_NORMAL); + break; + case UCSI_CONSTAT_ORIENTATION_REVERSE: + typec_set_orientation(con->port, TYPEC_ORIENTATION_REVERSE); + break; + default: + break; + } +} + static void ucsi_pwr_opmode_change(struct ucsi_connector *con) { switch (UCSI_CONSTAT(con, PWR_OPMODE)) { @@ -1020,14 +1068,17 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con) case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5: con->rdo = 0; typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_1_5A); + ucsi_port_psy_changed(con); break; case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0: con->rdo = 0; typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_3_0A); + ucsi_port_psy_changed(con); break; default: con->rdo = 0; typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_USB); + ucsi_port_psy_changed(con); break; } } @@ -1092,6 +1143,8 @@ static void ucsi_unregister_partner(struct ucsi_connector *con) return; typec_set_mode(con->port, TYPEC_STATE_SAFE); + if (con->ucsi->ops->remove_partner_altmodes) + con->ucsi->ops->remove_partner_altmodes(con); typec_partner_set_usb_power_delivery(con->partner, NULL); ucsi_unregister_partner_pdos(con); @@ -1135,10 +1188,18 @@ static void ucsi_partner_change(struct ucsi_connector *con) if (UCSI_CONSTAT(con, PARTNER_FLAG_USB)) typec_set_mode(con->port, TYPEC_STATE_USB); } + + if (((con->ucsi->version >= UCSI_VERSION_3_0 && + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN4)) || + (con->ucsi->version >= UCSI_VERSION_2_0 && + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN3))) && con->partner) + typec_partner_set_usb_mode(con->partner, USB_MODE_USB4); } - /* Only notify USB controller if partner supports USB data */ - if (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB))) + if ((!UCSI_CONSTAT(con, PARTNER_FLAG_USB)) && + ((con->ucsi->quirks & UCSI_USB4_IMPLIES_USB) && + (!(UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN3) || + UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN4))))) u_role = USB_ROLE_NONE; ret = usb_role_switch_set_role(con->usb_role_sw, u_role); @@ -1215,9 +1276,11 @@ static void ucsi_handle_connector_change(struct work_struct *work) struct ucsi_connector *con = container_of(work, struct ucsi_connector, work); struct ucsi *ucsi = con->ucsi; + u8 curr_scale, volt_scale; enum typec_role role; u16 change; int ret; + u32 val; mutex_lock(&con->lock); @@ -1243,6 +1306,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) if (change & UCSI_CONSTAT_POWER_DIR_CHANGE) { typec_set_pwr_role(con->port, role); + ucsi_port_psy_changed(con); /* Complete pending power role swap */ if (!completion_done(&con->complete)) @@ -1253,6 +1317,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) typec_set_pwr_role(con->port, role); ucsi_port_psy_changed(con); ucsi_partner_change(con); + ucsi_orientation(con); if (UCSI_CONSTAT(con, CONNECTED)) { ucsi_register_partner(con); @@ -1276,6 +1341,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) if (con->partner && (change & UCSI_CONSTAT_PARTNER_CHANGE)) { ucsi_partner_change(con); + ucsi_altmode_update_active(con); /* Complete pending data role swap */ if (!completion_done(&con->complete)) @@ -1285,9 +1351,24 @@ static void ucsi_handle_connector_change(struct work_struct *work) if (change & UCSI_CONSTAT_CAM_CHANGE) ucsi_partner_task(con, ucsi_check_altmodes, 1, HZ); - if (change & UCSI_CONSTAT_BC_CHANGE) + if (change & (UCSI_CONSTAT_BC_CHANGE | UCSI_CONSTAT_SINK_PATH_CHANGE)) ucsi_port_psy_changed(con); + if (con->ucsi->version >= UCSI_VERSION_2_1 && + UCSI_CONSTAT(con, PWR_READING_READY_V2_1)) { + curr_scale = UCSI_CONSTAT(con, CURRENT_SCALE_V2_1); + volt_scale = UCSI_CONSTAT(con, VOLTAGE_SCALE_V2_1); + + val = UCSI_CONSTAT(con, PEAK_CURRENT_V2_1); + con->peak_current = UCSI_CONSTAT_CURR_SCALE_MULT * curr_scale * val; + + val = UCSI_CONSTAT(con, AVG_CURRENT_V2_1); + con->avg_current = UCSI_CONSTAT_CURR_SCALE_MULT * curr_scale * val; + + val = UCSI_CONSTAT(con, VBUS_VOLTAGE_V2_1); + con->vbus_voltage = UCSI_CONSTAT_VOLT_SCALE_MULT * volt_scale * val; + } + out_unlock: mutex_unlock(&con->lock); } @@ -1613,6 +1694,10 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) cap->driver_data = con; cap->ops = &ucsi_ops; + cap->no_mode_control = !(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE); + + if (ucsi->version >= UCSI_VERSION_2_0) + con->typec_cap.orientation_aware = true; if (ucsi->ops->update_connector) ucsi->ops->update_connector(con); @@ -1670,6 +1755,7 @@ static int ucsi_register_port(struct ucsi *ucsi, struct ucsi_connector *con) typec_set_pwr_role(con->port, UCSI_CONSTAT(con, PWR_DIR)); ucsi_register_partner(con); ucsi_pwr_opmode_change(con); + ucsi_orientation(con); ucsi_port_psy_changed(con); if (con->ucsi->cap.features & UCSI_CAP_GET_PD_MESSAGE) ucsi_get_partner_identity(con); @@ -1787,9 +1873,15 @@ static int ucsi_init(struct ucsi *ucsi) ret = -ENODEV; goto err_reset; } + /* Check if reserved bit set. This is out of spec but happens in buggy FW */ + if (ucsi->cap.num_connectors & 0x80) { + dev_warn(ucsi->dev, "UCSI: Invalid num_connectors %d. Likely buggy FW\n", + ucsi->cap.num_connectors); + ucsi->cap.num_connectors &= 0x7f; // clear bit and carry on + } /* Allocate the connectors. Released in ucsi_unregister() */ - connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL); + connector = kzalloc_objs(*connector, ucsi->cap.num_connectors + 1); if (!connector) { ret = -ENOMEM; goto err_reset; @@ -1920,6 +2012,40 @@ void ucsi_set_drvdata(struct ucsi *ucsi, void *data) EXPORT_SYMBOL_GPL(ucsi_set_drvdata); /** + * ucsi_con_mutex_lock - Acquire the connector mutex + * @con: The connector interface to lock + * + * Returns true on success, false if the connector is disconnected + */ +bool ucsi_con_mutex_lock(struct ucsi_connector *con) +{ + bool mutex_locked = false; + bool connected = true; + + while (connected && !mutex_locked) { + mutex_locked = mutex_trylock(&con->lock) != 0; + connected = UCSI_CONSTAT(con, CONNECTED); + if (connected && !mutex_locked) + msleep(20); + } + + connected = connected && con->partner; + if (!connected && mutex_locked) + mutex_unlock(&con->lock); + + return connected; +} + +/** + * ucsi_con_mutex_unlock - Release the connector mutex + * @con: The connector interface to unlock + */ +void ucsi_con_mutex_unlock(struct ucsi_connector *con) +{ + mutex_unlock(&con->lock); +} + +/** * ucsi_create - Allocate UCSI instance * @dev: Device interface to the PPM (Platform Policy Manager) * @ops: I/O routines @@ -1933,7 +2059,7 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops) !ops->read_message_in || !ops->sync_control || !ops->async_control) return ERR_PTR(-EINVAL); - ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL); + ucsi = kzalloc_obj(*ucsi); if (!ucsi) return ERR_PTR(-ENOMEM); diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 28780acc4af2..cff9ddc2ae21 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -50,6 +50,7 @@ struct dentry; /* Command Status and Connector Change Indication (CCI) bits */ #define UCSI_CCI_CONNECTOR(_c_) (((_c_) & GENMASK(7, 1)) >> 1) #define UCSI_CCI_LENGTH(_c_) (((_c_) & GENMASK(15, 8)) >> 8) +#define UCSI_SET_CCI_LENGTH(_c_) ((_c_) << 8) #define UCSI_CCI_NOT_SUPPORTED BIT(25) #define UCSI_CCI_CANCEL_COMPLETE BIT(26) #define UCSI_CCI_RESET_COMPLETE BIT(27) @@ -69,6 +70,8 @@ struct dentry; * @update_altmodes: Squashes duplicate DP altmodes * @update_connector: Update connector capabilities before registering * @connector_status: Updates connector status, called holding connector lock + * @add_partner_altmodes: Start mode selection + * @remove_partner_altmodes: Clean mode selection * * Read and write routines for UCSI interface. @sync_write must wait for the * Command Completion Event from the PPM before returning, and @async_write must @@ -79,12 +82,16 @@ struct ucsi_operations { int (*read_cci)(struct ucsi *ucsi, u32 *cci); int (*poll_cci)(struct ucsi *ucsi, u32 *cci); int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len); - int (*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, + bool (*update_altmodes)(struct ucsi *ucsi, u8 recipient, + struct ucsi_altmode *orig, struct ucsi_altmode *updated); void (*update_connector)(struct ucsi_connector *con); void (*connector_status)(struct ucsi_connector *con); + void (*add_partner_altmodes)(struct ucsi_connector *con); + void (*remove_partner_altmodes)(struct ucsi_connector *con); }; struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops); @@ -93,6 +100,8 @@ int ucsi_register(struct ucsi *ucsi); void ucsi_unregister(struct ucsi *ucsi); void *ucsi_get_drvdata(struct ucsi *ucsi); void ucsi_set_drvdata(struct ucsi *ucsi, void *data); +bool ucsi_con_mutex_lock(struct ucsi_connector *con); +void ucsi_con_mutex_unlock(struct ucsi_connector *con); void ucsi_connector_change(struct ucsi *ucsi, u8 num); @@ -108,7 +117,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_GET_CAPABILITY_SIZE 128 #define UCSI_GET_CONNECTOR_CAPABILITY 0x07 #define UCSI_GET_CONNECTOR_CAPABILITY_SIZE 32 -#define UCSI_SET_UOM 0x08 +#define UCSI_SET_CCOM 0x08 #define UCSI_SET_UOR 0x09 #define UCSI_SET_PDM 0x0a #define UCSI_SET_PDR 0x0b @@ -122,8 +131,14 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num); #define UCSI_GET_CONNECTOR_STATUS 0x12 #define UCSI_GET_CONNECTOR_STATUS_SIZE 152 #define UCSI_GET_ERROR_STATUS 0x13 +#define UCSI_SET_POWER_LEVEL 0x14 +#define UCSI_GET_ATTENTION_VDO 0x16 #define UCSI_GET_PD_MESSAGE 0x15 +#define UCSI_GET_CAM_CS 0x18 #define UCSI_SET_SINK_PATH 0x1c +#define UCSI_READ_POWER_LEVEL 0x1e +#define UCSI_SET_USB 0x21 +#define UCSI_GET_LPM_PPM_INFO 0x22 #define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16) #define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff) @@ -350,6 +365,20 @@ struct ucsi_cable_property { #define UCSI_CONSTAT_BC_SLOW_CHARGING 2 #define UCSI_CONSTAT_BC_TRICKLE_CHARGING 3 #define UCSI_CONSTAT_PD_VERSION_V1_2 UCSI_DECLARE_BITFIELD_V1_2(70, 16) +#define UCSI_CONSTAT_ORIENTATION UCSI_DECLARE_BITFIELD_V2_0(86, 1) +#define UCSI_CONSTAT_ORIENTATION_NORMAL 0 +#define UCSI_CONSTAT_ORIENTATION_REVERSE 1 +#define UCSI_CONSTAT_SINK_PATH_STATUS_V2_0 UCSI_DECLARE_BITFIELD_V2_0(87, 1) +#define UCSI_CONSTAT_SINK_PATH_DISABLED 0 +#define UCSI_CONSTAT_SINK_PATH_ENABLED 1 +#define UCSI_CONSTAT_PWR_READING_READY_V2_1 UCSI_DECLARE_BITFIELD_V2_1(89, 1) +#define UCSI_CONSTAT_CURRENT_SCALE_V2_1 UCSI_DECLARE_BITFIELD_V2_1(90, 3) +#define UCSI_CONSTAT_PEAK_CURRENT_V2_1 UCSI_DECLARE_BITFIELD_V2_1(93, 16) +#define UCSI_CONSTAT_AVG_CURRENT_V2_1 UCSI_DECLARE_BITFIELD_V2_1(109, 16) +#define UCSI_CONSTAT_VOLTAGE_SCALE_V2_1 UCSI_DECLARE_BITFIELD_V2_1(125, 4) +#define UCSI_CONSTAT_VBUS_VOLTAGE_V2_1 UCSI_DECLARE_BITFIELD_V2_1(129, 16) +#define UCSI_CONSTAT_CURR_SCALE_MULT 5 +#define UCSI_CONSTAT_VOLT_SCALE_MULT 5 /* Connector Status Change Bits. */ #define UCSI_CONSTAT_EXT_SUPPLY_CHANGE BIT(1) @@ -361,6 +390,7 @@ struct ucsi_cable_property { #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_SINK_PATH_CHANGE BIT(13) #define UCSI_CONSTAT_CONNECT_CHANGE BIT(14) #define UCSI_CONSTAT_ERROR BIT(15) @@ -429,7 +459,7 @@ struct ucsi_debugfs_entry { u64 low; u64 high; } response; - u32 status; + int status; struct dentry *dentry; }; @@ -467,6 +497,9 @@ struct ucsi { unsigned long quirks; #define UCSI_NO_PARTNER_PDOS BIT(0) /* Don't read partner's PDOs */ #define UCSI_DELAY_DEVICE_PDOS BIT(1) /* Reading PDOs fails until the parter is in PD mode */ + +/* USB4 connection can imply that USB communcation is supported */ +#define UCSI_USB4_IMPLIES_USB BIT(2) }; #define UCSI_MAX_DATA_LENGTH(u) (((u)->version < UCSI_VERSION_2_0) ? 0x10 : 0xff) @@ -474,9 +507,10 @@ struct ucsi { #define UCSI_MAX_SVID 5 #define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6) -#define UCSI_TYPEC_VSAFE5V 5000 -#define UCSI_TYPEC_1_5_CURRENT 1500 -#define UCSI_TYPEC_3_0_CURRENT 3000 +#define UCSI_TYPEC_VSAFE5V 5000 +#define UCSI_TYPEC_DEFAULT_CURRENT 100 +#define UCSI_TYPEC_1_5_CURRENT 1500 +#define UCSI_TYPEC_3_0_CURRENT 3000 struct ucsi_connector { int num; @@ -509,6 +543,10 @@ struct ucsi_connector { u32 src_pdos[PDO_MAX_OBJECTS]; int num_pdos; + u32 peak_current; + u32 avg_current; + u32 vbus_voltage; + /* USB PD objects */ struct usb_power_delivery *pd; struct usb_power_delivery_capabilities *port_source_caps; @@ -531,7 +569,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); @@ -564,6 +603,26 @@ static inline void ucsi_displayport_remove_partner(struct typec_altmode *adev) { } #endif /* CONFIG_TYPEC_DP_ALTMODE */ +#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE) +struct typec_altmode * +ucsi_register_thunderbolt(struct ucsi_connector *con, + bool override, int offset, + struct typec_altmode_desc *desc); + +void ucsi_thunderbolt_remove_partner(struct typec_altmode *adev); +#else +static inline struct typec_altmode * +ucsi_register_thunderbolt(struct ucsi_connector *con, + bool override, int offset, + struct typec_altmode_desc *desc) +{ + return typec_port_register_altmode(con->port, desc); +} + +static inline void +ucsi_thunderbolt_remove_partner(struct typec_altmode *adev) { } +#endif /* CONFIG_TYPEC_TBT_ALTMODE */ + #ifdef CONFIG_DEBUG_FS void ucsi_debugfs_init(void); void ucsi_debugfs_exit(void); diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index ac1ebb5d9527..6b92f296e985 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -105,17 +105,23 @@ static const struct ucsi_operations ucsi_acpi_ops = { .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); 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) { /* Clear the bogus change */ @@ -128,28 +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, .poll_cci = ucsi_acpi_poll_cci, - .read_message_in = ucsi_gram_read_message_in, + .read_message_in = ucsi_acpi_read_message_in, .sync_control = ucsi_gram_sync_control, .async_control = ucsi_acpi_async_control }; diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 4b1668733a4b..199799b319c2 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/delay.h> #include <linux/firmware.h> +#include <linux/hex.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pci.h> @@ -222,7 +223,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]; @@ -395,6 +395,7 @@ static void ucsi_ccg_update_get_current_cam_cmd(struct ucsi_ccg *uc, u8 *data) } static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi, + u8 recipient, struct ucsi_altmode *orig, struct ucsi_altmode *updated) { @@ -403,6 +404,9 @@ static bool ucsi_ccg_update_altmodes(struct ucsi *ucsi, int i, j, k = 0; bool found = false; + if (recipient != UCSI_RECIPIENT_CON) + return false; + alt = uc->orig; new_alt = uc->updated; memset(uc->updated, 0, sizeof(uc->updated)); @@ -538,9 +542,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 +583,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 +607,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,11 +618,9 @@ 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; @@ -652,7 +630,31 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command) 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); @@ -1391,22 +1393,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) { @@ -1433,11 +1448,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) { @@ -1474,6 +1488,8 @@ static int ucsi_ccg_probe(struct i2c_client *client) i2c_set_clientdata(client, uc); + device_disable_async_suspend(uc->dev); + pm_runtime_set_active(uc->dev); pm_runtime_enable(uc->dev); pm_runtime_use_autosuspend(uc->dev); diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index 8af79101a2fc..12e07b9fe622 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -16,10 +16,10 @@ #define PMIC_GLINK_MAX_PORTS 3 -#define UCSI_BUF_SIZE 48 +#define UCSI_BUF_V1_SIZE (UCSI_MESSAGE_OUT + (UCSI_MESSAGE_OUT - UCSI_MESSAGE_IN)) +#define UCSI_BUF_V2_SIZE (UCSIv2_MESSAGE_OUT + (UCSIv2_MESSAGE_OUT - UCSI_MESSAGE_IN)) #define MSG_TYPE_REQ_RESP 1 -#define UCSI_BUF_SIZE 48 #define UC_NOTIFY_RECEIVER_UCSI 0x0 #define UC_UCSI_READ_BUF_REQ 0x11 @@ -30,24 +30,30 @@ struct ucsi_read_buf_req_msg { struct pmic_glink_hdr hdr; }; -struct ucsi_read_buf_resp_msg { +struct __packed ucsi_read_buf_resp_msg { struct pmic_glink_hdr hdr; - u8 buf[UCSI_BUF_SIZE]; + union { + u8 v2_buf[UCSI_BUF_V2_SIZE]; + u8 v1_buf[UCSI_BUF_V1_SIZE]; + } buf; u32 ret_code; }; -struct ucsi_write_buf_req_msg { +struct __packed ucsi_write_buf_req_msg { struct pmic_glink_hdr hdr; - u8 buf[UCSI_BUF_SIZE]; + union { + u8 v2_buf[UCSI_BUF_V2_SIZE]; + u8 v1_buf[UCSI_BUF_V1_SIZE]; + } buf; u32 reserved; }; -struct ucsi_write_buf_resp_msg { +struct __packed ucsi_write_buf_resp_msg { struct pmic_glink_hdr hdr; u32 ret_code; }; -struct ucsi_notify_ind_msg { +struct __packed ucsi_notify_ind_msg { struct pmic_glink_hdr hdr; u32 notification; u32 receiver; @@ -72,7 +78,7 @@ struct pmic_glink_ucsi { bool ucsi_registered; bool pd_running; - u8 read_buf[UCSI_BUF_SIZE]; + u8 read_buf[UCSI_BUF_V2_SIZE]; }; static int pmic_glink_ucsi_read(struct ucsi *__ucsi, unsigned int offset, @@ -132,17 +138,35 @@ static int pmic_glink_ucsi_locked_write(struct pmic_glink_ucsi *ucsi, unsigned i const void *val, size_t val_len) { struct ucsi_write_buf_req_msg req = {}; + size_t req_len, buf_len; unsigned long left; int ret; + u8 *buf; req.hdr.owner = PMIC_GLINK_OWNER_USBC; req.hdr.type = MSG_TYPE_REQ_RESP; req.hdr.opcode = UC_UCSI_WRITE_BUF_REQ; - memcpy(&req.buf[offset], val, val_len); + + if (ucsi->ucsi->version >= UCSI_VERSION_2_0) { + buf_len = UCSI_BUF_V2_SIZE; + buf = req.buf.v2_buf; + } else if (ucsi->ucsi->version) { + buf_len = UCSI_BUF_V1_SIZE; + buf = req.buf.v1_buf; + } else { + dev_err(ucsi->dev, "UCSI version unknown\n"); + return -EINVAL; + } + req_len = sizeof(struct pmic_glink_hdr) + buf_len + sizeof(u32); + + if (offset + val_len > buf_len) + return -EINVAL; + + memcpy(&buf[offset], val, val_len); reinit_completion(&ucsi->write_ack); - ret = pmic_glink_send(ucsi->client, &req, sizeof(req)); + ret = pmic_glink_send(ucsi->client, &req, req_len); if (ret < 0) { dev_err(ucsi->dev, "failed to send UCSI write request: %d\n", ret); return ret; @@ -216,12 +240,48 @@ static const struct ucsi_operations pmic_glink_ucsi_ops = { static void pmic_glink_ucsi_read_ack(struct pmic_glink_ucsi *ucsi, const void *data, int len) { - const struct ucsi_read_buf_resp_msg *resp = data; + u32 ret_code, resp_len, buf_len = 0; + u8 *buf; + + if (ucsi->ucsi->version) { + if (ucsi->ucsi->version >= UCSI_VERSION_2_0) { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v2_buf; + buf_len = UCSI_BUF_V2_SIZE; + } else { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v1_buf; + buf_len = UCSI_BUF_V1_SIZE; + } + } else if (!ucsi->ucsi_registered) { + /* + * If UCSI version is not known yet because device is not registered, choose buffer + * size which best fits incoming data + */ + if (len > sizeof(struct pmic_glink_hdr) + UCSI_BUF_V2_SIZE) { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v2_buf; + buf_len = UCSI_BUF_V2_SIZE; + } else { + buf = ((struct ucsi_read_buf_resp_msg *)data)->buf.v1_buf; + buf_len = UCSI_BUF_V1_SIZE; + } + } else { + dev_err(ucsi->dev, "Device has been registered but UCSI version is still unknown\n"); + return; + } - if (resp->ret_code) + resp_len = sizeof(struct pmic_glink_hdr) + buf_len + sizeof(u32); + + if (len > resp_len) + return; + + /* Ensure that buffer_len leaves space for ret_code to be read back from memory */ + if (buf_len > len - sizeof(struct pmic_glink_hdr) - sizeof(u32)) + buf_len = len - sizeof(struct pmic_glink_hdr) - sizeof(u32); + + memcpy(&ret_code, buf + buf_len, sizeof(u32)); + if (ret_code) return; - memcpy(ucsi->read_buf, resp->buf, UCSI_BUF_SIZE); + memcpy(ucsi->read_buf, buf, buf_len); complete(&ucsi->read_ack); } @@ -311,14 +371,18 @@ static void pmic_glink_ucsi_destroy(void *data) static unsigned long quirk_sc8180x = UCSI_NO_PARTNER_PDOS; static unsigned long quirk_sc8280xp = UCSI_NO_PARTNER_PDOS | UCSI_DELAY_DEVICE_PDOS; static unsigned long quirk_sm8450 = UCSI_DELAY_DEVICE_PDOS; +static unsigned long quirk_x1e80100 = UCSI_DELAY_DEVICE_PDOS | UCSI_USB4_IMPLIES_USB; static const struct of_device_id pmic_glink_ucsi_of_quirks[] = { + { .compatible = "qcom,glymur-pmic-glink", .data = &quirk_sm8450, }, + { .compatible = "qcom,kaanapali-pmic-glink", .data = &quirk_sm8450, }, { .compatible = "qcom,qcm6490-pmic-glink", .data = &quirk_sc8280xp, }, { .compatible = "qcom,sc8180x-pmic-glink", .data = &quirk_sc8180x, }, { .compatible = "qcom,sc8280xp-pmic-glink", .data = &quirk_sc8280xp, }, { .compatible = "qcom,sm8350-pmic-glink", .data = &quirk_sc8180x, }, { .compatible = "qcom,sm8450-pmic-glink", .data = &quirk_sm8450, }, { .compatible = "qcom,sm8550-pmic-glink", .data = &quirk_sm8450, }, + { .compatible = "qcom,x1e80100-pmic-glink", .data = &quirk_x1e80100, }, {} }; diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c new file mode 100644 index 000000000000..ca749fde49bd --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ucsi-huawei-gaokun - A UCSI driver for HUAWEI Matebook E Go + * + * Copyright (C) 2024-2025 Pengyu Luo <mitltlatltl@gmail.com> + */ + +#include <drm/bridge/aux-bridge.h> +#include <linux/auxiliary_bus.h> +#include <linux/bitops.h> +#include <linux/completion.h> +#include <linux/container_of.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/of.h> +#include <linux/platform_data/huawei-gaokun-ec.h> +#include <linux/string.h> +#include <linux/usb/pd_vdo.h> +#include <linux/usb/typec_altmode.h> +#include <linux/usb/typec_dp.h> +#include <linux/workqueue_types.h> + +#include "ucsi.h" + +#define EC_EVENT_UCSI 0x21 +#define EC_EVENT_USB 0x22 + +#define GAOKUN_CCX_MASK GENMASK(1, 0) +#define GAOKUN_MUX_MASK GENMASK(3, 2) + +#define GAOKUN_DPAM_MASK GENMASK(3, 0) +#define GAOKUN_HPD_STATE_MASK BIT(4) +#define GAOKUN_HPD_IRQ_MASK BIT(5) + +#define GET_IDX(updt) (ffs(updt) - 1) + +#define CCX_TO_ORI(ccx) (++(ccx) % 3) /* convert ccx to enum typec_orientation */ + +/* Configuration Channel Extension */ +enum gaokun_ucsi_ccx { + USBC_CCX_NORMAL, + USBC_CCX_REVERSE, + USBC_CCX_NONE, +}; + +enum gaokun_ucsi_mux { + USBC_MUX_NONE, + USBC_MUX_USB_2L, + USBC_MUX_DP_4L, + USBC_MUX_USB_DP, +}; + +/* based on pmic_glink_altmode_pin_assignment */ +enum gaokun_ucsi_dpam_pan { /* DP Alt Mode Pin Assignments */ + USBC_DPAM_PAN_NONE, + USBC_DPAM_PAN_A, /* Not supported after USB Type-C Standard v1.0b */ + USBC_DPAM_PAN_B, /* Not supported after USB Type-C Standard v1.0b */ + USBC_DPAM_PAN_C, /* USBC_DPAM_PAN_C_REVERSE - 6 */ + USBC_DPAM_PAN_D, + USBC_DPAM_PAN_E, + USBC_DPAM_PAN_F, /* Not supported after USB Type-C Standard v1.0b */ + USBC_DPAM_PAN_A_REVERSE,/* Not supported after USB Type-C Standard v1.0b */ + USBC_DPAM_PAN_B_REVERSE,/* Not supported after USB Type-C Standard v1.0b */ + USBC_DPAM_PAN_C_REVERSE, + USBC_DPAM_PAN_D_REVERSE, + USBC_DPAM_PAN_E_REVERSE, + USBC_DPAM_PAN_F_REVERSE,/* Not supported after USB Type-C Standard v1.0b */ +}; + +struct gaokun_ucsi_reg { + u8 num_ports; + u8 port_updt; + u8 port_data[4]; + u8 checksum; + u8 reserved; +} __packed; + +struct gaokun_ucsi_port { + struct completion usb_ack; + spinlock_t lock; /* serializing port resource access */ + + struct gaokun_ucsi *ucsi; + struct auxiliary_device *bridge; + + int idx; + enum gaokun_ucsi_ccx ccx; + enum gaokun_ucsi_mux mux; + u8 mode; + u16 svid; + u8 hpd_state; + u8 hpd_irq; +}; + +struct gaokun_ucsi { + struct gaokun_ec *ec; + struct ucsi *ucsi; + struct gaokun_ucsi_port *ports; + struct device *dev; + struct delayed_work work; + struct notifier_block nb; + u16 version; + u8 num_ports; +}; + +/* -------------------------------------------------------------------------- */ +/* For UCSI */ + +static int gaokun_ucsi_read_version(struct ucsi *ucsi, u16 *version) +{ + struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi); + + *version = uec->version; + + return 0; +} + +static int gaokun_ucsi_read_cci(struct ucsi *ucsi, u32 *cci) +{ + struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi); + u8 buf[GAOKUN_UCSI_READ_SIZE]; + int ret; + + ret = gaokun_ec_ucsi_read(uec->ec, buf); + if (ret) + return ret; + + memcpy(cci, buf, sizeof(*cci)); + + return 0; +} + +static int gaokun_ucsi_read_message_in(struct ucsi *ucsi, + void *val, size_t val_len) +{ + struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi); + u8 buf[GAOKUN_UCSI_READ_SIZE]; + int ret; + + ret = gaokun_ec_ucsi_read(uec->ec, buf); + if (ret) + return ret; + + memcpy(val, buf + GAOKUN_UCSI_CCI_SIZE, + min(val_len, GAOKUN_UCSI_MSGI_SIZE)); + + return 0; +} + +static int gaokun_ucsi_async_control(struct ucsi *ucsi, u64 command) +{ + struct gaokun_ucsi *uec = ucsi_get_drvdata(ucsi); + u8 buf[GAOKUN_UCSI_WRITE_SIZE] = {}; + + memcpy(buf, &command, sizeof(command)); + + return gaokun_ec_ucsi_write(uec->ec, buf); +} + +static void gaokun_ucsi_update_connector(struct ucsi_connector *con) +{ + struct gaokun_ucsi *uec = ucsi_get_drvdata(con->ucsi); + + if (con->num > uec->num_ports) + return; + + con->typec_cap.orientation_aware = true; +} + +static void gaokun_set_orientation(struct ucsi_connector *con, + struct gaokun_ucsi_port *port) +{ + enum gaokun_ucsi_ccx ccx; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + ccx = port->ccx; + spin_unlock_irqrestore(&port->lock, flags); + + typec_set_orientation(con->port, CCX_TO_ORI(ccx)); +} + +static void gaokun_ucsi_connector_status(struct ucsi_connector *con) +{ + struct gaokun_ucsi *uec = ucsi_get_drvdata(con->ucsi); + int idx; + + idx = con->num - 1; + if (con->num > uec->num_ports) { + dev_warn(uec->dev, "set orientation out of range: con%d\n", idx); + return; + } + + gaokun_set_orientation(con, &uec->ports[idx]); +} + +static const struct ucsi_operations gaokun_ucsi_ops = { + .read_version = gaokun_ucsi_read_version, + .read_cci = gaokun_ucsi_read_cci, + .poll_cci = gaokun_ucsi_read_cci, + .read_message_in = gaokun_ucsi_read_message_in, + .sync_control = ucsi_sync_control_common, + .async_control = gaokun_ucsi_async_control, + .update_connector = gaokun_ucsi_update_connector, + .connector_status = gaokun_ucsi_connector_status, +}; + +/* -------------------------------------------------------------------------- */ +/* For Altmode */ + +static void gaokun_ucsi_port_update(struct gaokun_ucsi_port *port, + const u8 *port_data) +{ + struct gaokun_ucsi *uec = port->ucsi; + int offset = port->idx * 2; /* every port has 2 Bytes data */ + unsigned long flags; + u8 dcc, ddi; + + dcc = port_data[offset]; + ddi = port_data[offset + 1]; + + spin_lock_irqsave(&port->lock, flags); + + port->ccx = FIELD_GET(GAOKUN_CCX_MASK, dcc); + port->mux = FIELD_GET(GAOKUN_MUX_MASK, dcc); + port->mode = FIELD_GET(GAOKUN_DPAM_MASK, ddi); + port->hpd_state = FIELD_GET(GAOKUN_HPD_STATE_MASK, ddi); + port->hpd_irq = FIELD_GET(GAOKUN_HPD_IRQ_MASK, ddi); + + /* Mode and SVID are unused; keeping them to make things clearer */ + switch (port->mode) { + case USBC_DPAM_PAN_C: + case USBC_DPAM_PAN_C_REVERSE: + port->mode = DP_PIN_ASSIGN_C; /* correct it for usb later */ + break; + case USBC_DPAM_PAN_D: + case USBC_DPAM_PAN_D_REVERSE: + port->mode = DP_PIN_ASSIGN_D; + break; + case USBC_DPAM_PAN_E: + case USBC_DPAM_PAN_E_REVERSE: + port->mode = DP_PIN_ASSIGN_E; + break; + case USBC_DPAM_PAN_NONE: + port->mode = TYPEC_STATE_SAFE; + break; + default: + dev_warn(uec->dev, "unknown mode %d\n", port->mode); + break; + } + + switch (port->mux) { + case USBC_MUX_NONE: + port->svid = 0; + break; + case USBC_MUX_USB_2L: + port->svid = USB_SID_PD; + port->mode = TYPEC_STATE_USB; /* same as PAN_C, correct it */ + break; + case USBC_MUX_DP_4L: + case USBC_MUX_USB_DP: + port->svid = USB_SID_DISPLAYPORT; + break; + default: + dev_warn(uec->dev, "unknown mux state %d\n", port->mux); + break; + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +static int gaokun_ucsi_refresh(struct gaokun_ucsi *uec) +{ + struct gaokun_ucsi_reg ureg; + int ret, idx; + + ret = gaokun_ec_ucsi_get_reg(uec->ec, &ureg); + if (ret) + return GAOKUN_UCSI_NO_PORT_UPDATE; + + uec->num_ports = ureg.num_ports; + idx = GET_IDX(ureg.port_updt); + + if (idx < 0 || idx >= ureg.num_ports) + return GAOKUN_UCSI_NO_PORT_UPDATE; + + gaokun_ucsi_port_update(&uec->ports[idx], ureg.port_data); + return idx; +} + +static void gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port *port) +{ + struct gaokun_ucsi *uec = port->ucsi; + int idx = port->idx; + + if (idx >= uec->ucsi->cap.num_connectors) { + dev_warn(uec->dev, "altmode port out of range: %d\n", idx); + return; + } + + /* UCSI callback .connector_status() have set orientation */ + if (port->bridge) + drm_aux_hpd_bridge_notify(&port->bridge->dev, + port->hpd_state ? + connector_status_connected : + connector_status_disconnected); + + gaokun_ec_ucsi_pan_ack(uec->ec, port->idx); +} + +static void gaokun_ucsi_altmode_notify_ind(struct gaokun_ucsi *uec) +{ + int idx; + + if (!uec->ucsi->connector) { /* slow to register */ + dev_err_ratelimited(uec->dev, "ucsi connector is not initialized yet\n"); + return; + } + + idx = gaokun_ucsi_refresh(uec); + if (idx == GAOKUN_UCSI_NO_PORT_UPDATE) + gaokun_ec_ucsi_pan_ack(uec->ec, idx); /* ack directly if no update */ + else + gaokun_ucsi_handle_altmode(&uec->ports[idx]); +} + +/* + * When inserting, 2 UCSI events(connector change) are followed by USB events. + * If we received one USB event, that means USB events are not blocked, so we + * can complelte for all ports, and we should signal all events. + */ +static void gaokun_ucsi_complete_usb_ack(struct gaokun_ucsi *uec) +{ + struct gaokun_ucsi_port *port; + int idx = 0; + + while (idx < uec->num_ports) { + port = &uec->ports[idx++]; + if (!completion_done(&port->usb_ack)) + complete_all(&port->usb_ack); + } +} + +/* + * USB event is necessary for enabling altmode, the event should follow + * UCSI event, if not after timeout(this notify may be disabled somehow), + * then force to enable altmode. + */ +static void gaokun_ucsi_handle_no_usb_event(struct gaokun_ucsi *uec, int idx) +{ + struct gaokun_ucsi_port *port; + + port = &uec->ports[idx]; + if (!wait_for_completion_timeout(&port->usb_ack, 2 * HZ)) { + dev_warn(uec->dev, "No USB EVENT, triggered by UCSI EVENT"); + gaokun_ucsi_altmode_notify_ind(uec); + } +} + +static int gaokun_ucsi_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + u32 cci; + int ret; + struct gaokun_ucsi *uec = container_of(nb, struct gaokun_ucsi, nb); + + switch (action) { + case EC_EVENT_USB: + gaokun_ucsi_complete_usb_ack(uec); + gaokun_ucsi_altmode_notify_ind(uec); + return NOTIFY_OK; + + case EC_EVENT_UCSI: + ret = gaokun_ucsi_read_cci(uec->ucsi, &cci); + if (ret) + return NOTIFY_DONE; + + ucsi_notify_common(uec->ucsi, cci); + if (UCSI_CCI_CONNECTOR(cci)) + gaokun_ucsi_handle_no_usb_event(uec, UCSI_CCI_CONNECTOR(cci) - 1); + + return NOTIFY_OK; + + default: + return NOTIFY_DONE; + } +} + +static int gaokun_ucsi_ports_init(struct gaokun_ucsi *uec) +{ + struct gaokun_ucsi_port *ucsi_port; + struct gaokun_ucsi_reg ureg = {}; + struct device *dev = uec->dev; + struct fwnode_handle *fwnode; + int i, ret, num_ports; + u32 port; + + gaokun_ec_ucsi_get_reg(uec->ec, &ureg); + num_ports = ureg.num_ports; + uec->ports = devm_kcalloc(dev, num_ports, sizeof(*uec->ports), + GFP_KERNEL); + if (!uec->ports) + return -ENOMEM; + + for (i = 0; i < num_ports; ++i) { + ucsi_port = &uec->ports[i]; + ucsi_port->ccx = USBC_CCX_NONE; + ucsi_port->idx = i; + ucsi_port->ucsi = uec; + init_completion(&ucsi_port->usb_ack); + spin_lock_init(&ucsi_port->lock); + } + + device_for_each_child_node(dev, fwnode) { + 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; + } + + if (port >= num_ports) { + dev_warn(dev, "invalid connector number %d, ignoring\n", port); + continue; + } + + ucsi_port = &uec->ports[port]; + ucsi_port->bridge = devm_drm_dp_hpd_bridge_alloc(dev, to_of_node(fwnode)); + if (IS_ERR(ucsi_port->bridge)) { + fwnode_handle_put(fwnode); + return PTR_ERR(ucsi_port->bridge); + } + } + + for (i = 0; i < num_ports; i++) { + if (!uec->ports[i].bridge) + continue; + + ret = devm_drm_dp_hpd_bridge_add(dev, uec->ports[i].bridge); + if (ret) + return ret; + } + + return 0; +} + +static void gaokun_ucsi_register_worker(struct work_struct *work) +{ + struct gaokun_ucsi *uec; + struct ucsi *ucsi; + int ret; + + uec = container_of(work, struct gaokun_ucsi, work.work); + ucsi = uec->ucsi; + + ret = gaokun_ec_register_notify(uec->ec, &uec->nb); + if (ret) { + dev_err_probe(ucsi->dev, ret, "notifier register failed\n"); + return; + } + + ret = ucsi_register(ucsi); + if (ret) + dev_err_probe(ucsi->dev, ret, "ucsi register failed\n"); +} + +static int gaokun_ucsi_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct gaokun_ec *ec = adev->dev.platform_data; + struct device *dev = &adev->dev; + struct gaokun_ucsi *uec; + int ret; + + uec = devm_kzalloc(dev, sizeof(*uec), GFP_KERNEL); + if (!uec) + return -ENOMEM; + + uec->ec = ec; + uec->dev = dev; + uec->version = UCSI_VERSION_1_0; + uec->nb.notifier_call = gaokun_ucsi_notify; + + INIT_DELAYED_WORK(&uec->work, gaokun_ucsi_register_worker); + + ret = gaokun_ucsi_ports_init(uec); + if (ret) + return ret; + + uec->ucsi = ucsi_create(dev, &gaokun_ucsi_ops); + if (IS_ERR(uec->ucsi)) + return PTR_ERR(uec->ucsi); + + ucsi_set_drvdata(uec->ucsi, uec); + auxiliary_set_drvdata(adev, uec); + + /* EC can't handle UCSI properly in the early stage */ + schedule_delayed_work(&uec->work, 3 * HZ); + + return 0; +} + +static void gaokun_ucsi_remove(struct auxiliary_device *adev) +{ + struct gaokun_ucsi *uec = auxiliary_get_drvdata(adev); + + disable_delayed_work_sync(&uec->work); + gaokun_ec_unregister_notify(uec->ec, &uec->nb); + ucsi_unregister(uec->ucsi); + ucsi_destroy(uec->ucsi); +} + +static const struct auxiliary_device_id gaokun_ucsi_id_table[] = { + { .name = GAOKUN_MOD_NAME "." GAOKUN_DEV_UCSI, }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, gaokun_ucsi_id_table); + +static struct auxiliary_driver gaokun_ucsi_driver = { + .name = GAOKUN_DEV_UCSI, + .id_table = gaokun_ucsi_id_table, + .probe = gaokun_ucsi_probe, + .remove = gaokun_ucsi_remove, +}; + +module_auxiliary_driver(gaokun_ucsi_driver); + +MODULE_DESCRIPTION("HUAWEI Matebook E Go UCSI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c index 57ef7d83a412..838ac0185082 100644 --- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c +++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c @@ -10,6 +10,7 @@ #include <linux/firmware.h> #include <linux/i2c.h> #include <linux/interrupt.h> +#include <linux/minmax.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/unaligned.h> @@ -523,11 +524,7 @@ static void ucsi_stm32g0_fw_cb(const struct firmware *fw, void *context) data = fw->data; end = fw->data + fw->size; while (data < end) { - if ((end - data) < STM32G0_I2C_BL_SZ) - size = end - data; - else - size = STM32G0_I2C_BL_SZ; - + size = min(end - data, STM32G0_I2C_BL_SZ); ret = ucsi_stm32g0_bl_write(g0->ucsi, addr, data, size); if (ret) { dev_err(g0->dev, "Write failed %d\n", ret); diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c index d33e3f2dd1d8..0187c1c4b21a 100644 --- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c +++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c @@ -7,18 +7,33 @@ */ #include <linux/auxiliary_bus.h> #include <linux/bitops.h> +#include <linux/bitfield.h> #include <linux/completion.h> #include <linux/container_of.h> #include <linux/module.h> #include <linux/notifier.h> +#include <linux/of.h> +#include <linux/property.h> #include <linux/string.h> #include <linux/platform_data/lenovo-yoga-c630.h> +#include <linux/usb/typec_dp.h> + +#include <drm/bridge/aux-bridge.h> #include "ucsi.h" +#define LENOVO_EC_USB_MUX 0x08 + +#define USB_MUX_MUXC GENMASK(1, 0) +#define USB_MUX_CCST GENMASK(3, 2) +#define USB_MUX_DPPN GENMASK(7, 4) +#define USB_MUX_HPDS BIT(8) +#define USB_MUX_HSFL GENMASK(11, 9) + struct yoga_c630_ucsi { struct yoga_c630_ec *ec; struct ucsi *ucsi; + struct auxiliary_device *bridge; struct notifier_block nb; u16 version; }; @@ -71,15 +86,127 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command) return yoga_c630_ec_ucsi_write(uec->ec, (u8*)&command); } +static int yoga_c630_ucsi_sync_control(struct ucsi *ucsi, + u64 command, + u32 *cci, + void *data, size_t size) +{ + int ret; + + /* + * EC doesn't return connector's DP mode even though it is supported. + * Fake it. + */ + if (UCSI_COMMAND(command) == UCSI_GET_ALTERNATE_MODES && + UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(command) == 1 && + UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_CON && + UCSI_ALTMODE_OFFSET(command) == 0) { + static const struct ucsi_altmode alt = { + .svid = USB_TYPEC_DP_SID, + .mid = USB_TYPEC_DP_MODE, + }; + + dev_dbg(ucsi->dev, "faking DP altmode for con1\n"); + memset(data, 0, size); + memcpy(data, &alt, min(sizeof(alt), size)); + *cci = UCSI_CCI_COMMAND_COMPLETE | UCSI_SET_CCI_LENGTH(sizeof(alt)); + return 0; + } + + /* + * EC can return AltModes present on CON1 (port0, right) for CON2 + * (port1, left) too. Ignore all requests going to CON2 (it doesn't + * support DP anyway). + */ + if (UCSI_COMMAND(command) == UCSI_GET_ALTERNATE_MODES && + UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(command) == 2) { + dev_dbg(ucsi->dev, "ignoring altmodes for con2\n"); + memset(data, 0, size); + *cci = UCSI_CCI_COMMAND_COMPLETE; + return 0; + } + + ret = ucsi_sync_control_common(ucsi, command, cci, data, size); + if (ret < 0) + return ret; + + /* UCSI_GET_CURRENT_CAM is off-by-one on all ports */ + if (UCSI_COMMAND(command) == UCSI_GET_CURRENT_CAM && data) + ((u8 *)data)[0]--; + + return ret; +} + +static bool yoga_c630_ucsi_update_altmodes(struct ucsi *ucsi, + u8 recipient, + struct ucsi_altmode *orig, + struct ucsi_altmode *updated) +{ + int i; + + if (orig[0].svid == 0 || recipient != UCSI_RECIPIENT_SOP) + return false; + + /* EC is nice and repeats altmodes again and again. Ignore copies. */ + for (i = 1; i < UCSI_MAX_ALTMODES; i++) { + if (orig[i].svid == orig[0].svid) { + dev_dbg(ucsi->dev, "Found duplicate altmodes, starting from %d\n", i); + memset(&orig[i], 0, (UCSI_MAX_ALTMODES - i) * sizeof(*orig)); + break; + } + } + + return false; +} + +static void yoga_c630_ucsi_update_connector(struct ucsi_connector *con) +{ + if (con->num == 1) + con->typec_cap.orientation_aware = true; +} + 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, + .sync_control = yoga_c630_ucsi_sync_control, .async_control = yoga_c630_ucsi_async_control, + .update_altmodes = yoga_c630_ucsi_update_altmodes, + .update_connector = yoga_c630_ucsi_update_connector, }; +static void yoga_c630_ucsi_read_port0_status(struct yoga_c630_ucsi *uec) +{ + int val; + unsigned int muxc, ccst, dppn, hpds, hsfl; + + val = yoga_c630_ec_read16(uec->ec, LENOVO_EC_USB_MUX); + + muxc = FIELD_GET(USB_MUX_MUXC, val); + ccst = FIELD_GET(USB_MUX_CCST, val); + dppn = FIELD_GET(USB_MUX_DPPN, val); + hpds = FIELD_GET(USB_MUX_HPDS, val); + hsfl = FIELD_GET(USB_MUX_HSFL, val); + + dev_dbg(uec->ucsi->dev, " mux %04x (muxc %d ccst %d dppn %d hpds %d hsfl %d)\n", + val, + muxc, ccst, dppn, hpds, hsfl); + + if (uec->ucsi->connector && uec->ucsi->connector[0].port) + typec_set_orientation(uec->ucsi->connector[0].port, + ccst == 1 ? + TYPEC_ORIENTATION_REVERSE : + TYPEC_ORIENTATION_NORMAL); + + if (uec->bridge) + drm_aux_hpd_bridge_notify(&uec->bridge->dev, + dppn != 0 ? + connector_status_connected : + connector_status_disconnected); + +} + static int yoga_c630_ucsi_notify(struct notifier_block *nb, unsigned long action, void *data) { @@ -90,6 +217,7 @@ static int yoga_c630_ucsi_notify(struct notifier_block *nb, switch (action) { case LENOVO_EC_EVENT_USB: case LENOVO_EC_EVENT_HPD: + yoga_c630_ucsi_read_port0_status(uec); ucsi_connector_change(uec->ucsi, 1); return NOTIFY_OK; @@ -121,6 +249,24 @@ static int yoga_c630_ucsi_probe(struct auxiliary_device *adev, uec->ec = ec; uec->nb.notifier_call = yoga_c630_ucsi_notify; + device_for_each_child_node_scoped(&adev->dev, fwnode) { + u32 port; + + ret = fwnode_property_read_u32(fwnode, "reg", &port); + if (ret < 0) { + dev_err(&adev->dev, "missing reg property of %pfwP\n", fwnode); + return ret; + } + + /* DP is only on port0 */ + if (port != 0) + continue; + + uec->bridge = devm_drm_dp_hpd_bridge_alloc(&adev->dev, to_of_node(fwnode)); + if (IS_ERR(uec->bridge)) + return PTR_ERR(uec->bridge); + } + uec->ucsi = ucsi_create(&adev->dev, &yoga_c630_ucsi_ops); if (IS_ERR(uec->ucsi)) return PTR_ERR(uec->ucsi); @@ -133,17 +279,39 @@ static int yoga_c630_ucsi_probe(struct auxiliary_device *adev, ret = yoga_c630_ec_register_notify(ec, &uec->nb); if (ret) - return ret; + goto err_destroy; + + ret = ucsi_register(uec->ucsi); + if (ret) + goto err_unregister; + + if (uec->bridge) { + ret = devm_drm_dp_hpd_bridge_add(&adev->dev, uec->bridge); + if (ret) + goto err_ucsi_unregister; + } - return ucsi_register(uec->ucsi); + return 0; + +err_ucsi_unregister: + ucsi_unregister(uec->ucsi); + +err_unregister: + yoga_c630_ec_unregister_notify(uec->ec, &uec->nb); + +err_destroy: + ucsi_destroy(uec->ucsi); + + return ret; } static void yoga_c630_ucsi_remove(struct auxiliary_device *adev) { struct yoga_c630_ucsi *uec = auxiliary_get_drvdata(adev); - yoga_c630_ec_unregister_notify(uec->ec, &uec->nb); ucsi_unregister(uec->ucsi); + yoga_c630_ec_unregister_notify(uec->ec, &uec->nb); + ucsi_destroy(uec->ucsi); } static const struct auxiliary_device_id yoga_c630_ucsi_id_table[] = { diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 900a64ad25e4..709a24092104 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -493,7 +493,7 @@ static int skel_probe(struct usb_interface *interface, int retval; /* allocate memory for our device state and initialize it */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc_obj(*dev); if (!dev) return -ENOMEM; diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c index ce625b1ce9a5..abfa11d6bde7 100644 --- a/drivers/usb/usbip/stub_dev.c +++ b/drivers/usb/usbip/stub_dev.c @@ -263,11 +263,11 @@ static struct stub_device *stub_device_alloc(struct usb_device *udev) dev_dbg(&udev->dev, "allocating stub device"); /* yes, it's a new device */ - sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL); + sdev = kzalloc_obj(struct stub_device); if (!sdev) return NULL; - sdev->udev = usb_get_dev(udev); + sdev->udev = udev; /* * devid is defined with devnum when this driver is first allocated. @@ -409,7 +409,6 @@ call_put_busid_priv: put_busid_priv(busid_priv); sdev_free: - usb_put_dev(udev); stub_device_free(sdev); return rc; @@ -488,8 +487,6 @@ static void stub_disconnect(struct usb_device *udev) /* shutdown the current connection */ shutdown_busid(busid_priv); - usb_put_dev(sdev->udev); - /* we already have busid_priv, just lock busid_lock */ spin_lock(&busid_priv->busid_lock); /* free sdev */ diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index 9aa30ef76f3b..1e9ae578810d 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -535,7 +535,7 @@ static void stub_recv_cmd_submit(struct stub_device *sdev, /* allocate urb array */ priv->num_urbs = num_urbs; - priv->urbs = kmalloc_array(num_urbs, sizeof(*priv->urbs), GFP_KERNEL); + priv->urbs = kmalloc_objs(*priv->urbs, num_urbs); if (!priv->urbs) goto err_urbs; diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index 7eb2e074012a..4ffd342250b5 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -4,6 +4,7 @@ */ #include <linux/kthread.h> +#include <linux/minmax.h> #include <linux/socket.h> #include <linux/scatterlist.h> @@ -16,7 +17,7 @@ void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, { struct stub_unlink *unlink; - unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC); + unlink = kzalloc_obj(struct stub_unlink, GFP_ATOMIC); if (!unlink) { usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC); return; @@ -54,8 +55,8 @@ void stub_complete(struct urb *urb) "stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n"); return; case -ECONNRESET: - dev_info(&urb->dev->dev, - "unlinked by a call to usb_unlink_urb()\n"); + dev_dbg(&urb->dev->dev, + "unlinked by a call to usb_unlink_urb()\n"); break; case -EPIPE: dev_info(&urb->dev->dev, "endpoint %d is stalled\n", @@ -190,7 +191,7 @@ static int stub_send_ret_submit(struct stub_device *sdev) else iovnum = 2; - iov = kcalloc(iovnum, sizeof(struct kvec), GFP_KERNEL); + iov = kzalloc_objs(struct kvec, iovnum); if (!iov) { usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); @@ -239,17 +240,13 @@ static int stub_send_ret_submit(struct stub_device *sdev) urb->actual_length > 0) { if (urb->num_sgs) { unsigned int copy = urb->actual_length; - int size; + unsigned int size; for_each_sg(urb->sg, sg, urb->num_sgs, i) { if (copy == 0) break; - if (copy < sg->length) - size = copy; - else - size = sg->length; - + size = min(copy, sg->length); iov[iovnum].iov_base = sg_virt(sg); iov[iovnum].iov_len = size; diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index a2b2da1255dd..a5837c0feb05 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -470,6 +470,18 @@ static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, urb->status = rpdu->status; urb->actual_length = rpdu->actual_length; urb->start_frame = rpdu->start_frame; + /* + * The number_of_packets field determines the length of + * iso_frame_desc[], which is a flexible array allocated + * at URB creation time. A response must never claim more + * packets than originally submitted; doing so would cause + * an out-of-bounds write in usbip_recv_iso() and + * usbip_pad_iso(). Clamp to zero on violation so both + * functions safely return early. + */ + if (rpdu->number_of_packets < 0 || + rpdu->number_of_packets > urb->number_of_packets) + rpdu->number_of_packets = 0; urb->number_of_packets = rpdu->number_of_packets; urb->error_count = rpdu->error_count; } @@ -662,19 +674,29 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) void *buff; struct usbip_iso_packet_descriptor *iso; int np = urb->number_of_packets; - int size = np * sizeof(*iso); + int size; int i; int ret; - int total_length = 0; + u32 total_length = 0; if (!usb_pipeisoc(urb->pipe)) return 0; - /* my Bluetooth dongle gets ISO URBs which are np = 0 */ - if (np == 0) - return 0; + if (np <= 0 || np > USBIP_MAX_ISO_PACKETS) { + dev_err(&urb->dev->dev, + "recv iso: invalid number_of_packets %d\n", np); + /* + * usbip_pack_ret_submit() already set urb->number_of_packets + * from the wire. Zero it so processcompl() does not iterate + * OOB descriptors on the way out. + */ + urb->number_of_packets = 0; + return -EPROTO; + } + + size = np * sizeof(*iso); - buff = kzalloc(size, GFP_KERNEL); + buff = kcalloc(np, sizeof(*iso), GFP_KERNEL); if (!buff) return -ENOMEM; @@ -696,14 +718,23 @@ int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) for (i = 0; i < np; i++) { usbip_iso_packet_correct_endian(&iso[i], 0); usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0); + if (urb->iso_frame_desc[i].actual_length > + (unsigned int)urb->transfer_buffer_length) { + dev_err(&urb->dev->dev, + "recv iso: frame actual_length %u exceeds buffer %d\n", + urb->iso_frame_desc[i].actual_length, + urb->transfer_buffer_length); + kfree(buff); + return -EPROTO; + } total_length += urb->iso_frame_desc[i].actual_length; } kfree(buff); - if (total_length != urb->actual_length) { + if (total_length != (u32)urb->actual_length) { dev_err(&urb->dev->dev, - "total length of iso packets %d not equal to actual length of buffer %d\n", + "total length of iso packets %u not equal to actual length of buffer %d\n", total_length, urb->actual_length); if (ud->side == USBIP_STUB || ud->side == USBIP_VUDC) @@ -751,6 +782,42 @@ void usbip_pad_iso(struct usbip_device *ud, struct urb *urb) */ for (i = np-1; i > 0; i--) { actualoffset -= urb->iso_frame_desc[i].actual_length; + + /* + * Validate source range: actualoffset can go negative + * via crafted actual_length values from the wire. + */ + if (actualoffset < 0 || + (unsigned int)actualoffset > + (unsigned int)urb->transfer_buffer_length || + urb->iso_frame_desc[i].actual_length > + (unsigned int)urb->transfer_buffer_length - + (unsigned int)actualoffset) { + dev_err(&urb->dev->dev, + "pad_iso: bad src off=%d len=%u bufsz=%d\n", + actualoffset, + urb->iso_frame_desc[i].actual_length, + urb->transfer_buffer_length); + return; + } + + /* + * Validate destination range: iso_frame_desc[i].offset + * is wire-supplied and must not exceed the buffer. + */ + if (urb->iso_frame_desc[i].offset > + (unsigned int)urb->transfer_buffer_length || + urb->iso_frame_desc[i].actual_length > + (unsigned int)urb->transfer_buffer_length - + urb->iso_frame_desc[i].offset) { + dev_err(&urb->dev->dev, + "pad_iso: bad dst off=%u len=%u bufsz=%d\n", + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length, + urb->transfer_buffer_length); + return; + } + memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, urb->transfer_buffer + actualoffset, urb->iso_frame_desc[i].actual_length); diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c index 26513540bcdb..0e00c2d000f8 100644 --- a/drivers/usb/usbip/usbip_event.c +++ b/drivers/usb/usbip/usbip_event.c @@ -158,7 +158,7 @@ void usbip_event_add(struct usbip_device *ud, unsigned long event) goto out; } - ue = kmalloc(sizeof(struct usbip_event), GFP_ATOMIC); + ue = kmalloc_obj(struct usbip_event, GFP_ATOMIC); if (ue == NULL) goto out; diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index e70fba9f55d6..39e8faf4c18c 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -346,9 +346,10 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (wIndex < 1 || wIndex > VHCI_HC_PORTS) { invalid_rhport = true; if (wIndex > VHCI_HC_PORTS) - pr_err("invalid port number %d\n", wIndex); - } else + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); + } else { rhport = wIndex - 1; + } vhci_hcd = hcd_to_vhci_hcd(hcd); vhci = vhci_hcd->vhci; @@ -368,14 +369,14 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case ClearPortFeature: if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (hcd->speed >= HCD_USB3) { - pr_err(" ClearPortFeature: USB_PORT_FEAT_SUSPEND req not " - "supported for USB 3.0 roothub\n"); + dev_err(hcd_dev(hcd), + "ClearPortFeature: USB_PORT_FEAT_SUSPEND req not supported for USB 3.0 roothub\n"); goto error; } usbip_dbg_vhci_rh( @@ -408,7 +409,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (hcd->speed >= HCD_USB3 && (wLength < USB_DT_SS_HUB_SIZE || wValue != (USB_DT_SS_HUB << 8))) { - pr_err("Wrong hub descriptor type for USB 3.0 roothub.\n"); + dev_err(hcd_dev(hcd), + "Wrong hub descriptor type for USB 3.0 roothub.\n"); goto error; } if (hcd->speed >= HCD_USB3) @@ -433,7 +435,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case GetPortStatus: usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); retval = -EPIPE; goto error; } @@ -483,7 +485,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, USB_PORT_STAT_LOW_SPEED; break; default: - pr_err("vhci_device speed not set\n"); + dev_err(hcd_dev(hcd), "vhci_device speed not set\n"); break; } } @@ -505,8 +507,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_LINK_STATE\n"); if (hcd->speed < HCD_USB3) { - pr_err("USB_PORT_FEAT_LINK_STATE req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_LINK_STATE req not supported for USB 2.0 roothub\n"); goto error; } /* @@ -523,8 +525,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, " SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n"); /* TODO: add suspend/resume support! */ if (hcd->speed < HCD_USB3) { - pr_err("USB_PORT_FEAT_U1/2_TIMEOUT req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_U1/2_TIMEOUT req not supported for USB 2.0 roothub\n"); goto error; } break; @@ -533,13 +535,13 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); /* Applicable only for USB2.0 hub */ if (hcd->speed >= HCD_USB3) { - pr_err("USB_PORT_FEAT_SUSPEND req not " - "supported for USB 3.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_SUSPEND req not supported for USB 3.0 roothub\n"); goto error; } if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } @@ -549,7 +551,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_POWER\n"); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } if (hcd->speed >= HCD_USB3) @@ -561,13 +563,13 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n"); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } /* Applicable only for USB3.0 hub */ if (hcd->speed < HCD_USB3) { - pr_err("USB_PORT_FEAT_BH_PORT_RESET req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "USB_PORT_FEAT_BH_PORT_RESET req not supported for USB 2.0 roothub\n"); goto error; } fallthrough; @@ -575,7 +577,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_RESET\n"); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } /* if it's already enabled, disable */ @@ -598,7 +600,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", wValue); if (invalid_rhport) { - pr_err("invalid port number %d\n", wIndex); + dev_err(hcd_dev(hcd), "invalid port number %d\n", wIndex); goto error; } if (wValue >= 32) @@ -618,8 +620,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case GetPortErrorCount: usbip_dbg_vhci_rh(" GetPortErrorCount\n"); if (hcd->speed < HCD_USB3) { - pr_err("GetPortErrorCount req not " - "supported for USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "GetPortErrorCount req not supported for USB 2.0 roothub\n"); goto error; } /* We'll always return 0 since this is a dummy hub */ @@ -628,13 +630,14 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case SetHubDepth: usbip_dbg_vhci_rh(" SetHubDepth\n"); if (hcd->speed < HCD_USB3) { - pr_err("SetHubDepth req not supported for " - "USB 2.0 roothub\n"); + dev_err(hcd_dev(hcd), + "SetHubDepth req not supported for USB 2.0 roothub\n"); goto error; } break; default: - pr_err("default hub control req: %04x v%04x i%04x l%d\n", + dev_err(hcd_dev(hcd), + "default hub control req: %04x v%04x i%04x l%d\n", typeReq, wValue, wIndex, wLength); error: /* "protocol stall" on error */ @@ -642,7 +645,7 @@ error: } if (usbip_dbg_flag_vhci_rh) { - pr_debug("port %d\n", rhport); + dev_dbg(hcd_dev(hcd), "%s port %d\n", __func__, rhport); /* Only dump valid port status */ if (!invalid_rhport) { dump_port_status_diff(prev_port_status[rhport], @@ -668,7 +671,7 @@ static void vhci_tx_urb(struct urb *urb, struct vhci_device *vdev) struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev); unsigned long flags; - priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC); + priv = kzalloc_obj(struct vhci_priv, GFP_ATOMIC); if (!priv) { usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); return; @@ -702,7 +705,7 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag unsigned long flags; if (portnum > VHCI_HC_PORTS) { - pr_err("invalid port number %d\n", portnum); + dev_err(hcd_dev(hcd), "invalid port number %d\n", portnum); return -ENODEV; } vdev = &vhci_hcd->vdev[portnum-1]; @@ -765,6 +768,17 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag ctrlreq->wValue, vdev->rhport); vdev->udev = usb_get_dev(urb->dev); + /* + * NOTE: A similar operation has been done via + * USB_REQ_GET_DESCRIPTOR handler below, which is + * supposed to always precede USB_REQ_SET_ADDRESS. + * + * It's not entirely clear if operating on a different + * usb_device instance here is a real possibility, + * otherwise this call and vdev->udev assignment above + * should be dropped. + */ + dev_pm_syscore_device(&vdev->udev->dev, true); usb_put_dev(old); spin_lock(&vdev->ud.lock); @@ -785,6 +799,17 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag "Not yet?:Get_Descriptor to device 0 (get max pipe size)\n"); vdev->udev = usb_get_dev(urb->dev); + /* + * Set syscore PM flag for the virtually attached + * devices to ensure they will not enter suspend on + * the client side. + * + * Note this doesn't have any impact on the physical + * devices attached to the host system on the server + * side, hence there is no need to undo the operation + * on disconnect. + */ + dev_pm_syscore_device(&vdev->udev->dev, true); usb_put_dev(old); goto out; @@ -809,15 +834,15 @@ out: no_need_xmit: usb_hcd_unlink_urb_from_ep(hcd, urb); no_need_unlink: - spin_unlock_irqrestore(&vhci->lock, flags); if (!ret) { /* usb_hcd_giveback_urb() should be called with * irqs disabled */ - local_irq_disable(); + spin_unlock(&vhci->lock); usb_hcd_giveback_urb(hcd, urb, urb->status); - local_irq_enable(); + spin_lock(&vhci->lock); } + spin_unlock_irqrestore(&vhci->lock, flags); return ret; } @@ -926,7 +951,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) spin_lock(&vdev->priv_lock); /* setup CMD_UNLINK pdu */ - unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); + unlink = kzalloc_obj(struct vhci_unlink, GFP_ATOMIC); if (!unlink) { spin_unlock(&vdev->priv_lock); spin_unlock_irqrestore(&vhci->lock, flags); @@ -936,7 +961,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) unlink->seqnum = atomic_inc_return(&vhci_hcd->seqnum); if (unlink->seqnum == 0xffff) - pr_info("seqnum max\n"); + dev_info(hcd_dev(hcd), "seqnum max\n"); unlink->unlink_seqnum = priv->seqnum; @@ -1014,10 +1039,11 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev) static void vhci_shutdown_connection(struct usbip_device *ud) { struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + struct usb_hcd *hcd = vhci_hcd_to_hcd(vdev_to_vhci_hcd(vdev)); /* need this? see stub_dev.c */ if (ud->tcp_socket) { - pr_debug("shutdown tcp_socket %d\n", ud->sockfd); + dev_dbg(hcd_dev(hcd), "shutdown tcp_socket %d\n", ud->sockfd); kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); } @@ -1030,7 +1056,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) kthread_stop_put(vdev->ud.tcp_tx); vdev->ud.tcp_tx = NULL; } - pr_info("stop threads\n"); + dev_info(hcd_dev(hcd), "stop threads\n"); /* active connection is closed */ if (vdev->ud.tcp_socket) { @@ -1038,7 +1064,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) vdev->ud.tcp_socket = NULL; vdev->ud.sockfd = -1; } - pr_info("release socket\n"); + dev_info(hcd_dev(hcd), "release socket\n"); vhci_device_unlink_cleanup(vdev); @@ -1064,7 +1090,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) */ rh_port_disconnect(vdev); - pr_info("disconnect device\n"); + dev_info(hcd_dev(hcd), "disconnect device\n"); } static void vhci_device_reset(struct usbip_device *ud) @@ -1200,7 +1226,7 @@ static int vhci_start(struct usb_hcd *hcd) id = hcd_name_to_id(hcd_name(hcd)); if (id < 0) { - pr_err("invalid vhci name %s\n", hcd_name(hcd)); + dev_err(hcd_dev(hcd), "invalid vhci name %s\n", hcd_name(hcd)); return -EINVAL; } @@ -1217,7 +1243,7 @@ static int vhci_start(struct usb_hcd *hcd) vhci_finish_attr_group(); return err; } - pr_info("created sysfs %s\n", hcd_name(hcd)); + dev_info(hcd_dev(hcd), "created sysfs %s\n", hcd_name(hcd)); } return 0; @@ -1350,10 +1376,9 @@ static int vhci_hcd_probe(struct platform_device *pdev) * Our private data is also allocated automatically. */ hcd_hs = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); - if (!hcd_hs) { - pr_err("create primary hcd failed\n"); - return -ENOMEM; - } + if (!hcd_hs) + return dev_err_probe(&pdev->dev, -ENOMEM, "create primary hcd failed\n"); + hcd_hs->has_tt = 1; /* @@ -1361,22 +1386,21 @@ static int vhci_hcd_probe(struct platform_device *pdev) * Call the driver's reset() and start() routines. */ ret = usb_add_hcd(hcd_hs, 0, 0); - if (ret != 0) { - pr_err("usb_add_hcd hs failed %d\n", ret); + if (ret) { + dev_err_probe(&pdev->dev, ret, "usb_add_hcd hs failed\n"); goto put_usb2_hcd; } hcd_ss = usb_create_shared_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev), hcd_hs); if (!hcd_ss) { - ret = -ENOMEM; - pr_err("create shared hcd failed\n"); + ret = dev_err_probe(&pdev->dev, -ENOMEM, "create shared hcd failed\n"); goto remove_usb2_hcd; } ret = usb_add_hcd(hcd_ss, 0, 0); if (ret) { - pr_err("usb_add_hcd ss failed %d\n", ret); + dev_err_probe(&pdev->dev, ret, "usb_add_hcd ss failed\n"); goto put_usb3_hcd; } @@ -1513,7 +1537,7 @@ static int __init vhci_hcd_init(void) if (vhci_num_controllers < 1) vhci_num_controllers = 1; - vhcis = kcalloc(vhci_num_controllers, sizeof(struct vhci), GFP_KERNEL); + vhcis = kzalloc_objs(struct vhci, vhci_num_controllers); if (vhcis == NULL) return -ENOMEM; diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index d5865460e82d..5bc8c47788d4 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -463,7 +463,7 @@ static void set_status_attr(int id) status = status_attrs + id; if (id == 0) - strcpy(status->name, "status"); + strscpy(status->name, "status"); else snprintf(status->name, MAX_STATUS_NAME+1, "status.%d", id); status->attr.attr.name = status->name; @@ -476,8 +476,7 @@ static int init_status_attrs(void) { int id; - status_attrs = kcalloc(vhci_num_controllers, sizeof(struct status_attr), - GFP_KERNEL); + status_attrs = kzalloc_objs(struct status_attr, vhci_num_controllers); if (status_attrs == NULL) return -ENOMEM; @@ -501,8 +500,7 @@ int vhci_init_attr_group(void) struct attribute **attrs; int ret, i; - attrs = kcalloc((vhci_num_controllers + 5), sizeof(struct attribute *), - GFP_KERNEL); + attrs = kzalloc_objs(struct attribute *, (vhci_num_controllers + 5)); if (attrs == NULL) return -ENOMEM; diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c index 0ae40a13a9fe..32e6fabccf72 100644 --- a/drivers/usb/usbip/vhci_tx.c +++ b/drivers/usb/usbip/vhci_tx.c @@ -82,7 +82,7 @@ static int vhci_send_cmd_submit(struct vhci_device *vdev) else iovnum = 3; - iov = kcalloc(iovnum, sizeof(*iov), GFP_KERNEL); + iov = kzalloc_objs(*iov, iovnum); if (!iov) { usbip_event_add(&vdev->ud, SDEV_EVENT_ERROR_MALLOC); return -ENOMEM; diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index f11535020e35..90383107b660 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -43,7 +43,7 @@ struct urbp *alloc_urbp(void) { struct urbp *urb_p; - urb_p = kzalloc(sizeof(*urb_p), GFP_KERNEL); + urb_p = kzalloc_obj(*urb_p); if (!urb_p) return urb_p; @@ -284,7 +284,7 @@ static struct usb_request *vep_alloc_request(struct usb_ep *_ep, if (!_ep) return NULL; - req = kzalloc(sizeof(*req), mem_flags); + req = kzalloc_obj(*req, mem_flags); if (!req) return NULL; @@ -491,7 +491,7 @@ struct vudc_device *alloc_vudc_device(int devid) { struct vudc_device *udc_dev; - udc_dev = kzalloc(sizeof(*udc_dev), GFP_KERNEL); + udc_dev = kzalloc_obj(*udc_dev); if (!udc_dev) return NULL; @@ -518,7 +518,7 @@ static int init_vudc_hw(struct vudc *udc) struct usbip_device *ud = &udc->ud; struct vep *ep; - udc->ep = kcalloc(VIRTUAL_ENDPOINTS, sizeof(*udc->ep), GFP_KERNEL); + udc->ep = kzalloc_objs(*udc->ep, VIRTUAL_ENDPOINTS); if (!udc->ep) goto nomem_ep; @@ -598,7 +598,7 @@ int vudc_probe(struct platform_device *pdev) struct vudc *udc; int ret = -ENOMEM; - udc = kzalloc(sizeof(*udc), GFP_KERNEL); + udc = kzalloc_obj(*udc); if (!udc) goto out; diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c index 2aae3edfc813..47f3a7d51736 100644 --- a/drivers/usb/usbip/vudc_sysfs.c +++ b/drivers/usb/usbip/vudc_sysfs.c @@ -259,7 +259,7 @@ static const struct bin_attribute *const dev_bin_attrs[] = { static const struct attribute_group vudc_attr_group = { .attrs = dev_attrs, - .bin_attrs_new = dev_bin_attrs, + .bin_attrs = dev_bin_attrs, }; const struct attribute_group *vudc_groups[] = { diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c index 7e801fee33bf..a4f02ea3e3ef 100644 --- a/drivers/usb/usbip/vudc_transfer.c +++ b/drivers/usb/usbip/vudc_transfer.c @@ -301,7 +301,7 @@ top: static void v_timer(struct timer_list *t) { - struct vudc *udc = from_timer(udc, t, tr_timer.timer); + struct vudc *udc = timer_container_of(udc, t, tr_timer.timer); struct transfer_timer *timer = &udc->tr_timer; struct urbp *urb_p, *tmp; unsigned long flags; diff --git a/drivers/usb/usbip/vudc_tx.c b/drivers/usb/usbip/vudc_tx.c index 30c11bf9f4e7..88edfbcc0035 100644 --- a/drivers/usb/usbip/vudc_tx.c +++ b/drivers/usb/usbip/vudc_tx.c @@ -97,7 +97,7 @@ static int v_send_ret_submit(struct vudc *udc, struct urbp *urb_p) else iovnum = 2; - iov = kcalloc(iovnum, sizeof(*iov), GFP_KERNEL); + iov = kzalloc_objs(*iov, iovnum); if (!iov) { usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_MALLOC); ret = -ENOMEM; @@ -246,12 +246,12 @@ void v_enqueue_ret_unlink(struct vudc *udc, __u32 seqnum, __u32 status) struct tx_item *txi; struct v_unlink *unlink; - txi = kzalloc(sizeof(*txi), GFP_ATOMIC); + txi = kzalloc_obj(*txi, GFP_ATOMIC); if (!txi) { usbip_event_add(&udc->ud, VDEV_EVENT_ERROR_MALLOC); return; } - unlink = kzalloc(sizeof(*unlink), GFP_ATOMIC); + unlink = kzalloc_obj(*unlink, GFP_ATOMIC); if (!unlink) { kfree(txi); usbip_event_add(&udc->ud, VDEV_EVENT_ERROR_MALLOC); @@ -271,7 +271,7 @@ void v_enqueue_ret_submit(struct vudc *udc, struct urbp *urb_p) { struct tx_item *txi; - txi = kzalloc(sizeof(*txi), GFP_ATOMIC); + txi = kzalloc_obj(*txi, GFP_ATOMIC); if (!txi) { usbip_event_add(&udc->ud, VDEV_EVENT_ERROR_MALLOC); return; |
