diff mbox

[v5,2/4] mmc: sdhci-of-esdhc: support both BE and LE host controller

Message ID 1443422113-15054-1-git-send-email-yangbo.lu@freescale.com (mailing list archive)
State Superseded, archived
Headers show

Commit Message

yangbo lu Sept. 28, 2015, 6:35 a.m. UTC
To support little endian eSDHC controller, we redefine both BE and
LE IO accessors. In the new accessors, use ioread*/iowrite* instead
of in_be32/out_be32 and will select accessors according to endian
mode in probe function.

Signed-off-by: Yangbo Lu <yangbo.lu@freescale.com>
---
 drivers/mmc/host/sdhci-esdhc.h    |   2 +
 drivers/mmc/host/sdhci-of-esdhc.c | 436 ++++++++++++++++++++++++++++----------
 2 files changed, 324 insertions(+), 114 deletions(-)

Comments

Ulf Hansson Sept. 29, 2015, 11:46 a.m. UTC | #1
On 28 September 2015 at 08:35, Yangbo Lu <yangbo.lu@freescale.com> wrote:
> To support little endian eSDHC controller, we redefine both BE and
> LE IO accessors. In the new accessors, use ioread*/iowrite* instead
> of in_be32/out_be32 and will select accessors according to endian
> mode in probe function.
>
> Signed-off-by: Yangbo Lu <yangbo.lu@freescale.com>
> ---

It's good practice to provide the reviewers with some history of what
has been changed between each revision. Can you please add that future
wise.

>  drivers/mmc/host/sdhci-esdhc.h    |   2 +
>  drivers/mmc/host/sdhci-of-esdhc.c | 436 ++++++++++++++++++++++++++++----------
>  2 files changed, 324 insertions(+), 114 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h
> index 163ac99..de132e2 100644
> --- a/drivers/mmc/host/sdhci-esdhc.h
> +++ b/drivers/mmc/host/sdhci-esdhc.h
> @@ -24,6 +24,8 @@
>                                 SDHCI_QUIRK_PIO_NEEDS_DELAY | \
>                                 SDHCI_QUIRK_NO_HISPD_BIT)
>
> +#define ESDHC_PROCTL           0x28
> +
>  #define ESDHC_SYSTEM_CONTROL   0x2c
>  #define ESDHC_CLOCK_MASK       0x0000fff0
>  #define ESDHC_PREDIV_SHIFT     8
> diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
> index 2a54dbe..5315864 100644
> --- a/drivers/mmc/host/sdhci-of-esdhc.c
> +++ b/drivers/mmc/host/sdhci-of-esdhc.c
> @@ -18,128 +18,326 @@
>  #include <linux/of.h>
>  #include <linux/delay.h>
>  #include <linux/module.h>
> +#include <linux/slab.h>
>  #include <linux/mmc/host.h>
>  #include "sdhci-pltfm.h"
>  #include "sdhci-esdhc.h"
>
>  #define VENDOR_V_22    0x12
>  #define VENDOR_V_23    0x13
> -static u32 esdhc_readl(struct sdhci_host *host, int reg)
> +
> +struct esdhc_host {
> +       u8 vendor_ver;
> +       u8 spec_ver;
> +       u16 xfer_mode_shadow;
> +       struct sdhci_host *host;
> +};
> +
> +static struct esdhc_host *esdhc;

This looks wrong, you shouldn't have a global variable for this.
Instead, I think you shall allocate one per device/host.

