diff options
Diffstat (limited to 'drivers/mmc/host/mmci.c')
| -rw-r--r-- | drivers/mmc/host/mmci.c | 114 | 
1 files changed, 88 insertions, 26 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 40e72c30ea84..e9ffce8d41ea 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -169,6 +169,8 @@ static struct variant_data variant_ux500 = {  	.cmdreg_srsp		= MCI_CPSM_RESPONSE,  	.datalength_bits	= 24,  	.datactrl_blocksz	= 11, +	.datactrl_any_blocksz	= true, +	.dma_power_of_2		= true,  	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,  	.st_sdio		= true,  	.st_clkdiv		= true, @@ -202,6 +204,8 @@ static struct variant_data variant_ux500v2 = {  	.datactrl_mask_ddrmode	= MCI_DPSM_ST_DDRMODE,  	.datalength_bits	= 24,  	.datactrl_blocksz	= 11, +	.datactrl_any_blocksz	= true, +	.dma_power_of_2		= true,  	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,  	.st_sdio		= true,  	.st_clkdiv		= true, @@ -261,6 +265,7 @@ static struct variant_data variant_stm32_sdmmc = {  	.datacnt_useless	= true,  	.datalength_bits	= 25,  	.datactrl_blocksz	= 14, +	.datactrl_any_blocksz	= true,  	.stm32_idmabsize_mask	= GENMASK(12, 5),  	.busy_timeout		= true,  	.busy_detect		= true, @@ -284,6 +289,7 @@ static struct variant_data variant_qcom = {  	.data_cmd_enable	= MCI_CPSM_QCOM_DATCMD,  	.datalength_bits	= 24,  	.datactrl_blocksz	= 11, +	.datactrl_any_blocksz	= true,  	.pwrreg_powerup		= MCI_PWR_UP,  	.f_max			= 208000000,  	.explicit_mclk_control	= true, @@ -452,10 +458,11 @@ static void mmci_dma_setup(struct mmci_host *host)  static int mmci_validate_data(struct mmci_host *host,  			      struct mmc_data *data)  { +	struct variant_data *variant = host->variant; +  	if (!data)  		return 0; - -	if (!is_power_of_2(data->blksz)) { +	if (!is_power_of_2(data->blksz) && !variant->datactrl_any_blocksz) {  		dev_err(mmc_dev(host->mmc),  			"unsupported block size (%d bytes)\n", data->blksz);  		return -EINVAL; @@ -520,7 +527,9 @@ static int mmci_dma_start(struct mmci_host *host, unsigned int datactrl)  		 "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",  		 data->sg_len, data->blksz, data->blocks, data->flags); -	host->ops->dma_start(host, &datactrl); +	ret = host->ops->dma_start(host, &datactrl); +	if (ret) +		return ret;  	/* Trigger the DMA transfer */  	mmci_write_datactrlreg(host, datactrl); @@ -706,10 +715,20 @@ int mmci_dmae_setup(struct mmci_host *host)  	host->dma_priv = dmae; -	dmae->rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), -						     "rx"); -	dmae->tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), -						     "tx"); +	dmae->rx_channel = dma_request_chan(mmc_dev(host->mmc), "rx"); +	if (IS_ERR(dmae->rx_channel)) { +		int ret = PTR_ERR(dmae->rx_channel); +		dmae->rx_channel = NULL; +		return ret; +	} + +	dmae->tx_channel = dma_request_chan(mmc_dev(host->mmc), "tx"); +	if (IS_ERR(dmae->tx_channel)) { +		if (PTR_ERR(dmae->tx_channel) == -EPROBE_DEFER) +			dev_warn(mmc_dev(host->mmc), +				 "Deferred probe for TX channel ignored\n"); +		dmae->tx_channel = NULL; +	}  	/*  	 * If only an RX channel is specified, the driver will @@ -888,6 +907,18 @@ static int _mmci_dmae_prep_data(struct mmci_host *host, struct mmc_data *data,  	if (data->blksz * data->blocks <= variant->fifosize)  		return -EINVAL; +	/* +	 * This is necessary to get SDIO working on the Ux500. We do not yet +	 * know if this is a bug in: +	 * - The Ux500 DMA controller (DMA40) +	 * - The MMCI DMA interface on the Ux500 +	 * some power of two blocks (such as 64 bytes) are sent regularly +	 * during SDIO traffic and those work fine so for these we enable DMA +	 * transfers. +	 */ +	if (host->variant->dma_power_of_2 && !is_power_of_2(data->blksz)) +		return -EINVAL; +  	device = chan->device;  	nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len,  			   mmc_get_dma_dir(data)); @@ -938,9 +969,14 @@ int mmci_dmae_prep_data(struct mmci_host *host,  int mmci_dmae_start(struct mmci_host *host, unsigned int *datactrl)  {  	struct mmci_dmae_priv *dmae = host->dma_priv; +	int ret;  	host->dma_in_progress = true; -	dmaengine_submit(dmae->desc_current); +	ret = dma_submit_error(dmaengine_submit(dmae->desc_current)); +	if (ret < 0) { +		host->dma_in_progress = false; +		return ret; +	}  	dma_async_issue_pending(dmae->cur);  	*datactrl |= MCI_DPSM_DMAENABLE; @@ -1321,6 +1357,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,  	} else if (host->variant->busy_timeout && busy_resp &&  		   status & MCI_DATATIMEOUT) {  		cmd->error = -ETIMEDOUT; +		host->irq_action = IRQ_WAKE_THREAD;  	} else {  		cmd->resp[0] = readl(base + MMCIRESPONSE0);  		cmd->resp[1] = readl(base + MMCIRESPONSE1); @@ -1339,7 +1376,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,  				return;  			}  		} -		mmci_request_end(host, host->mrq); + +		if (host->irq_action != IRQ_WAKE_THREAD) +			mmci_request_end(host, host->mrq); +  	} else if (sbc) {  		mmci_start_command(host, host->mrq->cmd, 0);  	} else if (!host->variant->datactrl_first && @@ -1532,9 +1572,9 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)  {  	struct mmci_host *host = dev_id;  	u32 status; -	int ret = 0;  	spin_lock(&host->lock); +	host->irq_action = IRQ_HANDLED;  	do {  		status = readl(host->base + MMCISTATUS); @@ -1574,12 +1614,41 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)  		if (host->variant->busy_detect_flag)  			status &= ~host->variant->busy_detect_flag; -		ret = 1;  	} while (status);  	spin_unlock(&host->lock); -	return IRQ_RETVAL(ret); +	return host->irq_action; +} + +/* + * mmci_irq_thread() - A threaded IRQ handler that manages a reset of the HW. + * + * A reset is needed for some variants, where a datatimeout for a R1B request + * causes the DPSM to stay busy (non-functional). + */ +static irqreturn_t mmci_irq_thread(int irq, void *dev_id) +{ +	struct mmci_host *host = dev_id; +	unsigned long flags; + +	if (host->rst) { +		reset_control_assert(host->rst); +		udelay(2); +		reset_control_deassert(host->rst); +	} + +	spin_lock_irqsave(&host->lock, flags); +	writel(host->clk_reg, host->base + MMCICLOCK); +	writel(host->pwr_reg, host->base + MMCIPOWER); +	writel(MCI_IRQENABLE | host->variant->start_err, +	       host->base + MMCIMASK0); + +	host->irq_action = IRQ_HANDLED; +	mmci_request_end(host, host->mrq); +	spin_unlock_irqrestore(&host->lock, flags); + +	return host->irq_action;  }  static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) @@ -1704,7 +1773,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)  		if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)  			pinctrl_select_state(host->pinctrl, host->pins_opendrain);  		else -			pinctrl_select_state(host->pinctrl, host->pins_default); +			pinctrl_select_default_state(mmc_dev(mmc));  	}  	/* @@ -1877,14 +1946,6 @@ static int mmci_probe(struct amba_device *dev,  			goto host_free;  		} -		host->pins_default = pinctrl_lookup_state(host->pinctrl, -							  PINCTRL_STATE_DEFAULT); -		if (IS_ERR(host->pins_default)) { -			dev_err(mmc_dev(mmc), "Can't select default pins\n"); -			ret = PTR_ERR(host->pins_default); -			goto host_free; -		} -  		host->pins_opendrain = pinctrl_lookup_state(host->pinctrl,  							    MMCI_PINCTRL_STATE_OPENDRAIN);  		if (IS_ERR(host->pins_opendrain)) { @@ -2062,17 +2123,18 @@ static int mmci_probe(struct amba_device *dev,  	 * silently of these do not exist  	 */  	if (!np) { -		ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL); +		ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0);  		if (ret == -EPROBE_DEFER)  			goto clk_disable; -		ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0, NULL); +		ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0);  		if (ret == -EPROBE_DEFER)  			goto clk_disable;  	} -	ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED, -			DRIVER_NAME " (cmd)", host); +	ret = devm_request_threaded_irq(&dev->dev, dev->irq[0], mmci_irq, +					mmci_irq_thread, IRQF_SHARED, +					DRIVER_NAME " (cmd)", host);  	if (ret)  		goto clk_disable; @@ -2203,7 +2265,7 @@ static int mmci_runtime_resume(struct device *dev)  		struct mmci_host *host = mmc_priv(mmc);  		clk_prepare_enable(host->clk);  		mmci_restore(host); -		pinctrl_pm_select_default_state(dev); +		pinctrl_select_default_state(dev);  	}  	return 0;  |