diff options
author | Vladimir Barinov <vladimir.barinov@cogentembedded.com> | 2017-05-16 23:37:25 +0300 |
---|---|---|
committer | Vladimir Barinov <vladimir.barinov@cogentembedded.com> | 2017-05-17 10:43:29 +0300 |
commit | 6de4102e912132ff6fbe2d88393517c337473d29 (patch) | |
tree | 0c0c7ff0ebaa918f0259b6243aab0b1d63ec79c3 /meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0007-mmc-sh_mobile_sdhi-Add-R-CarGen3-SDHI-SEQUENCER-supp.patch |
Initial commit for ADAS boards support in 2.19.0
Diffstat (limited to 'meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0007-mmc-sh_mobile_sdhi-Add-R-CarGen3-SDHI-SEQUENCER-supp.patch')
-rw-r--r-- | meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0007-mmc-sh_mobile_sdhi-Add-R-CarGen3-SDHI-SEQUENCER-supp.patch | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0007-mmc-sh_mobile_sdhi-Add-R-CarGen3-SDHI-SEQUENCER-supp.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0007-mmc-sh_mobile_sdhi-Add-R-CarGen3-SDHI-SEQUENCER-supp.patch new file mode 100644 index 0000000..803c2ed --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0007-mmc-sh_mobile_sdhi-Add-R-CarGen3-SDHI-SEQUENCER-supp.patch @@ -0,0 +1,1070 @@ +From 3b81bfa8f0dcf10e59172b10efce2c591b35a590 Mon Sep 17 00:00:00 2001 +From: Masaharu Hayakawa <masaharu.hayakawa.ry@renesas.com> +Date: Wed, 1 Feb 2017 10:59:39 +0900 +Subject: [PATCH] mmc: sh_mobile_sdhi: Add R-CarGen3 SDHI-SEQUENCER support + +This is Workaround patch for SDHI-DMAC restriction of R-Car H3(WS1.1)/M3(WS1.0). +Restriction: Mismatch of the transfer completion interrupt time and data transfer +completion time. +Overview: It does not take into account the bus response, the transfer completion +interrupt IP outputs is in the early out.Therefore, when carrying out the data +verification data read from the SD card, there is a possibility that the data of +the last sector might be missing.(MMC Interface is also the same.) + +S/W Workaround: The last sector data is preserved by reading data for 2 sectors +extra in the SDHI Driver of Linux kernel. + +The SDHI Driver achieves a dummy read for 2 sectors by the SDHI-SEQ function. +In case of eMMC: CMD17(MMC_READ_SINGLE_BLOCK) and CMD18(MMC_READ_MULTIPLE_BLOCK) + were requested, CMD8(SEND_EXT_CSD) is carried out additionally twice. +In case of SD card: CMD17(MMC_READ_SINGLE_BLOCK) and CMD18(MMC_READ_MULTIPLE_BLOCK) + were requested, 1024 bytes are read additionally by CMD18. +In other cases: CMD17 and CMD53(SD_IO_RW_EXTENDED) is carried out additionally twice. + +Signed-off-by: Kouei Abe <kouei.abe.cp@renesas.com> +Signed-off-by: Masaharu Hayakawa <masaharu.hayakawa.ry@renesas.com> +--- + drivers/mmc/host/Kconfig | 10 + + drivers/mmc/host/sh_mobile_sdhi.c | 13 + + drivers/mmc/host/tmio_mmc.h | 56 ++++ + drivers/mmc/host/tmio_mmc_dma_gen3.c | 490 +++++++++++++++++++++++++++++++++++ + drivers/mmc/host/tmio_mmc_pio.c | 245 ++++++++++++++++++ + 5 files changed, 814 insertions(+) + +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 7ec33c5..47b29b6 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -568,6 +568,16 @@ config MMC_SDHI_PIO + When switching the transfer mode from DMA to PIO, say Y here. + When switching the transfer mode from PIO to DMA, say N here. + ++config MMC_SDHI_PRE_REQ ++ bool "SDHI pre_req/post_req API support" ++ depends on MMC_SDHI && ARM64 && !MMC_SDHI_PIO ++ default y ++ ++config MMC_SDHI_SEQ ++ bool "SDHI Sequencer read/write support" ++ depends on MMC_SDHI && ARM64 && !MMC_SDHI_PIO ++ default y ++ + config MMC_CB710 + tristate "ENE CB710 MMC/SD Interface support" + depends on PCI +diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c +index ee7b188..35cd37f 100644 +--- a/drivers/mmc/host/sh_mobile_sdhi.c ++++ b/drivers/mmc/host/sh_mobile_sdhi.c +@@ -114,9 +114,22 @@ struct sh_mobile_sdhi_of_data { + .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | + MMC_CAP_CMD23, + .bus_shift = 2, ++#ifdef CONFIG_MMC_SDHI_SEQ ++ /* Gen3 SDHI SEQ can handle 0xffffffff/DM_SEQ_SIZE blk count */ ++ .max_blk_count = 0xffffffff / 512, ++ /* Gen3 SDHI SEQ can handle max 8 commands */ ++#ifdef CONFIG_MMC_BLOCK_BOUNCE ++ /* (CMD23+CMD18)*1 + (dummy read command) */ ++ .max_segs = 1, ++#else ++ /* (CMD23+CMD18)*3 + (dummy read command) */ ++ .max_segs = 3, ++#endif ++#else //CONFIG_MMC_SDHI_SEQ + /* Gen3 SDHI DMAC can handle 0xffffffff blk count, but seg = 1 */ + .max_blk_count = 0xffffffff, + .max_segs = 1, ++#endif //CONFIG_MMC_SDHI_SEQ + .sdbuf_64bit = true, + .scc_offset = 0x1000, + .taps = rcar_gen3_scc_taps, +diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h +index 438e4f8..52191ac 100644 +--- a/drivers/mmc/host/tmio_mmc.h ++++ b/drivers/mmc/host/tmio_mmc.h +@@ -51,6 +51,29 @@ + #define CTL_CLK_AND_WAIT_CTL 0x138 + #define CTL_RESET_SDIO 0x1e0 + ++#ifdef CONFIG_MMC_SDHI_SEQ ++#define DM_CM_SEQ_REGSET 0x800 ++#define DM_CM_SEQ_MODE 0x808 ++#define DM_CM_SEQ_CTRL 0x810 ++#define DM_CM_DTRAN_MODE 0x820 ++#define DM_CM_DTRAN_CTRL 0x828 ++#define DM_CM_RST 0x830 ++#define DM_CM_INFO1 0x840 ++#define DM_CM_INFO1_MASK 0x848 ++#define DM_CM_INFO2 0x850 ++#define DM_CM_INFO2_MASK 0x858 ++#define DM_CM_TUNING_STAT 0x860 ++#define DM_CM_SEQ_STAT 0x868 ++#define DM_DTRAN_ADDR 0x880 ++#define DM_SEQ_CMD 0x8a0 ++#define DM_SEQ_ARG 0x8a8 ++#define DM_SEQ_SIZE 0x8b0 ++#define DM_SEQ_SECCNT 0x8b8 ++#define DM_SEQ_RSP 0x8c0 ++#define DM_SEQ_RSP_CHK 0x8c8 ++#define DM_SEQ_ADDR 0x8d0 ++#endif ++ + /* Definitions for values the CTRL_STATUS register can take. */ + #define TMIO_STAT_CMDRESPEND BIT(0) + #define TMIO_STAT_DATAEND BIT(2) +@@ -78,6 +101,14 @@ + #define TMIO_STAT_CMD_BUSY BIT(30) + #define TMIO_STAT_ILL_ACCESS BIT(31) + ++#ifdef CONFIG_MMC_SDHI_SEQ ++/* Definitions for values the DM_CM_INFO1 register can take. */ ++#define DM_CM_INFO_SEQEND 0x00000001 ++#define DM_CM_INFO_SEQSUSPEND 0x00000100 ++#define DM_CM_INFO_DTRAEND_CH0 0x00010000 ++#define DM_CM_INFO_DTRAEND_CH1 0x00020000 ++#endif ++ + #define CLK_CTL_DIV_MASK 0xff + #define CLK_CTL_SCLKEN BIT(8) + +@@ -110,6 +141,13 @@ + struct tmio_mmc_data; + struct tmio_mmc_host; + ++#ifdef CONFIG_MMC_SDHI_PRE_REQ ++enum tmio_cookie { ++ COOKIE_UNMAPPED, ++ COOKIE_PRE_MAPPED, ++}; ++#endif ++ + struct tmio_mmc_dma { + enum dma_slave_buswidth dma_buswidth; + bool sdbuf_64bit; +@@ -145,6 +183,9 @@ struct tmio_mmc_host { + struct dma_chan *chan_tx; + struct tasklet_struct dma_complete; + struct tasklet_struct dma_issue; ++#ifdef CONFIG_MMC_SDHI_SEQ ++ struct tasklet_struct seq_complete; ++#endif + struct scatterlist bounce_sg; + u8 *bounce_buf; + u32 dma_tranend1; +@@ -243,6 +284,9 @@ static inline void tmio_mmc_kunmap_atomic(struct scatterlist *sg, + void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata); + void tmio_mmc_release_dma(struct tmio_mmc_host *host); + void tmio_mmc_abort_dma(struct tmio_mmc_host *host); ++#ifdef CONFIG_MMC_SDHI_SEQ ++void tmio_mmc_start_sequencer(struct tmio_mmc_host *host); ++#endif + #else + static inline void tmio_mmc_start_dma(struct tmio_mmc_host *host, + struct mmc_data *data) +@@ -327,4 +371,16 @@ static inline void sd_ctrl_write32_as_16_and_16(struct tmio_mmc_host *host, int + writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); + } + ++#ifdef CONFIG_MMC_SDHI_SEQ ++static inline u64 tmio_dm_read(struct tmio_mmc_host *host, int addr) ++{ ++ return readq(host->ctl + addr); ++} ++ ++static inline void tmio_dm_write(struct tmio_mmc_host *host, int addr, u64 val) ++{ ++ writeq(val, host->ctl + addr); ++} ++#endif ++ + #endif +diff --git a/drivers/mmc/host/tmio_mmc_dma_gen3.c b/drivers/mmc/host/tmio_mmc_dma_gen3.c +index d1c6c40..faee4ae 100644 +--- a/drivers/mmc/host/tmio_mmc_dma_gen3.c ++++ b/drivers/mmc/host/tmio_mmc_dma_gen3.c +@@ -19,9 +19,15 @@ + #include <linux/pagemap.h> + #include <linux/scatterlist.h> + #include <linux/sys_soc.h> ++#ifdef CONFIG_MMC_SDHI_SEQ ++#include <linux/mmc/mmc.h> ++#include <linux/mmc/sd.h> ++#include <linux/mmc/sdio.h> ++#endif + + #include "tmio_mmc.h" + ++#if !defined(CONFIG_MMC_SDHI_SEQ) + #define DM_CM_DTRAN_MODE 0x820 + #define DM_CM_DTRAN_CTRL 0x828 + #define DM_CM_RST 0x830 +@@ -30,6 +36,7 @@ + #define DM_CM_INFO2 0x850 + #define DM_CM_INFO2_MASK 0x858 + #define DM_DTRAN_ADDR 0x880 ++#endif + + /* DM_CM_DTRAN_MODE */ + #define DTRAN_MODE_CH_NUM_CH0 0 /* "downstream" = for write commands */ +@@ -43,6 +50,9 @@ + /* DM_CM_RST */ + #define RST_DTRANRST1 BIT(9) + #define RST_DTRANRST0 BIT(8) ++#ifdef CONFIG_MMC_SDHI_SEQ ++#define RST_SEQRST BIT(0) ++#endif + #define RST_RESERVED_BITS GENMASK_ULL(32, 0) + + /* DM_CM_INFO1 and DM_CM_INFO1_MASK */ +@@ -68,15 +78,17 @@ + * this driver cannot use original sd_ctrl_{write,read}32 functions. + */ + ++#if !defined(CONFIG_MMC_SDHI_SEQ) + static void tmio_dm_write(struct tmio_mmc_host *host, int addr, u64 val) + { + writeq(val, host->ctl + addr); + } + + static u32 tmio_dm_read(struct tmio_mmc_host *host, int addr) + { + return readl(host->ctl + addr); + } ++#endif + + void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable) + { +@@ -95,7 +107,11 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable) + + void tmio_mmc_abort_dma(struct tmio_mmc_host *host) + { ++#ifdef CONFIG_MMC_SDHI_SEQ ++ u64 val = RST_SEQRST | RST_DTRANRST1 | RST_DTRANRST0; ++#else + u64 val = RST_DTRANRST1 | RST_DTRANRST0; ++#endif + + dev_dbg(&host->pdev->dev, "%s\n", __func__); + +@@ -149,11 +165,17 @@ void tmio_mmc_start_dma(struct tmio_mmc_host *host, struct mmc_data *data) + irq_mask = TMIO_STAT_TXRQ; + } + ++#ifdef CONFIG_MMC_SDHI_PRE_REQ ++ if (host->data->host_cookie != COOKIE_PRE_MAPPED) { ++#endif + ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir); + if (ret < 0) { + dev_err(&host->pdev->dev, "%s: dma_map_sg failed\n", __func__); + return; + } ++#ifdef CONFIG_MMC_SDHI_PRE_REQ ++ } ++#endif + + tmio_clear_transtate(host); + tmio_mmc_enable_dma(host, true); +@@ -195,11 +217,450 @@ static void tmio_mmc_complete_tasklet_fn(unsigned long arg) + dir = DMA_TO_DEVICE; + + tmio_mmc_enable_dma(host, false); ++#ifdef CONFIG_MMC_SDHI_PRE_REQ ++ if (host->data->host_cookie != COOKIE_PRE_MAPPED) ++#endif + dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir); + tmio_mmc_do_data_irq(host); + } + #endif + ++#ifdef CONFIG_MMC_SDHI_SEQ ++/* DM_CM_SEQ_REGSET bits */ ++#define DM_CM_SEQ_REGSET_TABLE_NUM BIT(8) ++/* DM_CM_SEQ_CTRL bits */ ++#define DM_CM_SEQ_CTRL_SEQ_TABLE BIT(28) ++#define DM_CM_SEQ_CTRL_T_NUM BIT(24) ++#define DM_CM_SEQ_CTRL_SEQ_TYPE_SD BIT(16) ++#define DM_CM_SEQ_CTRL_START_NUM(x) ((x) << 12) ++#define DM_CM_SEQ_CTRL_END_NUM(x) ((x) << 8) ++#define DM_CM_SEQ_CTRL_SEQ_START BIT(0) ++/* DM_SEQ_CMD bits */ ++#define DM_SEQ_CMD_MULTI BIT(13) ++#define DM_SEQ_CMD_DIO BIT(12) ++#define DM_SEQ_CMD_CMDTYP BIT(11) ++#define DM_SEQ_CMD_RSP_NONE (BIT(9) | BIT(8)) ++#define DM_SEQ_CMD_RSP_R1 BIT(10) ++#define DM_SEQ_CMD_RSP_R1B (BIT(10) | BIT(8)) ++#define DM_SEQ_CMD_RSP_R2 (BIT(10) | BIT(9)) ++#define DM_SEQ_CMD_RSP_R3 (BIT(10) | BIT(9) | BIT(8)) ++#define DM_SEQ_CMD_NONAUTOSTP BIT(7) ++#define DM_SEQ_CMD_APP BIT(6) ++ ++#define MAX_CONTEXT_NUM 8 ++ ++struct tmio_mmc_context { ++ u64 seq_cmd; ++ u64 seq_arg; ++ u64 seq_size; ++ u64 seq_seccnt; ++ u64 seq_rsp; ++ u64 seq_rsp_chk; ++ u64 seq_addr; ++}; ++ ++static void tmio_mmc_set_seq_context(struct tmio_mmc_host *host, int ctxt_num, ++ struct tmio_mmc_context *ctxt) ++{ ++ u64 val; ++ ++ WARN_ON(ctxt_num >= MAX_CONTEXT_NUM); ++ ++ /* set sequencer table/context number */ ++ if (ctxt_num < 4) ++ val = ctxt_num; ++ else ++ val = DM_CM_SEQ_REGSET_TABLE_NUM | (ctxt_num - 4); ++ tmio_dm_write(host, DM_CM_SEQ_REGSET, val); ++ ++ /* set command parameter */ ++ tmio_dm_write(host, DM_SEQ_CMD, ctxt->seq_cmd); ++ tmio_dm_write(host, DM_SEQ_ARG, ctxt->seq_arg); ++ tmio_dm_write(host, DM_SEQ_SIZE, ctxt->seq_size); ++ tmio_dm_write(host, DM_SEQ_SECCNT, ctxt->seq_seccnt); ++ tmio_dm_write(host, DM_SEQ_RSP, ctxt->seq_rsp); ++ tmio_dm_write(host, DM_SEQ_RSP_CHK, ctxt->seq_rsp_chk); ++ tmio_dm_write(host, DM_SEQ_ADDR, ctxt->seq_addr); ++} ++ ++static int tmio_mmc_set_seq_table(struct tmio_mmc_host *host, ++ struct mmc_request *mrq, ++ struct scatterlist *sg, ++ bool ipmmu_on) ++{ ++ struct mmc_card *card = host->mmc->card; ++ struct mmc_data *data = mrq->data; ++ struct scatterlist *sg_tmp; ++ struct tmio_mmc_context ctxt; ++ unsigned int blksz, blocks; ++ u32 cmd_opcode, cmd_flag, cmd_arg; ++ u32 sbc_opcode = 0, sbc_arg = 0; ++ int i, ctxt_cnt = 0; ++ ++ /* FIXME: SD_COMBO media not tested */ ++ cmd_opcode = (mrq->cmd->opcode & 0x3f); ++ cmd_flag = DM_SEQ_CMD_CMDTYP; ++ if (data->flags & MMC_DATA_READ) ++ cmd_flag |= DM_SEQ_CMD_DIO; ++ if (mmc_op_multi(mrq->cmd->opcode) || ++ (cmd_opcode == SD_IO_RW_EXTENDED && mrq->cmd->arg & 0x08000000)) //FIXME ++ cmd_flag |= DM_SEQ_CMD_MULTI; ++ if (mrq->sbc || ++ cmd_opcode == SD_IO_RW_EXTENDED) //FIXME ++ cmd_flag |= DM_SEQ_CMD_NONAUTOSTP; ++ ++ switch (mmc_resp_type(mrq->cmd)) { ++ case MMC_RSP_NONE: ++ cmd_flag |= DM_SEQ_CMD_RSP_NONE; ++ break; ++ case MMC_RSP_R1: ++ case MMC_RSP_R1 & ~MMC_RSP_CRC: ++ cmd_flag |= DM_SEQ_CMD_RSP_R1; ++ break; ++ case MMC_RSP_R1B: ++ cmd_flag |= DM_SEQ_CMD_RSP_R1B; ++ break; ++ case MMC_RSP_R2: ++ cmd_flag |= DM_SEQ_CMD_RSP_R2; ++ break; ++ case MMC_RSP_R3: ++ cmd_flag |= DM_SEQ_CMD_RSP_R3; ++ break; ++ default: ++ pr_debug("Unknown response type %d\n", mmc_resp_type(mrq->cmd)); ++ return -EINVAL; ++ } ++ ++ cmd_arg = mrq->cmd->arg; ++ if (cmd_opcode == SD_IO_RW_EXTENDED && cmd_arg & 0x08000000) { ++ /* SDIO CMD53 block mode */ ++ cmd_arg &= ~0x1ff; ++ } ++ ++ if (mrq->sbc) { ++ sbc_opcode = (mrq->sbc->opcode & 0x3f) | DM_SEQ_CMD_RSP_R1; ++ sbc_arg = mrq->sbc->arg & (MMC_CMD23_ARG_REL_WR | ++ MMC_CMD23_ARG_PACKED | MMC_CMD23_ARG_TAG_REQ); ++ } ++ ++ blksz = data->blksz; ++ if (ipmmu_on) { ++ blocks = data->blocks; ++ memset(&ctxt, 0, sizeof(ctxt)); ++ ++ if (sbc_opcode) { ++ /* set CMD23 */ ++ ctxt.seq_cmd = sbc_opcode; ++ ctxt.seq_arg = sbc_arg | blocks; ++ tmio_mmc_set_seq_context(host, ctxt_cnt, &ctxt); ++ ctxt_cnt++; ++ } ++ ++ /* set CMD */ ++ ctxt.seq_cmd = cmd_opcode | cmd_flag; ++ ctxt.seq_arg = cmd_arg; ++ if (cmd_opcode == SD_IO_RW_EXTENDED && cmd_arg & 0x08000000) { ++ /* SDIO CMD53 block mode */ ++ ctxt.seq_arg |= blocks; ++ } ++ ctxt.seq_size = blksz; ++ ctxt.seq_seccnt = blocks; ++ ctxt.seq_addr = sg_dma_address(sg); ++ tmio_mmc_set_seq_context(host, ctxt_cnt, &ctxt); ++ } else { ++ for_each_sg(sg, sg_tmp, host->sg_len, i) { ++ blocks = sg_tmp->length / blksz; ++ memset(&ctxt, 0, sizeof(ctxt)); ++ ++ if (sbc_opcode) { ++ /* set CMD23 */ ++ ctxt.seq_cmd = sbc_opcode; ++ ctxt.seq_arg = sbc_arg | blocks; ++ if (sbc_arg & MMC_CMD23_ARG_TAG_REQ && card && ++ card->ext_csd.data_tag_unit_size && ++ blksz * blocks < card->ext_csd.data_tag_unit_size) ++ ctxt.seq_arg &= ~MMC_CMD23_ARG_TAG_REQ; ++ tmio_mmc_set_seq_context(host, ctxt_cnt, &ctxt); ++ ctxt_cnt++; ++ } ++ ++ /* set CMD */ ++ ctxt.seq_cmd = cmd_opcode | cmd_flag; ++ ctxt.seq_arg = cmd_arg; ++ if (cmd_opcode == SD_IO_RW_EXTENDED && ++ cmd_arg & 0x08000000) { ++ /* SDIO CMD53 block mode */ ++ ctxt.seq_arg |= blocks; ++ } ++ ctxt.seq_size = blksz; ++ ctxt.seq_seccnt = blocks; ++ ctxt.seq_addr = sg_dma_address(sg_tmp); ++ tmio_mmc_set_seq_context(host, ctxt_cnt, &ctxt); ++ ++ if (i < (host->sg_len - 1)) { ++ /* increment address */ ++ if (cmd_opcode == SD_IO_RW_EXTENDED) { ++ /* ++ * sg_len should be 1 in SDIO CMD53 ++ * byte mode ++ */ ++ WARN_ON(!(cmd_arg & 0x08000000)); ++ if (cmd_arg & 0x04000000) { ++ /* ++ * SDIO CMD53 address ++ * increment mode ++ */ ++ cmd_arg += (blocks * blksz) << 9; ++ } ++ } else { ++ if (card && !mmc_card_blockaddr(card)) ++ cmd_arg += blocks * blksz; ++ else ++ cmd_arg += blocks; ++ } ++ ctxt_cnt++; ++ } ++ } ++ } ++ ++ if (data->flags & MMC_DATA_READ) { ++ /* dummy read */ ++ if (cmd_opcode == MMC_READ_MULTIPLE_BLOCK && sbc_opcode && ++ data->blocks > 1) { ++ memset(&ctxt, 0, sizeof(ctxt)); ++ /* set CMD23 */ ++ ctxt.seq_cmd = sbc_opcode; ++ ctxt.seq_arg = sbc_arg | 2; ++ if (sbc_arg & MMC_CMD23_ARG_TAG_REQ && ++ card->ext_csd.data_tag_unit_size && ++ blksz * 2 < card->ext_csd.data_tag_unit_size) ++ ctxt.seq_arg &= ~MMC_CMD23_ARG_TAG_REQ; ++ ctxt_cnt++; ++ tmio_mmc_set_seq_context(host, ctxt_cnt, &ctxt); ++ ++ /* set CMD18 */ ++ ctxt.seq_cmd = cmd_opcode | cmd_flag; ++ ctxt.seq_arg = mrq->cmd->arg + data->blocks - 2; ++ ctxt.seq_size = 512; ++ ctxt.seq_seccnt = 2; ++ ctxt.seq_addr = sg_dma_address(&host->bounce_sg); ++ ctxt_cnt++; ++ tmio_mmc_set_seq_context(host, ctxt_cnt, &ctxt); ++ } else { ++ if ((card && mmc_card_mmc(card)) || ++ cmd_opcode == MMC_SEND_TUNING_BLOCK_HS200) { ++ /* In case of eMMC, set CMD8 twice */ ++ memset(&ctxt, 0, sizeof(ctxt)); ++ ctxt.seq_cmd = MMC_SEND_EXT_CSD | ++ DM_SEQ_CMD_CMDTYP | ++ DM_SEQ_CMD_DIO | ++ DM_SEQ_CMD_RSP_R1; ++ ctxt.seq_arg = 0; ++ ctxt.seq_size = 512; ++ ctxt.seq_seccnt = 1; ++ ctxt.seq_addr = sg_dma_address(&host->bounce_sg); ++ } else if (cmd_opcode == SD_SWITCH) { ++ /* set SD CMD6 twice */ ++ ctxt.seq_addr = sg_dma_address(&host->bounce_sg); ++ } else if ((card && (mmc_card_sdio(card) || ++ card->type == MMC_TYPE_SD_COMBO)) || ++ cmd_opcode == SD_IO_RW_EXTENDED) { ++ /* FIXME: ++ * In case of SDIO/SD_COMBO, ++ * read Common I/O Area 0x0-0x1FF twice. ++ */ ++ memset(&ctxt, 0, sizeof(ctxt)); ++ ctxt.seq_cmd = SD_IO_RW_EXTENDED | ++ DM_SEQ_CMD_CMDTYP | ++ DM_SEQ_CMD_DIO | ++ DM_SEQ_CMD_NONAUTOSTP | ++ DM_SEQ_CMD_RSP_R1; ++ /* ++ * SD_IO_RW_EXTENDED argument format: ++ * [31] R/W flag -> 0 ++ * [30:28] Function number -> 0x0 selects ++ * Common I/O Area ++ * [27] Block mode -> 0 ++ * [26] Increment address -> 1 ++ * [25:9] Regiser address -> 0x0 ++ * [8:0] Byte/block count -> 0x0 -> 512Bytes ++ */ ++ ctxt.seq_arg = 0x04000000; ++ ctxt.seq_size = 512; ++ ctxt.seq_seccnt = 1; ++ ctxt.seq_addr = sg_dma_address(&host->bounce_sg); ++ } else { ++ /* set CMD17 twice */ ++ memset(&ctxt, 0, sizeof(ctxt)); ++ ctxt.seq_cmd = MMC_READ_SINGLE_BLOCK | ++ DM_SEQ_CMD_CMDTYP | ++ DM_SEQ_CMD_DIO | ++ DM_SEQ_CMD_RSP_R1; ++ if (cmd_opcode == MMC_READ_SINGLE_BLOCK || ++ cmd_opcode == MMC_READ_MULTIPLE_BLOCK) ++ ctxt.seq_arg = mrq->cmd->arg; ++ else ++ ctxt.seq_arg = 0; //FIXME ++ ctxt.seq_size = 512; ++ ctxt.seq_seccnt = 1; ++ ctxt.seq_addr = sg_dma_address(&host->bounce_sg); ++ } ++ ++ for (i = 0; i < 2; i++) { ++ ctxt_cnt++; ++ tmio_mmc_set_seq_context(host, ctxt_cnt, &ctxt); ++ } ++ } ++ } ++ ++ return ctxt_cnt; ++} ++ ++void tmio_mmc_start_sequencer(struct tmio_mmc_host *host) ++{ ++ struct mmc_card *card = host->mmc->card; ++ struct scatterlist *sg = host->sg_ptr, *sg_tmp; ++ struct mmc_host *mmc = host->mmc; ++ struct mmc_request *mrq = host->mrq; ++ struct mmc_data *data = mrq->data; ++ enum dma_data_direction dir; ++ int ret, i, ctxt_num; ++ u32 val; ++ bool ipmmu_on = false; ++ ++ /* This DMAC cannot handle if sg_len larger than max_segs */ ++ if (mmc->max_segs == 1 || mmc->max_segs == 3) ++ WARN_ON(host->sg_len > mmc->max_segs); ++ else ++ ipmmu_on = true; ++ ++ dev_dbg(&host->pdev->dev, "%s: %d, %x\n", __func__, host->sg_len, ++ data->flags); ++ ++ if (!card && host->mrq->cmd->opcode == MMC_SEND_TUNING_BLOCK) { ++ /* ++ * workaround: if card is NULL, ++ * we can not decide a dummy read command to be added ++ * to the CMD19. ++ */ ++ host->force_pio = true; ++ tmio_mmc_enable_dma(host, false); ++ return; /* return for PIO */ ++ } ++ ++ if (ipmmu_on) { ++ if (!IS_ALIGNED(sg->offset, 8) || ++ ((sg_dma_address(sg) + data->blksz * data->blocks) > ++ GENMASK_ULL(32, 0))) { ++ dev_dbg(&host->pdev->dev, "%s: force pio\n", __func__); ++ host->force_pio = true; ++ tmio_mmc_enable_dma(host, false); ++ return; ++ } ++#if 1 //FIXME ++ /* ++ * workaround: if we use IPMMU, sometimes unhandled error ++ * happened ++ */ ++ switch (host->mrq->cmd->opcode) { ++ case MMC_SEND_TUNING_BLOCK_HS200: ++ case MMC_SEND_TUNING_BLOCK: ++ host->force_pio = true; ++ tmio_mmc_enable_dma(host, false); ++ return; /* return for PIO */ ++ default: ++ break; ++ } ++#endif ++ } else { ++ for_each_sg(sg, sg_tmp, host->sg_len, i) { ++ /* ++ * This DMAC cannot handle if buffer is not 8-bytes ++ * alignment ++ */ ++ if (!IS_ALIGNED(sg_tmp->offset, 8) || ++ !IS_ALIGNED(sg_tmp->length, data->blksz) || ++ ((sg_dma_address(sg_tmp) + sg_tmp->length) > ++ GENMASK_ULL(32, 0))) { ++ dev_dbg(&host->pdev->dev, "%s: force pio\n", ++ __func__); ++ host->force_pio = true; ++ tmio_mmc_enable_dma(host, false); ++ return; ++ } ++ } ++ } ++ ++#ifdef CONFIG_MMC_SDHI_PRE_REQ ++ if (host->data->host_cookie != COOKIE_PRE_MAPPED) { ++#endif ++ if (data->flags & MMC_DATA_READ) ++ dir = DMA_FROM_DEVICE; ++ else ++ dir = DMA_TO_DEVICE; ++ ++ ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir); ++ if (ret <= 0) { ++ dev_err(&host->pdev->dev, "%s: dma_map_sg failed\n", __func__); ++ host->force_pio = true; ++ tmio_mmc_enable_dma(host, false); ++ return; ++ } ++#ifdef CONFIG_MMC_SDHI_PRE_REQ ++ } ++#endif ++ ++ tmio_mmc_enable_dma(host, true); ++ /* set context */ ++ ctxt_num = tmio_mmc_set_seq_table(host, mrq, sg, ipmmu_on); ++ if (ctxt_num < 0) { ++ host->force_pio = true; ++ tmio_mmc_enable_dma(host, false); ++ return; ++ } ++ /* set dma mode */ ++ //FIXME ++ tmio_dm_write(host, DM_CM_DTRAN_MODE, ++ DTRAN_MODE_BUS_WID_TH); ++ //DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE); ++ /* enable SEQEND irq */ ++ tmio_dm_write(host, DM_CM_INFO1_MASK, ++ GENMASK_ULL(32, 0) & ~DM_CM_INFO_SEQEND); ++ ++ if (ctxt_num < 4) { ++ /* issue table0 commands */ ++ val = DM_CM_SEQ_CTRL_SEQ_TYPE_SD | ++ DM_CM_SEQ_CTRL_START_NUM(0) | ++ DM_CM_SEQ_CTRL_END_NUM(ctxt_num) | ++ DM_CM_SEQ_CTRL_SEQ_START; ++ tmio_dm_write(host, DM_CM_SEQ_CTRL, val); ++ } else { ++ /* issue table0 commands */ ++ val = DM_CM_SEQ_CTRL_SEQ_TYPE_SD | ++ DM_CM_SEQ_CTRL_T_NUM | ++ DM_CM_SEQ_CTRL_START_NUM(0) | ++ DM_CM_SEQ_CTRL_END_NUM(3) | ++ DM_CM_SEQ_CTRL_SEQ_START; ++ tmio_dm_write(host, DM_CM_SEQ_CTRL, val); ++ /* issue table1 commands */ ++ val = DM_CM_SEQ_CTRL_SEQ_TABLE | ++ DM_CM_SEQ_CTRL_SEQ_TYPE_SD | ++ DM_CM_SEQ_CTRL_T_NUM | ++ DM_CM_SEQ_CTRL_START_NUM(0) | ++ DM_CM_SEQ_CTRL_END_NUM(ctxt_num - 4) | ++ DM_CM_SEQ_CTRL_SEQ_START; ++ tmio_dm_write(host, DM_CM_SEQ_CTRL, val); ++ } ++ ++ return; ++} ++ ++static void tmio_mmc_seq_complete_tasklet_fn(unsigned long arg) ++{ ++ tmio_mmc_complete_tasklet_fn(arg); ++} ++#endif //CONFIG_MMC_SDHI_SEQ ++ + bool __tmio_mmc_dma_irq(struct tmio_mmc_host *host) + { + unsigned int ireg, status; +@@ -237,6 +698,27 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, + (unsigned long)host); + tasklet_init(&host->dma_issue, tmio_mmc_issue_tasklet_fn, + (unsigned long)host); ++#ifdef CONFIG_MMC_SDHI_SEQ ++ tasklet_init(&host->seq_complete, tmio_mmc_seq_complete_tasklet_fn, ++ (unsigned long)host); ++ /* alloc bounce_buf for dummy read */ ++ host->bounce_buf = (u8 *)__get_free_page(GFP_KERNEL | GFP_DMA); ++ if (!host->bounce_buf) ++ goto ebouncebuf; ++ /* setup bounce_sg for dummy read */ ++ sg_init_one(&host->bounce_sg, host->bounce_buf, 1024); ++ if (dma_map_sg(&host->pdev->dev, &host->bounce_sg, 1, DMA_FROM_DEVICE) <= 0) { ++ free_pages((unsigned long)host->bounce_buf, 0); ++ host->bounce_buf = NULL; ++ goto ebouncebuf; ++ } ++ ++ return; ++ ++ebouncebuf: ++ host->chan_rx = host->chan_tx = NULL; ++ return; ++#endif + #endif + } + +@@ -244,4 +726,12 @@ void tmio_mmc_release_dma(struct tmio_mmc_host *host) + { + /* Each value is set to zero to assume "disabling" each DMA */ + host->chan_rx = host->chan_tx = NULL; ++#ifdef CONFIG_MMC_SDHI_SEQ ++ /* free bounce_buf for dummy read */ ++ if (host->bounce_buf) { ++ dma_unmap_sg(&host->pdev->dev, &host->bounce_sg, 1, DMA_FROM_DEVICE); ++ free_pages((unsigned long)host->bounce_buf, 0); ++ host->bounce_buf = NULL; ++ } ++#endif + } +diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c +index 57a954a..e255503 100644 +--- a/drivers/mmc/host/tmio_mmc_pio.c ++++ b/drivers/mmc/host/tmio_mmc_pio.c +@@ -50,6 +50,9 @@ + #include <linux/scatterlist.h> + #include <linux/spinlock.h> + #include <linux/workqueue.h> ++#ifdef CONFIG_MMC_SDHI_PRE_REQ ++#include <linux/dma-mapping.h> ++#endif + + #include "tmio_mmc.h" + +@@ -587,6 +590,79 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host) + schedule_work(&host->done); + } + ++#ifdef CONFIG_MMC_SDHI_SEQ ++static void tmio_mmc_seq_irq(struct tmio_mmc_host *host, unsigned int stat, ++ u32 seq_stat1, u32 seq_stat2) ++{ ++ struct mmc_data *data; ++ struct mmc_command *cmd, *sbc; ++ ++ spin_lock(&host->lock); ++ data = host->data; ++ cmd = host->mrq->cmd; ++ sbc = host->mrq->sbc; ++ ++ //FIXME: How to get SEQ commands response? ++ ++ if (seq_stat2) { ++ //FIXME ++ pr_debug("sequencer error, CMD%d SD_INFO2=0x%x\n", ++ cmd->opcode, stat >> 16); ++ if (stat & TMIO_STAT_CMDTIMEOUT) { ++ cmd->error = -ETIMEDOUT; ++ if (sbc) ++ sbc->error = -ETIMEDOUT; ++ } else if ((stat & TMIO_STAT_CRCFAIL && ++ cmd->flags & MMC_RSP_CRC) || ++ stat & TMIO_STAT_STOPBIT_ERR || ++ stat & TMIO_STAT_CMD_IDX_ERR) { ++ cmd->error = -EILSEQ; ++ if (sbc) ++ sbc->error = -EILSEQ; ++ } ++ ++ if (stat & TMIO_STAT_DATATIMEOUT) ++ data->error = -ETIMEDOUT; ++ else if (stat & TMIO_STAT_CRCFAIL || ++ stat & TMIO_STAT_STOPBIT_ERR || ++ stat & TMIO_STAT_TXUNDERRUN) ++ data->error = -EILSEQ; ++ } ++ ++ if (host->chan_tx && (data->flags & MMC_DATA_WRITE)) { ++ //FIXME ++ u32 status = sd_ctrl_read16_and_16_as_32(host, CTL_STATUS); ++ bool done = false; ++ ++ /* ++ * Has all data been written out yet? Testing on SuperH showed, ++ * that in most cases the first interrupt comes already with the ++ * BUSY status bit clear, but on some operations, like mount or ++ * in the beginning of a write / sync / umount, there is one ++ * DATAEND interrupt with the BUSY bit set, in this cases ++ * waiting for one more interrupt fixes the problem. ++ */ ++ if (host->pdata->flags & TMIO_MMC_HAS_IDLE_WAIT) { ++ if (status & TMIO_STAT_ILL_FUNC) ++ done = true; ++ } else { ++ if (!(status & TMIO_STAT_CMD_BUSY)) ++ done = true; ++ } ++ ++ if (!done) ++ goto out; ++ } ++ ++ /* mask sequencer irq */ ++ tmio_dm_write(host, DM_CM_INFO1_MASK, 0xffffffff); ++ tasklet_schedule(&host->seq_complete); ++ ++out: ++ spin_unlock(&host->lock); ++} ++#endif //CONFIG_MMC_SDHI_SEQ ++ + static void tmio_mmc_data_irq(struct tmio_mmc_host *host, unsigned int stat) + { + struct mmc_data *data; +@@ -737,6 +813,22 @@ static bool __tmio_mmc_card_detect_irq(struct tmio_mmc_host *host, + static bool __tmio_mmc_sdcard_irq(struct tmio_mmc_host *host, + int ireg, int status) + { ++#ifdef CONFIG_MMC_SDHI_SEQ ++ u64 dm_cm_info1; ++ ++ dm_cm_info1 = tmio_dm_read(host, DM_CM_INFO1); ++ if (dm_cm_info1 & DM_CM_INFO_SEQEND) { ++ u64 dm_cm_info2; ++ dm_cm_info2 = tmio_dm_read(host, DM_CM_INFO2); ++ tmio_dm_write(host, DM_CM_INFO1, 0x0); ++ tmio_dm_write(host, DM_CM_INFO2, 0x0); ++ tmio_mmc_ack_mmc_irqs(host, ++ TMIO_MASK_IRQ & ~(TMIO_STAT_CARD_REMOVE | ++ TMIO_STAT_CARD_INSERT)); ++ tmio_mmc_seq_irq(host, status, dm_cm_info1, dm_cm_info2); ++ return true; ++ } ++#endif //CONFIG_MMC_SDHI_SEQ + /* Command completion */ + if (ireg & (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT)) { + tmio_mmc_ack_mmc_irqs(host, +@@ -814,6 +906,61 @@ irqreturn_t tmio_mmc_irq(int irq, void *devid) + } + EXPORT_SYMBOL(tmio_mmc_irq); + ++#ifdef CONFIG_MMC_SDHI_SEQ ++static int tmio_mmc_start_seq(struct tmio_mmc_host *host, ++ struct mmc_request *mrq) ++{ ++ struct tmio_mmc_data *pdata = host->pdata; ++ struct mmc_data *data = mrq->data; ++ ++ pr_debug("setup data transfer: blocksize %08x nr_blocks %d\n", ++ data->blksz, data->blocks); ++ ++ if (!host->chan_rx || !host->chan_tx) { ++ host->force_pio = true; ++ return 0; ++ } ++ ++ /* Some hardware cannot perform 2 byte requests in 4 bit mode */ ++ if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) { ++ int blksz_2bytes = pdata->flags & TMIO_MMC_BLKSZ_2BYTES; ++ ++ if (data->blksz < 2 || (data->blksz < 4 && !blksz_2bytes)) { ++ pr_err("%s: %d byte block unsupported in 4 bit mode\n", ++ mmc_hostname(host->mmc), data->blksz); ++ return -EINVAL; ++ } ++ } ++ ++ tmio_mmc_init_sg(host, data); ++ host->cmd = mrq->cmd; ++ host->data = data; ++ ++ //FIXME ++ sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x000); ++ //sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x100); ++ ++ tmio_mmc_start_sequencer(host); ++ ++ if (host->force_pio) { ++ host->cmd = NULL; ++ host->data = NULL; ++ } ++ ++ return 0; ++} ++ ++static void tmio_mmc_set_blklen_and_blkcnt(struct tmio_mmc_host *host, ++ struct mmc_data *data) ++{ ++ host->force_pio = true; ++ tmio_mmc_init_sg(host, data); ++ host->data = data; ++ ++ sd_ctrl_write16(host, CTL_SD_XFER_LEN, data->blksz); ++ sd_ctrl_write16(host, CTL_XFER_BLK_COUNT, data->blocks); ++} ++#else + static int tmio_mmc_start_data(struct tmio_mmc_host *host, + struct mmc_data *data) + { +@@ -845,6 +992,58 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host, + + return 0; + } ++#endif //CONFIG_MMC_SDHI_SEQ ++ ++#ifdef CONFIG_MMC_SDHI_PRE_REQ ++static void tmio_mmc_post_req(struct mmc_host *mmc, struct mmc_request *req, ++ int err) ++{ ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ struct mmc_data *data = req->data; ++ enum dma_data_direction dir; ++ ++ if (data && data->host_cookie == COOKIE_PRE_MAPPED) { ++ if (req->data->flags & MMC_DATA_READ) ++ dir = DMA_FROM_DEVICE; ++ else ++ dir = DMA_TO_DEVICE; ++ ++ dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len, dir); ++ data->host_cookie = COOKIE_UNMAPPED; ++ } ++} ++ ++static void tmio_mmc_pre_req(struct mmc_host *mmc, struct mmc_request *req, ++ bool is_first_req) ++{ ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ struct mmc_data *data = req->data; ++ enum dma_data_direction dir; ++ int ret; ++ ++#if 1 //FIXME: IPMMU workaround, skip pre_dma_mapping ++#ifdef CONFIG_MMC_SDHI_SEQ ++ if (mmc->max_segs != 1 && mmc->max_segs != 3) ++#else ++ if (mmc->max_segs != 1) ++#endif ++ return; ++#endif ++ ++ if (data && data->host_cookie == COOKIE_UNMAPPED) { ++ if (req->data->flags & MMC_DATA_READ) ++ dir = DMA_FROM_DEVICE; ++ else ++ dir = DMA_TO_DEVICE; ++ ++ ret = dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, dir); ++ if (ret <= 0) ++ dev_err(&host->pdev->dev, "%s: dma_map_sg failed\n", __func__); ++ else ++ data->host_cookie = COOKIE_PRE_MAPPED; ++ } ++} ++#endif //CONFIG_MMC_SDHI_PRE_REQ + + static void tmio_mmc_hw_reset(struct mmc_host *mmc) + { +@@ -934,6 +1133,25 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) + + spin_unlock_irqrestore(&host->lock, flags); + ++#ifdef CONFIG_MMC_SDHI_SEQ ++ //FIXME: SD_COMBO media not tested ++ if (mrq->data) { ++ /* Start SEQ */ ++ ret = tmio_mmc_start_seq(host, mrq); ++ if (ret) ++ goto fail; ++ else if (!host->force_pio) { ++ /* ++ * Successed to start SEQ ++ * Wait SEQ interrupt ++ */ ++ schedule_delayed_work(&host->delayed_reset_work, ++ msecs_to_jiffies(CMDREQ_TIMEOUT)); ++ return; ++ } ++ } ++#endif //CONFIG_MMC_SDHI_SEQ ++ + if (mrq->sbc) { + init_completion(&host->completion); + ret = tmio_mmc_start_command(host, mrq->sbc); +@@ -965,9 +1183,32 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) + } + + if (mrq->data) { ++#ifdef CONFIG_MMC_SDHI_SEQ ++ /* ++ * Failed to start SEQ ++ * Set blklen and blkcnt to transfer in PIO mode ++ */ ++ tmio_mmc_set_blklen_and_blkcnt(host, mrq->data); ++#else + ret = tmio_mmc_start_data(host, mrq->data); + if (ret) + goto fail; ++#endif ++ ++#ifdef CONFIG_MMC_SDHI_PRE_REQ ++ if (host->force_pio && ++ mrq->data->host_cookie == COOKIE_PRE_MAPPED) { ++ /* PIO mode, unmap pre_dma_mapped sg */ ++ enum dma_data_direction dir; ++ if (mrq->data->flags & MMC_DATA_READ) ++ dir = DMA_FROM_DEVICE; ++ else ++ dir = DMA_TO_DEVICE; ++ dma_unmap_sg(&host->pdev->dev, mrq->data->sg, ++ mrq->data->sg_len, dir); ++ mrq->data->host_cookie = COOKIE_UNMAPPED; ++ } ++#endif + } + + ret = tmio_mmc_start_command(host, mrq->cmd); +@@ -1160,6 +1401,10 @@ static int tmio_multi_io_quirk(struct mmc_card *card, + } + + static struct mmc_host_ops tmio_mmc_ops = { ++#ifdef CONFIG_MMC_SDHI_PRE_REQ ++ .post_req = tmio_mmc_post_req, ++ .pre_req = tmio_mmc_pre_req, ++#endif + .request = tmio_mmc_request, + .set_ios = tmio_mmc_set_ios, + .get_ro = tmio_mmc_get_ro, +-- +1.9.1 + |