修复展锐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);

 

posted @   PYPYN  阅读(1701)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
点击右上角即可分享
微信分享提示