Hi Lin, On 20 October 2015 at 20:37, Lin Huang <h...@rock-chips.com> wrote: > rk3036 mmc driver is similar to dw_mmc, but use external dma, > this patch implment fifo mode, need to do dma mode in future. > > Signed-off-by: Lin Huang <h...@rock-chips.com> > --- > Changes in v1: > - clean copyright announcement > > drivers/mmc/Kconfig | 9 + > drivers/mmc/Makefile | 1 + > drivers/mmc/rockchip_3036_dw_mmc.c | 479 > +++++++++++++++++++++++++++++++++++++ > 3 files changed, 489 insertions(+) > create mode 100644 drivers/mmc/rockchip_3036_dw_mmc.c > > diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig > index 6277f92..38bfb9c 100644 > --- a/drivers/mmc/Kconfig > +++ b/drivers/mmc/Kconfig > @@ -19,6 +19,15 @@ config ROCKCHIP_DWMMC > SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well > as removeable SD and micro-SD cards. > > +config ROCKCHIP_3036_DWMMC > + bool "Rockchip 3036 SD/MMC controller support" > + depends on DM_MMC && OF_CONTROL > + help > + This enables support for the Rockchip 3036 SD/MMM controller, which > is > + based on Designware IP. The device is compatible with at least > + SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well > + as removeable SD and micro-SD cards. > + > config SH_SDHI > bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support" > depends on RMOBILE > diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile > index 99d0295..ff3920a 100644 > --- a/drivers/mmc/Makefile > +++ b/drivers/mmc/Makefile > @@ -30,6 +30,7 @@ obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o > obj-$(CONFIG_X86) += pci_mmc.o > obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o > obj-$(CONFIG_ROCKCHIP_DWMMC) += rockchip_dw_mmc.o > +obj-$(CONFIG_ROCKCHIP_3036_DWMMC) += rockchip_3036_dw_mmc.o > obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o > obj-$(CONFIG_S3C_SDI) += s3c_sdi.o > obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o > diff --git a/drivers/mmc/rockchip_3036_dw_mmc.c > b/drivers/mmc/rockchip_3036_dw_mmc.c > new file mode 100644 > index 0000000..2a2df52 > --- /dev/null > +++ b/drivers/mmc/rockchip_3036_dw_mmc.c > @@ -0,0 +1,479 @@ > +/* > + * (C) Copyright 2015 Rockchip Electronics Co., Ltd > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <clk.h> > +#include <dm.h> > +#include <dwmmc.h> > +#include <errno.h> > +#include <syscon.h> > +#include <asm/arch/clock.h> > +#include <asm/arch/periph.h> > +#include <linux/err.h> > +#include <bouncebuf.h> > +#include <common.h> > +#include <errno.h> > +#include <malloc.h> > +#include <memalign.h> > +#include <mmc.h> > +#include <dwmmc.h> > +#include <asm-generic/errno.h> > + > +DECLARE_GLOBAL_DATA_PTR; > + > +#define PAGE_SIZE 4096 > +#define MMC_GET_FCNT(x) (((x)>>17) & 0x1FF)
Can we use the SHIFT and MASK enums instead (as for clocks)? I'd like to avoid these sort of macro accessors. > + > +struct rockchip_dwmmc_priv { > + struct udevice *clk; > + struct dwmci_host host; > +}; > + > +static int dwmci_wait_reset(struct dwmci_host *host, u32 value) > +{ > + unsigned long timeout = 1000; > + u32 ctrl; > + > + dwmci_writel(host, DWMCI_CTRL, value); > + > + while (timeout--) { > + ctrl = dwmci_readl(host, DWMCI_CTRL); > + if (!(ctrl & DWMCI_RESET_ALL)) > + return 1; The timeout here is somewhat indeterminate, since it does not reference the timer. Unless there is a special reason to do this (in which case we should have a comment here) we should use something like: unsigned long start; start = get_timer(0); do { } while (get_timer(start) < 1000); > + } > + return 0; > +} > + > +static int dwmci_set_transfer_mode(struct dwmci_host *host, > + struct mmc_data *data) > +{ > + unsigned long mode; > + > + mode = DWMCI_CMD_DATA_EXP; > + if (data->flags & MMC_DATA_WRITE) > + mode |= DWMCI_CMD_RW; > + > + return mode; > +} > + > +static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, > + struct mmc_data *data) > +{ > + struct dwmci_host *host = mmc->priv; > + int ret = 0, flags = 0, i; > + unsigned int timeout = 100000; > + u32 retry = 10000; > + u32 mask; > + ulong start = get_timer(0); > + int size; > + unsigned int fifo_len; > + unsigned int *buf = 0; > + > + while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { > + if (get_timer(start) > timeout) { > + debug("%s: Timeout on data busy\n", __func__); > + return TIMEOUT; > + } > + } > + > + dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); > + > + if (data) { > + /* > + * TODO: rk3036 use external DMA, > + * need to support DMA mode in future > + */ > + if (data->flags == MMC_DATA_READ) > + buf = (unsigned int *)data->dest; > + else > + buf = (unsigned int *)data->src; > + dwmci_writel(host, DWMCI_BLKSIZ, data->blocksize); > + dwmci_writel(host, DWMCI_BYTCNT, data->blocksize * > data->blocks); > + dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); > + } > + > + dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); > + > + if (data) > + flags = dwmci_set_transfer_mode(host, data); > + > + if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) > + return -1; > + > + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) > + flags |= DWMCI_CMD_ABORT_STOP; > + else > + flags |= DWMCI_CMD_PRV_DAT_WAIT; > + > + if (cmd->resp_type & MMC_RSP_PRESENT) { > + flags |= DWMCI_CMD_RESP_EXP; > + if (cmd->resp_type & MMC_RSP_136) > + flags |= DWMCI_CMD_RESP_LENGTH; > + } > + > + if (cmd->resp_type & MMC_RSP_CRC) > + flags |= DWMCI_CMD_CHECK_CRC; > + > + flags |= (cmd->cmdidx | DWMCI_CMD_START | DWMCI_CMD_USE_HOLD_REG); > + > + debug("Sending CMD%d\n", cmd->cmdidx); > + > + dwmci_writel(host, DWMCI_CMD, flags); > + > + for (i = 0; i < retry; i++) { > + mask = dwmci_readl(host, DWMCI_RINTSTS); > + if (mask & DWMCI_INTMSK_CDONE) { > + if (!data) > + dwmci_writel(host, DWMCI_RINTSTS, mask); > + break; > + } > + } > + > + if (i == retry) { > + debug("%s: Timeout.\n", __func__); > + return TIMEOUT; > + } > + > + if (mask & DWMCI_INTMSK_RTO) { > + /* > + * Timeout here is not necessarily fatal. (e)MMC cards > + * will splat here when they receive CMD55 as they do > + * not support this command and that is exactly the way > + * to tell them apart from SD cards. Thus, this output > + * below shall be debug(). eMMC cards also do not favor > + * CMD8, please keep that in mind. > + */ > + debug("%s: Response Timeout.\n", __func__); > + return TIMEOUT; > + } else if (mask & DWMCI_INTMSK_RE) { > + debug("%s: Response Error.\n", __func__); > + return -EIO; > + } > + > + if (cmd->resp_type & MMC_RSP_PRESENT) { > + if (cmd->resp_type & MMC_RSP_136) { > + cmd->response[0] = dwmci_readl(host, DWMCI_RESP3); > + cmd->response[1] = dwmci_readl(host, DWMCI_RESP2); > + cmd->response[2] = dwmci_readl(host, DWMCI_RESP1); > + cmd->response[3] = dwmci_readl(host, DWMCI_RESP0); > + } else { > + cmd->response[0] = dwmci_readl(host, DWMCI_RESP0); > + } > + } > + > + if (data) { > + size = data->blocksize * data->blocks / 4; > + start = get_timer(0); > + timeout = 1000; > + for (;;) { > + mask = dwmci_readl(host, DWMCI_RINTSTS); > + /* Error during data transfer. */ > + if (mask & (DWMCI_DATA_ERR | DWMCI_DATA_TOUT)) { > + debug("%s: DATA ERROR!\n", __func__); > + ret = -EINVAL; > + break; > + } > + > + /* > + * TODO: rk3036 use external DMA, > + * need to support DMA mode in future > + */ > + if (data->flags == MMC_DATA_READ) { > + if ((dwmci_readl(host, DWMCI_RINTSTS) && > + DWMCI_INTMSK_RXDR) && size) { > + fifo_len = dwmci_readl(host, > + DWMCI_STATUS); > + fifo_len = MMC_GET_FCNT(fifo_len); > + for (i = 0; i < fifo_len; i++) > + *buf++ = dwmci_readl(host, > + DWMCI_DATA); > + dwmci_writel(host, DWMCI_RINTSTS, > + DWMCI_INTMSK_RXDR); > + size = size > fifo_len ? > + (size - fifo_len) : 0; > + } > + } else { > + if ((dwmci_readl(host, DWMCI_RINTSTS) && > + DWMCI_INTMSK_TXDR) && size) { > + fifo_len = dwmci_readl(host, > + DWMCI_STATUS); > + fifo_len = MMC_GET_FCNT(fifo_len); > + for (i = 0; i < fifo_len; i++) > + dwmci_writel(host, DWMCI_DATA, > + *buf++); > + dwmci_writel(host, DWMCI_RINTSTS, > + DWMCI_INTMSK_TXDR); > + size = size > fifo_len ? > + (size - fifo_len) : 0; > + } > + } > + > + /* Data arrived correctly. */ > + if (mask & DWMCI_INTMSK_DTO) { > + ret = 0; > + break; > + } > + > + /* Check for timeout. */ > + if (get_timer(start) > timeout) { > + debug("%s: Timeout waiting for data!\n", > + __func__); > + ret = TIMEOUT; > + break; > + } > + } > + dwmci_writel(host, DWMCI_RINTSTS, mask); > + } > + > + udelay(100); > + > + return ret; > +} > + > +static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) > +{ > + u32 div, status; > + int timeout = 10000; > + unsigned long sclk; > + > + if ((freq == host->clock) || (freq == 0)) > + return 0; > + /* > + * If host->get_mmc_clk isn't defined, > + * then assume that host->bus_hz is source clock value. > + * host->bus_hz should be set by user. > + */ > + if (host->get_mmc_clk) > + sclk = host->get_mmc_clk(host, freq); > + else if (host->bus_hz) > + sclk = host->bus_hz; > + else { > + debug("%s: Didn't get source clock value.\n", __func__); > + return -EINVAL; > + } > + > + if (sclk == freq) > + div = 0; /* bypass mode */ > + else > + div = DIV_ROUND_UP(sclk, 2 * freq); > + > + dwmci_writel(host, DWMCI_CLKENA, 0); > + dwmci_writel(host, DWMCI_CLKSRC, 0); > + > + dwmci_writel(host, DWMCI_CLKDIV, div); > + dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | > + DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); > + > + do { > + status = dwmci_readl(host, DWMCI_CMD); Similar here. > + if (timeout-- < 0) { > + debug("%s: Timeout!\n", __func__); > + return -ETIMEDOUT; > + } > + } while (status & DWMCI_CMD_START); > + > + dwmci_writel(host, DWMCI_CLKENA, DWMCI_CLKEN_ENABLE | > + DWMCI_CLKEN_LOW_PWR); > + > + dwmci_writel(host, DWMCI_CMD, DWMCI_CMD_PRV_DAT_WAIT | > + DWMCI_CMD_UPD_CLK | DWMCI_CMD_START); > + > + timeout = 10000; > + do { > + status = dwmci_readl(host, DWMCI_CMD); > + if (timeout-- < 0) { > + debug("%s: Timeout!\n", __func__); > + return -ETIMEDOUT; > + } > + } while (status & DWMCI_CMD_START); > + > + host->clock = freq; > + > + return 0; > +} > + > +static void dwmci_set_ios(struct mmc *mmc) > +{ > + struct dwmci_host *host = (struct dwmci_host *)mmc->priv; > + u32 ctype, regs; > + > + debug("Buswidth = %d, clock: %d\n", mmc->bus_width, mmc->clock); > + > + dwmci_setup_bus(host, mmc->clock); > + switch (mmc->bus_width) { > + case 8: > + ctype = DWMCI_CTYPE_8BIT; > + break; > + case 4: > + ctype = DWMCI_CTYPE_4BIT; > + break; > + default: > + ctype = DWMCI_CTYPE_1BIT; > + break; > + } > + > + dwmci_writel(host, DWMCI_CTYPE, ctype); > + > + regs = dwmci_readl(host, DWMCI_UHS_REG); > + if (mmc->ddr_mode) > + regs |= DWMCI_DDR_MODE; > + else > + regs &= ~DWMCI_DDR_MODE; > + > + dwmci_writel(host, DWMCI_UHS_REG, regs); > + > + if (host->clksel) > + host->clksel(host); > +} > + > +static int dwmci_init(struct mmc *mmc) > +{ > + struct dwmci_host *host = mmc->priv; > + > + if (host->board_init) > + host->board_init(host); > + > + dwmci_writel(host, DWMCI_PWREN, 1); > + > + if (!dwmci_wait_reset(host, DWMCI_RESET_ALL)) { > + debug("%s[%d] Fail-reset!!\n", __func__, __LINE__); > + return -EIO; > + } > + > + /* Enumerate at 400KHz */ > + dwmci_setup_bus(host, mmc->cfg->f_min); > + > + dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF); > + dwmci_writel(host, DWMCI_INTMASK, 0); > + > + dwmci_writel(host, DWMCI_TMOUT, 0xFFFFFFFF); > + > + dwmci_writel(host, DWMCI_IDINTEN, 0); > + dwmci_writel(host, DWMCI_BMOD, 1); > + > + if (!host->fifoth_val) { > + uint32_t fifo_size; > + fifo_size = dwmci_readl(host, DWMCI_FIFOTH); > + fifo_size = ((fifo_size & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + > 1; > + host->fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size / 2 - 1) | > + TX_WMARK(fifo_size / 2); > + } > + dwmci_writel(host, DWMCI_FIFOTH, host->fifoth_val); > + > + dwmci_writel(host, DWMCI_CLKENA, 0); > + dwmci_writel(host, DWMCI_CLKSRC, 0); > + > + return 0; > +} > + > +static const struct mmc_ops dwmci_ops = { > + .send_cmd = dwmci_send_cmd, > + .set_ios = dwmci_set_ios, > + .init = dwmci_init, > +}; > + > +int add_dwmci(struct dwmci_host *host, u32 max_clk, u32 min_clk) > +{ > + host->cfg.name = host->name; > + host->cfg.ops = &dwmci_ops; > + host->cfg.f_min = min_clk; > + host->cfg.f_max = max_clk; > + > + host->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; > + > + host->cfg.host_caps = host->caps; > + > + if (host->buswidth == 8) { > + host->cfg.host_caps |= MMC_MODE_8BIT; > + host->cfg.host_caps &= ~MMC_MODE_4BIT; > + } else { > + host->cfg.host_caps |= MMC_MODE_4BIT; > + host->cfg.host_caps &= ~MMC_MODE_8BIT; > + } > + host->cfg.host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz; > + > + host->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; > + > + host->mmc = mmc_create(&host->cfg, host); > + if (host->mmc == NULL) > + return -1; > + > + return 0; > +} > + > +static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq) > +{ > + struct udevice *dev = host->priv; > + struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); > + int ret; > + > + ret = clk_set_periph_rate(priv->clk, PERIPH_ID_SDMMC0 + > host->dev_index, > + freq); > + if (ret < 0) { > + debug("%s: err=%d\n", __func__, ret); > + return ret; > + } > + > + return freq; > +} > + > +static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev) > +{ > + struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); > + struct dwmci_host *host = &priv->host; > + > + host->name = dev->name; > + host->ioaddr = (void *)dev_get_addr(dev); > + host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, > + "bus-width", 4); > + host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk; > + host->priv = dev; > + > + /* use non-removeable as sdcard and emmc as judgement */ > + if (fdtdec_lookup_phandle(gd->fdt_blob, dev->of_offset, > "non-removable") > + == -FDT_ERR_NOTFOUND) > + host->dev_index = (ulong)host->ioaddr == 1; > + > + return 0; > +} > + > +static int rockchip_dwmmc_probe(struct udevice *dev) > +{ > + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); > + struct rockchip_dwmmc_priv *priv = dev_get_priv(dev); > + struct dwmci_host *host = &priv->host; > + u32 minmax[2]; > + int ret; > + > + ret = uclass_get_device(UCLASS_CLK, CLK_GENERAL, &priv->clk); > + if (ret) > + return ret; > + > + ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, > + "clock-freq-min-max", minmax, 2); > + if (!ret) > + ret = add_dwmci(host, minmax[1], minmax[0]); > + if (ret) > + return ret; > + > + upriv->mmc = host->mmc; > + > + return 0; > +} > + > +static const struct udevice_id rockchip_dwmmc_ids[] = { > + { .compatible = "rockchip,rk3288-dw-mshc" }, > + { } > +}; > + > +U_BOOT_DRIVER(rockchip_dwmmc_drv) = { > + .name = "rockchip_3036_dwmmc", > + .id = UCLASS_MMC, > + .of_match = rockchip_dwmmc_ids, > + .ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata, > + .probe = rockchip_dwmmc_probe, > + .priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv), > +}; > -- > 1.9.1 > Regards, Simon _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot