diff options
Diffstat (limited to 'drivers/mmc/host/atmel-mci.c')
| -rw-r--r-- | drivers/mmc/host/atmel-mci.c | 116 | 
1 files changed, 38 insertions, 78 deletions
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 69e438ee043..bb585d94090 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -37,6 +37,7 @@  #include <linux/atmel-mci.h>  #include <linux/atmel_pdc.h> +#include <asm/cacheflush.h>  #include <asm/io.h>  #include <asm/unaligned.h> @@ -255,7 +256,6 @@ struct atmel_mci_slot {  #define ATMCI_CARD_PRESENT	0  #define ATMCI_CARD_NEED_INIT	1  #define ATMCI_SHUTDOWN		2 -#define ATMCI_SUSPENDED		3  	int			detect_pin;  	int			wp_pin; @@ -589,6 +589,13 @@ static void atmci_timeout_timer(unsigned long data)  	if (host->mrq->cmd->data) {  		host->mrq->cmd->data->error = -ETIMEDOUT;  		host->data = NULL; +		/* +		 * With some SDIO modules, sometimes DMA transfer hangs. If +		 * stop_transfer() is not called then the DMA request is not +		 * removed, following ones are queued and never computed. +		 */ +		if (host->state == STATE_DATA_XFER) +			host->stop_transfer(host);  	} else {  		host->mrq->cmd->error = -ETIMEDOUT;  		host->cmd = NULL; @@ -814,16 +821,9 @@ static void atmci_pdc_complete(struct atmel_mci *host)  	atmci_pdc_cleanup(host); -	/* -	 * If the card was removed, data will be NULL. No point trying -	 * to send the stop command or waiting for NBUSY in this case. -	 */ -	if (host->data) { -		dev_dbg(&host->pdev->dev, -		        "(%s) set pending xfer complete\n", __func__); -		atmci_set_pending(host, EVENT_XFER_COMPLETE); -		tasklet_schedule(&host->tasklet); -	} +	dev_dbg(&host->pdev->dev, "(%s) set pending xfer complete\n", __func__); +	atmci_set_pending(host, EVENT_XFER_COMPLETE); +	tasklet_schedule(&host->tasklet);  }  static void atmci_dma_cleanup(struct atmel_mci *host) @@ -1186,11 +1186,22 @@ static void atmci_start_request(struct atmel_mci *host,  	iflags |= ATMCI_CMDRDY;  	cmd = mrq->cmd;  	cmdflags = atmci_prepare_command(slot->mmc, cmd); -	atmci_send_command(host, cmd, cmdflags); + +	/* +	 * DMA transfer should be started before sending the command to avoid +	 * unexpected errors especially for read operations in SDIO mode. +	 * Unfortunately, in PDC mode, command has to be sent before starting +	 * the transfer. +	 */ +	if (host->submit_data != &atmci_submit_data_dma) +		atmci_send_command(host, cmd, cmdflags);  	if (data)  		host->submit_data(host, data); +	if (host->submit_data == &atmci_submit_data_dma) +		atmci_send_command(host, cmd, cmdflags); +  	if (mrq->stop) {  		host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);  		host->stop_cmdr |= ATMCI_CMDR_STOP_XFER; @@ -1385,8 +1396,14 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		clk_unprepare(host->mck);  	switch (ios->power_mode) { +	case MMC_POWER_OFF: +		if (!IS_ERR(mmc->supply.vmmc)) +			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); +		break;  	case MMC_POWER_UP:  		set_bit(ATMCI_CARD_NEED_INIT, &slot->flags); +		if (!IS_ERR(mmc->supply.vmmc)) +			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);  		break;  	default:  		/* @@ -1803,12 +1820,14 @@ static void atmci_tasklet_func(unsigned long priv)  			if (unlikely(status)) {  				host->stop_transfer(host);  				host->data = NULL; -				if (status & ATMCI_DTOE) { -					data->error = -ETIMEDOUT; -				} else if (status & ATMCI_DCRCE) { -					data->error = -EILSEQ; -				} else { -					data->error = -EIO; +				if (data) { +					if (status & ATMCI_DTOE) { +						data->error = -ETIMEDOUT; +					} else if (status & ATMCI_DCRCE) { +						data->error = -EILSEQ; +					} else { +						data->error = -EIO; +					}  				}  			} @@ -2196,6 +2215,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,  	}  	host->slot[id] = slot; +	mmc_regulator_get_supply(mmc);  	mmc_add_host(mmc);  	if (gpio_is_valid(slot->detect_pin)) { @@ -2520,70 +2540,10 @@ static int __exit atmci_remove(struct platform_device *pdev)  	return 0;  } -#ifdef CONFIG_PM_SLEEP -static int atmci_suspend(struct device *dev) -{ -	struct atmel_mci *host = dev_get_drvdata(dev); -	int i; - -	 for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { -		struct atmel_mci_slot *slot = host->slot[i]; -		int ret; - -		if (!slot) -			continue; -		ret = mmc_suspend_host(slot->mmc); -		if (ret < 0) { -			while (--i >= 0) { -				slot = host->slot[i]; -				if (slot -				&& test_bit(ATMCI_SUSPENDED, &slot->flags)) { -					mmc_resume_host(host->slot[i]->mmc); -					clear_bit(ATMCI_SUSPENDED, &slot->flags); -				} -			} -			return ret; -		} else { -			set_bit(ATMCI_SUSPENDED, &slot->flags); -		} -	} - -	return 0; -} - -static int atmci_resume(struct device *dev) -{ -	struct atmel_mci *host = dev_get_drvdata(dev); -	int i; -	int ret = 0; - -	for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { -		struct atmel_mci_slot *slot = host->slot[i]; -		int err; - -		slot = host->slot[i]; -		if (!slot) -			continue; -		if (!test_bit(ATMCI_SUSPENDED, &slot->flags)) -			continue; -		err = mmc_resume_host(slot->mmc); -		if (err < 0) -			ret = err; -		else -			clear_bit(ATMCI_SUSPENDED, &slot->flags); -	} - -	return ret; -} -#endif - -static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume); -  static struct platform_driver atmci_driver = {  	.remove		= __exit_p(atmci_remove),  	.driver		= {  		.name		= "atmel_mci", -		.pm		= &atmci_pm,  		.of_match_table	= of_match_ptr(atmci_dt_ids),  	},  };  | 
