[U-Boot] [PATCH v4 0/3] UniPhier SD/eMMC controller driver

Changes in v4: - Add some comments about HOST_MODE register - Rename uniphier_sd_wait_irq() to uniphier_sd_wait_for_irq()
Changes in v3: - Use dev_err/dev_dbg instead of pr_err/pr_debug - Tidy up uniphier_sd_set_ios() - Allow to use DMA even in SPL if the target address is DMA'able
Changes in v2: - Fix the divisor bug on the older IP (on PH1-LD4, PH1-sLD8, PH1-Pro4) - Increase time out because "mmc erase" can sometimes take long - Move HOST_MODE register setting to uniphier_sd_init() because this register does not need setting multipule times.
Masahiro Yamada (3): mmc: uniphier: add driver for UniPhier SD/MMC host controller ARM: uniphier: enable UniPhier SD/MMC host driver ARM: dts: uniphier: add SD/MMC host controller nodes
arch/arm/Kconfig | 1 + arch/arm/dts/uniphier-ph1-ld4-ref.dts | 4 + arch/arm/dts/uniphier-ph1-ld4.dtsi | 25 + arch/arm/dts/uniphier-ph1-ld6b-ref.dts | 4 + arch/arm/dts/uniphier-ph1-pro4-ace.dts | 4 + arch/arm/dts/uniphier-ph1-pro4-ref.dts | 8 + arch/arm/dts/uniphier-ph1-pro4-sanji.dts | 4 + arch/arm/dts/uniphier-ph1-pro4.dtsi | 37 ++ arch/arm/dts/uniphier-ph1-pro5-4kbox.dts | 8 + arch/arm/dts/uniphier-ph1-pro5.dtsi | 24 + arch/arm/dts/uniphier-ph1-sld3-ref.dts | 4 + arch/arm/dts/uniphier-ph1-sld3.dtsi | 19 + arch/arm/dts/uniphier-ph1-sld8-ref.dts | 4 + arch/arm/dts/uniphier-ph1-sld8.dtsi | 25 + arch/arm/dts/uniphier-pinctrl.dtsi | 15 + arch/arm/dts/uniphier-proxstream2-gentil.dts | 4 + arch/arm/dts/uniphier-proxstream2-vodka.dts | 4 + arch/arm/dts/uniphier-proxstream2.dtsi | 24 + configs/uniphier_ld4_sld8_defconfig | 1 + configs/uniphier_pro4_defconfig | 1 + configs/uniphier_pro5_defconfig | 1 + configs/uniphier_pxs2_ld6b_defconfig | 1 + configs/uniphier_sld3_defconfig | 1 + doc/README.uniphier | 1 + drivers/mmc/Kconfig | 6 + drivers/mmc/Makefile | 1 + drivers/mmc/uniphier-sd.c | 751 +++++++++++++++++++++++++++ include/configs/uniphier.h | 4 + 28 files changed, 986 insertions(+) create mode 100644 drivers/mmc/uniphier-sd.c

