From: Micky Ching <micky_ch...@realsil.com.cn>

SD4.0 mode using tlp for cmd/data transfer, add tlp functions to handle
this case.

Signed-off-by: Micky Ching <micky_ch...@realsil.com.cn>
Signed-off-by: Wei Wang <wei_w...@realsil.com.cn>
---
 drivers/mmc/host/sdhci.c | 244 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 220 insertions(+), 24 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index df1b88d..3c56944 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -976,6 +976,32 @@ static void sdhci_set_transfer_mode(struct sdhci_host 
*host,
        sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
 }
 
+static void sdhci_uhsii_set_transfer_mode(struct sdhci_host *host,
+       struct mmc_command *cmd)
+{
+       u16 mode = 0;
+       struct mmc_data *data = cmd->data;
+
+       if (UHSII_CHK_CCMD(cmd->tlp_send.header)) {
+               if (cmd->flags & MMC_RSP_BUSY)
+                       mode |= SDHCI_UHSII_TRNS_WAIT_EBSY;
+       } else {
+               u8 tmode = (cmd->tlp_send.argument & UHSII_ARG_TMODE_MASK) >>
+                       UHSII_ARG_TMODE_SHIFT;
+               if (tmode & UHSII_TMODE_DM_HD)
+                       mode |= SDHCI_UHSII_TRANS_2LANE_HD;
+               mode |= SDHCI_UHSII_TRNS_BLK_CNT_EN;
+               mode |= SDHCI_UHSII_TRNS_WAIT_EBSY;
+
+               if (data->flags & MMC_DATA_WRITE)
+                       mode |= SDHCI_UHSII_TRNS_WRITE;
+               if (host->flags & SDHCI_REQ_USE_DMA)
+                       mode |= SDHCI_UHSII_TRNS_DMA;
+       }
+
+       sdhci_writew(host, mode, SDHCI_UHSII_TRANSFER_MODE);
+}
+
 static void sdhci_finish_data(struct sdhci_host *host)
 {
        struct mmc_data *data;
@@ -1014,7 +1040,7 @@ static void sdhci_finish_data(struct sdhci_host *host)
         * a) open-ended multiblock transfer (no CMD23)
         * b) error in multiblock transfer
         */
