1. Esdhc driver aim to support different endian mode of eSDHC host on different SoC(Arm-little-endian/Powerpc-big-endian). Adding a property in device node to indicate the endian mode of the host controller. In the same time using generic IO entry to help auto transfer between little/big endian mode cpu. Example: * - big-endian/little-endian: Specifies the endian mode the controller * works in. esdhc: esdhc@1560000 { compatible = "fsl,esdhc"; reg = <0x1560000 0x10000>; interrupts = <0 94 0x04>; clock-names = "esdhc"; clocks = <&platform_clk 0>; voltage-ranges = <1800 3300>; sdhci,auto-cmd12; big-endian; bus-width = <4>; status = "disabled"; }; 2. Using sdhci_readl() to replace readl() in sdhci_pltfm_unregister to support working on different platforms. 3. Update IO entry for Nintendo Wii SDHCI controllers. This change needs to be verified. This patch has been verified on p1021rdb-pc(Powerpc), VF610(arm), ls1021A(arm) platforms. Signed-off-by: Haijun Zhang <Haijun.Zhang@xxxxxxxxxxxxx> --- .../devicetree/bindings/mmc/fsl-esdhc.txt | 3 + drivers/mmc/host/Kconfig | 1 - drivers/mmc/host/sdhci-of-esdhc.c | 107 ++++++++++++++------- drivers/mmc/host/sdhci-of-hlwd.c | 12 +-- drivers/mmc/host/sdhci-pltfm.c | 11 ++- drivers/mmc/host/sdhci-pltfm.h | 74 ++++++++++---- 6 files changed, 145 insertions(+), 63 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt index b7943f3..6d853d7 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt +++ b/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt @@ -22,6 +22,8 @@ Optional properties: - voltage-ranges : two cells are required, first cell specifies minimum slot voltage (mV), second cell specifies maximum slot voltage (mV). Several ranges could be specified. + - big-endian/little-endian: Specifies the endia mode the controller + works in. Example: @@ -33,4 +35,5 @@ sdhci@2e000 { /* Filled in by U-Boot */ clock-frequency = <0>; voltage-ranges = <3300 3300>; + big-endian; }; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 8aaf8c1..3cc367e 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -119,7 +119,6 @@ config MMC_SDHCI_OF_ARASAN config MMC_SDHCI_OF_ESDHC tristate "SDHCI OF support for the Freescale eSDHC controller" depends on MMC_SDHCI_PLTFM - depends on PPC_OF select MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER help This selects the Freescale eSDHC controller support. diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 0b24997..ef9d87b 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -28,7 +28,7 @@ static u32 esdhc_readl(struct sdhci_host *host, int reg) { u32 ret; - ret = in_be32(host->ioaddr + reg); + ret = sdhci_32bs_readl(host, reg); /* * The bit of ADMA flag in eSDHC is not compatible with standard * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is @@ -40,7 +40,7 @@ static u32 esdhc_readl(struct sdhci_host *host, int reg) * the verdor version number, oxFE is SDHCI_HOST_VERSION. */ if ((reg == SDHCI_CAPABILITIES) && (ret & SDHCI_CAN_DO_ADMA1)) { - u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); + u32 tmp = sdhci_32bs_readl(host, SDHCI_SLOT_INT_STATUS); tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; if (tmp > VENDOR_V_22) ret |= SDHCI_CAN_DO_ADMA2; @@ -56,9 +56,9 @@ static u16 esdhc_readw(struct sdhci_host *host, int reg) int shift = (reg & 0x2) * 8; if (unlikely(reg == SDHCI_HOST_VERSION)) - ret = in_be32(host->ioaddr + base) & 0xffff; + ret = sdhci_32bs_readl(host, base) & 0xffff; else - ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff; + ret = (sdhci_32bs_readl(host, base) >> shift) & 0xffff; return ret; } @@ -66,7 +66,10 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg) { int base = reg & ~0x3; int shift = (reg & 0x3) * 8; - u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; + u32 ret; + u8 val; + + ret = sdhci_32bs_readl(host, base); /* * "DMA select" locates at offset 0x28 in SD specification, but on @@ -75,16 +78,18 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg) if (reg == SDHCI_HOST_CONTROL) { u32 dma_bits; - dma_bits = in_be32(host->ioaddr + reg); /* DMA select is 22,23 bits in Protocol Control Register */ - dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK; + dma_bits = (ret >> 5) & SDHCI_CTRL_DMA_MASK; /* fixup the result */ ret &= ~SDHCI_CTRL_DMA_MASK; ret |= dma_bits; + val = (ret & 0xff); } - return ret; + val = (ret >> shift) & 0xff; + + return val; } static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) @@ -96,11 +101,28 @@ static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) */ if (reg == SDHCI_INT_ENABLE) val |= SDHCI_INT_BLK_GAP; - sdhci_be32bs_writel(host, val, reg); + sdhci_32bs_writel(host, val, reg); } static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + switch (reg) { + case SDHCI_TRANSFER_MODE: + /* + * Postpone this write, we must do it together with a + * command write that is down below. + */ + pltfm_host->xfer_mode_shadow = val; + return; + case SDHCI_COMMAND: + sdhci_32bs_writel(host, val << 16 | + pltfm_host->xfer_mode_shadow, + SDHCI_TRANSFER_MODE); + return; + } + if (reg == SDHCI_BLOCK_SIZE) { /* * Two last DMA bits are reserved, and first one is used for @@ -109,7 +131,7 @@ static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) */ val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); } - sdhci_be32bs_writew(host, val, reg); + sdhci_clrsetbits(host, 0xffff, val, reg); } static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) @@ -130,16 +152,16 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) /* DMA select is 22,23 bits in Protocol Control Register */ dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; - clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, - dma_bits); + sdhci_clrsetbits(host, SDHCI_CTRL_DMA_MASK << 5, dma_bits, + SDHCI_HOST_CONTROL); val &= ~SDHCI_CTRL_DMA_MASK; - val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK; + val |= sdhci_32bs_readl(host, reg) & SDHCI_CTRL_DMA_MASK; } /* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */ if (reg == SDHCI_HOST_CONTROL) val &= ~ESDHC_HOST_CONTROL_RES; - sdhci_be32bs_writeb(host, val, reg); + sdhci_clrsetbits(host, 0xff, val, reg); } /* @@ -156,7 +178,7 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) dma_addr_t dmastart; dma_addr_t dmanow; - tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); + tmp = esdhc_readl(host, SDHCI_SLOT_INT_STATUS); tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; applicable = (intmask & SDHCI_INT_DATA_END) && @@ -174,12 +196,13 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + SDHCI_DEFAULT_BOUNDARY_SIZE; host->data->bytes_xfered = dmanow - dmastart; - sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); + esdhc_writel(host, dmanow, SDHCI_DMA_ADDRESS); } static int esdhc_of_enable_dma(struct sdhci_host *host) { - setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); + esdhc_writel(host, esdhc_readl(host, ESDHC_DMA_SYSCTL) + | ESDHC_DMA_SNOOP, ESDHC_DMA_SYSCTL); return 0; } @@ -246,13 +269,13 @@ out: static u32 esdhc_proctl; static void esdhc_of_suspend(struct sdhci_host *host) { - esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL); + esdhc_proctl = esdhc_readl(host, SDHCI_HOST_CONTROL); } static void esdhc_of_resume(struct sdhci_host *host) { esdhc_of_enable_dma(host); - sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); + esdhc_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL); } #endif @@ -260,7 +283,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host) { u32 vvn; - vvn = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); + vvn = esdhc_readl(host, SDHCI_SLOT_INT_STATUS); vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; if (vvn == VENDOR_V_22) host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; @@ -287,8 +310,8 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) break; } - clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, - ESDHC_CTRL_BUSWIDTH_MASK, ctrl); + sdhci_clrsetbits(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl, + SDHCI_HOST_CONTROL); return 0; } @@ -324,19 +347,18 @@ static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { .ops = &sdhci_esdhc_ops, }; -static int sdhci_esdhc_probe(struct platform_device *pdev) +static void esdhc_get_property(struct platform_device *pdev) { - struct sdhci_host *host; - struct device_node *np; - int ret; - - host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); - if (IS_ERR(host)) - return PTR_ERR(host); + struct device_node *np = pdev->dev.of_node; + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); sdhci_get_of_property(pdev); - np = pdev->dev.of_node; + /* call to generic mmc_of_parse to support additional capabilities */ + mmc_of_parse(host->mmc); + mmc_of_parse_voltage(np, &host->ocr_mask); + if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { /* * Freescale messed up with P2020 as it has a non-standard @@ -345,10 +367,27 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL; } - /* call to generic mmc_of_parse to support additional capabilities */ - mmc_of_parse(host->mmc); - mmc_of_parse_voltage(np, &host->ocr_mask); + if (of_device_is_compatible(np, "fsl,ls1021a-esdhc")) + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + + if (!pltfm_host->clock) { + pltfm_host->clk = devm_clk_get(&pdev->dev, NULL); + pltfm_host->clock = clk_get_rate(pltfm_host->clk); + clk_prepare_enable(pltfm_host->clk); + } +} + +static int sdhci_esdhc_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + int ret; + + + host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0); + if (IS_ERR(host)) + return PTR_ERR(host); + esdhc_get_property(pdev); ret = sdhci_add_host(host); if (ret) sdhci_pltfm_free(pdev); diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index 57c514a..3605665 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -35,26 +35,26 @@ static void sdhci_hlwd_writel(struct sdhci_host *host, u32 val, int reg) { - sdhci_be32bs_writel(host, val, reg); + sdhci_32bs_writel(host, val, reg); udelay(SDHCI_HLWD_WRITE_DELAY); } static void sdhci_hlwd_writew(struct sdhci_host *host, u16 val, int reg) { - sdhci_be32bs_writew(host, val, reg); + sdhci_32bs_writew(host, val, reg); udelay(SDHCI_HLWD_WRITE_DELAY); } static void sdhci_hlwd_writeb(struct sdhci_host *host, u8 val, int reg) { - sdhci_be32bs_writeb(host, val, reg); + sdhci_32bs_writeb(host, val, reg); udelay(SDHCI_HLWD_WRITE_DELAY); } static const struct sdhci_ops sdhci_hlwd_ops = { - .read_l = sdhci_be32bs_readl, - .read_w = sdhci_be32bs_readw, - .read_b = sdhci_be32bs_readb, + .read_l = sdhci_32bs_readl, + .read_w = sdhci_32bs_readw, + .read_b = sdhci_32bs_readb, .write_l = sdhci_hlwd_writel, .write_w = sdhci_hlwd_writew, .write_b = sdhci_hlwd_writeb, diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index bef250e..0d31e7e 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -120,6 +120,7 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, { struct sdhci_host *host; struct device_node *np = pdev->dev.of_node; + struct sdhci_pltfm_host *pltfm_host; struct resource *iomem; int ret; @@ -145,6 +146,14 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, goto err; } + pltfm_host = sdhci_priv(host); + pltfm_host->endian_mode = BIG_ENDIAN_MODE; + +#ifdef CONFIG_OF + if (of_get_property(np, "little-endian", NULL)) + pltfm_host->endian_mode = LITTLE_ENDIAN_MODE; +#endif /* CONFIG_OF */ + host->hw_name = dev_name(&pdev->dev); if (pdata && pdata->ops) host->ops = pdata->ops; @@ -227,7 +236,7 @@ EXPORT_SYMBOL_GPL(sdhci_pltfm_register); int sdhci_pltfm_unregister(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); - int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); + int dead = (sdhci_readl(host, SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); sdhci_pltfm_free(pdev); diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index 04bc248..a8cf954 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -28,6 +28,10 @@ struct sdhci_pltfm_host { /* migrate from sdhci_of_host */ unsigned int clock; u16 xfer_mode_shadow; + enum endian_mode { + LITTLE_ENDIAN_MODE, + BIG_ENDIAN_MODE, + } endian_mode; unsigned long private[0] ____cacheline_aligned; }; @@ -37,33 +41,61 @@ struct sdhci_pltfm_host { * These accessors are designed for big endian hosts doing I/O to * little endian controllers incorporating a 32-bit hardware byte swapper. */ -static inline u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg) +static inline void sdhci_clrsetbits(struct sdhci_host *host, u32 mask, + u32 val, int reg) { - return in_be32(host->ioaddr + reg); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + void __iomem *base = host->ioaddr + (reg & ~0x3); + u32 shift = (reg & 0x3) * 8; + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + iowrite32be(((ioread32be(base) & ~(mask << shift)) | + (val << shift)), base); + else + iowrite32(((ioread32(base) & ~(mask << shift)) | + (val << shift)), base); } -static inline u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg) +static inline u32 sdhci_32bs_readl(struct sdhci_host *host, int reg) { - return in_be16(host->ioaddr + (reg ^ 0x2)); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + return ioread32be(host->ioaddr + reg); + else + return ioread32(host->ioaddr + reg); } -static inline u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg) +static inline u16 sdhci_32bs_readw(struct sdhci_host *host, int reg) { - return in_8(host->ioaddr + (reg ^ 0x3)); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + return ioread16be(host->ioaddr + (reg ^ 0x2)); + else + return ioread16(host->ioaddr + (reg ^ 0x2)); } -static inline void sdhci_be32bs_writel(struct sdhci_host *host, - u32 val, int reg) +static inline u8 sdhci_32bs_readb(struct sdhci_host *host, int reg) { - out_be32(host->ioaddr + reg, val); + return ioread8(host->ioaddr + (reg ^ 0x3)); } -static inline void sdhci_be32bs_writew(struct sdhci_host *host, +static inline void sdhci_32bs_writel(struct sdhci_host *host, + u32 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + iowrite32be(val, host->ioaddr + reg); + else + iowrite32(val, host->ioaddr + reg); +} + +static inline void sdhci_32bs_writew(struct sdhci_host *host, u16 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - int base = reg & ~0x3; - int shift = (reg & 0x2) * 8; switch (reg) { case SDHCI_TRANSFER_MODE: @@ -74,20 +106,20 @@ static inline void sdhci_be32bs_writew(struct sdhci_host *host, pltfm_host->xfer_mode_shadow = val; return; case SDHCI_COMMAND: - sdhci_be32bs_writel(host, - val << 16 | pltfm_host->xfer_mode_shadow, - SDHCI_TRANSFER_MODE); + if (pltfm_host->endian_mode == BIG_ENDIAN_MODE) + iowrite32be(val << 16 | pltfm_host->xfer_mode_shadow, + host->ioaddr + SDHCI_TRANSFER_MODE); + else + iowrite32(val << 16 | pltfm_host->xfer_mode_shadow, + host->ioaddr + SDHCI_TRANSFER_MODE); return; } - clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift); + sdhci_clrsetbits(host, 0xffff, val, reg); } -static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg) +static inline void sdhci_32bs_writeb(struct sdhci_host *host, u8 val, int reg) { - int base = reg & ~0x3; - int shift = (reg & 0x3) * 8; - - clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift); + sdhci_clrsetbits(host, 0xff, val, reg); } #endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */ -- 1.8.4 -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html