> +
> +/**
> + * esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register
> + *                    to make it compatible with SD spec.
> + *
> + * @esdhc: pointer to esdhc_host
> + * @spec_reg: SD spec register address
> + * @value: 32bit eSDHC register value on spec_reg address
> + *
> + * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC
> + * registers are 32 bits. There are differences in register size, register
> + * address, register function, bit position and function between eSDHC spec
> + * and SD spec.
> + *
> + * Return a fixed up register value
> + */
> +static u32 esdhc_readl_fixup(struct esdhc_host *esdhc,
> +                                    int spec_reg, u32 value)
>  {
>         u32 ret;
>
> -       ret = in_be32(host->ioaddr + 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
>          * supported by eSDHC.
>          * And for many FSL eSDHC controller, the reset value of field
> -        * SDHCI_CAN_DO_ADMA1 is one, but some of them can't support ADMA,
> +        * SDHCI_CAN_DO_ADMA1 is 1, but some of them can't support ADMA,
>          * only these vendor version is greater than 2.2/0x12 support ADMA.
> -        * For FSL eSDHC, must aligned 4-byte, so use 0xFC to read the
> -        * 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);
> -               tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
> -               if (tmp > VENDOR_V_22)
> -                       ret |= SDHCI_CAN_DO_ADMA2;
> +       if ((spec_reg == SDHCI_CAPABILITIES) && (value & SDHCI_CAN_DO_ADMA1)) {
> +               if (esdhc->vendor_ver > VENDOR_V_22) {
> +                       ret = value | SDHCI_CAN_DO_ADMA2;
> +                       return ret;
> +               }
>         }
> -
> +       ret = value;
>         return ret;
>  }
>
> -static u16 esdhc_readw(struct sdhci_host *host, int reg)
> +static u16 esdhc_readw_fixup(struct esdhc_host *esdhc,
> +                                    int spec_reg, u32 value)
>  {
>         u16 ret;
> -       int base = reg & ~0x3;
> -       int shift = (reg & 0x2) * 8;
> +       int shift = (spec_reg & 0x2) * 8;
>
> -       if (unlikely(reg == SDHCI_HOST_VERSION))
> -               ret = in_be32(host->ioaddr + base) & 0xffff;
> +       if (spec_reg == SDHCI_HOST_VERSION)
> +               ret = value & 0xffff;
>         else
> -               ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff;
> +               ret = (value >> shift) & 0xffff;
>         return ret;
>  }
>
> -static u8 esdhc_readb(struct sdhci_host *host, int reg)
> +static u8 esdhc_readb_fixup(struct esdhc_host *esdhc,
> +                                    int spec_reg, u32 value)
>  {
> -       int base = reg & ~0x3;
> -       int shift = (reg & 0x3) * 8;
> -       u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff;
> +       u8 ret;
> +       u8 dma_bits;
> +       int shift = (spec_reg & 0x3) * 8;
> +
> +       ret = (value >> shift) & 0xff;
>
>         /*
>          * "DMA select" locates at offset 0x28 in SD specification, but on
>          * P5020 or P3041, it locates at 0x29.
>          */
> -       if (reg == SDHCI_HOST_CONTROL) {
> -               u32 dma_bits;
> -
> -               dma_bits = in_be32(host->ioaddr + reg);
> +       if (spec_reg == SDHCI_HOST_CONTROL) {
>                 /* DMA select is 22,23 bits in Protocol Control Register */
> -               dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK;
> -
> +               dma_bits = (value >> 5) & SDHCI_CTRL_DMA_MASK;
>                 /* fixup the result */
>                 ret &= ~SDHCI_CTRL_DMA_MASK;
>                 ret |= dma_bits;
>         }
> -
>         return ret;
>  }
>
> -static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
> +/**
> + * esdhc_write*_fixup - Fixup the SD spec register value so that it could be
> + *                     written into eSDHC register.
> + *
> + * @esdhc: pointer to esdhc_host
> + * @spec_reg: SD spec register address
> + * @value: 8/16/32bit SD spec register value that would be written
> + * @old_value: 32bit eSDHC register value on spec_reg address
> + *
> + * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC
> + * registers are 32 bits. There are differences in register size, register
> + * address, register function, bit position and function between eSDHC spec
> + * and SD spec.
> + *
> + * Return a fixed up register value
> + */
> +static u32 esdhc_writel_fixup(struct esdhc_host *esdhc,
> +                                    int spec_reg, u32 value, u32 old_value)
>  {
> +       u32 ret;
> +
>         /*
> -        * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE]
> -        * when SYSCTL[RSTD]) is set for some special operations.
> -        * No any impact other operation.
> +        * Enabling IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE]
> +        * when SYSCTL[RSTD] is set for some special operations.
> +        * No any impact on other operation.
>          */
> -       if (reg == SDHCI_INT_ENABLE)
> -               val |= SDHCI_INT_BLK_GAP;
> -       sdhci_be32bs_writel(host, val, reg);
> +       if (spec_reg == SDHCI_INT_ENABLE)
> +               ret = value | SDHCI_INT_BLK_GAP;
> +       else
> +               ret = value;
> +
> +       return ret;
>  }
>
> -static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
> +static u32 esdhc_writew_fixup(struct esdhc_host *esdhc,
> +                                    int spec_reg, u16 value, u32 old_value)
>  {
> -       if (reg == SDHCI_BLOCK_SIZE) {
> +       u32 ret;
> +       int shift = (spec_reg & 0x2) * 8;
> +
> +       switch (spec_reg) {
> +       case SDHCI_TRANSFER_MODE:
> +               /*
> +                * Postpone this write, we must do it together with a
> +                * command write that is down below. Return old value.
> +                */
> +               esdhc->xfer_mode_shadow = value;
> +               return old_value;
> +       case SDHCI_COMMAND:
> +               ret = (value << 16) | esdhc->xfer_mode_shadow;
> +               return ret;
> +       }
> +
> +       ret = old_value & (~(0xffff << shift));
> +       ret |= (value << shift);
> +
> +       if (spec_reg == SDHCI_BLOCK_SIZE) {
>                 /*
>                  * Two last DMA bits are reserved, and first one is used for
>                  * non-standard blksz of 4096 bytes that we don't support
>                  * yet. So clear the DMA boundary bits.
>                  */
> -               val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
> +               ret &= (~SDHCI_MAKE_BLKSZ(0x7, 0));
>         }
> -       sdhci_be32bs_writew(host, val, reg);
> +       return ret;
>  }
>
> -static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
> +static u32 esdhc_writeb_fixup(struct esdhc_host *esdhc,
> +                                    int spec_reg, u8 value, u32 old_value)
>  {
> +       u32 ret;
> +       u32 dma_bits;
> +       u8 tmp;
> +       int shift = (spec_reg & 0x3) * 8;
> +
>         /*
>          * "DMA select" location is offset 0x28 in SD specification, but on
>          * P5020 or P3041, it's located at 0x29.
>          */
> -       if (reg == SDHCI_HOST_CONTROL) {
> -               u32 dma_bits;
> -
> +       if (spec_reg == SDHCI_HOST_CONTROL) {
>                 /*
>                  * If host control register is not standard, exit
>                  * this function
>                  */
> -               if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL)
> -                       return;
> +               if (esdhc->host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL)
> +                       return old_value;
>
>                 /* 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);
> -               val &= ~SDHCI_CTRL_DMA_MASK;
> -               val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK;
> +               dma_bits = (value & SDHCI_CTRL_DMA_MASK) << 5;
> +               ret = (old_value & (~(SDHCI_CTRL_DMA_MASK << 5))) | dma_bits;
> +               tmp = (value & (~SDHCI_CTRL_DMA_MASK)) |
> +                     (old_value & SDHCI_CTRL_DMA_MASK);
> +               ret = (ret & (~0xff)) | tmp;
> +
> +               /* Prevent SDHCI core from writing reserved bits (e.g. HISPD) */
> +               ret &= ~ESDHC_HOST_CONTROL_RES;
> +               return ret;
>         }
>
> -       /* 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);
> +       ret = (old_value & (~(0xff << shift))) | (value << shift);
> +       return ret;
> +}
> +
> +static u32 esdhc_be_readl(struct sdhci_host *host, int reg)
> +{
> +       u32 ret;
> +       u32 value;
> +
> +       value = ioread32be(host->ioaddr + reg);
> +       ret = esdhc_readl_fixup(esdhc, reg, value);
> +
> +       return ret;
> +}
> +
> +static u32 esdhc_le_readl(struct sdhci_host *host, int reg)
> +{
> +       u32 ret;
> +       u32 value;
> +
> +       value = ioread32(host->ioaddr + reg);
> +       ret = esdhc_readl_fixup(esdhc, reg, value);
> +
> +       return ret;
> +}
> +
> +static u16 esdhc_be_readw(struct sdhci_host *host, int reg)
> +{
> +       u16 ret;
> +       u32 value;
> +       int base = reg & ~0x3;
> +
> +       value = ioread32be(host->ioaddr + base);
> +       ret = esdhc_readw_fixup(esdhc, reg, value);
> +       return ret;
> +}
> +
> +static u16 esdhc_le_readw(struct sdhci_host *host, int reg)
> +{
> +       u16 ret;
> +       u32 value;
> +       int base = reg & ~0x3;
> +
> +       value = ioread32(host->ioaddr + base);
> +       ret = esdhc_readw_fixup(esdhc, reg, value);
> +       return ret;
> +}
> +
> +static u8 esdhc_be_readb(struct sdhci_host *host, int reg)
> +{
> +       u8 ret;
> +       u32 value;
> +       int base = reg & ~0x3;
> +
> +       value = ioread32be(host->ioaddr + base);
> +       ret = esdhc_readb_fixup(esdhc, reg, value);
> +       return ret;
> +}
> +
> +static u8 esdhc_le_readb(struct sdhci_host *host, int reg)
> +{
> +       u8 ret;
> +       u32 value;
> +       int base = reg & ~0x3;
> +
> +       value = ioread32(host->ioaddr + base);
> +       ret = esdhc_readb_fixup(esdhc, reg, value);
> +       return ret;
> +}
> +
> +static void esdhc_be_writel(struct sdhci_host *host, u32 val, int reg)
> +{
> +       u32 value;
> +
> +       value = esdhc_writel_fixup(esdhc, reg, val, 0);
> +       iowrite32be(value, host->ioaddr + reg);
> +}
> +
> +static void esdhc_le_writel(struct sdhci_host *host, u32 val, int reg)
> +{
> +       u32 value;
> +
> +       value = esdhc_writel_fixup(esdhc, reg, val, 0);
> +       iowrite32(value, host->ioaddr + reg);
> +}
> +
> +static void esdhc_be_writew(struct sdhci_host *host, u16 val, int reg)
> +{
> +       int base = reg & ~0x3;
> +       u32 value;
> +       u32 ret;
> +
> +       value = ioread32be(host->ioaddr + base);
> +       ret = esdhc_writew_fixup(esdhc, reg, val, value);
> +       if (reg != SDHCI_TRANSFER_MODE)
> +               iowrite32be(ret, host->ioaddr + base);
> +}
> +
> +static void esdhc_le_writew(struct sdhci_host *host, u16 val, int reg)
> +{
> +       int base = reg & ~0x3;
> +       u32 value;
> +       u32 ret;
> +
> +       value = ioread32(host->ioaddr + base);
> +       ret = esdhc_writew_fixup(esdhc, reg, val, value);
> +       if (reg != SDHCI_TRANSFER_MODE)
> +               iowrite32(ret, host->ioaddr + base);
> +}
> +
> +static void esdhc_be_writeb(struct sdhci_host *host, u8 val, int reg)
> +{
> +       int base = reg & ~0x3;
> +       u32 value;
> +       u32 ret;
> +
> +       value = ioread32be(host->ioaddr + base);
> +       ret = esdhc_writeb_fixup(esdhc, reg, val, value);
> +       iowrite32be(ret, host->ioaddr + base);
> +}
> +
> +static void esdhc_le_writeb(struct sdhci_host *host, u8 val, int reg)
> +{
> +       int base = reg & ~0x3;
> +       u32 value;
> +       u32 ret;
> +
> +       value = ioread32(host->ioaddr + base);
> +       ret = esdhc_writeb_fixup(esdhc, reg, val, value);
> +       iowrite32(ret, host->ioaddr + base);
>  }
>
>  /*
> @@ -149,19 +347,15 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
>   * For Continue, apply soft reset for data(SYSCTL[RSTD]);
>   * and re-issue the entire read transaction from beginning.
>   */
> -static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask)
> +static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask)
>  {
> -       u32 tmp;
>         bool applicable;
>         dma_addr_t dmastart;
>         dma_addr_t dmanow;
>
> -       tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
> -       tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
> -
>         applicable = (intmask & SDHCI_INT_DATA_END) &&
> -               (intmask & SDHCI_INT_BLK_GAP) &&
> -               (tmp == VENDOR_V_23);
> +                    (intmask & SDHCI_INT_BLK_GAP) &&
> +                    (esdhc->vendor_ver == VENDOR_V_23);
>         if (!applicable)
>                 return;
>
> @@ -179,7 +373,11 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask)
>
>  static int esdhc_of_enable_dma(struct sdhci_host *host)
>  {
> -       setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
> +       u32 value;
> +
> +       value = sdhci_readl(host, ESDHC_DMA_SYSCTL);
> +       value |= ESDHC_DMA_SNOOP;
> +       sdhci_writel(host, value, ESDHC_DMA_SYSCTL);
>         return 0;
>  }
>
> @@ -209,9 +407,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
>                 return;
>
>         /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
> -       temp = esdhc_readw(host, SDHCI_HOST_VERSION);
> -       temp = (temp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
> -       if (temp < VENDOR_V_23)
> +       if (esdhc->vendor_ver < VENDOR_V_23)
>                 pre_div = 2;
>
>         /* Workaround to reduce the clock frequency for p1010 esdhc */
> @@ -247,39 +443,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
>         mdelay(1);
>  }
>
> -static void esdhc_of_platform_init(struct sdhci_host *host)
> -{
> -       u32 vvn;
> -
> -       vvn = in_be32(host->ioaddr + 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;
> -
> -       if (vvn > VENDOR_V_22)
> -               host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
> -}
> -
>  static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
>  {
>         u32 ctrl;
>
> +       ctrl = sdhci_readl(host, ESDHC_PROCTL);
> +       ctrl &= (~ESDHC_CTRL_BUSWIDTH_MASK);
>         switch (width) {
>         case MMC_BUS_WIDTH_8:
> -               ctrl = ESDHC_CTRL_8BITBUS;
> +               ctrl |= ESDHC_CTRL_8BITBUS;
>                 break;
>
>         case MMC_BUS_WIDTH_4:
> -               ctrl = ESDHC_CTRL_4BITBUS;
> +               ctrl |= ESDHC_CTRL_4BITBUS;
>                 break;
>
>         default:
> -               ctrl = 0;
>                 break;
>         }
>
> -       clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL,
> -                       ESDHC_CTRL_BUSWIDTH_MASK, ctrl);
> +       sdhci_writel(host, ctrl, ESDHC_PROCTL);
>  }
>
>  static void esdhc_reset(struct sdhci_host *host, u8 mask)
> @@ -290,32 +473,13 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
>         sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
>  }
>
> -static const struct sdhci_ops sdhci_esdhc_ops = {
> -       .read_l = esdhc_readl,
> -       .read_w = esdhc_readw,
> -       .read_b = esdhc_readb,
> -       .write_l = esdhc_writel,
> -       .write_w = esdhc_writew,
> -       .write_b = esdhc_writeb,
> -       .set_clock = esdhc_of_set_clock,
> -       .enable_dma = esdhc_of_enable_dma,
> -       .get_max_clock = esdhc_of_get_max_clock,
> -       .get_min_clock = esdhc_of_get_min_clock,
> -       .platform_init = esdhc_of_platform_init,
> -       .adma_workaround = esdhci_of_adma_workaround,
> -       .set_bus_width = esdhc_pltfm_set_bus_width,
> -       .reset = esdhc_reset,
> -       .set_uhs_signaling = sdhci_set_uhs_signaling,
> -};
> -
>  #ifdef CONFIG_PM
> -
>  static u32 esdhc_proctl;
>  static int esdhc_of_suspend(struct device *dev)
>  {
>         struct sdhci_host *host = dev_get_drvdata(dev);
>
> -       esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL);
> +       esdhc_proctl = sdhci_readl(host, SDHCI_HOST_CONTROL);
>
>         return sdhci_suspend_host(host);
>  }
> @@ -328,9 +492,8 @@ static int esdhc_of_resume(struct device *dev)
>         if (ret == 0) {
>                 /* Isn't this already done by sdhci_resume_host() ? --rmk */
>                 esdhc_of_enable_dma(host);
> -               sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
> +               sdhci_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
>         }
> -
>         return ret;
>  }
>
> @@ -343,16 +506,56 @@ static const struct dev_pm_ops esdhc_pmops = {
>  #define ESDHC_PMOPS NULL
>  #endif
>
> -static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
> -       /*
> -        * card detection could be handled via GPIO
> -        * eSDHC cannot support End Attribute in NOP ADMA descriptor
> -        */
> -       .quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
> -               | SDHCI_QUIRK_NO_CARD_NO_RESET
> -               | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
> -       .ops = &sdhci_esdhc_ops,
> +static struct sdhci_ops sdhci_esdhc_ops = {
> +       .set_clock = esdhc_of_set_clock,
> +       .enable_dma = esdhc_of_enable_dma,
> +       .get_max_clock = esdhc_of_get_max_clock,
> +       .get_min_clock = esdhc_of_get_min_clock,
> +       .adma_workaround = esdhc_of_adma_workaround,
> +       .set_bus_width = esdhc_pltfm_set_bus_width,
> +       .reset = esdhc_reset,
> +       .set_uhs_signaling = sdhci_set_uhs_signaling,
>  };
> +static struct sdhci_pltfm_data sdhci_esdhc_pdata;
> +
> +static void esdhc_setup_pltfm_data(struct platform_device *pdev)
> +{
> +       struct device_node *np;
> +
> +       np = pdev->dev.of_node;
> +       if (of_get_property(np, "little-endian", NULL)) {
> +               sdhci_esdhc_ops.read_l  = esdhc_le_readl;
> +               sdhci_esdhc_ops.read_w  = esdhc_le_readw;
> +               sdhci_esdhc_ops.read_b  = esdhc_le_readb;
> +               sdhci_esdhc_ops.write_l = esdhc_le_writel;
> +               sdhci_esdhc_ops.write_w = esdhc_le_writew;
> +               sdhci_esdhc_ops.write_b = esdhc_le_writeb;
> +       } else {
> +               sdhci_esdhc_ops.read_l  = esdhc_be_readl;
> +               sdhci_esdhc_ops.read_w  = esdhc_be_readw;
> +               sdhci_esdhc_ops.read_b  = esdhc_be_readb;
> +               sdhci_esdhc_ops.write_l = esdhc_be_writel;
> +               sdhci_esdhc_ops.write_w = esdhc_be_writew;
> +               sdhci_esdhc_ops.write_b = esdhc_be_writeb;
> +       }
> +
> +       sdhci_esdhc_pdata.quirks = ESDHC_DEFAULT_QUIRKS |
> +                                  SDHCI_QUIRK_BROKEN_CARD_DETECTION |
> +                                  SDHCI_QUIRK_NO_CARD_NO_RESET |
> +                                  SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
> +       sdhci_esdhc_pdata.ops = &sdhci_esdhc_ops;
> +}
> +
> +static void esdhc_init(struct esdhc_host *esdhc, struct sdhci_host *host)
> +{
> +       u16 host_ver;
> +
> +       host_ver = sdhci_readw(host, SDHCI_HOST_VERSION);
> +       esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >>
> +                            SDHCI_VENDOR_VER_SHIFT;
> +       esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK;
> +       esdhc->host = host;
> +}
>
>  static int sdhci_esdhc_probe(struct platform_device *pdev)
>  {
> @@ -360,10 +563,15 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
>         struct device_node *np;
>         int ret;
>
> +       esdhc_setup_pltfm_data(pdev);
> +
>         host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0);
>         if (IS_ERR(host))
>                 return PTR_ERR(host);
>
> +       esdhc = kmalloc(sizeof(struct esdhc_host), GFP_KERNEL);

I think it's better to use devm_kzalloc(), thus you don't have to take
care of freeing data since that's handled automatically for the devm
APIs.

> +       esdhc_init(esdhc, host);
> +
>         sdhci_get_of_property(pdev);
>
>         np = pdev->dev.of_node;
> --
> 2.1.0.27.g96db324
>

Kind regards
Uffe
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arnd Bergmann Sept. 29, 2015, 12:25 p.m. UTC | #2
On Monday 28 September 2015 14:35:13 Yangbo Lu wrote:
> +struct esdhc_host {
> +	u8 vendor_ver;
> +	u8 spec_ver;
> +	u16 xfer_mode_shadow;
> +	struct sdhci_host *host;
> +};
> +
> +static struct esdhc_host *esdhc;

Please avoid global variables like this one and keep the state
within the per-device structure.

>  
> -static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
> -	/*
> -	 * card detection could be handled via GPIO
> -	 * eSDHC cannot support End Attribute in NOP ADMA descriptor
> -	 */
> -	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
> -		| SDHCI_QUIRK_NO_CARD_NO_RESET
> -		| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
> -	.ops = &sdhci_esdhc_ops,
> +static struct sdhci_ops sdhci_esdhc_ops = {
> +	.set_clock = esdhc_of_set_clock,
> +	.enable_dma = esdhc_of_enable_dma,
> +	.get_max_clock = esdhc_of_get_max_clock,
> +	.get_min_clock = esdhc_of_get_min_clock,
> +	.adma_workaround = esdhc_of_adma_workaround,
> +	.set_bus_width = esdhc_pltfm_set_bus_width,
> +	.reset = esdhc_reset,
> +	.set_uhs_signaling = sdhci_set_uhs_signaling,
>  };
> +static struct sdhci_pltfm_data sdhci_esdhc_pdata;
> +
> +static void esdhc_setup_pltfm_data(struct platform_device *pdev)
> +{
> +	struct device_node *np;
> +
> +	np = pdev->dev.of_node;
> +	if (of_get_property(np, "little-endian", NULL)) {
> +		sdhci_esdhc_ops.read_l	= esdhc_le_readl;
> +		sdhci_esdhc_ops.read_w	= esdhc_le_readw;
> +		sdhci_esdhc_ops.read_b	= esdhc_le_readb;
> +		sdhci_esdhc_ops.write_l	= esdhc_le_writel;
> +		sdhci_esdhc_ops.write_w	= esdhc_le_writew;
> +		sdhci_esdhc_ops.write_b	= esdhc_le_writeb;
> +	} else {
> +		sdhci_esdhc_ops.read_l	= esdhc_be_readl;
> +		sdhci_esdhc_ops.read_w	= esdhc_be_readw;
> +		sdhci_esdhc_ops.read_b	= esdhc_be_readb;
> +		sdhci_esdhc_ops.write_l	= esdhc_be_writel;
> +		sdhci_esdhc_ops.write_w	= esdhc_be_writew;
> +		sdhci_esdhc_ops.write_b	= esdhc_be_writeb;
> +	}

Just define two instances of the sdhci_esdhc_ops structure and
pick one or the other pointer, rather changing the members individually.
That makes it easier to read and gives smaller code. Similarly, use
two instances of sdhci_pltfm_data.

Ideally also mark the structure as 'const', as we normally do for
structures with callback pointers. (this might require a larger
cleanup though).

>  static int sdhci_esdhc_probe(struct platform_device *pdev)
>  {
> @@ -360,10 +563,15 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
>  	struct device_node *np;
>  	int ret;
>  
> +	esdhc_setup_pltfm_data(pdev);
> +
>  	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0);
>  	if (IS_ERR(host))
>  		return PTR_ERR(host);
>  
> +	esdhc = kmalloc(sizeof(struct esdhc_host), GFP_KERNEL);
> +	esdhc_init(esdhc, host);
> +

You can combine these, by passing sizeof(struct esdhc_host) as the 'priv'
argument of the sdhci_pltfm_init function.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h
index 163ac99..de132e2 100644
--- a/drivers/mmc/host/sdhci-esdhc.h
+++ b/drivers/mmc/host/sdhci-esdhc.h
@@ -24,6 +24,8 @@ 
 				SDHCI_QUIRK_PIO_NEEDS_DELAY | \
 				SDHCI_QUIRK_NO_HISPD_BIT)
 
+#define ESDHC_PROCTL		0x28
+
 #define ESDHC_SYSTEM_CONTROL	0x2c
 #define ESDHC_CLOCK_MASK	0x0000fff0
 #define ESDHC_PREDIV_SHIFT	8
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 2a54dbe..5315864 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -18,128 +18,326 @@ 
 #include <linux/of.h>
 #include <linux/delay.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/mmc/host.h>
 #include "sdhci-pltfm.h"
 #include "sdhci-esdhc.h"
 
 #define VENDOR_V_22	0x12
 #define VENDOR_V_23	0x13
-static u32 esdhc_readl(struct sdhci_host *host, int reg)
+
+struct esdhc_host {
+	u8 vendor_ver;
+	u8 spec_ver;
+	u16 xfer_mode_shadow;
+	struct sdhci_host *host;
+};
+
+static struct esdhc_host *esdhc;
+
+/**
+ * esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register
+ *		       to make it compatible with SD spec.
+ *
+ * @esdhc: pointer to esdhc_host
+ * @spec_reg: SD spec register address
+ * @value: 32bit eSDHC register value on spec_reg address
+ *
+ * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC
+ * registers are 32 bits. There are differences in register size, register
+ * address, register function, bit position and function between eSDHC spec
+ * and SD spec.
+ *
+ * Return a fixed up register value
+ */
+static u32 esdhc_readl_fixup(struct esdhc_host *esdhc,
+				     int spec_reg, u32 value)
 {
 	u32 ret;
 
-	ret = in_be32(host->ioaddr + 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
 	 * supported by eSDHC.
 	 * And for many FSL eSDHC controller, the reset value of field
-	 * SDHCI_CAN_DO_ADMA1 is one, but some of them can't support ADMA,
+	 * SDHCI_CAN_DO_ADMA1 is 1, but some of them can't support ADMA,
 	 * only these vendor version is greater than 2.2/0x12 support ADMA.
-	 * For FSL eSDHC, must aligned 4-byte, so use 0xFC to read the
-	 * 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);
-		tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
-		if (tmp > VENDOR_V_22)
-			ret |= SDHCI_CAN_DO_ADMA2;
+	if ((spec_reg == SDHCI_CAPABILITIES) && (value & SDHCI_CAN_DO_ADMA1)) {
+		if (esdhc->vendor_ver > VENDOR_V_22) {
+			ret = value | SDHCI_CAN_DO_ADMA2;
+			return ret;
+		}
 	}
-
+	ret = value;
 	return ret;
 }
 
-static u16 esdhc_readw(struct sdhci_host *host, int reg)
+static u16 esdhc_readw_fixup(struct esdhc_host *esdhc,
+				     int spec_reg, u32 value)
 {
 	u16 ret;
-	int base = reg & ~0x3;
-	int shift = (reg & 0x2) * 8;
+	int shift = (spec_reg & 0x2) * 8;
 
-	if (unlikely(reg == SDHCI_HOST_VERSION))
-		ret = in_be32(host->ioaddr + base) & 0xffff;
+	if (spec_reg == SDHCI_HOST_VERSION)
+		ret = value & 0xffff;
 	else
-		ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff;
+		ret = (value >> shift) & 0xffff;
 	return ret;
 }
 
-static u8 esdhc_readb(struct sdhci_host *host, int reg)
+static u8 esdhc_readb_fixup(struct esdhc_host *esdhc,
+				     int spec_reg, u32 value)
 {
-	int base = reg & ~0x3;
-	int shift = (reg & 0x3) * 8;
-	u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff;
+	u8 ret;
+	u8 dma_bits;
+	int shift = (spec_reg & 0x3) * 8;
+
+	ret = (value >> shift) & 0xff;
 
 	/*
 	 * "DMA select" locates at offset 0x28 in SD specification, but on
 	 * P5020 or P3041, it locates at 0x29.
 	 */
-	if (reg == SDHCI_HOST_CONTROL) {
-		u32 dma_bits;
-
-		dma_bits = in_be32(host->ioaddr + reg);
+	if (spec_reg == SDHCI_HOST_CONTROL) {
 		/* DMA select is 22,23 bits in Protocol Control Register */
-		dma_bits = (dma_bits >> 5) & SDHCI_CTRL_DMA_MASK;
-
+		dma_bits = (value >> 5) & SDHCI_CTRL_DMA_MASK;
 		/* fixup the result */
 		ret &= ~SDHCI_CTRL_DMA_MASK;
 		ret |= dma_bits;
 	}
-
 	return ret;
 }
 
-static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
+/**
+ * esdhc_write*_fixup - Fixup the SD spec register value so that it could be
+ *			written into eSDHC register.
+ *
+ * @esdhc: pointer to esdhc_host
+ * @spec_reg: SD spec register address
+ * @value: 8/16/32bit SD spec register value that would be written
+ * @old_value: 32bit eSDHC register value on spec_reg address
+ *
+ * In SD spec, there are 8/16/32/64 bits registers, while all of eSDHC
+ * registers are 32 bits. There are differences in register size, register
+ * address, register function, bit position and function between eSDHC spec
+ * and SD spec.
+ *
+ * Return a fixed up register value
+ */
+static u32 esdhc_writel_fixup(struct esdhc_host *esdhc,
+				     int spec_reg, u32 value, u32 old_value)
 {
+	u32 ret;
+
 	/*
-	 * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE]
-	 * when SYSCTL[RSTD]) is set for some special operations.
-	 * No any impact other operation.
+	 * Enabling IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE]
+	 * when SYSCTL[RSTD] is set for some special operations.
+	 * No any impact on other operation.
 	 */
-	if (reg == SDHCI_INT_ENABLE)
-		val |= SDHCI_INT_BLK_GAP;
-	sdhci_be32bs_writel(host, val, reg);
+	if (spec_reg == SDHCI_INT_ENABLE)
+		ret = value | SDHCI_INT_BLK_GAP;
+	else
+		ret = value;
+
+	return ret;
 }
 
-static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
+static u32 esdhc_writew_fixup(struct esdhc_host *esdhc,
+				     int spec_reg, u16 value, u32 old_value)
 {
-	if (reg == SDHCI_BLOCK_SIZE) {
+	u32 ret;
+	int shift = (spec_reg & 0x2) * 8;
+
+	switch (spec_reg) {
+	case SDHCI_TRANSFER_MODE:
+		/*
+		 * Postpone this write, we must do it together with a
+		 * command write that is down below. Return old value.
+		 */
+		esdhc->xfer_mode_shadow = value;
+		return old_value;
+	case SDHCI_COMMAND:
+		ret = (value << 16) | esdhc->xfer_mode_shadow;
+		return ret;
+	}
+
+	ret = old_value & (~(0xffff << shift));
+	ret |= (value << shift);
+
+	if (spec_reg == SDHCI_BLOCK_SIZE) {
 		/*
 		 * Two last DMA bits are reserved, and first one is used for
 		 * non-standard blksz of 4096 bytes that we don't support
 		 * yet. So clear the DMA boundary bits.
 		 */
-		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
+		ret &= (~SDHCI_MAKE_BLKSZ(0x7, 0));
 	}
-	sdhci_be32bs_writew(host, val, reg);
+	return ret;
 }
 
-static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
+static u32 esdhc_writeb_fixup(struct esdhc_host *esdhc,
+				     int spec_reg, u8 value, u32 old_value)
 {
+	u32 ret;
+	u32 dma_bits;
+	u8 tmp;
+	int shift = (spec_reg & 0x3) * 8;
+
 	/*
 	 * "DMA select" location is offset 0x28 in SD specification, but on
 	 * P5020 or P3041, it's located at 0x29.
 	 */
-	if (reg == SDHCI_HOST_CONTROL) {
-		u32 dma_bits;
-
+	if (spec_reg == SDHCI_HOST_CONTROL) {
 		/*
 		 * If host control register is not standard, exit
 		 * this function
 		 */
-		if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL)
-			return;
+		if (esdhc->host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL)
+			return old_value;
 
 		/* 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);
-		val &= ~SDHCI_CTRL_DMA_MASK;
-		val |= in_be32(host->ioaddr + reg) & SDHCI_CTRL_DMA_MASK;
+		dma_bits = (value & SDHCI_CTRL_DMA_MASK) << 5;
+		ret = (old_value & (~(SDHCI_CTRL_DMA_MASK << 5))) | dma_bits;
+		tmp = (value & (~SDHCI_CTRL_DMA_MASK)) |
+		      (old_value & SDHCI_CTRL_DMA_MASK);
+		ret = (ret & (~0xff)) | tmp;
+
+		/* Prevent SDHCI core from writing reserved bits (e.g. HISPD) */
+		ret &= ~ESDHC_HOST_CONTROL_RES;
+		return ret;
 	}
 
-	/* 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);
+	ret = (old_value & (~(0xff << shift))) | (value << shift);
+	return ret;
+}
+
+static u32 esdhc_be_readl(struct sdhci_host *host, int reg)
+{
+	u32 ret;
+	u32 value;
+
+	value = ioread32be(host->ioaddr + reg);
+	ret = esdhc_readl_fixup(esdhc, reg, value);
+
+	return ret;
+}
+
+static u32 esdhc_le_readl(struct sdhci_host *host, int reg)
+{
+	u32 ret;
+	u32 value;
+
+	value = ioread32(host->ioaddr + reg);
+	ret = esdhc_readl_fixup(esdhc, reg, value);
+
+	return ret;
+}
+
+static u16 esdhc_be_readw(struct sdhci_host *host, int reg)
+{
+	u16 ret;
+	u32 value;
+	int base = reg & ~0x3;
+
+	value = ioread32be(host->ioaddr + base);
+	ret = esdhc_readw_fixup(esdhc, reg, value);
+	return ret;
+}
+
+static u16 esdhc_le_readw(struct sdhci_host *host, int reg)
+{
+	u16 ret;
+	u32 value;
+	int base = reg & ~0x3;
+
+	value = ioread32(host->ioaddr + base);
+	ret = esdhc_readw_fixup(esdhc, reg, value);
+	return ret;
+}
+
+static u8 esdhc_be_readb(struct sdhci_host *host, int reg)
+{
+	u8 ret;
+	u32 value;
+	int base = reg & ~0x3;
+
+	value = ioread32be(host->ioaddr + base);
+	ret = esdhc_readb_fixup(esdhc, reg, value);
+	return ret;
+}
+
+static u8 esdhc_le_readb(struct sdhci_host *host, int reg)
+{
+	u8 ret;
+	u32 value;
+	int base = reg & ~0x3;
+
+	value = ioread32(host->ioaddr + base);
+	ret = esdhc_readb_fixup(esdhc, reg, value);
+	return ret;
+}
+
+static void esdhc_be_writel(struct sdhci_host *host, u32 val, int reg)
+{
+	u32 value;
+
+	value = esdhc_writel_fixup(esdhc, reg, val, 0);
+	iowrite32be(value, host->ioaddr + reg);
+}
+
+static void esdhc_le_writel(struct sdhci_host *host, u32 val, int reg)
+{
+	u32 value;
+
+	value = esdhc_writel_fixup(esdhc, reg, val, 0);
+	iowrite32(value, host->ioaddr + reg);
+}
+
+static void esdhc_be_writew(struct sdhci_host *host, u16 val, int reg)
+{
+	int base = reg & ~0x3;
+	u32 value;
+	u32 ret;
+
+	value = ioread32be(host->ioaddr + base);
+	ret = esdhc_writew_fixup(esdhc, reg, val, value);
+	if (reg != SDHCI_TRANSFER_MODE)
+		iowrite32be(ret, host->ioaddr + base);
+}
+
+static void esdhc_le_writew(struct sdhci_host *host, u16 val, int reg)
+{
+	int base = reg & ~0x3;
+	u32 value;
+	u32 ret;
+
+	value = ioread32(host->ioaddr + base);
+	ret = esdhc_writew_fixup(esdhc, reg, val, value);
+	if (reg != SDHCI_TRANSFER_MODE)
+		iowrite32(ret, host->ioaddr + base);
+}
+
+static void esdhc_be_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	int base = reg & ~0x3;
+	u32 value;
+	u32 ret;
+
+	value = ioread32be(host->ioaddr + base);
+	ret = esdhc_writeb_fixup(esdhc, reg, val, value);
+	iowrite32be(ret, host->ioaddr + base);
+}
+
+static void esdhc_le_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+	int base = reg & ~0x3;
+	u32 value;
+	u32 ret;
+
+	value = ioread32(host->ioaddr + base);
+	ret = esdhc_writeb_fixup(esdhc, reg, val, value);
+	iowrite32(ret, host->ioaddr + base);
 }
 
 /*
@@ -149,19 +347,15 @@  static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
  * For Continue, apply soft reset for data(SYSCTL[RSTD]);
  * and re-issue the entire read transaction from beginning.
  */
-static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask)
+static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask)
 {
-	u32 tmp;
 	bool applicable;
 	dma_addr_t dmastart;
 	dma_addr_t dmanow;
 
-	tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS);
-	tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
-
 	applicable = (intmask & SDHCI_INT_DATA_END) &&
-		(intmask & SDHCI_INT_BLK_GAP) &&
-		(tmp == VENDOR_V_23);
+		     (intmask & SDHCI_INT_BLK_GAP) &&
+		     (esdhc->vendor_ver == VENDOR_V_23);
 	if (!applicable)
 		return;
 
@@ -179,7 +373,11 @@  static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask)
 
 static int esdhc_of_enable_dma(struct sdhci_host *host)
 {
-	setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
+	u32 value;
+
+	value = sdhci_readl(host, ESDHC_DMA_SYSCTL);
+	value |= ESDHC_DMA_SNOOP;
+	sdhci_writel(host, value, ESDHC_DMA_SYSCTL);
 	return 0;
 }
 
@@ -209,9 +407,7 @@  static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
 		return;
 
 	/* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
-	temp = esdhc_readw(host, SDHCI_HOST_VERSION);
-	temp = (temp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
-	if (temp < VENDOR_V_23)
+	if (esdhc->vendor_ver < VENDOR_V_23)
 		pre_div = 2;
 
 	/* Workaround to reduce the clock frequency for p1010 esdhc */
@@ -247,39 +443,26 @@  static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
 	mdelay(1);
 }
 
