diff mbox

[v4,2/7] spi: Make support for regular transfers optional when ->mem_ops != NULL

Message ID 20180426161820.2852-3-boris.brezillon@bootlin.com (mailing list archive)
State Accepted
Headers show

Commit Message

Boris Brezillon April 26, 2018, 4:18 p.m. UTC
Some SPI/QuadSPI controllers only expose a high-level SPI memory
interface, thus preventing any regular SPI transfers from being done.

In that case, SPI controller drivers can leave all ->transfer_xxx()
hooks empty and only implement the spi_mem_ops interface.

Adjust the core to allow such situations:
- extend spi_controller_check_ops() to accept situations where all
  ->transfer_xxx() pointers are NULL only if ->mem_ops != NULL
- make sure we do not initialize the SPI message queue if
  ctlr->transfer_one and ctlr->transfer_one_message are missing
- return -ENOTSUPP if someone tries to do a regular SPI transfer on
  a controller that does not support it

Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
---
Changes in v4:
- none

Changes in v3:
- none

Changes in v2:
- patch added in v2
---
 drivers/spi/spi.c | 33 ++++++++++++++++++++++++++-------
 1 file changed, 26 insertions(+), 7 deletions(-)

Comments

Frieder Schrempf May 3, 2018, 6:40 p.m. UTC | #1
On 26.04.2018 18:18, Boris Brezillon wrote:
> Some SPI/QuadSPI controllers only expose a high-level SPI memory
> interface, thus preventing any regular SPI transfers from being done.
> 
> In that case, SPI controller drivers can leave all ->transfer_xxx()
> hooks empty and only implement the spi_mem_ops interface.
> 
> Adjust the core to allow such situations:
> - extend spi_controller_check_ops() to accept situations where all
>    ->transfer_xxx() pointers are NULL only if ->mem_ops != NULL
> - make sure we do not initialize the SPI message queue if
>    ctlr->transfer_one and ctlr->transfer_one_message are missing
> - return -ENOTSUPP if someone tries to do a regular SPI transfer on
>    a controller that does not support it
> 
> Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>

Reviewed-by: Frieder Schrempf <frieder.schrempf@exceet.de>
Tested-by: Frieder Schrempf <frieder.schrempf@exceet.de>

Regards,

Frieder

> ---
> Changes in v4:
> - none
> 
> Changes in v3:
> - none
> 
> Changes in v2:
> - patch added in v2
> ---
>   drivers/spi/spi.c | 33 ++++++++++++++++++++++++++-------
>   1 file changed, 26 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index 9ab65fb2738e..c85b0cf7b4a9 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -28,6 +28,7 @@
>   #include <linux/slab.h>
>   #include <linux/mod_devicetable.h>
>   #include <linux/spi/spi.h>
> +#include <linux/spi/spi-mem.h>
>   #include <linux/of_gpio.h>
>   #include <linux/pm_runtime.h>
>   #include <linux/pm_domain.h>
> @@ -2071,12 +2072,19 @@ static int of_spi_register_master(struct spi_controller *ctlr)
>   static int spi_controller_check_ops(struct spi_controller *ctlr)
>   {
>   	/*
> -	 * The controller must at least implement one of the ->transfer()
> -	 * hooks.
> +	 * The controller may implement only the high-level SPI-memory like
> +	 * operations if it does not support regular SPI transfers, and this is
> +	 * valid use case.
> +	 * If ->mem_ops is NULL, we request that at least one of the
> +	 * ->transfer_xxx() method be implemented.
>   	 */
> -	if (!ctlr->transfer && !ctlr->transfer_one &&
> -	    !ctlr->transfer_one_message)
> +	if (ctlr->mem_ops) {
> +		if (!ctlr->mem_ops->exec_op)
> +			return -EINVAL;
> +	} else if (!ctlr->transfer && !ctlr->transfer_one &&
> +		   !ctlr->transfer_one_message) {
>   		return -EINVAL;
> +	}
>   
>   	return 0;
>   }
> @@ -2187,10 +2195,14 @@ int spi_register_controller(struct spi_controller *ctlr)
>   			spi_controller_is_slave(ctlr) ? "slave" : "master",
>   			dev_name(&ctlr->dev));
>   
> -	/* If we're using a queued driver, start the queue */
> -	if (ctlr->transfer)
> +	/*
> +	 * If we're using a queued driver, start the queue. Note that we don't
> +	 * need the queueing logic if the driver is only supporting high-level
> +	 * memory operations.
> +	 */
> +	if (ctlr->transfer) {
>   		dev_info(dev, "controller is unqueued, this is deprecated\n");
> -	else {
> +	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
>   		status = spi_controller_initialize_queue(ctlr);
>   		if (status) {
>   			device_del(&ctlr->dev);
> @@ -2920,6 +2932,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
>   {
>   	struct spi_controller *ctlr = spi->controller;
>   
> +	/*
> +	 * Some controllers do not support doing regular SPI transfers. Return
> +	 * ENOTSUPP when this is the case.
> +	 */
> +	if (!ctlr->transfer)
> +		return -ENOTSUPP;
> +
>   	message->spi = spi;
>   
>   	SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 9ab65fb2738e..c85b0cf7b4a9 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -28,6 +28,7 @@ 
 #include <linux/slab.h>
 #include <linux/mod_devicetable.h>
 #include <linux/spi/spi.h>
+#include <linux/spi/spi-mem.h>
 #include <linux/of_gpio.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_domain.h>
@@ -2071,12 +2072,19 @@  static int of_spi_register_master(struct spi_controller *ctlr)
 static int spi_controller_check_ops(struct spi_controller *ctlr)
 {
 	/*
-	 * The controller must at least implement one of the ->transfer()
-	 * hooks.
+	 * The controller may implement only the high-level SPI-memory like
+	 * operations if it does not support regular SPI transfers, and this is
+	 * valid use case.
+	 * If ->mem_ops is NULL, we request that at least one of the
+	 * ->transfer_xxx() method be implemented.
 	 */
-	if (!ctlr->transfer && !ctlr->transfer_one &&
-	    !ctlr->transfer_one_message)
+	if (ctlr->mem_ops) {
+		if (!ctlr->mem_ops->exec_op)
+			return -EINVAL;
+	} else if (!ctlr->transfer && !ctlr->transfer_one &&
+		   !ctlr->transfer_one_message) {
 		return -EINVAL;
+	}
 
 	return 0;
 }
@@ -2187,10 +2195,14 @@  int spi_register_controller(struct spi_controller *ctlr)
 			spi_controller_is_slave(ctlr) ? "slave" : "master",
 			dev_name(&ctlr->dev));
 
-	/* If we're using a queued driver, start the queue */
-	if (ctlr->transfer)
+	/*
+	 * If we're using a queued driver, start the queue. Note that we don't
+	 * need the queueing logic if the driver is only supporting high-level
+	 * memory operations.
+	 */
+	if (ctlr->transfer) {
 		dev_info(dev, "controller is unqueued, this is deprecated\n");
-	else {
+	} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
 		status = spi_controller_initialize_queue(ctlr);
 		if (status) {
 			device_del(&ctlr->dev);
@@ -2920,6 +2932,13 @@  static int __spi_async(struct spi_device *spi, struct spi_message *message)
 {
 	struct spi_controller *ctlr = spi->controller;
 
+	/*
+	 * Some controllers do not support doing regular SPI transfers. Return
+	 * ENOTSUPP when this is the case.
+	 */
+	if (!ctlr->transfer)
+		return -ENOTSUPP;
+
 	message->spi = spi;
 
 	SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);