summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/atm/cxacru.c2
-rw-r--r--drivers/usb/atm/speedtch.c8
-rw-r--r--drivers/usb/atm/usbatm.c4
-rw-r--r--drivers/usb/cdns3/cdns3-gadget.c6
-rw-r--r--drivers/usb/cdns3/cdns3-ti.c107
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.c67
-rw-r--r--drivers/usb/cdns3/cdnsp-gadget.h10
-rw-r--r--drivers/usb/cdns3/cdnsp-pci.c12
-rw-r--r--drivers/usb/cdns3/cdnsp-ring.c3
-rw-r--r--drivers/usb/cdns3/core.c9
-rw-r--r--drivers/usb/cdns3/core.h5
-rw-r--r--drivers/usb/cdns3/host.c11
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c44
-rw-r--r--drivers/usb/chipidea/host.c13
-rw-r--r--drivers/usb/chipidea/otg_fsm.c3
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c10
-rw-r--r--drivers/usb/class/cdc-wdm.c21
-rw-r--r--drivers/usb/class/usblp.c2
-rw-r--r--drivers/usb/class/usbtmc.c80
-rw-r--r--drivers/usb/common/common.c14
-rw-r--r--drivers/usb/common/usb-conn-gpio.c5
-rw-r--r--drivers/usb/core/config.c70
-rw-r--r--drivers/usb/core/driver.c7
-rw-r--r--drivers/usb/core/generic.c12
-rw-r--r--drivers/usb/core/hcd-pci.c15
-rw-r--r--drivers/usb/core/hcd.c12
-rw-r--r--drivers/usb/core/hub.c32
-rw-r--r--drivers/usb/core/port.c3
-rw-r--r--drivers/usb/core/quirks.c15
-rw-r--r--drivers/usb/core/sysfs.c12
-rw-r--r--drivers/usb/core/urb.c2
-rw-r--r--drivers/usb/core/usb-acpi.c5
-rw-r--r--drivers/usb/dwc2/core.c1
-rw-r--r--drivers/usb/dwc2/core.h23
-rw-r--r--drivers/usb/dwc2/gadget.c116
-rw-r--r--drivers/usb/dwc2/hcd.c101
-rw-r--r--drivers/usb/dwc2/hcd_queue.c7
-rw-r--r--drivers/usb/dwc2/platform.c38
-rw-r--r--drivers/usb/dwc3/core.c20
-rw-r--r--drivers/usb/dwc3/core.h10
-rw-r--r--drivers/usb/dwc3/dwc3-am62.c94
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c9
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c13
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c10
-rw-r--r--drivers/usb/dwc3/dwc3-st.c8
-rw-r--r--drivers/usb/dwc3/dwc3-xilinx.c4
-rw-r--r--drivers/usb/dwc3/gadget.c246
-rw-r--r--drivers/usb/fotg210/fotg210-core.c5
-rw-r--r--drivers/usb/fotg210/fotg210-hcd.c3
-rw-r--r--drivers/usb/gadget/composite.c12
-rw-r--r--drivers/usb/gadget/function/f_ecm.c11
-rw-r--r--drivers/usb/gadget/function/f_hid.c12
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c4
-rw-r--r--drivers/usb/gadget/function/f_midi2.c2
-rw-r--r--drivers/usb/gadget/function/f_ncm.c6
-rw-r--r--drivers/usb/gadget/function/f_tcm.c653
-rw-r--r--drivers/usb/gadget/function/storage_common.h2
-rw-r--r--drivers/usb/gadget/function/tcm.h28
-rw-r--r--drivers/usb/gadget/function/u_serial.c3
-rw-r--r--drivers/usb/gadget/function/uvc_queue.c2
-rw-r--r--drivers/usb/gadget/function/uvc_video.c2
-rw-r--r--drivers/usb/gadget/legacy/inode.c3
-rw-r--r--drivers/usb/gadget/legacy/zero.c4
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/dev.c3
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/hub.c3
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c3
-rw-r--r--drivers/usb/gadget/udc/cdns2/cdns2-gadget.c13
-rw-r--r--drivers/usb/gadget/udc/core.c2
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c9
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c3
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c5
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c8
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c3
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c2
-rw-r--r--drivers/usb/gadget/udc/snps_udc_core.c4
-rw-r--r--drivers/usb/gadget/udc/tegra-xudc.c4
-rw-r--r--drivers/usb/host/ehci-hcd.c3
-rw-r--r--drivers/usb/host/ehci-platform.c2
-rw-r--r--drivers/usb/host/isp1362-hcd.c2
-rw-r--r--drivers/usb/host/max3421-hcd.c7
-rw-r--r--drivers/usb/host/ohci-hcd.c2
-rw-r--r--drivers/usb/host/ohci-hub.c2
-rw-r--r--drivers/usb/host/ohci-pci.c23
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c9
-rw-r--r--drivers/usb/host/r8a66597-hcd.c2
-rw-r--r--drivers/usb/host/sl811-hcd.c5
-rw-r--r--drivers/usb/host/uhci-hcd.c2
-rw-r--r--drivers/usb/host/uhci-platform.c2
-rw-r--r--drivers/usb/host/uhci-q.c2
-rw-r--r--drivers/usb/host/xen-hcd.c4
-rw-r--r--drivers/usb/host/xhci-caps.h6
-rw-r--r--drivers/usb/host/xhci-dbgcap.c21
-rw-r--r--drivers/usb/host/xhci-dbgcap.h3
-rw-r--r--drivers/usb/host/xhci-dbgtty.c98
-rw-r--r--drivers/usb/host/xhci-debugfs.c25
-rw-r--r--drivers/usb/host/xhci-histb.c2
-rw-r--r--drivers/usb/host/xhci-hub.c32
-rw-r--r--drivers/usb/host/xhci-mem.c34
-rw-r--r--drivers/usb/host/xhci-mtk.c4
-rw-r--r--drivers/usb/host/xhci-mvebu.c10
-rw-r--r--drivers/usb/host/xhci-mvebu.h6
-rw-r--r--drivers/usb/host/xhci-pci.c8
-rw-r--r--drivers/usb/host/xhci-plat.c16
-rw-r--r--drivers/usb/host/xhci-plat.h1
-rw-r--r--drivers/usb/host/xhci-ring.c449
-rw-r--r--drivers/usb/host/xhci-tegra.c20
-rw-r--r--drivers/usb/host/xhci.c48
-rw-r--r--drivers/usb/host/xhci.h33
-rw-r--r--drivers/usb/image/microtek.c4
-rw-r--r--drivers/usb/isp1760/isp1760-hcd.c2
-rw-r--r--drivers/usb/isp1760/isp1760-udc.c4
-rw-r--r--drivers/usb/misc/onboard_usb_dev.c117
-rw-r--r--drivers/usb/misc/onboard_usb_dev.h9
-rw-r--r--drivers/usb/misc/usb251xb.c6
-rw-r--r--drivers/usb/misc/usbtest.c2
-rw-r--r--drivers/usb/mtu3/mtu3_debugfs.c43
-rw-r--r--drivers/usb/mtu3/mtu3_dr.c3
-rw-r--r--drivers/usb/mtu3/mtu3_gadget.c3
-rw-r--r--drivers/usb/musb/da8xx.c9
-rw-r--r--drivers/usb/musb/jz4740.c4
-rw-r--r--drivers/usb/musb/mediatek.c2
-rw-r--r--drivers/usb/musb/mpfs.c6
-rw-r--r--drivers/usb/musb/musb_core.c21
-rw-r--r--drivers/usb/musb/musb_cppi41.c4
-rw-r--r--drivers/usb/musb/musb_dsps.c11
-rw-r--r--drivers/usb/musb/musb_gadget.c3
-rw-r--r--drivers/usb/musb/musb_host.c3
-rw-r--r--drivers/usb/musb/sunxi.c4
-rw-r--r--drivers/usb/musb/tusb6010.c8
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c3
-rw-r--r--drivers/usb/phy/phy-generic.c2
-rw-r--r--drivers/usb/phy/phy-mv-usb.c5
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c8
-rw-r--r--drivers/usb/phy/phy-tahvo.c3
-rw-r--r--drivers/usb/phy/phy-ulpi.c23
-rw-r--r--drivers/usb/phy/phy.c26
-rw-r--r--drivers/usb/renesas_usbhs/common.c50
-rw-r--r--drivers/usb/serial/ch341.c35
-rw-r--r--drivers/usb/serial/ftdi_sio.c16
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h18
-rw-r--r--drivers/usb/serial/mos7840.c13
-rw-r--r--drivers/usb/serial/option.c51
-rw-r--r--drivers/usb/serial/pl2303.c2
-rw-r--r--drivers/usb/serial/usb-serial-simple.c7
-rw-r--r--drivers/usb/storage/Kconfig3
-rw-r--r--drivers/usb/storage/alauda.c8
-rw-r--r--drivers/usb/storage/datafab.c14
-rw-r--r--drivers/usb/storage/debug.c4
-rw-r--r--drivers/usb/storage/initializers.c2
-rw-r--r--drivers/usb/storage/jumpshot.c10
-rw-r--r--drivers/usb/storage/realtek_cr.c8
-rw-r--r--drivers/usb/storage/scsiglue.c15
-rw-r--r--drivers/usb/storage/sddr09.c14
-rw-r--r--drivers/usb/storage/sddr55.c4
-rw-r--r--drivers/usb/storage/shuttle_usbat.c6
-rw-r--r--drivers/usb/storage/transport.c10
-rw-r--r--drivers/usb/storage/uas.c10
-rw-r--r--drivers/usb/storage/unusual_uas.h14
-rw-r--r--drivers/usb/typec/altmodes/Kconfig9
-rw-r--r--drivers/usb/typec/altmodes/Makefile2
-rw-r--r--drivers/usb/typec/altmodes/displayport.c4
-rw-r--r--drivers/usb/typec/altmodes/nvidia.c2
-rw-r--r--drivers/usb/typec/altmodes/thunderbolt.c388
-rw-r--r--drivers/usb/typec/bus.c8
-rw-r--r--drivers/usb/typec/class.c71
-rw-r--r--drivers/usb/typec/class.h1
-rw-r--r--drivers/usb/typec/hd3ss3220.c207
-rw-r--r--drivers/usb/typec/mux/Kconfig10
-rw-r--r--drivers/usb/typec/mux/Makefile1
-rw-r--r--drivers/usb/typec/mux/intel_pmc_mux.c2
-rw-r--r--drivers/usb/typec/mux/ps883x.c466
-rw-r--r--drivers/usb/typec/tcpm/fusb302.c24
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c3
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy_stub.c3
-rw-r--r--drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c4
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c4
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim_core.c3
-rw-r--r--drivers/usb/typec/tcpm/tcpci_mt6370.c1
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c213
-rw-r--r--drivers/usb/typec/ucsi/Kconfig13
-rw-r--r--drivers/usb/typec/ucsi/Makefile1
-rw-r--r--drivers/usb/typec/ucsi/cros_ec_ucsi.c341
-rw-r--r--drivers/usb/typec/ucsi/debugfs.c6
-rw-r--r--drivers/usb/typec/ucsi/displayport.c21
-rw-r--r--drivers/usb/typec/ucsi/trace.c2
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c53
-rw-r--r--drivers/usb/typec/ucsi/ucsi.h15
-rw-r--r--drivers/usb/typec/ucsi/ucsi_acpi.c29
-rw-r--r--drivers/usb/typec/ucsi/ucsi_ccg.c97
-rw-r--r--drivers/usb/typec/ucsi/ucsi_yoga_c630.c2
-rw-r--r--drivers/usb/usbip/stub_rx.c2
-rw-r--r--drivers/usb/usbip/stub_tx.c2
-rw-r--r--drivers/usb/usbip/vhci_hcd.c13
-rw-r--r--drivers/usb/usbip/vhci_rx.c6
-rw-r--r--drivers/usb/usbip/vudc_sysfs.c8
-rw-r--r--drivers/usb/usbip/vudc_tx.c2
196 files changed, 4159 insertions, 1550 deletions
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 47d06af33747..c6b9ad12e8fe 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -597,7 +597,7 @@ static int cxacru_start_wait_urb(struct urb *urb, struct completion *done,
timer_setup_on_stack(&timer.timer, cxacru_timeout_kill, 0);
mod_timer(&timer.timer, jiffies + msecs_to_jiffies(CMD_TIMEOUT));
wait_for_completion(done);
- del_timer_sync(&timer.timer);
+ timer_delete_sync(&timer.timer);
destroy_timer_on_stack(&timer.timer);
if (actual_length)
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
index 973548b5c15c..27e3d35ee7dd 100644
--- a/drivers/usb/atm/speedtch.c
+++ b/drivers/usb/atm/speedtch.c
@@ -612,7 +612,7 @@ static void speedtch_handle_int(struct urb *int_urb)
}
if ((count == 6) && !memcmp(up_int, instance->int_data, 6)) {
- del_timer(&instance->status_check_timer);
+ timer_delete(&instance->status_check_timer);
atm_info(usbatm, "DSL line goes up\n");
} else if ((count == 6) && !memcmp(down_int, instance->int_data, 6)) {
atm_info(usbatm, "DSL line goes down\n");
@@ -688,7 +688,7 @@ static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_de
atm_dbg(usbatm, "%s entered\n", __func__);
- del_timer_sync(&instance->status_check_timer);
+ timer_delete_sync(&instance->status_check_timer);
/*
* Since resubmit_timer and int_urb can schedule themselves and
@@ -697,14 +697,14 @@ static void speedtch_atm_stop(struct usbatm_data *usbatm, struct atm_dev *atm_de
instance->int_urb = NULL; /* signal shutdown */
mb();
usb_kill_urb(int_urb);
- del_timer_sync(&instance->resubmit_timer);
+ timer_delete_sync(&instance->resubmit_timer);
/*
* At this point, speedtch_handle_int and speedtch_resubmit_int
* can run or be running, but instance->int_urb == NULL means that
* they will not reschedule
*/
usb_kill_urb(int_urb);
- del_timer_sync(&instance->resubmit_timer);
+ timer_delete_sync(&instance->resubmit_timer);
usb_free_urb(int_urb);
flush_work(&instance->status_check_work);
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index d1e622bb1406..a6a05e85ef8c 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -1237,8 +1237,8 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
for (i = 0; i < num_rcv_urbs + num_snd_urbs; i++)
usb_kill_urb(instance->urbs[i]);
- del_timer_sync(&instance->rx_channel.delay);
- del_timer_sync(&instance->tx_channel.delay);
+ timer_delete_sync(&instance->rx_channel.delay);
+ timer_delete_sync(&instance->tx_channel.delay);
/* turn usbatm_[rt]x_process into something close to a no-op */
/* no need to take the spinlock */
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
index fd1beb10bba7..d9d8dc05b235 100644
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -1963,6 +1963,7 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
unsigned int bit;
unsigned long reg;
+ local_bh_disable();
spin_lock_irqsave(&priv_dev->lock, flags);
reg = readl(&priv_dev->regs->usb_ists);
@@ -2004,6 +2005,7 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
irqend:
writel(~0, &priv_dev->regs->ep_ien);
spin_unlock_irqrestore(&priv_dev->lock, flags);
+ local_bh_enable();
return ret;
}
@@ -3468,7 +3470,7 @@ __must_hold(&cdns->lock)
return 0;
}
-static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated)
+static int cdns3_gadget_resume(struct cdns *cdns, bool lost_power)
{
struct cdns3_device *priv_dev = cdns->gadget_dev;
@@ -3476,7 +3478,7 @@ static int cdns3_gadget_resume(struct cdns *cdns, bool hibernated)
return 0;
cdns3_gadget_config(priv_dev);
- if (hibernated)
+ if (lost_power)
writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
return 0;
diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c
index 040bb91e9c01..302ebf6d8e53 100644
--- a/drivers/usb/cdns3/cdns3-ti.c
+++ b/drivers/usb/cdns3/cdns3-ti.c
@@ -58,6 +58,7 @@ struct cdns_ti {
unsigned vbus_divider:1;
struct clk *usb2_refclk;
struct clk *lpm_clk;
+ int usb2_refclk_rate_code;
};
static const int cdns_ti_rate_table[] = { /* in KHZ */
@@ -98,15 +99,50 @@ static const struct of_dev_auxdata cdns_ti_auxdata[] = {
{},
};
+static void cdns_ti_reset_and_init_hw(struct cdns_ti *data)
+{
+ u32 reg;
+
+ /* assert RESET */
+ reg = cdns_ti_readl(data, USBSS_W1);
+ reg &= ~USBSS_W1_PWRUP_RST;
+ cdns_ti_writel(data, USBSS_W1, reg);
+
+ /* set static config */
+ reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
+ reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
+ reg |= data->usb2_refclk_rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
+
+ reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
+ if (data->vbus_divider)
+ reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
+
+ cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
+ reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
+
+ /* set USB2_ONLY mode if requested */
+ reg = cdns_ti_readl(data, USBSS_W1);
+ if (data->usb2_only)
+ reg |= USBSS_W1_USB2_ONLY;
+
+ /* set default modestrap */
+ reg |= USBSS_W1_MODESTRAP_SEL;
+ reg &= ~USBSS_W1_MODESTRAP_MASK;
+ reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT;
+ cdns_ti_writel(data, USBSS_W1, reg);
+
+ /* de-assert RESET */
+ reg |= USBSS_W1_PWRUP_RST;
+ cdns_ti_writel(data, USBSS_W1, reg);
+}
+
static int cdns_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct cdns_ti *data;
- int error;
- u32 reg;
- int rate_code, i;
unsigned long rate;
+ int error, i;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -146,7 +182,17 @@ static int cdns_ti_probe(struct platform_device *pdev)
return -EINVAL;
}
- rate_code = i;
+ data->usb2_refclk_rate_code = i;
+ data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
+ data->usb2_only = device_property_read_bool(dev, "ti,usb2-only");
+
+ /*
+ * The call below to pm_runtime_get_sync() MIGHT reset hardware, if it
+ * detects it as uninitialised. We want to enforce a reset at probe,
+ * and so do it manually here. This means the first runtime_resume()
+ * will be a no-op.
+ */
+ cdns_ti_reset_and_init_hw(data);
pm_runtime_enable(dev);
error = pm_runtime_get_sync(dev);
@@ -155,40 +201,6 @@ static int cdns_ti_probe(struct platform_device *pdev)
goto err;
}
- /* assert RESET */
- reg = cdns_ti_readl(data, USBSS_W1);
- reg &= ~USBSS_W1_PWRUP_RST;
- cdns_ti_writel(data, USBSS_W1, reg);
-
- /* set static config */
- reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
- reg &= ~USBSS1_STATIC_PLL_REF_SEL_MASK;
- reg |= rate_code << USBSS1_STATIC_PLL_REF_SEL_SHIFT;
-
- reg &= ~USBSS1_STATIC_VBUS_SEL_MASK;
- data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
- if (data->vbus_divider)
- reg |= 1 << USBSS1_STATIC_VBUS_SEL_SHIFT;
-
- cdns_ti_writel(data, USBSS_STATIC_CONFIG, reg);
- reg = cdns_ti_readl(data, USBSS_STATIC_CONFIG);
-
- /* set USB2_ONLY mode if requested */
- reg = cdns_ti_readl(data, USBSS_W1);
- data->usb2_only = device_property_read_bool(dev, "ti,usb2-only");
- if (data->usb2_only)
- reg |= USBSS_W1_USB2_ONLY;
-
- /* set default modestrap */
- reg |= USBSS_W1_MODESTRAP_SEL;
- reg &= ~USBSS_W1_MODESTRAP_MASK;
- reg |= USBSS_MODESTRAP_MODE_NONE << USBSS_W1_MODESTRAP_SHIFT;
- cdns_ti_writel(data, USBSS_W1, reg);
-
- /* de-assert RESET */
- reg |= USBSS_W1_PWRUP_RST;
- cdns_ti_writel(data, USBSS_W1, reg);
-
error = of_platform_populate(node, NULL, cdns_ti_auxdata, dev);
if (error) {
dev_err(dev, "failed to create children: %d\n", error);
@@ -224,6 +236,24 @@ static void cdns_ti_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
}
+static int cdns_ti_runtime_resume(struct device *dev)
+{
+ const u32 mask = USBSS_W1_PWRUP_RST | USBSS_W1_MODESTRAP_SEL;
+ struct cdns_ti *data = dev_get_drvdata(dev);
+ u32 w1;
+
+ w1 = cdns_ti_readl(data, USBSS_W1);
+ if ((w1 & mask) != mask)
+ cdns_ti_reset_and_init_hw(data);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cdns_ti_pm_ops = {
+ RUNTIME_PM_OPS(NULL, cdns_ti_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
static const struct of_device_id cdns_ti_of_match[] = {
{ .compatible = "ti,j721e-usb", },
{ .compatible = "ti,am64-usb", },
@@ -237,6 +267,7 @@ static struct platform_driver cdns_ti_driver = {
.driver = {
.name = "cdns3-ti",
.of_match_table = cdns_ti_of_match,
+ .pm = pm_ptr(&cdns_ti_pm_ops),
},
};
diff --git a/drivers/usb/cdns3/cdnsp-gadget.c b/drivers/usb/cdns3/cdnsp-gadget.c
index 4a3f0f958256..55f95f41b3b4 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.c
+++ b/drivers/usb/cdns3/cdnsp-gadget.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/log2.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/pci.h>
#include <linux/irq.h>
#include <linux/dmi.h>
@@ -28,7 +29,8 @@
unsigned int cdnsp_port_speed(unsigned int port_status)
{
/*Detect gadget speed based on PORTSC register*/
- if (DEV_SUPERSPEEDPLUS(port_status))
+ if (DEV_SUPERSPEEDPLUS(port_status) ||
+ DEV_SSP_GEN1x2(port_status) || DEV_SSP_GEN2x2(port_status))
return USB_SPEED_SUPER_PLUS;
else if (DEV_SUPERSPEED(port_status))
return USB_SPEED_SUPER;
@@ -138,6 +140,26 @@ static void cdnsp_clear_port_change_bit(struct cdnsp_device *pdev,
(portsc & PORT_CHANGE_BITS), port_regs);
}
+static void cdnsp_set_apb_timeout_value(struct cdnsp_device *pdev)
+{
+ struct cdns *cdns = dev_get_drvdata(pdev->dev);
+ __le32 __iomem *reg;
+ void __iomem *base;
+ u32 offset = 0;
+ u32 val;
+
+ if (!cdns->override_apb_timeout)
+ return;
+
+ base = &pdev->cap_regs->hc_capbase;
+ offset = cdnsp_find_next_ext_cap(base, offset, D_XEC_PRE_REGS_CAP);
+ reg = base + offset + REG_CHICKEN_BITS_3_OFFSET;
+
+ val = le32_to_cpu(readl(reg));
+ val = CHICKEN_APB_TIMEOUT_SET(val, cdns->override_apb_timeout);
+ writel(cpu_to_le32(val), reg);
+}
+
static void cdnsp_set_chicken_bits_2(struct cdnsp_device *pdev, u32 bit)
{
__le32 __iomem *reg;
@@ -526,6 +548,7 @@ int cdnsp_wait_for_cmd_compl(struct cdnsp_device *pdev)
dma_addr_t cmd_deq_dma;
union cdnsp_trb *event;
u32 cycle_state;
+ u32 retry = 10;
int ret, val;
u64 cmd_dma;
u32 flags;
@@ -557,8 +580,23 @@ int cdnsp_wait_for_cmd_compl(struct cdnsp_device *pdev)
flags = le32_to_cpu(event->event_cmd.flags);
/* Check the owner of the TRB. */
- if ((flags & TRB_CYCLE) != cycle_state)
+ if ((flags & TRB_CYCLE) != cycle_state) {
+ /*
+ * Give some extra time to get chance controller
+ * to finish command before returning error code.
+ * Checking CMD_RING_BUSY is not sufficient because
+ * this bit is cleared to '0' when the Command
+ * Descriptor has been executed by controller
+ * and not when command completion event has
+ * be added to event ring.
+ */
+ if (retry--) {
+ udelay(20);
+ continue;
+ }
+
return -EINVAL;
+ }
cmd_dma = le64_to_cpu(event->event_cmd.cmd_trb);
@@ -1671,12 +1709,12 @@ static int cdnsp_gadget_init_endpoints(struct cdnsp_device *pdev)
"CTRL: %s, INT: %s, BULK: %s, ISOC %s, "
"SupDir IN: %s, OUT: %s\n",
pep->name, 1024,
- (pep->endpoint.caps.type_control) ? "yes" : "no",
- (pep->endpoint.caps.type_int) ? "yes" : "no",
- (pep->endpoint.caps.type_bulk) ? "yes" : "no",
- (pep->endpoint.caps.type_iso) ? "yes" : "no",
- (pep->endpoint.caps.dir_in) ? "yes" : "no",
- (pep->endpoint.caps.dir_out) ? "yes" : "no");
+ str_yes_no(pep->endpoint.caps.type_control),
+ str_yes_no(pep->endpoint.caps.type_int),
+ str_yes_no(pep->endpoint.caps.type_bulk),
+ str_yes_no(pep->endpoint.caps.type_iso),
+ str_yes_no(pep->endpoint.caps.dir_in),
+ str_yes_no(pep->endpoint.caps.dir_out));
INIT_LIST_HEAD(&pep->pending_list);
}
@@ -1772,6 +1810,8 @@ static void cdnsp_get_rev_cap(struct cdnsp_device *pdev)
reg += cdnsp_find_next_ext_cap(reg, 0, RTL_REV_CAP);
pdev->rev_cap = reg;
+ pdev->rtl_revision = readl(&pdev->rev_cap->rtl_revision);
+
dev_info(pdev->dev, "Rev: %08x/%08x, eps: %08x, buff: %08x/%08x\n",
readl(&pdev->rev_cap->ctrl_revision),
readl(&pdev->rev_cap->rtl_revision),
@@ -1797,6 +1837,15 @@ static int cdnsp_gen_setup(struct cdnsp_device *pdev)
pdev->hci_version = HC_VERSION(pdev->hcc_params);
pdev->hcc_params = readl(&pdev->cap_regs->hcc_params);
+ /*
+ * Override the APB timeout value to give the controller more time for
+ * enabling UTMI clock and synchronizing APB and UTMI clock domains.
+ * This fix is platform specific and is required to fixes issue with
+ * reading incorrect value from PORTSC register after resuming
+ * from L1 state.
+ */
+ cdnsp_set_apb_timeout_value(pdev);
+
cdnsp_get_rev_cap(pdev);
/* Make sure the Device Controller is halted. */
@@ -1973,7 +2022,7 @@ static int cdnsp_gadget_suspend(struct cdns *cdns, bool do_wakeup)
return 0;
}
-static int cdnsp_gadget_resume(struct cdns *cdns, bool hibernated)
+static int cdnsp_gadget_resume(struct cdns *cdns, bool lost_power)
{
struct cdnsp_device *pdev = cdns->gadget_dev;
enum usb_device_speed max_speed;
diff --git a/drivers/usb/cdns3/cdnsp-gadget.h b/drivers/usb/cdns3/cdnsp-gadget.h
index 84887dfea763..2afa3e558f85 100644
--- a/drivers/usb/cdns3/cdnsp-gadget.h
+++ b/drivers/usb/cdns3/cdnsp-gadget.h
@@ -285,11 +285,15 @@ struct cdnsp_port_regs {
#define XDEV_HS (0x3 << 10)
#define XDEV_SS (0x4 << 10)
#define XDEV_SSP (0x5 << 10)
+#define XDEV_SSP1x2 (0x6 << 10)
+#define XDEV_SSP2x2 (0x7 << 10)
#define DEV_UNDEFSPEED(p) (((p) & DEV_SPEED_MASK) == (0x0 << 10))
#define DEV_FULLSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_FS)
#define DEV_HIGHSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_HS)
#define DEV_SUPERSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_SS)
#define DEV_SUPERSPEEDPLUS(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP)
+#define DEV_SSP_GEN1x2(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP1x2)
+#define DEV_SSP_GEN2x2(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP2x2)
#define DEV_SUPERSPEED_ANY(p) (((p) & DEV_SPEED_MASK) >= XDEV_SS)
#define DEV_PORT_SPEED(p) (((p) >> 10) & 0x0f)
/* Port Link State Write Strobe - set this when changing link state */
@@ -520,6 +524,9 @@ struct cdnsp_rev_cap {
#define REG_CHICKEN_BITS_2_OFFSET 0x48
#define CHICKEN_XDMA_2_TP_CACHE_DIS BIT(28)
+#define REG_CHICKEN_BITS_3_OFFSET 0x4C
+#define CHICKEN_APB_TIMEOUT_SET(p, val) (((p) & ~GENMASK(21, 0)) | (val))
+
/* XBUF Extended Capability ID. */
#define XBUF_CAP_ID 0xCB
#define XBUF_RX_TAG_MASK_0_OFFSET 0x1C
@@ -1357,6 +1364,7 @@ struct cdnsp_port {
* @rev_cap: Controller Capabilities Registers.
* @hcs_params1: Cached register copies of read-only HCSPARAMS1
* @hcc_params: Cached register copies of read-only HCCPARAMS1
+ * @rtl_revision: Cached controller rtl revision.
* @setup: Temporary buffer for setup packet.
* @ep0_preq: Internal allocated request used during enumeration.
* @ep0_stage: ep0 stage during enumeration process.
@@ -1411,6 +1419,8 @@ struct cdnsp_device {
__u32 hcs_params1;
__u32 hcs_params3;
__u32 hcc_params;
+ #define RTL_REVISION_NEW_LPM 0x2700
+ __u32 rtl_revision;
/* Lock used in interrupt thread context. */
spinlock_t lock;
struct usb_ctrlrequest setup;
diff --git a/drivers/usb/cdns3/cdnsp-pci.c b/drivers/usb/cdns3/cdnsp-pci.c
index a51144504ff3..8c361b8394e9 100644
--- a/drivers/usb/cdns3/cdnsp-pci.c
+++ b/drivers/usb/cdns3/cdnsp-pci.c
@@ -28,6 +28,8 @@
#define PCI_DRIVER_NAME "cdns-pci-usbssp"
#define PLAT_DRIVER_NAME "cdns-usbssp"
+#define CHICKEN_APB_TIMEOUT_VALUE 0x1C20
+
static struct pci_dev *cdnsp_get_second_fun(struct pci_dev *pdev)
{
/*
@@ -139,6 +141,14 @@ static int cdnsp_pci_probe(struct pci_dev *pdev,
cdnsp->otg_irq = pdev->irq;
}
+ /*
+ * Cadence PCI based platform require some longer timeout for APB
+ * to fixes domain clock synchronization issue after resuming
+ * controller from L1 state.
+ */
+ cdnsp->override_apb_timeout = CHICKEN_APB_TIMEOUT_VALUE;
+ pci_set_drvdata(pdev, cdnsp);
+
if (pci_is_enabled(func)) {
cdnsp->dev = dev;
cdnsp->gadget_init = cdnsp_gadget_init;
@@ -148,8 +158,6 @@ static int cdnsp_pci_probe(struct pci_dev *pdev,
goto free_cdnsp;
}
- pci_set_drvdata(pdev, cdnsp);
-
device_wakeup_enable(&pdev->dev);
if (pci_dev_run_wake(pdev))
pm_runtime_put_noidle(&pdev->dev);
diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c
index 46852529499d..fd06cb85c4ea 100644
--- a/drivers/usb/cdns3/cdnsp-ring.c
+++ b/drivers/usb/cdns3/cdnsp-ring.c
@@ -308,7 +308,8 @@ static bool cdnsp_ring_ep_doorbell(struct cdnsp_device *pdev,
writel(db_value, reg_addr);
- cdnsp_force_l0_go(pdev);
+ if (pdev->rtl_revision < RTL_REVISION_NEW_LPM)
+ cdnsp_force_l0_go(pdev);
/* Doorbell was set. */
return true;
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index 465e9267b49c..1243a5cea91b 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -524,14 +524,13 @@ EXPORT_SYMBOL_GPL(cdns_suspend);
int cdns_resume(struct cdns *cdns)
{
+ bool power_lost = cdns_power_is_lost(cdns);
enum usb_role real_role;
bool role_changed = false;
int ret = 0;
- if (cdns_power_is_lost(cdns)) {
- if (cdns->role_sw) {
- cdns->role = cdns_role_get(cdns->role_sw);
- } else {
+ if (power_lost) {
+ if (!cdns->role_sw) {
real_role = cdns_hw_role_state_machine(cdns);
if (real_role != cdns->role) {
ret = cdns_hw_role_switch(cdns);
@@ -553,7 +552,7 @@ int cdns_resume(struct cdns *cdns)
}
if (cdns->roles[cdns->role]->resume)
- cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns));
+ cdns->roles[cdns->role]->resume(cdns, power_lost);
return 0;
}
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
index 57d47348dc19..801be9e61340 100644
--- a/drivers/usb/cdns3/core.h
+++ b/drivers/usb/cdns3/core.h
@@ -30,7 +30,7 @@ struct cdns_role_driver {
int (*start)(struct cdns *cdns);
void (*stop)(struct cdns *cdns);
int (*suspend)(struct cdns *cdns, bool do_wakeup);
- int (*resume)(struct cdns *cdns, bool hibernated);
+ int (*resume)(struct cdns *cdns, bool lost_power);
const char *name;
#define CDNS_ROLE_STATE_INACTIVE 0
#define CDNS_ROLE_STATE_ACTIVE 1
@@ -79,6 +79,8 @@ struct cdns3_platform_data {
* @pdata: platform data from glue layer
* @lock: spinlock structure
* @xhci_plat_data: xhci private data structure pointer
+ * @override_apb_timeout: hold value of APB timeout. For value 0 the default
+ * value in CHICKEN_BITS_3 will be preserved.
* @gadget_init: pointer to gadget initialization function
*/
struct cdns {
@@ -117,6 +119,7 @@ struct cdns {
struct cdns3_platform_data *pdata;
spinlock_t lock;
struct xhci_plat_priv *xhci_plat_data;
+ u32 override_apb_timeout;
int (*gadget_init)(struct cdns *cdns);
};
diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
index 7ba760ee62e3..f0df114c2b53 100644
--- a/drivers/usb/cdns3/host.c
+++ b/drivers/usb/cdns3/host.c
@@ -138,6 +138,16 @@ static void cdns_host_exit(struct cdns *cdns)
cdns_drd_host_off(cdns);
}
+static int cdns_host_resume(struct cdns *cdns, bool power_lost)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(cdns->host_dev);
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
+
+ priv->power_lost = power_lost;
+
+ return 0;
+}
+
int cdns_host_init(struct cdns *cdns)
{
struct cdns_role_driver *rdrv;
@@ -148,6 +158,7 @@ int cdns_host_init(struct cdns *cdns)
rdrv->start = __cdns_host_init;
rdrv->stop = cdns_host_exit;
+ rdrv->resume = cdns_host_resume;
rdrv->state = CDNS_ROLE_STATE_INACTIVE;
rdrv->name = "host";
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index 1a7fc638213e..4f8bfd242b59 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -336,6 +336,13 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
return ret;
}
+static void ci_hdrc_imx_disable_regulator(void *arg)
+{
+ struct ci_hdrc_imx_data *data = arg;
+
+ regulator_disable(data->hsic_pad_regulator);
+}
+
static int ci_hdrc_imx_probe(struct platform_device *pdev)
{
struct ci_hdrc_imx_data *data;
@@ -394,6 +401,13 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
"Failed to enable HSIC pad regulator\n");
goto err_put;
}
+ ret = devm_add_action_or_reset(dev,
+ ci_hdrc_imx_disable_regulator, data);
+ if (ret) {
+ dev_err(dev,
+ "Failed to add regulator devm action\n");
+ goto err_put;
+ }
}
}
@@ -432,11 +446,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
ret = imx_get_clks(dev);
if (ret)
- goto disable_hsic_regulator;
+ goto qos_remove_request;
ret = imx_prepare_enable_clks(dev);
if (ret)
- goto disable_hsic_regulator;
+ goto qos_remove_request;
ret = clk_prepare_enable(data->clk_wakeup);
if (ret)
@@ -470,7 +484,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) {
pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL;
data->override_phy_control = true;
- usb_phy_init(pdata.usb_phy);
+ ret = usb_phy_init(pdata.usb_phy);
+ if (ret) {
+ dev_err(dev, "Failed to init phy\n");
+ goto err_clk;
+ }
}
if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
@@ -479,7 +497,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
ret = imx_usbmisc_init(data->usbmisc_data);
if (ret) {
dev_err(dev, "usbmisc init failed, ret=%d\n", ret);
- goto err_clk;
+ goto phy_shutdown;
}
data->ci_pdev = ci_hdrc_add_device(dev,
@@ -488,7 +506,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
if (IS_ERR(data->ci_pdev)) {
ret = PTR_ERR(data->ci_pdev);
dev_err_probe(dev, ret, "ci_hdrc_add_device failed\n");
- goto err_clk;
+ goto phy_shutdown;
}
if (data->usbmisc_data) {
@@ -522,19 +540,20 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
disable_device:
ci_hdrc_remove_device(data->ci_pdev);
+phy_shutdown:
+ if (data->override_phy_control)
+ usb_phy_shutdown(data->phy);
err_clk:
clk_disable_unprepare(data->clk_wakeup);
err_wakeup_clk:
imx_disable_unprepare_clks(dev);
-disable_hsic_regulator:
- if (data->hsic_pad_regulator)
- /* don't overwrite original ret (cf. EPROBE_DEFER) */
- regulator_disable(data->hsic_pad_regulator);
+qos_remove_request:
if (pdata.flags & CI_HDRC_PMQOS)
cpu_latency_qos_remove_request(&data->pm_qos_req);
data->ci_pdev = NULL;
err_put:
- put_device(data->usbmisc_data->dev);
+ if (data->usbmisc_data)
+ put_device(data->usbmisc_data->dev);
return ret;
}
@@ -556,10 +575,9 @@ static void ci_hdrc_imx_remove(struct platform_device *pdev)
clk_disable_unprepare(data->clk_wakeup);
if (data->plat_data->flags & CI_HDRC_PMQOS)
cpu_latency_qos_remove_request(&data->pm_qos_req);
- if (data->hsic_pad_regulator)
- regulator_disable(data->hsic_pad_regulator);
}
- put_device(data->usbmisc_data->dev);
+ if (data->usbmisc_data)
+ put_device(data->usbmisc_data->dev);
}
static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index 0cce19208370..ced6076a8248 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -13,6 +13,7 @@
#include <linux/usb/hcd.h>
#include <linux/usb/chipidea.h>
#include <linux/regulator/consumer.h>
+#include <linux/string_choices.h>
#include <linux/pinctrl/consumer.h>
#include "../host/ehci.h"
@@ -56,7 +57,7 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
if (ret) {
dev_err(dev,
"Failed to %s vbus regulator, ret=%d\n",
- enable ? "enable" : "disable", ret);
+ str_enable_disable(enable), ret);
return ret;
}
priv->enabled = enable;
@@ -256,8 +257,14 @@ static int ci_ehci_hub_control(
struct device *dev = hcd->self.controller;
struct ci_hdrc *ci = dev_get_drvdata(dev);
- port_index = wIndex & 0xff;
- port_index -= (port_index > 0);
+ /*
+ * Avoid out-of-bounds values while calculating the port index
+ * from wIndex. The compiler doesn't like pointers to invalid
+ * addresses, even if they are never used.
+ */
+ port_index = (wIndex - 1) & 0xff;
+ if (port_index >= HCS_N_PORTS_MAX)
+ port_index = 0;
status_reg = &ehci->regs->port_status[port_index];
spin_lock_irqsave(&ehci->lock, flags);
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index c17516c29b63..a093544482d5 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -424,8 +424,7 @@ static enum hrtimer_restart ci_otg_hrtimer_func(struct hrtimer *t)
/* Initialize timers */
static int ci_otg_init_timers(struct ci_hdrc *ci)
{
- hrtimer_init(&ci->otg_fsm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
- ci->otg_fsm_hrtimer.function = ci_otg_hrtimer_func;
+ hrtimer_setup(&ci->otg_fsm_hrtimer, ci_otg_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
return 0;
}
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 1394881fde5f..6243d8005f5d 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -440,7 +440,7 @@ static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
else if (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
}
- /* If the polarity is not set keep it as setup by the bootlader */
+ /* If the polarity is not set keep it as setup by the bootloader */
if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base + data->index * 4);
@@ -645,7 +645,7 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
else if (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
}
- /* If the polarity is not set keep it as setup by the bootlader */
+ /* If the polarity is not set keep it as setup by the bootloader */
if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base);
@@ -939,7 +939,7 @@ static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data)
else if (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
}
- /* If the polarity is not set keep it as setup by the bootlader */
+ /* If the polarity is not set keep it as setup by the bootloader */
if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
@@ -1185,7 +1185,7 @@ int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup)
if (usbmisc->ops->hsic_set_clk && data->hsic)
ret = usbmisc->ops->hsic_set_clk(data, false);
if (ret) {
- dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
+ dev_err(data->dev, "hsic_set_clk failed, ret=%d\n", ret);
return ret;
}
@@ -1224,7 +1224,7 @@ int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup)
if (usbmisc->ops->hsic_set_clk && data->hsic)
ret = usbmisc->ops->hsic_set_clk(data, true);
if (ret) {
- dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
+ dev_err(data->dev, "hsic_set_clk failed, ret=%d\n", ret);
goto hsic_set_clk_fail;
}
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 86ee39db013f..16e7fa4d488d 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -726,7 +726,7 @@ static int wdm_open(struct inode *inode, struct file *file)
rv = -EBUSY;
goto out;
}
-
+ smp_rmb(); /* ordered against wdm_wwan_port_stop() */
rv = usb_autopm_get_interface(desc->intf);
if (rv < 0) {
dev_err(&desc->intf->dev, "Error autopm - %d\n", rv);
@@ -829,6 +829,7 @@ static struct usb_class_driver wdm_class = {
static int wdm_wwan_port_start(struct wwan_port *port)
{
struct wdm_device *desc = wwan_port_get_drvdata(port);
+ int rv;
/* The interface is both exposed via the WWAN framework and as a
* legacy usbmisc chardev. If chardev is already open, just fail
@@ -848,7 +849,15 @@ static int wdm_wwan_port_start(struct wwan_port *port)
wwan_port_txon(port);
/* Start getting events */
- return usb_submit_urb(desc->validity, GFP_KERNEL);
+ rv = usb_submit_urb(desc->validity, GFP_KERNEL);
+ if (rv < 0) {
+ wwan_port_txoff(port);
+ desc->manage_power(desc->intf, 0);
+ /* this must be last lest we race with chardev open */
+ clear_bit(WDM_WWAN_IN_USE, &desc->flags);
+ }
+
+ return rv;
}
static void wdm_wwan_port_stop(struct wwan_port *port)
@@ -859,8 +868,10 @@ static void wdm_wwan_port_stop(struct wwan_port *port)
poison_urbs(desc);
desc->manage_power(desc->intf, 0);
clear_bit(WDM_READ, &desc->flags);
- clear_bit(WDM_WWAN_IN_USE, &desc->flags);
unpoison_urbs(desc);
+ smp_wmb(); /* ordered against wdm_open() */
+ /* this must be last lest we open a poisoned device */
+ clear_bit(WDM_WWAN_IN_USE, &desc->flags);
}
static void wdm_wwan_port_tx_complete(struct urb *urb)
@@ -868,7 +879,7 @@ static void wdm_wwan_port_tx_complete(struct urb *urb)
struct sk_buff *skb = urb->context;
struct wdm_device *desc = skb_shinfo(skb)->destructor_arg;
- usb_autopm_put_interface(desc->intf);
+ usb_autopm_put_interface_async(desc->intf);
wwan_port_txon(desc->wwanp);
kfree_skb(skb);
}
@@ -898,7 +909,7 @@ static int wdm_wwan_port_tx(struct wwan_port *port, struct sk_buff *skb)
req->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
req->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
req->wValue = 0;
- req->wIndex = desc->inum;
+ req->wIndex = desc->inum; /* already converted */
req->wLength = cpu_to_le16(skb->len);
skb_shinfo(skb)->destructor_arg = desc;
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index ff1a941fd2ed..e2527faa6592 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -87,7 +87,7 @@
/* Get two-int array: [0]=vendor ID, [1]=product ID: */
#define LPIOC_GET_VID_PID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_VID_PID, len)
/* Perform class specific soft reset */
-#define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0);
+#define LPIOC_SOFT_RESET _IOC(_IOC_NONE, 'P', IOCNR_SOFT_RESET, 0)
/*
* A DEVICE_ID string may include the printer's serial number.
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 34e46ef308ab..75de29725a45 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -482,6 +482,8 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
u8 *buffer;
u8 tag;
int rv;
+ long wait_rv;
+ unsigned long expire;
dev_dbg(dev, "Enter ioctl_read_stb iin_ep_present: %d\n",
data->iin_ep_present);
@@ -511,16 +513,18 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
}
if (data->iin_ep_present) {
- rv = wait_event_interruptible_timeout(
+ expire = msecs_to_jiffies(file_data->timeout);
+ wait_rv = wait_event_interruptible_timeout(
data->waitq,
atomic_read(&data->iin_data_valid) != 0,
- file_data->timeout);
- if (rv < 0) {
- dev_dbg(dev, "wait interrupted %d\n", rv);
+ expire);
+ if (wait_rv < 0) {
+ dev_dbg(dev, "wait interrupted %ld\n", wait_rv);
+ rv = wait_rv;
goto exit;
}
- if (rv == 0) {
+ if (wait_rv == 0) {
dev_dbg(dev, "wait timed out\n");
rv = -ETIMEDOUT;
goto exit;
@@ -539,6 +543,8 @@ static int usbtmc_get_stb(struct usbtmc_file_data *file_data, __u8 *stb)
dev_dbg(dev, "stb:0x%02x received %d\n", (unsigned int)*stb, rv);
+ rv = 0;
+
exit:
/* bump interrupt bTag */
data->iin_bTag += 1;
@@ -559,14 +565,15 @@ static int usbtmc488_ioctl_read_stb(struct usbtmc_file_data *file_data,
rv = usbtmc_get_stb(file_data, &stb);
- if (rv > 0) {
- srq_asserted = atomic_xchg(&file_data->srq_asserted,
- srq_asserted);
- if (srq_asserted)
- stb |= 0x40; /* Set RQS bit */
+ if (rv < 0)
+ return rv;
+
+ srq_asserted = atomic_xchg(&file_data->srq_asserted, srq_asserted);
+ if (srq_asserted)
+ stb |= 0x40; /* Set RQS bit */
+
+ rv = put_user(stb, (__u8 __user *)arg);
- rv = put_user(stb, (__u8 __user *)arg);
- }
return rv;
}
@@ -602,9 +609,9 @@ static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data,
{
struct usbtmc_device_data *data = file_data->data;
struct device *dev = &data->intf->dev;
- int rv;
u32 timeout;
unsigned long expire;
+ long wait_rv;
if (!data->iin_ep_present) {
dev_dbg(dev, "no interrupt endpoint present\n");
@@ -618,25 +625,24 @@ static int usbtmc488_ioctl_wait_srq(struct usbtmc_file_data *file_data,
mutex_unlock(&data->io_mutex);
- rv = wait_event_interruptible_timeout(
- data->waitq,
- atomic_read(&file_data->srq_asserted) != 0 ||
- atomic_read(&file_data->closing),
- expire);
+ wait_rv = wait_event_interruptible_timeout(
+ data->waitq,
+ atomic_read(&file_data->srq_asserted) != 0 ||
+ atomic_read(&file_data->closing),
+ expire);
mutex_lock(&data->io_mutex);
/* Note! disconnect or close could be called in the meantime */
if (atomic_read(&file_data->closing) || data->zombie)
- rv = -ENODEV;
+ return -ENODEV;
- if (rv < 0) {
- /* dev can be invalid now! */
- pr_debug("%s - wait interrupted %d\n", __func__, rv);
- return rv;
+ if (wait_rv < 0) {
+ dev_dbg(dev, "%s - wait interrupted %ld\n", __func__, wait_rv);
+ return wait_rv;
}
- if (rv == 0) {
+ if (wait_rv == 0) {
dev_dbg(dev, "%s - wait timed out\n", __func__);
return -ETIMEDOUT;
}
@@ -830,6 +836,7 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
unsigned long expire;
int bufcount = 1;
int again = 0;
+ long wait_rv;
/* mutex already locked */
@@ -942,19 +949,24 @@ static ssize_t usbtmc_generic_read(struct usbtmc_file_data *file_data,
if (!(flags & USBTMC_FLAG_ASYNC)) {
dev_dbg(dev, "%s: before wait time %lu\n",
__func__, expire);
- retval = wait_event_interruptible_timeout(
+ wait_rv = wait_event_interruptible_timeout(
file_data->wait_bulk_in,
usbtmc_do_transfer(file_data),
expire);
- dev_dbg(dev, "%s: wait returned %d\n",
- __func__, retval);
+ dev_dbg(dev, "%s: wait returned %ld\n",
+ __func__, wait_rv);
+
+ if (wait_rv < 0) {
+ retval = wait_rv;
+ goto error;
+ }
- if (retval <= 0) {
- if (retval == 0)
- retval = -ETIMEDOUT;
+ if (wait_rv == 0) {
+ retval = -ETIMEDOUT;
goto error;
}
+
}
urb = usb_get_from_anchor(&file_data->in_anchor);
@@ -1380,7 +1392,10 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
if (!buffer)
return -ENOMEM;
- mutex_lock(&data->io_mutex);
+ retval = mutex_lock_interruptible(&data->io_mutex);
+ if (retval < 0)
+ goto exit_nolock;
+
if (data->zombie) {
retval = -ENODEV;
goto exit;
@@ -1503,6 +1518,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
exit:
mutex_unlock(&data->io_mutex);
+exit_nolock:
kfree(buffer);
return retval;
}
@@ -2186,7 +2202,7 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case USBTMC_IOCTL_GET_STB:
retval = usbtmc_get_stb(file_data, &tmp_byte);
- if (retval > 0)
+ if (!retval)
retval = put_user(tmp_byte, (__u8 __user *)arg);
break;
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index 871cf199b6bf..fc0845f681be 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -41,6 +41,12 @@ const char *usb_ep_type_string(int ep_type)
}
EXPORT_SYMBOL_GPL(usb_ep_type_string);
+/**
+ * usb_otg_state_string() - returns human readable name of OTG state.
+ * @state: the OTG state to return the human readable name of. If it's not
+ * any of the states defined in usb_otg_state enum, 'UNDEFINED' will be
+ * returned.
+ */
const char *usb_otg_state_string(enum usb_otg_state state)
{
static const char *const names[] = {
@@ -179,6 +185,14 @@ static const char *const usb_dr_modes[] = {
[USB_DR_MODE_OTG] = "otg",
};
+/**
+ * usb_get_dr_mode_from_string() - Get dual role mode for given string
+ * @str: String to find the corresponding dual role mode for
+ *
+ * This function performs a lookup for the given string and returns the
+ * corresponding enum usb_dr_mode. If no match for the string could be found,
+ * 'USB_DR_MODE_UNKNOWN' is returned.
+ */
static enum usb_dr_mode usb_get_dr_mode_from_string(const char *str)
{
int ret;
diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c
index c84b4a700084..1e36be2a28fd 100644
--- a/drivers/usb/common/usb-conn-gpio.c
+++ b/drivers/usb/common/usb-conn-gpio.c
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/regulator/consumer.h>
+#include <linux/string_choices.h>
#include <linux/usb/role.h>
#define USB_GPIO_DEB_MS 20 /* ms */
@@ -111,7 +112,7 @@ static void usb_conn_detect_cable(struct work_struct *work)
if (info->vbus)
dev_dbg(info->dev, "vbus regulator is %s\n",
- regulator_is_enabled(info->vbus) ? "enabled" : "disabled");
+ str_enabled_disabled(regulator_is_enabled(info->vbus)));
power_supply_changed(info->charger);
}
@@ -157,7 +158,7 @@ static int usb_conn_psy_register(struct usb_conn_info *info)
struct device *dev = info->dev;
struct power_supply_desc *desc = &info->desc;
struct power_supply_config cfg = {
- .of_node = dev->of_node,
+ .fwnode = dev_fwnode(dev),
};
desc->name = "usb-charger";
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 25a00f974934..13bd4ec4ea5f 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -9,6 +9,7 @@
#include <linux/usb/quirks.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/device.h>
#include <asm/byteorder.h>
#include "usb.h"
@@ -18,12 +19,6 @@
#define USB_MAXCONFIG 8 /* Arbitrary limit */
-
-static inline const char *plural(int n)
-{
- return (n == 1 ? "" : "s");
-}
-
static int find_next_descriptor(unsigned char *buffer, int size,
int dt1, int dt2, int *num_skipped)
{
@@ -69,6 +64,37 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
memcpy(&ep->ssp_isoc_ep_comp, desc, USB_DT_SSP_ISOC_EP_COMP_SIZE);
}
+static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev,
+ int cfgno, int inum, int asnum, struct usb_host_endpoint *ep,
+ unsigned char *buffer, int size)
+{
+ struct usb_eusb2_isoc_ep_comp_descriptor *desc;
+ struct usb_descriptor_header *h;
+
+ /*
+ * eUSB2 isochronous endpoint companion descriptor for this endpoint
+ * shall be declared before the next endpoint or interface descriptor
+ */
+ while (size >= USB_DT_EUSB2_ISOC_EP_COMP_SIZE) {
+ h = (struct usb_descriptor_header *)buffer;
+
+ if (h->bDescriptorType == USB_DT_EUSB2_ISOC_ENDPOINT_COMP) {
+ desc = (struct usb_eusb2_isoc_ep_comp_descriptor *)buffer;
+ ep->eusb2_isoc_ep_comp = *desc;
+ return;
+ }
+ if (h->bDescriptorType == USB_DT_ENDPOINT ||
+ h->bDescriptorType == USB_DT_INTERFACE)
+ break;
+
+ buffer += h->bLength;
+ size -= h->bLength;
+ }
+
+ dev_notice(ddev, "No eUSB2 isoc ep %d companion for config %d interface %d altsetting %d\n",
+ ep->desc.bEndpointAddress, cfgno, inum, asnum);
+}
+
static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
int inum, int asnum, struct usb_host_endpoint *ep,
unsigned char *buffer, int size)
@@ -263,8 +289,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
int n, i, j, retval;
unsigned int maxp;
const unsigned short *maxpacket_maxes;
+ u16 bcdUSB;
d = (struct usb_endpoint_descriptor *) buffer;
+ bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB);
buffer += d->bLength;
size -= d->bLength;
@@ -414,15 +442,17 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
/*
* Validate the wMaxPacketSize field.
- * Some devices have isochronous endpoints in altsetting 0;
- * the USB-2 spec requires such endpoints to have wMaxPacketSize = 0
- * (see the end of section 5.6.3), so don't warn about them.
+ * eUSB2 devices (see USB 2.0 Double Isochronous IN ECN 9.6.6 Endpoint)
+ * and devices with isochronous endpoints in altsetting 0 (see USB 2.0
+ * end of section 5.6.3) have wMaxPacketSize = 0.
+ * So don't warn about those.
*/
maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize);
- if (maxp == 0 && !(usb_endpoint_xfer_isoc(d) && asnum == 0)) {
+
+ if (maxp == 0 && bcdUSB != 0x0220 &&
+ !(usb_endpoint_xfer_isoc(d) && asnum == 0))
dev_notice(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n",
cfgno, inum, asnum, d->bEndpointAddress);
- }
/* Find the highest legal maxpacket size for this endpoint */
i = 0; /* additional transactions per microframe */
@@ -470,6 +500,12 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
maxp);
}
+ /* Parse a possible eUSB2 periodic endpoint companion descriptor */
+ if (bcdUSB == 0x0220 && d->wMaxPacketSize == 0 &&
+ (usb_endpoint_xfer_isoc(d) || usb_endpoint_xfer_int(d)))
+ usb_parse_eusb2_isoc_endpoint_companion(ddev, cfgno, inum, asnum,
+ endpoint, buffer, size);
+
/* Parse a possible SuperSpeed endpoint companion descriptor */
if (udev->speed >= USB_SPEED_SUPER)
usb_parse_ss_endpoint_companion(ddev, cfgno,
@@ -484,7 +520,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
retval = buffer - buffer0 + i;
if (n > 0)
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
- n, plural(n), "endpoint");
+ n, str_plural(n), "endpoint");
return retval;
skip_to_next_endpoint_or_interface_descriptor:
@@ -563,7 +599,7 @@ static int usb_parse_interface(struct device *ddev, int cfgno,
alt->extralen = i;
if (n > 0)
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
- n, plural(n), "interface");
+ n, str_plural(n), "interface");
buffer += i;
size -= i;
@@ -605,7 +641,7 @@ static int usb_parse_interface(struct device *ddev, int cfgno,
dev_notice(ddev, "config %d interface %d altsetting %d has %d "
"endpoint descriptor%s, different from the interface "
"descriptor's value: %d\n",
- cfgno, inum, asnum, n, plural(n), num_ep_orig);
+ cfgno, inum, asnum, n, str_plural(n), num_ep_orig);
return buffer - buffer0;
skip_to_next_interface_descriptor:
@@ -664,7 +700,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
if (size2 < sizeof(struct usb_descriptor_header)) {
dev_notice(ddev, "config %d descriptor has %d excess "
"byte%s, ignoring\n",
- cfgno, size2, plural(size2));
+ cfgno, size2, str_plural(size2));
break;
}
@@ -754,7 +790,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
if (n != nintf)
dev_notice(ddev, "config %d has %d interface%s, different from "
"the descriptor's value: %d\n",
- cfgno, n, plural(n), nintf_orig);
+ cfgno, n, str_plural(n), nintf_orig);
else if (n == 0)
dev_notice(ddev, "config %d has no interfaces?\n", cfgno);
config->desc.bNumInterfaces = nintf = n;
@@ -798,7 +834,7 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
config->extralen = i;
if (n > 0)
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
- n, plural(n), "configuration");
+ n, str_plural(n), "configuration");
buffer += i;
size -= i;
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index f203fdbfb6f6..460d4dde5994 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1086,15 +1086,14 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
-out:
- return retval;
+ return 0;
out_newid:
driver_unregister(&new_driver->driver);
-
+out:
pr_err("%s: error %d registering interface driver %s\n",
usbcore_name, retval, new_driver->name);
- goto out;
+ return retval;
}
EXPORT_SYMBOL_GPL(usb_register_driver);
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index b134bff5c3fe..9c6ae5e1198b 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -21,14 +21,10 @@
#include <linux/usb.h>
#include <linux/usb/hcd.h>
+#include <linux/string_choices.h>
#include <uapi/linux/usb/audio.h>
#include "usb.h"
-static inline const char *plural(int n)
-{
- return (n == 1 ? "" : "s");
-}
-
static int is_rndis(struct usb_interface_descriptor *desc)
{
return desc->bInterfaceClass == USB_CLASS_COMM
@@ -194,18 +190,18 @@ int usb_choose_configuration(struct usb_device *udev)
if (insufficient_power > 0)
dev_info(&udev->dev, "rejected %d configuration%s "
"due to insufficient available bus power\n",
- insufficient_power, plural(insufficient_power));
+ insufficient_power, str_plural(insufficient_power));
if (best) {
i = best->desc.bConfigurationValue;
dev_dbg(&udev->dev,
"configuration #%d chosen from %d choice%s\n",
- i, num_configs, plural(num_configs));
+ i, num_configs, str_plural(num_configs));
} else {
i = -1;
dev_warn(&udev->dev,
"no configuration chosen from %d choice%s\n",
- num_configs, plural(num_configs));
+ num_configs, str_plural(num_configs));
}
return i;
}
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index a08f3f228e6d..56b534f59907 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -422,7 +422,12 @@ static int suspend_common(struct device *dev, pm_message_t msg)
bool do_wakeup;
int retval;
- do_wakeup = PMSG_IS_AUTO(msg) ? true : device_may_wakeup(dev);
+ if (PMSG_IS_AUTO(msg))
+ do_wakeup = true;
+ else if (PMSG_NO_WAKEUP(msg))
+ do_wakeup = false;
+ else
+ do_wakeup = device_may_wakeup(dev);
/* Root hub suspend should have stopped all downstream traffic,
* and all bus master traffic. And done so for both the interface
@@ -521,6 +526,11 @@ static int hcd_pci_suspend(struct device *dev)
return suspend_common(dev, PMSG_SUSPEND);
}
+static int hcd_pci_freeze(struct device *dev)
+{
+ return suspend_common(dev, PMSG_FREEZE);
+}
+
static int hcd_pci_suspend_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -590,6 +600,7 @@ static int hcd_pci_restore(struct device *dev)
#else
#define hcd_pci_suspend NULL
+#define hcd_pci_freeze NULL
#define hcd_pci_suspend_noirq NULL
#define hcd_pci_poweroff_late NULL
#define hcd_pci_resume_noirq NULL
@@ -624,7 +635,7 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = {
.suspend_noirq = hcd_pci_suspend_noirq,
.resume_noirq = hcd_pci_resume_noirq,
.resume = hcd_pci_resume,
- .freeze = hcd_pci_suspend,
+ .freeze = hcd_pci_freeze,
.freeze_noirq = check_root_hub_suspended,
.thaw_noirq = NULL,
.thaw = hcd_pci_resume,
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 0b2490347b9f..a63c793bac21 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -415,7 +415,7 @@ ascii2desc(char const *s, u8 *buf, unsigned len)
static unsigned
rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
{
- char buf[100];
+ char buf[160];
char const *s;
static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04};
@@ -842,7 +842,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
} else { /* Status URB */
if (!hcd->uses_new_polling)
- del_timer (&hcd->rh_timer);
+ timer_delete(&hcd->rh_timer);
if (urb == hcd->status_urb) {
hcd->status_urb = NULL;
usb_hcd_unlink_urb_from_ep(hcd, urb);
@@ -1609,7 +1609,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
if (retval == 0)
retval = -EINPROGRESS;
else if (retval != -EIDRM && retval != -EBUSY)
- dev_dbg(&udev->dev, "hcd_unlink_urb %pK fail %d\n",
+ dev_dbg(&udev->dev, "hcd_unlink_urb %p fail %d\n",
urb, retval);
usb_put_dev(udev);
}
@@ -1786,7 +1786,7 @@ rescan:
/* kick hcd */
unlink1(hcd, urb, -ESHUTDOWN);
dev_dbg (hcd->self.controller,
- "shutdown urb %pK ep%d%s-%s\n",
+ "shutdown urb %p ep%d%s-%s\n",
urb, usb_endpoint_num(&ep->desc),
is_in ? "in" : "out",
usb_ep_type_string(usb_endpoint_type(&ep->desc)));
@@ -2768,14 +2768,14 @@ static void usb_stop_hcd(struct usb_hcd *hcd)
{
hcd->rh_pollable = 0;
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
hcd->driver->stop(hcd);
hcd->state = HC_STATE_HALT;
/* In case the HCD restarted the timer, stop it again. */
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
}
/**
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 145787c424e0..9f19fc7494e0 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -18,6 +18,7 @@
#include <linux/sched/mm.h>
#include <linux/list.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/kcov.h>
#include <linux/ioctl.h>
#include <linux/usb.h>
@@ -1384,7 +1385,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
}
/* Stop hub_wq and related activity */
- del_timer_sync(&hub->irq_urb_retry);
+ timer_delete_sync(&hub->irq_urb_retry);
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
@@ -1496,7 +1497,7 @@ static int hub_configure(struct usb_hub *hub,
maxchild = hub->descriptor->bNbrPorts;
dev_info(hub_dev, "%d port%s detected\n", maxchild,
- (maxchild == 1) ? "" : "s");
+ str_plural(maxchild));
hub->ports = kcalloc(maxchild, sizeof(struct usb_port *), GFP_KERNEL);
if (!hub->ports) {
@@ -4150,14 +4151,14 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev,
break;
default:
dev_warn(&udev->dev, "%s: Can't %s non-U1 or U2 state.\n",
- __func__, enable ? "enable" : "disable");
+ __func__, str_enable_disable(enable));
return -EINVAL;
}
if (udev->state != USB_STATE_CONFIGURED) {
dev_dbg(&udev->dev, "%s: Can't %s %s state "
"for unconfigured device.\n",
- __func__, enable ? "enable" : "disable",
+ __func__, str_enable_disable(enable),
usb3_lpm_names[state]);
return 0;
}
@@ -4183,8 +4184,7 @@ static int usb_set_device_initiated_lpm(struct usb_device *udev,
}
if (ret < 0) {
dev_warn(&udev->dev, "%s of device-initiated %s failed.\n",
- enable ? "Enable" : "Disable",
- usb3_lpm_names[state]);
+ str_enable_disable(enable), usb3_lpm_names[state]);
return -EBUSY;
}
return 0;
@@ -4708,8 +4708,6 @@ void usb_ep0_reinit(struct usb_device *udev)
}
EXPORT_SYMBOL_GPL(usb_ep0_reinit);
-#define usb_sndaddr0pipe() (PIPE_CONTROL << 30)
-
static int hub_set_address(struct usb_device *udev, int devnum)
{
int retval;
@@ -4733,7 +4731,7 @@ static int hub_set_address(struct usb_device *udev, int devnum)
if (hcd->driver->address_device)
retval = hcd->driver->address_device(hcd, udev, timeout_ms);
else
- retval = usb_control_msg(udev, usb_sndaddr0pipe(),
+ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_ADDRESS, 0, devnum, 0,
NULL, 0, timeout_ms);
if (retval == 0) {
@@ -6135,6 +6133,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
struct usb_hub *parent_hub;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
struct usb_device_descriptor descriptor;
+ struct usb_interface *intf;
struct usb_host_bos *bos;
int i, j, ret = 0;
int port1 = udev->portnum;
@@ -6192,6 +6191,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (!udev->actconfig)
goto done;
+ /*
+ * Some devices can't handle setting default altsetting 0 with a
+ * Set-Interface request. Disable host-side endpoints of those
+ * interfaces here. Enable and reset them back after host has set
+ * its internal endpoint structures during usb_hcd_alloc_bandwith()
+ */
+ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ intf = udev->actconfig->interface[i];
+ if (intf->cur_altsetting->desc.bAlternateSetting == 0)
+ usb_disable_interface(udev, intf, true);
+ }
+
mutex_lock(hcd->bandwidth_mutex);
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
if (ret < 0) {
@@ -6223,12 +6234,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
*/
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
struct usb_host_config *config = udev->actconfig;
- struct usb_interface *intf = config->interface[i];
struct usb_interface_descriptor *desc;
+ intf = config->interface[i];
desc = &intf->cur_altsetting->desc;
if (desc->bAlternateSetting == 0) {
- usb_disable_interface(udev, intf, true);
usb_enable_interface(udev, intf, true);
ret = 0;
} else {
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index e857e532b35a..f54198171b6a 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -9,6 +9,7 @@
#include <linux/kstrtox.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/sysfs.h>
#include <linux/pm_qos.h>
#include <linux/component.h>
@@ -25,7 +26,7 @@ static ssize_t early_stop_show(struct device *dev,
{
struct usb_port *port_dev = to_usb_port(dev);
- return sysfs_emit(buf, "%s\n", port_dev->early_stop ? "yes" : "no");
+ return sysfs_emit(buf, "%s\n", str_yes_no(port_dev->early_stop));
}
static ssize_t early_stop_store(struct device *dev, struct device_attribute *attr,
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 6926bd639ec6..53d68d20fb62 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -369,6 +369,12 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0781, 0x5583), .driver_info = USB_QUIRK_NO_LPM },
{ USB_DEVICE(0x0781, 0x5591), .driver_info = USB_QUIRK_NO_LPM },
+ /* SanDisk Corp. SanDisk 3.2Gen1 */
+ { USB_DEVICE(0x0781, 0x55a3), .driver_info = USB_QUIRK_DELAY_INIT },
+
+ /* SanDisk Extreme 55AE */
+ { USB_DEVICE(0x0781, 0x55ae), .driver_info = USB_QUIRK_NO_LPM },
+
/* Realforce 87U Keyboard */
{ USB_DEVICE(0x0853, 0x011b), .driver_info = USB_QUIRK_NO_LPM },
@@ -383,6 +389,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0904, 0x6103), .driver_info =
USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+ /* Silicon Motion Flash Drive */
+ { USB_DEVICE(0x090c, 0x1000), .driver_info = USB_QUIRK_DELAY_INIT },
+
/* Sound Devices USBPre2 */
{ USB_DEVICE(0x0926, 0x0202), .driver_info =
USB_QUIRK_ENDPOINT_IGNORE },
@@ -398,6 +407,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Kingston DataTraveler 3.0 */
{ USB_DEVICE(0x0951, 0x1666), .driver_info = USB_QUIRK_NO_LPM },
+ /* TOSHIBA TransMemory-Mx */
+ { USB_DEVICE(0x0930, 0x1408), .driver_info = USB_QUIRK_NO_LPM },
+
/* NVIDIA Jetson devices in Force Recovery mode */
{ USB_DEVICE(0x0955, 0x7018), .driver_info = USB_QUIRK_RESET_RESUME },
{ USB_DEVICE(0x0955, 0x7019), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -536,6 +548,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x2040, 0x7200), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
+ /* VLI disk */
+ { USB_DEVICE(0x2109, 0x0711), .driver_info = USB_QUIRK_NO_LPM },
+
/* Raydium Touchscreen */
{ USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM },
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index b4cba23831ac..23f3cb1989f4 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -854,7 +854,7 @@ static const struct attribute_group dev_string_attr_grp = {
static ssize_t
descriptors_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr,
+ const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -890,11 +890,11 @@ descriptors_read(struct file *filp, struct kobject *kobj,
}
return count - nleft;
}
-static BIN_ATTR_RO(descriptors, 18 + 65535); /* dev descr + max-size raw descriptor */
+static const BIN_ATTR_RO(descriptors, 18 + 65535); /* dev descr + max-size raw descriptor */
static ssize_t
bos_descriptors_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *attr,
+ const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -913,12 +913,12 @@ bos_descriptors_read(struct file *filp, struct kobject *kobj,
}
return n;
}
-static BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */
+static const BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */
/* When modifying this list, be sure to modify dev_bin_attrs_are_visible()
* accordingly.
*/
-static struct bin_attribute *dev_bin_attrs[] = {
+static const struct bin_attribute *const dev_bin_attrs[] = {
&bin_attr_descriptors,
&bin_attr_bos_descriptors,
NULL
@@ -944,7 +944,7 @@ static umode_t dev_bin_attrs_are_visible(struct kobject *kobj,
}
static const struct attribute_group dev_bin_attr_grp = {
- .bin_attrs = dev_bin_attrs,
+ .bin_attrs_new = dev_bin_attrs,
.is_bin_visible = dev_bin_attrs_are_visible,
};
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 7576920e2d5a..5e52a35486af 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -376,7 +376,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
if (!urb || !urb->complete)
return -EINVAL;
if (urb->hcpriv) {
- WARN_ONCE(1, "URB %pK submitted while active\n", urb);
+ WARN_ONCE(1, "URB %p submitted while active\n", urb);
return -EBUSY;
}
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 03c22114214b..ea1ce8beb0cb 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -165,6 +165,8 @@ static int usb_acpi_add_usb4_devlink(struct usb_device *udev)
return 0;
hub = usb_hub_to_struct_hub(udev->parent);
+ if (!hub)
+ return 0;
port_dev = hub->ports[udev->portnum - 1];
struct fwnode_handle *nhi_fwnode __free(fwnode_handle) =
@@ -213,8 +215,7 @@ usb_acpi_get_connect_type(struct usb_port *port_dev, acpi_handle *handle)
* no connectable, the port would be not used.
*/
- status = acpi_get_physical_device_location(handle, &pld);
- if (ACPI_SUCCESS(status) && pld)
+ if (acpi_get_physical_device_location(handle, &pld) && pld)
port_dev->location = USB_ACPI_LOCATION_VALID |
pld->group_token << 8 | pld->group_position;
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index 9919ab725d54..c3d24312db0f 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -43,6 +43,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
/* Backup global regs */
gr = &hsotg->gr_backup;
+ gr->gintsts = dwc2_readl(hsotg, GINTSTS);
gr->gotgctl = dwc2_readl(hsotg, GOTGCTL);
gr->gintmsk = dwc2_readl(hsotg, GINTMSK);
gr->gahbcfg = dwc2_readl(hsotg, GAHBCFG);
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 2bd74f3033ed..34127b890b2a 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -667,6 +667,7 @@ struct dwc2_hw_params {
/**
* struct dwc2_gregs_backup - Holds global registers state before
* entering partial power down
+ * @gintsts: Backup of GINTSTS register
* @gotgctl: Backup of GOTGCTL register
* @gintmsk: Backup of GINTMSK register
* @gahbcfg: Backup of GAHBCFG register
@@ -683,6 +684,7 @@ struct dwc2_hw_params {
* @valid: True if registers values backuped.
*/
struct dwc2_gregs_backup {
+ u32 gintsts;
u32 gotgctl;
u32 gintmsk;
u32 gahbcfg;
@@ -1127,6 +1129,9 @@ struct dwc2_hsotg {
#define DWC2_FS_IOT_ID 0x55310000
#define DWC2_HS_IOT_ID 0x55320000
+#define DWC2_RESTORE_DCTL BIT(0)
+#define DWC2_RESTORE_DCFG BIT(1)
+
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags {
u32 d32;
@@ -1420,7 +1425,7 @@ int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
#define dwc2_is_device_enabled(hsotg) (hsotg->enabled)
int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg);
-int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup);
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, unsigned int flags);
int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg);
int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
int rem_wakeup, int reset);
@@ -1435,6 +1440,9 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg);
int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg);
void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg);
void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg);
+int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg);
+int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
+ unsigned int flags);
static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg)
{ hsotg->fifo_map = 0; }
#else
@@ -1459,7 +1467,7 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg,
- int remote_wakeup)
+ unsigned int flags)
{ return 0; }
static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
{ return 0; }
@@ -1482,6 +1490,11 @@ static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg)
{ return 0; }
static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {}
static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {}
+static inline int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
+ unsigned int flags)
+{ return 0; }
static inline void dwc2_clear_fifo_map(struct dwc2_hsotg *hsotg) {}
#endif
@@ -1505,6 +1518,8 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
void dwc2_host_enter_clock_gating(struct dwc2_hsotg *hsotg);
void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg, int rem_wakeup);
bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2);
+int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg);
+int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg);
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg)
{ schedule_work(&hsotg->phy_reset_work); }
#else
@@ -1544,6 +1559,10 @@ static inline void dwc2_host_exit_clock_gating(struct dwc2_hsotg *hsotg,
int rem_wakeup) {}
static inline bool dwc2_host_can_poweroff_phy(struct dwc2_hsotg *dwc2)
{ return false; }
+static inline int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg)
+{ return 0; }
static inline void dwc2_host_schedule_phy_reset(struct dwc2_hsotg *hsotg) {}
#endif
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index bd4c788f03bc..300ea4969f0c 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -5204,11 +5204,11 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
- * @remote_wakeup: Indicates whether resume is initiated by Device or Host.
+ * @flags: Defines which registers should be restored.
*
* Return: 0 if successful, negative error code otherwise
*/
-int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
+int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, unsigned int flags)
{
struct dwc2_dregs_backup *dr;
int i;
@@ -5224,7 +5224,10 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup)
}
dr->valid = false;
- if (!remote_wakeup)
+ if (flags & DWC2_RESTORE_DCFG)
+ dwc2_writel(hsotg, dr->dcfg, DCFG);
+
+ if (flags & DWC2_RESTORE_DCTL)
dwc2_writel(hsotg, dr->dctl, DCTL);
dwc2_writel(hsotg, dr->daintmsk, DAINTMSK);
@@ -5310,6 +5313,49 @@ void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK));
}
+int dwc2_gadget_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ /* Backup all registers */
+ ret = dwc2_backup_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup global registers\n",
+ __func__);
+ return ret;
+ }
+
+ ret = dwc2_backup_device_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup device registers\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int dwc2_gadget_restore_critical_registers(struct dwc2_hsotg *hsotg,
+ unsigned int flags)
+{
+ int ret;
+
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+ ret = dwc2_restore_device_registers(hsotg, flags);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore device registers\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
/**
* dwc2_gadget_enter_hibernation() - Put controller in Hibernation.
*
@@ -5327,18 +5373,9 @@ int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg)
/* Change to L2(suspend) state */
hsotg->lx_state = DWC2_L2;
dev_dbg(hsotg->dev, "Start of hibernation completed\n");
- ret = dwc2_backup_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup global registers\n",
- __func__);
- return ret;
- }
- ret = dwc2_backup_device_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup device registers\n",
- __func__);
+ ret = dwc2_gadget_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
gpwrdn = GPWRDN_PWRDNRSTN;
udelay(10);
@@ -5415,6 +5452,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
u32 gpwrdn;
u32 dctl;
int ret = 0;
+ unsigned int flags = 0;
struct dwc2_gregs_backup *gr;
struct dwc2_dregs_backup *dr;
@@ -5477,6 +5515,7 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
dctl = dwc2_readl(hsotg, DCTL);
dctl |= DCTL_PWRONPRGDONE;
dwc2_writel(hsotg, dctl, DCTL);
+ flags |= DWC2_RESTORE_DCTL;
}
/* Wait for interrupts which must be cleared */
mdelay(2);
@@ -5484,20 +5523,9 @@ int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg,
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Restore global registers */
- ret = dwc2_restore_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
- return ret;
- }
-
- /* Restore device registers */
- ret = dwc2_restore_device_registers(hsotg, rem_wakeup);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore device registers\n",
- __func__);
+ ret = dwc2_gadget_restore_critical_registers(hsotg, flags);
+ if (ret)
return ret;
- }
if (rem_wakeup) {
mdelay(10);
@@ -5531,19 +5559,9 @@ int dwc2_gadget_enter_partial_power_down(struct dwc2_hsotg *hsotg)
dev_dbg(hsotg->dev, "Entering device partial power down started.\n");
/* Backup all registers */
- ret = dwc2_backup_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup global registers\n",
- __func__);
- return ret;
- }
-
- ret = dwc2_backup_device_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup device registers\n",
- __func__);
+ ret = dwc2_gadget_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
/*
* Clear any pending interrupts since dwc2 will not be able to
@@ -5590,11 +5608,8 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
{
u32 pcgcctl;
u32 dctl;
- struct dwc2_dregs_backup *dr;
int ret = 0;
- dr = &hsotg->dr_backup;
-
dev_dbg(hsotg->dev, "Exiting device partial Power Down started.\n");
pcgcctl = dwc2_readl(hsotg, PCGCTL);
@@ -5611,21 +5626,10 @@ int dwc2_gadget_exit_partial_power_down(struct dwc2_hsotg *hsotg,
udelay(100);
if (restore) {
- ret = dwc2_restore_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
- return ret;
- }
- /* Restore DCFG */
- dwc2_writel(hsotg, dr->dcfg, DCFG);
-
- ret = dwc2_restore_device_registers(hsotg, 0);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore device registers\n",
- __func__);
+ ret = dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL |
+ DWC2_RESTORE_DCFG);
+ if (ret)
return ret;
- }
}
/* Set the Power-On Programming done bit */
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 8c3941ecaaf5..60ef8092259a 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -5081,7 +5081,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
cancel_work_sync(&hsotg->phy_reset_work);
- del_timer(&hsotg->wkp_timer);
+ timer_delete(&hsotg->wkp_timer);
}
static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
@@ -5474,6 +5474,49 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
return 0;
}
+int dwc2_host_backup_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ /* Backup all registers */
+ ret = dwc2_backup_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup global registers\n",
+ __func__);
+ return ret;
+ }
+
+ ret = dwc2_backup_host_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to backup host registers\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+int dwc2_host_restore_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ int ret;
+
+ ret = dwc2_restore_global_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore registers\n",
+ __func__);
+ return ret;
+ }
+
+ ret = dwc2_restore_host_registers(hsotg);
+ if (ret) {
+ dev_err(hsotg->dev, "%s: failed to restore host registers\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
/**
* dwc2_host_enter_hibernation() - Put controller in Hibernation.
*
@@ -5489,18 +5532,9 @@ int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg)
u32 gpwrdn;
dev_dbg(hsotg->dev, "Preparing host for hibernation\n");
- ret = dwc2_backup_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup global registers\n",
- __func__);
- return ret;
- }
- ret = dwc2_backup_host_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup host registers\n",
- __func__);
+ ret = dwc2_host_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
/* Enter USB Suspend Mode */
hprt0 = dwc2_readl(hsotg, HPRT0);
@@ -5694,20 +5728,9 @@ int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup,
dwc2_writel(hsotg, 0xffffffff, GINTSTS);
/* Restore global registers */
- ret = dwc2_restore_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
+ ret = dwc2_host_restore_critical_registers(hsotg);
+ if (ret)
return ret;
- }
-
- /* Restore host registers */
- ret = dwc2_restore_host_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore host registers\n",
- __func__);
- return ret;
- }
if (rem_wakeup) {
dwc2_hcd_rem_wakeup(hsotg);
@@ -5774,19 +5797,9 @@ int dwc2_host_enter_partial_power_down(struct dwc2_hsotg *hsotg)
dev_warn(hsotg->dev, "Suspend wasn't generated\n");
/* Backup all registers */
- ret = dwc2_backup_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup global registers\n",
- __func__);
- return ret;
- }
-
- ret = dwc2_backup_host_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to backup host registers\n",
- __func__);
+ ret = dwc2_host_backup_critical_registers(hsotg);
+ if (ret)
return ret;
- }
/*
* Clear any pending interrupts since dwc2 will not be able to
@@ -5855,19 +5868,9 @@ int dwc2_host_exit_partial_power_down(struct dwc2_hsotg *hsotg,
udelay(100);
if (restore) {
- ret = dwc2_restore_global_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore registers\n",
- __func__);
- return ret;
- }
-
- ret = dwc2_restore_host_registers(hsotg);
- if (ret) {
- dev_err(hsotg->dev, "%s: failed to restore host registers\n",
- __func__);
+ ret = dwc2_host_restore_critical_registers(hsotg);
+ if (ret)
return ret;
- }
}
/* Drive resume signaling and exit suspend mode on the port. */
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 238c6fd50e75..b0098792dd22 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -1302,7 +1302,7 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
}
/* Cancel pending unreserve; if canceled OK, unreserve was pending */
- if (del_timer(&qh->unreserve_timer))
+ if (timer_delete(&qh->unreserve_timer))
WARN_ON(!qh->unreserve_pending);
/*
@@ -1459,8 +1459,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
/* Initialize QH */
qh->hsotg = hsotg;
timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0);
- hrtimer_init(&qh->wait_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- qh->wait_timer.function = &dwc2_wait_timer_fn;
+ hrtimer_setup(&qh->wait_timer, &dwc2_wait_timer_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
qh->ep_type = ep_type;
qh->ep_is_in = ep_is_in;
@@ -1615,7 +1614,7 @@ struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
/* Make sure any unreserve work is finished. */
- if (del_timer_sync(&qh->unreserve_timer)) {
+ if (timer_delete_sync(&qh->unreserve_timer)) {
unsigned long flags;
spin_lock_irqsave(&hsotg->lock, flags);
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 91c80a92d9b8..12b4dc07d08a 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -685,6 +685,14 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
regulator_disable(dwc2->usb33d);
}
+ if (is_device_mode)
+ ret = dwc2_gadget_backup_critical_registers(dwc2);
+ else
+ ret = dwc2_host_backup_critical_registers(dwc2);
+
+ if (ret)
+ return ret;
+
if (dwc2->ll_hw_enabled &&
(is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
ret = __dwc2_lowlevel_hw_disable(dwc2);
@@ -694,6 +702,24 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
return ret;
}
+static int dwc2_restore_critical_registers(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_gregs_backup *gr;
+
+ gr = &hsotg->gr_backup;
+
+ if (!gr->valid) {
+ dev_err(hsotg->dev, "No valid register backup, failed to restore\n");
+ return -EINVAL;
+ }
+
+ if (gr->gintsts & GINTSTS_CURMODE_HOST)
+ return dwc2_host_restore_critical_registers(hsotg);
+
+ return dwc2_gadget_restore_critical_registers(hsotg, DWC2_RESTORE_DCTL |
+ DWC2_RESTORE_DCFG);
+}
+
static int __maybe_unused dwc2_resume(struct device *dev)
{
struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
@@ -706,6 +732,18 @@ static int __maybe_unused dwc2_resume(struct device *dev)
}
dwc2->phy_off_for_suspend = false;
+ /*
+ * During suspend it's possible that the power domain for the
+ * DWC2 controller is disabled and all register values get lost.
+ * In case the GUSBCFG register is not initialized, it's clear the
+ * registers must be restored.
+ */
+ if (!(dwc2_readl(dwc2, GUSBCFG) & GUSBCFG_TOUTCAL_MASK)) {
+ ret = dwc2_restore_critical_registers(dwc2);
+ if (ret)
+ return ret;
+ }
+
if (dwc2->params.activate_stm_id_vb_detection) {
unsigned long flags;
u32 ggpio, gotgctl;
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index b751904f09f3..66a08b527165 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1494,6 +1494,26 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
}
+ /*
+ * STAR 9001346572: This issue affects DWC_usb31 versions 1.80a and
+ * prior. When an active endpoint not currently cached in the host
+ * controller is chosen to be cached to the same index as an endpoint
+ * receiving NAKs, the endpoint receiving NAKs enters continuous
+ * retry mode. This prevents it from being evicted from the host
+ * controller cache, blocking the new endpoint from being cached and
+ * serviced.
+ *
+ * To resolve this, for controller versions 1.70a and 1.80a, set the
+ * GUCTL3 bit[16] (USB2.0 Internal Retry Disable) to 1. This bit
+ * disables the USB2.0 internal retry feature. The GUCTL3[16] register
+ * function is available only from version 1.70a.
+ */
+ if (DWC3_VER_IS_WITHIN(DWC31, 170A, 180A)) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);
+ reg |= DWC3_GUCTL3_USB20_RETRY_DISABLE;
+ dwc3_writel(dwc->regs, DWC3_GUCTL3, reg);
+ }
+
return 0;
err_power_off_phy:
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index ecadedd2fc0b..27eae4cf223d 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -425,6 +425,7 @@
/* Global User Control Register 3 */
#define DWC3_GUCTL3_SPLITDISABLE BIT(14)
+#define DWC3_GUCTL3_USB20_RETRY_DISABLE BIT(16)
/* Device Configuration Register */
#define DWC3_DCFG_NUMLANES(n) (((n) & 0x3) << 30) /* DWC_usb32 only */
@@ -716,6 +717,7 @@ struct dwc3_event_buffer {
/**
* struct dwc3_ep - device side endpoint representation
* @endpoint: usb endpoint
+ * @nostream_work: work for handling bulk NoStream
* @cancelled_list: list of cancelled requests for this endpoint
* @pending_list: list of pending requests for this endpoint
* @started_list: list of started requests on this endpoint
@@ -742,6 +744,7 @@ struct dwc3_event_buffer {
*/
struct dwc3_ep {
struct usb_ep endpoint;
+ struct delayed_work nostream_work;
struct list_head cancelled_list;
struct list_head pending_list;
struct list_head started_list;
@@ -764,7 +767,7 @@ struct dwc3_ep {
#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
-#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
+#define DWC3_EP_STREAM_PRIMED BIT(10)
#define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
#define DWC3_EP_TXFIFO_RESIZED BIT(12)
#define DWC3_EP_DELAY_STOP BIT(13)
@@ -957,7 +960,6 @@ struct dwc3_request {
struct usb_request request;
struct list_head list;
struct dwc3_ep *dep;
- struct scatterlist *sg;
struct scatterlist *start_sg;
unsigned int num_pending_sgs;
@@ -1162,6 +1164,9 @@ struct dwc3_scratchpad_array {
* @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
* DATWRREQINFO, and DESWRREQINFO value passed from
* glue driver.
+ * @wakeup_pending_funcs: Indicates whether any interface has requested for
+ * function wakeup in bitmap format where bit position
+ * represents interface_id.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1392,6 +1397,7 @@ struct dwc3 {
int num_ep_resized;
struct dentry *debug_root;
u32 gsbuscfg0_reqinfo;
+ u32 wakeup_pending_funcs;
};
#define INCRX_BURST_MODE 0
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index d102506f9471..9db8f3ca493d 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -108,6 +108,9 @@
#define DWC3_AM62_AUTOSUSPEND_DELAY 100
+#define USBSS_DEBUG_CFG_OFF 0x0
+#define USBSS_DEBUG_CFG_DISABLED 0x7
+
struct dwc3_am62 {
struct device *dev;
void __iomem *usbss;
@@ -117,6 +120,7 @@ struct dwc3_am62 {
unsigned int offset;
unsigned int vbus_divider;
u32 wakeup_stat;
+ void __iomem *phy_regs;
};
static const int dwc3_ti_rate_table[] = { /* in KHZ */
@@ -149,11 +153,11 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
{
struct device *dev = am62->dev;
struct device_node *node = dev->of_node;
- struct of_phandle_args args;
struct regmap *syscon;
int ret;
- syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-phy-pll-refclk");
+ syscon = syscon_regmap_lookup_by_phandle_args(node, "ti,syscon-phy-pll-refclk",
+ 1, &am62->offset);
if (IS_ERR(syscon)) {
dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n");
return PTR_ERR(syscon);
@@ -161,14 +165,6 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
am62->syscon = syscon;
- ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1,
- 0, &args);
- if (ret)
- return ret;
-
- of_node_put(args.np);
- am62->offset = args.args[0];
-
/* Core voltage. PHY_CORE_VOLTAGE bit Recommended to be 0 always */
ret = regmap_update_bits(am62->syscon, am62->offset, PHY_CORE_VOLTAGE_MASK, 0);
if (ret) {
@@ -185,15 +181,47 @@ static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
return 0;
}
+static int dwc3_ti_init(struct dwc3_am62 *am62)
+{
+ int ret;
+ u32 reg;
+
+ /* Read the syscon property and set the rate code */
+ ret = phy_syscon_pll_refclk(am62);
+ if (ret)
+ return ret;
+
+ /* Workaround Errata i2409 */
+ if (am62->phy_regs) {
+ reg = readl(am62->phy_regs + USB_PHY_PLL_REG12);
+ reg |= USB_PHY_PLL_LDO_REF_EN | USB_PHY_PLL_LDO_REF_EN_EN;
+ writel(reg, am62->phy_regs + USB_PHY_PLL_REG12);
+ }
+
+ /* VBUS divider select */
+ reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
+ if (am62->vbus_divider)
+ reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
+
+ dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
+
+ clk_prepare_enable(am62->usb2_refclk);
+
+ /* Set mode valid bit to indicate role is valid */
+ reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
+ reg |= USBSS_MODE_VALID;
+ dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
+
+ return 0;
+}
+
static int dwc3_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
struct dwc3_am62 *am62;
unsigned long rate;
- void __iomem *phy;
int i, ret;
- u32 reg;
am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL);
if (!am62)
@@ -229,29 +257,17 @@ static int dwc3_ti_probe(struct platform_device *pdev)
am62->rate_code = i;
- /* Read the syscon property and set the rate code */
- ret = phy_syscon_pll_refclk(am62);
- if (ret)
- return ret;
-
- /* Workaround Errata i2409 */
- phy = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(phy)) {
+ am62->phy_regs = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(am62->phy_regs)) {
dev_err(dev, "can't map PHY IOMEM resource. Won't apply i2409 fix.\n");
- phy = NULL;
- } else {
- reg = readl(phy + USB_PHY_PLL_REG12);
- reg |= USB_PHY_PLL_LDO_REF_EN | USB_PHY_PLL_LDO_REF_EN_EN;
- writel(reg, phy + USB_PHY_PLL_REG12);
+ am62->phy_regs = NULL;
}
- /* VBUS divider select */
am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
- reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
- if (am62->vbus_divider)
- reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
- dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
+ ret = dwc3_ti_init(am62);
+ if (ret)
+ return ret;
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -259,7 +275,6 @@ static int dwc3_ti_probe(struct platform_device *pdev)
* Don't ignore its dependencies with its children
*/
pm_suspend_ignore_children(dev, false);
- clk_prepare_enable(am62->usb2_refclk);
pm_runtime_get_noresume(dev);
ret = of_platform_populate(node, NULL, NULL, dev);
@@ -268,11 +283,6 @@ static int dwc3_ti_probe(struct platform_device *pdev)
goto err_pm_disable;
}
- /* Set mode valid bit to indicate role is valid */
- reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
- reg |= USBSS_MODE_VALID;
- dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
-
/* Device has capability to wakeup system from sleep */
device_set_wakeup_capable(dev, true);
ret = device_wakeup_enable(dev);
@@ -340,6 +350,9 @@ static int dwc3_ti_suspend_common(struct device *dev)
dwc3_ti_writel(am62, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
}
+ /* just to track if module resets on suspend */
+ dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_DISABLED);
+
clk_disable_unprepare(am62->usb2_refclk);
return 0;
@@ -350,7 +363,14 @@ static int dwc3_ti_resume_common(struct device *dev)
struct dwc3_am62 *am62 = dev_get_drvdata(dev);
u32 reg;
- clk_prepare_enable(am62->usb2_refclk);
+ reg = dwc3_ti_readl(am62, USBSS_DEBUG_CFG);
+ if (reg != USBSS_DEBUG_CFG_DISABLED) {
+ /* lost power/context */
+ dwc3_ti_init(am62);
+ } else {
+ dwc3_ti_writel(am62, USBSS_DEBUG_CFG, USBSS_DEBUG_CFG_OFF);
+ clk_prepare_enable(am62->usb2_refclk);
+ }
if (device_may_wakeup(dev)) {
/* Clear wakeup config enable bits */
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index f5d963fae9e0..de686b9e6404 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -163,6 +163,12 @@ static const struct dwc3_exynos_driverdata exynos7_drvdata = {
.suspend_clk_idx = 1,
};
+static const struct dwc3_exynos_driverdata exynos7870_drvdata = {
+ .clk_names = { "bus_early", "ref", "ctrl" },
+ .num_clks = 3,
+ .suspend_clk_idx = -1,
+};
+
static const struct dwc3_exynos_driverdata exynos850_drvdata = {
.clk_names = { "bus_early", "ref" },
.num_clks = 2,
@@ -186,6 +192,9 @@ static const struct of_device_id exynos_dwc3_match[] = {
.compatible = "samsung,exynos7-dwusb3",
.data = &exynos7_drvdata,
}, {
+ .compatible = "samsung,exynos7870-dwusb3",
+ .data = &exynos7870_drvdata,
+ }, {
.compatible = "samsung,exynos850-dwusb3",
.data = &exynos850_drvdata,
}, {
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index b261c46124c6..fe74d11bb629 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -457,7 +457,7 @@ static int dwc3_omap_probe(struct platform_device *pdev)
struct dwc3_omap *omap;
struct device *dev = &pdev->dev;
- struct regulator *vbus_reg = NULL;
+ struct regulator *vbus_reg;
int ret;
int irq;
@@ -483,12 +483,11 @@ static int dwc3_omap_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- if (of_property_read_bool(node, "vbus-supply")) {
- vbus_reg = devm_regulator_get(dev, "vbus");
- if (IS_ERR(vbus_reg)) {
- dev_err(dev, "vbus init failed\n");
- return PTR_ERR(vbus_reg);
- }
+ vbus_reg = devm_regulator_get_optional(dev, "vbus");
+ if (IS_ERR(vbus_reg)) {
+ if (PTR_ERR(vbus_reg) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(vbus_reg), "vbus init failed\n");
+ vbus_reg = NULL;
}
omap->dev = dev;
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 052852f80146..54a4ee2b90b7 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -148,11 +148,21 @@ static const struct property_entry dwc3_pci_intel_byt_properties[] = {
{}
};
+/*
+ * Intel Merrifield SoC uses these endpoints for tracing and they cannot
+ * be re-allocated if being used because the side band flow control signals
+ * are hard wired to certain endpoints:
+ * - 1 High BW Bulk IN (IN#1) (RTIT)
+ * - 1 1KB BW Bulk IN (IN#8) + 1 1KB BW Bulk OUT (Run Control) (OUT#8)
+ */
+static const u8 dwc3_pci_mrfld_reserved_endpoints[] = { 3, 16, 17 };
+
static const struct property_entry dwc3_pci_mrfld_properties[] = {
PROPERTY_ENTRY_STRING("dr_mode", "otg"),
PROPERTY_ENTRY_STRING("linux,extcon-name", "mrfld_bcove_pwrsrc"),
PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
+ PROPERTY_ENTRY_U8_ARRAY("snps,reserved-endpoints", dwc3_pci_mrfld_reserved_endpoints),
PROPERTY_ENTRY_BOOL("snps,usb2-gadget-lpm-disable"),
PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"),
{}
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
index e16c3237180e..5d513decaacd 100644
--- a/drivers/usb/dwc3/dwc3-st.c
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -225,7 +225,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
dwc3_data->syscfg_reg_off = res->start;
- dev_vdbg(dev, "glue-logic addr 0x%pK, syscfg-reg offset 0x%x\n",
+ dev_vdbg(dev, "glue-logic addr 0x%p, syscfg-reg offset 0x%x\n",
dwc3_data->glue_base, dwc3_data->syscfg_reg_off);
struct device_node *child __free(device_node) = of_get_compatible_child(node,
@@ -309,7 +309,6 @@ static void st_dwc3_remove(struct platform_device *pdev)
reset_control_assert(dwc3_data->rstc_rst);
}
-#ifdef CONFIG_PM_SLEEP
static int st_dwc3_suspend(struct device *dev)
{
struct st_dwc3 *dwc3_data = dev_get_drvdata(dev);
@@ -343,9 +342,8 @@ static int st_dwc3_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
-static SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(st_dwc3_dev_pm_ops, st_dwc3_suspend, st_dwc3_resume);
static const struct of_device_id st_dwc3_match[] = {
{ .compatible = "st,stih407-dwc3" },
@@ -360,7 +358,7 @@ static struct platform_driver st_dwc3_driver = {
.driver = {
.name = "usb-st-dwc3",
.of_match_table = st_dwc3_match,
- .pm = &st_dwc3_dev_pm_ops,
+ .pm = pm_sleep_ptr(&st_dwc3_dev_pm_ops),
},
};
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index a33a42ba0249..4ca7f6240d07 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -207,15 +207,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
skip_usb3_phy:
/* ulpi reset via gpio-modepin or gpio-framework driver */
- reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset_gpio)) {
return dev_err_probe(dev, PTR_ERR(reset_gpio),
"Failed to request reset GPIO\n");
}
if (reset_gpio) {
- /* Toggle ulpi to reset the phy. */
- gpiod_set_value_cansleep(reset_gpio, 1);
usleep_range(5000, 10000);
gpiod_set_value_cansleep(reset_gpio, 0);
usleep_range(5000, 10000);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 535b73217715..321361288935 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -276,8 +276,6 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
return ret;
}
-static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async);
-
/**
* dwc3_send_gadget_ep_cmd - issue an endpoint command
* @dep: the endpoint to which the command is going to be issued
@@ -547,6 +545,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
{
struct dwc3_gadget_ep_cmd_params params;
+ struct dwc3_ep *dep;
u32 cmd;
int i;
int ret;
@@ -563,8 +562,13 @@ int dwc3_gadget_start_config(struct dwc3 *dwc, unsigned int resource_index)
return ret;
/* Reset resource allocation flags */
- for (i = resource_index; i < dwc->num_eps && dwc->eps[i]; i++)
- dwc->eps[i]->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
+ for (i = resource_index; i < dwc->num_eps; i++) {
+ dep = dwc->eps[i];
+ if (!dep)
+ continue;
+
+ dep->flags &= ~DWC3_EP_RESOURCE_ALLOCATED;
+ }
return 0;
}
@@ -751,9 +755,11 @@ void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
dwc->last_fifo_depth = fifo_depth;
/* Clear existing TXFIFO for all IN eps except ep0 */
- for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM);
- num += 2) {
+ for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM); num += 2) {
dep = dwc->eps[num];
+ if (!dep)
+ continue;
+
/* Don't change TXFRAMNUM on usb31 version */
size = DWC3_IP_IS(DWC3) ? 0 :
dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
@@ -996,8 +1002,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
/*
* All stream eps will reinitiate stream on NoStream
- * rejection until we can determine that the host can
- * prime after the first transfer.
+ * rejection.
*
* However, if the controller is capable of
* TXF_FLUSH_BYPASS, then IN direction endpoints will
@@ -1972,12 +1977,12 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
return -ESHUTDOWN;
}
- if (WARN(req->dep != dep, "request %pK belongs to '%s'\n",
+ if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
&req->request, req->dep->name))
return -EINVAL;
if (WARN(req->status < DWC3_REQUEST_STATUS_COMPLETED,
- "%s: request %pK already in flight\n",
+ "%s: request %p already in flight\n",
dep->name, &req->request))
return -EINVAL;
@@ -2166,7 +2171,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
}
}
- dev_err(dwc->dev, "request %pK was not queued to %s\n",
+ dev_err(dwc->dev, "request %p was not queued to %s\n",
request, ep->name);
ret = -EINVAL;
out:
@@ -2352,10 +2357,8 @@ static int dwc3_gadget_get_frame(struct usb_gadget *g)
return __dwc3_gadget_get_frame(dwc);
}
-static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
+static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
{
- int retries;
-
int ret;
u32 reg;
@@ -2383,8 +2386,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
return -EINVAL;
}
- if (async)
- dwc3_gadget_enable_linksts_evts(dwc, true);
+ dwc3_gadget_enable_linksts_evts(dwc, true);
ret = dwc3_gadget_set_link_state(dwc, DWC3_LINK_STATE_RECOV);
if (ret < 0) {
@@ -2403,27 +2405,8 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc, bool async)
/*
* Since link status change events are enabled we will receive
- * an U0 event when wakeup is successful. So bail out.
+ * an U0 event when wakeup is successful.
*/
- if (async)
- return 0;
-
- /* poll until Link State changes to ON */
- retries = 20000;
-
- while (retries--) {
- reg = dwc3_readl(dwc->regs, DWC3_DSTS);
-
- /* in HS, means ON */
- if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0)
- break;
- }
-
- if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) {
- dev_err(dwc->dev, "failed to send remote wakeup\n");
- return -EINVAL;
- }
-
return 0;
}
@@ -2444,7 +2427,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
spin_unlock_irqrestore(&dwc->lock, flags);
return -EINVAL;
}
- ret = __dwc3_gadget_wakeup(dwc, true);
+ ret = __dwc3_gadget_wakeup(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2472,14 +2455,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
*/
link_state = dwc3_gadget_get_link_state(dwc);
if (link_state == DWC3_LINK_STATE_U3) {
- ret = __dwc3_gadget_wakeup(dwc, false);
- if (ret) {
- spin_unlock_irqrestore(&dwc->lock, flags);
- return -EINVAL;
- }
- dwc3_resume_gadget(dwc);
- dwc->suspended = false;
- dwc->link_state = DWC3_LINK_STATE_U0;
+ dwc->wakeup_pending_funcs |= BIT(intf_id);
+ ret = __dwc3_gadget_wakeup(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return ret;
}
ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
@@ -2774,6 +2753,8 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
__dwc3_gadget_stop(dwc);
spin_unlock_irqrestore(&dwc->lock, flags);
+ usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
+
return ret;
}
@@ -3332,6 +3313,50 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
return dwc3_alloc_trb_pool(dep);
}
+#define nostream_work_to_dep(w) (container_of(to_delayed_work(w), struct dwc3_ep, nostream_work))
+static void dwc3_nostream_work(struct work_struct *work)
+{
+ struct dwc3_ep *dep = nostream_work_to_dep(work);
+ struct dwc3 *dwc = dep->dwc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ if (dep->flags & DWC3_EP_STREAM_PRIMED)
+ goto out;
+
+ if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
+ (!DWC3_MST_CAPABLE(&dwc->hwparams) &&
+ !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
+ goto out;
+ /*
+ * If the host rejects a stream due to no active stream, by the
+ * USB and xHCI spec, the endpoint will be put back to idle
+ * state. When the host is ready (buffer added/updated), it will
+ * prime the endpoint to inform the usb device controller. This
+ * triggers the device controller to issue ERDY to restart the
+ * stream. However, some hosts don't follow this and keep the
+ * endpoint in the idle state. No prime will come despite host
+ * streams are updated, and the device controller will not be
+ * triggered to generate ERDY to move the next stream data. To
+ * workaround this and maintain compatibility with various
+ * hosts, force to reinitiate the stream until the host is ready
+ * instead of waiting for the host to prime the endpoint.
+ */
+ if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
+ unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
+
+ dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
+ } else {
+ dep->flags |= DWC3_EP_DELAY_START;
+ dwc3_stop_active_transfer(dep, true, true);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return;
+ }
+out:
+ dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
{
struct dwc3_ep *dep;
@@ -3377,20 +3402,60 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
INIT_LIST_HEAD(&dep->pending_list);
INIT_LIST_HEAD(&dep->started_list);
INIT_LIST_HEAD(&dep->cancelled_list);
+ INIT_DELAYED_WORK(&dep->nostream_work, dwc3_nostream_work);
dwc3_debugfs_create_endpoint_dir(dep);
return 0;
}
+static int dwc3_gadget_get_reserved_endpoints(struct dwc3 *dwc, const char *propname,
+ u8 *eps, u8 num)
+{
+ u8 count;
+ int ret;
+
+ if (!device_property_present(dwc->dev, propname))
+ return 0;
+
+ ret = device_property_count_u8(dwc->dev, propname);
+ if (ret < 0)
+ return ret;
+ count = ret;
+
+ ret = device_property_read_u8_array(dwc->dev, propname, eps, min(num, count));
+ if (ret)
+ return ret;
+
+ return count;
+}
+
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
{
+ const char *propname = "snps,reserved-endpoints";
u8 epnum;
+ u8 reserved_eps[DWC3_ENDPOINTS_NUM];
+ u8 count;
+ u8 num;
+ int ret;
INIT_LIST_HEAD(&dwc->gadget->ep_list);
+ ret = dwc3_gadget_get_reserved_endpoints(dwc, propname,
+ reserved_eps, ARRAY_SIZE(reserved_eps));
+ if (ret < 0) {
+ dev_err(dwc->dev, "failed to read %s\n", propname);
+ return ret;
+ }
+ count = ret;
+
for (epnum = 0; epnum < total; epnum++) {
- int ret;
+ for (num = 0; num < count; num++) {
+ if (epnum == reserved_eps[num])
+ break;
+ }
+ if (num < count)
+ continue;
ret = dwc3_gadget_init_endpoint(dwc, epnum);
if (ret)
@@ -3657,6 +3722,8 @@ out:
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
dep = dwc->eps[i];
+ if (!dep)
+ continue;
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
@@ -3776,66 +3843,27 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
- struct dwc3 *dwc = dep->dwc;
-
if (event->status == DEPEVT_STREAMEVT_FOUND) {
- dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
- goto out;
+ cancel_delayed_work(&dep->nostream_work);
+ dep->flags |= DWC3_EP_STREAM_PRIMED;
+ dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
+ return;
}
/* Note: NoStream rejection event param value is 0 and not 0xFFFF */
switch (event->parameters) {
case DEPEVT_STREAM_PRIME:
- /*
- * If the host can properly transition the endpoint state from
- * idle to prime after a NoStream rejection, there's no need to
- * force restarting the endpoint to reinitiate the stream. To
- * simplify the check, assume the host follows the USB spec if
- * it primed the endpoint more than once.
- */
- if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) {
- if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED)
- dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM;
- else
- dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
- }
-
+ cancel_delayed_work(&dep->nostream_work);
+ dep->flags |= DWC3_EP_STREAM_PRIMED;
+ dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
break;
case DEPEVT_STREAM_NOSTREAM:
- if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
- !(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
- (!DWC3_MST_CAPABLE(&dwc->hwparams) &&
- !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)))
- break;
-
- /*
- * If the host rejects a stream due to no active stream, by the
- * USB and xHCI spec, the endpoint will be put back to idle
- * state. When the host is ready (buffer added/updated), it will
- * prime the endpoint to inform the usb device controller. This
- * triggers the device controller to issue ERDY to restart the
- * stream. However, some hosts don't follow this and keep the
- * endpoint in the idle state. No prime will come despite host
- * streams are updated, and the device controller will not be
- * triggered to generate ERDY to move the next stream data. To
- * workaround this and maintain compatibility with various
- * hosts, force to reinitiate the stream until the host is ready
- * instead of waiting for the host to prime the endpoint.
- */
- if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
- unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
-
- dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
- } else {
- dep->flags |= DWC3_EP_DELAY_START;
- dwc3_stop_active_transfer(dep, true, true);
- return;
- }
+ dep->flags &= ~DWC3_EP_STREAM_PRIMED;
+ if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM)
+ queue_delayed_work(system_wq, &dep->nostream_work,
+ msecs_to_jiffies(100));
break;
}
-
-out:
- dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
@@ -3845,6 +3873,10 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
u8 epnum = event->endpoint_number;
dep = dwc->eps[epnum];
+ if (!dep) {
+ dev_warn(dwc->dev, "spurious event, endpoint %u is not allocated\n", epnum);
+ return;
+ }
if (!(dep->flags & DWC3_EP_ENABLED)) {
if ((epnum > 1) && !(dep->flags & DWC3_EP_TRANSFER_STARTED))
@@ -4293,6 +4325,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
{
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
unsigned int pwropt;
+ int ret;
+ int intf_id;
/*
* WORKAROUND: DWC3 < 2.50a have an issue when configured without
@@ -4368,7 +4402,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
switch (next) {
case DWC3_LINK_STATE_U0:
- if (dwc->gadget->wakeup_armed) {
+ if (dwc->gadget->wakeup_armed || dwc->wakeup_pending_funcs) {
dwc3_gadget_enable_linksts_evts(dwc, false);
dwc3_resume_gadget(dwc);
dwc->suspended = false;
@@ -4391,6 +4425,18 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
}
dwc->link_state = next;
+
+ /* Proceed with func wakeup if any interfaces that has requested */
+ while (dwc->wakeup_pending_funcs && (next == DWC3_LINK_STATE_U0)) {
+ intf_id = ffs(dwc->wakeup_pending_funcs) - 1;
+ ret = dwc3_send_gadget_generic_command(dwc, DWC3_DGCMD_DEV_NOTIFICATION,
+ DWC3_DGCMDPAR_DN_FUNC_WAKE |
+ DWC3_DGCMDPAR_INTF_SEL(intf_id));
+ if (ret)
+ dev_err(dwc->dev, "Failed to send DN wake for intf %d\n", intf_id);
+
+ dwc->wakeup_pending_funcs &= ~BIT(intf_id);
+ }
}
static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
@@ -4557,6 +4603,12 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
if (!count)
return IRQ_NONE;
+ if (count > evt->length) {
+ dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
+ count, evt->length);
+ return IRQ_NONE;
+ }
+
evt->count = count;
evt->flags |= DWC3_EVENT_PENDING;
diff --git a/drivers/usb/fotg210/fotg210-core.c b/drivers/usb/fotg210/fotg210-core.c
index 49f25a70b32e..7fb4d4715e9f 100644
--- a/drivers/usb/fotg210/fotg210-core.c
+++ b/drivers/usb/fotg210/fotg210-core.c
@@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/string_choices.h>
#include <linux/usb.h>
#include <linux/usb/otg.h>
@@ -119,8 +120,8 @@ void fotg210_vbus(struct fotg210 *fotg, bool enable)
ret = regmap_update_bits(fotg->map, GEMINI_GLOBAL_MISC_CTRL, mask, val);
if (ret)
dev_err(fotg->dev, "failed to %s VBUS\n",
- enable ? "enable" : "disable");
- dev_info(fotg->dev, "%s: %s VBUS\n", __func__, enable ? "enable" : "disable");
+ str_enable_disable(enable));
+ dev_info(fotg->dev, "%s: %s VBUS\n", __func__, str_enable_disable(enable));
}
static int fotg210_probe(struct platform_device *pdev)
diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c
index 3d404d19a205..64c4965a160f 100644
--- a/drivers/usb/fotg210/fotg210-hcd.c
+++ b/drivers/usb/fotg210/fotg210-hcd.c
@@ -4901,8 +4901,7 @@ static int hcd_fotg210_init(struct usb_hcd *hcd)
*/
fotg210->need_io_watchdog = 1;
- hrtimer_init(&fotg210->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
- fotg210->hrtimer.function = fotg210_hrtimer_func;
+ hrtimer_setup(&fotg210->hrtimer, fotg210_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
fotg210->next_hrtimer_event = FOTG210_HRTIMER_NO_EVENT;
hcc_params = fotg210_readl(fotg210, &fotg210->caps->hcc_params);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 869ad99afb48..8dbc132a505e 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -2011,15 +2011,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
if (f->get_status) {
status = f->get_status(f);
+
if (status < 0)
break;
- } else {
- /* Set D0 and D1 bits based on func wakeup capability */
- if (f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP) {
- status |= USB_INTRF_STAT_FUNC_RW_CAP;
- if (f->func_wakeup_armed)
- status |= USB_INTRF_STAT_FUNC_RW;
- }
+
+ /* if D5 is not set, then device is not wakeup capable */
+ if (!(f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP))
+ status &= ~(USB_INTRF_STAT_FUNC_RW_CAP | USB_INTRF_STAT_FUNC_RW);
}
put_unaligned_le16(status & 0x0000ffff, req->buf);
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index 6cb7771e8a69..027226325039 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
+#include <linux/string_choices.h>
#include "u_ether.h"
#include "u_ether_configfs.h"
@@ -387,8 +388,7 @@ static void ecm_do_notify(struct f_ecm *ecm)
event->wLength = 0;
req->length = sizeof *event;
- DBG(cdev, "notify connect %s\n",
- ecm->is_open ? "true" : "false");
+ DBG(cdev, "notify connect %s\n", str_true_false(ecm->is_open));
ecm->notify_state = ECM_NOTIFY_SPEED;
break;
@@ -892,6 +892,12 @@ static void ecm_resume(struct usb_function *f)
gether_resume(&ecm->port);
}
+static int ecm_get_status(struct usb_function *f)
+{
+ return (f->func_wakeup_armed ? USB_INTRF_STAT_FUNC_RW : 0) |
+ USB_INTRF_STAT_FUNC_RW_CAP;
+}
+
static void ecm_free(struct usb_function *f)
{
struct f_ecm *ecm;
@@ -960,6 +966,7 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
ecm->port.func.disable = ecm_disable;
ecm->port.func.free_func = ecm_free;
ecm->port.func.suspend = ecm_suspend;
+ ecm->port.func.get_status = ecm_get_status;
ecm->port.func.resume = ecm_resume;
return &ecm->port.func;
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 740311c4fa24..c7a05f842745 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -144,8 +144,8 @@ static struct hid_descriptor hidg_desc = {
.bcdHID = cpu_to_le16(0x0101),
.bCountryCode = 0x00,
.bNumDescriptors = 0x1,
- /*.desc[0].bDescriptorType = DYNAMIC */
- /*.desc[0].wDescriptorLenght = DYNAMIC */
+ /*.rpt_desc.bDescriptorType = DYNAMIC */
+ /*.rpt_desc.wDescriptorLength = DYNAMIC */
};
/* Super-Speed Support */
@@ -939,8 +939,8 @@ static int hidg_setup(struct usb_function *f,
struct hid_descriptor hidg_desc_copy = hidg_desc;
VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
- hidg_desc_copy.desc[0].bDescriptorType = HID_DT_REPORT;
- hidg_desc_copy.desc[0].wDescriptorLength =
+ hidg_desc_copy.rpt_desc.bDescriptorType = HID_DT_REPORT;
+ hidg_desc_copy.rpt_desc.wDescriptorLength =
cpu_to_le16(hidg->report_desc_length);
length = min_t(unsigned short, length,
@@ -1210,8 +1210,8 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
* We can use hidg_desc struct here but we should not relay
* that its content won't change after returning from this function.
*/
- hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
- hidg_desc.desc[0].wDescriptorLength =
+ hidg_desc.rpt_desc.bDescriptorType = HID_DT_REPORT;
+ hidg_desc.rpt_desc.wDescriptorLength =
cpu_to_le16(hidg->report_desc_length);
hidg_hs_in_ep_desc.bEndpointAddress =
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 2eae8fc2e0db..94d478b6bcd3 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2142,8 +2142,8 @@ static int do_scsi_command(struct fsg_common *common)
* of Posix locks.
*/
case FORMAT_UNIT:
- case RELEASE:
- case RESERVE:
+ case RELEASE_6:
+ case RESERVE_6:
case SEND_DIAGNOSTIC:
default:
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 12e866fb311d..0a800ba53816 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -475,7 +475,7 @@ static void reply_ump_stream_ep_info(struct f_midi2_ep *ep)
/* reply a UMP EP device info */
static void reply_ump_stream_ep_device(struct f_midi2_ep *ep)
{
- struct snd_ump_stream_msg_devince_info rep = {
+ struct snd_ump_stream_msg_device_info rep = {
.type = UMP_MSG_TYPE_STREAM,
.status = UMP_STREAM_MSG_STATUS_DEVICE_INFO,
.manufacture_id = ep->info.manufacturer,
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 8e761249d672..58b0dd575af3 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/crc32.h>
+#include <linux/string_choices.h>
#include <linux/usb/cdc.h>
@@ -558,7 +559,7 @@ static void ncm_do_notify(struct f_ncm *ncm)
req->length = sizeof *event;
DBG(cdev, "notify connect %s\n",
- ncm->is_open ? "true" : "false");
+ str_true_false(ncm->is_open));
ncm->notify_state = NCM_NOTIFY_NONE;
break;
@@ -1558,8 +1559,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm->port.open = ncm_open;
ncm->port.close = ncm_close;
- hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
- ncm->task_timer.function = ncm_tx_timeout;
+ hrtimer_setup(&ncm->task_timer, ncm_tx_timeout, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
ncm->port.in_ep->name, ncm->port.out_ep->name,
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 7b23631f4744..5a2e1237f85c 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -12,6 +12,7 @@
#include <linux/string.h>
#include <linux/configfs.h>
#include <linux/ctype.h>
+#include <linux/delay.h>
#include <linux/usb/ch9.h>
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
@@ -50,7 +51,7 @@ static int bot_enqueue_cmd_cbw(struct f_uas *fu)
if (fu->flags & USBG_BOT_CMD_PEND)
return 0;
- ret = usb_ep_queue(fu->ep_out, fu->cmd.req, GFP_ATOMIC);
+ ret = usb_ep_queue(fu->ep_out, fu->cmd[0].req, GFP_ATOMIC);
if (!ret)
fu->flags |= USBG_BOT_CMD_PEND;
return ret;
@@ -62,10 +63,11 @@ static void bot_status_complete(struct usb_ep *ep, struct usb_request *req)
struct f_uas *fu = cmd->fu;
transport_generic_free_cmd(&cmd->se_cmd, 0);
- if (req->status < 0) {
- pr_err("ERR %s(%d)\n", __func__, __LINE__);
+ if (req->status == -ESHUTDOWN)
return;
- }
+
+ if (req->status < 0)
+ pr_err("ERR %s(%d)\n", __func__, __LINE__);
/* CSW completed, wait for next CBW */
bot_enqueue_cmd_cbw(fu);
@@ -136,7 +138,7 @@ static void bot_send_bad_status(struct usbg_cmd *cmd)
}
req->complete = bot_err_compl;
req->context = cmd;
- req->buf = fu->cmd.buf;
+ req->buf = fu->cmd[0].buf;
usb_ep_queue(ep, req, GFP_KERNEL);
} else {
bot_enqueue_sense_code(fu, cmd);
@@ -196,6 +198,11 @@ static void bot_read_compl(struct usb_ep *ep, struct usb_request *req)
if (req->status < 0)
pr_err("ERR %s(%d)\n", __func__, __LINE__);
+ if (req->status == -ESHUTDOWN) {
+ transport_generic_free_cmd(&cmd->se_cmd, 0);
+ return;
+ }
+
bot_send_status(cmd, true);
}
@@ -244,10 +251,8 @@ static int usbg_prepare_w_request(struct usbg_cmd *, struct usb_request *);
static int bot_send_write_request(struct usbg_cmd *cmd)
{
struct f_uas *fu = cmd->fu;
- struct se_cmd *se_cmd = &cmd->se_cmd;
int ret;
- init_completion(&cmd->write_complete);
cmd->fu = fu;
if (!cmd->data_len) {
@@ -262,8 +267,6 @@ static int bot_send_write_request(struct usbg_cmd *cmd)
if (ret)
pr_err("%s(%d)\n", __func__, __LINE__);
- wait_for_completion(&cmd->write_complete);
- target_execute_cmd(se_cmd);
cleanup:
return ret;
}
@@ -275,14 +278,31 @@ static void bot_cmd_complete(struct usb_ep *ep, struct usb_request *req)
struct f_uas *fu = req->context;
int ret;
+ if (req->status == -ESHUTDOWN)
+ return;
+
fu->flags &= ~USBG_BOT_CMD_PEND;
- if (req->status < 0)
+ if (req->status < 0) {
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+
+ dev_err(&gadget->dev, "BOT command req err (%d)\n", req->status);
+ bot_enqueue_cmd_cbw(fu);
return;
+ }
ret = bot_submit_command(fu, req->buf, req->actual);
- if (ret)
+ if (ret) {
pr_err("%s(%d): %d\n", __func__, __LINE__, ret);
+ if (!(fu->flags & USBG_BOT_WEDGED))
+ usb_ep_set_wedge(fu->ep_in);
+
+ fu->flags |= USBG_BOT_WEDGED;
+ bot_enqueue_cmd_cbw(fu);
+ } else if (fu->flags & USBG_BOT_WEDGED) {
+ fu->flags &= ~USBG_BOT_WEDGED;
+ usb_ep_clear_halt(fu->ep_in);
+ }
}
static int bot_prepare_reqs(struct f_uas *fu)
@@ -297,8 +317,8 @@ static int bot_prepare_reqs(struct f_uas *fu)
if (!fu->bot_req_out)
goto err_out;
- fu->cmd.req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
- if (!fu->cmd.req)
+ fu->cmd[0].req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
+ if (!fu->cmd[0].req)
goto err_cmd;
fu->bot_status.req = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
@@ -310,27 +330,27 @@ static int bot_prepare_reqs(struct f_uas *fu)
fu->bot_status.req->complete = bot_status_complete;
fu->bot_status.csw.Signature = cpu_to_le32(US_BULK_CS_SIGN);
- fu->cmd.buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL);
- if (!fu->cmd.buf)
+ fu->cmd[0].buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL);
+ if (!fu->cmd[0].buf)
goto err_buf;
- fu->cmd.req->complete = bot_cmd_complete;
- fu->cmd.req->buf = fu->cmd.buf;
- fu->cmd.req->length = fu->ep_out->maxpacket;
- fu->cmd.req->context = fu;
+ fu->cmd[0].req->complete = bot_cmd_complete;
+ fu->cmd[0].req->buf = fu->cmd[0].buf;
+ fu->cmd[0].req->length = fu->ep_out->maxpacket;
+ fu->cmd[0].req->context = fu;
ret = bot_enqueue_cmd_cbw(fu);
if (ret)
goto err_queue;
return 0;
err_queue:
- kfree(fu->cmd.buf);
- fu->cmd.buf = NULL;
+ kfree(fu->cmd[0].buf);
+ fu->cmd[0].buf = NULL;
err_buf:
usb_ep_free_request(fu->ep_in, fu->bot_status.req);
err_sts:
- usb_ep_free_request(fu->ep_out, fu->cmd.req);
- fu->cmd.req = NULL;
+ usb_ep_free_request(fu->ep_out, fu->cmd[0].req);
+ fu->cmd[0].req = NULL;
err_cmd:
usb_ep_free_request(fu->ep_out, fu->bot_req_out);
fu->bot_req_out = NULL;
@@ -355,16 +375,16 @@ static void bot_cleanup_old_alt(struct f_uas *fu)
usb_ep_free_request(fu->ep_in, fu->bot_req_in);
usb_ep_free_request(fu->ep_out, fu->bot_req_out);
- usb_ep_free_request(fu->ep_out, fu->cmd.req);
+ usb_ep_free_request(fu->ep_out, fu->cmd[0].req);
usb_ep_free_request(fu->ep_in, fu->bot_status.req);
- kfree(fu->cmd.buf);
+ kfree(fu->cmd[0].buf);
fu->bot_req_in = NULL;
fu->bot_req_out = NULL;
- fu->cmd.req = NULL;
+ fu->cmd[0].req = NULL;
fu->bot_status.req = NULL;
- fu->cmd.buf = NULL;
+ fu->cmd[0].buf = NULL;
}
static void bot_set_alt(struct f_uas *fu)
@@ -424,14 +444,10 @@ static int usbg_bot_setup(struct usb_function *f,
pr_err("No LUNs configured?\n");
return -EINVAL;
}
- /*
- * If 4 LUNs are present we return 3 i.e. LUN 0..3 can be
- * accessed. The upper limit is 0xf
- */
luns--;
- if (luns > 0xf) {
+ if (luns > US_BULK_MAX_LUN_LIMIT) {
pr_info_once("Limiting the number of luns to 16\n");
- luns = 0xf;
+ luns = US_BULK_MAX_LUN_LIMIT;
}
ret_lun = cdev->req->buf;
*ret_lun = luns;
@@ -440,6 +456,11 @@ static int usbg_bot_setup(struct usb_function *f,
case US_BULK_RESET_REQUEST:
/* XXX maybe we should remove previous requests for IN + OUT */
+ if (fu->flags & USBG_BOT_WEDGED) {
+ fu->flags &= ~USBG_BOT_WEDGED;
+ usb_ep_clear_halt(fu->ep_in);
+ }
+
bot_enqueue_cmd_cbw(fu);
return 0;
}
@@ -448,6 +469,45 @@ static int usbg_bot_setup(struct usb_function *f,
/* Start uas.c code */
+static int tcm_to_uasp_response(enum tcm_tmrsp_table code)
+{
+ switch (code) {
+ case TMR_FUNCTION_FAILED:
+ return RC_TMF_FAILED;
+ case TMR_FUNCTION_COMPLETE:
+ case TMR_TASK_DOES_NOT_EXIST:
+ return RC_TMF_COMPLETE;
+ case TMR_LUN_DOES_NOT_EXIST:
+ return RC_INCORRECT_LUN;
+ case TMR_FUNCTION_REJECTED:
+ case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
+ default:
+ return RC_TMF_NOT_SUPPORTED;
+ }
+}
+
+static unsigned char uasp_to_tcm_func(int code)
+{
+ switch (code) {
+ case TMF_ABORT_TASK:
+ return TMR_ABORT_TASK;
+ case TMF_ABORT_TASK_SET:
+ return TMR_ABORT_TASK_SET;
+ case TMF_CLEAR_TASK_SET:
+ return TMR_CLEAR_TASK_SET;
+ case TMF_LOGICAL_UNIT_RESET:
+ return TMR_LUN_RESET;
+ case TMF_CLEAR_ACA:
+ return TMR_CLEAR_ACA;
+ case TMF_I_T_NEXUS_RESET:
+ case TMF_QUERY_TASK:
+ case TMF_QUERY_TASK_SET:
+ case TMF_QUERY_ASYNC_EVENT:
+ default:
+ return TMR_UNKNOWN;
+ }
+}
+
static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream)
{
/* We have either all three allocated or none */
@@ -465,10 +525,14 @@ static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream)
static void uasp_free_cmdreq(struct f_uas *fu)
{
- usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
- kfree(fu->cmd.buf);
- fu->cmd.req = NULL;
- fu->cmd.buf = NULL;
+ int i;
+
+ for (i = 0; i < USBG_NUM_CMDS; i++) {
+ usb_ep_free_request(fu->ep_cmd, fu->cmd[i].req);
+ kfree(fu->cmd[i].buf);
+ fu->cmd[i].req = NULL;
+ fu->cmd[i].buf = NULL;
+ }
}
static void uasp_cleanup_old_alt(struct f_uas *fu)
@@ -483,7 +547,7 @@ static void uasp_cleanup_old_alt(struct f_uas *fu)
usb_ep_disable(fu->ep_status);
usb_ep_disable(fu->ep_cmd);
- for (i = 0; i < UASP_SS_EP_COMP_NUM_STREAMS; i++)
+ for (i = 0; i < USBG_NUM_CMDS; i++)
uasp_cleanup_one_stream(fu, &fu->stream[i]);
uasp_free_cmdreq(fu);
}
@@ -495,7 +559,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd)
struct se_cmd *se_cmd = &cmd->se_cmd;
struct f_uas *fu = cmd->fu;
struct usb_gadget *gadget = fuas_to_gadget(fu);
- struct uas_stream *stream = cmd->stream;
+ struct uas_stream *stream = &fu->stream[se_cmd->map_tag];
if (!gadget->sg_supported) {
cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
@@ -515,6 +579,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd)
}
stream->req_in->is_last = 1;
+ stream->req_in->stream_id = cmd->tag;
stream->req_in->complete = uasp_status_data_cmpl;
stream->req_in->length = se_cmd->data_length;
stream->req_in->context = cmd;
@@ -527,7 +592,7 @@ static void uasp_prepare_status(struct usbg_cmd *cmd)
{
struct se_cmd *se_cmd = &cmd->se_cmd;
struct sense_iu *iu = &cmd->sense_iu;
- struct uas_stream *stream = cmd->stream;
+ struct uas_stream *stream = &cmd->fu->stream[se_cmd->map_tag];
cmd->state = UASP_QUEUE_COMMAND;
iu->iu_id = IU_ID_STATUS;
@@ -539,20 +604,76 @@ static void uasp_prepare_status(struct usbg_cmd *cmd)
iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
iu->status = se_cmd->scsi_status;
stream->req_status->is_last = 1;
+ stream->req_status->stream_id = cmd->tag;
stream->req_status->context = cmd;
stream->req_status->length = se_cmd->scsi_sense_length + 16;
stream->req_status->buf = iu;
stream->req_status->complete = uasp_status_data_cmpl;
}
+static void uasp_prepare_response(struct usbg_cmd *cmd)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct response_iu *rsp_iu = &cmd->response_iu;
+ struct uas_stream *stream = &cmd->fu->stream[se_cmd->map_tag];
+
+ cmd->state = UASP_QUEUE_COMMAND;
+ rsp_iu->iu_id = IU_ID_RESPONSE;
+ rsp_iu->tag = cpu_to_be16(cmd->tag);
+
+ if (cmd->tmr_rsp != RC_RESPONSE_UNKNOWN)
+ rsp_iu->response_code = cmd->tmr_rsp;
+ else
+ rsp_iu->response_code =
+ tcm_to_uasp_response(se_cmd->se_tmr_req->response);
+
+ /*
+ * The UASP driver must support all the task management functions listed
+ * in Table 20 of UAS-r04. To remain compliant while indicate that the
+ * TMR did not go through, report RC_TMF_FAILED instead of
+ * RC_TMF_NOT_SUPPORTED and print a warning to the user.
+ */
+ switch (cmd->tmr_func) {
+ case TMF_ABORT_TASK:
+ case TMF_ABORT_TASK_SET:
+ case TMF_CLEAR_TASK_SET:
+ case TMF_LOGICAL_UNIT_RESET:
+ case TMF_CLEAR_ACA:
+ case TMF_I_T_NEXUS_RESET:
+ case TMF_QUERY_TASK:
+ case TMF_QUERY_TASK_SET:
+ case TMF_QUERY_ASYNC_EVENT:
+ if (rsp_iu->response_code == RC_TMF_NOT_SUPPORTED) {
+ struct usb_gadget *gadget = fuas_to_gadget(cmd->fu);
+
+ dev_warn(&gadget->dev, "TMF function %d not supported\n",
+ cmd->tmr_func);
+ rsp_iu->response_code = RC_TMF_FAILED;
+ }
+ break;
+ default:
+ break;
+ }
+
+ stream->req_status->is_last = 1;
+ stream->req_status->stream_id = cmd->tag;
+ stream->req_status->context = cmd;
+ stream->req_status->length = sizeof(struct response_iu);
+ stream->req_status->buf = rsp_iu;
+ stream->req_status->complete = uasp_status_data_cmpl;
+}
+
+static void usbg_release_cmd(struct se_cmd *se_cmd);
+static int uasp_send_tm_response(struct usbg_cmd *cmd);
+
static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
{
struct usbg_cmd *cmd = req->context;
- struct uas_stream *stream = cmd->stream;
struct f_uas *fu = cmd->fu;
+ struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag];
int ret;
- if (req->status < 0)
+ if (req->status == -ESHUTDOWN)
goto cleanup;
switch (cmd->state) {
@@ -583,8 +704,37 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
break;
case UASP_QUEUE_COMMAND:
- transport_generic_free_cmd(&cmd->se_cmd, 0);
- usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+ /*
+ * Overlapped command detected and cancelled.
+ * So send overlapped attempted status.
+ */
+ if (cmd->tmr_rsp == RC_OVERLAPPED_TAG &&
+ req->status == -ECONNRESET) {
+ uasp_send_tm_response(cmd);
+ return;
+ }
+
+ hash_del(&stream->node);
+
+ /*
+ * If no command submitted to target core here, just free the
+ * bitmap index. This is for the cases where f_tcm handles
+ * status response instead of the target core.
+ */
+ if (cmd->tmr_rsp != RC_OVERLAPPED_TAG &&
+ cmd->tmr_rsp != RC_RESPONSE_UNKNOWN) {
+ struct se_session *se_sess;
+
+ se_sess = fu->tpg->tpg_nexus->tvn_se_sess;
+ sbitmap_queue_clear(&se_sess->sess_tag_pool,
+ cmd->se_cmd.map_tag,
+ cmd->se_cmd.map_cpu);
+ } else {
+ transport_generic_free_cmd(&cmd->se_cmd, 0);
+ }
+
+ usb_ep_queue(fu->ep_cmd, cmd->req, GFP_ATOMIC);
+ complete(&stream->cmd_completion);
break;
default:
@@ -593,27 +743,38 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
return;
cleanup:
+ hash_del(&stream->node);
transport_generic_free_cmd(&cmd->se_cmd, 0);
}
static int uasp_send_status_response(struct usbg_cmd *cmd)
{
struct f_uas *fu = cmd->fu;
- struct uas_stream *stream = cmd->stream;
+ struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag];
struct sense_iu *iu = &cmd->sense_iu;
iu->tag = cpu_to_be16(cmd->tag);
- stream->req_status->complete = uasp_status_data_cmpl;
- stream->req_status->context = cmd;
cmd->fu = fu;
uasp_prepare_status(cmd);
return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC);
}
+static int uasp_send_tm_response(struct usbg_cmd *cmd)
+{
+ struct f_uas *fu = cmd->fu;
+ struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag];
+ struct response_iu *iu = &cmd->response_iu;
+
+ iu->tag = cpu_to_be16(cmd->tag);
+ cmd->fu = fu;
+ uasp_prepare_response(cmd);
+ return usb_ep_queue(fu->ep_status, stream->req_status, GFP_ATOMIC);
+}
+
static int uasp_send_read_response(struct usbg_cmd *cmd)
{
struct f_uas *fu = cmd->fu;
- struct uas_stream *stream = cmd->stream;
+ struct uas_stream *stream = &fu->stream[cmd->se_cmd.map_tag];
struct sense_iu *iu = &cmd->sense_iu;
int ret;
@@ -657,11 +818,10 @@ static int uasp_send_write_request(struct usbg_cmd *cmd)
{
struct f_uas *fu = cmd->fu;
struct se_cmd *se_cmd = &cmd->se_cmd;
- struct uas_stream *stream = cmd->stream;
+ struct uas_stream *stream = &fu->stream[se_cmd->map_tag];
struct sense_iu *iu = &cmd->sense_iu;
int ret;
- init_completion(&cmd->write_complete);
cmd->fu = fu;
iu->tag = cpu_to_be16(cmd->tag);
@@ -693,36 +853,31 @@ static int uasp_send_write_request(struct usbg_cmd *cmd)
pr_err("%s(%d)\n", __func__, __LINE__);
}
- wait_for_completion(&cmd->write_complete);
- target_execute_cmd(se_cmd);
cleanup:
return ret;
}
-static int usbg_submit_command(struct f_uas *, void *, unsigned int);
+static int usbg_submit_command(struct f_uas *, struct usb_request *);
static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_uas *fu = req->context;
- int ret;
- if (req->status < 0)
+ if (req->status == -ESHUTDOWN)
return;
- ret = usbg_submit_command(fu, req->buf, req->actual);
- /*
- * Once we tune for performance enqueue the command req here again so
- * we can receive a second command while we processing this one. Pay
- * attention to properly sync STAUS endpoint with DATA IN + OUT so you
- * don't break HS.
- */
- if (!ret)
+ if (req->status < 0) {
+ usb_ep_queue(fu->ep_cmd, req, GFP_ATOMIC);
return;
- usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+ }
+
+ usbg_submit_command(fu, req);
}
static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream)
{
+ init_completion(&stream->cmd_completion);
+
stream->req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
if (!stream->req_in)
goto out;
@@ -747,66 +902,48 @@ out:
return -ENOMEM;
}
-static int uasp_alloc_cmd(struct f_uas *fu)
+static int uasp_alloc_cmd(struct f_uas *fu, int i)
{
- fu->cmd.req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL);
- if (!fu->cmd.req)
+ fu->cmd[i].req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL);
+ if (!fu->cmd[i].req)
goto err;
- fu->cmd.buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL);
- if (!fu->cmd.buf)
+ fu->cmd[i].buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL);
+ if (!fu->cmd[i].buf)
goto err_buf;
- fu->cmd.req->complete = uasp_cmd_complete;
- fu->cmd.req->buf = fu->cmd.buf;
- fu->cmd.req->length = fu->ep_cmd->maxpacket;
- fu->cmd.req->context = fu;
+ fu->cmd[i].req->complete = uasp_cmd_complete;
+ fu->cmd[i].req->buf = fu->cmd[i].buf;
+ fu->cmd[i].req->length = fu->ep_cmd->maxpacket;
+ fu->cmd[i].req->context = fu;
return 0;
err_buf:
- usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
+ usb_ep_free_request(fu->ep_cmd, fu->cmd[i].req);
err:
return -ENOMEM;
}
-static void uasp_setup_stream_res(struct f_uas *fu, int max_streams)
-{
- int i;
-
- for (i = 0; i < max_streams; i++) {
- struct uas_stream *s = &fu->stream[i];
-
- s->req_in->stream_id = i + 1;
- s->req_out->stream_id = i + 1;
- s->req_status->stream_id = i + 1;
- }
-}
-
static int uasp_prepare_reqs(struct f_uas *fu)
{
int ret;
int i;
- int max_streams;
-
- if (fu->flags & USBG_USE_STREAMS)
- max_streams = UASP_SS_EP_COMP_NUM_STREAMS;
- else
- max_streams = 1;
- for (i = 0; i < max_streams; i++) {
+ for (i = 0; i < USBG_NUM_CMDS; i++) {
ret = uasp_alloc_stream_res(fu, &fu->stream[i]);
if (ret)
goto err_cleanup;
}
- ret = uasp_alloc_cmd(fu);
- if (ret)
- goto err_free_stream;
- uasp_setup_stream_res(fu, max_streams);
+ for (i = 0; i < USBG_NUM_CMDS; i++) {
+ ret = uasp_alloc_cmd(fu, i);
+ if (ret)
+ goto err_free_stream;
- ret = usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
- if (ret)
- goto err_free_stream;
+ ret = usb_ep_queue(fu->ep_cmd, fu->cmd[i].req, GFP_ATOMIC);
+ if (ret)
+ goto err_free_stream;
+ }
return 0;
@@ -897,6 +1034,8 @@ static int get_cmd_dir(const unsigned char *cdb)
case READ_TOC:
case READ_FORMAT_CAPACITIES:
case REQUEST_SENSE:
+ case ATA_12:
+ case ATA_16:
ret = DMA_FROM_DEVICE;
break;
@@ -940,7 +1079,18 @@ static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req)
struct usbg_cmd *cmd = req->context;
struct se_cmd *se_cmd = &cmd->se_cmd;
- if (req->status < 0) {
+ cmd->state = UASP_QUEUE_COMMAND;
+
+ if (req->status == -ESHUTDOWN) {
+ struct uas_stream *stream = &cmd->fu->stream[se_cmd->map_tag];
+
+ hash_del(&stream->node);
+ target_put_sess_cmd(se_cmd);
+ transport_generic_free_cmd(&cmd->se_cmd, 0);
+ return;
+ }
+
+ if (req->status) {
pr_err("%s() state %d transfer failed\n", __func__, cmd->state);
goto cleanup;
}
@@ -952,12 +1102,22 @@ static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req)
se_cmd->data_length);
}
- complete(&cmd->write_complete);
+ cmd->flags |= USBG_CMD_PENDING_DATA_WRITE;
+ queue_work(cmd->fu->tpg->workqueue, &cmd->work);
return;
cleanup:
target_put_sess_cmd(se_cmd);
- transport_generic_free_cmd(&cmd->se_cmd, 0);
+
+ /* Command was aborted due to overlapped tag */
+ if (cmd->state == UASP_QUEUE_COMMAND &&
+ cmd->tmr_rsp == RC_OVERLAPPED_TAG) {
+ uasp_send_tm_response(cmd);
+ return;
+ }
+
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_CHECK_CONDITION_ABORT_CMD, 0);
}
static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
@@ -979,9 +1139,12 @@ static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
}
req->is_last = 1;
+ req->stream_id = cmd->tag;
req->complete = usbg_data_write_cmpl;
req->length = se_cmd->data_length;
req->context = cmd;
+
+ cmd->state = UASP_SEND_STATUS;
return 0;
}
@@ -1021,37 +1184,155 @@ static int usbg_send_read_response(struct se_cmd *se_cmd)
return uasp_send_read_response(cmd);
}
-static void usbg_cmd_work(struct work_struct *work)
+static void usbg_aborted_task(struct se_cmd *se_cmd);
+
+static void usbg_submit_tmr(struct usbg_cmd *cmd)
+{
+ struct se_session *se_sess;
+ struct se_cmd *se_cmd;
+ int flags = TARGET_SCF_ACK_KREF;
+
+ se_cmd = &cmd->se_cmd;
+ se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess;
+
+ target_submit_tmr(se_cmd, se_sess,
+ cmd->response_iu.add_response_info,
+ cmd->unpacked_lun, NULL, uasp_to_tcm_func(cmd->tmr_func),
+ GFP_ATOMIC, cmd->tag, flags);
+}
+
+static void usbg_submit_cmd(struct usbg_cmd *cmd)
{
- struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work);
struct se_cmd *se_cmd;
struct tcm_usbg_nexus *tv_nexus;
struct usbg_tpg *tpg;
int dir, flags = (TARGET_SCF_UNKNOWN_SIZE | TARGET_SCF_ACK_KREF);
+ /*
+ * Note: each command will spawn its own process, and each stage of the
+ * command is processed sequentially. Should this no longer be the case,
+ * locking is needed.
+ */
+ if (cmd->flags & USBG_CMD_PENDING_DATA_WRITE) {
+ target_execute_cmd(&cmd->se_cmd);
+ cmd->flags &= ~USBG_CMD_PENDING_DATA_WRITE;
+ return;
+ }
+
se_cmd = &cmd->se_cmd;
tpg = cmd->fu->tpg;
tv_nexus = tpg->tpg_nexus;
dir = get_cmd_dir(cmd->cmd_buf);
- if (dir < 0) {
- __target_init_cmd(se_cmd,
- tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
- tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
- cmd->prio_attr, cmd->sense_iu.sense,
- cmd->unpacked_lun, NULL);
+ if (dir < 0)
goto out;
- }
target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess, cmd->cmd_buf,
cmd->sense_iu.sense, cmd->unpacked_lun, 0,
cmd->prio_attr, dir, flags);
+
return;
out:
+ __target_init_cmd(se_cmd,
+ tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
+ tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
+ cmd->prio_attr, cmd->sense_iu.sense,
+ cmd->unpacked_lun, NULL);
transport_send_check_condition_and_sense(se_cmd,
TCM_UNSUPPORTED_SCSI_OPCODE, 0);
}
+static void usbg_cmd_work(struct work_struct *work)
+{
+ struct usbg_cmd *cmd = container_of(work, struct usbg_cmd, work);
+
+ /*
+ * Failure is detected by f_tcm here. Skip submitting the command to the
+ * target core if we already know the failing response and send the usb
+ * response to the host directly.
+ */
+ if (cmd->tmr_rsp != RC_RESPONSE_UNKNOWN)
+ goto skip;
+
+ if (cmd->tmr_func)
+ usbg_submit_tmr(cmd);
+ else
+ usbg_submit_cmd(cmd);
+
+ return;
+
+skip:
+ if (cmd->tmr_rsp == RC_OVERLAPPED_TAG) {
+ struct f_uas *fu = cmd->fu;
+ struct se_session *se_sess;
+ struct uas_stream *stream = NULL;
+ struct hlist_node *tmp;
+ struct usbg_cmd *active_cmd = NULL;
+
+ se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess;
+
+ hash_for_each_possible_safe(fu->stream_hash, stream, tmp, node, cmd->tag) {
+ int i = stream - &fu->stream[0];
+
+ active_cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[i];
+ if (active_cmd->tag == cmd->tag)
+ break;
+ }
+
+ /* Sanity check */
+ if (!stream || (active_cmd && active_cmd->tag != cmd->tag)) {
+ usbg_submit_command(cmd->fu, cmd->req);
+ return;
+ }
+
+ reinit_completion(&stream->cmd_completion);
+
+ /*
+ * A UASP command consists of the command, data, and status
+ * stages, each operating sequentially from different endpoints.
+ *
+ * Each USB endpoint operates independently, and depending on
+ * hardware implementation, a completion callback for a transfer
+ * from one endpoint may not reflect the order of completion on
+ * the wire. This is particularly true for devices with
+ * endpoints that have independent interrupts and event buffers.
+ *
+ * The driver must still detect misbehaving hosts and respond
+ * with an overlap status. To reduce false overlap failures,
+ * allow the active and matching stream ID a brief 1ms to
+ * complete before responding with an overlap command failure.
+ * Overlap failure should be rare.
+ */
+ wait_for_completion_timeout(&stream->cmd_completion, msecs_to_jiffies(1));
+
+ /* If the previous stream is completed, retry the command. */
+ if (!hash_hashed(&stream->node)) {
+ usbg_submit_command(cmd->fu, cmd->req);
+ return;
+ }
+
+ /*
+ * The command isn't submitted to the target core, so we're safe
+ * to remove the bitmap index from the session tag pool.
+ */
+ sbitmap_queue_clear(&se_sess->sess_tag_pool,
+ cmd->se_cmd.map_tag,
+ cmd->se_cmd.map_cpu);
+
+ /*
+ * Overlap command tag detected. Cancel any pending transfer of
+ * the command submitted to target core.
+ */
+ active_cmd->tmr_rsp = RC_OVERLAPPED_TAG;
+ usbg_aborted_task(&active_cmd->se_cmd);
+
+ /* Send the response after the transfer is aborted. */
+ return;
+ }
+
+ uasp_send_tm_response(cmd);
+}
+
static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu,
struct tcm_usbg_nexus *tv_nexus, u32 scsi_tag)
{
@@ -1067,6 +1348,7 @@ static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu,
memset(cmd, 0, sizeof(*cmd));
cmd->se_cmd.map_tag = tag;
cmd->se_cmd.map_cpu = cpu;
+ cmd->se_cmd.cpuid = cpu;
cmd->se_cmd.tag = cmd->tag = scsi_tag;
cmd->fu = fu;
@@ -1075,50 +1357,82 @@ static struct usbg_cmd *usbg_get_cmd(struct f_uas *fu,
static void usbg_release_cmd(struct se_cmd *);
-static int usbg_submit_command(struct f_uas *fu,
- void *cmdbuf, unsigned int len)
+static int usbg_submit_command(struct f_uas *fu, struct usb_request *req)
{
- struct command_iu *cmd_iu = cmdbuf;
+ struct iu *iu = req->buf;
struct usbg_cmd *cmd;
struct usbg_tpg *tpg = fu->tpg;
struct tcm_usbg_nexus *tv_nexus;
+ struct uas_stream *stream;
+ struct hlist_node *tmp;
+ struct command_iu *cmd_iu;
u32 cmd_len;
u16 scsi_tag;
- if (cmd_iu->iu_id != IU_ID_COMMAND) {
- pr_err("Unsupported type %d\n", cmd_iu->iu_id);
- return -EINVAL;
- }
-
tv_nexus = tpg->tpg_nexus;
if (!tv_nexus) {
pr_err("Missing nexus, ignoring command\n");
return -EINVAL;
}
- cmd_len = (cmd_iu->len & ~0x3) + 16;
- if (cmd_len > USBG_MAX_CMD)
- return -EINVAL;
-
- scsi_tag = be16_to_cpup(&cmd_iu->tag);
+ scsi_tag = be16_to_cpup(&iu->tag);
cmd = usbg_get_cmd(fu, tv_nexus, scsi_tag);
if (IS_ERR(cmd)) {
pr_err("usbg_get_cmd failed\n");
return -ENOMEM;
}
- memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len);
- if (fu->flags & USBG_USE_STREAMS) {
- if (cmd->tag > UASP_SS_EP_COMP_NUM_STREAMS)
- goto err;
- if (!cmd->tag)
- cmd->stream = &fu->stream[0];
- else
- cmd->stream = &fu->stream[cmd->tag - 1];
- } else {
- cmd->stream = &fu->stream[0];
+ cmd->req = req;
+ cmd->fu = fu;
+ cmd->tag = scsi_tag;
+ cmd->se_cmd.tag = scsi_tag;
+ cmd->tmr_func = 0;
+ cmd->tmr_rsp = RC_RESPONSE_UNKNOWN;
+ cmd->flags = 0;
+
+ cmd_iu = (struct command_iu *)iu;
+
+ /* Command and Task Management IUs share the same LUN offset */
+ cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun);
+
+ if (iu->iu_id != IU_ID_COMMAND && iu->iu_id != IU_ID_TASK_MGMT) {
+ cmd->tmr_rsp = RC_INVALID_INFO_UNIT;
+ goto skip;
}
+ hash_for_each_possible_safe(fu->stream_hash, stream, tmp, node, scsi_tag) {
+ struct usbg_cmd *active_cmd;
+ struct se_session *se_sess;
+ int i = stream - &fu->stream[0];
+
+ se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess;
+ active_cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[i];
+
+ if (active_cmd->tag == scsi_tag) {
+ cmd->tmr_rsp = RC_OVERLAPPED_TAG;
+ goto skip;
+ }
+ }
+
+ stream = &fu->stream[cmd->se_cmd.map_tag];
+ hash_add(fu->stream_hash, &stream->node, scsi_tag);
+
+ if (iu->iu_id == IU_ID_TASK_MGMT) {
+ struct task_mgmt_iu *tm_iu;
+
+ tm_iu = (struct task_mgmt_iu *)iu;
+ cmd->tmr_func = tm_iu->function;
+ goto skip;
+ }
+
+ cmd_len = (cmd_iu->len & ~0x3) + 16;
+ if (cmd_len > USBG_MAX_CMD) {
+ target_free_tag(tv_nexus->tvn_se_sess, &cmd->se_cmd);
+ hash_del(&stream->node);
+ return -EINVAL;
+ }
+ memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len);
+
switch (cmd_iu->prio_attr & 0x7) {
case UAS_HEAD_TAG:
cmd->prio_attr = TCM_HEAD_TAG;
@@ -1138,15 +1452,11 @@ static int usbg_submit_command(struct f_uas *fu,
break;
}
- cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun);
-
+skip:
INIT_WORK(&cmd->work, usbg_cmd_work);
queue_work(tpg->workqueue, &cmd->work);
return 0;
-err:
- usbg_release_cmd(&cmd->se_cmd);
- return -EINVAL;
}
static void bot_cmd_work(struct work_struct *work)
@@ -1155,27 +1465,38 @@ static void bot_cmd_work(struct work_struct *work)
struct se_cmd *se_cmd;
struct tcm_usbg_nexus *tv_nexus;
struct usbg_tpg *tpg;
+ int flags = TARGET_SCF_ACK_KREF;
int dir;
+ /*
+ * Note: each command will spawn its own process, and each stage of the
+ * command is processed sequentially. Should this no longer be the case,
+ * locking is needed.
+ */
+ if (cmd->flags & USBG_CMD_PENDING_DATA_WRITE) {
+ target_execute_cmd(&cmd->se_cmd);
+ cmd->flags &= ~USBG_CMD_PENDING_DATA_WRITE;
+ return;
+ }
+
se_cmd = &cmd->se_cmd;
tpg = cmd->fu->tpg;
tv_nexus = tpg->tpg_nexus;
dir = get_cmd_dir(cmd->cmd_buf);
- if (dir < 0) {
- __target_init_cmd(se_cmd,
- tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
- tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
- cmd->prio_attr, cmd->sense_iu.sense,
- cmd->unpacked_lun, NULL);
+ if (dir < 0)
goto out;
- }
target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess,
cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun,
- cmd->data_len, cmd->prio_attr, dir, 0);
+ cmd->data_len, cmd->prio_attr, dir, flags);
return;
out:
+ __target_init_cmd(se_cmd,
+ tv_nexus->tvn_se_sess->se_tpg->se_tpg_tfo,
+ tv_nexus->tvn_se_sess, cmd->data_len, DMA_NONE,
+ cmd->prio_attr, cmd->sense_iu.sense,
+ cmd->unpacked_lun, NULL);
transport_send_check_condition_and_sense(se_cmd,
TCM_UNSUPPORTED_SCSI_OPCODE, 0);
}
@@ -1221,6 +1542,7 @@ static int bot_submit_command(struct f_uas *fu,
cmd->is_read = cbw->Flags & US_BULK_FLAG_IN ? 1 : 0;
cmd->data_len = le32_to_cpu(cbw->DataTransferLength);
cmd->se_cmd.tag = le32_to_cpu(cmd->bot_tag);
+ cmd->flags = 0;
INIT_WORK(&cmd->work, bot_cmd_work);
queue_work(tpg->workqueue, &cmd->work);
@@ -1257,16 +1579,38 @@ static void usbg_release_cmd(struct se_cmd *se_cmd)
se_cmd);
struct se_session *se_sess = se_cmd->se_sess;
+ cmd->tag = 0;
kfree(cmd->data_buf);
target_free_tag(se_sess, se_cmd);
}
static void usbg_queue_tm_rsp(struct se_cmd *se_cmd)
{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, se_cmd);
+
+ uasp_send_tm_response(cmd);
}
static void usbg_aborted_task(struct se_cmd *se_cmd)
{
+ struct usbg_cmd *cmd = container_of(se_cmd, struct usbg_cmd, se_cmd);
+ struct f_uas *fu = cmd->fu;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+ struct uas_stream *stream = &fu->stream[se_cmd->map_tag];
+ int ret = 0;
+
+ if (stream->req_out->status == -EINPROGRESS)
+ ret = usb_ep_dequeue(fu->ep_out, stream->req_out);
+ else if (stream->req_in->status == -EINPROGRESS)
+ ret = usb_ep_dequeue(fu->ep_in, stream->req_in);
+ else if (stream->req_status->status == -EINPROGRESS)
+ ret = usb_ep_dequeue(fu->ep_status, stream->req_status);
+
+ if (ret)
+ dev_err(&gadget->dev, "Failed to abort cmd tag %d, (%d)\n",
+ cmd->tag, ret);
+
+ cmd->state = UASP_QUEUE_COMMAND;
}
static const char *usbg_check_wwn(const char *name)
@@ -1337,7 +1681,8 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
goto unref_dep;
mutex_init(&tpg->tpg_mutex);
atomic_set(&tpg->tpg_port_count, 0);
- tpg->workqueue = alloc_workqueue("tcm_usb_gadget", 0, 1);
+ tpg->workqueue = alloc_workqueue("tcm_usb_gadget",
+ WQ_UNBOUND, WQ_UNBOUND_MAX_ACTIVE);
if (!tpg->workqueue)
goto free_tpg;
@@ -1728,7 +2073,7 @@ static struct usb_endpoint_descriptor uasp_ss_bi_desc = {
static struct usb_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = {
.bLength = sizeof(uasp_bi_ep_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
- .bMaxBurst = 0,
+ .bMaxBurst = 15,
.bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS,
.wBytesPerInterval = 0,
};
@@ -1736,7 +2081,7 @@ static struct usb_ss_ep_comp_descriptor uasp_bi_ep_comp_desc = {
static struct usb_ss_ep_comp_descriptor bot_bi_ep_comp_desc = {
.bLength = sizeof(bot_bi_ep_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
- .bMaxBurst = 0,
+ .bMaxBurst = 15,
};
static struct usb_endpoint_descriptor uasp_bo_desc = {
@@ -1771,12 +2116,14 @@ static struct usb_endpoint_descriptor uasp_ss_bo_desc = {
static struct usb_ss_ep_comp_descriptor uasp_bo_ep_comp_desc = {
.bLength = sizeof(uasp_bo_ep_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 15,
.bmAttributes = UASP_SS_EP_COMP_LOG_STREAMS,
};
static struct usb_ss_ep_comp_descriptor bot_bo_ep_comp_desc = {
.bLength = sizeof(bot_bo_ep_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 15,
};
static struct usb_endpoint_descriptor uasp_status_desc = {
@@ -2257,6 +2604,8 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
fu->function.disable = tcm_disable;
fu->function.free_func = tcm_free;
fu->tpg = tpg_instances[i].tpg;
+
+ hash_init(fu->stream_hash);
mutex_unlock(&tpg_instances_lock);
return &fu->function;
diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h
index ced5d2b09234..11ac785d5eee 100644
--- a/drivers/usb/gadget/function/storage_common.h
+++ b/drivers/usb/gadget/function/storage_common.h
@@ -131,7 +131,7 @@ static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
#define FSG_BUFLEN ((u32)16384)
/* Maximal number of LUNs supported in mass storage function */
-#define FSG_MAX_LUNS 16
+#define FSG_MAX_LUNS (US_BULK_MAX_LUN_LIMIT + 1)
enum fsg_buffer_state {
BUF_STATE_SENDING = -2,
diff --git a/drivers/usb/gadget/function/tcm.h b/drivers/usb/gadget/function/tcm.h
index 3cd565794ad7..009974d81d66 100644
--- a/drivers/usb/gadget/function/tcm.h
+++ b/drivers/usb/gadget/function/tcm.h
@@ -4,6 +4,7 @@
#include <linux/kref.h>
/* #include <linux/usb/uas.h> */
+#include <linux/hashtable.h>
#include <linux/usb/composite.h>
#include <linux/usb/uas.h>
#include <linux/usb/storage.h>
@@ -13,9 +14,11 @@
#define USBG_NAMELEN 32
#define fuas_to_gadget(f) (f->function.config->cdev->gadget)
-#define UASP_SS_EP_COMP_LOG_STREAMS 4
+#define UASP_SS_EP_COMP_LOG_STREAMS 5
#define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS)
+#define USBG_NUM_CMDS (UASP_SS_EP_COMP_NUM_STREAMS + 1)
+
enum {
USB_G_STR_INT_UAS = 0,
USB_G_STR_INT_BBB,
@@ -24,7 +27,7 @@ enum {
#define USB_G_ALT_INT_BBB 0
#define USB_G_ALT_INT_UAS 1
-#define USB_G_DEFAULT_SESSION_TAGS 128
+#define USB_G_DEFAULT_SESSION_TAGS USBG_NUM_CMDS
struct tcm_usbg_nexus {
struct se_session *tvn_se_sess;
@@ -72,15 +75,23 @@ struct usbg_cmd {
struct se_cmd se_cmd;
void *data_buf; /* used if no sg support available */
struct f_uas *fu;
- struct completion write_complete;
struct kref ref;
+ struct usb_request *req;
+
+ u32 flags;
+#define USBG_CMD_PENDING_DATA_WRITE BIT(0)
+
/* UAS only */
u16 tag;
u16 prio_attr;
struct sense_iu sense_iu;
+ struct response_iu response_iu;
enum uas_state state;
- struct uas_stream *stream;
+
+ int tmr_func;
+ int tmr_rsp;
+#define RC_RESPONSE_UNKNOWN 0xff
/* BOT only */
__le32 bot_tag;
@@ -93,6 +104,9 @@ struct uas_stream {
struct usb_request *req_in;
struct usb_request *req_out;
struct usb_request *req_status;
+
+ struct completion cmd_completion;
+ struct hlist_node node;
};
struct usbg_cdb {
@@ -116,15 +130,17 @@ struct f_uas {
#define USBG_USE_STREAMS (1 << 2)
#define USBG_IS_BOT (1 << 3)
#define USBG_BOT_CMD_PEND (1 << 4)
+#define USBG_BOT_WEDGED (1 << 5)
- struct usbg_cdb cmd;
+ struct usbg_cdb cmd[USBG_NUM_CMDS];
struct usb_ep *ep_in;
struct usb_ep *ep_out;
/* UAS */
struct usb_ep *ep_status;
struct usb_ep *ep_cmd;
- struct uas_stream stream[UASP_SS_EP_COMP_NUM_STREAMS];
+ struct uas_stream stream[USBG_NUM_CMDS];
+ DECLARE_HASHTABLE(stream_hash, UASP_SS_EP_COMP_LOG_STREAMS);
/* BOT */
struct bot_status bot_status;
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index 53d9fc41acc5..36fff45e8c9b 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -21,6 +21,7 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/console.h>
@@ -1545,7 +1546,7 @@ static int __init userial_init(void)
pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
MAX_U_SERIAL_PORTS,
- (MAX_U_SERIAL_PORTS == 1) ? "" : "s");
+ str_plural(MAX_U_SERIAL_PORTS));
return status;
fail:
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 5eaeae3e2441..9a1bbd79ff5a 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -122,8 +122,6 @@ static const struct vb2_ops uvc_queue_qops = {
.queue_setup = uvc_queue_setup,
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
int uvcg_queue_init(struct uvc_video_queue *queue, struct device *dev, enum v4l2_buf_type type,
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 79e223713d8b..fb77b0b21790 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -818,7 +818,7 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
return -EINVAL;
/* Allocate a kthread for asynchronous hw submit handler. */
- video->kworker = kthread_create_worker(0, "UVCG");
+ video->kworker = kthread_run_worker(0, "UVCG");
if (IS_ERR(video->kworker)) {
uvcg_err(&video->uvc->func, "failed to create UVCG kworker\n");
return PTR_ERR(video->kworker);
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 9c7381661016..b6a30d88a800 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -20,6 +20,7 @@
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/poll.h>
#include <linux/kthread.h>
#include <linux/aio.h>
@@ -1182,7 +1183,7 @@ ep0_fasync (int f, struct file *fd, int on)
{
struct dev_data *dev = fd->private_data;
// caller must F_SETOWN before signal delivery happens
- VDEBUG (dev, "%s %s\n", __func__, on ? "on" : "off");
+ VDEBUG(dev, "%s %s\n", __func__, str_on_off(on));
return fasync_helper (f, fd, on, &dev->fasync);
}
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index e25e0d8dd387..a05785bdeb30 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -194,7 +194,7 @@ static void zero_suspend(struct usb_composite_dev *cdev)
static void zero_resume(struct usb_composite_dev *cdev)
{
DBG(cdev, "%s\n", __func__);
- del_timer(&autoresume_timer);
+ timer_delete(&autoresume_timer);
}
/*-------------------------------------------------------------------------*/
@@ -398,7 +398,7 @@ err_put_func_inst_ss:
static int zero_unbind(struct usb_composite_dev *cdev)
{
- del_timer_sync(&autoresume_timer);
+ timer_delete_sync(&autoresume_timer);
if (!IS_ERR_OR_NULL(func_ss))
usb_put_function(func_ss);
usb_put_function_instance(func_inst_ss);
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
index 573109ca5b79..a09f72772e6e 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
@@ -548,6 +548,9 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
d->vhub = vhub;
d->index = idx;
d->name = devm_kasprintf(parent, GFP_KERNEL, "port%d", idx+1);
+ if (!d->name)
+ return -ENOMEM;
+
d->regs = vhub->regs + 0x100 + 0x10 * idx;
ast_vhub_init_ep0(vhub, &d->ep0, d);
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
index a63e4af60a56..02fe1a08d575 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
@@ -22,6 +22,7 @@
#include <linux/usb/gadget.h>
#include <linux/of.h>
#include <linux/regmap.h>
+#include <linux/string_choices.h>
#include <linux/dma-mapping.h>
#include <linux/bcd.h>
#include <linux/version.h>
@@ -219,7 +220,7 @@ static int ast_vhub_hub_dev_feature(struct ast_vhub_ep *ep,
if (wValue == USB_DEVICE_REMOTE_WAKEUP) {
ep->vhub->wakeup_en = is_set;
EPDBG(ep, "Hub remote wakeup %s\n",
- is_set ? "enabled" : "disabled");
+ str_enabled_disabled(is_set));
return std_req_complete;
}
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index e3af4ec3794e..aa4c61094dc6 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -16,6 +16,7 @@
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@@ -131,7 +132,7 @@ static void proc_ep_show(struct seq_file *s, struct at91_ep *ep)
seq_printf(s, "csr %08x rxbytes=%d %s %s %s" EIGHTBITS "\n",
csr,
(csr & 0x07ff0000) >> 16,
- (csr & (1 << 15)) ? "enabled" : "disabled",
+ str_enabled_disabled(csr & (1 << 15)),
(csr & (1 << 11)) ? "DATA1" : "DATA0",
types[(csr & 0x700) >> 8],
diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
index 62fce42ef2da..7e69944ef18a 100644
--- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
+++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c
@@ -29,6 +29,7 @@
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/property.h>
+#include <linux/string_choices.h>
#include <linux/dmapool.h>
#include <linux/iopoll.h>
@@ -2233,12 +2234,12 @@ static int cdns2_init_eps(struct cdns2_device *pdev)
dev_dbg(pdev->dev, "Init %s, SupType: CTRL: %s, INT: %s, "
"BULK: %s, ISOC %s, SupDir IN: %s, OUT: %s\n",
pep->name,
- (pep->endpoint.caps.type_control) ? "yes" : "no",
- (pep->endpoint.caps.type_int) ? "yes" : "no",
- (pep->endpoint.caps.type_bulk) ? "yes" : "no",
- (pep->endpoint.caps.type_iso) ? "yes" : "no",
- (pep->endpoint.caps.dir_in) ? "yes" : "no",
- (pep->endpoint.caps.dir_out) ? "yes" : "no");
+ str_yes_no(pep->endpoint.caps.type_control),
+ str_yes_no(pep->endpoint.caps.type_int),
+ str_yes_no(pep->endpoint.caps.type_bulk),
+ str_yes_no(pep->endpoint.caps.type_iso),
+ str_yes_no(pep->endpoint.caps.dir_in),
+ str_yes_no(pep->endpoint.caps.dir_out));
INIT_LIST_HEAD(&pep->pending_list);
INIT_LIST_HEAD(&pep->deferred_list);
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 4b3d5075621a..d709e24c1fd4 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -1570,7 +1570,7 @@ static int gadget_match_driver(struct device *dev, const struct device_driver *d
{
struct usb_gadget *gadget = dev_to_usb_gadget(dev);
struct usb_udc *udc = gadget->udc;
- struct usb_gadget_driver *driver = container_of(drv,
+ const struct usb_gadget_driver *driver = container_of(drv,
struct usb_gadget_driver, driver);
/* If the driver specifies a udc_name, it must match the UDC's name */
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index a7e8fa45776b..4f1b5db51dda 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -28,6 +28,7 @@
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/hrtimer.h>
@@ -625,7 +626,7 @@ static int dummy_enable(struct usb_ep *_ep,
desc->bEndpointAddress & 0x0f,
(desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
usb_ep_type_string(usb_endpoint_type(desc)),
- max, ep->stream_en ? "enabled" : "disabled");
+ max, str_enabled_disabled(ep->stream_en));
/* at this point real hardware should be NAKing transfers
* to that endpoint, until a buffer is queued to it.
@@ -2478,8 +2479,7 @@ static DEVICE_ATTR_RO(urbs);
static int dummy_start_ss(struct dummy_hcd *dum_hcd)
{
- hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
- dum_hcd->timer.function = dummy_timer;
+ hrtimer_setup(&dum_hcd->timer, dummy_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dum_hcd->rh_state = DUMMY_RH_RUNNING;
dum_hcd->stream_en_ep = 0;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
@@ -2508,8 +2508,7 @@ static int dummy_start(struct usb_hcd *hcd)
return dummy_start_ss(dum_hcd);
spin_lock_init(&dum_hcd->dum->lock);
- hrtimer_init(&dum_hcd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
- dum_hcd->timer.function = dummy_timer;
+ hrtimer_setup(&dum_hcd->timer, dummy_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
dum_hcd->rh_state = DUMMY_RH_RUNNING;
INIT_LIST_HEAD(&dum_hcd->urbp_list);
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 8b7f7f961774..4dea8bc30cf6 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -22,6 +22,7 @@
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@@ -1181,7 +1182,7 @@ static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
udc = container_of(gadget, struct fsl_udc, gadget);
spin_lock_irqsave(&udc->lock, flags);
- dev_vdbg(&gadget->dev, "VBUS %s\n", is_active ? "on" : "off");
+ dev_vdbg(&gadget->dev, "VBUS %s\n", str_on_off(is_active));
udc->vbus_active = (is_active != 0);
if (can_pullup(udc))
fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index 698463bf697b..c93ea210c17c 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -18,6 +18,7 @@
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@@ -251,7 +252,7 @@ static int omap_ep_disable(struct usb_ep *_ep)
ep->has_dma = 0;
omap_writew(UDC_SET_HALT, UDC_CTRL);
list_del_init(&ep->iso);
- del_timer(&ep->timer);
+ timer_delete(&ep->timer);
spin_unlock_irqrestore(&ep->udc->lock, flags);
@@ -1252,7 +1253,7 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active)
udc = container_of(gadget, struct omap_udc, gadget);
spin_lock_irqsave(&udc->lock, flags);
- VDBG("VBUS %s\n", is_active ? "on" : "off");
+ VDBG("VBUS %s\n", str_on_off(is_active));
udc->vbus_active = (is_active != 0);
if (cpu_is_omap15xx()) {
/* "software" detect, ignored if !VBUS_MODE_1510 */
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 1e9998024aaa..24eb1ae78e45 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -1503,7 +1503,7 @@ reset_gadget(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
ep->stopped = 1;
nuke(ep, -ESHUTDOWN);
}
- del_timer_sync(&dev->timer);
+ timer_delete_sync(&dev->timer);
/* report reset; the driver is already quiesced */
if (driver)
@@ -1530,7 +1530,7 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
ep->stopped = 1;
nuke(ep, -ESHUTDOWN);
}
- del_timer_sync(&dev->timer);
+ timer_delete_sync(&dev->timer);
/* report disconnect; the driver is already quiesced */
if (driver)
@@ -1607,14 +1607,14 @@ static void handle_ep0 (struct pxa25x_udc *dev)
if (udccs0 & UDCCS0_SST) {
nuke(ep, -EPIPE);
udc_ep0_set_UDCCS(dev, UDCCS0_SST);
- del_timer(&dev->timer);
+ timer_delete(&dev->timer);
ep0_idle(dev);
}
/* previous request unfinished? non-error iff back-to-back ... */
if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) {
nuke(ep, 0);
- del_timer(&dev->timer);
+ timer_delete(&dev->timer);
ep0_idle(dev);
}
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index f9a55d4f189f..897f53601b5b 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -20,6 +20,7 @@
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/prefetch.h>
#include <linux/byteorder/generic.h>
#include <linux/platform_data/pxa2xx_udc.h>
@@ -1083,7 +1084,7 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
is_first_req = list_empty(&ep->queue);
ep_dbg(ep, "queue req %p(first=%s), len %d buf %p\n",
- _req, is_first_req ? "yes" : "no",
+ _req, str_yes_no(is_first_req),
_req->length, _req->buf);
if (!ep->enabled) {
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index ff6f846b1d41..6c1d15b2250c 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1810,7 +1810,7 @@ static void r8a66597_remove(struct platform_device *pdev)
struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
usb_del_gadget_udc(&r8a66597->gadget);
- del_timer_sync(&r8a66597->timer);
+ timer_delete_sync(&r8a66597->timer);
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
if (r8a66597->pdata->on_chip) {
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 1f8a99d2a643..373942ceb076 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -3035,12 +3035,12 @@ void udc_remove(struct udc *dev)
stop_timer++;
if (timer_pending(&udc_timer))
wait_for_completion(&on_exit);
- del_timer_sync(&udc_timer);
+ timer_delete_sync(&udc_timer);
/* remove pollstall timer */
stop_pollstall_timer++;
if (timer_pending(&udc_pollstall_timer))
wait_for_completion(&on_pollstall_exit);
- del_timer_sync(&udc_pollstall_timer);
+ timer_delete_sync(&udc_pollstall_timer);
udc = NULL;
}
EXPORT_SYMBOL_GPL(udc_remove);
diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c
index c7fdbc55fb0b..2957316fd3d0 100644
--- a/drivers/usb/gadget/udc/tegra-xudc.c
+++ b/drivers/usb/gadget/udc/tegra-xudc.c
@@ -1749,6 +1749,10 @@ static int __tegra_xudc_ep_disable(struct tegra_xudc_ep *ep)
val = xudc_readl(xudc, CTRL);
val &= ~CTRL_RUN;
xudc_writel(xudc, val, CTRL);
+
+ val = xudc_readl(xudc, ST);
+ if (val & ST_RC)
+ xudc_writel(xudc, ST_RC, ST);
}
dev_info(xudc->dev, "ep %u disabled\n", ep->index);
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 6de79ac5e6a4..6d1d190c914d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -466,8 +466,7 @@ static int ehci_init(struct usb_hcd *hcd)
*/
ehci->need_io_watchdog = 1;
- hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
- ehci->hrtimer.function = ehci_hrtimer_func;
+ hrtimer_setup(&ehci->hrtimer, ehci_hrtimer_func, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index cdf41886e8ca..150d2542cef0 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -224,7 +224,7 @@ static void quirk_poll_init(struct ehci_platform_priv *priv)
static void quirk_poll_end(struct ehci_platform_priv *priv)
{
- del_timer_sync(&priv->poll_timer);
+ timer_delete_sync(&priv->poll_timer);
cancel_delayed_work(&priv->poll_work);
}
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 2d3a082cb52f..954fc5ad565b 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -2357,7 +2357,7 @@ static void isp1362_hc_stop(struct usb_hcd *hcd)
pr_debug("%s:\n", __func__);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
spin_lock_irqsave(&isp1362_hcd->lock, flags);
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 0881fdd1823e..dcf31a592f5d 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -1946,6 +1946,12 @@ max3421_remove(struct spi_device *spi)
usb_put_hcd(hcd);
}
+static const struct spi_device_id max3421_spi_ids[] = {
+ { "max3421" },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, max3421_spi_ids);
+
static const struct of_device_id max3421_of_match_table[] = {
{ .compatible = "maxim,max3421", },
{},
@@ -1955,6 +1961,7 @@ MODULE_DEVICE_TABLE(of, max3421_of_match_table);
static struct spi_driver max3421_driver = {
.probe = max3421_probe,
.remove = max3421_remove,
+ .id_table = max3421_spi_ids,
.driver = {
.name = "max3421-hcd",
.of_match_table = max3421_of_match_table,
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 9b24181fee60..c7784bf8101d 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1003,7 +1003,7 @@ static void ohci_stop (struct usb_hcd *hcd)
if (quirk_nec(ohci))
flush_work(&ohci->nec_work);
- del_timer_sync(&ohci->io_watchdog);
+ timer_delete_sync(&ohci->io_watchdog);
ohci->prev_frame_no = IO_WATCHDOG_OFF;
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 90cee192e96d..b3d734ab6201 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -315,7 +315,7 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
spin_unlock_irq (&ohci->lock);
if (rc == 0) {
- del_timer_sync(&ohci->io_watchdog);
+ timer_delete_sync(&ohci->io_watchdog);
ohci->prev_frame_no = IO_WATCHDOG_OFF;
}
return rc;
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 900ea0d368e0..9f0a6b27e47c 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -165,6 +165,25 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd)
return 0;
}
+static int ohci_quirk_loongson(struct usb_hcd *hcd)
+{
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+
+ /*
+ * Loongson's LS7A OHCI controller (rev 0x02) has a
+ * flaw. MMIO register with offset 0x60/64 is treated
+ * as legacy PS2-compatible keyboard/mouse interface.
+ * Since OHCI only use 4KB BAR resource, LS7A OHCI's
+ * 32KB BAR is wrapped around (the 2nd 4KB BAR space
+ * is the same as the 1st 4KB internally). So add 4KB
+ * offset (0x1000) to the OHCI registers as a quirk.
+ */
+ if (pdev->revision == 0x2)
+ hcd->regs += SZ_4K; /* SZ_4K = 0x1000 */
+
+ return 0;
+}
+
static int ohci_quirk_qemu(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
@@ -225,6 +244,10 @@ static const struct pci_device_id ohci_pci_quirks[] = {
.driver_data = (unsigned long)ohci_quirk_amd700,
},
{
+ PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, 0x7a24),
+ .driver_data = (unsigned long)ohci_quirk_loongson,
+ },
+ {
.vendor = PCI_VENDOR_ID_APPLE,
.device = 0x003f,
.subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index a6c20facf945..d75b1b9b4db0 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -15,6 +15,7 @@
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/list.h>
@@ -1126,7 +1127,7 @@ static void ehci_mem_cleanup(struct oxu_hcd *oxu)
qh_put(oxu->async);
oxu->async = NULL;
- del_timer(&oxu->urb_timer);
+ timer_delete(&oxu->urb_timer);
oxu->periodic = NULL;
@@ -2756,7 +2757,7 @@ static void ehci_port_power(struct oxu_hcd *oxu, int is_on)
if (!HCS_PPC(oxu->hcs_params))
return;
- oxu_dbg(oxu, "...power%s ports...\n", is_on ? "up" : "down");
+ oxu_dbg(oxu, "...power%s ports...\n", str_up_down(is_on));
for (port = HCS_N_PORTS(oxu->hcs_params); port > 0; ) {
if (is_on)
oxu_hub_control(oxu_to_hcd(oxu), SetPortFeature,
@@ -3153,7 +3154,7 @@ static void oxu_stop(struct usb_hcd *hcd)
ehci_port_power(oxu, 0);
/* no more interrupts ... */
- del_timer_sync(&oxu->watchdog);
+ timer_delete_sync(&oxu->watchdog);
spin_lock_irq(&oxu->lock);
if (HC_IS_RUNNING(hcd->state))
@@ -3886,7 +3887,7 @@ static int oxu_bus_suspend(struct usb_hcd *hcd)
spin_unlock_irq(&oxu->lock);
/* turn off now-idle HC */
- del_timer_sync(&oxu->watchdog);
+ timer_delete_sync(&oxu->watchdog);
spin_lock_irq(&oxu->lock);
ehci_halt(oxu);
hcd->state = HC_STATE_SUSPENDED;
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index a44992e2561b..67e472116d11 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -2384,7 +2384,7 @@ static void r8a66597_remove(struct platform_device *pdev)
struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597);
- del_timer_sync(&r8a66597->rh_timer);
+ timer_delete_sync(&r8a66597->rh_timer);
usb_remove_hcd(hcd);
iounmap(r8a66597->reg);
if (r8a66597->pdata->on_chip)
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 036f5fd6d159..718b1b7fe366 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -48,6 +48,7 @@
#include <linux/usb/hcd.h>
#include <linux/platform_device.h>
#include <linux/prefetch.h>
+#include <linux/string_choices.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
@@ -98,7 +99,7 @@ static void port_power(struct sl811 *sl811, int is_on)
if (sl811->board && sl811->board->port_power) {
/* switch VBUS, at 500mA unless hub power budget gets set */
dev_dbg(hcd->self.controller, "power %s\n",
- is_on ? "on" : "off");
+ str_on_off(is_on));
sl811->board->port_power(hcd->self.controller, is_on);
}
@@ -1514,7 +1515,7 @@ sl811h_stop(struct usb_hcd *hcd)
struct sl811 *sl811 = hcd_to_sl811(hcd);
unsigned long flags;
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
spin_lock_irqsave(&sl811->lock, flags);
port_power(sl811, 0);
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index fd2408b553cf..14e6dfef16c6 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -716,7 +716,7 @@ static void uhci_stop(struct usb_hcd *hcd)
spin_unlock_irq(&uhci->lock);
synchronize_irq(hcd->irq);
- del_timer_sync(&uhci->fsbr_timer);
+ timer_delete_sync(&uhci->fsbr_timer);
release_uhci(uhci);
}
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
index a7c934404ebc..62318291f566 100644
--- a/drivers/usb/host/uhci-platform.c
+++ b/drivers/usb/host/uhci-platform.c
@@ -121,7 +121,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
}
/* Get and enable clock if any specified */
- uhci->clk = devm_clk_get(&pdev->dev, NULL);
+ uhci->clk = devm_clk_get_optional(&pdev->dev, NULL);
if (IS_ERR(uhci->clk)) {
ret = PTR_ERR(uhci->clk);
goto err_rmr;
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 35fcb826152c..45a8256a665f 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -84,7 +84,7 @@ static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp)
uhci_fsbr_on(uhci);
else if (uhci->fsbr_expiring) {
uhci->fsbr_expiring = 0;
- del_timer(&uhci->fsbr_timer);
+ timer_delete(&uhci->fsbr_timer);
}
}
}
diff --git a/drivers/usb/host/xen-hcd.c b/drivers/usb/host/xen-hcd.c
index 46fdab940092..05943f2213e4 100644
--- a/drivers/usb/host/xen-hcd.c
+++ b/drivers/usb/host/xen-hcd.c
@@ -327,7 +327,7 @@ static int xenhcd_bus_suspend(struct usb_hcd *hcd)
}
spin_unlock_irq(&info->lock);
- del_timer_sync(&info->watchdog);
+ timer_delete_sync(&info->watchdog);
return ret;
}
@@ -1307,7 +1307,7 @@ static void xenhcd_stop(struct usb_hcd *hcd)
{
struct xenhcd_info *info = xenhcd_hcd_to_info(hcd);
- del_timer_sync(&info->watchdog);
+ timer_delete_sync(&info->watchdog);
spin_lock_irq(&info->lock);
/* cancel all urbs */
hcd->state = HC_STATE_HALT;
diff --git a/drivers/usb/host/xhci-caps.h b/drivers/usb/host/xhci-caps.h
index 9e94cebf4a56..f6b9a00a0ab9 100644
--- a/drivers/usb/host/xhci-caps.h
+++ b/drivers/usb/host/xhci-caps.h
@@ -83,3 +83,9 @@
#define HCC2_CIC(p) ((p) & (1 << 5))
/* true: HC support Extended TBC Capability, Isoc burst count > 65535 */
#define HCC2_ETC(p) ((p) & (1 << 6))
+/* true: HC support Extended TBC TRB Status Capability */
+#define HCC2_ETC_TSC(p) ((p) & (1 << 7))
+/* true: HC support Get/Set Extended Property Capability */
+#define HCC2_GSC(p) ((p) & (1 << 8))
+/* true: HC support Virtualization Based Trusted I/O Capability */
+#define HCC2_VTC(p) ((p) & (1 << 9))
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 227e513867dd..0d4ce5734165 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -823,6 +823,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
{
dma_addr_t deq;
union xhci_trb *evt;
+ enum evtreturn ret = EVT_DONE;
u32 ctrl, portsc;
bool update_erdp = false;
@@ -909,6 +910,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
break;
case TRB_TYPE(TRB_TRANSFER):
dbc_handle_xfer_event(dbc, evt);
+ ret = EVT_XFER_DONE;
break;
default:
break;
@@ -927,7 +929,7 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
lo_hi_writeq(deq, &dbc->regs->erdp);
}
- return EVT_DONE;
+ return ret;
}
static void xhci_dbc_handle_events(struct work_struct *work)
@@ -936,6 +938,7 @@ static void xhci_dbc_handle_events(struct work_struct *work)
struct xhci_dbc *dbc;
unsigned long flags;
unsigned int poll_interval;
+ unsigned long busypoll_timelimit;
dbc = container_of(to_delayed_work(work), struct xhci_dbc, event_work);
poll_interval = dbc->poll_interval;
@@ -954,10 +957,20 @@ static void xhci_dbc_handle_events(struct work_struct *work)
dbc->driver->disconnect(dbc);
break;
case EVT_DONE:
- /* set fast poll rate if there are pending data transfers */
+ /*
+ * Set fast poll rate if there are pending out transfers, or
+ * a transfer was recently processed
+ */
+ busypoll_timelimit = dbc->xfer_timestamp +
+ msecs_to_jiffies(DBC_XFER_INACTIVITY_TIMEOUT);
+
if (!list_empty(&dbc->eps[BULK_OUT].list_pending) ||
- !list_empty(&dbc->eps[BULK_IN].list_pending))
- poll_interval = 1;
+ time_is_after_jiffies(busypoll_timelimit))
+ poll_interval = 0;
+ break;
+ case EVT_XFER_DONE:
+ dbc->xfer_timestamp = jiffies;
+ poll_interval = 0;
break;
default:
dev_info(dbc->dev, "stop handling dbc events\n");
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index 9dc8f4d8077c..47ac72c2286d 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -96,6 +96,7 @@ struct dbc_ep {
#define DBC_WRITE_BUF_SIZE 8192
#define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */
#define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */
+#define DBC_XFER_INACTIVITY_TIMEOUT 10 /* milliseconds */
/*
* Private structure for DbC hardware state:
*/
@@ -142,6 +143,7 @@ struct xhci_dbc {
enum dbc_state state;
struct delayed_work event_work;
unsigned int poll_interval; /* ms */
+ unsigned long xfer_timestamp;
unsigned resume_required:1;
struct dbc_ep eps[2];
@@ -187,6 +189,7 @@ struct dbc_request {
enum evtreturn {
EVT_ERR = -1,
EVT_DONE,
+ EVT_XFER_DONE,
EVT_GSER,
EVT_DISC,
};
diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c
index d719c16ea30b..60ed753c85bb 100644
--- a/drivers/usb/host/xhci-dbgtty.c
+++ b/drivers/usb/host/xhci-dbgtty.c
@@ -110,15 +110,74 @@ static void dbc_start_rx(struct dbc_port *port)
}
}
+/*
+ * Queue received data to tty buffer and push it.
+ *
+ * Returns nr of remaining bytes that didn't fit tty buffer, i.e. 0 if all
+ * bytes sucessfullt moved. In case of error returns negative errno.
+ * Call with lock held
+ */
+static int dbc_rx_push_buffer(struct dbc_port *port, struct dbc_request *req)
+{
+ char *packet = req->buf;
+ unsigned int n, size = req->actual;
+ int count;
+
+ if (!req->actual)
+ return 0;
+
+ /* if n_read is set then request was partially moved to tty buffer */
+ n = port->n_read;
+ if (n) {
+ packet += n;
+ size -= n;
+ }
+
+ count = tty_insert_flip_string(&port->port, packet, size);
+ if (count)
+ tty_flip_buffer_push(&port->port);
+ if (count != size) {
+ port->n_read += count;
+ return size - count;
+ }
+
+ port->n_read = 0;
+ return 0;
+}
+
static void
dbc_read_complete(struct xhci_dbc *dbc, struct dbc_request *req)
{
unsigned long flags;
struct dbc_port *port = dbc_to_port(dbc);
+ struct tty_struct *tty;
+ int untransferred;
+
+ tty = port->port.tty;
spin_lock_irqsave(&port->port_lock, flags);
+
+ /*
+ * Only defer copyig data to tty buffer in case:
+ * - !list_empty(&port->read_queue), there are older pending data
+ * - tty is throttled
+ * - failed to copy all data to buffer, defer remaining part
+ */
+
+ if (list_empty(&port->read_queue) && tty && !tty_throttled(tty)) {
+ untransferred = dbc_rx_push_buffer(port, req);
+ if (untransferred == 0) {
+ list_add_tail(&req->list_pool, &port->read_pool);
+ if (req->status != -ESHUTDOWN)
+ dbc_start_rx(port);
+ goto out;
+ }
+ }
+
+ /* defer moving data from req to tty buffer to a tasklet */
list_add_tail(&req->list_pool, &port->read_queue);
tasklet_schedule(&port->push);
+out:
spin_unlock_irqrestore(&port->port_lock, flags);
}
@@ -331,10 +390,10 @@ static void dbc_rx_push(struct tasklet_struct *t)
struct dbc_request *req;
struct tty_struct *tty;
unsigned long flags;
- bool do_push = false;
bool disconnect = false;
struct dbc_port *port = from_tasklet(port, t, push);
struct list_head *queue = &port->read_queue;
+ int untransferred;
spin_lock_irqsave(&port->port_lock, flags);
tty = port->port.tty;
@@ -356,42 +415,15 @@ static void dbc_rx_push(struct tasklet_struct *t)
break;
}
- if (req->actual) {
- char *packet = req->buf;
- unsigned int n, size = req->actual;
- int count;
-
- n = port->n_read;
- if (n) {
- packet += n;
- size -= n;
- }
-
- count = tty_insert_flip_string(&port->port, packet,
- size);
- if (count)
- do_push = true;
- if (count != size) {
- port->n_read += count;
- break;
- }
- port->n_read = 0;
- }
+ untransferred = dbc_rx_push_buffer(port, req);
+ if (untransferred > 0)
+ break;
list_move_tail(&req->list_pool, &port->read_pool);
}
- if (do_push)
- tty_flip_buffer_push(&port->port);
-
- if (!list_empty(queue) && tty) {
- if (!tty_throttled(tty)) {
- if (do_push)
- tasklet_schedule(&port->push);
- else
- pr_warn("ttyDBC0: RX not scheduled?\n");
- }
- }
+ if (!list_empty(queue))
+ tasklet_schedule(&port->push);
if (!disconnect)
dbc_start_rx(port);
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
index 4f0c1b96e208..1f5ef174abea 100644
--- a/drivers/usb/host/xhci-debugfs.c
+++ b/drivers/usb/host/xhci-debugfs.c
@@ -232,16 +232,7 @@ static struct xhci_file_map ring_files[] = {
static int xhci_ring_open(struct inode *inode, struct file *file)
{
- int i;
- struct xhci_file_map *f_map;
- const char *file_name = file_dentry(file)->d_iname;
-
- for (i = 0; i < ARRAY_SIZE(ring_files); i++) {
- f_map = &ring_files[i];
-
- if (strcmp(f_map->name, file_name) == 0)
- break;
- }
+ const struct xhci_file_map *f_map = debugfs_get_aux(file);
return single_open(file, f_map->show, inode->i_private);
}
@@ -318,16 +309,7 @@ static struct xhci_file_map context_files[] = {
static int xhci_context_open(struct inode *inode, struct file *file)
{
- int i;
- struct xhci_file_map *f_map;
- const char *file_name = file_dentry(file)->d_iname;
-
- for (i = 0; i < ARRAY_SIZE(context_files); i++) {
- f_map = &context_files[i];
-
- if (strcmp(f_map->name, file_name) == 0)
- break;
- }
+ const struct xhci_file_map *f_map = debugfs_get_aux(file);
return single_open(file, f_map->show, inode->i_private);
}
@@ -410,7 +392,8 @@ static void xhci_debugfs_create_files(struct xhci_hcd *xhci,
int i;
for (i = 0; i < nentries; i++)
- debugfs_create_file(files[i].name, 0444, parent, data, fops);
+ debugfs_create_file_aux(files[i].name, 0444, parent,
+ data, &files[i], fops);
}
static struct dentry *xhci_debugfs_create_ring_dir(struct xhci_hcd *xhci,
diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c
index 8a7d46dae62c..02396c8721dc 100644
--- a/drivers/usb/host/xhci-histb.c
+++ b/drivers/usb/host/xhci-histb.c
@@ -355,7 +355,7 @@ static int __maybe_unused xhci_histb_resume(struct device *dev)
if (!device_may_wakeup(dev))
xhci_histb_host_enable(histb);
- return xhci_resume(xhci, PMSG_RESUME);
+ return xhci_resume(xhci, false, false);
}
static const struct dev_pm_ops xhci_histb_pm_ops = {
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 69c278b64084..486347776cb2 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -926,7 +926,7 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status,
if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) {
xhci->port_status_u0 |= 1 << wIndex;
if (xhci->port_status_u0 == all_ports_seen_u0) {
- del_timer_sync(&xhci->comp_mode_recovery_timer);
+ timer_delete_sync(&xhci->comp_mode_recovery_timer);
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"All USB3 ports have entered U0 already!");
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
@@ -1878,9 +1878,10 @@ int xhci_bus_resume(struct usb_hcd *hcd)
int max_ports, port_index;
int sret;
u32 next_state;
- u32 temp, portsc;
+ u32 portsc;
struct xhci_hub *rhub;
struct xhci_port **ports;
+ bool disabled_irq = false;
rhub = xhci_get_rhub(hcd);
ports = rhub->ports;
@@ -1896,17 +1897,20 @@ int xhci_bus_resume(struct usb_hcd *hcd)
return -ESHUTDOWN;
}
- /* delay the irqs */
- temp = readl(&xhci->op_regs->command);
- temp &= ~CMD_EIE;
- writel(temp, &xhci->op_regs->command);
-
/* bus specific resume for ports we suspended at bus_suspend */
- if (hcd->speed >= HCD_USB3)
+ if (hcd->speed >= HCD_USB3) {
next_state = XDEV_U0;
- else
+ } else {
next_state = XDEV_RESUME;
-
+ if (bus_state->bus_suspended) {
+ /*
+ * prevent port event interrupts from interfering
+ * with usb2 port resume process
+ */
+ xhci_disable_interrupter(xhci->interrupters[0]);
+ disabled_irq = true;
+ }
+ }
port_index = max_ports;
while (port_index--) {
portsc = readl(ports[port_index]->addr);
@@ -1974,11 +1978,9 @@ int xhci_bus_resume(struct usb_hcd *hcd)
(void) readl(&xhci->op_regs->command);
bus_state->next_statechange = jiffies + msecs_to_jiffies(5);
- /* re-enable irqs */
- temp = readl(&xhci->op_regs->command);
- temp |= CMD_EIE;
- writel(temp, &xhci->op_regs->command);
- temp = readl(&xhci->op_regs->command);
+ /* re-enable interrupter */
+ if (disabled_irq)
+ xhci_enable_interrupter(xhci->interrupters[0]);
spin_unlock_irqrestore(&xhci->lock, flags);
return 0;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index fdf0c1008225..d698095fc88d 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1953,7 +1953,6 @@ no_bw:
xhci->interrupters = NULL;
xhci->page_size = 0;
- xhci->page_shift = 0;
xhci->usb2_rhub.bus_state.bus_suspended = 0;
xhci->usb3_rhub.bus_state.bus_suspended = 0;
}
@@ -2372,6 +2371,22 @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
}
EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
+static void xhci_hcd_page_size(struct xhci_hcd *xhci)
+{
+ u32 page_size;
+
+ page_size = readl(&xhci->op_regs->page_size) & XHCI_PAGE_SIZE_MASK;
+ if (!is_power_of_2(page_size)) {
+ xhci_warn(xhci, "Invalid page size register = 0x%x\n", page_size);
+ /* Fallback to 4K page size, since that's common */
+ page_size = 1;
+ }
+
+ xhci->page_size = page_size << 12;
+ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "HCD page size set to %iK",
+ xhci->page_size >> 10);
+}
+
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
struct xhci_interrupter *ir;
@@ -2379,7 +2394,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
dma_addr_t dma;
unsigned int val, val2;
u64 val_64;
- u32 page_size, temp;
+ u32 temp;
int i;
INIT_LIST_HEAD(&xhci->cmd_list);
@@ -2388,20 +2403,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
INIT_DELAYED_WORK(&xhci->cmd_timer, xhci_handle_command_timeout);
init_completion(&xhci->cmd_ring_stop_completion);
- page_size = readl(&xhci->op_regs->page_size);
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Supported page size register = 0x%x", page_size);
- i = ffs(page_size);
- if (i < 16)
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "Supported page size of %iK", (1 << (i+12)) / 1024);
- else
- xhci_warn(xhci, "WARN: no supported page size\n");
- /* Use 4K pages, since that's common and the minimum the HC supports */
- xhci->page_shift = 12;
- xhci->page_size = 1 << xhci->page_shift;
- xhci_dbg_trace(xhci, trace_xhci_dbg_init,
- "HCD page size set to %iK", xhci->page_size / 1024);
+ xhci_hcd_page_size(xhci);
/*
* Program the Number of Device Slots Enabled field in the CONFIG
diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 904831344440..208558cf822d 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -746,10 +746,10 @@ static int __maybe_unused xhci_mtk_suspend(struct device *dev)
xhci_dbg(xhci, "%s: stop port polling\n", __func__);
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
if (shared_hcd) {
clear_bit(HCD_FLAG_POLL_RH, &shared_hcd->flags);
- del_timer_sync(&shared_hcd->rh_timer);
+ timer_delete_sync(&shared_hcd->rh_timer);
}
ret = xhci_mtk_host_disable(mtk);
diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c
index 87f1597a0e5a..257e4d79971f 100644
--- a/drivers/usb/host/xhci-mvebu.c
+++ b/drivers/usb/host/xhci-mvebu.c
@@ -73,13 +73,3 @@ int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
return 0;
}
-
-int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
-{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-
- /* Without reset on resume, the HC won't work at all */
- xhci->quirks |= XHCI_RESET_ON_RESUME;
-
- return 0;
-}
diff --git a/drivers/usb/host/xhci-mvebu.h b/drivers/usb/host/xhci-mvebu.h
index 3be021793cc8..9d26e22c4842 100644
--- a/drivers/usb/host/xhci-mvebu.h
+++ b/drivers/usb/host/xhci-mvebu.h
@@ -12,16 +12,10 @@ struct usb_hcd;
#if IS_ENABLED(CONFIG_USB_XHCI_MVEBU)
int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd);
-int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd);
#else
static inline int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd)
{
return 0;
}
-
-static inline int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd)
-{
- return 0;
-}
#endif
#endif /* __LINUX_XHCI_MVEBU_H */
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 54460d11f7ee..0c481cbc8f08 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -807,8 +807,10 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
{
- struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
+ bool power_lost = msg.event == PM_EVENT_RESTORE;
+ bool is_auto_resume = msg.event == PM_EVENT_AUTO_RESUME;
reset_control_reset(xhci->reset);
@@ -839,7 +841,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg)
if (xhci->quirks & XHCI_PME_STUCK_QUIRK)
xhci_pme_quirk(hcd);
- return xhci_resume(xhci, msg);
+ return xhci_resume(xhci, power_lost, is_auto_resume);
}
static int xhci_pci_poweroff_late(struct usb_hcd *hcd, bool do_wakeup)
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index db109b570c5c..3155e3a842da 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -106,7 +106,7 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada = {
};
static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = {
- .init_quirk = xhci_mvebu_a3700_init_quirk,
+ .quirks = XHCI_RESET_ON_RESUME,
};
static const struct xhci_plat_priv xhci_plat_brcm = {
@@ -330,6 +330,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
usb3_hcd->can_do_streams = 1;
if (xhci->shared_hcd) {
+ xhci->shared_hcd->rsrc_start = hcd->rsrc_start;
+ xhci->shared_hcd->rsrc_len = hcd->rsrc_len;
ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
if (ret)
goto put_usb3_hcd;
@@ -477,9 +479,10 @@ static int xhci_plat_suspend(struct device *dev)
return 0;
}
-static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg)
+static int xhci_plat_resume_common(struct device *dev, bool power_lost)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int ret;
@@ -499,7 +502,7 @@ static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg)
if (ret)
goto disable_clks;
- ret = xhci_resume(xhci, pmsg);
+ ret = xhci_resume(xhci, power_lost || priv->power_lost, false);
if (ret)
goto disable_clks;
@@ -520,12 +523,12 @@ disable_clks:
static int xhci_plat_resume(struct device *dev)
{
- return xhci_plat_resume_common(dev, PMSG_RESUME);
+ return xhci_plat_resume_common(dev, false);
}
static int xhci_plat_restore(struct device *dev)
{
- return xhci_plat_resume_common(dev, PMSG_RESTORE);
+ return xhci_plat_resume_common(dev, true);
}
static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev)
@@ -546,7 +549,7 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
- return xhci_resume(xhci, PMSG_AUTO_RESUME);
+ return xhci_resume(xhci, false, true);
}
const struct dev_pm_ops xhci_plat_pm_ops = {
@@ -567,6 +570,7 @@ EXPORT_SYMBOL_GPL(xhci_plat_pm_ops);
static const struct acpi_device_id usb_xhci_acpi_match[] = {
/* XHCI-compliant USB Controller */
{ "PNP0D10", },
+ { "PNP0D15", },
{ }
};
MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match);
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index 6475130eac4b..fe4f95e690fa 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -15,6 +15,7 @@ struct usb_hcd;
struct xhci_plat_priv {
const char *firmware_name;
unsigned long long quirks;
+ bool power_lost;
void (*plat_start)(struct usb_hcd *);
int (*init_quirk)(struct usb_hcd *);
int (*suspend_quirk)(struct usb_hcd *);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index dfe1a676d487..423bf3649570 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -55,6 +55,7 @@
#include <linux/jiffies.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/dma-mapping.h>
#include "xhci.h"
#include "xhci-trace.h"
@@ -203,6 +204,50 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
}
/*
+ * If enqueue points at a link TRB, follow links until an ordinary TRB is reached.
+ * Toggle the cycle bit of passed link TRBs and optionally chain them.
+ */
+static void inc_enq_past_link(struct xhci_hcd *xhci, struct xhci_ring *ring, u32 chain)
+{
+ unsigned int link_trb_count = 0;
+
+ while (trb_is_link(ring->enqueue)) {
+
+ /*
+ * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
+ * set, but other sections talk about dealing with the chain bit set. This was
+ * fixed in the 0.96 specification errata, but we have to assume that all 0.95
+ * xHCI hardware can't handle the chain bit being cleared on a link TRB.
+ *
+ * On 0.95 and some 0.96 HCs the chain bit is set once at segment initalization
+ * and never changed here. On all others, modify it as requested by the caller.
+ */
+ if (!xhci_link_chain_quirk(xhci, ring->type)) {
+ ring->enqueue->link.control &= cpu_to_le32(~TRB_CHAIN);
+ ring->enqueue->link.control |= cpu_to_le32(chain);
+ }
+
+ /* Give this link TRB to the hardware */
+ wmb();
+ ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE);
+
+ /* Toggle the cycle bit after the last ring segment. */
+ if (link_trb_toggles_cycle(ring->enqueue))
+ ring->cycle_state ^= 1;
+
+ ring->enq_seg = ring->enq_seg->next;
+ ring->enqueue = ring->enq_seg->trbs;
+
+ trace_xhci_inc_enq(ring);
+
+ if (link_trb_count++ > ring->num_segs) {
+ xhci_warn(xhci, "Link TRB loop at enqueue\n");
+ break;
+ }
+ }
+}
+
+/*
* See Cycle bit rules. SW is the consumer for the event ring only.
*
* If we've just enqueued a TRB that is in the middle of a TD (meaning the
@@ -210,11 +255,6 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
* If we've enqueued the last TRB in a TD, make sure the following link TRBs
* have their chain bit cleared (so that each Link TRB is a separate TD).
*
- * Section 6.4.4.1 of the 0.95 spec says link TRBs cannot have the chain bit
- * set, but other sections talk about dealing with the chain bit set. This was
- * fixed in the 0.96 specification errata, but we have to assume that all 0.95
- * xHCI hardware can't handle the chain bit being cleared on a link TRB.
- *
* @more_trbs_coming: Will you enqueue more TRBs before calling
* prepare_transfer()?
*/
@@ -222,8 +262,6 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
bool more_trbs_coming)
{
u32 chain;
- union xhci_trb *next;
- unsigned int link_trb_count = 0;
chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
@@ -232,48 +270,67 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
return;
}
- next = ++(ring->enqueue);
+ ring->enqueue++;
- /* Update the dequeue pointer further if that was a link TRB */
- while (trb_is_link(next)) {
+ /*
+ * If we are in the middle of a TD or the caller plans to enqueue more
+ * TDs as one transfer (eg. control), traverse any link TRBs right now.
+ * Otherwise, enqueue can stay on a link until the next prepare_ring().
+ * This avoids enqueue entering deq_seg and simplifies ring expansion.
+ */
+ if (trb_is_link(ring->enqueue) && (chain || more_trbs_coming))
+ inc_enq_past_link(xhci, ring, chain);
+}
- /*
- * If the caller doesn't plan on enqueueing more TDs before
- * ringing the doorbell, then we don't want to give the link TRB
- * to the hardware just yet. We'll give the link TRB back in
- * prepare_ring() just before we enqueue the TD at the top of
- * the ring.
- */
- if (!chain && !more_trbs_coming)
- break;
+/*
+ * If the suspect DMA address is a TRB in this TD, this function returns that
+ * TRB's segment. Otherwise it returns 0.
+ */
+static struct xhci_segment *trb_in_td(struct xhci_td *td, dma_addr_t suspect_dma)
+{
+ dma_addr_t start_dma;
+ dma_addr_t end_seg_dma;
+ dma_addr_t end_trb_dma;
+ struct xhci_segment *cur_seg;
- /* If we're not dealing with 0.95 hardware or isoc rings on
- * AMD 0.96 host, carry over the chain bit of the previous TRB
- * (which may mean the chain bit is cleared).
- */
- if (!xhci_link_chain_quirk(xhci, ring->type)) {
- next->link.control &= cpu_to_le32(~TRB_CHAIN);
- next->link.control |= cpu_to_le32(chain);
- }
- /* Give this link TRB to the hardware */
- wmb();
- next->link.control ^= cpu_to_le32(TRB_CYCLE);
+ start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb);
+ cur_seg = td->start_seg;
- /* Toggle the cycle bit after the last ring segment. */
- if (link_trb_toggles_cycle(next))
- ring->cycle_state ^= 1;
+ do {
+ if (start_dma == 0)
+ return NULL;
+ /* We may get an event for a Link TRB in the middle of a TD */
+ end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
+ &cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
+ /* If the end TRB isn't in this segment, this is set to 0 */
+ end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb);
- ring->enq_seg = ring->enq_seg->next;
- ring->enqueue = ring->enq_seg->trbs;
- next = ring->enqueue;
+ if (end_trb_dma > 0) {
+ /* The end TRB is in this segment, so suspect should be here */
+ if (start_dma <= end_trb_dma) {
+ if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma)
+ return cur_seg;
+ } else {
+ /* Case for one segment with
+ * a TD wrapped around to the top
+ */
+ if ((suspect_dma >= start_dma &&
+ suspect_dma <= end_seg_dma) ||
+ (suspect_dma >= cur_seg->dma &&
+ suspect_dma <= end_trb_dma))
+ return cur_seg;
+ }
+ return NULL;
+ }
+ /* Might still be somewhere in this segment */
+ if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma)
+ return cur_seg;
- trace_xhci_inc_enq(ring);
+ cur_seg = cur_seg->next;
+ start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
+ } while (cur_seg != td->start_seg);
- if (link_trb_count++ > ring->num_segs) {
- xhci_warn(xhci, "%s: Ring link TRB loop\n", __func__);
- break;
- }
- }
+ return NULL;
}
/*
@@ -642,7 +699,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
int new_cycle;
dma_addr_t addr;
u64 hw_dequeue;
- bool cycle_found = false;
+ bool hw_dequeue_found = false;
bool td_last_trb_found = false;
u32 trb_sct = 0;
int ret;
@@ -658,25 +715,24 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
new_seg = ep_ring->deq_seg;
new_deq = ep_ring->dequeue;
- new_cycle = hw_dequeue & 0x1;
+ new_cycle = le32_to_cpu(td->end_trb->generic.field[3]) & TRB_CYCLE;
/*
- * We want to find the pointer, segment and cycle state of the new trb
- * (the one after current TD's end_trb). We know the cycle state at
- * hw_dequeue, so walk the ring until both hw_dequeue and end_trb are
- * found.
+ * Walk the ring until both the next TRB and hw_dequeue are found (don't
+ * move hw_dequeue back if it went forward due to a HW bug). Cycle state
+ * is loaded from a known good TRB, track later toggles to maintain it.
*/
do {
- if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq)
+ if (!hw_dequeue_found && xhci_trb_virt_to_dma(new_seg, new_deq)
== (dma_addr_t)(hw_dequeue & ~0xf)) {
- cycle_found = true;
+ hw_dequeue_found = true;
if (td_last_trb_found)
break;
}
if (new_deq == td->end_trb)
td_last_trb_found = true;
- if (cycle_found && trb_is_link(new_deq) &&
+ if (td_last_trb_found && trb_is_link(new_deq) &&
link_trb_toggles_cycle(new_deq))
new_cycle ^= 0x1;
@@ -688,7 +744,7 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
return -EINVAL;
}
- } while (!cycle_found || !td_last_trb_found);
+ } while (!hw_dequeue_found || !td_last_trb_found);
/* Don't update the ring cycle state for the producer (us). */
addr = xhci_trb_virt_to_dma(new_seg, new_deq);
@@ -1013,7 +1069,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
td->urb->stream_id);
hw_deq &= ~0xf;
- if (td->cancel_status == TD_HALTED || trb_in_td(xhci, td, hw_deq, false)) {
+ if (td->cancel_status == TD_HALTED || trb_in_td(td, hw_deq)) {
switch (td->cancel_status) {
case TD_CLEARED: /* TD is already no-op */
case TD_CLEARING_CACHE: /* set TR deq command already queued */
@@ -1103,7 +1159,7 @@ static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep)
hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0);
hw_deq &= ~0xf;
td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list);
- if (trb_in_td(ep->xhci, td, hw_deq, false))
+ if (trb_in_td(td, hw_deq))
return td;
}
return NULL;
@@ -1163,7 +1219,14 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
*/
switch (GET_EP_CTX_STATE(ep_ctx)) {
case EP_STATE_HALTED:
- xhci_dbg(xhci, "Stop ep completion raced with stall, reset ep\n");
+ xhci_dbg(xhci, "Stop ep completion raced with stall\n");
+ /*
+ * If the halt happened before Stop Endpoint failed, its transfer event
+ * should have already been handled and Reset Endpoint should be pending.
+ */
+ if (ep->ep_state & EP_HALTED)
+ goto reset_done;
+
if (ep->ep_state & EP_HAS_STREAMS) {
reset_type = EP_SOFT_RESET;
} else {
@@ -1174,8 +1237,11 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
}
/* reset ep, reset handler cleans up cancelled tds */
err = xhci_handle_halted_endpoint(xhci, ep, td, reset_type);
+ xhci_dbg(xhci, "Stop ep completion resetting ep, status %d\n", err);
if (err)
break;
+reset_done:
+ /* Reset EP handler will clean up cancelled TDs */
ep->ep_state &= ~EP_STOP_CMD_PENDING;
return;
case EP_STATE_STOPPED:
@@ -1197,16 +1263,19 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
* Stopped state, but it will soon change to Running.
*
* Assume this bug on unexpected Stop Endpoint failures.
- * Keep retrying until the EP starts and stops again, on
- * chips where this is known to help. Wait for 100ms.
+ * Keep retrying until the EP starts and stops again.
*/
- if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100)))
- break;
fallthrough;
case EP_STATE_RUNNING:
/* Race, HW handled stop ep cmd before ep was running */
xhci_dbg(xhci, "Stop ep completion ctx error, ctx_state %d\n",
GET_EP_CTX_STATE(ep_ctx));
+ /*
+ * Don't retry forever if we guessed wrong or a defective HC never starts
+ * the EP or says 'Running' but fails the command. We must give back TDs.
+ */
+ if (time_is_before_jiffies(ep->stop_time + msecs_to_jiffies(100)))
+ break;
command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
if (!command) {
@@ -1331,43 +1400,6 @@ void xhci_hc_died(struct xhci_hcd *xhci)
usb_hc_died(xhci_to_hcd(xhci));
}
-static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
- struct xhci_virt_device *dev,
- struct xhci_ring *ep_ring,
- unsigned int ep_index)
-{
- union xhci_trb *dequeue_temp;
-
- dequeue_temp = ep_ring->dequeue;
-
- /* If we get two back-to-back stalls, and the first stalled transfer
- * ends just before a link TRB, the dequeue pointer will be left on
- * the link TRB by the code in the while loop. So we have to update
- * the dequeue pointer one segment further, or we'll jump off
- * the segment into la-la-land.
- */
- if (trb_is_link(ep_ring->dequeue)) {
- ep_ring->deq_seg = ep_ring->deq_seg->next;
- ep_ring->dequeue = ep_ring->deq_seg->trbs;
- }
-
- while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
- /* We have more usable TRBs */
- ep_ring->dequeue++;
- if (trb_is_link(ep_ring->dequeue)) {
- if (ep_ring->dequeue ==
- dev->eps[ep_index].queued_deq_ptr)
- break;
- ep_ring->deq_seg = ep_ring->deq_seg->next;
- ep_ring->dequeue = ep_ring->deq_seg->trbs;
- }
- if (ep_ring->dequeue == dequeue_temp) {
- xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
- break;
- }
- }
-}
-
/*
* When we get a completion for a Set Transfer Ring Dequeue Pointer command,
* we need to clear the set deq pending flag in the endpoint ring state, so that
@@ -1472,8 +1504,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
/* Update the ring's dequeue segment and dequeue pointer
* to reflect the new position.
*/
- update_ring_for_set_deq_completion(xhci, ep->vdev,
- ep_ring, ep_index);
+ ep_ring->deq_seg = ep->queued_deq_seg;
+ ep_ring->dequeue = ep->queued_deq_ptr;
} else {
xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n");
xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n",
@@ -1650,12 +1682,13 @@ static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci,
NEC_FW_MINOR(le32_to_cpu(event->status)));
}
-static void xhci_complete_del_and_free_cmd(struct xhci_command *cmd, u32 status)
+static void xhci_complete_del_and_free_cmd(struct xhci_command *cmd, u32 comp_code, u32 comp_param)
{
list_del(&cmd->cmd_list);
if (cmd->completion) {
- cmd->status = status;
+ cmd->status = comp_code;
+ cmd->comp_param = comp_param;
complete(cmd->completion);
} else {
kfree(cmd);
@@ -1667,7 +1700,7 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci)
struct xhci_command *cur_cmd, *tmp_cmd;
xhci->current_cmd = NULL;
list_for_each_entry_safe(cur_cmd, tmp_cmd, &xhci->cmd_list, cmd_list)
- xhci_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED);
+ xhci_complete_del_and_free_cmd(cur_cmd, COMP_COMMAND_ABORTED, 0);
}
void xhci_handle_command_timeout(struct work_struct *work)
@@ -1752,6 +1785,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
struct xhci_event_cmd *event)
{
unsigned int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+ u32 status = le32_to_cpu(event->status);
u64 cmd_dma;
dma_addr_t cmd_dequeue_dma;
u32 cmd_comp_code;
@@ -1880,7 +1914,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
}
event_handled:
- xhci_complete_del_and_free_cmd(cmd, cmd_comp_code);
+ xhci_complete_del_and_free_cmd(cmd, cmd_comp_code, COMP_PARAM(status));
inc_deq(xhci, xhci->cmd_ring);
}
@@ -2113,67 +2147,6 @@ cleanup:
spin_lock(&xhci->lock);
}
-/*
- * If the suspect DMA address is a TRB in this TD, this function returns that
- * TRB's segment. Otherwise it returns 0.
- */
-struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_addr_t suspect_dma,
- bool debug)
-{
- dma_addr_t start_dma;
- dma_addr_t end_seg_dma;
- dma_addr_t end_trb_dma;
- struct xhci_segment *cur_seg;
-
- start_dma = xhci_trb_virt_to_dma(td->start_seg, td->start_trb);
- cur_seg = td->start_seg;
-
- do {
- if (start_dma == 0)
- return NULL;
- /* We may get an event for a Link TRB in the middle of a TD */
- end_seg_dma = xhci_trb_virt_to_dma(cur_seg,
- &cur_seg->trbs[TRBS_PER_SEGMENT - 1]);
- /* If the end TRB isn't in this segment, this is set to 0 */
- end_trb_dma = xhci_trb_virt_to_dma(cur_seg, td->end_trb);
-
- if (debug)
- xhci_warn(xhci,
- "Looking for event-dma %016llx trb-start %016llx trb-end %016llx seg-start %016llx seg-end %016llx\n",
- (unsigned long long)suspect_dma,
- (unsigned long long)start_dma,
- (unsigned long long)end_trb_dma,
- (unsigned long long)cur_seg->dma,
- (unsigned long long)end_seg_dma);
-
- if (end_trb_dma > 0) {
- /* The end TRB is in this segment, so suspect should be here */
- if (start_dma <= end_trb_dma) {
- if (suspect_dma >= start_dma && suspect_dma <= end_trb_dma)
- return cur_seg;
- } else {
- /* Case for one segment with
- * a TD wrapped around to the top
- */
- if ((suspect_dma >= start_dma &&
- suspect_dma <= end_seg_dma) ||
- (suspect_dma >= cur_seg->dma &&
- suspect_dma <= end_trb_dma))
- return cur_seg;
- }
- return NULL;
- } else {
- /* Might still be somewhere in this segment */
- if (suspect_dma >= start_dma && suspect_dma <= end_seg_dma)
- return cur_seg;
- }
- cur_seg = cur_seg->next;
- start_dma = xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]);
- } while (cur_seg != td->start_seg);
-
- return NULL;
-}
-
static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td,
struct xhci_virt_ep *ep)
{
@@ -2473,6 +2446,12 @@ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
if (ep_trb != td->end_trb)
td->error_mid_td = true;
break;
+ case COMP_MISSED_SERVICE_ERROR:
+ frame->status = -EXDEV;
+ sum_trbs_for_length = true;
+ if (ep_trb != td->end_trb)
+ td->error_mid_td = true;
+ break;
case COMP_INCOMPATIBLE_DEVICE_ERROR:
case COMP_STALL_ERROR:
frame->status = -EPROTO;
@@ -2641,6 +2620,22 @@ static int handle_transferless_tx_event(struct xhci_hcd *xhci, struct xhci_virt_
return 0;
}
+static bool xhci_spurious_success_tx_event(struct xhci_hcd *xhci,
+ struct xhci_ring *ring)
+{
+ switch (ring->old_trb_comp_code) {
+ case COMP_SHORT_PACKET:
+ return xhci->quirks & XHCI_SPURIOUS_SUCCESS;
+ case COMP_USB_TRANSACTION_ERROR:
+ case COMP_BABBLE_DETECTED_ERROR:
+ case COMP_ISOCH_BUFFER_OVERRUN:
+ return xhci->quirks & XHCI_ETRON_HOST &&
+ ring->type == TYPE_ISOC;
+ default:
+ return false;
+ }
+}
+
/*
* If this function returns an error condition, it means it got a Transfer
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@@ -2661,6 +2656,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
int status = -EINPROGRESS;
struct xhci_ep_ctx *ep_ctx;
u32 trb_comp_code;
+ bool ring_xrun_event = false;
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
@@ -2694,8 +2690,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
case COMP_SUCCESS:
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
trb_comp_code = COMP_SHORT_PACKET;
- xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u with last td short %d\n",
- slot_id, ep_index, ep_ring->last_td_was_short);
+ xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u with last td comp code %d\n",
+ slot_id, ep_index, ep_ring->old_trb_comp_code);
}
break;
case COMP_SHORT_PACKET:
@@ -2767,14 +2763,12 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* Underrun Event for OUT Isoch endpoint.
*/
xhci_dbg(xhci, "Underrun event on slot %u ep %u\n", slot_id, ep_index);
- if (ep->skip)
- break;
- return 0;
+ ring_xrun_event = true;
+ break;
case COMP_RING_OVERRUN:
xhci_dbg(xhci, "Overrun event on slot %u ep %u\n", slot_id, ep_index);
- if (ep->skip)
- break;
- return 0;
+ ring_xrun_event = true;
+ break;
case COMP_MISSED_SERVICE_ERROR:
/*
* When encounter missed service error, one or more isoc tds
@@ -2784,9 +2778,9 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
ep->skip = true;
xhci_dbg(xhci,
- "Miss service interval error for slot %u ep %u, set skip flag\n",
- slot_id, ep_index);
- return 0;
+ "Miss service interval error for slot %u ep %u, set skip flag%s\n",
+ slot_id, ep_index, ep_trb_dma ? ", skip now" : "");
+ break;
case COMP_NO_PING_RESPONSE_ERROR:
ep->skip = true;
xhci_dbg(xhci,
@@ -2829,11 +2823,15 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
td = list_first_entry_or_null(&ep_ring->td_list, struct xhci_td, td_list);
- if (td && td->error_mid_td && !trb_in_td(xhci, td, ep_trb_dma, false)) {
+ if (td && td->error_mid_td && !trb_in_td(td, ep_trb_dma)) {
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
xhci_dequeue_td(xhci, td, ep_ring, td->status);
}
+ /* If the TRB pointer is NULL, missed TDs will be skipped on the next event */
+ if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !ep_trb_dma)
+ return 0;
+
if (list_empty(&ep_ring->td_list)) {
/*
* Don't print wanings if ring is empty due to a stopped endpoint generating an
@@ -2843,7 +2841,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
if (trb_comp_code != COMP_STOPPED &&
trb_comp_code != COMP_STOPPED_LENGTH_INVALID &&
- !ep_ring->last_td_was_short) {
+ !ring_xrun_event &&
+ !xhci_spurious_success_tx_event(xhci, ep_ring)) {
xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n",
slot_id, ep_index);
}
@@ -2857,14 +2856,31 @@ static int handle_tx_event(struct xhci_hcd *xhci,
td_list);
/* Is this a TRB in the currently executing TD? */
- ep_seg = trb_in_td(xhci, td, ep_trb_dma, false);
+ ep_seg = trb_in_td(td, ep_trb_dma);
if (!ep_seg) {
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
+ /* this event is unlikely to match any TD, don't skip them all */
+ if (trb_comp_code == COMP_STOPPED_LENGTH_INVALID)
+ return 0;
+
skip_isoc_td(xhci, td, ep, status);
- if (!list_empty(&ep_ring->td_list))
+
+ if (!list_empty(&ep_ring->td_list)) {
+ if (ring_xrun_event) {
+ /*
+ * If we are here, we are on xHCI 1.0 host with no
+ * idea how many TDs were missed or where the xrun
+ * occurred. New TDs may have been added after the
+ * xrun, so skip only one TD to be safe.
+ */
+ xhci_dbg(xhci, "Skipped one TD for slot %u ep %u",
+ slot_id, ep_index);
+ return 0;
+ }
continue;
+ }
xhci_dbg(xhci, "All TDs skipped for slot %u ep %u. Clear skip flag.\n",
slot_id, ep_index);
@@ -2873,6 +2889,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
goto check_endpoint_halted;
}
+ /* TD was queued after xrun, maybe xrun was on a link, don't panic yet */
+ if (ring_xrun_event)
+ return 0;
+
/*
* Skip the Force Stopped Event. The 'ep_trb' of FSE is not in the current
* TD pointed by 'ep_ring->dequeue' because that the hardware dequeue
@@ -2887,21 +2907,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
/*
* Some hosts give a spurious success event after a short
- * transfer. Ignore it.
+ * transfer or error on last TRB. Ignore it.
*/
- if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
- ep_ring->last_td_was_short) {
- ep_ring->last_td_was_short = false;
+ if (xhci_spurious_success_tx_event(xhci, ep_ring)) {
+ xhci_dbg(xhci, "Spurious event dma %pad, comp_code %u after %u\n",
+ &ep_trb_dma, trb_comp_code, ep_ring->old_trb_comp_code);
+ ep_ring->old_trb_comp_code = 0;
return 0;
}
/* HC is busted, give up! */
- xhci_err(xhci,
- "ERROR Transfer event TRB DMA ptr not part of current TD ep_index %d comp_code %u\n",
- ep_index, trb_comp_code);
- trb_in_td(xhci, td, ep_trb_dma, true);
-
- return -ESHUTDOWN;
+ goto debug_finding_td;
}
if (ep->skip) {
@@ -2919,10 +2935,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
} while (ep->skip);
- if (trb_comp_code == COMP_SHORT_PACKET)
- ep_ring->last_td_was_short = true;
- else
- ep_ring->last_td_was_short = false;
+ ep_ring->old_trb_comp_code = trb_comp_code;
+
+ /* Get out if a TD was queued at enqueue after the xrun occurred */
+ if (ring_xrun_event)
+ return 0;
ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)];
trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, ep_trb_dma);
@@ -2954,6 +2971,17 @@ check_endpoint_halted:
return 0;
+debug_finding_td:
+ xhci_err(xhci, "Event dma %pad for ep %d status %d not part of TD at %016llx - %016llx\n",
+ &ep_trb_dma, ep_index, trb_comp_code,
+ (unsigned long long)xhci_trb_virt_to_dma(td->start_seg, td->start_trb),
+ (unsigned long long)xhci_trb_virt_to_dma(td->end_seg, td->end_trb));
+
+ xhci_for_each_ring_seg(ep_ring->first_seg, ep_seg)
+ xhci_warn(xhci, "Ring seg %u dma %pad\n", ep_seg->num, &ep_seg->dma);
+
+ return -ESHUTDOWN;
+
err_out:
xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n",
(unsigned long long) xhci_trb_virt_to_dma(
@@ -3213,7 +3241,6 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
u32 ep_state, unsigned int num_trbs, gfp_t mem_flags)
{
- unsigned int link_trb_count = 0;
unsigned int new_segs = 0;
/* Make sure the endpoint has been added to xHC schedule */
@@ -3261,33 +3288,9 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
}
}
- while (trb_is_link(ep_ring->enqueue)) {
- /* If we're not dealing with 0.95 hardware or isoc rings
- * on AMD 0.96 host, clear the chain bit.
- */
- if (!xhci_link_chain_quirk(xhci, ep_ring->type))
- ep_ring->enqueue->link.control &=
- cpu_to_le32(~TRB_CHAIN);
- else
- ep_ring->enqueue->link.control |=
- cpu_to_le32(TRB_CHAIN);
-
- wmb();
- ep_ring->enqueue->link.control ^= cpu_to_le32(TRB_CYCLE);
-
- /* Toggle the cycle bit after the last ring segment. */
- if (link_trb_toggles_cycle(ep_ring->enqueue))
- ep_ring->cycle_state ^= 1;
-
- ep_ring->enq_seg = ep_ring->enq_seg->next;
- ep_ring->enqueue = ep_ring->enq_seg->trbs;
-
- /* prevent infinite loop if all first trbs are link trbs */
- if (link_trb_count++ > ep_ring->num_segs) {
- xhci_warn(xhci, "Ring is an endless link TRB loop\n");
- return -EINVAL;
- }
- }
+ /* Ensure that new TRBs won't overwrite a link */
+ if (trb_is_link(ep_ring->enqueue))
+ inc_enq_past_link(xhci, ep_ring, 0);
if (last_trb_on_seg(ep_ring->enq_seg, ep_ring->enqueue)) {
xhci_warn(xhci, "Missing link TRB at end of ring segment\n");
@@ -3439,8 +3442,8 @@ static void check_interval(struct urb *urb, struct xhci_ep_ctx *ep_ctx)
if (xhci_interval != ep_interval) {
dev_dbg_ratelimited(&urb->dev->dev,
"Driver uses different interval (%d microframe%s) than xHCI (%d microframe%s)\n",
- ep_interval, ep_interval == 1 ? "" : "s",
- xhci_interval, xhci_interval == 1 ? "" : "s");
+ ep_interval, str_plural(ep_interval),
+ xhci_interval, str_plural(xhci_interval));
urb->interval = xhci_interval;
/* Convert back to frames for LS/FS devices */
if (urb->dev->speed == USB_SPEED_LOW ||
@@ -3773,7 +3776,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
* enqueue a No Op TRB, this can prevent the Setup and Data Stage
* TRB to be breaked by the Link TRB.
*/
- if (trb_is_link(ep_ring->enqueue + 1)) {
+ if (last_trb_on_seg(ep_ring->enq_seg, ep_ring->enqueue + 1)) {
field = TRB_TYPE(TRB_TR_NOOP) | ep_ring->cycle_state;
queue_trb(xhci, ep_ring, false, 0, 0,
TRB_INTR_TARGET(0), field);
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index 06ae193ec874..0c7af44d4dae 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -26,6 +26,7 @@
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/usb/otg.h>
#include <linux/usb/phy.h>
#include <linux/usb/role.h>
@@ -724,7 +725,7 @@ static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra,
if (err < 0) {
dev_err(dev,
"failed to %s LFPS detection on USB3#%u: %d\n",
- enable ? "enable" : "disable", port, err);
+ str_enable_disable(enable), port, err);
rsp.cmd = MBOX_CMD_NAK;
} else {
rsp.cmd = MBOX_CMD_ACK;
@@ -1349,7 +1350,7 @@ static void tegra_xhci_id_work(struct work_struct *work)
u32 status;
int ret;
- dev_dbg(tegra->dev, "host mode %s\n", tegra->host_mode ? "on" : "off");
+ dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode));
mutex_lock(&tegra->lock);
@@ -1363,6 +1364,7 @@ static void tegra_xhci_id_work(struct work_struct *work)
tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(tegra->padctl,
tegra->otg_usb2_port);
+ pm_runtime_get_sync(tegra->dev);
if (tegra->host_mode) {
/* switch to host mode */
if (tegra->otg_usb3_port >= 0) {
@@ -1392,6 +1394,7 @@ static void tegra_xhci_id_work(struct work_struct *work)
}
tegra_xhci_set_port_power(tegra, true, true);
+ pm_runtime_mark_last_busy(tegra->dev);
} else {
if (tegra->otg_usb3_port >= 0)
@@ -1399,6 +1402,7 @@ static void tegra_xhci_id_work(struct work_struct *work)
tegra_xhci_set_port_power(tegra, true, false);
}
+ pm_runtime_put_autosuspend(tegra->dev);
}
#if IS_ENABLED(CONFIG_PM) || IS_ENABLED(CONFIG_PM_SLEEP)
@@ -1667,7 +1671,7 @@ static int tegra_xusb_probe(struct platform_device *pdev)
goto put_padctl;
}
- if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
+ if (!of_property_present(pdev->dev.of_node, "power-domains")) {
tegra->host_rst = devm_reset_control_get(&pdev->dev,
"xusb_host");
if (IS_ERR(tegra->host_rst)) {
@@ -2161,11 +2165,11 @@ static void tegra_xhci_program_utmi_power_lp0_exit(struct tegra_xusb *tegra)
}
}
-static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool runtime)
+static int tegra_xusb_enter_elpg(struct tegra_xusb *tegra, bool is_auto_resume)
{
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
struct device *dev = tegra->dev;
- bool wakeup = runtime ? true : device_may_wakeup(dev);
+ bool wakeup = is_auto_resume ? true : device_may_wakeup(dev);
unsigned int i;
int err;
u32 usbcmd;
@@ -2231,11 +2235,11 @@ out:
return err;
}
-static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
+static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool is_auto_resume)
{
struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
struct device *dev = tegra->dev;
- bool wakeup = runtime ? true : device_may_wakeup(dev);
+ bool wakeup = is_auto_resume ? true : device_may_wakeup(dev);
unsigned int i;
u32 usbcmd;
int err;
@@ -2286,7 +2290,7 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime)
if (wakeup)
tegra_xhci_disable_phy_sleepwalk(tegra);
- err = xhci_resume(xhci, runtime ? PMSG_AUTO_RESUME : PMSG_RESUME);
+ err = xhci_resume(xhci, false, is_auto_resume);
if (err < 0) {
dev_err(tegra->dev, "failed to resume XHCI: %d\n", err);
goto disable_phy;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 55086d63e7ef..90eb491267b5 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -17,6 +17,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/dmi.h>
#include <linux/dma-mapping.h>
@@ -321,7 +322,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)
xhci_info(xhci, "Fault detected\n");
}
-static int xhci_enable_interrupter(struct xhci_interrupter *ir)
+int xhci_enable_interrupter(struct xhci_interrupter *ir)
{
u32 iman;
@@ -334,7 +335,7 @@ static int xhci_enable_interrupter(struct xhci_interrupter *ir)
return 0;
}
-static int xhci_disable_interrupter(struct xhci_interrupter *ir)
+int xhci_disable_interrupter(struct xhci_interrupter *ir)
{
u32 iman;
@@ -626,7 +627,7 @@ void xhci_stop(struct usb_hcd *hcd)
/* Deleting Compliance Mode Recovery Timer */
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
(!(xhci_all_ports_seen_u0(xhci)))) {
- del_timer_sync(&xhci->comp_mode_recovery_timer);
+ timer_delete_sync(&xhci->comp_mode_recovery_timer);
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"%s: compliance mode recovery timer deleted",
__func__);
@@ -671,11 +672,11 @@ void xhci_shutdown(struct usb_hcd *hcd)
xhci_dbg(xhci, "%s: stopping usb%d port polling.\n",
__func__, hcd->self.busnum);
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
if (xhci->shared_hcd) {
clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
- del_timer_sync(&xhci->shared_hcd->rh_timer);
+ timer_delete_sync(&xhci->shared_hcd->rh_timer);
}
spin_lock_irq(&xhci->lock);
@@ -907,10 +908,10 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
xhci_dbg(xhci, "%s: stopping usb%d port polling.\n",
__func__, hcd->self.busnum);
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- del_timer_sync(&hcd->rh_timer);
+ timer_delete_sync(&hcd->rh_timer);
if (xhci->shared_hcd) {
clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
- del_timer_sync(&xhci->shared_hcd->rh_timer);
+ timer_delete_sync(&xhci->shared_hcd->rh_timer);
}
if (xhci->quirks & XHCI_SUSPEND_DELAY)
@@ -977,7 +978,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
*/
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
(!(xhci_all_ports_seen_u0(xhci)))) {
- del_timer_sync(&xhci->comp_mode_recovery_timer);
+ timer_delete_sync(&xhci->comp_mode_recovery_timer);
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"%s: compliance mode recovery timer deleted",
__func__);
@@ -993,16 +994,14 @@ EXPORT_SYMBOL_GPL(xhci_suspend);
* This is called when the machine transition from S3/S4 mode.
*
*/
-int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
+int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume)
{
- bool hibernated = (msg.event == PM_EVENT_RESTORE);
u32 command, temp = 0;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
int retval = 0;
bool comp_timer_running = false;
bool pending_portevent = false;
bool suspended_usb3_devs = false;
- bool reinit_xhc = false;
if (!hcd->state)
return 0;
@@ -1021,10 +1020,10 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
spin_lock_irq(&xhci->lock);
- if (hibernated || xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend)
- reinit_xhc = true;
+ if (xhci->quirks & XHCI_RESET_ON_RESUME || xhci->broken_suspend)
+ power_lost = true;
- if (!reinit_xhc) {
+ if (!power_lost) {
/*
* Some controllers might lose power during suspend, so wait
* for controller not ready bit to clear, just as in xHC init.
@@ -1064,15 +1063,15 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
/* re-initialize the HC on Restore Error, or Host Controller Error */
if ((temp & (STS_SRE | STS_HCE)) &&
!(xhci->xhc_state & XHCI_STATE_REMOVING)) {
- reinit_xhc = true;
- if (!xhci->broken_suspend)
+ if (!power_lost)
xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp);
+ power_lost = true;
}
- if (reinit_xhc) {
+ if (power_lost) {
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
!(xhci_all_ports_seen_u0(xhci))) {
- del_timer_sync(&xhci->comp_mode_recovery_timer);
+ timer_delete_sync(&xhci->comp_mode_recovery_timer);
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
"Compliance Mode Recovery Timer deleted!");
}
@@ -1167,8 +1166,7 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
pending_portevent = xhci_pending_portevent(xhci);
- if (suspended_usb3_devs && !pending_portevent &&
- msg.event == PM_EVENT_AUTO_RESUME) {
+ if (suspended_usb3_devs && !pending_portevent && is_auto_resume) {
msleep(120);
pending_portevent = xhci_pending_portevent(xhci);
}
@@ -4527,7 +4525,7 @@ static int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
hlpm_addr = ports[port_num]->addr + PORTHLPMC;
xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n",
- enable ? "enable" : "disable", port_num + 1);
+ str_enable_disable(enable), port_num + 1);
if (enable) {
/* Host supports BESL timeout instead of HIRD */
@@ -4758,8 +4756,8 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
*/
if (timeout_ns <= USB3_LPM_U1_MAX_TIMEOUT)
return timeout_ns;
- dev_dbg(&udev->dev, "Hub-initiated U1 disabled "
- "due to long timeout %llu ms\n", timeout_ns);
+ dev_dbg(&udev->dev, "Hub-initiated U1 disabled due to long timeout %lluus\n",
+ timeout_ns);
return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U1);
}
@@ -4816,8 +4814,8 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
*/
if (timeout_ns <= USB3_LPM_U2_MAX_TIMEOUT)
return timeout_ns;
- dev_dbg(&udev->dev, "Hub-initiated U2 disabled "
- "due to long timeout %llu ms\n", timeout_ns);
+ dev_dbg(&udev->dev, "Hub-initiated U2 disabled due to long timeout %lluus\n",
+ timeout_ns * 256);
return xhci_get_timeout_no_hub_lpm(udev, USB3_LPM_U2);
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index dba1db259cd3..242ab9fbc8ae 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -211,6 +211,9 @@ struct xhci_op_regs {
#define CONFIG_CIE (1 << 9)
/* bits 10:31 - reserved and should be preserved */
+/* bits 15:0 - HCD page shift bit */
+#define XHCI_PAGE_SIZE_MASK 0xffff
+
/**
* struct xhci_intr_reg - Interrupt Register Set
* @irq_pending: IMAN - Interrupt Management Register. Used to enable
@@ -529,6 +532,7 @@ struct xhci_command {
/* Input context for changing device state */
struct xhci_container_ctx *in_ctx;
u32 status;
+ u32 comp_param;
int slot_id;
/* If completion is null, no one is waiting on this command
* and the structure can be freed after the command completes.
@@ -959,6 +963,9 @@ struct xhci_event_cmd {
__le32 flags;
};
+/* status bitmasks */
+#define COMP_PARAM(p) ((p) & 0xffffff) /* Command Completion Parameter */
+
/* Address device - disable SetAddress */
#define TRB_BSR (1<<9)
@@ -1367,7 +1374,7 @@ struct xhci_ring {
unsigned int num_trbs_free; /* used only by xhci DbC */
unsigned int bounce_buf_len;
enum xhci_ring_type type;
- bool last_td_was_short;
+ u32 old_trb_comp_code;
struct radix_tree_root *trb_address_map;
};
@@ -1510,10 +1517,7 @@ struct xhci_hcd {
u16 max_interrupters;
/* imod_interval in ns (I * 250ns) */
u32 imod_interval;
- /* 4KB min, 128MB max */
- int page_size;
- /* Valid values are 12 to 20, inclusive */
- int page_shift;
+ u32 page_size;
/* MSI-X/MSI vectors */
int nvecs;
/* optional clocks */
@@ -1755,11 +1759,20 @@ static inline void xhci_write_64(struct xhci_hcd *xhci,
}
-/* Link TRB chain should always be set on 0.95 hosts, and AMD 0.96 ISOC rings */
+/*
+ * Reportedly, some chapters of v0.95 spec said that Link TRB always has its chain bit set.
+ * Other chapters and later specs say that it should only be set if the link is inside a TD
+ * which continues from the end of one segment to the next segment.
+ *
+ * Some 0.95 hardware was found to misbehave if any link TRB doesn't have the chain bit set.
+ *
+ * 0.96 hardware from AMD and NEC was found to ignore unchained isochronous link TRBs when
+ * "resynchronizing the pipe" after a Missed Service Error.
+ */
static inline bool xhci_link_chain_quirk(struct xhci_hcd *xhci, enum xhci_ring_type type)
{
return (xhci->quirks & XHCI_LINK_TRB_QUIRK) ||
- (type == TYPE_ISOC && (xhci->quirks & XHCI_AMD_0x96_HOST));
+ (type == TYPE_ISOC && (xhci->quirks & (XHCI_AMD_0x96_HOST | XHCI_NEC_HOST)));
}
/* xHCI debugging */
@@ -1866,7 +1879,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
int xhci_ext_cap_init(struct xhci_hcd *xhci);
int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup);
-int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg);
+int xhci_resume(struct xhci_hcd *xhci, bool power_lost, bool is_auto_resume);
irqreturn_t xhci_irq(struct usb_hcd *hcd);
irqreturn_t xhci_msi_irq(int irq, void *hcd);
@@ -1877,11 +1890,11 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
struct usb_tt *tt, gfp_t mem_flags);
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
u32 imod_interval);
+int xhci_enable_interrupter(struct xhci_interrupter *ir);
+int xhci_disable_interrupter(struct xhci_interrupter *ir);
/* xHCI ring, segment, TRB, and TD functions */
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
-struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td,
- dma_addr_t suspect_dma, bool debug);
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
void xhci_ring_cmd_db(struct xhci_hcd *xhci);
int xhci_queue_slot_control(struct xhci_hcd *xhci, struct xhci_command *cmd,
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index 9f758241d9d3..934ec5310fb9 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -322,7 +322,7 @@ static inline void mts_urb_abort(struct mts_desc* desc) {
usb_kill_urb( desc->urb );
}
-static int mts_slave_alloc (struct scsi_device *s)
+static int mts_sdev_init (struct scsi_device *s)
{
s->inquiry_len = 0x24;
return 0;
@@ -626,7 +626,7 @@ static const struct scsi_host_template mts_scsi_host_template = {
.this_id = -1,
.emulated = 1,
.dma_alignment = 511,
- .slave_alloc = mts_slave_alloc,
+ .sdev_init = mts_sdev_init,
.max_sectors= 256, /* 128 K */
};
diff --git a/drivers/usb/isp1760/isp1760-hcd.c b/drivers/usb/isp1760/isp1760-hcd.c
index add2d2e3b61b..8dcd9cc22413 100644
--- a/drivers/usb/isp1760/isp1760-hcd.c
+++ b/drivers/usb/isp1760/isp1760-hcd.c
@@ -2458,7 +2458,7 @@ static void isp1760_stop(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
- del_timer(&errata2_timer);
+ timer_delete(&errata2_timer);
isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1,
NULL, 0);
diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c
index 5cafd23345ca..2af89ee28baa 100644
--- a/drivers/usb/isp1760/isp1760-udc.c
+++ b/drivers/usb/isp1760/isp1760-udc.c
@@ -1145,7 +1145,7 @@ static void isp1760_udc_disconnect(struct isp1760_udc *udc)
if (udc->driver->disconnect)
udc->driver->disconnect(&udc->gadget);
- del_timer(&udc->vbus_timer);
+ timer_delete(&udc->vbus_timer);
/* TODO Reset all endpoints ? */
}
@@ -1314,7 +1314,7 @@ static int isp1760_udc_stop(struct usb_gadget *gadget)
dev_dbg(udc->isp->dev, "%s\n", __func__);
- del_timer_sync(&udc->vbus_timer);
+ timer_delete_sync(&udc->vbus_timer);
isp1760_reg_write(udc->regs, mode_reg, 0);
diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c
index 75ac3c6aa92d..86f25bcb6425 100644
--- a/drivers/usb/misc/onboard_usb_dev.c
+++ b/drivers/usb/misc/onboard_usb_dev.c
@@ -36,9 +36,10 @@
#define USB5744_CMD_CREG_ACCESS 0x99
#define USB5744_CMD_CREG_ACCESS_LSB 0x37
#define USB5744_CREG_MEM_ADDR 0x00
+#define USB5744_CREG_MEM_RD_ADDR 0x04
#define USB5744_CREG_WRITE 0x00
-#define USB5744_CREG_RUNTIMEFLAGS2 0x41
-#define USB5744_CREG_RUNTIMEFLAGS2_LSB 0x1D
+#define USB5744_CREG_READ 0x01
+#define USB5744_CREG_RUNTIMEFLAGS2 0x411D
#define USB5744_CREG_BYPASS_UDC_SUSPEND BIT(3)
static void onboard_dev_attach_usb_driver(struct work_struct *work);
@@ -309,11 +310,88 @@ static void onboard_dev_attach_usb_driver(struct work_struct *work)
pr_err("Failed to attach USB driver: %pe\n", ERR_PTR(err));
}
+#if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744)
+static int onboard_dev_5744_i2c_read_byte(struct i2c_client *client, u16 addr, u8 *data)
+{
+ struct i2c_msg msg[2];
+ u8 rd_buf[3];
+ int ret;
+
+ u8 wr_buf[7] = {0, USB5744_CREG_MEM_ADDR, 4,
+ USB5744_CREG_READ, 1,
+ addr >> 8 & 0xff,
+ addr & 0xff};
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = sizeof(wr_buf);
+ msg[0].buf = wr_buf;
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0)
+ return ret;
+
+ wr_buf[0] = USB5744_CMD_CREG_ACCESS;
+ wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB;
+ wr_buf[2] = 0;
+ msg[0].len = 3;
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0)
+ return ret;
+
+ wr_buf[0] = 0;
+ wr_buf[1] = USB5744_CREG_MEM_RD_ADDR;
+ msg[0].len = 2;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = 2;
+ msg[1].buf = rd_buf;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret < 0)
+ return ret;
+ *data = rd_buf[1];
+
+ return 0;
+}
+
+static int onboard_dev_5744_i2c_write_byte(struct i2c_client *client, u16 addr, u8 data)
+{
+ struct i2c_msg msg[2];
+ int ret;
+
+ u8 wr_buf[8] = {0, USB5744_CREG_MEM_ADDR, 5,
+ USB5744_CREG_WRITE, 1,
+ addr >> 8 & 0xff,
+ addr & 0xff,
+ data};
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = sizeof(wr_buf);
+ msg[0].buf = wr_buf;
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0)
+ return ret;
+
+ msg[0].len = 3;
+ wr_buf[0] = USB5744_CMD_CREG_ACCESS;
+ wr_buf[1] = USB5744_CMD_CREG_ACCESS_LSB;
+ wr_buf[2] = 0;
+
+ ret = i2c_transfer(client->adapter, msg, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int onboard_dev_5744_i2c_init(struct i2c_client *client)
{
-#if IS_ENABLED(CONFIG_USB_ONBOARD_DEV_USB5744)
struct device *dev = &client->dev;
int ret;
+ u8 reg;
/*
* Set BYPASS_UDC_SUSPEND bit to ensure MCU is always enabled
@@ -321,20 +399,16 @@ static int onboard_dev_5744_i2c_init(struct i2c_client *client)
* The command writes 5 bytes to memory and single data byte in
* configuration register.
*/
- char wr_buf[7] = {USB5744_CREG_MEM_ADDR, 5,
- USB5744_CREG_WRITE, 1,
- USB5744_CREG_RUNTIMEFLAGS2,
- USB5744_CREG_RUNTIMEFLAGS2_LSB,
- USB5744_CREG_BYPASS_UDC_SUSPEND};
-
- ret = i2c_smbus_write_block_data(client, 0, sizeof(wr_buf), wr_buf);
+ ret = onboard_dev_5744_i2c_read_byte(client,
+ USB5744_CREG_RUNTIMEFLAGS2, &reg);
if (ret)
- return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n");
+ return dev_err_probe(dev, ret, "CREG_RUNTIMEFLAGS2 read failed\n");
- ret = i2c_smbus_write_word_data(client, USB5744_CMD_CREG_ACCESS,
- USB5744_CMD_CREG_ACCESS_LSB);
+ reg |= USB5744_CREG_BYPASS_UDC_SUSPEND;
+ ret = onboard_dev_5744_i2c_write_byte(client,
+ USB5744_CREG_RUNTIMEFLAGS2, reg);
if (ret)
- return dev_err_probe(dev, ret, "Configuration Register Access Command failed\n");
+ return dev_err_probe(dev, ret, "BYPASS_UDC_SUSPEND bit configuration failed\n");
/* Send SMBus command to boot hub. */
ret = i2c_smbus_write_word_data(client, USB5744_CMD_ATTACH,
@@ -343,10 +417,13 @@ static int onboard_dev_5744_i2c_init(struct i2c_client *client)
return dev_err_probe(dev, ret, "USB Attach with SMBus command failed\n");
return ret;
+}
#else
+static int onboard_dev_5744_i2c_init(struct i2c_client *client)
+{
return -ENODEV;
-#endif
}
+#endif
static int onboard_dev_probe(struct platform_device *pdev)
{
@@ -569,8 +646,14 @@ static void onboard_dev_usbdev_disconnect(struct usb_device *udev)
}
static const struct usb_device_id onboard_dev_id_table[] = {
- { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 HUB */
- { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6500) }, /* CYUSB330x 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6502) }, /* CYUSB330x 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6503) }, /* CYUSB33{0,1}x 2.0 HUB, Vendor Mode */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB331x 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB331x 2.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6507) }, /* CYUSB332x 2.0 HUB, Vendor Mode */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6508) }, /* CYUSB332x 3.0 HUB */
+ { USB_DEVICE(VENDOR_ID_CYPRESS, 0x650a) }, /* CYUSB332x 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_CYPRESS, 0x6570) }, /* CY7C6563x 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 HUB */
{ USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 HUB */
diff --git a/drivers/usb/misc/onboard_usb_dev.h b/drivers/usb/misc/onboard_usb_dev.h
index 317b3eb99c02..933797a7e084 100644
--- a/drivers/usb/misc/onboard_usb_dev.h
+++ b/drivers/usb/misc/onboard_usb_dev.h
@@ -23,6 +23,13 @@ static const struct onboard_dev_pdata microchip_usb424_data = {
.is_hub = true,
};
+static const struct onboard_dev_pdata microchip_usb2514_data = {
+ .reset_us = 1,
+ .num_supplies = 2,
+ .supply_names = { "vdd", "vdda" },
+ .is_hub = true,
+};
+
static const struct onboard_dev_pdata microchip_usb5744_data = {
.reset_us = 0,
.power_on_delay_us = 10000,
@@ -96,7 +103,7 @@ static const struct onboard_dev_pdata xmos_xvf3500_data = {
static const struct of_device_id onboard_dev_match[] = {
{ .compatible = "usb424,2412", .data = &microchip_usb424_data, },
- { .compatible = "usb424,2514", .data = &microchip_usb424_data, },
+ { .compatible = "usb424,2514", .data = &microchip_usb2514_data, },
{ .compatible = "usb424,2517", .data = &microchip_usb424_data, },
{ .compatible = "usb424,2744", .data = &microchip_usb5744_data, },
{ .compatible = "usb424,5744", .data = &microchip_usb5744_data, },
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index e24cdb667307..4fb453ca5450 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -636,10 +636,8 @@ static int usb251xb_probe(struct usb251xb *hub)
if (np && usb_data) {
err = usb251xb_get_ofdata(hub, usb_data);
- if (err) {
- dev_err(dev, "failed to get ofdata: %d\n", err);
- return err;
- }
+ if (err)
+ return dev_err_probe(dev, err, "failed to get ofdata\n");
}
/*
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 8d379ae835bc..853a5f082a70 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -626,7 +626,7 @@ static int perform_sglist(
mod_timer(&timeout.timer, jiffies +
msecs_to_jiffies(SIMPLE_IO_TIMEOUT));
usb_sg_wait(req);
- if (!del_timer_sync(&timeout.timer))
+ if (!timer_delete_sync(&timeout.timer))
retval = -ETIMEDOUT;
else
retval = req->status;
diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c
index f0de99858353..c003049bafbf 100644
--- a/drivers/usb/mtu3/mtu3_debugfs.c
+++ b/drivers/usb/mtu3/mtu3_debugfs.c
@@ -7,6 +7,7 @@
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
+#include <linux/string_choices.h>
#include <linux/uaccess.h>
#include "mtu3.h"
@@ -256,16 +257,7 @@ static const struct mtu3_file_map mtu3_ep_files[] = {
static int mtu3_ep_open(struct inode *inode, struct file *file)
{
- const char *file_name = file_dentry(file)->d_iname;
- const struct mtu3_file_map *f_map;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) {
- f_map = &mtu3_ep_files[i];
-
- if (strcmp(f_map->name, file_name) == 0)
- break;
- }
+ const struct mtu3_file_map *f_map = debugfs_get_aux(file);
return single_open(file, f_map->show, inode->i_private);
}
@@ -288,17 +280,8 @@ static const struct debugfs_reg32 mtu3_prb_regs[] = {
static int mtu3_probe_show(struct seq_file *sf, void *unused)
{
- const char *file_name = file_dentry(sf->file)->d_iname;
struct mtu3 *mtu = sf->private;
- const struct debugfs_reg32 *regs;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) {
- regs = &mtu3_prb_regs[i];
-
- if (strcmp(regs->name, file_name) == 0)
- break;
- }
+ const struct debugfs_reg32 *regs = debugfs_get_aux(sf->file);
seq_printf(sf, "0x%04x - 0x%08x\n", (u32)regs->offset,
mtu3_readl(mtu->ippc_base, (u32)regs->offset));
@@ -314,13 +297,11 @@ static int mtu3_probe_open(struct inode *inode, struct file *file)
static ssize_t mtu3_probe_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
- const char *file_name = file_dentry(file)->d_iname;
struct seq_file *sf = file->private_data;
struct mtu3 *mtu = sf->private;
- const struct debugfs_reg32 *regs;
+ const struct debugfs_reg32 *regs = debugfs_get_aux(file);
char buf[32];
u32 val;
- int i;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
@@ -328,12 +309,6 @@ static ssize_t mtu3_probe_write(struct file *file, const char __user *ubuf,
if (kstrtou32(buf, 0, &val))
return -EINVAL;
- for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) {
- regs = &mtu3_prb_regs[i];
-
- if (strcmp(regs->name, file_name) == 0)
- break;
- }
mtu3_writel(mtu->ippc_base, (u32)regs->offset, val);
return count;
@@ -358,8 +333,8 @@ static void mtu3_debugfs_create_prb_files(struct mtu3 *mtu)
for (i = 0; i < ARRAY_SIZE(mtu3_prb_regs); i++) {
regs = &mtu3_prb_regs[i];
- debugfs_create_file(regs->name, 0644, dir_prb,
- mtu, &mtu3_probe_fops);
+ debugfs_create_file_aux(regs->name, 0644, dir_prb,
+ mtu, regs, &mtu3_probe_fops);
}
mtu3_debugfs_regset(mtu, mtu->ippc_base, mtu3_prb_regs,
@@ -379,8 +354,8 @@ static void mtu3_debugfs_create_ep_dir(struct mtu3_ep *mep,
for (i = 0; i < ARRAY_SIZE(mtu3_ep_files); i++) {
files = &mtu3_ep_files[i];
- debugfs_create_file(files->name, 0444, dir_ep,
- mep, &mtu3_ep_fops);
+ debugfs_create_file_aux(files->name, 0444, dir_ep,
+ mep, files, &mtu3_ep_fops);
}
}
@@ -479,7 +454,7 @@ static int ssusb_vbus_show(struct seq_file *sf, void *unused)
struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
seq_printf(sf, "vbus state: %s\n(echo on/off)\n",
- regulator_is_enabled(otg_sx->vbus) ? "on" : "off");
+ str_on_off(regulator_is_enabled(otg_sx->vbus)));
return 0;
}
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
index 8191b7ed3852..ffa5b9401dad 100644
--- a/drivers/usb/mtu3/mtu3_dr.c
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -7,6 +7,7 @@
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
+#include <linux/string_choices.h>
#include "mtu3.h"
#include "mtu3_dr.h"
#include "mtu3_debug.h"
@@ -109,7 +110,7 @@ int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
if (!vbus)
return 0;
- dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off");
+ dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, str_on_off(is_on));
if (is_on) {
ret = regulator_enable(vbus);
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index ad0eeac4332d..bf73fbc29976 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -7,6 +7,7 @@
* Author: Chunfeng Yun <chunfeng.yun@mediatek.com>
*/
+#include <linux/string_choices.h>
#include "mtu3.h"
#include "mtu3_trace.h"
@@ -490,7 +491,7 @@ static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
unsigned long flags;
dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__,
- is_on ? "on" : "off", mtu->is_active ? "" : "in");
+ str_on_off(is_on), mtu->is_active ? "" : "in");
pm_runtime_get_sync(mtu->dev);
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index f772aa272bea..eebb24ab3ec8 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -21,6 +21,7 @@
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
+#include <linux/string_choices.h>
#include <linux/dma-mapping.h>
#include <linux/usb/usb_phy_generic.h>
@@ -203,7 +204,7 @@ static void __maybe_unused da8xx_musb_try_idle(struct musb *musb, unsigned long
musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
usb_otg_state_string(musb->xceiv->otg->state));
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
last_timer = jiffies;
return;
}
@@ -289,7 +290,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
MUSB_HST_MODE(musb);
musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
} else if (!(musb->int_usb & MUSB_INTR_BABBLE)) {
/*
* When babble condition happens, drvvbus interrupt
@@ -306,7 +307,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
}
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
- drvvbus ? "on" : "off",
+ str_on_off(drvvbus),
usb_otg_state_string(musb->xceiv->otg->state),
err ? " ERROR" : "",
devctl);
@@ -418,7 +419,7 @@ static int da8xx_musb_exit(struct musb *musb)
{
struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent);
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
phy_power_off(glue->phy);
phy_exit(glue->phy);
diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index acdeb1117cd3..df56c972986f 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -59,7 +59,7 @@ static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
return IRQ_NONE;
}
-static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
+static const struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
@@ -205,7 +205,7 @@ static const struct musb_hdrc_platform_data jz4740_musb_pdata = {
.platform_ops = &jz4740_musb_ops,
};
-static struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = {
+static const struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c
index aa988d74b58d..c6cbe718b1da 100644
--- a/drivers/usb/musb/mediatek.c
+++ b/drivers/usb/musb/mediatek.c
@@ -365,7 +365,7 @@ static const struct musb_platform_ops mtk_musb_ops = {
#define MTK_MUSB_MAX_EP_NUM 8
#define MTK_MUSB_RAM_BITS 11
-static struct musb_fifo_cfg mtk_musb_mode_cfg[] = {
+static const struct musb_fifo_cfg mtk_musb_mode_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c
index 7edc8429b274..020348a98514 100644
--- a/drivers/usb/musb/mpfs.c
+++ b/drivers/usb/musb/mpfs.c
@@ -29,7 +29,7 @@ struct mpfs_glue {
struct clk *clk;
};
-static struct musb_fifo_cfg mpfs_musb_mode_cfg[] = {
+static const struct musb_fifo_cfg mpfs_musb_mode_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
@@ -165,7 +165,7 @@ static void __maybe_unused mpfs_musb_try_idle(struct musb *musb, unsigned long t
musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
usb_otg_state_string(musb->xceiv->otg->state));
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
last_timer = jiffies;
return;
}
@@ -232,7 +232,7 @@ static int mpfs_musb_init(struct musb *musb)
static int mpfs_musb_exit(struct musb *musb)
{
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
return 0;
}
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 03b1154a6014..cbbb27178024 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -72,6 +72,7 @@
#include <linux/kobject.h>
#include <linux/prefetch.h>
#include <linux/platform_device.h>
+#include <linux/string_choices.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/dma-mapping.h>
@@ -920,7 +921,7 @@ b_host:
musb_set_state(musb, OTG_STATE_B_HOST);
if (musb->hcd)
musb->hcd->self.is_b_host = 1;
- del_timer(&musb->otg_timer);
+ timer_delete(&musb->otg_timer);
break;
default:
if ((devctl & MUSB_DEVCTL_VBUS)
@@ -1014,7 +1015,7 @@ static void musb_handle_intr_reset(struct musb *musb)
+ msecs_to_jiffies(TA_WAIT_BCON(musb)));
break;
case OTG_STATE_A_PERIPHERAL:
- del_timer(&musb->otg_timer);
+ timer_delete(&musb->otg_timer);
musb_g_reset(musb);
break;
case OTG_STATE_B_WAIT_ACON:
@@ -1270,7 +1271,7 @@ MODULE_PARM_DESC(fifo_mode, "initial endpoint configuration");
*/
/* mode 0 - fits in 2KB */
-static struct musb_fifo_cfg mode_0_cfg[] = {
+static const struct musb_fifo_cfg mode_0_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, },
@@ -1279,7 +1280,7 @@ static struct musb_fifo_cfg mode_0_cfg[] = {
};
/* mode 1 - fits in 4KB */
-static struct musb_fifo_cfg mode_1_cfg[] = {
+static const struct musb_fifo_cfg mode_1_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 2, .style = FIFO_RXTX, .maxpacket = 512, .mode = BUF_DOUBLE, },
@@ -1288,7 +1289,7 @@ static struct musb_fifo_cfg mode_1_cfg[] = {
};
/* mode 2 - fits in 4KB */
-static struct musb_fifo_cfg mode_2_cfg[] = {
+static const struct musb_fifo_cfg mode_2_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
@@ -1298,7 +1299,7 @@ static struct musb_fifo_cfg mode_2_cfg[] = {
};
/* mode 3 - fits in 4KB */
-static struct musb_fifo_cfg mode_3_cfg[] = {
+static const struct musb_fifo_cfg mode_3_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
@@ -1308,7 +1309,7 @@ static struct musb_fifo_cfg mode_3_cfg[] = {
};
/* mode 4 - fits in 16KB */
-static struct musb_fifo_cfg mode_4_cfg[] = {
+static const struct musb_fifo_cfg mode_4_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
@@ -1339,7 +1340,7 @@ static struct musb_fifo_cfg mode_4_cfg[] = {
};
/* mode 5 - fits in 8KB */
-static struct musb_fifo_cfg mode_5_cfg[] = {
+static const struct musb_fifo_cfg mode_5_cfg[] = {
{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
@@ -1446,7 +1447,7 @@ fifo_setup(struct musb *musb, struct musb_hw_ep *hw_ep,
return offset + (maxpacket << ((c_size & MUSB_FIFOSZ_DPB) ? 1 : 0));
}
-static struct musb_fifo_cfg ep0_cfg = {
+static const struct musb_fifo_cfg ep0_cfg = {
.style = FIFO_RXTX, .maxpacket = 64,
};
@@ -1937,7 +1938,7 @@ vbus_show(struct device *dev, struct device_attribute *attr, char *buf)
pm_runtime_put_sync(dev);
return sprintf(buf, "Vbus %s, timeout %lu msec\n",
- vbus ? "on" : "off", val);
+ str_on_off(vbus), val);
}
static DEVICE_ATTR_RW(vbus);
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index 9589243e8951..4cde3abb7006 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -760,8 +760,8 @@ cppi41_dma_controller_create(struct musb *musb, void __iomem *base)
if (!controller)
goto kzalloc_fail;
- hrtimer_init(&controller->early_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- controller->early_tx.function = cppi41_recheck_tx_req;
+ hrtimer_setup(&controller->early_tx, cppi41_recheck_tx_req, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
INIT_LIST_HEAD(&controller->early_tx_list);
controller->controller.channel_alloc = cppi41_dma_channel_allocate;
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 2542239ec64e..e5e813f97fac 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -24,6 +24,7 @@
#include <linux/usb/usb_phy_generic.h>
#include <linux/platform_data/usb-omap.h>
#include <linux/sizes.h>
+#include <linux/string_choices.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -200,7 +201,7 @@ static void dsps_musb_disable(struct musb *musb)
musb_writel(reg_base, wrp->coreintr_clear, wrp->usb_bitmap);
musb_writel(reg_base, wrp->epintr_clear,
wrp->txep_bitmap | wrp->rxep_bitmap);
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
}
/* Caller must take musb->lock */
@@ -214,7 +215,7 @@ static int dsps_check_status(struct musb *musb, void *unused)
int skip_session = 0;
if (glue->vbus_irq)
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
/*
* We poll because DSPS IP's won't expose several OTG-critical
@@ -378,7 +379,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
/* NOTE: this must complete power-on within 100 ms. */
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
- drvvbus ? "on" : "off",
+ str_on_off(drvvbus),
usb_otg_state_string(musb->xceiv->otg->state),
err ? " ERROR" : "",
devctl);
@@ -498,7 +499,7 @@ static int dsps_musb_exit(struct musb *musb)
struct device *dev = musb->controller;
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
phy_power_off(musb->phy);
phy_exit(musb->phy);
debugfs_remove_recursive(glue->dbgfs_root);
@@ -982,7 +983,7 @@ static int dsps_suspend(struct device *dev)
return ret;
}
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
mbase = musb->ctrl_base;
glue->context.control = musb_readl(mbase, wrp->control);
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index c6076df0d50c..6869c58367f2 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
+#include <linux/string_choices.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
@@ -1606,7 +1607,7 @@ static void musb_pullup(struct musb *musb, int is_on)
/* FIXME if on, HdrcStart; if off, HdrcStop */
musb_dbg(musb, "gadget D+ pullup %s",
- is_on ? "on" : "off");
+ str_on_off(is_on));
musb_writeb(musb->mregs, MUSB_POWER, power);
}
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 732ba981e607..6b4481a867c5 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
@@ -1028,7 +1029,7 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb)
+ urb->actual_length);
musb_dbg(musb, "Sending %d byte%s to ep0 fifo %p",
fifo_count,
- (fifo_count == 1) ? "" : "s",
+ str_plural(fifo_count),
fifo_dest);
musb_write_fifo(hw_ep, fifo_count, fifo_dest);
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index eac1cde86be3..a6bd3e968cc7 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -629,7 +629,7 @@ static const struct musb_platform_ops sunxi_musb_ops = {
#define SUNXI_MUSB_RAM_BITS 11
/* Allwinner OTG supports up to 5 endpoints */
-static struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = {
+static const struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = {
MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512),
MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512),
MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512),
@@ -643,7 +643,7 @@ static struct musb_fifo_cfg sunxi_musb_mode_cfg_5eps[] = {
};
/* H3/V3s OTG supports only 4 endpoints */
-static struct musb_fifo_cfg sunxi_musb_mode_cfg_4eps[] = {
+static const struct musb_fifo_cfg sunxi_musb_mode_cfg_4eps[] = {
MUSB_EP_FIFO_SINGLE(1, FIFO_TX, 512),
MUSB_EP_FIFO_SINGLE(1, FIFO_RX, 512),
MUSB_EP_FIFO_SINGLE(2, FIFO_TX, 512),
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index 90b760a95e4e..abd2472da7f7 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -525,7 +525,7 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout)
&& (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON))) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
usb_otg_state_string(musb->xceiv->otg->state));
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
last_timer = jiffies;
return;
}
@@ -875,7 +875,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci)
}
if (int_src & TUSB_INT_SRC_USB_IP_CONN)
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
/* OTG state change reports (annoyingly) not issued by Mentor core */
if (int_src & (TUSB_INT_SRC_VBUS_SENSE_CHNG
@@ -984,7 +984,7 @@ static void tusb_musb_disable(struct musb *musb)
musb_writel(tbase, TUSB_DMA_INT_MASK, 0x7fffffff);
musb_writel(tbase, TUSB_GPIO_INT_MASK, 0x1ff);
- del_timer(&musb->dev_timer);
+ timer_delete(&musb->dev_timer);
if (is_dma_capable() && !dma_off) {
printk(KERN_WARNING "%s %s: dma still active\n",
@@ -1174,7 +1174,7 @@ static int tusb_musb_exit(struct musb *musb)
{
struct tusb6010_glue *glue = dev_get_drvdata(musb->controller->parent);
- del_timer_sync(&musb->dev_timer);
+ timer_delete_sync(&musb->dev_timer);
the_musb = NULL;
gpiod_set_value(glue->enable, 0);
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 42c42e193232..40ac68e52cee 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/proc_fs.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
@@ -529,7 +530,7 @@ int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
if (!otg->gadget || !otg->gadget->dev.parent)
return -ENODEV;
- VDBG("gadget %s\n", on ? "on" : "off");
+ VDBG("gadget %s\n", str_on_off(on));
dev = otg->gadget->dev.parent;
if (on) {
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index 6c3ececf9137..8423be59ec0f 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -212,7 +212,7 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop)
if (of_property_read_u32(node, "clock-frequency", &clk_rate))
clk_rate = 0;
- needs_clk = of_property_read_bool(node, "clocks");
+ needs_clk = of_property_present(node, "clocks");
}
nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
GPIOD_ASIS);
diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c
index a7a102f2e163..638fba58420c 100644
--- a/drivers/usb/phy/phy-mv-usb.c
+++ b/drivers/usb/phy/phy-mv-usb.c
@@ -15,6 +15,7 @@
#include <linux/clk.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
+#include <linux/string_choices.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
@@ -109,7 +110,7 @@ static int mv_otg_cancel_timer(struct mv_otg *mvotg, unsigned int id)
timer = &mvotg->otg_ctrl.timer[id];
if (timer_pending(timer))
- del_timer(timer);
+ timer_delete(timer);
return 0;
}
@@ -217,7 +218,7 @@ static void mv_otg_start_periphrals(struct mv_otg *mvotg, int on)
if (!otg->gadget)
return;
- dev_info(mvotg->phy.dev, "gadget %s\n", on ? "on" : "off");
+ dev_info(mvotg->phy.dev, "gadget %s\n", str_on_off(on));
if (on)
usb_gadget_vbus_connect(otg->gadget);
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 7490f1798b46..7069dd3f4d0d 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -769,11 +769,9 @@ static int mxs_phy_probe(struct platform_device *pdev)
return PTR_ERR(base);
clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
- dev_err(&pdev->dev,
- "can't get the clock, err=%ld", PTR_ERR(clk));
- return PTR_ERR(clk);
- }
+ if (IS_ERR(clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(clk),
+ "can't get the clock\n");
mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL);
if (!mxs_phy)
diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c
index ae7bf3ff89ee..88607d0edb01 100644
--- a/drivers/usb/phy/phy-tahvo.c
+++ b/drivers/usb/phy/phy-tahvo.c
@@ -18,6 +18,7 @@
#include <linux/extcon-provider.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/string_choices.h>
#include <linux/usb/otg.h>
#include <linux/mfd/retu.h>
#include <linux/usb/gadget.h>
@@ -63,7 +64,7 @@ static ssize_t vbus_show(struct device *device,
struct device_attribute *attr, char *buf)
{
struct tahvo_usb *tu = dev_get_drvdata(device);
- return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off");
+ return sprintf(buf, "%s\n", str_on_off(tu->vbus_state));
}
static DEVICE_ATTR_RO(vbus);
diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c
index e683a37e3a7a..4df63e67bb37 100644
--- a/drivers/usb/phy/phy-ulpi.c
+++ b/drivers/usb/phy/phy-ulpi.c
@@ -256,29 +256,6 @@ static void otg_ulpi_init(struct usb_phy *phy, struct usb_otg *otg,
}
struct usb_phy *
-otg_ulpi_create(struct usb_phy_io_ops *ops,
- unsigned int flags)
-{
- struct usb_phy *phy;
- struct usb_otg *otg;
-
- phy = kzalloc(sizeof(*phy), GFP_KERNEL);
- if (!phy)
- return NULL;
-
- otg = kzalloc(sizeof(*otg), GFP_KERNEL);
- if (!otg) {
- kfree(phy);
- return NULL;
- }
-
- otg_ulpi_init(phy, otg, ops, flags);
-
- return phy;
-}
-EXPORT_SYMBOL_GPL(otg_ulpi_create);
-
-struct usb_phy *
devm_otg_ulpi_create(struct device *dev,
struct usb_phy_io_ops *ops,
unsigned int flags)
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index 1ce134505cee..e1435bc59662 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -346,13 +346,6 @@ static void devm_usb_phy_release2(struct device *dev, void *_res)
usb_put_phy(res->phy);
}
-static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
-{
- struct usb_phy **phy = res;
-
- return *phy == match_data;
-}
-
static void usb_charger_init(struct usb_phy *usb_phy)
{
usb_phy->chg_type = UNKNOWN_TYPE;
@@ -615,25 +608,6 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle);
/**
- * devm_usb_put_phy - release the USB PHY
- * @dev: device that wants to release this phy
- * @phy: the phy returned by devm_usb_get_phy()
- *
- * destroys the devres associated with this phy and invokes usb_put_phy
- * to release the phy.
- *
- * For use by USB host and peripheral drivers.
- */
-void devm_usb_put_phy(struct device *dev, struct usb_phy *phy)
-{
- int r;
-
- r = devres_release(dev, devm_usb_phy_release, devm_usb_phy_match, phy);
- dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
-}
-EXPORT_SYMBOL_GPL(devm_usb_put_phy);
-
-/**
* usb_put_phy - release the USB PHY
* @x: the phy returned by usb_get_phy()
*
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 4b35ef216125..16692e72b736 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -685,10 +685,29 @@ static int usbhs_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug);
spin_lock_init(usbhs_priv_to_lock(priv));
+ /*
+ * Acquire clocks and enable power management (PM) early in the
+ * probe process, as the driver accesses registers during
+ * initialization. Ensure the device is active before proceeding.
+ */
+ pm_runtime_enable(dev);
+
+ ret = usbhsc_clk_get(dev, priv);
+ if (ret)
+ goto probe_pm_disable;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ goto probe_clk_put;
+
+ ret = usbhsc_clk_prepare_enable(priv);
+ if (ret)
+ goto probe_pm_put;
+
/* call pipe and module init */
ret = usbhs_pipe_probe(priv);
if (ret < 0)
- return ret;
+ goto probe_clk_dis_unprepare;
ret = usbhs_fifo_probe(priv);
if (ret < 0)
@@ -705,10 +724,6 @@ static int usbhs_probe(struct platform_device *pdev)
if (ret)
goto probe_fail_rst;
- ret = usbhsc_clk_get(dev, priv);
- if (ret)
- goto probe_fail_clks;
-
/*
* deviece reset here because
* USB device might be used in boot loader.
@@ -721,7 +736,7 @@ static int usbhs_probe(struct platform_device *pdev)
if (ret) {
dev_warn(dev, "USB function not selected (GPIO)\n");
ret = -ENOTSUPP;
- goto probe_end_mod_exit;
+ goto probe_assert_rest;
}
}
@@ -735,14 +750,19 @@ static int usbhs_probe(struct platform_device *pdev)
ret = usbhs_platform_call(priv, hardware_init, pdev);
if (ret < 0) {
dev_err(dev, "platform init failed.\n");
- goto probe_end_mod_exit;
+ goto probe_assert_rest;
}
/* reset phy for connection */
usbhs_platform_call(priv, phy_reset, pdev);
- /* power control */
- pm_runtime_enable(dev);
+ /*
+ * Disable the clocks that were enabled earlier in the probe path,
+ * and let the driver handle the clocks beyond this point.
+ */
+ usbhsc_clk_disable_unprepare(priv);
+ pm_runtime_put(dev);
+
if (!usbhs_get_dparam(priv, runtime_pwctrl)) {
usbhsc_power_ctrl(priv, 1);
usbhs_mod_autonomy_mode(priv);
@@ -759,9 +779,7 @@ static int usbhs_probe(struct platform_device *pdev)
return ret;
-probe_end_mod_exit:
- usbhsc_clk_put(priv);
-probe_fail_clks:
+probe_assert_rest:
reset_control_assert(priv->rsts);
probe_fail_rst:
usbhs_mod_remove(priv);
@@ -769,6 +787,14 @@ probe_end_fifo_exit:
usbhs_fifo_remove(priv);
probe_end_pipe_exit:
usbhs_pipe_remove(priv);
+probe_clk_dis_unprepare:
+ usbhsc_clk_disable_unprepare(priv);
+probe_pm_put:
+ pm_runtime_put(dev);
+probe_clk_put:
+ usbhsc_clk_put(priv);
+probe_pm_disable:
+ pm_runtime_disable(dev);
dev_info(dev, "probe failed (%d)\n", ret);
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index d10e4c4848a0..7cc36f84821f 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -63,6 +63,7 @@
#define CH341_REG_DIVISOR 0x13
#define CH341_REG_LCR 0x18
#define CH341_REG_LCR2 0x25
+#define CH341_REG_FLOW_CTL 0x27
#define CH341_NBREAK_BITS 0x01
@@ -77,6 +78,9 @@
#define CH341_LCR_CS6 0x01
#define CH341_LCR_CS5 0x00
+#define CH341_FLOW_CTL_NONE 0x00
+#define CH341_FLOW_CTL_RTSCTS 0x01
+
#define CH341_QUIRK_LIMITED_PRESCALER BIT(0)
#define CH341_QUIRK_SIMULATE_BREAK BIT(1)
@@ -478,6 +482,28 @@ err_kill_interrupt_urb:
return r;
}
+static void ch341_set_flow_control(struct tty_struct *tty,
+ struct usb_serial_port *port,
+ const struct ktermios *old_termios)
+{
+ u16 flow_ctl;
+ int r;
+
+ if (C_CRTSCTS(tty))
+ flow_ctl = CH341_FLOW_CTL_RTSCTS;
+ else
+ flow_ctl = CH341_FLOW_CTL_NONE;
+
+ r = ch341_control_out(port->serial->dev,
+ CH341_REQ_WRITE_REG,
+ (CH341_REG_FLOW_CTL << 8) | CH341_REG_FLOW_CTL,
+ (flow_ctl << 8) | flow_ctl);
+ if (r < 0 && old_termios) {
+ tty->termios.c_cflag &= ~CRTSCTS;
+ tty->termios.c_cflag |= (old_termios->c_cflag & CRTSCTS);
+ }
+}
+
/* Old_termios contains the original termios settings and
* tty->termios contains the new setting to be used.
*/
@@ -546,6 +572,8 @@ static void ch341_set_termios(struct tty_struct *tty,
spin_unlock_irqrestore(&priv->lock, flags);
ch341_set_handshake(port->serial->dev, priv->mcr);
+
+ ch341_set_flow_control(tty, port, old_termios);
}
/*
@@ -632,13 +660,12 @@ restore:
static int ch341_break_ctl(struct tty_struct *tty, int break_state)
{
- const uint16_t ch341_break_reg =
- ((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK;
+ const u16 ch341_break_reg = (CH341_REG_LCR << 8) | CH341_REG_BREAK;
struct usb_serial_port *port = tty->driver_data;
struct ch341_private *priv = usb_get_serial_port_data(port);
+ u16 reg_contents;
+ u8 break_reg[2];
int r;
- uint16_t reg_contents;
- uint8_t break_reg[2];
if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK)
return ch341_simulate_break(tty, break_state);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index e07c5e3eb18c..6ac7a0a5cf07 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1079,6 +1079,22 @@ static const struct usb_device_id id_table_combined[] = {
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
/* GMC devices */
{ USB_DEVICE(GMC_VID, GMC_Z216C_PID) },
+ /* Altera USB Blaster 3 */
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_6022_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_6025_PID, 2) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_6026_PID, 2) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_6026_PID, 3) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_6029_PID, 2) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602A_PID, 2) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602A_PID, 3) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602C_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602D_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602D_PID, 2) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 2) },
+ { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 3) },
+ /* Abacus Electrics */
+ { USB_DEVICE(FTDI_VID, ABACUS_OPTICAL_PROBE_PID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 5ee60ba2a73c..9acb6f837327 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -443,6 +443,11 @@
#define LINX_FUTURE_2_PID 0xF44C /* Linx future device */
/*
+ * Abacus Electrics
+ */
+#define ABACUS_OPTICAL_PROBE_PID 0xf458 /* ABACUS ELECTRICS Optical Probe */
+
+/*
* Oceanic product ids
*/
#define FTDI_OCEANIC_PID 0xF460 /* Oceanic dive instrument */
@@ -1612,3 +1617,16 @@
*/
#define GMC_VID 0x1cd7
#define GMC_Z216C_PID 0x0217 /* GMC Z216C Adapter IR-USB */
+
+/*
+ * Altera USB Blaster 3 (http://www.altera.com).
+ */
+#define ALTERA_VID 0x09fb
+#define ALTERA_UB3_6022_PID 0x6022
+#define ALTERA_UB3_6025_PID 0x6025
+#define ALTERA_UB3_6026_PID 0x6026
+#define ALTERA_UB3_6029_PID 0x6029
+#define ALTERA_UB3_602A_PID 0x602a
+#define ALTERA_UB3_602C_PID 0x602c
+#define ALTERA_UB3_602D_PID 0x602d
+#define ALTERA_UB3_602E_PID 0x602e
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index ca3da79afd23..93710b762893 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -66,29 +66,16 @@
#define MOS_WDR_TIMEOUT 5000 /* default urb timeout */
-#define MOS_PORT1 0x0200
-#define MOS_PORT2 0x0300
-#define MOS_VENREG 0x0000
-#define MOS_MAX_PORT 0x02
-#define MOS_WRITE 0x0E
-#define MOS_READ 0x0D
-
/* Requests */
#define MCS_RD_RTYPE 0xC0
#define MCS_WR_RTYPE 0x40
#define MCS_RDREQ 0x0D
#define MCS_WRREQ 0x0E
-#define MCS_CTRL_TIMEOUT 500
#define VENDOR_READ_LENGTH (0x01)
-#define MAX_NAME_LEN 64
-
#define ZLP_REG1 0x3A /* Zero_Flag_Reg1 58 */
#define ZLP_REG5 0x3E /* Zero_Flag_Reg5 62 */
-/* For higher baud Rates use TIOCEXBAUD */
-#define TIOCEXBAUD 0x5462
-
/*
* Vendor id and device id defines
*
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 58bd54e8c483..27879cc57536 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -611,6 +611,7 @@ static void option_instat_callback(struct urb *urb);
/* Sierra Wireless products */
#define SIERRA_VENDOR_ID 0x1199
#define SIERRA_PRODUCT_EM9191 0x90d3
+#define SIERRA_PRODUCT_EM9291 0x90e3
/* UNISOC (Spreadtrum) products */
#define UNISOC_VENDOR_ID 0x1782
@@ -1368,13 +1369,13 @@ static const struct usb_device_id option_ids[] = {
.driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1075, 0xff), /* Telit FN990A (PCIe) */
.driver_info = RSVD(0) },
- { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1080, 0xff), /* Telit FE990 (rmnet) */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1080, 0xff), /* Telit FE990A (rmnet) */
.driver_info = NCTRL(0) | RSVD(1) | RSVD(2) },
- { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1081, 0xff), /* Telit FE990 (MBIM) */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1081, 0xff), /* Telit FE990A (MBIM) */
.driver_info = NCTRL(0) | RSVD(1) },
- { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1082, 0xff), /* Telit FE990 (RNDIS) */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1082, 0xff), /* Telit FE990A (RNDIS) */
.driver_info = NCTRL(2) | RSVD(3) },
- { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1083, 0xff), /* Telit FE990 (ECM) */
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1083, 0xff), /* Telit FE990A (ECM) */
.driver_info = NCTRL(0) | RSVD(1) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10a0, 0xff), /* Telit FN20C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) },
@@ -1388,28 +1389,44 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10aa, 0xff), /* Telit FN920C04 (MBIM) */
.driver_info = NCTRL(3) | RSVD(4) | RSVD(5) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x30), /* Telit FE990B (rmnet) */
+ .driver_info = NCTRL(5) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b0, 0xff, 0xff, 0x60) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b1, 0xff, 0xff, 0x30), /* Telit FE990B (MBIM) */
+ .driver_info = NCTRL(6) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b1, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b1, 0xff, 0xff, 0x60) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b2, 0xff, 0xff, 0x30), /* Telit FE990B (RNDIS) */
+ .driver_info = NCTRL(6) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b2, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b2, 0xff, 0xff, 0x60) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b3, 0xff, 0xff, 0x30), /* Telit FE990B (ECM) */
+ .driver_info = NCTRL(6) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b3, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10b3, 0xff, 0xff, 0x60) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c0, 0xff), /* Telit FE910C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c4, 0xff), /* Telit FE910C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(3) },
{ USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x10c8, 0xff), /* Telit FE910C04 (rmnet) */
.driver_info = RSVD(0) | NCTRL(2) | RSVD(3) | RSVD(4) },
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d0, 0x60) }, /* Telit FN990B (rmnet) */
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d0, 0x40) },
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d0, 0x30),
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x30), /* Telit FN990B (rmnet) */
.driver_info = NCTRL(5) },
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d1, 0x60) }, /* Telit FN990B (MBIM) */
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d1, 0x40) },
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d1, 0x30),
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d0, 0xff, 0xff, 0x60) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d1, 0xff, 0xff, 0x30), /* Telit FN990B (MBIM) */
.driver_info = NCTRL(6) },
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d2, 0x60) }, /* Telit FN990B (RNDIS) */
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d2, 0x40) },
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d2, 0x30),
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d1, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d1, 0xff, 0xff, 0x60) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d2, 0xff, 0xff, 0x30), /* Telit FN990B (RNDIS) */
.driver_info = NCTRL(6) },
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d3, 0x60) }, /* Telit FN990B (ECM) */
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d3, 0x40) },
- { USB_DEVICE_INTERFACE_PROTOCOL(TELIT_VENDOR_ID, 0x10d3, 0x30),
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d2, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d2, 0xff, 0xff, 0x60) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d3, 0xff, 0xff, 0x30), /* Telit FN990B (ECM) */
.driver_info = NCTRL(6) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d3, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(TELIT_VENDOR_ID, 0x10d3, 0xff, 0xff, 0x60) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910),
.driver_info = NCTRL(0) | RSVD(1) | RSVD(3) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM),
@@ -2416,6 +2433,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x30) },
{ USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0xff, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9191, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9291, 0xff, 0xff, 0x30) },
+ { USB_DEVICE_AND_INTERFACE_INFO(SIERRA_VENDOR_ID, SIERRA_PRODUCT_EM9291, 0xff, 0xff, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, TOZED_PRODUCT_LT70C, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(UNISOC_VENDOR_ID, LUAT_PRODUCT_AIR720U, 0xff, 0, 0) },
{ USB_DEVICE_INTERFACE_CLASS(0x1bbb, 0x0530, 0xff), /* TCL IK512 MBIM */
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 010688dd9e49..22579d0d8ab8 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -458,6 +458,8 @@ static int pl2303_detect_type(struct usb_serial *serial)
case 0x605:
case 0x700: /* GR */
case 0x705:
+ case 0x905: /* GT-2AB */
+ case 0x1005: /* GC-Q20 */
return TYPE_HXN;
}
break;
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index 2c12449ff60c..a0afaf254d12 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -100,6 +100,11 @@ DEVICE(nokia, NOKIA_IDS);
{ USB_DEVICE(0x09d7, 0x0100) } /* NovAtel FlexPack GPS */
DEVICE_N(novatel_gps, NOVATEL_IDS, 3);
+/* OWON electronic test and measurement equipment driver */
+#define OWON_IDS() \
+ { USB_DEVICE(0x5345, 0x1234) } /* HDS200 oscilloscopes and others */
+DEVICE(owon, OWON_IDS);
+
/* Siemens USB/MPI adapter */
#define SIEMENS_IDS() \
{ USB_DEVICE(0x908, 0x0004) }
@@ -134,6 +139,7 @@ static struct usb_serial_driver * const serial_drivers[] = {
&motorola_tetra_device,
&nokia_device,
&novatel_gps_device,
+ &owon_device,
&siemens_mpi_device,
&suunto_device,
&vivopay_device,
@@ -153,6 +159,7 @@ static const struct usb_device_id id_table[] = {
MOTOROLA_TETRA_IDS(),
NOKIA_IDS(),
NOVATEL_IDS(),
+ OWON_IDS(),
SIEMENS_IDS(),
SUUNTO_IDS(),
VIVOPAY_IDS(),
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index d17b60a644ef..4be1d617d63d 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -3,8 +3,7 @@
# USB Storage driver configuration
#
-comment "NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may"
-comment "also be needed; see USB_STORAGE Help for more info"
+comment "NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; see USB_STORAGE Help for more info"
config USB_STORAGE
tristate "USB Mass Storage support"
diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c
index 6263c4e61678..e01f3a42bde4 100644
--- a/drivers/usb/storage/alauda.c
+++ b/drivers/usb/storage/alauda.c
@@ -174,7 +174,7 @@ struct alauda_card_info {
unsigned char zoneshift; /* 1<<zs blocks per zone */
};
-static struct alauda_card_info alauda_card_ids[] = {
+static const struct alauda_card_info alauda_card_ids[] = {
/* NAND flash */
{ 0x6e, 20, 8, 4, 8}, /* 1 MB */
{ 0xe8, 20, 8, 4, 8}, /* 1 MB */
@@ -200,7 +200,7 @@ static struct alauda_card_info alauda_card_ids[] = {
{ 0,}
};
-static struct alauda_card_info *alauda_card_find_id(unsigned char id)
+static const struct alauda_card_info *alauda_card_find_id(unsigned char id)
{
int i;
@@ -383,7 +383,7 @@ static int alauda_init_media(struct us_data *us)
{
unsigned char *data = us->iobuf;
int ready = 0;
- struct alauda_card_info *media_info;
+ const struct alauda_card_info *media_info;
unsigned int num_zones;
while (ready == 0) {
@@ -1132,7 +1132,7 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us)
int rc;
struct alauda_info *info = (struct alauda_info *) us->extra;
unsigned char *ptr = us->iobuf;
- static unsigned char inquiry_response[36] = {
+ static const unsigned char inquiry_response[36] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
index bbfa2398b170..9ba369483c9b 100644
--- a/drivers/usb/storage/datafab.c
+++ b/drivers/usb/storage/datafab.c
@@ -319,7 +319,7 @@ static int datafab_determine_lun(struct us_data *us,
//
// There might be a better way of doing this?
- static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
+ static const unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
unsigned char *command = us->iobuf;
unsigned char *buf;
int count = 0, rc;
@@ -384,7 +384,7 @@ static int datafab_id_device(struct us_data *us,
// to the ATA spec, 'Sector Count' isn't used but the Windows driver
// sets this bit so we do too...
//
- static unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
+ static const unsigned char scommand[8] = { 0, 1, 0, 0, 0, 0xa0, 0xec, 1 };
unsigned char *command = us->iobuf;
unsigned char *reply;
int rc;
@@ -437,16 +437,16 @@ static int datafab_handle_mode_sense(struct us_data *us,
struct scsi_cmnd * srb,
int sense_6)
{
- static unsigned char rw_err_page[12] = {
+ static const unsigned char rw_err_page[12] = {
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
};
- static unsigned char cache_page[12] = {
+ static const unsigned char cache_page[12] = {
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
- static unsigned char rbac_page[12] = {
+ static const unsigned char rbac_page[12] = {
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
};
- static unsigned char timer_page[8] = {
+ static const unsigned char timer_page[8] = {
0x1C, 0x6, 0, 0, 0, 0
};
unsigned char pc, page_code;
@@ -550,7 +550,7 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us)
int rc;
unsigned long block, blocks;
unsigned char *ptr = us->iobuf;
- static unsigned char inquiry_reply[8] = {
+ static const unsigned char inquiry_reply[8] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c
index 576be66ad962..dda610f689b7 100644
--- a/drivers/usb/storage/debug.c
+++ b/drivers/usb/storage/debug.c
@@ -58,8 +58,8 @@ void usb_stor_show_command(const struct us_data *us, struct scsi_cmnd *srb)
case INQUIRY: what = "INQUIRY"; break;
case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
case MODE_SELECT: what = "MODE_SELECT"; break;
- case RESERVE: what = "RESERVE"; break;
- case RELEASE: what = "RELEASE"; break;
+ case RESERVE_6: what = "RESERVE"; break;
+ case RELEASE_6: what = "RELEASE"; break;
case COPY: what = "COPY"; break;
case ERASE: what = "ERASE"; break;
case MODE_SENSE: what = "MODE_SENSE"; break;
diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c
index f8f9ce8dc710..b243bd5521a6 100644
--- a/drivers/usb/storage/initializers.c
+++ b/drivers/usb/storage/initializers.c
@@ -54,7 +54,7 @@ int usb_stor_ucr61s2b_init(struct us_data *us)
struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap*) us->iobuf;
int res;
unsigned int partial;
- static char init_string[] = "\xec\x0a\x06\x00$PCCHIPS";
+ static const char init_string[] = "\xec\x0a\x06\x00$PCCHIPS";
usb_stor_dbg(us, "Sending UCR-61S2B initialization packet...\n");
diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
index 39ca84d68591..089c6f8ac85f 100644
--- a/drivers/usb/storage/jumpshot.c
+++ b/drivers/usb/storage/jumpshot.c
@@ -367,16 +367,16 @@ static int jumpshot_handle_mode_sense(struct us_data *us,
struct scsi_cmnd * srb,
int sense_6)
{
- static unsigned char rw_err_page[12] = {
+ static const unsigned char rw_err_page[12] = {
0x1, 0xA, 0x21, 1, 0, 0, 0, 0, 1, 0, 0, 0
};
- static unsigned char cache_page[12] = {
+ static const unsigned char cache_page[12] = {
0x8, 0xA, 0x1, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
- static unsigned char rbac_page[12] = {
+ static const unsigned char rbac_page[12] = {
0x1B, 0xA, 0, 0x81, 0, 0, 0, 0, 0, 0, 0, 0
};
- static unsigned char timer_page[8] = {
+ static const unsigned char timer_page[8] = {
0x1C, 0x6, 0, 0, 0, 0
};
unsigned char pc, page_code;
@@ -477,7 +477,7 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us)
int rc;
unsigned long block, blocks;
unsigned char *ptr = us->iobuf;
- static unsigned char inquiry_response[8] = {
+ static const unsigned char inquiry_response[8] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c
index 2a82ed7b68ea..b387863c245f 100644
--- a/drivers/usb/storage/realtek_cr.c
+++ b/drivers/usb/storage/realtek_cr.c
@@ -191,7 +191,7 @@ MODULE_DEVICE_TABLE(usb, realtek_cr_ids);
.initFunction = init_function, \
}
-static struct us_unusual_dev realtek_cr_unusual_dev_list[] = {
+static const struct us_unusual_dev realtek_cr_unusual_dev_list[] = {
# include "unusual_realtek.h"
{} /* Terminating entry */
};
@@ -797,10 +797,10 @@ static void rts51x_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
{
struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra);
static int card_first_show = 1;
- static u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0,
+ static const u8 media_not_present[] = { 0x70, 0, 0x02, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0x3A, 0, 0, 0, 0, 0
};
- static u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0,
+ static const u8 invalid_cmd_field[] = { 0x70, 0, 0x05, 0, 0, 0, 0,
10, 0, 0, 0, 0, 0x24, 0, 0, 0, 0, 0
};
int ret;
@@ -934,7 +934,7 @@ static void realtek_cr_destructor(void *extra)
#ifdef CONFIG_REALTEK_AUTOPM
if (ss_en) {
- del_timer(&chip->rts51x_suspend_timer);
+ timer_delete(&chip->rts51x_suspend_timer);
chip->timer_expires = 0;
}
#endif
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 8c8b5e6041cc..d2f476e48d0c 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -64,7 +64,7 @@ static const char* host_info(struct Scsi_Host *host)
return us->scsi_name;
}
-static int slave_alloc (struct scsi_device *sdev)
+static int sdev_init (struct scsi_device *sdev)
{
struct us_data *us = host_to_us(sdev->host);
@@ -88,7 +88,7 @@ static int slave_alloc (struct scsi_device *sdev)
return 0;
}
-static int device_configure(struct scsi_device *sdev, struct queue_limits *lim)
+static int sdev_configure(struct scsi_device *sdev, struct queue_limits *lim)
{
struct us_data *us = host_to_us(sdev->host);
struct device *dev = us->pusb_dev->bus->sysdev;
@@ -127,7 +127,7 @@ static int device_configure(struct scsi_device *sdev, struct queue_limits *lim)
lim->max_hw_sectors, dma_max_mapping_size(dev) >> SECTOR_SHIFT);
/*
- * We can't put these settings in slave_alloc() because that gets
+ * We can't put these settings in sdev_init() because that gets
* called before the device type is known. Consequently these
* settings can't be overridden via the scsi devinfo mechanism.
*/
@@ -592,12 +592,9 @@ static ssize_t max_sectors_store(struct device *dev, struct device_attribute *at
if (sscanf(buf, "%hu", &ms) <= 0)
return -EINVAL;
- blk_mq_freeze_queue(sdev->request_queue);
lim = queue_limits_start_update(sdev->request_queue);
lim.max_hw_sectors = ms;
- ret = queue_limits_commit_update(sdev->request_queue, &lim);
- blk_mq_unfreeze_queue(sdev->request_queue);
-
+ ret = queue_limits_commit_update_frozen(sdev->request_queue, &lim);
if (ret)
return ret;
return count;
@@ -637,8 +634,8 @@ static const struct scsi_host_template usb_stor_host_template = {
/* unknown initiator id */
.this_id = -1,
- .slave_alloc = slave_alloc,
- .device_configure = device_configure,
+ .sdev_init = sdev_init,
+ .sdev_configure = sdev_configure,
.target_alloc = target_alloc,
/* lots of sg segments can be handled */
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index d21ce3466e25..e66b920e99e2 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -144,7 +144,7 @@ static inline char *nand_flash_manufacturer(int manuf_id) {
* 256 MB NAND flash has a 5-byte ID with 2nd byte 0xaa, 0xba, 0xca or 0xda.
*/
-static struct nand_flash_dev nand_flash_ids[] = {
+static const struct nand_flash_dev nand_flash_ids[] = {
/* NAND flash */
{ 0x6e, 20, 8, 4, 8, 2}, /* 1 MB */
{ 0xe8, 20, 8, 4, 8, 2}, /* 1 MB */
@@ -169,7 +169,7 @@ static struct nand_flash_dev nand_flash_ids[] = {
{ 0,}
};
-static struct nand_flash_dev *
+static const struct nand_flash_dev *
nand_find_id(unsigned char id) {
int i;
@@ -1133,9 +1133,9 @@ sddr09_reset(struct us_data *us) {
}
#endif
-static struct nand_flash_dev *
+static const struct nand_flash_dev *
sddr09_get_cardinfo(struct us_data *us, unsigned char flags) {
- struct nand_flash_dev *cardinfo;
+ const struct nand_flash_dev *cardinfo;
unsigned char deviceID[4];
char blurbtxt[256];
int result;
@@ -1545,12 +1545,12 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
struct sddr09_card_info *info;
- static unsigned char inquiry_response[8] = {
+ static const unsigned char inquiry_response[8] = {
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
};
/* note: no block descriptor support */
- static unsigned char mode_page_01[19] = {
+ static const unsigned char mode_page_01[19] = {
0x00, 0x0F, 0x00, 0x0, 0x0, 0x0, 0x00,
0x01, 0x0A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
@@ -1584,7 +1584,7 @@ static int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us)
}
if (srb->cmnd[0] == READ_CAPACITY) {
- struct nand_flash_dev *cardinfo;
+ const struct nand_flash_dev *cardinfo;
sddr09_get_wp(us, info); /* read WP bit */
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
index d5cdff30f6f3..b323f0a36260 100644
--- a/drivers/usb/storage/sddr55.c
+++ b/drivers/usb/storage/sddr55.c
@@ -775,11 +775,11 @@ static void sddr55_card_info_destructor(void *extra) {
static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us)
{
int result;
- static unsigned char inquiry_response[8] = {
+ static const unsigned char inquiry_response[8] = {
0x00, 0x80, 0x00, 0x02, 0x1F, 0x00, 0x00, 0x00
};
// write-protected for now, no block descriptor support
- static unsigned char mode_page_01[20] = {
+ static const unsigned char mode_page_01[20] = {
0x0, 0x12, 0x00, 0x80, 0x0, 0x0, 0x0, 0x0,
0x01, 0x0A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c
index 087c706bb315..27faa0ead11d 100644
--- a/drivers/usb/storage/shuttle_usbat.c
+++ b/drivers/usb/storage/shuttle_usbat.c
@@ -32,6 +32,7 @@
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/cdrom.h>
#include <scsi/scsi.h>
@@ -651,8 +652,7 @@ static int usbat_hp8200e_rw_block_test(struct us_data *us,
return USB_STOR_TRANSPORT_FAILED;
usb_stor_dbg(us, "Redoing %s\n",
- direction == DMA_TO_DEVICE
- ? "write" : "read");
+ str_write_read(direction == DMA_TO_DEVICE));
} else if (result != USB_STOR_XFER_GOOD)
return USB_STOR_TRANSPORT_ERROR;
@@ -1683,7 +1683,7 @@ static int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us)
struct usbat_info *info = (struct usbat_info *) (us->extra);
unsigned long block, blocks;
unsigned char *ptr = us->iobuf;
- static unsigned char inquiry_response[36] = {
+ static const unsigned char inquiry_response[36] = {
0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00
};
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 9d767f6bf722..1aa1bd26c81f 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -528,7 +528,7 @@ static void last_sector_hacks(struct us_data *us, struct scsi_cmnd *srb)
u32 sector;
/* To Report "Medium Error: Record Not Found */
- static unsigned char record_not_found[18] = {
+ static const unsigned char record_not_found[18] = {
[0] = 0x70, /* current error */
[2] = MEDIUM_ERROR, /* = 0x03 */
[7] = 0x0a, /* additional length */
@@ -1087,13 +1087,9 @@ int usb_stor_Bulk_max_lun(struct us_data *us)
usb_stor_dbg(us, "GetMaxLUN command result is %d, data is %d\n",
result, us->iobuf[0]);
- /*
- * If we have a successful request, return the result if valid. The
- * CBW LUN field is 4 bits wide, so the value reported by the device
- * should fit into that.
- */
+ /* If we have a successful request, return the result if valid. */
if (result > 0) {
- if (us->iobuf[0] < 16) {
+ if (us->iobuf[0] <= US_BULK_MAX_LUN_LIMIT) {
return us->iobuf[0];
} else {
dev_info(&us->pusb_intf->dev,
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index f9ad90ce7af4..4ed0dc19afe0 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -817,7 +817,7 @@ static int uas_target_alloc(struct scsi_target *starget)
return 0;
}
-static int uas_slave_alloc(struct scsi_device *sdev)
+static int uas_sdev_init(struct scsi_device *sdev)
{
struct uas_dev_info *devinfo =
(struct uas_dev_info *)sdev->host->hostdata;
@@ -832,8 +832,8 @@ static int uas_slave_alloc(struct scsi_device *sdev)
return 0;
}
-static int uas_device_configure(struct scsi_device *sdev,
- struct queue_limits *lim)
+static int uas_sdev_configure(struct scsi_device *sdev,
+ struct queue_limits *lim)
{
struct uas_dev_info *devinfo = sdev->hostdata;
@@ -905,8 +905,8 @@ static const struct scsi_host_template uas_host_template = {
.name = "uas",
.queuecommand = uas_queuecommand,
.target_alloc = uas_target_alloc,
- .slave_alloc = uas_slave_alloc,
- .device_configure = uas_device_configure,
+ .sdev_init = uas_sdev_init,
+ .sdev_configure = uas_sdev_configure,
.eh_abort_handler = uas_eh_abort_handler,
.eh_device_reset_handler = uas_eh_device_reset_handler,
.this_id = -1,
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index 1f8c9b16a0fb..1477e31d7763 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -52,6 +52,13 @@ UNUSUAL_DEV(0x059f, 0x1061, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_REPORT_OPCODES | US_FL_NO_SAME),
+/* Reported-by: Zhihong Zhou <zhouzhihong@greatwall.com.cn> */
+UNUSUAL_DEV(0x0781, 0x55e8, 0x0000, 0x9999,
+ "SanDisk",
+ "",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_IGNORE_UAS),
+
/* Reported-by: Hongling Zeng <zenghongling@kylinos.cn> */
UNUSUAL_DEV(0x090c, 0x2000, 0x0000, 0x9999,
"Hiksemi",
@@ -83,6 +90,13 @@ UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_REPORT_LUNS),
+/* Reported-by: Oliver Neukum <oneukum@suse.com> */
+UNUSUAL_DEV(0x125f, 0xa94a, 0x0160, 0x0160,
+ "ADATA",
+ "Portable HDD CH94",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_ATA_1X),
+
/* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */
UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999,
"Initio Corporation",
diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig
index 1a6b5e872b0d..7867fa7c405d 100644
--- a/drivers/usb/typec/altmodes/Kconfig
+++ b/drivers/usb/typec/altmodes/Kconfig
@@ -23,4 +23,13 @@ config TYPEC_NVIDIA_ALTMODE
To compile this driver as a module, choose M here: the
module will be called typec_nvidia.
+config TYPEC_TBT_ALTMODE
+ tristate "Thunderbolt3 Alternate Mode driver"
+ help
+ Select this option if you have Thunderbolt3 hardware on your
+ system.
+
+ To compile this driver as a module, choose M here: the
+ module will be called typec_thunderbolt.
+
endmenu
diff --git a/drivers/usb/typec/altmodes/Makefile b/drivers/usb/typec/altmodes/Makefile
index 45717548b396..508a68351bd2 100644
--- a/drivers/usb/typec/altmodes/Makefile
+++ b/drivers/usb/typec/altmodes/Makefile
@@ -4,3 +4,5 @@ obj-$(CONFIG_TYPEC_DP_ALTMODE) += typec_displayport.o
typec_displayport-y := displayport.o
obj-$(CONFIG_TYPEC_NVIDIA_ALTMODE) += typec_nvidia.o
typec_nvidia-y := nvidia.o
+obj-$(CONFIG_TYPEC_TBT_ALTMODE) += typec_thunderbolt.o
+typec_thunderbolt-y := thunderbolt.o
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 2f03190a9873..ac84a6d64c2f 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -252,7 +252,7 @@ static void dp_altmode_work(struct work_struct *work)
case DP_STATE_ENTER:
ret = typec_altmode_enter(dp->alt, NULL);
if (ret && ret != -EBUSY)
- dev_err(&dp->alt->dev, "failed to enter mode\n");
+ dev_err(&dp->alt->dev, "failed to enter mode: %d\n", ret);
break;
case DP_STATE_ENTER_PRIME:
ret = typec_cable_altmode_enter(dp->alt, TYPEC_PLUG_SOP_P, NULL);
@@ -791,7 +791,7 @@ void dp_altmode_remove(struct typec_altmode *alt)
EXPORT_SYMBOL_GPL(dp_altmode_remove);
static const struct typec_device_id dp_typec_id[] = {
- { USB_TYPEC_DP_SID, USB_TYPEC_DP_MODE },
+ { USB_TYPEC_DP_SID },
{ },
};
MODULE_DEVICE_TABLE(typec, dp_typec_id);
diff --git a/drivers/usb/typec/altmodes/nvidia.c b/drivers/usb/typec/altmodes/nvidia.c
index fe70b36f078f..2b77d931e494 100644
--- a/drivers/usb/typec/altmodes/nvidia.c
+++ b/drivers/usb/typec/altmodes/nvidia.c
@@ -24,7 +24,7 @@ static void nvidia_altmode_remove(struct typec_altmode *alt)
}
static const struct typec_device_id nvidia_typec_id[] = {
- { USB_TYPEC_NVIDIA_VLINK_SID, TYPEC_ANY_MODE },
+ { USB_TYPEC_NVIDIA_VLINK_SID },
{ },
};
MODULE_DEVICE_TABLE(typec, nvidia_typec_id);
diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
new file mode 100644
index 000000000000..6eadf7835f8f
--- /dev/null
+++ b/drivers/usb/typec/altmodes/thunderbolt.c
@@ -0,0 +1,388 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Typec-C Thunderbolt3 Alternate Mode driver
+ *
+ * Copyright (C) 2019 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/usb/pd_vdo.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_tbt.h>
+
+enum tbt_state {
+ TBT_STATE_IDLE,
+ TBT_STATE_SOP_P_ENTER,
+ TBT_STATE_SOP_PP_ENTER,
+ TBT_STATE_ENTER,
+ TBT_STATE_EXIT,
+ TBT_STATE_SOP_PP_EXIT,
+ TBT_STATE_SOP_P_EXIT
+};
+
+struct tbt_altmode {
+ enum tbt_state state;
+ struct typec_cable *cable;
+ struct typec_altmode *alt;
+ struct typec_altmode *plug[2];
+ u32 enter_vdo;
+
+ struct work_struct work;
+ struct mutex lock; /* device lock */
+};
+
+static bool tbt_ready(struct typec_altmode *alt);
+
+static int tbt_enter_mode(struct tbt_altmode *tbt)
+{
+ struct typec_altmode *plug = tbt->plug[TYPEC_PLUG_SOP_P];
+ u32 vdo;
+
+ vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1);
+ vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0;
+ vdo |= TBT_MODE;
+
+ if (plug) {
+ if (typec_cable_is_active(tbt->cable))
+ vdo |= TBT_ENTER_MODE_ACTIVE_CABLE;
+
+ vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo));
+ vdo |= plug->vdo & TBT_CABLE_ROUNDED;
+ vdo |= plug->vdo & TBT_CABLE_OPTICAL;
+ vdo |= plug->vdo & TBT_CABLE_RETIMER;
+ vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING;
+ } else {
+ vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE);
+ }
+
+ tbt->enter_vdo = vdo;
+ return typec_altmode_enter(tbt->alt, &vdo);
+}
+
+static void tbt_altmode_work(struct work_struct *work)
+{
+ struct tbt_altmode *tbt = container_of(work, struct tbt_altmode, work);
+ int ret;
+
+ mutex_lock(&tbt->lock);
+
+ switch (tbt->state) {
+ case TBT_STATE_SOP_P_ENTER:
+ ret = typec_cable_altmode_enter(tbt->alt, TYPEC_PLUG_SOP_P, NULL);
+ if (ret) {
+ dev_dbg(&tbt->plug[TYPEC_PLUG_SOP_P]->dev,
+ "failed to enter mode (%d)\n", ret);
+ goto disable_plugs;
+ }
+ break;
+ case TBT_STATE_SOP_PP_ENTER:
+ ret = typec_cable_altmode_enter(tbt->alt, TYPEC_PLUG_SOP_PP, NULL);
+ if (ret) {
+ dev_dbg(&tbt->plug[TYPEC_PLUG_SOP_PP]->dev,
+ "failed to enter mode (%d)\n", ret);
+ goto disable_plugs;
+ }
+ break;
+ case TBT_STATE_ENTER:
+ ret = tbt_enter_mode(tbt);
+ if (ret)
+ dev_dbg(&tbt->alt->dev, "failed to enter mode (%d)\n",
+ ret);
+ break;
+ case TBT_STATE_EXIT:
+ typec_altmode_exit(tbt->alt);
+ break;
+ case TBT_STATE_SOP_PP_EXIT:
+ typec_cable_altmode_exit(tbt->alt, TYPEC_PLUG_SOP_PP);
+ break;
+ case TBT_STATE_SOP_P_EXIT:
+ typec_cable_altmode_exit(tbt->alt, TYPEC_PLUG_SOP_P);
+ break;
+ default:
+ break;
+ }
+
+ tbt->state = TBT_STATE_IDLE;
+
+ mutex_unlock(&tbt->lock);
+ return;
+
+disable_plugs:
+ for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) {
+ if (tbt->plug[i])
+ typec_altmode_put_plug(tbt->plug[i]);
+
+ tbt->plug[i] = NULL;
+ }
+
+ tbt->state = TBT_STATE_ENTER;
+ schedule_work(&tbt->work);
+ mutex_unlock(&tbt->lock);
+}
+
+/*
+ * If SOP' is available, enter that first (which will trigger a VDM response
+ * that will enter SOP" if available and then the port). If entering SOP' fails,
+ * stop attempting to enter either cable altmode (probably not supported) and
+ * directly enter the port altmode.
+ */
+static int tbt_enter_modes_ordered(struct typec_altmode *alt)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ int ret = 0;
+
+ lockdep_assert_held(&tbt->lock);
+
+ if (!tbt_ready(tbt->alt))
+ return -ENODEV;
+
+ if (tbt->plug[TYPEC_PLUG_SOP_P]) {
+ ret = typec_cable_altmode_enter(alt, TYPEC_PLUG_SOP_P, NULL);
+ if (ret < 0) {
+ for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) {
+ if (tbt->plug[i])
+ typec_altmode_put_plug(tbt->plug[i]);
+
+ tbt->plug[i] = NULL;
+ }
+ } else {
+ return ret;
+ }
+ }
+
+ return tbt_enter_mode(tbt);
+}
+
+static int tbt_cable_altmode_vdm(struct typec_altmode *alt,
+ enum typec_plug_index sop, const u32 hdr,
+ const u32 *vdo, int count)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ int cmd_type = PD_VDO_CMDT(hdr);
+ int cmd = PD_VDO_CMD(hdr);
+
+ mutex_lock(&tbt->lock);
+
+ if (tbt->state != TBT_STATE_IDLE) {
+ mutex_unlock(&tbt->lock);
+ return -EBUSY;
+ }
+
+ switch (cmd_type) {
+ case CMDT_RSP_ACK:
+ switch (cmd) {
+ case CMD_ENTER_MODE:
+ /*
+ * Following the order described in USB Type-C Spec
+ * R2.0 Section 6.7.3: SOP', SOP", then port.
+ */
+ if (sop == TYPEC_PLUG_SOP_P) {
+ if (tbt->plug[TYPEC_PLUG_SOP_PP])
+ tbt->state = TBT_STATE_SOP_PP_ENTER;
+ else
+ tbt->state = TBT_STATE_ENTER;
+ } else if (sop == TYPEC_PLUG_SOP_PP)
+ tbt->state = TBT_STATE_ENTER;
+
+ break;
+ case CMD_EXIT_MODE:
+ /* Exit in opposite order: Port, SOP", then SOP'. */
+ if (sop == TYPEC_PLUG_SOP_PP)
+ tbt->state = TBT_STATE_SOP_P_EXIT;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (tbt->state != TBT_STATE_IDLE)
+ schedule_work(&tbt->work);
+
+ mutex_unlock(&tbt->lock);
+ return 0;
+}
+
+static int tbt_altmode_vdm(struct typec_altmode *alt,
+ const u32 hdr, const u32 *vdo, int count)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ struct typec_thunderbolt_data data;
+ int cmd_type = PD_VDO_CMDT(hdr);
+ int cmd = PD_VDO_CMD(hdr);
+
+ mutex_lock(&tbt->lock);
+
+ if (tbt->state != TBT_STATE_IDLE) {
+ mutex_unlock(&tbt->lock);
+ return -EBUSY;
+ }
+
+ switch (cmd_type) {
+ case CMDT_RSP_ACK:
+ /* Port altmode is last to enter and first to exit. */
+ switch (cmd) {
+ case CMD_ENTER_MODE:
+ memset(&data, 0, sizeof(data));
+
+ data.device_mode = tbt->alt->vdo;
+ data.enter_vdo = tbt->enter_vdo;
+ if (tbt->plug[TYPEC_PLUG_SOP_P])
+ data.cable_mode = tbt->plug[TYPEC_PLUG_SOP_P]->vdo;
+
+ typec_altmode_notify(alt, TYPEC_STATE_MODAL, &data);
+ break;
+ case CMD_EXIT_MODE:
+ if (tbt->plug[TYPEC_PLUG_SOP_PP])
+ tbt->state = TBT_STATE_SOP_PP_EXIT;
+ else if (tbt->plug[TYPEC_PLUG_SOP_P])
+ tbt->state = TBT_STATE_SOP_P_EXIT;
+ break;
+ }
+ break;
+ case CMDT_RSP_NAK:
+ switch (cmd) {
+ case CMD_ENTER_MODE:
+ dev_warn(&alt->dev, "Enter Mode refused\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (tbt->state != TBT_STATE_IDLE)
+ schedule_work(&tbt->work);
+
+ mutex_unlock(&tbt->lock);
+
+ return 0;
+}
+
+static int tbt_altmode_activate(struct typec_altmode *alt, int activate)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ int ret;
+
+ mutex_lock(&tbt->lock);
+
+ if (activate)
+ ret = tbt_enter_modes_ordered(alt);
+ else
+ ret = typec_altmode_exit(alt);
+
+ mutex_unlock(&tbt->lock);
+
+ return ret;
+}
+
+static const struct typec_altmode_ops tbt_altmode_ops = {
+ .vdm = tbt_altmode_vdm,
+ .activate = tbt_altmode_activate
+};
+
+static const struct typec_cable_ops tbt_cable_ops = {
+ .vdm = tbt_cable_altmode_vdm,
+};
+
+static int tbt_altmode_probe(struct typec_altmode *alt)
+{
+ struct tbt_altmode *tbt;
+
+ tbt = devm_kzalloc(&alt->dev, sizeof(*tbt), GFP_KERNEL);
+ if (!tbt)
+ return -ENOMEM;
+
+ INIT_WORK(&tbt->work, tbt_altmode_work);
+ mutex_init(&tbt->lock);
+ tbt->alt = alt;
+
+ alt->desc = "Thunderbolt3";
+ typec_altmode_set_drvdata(alt, tbt);
+ typec_altmode_set_ops(alt, &tbt_altmode_ops);
+
+ if (tbt_ready(alt)) {
+ if (tbt->plug[TYPEC_PLUG_SOP_P])
+ tbt->state = TBT_STATE_SOP_P_ENTER;
+ else if (tbt->plug[TYPEC_PLUG_SOP_PP])
+ tbt->state = TBT_STATE_SOP_PP_ENTER;
+ else
+ tbt->state = TBT_STATE_ENTER;
+ schedule_work(&tbt->work);
+ }
+
+ return 0;
+}
+
+static void tbt_altmode_remove(struct typec_altmode *alt)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+
+ for (int i = TYPEC_PLUG_SOP_PP; i >= 0; --i) {
+ if (tbt->plug[i])
+ typec_altmode_put_plug(tbt->plug[i]);
+ }
+
+ if (tbt->cable)
+ typec_cable_put(tbt->cable);
+}
+
+static bool tbt_ready(struct typec_altmode *alt)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ struct typec_altmode *plug;
+
+ if (tbt->cable)
+ return true;
+
+ /* Thunderbolt 3 requires a cable with eMarker */
+ tbt->cable = typec_cable_get(typec_altmode2port(tbt->alt));
+ if (!tbt->cable)
+ return false;
+
+ /* We accept systems without SOP' or SOP''. This means the port altmode
+ * driver will be responsible for properly ordering entry/exit.
+ */
+ for (int i = 0; i < TYPEC_PLUG_SOP_PP + 1; i++) {
+ plug = typec_altmode_get_plug(tbt->alt, i);
+ if (!plug)
+ continue;
+
+ if (plug->svid != USB_TYPEC_TBT_SID)
+ break;
+
+ plug->desc = "Thunderbolt3";
+ plug->cable_ops = &tbt_cable_ops;
+ typec_altmode_set_drvdata(plug, tbt);
+
+ tbt->plug[i] = plug;
+ }
+
+ return true;
+}
+
+static const struct typec_device_id tbt_typec_id[] = {
+ { USB_TYPEC_TBT_SID },
+ { }
+};
+MODULE_DEVICE_TABLE(typec, tbt_typec_id);
+
+static struct typec_altmode_driver tbt_altmode_driver = {
+ .id_table = tbt_typec_id,
+ .probe = tbt_altmode_probe,
+ .remove = tbt_altmode_remove,
+ .driver = {
+ .name = "typec-thunderbolt",
+ }
+};
+module_typec_altmode_driver(tbt_altmode_driver);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Thunderbolt3 USB Type-C Alternate Mode");
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index aa879253d3b8..a884cec9ab7e 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -449,13 +449,12 @@ ATTRIBUTE_GROUPS(typec);
static int typec_match(struct device *dev, const struct device_driver *driver)
{
- struct typec_altmode_driver *drv = to_altmode_driver(driver);
+ const struct typec_altmode_driver *drv = to_altmode_driver(driver);
struct typec_altmode *altmode = to_typec_altmode(dev);
const struct typec_device_id *id;
for (id = drv->id_table; id->svid; id++)
- if (id->svid == altmode->svid &&
- (id->mode == TYPEC_ANY_MODE || id->mode == altmode->mode))
+ if (id->svid == altmode->svid)
return 1;
return 0;
}
@@ -470,8 +469,7 @@ static int typec_uevent(const struct device *dev, struct kobj_uevent_env *env)
if (add_uevent_var(env, "MODE=%u", altmode->mode))
return -ENOMEM;
- return add_uevent_var(env, "MODALIAS=typec:id%04Xm%02X",
- altmode->svid, altmode->mode);
+ return add_uevent_var(env, "MODALIAS=typec:id%04X", altmode->svid);
}
static int typec_altmode_create_links(struct altmode *alt)
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 4b3047e055a3..67a533e35150 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -10,6 +10,7 @@
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/usb/pd_vdo.h>
#include <linux/usb/typec_mux.h>
#include <linux/usb/typec_retimer.h>
@@ -229,21 +230,21 @@ static const char * const usb_modes[] = {
/* ------------------------------------------------------------------------- */
/* Alternate Modes */
-static int altmode_match(struct device *dev, void *data)
+static int altmode_match(struct device *dev, const void *data)
{
struct typec_altmode *adev = to_typec_altmode(dev);
- struct typec_device_id *id = data;
+ const struct typec_device_id *id = data;
if (!is_typec_altmode(dev))
return 0;
- return ((adev->svid == id->svid) && (adev->mode == id->mode));
+ return (adev->svid == id->svid);
}
static void typec_altmode_set_partner(struct altmode *altmode)
{
struct typec_altmode *adev = &altmode->adev;
- struct typec_device_id id = { adev->svid, adev->mode, };
+ struct typec_device_id id = { adev->svid };
struct typec_port *port = typec_altmode2port(adev);
struct altmode *partner;
struct device *dev;
@@ -361,7 +362,7 @@ active_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct typec_altmode *alt = to_typec_altmode(dev);
- return sprintf(buf, "%s\n", alt->active ? "yes" : "no");
+ return sprintf(buf, "%s\n", str_yes_no(alt->active));
}
static ssize_t active_store(struct device *dev, struct device_attribute *attr,
@@ -458,7 +459,8 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
if (attr == &dev_attr_active.attr)
- if (!adev->ops || !adev->ops->activate)
+ if (!is_typec_port(adev->dev.parent) &&
+ (!adev->ops || !adev->ops->activate))
return 0444;
return attr->mode;
@@ -563,7 +565,7 @@ typec_register_altmode(struct device *parent,
if (is_port) {
alt->attrs[3] = &dev_attr_supported_roles.attr;
- alt->adev.active = true; /* Enabled by default */
+ alt->adev.active = !desc->inactive; /* Enabled by default */
}
sprintf(alt->group_name, "mode%d", desc->mode);
@@ -706,7 +708,7 @@ static ssize_t supports_usb_power_delivery_show(struct device *dev,
{
struct typec_partner *p = to_typec_partner(dev);
- return sprintf(buf, "%s\n", p->usb_pd ? "yes" : "no");
+ return sprintf(buf, "%s\n", str_yes_no(p->usb_pd));
}
static DEVICE_ATTR_RO(supports_usb_power_delivery);
@@ -1050,9 +1052,11 @@ struct typec_partner *typec_register_partner(struct typec_port *port,
partner->usb_mode = USB_MODE_USB3;
}
+ mutex_lock(&port->partner_link_lock);
ret = device_register(&partner->dev);
if (ret) {
dev_err(&port->dev, "failed to register partner (%d)\n", ret);
+ mutex_unlock(&port->partner_link_lock);
put_device(&partner->dev);
return ERR_PTR(ret);
}
@@ -1061,6 +1065,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port,
typec_partner_link_device(partner, port->usb2_dev);
if (port->usb3_dev)
typec_partner_link_device(partner, port->usb3_dev);
+ mutex_unlock(&port->partner_link_lock);
return partner;
}
@@ -1081,12 +1086,18 @@ void typec_unregister_partner(struct typec_partner *partner)
port = to_typec_port(partner->dev.parent);
- if (port->usb2_dev)
+ mutex_lock(&port->partner_link_lock);
+ if (port->usb2_dev) {
typec_partner_unlink_device(partner, port->usb2_dev);
- if (port->usb3_dev)
+ port->usb2_dev = NULL;
+ }
+ if (port->usb3_dev) {
typec_partner_unlink_device(partner, port->usb3_dev);
+ port->usb3_dev = NULL;
+ }
device_unregister(&partner->dev);
+ mutex_unlock(&port->partner_link_lock);
}
EXPORT_SYMBOL_GPL(typec_unregister_partner);
@@ -1282,11 +1293,6 @@ const struct device_type typec_cable_dev_type = {
.release = typec_cable_release,
};
-static int cable_match(struct device *dev, void *data)
-{
- return is_typec_cable(dev);
-}
-
/**
* typec_cable_get - Get a reference to the USB Type-C cable
* @port: The USB Type-C Port the cable is connected to
@@ -1298,7 +1304,8 @@ struct typec_cable *typec_cable_get(struct typec_port *port)
{
struct device *dev;
- dev = device_find_child(&port->dev, NULL, cable_match);
+ dev = device_find_child(&port->dev, &typec_cable_dev_type,
+ device_match_type);
if (!dev)
return NULL;
@@ -1858,7 +1865,7 @@ static ssize_t vconn_source_show(struct device *dev,
struct typec_port *port = to_typec_port(dev);
return sprintf(buf, "%s\n",
- port->vconn_role == TYPEC_SOURCE ? "yes" : "no");
+ str_yes_no(port->vconn_role == TYPEC_SOURCE));
}
static DEVICE_ATTR_RW(vconn_source);
@@ -2028,16 +2035,12 @@ const struct device_type typec_port_dev_type = {
/* --------------------------------------- */
/* Driver callbacks to report role updates */
-static int partner_match(struct device *dev, void *data)
-{
- return is_typec_partner(dev);
-}
-
static struct typec_partner *typec_get_partner(struct typec_port *port)
{
struct device *dev;
- dev = device_find_child(&port->dev, NULL, partner_match);
+ dev = device_find_child(&port->dev, &typec_partner_dev_type,
+ device_match_type);
if (!dev)
return NULL;
@@ -2047,10 +2050,11 @@ static struct typec_partner *typec_get_partner(struct typec_port *port)
static void typec_partner_attach(struct typec_connector *con, struct device *dev)
{
struct typec_port *port = container_of(con, struct typec_port, con);
- struct typec_partner *partner = typec_get_partner(port);
+ struct typec_partner *partner;
struct usb_device *udev = to_usb_device(dev);
enum usb_mode usb_mode;
+ mutex_lock(&port->partner_link_lock);
if (udev->speed < USB_SPEED_SUPER) {
usb_mode = USB_MODE_USB2;
port->usb2_dev = dev;
@@ -2059,18 +2063,22 @@ static void typec_partner_attach(struct typec_connector *con, struct device *dev
port->usb3_dev = dev;
}
+ partner = typec_get_partner(port);
if (partner) {
typec_partner_set_usb_mode(partner, usb_mode);
typec_partner_link_device(partner, dev);
put_device(&partner->dev);
}
+ mutex_unlock(&port->partner_link_lock);
}
static void typec_partner_deattach(struct typec_connector *con, struct device *dev)
{
struct typec_port *port = container_of(con, struct typec_port, con);
- struct typec_partner *partner = typec_get_partner(port);
+ struct typec_partner *partner;
+ mutex_lock(&port->partner_link_lock);
+ partner = typec_get_partner(port);
if (partner) {
typec_partner_unlink_device(partner, dev);
put_device(&partner->dev);
@@ -2080,6 +2088,7 @@ static void typec_partner_deattach(struct typec_connector *con, struct device *d
port->usb2_dev = NULL;
else if (port->usb3_dev == dev)
port->usb3_dev = NULL;
+ mutex_unlock(&port->partner_link_lock);
}
/**
@@ -2170,7 +2179,9 @@ void typec_set_pwr_opmode(struct typec_port *port,
sysfs_notify(&port->dev.kobj, NULL, "power_operation_mode");
kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
- partner_dev = device_find_child(&port->dev, NULL, partner_match);
+ partner_dev = device_find_child(&port->dev,
+ &typec_partner_dev_type,
+ device_match_type);
if (partner_dev) {
struct typec_partner *partner = to_typec_partner(partner_dev);
@@ -2334,7 +2345,9 @@ int typec_get_negotiated_svdm_version(struct typec_port *port)
enum usb_pd_svdm_ver svdm_version;
struct device *partner_dev;
- partner_dev = device_find_child(&port->dev, NULL, partner_match);
+ partner_dev = device_find_child(&port->dev,
+ &typec_partner_dev_type,
+ device_match_type);
if (!partner_dev)
return -ENODEV;
@@ -2361,7 +2374,8 @@ int typec_get_cable_svdm_version(struct typec_port *port)
enum usb_pd_svdm_ver svdm_version;
struct device *cable_dev;
- cable_dev = device_find_child(&port->dev, NULL, cable_match);
+ cable_dev = device_find_child(&port->dev, &typec_cable_dev_type,
+ device_match_type);
if (!cable_dev)
return -ENODEV;
@@ -2615,6 +2629,7 @@ struct typec_port *typec_register_port(struct device *parent,
ida_init(&port->mode_ids);
mutex_init(&port->port_type_lock);
+ mutex_init(&port->partner_link_lock);
port->id = id;
port->ops = cap->ops;
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index b3076a24ad2e..db2fe96c48ff 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -59,6 +59,7 @@ struct typec_port {
enum typec_port_type port_type;
enum usb_mode usb_mode;
struct mutex port_type_lock;
+ struct mutex partner_link_lock;
enum typec_orientation orientation;
struct typec_switch *sw;
diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c
index fb1242e82ffd..3ecc688dda82 100644
--- a/drivers/usb/typec/hd3ss3220.c
+++ b/drivers/usb/typec/hd3ss3220.c
@@ -16,10 +16,17 @@
#include <linux/delay.h>
#include <linux/workqueue.h>
+#define HD3SS3220_REG_CN_STAT 0x08
#define HD3SS3220_REG_CN_STAT_CTRL 0x09
#define HD3SS3220_REG_GEN_CTRL 0x0A
#define HD3SS3220_REG_DEV_REV 0xA0
+/* Register HD3SS3220_REG_CN_STAT */
+#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_MASK (BIT(7) | BIT(6))
+#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_DEFAULT 0x00
+#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_MID BIT(6)
+#define HD3SS3220_REG_CN_STAT_CURRENT_MODE_HIGH BIT(7)
+
/* Register HD3SS3220_REG_CN_STAT_CTRL*/
#define HD3SS3220_REG_CN_STAT_CTRL_ATTACHED_STATE_MASK (BIT(7) | BIT(6))
#define HD3SS3220_REG_CN_STAT_CTRL_AS_DFP BIT(6)
@@ -28,10 +35,16 @@
#define HD3SS3220_REG_CN_STAT_CTRL_INT_STATUS BIT(4)
/* Register HD3SS3220_REG_GEN_CTRL*/
+#define HD3SS3220_REG_GEN_CTRL_DISABLE_TERM BIT(0)
#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK (BIT(2) | BIT(1))
#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT 0x00
#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK BIT(1)
#define HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC (BIT(2) | BIT(1))
+#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_MASK (BIT(5) | BIT(4))
+#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DEFAULT 0x00
+#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DFP BIT(5)
+#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_UFP BIT(4)
+#define HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DRP (BIT(5) | BIT(4))
struct hd3ss3220 {
struct device *dev;
@@ -43,8 +56,96 @@ struct hd3ss3220 {
bool poll;
};
-static int hd3ss3220_set_source_pref(struct hd3ss3220 *hd3ss3220, int src_pref)
+static int hd3ss3220_set_power_opmode(struct hd3ss3220 *hd3ss3220, int power_opmode)
+{
+ int current_mode;
+
+ switch (power_opmode) {
+ case TYPEC_PWR_MODE_USB:
+ current_mode = HD3SS3220_REG_CN_STAT_CURRENT_MODE_DEFAULT;
+ break;
+ case TYPEC_PWR_MODE_1_5A:
+ current_mode = HD3SS3220_REG_CN_STAT_CURRENT_MODE_MID;
+ break;
+ case TYPEC_PWR_MODE_3_0A:
+ current_mode = HD3SS3220_REG_CN_STAT_CURRENT_MODE_HIGH;
+ break;
+ case TYPEC_PWR_MODE_PD: /* Power delivery not supported */
+ default:
+ dev_err(hd3ss3220->dev, "bad power operation mode: %d\n", power_opmode);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT,
+ HD3SS3220_REG_CN_STAT_CURRENT_MODE_MASK,
+ current_mode);
+}
+
+static int hd3ss3220_set_port_type(struct hd3ss3220 *hd3ss3220, int type)
+{
+ int mode_select, err;
+
+ switch (type) {
+ case TYPEC_PORT_SRC:
+ mode_select = HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DFP;
+ break;
+ case TYPEC_PORT_SNK:
+ mode_select = HD3SS3220_REG_GEN_CTRL_MODE_SELECT_UFP;
+ break;
+ case TYPEC_PORT_DRP:
+ mode_select = HD3SS3220_REG_GEN_CTRL_MODE_SELECT_DRP;
+ break;
+ default:
+ dev_err(hd3ss3220->dev, "bad port type: %d\n", type);
+ return -EINVAL;
+ }
+
+ /* Disable termination before changing MODE_SELECT as required by datasheet */
+ err = regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
+ HD3SS3220_REG_GEN_CTRL_DISABLE_TERM,
+ HD3SS3220_REG_GEN_CTRL_DISABLE_TERM);
+ if (err < 0) {
+ dev_err(hd3ss3220->dev, "Failed to disable port for mode change: %d\n", err);
+ return err;
+ }
+
+ err = regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
+ HD3SS3220_REG_GEN_CTRL_MODE_SELECT_MASK,
+ mode_select);
+ if (err < 0) {
+ dev_err(hd3ss3220->dev, "Failed to change mode: %d\n", err);
+ regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
+ HD3SS3220_REG_GEN_CTRL_DISABLE_TERM, 0);
+ return err;
+ }
+
+ err = regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
+ HD3SS3220_REG_GEN_CTRL_DISABLE_TERM, 0);
+ if (err < 0)
+ dev_err(hd3ss3220->dev, "Failed to re-enable port after mode change: %d\n", err);
+
+ return err;
+}
+
+static int hd3ss3220_set_source_pref(struct hd3ss3220 *hd3ss3220, int prefer_role)
{
+ int src_pref;
+
+ switch (prefer_role) {
+ case TYPEC_NO_PREFERRED_ROLE:
+ src_pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT;
+ break;
+ case TYPEC_SINK:
+ src_pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK;
+ break;
+ case TYPEC_SOURCE:
+ src_pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC;
+ break;
+ default:
+ dev_err(hd3ss3220->dev, "bad role preference: %d\n", prefer_role);
+ return -EINVAL;
+ }
+
return regmap_update_bits(hd3ss3220->regmap, HD3SS3220_REG_GEN_CTRL,
HD3SS3220_REG_GEN_CTRL_SRC_PREF_MASK,
src_pref);
@@ -76,31 +177,23 @@ static enum usb_role hd3ss3220_get_attached_state(struct hd3ss3220 *hd3ss3220)
return attached_state;
}
-static int hd3ss3220_dr_set(struct typec_port *port, enum typec_data_role role)
+static int hd3ss3220_try_role(struct typec_port *port, int role)
{
struct hd3ss3220 *hd3ss3220 = typec_get_drvdata(port);
- enum usb_role role_val;
- int pref, ret = 0;
- if (role == TYPEC_HOST) {
- role_val = USB_ROLE_HOST;
- pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SRC;
- } else {
- role_val = USB_ROLE_DEVICE;
- pref = HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_TRY_SNK;
- }
-
- ret = hd3ss3220_set_source_pref(hd3ss3220, pref);
- usleep_range(10, 100);
+ return hd3ss3220_set_source_pref(hd3ss3220, role);
+}
- usb_role_switch_set_role(hd3ss3220->role_sw, role_val);
- typec_set_data_role(hd3ss3220->port, role);
+static int hd3ss3220_port_type_set(struct typec_port *port, enum typec_port_type type)
+{
+ struct hd3ss3220 *hd3ss3220 = typec_get_drvdata(port);
- return ret;
+ return hd3ss3220_set_port_type(hd3ss3220, type);
}
static const struct typec_operations hd3ss3220_ops = {
- .dr_set = hd3ss3220_dr_set
+ .try_role = hd3ss3220_try_role,
+ .port_type_set = hd3ss3220_port_type_set,
};
static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
@@ -108,9 +201,6 @@ static void hd3ss3220_set_role(struct hd3ss3220 *hd3ss3220)
enum usb_role role_state = hd3ss3220_get_attached_state(hd3ss3220);
usb_role_switch_set_role(hd3ss3220->role_sw, role_state);
- if (role_state == USB_ROLE_NONE)
- hd3ss3220_set_source_pref(hd3ss3220,
- HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT);
switch (role_state) {
case USB_ROLE_HOST:
@@ -162,6 +252,67 @@ static irqreturn_t hd3ss3220_irq_handler(int irq, void *data)
return hd3ss3220_irq(hd3ss3220);
}
+static int hd3ss3220_configure_power_opmode(struct hd3ss3220 *hd3ss3220,
+ struct fwnode_handle *connector)
+{
+ /*
+ * Supported power operation mode can be configured through device tree
+ */
+ const char *cap_str;
+ int ret, power_opmode;
+
+ ret = fwnode_property_read_string(connector, "typec-power-opmode", &cap_str);
+ if (ret)
+ return 0;
+
+ power_opmode = typec_find_pwr_opmode(cap_str);
+ return hd3ss3220_set_power_opmode(hd3ss3220, power_opmode);
+}
+
+static int hd3ss3220_configure_port_type(struct hd3ss3220 *hd3ss3220,
+ struct fwnode_handle *connector,
+ struct typec_capability *cap)
+{
+ /*
+ * Port type can be configured through device tree
+ */
+ const char *cap_str;
+ int ret;
+
+ ret = fwnode_property_read_string(connector, "power-role", &cap_str);
+ if (ret)
+ return 0;
+
+ ret = typec_find_port_power_role(cap_str);
+ if (ret < 0)
+ return ret;
+
+ cap->type = ret;
+ return hd3ss3220_set_port_type(hd3ss3220, cap->type);
+}
+
+static int hd3ss3220_configure_source_pref(struct hd3ss3220 *hd3ss3220,
+ struct fwnode_handle *connector,
+ struct typec_capability *cap)
+{
+ /*
+ * Preferred role can be configured through device tree
+ */
+ const char *cap_str;
+ int ret;
+
+ ret = fwnode_property_read_string(connector, "try-power-role", &cap_str);
+ if (ret)
+ return 0;
+
+ ret = typec_find_power_role(cap_str);
+ if (ret < 0)
+ return ret;
+
+ cap->prefer_role = ret;
+ return hd3ss3220_set_source_pref(hd3ss3220, cap->prefer_role);
+}
+
static const struct regmap_config config = {
.reg_bits = 8,
.val_bits = 8,
@@ -188,8 +339,6 @@ static int hd3ss3220_probe(struct i2c_client *client)
if (IS_ERR(hd3ss3220->regmap))
return PTR_ERR(hd3ss3220->regmap);
- hd3ss3220_set_source_pref(hd3ss3220,
- HD3SS3220_REG_GEN_CTRL_SRC_PREF_DRP_DEFAULT);
/* For backward compatibility check the connector child node first */
connector = device_get_named_child_node(hd3ss3220->dev, "connector");
if (connector) {
@@ -217,12 +366,24 @@ static int hd3ss3220_probe(struct i2c_client *client)
typec_cap.ops = &hd3ss3220_ops;
typec_cap.fwnode = connector;
+ ret = hd3ss3220_configure_source_pref(hd3ss3220, connector, &typec_cap);
+ if (ret < 0)
+ goto err_put_role;
+
+ ret = hd3ss3220_configure_port_type(hd3ss3220, connector, &typec_cap);
+ if (ret < 0)
+ goto err_put_role;
+
hd3ss3220->port = typec_register_port(&client->dev, &typec_cap);
if (IS_ERR(hd3ss3220->port)) {
ret = PTR_ERR(hd3ss3220->port);
goto err_put_role;
}
+ ret = hd3ss3220_configure_power_opmode(hd3ss3220, connector);
+ if (ret < 0)
+ goto err_unreg_port;
+
hd3ss3220_set_role(hd3ss3220);
ret = regmap_read(hd3ss3220->regmap, HD3SS3220_REG_CN_STAT_CTRL, &data);
if (ret < 0)
diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
index 67381b4ef4f6..6dd8f961b593 100644
--- a/drivers/usb/typec/mux/Kconfig
+++ b/drivers/usb/typec/mux/Kconfig
@@ -56,6 +56,16 @@ config TYPEC_MUX_NB7VPQ904M
Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C
redriver chip found on some devices with a Type-C port.
+config TYPEC_MUX_PS883X
+ tristate "Parade PS883x Type-C retimer driver"
+ depends on I2C
+ depends on DRM || DRM=n
+ select DRM_AUX_BRIDGE if DRM_BRIDGE && OF
+ select REGMAP_I2C
+ help
+ Say Y or M if your system has a Parade PS883x Type-C retimer chip
+ found on some devices with a Type-C port.
+
config TYPEC_MUX_PTN36502
tristate "NXP PTN36502 Type-C redriver driver"
depends on I2C
diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile
index 60879446da93..b4f599eb5053 100644
--- a/drivers/usb/typec/mux/Makefile
+++ b/drivers/usb/typec/mux/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o
obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o
obj-$(CONFIG_TYPEC_MUX_IT5205) += it5205.o
obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M) += nb7vpq904m.o
+obj-$(CONFIG_TYPEC_MUX_PS883X) += ps883x.o
obj-$(CONFIG_TYPEC_MUX_PTN36502) += ptn36502.o
obj-$(CONFIG_TYPEC_MUX_TUSB1046) += tusb1046.o
obj-$(CONFIG_TYPEC_MUX_WCD939X_USBSS) += wcd939x-usbss.o
diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c
index 5dfe95754394..65dda9183e6f 100644
--- a/drivers/usb/typec/mux/intel_pmc_mux.c
+++ b/drivers/usb/typec/mux/intel_pmc_mux.c
@@ -718,7 +718,7 @@ DEFINE_SHOW_ATTRIBUTE(port_iom_status);
static void pmc_mux_port_debugfs_init(struct pmc_usb_port *port)
{
struct dentry *debugfs_dir;
- char name[6];
+ char name[8];
snprintf(name, sizeof(name), "port%d", port->usb3_port - 1);
diff --git a/drivers/usb/typec/mux/ps883x.c b/drivers/usb/typec/mux/ps883x.c
new file mode 100644
index 000000000000..ad59babf7cce
--- /dev/null
+++ b/drivers/usb/typec/mux/ps883x.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Parade ps883x usb retimer driver
+ *
+ * Copyright (C) 2024 Linaro Ltd.
+ */
+
+#include <drm/bridge/aux-bridge.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_retimer.h>
+
+#define REG_USB_PORT_CONN_STATUS_0 0x00
+
+#define CONN_STATUS_0_CONNECTION_PRESENT BIT(0)
+#define CONN_STATUS_0_ORIENTATION_REVERSED BIT(1)
+#define CONN_STATUS_0_USB_3_1_CONNECTED BIT(5)
+
+#define REG_USB_PORT_CONN_STATUS_1 0x01
+
+#define CONN_STATUS_1_DP_CONNECTED BIT(0)
+#define CONN_STATUS_1_DP_SINK_REQUESTED BIT(1)
+#define CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D BIT(2)
+#define CONN_STATUS_1_DP_HPD_LEVEL BIT(7)
+
+#define REG_USB_PORT_CONN_STATUS_2 0x02
+
+struct ps883x_retimer {
+ struct i2c_client *client;
+ struct gpio_desc *reset_gpio;
+ struct regmap *regmap;
+ struct typec_switch_dev *sw;
+ struct typec_retimer *retimer;
+ struct clk *xo_clk;
+ struct regulator *vdd_supply;
+ struct regulator *vdd33_supply;
+ struct regulator *vdd33_cap_supply;
+ struct regulator *vddat_supply;
+ struct regulator *vddar_supply;
+ struct regulator *vddio_supply;
+
+ struct typec_switch *typec_switch;
+ struct typec_mux *typec_mux;
+
+ struct mutex lock; /* protect non-concurrent retimer & switch */
+
+ enum typec_orientation orientation;
+ unsigned long mode;
+ unsigned int svid;
+};
+
+static int ps883x_configure(struct ps883x_retimer *retimer, int cfg0,
+ int cfg1, int cfg2)
+{
+ struct device *dev = &retimer->client->dev;
+ int ret;
+
+ ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_0, cfg0);
+ if (ret) {
+ dev_err(dev, "failed to write conn_status_0: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_1, cfg1);
+ if (ret) {
+ dev_err(dev, "failed to write conn_status_1: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(retimer->regmap, REG_USB_PORT_CONN_STATUS_2, cfg2);
+ if (ret) {
+ dev_err(dev, "failed to write conn_status_2: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ps883x_set(struct ps883x_retimer *retimer)
+{
+ int cfg0 = CONN_STATUS_0_CONNECTION_PRESENT;
+ int cfg1 = 0x00;
+ int cfg2 = 0x00;
+
+ if (retimer->orientation == TYPEC_ORIENTATION_NONE ||
+ retimer->mode == TYPEC_STATE_SAFE) {
+ return ps883x_configure(retimer, cfg0, cfg1, cfg2);
+ }
+
+ if (retimer->mode != TYPEC_STATE_USB && retimer->svid != USB_TYPEC_DP_SID)
+ return -EINVAL;
+
+ if (retimer->orientation == TYPEC_ORIENTATION_REVERSE)
+ cfg0 |= CONN_STATUS_0_ORIENTATION_REVERSED;
+
+ switch (retimer->mode) {
+ case TYPEC_STATE_USB:
+ cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED;
+ break;
+
+ case TYPEC_DP_STATE_C:
+ cfg1 = CONN_STATUS_1_DP_CONNECTED |
+ CONN_STATUS_1_DP_SINK_REQUESTED |
+ CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D |
+ CONN_STATUS_1_DP_HPD_LEVEL;
+ break;
+
+ case TYPEC_DP_STATE_D:
+ cfg0 |= CONN_STATUS_0_USB_3_1_CONNECTED;
+ cfg1 = CONN_STATUS_1_DP_CONNECTED |
+ CONN_STATUS_1_DP_SINK_REQUESTED |
+ CONN_STATUS_1_DP_PIN_ASSIGNMENT_C_D |
+ CONN_STATUS_1_DP_HPD_LEVEL;
+ break;
+
+ case TYPEC_DP_STATE_E:
+ cfg1 = CONN_STATUS_1_DP_CONNECTED |
+ CONN_STATUS_1_DP_HPD_LEVEL;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ps883x_configure(retimer, cfg0, cfg1, cfg2);
+}
+
+static int ps883x_sw_set(struct typec_switch_dev *sw,
+ enum typec_orientation orientation)
+{
+ struct ps883x_retimer *retimer = typec_switch_get_drvdata(sw);
+ int ret = 0;
+
+ ret = typec_switch_set(retimer->typec_switch, orientation);
+ if (ret)
+ return ret;
+
+ mutex_lock(&retimer->lock);
+
+ if (retimer->orientation != orientation) {
+ retimer->orientation = orientation;
+
+ ret = ps883x_set(retimer);
+ }
+
+ mutex_unlock(&retimer->lock);
+
+ return ret;
+}
+
+static int ps883x_retimer_set(struct typec_retimer *rtmr,
+ struct typec_retimer_state *state)
+{
+ struct ps883x_retimer *retimer = typec_retimer_get_drvdata(rtmr);
+ struct typec_mux_state mux_state;
+ int ret = 0;
+
+ mutex_lock(&retimer->lock);
+
+ if (state->mode != retimer->mode) {
+ retimer->mode = state->mode;
+
+ if (state->alt)
+ retimer->svid = state->alt->svid;
+ else
+ retimer->svid = 0;
+
+ ret = ps883x_set(retimer);
+ }
+
+ mutex_unlock(&retimer->lock);
+
+ if (ret)
+ return ret;
+
+ mux_state.alt = state->alt;
+ mux_state.data = state->data;
+ mux_state.mode = state->mode;
+
+ return typec_mux_set(retimer->typec_mux, &mux_state);
+}
+
+static int ps883x_enable_vregs(struct ps883x_retimer *retimer)
+{
+ struct device *dev = &retimer->client->dev;
+ int ret;
+
+ ret = regulator_enable(retimer->vdd33_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD 3.3V regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(retimer->vdd33_cap_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD 3.3V CAP regulator: %d\n", ret);
+ goto err_vdd33_disable;
+ }
+
+ usleep_range(4000, 10000);
+
+ ret = regulator_enable(retimer->vdd_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD regulator: %d\n", ret);
+ goto err_vdd33_cap_disable;
+ }
+
+ ret = regulator_enable(retimer->vddar_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD AR regulator: %d\n", ret);
+ goto err_vdd_disable;
+ }
+
+ ret = regulator_enable(retimer->vddat_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD AT regulator: %d\n", ret);
+ goto err_vddar_disable;
+ }
+
+ ret = regulator_enable(retimer->vddio_supply);
+ if (ret) {
+ dev_err(dev, "cannot enable VDD IO regulator: %d\n", ret);
+ goto err_vddat_disable;
+ }
+
+ return 0;
+
+err_vddat_disable:
+ regulator_disable(retimer->vddat_supply);
+err_vddar_disable:
+ regulator_disable(retimer->vddar_supply);
+err_vdd_disable:
+ regulator_disable(retimer->vdd_supply);
+err_vdd33_cap_disable:
+ regulator_disable(retimer->vdd33_cap_supply);
+err_vdd33_disable:
+ regulator_disable(retimer->vdd33_supply);
+
+ return ret;
+}
+
+static void ps883x_disable_vregs(struct ps883x_retimer *retimer)
+{
+ regulator_disable(retimer->vddio_supply);
+ regulator_disable(retimer->vddat_supply);
+ regulator_disable(retimer->vddar_supply);
+ regulator_disable(retimer->vdd_supply);
+ regulator_disable(retimer->vdd33_cap_supply);
+ regulator_disable(retimer->vdd33_supply);
+}
+
+static int ps883x_get_vregs(struct ps883x_retimer *retimer)
+{
+ struct device *dev = &retimer->client->dev;
+
+ retimer->vdd_supply = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(retimer->vdd_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vdd_supply),
+ "failed to get VDD\n");
+
+ retimer->vdd33_supply = devm_regulator_get(dev, "vdd33");
+ if (IS_ERR(retimer->vdd33_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vdd33_supply),
+ "failed to get VDD 3.3V\n");
+
+ retimer->vdd33_cap_supply = devm_regulator_get(dev, "vdd33-cap");
+ if (IS_ERR(retimer->vdd33_cap_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vdd33_cap_supply),
+ "failed to get VDD CAP 3.3V\n");
+
+ retimer->vddat_supply = devm_regulator_get(dev, "vddat");
+ if (IS_ERR(retimer->vddat_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vddat_supply),
+ "failed to get VDD AT\n");
+
+ retimer->vddar_supply = devm_regulator_get(dev, "vddar");
+ if (IS_ERR(retimer->vddar_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vddar_supply),
+ "failed to get VDD AR\n");
+
+ retimer->vddio_supply = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(retimer->vddio_supply))
+ return dev_err_probe(dev, PTR_ERR(retimer->vddio_supply),
+ "failed to get VDD IO\n");
+
+ return 0;
+}
+
+static const struct regmap_config ps883x_retimer_regmap = {
+ .max_register = 0x1f,
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int ps883x_retimer_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct typec_switch_desc sw_desc = { };
+ struct typec_retimer_desc rtmr_desc = { };
+ struct ps883x_retimer *retimer;
+ unsigned int val;
+ int ret;
+
+ retimer = devm_kzalloc(dev, sizeof(*retimer), GFP_KERNEL);
+ if (!retimer)
+ return -ENOMEM;
+
+ retimer->client = client;
+
+ mutex_init(&retimer->lock);
+
+ retimer->regmap = devm_regmap_init_i2c(client, &ps883x_retimer_regmap);
+ if (IS_ERR(retimer->regmap))
+ return dev_err_probe(dev, PTR_ERR(retimer->regmap),
+ "failed to allocate register map\n");
+
+ ret = ps883x_get_vregs(retimer);
+ if (ret)
+ return ret;
+
+ retimer->xo_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(retimer->xo_clk))
+ return dev_err_probe(dev, PTR_ERR(retimer->xo_clk),
+ "failed to get xo clock\n");
+
+ retimer->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+ if (IS_ERR(retimer->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(retimer->reset_gpio),
+ "failed to get reset gpio\n");
+
+ retimer->typec_switch = typec_switch_get(dev);
+ if (IS_ERR(retimer->typec_switch))
+ return dev_err_probe(dev, PTR_ERR(retimer->typec_switch),
+ "failed to acquire orientation-switch\n");
+
+ retimer->typec_mux = typec_mux_get(dev);
+ if (IS_ERR(retimer->typec_mux)) {
+ ret = dev_err_probe(dev, PTR_ERR(retimer->typec_mux),
+ "failed to acquire mode-mux\n");
+ goto err_switch_put;
+ }
+
+ ret = drm_aux_bridge_register(dev);
+ if (ret)
+ goto err_mux_put;
+
+ ret = ps883x_enable_vregs(retimer);
+ if (ret)
+ goto err_mux_put;
+
+ ret = clk_prepare_enable(retimer->xo_clk);
+ if (ret) {
+ dev_err(dev, "failed to enable XO: %d\n", ret);
+ goto err_vregs_disable;
+ }
+
+ /* skip resetting if already configured */
+ if (regmap_test_bits(retimer->regmap, REG_USB_PORT_CONN_STATUS_0,
+ CONN_STATUS_0_CONNECTION_PRESENT) == 1) {
+ gpiod_direction_output(retimer->reset_gpio, 0);
+ } else {
+ gpiod_direction_output(retimer->reset_gpio, 1);
+
+ /* VDD IO supply enable to reset release delay */
+ usleep_range(4000, 14000);
+
+ gpiod_set_value(retimer->reset_gpio, 0);
+
+ /* firmware initialization delay */
+ msleep(60);
+
+ /* make sure device is accessible */
+ ret = regmap_read(retimer->regmap, REG_USB_PORT_CONN_STATUS_0,
+ &val);
+ if (ret) {
+ dev_err(dev, "failed to read conn_status_0: %d\n", ret);
+ if (ret == -ENXIO)
+ ret = -EIO;
+ goto err_clk_disable;
+ }
+ }
+
+ sw_desc.drvdata = retimer;
+ sw_desc.fwnode = dev_fwnode(dev);
+ sw_desc.set = ps883x_sw_set;
+
+ retimer->sw = typec_switch_register(dev, &sw_desc);
+ if (IS_ERR(retimer->sw)) {
+ ret = PTR_ERR(retimer->sw);
+ dev_err(dev, "failed to register typec switch: %d\n", ret);
+ goto err_clk_disable;
+ }
+
+ rtmr_desc.drvdata = retimer;
+ rtmr_desc.fwnode = dev_fwnode(dev);
+ rtmr_desc.set = ps883x_retimer_set;
+
+ retimer->retimer = typec_retimer_register(dev, &rtmr_desc);
+ if (IS_ERR(retimer->retimer)) {
+ ret = PTR_ERR(retimer->retimer);
+ dev_err(dev, "failed to register typec retimer: %d\n", ret);
+ goto err_switch_unregister;
+ }
+
+ return 0;
+
+err_switch_unregister:
+ typec_switch_unregister(retimer->sw);
+err_clk_disable:
+ clk_disable_unprepare(retimer->xo_clk);
+err_vregs_disable:
+ gpiod_set_value(retimer->reset_gpio, 1);
+ ps883x_disable_vregs(retimer);
+err_mux_put:
+ typec_mux_put(retimer->typec_mux);
+err_switch_put:
+ typec_switch_put(retimer->typec_switch);
+
+ return ret;
+}
+
+static void ps883x_retimer_remove(struct i2c_client *client)
+{
+ struct ps883x_retimer *retimer = i2c_get_clientdata(client);
+
+ typec_retimer_unregister(retimer->retimer);
+ typec_switch_unregister(retimer->sw);
+
+ gpiod_set_value(retimer->reset_gpio, 1);
+
+ clk_disable_unprepare(retimer->xo_clk);
+
+ ps883x_disable_vregs(retimer);
+
+ typec_mux_put(retimer->typec_mux);
+ typec_switch_put(retimer->typec_switch);
+}
+
+static const struct of_device_id ps883x_retimer_of_table[] = {
+ { .compatible = "parade,ps8830" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ps883x_retimer_of_table);
+
+static struct i2c_driver ps883x_retimer_driver = {
+ .driver = {
+ .name = "ps883x_retimer",
+ .of_match_table = ps883x_retimer_of_table,
+ },
+ .probe = ps883x_retimer_probe,
+ .remove = ps883x_retimer_remove,
+};
+
+module_i2c_driver(ps883x_retimer_driver);
+
+MODULE_DESCRIPTION("Parade ps883x Type-C Retimer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index e2fe479e16ad..f15c63d3a8f4 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
+#include <linux/string_choices.h>
#include <linux/types.h>
#include <linux/usb.h>
#include <linux/usb/typec.h>
@@ -733,7 +734,7 @@ static int tcpm_set_vconn(struct tcpc_dev *dev, bool on)
mutex_lock(&chip->lock);
if (chip->vconn_on == on) {
- fusb302_log(chip, "vconn is already %s", on ? "On" : "Off");
+ fusb302_log(chip, "vconn is already %s", str_on_off(on));
goto done;
}
if (on) {
@@ -746,7 +747,7 @@ static int tcpm_set_vconn(struct tcpc_dev *dev, bool on)
if (ret < 0)
goto done;
chip->vconn_on = on;
- fusb302_log(chip, "vconn := %s", on ? "On" : "Off");
+ fusb302_log(chip, "vconn := %s", str_on_off(on));
done:
mutex_unlock(&chip->lock);
@@ -761,7 +762,7 @@ static int tcpm_set_vbus(struct tcpc_dev *dev, bool on, bool charge)
mutex_lock(&chip->lock);
if (chip->vbus_on == on) {
- fusb302_log(chip, "vbus is already %s", on ? "On" : "Off");
+ fusb302_log(chip, "vbus is already %s", str_on_off(on));
} else {
if (on)
ret = regulator_enable(chip->vbus);
@@ -769,15 +770,14 @@ static int tcpm_set_vbus(struct tcpc_dev *dev, bool on, bool charge)
ret = regulator_disable(chip->vbus);
if (ret < 0) {
fusb302_log(chip, "cannot %s vbus regulator, ret=%d",
- on ? "enable" : "disable", ret);
+ str_enable_disable(on), ret);
goto done;
}
chip->vbus_on = on;
- fusb302_log(chip, "vbus := %s", on ? "On" : "Off");
+ fusb302_log(chip, "vbus := %s", str_on_off(on));
}
if (chip->charge_on == charge)
- fusb302_log(chip, "charge is already %s",
- charge ? "On" : "Off");
+ fusb302_log(chip, "charge is already %s", str_on_off(charge));
else
chip->charge_on = charge;
@@ -854,16 +854,16 @@ static int tcpm_set_pd_rx(struct tcpc_dev *dev, bool on)
ret = fusb302_pd_set_auto_goodcrc(chip, on);
if (ret < 0) {
fusb302_log(chip, "cannot turn %s auto GCRC, ret=%d",
- on ? "on" : "off", ret);
+ str_on_off(on), ret);
goto done;
}
ret = fusb302_pd_set_interrupts(chip, on);
if (ret < 0) {
fusb302_log(chip, "cannot turn %s pd interrupts, ret=%d",
- on ? "on" : "off", ret);
+ str_on_off(on), ret);
goto done;
}
- fusb302_log(chip, "pd := %s", on ? "on" : "off");
+ fusb302_log(chip, "pd := %s", str_on_off(on));
done:
mutex_unlock(&chip->lock);
@@ -1531,7 +1531,7 @@ static void fusb302_irq_work(struct work_struct *work)
if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) {
vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK);
fusb302_log(chip, "IRQ: VBUS_OK, vbus=%s",
- vbus_present ? "On" : "Off");
+ str_on_off(vbus_present));
if (vbus_present != chip->vbus_present) {
chip->vbus_present = vbus_present;
tcpm_vbus_change(chip->tcpm_port);
@@ -1562,7 +1562,7 @@ static void fusb302_irq_work(struct work_struct *work)
if ((interrupt & FUSB_REG_INTERRUPT_COMP_CHNG) && intr_comp_chng) {
comp_result = !!(status0 & FUSB_REG_STATUS0_COMP);
fusb302_log(chip, "IRQ: COMP_CHNG, comp=%s",
- comp_result ? "true" : "false");
+ str_true_false(comp_result));
if (comp_result) {
/* cc level > Rd_threshold, detach */
chip->cc1 = TYPEC_CC_OPEN;
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
index 726423684bae..18303b34594b 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
@@ -12,6 +12,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/usb/pd.h>
#include <linux/usb/tcpm.h>
#include "qcom_pmic_typec.h"
@@ -418,7 +419,7 @@ static int qcom_pmic_typec_pdphy_set_pd_rx(struct tcpc_dev *tcpc, bool on)
spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags);
- dev_dbg(pmic_typec_pdphy->dev, "set_pd_rx: %s\n", on ? "on" : "off");
+ dev_dbg(pmic_typec_pdphy->dev, "set_pd_rx: %s\n", str_on_off(on));
return ret;
}
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy_stub.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy_stub.c
index df79059cda67..8fac171778da 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy_stub.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy_stub.c
@@ -12,6 +12,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/usb/pd.h>
#include <linux/usb/tcpm.h>
#include "qcom_pmic_typec.h"
@@ -38,7 +39,7 @@ static int qcom_pmic_typec_pdphy_stub_set_pd_rx(struct tcpc_dev *tcpc, bool on)
struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc);
struct device *dev = tcpm->dev;
- dev_dbg(dev, "set_pd_rx: %s\n", on ? "on" : "off");
+ dev_dbg(dev, "set_pd_rx: %s\n", str_on_off(on));
return 0;
}
diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
index c37dede62e12..4fc83dcfae64 100644
--- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
+++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
@@ -13,6 +13,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/usb/tcpm.h>
#include <linux/usb/typec_mux.h>
#include <linux/workqueue.h>
@@ -562,7 +563,8 @@ done:
spin_unlock_irqrestore(&pmic_typec_port->lock, flags);
dev_dbg(dev, "set_vconn: orientation %d control 0x%08x state %s cc %s vconn %s\n",
- orientation, value, on ? "on" : "off", misc_to_vconn(misc), misc_to_cc(misc));
+ orientation, value, str_on_off(on), misc_to_vconn(misc),
+ misc_to_cc(misc));
return ret;
}
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index b2c83f552da5..19ab6647af70 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -283,7 +283,7 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc,
if (cc2 == TYPEC_CC_RD)
/* Role control would have the Rp setting when DRP was enabled */
reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RP);
- else
+ else if (cc2 >= TYPEC_CC_RP_DEF)
reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC2, TCPC_ROLE_CTRL_CC_RD);
} else {
reg &= ~TCPC_ROLE_CTRL_CC1;
@@ -291,7 +291,7 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc,
if (cc1 == TYPEC_CC_RD)
/* Role control would have the Rp setting when DRP was enabled */
reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RP);
- else
+ else if (cc1 >= TYPEC_CC_RP_DEF)
reg |= FIELD_PREP(TCPC_ROLE_CTRL_CC1, TCPC_ROLE_CTRL_CC_RD);
}
}
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
index fd1b80593367..648311f5e3cf 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
@@ -166,7 +166,8 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status)
return;
}
- if (count > sizeof(struct pd_message) || count + 1 > TCPC_RECEIVE_BUFFER_LEN) {
+ if (count > sizeof(struct pd_message) + 1 ||
+ count + 1 > TCPC_RECEIVE_BUFFER_LEN) {
dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d\n", count);
return;
}
diff --git a/drivers/usb/typec/tcpm/tcpci_mt6370.c b/drivers/usb/typec/tcpm/tcpci_mt6370.c
index 1479f961772d..ed822f438a09 100644
--- a/drivers/usb/typec/tcpm/tcpci_mt6370.c
+++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c
@@ -11,7 +11,6 @@
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/pm_wakeup.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 061c04efffa7..214d45f8e55c 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -21,6 +21,7 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/string_choices.h>
#include <linux/usb.h>
#include <linux/usb/pd.h>
#include <linux/usb/pd_ado.h>
@@ -185,7 +186,8 @@
S(UNSTRUCTURED_VDMS), \
S(STRUCTURED_VDMS), \
S(COUNTRY_INFO), \
- S(COUNTRY_CODES)
+ S(COUNTRY_CODES), \
+ S(REVISION_INFORMATION)
#define GENERATE_ENUM(e) e
#define GENERATE_STRING(s) #s
@@ -225,6 +227,7 @@ enum pd_msg_request {
PD_MSG_CTRL_NOT_SUPP,
PD_MSG_DATA_SINK_CAP,
PD_MSG_DATA_SOURCE_CAP,
+ PD_MSG_DATA_REV,
};
enum adev_actions {
@@ -310,6 +313,13 @@ struct pd_data {
unsigned int operating_snk_mw;
};
+struct pd_revision_info {
+ u8 rev_major;
+ u8 rev_minor;
+ u8 ver_major;
+ u8 ver_minor;
+};
+
/*
* @sink_wait_cap_time: Deadline (in ms) for tTypeCSinkWaitCap timer
* @ps_src_wait_off_time: Deadline (in ms) for tPSSourceOff timer
@@ -567,6 +577,9 @@ struct tcpm_port {
/* Timer deadline values configured at runtime */
struct pd_timings timings;
+
+ /* Indicates maximum (revision, version) supported */
+ struct pd_revision_info pd_rev;
#ifdef CONFIG_DEBUG_FS
struct dentry *dentry;
struct mutex logbuffer_lock; /* log buffer access lock */
@@ -583,6 +596,15 @@ struct pd_rx_event {
enum tcpm_transmit_type rx_sop_type;
};
+struct altmode_vdm_event {
+ struct kthread_work work;
+ struct tcpm_port *port;
+ u32 header;
+ u32 *data;
+ int cnt;
+ enum tcpm_transmit_type tx_sop_type;
+};
+
static const char * const pd_rev[] = {
[PD_REV10] = "rev1",
[PD_REV20] = "rev2",
@@ -880,8 +902,8 @@ static int tcpm_enable_auto_vbus_discharge(struct tcpm_port *port, bool enable)
if (port->tcpc->enable_auto_vbus_discharge) {
ret = port->tcpc->enable_auto_vbus_discharge(port->tcpc, enable);
- tcpm_log_force(port, "%s vbus discharge ret:%d", enable ? "enable" : "disable",
- ret);
+ tcpm_log_force(port, "%s vbus discharge ret:%d",
+ str_enable_disable(enable), ret);
if (!ret)
port->auto_vbus_discharge_enabled = enable;
}
@@ -1234,6 +1256,24 @@ static u32 tcpm_forge_legacy_pdo(struct tcpm_port *port, u32 pdo, enum typec_rol
}
}
+static int tcpm_pd_send_revision(struct tcpm_port *port)
+{
+ struct pd_message msg;
+ u32 rmdo;
+
+ memset(&msg, 0, sizeof(msg));
+ rmdo = RMDO(port->pd_rev.rev_major, port->pd_rev.rev_minor,
+ port->pd_rev.ver_major, port->pd_rev.ver_minor);
+ msg.payload[0] = cpu_to_le32(rmdo);
+ msg.header = PD_HEADER_LE(PD_DATA_REVISION,
+ port->pwr_role,
+ port->data_role,
+ port->negotiated_rev,
+ port->message_id,
+ 1);
+ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
+}
+
static int tcpm_pd_send_source_caps(struct tcpm_port *port)
{
struct pd_message msg;
@@ -1577,18 +1617,68 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header,
mod_vdm_delayed_work(port, 0);
}
-static void tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header,
- const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type)
+static void tcpm_queue_vdm_work(struct kthread_work *work)
{
- if (port->state != SRC_READY && port->state != SNK_READY &&
- port->state != SRC_VDM_IDENTITY_REQUEST)
- return;
+ struct altmode_vdm_event *event = container_of(work,
+ struct altmode_vdm_event,
+ work);
+ struct tcpm_port *port = event->port;
mutex_lock(&port->lock);
- tcpm_queue_vdm(port, header, data, cnt, tx_sop_type);
+ if (port->state != SRC_READY && port->state != SNK_READY &&
+ port->state != SRC_VDM_IDENTITY_REQUEST) {
+ tcpm_log_force(port, "dropping altmode_vdm_event");
+ goto port_unlock;
+ }
+
+ tcpm_queue_vdm(port, event->header, event->data, event->cnt, event->tx_sop_type);
+
+port_unlock:
+ kfree(event->data);
+ kfree(event);
mutex_unlock(&port->lock);
}
+static int tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 header,
+ const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type)
+{
+ struct altmode_vdm_event *event;
+ u32 *data_cpy;
+ int ret = -ENOMEM;
+
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
+ if (!event)
+ goto err_event;
+
+ data_cpy = kcalloc(cnt, sizeof(u32), GFP_KERNEL);
+ if (!data_cpy)
+ goto err_data;
+
+ kthread_init_work(&event->work, tcpm_queue_vdm_work);
+ event->port = port;
+ event->header = header;
+ memcpy(data_cpy, data, sizeof(u32) * cnt);
+ event->data = data_cpy;
+ event->cnt = cnt;
+ event->tx_sop_type = tx_sop_type;
+
+ ret = kthread_queue_work(port->wq, &event->work);
+ if (!ret) {
+ ret = -EBUSY;
+ goto err_queue;
+ }
+
+ return 0;
+
+err_queue:
+ kfree(data_cpy);
+err_data:
+ kfree(event);
+err_event:
+ tcpm_log_force(port, "failed to queue altmode vdm, err:%d", ret);
+ return ret;
+}
+
static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt)
{
u32 vdo = p[VDO_INDEX_IDH];
@@ -2799,8 +2889,7 @@ static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo)
header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE);
header |= VDO_OPOS(altmode->mode);
- tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP);
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP);
}
static int tcpm_altmode_exit(struct typec_altmode *altmode)
@@ -2816,8 +2905,7 @@ static int tcpm_altmode_exit(struct typec_altmode *altmode)
header = VDO(altmode->svid, 1, svdm_version, CMD_EXIT_MODE);
header |= VDO_OPOS(altmode->mode);
- tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP);
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP);
}
static int tcpm_altmode_vdm(struct typec_altmode *altmode,
@@ -2825,9 +2913,7 @@ static int tcpm_altmode_vdm(struct typec_altmode *altmode,
{
struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
- tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP);
-
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP);
}
static const struct typec_altmode_ops tcpm_altmode_ops = {
@@ -2851,8 +2937,7 @@ static int tcpm_cable_altmode_enter(struct typec_altmode *altmode, enum typec_pl
header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE);
header |= VDO_OPOS(altmode->mode);
- tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME);
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, TCPC_TX_SOP_PRIME);
}
static int tcpm_cable_altmode_exit(struct typec_altmode *altmode, enum typec_plug_index sop)
@@ -2868,8 +2953,7 @@ static int tcpm_cable_altmode_exit(struct typec_altmode *altmode, enum typec_plu
header = VDO(altmode->svid, 1, svdm_version, CMD_EXIT_MODE);
header |= VDO_OPOS(altmode->mode);
- tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP_PRIME);
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, NULL, 0, TCPC_TX_SOP_PRIME);
}
static int tcpm_cable_altmode_vdm(struct typec_altmode *altmode, enum typec_plug_index sop,
@@ -2877,9 +2961,7 @@ static int tcpm_cable_altmode_vdm(struct typec_altmode *altmode, enum typec_plug
{
struct tcpm_port *port = typec_altmode_get_drvdata(altmode);
- tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP_PRIME);
-
- return 0;
+ return tcpm_queue_vdm_unlocked(port, header, data, count - 1, TCPC_TX_SOP_PRIME);
}
static const struct typec_cable_ops tcpm_cable_ops = {
@@ -3539,6 +3621,17 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
PD_MSG_CTRL_NOT_SUPP,
NONE_AMS);
break;
+ case PD_CTRL_GET_REVISION:
+ if (port->negotiated_rev >= PD_REV30 && port->pd_rev.rev_major)
+ tcpm_pd_handle_msg(port, PD_MSG_DATA_REV,
+ REVISION_INFORMATION);
+ else
+ tcpm_pd_handle_msg(port,
+ port->negotiated_rev < PD_REV30 ?
+ PD_MSG_CTRL_REJECT :
+ PD_MSG_CTRL_NOT_SUPP,
+ NONE_AMS);
+ break;
default:
tcpm_pd_handle_msg(port,
port->negotiated_rev < PD_REV30 ?
@@ -3783,6 +3876,14 @@ static bool tcpm_send_queued_message(struct tcpm_port *port)
tcpm_ams_finish(port);
}
break;
+ case PD_MSG_DATA_REV:
+ ret = tcpm_pd_send_revision(port);
+ if (ret)
+ tcpm_log(port,
+ "Unable to send revision msg, ret=%d",
+ ret);
+ tcpm_ams_finish(port);
+ break;
default:
break;
}
@@ -4392,7 +4493,7 @@ static void tcpm_unregister_altmodes(struct tcpm_port *port)
static void tcpm_set_partner_usb_comm_capable(struct tcpm_port *port, bool capable)
{
- tcpm_log(port, "Setting usb_comm capable %s", capable ? "true" : "false");
+ tcpm_log(port, "Setting usb_comm capable %s", str_true_false(capable));
if (port->tcpc->set_partner_usb_comm_capable)
port->tcpc->set_partner_usb_comm_capable(port->tcpc, capable);
@@ -5067,16 +5168,16 @@ static void run_state_machine(struct tcpm_port *port)
*/
if (port->vbus_never_low) {
port->vbus_never_low = false;
- tcpm_set_state(port, SNK_SOFT_RESET,
- port->timings.sink_wait_cap_time);
+ upcoming_state = SNK_SOFT_RESET;
} else {
if (!port->self_powered)
upcoming_state = SNK_WAIT_CAPABILITIES_TIMEOUT;
else
upcoming_state = hard_reset_state(port);
- tcpm_set_state(port, SNK_WAIT_CAPABILITIES_TIMEOUT,
- port->timings.sink_wait_cap_time);
}
+
+ tcpm_set_state(port, upcoming_state,
+ port->timings.sink_wait_cap_time);
break;
case SNK_WAIT_CAPABILITIES_TIMEOUT:
/*
@@ -5915,7 +6016,7 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
case SNK_TRY_WAIT_DEBOUNCE:
if (!tcpm_port_is_sink(port)) {
port->max_wait = 0;
- tcpm_set_state(port, SRC_TRYWAIT, 0);
+ tcpm_set_state(port, SRC_TRYWAIT, PD_T_PD_DEBOUNCE);
}
break;
case SRC_TRY_WAIT:
@@ -7037,7 +7138,9 @@ static void tcpm_port_unregister_pd(struct tcpm_port *port)
static int tcpm_port_register_pd(struct tcpm_port *port)
{
- struct usb_power_delivery_desc desc = { port->typec_caps.pd_revision };
+ u16 pd_revision = port->typec_caps.pd_revision;
+ u16 pd_version = port->pd_rev.ver_major << 8 | port->pd_rev.ver_minor;
+ struct usb_power_delivery_desc desc = { pd_revision, pd_version };
struct usb_power_delivery_capabilities *cap;
int ret, i;
@@ -7332,6 +7435,29 @@ static int tcpm_fw_get_snk_vdos(struct tcpm_port *port, struct fwnode_handle *fw
return 0;
}
+static void tcpm_fw_get_pd_revision(struct tcpm_port *port, struct fwnode_handle *fwnode)
+{
+ int ret;
+ u8 val[4];
+
+ ret = fwnode_property_count_u8(fwnode, "pd-revision");
+ if (!ret || ret != 4) {
+ tcpm_log(port, "Unable to find pd-revision property or incorrect array size");
+ return;
+ }
+
+ ret = fwnode_property_read_u8_array(fwnode, "pd-revision", val, 4);
+ if (ret) {
+ tcpm_log(port, "Failed to parse pd-revision, ret:(%d)", ret);
+ return;
+ }
+
+ port->pd_rev.rev_major = val[0];
+ port->pd_rev.rev_minor = val[1];
+ port->pd_rev.ver_major = val[2];
+ port->pd_rev.ver_minor = val[3];
+}
+
/* Power Supply access to expose source power information */
enum tcpm_psy_online_states {
TCPM_PSY_OFFLINE = 0,
@@ -7636,7 +7762,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
mutex_init(&port->lock);
mutex_init(&port->swap_lock);
- port->wq = kthread_create_worker(0, dev_name(dev));
+ port->wq = kthread_run_worker(0, dev_name(dev));
if (IS_ERR(port->wq))
return ERR_CAST(port->wq);
sched_set_fifo(port->wq->task);
@@ -7646,14 +7772,14 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
kthread_init_work(&port->event_work, tcpm_pd_event_handler);
kthread_init_work(&port->enable_frs, tcpm_enable_frs_work);
kthread_init_work(&port->send_discover_work, tcpm_send_discover_work);
- hrtimer_init(&port->state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- port->state_machine_timer.function = state_machine_timer_handler;
- hrtimer_init(&port->vdm_state_machine_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- port->vdm_state_machine_timer.function = vdm_state_machine_timer_handler;
- hrtimer_init(&port->enable_frs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- port->enable_frs_timer.function = enable_frs_timer_handler;
- hrtimer_init(&port->send_discover_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- port->send_discover_timer.function = send_discover_timer_handler;
+ hrtimer_setup(&port->state_machine_timer, state_machine_timer_handler, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ hrtimer_setup(&port->vdm_state_machine_timer, vdm_state_machine_timer_handler,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_setup(&port->enable_frs_timer, enable_frs_timer_handler, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ hrtimer_setup(&port->send_discover_timer, send_discover_timer_handler, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
spin_lock_init(&port->pd_event_lock);
@@ -7670,11 +7796,18 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
goto out_destroy_wq;
tcpm_fw_get_timings(port, tcpc->fwnode);
+ tcpm_fw_get_pd_revision(port, tcpc->fwnode);
port->try_role = port->typec_caps.prefer_role;
port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */
- port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */
+
+ if (port->pd_rev.rev_major)
+ port->typec_caps.pd_revision = port->pd_rev.rev_major << 8 |
+ port->pd_rev.rev_minor;
+ else
+ port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */
+
port->typec_caps.svdm_version = SVDM_VER_2_0;
port->typec_caps.driver_data = port;
port->typec_caps.ops = &tcpm_ops;
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index 680e1b87b152..75559601fe8f 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -69,6 +69,19 @@ config UCSI_PMIC_GLINK
To compile the driver as a module, choose M here: the module will be
called ucsi_glink.
+config CROS_EC_UCSI
+ tristate "UCSI Driver for ChromeOS EC"
+ depends on MFD_CROS_EC_DEV
+ depends on CROS_USBPD_NOTIFY
+ depends on !EXTCON_TCSS_CROS_EC
+ default MFD_CROS_EC_DEV
+ help
+ This driver enables UCSI support for a ChromeOS EC. The EC is
+ expected to implement a PPM.
+
+ To compile the driver as a module, choose M here: the module
+ will be called cros_ec_ucsi.
+
config UCSI_LENOVO_YOGA_C630
tristate "UCSI Interface Driver for Lenovo Yoga C630"
depends on EC_LENOVO_YOGA_C630
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index aed41d23887b..be98a879104d 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -21,4 +21,5 @@ obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o
obj-$(CONFIG_UCSI_PMIC_GLINK) += ucsi_glink.o
+obj-$(CONFIG_CROS_EC_UCSI) += cros_ec_ucsi.o
obj-$(CONFIG_UCSI_LENOVO_YOGA_C630) += ucsi_yoga_c630.o
diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c
new file mode 100644
index 000000000000..4ec1c6d22310
--- /dev/null
+++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UCSI driver for ChromeOS EC
+ *
+ * Copyright 2024 Google LLC.
+ */
+
+#include <linux/container_of.h>
+#include <linux/dev_printk.h>
+#include <linux/jiffies.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_usbpd_notify.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "ucsi.h"
+
+/*
+ * Maximum size in bytes of a UCSI message between AP and EC
+ */
+#define MAX_EC_DATA_SIZE 256
+
+/*
+ * Maximum time in milliseconds the cros_ec_ucsi driver
+ * will wait for a response to a command or and ack.
+ */
+#define WRITE_TMO_MS 5000
+
+/* Number of times to attempt recovery from a write timeout before giving up. */
+#define WRITE_TMO_CTR_MAX 5
+
+struct cros_ucsi_data {
+ struct device *dev;
+ struct ucsi *ucsi;
+
+ struct cros_ec_device *ec;
+ struct notifier_block nb;
+ struct work_struct work;
+ struct delayed_work write_tmo;
+ int tmo_counter;
+
+ struct completion complete;
+ unsigned long flags;
+};
+
+static int cros_ucsi_read(struct ucsi *ucsi, unsigned int offset, void *val,
+ size_t val_len)
+{
+ struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi);
+ struct ec_params_ucsi_ppm_get req = {
+ .offset = offset,
+ .size = val_len,
+ };
+ int ret;
+
+ if (val_len > MAX_EC_DATA_SIZE) {
+ dev_err(udata->dev, "Can't read %zu bytes. Too big.\n", val_len);
+ return -EINVAL;
+ }
+
+ ret = cros_ec_cmd(udata->ec, 0, EC_CMD_UCSI_PPM_GET,
+ &req, sizeof(req), val, val_len);
+ if (ret < 0) {
+ dev_warn(udata->dev, "Failed to send EC message UCSI_PPM_GET: error=%d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int cros_ucsi_read_version(struct ucsi *ucsi, u16 *version)
+{
+ return cros_ucsi_read(ucsi, UCSI_VERSION, version, sizeof(*version));
+}
+
+static int cros_ucsi_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ return cros_ucsi_read(ucsi, UCSI_CCI, cci, sizeof(*cci));
+}
+
+static int cros_ucsi_read_message_in(struct ucsi *ucsi, void *val,
+ size_t val_len)
+{
+ return cros_ucsi_read(ucsi, UCSI_MESSAGE_IN, val, val_len);
+}
+
+static int cros_ucsi_async_control(struct ucsi *ucsi, u64 cmd)
+{
+ struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi);
+ u8 ec_buf[sizeof(struct ec_params_ucsi_ppm_set) + sizeof(cmd)];
+ struct ec_params_ucsi_ppm_set *req = (struct ec_params_ucsi_ppm_set *) ec_buf;
+ int ret;
+
+ req->offset = UCSI_CONTROL;
+ memcpy(req->data, &cmd, sizeof(cmd));
+ ret = cros_ec_cmd(udata->ec, 0, EC_CMD_UCSI_PPM_SET,
+ req, sizeof(ec_buf), NULL, 0);
+ if (ret < 0) {
+ dev_warn(udata->dev, "Failed to send EC message UCSI_PPM_SET: error=%d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci,
+ void *data, size_t size)
+{
+ struct cros_ucsi_data *udata = ucsi_get_drvdata(ucsi);
+ int ret;
+
+ ret = ucsi_sync_control_common(ucsi, cmd, cci, data, size);
+ switch (ret) {
+ case -EBUSY:
+ /* EC may return -EBUSY if CCI.busy is set.
+ * Convert this to a timeout.
+ */
+ case -ETIMEDOUT:
+ /* Schedule recovery attempt when we timeout
+ * or tried to send a command while still busy.
+ */
+ cancel_delayed_work_sync(&udata->write_tmo);
+ schedule_delayed_work(&udata->write_tmo,
+ msecs_to_jiffies(WRITE_TMO_MS));
+ break;
+ case 0:
+ /* Successful write. Cancel any pending recovery work. */
+ cancel_delayed_work_sync(&udata->write_tmo);
+ break;
+ }
+
+ return ret;
+}
+
+static const struct ucsi_operations cros_ucsi_ops = {
+ .read_version = cros_ucsi_read_version,
+ .read_cci = cros_ucsi_read_cci,
+ .read_message_in = cros_ucsi_read_message_in,
+ .async_control = cros_ucsi_async_control,
+ .sync_control = cros_ucsi_sync_control,
+};
+
+static void cros_ucsi_work(struct work_struct *work)
+{
+ struct cros_ucsi_data *udata = container_of(work, struct cros_ucsi_data, work);
+ u32 cci;
+
+ if (cros_ucsi_read_cci(udata->ucsi, &cci))
+ return;
+
+ ucsi_notify_common(udata->ucsi, cci);
+}
+
+static void cros_ucsi_write_timeout(struct work_struct *work)
+{
+ struct cros_ucsi_data *udata =
+ container_of(work, struct cros_ucsi_data, write_tmo.work);
+ u32 cci;
+ u64 cmd;
+
+ if (cros_ucsi_read(udata->ucsi, UCSI_CCI, &cci, sizeof(cci))) {
+ dev_err(udata->dev,
+ "Reading CCI failed; no write timeout recovery possible.\n");
+ return;
+ }
+
+ if (cci & UCSI_CCI_BUSY) {
+ udata->tmo_counter++;
+
+ if (udata->tmo_counter <= WRITE_TMO_CTR_MAX)
+ schedule_delayed_work(&udata->write_tmo,
+ msecs_to_jiffies(WRITE_TMO_MS));
+ else
+ dev_err(udata->dev,
+ "PPM unresponsive - too many write timeouts.\n");
+
+ return;
+ }
+
+ /* No longer busy means we can reset our timeout counter. */
+ udata->tmo_counter = 0;
+
+ /* Need to ack previous command which may have timed out. */
+ if (cci & UCSI_CCI_COMMAND_COMPLETE) {
+ cmd = UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE;
+ cros_ucsi_async_control(udata->ucsi, cmd);
+
+ /* Check again after a few seconds that the system has
+ * recovered to make sure our async write above was successful.
+ */
+ schedule_delayed_work(&udata->write_tmo,
+ msecs_to_jiffies(WRITE_TMO_MS));
+ return;
+ }
+
+ /* We recovered from a previous timeout. Treat this as a recovery from
+ * suspend and call resume.
+ */
+ ucsi_resume(udata->ucsi);
+}
+
+static int cros_ucsi_event(struct notifier_block *nb,
+ unsigned long host_event, void *_notify)
+{
+ struct cros_ucsi_data *udata = container_of(nb, struct cros_ucsi_data, nb);
+
+ if (host_event & PD_EVENT_INIT) {
+ /* Late init event received from ChromeOS EC. Treat this as a
+ * system resume to re-enable communication with the PPM.
+ */
+ dev_dbg(udata->dev, "Late PD init received\n");
+ ucsi_resume(udata->ucsi);
+ }
+
+ if (host_event & PD_EVENT_PPM) {
+ dev_dbg(udata->dev, "UCSI notification received\n");
+ flush_work(&udata->work);
+ schedule_work(&udata->work);
+ }
+
+ return NOTIFY_OK;
+}
+
+static void cros_ucsi_destroy(struct cros_ucsi_data *udata)
+{
+ cros_usbpd_unregister_notify(&udata->nb);
+ cancel_delayed_work_sync(&udata->write_tmo);
+ cancel_work_sync(&udata->work);
+ ucsi_destroy(udata->ucsi);
+}
+
+static int cros_ucsi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cros_ec_dev *ec_data = dev_get_drvdata(dev->parent);
+ struct cros_ucsi_data *udata;
+ int ret;
+
+ udata = devm_kzalloc(dev, sizeof(*udata), GFP_KERNEL);
+ if (!udata)
+ return -ENOMEM;
+
+ udata->dev = dev;
+
+ udata->ec = ec_data->ec_dev;
+ if (!udata->ec)
+ return dev_err_probe(dev, -ENODEV, "couldn't find parent EC device\n");
+
+ platform_set_drvdata(pdev, udata);
+
+ INIT_WORK(&udata->work, cros_ucsi_work);
+ INIT_DELAYED_WORK(&udata->write_tmo, cros_ucsi_write_timeout);
+ init_completion(&udata->complete);
+
+ udata->ucsi = ucsi_create(dev, &cros_ucsi_ops);
+ if (IS_ERR(udata->ucsi))
+ return dev_err_probe(dev, PTR_ERR(udata->ucsi), "failed to allocate UCSI instance\n");
+
+ ucsi_set_drvdata(udata->ucsi, udata);
+
+ udata->nb.notifier_call = cros_ucsi_event;
+ ret = cros_usbpd_register_notify(&udata->nb);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to register notifier\n");
+ ucsi_destroy(udata->ucsi);
+ return ret;
+ }
+
+ ret = ucsi_register(udata->ucsi);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to register UCSI\n");
+ cros_ucsi_destroy(udata);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cros_ucsi_remove(struct platform_device *dev)
+{
+ struct cros_ucsi_data *udata = platform_get_drvdata(dev);
+
+ ucsi_unregister(udata->ucsi);
+ cros_ucsi_destroy(udata);
+}
+
+static int __maybe_unused cros_ucsi_suspend(struct device *dev)
+{
+ struct cros_ucsi_data *udata = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&udata->write_tmo);
+ cancel_work_sync(&udata->work);
+
+ return 0;
+}
+
+static void __maybe_unused cros_ucsi_complete(struct device *dev)
+{
+ struct cros_ucsi_data *udata = dev_get_drvdata(dev);
+
+ ucsi_resume(udata->ucsi);
+}
+
+/*
+ * UCSI protocol is also used on ChromeOS platforms which reply on
+ * cros_ec_lpc.c driver for communication with embedded controller (EC).
+ * On such platforms communication with the EC is not available until
+ * the .complete() callback of the cros_ec_lpc driver is executed.
+ * For this reason we delay ucsi_resume() until the .complete() stage
+ * otherwise UCSI SET_NOTIFICATION_ENABLE command will fail and we won't
+ * receive any UCSI notifications from the EC where PPM is implemented.
+ */
+static const struct dev_pm_ops cros_ucsi_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = cros_ucsi_suspend,
+ .complete = cros_ucsi_complete,
+#endif
+};
+
+static const struct platform_device_id cros_ucsi_id[] = {
+ { KBUILD_MODNAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cros_ucsi_id);
+
+static struct platform_driver cros_ucsi_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .pm = &cros_ucsi_pm_ops,
+ },
+ .id_table = cros_ucsi_id,
+ .probe = cros_ucsi_probe,
+ .remove = cros_ucsi_remove,
+};
+
+module_platform_driver(cros_ucsi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("UCSI driver for ChromeOS EC");
diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c
index 83ff23086d79..eae2b18a2d8a 100644
--- a/drivers/usb/typec/ucsi/debugfs.c
+++ b/drivers/usb/typec/ucsi/debugfs.c
@@ -28,11 +28,12 @@ static int ucsi_cmd(void *data, u64 val)
ucsi->debugfs->status = 0;
switch (UCSI_COMMAND(val)) {
- case UCSI_SET_UOM:
+ case UCSI_SET_CCOM:
case UCSI_SET_UOR:
case UCSI_SET_PDR:
case UCSI_CONNECTOR_RESET:
case UCSI_SET_SINK_PATH:
+ case UCSI_SET_NEW_CAM:
ret = ucsi_send_command(ucsi, val, NULL, 0);
break;
case UCSI_GET_CAPABILITY:
@@ -42,6 +43,9 @@ static int ucsi_cmd(void *data, u64 val)
case UCSI_GET_PDOS:
case UCSI_GET_CABLE_PROPERTY:
case UCSI_GET_CONNECTOR_STATUS:
+ case UCSI_GET_ERROR_STATUS:
+ case UCSI_GET_CAM_CS:
+ case UCSI_GET_LPM_PPM_INFO:
ret = ucsi_send_command(ucsi, val,
&ucsi->debugfs->response,
sizeof(ucsi->debugfs->response));
diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c
index 420af5139c70..8aae80b457d7 100644
--- a/drivers/usb/typec/ucsi/displayport.c
+++ b/drivers/usb/typec/ucsi/displayport.c
@@ -54,7 +54,8 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
u8 cur = 0;
int ret;
- mutex_lock(&dp->con->lock);
+ if (!ucsi_con_mutex_lock(dp->con))
+ return -ENOTCONN;
if (!dp->override && dp->initialized) {
const struct typec_altmode *p = typec_altmode_get_partner(alt);
@@ -100,7 +101,7 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
schedule_work(&dp->work);
ret = 0;
err_unlock:
- mutex_unlock(&dp->con->lock);
+ ucsi_con_mutex_unlock(dp->con);
return ret;
}
@@ -112,7 +113,8 @@ static int ucsi_displayport_exit(struct typec_altmode *alt)
u64 command;
int ret = 0;
- mutex_lock(&dp->con->lock);
+ if (!ucsi_con_mutex_lock(dp->con))
+ return -ENOTCONN;
if (!dp->override) {
const struct typec_altmode *p = typec_altmode_get_partner(alt);
@@ -144,7 +146,7 @@ static int ucsi_displayport_exit(struct typec_altmode *alt)
schedule_work(&dp->work);
out_unlock:
- mutex_unlock(&dp->con->lock);
+ ucsi_con_mutex_unlock(dp->con);
return ret;
}
@@ -202,20 +204,21 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt,
int cmd = PD_VDO_CMD(header);
int svdm_version;
- mutex_lock(&dp->con->lock);
+ if (!ucsi_con_mutex_lock(dp->con))
+ return -ENOTCONN;
if (!dp->override && dp->initialized) {
const struct typec_altmode *p = typec_altmode_get_partner(alt);
dev_warn(&p->dev,
"firmware doesn't support alternate mode overriding\n");
- mutex_unlock(&dp->con->lock);
+ ucsi_con_mutex_unlock(dp->con);
return -EOPNOTSUPP;
}
svdm_version = typec_altmode_get_svdm_version(alt);
if (svdm_version < 0) {
- mutex_unlock(&dp->con->lock);
+ ucsi_con_mutex_unlock(dp->con);
return svdm_version;
}
@@ -259,7 +262,7 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt,
break;
}
- mutex_unlock(&dp->con->lock);
+ ucsi_con_mutex_unlock(dp->con);
return 0;
}
@@ -296,6 +299,8 @@ void ucsi_displayport_remove_partner(struct typec_altmode *alt)
if (!dp)
return;
+ cancel_work_sync(&dp->work);
+
dp->data.conf = 0;
dp->data.status = 0;
dp->initialized = false;
diff --git a/drivers/usb/typec/ucsi/trace.c b/drivers/usb/typec/ucsi/trace.c
index cb62ad835761..596a9542d401 100644
--- a/drivers/usb/typec/ucsi/trace.c
+++ b/drivers/usb/typec/ucsi/trace.c
@@ -12,7 +12,7 @@ static const char * const ucsi_cmd_strs[] = {
[UCSI_SET_NOTIFICATION_ENABLE] = "SET_NOTIFICATION_ENABLE",
[UCSI_GET_CAPABILITY] = "GET_CAPABILITY",
[UCSI_GET_CONNECTOR_CAPABILITY] = "GET_CONNECTOR_CAPABILITY",
- [UCSI_SET_UOM] = "SET_UOM",
+ [UCSI_SET_CCOM] = "SET_CCOM",
[UCSI_SET_UOR] = "SET_UOR",
[UCSI_SET_PDM] = "SET_PDM",
[UCSI_SET_PDR] = "SET_PDR",
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 2a2915b0a645..01ce858a1a2b 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -55,7 +55,8 @@ void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
}
EXPORT_SYMBOL_GPL(ucsi_notify_common);
-int ucsi_sync_control_common(struct ucsi *ucsi, u64 command)
+int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *data, size_t size)
{
bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI;
int ret;
@@ -80,6 +81,13 @@ out_clear_bit:
else
clear_bit(COMMAND_PENDING, &ucsi->flags);
+ if (!ret && cci)
+ ret = ucsi->ops->read_cci(ucsi, cci);
+
+ if (!ret && data &&
+ (*cci & UCSI_CCI_COMMAND_COMPLETE))
+ ret = ucsi->ops->read_message_in(ucsi, data, size);
+
return ret;
}
EXPORT_SYMBOL_GPL(ucsi_sync_control_common);
@@ -95,7 +103,7 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack)
ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
}
- return ucsi->ops->sync_control(ucsi, ctrl);
+ return ucsi->ops->sync_control(ucsi, ctrl, NULL, NULL, 0);
}
static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
@@ -108,9 +116,7 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
if (size > UCSI_MAX_DATA_LENGTH(ucsi))
return -EINVAL;
- ret = ucsi->ops->sync_control(ucsi, command);
- if (ucsi->ops->read_cci(ucsi, cci))
- return -EIO;
+ ret = ucsi->ops->sync_control(ucsi, command, cci, data, size);
if (*cci & UCSI_CCI_BUSY)
return ucsi_run_command(ucsi, UCSI_CANCEL, cci, NULL, 0, false) ?: -EBUSY;
@@ -127,9 +133,6 @@ static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
else
err = 0;
- if (!err && data && UCSI_CCI_LENGTH(*cci))
- err = ucsi->ops->read_message_in(ucsi, data, size);
-
/*
* Don't ACK connection change if there was an error.
*/
@@ -1920,6 +1923,40 @@ void ucsi_set_drvdata(struct ucsi *ucsi, void *data)
EXPORT_SYMBOL_GPL(ucsi_set_drvdata);
/**
+ * ucsi_con_mutex_lock - Acquire the connector mutex
+ * @con: The connector interface to lock
+ *
+ * Returns true on success, false if the connector is disconnected
+ */
+bool ucsi_con_mutex_lock(struct ucsi_connector *con)
+{
+ bool mutex_locked = false;
+ bool connected = true;
+
+ while (connected && !mutex_locked) {
+ mutex_locked = mutex_trylock(&con->lock) != 0;
+ connected = UCSI_CONSTAT(con, CONNECTED);
+ if (connected && !mutex_locked)
+ msleep(20);
+ }
+
+ connected = connected && con->partner;
+ if (!connected && mutex_locked)
+ mutex_unlock(&con->lock);
+
+ return connected;
+}
+
+/**
+ * ucsi_con_mutex_unlock - Release the connector mutex
+ * @con: The connector interface to unlock
+ */
+void ucsi_con_mutex_unlock(struct ucsi_connector *con)
+{
+ mutex_unlock(&con->lock);
+}
+
+/**
* ucsi_create - Allocate UCSI instance
* @dev: Device interface to the PPM (Platform Policy Manager)
* @ops: I/O routines
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index e4c563da715f..70910232a05d 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -30,6 +30,7 @@ struct dentry;
#define UCSIv2_MESSAGE_OUT 272
/* UCSI versions */
+#define UCSI_VERSION_1_0 0x0100
#define UCSI_VERSION_1_1 0x0110
#define UCSI_VERSION_1_2 0x0120
#define UCSI_VERSION_2_0 0x0200
@@ -78,7 +79,8 @@ struct ucsi_operations {
int (*read_cci)(struct ucsi *ucsi, u32 *cci);
int (*poll_cci)(struct ucsi *ucsi, u32 *cci);
int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len);
- int (*sync_control)(struct ucsi *ucsi, u64 command);
+ int (*sync_control)(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *data, size_t size);
int (*async_control)(struct ucsi *ucsi, u64 command);
bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig,
struct ucsi_altmode *updated);
@@ -92,6 +94,8 @@ int ucsi_register(struct ucsi *ucsi);
void ucsi_unregister(struct ucsi *ucsi);
void *ucsi_get_drvdata(struct ucsi *ucsi);
void ucsi_set_drvdata(struct ucsi *ucsi, void *data);
+bool ucsi_con_mutex_lock(struct ucsi_connector *con);
+void ucsi_con_mutex_unlock(struct ucsi_connector *con);
void ucsi_connector_change(struct ucsi *ucsi, u8 num);
@@ -107,7 +111,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_GET_CAPABILITY_SIZE 128
#define UCSI_GET_CONNECTOR_CAPABILITY 0x07
#define UCSI_GET_CONNECTOR_CAPABILITY_SIZE 32
-#define UCSI_SET_UOM 0x08
+#define UCSI_SET_CCOM 0x08
#define UCSI_SET_UOR 0x09
#define UCSI_SET_PDM 0x0a
#define UCSI_SET_PDR 0x0b
@@ -122,7 +126,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_GET_CONNECTOR_STATUS_SIZE 152
#define UCSI_GET_ERROR_STATUS 0x13
#define UCSI_GET_PD_MESSAGE 0x15
+#define UCSI_GET_CAM_CS 0x18
#define UCSI_SET_SINK_PATH 0x1c
+#define UCSI_GET_LPM_PPM_INFO 0x22
#define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16)
#define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff)
@@ -428,7 +434,7 @@ struct ucsi_debugfs_entry {
u64 low;
u64 high;
} response;
- u32 status;
+ int status;
struct dentry *dentry;
};
@@ -530,7 +536,8 @@ void ucsi_altmode_update_active(struct ucsi_connector *con);
int ucsi_resume(struct ucsi *ucsi);
void ucsi_notify_common(struct ucsi *ucsi, u32 cci);
-int ucsi_sync_control_common(struct ucsi *ucsi, u64 command);
+int ucsi_sync_control_common(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *data, size_t size);
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
int ucsi_register_port_psy(struct ucsi_connector *con);
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index ac1ebb5d9527..6b92f296e985 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -105,17 +105,23 @@ static const struct ucsi_operations ucsi_acpi_ops = {
.async_control = ucsi_acpi_async_control
};
-static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
+static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *val, size_t len)
{
u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE |
UCSI_CONSTAT_PDOS_CHANGE;
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
- ret = ucsi_acpi_read_message_in(ucsi, val, val_len);
+ ret = ucsi_sync_control_common(ucsi, command, cci, val, len);
if (ret < 0)
return ret;
+ if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
+ ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
+ ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
+ ua->check_bogus_event = true;
+
if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS &&
ua->check_bogus_event) {
/* Clear the bogus change */
@@ -128,28 +134,11 @@ static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_le
return ret;
}
-static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
-{
- struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
- int ret;
-
- ret = ucsi_sync_control_common(ucsi, command);
- if (ret < 0)
- return ret;
-
- if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
- ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
- ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
- ua->check_bogus_event = true;
-
- return ret;
-}
-
static const struct ucsi_operations ucsi_gram_ops = {
.read_version = ucsi_acpi_read_version,
.read_cci = ucsi_acpi_read_cci,
.poll_cci = ucsi_acpi_poll_cci,
- .read_message_in = ucsi_gram_read_message_in,
+ .read_message_in = ucsi_acpi_read_message_in,
.sync_control = ucsi_gram_sync_control,
.async_control = ucsi_acpi_async_control
};
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index 4b1668733a4b..f01e4ef6619d 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -222,7 +222,6 @@ struct ucsi_ccg {
u16 fw_build;
struct work_struct pm_work;
- u64 last_cmd_sent;
bool has_multiple_dp;
struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];
@@ -538,9 +537,10 @@ static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc,
* first and then vdo=0x3
*/
static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc,
- struct ucsi_altmode *alt)
+ struct ucsi_altmode *alt,
+ u64 command)
{
- switch (UCSI_ALTMODE_OFFSET(uc->last_cmd_sent)) {
+ switch (UCSI_ALTMODE_OFFSET(command)) {
case NVIDIA_FTB_DP_OFFSET:
if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DBG_VDO)
alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DP_VDO |
@@ -578,37 +578,11 @@ static int ucsi_ccg_read_cci(struct ucsi *ucsi, u32 *cci)
static int ucsi_ccg_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
- struct ucsi_capability *cap;
- struct ucsi_altmode *alt;
spin_lock(&uc->op_lock);
memcpy(val, uc->op_data.message_in, val_len);
spin_unlock(&uc->op_lock);
- switch (UCSI_COMMAND(uc->last_cmd_sent)) {
- case UCSI_GET_CURRENT_CAM:
- if (uc->has_multiple_dp)
- ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)val);
- break;
- case UCSI_GET_ALTERNATE_MODES:
- if (UCSI_ALTMODE_RECIPIENT(uc->last_cmd_sent) ==
- UCSI_RECIPIENT_SOP) {
- alt = val;
- if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID)
- ucsi_ccg_nvidia_altmode(uc, alt);
- }
- break;
- case UCSI_GET_CAPABILITY:
- if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) {
- cap = val;
- cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS;
- }
- break;
- default:
- break;
- }
- uc->last_cmd_sent = 0;
-
return 0;
}
@@ -628,7 +602,8 @@ static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command)
return ccg_write(uc, reg, (u8 *)&command, sizeof(command));
}
-static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
+static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *data, size_t size)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
struct ucsi_connector *con;
@@ -638,11 +613,9 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
mutex_lock(&uc->lock);
pm_runtime_get_sync(uc->dev);
- uc->last_cmd_sent = command;
-
- if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM &&
+ if (UCSI_COMMAND(command) == UCSI_SET_NEW_CAM &&
uc->has_multiple_dp) {
- con_index = (uc->last_cmd_sent >> 16) &
+ con_index = (command >> 16) &
UCSI_CMD_CONNECTOR_MASK;
if (con_index == 0) {
ret = -EINVAL;
@@ -652,7 +625,31 @@ static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
ucsi_ccg_update_set_new_cam_cmd(uc, con, &command);
}
- ret = ucsi_sync_control_common(ucsi, command);
+ ret = ucsi_sync_control_common(ucsi, command, cci, data, size);
+
+ switch (UCSI_COMMAND(command)) {
+ case UCSI_GET_CURRENT_CAM:
+ if (uc->has_multiple_dp)
+ ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)data);
+ break;
+ case UCSI_GET_ALTERNATE_MODES:
+ if (UCSI_ALTMODE_RECIPIENT(command) == UCSI_RECIPIENT_SOP) {
+ struct ucsi_altmode *alt = data;
+
+ if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID)
+ ucsi_ccg_nvidia_altmode(uc, alt, command);
+ }
+ break;
+ case UCSI_GET_CAPABILITY:
+ if (uc->fw_build == CCG_FW_BUILD_NVIDIA_TEGRA) {
+ struct ucsi_capability *cap = data;
+
+ cap->features &= ~UCSI_CAP_ALT_MODE_DETAILS;
+ }
+ break;
+ default:
+ break;
+ }
err_put:
pm_runtime_put_sync(uc->dev);
@@ -1391,22 +1388,35 @@ static ssize_t do_flash_store(struct device *dev,
if (!flash)
return n;
- if (uc->fw_build == 0x0) {
- dev_err(dev, "fail to flash FW due to missing FW build info\n");
- return -EINVAL;
- }
-
schedule_work(&uc->work);
return n;
}
+static umode_t ucsi_ccg_attrs_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct ucsi_ccg *uc = i2c_get_clientdata(to_i2c_client(dev));
+
+ if (!uc->fw_build)
+ return 0;
+
+ return attr->mode;
+}
+
static DEVICE_ATTR_WO(do_flash);
static struct attribute *ucsi_ccg_attrs[] = {
&dev_attr_do_flash.attr,
NULL,
};
-ATTRIBUTE_GROUPS(ucsi_ccg);
+static struct attribute_group ucsi_ccg_attr_group = {
+ .attrs = ucsi_ccg_attrs,
+ .is_visible = ucsi_ccg_attrs_is_visible,
+};
+static const struct attribute_group *ucsi_ccg_groups[] = {
+ &ucsi_ccg_attr_group,
+ NULL,
+};
static int ucsi_ccg_probe(struct i2c_client *client)
{
@@ -1433,11 +1443,10 @@ static int ucsi_ccg_probe(struct i2c_client *client)
uc->fw_build = CCG_FW_BUILD_NVIDIA_TEGRA;
else if (!strcmp(fw_name, "nvidia,gpu"))
uc->fw_build = CCG_FW_BUILD_NVIDIA;
+ if (!uc->fw_build)
+ dev_err(uc->dev, "failed to get FW build information\n");
}
- if (!uc->fw_build)
- dev_err(uc->dev, "failed to get FW build information\n");
-
/* reset ccg device and initialize ucsi */
status = ucsi_ccg_init(uc);
if (status < 0) {
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
index 40e5da4fd2a4..d33e3f2dd1d8 100644
--- a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -71,7 +71,7 @@ static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
return yoga_c630_ec_ucsi_write(uec->ec, (u8*)&command);
}
-const struct ucsi_operations yoga_c630_ucsi_ops = {
+static const struct ucsi_operations yoga_c630_ucsi_ops = {
.read_version = yoga_c630_ucsi_read_version,
.read_cci = yoga_c630_ucsi_read_cci,
.poll_cci = yoga_c630_ucsi_read_cci,
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index 6338d818bc8b..9aa30ef76f3b 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -269,7 +269,7 @@ static int stub_recv_cmd_unlink(struct stub_device *sdev,
return 0;
}
- usbip_dbg_stub_rx("seqnum %d is not pending\n",
+ usbip_dbg_stub_rx("seqnum %u is not pending\n",
pdu->u.cmd_unlink.seqnum);
/*
diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c
index b1c2f6781cb3..7eb2e074012a 100644
--- a/drivers/usb/usbip/stub_tx.c
+++ b/drivers/usb/usbip/stub_tx.c
@@ -201,7 +201,7 @@ static int stub_send_ret_submit(struct stub_device *sdev)
/* 1. setup usbip_header */
setup_ret_submit_pdu(&pdu_header, urb);
- usbip_dbg_stub_tx("setup txdata seqnum: %d\n",
+ usbip_dbg_stub_tx("setup txdata seqnum: %u\n",
pdu_header.base.seqnum);
if (priv->sgl) {
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index b03e5021c25b..e70fba9f55d6 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include "usbip_common.h"
#include "vhci.h"
@@ -675,7 +676,7 @@ static void vhci_tx_urb(struct urb *urb, struct vhci_device *vdev)
spin_lock_irqsave(&vdev->priv_lock, flags);
- priv->seqnum = atomic_inc_return(&vhci_hcd->seqnum);
+ priv->seqnum = (u32)atomic_inc_return(&vhci_hcd->seqnum);
if (priv->seqnum == 0xffff)
dev_info(&urb->dev->dev, "seqnum max\n");
@@ -1161,12 +1162,8 @@ static int vhci_setup(struct usb_hcd *hcd)
hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS;
}
- /*
- * Support SG.
- * sg_tablesize is an arbitrary value to alleviate memory pressure
- * on the host.
- */
- hcd->self.sg_tablesize = 32;
+ /* accept arbitrarily long scatter-gather lists */
+ hcd->self.sg_tablesize = ~0;
hcd->self.no_sg_constraint = 1;
return 0;
@@ -1453,7 +1450,7 @@ static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
if (connected > 0) {
dev_info(&pdev->dev,
"We have %d active connection%s. Do not suspend.\n",
- connected, (connected == 1 ? "" : "s"));
+ connected, str_plural(connected));
ret = -EBUSY;
} else {
dev_info(&pdev->dev, "suspend vhci_hcd");
diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
index 7f2d1c241559..a75f4a898a41 100644
--- a/drivers/usb/usbip/vhci_rx.c
+++ b/drivers/usb/usbip/vhci_rx.c
@@ -66,7 +66,7 @@ static void vhci_recv_ret_submit(struct vhci_device *vdev,
spin_unlock_irqrestore(&vdev->priv_lock, flags);
if (!urb) {
- pr_err("cannot find a urb of seqnum %u max seqnum %d\n",
+ pr_err("cannot find a urb of seqnum %u max seqnum %u\n",
pdu->base.seqnum,
atomic_read(&vhci_hcd->seqnum));
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
@@ -162,10 +162,10 @@ static void vhci_recv_ret_unlink(struct vhci_device *vdev,
* already received the result of its submit result and gave
* back the URB.
*/
- pr_info("the urb (seqnum %d) was already given back\n",
+ pr_info("the urb (seqnum %u) was already given back\n",
pdu->base.seqnum);
} else {
- usbip_dbg_vhci_rx("now giveback urb %d\n", pdu->base.seqnum);
+ usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum);
/* If unlink is successful, status is -ECONNRESET */
urb->status = pdu->u.ret_unlink.status;
diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c
index 907a43a00896..2aae3edfc813 100644
--- a/drivers/usb/usbip/vudc_sysfs.c
+++ b/drivers/usb/usbip/vudc_sysfs.c
@@ -67,7 +67,7 @@ out:
* Exposes device descriptor from the gadget driver.
*/
static ssize_t dev_desc_read(struct file *file, struct kobject *kobj,
- struct bin_attribute *attr, char *out,
+ const struct bin_attribute *attr, char *out,
loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
@@ -88,7 +88,7 @@ unlock:
spin_unlock_irqrestore(&udc->lock, flags);
return ret;
}
-static BIN_ATTR_RO(dev_desc, sizeof(struct usb_device_descriptor));
+static const BIN_ATTR_RO(dev_desc, sizeof(struct usb_device_descriptor));
static ssize_t usbip_sockfd_store(struct device *dev,
struct device_attribute *attr,
@@ -252,14 +252,14 @@ static struct attribute *dev_attrs[] = {
NULL,
};
-static struct bin_attribute *dev_bin_attrs[] = {
+static const struct bin_attribute *const dev_bin_attrs[] = {
&bin_attr_dev_desc,
NULL,
};
static const struct attribute_group vudc_attr_group = {
.attrs = dev_attrs,
- .bin_attrs = dev_bin_attrs,
+ .bin_attrs_new = dev_bin_attrs,
};
const struct attribute_group *vudc_groups[] = {
diff --git a/drivers/usb/usbip/vudc_tx.c b/drivers/usb/usbip/vudc_tx.c
index 3ccb17c3e840..30c11bf9f4e7 100644
--- a/drivers/usb/usbip/vudc_tx.c
+++ b/drivers/usb/usbip/vudc_tx.c
@@ -107,7 +107,7 @@ static int v_send_ret_submit(struct vudc *udc, struct urbp *urb_p)
/* 1. setup usbip_header */
setup_ret_submit_pdu(&pdu_header, urb_p);
- usbip_dbg_stub_tx("setup txdata seqnum: %d\n",
+ usbip_dbg_stub_tx("setup txdata seqnum: %u\n",
pdu_header.base.seqnum);
usbip_header_correct_endian(&pdu_header, 1);