From: Ryder Lee <ryder.lee@mediatek.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v2 16/18] mmc: mtk-sd: add SD/MMC host controller driver for MT7623 SoC
Date: Fri, 12 Oct 2018 15:01:05 +0800 [thread overview]
Message-ID: <8c1a855adb49d8f03d89e8762ab4d7df82cc1ab9.1539326908.git.ryder.lee@mediatek.com> (raw)
In-Reply-To: <cover.1539326908.git.ryder.lee@mediatek.com>
From: Weijie Gao <weijie.gao@mediatek.com>
This patch adds MT7623 host controller driver for accessing SD/MMC.
Cc: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Weijie Gao <weijie.gao@mediatek.com>
Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
Tested-by: Matthias Brugger <matthias.bgg@gmail.com>
---
drivers/mmc/Kconfig | 9 +
drivers/mmc/Makefile | 1 +
drivers/mmc/mtk-sd.c | 1331 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1341 insertions(+)
create mode 100644 drivers/mmc/mtk-sd.c
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 0a0d4aa..ca13341 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -598,6 +598,15 @@ config FTSDC010_SDIO
help
This can enable ftsdc010 sdio function.
+config MMC_MTK
+ bool "MediaTek SD/MMC Card Interface support"
+ default n
+ help
+ This selects the MediaTek(R) Secure digital and Multimedia card Interface.
+ If you have a machine with a integrated SD/MMC card reader, say Y or M here.
+ This is needed if support for any SD/SDIO/MMC devices is required.
+ If unsure, say N.
+
endif
config TEGRA124_MMC_DISABLE_EXT_LOOPBACK
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 23c5b0d..801a26d 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -65,3 +65,4 @@ obj-$(CONFIG_MMC_SUNXI) += sunxi_mmc.o
obj-$(CONFIG_MMC_UNIPHIER) += tmio-common.o uniphier-sd.o
obj-$(CONFIG_RENESAS_SDHI) += tmio-common.o renesas-sdhi.o
obj-$(CONFIG_MMC_BCM2835) += bcm2835_sdhost.o
+obj-$(CONFIG_MMC_MTK) += mtk-sd.o
diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c
new file mode 100644
index 0000000..5027764
--- /dev/null
+++ b/drivers/mmc/mtk-sd.c
@@ -0,0 +1,1331 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek SD/MMC Card Interface driver
+ *
+ * Copyright (C) 2018 MediaTek Inc.
+ * Author: Weijie Gao <weijie.gao@mediatek.com>
+ */
+
+#include <clk.h>
+#include <common.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <asm/gpio.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#define MSDC_CFG 0x0
+#define MSDC_CFG_HS400_CK_MODE_EXT BIT(22)
+#define MSDC_CFG_CKMOD_EXT_M 0x03
+#define MSDC_CFG_CKMOD_EXT_S 20
+#define MSDC_CFG_CKDIV_EXT_M 0xfff
+#define MSDC_CFG_CKDIV_EXT_S 8
+#define MSDC_CFG_HS400_CK_MODE BIT(18)
+#define MSDC_CFG_CKMOD_M 0x03
+#define MSDC_CFG_CKMOD_S 16
+#define MSDC_CFG_CKDIV_M 0xff
+#define MSDC_CFG_CKDIV_S 8
+#define MSDC_CFG_CKSTB BIT(7)
+#define MSDC_CFG_PIO BIT(3)
+#define MSDC_CFG_RST BIT(2)
+#define MSDC_CFG_CKPDN BIT(1)
+#define MSDC_CFG_MODE BIT(0)
+
+#define MSDC_IOCON 0x04
+#define MSDC_IOCON_W_DSPL BIT(8)
+#define MSDC_IOCON_DSPL BIT(2)
+#define MSDC_IOCON_RSPL BIT(1)
+
+#define MSDC_PS 0x08
+#define MSDC_PS_DAT0 BIT(16)
+#define MSDC_PS_CDEN BIT(0)
+
+#define MSDC_INT 0x0c
+#define MSDC_INTEN 0x10
+#define MSDC_INT_ACMDRDY BIT(3)
+#define MSDC_INT_ACMDTMO BIT(4)
+#define MSDC_INT_ACMDCRCERR BIT(5)
+#define MSDC_INT_CMDRDY BIT(8)
+#define MSDC_INT_CMDTMO BIT(9)
+#define MSDC_INT_RSPCRCERR BIT(10)
+#define MSDC_INT_XFER_COMPL BIT(12)
+#define MSDC_INT_DATTMO BIT(14)
+#define MSDC_INT_DATCRCERR BIT(15)
+
+#define MSDC_FIFOCS 0x14
+#define MSDC_FIFOCS_CLR BIT(31)
+#define MSDC_FIFOCS_TXCNT_M 0xff
+#define MSDC_FIFOCS_TXCNT_S 16
+#define MSDC_FIFOCS_RXCNT_M 0xff
+#define MSDC_FIFOCS_RXCNT_S 0
+
+#define MSDC_TXDATA 0x18
+#define MSDC_RXDATA 0x1c
+
+#define SDC_CFG 0x30
+#define SDC_CFG_DTOC_M 0xff
+#define SDC_CFG_DTOC_S 24
+#define SDC_CFG_SDIOIDE BIT(20)
+#define SDC_CFG_SDIO BIT(19)
+#define SDC_CFG_BUSWIDTH_M 0x03
+#define SDC_CFG_BUSWIDTH_S 16
+
+#define SDC_CMD 0x34
+#define SDC_CMD_BLK_LEN_M 0xfff
+#define SDC_CMD_BLK_LEN_S 16
+#define SDC_CMD_STOP BIT(14)
+#define SDC_CMD_WR BIT(13)
+#define SDC_CMD_DTYPE_M 0x03
+#define SDC_CMD_DTYPE_S 11
+#define SDC_CMD_RSPTYP_M 0x07
+#define SDC_CMD_RSPTYP_S 7
+#define SDC_CMD_CMD_M 0x3f
+#define SDC_CMD_CMD_S 0
+
+#define SDC_ARG 0x38
+
+#define SDC_STS 0x3c
+#define SDC_STS_CMDBUSY BIT(1)
+#define SDC_STS_SDCBUSY BIT(0)
+
+#define SDC_RESP0 0x40
+#define SDC_RESP1 0x44
+#define SDC_RESP2 0x48
+#define SDC_RESP3 0x4c
+
+#define SDC_BLK_NUM 0x50
+
+#define SDC_ADV_CFG0 0x64
+#define SDC_RX_ENHANCE_EN BIT(20)
+
+#define MSDC_PATCH_BIT 0xb0
+#define MSDC_INT_DAT_LATCH_CK_SEL_M 0x07
+#define MSDC_INT_DAT_LATCH_CK_SEL_S 7
+
+#define MSDC_PATCH_BIT1 0xb4
+#define MSDC_PB1_STOP_DLY_M 0x0f
+#define MSDC_PB1_STOP_DLY_S 8
+
+#define MSDC_PATCH_BIT2 0xb8
+#define MSDC_PB2_CRCSTSENSEL_M 0x07
+#define MSDC_PB2_CRCSTSENSEL_S 29
+#define MSDC_PB2_CFGCRCSTS BIT(28)
+#define MSDC_PB2_RESPSTSENSEL_M 0x07
+#define MSDC_PB2_RESPSTSENSEL_S 16
+#define MSDC_PB2_CFGRESP BIT(15)
+#define MSDC_PB2_RESPWAIT_M 0x03
+#define MSDC_PB2_RESPWAIT_S 2
+
+#define MSDC_PAD_TUNE 0xec
+#define MSDC_PAD_TUNE_CMDRRDLY_M 0x1f
+#define MSDC_PAD_TUNE_CMDRRDLY_S 22
+#define MSDC_PAD_TUNE_CMD_SEL BIT(21)
+#define MSDC_PAD_TUNE_CMDRDLY_M 0x1f
+#define MSDC_PAD_TUNE_CMDRDLY_S 16
+#define MSDC_PAD_TUNE_RXDLYSEL BIT(15)
+#define MSDC_PAD_TUNE_RD_SEL BIT(13)
+#define MSDC_PAD_TUNE_DATRRDLY_M 0x1f
+#define MSDC_PAD_TUNE_DATRRDLY_S 8
+#define MSDC_PAD_TUNE_DATWRDLY_M 0x1f
+#define MSDC_PAD_TUNE_DATWRDLY_S 0
+
+#define MSDC_PAD_TUNE0 0xf0
+
+#define PAD_DS_TUNE 0x188
+
+#define EMMC50_CFG0 0x208
+#define EMMC50_CFG_CFCSTS_SEL BIT(4)
+
+#define SDC_FIFO_CFG 0x228
+#define SDC_FIFO_CFG_WRVALIDSEL BIT(24)
+#define SDC_FIFO_CFG_RDVALIDSEL BIT(25)
+
+/* SDC_CFG_BUSWIDTH */
+#define MSDC_BUS_1BITS 0x0
+#define MSDC_BUS_4BITS 0x1
+#define MSDC_BUS_8BITS 0x2
+
+#define MSDC_FIFO_SIZE 128
+
+#define PAD_DELAY_MAX 32
+
+#define CMD_INTS_MASK \
+ (MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO)
+
+#define DATA_INTS_MASK \
+ (MSDC_INT_XFER_COMPL | MSDC_INT_DATTMO | MSDC_INT_DATCRCERR)
+
+struct msdc_compatible {
+ u8 clk_div_bits;
+ bool pad_tune0;
+ bool async_fifo;
+ bool data_tune;
+ bool busy_check;
+ bool stop_clk_fix;
+ bool enhance_rx;
+};
+
+struct msdc_delay_phase {
+ u8 maxlen;
+ u8 start;
+ u8 final_phase;
+};
+
+struct msdc_plat {
+ struct mmc_config cfg;
+ struct mmc mmc;
+};
+
+struct msdc_tune_para {
+ u32 iocon;
+ u32 pad_tune;
+};
+
+struct msdc_host {
+ void __iomem *base;
+ struct mmc *mmc;
+
+ struct msdc_compatible *dev_comp;
+
+ struct clk src_clk;
+ struct clk h_clk;
+
+ u32 mclk;
+ u32 src_clk_freq;
+ u32 sclk;
+
+ u32 timeout_ns;
+ u32 timeout_clks;
+
+ u32 hs400_ds_delay;
+ u32 hs200_cmd_int_delay;
+ u32 hs200_write_int_delay;
+ u32 latch_ck;
+ u32 r_smpl;
+ bool hs400_mode;
+
+ struct gpio_desc gpio_wp;
+ struct gpio_desc gpio_cd;
+
+ uint last_resp_type;
+ uint last_data_write;
+
+ enum bus_mode timing;
+
+ struct msdc_tune_para def_tune_para;
+ struct msdc_tune_para saved_tune_para;
+};
+
+static void msdc_reset_hw(struct msdc_host *host)
+{
+ u32 reg;
+
+ setbits_le32(host->base + MSDC_CFG, MSDC_CFG_RST);
+
+ readl_poll_timeout(host->base + MSDC_CFG, reg,
+ !(reg & MSDC_CFG_RST), 1000000);
+}
+
+static void msdc_fifo_clr(struct msdc_host *host)
+{
+ u32 reg;
+
+ setbits_le32(host->base + MSDC_FIFOCS, MSDC_FIFOCS_CLR);
+
+ readl_poll_timeout(host->base + MSDC_FIFOCS, reg,
+ !(reg & MSDC_FIFOCS_CLR), 1000000);
+}
+
+static u32 msdc_fifo_rx_bytes(struct msdc_host *host)
+{
+ return (readl(host->base + MSDC_FIFOCS) >> MSDC_FIFOCS_RXCNT_S) &
+ MSDC_FIFOCS_RXCNT_M;
+}
+
+static u32 msdc_fifo_tx_bytes(struct msdc_host *host)
+{
+ return (readl(host->base + MSDC_FIFOCS) >> MSDC_FIFOCS_TXCNT_S) &
+ MSDC_FIFOCS_TXCNT_M;
+}
+
+static u32 msdc_cmd_find_resp(struct msdc_host *host, struct mmc_cmd *cmd)
+{
+ u32 resp;
+
+ switch (cmd->resp_type) {
+ /* Actually, R1, R5, R6, R7 are the same */
+ case MMC_RSP_R1:
+ resp = 0x1;
+ break;
+ case MMC_RSP_R1b:
+ resp = 0x7;
+ break;
+ case MMC_RSP_R2:
+ resp = 0x2;
+ break;
+ case MMC_RSP_R3:
+ resp = 0x3;
+ break;
+ case MMC_RSP_NONE:
+ default:
+ resp = 0x0;
+ break;
+ }
+
+ return resp;
+}
+
+static u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
+ struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ u32 opcode = cmd->cmdidx;
+ u32 resp_type = msdc_cmd_find_resp(host, cmd);
+ uint blocksize = 0;
+ u32 dtype = 0;
+ u32 rawcmd = 0;
+
+ switch (opcode) {
+ case MMC_CMD_WRITE_MULTIPLE_BLOCK:
+ case MMC_CMD_READ_MULTIPLE_BLOCK:
+ dtype = 2;
+ break;
+ case MMC_CMD_WRITE_SINGLE_BLOCK:
+ case MMC_CMD_READ_SINGLE_BLOCK:
+ case SD_CMD_APP_SEND_SCR:
+ dtype = 1;
+ break;
+ case SD_CMD_SWITCH_FUNC: /* same as MMC_CMD_SWITCH */
+ case SD_CMD_SEND_IF_COND: /* same as MMC_CMD_SEND_EXT_CSD */
+ case SD_CMD_APP_SD_STATUS: /* same as MMC_CMD_SEND_STATUS */
+ if (data)
+ dtype = 1;
+ }
+
+ if (data) {
+ if (data->flags == MMC_DATA_WRITE)
+ rawcmd |= SDC_CMD_WR;
+
+ if (data->blocks > 1)
+ dtype = 2;
+
+ blocksize = data->blocksize;
+ }
+
+ rawcmd |= ((opcode & SDC_CMD_CMD_M) << SDC_CMD_CMD_S) |
+ ((resp_type & SDC_CMD_RSPTYP_M) << SDC_CMD_RSPTYP_S) |
+ ((blocksize & SDC_CMD_BLK_LEN_M) << SDC_CMD_BLK_LEN_S) |
+ ((dtype & SDC_CMD_DTYPE_M) << SDC_CMD_DTYPE_S);
+
+ if (opcode == MMC_CMD_STOP_TRANSMISSION)
+ rawcmd |= SDC_CMD_STOP;
+
+ return rawcmd;
+}
+
+static int msdc_cmd_done(struct msdc_host *host, int events,
+ struct mmc_cmd *cmd)
+{
+ u32 *rsp = cmd->response;
+ int ret = 0;
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ if (cmd->resp_type & MMC_RSP_136) {
+ rsp[0] = readl(host->base + SDC_RESP3);
+ rsp[1] = readl(host->base + SDC_RESP2);
+ rsp[2] = readl(host->base + SDC_RESP1);
+ rsp[3] = readl(host->base + SDC_RESP0);
+ } else {
+ rsp[0] = readl(host->base + SDC_RESP0);
+ }
+ }
+
+ if (!(events & MSDC_INT_CMDRDY)) {
+ if (cmd->cmdidx != MMC_CMD_SEND_TUNING_BLOCK &&
+ cmd->cmdidx != MMC_CMD_SEND_TUNING_BLOCK_HS200)
+ /*
+ * should not clear fifo/interrupt as the tune data
+ * may have alreay come.
+ */
+ msdc_reset_hw(host);
+
+ if (events & MSDC_INT_CMDTMO)
+ ret = -ETIMEDOUT;
+ else
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static bool msdc_cmd_is_ready(struct msdc_host *host)
+{
+ int ret;
+ u32 reg;
+
+ /* The max busy time we can endure is 20ms */
+ ret = readl_poll_timeout(host->base + SDC_STS, reg,
+ !(reg & SDC_STS_CMDBUSY), 20000);
+
+ if (ret) {
+ pr_err("CMD bus busy detected\n");
+ msdc_reset_hw(host);
+ return false;
+ }
+
+ if (host->last_resp_type == MMC_RSP_R1b && host->last_data_write) {
+ ret = readl_poll_timeout(host->base + MSDC_PS, reg,
+ reg & MSDC_PS_DAT0, 1000000);
+
+ if (ret) {
+ pr_err("Card stuck in programming state!\n");
+ msdc_reset_hw(host);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int msdc_start_command(struct msdc_host *host, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ u32 rawcmd;
+ u32 status;
+ u32 blocks = 0;
+ int ret;
+
+ if (!msdc_cmd_is_ready(host))
+ return -EIO;
+
+ msdc_fifo_clr(host);
+
+ host->last_resp_type = cmd->resp_type;
+ host->last_data_write = 0;
+
+ rawcmd = msdc_cmd_prepare_raw_cmd(host, cmd, data);
+
+ if (data)
+ blocks = data->blocks;
+
+ writel(CMD_INTS_MASK, host->base + MSDC_INT);
+ writel(blocks, host->base + SDC_BLK_NUM);
+ writel(cmd->cmdarg, host->base + SDC_ARG);
+ writel(rawcmd, host->base + SDC_CMD);
+
+ ret = readl_poll_timeout(host->base + MSDC_INT, status,
+ status & CMD_INTS_MASK, 1000000);
+
+ if (ret)
+ status = MSDC_INT_CMDTMO;
+
+ return msdc_cmd_done(host, status, cmd);
+}
+
+static void msdc_fifo_read(struct msdc_host *host, u8 *buf, u32 size)
+{
+ u32 *wbuf;
+
+ while ((size_t)buf % 4) {
+ *buf++ = readb(host->base + MSDC_RXDATA);
+ size--;
+ }
+
+ wbuf = (u32 *)buf;
+ while (size >= 4) {
+ *wbuf++ = readl(host->base + MSDC_RXDATA);
+ size -= 4;
+ }
+
+ buf = (u8 *)wbuf;
+ while (size) {
+ *buf++ = readb(host->base + MSDC_RXDATA);
+ size--;
+ }
+}
+
+static void msdc_fifo_write(struct msdc_host *host, const u8 *buf, u32 size)
+{
+ const u32 *wbuf;
+
+ while ((size_t)buf % 4) {
+ writeb(*buf++, host->base + MSDC_TXDATA);
+ size--;
+ }
+
+ wbuf = (const u32 *)buf;
+ while (size >= 4) {
+ writel(*wbuf++, host->base + MSDC_TXDATA);
+ size -= 4;
+ }
+
+ buf = (const u8 *)wbuf;
+ while (size) {
+ writeb(*buf++, host->base + MSDC_TXDATA);
+ size--;
+ }
+}
+
+static int msdc_pio_read(struct msdc_host *host, u8 *ptr, u32 size)
+{
+ u32 status;
+ u32 chksz;
+ int ret = 0;
+
+ while (1) {
+ status = readl(host->base + MSDC_INT);
+ writel(status, host->base + MSDC_INT);
+ status &= DATA_INTS_MASK;
+
+ if (status & MSDC_INT_DATCRCERR) {
+ ret = -EIO;
+ break;
+ }
+
+ if (status & MSDC_INT_DATTMO) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ if (status & MSDC_INT_XFER_COMPL) {
+ if (size) {
+ pr_err("data not fully read\n");
+ ret = -EIO;
+ }
+
+ break;
+ }
+
+ chksz = min(size, (u32)MSDC_FIFO_SIZE);
+
+ if (msdc_fifo_rx_bytes(host) >= chksz) {
+ msdc_fifo_read(host, ptr, chksz);
+ ptr += chksz;
+ size -= chksz;
+ }
+ }
+
+ return ret;
+}
+
+static int msdc_pio_write(struct msdc_host *host, const u8 *ptr, u32 size)
+{
+ u32 status;
+ u32 chksz;
+ int ret = 0;
+
+ while (1) {
+ status = readl(host->base + MSDC_INT);
+ writel(status, host->base + MSDC_INT);
+ status &= DATA_INTS_MASK;
+
+ if (status & MSDC_INT_DATCRCERR) {
+ ret = -EIO;
+ break;
+ }
+
+ if (status & MSDC_INT_DATTMO) {
+ ret = -ETIMEDOUT;
+ break;
+ }
+
+ if (status & MSDC_INT_XFER_COMPL) {
+ if (size) {
+ pr_err("data not fully written\n");
+ ret = -EIO;
+ }
+
+ break;
+ }
+
+ chksz = min(size, (u32)MSDC_FIFO_SIZE);
+
+ if (MSDC_FIFO_SIZE - msdc_fifo_tx_bytes(host) >= chksz) {
+ msdc_fifo_write(host, ptr, chksz);
+ ptr += chksz;
+ size -= chksz;
+ }
+ }
+
+ return ret;
+}
+
+static int msdc_start_data(struct msdc_host *host, struct mmc_data *data)
+{
+ u32 size;
+ int ret;
+
+ if (data->flags == MMC_DATA_WRITE)
+ host->last_data_write = 1;
+
+ writel(DATA_INTS_MASK, host->base + MSDC_INT);
+
+ size = data->blocks * data->blocksize;
+
+ if (data->flags == MMC_DATA_WRITE)
+ ret = msdc_pio_write(host, (const u8 *)data->src, size);
+ else
+ ret = msdc_pio_read(host, (u8 *)data->dest, size);
+
+ if (ret) {
+ msdc_reset_hw(host);
+ msdc_fifo_clr(host);
+ }
+
+ return ret;
+}
+
+static int msdc_ops_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ struct msdc_host *host = dev_get_priv(dev);
+ int ret;
+
+ ret = msdc_start_command(host, cmd, data);
+ if (ret)
+ return ret;
+
+ if (data)
+ return msdc_start_data(host, data);
+
+ return 0;
+}
+
+static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
+{
+ u32 timeout, clk_ns;
+ u32 mode = 0;
+
+ host->timeout_ns = ns;
+ host->timeout_clks = clks;
+
+ if (host->sclk == 0) {
+ timeout = 0;
+ } else {
+ clk_ns = 1000000000UL / host->sclk;
+ timeout = (ns + clk_ns - 1) / clk_ns + clks;
+ /* unit is 1048576 sclk cycles */
+ timeout = (timeout + (0x1 << 20) - 1) >> 20;
+ if (host->dev_comp->clk_div_bits == 8)
+ mode = (readl(host->base + MSDC_CFG) >>
+ MSDC_CFG_CKMOD_S) & MSDC_CFG_CKMOD_M;
+ else
+ mode = (readl(host->base + MSDC_CFG) >>
+ MSDC_CFG_CKMOD_EXT_S) & MSDC_CFG_CKMOD_EXT_M;
+ /* 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;
+ }
+
+ clrsetbits_le32(host->base + SDC_CFG, SDC_CFG_DTOC_M << SDC_CFG_DTOC_S,
+ timeout << SDC_CFG_DTOC_S);
+}
+
+static void msdc_set_buswidth(struct msdc_host *host, u32 width)
+{
+ u32 val = readl(host->base + SDC_CFG);
+
+ val &= ~(SDC_CFG_BUSWIDTH_M << SDC_CFG_BUSWIDTH_S);
+
+ switch (width) {
+ default:
+ case 1:
+ val |= (MSDC_BUS_1BITS << SDC_CFG_BUSWIDTH_S);
+ break;
+ case 4:
+ val |= (MSDC_BUS_4BITS << SDC_CFG_BUSWIDTH_S);
+ break;
+ case 8:
+ val |= (MSDC_BUS_8BITS << SDC_CFG_BUSWIDTH_S);
+ break;
+ }
+
+ writel(val, host->base + SDC_CFG);
+}
+
+static void msdc_set_mclk(struct msdc_host *host, enum bus_mode timing, u32 hz)
+{
+ u32 mode;
+ u32 div;
+ u32 sclk;
+ u32 reg;
+
+ if (!hz) {
+ host->mclk = 0;
+ clrbits_le32(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
+ return;
+ }
+
+ if (host->dev_comp->clk_div_bits == 8)
+ clrbits_le32(host->base + MSDC_CFG, MSDC_CFG_HS400_CK_MODE);
+ else
+ clrbits_le32(host->base + MSDC_CFG,
+ MSDC_CFG_HS400_CK_MODE_EXT);
+
+ if (timing == UHS_DDR50 || timing == MMC_DDR_52 ||
+ timing == MMC_HS_400) {
+ if (timing == MMC_HS_400)
+ mode = 0x3;
+ else
+ mode = 0x2; /* ddr mode and use divisor */
+
+ if (hz >= (host->src_clk_freq >> 2)) {
+ div = 0; /* mean div = 1/4 */
+ sclk = host->src_clk_freq >> 2; /* sclk = clk / 4 */
+ } else {
+ div = (host->src_clk_freq + ((hz << 2) - 1)) /
+ (hz << 2);
+ sclk = (host->src_clk_freq >> 2) / div;
+ div = (div >> 1);
+ }
+
+ if (timing == MMC_HS_400 && hz >= (host->src_clk_freq >> 1)) {
+ if (host->dev_comp->clk_div_bits == 8)
+ setbits_le32(host->base + MSDC_CFG,
+ MSDC_CFG_HS400_CK_MODE);
+ else
+ setbits_le32(host->base + MSDC_CFG,
+ MSDC_CFG_HS400_CK_MODE_EXT);
+
+ sclk = host->src_clk_freq >> 1;
+ div = 0; /* div is ignore when bit18 is set */
+ }
+ } else if (hz >= host->src_clk_freq) {
+ mode = 0x1; /* no divisor */
+ div = 0;
+ sclk = host->src_clk_freq;
+ } else {
+ mode = 0x0; /* use divisor */
+ if (hz >= (host->src_clk_freq >> 1)) {
+ div = 0; /* mean div = 1/2 */
+ sclk = host->src_clk_freq >> 1; /* sclk = clk / 2 */
+ } else {
+ div = (host->src_clk_freq + ((hz << 2) - 1)) /
+ (hz << 2);
+ sclk = (host->src_clk_freq >> 2) / div;
+ }
+ }
+
+ clrbits_le32(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
+
+ if (host->dev_comp->clk_div_bits == 8) {
+ div = min(div, (u32)MSDC_CFG_CKDIV_M);
+ clrsetbits_le32(host->base + MSDC_CFG,
+ (MSDC_CFG_CKMOD_M << MSDC_CFG_CKMOD_S) |
+ (MSDC_CFG_CKDIV_M << MSDC_CFG_CKDIV_S),
+ (mode << MSDC_CFG_CKMOD_S) |
+ (div << MSDC_CFG_CKDIV_S));
+ } else {
+ div = min(div, (u32)MSDC_CFG_CKDIV_EXT_M);
+ clrsetbits_le32(host->base + MSDC_CFG,
+ (MSDC_CFG_CKMOD_EXT_M << MSDC_CFG_CKMOD_EXT_S) |
+ (MSDC_CFG_CKDIV_EXT_M << MSDC_CFG_CKDIV_EXT_S),
+ (mode << MSDC_CFG_CKMOD_S) |
+ (div << MSDC_CFG_CKDIV_EXT_S));
+ }
+
+ readl_poll_timeout(host->base + MSDC_CFG, reg,
+ reg & MSDC_CFG_CKSTB, 1000000);
+
+ setbits_le32(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
+ host->sclk = sclk;
+ host->mclk = hz;
+ host->timing = timing;
+
+ /* needed because clk changed. */
+ msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
+
+ /*
+ * mmc_select_hs400() will drop to 50Mhz and High speed mode,
+ * tune result of hs200/200Mhz is not suitable for 50Mhz
+ */
+ if (host->sclk <= 52000000) {
+ writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
+ writel(host->def_tune_para.pad_tune,
+ host->base + MSDC_PAD_TUNE);
+ } else {
+ writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
+ writel(host->saved_tune_para.pad_tune,
+ host->base + MSDC_PAD_TUNE);
+ }
+
+ dev_dbg(dev, "sclk: %d, timing: %d\n", host->sclk, timing);
+}
+
+static int msdc_ops_set_ios(struct udevice *dev)
+{
+ struct msdc_plat *plat = dev_get_platdata(dev);
+ struct msdc_host *host = dev_get_priv(dev);
+ struct mmc *mmc = &plat->mmc;
+ uint clock = mmc->clock;
+
+ msdc_set_buswidth(host, mmc->bus_width);
+
+ if (mmc->clk_disable)
+ clock = 0;
+ else if (clock < mmc->cfg->f_min)
+ clock = mmc->cfg->f_min;
+
+ if (host->mclk != clock || host->timing != mmc->selected_mode)
+ msdc_set_mclk(host, mmc->selected_mode, clock);
+
+ return 0;
+}
+
+#ifdef CONFIG_DM_GPIO
+static int msdc_ops_get_cd(struct udevice *dev)
+{
+ struct msdc_host *host = dev_get_priv(dev);
+
+ if (!host->gpio_cd.dev)
+ return 1;
+
+ return dm_gpio_get_value(&host->gpio_cd);
+}
+
+static int msdc_ops_get_wp(struct udevice *dev)
+{
+ struct msdc_host *host = dev_get_priv(dev);
+
+ if (!host->gpio_wp.dev)
+ return 0;
+
+ return !dm_gpio_get_value(&host->gpio_wp);
+}
+#endif
+
+#ifdef MMC_SUPPORTS_TUNING
+static u32 test_delay_bit(u32 delay, u32 bit)
+{
+ bit %= PAD_DELAY_MAX;
+ return delay & (1 << bit);
+}
+
+static int get_delay_len(u32 delay, u32 start_bit)
+{
+ int i;
+
+ for (i = 0; i < (PAD_DELAY_MAX - start_bit); i++) {
+ if (test_delay_bit(delay, start_bit + i) == 0)
+ return i;
+ }
+
+ return PAD_DELAY_MAX - start_bit;
+}
+
+static struct msdc_delay_phase get_best_delay(struct msdc_host *host, u32 delay)
+{
+ int start = 0, len = 0;
+ int start_final = 0, len_final = 0;
+ u8 final_phase = 0xff;
+ struct msdc_delay_phase delay_phase = { 0, };
+
+ if (delay == 0) {
+ dev_err(dev, "phase error: [map:%x]\n", delay);
+ delay_phase.final_phase = final_phase;
+ return delay_phase;
+ }
+
+ while (start < PAD_DELAY_MAX) {
+ len = get_delay_len(delay, start);
+ if (len_final < len) {
+ start_final = start;
+ len_final = len;
+ }
+
+ start += len ? len : 1;
+ if (len >= 12 && start_final < 4)
+ break;
+ }
+
+ /* The rule is to find the smallest delay cell */
+ if (start_final == 0)
+ final_phase = (start_final + len_final / 3) % PAD_DELAY_MAX;
+ else
+ final_phase = (start_final + len_final / 2) % PAD_DELAY_MAX;
+
+ dev_info(dev, "phase: [map:%x] [maxlen:%d] [final:%d]\n",
+ delay, len_final, final_phase);
+
+ delay_phase.maxlen = len_final;
+ delay_phase.start = start_final;
+ delay_phase.final_phase = final_phase;
+ return delay_phase;
+}
+
+static int msdc_tune_response(struct udevice *dev, u32 opcode)
+{
+ struct msdc_plat *plat = dev_get_platdata(dev);
+ struct msdc_host *host = dev_get_priv(dev);
+ struct mmc *mmc = &plat->mmc;
+ u32 rise_delay = 0, fall_delay = 0;
+ struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0, };
+ struct msdc_delay_phase internal_delay_phase;
+ u8 final_delay, final_maxlen;
+ u32 internal_delay = 0;
+ u32 tune_reg = MSDC_PAD_TUNE;
+ int cmd_err;
+ int i, j;
+
+ if (host->dev_comp->pad_tune0)
+ tune_reg = MSDC_PAD_TUNE0;
+
+ if (mmc->selected_mode == MMC_HS_200 ||
+ mmc->selected_mode == UHS_SDR104)
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_CMDRRDLY_M <<
+ MSDC_PAD_TUNE_CMDRRDLY_S,
+ host->hs200_cmd_int_delay <<
+ MSDC_PAD_TUNE_CMDRRDLY_S);
+
+ clrbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+
+ for (i = 0; i < PAD_DELAY_MAX; i++) {
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_CMDRDLY_M <<
+ MSDC_PAD_TUNE_CMDRDLY_S,
+ i << MSDC_PAD_TUNE_CMDRDLY_S);
+
+ for (j = 0; j < 3; j++) {
+ mmc_send_tuning(mmc, opcode, &cmd_err);
+ if (!cmd_err) {
+ rise_delay |= (1 << i);
+ } else {
+ rise_delay &= ~(1 << i);
+ break;
+ }
+ }
+ }
+
+ final_rise_delay = get_best_delay(host, rise_delay);
+ /* if rising edge has enough margin, do not scan falling edge */
+ if (final_rise_delay.maxlen >= 12 ||
+ (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
+ goto skip_fall;
+
+ setbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+ for (i = 0; i < PAD_DELAY_MAX; i++) {
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_CMDRDLY_M <<
+ MSDC_PAD_TUNE_CMDRDLY_S,
+ i << MSDC_PAD_TUNE_CMDRDLY_S);
+
+ for (j = 0; j < 3; j++) {
+ mmc_send_tuning(mmc, opcode, &cmd_err);
+ if (!cmd_err) {
+ fall_delay |= (1 << i);
+ } else {
+ fall_delay &= ~(1 << i);
+ break;
+ }
+ }
+ }
+
+ 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) {
+ clrbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_CMDRDLY_M <<
+ MSDC_PAD_TUNE_CMDRDLY_S,
+ final_rise_delay.final_phase <<
+ MSDC_PAD_TUNE_CMDRDLY_S);
+ final_delay = final_rise_delay.final_phase;
+ } else {
+ setbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_CMDRDLY_M <<
+ MSDC_PAD_TUNE_CMDRDLY_S,
+ final_fall_delay.final_phase <<
+ MSDC_PAD_TUNE_CMDRDLY_S);
+ final_delay = final_fall_delay.final_phase;
+ }
+
+ if (host->dev_comp->async_fifo || host->hs200_cmd_int_delay)
+ goto skip_internal;
+
+ for (i = 0; i < PAD_DELAY_MAX; i++) {
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_CMDRRDLY_M <<
+ MSDC_PAD_TUNE_CMDRRDLY_S,
+ i << MSDC_PAD_TUNE_CMDRRDLY_S);
+
+ mmc_send_tuning(mmc, opcode, &cmd_err);
+ if (!cmd_err)
+ internal_delay |= (1 << i);
+ }
+
+ dev_err(dev, "Final internal delay: 0x%x\n", internal_delay);
+
+ internal_delay_phase = get_best_delay(host, internal_delay);
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_CMDRRDLY_M <<
+ MSDC_PAD_TUNE_CMDRRDLY_S,
+ internal_delay_phase.final_phase <<
+ MSDC_PAD_TUNE_CMDRRDLY_S);
+
+skip_internal:
+ dev_err(dev, "Final cmd pad delay: %x\n", final_delay);
+ return final_delay == 0xff ? -EIO : 0;
+}
+
+static int msdc_tune_data(struct udevice *dev, u32 opcode)
+{
+ struct msdc_plat *plat = dev_get_platdata(dev);
+ struct msdc_host *host = dev_get_priv(dev);
+ struct mmc *mmc = &plat->mmc;
+ 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 = MSDC_PAD_TUNE;
+ int cmd_err;
+ int i, ret;
+
+ if (host->dev_comp->pad_tune0)
+ tune_reg = MSDC_PAD_TUNE0;
+
+ clrbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
+ clrbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+
+ for (i = 0; i < PAD_DELAY_MAX; i++) {
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_DATRRDLY_M <<
+ MSDC_PAD_TUNE_DATRRDLY_S,
+ i << MSDC_PAD_TUNE_DATRRDLY_S);
+
+ ret = mmc_send_tuning(mmc, opcode, &cmd_err);
+ if (!ret) {
+ rise_delay |= (1 << i);
+ } else if (cmd_err) {
+ /* in this case, retune response is needed */
+ ret = msdc_tune_response(dev, opcode);
+ if (ret)
+ break;
+ }
+ }
+
+ final_rise_delay = get_best_delay(host, rise_delay);
+ if (final_rise_delay.maxlen >= 12 ||
+ (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
+ goto skip_fall;
+
+ setbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
+ setbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+
+ for (i = 0; i < PAD_DELAY_MAX; i++) {
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_DATRRDLY_M <<
+ MSDC_PAD_TUNE_DATRRDLY_S,
+ i << MSDC_PAD_TUNE_DATRRDLY_S);
+
+ ret = mmc_send_tuning(mmc, opcode, &cmd_err);
+ if (!ret) {
+ fall_delay |= (1 << i);
+ } else if (cmd_err) {
+ /* in this case, retune response is needed */
+ ret = msdc_tune_response(dev, opcode);
+ if (ret)
+ break;
+ }
+ }
+
+ 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) {
+ clrbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
+ clrbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_DATRRDLY_M <<
+ MSDC_PAD_TUNE_DATRRDLY_S,
+ final_rise_delay.final_phase <<
+ MSDC_PAD_TUNE_DATRRDLY_S);
+ final_delay = final_rise_delay.final_phase;
+ } else {
+ setbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
+ setbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_DATRRDLY_M <<
+ MSDC_PAD_TUNE_DATRRDLY_S,
+ final_fall_delay.final_phase <<
+ MSDC_PAD_TUNE_DATRRDLY_S);
+ final_delay = final_fall_delay.final_phase;
+ }
+
+ if (mmc->selected_mode == MMC_HS_200 ||
+ mmc->selected_mode == UHS_SDR104)
+ clrsetbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_DATWRDLY_M <<
+ MSDC_PAD_TUNE_DATWRDLY_S,
+ host->hs200_write_int_delay <<
+ MSDC_PAD_TUNE_DATWRDLY_S);
+
+ dev_err(dev, "Final data pad delay: %x\n", final_delay);
+
+ return final_delay == 0xff ? -EIO : 0;
+}
+
+static int msdc_execute_tuning(struct udevice *dev, uint opcode)
+{
+ struct msdc_plat *plat = dev_get_platdata(dev);
+ struct msdc_host *host = dev_get_priv(dev);
+ struct mmc *mmc = &plat->mmc;
+ int ret;
+
+ if (mmc->selected_mode == MMC_HS_400) {
+ writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
+ /* for hs400 mode it must be set to 0 */
+ clrbits_le32(host->base + MSDC_PATCH_BIT2, MSDC_PB2_CFGCRCSTS);
+ host->hs400_mode = true;
+ }
+
+ ret = msdc_tune_response(dev, opcode);
+ if (ret == -EIO) {
+ dev_err(dev, "Tune response fail!\n");
+ return ret;
+ }
+
+ if (!host->hs400_mode) {
+ ret = msdc_tune_data(dev, opcode);
+ if (ret == -EIO)
+ dev_err(dev, "Tune data fail!\n");
+ }
+
+ host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
+ host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
+
+ return ret;
+}
+#endif
+
+static void msdc_init_hw(struct msdc_host *host)
+{
+ u32 val;
+ u32 tune_reg = MSDC_PAD_TUNE;
+
+ if (host->dev_comp->pad_tune0)
+ tune_reg = MSDC_PAD_TUNE0;
+
+ /* Configure to MMC/SD mode, clock free running */
+ setbits_le32(host->base + MSDC_CFG, MSDC_CFG_MODE);
+
+ /* Use PIO mode */
+ setbits_le32(host->base + MSDC_CFG, MSDC_CFG_PIO);
+
+ /* Reset */
+ msdc_reset_hw(host);
+
+ /* Disable card detection */
+ clrbits_le32(host->base + MSDC_PS, MSDC_PS_CDEN);
+
+ /* Clear all interrupts */
+ val = readl(host->base + MSDC_INT);
+ writel(val, host->base + MSDC_INT);
+
+ /* Enable data & cmd interrupts */
+ writel(DATA_INTS_MASK | CMD_INTS_MASK, host->base + MSDC_INTEN);
+
+ writel(0, host->base + tune_reg);
+ writel(0, host->base + MSDC_IOCON);
+
+ if (host->r_smpl)
+ setbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+ else
+ clrbits_le32(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+
+ writel(0x403c0046, host->base + MSDC_PATCH_BIT);
+ writel(0xffff4089, host->base + MSDC_PATCH_BIT1);
+
+ if (host->dev_comp->stop_clk_fix)
+ clrsetbits_le32(host->base + MSDC_PATCH_BIT1,
+ MSDC_PB1_STOP_DLY_M << MSDC_PB1_STOP_DLY_S,
+ 3 << MSDC_PB1_STOP_DLY_S);
+
+ if (host->dev_comp->busy_check)
+ clrbits_le32(host->base + MSDC_PATCH_BIT1, (1 << 7));
+
+ setbits_le32(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
+
+ if (host->dev_comp->async_fifo) {
+ clrsetbits_le32(host->base + MSDC_PATCH_BIT2,
+ MSDC_PB2_RESPWAIT_M << MSDC_PB2_RESPWAIT_S,
+ 3 << MSDC_PB2_RESPWAIT_S);
+
+ if (host->dev_comp->enhance_rx) {
+ setbits_le32(host->base + SDC_ADV_CFG0,
+ SDC_RX_ENHANCE_EN);
+ } else {
+ clrsetbits_le32(host->base + MSDC_PATCH_BIT2,
+ MSDC_PB2_RESPSTSENSEL_M <<
+ MSDC_PB2_RESPSTSENSEL_S,
+ 2 << MSDC_PB2_RESPSTSENSEL_S);
+ clrsetbits_le32(host->base + MSDC_PATCH_BIT2,
+ MSDC_PB2_CRCSTSENSEL_M <<
+ MSDC_PB2_CRCSTSENSEL_S,
+ 2 << MSDC_PB2_CRCSTSENSEL_S);
+ }
+
+ /* use async fifo to avoid tune internal delay */
+ clrbits_le32(host->base + MSDC_PATCH_BIT2,
+ MSDC_PB2_CFGRESP);
+ clrbits_le32(host->base + MSDC_PATCH_BIT2,
+ MSDC_PB2_CFGCRCSTS);
+ }
+
+ if (host->dev_comp->data_tune) {
+ setbits_le32(host->base + tune_reg,
+ MSDC_PAD_TUNE_RD_SEL | MSDC_PAD_TUNE_CMD_SEL);
+ clrsetbits_le32(host->base + MSDC_PATCH_BIT,
+ MSDC_INT_DAT_LATCH_CK_SEL_S <<
+ MSDC_INT_DAT_LATCH_CK_SEL_M,
+ host->latch_ck <<
+ MSDC_INT_DAT_LATCH_CK_SEL_S);
+ } else {
+ /* choose clock tune */
+ setbits_le32(host->base + tune_reg, MSDC_PAD_TUNE_RXDLYSEL);
+ }
+
+ /* Configure to enable SDIO mode otherwise sdio cmd5 won't work */
+ setbits_le32(host->base + SDC_CFG, SDC_CFG_SDIO);
+
+ /* disable detecting SDIO device interrupt function */
+ clrbits_le32(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
+
+ /* Configure to default data timeout */
+ clrsetbits_le32(host->base + SDC_CFG,
+ SDC_CFG_DTOC_M << SDC_CFG_DTOC_S,
+ 3 << SDC_CFG_DTOC_S);
+
+ if (host->dev_comp->stop_clk_fix) {
+ clrbits_le32(host->base + SDC_FIFO_CFG,
+ SDC_FIFO_CFG_WRVALIDSEL);
+ clrbits_le32(host->base + SDC_FIFO_CFG,
+ SDC_FIFO_CFG_RDVALIDSEL);
+ }
+
+ host->def_tune_para.iocon = readl(host->base + MSDC_IOCON);
+ host->def_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
+}
+
+static void msdc_ungate_clock(struct msdc_host *host)
+{
+ clk_enable(&host->src_clk);
+ clk_enable(&host->h_clk);
+}
+
+static int msdc_drv_probe(struct udevice *dev)
+{
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+ struct msdc_plat *plat = dev_get_platdata(dev);
+ struct msdc_host *host = dev_get_priv(dev);
+ struct mmc_config *cfg = &plat->cfg;
+ int ret;
+
+ cfg->name = dev->name;
+
+ host->base = (void *)dev_read_addr(dev);
+ if (!host->base)
+ return -EINVAL;
+
+ ret = mmc_of_parse(dev, cfg);
+ if (ret)
+ return ret;
+
+ ret = clk_get_by_name(dev, "source", &host->src_clk);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_get_by_name(dev, "hclk", &host->h_clk);
+ if (ret < 0)
+ return ret;
+
+ gpio_request_by_name(dev, "wp-gpios", 0, &host->gpio_wp, GPIOD_IS_IN);
+ gpio_request_by_name(dev, "cd-gpios", 0, &host->gpio_cd, GPIOD_IS_IN);
+
+ host->hs400_ds_delay = dev_read_u32_default(dev, "hs400-ds-delay", 0);
+ host->hs200_cmd_int_delay =
+ dev_read_u32_default(dev, "cmd_int_delay", 0);
+ host->hs200_write_int_delay =
+ dev_read_u32_default(dev, "write_int_delay", 0);
+ host->latch_ck = dev_read_u32_default(dev, "latch-ck", 0);
+ host->r_smpl = dev_read_u32_default(dev, "r_smpl", 0);
+
+ host->dev_comp = (struct msdc_compatible *)dev_get_driver_data(dev);
+
+ host->src_clk_freq = clk_get_rate(&host->src_clk);
+
+ if (host->dev_comp->clk_div_bits == 8)
+ cfg->f_min = host->src_clk_freq / (4 * 255);
+ else
+ cfg->f_min = host->src_clk_freq / (4 * 4095);
+
+ cfg->b_max = 1024;
+ cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ host->mmc = &plat->mmc;
+ host->timeout_ns = 100000000;
+ host->timeout_clks = 3 * 1048576;
+
+ pinctrl_select_state(dev, "default");
+
+ msdc_ungate_clock(host);
+ msdc_init_hw(host);
+
+ upriv->mmc = &plat->mmc;
+
+ return 0;
+}
+
+static int msdc_drv_bind(struct udevice *dev)
+{
+ struct msdc_plat *plat = dev_get_platdata(dev);
+
+ return mmc_bind(dev, &plat->mmc, &plat->cfg);
+}
+
+static const struct dm_mmc_ops msdc_ops = {
+ .send_cmd = msdc_ops_send_cmd,
+ .set_ios = msdc_ops_set_ios,
+#ifdef CONFIG_DM_GPIO
+ .get_cd = msdc_ops_get_cd,
+ .get_wp = msdc_ops_get_wp,
+#endif
+#ifdef MMC_SUPPORTS_TUNING
+ .execute_tuning = msdc_execute_tuning,
+#endif
+};
+
+static const struct msdc_compatible mt7623_compat = {
+ .clk_div_bits = 12,
+ .pad_tune0 = true,
+ .async_fifo = true,
+ .data_tune = true,
+ .busy_check = false,
+ .stop_clk_fix = false,
+ .enhance_rx = false
+};
+
+static const struct udevice_id msdc_ids[] = {
+ { .compatible = "mediatek,mt7623-mmc", .data = (ulong)&mt7623_compat },
+ {}
+};
+
+U_BOOT_DRIVER(mtk_sd_drv) = {
+ .name = "mtk_sd",
+ .id = UCLASS_MMC,
+ .of_match = msdc_ids,
+ .bind = msdc_drv_bind,
+ .probe = msdc_drv_probe,
+ .ops = &msdc_ops,
+ .platdata_auto_alloc_size = sizeof(struct msdc_plat),
+ .priv_auto_alloc_size = sizeof(struct msdc_host),
+};
--
1.9.1
next prev parent reply other threads:[~2018-10-12 7:01 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-10-12 7:00 [U-Boot] [PATCH v2 00/18] Add U-Boot support for MediaTek SoCs - MT7623n & MT7629 Ryder Lee
2018-10-12 7:00 ` [U-Boot] [PATCH v2 01/18] tools: MediaTek: add MTK boot header generation to mkimage Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-25 13:11 ` Ryder Lee
2018-10-12 7:00 ` [U-Boot] [PATCH v2 02/18] arm: dts: MediaTek: add device tree for MT7629 Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-12 7:00 ` [U-Boot] [PATCH v2 03/18] arm: dts: MediaTek: add device tree for MT7623 Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-12 7:00 ` [U-Boot] [PATCH v2 04/18] arm: MediaTek: add basic support for MT7629 boards Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-25 6:44 ` Ryder Lee
2018-10-12 7:00 ` [U-Boot] [PATCH v2 05/18] arm: MediaTek: add basic support for MT7623 boards Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-25 6:40 ` Ryder Lee
2018-10-12 7:00 ` [U-Boot] [PATCH v2 06/18] clk: MediaTek: add clock driver for MT7629 SoC Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-25 6:37 ` Ryder Lee
2018-11-03 6:08 ` Simon Glass
2018-10-12 7:00 ` [U-Boot] [PATCH v2 07/18] clk: MediaTek: add clock driver for MT7623 SoC Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-12 7:00 ` [U-Boot] [PATCH v2 08/18] timer: MediaTek: add timer driver for MediaTek SoCs Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-12 7:00 ` [U-Boot] [PATCH v2 09/18] watchdog: MediaTek: add watchdog " Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-12 7:00 ` [U-Boot] [PATCH v2 10/18] pinctrl: MediaTek: add pinctrl driver for MT7629 SoC Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-25 6:08 ` Ryder Lee
2018-11-03 6:08 ` Simon Glass
2018-10-12 7:01 ` [U-Boot] [PATCH v2 11/18] pinctrl: MediaTek: add pinctrl driver for MT7623 SoC Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-25 6:13 ` Ryder Lee
2018-11-03 6:08 ` Simon Glass
2018-10-12 7:01 ` [U-Boot] [PATCH v2 12/18] power domain: MediaTek: add power domain driver for MT7629 SoC Ryder Lee
2018-10-25 3:29 ` Simon Glass
2018-10-12 7:01 ` [U-Boot] [PATCH v2 13/18] power domain: MediaTek: add power domain driver for MT7623 SoC Ryder Lee
2018-10-25 3:30 ` Simon Glass
2018-10-12 7:01 ` [U-Boot] [PATCH v2 14/18] serial: 16550: allow the driver to support MediaTek serial Ryder Lee
2018-10-25 3:30 ` Simon Glass
2018-10-12 7:01 ` [U-Boot] [PATCH v2 15/18] ram: MediaTek: add DDR3 driver for MT7629 SoC Ryder Lee
2018-10-25 3:30 ` Simon Glass
2018-10-25 9:38 ` Ryder Lee
2018-10-26 16:53 ` Simon Glass
2018-10-12 7:01 ` Ryder Lee [this message]
2018-10-25 3:30 ` [U-Boot] [PATCH v2 16/18] mmc: mtk-sd: add SD/MMC host controller driver for MT7623 SoC Simon Glass
2018-10-12 7:01 ` [U-Boot] [PATCH v2 17/18] spi: mtk_qspi: add qspi driver for MT7629 SoC Ryder Lee
2018-10-25 3:30 ` Simon Glass
2018-10-26 6:33 ` Jagan Teki
2018-11-14 9:04 ` Jagan Teki
2018-11-14 12:53 ` Guochun Mao
2018-11-21 9:38 ` Jagan Teki
2018-11-21 11:46 ` Guochun Mao
2018-11-22 6:21 ` Jagan Teki
2018-11-22 8:58 ` Guochun Mao
2018-11-23 5:43 ` Jagan Teki
2018-11-23 8:24 ` Guochun Mao
2018-10-12 7:01 ` [U-Boot] [PATCH v2 18/18] MAINTAINERS: add an entry for MediaTek Ryder Lee
2018-10-25 3:30 ` Simon Glass
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8c1a855adb49d8f03d89e8762ab4d7df82cc1ab9.1539326908.git.ryder.lee@mediatek.com \
--to=ryder.lee@mediatek.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.