xref: /linux/drivers/spi/spi-offload.c (revision 8e02d188698851436f76038ea998b726193d1b10)
1*8e02d188SDavid Lechner // SPDX-License-Identifier: GPL-2.0-only
2*8e02d188SDavid Lechner /*
3*8e02d188SDavid Lechner  * Copyright (C) 2024 Analog Devices Inc.
4*8e02d188SDavid Lechner  * Copyright (C) 2024 BayLibre, SAS
5*8e02d188SDavid Lechner  */
6*8e02d188SDavid Lechner 
7*8e02d188SDavid Lechner /*
8*8e02d188SDavid Lechner  * SPI Offloading support.
9*8e02d188SDavid Lechner  *
10*8e02d188SDavid Lechner  * Some SPI controllers support offloading of SPI transfers. Essentially, this
11*8e02d188SDavid Lechner  * is the ability for a SPI controller to perform SPI transfers with minimal
12*8e02d188SDavid Lechner  * or even no CPU intervention, e.g. via a specialized SPI controller with a
13*8e02d188SDavid Lechner  * hardware trigger or via a conventional SPI controller using a non-Linux MCU
14*8e02d188SDavid Lechner  * processor core to offload the work.
15*8e02d188SDavid Lechner  */
16*8e02d188SDavid Lechner 
17*8e02d188SDavid Lechner #define DEFAULT_SYMBOL_NAMESPACE "SPI_OFFLOAD"
18*8e02d188SDavid Lechner 
19*8e02d188SDavid Lechner #include <linux/cleanup.h>
20*8e02d188SDavid Lechner #include <linux/device.h>
21*8e02d188SDavid Lechner #include <linux/export.h>
22*8e02d188SDavid Lechner #include <linux/mutex.h>
23*8e02d188SDavid Lechner #include <linux/spi/offload/consumer.h>
24*8e02d188SDavid Lechner #include <linux/spi/offload/provider.h>
25*8e02d188SDavid Lechner #include <linux/spi/offload/types.h>
26*8e02d188SDavid Lechner #include <linux/spi/spi.h>
27*8e02d188SDavid Lechner #include <linux/types.h>
28*8e02d188SDavid Lechner 
29*8e02d188SDavid Lechner struct spi_controller_and_offload {
30*8e02d188SDavid Lechner 	struct spi_controller *controller;
31*8e02d188SDavid Lechner 	struct spi_offload *offload;
32*8e02d188SDavid Lechner };
33*8e02d188SDavid Lechner 
34*8e02d188SDavid Lechner /**
35*8e02d188SDavid Lechner  * devm_spi_offload_alloc() - Allocate offload instance
36*8e02d188SDavid Lechner  * @dev: Device for devm purposes and assigned to &struct spi_offload.provider_dev
37*8e02d188SDavid Lechner  * @priv_size: Size of private data to allocate
38*8e02d188SDavid Lechner  *
39*8e02d188SDavid Lechner  * Offload providers should use this to allocate offload instances.
40*8e02d188SDavid Lechner  *
41*8e02d188SDavid Lechner  * Return: Pointer to new offload instance or error on failure.
42*8e02d188SDavid Lechner  */
43*8e02d188SDavid Lechner struct spi_offload *devm_spi_offload_alloc(struct device *dev,
44*8e02d188SDavid Lechner 					   size_t priv_size)
45*8e02d188SDavid Lechner {
46*8e02d188SDavid Lechner 	struct spi_offload *offload;
47*8e02d188SDavid Lechner 	void *priv;
48*8e02d188SDavid Lechner 
49*8e02d188SDavid Lechner 	offload = devm_kzalloc(dev, sizeof(*offload), GFP_KERNEL);
50*8e02d188SDavid Lechner 	if (!offload)
51*8e02d188SDavid Lechner 		return ERR_PTR(-ENOMEM);
52*8e02d188SDavid Lechner 
53*8e02d188SDavid Lechner 	priv = devm_kzalloc(dev, priv_size, GFP_KERNEL);
54*8e02d188SDavid Lechner 	if (!priv)
55*8e02d188SDavid Lechner 		return ERR_PTR(-ENOMEM);
56*8e02d188SDavid Lechner 
57*8e02d188SDavid Lechner 	offload->provider_dev = dev;
58*8e02d188SDavid Lechner 	offload->priv = priv;
59*8e02d188SDavid Lechner 
60*8e02d188SDavid Lechner 	return offload;
61*8e02d188SDavid Lechner }
62*8e02d188SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_alloc);
63*8e02d188SDavid Lechner 
64*8e02d188SDavid Lechner static void spi_offload_put(void *data)
65*8e02d188SDavid Lechner {
66*8e02d188SDavid Lechner 	struct spi_controller_and_offload *resource = data;
67*8e02d188SDavid Lechner 
68*8e02d188SDavid Lechner 	resource->controller->put_offload(resource->offload);
69*8e02d188SDavid Lechner 	kfree(resource);
70*8e02d188SDavid Lechner }
71*8e02d188SDavid Lechner 
72*8e02d188SDavid Lechner /**
73*8e02d188SDavid Lechner  * devm_spi_offload_get() - Get an offload instance
74*8e02d188SDavid Lechner  * @dev: Device for devm purposes
75*8e02d188SDavid Lechner  * @spi: SPI device to use for the transfers
76*8e02d188SDavid Lechner  * @config: Offload configuration
77*8e02d188SDavid Lechner  *
78*8e02d188SDavid Lechner  * Peripheral drivers call this function to get an offload instance that meets
79*8e02d188SDavid Lechner  * the requirements specified in @config. If no suitable offload instance is
80*8e02d188SDavid Lechner  * available, -ENODEV is returned.
81*8e02d188SDavid Lechner  *
82*8e02d188SDavid Lechner  * Return: Offload instance or error on failure.
83*8e02d188SDavid Lechner  */
84*8e02d188SDavid Lechner struct spi_offload *devm_spi_offload_get(struct device *dev,
85*8e02d188SDavid Lechner 					 struct spi_device *spi,
86*8e02d188SDavid Lechner 					 const struct spi_offload_config *config)
87*8e02d188SDavid Lechner {
88*8e02d188SDavid Lechner 	struct spi_controller_and_offload *resource;
89*8e02d188SDavid Lechner 	int ret;
90*8e02d188SDavid Lechner 
91*8e02d188SDavid Lechner 	if (!spi || !config)
92*8e02d188SDavid Lechner 		return ERR_PTR(-EINVAL);
93*8e02d188SDavid Lechner 
94*8e02d188SDavid Lechner 	if (!spi->controller->get_offload)
95*8e02d188SDavid Lechner 		return ERR_PTR(-ENODEV);
96*8e02d188SDavid Lechner 
97*8e02d188SDavid Lechner 	resource = kzalloc(sizeof(*resource), GFP_KERNEL);
98*8e02d188SDavid Lechner 	if (!resource)
99*8e02d188SDavid Lechner 		return ERR_PTR(-ENOMEM);
100*8e02d188SDavid Lechner 
101*8e02d188SDavid Lechner 	resource->controller = spi->controller;
102*8e02d188SDavid Lechner 	resource->offload = spi->controller->get_offload(spi, config);
103*8e02d188SDavid Lechner 	if (IS_ERR(resource->offload)) {
104*8e02d188SDavid Lechner 		kfree(resource);
105*8e02d188SDavid Lechner 		return resource->offload;
106*8e02d188SDavid Lechner 	}
107*8e02d188SDavid Lechner 
108*8e02d188SDavid Lechner 	ret = devm_add_action_or_reset(dev, spi_offload_put, resource);
109*8e02d188SDavid Lechner 	if (ret)
110*8e02d188SDavid Lechner 		return ERR_PTR(ret);
111*8e02d188SDavid Lechner 
112*8e02d188SDavid Lechner 	return resource->offload;
113*8e02d188SDavid Lechner }
114*8e02d188SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_get);
115