diff options
-rw-r--r-- | drivers/mmc/omap_hsmmc.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 57548ee31f..2b77422eee 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -34,6 +34,10 @@ #endif #include <asm/io.h> #include <asm/arch/mmc_host_def.h> +#ifdef CONFIG_OMAP54XX +#include <asm/arch/mux_dra7xx.h> +#include <asm/arch/dra7xx_iodelay.h> +#endif #if !defined(CONFIG_SOC_KEYSTONE) #include <asm/gpio.h> #include <asm/arch/sys_proto.h> @@ -57,6 +61,15 @@ DECLARE_GLOBAL_DATA_PTR; #define SYSCTL_SRC (1 << 25) #define SYSCTL_SRD (1 << 26) +#ifdef CONFIG_IODELAY_RECALIBRATION +struct omap_hsmmc_pinctrl_state { + struct pad_conf_entry *padconf; + int npads; + struct iodelay_cfg_entry *iodelay; + int niodelays; +}; +#endif + struct omap_hsmmc_data { struct hsmmc *base_addr; #if !CONFIG_IS_ENABLED(DM_MMC) @@ -83,6 +96,21 @@ struct omap_hsmmc_data { struct omap_hsmmc_adma_desc *adma_desc_table; uint desc_slot; #endif +#ifdef CONFIG_IODELAY_RECALIBRATION + struct omap_hsmmc_pinctrl_state *default_pinctrl_state; + struct omap_hsmmc_pinctrl_state *hs_pinctrl_state; + struct omap_hsmmc_pinctrl_state *hs200_1_8v_pinctrl_state; + struct omap_hsmmc_pinctrl_state *ddr_1_8v_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr12_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr25_pinctrl_state; + struct omap_hsmmc_pinctrl_state *ddr50_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr50_pinctrl_state; + struct omap_hsmmc_pinctrl_state *sdr104_pinctrl_state; +#endif +}; + +struct omap_mmc_of_data { + u8 controller_flags; }; #ifndef CONFIG_OMAP34XX @@ -119,6 +147,7 @@ struct omap_hsmmc_adma_desc { #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) #define OMAP_HSMMC_NO_1_8_V BIT(1) #define OMAP_HSMMC_USE_ADMA BIT(2) +#define OMAP_HSMMC_REQUIRE_IODELAY BIT(3) static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size); static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, @@ -261,6 +290,56 @@ void mmc_init_stream(struct hsmmc *mmc_base) } #if CONFIG_IS_ENABLED(DM_MMC) +#ifdef CONFIG_IODELAY_RECALIBRATION +static void omap_hsmmc_io_recalibrate(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct omap_hsmmc_pinctrl_state *pinctrl_state; + + switch (priv->mode) { + case MMC_HS_200: + pinctrl_state = priv->hs200_1_8v_pinctrl_state; + break; + case UHS_SDR104: + pinctrl_state = priv->sdr104_pinctrl_state; + break; + case UHS_SDR50: + pinctrl_state = priv->sdr50_pinctrl_state; + break; + case UHS_DDR50: + pinctrl_state = priv->ddr50_pinctrl_state; + break; + case UHS_SDR25: + pinctrl_state = priv->sdr25_pinctrl_state; + break; + case UHS_SDR12: + pinctrl_state = priv->sdr12_pinctrl_state; + break; + case SD_HS: + case MMC_HS: + case MMC_HS_52: + pinctrl_state = priv->hs_pinctrl_state; + break; + case MMC_DDR_52: + pinctrl_state = priv->ddr_1_8v_pinctrl_state; + default: + pinctrl_state = priv->default_pinctrl_state; + break; + } + + if (priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY) { + if (pinctrl_state->iodelay) + late_recalibrate_iodelay(pinctrl_state->padconf, + pinctrl_state->npads, + pinctrl_state->iodelay, + pinctrl_state->niodelays); + else + do_set_mux32((*ctrl)->control_padconf_core_base, + pinctrl_state->padconf, + pinctrl_state->npads); + } +} +#endif static void omap_hsmmc_set_timing(struct mmc *mmc) { u32 val; @@ -269,6 +348,7 @@ static void omap_hsmmc_set_timing(struct mmc *mmc) mmc_base = priv->base_addr; + omap_hsmmc_stop_clock(mmc_base); val = readl(&mmc_base->ac12); val &= ~AC12_UHSMC_MASK; priv->mode = mmc->selected_mode; @@ -306,6 +386,11 @@ static void omap_hsmmc_set_timing(struct mmc *mmc) break; } writel(val, &mmc_base->ac12); + +#ifdef CONFIG_IODELAY_RECALIBRATION + omap_hsmmc_io_recalibrate(mmc); +#endif + omap_hsmmc_start_clock(mmc_base); } static void omap_hsmmc_conf_bus_power(struct mmc *mmc) @@ -1290,10 +1375,271 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, return 0; } #else + +#ifdef CONFIG_IODELAY_RECALIBRATION +static struct pad_conf_entry * +omap_hsmmc_get_pad_conf_entry(const fdt32_t *pinctrl, int count) +{ + int index = 0; + struct pad_conf_entry *padconf; + + padconf = (struct pad_conf_entry *)malloc(sizeof(*padconf) * count); + if (!padconf) { + debug("failed to allocate memory\n"); + return 0; + } + + while (index < count) { + padconf[index].offset = fdt32_to_cpu(pinctrl[2 * index]); + padconf[index].val = fdt32_to_cpu(pinctrl[2 * index + 1]); + index++; + } + + return padconf; +} + +static struct iodelay_cfg_entry * +omap_hsmmc_get_iodelay_cfg_entry(const fdt32_t *pinctrl, int count) +{ + int index = 0; + struct iodelay_cfg_entry *iodelay; + + iodelay = (struct iodelay_cfg_entry *)malloc(sizeof(*iodelay) * count); + if (!iodelay) { + debug("failed to allocate memory\n"); + return 0; + } + + while (index < count) { + iodelay[index].offset = fdt32_to_cpu(pinctrl[3 * index]); + iodelay[index].a_delay = fdt32_to_cpu(pinctrl[3 * index + 1]); + iodelay[index].g_delay = fdt32_to_cpu(pinctrl[3 * index + 2]); + index++; + } + + return iodelay; +} + +static const fdt32_t *omap_hsmmc_get_pinctrl_entry(u32 phandle, + const char *name, int *len) +{ + const void *fdt = gd->fdt_blob; + int offset; + const fdt32_t *pinctrl; + + offset = fdt_node_offset_by_phandle(fdt, phandle); + if (offset < 0) { + debug("failed to get pinctrl node %s.\n", + fdt_strerror(offset)); + return 0; + } + + pinctrl = fdt_getprop(fdt, offset, name, len); + if (!pinctrl) { + debug("failed to get property %s\n", name); + return 0; + } + + return pinctrl; +} + +static uint32_t omap_hsmmc_get_pad_conf_phandle(struct mmc *mmc, + char *prop_name) +{ + const void *fdt = gd->fdt_blob; + const __be32 *phandle; + int node = dev_of_offset(mmc->dev); + + phandle = fdt_getprop(fdt, node, prop_name, NULL); + if (!phandle) { + debug("failed to get property %s\n", prop_name); + return 0; + } + + return fdt32_to_cpu(*phandle); +} + +static uint32_t omap_hsmmc_get_iodelay_phandle(struct mmc *mmc, + char *prop_name) +{ + const void *fdt = gd->fdt_blob; + const __be32 *phandle; + int len; + int count; + int node = dev_of_offset(mmc->dev); + + phandle = fdt_getprop(fdt, node, prop_name, &len); + if (!phandle) { + debug("failed to get property %s\n", prop_name); + return 0; + } + + /* No manual mode iodelay values if count < 2 */ + count = len / sizeof(*phandle); + if (count < 2) + return 0; + + return fdt32_to_cpu(*(phandle + 1)); +} + +static struct pad_conf_entry * +omap_hsmmc_get_pad_conf(struct mmc *mmc, char *prop_name, int *npads) +{ + int len; + int count; + struct pad_conf_entry *padconf; + u32 phandle; + const fdt32_t *pinctrl; + + phandle = omap_hsmmc_get_pad_conf_phandle(mmc, prop_name); + if (!phandle) + return ERR_PTR(-EINVAL); + + pinctrl = omap_hsmmc_get_pinctrl_entry(phandle, "pinctrl-single,pins", + &len); + if (!pinctrl) + return ERR_PTR(-EINVAL); + + count = (len / sizeof(*pinctrl)) / 2; + padconf = omap_hsmmc_get_pad_conf_entry(pinctrl, count); + if (!padconf) + return ERR_PTR(-EINVAL); + + *npads = count; + + return padconf; +} + +static struct iodelay_cfg_entry * +omap_hsmmc_get_iodelay(struct mmc *mmc, char *prop_name, int *niodelay) +{ + int len; + int count; + struct iodelay_cfg_entry *iodelay; + u32 phandle; + const fdt32_t *pinctrl; + + phandle = omap_hsmmc_get_iodelay_phandle(mmc, prop_name); + /* Not all modes have manual mode iodelay values. So its not fatal */ + if (!phandle) + return 0; + + pinctrl = omap_hsmmc_get_pinctrl_entry(phandle, "pinctrl-pin-array", + &len); + if (!pinctrl) + return ERR_PTR(-EINVAL); + + count = (len / sizeof(*pinctrl)) / 3; + iodelay = omap_hsmmc_get_iodelay_cfg_entry(pinctrl, count); + if (!iodelay) + return ERR_PTR(-EINVAL); + + *niodelay = count; + + return iodelay; +} + +static struct omap_hsmmc_pinctrl_state * +omap_hsmmc_get_pinctrl_by_mode(struct mmc *mmc, char *mode) +{ + int index; + int npads = 0; + int niodelays = 0; + const void *fdt = gd->fdt_blob; + int node = dev_of_offset(mmc->dev); + char prop_name[11]; + struct omap_hsmmc_pinctrl_state *pinctrl_state; + + pinctrl_state = (struct omap_hsmmc_pinctrl_state *) + malloc(sizeof(*pinctrl_state)); + if (!pinctrl_state) { + debug("failed to allocate memory\n"); + return 0; + } + + index = fdt_stringlist_search(fdt, node, "pinctrl-names", mode); + if (index < 0) { + debug("fail to find %s mode %s\n", mode, fdt_strerror(index)); + goto err_pinctrl_state; + } + + sprintf(prop_name, "pinctrl-%d", index); + + pinctrl_state->padconf = omap_hsmmc_get_pad_conf(mmc, prop_name, + &npads); + if (IS_ERR(pinctrl_state->padconf)) + goto err_pinctrl_state; + pinctrl_state->npads = npads; + + pinctrl_state->iodelay = omap_hsmmc_get_iodelay(mmc, prop_name, + &niodelays); + if (IS_ERR(pinctrl_state->iodelay)) + goto err_padconf; + pinctrl_state->niodelays = niodelays; + + return pinctrl_state; + +err_padconf: + kfree(pinctrl_state->padconf); + +err_pinctrl_state: + kfree(pinctrl_state); + return 0; +} + +#define OMAP_HSMMC_SETUP_PINCTRL(capmask, mode) \ + do { \ + struct omap_hsmmc_pinctrl_state *s; \ + if (!(cfg->host_caps & capmask)) \ + break; \ + \ + s = omap_hsmmc_get_pinctrl_by_mode(mmc, #mode); \ + if (!s) { \ + debug("%s: no pinctrl for %s\n", \ + mmc->dev->name, #mode); \ + cfg->host_caps &= ~(capmask); \ + } else { \ + priv->mode##_pinctrl_state = s; \ + } \ + } while (0) + +static int omap_hsmmc_get_pinctrl_state(struct mmc *mmc) +{ + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + struct mmc_config *cfg = omap_hsmmc_get_cfg(mmc); + struct omap_hsmmc_pinctrl_state *default_pinctrl; + + if (!(priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY)) + return 0; + + default_pinctrl = omap_hsmmc_get_pinctrl_by_mode(mmc, "default"); + if (!default_pinctrl) { + printf("no pinctrl state for default mode\n"); + return -EINVAL; + } + + priv->default_pinctrl_state = default_pinctrl; + + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR104), sdr104); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR50), sdr50); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_DDR50), ddr50); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR25), sdr25); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(UHS_SDR12), sdr12); + + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_HS_200), hs200_1_8v); + OMAP_HSMMC_SETUP_PINCTRL(MMC_CAP(MMC_DDR_52), ddr_1_8v); + OMAP_HSMMC_SETUP_PINCTRL(MMC_MODE_HS, hs); + + return 0; +} +#endif + #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) { struct omap_hsmmc_plat *plat = dev_get_platdata(dev); + struct omap_mmc_of_data *of_data = (void *)dev_get_driver_data(dev); + struct mmc_config *cfg = &plat->cfg; const void *fdt = gd->fdt_blob; int node = dev_of_offset(dev); @@ -1315,6 +1661,8 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev) plat->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; if (fdtdec_get_bool(fdt, node, "no-1-8-v")) plat->controller_flags |= OMAP_HSMMC_NO_1_8_V; + if (of_data) + plat->controller_flags |= of_data->controller_flags; #ifdef OMAP_HSMMC_USE_GPIO plat->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted"); @@ -1340,9 +1688,13 @@ static int omap_hsmmc_probe(struct udevice *dev) struct omap_hsmmc_data *priv = dev_get_priv(dev); struct mmc_config *cfg = &plat->cfg; struct mmc *mmc; +#ifdef CONFIG_IODELAY_RECALIBRATION + int ret; +#endif cfg->name = "OMAP SD/MMC"; priv->base_addr = plat->base_addr; + priv->controller_flags = plat->controller_flags; #ifdef OMAP_HSMMC_USE_GPIO priv->cd_inverted = plat->cd_inverted; #endif @@ -1363,14 +1715,34 @@ static int omap_hsmmc_probe(struct udevice *dev) mmc->dev = dev; upriv->mmc = mmc; +#ifdef CONFIG_IODELAY_RECALIBRATION + ret = omap_hsmmc_get_pinctrl_state(mmc); + /* + * disable high speed modes for the platforms that require IO delay + * and for which we don't have this information + */ + if ((ret < 0) && + (priv->controller_flags & OMAP_HSMMC_REQUIRE_IODELAY)) { + priv->controller_flags &= ~OMAP_HSMMC_REQUIRE_IODELAY; + cfg->host_caps &= ~(MMC_CAP(MMC_HS_200) | MMC_CAP(MMC_DDR_52) | + UHS_CAPS); + } +#endif + return omap_hsmmc_init_setup(mmc); } #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) + +static const struct omap_mmc_of_data dra7_mmc_of_data = { + .controller_flags = OMAP_HSMMC_REQUIRE_IODELAY, +}; + static const struct udevice_id omap_hsmmc_ids[] = { { .compatible = "ti,omap3-hsmmc" }, { .compatible = "ti,omap4-hsmmc" }, { .compatible = "ti,am33xx-hsmmc" }, + { .compatible = "ti,dra7-hsmmc", .data = (ulong)&dra7_mmc_of_data }, { } }; #endif |