diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-08 10:03:52 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-08 10:03:52 -0700 |
commit | 132d68d37d33f1d0b9c1f507c8b4d64c27ecec8a (patch) | |
tree | b3c05972e5579e1574873fe745fb1358c62a269c /drivers/usb | |
parent | 80f232121b69cc69a31ccb2b38c1665d770b0710 (diff) | |
parent | 3515468a87a47781f6af818773650513ff14656a (diff) |
Merge tag 'usb-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB/PHY updates from Greg KH:
"Here is the big set of USB and PHY driver patches for 5.2-rc1
There is the usual set of:
- USB gadget updates
- PHY driver updates and additions
- USB serial driver updates and fixes
- typec updates and new chips supported
- mtu3 driver updates
- xhci driver updates
- other tiny driver updates
Nothing really interesting, just constant forward progress.
All of these have been in linux-next for a while with no reported
issues. The usb-gadget and usb-serial trees were merged a bit "late",
but both of them had been in linux-next before they got merged here
last Friday"
* tag 'usb-5.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (206 commits)
USB: serial: f81232: implement break control
USB: serial: f81232: add high baud rate support
USB: serial: f81232: clear overrun flag
USB: serial: f81232: fix interrupt worker not stop
usb: dwc3: Rename DWC3_DCTL_LPM_ERRATA
usb: dwc3: Fix default lpm_nyet_threshold value
usb: dwc3: debug: Print GET_STATUS(device) tracepoint
usb: dwc3: Do core validation early on probe
usb: dwc3: gadget: Set lpm_capable
usb: gadget: atmel: tie wake lock to running clock
usb: gadget: atmel: support USB suspend
usb: gadget: atmel_usba_udc: simplify setting of interrupt-enabled mask
dwc2: gadget: Fix completed transfer size calculation in DDMA
usb: dwc2: Set lpm mode parameters depend on HW configuration
usb: dwc2: Fix channel disable flow
usb: dwc2: Set actual frame number for completed ISOC transfer
usb: gadget: do not use __constant_cpu_to_le16
usb: dwc2: gadget: Increase descriptors count for ISOC's
usb: introduce usb_ep_type_string() function
usb: dwc3: move synchronize_irq() out of the spinlock protected block
...
Diffstat (limited to 'drivers/usb')
100 files changed, 5237 insertions, 1620 deletions
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index 880009987460..b8b3caad889c 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -205,12 +205,9 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev) if (IS_ERR(clk)) return PTR_ERR(clk); - ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs"); - if (IS_ERR(clk)) { - if (PTR_ERR(clk) == -EPROBE_DEFER) - return -EPROBE_DEFER; - ci->fs_clk = NULL; - } + ci->fs_clk = clk = devm_clk_get_optional(&pdev->dev, "fs"); + if (IS_ERR(clk)) + return PTR_ERR(clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ci->base = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index ec666eb4b7b4..183b41753c98 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -468,14 +468,13 @@ static void acm_read_bulk_callback(struct urb *urb) { struct acm_rb *rb = urb->context; struct acm *acm = rb->instance; - unsigned long flags; int status = urb->status; + bool stopped = false; + bool stalled = false; dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n", rb->index, urb->actual_length, status); - set_bit(rb->index, &acm->read_urbs_free); - if (!acm->dev) { dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); return; @@ -488,15 +487,16 @@ static void acm_read_bulk_callback(struct urb *urb) break; case -EPIPE: set_bit(EVENT_RX_STALL, &acm->flags); - schedule_work(&acm->work); - return; + stalled = true; + break; case -ENOENT: case -ECONNRESET: case -ESHUTDOWN: dev_dbg(&acm->data->dev, "%s - urb shutting down with status: %d\n", __func__, status); - return; + stopped = true; + break; default: dev_dbg(&acm->data->dev, "%s - nonzero urb status received: %d\n", @@ -505,20 +505,29 @@ static void acm_read_bulk_callback(struct urb *urb) } /* - * Unthrottle may run on another CPU which needs to see events - * in the same order. Submission has an implict barrier + * Make sure URB processing is done before marking as free to avoid + * racing with unthrottle() on another CPU. Matches the barriers + * implied by the test_and_clear_bit() in acm_submit_read_urb(). */ smp_mb__before_atomic(); + set_bit(rb->index, &acm->read_urbs_free); + /* + * Make sure URB is marked as free before checking the throttled flag + * to avoid racing with unthrottle() on another CPU. Matches the + * smp_mb() in unthrottle(). + */ + smp_mb__after_atomic(); - /* throttle device if requested by tty */ - spin_lock_irqsave(&acm->read_lock, flags); - acm->throttled = acm->throttle_req; - if (!acm->throttled) { - spin_unlock_irqrestore(&acm->read_lock, flags); - acm_submit_read_urb(acm, rb->index, GFP_ATOMIC); - } else { - spin_unlock_irqrestore(&acm->read_lock, flags); + if (stopped || stalled) { + if (stalled) + schedule_work(&acm->work); + return; } + + if (test_bit(ACM_THROTTLED, &acm->flags)) + return; + + acm_submit_read_urb(acm, rb->index, GFP_ATOMIC); } /* data interface wrote those outgoing bytes */ @@ -655,10 +664,7 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty) /* * Unthrottle device in case the TTY was closed while throttled. */ - spin_lock_irq(&acm->read_lock); - acm->throttled = 0; - acm->throttle_req = 0; - spin_unlock_irq(&acm->read_lock); + clear_bit(ACM_THROTTLED, &acm->flags); retval = acm_submit_read_urbs(acm, GFP_KERNEL); if (retval) @@ -826,24 +832,19 @@ static void acm_tty_throttle(struct tty_struct *tty) { struct acm *acm = tty->driver_data; - spin_lock_irq(&acm->read_lock); - acm->throttle_req = 1; - spin_unlock_irq(&acm->read_lock); + set_bit(ACM_THROTTLED, &acm->flags); } static void acm_tty_unthrottle(struct tty_struct *tty) { struct acm *acm = tty->driver_data; - unsigned int was_throttled; - spin_lock_irq(&acm->read_lock); - was_throttled = acm->throttled; - acm->throttled = 0; - acm->throttle_req = 0; - spin_unlock_irq(&acm->read_lock); + clear_bit(ACM_THROTTLED, &acm->flags); + + /* Matches the smp_mb__after_atomic() in acm_read_bulk_callback(). */ + smp_mb(); - if (was_throttled) - acm_submit_read_urbs(acm, GFP_KERNEL); + acm_submit_read_urbs(acm, GFP_KERNEL); } static int acm_tty_break_ctl(struct tty_struct *tty, int state) diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 515aad0847ee..ca1c026382c2 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -108,6 +108,7 @@ struct acm { unsigned long flags; # define EVENT_TTY_WAKEUP 0 # define EVENT_RX_STALL 1 +# define ACM_THROTTLED 2 struct usb_cdc_line_coding line; /* bits, stop, parity */ struct work_struct work; /* work queue entry for line discipline waking up */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ @@ -122,8 +123,6 @@ struct acm { unsigned int ctrl_caps; /* control capabilities from the class specific header */ unsigned int susp_count; /* number of suspended interfaces */ unsigned int combined_interfaces:1; /* control and data collapsed */ - unsigned int throttled:1; /* actually throttled */ - unsigned int throttle_req:1; /* throttle requested */ u8 bInterval; struct usb_anchor delayed; /* writes queued for a device about to be woken */ unsigned long quirks; diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index 73c8e6591746..18f5dcf58b0d 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -16,6 +16,22 @@ #include <linux/usb/otg.h> #include <linux/of_platform.h> +static const char *const ep_type_names[] = { + [USB_ENDPOINT_XFER_CONTROL] = "ctrl", + [USB_ENDPOINT_XFER_ISOC] = "isoc", + [USB_ENDPOINT_XFER_BULK] = "bulk", + [USB_ENDPOINT_XFER_INT] = "intr", +}; + +const char *usb_ep_type_string(int ep_type) +{ + if (ep_type < 0 || ep_type >= ARRAY_SIZE(ep_type_names)) + return "unknown"; + + return ep_type_names[ep_type]; +} +EXPORT_SYMBOL_GPL(usb_ep_type_string); + const char *usb_otg_state_string(enum usb_otg_state state) { static const char *const names[] = { diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 975d7c1288e3..94d22551fc1b 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1878,23 +1878,10 @@ rescan: /* kick hcd */ unlink1(hcd, urb, -ESHUTDOWN); dev_dbg (hcd->self.controller, - "shutdown urb %pK ep%d%s%s\n", + "shutdown urb %pK ep%d%s-%s\n", urb, usb_endpoint_num(&ep->desc), is_in ? "in" : "out", - ({ char *s; - - switch (usb_endpoint_type(&ep->desc)) { - case USB_ENDPOINT_XFER_CONTROL: - s = ""; break; - case USB_ENDPOINT_XFER_BULK: - s = "-bulk"; break; - case USB_ENDPOINT_XFER_INT: - s = "-intr"; break; - default: - s = "-iso"; break; - }; - s; - })); + usb_ep_type_string(usb_endpoint_type(&ep->desc))); usb_put_urb (urb); /* list contents may have changed */ @@ -2448,6 +2435,19 @@ EXPORT_SYMBOL_GPL(usb_hcd_irq); /*-------------------------------------------------------------------------*/ +/* Workqueue routine for when the root-hub has died. */ +static void hcd_died_work(struct work_struct *work) +{ + struct usb_hcd *hcd = container_of(work, struct usb_hcd, died_work); + static char *env[] = { + "ERROR=DEAD", + NULL + }; + + /* Notify user space that the host controller has died */ + kobject_uevent_env(&hcd->self.root_hub->dev.kobj, KOBJ_OFFLINE, env); +} + /** * usb_hc_died - report abnormal shutdown of a host controller (bus glue) * @hcd: pointer to the HCD representing the controller @@ -2488,6 +2488,13 @@ void usb_hc_died (struct usb_hcd *hcd) usb_kick_hub_wq(hcd->self.root_hub); } } + + /* Handle the case where this function gets called with a shared HCD */ + if (usb_hcd_is_primary_hcd(hcd)) + schedule_work(&hcd->died_work); + else + schedule_work(&hcd->primary_hcd->died_work); + spin_unlock_irqrestore (&hcd_root_hub_lock, flags); /* Make sure that the other roothub is also deallocated. */ } @@ -2555,6 +2562,8 @@ struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver, INIT_WORK(&hcd->wakeup_work, hcd_resume_work); #endif + INIT_WORK(&hcd->died_work, hcd_died_work); + hcd->driver = driver; hcd->speed = driver->flags & HCD_MASK; hcd->product_desc = (driver->product_desc) ? driver->product_desc : @@ -2908,6 +2917,7 @@ error_create_attr_group: #ifdef CONFIG_PM cancel_work_sync(&hcd->wakeup_work); #endif + cancel_work_sync(&hcd->died_work); mutex_lock(&usb_bus_idr_lock); usb_disconnect(&rhdev); /* Sets rhdev to NULL */ mutex_unlock(&usb_bus_idr_lock); @@ -2968,6 +2978,7 @@ void usb_remove_hcd(struct usb_hcd *hcd) #ifdef CONFIG_PM cancel_work_sync(&hcd->wakeup_work); #endif + cancel_work_sync(&hcd->died_work); mutex_lock(&usb_bus_idr_lock); usb_disconnect(&rhdev); /* Sets rhdev to NULL */ @@ -3020,6 +3031,9 @@ usb_hcd_platform_shutdown(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); + /* No need for pm_runtime_put(), we're shutting down */ + pm_runtime_get_sync(&dev->dev); + if (hcd->driver->shutdown) hcd->driver->shutdown(hcd); } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 8d4631c81b9f..2f94568ba385 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3174,13 +3174,14 @@ static int usb_disable_remote_wakeup(struct usb_device *udev) } /* Count of wakeup-enabled devices at or below udev */ -static unsigned wakeup_enabled_descendants(struct usb_device *udev) +unsigned usb_wakeup_enabled_descendants(struct usb_device *udev) { struct usb_hub *hub = usb_hub_to_struct_hub(udev); return udev->do_remote_wakeup + (hub ? hub->wakeup_enabled_descendants : 0); } +EXPORT_SYMBOL_GPL(usb_wakeup_enabled_descendants); /* * usb_port_suspend - suspend a usb device's upstream port @@ -3282,7 +3283,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) * Therefore we will turn on the suspend feature if udev or any of its * descendants is enabled for remote wakeup. */ - else if (PMSG_IS_AUTO(msg) || wakeup_enabled_descendants(udev) > 0) + else if (PMSG_IS_AUTO(msg) || usb_wakeup_enabled_descendants(udev) > 0) status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); else { @@ -3668,7 +3669,6 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_hub *hub = usb_get_intfdata(intf); struct usb_device *hdev = hub->hdev; unsigned port1; - int status; /* * Warn if children aren't already suspended. @@ -3687,7 +3687,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) } if (udev) hub->wakeup_enabled_descendants += - wakeup_enabled_descendants(udev); + usb_wakeup_enabled_descendants(udev); } if (hdev->do_remote_wakeup && hub->quirk_check_port_auto_suspend) { @@ -3702,12 +3702,12 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) { /* Enable hub to send remote wakeup for all ports. */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { - status = set_port_feature(hdev, - port1 | - USB_PORT_FEAT_REMOTE_WAKE_CONNECT | - USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT | - USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT, - USB_PORT_FEAT_REMOTE_WAKE_MASK); + set_port_feature(hdev, + port1 | + USB_PORT_FEAT_REMOTE_WAKE_CONNECT | + USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT | + USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT, + USB_PORT_FEAT_REMOTE_WAKE_MASK); } } @@ -5902,7 +5902,10 @@ int usb_reset_device(struct usb_device *udev) cintf->needs_binding = 1; } } - usb_unbind_and_rebind_marked_interfaces(udev); + + /* If the reset failed, hub_wq will unbind drivers later */ + if (ret == 0) + usb_unbind_and_rebind_marked_interfaces(udev); } usb_autosuspend_device(udev); diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 55d5ae2a7ec7..8b499d643461 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -1020,6 +1020,205 @@ int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask, return -ETIMEDOUT; } +/* + * Initializes the FSLSPClkSel field of the HCFG register depending on the + * PHY type + */ +void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) +{ + u32 hcfg, val; + + if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + hsotg->params.ulpi_fs_ls) || + hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* Full speed PHY */ + val = HCFG_FSLSPCLKSEL_48_MHZ; + } else { + /* High speed PHY running at full speed or high speed */ + val = HCFG_FSLSPCLKSEL_30_60_MHZ; + } + + dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val); + hcfg = dwc2_readl(hsotg, HCFG); + hcfg &= ~HCFG_FSLSPCLKSEL_MASK; + hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; + dwc2_writel(hsotg, hcfg, HCFG); +} + +static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) +{ + u32 usbcfg, ggpio, i2cctl; + int retval = 0; + + /* + * core_init() is now called on every switch so only call the + * following for the first time through + */ + if (select_phy) { + dev_dbg(hsotg->dev, "FS PHY selected\n"); + + usbcfg = dwc2_readl(hsotg, GUSBCFG); + if (!(usbcfg & GUSBCFG_PHYSEL)) { + usbcfg |= GUSBCFG_PHYSEL; + dwc2_writel(hsotg, usbcfg, GUSBCFG); + + /* Reset after a PHY select */ + retval = dwc2_core_reset(hsotg, false); + + if (retval) { + dev_err(hsotg->dev, + "%s: Reset failed, aborting", __func__); + return retval; + } + } + + if (hsotg->params.activate_stm_fs_transceiver) { + ggpio = dwc2_readl(hsotg, GGPIO); + if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { + dev_dbg(hsotg->dev, "Activating transceiver\n"); + /* + * STM32F4x9 uses the GGPIO register as general + * core configuration register. + */ + ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; + dwc2_writel(hsotg, ggpio, GGPIO); + } + } + } + + /* + * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also + * do this on HNP Dev/Host mode switches (done in dev_init and + * host_init). + */ + if (dwc2_is_host_mode(hsotg)) + dwc2_init_fs_ls_pclk_sel(hsotg); + + if (hsotg->params.i2c_enable) { + dev_dbg(hsotg->dev, "FS PHY enabling I2C\n"); + + /* Program GUSBCFG.OtgUtmiFsSel to I2C */ + usbcfg = dwc2_readl(hsotg, GUSBCFG); + usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; + dwc2_writel(hsotg, usbcfg, GUSBCFG); + + /* Program GI2CCTL.I2CEn */ + i2cctl = dwc2_readl(hsotg, GI2CCTL); + i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; + i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; + i2cctl &= ~GI2CCTL_I2CEN; + dwc2_writel(hsotg, i2cctl, GI2CCTL); + i2cctl |= GI2CCTL_I2CEN; + dwc2_writel(hsotg, i2cctl, GI2CCTL); + } + + return retval; +} + +static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) +{ + u32 usbcfg, usbcfg_old; + int retval = 0; + + if (!select_phy) + return 0; + + usbcfg = dwc2_readl(hsotg, GUSBCFG); + usbcfg_old = usbcfg; + + /* + * HS PHY parameters. These parameters are preserved during soft reset + * so only program the first time. Do a soft reset immediately after + * setting phyif. + */ + switch (hsotg->params.phy_type) { + case DWC2_PHY_TYPE_PARAM_ULPI: + /* ULPI interface */ + dev_dbg(hsotg->dev, "HS ULPI PHY selected\n"); + usbcfg |= GUSBCFG_ULPI_UTMI_SEL; + usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); + if (hsotg->params.phy_ulpi_ddr) + usbcfg |= GUSBCFG_DDRSEL; + + /* Set external VBUS indicator as needed. */ + if (hsotg->params.oc_disable) + usbcfg |= (GUSBCFG_ULPI_INT_VBUS_IND | + GUSBCFG_INDICATORPASSTHROUGH); + break; + case DWC2_PHY_TYPE_PARAM_UTMI: + /* UTMI+ interface */ + dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n"); + usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); + if (hsotg->params.phy_utmi_width == 16) + usbcfg |= GUSBCFG_PHYIF16; + + /* Set turnaround time */ + if (dwc2_is_device_mode(hsotg)) { + usbcfg &= ~GUSBCFG_USBTRDTIM_MASK; + if (hsotg->params.phy_utmi_width == 16) + usbcfg |= 5 << GUSBCFG_USBTRDTIM_SHIFT; + else + usbcfg |= 9 << GUSBCFG_USBTRDTIM_SHIFT; + } + break; + default: + dev_err(hsotg->dev, "FS PHY selected at HS!\n"); + break; + } + + if (usbcfg != usbcfg_old) { + dwc2_writel(hsotg, usbcfg, GUSBCFG); + + /* Reset after setting the PHY parameters */ + retval = dwc2_core_reset(hsotg, false); + if (retval) { + dev_err(hsotg->dev, + "%s: Reset failed, aborting", __func__); + return retval; + } + } + + return retval; +} + +int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) +{ + u32 usbcfg; + int retval = 0; + + if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL || + hsotg->params.speed == DWC2_SPEED_PARAM_LOW) && + hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* If FS/LS mode with FS/LS PHY */ + retval = dwc2_fs_phy_init(hsotg, select_phy); + if (retval) + return retval; + } else { + /* High speed PHY */ + retval = dwc2_hs_phy_init(hsotg, select_phy); + if (retval) + return retval; + } + + if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + hsotg->params.ulpi_fs_ls) { + dev_dbg(hsotg->dev, "Setting ULPI FSLS\n"); + usbcfg = dwc2_readl(hsotg, GUSBCFG); + usbcfg |= GUSBCFG_ULPI_FS_LS; + usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; + dwc2_writel(hsotg, usbcfg, GUSBCFG); + } else { + usbcfg = dwc2_readl(hsotg, GUSBCFG); + usbcfg &= ~GUSBCFG_ULPI_FS_LS; + usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; + dwc2_writel(hsotg, usbcfg, GUSBCFG); + } + + return retval; +} + MODULE_DESCRIPTION("DESIGNWARE HS OTG Core"); MODULE_AUTHOR("Synopsys, Inc."); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 30bab8463c96..152ac41dfb2d 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -859,6 +859,8 @@ struct dwc2_hregs_backup { * @gadget_enabled: Peripheral mode sub-driver initialization indicator. * @ll_hw_enabled: Status of low-level hardware resources. * @hibernated: True if core is hibernated + * @reset_phy_on_wake: Quirk saying that we should assert PHY reset on a + * remote wakeup. * @frame_number: Frame number read from the core. For both device * and host modes. The value ranges are from 0 * to HFNUM_MAX_FRNUM. @@ -869,7 +871,6 @@ struct dwc2_hregs_backup { * removed once all SoCs support usb transceiver. * @supplies: Definition of USB power supplies * @vbus_supply: Regulator supplying vbus. - * @phyif: PHY interface width * @lock: Spinlock that protects all the driver data structures * @priv: Stores a pointer to the struct usb_hcd * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth @@ -972,6 +973,7 @@ struct dwc2_hregs_backup { * @status_buf_dma: DMA address for status_buf * @start_work: Delayed work for handling host A-cable connection * @reset_work: Delayed work for handling a port reset + * @phy_reset_work: Work structure for doing a PHY reset * @otg_port: OTG port number * @frame_list: Frame list * @frame_list_dma: Frame list DMA address @@ -991,6 +993,7 @@ struct dwc2_hregs_backup { * @ctrl_buff: Buffer for EP0 control requests. * @ctrl_req: Request for EP0 control packets. * @ep0_state: EP0 control transfers state + * @delayed_status: true when gadget driver asks for delayed status * @test_mode: USB test mode requested by the host * @remote_wakeup_allowed: True if device is allowed to wake-up host by * remote-wakeup signalling @@ -1045,6 +1048,7 @@ struct dwc2_hsotg { unsigned int gadget_enabled:1; unsigned int ll_hw_enabled:1; unsigned int hibernated:1; + unsigned int reset_phy_on_wake:1; u16 frame_number; struct phy *phy; @@ -1052,7 +1056,6 @@ struct dwc2_hsotg { struct dwc2_hsotg_plat *plat; struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES]; struct regulator *vbus_supply; - u32 phyif; spinlock_t lock; void *priv; @@ -1147,6 +1150,7 @@ struct dwc2_hsotg { struct delayed_work start_work; struct delayed_work reset_work; + struct work_struct phy_reset_work; u8 otg_port; u32 *frame_list; dma_addr_t frame_list_dma; @@ -1172,6 +1176,7 @@ struct dwc2_hsotg { void *ep0_buff; void *ctrl_buff; enum dwc2_ep0_state ep0_state; + unsigned delayed_status : 1; u8 test_mode; dma_addr_t setup_desc_dma[2]; @@ -1283,6 +1288,8 @@ int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore); int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host); int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset, int is_host); +void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg); +int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy); void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host); void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg); @@ -1431,6 +1438,8 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset); +static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) +{ schedule_work(&hsotg->phy_reset_work); } #else static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { return 0; } @@ -1454,6 +1463,7 @@ static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, int reset) { return 0; } +static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {} #endif diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 19ae2595f1c3..6af6add3d4c0 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -435,6 +435,18 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Restart the Phy Clock */ pcgcctl &= ~PCGCTL_STOPPCLK; dwc2_writel(hsotg, pcgcctl, PCGCTL); + + /* + * If we've got this quirk then the PHY is stuck upon + * wakeup. Assert reset. This will propagate out and + * eventually we'll re-enumerate the device. Not great + * but the best we can do. We can't call phy_reset() + * at interrupt time but there's no hurry, so we'll + * schedule it for later. + */ + if (hsotg->reset_phy_on_wake) + dwc2_host_schedule_phy_reset(hsotg); + mod_timer(&hsotg->wkp_timer, jiffies + msecs_to_jiffies(71)); } else { diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 6812a8a3a98b..16ffd9fd9361 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -27,6 +27,8 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/phy.h> +#include <linux/usb/composite.h> + #include "core.h" #include "hw.h" @@ -714,13 +716,11 @@ static unsigned int dwc2_gadget_get_chain_limit(struct dwc2_hsotg_ep *hs_ep) unsigned int maxsize; if (is_isoc) - maxsize = hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_LIMIT : - DEV_DMA_ISOC_RX_NBYTES_LIMIT; + maxsize = (hs_ep->dir_in ? DEV_DMA_ISOC_TX_NBYTES_LIMIT : + DEV_DMA_ISOC_RX_NBYTES_LIMIT) * + MAX_DMA_DESC_NUM_HS_ISOC; else - maxsize = DEV_DMA_NBYTES_LIMIT; - - /* Above size of one descriptor was chosen, multiple it */ - maxsize *= MAX_DMA_DESC_NUM_GENERIC; + maxsize = DEV_DMA_NBYTES_LIMIT * MAX_DMA_DESC_NUM_GENERIC; return maxsize; } @@ -932,7 +932,7 @@ static int dwc2_gadget_fill_isoc_desc(struct dwc2_hsotg_ep *hs_ep, /* Update index of last configured entry in the chain */ hs_ep->next_desc++; - if (hs_ep->next_desc >= MAX_DMA_DESC_NUM_GENERIC) + if (hs_ep->next_desc >= MAX_DMA_DESC_NUM_HS_ISOC) hs_ep->next_desc = 0; return 0; @@ -964,7 +964,7 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep) } /* Initialize descriptor chain by Host Busy status */ - for (i = 0; i < MAX_DMA_DESC_NUM_GENERIC; i++) { + for (i = 0; i < MAX_DMA_DESC_NUM_HS_ISOC; i++) { desc = &hs_ep->desc_list[i]; desc->status = 0; desc->status |= (DEV_DMA_BUFF_STS_HBUSY @@ -1446,6 +1446,11 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, return 0; } + /* Change EP direction if status phase request is after data out */ + if (!hs_ep->index && !req->length && !hs_ep->dir_in && + hs->ep0_state == DWC2_EP0_DATA_OUT) + hs_ep->dir_in = 1; + if (first) { if (!hs_ep->isochronous) { dwc2_hsotg_start_req(hs, hs_ep, hs_req, false); @@ -1938,6 +1943,10 @@ static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg, dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret); } + hsotg->delayed_status = false; + if (ret == USB_GADGET_DELAYED_STATUS) + hsotg->delayed_status = true; + /* * the request is either unhandlable, or is not formatted correctly * so respond with a STALL for the status stage to indicate failure. @@ -2157,12 +2166,17 @@ static void dwc2_gadget_complete_isoc_request_ddma(struct dwc2_hsotg_ep *hs_ep) */ if (!hs_ep->dir_in && ureq->length & 0x3) ureq->actual += 4 - (ureq->length & 0x3); + + /* Set actual frame number for completed transfers */ + ureq->frame_number = + (desc_sts & DEV_DMA_ISOC_FRNUM_MASK) >> + DEV_DMA_ISOC_FRNUM_SHIFT; } dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); hs_ep->compl_desc++; - if (hs_ep->compl_desc > (MAX_DMA_DESC_NUM_GENERIC - 1)) + if (hs_ep->compl_desc > (MAX_DMA_DESC_NUM_HS_ISOC - 1)) hs_ep->compl_desc = 0; desc_sts = hs_ep->desc_list[hs_ep->compl_desc].status; } @@ -2311,6 +2325,7 @@ static unsigned int dwc2_gadget_get_xfersize_ddma(struct dwc2_hsotg_ep *hs_ep) if (status & DEV_DMA_STS_MASK) dev_err(hsotg->dev, "descriptor %d closed with %x\n", i, status & DEV_DMA_STS_MASK); + desc++; } return bytes_rem; @@ -2387,8 +2402,8 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) if (!using_desc_dma(hsotg) && epnum == 0 && hsotg->ep0_state == DWC2_EP0_DATA_OUT) { /* Move to STATUS IN */ - dwc2_hsotg_ep0_zlp(hsotg, true); - return; + if (!hsotg->delayed_status) + dwc2_hsotg_ep0_zlp(hsotg, true); } /* @@ -3053,8 +3068,20 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, /* Safety check EP0 state when STSPHSERCVD asserted */ if (hsotg->ep0_state == DWC2_EP0_DATA_OUT) { /* Move to STATUS IN for DDMA */ - if (using_desc_dma(hsotg)) - dwc2_hsotg_ep0_zlp(hsotg, true); + if (using_desc_dma(hsotg)) { + if (!hsotg->delayed_status) + dwc2_hsotg_ep0_zlp(hsotg, true); + else + /* In case of 3 stage Control Write with delayed + * status, when Status IN transfer started + * before STSPHSERCVD asserted, NAKSTS bit not + * cleared by CNAK in dwc2_hsotg_start_req() + * function. Clear now NAKSTS to allow complete + * transfer. + */ + dwc2_set_bit(hsotg, DIEPCTL(0), + DXEPCTL_CNAK); + } } } @@ -3314,21 +3341,14 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, /* keep other bits untouched (so e.g. forced modes are not lost) */ usbcfg = dwc2_readl(hsotg, GUSBCFG); - usbcfg &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP | - GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK); - - if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS && - (hsotg->params.speed == DWC2_SPEED_PARAM_FULL || - hsotg->params.speed == DWC2_SPEED_PARAM_LOW)) { - /* FS/LS Dedicated Transceiver Interface */ - usbcfg |= GUSBCFG_PHYSEL; - } else { - /* set the PLL on, remove the HNP/SRP and set the PHY */ - val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5; - usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) | - (val << GUSBCFG_USBTRDTIM_SHIFT); - } - dwc2_writel(hsotg, usbcfg, GUSBCFG); + usbcfg &= ~GUSBCFG_TOUTCAL_MASK; + usbcfg |= GUSBCFG_TOUTCAL(7); + + /* remove the HNP/SRP and set the PHY */ + usbcfg &= ~(GUSBCFG_SRPCAP | GUSBCFG_HNPCAP); + dwc2_writel(hsotg, usbcfg, GUSBCFG); + + dwc2_phy_init(hsotg, true); dwc2_hsotg_init_fifo(hsotg); @@ -3899,6 +3919,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, unsigned int i, val, size; int ret = 0; unsigned char ep_type; + int desc_num; dev_dbg(hsotg->dev, "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n", @@ -3945,11 +3966,15 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n", __func__, epctrl, epctrl_reg); + if (using_desc_dma(hsotg) && ep_type == USB_ENDPOINT_XFER_ISOC) + desc_num = MAX_DMA_DESC_NUM_HS_ISOC; + else + desc_num = MAX_DMA_DESC_NUM_GENERIC; + /* Allocate DMA descriptor chain for non-ctrl endpoints */ if (using_desc_dma(hsotg) && !hs_ep->desc_list) { hs_ep->desc_list = dmam_alloc_coherent(hsotg->dev, - MAX_DMA_DESC_NUM_GENERIC * - sizeof(struct dwc2_dma_desc), + desc_num * sizeof(struct dwc2_dma_desc), &hs_ep->desc_list_dma, GFP_ATOMIC); if (!hs_ep->desc_list) { ret = -ENOMEM; @@ -4092,7 +4117,7 @@ error1: error2: if (ret && using_desc_dma(hsotg) && hs_ep->desc_list) { - dmam_free_coherent(hsotg->dev, MAX_DMA_DESC_NUM_GENERIC * + dmam_free_coherent(hsotg->dev, desc_num * sizeof(struct dwc2_dma_desc), hs_ep->desc_list, hs_ep->desc_list_dma); hs_ep->desc_list = NULL; @@ -4328,8 +4353,6 @@ static const struct usb_ep_ops dwc2_hsotg_ep_ops = { */ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) { - u32 trdtim; - u32 usbcfg; /* unmask subset of endpoint interrupts */ dwc2_writel(hsotg, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | @@ -4353,17 +4376,6 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) dwc2_hsotg_init_fifo(hsotg); - /* keep other bits untouched (so e.g. forced modes are not lost) */ - usbcfg = dwc2_readl(hsotg, GUSBCFG); - usbcfg &= ~(GUSBCFG_TOUTCAL_MASK | GUSBCFG_PHYIF16 | GUSBCFG_SRPCAP | - GUSBCFG_HNPCAP | GUSBCFG_USBTRDTIM_MASK); - - /* set the PLL on, remove the HNP/SRP and set the PHY */ - trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5; - usbcfg |= hsotg->phyif | GUSBCFG_TOUTCAL(7) | - (trdtim << GUSBCFG_USBTRDTIM_SHIFT); - dwc2_writel(hsotg, usbcfg, GUSBCFG); - if (using_dma(hsotg)) dwc2_set_bit(hsotg, GAHBCFG, GAHBCFG_DMA_EN); } @@ -5073,6 +5085,7 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0; val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT; val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0; + val |= GLPMCFG_LPM_REJECT_CTRL_CONTROL; val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC; dwc2_writel(hsotg, val, GLPMCFG); dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG)); diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 3f087962f498..b50ec3714fd8 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -97,196 +97,6 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) dwc2_writel(hsotg, intmsk, GINTMSK); } -/* - * Initializes the FSLSPClkSel field of the HCFG register depending on the - * PHY type - */ -static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) -{ - u32 hcfg, val; - - if ((hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && - hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && - hsotg->params.ulpi_fs_ls) || - hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { - /* Full speed PHY */ - val = HCFG_FSLSPCLKSEL_48_MHZ; - } else { - /* High speed PHY running at full speed or high speed */ - val = HCFG_FSLSPCLKSEL_30_60_MHZ; - } - - dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val); - hcfg = dwc2_readl(hsotg, HCFG); - hcfg &= ~HCFG_FSLSPCLKSEL_MASK; - hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; - dwc2_writel(hsotg, hcfg, HCFG); -} - -static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) -{ - u32 usbcfg, ggpio, i2cctl; - int retval = 0; - - /* - * core_init() is now called on every switch so only call the - * following for the first time through - */ - if (select_phy) { - dev_dbg(hsotg->dev, "FS PHY selected\n"); - - usbcfg = dwc2_readl(hsotg, GUSBCFG); - if (!(usbcfg & GUSBCFG_PHYSEL)) { - usbcfg |= GUSBCFG_PHYSEL; - dwc2_writel(hsotg, usbcfg, GUSBCFG); - - /* Reset after a PHY select */ - retval = dwc2_core_reset(hsotg, false); - - if (retval) { - dev_err(hsotg->dev, - "%s: Reset failed, aborting", __func__); - return retval; - } - } - - if (hsotg->params.activate_stm_fs_transceiver) { - ggpio = dwc2_readl(hsotg, GGPIO); - if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { - dev_dbg(hsotg->dev, "Activating transceiver\n"); - /* - * STM32F4x9 uses the GGPIO register as general - * core configuration register. - */ - ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; - dwc2_writel(hsotg, ggpio, GGPIO); - } - } - } - - /* - * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also - * do this on HNP Dev/Host mode switches (done in dev_init and - * host_init). - */ - if (dwc2_is_host_mode(hsotg)) - dwc2_init_fs_ls_pclk_sel(hsotg); - - if (hsotg->params.i2c_enable) { - dev_dbg(hsotg->dev, "FS PHY enabling I2C\n"); - - /* Program GUSBCFG.OtgUtmiFsSel to I2C */ - usbcfg = dwc2_readl(hsotg, GUSBCFG); - usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; - dwc2_writel(hsotg, usbcfg, GUSBCFG); - - /* Program GI2CCTL.I2CEn */ - i2cctl = dwc2_readl(hsotg, GI2CCTL); - i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; - i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; - i2cctl &= ~GI2CCTL_I2CEN; - dwc2_writel(hsotg, i2cctl, GI2CCTL); - i2cctl |= GI2CCTL_I2CEN; - dwc2_writel(hsotg, i2cctl, GI2CCTL); - } - - return retval; -} - -static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) -{ - u32 usbcfg, usbcfg_old; - int retval = 0; - - if (!select_phy) - return 0; - - usbcfg = dwc2_readl(hsotg, GUSBCFG); - usbcfg_old = usbcfg; - - /* - * HS PHY parameters. These parameters are preserved during soft reset - * so only program the first time. Do a soft reset immediately after - * setting phyif. - */ - switch (hsotg->params.phy_type) { - case DWC2_PHY_TYPE_PARAM_ULPI: - /* ULPI interface */ - dev_dbg(hsotg->dev, "HS ULPI PHY selected\n"); - usbcfg |= GUSBCFG_ULPI_UTMI_SEL; - usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL); - if (hsotg->params.phy_ulpi_ddr) - usbcfg |= GUSBCFG_DDRSEL; - - /* Set external VBUS indicator as needed. */ - if (hsotg->params.oc_disable) - usbcfg |= (GUSBCFG_ULPI_INT_VBUS_IND | - GUSBCFG_INDICATORPASSTHROUGH); - break; - case DWC2_PHY_TYPE_PARAM_UTMI: - /* UTMI+ interface */ - dev_dbg(hsotg->dev, "HS UTMI+ PHY selected\n"); - usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); - if (hsotg->params.phy_utmi_width == 16) - usbcfg |= GUSBCFG_PHYIF16; - break; - default: - dev_err(hsotg->dev, "FS PHY selected at HS!\n"); - break; - } - - if (usbcfg != usbcfg_old) { - dwc2_writel(hsotg, usbcfg, GUSBCFG); - - /* Reset after setting the PHY parameters */ - retval = dwc2_core_reset(hsotg, false); - if (retval) { - dev_err(hsotg->dev, - "%s: Reset failed, aborting", __func__); - return retval; - } - } - - return retval; -} - -static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) -{ - u32 usbcfg; - int retval = 0; - - if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL || - hsotg->params.speed == DWC2_SPEED_PARAM_LOW) && - hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { - /* If FS/LS mode with FS/LS PHY */ - retval = dwc2_fs_phy_init(hsotg, select_phy); - if (retval) - return retval; - } else { - /* High speed PHY */ - retval = dwc2_hs_phy_init(hsotg, select_phy); - if (retval) - return retval; - } - - if (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && - hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && - hsotg->params.ulpi_fs_ls) { - dev_dbg(hsotg->dev, "Setting ULPI FSLS\n"); - usbcfg = dwc2_readl(hsotg, GUSBCFG); - usbcfg |= GUSBCFG_ULPI_FS_LS; - usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; - dwc2_writel(hsotg, usbcfg, GUSBCFG); - } else { - usbcfg = dwc2_readl(hsotg, GUSBCFG); - usbcfg &= ~GUSBCFG_ULPI_FS_LS; - usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; - dwc2_writel(hsotg, usbcfg, GUSBCFG); - } - - return retval; -} - static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg) { u32 ahbcfg = dwc2_readl(hsotg, GAHBCFG); @@ -2437,25 +2247,31 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) num_channels = hsotg->params.host_channels; for (i = 0; i < num_channels; i++) { hcchar = dwc2_readl(hsotg, HCCHAR(i)); - hcchar &= ~HCCHAR_CHENA; - hcchar |= HCCHAR_CHDIS; - hcchar &= ~HCCHAR_EPDIR; - dwc2_writel(hsotg, hcchar, HCCHAR(i)); + if (hcchar & HCCHAR_CHENA) { + hcchar &= ~HCCHAR_CHENA; + hcchar |= HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR; + dwc2_writel(hsotg, hcchar, HCCHAR(i)); + } } /* Halt all channels to put them into a known state */ for (i = 0; i < num_channels; i++) { hcchar = dwc2_readl(hsotg, HCCHAR(i)); - hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; - hcchar &= ~HCCHAR_EPDIR; - dwc2_writel(hsotg, hcchar, HCCHAR(i)); - dev_dbg(hsotg->dev, "%s: Halt channel %d\n", - __func__, i); - - if (dwc2_hsotg_wait_bit_clear(hsotg, HCCHAR(i), - HCCHAR_CHENA, 1000)) { - dev_warn(hsotg->dev, "Unable to clear enable on channel %d\n", - i); + if (hcchar & HCCHAR_CHENA) { + hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR; + dwc2_writel(hsotg, hcchar, HCCHAR(i)); + dev_dbg(hsotg->dev, "%s: Halt channel %d\n", + __func__, i); + + if (dwc2_hsotg_wait_bit_clear(hsotg, HCCHAR(i), + HCCHAR_CHENA, + 1000)) { + dev_warn(hsotg->dev, + "Unable to clear enable on channel %d\n", + i); + } } } } @@ -4376,6 +4192,17 @@ static void dwc2_hcd_reset_func(struct work_struct *work) spin_unlock_irqrestore(&hsotg->lock, flags); } +static void dwc2_hcd_phy_reset_func(struct work_struct *work) +{ + struct dwc2_hsotg *hsotg = container_of(work, struct dwc2_hsotg, + phy_reset_work); + int ret; + + ret = phy_reset(hsotg->phy); + if (ret) + dev_warn(hsotg->dev, "PHY reset failed\n"); +} + /* * ========================================================================= * Linux HC Driver Functions @@ -4471,6 +4298,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) unsigned long flags; int ret = 0; u32 hprt0; + u32 pcgctl; spin_lock_irqsave(&hsotg->lock, flags); @@ -4486,7 +4314,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) if (hsotg->op_state == OTG_STATE_B_PERIPHERAL) goto unlock; - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) + if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) goto skip_power_saving; /* @@ -4495,21 +4323,35 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) */ if (!hsotg->bus_suspended) { hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_SUSP; - hprt0 &= ~HPRT0_PWR; - dwc2_writel(hsotg, hprt0, HPRT0); - spin_unlock_irqrestore(&hsotg->lock, flags); - dwc2_vbus_supply_exit(hsotg); - spin_lock_irqsave(&hsotg->lock, flags); + if (hprt0 & HPRT0_CONNSTS) { + hprt0 |= HPRT0_SUSP; + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) + hprt0 &= ~HPRT0_PWR; + dwc2_writel(hsotg, hprt0, HPRT0); + } + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_vbus_supply_exit(hsotg); + spin_lock_irqsave(&hsotg->lock, flags); + } else { + pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + writel(pcgctl, hsotg->regs + PCGCTL); + } } - /* Enter partial_power_down */ - ret = dwc2_enter_partial_power_down(hsotg); - if (ret) { - if (ret != -ENOTSUPP) - dev_err(hsotg->dev, - "enter partial_power_down failed\n"); - goto skip_power_saving; + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { + /* Enter partial_power_down */ + ret = dwc2_enter_partial_power_down(hsotg); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "enter partial_power_down failed\n"); + goto skip_power_saving; + } + + /* After entering partial_power_down, hardware is no more accessible */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); } /* Ask phy to be suspended */ @@ -4519,9 +4361,6 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - /* After entering partial_power_down, hardware is no more accessible */ - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - skip_power_saving: hsotg->lx_state = DWC2_L2; unlock: @@ -4534,6 +4373,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) { struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; + u32 pcgctl; int ret = 0; spin_lock_irqsave(&hsotg->lock, flags); @@ -4544,18 +4384,12 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) if (hsotg->lx_state != DWC2_L2) goto unlock; - if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) { + if (hsotg->params.power_down > DWC2_POWER_DOWN_PARAM_PARTIAL) { hsotg->lx_state = DWC2_L0; goto unlock; } /* - * Set HW accessible bit before powering on the controller - * since an interrupt may rise. - */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - /* * Enable power if not already done. * This must not be spinlocked since duration * of this call is unknown. @@ -4566,10 +4400,23 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - /* Exit partial_power_down */ - ret = dwc2_exit_partial_power_down(hsotg, true); - if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit partial_power_down failed\n"); + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { + /* + * Set HW accessible bit before powering on the controller + * since an interrupt may rise. + */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + + /* Exit partial_power_down */ + ret = dwc2_exit_partial_power_down(hsotg, true); + if (ret && (ret != -ENOTSUPP)) + dev_err(hsotg->dev, "exit partial_power_down failed\n"); + } else { + pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + writel(pcgctl, hsotg->regs + PCGCTL); + } hsotg->lx_state = DWC2_L0; @@ -4581,10 +4428,12 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_unlock_irqrestore(&hsotg->lock, flags); dwc2_port_resume(hsotg); } else { - dwc2_vbus_supply_init(hsotg); + if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_PARTIAL) { + dwc2_vbus_supply_init(hsotg); - /* Wait for controller to correctly update D+/D- level */ - usleep_range(3000, 5000); + /* Wait for controller to correctly update D+/D- level */ + usleep_range(3000, 5000); + } /* * Clear Port Enable and Port Status changes. @@ -5130,6 +4979,8 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) destroy_workqueue(hsotg->wq_otg); } + cancel_work_sync(&hsotg->phy_reset_work); + del_timer(&hsotg->wkp_timer); } @@ -5271,11 +5122,10 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) hsotg->hc_ptr_array[i] = channel; } - /* Initialize hsotg start work */ + /* Initialize work */ INIT_DELAYED_WORK(&hsotg->start_work, dwc2_hcd_start_func); - - /* Initialize port reset work */ INIT_DELAYED_WORK(&hsotg->reset_work, dwc2_hcd_reset_func); + INIT_WORK(&hsotg->phy_reset_work, dwc2_hcd_phy_reset_func); /* * Allocate space for storing data on status transactions. Normally no diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 98af924a9a5c..510e87ec0be8 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -310,12 +310,12 @@ #define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) #define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 -#define GHWCFG4_ACG_SUPPORTED BIT(12) -#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) -#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10) #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 +#define GHWCFG4_ACG_SUPPORTED BIT(12) +#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) +#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10) #define GHWCFG4_XHIBER BIT(7) #define GHWCFG4_HIBER BIT(6) #define GHWCFG4_MIN_AHB_FREQ BIT(5) @@ -333,7 +333,7 @@ #define GLPMCFG_SNDLPM BIT(24) #define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) #define GLPMCFG_RETRY_CNT_SHIFT 21 -#define GLPMCFG_LPM_ACCEPT_CTRL_CONTROL BIT(21) +#define GLPMCFG_LPM_REJECT_CTRL_CONTROL BIT(21) #define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22) #define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) #define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 24ff5f21cb25..6900eea57526 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -121,6 +121,16 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg) p->power_down = DWC2_POWER_DOWN_PARAM_NONE; } +static void dwc2_set_amlogic_g12a_params(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->lpm = false; + p->lpm_clock_gating = false; + p->besl = false; + p->hird_threshold_en = false; +} + static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg) { struct dwc2_core_params *p = &hsotg->params; @@ -167,6 +177,8 @@ const struct of_device_id dwc2_of_match_table[] = { .data = dwc2_set_amlogic_params }, { .compatible = "amlogic,meson-gxbb-usb", .data = dwc2_set_amlogic_params }, + { .compatible = "amlogic,meson-g12a-usb", + .data = dwc2_set_amlogic_g12a_params }, { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params }, { .compatible = "st,stm32f4x9-fsotg", .data = dwc2_set_stm32f4x9_fsotg_params }, @@ -273,6 +285,23 @@ static void dwc2_set_param_power_down(struct dwc2_hsotg *hsotg) hsotg->params.power_down = val; } +static void dwc2_set_param_lpm(struct dwc2_hsotg *hsotg) +{ + struct dwc2_core_params *p = &hsotg->params; + + p->lpm = hsotg->hw_params.lpm_mode; + if (p->lpm) { + p->lpm_clock_gating = true; + p->besl = true; + p->hird_threshold_en = true; + p->hird_threshold = 4; + } else { + p->lpm_clock_gating = false; + p->besl = false; + p->hird_threshold_en = false; + } +} + /** * dwc2_set_default_params() - Set all core parameters to their * auto-detected default values. @@ -291,6 +320,7 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg) dwc2_set_param_speed(hsotg); dwc2_set_param_phy_utmi_width(hsotg); dwc2_set_param_power_down(hsotg); + dwc2_set_param_lpm(hsotg); p->phy_ulpi_ddr = false; p->phy_ulpi_ext_vbus = false; @@ -303,11 +333,6 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg) p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a); p->uframe_sched = true; p->external_id_pin_ctl = false; - p->lpm = true; - p->lpm_clock_gating = true; - p->besl = true; - p->hird_threshold_en = true; - p->hird_threshold = 4; p->ipg_isoc_en = false; p->service_interval = false; p->max_packet_count = hw->max_packet_count; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index c0b64d483552..d10a7f8daec3 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -230,9 +230,6 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) reset_control_deassert(hsotg->reset_ecc); - /* Set default UTMI width */ - hsotg->phyif = GUSBCFG_PHYIF16; - /* * Attempt to find a generic PHY, then look for an old style * USB PHY and then fall back to pdata @@ -280,14 +277,14 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) * width is 8-bit and set the phyif appropriately. */ if (phy_get_bus_width(hsotg->phy) == 8) - hsotg->phyif = GUSBCFG_PHYIF8; + hsotg->params.phy_utmi_width = 8; } /* Clock */ - hsotg->clk = devm_clk_get(hsotg->dev, "otg"); + hsotg->clk = devm_clk_get_optional(hsotg->dev, "otg"); if (IS_ERR(hsotg->clk)) { - hsotg->clk = NULL; - dev_dbg(hsotg->dev, "cannot get otg clock\n"); + dev_err(hsotg->dev, "cannot get otg clock\n"); + return PTR_ERR(hsotg->clk); } /* Regulators */ @@ -481,6 +478,15 @@ static int dwc2_driver_probe(struct platform_device *dev) hsotg->gadget_enabled = 1; } + hsotg->reset_phy_on_wake = + of_property_read_bool(dev->dev.of_node, + "snps,reset-phy-on-wake"); + if (hsotg->reset_phy_on_wake && !hsotg->phy) { + dev_warn(hsotg->dev, + "Quirk reset-phy-on-wake only supports generic PHYs\n"); + hsotg->reset_phy_on_wake = false; + } + if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { retval = dwc2_hcd_init(hsotg); if (retval) { diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 2b1494460d0c..4a62045cc812 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -54,7 +54,8 @@ comment "Platform Glue Driver Support" config USB_DWC3_OMAP tristate "Texas Instruments OMAP5 and similar Platforms" - depends on EXTCON && (ARCH_OMAP2PLUS || COMPILE_TEST) + depends on ARCH_OMAP2PLUS || COMPILE_TEST + depends on EXTCON || !EXTCON depends on OF default USB_DWC3 help @@ -95,6 +96,16 @@ config USB_DWC3_KEYSTONE Support of USB2/3 functionality in TI Keystone2 and AM654 platforms. Say 'Y' or 'M' here if you have one such device +config USB_DWC3_MESON_G12A + tristate "Amlogic Meson G12A Platforms" + depends on OF && COMMON_CLK + depends on ARCH_MESON || COMPILE_TEST + default USB_DWC3 + select USB_ROLE_SWITCH + help + Support USB2/3 functionality in Amlogic G12A platforms. + Say 'Y' or 'M' if you have one such device. + config USB_DWC3_OF_SIMPLE tristate "Generic OF Simple Glue Layer" depends on OF && COMMON_CLK @@ -115,7 +126,8 @@ config USB_DWC3_ST config USB_DWC3_QCOM tristate "Qualcomm Platform" - depends on EXTCON && (ARCH_QCOM || COMPILE_TEST) + depends on ARCH_QCOM || COMPILE_TEST + depends on EXTCON || !EXTCON depends on OF default USB_DWC3 help diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 6e3ef6144e5d..ae86da0dc5bd 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o obj-$(CONFIG_USB_DWC3_HAPS) += dwc3-haps.o obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o +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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a1b126f90261..4aff1d8dbc4f 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -828,6 +828,7 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) ret = device_property_read_u32_array(dev, "snps,incr-burst-type-adjustment", vals, ntype); if (ret) { + kfree(vals); dev_err(dev, "Error to get property\n"); return; } @@ -846,6 +847,8 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) incrx_mode = INCRX_BURST_MODE; } + kfree(vals); + /* Enable Undefined Length INCR Burst and Enable INCRx Burst */ cfg &= ~DWC3_GSBUSCFG0_INCRBRST_MASK; if (incrx_mode) @@ -893,12 +896,6 @@ static int dwc3_core_init(struct dwc3 *dwc) u32 reg; int ret; - if (!dwc3_core_is_valid(dwc)) { - dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); - ret = -ENODEV; - goto err0; - } - /* * Write Linux Version Code to our GUID register so it's easy to figure * out which kernel version a bug was found. @@ -1218,7 +1215,7 @@ static void dwc3_get_properties(struct dwc3 *dwc) u8 tx_max_burst_prd; /* default to highest possible threshold */ - lpm_nyet_threshold = 0xff; + lpm_nyet_threshold = 0xf; /* default to -3.5dB de-emphasis */ tx_de_emphasis = 1; @@ -1426,6 +1423,11 @@ static int dwc3_probe(struct platform_device *pdev) dwc->regs = regs; dwc->regs_size = resource_size(&dwc_res); + if (!dwc3_core_is_valid(dwc)) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + return -ENODEV; + } + dwc3_get_properties(dwc); dwc->reset = devm_reset_control_get_optional_shared(dev, NULL); @@ -1600,6 +1602,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_suspend(dwc); spin_unlock_irqrestore(&dwc->lock, flags); + synchronize_irq(dwc->irq_gadget); dwc3_core_exit(dwc); break; case DWC3_GCTL_PRTCAP_HOST: @@ -1632,6 +1635,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_suspend(dwc); spin_unlock_irqrestore(&dwc->lock, flags); + synchronize_irq(dwc->irq_gadget); } dwc3_otg_exit(dwc); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 1528d395b156..f19cbeb01087 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -406,8 +406,7 @@ #define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) /* These apply for core versions 1.94a and later */ -#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf) -#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20) +#define DWC3_DCTL_NYET_THRES(n) (((n) & 0xf) << 20) #define DWC3_DCTL_KEEP_CONNECT BIT(19) #define DWC3_DCTL_L1_HIBER_EN BIT(18) diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 6759a7efd8d5..068259fdfb0c 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -250,6 +250,9 @@ static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str, size_t size) { switch (t & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + snprintf(str, size, "Get Device Status(Length = %d)", l); + break; case USB_RECIP_INTERFACE: snprintf(str, size, "Get Interface Status(Intf = %d, Length = %d)", i, l); diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c new file mode 100644 index 000000000000..2aec31a2eacb --- /dev/null +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -0,0 +1,604 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Glue for Amlogic G12A SoCs + * + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +/* + * The USB is organized with a glue around the DWC3 Controller IP as : + * - Control registers for each USB2 Ports + * - Control registers for the USB PHY layer + * - SuperSpeed PHY can be enabled only if port is used + * + * TOFIX: + * - Add dynamic OTG switching with ID change interrupt + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/reset.h> +#include <linux/phy/phy.h> +#include <linux/usb/otg.h> +#include <linux/usb/role.h> +#include <linux/regulator/consumer.h> + +/* USB2 Ports Control Registers */ + +#define U2P_REG_SIZE 0x20 + +#define U2P_R0 0x0 + #define U2P_R0_HOST_DEVICE BIT(0) + #define U2P_R0_POWER_OK BIT(1) + #define U2P_R0_HAST_MODE BIT(2) + #define U2P_R0_POWER_ON_RESET BIT(3) + #define U2P_R0_ID_PULLUP BIT(4) + #define U2P_R0_DRV_VBUS BIT(5) + +#define U2P_R1 0x4 + #define U2P_R1_PHY_READY BIT(0) + #define U2P_R1_ID_DIG BIT(1) + #define U2P_R1_OTG_SESSION_VALID BIT(2) + #define U2P_R1_VBUS_VALID BIT(3) + +/* USB Glue Control Registers */ + +#define USB_R0 0x80 + #define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17) + #define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18) + #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19) + #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29) + #define USB_R0_U2D_ACT BIT(31) + +#define USB_R1 0x84 + #define USB_R1_U3H_BIGENDIAN_GS BIT(0) + #define USB_R1_U3H_PME_ENABLE BIT(1) + #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2) + #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK GENMASK(9, 7) + #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK GENMASK(13, 12) + #define USB_R1_U3H_HOST_U3_PORT_DISABLE BIT(16) + #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT BIT(17) + #define USB_R1_U3H_HOST_MSI_ENABLE BIT(18) + #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19) + #define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25) + +#define USB_R2 0x88 + #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20) + #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26) + +#define USB_R3 0x8c + #define USB_R3_P30_SSC_ENABLE BIT(0) + #define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1) + #define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4) + #define USB_R3_P30_REF_SSP_EN BIT(13) + +#define USB_R4 0x90 + #define USB_R4_P21_PORT_RESET_0 BIT(0) + #define USB_R4_P21_SLEEP_M0 BIT(1) + #define USB_R4_MEM_PD_MASK GENMASK(3, 2) + #define USB_R4_P21_ONLY BIT(4) + +#define USB_R5 0x94 + #define USB_R5_ID_DIG_SYNC BIT(0) + #define USB_R5_ID_DIG_REG BIT(1) + #define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2) + #define USB_R5_ID_DIG_EN_0 BIT(4) + #define USB_R5_ID_DIG_EN_1 BIT(5) + #define USB_R5_ID_DIG_CURR BIT(6) + #define USB_R5_ID_DIG_IRQ BIT(7) + #define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8) + #define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16) + +enum { + USB2_HOST_PHY = 0, + USB2_OTG_PHY, + USB3_HOST_PHY, + PHY_COUNT, +}; + +static const char *phy_names[PHY_COUNT] = { + "usb2-phy0", "usb2-phy1", "usb3-phy0", +}; + +struct dwc3_meson_g12a { + struct device *dev; + struct regmap *regmap; + struct clk *clk; + struct reset_control *reset; + struct phy *phys[PHY_COUNT]; + enum usb_dr_mode otg_mode; + enum phy_mode otg_phy_mode; + unsigned int usb2_ports; + unsigned int usb3_ports; + struct regulator *vbus; + struct usb_role_switch_desc switch_desc; + struct usb_role_switch *role_switch; +}; + +static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv, + int i, enum phy_mode mode) +{ + if (mode == PHY_MODE_USB_HOST) + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, + U2P_R0_HOST_DEVICE); + else + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_HOST_DEVICE, 0); +} + +static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv) +{ + int i; + + if (priv->otg_mode == USB_DR_MODE_PERIPHERAL) + priv->otg_phy_mode = PHY_MODE_USB_DEVICE; + else + priv->otg_phy_mode = PHY_MODE_USB_HOST; + + for (i = 0 ; i < USB3_HOST_PHY ; ++i) { + if (!priv->phys[i]) + continue; + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, + U2P_R0_POWER_ON_RESET); + + if (i == USB2_OTG_PHY) { + regmap_update_bits(priv->regmap, + U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, + U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS); + + dwc3_meson_g12a_usb2_set_mode(priv, i, + priv->otg_phy_mode); + } else + dwc3_meson_g12a_usb2_set_mode(priv, i, + PHY_MODE_USB_HOST); + + regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i), + U2P_R0_POWER_ON_RESET, 0); + } + + return 0; +} + +static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv) +{ + regmap_update_bits(priv->regmap, USB_R3, + USB_R3_P30_SSC_RANGE_MASK | + USB_R3_P30_REF_SSP_EN, + USB_R3_P30_SSC_ENABLE | + FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) | + USB_R3_P30_REF_SSP_EN); + udelay(2); + + regmap_update_bits(priv->regmap, USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15)); + + regmap_update_bits(priv->regmap, USB_R2, + USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, + FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20)); + + udelay(2); + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT, + USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT); + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_P30_PCS_TX_SWING_FULL_MASK, + FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127)); +} + +static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv) +{ + if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, USB_R0_U2D_ACT); + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0); + } else { + regmap_update_bits(priv->regmap, USB_R0, + USB_R0_U2D_ACT, 0); + regmap_update_bits(priv->regmap, USB_R4, + USB_R4_P21_SLEEP_M0, 0); + } +} + +static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv) +{ + int ret; + + ret = dwc3_meson_g12a_usb2_init(priv); + if (ret) + return ret; + + regmap_update_bits(priv->regmap, USB_R1, + USB_R1_U3H_FLADJ_30MHZ_REG_MASK, + FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20)); + + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_0, + USB_R5_ID_DIG_EN_0); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_EN_1, + USB_R5_ID_DIG_EN_1); + regmap_update_bits(priv->regmap, USB_R5, + USB_R5_ID_DIG_TH_MASK, + FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff)); + + /* If we have an actual SuperSpeed port, initialize it */ + if (priv->usb3_ports) + dwc3_meson_g12a_usb3_init(priv); + + dwc3_meson_g12a_usb_otg_apply_mode(priv); + + return 0; +} + +static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = USB_R5, +}; + +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv) +{ + int i; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]); + if (!priv->phys[i]) + continue; + + if (IS_ERR(priv->phys[i])) + return PTR_ERR(priv->phys[i]); + + if (i == USB3_HOST_PHY) + priv->usb3_ports++; + else + priv->usb2_ports++; + } + + dev_info(priv->dev, "USB2 ports: %d\n", priv->usb2_ports); + dev_info(priv->dev, "USB3 ports: %d\n", priv->usb3_ports); + + return 0; +} + +static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv) +{ + u32 reg; + + regmap_read(priv->regmap, USB_R5, ®); + + if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG)) + return PHY_MODE_USB_DEVICE; + + return PHY_MODE_USB_HOST; +} + +static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, + enum phy_mode mode) +{ + int ret; + + if (!priv->phys[USB2_OTG_PHY]) + return -EINVAL; + + if (mode == PHY_MODE_USB_HOST) + dev_info(priv->dev, "switching to Host Mode\n"); + else + dev_info(priv->dev, "switching to Device Mode\n"); + + if (priv->vbus) { + if (mode == PHY_MODE_USB_DEVICE) + ret = regulator_disable(priv->vbus); + else + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + } + + priv->otg_phy_mode = mode; + + dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode); + + dwc3_meson_g12a_usb_otg_apply_mode(priv); + + return 0; +} + +static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + enum phy_mode mode; + + if (role == USB_ROLE_NONE) + return 0; + + mode = (role == USB_ROLE_HOST) ? PHY_MODE_USB_HOST + : PHY_MODE_USB_DEVICE; + + if (mode == priv->otg_phy_mode) + return 0; + + return dwc3_meson_g12a_otg_mode_set(priv, mode); +} + +static enum usb_role dwc3_meson_g12a_role_get(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + return priv->otg_phy_mode == PHY_MODE_USB_HOST ? + USB_ROLE_HOST : USB_ROLE_DEVICE; +} + +static struct device *dwc3_meson_g12_find_child(struct device *dev, + const char *compatible) +{ + struct platform_device *pdev; + struct device_node *np; + + np = of_get_compatible_child(dev->of_node, compatible); + if (!np) + return NULL; + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (!pdev) + return NULL; + + return &pdev->dev; +} + +static int dwc3_meson_g12a_probe(struct platform_device *pdev) +{ + struct dwc3_meson_g12a *priv; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *base; + struct resource *res; + enum phy_mode otg_id; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap = devm_regmap_init_mmio(dev, base, + &phy_meson_g12a_usb3_regmap_conf); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->vbus = devm_regulator_get_optional(dev, "vbus"); + if (IS_ERR(priv->vbus)) { + if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) + return PTR_ERR(priv->vbus); + priv->vbus = NULL; + } + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + devm_add_action_or_reset(dev, + (void(*)(void *))clk_disable_unprepare, + priv->clk); + + platform_set_drvdata(pdev, priv); + priv->dev = dev; + + priv->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(priv->reset)) { + ret = PTR_ERR(priv->reset); + dev_err(dev, "failed to get device reset, err=%d\n", ret); + return ret; + } + + ret = reset_control_reset(priv->reset); + if (ret) + return ret; + + ret = dwc3_meson_g12a_get_phys(priv); + if (ret) + return ret; + + if (priv->vbus) { + ret = regulator_enable(priv->vbus); + if (ret) + return ret; + } + + /* Get dr_mode */ + priv->otg_mode = usb_get_dr_mode(dev); + + dwc3_meson_g12a_usb_init(priv); + + /* Init PHYs */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_init(priv->phys[i]); + if (ret) + return ret; + } + + /* Set PHY Power */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_power_on(priv->phys[i]); + if (ret) + goto err_phys_exit; + } + + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret) { + clk_disable_unprepare(priv->clk); + goto err_phys_power; + } + + /* Setup OTG mode corresponding to the ID pin */ + if (priv->otg_mode == USB_DR_MODE_OTG) { + /* TOFIX Handle ID mode toggling via IRQ */ + otg_id = dwc3_meson_g12a_get_id(priv); + if (otg_id != priv->otg_phy_mode) { + if (dwc3_meson_g12a_otg_mode_set(priv, otg_id)) + dev_warn(dev, "Failed to switch OTG mode\n"); + } + } + + /* Setup role switcher */ + priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev, + "snps,dwc3"); + priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2"); + priv->switch_desc.allow_userspace_control = true; + priv->switch_desc.set = dwc3_meson_g12a_role_set; + priv->switch_desc.get = dwc3_meson_g12a_role_get; + + priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); + if (IS_ERR(priv->role_switch)) + dev_warn(dev, "Unable to register Role Switch\n"); + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + + return 0; + +err_phys_power: + for (i = 0 ; i < PHY_COUNT ; ++i) + phy_power_off(priv->phys[i]); + +err_phys_exit: + for (i = 0 ; i < PHY_COUNT ; ++i) + phy_exit(priv->phys[i]); + + return ret; +} + +static int dwc3_meson_g12a_remove(struct platform_device *pdev) +{ + struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int i; + + usb_role_switch_unregister(priv->role_switch); + + of_platform_depopulate(dev); + + for (i = 0 ; i < PHY_COUNT ; ++i) { + phy_power_off(priv->phys[i]); + phy_exit(priv->phys[i]); + } + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + clk_disable(priv->clk); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + + return clk_enable(priv->clk); +} + +static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + int i; + + for (i = 0 ; i < PHY_COUNT ; ++i) { + phy_power_off(priv->phys[i]); + phy_exit(priv->phys[i]); + } + + reset_control_assert(priv->reset); + + return 0; +} + +static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev) +{ + struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + int i, ret; + + reset_control_deassert(priv->reset); + + dwc3_meson_g12a_usb_init(priv); + + /* Init PHYs */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_init(priv->phys[i]); + if (ret) + return ret; + } + + /* Set PHY Power */ + for (i = 0 ; i < PHY_COUNT ; ++i) { + ret = phy_power_on(priv->phys[i]); + if (ret) + return ret; + } + + return 0; +} + +static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume) + SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend, + dwc3_meson_g12a_runtime_resume, NULL) +}; + +static const struct of_device_id dwc3_meson_g12a_match[] = { + { .compatible = "amlogic,meson-g12a-usb-ctrl" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); + +static struct platform_driver dwc3_meson_g12a_driver = { + .probe = dwc3_meson_g12a_probe, + .remove = dwc3_meson_g12a_remove, + .driver = { + .name = "dwc3-meson-g12a", + .of_match_table = dwc3_meson_g12a_match, + .pm = &dwc3_meson_g12a_dev_pm_ops, + }, +}; + +module_platform_driver(dwc3_meson_g12a_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer"); +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index 4c2771c5e727..c4da82dd15c7 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -24,59 +24,13 @@ struct dwc3_of_simple { struct device *dev; - struct clk **clks; + struct clk_bulk_data *clks; int num_clocks; struct reset_control *resets; bool pulse_resets; bool need_reset; }; -static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) -{ - struct device *dev = simple->dev; - struct device_node *np = dev->of_node; - int i; - - simple->num_clocks = count; - - if (!count) - return 0; - - simple->clks = devm_kcalloc(dev, simple->num_clocks, - sizeof(struct clk *), GFP_KERNEL); - if (!simple->clks) - return -ENOMEM; - - for (i = 0; i < simple->num_clocks; i++) { - struct clk *clk; - int ret; - - clk = of_clk_get(np, i); - if (IS_ERR(clk)) { - while (--i >= 0) { - clk_disable_unprepare(simple->clks[i]); - clk_put(simple->clks[i]); - } - return PTR_ERR(clk); - } - - ret = clk_prepare_enable(clk); - if (ret < 0) { - while (--i >= 0) { - clk_disable_unprepare(simple->clks[i]); - clk_put(simple->clks[i]); - } - clk_put(clk); - - return ret; - } - - simple->clks[i] = clk; - } - - return 0; -} - static int dwc3_of_simple_probe(struct platform_device *pdev) { struct dwc3_of_simple *simple; @@ -84,7 +38,6 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; int ret; - int i; bool shared_resets = false; simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL); @@ -124,20 +77,18 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) goto err_resetc_put; } - ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np, - "clocks", "#clock-cells")); + ret = clk_bulk_get_all(simple->dev, &simple->clks); + if (ret < 0) + goto err_resetc_assert; + + simple->num_clocks = ret; + ret = clk_bulk_prepare_enable(simple->num_clocks, simple->clks); if (ret) goto err_resetc_assert; ret = of_platform_populate(np, NULL, NULL, dev); - if (ret) { - for (i = 0; i < simple->num_clocks; i++) { - clk_disable_unprepare(simple->clks[i]); - clk_put(simple->clks[i]); - } - - goto err_resetc_assert; - } + if (ret) + goto err_clk_put; pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -145,6 +96,10 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) return 0; +err_clk_put: + clk_bulk_disable_unprepare(simple->num_clocks, simple->clks); + clk_bulk_put_all(simple->num_clocks, simple->clks); + err_resetc_assert: if (!simple->pulse_resets) reset_control_assert(simple->resets); @@ -158,14 +113,11 @@ static int dwc3_of_simple_remove(struct platform_device *pdev) { struct dwc3_of_simple *simple = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - int i; of_platform_depopulate(dev); - for (i = 0; i < simple->num_clocks; i++) { - clk_disable_unprepare(simple->clks[i]); - clk_put(simple->clks[i]); - } + clk_bulk_disable_unprepare(simple->num_clocks, simple->clks); + clk_bulk_put_all(simple->num_clocks, simple->clks); simple->num_clocks = 0; if (!simple->pulse_resets) @@ -183,10 +135,8 @@ static int dwc3_of_simple_remove(struct platform_device *pdev) static int __maybe_unused dwc3_of_simple_runtime_suspend(struct device *dev) { struct dwc3_of_simple *simple = dev_get_drvdata(dev); - int i; - for (i = 0; i < simple->num_clocks; i++) - clk_disable(simple->clks[i]); + clk_bulk_disable(simple->num_clocks, simple->clks); return 0; } @@ -194,19 +144,8 @@ static int __maybe_unused dwc3_of_simple_runtime_suspend(struct device *dev) static int __maybe_unused dwc3_of_simple_runtime_resume(struct device *dev) { struct dwc3_of_simple *simple = dev_get_drvdata(dev); - int ret; - int i; - - for (i = 0; i < simple->num_clocks; i++) { - ret = clk_enable(simple->clks[i]); - if (ret < 0) { - while (--i >= 0) - clk_disable(simple->clks[i]); - return ret; - } - } - return 0; + return clk_bulk_enable(simple->num_clocks, simple->clks); } static int __maybe_unused dwc3_of_simple_suspend(struct device *dev) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e293400cc6e9..d67655384eb2 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2863,7 +2863,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) "LPM Erratum not available on dwc3 revisions < 2.40a\n"); if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A) - reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold); + reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold); dwc3_writel(dwc->regs, DWC3_DCTL, reg); } else { @@ -3301,6 +3301,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) dwc->gadget.sg_supported = true; dwc->gadget.name = "dwc3-gadget"; dwc->gadget.is_otg = dwc->dr_mode == USB_DR_MODE_OTG; + dwc->gadget.lpm_capable = true; /* * FIXME We might be setting max_speed to <SUPER, however versions @@ -3384,8 +3385,6 @@ int dwc3_gadget_suspend(struct dwc3 *dwc) dwc3_disconnect_gadget(dwc); __dwc3_gadget_stop(dwc); - synchronize_irq(dwc->irq_gadget); - return 0; } diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 20413c276c61..47be961f1bf3 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1133,7 +1133,8 @@ error_lock: error_mutex: mutex_unlock(&epfile->mutex); error: - ffs_free_buffer(io_data); + if (ret != -EIOCBQUEUED) /* don't free if there is iocb queued */ + ffs_free_buffer(io_data); return ret; } diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 5780fba620ab..2d6e76e4cffa 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -23,6 +23,7 @@ #include "u_ether.h" #include "u_ether_configfs.h" #include "u_ncm.h" +#include "configfs.h" /* * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link. @@ -35,9 +36,7 @@ /* to trigger crc/non-crc ndp signature */ -#define NCM_NDP_HDR_CRC_MASK 0x01000000 #define NCM_NDP_HDR_CRC 0x01000000 -#define NCM_NDP_HDR_NOCRC 0x00000000 enum ncm_notify_state { NCM_NOTIFY_NONE, /* don't notify */ @@ -526,6 +525,7 @@ static inline void ncm_reset_values(struct f_ncm *ncm) { ncm->parser_opts = &ndp16_opts; ncm->is_crc = false; + ncm->ndp_sign = ncm->parser_opts->ndp_sign; ncm->port.cdc_filter = DEFAULT_FILTER; /* doesn't make sense for ncm, fixed size used */ @@ -805,25 +805,20 @@ static int ncm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_SET_CRC_MODE: { - int ndp_hdr_crc = 0; - if (w_length != 0 || w_index != ncm->ctrl_id) goto invalid; switch (w_value) { case 0x0000: ncm->is_crc = false; - ndp_hdr_crc = NCM_NDP_HDR_NOCRC; DBG(cdev, "non-CRC mode selected\n"); break; case 0x0001: ncm->is_crc = true; - ndp_hdr_crc = NCM_NDP_HDR_CRC; DBG(cdev, "CRC mode selected\n"); break; default: goto invalid; } - ncm->ndp_sign = ncm->parser_opts->ndp_sign | ndp_hdr_crc; value = 0; break; } @@ -840,6 +835,8 @@ invalid: ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); } + ncm->ndp_sign = ncm->parser_opts->ndp_sign | + (ncm->is_crc ? NCM_NDP_HDR_CRC : 0); /* respond with data transfer or status phase? */ if (value >= 0) { @@ -1395,6 +1392,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) 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) + return -ENOMEM; + f->os_desc_n = 1; + f->os_desc_table[0].os_desc = &ncm_opts->ncm_os_desc; + } + /* * in drivers/usb/gadget/configfs.c:configfs_composite_bind() * configurations are bound in sequence with list_for_each_entry, @@ -1408,13 +1415,15 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) status = gether_register_netdev(ncm_opts->net); mutex_unlock(&ncm_opts->lock); if (status) - return status; + goto fail; ncm_opts->bound = true; } us = usb_gstrings_attach(cdev, ncm_strings, ARRAY_SIZE(ncm_string_defs)); - if (IS_ERR(us)) - return PTR_ERR(us); + if (IS_ERR(us)) { + status = PTR_ERR(us); + goto fail; + } 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; @@ -1431,6 +1440,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) 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; @@ -1510,6 +1523,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) 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); @@ -1564,16 +1580,22 @@ static void ncm_free_inst(struct usb_function_instance *f) gether_cleanup(netdev_priv(opts->net)); else free_netdev(opts->net); + kfree(opts->ncm_interf_group); kfree(opts); } static struct usb_function_instance *ncm_alloc_inst(void) { struct f_ncm_opts *opts; + struct usb_os_desc *descs[1]; + char *names[1]; + struct config_group *ncm_interf_group; opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return ERR_PTR(-ENOMEM); + opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id; + mutex_init(&opts->lock); opts->func_inst.free_func_inst = ncm_free_inst; opts->net = gether_setup_default(); @@ -1582,8 +1604,20 @@ static struct usb_function_instance *ncm_alloc_inst(void) kfree(opts); return ERR_CAST(net); } + INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop); + + descs[0] = &opts->ncm_os_desc; + names[0] = "ncm"; config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type); + ncm_interf_group = + usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, + names, THIS_MODULE); + if (IS_ERR(ncm_interf_group)) { + ncm_free_inst(&opts->func_inst); + return ERR_CAST(ncm_interf_group); + } + opts->ncm_interf_group = ncm_interf_group; return &opts->func_inst; } @@ -1609,6 +1643,9 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) hrtimer_cancel(&ncm->task_timer); + kfree(f->os_desc_table); + f->os_desc_n = 0; + ncm_string_defs[0].id = 0; usb_free_all_descriptors(f); diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c index 24c086bcdeaa..6677ae932de0 100644 --- a/drivers/usb/gadget/function/f_uac1_legacy.c +++ b/drivers/usb/gadget/function/f_uac1_legacy.c @@ -54,8 +54,8 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc = { .bLength = UAC_DT_AC_HEADER_LENGTH, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_HEADER, - .bcdADC = __constant_cpu_to_le16(0x0100), - .wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH), + .bcdADC = cpu_to_le16(0x0100), + .wTotalLength = cpu_to_le16(UAC_DT_TOTAL_LENGTH), .bInCollection = F_AUDIO_NUM_INTERFACES, .baInterfaceNr = { /* Interface number of the first AudioStream interface */ @@ -183,7 +183,7 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = { .bDescriptorSubtype = UAC_EP_GENERAL, .bmAttributes = 1, .bLockDelayUnits = 1, - .wLockDelay = __constant_cpu_to_le16(1), + .wLockDelay = cpu_to_le16(1), }; static struct usb_descriptor_header *f_audio_desc[] = { diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index d483e45c0f77..70da3201a1d0 100644 --- a/drivers/usb/gadget/function/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h @@ -20,6 +20,9 @@ struct f_ncm_opts { struct net_device *net; bool bound; + 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. * diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c index 83340f4fdc6e..35941dc125f9 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c @@ -593,10 +593,6 @@ static int ast_vhub_epn_disable(struct usb_ep* u_ep) static int ast_vhub_epn_enable(struct usb_ep* u_ep, const struct usb_endpoint_descriptor *desc) { - static const char *ep_type_string[] __maybe_unused = { "ctrl", - "isoc", - "bulk", - "intr" }; struct ast_vhub_ep *ep = to_ast_ep(u_ep); struct ast_vhub_dev *dev; struct ast_vhub *vhub; @@ -646,7 +642,7 @@ static int ast_vhub_epn_enable(struct usb_ep* u_ep, ep->epn.wedged = false; EPDBG(ep, "Enabling [%s] %s num %d maxpacket=%d\n", - ep->epn.is_in ? "in" : "out", ep_type_string[type], + ep->epn.is_in ? "in" : "out", usb_ep_type_string(type), usb_endpoint_num(desc), maxpacket); /* Can we use DMA descriptor mode ? */ diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 660712e0bf98..503d275bc4c4 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -358,8 +358,20 @@ static inline u32 usba_int_enb_get(struct usba_udc *udc) return udc->int_enb_cache; } -static inline void usba_int_enb_set(struct usba_udc *udc, u32 val) +static inline void usba_int_enb_set(struct usba_udc *udc, u32 mask) { + u32 val; + + val = udc->int_enb_cache | mask; + usba_writel(udc, INT_ENB, val); + udc->int_enb_cache = val; +} + +static inline void usba_int_enb_clear(struct usba_udc *udc, u32 mask) +{ + u32 val; + + val = udc->int_enb_cache & ~mask; usba_writel(udc, INT_ENB, val); udc->int_enb_cache = val; } @@ -629,14 +641,12 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) if (ep->can_dma) { u32 ctrl; - usba_int_enb_set(udc, usba_int_enb_get(udc) | - USBA_BF(EPT_INT, 1 << ep->index) | + usba_int_enb_set(udc, USBA_BF(EPT_INT, 1 << ep->index) | USBA_BF(DMA_INT, 1 << ep->index)); ctrl = USBA_AUTO_VALID | USBA_INTDIS_DMA; usba_ep_writel(ep, CTL_ENB, ctrl); } else { - usba_int_enb_set(udc, usba_int_enb_get(udc) | - USBA_BF(EPT_INT, 1 << ep->index)); + usba_int_enb_set(udc, USBA_BF(EPT_INT, 1 << ep->index)); } spin_unlock_irqrestore(&udc->lock, flags); @@ -680,8 +690,7 @@ static int usba_ep_disable(struct usb_ep *_ep) usba_dma_readl(ep, STATUS); } usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE); - usba_int_enb_set(udc, usba_int_enb_get(udc) & - ~USBA_BF(EPT_INT, 1 << ep->index)); + usba_int_enb_clear(udc, USBA_BF(EPT_INT, 1 << ep->index)); request_complete_list(ep, &req_list, -ESHUTDOWN); @@ -1694,6 +1703,9 @@ static void usba_dma_irq(struct usba_udc *udc, struct usba_ep *ep) } } +static int start_clock(struct usba_udc *udc); +static void stop_clock(struct usba_udc *udc); + static irqreturn_t usba_udc_irq(int irq, void *devid) { struct usba_udc *udc = devid; @@ -1708,10 +1720,13 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) DBG(DBG_INT, "irq, status=%#08x\n", status); if (status & USBA_DET_SUSPEND) { + usba_writel(udc, INT_CLR, USBA_DET_SUSPEND|USBA_WAKE_UP); + usba_int_enb_set(udc, USBA_WAKE_UP); + usba_int_enb_clear(udc, USBA_DET_SUSPEND); + udc->suspended = true; toggle_bias(udc, 0); - usba_writel(udc, INT_CLR, USBA_DET_SUSPEND); - usba_int_enb_set(udc, int_enb | USBA_WAKE_UP); udc->bias_pulse_needed = true; + stop_clock(udc); DBG(DBG_BUS, "Suspend detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver && udc->driver->suspend) { @@ -1722,14 +1737,17 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) } if (status & USBA_WAKE_UP) { + start_clock(udc); toggle_bias(udc, 1); usba_writel(udc, INT_CLR, USBA_WAKE_UP); - usba_int_enb_set(udc, int_enb & ~USBA_WAKE_UP); DBG(DBG_BUS, "Wake Up CPU detected\n"); } if (status & USBA_END_OF_RESUME) { + udc->suspended = false; usba_writel(udc, INT_CLR, USBA_END_OF_RESUME); + usba_int_enb_clear(udc, USBA_WAKE_UP); + usba_int_enb_set(udc, USBA_DET_SUSPEND); generate_bias_pulse(udc); DBG(DBG_BUS, "Resume detected\n"); if (udc->gadget.speed != USB_SPEED_UNKNOWN @@ -1744,6 +1762,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (dma_status) { int i; + usba_int_enb_set(udc, USBA_DET_SUSPEND); + for (i = 1; i <= USBA_NR_DMAS; i++) if (dma_status & (1 << i)) usba_dma_irq(udc, &udc->usba_ep[i]); @@ -1753,6 +1773,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (ep_status) { int i; + usba_int_enb_set(udc, USBA_DET_SUSPEND); + for (i = 0; i < udc->num_ep; i++) if (ep_status & (1 << i)) { if (ep_is_control(&udc->usba_ep[i])) @@ -1766,7 +1788,9 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) struct usba_ep *ep0, *ep; int i, n; - usba_writel(udc, INT_CLR, USBA_END_OF_RESET); + usba_writel(udc, INT_CLR, + USBA_END_OF_RESET|USBA_END_OF_RESUME + |USBA_DET_SUSPEND|USBA_WAKE_UP); generate_bias_pulse(udc); reset_all_endpoints(udc); @@ -1793,7 +1817,12 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) | USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE))); usba_ep_writel(ep0, CTL_ENB, USBA_EPT_ENABLE | USBA_RX_SETUP); - usba_int_enb_set(udc, int_enb | USBA_BF(EPT_INT, 1) | + + /* If we get reset while suspended... */ + udc->suspended = false; + usba_int_enb_clear(udc, USBA_WAKE_UP); + + usba_int_enb_set(udc, USBA_BF(EPT_INT, 1) | USBA_DET_SUSPEND | USBA_END_OF_RESUME); /* @@ -1827,6 +1856,8 @@ static int start_clock(struct usba_udc *udc) if (udc->clocked) return 0; + pm_stay_awake(&udc->pdev->dev); + ret = clk_prepare_enable(udc->pclk); if (ret) return ret; @@ -1849,6 +1880,8 @@ static void stop_clock(struct usba_udc *udc) clk_disable_unprepare(udc->pclk); udc->clocked = false; + + pm_relax(&udc->pdev->dev); } static int usba_start(struct usba_udc *udc) @@ -1860,9 +1893,19 @@ static int usba_start(struct usba_udc *udc) if (ret) return ret; + if (udc->suspended) + return 0; + spin_lock_irqsave(&udc->lock, flags); toggle_bias(udc, 1); usba_writel(udc, CTRL, USBA_ENABLE_MASK); + /* Clear all requested and pending interrupts... */ + usba_writel(udc, INT_ENB, 0); + udc->int_enb_cache = 0; + usba_writel(udc, INT_CLR, + USBA_END_OF_RESET|USBA_END_OF_RESUME + |USBA_DET_SUSPEND|USBA_WAKE_UP); + /* ...and enable just 'reset' IRQ to get us started */ usba_int_enb_set(udc, USBA_END_OF_RESET); spin_unlock_irqrestore(&udc->lock, flags); @@ -1873,6 +1916,9 @@ static void usba_stop(struct usba_udc *udc) { unsigned long flags; + if (udc->suspended) + return; + spin_lock_irqsave(&udc->lock, flags); udc->gadget.speed = USB_SPEED_UNKNOWN; reset_all_endpoints(udc); @@ -1900,6 +1946,7 @@ static irqreturn_t usba_vbus_irq_thread(int irq, void *devid) if (vbus) { usba_start(udc); } else { + udc->suspended = false; usba_stop(udc); if (udc->driver->disconnect) @@ -1963,6 +2010,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget) if (fifo_mode == 0) udc->configured_ep = 1; + udc->suspended = false; usba_stop(udc); udc->driver = NULL; @@ -2276,6 +2324,7 @@ static int usba_udc_suspend(struct device *dev) mutex_lock(&udc->vbus_mutex); if (!device_may_wakeup(dev)) { + udc->suspended = false; usba_stop(udc); goto out; } @@ -2285,10 +2334,13 @@ static int usba_udc_suspend(struct device *dev) * to request vbus irq, assuming always on. */ if (udc->vbus_pin) { + /* FIXME: right to stop here...??? */ usba_stop(udc); enable_irq_wake(gpiod_to_irq(udc->vbus_pin)); } + enable_irq_wake(udc->irq); + out: mutex_unlock(&udc->vbus_mutex); return 0; @@ -2302,8 +2354,12 @@ static int usba_udc_resume(struct device *dev) if (!udc->driver) return 0; - if (device_may_wakeup(dev) && udc->vbus_pin) - disable_irq_wake(gpiod_to_irq(udc->vbus_pin)); + if (device_may_wakeup(dev)) { + if (udc->vbus_pin) + disable_irq_wake(gpiod_to_irq(udc->vbus_pin)); + + disable_irq_wake(udc->irq); + } /* If Vbus is present, enable the controller and wait for reset */ mutex_lock(&udc->vbus_mutex); diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 030bf797cd25..a0225e4543d4 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -331,6 +331,7 @@ struct usba_udc { struct usba_ep *usba_ep; bool bias_pulse_needed; bool clocked; + bool suspended; u16 devstatus; diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 213b52508621..8414fac74493 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -617,21 +617,7 @@ static int dummy_enable(struct usb_ep *_ep, _ep->name, desc->bEndpointAddress & 0x0f, (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out", - ({ char *val; - switch (usb_endpoint_type(desc)) { - case USB_ENDPOINT_XFER_BULK: - val = "bulk"; - break; - case USB_ENDPOINT_XFER_ISOC: - val = "iso"; - break; - case USB_ENDPOINT_XFER_INT: - val = "intr"; - break; - default: - val = "ctrl"; - break; - } val; }), + usb_ep_type_string(usb_endpoint_type(desc)), max, ep->stream_en ? "enabled" : "disabled"); /* at this point real hardware should be NAKing transfers diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index b0781771704e..d8f1c60793ed 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -115,6 +115,11 @@ struct lpc32xx_ep { bool wedge; }; +enum atx_type { + ISP1301, + STOTG04, +}; + /* * Common UDC structure */ @@ -129,8 +134,6 @@ struct lpc32xx_udc { /* Board and device specific */ struct lpc32xx_usbd_cfg *board; - u32 io_p_start; - u32 io_p_size; void __iomem *udp_baseaddr; int udp_irq[4]; struct clk *usb_slv_clk; @@ -151,10 +154,10 @@ struct lpc32xx_udc { u8 last_vbus; int pullup; int poweron; + enum atx_type atx; /* Work queues related to I2C support */ struct work_struct pullup_job; - struct work_struct vbus_job; struct work_struct power_job; /* USB device peripheral - various */ @@ -553,6 +556,15 @@ static inline void remove_debug_file(struct lpc32xx_udc *udc) {} /* Primary initialization sequence for the ISP1301 transceiver */ static void isp1301_udc_configure(struct lpc32xx_udc *udc) { + u8 value; + s32 vendor, product; + + vendor = i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00); + product = i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x02); + + if (vendor == 0x0483 && product == 0xa0c4) + udc->atx = STOTG04; + /* LPC32XX only supports DAT_SE0 USB mode */ /* This sequence is important */ @@ -572,8 +584,12 @@ static void isp1301_udc_configure(struct lpc32xx_udc *udc) */ i2c_smbus_write_byte_data(udc->isp1301_i2c_client, (ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR), ~0); + + value = MC2_BI_DI; + if (udc->atx != STOTG04) + value |= MC2_SPD_SUSP_CTRL; i2c_smbus_write_byte_data(udc->isp1301_i2c_client, - ISP1301_I2C_MODE_CONTROL_2, (MC2_BI_DI | MC2_SPD_SUSP_CTRL)); + ISP1301_I2C_MODE_CONTROL_2, value); /* Driver VBUS_DRV high or low depending on board setup */ if (udc->board->vbus_drv_pol != 0) @@ -601,24 +617,19 @@ static void isp1301_udc_configure(struct lpc32xx_udc *udc) (ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), OTG1_VBUS_DISCHRG); - /* Clear and enable VBUS high edge interrupt */ i2c_smbus_write_byte_data(udc->isp1301_i2c_client, ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR, ~0); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); i2c_smbus_write_byte_data(udc->isp1301_i2c_client, - ISP1301_I2C_INTERRUPT_FALLING, INT_VBUS_VLD); - i2c_smbus_write_byte_data(udc->isp1301_i2c_client, ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); - i2c_smbus_write_byte_data(udc->isp1301_i2c_client, - ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD); - dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n", - i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00)); - dev_info(udc->dev, "ISP1301 Product ID : 0x%04x\n", - i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x02)); + dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n", vendor); + dev_info(udc->dev, "ISP1301 Product ID : 0x%04x\n", product); dev_info(udc->dev, "ISP1301 Version ID : 0x%04x\n", i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x14)); + } /* Enables or disables the USB device pullup via the ISP1301 transceiver */ @@ -661,6 +672,10 @@ static void isp1301_pullup_enable(struct lpc32xx_udc *udc, int en_pullup, /* Powers up or down the ISP1301 transceiver */ static void isp1301_set_powerstate(struct lpc32xx_udc *udc, int enable) { + /* There is no "global power down" register for stotg04 */ + if (udc->atx == STOTG04) + return; + if (enable != 0) /* Power up ISP1301 - this ISP1301 will automatically wakeup when VBUS is detected */ @@ -2830,11 +2845,9 @@ static irqreturn_t lpc32xx_usb_devdma_irq(int irq, void *_udc) * VBUS detection, pullup handler, and Gadget cable state notification * */ -static void vbus_work(struct work_struct *work) +static void vbus_work(struct lpc32xx_udc *udc) { u8 value; - struct lpc32xx_udc *udc = container_of(work, struct lpc32xx_udc, - vbus_job); if (udc->enabled != 0) { /* Discharge VBUS real quick */ @@ -2870,18 +2883,13 @@ static void vbus_work(struct work_struct *work) lpc32xx_vbus_session(&udc->gadget, udc->vbus); } } - - /* Re-enable after completion */ - enable_irq(udc->udp_irq[IRQ_USB_ATX]); } static irqreturn_t lpc32xx_usb_vbus_irq(int irq, void *_udc) { struct lpc32xx_udc *udc = _udc; - /* Defer handling of VBUS IRQ to work queue */ - disable_irq_nosync(udc->udp_irq[IRQ_USB_ATX]); - schedule_work(&udc->vbus_job); + vbus_work(udc); return IRQ_HANDLED; } @@ -2890,7 +2898,6 @@ static int lpc32xx_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { struct lpc32xx_udc *udc = to_udc(gadget); - int i; if (!driver || driver->max_speed < USB_SPEED_FULL || !driver->setup) { dev_err(udc->dev, "bad parameter.\n"); @@ -2910,22 +2917,25 @@ static int lpc32xx_start(struct usb_gadget *gadget, /* Force VBUS process once to check for cable insertion */ udc->last_vbus = udc->vbus = 0; - schedule_work(&udc->vbus_job); + vbus_work(udc); - /* Do not re-enable ATX IRQ (3) */ - for (i = IRQ_USB_LP; i < IRQ_USB_ATX; i++) - enable_irq(udc->udp_irq[i]); + /* enable interrupts */ + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_FALLING, INT_SESS_VLD | INT_VBUS_VLD); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_RISING, INT_SESS_VLD | INT_VBUS_VLD); return 0; } static int lpc32xx_stop(struct usb_gadget *gadget) { - int i; struct lpc32xx_udc *udc = to_udc(gadget); - for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++) - disable_irq(udc->udp_irq[i]); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); + i2c_smbus_write_byte_data(udc->isp1301_i2c_client, + ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); if (udc->clocked) { spin_lock(&udc->lock); @@ -2999,7 +3009,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) dma_addr_t dma_handle; struct device_node *isp1301_node; - udc = kmemdup(&controller_template, sizeof(*udc), GFP_KERNEL); + udc = devm_kmemdup(dev, &controller_template, sizeof(*udc), GFP_KERNEL); if (!udc) return -ENOMEM; @@ -3022,8 +3032,7 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) udc->isp1301_i2c_client = isp1301_get_client(isp1301_node); if (!udc->isp1301_i2c_client) { - retval = -EPROBE_DEFER; - goto phy_fail; + return -EPROBE_DEFER; } dev_info(udc->dev, "ISP1301 I2C device at address 0x%x\n", @@ -3032,7 +3041,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) - goto resource_fail; + return retval; udc->board = &lpc32xx_usbddata; @@ -3045,10 +3054,8 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) * IORESOURCE_IRQ, USB transceiver interrupt number */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - retval = -ENXIO; - goto resource_fail; - } + if (!res) + return -ENXIO; spin_lock_init(&udc->lock); @@ -3058,45 +3065,33 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) if (udc->udp_irq[i] < 0) { dev_err(udc->dev, "irq resource %d not available!\n", i); - retval = udc->udp_irq[i]; - goto irq_fail; + return udc->udp_irq[i]; } } - udc->io_p_start = res->start; - udc->io_p_size = resource_size(res); - if (!request_mem_region(udc->io_p_start, udc->io_p_size, driver_name)) { - dev_err(udc->dev, "someone's using UDC memory\n"); - retval = -EBUSY; - goto request_mem_region_fail; - } - - udc->udp_baseaddr = ioremap(udc->io_p_start, udc->io_p_size); + udc->udp_baseaddr = devm_ioremap_resource(dev, res); if (!udc->udp_baseaddr) { - retval = -ENOMEM; dev_err(udc->dev, "IO map failure\n"); - goto io_map_fail; + return -ENOMEM; } /* Get USB device clock */ - udc->usb_slv_clk = clk_get(&pdev->dev, NULL); + 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"); - retval = PTR_ERR(udc->usb_slv_clk); - goto usb_clk_get_fail; + return PTR_ERR(udc->usb_slv_clk); } /* 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"); - goto usb_clk_enable_fail; + return retval; } /* Setup deferred workqueue data */ udc->poweron = udc->pullup = 0; INIT_WORK(&udc->pullup_job, pullup_work); - INIT_WORK(&udc->vbus_job, vbus_work); #ifdef CONFIG_PM INIT_WORK(&udc->power_job, power_work); #endif @@ -3134,47 +3129,44 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) /* Request IRQs - low and high priority USB device IRQs are routed to * the same handler, while the DMA interrupt is routed elsewhere */ - retval = request_irq(udc->udp_irq[IRQ_USB_LP], lpc32xx_usb_lp_irq, - 0, "udc_lp", udc); + retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_LP], + lpc32xx_usb_lp_irq, 0, "udc_lp", udc); if (retval < 0) { dev_err(udc->dev, "LP request irq %d failed\n", udc->udp_irq[IRQ_USB_LP]); - goto irq_lp_fail; + goto irq_req_fail; } - retval = request_irq(udc->udp_irq[IRQ_USB_HP], lpc32xx_usb_hp_irq, - 0, "udc_hp", udc); + 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_hp_fail; + goto irq_req_fail; } - retval = request_irq(udc->udp_irq[IRQ_USB_DEVDMA], - lpc32xx_usb_devdma_irq, 0, "udc_dma", udc); + retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_DEVDMA], + lpc32xx_usb_devdma_irq, 0, "udc_dma", udc); if (retval < 0) { dev_err(udc->dev, "DEV request irq %d failed\n", udc->udp_irq[IRQ_USB_DEVDMA]); - goto irq_dev_fail; + goto irq_req_fail; } /* The transceiver interrupt is used for VBUS detection and will kick off the VBUS handler function */ - retval = request_irq(udc->udp_irq[IRQ_USB_ATX], lpc32xx_usb_vbus_irq, - 0, "udc_otg", udc); + retval = devm_request_threaded_irq(dev, udc->udp_irq[IRQ_USB_ATX], NULL, + lpc32xx_usb_vbus_irq, IRQF_ONESHOT, + "udc_otg", udc); if (retval < 0) { dev_err(udc->dev, "VBUS request irq %d failed\n", udc->udp_irq[IRQ_USB_ATX]); - goto irq_xcvr_fail; + goto irq_req_fail; } /* Initialize wait queue */ init_waitqueue_head(&udc->ep_disable_wait_queue); atomic_set(&udc->enabled_ep_cnt, 0); - /* Keep all IRQs disabled until GadgetFS starts up */ - for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++) - disable_irq(udc->udp_irq[i]); - retval = usb_add_gadget_udc(dev, &udc->gadget); if (retval < 0) goto add_gadget_fail; @@ -3190,32 +3182,15 @@ static int lpc32xx_udc_probe(struct platform_device *pdev) return 0; add_gadget_fail: - free_irq(udc->udp_irq[IRQ_USB_ATX], udc); -irq_xcvr_fail: - free_irq(udc->udp_irq[IRQ_USB_DEVDMA], udc); -irq_dev_fail: - free_irq(udc->udp_irq[IRQ_USB_HP], udc); -irq_hp_fail: - free_irq(udc->udp_irq[IRQ_USB_LP], udc); -irq_lp_fail: +irq_req_fail: dma_pool_destroy(udc->dd_cache); dma_alloc_fail: dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, udc->udca_v_base, udc->udca_p_base); i2c_fail: clk_disable_unprepare(udc->usb_slv_clk); -usb_clk_enable_fail: - clk_put(udc->usb_slv_clk); -usb_clk_get_fail: - iounmap(udc->udp_baseaddr); -io_map_fail: - release_mem_region(udc->io_p_start, udc->io_p_size); dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval); -request_mem_region_fail: -irq_fail: -resource_fail: -phy_fail: - kfree(udc); + return retval; } @@ -3231,24 +3206,14 @@ static int lpc32xx_udc_remove(struct platform_device *pdev) udc_disable(udc); pullup(udc, 0); - free_irq(udc->udp_irq[IRQ_USB_ATX], udc); - device_init_wakeup(&pdev->dev, 0); remove_debug_file(udc); dma_pool_destroy(udc->dd_cache); dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, udc->udca_v_base, udc->udca_p_base); - free_irq(udc->udp_irq[IRQ_USB_DEVDMA], udc); - free_irq(udc->udp_irq[IRQ_USB_HP], udc); - free_irq(udc->udp_irq[IRQ_USB_LP], udc); clk_disable_unprepare(udc->usb_slv_clk); - clk_put(udc->usb_slv_clk); - - iounmap(udc->udp_baseaddr); - release_mem_region(udc->io_p_start, udc->io_p_size); - kfree(udc); return 0; } diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index c2011cd7df8c..564aeee1a1fe 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -573,8 +573,7 @@ net2272_read_fifo(struct net2272_ep *ep, struct net2272_request *req) /* completion */ if (unlikely(cleanup || is_short || - ((req->req.actual == req->req.length) - && !req->req.zero))) { + req->req.actual == req->req.length)) { if (cleanup) { net2272_out_flush(ep); diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 898339e5df10..b6bbe2e448ba 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -789,8 +789,7 @@ static int read_fifo(struct net2280_ep *ep, struct net2280_request *req) (void) readl(&ep->regs->ep_rsp); } - return is_short || ((req->req.actual == req->req.length) && - !req->req.zero); + return is_short || req->req.actual == req->req.length; } /* fill out dma descriptor to match a given request */ @@ -1058,7 +1057,7 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* PIO ... stuff the fifo, or unblock it. */ if (ep->is_in) write_fifo(ep, _req); - else if (list_empty(&ep->queue)) { + else { u32 s; /* OUT FIFO might have packet(s) buffered */ diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index 3d12cdd5f999..3235d5307403 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -727,8 +727,7 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) } ed->speed = (urb->dev->speed == USB_SPEED_LOW) ? FHCI_LOW_SPEED : FHCI_FULL_SPEED; - ed->max_pkt_size = usb_maxpacket(urb->dev, - urb->pipe, usb_pipeout(urb->pipe)); + ed->max_pkt_size = usb_endpoint_maxp(&urb->ep->desc); urb->ep->hcpriv = ed; fhci_dbg(fhci, "new ep speed=%d max_pkt_size=%d\n", ed->speed, ed->max_pkt_size); @@ -768,8 +767,7 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) if (urb->transfer_flags & URB_ZERO_PACKET && urb->transfer_buffer_length > 0 && ((urb->transfer_buffer_length % - usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe))) == 0)) + usb_endpoint_maxp(&urb->ep->desc)) == 0)) urb_state = US_BULK0; while (data_len > 4096) { td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt, @@ -807,8 +805,8 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) break; case FHCI_TF_CTRL: ed->dev_addr = usb_pipedevice(urb->pipe); - ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe, - usb_pipeout(urb->pipe)); + ed->max_pkt_size = usb_endpoint_maxp(&urb->ep->desc); + /* setup stage */ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP, USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true); diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 6343fbacd244..4a5c9b599c57 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -3203,6 +3203,8 @@ static int __init u132_hcd_init(void) return -ENODEV; printk(KERN_INFO "driver %s\n", hcd_name); workqueue = create_singlethread_workqueue("u132"); + if (!workqueue) + return -ENOMEM; retval = platform_driver_register(&u132_platform_driver); if (retval) destroy_workqueue(workqueue); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 96a740543183..3abe70ff1b1e 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -487,8 +487,8 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, /* Write 1 to disable the port */ writel(port_status | PORT_PE, addr); port_status = readl(addr); - xhci_dbg(xhci, "disable port, actual port %d status = 0x%x\n", - wIndex, port_status); + xhci_dbg(xhci, "disable port %d-%d, portsc: 0x%x\n", + hcd->self.busnum, wIndex + 1, port_status); } static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, @@ -537,8 +537,9 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, /* Change bits are all write 1 to clear */ writel(port_status | status, addr); port_status = readl(addr); - xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n", - port_change_bit, wIndex, port_status); + + xhci_dbg(xhci, "clear port%d %s change, portsc: 0x%x\n", + wIndex + 1, port_change_bit, port_status); } struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd) @@ -565,13 +566,16 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, rhub = xhci_get_rhub(hcd); port = rhub->ports[index]; temp = readl(port->addr); + + xhci_dbg(xhci, "set port power %d-%d %s, portsc: 0x%x\n", + hcd->self.busnum, index + 1, on ? "ON" : "OFF", temp); + temp = xhci_port_state_to_neutral(temp); + if (on) { /* Power on */ writel(temp | PORT_POWER, port->addr); - temp = readl(port->addr); - xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", - index, temp); + readl(port->addr); } else { /* Power off */ writel(temp & ~PORT_POWER, port->addr); @@ -666,12 +670,17 @@ void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port, u32 link_state) { u32 temp; + u32 portsc; - temp = readl(port->addr); - temp = xhci_port_state_to_neutral(temp); + portsc = readl(port->addr); + temp = xhci_port_state_to_neutral(portsc); temp &= ~PORT_PLS_MASK; temp |= PORT_LINK_STROBE | link_state; writel(temp, port->addr); + + xhci_dbg(xhci, "Set port %d-%d link state, portsc: 0x%x, write 0x%x", + port->rhub->hcd->self.busnum, port->hcd_portnum + 1, + portsc, temp); } static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci, @@ -840,7 +849,9 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, } else if (time_after_eq(jiffies, bus_state->resume_done[wIndex])) { int time_left; - xhci_dbg(xhci, "Resume USB2 port %d\n", wIndex + 1); + xhci_dbg(xhci, "resume USB2 port %d-%d\n", + hcd->self.busnum, wIndex + 1); + bus_state->resume_done[wIndex] = 0; clear_bit(wIndex, &bus_state->resuming_ports); @@ -867,9 +878,8 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, } else { int port_status = readl(port->addr); - xhci_warn(xhci, "Port resume %i msec timed out, portsc = 0x%x\n", - XHCI_MAX_REXIT_TIMEOUT_MS, - port_status); + xhci_warn(xhci, "Port resume timed out, port %d-%d: 0x%x\n", + hcd->self.busnum, wIndex + 1, port_status); *status |= USB_PORT_STAT_SUSPEND; clear_bit(wIndex, &bus_state->rexit_ports); } @@ -1124,9 +1134,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (status == 0xffffffff) goto error; - xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", - wIndex, temp); - xhci_dbg(xhci, "Get port status returned 0x%x\n", status); + xhci_dbg(xhci, "Get port status %d-%d read: 0x%x, return 0x%x", + hcd->self.busnum, wIndex + 1, temp, status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); /* if USB 3.1 extended port status return additional 4 bytes */ @@ -1182,7 +1191,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = readl(ports[wIndex]->addr); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { - xhci_warn(xhci, "USB core suspending device not in U0/U1/U2.\n"); + xhci_warn(xhci, "USB core suspending port %d-%d not in U0/U1/U2\n", + hcd->self.busnum, wIndex + 1); goto error; } diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 60987c787e44..026fe18972d3 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -206,19 +206,6 @@ static int xhci_mtk_ssusb_config(struct xhci_hcd_mtk *mtk) return xhci_mtk_host_enable(mtk); } -/* ignore the error if the clock does not exist */ -static struct clk *optional_clk_get(struct device *dev, const char *id) -{ - struct clk *opt_clk; - - opt_clk = devm_clk_get(dev, id); - /* ignore error number except EPROBE_DEFER */ - if (IS_ERR(opt_clk) && (PTR_ERR(opt_clk) != -EPROBE_DEFER)) - opt_clk = NULL; - - return opt_clk; -} - static int xhci_mtk_clks_get(struct xhci_hcd_mtk *mtk) { struct device *dev = mtk->dev; @@ -229,15 +216,15 @@ static int xhci_mtk_clks_get(struct xhci_hcd_mtk *mtk) return PTR_ERR(mtk->sys_clk); } - mtk->ref_clk = optional_clk_get(dev, "ref_ck"); + mtk->ref_clk = devm_clk_get_optional(dev, "ref_ck"); if (IS_ERR(mtk->ref_clk)) return PTR_ERR(mtk->ref_clk); - mtk->mcu_clk = optional_clk_get(dev, "mcu_ck"); + mtk->mcu_clk = devm_clk_get_optional(dev, "mcu_ck"); if (IS_ERR(mtk->mcu_clk)) return PTR_ERR(mtk->mcu_clk); - mtk->dma_clk = optional_clk_get(dev, "dma_ck"); + mtk->dma_clk = devm_clk_get_optional(dev, "dma_ck"); return PTR_ERR_OR_ZERO(mtk->dma_clk); } diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 0ac4ec975547..998241f5fce3 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -165,8 +165,6 @@ static int xhci_plat_probe(struct platform_device *pdev) struct xhci_hcd *xhci; struct resource *res; struct usb_hcd *hcd; - struct clk *clk; - struct clk *reg_clk; int ret; int irq; @@ -235,31 +233,32 @@ static int xhci_plat_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); + xhci = hcd_to_xhci(hcd); + /* * Not all platforms have clks so it is not an error if the * clock do not exist. */ - reg_clk = devm_clk_get(&pdev->dev, "reg"); - if (!IS_ERR(reg_clk)) { - ret = clk_prepare_enable(reg_clk); - if (ret) - goto put_hcd; - } else if (PTR_ERR(reg_clk) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; + xhci->reg_clk = devm_clk_get_optional(&pdev->dev, "reg"); + if (IS_ERR(xhci->reg_clk)) { + ret = PTR_ERR(xhci->reg_clk); goto put_hcd; } - clk = devm_clk_get(&pdev->dev, NULL); - if (!IS_ERR(clk)) { - ret = clk_prepare_enable(clk); - if (ret) - goto disable_reg_clk; - } else if (PTR_ERR(clk) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; + ret = clk_prepare_enable(xhci->reg_clk); + if (ret) + goto put_hcd; + + xhci->clk = devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(xhci->clk)) { + ret = PTR_ERR(xhci->clk); goto disable_reg_clk; } - xhci = hcd_to_xhci(hcd); + ret = clk_prepare_enable(xhci->clk); + if (ret) + goto disable_reg_clk; + priv_match = of_device_get_match_data(&pdev->dev); if (priv_match) { struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); @@ -271,8 +270,6 @@ static int xhci_plat_probe(struct platform_device *pdev) device_wakeup_enable(hcd->self.controller); - xhci->clk = clk; - xhci->reg_clk = reg_clk; xhci->main_hcd = hcd; xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev, dev_name(&pdev->dev), hcd); @@ -348,10 +345,10 @@ put_usb3_hcd: usb_put_hcd(xhci->shared_hcd); disable_clk: - clk_disable_unprepare(clk); + clk_disable_unprepare(xhci->clk); disable_reg_clk: - clk_disable_unprepare(reg_clk); + clk_disable_unprepare(xhci->reg_clk); put_hcd: usb_put_hcd(hcd); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 9215a28dad40..fed3385aeac0 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1569,18 +1569,19 @@ static void handle_port_status(struct xhci_hcd *xhci, "WARN: xHC returned failed port status event\n"); port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0])); - xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id); - max_ports = HCS_MAX_PORTS(xhci->hcs_params1); + if ((port_id <= 0) || (port_id > max_ports)) { - xhci_warn(xhci, "Invalid port id %d\n", port_id); + xhci_warn(xhci, "Port change event with invalid port ID %d\n", + port_id); inc_deq(xhci, xhci->event_ring); return; } port = &xhci->hw_ports[port_id - 1]; if (!port || !port->rhub || port->hcd_portnum == DUPLICATE_ENTRY) { - xhci_warn(xhci, "Event for invalid port %u\n", port_id); + xhci_warn(xhci, "Port change event, no port for port ID %u\n", + port_id); bogus_port_status = true; goto cleanup; } @@ -1597,6 +1598,9 @@ static void handle_port_status(struct xhci_hcd *xhci, hcd_portnum = port->hcd_portnum; portsc = readl(port->addr); + xhci_dbg(xhci, "Port change event, %d-%d, id %d, portsc: 0x%x\n", + hcd->self.busnum, hcd_portnum + 1, port_id, portsc); + trace_xhci_handle_port_status(hcd_portnum, portsc); if (hcd->state == HC_STATE_SUSPENDED) { @@ -3275,6 +3279,12 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field |= TRB_IOC; more_trbs_coming = false; td->last_trb = ring->enqueue; + + if (xhci_urb_suitable_for_idt(urb)) { + memcpy(&send_addr, urb->transfer_buffer, + trb_buff_len); + field |= TRB_IDT; + } } /* Only set interrupt on short packet for IN endpoints */ @@ -3414,6 +3424,12 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (urb->transfer_buffer_length > 0) { u32 length_field, remainder; + if (xhci_urb_suitable_for_idt(urb)) { + memcpy(&urb->transfer_dma, urb->transfer_buffer, + urb->transfer_buffer_length); + field |= TRB_IDT; + } + remainder = xhci_td_remainder(xhci, 0, urb->transfer_buffer_length, urb->transfer_buffer_length, diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index efb0cad8710e..294158113d62 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -161,6 +161,7 @@ struct tegra_xusb_soc { } ports; bool scale_ss_clock; + bool has_ipfs; }; struct tegra_xusb { @@ -637,16 +638,18 @@ static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data) return IRQ_HANDLED; } -static void tegra_xusb_ipfs_config(struct tegra_xusb *tegra, - struct resource *regs) +static void tegra_xusb_config(struct tegra_xusb *tegra, + struct resource *regs) { u32 value; - value = ipfs_readl(tegra, IPFS_XUSB_HOST_CONFIGURATION_0); - value |= IPFS_EN_FPCI; - ipfs_writel(tegra, value, IPFS_XUSB_HOST_CONFIGURATION_0); + if (tegra->soc->has_ipfs) { + value = ipfs_readl(tegra, IPFS_XUSB_HOST_CONFIGURATION_0); + value |= IPFS_EN_FPCI; + ipfs_writel(tegra, value, IPFS_XUSB_HOST_CONFIGURATION_0); - usleep_range(10, 20); + usleep_range(10, 20); + } /* Program BAR0 space */ value = fpci_readl(tegra, XUSB_CFG_4); @@ -661,13 +664,15 @@ static void tegra_xusb_ipfs_config(struct tegra_xusb *tegra, value |= XUSB_IO_SPACE_EN | XUSB_MEM_SPACE_EN | XUSB_BUS_MASTER_EN; fpci_writel(tegra, value, XUSB_CFG_1); - /* Enable interrupt assertion */ - value = ipfs_readl(tegra, IPFS_XUSB_HOST_INTR_MASK_0); - value |= IPFS_IP_INT_MASK; - ipfs_writel(tegra, value, IPFS_XUSB_HOST_INTR_MASK_0); + if (tegra->soc->has_ipfs) { + /* Enable interrupt assertion */ + value = ipfs_readl(tegra, IPFS_XUSB_HOST_INTR_MASK_0); + value |= IPFS_IP_INT_MASK; + ipfs_writel(tegra, value, IPFS_XUSB_HOST_INTR_MASK_0); - /* Set hysteresis */ - ipfs_writel(tegra, 0x80, IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0); + /* Set hysteresis */ + ipfs_writel(tegra, 0x80, IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0); + } } static int tegra_xusb_clk_enable(struct tegra_xusb *tegra) @@ -1015,10 +1020,12 @@ static int tegra_xusb_probe(struct platform_device *pdev) if (IS_ERR(tegra->fpci_base)) return PTR_ERR(tegra->fpci_base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - tegra->ipfs_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(tegra->ipfs_base)) - return PTR_ERR(tegra->ipfs_base); + if (tegra->soc->has_ipfs) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + tegra->ipfs_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tegra->ipfs_base)) + return PTR_ERR(tegra->ipfs_base); + } tegra->xhci_irq = platform_get_irq(pdev, 0); if (tegra->xhci_irq < 0) @@ -1208,7 +1215,7 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto disable_rpm; } - tegra_xusb_ipfs_config(tegra, regs); + tegra_xusb_config(tegra, regs); err = tegra_xusb_load_firmware(tegra); if (err < 0) { @@ -1380,6 +1387,7 @@ static const struct tegra_xusb_soc tegra124_soc = { .usb3 = { .offset = 0, .count = 2, }, }, .scale_ss_clock = true, + .has_ipfs = true, }; MODULE_FIRMWARE("nvidia/tegra124/xusb.bin"); @@ -1411,12 +1419,38 @@ static const struct tegra_xusb_soc tegra210_soc = { .usb3 = { .offset = 0, .count = 4, }, }, .scale_ss_clock = false, + .has_ipfs = true, }; MODULE_FIRMWARE("nvidia/tegra210/xusb.bin"); +static const char * const tegra186_supply_names[] = { +}; + +static const struct tegra_xusb_phy_type tegra186_phy_types[] = { + { .name = "usb3", .num = 3, }, + { .name = "usb2", .num = 3, }, + { .name = "hsic", .num = 1, }, +}; + +static const struct tegra_xusb_soc tegra186_soc = { + .firmware = "nvidia/tegra186/xusb.bin", + .supply_names = tegra186_supply_names, + .num_supplies = ARRAY_SIZE(tegra186_supply_names), + .phy_types = tegra186_phy_types, + .num_types = ARRAY_SIZE(tegra186_phy_types), + .ports = { + .usb3 = { .offset = 0, .count = 3, }, + .usb2 = { .offset = 3, .count = 3, }, + .hsic = { .offset = 6, .count = 1, }, + }, + .scale_ss_clock = false, + .has_ipfs = false, +}; + static const struct of_device_id tegra_xusb_of_match[] = { { .compatible = "nvidia,tegra124-xusb", .data = &tegra124_soc }, { .compatible = "nvidia,tegra210-xusb", .data = &tegra210_soc }, + { .compatible = "nvidia,tegra186-xusb", .data = &tegra186_soc }, { }, }; MODULE_DEVICE_TABLE(of, tegra_xusb_of_match); diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 88b427434bd8..052a269d86f2 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -366,6 +366,11 @@ DEFINE_EVENT(xhci_log_ep_ctx, xhci_handle_cmd_config_ep, TP_ARGS(ctx) ); +DEFINE_EVENT(xhci_log_ep_ctx, xhci_add_endpoint, + TP_PROTO(struct xhci_ep_ctx *ctx), + TP_ARGS(ctx) +); + DECLARE_EVENT_CLASS(xhci_log_slot_ctx, TP_PROTO(struct xhci_slot_ctx *ctx), TP_ARGS(ctx), @@ -432,6 +437,31 @@ DEFINE_EVENT(xhci_log_slot_ctx, xhci_configure_endpoint, TP_ARGS(ctx) ); +DECLARE_EVENT_CLASS(xhci_log_ctrl_ctx, + TP_PROTO(struct xhci_input_control_ctx *ctrl_ctx), + TP_ARGS(ctrl_ctx), + TP_STRUCT__entry( + __field(u32, drop) + __field(u32, add) + ), + TP_fast_assign( + __entry->drop = le32_to_cpu(ctrl_ctx->drop_flags); + __entry->add = le32_to_cpu(ctrl_ctx->add_flags); + ), + TP_printk("%s", xhci_decode_ctrl_ctx(__entry->drop, __entry->add) + ) +); + +DEFINE_EVENT(xhci_log_ctrl_ctx, xhci_address_ctrl_ctx, + TP_PROTO(struct xhci_input_control_ctx *ctrl_ctx), + TP_ARGS(ctrl_ctx) +); + +DEFINE_EVENT(xhci_log_ctrl_ctx, xhci_configure_endpoint_ctrl_ctx, + TP_PROTO(struct xhci_input_control_ctx *ctrl_ctx), + TP_ARGS(ctrl_ctx) +); + DECLARE_EVENT_CLASS(xhci_log_ring, TP_PROTO(struct xhci_ring *ring), TP_ARGS(ring), diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7fa58c99f126..a9bb796794e3 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -893,7 +893,7 @@ static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci) struct xhci_port **ports; int port_index; unsigned long flags; - u32 t1, t2; + u32 t1, t2, portsc; spin_lock_irqsave(&xhci->lock, flags); @@ -902,10 +902,15 @@ static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci) ports = xhci->usb3_rhub.ports; while (port_index--) { t1 = readl(ports[port_index]->addr); + portsc = t1; t1 = xhci_port_state_to_neutral(t1); t2 = t1 & ~PORT_WAKE_BITS; - if (t1 != t2) + if (t1 != t2) { writel(t2, ports[port_index]->addr); + xhci_dbg(xhci, "disable wake bits port %d-%d, portsc: 0x%x, write: 0x%x\n", + xhci->usb3_rhub.hcd->self.busnum, + port_index + 1, portsc, t2); + } } /* disable usb2 ports Wake bits */ @@ -913,12 +918,16 @@ static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci) ports = xhci->usb2_rhub.ports; while (port_index--) { t1 = readl(ports[port_index]->addr); + portsc = t1; t1 = xhci_port_state_to_neutral(t1); t2 = t1 & ~PORT_WAKE_BITS; - if (t1 != t2) + if (t1 != t2) { writel(t2, ports[port_index]->addr); + xhci_dbg(xhci, "disable wake bits port %d-%d, portsc: 0x%x, write: 0x%x\n", + xhci->usb2_rhub.hcd->self.busnum, + port_index + 1, portsc, t2); + } } - spin_unlock_irqrestore(&xhci->lock, flags); } @@ -1238,6 +1247,21 @@ EXPORT_SYMBOL_GPL(xhci_resume); /*-------------------------------------------------------------------------*/ +/* + * Bypass the DMA mapping if URB is suitable for Immediate Transfer (IDT), + * we'll copy the actual data into the TRB address register. This is limited to + * transfers up to 8 bytes on output endpoints of any kind with wMaxPacketSize + * >= 8 bytes. If suitable for IDT only one Transfer TRB per TD is allowed. + */ +static int xhci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + if (xhci_urb_suitable_for_idt(urb)) + return 0; + + return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags); +} + /** * 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 @@ -1783,6 +1807,7 @@ static int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_container_ctx *in_ctx; unsigned int ep_index; struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_ep_ctx *ep_ctx; u32 added_ctxs; u32 new_add_flags, new_drop_flags; struct xhci_virt_device *virt_dev; @@ -1873,6 +1898,9 @@ static int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, /* Store the usb_device pointer for later use */ ep->hcpriv = udev; + ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); + trace_xhci_add_endpoint(ep_ctx); + xhci_debugfs_create_endpoint(xhci, virt_dev, ep_index); xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n", @@ -2747,6 +2775,8 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, } slot_ctx = xhci_get_slot_ctx(xhci, command->in_ctx); + + trace_xhci_configure_endpoint_ctrl_ctx(ctrl_ctx); trace_xhci_configure_endpoint(slot_ctx); if (!ctx_change) @@ -4012,6 +4042,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, trace_xhci_address_ctx(xhci, virt_dev->in_ctx, le32_to_cpu(slot_ctx->dev_info) >> 27); + trace_xhci_address_ctrl_ctx(ctrl_ctx); spin_lock_irqsave(&xhci->lock, flags); trace_xhci_setup_device(virt_dev); ret = xhci_queue_address_device(xhci, command, virt_dev->in_ctx->dma, @@ -5154,6 +5185,7 @@ static const struct hc_driver xhci_hc_driver = { /* * managing i/o requests and associated device resources */ + .map_urb_for_dma = xhci_map_urb_for_dma, .urb_enqueue = xhci_urb_enqueue, .urb_dequeue = xhci_urb_dequeue, .alloc_dev = xhci_alloc_dev, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 9334cdee382a..a450a99e90eb 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1303,6 +1303,8 @@ enum xhci_setup_dev { #define TRB_IOC (1<<5) /* The buffer pointer contains immediate data */ #define TRB_IDT (1<<6) +/* TDs smaller than this might use IDT */ +#define TRB_IDT_MAX_SIZE 8 /* Block Event Interrupt */ #define TRB_BEI (1<<9) @@ -2149,6 +2151,21 @@ static inline struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci, urb->stream_id); } +/* + * TODO: As per spec Isochronous IDT transmissions are supported. We bypass + * them anyways as we where unable to find a device that matches the + * constraints. + */ +static inline bool xhci_urb_suitable_for_idt(struct urb *urb) +{ + if (!usb_endpoint_xfer_isoc(&urb->ep->desc) && usb_urb_dir_out(urb) && + usb_endpoint_maxp(&urb->ep->desc) >= TRB_IDT_MAX_SIZE && + urb->transfer_buffer_length <= TRB_IDT_MAX_SIZE) + return true; + + return false; +} + static inline char *xhci_slot_state_string(u32 state) { switch (state) { @@ -2384,6 +2401,35 @@ static inline const char *xhci_decode_trb(u32 field0, u32 field1, u32 field2, return str; } +static inline const char *xhci_decode_ctrl_ctx(unsigned long drop, + unsigned long add) +{ + static char str[1024]; + unsigned int bit; + int ret = 0; + + if (drop) { + ret = sprintf(str, "Drop:"); + for_each_set_bit(bit, &drop, 32) + ret += sprintf(str + ret, " %d%s", + bit / 2, + bit % 2 ? "in":"out"); + ret += sprintf(str + ret, ", "); + } + + if (add) { + ret += sprintf(str + ret, "Add:%s%s", + (add & SLOT_FLAG) ? " slot":"", + (add & EP0_FLAG) ? " ep0":""); + add &= ~(SLOT_FLAG | EP0_FLAG); + for_each_set_bit(bit, &add, 32) + ret += sprintf(str + ret, " %d%s", + bit / 2, + bit % 2 ? "in":"out"); + } + return str; +} + static inline const char *xhci_decode_slot_context(u32 info, u32 info2, u32 tt_info, u32 state) { diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c index 8142c6b4c4cf..320fc4739835 100644 --- a/drivers/usb/isp1760/isp1760-hcd.c +++ b/drivers/usb/isp1760/isp1760-hcd.c @@ -788,11 +788,11 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, mem_reads8(hcd->regs, qtd->payload_addr, qtd->data_buffer, qtd->actual_length); - /* Fall through (?) */ + /* Fall through */ case OUT_PID: qtd->urb->actual_length += qtd->actual_length; - /* Fall through ... */ + /* Fall through */ case SETUP_PID: break; } diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 04684849d683..4d6ae3795a88 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -12,6 +12,7 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> +#include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/nls.h> @@ -222,11 +223,51 @@ static const struct usb251xb_data usb2517i_data = { .product_str = "USB2517i", }; +#ifdef CONFIG_GPIOLIB +static int usb251xb_check_dev_children(struct device *dev, void *child) +{ + if (dev->type == &i2c_adapter_type) { + return device_for_each_child(dev, child, + usb251xb_check_dev_children); + } + + return (dev == 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; + int ret; + + if (!hub->gpio_reset) + return 0; + + if (!gc) + return -EINVAL; + + 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"); + return -EINVAL; + } + + return 0; +} +#else +static int usb251x_check_gpio_chip(struct usb251xb *hub) +{ + return 0; +} +#endif + static void usb251xb_reset(struct usb251xb *hub, int state) { if (!hub->gpio_reset) return; + i2c_lock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT); + gpiod_set_value_cansleep(hub->gpio_reset, state); /* wait for hub recovery/stabilization */ @@ -234,6 +275,8 @@ static void usb251xb_reset(struct usb251xb *hub, int state) usleep_range(500, 750); /* >=500us at power on */ else usleep_range(1, 10); /* >=1us at power down */ + + i2c_unlock_bus(hub->i2c->adapter, I2C_LOCK_SEGMENT); } static int usb251xb_connect(struct usb251xb *hub) @@ -331,18 +374,31 @@ out_err: } #ifdef CONFIG_OF +static void usb251xb_get_ports_field(struct usb251xb *hub, + const char *prop_name, u8 port_cnt, u8 *fld) +{ + struct device *dev = hub->dev; + struct property *prop; + const __be32 *p; + u32 port; + + of_property_for_each_u32(dev->of_node, prop_name, prop, p, port) { + if ((port >= 1) && (port <= port_cnt)) + *fld |= BIT(port); + else + dev_warn(dev, "port %u doesn't exist\n", port); + } +} + static int usb251xb_get_ofdata(struct usb251xb *hub, struct usb251xb_data *data) { struct device *dev = hub->dev; struct device_node *np = dev->of_node; - int len, err, i; - u32 port, property_u32 = 0; - const u32 *cproperty_u32; + int len, err; + u32 property_u32 = 0; const char *cproperty_char; char str[USB251XB_STRING_BUFSIZE / 2]; - struct property *prop; - const __be32 *p; if (!np) { dev_err(dev, "failed to get ofdata\n"); @@ -444,46 +500,16 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, hub->conf_data3 |= BIT(0); hub->non_rem_dev = USB251XB_DEF_NON_REMOVABLE_DEVICES; - cproperty_u32 = of_get_property(np, "non-removable-ports", &len); - if (cproperty_u32 && (len / sizeof(u32)) > 0) { - for (i = 0; i < len / sizeof(u32); i++) { - u32 port = be32_to_cpu(cproperty_u32[i]); - - if ((port >= 1) && (port <= data->port_cnt)) - hub->non_rem_dev |= BIT(port); - else - dev_warn(dev, "NRD port %u doesn't exist\n", - port); - } - } + usb251xb_get_ports_field(hub, "non-removable-ports", data->port_cnt, + &hub->non_rem_dev); hub->port_disable_sp = USB251XB_DEF_PORT_DISABLE_SELF; - cproperty_u32 = of_get_property(np, "sp-disabled-ports", &len); - if (cproperty_u32 && (len / sizeof(u32)) > 0) { - for (i = 0; i < len / sizeof(u32); i++) { - u32 port = be32_to_cpu(cproperty_u32[i]); - - if ((port >= 1) && (port <= data->port_cnt)) - hub->port_disable_sp |= BIT(port); - else - dev_warn(dev, "PDS port %u doesn't exist\n", - port); - } - } + usb251xb_get_ports_field(hub, "sp-disabled-ports", data->port_cnt, + &hub->port_disable_sp); hub->port_disable_bp = USB251XB_DEF_PORT_DISABLE_BUS; - cproperty_u32 = of_get_property(np, "bp-disabled-ports", &len); - if (cproperty_u32 && (len / sizeof(u32)) > 0) { - for (i = 0; i < len / sizeof(u32); i++) { - u32 port = be32_to_cpu(cproperty_u32[i]); - - if ((port >= 1) && (port <= data->port_cnt)) - hub->port_disable_bp |= BIT(port); - else - dev_warn(dev, "PDB port %u doesn't exist\n", - port); - } - } + usb251xb_get_ports_field(hub, "bp-disabled-ports", data->port_cnt, + &hub->port_disable_bp); hub->max_power_sp = USB251XB_DEF_MAX_POWER_SELF; if (!of_property_read_u32(np, "sp-max-total-current-microamp", @@ -546,10 +572,10 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, * register controls the USB DP/DM signal swapping for each port. */ hub->port_swap = USB251XB_DEF_PORT_SWAP; - of_property_for_each_u32(np, "swap-dx-lanes", prop, p, port) { - if (port <= data->port_cnt) - hub->port_swap |= BIT(port); - } + usb251xb_get_ports_field(hub, "swap-dx-lanes", data->port_cnt, + &hub->port_swap); + if (of_get_property(np, "swap-us-lanes", NULL)) + hub->port_swap |= BIT(0); /* The following parameters are currently not exposed to devicetree, but * may be as soon as needed. @@ -621,6 +647,25 @@ static int usb251xb_probe(struct usb251xb *hub) } } + /* + * usb251x SMBus-slave SCL lane is muxed with CFG_SEL0 pin. So if anyone + * tries to work with the bus at the moment the hub reset is released, + * it may cause an invalid config being latched by usb251x. Particularly + * one of the config modes makes the hub loading a default registers + * value without SMBus-slave interface activation. If the hub + * accidentally gets this mode, this will cause the driver SMBus- + * functions failure. Normally we could just lock the SMBus-segment the + * hub i2c-interface resides for the device-specific reset timing. But + * the GPIO controller, which is used to handle the hub reset, might be + * placed at the same i2c-bus segment. In this case an error should be + * returned since we can't safely use the GPIO controller to clear the + * reset state (it may affect the hub configuration) and we can't lock + * the i2c-bus segment (it will cause a deadlock). + */ + err = usb251x_check_gpio_chip(hub); + if (err) + return err; + err = usb251xb_connect(hub); if (err) { dev_err(dev, "Failed to connect hub (%d)\n", err); diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index d5141aa79dd4..72f39a9751b5 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -172,7 +172,6 @@ static int usb3503_probe(struct usb3503 *hub) hub->gpio_reset = pdata->gpio_reset; hub->mode = pdata->initial_mode; } else if (np) { - struct clk *clk; u32 rate = 0; hub->port_off_mask = 0; @@ -198,34 +197,29 @@ static int usb3503_probe(struct usb3503 *hub) } } - clk = devm_clk_get(dev, "refclk"); - if (IS_ERR(clk) && PTR_ERR(clk) != -ENOENT) { + hub->clk = devm_clk_get_optional(dev, "refclk"); + if (IS_ERR(hub->clk)) { dev_err(dev, "unable to request refclk (%ld)\n", - PTR_ERR(clk)); - return PTR_ERR(clk); + PTR_ERR(hub->clk)); + return PTR_ERR(hub->clk); } - if (!IS_ERR(clk)) { - hub->clk = clk; - - if (rate != 0) { - err = clk_set_rate(hub->clk, rate); - if (err) { - dev_err(dev, - "unable to set reference clock rate to %d\n", - (int) rate); - return err; - } - } - - err = clk_prepare_enable(hub->clk); + if (rate != 0) { + err = clk_set_rate(hub->clk, rate); if (err) { dev_err(dev, - "unable to enable reference clock\n"); + "unable to set reference clock rate to %d\n", + (int)rate); return err; } } + err = clk_prepare_enable(hub->clk); + if (err) { + dev_err(dev, "unable to enable reference clock\n"); + return err; + } + property = of_get_property(np, "disabled-ports", &len); if (property && (len / sizeof(u32)) > 0) { int i; @@ -324,8 +318,7 @@ static int usb3503_i2c_remove(struct i2c_client *i2c) struct usb3503 *hub; hub = i2c_get_clientdata(i2c); - if (hub->clk) - clk_disable_unprepare(hub->clk); + clk_disable_unprepare(hub->clk); return 0; } @@ -348,8 +341,7 @@ static int usb3503_platform_remove(struct platform_device *pdev) struct usb3503 *hub; hub = platform_get_drvdata(pdev); - if (hub->clk) - clk_disable_unprepare(hub->clk); + clk_disable_unprepare(hub->clk); return 0; } @@ -358,18 +350,14 @@ static int usb3503_platform_remove(struct platform_device *pdev) static int usb3503_suspend(struct usb3503 *hub) { usb3503_switch_mode(hub, USB3503_MODE_STANDBY); - - if (hub->clk) - clk_disable_unprepare(hub->clk); + clk_disable_unprepare(hub->clk); return 0; } static int usb3503_resume(struct usb3503 *hub) { - if (hub->clk) - clk_prepare_enable(hub->clk); - + clk_prepare_enable(hub->clk); usb3503_switch_mode(hub, hub->mode); return 0; diff --git a/drivers/usb/mtu3/Makefile b/drivers/usb/mtu3/Makefile index 4a9715812bf9..3bf8cbcc1add 100644 --- a/drivers/usb/mtu3/Makefile +++ b/drivers/usb/mtu3/Makefile @@ -2,10 +2,17 @@ ccflags-$(CONFIG_USB_MTU3_DEBUG) += -DDEBUG +# define_trace.h needs to know how to find our header +CFLAGS_mtu3_trace.o := -I$(src) + obj-$(CONFIG_USB_MTU3) += mtu3.o mtu3-y := mtu3_plat.o +ifneq ($(CONFIG_TRACING),) + mtu3-y += mtu3_trace.o +endif + ifneq ($(filter y,$(CONFIG_USB_MTU3_HOST) $(CONFIG_USB_MTU3_DUAL_ROLE)),) mtu3-y += mtu3_host.o endif @@ -17,3 +24,7 @@ endif ifneq ($(CONFIG_USB_MTU3_DUAL_ROLE),) mtu3-y += mtu3_dr.o endif + +ifneq ($(CONFIG_DEBUG_FS),) + mtu3-y += mtu3_debugfs.o +endif diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 87823ac0d120..76ecf12fdf62 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -63,6 +63,15 @@ struct mtu3_request; #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, + * but not backward compatible + * 2. QMU extend buffer length supported + */ +#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 * 128 bits size of bitmap is large enough, that means it can manage @@ -135,45 +144,33 @@ struct mtu3_fifo_info { * The format of TX GPD is a little different from RX one. * And the size of GPD is 16 bytes. * - * @flag: + * @dw0_info: * bit0: Hardware Own (HWO) * bit1: Buffer Descriptor Present (BDP), always 0, BD is not supported * bit2: Bypass (BPS), 1: HW skips this GPD if HWO = 1 + * bit6: [EL] Zero Length Packet (ZLP), moved from @dw3_info[29] * bit7: Interrupt On Completion (IOC) - * @chksum: This is used to validate the contents of this GPD; - * If TXQ_CS_EN / RXQ_CS_EN bit is set, an interrupt is issued - * when checksum validation fails; - * Checksum value is calculated over the 16 bytes of the GPD by default; - * @data_buf_len (RX ONLY): This value indicates the length of - * the assigned data buffer - * @tx_ext_addr (TX ONLY): [3:0] are 4 extension bits of @buffer, - * [7:4] are 4 extension bits of @next_gpd + * bit[31:16]: ([EL] bit[31:12]) allow data buffer length (RX ONLY), + * the buffer length of the data to receive + * bit[23:16]: ([EL] bit[31:24]) extension address (TX ONLY), + * lower 4 bits are extension bits of @buffer, + * upper 4 bits are extension bits of @next_gpd * @next_gpd: Physical address of the next GPD * @buffer: Physical address of the data buffer - * @buf_len: - * (TX): This value indicates the length of the assigned data buffer - * (RX): The total length of data received - * @ext_len: reserved - * @rx_ext_addr(RX ONLY): [3:0] are 4 extension bits of @buffer, - * [7:4] are 4 extension bits of @next_gpd - * @ext_flag: - * bit5 (TX ONLY): Zero Length Packet (ZLP), + * @dw3_info: + * bit[15:0]: ([EL] bit[19:0]) data buffer length, + * (TX): the buffer length of the data to transmit + * (RX): The total length of data received + * bit[23:16]: ([EL] bit[31:24]) extension address (RX ONLY), + * lower 4 bits are extension bits of @buffer, + * upper 4 bits are extension bits of @next_gpd + * bit29: ([EL] abandoned) Zero Length Packet (ZLP) (TX ONLY) */ struct qmu_gpd { - __u8 flag; - __u8 chksum; - union { - __le16 data_buf_len; - __le16 tx_ext_addr; - }; + __le32 dw0_info; __le32 next_gpd; __le32 buffer; - __le16 buf_len; - union { - __u8 ext_len; - __u8 rx_ext_addr; - }; - __u8 ext_flag; + __le32 dw3_info; } __packed; /** @@ -316,6 +313,7 @@ static inline struct ssusb_mtk *dev_to_ssusb(struct device *dev) * @may_wakeup: means device's remote wakeup is enabled * @is_self_powered: is reported in device status and the config descriptor * @delayed_status: true when function drivers ask for delayed status + * @gen2cp: compatible with USB3 Gen2 IP * @ep0_req: dummy request used while handling standard USB requests * for GET_STATUS and SET_SEL * @setup_buf: ep0 response buffer for GET_STATUS and SET_SEL requests @@ -356,6 +354,7 @@ struct mtu3 { unsigned u2_enable:1; unsigned is_u3_ip:1; unsigned delayed_status:1; + unsigned gen2cp:1; u8 address; u8 test_mode_nr; diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 4fee200795a5..f8bd1d57e795 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -16,6 +16,8 @@ #include <linux/platform_device.h> #include "mtu3.h" +#include "mtu3_debug.h" +#include "mtu3_trace.h" static int ep_fifo_alloc(struct mtu3_ep *mep, u32 seg_size) { @@ -299,6 +301,7 @@ int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep, int interval, int burst, int mult) { void __iomem *mbase = mtu->mac_base; + bool gen2cp = mtu->gen2cp; int epnum = mep->epnum; u32 csr0, csr1, csr2; int fifo_sgsz, fifo_addr; @@ -319,7 +322,7 @@ int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep, num_pkts = (burst + 1) * (mult + 1) - 1; csr1 = TX_SS_BURST(burst) | TX_SLOT(mep->slot); - csr1 |= TX_MAX_PKT(num_pkts) | TX_MULT(mult); + csr1 |= TX_MAX_PKT(gen2cp, num_pkts) | TX_MULT(gen2cp, mult); csr2 = TX_FIFOADDR(fifo_addr >> 4); csr2 |= TX_FIFOSEGSIZE(fifo_sgsz); @@ -355,7 +358,7 @@ int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep, num_pkts = (burst + 1) * (mult + 1) - 1; csr1 = RX_SS_BURST(burst) | RX_SLOT(mep->slot); - csr1 |= RX_MAX_PKT(num_pkts) | RX_MULT(mult); + csr1 |= RX_MAX_PKT(gen2cp, num_pkts) | RX_MULT(gen2cp, mult); csr2 = RX_FIFOADDR(fifo_addr >> 4); csr2 |= RX_FIFOSEGSIZE(fifo_sgsz); @@ -600,6 +603,10 @@ static void mtu3_regs_init(struct mtu3 *mtu) mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON); /* enable automatical HWRW from L1 */ mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, LPM_HRWE); + + /* use new QMU format when HW version >= 0x1003 */ + if (mtu->gen2cp) + mtu3_writel(mbase, U3D_QFCR, ~0x0); } static irqreturn_t mtu3_link_isr(struct mtu3 *mtu) @@ -650,6 +657,8 @@ static irqreturn_t mtu3_link_isr(struct mtu3 *mtu) break; } dev_dbg(mtu->dev, "%s: %s\n", __func__, usb_speed_string(udev_speed)); + mtu3_dbg_trace(mtu->dev, "link speed %s", + usb_speed_string(udev_speed)); mtu->g.speed = udev_speed; mtu->g.ep0->maxpacket = maxpkt; @@ -672,6 +681,7 @@ static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu) ltssm &= mtu3_readl(mbase, U3D_LTSSM_INTR_ENABLE); mtu3_writel(mbase, U3D_LTSSM_INTR, ltssm); /* W1C */ dev_dbg(mtu->dev, "=== LTSSM[%x] ===\n", ltssm); + trace_mtu3_u3_ltssm_isr(ltssm); if (ltssm & (HOT_RST_INTR | WARM_RST_INTR)) mtu3_gadget_reset(mtu); @@ -702,6 +712,7 @@ static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu) u2comm &= mtu3_readl(mbase, U3D_COMMON_USB_INTR_ENABLE); mtu3_writel(mbase, U3D_COMMON_USB_INTR, u2comm); /* W1C */ dev_dbg(mtu->dev, "=== U2COMM[%x] ===\n", u2comm); + trace_mtu3_u2_common_isr(u2comm); if (u2comm & SUSPEND_INTR) mtu3_gadget_suspend(mtu); @@ -749,13 +760,15 @@ static irqreturn_t mtu3_irq(int irq, void *data) static int mtu3_hw_init(struct mtu3 *mtu) { - u32 cap_dev; + u32 value; int ret; - mtu->hw_version = mtu3_readl(mtu->ippc_base, U3D_SSUSB_HW_ID); + value = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_TRUNK_VERS); + mtu->hw_version = IP_TRUNK_VERS(value); + mtu->gen2cp = !!(mtu->hw_version >= MTU3_TRUNK_VERS_1003); - cap_dev = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_DEV_CAP); - mtu->is_u3_ip = !!SSUSB_IP_DEV_U3_PORT_NUM(cap_dev); + value = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_DEV_CAP); + mtu->is_u3_ip = !!SSUSB_IP_DEV_U3_PORT_NUM(value); dev_info(mtu->dev, "IP version 0x%x(%s IP)\n", mtu->hw_version, mtu->is_u3_ip ? "U3" : "U2"); @@ -893,6 +906,8 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) mtu3_stop(mtu); + ssusb_dev_debugfs_init(ssusb); + dev_dbg(dev, " %s() done...\n", __func__); return 0; diff --git a/drivers/usb/mtu3/mtu3_debug.h b/drivers/usb/mtu3/mtu3_debug.h new file mode 100644 index 000000000000..e96a69234d05 --- /dev/null +++ b/drivers/usb/mtu3/mtu3_debug.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mtu3_debug.h - debug header + * + * Copyright (C) 2019 MediaTek Inc. + * + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#ifndef __MTU3_DEBUG_H__ +#define __MTU3_DEBUG_H__ + +#include <linux/debugfs.h> + +#define MTU3_DEBUGFS_NAME_LEN 32 + +struct mtu3_regset { + char name[MTU3_DEBUGFS_NAME_LEN]; + struct debugfs_regset32 regset; + size_t nregs; +}; + +struct mtu3_file_map { + const char *name; + int (*show)(struct seq_file *s, void *unused); +}; + +#if IS_ENABLED(CONFIG_DEBUG_FS) +void ssusb_dev_debugfs_init(struct ssusb_mtk *ssusb); +void ssusb_dr_debugfs_init(struct ssusb_mtk *ssusb); +void ssusb_debugfs_create_root(struct ssusb_mtk *ssusb); +void ssusb_debugfs_remove_root(struct ssusb_mtk *ssusb); + +#else +static inline void ssusb_dev_debugfs_init(struct ssusb_mtk *ssusb) {} +static inline void ssusb_dr_debugfs_init(struct ssusb_mtk *ssusb) {} +static inline void ssusb_debugfs_create_root(struct ssusb_mtk *ssusb) {} +static inline void ssusb_debugfs_remove_root(struct ssusb_mtk *ssusb) {} + +#endif /* CONFIG_DEBUG_FS */ + +#if IS_ENABLED(CONFIG_TRACING) +void mtu3_dbg_trace(struct device *dev, const char *fmt, ...); + +#else +static inline void mtu3_dbg_trace(struct device *dev, const char *fmt, ...) {} + +#endif /* CONFIG_TRACING */ + +#endif /* __MTU3_DEBUG_H__ */ diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c new file mode 100644 index 000000000000..62c57ddc554e --- /dev/null +++ b/drivers/usb/mtu3/mtu3_debugfs.c @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mtu3_debugfs.c - debugfs interface + * + * Copyright (C) 2019 MediaTek Inc. + * + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#include <linux/uaccess.h> + +#include "mtu3.h" +#include "mtu3_dr.h" +#include "mtu3_debug.h" + +#define dump_register(nm) \ +{ \ + .name = __stringify(nm), \ + .offset = U3D_ ##nm, \ +} + +#define dump_prb_reg(nm, os) \ +{ \ + .name = nm, \ + .offset = os, \ +} + +static const struct debugfs_reg32 mtu3_ippc_regs[] = { + dump_register(SSUSB_IP_PW_CTRL0), + dump_register(SSUSB_IP_PW_CTRL1), + dump_register(SSUSB_IP_PW_CTRL2), + dump_register(SSUSB_IP_PW_CTRL3), + dump_register(SSUSB_OTG_STS), + dump_register(SSUSB_IP_XHCI_CAP), + dump_register(SSUSB_IP_DEV_CAP), + dump_register(SSUSB_U3_CTRL_0P), + dump_register(SSUSB_U2_CTRL_0P), + dump_register(SSUSB_HW_ID), + dump_register(SSUSB_HW_SUB_ID), + dump_register(SSUSB_IP_SPARE0), +}; + +static const struct debugfs_reg32 mtu3_dev_regs[] = { + dump_register(LV1ISR), + dump_register(LV1IER), + dump_register(EPISR), + dump_register(EPIER), + dump_register(EP0CSR), + dump_register(RXCOUNT0), + dump_register(QISAR0), + dump_register(QIER0), + dump_register(QISAR1), + dump_register(QIER1), + dump_register(CAP_EPNTXFFSZ), + dump_register(CAP_EPNRXFFSZ), + dump_register(CAP_EPINFO), + dump_register(MISC_CTRL), +}; + +static const struct debugfs_reg32 mtu3_csr_regs[] = { + dump_register(DEVICE_CONF), + dump_register(DEV_LINK_INTR_ENABLE), + dump_register(DEV_LINK_INTR), + dump_register(LTSSM_CTRL), + dump_register(USB3_CONFIG), + dump_register(LINK_STATE_MACHINE), + dump_register(LTSSM_INTR_ENABLE), + dump_register(LTSSM_INTR), + dump_register(U3U2_SWITCH_CTRL), + dump_register(POWER_MANAGEMENT), + dump_register(DEVICE_CONTROL), + dump_register(COMMON_USB_INTR_ENABLE), + dump_register(COMMON_USB_INTR), + dump_register(USB20_MISC_CONTROL), + dump_register(USB20_OPSTATE), +}; + +static int mtu3_link_state_show(struct seq_file *sf, void *unused) +{ + struct mtu3 *mtu = sf->private; + void __iomem *mbase = mtu->mac_base; + + seq_printf(sf, "opstate: %#x, ltssm: %#x\n", + mtu3_readl(mbase, U3D_USB20_OPSTATE), + LTSSM_STATE(mtu3_readl(mbase, U3D_LINK_STATE_MACHINE))); + + return 0; +} + +static int mtu3_ep_used_show(struct seq_file *sf, void *unused) +{ + struct mtu3 *mtu = sf->private; + struct mtu3_ep *mep; + unsigned long flags; + int used = 0; + int i; + + spin_lock_irqsave(&mtu->lock, flags); + + for (i = 0; i < mtu->num_eps; i++) { + mep = mtu->in_eps + i; + if (mep->flags & MTU3_EP_ENABLED) { + seq_printf(sf, "%s - type: %d\n", mep->name, mep->type); + used++; + } + + mep = mtu->out_eps + i; + if (mep->flags & MTU3_EP_ENABLED) { + seq_printf(sf, "%s - type: %d\n", mep->name, mep->type); + used++; + } + } + seq_printf(sf, "total used: %d eps\n", used); + + spin_unlock_irqrestore(&mtu->lock, flags); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mtu3_link_state); +DEFINE_SHOW_ATTRIBUTE(mtu3_ep_used); + +static void mtu3_debugfs_regset(struct mtu3 *mtu, void __iomem *base, + const struct debugfs_reg32 *regs, size_t nregs, + const char *name, struct dentry *parent) +{ + struct debugfs_regset32 *regset; + struct mtu3_regset *mregs; + + mregs = devm_kzalloc(mtu->dev, sizeof(*regset), GFP_KERNEL); + if (!mregs) + return; + + sprintf(mregs->name, "%s", name); + regset = &mregs->regset; + regset->regs = regs; + regset->nregs = nregs; + regset->base = base; + + debugfs_create_regset32(mregs->name, 0444, parent, regset); +} + +static void mtu3_debugfs_ep_regset(struct mtu3 *mtu, struct mtu3_ep *mep, + struct dentry *parent) +{ + struct debugfs_reg32 *regs; + int epnum = mep->epnum; + int in = mep->is_in; + + regs = devm_kcalloc(mtu->dev, 7, sizeof(*regs), GFP_KERNEL); + if (!regs) + return; + + regs[0].name = in ? "TCR0" : "RCR0"; + regs[0].offset = in ? MU3D_EP_TXCR0(epnum) : MU3D_EP_RXCR0(epnum); + regs[1].name = in ? "TCR1" : "RCR1"; + regs[1].offset = in ? MU3D_EP_TXCR1(epnum) : MU3D_EP_RXCR1(epnum); + regs[2].name = in ? "TCR2" : "RCR2"; + regs[2].offset = in ? MU3D_EP_TXCR2(epnum) : MU3D_EP_RXCR2(epnum); + regs[3].name = in ? "TQHIAR" : "RQHIAR"; + regs[3].offset = in ? USB_QMU_TQHIAR(epnum) : USB_QMU_RQHIAR(epnum); + regs[4].name = in ? "TQCSR" : "RQCSR"; + regs[4].offset = in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum); + regs[5].name = in ? "TQSAR" : "RQSAR"; + regs[5].offset = in ? USB_QMU_TQSAR(epnum) : USB_QMU_RQSAR(epnum); + regs[6].name = in ? "TQCPR" : "RQCPR"; + regs[6].offset = in ? USB_QMU_TQCPR(epnum) : USB_QMU_RQCPR(epnum); + + mtu3_debugfs_regset(mtu, mtu->mac_base, regs, 7, "ep-regs", parent); +} + +static int mtu3_ep_info_show(struct seq_file *sf, void *unused) +{ + struct mtu3_ep *mep = sf->private; + struct mtu3 *mtu = mep->mtu; + unsigned long flags; + + spin_lock_irqsave(&mtu->lock, flags); + seq_printf(sf, "ep - type:%d, maxp:%d, slot:%d, flags:%x\n", + mep->type, mep->maxp, mep->slot, mep->flags); + spin_unlock_irqrestore(&mtu->lock, flags); + + return 0; +} + +static int mtu3_fifo_show(struct seq_file *sf, void *unused) +{ + struct mtu3_ep *mep = sf->private; + struct mtu3 *mtu = mep->mtu; + unsigned long flags; + + spin_lock_irqsave(&mtu->lock, flags); + seq_printf(sf, "fifo - seg_size:%d, addr:%d, size:%d\n", + mep->fifo_seg_size, mep->fifo_addr, mep->fifo_size); + spin_unlock_irqrestore(&mtu->lock, flags); + + return 0; +} + +static int mtu3_qmu_ring_show(struct seq_file *sf, void *unused) +{ + struct mtu3_ep *mep = sf->private; + struct mtu3 *mtu = mep->mtu; + struct mtu3_gpd_ring *ring; + unsigned long flags; + + ring = &mep->gpd_ring; + spin_lock_irqsave(&mtu->lock, flags); + seq_printf(sf, + "qmu-ring - dma:%pad, start:%p, end:%p, enq:%p, dep:%p\n", + &ring->dma, ring->start, ring->end, + ring->enqueue, ring->dequeue); + spin_unlock_irqrestore(&mtu->lock, flags); + + return 0; +} + +static int mtu3_qmu_gpd_show(struct seq_file *sf, void *unused) +{ + struct mtu3_ep *mep = sf->private; + struct mtu3 *mtu = mep->mtu; + struct mtu3_gpd_ring *ring; + struct qmu_gpd *gpd; + dma_addr_t dma; + unsigned long flags; + int i; + + spin_lock_irqsave(&mtu->lock, flags); + ring = &mep->gpd_ring; + gpd = ring->start; + if (!gpd || !(mep->flags & MTU3_EP_ENABLED)) { + seq_puts(sf, "empty!\n"); + goto out; + } + + for (i = 0; i < MAX_GPD_NUM; i++, gpd++) { + dma = ring->dma + i * sizeof(*gpd); + seq_printf(sf, "gpd.%03d -> %pad, %p: %08x %08x %08x %08x\n", + i, &dma, gpd, gpd->dw0_info, gpd->next_gpd, + gpd->buffer, gpd->dw3_info); + } + +out: + spin_unlock_irqrestore(&mtu->lock, flags); + + return 0; +} + +static const struct mtu3_file_map mtu3_ep_files[] = { + {"ep-info", mtu3_ep_info_show, }, + {"fifo", mtu3_fifo_show, }, + {"qmu-ring", mtu3_qmu_ring_show, }, + {"qmu-gpd", mtu3_qmu_gpd_show, }, +}; + +static int mtu3_ep_open(struct inode *inode, struct file *file) +{ + const char *file_name = file_dentry(file)->d_iname; + const struct mtu3_file_map *f_map; + int i; + + for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) { + f_map = &mtu3_ep_files[i]; + + if (strcmp(f_map->name, file_name) == 0) + break; + } + + return single_open(file, f_map->show, inode->i_private); +} + +static const struct file_operations mtu3_ep_fops = { + .open = mtu3_ep_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct debugfs_reg32 mtu3_prb_regs[] = { + dump_prb_reg("enable", U3D_SSUSB_PRB_CTRL0), + dump_prb_reg("byte-sell", U3D_SSUSB_PRB_CTRL1), + dump_prb_reg("byte-selh", U3D_SSUSB_PRB_CTRL2), + dump_prb_reg("module-sel", U3D_SSUSB_PRB_CTRL3), + dump_prb_reg("sw-out", U3D_SSUSB_PRB_CTRL4), + dump_prb_reg("data", U3D_SSUSB_PRB_CTRL5), +}; + +static int mtu3_probe_show(struct seq_file *sf, void *unused) +{ + const char *file_name = file_dentry(sf->file)->d_iname; + struct mtu3 *mtu = sf->private; + const struct debugfs_reg32 *regs; + int i; + + for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) { + regs = &mtu3_prb_regs[i]; + + if (strcmp(regs->name, file_name) == 0) + break; + } + + seq_printf(sf, "0x%04x - 0x%08x\n", (u32)regs->offset, + mtu3_readl(mtu->ippc_base, (u32)regs->offset)); + + return 0; +} + +static int mtu3_probe_open(struct inode *inode, struct file *file) +{ + return single_open(file, mtu3_probe_show, inode->i_private); +} + +static ssize_t mtu3_probe_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + const char *file_name = file_dentry(file)->d_iname; + struct seq_file *sf = file->private_data; + struct mtu3 *mtu = sf->private; + const struct debugfs_reg32 *regs; + char buf[32]; + u32 val; + int i; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (kstrtou32(buf, 0, &val)) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) { + regs = &mtu3_prb_regs[i]; + + if (strcmp(regs->name, file_name) == 0) + break; + } + mtu3_writel(mtu->ippc_base, (u32)regs->offset, val); + + return count; +} + +static const struct file_operations mtu3_probe_fops = { + .open = mtu3_probe_open, + .write = mtu3_probe_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void mtu3_debugfs_create_prb_files(struct mtu3 *mtu) +{ + struct ssusb_mtk *ssusb = mtu->ssusb; + struct debugfs_reg32 *regs; + struct dentry *dir_prb; + int i; + + dir_prb = debugfs_create_dir("probe", ssusb->dbgfs_root); + + for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) { + regs = &mtu3_prb_regs[i]; + debugfs_create_file(regs->name, 0644, dir_prb, + mtu, &mtu3_probe_fops); + } + + mtu3_debugfs_regset(mtu, mtu->ippc_base, mtu3_prb_regs, + ARRAY_SIZE(mtu3_prb_regs), "regs", dir_prb); +} + +static void mtu3_debugfs_create_ep_dir(struct mtu3_ep *mep, + struct dentry *parent) +{ + const struct mtu3_file_map *files; + struct dentry *dir_ep; + int i; + + dir_ep = debugfs_create_dir(mep->name, parent); + mtu3_debugfs_ep_regset(mep->mtu, mep, dir_ep); + + for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) { + files = &mtu3_ep_files[i]; + + debugfs_create_file(files->name, 0444, dir_ep, + mep, &mtu3_ep_fops); + } +} + +static void mtu3_debugfs_create_ep_dirs(struct mtu3 *mtu) +{ + struct ssusb_mtk *ssusb = mtu->ssusb; + struct dentry *dir_eps; + int i; + + dir_eps = debugfs_create_dir("eps", ssusb->dbgfs_root); + + for (i = 1; i < mtu->num_eps; i++) { + mtu3_debugfs_create_ep_dir(mtu->in_eps + i, dir_eps); + mtu3_debugfs_create_ep_dir(mtu->out_eps + i, dir_eps); + } +} + +void ssusb_dev_debugfs_init(struct ssusb_mtk *ssusb) +{ + struct mtu3 *mtu = ssusb->u3d; + struct dentry *dir_regs; + + dir_regs = debugfs_create_dir("regs", ssusb->dbgfs_root); + + mtu3_debugfs_regset(mtu, mtu->ippc_base, + mtu3_ippc_regs, ARRAY_SIZE(mtu3_ippc_regs), + "reg-ippc", dir_regs); + + mtu3_debugfs_regset(mtu, mtu->mac_base, + mtu3_dev_regs, ARRAY_SIZE(mtu3_dev_regs), + "reg-dev", dir_regs); + + mtu3_debugfs_regset(mtu, mtu->mac_base, + mtu3_csr_regs, ARRAY_SIZE(mtu3_csr_regs), + "reg-csr", dir_regs); + + mtu3_debugfs_create_ep_dirs(mtu); + + mtu3_debugfs_create_prb_files(mtu); + + debugfs_create_file("link-state", 0444, ssusb->dbgfs_root, + mtu, &mtu3_link_state_fops); + debugfs_create_file("ep-used", 0444, ssusb->dbgfs_root, + mtu, &mtu3_ep_used_fops); +} + +static int ssusb_mode_show(struct seq_file *sf, void *unused) +{ + struct ssusb_mtk *ssusb = sf->private; + + seq_printf(sf, "current mode: %s(%s drd)\n(echo device/host)\n", + ssusb->is_host ? "host" : "device", + ssusb->otg_switch.manual_drd_enabled ? "manual" : "auto"); + + return 0; +} + +static int ssusb_mode_open(struct inode *inode, struct file *file) +{ + return single_open(file, ssusb_mode_show, inode->i_private); +} + +static ssize_t ssusb_mode_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *sf = file->private_data; + struct ssusb_mtk *ssusb = sf->private; + char buf[16]; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "host", 4) && !ssusb->is_host) { + ssusb_mode_manual_switch(ssusb, 1); + } else if (!strncmp(buf, "device", 6) && ssusb->is_host) { + ssusb_mode_manual_switch(ssusb, 0); + } else { + dev_err(ssusb->dev, "wrong or duplicated setting\n"); + return -EINVAL; + } + + return count; +} + +static const struct file_operations ssusb_mode_fops = { + .open = ssusb_mode_open, + .write = ssusb_mode_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int ssusb_vbus_show(struct seq_file *sf, void *unused) +{ + struct ssusb_mtk *ssusb = sf->private; + struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + + seq_printf(sf, "vbus state: %s\n(echo on/off)\n", + regulator_is_enabled(otg_sx->vbus) ? "on" : "off"); + + return 0; +} + +static int ssusb_vbus_open(struct inode *inode, struct file *file) +{ + return single_open(file, ssusb_vbus_show, inode->i_private); +} + +static ssize_t ssusb_vbus_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct seq_file *sf = file->private_data; + struct ssusb_mtk *ssusb = sf->private; + struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + char buf[16]; + bool enable; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (kstrtobool(buf, &enable)) { + dev_err(ssusb->dev, "wrong setting\n"); + return -EINVAL; + } + + ssusb_set_vbus(otg_sx, enable); + + return count; +} + +static const struct file_operations ssusb_vbus_fops = { + .open = ssusb_vbus_open, + .write = ssusb_vbus_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void ssusb_dr_debugfs_init(struct ssusb_mtk *ssusb) +{ + struct dentry *root = ssusb->dbgfs_root; + + debugfs_create_file("mode", 0644, root, ssusb, &ssusb_mode_fops); + debugfs_create_file("vbus", 0644, root, ssusb, &ssusb_vbus_fops); +} + +void ssusb_debugfs_create_root(struct ssusb_mtk *ssusb) +{ + ssusb->dbgfs_root = + debugfs_create_dir(dev_name(ssusb->dev), usb_debug_root); +} + +void ssusb_debugfs_remove_root(struct ssusb_mtk *ssusb) +{ + debugfs_remove_recursive(ssusb->dbgfs_root); + ssusb->dbgfs_root = NULL; +} diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c index ac60e9c8564e..5fcb71af875a 100644 --- a/drivers/usb/mtu3/mtu3_dr.c +++ b/drivers/usb/mtu3/mtu3_dr.c @@ -7,16 +7,9 @@ * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> */ -#include <linux/debugfs.h> -#include <linux/irq.h> -#include <linux/kernel.h> -#include <linux/of_device.h> -#include <linux/pinctrl/consumer.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> - #include "mtu3.h" #include "mtu3_dr.h" +#include "mtu3_debug.h" #define USB2_PORT 2 #define USB3_PORT 3 @@ -28,6 +21,22 @@ enum mtu3_vbus_id_state { MTU3_VBUS_VALID, }; +static char *mailbox_state_string(enum mtu3_vbus_id_state state) +{ + switch (state) { + case MTU3_ID_FLOAT: + return "ID_FLOAT"; + case MTU3_ID_GROUND: + return "ID_GROUND"; + case MTU3_VBUS_OFF: + return "VBUS_OFF"; + case MTU3_VBUS_VALID: + return "VBUS_VALID"; + default: + return "UNKNOWN"; + } +} + static void toggle_opstate(struct ssusb_mtk *ssusb) { if (!ssusb->otg_switch.is_u3_drd) { @@ -147,7 +156,8 @@ static void ssusb_set_mailbox(struct otg_switch_mtk *otg_sx, container_of(otg_sx, struct ssusb_mtk, otg_switch); struct mtu3 *mtu = ssusb->u3d; - dev_dbg(ssusb->dev, "mailbox state(%d)\n", status); + dev_dbg(ssusb->dev, "mailbox %s\n", mailbox_state_string(status)); + mtu3_dbg_trace(ssusb->dev, "mailbox %s", mailbox_state_string(status)); switch (status) { case MTU3_ID_GROUND: @@ -238,14 +248,18 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx) otg_sx->vbus_nb.notifier_call = ssusb_vbus_notifier; ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB, &otg_sx->vbus_nb); - if (ret < 0) + if (ret < 0) { dev_err(ssusb->dev, "failed to register notifier for USB\n"); + return ret; + } otg_sx->id_nb.notifier_call = ssusb_id_notifier; ret = devm_extcon_register_notifier(ssusb->dev, edev, EXTCON_USB_HOST, &otg_sx->id_nb); - if (ret < 0) + if (ret < 0) { dev_err(ssusb->dev, "failed to register notifier for USB-HOST\n"); + return ret; + } dev_dbg(ssusb->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n", extcon_get_state(edev, EXTCON_USB), @@ -266,7 +280,7 @@ static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx) * This is useful in special cases, such as uses TYPE-A receptacle but also * wants to support dual-role mode. */ -static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) +void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) { struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; @@ -281,114 +295,6 @@ static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) } } -static int ssusb_mode_show(struct seq_file *sf, void *unused) -{ - struct ssusb_mtk *ssusb = sf->private; - - seq_printf(sf, "current mode: %s(%s drd)\n(echo device/host)\n", - ssusb->is_host ? "host" : "device", - ssusb->otg_switch.manual_drd_enabled ? "manual" : "auto"); - - return 0; -} - -static int ssusb_mode_open(struct inode *inode, struct file *file) -{ - return single_open(file, ssusb_mode_show, inode->i_private); -} - -static ssize_t ssusb_mode_write(struct file *file, - const char __user *ubuf, size_t count, loff_t *ppos) -{ - struct seq_file *sf = file->private_data; - struct ssusb_mtk *ssusb = sf->private; - char buf[16]; - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) - return -EFAULT; - - if (!strncmp(buf, "host", 4) && !ssusb->is_host) { - ssusb_mode_manual_switch(ssusb, 1); - } else if (!strncmp(buf, "device", 6) && ssusb->is_host) { - ssusb_mode_manual_switch(ssusb, 0); - } else { - dev_err(ssusb->dev, "wrong or duplicated setting\n"); - return -EINVAL; - } - - return count; -} - -static const struct file_operations ssusb_mode_fops = { - .open = ssusb_mode_open, - .write = ssusb_mode_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int ssusb_vbus_show(struct seq_file *sf, void *unused) -{ - struct ssusb_mtk *ssusb = sf->private; - struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - - seq_printf(sf, "vbus state: %s\n(echo on/off)\n", - regulator_is_enabled(otg_sx->vbus) ? "on" : "off"); - - return 0; -} - -static int ssusb_vbus_open(struct inode *inode, struct file *file) -{ - return single_open(file, ssusb_vbus_show, inode->i_private); -} - -static ssize_t ssusb_vbus_write(struct file *file, - const char __user *ubuf, size_t count, loff_t *ppos) -{ - struct seq_file *sf = file->private_data; - struct ssusb_mtk *ssusb = sf->private; - struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - char buf[16]; - bool enable; - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) - return -EFAULT; - - if (kstrtobool(buf, &enable)) { - dev_err(ssusb->dev, "wrong setting\n"); - return -EINVAL; - } - - ssusb_set_vbus(otg_sx, enable); - - return count; -} - -static const struct file_operations ssusb_vbus_fops = { - .open = ssusb_vbus_open, - .write = ssusb_vbus_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void ssusb_debugfs_init(struct ssusb_mtk *ssusb) -{ - struct dentry *root; - - root = debugfs_create_dir(dev_name(ssusb->dev), usb_debug_root); - ssusb->dbgfs_root = root; - - debugfs_create_file("mode", 0644, root, ssusb, &ssusb_mode_fops); - debugfs_create_file("vbus", 0644, root, ssusb, &ssusb_vbus_fops); -} - -static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb) -{ - debugfs_remove_recursive(ssusb->dbgfs_root); -} - void ssusb_set_force_mode(struct ssusb_mtk *ssusb, enum mtu3_dr_force_mode mode) { @@ -415,25 +321,23 @@ void ssusb_set_force_mode(struct ssusb_mtk *ssusb, int ssusb_otg_switch_init(struct ssusb_mtk *ssusb) { struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + int ret = 0; INIT_WORK(&otg_sx->id_work, ssusb_id_work); INIT_WORK(&otg_sx->vbus_work, ssusb_vbus_work); if (otg_sx->manual_drd_enabled) - ssusb_debugfs_init(ssusb); + ssusb_dr_debugfs_init(ssusb); else - ssusb_extcon_register(otg_sx); + ret = ssusb_extcon_register(otg_sx); - return 0; + return ret; } void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb) { struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; - if (otg_sx->manual_drd_enabled) - ssusb_debugfs_exit(ssusb); - cancel_work_sync(&otg_sx->id_work); cancel_work_sync(&otg_sx->vbus_work); } diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h index 50702fdcde28..ba6fe357ce29 100644 --- a/drivers/usb/mtu3/mtu3_dr.h +++ b/drivers/usb/mtu3/mtu3_dr.h @@ -71,6 +71,7 @@ static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb) #if IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE) int ssusb_otg_switch_init(struct ssusb_mtk *ssusb); void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb); +void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host); int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on); void ssusb_set_force_mode(struct ssusb_mtk *ssusb, enum mtu3_dr_force_mode mode); @@ -85,6 +86,9 @@ static inline int ssusb_otg_switch_init(struct ssusb_mtk *ssusb) static inline void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb) {} +static inline void +ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host) {} + static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on) { return 0; diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index bbcd3332471d..f93732e53fd8 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -8,6 +8,7 @@ */ #include "mtu3.h" +#include "mtu3_trace.h" void mtu3_req_complete(struct mtu3_ep *mep, struct usb_request *req, int status) @@ -25,6 +26,8 @@ __acquires(mep->mtu->lock) mtu = mreq->mtu; mep->busy = 1; + + trace_mtu3_req_complete(mreq); spin_unlock(&mtu->lock); /* ep0 makes use of PIO, needn't unmap it */ @@ -201,6 +204,7 @@ error: spin_unlock_irqrestore(&mtu->lock, flags); dev_dbg(mtu->dev, "%s active_ep=%d\n", __func__, mtu->active_ep); + trace_mtu3_gadget_ep_enable(mep); return ret; } @@ -212,6 +216,7 @@ static int mtu3_gadget_ep_disable(struct usb_ep *ep) unsigned long flags; dev_dbg(mtu->dev, "%s %s\n", __func__, mep->name); + trace_mtu3_gadget_ep_disable(mep); if (!(mep->flags & MTU3_EP_ENABLED)) { dev_warn(mtu->dev, "%s is already disabled\n", mep->name); @@ -242,13 +247,17 @@ struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) mreq->request.dma = DMA_ADDR_INVALID; mreq->epnum = mep->epnum; mreq->mep = mep; + trace_mtu3_alloc_request(mreq); return &mreq->request; } void mtu3_free_request(struct usb_ep *ep, struct usb_request *req) { - kfree(to_mtu3_request(req)); + struct mtu3_request *mreq = to_mtu3_request(req); + + trace_mtu3_free_request(mreq); + kfree(mreq); } static int mtu3_gadget_queue(struct usb_ep *ep, @@ -278,10 +287,12 @@ static int mtu3_gadget_queue(struct usb_ep *ep, __func__, mep->is_in ? "TX" : "RX", mreq->epnum, ep->name, mreq, ep->maxpacket, mreq->request.length); - if (req->length > GPD_BUF_SIZE) { + if (req->length > GPD_BUF_SIZE || + (mtu->gen2cp && req->length > GPD_BUF_SIZE_EL)) { dev_warn(mtu->dev, "req length > supported MAX:%d requested:%d\n", - GPD_BUF_SIZE, req->length); + mtu->gen2cp ? GPD_BUF_SIZE_EL : GPD_BUF_SIZE, + req->length); return -EOPNOTSUPP; } @@ -314,6 +325,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep, error: spin_unlock_irqrestore(&mtu->lock, flags); + trace_mtu3_gadget_queue(mreq); return ret; } @@ -331,6 +343,7 @@ static int mtu3_gadget_dequeue(struct usb_ep *ep, struct usb_request *req) return -EINVAL; dev_dbg(mtu->dev, "%s : req=%p\n", __func__, req); + trace_mtu3_gadget_dequeue(mreq); spin_lock_irqsave(&mtu->lock, flags); @@ -401,6 +414,7 @@ static int mtu3_gadget_ep_set_halt(struct usb_ep *ep, int value) done: spin_unlock_irqrestore(&mtu->lock, flags); + trace_mtu3_gadget_ep_set_halt(mep); return ret; } diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 7cb7ac980446..4da216c99726 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -11,6 +11,8 @@ #include <linux/usb/composite.h> #include "mtu3.h" +#include "mtu3_debug.h" +#include "mtu3_trace.h" /* ep0 is always mtu3->in_eps[0] */ #define next_ep0_request(mtu) next_request((mtu)->ep0) @@ -634,6 +636,7 @@ __acquires(mtu->lock) int handled = 0; ep0_read_setup(mtu, &setup); + trace_mtu3_handle_setup(&setup); if ((setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) handled = handle_standard_request(mtu, &setup); @@ -710,6 +713,7 @@ irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu) ret = IRQ_HANDLED; } dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu)); + mtu3_dbg_trace(mtu->dev, "ep0_state %s", decode_ep0_state(mtu)); switch (mtu->ep0_state) { case MU3D_EP0_STATE_TX: diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index 1d65b7476f23..8382d066749e 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -49,6 +49,7 @@ #define U3D_QCR1 (SSUSB_DEV_BASE + 0x0404) #define U3D_QCR2 (SSUSB_DEV_BASE + 0x0408) #define U3D_QCR3 (SSUSB_DEV_BASE + 0x040C) +#define U3D_QFCR (SSUSB_DEV_BASE + 0x0428) #define U3D_TXQHIAR1 (SSUSB_DEV_BASE + 0x0484) #define U3D_RXQHIAR1 (SSUSB_DEV_BASE + 0x04C4) @@ -133,11 +134,23 @@ #define TX_W1C_BITS (~(TX_SENTSTALL)) /* U3D_TX1CSR1 */ -#define TX_MULT(x) (((x) & 0x3) << 22) -#define TX_MAX_PKT(x) (((x) & 0x3f) << 16) +#define TX_MAX_PKT_G2(x) (((x) & 0x7f) << 24) +#define TX_MULT_G2(x) (((x) & 0x7) << 21) +#define TX_MULT_OG(x) (((x) & 0x3) << 22) +#define TX_MAX_PKT_OG(x) (((x) & 0x3f) << 16) #define TX_SLOT(x) (((x) & 0x3f) << 8) #define TX_TYPE(x) (((x) & 0x3) << 4) #define TX_SS_BURST(x) (((x) & 0xf) << 0) +#define TX_MULT(g2c, x) \ +({ \ + typeof(x) x_ = (x); \ + (g2c) ? TX_MULT_G2(x_) : TX_MULT_OG(x_); \ +}) +#define TX_MAX_PKT(g2c, x) \ +({ \ + typeof(x) x_ = (x); \ + (g2c) ? TX_MAX_PKT_G2(x_) : TX_MAX_PKT_OG(x_); \ +}) /* for TX_TYPE & RX_TYPE */ #define TYPE_BULK (0x0) @@ -160,11 +173,23 @@ #define RX_W1C_BITS (~(RX_SENTSTALL | RX_RXPKTRDY)) /* U3D_RX1CSR1 */ -#define RX_MULT(x) (((x) & 0x3) << 22) -#define RX_MAX_PKT(x) (((x) & 0x3f) << 16) +#define RX_MAX_PKT_G2(x) (((x) & 0x7f) << 24) +#define RX_MULT_G2(x) (((x) & 0x7) << 21) +#define RX_MULT_OG(x) (((x) & 0x3) << 22) +#define RX_MAX_PKT_OG(x) (((x) & 0x3f) << 16) #define RX_SLOT(x) (((x) & 0x3f) << 8) #define RX_TYPE(x) (((x) & 0x3) << 4) #define RX_SS_BURST(x) (((x) & 0xf) << 0) +#define RX_MULT(g2c, x) \ +({ \ + typeof(x) x_ = (x); \ + (g2c) ? RX_MULT_G2(x_) : RX_MULT_OG(x_); \ +}) +#define RX_MAX_PKT(g2c, x) \ +({ \ + typeof(x) x_ = (x); \ + (g2c) ? RX_MAX_PKT_G2(x_) : RX_MAX_PKT_OG(x_); \ +}) /* U3D_RX1CSR2 */ #define RX_BINTERVAL(x) (((x) & 0xff) << 24) @@ -265,6 +290,7 @@ #define U3D_LTSSM_CTRL (SSUSB_USB3_MAC_CSR_BASE + 0x0010) #define U3D_USB3_CONFIG (SSUSB_USB3_MAC_CSR_BASE + 0x001C) +#define U3D_LINK_STATE_MACHINE (SSUSB_USB3_MAC_CSR_BASE + 0x0134) #define U3D_LTSSM_INTR_ENABLE (SSUSB_USB3_MAC_CSR_BASE + 0x013C) #define U3D_LTSSM_INTR (SSUSB_USB3_MAC_CSR_BASE + 0x0140) @@ -282,6 +308,9 @@ /* U3D_USB3_CONFIG */ #define USB3_EN BIT(0) +/* U3D_LINK_STATE_MACHINE */ +#define LTSSM_STATE(x) ((x) & 0x1f) + /* U3D_LTSSM_INTR_ENABLE */ /* U3D_LTSSM_INTR */ #define U3_RESUME_INTR BIT(18) @@ -347,6 +376,7 @@ #define U3D_USB20_FRAME_NUM (SSUSB_USB2_CSR_BASE + 0x003C) #define U3D_USB20_LPM_PARAMETER (SSUSB_USB2_CSR_BASE + 0x0044) #define U3D_USB20_MISC_CONTROL (SSUSB_USB2_CSR_BASE + 0x004C) +#define U3D_USB20_OPSTATE (SSUSB_USB2_CSR_BASE + 0x0060) /*---------------- SSUSB_USB2_CSR FIELD DEFINITION ----------------*/ @@ -419,6 +449,13 @@ #define U3D_SSUSB_DEV_RST_CTRL (SSUSB_SIFSLV_IPPC_BASE + 0x0098) #define U3D_SSUSB_HW_ID (SSUSB_SIFSLV_IPPC_BASE + 0x00A0) #define U3D_SSUSB_HW_SUB_ID (SSUSB_SIFSLV_IPPC_BASE + 0x00A4) +#define U3D_SSUSB_IP_TRUNK_VERS (U3D_SSUSB_HW_SUB_ID) +#define U3D_SSUSB_PRB_CTRL0 (SSUSB_SIFSLV_IPPC_BASE + 0x00B0) +#define U3D_SSUSB_PRB_CTRL1 (SSUSB_SIFSLV_IPPC_BASE + 0x00B4) +#define U3D_SSUSB_PRB_CTRL2 (SSUSB_SIFSLV_IPPC_BASE + 0x00B8) +#define U3D_SSUSB_PRB_CTRL3 (SSUSB_SIFSLV_IPPC_BASE + 0x00BC) +#define U3D_SSUSB_PRB_CTRL4 (SSUSB_SIFSLV_IPPC_BASE + 0x00C0) +#define U3D_SSUSB_PRB_CTRL5 (SSUSB_SIFSLV_IPPC_BASE + 0x00C4) #define U3D_SSUSB_IP_SPARE0 (SSUSB_SIFSLV_IPPC_BASE + 0x00C8) /*---------------- SSUSB_SIFSLV_IPPC FIELD DEFINITION ----------------*/ @@ -483,4 +520,7 @@ /* U3D_SSUSB_DEV_RST_CTRL */ #define SSUSB_DEV_SW_RST BIT(0) +/* U3D_SSUSB_IP_TRUNK_VERS */ +#define IP_TRUNK_VERS(x) (((x) >> 16) & 0xffff) + #endif /* _SSUSB_HW_REGS_H_ */ diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index e086630e41a9..fd0f6c5dfbc1 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -16,6 +16,7 @@ #include "mtu3.h" #include "mtu3_dr.h" +#include "mtu3_debug.h" /* u2-port0 should be powered on and enabled; */ int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks) @@ -210,30 +211,16 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb) mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); } -/* ignore the error if the clock does not exist */ -static struct clk *get_optional_clk(struct device *dev, const char *id) -{ - struct clk *opt_clk; - - opt_clk = devm_clk_get(dev, id); - /* ignore error number except EPROBE_DEFER */ - if (IS_ERR(opt_clk) && (PTR_ERR(opt_clk) != -EPROBE_DEFER)) - opt_clk = NULL; - - return opt_clk; -} - static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) { struct device_node *node = pdev->dev.of_node; struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; struct device *dev = &pdev->dev; - struct regulator *vbus; struct resource *res; int i; int ret; - ssusb->vusb33 = devm_regulator_get(&pdev->dev, "vusb33"); + ssusb->vusb33 = devm_regulator_get(dev, "vusb33"); if (IS_ERR(ssusb->vusb33)) { dev_err(dev, "failed to get vusb33\n"); return PTR_ERR(ssusb->vusb33); @@ -245,15 +232,15 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) return PTR_ERR(ssusb->sys_clk); } - ssusb->ref_clk = get_optional_clk(dev, "ref_ck"); + ssusb->ref_clk = devm_clk_get_optional(dev, "ref_ck"); if (IS_ERR(ssusb->ref_clk)) return PTR_ERR(ssusb->ref_clk); - ssusb->mcu_clk = get_optional_clk(dev, "mcu_ck"); + ssusb->mcu_clk = devm_clk_get_optional(dev, "mcu_ck"); if (IS_ERR(ssusb->mcu_clk)) return PTR_ERR(ssusb->mcu_clk); - ssusb->dma_clk = get_optional_clk(dev, "dma_ck"); + ssusb->dma_clk = devm_clk_get_optional(dev, "dma_ck"); if (IS_ERR(ssusb->dma_clk)) return PTR_ERR(ssusb->dma_clk); @@ -286,7 +273,7 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) ssusb->dr_mode = USB_DR_MODE_OTG; if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL) - return 0; + goto out; /* if host role is supported */ ret = ssusb_wakeup_of_property_parse(ssusb, node); @@ -299,15 +286,14 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) of_property_read_u32(node, "mediatek,u3p-dis-msk", &ssusb->u3p_dis_msk); - vbus = devm_regulator_get(&pdev->dev, "vbus"); - if (IS_ERR(vbus)) { + otg_sx->vbus = devm_regulator_get(dev, "vbus"); + if (IS_ERR(otg_sx->vbus)) { dev_err(dev, "failed to get vbus\n"); - return PTR_ERR(vbus); + return PTR_ERR(otg_sx->vbus); } - otg_sx->vbus = vbus; if (ssusb->dr_mode == USB_DR_MODE_HOST) - return 0; + goto out; /* if dual-role mode is supported */ otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd"); @@ -322,6 +308,7 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) } } +out: dev_info(dev, "dr_mode: %d, is_u3_dr: %d, u3p_dis_msk: %x, drd: %s\n", ssusb->dr_mode, otg_sx->is_u3_drd, ssusb->u3p_dis_msk, otg_sx->manual_drd_enabled ? "manual" : "auto"); @@ -354,6 +341,8 @@ static int mtu3_probe(struct platform_device *pdev) if (ret) return ret; + ssusb_debugfs_create_root(ssusb); + /* enable power domain */ pm_runtime_enable(dev); pm_runtime_get_sync(dev); @@ -401,7 +390,11 @@ static int mtu3_probe(struct platform_device *pdev) goto gadget_exit; } - ssusb_otg_switch_init(ssusb); + ret = ssusb_otg_switch_init(ssusb); + if (ret) { + dev_err(dev, "failed to initialize switch\n"); + goto host_exit; + } break; default: dev_err(dev, "unsupported mode: %d\n", ssusb->dr_mode); @@ -411,6 +404,8 @@ static int mtu3_probe(struct platform_device *pdev) return 0; +host_exit: + ssusb_host_exit(ssusb); gadget_exit: ssusb_gadget_exit(ssusb); comm_exit: @@ -418,6 +413,7 @@ comm_exit: comm_init_err: pm_runtime_put_sync(dev); pm_runtime_disable(dev); + ssusb_debugfs_remove_root(ssusb); return ret; } @@ -445,6 +441,7 @@ static int mtu3_remove(struct platform_device *pdev) ssusb_rscs_exit(ssusb); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + ssusb_debugfs_remove_root(ssusb); return 0; } diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index 09f19f70fe8f..3f414f91b589 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -22,17 +22,49 @@ #include <linux/iopoll.h> #include "mtu3.h" +#include "mtu3_trace.h" #define QMU_CHECKSUM_LEN 16 #define GPD_FLAGS_HWO BIT(0) #define GPD_FLAGS_BDP BIT(1) #define GPD_FLAGS_BPS BIT(2) +#define GPD_FLAGS_ZLP BIT(6) #define GPD_FLAGS_IOC BIT(7) - -#define GPD_EXT_FLAG_ZLP BIT(5) -#define GPD_EXT_NGP(x) (((x) & 0xf) << 4) -#define GPD_EXT_BUF(x) (((x) & 0xf) << 0) +#define GET_GPD_HWO(gpd) (le32_to_cpu((gpd)->dw0_info) & GPD_FLAGS_HWO) + +#define GPD_RX_BUF_LEN_OG(x) (((x) & 0xffff) << 16) +#define GPD_RX_BUF_LEN_EL(x) (((x) & 0xfffff) << 12) +#define GPD_RX_BUF_LEN(mtu, x) \ +({ \ + typeof(x) x_ = (x); \ + ((mtu)->gen2cp) ? GPD_RX_BUF_LEN_EL(x_) : GPD_RX_BUF_LEN_OG(x_); \ +}) + +#define GPD_DATA_LEN_OG(x) ((x) & 0xffff) +#define GPD_DATA_LEN_EL(x) ((x) & 0xfffff) +#define GPD_DATA_LEN(mtu, x) \ +({ \ + typeof(x) x_ = (x); \ + ((mtu)->gen2cp) ? GPD_DATA_LEN_EL(x_) : GPD_DATA_LEN_OG(x_); \ +}) + +#define GPD_EXT_FLAG_ZLP BIT(29) +#define GPD_EXT_NGP_OG(x) (((x) & 0xf) << 20) +#define GPD_EXT_BUF_OG(x) (((x) & 0xf) << 16) +#define GPD_EXT_NGP_EL(x) (((x) & 0xf) << 28) +#define GPD_EXT_BUF_EL(x) (((x) & 0xf) << 24) +#define GPD_EXT_NGP(mtu, x) \ +({ \ + typeof(x) x_ = (x); \ + ((mtu)->gen2cp) ? GPD_EXT_NGP_EL(x_) : GPD_EXT_NGP_OG(x_); \ +}) + +#define GPD_EXT_BUF(mtu, x) \ +({ \ + typeof(x) x_ = (x); \ + ((mtu)->gen2cp) ? GPD_EXT_BUF_EL(x_) : GPD_EXT_BUF_OG(x_); \ +}) #define HILO_GEN64(hi, lo) (((u64)(hi) << 32) + (lo)) #define HILO_DMA(hi, lo) \ @@ -125,7 +157,7 @@ static void reset_gpd_list(struct mtu3_ep *mep) struct qmu_gpd *gpd = ring->start; if (gpd) { - gpd->flag &= ~GPD_FLAGS_HWO; + gpd->dw0_info &= cpu_to_le32(~GPD_FLAGS_HWO); gpd_ring_init(ring, gpd); } } @@ -214,16 +246,14 @@ static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) struct mtu3_gpd_ring *ring = &mep->gpd_ring; struct qmu_gpd *gpd = ring->enqueue; struct usb_request *req = &mreq->request; + struct mtu3 *mtu = mep->mtu; dma_addr_t enq_dma; - u16 ext_addr; - - /* set all fields to zero as default value */ - memset(gpd, 0, sizeof(*gpd)); + u32 ext_addr; + gpd->dw0_info = 0; /* SW own it */ gpd->buffer = cpu_to_le32(lower_32_bits(req->dma)); - ext_addr = GPD_EXT_BUF(upper_32_bits(req->dma)); - gpd->buf_len = cpu_to_le16(req->length); - gpd->flag |= GPD_FLAGS_IOC; + ext_addr = GPD_EXT_BUF(mtu, upper_32_bits(req->dma)); + gpd->dw3_info = cpu_to_le32(GPD_DATA_LEN(mtu, req->length)); /* get the next GPD */ enq = advance_enq_gpd(ring); @@ -231,17 +261,22 @@ static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p, qdma=%pad\n", mep->epnum, gpd, enq, &enq_dma); - enq->flag &= ~GPD_FLAGS_HWO; + enq->dw0_info &= cpu_to_le32(~GPD_FLAGS_HWO); gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma)); - ext_addr |= GPD_EXT_NGP(upper_32_bits(enq_dma)); - gpd->tx_ext_addr = cpu_to_le16(ext_addr); - - if (req->zero) - gpd->ext_flag |= GPD_EXT_FLAG_ZLP; + ext_addr |= GPD_EXT_NGP(mtu, upper_32_bits(enq_dma)); + gpd->dw0_info = cpu_to_le32(ext_addr); + + if (req->zero) { + if (mtu->gen2cp) + gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_ZLP); + else + gpd->dw3_info |= cpu_to_le32(GPD_EXT_FLAG_ZLP); + } - gpd->flag |= GPD_FLAGS_HWO; + gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_IOC | GPD_FLAGS_HWO); mreq->gpd = gpd; + trace_mtu3_prepare_gpd(mep, gpd); return 0; } @@ -252,16 +287,14 @@ static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) struct mtu3_gpd_ring *ring = &mep->gpd_ring; struct qmu_gpd *gpd = ring->enqueue; struct usb_request *req = &mreq->request; + struct mtu3 *mtu = mep->mtu; dma_addr_t enq_dma; - u16 ext_addr; - - /* set all fields to zero as default value */ - memset(gpd, 0, sizeof(*gpd)); + u32 ext_addr; + gpd->dw0_info = 0; /* SW own it */ gpd->buffer = cpu_to_le32(lower_32_bits(req->dma)); - ext_addr = GPD_EXT_BUF(upper_32_bits(req->dma)); - gpd->data_buf_len = cpu_to_le16(req->length); - gpd->flag |= GPD_FLAGS_IOC; + ext_addr = GPD_EXT_BUF(mtu, upper_32_bits(req->dma)); + gpd->dw0_info = cpu_to_le32(GPD_RX_BUF_LEN(mtu, req->length)); /* get the next GPD */ enq = advance_enq_gpd(ring); @@ -269,13 +302,14 @@ static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p, qdma=%pad\n", mep->epnum, gpd, enq, &enq_dma); - enq->flag &= ~GPD_FLAGS_HWO; + enq->dw0_info &= cpu_to_le32(~GPD_FLAGS_HWO); gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma)); - ext_addr |= GPD_EXT_NGP(upper_32_bits(enq_dma)); - gpd->rx_ext_addr = cpu_to_le16(ext_addr); - gpd->flag |= GPD_FLAGS_HWO; + ext_addr |= GPD_EXT_NGP(mtu, upper_32_bits(enq_dma)); + gpd->dw3_info = cpu_to_le32(ext_addr); + gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_IOC | GPD_FLAGS_HWO); mreq->gpd = gpd; + trace_mtu3_prepare_gpd(mep, gpd); return 0; } @@ -382,27 +416,25 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) struct mtu3_gpd_ring *ring = &mep->gpd_ring; void __iomem *mbase = mtu->mac_base; struct qmu_gpd *gpd_current = NULL; - struct usb_request *req = NULL; struct mtu3_request *mreq; dma_addr_t cur_gpd_dma; u32 txcsr = 0; int ret; mreq = next_request(mep); - if (mreq && mreq->request.length == 0) - req = &mreq->request; - else + if (mreq && mreq->request.length != 0) return; cur_gpd_dma = read_txq_cur_addr(mbase, epnum); gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma); - if (le16_to_cpu(gpd_current->buf_len) != 0) { + if (GPD_DATA_LEN(mtu, le32_to_cpu(gpd_current->dw3_info)) != 0) { dev_err(mtu->dev, "TX EP%d buffer length error(!=0)\n", epnum); return; } - dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, req); + dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq); + trace_mtu3_zlp_exp_gpd(mep, gpd_current); mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN); @@ -415,8 +447,7 @@ static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_TXPKTRDY); /* by pass the current GDP */ - gpd_current->flag |= GPD_FLAGS_BPS; - gpd_current->flag |= GPD_FLAGS_HWO; + gpd_current->dw0_info |= cpu_to_le32(GPD_FLAGS_BPS | GPD_FLAGS_HWO); /*enable DMAREQEN, switch back to QMU mode */ mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN); @@ -448,7 +479,7 @@ static void qmu_done_tx(struct mtu3 *mtu, u8 epnum) dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n", __func__, epnum, gpd, gpd_current, ring->enqueue); - while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) { + while (gpd != gpd_current && !GET_GPD_HWO(gpd)) { mreq = next_request(mep); @@ -458,7 +489,8 @@ static void qmu_done_tx(struct mtu3 *mtu, u8 epnum) } request = &mreq->request; - request->actual = le16_to_cpu(gpd->buf_len); + request->actual = GPD_DATA_LEN(mtu, le32_to_cpu(gpd->dw3_info)); + trace_mtu3_complete_gpd(mep, gpd); mtu3_req_complete(mep, request, 0); gpd = advance_deq_gpd(ring); @@ -486,7 +518,7 @@ static void qmu_done_rx(struct mtu3 *mtu, u8 epnum) dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n", __func__, epnum, gpd, gpd_current, ring->enqueue); - while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) { + while (gpd != gpd_current && !GET_GPD_HWO(gpd)) { mreq = next_request(mep); @@ -496,7 +528,8 @@ static void qmu_done_rx(struct mtu3 *mtu, u8 epnum) } req = &mreq->request; - req->actual = le16_to_cpu(gpd->buf_len); + req->actual = GPD_DATA_LEN(mtu, le32_to_cpu(gpd->dw3_info)); + trace_mtu3_complete_gpd(mep, gpd); mtu3_req_complete(mep, req, 0); gpd = advance_deq_gpd(ring); @@ -574,6 +607,7 @@ irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu) dev_dbg(mtu->dev, "=== QMUdone[tx=%x, rx=%x] QMUexp[%x] ===\n", (qmu_done_status & 0xFFFF), qmu_done_status >> 16, qmu_status); + trace_mtu3_qmu_isr(qmu_done_status, qmu_status); if (qmu_done_status) qmu_done_isr(mtu, qmu_done_status); diff --git a/drivers/usb/mtu3/mtu3_qmu.h b/drivers/usb/mtu3/mtu3_qmu.h index 81f5151a55ed..9cfde201db63 100644 --- a/drivers/usb/mtu3/mtu3_qmu.h +++ b/drivers/usb/mtu3/mtu3_qmu.h @@ -15,6 +15,7 @@ #define QMU_GPD_RING_SIZE (MAX_GPD_NUM * QMU_GPD_SIZE) #define GPD_BUF_SIZE 65532 +#define GPD_BUF_SIZE_EL 1048572 void mtu3_qmu_stop(struct mtu3_ep *mep); int mtu3_qmu_start(struct mtu3_ep *mep); diff --git a/drivers/usb/mtu3/mtu3_trace.c b/drivers/usb/mtu3/mtu3_trace.c new file mode 100644 index 000000000000..4f5e7857ec31 --- /dev/null +++ b/drivers/usb/mtu3/mtu3_trace.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * mtu3_trace.c - trace support + * + * Copyright (C) 2019 MediaTek Inc. + * + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#define CREATE_TRACE_POINTS +#include "mtu3_trace.h" + +void mtu3_dbg_trace(struct device *dev, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + trace_mtu3_log(dev, &vaf); + va_end(args); +} diff --git a/drivers/usb/mtu3/mtu3_trace.h b/drivers/usb/mtu3/mtu3_trace.h new file mode 100644 index 000000000000..050e30f0fbd4 --- /dev/null +++ b/drivers/usb/mtu3/mtu3_trace.h @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * mtu3_trace.h - trace support + * + * Copyright (C) 2019 MediaTek Inc. + * + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mtu3 + +#if !defined(__MTU3_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) +#define __MTU3_TRACE_H__ + +#include <linux/types.h> +#include <linux/tracepoint.h> + +#include "mtu3.h" + +#define MTU3_MSG_MAX 256 + +TRACE_EVENT(mtu3_log, + TP_PROTO(struct device *dev, struct va_format *vaf), + TP_ARGS(dev, vaf), + TP_STRUCT__entry( + __string(name, dev_name(dev)) + __dynamic_array(char, msg, MTU3_MSG_MAX) + ), + TP_fast_assign( + __assign_str(name, dev_name(dev)); + vsnprintf(__get_str(msg), MTU3_MSG_MAX, vaf->fmt, *vaf->va); + ), + TP_printk("%s: %s", __get_str(name), __get_str(msg)) +); + +TRACE_EVENT(mtu3_u3_ltssm_isr, + TP_PROTO(u32 intr), + TP_ARGS(intr), + TP_STRUCT__entry( + __field(u32, intr) + ), + TP_fast_assign( + __entry->intr = intr; + ), + TP_printk("(%08x) %s %s %s %s %s %s", __entry->intr, + __entry->intr & HOT_RST_INTR ? "HOT_RST" : "", + __entry->intr & WARM_RST_INTR ? "WARM_RST" : "", + __entry->intr & ENTER_U3_INTR ? "ENT_U3" : "", + __entry->intr & EXIT_U3_INTR ? "EXIT_U3" : "", + __entry->intr & VBUS_RISE_INTR ? "VBUS_RISE" : "", + __entry->intr & VBUS_FALL_INTR ? "VBUS_FALL" : "" + ) +); + +TRACE_EVENT(mtu3_u2_common_isr, + TP_PROTO(u32 intr), + TP_ARGS(intr), + TP_STRUCT__entry( + __field(u32, intr) + ), + TP_fast_assign( + __entry->intr = intr; + ), + TP_printk("(%08x) %s %s %s", __entry->intr, + __entry->intr & SUSPEND_INTR ? "SUSPEND" : "", + __entry->intr & RESUME_INTR ? "RESUME" : "", + __entry->intr & RESET_INTR ? "RESET" : "" + ) +); + +TRACE_EVENT(mtu3_qmu_isr, + TP_PROTO(u32 done_intr, u32 exp_intr), + TP_ARGS(done_intr, exp_intr), + TP_STRUCT__entry( + __field(u32, done_intr) + __field(u32, exp_intr) + ), + TP_fast_assign( + __entry->done_intr = done_intr; + __entry->exp_intr = exp_intr; + ), + TP_printk("done (tx %04x, rx %04x), exp (%08x)", + __entry->done_intr & 0xffff, + __entry->done_intr >> 16, + __entry->exp_intr + ) +); + +DECLARE_EVENT_CLASS(mtu3_log_setup, + TP_PROTO(struct usb_ctrlrequest *setup), + TP_ARGS(setup), + TP_STRUCT__entry( + __field(__u8, bRequestType) + __field(__u8, bRequest) + __field(__u16, wValue) + __field(__u16, wIndex) + __field(__u16, wLength) + ), + TP_fast_assign( + __entry->bRequestType = setup->bRequestType; + __entry->bRequest = setup->bRequest; + __entry->wValue = le16_to_cpu(setup->wValue); + __entry->wIndex = le16_to_cpu(setup->wIndex); + __entry->wLength = le16_to_cpu(setup->wLength); + ), + TP_printk("setup - %02x %02x %04x %04x %04x", + __entry->bRequestType, __entry->bRequest, + __entry->wValue, __entry->wIndex, __entry->wLength + ) +); + +DEFINE_EVENT(mtu3_log_setup, mtu3_handle_setup, + TP_PROTO(struct usb_ctrlrequest *setup), + TP_ARGS(setup) +); + +DECLARE_EVENT_CLASS(mtu3_log_request, + TP_PROTO(struct mtu3_request *mreq), + TP_ARGS(mreq), + TP_STRUCT__entry( + __string(name, mreq->mep->name) + __field(struct mtu3_request *, mreq) + __field(struct qmu_gpd *, gpd) + __field(unsigned int, actual) + __field(unsigned int, length) + __field(int, status) + __field(int, zero) + __field(int, no_interrupt) + ), + TP_fast_assign( + __assign_str(name, mreq->mep->name); + __entry->mreq = mreq; + __entry->gpd = mreq->gpd; + __entry->actual = mreq->request.actual; + __entry->length = mreq->request.length; + __entry->status = mreq->request.status; + __entry->zero = mreq->request.zero; + __entry->no_interrupt = mreq->request.no_interrupt; + ), + TP_printk("%s: req %p gpd %p len %u/%u %s%s --> %d", + __get_str(name), __entry->mreq, __entry->gpd, + __entry->actual, __entry->length, + __entry->zero ? "Z" : "z", + __entry->no_interrupt ? "i" : "I", + __entry->status + ) +); + +DEFINE_EVENT(mtu3_log_request, mtu3_alloc_request, + TP_PROTO(struct mtu3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(mtu3_log_request, mtu3_free_request, + TP_PROTO(struct mtu3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(mtu3_log_request, mtu3_gadget_queue, + TP_PROTO(struct mtu3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(mtu3_log_request, mtu3_gadget_dequeue, + TP_PROTO(struct mtu3_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(mtu3_log_request, mtu3_req_complete, + TP_PROTO(struct mtu3_request *req), + TP_ARGS(req) +); + +DECLARE_EVENT_CLASS(mtu3_log_gpd, + TP_PROTO(struct mtu3_ep *mep, struct qmu_gpd *gpd), + TP_ARGS(mep, gpd), + TP_STRUCT__entry( + __string(name, mep->name) + __field(struct qmu_gpd *, gpd) + __field(u32, dw0) + __field(u32, dw1) + __field(u32, dw2) + __field(u32, dw3) + ), + TP_fast_assign( + __assign_str(name, mep->name); + __entry->gpd = gpd; + __entry->dw0 = le32_to_cpu(gpd->dw0_info); + __entry->dw1 = le32_to_cpu(gpd->next_gpd); + __entry->dw2 = le32_to_cpu(gpd->buffer); + __entry->dw3 = le32_to_cpu(gpd->dw3_info); + ), + TP_printk("%s: gpd %p - %08x %08x %08x %08x", + __get_str(name), __entry->gpd, + __entry->dw0, __entry->dw1, + __entry->dw2, __entry->dw3 + ) +); + +DEFINE_EVENT(mtu3_log_gpd, mtu3_prepare_gpd, + TP_PROTO(struct mtu3_ep *mep, struct qmu_gpd *gpd), + TP_ARGS(mep, gpd) +); + +DEFINE_EVENT(mtu3_log_gpd, mtu3_complete_gpd, + TP_PROTO(struct mtu3_ep *mep, struct qmu_gpd *gpd), + TP_ARGS(mep, gpd) +); + +DEFINE_EVENT(mtu3_log_gpd, mtu3_zlp_exp_gpd, + TP_PROTO(struct mtu3_ep *mep, struct qmu_gpd *gpd), + TP_ARGS(mep, gpd) +); + +DECLARE_EVENT_CLASS(mtu3_log_ep, + TP_PROTO(struct mtu3_ep *mep), + TP_ARGS(mep), + TP_STRUCT__entry( + __string(name, mep->name) + __field(unsigned int, type) + __field(unsigned int, slot) + __field(unsigned int, maxp) + __field(unsigned int, mult) + __field(unsigned int, maxburst) + __field(unsigned int, flags) + __field(unsigned int, direction) + __field(struct mtu3_gpd_ring *, gpd_ring) + ), + TP_fast_assign( + __assign_str(name, mep->name); + __entry->type = mep->type; + __entry->slot = mep->slot; + __entry->maxp = mep->ep.maxpacket; + __entry->mult = mep->ep.mult; + __entry->maxburst = mep->ep.maxburst; + __entry->flags = mep->flags; + __entry->direction = mep->is_in; + __entry->gpd_ring = &mep->gpd_ring; + ), + TP_printk("%s: type %d maxp %d slot %d mult %d burst %d ring %p/%pad flags %c:%c%c%c:%c", + __get_str(name), __entry->type, + __entry->maxp, __entry->slot, + __entry->mult, __entry->maxburst, + __entry->gpd_ring, &__entry->gpd_ring->dma, + __entry->flags & MTU3_EP_ENABLED ? 'E' : 'e', + __entry->flags & MTU3_EP_STALL ? 'S' : 's', + __entry->flags & MTU3_EP_WEDGE ? 'W' : 'w', + __entry->flags & MTU3_EP_BUSY ? 'B' : 'b', + __entry->direction ? '<' : '>' + ) +); + +DEFINE_EVENT(mtu3_log_ep, mtu3_gadget_ep_enable, + TP_PROTO(struct mtu3_ep *mep), + TP_ARGS(mep) +); + +DEFINE_EVENT(mtu3_log_ep, mtu3_gadget_ep_disable, + TP_PROTO(struct mtu3_ep *mep), + TP_ARGS(mep) +); + +DEFINE_EVENT(mtu3_log_ep, mtu3_gadget_ep_set_halt, + TP_PROTO(struct mtu3_ep *mep), + TP_ARGS(mep) +); + +#endif /* __MTU3_TRACE_H__ */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE mtu3_trace + +#include <trace/define_trace.h> diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index f742fddc5e2c..52f8e2b57ad5 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -67,7 +67,7 @@ config USB_MUSB_SUNXI depends on NOP_USB_XCEIV depends on PHY_SUN4I_USB depends on EXTCON - depends on GENERIC_PHY + select GENERIC_PHY select SUNXI_SRAM config USB_MUSB_DAVINCI diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index a60627bf7be3..5261f8dfedec 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -74,10 +74,14 @@ static struct musb_hdrc_platform_data jz4740_musb_platform_data = { static int jz4740_musb_init(struct musb *musb) { - usb_phy_generic_register(); - musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2); + struct device *dev = musb->controller->parent; + + if (dev->of_node) + musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0); + else + musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); if (IS_ERR(musb->xceiv)) { - pr_err("HS UDC: no transceiver configured\n"); + dev_err(dev, "No transceiver configured\n"); return PTR_ERR(musb->xceiv); } @@ -91,13 +95,6 @@ static int jz4740_musb_init(struct musb *musb) return 0; } -static int jz4740_musb_exit(struct musb *musb) -{ - usb_put_phy(musb->xceiv); - - return 0; -} - /* * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA, * so let's not set up the dma function pointers yet. @@ -106,7 +103,6 @@ static const struct musb_platform_ops jz4740_musb_ops = { .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, .fifo_mode = 2, .init = jz4740_musb_init, - .exit = jz4740_musb_exit, }; static int jz4740_probe(struct platform_device *pdev) @@ -183,7 +179,6 @@ static int jz4740_remove(struct platform_device *pdev) struct jz4740_glue *glue = platform_get_drvdata(pdev); platform_device_unregister(glue->musb); - usb_phy_generic_unregister(pdev); clk_disable_unprepare(glue->clk); return 0; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index b7d56272f9d1..9f5a4819a744 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1497,10 +1497,11 @@ static int musb_core_init(u16 musb_type, struct musb *musb) } else { musb->is_multipoint = 0; type = ""; -#ifndef CONFIG_USB_OTG_BLACKLIST_HUB - pr_err("%s: kernel must blacklist external hubs\n", - musb_driver_name); -#endif + if (IS_ENABLED(CONFIG_USB) && + !IS_ENABLED(CONFIG_USB_OTG_BLACKLIST_HUB)) { + pr_err("%s: kernel must blacklist external hubs\n", + musb_driver_name); + } } /* log release info */ diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 403eb97915f8..327d4f7baaf7 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -168,8 +168,7 @@ static void dsps_mod_timer_optional(struct dsps_glue *glue) static void dsps_musb_enable(struct musb *musb) { struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev->parent); - struct dsps_glue *glue = platform_get_drvdata(pdev); + struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; void __iomem *reg_base = musb->ctrl_base; u32 epmask, coremask; @@ -195,8 +194,7 @@ static void dsps_musb_enable(struct musb *musb) static void dsps_musb_disable(struct musb *musb) { struct device *dev = musb->controller; - struct platform_device *pdev = to_platform_device(dev->parent); - struct dsps_glue *glue = platform_get_drvdata(pdev); + struct dsps_glue *glue = dev_get_drvdata(dev->parent); const struct dsps_musb_wrapper *wrp = glue->wrp; void __iomem *reg_base = musb->ctrl_base; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index b1dd81fb5f55..a3d2fef67746 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -531,6 +531,9 @@ static int omap2430_runtime_suspend(struct device *dev) omap2430_low_level_exit(musb); + phy_power_off(musb->phy); + phy_exit(musb->phy); + return 0; } @@ -542,6 +545,9 @@ static int omap2430_runtime_resume(struct device *dev) if (!musb) return 0; + phy_init(musb->phy); + phy_power_on(musb->phy); + omap2430_low_level_init(musb); musb_writel(musb->mregs, OTG_INTERFSEL, musb->context.otg_interfsel); diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 8c509b060c09..24b4f091acb8 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -21,7 +21,7 @@ config AB8500_USB in host mode, low speed. config FSL_USB2_OTG - bool "Freescale USB OTG Transceiver Driver" + tristate "Freescale USB OTG Transceiver Driver" depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM=y && PM depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index ff38aa8963cf..71a9206ea1e2 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -189,16 +189,6 @@ static int ark3116_port_remove(struct usb_serial_port *port) return 0; } -static void ark3116_init_termios(struct tty_struct *tty) -{ - struct ktermios *termios = &tty->termios; - *termios = tty_std_termios; - termios->c_cflag = B9600 | CS8 - | CREAD | HUPCL | CLOCAL; - termios->c_ispeed = 9600; - termios->c_ospeed = 9600; -} - static void ark3116_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) @@ -645,7 +635,6 @@ static struct usb_serial_driver ark3116_device = { .port_probe = ark3116_port_probe, .port_remove = ark3116_port_remove, .set_termios = ark3116_set_termios, - .init_termios = ark3116_init_termios, .get_serial = ark3116_get_serial_info, .tiocmget = ark3116_tiocmget, .tiocmset = ark3116_tiocmset, diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index ed51bc48eea6..72d3ae1ebc64 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -98,7 +98,6 @@ struct cypress_private { int write_urb_interval; /* interval to use for write urb */ int read_urb_interval; /* interval to use for read urb */ int comm_is_ok; /* true if communication is (still) ok */ - int termios_initialized; __u8 line_control; /* holds dtr / rts value */ __u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */ __u8 current_config; /* stores the current configuration byte */ @@ -107,11 +106,7 @@ struct cypress_private { int get_cfg_unsafe; /* If true, the CYPRESS_GET_CONFIG is unsafe */ int baud_rate; /* stores current baud rate in integer form */ - int isthrottled; /* if throttled, discard reads */ char prev_status; /* used for TIOCMIWAIT */ - /* we pass a pointer to this as the argument sent to - cypress_set_termios old_termios */ - struct ktermios tmp_termios; /* stores the old termios settings */ }; /* function prototypes for the Cypress USB to serial device */ @@ -126,6 +121,7 @@ static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); static void cypress_send(struct usb_serial_port *port); static int cypress_write_room(struct tty_struct *tty); +static void cypress_earthmate_init_termios(struct tty_struct *tty); static void cypress_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); static int cypress_tiocmget(struct tty_struct *tty); @@ -153,6 +149,7 @@ static struct usb_serial_driver cypress_earthmate_device = { .dtr_rts = cypress_dtr_rts, .write = cypress_write, .write_room = cypress_write_room, + .init_termios = cypress_earthmate_init_termios, .set_termios = cypress_set_termios, .tiocmget = cypress_tiocmget, .tiocmset = cypress_tiocmset, @@ -467,7 +464,6 @@ static int cypress_generic_port_probe(struct usb_serial_port *port) priv->cmd_ctrl = 0; priv->line_control = 0; - priv->termios_initialized = 0; priv->rx_flags = 0; /* Default packet format setting is determined by packet size. Anything with a size larger then 9 must have a separate @@ -604,7 +600,7 @@ static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port) cypress_send(port); if (tty) - cypress_set_termios(tty, port, &priv->tmp_termios); + cypress_set_termios(tty, port, NULL); /* setup the port and start reading from the device */ usb_fill_int_urb(port->interrupt_in_urb, serial->dev, @@ -857,6 +853,11 @@ static int cypress_tiocmset(struct tty_struct *tty, return cypress_write(tty, port, NULL, 0); } +static void cypress_earthmate_init_termios(struct tty_struct *tty) +{ + tty_encode_baud_rate(tty, 4800, 4800); +} + static void cypress_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { @@ -868,45 +869,11 @@ static void cypress_set_termios(struct tty_struct *tty, __u8 oldlines; int linechange = 0; - spin_lock_irqsave(&priv->lock, flags); - /* We can't clean this one up as we don't know the device type - early enough */ - if (!priv->termios_initialized) { - if (priv->chiptype == CT_EARTHMATE) { - tty->termios = tty_std_termios; - tty->termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | - CLOCAL; - tty->termios.c_ispeed = 4800; - tty->termios.c_ospeed = 4800; - } else if (priv->chiptype == CT_CYPHIDCOM) { - tty->termios = tty_std_termios; - tty->termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | - CLOCAL; - tty->termios.c_ispeed = 9600; - tty->termios.c_ospeed = 9600; - } else if (priv->chiptype == CT_CA42V2) { - tty->termios = tty_std_termios; - tty->termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | - CLOCAL; - tty->termios.c_ispeed = 9600; - tty->termios.c_ospeed = 9600; - } - priv->termios_initialized = 1; - } - spin_unlock_irqrestore(&priv->lock, flags); - /* Unsupported features need clearing */ tty->termios.c_cflag &= ~(CMSPAR|CRTSCTS); cflag = tty->termios.c_cflag; - /* check if there are new settings */ - if (old_termios) { - spin_lock_irqsave(&priv->lock, flags); - priv->tmp_termios = tty->termios; - spin_unlock_irqrestore(&priv->lock, flags); - } - /* set number of data bits, parity, stop bits */ /* when parity is disabled the parity type bit is ignored */ diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index e7f244cf2c07..578ebdd86520 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -569,9 +569,9 @@ static int digi_set_modem_signals(struct usb_serial_port *port, ret = usb_submit_urb(oob_port->write_urb, GFP_ATOMIC); if (ret == 0) { oob_priv->dp_write_urb_in_use = 1; - port_priv->dp_modem_signals = - (port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS)) - | (modem_signals&(TIOCM_DTR|TIOCM_RTS)); + port_priv->dp_modem_signals &= ~(TIOCM_DTR | TIOCM_RTS); + port_priv->dp_modem_signals |= + modem_signals & (TIOCM_DTR | TIOCM_RTS); } spin_unlock(&port_priv->dp_port_lock); spin_unlock_irqrestore(&oob_priv->dp_port_lock, flags); @@ -740,9 +740,9 @@ static void digi_set_termios(struct tty_struct *tty, /* set parity */ tty->termios.c_cflag &= ~CMSPAR; - if ((cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD))) { - if (cflag&PARENB) { - if (cflag&PARODD) + if ((cflag & (PARENB | PARODD)) != (old_cflag & (PARENB | PARODD))) { + if (cflag & PARENB) { + if (cflag & PARODD) arg = DIGI_PARITY_ODD; else arg = DIGI_PARITY_EVEN; @@ -755,9 +755,9 @@ static void digi_set_termios(struct tty_struct *tty, buf[i++] = 0; } /* set word size */ - if ((cflag&CSIZE) != (old_cflag&CSIZE)) { + if ((cflag & CSIZE) != (old_cflag & CSIZE)) { arg = -1; - switch (cflag&CSIZE) { + switch (cflag & CSIZE) { case CS5: arg = DIGI_WORD_SIZE_5; break; case CS6: arg = DIGI_WORD_SIZE_6; break; case CS7: arg = DIGI_WORD_SIZE_7; break; @@ -765,7 +765,7 @@ static void digi_set_termios(struct tty_struct *tty, default: dev_dbg(dev, "digi_set_termios: can't handle word size %d\n", - (cflag&CSIZE)); + cflag & CSIZE); break; } @@ -779,9 +779,9 @@ static void digi_set_termios(struct tty_struct *tty, } /* set stop bits */ - if ((cflag&CSTOPB) != (old_cflag&CSTOPB)) { + if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) { - if ((cflag&CSTOPB)) + if ((cflag & CSTOPB)) arg = DIGI_STOP_BITS_2; else arg = DIGI_STOP_BITS_1; @@ -794,15 +794,15 @@ static void digi_set_termios(struct tty_struct *tty, } /* set input flow control */ - if ((iflag&IXOFF) != (old_iflag&IXOFF) - || (cflag&CRTSCTS) != (old_cflag&CRTSCTS)) { + if ((iflag & IXOFF) != (old_iflag & IXOFF) || + (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { arg = 0; - if (iflag&IXOFF) + if (iflag & IXOFF) arg |= DIGI_INPUT_FLOW_CONTROL_XON_XOFF; else arg &= ~DIGI_INPUT_FLOW_CONTROL_XON_XOFF; - if (cflag&CRTSCTS) { + if (cflag & CRTSCTS) { arg |= DIGI_INPUT_FLOW_CONTROL_RTS; /* On USB-4 it is necessary to assert RTS prior */ @@ -822,19 +822,18 @@ static void digi_set_termios(struct tty_struct *tty, } /* set output flow control */ - if ((iflag & IXON) != (old_iflag & IXON) - || (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { + if ((iflag & IXON) != (old_iflag & IXON) || + (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { arg = 0; if (iflag & IXON) arg |= DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; else arg &= ~DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; - if (cflag & CRTSCTS) { + if (cflag & CRTSCTS) arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS; - } else { + else arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS; - } buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; buf[i++] = priv->dp_port_num; @@ -1084,7 +1083,7 @@ static int digi_chars_in_buffer(struct tty_struct *tty) static void digi_dtr_rts(struct usb_serial_port *port, int on) { /* Adjust DTR and RTS */ - digi_set_modem_signals(port, on * (TIOCM_DTR|TIOCM_RTS), 1); + digi_set_modem_signals(port, on * (TIOCM_DTR | TIOCM_RTS), 1); } static int digi_open(struct tty_struct *tty, struct usb_serial_port *port) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 0dcdcb4b2cde..43fa1f0716b7 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -28,7 +28,8 @@ static const struct usb_device_id id_table[] = { MODULE_DEVICE_TABLE(usb, id_table); /* Maximum baudrate for F81232 */ -#define F81232_MAX_BAUDRATE 115200 +#define F81232_MAX_BAUDRATE 1500000 +#define F81232_DEF_BAUDRATE 9600 /* USB Control EP parameter */ #define F81232_REGISTER_REQUEST 0xa0 @@ -41,19 +42,46 @@ MODULE_DEVICE_TABLE(usb, id_table); #define FIFO_CONTROL_REGISTER (0x02 + SERIAL_BASE_ADDRESS) #define LINE_CONTROL_REGISTER (0x03 + SERIAL_BASE_ADDRESS) #define MODEM_CONTROL_REGISTER (0x04 + SERIAL_BASE_ADDRESS) +#define LINE_STATUS_REGISTER (0x05 + SERIAL_BASE_ADDRESS) #define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) +/* + * F81232 Clock registers (106h) + * + * Bit1-0: Clock source selector + * 00: 1.846MHz. + * 01: 18.46MHz. + * 10: 24MHz. + * 11: 14.77MHz. + */ +#define F81232_CLK_REGISTER 0x106 +#define F81232_CLK_1_846_MHZ 0 +#define F81232_CLK_18_46_MHZ BIT(0) +#define F81232_CLK_24_MHZ BIT(1) +#define F81232_CLK_14_77_MHZ (BIT(1) | BIT(0)) +#define F81232_CLK_MASK GENMASK(1, 0) + struct f81232_private { struct mutex lock; u8 modem_control; u8 modem_status; + u8 shadow_lcr; + speed_t baud_base; + struct work_struct lsr_work; struct work_struct interrupt_work; struct usb_serial_port *port; }; -static int calc_baud_divisor(speed_t baudrate) +static u32 const baudrate_table[] = { 115200, 921600, 1152000, 1500000 }; +static u8 const clock_table[] = { F81232_CLK_1_846_MHZ, F81232_CLK_14_77_MHZ, + F81232_CLK_18_46_MHZ, F81232_CLK_24_MHZ }; + +static int calc_baud_divisor(speed_t baudrate, speed_t clockrate) { - return DIV_ROUND_CLOSEST(F81232_MAX_BAUDRATE, baudrate); + if (!baudrate) + return 0; + + return DIV_ROUND_CLOSEST(clockrate, baudrate); } static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val) @@ -127,6 +155,21 @@ static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val) return status; } +static int f81232_set_mask_register(struct usb_serial_port *port, u16 reg, + u8 mask, u8 val) +{ + int status; + u8 tmp; + + status = f81232_get_register(port, reg, &tmp); + if (status) + return status; + + tmp = (tmp & ~mask) | (val & mask); + + return f81232_set_register(port, reg, tmp); +} + static void f81232_read_msr(struct usb_serial_port *port) { int status; @@ -282,6 +325,7 @@ exit: static void f81232_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; + struct f81232_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; char tty_flag; unsigned int i; @@ -315,6 +359,7 @@ static void f81232_process_read_urb(struct urb *urb) if (lsr & UART_LSR_OE) { port->icount.overrun++; + schedule_work(&priv->lsr_work); tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); } @@ -333,22 +378,72 @@ static void f81232_process_read_urb(struct urb *urb) static void f81232_break_ctl(struct tty_struct *tty, int break_state) { - /* FIXME - Stubbed out for now */ + struct usb_serial_port *port = tty->driver_data; + struct f81232_private *priv = usb_get_serial_port_data(port); + int status; - /* - * break_state = -1 to turn on break, and 0 to turn off break - * see drivers/char/tty_io.c to see it used. - * last_set_data_urb_value NEVER has the break bit set in it. - */ + mutex_lock(&priv->lock); + + if (break_state) + priv->shadow_lcr |= UART_LCR_SBC; + else + priv->shadow_lcr &= ~UART_LCR_SBC; + + status = f81232_set_register(port, LINE_CONTROL_REGISTER, + priv->shadow_lcr); + if (status) + dev_err(&port->dev, "set break failed: %d\n", status); + + mutex_unlock(&priv->lock); } -static void f81232_set_baudrate(struct usb_serial_port *port, speed_t baudrate) +static int f81232_find_clk(speed_t baudrate) { + int idx; + + for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) { + if (baudrate <= baudrate_table[idx] && + baudrate_table[idx] % baudrate == 0) + return idx; + } + + return -EINVAL; +} + +static void f81232_set_baudrate(struct tty_struct *tty, + struct usb_serial_port *port, speed_t baudrate, + speed_t old_baudrate) +{ + struct f81232_private *priv = usb_get_serial_port_data(port); u8 lcr; int divisor; int status = 0; + int i; + int idx; + speed_t baud_list[] = { baudrate, old_baudrate, F81232_DEF_BAUDRATE }; + + for (i = 0; i < ARRAY_SIZE(baud_list); ++i) { + idx = f81232_find_clk(baud_list[i]); + if (idx >= 0) { + baudrate = baud_list[i]; + tty_encode_baud_rate(tty, baudrate, baudrate); + break; + } + } - divisor = calc_baud_divisor(baudrate); + if (idx < 0) + return; + + priv->baud_base = baudrate_table[idx]; + divisor = calc_baud_divisor(baudrate, priv->baud_base); + + status = f81232_set_mask_register(port, F81232_CLK_REGISTER, + F81232_CLK_MASK, clock_table[idx]); + if (status) { + dev_err(&port->dev, "%s failed to set CLK_REG: %d\n", + __func__, status); + return; + } status = f81232_get_register(port, LINE_CONTROL_REGISTER, &lcr); /* get LCR */ @@ -435,9 +530,11 @@ static int f81232_port_disable(struct usb_serial_port *port) static void f81232_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { + struct f81232_private *priv = usb_get_serial_port_data(port); u8 new_lcr = 0; int status = 0; speed_t baudrate; + speed_t old_baud; /* Don't change anything if nothing has changed */ if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) @@ -450,11 +547,12 @@ static void f81232_set_termios(struct tty_struct *tty, baudrate = tty_get_baud_rate(tty); if (baudrate > 0) { - if (baudrate > F81232_MAX_BAUDRATE) { - baudrate = F81232_MAX_BAUDRATE; - tty_encode_baud_rate(tty, baudrate, baudrate); - } - f81232_set_baudrate(port, baudrate); + if (old_termios) + old_baud = tty_termios_baud_rate(old_termios); + else + old_baud = F81232_DEF_BAUDRATE; + + f81232_set_baudrate(tty, port, baudrate, old_baud); } if (C_PARENB(tty)) { @@ -486,11 +584,18 @@ static void f81232_set_termios(struct tty_struct *tty, break; } + mutex_lock(&priv->lock); + + new_lcr |= (priv->shadow_lcr & UART_LCR_SBC); status = f81232_set_register(port, LINE_CONTROL_REGISTER, new_lcr); if (status) { dev_err(&port->dev, "%s failed to set LCR: %d\n", __func__, status); } + + priv->shadow_lcr = new_lcr; + + mutex_unlock(&priv->lock); } static int f81232_tiocmget(struct tty_struct *tty) @@ -556,9 +661,13 @@ static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port) static void f81232_close(struct usb_serial_port *port) { + struct f81232_private *port_priv = usb_get_serial_port_data(port); + f81232_port_disable(port); usb_serial_generic_close(port); usb_kill_urb(port->interrupt_in_urb); + flush_work(&port_priv->interrupt_work); + flush_work(&port_priv->lsr_work); } static void f81232_dtr_rts(struct usb_serial_port *port, int on) @@ -587,11 +696,12 @@ static int f81232_get_serial_info(struct tty_struct *tty, struct serial_struct *ss) { struct usb_serial_port *port = tty->driver_data; + struct f81232_private *priv = usb_get_serial_port_data(port); ss->type = PORT_16550A; ss->line = port->minor; ss->port = port->port_number; - ss->baud_base = F81232_MAX_BAUDRATE; + ss->baud_base = priv->baud_base; return 0; } @@ -603,6 +713,21 @@ static void f81232_interrupt_work(struct work_struct *work) f81232_read_msr(priv->port); } +static void f81232_lsr_worker(struct work_struct *work) +{ + struct f81232_private *priv; + struct usb_serial_port *port; + int status; + u8 tmp; + + priv = container_of(work, struct f81232_private, lsr_work); + port = priv->port; + + status = f81232_get_register(port, LINE_STATUS_REGISTER, &tmp); + if (status) + dev_warn(&port->dev, "read LSR failed: %d\n", status); +} + static int f81232_port_probe(struct usb_serial_port *port) { struct f81232_private *priv; @@ -613,6 +738,7 @@ static int f81232_port_probe(struct usb_serial_port *port) mutex_init(&priv->lock); INIT_WORK(&priv->interrupt_work, f81232_interrupt_work); + INIT_WORK(&priv->lsr_work, f81232_lsr_worker); usb_set_serial_port_data(port, priv); @@ -632,6 +758,42 @@ static int f81232_port_remove(struct usb_serial_port *port) return 0; } +static int f81232_suspend(struct usb_serial *serial, pm_message_t message) +{ + struct usb_serial_port *port = serial->port[0]; + struct f81232_private *port_priv = usb_get_serial_port_data(port); + int i; + + for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) + usb_kill_urb(port->read_urbs[i]); + + usb_kill_urb(port->interrupt_in_urb); + + if (port_priv) { + flush_work(&port_priv->interrupt_work); + flush_work(&port_priv->lsr_work); + } + + return 0; +} + +static int f81232_resume(struct usb_serial *serial) +{ + struct usb_serial_port *port = serial->port[0]; + int result; + + if (tty_port_initialized(&port->port)) { + result = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); + if (result) { + dev_err(&port->dev, "submit interrupt urb failed: %d\n", + result); + return result; + } + } + + return usb_serial_generic_resume(serial); +} + static struct usb_serial_driver f81232_device = { .driver = { .owner = THIS_MODULE, @@ -655,6 +817,8 @@ static struct usb_serial_driver f81232_device = { .read_int_callback = f81232_read_int_callback, .port_probe = f81232_port_probe, .port_remove = f81232_port_remove, + .suspend = f81232_suspend, + .resume = f81232_resume, }; static struct usb_serial_driver * const serial_drivers[] = { diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 2274d9625f63..1be8bea372a2 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -106,12 +106,8 @@ void usb_serial_generic_deregister(void) int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port) { int result = 0; - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - port->throttled = 0; - port->throttle_req = 0; - spin_unlock_irqrestore(&port->lock, flags); + clear_bit(USB_SERIAL_THROTTLED, &port->flags); if (port->bulk_in_size) result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); @@ -375,7 +371,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; - unsigned long flags; + bool stopped = false; int status = urb->status; int i; @@ -383,42 +379,55 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) if (urb == port->read_urbs[i]) break; } - set_bit(i, &port->read_urbs_free); dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i, urb->actual_length); switch (status) { case 0: + usb_serial_debug_data(&port->dev, __func__, urb->actual_length, + data); + port->serial->type->process_read_urb(urb); break; case -ENOENT: case -ECONNRESET: case -ESHUTDOWN: dev_dbg(&port->dev, "%s - urb stopped: %d\n", __func__, status); - return; + stopped = true; + break; case -EPIPE: dev_err(&port->dev, "%s - urb stopped: %d\n", __func__, status); - return; + stopped = true; + break; default: dev_dbg(&port->dev, "%s - nonzero urb status: %d\n", __func__, status); - goto resubmit; + break; } - usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); - port->serial->type->process_read_urb(urb); + /* + * Make sure URB processing is done before marking as free to avoid + * racing with unthrottle() on another CPU. Matches the barriers + * implied by the test_and_clear_bit() in + * usb_serial_generic_submit_read_urb(). + */ + smp_mb__before_atomic(); + set_bit(i, &port->read_urbs_free); + /* + * Make sure URB is marked as free before checking the throttled flag + * to avoid racing with unthrottle() on another CPU. Matches the + * smp_mb() in unthrottle(). + */ + smp_mb__after_atomic(); -resubmit: - /* Throttle the device if requested by tty */ - spin_lock_irqsave(&port->lock, flags); - port->throttled = port->throttle_req; - if (!port->throttled) { - spin_unlock_irqrestore(&port->lock, flags); - usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC); - } else { - spin_unlock_irqrestore(&port->lock, flags); - } + if (stopped) + return; + + if (test_bit(USB_SERIAL_THROTTLED, &port->flags)) + return; + + usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC); } EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); @@ -454,10 +463,9 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb) default: dev_err_console(port, "%s - nonzero urb status: %d\n", __func__, status); - goto resubmit; + break; } -resubmit: usb_serial_generic_write_start(port, GFP_ATOMIC); usb_serial_port_softint(port); } @@ -466,26 +474,24 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); void usb_serial_generic_throttle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - port->throttle_req = 1; - spin_unlock_irqrestore(&port->lock, flags); + set_bit(USB_SERIAL_THROTTLED, &port->flags); } EXPORT_SYMBOL_GPL(usb_serial_generic_throttle); void usb_serial_generic_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; - int was_throttled; - spin_lock_irq(&port->lock); - was_throttled = port->throttled; - port->throttled = port->throttle_req = 0; - spin_unlock_irq(&port->lock); + clear_bit(USB_SERIAL_THROTTLED, &port->flags); + + /* + * Matches the smp_mb__after_atomic() in + * usb_serial_generic_read_bulk_callback(). + */ + smp_mb(); - if (was_throttled) - usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); + usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); } EXPORT_SYMBOL_GPL(usb_serial_generic_unthrottle); diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 4ca31c0e4174..48a439298a68 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1751,7 +1751,7 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial, edge_serial->rxState = EXPECT_HDR2; break; } - /* otherwise, drop on through */ + /* Fall through */ case EXPECT_HDR2: edge_serial->rxHeader2 = *buffer; ++buffer; @@ -1790,29 +1790,20 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial, edge_serial->rxHeader2, 0); edge_serial->rxState = EXPECT_HDR1; break; - } else { - edge_serial->rxPort = - IOSP_GET_HDR_PORT(edge_serial->rxHeader1); - edge_serial->rxBytesRemaining = - IOSP_GET_HDR_DATA_LEN( - edge_serial->rxHeader1, - edge_serial->rxHeader2); - dev_dbg(dev, "%s - Data for Port %u Len %u\n", - __func__, - edge_serial->rxPort, - edge_serial->rxBytesRemaining); - - /* ASSERT(DevExt->RxPort < DevExt->NumPorts); - * ASSERT(DevExt->RxBytesRemaining < - * IOSP_MAX_DATA_LENGTH); - */ - - if (bufferLength == 0) { - edge_serial->rxState = EXPECT_DATA; - break; - } - /* Else, drop through */ } + + edge_serial->rxPort = IOSP_GET_HDR_PORT(edge_serial->rxHeader1); + edge_serial->rxBytesRemaining = IOSP_GET_HDR_DATA_LEN(edge_serial->rxHeader1, + edge_serial->rxHeader2); + dev_dbg(dev, "%s - Data for Port %u Len %u\n", __func__, + edge_serial->rxPort, + edge_serial->rxBytesRemaining); + + if (bufferLength == 0) { + edge_serial->rxState = EXPECT_DATA; + break; + } + /* Fall through */ case EXPECT_DATA: /* Expect data */ if (bufferLength < edge_serial->rxBytesRemaining) { rxLen = bufferLength; diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 449e89db9cea..d5bff69b1769 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -942,9 +942,7 @@ static void iuu_close(struct usb_serial_port *port) static void iuu_init_termios(struct tty_struct *tty) { - tty->termios = tty_std_termios; - tty->termios.c_cflag = CLOCAL | CREAD | CS8 | B9600 - | TIOCM_CTS | CSTOPB | PARENB; + tty->termios.c_cflag = B9600 | CS8 | CSTOPB | CREAD | PARENB | CLOCAL; tty->termios.c_ispeed = 9600; tty->termios.c_ospeed = 9600; tty->termios.c_lflag = 0; diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index ae9cb15ee02d..38ae0fc826cc 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -393,10 +393,7 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty) static void oti6858_init_termios(struct tty_struct *tty) { - tty->termios = tty_std_termios; - tty->termios.c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios.c_ispeed = 38400; - tty->termios.c_ospeed = 38400; + tty_encode_baud_rate(tty, 38400, 38400); } static void oti6858_set_termios(struct tty_struct *tty, diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index bb3f9aa4a909..55122ac84518 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -145,6 +145,8 @@ MODULE_DEVICE_TABLE(usb, id_table); #define UART_OVERRUN_ERROR 0x40 #define UART_CTS 0x80 +#define PL2303_FLOWCTRL_MASK 0xf0 + static void pl2303_set_break(struct usb_serial_port *port, bool enable); enum pl2303_type { @@ -156,6 +158,7 @@ enum pl2303_type { struct pl2303_type_data { speed_t max_baud_rate; unsigned long quirks; + unsigned int no_autoxonxoff:1; }; struct pl2303_serial_private { @@ -173,11 +176,12 @@ struct pl2303_private { static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { [TYPE_01] = { - .max_baud_rate = 1228800, - .quirks = PL2303_QUIRK_LEGACY, + .max_baud_rate = 1228800, + .quirks = PL2303_QUIRK_LEGACY, + .no_autoxonxoff = true, }, [TYPE_HX] = { - .max_baud_rate = 12000000, + .max_baud_rate = 12000000, }, }; @@ -223,6 +227,29 @@ static int pl2303_vendor_write(struct usb_serial *serial, u16 value, u16 index) return 0; } +static int pl2303_update_reg(struct usb_serial *serial, u8 reg, u8 mask, u8 val) +{ + int ret = 0; + u8 *buf; + + buf = kmalloc(1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = pl2303_vendor_read(serial, reg | 0x80, buf); + if (ret) + goto out_free; + + *buf &= ~mask; + *buf |= val & mask; + + ret = pl2303_vendor_write(serial, reg, *buf); +out_free: + kfree(buf); + + return ret; +} + static int pl2303_probe(struct usb_serial *serial, const struct usb_device_id *id) { @@ -552,6 +579,20 @@ static bool pl2303_termios_change(const struct ktermios *a, const struct ktermio return tty_termios_hw_change(a, b) || ixon_change; } +static bool pl2303_enable_xonxoff(struct tty_struct *tty, const struct pl2303_type_data *type) +{ + if (!I_IXON(tty) || I_IXANY(tty)) + return false; + + if (START_CHAR(tty) != 0x11 || STOP_CHAR(tty) != 0x13) + return false; + + if (type->no_autoxonxoff) + return false; + + return true; +} + static void pl2303_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { @@ -678,14 +719,13 @@ static void pl2303_set_termios(struct tty_struct *tty, if (C_CRTSCTS(tty)) { if (spriv->quirks & PL2303_QUIRK_LEGACY) - pl2303_vendor_write(serial, 0x0, 0x41); + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x40); else - pl2303_vendor_write(serial, 0x0, 0x61); - } else if (I_IXON(tty) && !I_IXANY(tty) && START_CHAR(tty) == 0x11 && - STOP_CHAR(tty) == 0x13) { - pl2303_vendor_write(serial, 0x0, 0xc0); + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0x60); + } else if (pl2303_enable_xonxoff(tty, spriv->type)) { + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0xc0); } else { - pl2303_vendor_write(serial, 0x0, 0x0); + pl2303_update_reg(serial, 0, PL2303_FLOWCTRL_MASK, 0); } kfree(buf); diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index b42714855364..3bac55bd9bd9 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -281,10 +281,7 @@ static void spcp8x5_dtr_rts(struct usb_serial_port *port, int on) static void spcp8x5_init_termios(struct tty_struct *tty) { - tty->termios = tty_std_termios; - tty->termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios.c_ispeed = 115200; - tty->termios.c_ospeed = 115200; + tty_encode_baud_rate(tty, 115200, 115200); } static void spcp8x5_set_termios(struct tty_struct *tty, diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 7e89efbf2c28..676c296103a2 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -164,9 +164,9 @@ void usb_serial_put(struct usb_serial *serial) * @driver: the driver (USB in our case) * @tty: the tty being created * - * Create the termios objects for this tty. We use the default + * Initialise the termios structure for this tty. We use the default * USB serial settings but permit them to be overridden by - * serial->type->init_termios. + * serial->type->init_termios on first open. * * This is the first place a new tty gets used. Hence this is where we * acquire references to the usb_serial structure and the driver module, @@ -178,6 +178,7 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) int idx = tty->index; struct usb_serial *serial; struct usb_serial_port *port; + bool init_termios; int retval = -ENODEV; port = usb_serial_port_get_by_minor(idx); @@ -192,14 +193,16 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) if (retval) goto error_get_interface; + init_termios = (driver->termios[idx] == NULL); + retval = tty_standard_install(driver, tty); if (retval) goto error_init_termios; mutex_unlock(&serial->disc_mutex); - /* allow the driver to update the settings */ - if (serial->type->init_termios) + /* allow the driver to update the initial settings */ + if (init_termios && serial->type->init_termios) serial->type->init_termios(tty); tty->driver_data = port; diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index a73ea495d5a7..59190d88fa9f 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -65,6 +65,7 @@ static const char* host_info(struct Scsi_Host *host) static int slave_alloc (struct scsi_device *sdev) { struct us_data *us = host_to_us(sdev->host); + int maxp; /* * Set the INQUIRY transfer length to 36. We don't use any of @@ -74,20 +75,17 @@ static int slave_alloc (struct scsi_device *sdev) sdev->inquiry_len = 36; /* - * USB has unusual DMA-alignment requirements: Although the - * starting address of each scatter-gather element doesn't matter, - * the length of each element except the last must be divisible - * by the Bulk maxpacket value. There's currently no way to - * express this by block-layer constraints, so we'll cop out - * and simply require addresses to be aligned at 512-byte - * boundaries. This is okay since most block I/O involves - * hardware sectors that are multiples of 512 bytes in length, - * and since host controllers up through USB 2.0 have maxpacket - * values no larger than 512. - * - * But it doesn't suffice for Wireless USB, where Bulk maxpacket - * values can be as large as 2048. To make that work properly - * will require changes to the block layer. + * USB has unusual scatter-gather requirements: the length of each + * scatterlist element except the last must be divisible by the + * Bulk maxpacket value. Fortunately this value is always a + * power of 2. Inform the block layer about this requirement. + */ + maxp = usb_maxpacket(us->pusb_dev, us->recv_bulk_pipe, 0); + blk_queue_virt_boundary(sdev->request_queue, maxp - 1); + + /* + * Some host controllers may have alignment requirements. + * We'll play it safe by requiring 512-byte alignment always. */ blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c index 6ac60abd2e15..e605cbc3d8bf 100644 --- a/drivers/usb/storage/sierra_ms.c +++ b/drivers/usb/storage/sierra_ms.c @@ -194,8 +194,6 @@ int sierra_ms_init(struct us_data *us) kfree(swocInfo); } complete: - result = device_create_file(&us->pusb_intf->dev, &dev_attr_truinst); - - return 0; + return device_create_file(&us->pusb_intf->dev, &dev_attr_truinst); } diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index a6d68191c861..047c5922618f 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -789,24 +789,33 @@ static int uas_slave_alloc(struct scsi_device *sdev) { struct uas_dev_info *devinfo = (struct uas_dev_info *)sdev->host->hostdata; + int maxp; sdev->hostdata = devinfo; /* - * USB has unusual DMA-alignment requirements: Although the - * starting address of each scatter-gather element doesn't matter, - * the length of each element except the last must be divisible - * by the Bulk maxpacket value. There's currently no way to - * express this by block-layer constraints, so we'll cop out - * and simply require addresses to be aligned at 512-byte - * boundaries. This is okay since most block I/O involves - * hardware sectors that are multiples of 512 bytes in length, - * and since host controllers up through USB 2.0 have maxpacket - * values no larger than 512. + * We have two requirements here. We must satisfy the requirements + * of the physical HC and the demands of the protocol, as we + * definitely want no additional memory allocation in this path + * ruling out using bounce buffers. * - * But it doesn't suffice for Wireless USB, where Bulk maxpacket - * values can be as large as 2048. To make that work properly - * will require changes to the block layer. + * For a transmission on USB to continue we must never send + * a package that is smaller than maxpacket. Hence the length of each + * scatterlist element except the last must be divisible by the + * Bulk maxpacket value. + * If the HC does not ensure that through SG, + * the upper layer must do that. We must assume nothing + * about the capabilities off the HC, so we use the most + * pessimistic requirement. + */ + + maxp = usb_maxpacket(devinfo->udev, devinfo->data_in_pipe, 0); + blk_queue_virt_boundary(sdev->request_queue, maxp - 1); + + /* + * The protocol has no requirements on alignment in the strict sense. + * Controllers may or may not have alignment restrictions. + * As this is not exported, we use an extremely conservative guess. */ blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1)); diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig index ef2226eb7a33..187690fd1a5b 100644 --- a/drivers/usb/typec/altmodes/Kconfig +++ b/drivers/usb/typec/altmodes/Kconfig @@ -12,4 +12,14 @@ config TYPEC_DP_ALTMODE To compile this driver as a module, choose M here: the module will be called typec_displayport. +config TYPEC_NVIDIA_ALTMODE + tristate "NVIDIA Alternate Mode driver" + depends on TYPEC_DP_ALTMODE + help + Latest NVIDIA GPUs support VirtualLink devices. Select this + to enable support for VirtualLink devices with NVIDIA GPUs. + + To compile this driver as a module, choose M here: the + module will be called typec_displayport. + endmenu diff --git a/drivers/usb/typec/altmodes/Makefile b/drivers/usb/typec/altmodes/Makefile index eda8456f1c92..45717548b396 100644 --- a/drivers/usb/typec/altmodes/Makefile +++ b/drivers/usb/typec/altmodes/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_TYPEC_DP_ALTMODE) += typec_displayport.o typec_displayport-y := displayport.o +obj-$(CONFIG_TYPEC_NVIDIA_ALTMODE) += typec_nvidia.o +typec_nvidia-y := nvidia.o diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 610d790bc9be..4092248a5936 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -14,7 +14,7 @@ #include <linux/usb/pd_vdo.h> #include <linux/usb/typec_dp.h> -#define DP_HEADER(cmd) (VDO(USB_TYPEC_DP_SID, 1, cmd) | \ +#define DP_HEADER(_dp, cmd) (VDO((_dp)->alt->svid, 1, cmd) | \ VDO_OPOS(USB_TYPEC_DP_MODE)) enum { @@ -100,7 +100,7 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con) if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC && pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK) pin_assign &= DP_PIN_ASSIGN_MULTI_FUNC_MASK; - else + else if (pin_assign & DP_PIN_ASSIGN_DP_ONLY_MASK) pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK; if (!pin_assign) @@ -155,7 +155,7 @@ static int dp_altmode_configured(struct dp_altmode *dp) static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) { - u32 header = DP_HEADER(DP_CMD_CONFIGURE); + u32 header = DP_HEADER(dp, DP_CMD_CONFIGURE); int ret; ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data); @@ -193,7 +193,7 @@ static void dp_altmode_work(struct work_struct *work) dev_err(&dp->alt->dev, "failed to enter mode\n"); break; case DP_STATE_UPDATE: - header = DP_HEADER(DP_CMD_STATUS_UPDATE); + header = DP_HEADER(dp, DP_CMD_STATUS_UPDATE); vdo = 1; ret = typec_altmode_vdm(dp->alt, header, &vdo, 2); if (ret) @@ -507,7 +507,7 @@ static const struct attribute_group dp_altmode_group = { .attrs = dp_altmode_attrs, }; -static int dp_altmode_probe(struct typec_altmode *alt) +int dp_altmode_probe(struct typec_altmode *alt) { const struct typec_altmode *port = typec_altmode_get_partner(alt); struct dp_altmode *dp; @@ -545,14 +545,16 @@ static int dp_altmode_probe(struct typec_altmode *alt) return 0; } +EXPORT_SYMBOL_GPL(dp_altmode_probe); -static void dp_altmode_remove(struct typec_altmode *alt) +void dp_altmode_remove(struct typec_altmode *alt) { struct dp_altmode *dp = typec_altmode_get_drvdata(alt); sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group); cancel_work_sync(&dp->work); } +EXPORT_SYMBOL_GPL(dp_altmode_remove); static const struct typec_device_id dp_typec_id[] = { { USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE }, diff --git a/drivers/usb/typec/altmodes/displayport.h b/drivers/usb/typec/altmodes/displayport.h new file mode 100644 index 000000000000..e120364da9fd --- /dev/null +++ b/drivers/usb/typec/altmodes/displayport.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE) +int dp_altmode_probe(struct typec_altmode *alt); +void dp_altmode_remove(struct typec_altmode *alt); +#else +int dp_altmode_probe(struct typec_altmode *alt) { return -ENOTSUPP; } +void dp_altmode_remove(struct typec_altmode *alt) { } +#endif /* CONFIG_TYPEC_DP_ALTMODE */ diff --git a/drivers/usb/typec/altmodes/nvidia.c b/drivers/usb/typec/altmodes/nvidia.c new file mode 100644 index 000000000000..c36769736405 --- /dev/null +++ b/drivers/usb/typec/altmodes/nvidia.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 NVIDIA Corporation. All rights reserved. + * + * NVIDIA USB Type-C Alt Mode Driver + */ +#include <linux/module.h> +#include <linux/usb/typec_altmode.h> +#include <linux/usb/typec_dp.h> +#include "displayport.h" + +static int nvidia_altmode_probe(struct typec_altmode *alt) +{ + if (alt->svid == USB_TYPEC_NVIDIA_VLINK_SID) + return dp_altmode_probe(alt); + else + return -ENOTSUPP; +} + +static void nvidia_altmode_remove(struct typec_altmode *alt) +{ + if (alt->svid == USB_TYPEC_NVIDIA_VLINK_SID) + dp_altmode_remove(alt); +} + +static const struct typec_device_id nvidia_typec_id[] = { + { USB_TYPEC_NVIDIA_VLINK_SID, TYPEC_ANY_MODE }, + { }, +}; +MODULE_DEVICE_TABLE(typec, nvidia_typec_id); + +static struct typec_altmode_driver nvidia_altmode_driver = { + .id_table = nvidia_typec_id, + .probe = nvidia_altmode_probe, + .remove = nvidia_altmode_remove, + .driver = { + .name = "typec_nvidia", + .owner = THIS_MODULE, + }, +}; +module_typec_altmode_driver(nvidia_altmode_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("NVIDIA USB Type-C Alt Mode Driver"); diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c index 64eb5983e17a..9294e85fd34b 100644 --- a/drivers/usb/typec/mux/pi3usb30532.c +++ b/drivers/usb/typec/mux/pi3usb30532.c @@ -84,7 +84,8 @@ static int pi3usb30532_mux_set(struct typec_mux *mux, int state) switch (state) { case TYPEC_STATE_SAFE: - new_conf = PI3USB30532_CONF_OPEN; + new_conf = (new_conf & PI3USB30532_CONF_SWAP) | + PI3USB30532_CONF_OPEN; break; case TYPEC_STATE_USB: new_conf = (new_conf & PI3USB30532_CONF_SWAP) | diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index e9344997329c..7302f7501ec9 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -23,6 +23,7 @@ #include <linux/sched/clock.h> #include <linux/seq_file.h> #include <linux/slab.h> +#include <linux/spinlock.h> #include <linux/string.h> #include <linux/types.h> #include <linux/usb/typec.h> @@ -78,6 +79,10 @@ struct fusb302_chip { struct regulator *vbus; + spinlock_t irq_lock; + struct work_struct irq_work; + bool irq_suspended; + bool irq_while_suspended; int gpio_int_n; int gpio_int_n_irq; struct extcon_dev *extcon; @@ -85,9 +90,6 @@ struct fusb302_chip { struct workqueue_struct *wq; struct delayed_work bc_lvl_handler; - atomic_t pm_suspend; - atomic_t i2c_busy; - /* lock for sharing chip states */ struct mutex lock; @@ -99,7 +101,6 @@ struct fusb302_chip { bool intr_comp_chng; /* port status */ - bool pull_up; bool vconn_on; bool vbus_on; bool charge_on; @@ -124,13 +125,13 @@ struct fusb302_chip { */ #ifdef CONFIG_DEBUG_FS - static bool fusb302_log_full(struct fusb302_chip *chip) { return chip->logbuffer_tail == (chip->logbuffer_head + 1) % LOG_BUFFER_ENTRIES; } +__printf(2, 0) static void _fusb302_log(struct fusb302_chip *chip, const char *fmt, va_list args) { @@ -234,43 +235,15 @@ static void fusb302_debugfs_exit(const struct fusb302_chip *chip) { } #endif -#define FUSB302_RESUME_RETRY 10 -#define FUSB302_RESUME_RETRY_SLEEP 50 - -static bool fusb302_is_suspended(struct fusb302_chip *chip) -{ - int retry_cnt; - - for (retry_cnt = 0; retry_cnt < FUSB302_RESUME_RETRY; retry_cnt++) { - if (atomic_read(&chip->pm_suspend)) { - dev_err(chip->dev, "i2c: pm suspend, retry %d/%d\n", - retry_cnt + 1, FUSB302_RESUME_RETRY); - msleep(FUSB302_RESUME_RETRY_SLEEP); - } else { - return false; - } - } - - return true; -} - static int fusb302_i2c_write(struct fusb302_chip *chip, u8 address, u8 data) { int ret = 0; - atomic_set(&chip->i2c_busy, 1); - - if (fusb302_is_suspended(chip)) { - atomic_set(&chip->i2c_busy, 0); - return -ETIMEDOUT; - } - ret = i2c_smbus_write_byte_data(chip->i2c_client, address, data); if (ret < 0) fusb302_log(chip, "cannot write 0x%02x to 0x%02x, ret=%d", data, address, ret); - atomic_set(&chip->i2c_busy, 0); return ret; } @@ -282,19 +255,12 @@ static int fusb302_i2c_block_write(struct fusb302_chip *chip, u8 address, if (length <= 0) return ret; - atomic_set(&chip->i2c_busy, 1); - - if (fusb302_is_suspended(chip)) { - atomic_set(&chip->i2c_busy, 0); - return -ETIMEDOUT; - } ret = i2c_smbus_write_i2c_block_data(chip->i2c_client, address, length, data); if (ret < 0) fusb302_log(chip, "cannot block write 0x%02x, len=%d, ret=%d", address, length, ret); - atomic_set(&chip->i2c_busy, 0); return ret; } @@ -304,18 +270,10 @@ static int fusb302_i2c_read(struct fusb302_chip *chip, { int ret = 0; - atomic_set(&chip->i2c_busy, 1); - - if (fusb302_is_suspended(chip)) { - atomic_set(&chip->i2c_busy, 0); - return -ETIMEDOUT; - } - ret = i2c_smbus_read_byte_data(chip->i2c_client, address); *data = (u8)ret; if (ret < 0) fusb302_log(chip, "cannot read %02x, ret=%d", address, ret); - atomic_set(&chip->i2c_busy, 0); return ret; } @@ -327,12 +285,6 @@ static int fusb302_i2c_block_read(struct fusb302_chip *chip, u8 address, if (length <= 0) return ret; - atomic_set(&chip->i2c_busy, 1); - - if (fusb302_is_suspended(chip)) { - atomic_set(&chip->i2c_busy, 0); - return -ETIMEDOUT; - } ret = i2c_smbus_read_i2c_block_data(chip->i2c_client, address, length, data); @@ -348,8 +300,6 @@ static int fusb302_i2c_block_read(struct fusb302_chip *chip, u8 address, } done: - atomic_set(&chip->i2c_busy, 0); - return ret; } @@ -519,32 +469,6 @@ static int tcpm_get_current_limit(struct tcpc_dev *dev) return current_limit; } -static int fusb302_set_cc_pull(struct fusb302_chip *chip, - bool pull_up, bool pull_down) -{ - int ret = 0; - u8 data = 0x00; - u8 mask = FUSB_REG_SWITCHES0_CC1_PU_EN | - FUSB_REG_SWITCHES0_CC2_PU_EN | - FUSB_REG_SWITCHES0_CC1_PD_EN | - FUSB_REG_SWITCHES0_CC2_PD_EN; - - if (pull_up) - data |= (chip->cc_polarity == TYPEC_POLARITY_CC1) ? - FUSB_REG_SWITCHES0_CC1_PU_EN : - FUSB_REG_SWITCHES0_CC2_PU_EN; - if (pull_down) - data |= FUSB_REG_SWITCHES0_CC1_PD_EN | - FUSB_REG_SWITCHES0_CC2_PD_EN; - ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES0, - mask, data); - if (ret < 0) - return ret; - chip->pull_up = pull_up; - - return ret; -} - static int fusb302_set_src_current(struct fusb302_chip *chip, enum src_current_status status) { @@ -634,6 +558,8 @@ static int fusb302_set_toggling(struct fusb302_chip *chip, return ret; chip->intr_togdone = false; } else { + /* Datasheet says vconn MUST be off when toggling */ + WARN(chip->vconn_on, "Vconn is on during toggle start"); /* unmask TOGDONE interrupt */ ret = fusb302_i2c_clear_bits(chip, FUSB_REG_MASKA, FUSB_REG_MASKA_TOGDONE); @@ -676,26 +602,27 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) { struct fusb302_chip *chip = container_of(dev, struct fusb302_chip, tcpc_dev); + u8 switches0_mask = FUSB_REG_SWITCHES0_CC1_PU_EN | + FUSB_REG_SWITCHES0_CC2_PU_EN | + FUSB_REG_SWITCHES0_CC1_PD_EN | + FUSB_REG_SWITCHES0_CC2_PD_EN; + u8 rd_mda, switches0_data = 0x00; int ret = 0; - bool pull_up, pull_down; - u8 rd_mda; - enum toggling_mode mode; mutex_lock(&chip->lock); switch (cc) { case TYPEC_CC_OPEN: - pull_up = false; - pull_down = false; break; case TYPEC_CC_RD: - pull_up = false; - pull_down = true; + switches0_data |= FUSB_REG_SWITCHES0_CC1_PD_EN | + FUSB_REG_SWITCHES0_CC2_PD_EN; break; case TYPEC_CC_RP_DEF: case TYPEC_CC_RP_1_5: case TYPEC_CC_RP_3_0: - pull_up = true; - pull_down = false; + switches0_data |= (chip->cc_polarity == TYPEC_POLARITY_CC1) ? + FUSB_REG_SWITCHES0_CC1_PU_EN : + FUSB_REG_SWITCHES0_CC2_PU_EN; break; default: fusb302_log(chip, "unsupported cc value %s", @@ -703,34 +630,38 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) ret = -EINVAL; goto done; } + + fusb302_log(chip, "cc := %s", typec_cc_status_name[cc]); + ret = fusb302_set_toggling(chip, TOGGLING_MODE_OFF); if (ret < 0) { - fusb302_log(chip, "cannot stop toggling, ret=%d", ret); + fusb302_log(chip, "cannot set toggling mode, ret=%d", ret); goto done; } - ret = fusb302_set_cc_pull(chip, pull_up, pull_down); + + ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES0, + switches0_mask, switches0_data); if (ret < 0) { - fusb302_log(chip, - "cannot set cc pulling up %s, down %s, ret = %d", - pull_up ? "True" : "False", - pull_down ? "True" : "False", - ret); + fusb302_log(chip, "cannot set pull-up/-down, ret = %d", ret); goto done; } /* reset the cc status */ chip->cc1 = TYPEC_CC_OPEN; chip->cc2 = TYPEC_CC_OPEN; + /* adjust current for SRC */ - if (pull_up) { - ret = fusb302_set_src_current(chip, cc_src_current[cc]); - if (ret < 0) { - fusb302_log(chip, "cannot set src current %s, ret=%d", - typec_cc_status_name[cc], ret); - goto done; - } + ret = fusb302_set_src_current(chip, cc_src_current[cc]); + if (ret < 0) { + fusb302_log(chip, "cannot set src current %s, ret=%d", + typec_cc_status_name[cc], ret); + goto done; } + /* enable/disable interrupts, BC_LVL for SNK and COMP_CHNG for SRC */ - if (pull_up) { + switch (cc) { + case TYPEC_CC_RP_DEF: + case TYPEC_CC_RP_1_5: + case TYPEC_CC_RP_3_0: rd_mda = rd_mda_value[cc_src_current[cc]]; ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, rd_mda); if (ret < 0) { @@ -748,10 +679,9 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) ret); goto done; } - chip->intr_bc_lvl = false; chip->intr_comp_chng = true; - } - if (pull_down) { + break; + case TYPEC_CC_RD: ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK, FUSB_REG_MASK_BC_LVL | FUSB_REG_MASK_COMP_CHNG, @@ -762,32 +692,10 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) goto done; } chip->intr_bc_lvl = true; - chip->intr_comp_chng = false; - } - fusb302_log(chip, "cc := %s", typec_cc_status_name[cc]); - - /* Enable detection for fixed SNK or SRC only roles */ - switch (cc) { - case TYPEC_CC_RD: - mode = TOGGLING_MODE_SNK; - break; - case TYPEC_CC_RP_DEF: - case TYPEC_CC_RP_1_5: - case TYPEC_CC_RP_3_0: - mode = TOGGLING_MODE_SRC; break; default: - mode = TOGGLING_MODE_OFF; break; } - - if (mode != TOGGLING_MODE_OFF) { - ret = fusb302_set_toggling(chip, mode); - if (ret < 0) - fusb302_log(chip, - "cannot set fixed role toggling mode, ret=%d", - ret); - } done: mutex_unlock(&chip->lock); @@ -1005,13 +913,27 @@ done: return ret; } -static int tcpm_start_drp_toggling(struct tcpc_dev *dev, - enum typec_cc_status cc) +static int tcpm_start_toggling(struct tcpc_dev *dev, + enum typec_port_type port_type, + enum typec_cc_status cc) { struct fusb302_chip *chip = container_of(dev, struct fusb302_chip, tcpc_dev); + enum toggling_mode mode = TOGGLING_MODE_OFF; int ret = 0; + switch (port_type) { + case TYPEC_PORT_SRC: + mode = TOGGLING_MODE_SRC; + break; + case TYPEC_PORT_SNK: + mode = TOGGLING_MODE_SNK; + break; + case TYPEC_PORT_DRP: + mode = TOGGLING_MODE_DRP; + break; + } + mutex_lock(&chip->lock); ret = fusb302_set_src_current(chip, cc_src_current[cc]); if (ret < 0) { @@ -1019,7 +941,7 @@ static int tcpm_start_drp_toggling(struct tcpc_dev *dev, typec_cc_status_name[cc], ret); goto done; } - ret = fusb302_set_toggling(chip, TOGGLING_MODE_DRP); + ret = fusb302_set_toggling(chip, mode); if (ret < 0) { fusb302_log(chip, "unable to start drp toggling, ret=%d", ret); @@ -1217,7 +1139,7 @@ static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev) fusb302_tcpc_dev->set_vbus = tcpm_set_vbus; fusb302_tcpc_dev->set_pd_rx = tcpm_set_pd_rx; fusb302_tcpc_dev->set_roles = tcpm_set_roles; - fusb302_tcpc_dev->start_drp_toggling = tcpm_start_drp_toggling; + fusb302_tcpc_dev->start_toggling = tcpm_start_toggling; fusb302_tcpc_dev->pd_transmit = tcpm_pd_transmit; } @@ -1226,38 +1148,36 @@ static const char * const cc_polarity_name[] = { [TYPEC_POLARITY_CC2] = "Polarity_CC2", }; -static int fusb302_set_cc_polarity(struct fusb302_chip *chip, - enum typec_cc_polarity cc_polarity) +static int fusb302_set_cc_polarity_and_pull(struct fusb302_chip *chip, + enum typec_cc_polarity cc_polarity, + bool pull_up, bool pull_down) { int ret = 0; - u8 switches0_mask = FUSB_REG_SWITCHES0_CC1_PU_EN | - FUSB_REG_SWITCHES0_CC2_PU_EN | - FUSB_REG_SWITCHES0_VCONN_CC1 | - FUSB_REG_SWITCHES0_VCONN_CC2 | - FUSB_REG_SWITCHES0_MEAS_CC1 | - FUSB_REG_SWITCHES0_MEAS_CC2; u8 switches0_data = 0x00; u8 switches1_mask = FUSB_REG_SWITCHES1_TXCC1_EN | FUSB_REG_SWITCHES1_TXCC2_EN; u8 switches1_data = 0x00; + if (pull_down) + switches0_data |= FUSB_REG_SWITCHES0_CC1_PD_EN | + FUSB_REG_SWITCHES0_CC2_PD_EN; + if (cc_polarity == TYPEC_POLARITY_CC1) { - switches0_data = FUSB_REG_SWITCHES0_MEAS_CC1; + switches0_data |= FUSB_REG_SWITCHES0_MEAS_CC1; if (chip->vconn_on) switches0_data |= FUSB_REG_SWITCHES0_VCONN_CC2; - if (chip->pull_up) + if (pull_up) switches0_data |= FUSB_REG_SWITCHES0_CC1_PU_EN; switches1_data = FUSB_REG_SWITCHES1_TXCC1_EN; } else { - switches0_data = FUSB_REG_SWITCHES0_MEAS_CC2; + switches0_data |= FUSB_REG_SWITCHES0_MEAS_CC2; if (chip->vconn_on) switches0_data |= FUSB_REG_SWITCHES0_VCONN_CC1; - if (chip->pull_up) + if (pull_up) switches0_data |= FUSB_REG_SWITCHES0_CC2_PU_EN; switches1_data = FUSB_REG_SWITCHES1_TXCC2_EN; } - ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES0, - switches0_mask, switches0_data); + ret = fusb302_i2c_write(chip, FUSB_REG_SWITCHES0, switches0_data); if (ret < 0) return ret; ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES1, @@ -1278,16 +1198,10 @@ static int fusb302_handle_togdone_snk(struct fusb302_chip *chip, enum typec_cc_polarity cc_polarity; enum typec_cc_status cc_status_active, cc1, cc2; - /* set pull_up, pull_down */ - ret = fusb302_set_cc_pull(chip, false, true); - if (ret < 0) { - fusb302_log(chip, "cannot set cc to pull down, ret=%d", ret); - return ret; - } - /* set polarity */ + /* set polarity and pull_up, pull_down */ cc_polarity = (togdone_result == FUSB_REG_STATUS1A_TOGSS_SNK1) ? TYPEC_POLARITY_CC1 : TYPEC_POLARITY_CC2; - ret = fusb302_set_cc_polarity(chip, cc_polarity); + ret = fusb302_set_cc_polarity_and_pull(chip, cc_polarity, false, true); if (ret < 0) { fusb302_log(chip, "cannot set cc polarity %s, ret=%d", cc_polarity_name[cc_polarity], ret); @@ -1337,6 +1251,62 @@ static int fusb302_handle_togdone_snk(struct fusb302_chip *chip, return ret; } +/* On error returns < 0, otherwise a typec_cc_status value */ +static int fusb302_get_src_cc_status(struct fusb302_chip *chip, + enum typec_cc_polarity cc_polarity, + enum typec_cc_status *cc) +{ + u8 ra_mda = ra_mda_value[chip->src_current_status]; + u8 rd_mda = rd_mda_value[chip->src_current_status]; + u8 switches0_data, status0; + int ret; + + /* Step 1: Set switches so that we measure the right CC pin */ + switches0_data = (cc_polarity == TYPEC_POLARITY_CC1) ? + FUSB_REG_SWITCHES0_CC1_PU_EN | FUSB_REG_SWITCHES0_MEAS_CC1 : + FUSB_REG_SWITCHES0_CC2_PU_EN | FUSB_REG_SWITCHES0_MEAS_CC2; + ret = fusb302_i2c_write(chip, FUSB_REG_SWITCHES0, switches0_data); + if (ret < 0) + return ret; + + fusb302_i2c_read(chip, FUSB_REG_SWITCHES0, &status0); + fusb302_log(chip, "get_src_cc_status switches: 0x%0x", status0); + + /* Step 2: Set compararator volt to differentiate between Open and Rd */ + ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, rd_mda); + if (ret < 0) + return ret; + + usleep_range(50, 100); + ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0); + if (ret < 0) + return ret; + + fusb302_log(chip, "get_src_cc_status rd_mda status0: 0x%0x", status0); + if (status0 & FUSB_REG_STATUS0_COMP) { + *cc = TYPEC_CC_OPEN; + return 0; + } + + /* Step 3: Set compararator input to differentiate between Rd and Ra. */ + ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, ra_mda); + if (ret < 0) + return ret; + + usleep_range(50, 100); + ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0); + if (ret < 0) + return ret; + + fusb302_log(chip, "get_src_cc_status ra_mda status0: 0x%0x", status0); + if (status0 & FUSB_REG_STATUS0_COMP) + *cc = TYPEC_CC_RD; + else + *cc = TYPEC_CC_RA; + + return 0; +} + static int fusb302_handle_togdone_src(struct fusb302_chip *chip, u8 togdone_result) { @@ -1347,77 +1317,62 @@ static int fusb302_handle_togdone_src(struct fusb302_chip *chip, * - set I_COMP interrupt on */ int ret = 0; - u8 status0; - u8 ra_mda = ra_mda_value[chip->src_current_status]; u8 rd_mda = rd_mda_value[chip->src_current_status]; - bool ra_comp, rd_comp; + enum toggling_mode toggling_mode = chip->toggling_mode; enum typec_cc_polarity cc_polarity; - enum typec_cc_status cc_status_active, cc1, cc2; + enum typec_cc_status cc1, cc2; - /* set pull_up, pull_down */ - ret = fusb302_set_cc_pull(chip, true, false); - if (ret < 0) { - fusb302_log(chip, "cannot set cc to pull up, ret=%d", ret); + /* + * The toggle-engine will stop in a src state if it sees either Ra or + * Rd. Determine the status for both CC pins, starting with the one + * where toggling stopped, as that is where the switches point now. + */ + if (togdone_result == FUSB_REG_STATUS1A_TOGSS_SRC1) + ret = fusb302_get_src_cc_status(chip, TYPEC_POLARITY_CC1, &cc1); + else + ret = fusb302_get_src_cc_status(chip, TYPEC_POLARITY_CC2, &cc2); + if (ret < 0) return ret; - } - /* set polarity */ - cc_polarity = (togdone_result == FUSB_REG_STATUS1A_TOGSS_SRC1) ? - TYPEC_POLARITY_CC1 : TYPEC_POLARITY_CC2; - ret = fusb302_set_cc_polarity(chip, cc_polarity); + /* we must turn off toggling before we can measure the other pin */ + ret = fusb302_set_toggling(chip, TOGGLING_MODE_OFF); if (ret < 0) { - fusb302_log(chip, "cannot set cc polarity %s, ret=%d", - cc_polarity_name[cc_polarity], ret); + fusb302_log(chip, "cannot set toggling mode off, ret=%d", ret); return ret; } - /* fusb302_set_cc_polarity() has set the correct measure block */ - ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, rd_mda); - if (ret < 0) - return ret; - usleep_range(50, 100); - ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0); + /* get the status of the other pin */ + if (togdone_result == FUSB_REG_STATUS1A_TOGSS_SRC1) + ret = fusb302_get_src_cc_status(chip, TYPEC_POLARITY_CC2, &cc2); + else + ret = fusb302_get_src_cc_status(chip, TYPEC_POLARITY_CC1, &cc1); if (ret < 0) return ret; - rd_comp = !!(status0 & FUSB_REG_STATUS0_COMP); - if (!rd_comp) { - ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, ra_mda); - if (ret < 0) - return ret; - usleep_range(50, 100); - ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0); - if (ret < 0) - return ret; - ra_comp = !!(status0 & FUSB_REG_STATUS0_COMP); + + /* determine polarity based on the status of both pins */ + if (cc1 == TYPEC_CC_RD && + (cc2 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_RA)) { + cc_polarity = TYPEC_POLARITY_CC1; + } else if (cc2 == TYPEC_CC_RD && + (cc1 == TYPEC_CC_OPEN || cc1 == TYPEC_CC_RA)) { + cc_polarity = TYPEC_POLARITY_CC2; + } else { + fusb302_log(chip, "unexpected CC status cc1=%s, cc2=%s, restarting toggling", + typec_cc_status_name[cc1], + typec_cc_status_name[cc2]); + return fusb302_set_toggling(chip, toggling_mode); } - if (rd_comp) - cc_status_active = TYPEC_CC_OPEN; - else if (ra_comp) - cc_status_active = TYPEC_CC_RD; - else - /* Ra is not supported, report as Open */ - cc_status_active = TYPEC_CC_OPEN; - /* restart toggling if the cc status on the active line is OPEN */ - if (cc_status_active == TYPEC_CC_OPEN) { - fusb302_log(chip, "restart toggling as CC_OPEN detected"); - ret = fusb302_set_toggling(chip, chip->toggling_mode); + /* set polarity and pull_up, pull_down */ + ret = fusb302_set_cc_polarity_and_pull(chip, cc_polarity, true, false); + if (ret < 0) { + fusb302_log(chip, "cannot set cc polarity %s, ret=%d", + cc_polarity_name[cc_polarity], ret); return ret; } /* update tcpm with the new cc value */ - cc1 = (cc_polarity == TYPEC_POLARITY_CC1) ? - cc_status_active : TYPEC_CC_OPEN; - cc2 = (cc_polarity == TYPEC_POLARITY_CC2) ? - cc_status_active : TYPEC_CC_OPEN; if ((chip->cc1 != cc1) || (chip->cc2 != cc2)) { chip->cc1 = cc1; chip->cc2 = cc2; tcpm_cc_change(chip->tcpm_port); } - /* turn off toggling */ - ret = fusb302_set_toggling(chip, TOGGLING_MODE_OFF); - if (ret < 0) { - fusb302_log(chip, - "cannot set toggling mode off, ret=%d", ret); - return ret; - } /* set MDAC to Rd threshold, and unmask I_COMP for unplug detection */ ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, rd_mda); if (ret < 0) @@ -1427,7 +1382,7 @@ static int fusb302_handle_togdone_src(struct fusb302_chip *chip, FUSB_REG_MASK_COMP_CHNG); if (ret < 0) { fusb302_log(chip, - "cannot unmask bc_lcl interrupt, ret=%d", ret); + "cannot unmask comp_chng interrupt, ret=%d", ret); return ret; } chip->intr_comp_chng = true; @@ -1532,6 +1487,25 @@ static int fusb302_pd_read_message(struct fusb302_chip *chip, static irqreturn_t fusb302_irq_intn(int irq, void *dev_id) { struct fusb302_chip *chip = dev_id; + unsigned long flags; + + /* Disable our level triggered IRQ until our irq_work has cleared it */ + disable_irq_nosync(chip->gpio_int_n_irq); + + spin_lock_irqsave(&chip->irq_lock, flags); + if (chip->irq_suspended) + chip->irq_while_suspended = true; + else + schedule_work(&chip->irq_work); + spin_unlock_irqrestore(&chip->irq_lock, flags); + + return IRQ_HANDLED; +} + +static void fusb302_irq_work(struct work_struct *work) +{ + struct fusb302_chip *chip = container_of(work, struct fusb302_chip, + irq_work); int ret = 0; u8 interrupt; u8 interrupta; @@ -1602,11 +1576,9 @@ static irqreturn_t fusb302_irq_intn(int irq, void *dev_id) fusb302_log(chip, "IRQ: COMP_CHNG, comp=%s", comp_result ? "true" : "false"); if (comp_result) { - /* cc level > Rd_threashold, detach */ - if (chip->cc_polarity == TYPEC_POLARITY_CC1) - chip->cc1 = TYPEC_CC_OPEN; - else - chip->cc2 = TYPEC_CC_OPEN; + /* cc level > Rd_threshold, detach */ + chip->cc1 = TYPEC_CC_OPEN; + chip->cc2 = TYPEC_CC_OPEN; tcpm_cc_change(chip->tcpm_port); } } @@ -1662,8 +1634,7 @@ static irqreturn_t fusb302_irq_intn(int irq, void *dev_id) } done: mutex_unlock(&chip->lock); - - return IRQ_HANDLED; + enable_irq(chip->gpio_int_n_irq); } static int init_gpio(struct fusb302_chip *chip) @@ -1779,6 +1750,8 @@ static int fusb302_probe(struct i2c_client *client, if (!chip->wq) return -ENOMEM; + spin_lock_init(&chip->irq_lock); + INIT_WORK(&chip->irq_work, fusb302_irq_work); INIT_DELAYED_WORK(&chip->bc_lvl_handler, fusb302_bc_lvl_handler_work); init_tcpc_dev(&chip->tcpc_dev); @@ -1798,10 +1771,9 @@ static int fusb302_probe(struct i2c_client *client, goto destroy_workqueue; } - ret = devm_request_threaded_irq(chip->dev, chip->gpio_int_n_irq, - NULL, fusb302_irq_intn, - IRQF_ONESHOT | IRQF_TRIGGER_LOW, - "fsc_interrupt_int_n", chip); + ret = request_irq(chip->gpio_int_n_irq, 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; @@ -1824,6 +1796,10 @@ static int fusb302_remove(struct i2c_client *client) { struct fusb302_chip *chip = i2c_get_clientdata(client); + disable_irq_wake(chip->gpio_int_n_irq); + free_irq(chip->gpio_int_n_irq, chip); + cancel_work_sync(&chip->irq_work); + cancel_delayed_work_sync(&chip->bc_lvl_handler); tcpm_unregister_port(chip->tcpm_port); destroy_workqueue(chip->wq); fusb302_debugfs_exit(chip); @@ -1834,19 +1810,29 @@ static int fusb302_remove(struct i2c_client *client) static int fusb302_pm_suspend(struct device *dev) { struct fusb302_chip *chip = dev->driver_data; + unsigned long flags; - if (atomic_read(&chip->i2c_busy)) - return -EBUSY; - atomic_set(&chip->pm_suspend, 1); + spin_lock_irqsave(&chip->irq_lock, flags); + chip->irq_suspended = true; + spin_unlock_irqrestore(&chip->irq_lock, flags); + /* Make sure any pending irq_work is finished before the bus suspends */ + flush_work(&chip->irq_work); return 0; } static int fusb302_pm_resume(struct device *dev) { struct fusb302_chip *chip = dev->driver_data; + unsigned long flags; - atomic_set(&chip->pm_suspend, 0); + spin_lock_irqsave(&chip->irq_lock, flags); + if (chip->irq_while_suspended) { + schedule_work(&chip->irq_work); + chip->irq_while_suspended = false; + } + chip->irq_suspended = false; + spin_unlock_irqrestore(&chip->irq_lock, flags); return 0; } diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index ac6b418b15f1..c1f7073a56de 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -100,13 +100,17 @@ static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc) return 0; } -static int tcpci_start_drp_toggling(struct tcpc_dev *tcpc, - enum typec_cc_status cc) +static int tcpci_start_toggling(struct tcpc_dev *tcpc, + enum typec_port_type port_type, + enum typec_cc_status cc) { int ret; struct tcpci *tcpci = tcpc_to_tcpci(tcpc); unsigned int reg = TCPC_ROLE_CTRL_DRP; + if (port_type != TYPEC_PORT_DRP) + return -EOPNOTSUPP; + /* Handle vendor drp toggling */ if (tcpci->data->start_drp_toggling) { ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc); @@ -511,7 +515,7 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data) tcpci->tcpc.get_cc = tcpci_get_cc; tcpci->tcpc.set_polarity = tcpci_set_polarity; tcpci->tcpc.set_vconn = tcpci_set_vconn; - tcpci->tcpc.start_drp_toggling = tcpci_start_drp_toggling; + tcpci->tcpc.start_toggling = tcpci_start_toggling; tcpci->tcpc.set_pd_rx = tcpci_set_pd_rx; tcpci->tcpc.set_roles = tcpci_set_roles; diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index a2233d72ae7c..fba32d84e578 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -31,7 +31,7 @@ #define FOREACH_STATE(S) \ S(INVALID_STATE), \ - S(DRP_TOGGLING), \ + S(TOGGLING), \ S(SRC_UNATTACHED), \ S(SRC_ATTACH_WAIT), \ S(SRC_ATTACHED), \ @@ -472,7 +472,7 @@ static void tcpm_log(struct tcpm_port *port, const char *fmt, ...) /* Do not log while disconnected and unattached */ if (tcpm_port_is_disconnected(port) && (port->state == SRC_UNATTACHED || port->state == SNK_UNATTACHED || - port->state == DRP_TOGGLING)) + port->state == TOGGLING)) return; va_start(args, fmt); @@ -2540,20 +2540,16 @@ static int tcpm_set_charge(struct tcpm_port *port, bool charge) return 0; } -static bool tcpm_start_drp_toggling(struct tcpm_port *port, - enum typec_cc_status cc) +static bool tcpm_start_toggling(struct tcpm_port *port, enum typec_cc_status cc) { int ret; - if (port->tcpc->start_drp_toggling && - port->port_type == TYPEC_PORT_DRP) { - tcpm_log_force(port, "Start DRP toggling"); - ret = port->tcpc->start_drp_toggling(port->tcpc, cc); - if (!ret) - return true; - } + if (!port->tcpc->start_toggling) + return false; - return false; + tcpm_log_force(port, "Start toggling"); + ret = port->tcpc->start_toggling(port->tcpc, port->port_type, cc); + return ret == 0; } static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc) @@ -2847,15 +2843,15 @@ static void run_state_machine(struct tcpm_port *port) port->enter_state = port->state; switch (port->state) { - case DRP_TOGGLING: + case TOGGLING: break; /* SRC states */ case SRC_UNATTACHED: if (!port->non_pd_role_swap) tcpm_swap_complete(port, -ENOTCONN); tcpm_src_detach(port); - if (tcpm_start_drp_toggling(port, tcpm_rp_cc(port))) { - tcpm_set_state(port, DRP_TOGGLING, 0); + if (tcpm_start_toggling(port, tcpm_rp_cc(port))) { + tcpm_set_state(port, TOGGLING, 0); break; } tcpm_set_cc(port, tcpm_rp_cc(port)); @@ -3053,8 +3049,8 @@ static void run_state_machine(struct tcpm_port *port) tcpm_swap_complete(port, -ENOTCONN); tcpm_pps_complete(port, -ENOTCONN); tcpm_snk_detach(port); - if (tcpm_start_drp_toggling(port, TYPEC_CC_RD)) { - tcpm_set_state(port, DRP_TOGGLING, 0); + if (tcpm_start_toggling(port, TYPEC_CC_RD)) { + tcpm_set_state(port, TOGGLING, 0); break; } tcpm_set_cc(port, TYPEC_CC_RD); @@ -3621,7 +3617,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, : "connected"); switch (port->state) { - case DRP_TOGGLING: + case TOGGLING: if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || tcpm_port_is_source(port)) tcpm_set_state(port, SRC_ATTACH_WAIT, 0); diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 6770afd40765..6b317c150bdd 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -416,12 +416,16 @@ static int wcove_pd_transmit(struct tcpc_dev *tcpc, return regmap_write(wcove->regmap, USBC_TXCMD, cmd | USBC_TXCMD_START); } -static int wcove_start_drp_toggling(struct tcpc_dev *tcpc, - enum typec_cc_status cc) +static int wcove_start_toggling(struct tcpc_dev *tcpc, + enum typec_port_type port_type, + enum typec_cc_status cc) { struct wcove_typec *wcove = tcpc_to_wcove(tcpc); unsigned int usbc_ctrl; + if (port_type != TYPEC_PORT_DRP) + return -EOPNOTSUPP; + usbc_ctrl = USBC_CONTROL1_MODE_DRP | USBC_CONTROL1_DRPTOGGLE_RANDOM; switch (cc) { @@ -587,17 +591,14 @@ static const u32 snk_pdo[] = { PDO_VAR(5000, 12000, 3000), }; -static struct tcpc_config wcove_typec_config = { - .src_pdo = src_pdo, - .nr_src_pdo = ARRAY_SIZE(src_pdo), - .snk_pdo = snk_pdo, - .nr_snk_pdo = ARRAY_SIZE(snk_pdo), - - .operating_snk_mw = 15000, - - .type = TYPEC_PORT_DRP, - .data = TYPEC_PORT_DRD, - .default_role = TYPEC_SINK, +static const struct property_entry wcove_props[] = { + PROPERTY_ENTRY_STRING("data-role", "dual"), + PROPERTY_ENTRY_STRING("power-role", "dual"), + PROPERTY_ENTRY_STRING("try-power-role", "sink"), + PROPERTY_ENTRY_U32_ARRAY("source-pdos", src_pdo), + PROPERTY_ENTRY_U32_ARRAY("sink-pdos", snk_pdo), + PROPERTY_ENTRY_U32("op-sink-microwatt", 15000), + { } }; static int wcove_typec_probe(struct platform_device *pdev) @@ -642,23 +643,28 @@ static int wcove_typec_probe(struct platform_device *pdev) wcove->tcpc.set_polarity = wcove_set_polarity; wcove->tcpc.set_vconn = wcove_set_vconn; wcove->tcpc.set_current_limit = wcove_set_current_limit; - wcove->tcpc.start_drp_toggling = wcove_start_drp_toggling; + wcove->tcpc.start_toggling = wcove_start_toggling; wcove->tcpc.set_pd_rx = wcove_set_pd_rx; wcove->tcpc.set_roles = wcove_set_roles; wcove->tcpc.pd_transmit = wcove_pd_transmit; - wcove->tcpc.config = &wcove_typec_config; + wcove->tcpc.fwnode = fwnode_create_software_node(wcove_props, NULL); + if (IS_ERR(wcove->tcpc.fwnode)) + return PTR_ERR(wcove->tcpc.fwnode); wcove->tcpm = tcpm_register_port(wcove->dev, &wcove->tcpc); - if (IS_ERR(wcove->tcpm)) + if (IS_ERR(wcove->tcpm)) { + fwnode_remove_software_node(wcove->tcpc.fwnode); return PTR_ERR(wcove->tcpm); + } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, wcove_typec_irq, IRQF_ONESHOT, "wcove_typec", wcove); if (ret) { tcpm_unregister_port(wcove->tcpm); + fwnode_remove_software_node(wcove->tcpc.fwnode); return ret; } @@ -678,6 +684,7 @@ static int wcove_typec_remove(struct platform_device *pdev) regmap_write(wcove->regmap, USBC_IRQMASK2, val | USBC_IRQMASK2_ALL); tcpm_unregister_port(wcove->tcpm); + fwnode_remove_software_node(wcove->tcpc.fwnode); return 0; } diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 2f4900b26210..b35e15a1f02c 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -1,12 +1,15 @@ # SPDX-License-Identifier: GPL-2.0 -CFLAGS_trace.o := -I$(src) +CFLAGS_trace.o := -I$(src) -obj-$(CONFIG_TYPEC_UCSI) += typec_ucsi.o +obj-$(CONFIG_TYPEC_UCSI) += typec_ucsi.o -typec_ucsi-y := ucsi.o +typec_ucsi-y := ucsi.o -typec_ucsi-$(CONFIG_TRACING) += trace.o +typec_ucsi-$(CONFIG_TRACING) += trace.o -obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o +ifneq ($(CONFIG_TYPEC_DP_ALTMODE),) + typec_ucsi-y += displayport.o +endif -obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o +obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o +obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c new file mode 100644 index 000000000000..6c103697c582 --- /dev/null +++ b/drivers/usb/typec/ucsi/displayport.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * UCSI DisplayPort Alternate Mode Support + * + * Copyright (C) 2018, Intel Corporation + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + */ + +#include <linux/usb/typec_dp.h> +#include <linux/usb/pd_vdo.h> + +#include "ucsi.h" + +#define UCSI_CMD_SET_NEW_CAM(_con_num_, _enter_, _cam_, _am_) \ + (UCSI_SET_NEW_CAM | ((_con_num_) << 16) | ((_enter_) << 23) | \ + ((_cam_) << 24) | ((u64)(_am_) << 32)) + +struct ucsi_dp { + struct typec_displayport_data data; + struct ucsi_connector *con; + struct typec_altmode *alt; + struct work_struct work; + int offset; + + bool override; + bool initialized; + + u32 header; + u32 *vdo_data; + u8 vdo_size; +}; + +/* + * Note. Alternate mode control is optional feature in UCSI. It means that even + * if the system supports alternate modes, the OS may not be aware of them. + * + * In most cases however, the OS will be able to see the supported alternate + * modes, but it may still not be able to configure them, not even enter or exit + * them. That is because UCSI defines alt mode details and alt mode "overriding" + * as separate options. + * + * In case alt mode details are supported, but overriding is not, the driver + * will still display the supported pin assignments and configuration, but any + * changes the user attempts to do will lead into failure with return value of + * -EOPNOTSUPP. + */ + +static int ucsi_displayport_enter(struct typec_altmode *alt) +{ + struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); + struct ucsi_control ctrl; + u8 cur = 0; + int ret; + + mutex_lock(&dp->con->lock); + + 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); + return -EOPNOTSUPP; + } + + UCSI_CMD_GET_CURRENT_CAM(ctrl, dp->con->num); + ret = ucsi_send_command(dp->con->ucsi, &ctrl, &cur, sizeof(cur)); + if (ret < 0) { + if (dp->con->ucsi->ppm->data->version > 0x0100) { + mutex_unlock(&dp->con->lock); + return ret; + } + cur = 0xff; + } + + if (cur != 0xff) { + mutex_unlock(&dp->con->lock); + return -EBUSY; + } + + /* + * We can't send the New CAM command yet to the PPM as it needs the + * configuration value as well. Pretending that we have now entered the + * mode, and letting the alt mode driver continue. + */ + + dp->header = VDO(USB_TYPEC_DP_SID, 1, CMD_ENTER_MODE); + dp->header |= VDO_OPOS(USB_TYPEC_DP_MODE); + dp->header |= VDO_CMDT(CMDT_RSP_ACK); + + dp->vdo_data = NULL; + dp->vdo_size = 1; + + schedule_work(&dp->work); + + mutex_unlock(&dp->con->lock); + + return 0; +} + +static int ucsi_displayport_exit(struct typec_altmode *alt) +{ + struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); + struct ucsi_control ctrl; + int ret = 0; + + mutex_lock(&dp->con->lock); + + if (!dp->override) { + const struct typec_altmode *p = typec_altmode_get_partner(alt); + + dev_warn(&p->dev, + "firmware doesn't support alternate mode overriding\n"); + ret = -EOPNOTSUPP; + goto out_unlock; + } + + ctrl.raw_cmd = UCSI_CMD_SET_NEW_CAM(dp->con->num, 0, dp->offset, 0); + ret = ucsi_send_command(dp->con->ucsi, &ctrl, NULL, 0); + if (ret < 0) + goto out_unlock; + + dp->header = VDO(USB_TYPEC_DP_SID, 1, CMD_EXIT_MODE); + dp->header |= VDO_OPOS(USB_TYPEC_DP_MODE); + dp->header |= VDO_CMDT(CMDT_RSP_ACK); + + dp->vdo_data = NULL; + dp->vdo_size = 1; + + schedule_work(&dp->work); + +out_unlock: + mutex_unlock(&dp->con->lock); + + return ret; +} + +/* + * We do not actually have access to the Status Update VDO, so we have to guess + * things. + */ +static int ucsi_displayport_status_update(struct ucsi_dp *dp) +{ + u32 cap = dp->alt->vdo; + + dp->data.status = DP_STATUS_ENABLED; + + /* + * If pin assignement D is supported, claiming always + * that Multi-function is preferred. + */ + if (DP_CAP_CAPABILITY(cap) & DP_CAP_UFP_D) { + dp->data.status |= DP_STATUS_CON_UFP_D; + + if (DP_CAP_UFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) + dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; + } else { + dp->data.status |= DP_STATUS_CON_DFP_D; + + if (DP_CAP_DFP_D_PIN_ASSIGN(cap) & BIT(DP_PIN_ASSIGN_D)) + dp->data.status |= DP_STATUS_PREFER_MULTI_FUNC; + } + + dp->vdo_data = &dp->data.status; + dp->vdo_size = 2; + + return 0; +} + +static int ucsi_displayport_configure(struct ucsi_dp *dp) +{ + u32 pins = DP_CONF_GET_PIN_ASSIGN(dp->data.conf); + struct ucsi_control ctrl; + + if (!dp->override) + return 0; + + ctrl.raw_cmd = UCSI_CMD_SET_NEW_CAM(dp->con->num, 1, dp->offset, pins); + + return ucsi_send_command(dp->con->ucsi, &ctrl, NULL, 0); +} + +static int ucsi_displayport_vdm(struct typec_altmode *alt, + u32 header, const u32 *data, int count) +{ + struct ucsi_dp *dp = typec_altmode_get_drvdata(alt); + int cmd_type = PD_VDO_CMDT(header); + int cmd = PD_VDO_CMD(header); + + mutex_lock(&dp->con->lock); + + 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); + return -EOPNOTSUPP; + } + + switch (cmd_type) { + case CMDT_INIT: + dp->header = VDO(USB_TYPEC_DP_SID, 1, cmd); + dp->header |= VDO_OPOS(USB_TYPEC_DP_MODE); + + switch (cmd) { + case DP_CMD_STATUS_UPDATE: + if (ucsi_displayport_status_update(dp)) + dp->header |= VDO_CMDT(CMDT_RSP_NAK); + else + dp->header |= VDO_CMDT(CMDT_RSP_ACK); + break; + case DP_CMD_CONFIGURE: + dp->data.conf = *data; + if (ucsi_displayport_configure(dp)) { + dp->header |= VDO_CMDT(CMDT_RSP_NAK); + } else { + dp->header |= VDO_CMDT(CMDT_RSP_ACK); + if (dp->initialized) + ucsi_altmode_update_active(dp->con); + else + dp->initialized = true; + } + break; + default: + dp->header |= VDO_CMDT(CMDT_RSP_ACK); + break; + } + + schedule_work(&dp->work); + break; + default: + break; + } + + mutex_unlock(&dp->con->lock); + + return 0; +} + +static const struct typec_altmode_ops ucsi_displayport_ops = { + .enter = ucsi_displayport_enter, + .exit = ucsi_displayport_exit, + .vdm = ucsi_displayport_vdm, +}; + +static void ucsi_displayport_work(struct work_struct *work) +{ + struct ucsi_dp *dp = container_of(work, struct ucsi_dp, work); + int ret; + + mutex_lock(&dp->con->lock); + + ret = typec_altmode_vdm(dp->alt, dp->header, + dp->vdo_data, dp->vdo_size); + if (ret) + dev_err(&dp->alt->dev, "VDM 0x%x failed\n", dp->header); + + dp->vdo_data = NULL; + dp->vdo_size = 0; + dp->header = 0; + + mutex_unlock(&dp->con->lock); +} + +void ucsi_displayport_remove_partner(struct typec_altmode *alt) +{ + struct ucsi_dp *dp; + + if (!alt) + return; + + dp = typec_altmode_get_drvdata(alt); + dp->data.conf = 0; + dp->data.status = 0; + dp->initialized = false; +} + +struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con, + bool override, int offset, + struct typec_altmode_desc *desc) +{ + u8 all_assignments = BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D) | + BIT(DP_PIN_ASSIGN_E); + struct typec_altmode *alt; + struct ucsi_dp *dp; + + /* We can't rely on the firmware with the capabilities. */ + desc->vdo |= DP_CAP_DP_SIGNALING | DP_CAP_RECEPTACLE; + + /* Claiming that we support all pin assignments */ + desc->vdo |= all_assignments << 8; + desc->vdo |= all_assignments << 16; + + alt = typec_port_register_altmode(con->port, desc); + if (IS_ERR(alt)) + return alt; + + dp = devm_kzalloc(&alt->dev, sizeof(*dp), GFP_KERNEL); + if (!dp) { + typec_unregister_altmode(alt); + return ERR_PTR(-ENOMEM); + } + + INIT_WORK(&dp->work, ucsi_displayport_work); + dp->override = override; + dp->offset = offset; + dp->con = con; + dp->alt = alt; + + alt->ops = &ucsi_displayport_ops; + typec_altmode_set_drvdata(alt, dp); + + return alt; +} diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c index ffa3b4c3f338..1dabafb74320 100644 --- a/drivers/usb/typec/ucsi/trace.c +++ b/drivers/usb/typec/ucsi/trace.c @@ -60,3 +60,15 @@ const char *ucsi_cci_str(u32 cci) return ""; } + +static const char * const ucsi_recipient_strs[] = { + [UCSI_RECIPIENT_CON] = "port", + [UCSI_RECIPIENT_SOP] = "partner", + [UCSI_RECIPIENT_SOP_P] = "plug (prime)", + [UCSI_RECIPIENT_SOP_PP] = "plug (double prime)", +}; + +const char *ucsi_recipient_str(u8 recipient) +{ + return ucsi_recipient_strs[recipient]; +} diff --git a/drivers/usb/typec/ucsi/trace.h b/drivers/usb/typec/ucsi/trace.h index 5e2906df2db7..783ec9c72055 100644 --- a/drivers/usb/typec/ucsi/trace.h +++ b/drivers/usb/typec/ucsi/trace.h @@ -7,6 +7,7 @@ #define __UCSI_TRACE_H #include <linux/tracepoint.h> +#include <linux/usb/typec_altmode.h> const char *ucsi_cmd_str(u64 raw_cmd); const char *ucsi_ack_str(u8 ack); @@ -134,6 +135,31 @@ DEFINE_EVENT(ucsi_log_connector_status, ucsi_register_port, TP_ARGS(port, status) ); +DECLARE_EVENT_CLASS(ucsi_log_register_altmode, + TP_PROTO(u8 recipient, struct typec_altmode *alt), + TP_ARGS(recipient, alt), + TP_STRUCT__entry( + __field(u8, recipient) + __field(u16, svid) + __field(u8, mode) + __field(u32, vdo) + ), + TP_fast_assign( + __entry->recipient = recipient; + __entry->svid = alt->svid; + __entry->mode = alt->mode; + __entry->vdo = alt->vdo; + ), + TP_printk("%s alt mode: svid %04x, mode %d vdo %x", + ucsi_recipient_str(__entry->recipient), __entry->svid, + __entry->mode, __entry->vdo) +); + +DEFINE_EVENT(ucsi_log_register_altmode, ucsi_register_altmode, + TP_PROTO(u8 recipient, struct typec_altmode *alt), + TP_ARGS(recipient, alt) +); + #endif /* __UCSI_TRACE_H */ /* This part must be outside protection */ diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 8d0a6fe748bd..7850b851cecd 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/usb/typec.h> +#include <linux/usb/typec_dp.h> #include "ucsi.h" #include "trace.h" @@ -39,49 +39,6 @@ */ #define UCSI_SWAP_TIMEOUT_MS 5000 -enum ucsi_status { - UCSI_IDLE = 0, - UCSI_BUSY, - UCSI_ERROR, -}; - -struct ucsi_connector { - int num; - - struct ucsi *ucsi; - struct work_struct work; - struct completion complete; - - struct typec_port *port; - struct typec_partner *partner; - - struct typec_capability typec_cap; - - struct ucsi_connector_status status; - struct ucsi_connector_capability cap; -}; - -struct ucsi { - struct device *dev; - struct ucsi_ppm *ppm; - - enum ucsi_status status; - struct completion complete; - struct ucsi_capability cap; - struct ucsi_connector *connector; - - struct work_struct work; - - /* PPM Communication lock */ - struct mutex ppm_lock; - - /* PPM communication flags */ - unsigned long flags; -#define EVENT_PENDING 0 -#define COMMAND_PENDING 1 -#define ACK_PENDING 2 -}; - static inline int ucsi_sync(struct ucsi *ucsi) { if (ucsi->ppm && ucsi->ppm->sync) @@ -238,8 +195,226 @@ err: return ret; } +int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, + void *retval, size_t size) +{ + int ret; + + mutex_lock(&ucsi->ppm_lock); + ret = ucsi_run_command(ucsi, ctrl, retval, size); + mutex_unlock(&ucsi->ppm_lock); + + return ret; +} + /* -------------------------------------------------------------------------- */ +void ucsi_altmode_update_active(struct ucsi_connector *con) +{ + const struct typec_altmode *altmode = NULL; + struct ucsi_control ctrl; + int ret; + u8 cur; + int i; + + UCSI_CMD_GET_CURRENT_CAM(ctrl, con->num); + ret = ucsi_run_command(con->ucsi, &ctrl, &cur, sizeof(cur)); + if (ret < 0) { + if (con->ucsi->ppm->data->version > 0x0100) { + dev_err(con->ucsi->dev, + "GET_CURRENT_CAM command failed\n"); + return; + } + cur = 0xff; + } + + if (cur < UCSI_MAX_ALTMODES) + altmode = typec_altmode_get_partner(con->port_altmode[cur]); + + for (i = 0; con->partner_altmode[i]; i++) + typec_altmode_update_active(con->partner_altmode[i], + con->partner_altmode[i] == altmode); +} + +static u8 ucsi_altmode_next_mode(struct typec_altmode **alt, u16 svid) +{ + u8 mode = 1; + int i; + + for (i = 0; alt[i]; i++) + if (alt[i]->svid == svid) + mode++; + + return mode; +} + +static int ucsi_next_altmode(struct typec_altmode **alt) +{ + int i = 0; + + for (i = 0; i < UCSI_MAX_ALTMODES; i++) + if (!alt[i]) + return i; + + return -ENOENT; +} + +static int ucsi_register_altmode(struct ucsi_connector *con, + struct typec_altmode_desc *desc, + u8 recipient) +{ + struct typec_altmode *alt; + bool override; + int ret; + int i; + + override = !!(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE); + + switch (recipient) { + case UCSI_RECIPIENT_CON: + i = ucsi_next_altmode(con->port_altmode); + if (i < 0) { + ret = i; + goto err; + } + + desc->mode = ucsi_altmode_next_mode(con->port_altmode, + desc->svid); + + switch (desc->svid) { + case USB_TYPEC_DP_SID: + case USB_TYPEC_NVIDIA_VLINK_SID: + alt = ucsi_register_displayport(con, override, i, desc); + break; + default: + alt = typec_port_register_altmode(con->port, desc); + break; + } + + if (IS_ERR(alt)) { + ret = PTR_ERR(alt); + goto err; + } + + con->port_altmode[i] = alt; + break; + case UCSI_RECIPIENT_SOP: + i = ucsi_next_altmode(con->partner_altmode); + if (i < 0) { + ret = i; + goto err; + } + + desc->mode = ucsi_altmode_next_mode(con->partner_altmode, + desc->svid); + + alt = typec_partner_register_altmode(con->partner, desc); + if (IS_ERR(alt)) { + ret = PTR_ERR(alt); + goto err; + } + + con->partner_altmode[i] = alt; + break; + default: + return -EINVAL; + } + + trace_ucsi_register_altmode(recipient, alt); + + return 0; + +err: + dev_err(con->ucsi->dev, "failed to registers svid 0x%04x mode %d\n", + desc->svid, desc->mode); + + return ret; +} + +static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient) +{ + int max_altmodes = UCSI_MAX_ALTMODES; + struct typec_altmode_desc desc; + struct ucsi_altmode alt[2]; + struct ucsi_control ctrl; + int num = 1; + int ret; + int len; + int j; + int i; + + if (!(con->ucsi->cap.features & UCSI_CAP_ALT_MODE_DETAILS)) + return 0; + + if (recipient == UCSI_RECIPIENT_SOP && con->partner_altmode[0]) + return 0; + + if (recipient == UCSI_RECIPIENT_CON) + max_altmodes = con->ucsi->cap.num_alt_modes; + + for (i = 0; i < max_altmodes;) { + memset(alt, 0, sizeof(alt)); + UCSI_CMD_GET_ALTERNATE_MODES(ctrl, recipient, con->num, i, 1); + len = ucsi_run_command(con->ucsi, &ctrl, alt, sizeof(alt)); + if (len <= 0) + return len; + + /* + * This code is requesting one alt mode at a time, but some PPMs + * may still return two. If that happens both alt modes need be + * registered and the offset for the next alt mode has to be + * incremented. + */ + num = len / sizeof(alt[0]); + i += num; + + for (j = 0; j < num; j++) { + if (!alt[j].svid) + return 0; + + memset(&desc, 0, sizeof(desc)); + desc.vdo = alt[j].mid; + desc.svid = alt[j].svid; + desc.roles = TYPEC_PORT_DRD; + + ret = ucsi_register_altmode(con, &desc, recipient); + if (ret) + return ret; + } + } + + return 0; +} + +static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient) +{ + const struct typec_altmode *pdev; + struct typec_altmode **adev; + int i = 0; + + switch (recipient) { + case UCSI_RECIPIENT_CON: + adev = con->port_altmode; + break; + case UCSI_RECIPIENT_SOP: + adev = con->partner_altmode; + break; + default: + return; + } + + while (adev[i]) { + if (recipient == UCSI_RECIPIENT_SOP && + (adev[i]->svid == USB_TYPEC_DP_SID || + adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID)) { + pdev = typec_altmode_get_partner(adev[i]); + ucsi_displayport_remove_partner((void *)pdev); + } + typec_unregister_altmode(adev[i]); + adev[i++] = NULL; + } +} + static void ucsi_pwr_opmode_change(struct ucsi_connector *con) { switch (con->status.pwr_op_mode) { @@ -299,10 +474,43 @@ static void ucsi_unregister_partner(struct ucsi_connector *con) if (!con->partner) return; + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_SOP); typec_unregister_partner(con->partner); con->partner = NULL; } +static void ucsi_partner_change(struct ucsi_connector *con) +{ + int ret; + + if (!con->partner) + return; + + switch (con->status.partner_type) { + case UCSI_CONSTAT_PARTNER_TYPE_UFP: + typec_set_data_role(con->port, TYPEC_HOST); + break; + case UCSI_CONSTAT_PARTNER_TYPE_DFP: + typec_set_data_role(con->port, TYPEC_DEVICE); + break; + default: + break; + } + + /* Complete pending data role swap */ + if (!completion_done(&con->complete)) + complete(&con->complete); + + /* Can't rely on Partner Flags field. Always checking the alt modes. */ + ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); + if (ret) + dev_err(con->ucsi->dev, + "con%d: failed to register partner alternate modes\n", + con->num); + else + ucsi_altmode_update_active(con); +} + static void ucsi_connector_change(struct work_struct *work) { struct ucsi_connector *con = container_of(work, struct ucsi_connector, @@ -311,10 +519,10 @@ static void ucsi_connector_change(struct work_struct *work) struct ucsi_control ctrl; int ret; - mutex_lock(&ucsi->ppm_lock); + mutex_lock(&con->lock); UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num); - ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status)); + ret = ucsi_send_command(ucsi, &ctrl, &con->status, sizeof(con->status)); if (ret < 0) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", __func__, ret); @@ -332,23 +540,6 @@ static void ucsi_connector_change(struct work_struct *work) complete(&con->complete); } - if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) { - switch (con->status.partner_type) { - case UCSI_CONSTAT_PARTNER_TYPE_UFP: - typec_set_data_role(con->port, TYPEC_HOST); - break; - case UCSI_CONSTAT_PARTNER_TYPE_DFP: - typec_set_data_role(con->port, TYPEC_DEVICE); - break; - default: - break; - } - - /* Complete pending data role swap */ - if (!completion_done(&con->complete)) - complete(&con->complete); - } - if (con->status.change & UCSI_CONSTAT_CONNECT_CHANGE) { typec_set_pwr_role(con->port, con->status.pwr_dir); @@ -369,6 +560,19 @@ static void ucsi_connector_change(struct work_struct *work) ucsi_unregister_partner(con); } + if (con->status.change & UCSI_CONSTAT_CAM_CHANGE) { + /* + * We don't need to know the currently supported alt modes here. + * Running GET_CAM_SUPPORTED command just to make sure the PPM + * does not get stuck in case it assumes we do so. + */ + UCSI_CMD_GET_CAM_SUPPORTED(ctrl, con->num); + ucsi_run_command(con->ucsi, &ctrl, NULL, 0); + } + + if (con->status.change & UCSI_CONSTAT_PARTNER_CHANGE) + ucsi_partner_change(con); + ret = ucsi_ack(ucsi, UCSI_ACK_EVENT); if (ret) dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); @@ -377,7 +581,7 @@ static void ucsi_connector_change(struct work_struct *work) out_unlock: clear_bit(EVENT_PENDING, &ucsi->flags); - mutex_unlock(&ucsi->ppm_lock); + mutex_unlock(&con->lock); } /** @@ -427,7 +631,7 @@ static int ucsi_reset_connector(struct ucsi_connector *con, bool hard) UCSI_CMD_CONNECTOR_RESET(ctrl, con, hard); - return ucsi_run_command(con->ucsi, &ctrl, NULL, 0); + return ucsi_send_command(con->ucsi, &ctrl, NULL, 0); } static int ucsi_reset_ppm(struct ucsi *ucsi) @@ -481,15 +685,17 @@ static int ucsi_role_cmd(struct ucsi_connector *con, struct ucsi_control *ctrl) { int ret; - ret = ucsi_run_command(con->ucsi, ctrl, NULL, 0); + ret = ucsi_send_command(con->ucsi, ctrl, NULL, 0); if (ret == -ETIMEDOUT) { struct ucsi_control c; /* PPM most likely stopped responding. Resetting everything. */ + mutex_lock(&con->ucsi->ppm_lock); ucsi_reset_ppm(con->ucsi); + mutex_unlock(&con->ucsi->ppm_lock); UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL); - ucsi_run_command(con->ucsi, &c, NULL, 0); + ucsi_send_command(con->ucsi, &c, NULL, 0); ucsi_reset_connector(con, true); } @@ -504,10 +710,12 @@ ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role) struct ucsi_control ctrl; int ret = 0; - if (!con->partner) - return -ENOTCONN; + mutex_lock(&con->lock); - mutex_lock(&con->ucsi->ppm_lock); + if (!con->partner) { + ret = -ENOTCONN; + goto out_unlock; + } if ((con->status.partner_type == UCSI_CONSTAT_PARTNER_TYPE_DFP && role == TYPEC_DEVICE) || @@ -520,18 +728,14 @@ ucsi_dr_swap(const struct typec_capability *cap, enum typec_data_role role) if (ret < 0) goto out_unlock; - mutex_unlock(&con->ucsi->ppm_lock); - if (!wait_for_completion_timeout(&con->complete, msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) - return -ETIMEDOUT; - - return 0; + ret = -ETIMEDOUT; out_unlock: - mutex_unlock(&con->ucsi->ppm_lock); + mutex_unlock(&con->lock); - return ret; + return ret < 0 ? ret : 0; } static int @@ -541,10 +745,12 @@ ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role) struct ucsi_control ctrl; int ret = 0; - if (!con->partner) - return -ENOTCONN; + mutex_lock(&con->lock); - mutex_lock(&con->ucsi->ppm_lock); + if (!con->partner) { + ret = -ENOTCONN; + goto out_unlock; + } if (con->status.pwr_dir == role) goto out_unlock; @@ -554,13 +760,11 @@ ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role) if (ret < 0) goto out_unlock; - mutex_unlock(&con->ucsi->ppm_lock); - if (!wait_for_completion_timeout(&con->complete, - msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) - return -ETIMEDOUT; - - mutex_lock(&con->ucsi->ppm_lock); + msecs_to_jiffies(UCSI_SWAP_TIMEOUT_MS))) { + ret = -ETIMEDOUT; + goto out_unlock; + } /* Something has gone wrong while swapping the role */ if (con->status.pwr_op_mode != UCSI_CONSTAT_PWR_OPMODE_PD) { @@ -569,7 +773,7 @@ ucsi_pr_swap(const struct typec_capability *cap, enum typec_role role) } out_unlock: - mutex_unlock(&con->ucsi->ppm_lock); + mutex_unlock(&con->lock); return ret; } @@ -595,6 +799,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) INIT_WORK(&con->work, ucsi_connector_change); init_completion(&con->complete); + mutex_init(&con->lock); con->num = index + 1; con->ucsi = ucsi; @@ -636,6 +841,12 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) if (IS_ERR(con->port)) return PTR_ERR(con->port); + /* Alternate modes */ + ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_CON); + if (ret) + dev_err(ucsi->dev, "con%d: failed to register alt modes\n", + con->num); + /* Get the status */ UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num); ret = ucsi_run_command(ucsi, &ctrl, &con->status, sizeof(con->status)); @@ -662,6 +873,16 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) if (con->status.connected) ucsi_register_partner(con); + if (con->partner) { + ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP); + if (ret) + dev_err(ucsi->dev, + "con%d: failed to register alternate modes\n", + con->num); + else + ucsi_altmode_update_active(con); + } + trace_ucsi_register_port(con->num, &con->status); return 0; @@ -730,6 +951,7 @@ static void ucsi_init(struct work_struct *work) err_unregister: for (con = ucsi->connector; con->port; con++) { ucsi_unregister_partner(con); + ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); typec_unregister_port(con->port); con->port = NULL; } @@ -788,17 +1010,15 @@ void ucsi_unregister_ppm(struct ucsi *ucsi) /* Make sure that we are not in the middle of driver initialization */ cancel_work_sync(&ucsi->work); - mutex_lock(&ucsi->ppm_lock); - /* Disable everything except command complete notification */ UCSI_CMD_SET_NTFY_ENABLE(ctrl, UCSI_ENABLE_NTFY_CMD_COMPLETE) - ucsi_run_command(ucsi, &ctrl, NULL, 0); - - mutex_unlock(&ucsi->ppm_lock); + ucsi_send_command(ucsi, &ctrl, NULL, 0); for (i = 0; i < ucsi->cap.num_connectors; i++) { cancel_work_sync(&ucsi->connector[i].work); ucsi_unregister_partner(&ucsi->connector[i]); + ucsi_unregister_altmodes(&ucsi->connector[i], + UCSI_RECIPIENT_CON); typec_unregister_port(ucsi->connector[i].port); } diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 53b80f40a908..1e2981aef629 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -6,6 +6,7 @@ #include <linux/bitops.h> #include <linux/device.h> #include <linux/types.h> +#include <linux/usb/typec.h> /* -------------------------------------------------------------------------- */ @@ -60,6 +61,20 @@ struct ucsi_uor_cmd { u16:6; /* reserved */ } __packed; +/* Get Alternate Modes Command structure */ +struct ucsi_altmode_cmd { + u8 cmd; + u8 length; + u8 recipient; +#define UCSI_RECIPIENT_CON 0 +#define UCSI_RECIPIENT_SOP 1 +#define UCSI_RECIPIENT_SOP_P 2 +#define UCSI_RECIPIENT_SOP_PP 3 + u8 con_num; + u8 offset; + u8 num_altmodes; +} __packed; + struct ucsi_control { union { u64 raw_cmd; @@ -67,6 +82,7 @@ struct ucsi_control { struct ucsi_uor_cmd uor; struct ucsi_ack_cmd ack; struct ucsi_con_rst con_rst; + struct ucsi_altmode_cmd alt; }; }; @@ -112,6 +128,30 @@ struct ucsi_control { (_ctrl_).cmd.data = _con_; \ } +/* Helper for preparing ucsi_control for GET_ALTERNATE_MODES command. */ +#define UCSI_CMD_GET_ALTERNATE_MODES(_ctrl_, _r_, _con_num_, _o_, _num_)\ +{ \ + __UCSI_CMD((_ctrl_), UCSI_GET_ALTERNATE_MODES) \ + _ctrl_.alt.recipient = (_r_); \ + _ctrl_.alt.con_num = (_con_num_); \ + _ctrl_.alt.offset = (_o_); \ + _ctrl_.alt.num_altmodes = (_num_) - 1; \ +} + +/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */ +#define UCSI_CMD_GET_CAM_SUPPORTED(_ctrl_, _con_) \ +{ \ + __UCSI_CMD((_ctrl_), UCSI_GET_CAM_SUPPORTED) \ + _ctrl_.cmd.data = (_con_); \ +} + +/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */ +#define UCSI_CMD_GET_CURRENT_CAM(_ctrl_, _con_) \ +{ \ + __UCSI_CMD((_ctrl_), UCSI_GET_CURRENT_CAM) \ + _ctrl_.cmd.data = (_con_); \ +} + /* Helper for preparing ucsi_control for GET_CONNECTOR_STATUS command. */ #define UCSI_CMD_GET_CONNECTOR_STATUS(_ctrl_, _con_) \ { \ @@ -334,4 +374,82 @@ struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm); void ucsi_unregister_ppm(struct ucsi *ucsi); void ucsi_notify(struct ucsi *ucsi); +/* -------------------------------------------------------------------------- */ + +enum ucsi_status { + UCSI_IDLE = 0, + UCSI_BUSY, + UCSI_ERROR, +}; + +struct ucsi { + struct device *dev; + struct ucsi_ppm *ppm; + + enum ucsi_status status; + struct completion complete; + struct ucsi_capability cap; + struct ucsi_connector *connector; + + struct work_struct work; + + /* PPM Communication lock */ + struct mutex ppm_lock; + + /* PPM communication flags */ + unsigned long flags; +#define EVENT_PENDING 0 +#define COMMAND_PENDING 1 +#define ACK_PENDING 2 +}; + +#define UCSI_MAX_SVID 5 +#define UCSI_MAX_ALTMODES (UCSI_MAX_SVID * 6) + +struct ucsi_connector { + int num; + + struct ucsi *ucsi; + struct mutex lock; /* port lock */ + struct work_struct work; + struct completion complete; + + struct typec_port *port; + struct typec_partner *partner; + + struct typec_altmode *port_altmode[UCSI_MAX_ALTMODES]; + struct typec_altmode *partner_altmode[UCSI_MAX_ALTMODES]; + + struct typec_capability typec_cap; + + struct ucsi_connector_status status; + struct ucsi_connector_capability cap; +}; + +int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl, + void *retval, size_t size); + +void ucsi_altmode_update_active(struct ucsi_connector *con); + +#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE) +struct typec_altmode * +ucsi_register_displayport(struct ucsi_connector *con, + bool override, int offset, + struct typec_altmode_desc *desc); + +void ucsi_displayport_remove_partner(struct typec_altmode *adev); + +#else +static inline struct typec_altmode * +ucsi_register_displayport(struct ucsi_connector *con, + bool override, int offset, + struct typec_altmode_desc *desc) +{ + return NULL; +} + +static inline void +ucsi_displayport_remove_partner(struct typec_altmode *adev) { } +#endif /* CONFIG_TYPEC_DP_ALTMODE */ + #endif /* __DRIVER_USB_TYPEC_UCSI_H */ diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index de8a43bdff68..9d46aa9e4e35 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -9,6 +9,7 @@ */ #include <linux/acpi.h> #include <linux/delay.h> +#include <linux/firmware.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/pci.h> @@ -17,18 +18,172 @@ #include <asm/unaligned.h> #include "ucsi.h" +enum enum_fw_mode { + BOOT, /* bootloader */ + FW1, /* FW partition-1 (contains secondary fw) */ + FW2, /* FW partition-2 (contains primary fw) */ + FW_INVALID, +}; + +#define CCGX_RAB_DEVICE_MODE 0x0000 +#define CCGX_RAB_INTR_REG 0x0006 +#define DEV_INT BIT(0) +#define PORT0_INT BIT(1) +#define PORT1_INT BIT(2) +#define UCSI_READ_INT BIT(7) +#define CCGX_RAB_JUMP_TO_BOOT 0x0007 +#define TO_BOOT 'J' +#define TO_ALT_FW 'A' +#define CCGX_RAB_RESET_REQ 0x0008 +#define RESET_SIG 'R' +#define CMD_RESET_I2C 0x0 +#define CMD_RESET_DEV 0x1 +#define CCGX_RAB_ENTER_FLASHING 0x000A +#define FLASH_ENTER_SIG 'P' +#define CCGX_RAB_VALIDATE_FW 0x000B +#define CCGX_RAB_FLASH_ROW_RW 0x000C +#define FLASH_SIG 'F' +#define FLASH_RD_CMD 0x0 +#define FLASH_WR_CMD 0x1 +#define FLASH_FWCT1_WR_CMD 0x2 +#define FLASH_FWCT2_WR_CMD 0x3 +#define FLASH_FWCT_SIG_WR_CMD 0x4 +#define CCGX_RAB_READ_ALL_VER 0x0010 +#define CCGX_RAB_READ_FW2_VER 0x0020 +#define CCGX_RAB_UCSI_CONTROL 0x0039 +#define CCGX_RAB_UCSI_CONTROL_START BIT(0) +#define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) +#define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) +#define REG_FLASH_RW_MEM 0x0200 +#define DEV_REG_IDX CCGX_RAB_DEVICE_MODE +#define CCGX_RAB_PDPORT_ENABLE 0x002C +#define PDPORT_1 BIT(0) +#define PDPORT_2 BIT(1) +#define CCGX_RAB_RESPONSE 0x007E +#define ASYNC_EVENT BIT(7) + +/* CCGx events & async msg codes */ +#define RESET_COMPLETE 0x80 +#define EVENT_INDEX RESET_COMPLETE +#define PORT_CONNECT_DET 0x84 +#define PORT_DISCONNECT_DET 0x85 +#define ROLE_SWAP_COMPELETE 0x87 + +/* ccg firmware */ +#define CYACD_LINE_SIZE 527 +#define CCG4_ROW_SIZE 256 +#define FW1_METADATA_ROW 0x1FF +#define FW2_METADATA_ROW 0x1FE +#define FW_CFG_TABLE_SIG_SIZE 256 + +static int secondary_fw_min_ver = 41; + +enum enum_flash_mode { + SECONDARY_BL, /* update secondary using bootloader */ + PRIMARY, /* update primary using secondary */ + SECONDARY, /* update secondary using primary */ + FLASH_NOT_NEEDED, /* update not required */ + FLASH_INVALID, +}; + +static const char * const ccg_fw_names[] = { + "ccg_boot.cyacd", + "ccg_primary.cyacd", + "ccg_secondary.cyacd" +}; + +struct ccg_dev_info { +#define CCG_DEVINFO_FWMODE_SHIFT (0) +#define CCG_DEVINFO_FWMODE_MASK (0x3 << CCG_DEVINFO_FWMODE_SHIFT) +#define CCG_DEVINFO_PDPORTS_SHIFT (2) +#define CCG_DEVINFO_PDPORTS_MASK (0x3 << CCG_DEVINFO_PDPORTS_SHIFT) + u8 mode; + u8 bl_mode; + __le16 silicon_id; + __le16 bl_last_row; +} __packed; + +struct version_format { + __le16 build; + u8 patch; + u8 ver; +#define CCG_VERSION_MIN_SHIFT (0) +#define CCG_VERSION_MIN_MASK (0xf << CCG_VERSION_MIN_SHIFT) +#define CCG_VERSION_MAJ_SHIFT (4) +#define CCG_VERSION_MAJ_MASK (0xf << CCG_VERSION_MAJ_SHIFT) +} __packed; + +struct version_info { + struct version_format base; + struct version_format app; +}; + +struct fw_config_table { + u32 identity; + u16 table_size; + u8 fwct_version; + u8 is_key_change; + u8 guid[16]; + struct version_format base; + struct version_format app; + u8 primary_fw_digest[32]; + u32 key_exp_length; + u8 key_modulus[256]; + u8 key_exp[4]; +}; + +/* CCGx response codes */ +enum ccg_resp_code { + CMD_NO_RESP = 0x00, + CMD_SUCCESS = 0x02, + FLASH_DATA_AVAILABLE = 0x03, + CMD_INVALID = 0x05, + FLASH_UPDATE_FAIL = 0x07, + INVALID_FW = 0x08, + INVALID_ARG = 0x09, + CMD_NOT_SUPPORT = 0x0A, + TRANSACTION_FAIL = 0x0C, + PD_CMD_FAIL = 0x0D, + UNDEF_ERROR = 0x0F, + INVALID_RESP = 0x10, +}; + +#define CCG_EVENT_MAX (EVENT_INDEX + 43) + +struct ccg_cmd { + u16 reg; + u32 data; + int len; + u32 delay; /* ms delay for cmd timeout */ +}; + +struct ccg_resp { + u8 code; + u8 length; +}; + struct ucsi_ccg { struct device *dev; struct ucsi *ucsi; struct ucsi_ppm ppm; struct i2c_client *client; -}; + struct ccg_dev_info info; + /* version info for boot, primary and secondary */ + struct version_info version[FW2 + 1]; + /* CCG HPI communication flags */ + unsigned long flags; +#define RESET_PENDING 0 +#define DEV_CMD_PENDING 1 + struct ccg_resp dev_resp; + u8 cmd_resp; + int port_num; + int irq; + struct work_struct work; + struct mutex lock; /* to sync between user and driver thread */ -#define CCGX_RAB_INTR_REG 0x06 -#define CCGX_RAB_UCSI_CONTROL 0x39 -#define CCGX_RAB_UCSI_CONTROL_START BIT(0) -#define CCGX_RAB_UCSI_CONTROL_STOP BIT(1) -#define CCGX_RAB_UCSI_DATA_BLOCK(offset) (0xf000 | ((offset) & 0xff)) + /* fw build with vendor information */ + u16 fw_build; +}; static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) { @@ -220,6 +375,687 @@ static irqreturn_t ccg_irq_handler(int irq, void *data) return IRQ_HANDLED; } +static int get_fw_info(struct ucsi_ccg *uc) +{ + int err; + + err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)(&uc->version), + sizeof(uc->version)); + if (err < 0) + return err; + + err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), + sizeof(uc->info)); + if (err < 0) + return err; + + return 0; +} + +static inline bool invalid_async_evt(int code) +{ + return (code >= CCG_EVENT_MAX) || (code < EVENT_INDEX); +} + +static void ccg_process_response(struct ucsi_ccg *uc) +{ + struct device *dev = uc->dev; + + if (uc->dev_resp.code & ASYNC_EVENT) { + if (uc->dev_resp.code == RESET_COMPLETE) { + if (test_bit(RESET_PENDING, &uc->flags)) + uc->cmd_resp = uc->dev_resp.code; + get_fw_info(uc); + } + if (invalid_async_evt(uc->dev_resp.code)) + dev_err(dev, "invalid async evt %d\n", + uc->dev_resp.code); + } else { + if (test_bit(DEV_CMD_PENDING, &uc->flags)) { + uc->cmd_resp = uc->dev_resp.code; + clear_bit(DEV_CMD_PENDING, &uc->flags); + } else { + dev_err(dev, "dev resp 0x%04x but no cmd pending\n", + uc->dev_resp.code); + } + } +} + +static int ccg_read_response(struct ucsi_ccg *uc) +{ + unsigned long target = jiffies + msecs_to_jiffies(1000); + struct device *dev = uc->dev; + u8 intval; + int status; + + /* wait for interrupt status to get updated */ + do { + status = ccg_read(uc, CCGX_RAB_INTR_REG, &intval, + sizeof(intval)); + if (status < 0) + return status; + + if (intval & DEV_INT) + break; + usleep_range(500, 600); + } while (time_is_after_jiffies(target)); + + if (time_is_before_jiffies(target)) { + dev_err(dev, "response timeout error\n"); + return -ETIME; + } + + status = ccg_read(uc, CCGX_RAB_RESPONSE, (u8 *)&uc->dev_resp, + sizeof(uc->dev_resp)); + if (status < 0) + return status; + + status = ccg_write(uc, CCGX_RAB_INTR_REG, &intval, sizeof(intval)); + if (status < 0) + return status; + + return 0; +} + +/* Caller must hold uc->lock */ +static int ccg_send_command(struct ucsi_ccg *uc, struct ccg_cmd *cmd) +{ + struct device *dev = uc->dev; + int ret; + + switch (cmd->reg & 0xF000) { + case DEV_REG_IDX: + set_bit(DEV_CMD_PENDING, &uc->flags); + break; + default: + dev_err(dev, "invalid cmd register\n"); + break; + } + + ret = ccg_write(uc, cmd->reg, (u8 *)&cmd->data, cmd->len); + if (ret < 0) + return ret; + + msleep(cmd->delay); + + ret = ccg_read_response(uc); + if (ret < 0) { + dev_err(dev, "response read error\n"); + switch (cmd->reg & 0xF000) { + case DEV_REG_IDX: + clear_bit(DEV_CMD_PENDING, &uc->flags); + break; + default: + dev_err(dev, "invalid cmd register\n"); + break; + } + return -EIO; + } + ccg_process_response(uc); + + return uc->cmd_resp; +} + +static int ccg_cmd_enter_flashing(struct ucsi_ccg *uc) +{ + struct ccg_cmd cmd; + int ret; + + cmd.reg = CCGX_RAB_ENTER_FLASHING; + cmd.data = FLASH_ENTER_SIG; + cmd.len = 1; + cmd.delay = 50; + + mutex_lock(&uc->lock); + + ret = ccg_send_command(uc, &cmd); + + mutex_unlock(&uc->lock); + + if (ret != CMD_SUCCESS) { + dev_err(uc->dev, "enter flashing failed ret=%d\n", ret); + return ret; + } + + return 0; +} + +static int ccg_cmd_reset(struct ucsi_ccg *uc) +{ + struct ccg_cmd cmd; + u8 *p; + int ret; + + p = (u8 *)&cmd.data; + cmd.reg = CCGX_RAB_RESET_REQ; + p[0] = RESET_SIG; + p[1] = CMD_RESET_DEV; + cmd.len = 2; + cmd.delay = 5000; + + mutex_lock(&uc->lock); + + set_bit(RESET_PENDING, &uc->flags); + + ret = ccg_send_command(uc, &cmd); + if (ret != RESET_COMPLETE) + goto err_clear_flag; + + ret = 0; + +err_clear_flag: + clear_bit(RESET_PENDING, &uc->flags); + + mutex_unlock(&uc->lock); + + return ret; +} + +static int ccg_cmd_port_control(struct ucsi_ccg *uc, bool enable) +{ + struct ccg_cmd cmd; + int ret; + + cmd.reg = CCGX_RAB_PDPORT_ENABLE; + if (enable) + cmd.data = (uc->port_num == 1) ? + PDPORT_1 : (PDPORT_1 | PDPORT_2); + else + cmd.data = 0x0; + cmd.len = 1; + cmd.delay = 10; + + mutex_lock(&uc->lock); + + ret = ccg_send_command(uc, &cmd); + + mutex_unlock(&uc->lock); + + if (ret != CMD_SUCCESS) { + dev_err(uc->dev, "port control failed ret=%d\n", ret); + return ret; + } + return 0; +} + +static int ccg_cmd_jump_boot_mode(struct ucsi_ccg *uc, int bl_mode) +{ + struct ccg_cmd cmd; + int ret; + + cmd.reg = CCGX_RAB_JUMP_TO_BOOT; + + if (bl_mode) + cmd.data = TO_BOOT; + else + cmd.data = TO_ALT_FW; + + cmd.len = 1; + cmd.delay = 100; + + mutex_lock(&uc->lock); + + set_bit(RESET_PENDING, &uc->flags); + + ret = ccg_send_command(uc, &cmd); + if (ret != RESET_COMPLETE) + goto err_clear_flag; + + ret = 0; + +err_clear_flag: + clear_bit(RESET_PENDING, &uc->flags); + + mutex_unlock(&uc->lock); + + return ret; +} + +static int +ccg_cmd_write_flash_row(struct ucsi_ccg *uc, u16 row, + const void *data, u8 fcmd) +{ + struct i2c_client *client = uc->client; + struct ccg_cmd cmd; + u8 buf[CCG4_ROW_SIZE + 2]; + u8 *p; + int ret; + + /* Copy the data into the flash read/write memory. */ + put_unaligned_le16(REG_FLASH_RW_MEM, buf); + + memcpy(buf + 2, data, CCG4_ROW_SIZE); + + mutex_lock(&uc->lock); + + ret = i2c_master_send(client, buf, CCG4_ROW_SIZE + 2); + if (ret != CCG4_ROW_SIZE + 2) { + dev_err(uc->dev, "REG_FLASH_RW_MEM write fail %d\n", ret); + mutex_unlock(&uc->lock); + return ret < 0 ? ret : -EIO; + } + + /* Use the FLASH_ROW_READ_WRITE register to trigger */ + /* writing of data to the desired flash row */ + p = (u8 *)&cmd.data; + cmd.reg = CCGX_RAB_FLASH_ROW_RW; + p[0] = FLASH_SIG; + p[1] = fcmd; + put_unaligned_le16(row, &p[2]); + cmd.len = 4; + cmd.delay = 50; + if (fcmd == FLASH_FWCT_SIG_WR_CMD) + cmd.delay += 400; + if (row == 510) + cmd.delay += 220; + ret = ccg_send_command(uc, &cmd); + + mutex_unlock(&uc->lock); + + if (ret != CMD_SUCCESS) { + dev_err(uc->dev, "write flash row failed ret=%d\n", ret); + return ret; + } + + return 0; +} + +static int ccg_cmd_validate_fw(struct ucsi_ccg *uc, unsigned int fwid) +{ + struct ccg_cmd cmd; + int ret; + + cmd.reg = CCGX_RAB_VALIDATE_FW; + cmd.data = fwid; + cmd.len = 1; + cmd.delay = 500; + + mutex_lock(&uc->lock); + + ret = ccg_send_command(uc, &cmd); + + mutex_unlock(&uc->lock); + + if (ret != CMD_SUCCESS) + return ret; + + return 0; +} + +static bool ccg_check_vendor_version(struct ucsi_ccg *uc, + struct version_format *app, + struct fw_config_table *fw_cfg) +{ + struct device *dev = uc->dev; + + /* Check if the fw build is for supported vendors */ + if (le16_to_cpu(app->build) != uc->fw_build) { + dev_info(dev, "current fw is not from supported vendor\n"); + return false; + } + + /* Check if the new fw build is for supported vendors */ + if (le16_to_cpu(fw_cfg->app.build) != uc->fw_build) { + dev_info(dev, "new fw is not from supported vendor\n"); + return false; + } + return true; +} + +static bool ccg_check_fw_version(struct ucsi_ccg *uc, const char *fw_name, + struct version_format *app) +{ + const struct firmware *fw = NULL; + struct device *dev = uc->dev; + struct fw_config_table fw_cfg; + u32 cur_version, new_version; + bool is_later = false; + + if (request_firmware(&fw, fw_name, dev) != 0) { + dev_err(dev, "error: Failed to open cyacd file %s\n", fw_name); + return false; + } + + /* + * check if signed fw + * last part of fw image is fw cfg table and signature + */ + if (fw->size < sizeof(fw_cfg) + FW_CFG_TABLE_SIG_SIZE) + goto out_release_firmware; + + memcpy((uint8_t *)&fw_cfg, fw->data + fw->size - + sizeof(fw_cfg) - FW_CFG_TABLE_SIG_SIZE, sizeof(fw_cfg)); + + if (fw_cfg.identity != ('F' | 'W' << 8 | 'C' << 16 | 'T' << 24)) { + dev_info(dev, "not a signed image\n"); + goto out_release_firmware; + } + + /* compare input version with FWCT version */ + cur_version = le16_to_cpu(app->build) | app->patch << 16 | + app->ver << 24; + + new_version = le16_to_cpu(fw_cfg.app.build) | fw_cfg.app.patch << 16 | + fw_cfg.app.ver << 24; + + if (!ccg_check_vendor_version(uc, app, &fw_cfg)) + goto out_release_firmware; + + if (new_version > cur_version) + is_later = true; + +out_release_firmware: + release_firmware(fw); + return is_later; +} + +static int ccg_fw_update_needed(struct ucsi_ccg *uc, + enum enum_flash_mode *mode) +{ + struct device *dev = uc->dev; + int err; + struct version_info version[3]; + + err = ccg_read(uc, CCGX_RAB_DEVICE_MODE, (u8 *)(&uc->info), + sizeof(uc->info)); + if (err) { + dev_err(dev, "read device mode failed\n"); + return err; + } + + err = ccg_read(uc, CCGX_RAB_READ_ALL_VER, (u8 *)version, + sizeof(version)); + if (err) { + dev_err(dev, "read device mode failed\n"); + return err; + } + + if (memcmp(&version[FW1], "\0\0\0\0\0\0\0\0", + sizeof(struct version_info)) == 0) { + dev_info(dev, "secondary fw is not flashed\n"); + *mode = SECONDARY_BL; + } else if (le16_to_cpu(version[FW1].base.build) < + secondary_fw_min_ver) { + dev_info(dev, "secondary fw version is too low (< %d)\n", + secondary_fw_min_ver); + *mode = SECONDARY; + } else if (memcmp(&version[FW2], "\0\0\0\0\0\0\0\0", + sizeof(struct version_info)) == 0) { + dev_info(dev, "primary fw is not flashed\n"); + *mode = PRIMARY; + } else if (ccg_check_fw_version(uc, ccg_fw_names[PRIMARY], + &version[FW2].app)) { + dev_info(dev, "found primary fw with later version\n"); + *mode = PRIMARY; + } else { + dev_info(dev, "secondary and primary fw are the latest\n"); + *mode = FLASH_NOT_NEEDED; + } + return 0; +} + +static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode) +{ + struct device *dev = uc->dev; + const struct firmware *fw = NULL; + const char *p, *s; + const char *eof; + int err, row, len, line_sz, line_cnt = 0; + unsigned long start_time = jiffies; + struct fw_config_table fw_cfg; + u8 fw_cfg_sig[FW_CFG_TABLE_SIG_SIZE]; + u8 *wr_buf; + + err = request_firmware(&fw, ccg_fw_names[mode], dev); + if (err) { + dev_err(dev, "request %s failed err=%d\n", + ccg_fw_names[mode], err); + return err; + } + + if (((uc->info.mode & CCG_DEVINFO_FWMODE_MASK) >> + CCG_DEVINFO_FWMODE_SHIFT) == FW2) { + err = ccg_cmd_port_control(uc, false); + if (err < 0) + goto release_fw; + err = ccg_cmd_jump_boot_mode(uc, 0); + if (err < 0) + goto release_fw; + } + + eof = fw->data + fw->size; + + /* + * check if signed fw + * last part of fw image is fw cfg table and signature + */ + if (fw->size < sizeof(fw_cfg) + sizeof(fw_cfg_sig)) + goto not_signed_fw; + + memcpy((uint8_t *)&fw_cfg, fw->data + fw->size - + sizeof(fw_cfg) - sizeof(fw_cfg_sig), sizeof(fw_cfg)); + + if (fw_cfg.identity != ('F' | ('W' << 8) | ('C' << 16) | ('T' << 24))) { + dev_info(dev, "not a signed image\n"); + goto not_signed_fw; + } + eof = fw->data + fw->size - sizeof(fw_cfg) - sizeof(fw_cfg_sig); + + memcpy((uint8_t *)&fw_cfg_sig, + fw->data + fw->size - sizeof(fw_cfg_sig), sizeof(fw_cfg_sig)); + + /* flash fw config table and signature first */ + err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg, + FLASH_FWCT1_WR_CMD); + if (err) + goto release_fw; + + err = ccg_cmd_write_flash_row(uc, 0, (u8 *)&fw_cfg + CCG4_ROW_SIZE, + FLASH_FWCT2_WR_CMD); + if (err) + goto release_fw; + + err = ccg_cmd_write_flash_row(uc, 0, &fw_cfg_sig, + FLASH_FWCT_SIG_WR_CMD); + if (err) + goto release_fw; + +not_signed_fw: + wr_buf = kzalloc(CCG4_ROW_SIZE + 4, GFP_KERNEL); + if (!wr_buf) + return -ENOMEM; + + err = ccg_cmd_enter_flashing(uc); + if (err) + goto release_mem; + + /***************************************************************** + * CCG firmware image (.cyacd) file line format + * + * :00rrrrllll[dd....]cc/r/n + * + * :00 header + * rrrr is row number to flash (4 char) + * llll is data len to flash (4 char) + * dd is a data field represents one byte of data (512 char) + * cc is checksum (2 char) + * \r\n newline + * + * Total length: 3 + 4 + 4 + 512 + 2 + 2 = 527 + * + *****************************************************************/ + + p = strnchr(fw->data, fw->size, ':'); + while (p < eof) { + s = strnchr(p + 1, eof - p - 1, ':'); + + if (!s) + s = eof; + + line_sz = s - p; + + if (line_sz != CYACD_LINE_SIZE) { + dev_err(dev, "Bad FW format line_sz=%d\n", line_sz); + err = -EINVAL; + goto release_mem; + } + + if (hex2bin(wr_buf, p + 3, CCG4_ROW_SIZE + 4)) { + err = -EINVAL; + goto release_mem; + } + + row = get_unaligned_be16(wr_buf); + len = get_unaligned_be16(&wr_buf[2]); + + if (len != CCG4_ROW_SIZE) { + err = -EINVAL; + goto release_mem; + } + + err = ccg_cmd_write_flash_row(uc, row, wr_buf + 4, + FLASH_WR_CMD); + if (err) + goto release_mem; + + line_cnt++; + p = s; + } + + dev_info(dev, "total %d row flashed. time: %dms\n", + line_cnt, jiffies_to_msecs(jiffies - start_time)); + + err = ccg_cmd_validate_fw(uc, (mode == PRIMARY) ? FW2 : FW1); + if (err) + dev_err(dev, "%s validation failed err=%d\n", + (mode == PRIMARY) ? "FW2" : "FW1", err); + else + dev_info(dev, "%s validated\n", + (mode == PRIMARY) ? "FW2" : "FW1"); + + err = ccg_cmd_port_control(uc, false); + if (err < 0) + goto release_mem; + + err = ccg_cmd_reset(uc); + if (err < 0) + goto release_mem; + + err = ccg_cmd_port_control(uc, true); + if (err < 0) + goto release_mem; + +release_mem: + kfree(wr_buf); + +release_fw: + release_firmware(fw); + return err; +} + +/******************************************************************************* + * CCG4 has two copies of the firmware in addition to the bootloader. + * If the device is running FW1, FW2 can be updated with the new version. + * Dual firmware mode allows the CCG device to stay in a PD contract and support + * USB PD and Type-C functionality while a firmware update is in progress. + ******************************************************************************/ +static int ccg_fw_update(struct ucsi_ccg *uc, enum enum_flash_mode flash_mode) +{ + int err; + + while (flash_mode != FLASH_NOT_NEEDED) { + err = do_flash(uc, flash_mode); + if (err < 0) + return err; + err = ccg_fw_update_needed(uc, &flash_mode); + if (err < 0) + return err; + } + dev_info(uc->dev, "CCG FW update successful\n"); + + return err; +} + +static int ccg_restart(struct ucsi_ccg *uc) +{ + struct device *dev = uc->dev; + int status; + + status = ucsi_ccg_init(uc); + if (status < 0) { + dev_err(dev, "ucsi_ccg_start fail, err=%d\n", status); + return status; + } + + status = request_threaded_irq(uc->irq, NULL, ccg_irq_handler, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + dev_name(dev), uc); + if (status < 0) { + dev_err(dev, "request_threaded_irq failed - %d\n", status); + return status; + } + + uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); + if (IS_ERR(uc->ucsi)) { + dev_err(uc->dev, "ucsi_register_ppm failed\n"); + return PTR_ERR(uc->ucsi); + } + + return 0; +} + +static void ccg_update_firmware(struct work_struct *work) +{ + struct ucsi_ccg *uc = container_of(work, struct ucsi_ccg, work); + enum enum_flash_mode flash_mode; + int status; + + status = ccg_fw_update_needed(uc, &flash_mode); + if (status < 0) + return; + + if (flash_mode != FLASH_NOT_NEEDED) { + ucsi_unregister_ppm(uc->ucsi); + free_irq(uc->irq, uc); + + ccg_fw_update(uc, flash_mode); + ccg_restart(uc); + } +} + +static ssize_t do_flash_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev)); + bool flash; + + if (kstrtobool(buf, &flash)) + return -EINVAL; + + 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 DEVICE_ATTR_WO(do_flash); + +static struct attribute *ucsi_ccg_sysfs_attrs[] = { + &dev_attr_do_flash.attr, + NULL, +}; + +static struct attribute_group ucsi_ccg_attr_group = { + .attrs = ucsi_ccg_sysfs_attrs, +}; + static int ucsi_ccg_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -240,6 +1076,14 @@ static int ucsi_ccg_probe(struct i2c_client *client, uc->ppm.sync = ucsi_ccg_sync; uc->dev = dev; uc->client = client; + mutex_init(&uc->lock); + INIT_WORK(&uc->work, ccg_update_firmware); + + /* Only fail FW flashing when FW build information is not provided */ + status = device_property_read_u16(dev, "ccgx,firmware-build", + &uc->fw_build); + if (status) + dev_err(uc->dev, "failed to get FW build information\n"); /* reset ccg device and initialize ucsi */ status = ucsi_ccg_init(uc); @@ -248,15 +1092,27 @@ static int ucsi_ccg_probe(struct i2c_client *client, return status; } - status = devm_request_threaded_irq(dev, client->irq, NULL, - ccg_irq_handler, - IRQF_ONESHOT | IRQF_TRIGGER_HIGH, - dev_name(dev), uc); + status = get_fw_info(uc); + if (status < 0) { + dev_err(uc->dev, "get_fw_info failed - %d\n", status); + return status; + } + + uc->port_num = 1; + + if (uc->info.mode & CCG_DEVINFO_PDPORTS_MASK) + uc->port_num++; + + status = request_threaded_irq(client->irq, NULL, ccg_irq_handler, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + dev_name(dev), uc); if (status < 0) { dev_err(uc->dev, "request_threaded_irq failed - %d\n", status); return status; } + uc->irq = client->irq; + uc->ucsi = ucsi_register_ppm(dev, &uc->ppm); if (IS_ERR(uc->ucsi)) { dev_err(uc->dev, "ucsi_register_ppm failed\n"); @@ -273,6 +1129,11 @@ static int ucsi_ccg_probe(struct i2c_client *client, } i2c_set_clientdata(client, uc); + + status = sysfs_create_group(&uc->dev->kobj, &ucsi_ccg_attr_group); + if (status) + dev_err(uc->dev, "cannot create sysfs group: %d\n", status); + return 0; } @@ -280,7 +1141,10 @@ static int ucsi_ccg_remove(struct i2c_client *client) { struct ucsi_ccg *uc = i2c_get_clientdata(client); + cancel_work_sync(&uc->work); ucsi_unregister_ppm(uc->ucsi); + free_irq(uc->irq, uc); + sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group); return 0; } diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index dbfb2f24d71e..b0a855acafa3 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -17,9 +17,9 @@ static int is_clear_halt_cmd(struct urb *urb) req = (struct usb_ctrlrequest *) urb->setup_packet; - return (req->bRequest == USB_REQ_CLEAR_FEATURE) && - (req->bRequestType == USB_RECIP_ENDPOINT) && - (req->wValue == USB_ENDPOINT_HALT); + return (req->bRequest == USB_REQ_CLEAR_FEATURE) && + (req->bRequestType == USB_RECIP_ENDPOINT) && + (req->wValue == USB_ENDPOINT_HALT); } static int is_set_interface_cmd(struct urb *urb) diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index f46ee1fefe02..000ab7225717 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -508,6 +508,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_U1_TIMEOUT: usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_U1_TIMEOUT\n"); + /* Fall through */ case USB_PORT_FEAT_U2_TIMEOUT: usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n"); @@ -654,15 +655,9 @@ error: static void vhci_tx_urb(struct urb *urb, struct vhci_device *vdev) { struct vhci_priv *priv; - struct vhci_hcd *vhci_hcd; + struct vhci_hcd *vhci_hcd = vdev_to_vhci_hcd(vdev); unsigned long flags; - if (!vdev) { - pr_err("could not get virtual device"); - return; - } - vhci_hcd = vdev_to_vhci_hcd(vdev); - priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC); if (!priv) { usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); |