Loading drivers/usb/core/generic.c +14 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,11 @@ * (C) Copyright Greg Kroah-Hartman 2002-2003 * */ /* * NOTE: This file has been modified by Sony Mobile Communications Inc. * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, * and licensed under the license of the file. */ #include <linux/usb.h> #include <linux/usb/hcd.h> Loading @@ -23,6 +28,10 @@ #include <linux/usb/audio-v3.h> #include "usb.h" #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION #include <linux/usb/host_ext_event.h> #endif static inline const char *plural(int n) { return (n == 1 ? "" : "s"); Loading Loading @@ -168,10 +177,14 @@ int usb_choose_configuration(struct usb_device *udev) best = c; } if (insufficient_power > 0) if (insufficient_power > 0) { dev_info(&udev->dev, "rejected %d configuration%s " "due to insufficient available bus power\n", insufficient_power, plural(insufficient_power)); #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION host_send_uevent(USB_HOST_EXT_EVENT_INSUFFICIENT_POWER); #endif } if (best) { /* choose usb audio class preferred config if available */ Loading drivers/usb/dwc3/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -104,4 +104,13 @@ config USB_DWC3_QCOM Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside, say 'Y' or 'M' if you have one such device. config USB_DWC3_MSM_ID_POLL bool "Support ID polling function on DesignWare USB Controller for MSM" depends on USB_DWC3_DUAL_ROLE depends on USB_DWC3 default n help Say Y here to enable the ID polling function on the DesignWare USB3.0 (DRD) Controller for MSM driver. endif drivers/usb/dwc3/dwc3-msm.c +459 −1 Original line number Diff line number Diff line Loading @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ /* * NOTE: This file has been modified by Sony Mobile Communications Inc. * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, * and licensed under the license of the file. */ #include <linux/module.h> #include <linux/kernel.h> Loading Loading @@ -39,6 +44,9 @@ #include <linux/regulator/consumer.h> #include <linux/pm_wakeup.h> #include <linux/power_supply.h> #ifdef CONFIG_USB_DWC3_MSM_ID_POLL #include <linux/qpnp/qpnp-adc.h> #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ #include <linux/cdev.h> #include <linux/completion.h> #include <linux/clk/msm-clk.h> Loading @@ -47,6 +55,11 @@ #include <linux/extcon.h> #include <linux/reset.h> #include <soc/qcom/boot_stats.h> #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION #include <linux/usb/host_ext_event.h> #endif #include <linux/fb.h> #include <linux/wakelock.h> #include "power.h" #include "core.h" Loading Loading @@ -178,6 +191,7 @@ enum plug_orientation { #define ID 0 #define B_SESS_VLD 1 #define B_SUSPEND 2 #define A_VBUS_DROP_DET 3 #define WAIT_FOR_LPM 3 #define PM_QOS_SAMPLE_SEC 2 Loading Loading @@ -257,8 +271,10 @@ struct dwc3_msm { struct extcon_dev *extcon_vbus; struct extcon_dev *extcon_id; struct extcon_dev *extcon_vbus_drop; struct notifier_block vbus_nb; struct notifier_block id_nb; struct notifier_block vbus_drop_nb; struct notifier_block host_nb; bool host_only_mode; Loading @@ -278,6 +294,29 @@ struct dwc3_msm { enum usb_device_speed override_usb_speed; bool core_init_failed; #ifdef CONFIG_USB_DWC3_MSM_ID_POLL /* id polling */ bool id_polling_use; bool id_polling_start; struct delayed_work id_polling_work; struct workqueue_struct *id_polling_q; unsigned int id_polling_up_interval; unsigned int id_polling_up_period; int id_polling_pd_gpio; struct qpnp_vadc_chip *usb_detect_adc; spinlock_t id_polling_lock; bool otg_present; unsigned int lcd_blanked; struct wakeup_source id_polling_wu; struct delayed_work setsink_work; struct wake_lock setsink_lock; int setsink_cnt; #ifdef CONFIG_FB struct notifier_block fb_notif; #endif /* CONFIG_FB */ #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ bool send_vbus_drop_ue; }; #define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */ Loading @@ -294,6 +333,17 @@ struct dwc3_msm { #define DSTS_CONNECTSPD_SS 0x4 #ifdef CONFIG_USB_DWC3_MSM_ID_POLL #define USB_ID_POLLING_UP_INTERVAL 1000 /* s */ #define USB_ID_POLLING_UP_PERIOD 100 /* us */ #define USB_ID_POLLING_WAKE_TIMEOUT 2000 #define SETSINK_RETRY_INTERVAL 2000 #define WAKELOCK_RETRY_INTERVAL 2500 /* Max of retry to set SINK */ #define SETSINK_RETRY_MAX 3 #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ static void dwc3_pwr_event_handler(struct dwc3_msm *mdwc); static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA); Loading Loading @@ -2554,6 +2604,284 @@ static irqreturn_t msm_dwc3_pwr_irq(int irq, void *data) return IRQ_HANDLED; } #ifdef CONFIG_USB_DWC3_MSM_ID_POLL static void dwc3_setsink_work(struct work_struct *w) { struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, setsink_work.work); union power_supply_propval val = {0}; int ret; if (!mdwc->otg_present && mdwc->setsink_cnt < SETSINK_RETRY_MAX) { wake_lock_timeout(&mdwc->setsink_lock, msecs_to_jiffies(WAKELOCK_RETRY_INTERVAL)); mdwc->setsink_cnt++; power_supply_get_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); if (val.intval == POWER_SUPPLY_TYPEC_PR_SINK) { pr_info("%s(): power role was changed to sink\n", __func__); return; } val.intval = POWER_SUPPLY_TYPEC_PR_SINK; ret = power_supply_set_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); if (ret) { dev_err(mdwc->dev, "set power supply fail\n"); return; } pr_info("%s(): rerun set to sink\n", __func__); schedule_delayed_work(&mdwc->setsink_work, msecs_to_jiffies(SETSINK_RETRY_INTERVAL)); } else { if (mdwc->otg_present) pr_info("%s(): cable is connected, so power role does not change\n", __func__); else dev_err(mdwc->dev, "retry count is over, power role has not changed.\n"); } } static void dwc3_id_pullup(struct dwc3_msm *mdwc, int pullup) { if (pullup) { dev_dbg(mdwc->dev, "%s: pull up ID pin\n", __func__); if (mdwc->id_polling_pd_gpio) gpio_set_value(mdwc->id_polling_pd_gpio, 1); } else { if (mdwc->id_polling_pd_gpio) gpio_set_value(mdwc->id_polling_pd_gpio, 0); dev_dbg(mdwc->dev, "%s: pull down ID pin\n", __func__); } } static ssize_t id_polling_up_interval_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); if (!mdwc) return -EINVAL; return snprintf(buf, PAGE_SIZE, "%u\n", mdwc->id_polling_up_interval); } static ssize_t id_polling_up_interval_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); if (!mdwc) return -EINVAL; if (kstrtou32(buf, 0, &mdwc->id_polling_up_interval) < 0) { pr_err("id_polling_up_interval cannot read value\n"); return -EINVAL; } if (mdwc->id_polling_start) { /* restart id polling with new interval value. */ cancel_delayed_work_sync(&mdwc->id_polling_work); queue_delayed_work(mdwc->id_polling_q, &mdwc->id_polling_work, msecs_to_jiffies(mdwc->id_polling_up_interval)); } return size; } static DEVICE_ATTR(id_polling_up_interval, S_IRUGO | S_IWUSR, id_polling_up_interval_show, id_polling_up_interval_store); static ssize_t id_polling_up_period_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); if (!mdwc) return -EINVAL; return snprintf(buf, PAGE_SIZE, "%u\n", mdwc->id_polling_up_period); } static ssize_t id_polling_up_period_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); if (!mdwc) return -EINVAL; if (kstrtou32(buf, 0, &mdwc->id_polling_up_period) < 0) { pr_err("id_polling_up_period cannot read value\n"); return -EINVAL; } return size; } static DEVICE_ATTR(id_polling_up_period, S_IRUGO | S_IWUSR, id_polling_up_period_show, id_polling_up_period_store); static void dwc3_id_poll_update(struct dwc3_msm *mdwc) { if (!mdwc->otg_present && mdwc->lcd_blanked) { mdwc->id_polling_start = false; cancel_delayed_work(&mdwc->id_polling_work); } else if (!mdwc->id_polling_start) { mdwc->id_polling_start = true; queue_delayed_work(mdwc->id_polling_q, &mdwc->id_polling_work, 0); } } #ifdef CONFIG_FB static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct dwc3_msm *mdwc = container_of(self, struct dwc3_msm, fb_notif); struct fb_event *evdata = data; unsigned int blanked; unsigned long flags; if (!mdwc->id_polling_use || !evdata || !evdata->data || event != FB_EVENT_BLANK) return 0; blanked = !(*(unsigned int *)(evdata->data) == FB_BLANK_UNBLANK); dev_info(mdwc->dev, "receive fb event blank=%u->%u, otg_present=%d\n", mdwc->lcd_blanked, blanked, mdwc->otg_present); if (blanked == mdwc->lcd_blanked) return 0; spin_lock_irqsave(&mdwc->id_polling_lock, flags); mdwc->lcd_blanked = blanked; dwc3_id_poll_update(mdwc); spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); return 0; } #endif /* CONFIG_FB */ static int dwc3_msm_set_type_power_role(struct dwc3_msm *mdwc, int typec_power_role) { union power_supply_propval pval = {0}; int ret; if (!mdwc->usb_psy) { mdwc->usb_psy = power_supply_get_by_name("usb"); if (!mdwc->usb_psy) { dev_warn(mdwc->dev, "Could not get usb power_supply\n"); return -ENODEV; } } pr_info("%s(): typec power role=%d\n", __func__, typec_power_role); pval.intval = typec_power_role; ret = power_supply_set_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval); if (ret) { dev_err(mdwc->dev, "power supply error when setting property\n"); return ret; } if (typec_power_role == POWER_SUPPLY_TYPEC_PR_SINK) { mdwc->setsink_cnt = 0; wake_lock_timeout(&mdwc->setsink_lock, msecs_to_jiffies(WAKELOCK_RETRY_INTERVAL)); schedule_delayed_work(&mdwc->setsink_work, msecs_to_jiffies(SETSINK_RETRY_INTERVAL)); } return 0; } static int dwc3_get_usb_detect_adc(struct dwc3_msm *mdwc) { int rc = 0; struct qpnp_vadc_result results; if (IS_ERR_OR_NULL(mdwc->usb_detect_adc)) { mdwc->usb_detect_adc = qpnp_get_vadc(mdwc->dev, "usb_detect"); if (IS_ERR(mdwc->usb_detect_adc)) return PTR_ERR(mdwc->usb_detect_adc); } rc = qpnp_vadc_read(mdwc->usb_detect_adc, 0x14, &results); if (rc) return 1; else return (800000 < results.physical); } static void dwc3_id_polling_work(struct work_struct *w) { struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, id_polling_work.work); enum dwc3_id_state id; unsigned int delta; unsigned long flags; bool otg_present; if (!mdwc->id_polling_use) return; spin_lock_irqsave(&mdwc->id_polling_lock, flags); if (!mdwc->id_polling_start) { spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); return; } queue_delayed_work(mdwc->id_polling_q, to_delayed_work(w), msecs_to_jiffies(mdwc->id_polling_up_interval)); spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); pr_debug("id polling, interval=%u ms, period=%u us\n", mdwc->id_polling_up_interval, mdwc->id_polling_up_period); __pm_stay_awake(&mdwc->id_polling_wu); dwc3_id_pullup(mdwc, 1); if (mdwc->id_polling_up_period) { if (mdwc->id_polling_up_period < 10) { udelay(mdwc->id_polling_up_period); } else if (mdwc->id_polling_up_period < 20000) { delta = mdwc->id_polling_up_period / 10; usleep_range(mdwc->id_polling_up_period - delta, mdwc->id_polling_up_period + delta); } else if (mdwc->id_polling_up_period / 1000 < mdwc->id_polling_up_interval) { msleep(mdwc->id_polling_up_period / 1000); } else { pr_warn("pull up period is too long because it is longer than interval.\n"); } } id = dwc3_get_usb_detect_adc(mdwc) ? DWC3_ID_FLOAT : DWC3_ID_GROUND; dwc3_id_pullup(mdwc, 0); spin_lock_irqsave(&mdwc->id_polling_lock, flags); otg_present = mdwc->otg_present; mdwc->otg_present = id == DWC3_ID_GROUND; dwc3_id_poll_update(mdwc); spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); if (mdwc->otg_present != otg_present) { dwc3_msm_set_type_power_role(mdwc, mdwc->otg_present ? POWER_SUPPLY_TYPEC_PR_DUAL : POWER_SUPPLY_TYPEC_PR_SINK); __pm_wakeup_event(&mdwc->id_polling_wu, USB_ID_POLLING_WAKE_TIMEOUT); } __pm_relax(&mdwc->id_polling_wu); } #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ static int dwc3_cpu_notifier_cb(struct notifier_block *nfb, unsigned long action, void *hcpu) { Loading Loading @@ -2754,6 +3082,24 @@ static void check_for_sdp_connection(struct work_struct *w) } } static int dwc3_msm_vbus_drop_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { struct dwc3_msm *mdwc = container_of(nb, struct dwc3_msm, vbus_drop_nb); struct extcon_dev *edev = ptr; if (!edev) { dev_err(mdwc->dev, "%s: edev null\n", __func__); goto done; } set_bit(A_VBUS_DROP_DET, &mdwc->inputs); pr_info("%s: receive ocp notification\n", __func__); schedule_delayed_work(&mdwc->sm_work, 0); done: return NOTIFY_DONE; } static int dwc3_msm_vbus_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { Loading Loading @@ -2856,10 +3202,22 @@ static int dwc3_msm_extcon_register(struct dwc3_msm *mdwc) dev_err(mdwc->dev, "failed to register notifier for USB-HOST\n"); goto err; } mdwc->extcon_vbus_drop = edev; mdwc->vbus_drop_nb.notifier_call = dwc3_msm_vbus_drop_notifier; ret = extcon_register_notifier(edev, EXTCON_VBUS_DROP, &mdwc->vbus_drop_nb); if (ret < 0) { dev_err(mdwc->dev, "failed to register notifier for USB-DropT\n"); goto err; } } return 0; err: if (mdwc->extcon_id) extcon_unregister_notifier(mdwc->extcon_id, EXTCON_USB_HOST, &mdwc->id_nb); if (mdwc->extcon_vbus) extcon_unregister_notifier(mdwc->extcon_vbus, EXTCON_USB, &mdwc->vbus_nb); Loading Loading @@ -3063,6 +3421,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) } mdwc->id_state = DWC3_ID_FLOAT; mdwc->otg_present = true; set_bit(ID, &mdwc->inputs); mdwc->charging_disabled = of_property_read_bool(node, Loading Loading @@ -3136,6 +3495,16 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } #ifdef CONFIG_USB_DWC3_MSM_ID_POLL mdwc->id_polling_use = of_property_read_bool(node, "id_polling_use"); if (mdwc->id_polling_use) { dev_info(&pdev->dev, "id polling is enabled\n"); mdwc->id_state = DWC3_ID_FLOAT; } wake_lock_init(&mdwc->setsink_lock, WAKE_LOCK_SUSPEND, "typecsink_lock"); #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcsr_base"); if (!res) { dev_dbg(&pdev->dev, "missing TCSR memory resource\n"); Loading Loading @@ -3353,6 +3722,10 @@ static int dwc3_msm_probe(struct platform_device *pdev) else if (mdwc->extcon_id && extcon_get_cable_state_(mdwc->extcon_id, EXTCON_USB_HOST)) dwc3_msm_id_notifier(&mdwc->id_nb, true, mdwc->extcon_id); else if (mdwc->extcon_vbus_drop && extcon_get_cable_state_( mdwc->extcon_vbus_drop, EXTCON_VBUS_DROP)) dwc3_msm_vbus_drop_notifier(&mdwc->vbus_drop_nb, true, mdwc->extcon_vbus_drop); else if (!pval.intval) { /* USB cable is not connected */ queue_delayed_work(mdwc->sm_usb_wq, &mdwc->sm_work, 0); Loading Loading @@ -3382,6 +3755,47 @@ static int dwc3_msm_probe(struct platform_device *pdev) place_marker(boot_marker); #ifdef CONFIG_USB_DWC3_MSM_ID_POLL if (mdwc->id_polling_use) { INIT_DELAYED_WORK(&mdwc->setsink_work, dwc3_setsink_work); INIT_DELAYED_WORK(&mdwc->id_polling_work, dwc3_id_polling_work); mdwc->id_polling_q = create_singlethread_workqueue("id_polling_q"); mdwc->id_polling_up_interval = USB_ID_POLLING_UP_INTERVAL; of_property_read_u32(node, "id_polling_up_interval", &mdwc->id_polling_up_interval); dev_dbg(&pdev->dev, "id_polling_up_interval=%dms\n", mdwc->id_polling_up_interval); device_create_file(&pdev->dev, &dev_attr_id_polling_up_interval); mdwc->id_polling_up_period = USB_ID_POLLING_UP_PERIOD; of_property_read_u32(node, "id_polling_up_period", &mdwc->id_polling_up_period); dev_dbg(&pdev->dev, "id_polling_up_period=%dus\n", mdwc->id_polling_up_period); device_create_file(&pdev->dev, &dev_attr_id_polling_up_period); mdwc->id_polling_pd_gpio = of_get_named_gpio(node, "id_polling_pd_gpio", 0); if (!gpio_is_valid(mdwc->id_polling_pd_gpio)) dev_info(&pdev->dev, "id_polling_pd is missing\n"); wakeup_source_init(&mdwc->id_polling_wu, "id_polling"); spin_lock_init(&mdwc->id_polling_lock); #ifdef CONFIG_FB mdwc->fb_notif.notifier_call = fb_notifier_callback; if (fb_register_client(&mdwc->fb_notif)) dev_err(mdwc->dev, "failed to register fb_notifier\n"); #endif /* CONFIG_FB */ mdwc->id_polling_start = true; queue_delayed_work(mdwc->id_polling_q, &mdwc->id_polling_work, msecs_to_jiffies(mdwc->id_polling_up_interval)); } #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ return 0; put_dwc3: Loading @@ -3389,6 +3803,7 @@ put_dwc3: msm_bus_scale_unregister_client(mdwc->bus_perf_client); of_platform_depopulate(&pdev->dev); err: wake_lock_destroy(&mdwc->setsink_lock); destroy_workqueue(mdwc->dwc3_wq); return ret; } Loading Loading @@ -3425,6 +3840,22 @@ static int dwc3_msm_remove(struct platform_device *pdev) } cancel_delayed_work_sync(&mdwc->perf_vote_work); #ifdef CONFIG_USB_DWC3_MSM_ID_POLL cancel_delayed_work_sync(&mdwc->setsink_work); wake_lock_destroy(&mdwc->setsink_lock); if (mdwc->id_polling_use) { #ifdef CONFIG_FB fb_unregister_client(&mdwc->fb_notif); #endif /* CONFIG_FB */ wakeup_source_trash(&mdwc->id_polling_wu); device_remove_file(&pdev->dev, &dev_attr_id_polling_up_interval); device_remove_file(&pdev->dev, &dev_attr_id_polling_up_period); cancel_delayed_work_sync(&mdwc->id_polling_work); destroy_workqueue(mdwc->id_polling_q); } #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ cancel_delayed_work_sync(&mdwc->sm_work); if (mdwc->hs_phy) Loading Loading @@ -3912,7 +4343,6 @@ set_prop: return 0; } /** * dwc3_otg_sm_work - workqueue function. * Loading Loading @@ -4009,6 +4439,7 @@ static void dwc3_otg_sm_work(struct work_struct *w) work = 1; break; } else { mdwc->send_vbus_drop_ue = false; dwc3_msm_gadget_vbus_draw(mdwc, 0); pm_relax(mdwc->dev); dev_dbg(mdwc->dev, "Cable disconnected\n"); Loading Loading @@ -4074,9 +4505,22 @@ static void dwc3_otg_sm_work(struct work_struct *w) /* Switch to A-Device*/ if (test_bit(ID, &mdwc->inputs)) { dev_dbg(mdwc->dev, "id\n"); clear_bit(A_VBUS_DROP_DET, &mdwc->inputs); mdwc->drd_state = DRD_STATE_IDLE; mdwc->vbus_retry_count = 0; work = 1; #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION host_send_uevent(USB_HOST_EXT_EVENT_NONE); #endif } else if (test_bit(A_VBUS_DROP_DET, &mdwc->inputs)) { dev_dbg(mdwc->dev, "vbus_drop_det\n"); /* staying on here until exit from A-Device */ #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION if (!mdwc->send_vbus_drop_ue) { mdwc->send_vbus_drop_ue = true; host_send_uevent(USB_HOST_EXT_EVENT_VBUS_DROP); } #endif } else { mdwc->drd_state = DRD_STATE_HOST; ret = dwc3_otg_start_host(mdwc, 1); Loading Loading @@ -4116,6 +4560,20 @@ static void dwc3_otg_sm_work(struct work_struct *w) mdwc->vbus_retry_count = 0; mdwc->hc_died = false; work = 1; #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION host_send_uevent(USB_HOST_EXT_EVENT_NONE); #endif } else if (test_bit(A_VBUS_DROP_DET, &mdwc->inputs)) { dev_dbg(mdwc->dev, "vbus_drop_det\n"); dwc3_otg_start_host(mdwc, 0); mdwc->drd_state = DRD_STATE_HOST_IDLE; mdwc->vbus_retry_count = 0; #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION if (!mdwc->send_vbus_drop_ue) { mdwc->send_vbus_drop_ue = true; host_send_uevent(USB_HOST_EXT_EVENT_VBUS_DROP); } #endif } else { dev_dbg(mdwc->dev, "still in a_host state. Resuming root hub.\n"); dbg_event(0xFF, "XHCIResume", 0); Loading drivers/usb/host/Kconfig +7 −0 Original line number Diff line number Diff line Loading @@ -787,3 +787,10 @@ config USB_HCD_TEST_MODE This option is of interest only to developers who need to validate their USB hardware designs. It is not needed for normal use. If unsure, say N. config USB_HOST_EXTRA_NOTIFICATION bool "USB host extra notification" depends on USB default n help Provides the functions to notify extra USB host related events. drivers/usb/host/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -76,3 +76,4 @@ obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o obj-$(CONFIG_USB_HOST_EXTRA_NOTIFICATION) += host_ext_event.o Loading
drivers/usb/core/generic.c +14 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,11 @@ * (C) Copyright Greg Kroah-Hartman 2002-2003 * */ /* * NOTE: This file has been modified by Sony Mobile Communications Inc. * Modifications are Copyright (c) 2013 Sony Mobile Communications Inc, * and licensed under the license of the file. */ #include <linux/usb.h> #include <linux/usb/hcd.h> Loading @@ -23,6 +28,10 @@ #include <linux/usb/audio-v3.h> #include "usb.h" #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION #include <linux/usb/host_ext_event.h> #endif static inline const char *plural(int n) { return (n == 1 ? "" : "s"); Loading Loading @@ -168,10 +177,14 @@ int usb_choose_configuration(struct usb_device *udev) best = c; } if (insufficient_power > 0) if (insufficient_power > 0) { dev_info(&udev->dev, "rejected %d configuration%s " "due to insufficient available bus power\n", insufficient_power, plural(insufficient_power)); #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION host_send_uevent(USB_HOST_EXT_EVENT_INSUFFICIENT_POWER); #endif } if (best) { /* choose usb audio class preferred config if available */ Loading
drivers/usb/dwc3/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -104,4 +104,13 @@ config USB_DWC3_QCOM Recent Qualcomm SoCs ship with one DesignWare Core USB3 IP inside, say 'Y' or 'M' if you have one such device. config USB_DWC3_MSM_ID_POLL bool "Support ID polling function on DesignWare USB Controller for MSM" depends on USB_DWC3_DUAL_ROLE depends on USB_DWC3 default n help Say Y here to enable the ID polling function on the DesignWare USB3.0 (DRD) Controller for MSM driver. endif
drivers/usb/dwc3/dwc3-msm.c +459 −1 Original line number Diff line number Diff line Loading @@ -10,6 +10,11 @@ * GNU General Public License for more details. * */ /* * NOTE: This file has been modified by Sony Mobile Communications Inc. * Modifications are Copyright (c) 2015 Sony Mobile Communications Inc, * and licensed under the license of the file. */ #include <linux/module.h> #include <linux/kernel.h> Loading Loading @@ -39,6 +44,9 @@ #include <linux/regulator/consumer.h> #include <linux/pm_wakeup.h> #include <linux/power_supply.h> #ifdef CONFIG_USB_DWC3_MSM_ID_POLL #include <linux/qpnp/qpnp-adc.h> #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ #include <linux/cdev.h> #include <linux/completion.h> #include <linux/clk/msm-clk.h> Loading @@ -47,6 +55,11 @@ #include <linux/extcon.h> #include <linux/reset.h> #include <soc/qcom/boot_stats.h> #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION #include <linux/usb/host_ext_event.h> #endif #include <linux/fb.h> #include <linux/wakelock.h> #include "power.h" #include "core.h" Loading Loading @@ -178,6 +191,7 @@ enum plug_orientation { #define ID 0 #define B_SESS_VLD 1 #define B_SUSPEND 2 #define A_VBUS_DROP_DET 3 #define WAIT_FOR_LPM 3 #define PM_QOS_SAMPLE_SEC 2 Loading Loading @@ -257,8 +271,10 @@ struct dwc3_msm { struct extcon_dev *extcon_vbus; struct extcon_dev *extcon_id; struct extcon_dev *extcon_vbus_drop; struct notifier_block vbus_nb; struct notifier_block id_nb; struct notifier_block vbus_drop_nb; struct notifier_block host_nb; bool host_only_mode; Loading @@ -278,6 +294,29 @@ struct dwc3_msm { enum usb_device_speed override_usb_speed; bool core_init_failed; #ifdef CONFIG_USB_DWC3_MSM_ID_POLL /* id polling */ bool id_polling_use; bool id_polling_start; struct delayed_work id_polling_work; struct workqueue_struct *id_polling_q; unsigned int id_polling_up_interval; unsigned int id_polling_up_period; int id_polling_pd_gpio; struct qpnp_vadc_chip *usb_detect_adc; spinlock_t id_polling_lock; bool otg_present; unsigned int lcd_blanked; struct wakeup_source id_polling_wu; struct delayed_work setsink_work; struct wake_lock setsink_lock; int setsink_cnt; #ifdef CONFIG_FB struct notifier_block fb_notif; #endif /* CONFIG_FB */ #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ bool send_vbus_drop_ue; }; #define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */ Loading @@ -294,6 +333,17 @@ struct dwc3_msm { #define DSTS_CONNECTSPD_SS 0x4 #ifdef CONFIG_USB_DWC3_MSM_ID_POLL #define USB_ID_POLLING_UP_INTERVAL 1000 /* s */ #define USB_ID_POLLING_UP_PERIOD 100 /* us */ #define USB_ID_POLLING_WAKE_TIMEOUT 2000 #define SETSINK_RETRY_INTERVAL 2000 #define WAKELOCK_RETRY_INTERVAL 2500 /* Max of retry to set SINK */ #define SETSINK_RETRY_MAX 3 #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ static void dwc3_pwr_event_handler(struct dwc3_msm *mdwc); static int dwc3_msm_gadget_vbus_draw(struct dwc3_msm *mdwc, unsigned mA); Loading Loading @@ -2554,6 +2604,284 @@ static irqreturn_t msm_dwc3_pwr_irq(int irq, void *data) return IRQ_HANDLED; } #ifdef CONFIG_USB_DWC3_MSM_ID_POLL static void dwc3_setsink_work(struct work_struct *w) { struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, setsink_work.work); union power_supply_propval val = {0}; int ret; if (!mdwc->otg_present && mdwc->setsink_cnt < SETSINK_RETRY_MAX) { wake_lock_timeout(&mdwc->setsink_lock, msecs_to_jiffies(WAKELOCK_RETRY_INTERVAL)); mdwc->setsink_cnt++; power_supply_get_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); if (val.intval == POWER_SUPPLY_TYPEC_PR_SINK) { pr_info("%s(): power role was changed to sink\n", __func__); return; } val.intval = POWER_SUPPLY_TYPEC_PR_SINK; ret = power_supply_set_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); if (ret) { dev_err(mdwc->dev, "set power supply fail\n"); return; } pr_info("%s(): rerun set to sink\n", __func__); schedule_delayed_work(&mdwc->setsink_work, msecs_to_jiffies(SETSINK_RETRY_INTERVAL)); } else { if (mdwc->otg_present) pr_info("%s(): cable is connected, so power role does not change\n", __func__); else dev_err(mdwc->dev, "retry count is over, power role has not changed.\n"); } } static void dwc3_id_pullup(struct dwc3_msm *mdwc, int pullup) { if (pullup) { dev_dbg(mdwc->dev, "%s: pull up ID pin\n", __func__); if (mdwc->id_polling_pd_gpio) gpio_set_value(mdwc->id_polling_pd_gpio, 1); } else { if (mdwc->id_polling_pd_gpio) gpio_set_value(mdwc->id_polling_pd_gpio, 0); dev_dbg(mdwc->dev, "%s: pull down ID pin\n", __func__); } } static ssize_t id_polling_up_interval_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); if (!mdwc) return -EINVAL; return snprintf(buf, PAGE_SIZE, "%u\n", mdwc->id_polling_up_interval); } static ssize_t id_polling_up_interval_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); if (!mdwc) return -EINVAL; if (kstrtou32(buf, 0, &mdwc->id_polling_up_interval) < 0) { pr_err("id_polling_up_interval cannot read value\n"); return -EINVAL; } if (mdwc->id_polling_start) { /* restart id polling with new interval value. */ cancel_delayed_work_sync(&mdwc->id_polling_work); queue_delayed_work(mdwc->id_polling_q, &mdwc->id_polling_work, msecs_to_jiffies(mdwc->id_polling_up_interval)); } return size; } static DEVICE_ATTR(id_polling_up_interval, S_IRUGO | S_IWUSR, id_polling_up_interval_show, id_polling_up_interval_store); static ssize_t id_polling_up_period_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); if (!mdwc) return -EINVAL; return snprintf(buf, PAGE_SIZE, "%u\n", mdwc->id_polling_up_period); } static ssize_t id_polling_up_period_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct dwc3_msm *mdwc = dev_get_drvdata(dev); if (!mdwc) return -EINVAL; if (kstrtou32(buf, 0, &mdwc->id_polling_up_period) < 0) { pr_err("id_polling_up_period cannot read value\n"); return -EINVAL; } return size; } static DEVICE_ATTR(id_polling_up_period, S_IRUGO | S_IWUSR, id_polling_up_period_show, id_polling_up_period_store); static void dwc3_id_poll_update(struct dwc3_msm *mdwc) { if (!mdwc->otg_present && mdwc->lcd_blanked) { mdwc->id_polling_start = false; cancel_delayed_work(&mdwc->id_polling_work); } else if (!mdwc->id_polling_start) { mdwc->id_polling_start = true; queue_delayed_work(mdwc->id_polling_q, &mdwc->id_polling_work, 0); } } #ifdef CONFIG_FB static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct dwc3_msm *mdwc = container_of(self, struct dwc3_msm, fb_notif); struct fb_event *evdata = data; unsigned int blanked; unsigned long flags; if (!mdwc->id_polling_use || !evdata || !evdata->data || event != FB_EVENT_BLANK) return 0; blanked = !(*(unsigned int *)(evdata->data) == FB_BLANK_UNBLANK); dev_info(mdwc->dev, "receive fb event blank=%u->%u, otg_present=%d\n", mdwc->lcd_blanked, blanked, mdwc->otg_present); if (blanked == mdwc->lcd_blanked) return 0; spin_lock_irqsave(&mdwc->id_polling_lock, flags); mdwc->lcd_blanked = blanked; dwc3_id_poll_update(mdwc); spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); return 0; } #endif /* CONFIG_FB */ static int dwc3_msm_set_type_power_role(struct dwc3_msm *mdwc, int typec_power_role) { union power_supply_propval pval = {0}; int ret; if (!mdwc->usb_psy) { mdwc->usb_psy = power_supply_get_by_name("usb"); if (!mdwc->usb_psy) { dev_warn(mdwc->dev, "Could not get usb power_supply\n"); return -ENODEV; } } pr_info("%s(): typec power role=%d\n", __func__, typec_power_role); pval.intval = typec_power_role; ret = power_supply_set_property(mdwc->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval); if (ret) { dev_err(mdwc->dev, "power supply error when setting property\n"); return ret; } if (typec_power_role == POWER_SUPPLY_TYPEC_PR_SINK) { mdwc->setsink_cnt = 0; wake_lock_timeout(&mdwc->setsink_lock, msecs_to_jiffies(WAKELOCK_RETRY_INTERVAL)); schedule_delayed_work(&mdwc->setsink_work, msecs_to_jiffies(SETSINK_RETRY_INTERVAL)); } return 0; } static int dwc3_get_usb_detect_adc(struct dwc3_msm *mdwc) { int rc = 0; struct qpnp_vadc_result results; if (IS_ERR_OR_NULL(mdwc->usb_detect_adc)) { mdwc->usb_detect_adc = qpnp_get_vadc(mdwc->dev, "usb_detect"); if (IS_ERR(mdwc->usb_detect_adc)) return PTR_ERR(mdwc->usb_detect_adc); } rc = qpnp_vadc_read(mdwc->usb_detect_adc, 0x14, &results); if (rc) return 1; else return (800000 < results.physical); } static void dwc3_id_polling_work(struct work_struct *w) { struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, id_polling_work.work); enum dwc3_id_state id; unsigned int delta; unsigned long flags; bool otg_present; if (!mdwc->id_polling_use) return; spin_lock_irqsave(&mdwc->id_polling_lock, flags); if (!mdwc->id_polling_start) { spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); return; } queue_delayed_work(mdwc->id_polling_q, to_delayed_work(w), msecs_to_jiffies(mdwc->id_polling_up_interval)); spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); pr_debug("id polling, interval=%u ms, period=%u us\n", mdwc->id_polling_up_interval, mdwc->id_polling_up_period); __pm_stay_awake(&mdwc->id_polling_wu); dwc3_id_pullup(mdwc, 1); if (mdwc->id_polling_up_period) { if (mdwc->id_polling_up_period < 10) { udelay(mdwc->id_polling_up_period); } else if (mdwc->id_polling_up_period < 20000) { delta = mdwc->id_polling_up_period / 10; usleep_range(mdwc->id_polling_up_period - delta, mdwc->id_polling_up_period + delta); } else if (mdwc->id_polling_up_period / 1000 < mdwc->id_polling_up_interval) { msleep(mdwc->id_polling_up_period / 1000); } else { pr_warn("pull up period is too long because it is longer than interval.\n"); } } id = dwc3_get_usb_detect_adc(mdwc) ? DWC3_ID_FLOAT : DWC3_ID_GROUND; dwc3_id_pullup(mdwc, 0); spin_lock_irqsave(&mdwc->id_polling_lock, flags); otg_present = mdwc->otg_present; mdwc->otg_present = id == DWC3_ID_GROUND; dwc3_id_poll_update(mdwc); spin_unlock_irqrestore(&mdwc->id_polling_lock, flags); if (mdwc->otg_present != otg_present) { dwc3_msm_set_type_power_role(mdwc, mdwc->otg_present ? POWER_SUPPLY_TYPEC_PR_DUAL : POWER_SUPPLY_TYPEC_PR_SINK); __pm_wakeup_event(&mdwc->id_polling_wu, USB_ID_POLLING_WAKE_TIMEOUT); } __pm_relax(&mdwc->id_polling_wu); } #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ static int dwc3_cpu_notifier_cb(struct notifier_block *nfb, unsigned long action, void *hcpu) { Loading Loading @@ -2754,6 +3082,24 @@ static void check_for_sdp_connection(struct work_struct *w) } } static int dwc3_msm_vbus_drop_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { struct dwc3_msm *mdwc = container_of(nb, struct dwc3_msm, vbus_drop_nb); struct extcon_dev *edev = ptr; if (!edev) { dev_err(mdwc->dev, "%s: edev null\n", __func__); goto done; } set_bit(A_VBUS_DROP_DET, &mdwc->inputs); pr_info("%s: receive ocp notification\n", __func__); schedule_delayed_work(&mdwc->sm_work, 0); done: return NOTIFY_DONE; } static int dwc3_msm_vbus_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { Loading Loading @@ -2856,10 +3202,22 @@ static int dwc3_msm_extcon_register(struct dwc3_msm *mdwc) dev_err(mdwc->dev, "failed to register notifier for USB-HOST\n"); goto err; } mdwc->extcon_vbus_drop = edev; mdwc->vbus_drop_nb.notifier_call = dwc3_msm_vbus_drop_notifier; ret = extcon_register_notifier(edev, EXTCON_VBUS_DROP, &mdwc->vbus_drop_nb); if (ret < 0) { dev_err(mdwc->dev, "failed to register notifier for USB-DropT\n"); goto err; } } return 0; err: if (mdwc->extcon_id) extcon_unregister_notifier(mdwc->extcon_id, EXTCON_USB_HOST, &mdwc->id_nb); if (mdwc->extcon_vbus) extcon_unregister_notifier(mdwc->extcon_vbus, EXTCON_USB, &mdwc->vbus_nb); Loading Loading @@ -3063,6 +3421,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) } mdwc->id_state = DWC3_ID_FLOAT; mdwc->otg_present = true; set_bit(ID, &mdwc->inputs); mdwc->charging_disabled = of_property_read_bool(node, Loading Loading @@ -3136,6 +3495,16 @@ static int dwc3_msm_probe(struct platform_device *pdev) } } #ifdef CONFIG_USB_DWC3_MSM_ID_POLL mdwc->id_polling_use = of_property_read_bool(node, "id_polling_use"); if (mdwc->id_polling_use) { dev_info(&pdev->dev, "id polling is enabled\n"); mdwc->id_state = DWC3_ID_FLOAT; } wake_lock_init(&mdwc->setsink_lock, WAKE_LOCK_SUSPEND, "typecsink_lock"); #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcsr_base"); if (!res) { dev_dbg(&pdev->dev, "missing TCSR memory resource\n"); Loading Loading @@ -3353,6 +3722,10 @@ static int dwc3_msm_probe(struct platform_device *pdev) else if (mdwc->extcon_id && extcon_get_cable_state_(mdwc->extcon_id, EXTCON_USB_HOST)) dwc3_msm_id_notifier(&mdwc->id_nb, true, mdwc->extcon_id); else if (mdwc->extcon_vbus_drop && extcon_get_cable_state_( mdwc->extcon_vbus_drop, EXTCON_VBUS_DROP)) dwc3_msm_vbus_drop_notifier(&mdwc->vbus_drop_nb, true, mdwc->extcon_vbus_drop); else if (!pval.intval) { /* USB cable is not connected */ queue_delayed_work(mdwc->sm_usb_wq, &mdwc->sm_work, 0); Loading Loading @@ -3382,6 +3755,47 @@ static int dwc3_msm_probe(struct platform_device *pdev) place_marker(boot_marker); #ifdef CONFIG_USB_DWC3_MSM_ID_POLL if (mdwc->id_polling_use) { INIT_DELAYED_WORK(&mdwc->setsink_work, dwc3_setsink_work); INIT_DELAYED_WORK(&mdwc->id_polling_work, dwc3_id_polling_work); mdwc->id_polling_q = create_singlethread_workqueue("id_polling_q"); mdwc->id_polling_up_interval = USB_ID_POLLING_UP_INTERVAL; of_property_read_u32(node, "id_polling_up_interval", &mdwc->id_polling_up_interval); dev_dbg(&pdev->dev, "id_polling_up_interval=%dms\n", mdwc->id_polling_up_interval); device_create_file(&pdev->dev, &dev_attr_id_polling_up_interval); mdwc->id_polling_up_period = USB_ID_POLLING_UP_PERIOD; of_property_read_u32(node, "id_polling_up_period", &mdwc->id_polling_up_period); dev_dbg(&pdev->dev, "id_polling_up_period=%dus\n", mdwc->id_polling_up_period); device_create_file(&pdev->dev, &dev_attr_id_polling_up_period); mdwc->id_polling_pd_gpio = of_get_named_gpio(node, "id_polling_pd_gpio", 0); if (!gpio_is_valid(mdwc->id_polling_pd_gpio)) dev_info(&pdev->dev, "id_polling_pd is missing\n"); wakeup_source_init(&mdwc->id_polling_wu, "id_polling"); spin_lock_init(&mdwc->id_polling_lock); #ifdef CONFIG_FB mdwc->fb_notif.notifier_call = fb_notifier_callback; if (fb_register_client(&mdwc->fb_notif)) dev_err(mdwc->dev, "failed to register fb_notifier\n"); #endif /* CONFIG_FB */ mdwc->id_polling_start = true; queue_delayed_work(mdwc->id_polling_q, &mdwc->id_polling_work, msecs_to_jiffies(mdwc->id_polling_up_interval)); } #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ return 0; put_dwc3: Loading @@ -3389,6 +3803,7 @@ put_dwc3: msm_bus_scale_unregister_client(mdwc->bus_perf_client); of_platform_depopulate(&pdev->dev); err: wake_lock_destroy(&mdwc->setsink_lock); destroy_workqueue(mdwc->dwc3_wq); return ret; } Loading Loading @@ -3425,6 +3840,22 @@ static int dwc3_msm_remove(struct platform_device *pdev) } cancel_delayed_work_sync(&mdwc->perf_vote_work); #ifdef CONFIG_USB_DWC3_MSM_ID_POLL cancel_delayed_work_sync(&mdwc->setsink_work); wake_lock_destroy(&mdwc->setsink_lock); if (mdwc->id_polling_use) { #ifdef CONFIG_FB fb_unregister_client(&mdwc->fb_notif); #endif /* CONFIG_FB */ wakeup_source_trash(&mdwc->id_polling_wu); device_remove_file(&pdev->dev, &dev_attr_id_polling_up_interval); device_remove_file(&pdev->dev, &dev_attr_id_polling_up_period); cancel_delayed_work_sync(&mdwc->id_polling_work); destroy_workqueue(mdwc->id_polling_q); } #endif /* CONFIG_USB_DWC3_MSM_ID_POLL */ cancel_delayed_work_sync(&mdwc->sm_work); if (mdwc->hs_phy) Loading Loading @@ -3912,7 +4343,6 @@ set_prop: return 0; } /** * dwc3_otg_sm_work - workqueue function. * Loading Loading @@ -4009,6 +4439,7 @@ static void dwc3_otg_sm_work(struct work_struct *w) work = 1; break; } else { mdwc->send_vbus_drop_ue = false; dwc3_msm_gadget_vbus_draw(mdwc, 0); pm_relax(mdwc->dev); dev_dbg(mdwc->dev, "Cable disconnected\n"); Loading Loading @@ -4074,9 +4505,22 @@ static void dwc3_otg_sm_work(struct work_struct *w) /* Switch to A-Device*/ if (test_bit(ID, &mdwc->inputs)) { dev_dbg(mdwc->dev, "id\n"); clear_bit(A_VBUS_DROP_DET, &mdwc->inputs); mdwc->drd_state = DRD_STATE_IDLE; mdwc->vbus_retry_count = 0; work = 1; #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION host_send_uevent(USB_HOST_EXT_EVENT_NONE); #endif } else if (test_bit(A_VBUS_DROP_DET, &mdwc->inputs)) { dev_dbg(mdwc->dev, "vbus_drop_det\n"); /* staying on here until exit from A-Device */ #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION if (!mdwc->send_vbus_drop_ue) { mdwc->send_vbus_drop_ue = true; host_send_uevent(USB_HOST_EXT_EVENT_VBUS_DROP); } #endif } else { mdwc->drd_state = DRD_STATE_HOST; ret = dwc3_otg_start_host(mdwc, 1); Loading Loading @@ -4116,6 +4560,20 @@ static void dwc3_otg_sm_work(struct work_struct *w) mdwc->vbus_retry_count = 0; mdwc->hc_died = false; work = 1; #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION host_send_uevent(USB_HOST_EXT_EVENT_NONE); #endif } else if (test_bit(A_VBUS_DROP_DET, &mdwc->inputs)) { dev_dbg(mdwc->dev, "vbus_drop_det\n"); dwc3_otg_start_host(mdwc, 0); mdwc->drd_state = DRD_STATE_HOST_IDLE; mdwc->vbus_retry_count = 0; #ifdef CONFIG_USB_HOST_EXTRA_NOTIFICATION if (!mdwc->send_vbus_drop_ue) { mdwc->send_vbus_drop_ue = true; host_send_uevent(USB_HOST_EXT_EVENT_VBUS_DROP); } #endif } else { dev_dbg(mdwc->dev, "still in a_host state. Resuming root hub.\n"); dbg_event(0xFF, "XHCIResume", 0); Loading
drivers/usb/host/Kconfig +7 −0 Original line number Diff line number Diff line Loading @@ -787,3 +787,10 @@ config USB_HCD_TEST_MODE This option is of interest only to developers who need to validate their USB hardware designs. It is not needed for normal use. If unsure, say N. config USB_HOST_EXTRA_NOTIFICATION bool "USB host extra notification" depends on USB default n help Provides the functions to notify extra USB host related events.
drivers/usb/host/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -76,3 +76,4 @@ obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o obj-$(CONFIG_USB_HOST_EXTRA_NOTIFICATION) += host_ext_event.o