Commit f4932cfd authored by yangbo lu's avatar yangbo lu Committed by Ulf Hansson

mmc: sdhci-of-esdhc: support both BE and LE host controller

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: default avatarYangbo Lu <yangbo.lu@freescale.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent e38eeca5
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
SDHCI_QUIRK_PIO_NEEDS_DELAY | \ SDHCI_QUIRK_PIO_NEEDS_DELAY | \
SDHCI_QUIRK_NO_HISPD_BIT) SDHCI_QUIRK_NO_HISPD_BIT)
#define ESDHC_PROCTL 0x28
#define ESDHC_SYSTEM_CONTROL 0x2c #define ESDHC_SYSTEM_CONTROL 0x2c
#define ESDHC_CLOCK_MASK 0x0000fff0 #define ESDHC_CLOCK_MASK 0x0000fff0
#define ESDHC_PREDIV_SHIFT 8 #define ESDHC_PREDIV_SHIFT 8
......
...@@ -24,122 +24,318 @@ ...@@ -24,122 +24,318 @@
#define VENDOR_V_22 0x12 #define VENDOR_V_22 0x12
#define VENDOR_V_23 0x13 #define VENDOR_V_23 0x13
static u32 esdhc_readl(struct sdhci_host *host, int reg)
struct sdhci_esdhc {
u8 vendor_ver;
u8 spec_ver;
};
/**
* esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register
* to make it compatible with SD spec.
*
* @host: pointer to sdhci_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 sdhci_host *host,
int spec_reg, u32 value)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_esdhc *esdhc = pltfm_host->priv;
u32 ret; u32 ret;
ret = in_be32(host->ioaddr + reg);
/* /*
* The bit of ADMA flag in eSDHC is not compatible with standard * 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 * SDHC register, so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is
* supported by eSDHC. * supported by eSDHC.
* And for many FSL eSDHC controller, the reset value of field * 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. * 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)) { if ((spec_reg == SDHCI_CAPABILITIES) && (value & SDHCI_CAN_DO_ADMA1)) {
u32 tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); if (esdhc->vendor_ver > VENDOR_V_22) {
tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; ret = value | SDHCI_CAN_DO_ADMA2;
if (tmp > VENDOR_V_22) return ret;
ret |= SDHCI_CAN_DO_ADMA2; }
} }
ret = value;
return ret; return ret;
} }
static u16 esdhc_readw(struct sdhci_host *host, int reg) static u16 esdhc_readw_fixup(struct sdhci_host *host,
int spec_reg, u32 value)
{ {
u16 ret; u16 ret;
int base = reg & ~0x3; int shift = (spec_reg & 0x2) * 8;
int shift = (reg & 0x2) * 8;
if (unlikely(reg == SDHCI_HOST_VERSION)) if (spec_reg == SDHCI_HOST_VERSION)
ret = in_be32(host->ioaddr + base) & 0xffff; ret = value & 0xffff;
else else
ret = (in_be32(host->ioaddr + base) >> shift) & 0xffff; ret = (value >> shift) & 0xffff;
return ret; return ret;
} }
static u8 esdhc_readb(struct sdhci_host *host, int reg) static u8 esdhc_readb_fixup(struct sdhci_host *host,
int spec_reg, u32 value)
{ {
int base = reg & ~0x3; u8 ret;
int shift = (reg & 0x3) * 8; u8 dma_bits;
u8 ret = (in_be32(host->ioaddr + base) >> shift) & 0xff; int shift = (spec_reg & 0x3) * 8;
ret = (value >> shift) & 0xff;
/* /*
* "DMA select" locates at offset 0x28 in SD specification, but on * "DMA select" locates at offset 0x28 in SD specification, but on
* P5020 or P3041, it locates at 0x29. * P5020 or P3041, it locates at 0x29.
*/ */
if (reg == SDHCI_HOST_CONTROL) { if (spec_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 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 */ /* fixup the result */
ret &= ~SDHCI_CTRL_DMA_MASK; ret &= ~SDHCI_CTRL_DMA_MASK;
ret |= dma_bits; ret |= dma_bits;
} }
return ret; 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.
*
* @host: pointer to sdhci_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 sdhci_host *host,
int spec_reg, u32 value, u32 old_value)
{ {
u32 ret;
/* /*
* Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] * Enabling IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE]
* when SYSCTL[RSTD]) is set for some special operations. * when SYSCTL[RSTD] is set for some special operations.
* No any impact other operation. * No any impact on other operation.
*/ */
if (reg == SDHCI_INT_ENABLE) if (spec_reg == SDHCI_INT_ENABLE)
val |= SDHCI_INT_BLK_GAP; ret = value | SDHCI_INT_BLK_GAP;
sdhci_be32bs_writel(host, val, reg); else
ret = value;
return ret;
} }
static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) static u32 esdhc_writew_fixup(struct sdhci_host *host,
int spec_reg, u16 value, u32 old_value)
{ {
if (reg == SDHCI_BLOCK_SIZE) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
int shift = (spec_reg & 0x2) * 8;
u32 ret;
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.
*/
pltfm_host->xfer_mode_shadow = value;
return old_value;
case SDHCI_COMMAND:
ret = (value << 16) | pltfm_host->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 * Two last DMA bits are reserved, and first one is used for
* non-standard blksz of 4096 bytes that we don't support * non-standard blksz of 4096 bytes that we don't support
* yet. So clear the DMA boundary bits. * 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 sdhci_host *host,
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 * "DMA select" location is offset 0x28 in SD specification, but on
* P5020 or P3041, it's located at 0x29. * P5020 or P3041, it's located at 0x29.
*/ */
if (reg == SDHCI_HOST_CONTROL) { if (spec_reg == SDHCI_HOST_CONTROL) {
u32 dma_bits;
/* /*
* If host control register is not standard, exit * If host control register is not standard, exit
* this function * this function
*/ */
if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL) if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL)
return; return old_value;
/* DMA select is 22,23 bits in Protocol Control Register */ /* DMA select is 22,23 bits in Protocol Control Register */
dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; dma_bits = (value & SDHCI_CTRL_DMA_MASK) << 5;
clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5, ret = (old_value & (~(SDHCI_CTRL_DMA_MASK << 5))) | dma_bits;
dma_bits); tmp = (value & (~SDHCI_CTRL_DMA_MASK)) |
val &= ~SDHCI_CTRL_DMA_MASK; (old_value & SDHCI_CTRL_DMA_MASK);
val |= in_be32(host->ioaddr + reg) & 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). */ ret = (old_value & (~(0xff << shift))) | (value << shift);
if (reg == SDHCI_HOST_CONTROL) return ret;
val &= ~ESDHC_HOST_CONTROL_RES; }
sdhci_be32bs_writeb(host, val, reg);
static u32 esdhc_be_readl(struct sdhci_host *host, int reg)
{
u32 ret;
u32 value;
value = ioread32be(host->ioaddr + reg);
ret = esdhc_readl_fixup(host, 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(host, 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(host, 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(host, 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(host, 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(host, reg, value);
return ret;
}
static void esdhc_be_writel(struct sdhci_host *host, u32 val, int reg)
{
u32 value;
value = esdhc_writel_fixup(host, 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(host, 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(host, 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(host, 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(host, 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(host, reg, val, value);
iowrite32(ret, host->ioaddr + base);
} }
/* /*
...@@ -149,19 +345,17 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) ...@@ -149,19 +345,17 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
* For Continue, apply soft reset for data(SYSCTL[RSTD]); * For Continue, apply soft reset for data(SYSCTL[RSTD]);
* and re-issue the entire read transaction from beginning. * 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; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_esdhc *esdhc = pltfm_host->priv;
bool applicable; bool applicable;
dma_addr_t dmastart; dma_addr_t dmastart;
dma_addr_t dmanow; 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) && applicable = (intmask & SDHCI_INT_DATA_END) &&
(intmask & SDHCI_INT_BLK_GAP) && (intmask & SDHCI_INT_BLK_GAP) &&
(tmp == VENDOR_V_23); (esdhc->vendor_ver == VENDOR_V_23);
if (!applicable) if (!applicable)
return; return;
...@@ -179,7 +373,11 @@ static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) ...@@ -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) 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; return 0;
} }
...@@ -199,6 +397,8 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) ...@@ -199,6 +397,8 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_esdhc *esdhc = pltfm_host->priv;
int pre_div = 1; int pre_div = 1;
int div = 1; int div = 1;
u32 temp; u32 temp;
...@@ -209,9 +409,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -209,9 +409,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
return; return;
/* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */ /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
temp = esdhc_readw(host, SDHCI_HOST_VERSION); if (esdhc->vendor_ver < VENDOR_V_23)
temp = (temp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
if (temp < VENDOR_V_23)
pre_div = 2; pre_div = 2;
/* Workaround to reduce the clock frequency for p1010 esdhc */ /* Workaround to reduce the clock frequency for p1010 esdhc */
...@@ -247,39 +445,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -247,39 +445,26 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
mdelay(1); 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) static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
{ {
u32 ctrl; u32 ctrl;
ctrl = sdhci_readl(host, ESDHC_PROCTL);
ctrl &= (~ESDHC_CTRL_BUSWIDTH_MASK);
switch (width) { switch (width) {
case MMC_BUS_WIDTH_8: case MMC_BUS_WIDTH_8:
ctrl = ESDHC_CTRL_8BITBUS; ctrl |= ESDHC_CTRL_8BITBUS;
break; break;
case MMC_BUS_WIDTH_4: case MMC_BUS_WIDTH_4:
ctrl = ESDHC_CTRL_4BITBUS; ctrl |= ESDHC_CTRL_4BITBUS;
break; break;
default: default:
ctrl = 0;
break; break;
} }
clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL, sdhci_writel(host, ctrl, ESDHC_PROCTL);
ESDHC_CTRL_BUSWIDTH_MASK, ctrl);
} }
static void esdhc_reset(struct sdhci_host *host, u8 mask) static void esdhc_reset(struct sdhci_host *host, u8 mask)
...@@ -290,32 +475,13 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask) ...@@ -290,32 +475,13 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); 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 #ifdef CONFIG_PM
static u32 esdhc_proctl; static u32 esdhc_proctl;
static int esdhc_of_suspend(struct device *dev) static int esdhc_of_suspend(struct device *dev)
{ {
struct sdhci_host *host = dev_get_drvdata(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); return sdhci_suspend_host(host);
} }
...@@ -328,9 +494,8 @@ static int esdhc_of_resume(struct device *dev) ...@@ -328,9 +494,8 @@ static int esdhc_of_resume(struct device *dev)
if (ret == 0) { if (ret == 0) {
/* Isn't this already done by sdhci_resume_host() ? --rmk */ /* Isn't this already done by sdhci_resume_host() ? --rmk */