aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi.c
diff options
context:
space:
mode:
authorBoris Brezillon <boris.brezillon@bootlin.com>2018-04-26 18:18:15 +0200
committerMark Brown <broonie@kernel.org>2018-05-11 11:33:15 +0900
commitb5932f5c68e5fbe74fb7a27b4d0faf919e1e1642 (patch)
treeca5457a85c7597e715a21c9446c4aee24c0a4174 /drivers/spi/spi.c
parentspi: Extend the core to ease integration of SPI memory controllers (diff)
downloadlinux-dev-b5932f5c68e5fbe74fb7a27b4d0faf919e1e1642.tar.xz
linux-dev-b5932f5c68e5fbe74fb7a27b4d0faf919e1e1642.zip
spi: Make support for regular transfers optional when ->mem_ops != NULL
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> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi/spi.c')
-rw-r--r--drivers/spi/spi.c33
1 files 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);