-       if (data->stop &&
+       if (!host->uhsii_if_enabled && data->stop &&
            (data->error ||
             !host->mrq->sbc)) {
 
@@ -1032,6 +1058,51 @@ static void sdhci_finish_data(struct sdhci_host *host)
                tasklet_schedule(&host->finish_tasklet);
 }
 
+static void sdhci_send_native_tlp(struct sdhci_host *host, struct mmc_tlp *tlp)
+{
+       int i;
+       unsigned long timeout;
+       u16 cmdreg;
+       u8 plen, cmd_len = 4;
+
+       /* Wait max 10 ms */
+       timeout = 10;
+
+       if (sdhci_checkl(host, SDHCI_PRESENT_STATE, SDHCI_CMD_INHIBIT, 0, 10)) {
+               pr_err("%s: cmd busy...\n", mmc_hostname(host->mmc));
+               sdhci_dumpregs(host);
+               tlp->error = -EIO;
+               tasklet_schedule(&host->finish_tasklet);
+               return;
+       }
+
+       mod_timer(&host->timer, jiffies + 10 * HZ);
+
+       host->tlp = tlp;
+
+       plen = (u8)((tlp->tlp_send->argument & UHSII_ARG_PLEN_MASK) >>
+               UHSII_ARG_PLEN_SHIFT);
+
+       sdhci_writew(host, cpu_to_be16(tlp->tlp_send->header),
+                       SDHCI_UHSII_CMD_HEADER);
+       sdhci_writew(host, cpu_to_be16(tlp->tlp_send->argument),
+                       SDHCI_UHSII_CMD_ARGUMENT);
+       if (tlp->tlp_send->argument & UHSII_ARG_DIR_WRITE) {
+               for (i = 0; i < UHSII_PLEN_DWORDS(plen); i++)
+                       sdhci_writel(host,
+                               cpu_to_be32(tlp->tlp_send->payload[i]),
+                               SDHCI_UHSII_CMD_PAYLOAD + 4 * i);
+
+               cmd_len = UHSII_PLEN_BYTES(plen) + 4;
+       }
+
+       cmdreg = (cmd_len & SDHCI_UHSII_COMMAND_LEN_MASK) <<
+               SDHCI_UHSII_COMMAND_LEN_SHIFT;
+       if (tlp->cmd_type == UHSII_COMMAND_GO_DORMANT)
+               cmdreg |= SDHCI_UHSII_GO_DORMANT;
+       sdhci_writew(host, cmdreg, SDHCI_UHSII_COMMAND);
+}
+
 void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
 {
        int flags;
@@ -1077,6 +1148,35 @@ void sdhci_send_command(struct sdhci_host *host, struct 
mmc_command *cmd)
 
        sdhci_prepare_data(host, cmd);
 
+       if (cmd->use_tlp) {
+               int i;
+               u16 cmdreg;
+               u8 plen = 1, cmd_len = 8;
+
+               if (!UHSII_CHK_CCMD(cmd->tlp_send.header)) {
+                       plen = 2;
+                       cmd_len = 12;
+               }
+
+               sdhci_writew(host, cpu_to_be16(cmd->tlp_send.header),
+                               SDHCI_UHSII_CMD_HEADER);
+               sdhci_writew(host, cpu_to_be16(cmd->tlp_send.argument),
+                               SDHCI_UHSII_CMD_ARGUMENT);
+               for (i = 0; i < plen; i++)
+                       sdhci_writel(host,
+                               cpu_to_be32(cmd->tlp_send.payload[i]),
+                               SDHCI_UHSII_CMD_PAYLOAD + (4 * i));
+
+               sdhci_uhsii_set_transfer_mode(host, cmd);
+
+               cmdreg = (cmd_len & SDHCI_UHSII_COMMAND_LEN_MASK) <<
+                       SDHCI_UHSII_COMMAND_LEN_SHIFT;
+               if (cmd->data)
+                       cmdreg |= SDHCI_UHSII_DATA_PRESENT;
+               sdhci_writew(host, cmdreg, SDHCI_UHSII_COMMAND);
+               return;
+       }
+
        sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
 
        sdhci_set_transfer_mode(host, cmd);
@@ -1112,28 +1212,64 @@ void sdhci_send_command(struct sdhci_host *host, struct 
mmc_command *cmd)
 }
 EXPORT_SYMBOL_GPL(sdhci_send_command);
 
-static void sdhci_finish_command(struct sdhci_host *host)
+static void sdhci_read_rsp_136(struct sdhci_host *host)
 {
        int i;
 
+       if (host->cmd->use_tlp) {
+               for (i = 0; i < 4; i++) {
+                       u32 resp = sdhci_raw_readl(host,
+                                       SDHCI_UHSII_RESP_PAYLOAD + i * 4);
+                       host->cmd->resp[i] = be32_to_cpu(resp);
+               }
+       } else {
+               /* CRC is stripped so we need to do some shifting. */
+               for (i = 0; i < 4; i++) {
+                       host->cmd->resp[i] = sdhci_readl(host,
+                               SDHCI_RESPONSE + (3 - i) * 4) << 8;
+                       if (i != 3)
+                               host->cmd->resp[i] |= sdhci_readb(host,
+                                       SDHCI_RESPONSE + (3 - i) * 4 - 1);
+               }
+       }
+
+}
+
+static void sdhci_read_rsp_48(struct sdhci_host *host)
+{
+       if (host->cmd->use_tlp)
+               host->cmd->resp[0] = be32_to_cpu(
+                       sdhci_raw_readl(host, SDHCI_UHSII_RESP_PAYLOAD));
+       else
+               host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
+}
+
+static void sdhci_finish_command(struct sdhci_host *host)
+{
        BUG_ON(host->cmd == NULL);
 
-       if (host->cmd->flags & MMC_RSP_PRESENT) {
-               if (host->cmd->flags & MMC_RSP_136) {
-                       /* CRC is stripped so we need to do some shifting. */
-                       for (i = 0;i < 4;i++) {
-                               host->cmd->resp[i] = sdhci_readl(host,
-                                       SDHCI_RESPONSE + (3-i)*4) << 8;
-                               if (i != 3)
-                                       host->cmd->resp[i] |=
-                                               sdhci_readb(host,
-                                               SDHCI_RESPONSE + (3-i)*4-1);
-                       }
-               } else {
-                       host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
+       if (host->cmd->use_tlp) {
+               u16 arg = sdhci_readw(host, SDHCI_UHSII_RESP_ARGUMENT);
+
+               arg = be16_to_cpu(arg);
+               if (arg & UHSII_ARG_RES_NACK) {
+                       host->cmd->error = -EIO;
+                       DBG("Response NACK!");
+
+                       tasklet_schedule(&host->finish_tasklet);
+                       host->cmd = NULL;
+
+                       return;
                }
        }
 
+       if (host->cmd->flags & MMC_RSP_PRESENT) {
+               if (host->cmd->flags & MMC_RSP_136)
+                       sdhci_read_rsp_136(host);
+               else
+                       sdhci_read_rsp_48(host);
+       }
+
        host->cmd->error = 0;
 
        /* Finished CMD23, now send actual command. */
@@ -1153,6 +1289,21 @@ static void sdhci_finish_command(struct sdhci_host *host)
        }
 }
 
+static void sdhci_finish_native_tlp(struct sdhci_host *host)
+{
+       int i;
+
+       host->tlp->tlp_back->header = be16_to_cpu(
+               sdhci_readw(host, SDHCI_UHSII_RESP_HEADER));
+       host->tlp->tlp_back->argument = be16_to_cpu(
+               sdhci_readw(host, SDHCI_UHSII_RESP_ARGUMENT));
+       for (i = 0; i < 4; i++)
+               host->tlp->tlp_back->payload[i] = be32_to_cpu(
+                       sdhci_readl(host, SDHCI_UHSII_RESP_PAYLOAD + 4 * i));
+
+       tasklet_schedule(&host->finish_tasklet);
+}
+
 static u16 sdhci_get_preset_value(struct sdhci_host *host)
 {
        u16 preset = 0;
@@ -1410,7 +1561,7 @@ static void sdhci_request(struct mmc_host *mmc, struct 
mmc_request *mrq)
        host->mrq = mrq;
 
        if (!present || host->flags & SDHCI_DEVICE_DEAD) {
-               host->mrq->cmd->error = -ENOMEDIUM;
+               mmc_set_mrq_error_code(host->mrq, -ENOMEDIUM);
                tasklet_schedule(&host->finish_tasklet);
        } else {
                u32 present_state;
@@ -1446,10 +1597,13 @@ static void sdhci_request(struct mmc_host *mmc, struct 
mmc_request *mrq)
                        }
                }
 
-               if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
+               if (!host->uhsii_if_enabled && mrq->sbc &&
+                               !(host->flags & SDHCI_AUTO_CMD23))
                        sdhci_send_command(host, mrq->sbc);
-               else
+               else if (mrq->cmd)
                        sdhci_send_command(host, mrq->cmd);
+               else if (mrq->tlp)
+                       sdhci_send_native_tlp(host, mrq->tlp);
        }
 
        mmiowb();
@@ -2243,7 +2397,7 @@ static void sdhci_card_event(struct mmc_host *mmc)
                sdhci_do_reset(host, SDHCI_RESET_CMD);
                sdhci_do_reset(host, SDHCI_RESET_DATA);
 
-               host->mrq->cmd->error = -ENOMEDIUM;
+               mmc_set_mrq_error_code(host->mrq, -ENOMEDIUM);
                tasklet_schedule(&host->finish_tasklet);
        }
 
