diff options
Diffstat (limited to 'drivers/spi/spi-stm32-qspi.c')
| -rw-r--r-- | drivers/spi/spi-stm32-qspi.c | 125 | 
1 files changed, 116 insertions, 9 deletions
diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index f3fe92300639..9131660c1afb 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -15,6 +15,7 @@  #include <linux/mutex.h>  #include <linux/of.h>  #include <linux/of_device.h> +#include <linux/of_gpio.h>  #include <linux/pinctrl/consumer.h>  #include <linux/pm_runtime.h>  #include <linux/platform_device.h> @@ -355,10 +356,10 @@ static int stm32_qspi_get_mode(u8 buswidth)  	return buswidth;  } -static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op) +static int stm32_qspi_send(struct spi_device *spi, 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]; +	struct stm32_qspi *qspi = spi_controller_get_devdata(spi->master); +	struct stm32_qspi_flash *flash = &qspi->flash[spi->chip_select];  	u32 ccr, cr;  	int timeout, err = 0, err_poll_status = 0; @@ -465,7 +466,7 @@ static int stm32_qspi_poll_status(struct spi_mem *mem, const struct spi_mem_op *  	qspi->fmode = CCR_FMODE_APM;  	qspi->status_timeout = timeout_ms; -	ret = stm32_qspi_send(mem, op); +	ret = stm32_qspi_send(mem->spi, op);  	mutex_unlock(&qspi->lock);  	pm_runtime_mark_last_busy(qspi->dev); @@ -489,7 +490,7 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)  	else  		qspi->fmode = CCR_FMODE_INDW; -	ret = stm32_qspi_send(mem, op); +	ret = stm32_qspi_send(mem->spi, op);  	mutex_unlock(&qspi->lock);  	pm_runtime_mark_last_busy(qspi->dev); @@ -545,7 +546,7 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,  	else  		qspi->fmode = CCR_FMODE_INDR; -	ret = stm32_qspi_send(desc->mem, &op); +	ret = stm32_qspi_send(desc->mem->spi, &op);  	mutex_unlock(&qspi->lock);  	pm_runtime_mark_last_busy(qspi->dev); @@ -554,12 +555,96 @@ static ssize_t stm32_qspi_dirmap_read(struct spi_mem_dirmap_desc *desc,  	return ret ?: len;  } +static int stm32_qspi_transfer_one_message(struct spi_controller *ctrl, +					   struct spi_message *msg) +{ +	struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl); +	struct spi_transfer *transfer; +	struct spi_device *spi = msg->spi; +	struct spi_mem_op op; +	int ret = 0; + +	if (!spi->cs_gpiod) +		return -EOPNOTSUPP; + +	ret = pm_runtime_resume_and_get(qspi->dev); +	if (ret < 0) +		return ret; + +	mutex_lock(&qspi->lock); + +	gpiod_set_value_cansleep(spi->cs_gpiod, true); + +	list_for_each_entry(transfer, &msg->transfers, transfer_list) { +		u8 dummy_bytes = 0; + +		memset(&op, 0, sizeof(op)); + +		dev_dbg(qspi->dev, "tx_buf:%p tx_nbits:%d rx_buf:%p rx_nbits:%d len:%d dummy_data:%d\n", +			transfer->tx_buf, transfer->tx_nbits, +			transfer->rx_buf, transfer->rx_nbits, +			transfer->len, transfer->dummy_data); + +		/* +		 * QSPI hardware supports dummy bytes transfer. +		 * If current transfer is dummy byte, merge it with the next +		 * transfer in order to take into account QSPI block constraint +		 */ +		if (transfer->dummy_data) { +			op.dummy.buswidth = transfer->tx_nbits; +			op.dummy.nbytes = transfer->len; +			dummy_bytes = transfer->len; + +			/* if happens, means that message is not correctly built */ +			if (list_is_last(&transfer->transfer_list, &msg->transfers)) { +				ret = -EINVAL; +				goto end_of_transfer; +			} + +			transfer = list_next_entry(transfer, transfer_list); +		} + +		op.data.nbytes = transfer->len; + +		if (transfer->rx_buf) { +			qspi->fmode = CCR_FMODE_INDR; +			op.data.buswidth = transfer->rx_nbits; +			op.data.dir = SPI_MEM_DATA_IN; +			op.data.buf.in = transfer->rx_buf; +		} else { +			qspi->fmode = CCR_FMODE_INDW; +			op.data.buswidth = transfer->tx_nbits; +			op.data.dir = SPI_MEM_DATA_OUT; +			op.data.buf.out = transfer->tx_buf; +		} + +		ret = stm32_qspi_send(spi, &op); +		if (ret) +			goto end_of_transfer; + +		msg->actual_length += transfer->len + dummy_bytes; +	} + +end_of_transfer: +	gpiod_set_value_cansleep(spi->cs_gpiod, false); + +	mutex_unlock(&qspi->lock); + +	msg->status = ret; +	spi_finalize_current_message(ctrl); + +	pm_runtime_mark_last_busy(qspi->dev); +	pm_runtime_put_autosuspend(qspi->dev); + +	return ret; +} +  static int stm32_qspi_setup(struct spi_device *spi)  {  	struct spi_controller *ctrl = spi->master;  	struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl);  	struct stm32_qspi_flash *flash; -	u32 presc; +	u32 presc, mode;  	int ret;  	if (ctrl->busy) @@ -568,6 +653,16 @@ static int stm32_qspi_setup(struct spi_device *spi)  	if (!spi->max_speed_hz)  		return -EINVAL; +	mode = spi->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL); +	if ((mode == SPI_TX_OCTAL || mode == SPI_RX_OCTAL) || +	    ((mode == (SPI_TX_OCTAL | SPI_RX_OCTAL)) && +	    gpiod_count(qspi->dev, "cs") == -ENOENT)) { +		dev_err(qspi->dev, "spi-rx-bus-width\\/spi-tx-bus-width\\/cs-gpios\n"); +		dev_err(qspi->dev, "configuration not supported\n"); + +		return -EINVAL; +	} +  	ret = pm_runtime_resume_and_get(qspi->dev);  	if (ret < 0)  		return ret; @@ -580,6 +675,16 @@ static int stm32_qspi_setup(struct spi_device *spi)  	mutex_lock(&qspi->lock);  	qspi->cr_reg = CR_APMS | 3 << CR_FTHRES_SHIFT | CR_SSHIFT | CR_EN; + +	/* +	 * Dual flash mode is only enable in case SPI_TX_OCTAL and SPI_TX_OCTAL +	 * are both set in spi->mode and "cs-gpios" properties is found in DT +	 */ +	if (mode == (SPI_TX_OCTAL | SPI_RX_OCTAL)) { +		qspi->cr_reg |= CR_DFM; +		dev_dbg(qspi->dev, "Dual flash mode enable"); +	} +  	writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR);  	/* set dcr fsize to max address */ @@ -741,11 +846,13 @@ static int stm32_qspi_probe(struct platform_device *pdev)  	mutex_init(&qspi->lock); -	ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD -		| SPI_TX_DUAL | SPI_TX_QUAD; +	ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_OCTAL +		| SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_OCTAL;  	ctrl->setup = stm32_qspi_setup;  	ctrl->bus_num = -1;  	ctrl->mem_ops = &stm32_qspi_mem_ops; +	ctrl->use_gpio_descriptors = true; +	ctrl->transfer_one_message = stm32_qspi_transfer_one_message;  	ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP;  	ctrl->dev.of_node = dev->of_node;  |