X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fmmc%2Fgen_atmel_mci.c;h=1c108b5470dc8f0ed5159bef94e9db65192ffb89;hb=3fa9bc7969c130b373f41731db526a3511f40ea5;hp=7dc4a5de74e88c4f7dd796b6ad95009525a411cb;hpb=5b30997fd26f0e13837e9ba3cd289a037b8353bd;p=u-boot diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index 7dc4a5de74..1c108b5470 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -10,6 +10,8 @@ */ #include +#include +#include #include #include #include @@ -20,6 +22,8 @@ #include #include "atmel_mci.h" +DECLARE_GLOBAL_DATA_PTR; + #ifndef CONFIG_SYS_MMC_CLK_OD # define CONFIG_SYS_MMC_CLK_OD 150000 #endif @@ -32,11 +36,24 @@ # define MCI_BUS 0 #endif +#ifdef CONFIG_DM_MMC +struct atmel_mci_plat { + struct mmc mmc; + struct mmc_config cfg; + struct atmel_mci *mci; +}; +#endif + struct atmel_mci_priv { +#ifndef CONFIG_DM_MMC struct mmc_config cfg; struct atmel_mci *mci; +#endif unsigned int initialized:1; unsigned int curr_clk; +#ifdef CONFIG_DM_MMC + ulong bus_clk_rate; +#endif }; /* Read Atmel MCI IP version */ @@ -57,12 +74,37 @@ static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg) cmdr, cmdr & 0x3F, arg, status, msg); } +static inline void mci_set_blklen(atmel_mci_t *mci, int blklen) +{ + unsigned int version = atmel_mci_get_version(mci); + + blklen &= 0xfffc; + + /* MCI IP version >= 0x200 has blkr */ + if (version >= 0x200) + writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->blkr)), + &mci->blkr); + else + writel(MMCI_BFINS(BLKLEN, blklen, readl(&mci->mr)), &mci->mr); +} + /* Setup for MCI Clock and Block Size */ +#ifdef CONFIG_DM_MMC +static void mci_set_mode(struct udevice *dev, u32 hz, u32 blklen) +{ + struct atmel_mci_plat *plat = dev_get_platdata(dev); + struct atmel_mci_priv *priv = dev_get_priv(dev); + struct mmc *mmc = &plat->mmc; + u32 bus_hz = priv->bus_clk_rate; + atmel_mci_t *mci = plat->mci; +#else static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) { struct atmel_mci_priv *priv = mmc->priv; - atmel_mci_t *mci = priv->mci; u32 bus_hz = get_mci_clk_rate(); + atmel_mci_t *mci = priv->mci; +#endif + u32 clkdiv = 255; unsigned int version = atmel_mci_get_version(mci); u32 clkodd = 0; @@ -96,7 +138,6 @@ static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) priv->curr_clk = bus_hz / (clkdiv * 2 + clkodd + 2); else priv->curr_clk = (bus_hz / (clkdiv + 1)) / 2; - blklen &= 0xfffc; mr = MMCI_BF(CLKDIV, clkdiv); @@ -110,14 +151,10 @@ static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) */ if (version >= 0x500) mr |= MMCI_BF(CLKODD, clkodd); - else - mr |= MMCI_BF(BLKLEN, blklen); writel(mr, &mci->mr); - /* MCI IP version >= 0x200 has blkr */ - if (version >= 0x200) - writel(MMCI_BF(BLKLEN, blklen), &mci->blkr); + mci_set_blklen(mci, blklen); if (mmc->card_caps & mmc->cfg->host_caps & MMC_MODE_HS) writel(MMCI_BIT(HSMODE), &mci->cfg); @@ -202,11 +239,20 @@ io_fail: * Sends a command out on the bus and deals with the block data. * Takes the mmc pointer, a command pointer, and an optional data pointer. */ +#ifdef CONFIG_DM_MMC +static int atmel_mci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct atmel_mci_plat *plat = dev_get_platdata(dev); + struct atmel_mci_priv *priv = dev_get_priv(dev); + atmel_mci_t *mci = plat->mci; +#else static int mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct atmel_mci_priv *priv = mmc->priv; atmel_mci_t *mci = priv->mci; +#endif u32 cmdr; u32 error_flags = 0; u32 status; @@ -219,11 +265,13 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) /* Figure out the transfer arguments */ cmdr = mci_encode_cmd(cmd, data, &error_flags); + mci_set_blklen(mci, data->blocksize); + /* For multi blocks read/write, set the block register */ if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) || (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)) - writel(data->blocks | MMCI_BF(BLKLEN, mmc->read_bl_len), - &mci->blkr); + writel(data->blocks | MMCI_BF(BLKLEN, data->blocksize), + &mci->blkr); /* Send the command */ writel(cmd->cmdarg, &mci->argr); @@ -257,17 +305,15 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) if (data) { u32 word_count, block_count; u32* ioptr; - u32 sys_blocksize, dummy, i; + u32 i; u32 (*mci_data_op) (atmel_mci_t *mci, u32* data, u32 error_flags); if (data->flags & MMC_DATA_READ) { mci_data_op = mci_data_read; - sys_blocksize = mmc->read_bl_len; ioptr = (u32*)data->dest; } else { mci_data_op = mci_data_write; - sys_blocksize = mmc->write_bl_len; ioptr = (u32*)data->src; } @@ -290,16 +336,6 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) 1, cnt, 0); } #endif -#ifdef DEBUG - if (!status && word_count < (sys_blocksize / 4)) - printf("filling rest of block...\n"); -#endif - /* fill the rest of a full block */ - while (!status && word_count < (sys_blocksize / 4)) { - status = mci_data_op(mci, &dummy, - error_flags); - word_count++; - } if (status) { dump_cmd(cmdr, cmd->cmdarg, status, "Data Transfer Failed"); @@ -335,17 +371,29 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) return 0; } +#ifdef CONFIG_DM_MMC +static int atmel_mci_set_ios(struct udevice *dev) +{ + struct atmel_mci_plat *plat = dev_get_platdata(dev); + struct mmc *mmc = mmc_get_mmc_dev(dev); + atmel_mci_t *mci = plat->mci; +#else /* Entered into mmc structure during driver init */ static int mci_set_ios(struct mmc *mmc) { struct atmel_mci_priv *priv = mmc->priv; atmel_mci_t *mci = priv->mci; +#endif int bus_width = mmc->bus_width; unsigned int version = atmel_mci_get_version(mci); int busw; /* Set the clock speed */ +#ifdef CONFIG_DM_MMC + mci_set_mode(dev, mmc->clock, MMC_DEFAULT_BLKLEN); +#else mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN); +#endif /* * set the bus width and select slot for this interface @@ -374,11 +422,18 @@ static int mci_set_ios(struct mmc *mmc) return 0; } +#ifdef CONFIG_DM_MMC +static int atmel_mci_hw_init(struct udevice *dev) +{ + struct atmel_mci_plat *plat = dev_get_platdata(dev); + atmel_mci_t *mci = plat->mci; +#else /* Entered into mmc structure during driver init */ static int mci_init(struct mmc *mmc) { struct atmel_mci_priv *priv = mmc->priv; atmel_mci_t *mci = priv->mci; +#endif /* Initialize controller */ writel(MMCI_BIT(SWRST), &mci->cr); /* soft reset */ @@ -392,11 +447,16 @@ static int mci_init(struct mmc *mmc) writel(~0UL, &mci->idr); /* Set default clocks and blocklen */ +#ifdef CONFIG_DM_MMC + mci_set_mode(dev, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN); +#else mci_set_mode(mmc, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN); +#endif return 0; } +#ifndef CONFIG_DM_MMC static const struct mmc_ops atmel_mci_ops = { .send_cmd = mci_send_cmd, .set_ios = mci_set_ios, @@ -456,3 +516,117 @@ int atmel_mci_init(void *regs) return 0; } +#endif + +#ifdef CONFIG_DM_MMC +static const struct dm_mmc_ops atmel_mci_mmc_ops = { + .send_cmd = atmel_mci_send_cmd, + .set_ios = atmel_mci_set_ios, +}; + +static void atmel_mci_setup_cfg(struct udevice *dev) +{ + struct atmel_mci_plat *plat = dev_get_platdata(dev); + struct atmel_mci_priv *priv = dev_get_priv(dev); + struct mmc_config *cfg; + u32 version; + + cfg = &plat->cfg; + cfg->name = "Atmel mci"; + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + + /* + * If the version is above 3.0, the capabilities of the 8-bit + * bus width and high speed are supported. + */ + version = atmel_mci_get_version(plat->mci); + if ((version & 0xf00) >= 0x300) { + cfg->host_caps = MMC_MODE_8BIT | + MMC_MODE_HS | MMC_MODE_HS_52MHz; + } + + cfg->host_caps |= MMC_MODE_4BIT; + cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + cfg->f_min = priv->bus_clk_rate / (2 * 256); + cfg->f_max = priv->bus_clk_rate / 2; +} + +static int atmel_mci_enable_clk(struct udevice *dev) +{ + struct atmel_mci_priv *priv = dev_get_priv(dev); + struct clk clk; + ulong clk_rate; + int ret = 0; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) { + ret = -EINVAL; + goto failed; + } + + ret = clk_enable(&clk); + if (ret) + goto failed; + + clk_rate = clk_get_rate(&clk); + if (!clk_rate) { + ret = -EINVAL; + goto failed; + } + + priv->bus_clk_rate = clk_rate; + +failed: + clk_free(&clk); + + return ret; +} + +static int atmel_mci_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct atmel_mci_plat *plat = dev_get_platdata(dev); + struct mmc *mmc; + int ret; + + ret = atmel_mci_enable_clk(dev); + if (ret) + return ret; + + plat->mci = (struct atmel_mci *)devfdt_get_addr_ptr(dev); + + atmel_mci_setup_cfg(dev); + + mmc = &plat->mmc; + mmc->cfg = &plat->cfg; + mmc->dev = dev; + upriv->mmc = mmc; + + atmel_mci_hw_init(dev); + + return 0; +} + +static int atmel_mci_bind(struct udevice *dev) +{ + struct atmel_mci_plat *plat = dev_get_platdata(dev); + + return mmc_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id atmel_mci_ids[] = { + { .compatible = "atmel,hsmci" }, + { } +}; + +U_BOOT_DRIVER(atmel_mci) = { + .name = "atmel-mci", + .id = UCLASS_MMC, + .of_match = atmel_mci_ids, + .bind = atmel_mci_bind, + .probe = atmel_mci_probe, + .platdata_auto_alloc_size = sizeof(struct atmel_mci_plat), + .priv_auto_alloc_size = sizeof(struct atmel_mci_priv), + .ops = &atmel_mci_mmc_ops, +}; +#endif