Add a driver for the on-chip SD/eMMC host controller used by UniPhier SoC family.
Signed-off-by: Masahiro Yamada yamada.masahiro@socionext.com ---
Changes in v4: - Add some comments about HOST_MODE register - Rename uniphier_sd_wait_irq() to uniphier_sd_wait_for_irq()
Changes in v3: - Use dev_err/dev_dbg instead of pr_err/pr_debug - Tidy up uniphier_sd_set_ios() - Allow to use DMA even in SPL if the target address is DMA'able
Changes in v2: - Fix the divisor bug on the older IP (on PH1-LD4, PH1-sLD8, PH1-Pro4) - Increase time out because "mmc erase" can sometimes take long - Move HOST_MODE register setting to uniphier_sd_init() because this register does not need setting multipule times.
doc/README.uniphier | 1 + drivers/mmc/Kconfig | 6 + drivers/mmc/Makefile | 1 + drivers/mmc/uniphier-sd.c | 751 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 759 insertions(+) create mode 100644 drivers/mmc/uniphier-sd.c
diff --git a/doc/README.uniphier b/doc/README.uniphier index f03c207..c270ecb 100644 --- a/doc/README.uniphier +++ b/doc/README.uniphier @@ -93,6 +93,7 @@ Supported devices
- UART (on-chip) - NAND + - SD/eMMC - USB 2.0 (EHCI) - USB 3.0 (xHCI) - GPIO diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 9f4b766..faffefd 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -37,4 +37,10 @@ config PIC32_SDHCI help Support for Microchip PIC32 SDHCI controller.
+config MMC_UNIPHIER + bool "UniPhier SD/MMC Host Controller support" + depends on ARCH_UNIPHIER + help + This selects support for the SD/MMC Host Controller on UniPhier SoCs. + endmenu diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index c9c3e3e..b85e4bf 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_SH_SDHI) += sh_sdhi.o obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o obj-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o obj-$(CONFIG_TEGRA_MMC) += tegra_mmc.o +obj-$(CONFIG_MMC_UNIPHIER) += uniphier-sd.o obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o
ifdef CONFIG_SPL_BUILD diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c new file mode 100644 index 0000000..3bc4d94 --- /dev/null +++ b/drivers/mmc/uniphier-sd.c @@ -0,0 +1,751 @@ +/* + * Copyright (C) 2016 Masahiro Yamada yamada.masahiro@socionext.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <fdtdec.h> +#include <mapmem.h> +#include <mmc.h> +#include <dm/device.h> +#include <linux/compat.h> +#include <linux/io.h> +#include <asm/unaligned.h> +#include <asm/dma-mapping.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define UNIPHIER_SD_CMD 0x000 /* command */ +#define UNIPHIER_SD_CMD_NOSTOP BIT(14) /* No automatic CMD12 issue */ +#define UNIPHIER_SD_CMD_MULTI BIT(13) /* multiple block transfer */ +#define UNIPHIER_SD_CMD_RD BIT(12) /* 1: read, 0: write */ +#define UNIPHIER_SD_CMD_DATA BIT(11) /* data transfer */ +#define UNIPHIER_SD_CMD_APP BIT(6) /* ACMD preceded by CMD55 */ +#define UNIPHIER_SD_CMD_NORMAL (0 << 8)/* auto-detect of resp-type */ +#define UNIPHIER_SD_CMD_RSP_NONE (3 << 8)/* response: none */ +#define UNIPHIER_SD_CMD_RSP_R1 (4 << 8)/* response: R1, R5, R6, R7 */ +#define UNIPHIER_SD_CMD_RSP_R1B (5 << 8)/* response: R1b, R5b */ +#define UNIPHIER_SD_CMD_RSP_R2 (6 << 8)/* response: R2 */ +#define UNIPHIER_SD_CMD_RSP_R3 (7 << 8)/* response: R3, R4 */ +#define UNIPHIER_SD_ARG 0x008 /* command argument */ +#define UNIPHIER_SD_STOP 0x010 /* stop action control */ +#define UNIPHIER_SD_STOP_SEC BIT(8) /* use sector count */ +#define UNIPHIER_SD_STOP_STP BIT(0) /* issue CMD12 */ +#define UNIPHIER_SD_SECCNT 0x014 /* sector counter */ +#define UNIPHIER_SD_RSP10 0x018 /* response[39:8] */ +#define UNIPHIER_SD_RSP32 0x020 /* response[71:40] */ +#define UNIPHIER_SD_RSP54 0x028 /* response[103:72] */ +#define UNIPHIER_SD_RSP76 0x030 /* response[127:104] */ +#define UNIPHIER_SD_INFO1 0x038 /* IRQ status 1 */ +#define UNIPHIER_SD_INFO1_CD BIT(5) /* state of card detect */ +#define UNIPHIER_SD_INFO1_INSERT BIT(4) /* card inserted */ +#define UNIPHIER_SD_INFO1_REMOVE BIT(3) /* card removed */ +#define UNIPHIER_SD_INFO1_CMP BIT(2) /* data complete */ +#define UNIPHIER_SD_INFO1_RSP BIT(0) /* response complete */ +#define UNIPHIER_SD_INFO2 0x03c /* IRQ status 2 */ +#define UNIPHIER_SD_INFO2_ERR_ILA BIT(15) /* illegal access err */ +#define UNIPHIER_SD_INFO2_CBSY BIT(14) /* command busy */ +#define UNIPHIER_SD_INFO2_BWE BIT(9) /* write buffer ready */ +#define UNIPHIER_SD_INFO2_BRE BIT(8) /* read buffer ready */ +#define UNIPHIER_SD_INFO2_DAT0 BIT(7) /* SDDAT0 */ +#define UNIPHIER_SD_INFO2_ERR_RTO BIT(6) /* response time out */ +#define UNIPHIER_SD_INFO2_ERR_ILR BIT(5) /* illegal read err */ +#define UNIPHIER_SD_INFO2_ERR_ILW BIT(4) /* illegal write err */ +#define UNIPHIER_SD_INFO2_ERR_TO BIT(3) /* time out error */ +#define UNIPHIER_SD_INFO2_ERR_END BIT(2) /* END bit error */ +#define UNIPHIER_SD_INFO2_ERR_CRC BIT(1) /* CRC error */ +#define UNIPHIER_SD_INFO2_ERR_IDX BIT(0) /* cmd index error */ +#define UNIPHIER_SD_INFO1_MASK 0x040 +#define UNIPHIER_SD_INFO2_MASK 0x044 +#define UNIPHIER_SD_CLKCTL 0x048 /* clock divisor */ +#define UNIPHIER_SD_CLKCTL_DIV_MASK 0x104ff +#define UNIPHIER_SD_CLKCTL_DIV1024 BIT(16) /* SDCLK = CLK / 1024 */ +#define UNIPHIER_SD_CLKCTL_DIV512 BIT(7) /* SDCLK = CLK / 512 */ +#define UNIPHIER_SD_CLKCTL_DIV256 BIT(6) /* SDCLK = CLK / 256 */ +#define UNIPHIER_SD_CLKCTL_DIV128 BIT(5) /* SDCLK = CLK / 128 */ +#define UNIPHIER_SD_CLKCTL_DIV64 BIT(4) /* SDCLK = CLK / 64 */ +#define UNIPHIER_SD_CLKCTL_DIV32 BIT(3) /* SDCLK = CLK / 32 */ +#define UNIPHIER_SD_CLKCTL_DIV16 BIT(2) /* SDCLK = CLK / 16 */ +#define UNIPHIER_SD_CLKCTL_DIV8 BIT(1) /* SDCLK = CLK / 8 */ +#define UNIPHIER_SD_CLKCTL_DIV4 BIT(0) /* SDCLK = CLK / 4 */ +#define UNIPHIER_SD_CLKCTL_DIV2 0 /* SDCLK = CLK / 2 */ +#define UNIPHIER_SD_CLKCTL_DIV1 BIT(10) /* SDCLK = CLK */ +#define UNIPHIER_SD_CLKCTL_OFFEN BIT(9) /* stop SDCLK when unused */ +#define UNIPHIER_SD_CLKCTL_SCLKEN BIT(8) /* SDCLK output enable */ +#define UNIPHIER_SD_SIZE 0x04c /* block size */ +#define UNIPHIER_SD_OPTION 0x050 +#define UNIPHIER_SD_OPTION_WIDTH_MASK (5 << 13) +#define UNIPHIER_SD_OPTION_WIDTH_1 (4 << 13) +#define UNIPHIER_SD_OPTION_WIDTH_4 (0 << 13) +#define UNIPHIER_SD_OPTION_WIDTH_8 (1 << 13) +#define UNIPHIER_SD_BUF 0x060 /* read/write buffer */ +#define UNIPHIER_SD_EXTMODE 0x1b0 +#define UNIPHIER_SD_EXTMODE_DMA_EN BIT(1) /* transfer 1: DMA, 0: pio */ +#define UNIPHIER_SD_SOFT_RST 0x1c0 +#define UNIPHIER_SD_SOFT_RST_RSTX BIT(0) /* reset deassert */ +#define UNIPHIER_SD_VERSION 0x1c4 /* version register */ +#define UNIPHIER_SD_VERSION_IP 0xff /* IP version */ +#define UNIPHIER_SD_HOST_MODE 0x1c8 +#define UNIPHIER_SD_IF_MODE 0x1cc +#define UNIPHIER_SD_IF_MODE_DDR BIT(0) /* DDR mode */ +#define UNIPHIER_SD_VOLT 0x1e4 /* voltage switch */ +#define UNIPHIER_SD_VOLT_MASK (3 << 0) +#define UNIPHIER_SD_VOLT_OFF (0 << 0) +#define UNIPHIER_SD_VOLT_330 (1 << 0)/* 3.3V signal */ +#define UNIPHIER_SD_VOLT_180 (2 << 0)/* 1.8V signal */ +#define UNIPHIER_SD_DMA_MODE 0x410 +#define UNIPHIER_SD_DMA_MODE_DIR_RD BIT(16) /* 1: from device, 0: to dev */ +#define UNIPHIER_SD_DMA_MODE_ADDR_INC BIT(0) /* 1: address inc, 0: fixed */ +#define UNIPHIER_SD_DMA_CTL 0x414 +#define UNIPHIER_SD_DMA_CTL_START BIT(0) /* start DMA (auto cleared) */ +#define UNIPHIER_SD_DMA_RST 0x418 +#define UNIPHIER_SD_DMA_RST_RD BIT(9) +#define UNIPHIER_SD_DMA_RST_WR BIT(8) +#define UNIPHIER_SD_DMA_INFO1 0x420 +#define UNIPHIER_SD_DMA_INFO1_END_RD2 BIT(20) /* DMA from device is complete*/ +#define UNIPHIER_SD_DMA_INFO1_END_RD BIT(17) /* Don't use! Hardware bug */ +#define UNIPHIER_SD_DMA_INFO1_END_WR BIT(16) /* DMA to device is complete */ +#define UNIPHIER_SD_DMA_INFO1_MASK 0x424 +#define UNIPHIER_SD_DMA_INFO2 0x428 +#define UNIPHIER_SD_DMA_INFO2_ERR_RD BIT(17) +#define UNIPHIER_SD_DMA_INFO2_ERR_WR BIT(16) +#define UNIPHIER_SD_DMA_INFO2_MASK 0x42c +#define UNIPHIER_SD_DMA_ADDR_L 0x440 +#define UNIPHIER_SD_DMA_ADDR_H 0x444 + +/* alignment required by the DMA engine of this controller */ +#define UNIPHIER_SD_DMA_MINALIGN 0x10 + +struct uniphier_sd_priv { + struct mmc_config cfg; + struct mmc *mmc; + struct udevice *dev; + void __iomem *regbase; + unsigned long mclk; + unsigned int version; + u32 caps; +#define UNIPHIER_SD_CAP_NONREMOVABLE BIT(0) /* Nonremovable e.g. eMMC */ +#define UNIPHIER_SD_CAP_DMA_INTERNAL BIT(1) /* have internal DMA engine */ +#define UNIPHIER_SD_CAP_DIV1024 BIT(2) /* divisor 1024 is available */ +}; + +static dma_addr_t __dma_map_single(void *ptr, size_t size, + enum dma_data_direction dir) +{ + unsigned long addr = (unsigned long)ptr; + + if (dir == DMA_FROM_DEVICE) + invalidate_dcache_range(addr, addr + size); + else + flush_dcache_range(addr, addr + size); + + return addr; +} + +static void __dma_unmap_single(dma_addr_t addr, size_t size, + enum dma_data_direction dir) +{ + if (dir != DMA_TO_DEVICE) + invalidate_dcache_range(addr, addr + size); +} + +static int uniphier_sd_check_error(struct uniphier_sd_priv *priv) +{ + u32 info2 = readl(priv->regbase + UNIPHIER_SD_INFO2); + + if (info2 & UNIPHIER_SD_INFO2_ERR_RTO) { + /* + * TIMEOUT must be returned for unsupported command. Do not + * display error log since this might be a part of sequence to + * distinguish between SD and MMC. + */ + return TIMEOUT; + } + + if (info2 & UNIPHIER_SD_INFO2_ERR_TO) { + dev_err(priv->dev, "timeout error\n"); + return -ETIMEDOUT; + } + + if (info2 & (UNIPHIER_SD_INFO2_ERR_END | UNIPHIER_SD_INFO2_ERR_CRC | + UNIPHIER_SD_INFO2_ERR_IDX)) { + dev_err(priv->dev, "communication out of sync\n"); + return -EILSEQ; + } + + if (info2 & (UNIPHIER_SD_INFO2_ERR_ILA | UNIPHIER_SD_INFO2_ERR_ILR | + UNIPHIER_SD_INFO2_ERR_ILW)) { + dev_err(priv->dev, "illegal access\n"); + return -EIO; + } + + return 0; +} + +static int uniphier_sd_wait_for_irq(struct uniphier_sd_priv *priv, + unsigned int reg, u32 flag) +{ + long wait = 1000000; + int ret; + + while (!(readl(priv->regbase + reg) & flag)) { + if (wait-- < 0) { + dev_err(priv->dev, "timeout\n"); + return -ETIMEDOUT; + } + + ret = uniphier_sd_check_error(priv); + if (ret) + return ret; + + udelay(1); + } + + return 0; +} + +static int uniphier_sd_pio_read_one_block(struct mmc *mmc, u32 **pbuf, + uint blocksize) +{ + struct uniphier_sd_priv *priv = mmc->priv; + int i, ret; + + /* wait until the buffer is filled with data */ + ret = uniphier_sd_wait_for_irq(priv, UNIPHIER_SD_INFO2, + UNIPHIER_SD_INFO2_BRE); + if (ret) + return ret; + + /* + * Clear the status flag _before_ read the buffer out because + * UNIPHIER_SD_INFO2_BRE is edge-triggered, not level-triggered. + */ + writel(0, priv->regbase + UNIPHIER_SD_INFO2); + + if (likely(IS_ALIGNED((unsigned long)*pbuf, 4))) { + for (i = 0; i < blocksize / 4; i++) + *(*pbuf)++ = readl(priv->regbase + UNIPHIER_SD_BUF); + } else { + for (i = 0; i < blocksize / 4; i++) + put_unaligned(readl(priv->regbase + UNIPHIER_SD_BUF), + (*pbuf)++); + } + + return 0; +} + +static int uniphier_sd_pio_write_one_block(struct mmc *mmc, const u32 **pbuf, + uint blocksize) +{ + struct uniphier_sd_priv *priv = mmc->priv; + int i, ret; + + /* wait until the buffer becomes empty */ + ret = uniphier_sd_wait_for_irq(priv, UNIPHIER_SD_INFO2, + UNIPHIER_SD_INFO2_BWE); + if (ret) + return ret; + + writel(0, priv->regbase + UNIPHIER_SD_INFO2); + + if (likely(IS_ALIGNED((unsigned long)*pbuf, 4))) { + for (i = 0; i < blocksize / 4; i++) + writel(*(*pbuf)++, priv->regbase + UNIPHIER_SD_BUF); + } else { + for (i = 0; i < blocksize / 4; i++) + writel(get_unaligned((*pbuf)++), + priv->regbase + UNIPHIER_SD_BUF); + } + + return 0; +} + +static int uniphier_sd_pio_xfer(struct mmc *mmc, struct mmc_data *data) +{ + u32 *dest = (u32 *)data->dest; + const u32 *src = (const u32 *)data->src; + int i, ret; + + for (i = 0; i < data->blocks; i++) { + if (data->flags & MMC_DATA_READ) + ret = uniphier_sd_pio_read_one_block(mmc, &dest, + data->blocksize); + else + ret = uniphier_sd_pio_write_one_block(mmc, &src, + data->blocksize); + if (ret) + return ret; + } + + return 0; +} + +static void uniphier_sd_dma_start(struct uniphier_sd_priv *priv, + dma_addr_t dma_addr) +{ + u32 tmp; + + writel(0, priv->regbase + UNIPHIER_SD_DMA_INFO1); + writel(0, priv->regbase + UNIPHIER_SD_DMA_INFO2); + + /* enable DMA */ + tmp = readl(priv->regbase + UNIPHIER_SD_EXTMODE); + tmp |= UNIPHIER_SD_EXTMODE_DMA_EN; + writel(tmp, priv->regbase + UNIPHIER_SD_EXTMODE); + + writel(dma_addr & U32_MAX, priv->regbase + UNIPHIER_SD_DMA_ADDR_L); + + /* suppress the warning "right shift count >= width of type" */ + dma_addr >>= min_t(int, 32, 8 * sizeof(dma_addr)); + + writel(dma_addr & U32_MAX, priv->regbase + UNIPHIER_SD_DMA_ADDR_H); + + writel(UNIPHIER_SD_DMA_CTL_START, priv->regbase + UNIPHIER_SD_DMA_CTL); +} + +static int uniphier_sd_dma_wait_for_irq(struct uniphier_sd_priv *priv, u32 flag, + unsigned int blocks) +{ + long wait = 1000000 + 10 * blocks; + + while (!(readl(priv->regbase + UNIPHIER_SD_DMA_INFO1) & flag)) { + if (wait-- < 0) { + dev_err(priv->dev, "timeout during DMA\n"); + return -ETIMEDOUT; + } + + udelay(10); + } + + if (readl(priv->regbase + UNIPHIER_SD_DMA_INFO2)) { + dev_err(priv->dev, "error during DMA\n"); + return -EIO; + } + + return 0; +} + +static int uniphier_sd_dma_xfer(struct mmc *mmc, struct mmc_data *data) +{ + struct uniphier_sd_priv *priv = mmc->priv; + size_t len = data->blocks * data->blocksize; + void *buf; + enum dma_data_direction dir; + dma_addr_t dma_addr; + u32 poll_flag, tmp; + int ret; + + tmp = readl(priv->regbase + UNIPHIER_SD_DMA_MODE); + + if (data->flags & MMC_DATA_READ) { + buf = data->dest; + dir = DMA_FROM_DEVICE; + poll_flag = UNIPHIER_SD_DMA_INFO1_END_RD2; + tmp |= UNIPHIER_SD_DMA_MODE_DIR_RD; + } else { + buf = (void *)data->src; + dir = DMA_TO_DEVICE; + poll_flag = UNIPHIER_SD_DMA_INFO1_END_WR; + tmp &= ~UNIPHIER_SD_DMA_MODE_DIR_RD; + } + + writel(tmp, priv->regbase + UNIPHIER_SD_DMA_MODE); + + dma_addr = __dma_map_single(buf, len, dir); + + uniphier_sd_dma_start(priv, dma_addr); + + ret = uniphier_sd_dma_wait_for_irq(priv, poll_flag, data->blocks); + + __dma_unmap_single(dma_addr, len, dir); + + return ret; +} + +/* check if the address is DMA'able */ +static bool uniphier_sd_addr_is_dmaable(unsigned long addr) +{ + if (!IS_ALIGNED(addr, UNIPHIER_SD_DMA_MINALIGN)) + return false; + +#if defined(CONFIG_ARCH_UNIPHIER) && !defined(CONFIG_ARM64) && \ + defined(CONFIG_SPL_BUILD) + /* + * For UniPhier ARMv7 SoCs, the stack is allocated in the locked ways + * of L2, which is unreachable from the DMA engine. + */ + if (addr < CONFIG_SPL_STACK) + return false; +#endif + + return true; +} + +static int uniphier_sd_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct uniphier_sd_priv *priv = mmc->priv; + int ret; + u32 tmp; + + if (readl(priv->regbase + UNIPHIER_SD_INFO2) & UNIPHIER_SD_INFO2_CBSY) { + dev_err(priv->dev, "command busy\n"); + return -EBUSY; + } + + /* clear all status flags */ + writel(0, priv->regbase + UNIPHIER_SD_INFO1); + writel(0, priv->regbase + UNIPHIER_SD_INFO2); + + /* disable DMA once */ + tmp = readl(priv->regbase + UNIPHIER_SD_EXTMODE); + tmp &= ~UNIPHIER_SD_EXTMODE_DMA_EN; + writel(tmp, priv->regbase + UNIPHIER_SD_EXTMODE); + + writel(cmd->cmdarg, priv->regbase + UNIPHIER_SD_ARG); + + tmp = cmd->cmdidx; + + if (data) { + writel(data->blocksize, priv->regbase + UNIPHIER_SD_SIZE); + writel(data->blocks, priv->regbase + UNIPHIER_SD_SECCNT); + + /* Do not send CMD12 automatically */ + tmp |= UNIPHIER_SD_CMD_NOSTOP | UNIPHIER_SD_CMD_DATA; + + if (data->blocks > 1) + tmp |= UNIPHIER_SD_CMD_MULTI; + + if (data->flags & MMC_DATA_READ) + tmp |= UNIPHIER_SD_CMD_RD; + } + + /* + * Do not use the response type auto-detection on this hardware. + * CMD8, for example, has different response types on SD and eMMC, + * while this controller always assumes the response type for SD. + * Set the response type manually. + */ + switch (cmd->resp_type) { + case MMC_RSP_NONE: + tmp |= UNIPHIER_SD_CMD_RSP_NONE; + break; + case MMC_RSP_R1: + tmp |= UNIPHIER_SD_CMD_RSP_R1; + break; + case MMC_RSP_R1b: + tmp |= UNIPHIER_SD_CMD_RSP_R1B; + break; + case MMC_RSP_R2: + tmp |= UNIPHIER_SD_CMD_RSP_R2; + break; + case MMC_RSP_R3: + tmp |= UNIPHIER_SD_CMD_RSP_R3; + break; + default: + dev_err(priv->dev, "unknown response type\n"); + return -EINVAL; + } + + dev_dbg(priv->dev, "sending CMD%d (SD_CMD=%08x, SD_ARG=%08x)\n", + cmd->cmdidx, tmp, cmd->cmdarg); + writel(tmp, priv->regbase + UNIPHIER_SD_CMD); + + ret = uniphier_sd_wait_for_irq(priv, UNIPHIER_SD_INFO1, + UNIPHIER_SD_INFO1_RSP); + if (ret) + return ret; + + if (cmd->resp_type & MMC_RSP_136) { + u32 rsp_127_104 = readl(priv->regbase + UNIPHIER_SD_RSP76); + u32 rsp_103_72 = readl(priv->regbase + UNIPHIER_SD_RSP54); + u32 rsp_71_40 = readl(priv->regbase + UNIPHIER_SD_RSP32); + u32 rsp_39_8 = readl(priv->regbase + UNIPHIER_SD_RSP10); + + cmd->response[0] = (rsp_127_104 & 0xffffff) << 8 | + (rsp_103_72 & 0xff); + cmd->response[1] = (rsp_103_72 & 0xffffff) << 8 | + (rsp_71_40 & 0xff); + cmd->response[2] = (rsp_71_40 & 0xffffff) << 8 | + (rsp_39_8 & 0xff); + cmd->response[3] = (rsp_39_8 & 0xffffff) << 8; + } else { + /* bit 39-8 */ + cmd->response[0] = readl(priv->regbase + UNIPHIER_SD_RSP10); + } + + if (data) { + /* use DMA if the HW supports it and the buffer is aligned */ + if (priv->caps & UNIPHIER_SD_CAP_DMA_INTERNAL && + uniphier_sd_addr_is_dmaable((long)data->src)) + ret = uniphier_sd_dma_xfer(mmc, data); + else + ret = uniphier_sd_pio_xfer(mmc, data); + + ret = uniphier_sd_wait_for_irq(priv, UNIPHIER_SD_INFO1, + UNIPHIER_SD_INFO1_CMP); + if (ret) + return ret; + } + + return ret; +} + +static void uniphier_sd_set_bus_width(struct uniphier_sd_priv *priv, + struct mmc *mmc) +{ + u32 val, tmp; + + switch (mmc->bus_width) { + case 1: + val = UNIPHIER_SD_OPTION_WIDTH_1; + break; + case 4: + val = UNIPHIER_SD_OPTION_WIDTH_4; + break; + case 8: + val = UNIPHIER_SD_OPTION_WIDTH_8; + break; + default: + BUG(); + break; + } + + tmp = readl(priv->regbase + UNIPHIER_SD_OPTION); + tmp &= ~UNIPHIER_SD_OPTION_WIDTH_MASK; + tmp |= val; + writel(tmp, priv->regbase + UNIPHIER_SD_OPTION); +} + +static void uniphier_sd_set_ddr_mode(struct uniphier_sd_priv *priv, + struct mmc *mmc) +{ + u32 tmp; + + tmp = readl(priv->regbase + UNIPHIER_SD_IF_MODE); + if (mmc->ddr_mode) + tmp |= UNIPHIER_SD_IF_MODE_DDR; + else + tmp &= ~UNIPHIER_SD_IF_MODE_DDR; + writel(tmp, priv->regbase + UNIPHIER_SD_IF_MODE); +} + +static void uniphier_sd_set_clk_rate(struct uniphier_sd_priv *priv, + struct mmc *mmc) +{ + unsigned int divisor; + u32 val, tmp; + + if (!mmc->clock) + return; + + divisor = DIV_ROUND_UP(priv->mclk, mmc->clock); + + if (divisor <= 1) + val = UNIPHIER_SD_CLKCTL_DIV1; + else if (divisor <= 2) + val = UNIPHIER_SD_CLKCTL_DIV2; + else if (divisor <= 4) + val = UNIPHIER_SD_CLKCTL_DIV4; + else if (divisor <= 8) + val = UNIPHIER_SD_CLKCTL_DIV8; + else if (divisor <= 16) + val = UNIPHIER_SD_CLKCTL_DIV16; + else if (divisor <= 32) + val = UNIPHIER_SD_CLKCTL_DIV32; + else if (divisor <= 64) + val = UNIPHIER_SD_CLKCTL_DIV64; + else if (divisor <= 128) + val = UNIPHIER_SD_CLKCTL_DIV128; + else if (divisor <= 256) + val = UNIPHIER_SD_CLKCTL_DIV256; + else if (divisor <= 512 || !(priv->caps & UNIPHIER_SD_CAP_DIV1024)) + val = UNIPHIER_SD_CLKCTL_DIV512; + else + val = UNIPHIER_SD_CLKCTL_DIV1024; + + tmp = readl(priv->regbase + UNIPHIER_SD_CLKCTL); + + /* stop the clock before changing its rate to avoid a glitch signal */ + tmp &= ~UNIPHIER_SD_CLKCTL_SCLKEN; + writel(tmp, priv->regbase + UNIPHIER_SD_CLKCTL); + + tmp &= ~UNIPHIER_SD_CLKCTL_DIV_MASK; + tmp |= val | UNIPHIER_SD_CLKCTL_OFFEN; + writel(tmp, priv->regbase + UNIPHIER_SD_CLKCTL); + + tmp |= UNIPHIER_SD_CLKCTL_SCLKEN; + writel(tmp, priv->regbase + UNIPHIER_SD_CLKCTL); +} + +static void uniphier_sd_set_ios(struct mmc *mmc) +{ + struct uniphier_sd_priv *priv = mmc->priv; + + dev_dbg(priv->dev, "clock %uHz, DDRmode %d, width %u\n", + mmc->clock, mmc->ddr_mode, mmc->bus_width); + + uniphier_sd_set_bus_width(priv, mmc); + uniphier_sd_set_ddr_mode(priv, mmc); + uniphier_sd_set_clk_rate(priv, mmc); + + udelay(1000); +} + +static int uniphier_sd_init(struct mmc *mmc) +{ + struct uniphier_sd_priv *priv = mmc->priv; + u32 tmp; + + /* soft reset of the host */ + tmp = readl(priv->regbase + UNIPHIER_SD_SOFT_RST); + tmp &= ~UNIPHIER_SD_SOFT_RST_RSTX; + writel(tmp, priv->regbase + UNIPHIER_SD_SOFT_RST); + tmp |= UNIPHIER_SD_SOFT_RST_RSTX; + writel(tmp, priv->regbase + UNIPHIER_SD_SOFT_RST); + + /* FIXME: implement eMMC hw_reset */ + + writel(UNIPHIER_SD_STOP_SEC, priv->regbase + UNIPHIER_SD_STOP); + + /* + * Connected to 32bit AXI. + * This register dropped backward compatibility at version 0x10. + * Write an appropriate value depending on the IP version. + */ + writel(priv->version >= 0x10 ? 0x00000101 : 0x00000000, + priv->regbase + UNIPHIER_SD_HOST_MODE); + + if (priv->caps & UNIPHIER_SD_CAP_DMA_INTERNAL) { + tmp = readl(priv->regbase + UNIPHIER_SD_DMA_MODE); + tmp |= UNIPHIER_SD_DMA_MODE_ADDR_INC; + writel(tmp, priv->regbase + UNIPHIER_SD_DMA_MODE); + } + + return 0; +} + +static int uniphier_sd_getcd(struct mmc *mmc) +{ + struct uniphier_sd_priv *priv = mmc->priv; + + if (priv->caps & UNIPHIER_SD_CAP_NONREMOVABLE) + return 1; + + return !!(readl(priv->regbase + UNIPHIER_SD_INFO1) & + UNIPHIER_SD_INFO1_CD); +} + +static const struct mmc_ops uniphier_sd_ops = { + .send_cmd = uniphier_sd_send_cmd, + .set_ios = uniphier_sd_set_ios, + .init = uniphier_sd_init, + .getcd = uniphier_sd_getcd, +}; + +int uniphier_sd_probe(struct udevice *dev) +{ + struct uniphier_sd_priv *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + fdt_addr_t base; + fdt_size_t size; + struct udevice *clk_dev; + int clk_id; + int ret; + + priv->dev = dev; + + base = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", &size); + priv->regbase = map_sysmem(base, size); + if (!priv->regbase) + return -ENOMEM; + + clk_id = clk_get_by_index(dev, 0, &clk_dev); + if (clk_id < 0) { + dev_err(dev, "failed to get host clock\n"); + return clk_id; + } + + /* set to max rate */ + priv->mclk = clk_set_periph_rate(clk_dev, clk_id, ULONG_MAX); + if (IS_ERR_VALUE(priv->mclk)) { + dev_err(dev, "failed to set rate for host clock\n"); + return priv->mclk; + } + + ret = clk_enable(clk_dev, clk_id); + if (ret) { + dev_err(dev, "failed to enable host clock\n"); + return ret; + } + + priv->cfg.name = dev->name; + priv->cfg.ops = &uniphier_sd_ops; + priv->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS; + + switch (fdtdec_get_int(gd->fdt_blob, dev->of_offset, "bus-width", 1)) { + case 8: + priv->cfg.host_caps |= MMC_MODE_8BIT; + break; + case 4: + priv->cfg.host_caps |= MMC_MODE_4BIT; + break; + case 1: + break; + default: + dev_err(dev, "Invalid "bus-width" value\n"); + return -EINVAL; + } + + if (fdt_get_property(gd->fdt_blob, dev->of_offset, "non-removable", + NULL)) + priv->caps |= UNIPHIER_SD_CAP_NONREMOVABLE; + + priv->version = readl(priv->regbase + UNIPHIER_SD_VERSION) & + UNIPHIER_SD_VERSION_IP; + dev_dbg(dev, "version %x\n", priv->version); + if (priv->version >= 0x10) { + priv->caps |= UNIPHIER_SD_CAP_DMA_INTERNAL; + priv->caps |= UNIPHIER_SD_CAP_DIV1024; + } + + priv->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34; + priv->cfg.f_min = priv->mclk / + (priv->caps & UNIPHIER_SD_CAP_DIV1024 ? 1024 : 512); + priv->cfg.f_max = priv->mclk; + priv->cfg.b_max = U32_MAX; /* max value of UNIPHIER_SD_SECCNT */ + + priv->mmc = mmc_create(&priv->cfg, priv); + if (!priv->mmc) + return -EIO; + + upriv->mmc = priv->mmc; + + return 0; +} + +int uniphier_sd_remove(struct udevice *dev) +{ + struct uniphier_sd_priv *priv = dev_get_priv(dev); + + unmap_sysmem(priv->regbase); + mmc_destroy(priv->mmc); + + return 0; +} + +static const struct udevice_id uniphier_sd_match[] = { + { .compatible = "socionext,uniphier-sdhc" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(uniphier_mmc) = { + .name = "uniphier-mmc", + .id = UCLASS_MMC, + .of_match = uniphier_sd_match, + .probe = uniphier_sd_probe, + .remove = uniphier_sd_remove, + .priv_auto_alloc_size = sizeof(struct uniphier_sd_priv), +};

