From 39f7a7ee8b68299e50bbfd4a68f392379b2e98df Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 9 Nov 2016 08:14:08 -0800 Subject: i.MX: esdhc: Do not rely on CPU type for quirks CPU type is not a reliable indicator of the underlying type of IP core used, since there's no 1:1 mapping between the two. As example of one such violation consider Vybrid SoC which contains IP block from i.MX53. Instead port feature flags from corresponding Linux kernel driver and use the ones that are relevant. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/mci/imx-esdhc.c | 121 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 100 insertions(+), 21 deletions(-) (limited to 'drivers/mci') diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c index 262a904ace..764a106453 100644 --- a/drivers/mci/imx-esdhc.c +++ b/drivers/mci/imx-esdhc.c @@ -35,28 +35,81 @@ #include #include #include +#include #include "sdhci.h" #include "imx-esdhc.h" +/* + * The CMDTYPE of the CMD register (offset 0xE) should be set to + * "11" when the STOP CMD12 is issued on imx53 to abort one + * open ended multi-blk IO. Otherwise the TC INT wouldn't + * be generated. + * In exact block transfer, the controller doesn't complete the + * operations automatically as required at the end of the + * transfer and remains on hold if the abort command is not sent. + * As a result, the TC flag is not asserted and SW received timeout + * exeception. Bit1 of Vendor Spec registor is used to fix it. + */ +#define ESDHC_FLAG_MULTIBLK_NO_INT BIT(1) +/* + * The flag enables the workaround for ESDHC errata ENGcm07207 which + * affects i.MX25 and i.MX35. + */ +#define ESDHC_FLAG_ENGCM07207 BIT(2) +/* + * The flag tells that the ESDHC controller is an USDHC block that is + * integrated on the i.MX6 series. + */ +#define ESDHC_FLAG_USDHC BIT(3) +/* The IP supports manual tuning process */ +#define ESDHC_FLAG_MAN_TUNING BIT(4) +/* The IP supports standard tuning process */ +#define ESDHC_FLAG_STD_TUNING BIT(5) +/* The IP has SDHCI_CAPABILITIES_1 register */ +#define ESDHC_FLAG_HAVE_CAP1 BIT(6) +/* + * The IP has errata ERR004536 + * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow, + * when reading data from the card + */ +#define ESDHC_FLAG_ERR004536 BIT(7) +/* The IP supports HS200 mode */ +#define ESDHC_FLAG_HS200 BIT(8) +/* The IP supports HS400 mode */ +#define ESDHC_FLAG_HS400 BIT(9) + + #define IMX_SDHCI_WML 0x44 #define IMX_SDHCI_MIXCTRL 0x48 #define IMX_SDHCI_DLL_CTRL 0x60 #define IMX_SDHCI_MIX_CTRL_FBCLK_SEL (BIT(25)) +struct esdhc_soc_data { + u32 flags; +}; + struct fsl_esdhc_host { struct mci_host mci; void __iomem *regs; struct device_d *dev; struct clk *clk; + const struct esdhc_soc_data *socdata; }; #define to_fsl_esdhc(mci) container_of(mci, struct fsl_esdhc_host, mci) #define SDHCI_CMD_ABORTCMD (0xC0 << 16) +static inline int esdhc_is_usdhc(struct fsl_esdhc_host *data) +{ + return !!(data->socdata->flags & ESDHC_FLAG_USDHC); +} + + /* Return the XFERTYP flags for a given command and data packet */ -static u32 esdhc_xfertyp(struct mci_cmd *cmd, struct mci_data *data) +static u32 esdhc_xfertyp(struct fsl_esdhc_host *host, + struct mci_cmd *cmd, struct mci_data *data) { u32 xfertyp = 0; @@ -85,8 +138,8 @@ static u32 esdhc_xfertyp(struct mci_cmd *cmd, struct mci_data *data) xfertyp |= COMMAND_RSPTYP_48_BUSY; else if (cmd->resp_type & MMC_RSP_PRESENT) xfertyp |= COMMAND_RSPTYP_48; - if ((cpu_is_mx50() || cpu_is_mx51() || cpu_is_mx53()) && - cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + if ((host->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) && + (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)) xfertyp |= SDHCI_CMD_ABORTCMD; return COMMAND_CMD(cmd->cmdidx) | xfertyp; @@ -273,12 +326,12 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data) } /* Figure out the transfer arguments */ - xfertyp = esdhc_xfertyp(cmd, data); + xfertyp = esdhc_xfertyp(host, cmd, data); /* Send the command */ esdhc_write32(regs + SDHCI_ARGUMENT, cmd->cmdarg); - if (cpu_is_mx6()) { + if (esdhc_is_usdhc(host)) { /* write lower-half of xfertyp to mixctrl */ mixctrl = xfertyp & 0xFFFF; /* Keep the bits 22-25 of the register as is */ @@ -525,7 +578,7 @@ static int esdhc_reset(struct fsl_esdhc_host *host) SYSCTL_RSTA); /* extra register reset for i.MX6 Solo/DualLite */ - if (cpu_is_mx6()) { + if (esdhc_is_usdhc(host)) { /* reset bit FBCLK_SEL */ val = esdhc_read32(regs + IMX_SDHCI_MIXCTRL); val &= ~IMX_SDHCI_MIX_CTRL_FBCLK_SEL; @@ -570,6 +623,10 @@ static int fsl_esdhc_probe(struct device_d *dev) host = xzalloc(sizeof(*host)); mci = &host->mci; + host->socdata = of_device_get_match_data(dev); + if (!host->socdata) + return -EINVAL; + host->clk = clk_get(dev, NULL); if (IS_ERR(host->clk)) return PTR_ERR(host->clk); @@ -634,22 +691,44 @@ static int fsl_esdhc_probe(struct device_d *dev) return mci_register(&host->mci); } +static struct esdhc_soc_data esdhc_imx25_data = { + .flags = ESDHC_FLAG_ENGCM07207, +}; + +static struct esdhc_soc_data esdhc_imx50_data = { + .flags = ESDHC_FLAG_MULTIBLK_NO_INT, + /* .flags = 0, */ +}; + +static struct esdhc_soc_data esdhc_imx51_data = { + .flags = ESDHC_FLAG_MULTIBLK_NO_INT, + /* .flags = 0, */ +}; + +static struct esdhc_soc_data esdhc_imx53_data = { + .flags = ESDHC_FLAG_MULTIBLK_NO_INT, +}; + +static struct esdhc_soc_data usdhc_imx6q_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING, +}; + +static struct esdhc_soc_data usdhc_imx6sl_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536 + | ESDHC_FLAG_HS200, +}; + + static __maybe_unused struct of_device_id fsl_esdhc_compatible[] = { - { - .compatible = "fsl,imx25-esdhc", - }, { - .compatible = "fsl,imx50-esdhc", - }, { - .compatible = "fsl,imx51-esdhc", - }, { - .compatible = "fsl,imx53-esdhc", - }, { - .compatible = "fsl,imx6q-usdhc", - }, { - .compatible = "fsl,imx6sl-usdhc", - }, { - /* sentinel */ - } + + { .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data }, + { .compatible = "fsl,imx50-esdhc", .data = &esdhc_imx50_data }, + { .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data }, + { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data }, + { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data }, + { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data }, + { /* sentinel */ } }; static struct driver_d fsl_esdhc_driver = { -- cgit v1.2.3 From 05f625a9e1ecab34a6e7a8eb96f10ff8be2c4856 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 9 Nov 2016 08:14:09 -0800 Subject: i.MX: esdhc: Request "per" clock explicitly Calling clk_get() with NULL as the second argument will give us "ipg" clock as a result. The actual clock feeding into the peripheral is "per" and, depending on the SoC, "ipg" and "per" may be separated by a clock divider, so querying "ipg"'s rate may not result in rate that does not represent the actual peripheral clock rate. Change the code to request "per" as our peripheral clock to avoid aforementioned problem. Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/mci/imx-esdhc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mci') diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c index 764a106453..d72e3f8c80 100644 --- a/drivers/mci/imx-esdhc.c +++ b/drivers/mci/imx-esdhc.c @@ -627,7 +627,7 @@ static int fsl_esdhc_probe(struct device_d *dev) if (!host->socdata) return -EINVAL; - host->clk = clk_get(dev, NULL); + host->clk = clk_get(dev, "per"); if (IS_ERR(host->clk)) return PTR_ERR(host->clk); -- cgit v1.2.3