diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-01 20:40:49 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-01 20:40:49 +0300 |
commit | e4ee8b85b7657d9c769b727038faabdc2e6a3412 (patch) | |
tree | e6b52a1e866ed77b09b267f60f8f1b5449228325 /drivers/usb | |
parent | 7109a04eae81c41ed529da9f3c48c3655ccea741 (diff) | |
parent | d08dd3f3dd2ae351b793fc5b76abdbf0fd317b12 (diff) | |
download | linux-e4ee8b85b7657d9c769b727038faabdc2e6a3412.tar.xz |
Merge tag 'usb-4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB/PHY updates from Greg KH:
"Here is the big USB and PHY driver update for 4.16-rc1.
Along with the normally expected XHCI, MUSB, and Gadget driver
patches, there are some PHY driver fixes, license cleanups, sysfs
attribute cleanups, usbip changes, and a raft of other smaller fixes
and additions.
Full details are in the shortlog.
All of these have been in the linux-next tree for a long time with no
reported issues"
* tag 'usb-4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (137 commits)
USB: serial: pl2303: new device id for Chilitag
USB: misc: fix up some remaining DEVICE_ATTR() usages
USB: musb: fix up one odd DEVICE_ATTR() usage
USB: atm: fix up some remaining DEVICE_ATTR() usage
USB: move many drivers to use DEVICE_ATTR_WO
USB: move many drivers to use DEVICE_ATTR_RO
USB: move many drivers to use DEVICE_ATTR_RW
USB: misc: chaoskey: Use true and false for boolean values
USB: storage: remove old wording about how to submit a change
USB: storage: remove invalid URL from drivers
usb: ehci-omap: don't complain on -EPROBE_DEFER when no PHY found
usbip: list: don't list devices attached to vhci_hcd
usbip: prevent bind loops on devices attached to vhci_hcd
USB: serial: remove redundant initializations of 'mos_parport'
usb/gadget: Fix "high bandwidth" check in usb_gadget_ep_match_desc()
usb: gadget: compress return logic into one line
usbip: vhci_hcd: update 'status' file header and format
USB: serial: simple: add Motorola Tetra driver
CDC-ACM: apply quirk for card reader
usb: option: Add support for FS040U modem
...
Diffstat (limited to 'drivers/usb')
128 files changed, 3453 insertions, 1354 deletions
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 8af797252af2..e57a2be8754a 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -196,18 +196,16 @@ static void cxacru_poll_status(struct work_struct *work); /* Card info exported through sysfs */ #define CXACRU__ATTR_INIT(_name) \ -static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL) +static DEVICE_ATTR_RO(_name) #define CXACRU_CMD_INIT(_name) \ -static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \ - cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name) +static DEVICE_ATTR_RW(_name) #define CXACRU_SET_INIT(_name) \ -static DEVICE_ATTR(_name, S_IWUSR, \ - NULL, cxacru_sysfs_store_##_name) +static DEVICE_ATTR_WO(_name) #define CXACRU_ATTR_INIT(_value, _type, _name) \ -static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \ +static ssize_t _name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct cxacru_data *instance = to_usbatm_driver_data(\ @@ -302,7 +300,7 @@ static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf) * MAC_ADDRESS_LOW = 0x33221100 * Where 00-55 are bytes 0-5 of the MAC. */ -static ssize_t cxacru_sysfs_show_mac_address(struct device *dev, +static ssize_t mac_address_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cxacru_data *instance = to_usbatm_driver_data( @@ -315,7 +313,7 @@ static ssize_t cxacru_sysfs_show_mac_address(struct device *dev, instance->usbatm->atm_dev->esi); } -static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev, +static ssize_t adsl_state_show(struct device *dev, struct device_attribute *attr, char *buf) { static char *str[] = { "running", "stopped" }; @@ -332,7 +330,7 @@ static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev, return snprintf(buf, PAGE_SIZE, "%s\n", str[value]); } -static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev, +static ssize_t adsl_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cxacru_data *instance = to_usbatm_driver_data( @@ -435,7 +433,7 @@ static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev, /* CM_REQUEST_CARD_DATA_GET times out, so no show attribute */ -static ssize_t cxacru_sysfs_store_adsl_config(struct device *dev, +static ssize_t adsl_config_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cxacru_data *instance = to_usbatm_driver_data( diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index ab75690044bb..2754b4ce7136 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -2280,7 +2280,7 @@ static struct uea_softc *dev_to_uea(struct device *dev) return usbatm->driver_data; } -static ssize_t read_status(struct device *dev, struct device_attribute *attr, +static ssize_t stat_status_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = -ENODEV; @@ -2296,7 +2296,7 @@ out: return ret; } -static ssize_t reboot(struct device *dev, struct device_attribute *attr, +static ssize_t stat_status_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret = -ENODEV; @@ -2313,9 +2313,9 @@ out: return ret; } -static DEVICE_ATTR(stat_status, S_IWUSR | S_IRUGO, read_status, reboot); +static DEVICE_ATTR_RW(stat_status); -static ssize_t read_human_status(struct device *dev, +static ssize_t stat_human_status_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = -ENODEV; @@ -2376,9 +2376,9 @@ out: return ret; } -static DEVICE_ATTR(stat_human_status, S_IRUGO, read_human_status, NULL); +static DEVICE_ATTR_RO(stat_human_status); -static ssize_t read_delin(struct device *dev, struct device_attribute *attr, +static ssize_t stat_delin_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = -ENODEV; @@ -2408,11 +2408,11 @@ out: return ret; } -static DEVICE_ATTR(stat_delin, S_IRUGO, read_delin, NULL); +static DEVICE_ATTR_RO(stat_delin); #define UEA_ATTR(name, reset) \ \ -static ssize_t read_##name(struct device *dev, \ +static ssize_t stat_##name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ int ret = -ENODEV; \ @@ -2430,7 +2430,7 @@ out: \ return ret; \ } \ \ -static DEVICE_ATTR(stat_##name, S_IRUGO, read_##name, NULL) +static DEVICE_ATTR_RO(stat_##name) UEA_ATTR(mflags, 1); UEA_ATTR(vidcpe, 0); diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index 7b65a1040d2c..7f4d2b6af37a 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -29,7 +29,7 @@ static const struct tegra_udc_soc_info tegra20_udc_soc_info = { }; static const struct tegra_udc_soc_info tegra30_udc_soc_info = { - .flags = 0, + .flags = CI_HDRC_REQUIRES_ALIGNED_DMA, }; static const struct tegra_udc_soc_info tegra114_udc_soc_info = { diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index dd2dd9391bb7..33ae87fa3ff3 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -835,7 +835,7 @@ static void ci_get_otg_capable(struct ci_hdrc *ci) } } -static ssize_t ci_role_show(struct device *dev, struct device_attribute *attr, +static ssize_t role_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ci_hdrc *ci = dev_get_drvdata(dev); @@ -846,7 +846,7 @@ static ssize_t ci_role_show(struct device *dev, struct device_attribute *attr, return 0; } -static ssize_t ci_role_store(struct device *dev, +static ssize_t role_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct ci_hdrc *ci = dev_get_drvdata(dev); @@ -877,7 +877,7 @@ static ssize_t ci_role_store(struct device *dev, return (ret == 0) ? n : ret; } -static DEVICE_ATTR(role, 0644, ci_role_show, ci_role_store); +static DEVICE_ATTR_RW(role); static struct attribute *ci_attrs[] = { &dev_attr_role.attr, diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 9e2d300060bc..6ed4b00dba96 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -29,7 +29,7 @@ /* Add for otg: interact with user space app */ static ssize_t -get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +a_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf) { char *next; unsigned size, t; @@ -45,7 +45,7 @@ get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -set_a_bus_req(struct device *dev, struct device_attribute *attr, +a_bus_req_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ci_hdrc *ci = dev_get_drvdata(dev); @@ -75,10 +75,10 @@ set_a_bus_req(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, set_a_bus_req); +static DEVICE_ATTR_RW(a_bus_req); static ssize_t -get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) +a_bus_drop_show(struct device *dev, struct device_attribute *attr, char *buf) { char *next; unsigned size, t; @@ -94,7 +94,7 @@ get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -set_a_bus_drop(struct device *dev, struct device_attribute *attr, +a_bus_drop_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ci_hdrc *ci = dev_get_drvdata(dev); @@ -115,11 +115,10 @@ set_a_bus_drop(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, get_a_bus_drop, - set_a_bus_drop); +static DEVICE_ATTR_RW(a_bus_drop); static ssize_t -get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +b_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf) { char *next; unsigned size, t; @@ -135,7 +134,7 @@ get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -set_b_bus_req(struct device *dev, struct device_attribute *attr, +b_bus_req_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ci_hdrc *ci = dev_get_drvdata(dev); @@ -160,10 +159,10 @@ set_b_bus_req(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUSR, get_b_bus_req, set_b_bus_req); +static DEVICE_ATTR_RW(b_bus_req); static ssize_t -set_a_clr_err(struct device *dev, struct device_attribute *attr, +a_clr_err_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ci_hdrc *ci = dev_get_drvdata(dev); @@ -180,7 +179,7 @@ set_a_clr_err(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); +static DEVICE_ATTR_WO(a_clr_err); static struct attribute *inputs_attrs[] = { &dev_attr_a_bus_req.attr, diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 8e0636c963a7..06b3b54a0e68 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -235,7 +235,7 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb) /* * attributes exported through sysfs */ -static ssize_t show_caps +static ssize_t bmCapabilities_show (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -243,9 +243,9 @@ static ssize_t show_caps return sprintf(buf, "%d", acm->ctrl_caps); } -static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); +static DEVICE_ATTR_RO(bmCapabilities); -static ssize_t show_country_codes +static ssize_t wCountryCodes_show (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -255,9 +255,9 @@ static ssize_t show_country_codes return acm->country_code_size; } -static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); +static DEVICE_ATTR_RO(wCountryCodes); -static ssize_t show_country_rel_date +static ssize_t iCountryCodeRelDate_show (struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -266,7 +266,7 @@ static ssize_t show_country_rel_date return sprintf(buf, "%d", acm->country_rel_date); } -static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); +static DEVICE_ATTR_RO(iCountryCodeRelDate); /* * Interrupt handlers for various ACM device responses */ @@ -425,7 +425,7 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags) res = usb_submit_urb(acm->read_urbs[index], mem_flags); if (res) { - if (res != -EPERM) { + if (res != -EPERM && res != -ENODEV) { dev_err(&acm->data->dev, "urb %d failed submission with %d\n", index, res); @@ -1752,6 +1752,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ .driver_info = SINGLE_RX_URB, /* firmware bug */ }, + { USB_DEVICE(0x11ca, 0x0201), /* VeriFone Mx870 Gadget Serial */ + .driver_info = SINGLE_RX_URB, + }, { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index f45e8877771a..425247b7f728 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -1066,7 +1066,7 @@ static struct usb_class_driver usblp_class = { .minor_base = USBLP_MINOR_BASE, }; -static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t ieee1284_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct usblp *usblp = usb_get_intfdata(intf); @@ -1078,7 +1078,7 @@ static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribut return sprintf(buf, "%s", usblp->device_id_string+2); } -static DEVICE_ATTR(ieee1284_id, S_IRUGO, usblp_show_ieee1284_id, NULL); +static DEVICE_ATTR_RO(ieee1284_id); static int usblp_probe(struct usb_interface *intf, const struct usb_device_id *id) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 5e72bf36aca4..bf00166cbee0 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1681,8 +1681,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb u |= URB_ISO_ASAP; if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in) u |= URB_SHORT_NOT_OK; - if (uurb->flags & USBDEVFS_URB_NO_FSBR) - u |= URB_NO_FSBR; if (uurb->flags & USBDEVFS_URB_ZERO_PACKET) u |= URB_ZERO_PACKET; if (uurb->flags & USBDEVFS_URB_NO_INTERRUPT) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 64262a9a8829..9792cedfc351 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -342,8 +342,8 @@ static int usb_probe_interface(struct device *dev) if (driver->disable_hub_initiated_lpm) { lpm_disable_error = usb_unlocked_disable_lpm(udev); if (lpm_disable_error) { - dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.", - __func__, driver->name); + dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n", + __func__, driver->name); error = lpm_disable_error; goto err; } @@ -537,8 +537,8 @@ int usb_driver_claim_interface(struct usb_driver *driver, if (driver->disable_hub_initiated_lpm) { lpm_disable_error = usb_unlocked_disable_lpm(udev); if (lpm_disable_error) { - dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.", - __func__, driver->name); + dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n", + __func__, driver->name); return -ENOMEM; } } @@ -1070,7 +1070,7 @@ static void usb_rebind_intf(struct usb_interface *intf) if (!intf->dev.power.is_prepared) { intf->needs_binding = 0; rc = device_attach(&intf->dev); - if (rc < 0) + if (rc < 0 && rc != -EPROBE_DEFER) dev_warn(&intf->dev, "rebind failed: %d\n", rc); } } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index cf7bbcb9a63c..c5c1f6cf3228 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -38,6 +38,9 @@ #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 +#define USB_TP_TRANSMISSION_DELAY 40 /* ns */ +#define USB_TP_TRANSMISSION_DELAY_MAX 65535 /* ns */ + /* Protect struct usb_device->state and ->children members * Note: Both are also protected by ->dev.sem, except that ->state can * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ @@ -1049,12 +1052,10 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) ret = hcd->driver->update_hub_device(hcd, hdev, &hub->tt, GFP_NOIO); if (ret < 0) { - dev_err(hub->intfdev, "Host not " - "accepting hub info " - "update.\n"); - dev_err(hub->intfdev, "LS/FS devices " - "and hubs may not work " - "under this hub\n."); + dev_err(hub->intfdev, + "Host not accepting hub info update\n"); + dev_err(hub->intfdev, + "LS/FS devices and hubs may not work under this hub\n"); } } hub_power_on(hub, true); @@ -1352,6 +1353,20 @@ static int hub_configure(struct usb_hub *hub, goto fail; } + /* + * Accumulate wHubDelay + 40ns for every hub in the tree of devices. + * The resulting value will be used for SetIsochDelay() request. + */ + if (hub_is_superspeed(hdev) || hub_is_superspeedplus(hdev)) { + u32 delay = __le16_to_cpu(hub->descriptor->u.ss.wHubDelay); + + if (hdev->parent) + delay += hdev->parent->hub_delay; + + delay += USB_TP_TRANSMISSION_DELAY; + hdev->hub_delay = min_t(u32, delay, USB_TP_TRANSMISSION_DELAY_MAX); + } + maxchild = hub->descriptor->bNbrPorts; dev_info(hub_dev, "%d port%s detected\n", maxchild, (maxchild == 1) ? "" : "s"); @@ -3157,7 +3172,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) usb_set_usb2_hardware_lpm(udev, 0); if (usb_disable_ltm(udev)) { - dev_err(&udev->dev, "Failed to disable LTM before suspend\n."); + dev_err(&udev->dev, "Failed to disable LTM before suspend\n"); status = -ENOMEM; if (PMSG_IS_AUTO(msg)) goto err_ltm; @@ -4599,7 +4614,20 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, if (retval >= 0) retval = -EMSGSIZE; } else { + u32 delay; + retval = 0; + + delay = udev->parent->hub_delay; + udev->hub_delay = min_t(u32, delay, + USB_TP_TRANSMISSION_DELAY_MAX); + retval = usb_set_isoch_delay(udev); + if (retval) { + dev_dbg(&udev->dev, + "Failed set isoch delay, error %d\n", + retval); + retval = 0; + } break; } } @@ -5484,13 +5512,12 @@ static int usb_reset_and_verify_device(struct usb_device *udev) */ ret = usb_unlocked_disable_lpm(udev); if (ret) { - dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); + 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__); + dev_err(&udev->dev, "%s Failed to disable LTM\n", __func__); goto re_enumerate_no_bos; } diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c index 9dbb429cd471..d775ffea20c3 100644 --- a/drivers/usb/core/ledtrig-usbport.c +++ b/drivers/usb/core/ledtrig-usbport.c @@ -137,11 +137,17 @@ static bool usbport_trig_port_observed(struct usbport_trig_data *usbport_data, if (!led_np) return false; - /* Get node of port being added */ - port_np = usb_of_get_child_node(usb_dev->dev.of_node, port1); + /* + * Get node of port being added + * + * FIXME: This is really the device node of the connected device + */ + port_np = usb_of_get_device_node(usb_dev, port1); if (!port_np) return false; + of_node_put(port_np); + /* Amount of trigger sources for this LED */ count = of_count_phandle_with_args(led_np, "trigger-sources", "#trigger-source-cells"); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 77001bcfc504..c64cf6c4a83d 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -18,6 +18,7 @@ #include <linux/usb/cdc.h> #include <linux/usb/quirks.h> #include <linux/usb/hcd.h> /* for usbcore internals */ +#include <linux/usb/of.h> #include <asm/byteorder.h> #include "usb.h" @@ -776,7 +777,7 @@ static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) * deal with strings at all. Set string_langid to -1 in order to * prevent any string to be retrieved from the device */ if (err < 0) { - dev_err(&dev->dev, "string descriptor 0 read error: %d\n", + dev_info(&dev->dev, "string descriptor 0 read error: %d\n", err); dev->string_langid = -1; return -EPIPE; @@ -915,6 +916,30 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) return ret; } +/* + * usb_set_isoch_delay - informs the device of the packet transmit delay + * @dev: the device whose delay is to be informed + * Context: !in_interrupt() + * + * Since this is an optional request, we don't bother if it fails. + */ +int usb_set_isoch_delay(struct usb_device *dev) +{ + /* skip hub devices */ + if (dev->descriptor.bDeviceClass == USB_CLASS_HUB) + return 0; + + /* skip non-SS/non-SSP devices */ + if (dev->speed < USB_SPEED_SUPER) + return 0; + + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_ISOCH_DELAY, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + cpu_to_le16(dev->hub_delay), 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} + /** * usb_get_status - issues a GET_STATUS call * @dev: the device whose status is being checked @@ -1355,7 +1380,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * so that the xHCI driver can recalculate the U1/U2 timeouts. */ if (usb_disable_lpm(dev)) { - dev_err(&iface->dev, "%s Failed to disable LPM\n.", __func__); + dev_err(&iface->dev, "%s Failed to disable LPM\n", __func__); mutex_unlock(hcd->bandwidth_mutex); return -ENOMEM; } @@ -1499,7 +1524,7 @@ int usb_reset_configuration(struct usb_device *dev) * that the xHCI driver can recalculate the U1/U2 timeouts. */ if (usb_disable_lpm(dev)) { - dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__); + dev_err(&dev->dev, "%s Failed to disable LPM\n", __func__); mutex_unlock(hcd->bandwidth_mutex); return -ENOMEM; } @@ -1583,6 +1608,7 @@ static void usb_release_interface(struct device *dev) kref_put(&intfc->ref, usb_release_interface_cache); usb_put_dev(interface_to_usbdev(intf)); + of_node_put(dev->of_node); kfree(intf); } @@ -1846,7 +1872,7 @@ free_interfaces: * timeouts. */ if (dev->actconfig && usb_disable_lpm(dev)) { - dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__); + dev_err(&dev->dev, "%s Failed to disable LPM\n", __func__); mutex_unlock(hcd->bandwidth_mutex); ret = -ENOMEM; goto free_interfaces; @@ -1868,6 +1894,7 @@ free_interfaces: struct usb_interface_cache *intfc; struct usb_interface *intf; struct usb_host_interface *alt; + u8 ifnum; cp->interface[i] = intf = new_interfaces[i]; intfc = cp->intf_cache[i]; @@ -1886,11 +1913,17 @@ free_interfaces: if (!alt) alt = &intf->altsetting[0]; - intf->intf_assoc = - find_iad(dev, cp, alt->desc.bInterfaceNumber); + ifnum = alt->desc.bInterfaceNumber; + intf->intf_assoc = find_iad(dev, cp, ifnum); intf->cur_altsetting = alt; usb_enable_interface(dev, intf, true); intf->dev.parent = &dev->dev; + if (usb_of_has_combined_node(dev)) { + device_set_of_node_from_dev(&intf->dev, &dev->dev); + } else { + intf->dev.of_node = usb_of_get_interface_node(dev, + configuration, ifnum); + } intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; intf->dev.type = &usb_if_device_type; @@ -1905,9 +1938,8 @@ free_interfaces: intf->minor = -1; device_initialize(&intf->dev); pm_runtime_no_callbacks(&intf->dev); - dev_set_name(&intf->dev, "%d-%s:%d.%d", - dev->bus->busnum, dev->devpath, - configuration, alt->desc.bInterfaceNumber); + dev_set_name(&intf->dev, "%d-%s:%d.%d", dev->bus->busnum, + dev->devpath, configuration, ifnum); usb_get_dev(dev); } kfree(new_interfaces); diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index 2be968353257..fd77442c2d12 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -3,7 +3,8 @@ * of.c The helpers for hcd device tree support * * Copyright (C) 2016 Freescale Semiconductor, Inc. - * Author: Peter Chen <peter.chen@freescale.com> + * Author: Peter Chen <peter.chen@freescale.com> + * Copyright (C) 2017 Johan Hovold <johan@kernel.org> */ #include <linux/of.h> @@ -11,31 +12,99 @@ #include <linux/usb/of.h> /** - * usb_of_get_child_node - Find the device node match port number - * @parent: the parent device node - * @portnum: the port number which device is connecting + * usb_of_get_device_node() - get a USB device node + * @hub: hub to which device is connected + * @port1: one-based index of port * - * Find the node from device tree according to its port number. + * Look up the node of a USB device given its parent hub device and one-based + * port number. * * Return: A pointer to the node with incremented refcount if found, or * %NULL otherwise. */ -struct device_node *usb_of_get_child_node(struct device_node *parent, - int portnum) +struct device_node *usb_of_get_device_node(struct usb_device *hub, int port1) { struct device_node *node; - u32 port; + u32 reg; - for_each_child_of_node(parent, node) { - if (!of_property_read_u32(node, "reg", &port)) { - if (port == portnum) - return node; + for_each_child_of_node(hub->dev.of_node, node) { + if (of_property_read_u32(node, "reg", ®)) + continue; + + if (reg == port1) + return node; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(usb_of_get_device_node); + +/** + * usb_of_has_combined_node() - determine whether a device has a combined node + * @udev: USB device + * + * Determine whether a USB device has a so called combined node which is + * shared with its sole interface. This is the case if and only if the device + * has a node and its decriptors report the following: + * + * 1) bDeviceClass is 0 or 9, and + * 2) bNumConfigurations is 1, and + * 3) bNumInterfaces is 1. + * + * Return: True iff the device has a device node and its descriptors match the + * criteria for a combined node. + */ +bool usb_of_has_combined_node(struct usb_device *udev) +{ + struct usb_device_descriptor *ddesc = &udev->descriptor; + struct usb_config_descriptor *cdesc; + + if (!udev->dev.of_node) + return false; + + switch (ddesc->bDeviceClass) { + case USB_CLASS_PER_INTERFACE: + case USB_CLASS_HUB: + if (ddesc->bNumConfigurations == 1) { + cdesc = &udev->config->desc; + if (cdesc->bNumInterfaces == 1) + return true; } } + return false; +} +EXPORT_SYMBOL_GPL(usb_of_has_combined_node); + +/** + * usb_of_get_interface_node() - get a USB interface node + * @udev: USB device of interface + * @config: configuration value + * @ifnum: interface number + * + * Look up the node of a USB interface given its USB device, configuration + * value and interface number. + * + * Return: A pointer to the node with incremented refcount if found, or + * %NULL otherwise. + */ +struct device_node * +usb_of_get_interface_node(struct usb_device *udev, u8 config, u8 ifnum) +{ + struct device_node *node; + u32 reg[2]; + + for_each_child_of_node(udev->dev.of_node, node) { + if (of_property_read_u32_array(node, "reg", reg, 2)) + continue; + + if (reg[0] == ifnum && reg[1] == config) + return node; + } + return NULL; } -EXPORT_SYMBOL_GPL(usb_of_get_child_node); +EXPORT_SYMBOL_GPL(usb_of_get_interface_node); /** * usb_of_get_companion_dev - Find the companion device diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 9fdf137c4865..796c9b149728 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -479,9 +479,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) if (is_out) allowed |= URB_ZERO_PACKET; /* FALLTHROUGH */ - case USB_ENDPOINT_XFER_CONTROL: - allowed |= URB_NO_FSBR; /* only affects UHCI */ - /* FALLTHROUGH */ default: /* all non-iso endpoints */ if (!is_out) allowed |= URB_SHORT_NOT_OK; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 845286f08ab0..2f5fbc56a9dd 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -645,8 +645,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, raw_port = usb_hcd_find_raw_port_number(usb_hcd, port1); } - dev->dev.of_node = usb_of_get_child_node(parent->dev.of_node, - raw_port); + dev->dev.of_node = usb_of_get_device_node(parent, raw_port); /* hub driver sets up TT records */ } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 2bee08d084ae..149cc7480971 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -40,6 +40,7 @@ extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); +extern int usb_set_isoch_delay(struct usb_device *dev); extern int usb_get_bos_descriptor(struct usb_device *dev); extern void usb_release_bos_descriptor(struct usb_device *dev); extern char *usb_cache_string(struct usb_device *udev, int index); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 31749c79045f..cd77af3b1565 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -929,6 +929,7 @@ struct dwc2_hsotg { int irq; struct clk *clk; struct reset_control *reset; + struct reset_control *reset_ecc; unsigned int queuing_high_bandwidth:1; unsigned int srp_success:1; @@ -971,6 +972,7 @@ struct dwc2_hsotg { } flags; struct list_head non_periodic_sched_inactive; + struct list_head non_periodic_sched_waiting; struct list_head non_periodic_sched_active; struct list_head *non_periodic_qh_ptr; struct list_head periodic_sched_inactive; diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 7b6eb0ad513b..a5d72fcd1603 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -659,6 +659,10 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg, list_for_each_entry(qh, &hsotg->non_periodic_sched_inactive, qh_list_entry) dev_dbg(hsotg->dev, " %p\n", qh); + dev_dbg(hsotg->dev, " NP waiting sched:\n"); + list_for_each_entry(qh, &hsotg->non_periodic_sched_waiting, + qh_list_entry) + dev_dbg(hsotg->dev, " %p\n", qh); dev_dbg(hsotg->dev, " NP active sched:\n"); list_for_each_entry(qh, &hsotg->non_periodic_sched_active, qh_list_entry) @@ -1818,6 +1822,7 @@ static void dwc2_qh_list_free(struct dwc2_hsotg *hsotg, static void dwc2_kill_all_urbs(struct dwc2_hsotg *hsotg) { dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_inactive); + dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_waiting); dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->non_periodic_sched_active); dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_inactive); dwc2_kill_urbs_in_qh_list(hsotg, &hsotg->periodic_sched_ready); @@ -4998,6 +5003,7 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) /* Free memory for QH/QTD lists */ dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_inactive); + dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_waiting); dwc2_qh_list_free(hsotg, &hsotg->non_periodic_sched_active); dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_inactive); dwc2_qh_list_free(hsotg, &hsotg->periodic_sched_ready); @@ -5159,6 +5165,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) /* Initialize the non-periodic schedule */ INIT_LIST_HEAD(&hsotg->non_periodic_sched_inactive); + INIT_LIST_HEAD(&hsotg->non_periodic_sched_waiting); INIT_LIST_HEAD(&hsotg->non_periodic_sched_active); /* Initialize the periodic schedule */ diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 78e9e01051b5..ad60e46e66e1 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -314,12 +314,16 @@ struct dwc2_hs_transfer_time { * descriptor and indicates original XferSize value for the * descriptor * @unreserve_timer: Timer for releasing periodic reservation. + * @wait_timer: Timer used to wait before re-queuing. * @dwc2_tt: Pointer to our tt info (or NULL if no tt). * @ttport: Port number within our tt. * @tt_buffer_dirty True if clear_tt_buffer_complete is pending * @unreserve_pending: True if we planned to unreserve but haven't yet. * @schedule_low_speed: True if we have a low/full speed component (either the * host is in low/full speed mode or do_split). + * @want_wait: We should wait before re-queuing; only matters for non- + * periodic transfers and is ignored for periodic ones. + * @wait_timer_cancel: Set to true to cancel the wait_timer. * * A Queue Head (QH) holds the static characteristics of an endpoint and * maintains a list of transfers (QTDs) for that endpoint. A QH structure may @@ -354,11 +358,14 @@ struct dwc2_qh { u32 desc_list_sz; u32 *n_bytes; struct timer_list unreserve_timer; + struct timer_list wait_timer; struct dwc2_tt *dwc_tt; int ttport; unsigned tt_buffer_dirty:1; unsigned unreserve_pending:1; unsigned schedule_low_speed:1; + unsigned want_wait:1; + unsigned wait_timer_cancel:1; }; /** @@ -389,6 +396,7 @@ struct dwc2_qh { * @n_desc: Number of DMA descriptors for this QTD * @isoc_frame_index_last: Last activated frame (packet) index, used in * descriptor DMA mode only + * @num_naks: Number of NAKs received on this QTD. * @urb: URB for this transfer * @qh: Queue head for this QTD * @qtd_list_entry: For linking to the QH's list of QTDs @@ -419,6 +427,7 @@ struct dwc2_qtd { u8 error_count; u8 n_desc; u16 isoc_frame_index_last; + u16 num_naks; struct dwc2_hcd_urb *urb; struct dwc2_qh *qh; struct list_head qtd_list_entry; diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 916d991b96b8..a5dfd9d8bd9a 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -53,6 +53,12 @@ #include "core.h" #include "hcd.h" +/* + * If we get this many NAKs on a split transaction we'll slow down + * retransmission. A 1 here means delay after the first NAK. + */ +#define DWC2_NAKS_BEFORE_DELAY 3 + /* This function is for debug only */ static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg) { @@ -1201,11 +1207,25 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg, /* * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and * interrupt. Re-start the SSPLIT transfer. + * + * Normally for non-periodic transfers we'll retry right away, but to + * avoid interrupt storms we'll wait before retrying if we've got + * several NAKs. If we didn't do this we'd retry directly from the + * interrupt handler and could end up quickly getting another + * interrupt (another NAK), which we'd retry. + * + * Note that in DMA mode software only gets involved to re-send NAKed + * transfers for split transactions, so we only need to apply this + * delaying logic when handling splits. In non-DMA mode presumably we + * might want a similar delay if someone can demonstrate this problem + * affects that code path too. */ if (chan->do_split) { if (chan->complete_split) qtd->error_count = 0; qtd->complete_split = 0; + qtd->num_naks++; + qtd->qh->want_wait = qtd->num_naks >= DWC2_NAKS_BEFORE_DELAY; dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK); goto handle_nak_done; } diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index fcd1676c7f0b..e34ad5e65350 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -58,6 +58,9 @@ /* Wait this long before releasing periodic reservation */ #define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5)) +/* If we get a NAK, wait this long before retrying */ +#define DWC2_RETRY_WAIT_DELAY (msecs_to_jiffies(1)) + /** * dwc2_periodic_channel_available() - Checks that a channel is available for a * periodic transfer @@ -1441,6 +1444,55 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg, } /** + * dwc2_wait_timer_fn() - Timer function to re-queue after waiting + * + * As per the spec, a NAK indicates that "a function is temporarily unable to + * transmit or receive data, but will eventually be able to do so without need + * of host intervention". + * + * That means that when we encounter a NAK we're supposed to retry. + * + * ...but if we retry right away (from the interrupt handler that saw the NAK) + * then we can end up with an interrupt storm (if the other side keeps NAKing + * us) because on slow enough CPUs it could take us longer to get out of the + * interrupt routine than it takes for the device to send another NAK. That + * leads to a constant stream of NAK interrupts and the CPU locks. + * + * ...so instead of retrying right away in the case of a NAK we'll set a timer + * to retry some time later. This function handles that timer and moves the + * qh back to the "inactive" list, then queues transactions. + * + * @t: Pointer to wait_timer in a qh. + */ +static void dwc2_wait_timer_fn(struct timer_list *t) +{ + struct dwc2_qh *qh = from_timer(qh, t, wait_timer); + struct dwc2_hsotg *hsotg = qh->hsotg; + unsigned long flags; + + spin_lock_irqsave(&hsotg->lock, flags); + + /* + * We'll set wait_timer_cancel to true if we want to cancel this + * operation in dwc2_hcd_qh_unlink(). + */ + if (!qh->wait_timer_cancel) { + enum dwc2_transaction_type tr_type; + + qh->want_wait = false; + + list_move(&qh->qh_list_entry, + &hsotg->non_periodic_sched_inactive); + + tr_type = dwc2_hcd_select_transactions(hsotg); + if (tr_type != DWC2_TRANSACTION_NONE) + dwc2_hcd_queue_transactions(hsotg, tr_type); + } + + spin_unlock_irqrestore(&hsotg->lock, flags); +} + +/** * dwc2_qh_init() - Initializes a QH structure * * @hsotg: The HCD state structure for the DWC OTG controller @@ -1468,6 +1520,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, /* Initialize QH */ qh->hsotg = hsotg; timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0); + timer_setup(&qh->wait_timer, dwc2_wait_timer_fn, 0); qh->ep_type = ep_type; qh->ep_is_in = ep_is_in; @@ -1628,6 +1681,16 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) dwc2_do_unreserve(hsotg, qh); spin_unlock_irqrestore(&hsotg->lock, flags); } + + /* + * We don't have the lock so we can safely wait until the wait timer + * finishes. Of course, at this point in time we'd better have set + * wait_timer_active to false so if this timer was still pending it + * won't do anything anyway, but we want it to finish before we free + * memory. + */ + del_timer_sync(&qh->wait_timer); + dwc2_host_put_tt_info(hsotg, qh->dwc_tt); if (qh->desc_list) @@ -1663,9 +1726,16 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) qh->start_active_frame = hsotg->frame_number; qh->next_active_frame = qh->start_active_frame; - /* Always start in inactive schedule */ - list_add_tail(&qh->qh_list_entry, - &hsotg->non_periodic_sched_inactive); + if (qh->want_wait) { + list_add_tail(&qh->qh_list_entry, + &hsotg->non_periodic_sched_waiting); + qh->wait_timer_cancel = false; + mod_timer(&qh->wait_timer, + jiffies + DWC2_RETRY_WAIT_DELAY + 1); + } else { + list_add_tail(&qh->qh_list_entry, + &hsotg->non_periodic_sched_inactive); + } return 0; } @@ -1695,6 +1765,9 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) dev_vdbg(hsotg->dev, "%s()\n", __func__); + /* If the wait_timer is pending, this will stop it from acting */ + qh->wait_timer_cancel = true; + if (list_empty(&qh->qh_list_entry)) /* QH is not in a schedule */ return; @@ -1903,7 +1976,7 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, if (dwc2_qh_is_non_per(qh)) { dwc2_hcd_qh_unlink(hsotg, qh); if (!list_empty(&qh->qtd_list)) - /* Add back to inactive non-periodic schedule */ + /* Add back to inactive/waiting non-periodic schedule */ dwc2_hcd_qh_add(hsotg, qh); return; } diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 3e26550d13dd..4703478f702f 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -221,6 +221,15 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) reset_control_deassert(hsotg->reset); + hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc"); + if (IS_ERR(hsotg->reset_ecc)) { + ret = PTR_ERR(hsotg->reset_ecc); + dev_err(hsotg->dev, "error getting reset control for ecc %d\n", ret); + return ret; + } + + reset_control_deassert(hsotg->reset_ecc); + /* Set default UTMI width */ hsotg->phyif = GUSBCFG_PHYIF16; @@ -319,6 +328,7 @@ static int dwc2_driver_remove(struct platform_device *dev) dwc2_lowlevel_hw_disable(hsotg); reset_control_assert(hsotg->reset); + reset_control_assert(hsotg->reset_ecc); return 0; } diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 07832509584f..ade2ab00d37a 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -147,6 +147,7 @@ static void __dwc3_set_mode(struct work_struct *work) otg_set_vbus(dwc->usb2_phy->otg, true); phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); + phy_calibrate(dwc->usb2_generic_phy); } break; case DWC3_GCTL_PRTCAP_DEVICE: @@ -945,6 +946,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dev_err(dev, "failed to initialize host\n"); return ret; } + phy_calibrate(dwc->usb2_generic_phy); break; case USB_DR_MODE_OTG: INIT_WORK(&dwc->drd_work, __dwc3_set_mode); @@ -1062,6 +1064,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj); + dwc->dis_metastability_quirk = device_property_read_bool(dev, + "snps,dis_metastability_quirk"); + dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 4a4a4c98508c..03c7aaaac926 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -796,7 +796,6 @@ struct dwc3_scratchpad_array { * @usb2_generic_phy: pointer to USB2 PHY * @usb3_generic_phy: pointer to USB3 PHY * @ulpi: pointer to ulpi interface - * @isoch_delay: wValue from Set Isochronous Delay request; * @u2sel: parameter from Set SEL request. * @u2pel: parameter from Set SEL request. * @u1sel: parameter from Set SEL request. @@ -857,6 +856,7 @@ struct dwc3_scratchpad_array { * 1 - -3.5dB de-emphasis * 2 - No de-emphasis * 3 - Reserved + * @dis_metastability_quirk: set to disable metastability quirk. * @imod_interval: set the interrupt moderation interval in 250ns * increments or 0 to disable. */ @@ -955,7 +955,6 @@ struct dwc3 { enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; - u16 isoch_delay; u16 u2sel; u16 u2pel; u8 u1sel; @@ -1010,6 +1009,8 @@ struct dwc3 { unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; + unsigned dis_metastability_quirk:1; + u16 imod_interval; }; diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 368f8e59219a..bfb90c52d8fc 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -247,6 +247,15 @@ static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v, case USB_DEVICE_TEST_MODE: s = "Test Mode"; break; + case USB_DEVICE_U1_ENABLE: + s = "U1 Enable"; + break; + case USB_DEVICE_U2_ENABLE: + s = "U2 Enable"; + break; + case USB_DEVICE_LTM_ENABLE: + s = "LTM Enable"; + break; default: s = "UNKNOWN"; } s; }), diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index fd3e7ad2eb0e..9c2e4a17918e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -736,11 +736,7 @@ static int dwc3_ep0_set_isoch_delay(struct dwc3 *dwc, struct usb_ctrlrequest *ct if (wIndex || wLength) return -EINVAL; - /* - * REVISIT It's unclear from Databook what to do with this - * value. For now, just cache it. - */ - dwc->isoch_delay = wValue; + dwc->gadget.isoch_delay = wValue; return 0; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 639dd1b163a0..616ef49ccb49 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2005,7 +2005,8 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g, * STAR#9000525659: Clock Domain Crossing on DCTL in * USB 2.0 Mode */ - if (dwc->revision < DWC3_REVISION_220A) { + if (dwc->revision < DWC3_REVISION_220A && + !dwc->dis_metastability_quirk) { reg |= DWC3_DCFG_SUPERSPEED; } else { switch (speed) { @@ -3224,7 +3225,8 @@ int dwc3_gadget_init(struct dwc3 *dwc) * is less than super speed because we don't have means, yet, to tell * composite.c that we are USB 2.0 + LPM ECN. */ - if (dwc->revision < DWC3_REVISION_220A) + if (dwc->revision < DWC3_REVISION_220A && + !dwc->dis_metastability_quirk) dev_info(dwc->dev, "changing max_speed on rev %08x\n", dwc->revision); diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index a9dd5c64e6c7..babaee981aa7 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -37,12 +37,12 @@ DECLARE_EVENT_CLASS(dwc3_log_io, ); DEFINE_EVENT(dwc3_log_io, dwc3_readl, - TP_PROTO(void *base, u32 offset, u32 value), + TP_PROTO(void __iomem *base, u32 offset, u32 value), TP_ARGS(base, offset, value) ); DEFINE_EVENT(dwc3_log_io, dwc3_writel, - TP_PROTO(void *base, u32 offset, u32 value), + TP_PROTO(void __iomem *base, u32 offset, u32 value), TP_ARGS(base, offset, value) ); diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c index 8a700b45b9a9..e15e896f356c 100644 --- a/drivers/usb/early/xhci-dbc.c +++ b/drivers/usb/early/xhci-dbc.c @@ -328,7 +328,7 @@ static void xdbc_mem_init(void) ep_in = (struct xdbc_ep_context *)&ctx->in; ep_in->ep_info1 = 0; - ep_in->ep_info2 = cpu_to_le32(EP_TYPE(BULK_OUT_EP) | MAX_PACKET(1024) | MAX_BURST(max_burst)); + ep_in->ep_info2 = cpu_to_le32(EP_TYPE(BULK_IN_EP) | MAX_PACKET(1024) | MAX_BURST(max_burst)); ep_in->deq = cpu_to_le64(xdbc.in_seg.dma | xdbc.in_ring.cycle_state); /* Set DbC context and info registers: */ diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index b540935891af..67564725e371 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -266,6 +266,7 @@ static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req) } static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len) + __releases(&ffs->ev.waitq.lock) { struct usb_request *req = ffs->ep0req; int ret; @@ -458,6 +459,7 @@ done_spin: /* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf, size_t n) + __releases(&ffs->ev.waitq.lock) { /* * n cannot be bigger than ffs->ev.count, which cannot be bigger than @@ -543,6 +545,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, break; } + /* unlocks spinlock */ return __ffs_ep0_read_events(ffs, buf, min(n, (size_t)ffs->ev.count)); @@ -1246,7 +1249,7 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, desc = epfile->ep->descs[desc_idx]; spin_unlock_irq(&epfile->ffs->eps_lock); - ret = copy_to_user((void *)value, desc, desc->bLength); + ret = copy_to_user((void __user *)value, desc, desc->bLength); if (ret) ret = -EFAULT; return ret; @@ -2324,7 +2327,7 @@ static int __ffs_data_do_os_desc(enum ffs_os_desc_type type, length, pnl, type); return -EINVAL; } - pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl)); + pdl = le32_to_cpu(*(__le32 *)((u8 *)data + 10 + pnl)); if (length != 14 + pnl + pdl) { pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n", length, pnl, pdl, type); @@ -2878,7 +2881,7 @@ static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type, ext_prop->type = le32_to_cpu(desc->dwPropertyDataType); ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength); - ext_prop->data_len = le32_to_cpu(*(u32 *) + ext_prop->data_len = le32_to_cpu(*(__le32 *) usb_ext_prop_data_len_ptr(data, ext_prop->name_len)); length = ext_prop->name_len + ext_prop->data_len + 14; @@ -3700,7 +3703,8 @@ static void ffs_closed(struct ffs_data *ffs) ci = opts->func_inst.group.cg_item.ci_parent->ci_parent; ffs_dev_unlock(); - unregister_gadget_item(ci); + if (test_bit(FFS_FL_BOUND, &ffs->flags)) + unregister_gadget_item(ci); return; done: ffs_dev_unlock(); diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 4d653d2960d4..29436f75bbe0 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/console.h> #include <linux/kthread.h> +#include <linux/kfifo.h> #include "u_serial.h" @@ -80,19 +81,11 @@ #define WRITE_BUF_SIZE 8192 /* TX only */ #define GS_CONSOLE_BUF_SIZE 8192 -/* circular buffer */ -struct gs_buf { - unsigned buf_size; - char *buf_buf; - char *buf_get; - char *buf_put; -}; - /* console info */ struct gscons_info { struct gs_port *port; struct task_struct *console_thread; - struct gs_buf con_buf; + struct kfifo con_buf; /* protect the buf and busy flag */ spinlock_t con_lock; int req_busy; @@ -122,7 +115,7 @@ struct gs_port { struct list_head write_pool; int write_started; int write_allocated; - struct gs_buf port_write_buf; + struct kfifo port_write_buf; wait_queue_head_t drain_wait; /* wait while writes drain */ bool write_busy; wait_queue_head_t close_wait; @@ -154,144 +147,6 @@ static struct portmaster { /*-------------------------------------------------------------------------*/ -/* Circular Buffer */ - -/* - * gs_buf_alloc - * - * Allocate a circular buffer and all associated memory. - */ -static int gs_buf_alloc(struct gs_buf *gb, unsigned size) -{ - gb->buf_buf = kmalloc(size, GFP_KERNEL); - if (gb->buf_buf == NULL) - return -ENOMEM; - - gb->buf_size = size; - gb->buf_put = gb->buf_buf; - gb->buf_get = gb->buf_buf; - - return 0; -} - -/* - * gs_buf_free - * - * Free the buffer and all associated memory. - */ -static void gs_buf_free(struct gs_buf *gb) -{ - kfree(gb->buf_buf); - gb->buf_buf = NULL; -} - -/* - * gs_buf_clear - * - * Clear out all data in the circular buffer. - */ -static void gs_buf_clear(struct gs_buf *gb) -{ - gb->buf_get = gb->buf_put; - /* equivalent to a get of all data available */ -} - -/* - * gs_buf_data_avail - * - * Return the number of bytes of data written into the circular - * buffer. - */ -static unsigned gs_buf_data_avail(struct gs_buf *gb) -{ - return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size; -} - -/* - * gs_buf_space_avail - * - * Return the number of bytes of space available in the circular - * buffer. - */ -static unsigned gs_buf_space_avail(struct gs_buf *gb) -{ - return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size; -} - -/* - * gs_buf_put - * - * Copy data data from a user buffer and put it into the circular buffer. - * Restrict to the amount of space available. - * - * Return the number of bytes copied. - */ -static unsigned -gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count) -{ - unsigned len; - - len = gs_buf_space_avail(gb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = gb->buf_buf + gb->buf_size - gb->buf_put; - if (count > len) { - memcpy(gb->buf_put, buf, len); - memcpy(gb->buf_buf, buf+len, count - len); - gb->buf_put = gb->buf_buf + count - len; - } else { - memcpy(gb->buf_put, buf, count); - if (count < len) - gb->buf_put += count; - else /* count == len */ - gb->buf_put = gb->buf_buf; - } - - return count; -} - -/* - * gs_buf_get - * - * Get data from the circular buffer and copy to the given buffer. - * Restrict to the amount of data available. - * - * Return the number of bytes copied. - */ -static unsigned -gs_buf_get(struct gs_buf *gb, char *buf, unsigned count) -{ - unsigned len; - - len = gs_buf_data_avail(gb); - if (count > len) - count = len; - - if (count == 0) - return 0; - - len = gb->buf_buf + gb->buf_size - gb->buf_get; - if (count > len) { - memcpy(buf, gb->buf_get, len); - memcpy(buf+len, gb->buf_buf, count - len); - gb->buf_get = gb->buf_buf + count - len; - } else { - memcpy(buf, gb->buf_get, count); - if (count < len) - gb->buf_get += count; - else /* count == len */ - gb->buf_get = gb->buf_buf; - } - - return count; -} - -/*-------------------------------------------------------------------------*/ - /* I/O glue between TTY (upper) and USB function (lower) driver layers */ /* @@ -346,11 +201,11 @@ gs_send_packet(struct gs_port *port, char *packet, unsigned size) { unsigned len; - len = gs_buf_data_avail(&port->port_write_buf); + len = kfifo_len(&port->port_write_buf); if (len < size) size = len; if (size != 0) - size = gs_buf_get(&port->port_write_buf, packet, size); + size = kfifo_out(&port->port_write_buf, packet, size); return size; } @@ -398,7 +253,7 @@ __acquires(&port->port_lock) req->length = len; list_del(&req->list); - req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0); + req->zero = kfifo_is_empty(&port->port_write_buf); pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n", port->port_num, len, *((u8 *)req->buf), @@ -787,10 +642,11 @@ static int gs_open(struct tty_struct *tty, struct file *file) spin_lock_irq(&port->port_lock); /* allocate circular buffer on first open */ - if (port->port_write_buf.buf_buf == NULL) { + if (!kfifo_initialized(&port->port_write_buf)) { spin_unlock_irq(&port->port_lock); - status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE); + status = kfifo_alloc(&port->port_write_buf, + WRITE_BUF_SIZE, GFP_KERNEL); spin_lock_irq(&port->port_lock); if (status) { @@ -839,7 +695,7 @@ static int gs_writes_finished(struct gs_port *p) /* return true on disconnect or empty buffer */ spin_lock_irq(&p->port_lock); - cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf); + cond = (p->port_usb == NULL) || !kfifo_len(&p->port_write_buf); spin_unlock_irq(&p->port_lock); return cond; @@ -875,7 +731,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) /* wait for circular write buffer to drain, disconnect, or at * most GS_CLOSE_TIMEOUT seconds; then discard the rest */ - if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) { + if (kfifo_len(&port->port_write_buf) > 0 && gser) { spin_unlock_irq(&port->port_lock); wait_event_interruptible_timeout(port->drain_wait, gs_writes_finished(port), @@ -889,9 +745,9 @@ static void gs_close(struct tty_struct *tty, struct file *file) * let the push tasklet fire again until we're re-opened. */ if (gser == NULL) - gs_buf_free(&port->port_write_buf); + kfifo_free(&port->port_write_buf); else - gs_buf_clear(&port->port_write_buf); + kfifo_reset(&port->port_write_buf); port->port.tty = NULL; @@ -915,7 +771,7 @@ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) spin_lock_irqsave(&port->port_lock, flags); if (count) - count = gs_buf_put(&port->port_write_buf, buf, count); + count = kfifo_in(&port->port_write_buf, buf, count); /* treat count == 0 as flush_chars() */ if (port->port_usb) gs_start_tx(port); @@ -934,7 +790,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch) port->port_num, tty, ch, __builtin_return_address(0)); spin_lock_irqsave(&port->port_lock, flags); - status = gs_buf_put(&port->port_write_buf, &ch, 1); + status = kfifo_put(&port->port_write_buf, ch); spin_unlock_irqrestore(&port->port_lock, flags); return status; @@ -961,7 +817,7 @@ static int gs_write_room(struct tty_struct *tty) spin_lock_irqsave(&port->port_lock, flags); if (port->port_usb) - room = gs_buf_space_avail(&port->port_write_buf); + room = kfifo_avail(&port->port_write_buf); spin_unlock_irqrestore(&port->port_lock, flags); pr_vdebug("gs_write_room: (%d,%p) room=%d\n", @@ -977,7 +833,7 @@ static int gs_chars_in_buffer(struct tty_struct *tty) int chars = 0; spin_lock_irqsave(&port->port_lock, flags); - chars = gs_buf_data_avail(&port->port_write_buf); + chars = kfifo_len(&port->port_write_buf); spin_unlock_irqrestore(&port->port_lock, flags); pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n", @@ -1148,7 +1004,7 @@ static int gs_console_thread(void *data) ep = port->port_usb->in; spin_lock_irq(&info->con_lock); - count = gs_buf_data_avail(&info->con_buf); + count = kfifo_len(&info->con_buf); size = ep->maxpacket; if (count > 0 && !info->req_busy) { @@ -1156,7 +1012,7 @@ static int gs_console_thread(void *data) if (count < size) size = count; - xfer = gs_buf_get(&info->con_buf, req->buf, size); + xfer = kfifo_out(&info->con_buf, req->buf, size); req->length = xfer; spin_unlock(&info->con_lock); @@ -1192,7 +1048,7 @@ static int gs_console_setup(struct console *co, char *options) info->req_busy = 0; spin_lock_init(&info->con_lock); - status = gs_buf_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE); + status = kfifo_alloc(&info->con_buf, GS_CONSOLE_BUF_SIZE, GFP_KERNEL); if (status) { pr_err("%s: allocate console buffer failed\n", __func__); return status; @@ -1202,7 +1058,7 @@ static int gs_console_setup(struct console *co, char *options) co, "gs_console"); if (IS_ERR(info->console_thread)) { pr_err("%s: cannot create console thread\n", __func__); - gs_buf_free(&info->con_buf); + kfifo_free(&info->con_buf); return PTR_ERR(info->console_thread); } wake_up_process(info->console_thread); @@ -1217,7 +1073,7 @@ static void gs_console_write(struct console *co, unsigned long flags; spin_lock_irqsave(&info->con_lock, flags); - gs_buf_put(&info->con_buf, buf, count); + kfifo_in(&info->con_buf, buf, count); spin_unlock_irqrestore(&info->con_lock, flags); wake_up_process(info->console_thread); @@ -1256,7 +1112,7 @@ static void gserial_console_exit(void) unregister_console(&gserial_cons); if (!IS_ERR_OR_NULL(info->console_thread)) kthread_stop(info->console_thread); - gs_buf_free(&info->con_buf); + kfifo_free(&info->con_buf); } #else @@ -1529,7 +1385,7 @@ void gserial_disconnect(struct gserial *gser) /* finally, free any unused/unusable I/O buffers */ spin_lock_irqsave(&port->port_lock, flags); if (port->port.count == 0 && !port->openclose) - gs_buf_free(&port->port_write_buf); + kfifo_free(&port->port_write_buf); gs_free_requests(gser->out, &port->read_pool, NULL); gs_free_requests(gser->out, &port->read_queue, NULL); gs_free_requests(gser->in, &port->write_pool, NULL); diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 05691254d473..5960e76f4c75 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1470,7 +1470,6 @@ delegate: dev->setup_wLength = w_length; dev->setup_out_ready = 0; dev->setup_out_error = 0; - value = 0; /* read DATA stage for OUT right away */ if (unlikely (!dev->setup_in && w_length)) { diff --git a/drivers/usb/gadget/legacy/ncm.c b/drivers/usb/gadget/legacy/ncm.c index fcee1ee0bf66..8465f081e921 100644 --- a/drivers/usb/gadget/legacy/ncm.c +++ b/drivers/usb/gadget/legacy/ncm.c @@ -102,10 +102,8 @@ static int ncm_do_config(struct usb_configuration *c) } f_ncm = usb_get_function(f_ncm_inst); - if (IS_ERR(f_ncm)) { - status = PTR_ERR(f_ncm); - return status; - } + if (IS_ERR(f_ncm)) + return PTR_ERR(f_ncm); status = usb_add_function(c, f_ncm); if (status < 0) { diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index 29f254793592..465ccd1104de 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2385,10 +2385,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) goto out_uninit; } if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0, - dev_name(dev), udc) < 0) { - dev_err(dev, "error requesting IRQ #%d\n", irq); - goto out_uninit; - } + dev_name(dev), udc) < 0) + goto report_request_failure; /* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) { @@ -2398,10 +2396,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) goto out_uninit; } if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0, - dev_name(dev), &udc->iudma[i]) < 0) { - dev_err(dev, "error requesting IRQ #%d\n", irq); - goto out_uninit; - } + dev_name(dev), &udc->iudma[i]) < 0) + goto report_request_failure; } bcm63xx_udc_init_debugfs(udc); @@ -2413,6 +2409,10 @@ static int bcm63xx_udc_probe(struct platform_device *pdev) out_uninit: bcm63xx_uninit_udc_hw(udc); return rc; + +report_request_failure: + dev_err(dev, "error requesting IRQ #%d\n", irq); + goto out_uninit; } /** diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 1b3efb14aec7..859d5b11ba4c 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -912,7 +912,7 @@ int usb_gadget_ep_match_desc(struct usb_gadget *gadget, return 0; /* "high bandwidth" works only at high speed */ - if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp(desc) & (3<<11)) + if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp_mult(desc) > 1) return 0; switch (type) { @@ -1417,7 +1417,7 @@ EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); /* ------------------------------------------------------------------------- */ -static ssize_t usb_udc_srp_store(struct device *dev, +static ssize_t srp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct usb_udc *udc = container_of(dev, struct usb_udc, dev); @@ -1427,9 +1427,9 @@ static ssize_t usb_udc_srp_store(struct device *dev, return n; } -static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store); +static DEVICE_ATTR_WO(srp); -static ssize_t usb_udc_softconn_store(struct device *dev, +static ssize_t soft_connect_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct usb_udc *udc = container_of(dev, struct usb_udc, dev); @@ -1453,7 +1453,7 @@ static ssize_t usb_udc_softconn_store(struct device *dev, return n; } -static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); +static DEVICE_ATTR_WO(soft_connect); static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index d0128f92ec5a..e744d4b7bfed 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -925,20 +925,8 @@ static void dummy_udc_set_speed(struct usb_gadget *_gadget, struct dummy *dum; dum = gadget_dev_to_dummy(&_gadget->dev); - - if (mod_data.is_super_speed) - dum->gadget.speed = min_t(u8, USB_SPEED_SUPER, speed); - else if (mod_data.is_high_speed) - dum->gadget.speed = min_t(u8, USB_SPEED_HIGH, speed); - else - dum->gadget.speed = USB_SPEED_FULL; - + dum->gadget.speed = speed; dummy_udc_update_ep0(dum); - - if (dum->gadget.speed < speed) - dev_dbg(udc_dev(dum), "This device can perform faster" - " if you connect it to a %s port...\n", - usb_speed_string(speed)); } static int dummy_udc_start(struct usb_gadget *g, @@ -2193,8 +2181,6 @@ static int dummy_hub_control( USB_PORT_STAT_LOW_SPEED; break; default: - dum_hcd->dum->gadget.speed = - USB_SPEED_FULL; break; } } diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index d606d4f13098..e5b4ee96c4bf 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1543,7 +1543,7 @@ static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, udc->ep0_state = WAIT_FOR_SETUP; break; case WAIT_FOR_SETUP: - ERR("Unexpect ep0 packets\n"); + ERR("Unexpected ep0 packets\n"); break; default: ep0stall(udc); diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index be2761f1b3f5..fadcf2653c3d 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -979,8 +979,6 @@ static int write_fifo(struct pxa_ep *ep, struct pxa27x_request *req) max = ep->fifo_size; do { - is_short = 0; - udccsr = udc_ep_readl(ep, UDCCSR); if (udccsr & UDCCSR_PC) { ep_vdbg(ep, "Clearing Transmit Complete, udccsr=%x\n", @@ -1134,7 +1132,6 @@ static int pxa_ep_queue(struct usb_ep *_ep, struct usb_request *_req, if (unlikely(!_ep)) return -EINVAL; - dev = udc_usb_ep->dev; ep = udc_usb_ep->pxa_ep; if (unlikely(!ep)) return -EINVAL; diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h index f07ddb3f4bb9..2d1f68b5ea76 100644 --- a/drivers/usb/gadget/udc/trace.h +++ b/drivers/usb/gadget/udc/trace.h @@ -225,6 +225,7 @@ DECLARE_EVENT_CLASS(udc_log_req, __field(unsigned, short_not_ok) __field(int, status) __field(int, ret) + __field(struct usb_request *, req) ), TP_fast_assign( snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name); @@ -238,9 +239,10 @@ DECLARE_EVENT_CLASS(udc_log_req, __entry->short_not_ok = req->short_not_ok; __entry->status = req->status; __entry->ret = ret; + __entry->req = req; ), - TP_printk("%s: length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d", - __get_str(name), __entry->actual, __entry->length, + TP_printk("%s: req %p length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d", + __get_str(name),__entry->req, __entry->actual, __entry->length, __entry->num_mapped_sgs, __entry->num_sgs, __entry->stream_id, __entry->zero ? "Z" : "z", __entry->short_not_ok ? "S" : "s", diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index 7da2b9ce8cb3..6407e433bc78 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -963,10 +963,8 @@ static struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { struct xusb_ep *ep = to_xusb_ep(_ep); - struct xusb_udc *udc; struct xusb_req *req; - udc = ep->udc; req = kzalloc(sizeof(*req), gfp_flags); if (!req) return NULL; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index b80a94e632af..6150bed7cfa8 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -27,6 +27,14 @@ config USB_XHCI_HCD module will be called xhci-hcd. if USB_XHCI_HCD +config USB_XHCI_DBGCAP + bool "xHCI support for debug capability" + depends on TTY + ---help--- + Say 'Y' to enable the support for the xHCI debug capability. Make + sure that your xHCI host supports the extended debug capability and + you want a TTY serial device based on the xHCI debug capability + before enabling this option. If unsure, say 'N'. config USB_XHCI_PCI tristate diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 32b036e2ffef..4ede4ce12366 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -14,6 +14,11 @@ fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o xhci-hcd-y := xhci.o xhci-mem.o xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o xhci-hcd-y += xhci-trace.o + +ifneq ($(CONFIG_USB_XHCI_DBGCAP), ) + xhci-hcd-y += xhci-dbgcap.o xhci-dbgtty.o +endif + ifneq ($(CONFIG_USB_XHCI_MTK), ) xhci-hcd-y += xhci-mtk-sch.o endif diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 854b146a457d..8d8bafc70c1f 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -167,7 +167,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) continue; ret = PTR_ERR(phy); - dev_err(dev, "Can't get PHY device for port %d: %d\n", + if (ret != -EPROBE_DEFER) + dev_err(dev, "Can't get PHY for port %d: %d\n", i, ret); goto err_phy; } diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c index 71fb61dd4a87..8f75cb7b197c 100644 --- a/drivers/usb/host/ehci-sysfs.c +++ b/drivers/usb/host/ehci-sysfs.c @@ -7,7 +7,7 @@ /* Display the ports dedicated to the companion controller */ -static ssize_t show_companion(struct device *dev, +static ssize_t companion_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -34,7 +34,7 @@ static ssize_t show_companion(struct device *dev, * Syntax is "[-]portnum", where a leading '-' sign means * return control of the port to the EHCI controller. */ -static ssize_t store_companion(struct device *dev, +static ssize_t companion_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -59,13 +59,13 @@ static ssize_t store_companion(struct device *dev, set_owner(ehci, portnum, new_owner); return count; } -static DEVICE_ATTR(companion, 0644, show_companion, store_companion); +static DEVICE_ATTR_RW(companion); /* * Display / Set uframe_periodic_max */ -static ssize_t show_uframe_periodic_max(struct device *dev, +static ssize_t uframe_periodic_max_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -78,7 +78,7 @@ static ssize_t show_uframe_periodic_max(struct device *dev, } -static ssize_t store_uframe_periodic_max(struct device *dev, +static ssize_t uframe_periodic_max_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -143,7 +143,7 @@ out_unlock: spin_unlock_irqrestore (&ehci->lock, flags); return ret; } -static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max); +static DEVICE_ATTR_RW(uframe_periodic_max); static inline int create_sysfs_files(struct ehci_hcd *ehci) diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 62fc955085a1..d8abf401918a 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -1865,11 +1865,9 @@ static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, qh = kzalloc(sizeof(*qh), GFP_ATOMIC); if (!qh) goto done; - qh->hw = (struct fotg210_qh_hw *) - dma_pool_alloc(fotg210->qh_pool, flags, &dma); + qh->hw = dma_pool_zalloc(fotg210->qh_pool, flags, &dma); if (!qh->hw) goto fail; - memset(qh->hw, 0, sizeof(*qh->hw)); qh->qh_dma = dma; INIT_LIST_HEAD(&qh->qtd_list); @@ -4121,7 +4119,7 @@ static int itd_urb_transaction(struct fotg210_iso_stream *stream, } else { alloc_itd: spin_unlock_irqrestore(&fotg210->lock, flags); - itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, + itd = dma_pool_zalloc(fotg210->itd_pool, mem_flags, &itd_dma); spin_lock_irqsave(&fotg210->lock, flags); if (!itd) { @@ -4131,7 +4129,6 @@ alloc_itd: } } - memset(itd, 0, sizeof(*itd)); itd->itd_dma = itd_dma; list_add(&itd->itd_list, &sched->td_list); } @@ -4696,7 +4693,7 @@ static void scan_isoc(struct fotg210_hcd *fotg210) /* Display / Set uframe_periodic_max */ -static ssize_t show_uframe_periodic_max(struct device *dev, +static ssize_t uframe_periodic_max_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fotg210_hcd *fotg210; @@ -4708,7 +4705,7 @@ static ssize_t show_uframe_periodic_max(struct device *dev, } -static ssize_t store_uframe_periodic_max(struct device *dev, +static ssize_t uframe_periodic_max_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fotg210_hcd *fotg210; @@ -4775,8 +4772,7 @@ out_unlock: return ret; } -static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, - store_uframe_periodic_max); +static DEVICE_ATTR_RW(uframe_periodic_max); static inline int create_sysfs_files(struct fotg210_hcd *fotg210) { diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 0c507a0cfe1f..a55cbba40a5a 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -413,7 +413,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev) da8xx_ohci = to_da8xx_ohci(hcd); da8xx_ohci->hcd = hcd; - da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, "usb11"); + da8xx_ohci->usb11_clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(da8xx_ohci->usb11_clk)) { error = PTR_ERR(da8xx_ohci->usb11_clk); if (error != -EPROBE_DEFER) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index f5c90217777a..f9c3947577fc 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -600,7 +600,7 @@ static int uhci_start(struct usb_hcd *hcd) uhci->dentry = dentry; #endif - uhci->frame = dma_alloc_coherent(uhci_dev(uhci), + uhci->frame = dma_zalloc_coherent(uhci_dev(uhci), UHCI_NUMFRAMES * sizeof(*uhci->frame), &uhci->frame_dma_handle, GFP_KERNEL); if (!uhci->frame) { @@ -608,7 +608,6 @@ static int uhci_start(struct usb_hcd *hcd) "unable to allocate consistent memory for frame list\n"); goto err_alloc_frame; } - memset(uhci->frame, 0, UHCI_NUMFRAMES * sizeof(*uhci->frame)); uhci->frame_cpu = kcalloc(UHCI_NUMFRAMES, sizeof(*uhci->frame_cpu), GFP_KERNEL); diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index f1cc47292a59..7f9f33c8c232 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -4,6 +4,7 @@ #include <linux/list.h> #include <linux/usb.h> +#include <linux/clk.h> #define usb_packetid(pipe) (usb_pipein(pipe) ? USB_PID_IN : USB_PID_OUT) #define PIPE_DEVEP_MASK 0x0007ff00 @@ -447,6 +448,8 @@ struct uhci_hcd { int total_load; /* Sum of array values */ short load[MAX_PHASE]; /* Periodic allocations */ + struct clk *clk; /* (optional) clock source */ + /* Reset host controller */ void (*reset_hc) (struct uhci_hcd *uhci); int (*check_and_reset_hc) (struct uhci_hcd *uhci); diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 6cb16d4b2257..89700e26fb29 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -89,6 +89,8 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) if (!hcd) return -ENOMEM; + uhci = hcd_to_uhci(hcd); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hcd->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(hcd->regs)) { @@ -98,8 +100,6 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - uhci = hcd_to_uhci(hcd); - uhci->regs = hcd->regs; /* Grab some things from the device-tree */ @@ -119,13 +119,28 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev) "Enabled Aspeed implementation workarounds\n"); } } + + /* Get and enable clock if any specified */ + uhci->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(uhci->clk)) { + ret = PTR_ERR(uhci->clk); + goto err_rmr; + } + ret = clk_prepare_enable(uhci->clk); + if (ret) { + dev_err(&pdev->dev, "Error couldn't enable clock (%d)\n", ret); + goto err_rmr; + } + ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); if (ret) - goto err_rmr; + goto err_clk; device_wakeup_enable(hcd->self.controller); return 0; +err_clk: + clk_disable_unprepare(uhci->clk); err_rmr: usb_put_hcd(hcd); @@ -135,7 +150,9 @@ err_rmr: static int uhci_hcd_platform_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + clk_disable_unprepare(uhci->clk); usb_remove_hcd(hcd); usb_put_hcd(hcd); diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index d40438238938..35fcb826152c 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -73,8 +73,7 @@ static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb) { struct urb_priv *urbp = urb->hcpriv; - if (!(urb->transfer_flags & URB_NO_FSBR)) - urbp->fsbr = 1; + urbp->fsbr = 1; } static void uhci_urbp_wants_fsbr(struct uhci_hcd *uhci, struct urb_priv *urbp) diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index c5ac9efb076a..276fb34c8efd 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c @@ -90,9 +90,7 @@ static uint32_t process_qset(struct whc *whc, struct whc_qset *qset) while (qset->ntds) { struct whc_qtd *td; - int t; - t = qset->td_start; td = &qset->qtd[qset->td_start]; status = le32_to_cpu(td->status); diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 584d7b9a3683..386abf26641d 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -10,267 +10,6 @@ #include "xhci.h" -#define XHCI_INIT_VALUE 0x0 - -/* Add verbose debugging later, just print everything for now */ - -void xhci_dbg_regs(struct xhci_hcd *xhci) -{ - u32 temp; - - xhci_dbg(xhci, "// xHCI capability registers at %p:\n", - xhci->cap_regs); - temp = readl(&xhci->cap_regs->hc_capbase); - xhci_dbg(xhci, "// @%p = 0x%x (CAPLENGTH AND HCIVERSION)\n", - &xhci->cap_regs->hc_capbase, temp); - xhci_dbg(xhci, "// CAPLENGTH: 0x%x\n", - (unsigned int) HC_LENGTH(temp)); - xhci_dbg(xhci, "// HCIVERSION: 0x%x\n", - (unsigned int) HC_VERSION(temp)); - - xhci_dbg(xhci, "// xHCI operational registers at %p:\n", xhci->op_regs); - - temp = readl(&xhci->cap_regs->run_regs_off); - xhci_dbg(xhci, "// @%p = 0x%x RTSOFF\n", - &xhci->cap_regs->run_regs_off, - (unsigned int) temp & RTSOFF_MASK); - xhci_dbg(xhci, "// xHCI runtime registers at %p:\n", xhci->run_regs); - - temp = readl(&xhci->cap_regs->db_off); - xhci_dbg(xhci, "// @%p = 0x%x DBOFF\n", &xhci->cap_regs->db_off, temp); - xhci_dbg(xhci, "// Doorbell array at %p:\n", xhci->dba); -} - -static void xhci_print_cap_regs(struct xhci_hcd *xhci) -{ - u32 temp; - u32 hci_version; - - xhci_dbg(xhci, "xHCI capability registers at %p:\n", xhci->cap_regs); - - temp = readl(&xhci->cap_regs->hc_capbase); - hci_version = HC_VERSION(temp); - xhci_dbg(xhci, "CAPLENGTH AND HCIVERSION 0x%x:\n", - (unsigned int) temp); - xhci_dbg(xhci, "CAPLENGTH: 0x%x\n", - (unsigned int) HC_LENGTH(temp)); - xhci_dbg(xhci, "HCIVERSION: 0x%x\n", hci_version); - - temp = readl(&xhci->cap_regs->hcs_params1); - xhci_dbg(xhci, "HCSPARAMS 1: 0x%x\n", - (unsigned int) temp); - xhci_dbg(xhci, " Max device slots: %u\n", - (unsigned int) HCS_MAX_SLOTS(temp)); - xhci_dbg(xhci, " Max interrupters: %u\n", - (unsigned int) HCS_MAX_INTRS(temp)); - xhci_dbg(xhci, " Max ports: %u\n", - (unsigned int) HCS_MAX_PORTS(temp)); - - temp = readl(&xhci->cap_regs->hcs_params2); - xhci_dbg(xhci, "HCSPARAMS 2: 0x%x\n", - (unsigned int) temp); - xhci_dbg(xhci, " Isoc scheduling threshold: %u\n", - (unsigned int) HCS_IST(temp)); - xhci_dbg(xhci, " Maximum allowed segments in event ring: %u\n", - (unsigned int) HCS_ERST_MAX(temp)); - - temp = readl(&xhci->cap_regs->hcs_params3); - xhci_dbg(xhci, "HCSPARAMS 3 0x%x:\n", - (unsigned int) temp); - xhci_dbg(xhci, " Worst case U1 device exit latency: %u\n", - (unsigned int) HCS_U1_LATENCY(temp)); - xhci_dbg(xhci, " Worst case U2 device exit latency: %u\n", - (unsigned int) HCS_U2_LATENCY(temp)); - - temp = readl(&xhci->cap_regs->hcc_params); - xhci_dbg(xhci, "HCC PARAMS 0x%x:\n", (unsigned int) temp); - xhci_dbg(xhci, " HC generates %s bit addresses\n", - HCC_64BIT_ADDR(temp) ? "64" : "32"); - xhci_dbg(xhci, " HC %s Contiguous Frame ID Capability\n", - HCC_CFC(temp) ? "has" : "hasn't"); - xhci_dbg(xhci, " HC %s generate Stopped - Short Package event\n", - HCC_SPC(temp) ? "can" : "can't"); - /* FIXME */ - xhci_dbg(xhci, " FIXME: more HCCPARAMS debugging\n"); - - temp = readl(&xhci->cap_regs->run_regs_off); - xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK); - - /* xhci 1.1 controllers have the HCCPARAMS2 register */ - if (hci_version > 0x100) { - temp = readl(&xhci->cap_regs->hcc_params2); - xhci_dbg(xhci, "HCC PARAMS2 0x%x:\n", (unsigned int) temp); - xhci_dbg(xhci, " HC %s Force save context capability", - HCC2_FSC(temp) ? "supports" : "doesn't support"); - xhci_dbg(xhci, " HC %s Large ESIT Payload Capability", - HCC2_LEC(temp) ? "supports" : "doesn't support"); - xhci_dbg(xhci, " HC %s Extended TBC capability", - HCC2_ETC(temp) ? "supports" : "doesn't support"); - } -} - -static void xhci_print_command_reg(struct xhci_hcd *xhci) -{ - u32 temp; - - temp = readl(&xhci->op_regs->command); - xhci_dbg(xhci, "USBCMD 0x%x:\n", temp); - xhci_dbg(xhci, " HC is %s\n", - (temp & CMD_RUN) ? "running" : "being stopped"); - xhci_dbg(xhci, " HC has %sfinished hard reset\n", - (temp & CMD_RESET) ? "not " : ""); - xhci_dbg(xhci, " Event Interrupts %s\n", - (temp & CMD_EIE) ? "enabled " : "disabled"); - xhci_dbg(xhci, " Host System Error Interrupts %s\n", - (temp & CMD_HSEIE) ? "enabled " : "disabled"); - xhci_dbg(xhci, " HC has %sfinished light reset\n", - (temp & CMD_LRESET) ? "not " : ""); -} - -static void xhci_print_status(struct xhci_hcd *xhci) -{ - u32 temp; - - temp = readl(&xhci->op_regs->status); - xhci_dbg(xhci, "USBSTS 0x%x:\n", temp); - xhci_dbg(xhci, " Event ring is %sempty\n", - (temp & STS_EINT) ? "not " : ""); - xhci_dbg(xhci, " %sHost System Error\n", - (temp & STS_FATAL) ? "WARNING: " : "No "); - xhci_dbg(xhci, " HC is %s\n", - (temp & STS_HALT) ? "halted" : "running"); -} - -static void xhci_print_op_regs(struct xhci_hcd *xhci) -{ - xhci_dbg(xhci, "xHCI operational registers at %p:\n", xhci->op_regs); - xhci_print_command_reg(xhci); - xhci_print_status(xhci); -} - -static void xhci_print_ports(struct xhci_hcd *xhci) -{ - __le32 __iomem *addr; - int i, j; - int ports; - char *names[NUM_PORT_REGS] = { - "status", - "power", - "link", - "reserved", - }; - - ports = HCS_MAX_PORTS(xhci->hcs_params1); - addr = &xhci->op_regs->port_status_base; - for (i = 0; i < ports; i++) { - for (j = 0; j < NUM_PORT_REGS; j++) { - xhci_dbg(xhci, "%p port %s reg = 0x%x\n", - addr, names[j], - (unsigned int) readl(addr)); - addr++; - } - } -} - -void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num) -{ - struct xhci_intr_reg __iomem *ir_set = &xhci->run_regs->ir_set[set_num]; - void __iomem *addr; - u32 temp; - u64 temp_64; - - addr = &ir_set->irq_pending; - temp = readl(addr); - if (temp == XHCI_INIT_VALUE) - return; - - xhci_dbg(xhci, " %p: ir_set[%i]\n", ir_set, set_num); - - xhci_dbg(xhci, " %p: ir_set.pending = 0x%x\n", addr, - (unsigned int)temp); - - addr = &ir_set->irq_control; - temp = readl(addr); - xhci_dbg(xhci, " %p: ir_set.control = 0x%x\n", addr, - (unsigned int)temp); - - addr = &ir_set->erst_size; - temp = readl(addr); - xhci_dbg(xhci, " %p: ir_set.erst_size = 0x%x\n", addr, - (unsigned int)temp); - - addr = &ir_set->rsvd; - temp = readl(addr); - if (temp != XHCI_INIT_VALUE) - xhci_dbg(xhci, " WARN: %p: ir_set.rsvd = 0x%x\n", - addr, (unsigned int)temp); - - addr = &ir_set->erst_base; - temp_64 = xhci_read_64(xhci, addr); - xhci_dbg(xhci, " %p: ir_set.erst_base = @%08llx\n", - addr, temp_64); - - addr = &ir_set->erst_dequeue; - temp_64 = xhci_read_64(xhci, addr); - xhci_dbg(xhci, " %p: ir_set.erst_dequeue = @%08llx\n", - addr, temp_64); -} - -void xhci_print_run_regs(struct xhci_hcd *xhci) -{ - u32 temp; - int i; - - xhci_dbg(xhci, "xHCI runtime registers at %p:\n", xhci->run_regs); - temp = readl(&xhci->run_regs->microframe_index); - xhci_dbg(xhci, " %p: Microframe index = 0x%x\n", - &xhci->run_regs->microframe_index, - (unsigned int) temp); - for (i = 0; i < 7; i++) { - temp = readl(&xhci->run_regs->rsvd[i]); - if (temp != XHCI_INIT_VALUE) - xhci_dbg(xhci, " WARN: %p: Rsvd[%i] = 0x%x\n", - &xhci->run_regs->rsvd[i], - i, (unsigned int) temp); - } -} - -void xhci_print_registers(struct xhci_hcd *xhci) -{ - xhci_print_cap_regs(xhci); - xhci_print_op_regs(xhci); - xhci_print_ports(xhci); -} - -void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) -{ - u64 addr = erst->erst_dma_addr; - int i; - struct xhci_erst_entry *entry; - - for (i = 0; i < erst->num_entries; i++) { - entry = &erst->entries[i]; - xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", - addr, - lower_32_bits(le64_to_cpu(entry->seg_addr)), - upper_32_bits(le64_to_cpu(entry->seg_addr)), - le32_to_cpu(entry->seg_size), - le32_to_cpu(entry->rsvd)); - addr += sizeof(*entry); - } -} - -void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci) -{ - u64 val; - - val = xhci_read_64(xhci, &xhci->op_regs->cmd_ring); - xhci_dbg(xhci, "// xHC command ring deq ptr low bits + flags = @%08x\n", - lower_32_bits(val)); - xhci_dbg(xhci, "// xHC command ring deq ptr high bits = @%08x\n", - upper_32_bits(val)); -} - char *xhci_get_slot_state(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx) { diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c new file mode 100644 index 000000000000..a1ab8acf39ba --- /dev/null +++ b/drivers/usb/host/xhci-dbgcap.c @@ -0,0 +1,996 @@ +/** + * xhci-dbgcap.c - xHCI debug capability support + * + * Copyright (C) 2017 Intel Corporation + * + * Author: Lu Baolu <baolu.lu@linux.intel.com> + */ +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/nls.h> + +#include "xhci.h" +#include "xhci-trace.h" +#include "xhci-dbgcap.h" + +static inline void * +dbc_dma_alloc_coherent(struct xhci_hcd *xhci, size_t size, + dma_addr_t *dma_handle, gfp_t flags) +{ + void *vaddr; + + vaddr = dma_alloc_coherent(xhci_to_hcd(xhci)->self.sysdev, + size, dma_handle, flags); + memset(vaddr, 0, size); + return vaddr; +} + +static inline void +dbc_dma_free_coherent(struct xhci_hcd *xhci, size_t size, + void *cpu_addr, dma_addr_t dma_handle) +{ + if (cpu_addr) + dma_free_coherent(xhci_to_hcd(xhci)->self.sysdev, + size, cpu_addr, dma_handle); +} + +static u32 xhci_dbc_populate_strings(struct dbc_str_descs *strings) +{ + struct usb_string_descriptor *s_desc; + u32 string_length; + + /* Serial string: */ + s_desc = (struct usb_string_descriptor *)strings->serial; + utf8s_to_utf16s(DBC_STRING_SERIAL, strlen(DBC_STRING_SERIAL), + UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, + DBC_MAX_STRING_LENGTH); + + s_desc->bLength = (strlen(DBC_STRING_SERIAL) + 1) * 2; + s_desc->bDescriptorType = USB_DT_STRING; + string_length = s_desc->bLength; + string_length <<= 8; + + /* Product string: */ + s_desc = (struct usb_string_descriptor *)strings->product; + utf8s_to_utf16s(DBC_STRING_PRODUCT, strlen(DBC_STRING_PRODUCT), + UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, + DBC_MAX_STRING_LENGTH); + + s_desc->bLength = (strlen(DBC_STRING_PRODUCT) + 1) * 2; + s_desc->bDescriptorType = USB_DT_STRING; + string_length += s_desc->bLength; + string_length <<= 8; + + /* Manufacture string: */ + s_desc = (struct usb_string_descriptor *)strings->manufacturer; + utf8s_to_utf16s(DBC_STRING_MANUFACTURER, + strlen(DBC_STRING_MANUFACTURER), + UTF16_LITTLE_ENDIAN, (wchar_t *)s_desc->wData, + DBC_MAX_STRING_LENGTH); + + s_desc->bLength = (strlen(DBC_STRING_MANUFACTURER) + 1) * 2; + s_desc->bDescriptorType = USB_DT_STRING; + string_length += s_desc->bLength; + string_length <<= 8; + + /* String0: */ + strings->string0[0] = 4; + strings->string0[1] = USB_DT_STRING; + strings->string0[2] = 0x09; + strings->string0[3] = 0x04; + string_length += 4; + + return string_length; +} + +static void xhci_dbc_init_contexts(struct xhci_hcd *xhci, u32 string_length) +{ + struct xhci_dbc *dbc; + struct dbc_info_context *info; + struct xhci_ep_ctx *ep_ctx; + u32 dev_info; + dma_addr_t deq, dma; + unsigned int max_burst; + + dbc = xhci->dbc; + if (!dbc) + return; + + /* Populate info Context: */ + info = (struct dbc_info_context *)dbc->ctx->bytes; + dma = dbc->string_dma; + info->string0 = cpu_to_le64(dma); + info->manufacturer = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH); + info->product = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 2); + info->serial = cpu_to_le64(dma + DBC_MAX_STRING_LENGTH * 3); + info->length = cpu_to_le32(string_length); + + /* Populate bulk out endpoint context: */ + ep_ctx = dbc_bulkout_ctx(dbc); + max_burst = DBC_CTRL_MAXBURST(readl(&dbc->regs->control)); + deq = dbc_bulkout_enq(dbc); + ep_ctx->ep_info = 0; + ep_ctx->ep_info2 = dbc_epctx_info2(BULK_OUT_EP, 1024, max_burst); + ep_ctx->deq = cpu_to_le64(deq | dbc->ring_out->cycle_state); + + /* Populate bulk in endpoint context: */ + ep_ctx = dbc_bulkin_ctx(dbc); + deq = dbc_bulkin_enq(dbc); + ep_ctx->ep_info = 0; + ep_ctx->ep_info2 = dbc_epctx_info2(BULK_IN_EP, 1024, max_burst); + ep_ctx->deq = cpu_to_le64(deq | dbc->ring_in->cycle_state); + + /* Set DbC context and info registers: */ + xhci_write_64(xhci, dbc->ctx->dma, &dbc->regs->dccp); + + dev_info = cpu_to_le32((DBC_VENDOR_ID << 16) | DBC_PROTOCOL); + writel(dev_info, &dbc->regs->devinfo1); + + dev_info = cpu_to_le32((DBC_DEVICE_REV << 16) | DBC_PRODUCT_ID); + writel(dev_info, &dbc->regs->devinfo2); +} + +static void xhci_dbc_giveback(struct dbc_request *req, int status) + __releases(&dbc->lock) + __acquires(&dbc->lock) +{ + struct dbc_ep *dep = req->dep; + struct xhci_dbc *dbc = dep->dbc; + struct xhci_hcd *xhci = dbc->xhci; + struct device *dev = xhci_to_hcd(dbc->xhci)->self.sysdev; + + list_del_init(&req->list_pending); + req->trb_dma = 0; + req->trb = NULL; + + if (req->status == -EINPROGRESS) + req->status = status; + + trace_xhci_dbc_giveback_request(req); + + dma_unmap_single(dev, + req->dma, + req->length, + dbc_ep_dma_direction(dep)); + + /* Give back the transfer request: */ + spin_unlock(&dbc->lock); + req->complete(xhci, req); + spin_lock(&dbc->lock); +} + +static void xhci_dbc_flush_single_request(struct dbc_request *req) +{ + union xhci_trb *trb = req->trb; + + trb->generic.field[0] = 0; + trb->generic.field[1] = 0; + trb->generic.field[2] = 0; + trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE); + trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(TRB_TR_NOOP)); + + xhci_dbc_giveback(req, -ESHUTDOWN); +} + +static void xhci_dbc_flush_endpoint_requests(struct dbc_ep *dep) +{ + struct dbc_request *req, *tmp; + + list_for_each_entry_safe(req, tmp, &dep->list_pending, list_pending) + xhci_dbc_flush_single_request(req); +} + +static void xhci_dbc_flush_reqests(struct xhci_dbc *dbc) +{ + xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_OUT]); + xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_IN]); +} + +struct dbc_request * +dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags) +{ + struct dbc_request *req; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + req->dep = dep; + INIT_LIST_HEAD(&req->list_pending); + INIT_LIST_HEAD(&req->list_pool); + req->direction = dep->direction; + + trace_xhci_dbc_alloc_request(req); + + return req; +} + +void +dbc_free_request(struct dbc_ep *dep, struct dbc_request *req) +{ + trace_xhci_dbc_free_request(req); + + kfree(req); +} + +static void +xhci_dbc_queue_trb(struct xhci_ring *ring, u32 field1, + u32 field2, u32 field3, u32 field4) +{ + union xhci_trb *trb, *next; + + trb = ring->enqueue; + trb->generic.field[0] = cpu_to_le32(field1); + trb->generic.field[1] = cpu_to_le32(field2); + trb->generic.field[2] = cpu_to_le32(field3); + trb->generic.field[3] = cpu_to_le32(field4); + + trace_xhci_dbc_gadget_ep_queue(ring, &trb->generic); + + ring->num_trbs_free--; + next = ++(ring->enqueue); + if (TRB_TYPE_LINK_LE32(next->link.control)) { + next->link.control ^= cpu_to_le32(TRB_CYCLE); + ring->enqueue = ring->enq_seg->trbs; + ring->cycle_state ^= 1; + } +} + +static int xhci_dbc_queue_bulk_tx(struct dbc_ep *dep, + struct dbc_request *req) +{ + u64 addr; + union xhci_trb *trb; + unsigned int num_trbs; + struct xhci_dbc *dbc = dep->dbc; + struct xhci_ring *ring = dep->ring; + u32 length, control, cycle; + + num_trbs = count_trbs(req->dma, req->length); + WARN_ON(num_trbs != 1); + if (ring->num_trbs_free < num_trbs) + return -EBUSY; + + addr = req->dma; + trb = ring->enqueue; + cycle = ring->cycle_state; + length = TRB_LEN(req->length); + control = TRB_TYPE(TRB_NORMAL) | TRB_IOC; + + if (cycle) + control &= cpu_to_le32(~TRB_CYCLE); + else + control |= cpu_to_le32(TRB_CYCLE); + + req->trb = ring->enqueue; + req->trb_dma = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); + xhci_dbc_queue_trb(ring, + lower_32_bits(addr), + upper_32_bits(addr), + length, control); + + /* + * Add a barrier between writes of trb fields and flipping + * the cycle bit: + */ + wmb(); + + if (cycle) + trb->generic.field[3] |= cpu_to_le32(TRB_CYCLE); + else + trb->generic.field[3] &= cpu_to_le32(~TRB_CYCLE); + + writel(DBC_DOOR_BELL_TARGET(dep->direction), &dbc->regs->doorbell); + + return 0; +} + +static int +dbc_ep_do_queue(struct dbc_ep *dep, struct dbc_request *req) +{ + int ret; + struct device *dev; + struct xhci_dbc *dbc = dep->dbc; + struct xhci_hcd *xhci = dbc->xhci; + + dev = xhci_to_hcd(xhci)->self.sysdev; + + if (!req->length || !req->buf) + return -EINVAL; + + req->actual = 0; + req->status = -EINPROGRESS; + + req->dma = dma_map_single(dev, + req->buf, + req->length, + dbc_ep_dma_direction(dep)); + if (dma_mapping_error(dev, req->dma)) { + xhci_err(xhci, "failed to map buffer\n"); + return -EFAULT; + } + + ret = xhci_dbc_queue_bulk_tx(dep, req); + if (ret) { + xhci_err(xhci, "failed to queue trbs\n"); + dma_unmap_single(dev, + req->dma, + req->length, + dbc_ep_dma_direction(dep)); + return -EFAULT; + } + + list_add_tail(&req->list_pending, &dep->list_pending); + + return 0; +} + +int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req, + gfp_t gfp_flags) +{ + struct xhci_dbc *dbc = dep->dbc; + int ret = -ESHUTDOWN; + + spin_lock(&dbc->lock); + if (dbc->state == DS_CONFIGURED) + ret = dbc_ep_do_queue(dep, req); + spin_unlock(&dbc->lock); + + mod_delayed_work(system_wq, &dbc->event_work, 0); + + trace_xhci_dbc_queue_request(req); + + return ret; +} + +static inline void xhci_dbc_do_eps_init(struct xhci_hcd *xhci, bool direction) +{ + struct dbc_ep *dep; + struct xhci_dbc *dbc = xhci->dbc; + + dep = &dbc->eps[direction]; + dep->dbc = dbc; + dep->direction = direction; + dep->ring = direction ? dbc->ring_in : dbc->ring_out; + + INIT_LIST_HEAD(&dep->list_pending); +} + +static void xhci_dbc_eps_init(struct xhci_hcd *xhci) +{ + xhci_dbc_do_eps_init(xhci, BULK_OUT); + xhci_dbc_do_eps_init(xhci, BULK_IN); +} + +static void xhci_dbc_eps_exit(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + memset(dbc->eps, 0, sizeof(struct dbc_ep) * ARRAY_SIZE(dbc->eps)); +} + +static int xhci_dbc_mem_init(struct xhci_hcd *xhci, gfp_t flags) +{ + int ret; + dma_addr_t deq; + u32 string_length; + struct xhci_dbc *dbc = xhci->dbc; + + /* Allocate various rings for events and transfers: */ + dbc->ring_evt = xhci_ring_alloc(xhci, 1, 1, TYPE_EVENT, 0, flags); + if (!dbc->ring_evt) + goto evt_fail; + + dbc->ring_in = xhci_ring_alloc(xhci, 1, 1, TYPE_BULK, 0, flags); + if (!dbc->ring_in) + goto in_fail; + + dbc->ring_out = xhci_ring_alloc(xhci, 1, 1, TYPE_BULK, 0, flags); + if (!dbc->ring_out) + goto out_fail; + + /* Allocate and populate ERST: */ + ret = xhci_alloc_erst(xhci, dbc->ring_evt, &dbc->erst, flags); + if (ret) + goto erst_fail; + + /* Allocate context data structure: */ + dbc->ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags); + if (!dbc->ctx) + goto ctx_fail; + + /* Allocate the string table: */ + dbc->string_size = sizeof(struct dbc_str_descs); + dbc->string = dbc_dma_alloc_coherent(xhci, + dbc->string_size, + &dbc->string_dma, + flags); + if (!dbc->string) + goto string_fail; + + /* Setup ERST register: */ + writel(dbc->erst.erst_size, &dbc->regs->ersts); + xhci_write_64(xhci, dbc->erst.erst_dma_addr, &dbc->regs->erstba); + deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg, + dbc->ring_evt->dequeue); + xhci_write_64(xhci, deq, &dbc->regs->erdp); + + /* Setup strings and contexts: */ + string_length = xhci_dbc_populate_strings(dbc->string); + xhci_dbc_init_contexts(xhci, string_length); + + mmiowb(); + + xhci_dbc_eps_init(xhci); + dbc->state = DS_INITIALIZED; + + return 0; + +string_fail: + xhci_free_container_ctx(xhci, dbc->ctx); + dbc->ctx = NULL; +ctx_fail: + xhci_free_erst(xhci, &dbc->erst); +erst_fail: + xhci_ring_free(xhci, dbc->ring_out); + dbc->ring_out = NULL; +out_fail: + xhci_ring_free(xhci, dbc->ring_in); + dbc->ring_in = NULL; +in_fail: + xhci_ring_free(xhci, dbc->ring_evt); + dbc->ring_evt = NULL; +evt_fail: + return -ENOMEM; +} + +static void xhci_dbc_mem_cleanup(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + if (!dbc) + return; + + xhci_dbc_eps_exit(xhci); + + if (dbc->string) { + dbc_dma_free_coherent(xhci, + dbc->string_size, + dbc->string, dbc->string_dma); + dbc->string = NULL; + } + + xhci_free_container_ctx(xhci, dbc->ctx); + dbc->ctx = NULL; + + xhci_free_erst(xhci, &dbc->erst); + xhci_ring_free(xhci, dbc->ring_out); + xhci_ring_free(xhci, dbc->ring_in); + xhci_ring_free(xhci, dbc->ring_evt); + dbc->ring_in = NULL; + dbc->ring_out = NULL; + dbc->ring_evt = NULL; +} + +static int xhci_do_dbc_start(struct xhci_hcd *xhci) +{ + int ret; + u32 ctrl; + struct xhci_dbc *dbc = xhci->dbc; + + if (dbc->state != DS_DISABLED) + return -EINVAL; + + writel(0, &dbc->regs->control); + ret = xhci_handshake(&dbc->regs->control, + DBC_CTRL_DBC_ENABLE, + 0, 1000); + if (ret) + return ret; + + ret = xhci_dbc_mem_init(xhci, GFP_ATOMIC); + if (ret) + return ret; + + ctrl = readl(&dbc->regs->control); + writel(ctrl | DBC_CTRL_DBC_ENABLE | DBC_CTRL_PORT_ENABLE, + &dbc->regs->control); + ret = xhci_handshake(&dbc->regs->control, + DBC_CTRL_DBC_ENABLE, + DBC_CTRL_DBC_ENABLE, 1000); + if (ret) + return ret; + + dbc->state = DS_ENABLED; + + return 0; +} + +static void xhci_do_dbc_stop(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + if (dbc->state == DS_DISABLED) + return; + + writel(0, &dbc->regs->control); + xhci_dbc_mem_cleanup(xhci); + dbc->state = DS_DISABLED; +} + +static int xhci_dbc_start(struct xhci_hcd *xhci) +{ + int ret; + struct xhci_dbc *dbc = xhci->dbc; + + WARN_ON(!dbc); + + pm_runtime_get_sync(xhci_to_hcd(xhci)->self.controller); + + spin_lock(&dbc->lock); + ret = xhci_do_dbc_start(xhci); + spin_unlock(&dbc->lock); + + if (ret) { + pm_runtime_put(xhci_to_hcd(xhci)->self.controller); + return ret; + } + + return mod_delayed_work(system_wq, &dbc->event_work, 1); +} + +static void xhci_dbc_stop(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + struct dbc_port *port = &dbc->port; + + WARN_ON(!dbc); + + cancel_delayed_work_sync(&dbc->event_work); + + if (port->registered) + xhci_dbc_tty_unregister_device(xhci); + + spin_lock(&dbc->lock); + xhci_do_dbc_stop(xhci); + spin_unlock(&dbc->lock); + + pm_runtime_put_sync(xhci_to_hcd(xhci)->self.controller); +} + +static void +dbc_handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) +{ + u32 portsc; + struct xhci_dbc *dbc = xhci->dbc; + + portsc = readl(&dbc->regs->portsc); + if (portsc & DBC_PORTSC_CONN_CHANGE) + xhci_info(xhci, "DbC port connect change\n"); + + if (portsc & DBC_PORTSC_RESET_CHANGE) + xhci_info(xhci, "DbC port reset change\n"); + + if (portsc & DBC_PORTSC_LINK_CHANGE) + xhci_info(xhci, "DbC port link status change\n"); + + if (portsc & DBC_PORTSC_CONFIG_CHANGE) + xhci_info(xhci, "DbC config error change\n"); + + /* Port reset change bit will be cleared in other place: */ + writel(portsc & ~DBC_PORTSC_RESET_CHANGE, &dbc->regs->portsc); +} + +static void dbc_handle_xfer_event(struct xhci_hcd *xhci, union xhci_trb *event) +{ + struct dbc_ep *dep; + struct xhci_ring *ring; + int ep_id; + int status; + u32 comp_code; + size_t remain_length; + struct dbc_request *req = NULL, *r; + + comp_code = GET_COMP_CODE(le32_to_cpu(event->generic.field[2])); + remain_length = EVENT_TRB_LEN(le32_to_cpu(event->generic.field[2])); + ep_id = TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3])); + dep = (ep_id == EPID_OUT) ? + get_out_ep(xhci) : get_in_ep(xhci); + ring = dep->ring; + + switch (comp_code) { + case COMP_SUCCESS: + remain_length = 0; + /* FALLTHROUGH */ + case COMP_SHORT_PACKET: + status = 0; + break; + case COMP_TRB_ERROR: + case COMP_BABBLE_DETECTED_ERROR: + case COMP_USB_TRANSACTION_ERROR: + case COMP_STALL_ERROR: + xhci_warn(xhci, "tx error %d detected\n", comp_code); + status = -comp_code; + break; + default: + xhci_err(xhci, "unknown tx error %d\n", comp_code); + status = -comp_code; + break; + } + + /* Match the pending request: */ + list_for_each_entry(r, &dep->list_pending, list_pending) { + if (r->trb_dma == event->trans_event.buffer) { + req = r; + break; + } + } + + if (!req) { + xhci_warn(xhci, "no matched request\n"); + return; + } + + trace_xhci_dbc_handle_transfer(ring, &req->trb->generic); + + ring->num_trbs_free++; + req->actual = req->length - remain_length; + xhci_dbc_giveback(req, status); +} + +static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) +{ + dma_addr_t deq; + struct dbc_ep *dep; + union xhci_trb *evt; + u32 ctrl, portsc; + struct xhci_hcd *xhci = dbc->xhci; + bool update_erdp = false; + + /* DbC state machine: */ + switch (dbc->state) { + case DS_DISABLED: + case DS_INITIALIZED: + + return EVT_ERR; + case DS_ENABLED: + portsc = readl(&dbc->regs->portsc); + if (portsc & DBC_PORTSC_CONN_STATUS) { + dbc->state = DS_CONNECTED; + xhci_info(xhci, "DbC connected\n"); + } + + return EVT_DONE; + case DS_CONNECTED: + ctrl = readl(&dbc->regs->control); + if (ctrl & DBC_CTRL_DBC_RUN) { + dbc->state = DS_CONFIGURED; + xhci_info(xhci, "DbC configured\n"); + portsc = readl(&dbc->regs->portsc); + writel(portsc, &dbc->regs->portsc); + return EVT_GSER; + } + + return EVT_DONE; + case DS_CONFIGURED: + /* Handle cable unplug event: */ + portsc = readl(&dbc->regs->portsc); + if (!(portsc & DBC_PORTSC_PORT_ENABLED) && + !(portsc & DBC_PORTSC_CONN_STATUS)) { + xhci_info(xhci, "DbC cable unplugged\n"); + dbc->state = DS_ENABLED; + xhci_dbc_flush_reqests(dbc); + + return EVT_DISC; + } + + /* Handle debug port reset event: */ + if (portsc & DBC_PORTSC_RESET_CHANGE) { + xhci_info(xhci, "DbC port reset\n"); + writel(portsc, &dbc->regs->portsc); + dbc->state = DS_ENABLED; + xhci_dbc_flush_reqests(dbc); + + return EVT_DISC; + } + + /* Handle endpoint stall event: */ + ctrl = readl(&dbc->regs->control); + if ((ctrl & DBC_CTRL_HALT_IN_TR) || + (ctrl & DBC_CTRL_HALT_OUT_TR)) { + xhci_info(xhci, "DbC Endpoint stall\n"); + dbc->state = DS_STALLED; + + if (ctrl & DBC_CTRL_HALT_IN_TR) { + dep = get_in_ep(xhci); + xhci_dbc_flush_endpoint_requests(dep); + } + + if (ctrl & DBC_CTRL_HALT_OUT_TR) { + dep = get_out_ep(xhci); + xhci_dbc_flush_endpoint_requests(dep); + } + + return EVT_DONE; + } + + /* Clear DbC run change bit: */ + if (ctrl & DBC_CTRL_DBC_RUN_CHANGE) { + writel(ctrl, &dbc->regs->control); + ctrl = readl(&dbc->regs->control); + } + + break; + case DS_STALLED: + ctrl = readl(&dbc->regs->control); + if (!(ctrl & DBC_CTRL_HALT_IN_TR) && + !(ctrl & DBC_CTRL_HALT_OUT_TR) && + (ctrl & DBC_CTRL_DBC_RUN)) { + dbc->state = DS_CONFIGURED; + break; + } + + return EVT_DONE; + default: + xhci_err(xhci, "Unknown DbC state %d\n", dbc->state); + break; + } + + /* Handle the events in the event ring: */ + evt = dbc->ring_evt->dequeue; + while ((le32_to_cpu(evt->event_cmd.flags) & TRB_CYCLE) == + dbc->ring_evt->cycle_state) { + /* + * Add a barrier between reading the cycle flag and any + * reads of the event's flags/data below: + */ + rmb(); + + trace_xhci_dbc_handle_event(dbc->ring_evt, &evt->generic); + + switch (le32_to_cpu(evt->event_cmd.flags) & TRB_TYPE_BITMASK) { + case TRB_TYPE(TRB_PORT_STATUS): + dbc_handle_port_status(xhci, evt); + break; + case TRB_TYPE(TRB_TRANSFER): + dbc_handle_xfer_event(xhci, evt); + break; + default: + break; + } + + inc_deq(xhci, dbc->ring_evt); + evt = dbc->ring_evt->dequeue; + update_erdp = true; + } + + /* Update event ring dequeue pointer: */ + if (update_erdp) { + deq = xhci_trb_virt_to_dma(dbc->ring_evt->deq_seg, + dbc->ring_evt->dequeue); + xhci_write_64(xhci, deq, &dbc->regs->erdp); + } + + return EVT_DONE; +} + +static void xhci_dbc_handle_events(struct work_struct *work) +{ + int ret; + enum evtreturn evtr; + struct xhci_dbc *dbc; + struct xhci_hcd *xhci; + + dbc = container_of(to_delayed_work(work), struct xhci_dbc, event_work); + xhci = dbc->xhci; + + spin_lock(&dbc->lock); + evtr = xhci_dbc_do_handle_events(dbc); + spin_unlock(&dbc->lock); + + switch (evtr) { + case EVT_GSER: + ret = xhci_dbc_tty_register_device(xhci); + if (ret) { + xhci_err(xhci, "failed to alloc tty device\n"); + break; + } + + xhci_info(xhci, "DbC now attached to /dev/ttyDBC0\n"); + break; + case EVT_DISC: + xhci_dbc_tty_unregister_device(xhci); + break; + case EVT_DONE: + break; + default: + xhci_info(xhci, "stop handling dbc events\n"); + return; + } + + mod_delayed_work(system_wq, &dbc->event_work, 1); +} + +static void xhci_do_dbc_exit(struct xhci_hcd *xhci) +{ + unsigned long flags; + + spin_lock_irqsave(&xhci->lock, flags); + kfree(xhci->dbc); + xhci->dbc = NULL; + spin_unlock_irqrestore(&xhci->lock, flags); +} + +static int xhci_do_dbc_init(struct xhci_hcd *xhci) +{ + u32 reg; + struct xhci_dbc *dbc; + unsigned long flags; + void __iomem *base; + int dbc_cap_offs; + + base = &xhci->cap_regs->hc_capbase; + dbc_cap_offs = xhci_find_next_ext_cap(base, 0, XHCI_EXT_CAPS_DEBUG); + if (!dbc_cap_offs) + return -ENODEV; + + dbc = kzalloc(sizeof(*dbc), GFP_KERNEL); + if (!dbc) + return -ENOMEM; + + dbc->regs = base + dbc_cap_offs; + + /* We will avoid using DbC in xhci driver if it's in use. */ + reg = readl(&dbc->regs->control); + if (reg & DBC_CTRL_DBC_ENABLE) { + kfree(dbc); + return -EBUSY; + } + + spin_lock_irqsave(&xhci->lock, flags); + if (xhci->dbc) { + spin_unlock_irqrestore(&xhci->lock, flags); + kfree(dbc); + return -EBUSY; + } + xhci->dbc = dbc; + spin_unlock_irqrestore(&xhci->lock, flags); + + dbc->xhci = xhci; + INIT_DELAYED_WORK(&dbc->event_work, xhci_dbc_handle_events); + spin_lock_init(&dbc->lock); + + return 0; +} + +static ssize_t dbc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const char *p; + struct xhci_dbc *dbc; + struct xhci_hcd *xhci; + + xhci = hcd_to_xhci(dev_get_drvdata(dev)); + dbc = xhci->dbc; + + switch (dbc->state) { + case DS_DISABLED: + p = "disabled"; + break; + case DS_INITIALIZED: + p = "initialized"; + break; + case DS_ENABLED: + p = "enabled"; + break; + case DS_CONNECTED: + p = "connected"; + break; + case DS_CONFIGURED: + p = "configured"; + break; + case DS_STALLED: + p = "stalled"; + break; + default: + p = "unknown"; + } + + return sprintf(buf, "%s\n", p); +} + +static ssize_t dbc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct xhci_dbc *dbc; + struct xhci_hcd *xhci; + + xhci = hcd_to_xhci(dev_get_drvdata(dev)); + dbc = xhci->dbc; + + if (!strncmp(buf, "enable", 6)) + xhci_dbc_start(xhci); + else if (!strncmp(buf, "disable", 7)) + xhci_dbc_stop(xhci); + else + return -EINVAL; + + return count; +} + +static DEVICE_ATTR_RW(dbc); + +int xhci_dbc_init(struct xhci_hcd *xhci) +{ + int ret; + struct device *dev = xhci_to_hcd(xhci)->self.controller; + + ret = xhci_do_dbc_init(xhci); + if (ret) + goto init_err3; + + ret = xhci_dbc_tty_register_driver(xhci); + if (ret) + goto init_err2; + + ret = device_create_file(dev, &dev_attr_dbc); + if (ret) + goto init_err1; + + return 0; + +init_err1: + xhci_dbc_tty_unregister_driver(); +init_err2: + xhci_do_dbc_exit(xhci); +init_err3: + return ret; +} + +void xhci_dbc_exit(struct xhci_hcd *xhci) +{ + struct device *dev = xhci_to_hcd(xhci)->self.controller; + + if (!xhci->dbc) + return; + + device_remove_file(dev, &dev_attr_dbc); + xhci_dbc_tty_unregister_driver(); + xhci_dbc_stop(xhci); + xhci_do_dbc_exit(xhci); +} + +#ifdef CONFIG_PM +int xhci_dbc_suspend(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + if (!dbc) + return 0; + + if (dbc->state == DS_CONFIGURED) + dbc->resume_required = 1; + + xhci_dbc_stop(xhci); + + return 0; +} + +int xhci_dbc_resume(struct xhci_hcd *xhci) +{ + int ret = 0; + struct xhci_dbc *dbc = xhci->dbc; + + if (!dbc) + return 0; + + if (dbc->resume_required) { + dbc->resume_required = 0; + xhci_dbc_start(xhci); + } + + return ret; +} +#endif /* CONFIG_PM */ diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h new file mode 100644 index 000000000000..e66ea0748ba3 --- /dev/null +++ b/drivers/usb/host/xhci-dbgcap.h @@ -0,0 +1,229 @@ + +/** + * xhci-dbgcap.h - xHCI debug capability support + * + * Copyright (C) 2017 Intel Corporation + * + * Author: Lu Baolu <baolu.lu@linux.intel.com> + */ +#ifndef __LINUX_XHCI_DBGCAP_H +#define __LINUX_XHCI_DBGCAP_H + +#include <linux/tty.h> +#include <linux/kfifo.h> + +struct dbc_regs { + __le32 capability; + __le32 doorbell; + __le32 ersts; /* Event Ring Segment Table Size*/ + __le32 __reserved_0; /* 0c~0f reserved bits */ + __le64 erstba; /* Event Ring Segment Table Base Address */ + __le64 erdp; /* Event Ring Dequeue Pointer */ + __le32 control; + __le32 status; + __le32 portsc; /* Port status and control */ + __le32 __reserved_1; /* 2b~28 reserved bits */ + __le64 dccp; /* Debug Capability Context Pointer */ + __le32 devinfo1; /* Device Descriptor Info Register 1 */ + __le32 devinfo2; /* Device Descriptor Info Register 2 */ +}; + +struct dbc_info_context { + __le64 string0; + __le64 manufacturer; + __le64 product; + __le64 serial; + __le32 length; + __le32 __reserved_0[7]; +}; + +#define DBC_CTRL_DBC_RUN BIT(0) +#define DBC_CTRL_PORT_ENABLE BIT(1) +#define DBC_CTRL_HALT_OUT_TR BIT(2) +#define DBC_CTRL_HALT_IN_TR BIT(3) +#define DBC_CTRL_DBC_RUN_CHANGE BIT(4) +#define DBC_CTRL_DBC_ENABLE BIT(31) +#define DBC_CTRL_MAXBURST(p) (((p) >> 16) & 0xff) +#define DBC_DOOR_BELL_TARGET(p) (((p) & 0xff) << 8) + +#define DBC_MAX_PACKET 1024 +#define DBC_MAX_STRING_LENGTH 64 +#define DBC_STRING_MANUFACTURER "Linux Foundation" +#define DBC_STRING_PRODUCT "Linux USB Debug Target" +#define DBC_STRING_SERIAL "0001" +#define DBC_CONTEXT_SIZE 64 + +/* + * Port status: + */ +#define DBC_PORTSC_CONN_STATUS BIT(0) +#define DBC_PORTSC_PORT_ENABLED BIT(1) +#define DBC_PORTSC_CONN_CHANGE BIT(17) +#define DBC_PORTSC_RESET_CHANGE BIT(21) +#define DBC_PORTSC_LINK_CHANGE BIT(22) +#define DBC_PORTSC_CONFIG_CHANGE BIT(23) + +struct dbc_str_descs { + char string0[DBC_MAX_STRING_LENGTH]; + char manufacturer[DBC_MAX_STRING_LENGTH]; + char product[DBC_MAX_STRING_LENGTH]; + char serial[DBC_MAX_STRING_LENGTH]; +}; + +#define DBC_PROTOCOL 1 /* GNU Remote Debug Command */ +#define DBC_VENDOR_ID 0x1d6b /* Linux Foundation 0x1d6b */ +#define DBC_PRODUCT_ID 0x0010 /* device 0010 */ +#define DBC_DEVICE_REV 0x0010 /* 0.10 */ + +enum dbc_state { + DS_DISABLED = 0, + DS_INITIALIZED, + DS_ENABLED, + DS_CONNECTED, + DS_CONFIGURED, + DS_STALLED, +}; + +struct dbc_request { + void *buf; + unsigned int length; + dma_addr_t dma; + void (*complete)(struct xhci_hcd *xhci, + struct dbc_request *req); + struct list_head list_pool; + int status; + unsigned int actual; + + struct dbc_ep *dep; + struct list_head list_pending; + dma_addr_t trb_dma; + union xhci_trb *trb; + unsigned direction:1; +}; + +struct dbc_ep { + struct xhci_dbc *dbc; + struct list_head list_pending; + struct xhci_ring *ring; + unsigned direction:1; +}; + +#define DBC_QUEUE_SIZE 16 +#define DBC_WRITE_BUF_SIZE 8192 + +/* + * Private structure for DbC hardware state: + */ +struct dbc_port { + struct tty_port port; + spinlock_t port_lock; /* port access */ + + struct list_head read_pool; + struct list_head read_queue; + unsigned int n_read; + struct tasklet_struct push; + + struct list_head write_pool; + struct kfifo write_fifo; + + bool registered; + struct dbc_ep *in; + struct dbc_ep *out; +}; + +struct xhci_dbc { + spinlock_t lock; /* device access */ + struct xhci_hcd *xhci; + struct dbc_regs __iomem *regs; + struct xhci_ring *ring_evt; + struct xhci_ring *ring_in; + struct xhci_ring *ring_out; + struct xhci_erst erst; + struct xhci_container_ctx *ctx; + + struct dbc_str_descs *string; + dma_addr_t string_dma; + size_t string_size; + + enum dbc_state state; + struct delayed_work event_work; + unsigned resume_required:1; + struct dbc_ep eps[2]; + + struct dbc_port port; +}; + +#define dbc_bulkout_ctx(d) \ + ((struct xhci_ep_ctx *)((d)->ctx->bytes + DBC_CONTEXT_SIZE)) +#define dbc_bulkin_ctx(d) \ + ((struct xhci_ep_ctx *)((d)->ctx->bytes + DBC_CONTEXT_SIZE * 2)) +#define dbc_bulkout_enq(d) \ + xhci_trb_virt_to_dma((d)->ring_out->enq_seg, (d)->ring_out->enqueue) +#define dbc_bulkin_enq(d) \ + xhci_trb_virt_to_dma((d)->ring_in->enq_seg, (d)->ring_in->enqueue) +#define dbc_epctx_info2(t, p, b) \ + cpu_to_le32(EP_TYPE(t) | MAX_PACKET(p) | MAX_BURST(b)) +#define dbc_ep_dma_direction(d) \ + ((d)->direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE) + +#define BULK_OUT 0 +#define BULK_IN 1 +#define EPID_OUT 2 +#define EPID_IN 3 + +enum evtreturn { + EVT_ERR = -1, + EVT_DONE, + EVT_GSER, + EVT_DISC, +}; + +static inline struct dbc_ep *get_in_ep(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + return &dbc->eps[BULK_IN]; +} + +static inline struct dbc_ep *get_out_ep(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + + return &dbc->eps[BULK_OUT]; +} + +#ifdef CONFIG_USB_XHCI_DBGCAP +int xhci_dbc_init(struct xhci_hcd *xhci); +void xhci_dbc_exit(struct xhci_hcd *xhci); +int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci); +void xhci_dbc_tty_unregister_driver(void); +int xhci_dbc_tty_register_device(struct xhci_hcd *xhci); +void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci); +struct dbc_request *dbc_alloc_request(struct dbc_ep *dep, gfp_t gfp_flags); +void dbc_free_request(struct dbc_ep *dep, struct dbc_request *req); +int dbc_ep_queue(struct dbc_ep *dep, struct dbc_request *req, gfp_t gfp_flags); +#ifdef CONFIG_PM +int xhci_dbc_suspend(struct xhci_hcd *xhci); +int xhci_dbc_resume(struct xhci_hcd *xhci); +#endif /* CONFIG_PM */ +#else +static inline int xhci_dbc_init(struct xhci_hcd *xhci) +{ + return 0; +} + +static inline void xhci_dbc_exit(struct xhci_hcd *xhci) +{ +} + +static inline int xhci_dbc_suspend(struct xhci_hcd *xhci) +{ + return 0; +} + +static inline int xhci_dbc_resume(struct xhci_hcd *xhci) +{ + return 0; +} +#endif /* CONFIG_USB_XHCI_DBGCAP */ +#endif /* __LINUX_XHCI_DBGCAP_H */ diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c new file mode 100644 index 000000000000..8d47b6fbf973 --- /dev/null +++ b/drivers/usb/host/xhci-dbgtty.c @@ -0,0 +1,497 @@ +/** + * xhci-dbgtty.c - tty glue for xHCI debug capability + * + * Copyright (C) 2017 Intel Corporation + * + * Author: Lu Baolu <baolu.lu@linux.intel.com> + */ + +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> + +#include "xhci.h" +#include "xhci-dbgcap.h" + +static unsigned int +dbc_send_packet(struct dbc_port *port, char *packet, unsigned int size) +{ + unsigned int len; + + len = kfifo_len(&port->write_fifo); + if (len < size) + size = len; + if (size != 0) + size = kfifo_out(&port->write_fifo, packet, size); + return size; +} + +static int dbc_start_tx(struct dbc_port *port) + __releases(&port->port_lock) + __acquires(&port->port_lock) +{ + int len; + struct dbc_request *req; + int status = 0; + bool do_tty_wake = false; + struct list_head *pool = &port->write_pool; + + while (!list_empty(pool)) { + req = list_entry(pool->next, struct dbc_request, list_pool); + len = dbc_send_packet(port, req->buf, DBC_MAX_PACKET); + if (len == 0) + break; + do_tty_wake = true; + + req->length = len; + list_del(&req->list_pool); + + spin_unlock(&port->port_lock); + status = dbc_ep_queue(port->out, req, GFP_ATOMIC); + spin_lock(&port->port_lock); + + if (status) { + list_add(&req->list_pool, pool); + break; + } + } + + if (do_tty_wake && port->port.tty) + tty_wakeup(port->port.tty); + + return status; +} + +static void dbc_start_rx(struct dbc_port *port) + __releases(&port->port_lock) + __acquires(&port->port_lock) +{ + struct dbc_request *req; + int status; + struct list_head *pool = &port->read_pool; + + while (!list_empty(pool)) { + if (!port->port.tty) + break; + + req = list_entry(pool->next, struct dbc_request, list_pool); + list_del(&req->list_pool); + req->length = DBC_MAX_PACKET; + + spin_unlock(&port->port_lock); + status = dbc_ep_queue(port->in, req, GFP_ATOMIC); + spin_lock(&port->port_lock); + + if (status) { + list_add(&req->list_pool, pool); + break; + } + } +} + +static void +dbc_read_complete(struct xhci_hcd *xhci, struct dbc_request *req) +{ + struct xhci_dbc *dbc = xhci->dbc; + struct dbc_port *port = &dbc->port; + + spin_lock(&port->port_lock); + list_add_tail(&req->list_pool, &port->read_queue); + tasklet_schedule(&port->push); + spin_unlock(&port->port_lock); +} + +static void dbc_write_complete(struct xhci_hcd *xhci, struct dbc_request *req) +{ + struct xhci_dbc *dbc = xhci->dbc; + struct dbc_port *port = &dbc->port; + + spin_lock(&port->port_lock); + list_add(&req->list_pool, &port->write_pool); + switch (req->status) { + case 0: + dbc_start_tx(port); + break; + case -ESHUTDOWN: + break; + default: + xhci_warn(xhci, "unexpected write complete status %d\n", + req->status); + break; + } + spin_unlock(&port->port_lock); +} + +static void xhci_dbc_free_req(struct dbc_ep *dep, struct dbc_request *req) +{ + kfree(req->buf); + dbc_free_request(dep, req); +} + +static int +xhci_dbc_alloc_requests(struct dbc_ep *dep, struct list_head *head, + void (*fn)(struct xhci_hcd *, struct dbc_request *)) +{ + int i; + struct dbc_request *req; + + for (i = 0; i < DBC_QUEUE_SIZE; i++) { + req = dbc_alloc_request(dep, GFP_ATOMIC); + if (!req) + break; + + req->length = DBC_MAX_PACKET; + req->buf = kmalloc(req->length, GFP_KERNEL); + if (!req->buf) { + xhci_dbc_free_req(dep, req); + break; + } + + req->complete = fn; + list_add_tail(&req->list_pool, head); + } + + return list_empty(head) ? -ENOMEM : 0; +} + +static void +xhci_dbc_free_requests(struct dbc_ep *dep, struct list_head *head) +{ + struct dbc_request *req; + + while (!list_empty(head)) { + req = list_entry(head->next, struct dbc_request, list_pool); + list_del(&req->list_pool); + xhci_dbc_free_req(dep, req); + } +} + +static int dbc_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct dbc_port *port = driver->driver_state; + + tty->driver_data = port; + + return tty_port_install(&port->port, driver, tty); +} + +static int dbc_tty_open(struct tty_struct *tty, struct file *file) +{ + struct dbc_port *port = tty->driver_data; + + return tty_port_open(&port->port, tty, file); +} + +static void dbc_tty_close(struct tty_struct *tty, struct file *file) +{ + struct dbc_port *port = tty->driver_data; + + tty_port_close(&port->port, tty, file); +} + +static int dbc_tty_write(struct tty_struct *tty, + const unsigned char *buf, + int count) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + if (count) + count = kfifo_in(&port->write_fifo, buf, count); + dbc_start_tx(port); + spin_unlock_irqrestore(&port->port_lock, flags); + + return count; +} + +static int dbc_tty_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + int status; + + spin_lock_irqsave(&port->port_lock, flags); + status = kfifo_put(&port->write_fifo, ch); + spin_unlock_irqrestore(&port->port_lock, flags); + + return status; +} + +static void dbc_tty_flush_chars(struct tty_struct *tty) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + dbc_start_tx(port); + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static int dbc_tty_write_room(struct tty_struct *tty) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + int room = 0; + + spin_lock_irqsave(&port->port_lock, flags); + room = kfifo_avail(&port->write_fifo); + spin_unlock_irqrestore(&port->port_lock, flags); + + return room; +} + +static int dbc_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + int chars = 0; + + spin_lock_irqsave(&port->port_lock, flags); + chars = kfifo_len(&port->write_fifo); + spin_unlock_irqrestore(&port->port_lock, flags); + + return chars; +} + +static void dbc_tty_unthrottle(struct tty_struct *tty) +{ + struct dbc_port *port = tty->driver_data; + unsigned long flags; + + spin_lock_irqsave(&port->port_lock, flags); + tasklet_schedule(&port->push); + spin_unlock_irqrestore(&port->port_lock, flags); +} + +static const struct tty_operations dbc_tty_ops = { + .install = dbc_tty_install, + .open = dbc_tty_open, + .close = dbc_tty_close, + .write = dbc_tty_write, + .put_char = dbc_tty_put_char, + .flush_chars = dbc_tty_flush_chars, + .write_room = dbc_tty_write_room, + .chars_in_buffer = dbc_tty_chars_in_buffer, + .unthrottle = dbc_tty_unthrottle, +}; + +static struct tty_driver *dbc_tty_driver; + +int xhci_dbc_tty_register_driver(struct xhci_hcd *xhci) +{ + int status; + struct xhci_dbc *dbc = xhci->dbc; + + dbc_tty_driver = tty_alloc_driver(1, TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(dbc_tty_driver)) { + status = PTR_ERR(dbc_tty_driver); + dbc_tty_driver = NULL; + return status; + } + + dbc_tty_driver->driver_name = "dbc_serial"; + dbc_tty_driver->name = "ttyDBC"; + + dbc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + dbc_tty_driver->subtype = SERIAL_TYPE_NORMAL; + dbc_tty_driver->init_termios = tty_std_termios; + dbc_tty_driver->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + dbc_tty_driver->init_termios.c_ispeed = 9600; + dbc_tty_driver->init_termios.c_ospeed = 9600; + dbc_tty_driver->driver_state = &dbc->port; + + tty_set_operations(dbc_tty_driver, &dbc_tty_ops); + + status = tty_register_driver(dbc_tty_driver); + if (status) { + xhci_err(xhci, + "can't register dbc tty driver, err %d\n", status); + put_tty_driver(dbc_tty_driver); + dbc_tty_driver = NULL; + } + + return status; +} + +void xhci_dbc_tty_unregister_driver(void) +{ + tty_unregister_driver(dbc_tty_driver); + put_tty_driver(dbc_tty_driver); + dbc_tty_driver = NULL; +} + +static void dbc_rx_push(unsigned long _port) +{ + struct dbc_request *req; + struct tty_struct *tty; + bool do_push = false; + bool disconnect = false; + struct dbc_port *port = (void *)_port; + struct list_head *queue = &port->read_queue; + + spin_lock_irq(&port->port_lock); + tty = port->port.tty; + while (!list_empty(queue)) { + req = list_first_entry(queue, struct dbc_request, list_pool); + + if (tty && tty_throttled(tty)) + break; + + switch (req->status) { + case 0: + break; + case -ESHUTDOWN: + disconnect = true; + break; + default: + pr_warn("ttyDBC0: unexpected RX status %d\n", + req->status); + break; + } + + if (req->actual) { + char *packet = req->buf; + unsigned int n, size = req->actual; + int count; + + n = port->n_read; + if (n) { + packet += n; + size -= n; + } + + count = tty_insert_flip_string(&port->port, packet, + size); + if (count) + do_push = true; + if (count != size) { + port->n_read += count; + break; + } + port->n_read = 0; + } + + list_move(&req->list_pool, &port->read_pool); + } + + if (do_push) + tty_flip_buffer_push(&port->port); + + if (!list_empty(queue) && tty) { + if (!tty_throttled(tty)) { + if (do_push) + tasklet_schedule(&port->push); + else + pr_warn("ttyDBC0: RX not scheduled?\n"); + } + } + + if (!disconnect) + dbc_start_rx(port); + + spin_unlock_irq(&port->port_lock); +} + +static int dbc_port_activate(struct tty_port *_port, struct tty_struct *tty) +{ + struct dbc_port *port = container_of(_port, struct dbc_port, port); + + spin_lock_irq(&port->port_lock); + dbc_start_rx(port); + spin_unlock_irq(&port->port_lock); + + return 0; +} + +static const struct tty_port_operations dbc_port_ops = { + .activate = dbc_port_activate, +}; + +static void +xhci_dbc_tty_init_port(struct xhci_hcd *xhci, struct dbc_port *port) +{ + tty_port_init(&port->port); + spin_lock_init(&port->port_lock); + tasklet_init(&port->push, dbc_rx_push, (unsigned long)port); + INIT_LIST_HEAD(&port->read_pool); + INIT_LIST_HEAD(&port->read_queue); + INIT_LIST_HEAD(&port->write_pool); + + port->in = get_in_ep(xhci); + port->out = get_out_ep(xhci); + port->port.ops = &dbc_port_ops; + port->n_read = 0; +} + +static void +xhci_dbc_tty_exit_port(struct dbc_port *port) +{ + tasklet_kill(&port->push); + tty_port_destroy(&port->port); +} + +int xhci_dbc_tty_register_device(struct xhci_hcd *xhci) +{ + int ret; + struct device *tty_dev; + struct xhci_dbc *dbc = xhci->dbc; + struct dbc_port *port = &dbc->port; + + 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) + goto register_fail; + + ret = kfifo_alloc(&port->write_fifo, DBC_WRITE_BUF_SIZE, GFP_KERNEL); + if (ret) + goto buf_alloc_fail; + + ret = xhci_dbc_alloc_requests(port->in, &port->read_pool, + dbc_read_complete); + if (ret) + goto request_fail; + + ret = xhci_dbc_alloc_requests(port->out, &port->write_pool, + dbc_write_complete); + if (ret) + goto request_fail; + + port->registered = true; + + return 0; + +request_fail: + xhci_dbc_free_requests(port->in, &port->read_pool); + xhci_dbc_free_requests(port->out, &port->write_pool); + kfifo_free(&port->write_fifo); + +buf_alloc_fail: + tty_unregister_device(dbc_tty_driver, 0); + +register_fail: + xhci_dbc_tty_exit_port(port); + + xhci_err(xhci, "can't register tty port, err %d\n", ret); + + return ret; +} + +void xhci_dbc_tty_unregister_device(struct xhci_hcd *xhci) +{ + struct xhci_dbc *dbc = xhci->dbc; + struct dbc_port *port = &dbc->port; + + tty_unregister_device(dbc_tty_driver, 0); + xhci_dbc_tty_exit_port(port); + port->registered = false; + + kfifo_free(&port->write_fifo); + xhci_dbc_free_requests(get_out_ep(xhci), &port->read_pool); + xhci_dbc_free_requests(get_out_ep(xhci), &port->read_queue); + xhci_dbc_free_requests(get_in_ep(xhci), &port->write_pool); +} diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 2a90229be7a6..46d5e08f05f1 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -388,7 +388,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) trace_xhci_stop_device(virt_dev); - cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO); + cmd = xhci_alloc_command(xhci, true, GFP_NOIO); if (!cmd) return -ENOMEM; @@ -404,8 +404,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING) continue; - command = xhci_alloc_command(xhci, false, false, - GFP_NOWAIT); + command = xhci_alloc_command(xhci, false, GFP_NOWAIT); if (!command) { spin_unlock_irqrestore(&xhci->lock, flags); ret = -ENOMEM; @@ -1077,6 +1076,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, retval = -ENODEV; break; } + trace_xhci_get_port_status(wIndex, temp); status = xhci_get_port_status(hcd, bus_state, port_array, wIndex, temp, flags); if (status == 0xffffffff) @@ -1443,6 +1443,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) retval = -ENODEV; break; } + trace_xhci_hub_status_data(i, temp); + if ((temp & mask) != 0 || (bus_state->port_c_suspend & 1 << i) || (bus_state->resume_done[i] && time_after_eq( diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 3a29b32a3bd0..332420d10be9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -357,7 +357,7 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci, * Set the end flag and the cycle toggle bit on the last segment. * See section 4.9.1 and figures 15 and 16. */ -static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, unsigned int num_segs, unsigned int cycle_state, enum xhci_ring_type type, unsigned int max_packet, gfp_t flags) { @@ -454,7 +454,7 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, return 0; } -static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, +struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, int type, gfp_t flags) { struct xhci_container_ctx *ctx; @@ -479,7 +479,7 @@ static struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci return ctx; } -static void xhci_free_container_ctx(struct xhci_hcd *xhci, +void xhci_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx) { if (!ctx) @@ -650,7 +650,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, /* Allocate everything needed to free the stream rings later */ stream_info->free_streams_command = - xhci_alloc_command(xhci, true, true, mem_flags); + xhci_alloc_command_with_ctx(xhci, true, mem_flags); if (!stream_info->free_streams_command) goto cleanup_ctx; @@ -1715,8 +1715,7 @@ static void scratchpad_free(struct xhci_hcd *xhci) } struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, - bool allocate_in_ctx, bool allocate_completion, - gfp_t mem_flags) + bool allocate_completion, gfp_t mem_flags) { struct xhci_command *command; @@ -1724,21 +1723,10 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, if (!command) return NULL; - if (allocate_in_ctx) { - command->in_ctx = - xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, - mem_flags); - if (!command->in_ctx) { - kfree(command); - return NULL; - } - } - if (allocate_completion) { command->completion = kzalloc(sizeof(struct completion), mem_flags); if (!command->completion) { - xhci_free_container_ctx(xhci, command->in_ctx); kfree(command); return NULL; } @@ -1750,6 +1738,25 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, return command; } +struct xhci_command *xhci_alloc_command_with_ctx(struct xhci_hcd *xhci, + bool allocate_completion, gfp_t mem_flags) +{ + struct xhci_command *command; + + command = xhci_alloc_command(xhci, allocate_completion, mem_flags); + if (!command) + return NULL; + + command->in_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, + mem_flags); + if (!command->in_ctx) { + kfree(command->completion); + kfree(command); + return NULL; + } + return command; +} + void xhci_urb_free_priv(struct urb_priv *urb_priv) { kfree(urb_priv); @@ -1764,21 +1771,58 @@ void xhci_free_command(struct xhci_hcd *xhci, kfree(command); } +int xhci_alloc_erst(struct xhci_hcd *xhci, + struct xhci_ring *evt_ring, + struct xhci_erst *erst, + gfp_t flags) +{ + size_t size; + unsigned int val; + struct xhci_segment *seg; + struct xhci_erst_entry *entry; + + size = sizeof(struct xhci_erst_entry) * evt_ring->num_segs; + erst->entries = dma_zalloc_coherent(xhci_to_hcd(xhci)->self.sysdev, + size, &erst->erst_dma_addr, flags); + if (!erst->entries) + return -ENOMEM; + + erst->num_entries = evt_ring->num_segs; + + seg = evt_ring->first_seg; + for (val = 0; val < evt_ring->num_segs; val++) { + entry = &erst->entries[val]; + entry->seg_addr = cpu_to_le64(seg->dma); + entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); + entry->rsvd = 0; + seg = seg->next; + } + + return 0; +} + +void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) +{ + size_t size; + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + + size = sizeof(struct xhci_erst_entry) * (erst->num_entries); + if (erst->entries) + dma_free_coherent(dev, size, + erst->entries, + erst->erst_dma_addr); + erst->entries = NULL; +} + void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; - int size; int i, j, num_ports; cancel_delayed_work_sync(&xhci->cmd_timer); - /* Free the Event Ring Segment Table and the actual Event Ring */ - size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); - if (xhci->erst.entries) - dma_free_coherent(dev, size, - xhci->erst.entries, xhci->erst.erst_dma_addr); - xhci->erst.entries = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed ERST"); + xhci_free_erst(xhci, &xhci->erst); + if (xhci->event_ring) xhci_ring_free(xhci, xhci->event_ring); xhci->event_ring = NULL; @@ -2315,9 +2359,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) struct device *dev = xhci_to_hcd(xhci)->self.sysdev; unsigned int val, val2; u64 val_64; - struct xhci_segment *seg; - u32 page_size, temp; - int i; + u32 page_size, temp; + int i, ret; INIT_LIST_HEAD(&xhci->cmd_list); @@ -2421,9 +2464,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Setting command ring address to 0x%016llx", val_64); xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring); - xhci_dbg_cmd_ptrs(xhci); - xhci->lpm_command = xhci_alloc_command(xhci, true, true, flags); + xhci->lpm_command = xhci_alloc_command_with_ctx(xhci, true, flags); if (!xhci->lpm_command) goto fail; @@ -2439,8 +2481,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) "// Doorbell array is located at offset 0x%x" " from cap regs base addr", val); xhci->dba = (void __iomem *) xhci->cap_regs + val; - xhci_dbg_regs(xhci); - xhci_print_run_regs(xhci); /* Set ir_set to interrupt register set 0 */ xhci->ir_set = &xhci->run_regs->ir_set[0]; @@ -2456,32 +2496,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) if (xhci_check_trb_in_td_math(xhci) < 0) goto fail; - xhci->erst.entries = dma_alloc_coherent(dev, - sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma, - flags); - if (!xhci->erst.entries) + ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags); + if (ret) goto fail; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Allocated event ring segment table at 0x%llx", - (unsigned long long)dma); - - memset(xhci->erst.entries, 0, sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS); - xhci->erst.num_entries = ERST_NUM_SEGS; - xhci->erst.erst_dma_addr = dma; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx", - xhci->erst.num_entries, - xhci->erst.entries, - (unsigned long long)xhci->erst.erst_dma_addr); - - /* set ring base address and size for each segment table entry */ - for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) { - struct xhci_erst_entry *entry = &xhci->erst.entries[val]; - entry->seg_addr = cpu_to_le64(seg->dma); - entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); - entry->rsvd = 0; - seg = seg->next; - } /* set ERST count with the number of entries in the segment table */ val = readl(&xhci->ir_set->erst_size); @@ -2507,7 +2524,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) xhci_set_hc_event_deq(xhci); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Wrote ERST address to ir_set 0."); - xhci_print_ir_set(xhci, 0); /* * XXX: Might need to set the Interrupter Moderation Register to diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index b62a1d23244b..b0ab4d5e2751 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -57,26 +57,21 @@ /* u2_phy_pll register */ #define CTRL_U2_FORCE_PLL_STB BIT(28) -#define PERI_WK_CTRL0 0x400 -#define UWK_CTR0_0P_LS_PE BIT(8) /* posedge */ -#define UWK_CTR0_0P_LS_NE BIT(7) /* negedge for 0p linestate*/ -#define UWK_CTL1_1P_LS_C(x) (((x) & 0xf) << 1) -#define UWK_CTL1_1P_LS_E BIT(0) - -#define PERI_WK_CTRL1 0x404 -#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26) -#define UWK_CTL1_IS_E BIT(25) -#define UWK_CTL1_0P_LS_C(x) (((x) & 0xf) << 21) -#define UWK_CTL1_0P_LS_E BIT(20) -#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */ -#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */ -#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */ -#define UWK_CTL1_0P_LS_P BIT(7) -#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */ - -enum ssusb_wakeup_src { - SSUSB_WK_IP_SLEEP = 1, - SSUSB_WK_LINE_STATE = 2, +/* usb remote wakeup registers in syscon */ +/* mt8173 etc */ +#define PERI_WK_CTRL1 0x4 +#define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */ +#define WC1_IS_EN BIT(25) +#define WC1_IS_P BIT(6) /* polarity for ip sleep */ + +/* mt2712 etc */ +#define PERI_SSUSB_SPM_CTRL 0x0 +#define SSC_IP_SLEEP_EN BIT(4) +#define SSC_SPM_INT_EN BIT(1) + +enum ssusb_uwk_vers { + SSUSB_UWK_V1 = 1, + SSUSB_UWK_V2, }; static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk) @@ -296,112 +291,58 @@ static void xhci_mtk_clks_disable(struct xhci_hcd_mtk *mtk) } /* only clocks can be turn off for ip-sleep wakeup mode */ -static void usb_wakeup_ip_sleep_en(struct xhci_hcd_mtk *mtk) +static void usb_wakeup_ip_sleep_set(struct xhci_hcd_mtk *mtk, bool enable) { - u32 tmp; - struct regmap *pericfg = mtk->pericfg; - - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_IS_P; - tmp &= ~(UWK_CTL1_IS_C(0xf)); - tmp |= UWK_CTL1_IS_C(0x8); - regmap_write(pericfg, PERI_WK_CTRL1, tmp); - regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E); - - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - dev_dbg(mtk->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n", - __func__, tmp); + u32 reg, msk, val; + + switch (mtk->uwk_vers) { + case SSUSB_UWK_V1: + reg = mtk->uwk_reg_base + PERI_WK_CTRL1; + msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P; + val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0; + break; + case SSUSB_UWK_V2: + reg = mtk->uwk_reg_base + PERI_SSUSB_SPM_CTRL; + msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; + val = enable ? msk : 0; + break; + default: + return; + } + regmap_update_bits(mtk->uwk, reg, msk, val); } -static void usb_wakeup_ip_sleep_dis(struct xhci_hcd_mtk *mtk) +static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk, + struct device_node *dn) { - u32 tmp; + struct of_phandle_args args; + int ret; - regmap_read(mtk->pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_IS_E; - regmap_write(mtk->pericfg, PERI_WK_CTRL1, tmp); -} + /* Wakeup function is optional */ + mtk->uwk_en = of_property_read_bool(dn, "wakeup-source"); + if (!mtk->uwk_en) + return 0; -/* -* for line-state wakeup mode, phy's power should not power-down -* and only support cable plug in/out -*/ -static void usb_wakeup_line_state_en(struct xhci_hcd_mtk *mtk) -{ - u32 tmp; - struct regmap *pericfg = mtk->pericfg; - - /* line-state of u2-port0 */ - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_0P_LS_P; - tmp &= ~(UWK_CTL1_0P_LS_C(0xf)); - tmp |= UWK_CTL1_0P_LS_C(0x8); - regmap_write(pericfg, PERI_WK_CTRL1, tmp); - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_0P_LS_E); - - /* line-state of u2-port1 */ - regmap_read(pericfg, PERI_WK_CTRL0, &tmp); - tmp &= ~(UWK_CTL1_1P_LS_C(0xf)); - tmp |= UWK_CTL1_1P_LS_C(0x8); - regmap_write(pericfg, PERI_WK_CTRL0, tmp); - regmap_write(pericfg, PERI_WK_CTRL0, tmp | UWK_CTL1_1P_LS_E); -} + ret = of_parse_phandle_with_fixed_args(dn, + "mediatek,syscon-wakeup", 2, 0, &args); + if (ret) + return ret; -static void usb_wakeup_line_state_dis(struct xhci_hcd_mtk *mtk) -{ - u32 tmp; - struct regmap *pericfg = mtk->pericfg; - - /* line-state of u2-port0 */ - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_0P_LS_E; - regmap_write(pericfg, PERI_WK_CTRL1, tmp); - - /* line-state of u2-port1 */ - regmap_read(pericfg, PERI_WK_CTRL0, &tmp); - tmp &= ~UWK_CTL1_1P_LS_E; - regmap_write(pericfg, PERI_WK_CTRL0, tmp); -} + mtk->uwk_reg_base = args.args[0]; + mtk->uwk_vers = args.args[1]; + mtk->uwk = syscon_node_to_regmap(args.np); + of_node_put(args.np); + dev_info(mtk->dev, "uwk - reg:0x%x, version:%d\n", + mtk->uwk_reg_base, mtk->uwk_vers); -static void usb_wakeup_enable(struct xhci_hcd_mtk *mtk) -{ - if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP) - usb_wakeup_ip_sleep_en(mtk); - else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE) - usb_wakeup_line_state_en(mtk); -} + return PTR_ERR_OR_ZERO(mtk->uwk); -static void usb_wakeup_disable(struct xhci_hcd_mtk *mtk) -{ - if (mtk->wakeup_src == SSUSB_WK_IP_SLEEP) - usb_wakeup_ip_sleep_dis(mtk); - else if (mtk->wakeup_src == SSUSB_WK_LINE_STATE) - usb_wakeup_line_state_dis(mtk); } -static int usb_wakeup_of_property_parse(struct xhci_hcd_mtk *mtk, - struct device_node *dn) +static void usb_wakeup_set(struct xhci_hcd_mtk *mtk, bool enable) { - struct device *dev = mtk->dev; - - /* - * wakeup function is optional, so it is not an error if this property - * does not exist, and in such case, no need to get relative - * properties anymore. - */ - of_property_read_u32(dn, "mediatek,wakeup-src", &mtk->wakeup_src); - if (!mtk->wakeup_src) - return 0; - - mtk->pericfg = syscon_regmap_lookup_by_phandle(dn, - "mediatek,syscon-wakeup"); - if (IS_ERR(mtk->pericfg)) { - dev_err(dev, "fail to get pericfg regs\n"); - return PTR_ERR(mtk->pericfg); - } - - return 0; + if (mtk->uwk_en) + usb_wakeup_ip_sleep_set(mtk, enable); } static int xhci_mtk_setup(struct usb_hcd *hcd); @@ -583,8 +524,10 @@ static int xhci_mtk_probe(struct platform_device *pdev) &mtk->u3p_dis_msk); ret = usb_wakeup_of_property_parse(mtk, node); - if (ret) + if (ret) { + dev_err(dev, "failed to parse uwk property\n"); return ret; + } mtk->num_phys = of_count_phandle_with_args(node, "phys", "#phy-cells"); @@ -674,6 +617,15 @@ static int xhci_mtk_probe(struct platform_device *pdev) xhci = hcd_to_xhci(hcd); xhci->main_hcd = hcd; + + /* + * imod_interval is the interrupt moderation value in nanoseconds. + * The increment interval is 8 times as much as that defined in + * the xHCI spec on MTK's controller. + */ + xhci->imod_interval = 5000; + device_property_read_u32(dev, "imod-interval-ns", &xhci->imod_interval); + xhci->shared_hcd = usb_create_shared_hcd(driver, dev, dev_name(dev), hcd); if (!xhci->shared_hcd) { @@ -768,7 +720,7 @@ static int __maybe_unused xhci_mtk_suspend(struct device *dev) xhci_mtk_host_disable(mtk); xhci_mtk_phy_power_off(mtk); xhci_mtk_clks_disable(mtk); - usb_wakeup_enable(mtk); + usb_wakeup_set(mtk, true); return 0; } @@ -778,7 +730,7 @@ static int __maybe_unused xhci_mtk_resume(struct device *dev) struct usb_hcd *hcd = mtk->hcd; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - usb_wakeup_disable(mtk); + usb_wakeup_set(mtk, false); xhci_mtk_clks_enable(mtk); xhci_mtk_phy_power_on(mtk); xhci_mtk_host_enable(mtk); diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index 6b74ce5b7564..cc59d80b663b 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -122,8 +122,12 @@ struct xhci_hcd_mtk { struct regmap *pericfg; struct phy **phys; int num_phys; - int wakeup_src; bool lpm_support; + /* usb remote wakeup */ + bool uwk_en; + struct regmap *uwk; + u32 uwk_reg_base; + u32 uwk_vers; }; static inline struct xhci_hcd_mtk *hcd_to_mtk(struct usb_hcd *hcd) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 1aad89b8aba0..6c79037876db 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -237,6 +237,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (!xhci->sbrn) pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn); + /* imod_interval is the interrupt moderation value in nanoseconds. */ + xhci->imod_interval = 40000; + retval = xhci_gen_setup(hcd, xhci_pci_quirks); if (retval) return retval; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 09f164f8cf8c..6f038306c14d 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -269,6 +269,11 @@ static int xhci_plat_probe(struct platform_device *pdev) if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped")) xhci->quirks |= XHCI_BROKEN_PORT_PED; + /* imod_interval is the interrupt moderation value in nanoseconds. */ + xhci->imod_interval = 40000; + device_property_read_u32(sysdev, "imod-interval-ns", + &xhci->imod_interval); + hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0); if (IS_ERR(hcd->usb_phy)) { ret = PTR_ERR(hcd->usb_phy); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index c5cbc685c691..daa94c3aed80 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -153,7 +153,7 @@ static void next_trb(struct xhci_hcd *xhci, * See Cycle bit rules. SW is the consumer for the event ring only. * Don't make a ring full of link TRBs. That would be dumb and this would loop. */ -static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) +void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) { /* event ring doesn't have link trbs, check for last trb */ if (ring->type == TYPE_EVENT) { @@ -1141,7 +1141,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, if (xhci->quirks & XHCI_RESET_EP_QUIRK) { struct xhci_command *command; - command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) return; @@ -1821,7 +1821,7 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci, { struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; struct xhci_command *command; - command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) return; @@ -1878,12 +1878,10 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code) static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_ring *ep_ring, int *status) { - struct urb_priv *urb_priv; struct urb *urb = NULL; /* Clean up the endpoint's TD list */ urb = td->urb; - urb_priv = urb->hcpriv; /* if a bounce buffer was used to align this td then unmap it */ xhci_unmap_td_bounce_buffer(xhci, ep_ring, td); @@ -1994,7 +1992,6 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_virt_ep *ep, int *status) { struct xhci_virt_device *xdev; - struct xhci_ring *ep_ring; unsigned int slot_id; int ep_index; struct xhci_ep_ctx *ep_ctx; @@ -2006,7 +2003,6 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); xdev = xhci->devs[slot_id]; ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; - ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); requested = td->urb->transfer_buffer_length; @@ -2965,7 +2961,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, return 0; } -static unsigned int count_trbs(u64 addr, u64 len) +unsigned int count_trbs(u64 addr, u64 len) { unsigned int num_trbs; @@ -4044,7 +4040,7 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, } /* This function gets called from contexts where it cannot sleep */ - cmd = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!cmd) return; diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index a3b57c781db1..410544ffe78f 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -23,6 +23,7 @@ #include <linux/tracepoint.h> #include "xhci.h" +#include "xhci-dbgcap.h" #define XHCI_MSG_MAX 500 @@ -155,6 +156,21 @@ DEFINE_EVENT(xhci_log_trb, xhci_queue_trb, TP_ARGS(ring, trb) ); +DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_event, + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), + TP_ARGS(ring, trb) +); + +DEFINE_EVENT(xhci_log_trb, xhci_dbc_handle_transfer, + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), + TP_ARGS(ring, trb) +); + +DEFINE_EVENT(xhci_log_trb, xhci_dbc_gadget_ep_queue, + TP_PROTO(struct xhci_ring *ring, struct xhci_generic_trb *trb), + TP_ARGS(ring, trb) +); + DECLARE_EVENT_CLASS(xhci_log_virt_dev, TP_PROTO(struct xhci_virt_device *vdev), TP_ARGS(vdev), @@ -478,6 +494,59 @@ DEFINE_EVENT(xhci_log_portsc, xhci_handle_port_status, TP_ARGS(portnum, portsc) ); +DEFINE_EVENT(xhci_log_portsc, xhci_get_port_status, + TP_PROTO(u32 portnum, u32 portsc), + TP_ARGS(portnum, portsc) +); + +DEFINE_EVENT(xhci_log_portsc, xhci_hub_status_data, + TP_PROTO(u32 portnum, u32 portsc), + TP_ARGS(portnum, portsc) +); + +DECLARE_EVENT_CLASS(xhci_dbc_log_request, + TP_PROTO(struct dbc_request *req), + TP_ARGS(req), + TP_STRUCT__entry( + __field(struct dbc_request *, req) + __field(bool, dir) + __field(unsigned int, actual) + __field(unsigned int, length) + __field(int, status) + ), + TP_fast_assign( + __entry->req = req; + __entry->dir = req->direction; + __entry->actual = req->actual; + __entry->length = req->length; + __entry->status = req->status; + ), + TP_printk("%s: req %p length %u/%u ==> %d", + __entry->dir ? "bulk-in" : "bulk-out", + __entry->req, __entry->actual, + __entry->length, __entry->status + ) +); + +DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_alloc_request, + TP_PROTO(struct dbc_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_free_request, + TP_PROTO(struct dbc_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_queue_request, + TP_PROTO(struct dbc_request *req), + TP_ARGS(req) +); + +DEFINE_EVENT(xhci_dbc_log_request, xhci_dbc_giveback_request, + TP_PROTO(struct dbc_request *req), + TP_ARGS(req) +); #endif /* __XHCI_TRACE_H */ /* this part must be outside header guard */ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index da6dbe3ebd8b..1eeb3396300f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -21,6 +21,7 @@ #include "xhci-trace.h" #include "xhci-mtk.h" #include "xhci-debugfs.h" +#include "xhci-dbgcap.h" #define DRIVER_AUTHOR "Sarah Sharp" #define DRIVER_DESC "'eXtensible' Host Controller (xHC) Driver" @@ -573,10 +574,6 @@ int xhci_run(struct usb_hcd *hcd) if (ret) return ret; - xhci_dbg_cmd_ptrs(xhci); - - xhci_dbg(xhci, "ERST memory map follows:\n"); - xhci_dbg_erst(xhci, &xhci->erst); temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); temp_64 &= ~ERST_PTR_MASK; xhci_dbg_trace(xhci, trace_xhci_dbg_init, @@ -586,11 +583,7 @@ int xhci_run(struct usb_hcd *hcd) "// Set the interrupt modulation register"); temp = readl(&xhci->ir_set->irq_control); temp &= ~ER_IRQ_INTERVAL_MASK; - /* - * the increment interval is 8 times as much as that defined - * in xHCI spec on MTK's controller - */ - temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160); + temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK; writel(temp, &xhci->ir_set->irq_control); /* Set the HCD state before we enable the irqs */ @@ -605,12 +598,11 @@ int xhci_run(struct usb_hcd *hcd) "// Enabling event ring interrupter %p by writing 0x%x to irq_pending", xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp)); writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); - xhci_print_ir_set(xhci, 0); if (xhci->quirks & XHCI_NEC_HOST) { struct xhci_command *command; - command = xhci_alloc_command(xhci, false, false, GFP_KERNEL); + command = xhci_alloc_command(xhci, false, GFP_KERNEL); if (!command) return -ENOMEM; @@ -622,6 +614,8 @@ int xhci_run(struct usb_hcd *hcd) xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_run for USB2 roothub"); + xhci_dbc_init(xhci); + xhci_debugfs_init(xhci); return 0; @@ -654,6 +648,8 @@ static void xhci_stop(struct usb_hcd *hcd) xhci_debugfs_exit(xhci); + xhci_dbc_exit(xhci); + spin_lock_irq(&xhci->lock); xhci->xhc_state |= XHCI_STATE_HALTED; xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; @@ -681,7 +677,6 @@ static void xhci_stop(struct usb_hcd *hcd) writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); temp = readl(&xhci->ir_set->irq_pending); writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); - xhci_print_ir_set(xhci, 0); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -870,6 +865,8 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) xhci->shared_hcd->state != HC_STATE_SUSPENDED) return -EINVAL; + xhci_dbc_suspend(xhci); + /* Clear root port wake on bits if wakeup not allowed. */ if (!do_wakeup) xhci_disable_port_wake_on_bits(xhci); @@ -1014,7 +1011,6 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); temp = readl(&xhci->ir_set->irq_pending); writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); - xhci_print_ir_set(xhci, 0); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); @@ -1065,6 +1061,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) spin_unlock_irq(&xhci->lock); + xhci_dbc_resume(xhci); + done: if (retval == 0) { /* Resume root hubs only when have pending events. */ @@ -1243,7 +1241,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, * changes max packet sizes. */ - command = xhci_alloc_command(xhci, false, true, GFP_KERNEL); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) return -ENOMEM; @@ -1498,7 +1496,7 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) * the first cancellation to be handled. */ if (!(ep->ep_state & EP_STOP_CMD_PENDING)) { - command = xhci_alloc_command(xhci, false, false, GFP_ATOMIC); + command = xhci_alloc_command(xhci, false, GFP_ATOMIC); if (!command) { ret = -ENOMEM; goto done; @@ -2683,7 +2681,7 @@ static int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev); virt_dev = xhci->devs[udev->slot_id]; - command = xhci_alloc_command(xhci, false, true, GFP_KERNEL); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) return -ENOMEM; @@ -2838,12 +2836,10 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index, unsigned int stream_id, struct xhci_td *td) { struct xhci_dequeue_state deq_state; - struct xhci_virt_ep *ep; struct usb_device *udev = td->urb->dev; xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep, "Cleaning up stalled endpoint ring"); - ep = &xhci->devs[udev->slot_id]->eps[ep_index]; /* We need to move the HW's dequeue pointer past this TD, * or it will attempt to resend it on the next doorbell ring. */ @@ -3092,7 +3088,7 @@ static int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, return -ENOSYS; } - config_cmd = xhci_alloc_command(xhci, true, true, mem_flags); + config_cmd = xhci_alloc_command_with_ctx(xhci, true, mem_flags); if (!config_cmd) return -ENOMEM; @@ -3363,7 +3359,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, unsigned int slot_id; struct xhci_virt_device *virt_dev; struct xhci_command *reset_device_cmd; - int last_freed_endpoint; struct xhci_slot_ctx *slot_ctx; int old_active_eps = 0; @@ -3416,7 +3411,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, * reset as part of error handling, so use GFP_NOIO instead of * GFP_KERNEL. */ - reset_device_cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO); + reset_device_cmd = xhci_alloc_command(xhci, true, GFP_NOIO); if (!reset_device_cmd) { xhci_dbg(xhci, "Couldn't allocate command structure.\n"); return -ENOMEM; @@ -3478,7 +3473,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, } /* Everything but endpoint 0 is disabled, so free the rings. */ - last_freed_endpoint = 1; for (i = 1; i < 31; i++) { struct xhci_virt_ep *ep = &virt_dev->eps[i]; @@ -3493,7 +3487,6 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd, if (ep->ring) { xhci_debugfs_remove_endpoint(xhci, virt_dev, i); xhci_free_endpoint_ring(xhci, virt_dev, i); - last_freed_endpoint = i; } if (!list_empty(&virt_dev->eps[i].bw_endpoint_list)) xhci_drop_ep_from_interval_table(xhci, @@ -3566,7 +3559,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id) u32 state; int ret = 0; - command = xhci_alloc_command(xhci, false, false, GFP_KERNEL); + command = xhci_alloc_command(xhci, false, GFP_KERNEL); if (!command) return -ENOMEM; @@ -3628,7 +3621,7 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev) int ret, slot_id; struct xhci_command *command; - command = xhci_alloc_command(xhci, false, true, GFP_KERNEL); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) return 0; @@ -3761,7 +3754,7 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, } } - command = xhci_alloc_command(xhci, false, true, GFP_KERNEL); + command = xhci_alloc_command(xhci, true, GFP_KERNEL); if (!command) { ret = -ENOMEM; goto out; @@ -4680,7 +4673,7 @@ static int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, return -EINVAL; } - config_cmd = xhci_alloc_command(xhci, true, true, mem_flags); + config_cmd = xhci_alloc_command_with_ctx(xhci, true, mem_flags); if (!config_cmd) return -ENOMEM; @@ -4828,7 +4821,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->hcc_params = readl(&xhci->cap_regs->hcc_params); if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); - xhci_print_registers(xhci); xhci->quirks |= quirks; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 99a014a920d3..96099a245c69 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1717,6 +1717,8 @@ struct xhci_hcd { u8 max_interrupters; u8 max_ports; u8 isoc_threshold; + /* imod_interval in ns (I * 250ns) */ + u32 imod_interval; int event_ring_max; /* 4KB min, 128MB max */ int page_size; @@ -1856,6 +1858,7 @@ struct xhci_hcd { struct dentry *debugfs_slots; struct list_head regset_list; + void *dbc; /* platform-specific data -- must come last */ unsigned long priv[0] __aligned(sizeof(s64)); }; @@ -1924,12 +1927,6 @@ static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci) } /* xHCI debugging */ -void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num); -void xhci_print_registers(struct xhci_hcd *xhci); -void xhci_dbg_regs(struct xhci_hcd *xhci); -void xhci_print_run_regs(struct xhci_hcd *xhci); -void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst); -void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci); char *xhci_get_slot_state(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx); void xhci_dbg_trace(struct xhci_hcd *xhci, void (*trace)(struct va_format *), @@ -1965,9 +1962,17 @@ void xhci_slot_copy(struct xhci_hcd *xhci, int xhci_endpoint_init(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *udev, struct usb_host_endpoint *ep, gfp_t mem_flags); +struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, + unsigned int num_segs, unsigned int cycle_state, + enum xhci_ring_type type, unsigned int max_packet, gfp_t flags); void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring); int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, - unsigned int num_trbs, gfp_t flags); + unsigned int num_trbs, gfp_t flags); +int xhci_alloc_erst(struct xhci_hcd *xhci, + struct xhci_ring *evt_ring, + struct xhci_erst *erst, + gfp_t flags); +void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst); void xhci_free_endpoint_ring(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, unsigned int ep_index); @@ -1992,11 +1997,16 @@ struct xhci_ring *xhci_stream_id_to_ring( unsigned int ep_index, unsigned int stream_id); struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, - bool allocate_in_ctx, bool allocate_completion, - gfp_t mem_flags); + bool allocate_completion, gfp_t mem_flags); +struct xhci_command *xhci_alloc_command_with_ctx(struct xhci_hcd *xhci, + bool allocate_completion, gfp_t mem_flags); void xhci_urb_free_priv(struct urb_priv *urb_priv); void xhci_free_command(struct xhci_hcd *xhci, struct xhci_command *command); +struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci, + int type, gfp_t flags); +void xhci_free_container_ctx(struct xhci_hcd *xhci, + struct xhci_container_ctx *ctx); /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); @@ -2070,6 +2080,8 @@ void xhci_handle_command_timeout(struct work_struct *work); void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id); void xhci_cleanup_command_queue(struct xhci_hcd *xhci); +void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring); +unsigned int count_trbs(u64 addr, u64 len); /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index b6a1b9331aa0..716cb515523e 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -183,10 +183,10 @@ static int chaoskey_probe(struct usb_interface *interface, dev->in_ep = in_ep; if (le16_to_cpu(udev->descriptor.idVendor) != ALEA_VENDOR_ID) - dev->reads_started = 1; + dev->reads_started = true; dev->size = size; - dev->present = 1; + dev->present = true; init_waitqueue_head(&dev->wait_q); @@ -239,7 +239,7 @@ static void chaoskey_disconnect(struct usb_interface *interface) usb_set_intfdata(interface, NULL); mutex_lock(&dev->lock); - dev->present = 0; + dev->present = false; usb_poison_urb(dev->urb); if (!dev->open) { diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index bf4d2778907e..9d780b77314b 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -144,7 +144,7 @@ error: } /* attribute callback handler (write) */ -static ssize_t set_port0_handler(struct device *dev, +static ssize_t port0_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -152,7 +152,7 @@ static ssize_t set_port0_handler(struct device *dev, } /* attribute callback handler (write) */ -static ssize_t set_port1_handler(struct device *dev, +static ssize_t port1_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -178,22 +178,22 @@ static ssize_t read_port(struct device *dev, struct device_attribute *attr, } /* attribute callback handler (read) */ -static ssize_t get_port0_handler(struct device *dev, +static ssize_t port0_show(struct device *dev, struct device_attribute *attr, char *buf) { return read_port(dev, attr, buf, 0, CYPRESS_READ_PORT_ID0); } /* attribute callback handler (read) */ -static ssize_t get_port1_handler(struct device *dev, +static ssize_t port1_show(struct device *dev, struct device_attribute *attr, char *buf) { return read_port(dev, attr, buf, 1, CYPRESS_READ_PORT_ID1); } -static DEVICE_ATTR(port0, S_IRUGO | S_IWUSR, get_port0_handler, set_port0_handler); +static DEVICE_ATTR_RW(port0); -static DEVICE_ATTR(port1, S_IRUGO | S_IWUSR, get_port1_handler, set_port1_handler); +static DEVICE_ATTR_RW(port1); static int cypress_probe(struct usb_interface *interface, diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 66be86077292..8b15ab5e1450 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -78,7 +78,7 @@ static int vendor_command(struct usb_device *dev, unsigned char request, #define BRIGHTNESS 0x2c /* RAM location for brightness value */ #define BRIGHTNESS_SEM 0x2b /* RAM location for brightness semaphore */ -static ssize_t show_brightness(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct usb_cytherm *cytherm = usb_get_intfdata(intf); @@ -86,7 +86,7 @@ static ssize_t show_brightness(struct device *dev, struct device_attribute *attr return sprintf(buf, "%i", cytherm->brightness); } -static ssize_t set_brightness(struct device *dev, struct device_attribute *attr, const char *buf, +static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); @@ -121,15 +121,13 @@ static ssize_t set_brightness(struct device *dev, struct device_attribute *attr, return count; } - -static DEVICE_ATTR(brightness, S_IRUGO | S_IWUSR | S_IWGRP, - show_brightness, set_brightness); +static DEVICE_ATTR_RW(brightness); #define TEMP 0x33 /* RAM location for temperature */ #define SIGN 0x34 /* RAM location for temperature sign */ -static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -161,19 +159,12 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char return sprintf(buf, "%c%i.%i", sign ? '-' : '+', temp >> 1, 5*(temp - ((temp >> 1) << 1))); } - - -static ssize_t set_temp(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - return count; -} - -static DEVICE_ATTR(temp, S_IRUGO, show_temp, set_temp); +static DEVICE_ATTR_RO(temp); #define BUTTON 0x7a -static ssize_t show_button(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t button_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -200,17 +191,10 @@ static ssize_t show_button(struct device *dev, struct device_attribute *attr, ch else return sprintf(buf, "0"); } +static DEVICE_ATTR_RO(button); -static ssize_t set_button(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - return count; -} - -static DEVICE_ATTR(button, S_IRUGO, show_button, set_button); - - -static ssize_t show_port0(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t port0_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct usb_cytherm *cytherm = usb_get_intfdata(intf); @@ -234,7 +218,7 @@ static ssize_t show_port0(struct device *dev, struct device_attribute *attr, cha } -static ssize_t set_port0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t port0_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); struct usb_cytherm *cytherm = usb_get_intfdata(intf); @@ -263,10 +247,9 @@ static ssize_t set_port0(struct device *dev, struct device_attribute *attr, cons return count; } +static DEVICE_ATTR_RW(port0); -static DEVICE_ATTR(port0, S_IRUGO | S_IWUSR | S_IWGRP, show_port0, set_port0); - -static ssize_t show_port1(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t port1_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); struct usb_cytherm *cytherm = usb_get_intfdata(intf); @@ -290,7 +273,7 @@ static ssize_t show_port1(struct device *dev, struct device_attribute *attr, cha } -static ssize_t set_port1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t port1_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); struct usb_cytherm *cytherm = usb_get_intfdata(intf); @@ -319,9 +302,7 @@ static ssize_t set_port1(struct device *dev, struct device_attribute *attr, cons return count; } - -static DEVICE_ATTR(port1, S_IRUGO | S_IWUSR | S_IWGRP, show_port1, set_port1); - +static DEVICE_ATTR_RW(port1); static int cytherm_probe(struct usb_interface *interface, diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c index d83af2a332e4..b3e1f553954a 100644 --- a/drivers/usb/misc/trancevibrator.c +++ b/drivers/usb/misc/trancevibrator.c @@ -30,7 +30,7 @@ struct trancevibrator { unsigned int speed; }; -static ssize_t show_speed(struct device *dev, struct device_attribute *attr, +static ssize_t speed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -39,7 +39,7 @@ static ssize_t show_speed(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", tv->speed); } -static ssize_t set_speed(struct device *dev, struct device_attribute *attr, +static ssize_t speed_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); @@ -70,7 +70,7 @@ static ssize_t set_speed(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR, show_speed, set_speed); +static DEVICE_ATTR_RW(speed); static int tv_probe(struct usb_interface *interface, const struct usb_device_id *id) diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index 12f7e94695a2..1923d5b6d9c9 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -165,7 +165,7 @@ static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf) } #define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn) \ -static ssize_t show_attr_##name(struct device *dev, \ +static ssize_t name##_show(struct device *dev, \ struct device_attribute *attr, char *buf) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ @@ -174,7 +174,7 @@ static ssize_t show_attr_##name(struct device *dev, \ return sprintf(buf, "%u\n", mydev->name); \ } \ \ -static ssize_t set_attr_##name(struct device *dev, \ +static ssize_t name##_store(struct device *dev, \ struct device_attribute *attr, const char *buf, size_t count) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ @@ -185,9 +185,9 @@ static ssize_t set_attr_##name(struct device *dev, \ \ return count; \ } \ -static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_attr_##name, set_attr_##name); +static DEVICE_ATTR_RW(name); -static ssize_t show_attr_text(struct device *dev, +static ssize_t text_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -196,7 +196,7 @@ static ssize_t show_attr_text(struct device *dev, return snprintf(buf, mydev->textlength, "%s\n", mydev->text); } -static ssize_t set_attr_text(struct device *dev, +static ssize_t text_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); @@ -216,9 +216,9 @@ static ssize_t set_attr_text(struct device *dev, return count; } -static DEVICE_ATTR(text, S_IRUGO | S_IWUSR, show_attr_text, set_attr_text); +static DEVICE_ATTR_RW(text); -static ssize_t show_attr_decimals(struct device *dev, +static ssize_t decimals_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -240,7 +240,7 @@ static ssize_t show_attr_decimals(struct device *dev, return sizeof(mydev->decimals) + 1; } -static ssize_t set_attr_decimals(struct device *dev, +static ssize_t decimals_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); @@ -265,9 +265,9 @@ static ssize_t set_attr_decimals(struct device *dev, return count; } -static DEVICE_ATTR(decimals, S_IRUGO | S_IWUSR, show_attr_decimals, set_attr_decimals); +static DEVICE_ATTR_RW(decimals); -static ssize_t show_attr_textmode(struct device *dev, +static ssize_t textmode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_interface *intf = to_usb_interface(dev); @@ -293,7 +293,7 @@ static ssize_t show_attr_textmode(struct device *dev, return strlen(buf); } -static ssize_t set_attr_textmode(struct device *dev, +static ssize_t textmode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct usb_interface *intf = to_usb_interface(dev); @@ -309,7 +309,7 @@ static ssize_t set_attr_textmode(struct device *dev, return count; } -static DEVICE_ATTR(textmode, S_IRUGO | S_IWUSR, show_attr_textmode, set_attr_textmode); +static DEVICE_ATTR_RW(textmode); MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index aedc9a7f149e..90028ef541e3 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1710,6 +1710,35 @@ static int test_halt(struct usbtest_dev *tdev, int ep, struct urb *urb) return 0; } +static int test_toggle_sync(struct usbtest_dev *tdev, int ep, struct urb *urb) +{ + int retval; + + /* clear initial data toggle to DATA0 */ + retval = usb_clear_halt(urb->dev, urb->pipe); + if (retval < 0) { + ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval); + return retval; + } + + /* transfer 3 data packets, should be DATA0, DATA1, DATA0 */ + retval = simple_io(tdev, urb, 1, 0, 0, __func__); + if (retval != 0) + return -EINVAL; + + /* clear halt resets device side data toggle, host should react to it */ + retval = usb_clear_halt(urb->dev, urb->pipe); + if (retval < 0) { + ERROR(tdev, "ep %02x couldn't clear halt, %d\n", ep, retval); + return retval; + } + + /* host should use DATA0 again after clear halt */ + retval = simple_io(tdev, urb, 1, 0, 0, __func__); + + return retval; +} + static int halt_simple(struct usbtest_dev *dev) { int ep; @@ -1742,6 +1771,33 @@ done: return retval; } +static int toggle_sync_simple(struct usbtest_dev *dev) +{ + int ep; + int retval = 0; + struct urb *urb; + struct usb_device *udev = testdev_to_usbdev(dev); + unsigned maxp = get_maxpacket(udev, dev->out_pipe); + + /* + * Create a URB that causes a transfer of uneven amount of data packets + * This way the clear toggle has an impact on the data toggle sequence. + * Use 2 maxpacket length packets and one zero packet. + */ + urb = simple_alloc_urb(udev, 0, 2 * maxp, 0); + if (urb == NULL) + return -ENOMEM; + + urb->transfer_flags |= URB_ZERO_PACKET; + + ep = usb_pipeendpoint(dev->out_pipe); + urb->pipe = dev->out_pipe; + retval = test_toggle_sync(dev, ep, urb); + + simple_free_urb(urb); + return retval; +} + /*-------------------------------------------------------------------------*/ /* Control OUT tests use the vendor control requests from Intel's @@ -2524,6 +2580,20 @@ usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param) retval = test_queue(dev, param, dev->in_pipe, NULL, 0); break; + /* Test data Toggle/seq_nr clear between bulk out transfers */ + case 29: + if (dev->out_pipe == 0) + break; + retval = 0; + dev_info(&intf->dev, "TEST 29: Clear toggle between bulk writes %d times\n", + param->iterations); + for (i = param->iterations; retval == 0 && i > 0; --i) + retval = toggle_sync_simple(dev); + + if (retval) + ERROR(dev, "toggle sync failed, iterations left %d\n", + i); + break; } return retval; } diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 3c888d942a9f..2cd00a24afd9 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -229,7 +229,10 @@ struct otg_switch_mtk { * @u3p_dis_msk: mask of disabling usb3 ports, for example, bit0==1 to * disable u3port0, bit1==1 to disable u3port1,... etc * @dbgfs_root: only used when supports manual dual-role switch via debugfs - * @wakeup_en: it's true when supports remote wakeup in host mode + * @uwk_en: it's true when supports remote wakeup in host mode + * @uwk: syscon including usb wakeup glue layer between SSUSB IP and SPM + * @uwk_reg_base: the base address of the wakeup glue layer in @uwk + * @uwk_vers: the version of the wakeup glue layer */ struct ssusb_mtk { struct device *dev; @@ -253,8 +256,10 @@ struct ssusb_mtk { int u3p_dis_msk; struct dentry *dbgfs_root; /* usb wakeup for host mode */ - bool wakeup_en; - struct regmap *pericfg; + bool uwk_en; + struct regmap *uwk; + u32 uwk_reg_base; + u32 uwk_vers; }; /** diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h index c179192408ba..50702fdcde28 100644 --- a/drivers/usb/mtu3/mtu3_dr.h +++ b/drivers/usb/mtu3/mtu3_dr.h @@ -18,8 +18,7 @@ int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb, struct device_node *dn); int ssusb_host_enable(struct ssusb_mtk *ssusb); int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend); -int ssusb_wakeup_enable(struct ssusb_mtk *ssusb); -void ssusb_wakeup_disable(struct ssusb_mtk *ssusb); +void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable); #else @@ -49,12 +48,7 @@ static inline int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend) return 0; } -static inline int ssusb_wakeup_enable(struct ssusb_mtk *ssusb) -{ - return 0; -} - -static inline void ssusb_wakeup_disable(struct ssusb_mtk *ssusb) +static inline void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable) {} #endif diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index d237d7e65c44..c871b94f3e6f 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -18,66 +18,77 @@ #include "mtu3.h" #include "mtu3_dr.h" -#define PERI_WK_CTRL1 0x404 -#define UWK_CTL1_IS_C(x) (((x) & 0xf) << 26) -#define UWK_CTL1_IS_E BIT(25) -#define UWK_CTL1_IDDIG_C(x) (((x) & 0xf) << 11) /* cycle debounce */ -#define UWK_CTL1_IDDIG_E BIT(10) /* enable debounce */ -#define UWK_CTL1_IDDIG_P BIT(9) /* polarity */ -#define UWK_CTL1_IS_P BIT(6) /* polarity for ip sleep */ +/* mt8173 etc */ +#define PERI_WK_CTRL1 0x4 +#define WC1_IS_C(x) (((x) & 0xf) << 26) /* cycle debounce */ +#define WC1_IS_EN BIT(25) +#define WC1_IS_P BIT(6) /* polarity for ip sleep */ + +/* mt2712 etc */ +#define PERI_SSUSB_SPM_CTRL 0x0 +#define SSC_IP_SLEEP_EN BIT(4) +#define SSC_SPM_INT_EN BIT(1) + +enum ssusb_uwk_vers { + SSUSB_UWK_V1 = 1, + SSUSB_UWK_V2, +}; /* * ip-sleep wakeup mode: * all clocks can be turn off, but power domain should be kept on */ -static void ssusb_wakeup_ip_sleep_en(struct ssusb_mtk *ssusb) +static void ssusb_wakeup_ip_sleep_set(struct ssusb_mtk *ssusb, bool enable) { - u32 tmp; - struct regmap *pericfg = ssusb->pericfg; - - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_IS_P; - tmp &= ~(UWK_CTL1_IS_C(0xf)); - tmp |= UWK_CTL1_IS_C(0x8); - regmap_write(pericfg, PERI_WK_CTRL1, tmp); - regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E); - - regmap_read(pericfg, PERI_WK_CTRL1, &tmp); - dev_dbg(ssusb->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n", - __func__, tmp); -} - -static void ssusb_wakeup_ip_sleep_dis(struct ssusb_mtk *ssusb) -{ - u32 tmp; - - regmap_read(ssusb->pericfg, PERI_WK_CTRL1, &tmp); - tmp &= ~UWK_CTL1_IS_E; - regmap_write(ssusb->pericfg, PERI_WK_CTRL1, tmp); + u32 reg, msk, val; + + switch (ssusb->uwk_vers) { + case SSUSB_UWK_V1: + reg = ssusb->uwk_reg_base + PERI_WK_CTRL1; + msk = WC1_IS_EN | WC1_IS_C(0xf) | WC1_IS_P; + val = enable ? (WC1_IS_EN | WC1_IS_C(0x8)) : 0; + break; + case SSUSB_UWK_V2: + reg = ssusb->uwk_reg_base + PERI_SSUSB_SPM_CTRL; + msk = SSC_IP_SLEEP_EN | SSC_SPM_INT_EN; + val = enable ? msk : 0; + break; + default: + return; + } + regmap_update_bits(ssusb->uwk, reg, msk, val); } int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb, struct device_node *dn) { - struct device *dev = ssusb->dev; + struct of_phandle_args args; + int ret; - /* - * Wakeup function is optional, so it is not an error if this property - * does not exist, and in such case, no need to get relative - * properties anymore. - */ - ssusb->wakeup_en = of_property_read_bool(dn, "mediatek,enable-wakeup"); - if (!ssusb->wakeup_en) + /* wakeup function is optional */ + ssusb->uwk_en = of_property_read_bool(dn, "wakeup-source"); + if (!ssusb->uwk_en) return 0; - ssusb->pericfg = syscon_regmap_lookup_by_phandle(dn, - "mediatek,syscon-wakeup"); - if (IS_ERR(ssusb->pericfg)) { - dev_err(dev, "fail to get pericfg regs\n"); - return PTR_ERR(ssusb->pericfg); - } + ret = of_parse_phandle_with_fixed_args(dn, + "mediatek,syscon-wakeup", 2, 0, &args); + if (ret) + return ret; - return 0; + ssusb->uwk_reg_base = args.args[0]; + ssusb->uwk_vers = args.args[1]; + ssusb->uwk = syscon_node_to_regmap(args.np); + of_node_put(args.np); + dev_info(ssusb->dev, "uwk - reg:0x%x, version:%d\n", + ssusb->uwk_reg_base, ssusb->uwk_vers); + + return PTR_ERR_OR_ZERO(ssusb->uwk); +} + +void ssusb_wakeup_set(struct ssusb_mtk *ssusb, bool enable) +{ + if (ssusb->uwk_en) + ssusb_wakeup_ip_sleep_set(ssusb, enable); } static void host_ports_num_get(struct ssusb_mtk *ssusb) @@ -235,17 +246,3 @@ void ssusb_host_exit(struct ssusb_mtk *ssusb) of_platform_depopulate(ssusb->dev); ssusb_host_cleanup(ssusb); } - -int ssusb_wakeup_enable(struct ssusb_mtk *ssusb) -{ - if (ssusb->wakeup_en) - ssusb_wakeup_ip_sleep_en(ssusb); - - return 0; -} - -void ssusb_wakeup_disable(struct ssusb_mtk *ssusb) -{ - if (ssusb->wakeup_en) - ssusb_wakeup_ip_sleep_dis(ssusb); -} diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 3650fd11fc49..628d5ce356ca 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -282,8 +282,10 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) /* if host role is supported */ ret = ssusb_wakeup_of_property_parse(ssusb, node); - if (ret) + if (ret) { + dev_err(dev, "failed to parse uwk property\n"); return ret; + } /* optional property, ignore the error if it does not exist */ of_property_read_u32(node, "mediatek,u3p-dis-msk", @@ -308,7 +310,7 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0); if (IS_ERR(otg_sx->edev)) { dev_err(ssusb->dev, "couldn't get extcon device\n"); - return -EPROBE_DEFER; + return PTR_ERR(otg_sx->edev); } } @@ -457,7 +459,7 @@ static int __maybe_unused mtu3_suspend(struct device *dev) ssusb_host_disable(ssusb, true); ssusb_phy_power_off(ssusb); ssusb_clks_disable(ssusb); - ssusb_wakeup_enable(ssusb); + ssusb_wakeup_set(ssusb, true); return 0; } @@ -473,7 +475,7 @@ static int __maybe_unused mtu3_resume(struct device *dev) if (!ssusb->is_host) return 0; - ssusb_wakeup_disable(ssusb); + ssusb_wakeup_set(ssusb, false); ret = ssusb_clks_enable(ssusb); if (ret) goto clks_err; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 6c036de63272..b8295ce7c4fe 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -520,7 +520,7 @@ static int da8xx_probe(struct platform_device *pdev) if (!glue) return -ENOMEM; - clk = devm_clk_get(&pdev->dev, "usb20"); + clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get clock\n"); return PTR_ERR(clk); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index ea5013aa69e2..968bf1e8b0fe 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1687,7 +1687,7 @@ EXPORT_SYMBOL_GPL(musb_mailbox); /*-------------------------------------------------------------------------*/ static ssize_t -musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct musb *musb = dev_to_musb(dev); unsigned long flags; @@ -1701,7 +1701,7 @@ musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -musb_mode_store(struct device *dev, struct device_attribute *attr, +mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct musb *musb = dev_to_musb(dev); @@ -1721,10 +1721,10 @@ musb_mode_store(struct device *dev, struct device_attribute *attr, return (status == 0) ? n : status; } -static DEVICE_ATTR(mode, 0644, musb_mode_show, musb_mode_store); +static DEVICE_ATTR_RW(mode); static ssize_t -musb_vbus_store(struct device *dev, struct device_attribute *attr, +vbus_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct musb *musb = dev_to_musb(dev); @@ -1748,7 +1748,7 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr, } static ssize_t -musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) +vbus_show(struct device *dev, struct device_attribute *attr, char *buf) { struct musb *musb = dev_to_musb(dev); unsigned long flags; @@ -1773,13 +1773,12 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "Vbus %s, timeout %lu msec\n", vbus ? "on" : "off", val); } -static DEVICE_ATTR(vbus, 0644, musb_vbus_show, musb_vbus_store); +static DEVICE_ATTR_RW(vbus); /* Gadget drivers can't know that a host is connected so they might want * to start SRP, but users can. This allows userspace to trigger SRP. */ -static ssize_t -musb_srp_store(struct device *dev, struct device_attribute *attr, +static ssize_t srp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { struct musb *musb = dev_to_musb(dev); @@ -1796,7 +1795,7 @@ musb_srp_store(struct device *dev, struct device_attribute *attr, return n; } -static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store); +static DEVICE_ATTR_WO(srp); static struct attribute *musb_attributes[] = { &dev_attr_mode.attr, diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 2627363fb4fe..394b4ac86161 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -195,7 +195,6 @@ static struct musb_qh *musb_ep_get_qh(struct musb_hw_ep *ep, int is_in) static void musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) { - u16 frame; u32 len; void __iomem *mbase = musb->mregs; struct urb *urb = next_urb(qh); @@ -244,7 +243,6 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: musb_dbg(musb, "check whether there's still time for periodic Tx"); - frame = musb_readw(mbase, MUSB_FRAME); /* FIXME this doesn't implement that scheduling policy ... * or handle framecounter wrapping */ @@ -1781,7 +1779,6 @@ void musb_host_rx(struct musb *musb, u8 epnum) struct musb_qh *qh = hw_ep->in_qh; size_t xfer_len; void __iomem *mbase = musb->mregs; - int pipe; u16 rx_csr, val; bool iso_err = false; bool done = false; @@ -1810,8 +1807,6 @@ void musb_host_rx(struct musb *musb, u8 epnum) return; } - pipe = urb->pipe; - trace_musb_urb_rx(musb, urb); /* check for errors, concurrent stall & unlink is not really diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index 554b72282276..cfd9add10bf4 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -519,7 +519,7 @@ static irqreturn_t mv_otg_inputs_irq(int irq, void *dev) } static ssize_t -get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) +a_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mv_otg *mvotg = dev_get_drvdata(dev); return scnprintf(buf, PAGE_SIZE, "%d\n", @@ -527,7 +527,7 @@ get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -set_a_bus_req(struct device *dev, struct device_attribute *attr, +a_bus_req_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mv_otg *mvotg = dev_get_drvdata(dev); @@ -559,11 +559,10 @@ set_a_bus_req(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUSR, get_a_bus_req, - set_a_bus_req); +static DEVICE_ATTR_RW(a_bus_req); static ssize_t -set_a_clr_err(struct device *dev, struct device_attribute *attr, +a_clr_err_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mv_otg *mvotg = dev_get_drvdata(dev); @@ -587,10 +586,10 @@ set_a_clr_err(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(a_clr_err, S_IWUSR, NULL, set_a_clr_err); +static DEVICE_ATTR_WO(a_clr_err); static ssize_t -get_a_bus_drop(struct device *dev, struct device_attribute *attr, +a_bus_drop_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mv_otg *mvotg = dev_get_drvdata(dev); @@ -599,7 +598,7 @@ get_a_bus_drop(struct device *dev, struct device_attribute *attr, } static ssize_t -set_a_bus_drop(struct device *dev, struct device_attribute *attr, +a_bus_drop_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mv_otg *mvotg = dev_get_drvdata(dev); @@ -630,8 +629,7 @@ set_a_bus_drop(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUSR, - get_a_bus_drop, set_a_bus_drop); +static DEVICE_ATTR_RW(a_bus_drop); static struct attribute *inputs_attrs[] = { &dev_attr_a_bus_req.attr, diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index b3ce42edb373..0981abc3d1ad 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -59,13 +59,13 @@ static const unsigned int tahvo_cable[] = { EXTCON_NONE, }; -static ssize_t vbus_state_show(struct device *device, +static ssize_t vbus_show(struct device *device, struct device_attribute *attr, char *buf) { struct tahvo_usb *tu = dev_get_drvdata(device); return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off"); } -static DEVICE_ATTR(vbus, 0444, vbus_state_show, NULL); +static DEVICE_ATTR_RO(vbus); static void check_vbus_state(struct tahvo_usb *tu) { @@ -310,7 +310,7 @@ static ssize_t otg_mode_store(struct device *device, return r; } -static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store); +static DEVICE_ATTR_RW(otg_mode); static struct attribute *tahvo_attributes[] = { &dev_attr_vbus.attr, diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index e78ed52339e6..183550b63faa 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -168,7 +168,7 @@ static int twl6030_usb_ldo_init(struct twl6030_usb *twl) return 0; } -static ssize_t twl6030_usb_vbus_show(struct device *dev, +static ssize_t vbus_show(struct device *dev, struct device_attribute *attr, char *buf) { struct twl6030_usb *twl = dev_get_drvdata(dev); @@ -194,7 +194,7 @@ static ssize_t twl6030_usb_vbus_show(struct device *dev, return ret; } -static DEVICE_ATTR(vbus, 0444, twl6030_usb_vbus_show, NULL); +static DEVICE_ATTR_RO(vbus); static irqreturn_t twl6030_usb_irq(int irq, void *_twl) { diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index f97cb47577fc..bceb2c9988dd 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -323,6 +323,14 @@ static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) return *phy == match_data; } +static void usb_charger_init(struct usb_phy *usb_phy) +{ + usb_phy->chg_type = UNKNOWN_TYPE; + usb_phy->chg_state = USB_CHARGER_DEFAULT; + usb_phy_set_default_current(usb_phy); + INIT_WORK(&usb_phy->chg_work, usb_phy_notify_charger_work); +} + static int usb_add_extcon(struct usb_phy *x) { int ret; @@ -406,10 +414,6 @@ static int usb_add_extcon(struct usb_phy *x) } } - usb_phy_set_default_current(x); - INIT_WORK(&x->chg_work, usb_phy_notify_charger_work); - x->chg_type = UNKNOWN_TYPE; - x->chg_state = USB_CHARGER_DEFAULT; if (x->type_nb.notifier_call) __usb_phy_get_charger_type(x); @@ -704,6 +708,7 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) return -EINVAL; } + usb_charger_init(x); ret = usb_add_extcon(x); if (ret) return ret; @@ -749,6 +754,7 @@ int usb_add_phy_dev(struct usb_phy *x) return -EINVAL; } + usb_charger_init(x); ret = usb_add_extcon(x); if (ret) return ret; diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index fac147a3ad23..5c5b51bb48ef 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o -renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o rcar3.o +renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o rcar3.o rza.o ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) renesas_usbhs-y += mod_host.o diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 56079bb6759a..4310df46639d 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -17,6 +17,7 @@ #include "common.h" #include "rcar2.h" #include "rcar3.h" +#include "rza.h" /* * image of renesas_usbhs @@ -488,6 +489,10 @@ static const struct of_device_id usbhs_of_match[] = { .compatible = "renesas,rcar-gen3-usbhs", .data = (void *)USBHS_TYPE_RCAR_GEN3, }, + { + .compatible = "renesas,rza1-usbhs", + .data = (void *)USBHS_TYPE_RZA1, + }, { }, }; MODULE_DEVICE_TABLE(of, usbhs_of_match); @@ -520,6 +525,11 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe); } + if (dparam->type == USBHS_TYPE_RZA1) { + dparam->pipe_configs = usbhsc_new_pipe; + dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe); + } + return info; } @@ -581,6 +591,18 @@ static int usbhs_probe(struct platform_device *pdev) break; case USBHS_TYPE_RCAR_GEN3_WITH_PLL: priv->pfunc = usbhs_rcar3_with_pll_ops; + if (!IS_ERR_OR_NULL(priv->edev)) { + priv->nb.notifier_call = priv->pfunc.notifier; + ret = devm_extcon_register_notifier(&pdev->dev, + priv->edev, + EXTCON_USB_HOST, + &priv->nb); + if (ret < 0) + dev_err(&pdev->dev, "no notifier registered\n"); + } + break; + case USBHS_TYPE_RZA1: + priv->pfunc = usbhs_rza1_ops; break; default: if (!info->platform_callback.get_id) { diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 64797784a6df..f619afeae2b8 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -98,6 +98,7 @@ struct usbhs_priv; #define D2FIFOCTR 0x00F2 /* for R-Car Gen2 */ #define D3FIFOSEL 0x00F4 /* for R-Car Gen2 */ #define D3FIFOCTR 0x00F6 /* for R-Car Gen2 */ +#define SUSPMODE 0x0102 /* for RZ/A */ /* SYSCFG */ #define SCKE (1 << 10) /* USB Module Clock Enable */ @@ -106,6 +107,8 @@ struct usbhs_priv; #define DRPD (1 << 5) /* D+ Line/D- Line Resistance Control */ #define DPRPU (1 << 4) /* D+ Line Resistance Control */ #define USBE (1 << 0) /* USB Module Operation Enable */ +#define UCKSEL (1 << 2) /* Clock Select for RZ/A1 */ +#define UPLLE (1 << 1) /* USB PLL Enable for RZ/A1 */ /* DVSTCTR */ #define EXTLP (1 << 10) /* Controls the EXTLP pin output state */ @@ -233,6 +236,9 @@ struct usbhs_priv; #define USBSPD_SPEED_FULL 0x2 #define USBSPD_SPEED_HIGH 0x3 +/* SUSPMODE */ +#define SUSPM (1 << 14) /* SuspendM Control */ + /* * struct */ @@ -249,6 +255,7 @@ struct usbhs_priv { struct platform_device *pdev; struct extcon_dev *edev; + struct notifier_block nb; spinlock_t lock; diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 2d24ef3076ef..5925d111bd47 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -94,8 +94,6 @@ static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe) return list_first_entry_or_null(&pipe->list, struct usbhs_pkt, node); } -static void usbhsf_fifo_clear(struct usbhs_pipe *pipe, - struct usbhs_fifo *fifo); static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo, @@ -124,10 +122,11 @@ struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt) chan = usbhsf_dma_chan_get(fifo, pkt); if (chan) { dmaengine_terminate_all(chan); - usbhsf_fifo_clear(pipe, fifo); usbhsf_dma_unmap(pkt); } + usbhs_pipe_clear_without_sequence(pipe, 0, 0); + __usbhsf_pkt_del(pkt); } @@ -256,15 +255,9 @@ static void usbhsf_send_terminator(struct usbhs_pipe *pipe, static int usbhsf_fifo_barrier(struct usbhs_priv *priv, struct usbhs_fifo *fifo) { - int timeout = 1024; - - do { - /* The FIFO port is accessible */ - if (usbhs_read(priv, fifo->ctr) & FRDY) - return 0; - - udelay(10); - } while (timeout--); + /* The FIFO port is accessible */ + if (usbhs_read(priv, fifo->ctr) & FRDY) + return 0; return -EBUSY; } @@ -278,8 +271,8 @@ static void usbhsf_fifo_clear(struct usbhs_pipe *pipe, if (!usbhs_pipe_is_dcp(pipe)) { /* * This driver checks the pipe condition first to avoid -EBUSY - * from usbhsf_fifo_barrier() with about 10 msec delay in - * the interrupt handler if the pipe is RX direction and empty. + * from usbhsf_fifo_barrier() if the pipe is RX direction and + * empty. */ if (usbhs_pipe_is_dir_in(pipe)) ret = usbhs_pipe_is_accessible(pipe); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 093cd8e87335..9677e0e31475 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -590,10 +590,22 @@ void usbhs_pipe_clear(struct usbhs_pipe *pipe) } } -void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable) +/* Should call usbhsp_pipe_select() before */ +void usbhs_pipe_clear_without_sequence(struct usbhs_pipe *pipe, + int needs_bfre, int bfre_enable) { int sequence; + usbhsp_pipe_select(pipe); + sequence = usbhs_pipe_get_data_sequence(pipe); + if (needs_bfre) + usbhsp_pipe_cfg_set(pipe, BFRE, bfre_enable ? BFRE : 0); + usbhs_pipe_clear(pipe); + usbhs_pipe_data_sequence(pipe, sequence); +} + +void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable) +{ if (usbhs_pipe_is_dcp(pipe)) return; @@ -602,10 +614,7 @@ void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable) if (!(enable ^ !!(usbhsp_pipe_cfg_get(pipe) & BFRE))) return; - sequence = usbhs_pipe_get_data_sequence(pipe); - usbhsp_pipe_cfg_set(pipe, BFRE, enable ? BFRE : 0); - usbhs_pipe_clear(pipe); - usbhs_pipe_data_sequence(pipe, sequence); + usbhs_pipe_clear_without_sequence(pipe, 1, enable); } static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index d3d002244891..3080423e600c 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -80,6 +80,8 @@ void usbhs_pipe_init(struct usbhs_priv *priv, struct usbhs_pkt *pkt, int map)); int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); void usbhs_pipe_clear(struct usbhs_pipe *pipe); +void usbhs_pipe_clear_without_sequence(struct usbhs_pipe *pipe, + int needs_bfre, int bfre_enable); int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe); void usbhs_pipe_enable(struct usbhs_pipe *pipe); void usbhs_pipe_disable(struct usbhs_pipe *pipe); diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c index c929d296c77b..d0ea4ff89622 100644 --- a/drivers/usb/renesas_usbhs/rcar3.c +++ b/drivers/usb/renesas_usbhs/rcar3.c @@ -27,6 +27,7 @@ * Remarks: bit[31:11] and bit[9:6] should be 0 */ #define UGCTRL2_RESERVED_3 0x00000001 /* bit[3:0] should be B'0001 */ +#define UGCTRL2_USB0SEL_EHCI 0x00000010 #define UGCTRL2_USB0SEL_HSUSB 0x00000020 #define UGCTRL2_USB0SEL_OTG 0x00000030 #define UGCTRL2_VBUSSEL 0x00000400 @@ -44,13 +45,25 @@ static u32 usbhs_read32(struct usbhs_priv *priv, u32 reg) return ioread32(priv->base + reg); } +static void usbhs_rcar3_set_ugctrl2(struct usbhs_priv *priv, u32 val) +{ + usbhs_write32(priv, UGCTRL2, val | UGCTRL2_RESERVED_3); +} + +static void usbhs_rcar3_set_usbsel(struct usbhs_priv *priv, bool ehci) +{ + if (ehci) + usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_EHCI); + else + usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_HSUSB); +} + static int usbhs_rcar3_power_ctrl(struct platform_device *pdev, void __iomem *base, int enable) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); - usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | UGCTRL2_USB0SEL_OTG | - UGCTRL2_VBUSSEL); + usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_OTG | UGCTRL2_VBUSSEL); if (enable) { usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM); @@ -70,11 +83,14 @@ static int usbhs_rcar3_power_and_pll_ctrl(struct platform_device *pdev, struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); u32 val; int timeout = 1000; + bool is_host = false; if (enable) { usbhs_write32(priv, UGCTRL, 0); /* release PLLRESET */ - usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | - UGCTRL2_USB0SEL_HSUSB); + if (priv->edev) + is_host = extcon_get_state(priv->edev, EXTCON_USB_HOST); + + usbhs_rcar3_set_usbsel(priv, is_host); usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM); do { @@ -96,6 +112,16 @@ static int usbhs_rcar3_get_id(struct platform_device *pdev) return USBHS_GADGET; } +static int usbhs_rcar3_notifier(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct usbhs_priv *priv = container_of(nb, struct usbhs_priv, nb); + + usbhs_rcar3_set_usbsel(priv, !!event); + + return NOTIFY_DONE; +} + const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = { .power_ctrl = usbhs_rcar3_power_ctrl, .get_id = usbhs_rcar3_get_id, @@ -104,4 +130,5 @@ const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = { const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops = { .power_ctrl = usbhs_rcar3_power_and_pll_ctrl, .get_id = usbhs_rcar3_get_id, + .notifier = usbhs_rcar3_notifier, }; diff --git a/drivers/usb/renesas_usbhs/rza.c b/drivers/usb/renesas_usbhs/rza.c new file mode 100644 index 000000000000..5b287257ec11 --- /dev/null +++ b/drivers/usb/renesas_usbhs/rza.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas USB driver RZ/A initialization and power control + * + * Copyright (C) 2018 Chris Brandt + * Copyright (C) 2018 Renesas Electronics Corporation + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of_device.h> +#include "common.h" +#include "rza.h" + +static int usbhs_rza1_hardware_init(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + struct device_node *usb_x1_clk, *extal_clk; + u32 freq_usb = 0, freq_extal = 0; + + /* Input Clock Selection (NOTE: ch0 controls both ch0 and ch1) */ + usb_x1_clk = of_find_node_by_name(NULL, "usb_x1"); + extal_clk = of_find_node_by_name(NULL, "extal"); + of_property_read_u32(usb_x1_clk, "clock-frequency", &freq_usb); + of_property_read_u32(extal_clk, "clock-frequency", &freq_extal); + if (freq_usb == 0) { + if (freq_extal == 12000000) { + /* Select 12MHz XTAL */ + usbhs_bset(priv, SYSCFG, UCKSEL, UCKSEL); + } else { + dev_err(usbhs_priv_to_dev(priv), "A 48MHz USB clock or 12MHz main clock is required.\n"); + return -EIO; + } + } + + /* Enable USB PLL (NOTE: ch0 controls both ch0 and ch1) */ + usbhs_bset(priv, SYSCFG, UPLLE, UPLLE); + udelay(1000); + usbhs_bset(priv, SUSPMODE, SUSPM, SUSPM); + + return 0; +} + +static int usbhs_rza_get_id(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + +const struct renesas_usbhs_platform_callback usbhs_rza1_ops = { + .hardware_init = usbhs_rza1_hardware_init, + .get_id = usbhs_rza_get_id, +}; diff --git a/drivers/usb/renesas_usbhs/rza.h b/drivers/usb/renesas_usbhs/rza.h new file mode 100644 index 000000000000..ca917ca54f6d --- /dev/null +++ b/drivers/usb/renesas_usbhs/rza.h @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "common.h" + +extern const struct renesas_usbhs_platform_callback usbhs_rza1_ops; diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index a8d5f2e4878d..c66b93664d54 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -63,6 +63,7 @@ config USB_SERIAL_SIMPLE - Google USB serial devices - HP4x calculators - a number of Motorola phones + - Motorola Tetra devices - Novatel Wireless GPS receivers - Siemens USB/MPI adapter. - ViVOtech ViVOpay USB device. diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 3c544782f60b..7796ad8e33c6 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -83,7 +83,10 @@ static int ark3116_write_reg(struct usb_serial *serial, usb_sndctrlpipe(serial->dev, 0), 0xfe, 0x40, val, reg, NULL, 0, ARK_TIMEOUT); - return result; + if (result) + return result; + + return 0; } static int ark3116_read_reg(struct usb_serial *serial, @@ -105,7 +108,7 @@ static int ark3116_read_reg(struct usb_serial *serial, return result; } - return buf[0]; + return 0; } static inline int calc_divisor(int bps) @@ -355,13 +358,13 @@ static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) /* read modem status */ result = ark3116_read_reg(serial, UART_MSR, buf); - if (result < 0) + if (result) goto err_close; priv->msr = *buf; /* read line status */ result = ark3116_read_reg(serial, UART_LSR, buf); - if (result < 0) + if (result) goto err_close; priv->lsr = *buf; @@ -394,31 +397,35 @@ err_free: return result; } +static int ark3116_get_serial_info(struct usb_serial_port *port, + struct serial_struct __user *retinfo) +{ + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + + tmp.type = PORT_16654; + tmp.line = port->minor; + tmp.port = port->port_number; + tmp.baud_base = 460800; + + if (copy_to_user(retinfo, &tmp, sizeof(tmp))) + return -EFAULT; + + return 0; +} + static int ark3116_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; - struct serial_struct serstruct; void __user *user_arg = (void __user *)arg; switch (cmd) { case TIOCGSERIAL: - /* XXX: Some of these values are probably wrong. */ - memset(&serstruct, 0, sizeof(serstruct)); - serstruct.type = PORT_16654; - serstruct.line = port->minor; - serstruct.port = port->port_number; - serstruct.custom_divisor = 0; - serstruct.baud_base = 460800; - - if (copy_to_user(user_arg, &serstruct, sizeof(serstruct))) - return -EFAULT; - - return 0; - case TIOCSSERIAL: - if (copy_from_user(&serstruct, user_arg, sizeof(serstruct))) - return -EFAULT; - return 0; + return ark3116_get_serial_info(port, user_arg); + default: + break; } return -ENOIOCTLCMD; diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index e4573b4c8935..4dfbff20bda4 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -41,6 +41,7 @@ #define F81534_MODEM_CONTROL_REG (0x04 + F81534_UART_BASE_ADDRESS) #define F81534_LINE_STATUS_REG (0x05 + F81534_UART_BASE_ADDRESS) #define F81534_MODEM_STATUS_REG (0x06 + F81534_UART_BASE_ADDRESS) +#define F81534_CLOCK_REG (0x08 + F81534_UART_BASE_ADDRESS) #define F81534_CONFIG1_REG (0x09 + F81534_UART_BASE_ADDRESS) #define F81534_DEF_CONF_ADDRESS_START 0x3000 @@ -51,13 +52,14 @@ #define F81534_CUSTOM_NO_CUSTOM_DATA 0xff #define F81534_CUSTOM_VALID_TOKEN 0xf0 #define F81534_CONF_OFFSET 1 +#define F81534_CONF_GPIO_OFFSET 4 #define F81534_MAX_DATA_BLOCK 64 #define F81534_MAX_BUS_RETRY 20 /* Default URB timeout for USB operations */ #define F81534_USB_MAX_RETRY 10 -#define F81534_USB_TIMEOUT 1000 +#define F81534_USB_TIMEOUT 2000 #define F81534_SET_GET_REGISTER 0xA0 #define F81534_NUM_PORT 4 @@ -96,16 +98,43 @@ #define F81534_CMD_READ 0x03 #define F81534_DEFAULT_BAUD_RATE 9600 -#define F81534_MAX_BAUDRATE 115200 +#define F81534_PORT_CONF_RS232 0 +#define F81534_PORT_CONF_RS485 BIT(0) +#define F81534_PORT_CONF_RS485_INVERT (BIT(0) | BIT(1)) +#define F81534_PORT_CONF_MODE_MASK GENMASK(1, 0) #define F81534_PORT_CONF_DISABLE_PORT BIT(3) #define F81534_PORT_CONF_NOT_EXIST_PORT BIT(7) #define F81534_PORT_UNAVAILABLE \ (F81534_PORT_CONF_DISABLE_PORT | F81534_PORT_CONF_NOT_EXIST_PORT) + #define F81534_1X_RXTRIGGER 0xc3 #define F81534_8X_RXTRIGGER 0xcf +/* + * F81532/534 Clock registers (offset +08h) + * + * Bit0: UART Enable (always on) + * Bit2-1: Clock source selector + * 00: 1.846MHz. + * 01: 18.46MHz. + * 10: 24MHz. + * 11: 14.77MHz. + * Bit4: Auto direction(RTS) control (RTS pin Low when TX) + * Bit5: Invert direction(RTS) when Bit4 enabled (RTS pin high when TX) + */ + +#define F81534_UART_EN BIT(0) +#define F81534_CLK_1_846_MHZ 0 +#define F81534_CLK_18_46_MHZ BIT(1) +#define F81534_CLK_24_MHZ BIT(2) +#define F81534_CLK_14_77_MHZ (BIT(1) | BIT(2)) +#define F81534_CLK_MASK GENMASK(2, 1) +#define F81534_CLK_TX_DELAY_1BIT BIT(3) +#define F81534_CLK_RS485_MODE BIT(4) +#define F81534_CLK_RS485_INVERT BIT(5) + static const struct usb_device_id f81534_id_table[] = { { USB_DEVICE(FINTEK_VENDOR_ID_1, FINTEK_DEVICE_ID) }, { USB_DEVICE(FINTEK_VENDOR_ID_2, FINTEK_DEVICE_ID) }, @@ -129,12 +158,35 @@ struct f81534_port_private { struct usb_serial_port *port; unsigned long tx_empty; spinlock_t msr_lock; + u32 baud_base; u8 shadow_mcr; u8 shadow_lcr; u8 shadow_msr; + u8 shadow_clk; u8 phy_num; }; +struct f81534_pin_data { + const u16 reg_addr; + const u8 reg_mask; +}; + +struct f81534_port_out_pin { + struct f81534_pin_data pin[3]; +}; + +/* Pin output value for M2/M1/M0(SD) */ +static const struct f81534_port_out_pin f81534_port_out_pins[] = { + { { { 0x2ae8, BIT(7) }, { 0x2a90, BIT(5) }, { 0x2a90, BIT(4) } } }, + { { { 0x2ae8, BIT(6) }, { 0x2ae8, BIT(0) }, { 0x2ae8, BIT(3) } } }, + { { { 0x2a90, BIT(0) }, { 0x2ae8, BIT(2) }, { 0x2a80, BIT(6) } } }, + { { { 0x2a90, BIT(3) }, { 0x2a90, BIT(2) }, { 0x2a90, BIT(1) } } }, +}; + +static u32 const baudrate_table[] = { 115200, 921600, 1152000, 1500000 }; +static u8 const clock_table[] = { F81534_CLK_1_846_MHZ, F81534_CLK_14_77_MHZ, + F81534_CLK_18_46_MHZ, F81534_CLK_24_MHZ }; + static int f81534_logic_to_phy_port(struct usb_serial *serial, struct usb_serial_port *port) { @@ -240,6 +292,36 @@ end: return status; } +static int f81534_set_mask_register(struct usb_serial *serial, u16 reg, + u8 mask, u8 data) +{ + int status; + u8 tmp; + + status = f81534_get_register(serial, reg, &tmp); + if (status) + return status; + + tmp &= ~mask; + tmp |= (mask & data); + + return f81534_set_register(serial, reg, tmp); +} + +static int f81534_set_phy_port_register(struct usb_serial *serial, int phy, + u16 reg, u8 data) +{ + return f81534_set_register(serial, reg + F81534_UART_OFFSET * phy, + data); +} + +static int f81534_get_phy_port_register(struct usb_serial *serial, int phy, + u16 reg, u8 *data) +{ + return f81534_get_register(serial, reg + F81534_UART_OFFSET * phy, + data); +} + static int f81534_set_port_register(struct usb_serial_port *port, u16 reg, u8 data) { @@ -460,13 +542,52 @@ static u32 f81534_calc_baud_divisor(u32 baudrate, u32 clockrate) return DIV_ROUND_CLOSEST(clockrate, baudrate); } -static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate, - u8 lcr) +static int f81534_find_clk(u32 baudrate) +{ + int idx; + + for (idx = 0; idx < ARRAY_SIZE(baudrate_table); ++idx) { + if (baudrate <= baudrate_table[idx] && + baudrate_table[idx] % baudrate == 0) + return idx; + } + + return -EINVAL; +} + +static int f81534_set_port_config(struct usb_serial_port *port, + struct tty_struct *tty, u32 baudrate, u32 old_baudrate, u8 lcr) { struct f81534_port_private *port_priv = usb_get_serial_port_data(port); u32 divisor; int status; + int i; + int idx; u8 value; + u32 baud_list[] = {baudrate, old_baudrate, F81534_DEFAULT_BAUD_RATE}; + + for (i = 0; i < ARRAY_SIZE(baud_list); ++i) { + idx = f81534_find_clk(baud_list[i]); + if (idx >= 0) { + baudrate = baud_list[i]; + tty_encode_baud_rate(tty, baudrate, baudrate); + break; + } + } + + if (idx < 0) + return -EINVAL; + + port_priv->baud_base = baudrate_table[idx]; + port_priv->shadow_clk &= ~F81534_CLK_MASK; + port_priv->shadow_clk |= clock_table[idx]; + + status = f81534_set_port_register(port, F81534_CLOCK_REG, + port_priv->shadow_clk); + if (status) { + dev_err(&port->dev, "CLOCK_REG setting failed\n"); + return status; + } if (baudrate <= 1200) value = F81534_1X_RXTRIGGER; /* 128 FIFO & TL: 1x */ @@ -482,7 +603,7 @@ static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate, if (baudrate <= 1200) value = UART_FCR_TRIGGER_1 | UART_FCR_ENABLE_FIFO; /* TL: 1 */ else - value = UART_FCR_R_TRIG_11 | UART_FCR_ENABLE_FIFO; /* TL: 14 */ + value = UART_FCR_TRIGGER_8 | UART_FCR_ENABLE_FIFO; /* TL: 8 */ status = f81534_set_port_register(port, F81534_FIFO_CONTROL_REG, value); @@ -491,7 +612,7 @@ static int f81534_set_port_config(struct usb_serial_port *port, u32 baudrate, return status; } - divisor = f81534_calc_baud_divisor(baudrate, F81534_MAX_BAUDRATE); + divisor = f81534_calc_baud_divisor(baudrate, port_priv->baud_base); mutex_lock(&port_priv->lcr_mutex); @@ -627,6 +748,70 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) } /* + * The F81532/534 will not report serial port to USB serial subsystem when + * H/W DCD/DSR/CTS/RI/RX pin connected to ground. + * + * To detect RX pin status, we'll enable MCR interal loopback, disable it and + * delayed for 60ms. It connected to ground If LSR register report UART_LSR_BI. + */ +static bool f81534_check_port_hw_disabled(struct usb_serial *serial, int phy) +{ + int status; + u8 old_mcr; + u8 msr; + u8 lsr; + u8 msr_mask; + + msr_mask = UART_MSR_DCD | UART_MSR_RI | UART_MSR_DSR | UART_MSR_CTS; + + status = f81534_get_phy_port_register(serial, phy, + F81534_MODEM_STATUS_REG, &msr); + if (status) + return false; + + if ((msr & msr_mask) != msr_mask) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_FIFO_CONTROL_REG, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + if (status) + return false; + + status = f81534_get_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, &old_mcr); + if (status) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, UART_MCR_LOOP); + if (status) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, 0x0); + if (status) + return false; + + msleep(60); + + status = f81534_get_phy_port_register(serial, phy, + F81534_LINE_STATUS_REG, &lsr); + if (status) + return false; + + status = f81534_set_phy_port_register(serial, phy, + F81534_MODEM_CONTROL_REG, old_mcr); + if (status) + return false; + + if ((lsr & UART_LSR_BI) == UART_LSR_BI) + return true; + + return false; +} + +/* * We had 2 generation of F81532/534 IC. All has an internal storage. * * 1st is pure USB-to-TTL RS232 IC and designed for 4 ports only, no any @@ -647,14 +832,14 @@ static int f81534_find_config_idx(struct usb_serial *serial, u8 *index) static int f81534_calc_num_ports(struct usb_serial *serial, struct usb_serial_endpoints *epds) { + struct f81534_serial_private *serial_priv; struct device *dev = &serial->interface->dev; int size_bulk_in = usb_endpoint_maxp(epds->bulk_in[0]); int size_bulk_out = usb_endpoint_maxp(epds->bulk_out[0]); - u8 setting[F81534_CUSTOM_DATA_SIZE]; - u8 setting_idx; u8 num_port = 0; + int index = 0; int status; - size_t i; + int i; if (size_bulk_out != F81534_WRITE_BUFFER_SIZE || size_bulk_in != F81534_MAX_RECEIVE_BLOCK_SIZE) { @@ -662,8 +847,16 @@ static int f81534_calc_num_ports(struct usb_serial *serial, return -ENODEV; } + serial_priv = devm_kzalloc(&serial->interface->dev, + sizeof(*serial_priv), GFP_KERNEL); + if (!serial_priv) + return -ENOMEM; + + usb_set_serial_data(serial, serial_priv); + mutex_init(&serial_priv->urb_mutex); + /* Check had custom setting */ - status = f81534_find_config_idx(serial, &setting_idx); + status = f81534_find_config_idx(serial, &serial_priv->setting_idx); if (status) { dev_err(&serial->interface->dev, "%s: find idx failed: %d\n", __func__, status); @@ -674,11 +867,12 @@ static int f81534_calc_num_ports(struct usb_serial *serial, * We'll read custom data only when data available, otherwise we'll * read default value instead. */ - if (setting_idx != F81534_CUSTOM_NO_CUSTOM_DATA) { + if (serial_priv->setting_idx != F81534_CUSTOM_NO_CUSTOM_DATA) { status = f81534_read_flash(serial, F81534_CUSTOM_ADDRESS_START + F81534_CONF_OFFSET, - sizeof(setting), setting); + sizeof(serial_priv->conf_data), + serial_priv->conf_data); if (status) { dev_err(&serial->interface->dev, "%s: get custom data failed: %d\n", @@ -688,13 +882,13 @@ static int f81534_calc_num_ports(struct usb_serial *serial, dev_dbg(&serial->interface->dev, "%s: read config from block: %d\n", __func__, - setting_idx); + serial_priv->setting_idx); } else { /* Read default board setting */ status = f81534_read_flash(serial, - F81534_DEF_CONF_ADDRESS_START, F81534_NUM_PORT, - setting); - + F81534_DEF_CONF_ADDRESS_START, + sizeof(serial_priv->conf_data), + serial_priv->conf_data); if (status) { dev_err(&serial->interface->dev, "%s: read failed: %d\n", __func__, @@ -708,7 +902,10 @@ static int f81534_calc_num_ports(struct usb_serial *serial, /* New style, find all possible ports */ for (i = 0; i < F81534_NUM_PORT; ++i) { - if (setting[i] & F81534_PORT_UNAVAILABLE) + if (f81534_check_port_hw_disabled(serial, i)) + serial_priv->conf_data[i] |= F81534_PORT_UNAVAILABLE; + + if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE) continue; ++num_port; @@ -720,6 +917,17 @@ static int f81534_calc_num_ports(struct usb_serial *serial, num_port = 4; /* Nothing found, oldest version IC */ } + /* Assign phy-to-logic mapping */ + for (i = 0; i < F81534_NUM_PORT; ++i) { + if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE) + continue; + + serial_priv->tty_idx[i] = index++; + dev_dbg(&serial->interface->dev, + "%s: phy_num: %d, tty_idx: %d\n", __func__, i, + serial_priv->tty_idx[i]); + } + /* * Setup bulk-out endpoint multiplexing. All ports share the same * bulk-out endpoint. @@ -741,6 +949,7 @@ static void f81534_set_termios(struct tty_struct *tty, u8 new_lcr = 0; int status; u32 baud; + u32 old_baud; if (C_BAUD(tty) == B0) f81534_update_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS); @@ -780,18 +989,14 @@ static void f81534_set_termios(struct tty_struct *tty, if (!baud) return; - if (baud > F81534_MAX_BAUDRATE) { - if (old_termios) - baud = tty_termios_baud_rate(old_termios); - else - baud = F81534_DEFAULT_BAUD_RATE; - - tty_encode_baud_rate(tty, baud, baud); - } + if (old_termios) + old_baud = tty_termios_baud_rate(old_termios); + else + old_baud = F81534_DEFAULT_BAUD_RATE; dev_dbg(&port->dev, "%s: baud: %d\n", __func__, baud); - status = f81534_set_port_config(port, baud, new_lcr); + status = f81534_set_port_config(port, tty, baud, old_baud, new_lcr); if (status < 0) { dev_err(&port->dev, "%s: set port config failed: %d\n", __func__, status); @@ -947,7 +1152,7 @@ static int f81534_get_serial_info(struct usb_serial_port *port, tmp.type = PORT_16550A; tmp.port = port->port_number; tmp.line = port->minor; - tmp.baud_base = F81534_MAX_BAUDRATE; + tmp.baud_base = port_priv->baud_base; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; @@ -1124,79 +1329,6 @@ static void f81534_write_usb_callback(struct urb *urb) } } -static int f81534_attach(struct usb_serial *serial) -{ - struct f81534_serial_private *serial_priv; - int index = 0; - int status; - int i; - - serial_priv = devm_kzalloc(&serial->interface->dev, - sizeof(*serial_priv), GFP_KERNEL); - if (!serial_priv) - return -ENOMEM; - - usb_set_serial_data(serial, serial_priv); - - mutex_init(&serial_priv->urb_mutex); - - /* Check had custom setting */ - status = f81534_find_config_idx(serial, &serial_priv->setting_idx); - if (status) { - dev_err(&serial->interface->dev, "%s: find idx failed: %d\n", - __func__, status); - return status; - } - - /* - * We'll read custom data only when data available, otherwise we'll - * read default value instead. - */ - if (serial_priv->setting_idx == F81534_CUSTOM_NO_CUSTOM_DATA) { - /* - * The default configuration layout: - * byte 0/1/2/3: uart setting - */ - status = f81534_read_flash(serial, - F81534_DEF_CONF_ADDRESS_START, - F81534_DEF_CONF_SIZE, - serial_priv->conf_data); - if (status) { - dev_err(&serial->interface->dev, - "%s: read reserve data failed: %d\n", - __func__, status); - return status; - } - } else { - /* Only read 8 bytes for mode & GPIO */ - status = f81534_read_flash(serial, - F81534_CUSTOM_ADDRESS_START + - F81534_CONF_OFFSET, - sizeof(serial_priv->conf_data), - serial_priv->conf_data); - if (status) { - dev_err(&serial->interface->dev, - "%s: idx: %d get data failed: %d\n", - __func__, serial_priv->setting_idx, - status); - return status; - } - } - - /* Assign phy-to-logic mapping */ - for (i = 0; i < F81534_NUM_PORT; ++i) { - if (serial_priv->conf_data[i] & F81534_PORT_UNAVAILABLE) - continue; - - serial_priv->tty_idx[i] = index++; - dev_dbg(&serial->interface->dev, - "%s: phy_num: %d, tty_idx: %d\n", __func__, i, - serial_priv->tty_idx[i]); - } - - return 0; -} - static void f81534_lsr_worker(struct work_struct *work) { struct f81534_port_private *port_priv; @@ -1212,15 +1344,54 @@ static void f81534_lsr_worker(struct work_struct *work) dev_warn(&port->dev, "read LSR failed: %d\n", status); } +static int f81534_set_port_output_pin(struct usb_serial_port *port) +{ + struct f81534_serial_private *serial_priv; + struct f81534_port_private *port_priv; + struct usb_serial *serial; + const struct f81534_port_out_pin *pins; + int status; + int i; + u8 value; + u8 idx; + + serial = port->serial; + serial_priv = usb_get_serial_data(serial); + port_priv = usb_get_serial_port_data(port); + + idx = F81534_CONF_GPIO_OFFSET + port_priv->phy_num; + value = serial_priv->conf_data[idx]; + pins = &f81534_port_out_pins[port_priv->phy_num]; + + for (i = 0; i < ARRAY_SIZE(pins->pin); ++i) { + status = f81534_set_mask_register(serial, + pins->pin[i].reg_addr, pins->pin[i].reg_mask, + value & BIT(i) ? pins->pin[i].reg_mask : 0); + if (status) + return status; + } + + dev_dbg(&port->dev, "Output pin (M0/M1/M2): %d\n", value); + return 0; +} + static int f81534_port_probe(struct usb_serial_port *port) { + struct f81534_serial_private *serial_priv; struct f81534_port_private *port_priv; int ret; + u8 value; + serial_priv = usb_get_serial_data(port->serial); port_priv = devm_kzalloc(&port->dev, sizeof(*port_priv), GFP_KERNEL); if (!port_priv) return -ENOMEM; + /* + * We'll make tx frame error when baud rate from 384~500kps. So we'll + * delay all tx data frame with 1bit. + */ + port_priv->shadow_clk = F81534_UART_EN | F81534_CLK_TX_DELAY_1BIT; spin_lock_init(&port_priv->msr_lock); mutex_init(&port_priv->mcr_mutex); mutex_init(&port_priv->lcr_mutex); @@ -1248,7 +1419,25 @@ static int f81534_port_probe(struct usb_serial_port *port) if (ret) return ret; - return 0; + value = serial_priv->conf_data[port_priv->phy_num]; + switch (value & F81534_PORT_CONF_MODE_MASK) { + case F81534_PORT_CONF_RS485_INVERT: + port_priv->shadow_clk |= F81534_CLK_RS485_MODE | + F81534_CLK_RS485_INVERT; + dev_dbg(&port->dev, "RS485 invert mode\n"); + break; + case F81534_PORT_CONF_RS485: + port_priv->shadow_clk |= F81534_CLK_RS485_MODE; + dev_dbg(&port->dev, "RS485 mode\n"); + break; + + default: + case F81534_PORT_CONF_RS232: + dev_dbg(&port->dev, "RS232 mode\n"); + break; + } + + return f81534_set_port_output_pin(port); } static int f81534_port_remove(struct usb_serial_port *port) @@ -1387,7 +1576,6 @@ static struct usb_serial_driver f81534_device = { .write = f81534_write, .tx_empty = f81534_tx_empty, .calc_num_ports = f81534_calc_num_ports, - .attach = f81534_attach, .port_probe = f81534_port_probe, .port_remove = f81534_port_remove, .break_ctl = f81534_break_ctl, diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index fc68952c994a..f58c4ff6b387 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1691,7 +1691,7 @@ static DEVICE_ATTR_RW(latency_timer); /* Write an event character directly to the FTDI register. The ASCII value is in the low 8 bits, with the enable bit in the 9th bit. */ -static ssize_t store_event_char(struct device *dev, +static ssize_t event_char_store(struct device *dev, struct device_attribute *attr, const char *valbuf, size_t count) { struct usb_serial_port *port = to_usb_serial_port(dev); @@ -1718,7 +1718,7 @@ static ssize_t store_event_char(struct device *dev, return count; } -static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_event_char); +static DEVICE_ATTR_WO(event_char); static int create_sysfs_attrs(struct usb_serial_port *port) { diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 219265ce3711..17283f4b4779 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2282,7 +2282,6 @@ static int write_cmd_usb(struct edgeport_port *edge_port, /* something went wrong */ dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n", __func__, status); - usb_kill_urb(urb); usb_free_urb(urb); atomic_dec(&CmdUrbs); return status; diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 397a8012ffa3..62c91e360baf 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -472,7 +472,6 @@ static int iuu_clk(struct usb_serial_port *port, int dwFrq) } } P2 = ((P - PO) / 2) - 4; - DIV = DIV; PUMP = 0x04; PBmsb = (P2 >> 8 & 0x03); PBlsb = P2 & 0xFF; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index f4df3d5bf69c..bd57630e67e2 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -518,7 +518,7 @@ static void parport_mos7715_write_control(struct parport *pp, unsigned char d) static unsigned char parport_mos7715_read_control(struct parport *pp) { - struct mos7715_parport *mos_parport = pp->private_data; + struct mos7715_parport *mos_parport; __u8 dcr; spin_lock(&release_lock); @@ -554,7 +554,7 @@ static unsigned char parport_mos7715_frob_control(struct parport *pp, static unsigned char parport_mos7715_read_status(struct parport *pp) { unsigned char status; - struct mos7715_parport *mos_parport = pp->private_data; + struct mos7715_parport *mos_parport; spin_lock(&release_lock); mos_parport = pp->private_data; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index b6320e3be429..5db8ed517e0e 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -380,6 +380,9 @@ static void option_instat_callback(struct urb *urb); #define FOUR_G_SYSTEMS_PRODUCT_W14 0x9603 #define FOUR_G_SYSTEMS_PRODUCT_W100 0x9b01 +/* Fujisoft products */ +#define FUJISOFT_PRODUCT_FS040U 0x9b02 + /* iBall 3.5G connect wireless modem */ #define IBALL_3_5G_CONNECT 0x9605 @@ -1894,6 +1897,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(LONGCHEER_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W100), .driver_info = (kernel_ulong_t)&four_g_w100_blacklist }, + {USB_DEVICE(LONGCHEER_VENDOR_ID, FUJISOFT_PRODUCT_FS040U), + .driver_info = (kernel_ulong_t)&net_intf3_blacklist}, { 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 }, diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 57ae832a49ff..46dd09da2434 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -38,6 +38,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_DCU11) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ3) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_CHILITAG) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_PHAROS) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ALDIGA) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MMX) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index f98fd84890de..fcd72396a7b6 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -12,6 +12,7 @@ #define PL2303_PRODUCT_ID_DCU11 0x1234 #define PL2303_PRODUCT_ID_PHAROS 0xaaa0 #define PL2303_PRODUCT_ID_RSAQ3 0xaaa2 +#define PL2303_PRODUCT_ID_CHILITAG 0xaaa8 #define PL2303_PRODUCT_ID_ALDIGA 0x0611 #define PL2303_PRODUCT_ID_MMX 0x0612 #define PL2303_PRODUCT_ID_GPRS 0x0609 diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index 74172fe158df..4ef79e29cb26 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -77,6 +77,11 @@ DEVICE(vivopay, VIVOPAY_IDS); { USB_DEVICE(0x22b8, 0x2c64) } /* Motorola V950 phone */ DEVICE(moto_modem, MOTO_IDS); +/* Motorola Tetra driver */ +#define MOTOROLA_TETRA_IDS() \ + { USB_DEVICE(0x0cad, 0x9011) } /* Motorola Solutions TETRA PEI */ +DEVICE(motorola_tetra, MOTOROLA_TETRA_IDS); + /* Novatel Wireless GPS driver */ #define NOVATEL_IDS() \ { USB_DEVICE(0x09d7, 0x0100) } /* NovAtel FlexPack GPS */ @@ -107,6 +112,7 @@ static struct usb_serial_driver * const serial_drivers[] = { &google_device, &vivopay_device, &moto_modem_device, + &motorola_tetra_device, &novatel_gps_device, &hp4x_device, &suunto_device, @@ -122,6 +128,7 @@ static const struct usb_device_id id_table[] = { GOOGLE_IDS(), VIVOPAY_IDS(), MOTO_IDS(), + MOTOROLA_TETRA_IDS(), NOVATEL_IDS(), HP4X_IDS(), SUUNTO_IDS(), diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index e5a4969d15ae..d7f50b7a079e 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -25,9 +25,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #include <linux/device.h> diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index 8833cd4f78b6..6d64f342f587 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -22,9 +22,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #ifndef _DEBUG_H_ diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 93a6bcf77806..f8f9ce8dc710 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -18,9 +18,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #include <linux/errno.h> diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index e4cf28efb4a7..2dbf9c7d9749 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -18,9 +18,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #include "usb.h" diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index f3f2a93f52e1..9033e505db7f 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -25,9 +25,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #include <linux/highmem.h> diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h index 9198396e8c6e..072f1ffda2af 100644 --- a/drivers/usb/storage/protocol.h +++ b/drivers/usb/storage/protocol.h @@ -19,9 +19,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #ifndef _PROTOCOL_H_ diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 585efd120193..c267f2812a04 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -26,9 +26,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #include <linux/module.h> diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h index bf99c6201331..2bc5ea045bf7 100644 --- a/drivers/usb/storage/scsiglue.h +++ b/drivers/usb/storage/scsiglue.h @@ -19,9 +19,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #ifndef _SCSIGLUE_H_ diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c index daf62448483f..6ac60abd2e15 100644 --- a/drivers/usb/storage/sierra_ms.c +++ b/drivers/usb/storage/sierra_ms.c @@ -90,7 +90,7 @@ static void debug_swoc(const struct device *dev, struct swoc_info *swocInfo) } -static ssize_t show_truinst(struct device *dev, struct device_attribute *attr, +static ssize_t truinst_show(struct device *dev, struct device_attribute *attr, char *buf) { struct swoc_info *swocInfo; @@ -122,7 +122,7 @@ static ssize_t show_truinst(struct device *dev, struct device_attribute *attr, } return result; } -static DEVICE_ATTR(truinst, S_IRUGO, show_truinst, NULL); +static DEVICE_ATTR_RO(truinst); int sierra_ms_init(struct us_data *us) { diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index d947957f3635..96cb0409dd89 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -26,9 +26,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #include <linux/sched.h> diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index f559dc575f4f..fb3bb4ee4ccf 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -19,9 +19,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #ifndef _TRANSPORT_H_ diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 5d04c40ee40a..3b1b9695177a 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1076,20 +1076,19 @@ static int uas_post_reset(struct usb_interface *intf) return 0; err = uas_configure_endpoints(devinfo); - if (err) { + if (err && err != ENODEV) shost_printk(KERN_ERR, shost, "%s: alloc streams error %d after reset", __func__, err); - return 1; - } + /* we must unblock the host in every case lest we deadlock */ spin_lock_irqsave(shost->host_lock, flags); scsi_report_bus_reset(shost, 0); spin_unlock_irqrestore(shost->host_lock, flags); scsi_unblock_requests(shost); - return 0; + return err ? 1 : 0; } static int uas_suspend(struct usb_interface *intf, pm_message_t message) diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index f72d045ee9ef..264af199aec8 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -8,9 +8,6 @@ * * Initial work by: * (c) 2000 Adam J. Richter (adam@yggdrasil.com), Yggdrasil Computing, Inc. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ /* @@ -31,10 +28,7 @@ * explanation of the reason for the entry), * - a copy of /sys/kernel/debug/usb/devices with your device plugged in * running with this patch. - * Send your submission to either Phil Dibowitz <phil@ipom.com> or - * Alan Stern <stern@rowland.harvard.edu>, and don't forget to CC: the - * USB development list <linux-usb@vger.kernel.org> and the USB storage list - * <usb-storage@lists.one-eyed-alien.net> + * Send your submission to the USB development list <linux-usb@vger.kernel.org> */ /* diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index a7d08ae0adad..38434d88954a 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -38,20 +38,6 @@ UNUSUAL_DEV(0x0984, 0x0301, 0x0128, 0x0128, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_IGNORE_UAS), -/* https://bugzilla.kernel.org/show_bug.cgi?id=79511 */ -UNUSUAL_DEV(0x0bc2, 0x2312, 0x0000, 0x9999, - "Seagate", - "Expansion Desk", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* https://bbs.archlinux.org/viewtopic.php?id=183190 */ -UNUSUAL_DEV(0x0bc2, 0x3312, 0x0000, 0x9999, - "Seagate", - "Expansion Desk", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - /* Reported-by: David Webb <djw@noc.ac.uk> */ UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999, "Seagate", @@ -59,55 +45,6 @@ UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_LUNS), -/* Reported-by: Hans de Goede <hdegoede@redhat.com> */ -UNUSUAL_DEV(0x0bc2, 0x3320, 0x0000, 0x9999, - "Seagate", - "Expansion Desk", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* Reported-by: Bogdan Mihalcea <bogdan.mihalcea@infim.ro> */ -UNUSUAL_DEV(0x0bc2, 0xa003, 0x0000, 0x9999, - "Seagate", - "Backup Plus", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* Reported-by: Marcin ZajÄ…czkowski <mszpak@wp.pl> */ -UNUSUAL_DEV(0x0bc2, 0xa013, 0x0000, 0x9999, - "Seagate", - "Backup Plus", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* Reported-by: Hans de Goede <hdegoede@redhat.com> */ -UNUSUAL_DEV(0x0bc2, 0xa0a4, 0x0000, 0x9999, - "Seagate", - "Backup Plus Desk", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* https://bbs.archlinux.org/viewtopic.php?id=183190 */ -UNUSUAL_DEV(0x0bc2, 0xab20, 0x0000, 0x9999, - "Seagate", - "Backup+ BK", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* https://bbs.archlinux.org/viewtopic.php?id=183190 */ -UNUSUAL_DEV(0x0bc2, 0xab21, 0x0000, 0x9999, - "Seagate", - "Backup+ BK", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - -/* Reported-by: G. Richard Bellamy <rbellamy@pteradigm.com> */ -UNUSUAL_DEV(0x0bc2, 0xab2a, 0x0000, 0x9999, - "Seagate", - "BUP Fast HDD", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), - /* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */ UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999, "Initio Corporation", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index a0c07e05a8f1..9a79cd9762f3 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -28,9 +28,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #ifdef CONFIG_USB_STORAGE_DEBUG @@ -211,8 +208,8 @@ int usb_stor_reset_resume(struct usb_interface *iface) usb_stor_report_bus_reset(us); /* - * FIXME: Notify the subdrivers that they need to reinitialize - * the device + * If any of the subdrivers implemented a reinitialization scheme, + * this is where the callback would be invoked. */ return 0; } @@ -243,8 +240,8 @@ int usb_stor_post_reset(struct usb_interface *iface) usb_stor_report_bus_reset(us); /* - * FIXME: Notify the subdrivers that they need to reinitialize - * the device + * If any of the subdrivers implemented a reinitialization scheme, + * this is where the callback would be invoked. */ mutex_unlock(&us->dev_mutex); diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 90133e16bec5..85052cd66839 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -22,9 +22,6 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #ifndef _USB_H_ diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c index 83ad01747eed..cfd12e523678 100644 --- a/drivers/usb/storage/usual-tables.c +++ b/drivers/usb/storage/usual-tables.c @@ -4,9 +4,6 @@ * Usual Tables File for usb-storage and libusual * * Copyright (C) 2009 Alan Stern (stern@rowland.harvard.edu) - * - * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more - * information about this driver. */ #include <linux/kernel.h> diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c index 72cb060b3fca..9ce4756adad6 100644 --- a/drivers/usb/typec/fusb302/fusb302.c +++ b/drivers/usb/typec/fusb302/fusb302.c @@ -16,7 +16,6 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of_device.h> -#include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/pinctrl/consumer.h> #include <linux/power_supply.h> @@ -1543,6 +1542,21 @@ static int fusb302_pd_read_message(struct fusb302_chip *chip, fusb302_log(chip, "PD message header: %x", msg->header); fusb302_log(chip, "PD message len: %d", len); + /* + * Check if we've read off a GoodCRC message. If so then indicate to + * TCPM that the previous transmission has completed. Otherwise we pass + * the received message over to TCPM for processing. + * + * We make this check here instead of basing the reporting decision on + * the IRQ event type, as it's possible for the chip to report the + * TX_SUCCESS and GCRCSENT events out of order on occasion, so we need + * to check the message type to ensure correct reporting to TCPM. + */ + if ((!len) && (pd_header_type_le(msg->header) == PD_CTRL_GOOD_CRC)) + tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS); + else + tcpm_pd_receive(chip->tcpm_port, msg); + return ret; } @@ -1650,13 +1664,12 @@ static irqreturn_t fusb302_irq_intn(int irq, void *dev_id) if (interrupta & FUSB_REG_INTERRUPTA_TX_SUCCESS) { fusb302_log(chip, "IRQ: PD tx success"); - /* read out the received good CRC */ ret = fusb302_pd_read_message(chip, &pd_msg); if (ret < 0) { - fusb302_log(chip, "cannot read in GCRC, ret=%d", ret); + fusb302_log(chip, + "cannot read in PD message, ret=%d", ret); goto done; } - tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS); } if (interrupta & FUSB_REG_INTERRUPTA_HARDRESET) { @@ -1677,7 +1690,6 @@ static irqreturn_t fusb302_irq_intn(int irq, void *dev_id) "cannot read in PD message, ret=%d", ret); goto done; } - tcpm_pd_receive(chip->tcpm_port, &pd_msg); } done: mutex_unlock(&chip->lock); @@ -1731,24 +1743,24 @@ static int init_gpio(struct fusb302_chip *chip) chip->gpio_int_n = of_get_named_gpio(node, "fcs,int_n", 0); if (!gpio_is_valid(chip->gpio_int_n)) { ret = chip->gpio_int_n; - fusb302_log(chip, "cannot get named GPIO Int_N, ret=%d", ret); + dev_err(chip->dev, "cannot get named GPIO Int_N, ret=%d", ret); return ret; } ret = devm_gpio_request(chip->dev, chip->gpio_int_n, "fcs,int_n"); if (ret < 0) { - fusb302_log(chip, "cannot request GPIO Int_N, ret=%d", ret); + dev_err(chip->dev, "cannot request GPIO Int_N, ret=%d", ret); return ret; } ret = gpio_direction_input(chip->gpio_int_n); if (ret < 0) { - fusb302_log(chip, - "cannot set GPIO Int_N to input, ret=%d", ret); + dev_err(chip->dev, + "cannot set GPIO Int_N to input, ret=%d", ret); return ret; } ret = gpio_to_irq(chip->gpio_int_n); if (ret < 0) { - fusb302_log(chip, - "cannot request IRQ for GPIO Int_N, ret=%d", ret); + dev_err(chip->dev, + "cannot request IRQ for GPIO Int_N, ret=%d", ret); return ret; } chip->gpio_int_n_irq = ret; @@ -1845,7 +1857,7 @@ static int fusb302_probe(struct i2c_client *client, chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev); if (IS_ERR(chip->tcpm_port)) { ret = PTR_ERR(chip->tcpm_port); - fusb302_log(chip, "cannot register tcpm port, ret=%d", ret); + dev_err(dev, "cannot register tcpm port, ret=%d", ret); goto destroy_workqueue; } @@ -1854,8 +1866,7 @@ static int fusb302_probe(struct i2c_client *client, IRQF_ONESHOT | IRQF_TRIGGER_LOW, "fsc_interrupt_int_n", chip); if (ret < 0) { - fusb302_log(chip, - "cannot request IRQ for GPIO Int_N, ret=%d", ret); + dev_err(dev, "cannot request IRQ for GPIO Int_N, ret=%d", ret); goto tcpm_unregister_port; } enable_irq_wake(chip->gpio_int_n_irq); diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c index c166fc77dfb8..f4d563ee7690 100644 --- a/drivers/usb/typec/tcpm.c +++ b/drivers/usb/typec/tcpm.c @@ -252,6 +252,9 @@ struct tcpm_port { unsigned int nr_src_pdo; u32 snk_pdo[PDO_MAX_OBJECTS]; unsigned int nr_snk_pdo; + unsigned int nr_fixed; /* number of fixed sink PDOs */ + unsigned int nr_var; /* number of variable sink PDOs */ + unsigned int nr_batt; /* number of battery sink PDOs */ u32 snk_vdo[VDO_MAX_OBJECTS]; unsigned int nr_snk_vdo; @@ -1247,6 +1250,100 @@ static void vdm_state_machine_work(struct work_struct *work) mutex_unlock(&port->lock); } +enum pdo_err { + PDO_NO_ERR, + PDO_ERR_NO_VSAFE5V, + PDO_ERR_VSAFE5V_NOT_FIRST, + PDO_ERR_PDO_TYPE_NOT_IN_ORDER, + PDO_ERR_FIXED_NOT_SORTED, + PDO_ERR_VARIABLE_BATT_NOT_SORTED, + PDO_ERR_DUPE_PDO, +}; + +static const char * const pdo_err_msg[] = { + [PDO_ERR_NO_VSAFE5V] = + " err: source/sink caps should atleast have vSafe5V", + [PDO_ERR_VSAFE5V_NOT_FIRST] = + " err: vSafe5V Fixed Supply Object Shall always be the first object", + [PDO_ERR_PDO_TYPE_NOT_IN_ORDER] = + " err: PDOs should be in the following order: Fixed; Battery; Variable", + [PDO_ERR_FIXED_NOT_SORTED] = + " err: Fixed supply pdos should be in increasing order of their fixed voltage", + [PDO_ERR_VARIABLE_BATT_NOT_SORTED] = + " err: Variable/Battery supply pdos should be in increasing order of their minimum voltage", + [PDO_ERR_DUPE_PDO] = + " err: Variable/Batt supply pdos cannot have same min/max voltage", +}; + +static enum pdo_err tcpm_caps_err(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo) +{ + unsigned int i; + + /* Should at least contain vSafe5v */ + if (nr_pdo < 1) + return PDO_ERR_NO_VSAFE5V; + + /* The vSafe5V Fixed Supply Object Shall always be the first object */ + if (pdo_type(pdo[0]) != PDO_TYPE_FIXED || + pdo_fixed_voltage(pdo[0]) != VSAFE5V) + return PDO_ERR_VSAFE5V_NOT_FIRST; + + for (i = 1; i < nr_pdo; i++) { + if (pdo_type(pdo[i]) < pdo_type(pdo[i - 1])) { + return PDO_ERR_PDO_TYPE_NOT_IN_ORDER; + } else if (pdo_type(pdo[i]) == pdo_type(pdo[i - 1])) { + enum pd_pdo_type type = pdo_type(pdo[i]); + + switch (type) { + /* + * The remaining Fixed Supply Objects, if + * present, shall be sent in voltage order; + * lowest to highest. + */ + case PDO_TYPE_FIXED: + if (pdo_fixed_voltage(pdo[i]) <= + pdo_fixed_voltage(pdo[i - 1])) + return PDO_ERR_FIXED_NOT_SORTED; + break; + /* + * The Battery Supply Objects and Variable + * supply, if present shall be sent in Minimum + * Voltage order; lowest to highest. + */ + case PDO_TYPE_VAR: + case PDO_TYPE_BATT: + if (pdo_min_voltage(pdo[i]) < + pdo_min_voltage(pdo[i - 1])) + return PDO_ERR_VARIABLE_BATT_NOT_SORTED; + else if ((pdo_min_voltage(pdo[i]) == + pdo_min_voltage(pdo[i - 1])) && + (pdo_max_voltage(pdo[i]) == + pdo_min_voltage(pdo[i - 1]))) + return PDO_ERR_DUPE_PDO; + break; + default: + tcpm_log_force(port, " Unknown pdo type"); + } + } + } + + return PDO_NO_ERR; +} + +static int tcpm_validate_caps(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo) +{ + enum pdo_err err_index = tcpm_caps_err(port, pdo, nr_pdo); + + if (err_index != PDO_NO_ERR) { + tcpm_log_force(port, " %s", pdo_err_msg[err_index]); + return -EINVAL; + } + + return 0; +} + /* * PD (data, control) command handling functions */ @@ -1269,6 +1366,9 @@ static void tcpm_pd_data_request(struct tcpm_port *port, tcpm_log_source_caps(port); + tcpm_validate_caps(port, port->source_caps, + port->nr_source_caps); + /* * This message may be received even if VBUS is not * present. This is quite unexpected; see USB PD @@ -1670,39 +1770,90 @@ static int tcpm_pd_check_request(struct tcpm_port *port) return 0; } -static int tcpm_pd_select_pdo(struct tcpm_port *port) +#define min_power(x, y) min(pdo_max_power(x), pdo_max_power(y)) +#define min_current(x, y) min(pdo_max_current(x), pdo_max_current(y)) + +static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo, + int *src_pdo) { - unsigned int i, max_mw = 0, max_mv = 0; + unsigned int i, j, max_mw = 0, max_mv = 0, mw = 0, mv = 0, ma = 0; int ret = -EINVAL; /* - * Select the source PDO providing the most power while staying within - * the board's voltage limits. Prefer PDO providing exp + * Select the source PDO providing the most power which has a + * matchig sink cap. */ for (i = 0; i < port->nr_source_caps; i++) { u32 pdo = port->source_caps[i]; enum pd_pdo_type type = pdo_type(pdo); - unsigned int mv, ma, mw; - - if (type == PDO_TYPE_FIXED) - mv = pdo_fixed_voltage(pdo); - else - mv = pdo_min_voltage(pdo); - - if (type == PDO_TYPE_BATT) { - mw = pdo_max_power(pdo); - } else { - ma = min(pdo_max_current(pdo), - port->max_snk_ma); - mw = ma * mv / 1000; - } - /* Perfer higher voltages if available */ - if ((mw > max_mw || (mw == max_mw && mv > max_mv)) && - mv <= port->max_snk_mv) { - ret = i; - max_mw = mw; - max_mv = mv; + if (type == PDO_TYPE_FIXED) { + for (j = 0; j < port->nr_fixed; j++) { + if (pdo_fixed_voltage(pdo) == + pdo_fixed_voltage(port->snk_pdo[j])) { + ma = min_current(pdo, port->snk_pdo[j]); + mv = pdo_fixed_voltage(pdo); + mw = ma * mv / 1000; + if (mw > max_mw || + (mw == max_mw && mv > max_mv)) { + ret = 0; + *src_pdo = i; + *sink_pdo = j; + max_mw = mw; + max_mv = mv; + } + /* There could only be one fixed pdo + * at a specific voltage level. + * So breaking here. + */ + break; + } + } + } else if (type == PDO_TYPE_BATT) { + for (j = port->nr_fixed; + j < port->nr_fixed + + port->nr_batt; + j++) { + if (pdo_min_voltage(pdo) >= + pdo_min_voltage(port->snk_pdo[j]) && + pdo_max_voltage(pdo) <= + pdo_max_voltage(port->snk_pdo[j])) { + mw = min_power(pdo, port->snk_pdo[j]); + mv = pdo_min_voltage(pdo); + if (mw > max_mw || + (mw == max_mw && mv > max_mv)) { + ret = 0; + *src_pdo = i; + *sink_pdo = j; + max_mw = mw; + max_mv = mv; + } + } + } + } else if (type == PDO_TYPE_VAR) { + for (j = port->nr_fixed + + port->nr_batt; + j < port->nr_fixed + + port->nr_batt + + port->nr_var; + j++) { + if (pdo_min_voltage(pdo) >= + pdo_min_voltage(port->snk_pdo[j]) && + pdo_max_voltage(pdo) <= + pdo_max_voltage(port->snk_pdo[j])) { + ma = min_current(pdo, port->snk_pdo[j]); + mv = pdo_min_voltage(pdo); + mw = ma * mv / 1000; + if (mw > max_mw || + (mw == max_mw && mv > max_mv)) { + ret = 0; + *src_pdo = i; + *sink_pdo = j; + max_mw = mw; + max_mv = mv; + } + } + } } } @@ -1714,13 +1865,14 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) unsigned int mv, ma, mw, flags; unsigned int max_ma, max_mw; enum pd_pdo_type type; - int index; - u32 pdo; + int src_pdo_index, snk_pdo_index; + u32 pdo, matching_snk_pdo; - index = tcpm_pd_select_pdo(port); - if (index < 0) + if (tcpm_pd_select_pdo(port, &snk_pdo_index, &src_pdo_index) < 0) return -EINVAL; - pdo = port->source_caps[index]; + + pdo = port->source_caps[src_pdo_index]; + matching_snk_pdo = port->snk_pdo[snk_pdo_index]; type = pdo_type(pdo); if (type == PDO_TYPE_FIXED) @@ -1728,26 +1880,28 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) else mv = pdo_min_voltage(pdo); - /* Select maximum available current within the board's power limit */ + /* Select maximum available current within the sink pdo's limit */ if (type == PDO_TYPE_BATT) { - mw = pdo_max_power(pdo); - ma = 1000 * min(mw, port->max_snk_mw) / mv; + mw = min_power(pdo, matching_snk_pdo); + ma = 1000 * mw / mv; } else { - ma = min(pdo_max_current(pdo), - 1000 * port->max_snk_mw / mv); + ma = min_current(pdo, matching_snk_pdo); + mw = ma * mv / 1000; } - ma = min(ma, port->max_snk_ma); flags = RDO_USB_COMM | RDO_NO_SUSPEND; /* Set mismatch bit if offered power is less than operating power */ - mw = ma * mv / 1000; max_ma = ma; max_mw = mw; if (mw < port->operating_snk_mw) { flags |= RDO_CAP_MISMATCH; - max_mw = port->operating_snk_mw; - max_ma = max_mw * 1000 / mv; + if (type == PDO_TYPE_BATT && + (pdo_max_power(matching_snk_pdo) > pdo_max_power(pdo))) + max_mw = pdo_max_power(matching_snk_pdo); + else if (pdo_max_current(matching_snk_pdo) > + pdo_max_current(pdo)) + max_ma = pdo_max_current(matching_snk_pdo); } tcpm_log(port, "cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d", @@ -1756,16 +1910,16 @@ static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) port->polarity); if (type == PDO_TYPE_BATT) { - *rdo = RDO_BATT(index + 1, mw, max_mw, flags); + *rdo = RDO_BATT(src_pdo_index + 1, mw, max_mw, flags); tcpm_log(port, "Requesting PDO %d: %u mV, %u mW%s", - index, mv, mw, + src_pdo_index, mv, mw, flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); } else { - *rdo = RDO_FIXED(index + 1, ma, max_ma, flags); + *rdo = RDO_FIXED(src_pdo_index + 1, ma, max_ma, flags); tcpm_log(port, "Requesting PDO %d: %u mV, %u mA%s", - index, mv, ma, + src_pdo_index, mv, ma, flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); } @@ -3435,9 +3589,12 @@ static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo, return nr_vdo; } -void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo) +int tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo) { + if (tcpm_validate_caps(port, pdo, nr_pdo)) + return -EINVAL; + mutex_lock(&port->lock); port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, pdo, nr_pdo); switch (port->state) { @@ -3457,16 +3614,20 @@ void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, break; } mutex_unlock(&port->lock); + return 0; } EXPORT_SYMBOL_GPL(tcpm_update_source_capabilities); -void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo, - unsigned int max_snk_mv, - unsigned int max_snk_ma, - unsigned int max_snk_mw, - unsigned int operating_snk_mw) +int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo, + unsigned int max_snk_mv, + unsigned int max_snk_ma, + unsigned int max_snk_mw, + unsigned int operating_snk_mw) { + if (tcpm_validate_caps(port, pdo, nr_pdo)) + return -EINVAL; + mutex_lock(&port->lock); port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, pdo, nr_pdo); port->max_snk_mv = max_snk_mv; @@ -3485,9 +3646,23 @@ void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, break; } mutex_unlock(&port->lock); + return 0; } EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities); +static int nr_type_pdos(const u32 *pdo, unsigned int nr_pdo, + enum pd_pdo_type type) +{ + int count = 0; + int i; + + for (i = 0; i < nr_pdo; i++) { + if (pdo_type(pdo[i]) == type) + count++; + } + return count; +} + struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) { struct tcpm_port *port; @@ -3520,11 +3695,28 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) init_completion(&port->tx_complete); init_completion(&port->swap_complete); + tcpm_debugfs_init(port); + if (tcpm_validate_caps(port, tcpc->config->src_pdo, + tcpc->config->nr_src_pdo) || + tcpm_validate_caps(port, tcpc->config->snk_pdo, + tcpc->config->nr_snk_pdo)) { + err = -EINVAL; + goto out_destroy_wq; + } port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcpc->config->src_pdo, tcpc->config->nr_src_pdo); port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo, tcpc->config->nr_snk_pdo); + port->nr_fixed = nr_type_pdos(port->snk_pdo, + port->nr_snk_pdo, + PDO_TYPE_FIXED); + port->nr_var = nr_type_pdos(port->snk_pdo, + port->nr_snk_pdo, + PDO_TYPE_VAR); + port->nr_batt = nr_type_pdos(port->snk_pdo, + port->nr_snk_pdo, + PDO_TYPE_BATT); port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcpc->config->snk_vdo, tcpc->config->nr_snk_vdo); @@ -3575,7 +3767,6 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) } } - tcpm_debugfs_init(port); mutex_lock(&port->lock); tcpm_init(port); mutex_unlock(&port->lock); diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/typec_wcove.c index a8d479eb221a..2e990e0d917d 100644 --- a/drivers/usb/typec/typec_wcove.c +++ b/drivers/usb/typec/typec_wcove.c @@ -556,10 +556,8 @@ static const u32 src_pdo[] = { }; static const u32 snk_pdo[] = { - PDO_FIXED(12000, 3000, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | + PDO_FIXED(5000, 500, PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM), - PDO_BATT(4750, 12000, 15000), - PDO_VAR(4750, 12000, 3000), }; static struct tcpc_config wcove_typec_config = { diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c index e31a6f204397..49e552472c3f 100644 --- a/drivers/usb/usbip/stub_dev.c +++ b/drivers/usb/usbip/stub_dev.c @@ -39,7 +39,7 @@ static DEVICE_ATTR_RO(usbip_status); * is used to transfer usbip requests by kernel threads. -1 is a magic number * by which usbip connection is finished. */ -static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, +static ssize_t usbip_sockfd_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct stub_device *sdev = dev_get_drvdata(dev); @@ -103,7 +103,7 @@ err: spin_unlock_irq(&sdev->ud.lock); return -EINVAL; } -static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd); +static DEVICE_ATTR_WO(usbip_sockfd); static int stub_add_files(struct device *dev) { diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index 6c5a59313999..97b09a42a10c 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -428,9 +428,6 @@ static void masking_bogus_flags(struct urb *urb) if (is_out) allowed |= URB_ZERO_PACKET; /* FALLTHROUGH */ - case USB_ENDPOINT_XFER_CONTROL: - allowed |= URB_NO_FSBR; /* only affects UHCI */ - /* FALLTHROUGH */ default: /* all non-iso endpoints */ if (!is_out) allowed |= URB_SHORT_NOT_OK; diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index ee2bbce24584..9756752c0681 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -314,7 +314,6 @@ int usbip_recv(struct socket *sock, void *buf, int size) usbip_dbg_xmit("enter\n"); do { - msg_data_left(&msg); sock->sk->sk_allocation = GFP_NOIO; result = sock_recvmsg(sock, &msg, MSG_WAITALL); diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index 112ebb90d8c9..44cd64518925 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -30,7 +30,7 @@ struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum) /* fall through */ case -ECONNRESET: dev_dbg(&urb->dev->dev, - "urb seq# %u was unlinked %ssynchronuously\n", + "urb seq# %u was unlinked %ssynchronously\n", seqnum, status == -ENOENT ? "" : "a"); break; case -EINPROGRESS: diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index 091f76b7196d..48808388ec33 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -17,10 +17,10 @@ /* * output example: - * hub port sta spd dev sockfd local_busid - * hs 0000 004 000 00000000 3 1-2.3 + * hub port sta spd dev sockfd local_busid + * hs 0000 004 000 00000000 000003 1-2.3 * ................................................ - * ss 0008 004 000 00000000 4 2-3.4 + * ss 0008 004 000 00000000 000004 2-3.4 * ................................................ * * Output includes socket fd instead of socket pointer address to avoid @@ -44,13 +44,13 @@ static void port_show_vhci(char **out, int hub, int port, struct vhci_device *vd if (vdev->ud.status == VDEV_ST_USED) { *out += sprintf(*out, "%03u %08x ", vdev->speed, vdev->devid); - *out += sprintf(*out, "%u %s", + *out += sprintf(*out, "%06u %s", vdev->ud.sockfd, dev_name(&vdev->udev->dev)); } else { *out += sprintf(*out, "000 00000000 "); - *out += sprintf(*out, "0000000000000000 0-0"); + *out += sprintf(*out, "000000 0-0"); } *out += sprintf(*out, "\n"); @@ -148,7 +148,7 @@ static ssize_t status_show(struct device *dev, int pdev_nr; out += sprintf(out, - "hub port sta spd dev socket local_busid\n"); + "hub port sta spd dev sockfd local_busid\n"); pdev_nr = status_name_to_id(attr->attr.name); if (pdev_nr < 0) @@ -218,7 +218,7 @@ static int valid_port(__u32 pdev_nr, __u32 rhport) return 1; } -static ssize_t store_detach(struct device *dev, struct device_attribute *attr, +static ssize_t detach_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { __u32 port = 0, pdev_nr = 0, rhport = 0; @@ -256,7 +256,7 @@ static ssize_t store_detach(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach); +static DEVICE_ATTR_WO(detach); static int valid_args(__u32 pdev_nr, __u32 rhport, enum usb_device_speed speed) { @@ -292,7 +292,7 @@ static int valid_args(__u32 pdev_nr, __u32 rhport, enum usb_device_speed speed) * * write() returns 0 on success, else negative errno. */ -static ssize_t store_attach(struct device *dev, struct device_attribute *attr, +static ssize_t attach_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct socket *socket; @@ -387,7 +387,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, return count; } -static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach); +static DEVICE_ATTR_WO(attach); #define MAX_STATUS_NAME 16 diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c index 1adc8af292ec..d86f72bbbb91 100644 --- a/drivers/usb/usbip/vudc_sysfs.c +++ b/drivers/usb/usbip/vudc_sysfs.c @@ -90,7 +90,7 @@ unlock: } static BIN_ATTR_RO(dev_desc, sizeof(struct usb_device_descriptor)); -static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, +static ssize_t usbip_sockfd_store(struct device *dev, struct device_attribute *attr, const char *in, size_t count) { struct vudc *udc = (struct vudc *) dev_get_drvdata(dev); @@ -180,7 +180,7 @@ unlock: return ret; } -static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd); +static DEVICE_ATTR_WO(usbip_sockfd); static ssize_t usbip_status_show(struct device *dev, struct device_attribute *attr, char *out) |