
Hi Ryder.
On 12 October 2018 at 01:01, Ryder Lee ryder.lee@mediatek.com wrote:
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
Reviewed-by: Simon Glass sjg@chromium.org
nits below
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
You should be able to omit this since it is the default.
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>
Please check include-file order on all these patches.
+#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
We mostly use shifted masks in U-Boot since they are easier to use:
+#define MSDC_CFG_CKDIV_S 8 +#define MSDC_CFG_CKDIV_M (0xff << MSDC_CFG_CKDIV_S)
+#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;
This struct needs comments.
+};
+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);
Use a C struct instead of all this base +_ xxx stuff?
[..]
+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;
Normally we read the DT in the ofdata_to_platdata() method.
[..]
Regards, Simon