summaryrefslogtreecommitdiff
path: root/drivers/mmc/host/mtk-sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/mtk-sd.c')
-rw-r--r--drivers/mmc/host/mtk-sd.c541
1 files changed, 388 insertions, 153 deletions
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 1634b1f5d201..31eb90536bce 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -5,6 +5,7 @@
*/
#include <linux/module.h>
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -13,7 +14,6 @@
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
@@ -34,6 +34,7 @@
#include <linux/mmc/slot-gpio.h>
#include "cqhci.h"
+#include "mmc_hsq.h"
#define MAX_BD_NUM 1024
#define MSDC_NR_CLOCKS 3
@@ -66,6 +67,7 @@
#define SDC_RESP3 0x4c
#define SDC_BLK_NUM 0x50
#define SDC_ADV_CFG0 0x64
+#define MSDC_NEW_RX_CFG 0x68
#define EMMC_IOCON 0x7c
#define SDC_ACMD_RESP 0x80
#define DMA_SA_H4BIT 0x8c
@@ -82,6 +84,7 @@
#define EMMC51_CFG0 0x204
#define EMMC50_CFG0 0x208
#define EMMC50_CFG1 0x20c
+#define EMMC50_CFG2 0x21c
#define EMMC50_CFG3 0x220
#define SDC_FIFO_CFG 0x228
#define CQHCI_SETTING 0x7fc
@@ -92,6 +95,7 @@
#define EMMC_TOP_CONTROL 0x00
#define EMMC_TOP_CMD 0x04
#define EMMC50_PAD_DS_TUNE 0x0c
+#define LOOP_TEST_CONTROL 0x30
/*--------------------------------------------------------------------------*/
/* Register Mask */
@@ -203,9 +207,13 @@
#define SDC_STS_CMDBUSY BIT(1) /* RW */
#define SDC_STS_SWR_COMPL BIT(31) /* RW */
-#define SDC_DAT1_IRQ_TRIGGER BIT(19) /* RW */
/* SDC_ADV_CFG0 mask */
+#define SDC_DAT1_IRQ_TRIGGER BIT(19) /* RW */
#define SDC_RX_ENHANCE_EN BIT(20) /* RW */
+#define SDC_NEW_TX_EN BIT(31) /* RW */
+
+/* MSDC_NEW_RX_CFG mask */
+#define MSDC_NEW_RX_PATH_SEL BIT(0) /* RW */
/* DMA_SA_H4BIT mask */
#define DMA_ADDR_HIGH_4BIT GENMASK(3, 0) /* RW */
@@ -227,6 +235,9 @@
/* MSDC_PATCH_BIT mask */
#define MSDC_PATCH_BIT_ODDSUPP BIT(1) /* RW */
+#define MSDC_PATCH_BIT_DIS_WRMON BIT(2) /* RW */
+#define MSDC_PATCH_BIT_RD_DAT_SEL BIT(3) /* RW */
+#define MSDC_PATCH_BIT_DESCUP_SEL BIT(6) /* RW */
#define MSDC_INT_DAT_LATCH_CK_SEL GENMASK(9, 7)
#define MSDC_CKGEN_MSDC_DLY_SEL GENMASK(14, 10)
#define MSDC_PATCH_BIT_IODSSEL BIT(16) /* RW */
@@ -239,15 +250,29 @@
#define MSDC_PATCH_BIT_SPCPUSH BIT(29) /* RW */
#define MSDC_PATCH_BIT_DECRCTMO BIT(30) /* RW */
-#define MSDC_PATCH_BIT1_CMDTA GENMASK(5, 3) /* RW */
+/* MSDC_PATCH_BIT1 mask */
+#define MSDC_PB1_WRDAT_CRC_TACNTR GENMASK(2, 0) /* RW */
+#define MSDC_PATCH_BIT1_CMDTA GENMASK(5, 3) /* RW */
#define MSDC_PB1_BUSY_CHECK_SEL BIT(7) /* RW */
#define MSDC_PATCH_BIT1_STOP_DLY GENMASK(11, 8) /* RW */
-
+#define MSDC_PB1_DDR_CMD_FIX_SEL BIT(14) /* RW */
+#define MSDC_PB1_SINGLE_BURST BIT(16) /* RW */
+#define MSDC_PB1_RSVD20 GENMASK(18, 17) /* RW */
+#define MSDC_PB1_AUTO_SYNCST_CLR BIT(19) /* RW */
+#define MSDC_PB1_MARK_POP_WATER BIT(20) /* RW */
+#define MSDC_PB1_LP_DCM_EN BIT(21) /* RW */
+#define MSDC_PB1_RSVD3 BIT(22) /* RW */
+#define MSDC_PB1_AHB_GDMA_HCLK BIT(23) /* RW */
+#define MSDC_PB1_MSDC_CLK_ENFEAT GENMASK(31, 24) /* RW */
+
+/* MSDC_PATCH_BIT2 mask */
#define MSDC_PATCH_BIT2_CFGRESP BIT(15) /* RW */
#define MSDC_PATCH_BIT2_CFGCRCSTS BIT(28) /* RW */
#define MSDC_PB2_SUPPORT_64G BIT(1) /* RW */
#define MSDC_PB2_RESPWAIT GENMASK(3, 2) /* RW */
#define MSDC_PB2_RESPSTSENSEL GENMASK(18, 16) /* RW */
+#define MSDC_PB2_POP_EN_CNT GENMASK(23, 20) /* RW */
+#define MSDC_PB2_CFGCRCSTSEDGE BIT(25) /* RW */
#define MSDC_PB2_CRCSTSENSEL GENMASK(31, 29) /* RW */
#define MSDC_PAD_TUNE_DATWRDLY GENMASK(4, 0) /* RW */
@@ -264,6 +289,7 @@
#define MSDC_PAD_TUNE_CMD2_SEL BIT(21) /* RW */
#define PAD_DS_TUNE_DLY_SEL BIT(0) /* RW */
+#define PAD_DS_TUNE_DLY2_SEL BIT(1) /* RW */
#define PAD_DS_TUNE_DLY1 GENMASK(6, 2) /* RW */
#define PAD_DS_TUNE_DLY2 GENMASK(11, 7) /* RW */
#define PAD_DS_TUNE_DLY3 GENMASK(16, 12) /* RW */
@@ -281,7 +307,10 @@
/* EMMC50_CFG1 mask */
#define EMMC50_CFG1_DS_CFG BIT(28) /* RW */
-#define EMMC50_CFG3_OUTS_WR GENMASK(4, 0) /* RW */
+/* EMMC50_CFG2 mask */
+#define EMMC50_CFG2_AXI_SET_LEN GENMASK(27, 24) /* RW */
+
+#define EMMC50_CFG3_OUTS_WR GENMASK(4, 0) /* RW */
#define SDC_FIFO_CFG_WRVALIDSEL BIT(24) /* RW */
#define SDC_FIFO_CFG_RDVALIDSEL BIT(25) /* RW */
@@ -309,9 +338,16 @@
/* EMMC50_PAD_DS_TUNE mask */
#define PAD_DS_DLY_SEL BIT(16) /* RW */
+#define PAD_DS_DLY2_SEL BIT(15) /* RW */
#define PAD_DS_DLY1 GENMASK(14, 10) /* RW */
#define PAD_DS_DLY3 GENMASK(4, 0) /* RW */
+/* LOOP_TEST_CONTROL mask */
+#define TEST_LOOP_DSCLK_MUX_SEL BIT(0) /* RW */
+#define TEST_LOOP_LATCH_MUX_SEL BIT(1) /* RW */
+#define LOOP_EN_SEL_CLK BIT(20) /* RW */
+#define TEST_HS400_CMD_LOOP_MUX_SEL BIT(31) /* RW */
+
#define REQ_CMD_EIO BIT(0)
#define REQ_CMD_TMO BIT(1)
#define REQ_DAT_ERR BIT(2)
@@ -392,20 +428,26 @@ struct msdc_save_para {
u32 emmc_top_control;
u32 emmc_top_cmd;
u32 emmc50_pad_ds_tune;
+ u32 loop_test_control;
};
struct mtk_mmc_compatible {
u8 clk_div_bits;
bool recheck_sdio_irq;
bool hs400_tune; /* only used for MT8173 */
+ bool needs_top_base;
u32 pad_tune_reg;
bool async_fifo;
bool data_tune;
bool busy_check;
bool stop_clk_fix;
+ u8 stop_dly_sel;
+ u8 pop_en_cnt;
bool enhance_rx;
bool support_64g;
bool use_internal_cd;
+ bool support_new_tx;
+ bool support_new_rx;
};
struct msdc_tune_para {
@@ -474,6 +516,7 @@ struct msdc_host {
bool hs400_tuning; /* hs400 mode online tuning */
bool internal_cd; /* Use internal card-detect logic */
bool cqhci; /* support eMMC hw cmdq */
+ bool hsq_en; /* Host Software Queue is enabled */
struct msdc_save_para save_para; /* used when gate HCLK */
struct msdc_tune_para def_tune_para; /* default tune setting */
struct msdc_tune_para saved_tune_para; /* tune result of CMD21/CMD19 */
@@ -503,6 +546,7 @@ static const struct mtk_mmc_compatible mt2712_compat = {
.data_tune = true,
.busy_check = true,
.stop_clk_fix = true,
+ .stop_dly_sel = 3,
.enhance_rx = true,
.support_64g = true,
};
@@ -516,6 +560,7 @@ static const struct mtk_mmc_compatible mt6779_compat = {
.data_tune = true,
.busy_check = true,
.stop_clk_fix = true,
+ .stop_dly_sel = 3,
.enhance_rx = true,
.support_64g = true,
};
@@ -555,6 +600,7 @@ static const struct mtk_mmc_compatible mt7622_compat = {
.data_tune = true,
.busy_check = true,
.stop_clk_fix = true,
+ .stop_dly_sel = 3,
.enhance_rx = true,
.support_64g = false,
};
@@ -563,11 +609,13 @@ static const struct mtk_mmc_compatible mt7986_compat = {
.clk_div_bits = 12,
.recheck_sdio_irq = true,
.hs400_tune = false,
+ .needs_top_base = true,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
.busy_check = true,
.stop_clk_fix = true,
+ .stop_dly_sel = 3,
.enhance_rx = true,
.support_64g = true,
};
@@ -602,11 +650,13 @@ static const struct mtk_mmc_compatible mt8183_compat = {
.clk_div_bits = 12,
.recheck_sdio_irq = false,
.hs400_tune = false,
+ .needs_top_base = true,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
.busy_check = true,
.stop_clk_fix = true,
+ .stop_dly_sel = 3,
.enhance_rx = true,
.support_64g = true,
};
@@ -620,6 +670,25 @@ static const struct mtk_mmc_compatible mt8516_compat = {
.data_tune = true,
.busy_check = true,
.stop_clk_fix = true,
+ .stop_dly_sel = 3,
+};
+
+static const struct mtk_mmc_compatible mt8196_compat = {
+ .clk_div_bits = 12,
+ .recheck_sdio_irq = false,
+ .hs400_tune = false,
+ .needs_top_base = true,
+ .pad_tune_reg = MSDC_PAD_TUNE0,
+ .async_fifo = true,
+ .data_tune = true,
+ .busy_check = true,
+ .stop_clk_fix = true,
+ .stop_dly_sel = 1,
+ .pop_en_cnt = 2,
+ .enhance_rx = true,
+ .support_64g = true,
+ .support_new_tx = true,
+ .support_new_rx = true,
};
static const struct of_device_id msdc_of_ids[] = {
@@ -630,9 +699,11 @@ static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt7620-mmc", .data = &mt7620_compat},
{ .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
{ .compatible = "mediatek,mt7986-mmc", .data = &mt7986_compat},
+ { .compatible = "mediatek,mt7988-mmc", .data = &mt7986_compat},
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
{ .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat},
+ { .compatible = "mediatek,mt8196-mmc", .data = &mt8196_compat},
{ .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat},
{}
@@ -796,14 +867,13 @@ static void msdc_unprepare_data(struct msdc_host *host, struct mmc_data *data)
static u64 msdc_timeout_cal(struct msdc_host *host, u64 ns, u64 clks)
{
struct mmc_host *mmc = mmc_from_priv(host);
- u64 timeout, clk_ns;
- u32 mode = 0;
+ u64 timeout;
+ u32 clk_ns, mode = 0;
if (mmc->actual_clock == 0) {
timeout = 0;
} else {
- clk_ns = 1000000000ULL;
- do_div(clk_ns, mmc->actual_clock);
+ clk_ns = 1000000000U / mmc->actual_clock;
timeout = ns + clk_ns - 1;
do_div(timeout, clk_ns);
timeout += clks;
@@ -832,7 +902,7 @@ static void msdc_set_timeout(struct msdc_host *host, u64 ns, u64 clks)
timeout = msdc_timeout_cal(host, ns, clks);
sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC,
- (u32)(timeout > 255 ? 255 : timeout));
+ min_t(u32, timeout, 255));
}
static void msdc_set_busy_timeout(struct msdc_host *host, u64 ns, u64 clks)
@@ -841,7 +911,7 @@ static void msdc_set_busy_timeout(struct msdc_host *host, u64 ns, u64 clks)
timeout = msdc_timeout_cal(host, ns, clks);
sdr_set_field(host->base + SDC_CFG, SDC_CFG_WRDTOC,
- (u32)(timeout > 8191 ? 8191 : timeout));
+ min_t(u32, timeout, 8191));
}
static void msdc_gate_clock(struct msdc_host *host)
@@ -874,6 +944,40 @@ static int msdc_ungate_clock(struct msdc_host *host)
(val & MSDC_CFG_CKSTB), 1, 20000);
}
+static void msdc_new_tx_setting(struct msdc_host *host)
+{
+ u32 val;
+
+ if (!host->top_base)
+ return;
+
+ val = readl(host->top_base + LOOP_TEST_CONTROL);
+ val |= TEST_LOOP_DSCLK_MUX_SEL;
+ val |= TEST_LOOP_LATCH_MUX_SEL;
+ val &= ~TEST_HS400_CMD_LOOP_MUX_SEL;
+
+ switch (host->timing) {
+ case MMC_TIMING_LEGACY:
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_SDR12:
+ case MMC_TIMING_UHS_SDR25:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ val &= ~LOOP_EN_SEL_CLK;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ case MMC_TIMING_MMC_HS400:
+ val |= LOOP_EN_SEL_CLK;
+ break;
+ default:
+ break;
+ }
+ writel(val, host->top_base + LOOP_TEST_CONTROL);
+}
+
static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
{
struct mmc_host *mmc = mmc_from_priv(host);
@@ -883,6 +987,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
u32 sclk;
u32 tune_reg = host->dev_comp->pad_tune_reg;
u32 val;
+ bool timing_changed;
if (!hz) {
dev_dbg(host->dev, "set mclk to 0\n");
@@ -892,6 +997,11 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
return;
}
+ if (host->timing != timing)
+ timing_changed = true;
+ else
+ timing_changed = false;
+
flags = readl(host->base + MSDC_INTEN);
sdr_clr_bits(host->base + MSDC_INTEN, flags);
if (host->dev_comp->clk_div_bits == 8)
@@ -998,6 +1108,9 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_CMDRRDLY,
host->hs400_cmd_int_delay);
+ if (host->dev_comp->support_new_tx && timing_changed)
+ msdc_new_tx_setting(host);
+
dev_dbg(host->dev, "sclk: %d, timing: %d\n", mmc->actual_clock,
timing);
}
@@ -1008,11 +1121,12 @@ static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
u32 resp;
switch (mmc_resp_type(cmd)) {
- /* Actually, R1, R5, R6, R7 are the same */
+ /* Actually, R1, R5, R6, R7 are the same */
case MMC_RSP_R1:
resp = 0x1;
break;
case MMC_RSP_R1B:
+ case MMC_RSP_R1B_NO_CRC:
resp = 0x7;
break;
case MMC_RSP_R2:
@@ -1165,7 +1279,9 @@ static void msdc_track_cmd_data(struct msdc_host *host, struct mmc_command *cmd)
static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
{
+ struct mmc_host *mmc = mmc_from_priv(host);
unsigned long flags;
+ bool hsq_req_done;
/*
* No need check the return value of cancel_delayed_work, as only ONE
@@ -1173,6 +1289,27 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
*/
cancel_delayed_work(&host->req_timeout);
+ /*
+ * If the request was handled from Host Software Queue, there's almost
+ * nothing to do here, and we also don't need to reset mrq as any race
+ * condition would not have any room to happen, since HSQ stores the
+ * "scheduled" mrqs in an internal array of mrq slots anyway.
+ * However, if the controller experienced an error, we still want to
+ * reset it as soon as possible.
+ *
+ * Note that non-HSQ requests will still be happening at times, even
+ * though it is enabled, and that's what is going to reset host->mrq.
+ * Also, msdc_unprepare_data() is going to be called by HSQ when needed
+ * as HSQ request finalization will eventually call the .post_req()
+ * callback of this driver which, in turn, unprepares the data.
+ */
+ hsq_req_done = host->hsq_en ? mmc_hsq_finalize_request(mmc, mrq) : false;
+ if (hsq_req_done) {
+ if (host->error)
+ msdc_reset_hw(host);
+ return;
+ }
+
spin_lock_irqsave(&host->lock, flags);
host->mrq = NULL;
spin_unlock_irqrestore(&host->lock, flags);
@@ -1182,7 +1319,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
msdc_unprepare_data(host, mrq->data);
if (host->error)
msdc_reset_hw(host);
- mmc_request_done(mmc_from_priv(host), mrq);
+ mmc_request_done(mmc, mrq);
if (host->dev_comp->recheck_sdio_irq)
msdc_recheck_sdio_irq(host);
}
@@ -1231,7 +1368,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
}
if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
- if (events & MSDC_INT_CMDTMO ||
+ if ((events & MSDC_INT_CMDTMO && !host->hs400_tuning) ||
(!mmc_op_tuning(cmd->opcode) && !host->hs400_tuning))
/*
* should not clear fifo/interrupt as the tune data
@@ -1239,7 +1376,8 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
* CRC error.
*/
msdc_reset_hw(host);
- if (events & MSDC_INT_RSPCRCERR) {
+ if (events & MSDC_INT_RSPCRCERR &&
+ mmc_resp_type(cmd) != MMC_RSP_R1B_NO_CRC) {
cmd->error = -EILSEQ;
host->error |= REQ_CMD_EIO;
} else if (events & MSDC_INT_CMDTMO) {
@@ -1324,9 +1462,9 @@ static void msdc_start_command(struct msdc_host *host,
static void msdc_cmd_next(struct msdc_host *host,
struct mmc_request *mrq, struct mmc_command *cmd)
{
- if ((cmd->error &&
- !(cmd->error == -EILSEQ &&
- (mmc_op_tuning(cmd->opcode) || host->hs400_tuning))) ||
+ if ((cmd->error && !host->hs400_tuning &&
+ !(cmd->error == -EILSEQ &&
+ mmc_op_tuning(cmd->opcode))) ||
(mrq->sbc && mrq->sbc->error))
msdc_request_done(host, mrq);
else if (cmd == mrq->sbc)
@@ -1342,7 +1480,7 @@ static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct msdc_host *host = mmc_priv(mmc);
host->error = 0;
- WARN_ON(host->mrq);
+ WARN_ON(!host->hsq_en && host->mrq);
host->mrq = mrq;
if (mrq->data)
@@ -1696,7 +1834,7 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
static void msdc_init_hw(struct msdc_host *host)
{
- u32 val;
+ u32 val, pb1_val, pb2_val;
u32 tune_reg = host->dev_comp->pad_tune_reg;
struct mmc_host *mmc = mmc_from_priv(host);
@@ -1706,6 +1844,17 @@ static void msdc_init_hw(struct msdc_host *host)
reset_control_deassert(host->reset);
}
+ /* New tx/rx enable bit need to be 0->1 for hardware check */
+ if (host->dev_comp->support_new_tx) {
+ sdr_clr_bits(host->base + SDC_ADV_CFG0, SDC_NEW_TX_EN);
+ sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_NEW_TX_EN);
+ msdc_new_tx_setting(host);
+ }
+ if (host->dev_comp->support_new_rx) {
+ sdr_clr_bits(host->base + MSDC_NEW_RX_CFG, MSDC_NEW_RX_PATH_SEL);
+ sdr_set_bits(host->base + MSDC_NEW_RX_CFG, MSDC_NEW_RX_PATH_SEL);
+ }
+
/* Configure to MMC/SD mode, clock free running */
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_MODE | MSDC_CFG_CKPDN);
@@ -1738,63 +1887,115 @@ static void msdc_init_hw(struct msdc_host *host)
}
writel(0, host->base + MSDC_IOCON);
sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
- writel(0x403c0046, host->base + MSDC_PATCH_BIT);
- sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
- writel(0xffff4089, host->base + MSDC_PATCH_BIT1);
- sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
+
+ /*
+ * Patch bit 0 and 1 are completely rewritten, but for patch bit 2
+ * defaults are retained and, if necessary, only some bits are fixed
+ * up: read the PB2 register here for later usage in this function.
+ */
+ pb2_val = readl(host->base + MSDC_PATCH_BIT2);
+
+ /* Enable odd number support for 8-bit data bus */
+ val = MSDC_PATCH_BIT_ODDSUPP;
+
+ /* Disable SD command register write monitor */
+ val |= MSDC_PATCH_BIT_DIS_WRMON;
+
+ /* Issue transfer done interrupt after GPD update */
+ val |= MSDC_PATCH_BIT_DESCUP_SEL;
+
+ /* Extend R1B busy detection delay (in clock cycles) */
+ val |= FIELD_PREP(MSDC_PATCH_BIT_BUSYDLY, 15);
+
+ /* Enable CRC phase timeout during data write operation */
+ val |= MSDC_PATCH_BIT_DECRCTMO;
+
+ /* Set CKGEN delay to one stage */
+ val |= FIELD_PREP(MSDC_CKGEN_MSDC_DLY_SEL, 1);
+
+ /* First MSDC_PATCH_BIT setup is done: pull the trigger! */
+ writel(val, host->base + MSDC_PATCH_BIT);
+
+ /* Set wr data, crc status, cmd response turnaround period for UHS104 */
+ pb1_val = FIELD_PREP(MSDC_PB1_WRDAT_CRC_TACNTR, 1);
+ pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_CMDTA, 1);
+ pb1_val |= MSDC_PB1_DDR_CMD_FIX_SEL;
+
+ /* Support 'single' burst type only when AXI_LEN is 0 */
+ sdr_get_field(host->base + EMMC50_CFG2, EMMC50_CFG2_AXI_SET_LEN, &val);
+ if (!val)
+ pb1_val |= MSDC_PB1_SINGLE_BURST;
+
+ /* Set auto sync state clear, block gap stop clk */
+ pb1_val |= MSDC_PB1_RSVD20 | MSDC_PB1_AUTO_SYNCST_CLR | MSDC_PB1_MARK_POP_WATER;
+
+ /* Set low power DCM, use HCLK for GDMA, use MSDC CLK for everything else */
+ pb1_val |= MSDC_PB1_LP_DCM_EN | MSDC_PB1_RSVD3 |
+ MSDC_PB1_AHB_GDMA_HCLK | MSDC_PB1_MSDC_CLK_ENFEAT;
+
+ /* If needed, enable R1b command busy check at controller init time */
+ if (!host->dev_comp->busy_check)
+ pb1_val |= MSDC_PB1_BUSY_CHECK_SEL;
if (host->dev_comp->stop_clk_fix) {
- sdr_set_field(host->base + MSDC_PATCH_BIT1,
- MSDC_PATCH_BIT1_STOP_DLY, 3);
- sdr_clr_bits(host->base + SDC_FIFO_CFG,
- SDC_FIFO_CFG_WRVALIDSEL);
- sdr_clr_bits(host->base + SDC_FIFO_CFG,
- SDC_FIFO_CFG_RDVALIDSEL);
- }
+ if (host->dev_comp->stop_dly_sel)
+ pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_STOP_DLY,
+ host->dev_comp->stop_dly_sel);
+
+ if (host->dev_comp->pop_en_cnt) {
+ pb2_val &= ~MSDC_PB2_POP_EN_CNT;
+ pb2_val |= FIELD_PREP(MSDC_PB2_POP_EN_CNT,
+ host->dev_comp->pop_en_cnt);
+ }
- if (host->dev_comp->busy_check)
- sdr_clr_bits(host->base + MSDC_PATCH_BIT1, BIT(7));
+ sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_WRVALIDSEL);
+ sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_RDVALIDSEL);
+ }
if (host->dev_comp->async_fifo) {
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_RESPWAIT, 3);
- if (host->dev_comp->enhance_rx) {
- if (host->top_base)
- sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
- SDC_RX_ENH_EN);
- else
- sdr_set_bits(host->base + SDC_ADV_CFG0,
- SDC_RX_ENHANCE_EN);
+ /* Set CMD response timeout multiplier to 65 + (16 * 3) cycles */
+ pb2_val &= ~MSDC_PB2_RESPWAIT;
+ pb2_val |= FIELD_PREP(MSDC_PB2_RESPWAIT, 3);
+
+ /* eMMC4.5: Select async FIFO path for CMD resp and CRC status */
+ pb2_val &= ~MSDC_PATCH_BIT2_CFGRESP;
+ pb2_val |= MSDC_PATCH_BIT2_CFGCRCSTS;
+
+ if (!host->dev_comp->enhance_rx) {
+ /* eMMC4.5: Delay 2T for CMD resp and CRC status EN signals */
+ pb2_val &= ~(MSDC_PB2_RESPSTSENSEL | MSDC_PB2_CRCSTSENSEL);
+ pb2_val |= FIELD_PREP(MSDC_PB2_RESPSTSENSEL, 2);
+ pb2_val |= FIELD_PREP(MSDC_PB2_CRCSTSENSEL, 2);
+ } else if (host->top_base) {
+ sdr_set_bits(host->top_base + EMMC_TOP_CONTROL, SDC_RX_ENH_EN);
} else {
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_RESPSTSENSEL, 2);
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_CRCSTSENSEL, 2);
+ sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_RX_ENHANCE_EN);
}
- /* use async fifo, then no need tune internal delay */
- sdr_clr_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PATCH_BIT2_CFGRESP);
- sdr_set_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PATCH_BIT2_CFGCRCSTS);
}
if (host->dev_comp->support_64g)
- sdr_set_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_SUPPORT_64G);
+ pb2_val |= MSDC_PB2_SUPPORT_64G;
+
+ /* Patch Bit 1/2 setup is done: pull the trigger! */
+ writel(pb1_val, host->base + MSDC_PATCH_BIT1);
+ writel(pb2_val, host->base + MSDC_PATCH_BIT2);
+ sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
+
if (host->dev_comp->data_tune) {
if (host->top_base) {
- sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY_SEL);
- sdr_clr_bits(host->top_base + EMMC_TOP_CONTROL,
- DATA_K_VALUE_SEL);
- sdr_set_bits(host->top_base + EMMC_TOP_CMD,
- PAD_CMD_RD_RXDLY_SEL);
+ u32 top_ctl_val = readl(host->top_base + EMMC_TOP_CONTROL);
+ u32 top_cmd_val = readl(host->top_base + EMMC_TOP_CMD);
+
+ top_cmd_val |= PAD_CMD_RD_RXDLY_SEL;
+ top_ctl_val |= PAD_DAT_RD_RXDLY_SEL;
+ top_ctl_val &= ~DATA_K_VALUE_SEL;
if (host->tuning_step > PAD_DELAY_HALF) {
- sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY2_SEL);
- sdr_set_bits(host->top_base + EMMC_TOP_CMD,
- PAD_CMD_RD_RXDLY2_SEL);
+ top_cmd_val |= PAD_CMD_RD_RXDLY2_SEL;
+ top_ctl_val |= PAD_DAT_RD_RXDLY2_SEL;
}
+
+ writel(top_ctl_val, host->top_base + EMMC_TOP_CONTROL);
+ writel(top_cmd_val, host->top_base + EMMC_TOP_CMD);
} else {
sdr_set_bits(host->base + tune_reg,
MSDC_PAD_TUNE_RD_SEL |
@@ -2004,15 +2205,17 @@ static inline void msdc_set_cmd_delay(struct msdc_host *host, u32 value)
u32 tune_reg = host->dev_comp->pad_tune_reg;
if (host->top_base) {
+ u32 regval = readl(host->top_base + EMMC_TOP_CMD);
+
+ regval &= ~(PAD_CMD_RXDLY | PAD_CMD_RXDLY2);
+
if (value < PAD_DELAY_HALF) {
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY, value);
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY2, 0);
+ regval |= FIELD_PREP(PAD_CMD_RXDLY, value);
} else {
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY,
- PAD_DELAY_HALF - 1);
- sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY2,
- value - PAD_DELAY_HALF);
+ regval |= FIELD_PREP(PAD_CMD_RXDLY, PAD_DELAY_HALF - 1);
+ regval |= FIELD_PREP(PAD_CMD_RXDLY2, value - PAD_DELAY_HALF);
}
+ writel(regval, host->top_base + EMMC_TOP_CMD);
} else {
if (value < PAD_DELAY_HALF) {
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRDLY, value);
@@ -2032,17 +2235,18 @@ static inline void msdc_set_data_delay(struct msdc_host *host, u32 value)
u32 tune_reg = host->dev_comp->pad_tune_reg;
if (host->top_base) {
+ u32 regval = readl(host->top_base + EMMC_TOP_CONTROL);
+
+ regval &= ~(PAD_DAT_RD_RXDLY | PAD_DAT_RD_RXDLY2);
+
if (value < PAD_DELAY_HALF) {
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY, value);
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY2, 0);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY, value);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY2, value);
} else {
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY, PAD_DELAY_HALF - 1);
- sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
- PAD_DAT_RD_RXDLY2, value - PAD_DELAY_HALF);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY, PAD_DELAY_HALF - 1);
+ regval |= FIELD_PREP(PAD_DAT_RD_RXDLY2, value - PAD_DELAY_HALF);
}
+ writel(regval, host->top_base + EMMC_TOP_CONTROL);
} else {
if (value < PAD_DELAY_HALF) {
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_DATRRDLY, value);
@@ -2057,6 +2261,19 @@ static inline void msdc_set_data_delay(struct msdc_host *host, u32 value)
}
}
+static inline void msdc_set_data_sample_edge(struct msdc_host *host, bool rising)
+{
+ u32 value = rising ? 0 : 1;
+
+ if (host->dev_comp->support_new_rx) {
+ sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_PATCH_BIT_RD_DAT_SEL, value);
+ sdr_set_field(host->base + MSDC_PATCH_BIT2, MSDC_PB2_CFGCRCSTSEDGE, value);
+ } else {
+ sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DSPL, value);
+ sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL, value);
+ }
+}
+
static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
{
struct msdc_host *host = mmc_priv(mmc);
@@ -2212,8 +2429,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_INT_DAT_LATCH_CK_SEL,
host->latch_ck);
- sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
- sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, true);
for (i = 0; i < host->tuning_step; i++) {
msdc_set_data_delay(host, i);
ret = mmc_send_tuning(mmc, opcode, NULL);
@@ -2226,8 +2442,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
(final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
goto skip_fall;
- sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
- sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, false);
for (i = 0; i < host->tuning_step; i++) {
msdc_set_data_delay(host, i);
ret = mmc_send_tuning(mmc, opcode, NULL);
@@ -2239,12 +2454,10 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
skip_fall:
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
if (final_maxlen == final_rise_delay.maxlen) {
- sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
- sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, true);
final_delay = final_rise_delay.final_phase;
} else {
- sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
- sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, false);
final_delay = final_fall_delay.final_phase;
}
msdc_set_data_delay(host, final_delay);
@@ -2269,8 +2482,7 @@ static int msdc_tune_together(struct mmc_host *mmc, u32 opcode)
host->latch_ck);
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
- sdr_clr_bits(host->base + MSDC_IOCON,
- MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, true);
for (i = 0; i < host->tuning_step; i++) {
msdc_set_cmd_delay(host, i);
msdc_set_data_delay(host, i);
@@ -2285,8 +2497,7 @@ static int msdc_tune_together(struct mmc_host *mmc, u32 opcode)
goto skip_fall;
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
- sdr_set_bits(host->base + MSDC_IOCON,
- MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, false);
for (i = 0; i < host->tuning_step; i++) {
msdc_set_cmd_delay(host, i);
msdc_set_data_delay(host, i);
@@ -2300,13 +2511,11 @@ skip_fall:
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
if (final_maxlen == final_rise_delay.maxlen) {
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
- sdr_clr_bits(host->base + MSDC_IOCON,
- MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, true);
final_delay = final_rise_delay.final_phase;
} else {
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
- sdr_set_bits(host->base + MSDC_IOCON,
- MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, false);
final_delay = final_fall_delay.final_phase;
}
@@ -2326,8 +2535,7 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->dev_comp->data_tune && host->dev_comp->async_fifo) {
ret = msdc_tune_together(mmc, opcode);
if (host->hs400_mode) {
- sdr_clr_bits(host->base + MSDC_IOCON,
- MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, true);
msdc_set_data_delay(host, 0);
}
goto tune_done;
@@ -2363,13 +2571,23 @@ tune_done:
static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct msdc_host *host = mmc_priv(mmc);
+
host->hs400_mode = true;
- if (host->top_base)
- writel(host->hs400_ds_delay,
- host->top_base + EMMC50_PAD_DS_TUNE);
- else
- writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
+ if (host->top_base) {
+ if (host->hs400_ds_dly3)
+ sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE,
+ PAD_DS_DLY3, host->hs400_ds_dly3);
+ if (host->hs400_ds_delay)
+ writel(host->hs400_ds_delay,
+ host->top_base + EMMC50_PAD_DS_TUNE);
+ } else {
+ if (host->hs400_ds_dly3)
+ sdr_set_field(host->base + PAD_DS_TUNE,
+ PAD_DS_TUNE_DLY3, host->hs400_ds_dly3);
+ if (host->hs400_ds_delay)
+ writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
+ }
/* hs400 mode must set it to 0 */
sdr_clr_bits(host->base + MSDC_PATCH_BIT2, MSDC_PATCH_BIT2_CFGCRCSTS);
/* to improve read performance, set outstanding to 2 */
@@ -2389,14 +2607,11 @@ static int msdc_execute_hs400_tuning(struct mmc_host *mmc, struct mmc_card *card
if (host->top_base) {
sdr_set_bits(host->top_base + EMMC50_PAD_DS_TUNE,
PAD_DS_DLY_SEL);
- if (host->hs400_ds_dly3)
- sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE,
- PAD_DS_DLY3, host->hs400_ds_dly3);
+ sdr_clr_bits(host->top_base + EMMC50_PAD_DS_TUNE,
+ PAD_DS_DLY2_SEL);
} else {
sdr_set_bits(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY_SEL);
- if (host->hs400_ds_dly3)
- sdr_set_field(host->base + PAD_DS_TUNE,
- PAD_DS_TUNE_DLY3, host->hs400_ds_dly3);
+ sdr_clr_bits(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY2_SEL);
}
host->hs400_tuning = true;
@@ -2729,7 +2944,6 @@ static int msdc_drv_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
struct msdc_host *host;
- struct resource *res;
int ret;
if (!pdev->dev.of_node) {
@@ -2738,77 +2952,68 @@ static int msdc_drv_probe(struct platform_device *pdev)
}
/* Allocate MMC host for this device */
- mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
+ mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(struct msdc_host));
if (!mmc)
return -ENOMEM;
host = mmc_priv(mmc);
ret = mmc_of_parse(mmc);
if (ret)
- goto host_free;
+ return ret;
host->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(host->base)) {
- ret = PTR_ERR(host->base);
- goto host_free;
- }
+ if (IS_ERR(host->base))
+ return PTR_ERR(host->base);
+
+ host->dev_comp = of_device_get_match_data(&pdev->dev);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (res) {
- host->top_base = devm_ioremap_resource(&pdev->dev, res);
+ if (host->dev_comp->needs_top_base) {
+ host->top_base = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(host->top_base))
- host->top_base = NULL;
+ return PTR_ERR(host->top_base);
}
ret = mmc_regulator_get_supply(mmc);
if (ret)
- goto host_free;
+ return ret;
ret = msdc_of_clock_parse(pdev, host);
if (ret)
- goto host_free;
+ return ret;
host->reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
"hrst");
- if (IS_ERR(host->reset)) {
- ret = PTR_ERR(host->reset);
- goto host_free;
- }
+ if (IS_ERR(host->reset))
+ return PTR_ERR(host->reset);
/* only eMMC has crypto property */
if (!(mmc->caps2 & MMC_CAP2_NO_MMC)) {
host->crypto_clk = devm_clk_get_optional(&pdev->dev, "crypto");
if (IS_ERR(host->crypto_clk))
- host->crypto_clk = NULL;
- else
+ return PTR_ERR(host->crypto_clk);
+ else if (host->crypto_clk)
mmc->caps2 |= MMC_CAP2_CRYPTO;
}
host->irq = platform_get_irq(pdev, 0);
- if (host->irq < 0) {
- ret = host->irq;
- goto host_free;
- }
+ if (host->irq < 0)
+ return host->irq;
host->pinctrl = devm_pinctrl_get(&pdev->dev);
- if (IS_ERR(host->pinctrl)) {
- ret = PTR_ERR(host->pinctrl);
- dev_err(&pdev->dev, "Cannot find pinctrl!\n");
- goto host_free;
- }
+ if (IS_ERR(host->pinctrl))
+ return dev_err_probe(&pdev->dev, PTR_ERR(host->pinctrl),
+ "Cannot find pinctrl");
host->pins_default = pinctrl_lookup_state(host->pinctrl, "default");
if (IS_ERR(host->pins_default)) {
- ret = PTR_ERR(host->pins_default);
dev_err(&pdev->dev, "Cannot find pinctrl default!\n");
- goto host_free;
+ return PTR_ERR(host->pins_default);
}
host->pins_uhs = pinctrl_lookup_state(host->pinctrl, "state_uhs");
if (IS_ERR(host->pins_uhs)) {
- ret = PTR_ERR(host->pins_uhs);
dev_err(&pdev->dev, "Cannot find pinctrl uhs!\n");
- goto host_free;
+ return PTR_ERR(host->pins_uhs);
}
/* Support for SDIO eint irq ? */
@@ -2828,7 +3033,6 @@ static int msdc_drv_probe(struct platform_device *pdev)
msdc_of_property_parse(pdev, host);
host->dev = &pdev->dev;
- host->dev_comp = of_device_get_match_data(&pdev->dev);
host->src_clk_freq = clk_get_rate(host->src_clk);
/* Set host parameters to mmc */
mmc->ops = &mt_msdc_ops;
@@ -2838,7 +3042,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095);
if (!(mmc->caps & MMC_CAP_NONREMOVABLE) &&
- !mmc_can_gpio_cd(mmc) &&
+ !mmc_host_can_gpio_cd(mmc) &&
host->dev_comp->use_internal_cd) {
/*
* Is removable but no GPIO declared, so
@@ -2887,7 +3091,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
ret = msdc_ungate_clock(host);
if (ret) {
dev_err(&pdev->dev, "Cannot ungate clocks!\n");
- goto release_mem;
+ goto release_clk;
}
msdc_init_hw(host);
@@ -2897,20 +3101,33 @@ static int msdc_drv_probe(struct platform_device *pdev)
GFP_KERNEL);
if (!host->cq_host) {
ret = -ENOMEM;
- goto host_free;
+ goto release;
}
host->cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
host->cq_host->mmio = host->base + 0x800;
host->cq_host->ops = &msdc_cmdq_ops;
ret = cqhci_init(host->cq_host, mmc, true);
if (ret)
- goto host_free;
+ goto release;
mmc->max_segs = 128;
/* cqhci 16bit length */
/* 0 size, means 65536 so we don't have to -1 here */
mmc->max_seg_size = 64 * 1024;
/* Reduce CIT to 0x40 that corresponds to 2.35us */
msdc_cqe_cit_cal(host, 2350);
+ } else if (mmc->caps2 & MMC_CAP2_NO_SDIO) {
+ /* Use HSQ on eMMC/SD (but not on SDIO) if HW CQE not supported */
+ struct mmc_hsq *hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL);
+ if (!hsq) {
+ ret = -ENOMEM;
+ goto release;
+ }
+
+ ret = mmc_hsq_init(hsq, mmc);
+ if (ret)
+ goto release;
+
+ host->hsq_en = true;
}
ret = devm_request_irq(&pdev->dev, host->irq, msdc_irq,
@@ -2931,21 +3148,20 @@ static int msdc_drv_probe(struct platform_device *pdev)
end:
pm_runtime_disable(host->dev);
release:
- platform_set_drvdata(pdev, NULL);
msdc_deinit_hw(host);
+release_clk:
msdc_gate_clock(host);
+ platform_set_drvdata(pdev, NULL);
release_mem:
+ device_init_wakeup(&pdev->dev, false);
if (host->dma.gpd)
dma_free_coherent(&pdev->dev,
2 * sizeof(struct mt_gpdma_desc),
host->dma.gpd, host->dma.gpd_addr);
if (host->dma.bd)
dma_free_coherent(&pdev->dev,
- MAX_BD_NUM * sizeof(struct mt_bdma_desc),
- host->dma.bd, host->dma.bd_addr);
-host_free:
- mmc_free_host(mmc);
-
+ MAX_BD_NUM * sizeof(struct mt_bdma_desc),
+ host->dma.bd, host->dma.bd_addr);
return ret;
}
@@ -2970,9 +3186,8 @@ static void msdc_drv_remove(struct platform_device *pdev)
2 * sizeof(struct mt_gpdma_desc),
host->dma.gpd, host->dma.gpd_addr);
dma_free_coherent(&pdev->dev, MAX_BD_NUM * sizeof(struct mt_bdma_desc),
- host->dma.bd, host->dma.bd_addr);
-
- mmc_free_host(mmc);
+ host->dma.bd, host->dma.bd_addr);
+ device_init_wakeup(&pdev->dev, false);
}
static void msdc_save_reg(struct msdc_host *host)
@@ -2997,6 +3212,8 @@ static void msdc_save_reg(struct msdc_host *host)
readl(host->top_base + EMMC_TOP_CMD);
host->save_para.emmc50_pad_ds_tune =
readl(host->top_base + EMMC50_PAD_DS_TUNE);
+ host->save_para.loop_test_control =
+ readl(host->top_base + LOOP_TEST_CONTROL);
} else {
host->save_para.pad_tune = readl(host->base + tune_reg);
}
@@ -3007,6 +3224,15 @@ static void msdc_restore_reg(struct msdc_host *host)
struct mmc_host *mmc = mmc_from_priv(host);
u32 tune_reg = host->dev_comp->pad_tune_reg;
+ if (host->dev_comp->support_new_tx) {
+ sdr_clr_bits(host->base + SDC_ADV_CFG0, SDC_NEW_TX_EN);
+ sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_NEW_TX_EN);
+ }
+ if (host->dev_comp->support_new_rx) {
+ sdr_clr_bits(host->base + MSDC_NEW_RX_CFG, MSDC_NEW_RX_PATH_SEL);
+ sdr_set_bits(host->base + MSDC_NEW_RX_CFG, MSDC_NEW_RX_PATH_SEL);
+ }
+
writel(host->save_para.msdc_cfg, host->base + MSDC_CFG);
writel(host->save_para.iocon, host->base + MSDC_IOCON);
writel(host->save_para.sdc_cfg, host->base + SDC_CFG);
@@ -3025,6 +3251,8 @@ static void msdc_restore_reg(struct msdc_host *host)
host->top_base + EMMC_TOP_CMD);
writel(host->save_para.emmc50_pad_ds_tune,
host->top_base + EMMC50_PAD_DS_TUNE);
+ writel(host->save_para.loop_test_control,
+ host->top_base + LOOP_TEST_CONTROL);
} else {
writel(host->save_para.pad_tune, host->base + tune_reg);
}
@@ -3038,6 +3266,9 @@ static int __maybe_unused msdc_runtime_suspend(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev);
struct msdc_host *host = mmc_priv(mmc);
+ if (host->hsq_en)
+ mmc_hsq_suspend(mmc);
+
msdc_save_reg(host);
if (sdio_irq_claimed(mmc)) {
@@ -3068,6 +3299,10 @@ static int __maybe_unused msdc_runtime_resume(struct device *dev)
pinctrl_select_state(host->pinctrl, host->pins_uhs);
enable_irq(host->irq);
}
+
+ if (host->hsq_en)
+ mmc_hsq_resume(mmc);
+
return 0;
}
@@ -3114,7 +3349,7 @@ static const struct dev_pm_ops msdc_dev_pm_ops = {
static struct platform_driver mt_msdc_driver = {
.probe = msdc_drv_probe,
- .remove_new = msdc_drv_remove,
+ .remove = msdc_drv_remove,
.driver = {
.name = "mtk-msdc",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,