[U-Boot] [PATCH v2 3/4] mmc: meson: add MMC driver for Meson GX (S905)

This driver implements MMC support on Meson GX (S905) based systems. It's based on Carlo Caione's work, changes: - BLK support added - general refactoring
Original author: Carlo Caione carlo@caione.org Signed-off-by: Heiner Kallweit hkallweit1@gmail.com --- v2: - addressed review comments - general refactoring --- arch/arm/include/asm/arch-meson/sd_emmc.h | 89 +++++++++ drivers/mmc/Kconfig | 11 ++ drivers/mmc/Makefile | 1 + drivers/mmc/meson_gx_mmc.c | 291 ++++++++++++++++++++++++++++++ include/configs/meson-gxbb-common.h | 4 + 5 files changed, 396 insertions(+) create mode 100644 arch/arm/include/asm/arch-meson/sd_emmc.h create mode 100644 drivers/mmc/meson_gx_mmc.c
diff --git a/arch/arm/include/asm/arch-meson/sd_emmc.h b/arch/arm/include/asm/arch-meson/sd_emmc.h new file mode 100644 index 0000000..25dbf60 --- /dev/null +++ b/arch/arm/include/asm/arch-meson/sd_emmc.h @@ -0,0 +1,89 @@ +/* + * (C) Copyright 2017 Carlo Caione carlo@caione.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __SD_EMMC_H__ +#define __SD_EMMC_H__ + +#include <mmc.h> + +#define SDIO_PORT_A 0 +#define SDIO_PORT_B 1 +#define SDIO_PORT_C 2 + +#define SD_EMMC_CLKSRC_24M 24000000 /* 24 MHz */ +#define SD_EMMC_CLKSRC_DIV2 1000000000 /* 1 GHz */ + +#define MESON_SD_EMMC_CLOCK 0x00 +#define CLK_MAX_DIV 63 +#define CLK_SRC_24M (0 << 6) +#define CLK_SRC_DIV2 (1 << 6) +#define CLK_CO_PHASE_000 (0 << 8) +#define CLK_CO_PHASE_090 (1 << 8) +#define CLK_CO_PHASE_180 (2 << 8) +#define CLK_CO_PHASE_270 (3 << 8) +#define CLK_TX_PHASE_000 (0 << 10) +#define CLK_TX_PHASE_090 (1 << 10) +#define CLK_TX_PHASE_180 (2 << 10) +#define CLK_TX_PHASE_270 (3 << 10) +#define CLK_ALWAYS_ON BIT(24) + +#define MESON_SD_EMMC_CFG 0x44 +#define CFG_BUS_WIDTH_MASK GENMASK(1, 0) +#define CFG_BUS_WIDTH_1 0 +#define CFG_BUS_WIDTH_4 1 +#define CFG_BUS_WIDTH_8 2 +#define CFG_BL_LEN_MASK GENMASK(7, 4) +#define CFG_BL_LEN_SHIFT 4 +#define CFG_BL_LEN_512 (9 << 4) +#define CFG_RESP_TIMEOUT_MASK GENMASK(11, 8) +#define CFG_RESP_TIMEOUT_256 (8 << 8) +#define CFG_RC_CC_MASK GENMASK(15, 12) +#define CFG_RC_CC_16 (4 << 12) +#define CFG_SDCLK_ALWAYS_ON BIT(18) +#define CFG_AUTO_CLK BIT(23) + +#define MESON_SD_EMMC_STATUS 0x48 +#define STATUS_MASK GENMASK(15, 0) +#define STATUS_ERR_MASK GENMASK(12, 0) +#define STATUS_RXD_ERR_MASK GENMASK(7, 0) +#define STATUS_TXD_ERR BIT(8) +#define STATUS_DESC_ERR BIT(9) +#define STATUS_RESP_ERR BIT(10) +#define STATUS_RESP_TIMEOUT BIT(11) +#define STATUS_DESC_TIMEOUT BIT(12) +#define STATUS_END_OF_CHAIN BIT(13) + +#define MESON_SD_EMMC_IRQ_EN 0x4c + +#define MESON_SD_EMMC_CMD_CFG 0x50 +#define CMD_CFG_LENGTH_MASK GENMASK(8, 0) +#define CMD_CFG_BLOCK_MODE BIT(9) +#define CMD_CFG_R1B BIT(10) +#define CMD_CFG_END_OF_CHAIN BIT(11) +#define CMD_CFG_TIMEOUT_4S (12 << 12) +#define CMD_CFG_NO_RESP BIT(16) +#define CMD_CFG_DATA_IO BIT(18) +#define CMD_CFG_DATA_WR BIT(19) +#define CMD_CFG_RESP_NOCRC BIT(20) +#define CMD_CFG_RESP_128 BIT(21) +#define CMD_CFG_CMD_INDEX_SHIFT 24 +#define CMD_CFG_OWNER BIT(31) + +#define MESON_SD_EMMC_CMD_ARG 0x54 +#define MESON_SD_EMMC_CMD_DAT 0x58 +#define MESON_SD_EMMC_CMD_RSP 0x5c +#define MESON_SD_EMMC_CMD_RSP1 0x60 +#define MESON_SD_EMMC_CMD_RSP2 0x64 +#define MESON_SD_EMMC_CMD_RSP3 0x68 + +struct meson_mmc_platdata { + struct mmc_config cfg; + struct mmc mmc; + void *regbase; + void *w_buf; +}; + +#endif diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 9ed8da3..03416ff 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -168,6 +168,17 @@ config ZYNQ_SDHCI help Support for Arasan SDHCI host controller on Zynq/ZynqMP ARM SoCs platform
+config MMC_MESON_GX + bool "Meson GX EMMC controller support" + depends on DM_MMC && BLK && DM_MMC_OPS && ARCH_MESON + help + Support for EMMC host controller on Meson GX ARM SoCs platform (S905) + +config MMC_MESON_GX_SD_PORT + int "Meson SD port selection" + range 0 2 + depends on MMC_MESON_GX + config ROCKCHIP_SDHCI bool "Arasan SDHCI controller for Rockchip support" depends on DM_MMC && BLK && DM_MMC_OPS diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 4dca09c..6e951a0 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -31,6 +31,7 @@ ifdef CONFIG_SUPPORT_EMMC_BOOT obj-$(CONFIG_GENERIC_MMC) += mmc_boot.o endif obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o +obj-$(CONFIG_MMC_MESON_GX) += meson_gx_mmc.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o obj-$(CONFIG_MVEBU_MMC) += mvebu_mmc.o obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o diff --git a/drivers/mmc/meson_gx_mmc.c b/drivers/mmc/meson_gx_mmc.c new file mode 100644 index 0000000..d4c6e04 --- /dev/null +++ b/drivers/mmc/meson_gx_mmc.c @@ -0,0 +1,291 @@ +/* + * (C) Copyright 2017 Carlo Caione carlo@caione.org + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <malloc.h> +#include <mmc.h> +#include <asm/io.h> +#include <asm/arch/sd_emmc.h> +#include <dm/device.h> +#include <linux/log2.h> + +static inline void *get_regbase(const struct mmc *mmc) +{ + struct meson_mmc_platdata *pdata = mmc->priv; + + return pdata->regbase; +} + +static inline uint32_t meson_read(struct mmc *mmc, int offset) +{ + return readl(get_regbase(mmc) + offset); +} + +static inline void meson_write(struct mmc *mmc, uint32_t val, int offset) +{ + writel(val, get_regbase(mmc) + offset); +} + +static void meson_mmc_config_clock(struct mmc *mmc) +{ + uint32_t meson_mmc_clk = 0; + unsigned int clk, clk_src, clk_div; + + /* 1GHz / CLK_MAX_DIV = 15,9 MHz */ + if (mmc->clock > 16000000) { + clk = SD_EMMC_CLKSRC_DIV2; + clk_src = CLK_SRC_DIV2; + } else { + clk = SD_EMMC_CLKSRC_24M; + clk_src = CLK_SRC_24M; + } + clk_div = DIV_ROUND_UP(clk, mmc->clock); + + /* 180 phase core clock */ + meson_mmc_clk |= CLK_CO_PHASE_180; + + /* 180 phase tx clock */ + meson_mmc_clk |= CLK_TX_PHASE_180; + + /* clock settings */ + meson_mmc_clk |= clk_src; + meson_mmc_clk |= clk_div; + + meson_write(mmc, meson_mmc_clk, MESON_SD_EMMC_CLOCK); +} + +static int meson_dm_mmc_set_ios(struct udevice *dev) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); + uint32_t meson_mmc_cfg; + + meson_mmc_config_clock(mmc); + + meson_mmc_cfg = meson_read(mmc, MESON_SD_EMMC_CFG); + + meson_mmc_cfg &= ~CFG_BUS_WIDTH_MASK; + if (mmc->bus_width == 1) + meson_mmc_cfg |= CFG_BUS_WIDTH_1; + else if (mmc->bus_width == 4) + meson_mmc_cfg |= CFG_BUS_WIDTH_4; + else if (mmc->bus_width == 8) + meson_mmc_cfg |= CFG_BUS_WIDTH_8; + else + return -EINVAL; + + /* 512 bytes block length */ + meson_mmc_cfg &= ~CFG_BL_LEN_MASK; + meson_mmc_cfg |= CFG_BL_LEN_512; + + /* Response timeout 256 clk */ + meson_mmc_cfg &= ~CFG_RESP_TIMEOUT_MASK; + meson_mmc_cfg |= CFG_RESP_TIMEOUT_256; + + /* Command-command gap 16 clk */ + meson_mmc_cfg &= ~CFG_RC_CC_MASK; + meson_mmc_cfg |= CFG_RC_CC_16; + + meson_write(mmc, meson_mmc_cfg, MESON_SD_EMMC_CFG); + + return 0; +} + +static void meson_mmc_setup_cmd(struct mmc *mmc, struct mmc_data *data, + struct mmc_cmd *cmd) +{ + uint32_t meson_mmc_cmd = 0, cfg; + + meson_mmc_cmd |= cmd->cmdidx << CMD_CFG_CMD_INDEX_SHIFT; + + if (cmd->resp_type & MMC_RSP_PRESENT) { + if (cmd->resp_type & MMC_RSP_136) + meson_mmc_cmd |= CMD_CFG_RESP_128; + + if (cmd->resp_type & MMC_RSP_BUSY) + meson_mmc_cmd |= CMD_CFG_R1B; + + if (!(cmd->resp_type & MMC_RSP_CRC)) + meson_mmc_cmd |= CMD_CFG_RESP_NOCRC; + } else { + meson_mmc_cmd |= CMD_CFG_NO_RESP; + } + + if (data) { + cfg = meson_read(mmc, MESON_SD_EMMC_CFG); + cfg &= ~CFG_BL_LEN_MASK; + cfg |= ilog2(data->blocksize) << CFG_BL_LEN_SHIFT; + meson_write(mmc, cfg, MESON_SD_EMMC_CFG); + + if (data->flags == MMC_DATA_WRITE) + meson_mmc_cmd |= CMD_CFG_DATA_WR; + + cfg = CMD_CFG_DATA_IO | CMD_CFG_BLOCK_MODE | data->blocks; + meson_mmc_cmd |= cfg; + } + + cfg = CMD_CFG_TIMEOUT_4S | CMD_CFG_OWNER | CMD_CFG_END_OF_CHAIN; + meson_mmc_cmd |= cfg; + + meson_write(mmc, meson_mmc_cmd, MESON_SD_EMMC_CMD_CFG); +} + +static void meson_mmc_setup_addr(struct mmc *mmc, struct mmc_data *data) +{ + struct meson_mmc_platdata *pdata = mmc->priv; + unsigned int data_size; + uint32_t data_addr = 0; + + if (data) { + data_size = data->blocks * data->blocksize; + + if (data->flags == MMC_DATA_READ) { + data_addr = (ulong) data->dest; + invalidate_dcache_range(data_addr, + data_addr + data_size); + } else if (data->flags == MMC_DATA_WRITE) { + pdata->w_buf = calloc(data_size, sizeof(char)); + data_addr = (ulong) pdata->w_buf; + memcpy(pdata->w_buf, data->src, data_size); + flush_dcache_range(data_addr, data_addr + data_size); + } + } + + meson_write(mmc, data_addr, MESON_SD_EMMC_CMD_DAT); +} + +static void meson_mmc_read_response(struct mmc *mmc, struct mmc_cmd *cmd) +{ + if (cmd->resp_type & MMC_RSP_136) { + cmd->response[0] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP3); + cmd->response[1] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP2); + cmd->response[2] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP1); + cmd->response[3] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP); + } else { + cmd->response[0] = meson_read(mmc, MESON_SD_EMMC_CMD_RSP); + } +} + +static int meson_dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); + struct meson_mmc_platdata *pdata = mmc->priv; + uint32_t status; + ulong start; + int ret = 0; + + /* max block size supported by chip is 512 byte */ + if (data && data->blocksize > 512) + return -EINVAL; + + meson_mmc_setup_cmd(mmc, data, cmd); + meson_mmc_setup_addr(mmc, data); + + meson_write(mmc, cmd->cmdarg, MESON_SD_EMMC_CMD_ARG); + + /* use 10s timeout */ + start = get_timer(0); + do { + status = meson_read(mmc, MESON_SD_EMMC_STATUS); + } while(!(status & STATUS_END_OF_CHAIN) && get_timer(start) < 10000); + + if (!(status & STATUS_END_OF_CHAIN)) + ret = -ETIMEDOUT; + else if (status & STATUS_RESP_TIMEOUT) + ret = -ETIMEDOUT; + else if (status & STATUS_ERR_MASK) + ret = -EIO; + + meson_mmc_read_response(mmc, cmd); + + if (data && data->flags == MMC_DATA_WRITE) + free(pdata->w_buf); + + /* reset status bits */ + meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS); + + return ret; +} + +static const struct dm_mmc_ops meson_dm_mmc_ops = { + .send_cmd = meson_dm_mmc_send_cmd, + .set_ios = meson_dm_mmc_set_ios, +}; + +static int meson_mmc_ofdata_to_platdata(struct udevice *dev) +{ + struct meson_mmc_platdata *pdata = dev_get_platdata(dev); + fdt_addr_t addr; + + addr = dev_get_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + pdata->regbase = (void *)addr; + + return 0; +} + +static int meson_mmc_probe(struct udevice *dev) +{ + struct meson_mmc_platdata *pdata = dev_get_platdata(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mmc *mmc = &pdata->mmc; + struct mmc_config *cfg = &pdata->cfg; + uint32_t val; + + cfg->voltages = MMC_VDD_33_34 | MMC_VDD_32_33 | + MMC_VDD_31_32 | MMC_VDD_165_195; + cfg->host_caps = MMC_MODE_8BIT | MMC_MODE_4BIT | + MMC_MODE_HS_52MHz | MMC_MODE_HS; + cfg->f_min = DIV_ROUND_UP(SD_EMMC_CLKSRC_24M, CLK_MAX_DIV); + cfg->f_max = 100000000; /* 100 MHz */ + cfg->b_max = 256; /* max 256 blocks */ + cfg->name = dev->name; + + mmc->priv = pdata; + upriv->mmc = mmc; + + mmc_set_clock(mmc, cfg->f_min); + + /* reset all status bits */ + meson_write(mmc, STATUS_MASK, MESON_SD_EMMC_STATUS); + + /* disable interrupts */ + meson_write(mmc, 0, MESON_SD_EMMC_IRQ_EN); + + /* enable auto clock mode */ + val = meson_read(mmc, MESON_SD_EMMC_CFG); + val &= ~CFG_SDCLK_ALWAYS_ON; + val |= CFG_AUTO_CLK; + meson_write(mmc, val, MESON_SD_EMMC_CFG); + + return 0; +} + +int meson_mmc_bind(struct udevice *dev) +{ + struct meson_mmc_platdata *pdata = dev_get_platdata(dev); + + return mmc_bind(dev, &pdata->mmc, &pdata->cfg); +} + +static const struct udevice_id meson_mmc_match[] = { + { .compatible = "amlogic,meson-gx-mmc" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(meson_mmc) = { + .name = "meson_gx_mmc", + .id = UCLASS_MMC, + .of_match = meson_mmc_match, + .ops = &meson_dm_mmc_ops, + .probe = meson_mmc_probe, + .bind = meson_mmc_bind, + .ofdata_to_platdata = meson_mmc_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct meson_mmc_platdata), +}; diff --git a/include/configs/meson-gxbb-common.h b/include/configs/meson-gxbb-common.h index 3bba2e6..53c04a9 100644 --- a/include/configs/meson-gxbb-common.h +++ b/include/configs/meson-gxbb-common.h @@ -24,6 +24,10 @@ #define CONFIG_SYS_INIT_SP_ADDR 0x20000000 #define CONFIG_SYS_LOAD_ADDR CONFIG_SYS_TEXT_BASE
+#ifdef CONFIG_MMC +#define CONFIG_GENERIC_MMC +#endif + /* Generic Interrupt Controller Definitions */ #define GICD_BASE 0xc4301000 #define GICC_BASE 0xc4302000

Am 25.01.2017 um 20:54 schrieb Heiner Kallweit:
This driver implements MMC support on Meson GX (S905) based systems. It's based on Carlo Caione's work, changes:
- BLK support added
- general refactoring
Original author: Carlo Caione carlo@caione.org Signed-off-by: Heiner Kallweit hkallweit1@gmail.com
There is no such thing as "Original author:". You must keep Carlo's Signed-off-by line, and if you based this on my incomplete update attempt at https://github.com/afaerber/u-boot/commits/odroidc2 then also my Signed-off-by (sorted in chronological order with yours last). I would've also expected to see Carlo as actual author (--author="...") instead of such a textual remark.
Jaehoon, please hold off applying this until it has seen some more review.
Regards, Andreas

Am 27.01.2017 um 00:04 schrieb Andreas Färber:
Am 25.01.2017 um 20:54 schrieb Heiner Kallweit:
This driver implements MMC support on Meson GX (S905) based systems. It's based on Carlo Caione's work, changes:
- BLK support added
- general refactoring
Original author: Carlo Caione carlo@caione.org Signed-off-by: Heiner Kallweit hkallweit1@gmail.com
There is no such thing as "Original author:". You must keep Carlo's Signed-off-by line, and if you based this on my incomplete update attempt at https://github.com/afaerber/u-boot/commits/odroidc2 then also my Signed-off-by (sorted in chronological order with yours last). I would've also expected to see Carlo as actual author (--author="...") instead of such a textual remark.
Maybe 50% of Carlo's original driver are left.So I leave it up to him whether he wants to see his SoB here. Whatever the maintainers prefer is fine with me. And no, sorry, I haven't seen your work.
If more than one attempt was made, then why no driver made it to mainline yet?
Rgds, Heiner
Jaehoon, please hold off applying this until it has seen some more review.
Regards, Andreas

On 01/27/2017 08:04 AM, Andreas Färber wrote:
Am 25.01.2017 um 20:54 schrieb Heiner Kallweit:
This driver implements MMC support on Meson GX (S905) based systems. It's based on Carlo Caione's work, changes:
- BLK support added
- general refactoring
Original author: Carlo Caione carlo@caione.org Signed-off-by: Heiner Kallweit hkallweit1@gmail.com
There is no such thing as "Original author:". You must keep Carlo's Signed-off-by line, and if you based this on my incomplete update attempt at https://github.com/afaerber/u-boot/commits/odroidc2 then also my Signed-off-by (sorted in chronological order with yours last). I would've also expected to see Carlo as actual author (--author="...") instead of such a textual remark.
Jaehoon, please hold off applying this until it has seen some more review.
Sure, I will wait.
Best Regards, Jaehoon Chung
Regards, Andreas
participants (3)
-
Andreas Färber
-
Heiner Kallweit
-
Jaehoon Chung