From mboxrd@z Thu Jan 1 00:00:00 1970 From: Marek Vasut Date: Sun, 25 Nov 2012 19:09:02 +0100 Subject: [U-Boot] [PATCH 02/22] ARM: sunxi: MMC driver In-Reply-To: <1353843479.17518.14.camel@home.hno.se> References: <1353843479.17518.14.camel@home.hno.se> Message-ID: <201211251909.02478.marex@denx.de> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de 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 > Signed-off-by: Henrik Nodstrom > Signed-off-by: Stefan Roese > --- [...] > +#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; > +} > + [...]