diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-of-esdhc.c')
| -rw-r--r-- | drivers/mmc/host/sdhci-of-esdhc.c | 60 | 
1 files changed, 56 insertions, 4 deletions
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 63d219f57ca..f32526d2d96 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -22,6 +22,7 @@  #include "sdhci-esdhc.h"  #define VENDOR_V_22	0x12 +#define VENDOR_V_23	0x13  static u32 esdhc_readl(struct sdhci_host *host, int reg)  {  	u32 ret; @@ -85,6 +86,18 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg)  	return ret;  } +static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) +{ +	/* +	 * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] +	 * when SYSCTL[RSTD]) is set for some special operations. +	 * No any impact other operation. +	 */ +	if (reg == SDHCI_INT_ENABLE) +		val |= SDHCI_INT_BLK_GAP; +	sdhci_be32bs_writel(host, val, reg); +} +  static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)  {  	if (reg == SDHCI_BLOCK_SIZE) { @@ -121,6 +134,41 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)  	sdhci_be32bs_writeb(host, val, reg);  } +/* + * For Abort or Suspend after Stop at Block Gap, ignore the ADMA + * error(IRQSTAT[ADMAE]) if both Transfer Complete(IRQSTAT[TC]) + * and Block Gap Event(IRQSTAT[BGE]) are also set. + * 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) +{ +	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); +	if (!applicable) +		return; + +	host->data->error = 0; +	dmastart = sg_dma_address(host->data->sg); +	dmanow = dmastart + host->data->bytes_xfered; +	/* +	 * Force update to the next DMA block boundary. +	 */ +	dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + +		SDHCI_DEFAULT_BOUNDARY_SIZE; +	host->data->bytes_xfered = dmanow - dmastart; +	sdhci_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); @@ -177,13 +225,16 @@ static void esdhc_of_platform_init(struct sdhci_host *host)  	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 struct sdhci_ops sdhci_esdhc_ops = {  	.read_l = esdhc_readl,  	.read_w = esdhc_readw,  	.read_b = esdhc_readb, -	.write_l = sdhci_be32bs_writel, +	.write_l = esdhc_writel,  	.write_w = esdhc_writew,  	.write_b = esdhc_writeb,  	.set_clock = esdhc_of_set_clock, @@ -195,6 +246,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {  	.platform_suspend = esdhc_of_suspend,  	.platform_resume = esdhc_of_resume,  #endif +	.adma_workaround = esdhci_of_adma_workaround,  };  static struct sdhci_pltfm_data sdhci_esdhc_pdata = { @@ -208,12 +260,12 @@ static struct sdhci_pltfm_data sdhci_esdhc_pdata = {  	.ops = &sdhci_esdhc_ops,  }; -static int __devinit sdhci_esdhc_probe(struct platform_device *pdev) +static int sdhci_esdhc_probe(struct platform_device *pdev)  {  	return sdhci_pltfm_register(pdev, &sdhci_esdhc_pdata);  } -static int __devexit sdhci_esdhc_remove(struct platform_device *pdev) +static int sdhci_esdhc_remove(struct platform_device *pdev)  {  	return sdhci_pltfm_unregister(pdev);  } @@ -234,7 +286,7 @@ static struct platform_driver sdhci_esdhc_driver = {  		.pm = SDHCI_PLTFM_PMOPS,  	},  	.probe = sdhci_esdhc_probe, -	.remove = __devexit_p(sdhci_esdhc_remove), +	.remove = sdhci_esdhc_remove,  };  module_platform_driver(sdhci_esdhc_driver);  |