diff options
Diffstat (limited to 'drivers/spi/spi-stm32-qspi.c')
| -rw-r--r-- | drivers/spi/spi-stm32-qspi.c | 106 | 
1 files changed, 84 insertions, 22 deletions
| diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 947e6b9dc9f4..7e640ccc7e77 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -269,8 +269,9 @@ static int stm32_qspi_tx(struct stm32_qspi *qspi, const struct spi_mem_op *op)  	if (qspi->fmode == CCR_FMODE_MM)  		return stm32_qspi_tx_mm(qspi, op); -	else if ((op->data.dir == SPI_MEM_DATA_IN && qspi->dma_chrx) || -		 (op->data.dir == SPI_MEM_DATA_OUT && qspi->dma_chtx)) +	else if (((op->data.dir == SPI_MEM_DATA_IN && qspi->dma_chrx) || +		 (op->data.dir == SPI_MEM_DATA_OUT && qspi->dma_chtx)) && +		  op->data.nbytes > 4)  		if (!stm32_qspi_tx_dma(qspi, op))  			return 0; @@ -330,7 +331,7 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)  {  	struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master);  	struct stm32_qspi_flash *flash = &qspi->flash[mem->spi->chip_select]; -	u32 ccr, cr, addr_max; +	u32 ccr, cr;  	int timeout, err = 0;  	dev_dbg(qspi->dev, "cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n", @@ -342,18 +343,6 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)  	if (err)  		goto abort; -	addr_max = op->addr.val + op->data.nbytes + 1; - -	if (op->data.dir == SPI_MEM_DATA_IN) { -		if (addr_max < qspi->mm_size && -		    op->addr.buswidth) -			qspi->fmode = CCR_FMODE_MM; -		else -			qspi->fmode = CCR_FMODE_INDR; -	} else { -		qspi->fmode = CCR_FMODE_INDW; -	} -  	cr = readl_relaxed(qspi->io_base + QSPI_CR);  	cr &= ~CR_PRESC_MASK & ~CR_FSEL;  	cr |= FIELD_PREP(CR_PRESC_MASK, flash->presc); @@ -363,8 +352,6 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)  	if (op->data.nbytes)  		writel_relaxed(op->data.nbytes - 1,  			       qspi->io_base + QSPI_DLR); -	else -		qspi->fmode = CCR_FMODE_INDW;  	ccr = qspi->fmode;  	ccr |= FIELD_PREP(CCR_INST_MASK, op->cmd.opcode); @@ -440,6 +427,11 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)  	}  	mutex_lock(&qspi->lock); +	if (op->data.dir == SPI_MEM_DATA_IN && op->data.nbytes) +		qspi->fmode = CCR_FMODE_INDR; +	else +		qspi->fmode = CCR_FMODE_INDW; +  	ret = stm32_qspi_send(mem, op);  	mutex_unlock(&qspi->lock); @@ -449,6 +441,64 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)  	return ret;  } +static int stm32_qspi_dirmap_create(struct spi_mem_dirmap_desc *desc) +{ +	struct stm32_qspi *qspi = spi_controller_get_devdata(desc->mem->spi->master); + +	if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) +		return -EOPNOTSUPP; + +	/* should never happen, as mm_base == null is an error probe exit condition */ +	if (!qspi->mm_base && desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) +		return -EOPNOTSUPP; + +	if (!qspi->mm_size) +		return -EOPNOTSUPP; + +	return 0; +} + +static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc, +				      u64 offs, size_t len, void *buf) +{ +	struct stm32_qspi *qspi = spi_controller_get_devdata(desc->mem->spi->master); +	struct spi_mem_op op; +	u32 addr_max; +	int ret; + +	ret = pm_runtime_get_sync(qspi->dev); +	if (ret < 0) { +		pm_runtime_put_noidle(qspi->dev); +		return ret; +	} + +	mutex_lock(&qspi->lock); +	/* make a local copy of desc op_tmpl and complete dirmap rdesc +	 * spi_mem_op template with offs, len and *buf in  order to get +	 * all needed transfer information into struct spi_mem_op +	 */ +	memcpy(&op, &desc->info.op_tmpl, sizeof(struct spi_mem_op)); +	dev_dbg(qspi->dev, "%s len = 0x%zx offs = 0x%llx buf = 0x%p\n", __func__, len, offs, buf); + +	op.data.nbytes = len; +	op.addr.val = desc->info.offset + offs; +	op.data.buf.in = buf; + +	addr_max = op.addr.val + op.data.nbytes + 1; +	if (addr_max < qspi->mm_size && op.addr.buswidth) +		qspi->fmode = CCR_FMODE_MM; +	else +		qspi->fmode = CCR_FMODE_INDR; + +	ret = stm32_qspi_send(desc->mem, &op); +	mutex_unlock(&qspi->lock); + +	pm_runtime_mark_last_busy(qspi->dev); +	pm_runtime_put_autosuspend(qspi->dev); + +	return ret ?: len; +} +  static int stm32_qspi_setup(struct spi_device *spi)  {  	struct spi_controller *ctrl = spi->master; @@ -554,7 +604,9 @@ static void stm32_qspi_dma_free(struct stm32_qspi *qspi)   * to check supported mode.   */  static const struct spi_controller_mem_ops stm32_qspi_mem_ops = { -	.exec_op = stm32_qspi_exec_op, +	.exec_op	= stm32_qspi_exec_op, +	.dirmap_create	= stm32_qspi_dirmap_create, +	.dirmap_read	= stm32_qspi_dirmap_read,  };  static int stm32_qspi_probe(struct platform_device *pdev) @@ -727,21 +779,31 @@ static int __maybe_unused stm32_qspi_suspend(struct device *dev)  {  	pinctrl_pm_select_sleep_state(dev); -	return 0; +	return pm_runtime_force_suspend(dev);  }  static int __maybe_unused stm32_qspi_resume(struct device *dev)  {  	struct stm32_qspi *qspi = dev_get_drvdata(dev); +	int ret; + +	ret = pm_runtime_force_resume(dev); +	if (ret < 0) +		return ret;  	pinctrl_pm_select_default_state(dev); -	clk_prepare_enable(qspi->clk); + +	ret = pm_runtime_get_sync(dev); +	if (ret < 0) { +		pm_runtime_put_noidle(dev); +		return ret; +	}  	writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR);  	writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); -	pm_runtime_mark_last_busy(qspi->dev); -	pm_runtime_put_autosuspend(qspi->dev); +	pm_runtime_mark_last_busy(dev); +	pm_runtime_put_autosuspend(dev);  	return 0;  } | 
