修复展锐SL8541E 偶现开机无法启动OTG
哎,又是紫光展锐,真的拉啊!!!
问题是这样的,当USB口插上OTG线再开机,会偶现无法启动OTG。这个问题排查了好久,现在终于改好了,下面记录一下分析过程。
异常log【上】和正常log【下】
前面部分就不贴了,都是一样的。可以看到开机时通知器会上报两个通知,先来的是vbus notify,后来的id notify。出现异常时vbus的通知器函数会去设置状态机,但是状态机的work queue还没跑完呢,id的通知器函数就开始执行了。经过分析,正确设置状态机就需要让各个通知器先等状态机的work queue跑完了再进行。
最终修改方案,使用一个原子量来给id、vbus通知器回调函数与状态机的work_queue做同步。这里为什么要用原子量,因为展锐的这个驱动写的太烂了,work_queue有可能会出现长时间卡在里面,因此不得不使用原子量,死等肯定是不行的。
diff --git a/drivers/usb/musb/musb_sprd.c b/drivers/usb/musb/musb_sprd.c index d32027a..c8dc027 100644 --- a/drivers/usb/musb/musb_sprd.c +++ b/drivers/usb/musb/musb_sprd.c @@ -34,6 +34,7 @@ #include <linux/wait.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> +#include <linux/mutex.h> #include "musb_core.h" #include "sprd_musbhsdma.h" @@ -44,6 +45,7 @@ MODULE_DESCRIPTION(DRIVER_INFO); MODULE_LICENSE("GPL v2"); +static DEFINE_MUTEX(musb_sprd_extcon_mutex); #define MUSB_RECOVER_TIMEOUT 100 struct sprd_glue { @@ -53,6 +55,8 @@ struct sprd_glue { struct phy *phy; struct usb_phy *xceiv; struct regulator *vbus; + struct regulator *eth; + struct regulator *usbnet; struct wakeup_source pd_wake_lock; struct regmap *pmu; @@ -71,6 +75,9 @@ struct sprd_glue { struct notifier_block vbus_nb; struct notifier_block id_nb; + atomic_t workqueue_running; /* for extcon sync */ + atomic_t extcon_running; /* for extcon sync */ + bool bus_active; bool vbus_active; bool charging_mode; @@ -85,6 +92,13 @@ struct sprd_glue { }; static int boot_charging; +static char* musb_dr_mode_show[] = { + "unknown", + "host", + "device", + "otg", +}; + static void sprd_musb_enable(struct musb *musb) { struct sprd_glue *glue = dev_get_drvdata(musb->controller->parent); @@ -452,41 +466,44 @@ static int musb_sprd_vbus_notifier(struct notifier_block *nb, unsigned long event, void *data) { struct sprd_glue *glue = container_of(nb, struct sprd_glue, vbus_nb); - unsigned long flags; + int try = 500; + + mutex_lock(&musb_sprd_extcon_mutex); + while (atomic_read(&glue->workqueue_running) && try--) + msleep(1); if (event) { - spin_lock_irqsave(&glue->lock, flags); if (glue->vbus_active == 1 || glue->dr_mode == USB_DR_MODE_HOST) { - spin_unlock_irqrestore(&glue->lock, flags); dev_info(glue->dev, "ignore device connection detected from VBUS GPIO.\n"); + mutex_unlock(&musb_sprd_extcon_mutex); return 0; } if (glue->dpdm_switch) gpiod_set_value_cansleep(glue->dpdm_switch, 0); glue->vbus_active = 1; glue->wq_mode = USB_DR_MODE_PERIPHERAL; + atomic_set(&glue->workqueue_running, 1); queue_work(system_unbound_wq, &glue->work); - spin_unlock_irqrestore(&glue->lock, flags); dev_info(glue->dev, "device connection detected from VBUS GPIO.\n"); } else { - spin_lock_irqsave(&glue->lock, flags); if (glue->vbus_active == 0 || glue->dr_mode == USB_DR_MODE_HOST) { - spin_unlock_irqrestore(&glue->lock, flags); dev_info(glue->dev, "ignore device disconnect detected from VBUS GPIO.\n"); + mutex_unlock(&musb_sprd_extcon_mutex); return 0; } glue->vbus_active = 0; glue->wq_mode = USB_DR_MODE_PERIPHERAL; + atomic_set(&glue->workqueue_running, 1); queue_work(system_unbound_wq, &glue->work); - spin_unlock_irqrestore(&glue->lock, flags); dev_info(glue->dev, "device disconnect detected from VBUS GPIO.\n"); } + mutex_unlock(&musb_sprd_extcon_mutex); return 0; } @@ -494,41 +511,44 @@ static int musb_sprd_id_notifier(struct notifier_block *nb, unsigned long event, void *data) { struct sprd_glue *glue = container_of(nb, struct sprd_glue, id_nb); - unsigned long flags; + int try = 500; + + mutex_lock(&musb_sprd_extcon_mutex); + while (atomic_read(&glue->workqueue_running) && try--) + msleep(1); if (event) { - spin_lock_irqsave(&glue->lock, flags); if (glue->vbus_active == 1 || glue->dr_mode == USB_DR_MODE_PERIPHERAL) { - spin_unlock_irqrestore(&glue->lock, flags); dev_info(glue->dev, "ignore host connection detected from ID GPIO.\n"); + mutex_unlock(&musb_sprd_extcon_mutex); return 0; } if (glue->dpdm_switch) gpiod_set_value_cansleep(glue->dpdm_switch, 1); glue->vbus_active = 1; glue->wq_mode = USB_DR_MODE_HOST; + atomic_set(&glue->workqueue_running, 1); queue_work(system_unbound_wq, &glue->work); - spin_unlock_irqrestore(&glue->lock, flags); dev_info(glue->dev, "host connection detected from ID GPIO.\n"); } else { - spin_lock_irqsave(&glue->lock, flags); if (glue->vbus_active == 0 || glue->dr_mode == USB_DR_MODE_PERIPHERAL) { - spin_unlock_irqrestore(&glue->lock, flags); dev_info(glue->dev, "ignore host disconnect detected from ID GPIO.\n"); + mutex_unlock(&musb_sprd_extcon_mutex); return 0; } glue->vbus_active = 0; glue->wq_mode = USB_DR_MODE_HOST; + atomic_set(&glue->workqueue_running, 1); queue_work(system_unbound_wq, &glue->work); - spin_unlock_irqrestore(&glue->lock, flags); dev_info(glue->dev, "host disconnect detected from ID GPIO.\n"); } + mutex_unlock(&musb_sprd_extcon_mutex); return 0; } @@ -651,16 +671,16 @@ static bool musb_sprd_is_connect_host(struct sprd_glue *glue) return false; } -static __init int musb_sprd_charger_mode(char *str) -{ - if (strcmp(str, "charger")) - boot_charging = 0; - else - boot_charging = 1; +// static __init int musb_sprd_charger_mode(char *str) +// { +// if (strcmp(str, "charger")) +// boot_charging = 0; +// else +// boot_charging = 1; - return 0; -} -__setup("androidboot.mode=", musb_sprd_charger_mode); +// return 0; +// } +// __setup("androidboot.mode=", musb_sprd_charger_mode); static void sprd_musb_recover_work(struct work_struct *work) { @@ -704,13 +724,17 @@ static void sprd_musb_work(struct work_struct *work) int ret; int cnt = 100; + dev_info(glue->dev, "enter sprd_musb_work()\n"); + atomic_set(&glue->workqueue_running, 1); spin_lock_irqsave(&glue->lock, flags); current_mode = glue->wq_mode; current_state = glue->vbus_active; glue->wq_mode = USB_DR_MODE_UNKNOWN; spin_unlock_irqrestore(&glue->lock, flags); - if (current_mode == USB_DR_MODE_UNKNOWN) + if (current_mode == USB_DR_MODE_UNKNOWN) { + atomic_set(&glue->workqueue_running, 0); return; + } /* * There is a hidden danger, when system is going to suspend. @@ -780,7 +804,10 @@ static void sprd_musb_work(struct work_struct *work) if (glue->dr_mode == USB_DR_MODE_HOST) { if (!glue->vbus) { glue->vbus = devm_regulator_get(glue->dev, "vbus"); - if (IS_ERR(glue->vbus)) { + glue->eth = devm_regulator_get(glue->dev, "eth"); + glue->usbnet = devm_regulator_get(glue->dev, "usbnet"); + if (IS_ERR(glue->vbus) || IS_ERR(glue->eth) \ + || IS_ERR(glue->usbnet)) { dev_err(glue->dev, "unable to get vbus supply\n"); glue->vbus = NULL; @@ -788,10 +815,12 @@ static void sprd_musb_work(struct work_struct *work) } } ret = regulator_enable(glue->vbus); + ret = regulator_enable(glue->eth); + ret = regulator_enable(glue->usbnet); if (ret) { dev_err(glue->dev, "Failed to enable vbus: %d\n", ret); - goto end; + // goto end; } } @@ -869,10 +898,12 @@ static void sprd_musb_work(struct work_struct *work) if (glue->dr_mode == USB_DR_MODE_HOST && glue->vbus) { ret = regulator_disable(glue->vbus); + ret = regulator_disable(glue->eth); + ret = regulator_disable(glue->usbnet); if (ret) { dev_err(glue->dev, "Failed to disable vbus: %d\n", ret); - goto end; + // goto end; } } @@ -909,6 +940,7 @@ static void sprd_musb_work(struct work_struct *work) glue->schedule_work_done_flag = 1; __pm_relax(&glue->pd_wake_lock); enable_irq(glue->vbus_irq); + atomic_set(&glue->workqueue_running, 0); } /** @@ -1031,7 +1063,38 @@ static ssize_t mode_switch_store(struct device *dev, return count; } -DEVICE_ATTR_WO(mode_switch); +static ssize_t mode_switch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sprd_glue *glue = dev_get_drvdata(dev); + const char *str = NULL; + + switch (glue->dr_mode) { + case USB_DR_MODE_UNKNOWN : + str = musb_dr_mode_show[USB_DR_MODE_UNKNOWN]; + break; + + case USB_DR_MODE_HOST : + str = musb_dr_mode_show[USB_DR_MODE_HOST]; + break; + + case USB_DR_MODE_PERIPHERAL : + str = musb_dr_mode_show[USB_DR_MODE_PERIPHERAL]; + break; + + case USB_DR_MODE_OTG : + str = musb_dr_mode_show[USB_DR_MODE_OTG]; + break; + + default: + str = musb_dr_mode_show[USB_DR_MODE_UNKNOWN]; + break; + } + + return sprintf(buf, "%s\n", str); +} + +DEVICE_ATTR_RW(mode_switch); static ssize_t maximum_speed_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1127,6 +1190,9 @@ static int musb_sprd_probe(struct platform_device *pdev) else dev_err(&pdev->dev, "Invalid or missing 'dr_mode' property\n"); + atomic_set(&glue->workqueue_running, 0); + atomic_set(&glue->extcon_running, 0); + glue->clk = devm_clk_get(dev, "core_clk"); if (IS_ERR(glue->clk)) { dev_err(dev, "no core clk specified\n"); @@ -1148,6 +1214,8 @@ static int musb_sprd_probe(struct platform_device *pdev) if (pdata.mode == MUSB_PORT_MODE_HOST || pdata.mode == MUSB_PORT_MODE_DUAL_ROLE) { glue->vbus = devm_regulator_get(dev, "vbus"); + glue->eth = devm_regulator_get(dev, "eth"); + glue->usbnet = devm_regulator_get(dev, "usbnet"); if (IS_ERR(glue->vbus)) { ret = PTR_ERR(glue->vbus); dev_warn(dev, "unable to get vbus supply %d\n", ret); @@ -1229,7 +1297,7 @@ static int musb_sprd_probe(struct platform_device *pdev) goto err_glue_musb; } glue->id_nb.notifier_call = musb_sprd_id_notifier; - glue->id_edev = extcon_get_edev_by_phandle(glue->dev, 1); + glue->id_edev = extcon_get_edev_by_phandle(glue->dev, 0); if (IS_ERR(glue->id_edev)) { glue->id_edev = NULL; dev_info(dev, "No separate ID extcon device.\n"); @@ -1390,6 +1458,8 @@ static int musb_sprd_suspend(struct device *dev) if (musb->is_offload && !musb->offload_used) { if (glue->vbus) { ret = regulator_disable(glue->vbus); + ret = regulator_disable(glue->eth); + ret = regulator_disable(glue->usbnet); if (ret < 0) dev_err(glue->dev, "Failed to disable vbus: %d\n", ret); @@ -1416,6 +1486,8 @@ static int musb_sprd_resume(struct device *dev) if (musb->is_offload && !musb->offload_used) { if (glue->vbus) { ret = regulator_enable(glue->vbus); + ret = regulator_enable(glue->eth); + ret = regulator_enable(glue->usbnet); if (ret < 0) dev_err(glue->dev, "Failed to enable vbus: %d\n", ret);
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程