diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/Makefile | 24 | ||||
| -rw-r--r-- | drivers/mmc/arm_pl180_mmci.c | 15 | ||||
| -rw-r--r-- | drivers/mmc/arm_pl180_mmci.h | 15 | ||||
| -rw-r--r-- | drivers/mmc/davinci_mmc.c | 14 | ||||
| -rw-r--r-- | drivers/mmc/dw_mmc.c | 52 | ||||
| -rw-r--r-- | drivers/mmc/exynos_dw_mmc.c | 142 | ||||
| -rw-r--r-- | drivers/mmc/fsl_esdhc.c | 37 | ||||
| -rw-r--r-- | drivers/mmc/fsl_esdhc_spl.c | 115 | ||||
| -rw-r--r-- | drivers/mmc/ftsdc010_mci.c | 9 | ||||
| -rw-r--r-- | drivers/mmc/gen_atmel_mci.c | 65 | ||||
| -rw-r--r-- | drivers/mmc/mmc.c | 430 | ||||
| -rw-r--r-- | drivers/mmc/mmc_private.h | 45 | ||||
| -rw-r--r-- | drivers/mmc/mmc_write.c | 179 | ||||
| -rw-r--r-- | drivers/mmc/mxsmmc.c | 23 | ||||
| -rw-r--r-- | drivers/mmc/omap_hsmmc.c | 69 | ||||
| -rw-r--r-- | drivers/mmc/pxa_mmc_gen.c | 15 | ||||
| -rw-r--r-- | drivers/mmc/s5p_sdhci.c | 18 | ||||
| -rw-r--r-- | drivers/mmc/sdhci.c | 40 | ||||
| -rw-r--r-- | drivers/mmc/spear_sdhci.c | 14 | ||||
| -rw-r--r-- | drivers/mmc/tegra_mmc.c | 14 | ||||
| -rw-r--r-- | drivers/mmc/zynq_sdhci.c | 33 |
21 files changed, 886 insertions, 482 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 2b581781d..06280d1fa 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -2,23 +2,7 @@ # (C) Copyright 2006 # Wolfgang Denk, DENX Software Engineering, wd@denx.de. # -# See file CREDITS for list of people who contributed to this -# project. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, -# MA 02111-1307 USA +# SPDX-License-Identifier: GPL-2.0+ # include $(TOPDIR)/config.mk @@ -47,6 +31,12 @@ COBJS-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o COBJS-$(CONFIG_TEGRA_MMC) += tegra_mmc.o COBJS-$(CONFIG_DWMMC) += dw_mmc.o COBJS-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o +COBJS-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o +ifdef CONFIG_SPL_BUILD +COBJS-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o +else +COBJS-$(CONFIG_GENERIC_MMC) += mmc_write.o +endif COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index ab2e81e5d..5a55fe73b 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -7,20 +7,7 @@ * Author: Martin Lundholm <martin.xa.lundholm@stericsson.com> * Ported to drivers/mmc/ by: Matt Waddel <matt.waddel@linaro.org> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ /* #define DEBUG */ diff --git a/drivers/mmc/arm_pl180_mmci.h b/drivers/mmc/arm_pl180_mmci.h index 06709ed7f..72344498d 100644 --- a/drivers/mmc/arm_pl180_mmci.h +++ b/drivers/mmc/arm_pl180_mmci.h @@ -7,20 +7,7 @@ * Author: Martin Lundholm <martin.xa.lundholm@stericsson.com> * Ported to drivers/mmc/ by: Matt Waddel <matt.waddel@linaro.org> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #ifndef __ARM_PL180_MMCI_H__ diff --git a/drivers/mmc/davinci_mmc.c b/drivers/mmc/davinci_mmc.c index 5aa218426..b38096118 100644 --- a/drivers/mmc/davinci_mmc.c +++ b/drivers/mmc/davinci_mmc.c @@ -3,19 +3,7 @@ * * Copyright (C) 2010 Texas Instruments Incorporated * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * SPDX-License-Identifier: GPL-2.0+ */ #include <config.h> diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 4070d4ea5..9a803a02d 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -3,27 +3,13 @@ * Jaehoon Chung <jh80.chung@samsung.com> * Rajeshawari Shinde <rajeshwari.s@samsung.com> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> #include <malloc.h> #include <mmc.h> #include <dwmmc.h> -#include <asm/arch/clk.h> #include <asm-generic/errno.h> #define PAGE_SIZE 4096 @@ -55,12 +41,11 @@ static void dwmci_set_idma_desc(struct dwmci_idmac *idmac, } static void dwmci_prepare_data(struct dwmci_host *host, - struct mmc_data *data) + struct mmc_data *data, struct dwmci_idmac *cur_idmac) { unsigned long ctrl; unsigned int i = 0, flags, cnt, blk_cnt; ulong data_start, data_end, start_addr; - ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, data->blocks); blk_cnt = data->blocks; @@ -87,7 +72,7 @@ static void dwmci_prepare_data(struct dwmci_host *host, dwmci_set_idma_desc(cur_idmac, flags, cnt, start_addr + (i * PAGE_SIZE)); - if(blk_cnt < 8) + if (blk_cnt <= 8) break; blk_cnt -= 8; cur_idmac++; @@ -125,24 +110,25 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct dwmci_host *host = (struct dwmci_host *)mmc->priv; + ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, + data ? DIV_ROUND_UP(data->blocks, 8) : 0); int flags = 0, i; unsigned int timeout = 100000; u32 retry = 10000; u32 mask, ctrl; + ulong start = get_timer(0); while (dwmci_readl(host, DWMCI_STATUS) & DWMCI_BUSY) { - if (timeout == 0) { + if (get_timer(start) > timeout) { printf("Timeout on data busy\n"); return TIMEOUT; } - timeout--; } dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); if (data) - dwmci_prepare_data(host, data); - + dwmci_prepare_data(host, data, cur_idmac); dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); @@ -231,9 +217,8 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq) int timeout = 10000; unsigned long sclk; - if (freq == host->clock) + if ((freq == host->clock) || (freq == 0)) return 0; - /* * If host->mmc_clk didn't define, * then assume that host->bus_hz is source clock value. @@ -314,7 +299,7 @@ static void dwmci_set_ios(struct mmc *mmc) static int dwmci_init(struct mmc *mmc) { struct dwmci_host *host = (struct dwmci_host *)mmc->priv; - u32 fifo_size, fifoth_val; + u32 fifo_size; dwmci_writel(host, DWMCI_PWREN, 1); @@ -323,6 +308,9 @@ static int dwmci_init(struct mmc *mmc) return -1; } + /* Enumerate at 400KHz */ + dwmci_setup_bus(host, mmc->f_min); + dwmci_writel(host, DWMCI_RINTSTS, 0xFFFFFFFF); dwmci_writel(host, DWMCI_INTMASK, 0); @@ -331,13 +319,13 @@ static int dwmci_init(struct mmc *mmc) dwmci_writel(host, DWMCI_IDINTEN, 0); dwmci_writel(host, DWMCI_BMOD, 1); - fifo_size = dwmci_readl(host, DWMCI_FIFOTH); - if (host->fifoth_val) - fifoth_val = host->fifoth_val; - else - fifoth_val = MSIZE(0x2) | RX_WMARK(fifo_size/2 -1) | - TX_WMARK(fifo_size/2); - dwmci_writel(host, DWMCI_FIFOTH, fifoth_val); + if (!host->fifoth_val) { + 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); diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c index 72a31b73f..4ef9fec0e 100644 --- a/drivers/mmc/exynos_dw_mmc.c +++ b/drivers/mmc/exynos_dw_mmc.c @@ -2,56 +2,150 @@ * (C) Copyright 2012 SAMSUNG Electronics * Jaehoon Chung <jh80.chung@samsung.com> * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> -#include <malloc.h> #include <dwmmc.h> +#include <fdtdec.h> +#include <libfdt.h> +#include <malloc.h> #include <asm/arch/dwmmc.h> #include <asm/arch/clk.h> +#include <asm/arch/pinmux.h> -static char *EXYNOS_NAME = "EXYNOS DWMMC"; +#define DWMMC_MAX_CH_NUM 4 +#define DWMMC_MAX_FREQ 52000000 +#define DWMMC_MIN_FREQ 400000 +#define DWMMC_MMC0_CLKSEL_VAL 0x03030001 +#define DWMMC_MMC2_CLKSEL_VAL 0x03020001 +/* + * Function used as callback function to initialise the + * CLKSEL register for every mmc channel. + */ static void exynos_dwmci_clksel(struct dwmci_host *host) { - u32 val; - val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) | - DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(0); + dwmci_writel(host, DWMCI_CLKSEL, host->clksel_val); +} - dwmci_writel(host, DWMCI_CLKSEL, val); +unsigned int exynos_dwmci_get_clk(int dev_index) +{ + return get_mmc_clk(dev_index); } -int exynos_dwmci_init(u32 regbase, int bus_width, int index) +/* + * This function adds the mmc channel to be registered with mmc core. + * index - mmc channel number. + * regbase - register base address of mmc channel specified in 'index'. + * bus_width - operating bus width of mmc channel specified in 'index'. + * clksel - value to be written into CLKSEL register in case of FDT. + * NULL in case od non-FDT. + */ +int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel) { struct dwmci_host *host = NULL; + unsigned int div; + unsigned long freq, sclk; host = malloc(sizeof(struct dwmci_host)); if (!host) { printf("dwmci_host malloc fail!\n"); return 1; } + /* request mmc clock vlaue of 52MHz. */ + freq = 52000000; + sclk = get_mmc_clk(index); + div = DIV_ROUND_UP(sclk, freq); + /* set the clock divisor for mmc */ + set_mmc_clk(index, div); - host->name = EXYNOS_NAME; + host->name = "EXYNOS DWMMC"; host->ioaddr = (void *)regbase; host->buswidth = bus_width; + + if (clksel) { + host->clksel_val = clksel; + } else { + if (0 == index) + host->clksel_val = DWMMC_MMC0_CLKSEL_VAL; + if (2 == index) + host->clksel_val = DWMMC_MMC2_CLKSEL_VAL; + } + host->clksel = exynos_dwmci_clksel; host->dev_index = index; + host->mmc_clk = exynos_dwmci_get_clk; + /* Add the mmc channel to be registered with mmc core */ + if (add_dwmci(host, DWMMC_MAX_FREQ, DWMMC_MIN_FREQ)) { + debug("dwmmc%d registration failed\n", index); + return -1; + } + return 0; +} + +#ifdef CONFIG_OF_CONTROL +int exynos_dwmmc_init(const void *blob) +{ + int index, bus_width; + int node_list[DWMMC_MAX_CH_NUM]; + int err = 0, dev_id, flag, count, i; + u32 clksel_val, base, timing[3]; + + count = fdtdec_find_aliases_for_id(blob, "mmc", + COMPAT_SAMSUNG_EXYNOS5_DWMMC, node_list, + DWMMC_MAX_CH_NUM); + + for (i = 0; i < count; i++) { + int node = node_list[i]; - add_dwmci(host, 52000000, 400000); + if (node <= 0) + continue; + /* Extract device id for each mmc channel */ + dev_id = pinmux_decode_periph_id(blob, node); + + /* Get the bus width from the device node */ + bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0); + if (bus_width <= 0) { + debug("DWMMC: Can't get bus-width\n"); + return -1; + } + if (8 == bus_width) + flag = PINMUX_FLAG_8BIT_MODE; + else + flag = PINMUX_FLAG_NONE; + + /* config pinmux for each mmc channel */ + err = exynos_pinmux_config(dev_id, flag); + if (err) { + debug("DWMMC not configured\n"); + return err; + } + + index = dev_id - PERIPH_ID_SDMMC0; + + /* Get the base address from the device node */ + base = fdtdec_get_addr(blob, node, "reg"); + if (!base) { + debug("DWMMC: Can't get base address\n"); + return -1; + } + /* Extract the timing info from the node */ + err = fdtdec_get_int_array(blob, node, "samsung,timing", + timing, 3); + if (err) { + debug("Can't get sdr-timings for divider\n"); + return -1; + } + + clksel_val = (DWMCI_SET_SAMPLE_CLK(timing[0]) | + DWMCI_SET_DRV_CLK(timing[1]) | + DWMCI_SET_DIV_RATIO(timing[2])); + /* Initialise each mmc channel */ + err = exynos_dwmci_add_port(index, base, bus_width, clksel_val); + if (err) + debug("dwmmc Channel-%d init failed\n", index); + } return 0; } - +#endif diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 861f4b9d6..a7170b49d 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -6,23 +6,7 @@ * (C) Copyright 2003 * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include <config.h> @@ -100,7 +84,7 @@ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) else if (cmd->resp_type & MMC_RSP_PRESENT) xfertyp |= XFERTYP_RSPTYP_48; -#ifdef CONFIG_MX53 +#if defined(CONFIG_MX53) || defined(CONFIG_T4240QDS) if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) xfertyp |= XFERTYP_CMDTYP_ABORT; #endif @@ -178,7 +162,7 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) int timeout; struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; -#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO uint wml_value; wml_value = data->blocksize/4; @@ -310,6 +294,9 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) /* Figure out the transfer arguments */ xfertyp = esdhc_xfertyp(cmd, data); + /* Mask all irqs */ + esdhc_write32(®s->irqsigen, 0); + /* Send the command */ esdhc_write32(®s->cmdarg, cmd->cmdarg); #if defined(CONFIG_FSL_USDHC) @@ -320,15 +307,11 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) esdhc_write32(®s->xfertyp, xfertyp); #endif - /* Mask all irqs */ - esdhc_write32(®s->irqsigen, 0); - /* Wait for the command to complete */ while (!(esdhc_read32(®s->irqstat) & (IRQSTAT_CC | IRQSTAT_CTOE))) ; irqstat = esdhc_read32(®s->irqstat); - esdhc_write32(®s->irqstat, irqstat); /* Reset CMD and DATA portions on error */ if (irqstat & (CMD_ERR | IRQSTAT_CTOE)) { @@ -471,7 +454,7 @@ static int esdhc_init(struct mmc *mmc) int timeout = 1000; /* Reset the entire host controller */ - esdhc_write32(®s->sysctl, SYSCTL_RSTA); + esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); /* Wait until the controller is available */ while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) @@ -482,7 +465,7 @@ static int esdhc_init(struct mmc *mmc) esdhc_write32(®s->scr, 0x00000040); #endif - esdhc_write32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); + esdhc_setbits32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); /* Set the initial clock speed */ mmc_set_clock(mmc, 400000); @@ -516,7 +499,7 @@ static void esdhc_reset(struct fsl_esdhc *regs) unsigned long timeout = 100; /* wait max 100 ms */ /* reset the controller */ - esdhc_write32(®s->sysctl, SYSCTL_RSTA); + esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); /* hardware clears the bit when it is done */ while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) @@ -535,6 +518,8 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) return -1; mmc = malloc(sizeof(struct mmc)); + if (!mmc) + return -ENOMEM; sprintf(mmc->name, "FSL_SDHC"); regs = (struct fsl_esdhc *)cfg->esdhc_base; diff --git a/drivers/mmc/fsl_esdhc_spl.c b/drivers/mmc/fsl_esdhc_spl.c new file mode 100644 index 000000000..65c52a22d --- /dev/null +++ b/drivers/mmc/fsl_esdhc_spl.c @@ -0,0 +1,115 @@ +/* + * Copyright 2013 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <mmc.h> +#include <malloc.h> + +/* + * The environment variables are written to just after the u-boot image + * on SDCard, so we must read the MBR to get the start address and code + * length of the u-boot image, then calculate the address of the env. + */ +#define ESDHC_BOOT_IMAGE_SIZE 0x48 +#define ESDHC_BOOT_IMAGE_ADDR 0x50 +#define MBRDBR_BOOT_SIG_55 0x1fe +#define MBRDBR_BOOT_SIG_AA 0x1ff +#define CONFIG_CFG_DATA_SECTOR 0 + +/* + * The main entry for mmc booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from mmc into SDRAM and starts it from there. + */ + +void __noreturn mmc_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + uint blk_start, blk_cnt, err; + u32 blklen; + uchar *tmp_buf; + uchar val; + uint i, byte_num; + u32 offset, code_len; + struct mmc *mmc; + + mmc = find_mmc_device(0); + if (!mmc) { + puts("spl: mmc device not found!!\n"); + hang(); + } + + blklen = mmc->read_bl_len; + tmp_buf = malloc(blklen); + if (!tmp_buf) { + puts("spl: malloc memory failed!!\n"); + hang(); + } + memset(tmp_buf, 0, blklen); + + /* + * Read source addr from sd card + */ + err = mmc->block_dev.block_read(0, CONFIG_CFG_DATA_SECTOR, 1, tmp_buf); + if (err != 1) { + puts("spl: mmc read failed!!\n"); + free(tmp_buf); + hang(); + } + + val = *(tmp_buf + MBRDBR_BOOT_SIG_55); + if (0x55 != val) { + puts("spl: mmc signature is not valid!!\n"); + free(tmp_buf); + hang(); + } + val = *(tmp_buf + MBRDBR_BOOT_SIG_AA); + if (0xAA != val) { + puts("spl: mmc signature is not valid!!\n"); + free(tmp_buf); + hang(); + } + + byte_num = 4; + offset = 0; + for (i = 0; i < byte_num; i++) { + val = *(tmp_buf + ESDHC_BOOT_IMAGE_ADDR + i); + offset = (offset << 8) + val; + } + offset += CONFIG_SYS_MMC_U_BOOT_OFFS; + /* Get the code size from offset 0x48 */ + byte_num = 4; + code_len = 0; + for (i = 0; i < byte_num; i++) { + val = *(tmp_buf + ESDHC_BOOT_IMAGE_SIZE + i); + code_len = (code_len << 8) + val; + } + code_len -= CONFIG_SYS_MMC_U_BOOT_OFFS; + /* + * Load U-Boot image from mmc into RAM + */ + blk_start = ALIGN(offset, mmc->read_bl_len) / mmc->read_bl_len; + blk_cnt = ALIGN(code_len, mmc->read_bl_len) / mmc->read_bl_len; + err = mmc->block_dev.block_read(0, blk_start, blk_cnt, + (uchar *)CONFIG_SYS_MMC_U_BOOT_DST); + if (err != blk_cnt) { + puts("spl: mmc read failed!!\n"); + free(tmp_buf); + hang(); + } + + /* + * Clean d-cache and invalidate i-cache, to + * make sure that no stale data is executed. + */ + flush_cache(CONFIG_SYS_MMC_U_BOOT_DST, CONFIG_SYS_MMC_U_BOOT_SIZE); + + /* + * Jump to U-Boot image + */ + uboot = (void *)CONFIG_SYS_MMC_U_BOOT_START; + (*uboot)(); +} diff --git a/drivers/mmc/ftsdc010_mci.c b/drivers/mmc/ftsdc010_mci.c index 562b14aff..7600d5ce5 100644 --- a/drivers/mmc/ftsdc010_mci.c +++ b/drivers/mmc/ftsdc010_mci.c @@ -4,8 +4,7 @@ * (C) Copyright 2010 Faraday Technology * Dante Su <dantesu@faraday-tech.com> * - * This file is released under the terms of GPL v2 and any later version. - * See the file COPYING in the root directory of the source tree for details. + * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> @@ -175,7 +174,11 @@ static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd, len = data->blocksize * data->blocks; /* 1. data disable + fifo reset */ - writel(FTSDC010_DCR_FIFO_RST, ®s->dcr); + dcr = 0; +#ifdef CONFIG_FTSDC010_SDIO + dcr |= FTSDC010_DCR_FIFO_RST; +#endif + writel(dcr, ®s->dcr); /* 2. clear status register */ writel(FTSDC010_STATUS_DATA_MASK | FTSDC010_STATUS_FIFO_URUN diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index 70a9f91c8..c11dcd0f9 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -6,23 +6,7 @@ * Original Driver: * Copyright (C) 2004-2006 Atmel Corporation * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> @@ -50,6 +34,12 @@ static int initialized = 0; +/* Read Atmel MCI IP version */ +static unsigned int atmel_mci_get_version(struct atmel_mci *mci) +{ + return readl(&mci->version) & 0x00000fff; +} + /* * Print command and status: * @@ -205,7 +195,10 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) /* Wait for the command to complete */ while (!((status = readl(&mci->sr)) & MMCI_BIT(CMDRDY))); - if (status & error_flags) { + if ((status & error_flags) & MMCI_BIT(RTOE)) { + dump_cmd(cmdr, cmd->cmdarg, status, "Command Time Out"); + return TIMEOUT; + } else if (status & error_flags) { dump_cmd(cmdr, cmd->cmdarg, status, "Command Failed"); return COMM_ERR; } @@ -297,7 +290,9 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) static void mci_set_ios(struct mmc *mmc) { atmel_mci_t *mci = (atmel_mci_t *)mmc->priv; - int busw = (mmc->bus_width == 4) ? 1 : 0; + int bus_width = mmc->bus_width; + unsigned int version = atmel_mci_get_version(mci); + int busw; /* Set the clock speed */ mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN); @@ -305,9 +300,26 @@ static void mci_set_ios(struct mmc *mmc) /* * set the bus width and select slot for this interface * there is no capability for multiple slots on the same interface yet - * Bitfield SCDBUS needs to be expanded to 2 bits for 8-bit buses */ - writel(MMCI_BF(SCDBUS, busw) | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); + if ((version & 0xf00) >= 0x300) { + switch (bus_width) { + case 8: + busw = 3; + break; + case 4: + busw = 2; + break; + default: + busw = 0; + break; + } + + writel(busw << 6 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); + } else { + busw = (bus_width == 4) ? 1 : 0; + + writel(busw << 7 | MMCI_BF(SCDSEL, MCI_BUS), &mci->sdcr); + } } /* Entered into mmc structure during driver init */ @@ -340,9 +352,12 @@ static int mci_init(struct mmc *mmc) int atmel_mci_init(void *regs) { struct mmc *mmc = malloc(sizeof(struct mmc)); + struct atmel_mci *mci; + unsigned int version; if (!mmc) return -1; + strcpy(mmc->name, "mci"); mmc->priv = regs; mmc->send_cmd = mci_send_cmd; @@ -353,7 +368,13 @@ int atmel_mci_init(void *regs) /* need to be able to pass these in on a board by board basis */ mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->host_caps = MMC_MODE_4BIT; + mci = (struct atmel_mci *)mmc->priv; + version = atmel_mci_get_version(mci); + if ((version & 0xf00) >= 0x300) + mmc->host_caps = MMC_MODE_8BIT; + + mmc->host_caps |= MMC_MODE_4BIT; + /* * min and max frequencies determined by * max and min of clock divider diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0a2f5358e..84dae4d8b 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -4,23 +4,7 @@ * * Based vaguely on the Linux code * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include <config.h> @@ -31,6 +15,7 @@ #include <malloc.h> #include <linux/list.h> #include <div64.h> +#include "mmc_private.h" /* Set block count limit because of 16 bit register limit on some hardware*/ #ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT @@ -68,14 +53,10 @@ int __board_mmc_getcd(struct mmc *mmc) { int board_mmc_getcd(struct mmc *mmc)__attribute__((weak, alias("__board_mmc_getcd"))); -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) +int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { - struct mmc_data backup; int ret; - memset(&backup, 0, sizeof(backup)); - #ifdef CONFIG_MMC_TRACE int i; u8 *ptr; @@ -130,7 +111,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return ret; } -static int mmc_send_status(struct mmc *mmc, int timeout) +int mmc_send_status(struct mmc *mmc, int timeout) { struct mmc_cmd cmd; int err, retries = 5; @@ -151,8 +132,10 @@ static int mmc_send_status(struct mmc *mmc, int timeout) MMC_STATE_PRG) break; else if (cmd.response[0] & MMC_STATUS_MASK) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Status Error: 0x%08X\n", cmd.response[0]); +#endif return COMM_ERR; } } else if (--retries < 0) @@ -167,14 +150,16 @@ static int mmc_send_status(struct mmc *mmc, int timeout) printf("CURR STATE:%d\n", status); #endif if (timeout <= 0) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Timeout waiting card ready\n"); +#endif return TIMEOUT; } return 0; } -static int mmc_set_blocklen(struct mmc *mmc, int len) +int mmc_set_blocklen(struct mmc *mmc, int len) { struct mmc_cmd cmd; @@ -197,177 +182,14 @@ struct mmc *find_mmc_device(int dev_num) return m; } +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("MMC Device %d not found\n", dev_num); +#endif return NULL; } -static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) -{ - struct mmc_cmd cmd; - ulong end; - int err, start_cmd, end_cmd; - - if (mmc->high_capacity) - end = start + blkcnt - 1; - else { - end = (start + blkcnt - 1) * mmc->write_bl_len; - start *= mmc->write_bl_len; - } - - if (IS_SD(mmc)) { - start_cmd = SD_CMD_ERASE_WR_BLK_START; - end_cmd = SD_CMD_ERASE_WR_BLK_END; - } else { - start_cmd = MMC_CMD_ERASE_GROUP_START; - end_cmd = MMC_CMD_ERASE_GROUP_END; - } - - cmd.cmdidx = start_cmd; - cmd.cmdarg = start; - cmd.resp_type = MMC_RSP_R1; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - goto err_out; - - cmd.cmdidx = end_cmd; - cmd.cmdarg = end; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - goto err_out; - - cmd.cmdidx = MMC_CMD_ERASE; - cmd.cmdarg = SECURE_ERASE; - cmd.resp_type = MMC_RSP_R1b; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - goto err_out; - - return 0; - -err_out: - puts("mmc erase failed\n"); - return err; -} - -static unsigned long -mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt) -{ - int err = 0; - struct mmc *mmc = find_mmc_device(dev_num); - lbaint_t blk = 0, blk_r = 0; - int timeout = 1000; - - if (!mmc) - return -1; - - if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) - printf("\n\nCaution! Your devices Erase group is 0x%x\n" - "The erase range would be change to 0x%lx~0x%lx\n\n", - mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), - ((start + blkcnt + mmc->erase_grp_size) - & ~(mmc->erase_grp_size - 1)) - 1); - - while (blk < blkcnt) { - blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? - mmc->erase_grp_size : (blkcnt - blk); - err = mmc_erase_t(mmc, start + blk, blk_r); - if (err) - break; - - blk += blk_r; - - /* Waiting for the ready status */ - if (mmc_send_status(mmc, timeout)) - return 0; - } - - return blk; -} - -static ulong -mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) -{ - struct mmc_cmd cmd; - struct mmc_data data; - int timeout = 1000; - - if ((start + blkcnt) > mmc->block_dev.lba) { - printf("MMC: block number 0x%lx exceeds max(0x%lx)\n", - start + blkcnt, mmc->block_dev.lba); - return 0; - } - - if (blkcnt > 1) - cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; - else - cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; - - if (mmc->high_capacity) - cmd.cmdarg = start; - else - cmd.cmdarg = start * mmc->write_bl_len; - - cmd.resp_type = MMC_RSP_R1; - - data.src = src; - data.blocks = blkcnt; - data.blocksize = mmc->write_bl_len; - data.flags = MMC_DATA_WRITE; - - if (mmc_send_cmd(mmc, &cmd, &data)) { - printf("mmc write failed\n"); - return 0; - } - - /* SPI multiblock writes terminate using a special - * token, not a STOP_TRANSMISSION request. - */ - if (!mmc_host_is_spi(mmc) && blkcnt > 1) { - cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; - cmd.cmdarg = 0; - cmd.resp_type = MMC_RSP_R1b; - if (mmc_send_cmd(mmc, &cmd, NULL)) { - printf("mmc fail to send stop cmd\n"); - return 0; - } - } - - /* Waiting for the ready status */ - if (mmc_send_status(mmc, timeout)) - return 0; - - return blkcnt; -} - -static ulong -mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) -{ - lbaint_t cur, blocks_todo = blkcnt; - - struct mmc *mmc = find_mmc_device(dev_num); - if (!mmc) - return 0; - - if (mmc_set_blocklen(mmc, mmc->write_bl_len)) - return 0; - - do { - cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo; - if(mmc_write_blocks(mmc, start, cur, src) != cur) - return 0; - blocks_todo -= cur; - start += cur; - src += cur * mmc->write_bl_len; - } while (blocks_todo > 0); - - return blkcnt; -} - -static int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, +static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, lbaint_t blkcnt) { struct mmc_cmd cmd; @@ -398,7 +220,9 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1b; if (mmc_send_cmd(mmc, &cmd, NULL)) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("mmc fail to send stop cmd\n"); +#endif return 0; } } @@ -406,7 +230,7 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, return blkcnt; } -static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) +static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst) { lbaint_t cur, blocks_todo = blkcnt; @@ -418,8 +242,10 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) return 0; if ((start + blkcnt) > mmc->block_dev.lba) { - printf("MMC: block number 0x%lx exceeds max(0x%lx)\n", +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", start + blkcnt, mmc->block_dev.lba); +#endif return 0; } @@ -700,16 +526,49 @@ static int mmc_change_freq(struct mmc *mmc) return 0; } +static int mmc_set_capacity(struct mmc *mmc, int part_num) +{ + switch (part_num) { + case 0: + mmc->capacity = mmc->capacity_user; + break; + case 1: + case 2: + mmc->capacity = mmc->capacity_boot; + break; + case 3: + mmc->capacity = mmc->capacity_rpmb; + break; + case 4: + case 5: + case 6: + case 7: + mmc->capacity = mmc->capacity_gp[part_num - 4]; + break; + default: + return -1; + } + + mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); + + return 0; +} + int mmc_switch_part(int dev_num, unsigned int part_num) { struct mmc *mmc = find_mmc_device(dev_num); + int ret; if (!mmc) return -1; - return mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, - (mmc->part_config & ~PART_ACCESS_MASK) - | (part_num & PART_ACCESS_MASK)); + ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, + (mmc->part_config & ~PART_ACCESS_MASK) + | (part_num & PART_ACCESS_MASK)); + if (ret) + return ret; + + return mmc_set_capacity(mmc, part_num); } int mmc_getcd(struct mmc *mmc) @@ -917,7 +776,7 @@ static void mmc_set_bus_width(struct mmc *mmc, uint width) static int mmc_startup(struct mmc *mmc) { - int err; + int err, i; uint mult, freq; u64 cmult, csize, capacity; struct mmc_cmd cmd; @@ -1035,8 +894,12 @@ static int mmc_startup(struct mmc *mmc) cmult = (mmc->csd[2] & 0x00038000) >> 15; } - mmc->capacity = (csize + 1) << (cmult + 2); - mmc->capacity *= mmc->read_bl_len; + mmc->capacity_user = (csize + 1) << (cmult + 2); + mmc->capacity_user *= mmc->read_bl_len; + mmc->capacity_boot = 0; + mmc->capacity_rpmb = 0; + for (i = 0; i < 4; i++) + mmc->capacity_gp[i] = 0; if (mmc->read_bl_len > MMC_MAX_BLOCK_LEN) mmc->read_bl_len = MMC_MAX_BLOCK_LEN; @@ -1075,7 +938,7 @@ static int mmc_startup(struct mmc *mmc) | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; capacity *= MMC_MAX_BLOCK_LEN; if ((capacity >> 20) > 2 * 1024) - mmc->capacity = capacity; + mmc->capacity_user = capacity; } switch (ext_csd[EXT_CSD_REV]) { @@ -1117,8 +980,25 @@ static int mmc_startup(struct mmc *mmc) if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || ext_csd[EXT_CSD_BOOT_MULT]) mmc->part_config = ext_csd[EXT_CSD_PART_CONF]; + + mmc->capacity_boot = ext_csd[EXT_CSD_BOOT_MULT] << 17; + + mmc->capacity_rpmb = ext_csd[EXT_CSD_RPMB_MULT] << 17; + + for (i = 0; i < 4; i++) { + int idx = EXT_CSD_GP_SIZE_MULT + i * 3; + mmc->capacity_gp[i] = (ext_csd[idx + 2] << 16) + + (ext_csd[idx + 1] << 8) + ext_csd[idx]; + mmc->capacity_gp[i] *= + ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]; + mmc->capacity_gp[i] *= ext_csd[EXT_CSD_HC_WP_GRP_SIZE]; + } } + err = mmc_set_capacity(mmc, mmc->part_num); + if (err) + return err; + if (IS_SD(mmc)) err = sd_change_freq(mmc); else @@ -1227,6 +1107,7 @@ static int mmc_startup(struct mmc *mmc) mmc->block_dev.blksz = mmc->read_bl_len; mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz); mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x", mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff), (mmc->cid[3] >> 16) & 0xffff); @@ -1236,6 +1117,11 @@ static int mmc_startup(struct mmc *mmc) (mmc->cid[2] >> 24) & 0xff); sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf, (mmc->cid[2] >> 16) & 0xf); +#else + mmc->block_dev.vendor[0] = 0; + mmc->block_dev.product[0] = 0; + mmc->block_dev.revision[0] = 0; +#endif #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT) init_part(&mmc->block_dev); #endif @@ -1302,7 +1188,9 @@ int mmc_start_init(struct mmc *mmc) if (mmc_getcd(mmc) == 0) { mmc->has_init = 0; +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("MMC: no card present\n"); +#endif return NO_CARD_ERR; } @@ -1337,7 +1225,9 @@ int mmc_start_init(struct mmc *mmc) err = mmc_send_op_cond(mmc); if (err && err != IN_PROGRESS) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Card did not respond to voltage select!\n"); +#endif return UNUSABLE_ERR; } } @@ -1393,6 +1283,8 @@ static int __def_mmc_init(bd_t *bis) int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + void print_mmc_devices(char separator) { struct mmc *m; @@ -1410,6 +1302,10 @@ void print_mmc_devices(char separator) printf("\n"); } +#else +void print_mmc_devices(char separator) { } +#endif + int get_mmc_num(void) { return cur_dev_num; @@ -1442,8 +1338,144 @@ int mmc_initialize(bd_t *bis) if (board_mmc_init(bis) < 0) cpu_mmc_init(bis); +#ifndef CONFIG_SPL_BUILD print_mmc_devices(','); +#endif do_preinit(); return 0; } + +#ifdef CONFIG_SUPPORT_EMMC_BOOT +/* + * This function changes the size of boot partition and the size of rpmb + * partition present on EMMC devices. + * + * Input Parameters: + * struct *mmc: pointer for the mmc device strcuture + * bootsize: size of boot partition + * rpmbsize: size of rpmb partition + * + * Returns 0 on success. + */ + +int mmc_boot_partition_size_change(struct mmc *mmc, unsigned long bootsize, + unsigned long rpmbsize) +{ + int err; + struct mmc_cmd cmd; + + /* Only use this command for raw EMMC moviNAND. Enter backdoor mode */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = MMC_CMD62_ARG1; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error1 = %d\n", err); + return err; + } + + /* Boot partition changing mode */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = MMC_CMD62_ARG2; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error2 = %d\n", err); + return err; + } + /* boot partition size is multiple of 128KB */ + bootsize = (bootsize * 1024) / 128; + + /* Arg: boot partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = bootsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error3 = %d\n", err); + return err; + } + /* RPMB partition size is multiple of 128KB */ + rpmbsize = (rpmbsize * 1024) / 128; + /* Arg: RPMB partition size */ + cmd.cmdidx = MMC_CMD_RES_MAN; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = rpmbsize; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc_boot_partition_size_change: Error4 = %d\n", err); + return err; + } + return 0; +} + +/* + * This function shall form and send the commands to open / close the + * boot partition specified by user. + * + * Input Parameters: + * ack: 0x0 - No boot acknowledge sent (default) + * 0x1 - Boot acknowledge sent during boot operation + * part_num: User selects boot data that will be sent to master + * 0x0 - Device not boot enabled (default) + * 0x1 - Boot partition 1 enabled for boot + * 0x2 - Boot partition 2 enabled for boot + * access: User selects partitions to access + * 0x0 : No access to boot partition (default) + * 0x1 : R/W boot partition 1 + * 0x2 : R/W boot partition 2 + * 0x3 : R/W Replay Protected Memory Block (RPMB) + * + * Returns 0 on success. + */ +int mmc_boot_part_access(struct mmc *mmc, u8 ack, u8 part_num, u8 access) +{ + int err; + struct mmc_cmd cmd; + + /* Boot ack enable, boot partition enable , boot partition access */ + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_RSP_R1b; + + cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_PART_CONF << 16) | + ((EXT_CSD_BOOT_ACK(ack) | + EXT_CSD_BOOT_PART_NUM(part_num) | + EXT_CSD_PARTITION_ACCESS(access)) << 8); + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + if (access) { + debug("mmc boot partition#%d open fail:Error1 = %d\n", + part_num, err); + } else { + debug("mmc boot partition#%d close fail:Error = %d\n", + part_num, err); + } + return err; + } + + if (access) { + /* 4bit transfer mode at booting time. */ + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_RSP_R1b; + + cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_BOOT_BUS_WIDTH << 16) | + ((1 << 0) << 8); + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) { + debug("mmc boot partition#%d open fail:Error2 = %d\n", + part_num, err); + return err; + } + } + return 0; +} +#endif diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h new file mode 100644 index 000000000..16dcf9ff6 --- /dev/null +++ b/drivers/mmc/mmc_private.h @@ -0,0 +1,45 @@ +/* + * Copyright 2008,2010 Freescale Semiconductor, Inc + * Andy Fleming + * + * Based (loosely) on the Linux code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _MMC_PRIVATE_H_ +#define _MMC_PRIVATE_H_ + +#include <mmc.h> + +extern int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data); +extern int mmc_send_status(struct mmc *mmc, int timeout); +extern int mmc_set_blocklen(struct mmc *mmc, int len); + +#ifndef CONFIG_SPL_BUILD + +extern unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt); + +extern ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, + const void *src); + +#else /* CONFIG_SPL_BUILD */ + +/* SPL will never write or erase, declare dummies to reduce code size. */ + +static inline unsigned long mmc_berase(int dev_num, lbaint_t start, + lbaint_t blkcnt) +{ + return 0; +} + +static inline ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, + const void *src) +{ + return 0; +} + +#endif /* CONFIG_SPL_BUILD */ + +#endif /* _MMC_PRIVATE_H_ */ diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c new file mode 100644 index 000000000..aa2fdefa7 --- /dev/null +++ b/drivers/mmc/mmc_write.c @@ -0,0 +1,179 @@ +/* + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the Linux code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <common.h> +#include <part.h> +#include "mmc_private.h" + +static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) +{ + struct mmc_cmd cmd; + ulong end; + int err, start_cmd, end_cmd; + + if (mmc->high_capacity) { + end = start + blkcnt - 1; + } else { + end = (start + blkcnt - 1) * mmc->write_bl_len; + start *= mmc->write_bl_len; + } + + if (IS_SD(mmc)) { + start_cmd = SD_CMD_ERASE_WR_BLK_START; + end_cmd = SD_CMD_ERASE_WR_BLK_END; + } else { + start_cmd = MMC_CMD_ERASE_GROUP_START; + end_cmd = MMC_CMD_ERASE_GROUP_END; + } + + cmd.cmdidx = start_cmd; + cmd.cmdarg = start; + cmd.resp_type = MMC_RSP_R1; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + cmd.cmdidx = end_cmd; + cmd.cmdarg = end; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + cmd.cmdidx = MMC_CMD_ERASE; + cmd.cmdarg = SECURE_ERASE; + cmd.resp_type = MMC_RSP_R1b; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + return 0; + +err_out: + puts("mmc erase failed\n"); + return err; +} + +unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) +{ + int err = 0; + struct mmc *mmc = find_mmc_device(dev_num); + lbaint_t blk = 0, blk_r = 0; + int timeout = 1000; + + if (!mmc) + return -1; + + if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) + printf("\n\nCaution! Your devices Erase group is 0x%x\n" + "The erase range would be change to " + "0x" LBAF "~0x" LBAF "\n\n", + mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), + ((start + blkcnt + mmc->erase_grp_size) + & ~(mmc->erase_grp_size - 1)) - 1); + + while (blk < blkcnt) { + blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? + mmc->erase_grp_size : (blkcnt - blk); + err = mmc_erase_t(mmc, start + blk, blk_r); + if (err) + break; + + blk += blk_r; + + /* Waiting for the ready status */ + if (mmc_send_status(mmc, timeout)) + return 0; + } + + return blk; +} + +static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start, + lbaint_t blkcnt, const void *src) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int timeout = 1000; + + if ((start + blkcnt) > mmc->block_dev.lba) { + printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", + start + blkcnt, mmc->block_dev.lba); + return 0; + } + + if (blkcnt == 0) + return 0; + else if (blkcnt == 1) + cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = start; + else + cmd.cmdarg = start * mmc->write_bl_len; + + cmd.resp_type = MMC_RSP_R1; + + data.src = src; + data.blocks = blkcnt; + data.blocksize = mmc->write_bl_len; + data.flags = MMC_DATA_WRITE; + + if (mmc_send_cmd(mmc, &cmd, &data)) { + printf("mmc write failed\n"); + return 0; + } + + /* SPI multiblock writes terminate using a special + * token, not a STOP_TRANSMISSION request. + */ + if (!mmc_host_is_spi(mmc) && blkcnt > 1) { + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1b; + if (mmc_send_cmd(mmc, &cmd, NULL)) { + printf("mmc fail to send stop cmd\n"); + return 0; + } + } + + /* Waiting for the ready status */ + if (mmc_send_status(mmc, timeout)) + return 0; + + return blkcnt; +} + +ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void *src) +{ + lbaint_t cur, blocks_todo = blkcnt; + + struct mmc *mmc = find_mmc_device(dev_num); + if (!mmc) + return 0; + + if (mmc_set_blocklen(mmc, mmc->write_bl_len)) + return 0; + + do { + cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo; + if (mmc_write_blocks(mmc, start, cur, src) != cur) + return 0; + blocks_todo -= cur; + start += cur; + src += cur * mmc->write_bl_len; + } while (blocks_todo > 0); + + return blkcnt; +} diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index a89660f13..245f9d0c6 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -15,23 +15,7 @@ * (C) Copyright 2003 * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> #include <malloc.h> @@ -41,7 +25,7 @@ #include <asm/arch/clock.h> #include <asm/arch/imx-regs.h> #include <asm/arch/sys_proto.h> -#include <asm/arch/dma.h> +#include <asm/imx-common/dma.h> #include <bouncebuf.h> struct mxsmmc_priv { @@ -426,7 +410,8 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int), int (*cd)(int)) mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT | - MMC_MODE_HS_52MHz | MMC_MODE_HS; + MMC_MODE_HS_52MHz | MMC_MODE_HS | + MMC_MODE_HC; /* * SSPCLK = 480 * 18 / 29 / 1 = 297.731 MHz diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 166744c32..d3a8b5303 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -29,7 +29,7 @@ #include <i2c.h> #include <twl4030.h> #include <twl6030.h> -#include <twl6035.h> +#include <palmas.h> #include <asm/gpio.h> #include <asm/io.h> #include <asm/arch/mmc_host_def.h> @@ -107,29 +107,27 @@ static void omap4_vmmc_pbias_config(struct mmc *mmc) } #endif -#if defined(CONFIG_OMAP54XX) && defined(CONFIG_TWL6035_POWER) +#if defined(CONFIG_OMAP54XX) && defined(CONFIG_PALMAS_POWER) static void omap5_pbias_config(struct mmc *mmc) { u32 value = 0; value = readl((*ctrl)->control_pbias); - value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ); - value |= SDCARD_BIAS_HIZ_MODE; + value &= ~SDCARD_PWRDNZ; + writel(value, (*ctrl)->control_pbias); + udelay(10); /* wait 10 us */ + value &= ~SDCARD_BIAS_PWRDNZ; writel(value, (*ctrl)->control_pbias); - twl6035_mmc1_poweron_ldo(); + palmas_mmc1_poweron_ldo(); value = readl((*ctrl)->control_pbias); - value &= ~SDCARD_BIAS_HIZ_MODE; - value |= SDCARD_PBIASLITE_VMODE | SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ; + value |= SDCARD_BIAS_PWRDNZ; writel(value, (*ctrl)->control_pbias); - - value = readl((*ctrl)->control_pbias); - if (value & (1 << 23)) { - value &= ~(SDCARD_PWRDNZ | SDCARD_BIAS_PWRDNZ); - value |= SDCARD_BIAS_HIZ_MODE; - writel(value, (*ctrl)->control_pbias); - } + udelay(150); /* wait 150 us */ + value |= SDCARD_PWRDNZ; + writel(value, (*ctrl)->control_pbias); + udelay(150); /* wait 150 us */ } #endif @@ -178,7 +176,7 @@ unsigned char mmc_board_init(struct mmc *mmc) if (mmc->block_dev.dev == 0) omap4_vmmc_pbias_config(mmc); #endif -#if defined(CONFIG_OMAP54XX) && defined(CONFIG_TWL6035_POWER) +#if defined(CONFIG_OMAP54XX) && defined(CONFIG_PALMAS_POWER) if (mmc->block_dev.dev == 0) omap5_pbias_config(mmc); #endif @@ -290,6 +288,30 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) mmc_reg_out(&mmc_base->sysctl, bit, bit); + /* + * CMD(DAT) lines reset procedures are slightly different + * for OMAP3 and OMAP4(AM335x,OMAP5,DRA7xx). + * According to OMAP3 TRM: + * Set SRC(SRD) bit in MMCHS_SYSCTL register to 0x1 and wait until it + * returns to 0x0. + * According to OMAP4(AM335x,OMAP5,DRA7xx) TRMs, CMD(DATA) lines reset + * procedure steps must be as follows: + * 1. Initiate CMD(DAT) line reset by writing 0x1 to SRC(SRD) bit in + * MMCHS_SYSCTL register (SD_SYSCTL for AM335x). + * 2. Poll the SRC(SRD) bit until it is set to 0x1. + * 3. Wait until the SRC (SRD) bit returns to 0x0 + * (reset procedure is completed). + */ +#if defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ + defined(CONFIG_AM33XX) + if (!(readl(&mmc_base->sysctl) & bit)) { + start = get_timer(0); + while (!(readl(&mmc_base->sysctl) & bit)) { + if (get_timer(0) - start > MAX_RETRY_MS) + return; + } + } +#endif start = get_timer(0); while ((readl(&mmc_base->sysctl) & bit) != 0) { if (get_timer(0) - start > MAX_RETRY_MS) { @@ -378,6 +400,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, } writel(cmd->cmdarg, &mmc_base->arg); + udelay(20); /* To fix "No status update" error on eMMC */ writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd); start = get_timer(0); @@ -482,7 +505,7 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, unsigned int count; /* - * Start Polled Read + * Start Polled Write */ count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size; count /= 4; @@ -588,6 +611,8 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, { struct mmc *mmc = &hsmmc_dev[dev_index]; struct omap_hsmmc_data *priv_data = &hsmmc_dev_data[dev_index]; + uint host_caps_val = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | + MMC_MODE_HC; sprintf(mmc->name, "OMAP SD/MMC"); mmc->send_cmd = mmc_send_cmd; @@ -602,11 +627,20 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, #ifdef OMAP_HSMMC2_BASE case 1: priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC2_BASE; +#if (defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ + defined(CONFIG_DRA7XX)) && defined(CONFIG_HSMMC2_8BIT) + /* Enable 8-bit interface for eMMC on OMAP4/5 or DRA7XX */ + host_caps_val |= MMC_MODE_8BIT; +#endif break; #endif #ifdef OMAP_HSMMC3_BASE case 2: priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC3_BASE; +#if defined(CONFIG_DRA7XX) && defined(CONFIG_HSMMC3_8BIT) + /* Enable 8-bit interface for eMMC on DRA7XX */ + host_caps_val |= MMC_MODE_8BIT; +#endif break; #endif default: @@ -622,8 +656,7 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, mmc->getwp = omap_mmc_getwp; mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; - mmc->host_caps = (MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | - MMC_MODE_HC) & ~host_caps_mask; + mmc->host_caps = host_caps_val & ~host_caps_mask; mmc->f_min = 400000; diff --git a/drivers/mmc/pxa_mmc_gen.c b/drivers/mmc/pxa_mmc_gen.c index b3ec441b6..29f3eaf30 100644 --- a/drivers/mmc/pxa_mmc_gen.c +++ b/drivers/mmc/pxa_mmc_gen.c @@ -3,20 +3,7 @@ * * Loosely based on the old code and Linux's PXA MMC driver * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include <config.h> diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index e50ff925a..40ff8739b 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -2,19 +2,7 @@ * (C) Copyright 2012 SAMSUNG Electronics * Jaehoon Chung <jh80.chung@samsung.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> @@ -84,7 +72,7 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width) host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR | - SDHCI_QUIRK_WAIT_SEND_CMD; + SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8; host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; host->version = sdhci_readw(host, SDHCI_HOST_VERSION); @@ -93,6 +81,8 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width) host->index = index; host->host_caps = MMC_MODE_HC; + if (bus_width == 8) + host->host_caps |= MMC_MODE_8BIT; return add_sdhci(host, 52000000, 400000); } diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 1eaea04ad..dfb2eeeb4 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -2,23 +2,7 @@ * Copyright 2011, Marvell Semiconductor Inc. * Lei Wen <leiwen@marvell.com> * - * See file CREDITS for list of people who contributed to this - * project. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ * * Back ported to the 8xx platform (from the 8260 platform) by * Murray.Jensen@cmst.csiro.au, 27-Jan-01. @@ -84,10 +68,9 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, unsigned int stat, rdy, mask, timeout, block = 0; #ifdef CONFIG_MMC_SDMA unsigned char ctrl; - ctrl = sdhci_readl(host, SDHCI_HOST_CONTROL); + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_DMA_MASK; - ctrl |= SDHCI_CTRL_SDMA; - sdhci_writel(host, ctrl, SDHCI_HOST_CONTROL); + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); #endif timeout = 1000000; @@ -270,7 +253,7 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) if (clock == 0) return 0; - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) { + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { /* Version 3.00 divisors must be a multiple of 2. */ if (mmc->f_max <= clock) div = 1; @@ -363,10 +346,11 @@ void sdhci_set_ios(struct mmc *mmc) ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); if (mmc->bus_width == 8) { ctrl &= ~SDHCI_CTRL_4BITBUS; - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) || + (host->quirks & SDHCI_QUIRK_USE_WIDE8)) ctrl |= SDHCI_CTRL_8BITBUS; } else { - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) ctrl &= ~SDHCI_CTRL_8BITBUS; if (mmc->bus_width == 4) ctrl |= SDHCI_CTRL_4BITBUS; @@ -453,7 +437,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) if (max_clk) mmc->f_max = max_clk; else { - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) mmc->f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; else @@ -468,7 +452,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) if (min_clk) mmc->f_min = min_clk; else { - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_300; else mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_200; @@ -486,8 +470,10 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) mmc->voltages |= host->voltages; mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; - if (caps & SDHCI_CAN_DO_8BIT) - mmc->host_caps |= MMC_MODE_8BIT; + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { + if (caps & SDHCI_CAN_DO_8BIT) + mmc->host_caps |= MMC_MODE_8BIT; + } if (host->host_caps) mmc->host_caps |= host->host_caps; diff --git a/drivers/mmc/spear_sdhci.c b/drivers/mmc/spear_sdhci.c index 23f1f4b70..6ca96a2d0 100644 --- a/drivers/mmc/spear_sdhci.c +++ b/drivers/mmc/spear_sdhci.c @@ -2,19 +2,7 @@ * (C) Copyright 2012 * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index e86bc680f..e1817e24f 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -4,19 +4,7 @@ * Jaehoon Chung <jh80.chung@samsung.com> * Portions Copyright 2011-2013 NVIDIA Corporation * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include <bouncebuf.h> diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c new file mode 100644 index 000000000..610bef5cb --- /dev/null +++ b/drivers/mmc/zynq_sdhci.c @@ -0,0 +1,33 @@ +/* + * (C) Copyright 2013 Inc. + * + * Xilinx Zynq SD Host Controller Interface + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <sdhci.h> +#include <asm/arch/sys_proto.h> + +int zynq_sdhci_init(u32 regbase) +{ + struct sdhci_host *host = NULL; + + host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); + if (!host) { + printf("zynq_sdhci_init: sdhci_host malloc fail\n"); + return 1; + } + + host->name = "zynq_sdhci"; + host->ioaddr = (void *)regbase; + host->quirks = SDHCI_QUIRK_NO_CD | SDHCI_QUIRK_WAIT_SEND_CMD; + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + host->host_caps = MMC_MODE_HC; + + add_sdhci(host, 52000000, 52000000 >> 9); + return 0; +} |