This patch adds little-endian mode support for little-endian host controller. To indicate the little-endian mode, we could add 'little-endian' property in device tree node. Signed-off-by: Yangbo Lu <yangbo.lu@xxxxxxxxxxxxx> --- drivers/mmc/host/sdhci-pltfm.c | 12 ++++++- drivers/mmc/host/sdhci-pltfm.h | 74 ++++++++++++++++++++++++++++++------------ 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index a207f5a..7ef1dcb 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -121,6 +121,8 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, size_t priv_size) { struct sdhci_host *host; + struct device_node *np = pdev->dev.of_node; + struct sdhci_pltfm_host *pltfm_host; struct resource *iomem; int ret; @@ -141,6 +143,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; @@ -224,7 +234,7 @@ int sdhci_pltfm_unregister(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); + int dead = (sdhci_readl(host, SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); clk_disable_unprepare(pltfm_host->clk); 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 */ -- 2.1.0.27.g96db324 -- 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