-static void esdhc_of_platform_init(struct sdhci_host *host)
-{
-	u32 vvn;
-
-	vvn = in_be32(host->ioaddr + 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;
-
-	if (vvn > VENDOR_V_22)
-		host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
-}
-
 static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
 {
 	u32 ctrl;
 
+	ctrl = sdhci_readl(host, ESDHC_PROCTL);
+	ctrl &= (~ESDHC_CTRL_BUSWIDTH_MASK);
 	switch (width) {
 	case MMC_BUS_WIDTH_8:
-		ctrl = ESDHC_CTRL_8BITBUS;
+		ctrl |= ESDHC_CTRL_8BITBUS;
 		break;
 
 	case MMC_BUS_WIDTH_4:
-		ctrl = ESDHC_CTRL_4BITBUS;
+		ctrl |= ESDHC_CTRL_4BITBUS;
 		break;
 
 	default:
-		ctrl = 0;
 		break;
 	}
 
-	clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL,
-			ESDHC_CTRL_BUSWIDTH_MASK, ctrl);
+	sdhci_writel(host, ctrl, ESDHC_PROCTL);
 }
 
 static void esdhc_reset(struct sdhci_host *host, u8 mask)
@@ -290,32 +473,13 @@  static void esdhc_reset(struct sdhci_host *host, u8 mask)
 	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 }
 