@@ -2450,6 +2604,19 @@ static void sdhci_tasklet_finish(unsigned long param)
 
        mrq = host->mrq;
 
+       if (mrq->tlp) {
+               if (mrq->tlp->cmd_type == UHSII_COMMAND_GO_DORMANT) {
+                       /* Wait In Dormant State */
+                       if (sdhci_checkl(host, SDHCI_PRESENT_STATE,
+                                       SDHCI_IN_DORMANT_STATE,
+                                       SDHCI_IN_DORMANT_STATE, 100) < 0) {
+                               pr_err("%s: Not in dormant state.\n",
+                                       mmc_hostname(host->mmc));
+                               mrq->tlp->error = -ETIMEDOUT;
+                       }
+               }
+       }
+
        /*
         * The controller needs a reset of internal state machines
         * upon error conditions.
@@ -2457,6 +2624,7 @@ static void sdhci_tasklet_finish(unsigned long param)
        if (!(host->flags & SDHCI_DEVICE_DEAD) &&
            ((mrq->cmd && mrq->cmd->error) ||
             (mrq->sbc && mrq->sbc->error) ||
+            (mrq->tlp && mrq->tlp->error) ||
             (mrq->data && ((mrq->data->error && !mrq->data->stop) ||
                            (mrq->data->stop && mrq->data->stop->error))) ||
             (host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) {
@@ -2475,6 +2643,7 @@ static void sdhci_tasklet_finish(unsigned long param)
        host->mrq = NULL;
        host->cmd = NULL;
        host->data = NULL;
+       host->tlp = NULL;
 
 #ifndef SDHCI_USE_LEDS_CLASS
        sdhci_deactivate_led(host);
@@ -2505,10 +2674,18 @@ static void sdhci_timeout_timer(unsigned long data)
                        host->data->error = -ETIMEDOUT;
                        sdhci_finish_data(host);
                } else {
-                       if (host->cmd)
-                               host->cmd->error = -ETIMEDOUT;
-                       else
-                               host->mrq->cmd->error = -ETIMEDOUT;
+                       if (host->cmd || host->mrq->cmd) {
+                               if (host->cmd)
+                                       host->cmd->error = -ETIMEDOUT;
+                               else
+                                       host->mrq->cmd->error = -ETIMEDOUT;
+                       }
+                       if (host->tlp || host->mrq->tlp) {
+                               if (host->tlp)
+                                       host->tlp->error = -ETIMEDOUT;
+                               else
+                                       host->mrq->tlp->error = -ETIMEDOUT;
+                       }
 
                        tasklet_schedule(&host->finish_tasklet);
                }
@@ -2550,6 +2727,25 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 
intmask, u32 *mask)
                return;
        }
 
+       if (host->tlp) {
+               if (uhsii_intmask & SDHCI_UHSII_INT_TIMEOUT)
+                       host->tlp->error = -ETIMEDOUT;
+               else if (uhsii_intmask & (SDHCI_UHSII_INT_CRC |
+                               SDHCI_UHSII_INT_HEADER |
+                               SDHCI_UHSII_INT_RES |
+                               SDHCI_UHSII_INT_UNRECOVERABLE))
+                       host->tlp->error = -EILSEQ;
+
+               if (host->tlp->error) {
+                       tasklet_schedule(&host->finish_tasklet);
+                       return;
+               }
+
+               if (intmask & SDHCI_INT_RESPONSE)
+                       sdhci_finish_native_tlp(host);
+               return;
+       }
+
        if (intmask & SDHCI_INT_TIMEOUT)
                host->cmd->error = -ETIMEDOUT;
        else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
@@ -3669,7 +3865,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
                        pr_err("%s: Controller removed during "
                                " transfer!\n", mmc_hostname(mmc));
 
-                       host->mrq->cmd->error = -ENOMEDIUM;
+                       mmc_set_mrq_error_code(host->mrq, -ENOMEDIUM);
                        tasklet_schedule(&host->finish_tasklet);
                }
 
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to