mmc: dw_mmc: add quirk for broken data transfer over scheme
Heiko Stübner
maintainerPATCH v6 1/1 mmc: dw_mmc: add quirk for broken data transfer over scheme
From: Addy Ke <[email protected]>
This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time.
dw_mmc call mmc_request_done func to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked.
We got the reply from synopsys:
There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped.
Host Starvation timeout counter is triggered with stop clock condition.
This means that host should get DRTO and DTO interrupt.
But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state.
There are two possibility that data over interrupt doesn't come in reading data state on RK3X SoCs:
- get command done interrupt, but doesn't get any data-related interrupt.
- get data error interrupt, but doesn't get data over interrupt.
Address review comments from Jaehoon Chung.
---
Changes in v2:
- fix some typo.
- remove extra timeout value (250ms).
- remove dw_mci_dto_start_monitor func.
- use "broken-dto" for new quirk and change Subject for it.
Changes in v3:
- Remove dts for broken-dto, just add this quirk in dw_mci_rockchip_init
Changes in v4:
- fix bug that may cause 32 bit overflow by (drto_clks * 1000).
- doesn't call mod_timer in writing data state, becase TMOUT register only for reading data.
Changes in v5:
- fix some typo.
- add a buffer for drto_ms.
- move drto_ms related code to a helper function.
Changes in v6:
- better spare time comment
- let dw_mci_set_drto start the timer itself
drivers/mmc/host/dw_mmc-rockchip.c | 3 ++ drivers/mmc/host/dw_mmc.c | 64 ++++++++++++++++++++++++++++++++++++-- include/linux/mmc/dw_mmc.h | 4 +++ 3 files changed, 69 insertions(+), 2 deletions(-)
drivers/mmc/host/dw_mmc- renamed from drivers/mmc/host/dw_mmc-rockchip.c +0 -0
rockchip.c
index de15121..bc76aa2 100644
drivers/mmc/host/dw_mmc-rockchip.c +3 -0
@@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host)/* It is slot 8 on Rockchip SoCs */ host->sdio_id0 =8 ; +/* It needs this quirk on all Rockchip SoCs */ + host->pdata->quirks |=DW_MCI_QUIRK_BROKEN_DTO ; +return 0 ; }
drivers/mmc/host/dw_mmc.c +0 -0
@@ -1528,6 +1528,20 @@ static int dw_mci_data_complete(struct dw_mci *host,
struct mmc_data *data)
return data->error;
}
+static void dw_mci_set_drto(struct dw_mci *host)
+{
+ unsigned int drto_clks;
+ unsigned int drto_ms;
+
+ drto_clks = mci_readl(host, TMOUT) >> 8;
+ drto_ms = DIV_ROUND_UP(drto_clks, host->bus_hz / 1000);
+
+ /* add a bit spare time */
+ drto_ms += 10;
+
+ mod_timer(&host->dto_timer, jiffies + msecs_to_jiffies(drto_ms));
+}
+
static void dw_mci_tasklet_func(unsigned long priv)
{
struct dw_mci *host = (struct dw_mci *)priv;
@@ -1605,8 +1619,16 @@ static void dw_mci_tasklet_func(unsigned long priv)
}
if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
- &host->pending_events))
+ &host->pending_events)) {
+ /*
+ * If all data-related interrupts don't come
+ * within the given time in reading data state.
+ */
+ if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) &&
+ (host->dir_status == DW_MCI_RECV_STATUS))
+ dw_mci_set_drto(host);
break;
+ }
set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
@@ -1639,8 +1661,17 @@ static void dw_mci_tasklet_func(unsigned long priv)
case STATE_DATA_BUSY:
if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
- &host->pending_events))
+ &host->pending_events)) {
+ /*
+ * If data error interrupt comes but data over
+ * interrupt doesn't come within the given time.
+ * in reading data state.
+ */
+ if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) &&
+ (host->dir_status == DW_MCI_RECV_STATUS))
+ dw_mci_set_drto(host);
break;
+ }
host->data = NULL; set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
@@ -2201,6 +2232,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
}
if (pending & SDMMC_INT_DATA_OVER) {
+ if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
+ del_timer(&host->dto_timer);
+
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status)
host->data_status = pending;
@@ -2593,6 +2627,28 @@ static void dw_mci_cmd11_timer(unsigned long arg)
tasklet_schedule(&host->tasklet);
}
+static void dw_mci_dto_timer(unsigned long arg) +{
+ struct dw_mci *host = (struct dw_mci *)arg;
+
+ switch (host->state) {
+ case STATE_SENDING_DATA:
+ case STATE_DATA_BUSY:
+ /*
+ * If DTO interrupt does NOT come in sending data state,
+ * we should notify the driver to terminate current transfer
+ * and report a data timeout to the core.
+ */
+ host->data_status = SDMMC_INT_DRTO;
+ set_bit(EVENT_DATA_ERROR, &host->pending_events);
+ set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+ tasklet_schedule(&host->tasklet);
+ break;
+ default:
+ break;
+ }
+}
+
#ifdef CONFIG_OF
static struct dw_mci_of_quirks {
char *quirk;
@@ -2769,6 +2825,10 @@ int dw_mci_probe(struct dw_mci *host)
host->quirks = host->pdata->quirks;
+ if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
+ setup_timer(&host->dto_timer,
+ dw_mci_dto_timer, (unsigned long)host);
+
spin_lock_init(&host->lock);
spin_lock_init(&host->irq_lock);
INIT_LIST_HEAD(&host->queue);
include/linux/mmc/dw_mmc.h +4 -0
@@ -98,6 +98,7 @@ struct mmc_data;* @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. * @sdio_id0: Number of slot0 in the SDIO interrupt registers. +* @dto_timer: Timer for broken data transfer over scheme. * * Locking * =======
@@ -204,6 +205,7 @@ struct dw_mci {int sdio_id0;struct timer_list cmd11_timer; +struct timer_list dto_timer; };/* DMA ops for Internal/External DMAC interface */
@@ -226,6 +228,8 @@ struct dw_mci_dma_ops {#define DW_MCI_QUIRK_HIGHSPEED BIT(2)/* Unreliable card detection */ #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) +/* Timer for broken data transfer over scheme */ +#define DW_MCI_QUIRK_BROKEN_DTO BIT(4)struct dma_pdata ;
Signature · 1 line
2.1.4
Jaehoon Chung
maintainerHi, Heiko.
Sorry, i had missed this patch. And thanks for reminding. I will check on today..and reply until tomorrow.
Best Regards,
Jaehoon Chung
Quoted 221 lines · Heiko Stübner
From: Addy Ke <[email protected]>
This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time.
dw_mmccallmmc_request_donefunc to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked.We got the reply from synopsys:
There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped.
Host Starvation timeout counter is triggered with stop clock condition.This means that host should get DRTO and DTO interrupt.
But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state.
There are two possibility that data over interrupt doesn't come in reading data state on RK3X SoCs:
- get command done interrupt, but doesn't get any data-related interrupt.
- get data error interrupt, but doesn't get data over interrupt.Signed-off-by Addy Ke <[email protected]>Address review comments from Jaehoon Chung.
Signed-off-by Heiko Stuebner <[email protected]>---
Changes in v2:
- fix some typo.
- remove extra timeout value (250ms).
- removedw_mci_dto_start_monitorfunc.
- use "broken-dto" for new quirk and change Subject for it.Changes in v3:
- Remove dts for broken-dto, just add this quirk indw_mci_rockchip_initChanges in v4:
- fix bug that may cause 32 bit overflow by (drto_clks* 1000).
- doesn't callmod_timerin writing data state, becase TMOUT register only for reading data.Changes in v5:
- fix some typo.
- add a buffer fordrto_ms.
- movedrto_msrelated code to a helper function.Changes in v6:
- better spare time comment
- letdw_mci_set_drtostart the timer itselfdrivers/mmc/host/dw_mmc-rockchip.c | 3 ++ drivers/mmc/host/dw_mmc.c | 64 ++++++++++++++++++++++++++++++++++++-- include/linux/mmc/dw_mmc.h | 4 +++ 3 files changed, 69 insertions(+), 2 deletions(-)drivers/mmc/host/dw_mmc- renamed from drivers/mmc/host/dw_mmc-rockchip.c +0
-0
rockchip.c
index de15121..bc76aa2 100644drivers/mmc/host/dw_mmc-rockchip.c +3
-0@@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host)/* It is slot 8 on Rockchip SoCs */ host->sdio_id0 =8 ; +/* It needs this quirk on all Rockchip SoCs */ + host->pdata->quirks |=DW_MCI_QUIRK_BROKEN_DTO ; +return 0 ; }drivers/mmc/host/dw_mmc.c +14
-0@@ -1528,6 +1528,20 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)return data->error; } +static void dw_mci_set_drto (struct dw_mci *host) +{ +unsigned int drto_clks; +unsigned int drto_ms; + + drto_clks =mci_readl (host,TMOUT ) >>8 ; + drto_ms =DIV_ROUND_UP (drto_clks, host->bus_hz /1000 ); + +/* add a bit spare time */ + drto_ms +=10 ; + +mod_timer (&host->dto_timer, jiffies +msecs_to_jiffies (drto_ms)); +} +static void dw_mci_tasklet_func (unsigned long priv) {drivers/mmc/host/dw_mmc.c +22
-2struct dw_mci *host = (struct dw_mci *)priv;@@ -1605,8 +1619,16 @@ static void dw_mci_tasklet_func(unsigned long priv) }if (!test_and_clear_bit (EVENT_XFER_COMPLETE , - &host->pending_events)) + &host->pending_events)) { +/* +* If all data-related interrupts don't come +* within the given time in reading data state. +*/ +if ((host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) && + (host->dir_status ==DW_MCI_RECV_STATUS )) +dw_mci_set_drto (host);break ; + }set_bit (EVENT_XFER_COMPLETE , &host->completed_events);@@ -1639,8 +1661,17 @@ static void dw_mci_tasklet_func(unsigned long priv)case STATE_DATA_BUSY :if (!test_and_clear_bit (EVENT_DATA_COMPLETE , - &host->pending_events)) + &host->pending_events)) { +/* +* If data error interrupt comes but data over +* interrupt doesn't come within the given time. +* in reading data state. +*/ +if ((host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) && + (host->dir_status ==DW_MCI_RECV_STATUS )) +dw_mci_set_drto (host);break ; + } host->data =NULL ;set_bit (EVENT_DATA_COMPLETE , &host->completed_events);@@ -2201,6 +2232,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) }if (pending &SDMMC_INT_DATA_OVER ) { +if (host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) +del_timer (&host->dto_timer); +mci_writel (host,RINTSTS ,SDMMC_INT_DATA_OVER );if (!host->data_status)drivers/mmc/host/dw_mmc.c +26
-0host->data_status = pending;@@ -2593,6 +2627,28 @@ static void dw_mci_cmd11_timer(unsigned long arg)tasklet_schedule (&host->tasklet); } +static void dw_mci_dto_timer (unsigned long arg) +{ +struct dw_mci *host = (struct dw_mci *)arg; + +switch (host->state) { +case STATE_SENDING_DATA : +case STATE_DATA_BUSY : +/* +* If DTO interrupt does NOT come in sending data state, +* we should notify the driver to terminate current transfer +* and report a data timeout to the core. +*/ + host->data_status =SDMMC_INT_DRTO ; +set_bit (EVENT_DATA_ERROR , &host->pending_events); +set_bit (EVENT_DATA_COMPLETE , &host->pending_events); +tasklet_schedule (&host->tasklet); +break ; +default : +break ; + } +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk;@@ -2769,6 +2825,10 @@ int dw_mci_probe(struct dw_mci *host) host->quirks = host->pdata->quirks; +if (host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) +setup_timer (&host->dto_timer, + dw_mci_dto_timer, (unsigned long )host); +spin_lock_init (&host->lock);spin_lock_init (&host->irq_lock);INIT_LIST_HEAD (&host->queue);include/linux/mmc/dw_mmc.h +4
-0@@ -98,6 +98,7 @@ struct mmc_data;* @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. * @sdio_id0: Number of slot0 in the SDIO interrupt registers. +* @dto_timer: Timer for broken data transfer over scheme. * * Locking * ======= @@ -204,6 +205,7 @@ struct dw_mci {int sdio_id0;struct timer_list cmd11_timer; +struct timer_list dto_timer; };/* DMA ops for Internal/External DMAC interface */ @@ -226,6 +228,8 @@ struct dw_mci_dma_ops {#define DW_MCI_QUIRK_HIGHSPEED BIT(2)/* Unreliable card detection */ #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) +/* Timer for broken data transfer over scheme */ +#define DW_MCI_QUIRK_BROKEN_DTO BIT(4)struct dma_pdata ;
Hi, Heiko.
Is this patch based on ulf's tree?
At my dw-mmc tree, this patch can't apply..
So if you're ok, i will rework this patch about my dw-mmc tree. how about?
Best Regards,
Jaehoon Chung
Quoted 221 lines · Heiko Stübner
From: Addy Ke <[email protected]>
This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time.
dw_mmccallmmc_request_donefunc to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked.We got the reply from synopsys:
There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped.
Host Starvation timeout counter is triggered with stop clock condition.This means that host should get DRTO and DTO interrupt.
But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state.
There are two possibility that data over interrupt doesn't come in reading data state on RK3X SoCs:
- get command done interrupt, but doesn't get any data-related interrupt.
- get data error interrupt, but doesn't get data over interrupt.Signed-off-by Addy Ke <[email protected]>Address review comments from Jaehoon Chung.
Signed-off-by Heiko Stuebner <[email protected]>---
Changes in v2:
- fix some typo.
- remove extra timeout value (250ms).
- removedw_mci_dto_start_monitorfunc.
- use "broken-dto" for new quirk and change Subject for it.Changes in v3:
- Remove dts for broken-dto, just add this quirk indw_mci_rockchip_initChanges in v4:
- fix bug that may cause 32 bit overflow by (drto_clks* 1000).
- doesn't callmod_timerin writing data state, becase TMOUT register only for reading data.Changes in v5:
- fix some typo.
- add a buffer fordrto_ms.
- movedrto_msrelated code to a helper function.Changes in v6:
- better spare time comment
- letdw_mci_set_drtostart the timer itselfdrivers/mmc/host/dw_mmc-rockchip.c | 3 ++ drivers/mmc/host/dw_mmc.c | 64 ++++++++++++++++++++++++++++++++++++-- include/linux/mmc/dw_mmc.h | 4 +++ 3 files changed, 69 insertions(+), 2 deletions(-)drivers/mmc/host/dw_mmc- renamed from drivers/mmc/host/dw_mmc-rockchip.c +0
-0
rockchip.c
index de15121..bc76aa2 100644drivers/mmc/host/dw_mmc-rockchip.c +3
-0@@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host)/* It is slot 8 on Rockchip SoCs */ host->sdio_id0 =8 ; +/* It needs this quirk on all Rockchip SoCs */ + host->pdata->quirks |=DW_MCI_QUIRK_BROKEN_DTO ; +return 0 ; }drivers/mmc/host/dw_mmc.c +14
-0@@ -1528,6 +1528,20 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)return data->error; } +static void dw_mci_set_drto (struct dw_mci *host) +{ +unsigned int drto_clks; +unsigned int drto_ms; + + drto_clks =mci_readl (host,TMOUT ) >>8 ; + drto_ms =DIV_ROUND_UP (drto_clks, host->bus_hz /1000 ); + +/* add a bit spare time */ + drto_ms +=10 ; + +mod_timer (&host->dto_timer, jiffies +msecs_to_jiffies (drto_ms)); +} +static void dw_mci_tasklet_func (unsigned long priv) {drivers/mmc/host/dw_mmc.c +22
-2struct dw_mci *host = (struct dw_mci *)priv;@@ -1605,8 +1619,16 @@ static void dw_mci_tasklet_func(unsigned long priv) }if (!test_and_clear_bit (EVENT_XFER_COMPLETE , - &host->pending_events)) + &host->pending_events)) { +/* +* If all data-related interrupts don't come +* within the given time in reading data state. +*/ +if ((host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) && + (host->dir_status ==DW_MCI_RECV_STATUS )) +dw_mci_set_drto (host);break ; + }set_bit (EVENT_XFER_COMPLETE , &host->completed_events);@@ -1639,8 +1661,17 @@ static void dw_mci_tasklet_func(unsigned long priv)case STATE_DATA_BUSY :if (!test_and_clear_bit (EVENT_DATA_COMPLETE , - &host->pending_events)) + &host->pending_events)) { +/* +* If data error interrupt comes but data over +* interrupt doesn't come within the given time. +* in reading data state. +*/ +if ((host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) && + (host->dir_status ==DW_MCI_RECV_STATUS )) +dw_mci_set_drto (host);break ; + } host->data =NULL ;set_bit (EVENT_DATA_COMPLETE , &host->completed_events);@@ -2201,6 +2232,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) }if (pending &SDMMC_INT_DATA_OVER ) { +if (host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) +del_timer (&host->dto_timer); +mci_writel (host,RINTSTS ,SDMMC_INT_DATA_OVER );if (!host->data_status)drivers/mmc/host/dw_mmc.c +26
-0host->data_status = pending;@@ -2593,6 +2627,28 @@ static void dw_mci_cmd11_timer(unsigned long arg)tasklet_schedule (&host->tasklet); } +static void dw_mci_dto_timer (unsigned long arg) +{ +struct dw_mci *host = (struct dw_mci *)arg; + +switch (host->state) { +case STATE_SENDING_DATA : +case STATE_DATA_BUSY : +/* +* If DTO interrupt does NOT come in sending data state, +* we should notify the driver to terminate current transfer +* and report a data timeout to the core. +*/ + host->data_status =SDMMC_INT_DRTO ; +set_bit (EVENT_DATA_ERROR , &host->pending_events); +set_bit (EVENT_DATA_COMPLETE , &host->pending_events); +tasklet_schedule (&host->tasklet); +break ; +default : +break ; + } +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk;@@ -2769,6 +2825,10 @@ int dw_mci_probe(struct dw_mci *host) host->quirks = host->pdata->quirks; +if (host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) +setup_timer (&host->dto_timer, + dw_mci_dto_timer, (unsigned long )host); +spin_lock_init (&host->lock);spin_lock_init (&host->irq_lock);INIT_LIST_HEAD (&host->queue);include/linux/mmc/dw_mmc.h +4
-0@@ -98,6 +98,7 @@ struct mmc_data;* @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. * @sdio_id0: Number of slot0 in the SDIO interrupt registers. +* @dto_timer: Timer for broken data transfer over scheme. * * Locking * ======= @@ -204,6 +205,7 @@ struct dw_mci {int sdio_id0;struct timer_list cmd11_timer; +struct timer_list dto_timer; };/* DMA ops for Internal/External DMAC interface */ @@ -226,6 +228,8 @@ struct dw_mci_dma_ops {#define DW_MCI_QUIRK_HIGHSPEED BIT(2)/* Unreliable card detection */ #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) +/* Timer for broken data transfer over scheme */ +#define DW_MCI_QUIRK_BROKEN_DTO BIT(4)struct dma_pdata ;
Heiko Stübner
maintainerHi Jaehoon,
Am Dienstag, 11. August 2015, 01:26:52 schrieb Jaehoon Chung:
Quoted 5 lines
Hi, Heiko.
Is this patch based on ulf's tree?
At my dw-mmc tree, this patch can't apply..
So if you're ok, i will rework this patch about my dw-mmc tree.
looks I'm to late to comment, so thanks for adapting the patch and including it in your pull request :-)
Thanks
Heiko
Quoted 267 lines
how about?
Best Regards,
Jaehoon ChungQuoted 261 lines · Heiko Stübner
From: Addy Ke <[email protected]>
This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time.
dw_mmccallmmc_request_donefunc to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked.We got the reply from synopsys:
There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped.
Host Starvation timeout counter is triggered with stop clock condition.This means that host should get DRTO and DTO interrupt.
But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state.
There are two possibility that data over interrupt doesn't come in reading data state on RK3X SoCs:
- get command done interrupt, but doesn't get any data-related interrupt.
- get data error interrupt, but doesn't get data over interrupt.Signed-off-by Addy Ke <[email protected]>Address review comments from Jaehoon Chung.
Signed-off-by Heiko Stuebner <[email protected]>---
Changes in v2:
- fix some typo.
- remove extra timeout value (250ms).
- removedw_mci_dto_start_monitorfunc.
- use "broken-dto" for new quirk and change Subject for it.Changes in v3:
- Remove dts for broken-dto, just add this quirk indw_mci_rockchip_initChanges in v4:
- fix bug that may cause 32 bit overflow by (drto_clks* 1000).
- doesn't callmod_timerin writing data state, becase TMOUT register onlyfor reading data.
Changes in v5:
- fix some typo.
- add a buffer fordrto_ms.
- movedrto_msrelated code to a helper function.Changes in v6:
- better spare time comment
- letdw_mci_set_drtostart the timer itselfdrivers/mmc/host/dw_mmc-rockchip.c | 3 ++ drivers/mmc/host/dw_mmc.c | 64++++++++++++++++++++++++++++++++++++--
include/linux/mmc/dw_mmc.h | 4 +++ 3 files changed, 69 insertions(+), 2 deletions(-)drivers/mmc/host/dw_mmc- renamed from drivers/mmc/host/dw_mmc-rockchip.c +0
-0
rockchip.c
index de15121..bc76aa2 100644drivers/mmc/host/dw_mmc-rockchip.c +3
-0@@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host)/* It is slot 8 on Rockchip SoCs */ host->sdio_id0 =8 ; +/* It needs this quirk on all Rockchip SoCs */ + host->pdata->quirks |=DW_MCI_QUIRK_BROKEN_DTO ; +return 0 ;}
drivers/mmc/host/dw_mmc.c +33
-2@@ -1528,6 +1528,20 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)return data->error; } +static void dw_mci_set_drto (struct dw_mci *host) +{ +unsigned int drto_clks; +unsigned int drto_ms; + + drto_clks =mci_readl (host,TMOUT ) >>8 ; + drto_ms =DIV_ROUND_UP (drto_clks, host->bus_hz /1000 ); + +/* add a bit spare time */ + drto_ms +=10 ; + +mod_timer (&host->dto_timer, jiffies +msecs_to_jiffies (drto_ms)); +} +static void dw_mci_tasklet_func (unsigned long priv) {struct dw_mci *host = (struct dw_mci *)priv;@@ -1605,8 +1619,16 @@ static void dw_mci_tasklet_func(unsigned long priv) }if (!test_and_clear_bit (EVENT_XFER_COMPLETE , - &host->pending_events)) + &host->pending_events)) { +/* +* If all data-related interrupts don't come +* within the given time in reading data state. +*/ +if ((host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) && + (host->dir_status ==DW_MCI_RECV_STATUS )) +dw_mci_set_drto (host);break ; + }set_bit (EVENT_XFER_COMPLETE , &host->completed_events);@@ -1639,8 +1661,17 @@ static void dw_mci_tasklet_func(unsigned long priv)case STATE_DATA_BUSY :if (!test_and_clear_bit (EVENT_DATA_COMPLETE , - &host->pending_events)) + &host->pending_events)) { +/* +* If data error interrupt comes but data over +* interrupt doesn't come within the given time. +* in reading data state. +*/ +if ((host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) && + (host->dir_status ==DW_MCI_RECV_STATUS )) +dw_mci_set_drto (host);break ; + }drivers/mmc/host/dw_mmc.c +3
-0host->data =NULL ;set_bit (EVENT_DATA_COMPLETE , &host->completed_events);@@ -2201,6 +2232,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) }if (pending &SDMMC_INT_DATA_OVER ) { +if (host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) +del_timer (&host->dto_timer); +drivers/mmc/host/dw_mmc.c +22
-0mci_writel (host,RINTSTS ,SDMMC_INT_DATA_OVER );if (!host->data_status) host->data_status = pending;@@ -2593,6 +2627,28 @@ static void dw_mci_cmd11_timer(unsigned long arg)tasklet_schedule (&host->tasklet); } +static void dw_mci_dto_timer (unsigned long arg) +{ +struct dw_mci *host = (struct dw_mci *)arg; + +switch (host->state) { +case STATE_SENDING_DATA : +case STATE_DATA_BUSY : +/* +* If DTO interrupt does NOT come in sending data state, +* we should notify the driver to terminate current transfer +* and report a data timeout to the core. +*/ + host->data_status =SDMMC_INT_DRTO ; +set_bit (EVENT_DATA_ERROR , &host->pending_events); +set_bit (EVENT_DATA_COMPLETE , &host->pending_events); +tasklet_schedule (&host->tasklet); +break ; +default : +break ; + } +} +#ifdef
CONFIG_OF
static structdw_mci_of_quirks{drivers/mmc/host/dw_mmc.c +4
-0char *quirk;@@ -2769,6 +2825,10 @@ int dw_mci_probe(struct dw_mci *host) host->quirks = host->pdata->quirks; +if (host->quirks &DW_MCI_QUIRK_BROKEN_DTO ) +setup_timer (&host->dto_timer, + dw_mci_dto_timer, (unsigned long )host); +spin_lock_init (&host->lock);spin_lock_init (&host->irq_lock);drivers/mmc/host/dw_mmc.c +0
-0INIT_LIST_HEAD (&host->queue);include/linux/mmc/dw_mmc.h +1
-0@@ -98,6 +98,7 @@ struct mmc_data;* @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. * @sdio_id0: Number of slot0 in the SDIO interrupt registers. +* @dto_timer: Timer for broken data transfer over scheme. *
* Locking
* =======include/linux/mmc/dw_mmc.h +1
-0@@ -204,6 +205,7 @@ struct dw_mci {int sdio_id0;struct timer_list cmd11_timer; +struct timer_list dto_timer;};
/* DMA ops for Internal/External DMAC interface */
include/linux/mmc/dw_mmc.h +2
-0@@ -226,6 +228,8 @@ struct dw_mci_dma_ops {#define DW_MCI_QUIRK_HIGHSPEED BIT(2)/* Unreliable card detection */ #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) +/* Timer for broken data transfer over scheme */ +#define DW_MCI_QUIRK_BROKEN_DTO BIT(4)struct
dma_pdata;