diff options
Diffstat (limited to 'drivers/mmc/host/mmci.c')
| -rw-r--r-- | drivers/mmc/host/mmci.c | 128 | 
1 files changed, 91 insertions, 37 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index df990bb8c873..01a804792f30 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -71,7 +71,12 @@ static unsigned int fmax = 515633;   * @f_max: maximum clk frequency supported by the controller.   * @signal_direction: input/out direction of bus signals can be indicated   * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock - * @busy_detect: true if busy detection on dat0 is supported + * @busy_detect: true if the variant supports busy detection on DAT0. + * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM + * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register + *		      indicating that the card is busy + * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for + *		      getting busy end detection interrupts   * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply   * @explicit_mclk_control: enable explicit mclk control in driver.   * @qcom_fifo: enables qcom specific fifo pio read logic. @@ -98,6 +103,9 @@ struct variant_data {  	bool			signal_direction;  	bool			pwrreg_clkgate;  	bool			busy_detect; +	u32			busy_dpsm_flag; +	u32			busy_detect_flag; +	u32			busy_detect_mask;  	bool			pwrreg_nopower;  	bool			explicit_mclk_control;  	bool			qcom_fifo; @@ -137,7 +145,7 @@ static struct variant_data variant_u300 = {  	.clkreg_enable		= MCI_ST_U300_HWFCEN,  	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,  	.datalength_bits	= 16, -	.datactrl_mask_sdio	= MCI_ST_DPSM_SDIOEN, +	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,  	.st_sdio			= true,  	.pwrreg_powerup		= MCI_PWR_ON,  	.f_max			= 100000000, @@ -152,7 +160,7 @@ static struct variant_data variant_nomadik = {  	.clkreg			= MCI_CLK_ENABLE,  	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,  	.datalength_bits	= 24, -	.datactrl_mask_sdio	= MCI_ST_DPSM_SDIOEN, +	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,  	.st_sdio		= true,  	.st_clkdiv		= true,  	.pwrreg_powerup		= MCI_PWR_ON, @@ -170,7 +178,7 @@ static struct variant_data variant_ux500 = {  	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,  	.clkreg_neg_edge_enable	= MCI_ST_UX500_NEG_EDGE,  	.datalength_bits	= 24, -	.datactrl_mask_sdio	= MCI_ST_DPSM_SDIOEN, +	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,  	.st_sdio		= true,  	.st_clkdiv		= true,  	.pwrreg_powerup		= MCI_PWR_ON, @@ -178,6 +186,9 @@ static struct variant_data variant_ux500 = {  	.signal_direction	= true,  	.pwrreg_clkgate		= true,  	.busy_detect		= true, +	.busy_dpsm_flag		= MCI_DPSM_ST_BUSYMODE, +	.busy_detect_flag	= MCI_ST_CARDBUSY, +	.busy_detect_mask	= MCI_ST_BUSYENDMASK,  	.pwrreg_nopower		= true,  }; @@ -188,9 +199,9 @@ static struct variant_data variant_ux500v2 = {  	.clkreg_enable		= MCI_ST_UX500_HWFCEN,  	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,  	.clkreg_neg_edge_enable	= MCI_ST_UX500_NEG_EDGE, -	.datactrl_mask_ddrmode	= MCI_ST_DPSM_DDRMODE, +	.datactrl_mask_ddrmode	= MCI_DPSM_ST_DDRMODE,  	.datalength_bits	= 24, -	.datactrl_mask_sdio	= MCI_ST_DPSM_SDIOEN, +	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,  	.st_sdio		= true,  	.st_clkdiv		= true,  	.blksz_datactrl16	= true, @@ -199,6 +210,9 @@ static struct variant_data variant_ux500v2 = {  	.signal_direction	= true,  	.pwrreg_clkgate		= true,  	.busy_detect		= true, +	.busy_dpsm_flag		= MCI_DPSM_ST_BUSYMODE, +	.busy_detect_flag	= MCI_ST_CARDBUSY, +	.busy_detect_mask	= MCI_ST_BUSYENDMASK,  	.pwrreg_nopower		= true,  }; @@ -210,7 +224,7 @@ static struct variant_data variant_qcom = {  				  MCI_QCOM_CLK_SELECT_IN_FBCLK,  	.clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8,  	.datactrl_mask_ddrmode	= MCI_QCOM_CLK_SELECT_IN_DDR_MODE, -	.data_cmd_enable	= MCI_QCOM_CSPM_DATCMD, +	.data_cmd_enable	= MCI_CPSM_QCOM_DATCMD,  	.blksz_datactrl4	= true,  	.datalength_bits	= 24,  	.pwrreg_powerup		= MCI_PWR_UP, @@ -220,6 +234,7 @@ static struct variant_data variant_qcom = {  	.qcom_dml		= true,  }; +/* Busy detection for the ST Micro variant */  static int mmci_card_busy(struct mmc_host *mmc)  {  	struct mmci_host *host = mmc_priv(mmc); @@ -227,7 +242,7 @@ static int mmci_card_busy(struct mmc_host *mmc)  	int busy = 0;  	spin_lock_irqsave(&host->lock, flags); -	if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY) +	if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag)  		busy = 1;  	spin_unlock_irqrestore(&host->lock, flags); @@ -294,8 +309,8 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)   */  static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)  { -	/* Keep ST Micro busy mode if enabled */ -	datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE; +	/* Keep busy mode in DPSM if enabled */ +	datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag;  	if (host->datactrl_reg != datactrl) {  		host->datactrl_reg = datactrl; @@ -684,8 +699,7 @@ static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)  	next->dma_chan = NULL;  } -static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq, -			     bool is_first_req) +static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq)  {  	struct mmci_host *host = mmc_priv(mmc);  	struct mmc_data *data = mrq->data; @@ -973,37 +987,66 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,  	     unsigned int status)  {  	void __iomem *base = host->base; -	bool sbc, busy_resp; +	bool sbc;  	if (!cmd)  		return;  	sbc = (cmd == host->mrq->sbc); -	busy_resp = host->variant->busy_detect && (cmd->flags & MMC_RSP_BUSY); -	if (!((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT| -		MCI_CMDSENT|MCI_CMDRESPEND))) +	/* +	 * We need to be one of these interrupts to be considered worth +	 * handling. Note that we tag on any latent IRQs postponed +	 * due to waiting for busy status. +	 */ +	if (!((status|host->busy_status) & +	      (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND)))  		return; -	/* Check if we need to wait for busy completion. */ -	if (host->busy_status && (status & MCI_ST_CARDBUSY)) -		return; +	/* +	 * ST Micro variant: handle busy detection. +	 */ +	if (host->variant->busy_detect) { +		bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY); -	/* Enable busy completion if needed and supported. */ -	if (!host->busy_status && busy_resp && -		!(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && -		(readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) { -		writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND, -			base + MMCIMASK0); -		host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND); -		return; -	} +		/* We are busy with a command, return */ +		if (host->busy_status && +		    (status & host->variant->busy_detect_flag)) +			return; + +		/* +		 * We were not busy, but we now got a busy response on +		 * something that was not an error, and we double-check +		 * that the special busy status bit is still set before +		 * proceeding. +		 */ +		if (!host->busy_status && busy_resp && +		    !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && +		    (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) { +			/* Unmask the busy IRQ */ +			writel(readl(base + MMCIMASK0) | +			       host->variant->busy_detect_mask, +			       base + MMCIMASK0); +			/* +			 * Now cache the last response status code (until +			 * the busy bit goes low), and return. +			 */ +			host->busy_status = +				status & (MCI_CMDSENT|MCI_CMDRESPEND); +			return; +		} -	/* At busy completion, mask the IRQ and complete the request. */ -	if (host->busy_status) { -		writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND, -			base + MMCIMASK0); -		host->busy_status = 0; +		/* +		 * At this point we are not busy with a command, we have +		 * not received a new busy request, mask the busy IRQ and +		 * fall through to process the IRQ. +		 */ +		if (host->busy_status) { +			writel(readl(base + MMCIMASK0) & +			       ~host->variant->busy_detect_mask, +			       base + MMCIMASK0); +			host->busy_status = 0; +		}  	}  	host->cmd = NULL; @@ -1257,9 +1300,11 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)  			mmci_data_irq(host, host->data, status);  		} -		/* Don't poll for busy completion in irq context. */ -		if (host->busy_status) -			status &= ~MCI_ST_CARDBUSY; +		/* +		 * Don't poll for busy completion in irq context. +		 */ +		if (host->variant->busy_detect && host->busy_status) +			status &= ~host->variant->busy_detect_flag;  		ret = 1;  	} while (status); @@ -1612,9 +1657,18 @@ static int mmci_probe(struct amba_device *dev,  	/* We support these capabilities. */  	mmc->caps |= MMC_CAP_CMD23; +	/* +	 * Enable busy detection. +	 */  	if (variant->busy_detect) {  		mmci_ops.card_busy = mmci_card_busy; -		mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE); +		/* +		 * Not all variants have a flag to enable busy detection +		 * in the DPSM, but if they do, set it here. +		 */ +		if (variant->busy_dpsm_flag) +			mmci_write_datactrlreg(host, +					       host->variant->busy_dpsm_flag);  		mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;  		mmc->max_busy_timeout = 0;  	}  |