[U-Boot] [PATCH 02/22] ARM: sunxi: MMC driver
Marek Vasut
marex at denx.de
Sun Nov 25 19:09:02 CET 2012
Dear Henrik Nordström,
> This adds a basic MMC driver for Allwinner sun4i/sun5i family of SoC
> this driver is limited to a single MMC channel.
>
> Signed-off-by: Tom Cubie <tangliang at allwinnertech.com>
> Signed-off-by: Henrik Nodstrom <henrik at henriknordstrom.net>
> Signed-off-by: Stefan Roese <sr at denx.de>
> ---
[...]
> +#undef SUNXI_MMCDBG
debug_cond() won't work for you ?
[...]
> + case 0:
> + /* D1-PF0, D0-PF1, CLK-PF2, CMD-PF3, D3-PF4, D4-PF5 */
Magic goo below?
> + writel(0x222222, &gpio_f->cfg[0]);
> + writel(0x555, &gpio_f->pull[0]);
> + writel(0xaaa, &gpio_f->drv[0]);
> + break;
> +
> + case 1:
> +#if CONFIG_MMC1_PG
> + /* PG0-CMD, PG1-CLK, PG2~5-D0~3 : 4 */
> + writel(0x444444, &gpio_g->cfg[0]);
> + writel(0x555, &gpio_g->pull[0]);
> + writel(0xaaa, &gpio_g->drv[0]);
> +#else
> + /* PH22-CMD, PH23-CLK, PH24~27-D0~D3 : 5 */
> + writel(0x55 << 24, &gpio_h->cfg[2]);
> + writel(0x5555, &gpio_h->cfg[3]);
> + writel(0x555 << 12, &gpio_h->pull[1]);
> + writel(0xaaa << 12, &gpio_h->drv[1]);
> +#endif
> + break;
> +
> + case 2:
> + /* CMD-PC6, CLK-PC7, D0-PC8, D1-PC9, D2-PC10, D3-PC11 */
> + writel(0x33 << 24, &gpio_c->cfg[0]);
> + writel(0x3333, &gpio_c->cfg[1]);
> + writel(0x555 << 12, &gpio_c->pull[0]);
> + writel(0xaaa << 12, &gpio_c->drv[0]);
> + break;
> +
> + case 3:
> + /* PI4-CMD, PI5-CLK, PI6~9-D0~D3 : 2 */
> + writel(0x2222 << 16, &gpio_i->cfg[0]);
> + writel(0x22, &gpio_i->cfg[1]);
> + writel(0x555 << 8, &gpio_i->pull[0]);
> + writel(0x555 << 8, &gpio_i->drv[0]);
> + break;
> +
> + default:
> + return -1;
> + }
> +
> + /* config ahb clock */
> + rval = readl(&ccm->ahb_gate0);
> + rval |= (1 << (8 + sdc_no));
> + writel(rval, &ccm->ahb_gate0);
> +
> + /* config mod clock */
> + pll5_clk = clock_get_pll5();
> + if (pll5_clk > 400000000)
> + divider = 4;
> + else
> + divider = 3;
> + writel((1U << 31) | (2U << 24) | divider, mmchost->mclkreg);
> + mmchost->mod_clk = pll5_clk / (divider + 1);
> +
> + dumphex32("ccmu", (char *)SUNXI_CCM_BASE, 0x100);
> + dumphex32("gpio", (char *)SUNXI_PIO_BASE, 0x100);
> + dumphex32("mmc", (char *)mmchost->reg, 0x100);
> +
> + return 0;
> +}
> +
> +static int mmc_update_clk(struct mmc *mmc)
> +{
> + struct sunxi_mmc_host *mmchost = (struct sunxi_mmc_host *)mmc->priv;
> + unsigned int cmd;
> + unsigned timeout = 0xfffff;
> +
> + cmd = (1U << 31) | (1 << 21) | (1 << 13);
> + writel(cmd, &mmchost->reg->cmd);
> + while ((readl(&mmchost->reg->cmd) & 0x80000000) && timeout--)
> + ;
> + if (!timeout)
> + return -1;
> +
> + writel(readl(&mmchost->reg->rint), &mmchost->reg->rint);
> +
> + return 0;
> +}
> +
> +static int mmc_config_clock(struct mmc *mmc, unsigned div)
> +{
> + struct sunxi_mmc_host *mmchost = (struct sunxi_mmc_host *)mmc->priv;
> + unsigned rval = readl(&mmchost->reg->clkcr);
> +
> + /*
> + * CLKCREG[7:0]: divider
> + * CLKCREG[16]: on/off
> + * CLKCREG[17]: power save
> + */
> + /* Disable Clock */
> + rval &= ~(1 << 16);
> + writel(rval, &mmchost->reg->clkcr);
> + if (mmc_update_clk(mmc))
> + return -1;
> +
> + /* Change Divider Factor */
> + rval &= ~(0xFF);
> + rval |= div;
> + writel(rval, &mmchost->reg->clkcr);
> + if (mmc_update_clk(mmc))
> + return -1;
> + /* Re-enable Clock */
> + rval |= (1 << 16);
> + writel(rval, &mmchost->reg->clkcr);
> +
> + if (mmc_update_clk(mmc))
> + return -1;
> +
> + return 0;
> +}
> +
> +static void mmc_set_ios(struct mmc *mmc)
> +{
> + struct sunxi_mmc_host *mmchost = (struct sunxi_mmc_host *)mmc->priv;
> + unsigned int clkdiv = 0;
> +
> + MMCDBG("set ios: bus_width: %x, clock: %d, mod_clk\n", mmc->bus_width,
> + mmc->clock, mmchost->mod_clk);
> +
> + /* Change clock first */
> + clkdiv = (mmchost->mod_clk + (mmc->clock >> 1)) / mmc->clock / 2;
> + if (mmc->clock)
> + if (mmc_config_clock(mmc, clkdiv)) {
> + mmchost->fatal_err = 1;
> + return;
> + }
> +
> + /* Change bus width */
> + if (mmc->bus_width == 8)
> + writel(2, &mmchost->reg->width);
> + else if (mmc->bus_width == 4)
> + writel(1, &mmchost->reg->width);
> + else
> + writel(0, &mmchost->reg->width);
> +}
> +
> +static int mmc_core_init(struct mmc *mmc)
> +{
> + struct sunxi_mmc_host *mmchost = (struct sunxi_mmc_host *)mmc->priv;
> +
> + /* Reset controller */
> + writel(0x7, &mmchost->reg->gctrl);
> +
> + return 0;
> +}
> +
> +static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data)
> +{
> + struct sunxi_mmc_host *mmchost = (struct sunxi_mmc_host *)mmc->priv;
> + unsigned i;
> + unsigned byte_cnt = data->blocksize * data->blocks;
> + unsigned *buff;
> + unsigned timeout = 0xfffff;
> +
> + if (data->flags & MMC_DATA_READ) {
> + buff = (unsigned int *)data->dest;
> + for (i = 0; i < (byte_cnt >> 2); i++) {
> + while (--timeout
> + && (readl(&mmchost->reg->status) & (1 << 2)))
More magic.
> + ;
> + if (timeout <= 0)
> + goto out;
> + buff[i] = readl(mmchost->database);
> + timeout = 0xfffff;
> + }
> + } else {
> + buff = (unsigned int *)data->src;
> + for (i = 0; i < (byte_cnt >> 2); i++) {
> + while (--timeout
> + && (readl(&mmchost->reg->status) & (1 << 3)))
> + ;
> + if (timeout <= 0)
> + goto out;
> + writel(buff[i], mmchost->database);
> + timeout = 0xfffff;
> + }
> + }
> +
> +out:
> + if (timeout <= 0)
> + return -1;
> +
> + return 0;
> +}
> +
[...]
More information about the U-Boot
mailing list