diff options
Diffstat (limited to 'drivers/mmc/host/mmci.c')
| -rw-r--r-- | drivers/mmc/host/mmci.c | 148 | 
1 files changed, 88 insertions, 60 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 2ed435bd4b6..840b301b567 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -26,7 +26,6 @@  #include <linux/amba/mmci.h>  #include <linux/regulator/consumer.h> -#include <asm/cacheflush.h>  #include <asm/div64.h>  #include <asm/io.h>  #include <asm/sizes.h> @@ -37,12 +36,39 @@  static unsigned int fmax = 515633; +/** + * struct variant_data - MMCI variant-specific quirks + * @clkreg: default value for MCICLOCK register + * @clkreg_enable: enable value for MMCICLOCK register + * @datalength_bits: number of bits in the MMCIDATALENGTH register + */ +struct variant_data { +	unsigned int		clkreg; +	unsigned int		clkreg_enable; +	unsigned int		datalength_bits; +}; + +static struct variant_data variant_arm = { +	.datalength_bits	= 16, +}; + +static struct variant_data variant_u300 = { +	.clkreg_enable		= 1 << 13, /* HWFCEN */ +	.datalength_bits	= 16, +}; + +static struct variant_data variant_ux500 = { +	.clkreg			= MCI_CLK_ENABLE, +	.clkreg_enable		= 1 << 14, /* HWFCEN */ +	.datalength_bits	= 24, +};  /*   * This must be called with host->lock held   */  static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)  { -	u32 clk = 0; +	struct variant_data *variant = host->variant; +	u32 clk = variant->clkreg;  	if (desired) {  		if (desired >= host->mclk) { @@ -54,8 +80,8 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)  				clk = 255;  			host->cclk = host->mclk / (2 * (clk + 1));  		} -		if (host->hw_designer == AMBA_VENDOR_ST) -			clk |= MCI_ST_FCEN; /* Bug fix in ST IP block */ + +		clk |= variant->clkreg_enable;  		clk |= MCI_CLK_ENABLE;  		/* This hasn't proven to be worthwhile */  		/* clk |= MCI_CLK_PWRSAVE; */ @@ -98,6 +124,18 @@ static void mmci_stop_data(struct mmci_host *host)  	host->data = NULL;  } +static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) +{ +	unsigned int flags = SG_MITER_ATOMIC; + +	if (data->flags & MMC_DATA_READ) +		flags |= SG_MITER_TO_SG; +	else +		flags |= SG_MITER_FROM_SG; + +	sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); +} +  static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)  {  	unsigned int datactrl, timeout, irqmask; @@ -109,7 +147,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)  		data->blksz, data->blocks, data->flags);  	host->data = data; -	host->size = data->blksz; +	host->size = data->blksz * data->blocks;  	host->data_xfered = 0;  	mmci_init_sg(host, data); @@ -210,8 +248,17 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,  		 * We hit an error condition.  Ensure that any data  		 * partially written to a page is properly coherent.  		 */ -		if (host->sg_len && data->flags & MMC_DATA_READ) -			flush_dcache_page(sg_page(host->sg_ptr)); +		if (data->flags & MMC_DATA_READ) { +			struct sg_mapping_iter *sg_miter = &host->sg_miter; +			unsigned long flags; + +			local_irq_save(flags); +			if (sg_miter_next(sg_miter)) { +				flush_dcache_page(sg_miter->page); +				sg_miter_stop(sg_miter); +			} +			local_irq_restore(flags); +		}  	}  	if (status & MCI_DATAEND) {  		mmci_stop_data(host); @@ -314,15 +361,18 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem  static irqreturn_t mmci_pio_irq(int irq, void *dev_id)  {  	struct mmci_host *host = dev_id; +	struct sg_mapping_iter *sg_miter = &host->sg_miter;  	void __iomem *base = host->base; +	unsigned long flags;  	u32 status;  	status = readl(base + MMCISTATUS);  	dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); +	local_irq_save(flags); +  	do { -		unsigned long flags;  		unsigned int remain, len;  		char *buffer; @@ -336,11 +386,11 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)  		if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))  			break; -		/* -		 * Map the current scatter buffer. -		 */ -		buffer = mmci_kmap_atomic(host, &flags) + host->sg_off; -		remain = host->sg_ptr->length - host->sg_off; +		if (!sg_miter_next(sg_miter)) +			break; + +		buffer = sg_miter->addr; +		remain = sg_miter->length;  		len = 0;  		if (status & MCI_RXACTIVE) @@ -348,31 +398,24 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)  		if (status & MCI_TXACTIVE)  			len = mmci_pio_write(host, buffer, remain, status); -		/* -		 * Unmap the buffer. -		 */ -		mmci_kunmap_atomic(host, buffer, &flags); +		sg_miter->consumed = len; -		host->sg_off += len;  		host->size -= len;  		remain -= len;  		if (remain)  			break; -		/* -		 * If we were reading, and we have completed this -		 * page, ensure that the data cache is coherent. -		 */  		if (status & MCI_RXACTIVE) -			flush_dcache_page(sg_page(host->sg_ptr)); - -		if (!mmci_next_sg(host)) -			break; +			flush_dcache_page(sg_miter->page);  		status = readl(base + MMCISTATUS);  	} while (1); +	sg_miter_stop(sg_miter); + +	local_irq_restore(flags); +  	/*  	 * If we're nearing the end of the read, switch to  	 * "any data available" mode. @@ -477,16 +520,9 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  			/* This implicitly enables the regulator */  			mmc_regulator_set_ocr(host->vcc, ios->vdd);  #endif -		/* -		 * The translate_vdd function is not used if you have -		 * an external regulator, or your design is really weird. -		 * Using it would mean sending in power control BOTH using -		 * a regulator AND the 4 MMCIPWR bits. If we don't have -		 * a regulator, we might have some other platform specific -		 * power control behind this translate function. -		 */ -		if (!host->vcc && host->plat->translate_vdd) -			pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); +		if (host->plat->vdd_handler) +			pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd, +						       ios->power_mode);  		/* The ST version does not have this, fall through to POWER_ON */  		if (host->hw_designer != AMBA_VENDOR_ST) {  			pwr |= MCI_PWR_UP; @@ -555,21 +591,10 @@ static const struct mmc_host_ops mmci_ops = {  	.get_cd		= mmci_get_cd,  }; -static void mmci_check_status(unsigned long data) -{ -	struct mmci_host *host = (struct mmci_host *)data; -	unsigned int status = mmci_get_cd(host->mmc); - -	if (status ^ host->oldstat) -		mmc_detect_change(host->mmc, 0); - -	host->oldstat = status; -	mod_timer(&host->timer, jiffies + HZ); -} -  static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)  {  	struct mmci_platform_data *plat = dev->dev.platform_data; +	struct variant_data *variant = id->data;  	struct mmci_host *host;  	struct mmc_host *mmc;  	int ret; @@ -613,6 +638,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)  		goto clk_free;  	host->plat = plat; +	host->variant = variant;  	host->mclk = clk_get_rate(host->clk);  	/*  	 * According to the spec, mclk is max 100 MHz, @@ -673,6 +699,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)  	if (host->vcc == NULL)  		mmc->ocr_avail = plat->ocr_mask;  	mmc->caps = plat->capabilities; +	mmc->caps |= MMC_CAP_NEEDS_POLL;  	/*  	 * We can do SGIO @@ -681,10 +708,11 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)  	mmc->max_phys_segs = NR_SG;  	/* -	 * Since we only have a 16-bit data length register, we must -	 * ensure that we don't exceed 2^16-1 bytes in a single request. +	 * Since only a certain number of bits are valid in the data length +	 * register, we must ensure that we don't exceed 2^num-1 bytes in a +	 * single request.  	 */ -	mmc->max_req_size = 65535; +	mmc->max_req_size = (1 << variant->datalength_bits) - 1;  	/*  	 * Set the maximum segment size.  Since we aren't doing DMA @@ -738,7 +766,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)  	writel(MCI_IRQENABLE, host->base + MMCIMASK0);  	amba_set_drvdata(dev, mmc); -	host->oldstat = mmci_get_cd(host->mmc);  	mmc_add_host(mmc); @@ -746,12 +773,6 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)  		mmc_hostname(mmc), amba_rev(dev), amba_config(dev),  		(unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]); -	init_timer(&host->timer); -	host->timer.data = (unsigned long)host; -	host->timer.function = mmci_check_status; -	host->timer.expires = jiffies + HZ; -	add_timer(&host->timer); -  	return 0;   irq0_free: @@ -785,8 +806,6 @@ static int __devexit mmci_remove(struct amba_device *dev)  	if (mmc) {  		struct mmci_host *host = mmc_priv(mmc); -		del_timer_sync(&host->timer); -  		mmc_remove_host(mmc);  		writel(0, host->base + MMCIMASK0); @@ -860,19 +879,28 @@ static struct amba_id mmci_ids[] = {  	{  		.id	= 0x00041180,  		.mask	= 0x000fffff, +		.data	= &variant_arm,  	},  	{  		.id	= 0x00041181,  		.mask	= 0x000fffff, +		.data	= &variant_arm,  	},  	/* ST Micro variants */  	{  		.id     = 0x00180180,  		.mask   = 0x00ffffff, +		.data	= &variant_u300,  	},  	{  		.id     = 0x00280180,  		.mask   = 0x00ffffff, +		.data	= &variant_u300, +	}, +	{ +		.id     = 0x00480180, +		.mask   = 0x00ffffff, +		.data	= &variant_ux500,  	},  	{ 0, 0 },  };  |