-static const struct sdhci_ops sdhci_esdhc_ops = {
-	.read_l = esdhc_readl,
-	.read_w = esdhc_readw,
-	.read_b = esdhc_readb,
-	.write_l = esdhc_writel,
-	.write_w = esdhc_writew,
-	.write_b = esdhc_writeb,
-	.set_clock = esdhc_of_set_clock,
-	.enable_dma = esdhc_of_enable_dma,
-	.get_max_clock = esdhc_of_get_max_clock,
-	.get_min_clock = esdhc_of_get_min_clock,
-	.platform_init = esdhc_of_platform_init,
-	.adma_workaround = esdhci_of_adma_workaround,
-	.set_bus_width = esdhc_pltfm_set_bus_width,
-	.reset = esdhc_reset,
-	.set_uhs_signaling = sdhci_set_uhs_signaling,
-};
-
 #ifdef CONFIG_PM
-
 static u32 esdhc_proctl;
 static int esdhc_of_suspend(struct device *dev)
 {
 	struct sdhci_host *host = dev_get_drvdata(dev);
 
-	esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL);
+	esdhc_proctl = sdhci_readl(host, SDHCI_HOST_CONTROL);
 
 	return sdhci_suspend_host(host);
 }
@@ -328,9 +492,8 @@  static int esdhc_of_resume(struct device *dev)
 	if (ret == 0) {
 		/* Isn't this already done by sdhci_resume_host() ? --rmk */
 		esdhc_of_enable_dma(host);
-		sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
+		sdhci_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
 	}
