修复展锐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);
修复展锐SL8541E 偶现开机无法启动OTG的更多相关文章
- 偶现bug如何处理?
请先允许我对此类bug进行吐槽,相信做测试的同学都碰见过这种bug! 我们在测试过程中经常会碰见一类很头疼的bug,就是偶现性的bug,所谓偶现性,是相对于必现而言,这类bug有些可以有重现路径,但是 ...
- [转帖]紫光展锐5G芯片
紫光展锐5G芯片已流片:7nm工艺 2019年问世 https://news.mydrivers.com/1/612/612346.htm 本文转载自超能网,其他媒体转载需经超能网同意 高通骁龙X ...
- CAT偶现NPE的问题
1.背景 我们公司的调用链系统是基于大众点评的CAT客户端改造的,服务端完全有自己设计开发的.在是用CAT客户端收集dubbo调用信息的时候,我们发现了一个CAT偶现NPE的bug,该bug隐藏的很深 ...
- Ubuntu16.04下写的Qt程序,调试时没问题,运行时偶现崩溃 (需要在运行时生成core dump文件,QMAKE_CC += -g)
记录一下 Ubuntu16.04下写的Qt程序,调试时没问题,运行时偶现崩溃 需要在运行时生成core dump文件 首先在pro结尾里加入 QMAKE_CC += -g QMAKE_CXX += - ...
- 【Golang】嗅探抓包,解决线上偶现问题来不及抓包的情况
背景 测试群里经常看到客户端的同学反馈发现了偶现Bug,但是来不及抓包,最后不了了之,最近出现得比较频繁,所以写个小脚本解决这个问题. 实现思路 实现的思路比较简单: 抓包 存日志 做日志管理 具体实 ...
- 程序员的踩坑经验总结(一):如何把Bug的偶现变必现
程序员的踩过的坑也是可以分类的,很常见又很难解决的一类是偶然的现象,表现起来比较怪异. 而把一个问题Bug的偶现变成必现,是开发人员的一种能力.我认为也应该是测试人员的一种能力,但是各个公司要求不一样 ...
- 完美解决 开机无法启动 提示0xc000000e
注:昨天装系统碰到这个问题,这个方法说的较详细,我的是WIN7系统,开机提示引导文件错误,代码为0xc000000e 无法进入系统,使用PE进入后,在运行里输入CMD,然后按下文红字开始操作 完美解决 ...
- Android开机动画启动流程
android开机动画启动流程 从android的Surface Flinger服务启动分析知道,开机动画是在SurfaceFlinger实例通过调用startBootAnim()启动的. 下面我 ...
- 关于自定义脚本rc.local里开机不启动的问题--以tomcat和perl相关的脚本为例
本文将自己遇到的一些自定义脚本加入开机启动项却不成功的问题加以说明,花费了我很长时间才得以解决,当然也多谢了自己朋友的帮忙,正是因为他们的提醒,最后才找到了解决的办法,谢谢他们!!!! 系统是cent ...
- 在 win10 环境下,设置自己写的 程序 开机自动 启动的方法
原文:在 win10 环境下,设置自己写的 程序 开机自动 启动的方法 1.是登录自己用户时才能开机启 C:\Users\username\AppData\Roaming\Microsoft\Wind ...
随机推荐
- css动画(仿微信聊天页面)
微信聊天框以其简洁直观的界面和流畅的交互体验而广受欢迎.本文将展示如何利用HTML和CSS技术,在自己的网页上实现类似微信的聊天框效果.我们将一步步指导您完成,让网站或应用也能拥有专业且用户友好的聊天 ...
- 通义灵码:体验AI编程新技能-@workspace 和 @terminal为你的编程插上一双翅膀
1.前言 我是一位运维工程师,用通义灵码个人版的@workspace 和 @terminal 的能力做快速了解一个工程.查找工程内的实现逻辑,以及执行指令不知道如何写,或者不清楚某个指令的意思,对比之 ...
- 什么DevOps方法论?
最近项目组事情越来越多,人员管理和项目事项管理成为了重点关注的问题,无意间听到同事间讨论DevOps方法论可以有效提升项目管理能力,实现组织精益化管理,运维一体化.于是我上网查了一下"Dev ...
- Kubernetes 中实现 MySQL 的读写分离
Kubernetes 中实现 MySQL 的读写分离 在 Kubernetes 中实现 MySQL 的读写分离,可以通过主从复制架构来实现.在这种架构中,MySQL 主节点(Master)负责处理所有 ...
- VMware安装教程---------------------以及Windows,Linux,Apple MAC OS系统安装
1.什么是VMware虚拟机 VMware虚拟机是一个虚拟机软件,它可以在一台机器上同时运行多个系统,这些系统包括Windows,Linux,Apple os等. 2.虚拟机有什么用 虚拟机的用处很多 ...
- hyperf的使用
hyperf是swoole的封装框架,用起来效率还是不错的. 使用方式看手册 https://hyperf.wiki/2.2/#/zh-cn/quick-start/install 其实是靠compo ...
- Apache Log4j2远程命令执行漏洞复现
目录 漏洞原理 复现 漏洞修复 Apache Log4j2 是一个基于Java的日志记录工具,被广泛应用于业务系统开发,开发者可以利用该工具将程序的输入输出信息进行日志记录.Log4j2 远程代码执行 ...
- 崖山数据库-监控运维平台-YCM 配置部署详解
准备工作:操作系统版本:[root@node10 ~]# uname -aLinux node10 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 ...
- JS之Class类
转载:https://juejin.cn/post/7098891689955164168 ECMAScript 6 提供了更接近传统语言的写法,新引入的class关键字具有正式定义类的能力.类(cl ...
- vue项目中如何加载markdown
场景 今天忽然临时接到一个需求: 就是将markdown文件直接在vue项目中进行加载,并正常显示出来. 这......,我知道是可以进行加载markdown文件的. 但是我之前没有做过,答复的是:可 ...