Enable the driver in all UniPhier defconfig files and add some needed defines to the common files.
Signed-off-by: Masahiro Yamada yamada.masahiro@socionext.com ---
Changes in v4: None Changes in v3: None Changes in v2: None
arch/arm/Kconfig | 1 + configs/uniphier_ld4_sld8_defconfig | 1 + configs/uniphier_pro4_defconfig | 1 + configs/uniphier_pro5_defconfig | 1 + configs/uniphier_pxs2_ld6b_defconfig | 1 + configs/uniphier_sld3_defconfig | 1 + include/configs/uniphier.h | 4 ++++ 7 files changed, 10 insertions(+)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 94bd7ec..37b20ff 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -700,6 +700,7 @@ config ARCH_UNIPHIER select DM_GPIO select DM_SERIAL select DM_I2C + select DM_MMC help Support for UniPhier SoC family developed by Socionext Inc. (formerly, System LSI Business Division of Panasonic Corporation) diff --git a/configs/uniphier_ld4_sld8_defconfig b/configs/uniphier_ld4_sld8_defconfig index dbee08e..892bccc 100644 --- a/configs/uniphier_ld4_sld8_defconfig +++ b/configs/uniphier_ld4_sld8_defconfig @@ -21,6 +21,7 @@ CONFIG_CMD_TIME=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_SIMPLE_BUS=y CONFIG_GPIO_UNIPHIER=y +CONFIG_MMC_UNIPHIER=y CONFIG_NAND_DENALI=y CONFIG_SYS_NAND_DENALI_64BIT=y CONFIG_NAND_DENALI_SPARE_AREA_SKIP_BYTES=8 diff --git a/configs/uniphier_pro4_defconfig b/configs/uniphier_pro4_defconfig index 3c2f7b0..45ef883 100644 --- a/configs/uniphier_pro4_defconfig +++ b/configs/uniphier_pro4_defconfig @@ -20,6 +20,7 @@ CONFIG_CMD_TIME=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_SIMPLE_BUS=y CONFIG_GPIO_UNIPHIER=y +CONFIG_MMC_UNIPHIER=y CONFIG_NAND_DENALI=y CONFIG_SYS_NAND_DENALI_64BIT=y CONFIG_NAND_DENALI_SPARE_AREA_SKIP_BYTES=8 diff --git a/configs/uniphier_pro5_defconfig b/configs/uniphier_pro5_defconfig index cf5f1ce..0029cd3 100644 --- a/configs/uniphier_pro5_defconfig +++ b/configs/uniphier_pro5_defconfig @@ -20,6 +20,7 @@ CONFIG_CMD_TIME=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_SIMPLE_BUS=y CONFIG_GPIO_UNIPHIER=y +CONFIG_MMC_UNIPHIER=y CONFIG_NAND_DENALI=y CONFIG_SYS_NAND_DENALI_64BIT=y CONFIG_NAND_DENALI_SPARE_AREA_SKIP_BYTES=8 diff --git a/configs/uniphier_pxs2_ld6b_defconfig b/configs/uniphier_pxs2_ld6b_defconfig index 00a2900..0115c21 100644 --- a/configs/uniphier_pxs2_ld6b_defconfig +++ b/configs/uniphier_pxs2_ld6b_defconfig @@ -21,6 +21,7 @@ CONFIG_CMD_TIME=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_SIMPLE_BUS=y CONFIG_GPIO_UNIPHIER=y +CONFIG_MMC_UNIPHIER=y CONFIG_NAND_DENALI=y CONFIG_SYS_NAND_DENALI_64BIT=y CONFIG_NAND_DENALI_SPARE_AREA_SKIP_BYTES=8 diff --git a/configs/uniphier_sld3_defconfig b/configs/uniphier_sld3_defconfig index 013fc8a..5f0d678 100644 --- a/configs/uniphier_sld3_defconfig +++ b/configs/uniphier_sld3_defconfig @@ -18,6 +18,7 @@ CONFIG_CMD_TIME=y # CONFIG_CMD_MISC is not set CONFIG_NET_RANDOM_ETHADDR=y CONFIG_GPIO_UNIPHIER=y +CONFIG_MMC_UNIPHIER=y CONFIG_NAND_DENALI=y CONFIG_SYS_NAND_DENALI_64BIT=y CONFIG_NAND_DENALI_SPARE_AREA_SKIP_BYTES=8 diff --git a/include/configs/uniphier.h b/include/configs/uniphier.h index fcec0c0..9d14155 100644 --- a/include/configs/uniphier.h +++ b/include/configs/uniphier.h @@ -146,6 +146,10 @@ #define CONFIG_FAT_WRITE #define CONFIG_DOS_PARTITION
+/* SD/MMC */ +#define CONFIG_CMD_MMC +#define CONFIG_GENERIC_MMC + /* memtest works on */ #define CONFIG_SYS_MEMTEST_START CONFIG_SYS_SDRAM_BASE #define CONFIG_SYS_MEMTEST_END (CONFIG_SYS_SDRAM_BASE + 0x01000000)