-
 	return ret;
 }
 
@@ -343,16 +506,56 @@  static const struct dev_pm_ops esdhc_pmops = {
 #define ESDHC_PMOPS NULL
 #endif
 
-static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
-	/*
-	 * card detection could be handled via GPIO
-	 * eSDHC cannot support End Attribute in NOP ADMA descriptor
-	 */
-	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
-		| SDHCI_QUIRK_NO_CARD_NO_RESET
-		| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
-	.ops = &sdhci_esdhc_ops,
+static struct sdhci_ops sdhci_esdhc_ops = {
+	.set_clock = esdhc_of_set_clock,
+	.enable_dma = esdhc_of_enable_dma,
+	.get_max_clock = esdhc_of_get_max_clock,
+	.get_min_clock = esdhc_of_get_min_clock,
+	.adma_workaround = esdhc_of_adma_workaround,
+	.set_bus_width = esdhc_pltfm_set_bus_width,
+	.reset = esdhc_reset,
+	.set_uhs_signaling = sdhci_set_uhs_signaling,
 };
+static struct sdhci_pltfm_data sdhci_esdhc_pdata;
+
+static void esdhc_setup_pltfm_data(struct platform_device *pdev)
+{
+	struct device_node *np;
+
+	np = pdev->dev.of_node;
+	if (of_get_property(np, "little-endian", NULL)) {
+		sdhci_esdhc_ops.read_l	= esdhc_le_readl;
+		sdhci_esdhc_ops.read_w	= esdhc_le_readw;
+		sdhci_esdhc_ops.read_b	= esdhc_le_readb;
+		sdhci_esdhc_ops.write_l	= esdhc_le_writel;
+		sdhci_esdhc_ops.write_w	= esdhc_le_writew;
+		sdhci_esdhc_ops.write_b	= esdhc_le_writeb;
+	} else {
+		sdhci_esdhc_ops.read_l	= esdhc_be_readl;
+		sdhci_esdhc_ops.read_w	= esdhc_be_readw;
+		sdhci_esdhc_ops.read_b	= esdhc_be_readb;
+		sdhci_esdhc_ops.write_l	= esdhc_be_writel;
+		sdhci_esdhc_ops.write_w	= esdhc_be_writew;
+		sdhci_esdhc_ops.write_b	= esdhc_be_writeb;
+	}
+
+	sdhci_esdhc_pdata.quirks = ESDHC_DEFAULT_QUIRKS |
+				   SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+				   SDHCI_QUIRK_NO_CARD_NO_RESET |
+				   SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
+	sdhci_esdhc_pdata.ops = &sdhci_esdhc_ops;
+}
+
+static void esdhc_init(struct esdhc_host *esdhc, struct sdhci_host *host)
+{
+	u16 host_ver;
+
+	host_ver = sdhci_readw(host, SDHCI_HOST_VERSION);
+	esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >>
+			     SDHCI_VENDOR_VER_SHIFT;
+	esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK;
+	esdhc->host = host;
+}
 
 static int sdhci_esdhc_probe(struct platform_device *pdev)
 {
@@ -360,10 +563,15 @@  static int sdhci_esdhc_probe(struct platform_device *pdev)
 	struct device_node *np;
 	int ret;
 
+	esdhc_setup_pltfm_data(pdev);
+
 	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0);
 	if (IS_ERR(host))
 		return PTR_ERR(host);
 
+	esdhc = kmalloc(sizeof(struct esdhc_host), GFP_KERNEL);
+	esdhc_init(esdhc, host);
+
 	sdhci_get_of_property(pdev);
 
 	np = pdev->dev.of_node;