Unverified Commit 8e02d188 authored by David Lechner's avatar David Lechner Committed by Mark Brown
Browse files

spi: add basic support for SPI offloading



Add the basic infrastructure to support SPI offload providers and
consumers.

SPI offloading is a feature that allows the SPI controller to perform
transfers without any CPU intervention. This is useful, e.g. for
high-speed data acquisition.

SPI controllers with offload support need to implement the get_offload
and put_offload callbacks and can use the devm_spi_offload_alloc() to
allocate offload instances.

SPI peripheral drivers will call devm_spi_offload_get() to get a
reference to the matching offload instance. This offload instance can
then be attached to a SPI message to request offloading that message.

It is expected that SPI controllers with offload support will check for
the offload instance in the SPI message in the ctlr->optimize_message()
callback and handle it accordingly.

CONFIG_SPI_OFFLOAD is intended to be a select-only option. Both
consumer and provider drivers should `select SPI_OFFLOAD` in their
Kconfig to ensure that the SPI core is built with offload support.

Reviewed-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: default avatarNuno Sa <nuno.sa@analog.com>
Signed-off-by: default avatarDavid Lechner <dlechner@baylibre.com>
Link: https://patch.msgid.link/20250207-dlech-mainline-spi-engine-offload-2-v8-1-e48a489be48c@baylibre.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 2014c95a
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -22295,6 +22295,12 @@ F: Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml
F:	drivers/mtd/spi-nor/
F:	include/linux/mtd/spi-nor.h
SPI OFFLOAD
R:	David Lechner <dlechner@baylibre.com>
F:	drivers/spi/spi-offload.c
F:	include/linux/spi/spi-offload.h
K:	spi_offload
SPI SUBSYSTEM
M:	Mark Brown <broonie@kernel.org>
L:	linux-spi@vger.kernel.org
+3 −0
Original line number Diff line number Diff line
@@ -55,6 +55,9 @@ config SPI_MEM
	  This extension is meant to simplify interaction with SPI memories
	  by providing a high-level interface to send memory-like commands.

config SPI_OFFLOAD
	bool

comment "SPI Master Controller Drivers"

config SPI_AIROHA_SNFI
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
obj-$(CONFIG_SPI_MASTER)		+= spi.o
obj-$(CONFIG_SPI_MEM)			+= spi-mem.o
obj-$(CONFIG_SPI_MUX)			+= spi-mux.o
obj-$(CONFIG_SPI_OFFLOAD)		+= spi-offload.o
obj-$(CONFIG_SPI_SPIDEV)		+= spidev.o
obj-$(CONFIG_SPI_LOOPBACK_TEST)		+= spi-loopback-test.o

+114 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2024 Analog Devices Inc.
 * Copyright (C) 2024 BayLibre, SAS
 */

/*
 * SPI Offloading support.
 *
 * Some SPI controllers support offloading of SPI transfers. Essentially, this
 * is the ability for a SPI controller to perform SPI transfers with minimal
 * or even no CPU intervention, e.g. via a specialized SPI controller with a
 * hardware trigger or via a conventional SPI controller using a non-Linux MCU
 * processor core to offload the work.
 */

#define DEFAULT_SYMBOL_NAMESPACE "SPI_OFFLOAD"

#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/spi/offload/consumer.h>
#include <linux/spi/offload/provider.h>
#include <linux/spi/offload/types.h>
#include <linux/spi/spi.h>
#include <linux/types.h>

struct spi_controller_and_offload {
	struct spi_controller *controller;
	struct spi_offload *offload;
};

/**
 * devm_spi_offload_alloc() - Allocate offload instance
 * @dev: Device for devm purposes and assigned to &struct spi_offload.provider_dev
 * @priv_size: Size of private data to allocate
 *
 * Offload providers should use this to allocate offload instances.
 *
 * Return: Pointer to new offload instance or error on failure.
 */
struct spi_offload *devm_spi_offload_alloc(struct device *dev,
					   size_t priv_size)
{
	struct spi_offload *offload;
	void *priv;

	offload = devm_kzalloc(dev, sizeof(*offload), GFP_KERNEL);
	if (!offload)
		return ERR_PTR(-ENOMEM);

	priv = devm_kzalloc(dev, priv_size, GFP_KERNEL);
	if (!priv)
		return ERR_PTR(-ENOMEM);

	offload->provider_dev = dev;
	offload->priv = priv;

	return offload;
}
EXPORT_SYMBOL_GPL(devm_spi_offload_alloc);

static void spi_offload_put(void *data)
{
	struct spi_controller_and_offload *resource = data;

	resource->controller->put_offload(resource->offload);
	kfree(resource);
}

/**
 * devm_spi_offload_get() - Get an offload instance
 * @dev: Device for devm purposes
 * @spi: SPI device to use for the transfers
 * @config: Offload configuration
 *
 * Peripheral drivers call this function to get an offload instance that meets
 * the requirements specified in @config. If no suitable offload instance is
 * available, -ENODEV is returned.
 *
 * Return: Offload instance or error on failure.
 */
struct spi_offload *devm_spi_offload_get(struct device *dev,
					 struct spi_device *spi,
					 const struct spi_offload_config *config)
{
	struct spi_controller_and_offload *resource;
	int ret;

	if (!spi || !config)
		return ERR_PTR(-EINVAL);

	if (!spi->controller->get_offload)
		return ERR_PTR(-ENODEV);

	resource = kzalloc(sizeof(*resource), GFP_KERNEL);
	if (!resource)
		return ERR_PTR(-ENOMEM);

	resource->controller = spi->controller;
	resource->offload = spi->controller->get_offload(spi, config);
	if (IS_ERR(resource->offload)) {
		kfree(resource);
		return resource->offload;
	}

	ret = devm_add_action_or_reset(dev, spi_offload_put, resource);
	if (ret)
		return ERR_PTR(ret);

	return resource->offload;
}
EXPORT_SYMBOL_GPL(devm_spi_offload_get);
+22 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2024 Analog Devices Inc.
 * Copyright (C) 2024 BayLibre, SAS
 */

#ifndef __LINUX_SPI_OFFLOAD_CONSUMER_H
#define __LINUX_SPI_OFFLOAD_CONSUMER_H

#include <linux/module.h>
#include <linux/spi/offload/types.h>
#include <linux/types.h>

MODULE_IMPORT_NS("SPI_OFFLOAD");

struct device;
struct spi_device;

struct spi_offload *devm_spi_offload_get(struct device *dev, struct spi_device *spi,
					 const struct spi_offload_config *config);

#endif /* __LINUX_SPI_OFFLOAD_CONSUMER_H */
Loading