From 95099d4622f8cb224d94e314c7a8e0df60b13f87 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Sat, 09 Dec 2023 08:38:01 +0000
Subject: [PATCH] enable docker ppp
---
kernel/drivers/mmc/host/mtk-sd.c | 798 ++++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 704 insertions(+), 94 deletions(-)
diff --git a/kernel/drivers/mmc/host/mtk-sd.c b/kernel/drivers/mmc/host/mtk-sd.c
index 967e477..d71c113 100644
--- a/kernel/drivers/mmc/host/mtk-sd.c
+++ b/kernel/drivers/mmc/host/mtk-sd.c
@@ -1,21 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014-2015 MediaTek Inc.
* Author: Chaotian.Jing <chaotian.jing@mediatek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/of_address.h>
@@ -30,6 +23,7 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
+#include <linux/reset.h>
#include <linux/mmc/card.h>
#include <linux/mmc/core.h>
@@ -38,6 +32,8 @@
#include <linux/mmc/sd.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/slot-gpio.h>
+
+#include "cqhci.h"
#define MAX_BD_NUM 1024
@@ -87,6 +83,13 @@
#define SDC_FIFO_CFG 0x228
/*--------------------------------------------------------------------------*/
+/* Top Pad Register Offset */
+/*--------------------------------------------------------------------------*/
+#define EMMC_TOP_CONTROL 0x00
+#define EMMC_TOP_CMD 0x04
+#define EMMC50_PAD_DS_TUNE 0x0c
+
+/*--------------------------------------------------------------------------*/
/* Register Mask */
/*--------------------------------------------------------------------------*/
@@ -129,6 +132,7 @@
#define MSDC_PS_CDSTS (0x1 << 1) /* R */
#define MSDC_PS_CDDEBOUNCE (0xf << 12) /* RW */
#define MSDC_PS_DAT (0xff << 16) /* R */
+#define MSDC_PS_DATA1 (0x1 << 17) /* R */
#define MSDC_PS_CMD (0x1 << 24) /* R */
#define MSDC_PS_WP (0x1 << 31) /* R */
@@ -152,6 +156,7 @@
#define MSDC_INT_DMA_BDCSERR (0x1 << 17) /* W1C */
#define MSDC_INT_DMA_GPDCSERR (0x1 << 18) /* W1C */
#define MSDC_INT_DMA_PROTECT (0x1 << 19) /* W1C */
+#define MSDC_INT_CMDQ (0x1 << 28) /* W1C */
/* MSDC_INTEN mask */
#define MSDC_INTEN_MMCIRQ (0x1 << 0) /* RW */
@@ -182,6 +187,7 @@
/* SDC_CFG mask */
#define SDC_CFG_SDIOINTWKUP (0x1 << 0) /* RW */
#define SDC_CFG_INSWKUP (0x1 << 1) /* RW */
+#define SDC_CFG_WRDTOC (0x1fff << 2) /* RW */
#define SDC_CFG_BUSWIDTH (0x3 << 16) /* RW */
#define SDC_CFG_SDIO (0x1 << 19) /* RW */
#define SDC_CFG_SDIOIDE (0x1 << 20) /* RW */
@@ -193,6 +199,7 @@
#define SDC_STS_CMDBUSY (0x1 << 1) /* RW */
#define SDC_STS_SWR_COMPL (0x1 << 31) /* RW */
+#define SDC_DAT1_IRQ_TRIGGER (0x1 << 19) /* RW */
/* SDC_ADV_CFG0 mask */
#define SDC_RX_ENHANCE_EN (0x1 << 20) /* RW */
@@ -229,6 +236,7 @@
#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */
#define MSDC_PATCH_BIT1_CMDTA (0x7 << 3) /* RW */
+#define MSDC_PB1_BUSY_CHECK_SEL (0x1 << 7) /* RW */
#define MSDC_PATCH_BIT1_STOP_DLY (0xf << 8) /* RW */
#define MSDC_PATCH_BIT2_CFGRESP (0x1 << 15) /* RW */
@@ -262,6 +270,23 @@
#define SDC_FIFO_CFG_WRVALIDSEL (0x1 << 24) /* RW */
#define SDC_FIFO_CFG_RDVALIDSEL (0x1 << 25) /* RW */
+/* EMMC_TOP_CONTROL mask */
+#define PAD_RXDLY_SEL (0x1 << 0) /* RW */
+#define DELAY_EN (0x1 << 1) /* RW */
+#define PAD_DAT_RD_RXDLY2 (0x1f << 2) /* RW */
+#define PAD_DAT_RD_RXDLY (0x1f << 7) /* RW */
+#define PAD_DAT_RD_RXDLY2_SEL (0x1 << 12) /* RW */
+#define PAD_DAT_RD_RXDLY_SEL (0x1 << 13) /* RW */
+#define DATA_K_VALUE_SEL (0x1 << 14) /* RW */
+#define SDC_RX_ENH_EN (0x1 << 15) /* TW */
+
+/* EMMC_TOP_CMD mask */
+#define PAD_CMD_RXDLY2 (0x1f << 0) /* RW */
+#define PAD_CMD_RXDLY (0x1f << 5) /* RW */
+#define PAD_CMD_RD_RXDLY2_SEL (0x1 << 10) /* RW */
+#define PAD_CMD_RD_RXDLY_SEL (0x1 << 11) /* RW */
+#define PAD_CMD_TX_DLY (0x1f << 12) /* RW */
+
#define REQ_CMD_EIO (0x1 << 0)
#define REQ_CMD_TMO (0x1 << 1)
#define REQ_DAT_ERR (0x1 << 2)
@@ -276,6 +301,8 @@
#define MTK_MMC_AUTOSUSPEND_DELAY 50
#define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */
#define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */
+
+#define DEFAULT_DEBOUNCE (8) /* 8 cycles CD debounce */
#define PAD_DELAY_MAX 32 /* PAD delay cells */
/*--------------------------------------------------------------------------*/
@@ -311,6 +338,7 @@
u32 ptr;
u32 bd_data_len;
#define BDMA_DESC_BUFLEN (0xffff) /* bit0 ~ bit15 */
+#define BDMA_DESC_BUFLEN_EXT (0xffffff) /* bit0 ~ bit23 */
};
struct msdc_dma {
@@ -334,10 +362,14 @@
u32 emmc50_cfg0;
u32 emmc50_cfg3;
u32 sdc_fifo_cfg;
+ u32 emmc_top_control;
+ u32 emmc_top_cmd;
+ u32 emmc50_pad_ds_tune;
};
struct mtk_mmc_compatible {
u8 clk_div_bits;
+ bool recheck_sdio_irq;
bool hs400_tune; /* only used for MT8173 */
u32 pad_tune_reg;
bool async_fifo;
@@ -346,12 +378,15 @@
bool stop_clk_fix;
bool enhance_rx;
bool support_64g;
+ bool use_internal_cd;
};
struct msdc_tune_para {
u32 iocon;
u32 pad_tune;
u32 pad_cmd_tune;
+ u32 emmc_top_control;
+ u32 emmc_top_cmd;
};
struct msdc_delay_phase {
@@ -363,7 +398,6 @@
struct msdc_host {
struct device *dev;
const struct mtk_mmc_compatible *dev_comp;
- struct mmc_host *mmc; /* mmc structure */
int cmd_rsp;
spinlock_t lock;
@@ -373,6 +407,7 @@
int error;
void __iomem *base; /* host base address */
+ void __iomem *top_base; /* host top register base address */
struct msdc_dma dma; /* dma channel */
u64 dma_mask;
@@ -385,9 +420,11 @@
struct pinctrl_state *pins_uhs;
struct delayed_work req_timeout;
int irq; /* host interrupt */
+ struct reset_control *reset;
struct clk *src_clk; /* msdc source clock */
struct clk *h_clk; /* msdc h_clk */
+ struct clk *bus_clk; /* bus clock which used to access register */
struct clk *src_clk_cg; /* msdc source clock control gate */
u32 mclk; /* mmc subsystem clock frequency */
u32 src_clk_freq; /* source clock frequency */
@@ -400,13 +437,17 @@
bool hs400_cmd_resp_sel_rising;
/* cmd response sample selection for HS400 */
bool hs400_mode; /* current eMMC will run at hs400 mode */
+ bool internal_cd; /* Use internal card-detect logic */
+ bool cqhci; /* support eMMC hw cmdq */
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 */
+ struct cqhci_host *cq_host;
};
static const struct mtk_mmc_compatible mt8135_compat = {
.clk_div_bits = 8,
+ .recheck_sdio_irq = true,
.hs400_tune = false,
.pad_tune_reg = MSDC_PAD_TUNE,
.async_fifo = false,
@@ -419,6 +460,7 @@
static const struct mtk_mmc_compatible mt8173_compat = {
.clk_div_bits = 8,
+ .recheck_sdio_irq = true,
.hs400_tune = true,
.pad_tune_reg = MSDC_PAD_TUNE,
.async_fifo = false,
@@ -429,8 +471,22 @@
.support_64g = false,
};
+static const struct mtk_mmc_compatible mt8183_compat = {
+ .clk_div_bits = 12,
+ .recheck_sdio_irq = false,
+ .hs400_tune = false,
+ .pad_tune_reg = MSDC_PAD_TUNE0,
+ .async_fifo = true,
+ .data_tune = true,
+ .busy_check = true,
+ .stop_clk_fix = true,
+ .enhance_rx = true,
+ .support_64g = true,
+};
+
static const struct mtk_mmc_compatible mt2701_compat = {
.clk_div_bits = 12,
+ .recheck_sdio_irq = true,
.hs400_tune = false,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
@@ -443,6 +499,7 @@
static const struct mtk_mmc_compatible mt2712_compat = {
.clk_div_bits = 12,
+ .recheck_sdio_irq = false,
.hs400_tune = false,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
@@ -455,6 +512,7 @@
static const struct mtk_mmc_compatible mt7622_compat = {
.clk_div_bits = 12,
+ .recheck_sdio_irq = true,
.hs400_tune = false,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
@@ -465,12 +523,53 @@
.support_64g = false,
};
+static const struct mtk_mmc_compatible mt8516_compat = {
+ .clk_div_bits = 12,
+ .recheck_sdio_irq = true,
+ .hs400_tune = false,
+ .pad_tune_reg = MSDC_PAD_TUNE0,
+ .async_fifo = true,
+ .data_tune = true,
+ .busy_check = true,
+ .stop_clk_fix = true,
+};
+
+static const struct mtk_mmc_compatible mt7620_compat = {
+ .clk_div_bits = 8,
+ .recheck_sdio_irq = true,
+ .hs400_tune = false,
+ .pad_tune_reg = MSDC_PAD_TUNE,
+ .async_fifo = false,
+ .data_tune = false,
+ .busy_check = false,
+ .stop_clk_fix = false,
+ .enhance_rx = false,
+ .use_internal_cd = true,
+};
+
+static const struct mtk_mmc_compatible mt6779_compat = {
+ .clk_div_bits = 12,
+ .recheck_sdio_irq = false,
+ .hs400_tune = false,
+ .pad_tune_reg = MSDC_PAD_TUNE0,
+ .async_fifo = true,
+ .data_tune = true,
+ .busy_check = true,
+ .stop_clk_fix = true,
+ .enhance_rx = true,
+ .support_64g = true,
+};
+
static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
+ { .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat},
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
{ .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
+ { .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat},
+ { .compatible = "mediatek,mt7620-mmc", .data = &mt7620_compat},
+ { .compatible = "mediatek,mt6779-mmc", .data = &mt6779_compat},
{}
};
MODULE_DEVICE_TABLE(of, msdc_of_ids);
@@ -525,6 +624,7 @@
static void msdc_cmd_next(struct msdc_host *host,
struct mmc_request *mrq, struct mmc_command *cmd);
+static void __msdc_enable_sdio_irq(struct msdc_host *host, int enb);
static const u32 cmd_ints_mask = MSDC_INTEN_CMDRDY | MSDC_INTEN_RSPCRCERR |
MSDC_INTEN_CMDTMO | MSDC_INTEN_ACMDRDY |
@@ -578,8 +678,14 @@
bd[j].bd_info |= (upper_32_bits(dma_address) & 0xf)
<< 28;
}
- bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN;
- bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN);
+
+ if (host->dev_comp->support_64g) {
+ bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN_EXT;
+ bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN_EXT);
+ } else {
+ bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN;
+ bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN);
+ }
if (j == data->sg_count - 1) /* the last bd */
bd[j].bd_info |= BDMA_DESC_EOL;
@@ -627,21 +733,22 @@
}
}
-/* clock control primitives */
-static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
+static u64 msdc_timeout_cal(struct msdc_host *host, u64 ns, u64 clks)
{
- u32 timeout, clk_ns;
+ struct mmc_host *mmc = mmc_from_priv(host);
+ u64 timeout, clk_ns;
u32 mode = 0;
- host->timeout_ns = ns;
- host->timeout_clks = clks;
- if (host->mmc->actual_clock == 0) {
+ if (mmc->actual_clock == 0) {
timeout = 0;
} else {
- clk_ns = 1000000000UL / host->mmc->actual_clock;
- timeout = (ns + clk_ns - 1) / clk_ns + clks;
+ clk_ns = 1000000000ULL;
+ do_div(clk_ns, mmc->actual_clock);
+ timeout = ns + clk_ns - 1;
+ do_div(timeout, clk_ns);
+ timeout += clks;
/* in 1048576 sclk cycle unit */
- timeout = (timeout + (0x1 << 20) - 1) >> 20;
+ timeout = DIV_ROUND_UP(timeout, (0x1 << 20));
if (host->dev_comp->clk_div_bits == 8)
sdr_get_field(host->base + MSDC_CFG,
MSDC_CFG_CKMOD, &mode);
@@ -651,21 +758,44 @@
/*DDR mode will double the clk cycles for data timeout */
timeout = mode >= 2 ? timeout * 2 : timeout;
timeout = timeout > 1 ? timeout - 1 : 0;
- timeout = timeout > 255 ? 255 : timeout;
}
- sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, timeout);
+ return timeout;
+}
+
+/* clock control primitives */
+static void msdc_set_timeout(struct msdc_host *host, u64 ns, u64 clks)
+{
+ u64 timeout;
+
+ host->timeout_ns = ns;
+ host->timeout_clks = clks;
+
+ timeout = msdc_timeout_cal(host, ns, clks);
+ sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC,
+ (u32)(timeout > 255 ? 255 : timeout));
+}
+
+static void msdc_set_busy_timeout(struct msdc_host *host, u64 ns, u64 clks)
+{
+ u64 timeout;
+
+ timeout = msdc_timeout_cal(host, ns, clks);
+ sdr_set_field(host->base + SDC_CFG, SDC_CFG_WRDTOC,
+ (u32)(timeout > 8191 ? 8191 : timeout));
}
static void msdc_gate_clock(struct msdc_host *host)
{
clk_disable_unprepare(host->src_clk_cg);
clk_disable_unprepare(host->src_clk);
+ clk_disable_unprepare(host->bus_clk);
clk_disable_unprepare(host->h_clk);
}
static void msdc_ungate_clock(struct msdc_host *host)
{
clk_prepare_enable(host->h_clk);
+ clk_prepare_enable(host->bus_clk);
clk_prepare_enable(host->src_clk);
clk_prepare_enable(host->src_clk_cg);
while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
@@ -674,6 +804,7 @@
static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
{
+ struct mmc_host *mmc = mmc_from_priv(host);
u32 mode;
u32 flags;
u32 div;
@@ -683,7 +814,7 @@
if (!hz) {
dev_dbg(host->dev, "set mclk to 0\n");
host->mclk = 0;
- host->mmc->actual_clock = 0;
+ mmc->actual_clock = 0;
sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
return;
}
@@ -762,7 +893,7 @@
while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
cpu_relax();
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
- host->mmc->actual_clock = sclk;
+ mmc->actual_clock = sclk;
host->mclk = hz;
host->timing = timing;
/* need because clk changed. */
@@ -773,14 +904,30 @@
* mmc_select_hs400() will drop to 50Mhz and High speed mode,
* tune result of hs200/200Mhz is not suitable for 50Mhz
*/
- if (host->mmc->actual_clock <= 52000000) {
+ if (mmc->actual_clock <= 52000000) {
writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
- writel(host->def_tune_para.pad_tune, host->base + tune_reg);
+ if (host->top_base) {
+ writel(host->def_tune_para.emmc_top_control,
+ host->top_base + EMMC_TOP_CONTROL);
+ writel(host->def_tune_para.emmc_top_cmd,
+ host->top_base + EMMC_TOP_CMD);
+ } else {
+ writel(host->def_tune_para.pad_tune,
+ host->base + tune_reg);
+ }
} else {
writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
- writel(host->saved_tune_para.pad_tune, host->base + tune_reg);
writel(host->saved_tune_para.pad_cmd_tune,
host->base + PAD_CMD_TUNE);
+ if (host->top_base) {
+ writel(host->saved_tune_para.emmc_top_control,
+ host->top_base + EMMC_TOP_CONTROL);
+ writel(host->saved_tune_para.emmc_top_cmd,
+ host->top_base + EMMC_TOP_CMD);
+ } else {
+ writel(host->saved_tune_para.pad_tune,
+ host->base + tune_reg);
+ }
}
if (timing == MMC_TIMING_MMC_HS400 &&
@@ -788,7 +935,7 @@
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_CMDRRDLY,
host->hs400_cmd_int_delay);
- dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->mmc->actual_clock,
+ dev_dbg(host->dev, "sclk: %d, timing: %d\n", mmc->actual_clock,
timing);
}
@@ -823,6 +970,7 @@
static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
struct mmc_request *mrq, struct mmc_command *cmd)
{
+ struct mmc_host *mmc = mmc_from_priv(host);
/* rawcmd :
* vol_swt << 30 | auto_cmd << 28 | blklen << 16 | go_irq << 15 |
* stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
@@ -849,7 +997,7 @@
struct mmc_data *data = cmd->data;
if (mmc_op_multi(opcode)) {
- if (mmc_card_mmc(host->mmc->card) && mrq->sbc &&
+ if (mmc_card_mmc(mmc->card) && mrq->sbc &&
!(mrq->sbc->arg & 0xFFFF0000))
rawcmd |= 0x2 << 28; /* AutoCMD23 */
}
@@ -917,6 +1065,32 @@
return cmd->error;
}
+/*
+ * msdc_recheck_sdio_irq - recheck whether the SDIO irq is lost
+ *
+ * Host controller may lost interrupt in some special case.
+ * Add SDIO irq recheck mechanism to make sure all interrupts
+ * can be processed immediately
+ */
+static void msdc_recheck_sdio_irq(struct msdc_host *host)
+{
+ struct mmc_host *mmc = mmc_from_priv(host);
+ u32 reg_int, reg_inten, reg_ps;
+
+ if (mmc->caps & MMC_CAP_SDIO_IRQ) {
+ reg_inten = readl(host->base + MSDC_INTEN);
+ if (reg_inten & MSDC_INTEN_SDIOIRQ) {
+ reg_int = readl(host->base + MSDC_INT);
+ reg_ps = readl(host->base + MSDC_PS);
+ if (!(reg_int & MSDC_INT_SDIOIRQ ||
+ reg_ps & MSDC_PS_DATA1)) {
+ __msdc_enable_sdio_irq(host, 0);
+ sdio_signal_irq(mmc);
+ }
+ }
+ }
+}
+
static void msdc_track_cmd_data(struct msdc_host *host,
struct mmc_command *cmd, struct mmc_data *data)
{
@@ -942,7 +1116,11 @@
msdc_track_cmd_data(host, mrq->cmd, mrq->data);
if (mrq->data)
msdc_unprepare_data(host, mrq);
- mmc_request_done(host->mmc, mrq);
+ if (host->error)
+ msdc_reset_hw(host);
+ mmc_request_done(mmc_from_priv(host), mrq);
+ if (host->dev_comp->recheck_sdio_irq)
+ msdc_recheck_sdio_irq(host);
}
/* returns true if command is fully handled; returns false otherwise */
@@ -989,11 +1167,13 @@
}
if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
- if (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
- cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)
+ if (events & MSDC_INT_CMDTMO ||
+ (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
+ cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200))
/*
* should not clear fifo/interrupt as the tune data
- * may have alreay come.
+ * may have alreay come when cmd19/cmd21 gets response
+ * CRC error.
*/
msdc_reset_hw(host);
if (events & MSDC_INT_RSPCRCERR) {
@@ -1054,6 +1234,7 @@
struct mmc_request *mrq, struct mmc_command *cmd)
{
u32 rawcmd;
+ unsigned long flags;
WARN_ON(host->cmd);
host->cmd = cmd;
@@ -1071,7 +1252,10 @@
cmd->error = 0;
rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd);
+ spin_lock_irqsave(&host->lock, flags);
sdr_set_bits(host->base + MSDC_INTEN, cmd_ints_mask);
+ spin_unlock_irqrestore(&host->lock, flags);
+
writel(cmd->arg, host->base + SDC_ARG);
writel(rawcmd, host->base + SDC_CMD);
}
@@ -1234,7 +1418,7 @@
static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct msdc_host *host = mmc_priv(mmc);
- int ret = 0;
+ int ret;
if (!IS_ERR(mmc->supply.vqmmc)) {
if (ios->signal_voltage != MMC_SIGNAL_VOLTAGE_330 &&
@@ -1244,18 +1428,19 @@
}
ret = mmc_regulator_set_vqmmc(mmc, ios);
- if (ret) {
+ if (ret < 0) {
dev_dbg(host->dev, "Regulator set error %d (%d)\n",
ret, ios->signal_voltage);
- } else {
- /* Apply different pinctrl settings for different signal voltage */
- if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
- pinctrl_select_state(host->pinctrl, host->pins_uhs);
- else
- pinctrl_select_state(host->pinctrl, host->pins_default);
+ return ret;
}
+
+ /* Apply different pinctrl settings for different signal voltage */
+ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+ pinctrl_select_state(host->pinctrl, host->pins_uhs);
+ else
+ pinctrl_select_state(host->pinctrl, host->pins_default);
}
- return ret;
+ return 0;
}
static int msdc_card_busy(struct mmc_host *mmc)
@@ -1292,9 +1477,67 @@
}
}
+static void __msdc_enable_sdio_irq(struct msdc_host *host, int enb)
+{
+ if (enb) {
+ sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ);
+ sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
+ if (host->dev_comp->recheck_sdio_irq)
+ msdc_recheck_sdio_irq(host);
+ } else {
+ sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_SDIOIRQ);
+ sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
+ }
+}
+
+static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb)
+{
+ unsigned long flags;
+ struct msdc_host *host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+ __msdc_enable_sdio_irq(host, enb);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (enb)
+ pm_runtime_get_noresume(host->dev);
+ else
+ pm_runtime_put_noidle(host->dev);
+}
+
+static irqreturn_t msdc_cmdq_irq(struct msdc_host *host, u32 intsts)
+{
+ struct mmc_host *mmc = mmc_from_priv(host);
+ int cmd_err = 0, dat_err = 0;
+
+ if (intsts & MSDC_INT_RSPCRCERR) {
+ cmd_err = -EILSEQ;
+ dev_err(host->dev, "%s: CMD CRC ERR", __func__);
+ } else if (intsts & MSDC_INT_CMDTMO) {
+ cmd_err = -ETIMEDOUT;
+ dev_err(host->dev, "%s: CMD TIMEOUT ERR", __func__);
+ }
+
+ if (intsts & MSDC_INT_DATCRCERR) {
+ dat_err = -EILSEQ;
+ dev_err(host->dev, "%s: DATA CRC ERR", __func__);
+ } else if (intsts & MSDC_INT_DATTMO) {
+ dat_err = -ETIMEDOUT;
+ dev_err(host->dev, "%s: DATA TIMEOUT ERR", __func__);
+ }
+
+ if (cmd_err || dat_err) {
+ dev_err(host->dev, "cmd_err = %d, dat_err =%d, intsts = 0x%x",
+ cmd_err, dat_err, intsts);
+ }
+
+ return cqhci_irq(mmc, 0, cmd_err, dat_err);
+}
+
static irqreturn_t msdc_irq(int irq, void *dev_id)
{
struct msdc_host *host = (struct msdc_host *) dev_id;
+ struct mmc_host *mmc = mmc_from_priv(host);
while (true) {
unsigned long flags;
@@ -1306,6 +1549,8 @@
spin_lock_irqsave(&host->lock, flags);
events = readl(host->base + MSDC_INT);
event_mask = readl(host->base + MSDC_INTEN);
+ if ((events & event_mask) & MSDC_INT_SDIOIRQ)
+ __msdc_enable_sdio_irq(host, 0);
/* clear interrupts */
writel(events & event_mask, host->base + MSDC_INT);
@@ -1314,8 +1559,25 @@
data = host->data;
spin_unlock_irqrestore(&host->lock, flags);
- if (!(events & event_mask))
+ if ((events & event_mask) & MSDC_INT_SDIOIRQ)
+ sdio_signal_irq(mmc);
+
+ if ((events & event_mask) & MSDC_INT_CDSC) {
+ if (host->internal_cd)
+ mmc_detect_change(mmc, msecs_to_jiffies(20));
+ events &= ~MSDC_INT_CDSC;
+ }
+
+ if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ)))
break;
+
+ if ((mmc->caps2 & MMC_CAP2_CQE) &&
+ (events & MSDC_INT_CMDQ)) {
+ msdc_cmdq_irq(host, events);
+ /* clear interrupts */
+ writel(events, host->base + MSDC_INT);
+ return IRQ_HANDLED;
+ }
if (!mrq) {
dev_err(host->dev,
@@ -1341,21 +1603,42 @@
u32 val;
u32 tune_reg = host->dev_comp->pad_tune_reg;
+ if (host->reset) {
+ reset_control_assert(host->reset);
+ usleep_range(10, 50);
+ reset_control_deassert(host->reset);
+ }
+
/* Configure to MMC/SD mode, clock free running */
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_MODE | MSDC_CFG_CKPDN);
/* Reset */
msdc_reset_hw(host);
- /* Disable card detection */
- sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN);
-
/* Disable and clear all interrupts */
writel(0, host->base + MSDC_INTEN);
val = readl(host->base + MSDC_INT);
writel(val, host->base + MSDC_INT);
- writel(0, host->base + tune_reg);
+ /* Configure card detection */
+ if (host->internal_cd) {
+ sdr_set_field(host->base + MSDC_PS, MSDC_PS_CDDEBOUNCE,
+ DEFAULT_DEBOUNCE);
+ sdr_set_bits(host->base + MSDC_PS, MSDC_PS_CDEN);
+ sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CDSC);
+ sdr_set_bits(host->base + SDC_CFG, SDC_CFG_INSWKUP);
+ } else {
+ sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_INSWKUP);
+ sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN);
+ sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CDSC);
+ }
+
+ if (host->top_base) {
+ writel(0, host->top_base + EMMC_TOP_CONTROL);
+ writel(0, host->top_base + EMMC_TOP_CMD);
+ } else {
+ writel(0, host->base + tune_reg);
+ }
writel(0, host->base + MSDC_IOCON);
sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
writel(0x403c0046, host->base + MSDC_PATCH_BIT);
@@ -1379,8 +1662,12 @@
sdr_set_field(host->base + MSDC_PATCH_BIT2,
MSDC_PB2_RESPWAIT, 3);
if (host->dev_comp->enhance_rx) {
- sdr_set_bits(host->base + SDC_ADV_CFG0,
- SDC_RX_ENHANCE_EN);
+ 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);
} else {
sdr_set_field(host->base + MSDC_PATCH_BIT2,
MSDC_PB2_RESPSTSENSEL, 2);
@@ -1398,11 +1685,26 @@
sdr_set_bits(host->base + MSDC_PATCH_BIT2,
MSDC_PB2_SUPPORT_64G);
if (host->dev_comp->data_tune) {
- sdr_set_bits(host->base + tune_reg,
- MSDC_PAD_TUNE_RD_SEL | MSDC_PAD_TUNE_CMD_SEL);
+ 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);
+ } else {
+ sdr_set_bits(host->base + tune_reg,
+ MSDC_PAD_TUNE_RD_SEL |
+ MSDC_PAD_TUNE_CMD_SEL);
+ }
} else {
/* choose clock tune */
- sdr_set_bits(host->base + tune_reg, MSDC_PAD_TUNE_RXDLYSEL);
+ if (host->top_base)
+ sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
+ PAD_RXDLY_SEL);
+ else
+ sdr_set_bits(host->base + tune_reg,
+ MSDC_PAD_TUNE_RXDLYSEL);
}
/* Configure to enable SDIO mode.
@@ -1410,22 +1712,41 @@
*/
sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO);
- /* disable detect SDIO device interrupt function */
+ /* Config SDIO device detect interrupt function */
sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
+ sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_DAT1_IRQ_TRIGGER);
/* Configure to default data timeout */
sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3);
host->def_tune_para.iocon = readl(host->base + MSDC_IOCON);
- host->def_tune_para.pad_tune = readl(host->base + tune_reg);
host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
- host->saved_tune_para.pad_tune = readl(host->base + tune_reg);
+ if (host->top_base) {
+ host->def_tune_para.emmc_top_control =
+ readl(host->top_base + EMMC_TOP_CONTROL);
+ host->def_tune_para.emmc_top_cmd =
+ readl(host->top_base + EMMC_TOP_CMD);
+ host->saved_tune_para.emmc_top_control =
+ readl(host->top_base + EMMC_TOP_CONTROL);
+ host->saved_tune_para.emmc_top_cmd =
+ readl(host->top_base + EMMC_TOP_CMD);
+ } else {
+ host->def_tune_para.pad_tune = readl(host->base + tune_reg);
+ host->saved_tune_para.pad_tune = readl(host->base + tune_reg);
+ }
dev_dbg(host->dev, "init hardware done!");
}
static void msdc_deinit_hw(struct msdc_host *host)
{
u32 val;
+
+ if (host->internal_cd) {
+ /* Disabled card-detect */
+ sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN);
+ sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_INSWKUP);
+ }
+
/* Disable and clear all interrupts */
writel(0, host->base + MSDC_INTEN);
@@ -1567,6 +1888,30 @@
return delay_phase;
}
+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)
+ sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY,
+ value);
+ else
+ sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRDLY,
+ value);
+}
+
+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)
+ sdr_set_field(host->top_base + EMMC_TOP_CONTROL,
+ PAD_DAT_RD_RXDLY, value);
+ else
+ sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_DATRRDLY,
+ value);
+}
+
static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
{
struct msdc_host *host = mmc_priv(mmc);
@@ -1587,8 +1932,7 @@
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
for (i = 0 ; i < PAD_DELAY_MAX; i++) {
- sdr_set_field(host->base + tune_reg,
- MSDC_PAD_TUNE_CMDRDLY, i);
+ msdc_set_cmd_delay(host, i);
/*
* Using the same parameters, it may sometimes pass the test,
* but sometimes it may fail. To make sure the parameters are
@@ -1612,8 +1956,7 @@
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
for (i = 0; i < PAD_DELAY_MAX; i++) {
- sdr_set_field(host->base + tune_reg,
- MSDC_PAD_TUNE_CMDRDLY, i);
+ msdc_set_cmd_delay(host, i);
/*
* Using the same parameters, it may sometimes pass the test,
* but sometimes it may fail. To make sure the parameters are
@@ -1637,15 +1980,13 @@
final_maxlen = final_fall_delay.maxlen;
if (final_maxlen == final_rise_delay.maxlen) {
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
- sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRDLY,
- final_rise_delay.final_phase);
final_delay = final_rise_delay.final_phase;
} else {
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
- sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRDLY,
- final_fall_delay.final_phase);
final_delay = final_fall_delay.final_phase;
}
+ msdc_set_cmd_delay(host, final_delay);
+
if (host->dev_comp->async_fifo || host->hs200_cmd_int_delay)
goto skip_internal;
@@ -1721,7 +2062,6 @@
u32 rise_delay = 0, fall_delay = 0;
struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
u8 final_delay, final_maxlen;
- u32 tune_reg = host->dev_comp->pad_tune_reg;
int i, ret;
sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_INT_DAT_LATCH_CK_SEL,
@@ -1729,8 +2069,7 @@
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
for (i = 0 ; i < PAD_DELAY_MAX; i++) {
- sdr_set_field(host->base + tune_reg,
- MSDC_PAD_TUNE_DATRRDLY, i);
+ msdc_set_data_delay(host, i);
ret = mmc_send_tuning(mmc, opcode, NULL);
if (!ret)
rise_delay |= (1 << i);
@@ -1744,8 +2083,7 @@
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
for (i = 0; i < PAD_DELAY_MAX; i++) {
- sdr_set_field(host->base + tune_reg,
- MSDC_PAD_TUNE_DATRRDLY, i);
+ msdc_set_data_delay(host, i);
ret = mmc_send_tuning(mmc, opcode, NULL);
if (!ret)
fall_delay |= (1 << i);
@@ -1757,20 +2095,79 @@
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);
- sdr_set_field(host->base + tune_reg,
- MSDC_PAD_TUNE_DATRRDLY,
- final_rise_delay.final_phase);
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);
- sdr_set_field(host->base + tune_reg,
- MSDC_PAD_TUNE_DATRRDLY,
- final_fall_delay.final_phase);
+ final_delay = final_fall_delay.final_phase;
+ }
+ msdc_set_data_delay(host, final_delay);
+
+ dev_dbg(host->dev, "Final data pad delay: %x\n", final_delay);
+ return final_delay == 0xff ? -EIO : 0;
+}
+
+/*
+ * MSDC IP which supports data tune + async fifo can do CMD/DAT tune
+ * together, which can save the tuning time.
+ */
+static int msdc_tune_together(struct mmc_host *mmc, u32 opcode)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 rise_delay = 0, fall_delay = 0;
+ struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
+ u8 final_delay, final_maxlen;
+ int i, ret;
+
+ 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_RSPL);
+ sdr_clr_bits(host->base + MSDC_IOCON,
+ MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ for (i = 0 ; i < PAD_DELAY_MAX; i++) {
+ msdc_set_cmd_delay(host, i);
+ msdc_set_data_delay(host, i);
+ ret = mmc_send_tuning(mmc, opcode, NULL);
+ if (!ret)
+ rise_delay |= (1 << i);
+ }
+ final_rise_delay = get_best_delay(host, rise_delay);
+ /* if rising edge has enough margin, then do not scan falling edge */
+ if (final_rise_delay.maxlen >= 12 ||
+ (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
+ 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);
+ for (i = 0; i < PAD_DELAY_MAX; i++) {
+ msdc_set_cmd_delay(host, i);
+ msdc_set_data_delay(host, i);
+ ret = mmc_send_tuning(mmc, opcode, NULL);
+ if (!ret)
+ fall_delay |= (1 << i);
+ }
+ final_fall_delay = get_best_delay(host, fall_delay);
+
+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);
+ 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);
final_delay = final_fall_delay.final_phase;
}
- dev_dbg(host->dev, "Final data pad delay: %x\n", final_delay);
+ msdc_set_cmd_delay(host, final_delay);
+ msdc_set_data_delay(host, final_delay);
+
+ dev_dbg(host->dev, "Final pad delay: %x\n", final_delay);
return final_delay == 0xff ? -EIO : 0;
}
@@ -1780,6 +2177,15 @@
int ret;
u32 tune_reg = host->dev_comp->pad_tune_reg;
+ 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_delay(host, 0);
+ }
+ goto tune_done;
+ }
if (host->hs400_mode &&
host->dev_comp->hs400_tune)
ret = hs400_tune_response(mmc, opcode);
@@ -1795,9 +2201,16 @@
dev_err(host->dev, "Tune data fail!\n");
}
+tune_done:
host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
host->saved_tune_para.pad_tune = readl(host->base + tune_reg);
host->saved_tune_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
+ if (host->top_base) {
+ host->saved_tune_para.emmc_top_control = readl(host->top_base +
+ EMMC_TOP_CONTROL);
+ host->saved_tune_para.emmc_top_cmd = readl(host->top_base +
+ EMMC_TOP_CMD);
+ }
return ret;
}
@@ -1806,7 +2219,11 @@
struct msdc_host *host = mmc_priv(mmc);
host->hs400_mode = true;
- writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
+ 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);
/* 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 */
@@ -1824,18 +2241,112 @@
sdr_clr_bits(host->base + EMMC_IOCON, 1);
}
+static void msdc_ack_sdio_irq(struct mmc_host *mmc)
+{
+ unsigned long flags;
+ struct msdc_host *host = mmc_priv(mmc);
+
+ spin_lock_irqsave(&host->lock, flags);
+ __msdc_enable_sdio_irq(host, 1);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static int msdc_get_cd(struct mmc_host *mmc)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ int val;
+
+ if (mmc->caps & MMC_CAP_NONREMOVABLE)
+ return 1;
+
+ if (!host->internal_cd)
+ return mmc_gpio_get_cd(mmc);
+
+ val = readl(host->base + MSDC_PS) & MSDC_PS_CDSTS;
+ if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)
+ return !!val;
+ else
+ return !val;
+}
+
+static void msdc_cqe_enable(struct mmc_host *mmc)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+
+ /* enable cmdq irq */
+ writel(MSDC_INT_CMDQ, host->base + MSDC_INTEN);
+ /* enable busy check */
+ sdr_set_bits(host->base + MSDC_PATCH_BIT1, MSDC_PB1_BUSY_CHECK_SEL);
+ /* default write data / busy timeout 20s */
+ msdc_set_busy_timeout(host, 20 * 1000000000ULL, 0);
+ /* default read data timeout 1s */
+ msdc_set_timeout(host, 1000000000ULL, 0);
+}
+
+static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ unsigned int val = 0;
+
+ /* disable cmdq irq */
+ sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INT_CMDQ);
+ /* disable busy check */
+ sdr_clr_bits(host->base + MSDC_PATCH_BIT1, MSDC_PB1_BUSY_CHECK_SEL);
+
+ val = readl(host->base + MSDC_INT);
+ writel(val, host->base + MSDC_INT);
+
+ if (recovery) {
+ sdr_set_field(host->base + MSDC_DMA_CTRL,
+ MSDC_DMA_CTRL_STOP, 1);
+ if (WARN_ON(readl_poll_timeout(host->base + MSDC_DMA_CFG, val,
+ !(val & MSDC_DMA_CFG_STS), 1, 3000)))
+ return;
+ msdc_reset_hw(host);
+ }
+}
+
+static void msdc_cqe_pre_enable(struct mmc_host *mmc)
+{
+ struct cqhci_host *cq_host = mmc->cqe_private;
+ u32 reg;
+
+ reg = cqhci_readl(cq_host, CQHCI_CFG);
+ reg |= CQHCI_ENABLE;
+ cqhci_writel(cq_host, reg, CQHCI_CFG);
+}
+
+static void msdc_cqe_post_disable(struct mmc_host *mmc)
+{
+ struct cqhci_host *cq_host = mmc->cqe_private;
+ u32 reg;
+
+ reg = cqhci_readl(cq_host, CQHCI_CFG);
+ reg &= ~CQHCI_ENABLE;
+ cqhci_writel(cq_host, reg, CQHCI_CFG);
+}
+
static const struct mmc_host_ops mt_msdc_ops = {
.post_req = msdc_post_req,
.pre_req = msdc_pre_req,
.request = msdc_ops_request,
.set_ios = msdc_ops_set_ios,
.get_ro = mmc_gpio_get_ro,
- .get_cd = mmc_gpio_get_cd,
+ .get_cd = msdc_get_cd,
+ .enable_sdio_irq = msdc_enable_sdio_irq,
+ .ack_sdio_irq = msdc_ack_sdio_irq,
.start_signal_voltage_switch = msdc_ops_switch_volt,
.card_busy = msdc_card_busy,
.execute_tuning = msdc_execute_tuning,
.prepare_hs400_tuning = msdc_prepare_hs400_tuning,
.hw_reset = msdc_hw_reset,
+};
+
+static const struct cqhci_host_ops msdc_cmdq_ops = {
+ .enable = msdc_cqe_enable,
+ .disable = msdc_cqe_disable,
+ .pre_enable = msdc_cqe_pre_enable,
+ .post_disable = msdc_cqe_post_disable,
};
static void msdc_of_property_parse(struct platform_device *pdev,
@@ -1858,6 +2369,12 @@
host->hs400_cmd_resp_sel_rising = true;
else
host->hs400_cmd_resp_sel_rising = false;
+
+ if (of_property_read_bool(pdev->dev.of_node,
+ "supports-cqe"))
+ host->cqhci = true;
+ else
+ host->cqhci = false;
}
static int msdc_drv_probe(struct platform_device *pdev)
@@ -1882,11 +2399,17 @@
if (ret)
goto host_free;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- host->base = devm_ioremap_resource(&pdev->dev, res);
+ host->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(host->base)) {
ret = PTR_ERR(host->base);
goto host_free;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (res) {
+ host->top_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->top_base))
+ host->top_base = NULL;
}
ret = mmc_regulator_get_supply(mmc);
@@ -1905,10 +2428,18 @@
goto host_free;
}
+ host->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
+ if (IS_ERR(host->bus_clk))
+ host->bus_clk = NULL;
/*source clock control gate is optional clock*/
host->src_clk_cg = devm_clk_get(&pdev->dev, "source_cg");
if (IS_ERR(host->src_clk_cg))
host->src_clk_cg = NULL;
+
+ host->reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
+ "hrst");
+ if (IS_ERR(host->reset))
+ return PTR_ERR(host->reset);
host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0) {
@@ -1941,7 +2472,6 @@
host->dev = &pdev->dev;
host->dev_comp = of_device_get_match_data(&pdev->dev);
- host->mmc = mmc;
host->src_clk_freq = clk_get_rate(host->src_clk);
/* Set host parameters to mmc */
mmc->ops = &mt_msdc_ops;
@@ -1950,10 +2480,28 @@
else
mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095);
- mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
+ if (!(mmc->caps & MMC_CAP_NONREMOVABLE) &&
+ !mmc_can_gpio_cd(mmc) &&
+ host->dev_comp->use_internal_cd) {
+ /*
+ * Is removable but no GPIO declared, so
+ * use internal functionality.
+ */
+ host->internal_cd = true;
+ }
+
+ if (mmc->caps & MMC_CAP_SDIO_IRQ)
+ mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
+
+ mmc->caps |= MMC_CAP_CMD23;
+ if (host->cqhci)
+ mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
/* MMC core transfer sizes tunable parameters */
mmc->max_segs = MAX_BD_NUM;
- mmc->max_seg_size = BDMA_DESC_BUFLEN;
+ if (host->dev_comp->support_64g)
+ mmc->max_seg_size = BDMA_DESC_BUFLEN_EXT;
+ else
+ mmc->max_seg_size = BDMA_DESC_BUFLEN;
mmc->max_blk_size = 2048;
mmc->max_req_size = 512 * 1024;
mmc->max_blk_count = mmc->max_req_size / 512;
@@ -1982,8 +2530,28 @@
msdc_ungate_clock(host);
msdc_init_hw(host);
+ if (mmc->caps2 & MMC_CAP2_CQE) {
+ host->cq_host = devm_kzalloc(mmc->parent,
+ sizeof(*host->cq_host),
+ GFP_KERNEL);
+ if (!host->cq_host) {
+ ret = -ENOMEM;
+ goto host_free;
+ }
+ 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;
+ 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;
+ }
+
ret = devm_request_irq(&pdev->dev, host->irq, msdc_irq,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT, pdev->name, host);
+ IRQF_TRIGGER_NONE, pdev->name, host);
if (ret)
goto release;
@@ -2029,7 +2597,7 @@
pm_runtime_get_sync(host->dev);
platform_set_drvdata(pdev, NULL);
- mmc_remove_host(host->mmc);
+ mmc_remove_host(mmc);
msdc_deinit_hw(host);
msdc_gate_clock(host);
@@ -2041,12 +2609,11 @@
dma_free_coherent(&pdev->dev, MAX_BD_NUM * sizeof(struct mt_bdma_desc),
host->dma.bd, host->dma.bd_addr);
- mmc_free_host(host->mmc);
+ mmc_free_host(mmc);
return 0;
}
-#ifdef CONFIG_PM
static void msdc_save_reg(struct msdc_host *host)
{
u32 tune_reg = host->dev_comp->pad_tune_reg;
@@ -2054,7 +2621,6 @@
host->save_para.msdc_cfg = readl(host->base + MSDC_CFG);
host->save_para.iocon = readl(host->base + MSDC_IOCON);
host->save_para.sdc_cfg = readl(host->base + SDC_CFG);
- host->save_para.pad_tune = readl(host->base + tune_reg);
host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
host->save_para.patch_bit2 = readl(host->base + MSDC_PATCH_BIT2);
@@ -2063,16 +2629,26 @@
host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
host->save_para.emmc50_cfg3 = readl(host->base + EMMC50_CFG3);
host->save_para.sdc_fifo_cfg = readl(host->base + SDC_FIFO_CFG);
+ if (host->top_base) {
+ host->save_para.emmc_top_control =
+ readl(host->top_base + EMMC_TOP_CONTROL);
+ host->save_para.emmc_top_cmd =
+ readl(host->top_base + EMMC_TOP_CMD);
+ host->save_para.emmc50_pad_ds_tune =
+ readl(host->top_base + EMMC50_PAD_DS_TUNE);
+ } else {
+ host->save_para.pad_tune = readl(host->base + tune_reg);
+ }
}
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;
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);
- writel(host->save_para.pad_tune, host->base + tune_reg);
writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
writel(host->save_para.patch_bit2, host->base + MSDC_PATCH_BIT2);
@@ -2081,9 +2657,22 @@
writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0);
writel(host->save_para.emmc50_cfg3, host->base + EMMC50_CFG3);
writel(host->save_para.sdc_fifo_cfg, host->base + SDC_FIFO_CFG);
+ if (host->top_base) {
+ writel(host->save_para.emmc_top_control,
+ host->top_base + EMMC_TOP_CONTROL);
+ writel(host->save_para.emmc_top_cmd,
+ host->top_base + EMMC_TOP_CMD);
+ writel(host->save_para.emmc50_pad_ds_tune,
+ host->top_base + EMMC50_PAD_DS_TUNE);
+ } else {
+ writel(host->save_para.pad_tune, host->base + tune_reg);
+ }
+
+ if (sdio_irq_claimed(mmc))
+ __msdc_enable_sdio_irq(host, 1);
}
-static int msdc_runtime_suspend(struct device *dev)
+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);
@@ -2093,7 +2682,7 @@
return 0;
}
-static int msdc_runtime_resume(struct device *dev)
+static int __maybe_unused msdc_runtime_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct msdc_host *host = mmc_priv(mmc);
@@ -2102,11 +2691,31 @@
msdc_restore_reg(host);
return 0;
}
-#endif
+
+static int __maybe_unused msdc_suspend(struct device *dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ int ret;
+ u32 val;
+
+ if (mmc->caps2 & MMC_CAP2_CQE) {
+ ret = cqhci_suspend(mmc);
+ if (ret)
+ return ret;
+ val = readl(((struct msdc_host *)mmc_priv(mmc))->base + MSDC_INT);
+ writel(val, ((struct msdc_host *)mmc_priv(mmc))->base + MSDC_INT);
+ }
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int __maybe_unused msdc_resume(struct device *dev)
+{
+ return pm_runtime_force_resume(dev);
+}
static const struct dev_pm_ops msdc_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(msdc_suspend, msdc_resume)
SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL)
};
@@ -2115,6 +2724,7 @@
.remove = msdc_drv_remove,
.driver = {
.name = "mtk-msdc",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = msdc_of_ids,
.pm = &msdc_dev_pm_ops,
},
--
Gitblit v1.6.2