[PATCH 08/16] mmc: host: omap_hsmmc: Add software timer when timeout greater than hardware capablility

2017-06-16 Thread Kishon Vijay Abraham I
From: Mugunthan V N 

DRA7 Errata No i834: When using high speed HS200 and SDR104
cards, the functional clock for MMC module will be 192MHz.
At this frequency, the maximum obtainable timeout (DTO =0xE)
in hardware is (1/192MHz)*2^27 = 700ms. Commands taking longer
than 700ms will be affected by this small window frame and
will be timing out frequently even without a genune timeout
from the card. Workarround for this errata is use a software
timer instead of hardware timer to provide the delay requested
by the upper layer

So adding a software timer as a work around for the errata.
Instead of using software timeout only for larger delays requested
when using HS200/SDR104 cards which results in hardware and
software timer race conditions, so move all the timeout request
to use software timer when HS200/SDR104 card is connected and
use hardware timer when other type cards are connected.

Also start the software timer after queueing to DMA to ensure
we are more likely to expire within correct limits. To be ever
more sure that we won't expire this soft timer too early, we're
adding a 100ns slack to the data timeout requested by the
upper layer.

[r...@ti.com: fix compiler warning in sw timeout function and
use sw timeout for busy timeout]
Signed-off-by: Ravikumar Kattekola 
Signed-off-by: Mugunthan V N 
[kis...@ti.com: Fix the timeout value to account for the entire
transfer to complete here.]
Signed-off-by: Kishon Vijay Abraham I 
Signed-off-by: Sekhar Nori 
---
 drivers/mmc/host/omap_hsmmc.c | 126 --
 1 file changed, 109 insertions(+), 17 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index f1d5e8385591..3676ffa72929 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -185,6 +185,11 @@
 #define PSTATE_DLEV(0xF << 20)
 #define PSTATE_DLEV_DAT0   (0x1 << 20)
 
