Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
cloud-kernel
提交
eaaffcef
cloud-kernel
项目概览
openanolis
/
cloud-kernel
大约 2 年 前同步成功
通知
173
Star
36
Fork
7
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
10
列表
看板
标记
里程碑
合并请求
2
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
cloud-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
10
Issue
10
列表
看板
标记
里程碑
合并请求
2
合并请求
2
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
eaaffcef
编写于
11月 02, 2017
作者:
U
Ulf Hansson
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'fixes' into next
上级
2d1d31dd
9d9491a7
变更
1
显示空白变更内容
内联
并排
Showing
1 changed file
with
94 addition
and
13 deletion
+94
-13
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/dw_mmc.c
+94
-13
未找到文件。
drivers/mmc/host/dw_mmc.c
浏览文件 @
eaaffcef
...
...
@@ -401,16 +401,37 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
static
inline
void
dw_mci_set_cto
(
struct
dw_mci
*
host
)
{
unsigned
int
cto_clks
;
unsigned
int
cto_div
;
unsigned
int
cto_ms
;
unsigned
long
irqflags
;
cto_clks
=
mci_readl
(
host
,
TMOUT
)
&
0xff
;
cto_ms
=
DIV_ROUND_UP
(
cto_clks
,
host
->
bus_hz
/
1000
);
cto_div
=
(
mci_readl
(
host
,
CLKDIV
)
&
0xff
)
*
2
;
if
(
cto_div
==
0
)
cto_div
=
1
;
cto_ms
=
DIV_ROUND_UP
(
MSEC_PER_SEC
*
cto_clks
*
cto_div
,
host
->
bus_hz
);
/* add a bit spare time */
cto_ms
+=
10
;
/*
* The durations we're working with are fairly short so we have to be
* extra careful about synchronization here. Specifically in hardware a
* command timeout is _at most_ 5.1 ms, so that means we expect an
* interrupt (either command done or timeout) to come rather quickly
* after the mci_writel. ...but just in case we have a long interrupt
* latency let's add a bit of paranoia.
*
* In general we'll assume that at least an interrupt will be asserted
* in hardware by the time the cto_timer runs. ...and if it hasn't
* been asserted in hardware by that time then we'll assume it'll never
* come.
*/
spin_lock_irqsave
(
&
host
->
irq_lock
,
irqflags
);
if
(
!
test_bit
(
EVENT_CMD_COMPLETE
,
&
host
->
pending_events
))
mod_timer
(
&
host
->
cto_timer
,
jiffies
+
msecs_to_jiffies
(
cto_ms
)
+
1
);
spin_unlock_irqrestore
(
&
host
->
irq_lock
,
irqflags
);
}
static
void
dw_mci_start_command
(
struct
dw_mci
*
host
,
...
...
@@ -425,11 +446,11 @@ static void dw_mci_start_command(struct dw_mci *host,
wmb
();
/* drain writebuffer */
dw_mci_wait_while_busy
(
host
,
cmd_flags
);
mci_writel
(
host
,
CMD
,
cmd_flags
|
SDMMC_CMD_START
);
/* response expected command only */
if
(
cmd_flags
&
SDMMC_CMD_RESP_EXP
)
dw_mci_set_cto
(
host
);
mci_writel
(
host
,
CMD
,
cmd_flags
|
SDMMC_CMD_START
);
}
static
inline
void
send_stop_abort
(
struct
dw_mci
*
host
,
struct
mmc_data
*
data
)
...
...
@@ -1915,10 +1936,15 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
static
void
dw_mci_set_drto
(
struct
dw_mci
*
host
)
{
unsigned
int
drto_clks
;
unsigned
int
drto_div
;
unsigned
int
drto_ms
;
drto_clks
=
mci_readl
(
host
,
TMOUT
)
>>
8
;
drto_ms
=
DIV_ROUND_UP
(
drto_clks
,
host
->
bus_hz
/
1000
);
drto_div
=
(
mci_readl
(
host
,
CLKDIV
)
&
0xff
)
*
2
;
if
(
drto_div
==
0
)
drto_div
=
1
;
drto_ms
=
DIV_ROUND_UP
(
MSEC_PER_SEC
*
drto_clks
*
drto_div
,
host
->
bus_hz
);
/* add a bit spare time */
drto_ms
+=
10
;
...
...
@@ -1926,6 +1952,24 @@ static void dw_mci_set_drto(struct dw_mci *host)
mod_timer
(
&
host
->
dto_timer
,
jiffies
+
msecs_to_jiffies
(
drto_ms
));
}
static
bool
dw_mci_clear_pending_cmd_complete
(
struct
dw_mci
*
host
)
{
if
(
!
test_bit
(
EVENT_CMD_COMPLETE
,
&
host
->
pending_events
))
return
false
;
/*
* Really be certain that the timer has stopped. This is a bit of
* paranoia and could only really happen if we had really bad
* interrupt latency and the interrupt routine and timeout were
* running concurrently so that the del_timer() in the interrupt
* handler couldn't run.
*/
WARN_ON
(
del_timer_sync
(
&
host
->
cto_timer
));
clear_bit
(
EVENT_CMD_COMPLETE
,
&
host
->
pending_events
);
return
true
;
}
static
void
dw_mci_tasklet_func
(
unsigned
long
priv
)
{
struct
dw_mci
*
host
=
(
struct
dw_mci
*
)
priv
;
...
...
@@ -1952,8 +1996,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
case
STATE_SENDING_CMD11
:
case
STATE_SENDING_CMD
:
if
(
!
test_and_clear_bit
(
EVENT_CMD_COMPLETE
,
&
host
->
pending_events
))
if
(
!
dw_mci_clear_pending_cmd_complete
(
host
))
break
;
cmd
=
host
->
cmd
;
...
...
@@ -2122,8 +2165,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
/* fall through */
case
STATE_SENDING_STOP
:
if
(
!
test_and_clear_bit
(
EVENT_CMD_COMPLETE
,
&
host
->
pending_events
))
if
(
!
dw_mci_clear_pending_cmd_complete
(
host
))
break
;
/* CMD error in data command */
...
...
@@ -2570,6 +2612,8 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
static
void
dw_mci_cmd_interrupt
(
struct
dw_mci
*
host
,
u32
status
)
{
del_timer
(
&
host
->
cto_timer
);
if
(
!
host
->
cmd_status
)
host
->
cmd_status
=
status
;
...
...
@@ -2594,6 +2638,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
struct
dw_mci
*
host
=
dev_id
;
u32
pending
;
struct
dw_mci_slot
*
slot
=
host
->
slot
;
unsigned
long
irqflags
;
pending
=
mci_readl
(
host
,
MINTSTS
);
/* read-only mask reg */
...
...
@@ -2601,8 +2646,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
/* Check volt switch first, since it can look like an error */
if
((
host
->
state
==
STATE_SENDING_CMD11
)
&&
(
pending
&
SDMMC_INT_VOLT_SWITCH
))
{
unsigned
long
irqflags
;
mci_writel
(
host
,
RINTSTS
,
SDMMC_INT_VOLT_SWITCH
);
pending
&=
~
SDMMC_INT_VOLT_SWITCH
;
...
...
@@ -2618,11 +2661,15 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
}
if
(
pending
&
DW_MCI_CMD_ERROR_FLAGS
)
{
spin_lock_irqsave
(
&
host
->
irq_lock
,
irqflags
);
del_timer
(
&
host
->
cto_timer
);
mci_writel
(
host
,
RINTSTS
,
DW_MCI_CMD_ERROR_FLAGS
);
host
->
cmd_status
=
pending
;
smp_wmb
();
/* drain writebuffer */
set_bit
(
EVENT_CMD_COMPLETE
,
&
host
->
pending_events
);
spin_unlock_irqrestore
(
&
host
->
irq_lock
,
irqflags
);
}
if
(
pending
&
DW_MCI_DATA_ERROR_FLAGS
)
{
...
...
@@ -2662,9 +2709,12 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
}
if
(
pending
&
SDMMC_INT_CMD_DONE
)
{
del_timer
(
&
host
->
cto_timer
);
spin_lock_irqsave
(
&
host
->
irq_lock
,
irqflags
);
mci_writel
(
host
,
RINTSTS
,
SDMMC_INT_CMD_DONE
);
dw_mci_cmd_interrupt
(
host
,
pending
);
spin_unlock_irqrestore
(
&
host
->
irq_lock
,
irqflags
);
}
if
(
pending
&
SDMMC_INT_CD
)
{
...
...
@@ -2938,7 +2988,35 @@ static void dw_mci_cmd11_timer(unsigned long arg)
static
void
dw_mci_cto_timer
(
unsigned
long
arg
)
{
struct
dw_mci
*
host
=
(
struct
dw_mci
*
)
arg
;
unsigned
long
irqflags
;
u32
pending
;
spin_lock_irqsave
(
&
host
->
irq_lock
,
irqflags
);
/*
* If somehow we have very bad interrupt latency it's remotely possible
* that the timer could fire while the interrupt is still pending or
* while the interrupt is midway through running. Let's be paranoid
* and detect those two cases. Note that this is paranoia is somewhat
* justified because in this function we don't actually cancel the
* pending command in the controller--we just assume it will never come.
*/
pending
=
mci_readl
(
host
,
MINTSTS
);
/* read-only mask reg */
if
(
pending
&
(
DW_MCI_CMD_ERROR_FLAGS
|
SDMMC_INT_CMD_DONE
))
{
/* The interrupt should fire; no need to act but we can warn */
dev_warn
(
host
->
dev
,
"Unexpected interrupt latency
\n
"
);
goto
exit
;
}
if
(
test_bit
(
EVENT_CMD_COMPLETE
,
&
host
->
pending_events
))
{
/* Presumably interrupt handler couldn't delete the timer */
dev_warn
(
host
->
dev
,
"CTO timeout when already completed
\n
"
);
goto
exit
;
}
/*
* Continued paranoia to make sure we're in the state we expect.
* This paranoia isn't really justified but it seems good to be safe.
*/
switch
(
host
->
state
)
{
case
STATE_SENDING_CMD11
:
case
STATE_SENDING_CMD
:
...
...
@@ -2957,6 +3035,9 @@ static void dw_mci_cto_timer(unsigned long arg)
host
->
state
);
break
;
}
exit:
spin_unlock_irqrestore
(
&
host
->
irq_lock
,
irqflags
);
}
static
void
dw_mci_dto_timer
(
unsigned
long
arg
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录