This host controller is available for all UniPhier SoCs.
Signed-off-by: Masahiro Yamada yamada.masahiro@socionext.com ---
Changes in v4: None Changes in v3: None Changes in v2: None
arch/arm/dts/uniphier-ph1-ld4-ref.dts | 4 +++ arch/arm/dts/uniphier-ph1-ld4.dtsi | 25 +++++++++++++++++++ arch/arm/dts/uniphier-ph1-ld6b-ref.dts | 4 +++ arch/arm/dts/uniphier-ph1-pro4-ace.dts | 4 +++ arch/arm/dts/uniphier-ph1-pro4-ref.dts | 8 ++++++ arch/arm/dts/uniphier-ph1-pro4-sanji.dts | 4 +++ arch/arm/dts/uniphier-ph1-pro4.dtsi | 37 ++++++++++++++++++++++++++++ arch/arm/dts/uniphier-ph1-pro5-4kbox.dts | 8 ++++++ arch/arm/dts/uniphier-ph1-pro5.dtsi | 24 ++++++++++++++++++ arch/arm/dts/uniphier-ph1-sld3-ref.dts | 4 +++ arch/arm/dts/uniphier-ph1-sld3.dtsi | 19 ++++++++++++++ arch/arm/dts/uniphier-ph1-sld8-ref.dts | 4 +++ arch/arm/dts/uniphier-ph1-sld8.dtsi | 25 +++++++++++++++++++ arch/arm/dts/uniphier-pinctrl.dtsi | 15 +++++++++++ arch/arm/dts/uniphier-proxstream2-gentil.dts | 4 +++ arch/arm/dts/uniphier-proxstream2-vodka.dts | 4 +++ arch/arm/dts/uniphier-proxstream2.dtsi | 24 ++++++++++++++++++ 17 files changed, 217 insertions(+)
diff --git a/arch/arm/dts/uniphier-ph1-ld4-ref.dts b/arch/arm/dts/uniphier-ph1-ld4-ref.dts index f62916d..d7b0007 100644 --- a/arch/arm/dts/uniphier-ph1-ld4-ref.dts +++ b/arch/arm/dts/uniphier-ph1-ld4-ref.dts @@ -51,6 +51,10 @@ status = "okay"; };
+&sd { + status = "okay"; +}; + &usb0 { status = "okay"; }; diff --git a/arch/arm/dts/uniphier-ph1-ld4.dtsi b/arch/arm/dts/uniphier-ph1-ld4.dtsi index f13c6db..5ae029e 100644 --- a/arch/arm/dts/uniphier-ph1-ld4.dtsi +++ b/arch/arm/dts/uniphier-ph1-ld4.dtsi @@ -220,6 +220,31 @@ clock-frequency = <100000>; };
+ sd: sdhc@5a400000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x5a400000 0x200>; + interrupts = <0 76 4>; + pinctrl-names = "default", "1.8v"; + pinctrl-0 = <&pinctrl_sd>; + pinctrl-1 = <&pinctrl_sd_1v8>; + clocks = <&mio 0>; + bus-width = <4>; + }; + + emmc: sdhc@5a500000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x5a500000 0x200>; + interrupts = <0 78 4>; + pinctrl-names = "default", "1.8v"; + pinctrl-0 = <&pinctrl_emmc>; + pinctrl-1 = <&pinctrl_emmc_1v8>; + clocks = <&mio 1>; + bus-width = <8>; + non-removable; + }; + usb0: usb@5a800100 { compatible = "socionext,uniphier-ehci", "generic-ehci"; status = "disabled"; diff --git a/arch/arm/dts/uniphier-ph1-ld6b-ref.dts b/arch/arm/dts/uniphier-ph1-ld6b-ref.dts index dca408b..13a29fd 100644 --- a/arch/arm/dts/uniphier-ph1-ld6b-ref.dts +++ b/arch/arm/dts/uniphier-ph1-ld6b-ref.dts @@ -53,6 +53,10 @@ status = "okay"; };
+&sd { + status = "okay"; +}; + &usb0 { status = "okay"; }; diff --git a/arch/arm/dts/uniphier-ph1-pro4-ace.dts b/arch/arm/dts/uniphier-ph1-pro4-ace.dts index 6e741ea..37e0853 100644 --- a/arch/arm/dts/uniphier-ph1-pro4-ace.dts +++ b/arch/arm/dts/uniphier-ph1-pro4-ace.dts @@ -69,6 +69,10 @@ status = "okay"; };
+&sd { + status = "okay"; +}; + &usb0 { status = "okay"; }; diff --git a/arch/arm/dts/uniphier-ph1-pro4-ref.dts b/arch/arm/dts/uniphier-ph1-pro4-ref.dts index 202a642..07a9783 100644 --- a/arch/arm/dts/uniphier-ph1-pro4-ref.dts +++ b/arch/arm/dts/uniphier-ph1-pro4-ref.dts @@ -54,6 +54,14 @@ status = "okay"; };
+&sd { + status = "okay"; +}; + +&sd1 { + status = "okay"; +}; + &usb0 { status = "okay"; }; diff --git a/arch/arm/dts/uniphier-ph1-pro4-sanji.dts b/arch/arm/dts/uniphier-ph1-pro4-sanji.dts index 91a71ef..1ca1042 100644 --- a/arch/arm/dts/uniphier-ph1-pro4-sanji.dts +++ b/arch/arm/dts/uniphier-ph1-pro4-sanji.dts @@ -64,6 +64,10 @@ status = "okay"; };
+&emmc { + status = "okay"; +}; + &usb0 { status = "okay"; }; diff --git a/arch/arm/dts/uniphier-ph1-pro4.dtsi b/arch/arm/dts/uniphier-ph1-pro4.dtsi index 6637aea..d5767b6 100644 --- a/arch/arm/dts/uniphier-ph1-pro4.dtsi +++ b/arch/arm/dts/uniphier-ph1-pro4.dtsi @@ -343,6 +343,43 @@ clock-frequency = <400000>; };
+ sd: sdhc@5a400000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x5a400000 0x200>; + interrupts = <0 76 4>; + pinctrl-names = "default", "1.8v"; + pinctrl-0 = <&pinctrl_sd>; + pinctrl-1 = <&pinctrl_sd_1v8>; + clocks = <&mio 0>; + bus-width = <4>; + }; + + emmc: sdhc@5a500000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x5a500000 0x200>; + interrupts = <0 78 4>; + pinctrl-names = "default", "1.8v"; + pinctrl-0 = <&pinctrl_emmc>; + pinctrl-1 = <&pinctrl_emmc_1v8>; + clocks = <&mio 1>; + bus-width = <8>; + non-removable; + }; + + sd1: sdhc@5a600000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x5a600000 0x200>; + interrupts = <0 85 4>; + pinctrl-names = "default", "1.8v"; + pinctrl-0 = <&pinctrl_sd1>; + pinctrl-1 = <&pinctrl_sd1_1v8>; + clocks = <&mio 2>; + bus-width = <4>; + }; + usb2: usb@5a800100 { compatible = "socionext,uniphier-ehci", "generic-ehci"; status = "disabled"; diff --git a/arch/arm/dts/uniphier-ph1-pro5-4kbox.dts b/arch/arm/dts/uniphier-ph1-pro5-4kbox.dts index 02a3362..cbdc3eb 100644 --- a/arch/arm/dts/uniphier-ph1-pro5-4kbox.dts +++ b/arch/arm/dts/uniphier-ph1-pro5-4kbox.dts @@ -47,6 +47,14 @@ status = "okay"; };
+&emmc { + status = "okay"; +}; + +&sd { + status = "okay"; +}; + /* for U-Boot only */ / { soc { diff --git a/arch/arm/dts/uniphier-ph1-pro5.dtsi b/arch/arm/dts/uniphier-ph1-pro5.dtsi index 67a435e..bd1b4b1 100644 --- a/arch/arm/dts/uniphier-ph1-pro5.dtsi +++ b/arch/arm/dts/uniphier-ph1-pro5.dtsi @@ -355,6 +355,30 @@ clock-frequency = <400000>; };
+ emmc: sdhc@68400000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x68400000 0x800>; + interrupts = <0 78 4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_emmc>; + clocks = <&mio 1>; + bus-width = <8>; + non-removable; + }; + + sd: sdhc@68800000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x68800000 0x800>; + interrupts = <0 76 4>; + pinctrl-names = "default", "1.8v"; + pinctrl-0 = <&pinctrl_sd>; + pinctrl-1 = <&pinctrl_sd_1v8>; + clocks = <&mio 0>; + bus-width = <4>; + }; + usb0: usb@65a00000 { compatible = "socionext,uniphier-xhci", "generic-xhci"; status = "disabled"; diff --git a/arch/arm/dts/uniphier-ph1-sld3-ref.dts b/arch/arm/dts/uniphier-ph1-sld3-ref.dts index ff17945..c7213c9 100644 --- a/arch/arm/dts/uniphier-ph1-sld3-ref.dts +++ b/arch/arm/dts/uniphier-ph1-sld3-ref.dts @@ -52,6 +52,10 @@ status = "okay"; };
+&sd { + status = "okay"; +}; + &usb0 { status = "okay"; }; diff --git a/arch/arm/dts/uniphier-ph1-sld3.dtsi b/arch/arm/dts/uniphier-ph1-sld3.dtsi index 9a6ca57..789713d 100644 --- a/arch/arm/dts/uniphier-ph1-sld3.dtsi +++ b/arch/arm/dts/uniphier-ph1-sld3.dtsi @@ -298,6 +298,25 @@ clocks = <&sysctrl 10>, <&sysctrl 18>; };
+ emmc: sdhc@5a400000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x5a400000 0x200>; + interrupts = <0 78 4>; + clocks = <&mio 1>; + bus-width = <8>; + non-removable; + }; + + sd: sdhc@5a500000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x5a500000 0x200>; + interrupts = <0 76 4>; + clocks = <&mio 0>; + bus-width = <4>; + }; + usb0: usb@5a800100 { compatible = "socionext,uniphier-ehci", "generic-ehci"; status = "disabled"; diff --git a/arch/arm/dts/uniphier-ph1-sld8-ref.dts b/arch/arm/dts/uniphier-ph1-sld8-ref.dts index b5b6f65..ec5c5bd 100644 --- a/arch/arm/dts/uniphier-ph1-sld8-ref.dts +++ b/arch/arm/dts/uniphier-ph1-sld8-ref.dts @@ -51,6 +51,10 @@ status = "okay"; };
+&sd { + status = "okay"; +}; + &usb0 { status = "okay"; }; diff --git a/arch/arm/dts/uniphier-ph1-sld8.dtsi b/arch/arm/dts/uniphier-ph1-sld8.dtsi index 985848a..61e0b45 100644 --- a/arch/arm/dts/uniphier-ph1-sld8.dtsi +++ b/arch/arm/dts/uniphier-ph1-sld8.dtsi @@ -220,6 +220,31 @@ clock-frequency = <100000>; };
+ sd: sdhc@5a400000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x5a400000 0x200>; + interrupts = <0 76 4>; + pinctrl-names = "default", "1.8v"; + pinctrl-0 = <&pinctrl_sd>; + pinctrl-1 = <&pinctrl_sd_1v8>; + clocks = <&mio 0>; + bus-width = <4>; + }; + + emmc: sdhc@5a500000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + interrupts = <0 78 4>; + reg = <0x5a500000 0x200>; + pinctrl-names = "default", "1.8v"; + pinctrl-0 = <&pinctrl_emmc>; + pinctrl-1 = <&pinctrl_emmc_1v8>; + clocks = <&mio 1>; + bus-width = <8>; + non-removable; + }; + usb0: usb@5a800100 { compatible = "socionext,uniphier-ehci", "generic-ehci"; status = "disabled"; diff --git a/arch/arm/dts/uniphier-pinctrl.dtsi b/arch/arm/dts/uniphier-pinctrl.dtsi index b1691d0..494139a 100644 --- a/arch/arm/dts/uniphier-pinctrl.dtsi +++ b/arch/arm/dts/uniphier-pinctrl.dtsi @@ -12,6 +12,11 @@ function = "emmc"; };
+ pinctrl_emmc_1v8: emmc_grp_1v8 { + groups = "emmc", "emmc_dat8"; + function = "emmc"; + }; + pinctrl_i2c0: i2c0_grp { groups = "i2c0"; function = "i2c0"; @@ -37,11 +42,21 @@ function = "sd"; };
+ pinctrl_sd_1v8: sd_grp_1v8 { + groups = "sd"; + function = "sd"; + }; + pinctrl_sd1: sd1_grp { groups = "sd1"; function = "sd1"; };
+ pinctrl_sd1_1v8: sd1_grp_1v8 { + groups = "sd1"; + function = "sd1"; + }; + pinctrl_uart0: uart0_grp { groups = "uart0"; function = "uart0"; diff --git a/arch/arm/dts/uniphier-proxstream2-gentil.dts b/arch/arm/dts/uniphier-proxstream2-gentil.dts index dc0def3..c3551fe 100644 --- a/arch/arm/dts/uniphier-proxstream2-gentil.dts +++ b/arch/arm/dts/uniphier-proxstream2-gentil.dts @@ -52,6 +52,10 @@ status = "okay"; };
+&emmc { + status = "okay"; +}; + &usb0 { status = "okay"; }; diff --git a/arch/arm/dts/uniphier-proxstream2-vodka.dts b/arch/arm/dts/uniphier-proxstream2-vodka.dts index 3703ad3..d61e0b6 100644 --- a/arch/arm/dts/uniphier-proxstream2-vodka.dts +++ b/arch/arm/dts/uniphier-proxstream2-vodka.dts @@ -41,6 +41,10 @@ status = "okay"; };
+&emmc { + status = "okay"; +}; + &usb0 { status = "okay"; }; diff --git a/arch/arm/dts/uniphier-proxstream2.dtsi b/arch/arm/dts/uniphier-proxstream2.dtsi index 21fad0c..12968bd 100644 --- a/arch/arm/dts/uniphier-proxstream2.dtsi +++ b/arch/arm/dts/uniphier-proxstream2.dtsi @@ -359,6 +359,30 @@ clock-frequency = <400000>; };
+ emmc: sdhc@5a000000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x5a000000 0x800>; + interrupts = <0 78 4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_emmc>; + clocks = <&mio 1>; + bus-width = <8>; + non-removable; + }; + + sd: sdhc@5a400000 { + compatible = "socionext,uniphier-sdhc"; + status = "disabled"; + reg = <0x5a400000 0x800>; + interrupts = <0 76 4>; + pinctrl-names = "default", "1.8v"; + pinctrl-0 = <&pinctrl_sd>; + pinctrl-1 = <&pinctrl_sd_1v8>; + clocks = <&mio 0>; + bus-width = <4>; + }; + usb0: usb@65a00000 { compatible = "socionext,uniphier-xhci", "generic-xhci"; status = "disabled";

2016-02-18 19:52 GMT+09:00 Masahiro Yamada yamada.masahiro@socionext.com:
Changes in v4:
- Add some comments about HOST_MODE register
- Rename uniphier_sd_wait_irq() to uniphier_sd_wait_for_irq()
Changes in v3:
- Use dev_err/dev_dbg instead of pr_err/pr_debug
- Tidy up uniphier_sd_set_ios()
- Allow to use DMA even in SPL if the target address is DMA'able
Changes in v2:
- Fix the divisor bug on the older IP (on PH1-LD4, PH1-sLD8, PH1-Pro4)
- Increase time out because "mmc erase" can sometimes take long
- Move HOST_MODE register setting to uniphier_sd_init() because this register does not need setting multipule times.
Masahiro Yamada (3): mmc: uniphier: add driver for UniPhier SD/MMC host controller ARM: uniphier: enable UniPhier SD/MMC host driver ARM: dts: uniphier: add SD/MMC host controller nodes
Series, applied to u-boot-uniphier.
participants (1)
-
Masahiro Yamada