+#define MMC_BLOCK_TRANSFER_TIME_NS(blksz, bus_width, freq) \
+  ((unsigned long long)\
+  (2 * (((blksz) * NSEC_PER_SEC *  \
+  (8 / (bus_width))) / (freq
+
 /*
  * One controller can have multiple slots, like on some omap boards using
  * omap.c controller driver. Luckily this is not currently done on any known
@@ -250,6 +255,8 @@ struct omap_hsmmc_host {
struct  omap_hsmmc_platform_data*pdata;
 
boolis_tuning;
+   struct timer_list   timer;
+   unsigned long long  data_timeout;
 
/* return MMC cover switch state, can be NULL if not supported.
 *
@@ -631,8 +638,8 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host 
*host,
if (host->use_dma)
irq_mask &= ~(BRR_EN | BWR_EN);
 
-   /* Disable timeout for erases */
-   if (cmd->opcode == MMC_ERASE)
+   /* Disable timeout for erases or when using software timeout */
+   if (cmd && (cmd->opcode == MMC_ERASE || host->data_timeout))
irq_mask &= ~DTO_EN;
 
if (host->flags & CLKEXTFREE_ENABLED)
@@ -1239,8 +1246,16 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host 
*host, int status)
}
 
OMAP_HSMMC_WRITE(host->base, STAT, status);
-   if (end_cmd || ((status & CC_EN) && host->cmd))
+   if (end_cmd || ((status & CC_EN) && host->cmd)) {
omap_hsmmc_cmd_done(host, host->cmd);
+   if (host->data_timeout) {
+   unsigned long timeout;
+
+   timeout = jiffies +
+ nsecs_to_jiffies(host->data_timeout);
+   mod_timer(>timer, timeout);
+   }
+   }
if ((end_trans || (status & TC_EN)) && host->mrq)
omap_hsmmc_xfer_done(host, data);
 }
@@ -1254,7 +1269,19 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
int status;
 
status = OMAP_HSMMC_READ(host->base, STAT);
+
while (status & (INT_EN_MASK | CIRQ_EN)) {
+   /*
+* During a successful bulk data transfer command-completion
+* interrupt and transfer-completion interrupt will be
+* generated, but software-timeout timer should be deleted
+* only on non-cc interrupts (transfer complete or error)
+*/
+   if (host->data_timeout && (status & (~CC_EN))) {
+   del_timer(>timer);
+   host->data_timeout = 0;
+   }
+
if (host->req_in_progress)
omap_hsmmc_do_irq(host, status);
 
@@ -1268,6 +1295,25 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
return IRQ_HANDLED;
 }
 
+static void omap_hsmmc_soft_timeout(unsigned long data)
+{
+   struct omap_hsmmc_host *host = 

[PATCH 08/16] mmc: host: omap_hsmmc: Add software timer when timeout greater than hardware capablility

2017-06-16 Thread Kishon Vijay Abraham I
From: Mugunthan V N 

DRA7 Errata No i834: When using high speed HS200 and SDR104
cards, the functional clock for MMC module will be 192MHz.
At this frequency, the maximum obtainable timeout (DTO =0xE)
in hardware is (1/192MHz)*2^27 = 700ms. Commands taking longer
than 700ms will be affected by this small window frame and
will be timing out frequently even without a genune timeout
from the card. Workarround for this errata is use a software
timer instead of hardware timer to provide the delay requested
by the upper layer

So adding a software timer as a work around for the errata.
Instead of using software timeout only for larger delays requested
when using HS200/SDR104 cards which results in hardware and
software timer race conditions, so move all the timeout request
to use software timer when HS200/SDR104 card is connected and
use hardware timer when other type cards are connected.

Also start the software timer after queueing to DMA to ensure
we are more likely to expire within correct limits. To be ever
more sure that we won't expire this soft timer too early, we're
adding a 100ns slack to the data timeout requested by the
upper layer.

[r...@ti.com: fix compiler warning in sw timeout function and
use sw timeout for busy timeout]
Signed-off-by: Ravikumar Kattekola 
Signed-off-by: Mugunthan V N 
[kis...@ti.com: Fix the timeout value to account for the entire
transfer to complete here.]
Signed-off-by: Kishon Vijay Abraham I 
Signed-off-by: Sekhar Nori 
---
 drivers/mmc/host/omap_hsmmc.c | 126 --
 1 file changed, 109 insertions(+), 17 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index f1d5e8385591..3676ffa72929 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -185,6 +185,11 @@
 #define PSTATE_DLEV(0xF << 20)
 #define PSTATE_DLEV_DAT0   (0x1 << 20)
 
+#define MMC_BLOCK_TRANSFER_TIME_NS(blksz, bus_width, freq) \
+  ((unsigned long long)\
+  (2 * (((blksz) * NSEC_PER_SEC *  \
+  (8 / (bus_width))) / (freq
+
 /*
  * One controller can have multiple slots, like on some omap boards using
  * omap.c controller driver. Luckily this is not currently done on any known
@@ -250,6 +255,8 @@ struct omap_hsmmc_host {
struct  omap_hsmmc_platform_data*pdata;
 
boolis_tuning;
+   struct timer_list   timer;
+   unsigned long long  data_timeout;
 
/* return MMC cover switch state, can be NULL if not supported.
 *
@@ -631,8 +638,8 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host 
*host,
if (host->use_dma)
irq_mask &= ~(BRR_EN | BWR_EN);
 
-   /* Disable timeout for erases */
-   if (cmd->opcode == MMC_ERASE)
+   /* Disable timeout for erases or when using software timeout */
+   if (cmd && (cmd->opcode == MMC_ERASE || host->data_timeout))
irq_mask &= ~DTO_EN;
 
if (host->flags & CLKEXTFREE_ENABLED)
@@ -1239,8 +1246,16 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host 
*host, int status)
}
 
OMAP_HSMMC_WRITE(host->base, STAT, status);
-   if (end_cmd || ((status & CC_EN) && host->cmd))
+   if (end_cmd || ((status & CC_EN) && host->cmd)) {
omap_hsmmc_cmd_done(host, host->cmd);
+   if (host->data_timeout) {
+   unsigned long timeout;
+
+   timeout = jiffies +
+ nsecs_to_jiffies(host->data_timeout);
+   mod_timer(>timer, timeout);
+   }
+   }
if ((end_trans || (status & TC_EN)) && host->mrq)
omap_hsmmc_xfer_done(host, data);
 }
@@ -1254,7 +1269,19 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
int status;
 
status = OMAP_HSMMC_READ(host->base, STAT);
+
while (status & (INT_EN_MASK | CIRQ_EN)) {
+   /*
+* During a successful bulk data transfer command-completion
+* interrupt and transfer-completion interrupt will be
+* generated, but software-timeout timer should be deleted
+* only on non-cc interrupts (transfer complete or error)
+*/
+   if (host->data_timeout && (status & (~CC_EN))) {
+   del_timer(>timer);
+   host->data_timeout = 0;
+   }
+
if (host->req_in_progress)
omap_hsmmc_do_irq(host, status);
 
@@ -1268,6 +1295,25 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
return IRQ_HANDLED;
 }
 
+static void omap_hsmmc_soft_timeout(unsigned long data)
+{
+   struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)data;
+   bool end_trans = false;
+
+