--- zzzz-none-000/linux-4.4.271/drivers/usb/core/hub.c 2021-06-03 06:22:09.000000000 +0000 +++ hawkeye-5590-750/linux-4.4.271/drivers/usb/core/hub.c 2023-04-19 10:22:29.000000000 +0000 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,16 @@ #include "hub.h" #include "otg_whitelist.h" +#if defined (CONFIG_SOC_QCA955X) +#include +#endif + +/* == 20160301 AVM/VGJ - CHANGESET: AVM Power Meter == */ +#ifdef CONFIG_AVM_POWERMETER +#include +#include +#endif /*--- #ifdef CONFIG_AVM_POWERMETER ---*/ + #define USB_VENDOR_GENESYS_LOGIC 0x05e3 #define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01 @@ -99,6 +110,42 @@ #define HUB_DEBOUNCE_STEP 25 #define HUB_DEBOUNCE_STABLE 100 +/* 20171027 AVM/VGJ USB KPI */ +#if defined (CONFIG_AVM_KERNEL) + +#define AVM_RECONNECT_ERROR_MS_THRESHOLD 2500 + +unsigned long disconnect_time[2]; +unsigned int disconnect_device_id[2]; + +int reconnect_count = 0; +module_param(reconnect_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(reconnect_count, "Count of reconnects"); + +int enum_error_count = 0; +module_param(enum_error_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(enum_error_count, "Count of enumeration errors"); + +int overcurrent_error_count = 0; +module_param(overcurrent_error_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(overcurrent_error_count, "Count of overcurrent errors"); + +#if defined (CONFIG_AVM_USB_PM) +int suspend_count = 0; +module_param(suspend_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(suspend_count, "Count of suspends"); + +int reset_resume_count = 0; +module_param(reset_resume_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(reset_resume_count, "Count of resets during resume"); + +int resume_error_count = 0; +module_param(resume_error_count, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(resume_error_count, "Count of failed resumes"); +#endif + +#endif + static void hub_release(struct kref *kref); static int usb_reset_and_verify_device(struct usb_device *udev); static int hub_port_disable(struct usb_hub *hub, int port1, int set_state); @@ -1720,7 +1767,8 @@ * - If user has indicated to prevent autosuspend by passing * usbcore.autosuspend = -1 then keep autosuspend disabled. */ -#ifdef CONFIG_PM +#if defined(CONFIG_PM) && !defined(CONFIG_AVM_KERNEL) + /* AVM USB stability: no immediate hub suspend */ if (hdev->dev.power.autosuspend_delay >= 0) pm_runtime_set_autosuspend_delay(&hdev->dev, 0); #endif @@ -2117,6 +2165,31 @@ dev_info(&udev->dev, "USB disconnect, device number %d\n", udev->devnum); +/* == 20160301 AVM/VGJ - CHANGESET: AVM Power Meter for DWC3 USB Controller == */ +#if defined(CONFIG_AVM_POWERMETER) && defined(CONFIG_USB_DWC3_HOST) + if (udev->level == 1) { + unsigned nextmA = 0; + unsigned avm_powerdevice; + + // Buses 1 and 2 belong to one port and buses 3 and 4 to the other + avm_powerdevice = (udev->bus->busnum < 3)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Bus#%u disconnect: AVM Powermeter changed to %u mA\n", udev->bus->busnum, nextmA); + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } +#endif // CONFIG_AVM_POWERMETER + +/* 20171012 AVM/VGJ store disconnect timestamp to check possible reconnects */ +#if defined (CONFIG_AVM_KERNEL) + // Only check reconnects of devices directly connected to root-hub + if (udev->level == 1) { + int port_index; + unsigned int device_id; + port_index = (udev->bus->busnum < 3)? 0 : 1; + disconnect_time[port_index] = jiffies; + device_id = (udev->descriptor.idVendor << 16) | udev->descriptor.idProduct; + disconnect_device_id[port_index] = device_id; + } +#endif /* * Ensure that the pm runtime code knows that the USB device * is in the process of being disconnected. @@ -2125,6 +2198,14 @@ usb_lock_device(udev); +#ifdef CONFIG_AVM_USB_PM + /* == 20180220 AVM/VGJ - Blacklist device, if disconnected recently after suspend == */ + if ((udev->dev.power.runtime_status == RPM_SUSPENDED) && ((jiffies - udev->dev.power.accounting_timestamp) < msecs_to_jiffies(1000))) { + suspend_error_count++; + avm_usb_pm_add_to_blacklist(udev); + } +#endif + hub_disconnect_children(udev); /* deallocate hcd/hardware state ... nuking all pending urbs and @@ -2447,6 +2528,50 @@ /* Tell the world! */ announce_device(udev); +/* == 20160301 AVM/VGJ - CHANGESET: AVM Power Meter for DWC3 USB Controller == */ +#if defined(CONFIG_AVM_POWERMETER) && defined(CONFIG_USB_DWC3_HOST) + if (udev->level == 1) { + unsigned nextmA = (udev->speed == USB_SPEED_SUPER)? 150: 100; + unsigned avm_powerdevice; + + // Buses 1 and 2 belong to one port and buses 3 and 4 to the other + avm_powerdevice = (udev->bus->busnum < 3)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Bus#%u connect: AVM Powermeter changed to %u mA\n", udev->bus->busnum, nextmA); + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } +#endif // CONFIG_AVM_POWERMETER + +/* 20171012 AVM/VGJ If connect comes too fast after disconnect -> reconnect error */ +#if defined (CONFIG_AVM_KERNEL) + // Only check reconnects of devices directly connected to root-hub + if (udev->level == 1) { + unsigned long jiffies_diff; + unsigned int device_id; + int port_index; + port_index = (udev->bus->busnum < 3)? 0 : 1; + // Check device_id to make sure that the stored time belongs to the same device + device_id = (udev->descriptor.idVendor << 16) | udev->descriptor.idProduct; + if ( device_id == disconnect_device_id[port_index] ) { + jiffies_diff = jiffies - disconnect_time[port_index]; + if ( jiffies_diff < msecs_to_jiffies(AVM_RECONNECT_ERROR_MS_THRESHOLD) ) { + printk(KERN_ERR "Spontaneous reconnect\n"); + reconnect_count++; + } + } + } +#endif + +/* == 20171115 AVM/VGJ Prevent suspension of blacklisted devices == */ +#if defined (CONFIG_AVM_USB_PM) + if (avm_usb_pm_is_blacklisted(udev)) { + pm_runtime_set_autosuspend_delay(&udev->dev, -1); + printk(KERN_INFO "USB device %04x:%04x:%04x is blacklisted for suspension\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + le16_to_cpu(udev->descriptor.bcdDevice)); + } +#endif + if (udev->serial) add_device_randomness(udev->serial, strlen(udev->serial)); if (udev->product) @@ -2639,19 +2764,63 @@ || link_state == USB_SS_PORT_LS_COMP_MOD; } +#if defined(CONFIG_SOC_QCA955X) +/* + * Scorpion and Wasp Specific (from AVM kernel 2.6.32) + * USB Enumeration Failure Fix: Refer EV 101139 + */ +static inline void ath_usb_dig_phy_rst(u32 ath_usb_sts_reg, u32 ath_usb_sts_sof, u32 ath_reset_reg, u32 ath_reset_usb_phy) +{ + int count = 0; + ath_reg_rmw_set(ath_usb_sts_reg, ath_usb_sts_sof); // Clear prev SOF status + udelay(5); + count = 0; + // Wait for one SOF(SOF sent every 125 usec) to sent out before Reset. + while(1) { + if (ath_reg_rd(ath_usb_sts_reg) & ath_usb_sts_sof) { + break; + } + if (count && (count % 5000 == 0)) + printk("%s[%d] count %d USBSTS = 0x%08x\n", __func__, __LINE__, count, ath_reg_rd(ath_usb_sts_reg)); + count++; + } + udelay(5); + // Reset USB PHY + ath_reg_rmw_set(ath_reset_reg, ath_reset_usb_phy); + ath_reg_rmw_clear(ath_reset_reg, ath_reset_usb_phy); + udelay(250); +} +#endif /* CONFIG_SOC_QCA955X */ + static int hub_port_wait_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay, bool warm) { int delay_time, ret; u16 portstatus; u16 portchange; + struct usb_hcd *ss_root_hcd = 0; + + /* 20171207 AVM/VGJ Reset WA: set flag if hub is root and super-speed */ + if (!hub->hdev->parent && hub_is_superspeed(hub->hdev)) { + ss_root_hcd = bus_to_hcd(hub->hdev->bus); + } for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) { + /* 20171207 AVM/VGJ Reset WA: unlock mutex to allow high-speed root hub to reset */ + if(ss_root_hcd) { + mutex_unlock(ss_root_hcd->address0_mutex); + } + /* wait to give the device a chance to reset */ msleep(delay); + /* 20171207 AVM/VGJ Reset WA: lock mutex */ + if(ss_root_hcd) { + mutex_lock(ss_root_hcd->address0_mutex); + } + /* read and decode port status */ ret = hub_port_status(hub, port1, &portstatus, &portchange); if (ret < 0) @@ -2663,9 +2832,11 @@ * On top of that, some chips may require additional time * to re-establish a connection after the reset is complete, * so also wait for the connection to be re-established. + * + * 20171207 AVM/VGJ Reset WA: wait only for super-speed root hub */ if (!(portstatus & USB_PORT_STAT_RESET) && - (portstatus & USB_PORT_STAT_CONNECTION)) + (!ss_root_hcd || (portstatus & USB_PORT_STAT_CONNECTION))) break; /* switch to the long delay after two short delay failures */ @@ -2714,6 +2885,27 @@ udev->speed = USB_SPEED_LOW; else udev->speed = USB_SPEED_FULL; + +#if defined(CONFIG_SOC_QCA955X) && defined (CONFIG_AVM_KERNEL) + if (udev->speed == USB_SPEED_HIGH) { + /* + * Scorpion Specific + * USB Enumeration Failure Fix: Refer EV 101139 + */ + if (!(hub->hdev->parent)) { + +#define ATH_USB_STS QCA955X_EHCI0_BASE+0x144 +#define ATH_USB_STS_SOF (1<<7) +#define RST_RESET_USB_PHY_RESET_MASK 0x00000010 +#define RST_RESET_ADDRESS 0x1806001c + /* + * Host controller 1 + */ + ath_usb_dig_phy_rst(ATH_USB_STS, ATH_USB_STS_SOF, RST_RESET_ADDRESS, RST_RESET_USB_PHY_RESET_MASK); + } + } +#endif /* CONFIG_SOC_QCA955X */ + return 0; } @@ -2724,6 +2916,7 @@ int i, status; u16 portchange, portstatus; struct usb_port *port_dev = hub->ports[port1 - 1]; + struct usb_hcd *hcd = bus_to_hcd(hub->hdev->bus); if (!hub_is_superspeed(hub->hdev)) { if (warm) { @@ -2731,6 +2924,10 @@ "warm reset\n"); return -EINVAL; } + + if (!hub->hdev->parent && hcd->primary_hcd) + usb_host_discon(hcd->primary_hcd->susphy, true); + /* Block EHCI CF initialization during the port reset. * Some companion controllers don't like it when they mix. */ @@ -2836,8 +3033,11 @@ usb_set_device_state(udev, USB_STATE_NOTATTACHED); } - if (!hub_is_superspeed(hub->hdev)) + if (!hub_is_superspeed(hub->hdev)) { + if (!hub->hdev->parent && hcd->primary_hcd) + usb_host_discon(hcd->primary_hcd->susphy, false); up_read(&ehci_cf_port_reset_rwsem); + } return status; } @@ -2908,10 +3108,23 @@ /* pass */; } /* Is the device still present? */ +#if defined (CONFIG_AVM_KERNEL) + /* AVM/VGJ 20180420 Do not treat port_is_suspended as No Device Error */ + else if (status || +#else else if (status || port_is_suspended(hub, portstatus) || +#endif !port_is_power_on(hub, portstatus)) { if (status >= 0) status = -ENODEV; +#if defined (CONFIG_AVM_KERNEL) + /* AVM/VGJ 20180420 follow up port_is_suspended with reset resume */ + } else if (port_is_suspended(hub, portstatus)) { + dev_dbg(hub->intfdev, + "port %d status %04x.%04x still suspended\n", + port1, portchange, portstatus); + udev->reset_resume = 1; +#endif } else if (!(portstatus & USB_PORT_STAT_CONNECTION)) { if (retries--) { usleep_range(200, 300); @@ -3116,6 +3329,7 @@ int port1 = udev->portnum; int status; bool really_suspend = true; + struct usb_hcd *hcd = bus_to_hcd(hub->hdev->bus); usb_lock_port(port_dev); @@ -3193,15 +3407,37 @@ if (!PMSG_IS_AUTO(msg)) status = 0; } else { - dev_dbg(&udev->dev, "usb %ssuspend, wakeup %d\n", + /* == 20170929 AVM/VGJ - Show also without CONFIG_USB_DEBUG == */ + dev_info(&udev->dev, "usb %ssuspend, wakeup %d\n", (PMSG_IS_AUTO(msg) ? "auto-" : ""), udev->do_remote_wakeup); if (really_suspend) { udev->port_is_suspended = 1; +#if defined (CONFIG_AVM_USB_PM) + suspend_count++; +#endif + +/* == 20170929 AVM/VGJ - CHANGESET: AVM Power Meter change for suspended device == */ +#if defined(CONFIG_AVM_POWERMETER) && defined (CONFIG_AVM_USB_PM) + if (udev->level == 1) { + unsigned nextmA = 50; + unsigned avm_powerdevice; + + avm_powerdevice = (udev->bus->busnum < 3)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Bus#%u suspend: AVM Powermeter changed to %u mA\n", udev->bus->busnum, nextmA); + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } +#endif // CONFIG_AVM_POWERMETER + /* device has up to 10 msec to fully suspend */ msleep(10); } + + if ((!hub->hdev->parent) && hcd->primary_hcd) + usb_suspend_phy(hcd->primary_hcd->susphy, + udev->speed, true); + usb_set_device_state(udev, USB_STATE_SUSPENDED); } @@ -3232,7 +3468,8 @@ u16 devstatus = 0; /* caller owns the udev device lock */ - dev_dbg(&udev->dev, "%s\n", + /* == 20170929 AVM/VGJ - Show also without CONFIG_USB_DEBUG == */ + dev_info(&udev->dev, "%s\n", udev->reset_resume ? "finish reset-resume" : "finish resume"); /* usb ch9 identifies four variants of SUSPENDED, based on what @@ -3279,8 +3516,15 @@ } } +#if defined (CONFIG_AVM_USB_PM) + if (udev->reset_resume) { + reset_resume_count++; + } +#endif + if (status) { - dev_dbg(&udev->dev, "gone after usb resume? status %d\n", + /* == 20170929 AVM/VGJ - Show also without CONFIG_USB_DEBUG == */ + dev_info(&udev->dev, "gone after usb resume? status %d\n", status); /* * There are a few quirky devices which violate the standard @@ -3421,7 +3665,8 @@ dev_dbg(&port_dev->dev, "can't resume, status %d\n", status); } else { /* drive resume for USB_RESUME_TIMEOUT msec */ - dev_dbg(&udev->dev, "usb %sresume\n", + /* == 20170929 AVM/VGJ - Show also without CONFIG_USB_DEBUG == */ + dev_info(&udev->dev, "usb %sresume\n", (PMSG_IS_AUTO(msg) ? "auto-" : "")); msleep(USB_RESUME_TIMEOUT); @@ -3445,6 +3690,30 @@ USB_PORT_FEAT_C_SUSPEND); } +/* == 20170929 AVM/VGJ - CHANGESET: AVM Power Meter change for resumed device == */ +#if defined(CONFIG_AVM_POWERMETER) && defined (CONFIG_AVM_USB_PM) + if (udev->level == 1) { + unsigned nextmA; + unsigned avm_powerdevice; + + if (udev->speed == USB_SPEED_SUPER) { + nextmA = (udev->config->desc.bMaxPower * 8); + if (nextmA <= 150) { + nextmA = 150; + } + } else { + nextmA = (udev->config->desc.bMaxPower * 2); + /* A device should display at least 100 mA in AVM_POWERMETER */ + if (nextmA < 100) { + nextmA = 100; + } + } + + avm_powerdevice = (udev->bus->busnum < 3)? powerdevice_usb_host : powerdevice_usb_host2; + printk (KERN_INFO "Bus#%u resume: AVM Powermeter changed to %u mA\n", udev->bus->busnum, nextmA); + PowerManagmentRessourceInfo(avm_powerdevice, nextmA); + } +#endif // CONFIG_AVM_POWERMETER /* TRSMRCY = 10 msec */ msleep(10); } @@ -3458,7 +3727,12 @@ if (status == 0) status = finish_port_resume(udev); if (status < 0) { - dev_dbg(&udev->dev, "can't resume, status %d\n", status); +#if defined (CONFIG_AVM_USB_PM) + avm_usb_pm_add_to_blacklist(udev); + resume_error_count++; +#endif + /* == 20170929 AVM/VGJ - Show also without CONFIG_USB_DEBUG == */ + dev_info(&udev->dev, "can't resume, status %d\n", status); hub_port_logical_disconnect(hub, port1); } else { /* Try to enable USB2 hardware LPM */ @@ -4356,6 +4630,15 @@ retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ goto fail; + + if (hcd->primary_hcd && hcd->primary_hcd->usb3_dev_reset_quirk) { + if (udev->speed != USB_SPEED_SUPER) { + retval = hub_port_reset(hub, port1, udev, delay, false); + if (retval < 0) /* error or disconnect */ + goto fail; + } + } + /* success, speed is known */ retval = -ENODEV; @@ -4571,6 +4854,8 @@ if (retval) goto fail; + if (hcd->primary_hcd) + usb_suspend_phy(hcd->primary_hcd->susphy, udev->speed, false); /* * Some superspeed devices have finished the link training process * and attached to a superspeed hub port, but the device descriptor @@ -4733,6 +5018,10 @@ if (hcd->usb_phy && !hdev->parent) usb_phy_notify_disconnect(hcd->usb_phy, udev->speed); usb_disconnect(&port_dev->child); + + if (!hdev->parent && !(portstatus & USB_PORT_STAT_CONNECTION) && hcd->primary_hcd) + usb_suspend_phy(hcd->primary_hcd->susphy, + udev->speed, true); } /* We can forget about a "removed" device when there's a physical @@ -4926,9 +5215,14 @@ if (hub->hdev->parent || !hcd->driver->port_handed_over || !(hcd->driver->port_handed_over)(hcd, port1)) { - if (status != -ENOTCONN && status != -ENODEV) + if (status != -ENOTCONN && status != -ENODEV) { dev_err(&port_dev->dev, "unable to enumerate USB device\n"); +/* 20171027 AVM/VGJ increment enumeration error */ +#if defined (CONFIG_AVM_KERNEL) + enum_error_count++; +#endif + } } done: @@ -5048,8 +5342,15 @@ msleep(100); /* Cool down */ hub_power_on(hub, true); hub_port_status(hub, port1, &status, &unused); - if (status & USB_PORT_STAT_OVERCURRENT) + if (status & USB_PORT_STAT_OVERCURRENT) { dev_err(&port_dev->dev, "over-current condition\n"); +/* 20171027 AVM/VGJ increment overcurrent error */ +#if defined (CONFIG_AVM_KERNEL) + // Only count overcurrent events for root-hub + if(hdev->level == 0) + overcurrent_error_count++; +#endif + } } if (portchange & USB_PORT_STAT_C_RESET) { @@ -5212,8 +5513,15 @@ msleep(500); /* Cool down */ hub_power_on(hub, true); hub_hub_status(hub, &status, &unused); - if (status & HUB_STATUS_OVERCURRENT) + if (status & HUB_STATUS_OVERCURRENT) { dev_err(hub_dev, "over-current condition\n"); +/* 20171027 AVM/VGJ increment overcurrent error */ +#if defined (CONFIG_AVM_KERNEL) + // Only count overcurrent events for root-hub + if(hdev->level == 0) + overcurrent_error_count++; +#endif + } } }