diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-04 17:55:35 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-04 17:55:35 -0700 |
commit | ac9053d2dcb9e8c3fa35ce458dfca8fddc141680 (patch) | |
tree | 3ffa30d58dac22ee0a80e2dd32f41b71da91132b /drivers/usb | |
parent | f9ca6a561d40115696a54f16085c4edb17effc74 (diff) | |
parent | 5267c5e09c25e2ee6242b37833a9bdf9d97f54a2 (diff) | |
download | linux-ac9053d2dcb9e8c3fa35ce458dfca8fddc141680.tar.bz2 |
Merge tag 'usb-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB/PHY updates from Greg KH:
"Here is the big set of USB and PHY driver patches for 4.17-rc1.
Lots of USB typeC work happened this round, with code moving from the
staging directory into the "real" part of the kernel, as well as new
infrastructure being added to be able to handle the different types of
"roles" that typeC requires.
There is also the normal huge set of USB gadget controller and driver
updates, along with XHCI changes, and a raft of other tiny fixes all
over the USB tree. And the PHY driver updates are merged in here as
well as they interacted with the USB drivers in some places.
All of these have been in linux-next for a while with no reported
issues"
* tag 'usb-4.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (250 commits)
Revert "USB: serial: ftdi_sio: add Id for Physik Instrumente E-870"
usb: musb: gadget: misplaced out of bounds check
usb: chipidea: imx: Fix ULPI on imx53
usb: chipidea: imx: Cleanup ci_hdrc_imx_platform_flag
usb: chipidea: usbmisc: small clean up
usb: chipidea: usbmisc: evdo can be set e/o reset
usb: chipidea: usbmisc: evdo is only specific to OTG port
USB: serial: ftdi_sio: add Id for Physik Instrumente E-870
usb: dwc3: gadget: never call ->complete() from ->ep_queue()
usb: gadget: udc: core: update usb_ep_queue() documentation
usb: host: Remove the deprecated ATH79 USB host config options
usb: roles: Fix return value check in intel_xhci_usb_probe()
USB: gadget: f_midi: fixing a possible double-free in f_midi
usb: core: Add USB_QUIRK_DELAY_CTRL_MSG to usbcore quirks
usb: core: Copy parameter string correctly and remove superfluous null check
USB: announce bcdDevice as well as idVendor, idProduct.
USB:fix USB3 devices behind USB3 hubs not resuming at hibernate thaw
usb: hub: Reduce warning to notice on power loss
USB: serial: ftdi_sio: add support for Harman FirmwareHubEmulator
USB: serial: cp210x: add ELDAT Easywave RX09 id
...
Diffstat (limited to 'drivers/usb')
115 files changed, 4521 insertions, 2522 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 148f3ee70286..75f7fb151f71 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -171,6 +171,8 @@ source "drivers/usb/gadget/Kconfig" source "drivers/usb/typec/Kconfig" +source "drivers/usb/roles/Kconfig" + config USB_LED_TRIG bool "USB LED Triggers" depends on LEDS_CLASS && LEDS_TRIGGERS @@ -203,4 +205,7 @@ config USB_ULPI_BUS To compile this driver as a module, choose M here: the module will be called ulpi. +config USB_ROLE_SWITCH + tristate + endif # USB_SUPPORT diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 060643a1b5c8..7d1b8c82b208 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -65,3 +65,5 @@ obj-$(CONFIG_USB_COMMON) += common/ obj-$(CONFIG_USBIP_CORE) += usbip/ obj-$(CONFIG_TYPEC) += typec/ + +obj-$(CONFIG_USB_ROLE_SWITCH) += roles/ diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 3b45c25f296e..e431c5aafe35 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -20,7 +20,6 @@ struct ci_hdrc_imx_platform_flag { unsigned int flags; - bool runtime_pm; }; static const struct ci_hdrc_imx_platform_flag imx23_usb_data = { @@ -29,7 +28,7 @@ static const struct ci_hdrc_imx_platform_flag imx23_usb_data = { }; static const struct ci_hdrc_imx_platform_flag imx27_usb_data = { - CI_HDRC_DISABLE_STREAMING, + .flags = CI_HDRC_DISABLE_STREAMING, }; static const struct ci_hdrc_imx_platform_flag imx28_usb_data = { @@ -84,6 +83,7 @@ struct ci_hdrc_imx_data { struct clk *clk; struct imx_usbmisc_data *usbmisc_data; bool supports_runtime_pm; + bool override_phy_control; bool in_lpm; /* SoC before i.mx6 (except imx23/imx28) needs three clks */ bool need_three_clks; @@ -255,6 +255,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) int ret; const struct of_device_id *of_id; const struct ci_hdrc_imx_platform_flag *imx_platform_flag; + struct device_node *np = pdev->dev.of_node; of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev); if (!of_id) @@ -289,6 +290,14 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) } pdata.usb_phy = data->phy; + + if (of_device_is_compatible(np, "fsl,imx53-usb") && pdata.usb_phy && + 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); + } + pdata.flags |= imx_platform_flag->flags; if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM) data->supports_runtime_pm = true; @@ -342,6 +351,8 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) pm_runtime_put_noidle(&pdev->dev); } ci_hdrc_remove_device(data->ci_pdev); + if (data->override_phy_control) + usb_phy_shutdown(data->phy); imx_disable_unprepare_clks(&pdev->dev); return 0; diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index c9e1a165ed82..ce648cb3ed94 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -45,18 +45,7 @@ static int ci_device_show(struct seq_file *s, void *data) return 0; } - -static int ci_device_open(struct inode *inode, struct file *file) -{ - return single_open(file, ci_device_show, inode->i_private); -} - -static const struct file_operations ci_device_fops = { - .open = ci_device_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(ci_device); /** * ci_port_test_show: reads port test mode @@ -156,18 +145,7 @@ static int ci_qheads_show(struct seq_file *s, void *data) return 0; } - -static int ci_qheads_open(struct inode *inode, struct file *file) -{ - return single_open(file, ci_qheads_show, inode->i_private); -} - -static const struct file_operations ci_qheads_fops = { - .open = ci_qheads_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(ci_qheads); /** * ci_requests_show: DMA contents of all requests currently queued (all endpts) @@ -204,18 +182,7 @@ static int ci_requests_show(struct seq_file *s, void *data) return 0; } - -static int ci_requests_open(struct inode *inode, struct file *file) -{ - return single_open(file, ci_requests_show, inode->i_private); -} - -static const struct file_operations ci_requests_fops = { - .open = ci_requests_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(ci_requests); static int ci_otg_show(struct seq_file *s, void *unused) { @@ -278,18 +245,7 @@ static int ci_otg_show(struct seq_file *s, void *unused) return 0; } - -static int ci_otg_open(struct inode *inode, struct file *file) -{ - return single_open(file, ci_otg_show, inode->i_private); -} - -static const struct file_operations ci_otg_fops = { - .open = ci_otg_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(ci_otg); static int ci_role_show(struct seq_file *s, void *data) { @@ -376,18 +332,7 @@ static int ci_registers_show(struct seq_file *s, void *unused) return 0; } - -static int ci_registers_open(struct inode *inode, struct file *file) -{ - return single_open(file, ci_registers_show, inode->i_private); -} - -static const struct file_operations ci_registers_fops = { - .open = ci_registers_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(ci_registers); /** * dbg_create_files: initializes the attribute interface diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 19d60ed7e41f..af45aa3222b5 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -124,10 +124,8 @@ static int host_start(struct ci_hdrc *ci) hcd->power_budget = ci->platdata->power_budget; hcd->tpl_support = ci->platdata->tpl_support; - if (ci->phy) - hcd->phy = ci->phy; - else - hcd->usb_phy = ci->usb_phy; + if (ci->phy || ci->usb_phy) + hcd->skip_phy_initialization = 1; ehci = hcd_to_ehci(hcd); ehci->caps = ci->hw_bank.cap; diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 8cdf0af156c6..34ad5bf8acd8 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -148,14 +148,21 @@ static int usbmisc_imx25_post(struct imx_usbmisc_data *data) if (data->index > 2) return -EINVAL; - if (data->evdo) { - spin_lock_irqsave(&usbmisc->lock, flags); - reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET; - val = readl(reg); - writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg); - spin_unlock_irqrestore(&usbmisc->lock, flags); - usleep_range(5000, 10000); /* needed to stabilize voltage */ - } + if (data->index) + return 0; + + spin_lock_irqsave(&usbmisc->lock, flags); + reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET; + val = readl(reg); + + if (data->evdo) + val |= MX25_BM_EXTERNAL_VBUS_DIVIDER; + else + val &= ~MX25_BM_EXTERNAL_VBUS_DIVIDER; + + writel(val, reg); + spin_unlock_irqrestore(&usbmisc->lock, flags); + usleep_range(5000, 10000); /* needed to stabilize voltage */ return 0; } @@ -308,13 +315,12 @@ static int usbmisc_imx6q_set_wakeup val = readl(usbmisc->base + data->index * 4); if (enabled) { val |= wakeup_setting; - writel(val, usbmisc->base + data->index * 4); } else { if (val & MX6_BM_WAKEUP_INTR) pr_debug("wakeup int at ci_hdrc.%d\n", data->index); val &= ~wakeup_setting; - writel(val, usbmisc->base + data->index * 4); } + writel(val, usbmisc->base + data->index * 4); spin_unlock_irqrestore(&usbmisc->lock, flags); return ret; diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index 0a7c45e85481..fb4d5ef4165c 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -9,3 +9,4 @@ usb-common-$(CONFIG_USB_LED_TRIG) += led.o obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o +obj-$(CONFIG_USB_ROLE_SWITCH) += roles.o diff --git a/drivers/usb/common/roles.c b/drivers/usb/common/roles.c new file mode 100644 index 000000000000..15cc76e22123 --- /dev/null +++ b/drivers/usb/common/roles.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Role Switch Support + * + * Copyright (C) 2018 Intel Corporation + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/usb/role.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> + +static struct class *role_class; + +struct usb_role_switch { + struct device dev; + struct mutex lock; /* device lock*/ + enum usb_role role; + + /* From descriptor */ + struct device *usb2_port; + struct device *usb3_port; + struct device *udc; + usb_role_switch_set_t set; + usb_role_switch_get_t get; + bool allow_userspace_control; +}; + +#define to_role_switch(d) container_of(d, struct usb_role_switch, dev) + +/** + * usb_role_switch_set_role - Set USB role for a switch + * @sw: USB role switch + * @role: USB role to be switched to + * + * Set USB role @role for @sw. + */ +int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role) +{ + int ret; + + if (IS_ERR_OR_NULL(sw)) + return 0; + + mutex_lock(&sw->lock); + + ret = sw->set(sw->dev.parent, role); + if (!ret) + sw->role = role; + + mutex_unlock(&sw->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_role_switch_set_role); + +/** + * usb_role_switch_get_role - Get the USB role for a switch + * @sw: USB role switch + * + * Depending on the role-switch-driver this function returns either a cached + * value of the last set role, or reads back the actual value from the hardware. + */ +enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) +{ + enum usb_role role; + + if (IS_ERR_OR_NULL(sw)) + return USB_ROLE_NONE; + + mutex_lock(&sw->lock); + + if (sw->get) + role = sw->get(sw->dev.parent); + else + role = sw->role; + + mutex_unlock(&sw->lock); + + return role; +} +EXPORT_SYMBOL_GPL(usb_role_switch_get_role); + +static int __switch_match(struct device *dev, const void *name) +{ + return !strcmp((const char *)name, dev_name(dev)); +} + +static void *usb_role_switch_match(struct device_connection *con, int ep, + void *data) +{ + struct device *dev; + + dev = class_find_device(role_class, NULL, con->endpoint[ep], + __switch_match); + + return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); +} + +/** + * usb_role_switch_get - Find USB role switch linked with the caller + * @dev: The caller device + * + * Finds and returns role switch linked with @dev. The reference count for the + * found switch is incremented. + */ +struct usb_role_switch *usb_role_switch_get(struct device *dev) +{ + return device_connection_find_match(dev, "usb-role-switch", NULL, + usb_role_switch_match); +} +EXPORT_SYMBOL_GPL(usb_role_switch_get); + +/** + * usb_role_switch_put - Release handle to a switch + * @sw: USB Role Switch + * + * Decrement reference count for @sw. + */ +void usb_role_switch_put(struct usb_role_switch *sw) +{ + if (!IS_ERR_OR_NULL(sw)) + put_device(&sw->dev); +} +EXPORT_SYMBOL_GPL(usb_role_switch_put); + +static umode_t +usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, typeof(*dev), kobj); + struct usb_role_switch *sw = to_role_switch(dev); + + if (sw->allow_userspace_control) + return attr->mode; + + return 0; +} + +static const char * const usb_roles[] = { + [USB_ROLE_NONE] = "none", + [USB_ROLE_HOST] = "host", + [USB_ROLE_DEVICE] = "device", +}; + +static ssize_t +role_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_role_switch *sw = to_role_switch(dev); + enum usb_role role = usb_role_switch_get_role(sw); + + return sprintf(buf, "%s\n", usb_roles[role]); +} + +static ssize_t role_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct usb_role_switch *sw = to_role_switch(dev); + int ret; + + ret = sysfs_match_string(usb_roles, buf); + if (ret < 0) { + bool res; + + /* Extra check if the user wants to disable the switch */ + ret = kstrtobool(buf, &res); + if (ret || res) + return -EINVAL; + } + + ret = usb_role_switch_set_role(sw, ret); + if (ret) + return ret; + + return size; +} +static DEVICE_ATTR_RW(role); + +static struct attribute *usb_role_switch_attrs[] = { + &dev_attr_role.attr, + NULL, +}; + +static const struct attribute_group usb_role_switch_group = { + .is_visible = usb_role_switch_is_visible, + .attrs = usb_role_switch_attrs, +}; + +static const struct attribute_group *usb_role_switch_groups[] = { + &usb_role_switch_group, + NULL, +}; + +static int +usb_role_switch_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + int ret; + + ret = add_uevent_var(env, "USB_ROLE_SWITCH=%s", dev_name(dev)); + if (ret) + dev_err(dev, "failed to add uevent USB_ROLE_SWITCH\n"); + + return ret; +} + +static void usb_role_switch_release(struct device *dev) +{ + struct usb_role_switch *sw = to_role_switch(dev); + + kfree(sw); +} + +static const struct device_type usb_role_dev_type = { + .name = "usb_role_switch", + .groups = usb_role_switch_groups, + .uevent = usb_role_switch_uevent, + .release = usb_role_switch_release, +}; + +/** + * usb_role_switch_register - Register USB Role Switch + * @parent: Parent device for the switch + * @desc: Description of the switch + * + * USB Role Switch is a device capable or choosing the role for USB connector. + * On platforms where the USB controller is dual-role capable, the controller + * driver will need to register the switch. On platforms where the USB host and + * USB device controllers behind the connector are separate, there will be a + * mux, and the driver for that mux will need to register the switch. + * + * Returns handle to a new role switch or ERR_PTR. The content of @desc is + * copied. + */ +struct usb_role_switch * +usb_role_switch_register(struct device *parent, + const struct usb_role_switch_desc *desc) +{ + struct usb_role_switch *sw; + int ret; + + if (!desc || !desc->set) + return ERR_PTR(-EINVAL); + + sw = kzalloc(sizeof(*sw), GFP_KERNEL); + if (!sw) + return ERR_PTR(-ENOMEM); + + mutex_init(&sw->lock); + + sw->allow_userspace_control = desc->allow_userspace_control; + sw->usb2_port = desc->usb2_port; + sw->usb3_port = desc->usb3_port; + sw->udc = desc->udc; + sw->set = desc->set; + sw->get = desc->get; + + sw->dev.parent = parent; + sw->dev.class = role_class; + sw->dev.type = &usb_role_dev_type; + dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent)); + + ret = device_register(&sw->dev); + if (ret) { + put_device(&sw->dev); + return ERR_PTR(ret); + } + + /* TODO: Symlinks for the host port and the device controller. */ + + return sw; +} +EXPORT_SYMBOL_GPL(usb_role_switch_register); + +/** + * usb_role_switch_unregister - Unregsiter USB Role Switch + * @sw: USB Role Switch + * + * Unregister switch that was registered with usb_role_switch_register(). + */ +void usb_role_switch_unregister(struct usb_role_switch *sw) +{ + if (!IS_ERR_OR_NULL(sw)) + device_unregister(&sw->dev); +} +EXPORT_SYMBOL_GPL(usb_role_switch_unregister); + +static int __init usb_roles_init(void) +{ + role_class = class_create(THIS_MODULE, "usb_role"); + return PTR_ERR_OR_ZERO(role_class); +} +subsys_initcall(usb_roles_init); + +static void __exit usb_roles_exit(void) +{ + class_destroy(role_class); +} +module_exit(usb_roles_exit); + +MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("USB Role Class"); diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 92c9cefb4317..18e874b0441e 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -6,7 +6,7 @@ usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o usbcore-y += devio.o notify.o generic.o quirks.o devices.o -usbcore-y += port.o +usbcore-y += phy.o port.o usbcore-$(CONFIG_OF) += of.o usbcore-$(CONFIG_USB_PCI) += hcd-pci.o diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 83c14dda6300..bc8242bc4564 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -210,8 +210,13 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg) if (!udev->parent) rc = hcd_bus_suspend(udev, msg); - /* Non-root devices don't need to do anything for FREEZE or PRETHAW */ - else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW) + /* + * Non-root USB2 devices don't need to do anything for FREEZE + * or PRETHAW. USB3 devices don't support global suspend and + * needs to be selectively suspended. + */ + else if ((msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW) + && (udev->speed < USB_SPEED_SUPER)) rc = 0; else rc = usb_port_suspend(udev, msg); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index fc32391a34d5..777036ae6367 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -37,6 +37,7 @@ #include <linux/usb/otg.h> #include "usb.h" +#include "phy.h" /*-------------------------------------------------------------------------*/ @@ -2260,6 +2261,9 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg) usb_set_device_state(rhdev, USB_STATE_SUSPENDED); hcd->state = HC_STATE_SUSPENDED; + if (!PMSG_IS_AUTO(msg)) + usb_phy_roothub_power_off(hcd->phy_roothub); + /* Did we race with a root-hub wakeup event? */ if (rhdev->do_remote_wakeup) { char buffer[6]; @@ -2296,6 +2300,13 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume"); return 0; } + + if (!PMSG_IS_AUTO(msg)) { + status = usb_phy_roothub_power_on(hcd->phy_roothub); + if (status) + return status; + } + if (!hcd->driver->bus_resume) return -ENOENT; if (HCD_RH_RUNNING(hcd)) @@ -2333,6 +2344,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg) } } else { hcd->state = old_state; + usb_phy_roothub_power_off(hcd->phy_roothub); dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", "resume", status); if (status != -ESHUTDOWN) @@ -2727,7 +2739,7 @@ int usb_add_hcd(struct usb_hcd *hcd, int retval; struct usb_device *rhdev; - if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->usb_phy) { + if (IS_ENABLED(CONFIG_USB_PHY) && !hcd->skip_phy_initialization) { struct usb_phy *phy = usb_get_phy_dev(hcd->self.sysdev, 0); if (IS_ERR(phy)) { @@ -2745,28 +2757,16 @@ int usb_add_hcd(struct usb_hcd *hcd, } } - if (IS_ENABLED(CONFIG_GENERIC_PHY) && !hcd->phy) { - struct phy *phy = phy_get(hcd->self.sysdev, "usb"); - - if (IS_ERR(phy)) { - retval = PTR_ERR(phy); - if (retval == -EPROBE_DEFER) - goto err_phy; - } else { - retval = phy_init(phy); - if (retval) { - phy_put(phy); - goto err_phy; - } - retval = phy_power_on(phy); - if (retval) { - phy_exit(phy); - phy_put(phy); - goto err_phy; - } - hcd->phy = phy; - hcd->remove_phy = 1; + if (!hcd->skip_phy_initialization && usb_hcd_is_primary_hcd(hcd)) { + hcd->phy_roothub = usb_phy_roothub_init(hcd->self.sysdev); + if (IS_ERR(hcd->phy_roothub)) { + retval = PTR_ERR(hcd->phy_roothub); + goto err_phy_roothub_init; } + + retval = usb_phy_roothub_power_on(hcd->phy_roothub); + if (retval) + goto err_usb_phy_roothub_power_on; } dev_info(hcd->self.controller, "%s\n", hcd->product_desc); @@ -2933,13 +2933,10 @@ err_allocate_root_hub: err_register_bus: hcd_buffer_destroy(hcd); err_create_buf: - if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) { - phy_power_off(hcd->phy); - phy_exit(hcd->phy); - phy_put(hcd->phy); - hcd->phy = NULL; - } -err_phy: + usb_phy_roothub_power_off(hcd->phy_roothub); +err_usb_phy_roothub_power_on: + usb_phy_roothub_exit(hcd->phy_roothub); +err_phy_roothub_init: if (hcd->remove_phy && hcd->usb_phy) { usb_phy_shutdown(hcd->usb_phy); usb_put_phy(hcd->usb_phy); @@ -3017,12 +3014,9 @@ void usb_remove_hcd(struct usb_hcd *hcd) usb_deregister_bus(&hcd->self); hcd_buffer_destroy(hcd); - if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) { - phy_power_off(hcd->phy); - phy_exit(hcd->phy); - phy_put(hcd->phy); - hcd->phy = NULL; - } + usb_phy_roothub_power_off(hcd->phy_roothub); + usb_phy_roothub_exit(hcd->phy_roothub); + if (hcd->remove_phy && hcd->usb_phy) { usb_phy_shutdown(hcd->usb_phy); usb_put_phy(hcd->usb_phy); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c5c1f6cf3228..f6ea16e9f6bb 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2192,9 +2192,13 @@ static void show_string(struct usb_device *udev, char *id, char *string) static void announce_device(struct usb_device *udev) { - dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n", + u16 bcdDevice = le16_to_cpu(udev->descriptor.bcdDevice); + + dev_info(&udev->dev, + "New USB device found, idVendor=%04x, idProduct=%04x, bcdDevice=%2x.%02x\n", le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); + le16_to_cpu(udev->descriptor.idProduct), + bcdDevice >> 8, bcdDevice & 0xff); dev_info(&udev->dev, "New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n", udev->descriptor.iManufacturer, @@ -3655,7 +3659,7 @@ static int hub_reset_resume(struct usb_interface *intf) */ void usb_root_hub_lost_power(struct usb_device *rhdev) { - dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); + dev_notice(&rhdev->dev, "root hub lost power or was reset\n"); rhdev->reset_resume = 1; } EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); @@ -5104,8 +5108,10 @@ static void port_event(struct usb_hub *hub, int port1) if (portchange & USB_PORT_STAT_C_OVERCURRENT) { u16 status = 0, unused; + port_dev->over_current_count++; - dev_dbg(&port_dev->dev, "over-current change\n"); + dev_dbg(&port_dev->dev, "over-current change #%u\n", + port_dev->over_current_count); usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_OVER_CURRENT); msleep(100); /* Cool down */ @@ -5505,21 +5511,15 @@ static int usb_reset_and_verify_device(struct usb_device *udev) if (udev->usb2_hw_lpm_enabled == 1) usb_set_usb2_hardware_lpm(udev, 0); - /* Disable LPM and LTM while we reset the device and reinstall the alt - * settings. Device-initiated LPM settings, and system exit latency - * settings are cleared when the device is reset, so we have to set - * them up again. + /* Disable LPM while we reset the device and reinstall the alt settings. + * Device-initiated LPM, and system exit latency settings are cleared + * when the device is reset, so we have to set them up again. */ ret = usb_unlocked_disable_lpm(udev); if (ret) { dev_err(&udev->dev, "%s Failed to disable LPM\n", __func__); goto re_enumerate_no_bos; } - ret = usb_disable_ltm(udev); - if (ret) { - dev_err(&udev->dev, "%s Failed to disable LTM\n", __func__); - goto re_enumerate_no_bos; - } bos = udev->bos; udev->bos = NULL; diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 2a700ccc868c..4dc769ee9c74 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -96,6 +96,7 @@ struct usb_port { enum usb_port_connect_type connect_type; usb_port_location_t location; struct mutex status_lock; + u32 over_current_count; u8 portnum; unsigned int is_superspeed:1; unsigned int usb3_lpm_u1_permit:1; diff --git a/drivers/usb/core/phy.c b/drivers/usb/core/phy.c new file mode 100644 index 000000000000..09b7c43c0ea4 --- /dev/null +++ b/drivers/usb/core/phy.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * A wrapper for multiple PHYs which passes all phy_* function calls to + * multiple (actual) PHY devices. This is comes handy when initializing + * all PHYs on a HCD and to keep them all in the same state. + * + * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + */ + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/phy/phy.h> +#include <linux/of.h> + +#include "phy.h" + +struct usb_phy_roothub { + struct phy *phy; + struct list_head list; +}; + +static struct usb_phy_roothub *usb_phy_roothub_alloc(struct device *dev) +{ + struct usb_phy_roothub *roothub_entry; + + roothub_entry = devm_kzalloc(dev, sizeof(*roothub_entry), GFP_KERNEL); + if (!roothub_entry) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&roothub_entry->list); + + return roothub_entry; +} + +static int usb_phy_roothub_add_phy(struct device *dev, int index, + struct list_head *list) +{ + struct usb_phy_roothub *roothub_entry; + struct phy *phy = devm_of_phy_get_by_index(dev, dev->of_node, index); + + if (IS_ERR_OR_NULL(phy)) { + if (!phy || PTR_ERR(phy) == -ENODEV) + return 0; + else + return PTR_ERR(phy); + } + + roothub_entry = usb_phy_roothub_alloc(dev); + if (IS_ERR(roothub_entry)) + return PTR_ERR(roothub_entry); + + roothub_entry->phy = phy; + + list_add_tail(&roothub_entry->list, list); + + return 0; +} + +struct usb_phy_roothub *usb_phy_roothub_init(struct device *dev) +{ + struct usb_phy_roothub *phy_roothub; + struct usb_phy_roothub *roothub_entry; + struct list_head *head; + int i, num_phys, err; + + num_phys = of_count_phandle_with_args(dev->of_node, "phys", + "#phy-cells"); + if (num_phys <= 0) + return NULL; + + phy_roothub = usb_phy_roothub_alloc(dev); + if (IS_ERR(phy_roothub)) + return phy_roothub; + + for (i = 0; i < num_phys; i++) { + err = usb_phy_roothub_add_phy(dev, i, &phy_roothub->list); + if (err) + goto err_out; + } + + head = &phy_roothub->list; + + list_for_each_entry(roothub_entry, head, list) { + err = phy_init(roothub_entry->phy); + if (err) + goto err_exit_phys; + } + + return phy_roothub; + +err_exit_phys: + list_for_each_entry_continue_reverse(roothub_entry, head, list) + phy_exit(roothub_entry->phy); + +err_out: + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(usb_phy_roothub_init); + +int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub) +{ + struct usb_phy_roothub *roothub_entry; + struct list_head *head; + int err, ret = 0; + + if (!phy_roothub) + return 0; + + head = &phy_roothub->list; + + list_for_each_entry(roothub_entry, head, list) { + err = phy_exit(roothub_entry->phy); + if (err) + ret = ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(usb_phy_roothub_exit); + +int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub) +{ + struct usb_phy_roothub *roothub_entry; + struct list_head *head; + int err; + + if (!phy_roothub) + return 0; + + head = &phy_roothub->list; + + list_for_each_entry(roothub_entry, head, list) { + err = phy_power_on(roothub_entry->phy); + if (err) + goto err_out; + } + + return 0; + +err_out: + list_for_each_entry_continue_reverse(roothub_entry, head, list) + phy_power_off(roothub_entry->phy); + + return err; +} +EXPORT_SYMBOL_GPL(usb_phy_roothub_power_on); + +void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub) +{ + struct usb_phy_roothub *roothub_entry; + + if (!phy_roothub) + return; + + list_for_each_entry_reverse(roothub_entry, &phy_roothub->list, list) + phy_power_off(roothub_entry->phy); +} +EXPORT_SYMBOL_GPL(usb_phy_roothub_power_off); diff --git a/drivers/usb/core/phy.h b/drivers/usb/core/phy.h new file mode 100644 index 000000000000..6fde59bfbff8 --- /dev/null +++ b/drivers/usb/core/phy.h @@ -0,0 +1,7 @@ +struct usb_phy_roothub; + +struct usb_phy_roothub *usb_phy_roothub_init(struct device *dev); +int usb_phy_roothub_exit(struct usb_phy_roothub *phy_roothub); + +int usb_phy_roothub_power_on(struct usb_phy_roothub *phy_roothub); +void usb_phy_roothub_power_off(struct usb_phy_roothub *phy_roothub); diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 1a01e9ad3804..6979bde87d31 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -41,6 +41,15 @@ static ssize_t connect_type_show(struct device *dev, } static DEVICE_ATTR_RO(connect_type); +static ssize_t over_current_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_port *port_dev = to_usb_port(dev); + + return sprintf(buf, "%u\n", port_dev->over_current_count); +} +static DEVICE_ATTR_RO(over_current_count); + static ssize_t usb3_lpm_permit_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -109,6 +118,7 @@ static DEVICE_ATTR_RW(usb3_lpm_permit); static struct attribute *port_dev_attrs[] = { &dev_attr_connect_type.attr, + &dev_attr_over_current_count.attr, NULL, }; diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 54b019e267c5..920f48a49a87 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -6,11 +6,157 @@ * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de> */ +#include <linux/moduleparam.h> #include <linux/usb.h> #include <linux/usb/quirks.h> #include <linux/usb/hcd.h> #include "usb.h" +struct quirk_entry { + u16 vid; + u16 pid; + u32 flags; +}; + +static DEFINE_MUTEX(quirk_mutex); + +static struct quirk_entry *quirk_list; +static unsigned int quirk_count; + +static char quirks_param[128]; + +static int quirks_param_set(const char *val, const struct kernel_param *kp) +{ + char *p, *field; + u16 vid, pid; + u32 flags; + size_t i; + int err; + + err = param_set_copystring(val, kp); + if (err) + return err; + + mutex_lock(&quirk_mutex); + + if (!*val) { + quirk_count = 0; + kfree(quirk_list); + quirk_list = NULL; + goto unlock; + } + + for (quirk_count = 1, i = 0; val[i]; i++) + if (val[i] == ',') + quirk_count++; + + if (quirk_list) { + kfree(quirk_list); + quirk_list = NULL; + } + + quirk_list = kcalloc(quirk_count, sizeof(struct quirk_entry), + GFP_KERNEL); + if (!quirk_list) { + mutex_unlock(&quirk_mutex); + return -ENOMEM; + } + + for (i = 0, p = (char *)val; p && *p;) { + /* Each entry consists of VID:PID:flags */ + field = strsep(&p, ":"); + if (!field) + break; + + if (kstrtou16(field, 16, &vid)) + break; + + field = strsep(&p, ":"); + if (!field) + break; + + if (kstrtou16(field, 16, &pid)) + break; + + field = strsep(&p, ","); + if (!field || !*field) + break; + + /* Collect the flags */ + for (flags = 0; *field; field++) { + switch (*field) { + case 'a': + flags |= USB_QUIRK_STRING_FETCH_255; + break; + case 'b': + flags |= USB_QUIRK_RESET_RESUME; + break; + case 'c': + flags |= USB_QUIRK_NO_SET_INTF; + break; + case 'd': + flags |= USB_QUIRK_CONFIG_INTF_STRINGS; + break; + case 'e': + flags |= USB_QUIRK_RESET; + break; + case 'f': + flags |= USB_QUIRK_HONOR_BNUMINTERFACES; + break; + case 'g': + flags |= USB_QUIRK_DELAY_INIT; + break; + case 'h': + flags |= USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL; + break; + case 'i': + flags |= USB_QUIRK_DEVICE_QUALIFIER; + break; + case 'j': + flags |= USB_QUIRK_IGNORE_REMOTE_WAKEUP; + break; + case 'k': + flags |= USB_QUIRK_NO_LPM; + break; + case 'l': + flags |= USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL; + break; + case 'm': + flags |= USB_QUIRK_DISCONNECT_SUSPEND; + break; + case 'n': + flags |= USB_QUIRK_DELAY_CTRL_MSG; + break; + /* Ignore unrecognized flag characters */ + } + } + + quirk_list[i++] = (struct quirk_entry) + { .vid = vid, .pid = pid, .flags = flags }; + } + + if (i < quirk_count) + quirk_count = i; + +unlock: + mutex_unlock(&quirk_mutex); + + return 0; +} + +static const struct kernel_param_ops quirks_param_ops = { + .set = quirks_param_set, + .get = param_get_string, +}; + +static struct kparam_string quirks_param_string = { + .maxlen = sizeof(quirks_param), + .string = quirks_param, +}; + +module_param_cb(quirks, &quirks_param_ops, &quirks_param_string, 0644); +MODULE_PARM_DESC(quirks, "Add/modify USB quirks by specifying quirks=vendorID:productID:quirks"); + /* Lists of quirky USB devices, split in device quirks and interface quirks. * Device quirks are applied at the very beginning of the enumeration process, * right after reading the device descriptor. They can thus only match on device @@ -321,8 +467,8 @@ static int usb_amd_resume_quirk(struct usb_device *udev) return 0; } -static u32 __usb_detect_quirks(struct usb_device *udev, - const struct usb_device_id *id) +static u32 usb_detect_static_quirks(struct usb_device *udev, + const struct usb_device_id *id) { u32 quirks = 0; @@ -340,21 +486,43 @@ static u32 __usb_detect_quirks(struct usb_device *udev, return quirks; } +static u32 usb_detect_dynamic_quirks(struct usb_device *udev) +{ + u16 vid = le16_to_cpu(udev->descriptor.idVendor); + u16 pid = le16_to_cpu(udev->descriptor.idProduct); + int i, flags = 0; + + mutex_lock(&quirk_mutex); + + for (i = 0; i < quirk_count; i++) { + if (vid == quirk_list[i].vid && pid == quirk_list[i].pid) { + flags = quirk_list[i].flags; + break; + } + } + + mutex_unlock(&quirk_mutex); + + return flags; +} + /* * Detect any quirks the device has, and do any housekeeping for it if needed. */ void usb_detect_quirks(struct usb_device *udev) { - udev->quirks = __usb_detect_quirks(udev, usb_quirk_list); + udev->quirks = usb_detect_static_quirks(udev, usb_quirk_list); /* * Pixart-based mice would trigger remote wakeup issue on AMD * Yangtze chipset, so set them as RESET_RESUME flag. */ if (usb_amd_resume_quirk(udev)) - udev->quirks |= __usb_detect_quirks(udev, + udev->quirks |= usb_detect_static_quirks(udev, usb_amd_resume_quirk_list); + udev->quirks ^= usb_detect_dynamic_quirks(udev); + if (udev->quirks) dev_dbg(&udev->dev, "USB quirks for this device: %x\n", udev->quirks); @@ -373,7 +541,7 @@ void usb_detect_interface_quirks(struct usb_device *udev) { u32 quirks; - quirks = __usb_detect_quirks(udev, usb_interface_quirk_list); + quirks = usb_detect_static_quirks(udev, usb_interface_quirk_list); if (quirks == 0) return; @@ -381,3 +549,11 @@ void usb_detect_interface_quirks(struct usb_device *udev) quirks); udev->quirks |= quirks; } + +void usb_release_quirk_list(void) +{ + mutex_lock(&quirk_mutex); + kfree(quirk_list); + quirk_list = NULL; + mutex_unlock(&quirk_mutex); +} diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 796c9b149728..f51750bcd152 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -433,6 +433,14 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) max *= mult; } + if (dev->speed == USB_SPEED_SUPER_PLUS && + USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes)) { + struct usb_ssp_isoc_ep_comp_descriptor *isoc_ep_comp; + + isoc_ep_comp = &ep->ssp_isoc_ep_comp; + max = le32_to_cpu(isoc_ep_comp->dwBytesPerInterval); + } + /* "high bandwidth" mode, 1-3 packets/uframe? */ if (dev->speed == USB_SPEED_HIGH) max *= usb_endpoint_maxp_mult(&ep->desc); diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index 84da17460568..e221861b3187 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -90,8 +90,8 @@ static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle, acpi_status status; /* - * According to ACPI Spec 9.13. PLD indicates whether usb port is - * user visible and _UPC indicates whether it is connectable. If + * According to 9.14 in ACPI Spec 6.2. _PLD indicates whether usb port + * is user visible and _UPC indicates whether it is connectable. If * the port was visible and connectable, it could be freely connected * and disconnected with USB devices. If no visible and connectable, * a usb device is directly hard-wired to the port. If no visible and diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 2f5fbc56a9dd..0adb6345ff2e 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -1259,6 +1259,7 @@ static void __exit usb_exit(void) if (usb_disabled()) return; + usb_release_quirk_list(); usb_deregister_device_driver(&usb_generic_driver); usb_major_cleanup(); usb_deregister(&usbfs_driver); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 149cc7480971..546a2219454b 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -36,6 +36,7 @@ extern void usb_deauthorize_interface(struct usb_interface *); extern void usb_authorize_interface(struct usb_interface *); extern void usb_detect_quirks(struct usb_device *udev); extern void usb_detect_interface_quirks(struct usb_device *udev); +extern void usb_release_quirk_list(void); extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 82a7d98c3436..18a0a1771289 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -64,10 +64,11 @@ * * @hsotg: Programming view of the DWC_otg controller */ -static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) +int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) { struct dwc2_gregs_backup *gr; - int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); /* Backup global regs */ gr = &hsotg->gr_backup; @@ -78,10 +79,11 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) gr->gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); gr->grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ); gr->gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); - gr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ); gr->gdfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG); - for (i = 0; i < MAX_EPS_CHANNELS; i++) - gr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i)); + gr->pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1); + gr->glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + gr->gi2cctl = dwc2_readl(hsotg->regs + GI2CCTL); + gr->pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); gr->valid = true; return 0; @@ -94,10 +96,9 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) * * @hsotg: Programming view of the DWC_otg controller */ -static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) +int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) { struct dwc2_gregs_backup *gr; - int i; dev_dbg(hsotg->dev, "%s\n", __func__); @@ -117,26 +118,27 @@ static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) dwc2_writel(gr->gahbcfg, hsotg->regs + GAHBCFG); dwc2_writel(gr->grxfsiz, hsotg->regs + GRXFSIZ); dwc2_writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ); - dwc2_writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ); dwc2_writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG); - for (i = 0; i < MAX_EPS_CHANNELS; i++) - dwc2_writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); + dwc2_writel(gr->pcgcctl1, hsotg->regs + PCGCCTL1); + dwc2_writel(gr->glpmcfg, hsotg->regs + GLPMCFG); + dwc2_writel(gr->pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(gr->gi2cctl, hsotg->regs + GI2CCTL); return 0; } /** - * dwc2_exit_hibernation() - Exit controller from Partial Power Down. + * dwc2_exit_partial_power_down() - Exit controller from Partial Power Down. * * @hsotg: Programming view of the DWC_otg controller * @restore: Controller registers need to be restored */ -int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) +int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore) { u32 pcgcctl; int ret = 0; - if (!hsotg->params.hibernation) + if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) return -ENOTSUPP; pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); @@ -167,7 +169,7 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) return ret; } } else { - ret = dwc2_restore_device_registers(hsotg); + ret = dwc2_restore_device_registers(hsotg, 0); if (ret) { dev_err(hsotg->dev, "%s: failed to restore device registers\n", __func__); @@ -180,16 +182,16 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) } /** - * dwc2_enter_hibernation() - Put controller in Partial Power Down. + * dwc2_enter_partial_power_down() - Put controller in Partial Power Down. * * @hsotg: Programming view of the DWC_otg controller */ -int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) +int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg) { u32 pcgcctl; int ret = 0; - if (!hsotg->params.hibernation) + if (!hsotg->params.power_down) return -ENOTSUPP; /* Backup all registers */ @@ -218,7 +220,7 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) /* * Clear any pending interrupts since dwc2 will not be able to - * clear them after entering hibernation. + * clear them after entering partial_power_down. */ dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); @@ -240,6 +242,142 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) } /** + * dwc2_restore_essential_regs() - Restore essiential regs of core. + * + * @hsotg: Programming view of the DWC_otg controller + * @rmode: Restore mode, enabled in case of remote-wakeup. + * @is_host: Host or device mode. + */ +static void dwc2_restore_essential_regs(struct dwc2_hsotg *hsotg, int rmode, + int is_host) +{ + u32 pcgcctl; + struct dwc2_gregs_backup *gr; + struct dwc2_dregs_backup *dr; + struct dwc2_hregs_backup *hr; + + gr = &hsotg->gr_backup; + dr = &hsotg->dr_backup; + hr = &hsotg->hr_backup; + + dev_dbg(hsotg->dev, "%s: restoring essential regs\n", __func__); + + /* Load restore values for [31:14] bits */ + pcgcctl = (gr->pcgcctl & 0xffffc000); + /* If High Speed */ + if (is_host) { + if (!(pcgcctl & PCGCTL_P2HD_PRT_SPD_MASK)) + pcgcctl |= BIT(17); + } else { + if (!(pcgcctl & PCGCTL_P2HD_DEV_ENUM_SPD_MASK)) + pcgcctl |= BIT(17); + } + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + + /* Umnask global Interrupt in GAHBCFG and restore it */ + dwc2_writel(gr->gahbcfg | GAHBCFG_GLBL_INTR_EN, hsotg->regs + GAHBCFG); + + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + + /* Unmask restore done interrupt */ + dwc2_writel(GINTSTS_RESTOREDONE, hsotg->regs + GINTMSK); + + /* Restore GUSBCFG and HCFG/DCFG */ + dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + + if (is_host) { + dwc2_writel(hr->hcfg, hsotg->regs + HCFG); + if (rmode) + pcgcctl |= PCGCTL_RESTOREMODE; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + udelay(10); + + pcgcctl |= PCGCTL_ESS_REG_RESTORED; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + udelay(10); + } else { + dwc2_writel(dr->dcfg, hsotg->regs + DCFG); + if (!rmode) + pcgcctl |= PCGCTL_RESTOREMODE | PCGCTL_RSTPDWNMODULE; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + udelay(10); + + pcgcctl |= PCGCTL_ESS_REG_RESTORED; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + udelay(10); + } +} + +/** + * dwc2_hib_restore_common() - Common part of restore routine. + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup. + * @is_host: Host or device mode. + */ +void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, + int is_host) +{ + u32 gpwrdn; + + /* Switch-on voltage to the core */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_PWRDNSWTCH; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Reset core */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_PWRDNRSTN; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Enable restore from PMU */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_RESTORE; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Disable Power Down Clamp */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_PWRDNCLMP; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(50); + + if (!is_host && rem_wakeup) + udelay(70); + + /* Deassert reset core */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNRSTN; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Disable PMU interrupt */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_PMUINTSEL; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Set Restore Essential Regs bit in PCGCCTL register */ + dwc2_restore_essential_regs(hsotg, rem_wakeup, is_host); + + /* + * Wait For Restore_done Interrupt. This mechanism of polling the + * interrupt is introduced to avoid any possible race conditions + */ + if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_RESTOREDONE, + 20000)) { + dev_dbg(hsotg->dev, + "%s: Restore Done wan't generated here\n", + __func__); + } else { + dev_dbg(hsotg->dev, "restore done generated here\n"); + } +} + +/** * dwc2_wait_for_mode() - Waits for the controller mode. * @hsotg: Programming view of the DWC_otg controller. * @host_mode: If true, waits for host mode, otherwise device mode. @@ -311,13 +449,50 @@ static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg) } /* + * dwc2_enter_hibernation() - Common function to enter hibernation. + * + * @hsotg: Programming view of the DWC_otg controller + * @is_host: True if core is in host mode. + * + * Return: 0 if successful, negative error code otherwise + */ +int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host) +{ + if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_HIBERNATION) + return -ENOTSUPP; + + if (is_host) + return dwc2_host_enter_hibernation(hsotg); + else + return dwc2_gadget_enter_hibernation(hsotg); +} + +/* + * dwc2_exit_hibernation() - Common function to exit from hibernation. + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: Remote-wakeup, enabled in case of remote-wakeup. + * @reset: Enabled in case of restore with reset. + * @is_host: True if core is in host mode. + * + * Return: 0 if successful, negative error code otherwise + */ +int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, + int reset, int is_host) +{ + if (is_host) + return dwc2_host_exit_hibernation(hsotg, rem_wakeup, reset); + else + return dwc2_gadget_exit_hibernation(hsotg, rem_wakeup, reset); +} + +/* * Do core a soft reset of the core. Be careful with this because it * resets all the internal state machines of the core. */ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait) { u32 greset; - int count = 0; bool wait_for_host_mode = false; dev_vdbg(hsotg->dev, "%s()\n", __func__); @@ -346,29 +521,19 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait) greset = dwc2_readl(hsotg->regs + GRSTCTL); greset |= GRSTCTL_CSFTRST; dwc2_writel(greset, hsotg->regs + GRSTCTL); - do { - udelay(1); - greset = dwc2_readl(hsotg->regs + GRSTCTL); - if (++count > 50) { - dev_warn(hsotg->dev, - "%s() HANG! Soft Reset GRSTCTL=%0x\n", - __func__, greset); - return -EBUSY; - } - } while (greset & GRSTCTL_CSFTRST); + + if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 50)) { + dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n", + __func__); + return -EBUSY; + } /* Wait for AHB master IDLE state */ - count = 0; - do { - udelay(1); - greset = dwc2_readl(hsotg->regs + GRSTCTL); - if (++count > 50) { - dev_warn(hsotg->dev, - "%s() HANG! AHB Idle GRSTCTL=%0x\n", - __func__, greset); - return -EBUSY; - } - } while (!(greset & GRSTCTL_AHBIDLE)); + if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 50)) { + dev_warn(hsotg->dev, "%s: HANG! AHB Idle timeout GRSTCTL GRSTCTL_AHBIDLE\n", + __func__); + return -EBUSY; + } if (wait_for_host_mode && !skip_wait) dwc2_wait_for_mode(hsotg, true); @@ -376,14 +541,14 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait) return 0; } -/* - * Force the mode of the controller. +/** + * dwc2_force_mode() - Force the mode of the controller. * * Forcing the mode is needed for two cases: * * 1) If the dr_mode is set to either HOST or PERIPHERAL we force the * controller to stay in a particular mode regardless of ID pin - * changes. We do this usually after a core reset. + * changes. We do this once during probe. * * 2) During probe we want to read reset values of the hw * configuration registers that are only available in either host or @@ -400,7 +565,7 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait) * the filter is configured and enabled. We poll the current mode of * the controller to account for this delay. */ -static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) +void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) { u32 gusbcfg; u32 set; @@ -412,17 +577,17 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) * Force mode has no effect if the hardware is not OTG. */ if (!dwc2_hw_is_otg(hsotg)) - return false; + return; /* * If dr_mode is either peripheral or host only, there is no * need to ever force the mode to the opposite mode. */ if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)) - return false; + return; if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST)) - return false; + return; gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); @@ -434,7 +599,7 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); dwc2_wait_for_mode(hsotg, host); - return true; + return; } /** @@ -446,10 +611,15 @@ static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) * the force mode. We only need to call this once during probe if * dr_mode == OTG. */ -void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg) +static void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg) { u32 gusbcfg; + if (!dwc2_hw_is_otg(hsotg)) + return; + + dev_dbg(hsotg->dev, "Clearing force mode bits\n"); + gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; gusbcfg &= ~GUSBCFG_FORCEDEVMODE; @@ -464,16 +634,13 @@ void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg) */ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg) { - bool ret; - switch (hsotg->dr_mode) { case USB_DR_MODE_HOST: - ret = dwc2_force_mode(hsotg, true); /* * NOTE: This is required for some rockchip soc based * platforms on their host-only dwc2. */ - if (!ret) + if (!dwc2_hw_is_otg(hsotg)) msleep(50); break; @@ -491,22 +658,17 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg) } /* - * Do core a soft reset of the core. Be careful with this because it - * resets all the internal state machines of the core. - * - * Additionally this will apply force mode as per the hsotg->dr_mode - * parameter. + * dwc2_enable_acg - enable active clock gating feature */ -int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg) +void dwc2_enable_acg(struct dwc2_hsotg *hsotg) { - int retval; + if (hsotg->params.acg_enable) { + u32 pcgcctl1 = dwc2_readl(hsotg->regs + PCGCCTL1); - retval = dwc2_core_reset(hsotg, false); - if (retval) - return retval; - - dwc2_force_dr_mode(hsotg); - return 0; + dev_dbg(hsotg->dev, "Enabling Active Clock Gating\n"); + pcgcctl1 |= PCGCCTL1_GATEEN; + dwc2_writel(pcgcctl1, hsotg->regs + PCGCCTL1); + } } /** @@ -683,25 +845,21 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg) void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num) { u32 greset; - int count = 0; dev_vdbg(hsotg->dev, "Flush Tx FIFO %d\n", num); + /* Wait for AHB master IDLE state */ + if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000)) + dev_warn(hsotg->dev, "%s: HANG! AHB Idle GRSCTL\n", + __func__); + greset = GRSTCTL_TXFFLSH; greset |= num << GRSTCTL_TXFNUM_SHIFT & GRSTCTL_TXFNUM_MASK; dwc2_writel(greset, hsotg->regs + GRSTCTL); - do { - greset = dwc2_readl(hsotg->regs + GRSTCTL); - if (++count > 10000) { - dev_warn(hsotg->dev, - "%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n", - __func__, greset, - dwc2_readl(hsotg->regs + GNPTXSTS)); - break; - } - udelay(1); - } while (greset & GRSTCTL_TXFFLSH); + if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 10000)) + dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_TXFFLSH\n", + __func__); /* Wait for at least 3 PHY Clocks */ udelay(1); @@ -715,43 +873,26 @@ void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num) void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg) { u32 greset; - int count = 0; dev_vdbg(hsotg->dev, "%s()\n", __func__); + /* Wait for AHB master IDLE state */ + if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, GRSTCTL_AHBIDLE, 10000)) + dev_warn(hsotg->dev, "%s: HANG! AHB Idle GRSCTL\n", + __func__); + greset = GRSTCTL_RXFFLSH; dwc2_writel(greset, hsotg->regs + GRSTCTL); - do { - greset = dwc2_readl(hsotg->regs + GRSTCTL); - if (++count > 10000) { - dev_warn(hsotg->dev, "%s() HANG! GRSTCTL=%0x\n", - __func__, greset); - break; - } - udelay(1); - } while (greset & GRSTCTL_RXFFLSH); + /* Wait for RxFIFO flush done */ + if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_RXFFLSH, 10000)) + dev_warn(hsotg->dev, "%s: HANG! timeout GRSTCTL GRSTCTL_RXFFLSH\n", + __func__); /* Wait for at least 3 PHY Clocks */ udelay(1); } -/* - * Forces either host or device mode if the controller is not - * currently in that mode. - * - * Returns true if the mode was forced. - */ -bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host) -{ - if (host && dwc2_is_host_mode(hsotg)) - return false; - else if (!host && dwc2_is_device_mode(hsotg)) - return false; - - return dwc2_force_mode(hsotg, host); -} - bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg) { if (dwc2_readl(hsotg->regs + GSNPSID) == 0xffffffff) @@ -825,6 +966,52 @@ bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg) (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE); } +/** + * dwc2_hsotg_wait_bit_set - Waits for bit to be set. + * @hsotg: Programming view of DWC_otg controller. + * @offset: Register's offset where bit/bits must be set. + * @mask: Mask of the bit/bits which must be set. + * @timeout: Timeout to wait. + * + * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout. + */ +int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hsotg, u32 offset, u32 mask, + u32 timeout) +{ + u32 i; + + for (i = 0; i < timeout; i++) { + if (dwc2_readl(hsotg->regs + offset) & mask) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +/** + * dwc2_hsotg_wait_bit_clear - Waits for bit to be clear. + * @hsotg: Programming view of DWC_otg controller. + * @offset: Register's offset where bit/bits must be set. + * @mask: Mask of the bit/bits which must be set. + * @timeout: Timeout to wait. + * + * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout. + */ +int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hsotg, u32 offset, u32 mask, + u32 timeout) +{ + u32 i; + + for (i = 0; i < timeout; i++) { + if (!(dwc2_readl(hsotg->regs + offset) & mask)) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + MODULE_DESCRIPTION("DESIGNWARE HS OTG Core"); MODULE_AUTHOR("Synopsys, Inc."); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index cd77af3b1565..d83be5651f87 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -217,7 +217,7 @@ struct dwc2_hsotg_ep { unsigned char dir_in; unsigned char index; unsigned char mc; - unsigned char interval; + u16 interval; unsigned int halted:1; unsigned int periodic:1; @@ -408,7 +408,7 @@ enum dwc2_ep0_state { * @ahbcfg: This field allows the default value of the GAHBCFG * register to be overridden * -1 - GAHBCFG value will be set to 0x06 - * (INCR4, default) + * (INCR, default) * all others - GAHBCFG value will be overridden with * this value * Not all bits can be controlled like this, the @@ -421,12 +421,26 @@ enum dwc2_ep0_state { * case. * 0 - No (default) * 1 - Yes - * @hibernation: Specifies whether the controller support hibernation. - * If hibernation is enabled, the controller will enter - * hibernation in both peripheral and host mode when + * @power_down: Specifies whether the controller support power_down. + * If power_down is enabled, the controller will enter + * power_down in both peripheral and host mode when * needed. * 0 - No (default) + * 1 - Partial power down + * 2 - Hibernation + * @lpm: Enable LPM support. + * 0 - No * 1 - Yes + * @lpm_clock_gating: Enable core PHY clock gating. + * 0 - No + * 1 - Yes + * @besl: Enable LPM Errata support. + * 0 - No + * 1 - Yes + * @hird_threshold_en: HIRD or HIRD Threshold enable. + * 0 - No + * 1 - Yes + * @hird_threshold: Value of BESL or HIRD Threshold. * @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO * register. * 0 - Deactivate the transceiver (default) @@ -479,12 +493,23 @@ struct dwc2_core_params { bool enable_dynamic_fifo; bool en_multiple_tx_fifo; bool i2c_enable; + bool acg_enable; bool ulpi_fs_ls; bool ts_dline; bool reload_ctl; bool uframe_sched; bool external_id_pin_ctl; - bool hibernation; + + int power_down; +#define DWC2_POWER_DOWN_PARAM_NONE 0 +#define DWC2_POWER_DOWN_PARAM_PARTIAL 1 +#define DWC2_POWER_DOWN_PARAM_HIBERNATION 2 + + bool lpm; + bool lpm_clock_gating; + bool besl; + bool hird_threshold_en; + u8 hird_threshold; bool activate_stm_fs_transceiver; u16 max_packet_count; u32 max_transfer_size; @@ -560,6 +585,7 @@ struct dwc2_core_params { * 2 - FS pins shared with UTMI+ pins * 3 - FS pins shared with ULPI pins * @total_fifo_size: Total internal RAM for FIFOs (bytes) + * @hibernation Is hibernation enabled? * @utmi_phy_data_width UTMI+ PHY data width * 0 - 8 bits * 1 - 16 bits @@ -587,12 +613,15 @@ struct dwc2_hw_params { unsigned hs_phy_type:2; unsigned fs_phy_type:2; unsigned i2c_enable:1; + unsigned acg_enable:1; unsigned num_dev_ep:4; unsigned num_dev_in_eps : 4; unsigned num_dev_perio_in_ep:4; unsigned total_fifo_size:16; unsigned power_optimized:1; + unsigned hibernation:1; unsigned utmi_phy_data_width:2; + unsigned lpm_mode:1; u32 snpsid; u32 dev_ep_dirs; u32 g_tx_fifo_size[MAX_EPS_CHANNELS]; @@ -611,9 +640,8 @@ struct dwc2_hw_params { * @grxfsiz: Backup of GRXFSIZ register * @gnptxfsiz: Backup of GNPTXFSIZ register * @gi2cctl: Backup of GI2CCTL register - * @hptxfsiz: Backup of HPTXFSIZ register + * @glpmcfg: Backup of GLPMCFG register * @gdfifocfg: Backup of GDFIFOCFG register - * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint * @gpwrdn: Backup of GPWRDN register */ struct dwc2_gregs_backup { @@ -624,10 +652,10 @@ struct dwc2_gregs_backup { u32 grxfsiz; u32 gnptxfsiz; u32 gi2cctl; - u32 hptxfsiz; + u32 glpmcfg; u32 pcgcctl; + u32 pcgcctl1; u32 gdfifocfg; - u32 dtxfsiz[MAX_EPS_CHANNELS]; u32 gpwrdn; bool valid; }; @@ -646,6 +674,7 @@ struct dwc2_gregs_backup { * @doepctl: Backup of DOEPCTL register * @doeptsiz: Backup of DOEPTSIZ register * @doepdma: Backup of DOEPDMA register + * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint */ struct dwc2_dregs_backup { u32 dcfg; @@ -659,6 +688,7 @@ struct dwc2_dregs_backup { u32 doepctl[MAX_EPS_CHANNELS]; u32 doeptsiz[MAX_EPS_CHANNELS]; u32 doepdma[MAX_EPS_CHANNELS]; + u32 dtxfsiz[MAX_EPS_CHANNELS]; bool valid; }; @@ -670,6 +700,7 @@ struct dwc2_dregs_backup { * @hcintmsk: Backup of HCINTMSK register * @hptr0: Backup of HPTR0 register * @hfir: Backup of HFIR register + * @hptxfsiz: Backup of HPTXFSIZ register */ struct dwc2_hregs_backup { u32 hcfg; @@ -677,6 +708,7 @@ struct dwc2_hregs_backup { u32 hcintmsk[MAX_EPS_CHANNELS]; u32 hprt0; u32 hfir; + u32 hptxfsiz; bool valid; }; @@ -780,12 +812,14 @@ struct dwc2_hregs_backup { * @hcd_enabled Host mode sub-driver initialization indicator. * @gadget_enabled Peripheral mode sub-driver initialization indicator. * @ll_hw_enabled Status of low-level hardware resources. + * @hibernated: True if core is hibernated * @phy: The otg phy transceiver structure for phy control. * @uphy: The otg phy transceiver structure for old USB phy * control. * @plat: The platform specific configuration data. This can be * removed once all SoCs support usb transceiver. * @supplies: Definition of USB power supplies + * @vbus_supply: Regulator supplying vbus. * @phyif: PHY interface width * @lock: Spinlock that protects all the driver data structures * @priv: Stores a pointer to the struct usb_hcd @@ -897,6 +931,8 @@ struct dwc2_hregs_backup { * @ctrl_req: Request for EP0 control packets. * @ep0_state: EP0 control transfers state * @test_mode: USB test mode requested by the host + * @remote_wakeup_allowed: True if device is allowed to wake-up host by + * remote-wakeup signalling * @setup_desc_dma: EP0 setup stage desc chain DMA address * @setup_desc: EP0 setup stage desc chain pointer * @ctrl_in_desc_dma: EP0 IN data phase desc chain DMA address @@ -917,11 +953,13 @@ struct dwc2_hsotg { unsigned int hcd_enabled:1; unsigned int gadget_enabled:1; unsigned int ll_hw_enabled:1; + unsigned int hibernated:1; struct phy *phy; struct usb_phy *uphy; struct dwc2_hsotg_plat *plat; struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES]; + struct regulator *vbus_supply; u32 phyif; spinlock_t lock; @@ -947,6 +985,7 @@ struct dwc2_hsotg { /* DWC OTG HW Release versions */ #define DWC2_CORE_REV_2_71a 0x4f54271a +#define DWC2_CORE_REV_2_80a 0x4f54280a #define DWC2_CORE_REV_2_90a 0x4f54290a #define DWC2_CORE_REV_2_91a 0x4f54291a #define DWC2_CORE_REV_2_92a 0x4f54292a @@ -956,6 +995,11 @@ struct dwc2_hsotg { #define DWC2_FS_IOT_REV_1_00a 0x5531100a #define DWC2_HS_IOT_REV_1_00a 0x5532100a + /* DWC OTG HW Core ID */ +#define DWC2_OTG_ID 0x4f540000 +#define DWC2_FS_IOT_ID 0x55310000 +#define DWC2_HS_IOT_ID 0x55320000 + #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) union dwc2_hcd_internal_flags { u32 d32; @@ -1016,24 +1060,6 @@ struct dwc2_hsotg { struct kmem_cache *desc_gen_cache; struct kmem_cache *desc_hsisoc_cache; -#ifdef DEBUG - u32 frrem_samples; - u64 frrem_accum; - - u32 hfnum_7_samples_a; - u64 hfnum_7_frrem_accum_a; - u32 hfnum_0_samples_a; - u64 hfnum_0_frrem_accum_a; - u32 hfnum_other_samples_a; - u64 hfnum_other_frrem_accum_a; - - u32 hfnum_7_samples_b; - u64 hfnum_7_frrem_accum_b; - u32 hfnum_0_samples_b; - u64 hfnum_0_frrem_accum_b; - u32 hfnum_other_samples_b; - u64 hfnum_other_frrem_accum_b; -#endif #endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */ #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ @@ -1062,6 +1088,7 @@ struct dwc2_hsotg { struct usb_gadget gadget; unsigned int enabled:1; unsigned int connected:1; + unsigned int remote_wakeup_allowed:1; struct dwc2_hsotg_ep *eps_in[MAX_EPS_CHANNELS]; struct dwc2_hsotg_ep *eps_out[MAX_EPS_CHANNELS]; #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */ @@ -1106,12 +1133,13 @@ static inline bool dwc2_is_hs_iot(struct dwc2_hsotg *hsotg) * and the DWC_otg controller */ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait); -int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg); -int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg); -int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore); +int dwc2_enter_partial_power_down(struct dwc2_hsotg *hsotg); +int dwc2_exit_partial_power_down(struct dwc2_hsotg *hsotg, bool restore); +int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg, int is_host); +int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, + int reset, int is_host); -bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host); -void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg); +void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host); void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg); bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg); @@ -1128,6 +1156,13 @@ void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg); void dwc2_enable_global_interrupts(struct dwc2_hsotg *hcd); void dwc2_disable_global_interrupts(struct dwc2_hsotg *hcd); +void dwc2_hib_restore_common(struct dwc2_hsotg *hsotg, int rem_wakeup, + int is_host); +int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg); +int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg); + +void dwc2_enable_acg(struct dwc2_hsotg *hsotg); + /* This function should be called on every hardware interrupt. */ irqreturn_t dwc2_handle_common_intr(int irq, void *dev); @@ -1137,6 +1172,11 @@ extern const struct of_device_id dwc2_of_match_table[]; int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg); int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg); +/* Common polling functions */ +int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit, + u32 timeout); +int dwc2_hsotg_wait_bit_clear(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit, + u32 timeout); /* Parameters */ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); int dwc2_init_params(struct dwc2_hsotg *hsotg); @@ -1180,7 +1220,7 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg); int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg); int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2); int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2); -int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq); +int dwc2_gadget_init(struct dwc2_hsotg *hsotg); void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset); void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg); @@ -1188,10 +1228,14 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2); int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); #define dwc2_is_device_connected(hsotg) (hsotg->connected) int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg); -int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg); +int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup); +int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg); +int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset); int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg); 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); #else static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1199,7 +1243,7 @@ static inline int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2) { return 0; } static inline int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2) { return 0; } -static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) +static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset) {} @@ -1211,7 +1255,13 @@ static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, #define dwc2_is_device_connected(hsotg) (0) static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) { return 0; } -static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, + int remote_wakeup) +{ return 0; } +static inline int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset) { return 0; } static inline int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg) { return 0; } @@ -1219,6 +1269,7 @@ static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) { return 0; } 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) {} #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) @@ -1227,8 +1278,12 @@ int dwc2_hcd_get_future_frame_number(struct dwc2_hsotg *hsotg, int us); void dwc2_hcd_connect(struct dwc2_hsotg *hsotg); void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force); void dwc2_hcd_start(struct dwc2_hsotg *hsotg); +int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup); int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg); int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg); +int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg); +int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset); #else static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { return 0; } @@ -1239,12 +1294,19 @@ static inline void dwc2_hcd_connect(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force) {} static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {} +static inline int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) +{ return 0; } static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) { return 0; } static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) { return 0; } +static inline int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset) +{ return 0; } #endif diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index ab3fa1630853..2982a155734d 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -321,10 +321,10 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) if (dwc2_is_device_mode(hsotg)) { if (hsotg->lx_state == DWC2_L2) { - ret = dwc2_exit_hibernation(hsotg, true); + ret = dwc2_exit_partial_power_down(hsotg, true); if (ret && (ret != -ENOTSUPP)) dev_err(hsotg->dev, - "exit hibernation failed\n"); + "exit power_down failed\n"); } /* @@ -335,6 +335,57 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) } } +/** + * dwc2_wakeup_from_lpm_l1 - Exit the device from LPM L1 state + * + * @hsotg: Programming view of DWC_otg controller + * + */ +static void dwc2_wakeup_from_lpm_l1(struct dwc2_hsotg *hsotg) +{ + u32 glpmcfg; + u32 i = 0; + + if (hsotg->lx_state != DWC2_L1) { + dev_err(hsotg->dev, "Core isn't in DWC2_L1 state\n"); + return; + } + + glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + if (dwc2_is_device_mode(hsotg)) { + dev_dbg(hsotg->dev, "Exit from L1 state\n"); + glpmcfg &= ~GLPMCFG_ENBLSLPM; + glpmcfg &= ~GLPMCFG_HIRD_THRES_EN; + dwc2_writel(glpmcfg, hsotg->regs + GLPMCFG); + + do { + glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + + if (!(glpmcfg & (GLPMCFG_COREL1RES_MASK | + GLPMCFG_L1RESUMEOK | GLPMCFG_SLPSTS))) + break; + + udelay(1); + } while (++i < 200); + + if (i == 200) { + dev_err(hsotg->dev, "Failed to exit L1 sleep state in 200us.\n"); + return; + } + dwc2_gadget_init_lpm(hsotg); + } else { + /* TODO */ + dev_err(hsotg->dev, "Host side LPM is not supported.\n"); + return; + } + + /* Change to L0 state */ + hsotg->lx_state = DWC2_L0; + + /* Inform gadget to exit from L1 */ + call_gadget(hsotg, resume); +} + /* * This interrupt indicates that the DWC_otg controller has detected a * resume or remote wakeup sequence. If the DWC_otg controller is in @@ -352,6 +403,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n"); dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); + if (hsotg->lx_state == DWC2_L1) { + dwc2_wakeup_from_lpm_l1(hsotg); + return; + } + if (dwc2_is_device_mode(hsotg)) { dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dwc2_readl(hsotg->regs + DSTS)); @@ -361,16 +417,16 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Clear Remote Wakeup Signaling */ dctl &= ~DCTL_RMTWKUPSIG; dwc2_writel(dctl, hsotg->regs + DCTL); - ret = dwc2_exit_hibernation(hsotg, true); + ret = dwc2_exit_partial_power_down(hsotg, true); if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit hibernation failed\n"); + dev_err(hsotg->dev, "exit power_down failed\n"); call_gadget(hsotg, resume); } /* Change to L0 state */ hsotg->lx_state = DWC2_L0; } else { - if (hsotg->params.hibernation) + if (hsotg->params.power_down) return; if (hsotg->lx_state != DWC2_L1) { @@ -428,32 +484,44 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) * state is active */ dsts = dwc2_readl(hsotg->regs + DSTS); - dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts); + dev_dbg(hsotg->dev, "%s: DSTS=0x%0x\n", __func__, dsts); dev_dbg(hsotg->dev, - "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", + "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d HWCFG4.Hibernation=%d\n", !!(dsts & DSTS_SUSPSTS), - hsotg->hw_params.power_optimized); - if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) { - /* Ignore suspend request before enumeration */ - if (!dwc2_is_device_connected(hsotg)) { - dev_dbg(hsotg->dev, - "ignore suspend request before enumeration\n"); - return; + hsotg->hw_params.power_optimized, + hsotg->hw_params.hibernation); + + /* Ignore suspend request before enumeration */ + if (!dwc2_is_device_connected(hsotg)) { + dev_dbg(hsotg->dev, + "ignore suspend request before enumeration\n"); + return; + } + if (dsts & DSTS_SUSPSTS) { + if (hsotg->hw_params.power_optimized) { + ret = dwc2_enter_partial_power_down(hsotg); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "%s: enter partial_power_down failed\n", + __func__); + goto skip_power_saving; + } + + udelay(100); + + /* Ask phy to be suspended */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) + usb_phy_set_suspend(hsotg->uphy, true); } - ret = dwc2_enter_hibernation(hsotg); - if (ret) { - if (ret != -ENOTSUPP) + if (hsotg->hw_params.hibernation) { + ret = dwc2_enter_hibernation(hsotg, 0); + if (ret && ret != -ENOTSUPP) dev_err(hsotg->dev, - "enter hibernation failed\n"); - goto skip_power_saving; + "%s: enter hibernation failed\n", + __func__); } - - udelay(100); - - /* Ask phy to be suspended */ - if (!IS_ERR_OR_NULL(hsotg->uphy)) - usb_phy_set_suspend(hsotg->uphy, true); skip_power_saving: /* * Change to L2 (suspend) state before releasing @@ -479,10 +547,75 @@ skip_power_saving: } } +/** + * dwc2_handle_lpm_intr - GINTSTS_LPMTRANRCVD Interrupt handler + * + * @hsotg: Programming view of DWC_otg controller + * + */ +static void dwc2_handle_lpm_intr(struct dwc2_hsotg *hsotg) +{ + u32 glpmcfg; + u32 pcgcctl; + u32 hird; + u32 hird_thres; + u32 hird_thres_en; + u32 enslpm; + + /* Clear interrupt */ + dwc2_writel(GINTSTS_LPMTRANRCVD, hsotg->regs + GINTSTS); + + glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + + if (!(glpmcfg & GLPMCFG_LPMCAP)) { + dev_err(hsotg->dev, "Unexpected LPM interrupt\n"); + return; + } + + hird = (glpmcfg & GLPMCFG_HIRD_MASK) >> GLPMCFG_HIRD_SHIFT; + hird_thres = (glpmcfg & GLPMCFG_HIRD_THRES_MASK & + ~GLPMCFG_HIRD_THRES_EN) >> GLPMCFG_HIRD_THRES_SHIFT; + hird_thres_en = glpmcfg & GLPMCFG_HIRD_THRES_EN; + enslpm = glpmcfg & GLPMCFG_ENBLSLPM; + + if (dwc2_is_device_mode(hsotg)) { + dev_dbg(hsotg->dev, "HIRD_THRES_EN = %d\n", hird_thres_en); + + if (hird_thres_en && hird >= hird_thres) { + dev_dbg(hsotg->dev, "L1 with utmi_l1_suspend_n\n"); + } else if (enslpm) { + dev_dbg(hsotg->dev, "L1 with utmi_sleep_n\n"); + } else { + dev_dbg(hsotg->dev, "Entering Sleep with L1 Gating\n"); + + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl |= PCGCTL_ENBL_SLEEP_GATING; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + } + /** + * Examine prt_sleep_sts after TL1TokenTetry period max (10 us) + */ + udelay(10); + + glpmcfg = dwc2_readl(hsotg->regs + GLPMCFG); + + if (glpmcfg & GLPMCFG_SLPSTS) { + /* Save the current state */ + hsotg->lx_state = DWC2_L1; + dev_dbg(hsotg->dev, + "Core is in L1 sleep glpmcfg=%08x\n", glpmcfg); + + /* Inform gadget that we are in L1 state */ + call_gadget(hsotg, suspend); + } + } +} + #define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \ GINTSTS_CONIDSTSCHNG | GINTSTS_OTGINT | \ GINTSTS_MODEMIS | GINTSTS_DISCONNINT | \ - GINTSTS_USBSUSP | GINTSTS_PRTINT) + GINTSTS_USBSUSP | GINTSTS_PRTINT | \ + GINTSTS_LPMTRANRCVD) /* * This function returns the Core Interrupt register @@ -510,6 +643,116 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg) } /* + * GPWRDN interrupt handler. + * + * The GPWRDN interrupts are those that occur in both Host and + * Device mode while core is in hibernated state. + */ +static void dwc2_handle_gpwrdn_intr(struct dwc2_hsotg *hsotg) +{ + u32 gpwrdn; + int linestate; + + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + /* clear all interrupt */ + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + linestate = (gpwrdn & GPWRDN_LINESTATE_MASK) >> GPWRDN_LINESTATE_SHIFT; + dev_dbg(hsotg->dev, + "%s: dwc2_handle_gpwrdwn_intr called gpwrdn= %08x\n", __func__, + gpwrdn); + + if ((gpwrdn & GPWRDN_DISCONN_DET) && + (gpwrdn & GPWRDN_DISCONN_DET_MSK) && !linestate) { + u32 gpwrdn_tmp; + + dev_dbg(hsotg->dev, "%s: GPWRDN_DISCONN_DET\n", __func__); + + /* Switch-on voltage to the core */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNSWTCH; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + udelay(10); + + /* Reset core */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNRSTN; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + udelay(10); + + /* Disable Power Down Clamp */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PWRDNCLMP; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + udelay(10); + + /* Deassert reset core */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp |= GPWRDN_PWRDNRSTN; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + udelay(10); + + /* Disable PMU interrupt */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PMUINTSEL; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + + /* De-assert Wakeup Logic */ + gpwrdn_tmp = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn_tmp &= ~GPWRDN_PMUACTV; + dwc2_writel(gpwrdn_tmp, hsotg->regs + GPWRDN); + + hsotg->hibernated = 0; + + if (gpwrdn & GPWRDN_IDSTS) { + hsotg->op_state = OTG_STATE_B_PERIPHERAL; + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hsotg_core_init_disconnected(hsotg, false); + dwc2_hsotg_core_connect(hsotg); + } else { + hsotg->op_state = OTG_STATE_A_HOST; + + /* Initialize the Core for Host mode */ + dwc2_core_init(hsotg, false); + dwc2_enable_global_interrupts(hsotg); + dwc2_hcd_start(hsotg); + } + } + + if ((gpwrdn & GPWRDN_LNSTSCHG) && + (gpwrdn & GPWRDN_LNSTSCHG_MSK) && linestate) { + dev_dbg(hsotg->dev, "%s: GPWRDN_LNSTSCHG\n", __func__); + if (hsotg->hw_params.hibernation && + hsotg->hibernated) { + if (gpwrdn & GPWRDN_IDSTS) { + dwc2_exit_hibernation(hsotg, 0, 0, 0); + call_gadget(hsotg, resume); + } else { + dwc2_exit_hibernation(hsotg, 1, 0, 1); + } + } + } + if ((gpwrdn & GPWRDN_RST_DET) && (gpwrdn & GPWRDN_RST_DET_MSK)) { + dev_dbg(hsotg->dev, "%s: GPWRDN_RST_DET\n", __func__); + if (!linestate && (gpwrdn & GPWRDN_BSESSVLD)) + dwc2_exit_hibernation(hsotg, 0, 1, 0); + } + if ((gpwrdn & GPWRDN_STS_CHGINT) && + (gpwrdn & GPWRDN_STS_CHGINT_MSK) && linestate) { + dev_dbg(hsotg->dev, "%s: GPWRDN_STS_CHGINT\n", __func__); + if (hsotg->hw_params.hibernation && + hsotg->hibernated) { + if (gpwrdn & GPWRDN_IDSTS) { + dwc2_exit_hibernation(hsotg, 0, 0, 0); + call_gadget(hsotg, resume); + } else { + dwc2_exit_hibernation(hsotg, 1, 0, 1); + } + } + } +} + +/* * Common interrupt handler * * The common interrupts are those that occur in both Host and Device mode. @@ -539,6 +782,13 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) if (gintsts & ~GINTSTS_PRTINT) retval = IRQ_HANDLED; + /* In case of hibernated state gintsts must not work */ + if (hsotg->hibernated) { + dwc2_handle_gpwrdn_intr(hsotg); + retval = IRQ_HANDLED; + goto out; + } + if (gintsts & GINTSTS_MODEMIS) dwc2_handle_mode_mismatch_intr(hsotg); if (gintsts & GINTSTS_OTGINT) @@ -553,6 +803,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) dwc2_handle_wakeup_detected_intr(hsotg); if (gintsts & GINTSTS_USBSUSP) dwc2_handle_usb_suspend_intr(hsotg); + if (gintsts & GINTSTS_LPMTRANRCVD) + dwc2_handle_lpm_intr(hsotg); if (gintsts & GINTSTS_PRTINT) { /* diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index f4650a88be78..58c691f882a8 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -170,19 +170,7 @@ static int state_show(struct seq_file *seq, void *v) return 0; } - -static int state_open(struct inode *inode, struct file *file) -{ - return single_open(file, state_show, inode->i_private); -} - -static const struct file_operations state_fops = { - .owner = THIS_MODULE, - .open = state_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(state); /** * fifo_show - debugfs: show the fifo information @@ -219,19 +207,7 @@ static int fifo_show(struct seq_file *seq, void *v) return 0; } - -static int fifo_open(struct inode *inode, struct file *file) -{ - return single_open(file, fifo_show, inode->i_private); -} - -static const struct file_operations fifo_fops = { - .owner = THIS_MODULE, - .open = fifo_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(fifo); static const char *decode_direction(int is_in) { @@ -303,19 +279,7 @@ static int ep_show(struct seq_file *seq, void *v) return 0; } - -static int ep_open(struct inode *inode, struct file *file) -{ - return single_open(file, ep_show, inode->i_private); -} - -static const struct file_operations ep_fops = { - .owner = THIS_MODULE, - .open = ep_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(ep); /** * dwc2_hsotg_create_debug - create debugfs directory and files @@ -754,7 +718,12 @@ static int params_show(struct seq_file *seq, void *v) print_param_hex(seq, p, ahbcfg); print_param(seq, p, uframe_sched); print_param(seq, p, external_id_pin_ctl); - print_param(seq, p, hibernation); + print_param(seq, p, power_down); + print_param(seq, p, lpm); + print_param(seq, p, lpm_clock_gating); + print_param(seq, p, besl); + print_param(seq, p, hird_threshold_en); + print_param(seq, p, hird_threshold); print_param(seq, p, host_dma); print_param(seq, p, g_dma); print_param(seq, p, g_dma_desc); @@ -770,19 +739,7 @@ static int params_show(struct seq_file *seq, void *v) return 0; } - -static int params_open(struct inode *inode, struct file *file) -{ - return single_open(file, params_show, inode->i_private); -} - -static const struct file_operations params_fops = { - .owner = THIS_MODULE, - .open = params_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(params); static int hw_params_show(struct seq_file *seq, void *v) { @@ -817,19 +774,7 @@ static int hw_params_show(struct seq_file *seq, void *v) return 0; } - -static int hw_params_open(struct inode *inode, struct file *file) -{ - return single_open(file, hw_params_show, inode->i_private); -} - -static const struct file_operations hw_params_fops = { - .owner = THIS_MODULE, - .open = hw_params_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(hw_params); static int dr_mode_show(struct seq_file *seq, void *v) { @@ -840,19 +785,7 @@ static int dr_mode_show(struct seq_file *seq, void *v) seq_printf(seq, "%s\n", dr_mode); return 0; } - -static int dr_mode_open(struct inode *inode, struct file *file) -{ - return single_open(file, dr_mode_show, inode->i_private); -} - -static const struct file_operations dr_mode_fops = { - .owner = THIS_MODULE, - .open = dr_mode_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(dr_mode); int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) { diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 5bcad1d869b5..6c32bf26e48e 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -47,12 +47,12 @@ static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget) return container_of(gadget, struct dwc2_hsotg, gadget); } -static inline void __orr32(void __iomem *ptr, u32 val) +static inline void dwc2_set_bit(void __iomem *ptr, u32 val) { dwc2_writel(dwc2_readl(ptr) | val, ptr); } -static inline void __bic32(void __iomem *ptr, u32 val) +static inline void dwc2_clear_bit(void __iomem *ptr, u32 val) { dwc2_writel(dwc2_readl(ptr) & ~val, ptr); } @@ -116,10 +116,10 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep) { hs_ep->target_frame += hs_ep->interval; if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) { - hs_ep->frame_overrun = 1; + hs_ep->frame_overrun = true; hs_ep->target_frame &= DSTS_SOFFN_LIMIT; } else { - hs_ep->frame_overrun = 0; + hs_ep->frame_overrun = false; } } @@ -252,6 +252,7 @@ static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) unsigned int ep; unsigned int addr; int timeout; + u32 val; u32 *txfsz = hsotg->params.g_tx_fifo_size; @@ -1296,8 +1297,8 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, req->zero, req->short_not_ok); /* Prevent new request submission when controller is suspended */ - if (hs->lx_state == DWC2_L2) { - dev_dbg(hs->dev, "%s: don't submit request while suspended\n", + if (hs->lx_state != DWC2_L0) { + dev_dbg(hs->dev, "%s: submit request only in active state\n", __func__); return -EAGAIN; } @@ -1639,6 +1640,10 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, switch (recip) { case USB_RECIP_DEVICE: switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + hsotg->remote_wakeup_allowed = 1; + break; + case USB_DEVICE_TEST_MODE: if ((wIndex & 0xff) != 0) return -EINVAL; @@ -2495,30 +2500,13 @@ bad_mps: */ static void dwc2_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx) { - int timeout; - int val; - dwc2_writel(GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH, hsotg->regs + GRSTCTL); /* wait until the fifo is flushed */ - timeout = 100; - - while (1) { - val = dwc2_readl(hsotg->regs + GRSTCTL); - - if ((val & (GRSTCTL_TXFFLSH)) == 0) - break; - - if (--timeout == 0) { - dev_err(hsotg->dev, - "%s: timeout flushing fifo (GRSTCTL=%08x)\n", - __func__, val); - break; - } - - udelay(1); - } + if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_TXFFLSH, 100)) + dev_warn(hsotg->dev, "%s: timeout flushing fifo GRSTCTL_TXFFLSH\n", + __func__); } /** @@ -3253,7 +3241,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_hsotg_init_fifo(hsotg); if (!is_usb_reset) - __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); dcfg |= DCFG_EPMISCNT(1); @@ -3282,7 +3270,8 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | GINTSTS_USBRST | GINTSTS_RESETDET | GINTSTS_ENUMDONE | GINTSTS_OTGINT | - GINTSTS_USBSUSP | GINTSTS_WKUPINT; + GINTSTS_USBSUSP | GINTSTS_WKUPINT | + GINTSTS_LPMTRANRCVD; if (!using_desc_dma(hsotg)) intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT; @@ -3294,12 +3283,12 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, if (using_dma(hsotg)) { dwc2_writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN | - (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT), + hsotg->params.ahbcfg, hsotg->regs + GAHBCFG); /* Set DDMA mode support in the core if needed */ if (using_desc_dma(hsotg)) - __orr32(hsotg->regs + DCFG, DCFG_DESCDMA_EN); + dwc2_set_bit(hsotg->regs + DCFG, DCFG_DESCDMA_EN); } else { dwc2_writel(((hsotg->dedicated_fifos) ? @@ -3332,7 +3321,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, /* Enable BNA interrupt for DDMA */ if (using_desc_dma(hsotg)) - __orr32(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK); + dwc2_set_bit(hsotg->regs + DOEPMSK, DOEPMSK_BNAMSK); dwc2_writel(0, hsotg->regs + DAINTMSK); @@ -3356,9 +3345,9 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_hsotg_ctrl_epint(hsotg, 0, 1, 1); if (!is_usb_reset) { - __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); udelay(10); /* see openiboot */ - __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); + dwc2_clear_bit(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); } dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg->regs + DCTL)); @@ -3385,7 +3374,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, val = DCTL_CGOUTNAK | DCTL_CGNPINNAK; if (!is_usb_reset) val |= DCTL_SFTDISCON; - __orr32(hsotg->regs + DCTL, val); + dwc2_set_bit(hsotg->regs + DCTL, val); + + /* configure the core to support LPM */ + dwc2_gadget_init_lpm(hsotg); /* must be at-least 3ms to allow bus to see disconnect */ mdelay(3); @@ -3402,13 +3394,13 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) { /* set the soft-disconnect bit */ - __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); } void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) { /* remove the soft-disconnect and let's go */ - __bic32(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_clear_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); } /** @@ -3428,14 +3420,21 @@ static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg) { struct dwc2_hsotg_ep *hs_ep; u32 epctrl; + u32 daintmsk; u32 idx; dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n"); + daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + for (idx = 1; idx <= hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_in[idx]; + /* Proceed only unmasked ISOC EPs */ + if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) + continue; + epctrl = dwc2_readl(hsotg->regs + DIEPCTL(idx)); - if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous && + if ((epctrl & DXEPCTL_EPENA) && dwc2_gadget_target_frame_elapsed(hs_ep)) { epctrl |= DXEPCTL_SNAK; epctrl |= DXEPCTL_EPDIS; @@ -3464,16 +3463,24 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg) { u32 gintsts; u32 gintmsk; + u32 daintmsk; u32 epctrl; struct dwc2_hsotg_ep *hs_ep; int idx; dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__); + daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + daintmsk >>= DAINT_OUTEP_SHIFT; + for (idx = 1; idx <= hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_out[idx]; + /* Proceed only unmasked ISOC EPs */ + if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) + continue; + epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx)); - if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous && + if ((epctrl & DXEPCTL_EPENA) && dwc2_gadget_target_frame_elapsed(hs_ep)) { /* Unmask GOUTNAKEFF interrupt */ gintmsk = dwc2_readl(hsotg->regs + GINTMSK); @@ -3481,8 +3488,10 @@ static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg) dwc2_writel(gintmsk, hsotg->regs + GINTMSK); gintsts = dwc2_readl(hsotg->regs + GINTSTS); - if (!(gintsts & GINTSTS_GOUTNAKEFF)) - __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK); + if (!(gintsts & GINTSTS_GOUTNAKEFF)) { + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK); + break; + } } } @@ -3522,7 +3531,7 @@ irq_retry: /* This event must be used only if controller is suspended */ if (hsotg->lx_state == DWC2_L2) { - dwc2_exit_hibernation(hsotg, true); + dwc2_exit_partial_power_down(hsotg, true); hsotg->lx_state = DWC2_L0; } } @@ -3541,7 +3550,7 @@ irq_retry: dwc2_hsotg_disconnect(hsotg); /* Reset device address to zero */ - __bic32(hsotg->regs + DCFG, DCFG_DEVADDR_MASK); + dwc2_clear_bit(hsotg->regs + DCFG, DCFG_DEVADDR_MASK); if (usb_status & GOTGCTL_BSESVLD && connected) dwc2_hsotg_core_init_disconnected(hsotg, true); @@ -3627,8 +3636,11 @@ irq_retry: u8 idx; u32 epctrl; u32 gintmsk; + u32 daintmsk; struct dwc2_hsotg_ep *hs_ep; + daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + daintmsk >>= DAINT_OUTEP_SHIFT; /* Mask this interrupt */ gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk &= ~GINTSTS_GOUTNAKEFF; @@ -3637,9 +3649,13 @@ irq_retry: dev_dbg(hsotg->dev, "GOUTNakEff triggered\n"); for (idx = 1; idx <= hsotg->num_of_eps; idx++) { hs_ep = hsotg->eps_out[idx]; + /* Proceed only unmasked ISOC EPs */ + if (!hs_ep->isochronous || (BIT(idx) & ~daintmsk)) + continue; + epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx)); - if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) { + if (epctrl & DXEPCTL_EPENA) { epctrl |= DXEPCTL_SNAK; epctrl |= DXEPCTL_EPDIS; dwc2_writel(epctrl, hsotg->regs + DOEPCTL(idx)); @@ -3652,7 +3668,7 @@ irq_retry: if (gintsts & GINTSTS_GINNAKEFF) { dev_info(hsotg->dev, "GINNakEff triggered\n"); - __orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK); dwc2_hsotg_dump(hsotg); } @@ -3676,20 +3692,6 @@ irq_retry: return IRQ_HANDLED; } -static int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, - u32 bit, u32 timeout) -{ - u32 i; - - for (i = 0; i < timeout; i++) { - if (dwc2_readl(hs_otg->regs + reg) & bit) - return 0; - udelay(1); - } - - return -ETIMEDOUT; -} - static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, struct dwc2_hsotg_ep *hs_ep) { @@ -3706,7 +3708,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, if (hs_ep->dir_in) { if (hsotg->dedicated_fifos || hs_ep->periodic) { - __orr32(hsotg->regs + epctrl_reg, DXEPCTL_SNAK); + dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_SNAK); /* Wait for Nak effect */ if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_INEPNAKEFF, 100)) @@ -3714,7 +3716,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, "%s: timeout DIEPINT.NAKEFF\n", __func__); } else { - __orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGNPINNAK); /* Wait for Nak effect */ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, GINTSTS_GINNAKEFF, 100)) @@ -3724,7 +3726,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, } } else { if (!(dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_GOUTNAKEFF)) - __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SGOUTNAK); /* Wait for global nak to take effect */ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, @@ -3734,7 +3736,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, } /* Disable ep */ - __orr32(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); + dwc2_set_bit(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); /* Wait for ep to be disabled */ if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100)) @@ -3742,7 +3744,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, "%s: timeout DOEPCTL.EPDisable\n", __func__); /* Clear EPDISBLD interrupt */ - __orr32(hsotg->regs + epint_reg, DXEPINT_EPDISBLD); + dwc2_set_bit(hsotg->regs + epint_reg, DXEPINT_EPDISBLD); if (hs_ep->dir_in) { unsigned short fifo_index; @@ -3757,11 +3759,11 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, /* Clear Global In NP NAK in Shared FIFO for non periodic ep */ if (!hsotg->dedicated_fifos && !hs_ep->periodic) - __orr32(hsotg->regs + DCTL, DCTL_CGNPINNAK); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGNPINNAK); } else { /* Remove global NAKs */ - __orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_CGOUTNAK); } } @@ -4183,7 +4185,7 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) dwc2_writel(0, hsotg->regs + DAINTMSK); /* Be in disconnected state until gadget is registered */ - __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); + dwc2_set_bit(hsotg->regs + DCTL, DCTL_SFTDISCON); /* setup fifos */ @@ -4205,7 +4207,7 @@ static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); if (using_dma(hsotg)) - __orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN); + dwc2_set_bit(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN); } /** @@ -4352,6 +4354,8 @@ static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on) if (is_on) { hsotg->enabled = 1; dwc2_hsotg_core_init_disconnected(hsotg, false); + /* Enable ACG feature in device mode,if supported */ + dwc2_enable_acg(hsotg); dwc2_hsotg_core_connect(hsotg); } else { dwc2_hsotg_core_disconnect(hsotg); @@ -4374,18 +4378,21 @@ static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) spin_lock_irqsave(&hsotg->lock, flags); /* - * If controller is hibernated, it must exit from hibernation + * If controller is hibernated, it must exit from power_down * before being initialized / de-initialized */ if (hsotg->lx_state == DWC2_L2) - dwc2_exit_hibernation(hsotg, false); + dwc2_exit_partial_power_down(hsotg, false); if (is_active) { hsotg->op_state = OTG_STATE_B_PERIPHERAL; dwc2_hsotg_core_init_disconnected(hsotg, false); - if (hsotg->enabled) + if (hsotg->enabled) { + /* Enable ACG feature in device mode,if supported */ + dwc2_enable_acg(hsotg); dwc2_hsotg_core_connect(hsotg); + } } else { dwc2_hsotg_core_disconnect(hsotg); dwc2_hsotg_disconnect(hsotg); @@ -4606,9 +4613,8 @@ static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) /** * dwc2_gadget_init - init function for gadget * @dwc2: The data structure for the DWC2 driver. - * @irq: The IRQ number for the controller. */ -int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) +int dwc2_gadget_init(struct dwc2_hsotg *hsotg) { struct device *dev = hsotg->dev; int epnum; @@ -4622,6 +4628,11 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); + hsotg->remote_wakeup_allowed = 0; + + if (hsotg->params.lpm) + hsotg->gadget.lpm_capable = true; + if (hsotg->dr_mode == USB_DR_MODE_OTG) hsotg->gadget.is_otg = 1; else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) @@ -4649,8 +4660,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) return ret; } - ret = devm_request_irq(hsotg->dev, irq, dwc2_hsotg_irq, IRQF_SHARED, - dev_name(hsotg->dev), hsotg); + ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq, + IRQF_SHARED, dev_name(hsotg->dev), hsotg); if (ret < 0) { dev_err(dev, "cannot claim IRQ for gadget\n"); return ret; @@ -4751,8 +4762,11 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg) spin_lock_irqsave(&hsotg->lock, flags); dwc2_hsotg_core_init_disconnected(hsotg, false); - if (hsotg->enabled) + if (hsotg->enabled) { + /* Enable ACG feature in device mode,if supported */ + dwc2_enable_acg(hsotg); dwc2_hsotg_core_connect(hsotg); + } spin_unlock_irqrestore(&hsotg->lock, flags); } @@ -4806,6 +4820,7 @@ int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) dr->doeptsiz[i] = dwc2_readl(hsotg->regs + DOEPTSIZ(i)); dr->doepdma[i] = dwc2_readl(hsotg->regs + DOEPDMA(i)); + dr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i)); } dr->valid = true; return 0; @@ -4817,11 +4832,13 @@ 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. + * + * Return: 0 if successful, negative error code otherwise */ -int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg, int remote_wakeup) { struct dwc2_dregs_backup *dr; - u32 dctl; int i; dev_dbg(hsotg->dev, "%s\n", __func__); @@ -4835,28 +4852,240 @@ int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) } dr->valid = false; - dwc2_writel(dr->dcfg, hsotg->regs + DCFG); - dwc2_writel(dr->dctl, hsotg->regs + DCTL); + if (!remote_wakeup) + dwc2_writel(dr->dctl, hsotg->regs + DCTL); + dwc2_writel(dr->daintmsk, hsotg->regs + DAINTMSK); dwc2_writel(dr->diepmsk, hsotg->regs + DIEPMSK); dwc2_writel(dr->doepmsk, hsotg->regs + DOEPMSK); for (i = 0; i < hsotg->num_of_eps; i++) { /* Restore IN EPs */ - dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); dwc2_writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i)); dwc2_writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i)); - + dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); + /** WA for enabled EPx's IN in DDMA mode. On entering to + * hibernation wrong value read and saved from DIEPDMAx, + * as result BNA interrupt asserted on hibernation exit + * by restoring from saved area. + */ + if (hsotg->params.g_dma_desc && + (dr->diepctl[i] & DXEPCTL_EPENA)) + dr->diepdma[i] = hsotg->eps_in[i]->desc_list_dma; + dwc2_writel(dr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); + dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); /* Restore OUT EPs */ - dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); + /* WA for enabled EPx's OUT in DDMA mode. On entering to + * hibernation wrong value read and saved from DOEPDMAx, + * as result BNA interrupt asserted on hibernation exit + * by restoring from saved area. + */ + if (hsotg->params.g_dma_desc && + (dr->doepctl[i] & DXEPCTL_EPENA)) + dr->doepdma[i] = hsotg->eps_out[i]->desc_list_dma; dwc2_writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i)); + dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); } - /* Set the Power-On Programming done bit */ - dctl = dwc2_readl(hsotg->regs + DCTL); - dctl |= DCTL_PWRONPRGDONE; - dwc2_writel(dctl, hsotg->regs + DCTL); - return 0; } + +/** + * dwc2_gadget_init_lpm - Configure the core to support LPM in device mode + * + * @hsotg: Programming view of DWC_otg controller + * + */ +void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) +{ + u32 val; + + if (!hsotg->params.lpm) + return; + + val = GLPMCFG_LPMCAP | GLPMCFG_APPL1RES; + val |= hsotg->params.hird_threshold_en ? GLPMCFG_HIRD_THRES_EN : 0; + val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0; + val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT; + val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0; + dwc2_writel(val, hsotg->regs + GLPMCFG); + dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg->regs + + GLPMCFG)); +} + +/** + * dwc2_gadget_enter_hibernation() - Put controller in Hibernation. + * + * @hsotg: Programming view of the DWC_otg controller + * + * Return non-zero if failed to enter to hibernation. + */ +int dwc2_gadget_enter_hibernation(struct dwc2_hsotg *hsotg) +{ + u32 gpwrdn; + int ret = 0; + + /* 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__); + return ret; + } + + gpwrdn = GPWRDN_PWRDNRSTN; + gpwrdn |= GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Set flag to indicate that we are in hibernation */ + hsotg->hibernated = 1; + + /* Enable interrupts from wake up logic */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PMUINTSEL; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Unmask device mode interrupts in GPWRDN */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_RST_DET_MSK; + gpwrdn |= GPWRDN_LNSTSCHG_MSK; + gpwrdn |= GPWRDN_STS_CHGINT_MSK; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Enable Power Down Clamp */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNCLMP; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Switch off VDD */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNSWTCH; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Save gpwrdn register for further usage if stschng interrupt */ + hsotg->gr_backup.gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + dev_dbg(hsotg->dev, "Hibernation completed\n"); + + return ret; +} + +/** + * dwc2_gadget_exit_hibernation() + * This function is for exiting from Device mode hibernation by host initiated + * resume/reset and device initiated remote-wakeup. + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by Device or Host. + * @param reset: indicates whether resume is initiated by Reset. + * + * Return non-zero if failed to exit from hibernation. + */ +int dwc2_gadget_exit_hibernation(struct dwc2_hsotg *hsotg, + int rem_wakeup, int reset) +{ + u32 pcgcctl; + u32 gpwrdn; + u32 dctl; + int ret = 0; + struct dwc2_gregs_backup *gr; + struct dwc2_dregs_backup *dr; + + gr = &hsotg->gr_backup; + dr = &hsotg->dr_backup; + + if (!hsotg->hibernated) { + dev_dbg(hsotg->dev, "Already exited from Hibernation\n"); + return 1; + } + dev_dbg(hsotg->dev, + "%s: called with rem_wakeup = %d reset = %d\n", + __func__, rem_wakeup, reset); + + dwc2_hib_restore_common(hsotg, rem_wakeup, 0); + + if (!reset) { + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + } + + /* De-assert Restore */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_RESTORE; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + if (!rem_wakeup) { + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_RSTPDWNMODULE; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + } + + /* Restore GUSBCFG, DCFG and DCTL */ + dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(dr->dcfg, hsotg->regs + DCFG); + dwc2_writel(dr->dctl, hsotg->regs + DCTL); + + /* De-assert Wakeup Logic */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + + if (rem_wakeup) { + udelay(10); + /* Start Remote Wakeup Signaling */ + dwc2_writel(dr->dctl | DCTL_RMTWKUPSIG, hsotg->regs + DCTL); + } else { + udelay(50); + /* Set Device programming done bit */ + dctl = dwc2_readl(hsotg->regs + DCTL); + dctl |= DCTL_PWRONPRGDONE; + dwc2_writel(dctl, hsotg->regs + DCTL); + } + /* Wait for interrupts which must be cleared */ + mdelay(2); + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + 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__); + return ret; + } + + if (rem_wakeup) { + mdelay(10); + dctl = dwc2_readl(hsotg->regs + DCTL); + dctl &= ~DCTL_RMTWKUPSIG; + dwc2_writel(dctl, hsotg->regs + DCTL); + } + + hsotg->hibernated = 0; + hsotg->lx_state = DWC2_L0; + dev_dbg(hsotg->dev, "Hibernation recovery completes here\n"); + + return ret; +} diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index a5d72fcd1603..190f95964000 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -91,6 +91,9 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTSTS_SESSREQINT; + if (dwc2_is_device_mode(hsotg) && hsotg->params.lpm) + intmsk |= GINTSTS_LPMTRANRCVD; + dwc2_writel(intmsk, hsotg->regs + GINTMSK); } @@ -138,7 +141,7 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); /* Reset after a PHY select */ - retval = dwc2_core_reset_and_force_dr_mode(hsotg); + retval = dwc2_core_reset(hsotg, false); if (retval) { dev_err(hsotg->dev, @@ -236,7 +239,7 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); /* Reset after setting the PHY parameters */ - retval = dwc2_core_reset_and_force_dr_mode(hsotg); + retval = dwc2_core_reset(hsotg, false); if (retval) { dev_err(hsotg->dev, "%s: Reset failed, aborting", __func__); @@ -308,22 +311,10 @@ static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg) break; } - dev_dbg(hsotg->dev, "host_dma:%d dma_desc_enable:%d\n", - hsotg->params.host_dma, - hsotg->params.dma_desc_enable); - - if (hsotg->params.host_dma) { - if (hsotg->params.dma_desc_enable) - dev_dbg(hsotg->dev, "Using Descriptor DMA mode\n"); - else - dev_dbg(hsotg->dev, "Using Buffer DMA mode\n"); - } else { - dev_dbg(hsotg->dev, "Using Slave mode\n"); - hsotg->params.dma_desc_enable = false; - } - if (hsotg->params.host_dma) ahbcfg |= GAHBCFG_DMA_EN; + else + hsotg->params.dma_desc_enable = false; dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); @@ -365,6 +356,23 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); } +static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg) +{ + hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus"); + if (IS_ERR(hsotg->vbus_supply)) + return 0; + + return regulator_enable(hsotg->vbus_supply); +} + +static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg) +{ + if (hsotg->vbus_supply) + return regulator_disable(hsotg->vbus_supply); + + return 0; +} + /** * dwc2_enable_host_interrupts() - Enables the Host mode interrupts * @@ -989,6 +997,24 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "%s()\n", __func__); + + /* + * In buffer DMA or external DMA mode channel can't be halted + * for non-split periodic channels. At the end of the next + * uframe/frame (in the worst case), the core generates a channel + * halted and disables the channel automatically. + */ + if ((hsotg->params.g_dma && !hsotg->params.g_dma_desc) || + hsotg->hw_params.arch == GHWCFG2_EXT_DMA_ARCH) { + if (!chan->do_split && + (chan->ep_type == USB_ENDPOINT_XFER_ISOC || + chan->ep_type == USB_ENDPOINT_XFER_INT)) { + dev_err(hsotg->dev, "%s() Channel can't be halted\n", + __func__); + return; + } + } + if (halt_status == DWC2_HC_XFER_NO_HALT_STATUS) dev_err(hsotg->dev, "!!! halt_status = %d !!!\n", halt_status); @@ -2232,7 +2258,7 @@ static int dwc2_hcd_endpoint_reset(struct dwc2_hsotg *hsotg, * @hsotg: Programming view of the DWC_otg controller * @initial_setup: If true then this is the first init for this instance. */ -static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) +int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) { u32 usbcfg, otgctl; int retval; @@ -2261,7 +2287,7 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) * needed to in order to properly detect various parameters). */ if (!initial_setup) { - retval = dwc2_core_reset_and_force_dr_mode(hsotg); + retval = dwc2_core_reset(hsotg, false); if (retval) { dev_err(hsotg->dev, "%s(): Reset failed, aborting\n", __func__); @@ -2322,10 +2348,22 @@ static int dwc2_core_init(struct dwc2_hsotg *hsotg, bool initial_setup) */ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) { - u32 hcfg, hfir, otgctl; + u32 hcfg, hfir, otgctl, usbcfg; dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); + /* Set HS/FS Timeout Calibration to 7 (max available value). + * The number of PHY clocks that the application programs in + * this field is added to the high/full speed interpacket timeout + * duration in the core to account for any additional delays + * introduced by the PHY. This can be required, because the delay + * introduced by the PHY in generating the linestate condition + * can vary from one PHY to another. + */ + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + usbcfg |= GUSBCFG_TOUTCAL(7); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); + /* Restart the Phy Clock */ dwc2_writel(0, hsotg->regs + PCGCTL); @@ -2403,27 +2441,24 @@ static void dwc2_core_host_init(struct dwc2_hsotg *hsotg) /* Halt all channels to put them into a known state */ for (i = 0; i < num_channels; i++) { - int count = 0; - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; hcchar &= ~HCCHAR_EPDIR; dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); dev_dbg(hsotg->dev, "%s: Halt channel %d\n", __func__, i); - do { - hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); - if (++count > 1000) { - dev_err(hsotg->dev, - "Unable to clear enable on channel %d\n", - i); - break; - } - udelay(1); - } while (hcchar & HCCHAR_CHENA); + + if (dwc2_hsotg_wait_bit_clear(hsotg, HCCHAR(i), + HCCHAR_CHENA, 1000)) { + dev_warn(hsotg->dev, "Unable to clear enable on channel %d\n", + i); + } } } + /* Enable ACG feature in host mode, if supported */ + dwc2_enable_acg(hsotg); + /* Turn on the vbus power */ dev_dbg(hsotg->dev, "Init: Port Power? op_state=%d\n", hsotg->op_state); if (hsotg->op_state == OTG_STATE_A_HOST) { @@ -3257,6 +3292,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work) /* B-Device connector (Device Mode) */ if (gotgctl & GOTGCTL_CONID_B) { + dwc2_vbus_supply_exit(hsotg); /* Wait for switch to device mode */ dev_dbg(hsotg->dev, "connId B\n"); if (hsotg->bus_suspended) { @@ -3290,6 +3326,8 @@ static void dwc2_conn_id_status_change(struct work_struct *work) spin_lock_irqsave(&hsotg->lock, flags); dwc2_hsotg_core_init_disconnected(hsotg, false); spin_unlock_irqrestore(&hsotg->lock, flags); + /* Enable ACG feature in device mode,if supported */ + dwc2_enable_acg(hsotg); dwc2_hsotg_core_connect(hsotg); } else { host: @@ -3377,10 +3415,10 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) hsotg->bus_suspended = true; /* - * If hibernation is supported, Phy clock will be suspended + * If power_down is supported, Phy clock will be suspended * after registers are backuped. */ - if (!hsotg->params.hibernation) { + if (!hsotg->params.power_down) { /* Suspend the Phy Clock */ pcgctl = dwc2_readl(hsotg->regs + PCGCTL); pcgctl |= PCGCTL_STOPPCLK; @@ -3412,10 +3450,10 @@ static void dwc2_port_resume(struct dwc2_hsotg *hsotg) spin_lock_irqsave(&hsotg->lock, flags); /* - * If hibernation is supported, Phy clock is already resumed + * If power_down is supported, Phy clock is already resumed * after registers restore. */ - if (!hsotg->params.hibernation) { + if (!hsotg->params.power_down) { pcgctl = dwc2_readl(hsotg->regs + PCGCTL); pcgctl &= ~PCGCTL_STOPPCLK; dwc2_writel(pcgctl, hsotg->regs + PCGCTL); @@ -3486,8 +3524,12 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, dev_dbg(hsotg->dev, "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); - if (hsotg->bus_suspended) - dwc2_port_resume(hsotg); + if (hsotg->bus_suspended) { + if (hsotg->hibernated) + dwc2_exit_hibernation(hsotg, 0, 0, 1); + else + dwc2_port_resume(hsotg); + } break; case USB_PORT_FEAT_POWER: @@ -3695,7 +3737,10 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); if (windex != hsotg->otg_port) goto error; - dwc2_port_suspend(hsotg, windex); + if (hsotg->params.power_down == 2) + dwc2_enter_hibernation(hsotg, 1); + else + dwc2_port_suspend(hsotg, windex); break; case USB_PORT_FEAT_POWER: @@ -3707,6 +3752,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, break; case USB_PORT_FEAT_RESET: + if (hsotg->params.power_down == 2 && + hsotg->hibernated) + dwc2_exit_hibernation(hsotg, 0, 1, 1); hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "SetPortFeature - USB_PORT_FEAT_RESET\n"); @@ -4002,7 +4050,6 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) (p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT); dev_dbg(hsotg->dev, " P Tx FIFO Space Avail: %d\n", (p_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT); - dwc2_hcd_dump_frrem(hsotg); dwc2_dump_global_registers(hsotg); dwc2_dump_host_registers(hsotg); dev_dbg(hsotg->dev, @@ -4011,75 +4058,6 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) #endif } -/* - * NOTE: This function will be removed once the peripheral controller code - * is integrated and the driver is stable - */ -void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg) -{ -#ifdef DWC2_DUMP_FRREM - dev_dbg(hsotg->dev, "Frame remaining at SOF:\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->frrem_samples, hsotg->frrem_accum, - hsotg->frrem_samples > 0 ? - hsotg->frrem_accum / hsotg->frrem_samples : 0); - dev_dbg(hsotg->dev, "\n"); - dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 7):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_7_samples, - hsotg->hfnum_7_frrem_accum, - hsotg->hfnum_7_samples > 0 ? - hsotg->hfnum_7_frrem_accum / hsotg->hfnum_7_samples : 0); - dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 0):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_0_samples, - hsotg->hfnum_0_frrem_accum, - hsotg->hfnum_0_samples > 0 ? - hsotg->hfnum_0_frrem_accum / hsotg->hfnum_0_samples : 0); - dev_dbg(hsotg->dev, "Frame remaining at start_transfer (uframe 1-6):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_other_samples, - hsotg->hfnum_other_frrem_accum, - hsotg->hfnum_other_samples > 0 ? - hsotg->hfnum_other_frrem_accum / hsotg->hfnum_other_samples : - 0); - dev_dbg(hsotg->dev, "\n"); - dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 7):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_7_samples_a, hsotg->hfnum_7_frrem_accum_a, - hsotg->hfnum_7_samples_a > 0 ? - hsotg->hfnum_7_frrem_accum_a / hsotg->hfnum_7_samples_a : 0); - dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 0):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_0_samples_a, hsotg->hfnum_0_frrem_accum_a, - hsotg->hfnum_0_samples_a > 0 ? - hsotg->hfnum_0_frrem_accum_a / hsotg->hfnum_0_samples_a : 0); - dev_dbg(hsotg->dev, "Frame remaining at sample point A (uframe 1-6):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_other_samples_a, hsotg->hfnum_other_frrem_accum_a, - hsotg->hfnum_other_samples_a > 0 ? - hsotg->hfnum_other_frrem_accum_a / hsotg->hfnum_other_samples_a - : 0); - dev_dbg(hsotg->dev, "\n"); - dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 7):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_7_samples_b, hsotg->hfnum_7_frrem_accum_b, - hsotg->hfnum_7_samples_b > 0 ? - hsotg->hfnum_7_frrem_accum_b / hsotg->hfnum_7_samples_b : 0); - dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 0):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_0_samples_b, hsotg->hfnum_0_frrem_accum_b, - (hsotg->hfnum_0_samples_b > 0) ? - hsotg->hfnum_0_frrem_accum_b / hsotg->hfnum_0_samples_b : 0); - dev_dbg(hsotg->dev, "Frame remaining at sample point B (uframe 1-6):\n"); - dev_dbg(hsotg->dev, " samples %u, accum %llu, avg %llu\n", - hsotg->hfnum_other_samples_b, hsotg->hfnum_other_frrem_accum_b, - (hsotg->hfnum_other_samples_b > 0) ? - hsotg->hfnum_other_frrem_accum_b / hsotg->hfnum_other_samples_b - : 0); -#endif -} - struct wrapper_priv_data { struct dwc2_hsotg *hsotg; }; @@ -4363,6 +4341,9 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd) } spin_unlock_irqrestore(&hsotg->lock, flags); + + dwc2_vbus_supply_init(hsotg); + return 0; } @@ -4390,6 +4371,8 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_vbus_supply_exit(hsotg); + usleep_range(1000, 3000); } @@ -4414,7 +4397,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) if (hsotg->op_state == OTG_STATE_B_PERIPHERAL) goto unlock; - if (!hsotg->params.hibernation) + if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) goto skip_power_saving; /* @@ -4426,14 +4409,15 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) hprt0 |= HPRT0_SUSP; hprt0 &= ~HPRT0_PWR; dwc2_writel(hprt0, hsotg->regs + HPRT0); + dwc2_vbus_supply_exit(hsotg); } - /* Enter hibernation */ - ret = dwc2_enter_hibernation(hsotg); + /* Enter partial_power_down */ + ret = dwc2_enter_partial_power_down(hsotg); if (ret) { if (ret != -ENOTSUPP) dev_err(hsotg->dev, - "enter hibernation failed\n"); + "enter partial_power_down failed\n"); goto skip_power_saving; } @@ -4444,7 +4428,7 @@ static int _dwc2_hcd_suspend(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - /* After entering hibernation, hardware is no more accessible */ + /* After entering partial_power_down, hardware is no more accessible */ clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); skip_power_saving: @@ -4469,7 +4453,7 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) if (hsotg->lx_state != DWC2_L2) goto unlock; - if (!hsotg->params.hibernation) { + if (hsotg->params.power_down != DWC2_POWER_DOWN_PARAM_PARTIAL) { hsotg->lx_state = DWC2_L0; goto unlock; } @@ -4491,10 +4475,10 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_lock_irqsave(&hsotg->lock, flags); } - /* Exit hibernation */ - ret = dwc2_exit_hibernation(hsotg, true); + /* Exit partial_power_down */ + ret = dwc2_exit_partial_power_down(hsotg, true); if (ret && (ret != -ENOTSUPP)) - dev_err(hsotg->dev, "exit hibernation failed\n"); + dev_err(hsotg->dev, "exit partial_power_down failed\n"); hsotg->lx_state = DWC2_L0; @@ -4506,6 +4490,8 @@ static int _dwc2_hcd_resume(struct usb_hcd *hcd) spin_unlock_irqrestore(&hsotg->lock, flags); dwc2_port_resume(hsotg); } else { + dwc2_vbus_supply_init(hsotg); + /* Wait for controller to correctly update D+/D- level */ usleep_range(3000, 5000); @@ -5368,6 +5354,7 @@ int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) hr->hprt0 = dwc2_read_hprt0(hsotg); hr->hfir = dwc2_readl(hsotg->regs + HFIR); + hr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ); hr->valid = true; return 0; @@ -5404,7 +5391,231 @@ int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) dwc2_writel(hr->hprt0, hsotg->regs + HPRT0); dwc2_writel(hr->hfir, hsotg->regs + HFIR); + dwc2_writel(hr->hptxfsiz, hsotg->regs + HPTXFSIZ); hsotg->frame_number = 0; return 0; } + +/** + * dwc2_host_enter_hibernation() - Put controller in Hibernation. + * + * @hsotg: Programming view of the DWC_otg controller + */ +int dwc2_host_enter_hibernation(struct dwc2_hsotg *hsotg) +{ + unsigned long flags; + int ret = 0; + u32 hprt0; + u32 pcgcctl; + u32 gusbcfg; + 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__); + return ret; + } + + /* Enter USB Suspend Mode */ + hprt0 = dwc2_readl(hsotg->regs + HPRT0); + hprt0 |= HPRT0_SUSP; + hprt0 &= ~HPRT0_ENA; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + + /* Wait for the HPRT0.PrtSusp register field to be set */ + if (dwc2_hsotg_wait_bit_set(hsotg, HPRT0, HPRT0_SUSP, 300)) + dev_warn(hsotg->dev, "Suspend wasn't generated\n"); + + /* + * We need to disable interrupts to prevent servicing of any IRQ + * during going to hibernation + */ + spin_lock_irqsave(&hsotg->lock, flags); + hsotg->lx_state = DWC2_L2; + + gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + if (gusbcfg & GUSBCFG_ULPI_UTMI_SEL) { + /* ULPI interface */ + /* Suspend the Phy Clock */ + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + udelay(10); + + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + } else { + /* UTMI+ Interface */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgcctl |= PCGCTL_STOPPCLK; + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); + udelay(10); + } + + /* Enable interrupts from wake up logic */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PMUINTSEL; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Unmask host mode interrupts in GPWRDN */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_DISCONN_DET_MSK; + gpwrdn |= GPWRDN_LNSTSCHG_MSK; + gpwrdn |= GPWRDN_STS_CHGINT_MSK; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Enable Power Down Clamp */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNCLMP; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Switch off VDD */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn |= GPWRDN_PWRDNSWTCH; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + + hsotg->hibernated = 1; + hsotg->bus_suspended = 1; + dev_dbg(hsotg->dev, "Host hibernation completed\n"); + spin_unlock_irqrestore(&hsotg->lock, flags); + return ret; +} + +/* + * dwc2_host_exit_hibernation() + * + * @hsotg: Programming view of the DWC_otg controller + * @rem_wakeup: indicates whether resume is initiated by Device or Host. + * @param reset: indicates whether resume is initiated by Reset. + * + * Return: non-zero if failed to enter to hibernation. + * + * This function is for exiting from Host mode hibernation by + * Host Initiated Resume/Reset and Device Initiated Remote-Wakeup. + */ +int dwc2_host_exit_hibernation(struct dwc2_hsotg *hsotg, int rem_wakeup, + int reset) +{ + u32 gpwrdn; + u32 hprt0; + int ret = 0; + struct dwc2_gregs_backup *gr; + struct dwc2_hregs_backup *hr; + + gr = &hsotg->gr_backup; + hr = &hsotg->hr_backup; + + dev_dbg(hsotg->dev, + "%s: called with rem_wakeup = %d reset = %d\n", + __func__, rem_wakeup, reset); + + dwc2_hib_restore_common(hsotg, rem_wakeup, 1); + hsotg->hibernated = 0; + + /* + * This step is not described in functional spec but if not wait for + * this delay, mismatch interrupts occurred because just after restore + * core is in Device mode(gintsts.curmode == 0) + */ + mdelay(100); + + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + + /* De-assert Restore */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_RESTORE; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + /* Restore GUSBCFG, HCFG */ + dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(hr->hcfg, hsotg->regs + HCFG); + + /* De-assert Wakeup Logic */ + gpwrdn = dwc2_readl(hsotg->regs + GPWRDN); + gpwrdn &= ~GPWRDN_PMUACTV; + dwc2_writel(gpwrdn, hsotg->regs + GPWRDN); + udelay(10); + + hprt0 = hr->hprt0; + hprt0 |= HPRT0_PWR; + hprt0 &= ~HPRT0_ENA; + hprt0 &= ~HPRT0_SUSP; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + + hprt0 = hr->hprt0; + hprt0 |= HPRT0_PWR; + hprt0 &= ~HPRT0_ENA; + hprt0 &= ~HPRT0_SUSP; + + if (reset) { + hprt0 |= HPRT0_RST; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + + /* Wait for Resume time and then program HPRT again */ + mdelay(60); + hprt0 &= ~HPRT0_RST; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + } else { + hprt0 |= HPRT0_RES; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + + /* Wait for Resume time and then program HPRT again */ + mdelay(100); + hprt0 &= ~HPRT0_RES; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + } + /* Clear all interrupt status */ + hprt0 = dwc2_readl(hsotg->regs + HPRT0); + hprt0 |= HPRT0_CONNDET; + hprt0 |= HPRT0_ENACHG; + hprt0 &= ~HPRT0_ENA; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + + hprt0 = dwc2_readl(hsotg->regs + HPRT0); + + /* Clear all pending interupts */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + + /* Restore global registers */ + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + + /* Restore host registers */ + ret = dwc2_restore_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore host registers\n", + __func__); + return ret; + } + + hsotg->hibernated = 0; + hsotg->bus_suspended = 0; + hsotg->lx_state = DWC2_L0; + dev_dbg(hsotg->dev, "Host hibernation restore complete\n"); + return ret; +} diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index ad60e46e66e1..96a9da5fb202 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -783,19 +783,6 @@ int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg); */ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg); -/** - * dwc2_hcd_dump_frrem() - Dumps the average frame remaining at SOF - * - * @hsotg: The DWC2 HCD - * - * This can be used to determine average interrupt latency. Frame remaining is - * also shown for start transfer and two additional sample points. - * - * NOTE: This function will be removed once the peripheral controller code - * is integrated and the driver is stable - */ -void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg); - /* URB interface */ /* Transfer flags */ @@ -813,47 +800,4 @@ int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context); void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, int status); -#ifdef DEBUG -/* - * Macro to sample the remaining PHY clocks left in the current frame. This - * may be used during debugging to determine the average time it takes to - * execute sections of code. There are two possible sample points, "a" and - * "b", so the _letter_ argument must be one of these values. - * - * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For - * example, "cat /sys/devices/lm0/hcd_frrem". - */ -#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) \ -do { \ - struct hfnum_data _hfnum_; \ - struct dwc2_qtd *_qtd_; \ - \ - _qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd, \ - qtd_list_entry); \ - if (usb_pipeint(_qtd_->urb->pipe) && \ - (_qh_)->start_active_frame != 0 && !_qtd_->complete_split) { \ - _hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM); \ - switch (_hfnum_.b.frnum & 0x7) { \ - case 7: \ - (_hcd_)->hfnum_7_samples_##_letter_++; \ - (_hcd_)->hfnum_7_frrem_accum_##_letter_ += \ - _hfnum_.b.frrem; \ - break; \ - case 0: \ - (_hcd_)->hfnum_0_samples_##_letter_++; \ - (_hcd_)->hfnum_0_frrem_accum_##_letter_ += \ - _hfnum_.b.frrem; \ - break; \ - default: \ - (_hcd_)->hfnum_other_samples_##_letter_++; \ - (_hcd_)->hfnum_other_frrem_accum_##_letter_ += \ - _hfnum_.b.frrem; \ - break; \ - } \ - } \ -} while (0) -#else -#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) do {} while (0) -#endif - #endif /* __DWC2_HCD_H__ */ diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 2c906d8ee465..38391e48351f 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -231,6 +231,7 @@ #define GUID HSOTG_REG(0x003c) #define GSNPSID HSOTG_REG(0x0040) #define GHWCFG1 HSOTG_REG(0x0044) +#define GSNPSID_ID_MASK GENMASK(31, 16) #define GHWCFG2 HSOTG_REG(0x0048) #define GHWCFG2_OTG_ENABLE_IC_USB BIT(31) @@ -309,6 +310,7 @@ #define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) #define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 +#define GHWCFG4_ACG_SUPPORTED BIT(12) #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 @@ -320,28 +322,30 @@ #define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0 #define GLPMCFG HSOTG_REG(0x0054) -#define GLPMCFG_INV_SEL_HSIC BIT(31) -#define GLPMCFG_HSIC_CONNECT BIT(30) -#define GLPMCFG_RETRY_COUNT_STS_MASK (0x7 << 25) -#define GLPMCFG_RETRY_COUNT_STS_SHIFT 25 -#define GLPMCFG_SEND_LPM BIT(24) -#define GLPMCFG_RETRY_COUNT_MASK (0x7 << 21) -#define GLPMCFG_RETRY_COUNT_SHIFT 21 -#define GLPMCFG_LPM_CHAN_INDEX_MASK (0xf << 17) -#define GLPMCFG_LPM_CHAN_INDEX_SHIFT 17 -#define GLPMCFG_SLEEP_STATE_RESUMEOK BIT(16) -#define GLPMCFG_PRT_SLEEP_STS BIT(15) -#define GLPMCFG_LPM_RESP_MASK (0x3 << 13) -#define GLPMCFG_LPM_RESP_SHIFT 13 +#define GLPMCFG_INVSELHSIC BIT(31) +#define GLPMCFG_HSICCON BIT(30) +#define GLPMCFG_RSTRSLPSTS BIT(29) +#define GLPMCFG_ENBESL BIT(28) +#define GLPMCFG_LPM_RETRYCNT_STS_MASK (0x7 << 25) +#define GLPMCFG_LPM_RETRYCNT_STS_SHIFT 25 +#define GLPMCFG_SNDLPM BIT(24) +#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) +#define GLPMCFG_RETRY_CNT_SHIFT 21 +#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) +#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 +#define GLPMCFG_L1RESUMEOK BIT(16) +#define GLPMCFG_SLPSTS BIT(15) +#define GLPMCFG_COREL1RES_MASK (0x3 << 13) +#define GLPMCFG_COREL1RES_SHIFT 13 #define GLPMCFG_HIRD_THRES_MASK (0x1f << 8) #define GLPMCFG_HIRD_THRES_SHIFT 8 -#define GLPMCFG_HIRD_THRES_EN (0x10 << 8) -#define GLPMCFG_EN_UTMI_SLEEP BIT(7) -#define GLPMCFG_REM_WKUP_EN BIT(6) +#define GLPMCFG_HIRD_THRES_EN (0x10 << 8) +#define GLPMCFG_ENBLSLPM BIT(7) +#define GLPMCFG_BREMOTEWAKE BIT(6) #define GLPMCFG_HIRD_MASK (0xf << 2) #define GLPMCFG_HIRD_SHIFT 2 -#define GLPMCFG_APPL_RESP BIT(1) -#define GLPMCFG_LPM_CAP_EN BIT(0) +#define GLPMCFG_APPL1RES BIT(1) +#define GLPMCFG_LPMCAP BIT(0) #define GPWRDN HSOTG_REG(0x0058) #define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24) @@ -644,6 +648,10 @@ #define PCGCTL_GATEHCLK BIT(1) #define PCGCTL_STOPPCLK BIT(0) +#define PCGCCTL1 HSOTG_REG(0xe04) +#define PCGCCTL1_TIMER (0x3 << 1) +#define PCGCCTL1_GATEEN BIT(0) + #define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000)) /* Host Mode Registers */ diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index c4a47496d2fb..f03e41879224 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -252,6 +252,20 @@ static void dwc2_set_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg) p->g_tx_fifo_size[i] = depth_average; } +static void dwc2_set_param_power_down(struct dwc2_hsotg *hsotg) +{ + int val; + + if (hsotg->hw_params.hibernation) + val = 2; + else if (hsotg->hw_params.power_optimized) + val = 1; + else + val = 0; + + hsotg->params.power_down = val; +} + /** * dwc2_set_default_params() - Set all core parameters to their * auto-detected default values. @@ -266,21 +280,27 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg) dwc2_set_param_phy_type(hsotg); dwc2_set_param_speed(hsotg); dwc2_set_param_phy_utmi_width(hsotg); + dwc2_set_param_power_down(hsotg); p->phy_ulpi_ddr = false; p->phy_ulpi_ext_vbus = false; p->enable_dynamic_fifo = hw->enable_dynamic_fifo; p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo; p->i2c_enable = hw->i2c_enable; + p->acg_enable = hw->acg_enable; p->ulpi_fs_ls = false; p->ts_dline = false; p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a); p->uframe_sched = true; p->external_id_pin_ctl = false; - p->hibernation = false; + p->lpm = true; + p->lpm_clock_gating = true; + p->besl = true; + p->hird_threshold_en = true; + p->hird_threshold = 4; p->max_packet_count = hw->max_packet_count; p->max_transfer_size = hw->max_transfer_size; - p->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT; if ((hsotg->dr_mode == USB_DR_MODE_HOST) || (hsotg->dr_mode == USB_DR_MODE_OTG)) { @@ -463,6 +483,38 @@ static void dwc2_check_param_phy_utmi_width(struct dwc2_hsotg *hsotg) dwc2_set_param_phy_utmi_width(hsotg); } +static void dwc2_check_param_power_down(struct dwc2_hsotg *hsotg) +{ + int param = hsotg->params.power_down; + + switch (param) { + case DWC2_POWER_DOWN_PARAM_NONE: + break; + case DWC2_POWER_DOWN_PARAM_PARTIAL: + if (hsotg->hw_params.power_optimized) + break; + dev_dbg(hsotg->dev, + "Partial power down isn't supported by HW\n"); + param = DWC2_POWER_DOWN_PARAM_NONE; + break; + case DWC2_POWER_DOWN_PARAM_HIBERNATION: + if (hsotg->hw_params.hibernation) + break; + dev_dbg(hsotg->dev, + "Hibernation isn't supported by HW\n"); + param = DWC2_POWER_DOWN_PARAM_NONE; + break; + default: + dev_err(hsotg->dev, + "%s: Invalid parameter power_down=%d\n", + __func__, param); + param = DWC2_POWER_DOWN_PARAM_NONE; + break; + } + + hsotg->params.power_down = param; +} + static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg) { int fifo_count; @@ -523,10 +575,19 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg) dwc2_check_param_phy_type(hsotg); dwc2_check_param_speed(hsotg); dwc2_check_param_phy_utmi_width(hsotg); + dwc2_check_param_power_down(hsotg); CHECK_BOOL(enable_dynamic_fifo, hw->enable_dynamic_fifo); CHECK_BOOL(en_multiple_tx_fifo, hw->en_multiple_tx_fifo); CHECK_BOOL(i2c_enable, hw->i2c_enable); + CHECK_BOOL(acg_enable, hw->acg_enable); CHECK_BOOL(reload_ctl, (hsotg->hw_params.snpsid > DWC2_CORE_REV_2_92a)); + CHECK_BOOL(lpm, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_80a)); + CHECK_BOOL(lpm, hw->lpm_mode); + CHECK_BOOL(lpm_clock_gating, hsotg->params.lpm); + CHECK_BOOL(besl, hsotg->params.lpm); + CHECK_BOOL(besl, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a)); + CHECK_BOOL(hird_threshold_en, hsotg->params.lpm); + CHECK_RANGE(hird_threshold, 0, hsotg->params.besl ? 12 : 7, 0); CHECK_RANGE(max_packet_count, 15, hw->max_packet_count, hw->max_packet_count); @@ -579,19 +640,15 @@ static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg) struct dwc2_hw_params *hw = &hsotg->hw_params; u32 gnptxfsiz; u32 hptxfsiz; - bool forced; if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) return; - forced = dwc2_force_mode_if_needed(hsotg, true); + dwc2_force_mode(hsotg, true); gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ); - if (forced) - dwc2_clear_force_mode(hsotg); - hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT; hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >> @@ -606,14 +663,13 @@ static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg) static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg) { struct dwc2_hw_params *hw = &hsotg->hw_params; - bool forced; u32 gnptxfsiz; int fifo, fifo_count; if (hsotg->dr_mode == USB_DR_MODE_HOST) return; - forced = dwc2_force_mode_if_needed(hsotg, false); + dwc2_force_mode(hsotg, false); gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); @@ -625,9 +681,6 @@ static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg) FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT; } - if (forced) - dwc2_clear_force_mode(hsotg); - hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT; } @@ -646,14 +699,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) /* * Attempt to ensure this device is really a DWC_otg Controller. * Read and verify the GSNPSID register contents. The value should be - * 0x45f42xxx or 0x45f43xxx, which corresponds to either "OT2" or "OT3", - * as in "OTG version 2.xx" or "OTG version 3.xx". + * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx */ + hw->snpsid = dwc2_readl(hsotg->regs + GSNPSID); - if ((hw->snpsid & 0xfffff000) != 0x4f542000 && - (hw->snpsid & 0xfffff000) != 0x4f543000 && - (hw->snpsid & 0xffff0000) != 0x55310000 && - (hw->snpsid & 0xffff0000) != 0x55320000) { + if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID && + (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID && + (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) { dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n", hw->snpsid); return -ENODEV; @@ -706,6 +758,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C); hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >> GHWCFG3_DFIFO_DEPTH_SHIFT; + hw->lpm_mode = !!(hwcfg3 & GHWCFG3_OTG_LPM_EN); /* hwcfg4 */ hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN); @@ -715,8 +768,10 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) GHWCFG4_NUM_IN_EPS_SHIFT; hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA); hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ); + hw->hibernation = !!(hwcfg4 & GHWCFG4_HIBER); hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >> GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT; + hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED); /* fifo sizes */ hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >> diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c index 3ecc951a1aea..7f21747007f1 100644 --- a/drivers/usb/dwc2/pci.c +++ b/drivers/usb/dwc2/pci.c @@ -83,7 +83,6 @@ static void dwc2_pci_remove(struct pci_dev *pci) platform_device_unregister(glue->dwc2); usb_phy_generic_unregister(glue->phy); - kfree(glue); pci_set_drvdata(pci, NULL); } @@ -105,10 +104,17 @@ static int dwc2_pci_probe(struct pci_dev *pci, pci_set_master(pci); + phy = usb_phy_generic_register(); + if (IS_ERR(phy)) { + dev_err(dev, "error registering generic PHY (%ld)\n", + PTR_ERR(phy)); + return PTR_ERR(phy); + } + dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO); if (!dwc2) { dev_err(dev, "couldn't allocate dwc2 device\n"); - return -ENOMEM; + goto err; } memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); @@ -125,32 +131,25 @@ static int dwc2_pci_probe(struct pci_dev *pci, ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res)); if (ret) { dev_err(dev, "couldn't add resources to dwc2 device\n"); - return ret; + goto err; } dwc2->dev.parent = dev; - phy = usb_phy_generic_register(); - if (IS_ERR(phy)) { - dev_err(dev, "error registering generic PHY (%ld)\n", - PTR_ERR(phy)); - return PTR_ERR(phy); - } - ret = dwc2_pci_quirks(pci, dwc2); if (ret) goto err; + glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); + if (!glue) + goto err; + ret = platform_device_add(dwc2); if (ret) { dev_err(dev, "failed to register dwc2 device\n"); goto err; } - glue = kzalloc(sizeof(*glue), GFP_KERNEL); - if (!glue) - return -ENOMEM; - glue->phy = phy; glue->dwc2 = dwc2; pci_set_drvdata(pci, glue); diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 4703478f702f..4c0819554bcd 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -382,8 +382,10 @@ static int dwc2_driver_probe(struct platform_device *dev) if (!dev->dev.dma_mask) dev->dev.dma_mask = &dev->dev.coherent_dma_mask; retval = dma_set_coherent_mask(&dev->dev, DMA_BIT_MASK(32)); - if (retval) + if (retval) { + dev_err(&dev->dev, "can't set coherent DMA mask: %d\n", retval); return retval; + } res = platform_get_resource(dev, IORESOURCE_MEM, 0); hsotg->regs = devm_ioremap_resource(&dev->dev, res); @@ -425,13 +427,20 @@ static int dwc2_driver_probe(struct platform_device *dev) * Reset before dwc2_get_hwparams() then it could get power-on real * reset value form registers. */ - dwc2_core_reset_and_force_dr_mode(hsotg); + retval = dwc2_core_reset(hsotg, false); + if (retval) + goto error; /* Detect config values from hardware */ retval = dwc2_get_hwparams(hsotg); if (retval) goto error; + /* + * For OTG cores, set the force mode bits to reflect the value + * of dr_mode. Force mode bits should not be touched at any + * other time after this. + */ dwc2_force_dr_mode(hsotg); retval = dwc2_init_params(hsotg); @@ -439,7 +448,7 @@ static int dwc2_driver_probe(struct platform_device *dev) goto error; if (hsotg->dr_mode != USB_DR_MODE_HOST) { - retval = dwc2_gadget_init(hsotg, hsotg->irq); + retval = dwc2_gadget_init(hsotg); if (retval) goto error; hsotg->gadget_enabled = 1; @@ -456,6 +465,7 @@ static int dwc2_driver_probe(struct platform_device *dev) } platform_set_drvdata(dev, hsotg); + hsotg->hibernated = 0; dwc2_debugfs_init(hsotg); diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 7ac725038f8d..025bc68094fc 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_USB_DWC3) += dwc3.o dwc3-y := core.o -ifneq ($(CONFIG_FTRACE),) +ifneq ($(CONFIG_TRACING),) dwc3-y += trace.o endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index e94bf91cc58a..a15648d25e30 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -89,10 +89,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc) return 0; } -static void dwc3_event_buffers_cleanup(struct dwc3 *dwc); -static int dwc3_event_buffers_setup(struct dwc3 *dwc); - -static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode) { u32 reg; @@ -110,13 +107,19 @@ static void __dwc3_set_mode(struct work_struct *work) unsigned long flags; int ret; + if (dwc->dr_mode != USB_DR_MODE_OTG) + return; + + if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG) + dwc3_otg_update(dwc, 0); + if (!dwc->desired_dr_role) return; if (dwc->desired_dr_role == dwc->current_dr_role) return; - if (dwc->dr_mode != USB_DR_MODE_OTG) + if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev) return; switch (dwc->current_dr_role) { @@ -127,6 +130,13 @@ static void __dwc3_set_mode(struct work_struct *work) dwc3_gadget_exit(dwc); dwc3_event_buffers_cleanup(dwc); break; + case DWC3_GCTL_PRTCAP_OTG: + dwc3_otg_exit(dwc); + spin_lock_irqsave(&dwc->lock, flags); + dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE; + spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_otg_update(dwc, 1); + break; default: break; } @@ -162,9 +172,14 @@ static void __dwc3_set_mode(struct work_struct *work) if (ret) dev_err(dwc->dev, "failed to initialize peripheral\n"); break; + case DWC3_GCTL_PRTCAP_OTG: + dwc3_otg_init(dwc); + dwc3_otg_update(dwc, 0); + break; default: break; } + } void dwc3_set_mode(struct dwc3 *dwc, u32 mode) @@ -229,7 +244,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) do { reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (!(reg & DWC3_DCTL_CSFTRST)) - return 0; + goto done; udelay(1); } while (--retries); @@ -238,6 +253,17 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) phy_exit(dwc->usb2_generic_phy); return -ETIMEDOUT; + +done: + /* + * For DWC_usb31 controller, once DWC3_DCTL_CSFTRST bit is cleared, + * we must wait at least 50ms before accessing the PHY domain + * (synchronization delay). DWC_usb31 programming guide section 1.3.2. + */ + if (dwc3_is_usb31(dwc)) + msleep(50); + + return 0; } /* @@ -348,7 +374,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) * * Returns 0 on success otherwise negative errno. */ -static int dwc3_event_buffers_setup(struct dwc3 *dwc) +int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; @@ -365,7 +391,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc) return 0; } -static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) +void dwc3_event_buffers_cleanup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; @@ -846,6 +872,43 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUCTL1, reg); } + /* + * Must config both number of packets and max burst settings to enable + * RX and/or TX threshold. + */ + if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) { + u8 rx_thr_num = dwc->rx_thr_num_pkt_prd; + u8 rx_maxburst = dwc->rx_max_burst_prd; + u8 tx_thr_num = dwc->tx_thr_num_pkt_prd; + u8 tx_maxburst = dwc->tx_max_burst_prd; + + if (rx_thr_num && rx_maxburst) { + reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); + reg |= DWC31_RXTHRNUMPKTSEL_PRD; + + reg &= ~DWC31_RXTHRNUMPKT_PRD(~0); + reg |= DWC31_RXTHRNUMPKT_PRD(rx_thr_num); + + reg &= ~DWC31_MAXRXBURSTSIZE_PRD(~0); + reg |= DWC31_MAXRXBURSTSIZE_PRD(rx_maxburst); + + dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); + } + + if (tx_thr_num && tx_maxburst) { + reg = dwc3_readl(dwc->regs, DWC3_GTXTHRCFG); + reg |= DWC31_TXTHRNUMPKTSEL_PRD; + + reg &= ~DWC31_TXTHRNUMPKT_PRD(~0); + reg |= DWC31_TXTHRNUMPKT_PRD(tx_thr_num); + + reg &= ~DWC31_MAXTXBURSTSIZE_PRD(~0); + reg |= DWC31_MAXTXBURSTSIZE_PRD(tx_maxburst); + + dwc3_writel(dwc->regs, DWC3_GTXTHRCFG, reg); + } + } + return 0; err4: @@ -1016,6 +1079,10 @@ static void dwc3_get_properties(struct dwc3 *dwc) u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; + u8 rx_thr_num_pkt_prd; + u8 rx_max_burst_prd; + u8 tx_thr_num_pkt_prd; + u8 tx_max_burst_prd; /* default to highest possible threshold */ lpm_nyet_threshold = 0xff; @@ -1050,6 +1117,14 @@ static void dwc3_get_properties(struct dwc3 *dwc) &hird_threshold); dwc->usb3_lpm_capable = device_property_read_bool(dev, "snps,usb3_lpm_capable"); + device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd", + &rx_thr_num_pkt_prd); + device_property_read_u8(dev, "snps,rx-max-burst-prd", + &rx_max_burst_prd); + device_property_read_u8(dev, "snps,tx-thr-num-pkt-prd", + &tx_thr_num_pkt_prd); + device_property_read_u8(dev, "snps,tx-max-burst-prd", + &tx_max_burst_prd); dwc->disable_scramble_quirk = device_property_read_bool(dev, "snps,disable_scramble_quirk"); @@ -1100,6 +1175,12 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); + dwc->rx_thr_num_pkt_prd = rx_thr_num_pkt_prd; + dwc->rx_max_burst_prd = rx_max_burst_prd; + + dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd; + dwc->tx_max_burst_prd = tx_max_burst_prd; + dwc->imod_interval = 0; } @@ -1326,6 +1407,20 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) if (!PMSG_IS_AUTO(msg)) dwc3_core_exit(dwc); break; + case DWC3_GCTL_PRTCAP_OTG: + /* do nothing during runtime_suspend */ + if (PMSG_IS_AUTO(msg)) + break; + + if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) { + spin_lock_irqsave(&dwc->lock, flags); + dwc3_gadget_suspend(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + } + + dwc3_otg_exit(dwc); + dwc3_core_exit(dwc); + break; default: /* do nothing */ break; @@ -1345,6 +1440,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) if (ret) return ret; + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE); spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_resume(dwc); spin_unlock_irqrestore(&dwc->lock, flags); @@ -1355,7 +1451,29 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) ret = dwc3_core_init(dwc); if (ret) return ret; + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST); + } + break; + case DWC3_GCTL_PRTCAP_OTG: + /* nothing to do on runtime_resume */ + if (PMSG_IS_AUTO(msg)) + break; + + ret = dwc3_core_init(dwc); + if (ret) + return ret; + + dwc3_set_prtcap(dwc, dwc->current_dr_role); + + dwc3_otg_init(dwc); + if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST) { + dwc3_otg_host_init(dwc); + } else if (dwc->current_otg_role == DWC3_OTG_ROLE_DEVICE) { + spin_lock_irqsave(&dwc->lock, flags); + dwc3_gadget_resume(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); } + break; default: /* do nothing */ diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 860d2bc184d1..4f3b43809917 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -58,6 +58,11 @@ #define DWC3_DEVICE_EVENT_CMD_CMPL 10 #define DWC3_DEVICE_EVENT_OVERFLOW 11 +/* Controller's role while using the OTG block */ +#define DWC3_OTG_ROLE_IDLE 0 +#define DWC3_OTG_ROLE_HOST 1 +#define DWC3_OTG_ROLE_DEVICE 2 + #define DWC3_GEVNTCOUNT_MASK 0xfffc #define DWC3_GEVNTCOUNT_EHB BIT(31) #define DWC3_GSNPSID_MASK 0xffff0000 @@ -100,6 +105,11 @@ #define DWC3_GHWPARAMS7 0xc15c #define DWC3_GDBGFIFOSPACE 0xc160 #define DWC3_GDBGLTSSM 0xc164 +#define DWC3_GDBGBMU 0xc16c +#define DWC3_GDBGLSPMUX 0xc170 +#define DWC3_GDBGLSP 0xc174 +#define DWC3_GDBGEPINFO0 0xc178 +#define DWC3_GDBGEPINFO1 0xc17c #define DWC3_GPRTBIMAP_HS0 0xc180 #define DWC3_GPRTBIMAP_HS1 0xc184 #define DWC3_GPRTBIMAP_FS0 0xc188 @@ -173,6 +183,26 @@ #define DWC3_GRXTHRCFG_RXPKTCNT(n) (((n) & 0xf) << 24) #define DWC3_GRXTHRCFG_PKTCNTSEL BIT(29) +/* Global RX Threshold Configuration Register for DWC_usb31 only */ +#define DWC31_GRXTHRCFG_MAXRXBURSTSIZE(n) (((n) & 0x1f) << 16) +#define DWC31_GRXTHRCFG_RXPKTCNT(n) (((n) & 0x1f) << 21) +#define DWC31_GRXTHRCFG_PKTCNTSEL BIT(26) +#define DWC31_RXTHRNUMPKTSEL_HS_PRD BIT(15) +#define DWC31_RXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13) +#define DWC31_RXTHRNUMPKTSEL_PRD BIT(10) +#define DWC31_RXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5) +#define DWC31_MAXRXBURSTSIZE_PRD(n) ((n) & 0x1f) + +/* Global TX Threshold Configuration Register for DWC_usb31 only */ +#define DWC31_GTXTHRCFG_MAXTXBURSTSIZE(n) (((n) & 0x1f) << 16) +#define DWC31_GTXTHRCFG_TXPKTCNT(n) (((n) & 0x1f) << 21) +#define DWC31_GTXTHRCFG_PKTCNTSEL BIT(26) +#define DWC31_TXTHRNUMPKTSEL_HS_PRD BIT(15) +#define DWC31_TXTHRNUMPKT_HS_PRD(n) (((n) & 0x3) << 13) +#define DWC31_TXTHRNUMPKTSEL_PRD BIT(10) +#define DWC31_TXTHRNUMPKT_PRD(n) (((n) & 0x1f) << 5) +#define DWC31_MAXTXBURSTSIZE_PRD(n) ((n) & 0x1f) + /* Global Configuration Register */ #define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) #define DWC3_GCTL_U2RSTECN BIT(16) @@ -201,6 +231,15 @@ #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28) #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24) +/* Global Status Register */ +#define DWC3_GSTS_OTG_IP BIT(10) +#define DWC3_GSTS_BC_IP BIT(9) +#define DWC3_GSTS_ADP_IP BIT(8) +#define DWC3_GSTS_HOST_IP BIT(7) +#define DWC3_GSTS_DEVICE_IP BIT(6) +#define DWC3_GSTS_CSR_TIMEOUT BIT(5) +#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4) + /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31) #define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30) @@ -241,6 +280,8 @@ #define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1) /* Global TX Fifo Size Register */ +#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */ +#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */ #define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) #define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) @@ -286,6 +327,11 @@ #define DWC3_MAX_HIBER_SCRATCHBUFS 15 /* Global HWPARAMS6 Register */ +#define DWC3_GHWPARAMS6_BCSUPPORT BIT(14) +#define DWC3_GHWPARAMS6_OTG3SUPPORT BIT(13) +#define DWC3_GHWPARAMS6_ADPSUPPORT BIT(12) +#define DWC3_GHWPARAMS6_HNPSUPPORT BIT(11) +#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10) #define DWC3_GHWPARAMS6_EN_FPGA BIT(7) /* Global HWPARAMS7 Register */ @@ -467,6 +513,74 @@ #define DWC3_DEV_IMOD_INTERVAL_SHIFT 0 #define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0) +/* OTG Configuration Register */ +#define DWC3_OCFG_DISPWRCUTTOFF BIT(5) +#define DWC3_OCFG_HIBDISMASK BIT(4) +#define DWC3_OCFG_SFTRSTMASK BIT(3) +#define DWC3_OCFG_OTGVERSION BIT(2) +#define DWC3_OCFG_HNPCAP BIT(1) +#define DWC3_OCFG_SRPCAP BIT(0) + +/* OTG CTL Register */ +#define DWC3_OCTL_OTG3GOERR BIT(7) +#define DWC3_OCTL_PERIMODE BIT(6) +#define DWC3_OCTL_PRTPWRCTL BIT(5) +#define DWC3_OCTL_HNPREQ BIT(4) +#define DWC3_OCTL_SESREQ BIT(3) +#define DWC3_OCTL_TERMSELIDPULSE BIT(2) +#define DWC3_OCTL_DEVSETHNPEN BIT(1) +#define DWC3_OCTL_HSTSETHNPEN BIT(0) + +/* OTG Event Register */ +#define DWC3_OEVT_DEVICEMODE BIT(31) +#define DWC3_OEVT_XHCIRUNSTPSET BIT(27) +#define DWC3_OEVT_DEVRUNSTPSET BIT(26) +#define DWC3_OEVT_HIBENTRY BIT(25) +#define DWC3_OEVT_CONIDSTSCHNG BIT(24) +#define DWC3_OEVT_HRRCONFNOTIF BIT(23) +#define DWC3_OEVT_HRRINITNOTIF BIT(22) +#define DWC3_OEVT_ADEVIDLE BIT(21) +#define DWC3_OEVT_ADEVBHOSTEND BIT(20) +#define DWC3_OEVT_ADEVHOST BIT(19) +#define DWC3_OEVT_ADEVHNPCHNG BIT(18) +#define DWC3_OEVT_ADEVSRPDET BIT(17) +#define DWC3_OEVT_ADEVSESSENDDET BIT(16) +#define DWC3_OEVT_BDEVBHOSTEND BIT(11) +#define DWC3_OEVT_BDEVHNPCHNG BIT(10) +#define DWC3_OEVT_BDEVSESSVLDDET BIT(9) +#define DWC3_OEVT_BDEVVBUSCHNG BIT(8) +#define DWC3_OEVT_BSESSVLD BIT(3) +#define DWC3_OEVT_HSTNEGSTS BIT(2) +#define DWC3_OEVT_SESREQSTS BIT(1) +#define DWC3_OEVT_ERROR BIT(0) + +/* OTG Event Enable Register */ +#define DWC3_OEVTEN_XHCIRUNSTPSETEN BIT(27) +#define DWC3_OEVTEN_DEVRUNSTPSETEN BIT(26) +#define DWC3_OEVTEN_HIBENTRYEN BIT(25) +#define DWC3_OEVTEN_CONIDSTSCHNGEN BIT(24) +#define DWC3_OEVTEN_HRRCONFNOTIFEN BIT(23) +#define DWC3_OEVTEN_HRRINITNOTIFEN BIT(22) +#define DWC3_OEVTEN_ADEVIDLEEN BIT(21) +#define DWC3_OEVTEN_ADEVBHOSTENDEN BIT(20) +#define DWC3_OEVTEN_ADEVHOSTEN BIT(19) +#define DWC3_OEVTEN_ADEVHNPCHNGEN BIT(18) +#define DWC3_OEVTEN_ADEVSRPDETEN BIT(17) +#define DWC3_OEVTEN_ADEVSESSENDDETEN BIT(16) +#define DWC3_OEVTEN_BDEVBHOSTENDEN BIT(11) +#define DWC3_OEVTEN_BDEVHNPCHNGEN BIT(10) +#define DWC3_OEVTEN_BDEVSESSVLDDETEN BIT(9) +#define DWC3_OEVTEN_BDEVVBUSCHNGEN BIT(8) + +/* OTG Status Register */ +#define DWC3_OSTS_DEVRUNSTP BIT(13) +#define DWC3_OSTS_XHCIRUNSTP BIT(12) +#define DWC3_OSTS_PERIPHERALSTATE BIT(4) +#define DWC3_OSTS_XHCIPRTPOWER BIT(3) +#define DWC3_OSTS_BSESVLD BIT(2) +#define DWC3_OSTS_VBUSVLD BIT(1) +#define DWC3_OSTS_CONIDSTS BIT(0) + /* Structures */ struct dwc3_trb; @@ -781,6 +895,10 @@ struct dwc3_scratchpad_array { * @regs_size: address space size * @fladj: frame length adjustment * @irq_gadget: peripheral controller's IRQ number + * @otg_irq: IRQ number for OTG IRQs + * @current_otg_role: current role of operation while using the OTG block + * @desired_otg_role: desired role of operation while using the OTG block + * @otg_restart_host: flag that OTG controller needs to restart host * @nr_scratch: number of scratch buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) @@ -816,6 +934,10 @@ struct dwc3_scratchpad_array { * @test_mode_nr: test feature selector * @lpm_nyet_threshold: LPM NYET response threshold * @hird_threshold: HIRD threshold + * @rx_thr_num_pkt_prd: periodic ESS receive packet count + * @rx_max_burst_prd: max periodic ESS receive burst size + * @tx_thr_num_pkt_prd: periodic ESS transmit packet count + * @tx_max_burst_prd: max periodic ESS transmit burst size * @hsphy_interface: "utmi" or "ulpi" * @connected: true when we're connected to a host, false otherwise * @delayed_status: true when gadget driver asks for delayed status @@ -914,6 +1036,10 @@ struct dwc3 { u32 fladj; u32 irq_gadget; + u32 otg_irq; + u32 current_otg_role; + u32 desired_otg_role; + bool otg_restart_host; u32 nr_scratch; u32 u1u2; u32 maximum_speed; @@ -979,6 +1105,10 @@ struct dwc3 { u8 test_mode_nr; u8 lpm_nyet_threshold; u8 hird_threshold; + u8 rx_thr_num_pkt_prd; + u8 rx_max_burst_prd; + u8 tx_thr_num_pkt_prd; + u8 tx_max_burst_prd; const char *hsphy_interface; @@ -1175,6 +1305,7 @@ struct dwc3_gadget_ep_cmd_params { #define DWC3_HAS_OTG BIT(3) /* prototypes */ +void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode); void dwc3_set_mode(struct dwc3 *dwc, u32 mode); u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type); @@ -1192,6 +1323,9 @@ static inline bool dwc3_is_usb31(struct dwc3 *dwc) bool dwc3_has_imod(struct dwc3 *dwc); +int dwc3_event_buffers_setup(struct dwc3 *dwc); +void dwc3_event_buffers_cleanup(struct dwc3 *dwc); + #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_host_init(struct dwc3 *dwc); void dwc3_host_exit(struct dwc3 *dwc); @@ -1235,11 +1369,23 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_drd_init(struct dwc3 *dwc); void dwc3_drd_exit(struct dwc3 *dwc); +void dwc3_otg_init(struct dwc3 *dwc); +void dwc3_otg_exit(struct dwc3 *dwc); +void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus); +void dwc3_otg_host_init(struct dwc3 *dwc); #else static inline int dwc3_drd_init(struct dwc3 *dwc) { return 0; } static inline void dwc3_drd_exit(struct dwc3 *dwc) { } +static inline void dwc3_otg_init(struct dwc3 *dwc) +{ } +static inline void dwc3_otg_exit(struct dwc3 *dwc) +{ } +static inline void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) +{ } +static inline void dwc3_otg_host_init(struct dwc3 *dwc) +{ } #endif /* power management interface */ diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 00e65530c81e..2f07be1e1f31 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -81,6 +81,11 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(GHWPARAMS7), dump_register(GDBGFIFOSPACE), dump_register(GDBGLTSSM), + dump_register(GDBGBMU), + dump_register(GDBGLSPMUX), + dump_register(GDBGLSP), + dump_register(GDBGEPINFO0), + dump_register(GDBGEPINFO1), dump_register(GPRTBIMAP_HS0), dump_register(GPRTBIMAP_HS1), dump_register(GPRTBIMAP_FS0), @@ -487,8 +492,8 @@ static const struct file_operations dwc3_link_state_fops = { }; struct dwc3_ep_file_map { - char name[25]; - int (*show)(struct seq_file *s, void *unused); + const char name[25]; + const struct file_operations *const fops; }; static int dwc3_tx_fifo_queue_show(struct seq_file *s, void *unused) @@ -596,7 +601,7 @@ static int dwc3_event_queue_show(struct seq_file *s, void *unused) return 0; } -static int dwc3_ep_transfer_type_show(struct seq_file *s, void *unused) +static int dwc3_transfer_type_show(struct seq_file *s, void *unused) { struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; @@ -632,7 +637,7 @@ out: return 0; } -static int dwc3_ep_trb_ring_show(struct seq_file *s, void *unused) +static int dwc3_trb_ring_show(struct seq_file *s, void *unused) { struct dwc3_ep *dep = s->private; struct dwc3 *dwc = dep->dwc; @@ -670,58 +675,39 @@ out: return 0; } -static struct dwc3_ep_file_map map[] = { - { "tx_fifo_queue", dwc3_tx_fifo_queue_show, }, - { "rx_fifo_queue", dwc3_rx_fifo_queue_show, }, - { "tx_request_queue", dwc3_tx_request_queue_show, }, - { "rx_request_queue", dwc3_rx_request_queue_show, }, - { "rx_info_queue", dwc3_rx_info_queue_show, }, - { "descriptor_fetch_queue", dwc3_descriptor_fetch_queue_show, }, - { "event_queue", dwc3_event_queue_show, }, - { "transfer_type", dwc3_ep_transfer_type_show, }, - { "trb_ring", dwc3_ep_trb_ring_show, }, +DEFINE_SHOW_ATTRIBUTE(dwc3_tx_fifo_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_rx_fifo_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_tx_request_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_rx_request_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_rx_info_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_descriptor_fetch_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_event_queue); +DEFINE_SHOW_ATTRIBUTE(dwc3_transfer_type); +DEFINE_SHOW_ATTRIBUTE(dwc3_trb_ring); + +static const struct dwc3_ep_file_map dwc3_ep_file_map[] = { + { "tx_fifo_queue", &dwc3_tx_fifo_queue_fops, }, + { "rx_fifo_queue", &dwc3_rx_fifo_queue_fops, }, + { "tx_request_queue", &dwc3_tx_request_queue_fops, }, + { "rx_request_queue", &dwc3_rx_request_queue_fops, }, + { "rx_info_queue", &dwc3_rx_info_queue_fops, }, + { "descriptor_fetch_queue", &dwc3_descriptor_fetch_queue_fops, }, + { "event_queue", &dwc3_event_queue_fops, }, + { "transfer_type", &dwc3_transfer_type_fops, }, + { "trb_ring", &dwc3_trb_ring_fops, }, }; -static int dwc3_endpoint_open(struct inode *inode, struct file *file) -{ - const char *file_name = file_dentry(file)->d_iname; - struct dwc3_ep_file_map *f_map; - int i; - - for (i = 0; i < ARRAY_SIZE(map); i++) { - f_map = &map[i]; - - if (strcmp(f_map->name, file_name) == 0) - break; - } - - return single_open(file, f_map->show, inode->i_private); -} - -static const struct file_operations dwc3_endpoint_fops = { - .open = dwc3_endpoint_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void dwc3_debugfs_create_endpoint_file(struct dwc3_ep *dep, - struct dentry *parent, int type) -{ - struct dentry *file; - struct dwc3_ep_file_map *ep_file = &map[type]; - - file = debugfs_create_file(ep_file->name, S_IRUGO, parent, dep, - &dwc3_endpoint_fops); -} - static void dwc3_debugfs_create_endpoint_files(struct dwc3_ep *dep, struct dentry *parent) { int i; - for (i = 0; i < ARRAY_SIZE(map); i++) - dwc3_debugfs_create_endpoint_file(dep, parent, i); + for (i = 0; i < ARRAY_SIZE(dwc3_ep_file_map); i++) { + const struct file_operations *fops = dwc3_ep_file_map[i].fops; + const char *name = dwc3_ep_file_map[i].name; + + debugfs_create_file(name, S_IRUGO, parent, dep, fops); + } } static void dwc3_debugfs_create_endpoint_dir(struct dwc3_ep *dep, diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index cc8ab9a8e9d2..1d8c557e97e0 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -8,22 +8,423 @@ */ #include <linux/extcon.h> +#include <linux/platform_device.h> #include "debug.h" #include "core.h" #include "gadget.h" -static void dwc3_drd_update(struct dwc3 *dwc) +static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask) +{ + u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN); + + reg &= ~(disable_mask); + dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); +} + +static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask) +{ + u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN); + + reg |= (enable_mask); + dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); +} + +static void dwc3_otg_clear_events(struct dwc3 *dwc) +{ + u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT); + + dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); +} + +#define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \ + DWC3_OEVTEN_DEVRUNSTPSETEN | DWC3_OEVTEN_HIBENTRYEN | \ + DWC3_OEVTEN_CONIDSTSCHNGEN | DWC3_OEVTEN_HRRCONFNOTIFEN | \ + DWC3_OEVTEN_HRRINITNOTIFEN | DWC3_OEVTEN_ADEVIDLEEN | \ + DWC3_OEVTEN_ADEVBHOSTENDEN | DWC3_OEVTEN_ADEVHOSTEN | \ + DWC3_OEVTEN_ADEVHNPCHNGEN | DWC3_OEVTEN_ADEVSRPDETEN | \ + DWC3_OEVTEN_ADEVSESSENDDETEN | DWC3_OEVTEN_BDEVBHOSTENDEN | \ + DWC3_OEVTEN_BDEVHNPCHNGEN | DWC3_OEVTEN_BDEVSESSVLDDETEN | \ + DWC3_OEVTEN_BDEVVBUSCHNGEN) + +static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc) { + struct dwc3 *dwc = _dwc; + + spin_lock(&dwc->lock); + if (dwc->otg_restart_host) { + dwc3_otg_host_init(dwc); + dwc->otg_restart_host = 0; + } + + spin_unlock(&dwc->lock); + + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); + + return IRQ_HANDLED; +} + +static irqreturn_t dwc3_otg_irq(int irq, void *_dwc) +{ + u32 reg; + struct dwc3 *dwc = _dwc; + irqreturn_t ret = IRQ_NONE; + + reg = dwc3_readl(dwc->regs, DWC3_OEVT); + if (reg) { + /* ignore non OTG events, we can't disable them in OEVTEN */ + if (!(reg & DWC3_OTG_ALL_EVENTS)) { + dwc3_writel(dwc->regs, DWC3_OEVT, reg); + return IRQ_NONE; + } + + if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST && + !(reg & DWC3_OEVT_DEVICEMODE)) + dwc->otg_restart_host = 1; + dwc3_writel(dwc->regs, DWC3_OEVT, reg); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} + +static void dwc3_otgregs_init(struct dwc3 *dwc) +{ + u32 reg; + + /* + * Prevent host/device reset from resetting OTG core. + * If we don't do this then xhci_reset (USBCMD.HCRST) will reset + * the signal outputs sent to the PHY, the OTG FSM logic of the + * core and also the resets to the VBUS filters inside the core. + */ + reg = dwc3_readl(dwc->regs, DWC3_OCFG); + reg |= DWC3_OCFG_SFTRSTMASK; + dwc3_writel(dwc->regs, DWC3_OCFG, reg); + + /* Disable hibernation for simplicity */ + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_GBLHIBERNATIONEN; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + /* + * Initialize OTG registers as per + * Figure 11-4 OTG Driver Overall Programming Flow + */ + /* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */ + reg = dwc3_readl(dwc->regs, DWC3_OCFG); + reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP); + dwc3_writel(dwc->regs, DWC3_OCFG, reg); + /* OEVT = FFFF */ + dwc3_otg_clear_events(dwc); + /* OEVTEN = 0 */ + dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS); + /* OEVTEN.ConIDStsChngEn = 1. Instead we enable all events */ + dwc3_otg_enable_events(dwc, DWC3_OTG_ALL_EVENTS); + /* + * OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0, + * OCTL.HNPReq = 0 + */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg |= DWC3_OCTL_PERIMODE; + reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN | + DWC3_OCTL_HNPREQ); + dwc3_writel(dwc->regs, DWC3_OCTL, reg); +} + +static int dwc3_otg_get_irq(struct dwc3 *dwc) +{ + struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); + int irq; + + irq = platform_get_irq_byname(dwc3_pdev, "otg"); + if (irq > 0) + goto out; + + if (irq == -EPROBE_DEFER) + goto out; + + irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); + if (irq > 0) + goto out; + + if (irq == -EPROBE_DEFER) + goto out; + + irq = platform_get_irq(dwc3_pdev, 0); + if (irq > 0) + goto out; + + if (irq != -EPROBE_DEFER) + dev_err(dwc->dev, "missing OTG IRQ\n"); + + if (!irq) + irq = -EINVAL; + +out: + return irq; +} + +void dwc3_otg_init(struct dwc3 *dwc) +{ + u32 reg; + + /* + * As per Figure 11-4 OTG Driver Overall Programming Flow, + * block "Initialize GCTL for OTG operation". + */ + /* GCTL.PrtCapDir=2'b11 */ + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG); + /* GUSB2PHYCFG0.SusPHY=0 */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + /* Initialize OTG registers */ + dwc3_otgregs_init(dwc); +} + +void dwc3_otg_exit(struct dwc3 *dwc) +{ + /* disable all OTG IRQs */ + dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS); + /* clear all events */ + dwc3_otg_clear_events(dwc); +} + +/* should be called before Host controller driver is started */ +void dwc3_otg_host_init(struct dwc3 *dwc) +{ + u32 reg; + + /* As per Figure 11-10 A-Device Flow Diagram */ + /* OCFG.HNPCap = 0, OCFG.SRPCap = 0. Already 0 */ + + /* + * OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0, + * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0 + */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE | + DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN); + dwc3_writel(dwc->regs, DWC3_OCTL, reg); + + /* + * OCFG.DisPrtPwrCutoff = 0/1 + */ + reg = dwc3_readl(dwc->regs, DWC3_OCFG); + reg &= ~DWC3_OCFG_DISPWRCUTTOFF; + dwc3_writel(dwc->regs, DWC3_OCFG, reg); + + /* + * OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP + * We don't want SRP/HNP for simple dual-role so leave + * these disabled. + */ + + /* + * OEVTEN.OTGADevHostEvntEn = 1 + * OEVTEN.OTGADevSessEndDetEvntEn = 1 + * We don't want HNP/role-swap so leave these disabled. + */ + + /* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */ + if (!dwc->dis_u2_susphy_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } + + /* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg |= DWC3_OCTL_PRTPWRCTL; + dwc3_writel(dwc->regs, DWC3_OCTL, reg); +} + +/* should be called after Host controller driver is stopped */ +static void dwc3_otg_host_exit(struct dwc3 *dwc) +{ + u32 reg; + + /* + * Exit from A-device flow as per + * Figure 11-4 OTG Driver Overall Programming Flow + */ + + /* + * OEVTEN.OTGADevBHostEndEvntEn=0, OEVTEN.OTGADevHNPChngEvntEn=0 + * OEVTEN.OTGADevSessEndDetEvntEn=0, + * OEVTEN.OTGADevHostEvntEn = 0 + * But we don't disable any OTG events + */ + + /* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL); + dwc3_writel(dwc->regs, DWC3_OCTL, reg); +} + +/* should be called before the gadget controller driver is started */ +static void dwc3_otg_device_init(struct dwc3 *dwc) +{ + u32 reg; + + /* As per Figure 11-20 B-Device Flow Diagram */ + + /* + * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1 + * but we keep them 0 for simple dual-role operation. + */ + reg = dwc3_readl(dwc->regs, DWC3_OCFG); + /* OCFG.OTGSftRstMsk = 0/1 */ + reg |= DWC3_OCFG_SFTRSTMASK; + dwc3_writel(dwc->regs, DWC3_OCFG, reg); + /* + * OCTL.PeriMode = 1 + * OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0 + * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0 + */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg |= DWC3_OCTL_PERIMODE; + reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ | + DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN); + dwc3_writel(dwc->regs, DWC3_OCTL, reg); + /* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */ + dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN); + /* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */ + if (!dwc->dis_u2_susphy_quirk) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } + /* GCTL.GblHibernationEn = 0. Already 0. */ +} + +/* should be called after the gadget controller driver is stopped */ +static void dwc3_otg_device_exit(struct dwc3 *dwc) +{ + u32 reg; + + /* + * Exit from B-device flow as per + * Figure 11-4 OTG Driver Overall Programming Flow + */ + + /* + * OEVTEN.OTGBDevHNPChngEvntEn = 0 + * OEVTEN.OTGBDevVBusChngEvntEn = 0 + * OEVTEN.OTGBDevBHostEndEvntEn = 0 + */ + dwc3_otg_disable_events(dwc, DWC3_OEVTEN_BDEVHNPCHNGEN | + DWC3_OEVTEN_BDEVVBUSCHNGEN | + DWC3_OEVTEN_BDEVBHOSTENDEN); + + /* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */ + reg = dwc3_readl(dwc->regs, DWC3_OCTL); + reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ); + reg |= DWC3_OCTL_PERIMODE; + dwc3_writel(dwc->regs, DWC3_OCTL, reg); +} + +void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) +{ + int ret; + u32 reg; int id; + unsigned long flags; - id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); - if (id < 0) - id = 0; + if (dwc->dr_mode != USB_DR_MODE_OTG) + return; - dwc3_set_mode(dwc, id ? - DWC3_GCTL_PRTCAP_HOST : - DWC3_GCTL_PRTCAP_DEVICE); + /* don't do anything if debug user changed role to not OTG */ + if (dwc->current_dr_role != DWC3_GCTL_PRTCAP_OTG) + return; + + if (!ignore_idstatus) { + reg = dwc3_readl(dwc->regs, DWC3_OSTS); + id = !!(reg & DWC3_OSTS_CONIDSTS); + + dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE : + DWC3_OTG_ROLE_HOST; + } + + if (dwc->desired_otg_role == dwc->current_otg_role) + return; + + switch (dwc->current_otg_role) { + case DWC3_OTG_ROLE_HOST: + dwc3_host_exit(dwc); + spin_lock_irqsave(&dwc->lock, flags); + dwc3_otg_host_exit(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + break; + case DWC3_OTG_ROLE_DEVICE: + dwc3_gadget_exit(dwc); + spin_lock_irqsave(&dwc->lock, flags); + dwc3_event_buffers_cleanup(dwc); + dwc3_otg_device_exit(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + break; + default: + break; + } + + spin_lock_irqsave(&dwc->lock, flags); + + dwc->current_otg_role = dwc->desired_otg_role; + + spin_unlock_irqrestore(&dwc->lock, flags); + + switch (dwc->desired_otg_role) { + case DWC3_OTG_ROLE_HOST: + spin_lock_irqsave(&dwc->lock, flags); + dwc3_otgregs_init(dwc); + dwc3_otg_host_init(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + ret = dwc3_host_init(dwc); + if (ret) { + dev_err(dwc->dev, "failed to initialize host\n"); + } else { + if (dwc->usb2_phy) + otg_set_vbus(dwc->usb2_phy->otg, true); + if (dwc->usb2_generic_phy) + phy_set_mode(dwc->usb2_generic_phy, + PHY_MODE_USB_HOST); + } + break; + case DWC3_OTG_ROLE_DEVICE: + spin_lock_irqsave(&dwc->lock, flags); + dwc3_otgregs_init(dwc); + dwc3_otg_device_init(dwc); + dwc3_event_buffers_setup(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + + if (dwc->usb2_phy) + otg_set_vbus(dwc->usb2_phy->otg, false); + if (dwc->usb2_generic_phy) + phy_set_mode(dwc->usb2_generic_phy, + PHY_MODE_USB_DEVICE); + ret = dwc3_gadget_init(dwc); + if (ret) + dev_err(dwc->dev, "failed to initialize peripheral\n"); + break; + default: + break; + } +} + +static void dwc3_drd_update(struct dwc3 *dwc) +{ + int id; + + if (dwc->edev) { + id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); + if (id < 0) + id = 0; + dwc3_set_mode(dwc, id ? + DWC3_GCTL_PRTCAP_HOST : + DWC3_GCTL_PRTCAP_DEVICE); + } } static int dwc3_drd_notifier(struct notifier_block *nb, @@ -40,11 +441,11 @@ static int dwc3_drd_notifier(struct notifier_block *nb, int dwc3_drd_init(struct dwc3 *dwc) { - int ret; + int ret, irq; - if (dwc->dev->of_node) { - if (of_property_read_bool(dwc->dev->of_node, "extcon")) - dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0); + if (dwc->dev->of_node && + of_property_read_bool(dwc->dev->of_node, "extcon")) { + dwc->edev = extcon_get_edev_by_phandle(dwc->dev, 0); if (IS_ERR(dwc->edev)) return PTR_ERR(dwc->edev); @@ -56,19 +457,71 @@ int dwc3_drd_init(struct dwc3 *dwc) dev_err(dwc->dev, "couldn't register cable notifier\n"); return ret; } - } - dwc3_drd_update(dwc); + dwc3_drd_update(dwc); + } else { + dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG); + dwc->current_dr_role = DWC3_GCTL_PRTCAP_OTG; + + /* use OTG block to get ID event */ + irq = dwc3_otg_get_irq(dwc); + if (irq < 0) + return irq; + + dwc->otg_irq = irq; + + /* disable all OTG IRQs */ + dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS); + /* clear all events */ + dwc3_otg_clear_events(dwc); + + ret = request_threaded_irq(dwc->otg_irq, dwc3_otg_irq, + dwc3_otg_thread_irq, + IRQF_SHARED, "dwc3-otg", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + dwc->otg_irq, ret); + ret = -ENODEV; + return ret; + } + + dwc3_otg_init(dwc); + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); + } return 0; } void dwc3_drd_exit(struct dwc3 *dwc) { - extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, - &dwc->edev_nb); + unsigned long flags; + + if (dwc->edev) + extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, + &dwc->edev_nb); + + cancel_work_sync(&dwc->drd_work); + + /* debug user might have changed role, clean based on current role */ + switch (dwc->current_dr_role) { + case DWC3_GCTL_PRTCAP_HOST: + dwc3_host_exit(dwc); + break; + case DWC3_GCTL_PRTCAP_DEVICE: + dwc3_gadget_exit(dwc); + dwc3_event_buffers_cleanup(dwc); + break; + case DWC3_GCTL_PRTCAP_OTG: + dwc3_otg_exit(dwc); + spin_lock_irqsave(&dwc->lock, flags); + dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE; + spin_unlock_irqrestore(&dwc->lock, flags); + dwc3_otg_update(dwc, 1); + break; + default: + break; + } - dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); - flush_work(&dwc->drd_work); - dwc3_gadget_exit(dwc); + if (!dwc->edev) + free_irq(dwc->otg_irq, dwc); } diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index e54c3622eb28..cb2ee96fd3e8 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -27,6 +27,7 @@ struct dwc3_of_simple { struct clk **clks; int num_clocks; struct reset_control *resets; + bool pulse_resets; }; static int dwc3_of_simple_clk_init(struct dwc3_of_simple *simple, int count) @@ -83,6 +84,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) int ret; int i; + bool shared_resets = false; simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL); if (!simple) @@ -91,16 +93,28 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) platform_set_drvdata(pdev, simple); simple->dev = dev; - simple->resets = of_reset_control_array_get_optional_exclusive(np); + if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") || + of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) { + shared_resets = true; + simple->pulse_resets = true; + } + + simple->resets = of_reset_control_array_get(np, shared_resets, true); if (IS_ERR(simple->resets)) { ret = PTR_ERR(simple->resets); dev_err(dev, "failed to get device resets, err=%d\n", ret); return ret; } - ret = reset_control_deassert(simple->resets); - if (ret) - goto err_resetc_put; + if (simple->pulse_resets) { + ret = reset_control_reset(simple->resets); + if (ret) + goto err_resetc_put; + } else { + ret = reset_control_deassert(simple->resets); + if (ret) + goto err_resetc_put; + } ret = dwc3_of_simple_clk_init(simple, of_count_phandle_with_args(np, "clocks", "#clock-cells")); @@ -124,7 +138,8 @@ static int dwc3_of_simple_probe(struct platform_device *pdev) return 0; err_resetc_assert: - reset_control_assert(simple->resets); + if (!simple->pulse_resets) + reset_control_assert(simple->resets); err_resetc_put: reset_control_put(simple->resets); @@ -145,7 +160,9 @@ static int dwc3_of_simple_remove(struct platform_device *pdev) } simple->num_clocks = 0; - reset_control_assert(simple->resets); + if (!simple->pulse_resets) + reset_control_assert(simple->resets); + reset_control_put(simple->resets); pm_runtime_put_sync(dev); @@ -196,6 +213,8 @@ static const struct of_device_id of_dwc3_simple_match[] = { { .compatible = "xlnx,zynqmp-dwc3" }, { .compatible = "cavium,octeon-7130-usb-uctl" }, { .compatible = "sprd,sc9860-dwc3" }, + { .compatible = "amlogic,meson-axg-dwc3" }, + { .compatible = "amlogic,meson-gxl-dwc3" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 3ba11136ebf0..c961a94d136b 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -222,7 +222,7 @@ static int dwc3_pci_probe(struct pci_dev *pci, ret = platform_device_add_resources(dwc->dwc3, res, ARRAY_SIZE(res)); if (ret) { dev_err(dev, "couldn't add resources to dwc3 device\n"); - return ret; + goto err; } dwc->pci = pci; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 18be31d5743a..5a991bca8ed7 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -814,7 +814,7 @@ out: static void dwc3_ep0_complete_data(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { - struct dwc3_request *r = NULL; + struct dwc3_request *r; struct usb_request *ur; struct dwc3_trb *trb; struct dwc3_ep *ep0; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2bda4eb1e9ac..8796a5ee9bb9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -166,18 +166,8 @@ static void dwc3_ep_inc_deq(struct dwc3_ep *dep) dwc3_ep_inc_trb(&dep->trb_dequeue); } -/** - * dwc3_gadget_giveback - call struct usb_request's ->complete callback - * @dep: The endpoint to whom the request belongs to - * @req: The request we're giving back - * @status: completion code for the request - * - * Must be called with controller's lock held and interrupts disabled. This - * function will unmap @req and call its ->complete() callback to notify upper - * layers that it has completed. - */ -void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, - int status) +void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep, + struct dwc3_request *req, int status) { struct dwc3 *dwc = dep->dwc; @@ -190,18 +180,35 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, if (req->trb) usb_gadget_unmap_request_by_dev(dwc->sysdev, - &req->request, req->direction); + &req->request, req->direction); req->trb = NULL; - trace_dwc3_gadget_giveback(req); + if (dep->number > 1) + pm_runtime_put(dwc->dev); +} + +/** + * dwc3_gadget_giveback - call struct usb_request's ->complete callback + * @dep: The endpoint to whom the request belongs to + * @req: The request we're giving back + * @status: completion code for the request + * + * Must be called with controller's lock held and interrupts disabled. This + * function will unmap @req and call its ->complete() callback to notify upper + * layers that it has completed. + */ +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status) +{ + struct dwc3 *dwc = dep->dwc; + + dwc3_gadget_del_and_unmap_request(dep, req, status); + spin_unlock(&dwc->lock); usb_gadget_giveback_request(&dep->endpoint, &req->request); spin_lock(&dwc->lock); - - if (dep->number > 1) - pm_runtime_put(dwc->dev); } /** @@ -1227,7 +1234,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep) if (req->trb) memset(req->trb, 0, sizeof(struct dwc3_trb)); dep->queued_requests--; - dwc3_gadget_giveback(dep, req, ret); + dwc3_gadget_del_and_unmap_request(dep, req, ret); return ret; } @@ -1858,7 +1865,11 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) * bursts of data without going through any sort of endpoint throttling. */ reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG); - reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL; + if (dwc3_is_usb31(dwc)) + reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL; + else + reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL; + dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); dwc3_gadget_setup_nump(dwc); @@ -1950,6 +1961,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g) struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; int epnum; + u32 tmo_eps = 0; spin_lock_irqsave(&dwc->lock, flags); @@ -1960,6 +1972,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g) for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) { struct dwc3_ep *dep = dwc->eps[epnum]; + int ret; if (!dep) continue; @@ -1967,9 +1980,24 @@ static int dwc3_gadget_stop(struct usb_gadget *g) if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) continue; - wait_event_lock_irq(dep->wait_end_transfer, - !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), - dwc->lock); + ret = wait_event_interruptible_lock_irq_timeout(dep->wait_end_transfer, + !(dep->flags & DWC3_EP_END_TRANSFER_PENDING), + dwc->lock, msecs_to_jiffies(5)); + + if (ret <= 0) { + /* Timed out or interrupted! There's nothing much + * we can do so we just log here and print which + * endpoints timed out at the end. + */ + tmo_eps |= 1 << epnum; + dep->flags &= DWC3_EP_END_TRANSFER_PENDING; + } + } + + if (tmo_eps) { + dev_err(dwc->dev, + "end transfer timed out on endpoints 0x%x [bitmap]\n", + tmo_eps); } out: @@ -2023,7 +2051,10 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g, reg |= DWC3_DCFG_SUPERSPEED; break; case USB_SPEED_SUPER_PLUS: - reg |= DWC3_DCFG_SUPERSPEED_PLUS; + if (dwc3_is_usb31(dwc)) + reg |= DWC3_DCFG_SUPERSPEED_PLUS; + else + reg |= DWC3_DCFG_SUPERSPEED; break; default: dev_err(dwc->dev, "invalid speed (%d)\n", speed); @@ -2101,7 +2132,10 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total) mdwidth /= 8; size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num)); - size = DWC3_GTXFIFOSIZ_TXFDEF(size); + if (dwc3_is_usb31(dwc)) + size = DWC31_GTXFIFOSIZ_TXFDEF(size); + else + size = DWC3_GTXFIFOSIZ_TXFDEF(size); /* FIFO Depth is in MDWDITH bytes. Multiply */ size *= mdwidth; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 77c7ecca816a..63a7cb87514a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1422,11 +1422,12 @@ static int count_ext_compat(struct usb_configuration *c) return res; } -static void fill_ext_compat(struct usb_configuration *c, u8 *buf) +static int fill_ext_compat(struct usb_configuration *c, u8 *buf) { int i, count; count = 16; + buf += 16; for (i = 0; i < c->next_interface_id; ++i) { struct usb_function *f; int j; @@ -1449,10 +1450,12 @@ static void fill_ext_compat(struct usb_configuration *c, u8 *buf) buf += 23; } count += 24; - if (count >= 4096) - return; + if (count + 24 >= USB_COMP_EP0_OS_DESC_BUFSIZ) + return count; } } + + return count; } static int count_ext_prop(struct usb_configuration *c, int interface) @@ -1497,25 +1500,21 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) struct usb_os_desc *d; struct usb_os_desc_ext_prop *ext_prop; int j, count, n, ret; - u8 *start = buf; f = c->interface[interface]; + count = 10; /* header length */ + buf += 10; for (j = 0; j < f->os_desc_n; ++j) { if (interface != f->os_desc_table[j].if_id) continue; d = f->os_desc_table[j].os_desc; if (d) list_for_each_entry(ext_prop, &d->ext_prop, entry) { - /* 4kB minus header length */ - n = buf - start; - if (n >= 4086) - return 0; - - count = ext_prop->data_len + + n = ext_prop->data_len + ext_prop->name_len + 14; - if (count > 4086 - n) - return -EINVAL; - usb_ext_prop_put_size(buf, count); + if (count + n >= USB_COMP_EP0_OS_DESC_BUFSIZ) + return count; + usb_ext_prop_put_size(buf, n); usb_ext_prop_put_type(buf, ext_prop->type); ret = usb_ext_prop_put_name(buf, ext_prop->name, ext_prop->name_len); @@ -1541,11 +1540,12 @@ static int fill_ext_prop(struct usb_configuration *c, int interface, u8 *buf) default: return -EINVAL; } - buf += count; + buf += n; + count += n; } } - return 0; + return count; } /* @@ -1827,6 +1827,7 @@ unknown: req->complete = composite_setup_complete; buf = req->buf; os_desc_cfg = cdev->os_desc_config; + w_length = min_t(u16, w_length, USB_COMP_EP0_OS_DESC_BUFSIZ); memset(buf, 0, w_length); buf[5] = 0x01; switch (ctrl->bRequestType & USB_RECIP_MASK) { @@ -1834,24 +1835,16 @@ unknown: if (w_index != 0x4 || (w_value >> 8)) break; buf[6] = w_index; - if (w_length == 0x10) { - /* Number of ext compat interfaces */ - count = count_ext_compat(os_desc_cfg); - buf[8] = count; - count *= 24; /* 24 B/ext compat desc */ - count += 16; /* header */ - put_unaligned_le32(count, buf); - value = w_length; - } else { - /* "extended compatibility ID"s */ - count = count_ext_compat(os_desc_cfg); - buf[8] = count; - count *= 24; /* 24 B/ext compat desc */ - count += 16; /* header */ - put_unaligned_le32(count, buf); - buf += 16; - fill_ext_compat(os_desc_cfg, buf); - value = w_length; + /* Number of ext compat interfaces */ + count = count_ext_compat(os_desc_cfg); + buf[8] = count; + count *= 24; /* 24 B/ext compat desc */ + count += 16; /* header */ + put_unaligned_le32(count, buf); + value = w_length; + if (w_length > 0x10) { + value = fill_ext_compat(os_desc_cfg, buf); + value = min_t(u16, w_length, value); } break; case USB_RECIP_INTERFACE: @@ -1859,47 +1852,23 @@ unknown: break; interface = w_value & 0xFF; buf[6] = w_index; - if (w_length == 0x0A) { - count = count_ext_prop(os_desc_cfg, - interface); - put_unaligned_le16(count, buf + 8); - count = len_ext_prop(os_desc_cfg, - interface); - put_unaligned_le32(count, buf); - - value = w_length; - } else { - count = count_ext_prop(os_desc_cfg, - interface); - put_unaligned_le16(count, buf + 8); - count = len_ext_prop(os_desc_cfg, - interface); - put_unaligned_le32(count, buf); - buf += 10; + count = count_ext_prop(os_desc_cfg, + interface); + put_unaligned_le16(count, buf + 8); + count = len_ext_prop(os_desc_cfg, + interface); + put_unaligned_le32(count, buf); + value = w_length; + if (w_length > 0x0A) { value = fill_ext_prop(os_desc_cfg, interface, buf); - if (value < 0) - return value; - - value = w_length; + if (value >= 0) + value = min_t(u16, w_length, value); } break; } - if (value >= 0) { - req->length = value; - req->context = cdev; - req->zero = value < w_length; - value = composite_ep0_queue(cdev, req, - GFP_ATOMIC); - if (value < 0) { - DBG(cdev, "ep_queue --> %d\n", value); - req->status = 0; - composite_setup_complete(gadget->ep0, - req); - } - } - return value; + goto check_value; } VDBG(cdev, @@ -1973,6 +1942,7 @@ try_fun_setup: goto done; } +check_value: /* respond with data transfer before status phase? */ if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) { req->length = value; @@ -2156,8 +2126,8 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, goto end; } - /* OS feature descriptor length <= 4kB */ - cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL); + cdev->os_desc_req->buf = kmalloc(USB_COMP_EP0_OS_DESC_BUFSIZ, + GFP_KERNEL); if (!cdev->os_desc_req->buf) { ret = -ENOMEM; usb_ep_free_request(ep0, cdev->os_desc_req); @@ -2172,6 +2142,7 @@ end: void composite_dev_cleanup(struct usb_composite_dev *cdev) { struct usb_gadget_string_container *uc, *tmp; + struct usb_ep *ep, *tmp_ep; list_for_each_entry_safe(uc, tmp, &cdev->gstrings, list) { list_del(&uc->list); @@ -2193,6 +2164,21 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev) } cdev->next_string_id = 0; device_remove_file(&cdev->gadget->dev, &dev_attr_suspended); + + /* + * Some UDC backends have a dynamic EP allocation scheme. + * + * In that case, the dispose() callback is used to notify the + * backend that the EPs are no longer in use. + * + * Note: The UDC backend can remove the EP from the ep_list as + * a result, so we need to use the _safe list iterator. + */ + list_for_each_entry_safe(ep, tmp_ep, + &cdev->gadget->ep_list, ep_list) { + if (ep->ops->dispose) + ep->ops->dispose(ep); + } } static int composite_bind(struct usb_gadget *gadget, diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index d2428a9e8900..0294e4f18873 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -758,9 +758,13 @@ static void ffs_user_copy_worker(struct work_struct *work) bool kiocb_has_eventfd = io_data->kiocb->ki_flags & IOCB_EVENTFD; if (io_data->read && ret > 0) { + mm_segment_t oldfs = get_fs(); + + set_fs(USER_DS); use_mm(io_data->mm); ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data); unuse_mm(io_data->mm); + set_fs(oldfs); } io_data->kiocb->ki_complete(io_data->kiocb, ret, ret); @@ -3238,7 +3242,7 @@ static int ffs_func_setup(struct usb_function *f, __ffs_event_add(ffs, FUNCTIONFS_SETUP); spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags); - return 0; + return USB_GADGET_DELAYED_STATUS; } static bool ffs_func_req_match(struct usb_function *f, diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 4eb96b91cc40..e8f35db42394 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -404,7 +404,8 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (err) { ERROR(midi, "%s: couldn't enqueue request: %d\n", midi->out_ep->name, err); - free_ep_req(midi->out_ep, req); + if (req->buf != NULL) + free_ep_req(midi->out_ep, req); return err; } } diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index da81cf16b850..d78dbb73bde8 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -1094,7 +1094,7 @@ static int usbg_submit_command(struct f_uas *fu, struct command_iu *cmd_iu = cmdbuf; struct usbg_cmd *cmd; struct usbg_tpg *tpg = fu->tpg; - struct tcm_usbg_nexus *tv_nexus = tpg->tpg_nexus; + struct tcm_usbg_nexus *tv_nexus; u32 cmd_len; u16 scsi_tag; diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index ef3d25259b0e..fd5595ac5bf7 100644 --- a/drivers/usb/gadget/legacy/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c @@ -225,7 +225,7 @@ static int msg_unbind(struct usb_composite_dev *cdev) static struct usb_composite_driver msg_driver = { .name = "g_mass_storage", .dev = &msg_device_desc, - .max_speed = USB_SPEED_SUPER, + .max_speed = USB_SPEED_SUPER_PLUS, .needs_serial = 1, .strings = dev_strings, .bind = msg_bind, diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h index c3fbef2bb5db..09f90447fed5 100644 --- a/drivers/usb/gadget/u_f.h +++ b/drivers/usb/gadget/u_f.h @@ -61,7 +61,9 @@ struct usb_request *alloc_ep_req(struct usb_ep *ep, size_t len); /* Frees a usb_request previously allocated by alloc_ep_req() */ static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req) { + WARN_ON(req->buf == NULL); kfree(req->buf); + req->buf = NULL; usb_ep_free_request(ep, req); } diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 075eaaa8a408..27c16399c7e8 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -23,7 +23,8 @@ #include <linux/usb/atmel_usba_udc.h> #include <linux/delay.h> #include <linux/of.h> -#include <linux/of_gpio.h> +#include <linux/irq.h> +#include <linux/gpio/consumer.h> #include "atmel_usba_udc.h" #define USBA_VBUS_IRQFLAGS (IRQF_ONESHOT \ @@ -415,8 +416,8 @@ static inline void usba_int_enb_set(struct usba_udc *udc, u32 val) static int vbus_is_present(struct usba_udc *udc) { - if (gpio_is_valid(udc->vbus_pin)) - return gpio_get_value(udc->vbus_pin) ^ udc->vbus_pin_inverted; + if (udc->vbus_pin) + return gpiod_get_value(udc->vbus_pin) ^ udc->vbus_pin_inverted; /* No Vbus detection: Assume always present */ return 1; @@ -1975,8 +1976,8 @@ static int atmel_usba_start(struct usb_gadget *gadget, mutex_lock(&udc->vbus_mutex); - if (gpio_is_valid(udc->vbus_pin)) - enable_irq(gpio_to_irq(udc->vbus_pin)); + if (udc->vbus_pin) + enable_irq(gpiod_to_irq(udc->vbus_pin)); /* If Vbus is present, enable the controller and wait for reset */ udc->vbus_prev = vbus_is_present(udc); @@ -1990,8 +1991,8 @@ static int atmel_usba_start(struct usb_gadget *gadget, return 0; err: - if (gpio_is_valid(udc->vbus_pin)) - disable_irq(gpio_to_irq(udc->vbus_pin)); + if (udc->vbus_pin) + disable_irq(gpiod_to_irq(udc->vbus_pin)); mutex_unlock(&udc->vbus_mutex); @@ -2006,8 +2007,8 @@ static int atmel_usba_stop(struct usb_gadget *gadget) { struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget); - if (gpio_is_valid(udc->vbus_pin)) - disable_irq(gpio_to_irq(udc->vbus_pin)); + if (udc->vbus_pin) + disable_irq(gpiod_to_irq(udc->vbus_pin)); if (fifo_mode == 0) udc->configured_ep = 1; @@ -2019,7 +2020,6 @@ static int atmel_usba_stop(struct usb_gadget *gadget) return 0; } -#ifdef CONFIG_OF static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on) { regmap_update_bits(udc->pmc, AT91_CKGR_UCKR, AT91_PMC_BIASEN, @@ -2055,7 +2055,6 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, { u32 val; const char *name; - enum of_gpio_flags flags; struct device_node *np = pdev->dev.of_node; const struct of_device_id *match; struct device_node *pp; @@ -2075,9 +2074,9 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, udc->num_ep = 0; - udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0, - &flags); - udc->vbus_pin_inverted = (flags & OF_GPIO_ACTIVE_LOW) ? 1 : 0; + udc->vbus_pin = devm_gpiod_get_optional(&pdev->dev, "atmel,vbus", + GPIOD_IN); + udc->vbus_pin_inverted = gpiod_is_active_low(udc->vbus_pin); if (fifo_mode == 0) { pp = NULL; @@ -2204,75 +2203,10 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, err: return ERR_PTR(ret); } -#else -static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, - struct usba_udc *udc) -{ - return ERR_PTR(-ENOSYS); -} -#endif - -static struct usba_ep * usba_udc_pdata(struct platform_device *pdev, - struct usba_udc *udc) -{ - struct usba_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct usba_ep *eps; - int i; - - if (!pdata) - return ERR_PTR(-ENXIO); - - eps = devm_kzalloc(&pdev->dev, sizeof(struct usba_ep) * pdata->num_ep, - GFP_KERNEL); - if (!eps) - return ERR_PTR(-ENOMEM); - - udc->gadget.ep0 = &eps[0].ep; - - udc->vbus_pin = pdata->vbus_pin; - udc->vbus_pin_inverted = pdata->vbus_pin_inverted; - udc->num_ep = pdata->num_ep; - - INIT_LIST_HEAD(&eps[0].ep.ep_list); - - for (i = 0; i < pdata->num_ep; i++) { - struct usba_ep *ep = &eps[i]; - - ep->ep_regs = udc->regs + USBA_EPT_BASE(i); - ep->dma_regs = udc->regs + USBA_DMA_BASE(i); - ep->fifo = udc->fifo + USBA_FIFO_BASE(i); - ep->ep.ops = &usba_ep_ops; - ep->ep.name = pdata->ep[i].name; - ep->fifo_size = pdata->ep[i].fifo_size; - usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size); - ep->udc = udc; - INIT_LIST_HEAD(&ep->queue); - ep->nr_banks = pdata->ep[i].nr_banks; - ep->index = pdata->ep[i].index; - ep->can_dma = pdata->ep[i].can_dma; - ep->can_isoc = pdata->ep[i].can_isoc; - - if (i == 0) { - ep->ep.caps.type_control = true; - } else { - ep->ep.caps.type_iso = ep->can_isoc; - ep->ep.caps.type_bulk = true; - ep->ep.caps.type_int = true; - } - - ep->ep.caps.dir_in = true; - ep->ep.caps.dir_out = true; - - if (i) - list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); - } - - return eps; -} static int usba_udc_probe(struct platform_device *pdev) { - struct resource *regs, *fifo; + struct resource *res; struct clk *pclk, *hclk; struct usba_udc *udc; int irq, ret, i; @@ -2284,10 +2218,18 @@ static int usba_udc_probe(struct platform_device *pdev) udc->gadget = usba_gadget_template; INIT_LIST_HEAD(&udc->gadget.ep_list); - regs = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); - fifo = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID); - if (!regs || !fifo) - return -ENXIO; + res = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); + udc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(udc->regs)) + return PTR_ERR(udc->regs); + dev_info(&pdev->dev, "MMIO registers at %pR mapped at %p\n", + res, udc->regs); + + res = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID); + udc->fifo = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(udc->fifo)) + return PTR_ERR(udc->fifo); + dev_info(&pdev->dev, "FIFO at %pR mapped at %p\n", res, udc->fifo); irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -2305,23 +2247,6 @@ static int usba_udc_probe(struct platform_device *pdev) udc->pdev = pdev; udc->pclk = pclk; udc->hclk = hclk; - udc->vbus_pin = -ENODEV; - - ret = -ENOMEM; - udc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); - if (!udc->regs) { - dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n"); - return ret; - } - dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n", - (unsigned long)regs->start, udc->regs); - udc->fifo = devm_ioremap(&pdev->dev, fifo->start, resource_size(fifo)); - if (!udc->fifo) { - dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n"); - return ret; - } - dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n", - (unsigned long)fifo->start, udc->fifo); platform_set_drvdata(pdev, udc); @@ -2335,10 +2260,7 @@ static int usba_udc_probe(struct platform_device *pdev) usba_writel(udc, CTRL, USBA_DISABLE_MASK); clk_disable_unprepare(pclk); - if (pdev->dev.of_node) - udc->usba_ep = atmel_udc_of_init(pdev, udc); - else - udc->usba_ep = usba_udc_pdata(pdev, udc); + udc->usba_ep = atmel_udc_of_init(pdev, udc); toggle_bias(udc, 0); @@ -2354,24 +2276,18 @@ static int usba_udc_probe(struct platform_device *pdev) } udc->irq = irq; - if (gpio_is_valid(udc->vbus_pin)) { - if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) { - irq_set_status_flags(gpio_to_irq(udc->vbus_pin), - IRQ_NOAUTOEN); - ret = devm_request_threaded_irq(&pdev->dev, - gpio_to_irq(udc->vbus_pin), NULL, + if (udc->vbus_pin) { + irq_set_status_flags(gpiod_to_irq(udc->vbus_pin), IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(&pdev->dev, + gpiod_to_irq(udc->vbus_pin), NULL, usba_vbus_irq_thread, USBA_VBUS_IRQFLAGS, "atmel_usba_udc", udc); if (ret) { - udc->vbus_pin = -ENODEV; + udc->vbus_pin = NULL; dev_warn(&udc->pdev->dev, "failed to request vbus irq; " "assuming always on\n"); } - } else { - /* gpio_request fail so use -EINVAL for gpio_is_valid */ - udc->vbus_pin = -EINVAL; - } } ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); @@ -2423,9 +2339,9 @@ static int usba_udc_suspend(struct device *dev) * Device may wake up. We stay clocked if we failed * to request vbus irq, assuming always on. */ - if (gpio_is_valid(udc->vbus_pin)) { + if (udc->vbus_pin) { usba_stop(udc); - enable_irq_wake(gpio_to_irq(udc->vbus_pin)); + enable_irq_wake(gpiod_to_irq(udc->vbus_pin)); } out: @@ -2441,8 +2357,8 @@ static int usba_udc_resume(struct device *dev) if (!udc->driver) return 0; - if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin)) - disable_irq_wake(gpio_to_irq(udc->vbus_pin)); + if (device_may_wakeup(dev) && udc->vbus_pin) + disable_irq_wake(gpiod_to_irq(udc->vbus_pin)); /* If Vbus is present, enable the controller and wait for reset */ mutex_lock(&udc->vbus_mutex); @@ -2462,7 +2378,7 @@ static struct platform_driver udc_driver = { .driver = { .name = "atmel_usba_udc", .pm = &usba_udc_pm_ops, - .of_match_table = of_match_ptr(atmel_udc_dt_ids), + .of_match_table = atmel_udc_dt_ids, }, }; diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index 860a00a6fdd0..969ce8f3c3e2 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -7,6 +7,8 @@ #ifndef __LINUX_USB_GADGET_USBA_UDC_H__ #define __LINUX_USB_GADGET_USBA_UDC_H__ +#include <linux/gpio/consumer.h> + /* USB register offsets */ #define USBA_CTRL 0x0000 #define USBA_FNUM 0x0004 @@ -323,7 +325,7 @@ struct usba_udc { struct platform_device *pdev; const struct usba_udc_errata *errata; int irq; - int vbus_pin; + struct gpio_desc *vbus_pin; int vbus_pin_inverted; int num_ep; int configured_ep; diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index 465ccd1104de..3a8df8601074 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2158,6 +2158,7 @@ static int bcm63xx_usbd_dbg_show(struct seq_file *s, void *p) return 0; } +DEFINE_SHOW_ATTRIBUTE(bcm63xx_usbd_dbg); /* * bcm63xx_iudma_dbg_show - Show IUDMA status and descriptors. @@ -2238,33 +2239,7 @@ static int bcm63xx_iudma_dbg_show(struct seq_file *s, void *p) return 0; } - -static int bcm63xx_usbd_dbg_open(struct inode *inode, struct file *file) -{ - return single_open(file, bcm63xx_usbd_dbg_show, inode->i_private); -} - -static int bcm63xx_iudma_dbg_open(struct inode *inode, struct file *file) -{ - return single_open(file, bcm63xx_iudma_dbg_show, inode->i_private); -} - -static const struct file_operations usbd_dbg_fops = { - .owner = THIS_MODULE, - .open = bcm63xx_usbd_dbg_open, - .llseek = seq_lseek, - .read = seq_read, - .release = single_release, -}; - -static const struct file_operations iudma_dbg_fops = { - .owner = THIS_MODULE, - .open = bcm63xx_iudma_dbg_open, - .llseek = seq_lseek, - .read = seq_read, - .release = single_release, -}; - +DEFINE_SHOW_ATTRIBUTE(bcm63xx_iudma_dbg); /** * bcm63xx_udc_init_debugfs - Create debugfs entries. @@ -2282,11 +2257,11 @@ static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc) goto err_root; usbd = debugfs_create_file("usbd", 0400, root, udc, - &usbd_dbg_fops); + &bcm63xx_usbd_dbg_fops); if (!usbd) goto err_usbd; iudma = debugfs_create_file("iudma", 0400, root, udc, - &iudma_dbg_fops); + &bcm63xx_iudma_dbg_fops); if (!iudma) goto err_iudma; diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index f40d4c13cfa4..03149b9d7ea7 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -151,7 +151,7 @@ static int ep_bd_list_alloc(struct bdc_ep *ep) if (!bd_table) goto fail; - bd_table->start_bd = dma_pool_alloc(bdc->bd_table_pool, + bd_table->start_bd = dma_pool_zalloc(bdc->bd_table_pool, GFP_ATOMIC, &dma); if (!bd_table->start_bd) { @@ -167,7 +167,6 @@ static int ep_bd_list_alloc(struct bdc_ep *ep) (unsigned long long)bd_table->dma, prev_table); ep->bd_list.bd_table_array[index] = bd_table; - memset(bd_table->start_bd, 0, bd_p_tab * sizeof(struct bdc_bd)); if (prev_table) chain_table(prev_table, bd_table, bd_p_tab); diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 1f8b19d9cf97..842814bc0e4f 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -238,6 +238,9 @@ EXPORT_SYMBOL_GPL(usb_ep_free_request); * arranges to poll once per interval, and the gadget driver usually will * have queued some data to transfer at that time. * + * Note that @req's ->complete() callback must never be called from + * within usb_ep_queue() as that can create deadlock situations. + * * Returns zero, or a negative error code. Endpoints that are not enabled * report errors; errors will also be * reported when the usb peripheral is disconnected. @@ -1482,7 +1485,7 @@ ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ - return snprintf(buf, PAGE_SIZE, "%s\n", \ + return scnprintf(buf, PAGE_SIZE, "%s\n", \ usb_speed_string(udc->gadget->param)); \ } \ static DEVICE_ATTR_RO(name) @@ -1497,7 +1500,7 @@ ssize_t name##_show(struct device *dev, \ struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ struct usb_gadget *gadget = udc->gadget; \ \ - return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ + return scnprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ } \ static DEVICE_ATTR_RO(name) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index e744d4b7bfed..baf72f95f0f1 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2366,7 +2366,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb) { int ep = usb_pipeendpoint(urb->pipe); - return snprintf(buf, size, + return scnprintf(buf, size, "urb/%p %s ep%d%s%s len %d/%d\n", urb, ({ char *s; diff --git a/drivers/usb/gadget/udc/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h index 26601bf4e7a9..70023d401079 100644 --- a/drivers/usb/gadget/udc/goku_udc.h +++ b/drivers/usb/gadget/udc/goku_udc.h @@ -25,7 +25,7 @@ struct goku_udc_regs { # define INT_EP1DATASET 0x00040 # define INT_EP2DATASET 0x00080 # define INT_EP3DATASET 0x00100 -#define INT_EPnNAK(n) (0x00100 < (n)) /* 0 < n < 4 */ +#define INT_EPnNAK(n) (0x00100 << (n)) /* 0 < n < 4 */ # define INT_EP1NAK 0x00200 # define INT_EP2NAK 0x00400 # define INT_EP3NAK 0x00800 diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index b3fb1bbdb854..ca83c15d8ea4 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -179,8 +179,7 @@ static void gr_seq_ep_show(struct seq_file *seq, struct gr_ep *ep) seq_puts(seq, "\n"); } - -static int gr_seq_show(struct seq_file *seq, void *v) +static int gr_dfs_show(struct seq_file *seq, void *v) { struct gr_udc *dev = seq->private; u32 control = gr_read32(&dev->regs->control); @@ -203,19 +202,7 @@ static int gr_seq_show(struct seq_file *seq, void *v) return 0; } - -static int gr_dfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, gr_seq_show, inode->i_private); -} - -static const struct file_operations gr_dfs_fops = { - .owner = THIS_MODULE, - .open = gr_dfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(gr_dfs); static void gr_dfs_create(struct gr_udc *dev) { diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 0e3f5faa000e..d4be53559f2e 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -1233,8 +1233,7 @@ static const struct usb_gadget_ops pxa25x_udc_ops = { #ifdef CONFIG_USB_GADGET_DEBUG_FS -static int -udc_seq_show(struct seq_file *m, void *_d) +static int udc_debug_show(struct seq_file *m, void *_d) { struct pxa25x_udc *dev = m->private; unsigned long flags; @@ -1335,25 +1334,12 @@ done: local_irq_restore(flags); return 0; } - -static int -udc_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, udc_seq_show, inode->i_private); -} - -static const struct file_operations debug_fops = { - .open = udc_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(udc_debug); #define create_debug_files(dev) \ do { \ dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \ - S_IRUGO, NULL, dev, &debug_fops); \ + S_IRUGO, NULL, dev, &udc_debug_fops); \ } while (0) #define remove_debug_files(dev) debugfs_remove(dev->debugfs_udc) diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index fadcf2653c3d..a58242e901df 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -131,6 +131,7 @@ static int state_dbg_show(struct seq_file *s, void *p) return 0; } +DEFINE_SHOW_ATTRIBUTE(state_dbg); static int queues_dbg_show(struct seq_file *s, void *p) { @@ -163,6 +164,7 @@ static int queues_dbg_show(struct seq_file *s, void *p) return 0; } +DEFINE_SHOW_ATTRIBUTE(queues_dbg); static int eps_dbg_show(struct seq_file *s, void *p) { @@ -199,45 +201,7 @@ static int eps_dbg_show(struct seq_file *s, void *p) return 0; } - -static int eps_dbg_open(struct inode *inode, struct file *file) -{ - return single_open(file, eps_dbg_show, inode->i_private); -} - -static int queues_dbg_open(struct inode *inode, struct file *file) -{ - return single_open(file, queues_dbg_show, inode->i_private); -} - -static int state_dbg_open(struct inode *inode, struct file *file) -{ - return single_open(file, state_dbg_show, inode->i_private); -} - -static const struct file_operations state_dbg_fops = { - .owner = THIS_MODULE, - .open = state_dbg_open, - .llseek = seq_lseek, - .read = seq_read, - .release = single_release, -}; - -static const struct file_operations queues_dbg_fops = { - .owner = THIS_MODULE, - .open = queues_dbg_open, - .llseek = seq_lseek, - .read = seq_read, - .release = single_release, -}; - -static const struct file_operations eps_dbg_fops = { - .owner = THIS_MODULE, - .open = eps_dbg_open, - .llseek = seq_lseek, - .read = seq_read, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(eps_dbg); static void pxa_init_debugfs(struct pxa_udc *udc) { diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index b85822f0c874..5d958da8e1bc 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -293,19 +293,6 @@ config USB_CNS3XXX_EHCI It is needed for high-speed (480Mbit/sec) USB 2.0 device support. -config USB_EHCI_ATH79 - bool "EHCI support for AR7XXX/AR9XXX SoCs (DEPRECATED)" - depends on (SOC_AR71XX || SOC_AR724X || SOC_AR913X || SOC_AR933X) - select USB_EHCI_ROOT_HUB_TT - select USB_EHCI_HCD_PLATFORM - default y - ---help--- - This option is deprecated now and the driver was removed, use - USB_EHCI_HCD_PLATFORM instead. - - Enables support for the built-in EHCI controller present - on the Atheros AR7XXX/AR9XXX SoCs. - config USB_EHCI_HCD_PLATFORM tristate "Generic EHCI driver for a platform device" default n @@ -490,18 +477,6 @@ config USB_OHCI_HCD_DAVINCI controller. This driver cannot currently be a loadable module because it lacks a proper PHY abstraction. -config USB_OHCI_ATH79 - bool "USB OHCI support for the Atheros AR71XX/AR7240 SoCs (DEPRECATED)" - depends on (SOC_AR71XX || SOC_AR724X) - select USB_OHCI_HCD_PLATFORM - default y - help - This option is deprecated now and the driver was removed, use - USB_OHCI_HCD_PLATFORM instead. - - Enables support for the built-in OHCI controller present on the - Atheros AR71XX/AR7240 SoCs. - config USB_OHCI_HCD_PPC_OF_BE bool "OHCI support for OF platform bus (big endian)" depends on PPC diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 4ede4ce12366..8a8cffe0b445 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -11,7 +11,7 @@ fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o -xhci-hcd-y := xhci.o xhci-mem.o +xhci-hcd-y := xhci.o xhci-mem.o xhci-ext-caps.o xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o xhci-hcd-y += xhci-trace.o diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index c5094cb88cd5..0a9fd2022acf 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -155,6 +155,8 @@ static int fsl_ehci_drv_probe(struct platform_device *pdev) retval = -ENODEV; goto err2; } + + hcd->skip_phy_initialization = 1; } #endif return retval; diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 21307d862af6..4c6c08b675b5 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -73,10 +73,9 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) if (!qh) goto done; qh->hw = (struct ehci_qh_hw *) - dma_pool_alloc(ehci->qh_pool, flags, &dma); + dma_pool_zalloc(ehci->qh_pool, flags, &dma); if (!qh->hw) goto fail; - memset(qh->hw, 0, sizeof *qh->hw); qh->qh_dma = dma; // INIT_LIST_HEAD (&qh->qh_list); INIT_LIST_HEAD (&qh->qtd_list); diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index b065a960adc2..4c306fb6b069 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -27,7 +27,6 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/reset.h> #include <linux/usb.h> @@ -44,8 +43,6 @@ struct ehci_platform_priv { struct clk *clks[EHCI_MAX_CLKS]; struct reset_control *rsts; - struct phy **phys; - int num_phys; bool reset_on_resume; }; @@ -80,7 +77,7 @@ static int ehci_platform_power_on(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); - int clk, ret, phy_num; + int clk, ret; for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) { ret = clk_prepare_enable(priv->clks[clk]); @@ -88,24 +85,8 @@ static int ehci_platform_power_on(struct platform_device *dev) goto err_disable_clks; } - for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - ret = phy_init(priv->phys[phy_num]); - if (ret) - goto err_exit_phy; - ret = phy_power_on(priv->phys[phy_num]); - if (ret) { - phy_exit(priv->phys[phy_num]); - goto err_exit_phy; - } - } - return 0; -err_exit_phy: - while (--phy_num >= 0) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } err_disable_clks: while (--clk >= 0) clk_disable_unprepare(priv->clks[clk]); @@ -117,12 +98,7 @@ static void ehci_platform_power_off(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); - int clk, phy_num; - - for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } + int clk; for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) if (priv->clks[clk]) @@ -149,7 +125,7 @@ static int ehci_platform_probe(struct platform_device *dev) struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); struct ehci_platform_priv *priv; struct ehci_hcd *ehci; - int err, irq, phy_num, clk = 0; + int err, irq, clk = 0; if (usb_disabled()) return -ENODEV; @@ -202,29 +178,6 @@ static int ehci_platform_probe(struct platform_device *dev) "has-transaction-translator")) hcd->has_tt = 1; - priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, - "phys", "#phy-cells"); - - if (priv->num_phys > 0) { - priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, - sizeof(struct phy *), GFP_KERNEL); - if (!priv->phys) - return -ENOMEM; - } else - priv->num_phys = 0; - - for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - priv->phys[phy_num] = devm_of_phy_get_by_index( - &dev->dev, dev->dev.of_node, phy_num); - if (IS_ERR(priv->phys[phy_num])) { - err = PTR_ERR(priv->phys[phy_num]); - goto err_put_hcd; - } else if (!hcd->phy) { - /* Avoiding phy_get() in usb_add_hcd() */ - hcd->phy = priv->phys[phy_num]; - } - } - for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); if (IS_ERR(priv->clks[clk])) { @@ -306,7 +259,7 @@ err_reset: err_put_clks: while (--clk >= 0) clk_put(priv->clks[clk]); -err_put_hcd: + if (pdata == &ehci_platform_defaults) dev->dev.platform_data = NULL; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index e56db44708bc..28e2a338b481 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1287,7 +1287,7 @@ itd_urb_transaction( } else { alloc_itd: spin_unlock_irqrestore(&ehci->lock, flags); - itd = dma_pool_alloc(ehci->itd_pool, mem_flags, + itd = dma_pool_zalloc(ehci->itd_pool, mem_flags, &itd_dma); spin_lock_irqsave(&ehci->lock, flags); if (!itd) { @@ -1297,7 +1297,6 @@ itd_urb_transaction( } } - memset(itd, 0, sizeof(*itd)); itd->itd_dma = itd_dma; itd->frame = NO_FRAME; list_add(&itd->itd_list, &sched->td_list); @@ -2081,7 +2080,7 @@ sitd_urb_transaction( } else { alloc_sitd: spin_unlock_irqrestore(&ehci->lock, flags); - sitd = dma_pool_alloc(ehci->sitd_pool, mem_flags, + sitd = dma_pool_zalloc(ehci->sitd_pool, mem_flags, &sitd_dma); spin_lock_irqsave(&ehci->lock, flags); if (!sitd) { @@ -2091,7 +2090,6 @@ sitd_urb_transaction( } } - memset(sitd, 0, sizeof(*sitd)); sitd->sitd_dma = sitd_dma; sitd->frame = NO_FRAME; list_add(&sitd->sitd_list, &iso_sched->td_list); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index c809f7d2f08f..a6f4389f7e88 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -461,6 +461,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto cleanup_clk_en; } hcd->usb_phy = u_phy; + hcd->skip_phy_initialization = 1; tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node, "nvidia,needs-double-reset"); diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c index fafa91189e45..ebf9bb219f75 100644 --- a/drivers/usb/host/fhci-dbg.c +++ b/drivers/usb/host/fhci-dbg.c @@ -55,6 +55,7 @@ static int fhci_dfs_regs_show(struct seq_file *s, void *v) return 0; } +DEFINE_SHOW_ATTRIBUTE(fhci_dfs_regs); static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v) { @@ -75,30 +76,7 @@ static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v) return 0; } - -static int fhci_dfs_regs_open(struct inode *inode, struct file *file) -{ - return single_open(file, fhci_dfs_regs_show, inode->i_private); -} - -static int fhci_dfs_irq_stat_open(struct inode *inode, struct file *file) -{ - return single_open(file, fhci_dfs_irq_stat_show, inode->i_private); -} - -static const struct file_operations fhci_dfs_regs_fops = { - .open = fhci_dfs_regs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations fhci_dfs_irq_stat_fops = { - .open = fhci_dfs_irq_stat_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(fhci_dfs_irq_stat); void fhci_dfs_create(struct fhci_hcd *fhci) { diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c index b964f9a51d87..a213ed6f07b5 100644 --- a/drivers/usb/host/imx21-dbg.c +++ b/drivers/usb/host/imx21-dbg.c @@ -245,6 +245,7 @@ static int debug_status_show(struct seq_file *s, void *v) return 0; } +DEFINE_SHOW_ATTRIBUTE(debug_status); static int debug_dmem_show(struct seq_file *s, void *v) { @@ -266,6 +267,7 @@ static int debug_dmem_show(struct seq_file *s, void *v) return 0; } +DEFINE_SHOW_ATTRIBUTE(debug_dmem); static int debug_etd_show(struct seq_file *s, void *v) { @@ -334,6 +336,7 @@ static int debug_etd_show(struct seq_file *s, void *v) return 0; } +DEFINE_SHOW_ATTRIBUTE(debug_etd); static void debug_statistics_show_one(struct seq_file *s, const char *name, struct debug_stats *stats) @@ -368,6 +371,7 @@ static int debug_statistics_show(struct seq_file *s, void *v) return 0; } +DEFINE_SHOW_ATTRIBUTE(debug_statistics); static void debug_isoc_show_one(struct seq_file *s, const char *name, int index, struct debug_isoc_trace *trace) @@ -409,66 +413,7 @@ static int debug_isoc_show(struct seq_file *s, void *v) return 0; } - -static int debug_status_open(struct inode *inode, struct file *file) -{ - return single_open(file, debug_status_show, inode->i_private); -} - -static int debug_dmem_open(struct inode *inode, struct file *file) -{ - return single_open(file, debug_dmem_show, inode->i_private); -} - -static int debug_etd_open(struct inode *inode, struct file *file) -{ - return single_open(file, debug_etd_show, inode->i_private); -} - -static int debug_statistics_open(struct inode *inode, struct file *file) -{ - return single_open(file, debug_statistics_show, inode->i_private); -} - -static int debug_isoc_open(struct inode *inode, struct file *file) -{ - return single_open(file, debug_isoc_show, inode->i_private); -} - -static const struct file_operations debug_status_fops = { - .open = debug_status_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations debug_dmem_fops = { - .open = debug_dmem_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations debug_etd_fops = { - .open = debug_etd_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations debug_statistics_fops = { - .open = debug_statistics_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct file_operations debug_isoc_fops = { - .open = debug_isoc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(debug_isoc); static void create_debug_files(struct imx21 *imx21) { diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 5f9234b9cf7b..4602ed801f0a 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1168,7 +1168,7 @@ static void dump_int(struct seq_file *s, char *label, u32 mask) mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : ""); } -static int isp116x_show_dbg(struct seq_file *s, void *unused) +static int isp116x_debug_show(struct seq_file *s, void *unused) { struct isp116x *isp116x = s->private; @@ -1196,18 +1196,7 @@ static int isp116x_show_dbg(struct seq_file *s, void *unused) return 0; } - -static int isp116x_open_seq(struct inode *inode, struct file *file) -{ - return single_open(file, isp116x_show_dbg, inode->i_private); -} - -static const struct file_operations isp116x_debug_fops = { - .open = isp116x_open_seq, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(isp116x_debug); static int create_debug_file(struct isp116x *isp116x) { diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 0201c49bc4fc..d8d35d456456 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -230,6 +230,7 @@ static int ohci_omap_reset(struct usb_hcd *hcd) } else { return -EPROBE_DEFER; } + hcd->skip_phy_initialization = 1; ohci->start_hnp = start_hnp; } #endif diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 1e6c954f4b3f..65a1c3fdc88c 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -21,7 +21,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/err.h> -#include <linux/phy/phy.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/reset.h> @@ -38,8 +38,6 @@ struct ohci_platform_priv { struct clk *clks[OHCI_MAX_CLKS]; struct reset_control *resets; - struct phy **phys; - int num_phys; }; static const char hcd_name[] = "ohci-platform"; @@ -48,7 +46,7 @@ static int ohci_platform_power_on(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); - int clk, ret, phy_num; + int clk, ret; for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) { ret = clk_prepare_enable(priv->clks[clk]); @@ -56,24 +54,8 @@ static int ohci_platform_power_on(struct platform_device *dev) goto err_disable_clks; } - for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - ret = phy_init(priv->phys[phy_num]); - if (ret) - goto err_exit_phy; - ret = phy_power_on(priv->phys[phy_num]); - if (ret) { - phy_exit(priv->phys[phy_num]); - goto err_exit_phy; - } - } - return 0; -err_exit_phy: - while (--phy_num >= 0) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } err_disable_clks: while (--clk >= 0) clk_disable_unprepare(priv->clks[clk]); @@ -85,12 +67,7 @@ static void ohci_platform_power_off(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); - int clk, phy_num; - - for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } + int clk; for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) if (priv->clks[clk]) @@ -117,7 +94,7 @@ static int ohci_platform_probe(struct platform_device *dev) struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); struct ohci_platform_priv *priv; struct ohci_hcd *ohci; - int err, irq, phy_num, clk = 0; + int err, irq, clk = 0; if (usb_disabled()) return -ENODEV; @@ -169,29 +146,6 @@ static int ohci_platform_probe(struct platform_device *dev) of_property_read_u32(dev->dev.of_node, "num-ports", &ohci->num_ports); - priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, - "phys", "#phy-cells"); - - if (priv->num_phys > 0) { - priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, - sizeof(struct phy *), GFP_KERNEL); - if (!priv->phys) - return -ENOMEM; - } else - priv->num_phys = 0; - - for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - priv->phys[phy_num] = devm_of_phy_get_by_index( - &dev->dev, dev->dev.of_node, phy_num); - if (IS_ERR(priv->phys[phy_num])) { - err = PTR_ERR(priv->phys[phy_num]); - goto err_put_hcd; - } else if (!hcd->phy) { - /* Avoiding phy_get() in usb_add_hcd() */ - hcd->phy = priv->phys[phy_num]; - } - } - for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); if (IS_ERR(priv->clks[clk])) { @@ -277,7 +231,7 @@ err_reset: err_put_clks: while (--clk >= 0) clk_put(priv->clks[clk]); -err_put_hcd: + if (pdata == &ohci_platform_defaults) dev->dev.platform_data = NULL; diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index fa88a903fa2e..5b061e599948 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1381,7 +1381,7 @@ static void dump_irq(struct seq_file *s, char *label, u8 mask) (mask & SL11H_INTMASK_DP) ? " dp" : ""); } -static int sl811h_show(struct seq_file *s, void *unused) +static int sl811h_debug_show(struct seq_file *s, void *unused) { struct sl811 *sl811 = s->private; struct sl811h_ep *ep; @@ -1491,25 +1491,14 @@ static int sl811h_show(struct seq_file *s, void *unused) return 0; } - -static int sl811h_open(struct inode *inode, struct file *file) -{ - return single_open(file, sl811h_show, inode->i_private); -} - -static const struct file_operations debug_ops = { - .open = sl811h_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(sl811h_debug); /* expect just one sl811 per system */ static void create_debug_file(struct sl811 *sl811) { sl811->debug_file = debugfs_create_file("sl811h", S_IRUGO, usb_debug_root, sl811, - &debug_ops); + &sl811h_debug_fops); } static void remove_debug_file(struct sl811 *sl811) diff --git a/drivers/usb/host/whci/debug.c b/drivers/usb/host/whci/debug.c index f154e5791bfd..8ddfe3f1f693 100644 --- a/drivers/usb/host/whci/debug.c +++ b/drivers/usb/host/whci/debug.c @@ -72,7 +72,7 @@ static void qset_print(struct seq_file *s, struct whc_qset *qset) } } -static int di_print(struct seq_file *s, void *p) +static int di_show(struct seq_file *s, void *p) { struct whc *whc = s->private; int d; @@ -91,8 +91,9 @@ static int di_print(struct seq_file *s, void *p) } return 0; } +DEFINE_SHOW_ATTRIBUTE(di); -static int asl_print(struct seq_file *s, void *p) +static int asl_show(struct seq_file *s, void *p) { struct whc *whc = s->private; struct whc_qset *qset; @@ -103,8 +104,9 @@ static int asl_print(struct seq_file *s, void *p) return 0; } +DEFINE_SHOW_ATTRIBUTE(asl); -static int pzl_print(struct seq_file *s, void *p) +static int pzl_show(struct seq_file *s, void *p) { struct whc *whc = s->private; struct whc_qset *qset; @@ -118,45 +120,7 @@ static int pzl_print(struct seq_file *s, void *p) } return 0; } - -static int di_open(struct inode *inode, struct file *file) -{ - return single_open(file, di_print, inode->i_private); -} - -static int asl_open(struct inode *inode, struct file *file) -{ - return single_open(file, asl_print, inode->i_private); -} - -static int pzl_open(struct inode *inode, struct file *file) -{ - return single_open(file, pzl_print, inode->i_private); -} - -static const struct file_operations di_fops = { - .open = di_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations asl_fops = { - .open = asl_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations pzl_fops = { - .open = pzl_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; +DEFINE_SHOW_ATTRIBUTE(pzl); void whc_dbg_init(struct whc *whc) { diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c index 75f0b92694ba..48779c44c361 100644 --- a/drivers/usb/host/xhci-dbgtty.c +++ b/drivers/usb/host/xhci-dbgtty.c @@ -447,9 +447,10 @@ int xhci_dbc_tty_register_device(struct xhci_hcd *xhci) xhci_dbc_tty_init_port(xhci, port); tty_dev = tty_port_register_device(&port->port, dbc_tty_driver, 0, NULL); - ret = IS_ERR_OR_NULL(tty_dev); - if (ret) + if (IS_ERR(tty_dev)) { + ret = PTR_ERR(tty_dev); goto register_fail; + } ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL); if (ret) diff --git a/drivers/usb/host/xhci-ext-caps.c b/drivers/usb/host/xhci-ext-caps.c new file mode 100644 index 000000000000..399113f9fc5c --- /dev/null +++ b/drivers/usb/host/xhci-ext-caps.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * XHCI extended capability handling + * + * Copyright (c) 2017 Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/platform_device.h> +#include "xhci.h" + +#define USB_SW_DRV_NAME "intel_xhci_usb_sw" +#define USB_SW_RESOURCE_SIZE 0x400 + +static void xhci_intel_unregister_pdev(void *arg) +{ + platform_device_unregister(arg); +} + +static int xhci_create_intel_xhci_sw_pdev(struct xhci_hcd *xhci, u32 cap_offset) +{ + struct usb_hcd *hcd = xhci_to_hcd(xhci); + struct device *dev = hcd->self.controller; + struct platform_device *pdev; + struct resource res = { 0, }; + int ret; + + pdev = platform_device_alloc(USB_SW_DRV_NAME, PLATFORM_DEVID_NONE); + if (!pdev) { + xhci_err(xhci, "couldn't allocate %s platform device\n", + USB_SW_DRV_NAME); + return -ENOMEM; + } + + res.start = hcd->rsrc_start + cap_offset; + res.end = res.start + USB_SW_RESOURCE_SIZE - 1; + res.name = USB_SW_DRV_NAME; + res.flags = IORESOURCE_MEM; + + ret = platform_device_add_resources(pdev, &res, 1); + if (ret) { + dev_err(dev, "couldn't add resources to intel_xhci_usb_sw pdev\n"); + platform_device_put(pdev); + return ret; + } + + pdev->dev.parent = dev; + + ret = platform_device_add(pdev); + if (ret) { + dev_err(dev, "couldn't register intel_xhci_usb_sw pdev\n"); + platform_device_put(pdev); + return ret; + } + + ret = devm_add_action_or_reset(dev, xhci_intel_unregister_pdev, pdev); + if (ret) { + dev_err(dev, "couldn't add unregister action for intel_xhci_usb_sw pdev\n"); + return ret; + } + + return 0; +} + +int xhci_ext_cap_init(struct xhci_hcd *xhci) +{ + void __iomem *base = &xhci->cap_regs->hc_capbase; + u32 offset, val; + int ret; + + offset = xhci_find_next_ext_cap(base, 0, 0); + + while (offset) { + val = readl(base + offset); + + switch (XHCI_EXT_CAPS_ID(val)) { + case XHCI_EXT_CAPS_VENDOR_INTEL: + if (xhci->quirks & XHCI_INTEL_USB_ROLE_SW) { + ret = xhci_create_intel_xhci_sw_pdev(xhci, + offset); + if (ret) + return ret; + } + break; + } + offset = xhci_find_next_ext_cap(base, offset, 0); + } + + return 0; +} +EXPORT_SYMBOL_GPL(xhci_ext_cap_init); diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h index bf7316e130d3..268328c20681 100644 --- a/drivers/usb/host/xhci-ext-caps.h +++ b/drivers/usb/host/xhci-ext-caps.h @@ -39,6 +39,8 @@ #define XHCI_EXT_CAPS_ROUTE 5 /* IDs 6-9 reserved */ #define XHCI_EXT_CAPS_DEBUG 10 +/* Vendor caps */ +#define XHCI_EXT_CAPS_VENDOR_INTEL 192 /* USB Legacy Support Capability - section 7.1.1 */ #define XHCI_HC_BIOS_OWNED (1 << 16) #define XHCI_HC_OS_OWNED (1 << 24) @@ -84,7 +86,8 @@ * @base PCI MMIO registers base address. * @start address at which to start looking, (0 or HCC_PARAMS to start at * beginning of list) - * @id Extended capability ID to search for. + * @id Extended capability ID to search for, or 0 for the next + * capability * * Returns the offset of the next matching extended capability structure. * Some capabilities can occur several times, e.g., the XHCI_EXT_CAPS_PROTOCOL, @@ -110,7 +113,7 @@ static inline int xhci_find_next_ext_cap(void __iomem *base, u32 start, int id) val = readl(base + offset); if (val == ~0) return 0; - if (XHCI_EXT_CAPS_ID(val) == id && offset != start) + if (offset != start && (id == 0 || XHCI_EXT_CAPS_ID(val) == id)) return offset; next = XHCI_EXT_CAPS_NEXT(val); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 332420d10be9..e5ace8995b3b 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -913,6 +913,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) if (dev->out_ctx) xhci_free_container_ctx(xhci, dev->out_ctx); + if (dev->udev && dev->udev->slot_id) + dev->udev->slot_id = 0; kfree(xhci->devs[slot_id]); xhci->devs[slot_id] = NULL; } diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index b0ab4d5e2751..7334da9e9779 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -14,7 +14,6 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -352,62 +351,6 @@ static const struct xhci_driver_overrides xhci_mtk_overrides __initconst = { static struct hc_driver __read_mostly xhci_mtk_hc_driver; -static int xhci_mtk_phy_init(struct xhci_hcd_mtk *mtk) -{ - int i; - int ret; - - for (i = 0; i < mtk->num_phys; i++) { - ret = phy_init(mtk->phys[i]); - if (ret) - goto exit_phy; - } - return 0; - -exit_phy: - for (; i > 0; i--) - phy_exit(mtk->phys[i - 1]); - - return ret; -} - -static int xhci_mtk_phy_exit(struct xhci_hcd_mtk *mtk) -{ - int i; - - for (i = 0; i < mtk->num_phys; i++) - phy_exit(mtk->phys[i]); - - return 0; -} - -static int xhci_mtk_phy_power_on(struct xhci_hcd_mtk *mtk) -{ - int i; - int ret; - - for (i = 0; i < mtk->num_phys; i++) { - ret = phy_power_on(mtk->phys[i]); - if (ret) - goto power_off_phy; - } - return 0; - -power_off_phy: - for (; i > 0; i--) - phy_power_off(mtk->phys[i - 1]); - - return ret; -} - -static void xhci_mtk_phy_power_off(struct xhci_hcd_mtk *mtk) -{ - unsigned int i; - - for (i = 0; i < mtk->num_phys; i++) - phy_power_off(mtk->phys[i]); -} - static int xhci_mtk_ldos_enable(struct xhci_hcd_mtk *mtk) { int ret; @@ -488,8 +431,6 @@ static int xhci_mtk_probe(struct platform_device *pdev) struct xhci_hcd *xhci; struct resource *res; struct usb_hcd *hcd; - struct phy *phy; - int phy_num; int ret = -ENODEV; int irq; @@ -529,16 +470,6 @@ static int xhci_mtk_probe(struct platform_device *pdev) return ret; } - mtk->num_phys = of_count_phandle_with_args(node, - "phys", "#phy-cells"); - if (mtk->num_phys > 0) { - mtk->phys = devm_kcalloc(dev, mtk->num_phys, - sizeof(*mtk->phys), GFP_KERNEL); - if (!mtk->phys) - return -ENOMEM; - } else { - mtk->num_phys = 0; - } pm_runtime_enable(dev); pm_runtime_get_sync(dev); device_enable_async_suspend(dev); @@ -596,23 +527,6 @@ static int xhci_mtk_probe(struct platform_device *pdev) mtk->has_ippc = false; } - for (phy_num = 0; phy_num < mtk->num_phys; phy_num++) { - phy = devm_of_phy_get_by_index(dev, node, phy_num); - if (IS_ERR(phy)) { - ret = PTR_ERR(phy); - goto put_usb2_hcd; - } - mtk->phys[phy_num] = phy; - } - - ret = xhci_mtk_phy_init(mtk); - if (ret) - goto put_usb2_hcd; - - ret = xhci_mtk_phy_power_on(mtk); - if (ret) - goto exit_phys; - device_init_wakeup(dev, true); xhci = hcd_to_xhci(hcd); @@ -630,7 +544,7 @@ static int xhci_mtk_probe(struct platform_device *pdev) dev_name(dev), hcd); if (!xhci->shared_hcd) { ret = -ENOMEM; - goto power_off_phys; + goto disable_device_wakeup; } ret = usb_add_hcd(hcd, irq, IRQF_SHARED); @@ -653,13 +567,9 @@ put_usb3_hcd: xhci_mtk_sch_exit(mtk); usb_put_hcd(xhci->shared_hcd); -power_off_phys: - xhci_mtk_phy_power_off(mtk); +disable_device_wakeup: device_init_wakeup(dev, false); -exit_phys: - xhci_mtk_phy_exit(mtk); - put_usb2_hcd: usb_put_hcd(hcd); @@ -682,8 +592,6 @@ static int xhci_mtk_remove(struct platform_device *dev) struct xhci_hcd *xhci = hcd_to_xhci(hcd); usb_remove_hcd(xhci->shared_hcd); - xhci_mtk_phy_power_off(mtk); - xhci_mtk_phy_exit(mtk); device_init_wakeup(&dev->dev, false); usb_remove_hcd(hcd); @@ -718,7 +626,6 @@ static int __maybe_unused xhci_mtk_suspend(struct device *dev) del_timer_sync(&xhci->shared_hcd->rh_timer); xhci_mtk_host_disable(mtk); - xhci_mtk_phy_power_off(mtk); xhci_mtk_clks_disable(mtk); usb_wakeup_set(mtk, true); return 0; @@ -732,7 +639,6 @@ static int __maybe_unused xhci_mtk_resume(struct device *dev) usb_wakeup_set(mtk, false); xhci_mtk_clks_enable(mtk); - xhci_mtk_phy_power_on(mtk); xhci_mtk_host_enable(mtk); xhci_dbg(xhci, "%s: restart port polling\n", __func__); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index d9f831b67e57..f17b7eab66cf 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -178,6 +178,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_INTEL && pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { xhci->quirks |= XHCI_SSIC_PORT_UNUSED; + xhci->quirks |= XHCI_INTEL_USB_ROLE_SW; } if (pdev->vendor == PCI_VENDOR_ID_INTEL && (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || @@ -311,6 +312,10 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto dealloc_usb2_hcd; } + retval = xhci_ext_cap_init(xhci); + if (retval) + goto put_usb3_hcd; + retval = usb_add_hcd(xhci->shared_hcd, dev->irq, IRQF_SHARED); if (retval) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 6652e2d5bd2e..df327dcc2bac 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -284,6 +284,7 @@ static int xhci_plat_probe(struct platform_device *pdev) ret = usb_phy_init(hcd->usb_phy); if (ret) goto put_usb3_hcd; + hcd->skip_phy_initialization = 1; } ret = usb_add_hcd(hcd, irq, IRQF_SHARED); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index daa94c3aed80..91a1a824673d 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1436,7 +1436,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, case TRB_STOP_RING: WARN_ON(slot_id != TRB_TO_SLOT_ID( le32_to_cpu(cmd_trb->generic.field[3]))); - xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb, event); + if (!cmd->completion) + xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb, event); break; case TRB_SET_DEQ: WARN_ON(slot_id != TRB_TO_SLOT_ID( @@ -1815,8 +1816,7 @@ struct xhci_segment *trb_in_td(struct xhci_hcd *xhci, static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, - unsigned int stream_id, - struct xhci_td *td, union xhci_trb *ep_trb, + unsigned int stream_id, struct xhci_td *td, enum xhci_ep_reset_type reset_type) { struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; @@ -1829,9 +1829,10 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci, xhci_queue_reset_ep(xhci, command, slot_id, ep_index, reset_type); - if (reset_type == EP_HARD_RESET) + if (reset_type == EP_HARD_RESET) { + ep->ep_state |= EP_HARD_CLEAR_TOGGLE; xhci_cleanup_stalled_ring(xhci, ep_index, stream_id, td); - + } xhci_ring_cmd_db(xhci); } @@ -1922,7 +1923,7 @@ static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, } static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event, + struct xhci_transfer_event *event, struct xhci_virt_ep *ep, int *status) { struct xhci_virt_device *xdev; @@ -1957,8 +1958,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, * The class driver clears the device side halt later. */ xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, - ep_ring->stream_id, td, ep_trb, - EP_HARD_RESET); + ep_ring->stream_id, td, EP_HARD_RESET); } else { /* Update ring dequeue pointer */ while (ep_ring->dequeue != td->last_trb) @@ -2083,7 +2083,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, td->urb->actual_length = requested; finish_td: - return finish_td(xhci, td, ep_trb, event, ep, status); + return finish_td(xhci, td, event, ep, status); } /* @@ -2170,7 +2170,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, td->urb->actual_length += frame->actual_length; - return finish_td(xhci, td, ep_trb, event, ep, status); + return finish_td(xhci, td, event, ep, status); } static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, @@ -2260,7 +2260,7 @@ finish_td: remaining); td->urb->actual_length = 0; } - return finish_td(xhci, td, ep_trb, event, ep, status); + return finish_td(xhci, td, event, ep, status); } /* @@ -2318,7 +2318,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, case COMP_INVALID_STREAM_TYPE_ERROR: case COMP_INVALID_STREAM_ID_ERROR: xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, 0, - NULL, NULL, EP_SOFT_RESET); + NULL, EP_SOFT_RESET); goto cleanup; case COMP_RING_UNDERRUN: case COMP_RING_OVERRUN: @@ -2584,8 +2584,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, ep_ring->stream_id, - td, ep_trb, - EP_HARD_RESET); + td, EP_HARD_RESET); goto cleanup; } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 5d37700ae4b0..9b27798ecce5 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1290,7 +1290,8 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag struct xhci_hcd *xhci = hcd_to_xhci(hcd); unsigned long flags; int ret = 0; - unsigned int slot_id, ep_index, ep_state; + unsigned int slot_id, ep_index; + unsigned int *ep_state; struct urb_priv *urb_priv; int num_tds; @@ -1300,6 +1301,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag slot_id = urb->dev->slot_id; ep_index = xhci_get_endpoint_index(&urb->ep->desc); + ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state; if (!HCD_HW_ACCESSIBLE(hcd)) { if (!in_interrupt()) @@ -1351,6 +1353,17 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag ret = -ESHUTDOWN; goto free_priv; } + if (*ep_state & (EP_GETTING_STREAMS | EP_GETTING_NO_STREAMS)) { + xhci_warn(xhci, "WARN: Can't enqueue URB, ep in streams transition state %x\n", + *ep_state); + ret = -EINVAL; + goto free_priv; + } + if (*ep_state & EP_SOFT_CLEAR_TOGGLE) { + xhci_warn(xhci, "Can't enqueue URB while manually clearing toggle\n"); + ret = -EINVAL; + goto free_priv; + } switch (usb_endpoint_type(&urb->ep->desc)) { @@ -1359,23 +1372,13 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag slot_id, ep_index); break; case USB_ENDPOINT_XFER_BULK: - ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state; - if (ep_state & (EP_GETTING_STREAMS | EP_GETTING_NO_STREAMS)) { - xhci_warn(xhci, "WARN: Can't enqueue URB, ep in streams transition state %x\n", - ep_state); - ret = -EINVAL; - break; - } ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); break; - - case USB_ENDPOINT_XFER_INT: ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb, slot_id, ep_index); break; - case USB_ENDPOINT_XFER_ISOC: ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb, slot_id, ep_index); @@ -2874,33 +2877,103 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index, } } -/* Called when clearing halted device. The core should have sent the control - * message to clear the device halt condition. The host side of the halt should - * already be cleared with a reset endpoint command issued when the STALL tx - * event was received. +/* + * Called after usb core issues a clear halt control message. + * The host side of the halt should already be cleared by a reset endpoint + * command issued when the STALL event was received. * - * Context: in_interrupt + * The reset endpoint command may only be issued to endpoints in the halted + * state. For software that wishes to reset the data toggle or sequence number + * of an endpoint that isn't in the halted state this function will issue a + * configure endpoint command with the Drop and Add bits set for the target + * endpoint. Refer to the additional note in xhci spcification section 4.6.8. */ static void xhci_endpoint_reset(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) + struct usb_host_endpoint *host_ep) { struct xhci_hcd *xhci; + struct usb_device *udev; + struct xhci_virt_device *vdev; + struct xhci_virt_ep *ep; + struct xhci_input_control_ctx *ctrl_ctx; + struct xhci_command *stop_cmd, *cfg_cmd; + unsigned int ep_index; + unsigned long flags; + u32 ep_flag; xhci = hcd_to_xhci(hcd); + if (!host_ep->hcpriv) + return; + udev = (struct usb_device *) host_ep->hcpriv; + vdev = xhci->devs[udev->slot_id]; + ep_index = xhci_get_endpoint_index(&host_ep->desc); + ep = &vdev->eps[ep_index]; + + /* Bail out if toggle is already being cleared by a endpoint reset */ + if (ep->ep_state & EP_HARD_CLEAR_TOGGLE) { + ep->ep_state &= ~EP_HARD_CLEAR_TOGGLE; + return; + } + /* Only interrupt and bulk ep's use data toggle, USB2 spec 5.5.4-> */ + if (usb_endpoint_xfer_control(&host_ep->desc) || + usb_endpoint_xfer_isoc(&host_ep->desc)) + return; + + ep_flag = xhci_get_endpoint_flag(&host_ep->desc); + + if (ep_flag == SLOT_FLAG || ep_flag == EP0_FLAG) + return; + + stop_cmd = xhci_alloc_command(xhci, true, GFP_NOWAIT); + if (!stop_cmd) + return; + + cfg_cmd = xhci_alloc_command_with_ctx(xhci, true, GFP_NOWAIT); + if (!cfg_cmd) + goto cleanup; + + spin_lock_irqsave(&xhci->lock, flags); + + /* block queuing new trbs and ringing ep doorbell */ + ep->ep_state |= EP_SOFT_CLEAR_TOGGLE; /* - * We might need to implement the config ep cmd in xhci 4.8.1 note: - * The Reset Endpoint Command may only be issued to endpoints in the - * Halted state. If software wishes reset the Data Toggle or Sequence - * Number of an endpoint that isn't in the Halted state, then software - * may issue a Configure Endpoint Command with the Drop and Add bits set - * for the target endpoint. that is in the Stopped state. + * Make sure endpoint ring is empty before resetting the toggle/seq. + * Driver is required to synchronously cancel all transfer request. + * Stop the endpoint to force xHC to update the output context */ - /* For now just print debug to follow the situation */ - xhci_dbg(xhci, "Endpoint 0x%x ep reset callback called\n", - ep->desc.bEndpointAddress); + if (!list_empty(&ep->ring->td_list)) { + dev_err(&udev->dev, "EP not empty, refuse reset\n"); + spin_unlock_irqrestore(&xhci->lock, flags); + goto cleanup; + } + xhci_queue_stop_endpoint(xhci, stop_cmd, udev->slot_id, ep_index, 0); + xhci_ring_cmd_db(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); + + wait_for_completion(stop_cmd->completion); + + spin_lock_irqsave(&xhci->lock, flags); + + /* config ep command clears toggle if add and drop ep flags are set */ + ctrl_ctx = xhci_get_input_control_ctx(cfg_cmd->in_ctx); + xhci_setup_input_ctx_for_config_ep(xhci, cfg_cmd->in_ctx, vdev->out_ctx, + ctrl_ctx, ep_flag, ep_flag); + xhci_endpoint_copy(xhci, cfg_cmd->in_ctx, vdev->out_ctx, ep_index); + + xhci_queue_configure_endpoint(xhci, cfg_cmd, cfg_cmd->in_ctx->dma, + udev->slot_id, false); + xhci_ring_cmd_db(xhci); + spin_unlock_irqrestore(&xhci->lock, flags); + + wait_for_completion(cfg_cmd->completion); + + ep->ep_state &= ~EP_SOFT_CLEAR_TOGGLE; + xhci_free_command(xhci, cfg_cmd); +cleanup: + xhci_free_command(xhci, stop_cmd); } static int xhci_check_streams_endpoint(struct xhci_hcd *xhci, @@ -4768,6 +4841,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) * quirks */ struct device *dev = hcd->self.sysdev; + unsigned int minor_rev; int retval; /* Accept arbitrarily long scatter-gather lists */ @@ -4795,12 +4869,19 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) */ hcd->has_tt = 1; } else { - /* Some 3.1 hosts return sbrn 0x30, can't rely on sbrn alone */ - if (xhci->sbrn == 0x31 || xhci->usb3_rhub.min_rev >= 1) { - xhci_info(xhci, "Host supports USB 3.1 Enhanced SuperSpeed\n"); + /* + * Some 3.1 hosts return sbrn 0x30, use xhci supported protocol + * minor revision instead of sbrn + */ + minor_rev = xhci->usb3_rhub.min_rev; + if (minor_rev) { hcd->speed = HCD_USB31; hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS; } + xhci_info(xhci, "Host supports USB 3.%x %s SuperSpeed\n", + minor_rev, + minor_rev ? "Enhanced" : ""); + /* xHCI private pointer was set in xhci_pci_probe for the second * registered roothub. */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 866e141d4972..05c909b04f14 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -922,6 +922,8 @@ struct xhci_virt_ep { #define EP_HAS_STREAMS (1 << 4) /* Transitioning the endpoint to not using streams, don't enqueue URBs */ #define EP_GETTING_NO_STREAMS (1 << 5) +#define EP_HARD_CLEAR_TOGGLE (1 << 6) +#define EP_SOFT_CLEAR_TOGGLE (1 << 7) /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; /* Watchdog timer for stop endpoint command to cancel URBs */ @@ -1827,6 +1829,7 @@ struct xhci_hcd { #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28) #define XHCI_HW_LPM_DISABLE (1 << 29) #define XHCI_SUSPEND_DELAY (1 << 30) +#define XHCI_INTEL_USB_ROLE_SW (1 << 31) unsigned int num_active_eps; unsigned int limit_active_eps; @@ -2022,6 +2025,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); void xhci_init_driver(struct hc_driver *drv, const struct xhci_driver_overrides *over); 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, bool hibernated); diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index bac4ef5d9512..1714b2258b54 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -1441,7 +1441,6 @@ int isp1760_udc_register(struct isp1760_device *isp, int irq, unsigned long irqflags) { struct isp1760_udc *udc = &isp->udc; - const char *devname; int ret; udc->irq = -1; @@ -1455,13 +1454,10 @@ int isp1760_udc_register(struct isp1760_device *isp, int irq, if (ret < 0) return ret; - devname = dev_name(isp->dev); - udc->irqname = kmalloc(strlen(devname) + 7, GFP_KERNEL); + udc->irqname = kasprintf(GFP_KERNEL, "%s (udc)", dev_name(isp->dev)); if (!udc->irqname) return -ENOMEM; - sprintf(udc->irqname, "%s (udc)", devname); - ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | irqflags, udc->irqname, udc); if (ret < 0) diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 4b8712733fc7..b3160afe0458 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -54,7 +54,7 @@ MODULE_DEVICE_TABLE(usb, device_table); /* we can have up to this number of device plugged in at once */ #define MAX_DEVICES 16 -#define COMMAND_TIMEOUT (2*HZ) /* 60 second timeout for a command */ +#define COMMAND_TIMEOUT (2*HZ) /* * The locking scheme is a vanilla 3-lock: @@ -132,6 +132,8 @@ static void adu_abort_transfers(struct adu_device *dev) spin_lock_irqsave(&dev->buflock, flags); if (!dev->out_urb_finished) { spin_unlock_irqrestore(&dev->buflock, flags); + wait_event_timeout(dev->write_wait, dev->out_urb_finished, + COMMAND_TIMEOUT); usb_kill_urb(dev->interrupt_out_urb); } else spin_unlock_irqrestore(&dev->buflock, flags); diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index 716cb515523e..cf5828ce927a 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -168,14 +168,10 @@ static int chaoskey_probe(struct usb_interface *interface, */ if (udev->product && udev->serial) { - dev->name = kmalloc(strlen(udev->product) + 1 + - strlen(udev->serial) + 1, GFP_KERNEL); + dev->name = kasprintf(GFP_KERNEL, "%s-%s", udev->product, + udev->serial); if (dev->name == NULL) goto out; - - strcpy(dev->name, udev->product); - strcat(dev->name, "-"); - strcat(dev->name, udev->serial); } dev->interface = interface; diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 90028ef541e3..9e1142b8b91b 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -2028,11 +2028,14 @@ test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param, unsigned i; unsigned long packets = 0; int status = 0; - struct urb *urbs[param->sglen]; + struct urb *urbs[MAX_SGLEN]; if (!param->sglen || param->iterations > UINT_MAX / param->sglen) return -EINVAL; + if (param->sglen > MAX_SGLEN) + return -EINVAL; + memset(&context, 0, sizeof(context)); context.count = param->iterations * param->sglen; context.dev = dev; diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index 263c97fec708..de9a502491c2 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -769,10 +769,15 @@ static void uss720_disconnect(struct usb_interface *intf) /* table of cables that work through this driver */ static const struct usb_device_id uss720_table[] = { { USB_DEVICE(0x047e, 0x1001) }, + { USB_DEVICE(0x04b8, 0x0002) }, + { USB_DEVICE(0x04b8, 0x0003) }, + { USB_DEVICE(0x050d, 0x0002) }, + { USB_DEVICE(0x050d, 0x1202) }, { USB_DEVICE(0x0557, 0x2001) }, + { USB_DEVICE(0x05ab, 0x0002) }, + { USB_DEVICE(0x06c6, 0x0100) }, { USB_DEVICE(0x0729, 0x1284) }, { USB_DEVICE(0x1293, 0x0002) }, - { USB_DEVICE(0x050d, 0x0002) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index 7dac456f7ebc..e2050cac3eae 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -110,11 +110,7 @@ static int musb_regdump_show(struct seq_file *s, void *unused) pm_runtime_put_autosuspend(musb->controller); return 0; } - -static int musb_regdump_open(struct inode *inode, struct file *file) -{ - return single_open(file, musb_regdump_show, inode->i_private); -} +DEFINE_SHOW_ATTRIBUTE(musb_regdump); static int musb_test_mode_show(struct seq_file *s, void *unused) { @@ -159,13 +155,6 @@ static int musb_test_mode_show(struct seq_file *s, void *unused) return 0; } -static const struct file_operations musb_regdump_fops = { - .open = musb_regdump_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - static int musb_test_mode_open(struct inode *inode, struct file *file) { return single_open(file, musb_test_mode_show, inode->i_private); diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 18da4873e52e..91a5027b5c1f 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -89,15 +89,19 @@ static int service_tx_status_request( } is_in = epnum & USB_DIR_IN; - if (is_in) { - epnum &= 0x0f; + epnum &= 0x0f; + if (epnum >= MUSB_C_NUM_EPS) { + handled = -EINVAL; + break; + } + + if (is_in) ep = &musb->endpoints[epnum].ep_in; - } else { + else ep = &musb->endpoints[epnum].ep_out; - } regs = musb->endpoints[epnum].regs; - if (epnum >= MUSB_C_NUM_EPS || !ep->desc) { + if (!ep->desc) { handled = -EINVAL; break; } diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 87295313a10c..66143ab8c043 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -29,17 +29,12 @@ /* Bank AB8500_USB */ #define AB8500_USB_LINE_STAT_REG 0x80 #define AB8505_USB_LINE_STAT_REG 0x94 -#define AB8540_USB_LINK_STAT_REG 0x94 -#define AB9540_USB_LINK_STAT_REG 0x94 -#define AB8540_USB_OTG_CTL_REG 0x87 #define AB8500_USB_PHY_CTRL_REG 0x8A -#define AB8540_VBUS_CTRL_REG 0x82 /* Bank AB8500_DEVELOPMENT */ #define AB8500_BANK12_ACCESS 0x00 /* Bank AB8500_DEBUG */ -#define AB8540_DEBUG 0x32 #define AB8500_USB_PHY_TUNE1 0x05 #define AB8500_USB_PHY_TUNE2 0x06 #define AB8500_USB_PHY_TUNE3 0x07 @@ -53,10 +48,6 @@ #define AB8500_BIT_WD_CTRL_ENABLE (1 << 0) #define AB8500_BIT_WD_CTRL_KICK (1 << 1) #define AB8500_BIT_SOURCE2_VBUSDET (1 << 7) -#define AB8540_BIT_OTG_CTL_VBUS_VALID_ENA (1 << 0) -#define AB8540_BIT_OTG_CTL_ID_HOST_ENA (1 << 1) -#define AB8540_BIT_OTG_CTL_ID_DEV_ENA (1 << 5) -#define AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA (1 << 0) #define AB8500_WD_KICK_DELAY_US 100 /* usec */ #define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */ @@ -113,68 +104,6 @@ enum ab8505_usb_link_status { USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8505, }; -enum ab8540_usb_link_status { - USB_LINK_NOT_CONFIGURED_8540 = 0, - USB_LINK_STD_HOST_NC_8540, - USB_LINK_STD_HOST_C_NS_8540, - USB_LINK_STD_HOST_C_S_8540, - USB_LINK_CDP_8540, - USB_LINK_RESERVED0_8540, - USB_LINK_RESERVED1_8540, - USB_LINK_DEDICATED_CHG_8540, - USB_LINK_ACA_RID_A_8540, - USB_LINK_ACA_RID_B_8540, - USB_LINK_ACA_RID_C_NM_8540, - USB_LINK_RESERVED2_8540, - USB_LINK_RESERVED3_8540, - USB_LINK_HM_IDGND_8540, - USB_LINK_CHARGERPORT_NOT_OK_8540, - USB_LINK_CHARGER_DM_HIGH_8540, - USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8540, - USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_8540, - USB_LINK_STD_UPSTREAM_8540, - USB_LINK_CHARGER_SE1_8540, - USB_LINK_CARKIT_CHGR_1_8540, - USB_LINK_CARKIT_CHGR_2_8540, - USB_LINK_ACA_DOCK_CHGR_8540, - USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_8540, - USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_8540, - USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8540, - USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8540, - USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8540 -}; - -enum ab9540_usb_link_status { - USB_LINK_NOT_CONFIGURED_9540 = 0, - USB_LINK_STD_HOST_NC_9540, - USB_LINK_STD_HOST_C_NS_9540, - USB_LINK_STD_HOST_C_S_9540, - USB_LINK_CDP_9540, - USB_LINK_RESERVED0_9540, - USB_LINK_RESERVED1_9540, - USB_LINK_DEDICATED_CHG_9540, - USB_LINK_ACA_RID_A_9540, - USB_LINK_ACA_RID_B_9540, - USB_LINK_ACA_RID_C_NM_9540, - USB_LINK_RESERVED2_9540, - USB_LINK_RESERVED3_9540, - USB_LINK_HM_IDGND_9540, - USB_LINK_CHARGERPORT_NOT_OK_9540, - USB_LINK_CHARGER_DM_HIGH_9540, - USB_LINK_PHYEN_NO_VBUS_NO_IDGND_9540, - USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_9540, - USB_LINK_STD_UPSTREAM_9540, - USB_LINK_CHARGER_SE1_9540, - USB_LINK_CARKIT_CHGR_1_9540, - USB_LINK_CARKIT_CHGR_2_9540, - USB_LINK_ACA_DOCK_CHGR_9540, - USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_9540, - USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_9540, - USB_LINK_SAMSUNG_UART_CBL_PHY_EN_9540, - USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_9540, - USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_9540 -}; - enum ab8500_usb_mode { USB_IDLE = 0, USB_PERIPHERAL, @@ -192,10 +121,6 @@ enum ab8500_usb_mode { #define AB8500_USB_FLAG_USE_AB_IDDET (1 << 3) /* Enable setting regulators voltage */ #define AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE (1 << 4) -/* Enable the check_vbus_status workaround */ -#define AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS (1 << 5) -/* Enable the vbus host workaround */ -#define AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK (1 << 6) struct ab8500_usb { struct usb_phy phy; @@ -203,7 +128,6 @@ struct ab8500_usb { struct ab8500 *ab8500; unsigned vbus_draw; struct work_struct phy_dis_work; - struct work_struct vbus_event_work; enum ab8500_usb_mode mode; struct clk *sysclk; struct regulator *v_ape; @@ -342,15 +266,6 @@ static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host) abx500_mask_and_set_register_interruptible(ab->dev, AB8500_USB, AB8500_USB_PHY_CTRL_REG, bit, bit); - - if (ab->flags & AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK) { - if (sel_host) - abx500_set_register_interruptible(ab->dev, - AB8500_USB, AB8540_USB_OTG_CTL_REG, - AB8540_BIT_OTG_CTL_VBUS_VALID_ENA | - AB8540_BIT_OTG_CTL_ID_HOST_ENA | - AB8540_BIT_OTG_CTL_ID_DEV_ENA); - } } static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) @@ -395,263 +310,6 @@ static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) #define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_enable(ab, false) #define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_disable(ab, false) -static int ab9540_usb_link_status_update(struct ab8500_usb *ab, - enum ab9540_usb_link_status lsts) -{ - enum ux500_musb_vbus_id_status event = 0; - - dev_dbg(ab->dev, "ab9540_usb_link_status_update %d\n", lsts); - - if (ab->previous_link_status_state == USB_LINK_HM_IDGND_9540 && - (lsts == USB_LINK_STD_HOST_C_NS_9540 || - lsts == USB_LINK_STD_HOST_NC_9540)) - return 0; - - if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_9540 && - (lsts == USB_LINK_STD_HOST_NC_9540)) - return 0; - - ab->previous_link_status_state = lsts; - - switch (lsts) { - case USB_LINK_ACA_RID_B_9540: - event = UX500_MUSB_RIDB; - case USB_LINK_NOT_CONFIGURED_9540: - case USB_LINK_RESERVED0_9540: - case USB_LINK_RESERVED1_9540: - case USB_LINK_RESERVED2_9540: - case USB_LINK_RESERVED3_9540: - if (ab->mode == USB_PERIPHERAL) - atomic_notifier_call_chain(&ab->phy.notifier, - UX500_MUSB_CLEAN, &ab->vbus_draw); - ab->mode = USB_IDLE; - ab->phy.otg->default_a = false; - ab->vbus_draw = 0; - if (event != UX500_MUSB_RIDB) - event = UX500_MUSB_NONE; - /* Fallback to default B_IDLE as nothing is connected. */ - ab->phy.otg->state = OTG_STATE_B_IDLE; - usb_phy_set_event(&ab->phy, USB_EVENT_NONE); - break; - - case USB_LINK_ACA_RID_C_NM_9540: - event = UX500_MUSB_RIDC; - case USB_LINK_STD_HOST_NC_9540: - case USB_LINK_STD_HOST_C_NS_9540: - case USB_LINK_STD_HOST_C_S_9540: - case USB_LINK_CDP_9540: - if (ab->mode == USB_HOST) { - ab->mode = USB_PERIPHERAL; - ab8500_usb_host_phy_dis(ab); - ab8500_usb_peri_phy_en(ab); - atomic_notifier_call_chain(&ab->phy.notifier, - UX500_MUSB_PREPARE, &ab->vbus_draw); - usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED); - } - if (ab->mode == USB_IDLE) { - ab->mode = USB_PERIPHERAL; - ab8500_usb_peri_phy_en(ab); - atomic_notifier_call_chain(&ab->phy.notifier, - UX500_MUSB_PREPARE, &ab->vbus_draw); - usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED); - } - if (event != UX500_MUSB_RIDC) - event = UX500_MUSB_VBUS; - break; - - case USB_LINK_ACA_RID_A_9540: - event = UX500_MUSB_RIDA; - case USB_LINK_HM_IDGND_9540: - case USB_LINK_STD_UPSTREAM_9540: - if (ab->mode == USB_PERIPHERAL) { - ab->mode = USB_HOST; - ab8500_usb_peri_phy_dis(ab); - ab8500_usb_host_phy_en(ab); - atomic_notifier_call_chain(&ab->phy.notifier, - UX500_MUSB_PREPARE, &ab->vbus_draw); - } - if (ab->mode == USB_IDLE) { - ab->mode = USB_HOST; - ab8500_usb_host_phy_en(ab); - atomic_notifier_call_chain(&ab->phy.notifier, - UX500_MUSB_PREPARE, &ab->vbus_draw); - } - ab->phy.otg->default_a = true; - if (event != UX500_MUSB_RIDA) - event = UX500_MUSB_ID; - - atomic_notifier_call_chain(&ab->phy.notifier, - event, &ab->vbus_draw); - break; - - case USB_LINK_DEDICATED_CHG_9540: - ab->mode = USB_DEDICATED_CHG; - event = UX500_MUSB_CHARGER; - atomic_notifier_call_chain(&ab->phy.notifier, - event, &ab->vbus_draw); - usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER); - break; - - case USB_LINK_PHYEN_NO_VBUS_NO_IDGND_9540: - case USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_9540: - if (!(is_ab9540_2p0_or_earlier(ab->ab8500))) { - event = UX500_MUSB_NONE; - if (ab->mode == USB_HOST) { - ab->phy.otg->default_a = false; - ab->vbus_draw = 0; - atomic_notifier_call_chain(&ab->phy.notifier, - event, &ab->vbus_draw); - ab8500_usb_host_phy_dis(ab); - ab->mode = USB_IDLE; - } - if (ab->mode == USB_PERIPHERAL) { - atomic_notifier_call_chain(&ab->phy.notifier, - event, &ab->vbus_draw); - ab8500_usb_peri_phy_dis(ab); - atomic_notifier_call_chain(&ab->phy.notifier, - UX500_MUSB_CLEAN, - &ab->vbus_draw); - ab->mode = USB_IDLE; - ab->phy.otg->default_a = false; - ab->vbus_draw = 0; - usb_phy_set_event(&ab->phy, USB_EVENT_NONE); - } - } - break; - - default: - break; - } - - return 0; -} - -static int ab8540_usb_link_status_update(struct ab8500_usb *ab, - enum ab8540_usb_link_status lsts) -{ - enum ux500_musb_vbus_id_status event = 0; - - dev_dbg(ab->dev, "ab8540_usb_link_status_update %d\n", lsts); - - if (ab->enabled_charging_detection) { - /* Disable USB Charger detection */ - abx500_mask_and_set_register_interruptible(ab->dev, - AB8500_USB, AB8540_VBUS_CTRL_REG, - AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA, 0x00); - ab->enabled_charging_detection = false; - } - - /* - * Spurious link_status interrupts are seen in case of a - * disconnection of a device in IDGND and RIDA stage - */ - if (ab->previous_link_status_state == USB_LINK_HM_IDGND_8540 && - (lsts == USB_LINK_STD_HOST_C_NS_8540 || - lsts == USB_LINK_STD_HOST_NC_8540)) - return 0; - - if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8540 && - (lsts == USB_LINK_STD_HOST_NC_8540)) - return 0; - - ab->previous_link_status_state = lsts; - - switch (lsts) { - case USB_LINK_ACA_RID_B_8540: - event = UX500_MUSB_RIDB; - case USB_LINK_NOT_CONFIGURED_8540: - case USB_LINK_RESERVED0_8540: - case USB_LINK_RESERVED1_8540: - case USB_LINK_RESERVED2_8540: - case USB_LINK_RESERVED3_8540: - ab->mode = USB_IDLE; - ab->phy.otg->default_a = false; - ab->vbus_draw = 0; - if (event != UX500_MUSB_RIDB) - event = UX500_MUSB_NONE; - /* - * Fallback to default B_IDLE as nothing - * is connected - */ - ab->phy.otg->state = OTG_STATE_B_IDLE; - usb_phy_set_event(&ab->phy, USB_EVENT_NONE); - break; - - case USB_LINK_ACA_RID_C_NM_8540: - event = UX500_MUSB_RIDC; - case USB_LINK_STD_HOST_NC_8540: - case USB_LINK_STD_HOST_C_NS_8540: - case USB_LINK_STD_HOST_C_S_8540: - case USB_LINK_CDP_8540: - if (ab->mode == USB_IDLE) { - ab->mode = USB_PERIPHERAL; - ab8500_usb_peri_phy_en(ab); - atomic_notifier_call_chain(&ab->phy.notifier, - UX500_MUSB_PREPARE, &ab->vbus_draw); - usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED); - } - if (event != UX500_MUSB_RIDC) - event = UX500_MUSB_VBUS; - break; - - case USB_LINK_ACA_RID_A_8540: - case USB_LINK_ACA_DOCK_CHGR_8540: - event = UX500_MUSB_RIDA; - case USB_LINK_HM_IDGND_8540: - case USB_LINK_STD_UPSTREAM_8540: - if (ab->mode == USB_IDLE) { - ab->mode = USB_HOST; - ab8500_usb_host_phy_en(ab); - atomic_notifier_call_chain(&ab->phy.notifier, - UX500_MUSB_PREPARE, &ab->vbus_draw); - } - ab->phy.otg->default_a = true; - if (event != UX500_MUSB_RIDA) - event = UX500_MUSB_ID; - atomic_notifier_call_chain(&ab->phy.notifier, - event, &ab->vbus_draw); - break; - - case USB_LINK_DEDICATED_CHG_8540: - ab->mode = USB_DEDICATED_CHG; - event = UX500_MUSB_CHARGER; - atomic_notifier_call_chain(&ab->phy.notifier, - event, &ab->vbus_draw); - usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER); - break; - - case USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8540: - case USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_8540: - event = UX500_MUSB_NONE; - if (ab->mode == USB_HOST) { - ab->phy.otg->default_a = false; - ab->vbus_draw = 0; - atomic_notifier_call_chain(&ab->phy.notifier, - event, &ab->vbus_draw); - ab8500_usb_host_phy_dis(ab); - ab->mode = USB_IDLE; - } - if (ab->mode == USB_PERIPHERAL) { - atomic_notifier_call_chain(&ab->phy.notifier, - event, &ab->vbus_draw); - ab8500_usb_peri_phy_dis(ab); - atomic_notifier_call_chain(&ab->phy.notifier, - UX500_MUSB_CLEAN, &ab->vbus_draw); - ab->mode = USB_IDLE; - ab->phy.otg->default_a = false; - ab->vbus_draw = 0; - usb_phy_set_event(&ab->phy, USB_EVENT_NONE); - } - break; - - default: - event = UX500_MUSB_NONE; - break; - } - - return 0; -} - static int ab8505_usb_link_status_update(struct ab8500_usb *ab, enum ab8505_usb_link_status lsts) { @@ -858,20 +516,6 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab) AB8500_USB, AB8505_USB_LINE_STAT_REG, ®); lsts = (reg >> 3) & 0x1F; ret = ab8505_usb_link_status_update(ab, lsts); - } else if (is_ab8540(ab->ab8500)) { - enum ab8540_usb_link_status lsts; - - abx500_get_register_interruptible(ab->dev, - AB8500_USB, AB8540_USB_LINK_STAT_REG, ®); - lsts = (reg >> 3) & 0xFF; - ret = ab8540_usb_link_status_update(ab, lsts); - } else if (is_ab9540(ab->ab8500)) { - enum ab9540_usb_link_status lsts; - - abx500_get_register_interruptible(ab->dev, - AB8500_USB, AB9540_USB_LINK_STAT_REG, ®); - lsts = (reg >> 3) & 0xFF; - ret = ab9540_usb_link_status_update(ab, lsts); } return ret; @@ -889,7 +533,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab) static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data) { struct ab8500_usb *ab = (struct ab8500_usb *) data; - enum usb_phy_events event = UX500_MUSB_NONE; + enum usb_phy_events event = USB_EVENT_NONE; /* Link status will not be updated till phy is disabled. */ if (ab->mode == USB_HOST) { @@ -946,69 +590,6 @@ static void ab8500_usb_phy_disable_work(struct work_struct *work) ab8500_usb_peri_phy_dis(ab); } -/* Check if VBUS is set and linkstatus has not detected a cable. */ -static bool ab8500_usb_check_vbus_status(struct ab8500_usb *ab) -{ - u8 isource2; - u8 reg; - enum ab8540_usb_link_status lsts; - - abx500_get_register_interruptible(ab->dev, - AB8500_INTERRUPT, AB8500_IT_SOURCE2_REG, - &isource2); - - /* If Vbus is below 3.6V abort */ - if (!(isource2 & AB8500_BIT_SOURCE2_VBUSDET)) - return false; - - abx500_get_register_interruptible(ab->dev, - AB8500_USB, AB8540_USB_LINK_STAT_REG, - ®); - - lsts = (reg >> 3) & 0xFF; - - /* Check if linkstatus has detected a cable */ - if (lsts) - return false; - - return true; -} - -/* re-trigger charger detection again with watchdog re-kick. */ -static void ab8500_usb_vbus_turn_on_event_work(struct work_struct *work) -{ - struct ab8500_usb *ab = container_of(work, struct ab8500_usb, - vbus_event_work); - - if (ab->mode != USB_IDLE) - return; - - abx500_set_register_interruptible(ab->dev, - AB8500_SYS_CTRL2_BLOCK, AB8500_MAIN_WD_CTRL_REG, - AB8500_BIT_WD_CTRL_ENABLE); - - udelay(100); - - abx500_set_register_interruptible(ab->dev, - AB8500_SYS_CTRL2_BLOCK, AB8500_MAIN_WD_CTRL_REG, - AB8500_BIT_WD_CTRL_ENABLE | AB8500_BIT_WD_CTRL_KICK); - - udelay(100); - - /* Disable Main watchdog */ - abx500_set_register_interruptible(ab->dev, - AB8500_SYS_CTRL2_BLOCK, AB8500_MAIN_WD_CTRL_REG, - 0x0); - - /* Enable USB Charger detection */ - abx500_mask_and_set_register_interruptible(ab->dev, - AB8500_USB, AB8540_VBUS_CTRL_REG, - AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA, - AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA); - - ab->enabled_charging_detection = true; -} - static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend) { /* TODO */ @@ -1256,66 +837,6 @@ static void ab8500_usb_set_ab8505_tuning_values(struct ab8500_usb *ab) err); } -static void ab8500_usb_set_ab8540_tuning_values(struct ab8500_usb *ab) -{ - int err; - - err = abx500_set_register_interruptible(ab->dev, - AB8540_DEBUG, AB8500_USB_PHY_TUNE1, 0xCC); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE1 register ret=%d\n", - err); - - err = abx500_set_register_interruptible(ab->dev, - AB8540_DEBUG, AB8500_USB_PHY_TUNE2, 0x60); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE2 register ret=%d\n", - err); - - err = abx500_set_register_interruptible(ab->dev, - AB8540_DEBUG, AB8500_USB_PHY_TUNE3, 0x90); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE3 register ret=%d\n", - err); -} - -static void ab8500_usb_set_ab9540_tuning_values(struct ab8500_usb *ab) -{ - int err; - - /* Enable the PBT/Bank 0x12 access */ - err = abx500_set_register_interruptible(ab->dev, - AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x01); - if (err < 0) - dev_err(ab->dev, "Failed to enable bank12 access err=%d\n", - err); - - err = abx500_set_register_interruptible(ab->dev, - AB8500_DEBUG, AB8500_USB_PHY_TUNE1, 0xC8); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n", - err); - - err = abx500_set_register_interruptible(ab->dev, - AB8500_DEBUG, AB8500_USB_PHY_TUNE2, 0x60); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n", - err); - - err = abx500_set_register_interruptible(ab->dev, - AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x80); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE3 register err=%d\n", - err); - - /* Switch to normal mode/disable Bank 0x12 access */ - err = abx500_set_register_interruptible(ab->dev, - AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x00); - if (err < 0) - dev_err(ab->dev, "Failed to switch bank12 access err=%d\n", - err); -} - static int ab8500_usb_probe(struct platform_device *pdev) { struct ab8500_usb *ab; @@ -1362,17 +883,6 @@ static int ab8500_usb_probe(struct platform_device *pdev) AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ | AB8500_USB_FLAG_USE_VBUS_DET_IRQ | AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE; - } else if (is_ab8540(ab->ab8500)) { - ab->flags |= AB8500_USB_FLAG_USE_LINK_STATUS_IRQ | - AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS | - AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK | - AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE; - } else if (is_ab9540(ab->ab8500)) { - ab->flags |= AB8500_USB_FLAG_USE_LINK_STATUS_IRQ | - AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE; - if (is_ab9540_2p0_or_earlier(ab->ab8500)) - ab->flags |= AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ | - AB8500_USB_FLAG_USE_VBUS_DET_IRQ; } /* Disable regulator voltage setting for AB8500 <= v2.0 */ @@ -1384,8 +894,6 @@ static int ab8500_usb_probe(struct platform_device *pdev) /* all: Disable phy when called from set_host and set_peripheral */ INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); - INIT_WORK(&ab->vbus_event_work, ab8500_usb_vbus_turn_on_event_work); - err = ab8500_usb_regulator_get(ab); if (err) return err; @@ -1412,12 +920,6 @@ static int ab8500_usb_probe(struct platform_device *pdev) else if (is_ab8505(ab->ab8500)) /* Phy tuning values for AB8505 */ ab8500_usb_set_ab8505_tuning_values(ab); - else if (is_ab8540(ab->ab8500)) - /* Phy tuning values for AB8540 */ - ab8500_usb_set_ab8540_tuning_values(ab); - else if (is_ab9540(ab->ab8500)) - /* Phy tuning values for AB9540 */ - ab8500_usb_set_ab9540_tuning_values(ab); /* Needed to enable ID detection. */ ab8500_usb_wd_workaround(ab); @@ -1428,11 +930,6 @@ static int ab8500_usb_probe(struct platform_device *pdev) */ ab8500_usb_restart_phy(ab); - if (ab->flags & AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS) { - if (ab8500_usb_check_vbus_status(ab)) - schedule_work(&ab->vbus_event_work); - } - abx500_usb_link_status_update(ab); dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev); @@ -1445,7 +942,6 @@ static int ab8500_usb_remove(struct platform_device *pdev) struct ab8500_usb *ab = platform_get_drvdata(pdev); cancel_work_sync(&ab->phy_dis_work); - cancel_work_sync(&ab->vbus_event_work); usb_remove_phy(&ab->phy); @@ -1459,8 +955,6 @@ static int ab8500_usb_remove(struct platform_device *pdev) static const struct platform_device_id ab8500_usb_devtype[] = { { .name = "ab8500-usb", }, - { .name = "ab8540-usb", }, - { .name = "ab9540-usb", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, ab8500_usb_devtype); diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 74ba88297991..a53b89be5324 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -63,9 +63,9 @@ static void nop_reset(struct usb_phy_generic *nop) if (!nop->gpiod_reset) return; - gpiod_set_value(nop->gpiod_reset, 1); + gpiod_set_value_cansleep(nop->gpiod_reset, 1); usleep_range(10000, 20000); - gpiod_set_value(nop->gpiod_reset, 0); + gpiod_set_value_cansleep(nop->gpiod_reset, 0); } /* interface to regulator framework */ @@ -159,7 +159,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy) { struct usb_phy_generic *nop = dev_get_drvdata(phy->dev); - gpiod_set_value(nop->gpiod_reset, 1); + gpiod_set_value_cansleep(nop->gpiod_reset, 1); if (!IS_ERR(nop->clk)) clk_disable_unprepare(nop->clk); diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index fbec863350f6..e5aa24c1e4fd 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -578,7 +578,7 @@ static enum usb_charger_type mxs_charger_primary_detection(struct mxs_phy *x) * It must be called after DP is pulled up, which is used to * differentiate DCP and CDP. */ -enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x) +static enum usb_charger_type mxs_charger_secondary_detection(struct mxs_phy *x) { struct regmap *regmap = x->regmap_anatop; int val; diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index f668bfb708d3..0e8d23e51732 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -16,7 +16,7 @@ #include <linux/export.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/io.h> +#include <linux/iopoll.h> #include <linux/gpio.h> #include <linux/of.h> #include <linux/of_device.h> @@ -305,14 +305,10 @@ static int utmip_pad_power_off(struct tegra_usb_phy *phy) static int utmi_wait_register(void __iomem *reg, u32 mask, u32 result) { - unsigned long timeout = 2000; - do { - if ((readl(reg) & mask) == result) - return 0; - udelay(1); - timeout--; - } while (timeout); - return -1; + u32 tmp; + + return readl_poll_timeout(reg, tmp, (tmp & mask) == result, + 2000, 6000); } static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) diff --git a/drivers/usb/roles/Kconfig b/drivers/usb/roles/Kconfig new file mode 100644 index 000000000000..f5a5e6f79f1b --- /dev/null +++ b/drivers/usb/roles/Kconfig @@ -0,0 +1,14 @@ +if USB_ROLE_SWITCH + +config USB_ROLES_INTEL_XHCI + tristate "Intel XHCI USB Role Switch" + depends on ACPI && X86 + help + Driver for the internal USB role switch for switching the USB data + lines between the xHCI host controller and the dwc3 gadget controller + found on various Intel SoCs. + + To compile the driver as a module, choose M here: the module will + be called intel-xhci-usb-role-switch. + +endif # USB_ROLE_SWITCH diff --git a/drivers/usb/roles/Makefile b/drivers/usb/roles/Makefile new file mode 100644 index 000000000000..e44b179ba275 --- /dev/null +++ b/drivers/usb/roles/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_ROLES_INTEL_XHCI) += intel-xhci-usb-role-switch.o diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c new file mode 100644 index 000000000000..de72eedb762e --- /dev/null +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Intel XHCI (Cherry Trail, Broxton and others) USB OTG role switch driver + * + * Copyright (c) 2016-2017 Hans de Goede <hdegoede@redhat.com> + * + * Loosely based on android x86 kernel code which is: + * + * Copyright (C) 2014 Intel Corp. + * + * Author: Wu, Hao + */ + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/usb/role.h> + +/* register definition */ +#define DUAL_ROLE_CFG0 0x68 +#define SW_VBUS_VALID BIT(24) +#define SW_IDPIN_EN BIT(21) +#define SW_IDPIN BIT(20) + +#define DUAL_ROLE_CFG1 0x6c +#define HOST_MODE BIT(29) + +#define DUAL_ROLE_CFG1_POLL_TIMEOUT 1000 + +#define DRV_NAME "intel_xhci_usb_sw" + +struct intel_xhci_usb_data { + struct usb_role_switch *role_sw; + void __iomem *base; +}; + +struct intel_xhci_acpi_match { + const char *hid; + int hrv; +}; + +/* + * ACPI IDs for PMICs which do not support separate data and power role + * detection (USB ACA detection for micro USB OTG), we allow userspace to + * change the role manually on these. + */ +static const struct intel_xhci_acpi_match allow_userspace_ctrl_ids[] = { + { "INT33F4", 3 }, /* X-Powers AXP288 PMIC */ +}; + +static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) +{ + struct intel_xhci_usb_data *data = dev_get_drvdata(dev); + unsigned long timeout; + acpi_status status; + u32 glk, val; + + /* + * On many CHT devices ACPI event (_AEI) handlers read / modify / + * write the cfg0 register, just like we do. Take the ACPI lock + * to avoid us racing with the AML code. + */ + status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk); + if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { + dev_err(dev, "Error could not acquire lock\n"); + return -EIO; + } + + /* Set idpin value as requested */ + val = readl(data->base + DUAL_ROLE_CFG0); + switch (role) { + case USB_ROLE_NONE: + val |= SW_IDPIN; + val &= ~SW_VBUS_VALID; + break; + case USB_ROLE_HOST: + val &= ~SW_IDPIN; + val &= ~SW_VBUS_VALID; + break; + case USB_ROLE_DEVICE: + val |= SW_IDPIN; + val |= SW_VBUS_VALID; + break; + } + val |= SW_IDPIN_EN; + + writel(val, data->base + DUAL_ROLE_CFG0); + + acpi_release_global_lock(glk); + + /* In most case it takes about 600ms to finish mode switching */ + timeout = jiffies + msecs_to_jiffies(DUAL_ROLE_CFG1_POLL_TIMEOUT); + + /* Polling on CFG1 register to confirm mode switch.*/ + do { + val = readl(data->base + DUAL_ROLE_CFG1); + if (!!(val & HOST_MODE) == (role == USB_ROLE_HOST)) + return 0; + + /* Interval for polling is set to about 5 - 10 ms */ + usleep_range(5000, 10000); + } while (time_before(jiffies, timeout)); + + dev_warn(dev, "Timeout waiting for role-switch\n"); + return -ETIMEDOUT; +} + +static enum usb_role intel_xhci_usb_get_role(struct device *dev) +{ + struct intel_xhci_usb_data *data = dev_get_drvdata(dev); + enum usb_role role; + u32 val; + + val = readl(data->base + DUAL_ROLE_CFG0); + + if (!(val & SW_IDPIN)) + role = USB_ROLE_HOST; + else if (val & SW_VBUS_VALID) + role = USB_ROLE_DEVICE; + else + role = USB_ROLE_NONE; + + return role; +} + +static struct usb_role_switch_desc sw_desc = { + .set = intel_xhci_usb_set_role, + .get = intel_xhci_usb_get_role, +}; + +static int intel_xhci_usb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_xhci_usb_data *data; + struct resource *res; + int i; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->base = devm_ioremap_nocache(dev, res->start, resource_size(res)); + if (!data->base) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(allow_userspace_ctrl_ids); i++) + if (acpi_dev_present(allow_userspace_ctrl_ids[i].hid, "1", + allow_userspace_ctrl_ids[i].hrv)) + sw_desc.allow_userspace_control = true; + + platform_set_drvdata(pdev, data); + + data->role_sw = usb_role_switch_register(dev, &sw_desc); + if (IS_ERR(data->role_sw)) + return PTR_ERR(data->role_sw); + + return 0; +} + +static int intel_xhci_usb_remove(struct platform_device *pdev) +{ + struct intel_xhci_usb_data *data = platform_get_drvdata(pdev); + + usb_role_switch_unregister(data->role_sw); + return 0; +} + +static const struct platform_device_id intel_xhci_usb_table[] = { + { .name = DRV_NAME }, + {} +}; +MODULE_DEVICE_TABLE(platform, intel_xhci_usb_table); + +static struct platform_driver intel_xhci_usb_driver = { + .driver = { + .name = DRV_NAME, + }, + .id_table = intel_xhci_usb_table, + .probe = intel_xhci_usb_probe, + .remove = intel_xhci_usb_remove, +}; + +module_platform_driver(intel_xhci_usb_driver); + +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("Intel XHCI USB role switch driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 06d502b3e913..de1e759dd512 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -155,6 +155,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x12B8, 0xEC62) }, /* Link G4+ ECU */ { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */ { USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */ + { USB_DEVICE(0x155A, 0x1006) }, /* ELDAT Easywave RX09 */ { USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */ { USB_DEVICE(0x166A, 0x0301) }, /* Clipsal 5800PC C-Bus Wireless PC Interface */ { USB_DEVICE(0x166A, 0x0303) }, /* Clipsal 5500PCU C-Bus USB interface */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f58c4ff6b387..87202ad5a50d 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -769,6 +769,7 @@ static const struct usb_device_id id_table_combined[] = { .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, { USB_DEVICE(NOVITUS_VID, NOVITUS_BONO_E_PID) }, + { USB_DEVICE(FTDI_VID, RTSYSTEMS_USB_VX8_PID) }, { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_S03_PID) }, { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_59_PID) }, { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_USB_57A_PID) }, @@ -931,6 +932,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_FHE_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) }, { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 8b4ecd2bd297..975d02666c5a 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -923,6 +923,9 @@ /* * RT Systems programming cables for various ham radios */ +/* This device uses the VID of FTDI */ +#define RTSYSTEMS_USB_VX8_PID 0x9e50 /* USB-VX8 USB to 7 pin modular plug for Yaesu VX-8 radio */ + #define RTSYSTEMS_VID 0x2100 /* Vendor ID */ #define RTSYSTEMS_USB_S03_PID 0x9001 /* RTS-03 USB to Serial Adapter */ #define RTSYSTEMS_USB_59_PID 0x9e50 /* USB-59 USB to 8 pin plug */ @@ -1442,6 +1445,12 @@ #define FTDI_CINTERION_MC55I_PID 0xA951 /* + * Product: FirmwareHubEmulator + * Manufacturer: Harman Becker Automotive Systems + */ +#define FTDI_FHE_PID 0xA9A0 + +/* * Product: Comet Caller ID decoder * Manufacturer: Crucible Technologies */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 2d8d9150da0c..c3f252283ab9 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -548,151 +548,15 @@ static void option_instat_callback(struct urb *urb); #define WETELECOM_PRODUCT_6802 0x6802 #define WETELECOM_PRODUCT_WMD300 0x6803 -struct option_blacklist_info { - /* bitmask of interface numbers blacklisted for send_setup */ - const unsigned long sendsetup; - /* bitmask of interface numbers that are reserved */ - const unsigned long reserved; -}; - -static const struct option_blacklist_info four_g_w14_blacklist = { - .sendsetup = BIT(0) | BIT(1), -}; - -static const struct option_blacklist_info four_g_w100_blacklist = { - .sendsetup = BIT(1) | BIT(2), - .reserved = BIT(3), -}; - -static const struct option_blacklist_info alcatel_x200_blacklist = { - .sendsetup = BIT(0) | BIT(1), - .reserved = BIT(4), -}; - -static const struct option_blacklist_info zte_0037_blacklist = { - .sendsetup = BIT(0) | BIT(1), -}; - -static const struct option_blacklist_info zte_k3765_z_blacklist = { - .sendsetup = BIT(0) | BIT(1) | BIT(2), - .reserved = BIT(4), -}; - -static const struct option_blacklist_info zte_ad3812_z_blacklist = { - .sendsetup = BIT(0) | BIT(1) | BIT(2), -}; - -static const struct option_blacklist_info zte_mc2718_z_blacklist = { - .sendsetup = BIT(1) | BIT(2) | BIT(3) | BIT(4), -}; - -static const struct option_blacklist_info zte_mc2716_z_blacklist = { - .sendsetup = BIT(1) | BIT(2) | BIT(3), -}; - -static const struct option_blacklist_info zte_me3620_mbim_blacklist = { - .reserved = BIT(2) | BIT(3) | BIT(4), -}; - -static const struct option_blacklist_info zte_me3620_xl_blacklist = { - .reserved = BIT(3) | BIT(4) | BIT(5), -}; - -static const struct option_blacklist_info zte_zm8620_x_blacklist = { - .reserved = BIT(3) | BIT(4) | BIT(5), -}; - -static const struct option_blacklist_info huawei_cdc12_blacklist = { - .reserved = BIT(1) | BIT(2), -}; - -static const struct option_blacklist_info net_intf0_blacklist = { - .reserved = BIT(0), -}; - -static const struct option_blacklist_info net_intf1_blacklist = { - .reserved = BIT(1), -}; - -static const struct option_blacklist_info net_intf2_blacklist = { - .reserved = BIT(2), -}; - -static const struct option_blacklist_info net_intf3_blacklist = { - .reserved = BIT(3), -}; - -static const struct option_blacklist_info net_intf4_blacklist = { - .reserved = BIT(4), -}; - -static const struct option_blacklist_info net_intf5_blacklist = { - .reserved = BIT(5), -}; - -static const struct option_blacklist_info net_intf6_blacklist = { - .reserved = BIT(6), -}; - -static const struct option_blacklist_info zte_mf626_blacklist = { - .sendsetup = BIT(0) | BIT(1), - .reserved = BIT(4), -}; - -static const struct option_blacklist_info zte_1255_blacklist = { - .reserved = BIT(3) | BIT(4), -}; - -static const struct option_blacklist_info simcom_sim7100e_blacklist = { - .reserved = BIT(5) | BIT(6), -}; - -static const struct option_blacklist_info telit_me910_blacklist = { - .sendsetup = BIT(0), - .reserved = BIT(1) | BIT(3), -}; - -static const struct option_blacklist_info telit_me910_dual_modem_blacklist = { - .sendsetup = BIT(0), - .reserved = BIT(3), -}; - -static const struct option_blacklist_info telit_le910_blacklist = { - .sendsetup = BIT(0), - .reserved = BIT(1) | BIT(2), -}; -static const struct option_blacklist_info telit_le920_blacklist = { - .sendsetup = BIT(0), - .reserved = BIT(1) | BIT(5), -}; +/* Device flags */ -static const struct option_blacklist_info telit_le920a4_blacklist_1 = { - .sendsetup = BIT(0), - .reserved = BIT(1), -}; +/* Interface does not support modem-control requests */ +#define NCTRL(ifnum) ((BIT(ifnum) & 0xff) << 8) -static const struct option_blacklist_info telit_le922_blacklist_usbcfg0 = { - .sendsetup = BIT(2), - .reserved = BIT(0) | BIT(1) | BIT(3), -}; +/* Interface is reserved */ +#define RSVD(ifnum) ((BIT(ifnum) & 0xff) << 0) -static const struct option_blacklist_info telit_le922_blacklist_usbcfg3 = { - .sendsetup = BIT(0), - .reserved = BIT(1) | BIT(2) | BIT(3), -}; - -static const struct option_blacklist_info cinterion_rmnet2_blacklist = { - .reserved = BIT(4) | BIT(5), -}; - -static const struct option_blacklist_info yuga_clm920_nc5_blacklist = { - .reserved = BIT(1) | BIT(4), -}; - -static const struct option_blacklist_info quectel_ep06_blacklist = { - .reserved = BIT(4) | BIT(5), -}; static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, @@ -726,26 +590,26 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) }, { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) }, { USB_DEVICE(QUANTA_VENDOR_ID, 0xea42), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c05, USB_CLASS_COMM, 0x02, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c1f, USB_CLASS_COMM, 0x02, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1c23, USB_CLASS_COMM, 0x02, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t) &net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173S6, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t) &net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1750, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t) &net_intf2_blacklist }, + .driver_info = RSVD(2) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1441, USB_CLASS_COMM, 0x02, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x1442, USB_CLASS_COMM, 0x02, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, + .driver_info = RSVD(1) | RSVD(2) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, + .driver_info = RSVD(1) | RSVD(2) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0x14ac, 0xff, 0xff, 0xff), /* Huawei E1820 */ - .driver_info = (kernel_ulong_t) &net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, + .driver_info = RSVD(1) | RSVD(2) }, { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0xff, 0xff) }, { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x01) }, { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x02) }, @@ -1190,67 +1054,67 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) }, { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */ { USB_DEVICE_AND_INTERFACE_INFO(QUALCOMM_VENDOR_ID, 0x6001, 0xff, 0xff, 0xff), /* 4G LTE usb-modem U901 */ - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */ /* Quectel products using Qualcomm vendor ID */ { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)}, { USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, /* Yuga products use Qualcomm vendor ID */ { USB_DEVICE(QUALCOMM_VENDOR_ID, YUGA_PRODUCT_CLM920_NC5), - .driver_info = (kernel_ulong_t)&yuga_clm920_nc5_blacklist }, + .driver_info = RSVD(1) | RSVD(4) }, /* Quectel products using Quectel vendor ID */ { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC25), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06), - .driver_info = (kernel_ulong_t)&quectel_ep06_blacklist }, + .driver_info = RSVD(4) | RSVD(5) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003), - .driver_info = (kernel_ulong_t)&net_intf0_blacklist }, + .driver_info = RSVD(0) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6004) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6005) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CGU_628A) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHE_628S), - .driver_info = (kernel_ulong_t)&net_intf0_blacklist }, + .driver_info = RSVD(0) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_301), - .driver_info = (kernel_ulong_t)&net_intf0_blacklist }, + .driver_info = RSVD(0) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_628), - .driver_info = (kernel_ulong_t)&net_intf0_blacklist }, + .driver_info = RSVD(0) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_628S) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDU_680) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CDU_685A) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_720S), - .driver_info = (kernel_ulong_t)&net_intf0_blacklist }, + .driver_info = RSVD(0) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7002), - .driver_info = (kernel_ulong_t)&net_intf0_blacklist }, + .driver_info = RSVD(0) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_629K), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7004), - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7005) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CGU_629), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_629S), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CHU_720I), - .driver_info = (kernel_ulong_t)&net_intf0_blacklist }, + .driver_info = RSVD(0) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7212), - .driver_info = (kernel_ulong_t)&net_intf0_blacklist }, + .driver_info = RSVD(0) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7213), - .driver_info = (kernel_ulong_t)&net_intf0_blacklist }, + .driver_info = RSVD(0) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7251), - .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7252), - .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_7253), - .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_DUAL) }, @@ -1258,38 +1122,38 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0), - .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 }, + .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1), - .driver_info = (kernel_ulong_t)&telit_le910_blacklist }, + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2), - .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3), - .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff), - .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 }, + .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910), - .driver_info = (kernel_ulong_t)&telit_me910_blacklist }, + .driver_info = NCTRL(0) | RSVD(1) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM), - .driver_info = (kernel_ulong_t)&telit_me910_dual_modem_blacklist }, + .driver_info = NCTRL(0) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910), - .driver_info = (kernel_ulong_t)&telit_le910_blacklist }, + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4), - .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920), - .driver_info = (kernel_ulong_t)&telit_le920_blacklist }, + .driver_info = NCTRL(0) | RSVD(1) | RSVD(5) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1207) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1208), - .driver_info = (kernel_ulong_t)&telit_le920a4_blacklist_1 }, + .driver_info = NCTRL(0) | RSVD(1) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1211), - .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1212), - .driver_info = (kernel_ulong_t)&telit_le920a4_blacklist_1 }, + .driver_info = NCTRL(0) | RSVD(1) }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1213, 0xff) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1214), - .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, + .driver_info = NCTRL(0) | RSVD(1) | RSVD(2) | RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0004, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0005, 0xff, 0xff, 0xff) }, @@ -1305,58 +1169,58 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0010, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0011, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0018, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0019, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0020, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0022, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0023, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, - 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mf626_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, 0xff, 0xff), + .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&zte_0037_blacklist }, + .driver_info = NCTRL(0) | NCTRL(1) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0038, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0040, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0043, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0044, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0048, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0050, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0056, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0064, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0065, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) }, @@ -1381,26 +1245,26 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0096, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0097, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0106, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0108, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0122, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0123, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0124, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0125, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf6_blacklist }, + .driver_info = RSVD(6) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0126, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0128, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0135, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0136, 0xff, 0xff, 0xff) }, @@ -1416,50 +1280,50 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0159, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0162, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0164, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0189, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0191, 0xff, 0xff, 0xff), /* ZTE EuFi890 */ - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0196, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0197, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0199, 0xff, 0xff, 0xff), /* ZTE MF820S */ - .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0200, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0201, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0254, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0257, 0xff, 0xff, 0xff), /* ZTE MF821 */ - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0265, 0xff, 0xff, 0xff), /* ONDA MT8205 */ - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0284, 0xff, 0xff, 0xff), /* ZTE MF880 */ - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0317, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0326, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0330, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0395, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0412, 0xff, 0xff, 0xff), /* Telewell TW-LTE 4G */ - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0414, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0417, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1018, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1021, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + .driver_info = RSVD(2) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1059, 0xff, 0xff, 0xff) }, @@ -1576,23 +1440,23 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1170, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1244, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1245, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1246, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1247, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1248, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1249, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1250, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1251, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1252, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1253, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1254, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1255, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&zte_1255_blacklist }, + .driver_info = RSVD(3) | RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1256, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1257, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1258, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1259, 0xff, 0xff, 0xff) }, @@ -1607,7 +1471,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1268, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1269, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1270, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1271, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1272, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1273, 0xff, 0xff, 0xff) }, @@ -1643,17 +1507,17 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1303, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1333, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1401, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + .driver_info = RSVD(2) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + .driver_info = RSVD(2) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1424, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + .driver_info = RSVD(2) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1425, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + .driver_info = RSVD(2) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1426, 0xff, 0xff, 0xff), /* ZTE MF91 */ - .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + .driver_info = RSVD(2) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1428, 0xff, 0xff, 0xff), /* Telewell TW-LTE 4G v2 */ - .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + .driver_info = RSVD(2) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1533, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1534, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1535, 0xff, 0xff, 0xff) }, @@ -1671,8 +1535,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1596, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1598, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1600, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, - 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, 0xff, 0xff), + .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) | RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, /* ZTE CDMA products */ @@ -1683,20 +1547,20 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0073, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0094, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0130, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, + .driver_info = RSVD(1) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0133, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0141, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0147, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0152, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0168, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0170, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0176, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff42, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff43, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff44, 0xff, 0xff, 0xff) }, @@ -1848,19 +1712,19 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710T, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2718, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&zte_mc2718_z_blacklist }, + .driver_info = NCTRL(1) | NCTRL(2) | NCTRL(3) | NCTRL(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AD3812, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist }, + .driver_info = NCTRL(0) | NCTRL(1) | NCTRL(2) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff), - .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist }, + .driver_info = NCTRL(1) | NCTRL(2) | NCTRL(3) }, { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_L), - .driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist }, + .driver_info = RSVD(3) | RSVD(4) | RSVD(5) }, { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_MBIM), - .driver_info = (kernel_ulong_t)&zte_me3620_mbim_blacklist }, + .driver_info = RSVD(2) | RSVD(3) | RSVD(4) }, { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_X), - .driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist }, + .driver_info = RSVD(3) | RSVD(4) | RSVD(5) }, { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ZM8620_X), - .driver_info = (kernel_ulong_t)&zte_zm8620_x_blacklist }, + .driver_info = RSVD(3) | RSVD(4) | RSVD(5) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) }, @@ -1880,37 +1744,34 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(ALINK_VENDOR_ID, ALINK_PRODUCT_PH300) }, { USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) }, { USB_DEVICE(ALINK_VENDOR_ID, SIMCOM_PRODUCT_SIM7100E), - .driver_info = (kernel_ulong_t)&simcom_sim7100e_blacklist }, + .driver_info = RSVD(5) | RSVD(6) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S_X200), - .driver_info = (kernel_ulong_t)&alcatel_x200_blacklist - }, + .driver_info = NCTRL(0) | NCTRL(1) | RSVD(4) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X220_X500D), - .driver_info = (kernel_ulong_t)&net_intf6_blacklist }, + .driver_info = RSVD(6) }, { USB_DEVICE(ALCATEL_VENDOR_ID, 0x0052), - .driver_info = (kernel_ulong_t)&net_intf6_blacklist }, + .driver_info = RSVD(6) }, { USB_DEVICE(ALCATEL_VENDOR_ID, 0x00b6), - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE(ALCATEL_VENDOR_ID, 0x00b7), - .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, + .driver_info = RSVD(5) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L100V), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_L800MA), - .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, + .driver_info = RSVD(2) }, { USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) }, { USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14), - .driver_info = (kernel_ulong_t)&four_g_w14_blacklist - }, + .driver_info = NCTRL(0) | NCTRL(1) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W100), - .driver_info = (kernel_ulong_t)&four_g_w100_blacklist - }, + .driver_info = NCTRL(1) | NCTRL(2) | RSVD(3) }, {USB_DEVICE(LONGCHEER_VENDOR_ID, FUJISOFT_PRODUCT_FS040U), - .driver_info = (kernel_ulong_t)&net_intf3_blacklist}, + .driver_info = RSVD(3)}, { USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, SPEEDUP_PRODUCT_SU9800, 0xff) }, { USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9801, 0xff), - .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, + .driver_info = RSVD(3) }, { USB_DEVICE_INTERFACE_CLASS(LONGCHEER_VENDOR_ID, 0x9803, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, ZOOM_PRODUCT_4597) }, { USB_DEVICE(LONGCHEER_VENDOR_ID, IBALL_3_5G_CONNECT) }, { USB_DEVICE(HAIER_VENDOR_ID, HAIER_PRODUCT_CE100) }, @@ -1936,14 +1797,14 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EU3_E) }, { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_EU3_P) }, { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX, 0xff) }, { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_2RMNET, 0xff), - .driver_info = (kernel_ulong_t)&cinterion_rmnet2_blacklist }, + .driver_info = RSVD(4) | RSVD(5) }, { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_AUDIO, 0xff), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_2RMNET, 0xff) }, { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) }, { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) }, @@ -1953,20 +1814,20 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) }, /* HC28 enumerates with Siemens or Cinterion VID depending on FW revision */ { USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) }, { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD120), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD140), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD145) }, { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD155), - .driver_info = (kernel_ulong_t)&net_intf6_blacklist }, + .driver_info = RSVD(6) }, { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD200), - .driver_info = (kernel_ulong_t)&net_intf6_blacklist }, + .driver_info = RSVD(6) }, { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD160), - .driver_info = (kernel_ulong_t)&net_intf6_blacklist }, + .driver_info = RSVD(6) }, { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD500), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */ { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730 LTE USB modem.*/ { USB_DEVICE(YUGA_VENDOR_ID, YUGA_PRODUCT_CEM600) }, @@ -2043,9 +1904,9 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600E) }, { USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, TPLINK_PRODUCT_LTE, 0xff, 0x00, 0x00) }, /* TP-Link LTE Module */ { USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180), - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(TPLINK_VENDOR_ID, 0x9000), /* TP-Link MA260 */ - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE(CHANGHONG_VENDOR_ID, CHANGHONG_PRODUCT_CH690) }, { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d01, 0xff) }, /* D-Link DWM-156 (variant) */ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d02, 0xff) }, @@ -2053,9 +1914,9 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d0e, 0xff) }, /* D-Link DWM-157 C1 */ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */ - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e35, 0xff), /* D-Link DWM-222 */ - .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + .driver_info = RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */ @@ -2114,11 +1975,10 @@ static int option_probe(struct usb_serial *serial, { struct usb_interface_descriptor *iface_desc = &serial->interface->cur_altsetting->desc; - struct usb_device_descriptor *dev_desc = &serial->dev->descriptor; - const struct option_blacklist_info *blacklist; + unsigned long device_flags = id->driver_info; /* Never bind to the CD-Rom emulation interface */ - if (iface_desc->bInterfaceClass == 0x08) + if (iface_desc->bInterfaceClass == USB_CLASS_MASS_STORAGE) return -ENODEV; /* @@ -2126,21 +1986,11 @@ static int option_probe(struct usb_serial *serial, * the same class/subclass/protocol as the serial interfaces. Look at * the Windows driver .INF files for reserved interface numbers. */ - blacklist = (void *)id->driver_info; - if (blacklist && test_bit(iface_desc->bInterfaceNumber, - &blacklist->reserved)) - return -ENODEV; - /* - * Don't bind network interface on Samsung GT-B3730, it is handled by - * a separate module. - */ - if (dev_desc->idVendor == cpu_to_le16(SAMSUNG_VENDOR_ID) && - dev_desc->idProduct == cpu_to_le16(SAMSUNG_PRODUCT_GT_B3730) && - iface_desc->bInterfaceClass != USB_CLASS_CDC_DATA) + if (device_flags & RSVD(iface_desc->bInterfaceNumber)) return -ENODEV; - /* Store the blacklist info so we can use it during attach. */ - usb_set_serial_data(serial, (void *)blacklist); + /* Store the device flags so we can use them during attach. */ + usb_set_serial_data(serial, (void *)device_flags); return 0; } @@ -2148,22 +1998,21 @@ static int option_probe(struct usb_serial *serial, static int option_attach(struct usb_serial *serial) { struct usb_interface_descriptor *iface_desc; - const struct option_blacklist_info *blacklist; struct usb_wwan_intf_private *data; + unsigned long device_flags; data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) return -ENOMEM; - /* Retrieve blacklist info stored at probe. */ - blacklist = usb_get_serial_data(serial); + /* Retrieve device flags stored at probe. */ + device_flags = (unsigned long)usb_get_serial_data(serial); iface_desc = &serial->interface->cur_altsetting->desc; - if (!blacklist || !test_bit(iface_desc->bInterfaceNumber, - &blacklist->sendsetup)) { + if (!(device_flags & NCTRL(iface_desc->bInterfaceNumber))) data->use_send_setup = 1; - } + spin_lock_init(&data->susp_lock); usb_set_serial_data(serial, data); diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index bcb2744c5977..030f88cb0c3f 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -48,6 +48,7 @@ if TYPEC config TYPEC_TCPM tristate "USB Type-C Port Controller Manager" depends on USB + select USB_ROLE_SWITCH help The Type-C Port Controller Manager provides a USB PD and USB Type-C state machine for use with Type-C Port Controllers. @@ -84,4 +85,6 @@ config TYPEC_TPS6598X If you choose to build this driver as a dynamically linked module, the module will be called tps6598x.ko. +source "drivers/usb/typec/mux/Kconfig" + endif # TYPEC diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index bb3138a6eaac..1f599a6c30cc 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,7 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC) += typec.o +typec-y := class.o mux.o obj-$(CONFIG_TYPEC_TCPM) += tcpm.o obj-y += fusb302/ obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o obj-$(CONFIG_TYPEC_UCSI) += ucsi/ obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o +obj-$(CONFIG_TYPEC) += mux/ diff --git a/drivers/usb/typec/typec.c b/drivers/usb/typec/class.c index 735726ced602..53df10df2f9d 100644 --- a/drivers/usb/typec/typec.c +++ b/drivers/usb/typec/class.c @@ -11,6 +11,7 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <linux/usb/typec.h> +#include <linux/usb/typec_mux.h> struct typec_mode { int index; @@ -70,6 +71,10 @@ struct typec_port { enum typec_port_type port_type; struct mutex port_type_lock; + enum typec_orientation orientation; + struct typec_switch *sw; + struct typec_mux *mux; + const struct typec_capability *cap; }; @@ -92,6 +97,7 @@ static const struct device_type typec_port_dev_type; static DEFINE_IDA(typec_index_ida); static struct class *typec_class; +/* ------------------------------------------------------------------------- */ /* Common attributes */ static const char * const typec_accessory_modes[] = { @@ -276,10 +282,10 @@ typec_altmode_roles_show(struct device *dev, struct device_attribute *attr, ssize_t ret; switch (mode->roles) { - case TYPEC_PORT_DFP: + case TYPEC_PORT_SRC: ret = sprintf(buf, "source\n"); break; - case TYPEC_PORT_UFP: + case TYPEC_PORT_SNK: ret = sprintf(buf, "sink\n"); break; case TYPEC_PORT_DRP: @@ -386,7 +392,7 @@ typec_register_altmode(struct device *parent, alt = kzalloc(sizeof(*alt), GFP_KERNEL); if (!alt) - return NULL; + return ERR_PTR(-ENOMEM); alt->svid = desc->svid; alt->n_modes = desc->n_modes; @@ -402,7 +408,7 @@ typec_register_altmode(struct device *parent, dev_err(parent, "failed to register alternate mode (%d)\n", ret); put_device(&alt->dev); - return NULL; + return ERR_PTR(ret); } return alt; @@ -417,7 +423,7 @@ typec_register_altmode(struct device *parent, */ void typec_unregister_altmode(struct typec_altmode *alt) { - if (alt) + if (!IS_ERR_OR_NULL(alt)) device_unregister(&alt->dev); } EXPORT_SYMBOL_GPL(typec_unregister_altmode); @@ -509,7 +515,7 @@ EXPORT_SYMBOL_GPL(typec_partner_register_altmode); * * Registers a device for USB Type-C Partner described in @desc. * - * Returns handle to the partner on success or NULL on failure. + * Returns handle to the partner on success or ERR_PTR on failure. */ struct typec_partner *typec_register_partner(struct typec_port *port, struct typec_partner_desc *desc) @@ -519,7 +525,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, partner = kzalloc(sizeof(*partner), GFP_KERNEL); if (!partner) - return NULL; + return ERR_PTR(-ENOMEM); partner->usb_pd = desc->usb_pd; partner->accessory = desc->accessory; @@ -542,7 +548,7 @@ struct typec_partner *typec_register_partner(struct typec_port *port, if (ret) { dev_err(&port->dev, "failed to register partner (%d)\n", ret); put_device(&partner->dev); - return NULL; + return ERR_PTR(ret); } return partner; @@ -557,7 +563,7 @@ EXPORT_SYMBOL_GPL(typec_register_partner); */ void typec_unregister_partner(struct typec_partner *partner) { - if (partner) + if (!IS_ERR_OR_NULL(partner)) device_unregister(&partner->dev); } EXPORT_SYMBOL_GPL(typec_unregister_partner); @@ -587,7 +593,7 @@ static const struct device_type typec_plug_dev_type = { * the plug lists in response to Discover Modes command need to be listed in an * array in @desc. * - * Returns handle to the alternate mode on success or NULL on failure. + * Returns handle to the alternate mode on success or ERR_PTR on failure. */ struct typec_altmode * typec_plug_register_altmode(struct typec_plug *plug, @@ -606,7 +612,7 @@ EXPORT_SYMBOL_GPL(typec_plug_register_altmode); * Cable Plug represents a plug with electronics in it that can response to USB * Power Delivery SOP Prime or SOP Double Prime packages. * - * Returns handle to the cable plug on success or NULL on failure. + * Returns handle to the cable plug on success or ERR_PTR on failure. */ struct typec_plug *typec_register_plug(struct typec_cable *cable, struct typec_plug_desc *desc) @@ -617,7 +623,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, plug = kzalloc(sizeof(*plug), GFP_KERNEL); if (!plug) - return NULL; + return ERR_PTR(-ENOMEM); sprintf(name, "plug%d", desc->index); @@ -631,7 +637,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable, if (ret) { dev_err(&cable->dev, "failed to register plug (%d)\n", ret); put_device(&plug->dev); - return NULL; + return ERR_PTR(ret); } return plug; @@ -646,7 +652,7 @@ EXPORT_SYMBOL_GPL(typec_register_plug); */ void typec_unregister_plug(struct typec_plug *plug) { - if (plug) + if (!IS_ERR_OR_NULL(plug)) device_unregister(&plug->dev); } EXPORT_SYMBOL_GPL(typec_unregister_plug); @@ -724,7 +730,7 @@ EXPORT_SYMBOL_GPL(typec_cable_set_identity); * Registers a device for USB Type-C Cable described in @desc. The cable will be * parent for the optional cable plug devises. * - * Returns handle to the cable on success or NULL on failure. + * Returns handle to the cable on success or ERR_PTR on failure. */ struct typec_cable *typec_register_cable(struct typec_port *port, struct typec_cable_desc *desc) @@ -734,7 +740,7 @@ struct typec_cable *typec_register_cable(struct typec_port *port, cable = kzalloc(sizeof(*cable), GFP_KERNEL); if (!cable) - return NULL; + return ERR_PTR(-ENOMEM); cable->type = desc->type; cable->active = desc->active; @@ -757,7 +763,7 @@ struct typec_cable *typec_register_cable(struct typec_port *port, if (ret) { dev_err(&port->dev, "failed to register cable (%d)\n", ret); put_device(&cable->dev); - return NULL; + return ERR_PTR(ret); } return cable; @@ -772,7 +778,7 @@ EXPORT_SYMBOL_GPL(typec_register_cable); */ void typec_unregister_cable(struct typec_cable *cable) { - if (cable) + if (!IS_ERR_OR_NULL(cable)) device_unregister(&cable->dev); } EXPORT_SYMBOL_GPL(typec_unregister_cable); @@ -791,14 +797,14 @@ static const char * const typec_data_roles[] = { }; static const char * const typec_port_types[] = { - [TYPEC_PORT_DFP] = "source", - [TYPEC_PORT_UFP] = "sink", + [TYPEC_PORT_SRC] = "source", + [TYPEC_PORT_SNK] = "sink", [TYPEC_PORT_DRP] = "dual", }; static const char * const typec_port_types_drp[] = { - [TYPEC_PORT_DFP] = "dual [source] sink", - [TYPEC_PORT_UFP] = "dual source [sink]", + [TYPEC_PORT_SRC] = "dual [source] sink", + [TYPEC_PORT_SNK] = "dual source [sink]", [TYPEC_PORT_DRP] = "[dual] source sink", }; @@ -869,9 +875,7 @@ static ssize_t data_role_store(struct device *dev, return ret; mutex_lock(&port->port_type_lock); - if (port->port_type != TYPEC_PORT_DRP) { - dev_dbg(dev, "port type fixed at \"%s\"", - typec_port_types[port->port_type]); + if (port->cap->data != TYPEC_PORT_DRD) { ret = -EOPNOTSUPP; goto unlock_and_ret; } @@ -891,7 +895,7 @@ static ssize_t data_role_show(struct device *dev, { struct typec_port *port = to_typec_port(dev); - if (port->cap->type == TYPEC_PORT_DRP) + if (port->cap->data == TYPEC_PORT_DRD) return sprintf(buf, "%s\n", port->data_role == TYPEC_HOST ? "[host] device" : "host [device]"); @@ -1137,6 +1141,8 @@ static void typec_release(struct device *dev) struct typec_port *port = to_typec_port(dev); ida_simple_remove(&typec_index_ida, port->id); + typec_switch_put(port->sw); + typec_mux_put(port->mux); kfree(port); } @@ -1246,6 +1252,47 @@ void typec_set_pwr_opmode(struct typec_port *port, } EXPORT_SYMBOL_GPL(typec_set_pwr_opmode); +/* ------------------------------------------ */ +/* API for Multiplexer/DeMultiplexer Switches */ + +/** + * typec_set_orientation - Set USB Type-C cable plug orientation + * @port: USB Type-C Port + * @orientation: USB Type-C cable plug orientation + * + * Set cable plug orientation for @port. + */ +int typec_set_orientation(struct typec_port *port, + enum typec_orientation orientation) +{ + int ret; + + if (port->sw) { + ret = port->sw->set(port->sw, orientation); + if (ret) + return ret; + } + + port->orientation = orientation; + + return 0; +} +EXPORT_SYMBOL_GPL(typec_set_orientation); + +/** + * typec_set_mode - Set mode of operation for USB Type-C connector + * @port: USB Type-C port for the connector + * @mode: Operation mode for the connector + * + * Set mode @mode for @port. This function will configure the muxes needed to + * enter @mode. + */ +int typec_set_mode(struct typec_port *port, int mode) +{ + return port->mux ? port->mux->set(port->mux, mode) : 0; +} +EXPORT_SYMBOL_GPL(typec_set_mode); + /* --------------------------------------- */ /** @@ -1256,7 +1303,7 @@ EXPORT_SYMBOL_GPL(typec_set_pwr_opmode); * This routine is used to register an alternate mode that @port is capable of * supporting. * - * Returns handle to the alternate mode on success or NULL on failure. + * Returns handle to the alternate mode on success or ERR_PTR on failure. */ struct typec_altmode * typec_port_register_altmode(struct typec_port *port, @@ -1273,41 +1320,67 @@ EXPORT_SYMBOL_GPL(typec_port_register_altmode); * * Registers a device for USB Type-C Port described in @cap. * - * Returns handle to the port on success or NULL on failure. + * Returns handle to the port on success or ERR_PTR on failure. */ struct typec_port *typec_register_port(struct device *parent, const struct typec_capability *cap) { struct typec_port *port; - int role; int ret; int id; port = kzalloc(sizeof(*port), GFP_KERNEL); if (!port) - return NULL; + return ERR_PTR(-ENOMEM); id = ida_simple_get(&typec_index_ida, 0, 0, GFP_KERNEL); if (id < 0) { kfree(port); - return NULL; + return ERR_PTR(id); } - if (cap->type == TYPEC_PORT_DFP) - role = TYPEC_SOURCE; - else if (cap->type == TYPEC_PORT_UFP) - role = TYPEC_SINK; - else - role = cap->prefer_role; + port->sw = typec_switch_get(cap->fwnode ? &port->dev : parent); + if (IS_ERR(port->sw)) { + ret = PTR_ERR(port->sw); + goto err_switch; + } - if (role == TYPEC_SOURCE) { - port->data_role = TYPEC_HOST; + port->mux = typec_mux_get(cap->fwnode ? &port->dev : parent); + if (IS_ERR(port->mux)) { + ret = PTR_ERR(port->mux); + goto err_mux; + } + + switch (cap->type) { + case TYPEC_PORT_SRC: port->pwr_role = TYPEC_SOURCE; port->vconn_role = TYPEC_SOURCE; - } else { - port->data_role = TYPEC_DEVICE; + break; + case TYPEC_PORT_SNK: port->pwr_role = TYPEC_SINK; port->vconn_role = TYPEC_SINK; + break; + case TYPEC_PORT_DRP: + if (cap->prefer_role != TYPEC_NO_PREFERRED_ROLE) + port->pwr_role = cap->prefer_role; + else + port->pwr_role = TYPEC_SINK; + break; + } + + switch (cap->data) { + case TYPEC_PORT_DFP: + port->data_role = TYPEC_HOST; + break; + case TYPEC_PORT_UFP: + port->data_role = TYPEC_DEVICE; + break; + case TYPEC_PORT_DRD: + if (cap->prefer_role == TYPEC_SOURCE) + port->data_role = TYPEC_HOST; + else + port->data_role = TYPEC_DEVICE; + break; } port->id = id; @@ -1326,10 +1399,19 @@ struct typec_port *typec_register_port(struct device *parent, if (ret) { dev_err(parent, "failed to register port (%d)\n", ret); put_device(&port->dev); - return NULL; + return ERR_PTR(ret); } return port; + +err_mux: + typec_switch_put(port->sw); + +err_switch: + ida_simple_remove(&typec_index_ida, port->id); + kfree(port); + + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(typec_register_port); @@ -1341,7 +1423,7 @@ EXPORT_SYMBOL_GPL(typec_register_port); */ void typec_unregister_port(struct typec_port *port) { - if (port) + if (!IS_ERR_OR_NULL(port)) device_unregister(&port->dev); } EXPORT_SYMBOL_GPL(typec_unregister_port); diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c index dcd8ef085b30..703617129067 100644 --- a/drivers/usb/typec/fusb302/fusb302.c +++ b/drivers/usb/typec/fusb302/fusb302.c @@ -199,7 +199,7 @@ static void fusb302_log(struct fusb302_chip *chip, const char *fmt, ...) va_end(args); } -static int fusb302_seq_show(struct seq_file *s, void *v) +static int fusb302_debug_show(struct seq_file *s, void *v) { struct fusb302_chip *chip = (struct fusb302_chip *)s->private; int tail; @@ -216,18 +216,7 @@ static int fusb302_seq_show(struct seq_file *s, void *v) return 0; } - -static int fusb302_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, fusb302_seq_show, inode->i_private); -} - -static const struct file_operations fusb302_debug_operations = { - .open = fusb302_debug_open, - .llseek = seq_lseek, - .read = seq_read, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(fusb302_debug); static struct dentry *rootdir; @@ -242,7 +231,7 @@ static int fusb302_debugfs_init(struct fusb302_chip *chip) chip->dentry = debugfs_create_file(dev_name(chip->dev), S_IFREG | 0444, rootdir, - chip, &fusb302_debug_operations); + chip, &fusb302_debug_fops); return 0; } @@ -1230,6 +1219,7 @@ static const struct tcpc_config fusb302_tcpc_config = { .max_snk_mw = 15000, .operating_snk_mw = 2500, .type = TYPEC_PORT_DRP, + .data = TYPEC_PORT_DRD, .default_role = TYPEC_SINK, .alt_modes = NULL, }; @@ -1249,7 +1239,6 @@ static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev) fusb302_tcpc_dev->set_roles = tcpm_set_roles; fusb302_tcpc_dev->start_drp_toggling = tcpm_start_drp_toggling; fusb302_tcpc_dev->pd_transmit = tcpm_pd_transmit; - fusb302_tcpc_dev->mux = NULL; } static const char * const cc_polarity_name[] = { diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c new file mode 100644 index 000000000000..f89093bd7185 --- /dev/null +++ b/drivers/usb/typec/mux.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * USB Type-C Multiplexer/DeMultiplexer Switch support + * + * Copyright (C) 2018 Intel Corporation + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/usb/typec_mux.h> + +static DEFINE_MUTEX(switch_lock); +static DEFINE_MUTEX(mux_lock); +static LIST_HEAD(switch_list); +static LIST_HEAD(mux_list); + +static void *typec_switch_match(struct device_connection *con, int ep, + void *data) +{ + struct typec_switch *sw; + + list_for_each_entry(sw, &switch_list, entry) + if (!strcmp(con->endpoint[ep], dev_name(sw->dev))) + return sw; + + /* + * We only get called if a connection was found, tell the caller to + * wait for the switch to show up. + */ + return ERR_PTR(-EPROBE_DEFER); +} + +/** + * typec_switch_get - Find USB Type-C orientation switch + * @dev: The caller device + * + * Finds a switch linked with @dev. Returns a reference to the switch on + * success, NULL if no matching connection was found, or + * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch + * has not been enumerated yet. + */ +struct typec_switch *typec_switch_get(struct device *dev) +{ + struct typec_switch *sw; + + mutex_lock(&switch_lock); + sw = device_connection_find_match(dev, "typec-switch", NULL, + typec_switch_match); + if (!IS_ERR_OR_NULL(sw)) + get_device(sw->dev); + mutex_unlock(&switch_lock); + + return sw; +} +EXPORT_SYMBOL_GPL(typec_switch_get); + +/** + * typec_put_switch - Release USB Type-C orientation switch + * @sw: USB Type-C orientation switch + * + * Decrement reference count for @sw. + */ +void typec_switch_put(struct typec_switch *sw) +{ + if (!IS_ERR_OR_NULL(sw)) + put_device(sw->dev); +} +EXPORT_SYMBOL_GPL(typec_switch_put); + +/** + * typec_switch_register - Register USB Type-C orientation switch + * @sw: USB Type-C orientation switch + * + * This function registers a switch that can be used for routing the correct + * data pairs depending on the cable plug orientation from the USB Type-C + * connector to the USB controllers. USB Type-C plugs can be inserted + * right-side-up or upside-down. + */ +int typec_switch_register(struct typec_switch *sw) +{ + mutex_lock(&switch_lock); + list_add_tail(&sw->entry, &switch_list); + mutex_unlock(&switch_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(typec_switch_register); + +/** + * typec_switch_unregister - Unregister USB Type-C orientation switch + * @sw: USB Type-C orientation switch + * + * Unregister switch that was registered with typec_switch_register(). + */ +void typec_switch_unregister(struct typec_switch *sw) +{ + mutex_lock(&switch_lock); + list_del(&sw->entry); + mutex_unlock(&switch_lock); +} +EXPORT_SYMBOL_GPL(typec_switch_unregister); + +/* ------------------------------------------------------------------------- */ + +static void *typec_mux_match(struct device_connection *con, int ep, void *data) +{ + struct typec_mux *mux; + + list_for_each_entry(mux, &mux_list, entry) + if (!strcmp(con->endpoint[ep], dev_name(mux->dev))) + return mux; + + /* + * We only get called if a connection was found, tell the caller to + * wait for the switch to show up. + */ + return ERR_PTR(-EPROBE_DEFER); +} + +/** + * typec_mux_get - Find USB Type-C Multiplexer + * @dev: The caller device + * + * Finds a mux linked to the caller. This function is primarily meant for the + * Type-C drivers. Returns a reference to the mux on success, NULL if no + * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection + * was found but the mux has not been enumerated yet. + */ +struct typec_mux *typec_mux_get(struct device *dev) +{ + struct typec_mux *mux; + + mutex_lock(&mux_lock); + mux = device_connection_find_match(dev, "typec-mux", NULL, + typec_mux_match); + if (!IS_ERR_OR_NULL(mux)) + get_device(mux->dev); + mutex_unlock(&mux_lock); + + return mux; +} +EXPORT_SYMBOL_GPL(typec_mux_get); + +/** + * typec_mux_put - Release handle to a Multiplexer + * @mux: USB Type-C Connector Multiplexer/DeMultiplexer + * + * Decrements reference count for @mux. + */ +void typec_mux_put(struct typec_mux *mux) +{ + if (!IS_ERR_OR_NULL(mux)) + put_device(mux->dev); +} +EXPORT_SYMBOL_GPL(typec_mux_put); + +/** + * typec_mux_register - Register Multiplexer routing USB Type-C pins + * @mux: USB Type-C Connector Multiplexer/DeMultiplexer + * + * USB Type-C connectors can be used for alternate modes of operation besides + * USB when Accessory/Alternate Modes are supported. With some of those modes, + * the pins on the connector need to be reconfigured. This function registers + * multiplexer switches routing the pins on the connector. + */ +int typec_mux_register(struct typec_mux *mux) +{ + mutex_lock(&mux_lock); + list_add_tail(&mux->entry, &mux_list); + mutex_unlock(&mux_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(typec_mux_register); + +/** + * typec_mux_unregister - Unregister Multiplexer Switch + * @sw: USB Type-C Connector Multiplexer/DeMultiplexer + * + * Unregister mux that was registered with typec_mux_register(). + */ +void typec_mux_unregister(struct typec_mux *mux) +{ + mutex_lock(&mux_lock); + list_del(&mux->entry); + mutex_unlock(&mux_lock); +} +EXPORT_SYMBOL_GPL(typec_mux_unregister); diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig new file mode 100644 index 000000000000..9a954d2b8d8f --- /dev/null +++ b/drivers/usb/typec/mux/Kconfig @@ -0,0 +1,10 @@ +menu "USB Type-C Multiplexer/DeMultiplexer Switch support" + +config TYPEC_MUX_PI3USB30532 + tristate "Pericom PI3USB30532 Type-C cross switch driver" + depends on I2C + help + Say Y or M if your system has a Pericom PI3USB30532 Type-C cross + switch / mux chip found on some devices with a Type-C port. + +endmenu diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile new file mode 100644 index 000000000000..1332e469b8a0 --- /dev/null +++ b/drivers/usb/typec/mux/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c new file mode 100644 index 000000000000..b0e88db60ecf --- /dev/null +++ b/drivers/usb/typec/mux/pi3usb30532.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Pericom PI3USB30532 Type-C cross switch / mux driver + * + * Copyright (c) 2017-2018 Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/usb/tcpm.h> +#include <linux/usb/typec_mux.h> + +#define PI3USB30532_CONF 0x00 + +#define PI3USB30532_CONF_OPEN 0x00 +#define PI3USB30532_CONF_SWAP 0x01 +#define PI3USB30532_CONF_4LANE_DP 0x02 +#define PI3USB30532_CONF_USB3 0x04 +#define PI3USB30532_CONF_USB3_AND_2LANE_DP 0x06 + +struct pi3usb30532 { + struct i2c_client *client; + struct mutex lock; /* protects the cached conf register */ + struct typec_switch sw; + struct typec_mux mux; + u8 conf; +}; + +static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf) +{ + int ret = 0; + + if (pi->conf == new_conf) + return 0; + + ret = i2c_smbus_write_byte_data(pi->client, PI3USB30532_CONF, new_conf); + if (ret) { + dev_err(&pi->client->dev, "Error writing conf: %d\n", ret); + return ret; + } + + pi->conf = new_conf; + return 0; +} + +static int pi3usb30532_sw_set(struct typec_switch *sw, + enum typec_orientation orientation) +{ + struct pi3usb30532 *pi = container_of(sw, struct pi3usb30532, sw); + u8 new_conf; + int ret; + + mutex_lock(&pi->lock); + new_conf = pi->conf; + + switch (orientation) { + case TYPEC_ORIENTATION_NONE: + new_conf = PI3USB30532_CONF_OPEN; + break; + case TYPEC_ORIENTATION_NORMAL: + new_conf &= ~PI3USB30532_CONF_SWAP; + break; + case TYPEC_ORIENTATION_REVERSE: + new_conf |= PI3USB30532_CONF_SWAP; + break; + } + + ret = pi3usb30532_set_conf(pi, new_conf); + mutex_unlock(&pi->lock); + + return ret; +} + +static int pi3usb30532_mux_set(struct typec_mux *mux, int state) +{ + struct pi3usb30532 *pi = container_of(mux, struct pi3usb30532, mux); + u8 new_conf; + int ret; + + mutex_lock(&pi->lock); + new_conf = pi->conf; + + switch (state) { + case TYPEC_MUX_NONE: + new_conf = PI3USB30532_CONF_OPEN; + break; + case TYPEC_MUX_USB: + new_conf = (new_conf & PI3USB30532_CONF_SWAP) | + PI3USB30532_CONF_USB3; + break; + case TYPEC_MUX_DP: + new_conf = (new_conf & PI3USB30532_CONF_SWAP) | + PI3USB30532_CONF_4LANE_DP; + break; + case TYPEC_MUX_DOCK: + new_conf = (new_conf & PI3USB30532_CONF_SWAP) | + PI3USB30532_CONF_USB3_AND_2LANE_DP; + break; + } + + ret = pi3usb30532_set_conf(pi, new_conf); + mutex_unlock(&pi->lock); + + return ret; +} + +static int pi3usb30532_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pi3usb30532 *pi; + int ret; + + pi = devm_kzalloc(dev, sizeof(*pi), GFP_KERNEL); + if (!pi) + return -ENOMEM; + + pi->client = client; + pi->sw.dev = dev; + pi->sw.set = pi3usb30532_sw_set; + pi->mux.dev = dev; + pi->mux.set = pi3usb30532_mux_set; + mutex_init(&pi->lock); + + ret = i2c_smbus_read_byte_data(client, PI3USB30532_CONF); + if (ret < 0) { + dev_err(dev, "Error reading config register %d\n", ret); + return ret; + } + pi->conf = ret; + + ret = typec_switch_register(&pi->sw); + if (ret) { + dev_err(dev, "Error registering typec switch: %d\n", ret); + return ret; + } + + ret = typec_mux_register(&pi->mux); + if (ret) { + typec_switch_unregister(&pi->sw); + dev_err(dev, "Error registering typec mux: %d\n", ret); + return ret; + } + + i2c_set_clientdata(client, pi); + return 0; +} + +static int pi3usb30532_remove(struct i2c_client *client) +{ + struct pi3usb30532 *pi = i2c_get_clientdata(client); + + typec_mux_unregister(&pi->mux); + typec_switch_unregister(&pi->sw); + return 0; +} + +static const struct i2c_device_id pi3usb30532_table[] = { + { "pi3usb30532" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pi3usb30532_table); + +static struct i2c_driver pi3usb30532_driver = { + .driver = { + .name = "pi3usb30532", + }, + .probe_new = pi3usb30532_probe, + .remove = pi3usb30532_remove, + .id_table = pi3usb30532_table, +}; + +module_i2c_driver(pi3usb30532_driver); + +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("Pericom PI3USB30532 Type-C mux driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index 8b637a4b474b..677d12138dbd 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -20,6 +20,7 @@ #include <linux/usb/pd.h> #include <linux/usb/pd_bdo.h> #include <linux/usb/pd_vdo.h> +#include <linux/usb/role.h> #include <linux/usb/tcpm.h> #include <linux/usb/typec.h> #include <linux/workqueue.h> @@ -176,6 +177,7 @@ struct tcpm_port { struct typec_port *typec_port; struct tcpc_dev *tcpc; + struct usb_role_switch *role_sw; enum typec_role vconn_role; enum typec_role pwr_role; @@ -345,7 +347,7 @@ static enum tcpm_state tcpm_default_state(struct tcpm_port *port) else if (port->tcpc->config->default_role == TYPEC_SINK) return SNK_UNATTACHED; /* Fall through to return SRC_UNATTACHED */ - } else if (port->port_type == TYPEC_PORT_UFP) { + } else if (port->port_type == TYPEC_PORT_SNK) { return SNK_UNATTACHED; } return SRC_UNATTACHED; @@ -503,7 +505,7 @@ static void tcpm_log_source_caps(struct tcpm_port *port) } } -static int tcpm_seq_show(struct seq_file *s, void *v) +static int tcpm_debug_show(struct seq_file *s, void *v) { struct tcpm_port *port = (struct tcpm_port *)s->private; int tail; @@ -520,18 +522,7 @@ static int tcpm_seq_show(struct seq_file *s, void *v) return 0; } - -static int tcpm_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, tcpm_seq_show, inode->i_private); -} - -static const struct file_operations tcpm_debug_operations = { - .open = tcpm_debug_open, - .llseek = seq_lseek, - .read = seq_read, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(tcpm_debug); static struct dentry *rootdir; @@ -547,7 +538,7 @@ static int tcpm_debugfs_init(struct tcpm_port *port) port->dentry = debugfs_create_file(dev_name(port->dev), S_IFREG | 0444, rootdir, - port, &tcpm_debug_operations); + port, &tcpm_debug_fops); return 0; } @@ -615,18 +606,25 @@ void tcpm_pd_transmit_complete(struct tcpm_port *port, EXPORT_SYMBOL_GPL(tcpm_pd_transmit_complete); static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode, - enum tcpc_usb_switch config) + enum usb_role usb_role, + enum typec_orientation orientation) { - int ret = 0; + int ret; - tcpm_log(port, "Requesting mux mode %d, config %d, polarity %d", - mode, config, port->polarity); + tcpm_log(port, "Requesting mux mode %d, usb-role %d, orientation %d", + mode, usb_role, orientation); - if (port->tcpc->mux) - ret = port->tcpc->mux->set(port->tcpc->mux, mode, config, - port->polarity); + ret = typec_set_orientation(port->typec_port, orientation); + if (ret) + return ret; - return ret; + if (port->role_sw) { + ret = usb_role_switch_set_role(port->role_sw, usb_role); + if (ret) + return ret; + } + + return typec_set_mode(port->typec_port, mode); } static int tcpm_set_polarity(struct tcpm_port *port, @@ -739,14 +737,21 @@ static int tcpm_set_attached_state(struct tcpm_port *port, bool attached) static int tcpm_set_roles(struct tcpm_port *port, bool attached, enum typec_role role, enum typec_data_role data) { + enum typec_orientation orientation; + enum usb_role usb_role; int ret; + if (port->polarity == TYPEC_POLARITY_CC1) + orientation = TYPEC_ORIENTATION_NORMAL; + else + orientation = TYPEC_ORIENTATION_REVERSE; + if (data == TYPEC_HOST) - ret = tcpm_mux_set(port, TYPEC_MUX_USB, - TCPC_USB_SWITCH_CONNECT); + usb_role = USB_ROLE_HOST; else - ret = tcpm_mux_set(port, TYPEC_MUX_NONE, - TCPC_USB_SWITCH_DISCONNECT); + usb_role = USB_ROLE_DEVICE; + + ret = tcpm_mux_set(port, TYPEC_MUX_USB, usb_role, orientation); if (ret < 0) return ret; @@ -1041,7 +1046,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, break; case CMDT_RSP_ACK: /* silently drop message if we are not connected */ - if (!port->partner) + if (IS_ERR_OR_NULL(port->partner)) break; switch (cmd) { @@ -2039,7 +2044,8 @@ out_disable_vconn: out_disable_pd: port->tcpc->set_pd_rx(port->tcpc, false); out_disable_mux: - tcpm_mux_set(port, TYPEC_MUX_NONE, TCPC_USB_SWITCH_DISCONNECT); + tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE, + TYPEC_ORIENTATION_NONE); return ret; } @@ -2083,6 +2089,8 @@ static void tcpm_reset_port(struct tcpm_port *port) tcpm_init_vconn(port); tcpm_set_current_limit(port, 0, 0); tcpm_set_polarity(port, TYPEC_POLARITY_CC1); + tcpm_mux_set(port, TYPEC_MUX_NONE, USB_ROLE_NONE, + TYPEC_ORIENTATION_NONE); tcpm_set_attached_state(port, false); port->try_src_count = 0; port->try_snk_count = 0; @@ -2133,8 +2141,6 @@ static int tcpm_snk_attach(struct tcpm_port *port) static void tcpm_snk_detach(struct tcpm_port *port) { tcpm_detach(port); - - /* XXX: (Dis)connect SuperSpeed mux? */ } static int tcpm_acc_attach(struct tcpm_port *port) @@ -2190,7 +2196,7 @@ static inline enum tcpm_state unattached_state(struct tcpm_port *port) return SRC_UNATTACHED; else return SNK_UNATTACHED; - } else if (port->port_type == TYPEC_PORT_DFP) { + } else if (port->port_type == TYPEC_PORT_SRC) { return SRC_UNATTACHED; } @@ -3480,11 +3486,11 @@ static int tcpm_port_type_set(const struct typec_capability *cap, if (!port->connected) { tcpm_set_state(port, PORT_RESET, 0); - } else if (type == TYPEC_PORT_UFP) { + } else if (type == TYPEC_PORT_SNK) { if (!(port->pwr_role == TYPEC_SINK && port->data_role == TYPEC_DEVICE)) tcpm_set_state(port, PORT_RESET, 0); - } else if (type == TYPEC_PORT_DFP) { + } else if (type == TYPEC_PORT_SRC) { if (!(port->pwr_role == TYPEC_SOURCE && port->data_role == TYPEC_HOST)) tcpm_set_state(port, PORT_RESET, 0); @@ -3652,6 +3658,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->typec_caps.prefer_role = tcpc->config->default_role; port->typec_caps.type = tcpc->config->type; + port->typec_caps.data = tcpc->config->data; port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ port->typec_caps.pd_revision = 0x0200; /* USB-PD spec release 2.0 */ port->typec_caps.dr_set = tcpm_dr_set; @@ -3663,9 +3670,15 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->partner_desc.identity = &port->partner_ident; port->port_type = tcpc->config->type; + port->role_sw = usb_role_switch_get(port->dev); + if (IS_ERR(port->role_sw)) { + err = PTR_ERR(port->role_sw); + goto out_destroy_wq; + } + port->typec_port = typec_register_port(port->dev, &port->typec_caps); - if (!port->typec_port) { - err = -ENOMEM; + if (IS_ERR(port->typec_port)) { + err = PTR_ERR(port->typec_port); goto out_destroy_wq; } @@ -3674,15 +3687,17 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) i = 0; while (paltmode->svid && i < ARRAY_SIZE(port->port_altmode)) { - port->port_altmode[i] = - typec_port_register_altmode(port->typec_port, - paltmode); - if (!port->port_altmode[i]) { + struct typec_altmode *alt; + + alt = typec_port_register_altmode(port->typec_port, + paltmode); + if (IS_ERR(alt)) { tcpm_log(port, "%s: failed to register port alternate mode 0x%x", dev_name(dev), paltmode->svid); break; } + port->port_altmode[i] = alt; i++; paltmode++; } @@ -3696,6 +3711,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) return port; out_destroy_wq: + usb_role_switch_put(port->role_sw); destroy_workqueue(port->wq); return ERR_PTR(err); } diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index 2719f5d382f7..8b8406867c02 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -158,15 +158,15 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) desc.identity = &tps->partner_identity; } - tps->partner = typec_register_partner(tps->port, &desc); - if (!tps->partner) - return -ENODEV; - typec_set_pwr_opmode(tps->port, mode); typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status)); typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status)); + tps->partner = typec_register_partner(tps->port, &desc); + if (IS_ERR(tps->partner)) + return PTR_ERR(tps->partner); + if (desc.identity) typec_partner_set_identity(tps->partner); @@ -175,7 +175,8 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status) static void tps6598x_disconnect(struct tps6598x *tps, u32 status) { - typec_unregister_partner(tps->partner); + if (!IS_ERR(tps->partner)) + typec_unregister_partner(tps->partner); tps->partner = NULL; typec_set_pwr_opmode(tps->port, TYPEC_PWR_MODE_USB); typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status)); @@ -392,34 +393,42 @@ static int tps6598x_probe(struct i2c_client *client) if (ret < 0) return ret; + tps->typec_cap.revision = USB_TYPEC_REV_1_2; + tps->typec_cap.pd_revision = 0x200; + tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; + tps->typec_cap.pr_set = tps6598x_pr_set; + tps->typec_cap.dr_set = tps6598x_dr_set; + switch (TPS_SYSCONF_PORTINFO(conf)) { case TPS_PORTINFO_SINK_ACCESSORY: case TPS_PORTINFO_SINK: - tps->typec_cap.type = TYPEC_PORT_UFP; + tps->typec_cap.type = TYPEC_PORT_SNK; + tps->typec_cap.data = TYPEC_PORT_UFP; break; case TPS_PORTINFO_DRP_UFP_DRD: case TPS_PORTINFO_DRP_DFP_DRD: - tps->typec_cap.dr_set = tps6598x_dr_set; - /* fall through */ + tps->typec_cap.type = TYPEC_PORT_DRP; + tps->typec_cap.data = TYPEC_PORT_DRD; + break; case TPS_PORTINFO_DRP_UFP: + tps->typec_cap.type = TYPEC_PORT_DRP; + tps->typec_cap.data = TYPEC_PORT_UFP; + break; case TPS_PORTINFO_DRP_DFP: - tps->typec_cap.pr_set = tps6598x_pr_set; tps->typec_cap.type = TYPEC_PORT_DRP; + tps->typec_cap.data = TYPEC_PORT_DFP; break; case TPS_PORTINFO_SOURCE: - tps->typec_cap.type = TYPEC_PORT_DFP; + tps->typec_cap.type = TYPEC_PORT_SRC; + tps->typec_cap.data = TYPEC_PORT_DFP; break; default: return -ENODEV; } - tps->typec_cap.revision = USB_TYPEC_REV_1_2; - tps->typec_cap.pd_revision = 0x200; - tps->typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; - tps->port = typec_register_port(&client->dev, &tps->typec_cap); - if (!tps->port) - return -ENODEV; + if (IS_ERR(tps->port)) + return PTR_ERR(tps->port); if (status & TPS_STATUS_PLUG_PRESENT) { ret = tps6598x_connect(tps, status); diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/typec_wcove.c index 2e990e0d917d..19cca7f1b2c5 100644 --- a/drivers/usb/typec/typec_wcove.c +++ b/drivers/usb/typec/typec_wcove.c @@ -572,6 +572,7 @@ static struct tcpc_config wcove_typec_config = { .operating_snk_mw = 15000, .type = TYPEC_PORT_DRP, + .data = TYPEC_PORT_DRD, .default_role = TYPEC_SINK, }; diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 79046fe66426..bf0977fbd100 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -260,38 +260,45 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con) static int ucsi_register_partner(struct ucsi_connector *con) { - struct typec_partner_desc partner; + struct typec_partner_desc desc; + struct typec_partner *partner; if (con->partner) return 0; - memset(&partner, 0, sizeof(partner)); + memset(&desc, 0, sizeof(desc)); switch (con->status.partner_type) { case UCSI_CONSTAT_PARTNER_TYPE_DEBUG: - partner.accessory = TYPEC_ACCESSORY_DEBUG; + desc.accessory = TYPEC_ACCESSORY_DEBUG; break; case UCSI_CONSTAT_PARTNER_TYPE_AUDIO: - partner.accessory = TYPEC_ACCESSORY_AUDIO; + desc.accessory = TYPEC_ACCESSORY_AUDIO; break; default: break; } - partner.usb_pd = con->status.pwr_op_mode == UCSI_CONSTAT_PWR_OPMODE_PD; + desc.usb_pd = con->status.pwr_op_mode == UCSI_CONSTAT_PWR_OPMODE_PD; - con->partner = typec_register_partner(con->port, &partner); - if (!con->partner) { - dev_err(con->ucsi->dev, "con%d: failed to register partner\n", - con->num); - return -ENODEV; + partner = typec_register_partner(con->port, &desc); + if (IS_ERR(partner)) { + dev_err(con->ucsi->dev, + "con%d: failed to register partner (%ld)\n", con->num, + PTR_ERR(partner)); + return PTR_ERR(partner); } + con->partner = partner; + return 0; } static void ucsi_unregister_partner(struct ucsi_connector *con) { + if (!con->partner) + return; + typec_unregister_partner(con->partner); con->partner = NULL; } @@ -585,11 +592,18 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) return ret; if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DRP) - cap->type = TYPEC_PORT_DRP; + cap->data = TYPEC_PORT_DRD; else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DFP) - cap->type = TYPEC_PORT_DFP; + cap->data = TYPEC_PORT_DFP; else if (con->cap.op_mode & UCSI_CONCAP_OPMODE_UFP) - cap->type = TYPEC_PORT_UFP; + cap->data = TYPEC_PORT_UFP; + + if (con->cap.provider && con->cap.consumer) + cap->type = TYPEC_PORT_DRP; + else if (con->cap.provider) + cap->type = TYPEC_PORT_SRC; + else if (con->cap.consumer) + cap->type = TYPEC_PORT_SNK; cap->revision = ucsi->cap.typec_version; cap->pd_revision = ucsi->cap.pd_version; @@ -606,8 +620,8 @@ static int ucsi_register_port(struct ucsi *ucsi, int index) /* Register the connector */ con->port = typec_register_port(ucsi->dev, cap); - if (!con->port) - return -ENODEV; + if (IS_ERR(con->port)) + return PTR_ERR(con->port); /* Get the status */ UCSI_CMD_GET_CONNECTOR_STATUS(ctrl, con->num); diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 26ca0ec01fd5..c3ddd0f1f449 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -640,4 +640,4 @@ static struct usb_driver skel_driver = { module_usb_driver(skel_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig index eeefa29f8aa2..a20b65cb6678 100644 --- a/drivers/usb/usbip/Kconfig +++ b/drivers/usb/usbip/Kconfig @@ -27,7 +27,7 @@ config USBIP_VHCI_HCD config USBIP_VHCI_HC_PORTS int "Number of ports per USB/IP virtual host controller" - range 1 31 + range 1 15 default 8 depends on USBIP_VHCI_HCD ---help--- diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c index 4c00be2d1993..aff50eb09ca9 100644 --- a/drivers/usb/wusbcore/crypto.c +++ b/drivers/usb/wusbcore/crypto.c @@ -202,7 +202,7 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc, struct scatterlist sg[4], sg_dst; void *dst_buf; size_t dst_size; - u8 iv[crypto_skcipher_ivsize(tfm_cbc)]; + u8 *iv; size_t zero_padding; /* @@ -224,7 +224,9 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc, if (!dst_buf) goto error_dst_buf; - memset(iv, 0, sizeof(iv)); + iv = kzalloc(crypto_skcipher_ivsize(tfm_cbc), GFP_KERNEL); + if (!iv) + goto error_iv; /* Setup B0 */ scratch->b0.flags = 0x59; /* Format B0 */ @@ -276,6 +278,8 @@ static int wusb_ccm_mac(struct crypto_skcipher *tfm_cbc, bytewise_xor(mic, &scratch->ax, iv, 8); result = 8; error_cbc_crypt: + kfree(iv); +error_iv: kfree(dst_buf); error_dst_buf: return result; diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/usb/wusbcore/wa-nep.c index 9fdcb6b84abf..5f0656db5482 100644 --- a/drivers/usb/wusbcore/wa-nep.c +++ b/drivers/usb/wusbcore/wa-nep.c @@ -93,7 +93,6 @@ static void wa_notif_dispatch(struct work_struct *ws) goto out; /* screw it */ #endif atomic_dec(&wa->notifs_queued); /* Throttling ctl */ - dev = &wa->usb_iface->dev; size = nw->size; itr = nw->data; |