diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/mmc/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/fsl_esdhc.c | 625 | ||||
-rw-r--r-- | drivers/mmc/fsl_esdhc_imx.c | 1650 | ||||
-rw-r--r-- | drivers/mtd/ubi/Kconfig | 2 | ||||
-rw-r--r-- | drivers/mtd/ubi/io.c | 2 | ||||
-rw-r--r-- | drivers/mtd/ubispl/ubispl.c | 215 | ||||
-rw-r--r-- | drivers/mtd/ubispl/ubispl.h | 9 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 8 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/atmel-quadspi.c | 536 |
11 files changed, 2444 insertions, 615 deletions
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index c23299ea96..93588725f2 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -668,8 +668,14 @@ config TEGRA124_MMC_DISABLE_EXT_LOOPBACK config FSL_ESDHC bool "Freescale/NXP eSDHC controller support" help - This selects support for the eSDHC (enhanced secure digital host - controller) found on numerous Freescale/NXP SoCs. + This selects support for the eSDHC (Enhanced Secure Digital Host + Controller) found on numerous Freescale/NXP SoCs. + +config FSL_ESDHC_IMX + bool "Freescale/NXP i.MX eSDHC controller support" + help + This selects support for the i.MX eSDHC (Enhanced Secure Digital Host + Controller) found on numerous Freescale/NXP SoCs. endmenu diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 0076fc393b..3c8c53a9e1 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_MMC_DW_ROCKCHIP) += rockchip_dw_mmc.o obj-$(CONFIG_MMC_DW_SOCFPGA) += socfpga_dw_mmc.o obj-$(CONFIG_MMC_DW_SNPS) += snps_dw_mmc.o obj-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o +obj-$(CONFIG_FSL_ESDHC_IMX) += fsl_esdhc_imx.o obj-$(CONFIG_FTSDC010) += ftsdc010_mci.o obj-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o obj-$(CONFIG_MMC_MESON_GX) += meson_gx_mmc.o diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 6a191a1765..07318472a7 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -17,14 +17,11 @@ #include <hwconfig.h> #include <mmc.h> #include <part.h> -#include <power/regulator.h> #include <malloc.h> #include <fsl_esdhc.h> #include <fdt_support.h> #include <asm/io.h> #include <dm.h> -#include <asm-generic/gpio.h> -#include <dm/pinctrl.h> #if !CONFIG_IS_ENABLED(BLK) #include "mmc_private.h" @@ -38,7 +35,6 @@ DECLARE_GLOBAL_DATA_PTR; IRQSTATEN_CIE | IRQSTATEN_DTOE | IRQSTATEN_DCE | \ IRQSTATEN_DEBE | IRQSTATEN_BRR | IRQSTATEN_BWR | \ IRQSTATEN_DINT) -#define MAX_TUNING_LOOP 40 #define ESDHC_DRIVER_STAGE_VALUE 0xffffffff struct fsl_esdhc { @@ -60,37 +56,20 @@ struct fsl_esdhc { uint autoc12err; /* Auto CMD error status register */ uint hostcapblt; /* Host controller capabilities register */ uint wml; /* Watermark level register */ - uint mixctrl; /* For USDHC */ - char reserved1[4]; /* reserved */ + char reserved1[8]; /* reserved */ uint fevt; /* Force event register */ uint admaes; /* ADMA error status register */ uint adsaddr; /* ADMA system address register */ - char reserved2[4]; - uint dllctrl; - uint dllstat; - uint clktunectrlstatus; - char reserved3[4]; - uint strobe_dllctrl; - uint strobe_dllstat; - char reserved4[72]; - uint vendorspec; - uint mmcboot; - uint vendorspec2; - uint tuning_ctrl; /* on i.MX6/7/8 */ - char reserved5[44]; + char reserved2[160]; uint hostver; /* Host controller version register */ - char reserved6[4]; /* reserved */ + char reserved3[4]; /* reserved */ uint dmaerraddr; /* DMA error address register */ - char reserved7[4]; /* reserved */ + char reserved4[4]; /* reserved */ uint dmaerrattr; /* DMA error attribute register */ - char reserved8[4]; /* reserved */ + char reserved5[4]; /* reserved */ uint hostcapblt2; /* Host controller capabilities register 2 */ - char reserved9[8]; /* reserved */ - uint tcr; /* Tuning control register */ - char reserved10[28]; /* reserved */ - uint sddirctl; /* SD direction control register */ - char reserved11[712];/* reserved */ - uint scr; /* eSDHC control register */ + char reserved6[756]; /* reserved */ + uint esdhcctl; /* eSDHC control register */ }; struct fsl_esdhc_plat { @@ -98,11 +77,6 @@ struct fsl_esdhc_plat { struct mmc mmc; }; -struct esdhc_soc_data { - u32 flags; - u32 caps; -}; - /** * struct fsl_esdhc_priv * @@ -115,13 +89,6 @@ struct esdhc_soc_data { * @dev: pointer for the device * @non_removable: 0: removable; 1: non-removable * @wp_enable: 1: enable checking wp; 0: no check - * @vs18_enable: 1: use 1.8V voltage; 0: use 3.3V - * @flags: ESDHC_FLAG_xx in include/fsl_esdhc.h - * @caps: controller capabilities - * @tuning_step: tuning step setting in tuning_ctrl register - * @start_tuning_tap: the start point for tuning in tuning_ctrl register - * @strobe_dll_delay_target: settings in strobe_dllctrl - * @signal_voltage: indicating the current voltage * @cd_gpio: gpio for card detection * @wp_gpio: gpio for write protection */ @@ -130,7 +97,6 @@ struct fsl_esdhc_priv { unsigned int sdhc_clk; struct clk per_clk; unsigned int clock; - unsigned int mode; unsigned int bus_width; #if !CONFIG_IS_ENABLED(BLK) struct mmc *mmc; @@ -138,21 +104,6 @@ struct fsl_esdhc_priv { struct udevice *dev; int non_removable; int wp_enable; - int vs18_enable; - u32 flags; - u32 caps; - u32 tuning_step; - u32 tuning_start_tap; - u32 strobe_dll_delay_target; - u32 signal_voltage; -#if IS_ENABLED(CONFIG_DM_REGULATOR) - struct udevice *vqmmc_dev; - struct udevice *vmmc_dev; -#endif -#ifdef CONFIG_DM_GPIO - struct gpio_desc cd_gpio; - struct gpio_desc wp_gpio; -#endif }; /* Return the XFERTYP flags for a given command and data packet */ @@ -264,8 +215,7 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, { int timeout; struct fsl_esdhc *regs = priv->esdhc_regs; -#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \ - defined(CONFIG_IMX8) || defined(CONFIG_IMX8M) +#if defined(CONFIG_FSL_LAYERSCAPE) dma_addr_t addr; #endif uint wml_value; @@ -278,8 +228,7 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value); #ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO -#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \ - defined(CONFIG_IMX8) || defined(CONFIG_IMX8M) +#if defined(CONFIG_FSL_LAYERSCAPE) addr = virt_to_phys((void *)(data->dest)); if (upper_32_bits(addr)) printf("Error found for upper 32 bits\n"); @@ -303,20 +252,12 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); return -ETIMEDOUT; } - } else { -#ifdef CONFIG_DM_GPIO - if (dm_gpio_is_valid(&priv->wp_gpio) && dm_gpio_get_value(&priv->wp_gpio)) { - printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); - return -ETIMEDOUT; - } -#endif } esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK, wml_value << 16); #ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO -#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \ - defined(CONFIG_IMX8) || defined(CONFIG_IMX8M) +#if defined(CONFIG_FSL_LAYERSCAPE) addr = virt_to_phys((void *)(data->src)); if (upper_32_bits(addr)) printf("Error found for upper 32 bits\n"); @@ -381,8 +322,7 @@ static void check_and_invalidate_dcache_range unsigned end = 0; unsigned size = roundup(ARCH_DMA_MINALIGN, data->blocks*data->blocksize); -#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \ - defined(CONFIG_IMX8) || defined(CONFIG_IMX8M) +#if defined(CONFIG_FSL_LAYERSCAPE) dma_addr_t addr; addr = virt_to_phys((void *)(data->dest)); @@ -397,25 +337,6 @@ static void check_and_invalidate_dcache_range invalidate_dcache_range(start, end); } -#ifdef CONFIG_MCF5441x -/* - * Swaps 32-bit words to little-endian byte order. - */ -static inline void sd_swap_dma_buff(struct mmc_data *data) -{ - int i, size = data->blocksize >> 2; - u32 *buffer = (u32 *)data->dest; - u32 sw; - - while (data->blocks--) { - for (i = 0; i < size; i++) { - sw = __sw32(*buffer); - *buffer++ = sw; - } - } -} -#endif - /* * Sends a command out on the bus. Takes the mmc pointer, * a command pointer, and an optional data pointer. @@ -472,14 +393,7 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc, /* Send the command */ esdhc_write32(®s->cmdarg, cmd->cmdarg); -#if defined(CONFIG_FSL_USDHC) - esdhc_write32(®s->mixctrl, - (esdhc_read32(®s->mixctrl) & 0xFFFFFF80) | (xfertyp & 0x7F) - | (mmc->ddr_mode ? XFERTYP_DDREN : 0)); - esdhc_write32(®s->xfertyp, xfertyp & 0xFFFF0000); -#else esdhc_write32(®s->xfertyp, xfertyp); -#endif if ((cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK) || (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)) @@ -506,15 +420,6 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc, goto out; } - /* Switch voltage to 1.8V if CMD11 succeeded */ - if (cmd->cmdidx == SD_CMD_SWITCH_UHS18V) { - esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); - - printf("Run CMD11 1.8V switch\n"); - /* Sleep for 5 ms - max time for card to switch to 1.8V */ - udelay(5000); - } - /* Workaround for ESDHC errata ENGcm03648 */ if (!data && (cmd->resp_type & MMC_RSP_BUSY)) { int timeout = 6000; @@ -580,9 +485,6 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc, */ if (data->flags & MMC_DATA_READ) { check_and_invalidate_dcache_range(cmd, data); -#ifdef CONFIG_MCF5441x - sd_swap_dma_buff(data); -#endif } #endif } @@ -602,10 +504,6 @@ out: while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTD)) ; } - - /* If this was CMD11, then notify that power cycle is needed */ - if (cmd->cmdidx == SD_CMD_SWITCH_UHS18V) - printf("CMD11 to switch to 1.8V mode failed, card requires power cycle.\n"); } esdhc_write32(®s->irqstat, -1); @@ -617,16 +515,7 @@ static void set_sysctl(struct fsl_esdhc_priv *priv, struct mmc *mmc, uint clock) { struct fsl_esdhc *regs = priv->esdhc_regs; int div = 1; -#ifdef ARCH_MXC -#ifdef CONFIG_MX53 - /* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */ - int pre_div = (regs == (struct fsl_esdhc *)MMC_SDHC3_BASE_ADDR) ? 2 : 1; -#else - int pre_div = 1; -#endif -#else int pre_div = 2; -#endif int ddr_pre_div = mmc->ddr_mode ? 2 : 1; int sdhc_clk = priv->sdhc_clk; uint clk; @@ -645,21 +534,13 @@ static void set_sysctl(struct fsl_esdhc_priv *priv, struct mmc *mmc, uint clock) clk = (pre_div << 8) | (div << 4); -#ifdef CONFIG_FSL_USDHC - esdhc_clrbits32(®s->vendorspec, VENDORSPEC_CKEN); -#else esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN); -#endif esdhc_clrsetbits32(®s->sysctl, SYSCTL_CLOCK_MASK, clk); udelay(10000); -#ifdef CONFIG_FSL_USDHC - esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN | VENDORSPEC_CKEN); -#else esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_CKEN); -#endif priv->clock = clock; } @@ -693,317 +574,20 @@ static void esdhc_clock_control(struct fsl_esdhc_priv *priv, bool enable) } #endif -#ifdef MMC_SUPPORTS_TUNING -static int esdhc_change_pinstate(struct udevice *dev) -{ - struct fsl_esdhc_priv *priv = dev_get_priv(dev); - int ret; - - switch (priv->mode) { - case UHS_SDR50: - case UHS_DDR50: - ret = pinctrl_select_state(dev, "state_100mhz"); - break; - case UHS_SDR104: - case MMC_HS_200: - case MMC_HS_400: - ret = pinctrl_select_state(dev, "state_200mhz"); - break; - default: - ret = pinctrl_select_state(dev, "default"); - break; - } - - if (ret) - printf("%s %d error\n", __func__, priv->mode); - - return ret; -} - -static void esdhc_reset_tuning(struct mmc *mmc) -{ - struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev); - struct fsl_esdhc *regs = priv->esdhc_regs; - - if (priv->flags & ESDHC_FLAG_USDHC) { - if (priv->flags & ESDHC_FLAG_STD_TUNING) { - esdhc_clrbits32(®s->autoc12err, - MIX_CTRL_SMPCLK_SEL | - MIX_CTRL_EXE_TUNE); - } - } -} - -static void esdhc_set_strobe_dll(struct mmc *mmc) -{ - struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev); - struct fsl_esdhc *regs = priv->esdhc_regs; - u32 val; - - if (priv->clock > ESDHC_STROBE_DLL_CLK_FREQ) { - writel(ESDHC_STROBE_DLL_CTRL_RESET, ®s->strobe_dllctrl); - - /* - * enable strobe dll ctrl and adjust the delay target - * for the uSDHC loopback read clock - */ - val = ESDHC_STROBE_DLL_CTRL_ENABLE | - (priv->strobe_dll_delay_target << - ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); - writel(val, ®s->strobe_dllctrl); - /* wait 1us to make sure strobe dll status register stable */ - mdelay(1); - val = readl(®s->strobe_dllstat); - if (!(val & ESDHC_STROBE_DLL_STS_REF_LOCK)) - pr_warn("HS400 strobe DLL status REF not lock!\n"); - if (!(val & ESDHC_STROBE_DLL_STS_SLV_LOCK)) - pr_warn("HS400 strobe DLL status SLV not lock!\n"); - } -} - -static int esdhc_set_timing(struct mmc *mmc) -{ - struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev); - struct fsl_esdhc *regs = priv->esdhc_regs; - u32 mixctrl; - - mixctrl = readl(®s->mixctrl); - mixctrl &= ~(MIX_CTRL_DDREN | MIX_CTRL_HS400_EN); - - switch (mmc->selected_mode) { - case MMC_LEGACY: - case SD_LEGACY: - esdhc_reset_tuning(mmc); - writel(mixctrl, ®s->mixctrl); - break; - case MMC_HS_400: - mixctrl |= MIX_CTRL_DDREN | MIX_CTRL_HS400_EN; - writel(mixctrl, ®s->mixctrl); - esdhc_set_strobe_dll(mmc); - break; - case MMC_HS: - case MMC_HS_52: - case MMC_HS_200: - case SD_HS: - case UHS_SDR12: - case UHS_SDR25: - case UHS_SDR50: - case UHS_SDR104: - writel(mixctrl, ®s->mixctrl); - break; - case UHS_DDR50: - case MMC_DDR_52: - mixctrl |= MIX_CTRL_DDREN; - writel(mixctrl, ®s->mixctrl); - break; - default: - printf("Not supported %d\n", mmc->selected_mode); - return -EINVAL; - } - - priv->mode = mmc->selected_mode; - - return esdhc_change_pinstate(mmc->dev); -} - -static int esdhc_set_voltage(struct mmc *mmc) -{ - struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev); - struct fsl_esdhc *regs = priv->esdhc_regs; - int ret; - - priv->signal_voltage = mmc->signal_voltage; - switch (mmc->signal_voltage) { - case MMC_SIGNAL_VOLTAGE_330: - if (priv->vs18_enable) - return -EIO; -#if CONFIG_IS_ENABLED(DM_REGULATOR) - if (!IS_ERR_OR_NULL(priv->vqmmc_dev)) { - ret = regulator_set_value(priv->vqmmc_dev, 3300000); - if (ret) { - printf("Setting to 3.3V error"); - return -EIO; - } - /* Wait for 5ms */ - mdelay(5); - } -#endif - - esdhc_clrbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); - if (!(esdhc_read32(®s->vendorspec) & - ESDHC_VENDORSPEC_VSELECT)) - return 0; - - return -EAGAIN; - case MMC_SIGNAL_VOLTAGE_180: -#if CONFIG_IS_ENABLED(DM_REGULATOR) - if (!IS_ERR_OR_NULL(priv->vqmmc_dev)) { - ret = regulator_set_value(priv->vqmmc_dev, 1800000); - if (ret) { - printf("Setting to 1.8V error"); - return -EIO; - } - } -#endif - esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); - if (esdhc_read32(®s->vendorspec) & ESDHC_VENDORSPEC_VSELECT) - return 0; - - return -EAGAIN; - case MMC_SIGNAL_VOLTAGE_120: - return -ENOTSUPP; - default: - return 0; - } -} - -static void esdhc_stop_tuning(struct mmc *mmc) -{ - struct mmc_cmd cmd; - - cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; - cmd.cmdarg = 0; - cmd.resp_type = MMC_RSP_R1b; - - dm_mmc_send_cmd(mmc->dev, &cmd, NULL); -} - -static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode) -{ - struct fsl_esdhc_plat *plat = dev_get_platdata(dev); - struct fsl_esdhc_priv *priv = dev_get_priv(dev); - struct fsl_esdhc *regs = priv->esdhc_regs; - struct mmc *mmc = &plat->mmc; - u32 irqstaten = readl(®s->irqstaten); - u32 irqsigen = readl(®s->irqsigen); - int i, ret = -ETIMEDOUT; - u32 val, mixctrl; - - /* clock tuning is not needed for upto 52MHz */ - if (mmc->clock <= 52000000) - return 0; - - /* This is readw/writew SDHCI_HOST_CONTROL2 when tuning */ - if (priv->flags & ESDHC_FLAG_STD_TUNING) { - val = readl(®s->autoc12err); - mixctrl = readl(®s->mixctrl); - val &= ~MIX_CTRL_SMPCLK_SEL; - mixctrl &= ~(MIX_CTRL_FBCLK_SEL | MIX_CTRL_AUTO_TUNE_EN); - - val |= MIX_CTRL_EXE_TUNE; - mixctrl |= MIX_CTRL_FBCLK_SEL | MIX_CTRL_AUTO_TUNE_EN; - - writel(val, ®s->autoc12err); - writel(mixctrl, ®s->mixctrl); - } - - /* sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); */ - mixctrl = readl(®s->mixctrl); - mixctrl = MIX_CTRL_DTDSEL_READ | (mixctrl & ~MIX_CTRL_SDHCI_MASK); - writel(mixctrl, ®s->mixctrl); - - writel(IRQSTATEN_BRR, ®s->irqstaten); - writel(IRQSTATEN_BRR, ®s->irqsigen); - - /* - * Issue opcode repeatedly till Execute Tuning is set to 0 or the number - * of loops reaches 40 times. - */ - for (i = 0; i < MAX_TUNING_LOOP; i++) { - u32 ctrl; - - if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200) { - if (mmc->bus_width == 8) - writel(0x7080, ®s->blkattr); - else if (mmc->bus_width == 4) - writel(0x7040, ®s->blkattr); - } else { - writel(0x7040, ®s->blkattr); - } - - /* sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE) */ - val = readl(®s->mixctrl); - val = MIX_CTRL_DTDSEL_READ | (val & ~MIX_CTRL_SDHCI_MASK); - writel(val, ®s->mixctrl); - - /* We are using STD tuning, no need to check return value */ - mmc_send_tuning(mmc, opcode, NULL); - - ctrl = readl(®s->autoc12err); - if ((!(ctrl & MIX_CTRL_EXE_TUNE)) && - (ctrl & MIX_CTRL_SMPCLK_SEL)) { - /* - * need to wait some time, make sure sd/mmc fininsh - * send out tuning data, otherwise, the sd/mmc can't - * response to any command when the card still out - * put the tuning data. - */ - mdelay(1); - ret = 0; - break; - } - - /* Add 1ms delay for SD and eMMC */ - mdelay(1); - } - - writel(irqstaten, ®s->irqstaten); - writel(irqsigen, ®s->irqsigen); - - esdhc_stop_tuning(mmc); - - return ret; -} -#endif - static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) { struct fsl_esdhc *regs = priv->esdhc_regs; - int ret __maybe_unused; #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK /* Select to use peripheral clock */ esdhc_clock_control(priv, false); - esdhc_setbits32(®s->scr, ESDHCCTL_PCS); + esdhc_setbits32(®s->esdhcctl, ESDHCCTL_PCS); esdhc_clock_control(priv, true); #endif /* Set the clock speed */ if (priv->clock != mmc->clock) set_sysctl(priv, mmc, mmc->clock); -#ifdef MMC_SUPPORTS_TUNING - if (mmc->clk_disable) { -#ifdef CONFIG_FSL_USDHC - esdhc_clrbits32(®s->vendorspec, VENDORSPEC_CKEN); -#else - esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN); -#endif - } else { -#ifdef CONFIG_FSL_USDHC - esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN | - VENDORSPEC_CKEN); -#else - esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_CKEN); -#endif - } - - if (priv->mode != mmc->selected_mode) { - ret = esdhc_set_timing(mmc); - if (ret) { - printf("esdhc_set_timing error %d\n", ret); - return ret; - } - } - - if (priv->signal_voltage != mmc->signal_voltage) { - ret = esdhc_set_voltage(mmc); - if (ret) { - printf("esdhc_set_voltage error %d\n", ret); - return ret; - } - } -#endif - /* Set the bus width */ esdhc_clrbits32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); @@ -1030,34 +614,10 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) return -ETIMEDOUT; } -#if defined(CONFIG_FSL_USDHC) - /* RSTA doesn't reset MMC_BOOT register, so manually reset it */ - esdhc_write32(®s->mmcboot, 0x0); - /* Reset MIX_CTRL and CLK_TUNE_CTRL_STATUS regs to 0 */ - esdhc_write32(®s->mixctrl, 0x0); - esdhc_write32(®s->clktunectrlstatus, 0x0); - - /* Put VEND_SPEC to default value */ - if (priv->vs18_enable) - esdhc_write32(®s->vendorspec, (VENDORSPEC_INIT | - ESDHC_VENDORSPEC_VSELECT)); - else - esdhc_write32(®s->vendorspec, VENDORSPEC_INIT); - - /* Disable DLL_CTRL delay line */ - esdhc_write32(®s->dllctrl, 0x0); -#endif - -#ifndef ARCH_MXC /* Enable cache snooping */ - esdhc_write32(®s->scr, 0x00000040); -#endif + esdhc_write32(®s->esdhcctl, 0x00000040); -#ifndef CONFIG_FSL_USDHC esdhc_setbits32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); -#else - esdhc_setbits32(®s->vendorspec, VENDORSPEC_HCKEN | VENDORSPEC_IPGEN); -#endif /* Set the initial clock speed */ mmc_set_clock(mmc, 400000, MMC_CLK_ENABLE); @@ -1065,12 +625,8 @@ static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) /* Disable the BRR and BWR bits in IRQSTAT */ esdhc_clrbits32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); -#ifdef CONFIG_MCF5441x - esdhc_write32(®s->proctl, PROCTL_INIT | PROCTL_D3CD); -#else /* Put the PROCTL reg back to the default */ esdhc_write32(®s->proctl, PROCTL_INIT); -#endif /* Set timout to the maximum value */ esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16); @@ -1091,10 +647,6 @@ static int esdhc_getcd_common(struct fsl_esdhc_priv *priv) #if CONFIG_IS_ENABLED(DM_MMC) if (priv->non_removable) return 1; -#ifdef CONFIG_DM_GPIO - if (dm_gpio_is_valid(&priv->cd_gpio)) - return dm_gpio_get_value(&priv->cd_gpio); -#endif #endif while (!(esdhc_read32(®s->prsstat) & PRSSTAT_CINS) && --timeout) @@ -1178,25 +730,8 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv, if (ret) return ret; -#ifdef CONFIG_MCF5441x - /* ColdFire, using SDHC_DATA[3] for card detection */ - esdhc_write32(®s->proctl, PROCTL_INIT | PROCTL_D3CD); -#endif - -#ifndef CONFIG_FSL_USDHC - esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN - | SYSCTL_IPGEN | SYSCTL_CKEN); - /* Clearing tuning bits in case ROM has set it already */ - esdhc_write32(®s->mixctrl, 0); - esdhc_write32(®s->autoc12err, 0); - esdhc_write32(®s->clktunectrlstatus, 0); -#else - esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN | - VENDORSPEC_HCKEN | VENDORSPEC_IPGEN | VENDORSPEC_CKEN); -#endif - - if (priv->vs18_enable) - esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); + esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN | + SYSCTL_IPGEN | SYSCTL_CKEN); writel(SDHCI_IRQ_EN_BITS, ®s->irqstaten); cfg = &plat->cfg; @@ -1207,15 +742,6 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv, voltage_caps = 0; caps = esdhc_read32(®s->hostcapblt); -#ifdef CONFIG_MCF5441x - /* - * MCF5441x RM declares in more points that sdhc clock speed must - * never exceed 25 Mhz. From this, the HS bit needs to be disabled - * from host capabilities. - */ - caps &= ~ESDHC_HOSTCAPBLT_HSS; -#endif - #ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC135 caps = caps & ~(ESDHC_HOSTCAPBLT_SRS | ESDHC_HOSTCAPBLT_VS18 | ESDHC_HOSTCAPBLT_VS30); @@ -1272,27 +798,11 @@ static int fsl_esdhc_init(struct fsl_esdhc_priv *priv, cfg->host_caps &= ~MMC_MODE_8BIT; #endif - cfg->host_caps |= priv->caps; - cfg->f_min = 400000; cfg->f_max = min(priv->sdhc_clk, (u32)200000000); cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; - writel(0, ®s->dllctrl); - if (priv->flags & ESDHC_FLAG_USDHC) { - if (priv->flags & ESDHC_FLAG_STD_TUNING) { - u32 val = readl(®s->tuning_ctrl); - - val |= ESDHC_STD_TUNING_EN; - val &= ~ESDHC_TUNING_START_TAP_MASK; - val |= priv->tuning_start_tap; - val &= ~ESDHC_TUNING_STEP_MASK; - val |= (priv->tuning_step) << ESDHC_TUNING_STEP_SHIFT; - writel(val, ®s->tuning_ctrl); - } - } - return 0; } @@ -1307,7 +817,6 @@ static int fsl_esdhc_cfg_to_priv(struct fsl_esdhc_cfg *cfg, priv->bus_width = cfg->max_bus_width; priv->sdhc_clk = cfg->sdhc_clk; priv->wp_enable = cfg->wp_enable; - priv->vs18_enable = cfg->vs18_enable; return 0; }; @@ -1444,22 +953,11 @@ void fdt_fixup_esdhc(void *blob, bd_t *bd) #ifndef CONFIG_PPC #include <asm/arch/clock.h> #endif -__weak void init_clk_usdhc(u32 index) -{ -} - static int fsl_esdhc_probe(struct udevice *dev) { struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); struct fsl_esdhc_plat *plat = dev_get_platdata(dev); struct fsl_esdhc_priv *priv = dev_get_priv(dev); - const void *fdt = gd->fdt_blob; - int node = dev_of_offset(dev); - struct esdhc_soc_data *data = - (struct esdhc_soc_data *)dev_get_driver_data(dev); -#if CONFIG_IS_ENABLED(DM_REGULATOR) - struct udevice *vqmmc_dev; -#endif fdt_addr_t addr; unsigned int val; struct mmc *mmc; @@ -1477,11 +975,6 @@ static int fsl_esdhc_probe(struct udevice *dev) priv->esdhc_regs = (struct fsl_esdhc *)addr; #endif priv->dev = dev; - priv->mode = -1; - if (data) { - priv->flags = data->flags; - priv->caps = data->caps; - } val = dev_read_u32_default(dev, "bus-width", -1); if (val == 8) @@ -1491,81 +984,13 @@ static int fsl_esdhc_probe(struct udevice *dev) else priv->bus_width = 1; - val = fdtdec_get_int(fdt, node, "fsl,tuning-step", 1); - priv->tuning_step = val; - val = fdtdec_get_int(fdt, node, "fsl,tuning-start-tap", - ESDHC_TUNING_START_TAP_DEFAULT); - priv->tuning_start_tap = val; - val = fdtdec_get_int(fdt, node, "fsl,strobe-dll-delay-target", - ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT); - priv->strobe_dll_delay_target = val; - if (dev_read_bool(dev, "non-removable")) { priv->non_removable = 1; } else { priv->non_removable = 0; -#ifdef CONFIG_DM_GPIO - gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, - GPIOD_IS_IN); -#endif } - if (dev_read_prop(dev, "fsl,wp-controller", NULL)) { - priv->wp_enable = 1; - } else { - priv->wp_enable = 0; -#ifdef CONFIG_DM_GPIO - gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, - GPIOD_IS_IN); -#endif - } - - priv->vs18_enable = 0; - -#if CONFIG_IS_ENABLED(DM_REGULATOR) - /* - * If emmc I/O has a fixed voltage at 1.8V, this must be provided, - * otherwise, emmc will work abnormally. - */ - ret = device_get_supply_regulator(dev, "vqmmc-supply", &vqmmc_dev); - if (ret) { - dev_dbg(dev, "no vqmmc-supply\n"); - } else { - ret = regulator_set_enable(vqmmc_dev, true); - if (ret) { - dev_err(dev, "fail to enable vqmmc-supply\n"); - return ret; - } - - if (regulator_get_value(vqmmc_dev) == 1800000) - priv->vs18_enable = 1; - } -#endif - - if (fdt_get_property(fdt, node, "no-1-8-v", NULL)) - priv->caps &= ~(UHS_CAPS | MMC_MODE_HS200 | MMC_MODE_HS400); - - /* - * TODO: - * Because lack of clk driver, if SDHC clk is not enabled, - * need to enable it first before this driver is invoked. - * - * we use MXC_ESDHC_CLK to get clk freq. - * If one would like to make this function work, - * the aliases should be provided in dts as this: - * - * aliases { - * mmc0 = &usdhc1; - * mmc1 = &usdhc2; - * mmc2 = &usdhc3; - * mmc3 = &usdhc4; - * }; - * Then if your board only supports mmc2 and mmc3, but we can - * correctly get the seq as 2 and 3, then let mxc_get_clock - * work as expected. - */ - - init_clk_usdhc(dev->seq); + priv->wp_enable = 1; if (IS_ENABLED(CONFIG_CLK)) { /* Assigned clock already set clock */ @@ -1656,28 +1081,10 @@ static const struct dm_mmc_ops fsl_esdhc_ops = { .get_cd = fsl_esdhc_get_cd, .send_cmd = fsl_esdhc_send_cmd, .set_ios = fsl_esdhc_set_ios, -#ifdef MMC_SUPPORTS_TUNING - .execute_tuning = fsl_esdhc_execute_tuning, -#endif }; #endif -static struct esdhc_soc_data usdhc_imx7d_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING - | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 - | ESDHC_FLAG_HS400, - .caps = UHS_CAPS | MMC_MODE_HS200 | MMC_MODE_DDR_52MHz | - MMC_MODE_HS_52MHz | MMC_MODE_HS, -}; - static const struct udevice_id fsl_esdhc_ids[] = { - { .compatible = "fsl,imx53-esdhc", }, - { .compatible = "fsl,imx6ul-usdhc", }, - { .compatible = "fsl,imx6sx-usdhc", }, - { .compatible = "fsl,imx6sl-usdhc", }, - { .compatible = "fsl,imx6q-usdhc", }, - { .compatible = "fsl,imx7d-usdhc", .data = (ulong)&usdhc_imx7d_data,}, - { .compatible = "fsl,imx7ulp-usdhc", }, { .compatible = "fsl,esdhc", }, { /* sentinel */ } }; diff --git a/drivers/mmc/fsl_esdhc_imx.c b/drivers/mmc/fsl_esdhc_imx.c new file mode 100644 index 0000000000..c0d47ba378 --- /dev/null +++ b/drivers/mmc/fsl_esdhc_imx.c @@ -0,0 +1,1650 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2007, 2010-2011 Freescale Semiconductor, Inc + * Copyright 2019 NXP Semiconductors + * Andy Fleming + * Yangbo Lu <yangbo.lu@nxp.com> + * + * Based vaguely on the pxa mmc code: + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + */ + +#include <config.h> +#include <common.h> +#include <command.h> +#include <clk.h> +#include <errno.h> +#include <hwconfig.h> +#include <mmc.h> +#include <part.h> +#include <power/regulator.h> +#include <malloc.h> +#include <fsl_esdhc_imx.h> +#include <fdt_support.h> +#include <asm/io.h> +#include <dm.h> +#include <asm-generic/gpio.h> +#include <dm/pinctrl.h> + +#if !CONFIG_IS_ENABLED(BLK) +#include "mmc_private.h" +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#define SDHCI_IRQ_EN_BITS (IRQSTATEN_CC | IRQSTATEN_TC | \ + IRQSTATEN_CINT | \ + IRQSTATEN_CTOE | IRQSTATEN_CCE | IRQSTATEN_CEBE | \ + IRQSTATEN_CIE | IRQSTATEN_DTOE | IRQSTATEN_DCE | \ + IRQSTATEN_DEBE | IRQSTATEN_BRR | IRQSTATEN_BWR | \ + IRQSTATEN_DINT) +#define MAX_TUNING_LOOP 40 +#define ESDHC_DRIVER_STAGE_VALUE 0xffffffff + +struct fsl_esdhc { + uint dsaddr; /* SDMA system address register */ + uint blkattr; /* Block attributes register */ + uint cmdarg; /* Command argument register */ + uint xfertyp; /* Transfer type register */ + uint cmdrsp0; /* Command response 0 register */ + uint cmdrsp1; /* Command response 1 register */ + uint cmdrsp2; /* Command response 2 register */ + uint cmdrsp3; /* Command response 3 register */ + uint datport; /* Buffer data port register */ + uint prsstat; /* Present state register */ + uint proctl; /* Protocol control register */ + uint sysctl; /* System Control Register */ + uint irqstat; /* Interrupt status register */ + uint irqstaten; /* Interrupt status enable register */ + uint irqsigen; /* Interrupt signal enable register */ + uint autoc12err; /* Auto CMD error status register */ + uint hostcapblt; /* Host controller capabilities register */ + uint wml; /* Watermark level register */ + uint mixctrl; /* For USDHC */ + char reserved1[4]; /* reserved */ + uint fevt; /* Force event register */ + uint admaes; /* ADMA error status register */ + uint adsaddr; /* ADMA system address register */ + char reserved2[4]; + uint dllctrl; + uint dllstat; + uint clktunectrlstatus; + char reserved3[4]; + uint strobe_dllctrl; + uint strobe_dllstat; + char reserved4[72]; + uint vendorspec; + uint mmcboot; + uint vendorspec2; + uint tuning_ctrl; /* on i.MX6/7/8 */ + char reserved5[44]; + uint hostver; /* Host controller version register */ + char reserved6[4]; /* reserved */ + uint dmaerraddr; /* DMA error address register */ + char reserved7[4]; /* reserved */ + uint dmaerrattr; /* DMA error attribute register */ + char reserved8[4]; /* reserved */ + uint hostcapblt2; /* Host controller capabilities register 2 */ + char reserved9[8]; /* reserved */ + uint tcr; /* Tuning control register */ + char reserved10[28]; /* reserved */ + uint sddirctl; /* SD direction control register */ + char reserved11[712];/* reserved */ + uint scr; /* eSDHC control register */ +}; + +struct fsl_esdhc_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +struct esdhc_soc_data { + u32 flags; + u32 caps; +}; + +/** + * struct fsl_esdhc_priv + * + * @esdhc_regs: registers of the sdhc controller + * @sdhc_clk: Current clk of the sdhc controller + * @bus_width: bus width, 1bit, 4bit or 8bit + * @cfg: mmc config + * @mmc: mmc + * Following is used when Driver Model is enabled for MMC + * @dev: pointer for the device + * @non_removable: 0: removable; 1: non-removable + * @wp_enable: 1: enable checking wp; 0: no check + * @vs18_enable: 1: use 1.8V voltage; 0: use 3.3V + * @flags: ESDHC_FLAG_xx in include/fsl_esdhc_imx.h + * @caps: controller capabilities + * @tuning_step: tuning step setting in tuning_ctrl register + * @start_tuning_tap: the start point for tuning in tuning_ctrl register + * @strobe_dll_delay_target: settings in strobe_dllctrl + * @signal_voltage: indicating the current voltage + * @cd_gpio: gpio for card detection + * @wp_gpio: gpio for write protection + */ +struct fsl_esdhc_priv { + struct fsl_esdhc *esdhc_regs; + unsigned int sdhc_clk; + struct clk per_clk; + unsigned int clock; + unsigned int mode; + unsigned int bus_width; +#if !CONFIG_IS_ENABLED(BLK) + struct mmc *mmc; +#endif + struct udevice *dev; + int non_removable; + int wp_enable; + int vs18_enable; + u32 flags; + u32 caps; + u32 tuning_step; + u32 tuning_start_tap; + u32 strobe_dll_delay_target; + u32 signal_voltage; +#if IS_ENABLED(CONFIG_DM_REGULATOR) + struct udevice *vqmmc_dev; + struct udevice *vmmc_dev; +#endif +#ifdef CONFIG_DM_GPIO + struct gpio_desc cd_gpio; + struct gpio_desc wp_gpio; +#endif +}; + +/* Return the XFERTYP flags for a given command and data packet */ +static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) +{ + uint xfertyp = 0; + + if (data) { + xfertyp |= XFERTYP_DPSEL; +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO + xfertyp |= XFERTYP_DMAEN; +#endif + if (data->blocks > 1) { + xfertyp |= XFERTYP_MSBSEL; + xfertyp |= XFERTYP_BCEN; +#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111 + xfertyp |= XFERTYP_AC12EN; +#endif + } + + if (data->flags & MMC_DATA_READ) + xfertyp |= XFERTYP_DTDSEL; + } + + if (cmd->resp_type & MMC_RSP_CRC) + xfertyp |= XFERTYP_CCCEN; + if (cmd->resp_type & MMC_RSP_OPCODE) + xfertyp |= XFERTYP_CICEN; + if (cmd->resp_type & MMC_RSP_136) + xfertyp |= XFERTYP_RSPTYP_136; + else if (cmd->resp_type & MMC_RSP_BUSY) + xfertyp |= XFERTYP_RSPTYP_48_BUSY; + else if (cmd->resp_type & MMC_RSP_PRESENT) + xfertyp |= XFERTYP_RSPTYP_48; + + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + xfertyp |= XFERTYP_CMDTYP_ABORT; + + return XFERTYP_CMD(cmd->cmdidx) | xfertyp; +} + +#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO +/* + * PIO Read/Write Mode reduce the performace as DMA is not used in this mode. + */ +static void esdhc_pio_read_write(struct fsl_esdhc_priv *priv, + struct mmc_data *data) +{ + struct fsl_esdhc *regs = priv->esdhc_regs; + uint blocks; + char *buffer; + uint databuf; + uint size; + uint irqstat; + ulong start; + + if (data->flags & MMC_DATA_READ) { + blocks = data->blocks; + buffer = data->dest; + while (blocks) { + start = get_timer(0); + size = data->blocksize; + irqstat = esdhc_read32(®s->irqstat); + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_BREN)) { + if (get_timer(start) > PIO_TIMEOUT) { + printf("\nData Read Failed in PIO Mode."); + return; + } + } + while (size && (!(irqstat & IRQSTAT_TC))) { + udelay(100); /* Wait before last byte transfer complete */ + irqstat = esdhc_read32(®s->irqstat); + databuf = in_le32(®s->datport); + *((uint *)buffer) = databuf; + buffer += 4; + size -= 4; + } + blocks--; + } + } else { + blocks = data->blocks; + buffer = (char *)data->src; + while (blocks) { + start = get_timer(0); + size = data->blocksize; + irqstat = esdhc_read32(®s->irqstat); + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_BWEN)) { + if (get_timer(start) > PIO_TIMEOUT) { + printf("\nData Write Failed in PIO Mode."); + return; + } + } + while (size && (!(irqstat & IRQSTAT_TC))) { + udelay(100); /* Wait before last byte transfer complete */ + databuf = *((uint *)buffer); + buffer += 4; + size -= 4; + irqstat = esdhc_read32(®s->irqstat); + out_le32(®s->datport, databuf); + } + blocks--; + } + } +} +#endif + +static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, + struct mmc_data *data) +{ + int timeout; + struct fsl_esdhc *regs = priv->esdhc_regs; +#if defined(CONFIG_S32V234) || defined(CONFIG_IMX8) || defined(CONFIG_IMX8M) + dma_addr_t addr; +#endif + uint wml_value; + + wml_value = data->blocksize/4; + + if (data->flags & MMC_DATA_READ) { + if (wml_value > WML_RD_WML_MAX) + wml_value = WML_RD_WML_MAX_VAL; + + esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value); +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO +#if defined(CONFIG_S32V234) || defined(CONFIG_IMX8) || defined(CONFIG_IMX8M) + addr = virt_to_phys((void *)(data->dest)); + if (upper_32_bits(addr)) + printf("Error found for upper 32 bits\n"); + else + esdhc_write32(®s->dsaddr, lower_32_bits(addr)); +#else + esdhc_write32(®s->dsaddr, (u32)data->dest); +#endif +#endif + } else { +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO + flush_dcache_range((ulong)data->src, + (ulong)data->src+data->blocks + *data->blocksize); +#endif + if (wml_value > WML_WR_WML_MAX) + wml_value = WML_WR_WML_MAX_VAL; + if (priv->wp_enable) { + if ((esdhc_read32(®s->prsstat) & + PRSSTAT_WPSPL) == 0) { + printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); + return -ETIMEDOUT; + } + } else { +#ifdef CONFIG_DM_GPIO + if (dm_gpio_is_valid(&priv->wp_gpio) && dm_gpio_get_value(&priv->wp_gpio)) { + printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); + return -ETIMEDOUT; + } +#endif + } + + esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK, + wml_value << 16); +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO +#if defined(CONFIG_S32V234) || defined(CONFIG_IMX8) || defined(CONFIG_IMX8M) + addr = virt_to_phys((void *)(data->src)); + if (upper_32_bits(addr)) + printf("Error found for upper 32 bits\n"); + else + esdhc_write32(®s->dsaddr, lower_32_bits(addr)); +#else + esdhc_write32(®s->dsaddr, (u32)data->src); +#endif +#endif + } + + esdhc_write32(®s->blkattr, data->blocks << 16 | data->blocksize); + + /* Calculate the timeout period for data transactions */ + /* + * 1)Timeout period = (2^(timeout+13)) SD Clock cycles + * 2)Timeout period should be minimum 0.250sec as per SD Card spec + * So, Number of SD Clock cycles for 0.25sec should be minimum + * (SD Clock/sec * 0.25 sec) SD Clock cycles + * = (mmc->clock * 1/4) SD Clock cycles + * As 1) >= 2) + * => (2^(timeout+13)) >= mmc->clock * 1/4 + * Taking log2 both the sides + * => timeout + 13 >= log2(mmc->clock/4) + * Rounding up to next power of 2 + * => timeout + 13 = log2(mmc->clock/4) + 1 + * => timeout + 13 = fls(mmc->clock/4) + * + * However, the MMC spec "It is strongly recommended for hosts to + * implement more than 500ms timeout value even if the card + * indicates the 250ms maximum busy length." Even the previous + * value of 300ms is known to be insufficient for some cards. + * So, we use + * => timeout + 13 = fls(mmc->clock/2) + */ + timeout = fls(mmc->clock/2); + timeout -= 13; + + if (timeout > 14) + timeout = 14; + + if (timeout < 0) + timeout = 0; + +#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC_A001 + if ((timeout == 4) || (timeout == 8) || (timeout == 12)) + timeout++; +#endif + +#ifdef ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE + timeout = 0xE; +#endif + esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16); + + return 0; +} + +static void check_and_invalidate_dcache_range + (struct mmc_cmd *cmd, + struct mmc_data *data) { + unsigned start = 0; + unsigned end = 0; + unsigned size = roundup(ARCH_DMA_MINALIGN, + data->blocks*data->blocksize); +#if defined(CONFIG_S32V234) || defined(CONFIG_IMX8) || defined(CONFIG_IMX8M) + dma_addr_t addr; + + addr = virt_to_phys((void *)(data->dest)); + if (upper_32_bits(addr)) + printf("Error found for upper 32 bits\n"); + else + start = lower_32_bits(addr); +#else + start = (unsigned)data->dest; +#endif + end = start + size; + invalidate_dcache_range(start, end); +} + +#ifdef CONFIG_MCF5441x +/* + * Swaps 32-bit words to little-endian byte order. + */ +static inline void sd_swap_dma_buff(struct mmc_data *data) +{ + int i, size = data->blocksize >> 2; + u32 *buffer = (u32 *)data->dest; + u32 sw; + + while (data->blocks--) { + for (i = 0; i < size; i++) { + sw = __sw32(*buffer); + *buffer++ = sw; + } + } +} +#endif + +/* + * Sends a command out on the bus. Takes the mmc pointer, + * a command pointer, and an optional data pointer. + */ +static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc, + struct mmc_cmd *cmd, struct mmc_data *data) +{ + int err = 0; + uint xfertyp; + uint irqstat; + u32 flags = IRQSTAT_CC | IRQSTAT_CTOE; + struct fsl_esdhc *regs = priv->esdhc_regs; + unsigned long start; + +#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111 + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + return 0; +#endif + + esdhc_write32(®s->irqstat, -1); + + sync(); + + /* Wait for the bus to be idle */ + while ((esdhc_read32(®s->prsstat) & PRSSTAT_CICHB) || + (esdhc_read32(®s->prsstat) & PRSSTAT_CIDHB)) + ; + + while (esdhc_read32(®s->prsstat) & PRSSTAT_DLA) + ; + + /* Wait at least 8 SD clock cycles before the next command */ + /* + * Note: This is way more than 8 cycles, but 1ms seems to + * resolve timing issues with some cards + */ + udelay(1000); + + /* Set up for a data transfer if we have one */ + if (data) { + err = esdhc_setup_data(priv, mmc, data); + if(err) + return err; + + if (data->flags & MMC_DATA_READ) + check_and_invalidate_dcache_range(cmd, 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) + esdhc_write32(®s->mixctrl, + (esdhc_read32(®s->mixctrl) & 0xFFFFFF80) | (xfertyp & 0x7F) + | (mmc->ddr_mode ? XFERTYP_DDREN : 0)); + esdhc_write32(®s->xfertyp, xfertyp & 0xFFFF0000); +#else + esdhc_write32(®s->xfertyp, xfertyp); +#endif + + if ((cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK) || + (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)) + flags = IRQSTAT_BRR; + + /* Wait for the command to complete */ + start = get_timer(0); + while (!(esdhc_read32(®s->irqstat) & flags)) { + if (get_timer(start) > 1000) { + err = -ETIMEDOUT; + goto out; + } + } + + irqstat = esdhc_read32(®s->irqstat); + + if (irqstat & CMD_ERR) { + err = -ECOMM; + goto out; + } + + if (irqstat & IRQSTAT_CTOE) { + err = -ETIMEDOUT; + goto out; + } + + /* Switch voltage to 1.8V if CMD11 succeeded */ + if (cmd->cmdidx == SD_CMD_SWITCH_UHS18V) { + esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); + + printf("Run CMD11 1.8V switch\n"); + /* Sleep for 5 ms - max time for card to switch to 1.8V */ + udelay(5000); + } + + /* Workaround for ESDHC errata ENGcm03648 */ + if (!data && (cmd->resp_type & MMC_RSP_BUSY)) { + int timeout = 6000; + + /* Poll on DATA0 line for cmd with busy signal for 600 ms */ + while (timeout > 0 && !(esdhc_read32(®s->prsstat) & + PRSSTAT_DAT0)) { + udelay(100); + timeout--; + } + + if (timeout <= 0) { + printf("Timeout waiting for DAT0 to go high!\n"); + err = -ETIMEDOUT; + goto out; + } + } + + /* Copy the response to the response buffer */ + if (cmd->resp_type & MMC_RSP_136) { + u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; + + cmdrsp3 = esdhc_read32(®s->cmdrsp3); + cmdrsp2 = esdhc_read32(®s->cmdrsp2); + cmdrsp1 = esdhc_read32(®s->cmdrsp1); + cmdrsp0 = esdhc_read32(®s->cmdrsp0); + cmd->response[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24); + cmd->response[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24); + cmd->response[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24); + cmd->response[3] = (cmdrsp0 << 8); + } else + cmd->response[0] = esdhc_read32(®s->cmdrsp0); + + /* Wait until all of the blocks are transferred */ + if (data) { +#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO + esdhc_pio_read_write(priv, data); +#else + flags = DATA_COMPLETE; + if ((cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK) || + (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)) { + flags = IRQSTAT_BRR; + } + + do { + irqstat = esdhc_read32(®s->irqstat); + + if (irqstat & IRQSTAT_DTOE) { + err = -ETIMEDOUT; + goto out; + } + + if (irqstat & DATA_ERR) { + err = -ECOMM; + goto out; + } + } while ((irqstat & flags) != flags); + + /* + * Need invalidate the dcache here again to avoid any + * cache-fill during the DMA operations such as the + * speculative pre-fetching etc. + */ + if (data->flags & MMC_DATA_READ) { + check_and_invalidate_dcache_range(cmd, data); +#ifdef CONFIG_MCF5441x + sd_swap_dma_buff(data); +#endif + } +#endif + } + +out: + /* Reset CMD and DATA portions on error */ + if (err) { + esdhc_write32(®s->sysctl, esdhc_read32(®s->sysctl) | + SYSCTL_RSTC); + while (esdhc_read32(®s->sysctl) & SYSCTL_RSTC) + ; + + if (data) { + esdhc_write32(®s->sysctl, + esdhc_read32(®s->sysctl) | + SYSCTL_RSTD); + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTD)) + ; + } + + /* If this was CMD11, then notify that power cycle is needed */ + if (cmd->cmdidx == SD_CMD_SWITCH_UHS18V) + printf("CMD11 to switch to 1.8V mode failed, card requires power cycle.\n"); + } + + esdhc_write32(®s->irqstat, -1); + + return err; +} + +static void set_sysctl(struct fsl_esdhc_priv *priv, struct mmc *mmc, uint clock) +{ + struct fsl_esdhc *regs = priv->esdhc_regs; + int div = 1; +#ifdef ARCH_MXC +#ifdef CONFIG_MX53 + /* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */ + int pre_div = (regs == (struct fsl_esdhc *)MMC_SDHC3_BASE_ADDR) ? 2 : 1; +#else + int pre_div = 1; +#endif +#else + int pre_div = 2; +#endif + int ddr_pre_div = mmc->ddr_mode ? 2 : 1; + int sdhc_clk = priv->sdhc_clk; + uint clk; + + if (clock < mmc->cfg->f_min) + clock = mmc->cfg->f_min; + + while (sdhc_clk / (16 * pre_div * ddr_pre_div) > clock && pre_div < 256) + pre_div *= 2; + + while (sdhc_clk / (div * pre_div * ddr_pre_div) > clock && div < 16) + div++; + + pre_div >>= 1; + div -= 1; + + clk = (pre_div << 8) | (div << 4); + +#ifdef CONFIG_FSL_USDHC + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_CKEN); +#else + esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN); +#endif + + esdhc_clrsetbits32(®s->sysctl, SYSCTL_CLOCK_MASK, clk); + + udelay(10000); + +#ifdef CONFIG_FSL_USDHC + esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN | VENDORSPEC_CKEN); +#else + esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_CKEN); +#endif + + priv->clock = clock; +} + +#ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK +static void esdhc_clock_control(struct fsl_esdhc_priv *priv, bool enable) +{ + struct fsl_esdhc *regs = priv->esdhc_regs; + u32 value; + u32 time_out; + + value = esdhc_read32(®s->sysctl); + + if (enable) + value |= SYSCTL_CKEN; + else + value &= ~SYSCTL_CKEN; + + esdhc_write32(®s->sysctl, value); + + time_out = 20; + value = PRSSTAT_SDSTB; + while (!(esdhc_read32(®s->prsstat) & value)) { + if (time_out == 0) { + printf("fsl_esdhc: Internal clock never stabilised.\n"); + break; + } + time_out--; + mdelay(1); + } +} +#endif + +#ifdef MMC_SUPPORTS_TUNING +static int esdhc_change_pinstate(struct udevice *dev) +{ + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + int ret; + + switch (priv->mode) { + case UHS_SDR50: + case UHS_DDR50: + ret = pinctrl_select_state(dev, "state_100mhz"); + break; + case UHS_SDR104: + case MMC_HS_200: + case MMC_HS_400: + ret = pinctrl_select_state(dev, "state_200mhz"); + break; + default: + ret = pinctrl_select_state(dev, "default"); + break; + } + + if (ret) + printf("%s %d error\n", __func__, priv->mode); + + return ret; +} + +static void esdhc_reset_tuning(struct mmc *mmc) +{ + struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev); + struct fsl_esdhc *regs = priv->esdhc_regs; + + if (priv->flags & ESDHC_FLAG_USDHC) { + if (priv->flags & ESDHC_FLAG_STD_TUNING) { + esdhc_clrbits32(®s->autoc12err, + MIX_CTRL_SMPCLK_SEL | + MIX_CTRL_EXE_TUNE); + } + } +} + +static void esdhc_set_strobe_dll(struct mmc *mmc) +{ + struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev); + struct fsl_esdhc *regs = priv->esdhc_regs; + u32 val; + + if (priv->clock > ESDHC_STROBE_DLL_CLK_FREQ) { + writel(ESDHC_STROBE_DLL_CTRL_RESET, ®s->strobe_dllctrl); + + /* + * enable strobe dll ctrl and adjust the delay target + * for the uSDHC loopback read clock + */ + val = ESDHC_STROBE_DLL_CTRL_ENABLE | + (priv->strobe_dll_delay_target << + ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); + writel(val, ®s->strobe_dllctrl); + /* wait 1us to make sure strobe dll status register stable */ + mdelay(1); + val = readl(®s->strobe_dllstat); + if (!(val & ESDHC_STROBE_DLL_STS_REF_LOCK)) + pr_warn("HS400 strobe DLL status REF not lock!\n"); + if (!(val & ESDHC_STROBE_DLL_STS_SLV_LOCK)) + pr_warn("HS400 strobe DLL status SLV not lock!\n"); + } +} + +static int esdhc_set_timing(struct mmc *mmc) +{ + struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev); + struct fsl_esdhc *regs = priv->esdhc_regs; + u32 mixctrl; + + mixctrl = readl(®s->mixctrl); + mixctrl &= ~(MIX_CTRL_DDREN | MIX_CTRL_HS400_EN); + + switch (mmc->selected_mode) { + case MMC_LEGACY: + case SD_LEGACY: + esdhc_reset_tuning(mmc); + writel(mixctrl, ®s->mixctrl); + break; + case MMC_HS_400: + mixctrl |= MIX_CTRL_DDREN | MIX_CTRL_HS400_EN; + writel(mixctrl, ®s->mixctrl); + esdhc_set_strobe_dll(mmc); + break; + case MMC_HS: + case MMC_HS_52: + case MMC_HS_200: + case SD_HS: + case UHS_SDR12: + case UHS_SDR25: + case UHS_SDR50: + case UHS_SDR104: + writel(mixctrl, ®s->mixctrl); + break; + case UHS_DDR50: + case MMC_DDR_52: + mixctrl |= MIX_CTRL_DDREN; + writel(mixctrl, ®s->mixctrl); + break; + default: + printf("Not supported %d\n", mmc->selected_mode); + return -EINVAL; + } + + priv->mode = mmc->selected_mode; + + return esdhc_change_pinstate(mmc->dev); +} + +static int esdhc_set_voltage(struct mmc *mmc) +{ + struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev); + struct fsl_esdhc *regs = priv->esdhc_regs; + int ret; + + priv->signal_voltage = mmc->signal_voltage; + switch (mmc->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + if (priv->vs18_enable) + return -EIO; +#if CONFIG_IS_ENABLED(DM_REGULATOR) + if (!IS_ERR_OR_NULL(priv->vqmmc_dev)) { + ret = regulator_set_value(priv->vqmmc_dev, 3300000); + if (ret) { + printf("Setting to 3.3V error"); + return -EIO; + } + /* Wait for 5ms */ + mdelay(5); + } +#endif + + esdhc_clrbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); + if (!(esdhc_read32(®s->vendorspec) & + ESDHC_VENDORSPEC_VSELECT)) + return 0; + + return -EAGAIN; + case MMC_SIGNAL_VOLTAGE_180: +#if CONFIG_IS_ENABLED(DM_REGULATOR) + if (!IS_ERR_OR_NULL(priv->vqmmc_dev)) { + ret = regulator_set_value(priv->vqmmc_dev, 1800000); + if (ret) { + printf("Setting to 1.8V error"); + return -EIO; + } + } +#endif + esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); + if (esdhc_read32(®s->vendorspec) & ESDHC_VENDORSPEC_VSELECT) + return 0; + + return -EAGAIN; + case MMC_SIGNAL_VOLTAGE_120: + return -ENOTSUPP; + default: + return 0; + } +} + +static void esdhc_stop_tuning(struct mmc *mmc) +{ + struct mmc_cmd cmd; + + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1b; + + dm_mmc_send_cmd(mmc->dev, &cmd, NULL); +} + +static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode) +{ + struct fsl_esdhc_plat *plat = dev_get_platdata(dev); + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + struct fsl_esdhc *regs = priv->esdhc_regs; + struct mmc *mmc = &plat->mmc; + u32 irqstaten = readl(®s->irqstaten); + u32 irqsigen = readl(®s->irqsigen); + int i, ret = -ETIMEDOUT; + u32 val, mixctrl; + + /* clock tuning is not needed for upto 52MHz */ + if (mmc->clock <= 52000000) + return 0; + + /* This is readw/writew SDHCI_HOST_CONTROL2 when tuning */ + if (priv->flags & ESDHC_FLAG_STD_TUNING) { + val = readl(®s->autoc12err); + mixctrl = readl(®s->mixctrl); + val &= ~MIX_CTRL_SMPCLK_SEL; + mixctrl &= ~(MIX_CTRL_FBCLK_SEL | MIX_CTRL_AUTO_TUNE_EN); + + val |= MIX_CTRL_EXE_TUNE; + mixctrl |= MIX_CTRL_FBCLK_SEL | MIX_CTRL_AUTO_TUNE_EN; + + writel(val, ®s->autoc12err); + writel(mixctrl, ®s->mixctrl); + } + + /* sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE); */ + mixctrl = readl(®s->mixctrl); + mixctrl = MIX_CTRL_DTDSEL_READ | (mixctrl & ~MIX_CTRL_SDHCI_MASK); + writel(mixctrl, ®s->mixctrl); + + writel(IRQSTATEN_BRR, ®s->irqstaten); + writel(IRQSTATEN_BRR, ®s->irqsigen); + + /* + * Issue opcode repeatedly till Execute Tuning is set to 0 or the number + * of loops reaches 40 times. + */ + for (i = 0; i < MAX_TUNING_LOOP; i++) { + u32 ctrl; + + if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200) { + if (mmc->bus_width == 8) + writel(0x7080, ®s->blkattr); + else if (mmc->bus_width == 4) + writel(0x7040, ®s->blkattr); + } else { + writel(0x7040, ®s->blkattr); + } + + /* sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE) */ + val = readl(®s->mixctrl); + val = MIX_CTRL_DTDSEL_READ | (val & ~MIX_CTRL_SDHCI_MASK); + writel(val, ®s->mixctrl); + + /* We are using STD tuning, no need to check return value */ + mmc_send_tuning(mmc, opcode, NULL); + + ctrl = readl(®s->autoc12err); + if ((!(ctrl & MIX_CTRL_EXE_TUNE)) && + (ctrl & MIX_CTRL_SMPCLK_SEL)) { + /* + * need to wait some time, make sure sd/mmc fininsh + * send out tuning data, otherwise, the sd/mmc can't + * response to any command when the card still out + * put the tuning data. + */ + mdelay(1); + ret = 0; + break; + } + + /* Add 1ms delay for SD and eMMC */ + mdelay(1); + } + + writel(irqstaten, ®s->irqstaten); + writel(irqsigen, ®s->irqsigen); + + esdhc_stop_tuning(mmc); + + return ret; +} +#endif + +static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) +{ + struct fsl_esdhc *regs = priv->esdhc_regs; + int ret __maybe_unused; + +#ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK + /* Select to use peripheral clock */ + esdhc_clock_control(priv, false); + esdhc_setbits32(®s->scr, ESDHCCTL_PCS); + esdhc_clock_control(priv, true); +#endif + /* Set the clock speed */ + if (priv->clock != mmc->clock) + set_sysctl(priv, mmc, mmc->clock); + +#ifdef MMC_SUPPORTS_TUNING + if (mmc->clk_disable) { +#ifdef CONFIG_FSL_USDHC + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_CKEN); +#else + esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN); +#endif + } else { +#ifdef CONFIG_FSL_USDHC + esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN | + VENDORSPEC_CKEN); +#else + esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_CKEN); +#endif + } + + if (priv->mode != mmc->selected_mode) { + ret = esdhc_set_timing(mmc); + if (ret) { + printf("esdhc_set_timing error %d\n", ret); + return ret; + } + } + + if (priv->signal_voltage != mmc->signal_voltage) { + ret = esdhc_set_voltage(mmc); + if (ret) { + printf("esdhc_set_voltage error %d\n", ret); + return ret; + } + } +#endif + + /* Set the bus width */ + esdhc_clrbits32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); + + if (mmc->bus_width == 4) + esdhc_setbits32(®s->proctl, PROCTL_DTW_4); + else if (mmc->bus_width == 8) + esdhc_setbits32(®s->proctl, PROCTL_DTW_8); + + return 0; +} + +static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) +{ + struct fsl_esdhc *regs = priv->esdhc_regs; + ulong start; + + /* Reset the entire host controller */ + esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); + + /* Wait until the controller is available */ + start = get_timer(0); + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA)) { + if (get_timer(start) > 1000) + return -ETIMEDOUT; + } + +#if defined(CONFIG_FSL_USDHC) + /* RSTA doesn't reset MMC_BOOT register, so manually reset it */ + esdhc_write32(®s->mmcboot, 0x0); + /* Reset MIX_CTRL and CLK_TUNE_CTRL_STATUS regs to 0 */ + esdhc_write32(®s->mixctrl, 0x0); + esdhc_write32(®s->clktunectrlstatus, 0x0); + + /* Put VEND_SPEC to default value */ + if (priv->vs18_enable) + esdhc_write32(®s->vendorspec, (VENDORSPEC_INIT | + ESDHC_VENDORSPEC_VSELECT)); + else + esdhc_write32(®s->vendorspec, VENDORSPEC_INIT); + + /* Disable DLL_CTRL delay line */ + esdhc_write32(®s->dllctrl, 0x0); +#endif + +#ifndef ARCH_MXC + /* Enable cache snooping */ + esdhc_write32(®s->scr, 0x00000040); +#endif + +#ifndef CONFIG_FSL_USDHC + esdhc_setbits32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); +#else + esdhc_setbits32(®s->vendorspec, VENDORSPEC_HCKEN | VENDORSPEC_IPGEN); +#endif + + /* Set the initial clock speed */ + mmc_set_clock(mmc, 400000, MMC_CLK_ENABLE); + + /* Disable the BRR and BWR bits in IRQSTAT */ + esdhc_clrbits32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); + +#ifdef CONFIG_MCF5441x + esdhc_write32(®s->proctl, PROCTL_INIT | PROCTL_D3CD); +#else + /* Put the PROCTL reg back to the default */ + esdhc_write32(®s->proctl, PROCTL_INIT); +#endif + + /* Set timout to the maximum value */ + esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16); + + return 0; +} + +static int esdhc_getcd_common(struct fsl_esdhc_priv *priv) +{ + struct fsl_esdhc *regs = priv->esdhc_regs; + int timeout = 1000; + +#ifdef CONFIG_ESDHC_DETECT_QUIRK + if (CONFIG_ESDHC_DETECT_QUIRK) + return 1; +#endif + +#if CONFIG_IS_ENABLED(DM_MMC) + if (priv->non_removable) + return 1; +#ifdef CONFIG_DM_GPIO + if (dm_gpio_is_valid(&priv->cd_gpio)) + return dm_gpio_get_value(&priv->cd_gpio); +#endif +#endif + + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_CINS) && --timeout) + udelay(1000); + + return timeout > 0; +} + +static int esdhc_reset(struct fsl_esdhc *regs) +{ + ulong start; + + /* reset the controller */ + esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); + + /* hardware clears the bit when it is done */ + start = get_timer(0); + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA)) { + if (get_timer(start) > 100) { + printf("MMC/SD: Reset never completed.\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +#if !CONFIG_IS_ENABLED(DM_MMC) +static int esdhc_getcd(struct mmc *mmc) +{ + struct fsl_esdhc_priv *priv = mmc->priv; + + return esdhc_getcd_common(priv); +} + +static int esdhc_init(struct mmc *mmc) +{ + struct fsl_esdhc_priv *priv = mmc->priv; + + return esdhc_init_common(priv, mmc); +} + +static int esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct fsl_esdhc_priv *priv = mmc->priv; + + return esdhc_send_cmd_common(priv, mmc, cmd, data); +} + +static int esdhc_set_ios(struct mmc *mmc) +{ + struct fsl_esdhc_priv *priv = mmc->priv; + + return esdhc_set_ios_common(priv, mmc); +} + +static const struct mmc_ops esdhc_ops = { + .getcd = esdhc_getcd, + .init = esdhc_init, + .send_cmd = esdhc_send_cmd, + .set_ios = esdhc_set_ios, +}; +#endif + +static int fsl_esdhc_init(struct fsl_esdhc_priv *priv, + struct fsl_esdhc_plat *plat) +{ + struct mmc_config *cfg; + struct fsl_esdhc *regs; + u32 caps, voltage_caps; + int ret; + + if (!priv) + return -EINVAL; + + regs = priv->esdhc_regs; + + /* First reset the eSDHC controller */ + ret = esdhc_reset(regs); + if (ret) + return ret; + +#ifdef CONFIG_MCF5441x + /* ColdFire, using SDHC_DATA[3] for card detection */ + esdhc_write32(®s->proctl, PROCTL_INIT | PROCTL_D3CD); +#endif + +#ifndef CONFIG_FSL_USDHC + esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN + | SYSCTL_IPGEN | SYSCTL_CKEN); + /* Clearing tuning bits in case ROM has set it already */ + esdhc_write32(®s->mixctrl, 0); + esdhc_write32(®s->autoc12err, 0); + esdhc_write32(®s->clktunectrlstatus, 0); +#else + esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN | + VENDORSPEC_HCKEN | VENDORSPEC_IPGEN | VENDORSPEC_CKEN); +#endif + + if (priv->vs18_enable) + esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); + + writel(SDHCI_IRQ_EN_BITS, ®s->irqstaten); + cfg = &plat->cfg; +#ifndef CONFIG_DM_MMC + memset(cfg, '\0', sizeof(*cfg)); +#endif + + voltage_caps = 0; + caps = esdhc_read32(®s->hostcapblt); + +#ifdef CONFIG_MCF5441x + /* + * MCF5441x RM declares in more points that sdhc clock speed must + * never exceed 25 Mhz. From this, the HS bit needs to be disabled + * from host capabilities. + */ + caps &= ~ESDHC_HOSTCAPBLT_HSS; +#endif + +#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC135 + caps = caps & ~(ESDHC_HOSTCAPBLT_SRS | + ESDHC_HOSTCAPBLT_VS18 | ESDHC_HOSTCAPBLT_VS30); +#endif + +/* T4240 host controller capabilities register should have VS33 bit */ +#ifdef CONFIG_SYS_FSL_MMC_HAS_CAPBLT_VS33 + caps = caps | ESDHC_HOSTCAPBLT_VS33; +#endif + + if (caps & ESDHC_HOSTCAPBLT_VS18) + voltage_caps |= MMC_VDD_165_195; + if (caps & ESDHC_HOSTCAPBLT_VS30) + voltage_caps |= MMC_VDD_29_30 | MMC_VDD_30_31; + if (caps & ESDHC_HOSTCAPBLT_VS33) + voltage_caps |= MMC_VDD_32_33 | MMC_VDD_33_34; + + cfg->name = "FSL_SDHC"; +#if !CONFIG_IS_ENABLED(DM_MMC) + cfg->ops = &esdhc_ops; +#endif +#ifdef CONFIG_SYS_SD_VOLTAGE + cfg->voltages = CONFIG_SYS_SD_VOLTAGE; +#else + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; +#endif + if ((cfg->voltages & voltage_caps) == 0) { + printf("voltage not supported by controller\n"); + return -1; + } + + if (priv->bus_width == 8) + cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; + else if (priv->bus_width == 4) + cfg->host_caps = MMC_MODE_4BIT; + + cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; +#ifdef CONFIG_SYS_FSL_ESDHC_HAS_DDR_MODE + cfg->host_caps |= MMC_MODE_DDR_52MHz; +#endif + + if (priv->bus_width > 0) { + if (priv->bus_width < 8) + cfg->host_caps &= ~MMC_MODE_8BIT; + if (priv->bus_width < 4) + cfg->host_caps &= ~MMC_MODE_4BIT; + } + + if (caps & ESDHC_HOSTCAPBLT_HSS) + cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + +#ifdef CONFIG_ESDHC_DETECT_8_BIT_QUIRK + if (CONFIG_ESDHC_DETECT_8_BIT_QUIRK) + cfg->host_caps &= ~MMC_MODE_8BIT; +#endif + + cfg->host_caps |= priv->caps; + + cfg->f_min = 400000; + cfg->f_max = min(priv->sdhc_clk, (u32)200000000); + + cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + + writel(0, ®s->dllctrl); + if (priv->flags & ESDHC_FLAG_USDHC) { + if (priv->flags & ESDHC_FLAG_STD_TUNING) { + u32 val = readl(®s->tuning_ctrl); + + val |= ESDHC_STD_TUNING_EN; + val &= ~ESDHC_TUNING_START_TAP_MASK; + val |= priv->tuning_start_tap; + val &= ~ESDHC_TUNING_STEP_MASK; + val |= (priv->tuning_step) << ESDHC_TUNING_STEP_SHIFT; + writel(val, ®s->tuning_ctrl); + } + } + + return 0; +} + +#if !CONFIG_IS_ENABLED(DM_MMC) +static int fsl_esdhc_cfg_to_priv(struct fsl_esdhc_cfg *cfg, + struct fsl_esdhc_priv *priv) +{ + if (!cfg || !priv) + return -EINVAL; + + priv->esdhc_regs = (struct fsl_esdhc *)(unsigned long)(cfg->esdhc_base); + priv->bus_width = cfg->max_bus_width; + priv->sdhc_clk = cfg->sdhc_clk; + priv->wp_enable = cfg->wp_enable; + priv->vs18_enable = cfg->vs18_enable; + + return 0; +}; + +int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) +{ + struct fsl_esdhc_plat *plat; + struct fsl_esdhc_priv *priv; + struct mmc *mmc; + int ret; + + if (!cfg) + return -EINVAL; + + priv = calloc(sizeof(struct fsl_esdhc_priv), 1); + if (!priv) + return -ENOMEM; + plat = calloc(sizeof(struct fsl_esdhc_plat), 1); + if (!plat) { + free(priv); + return -ENOMEM; + } + + ret = fsl_esdhc_cfg_to_priv(cfg, priv); + if (ret) { + debug("%s xlate failure\n", __func__); + free(plat); + free(priv); + return ret; + } + + ret = fsl_esdhc_init(priv, plat); + if (ret) { + debug("%s init failure\n", __func__); + free(plat); + free(priv); + return ret; + } + + mmc = mmc_create(&plat->cfg, priv); + if (!mmc) + return -EIO; + + priv->mmc = mmc; + + return 0; +} + +int fsl_esdhc_mmc_init(bd_t *bis) +{ + struct fsl_esdhc_cfg *cfg; + + cfg = calloc(sizeof(struct fsl_esdhc_cfg), 1); + cfg->esdhc_base = CONFIG_SYS_FSL_ESDHC_ADDR; + cfg->sdhc_clk = gd->arch.sdhc_clk; + return fsl_esdhc_initialize(bis, cfg); +} +#endif + +#ifdef CONFIG_OF_LIBFDT +__weak int esdhc_status_fixup(void *blob, const char *compat) +{ +#ifdef CONFIG_FSL_ESDHC_PIN_MUX + if (!hwconfig("esdhc")) { + do_fixup_by_compat(blob, compat, "status", "disabled", + sizeof("disabled"), 1); + return 1; + } +#endif + return 0; +} + +void fdt_fixup_esdhc(void *blob, bd_t *bd) +{ + const char *compat = "fsl,esdhc"; + + if (esdhc_status_fixup(blob, compat)) + return; + +#ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK + do_fixup_by_compat_u32(blob, compat, "peripheral-frequency", + gd->arch.sdhc_clk, 1); +#else + do_fixup_by_compat_u32(blob, compat, "clock-frequency", + gd->arch.sdhc_clk, 1); +#endif +} +#endif + +#if CONFIG_IS_ENABLED(DM_MMC) +#include <asm/arch/clock.h> +__weak void init_clk_usdhc(u32 index) +{ +} + +static int fsl_esdhc_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct fsl_esdhc_plat *plat = dev_get_platdata(dev); + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + const void *fdt = gd->fdt_blob; + int node = dev_of_offset(dev); + struct esdhc_soc_data *data = + (struct esdhc_soc_data *)dev_get_driver_data(dev); +#if CONFIG_IS_ENABLED(DM_REGULATOR) + struct udevice *vqmmc_dev; +#endif + fdt_addr_t addr; + unsigned int val; + struct mmc *mmc; +#if !CONFIG_IS_ENABLED(BLK) + struct blk_desc *bdesc; +#endif + int ret; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + priv->esdhc_regs = (struct fsl_esdhc *)addr; + priv->dev = dev; + priv->mode = -1; + if (data) { + priv->flags = data->flags; + priv->caps = data->caps; + } + + val = dev_read_u32_default(dev, "bus-width", -1); + if (val == 8) + priv->bus_width = 8; + else if (val == 4) + priv->bus_width = 4; + else + priv->bus_width = 1; + + val = fdtdec_get_int(fdt, node, "fsl,tuning-step", 1); + priv->tuning_step = val; + val = fdtdec_get_int(fdt, node, "fsl,tuning-start-tap", + ESDHC_TUNING_START_TAP_DEFAULT); + priv->tuning_start_tap = val; + val = fdtdec_get_int(fdt, node, "fsl,strobe-dll-delay-target", + ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT); + priv->strobe_dll_delay_target = val; + + if (dev_read_bool(dev, "non-removable")) { + priv->non_removable = 1; + } else { + priv->non_removable = 0; +#ifdef CONFIG_DM_GPIO + gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, + GPIOD_IS_IN); +#endif + } + + if (dev_read_prop(dev, "fsl,wp-controller", NULL)) { + priv->wp_enable = 1; + } else { + priv->wp_enable = 0; +#ifdef CONFIG_DM_GPIO + gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, + GPIOD_IS_IN); +#endif + } + + priv->vs18_enable = 0; + +#if CONFIG_IS_ENABLED(DM_REGULATOR) + /* + * If emmc I/O has a fixed voltage at 1.8V, this must be provided, + * otherwise, emmc will work abnormally. + */ + ret = device_get_supply_regulator(dev, "vqmmc-supply", &vqmmc_dev); + if (ret) { + dev_dbg(dev, "no vqmmc-supply\n"); + } else { + ret = regulator_set_enable(vqmmc_dev, true); + if (ret) { + dev_err(dev, "fail to enable vqmmc-supply\n"); + return ret; + } + + if (regulator_get_value(vqmmc_dev) == 1800000) + priv->vs18_enable = 1; + } +#endif + + if (fdt_get_property(fdt, node, "no-1-8-v", NULL)) + priv->caps &= ~(UHS_CAPS | MMC_MODE_HS200 | MMC_MODE_HS400); + + /* + * TODO: + * Because lack of clk driver, if SDHC clk is not enabled, + * need to enable it first before this driver is invoked. + * + * we use MXC_ESDHC_CLK to get clk freq. + * If one would like to make this function work, + * the aliases should be provided in dts as this: + * + * aliases { + * mmc0 = &usdhc1; + * mmc1 = &usdhc2; + * mmc2 = &usdhc3; + * mmc3 = &usdhc4; + * }; + * Then if your board only supports mmc2 and mmc3, but we can + * correctly get the seq as 2 and 3, then let mxc_get_clock + * work as expected. + */ + + init_clk_usdhc(dev->seq); + + if (IS_ENABLED(CONFIG_CLK)) { + /* Assigned clock already set clock */ + ret = clk_get_by_name(dev, "per", &priv->per_clk); + if (ret) { + printf("Failed to get per_clk\n"); + return ret; + } + ret = clk_enable(&priv->per_clk); + if (ret) { + printf("Failed to enable per_clk\n"); + return ret; + } + + priv->sdhc_clk = clk_get_rate(&priv->per_clk); + } else { + priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq); + if (priv->sdhc_clk <= 0) { + dev_err(dev, "Unable to get clk for %s\n", dev->name); + return -EINVAL; + } + } + + ret = fsl_esdhc_init(priv, plat); + if (ret) { + dev_err(dev, "fsl_esdhc_init failure\n"); + return ret; + } + + mmc = &plat->mmc; + mmc->cfg = &plat->cfg; + mmc->dev = dev; +#if !CONFIG_IS_ENABLED(BLK) + mmc->priv = priv; + + /* Setup dsr related values */ + mmc->dsr_imp = 0; + mmc->dsr = ESDHC_DRIVER_STAGE_VALUE; + /* Setup the universal parts of the block interface just once */ + bdesc = mmc_get_blk_desc(mmc); + bdesc->if_type = IF_TYPE_MMC; + bdesc->removable = 1; + bdesc->devnum = mmc_get_next_devnum(); + bdesc->block_read = mmc_bread; + bdesc->block_write = mmc_bwrite; + bdesc->block_erase = mmc_berase; + + /* setup initial part type */ + bdesc->part_type = mmc->cfg->part_type; + mmc_list_add(mmc); +#endif + + upriv->mmc = mmc; + + return esdhc_init_common(priv, mmc); +} + +#if CONFIG_IS_ENABLED(DM_MMC) +static int fsl_esdhc_get_cd(struct udevice *dev) +{ + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + + return esdhc_getcd_common(priv); +} + +static int fsl_esdhc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct fsl_esdhc_plat *plat = dev_get_platdata(dev); + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + + return esdhc_send_cmd_common(priv, &plat->mmc, cmd, data); +} + +static int fsl_esdhc_set_ios(struct udevice *dev) +{ + struct fsl_esdhc_plat *plat = dev_get_platdata(dev); + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + + return esdhc_set_ios_common(priv, &plat->mmc); +} + +static const struct dm_mmc_ops fsl_esdhc_ops = { + .get_cd = fsl_esdhc_get_cd, + .send_cmd = fsl_esdhc_send_cmd, + .set_ios = fsl_esdhc_set_ios, +#ifdef MMC_SUPPORTS_TUNING + .execute_tuning = fsl_esdhc_execute_tuning, +#endif +}; +#endif + +static struct esdhc_soc_data usdhc_imx7d_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400, + .caps = UHS_CAPS | MMC_MODE_HS200 | MMC_MODE_DDR_52MHz | + MMC_MODE_HS_52MHz | MMC_MODE_HS, +}; + +static const struct udevice_id fsl_esdhc_ids[] = { + { .compatible = "fsl,imx53-esdhc", }, + { .compatible = "fsl,imx6ul-usdhc", }, + { .compatible = "fsl,imx6sx-usdhc", }, + { .compatible = "fsl,imx6sl-usdhc", }, + { .compatible = "fsl,imx6q-usdhc", }, + { .compatible = "fsl,imx7d-usdhc", .data = (ulong)&usdhc_imx7d_data,}, + { .compatible = "fsl,imx7ulp-usdhc", }, + { .compatible = "fsl,esdhc", }, + { /* sentinel */ } +}; + +#if CONFIG_IS_ENABLED(BLK) +static int fsl_esdhc_bind(struct udevice *dev) +{ + struct fsl_esdhc_plat *plat = dev_get_platdata(dev); + + return mmc_bind(dev, &plat->mmc, &plat->cfg); +} +#endif + +U_BOOT_DRIVER(fsl_esdhc) = { + .name = "fsl-esdhc-mmc", + .id = UCLASS_MMC, + .of_match = fsl_esdhc_ids, + .ops = &fsl_esdhc_ops, +#if CONFIG_IS_ENABLED(BLK) + .bind = fsl_esdhc_bind, +#endif + .probe = fsl_esdhc_probe, + .platdata_auto_alloc_size = sizeof(struct fsl_esdhc_plat), + .priv_auto_alloc_size = sizeof(struct fsl_esdhc_priv), +}; +#endif diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index 2b17eae947..a78fd51ba7 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -1,6 +1,6 @@ menu "UBI support" -config CONFIG_UBI_SILENCE_MSG +config UBI_SILENCE_MSG bool "UBI silence verbose messages" default ENV_IS_IN_UBI help diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 8ef7823b37..688fb509d2 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -1351,6 +1351,7 @@ static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum, ubi_err(ubi, "self-check failed for PEB %d:%d, len %d", pnum, offset, len); +#if !defined(CONFIG_UBI_SILENCE_MSG) ubi_msg(ubi, "data differ at position %d", i); ubi_msg(ubi, "hex dump of the original buffer from %d to %d", i, i + dump_len); @@ -1360,6 +1361,7 @@ static int self_check_write(struct ubi_device *ubi, const void *buf, int pnum, i, i + dump_len); print_hex_dump("", DUMP_PREFIX_OFFSET, 32, 1, buf1 + i, dump_len, 1); +#endif dump_stack(); err = -EINVAL; goto out_free; diff --git a/drivers/mtd/ubispl/ubispl.c b/drivers/mtd/ubispl/ubispl.c index eeb1cbefb7..3f3b9b4367 100644 --- a/drivers/mtd/ubispl/ubispl.c +++ b/drivers/mtd/ubispl/ubispl.c @@ -45,6 +45,187 @@ static int ubi_io_is_bad(struct ubi_scan_info *ubi, int peb) return peb >= ubi->peb_count || peb < 0; } +#ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME + +/** + * ubi_dump_vtbl_record - dump a &struct ubi_vtbl_record object. + * @r: the object to dump + * @idx: volume table index + */ +void ubi_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx) +{ + int name_len = be16_to_cpu(r->name_len); + + ubi_dbg("Volume table record %d dump: size: %d", + idx, sizeof(struct ubi_vtbl_record)); + ubi_dbg("\treserved_pebs %d", be32_to_cpu(r->reserved_pebs)); + ubi_dbg("\talignment %d", be32_to_cpu(r->alignment)); + ubi_dbg("\tdata_pad %d", be32_to_cpu(r->data_pad)); + ubi_dbg("\tvol_type %d", (int)r->vol_type); + ubi_dbg("\tupd_marker %d", (int)r->upd_marker); + ubi_dbg("\tname_len %d", name_len); + + if (r->name[0] == '\0') { + ubi_dbg("\tname NULL"); + return; + } + + if (name_len <= UBI_VOL_NAME_MAX && + strnlen(&r->name[0], name_len + 1) == name_len) { + ubi_dbg("\tname %s", &r->name[0]); + } else { + ubi_dbg("\t1st 5 characters of name: %c%c%c%c%c", + r->name[0], r->name[1], r->name[2], r->name[3], + r->name[4]); + } + ubi_dbg("\tcrc %#08x", be32_to_cpu(r->crc)); +} + +/* Empty volume table record */ +static struct ubi_vtbl_record empty_vtbl_record; + +/** + * vtbl_check - check if volume table is not corrupted and sensible. + * @ubi: UBI device description object + * @vtbl: volume table + * + * This function returns zero if @vtbl is all right, %1 if CRC is incorrect, + * and %-EINVAL if it contains inconsistent data. + */ +static int vtbl_check(struct ubi_scan_info *ubi, + struct ubi_vtbl_record *vtbl) +{ + int i, n, reserved_pebs, alignment, data_pad, vol_type, name_len; + int upd_marker, err; + uint32_t crc; + const char *name; + + for (i = 0; i < UBI_SPL_VOL_IDS; i++) { + reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs); + alignment = be32_to_cpu(vtbl[i].alignment); + data_pad = be32_to_cpu(vtbl[i].data_pad); + upd_marker = vtbl[i].upd_marker; + vol_type = vtbl[i].vol_type; + name_len = be16_to_cpu(vtbl[i].name_len); + name = &vtbl[i].name[0]; + + crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC); + if (be32_to_cpu(vtbl[i].crc) != crc) { + ubi_err("bad CRC at record %u: %#08x, not %#08x", + i, crc, be32_to_cpu(vtbl[i].crc)); + ubi_dump_vtbl_record(&vtbl[i], i); + return 1; + } + + if (reserved_pebs == 0) { + if (memcmp(&vtbl[i], &empty_vtbl_record, + UBI_VTBL_RECORD_SIZE)) { + err = 2; + goto bad; + } + continue; + } + + if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 || + name_len < 0) { + err = 3; + goto bad; + } + + if (alignment > ubi->leb_size || alignment == 0) { + err = 4; + goto bad; + } + + n = alignment & (CONFIG_SPL_UBI_VID_OFFSET - 1); + if (alignment != 1 && n) { + err = 5; + goto bad; + } + + n = ubi->leb_size % alignment; + if (data_pad != n) { + ubi_err("bad data_pad, has to be %d", n); + err = 6; + goto bad; + } + + if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { + err = 7; + goto bad; + } + + if (upd_marker != 0 && upd_marker != 1) { + err = 8; + goto bad; + } + + if (name_len > UBI_VOL_NAME_MAX) { + err = 10; + goto bad; + } + + if (name[0] == '\0') { + err = 11; + goto bad; + } + + if (name_len != strnlen(name, name_len + 1)) { + err = 12; + goto bad; + } + + ubi_dump_vtbl_record(&vtbl[i], i); + } + + /* Checks that all names are unique */ + for (i = 0; i < UBI_SPL_VOL_IDS - 1; i++) { + for (n = i + 1; n < UBI_SPL_VOL_IDS; n++) { + int len1 = be16_to_cpu(vtbl[i].name_len); + int len2 = be16_to_cpu(vtbl[n].name_len); + + if (len1 > 0 && len1 == len2 && + !strncmp(vtbl[i].name, vtbl[n].name, len1)) { + ubi_err("volumes %d and %d have the same name \"%s\"", + i, n, vtbl[i].name); + ubi_dump_vtbl_record(&vtbl[i], i); + ubi_dump_vtbl_record(&vtbl[n], n); + return -EINVAL; + } + } + } + + return 0; + +bad: + ubi_err("volume table check failed: record %d, error %d", i, err); + ubi_dump_vtbl_record(&vtbl[i], i); + return -EINVAL; +} + +static int ubi_read_volume_table(struct ubi_scan_info *ubi, u32 pnum) +{ + int err = -EINVAL; + + empty_vtbl_record.crc = cpu_to_be32(0xf116c36b); + + err = ubi_io_read(ubi, &ubi->vtbl, pnum, ubi->leb_start, + sizeof(struct ubi_vtbl_record) * UBI_SPL_VOL_IDS); + if (err && err != UBI_IO_BITFLIPS) { + ubi_err("unable to read volume table"); + goto out; + } + + if (!vtbl_check(ubi, ubi->vtbl)) { + ubi->vtbl_valid = 1; + err = 0; + } +out: + return err; +} + +#endif /* CONFIG_SPL_UBI_LOAD_BY_VOLNAME */ + static int ubi_io_read_vid_hdr(struct ubi_scan_info *ubi, int pnum, struct ubi_vid_hdr *vh, int unused) { @@ -210,14 +391,23 @@ static int ubi_scan_vid_hdr(struct ubi_scan_info *ubi, struct ubi_vid_hdr *vh, if (vol_id == UBI_FM_SB_VOLUME_ID) return ubi->fm_enabled ? UBI_FASTMAP_ANCHOR : 0; +#ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME + /* If this is a UBI volume table, read it and return */ + if (vol_id == UBI_LAYOUT_VOLUME_ID && !ubi->vtbl_valid) { + res = ubi_read_volume_table(ubi, pnum); + return res; + } +#endif + /* We only care about static volumes with an id < UBI_SPL_VOL_IDS */ if (vol_id >= UBI_SPL_VOL_IDS || vh->vol_type != UBI_VID_STATIC) return 0; +#ifndef CONFIG_SPL_UBI_LOAD_BY_VOLNAME /* We are only interested in the volumes to load */ if (!test_bit(vol_id, ubi->toload)) return 0; - +#endif lnum = be32_to_cpu(vh->lnum); return ubi_add_peb_to_vol(ubi, vh, vol_id, pnum, lnum); } @@ -232,13 +422,14 @@ static int assign_aeb_to_av(struct ubi_scan_info *ubi, u32 pnum, u32 lnum, ubi->fastmap_pebs++; +#ifndef CONFIG_SPL_UBI_LOAD_BY_VOLNAME if (vol_id >= UBI_SPL_VOL_IDS || vol_type != UBI_STATIC_VOLUME) return 0; /* We are only interested in the volumes to load */ if (!test_bit(vol_id, ubi->toload)) return 0; - +#endif vh = ubi->blockinfo + pnum; return ubi_scan_vid_hdr(ubi, vh, pnum); @@ -892,6 +1083,10 @@ retry: ubi->peb_count = info->peb_count; ubi->peb_offset = info->peb_offset; +#ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME + ubi->vtbl_valid = 0; +#endif + fsize = info->peb_size * info->peb_count; ubi->fsize_mb = fsize >> 20; @@ -910,7 +1105,23 @@ retry: for (i = 0; i < nrvols; i++) { struct ubispl_load *lv = lvols + i; +#ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME + if (lv->vol_id == -1) { + for (int j = 0; j < UBI_SPL_VOL_IDS; j++) { + int len = be16_to_cpu(ubi->vtbl[j].name_len); + + if (strncmp(lv->name, + ubi->vtbl[j].name, + len) == 0) { + lv->vol_id = j; + break; + } + } + } + ubi_msg("Loading VolName %s (VolId #%d)", lv->name, lv->vol_id); +#else ubi_msg("Loading VolId #%d", lv->vol_id); +#endif res = ipl_load(ubi, lv->vol_id, lv->load_addr); if (res < 0) { if (fastmap) { diff --git a/drivers/mtd/ubispl/ubispl.h b/drivers/mtd/ubispl/ubispl.h index 9e40b46eac..b7cb7fc941 100644 --- a/drivers/mtd/ubispl/ubispl.h +++ b/drivers/mtd/ubispl/ubispl.h @@ -77,6 +77,8 @@ struct ubi_vol_info { * @blockinfo: The vid headers of the scanned blocks * @volinfo: The volume information of the interesting (toload) * volumes + * @vtbl_corrupted: Flag to indicate status of volume table + * @vtbl: Volume table * * @fm_buf: The large fastmap attach buffer */ @@ -112,6 +114,11 @@ struct ubi_scan_info { struct ubi_vol_info volinfo[UBI_SPL_VOL_IDS]; struct ubi_vid_hdr blockinfo[CONFIG_SPL_UBI_MAX_PEBS]; +#ifdef CONFIG_SPL_UBI_LOAD_BY_VOLNAME + /* Volume table */ + int vtbl_valid; + struct ubi_vtbl_record vtbl[UBI_SPL_VOL_IDS]; +#endif /* The large buffer for the fastmap */ uint8_t fm_buf[UBI_FM_BUF_SIZE]; }; @@ -122,7 +129,7 @@ struct ubi_scan_info { #define ubi_dbg(fmt, ...) #endif -#ifdef CONFIG_UBI_SILENCE_MSG +#ifdef CONFIG_UBI_SPL_SILENCE_MSG #define ubi_msg(fmt, ...) #else #define ubi_msg(fmt, ...) printf("UBI: " fmt "\n", ##__VA_ARGS__) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 9469147152..f9b282313a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -49,6 +49,14 @@ config ATH79_SPI uses driver model and requires a device tree binding to operate. please refer to doc/device-tree-bindings/spi/spi-ath79.txt. +config ATMEL_QSPI + bool "Atmel Quad SPI Controller" + depends on ARCH_AT91 + help + Enable the Atmel Quad SPI controller in master mode. This driver + does not support generic SPI. The implementation supports only the + spi-mem interface. + config ATMEL_SPI bool "Atmel SPI driver" default y if ARCH_AT91 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3f9f2fab2b..64c407e2ed 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -18,6 +18,7 @@ endif obj-$(CONFIG_ALTERA_SPI) += altera_spi.o obj-$(CONFIG_ATH79_SPI) += ath79_spi.o +obj-$(CONFIG_ATMEL_QSPI) += atmel-quadspi.o obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o obj-$(CONFIG_BCM63XX_HSSPI) += bcm63xx_hsspi.o obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c new file mode 100644 index 0000000000..7d9a54011d --- /dev/null +++ b/drivers/spi/atmel-quadspi.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Atmel QSPI Controller + * + * Copyright (C) 2015 Atmel Corporation + * Copyright (C) 2018 Cryptera A/S + * + * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com> + * Author: Piotr Bugalski <bugalski.piotr@gmail.com> + */ + +#include <asm/io.h> +#include <clk.h> +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/ioport.h> +#include <mach/clk.h> +#include <spi.h> +#include <spi-mem.h> + +/* QSPI register offsets */ +#define QSPI_CR 0x0000 /* Control Register */ +#define QSPI_MR 0x0004 /* Mode Register */ +#define QSPI_RD 0x0008 /* Receive Data Register */ +#define QSPI_TD 0x000c /* Transmit Data Register */ +#define QSPI_SR 0x0010 /* Status Register */ +#define QSPI_IER 0x0014 /* Interrupt Enable Register */ +#define QSPI_IDR 0x0018 /* Interrupt Disable Register */ +#define QSPI_IMR 0x001c /* Interrupt Mask Register */ +#define QSPI_SCR 0x0020 /* Serial Clock Register */ + +#define QSPI_IAR 0x0030 /* Instruction Address Register */ +#define QSPI_ICR 0x0034 /* Instruction Code Register */ +#define QSPI_WICR 0x0034 /* Write Instruction Code Register */ +#define QSPI_IFR 0x0038 /* Instruction Frame Register */ +#define QSPI_RICR 0x003C /* Read Instruction Code Register */ + +#define QSPI_SMR 0x0040 /* Scrambling Mode Register */ +#define QSPI_SKR 0x0044 /* Scrambling Key Register */ + +#define QSPI_WPMR 0x00E4 /* Write Protection Mode Register */ +#define QSPI_WPSR 0x00E8 /* Write Protection Status Register */ + +#define QSPI_VERSION 0x00FC /* Version Register */ + +/* Bitfields in QSPI_CR (Control Register) */ +#define QSPI_CR_QSPIEN BIT(0) +#define QSPI_CR_QSPIDIS BIT(1) +#define QSPI_CR_SWRST BIT(7) +#define QSPI_CR_LASTXFER BIT(24) + +/* Bitfields in QSPI_MR (Mode Register) */ +#define QSPI_MR_SMM BIT(0) +#define QSPI_MR_LLB BIT(1) +#define QSPI_MR_WDRBT BIT(2) +#define QSPI_MR_SMRM BIT(3) +#define QSPI_MR_CSMODE_MASK GENMASK(5, 4) +#define QSPI_MR_CSMODE_NOT_RELOADED (0 << 4) +#define QSPI_MR_CSMODE_LASTXFER (1 << 4) +#define QSPI_MR_CSMODE_SYSTEMATICALLY (2 << 4) +#define QSPI_MR_NBBITS_MASK GENMASK(11, 8) +#define QSPI_MR_NBBITS(n) ((((n) - 8) << 8) & QSPI_MR_NBBITS_MASK) +#define QSPI_MR_DLYBCT_MASK GENMASK(23, 16) +#define QSPI_MR_DLYBCT(n) (((n) << 16) & QSPI_MR_DLYBCT_MASK) +#define QSPI_MR_DLYCS_MASK GENMASK(31, 24) +#define QSPI_MR_DLYCS(n) (((n) << 24) & QSPI_MR_DLYCS_MASK) + +/* Bitfields in QSPI_SR/QSPI_IER/QSPI_IDR/QSPI_IMR */ +#define QSPI_SR_RDRF BIT(0) +#define QSPI_SR_TDRE BIT(1) +#define QSPI_SR_TXEMPTY BIT(2) +#define QSPI_SR_OVRES BIT(3) +#define QSPI_SR_CSR BIT(8) +#define QSPI_SR_CSS BIT(9) +#define QSPI_SR_INSTRE BIT(10) +#define QSPI_SR_QSPIENS BIT(24) + +#define QSPI_SR_CMD_COMPLETED (QSPI_SR_INSTRE | QSPI_SR_CSR) + +/* Bitfields in QSPI_SCR (Serial Clock Register) */ +#define QSPI_SCR_CPOL BIT(0) +#define QSPI_SCR_CPHA BIT(1) +#define QSPI_SCR_SCBR_MASK GENMASK(15, 8) +#define QSPI_SCR_SCBR(n) (((n) << 8) & QSPI_SCR_SCBR_MASK) +#define QSPI_SCR_DLYBS_MASK GENMASK(23, 16) +#define QSPI_SCR_DLYBS(n) (((n) << 16) & QSPI_SCR_DLYBS_MASK) + +/* Bitfields in QSPI_ICR (Read/Write Instruction Code Register) */ +#define QSPI_ICR_INST_MASK GENMASK(7, 0) +#define QSPI_ICR_INST(inst) (((inst) << 0) & QSPI_ICR_INST_MASK) +#define QSPI_ICR_OPT_MASK GENMASK(23, 16) +#define QSPI_ICR_OPT(opt) (((opt) << 16) & QSPI_ICR_OPT_MASK) + +/* Bitfields in QSPI_IFR (Instruction Frame Register) */ +#define QSPI_IFR_WIDTH_MASK GENMASK(2, 0) +#define QSPI_IFR_WIDTH_SINGLE_BIT_SPI (0 << 0) +#define QSPI_IFR_WIDTH_DUAL_OUTPUT (1 << 0) +#define QSPI_IFR_WIDTH_QUAD_OUTPUT (2 << 0) +#define QSPI_IFR_WIDTH_DUAL_IO (3 << 0) +#define QSPI_IFR_WIDTH_QUAD_IO (4 << 0) +#define QSPI_IFR_WIDTH_DUAL_CMD (5 << 0) +#define QSPI_IFR_WIDTH_QUAD_CMD (6 << 0) +#define QSPI_IFR_INSTEN BIT(4) +#define QSPI_IFR_ADDREN BIT(5) +#define QSPI_IFR_OPTEN BIT(6) +#define QSPI_IFR_DATAEN BIT(7) +#define QSPI_IFR_OPTL_MASK GENMASK(9, 8) +#define QSPI_IFR_OPTL_1BIT (0 << 8) +#define QSPI_IFR_OPTL_2BIT (1 << 8) +#define QSPI_IFR_OPTL_4BIT (2 << 8) +#define QSPI_IFR_OPTL_8BIT (3 << 8) +#define QSPI_IFR_ADDRL BIT(10) +#define QSPI_IFR_TFRTYP_MEM BIT(12) +#define QSPI_IFR_SAMA5D2_WRITE_TRSFR BIT(13) +#define QSPI_IFR_CRM BIT(14) +#define QSPI_IFR_NBDUM_MASK GENMASK(20, 16) +#define QSPI_IFR_NBDUM(n) (((n) << 16) & QSPI_IFR_NBDUM_MASK) +#define QSPI_IFR_APBTFRTYP_READ BIT(24) /* Defined in SAM9X60 */ + +/* Bitfields in QSPI_SMR (Scrambling Mode Register) */ +#define QSPI_SMR_SCREN BIT(0) +#define QSPI_SMR_RVDIS BIT(1) + +/* Bitfields in QSPI_WPMR (Write Protection Mode Register) */ +#define QSPI_WPMR_WPEN BIT(0) +#define QSPI_WPMR_WPKEY_MASK GENMASK(31, 8) +#define QSPI_WPMR_WPKEY(wpkey) (((wpkey) << 8) & QSPI_WPMR_WPKEY_MASK) + +/* Bitfields in QSPI_WPSR (Write Protection Status Register) */ +#define QSPI_WPSR_WPVS BIT(0) +#define QSPI_WPSR_WPVSRC_MASK GENMASK(15, 8) +#define QSPI_WPSR_WPVSRC(src) (((src) << 8) & QSPI_WPSR_WPVSRC) + +struct atmel_qspi_caps { + bool has_qspick; + bool has_ricr; +}; + +struct atmel_qspi { + void __iomem *regs; + void __iomem *mem; + const struct atmel_qspi_caps *caps; + ulong bus_clk_rate; + u32 mr; +}; + +struct atmel_qspi_mode { + u8 cmd_buswidth; + u8 addr_buswidth; + u8 data_buswidth; + u32 config; +}; + +static const struct atmel_qspi_mode atmel_qspi_modes[] = { + { 1, 1, 1, QSPI_IFR_WIDTH_SINGLE_BIT_SPI }, + { 1, 1, 2, QSPI_IFR_WIDTH_DUAL_OUTPUT }, + { 1, 1, 4, QSPI_IFR_WIDTH_QUAD_OUTPUT }, + { 1, 2, 2, QSPI_IFR_WIDTH_DUAL_IO }, + { 1, 4, 4, QSPI_IFR_WIDTH_QUAD_IO }, + { 2, 2, 2, QSPI_IFR_WIDTH_DUAL_CMD }, + { 4, 4, 4, QSPI_IFR_WIDTH_QUAD_CMD }, +}; + +static inline bool atmel_qspi_is_compatible(const struct spi_mem_op *op, + const struct atmel_qspi_mode *mode) +{ + if (op->cmd.buswidth != mode->cmd_buswidth) + return false; + + if (op->addr.nbytes && op->addr.buswidth != mode->addr_buswidth) + return false; + + if (op->data.nbytes && op->data.buswidth != mode->data_buswidth) + return false; + + return true; +} + +static int atmel_qspi_find_mode(const struct spi_mem_op *op) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(atmel_qspi_modes); i++) + if (atmel_qspi_is_compatible(op, &atmel_qspi_modes[i])) + return i; + + return -ENOTSUPP; +} + +static bool atmel_qspi_supports_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + if (atmel_qspi_find_mode(op) < 0) + return false; + + /* special case not supported by hardware */ + if (op->addr.nbytes == 2 && op->cmd.buswidth != op->addr.buswidth && + op->dummy.nbytes == 0) + return false; + + return true; +} + +static int atmel_qspi_set_cfg(struct atmel_qspi *aq, + const struct spi_mem_op *op, u32 *offset) +{ + u32 iar, icr, ifr; + u32 dummy_cycles = 0; + int mode; + + iar = 0; + icr = QSPI_ICR_INST(op->cmd.opcode); + ifr = QSPI_IFR_INSTEN; + + mode = atmel_qspi_find_mode(op); + if (mode < 0) + return mode; + ifr |= atmel_qspi_modes[mode].config; + + if (op->dummy.buswidth && op->dummy.nbytes) + dummy_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth; + + /* + * The controller allows 24 and 32-bit addressing while NAND-flash + * requires 16-bit long. Handling 8-bit long addresses is done using + * the option field. For the 16-bit addresses, the workaround depends + * of the number of requested dummy bits. If there are 8 or more dummy + * cycles, the address is shifted and sent with the first dummy byte. + * Otherwise opcode is disabled and the first byte of the address + * contains the command opcode (works only if the opcode and address + * use the same buswidth). The limitation is when the 16-bit address is + * used without enough dummy cycles and the opcode is using a different + * buswidth than the address. + */ + if (op->addr.buswidth) { + switch (op->addr.nbytes) { + case 0: + break; + case 1: + ifr |= QSPI_IFR_OPTEN | QSPI_IFR_OPTL_8BIT; + icr |= QSPI_ICR_OPT(op->addr.val & 0xff); + break; + case 2: + if (dummy_cycles < 8 / op->addr.buswidth) { + ifr &= ~QSPI_IFR_INSTEN; + ifr |= QSPI_IFR_ADDREN; + iar = (op->cmd.opcode << 16) | + (op->addr.val & 0xffff); + } else { + ifr |= QSPI_IFR_ADDREN; + iar = (op->addr.val << 8) & 0xffffff; + dummy_cycles -= 8 / op->addr.buswidth; + } + break; + case 3: + ifr |= QSPI_IFR_ADDREN; + iar = op->addr.val & 0xffffff; + break; + case 4: + ifr |= QSPI_IFR_ADDREN | QSPI_IFR_ADDRL; + iar = op->addr.val & 0x7ffffff; + break; + default: + return -ENOTSUPP; + } + } + + /* offset of the data access in the QSPI memory space */ + *offset = iar; + + /* Set number of dummy cycles */ + if (dummy_cycles) + ifr |= QSPI_IFR_NBDUM(dummy_cycles); + + /* Set data enable */ + if (op->data.nbytes) + ifr |= QSPI_IFR_DATAEN; + + /* + * If the QSPI controller is set in regular SPI mode, set it in + * Serial Memory Mode (SMM). + */ + if (aq->mr != QSPI_MR_SMM) { + writel(QSPI_MR_SMM, aq->regs + QSPI_MR); + aq->mr = QSPI_MR_SMM; + } + + /* Clear pending interrupts */ + (void)readl(aq->regs + QSPI_SR); + + if (aq->caps->has_ricr) { + if (!op->addr.nbytes && op->data.dir == SPI_MEM_DATA_IN) + ifr |= QSPI_IFR_APBTFRTYP_READ; + + /* Set QSPI Instruction Frame registers */ + writel(iar, aq->regs + QSPI_IAR); + if (op->data.dir == SPI_MEM_DATA_IN) + writel(icr, aq->regs + QSPI_RICR); + else + writel(icr, aq->regs + QSPI_WICR); + writel(ifr, aq->regs + QSPI_IFR); + } else { + if (op->data.dir == SPI_MEM_DATA_OUT) + ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR; + + /* Set QSPI Instruction Frame registers */ + writel(iar, aq->regs + QSPI_IAR); + writel(icr, aq->regs + QSPI_ICR); + writel(ifr, aq->regs + QSPI_IFR); + } + + return 0; +} + +static int atmel_qspi_exec_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + struct atmel_qspi *aq = dev_get_priv(slave->dev->parent); + u32 sr, imr, offset; + int err; + + err = atmel_qspi_set_cfg(aq, op, &offset); + if (err) + return err; + + /* Skip to the final steps if there is no data */ + if (op->data.nbytes) { + /* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */ + (void)readl(aq->regs + QSPI_IFR); + + /* Send/Receive data */ + if (op->data.dir == SPI_MEM_DATA_IN) + memcpy_fromio(op->data.buf.in, aq->mem + offset, + op->data.nbytes); + else + memcpy_toio(aq->mem + offset, op->data.buf.out, + op->data.nbytes); + + /* Release the chip-select */ + writel(QSPI_CR_LASTXFER, aq->regs + QSPI_CR); + } + + /* Poll INSTruction End and Chip Select Rise flags. */ + imr = QSPI_SR_INSTRE | QSPI_SR_CSR; + return readl_poll_timeout(aq->regs + QSPI_SR, sr, (sr & imr) == imr, + 1000000); +} + +static int atmel_qspi_set_speed(struct udevice *bus, uint hz) +{ + struct atmel_qspi *aq = dev_get_priv(bus); + u32 scr, scbr, mask, new_value; + + /* Compute the QSPI baudrate */ + scbr = DIV_ROUND_UP(aq->bus_clk_rate, hz); + if (scbr > 0) + scbr--; + + new_value = QSPI_SCR_SCBR(scbr); + mask = QSPI_SCR_SCBR_MASK; + + scr = readl(aq->regs + QSPI_SCR); + if ((scr & mask) == new_value) + return 0; + + scr = (scr & ~mask) | new_value; + writel(scr, aq->regs + QSPI_SCR); + + return 0; +} + +static int atmel_qspi_set_mode(struct udevice *bus, uint mode) +{ + struct atmel_qspi *aq = dev_get_priv(bus); + u32 scr, mask, new_value = 0; + + if (mode & SPI_CPOL) + new_value = QSPI_SCR_CPOL; + if (mode & SPI_CPHA) + new_value = QSPI_SCR_CPHA; + + mask = QSPI_SCR_CPOL | QSPI_SCR_CPHA; + + scr = readl(aq->regs + QSPI_SCR); + if ((scr & mask) == new_value) + return 0; + + scr = (scr & ~mask) | new_value; + writel(scr, aq->regs + QSPI_SCR); + + return 0; +} + +static int atmel_qspi_enable_clk(struct udevice *dev) +{ + struct atmel_qspi *aq = dev_get_priv(dev); + struct clk pclk, qspick; + int ret; + + ret = clk_get_by_name(dev, "pclk", &pclk); + if (ret) + ret = clk_get_by_index(dev, 0, &pclk); + + if (ret) { + dev_err(dev, "Missing QSPI peripheral clock\n"); + return ret; + } + + ret = clk_enable(&pclk); + if (ret) { + dev_err(dev, "Failed to enable QSPI peripheral clock\n"); + goto free_pclk; + } + + if (aq->caps->has_qspick) { + /* Get the QSPI system clock */ + ret = clk_get_by_name(dev, "qspick", &qspick); + if (ret) { + dev_err(dev, "Missing QSPI peripheral clock\n"); + goto free_pclk; + } + + ret = clk_enable(&qspick); + if (ret) + dev_err(dev, "Failed to enable QSPI system clock\n"); + clk_free(&qspick); + } + + aq->bus_clk_rate = clk_get_rate(&pclk); + if (!aq->bus_clk_rate) + ret = -EINVAL; + +free_pclk: + clk_free(&pclk); + + return ret; +} + +static void atmel_qspi_init(struct atmel_qspi *aq) +{ + /* Reset the QSPI controller */ + writel(QSPI_CR_SWRST, aq->regs + QSPI_CR); + + /* Set the QSPI controller by default in Serial Memory Mode */ + writel(QSPI_MR_SMM, aq->regs + QSPI_MR); + aq->mr = QSPI_MR_SMM; + + /* Enable the QSPI controller */ + writel(QSPI_CR_QSPIEN, aq->regs + QSPI_CR); +} + +static int atmel_qspi_probe(struct udevice *dev) +{ + struct atmel_qspi *aq = dev_get_priv(dev); + struct resource res; + int ret; + + aq->caps = (struct atmel_qspi_caps *)dev_get_driver_data(dev); + if (!aq->caps) { + dev_err(dev, "Could not retrieve QSPI caps\n"); + return -EINVAL; + }; + + /* Map the registers */ + ret = dev_read_resource_byname(dev, "qspi_base", &res); + if (ret) { + dev_err(dev, "missing registers\n"); + return ret; + } + + aq->regs = devm_ioremap(dev, res.start, resource_size(&res)); + if (IS_ERR(aq->regs)) + return PTR_ERR(aq->regs); + + /* Map the AHB memory */ + ret = dev_read_resource_byname(dev, "qspi_mmap", &res); + if (ret) { + dev_err(dev, "missing AHB memory\n"); + return ret; + } + + aq->mem = devm_ioremap(dev, res.start, resource_size(&res)); + if (IS_ERR(aq->mem)) + return PTR_ERR(aq->mem); + + ret = atmel_qspi_enable_clk(dev); + if (ret) + return ret; + + atmel_qspi_init(aq); + + return 0; +} + +static const struct spi_controller_mem_ops atmel_qspi_mem_ops = { + .supports_op = atmel_qspi_supports_op, + .exec_op = atmel_qspi_exec_op, +}; + +static const struct dm_spi_ops atmel_qspi_ops = { + .set_speed = atmel_qspi_set_speed, + .set_mode = atmel_qspi_set_mode, + .mem_ops = &atmel_qspi_mem_ops, +}; + +static const struct atmel_qspi_caps atmel_sama5d2_qspi_caps = {}; + +static const struct atmel_qspi_caps atmel_sam9x60_qspi_caps = { + .has_qspick = true, + .has_ricr = true, +}; + +static const struct udevice_id atmel_qspi_ids[] = { + { + .compatible = "atmel,sama5d2-qspi", + .data = (ulong)&atmel_sama5d2_qspi_caps, + }, + { + .compatible = "microchip,sam9x60-qspi", + .data = (ulong)&atmel_sam9x60_qspi_caps, + }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(atmel_qspi) = { + .name = "atmel_qspi", + .id = UCLASS_SPI, + .of_match = atmel_qspi_ids, + .ops = &atmel_qspi_ops, + .priv_auto_alloc_size = sizeof(struct atmel_qspi), + .probe = atmel_qspi_probe, +}; |