File Index
Files and directories
Sign in
FILE

spi-offload.c

File preview with syntax highlighting.

linux-6.19.14/drivers/spi/spi-offload.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2024 Analog Devices Inc.
  4 * Copyright (C) 2024 BayLibre, SAS
  5 */
  6
  7/*
  8 * SPI Offloading support.
  9 *
 10 * Some SPI controllers support offloading of SPI transfers. Essentially, this
 11 * is the ability for a SPI controller to perform SPI transfers with minimal
 12 * or even no CPU intervention, e.g. via a specialized SPI controller with a
 13 * hardware trigger or via a conventional SPI controller using a non-Linux MCU
 14 * processor core to offload the work.
 15 */
 16
 17#define DEFAULT_SYMBOL_NAMESPACE "SPI_OFFLOAD"
 18
 19#include <linux/cleanup.h>
 20#include <linux/device.h>
 21#include <linux/dmaengine.h>
 22#include <linux/export.h>
 23#include <linux/kref.h>
 24#include <linux/list.h>
 25#include <linux/mutex.h>
 26#include <linux/of.h>
 27#include <linux/property.h>
 28#include <linux/spi/offload/consumer.h>
 29#include <linux/spi/offload/provider.h>
 30#include <linux/spi/offload/types.h>
 31#include <linux/spi/spi.h>
 32#include <linux/types.h>
 33
 34struct spi_controller_and_offload {
 35	struct spi_controller *controller;
 36	struct spi_offload *offload;
 37};
 38
 39struct spi_offload_trigger {
 40	struct list_head list;
 41	struct kref ref;
 42	struct fwnode_handle *fwnode;
 43	/* synchronizes calling ops and driver registration */
 44	struct mutex lock;
 45	/*
 46	 * If the provider goes away while the consumer still has a reference,
 47	 * ops and priv will be set to NULL and all calls will fail with -ENODEV.
 48	 */
 49	const struct spi_offload_trigger_ops *ops;
 50	void *priv;
 51};
 52
 53static LIST_HEAD(spi_offload_triggers);
 54static DEFINE_MUTEX(spi_offload_triggers_lock);
 55
 56/**
 57 * devm_spi_offload_alloc() - Allocate offload instance
 58 * @dev: Device for devm purposes and assigned to &struct spi_offload.provider_dev
 59 * @priv_size: Size of private data to allocate
 60 *
 61 * Offload providers should use this to allocate offload instances.
 62 *
 63 * Return: Pointer to new offload instance or error on failure.
 64 */
 65struct spi_offload *devm_spi_offload_alloc(struct device *dev,
 66					   size_t priv_size)
 67{
 68	struct spi_offload *offload;
 69	void *priv;
 70
 71	offload = devm_kzalloc(dev, sizeof(*offload), GFP_KERNEL);
 72	if (!offload)
 73		return ERR_PTR(-ENOMEM);
 74
 75	priv = devm_kzalloc(dev, priv_size, GFP_KERNEL);
 76	if (!priv)
 77		return ERR_PTR(-ENOMEM);
 78
 79	offload->provider_dev = dev;
 80	offload->priv = priv;
 81
 82	return offload;
 83}
 84EXPORT_SYMBOL_GPL(devm_spi_offload_alloc);
 85
 86static void spi_offload_put(void *data)
 87{
 88	struct spi_controller_and_offload *resource = data;
 89
 90	resource->controller->put_offload(resource->offload);
 91	kfree(resource);
 92}
 93
 94/**
 95 * devm_spi_offload_get() - Get an offload instance
 96 * @dev: Device for devm purposes
 97 * @spi: SPI device to use for the transfers
 98 * @config: Offload configuration
 99 *
100 * Peripheral drivers call this function to get an offload instance that meets
101 * the requirements specified in @config. If no suitable offload instance is
102 * available, -ENODEV is returned.
103 *
104 * Return: Offload instance or error on failure.
105 */
106struct spi_offload *devm_spi_offload_get(struct device *dev,
107					 struct spi_device *spi,
108					 const struct spi_offload_config *config)
109{
110	struct spi_controller_and_offload *resource;
111	struct spi_offload *offload;
112	int ret;
113
114	if (!spi || !config)
115		return ERR_PTR(-EINVAL);
116
117	if (!spi->controller->get_offload)
118		return ERR_PTR(-ENODEV);
119
120	resource = kzalloc(sizeof(*resource), GFP_KERNEL);
121	if (!resource)
122		return ERR_PTR(-ENOMEM);
123
124	offload = spi->controller->get_offload(spi, config);
125	if (IS_ERR(offload)) {
126		kfree(resource);
127		return offload;
128	}
129
130	resource->controller = spi->controller;
131	resource->offload = offload;
132
133	ret = devm_add_action_or_reset(dev, spi_offload_put, resource);
134	if (ret)
135		return ERR_PTR(ret);
136
137	return offload;
138}
139EXPORT_SYMBOL_GPL(devm_spi_offload_get);
140
141static void spi_offload_trigger_free(struct kref *ref)
142{
143	struct spi_offload_trigger *trigger =
144		container_of(ref, struct spi_offload_trigger, ref);
145
146	mutex_destroy(&trigger->lock);
147	fwnode_handle_put(trigger->fwnode);
148	kfree(trigger);
149}
150
151static void spi_offload_trigger_put(void *data)
152{
153	struct spi_offload_trigger *trigger = data;
154
155	scoped_guard(mutex, &trigger->lock)
156		if (trigger->ops && trigger->ops->release)
157			trigger->ops->release(trigger);
158
159	kref_put(&trigger->ref, spi_offload_trigger_free);
160}
161
162static struct spi_offload_trigger
163*spi_offload_trigger_get(enum spi_offload_trigger_type type,
164			 struct fwnode_reference_args *args)
165{
166	struct spi_offload_trigger *trigger;
167	bool match = false;
168	int ret;
169
170	guard(mutex)(&spi_offload_triggers_lock);
171
172	list_for_each_entry(trigger, &spi_offload_triggers, list) {
173		if (trigger->fwnode != args->fwnode)
174			continue;
175
176		match = trigger->ops->match(trigger, type, args->args, args->nargs);
177		if (match)
178			break;
179	}
180
181	if (!match)
182		return ERR_PTR(-EPROBE_DEFER);
183
184	guard(mutex)(&trigger->lock);
185
186	if (trigger->ops->request) {
187		ret = trigger->ops->request(trigger, type, args->args, args->nargs);
188		if (ret)
189			return ERR_PTR(ret);
190	}
191
192	kref_get(&trigger->ref);
193
194	return trigger;
195}
196
197/**
198 * devm_spi_offload_trigger_get() - Get an offload trigger instance
199 * @dev: Device for devm purposes.
200 * @offload: Offload instance connected to a trigger.
201 * @type: Trigger type to get.
202 *
203 * Return: Offload trigger instance or error on failure.
204 */
205struct spi_offload_trigger
206*devm_spi_offload_trigger_get(struct device *dev,
207			      struct spi_offload *offload,
208			      enum spi_offload_trigger_type type)
209{
210	struct spi_offload_trigger *trigger;
211	struct fwnode_reference_args args;
212	int ret;
213
214	ret = fwnode_property_get_reference_args(dev_fwnode(offload->provider_dev),
215						 "trigger-sources",
216						 "#trigger-source-cells", 0, 0,
217						 &args);
218	if (ret)
219		return ERR_PTR(ret);
220
221	trigger = spi_offload_trigger_get(type, &args);
222	fwnode_handle_put(args.fwnode);
223	if (IS_ERR(trigger))
224		return trigger;
225
226	ret = devm_add_action_or_reset(dev, spi_offload_trigger_put, trigger);
227	if (ret)
228		return ERR_PTR(ret);
229
230	return trigger;
231}
232EXPORT_SYMBOL_GPL(devm_spi_offload_trigger_get);
233
234/**
235 * spi_offload_trigger_validate - Validate the requested trigger
236 * @trigger: Offload trigger instance
237 * @config: Trigger config to validate
238 *
239 * On success, @config may be modifed to reflect what the hardware can do.
240 * For example, the frequency of a periodic trigger may be adjusted to the
241 * nearest supported value.
242 *
243 * Callers will likely need to do additional validation of the modified trigger
244 * parameters.
245 *
246 * Return: 0 on success, negative error code on failure.
247 */
248int spi_offload_trigger_validate(struct spi_offload_trigger *trigger,
249				 struct spi_offload_trigger_config *config)
250{
251	guard(mutex)(&trigger->lock);
252
253	if (!trigger->ops)
254		return -ENODEV;
255
256	if (!trigger->ops->validate)
257		return -EOPNOTSUPP;
258
259	return trigger->ops->validate(trigger, config);
260}
261EXPORT_SYMBOL_GPL(spi_offload_trigger_validate);
262
263/**
264 * spi_offload_trigger_enable - enables trigger for offload
265 * @offload: Offload instance
266 * @trigger: Offload trigger instance
267 * @config: Trigger config to validate
268 *
269 * There must be a prepared offload instance with the specified ID (i.e.
270 * spi_optimize_message() was called with the same offload assigned to the
271 * message). This will also reserve the bus for exclusive use by the offload
272 * instance until the trigger is disabled. Any other attempts to send a
273 * transfer or lock the bus will fail with -EBUSY during this time.
274 *
275 * Calls must be balanced with spi_offload_trigger_disable().
276 *
277 * Context: can sleep
278 * Return: 0 on success, else a negative error code.
279 */
280int spi_offload_trigger_enable(struct spi_offload *offload,
281			       struct spi_offload_trigger *trigger,
282			       struct spi_offload_trigger_config *config)
283{
284	int ret;
285
286	guard(mutex)(&trigger->lock);
287
288	if (!trigger->ops)
289		return -ENODEV;
290
291	if (offload->ops && offload->ops->trigger_enable) {
292		ret = offload->ops->trigger_enable(offload);
293		if (ret)
294			return ret;
295	}
296
297	if (trigger->ops->enable) {
298		ret = trigger->ops->enable(trigger, config);
299		if (ret) {
300			if (offload->ops && offload->ops->trigger_disable)
301				offload->ops->trigger_disable(offload);
302			return ret;
303		}
304	}
305
306	return 0;
307}
308EXPORT_SYMBOL_GPL(spi_offload_trigger_enable);
309
310/**
311 * spi_offload_trigger_disable - disables hardware trigger for offload
312 * @offload: Offload instance
313 * @trigger: Offload trigger instance
314 *
315 * Disables the hardware trigger for the offload instance with the specified ID
316 * and releases the bus for use by other clients.
317 *
318 * Context: can sleep
319 */
320void spi_offload_trigger_disable(struct spi_offload *offload,
321				 struct spi_offload_trigger *trigger)
322{
323	if (offload->ops && offload->ops->trigger_disable)
324		offload->ops->trigger_disable(offload);
325
326	guard(mutex)(&trigger->lock);
327
328	if (!trigger->ops)
329		return;
330
331	if (trigger->ops->disable)
332		trigger->ops->disable(trigger);
333}
334EXPORT_SYMBOL_GPL(spi_offload_trigger_disable);
335
336static void spi_offload_release_dma_chan(void *chan)
337{
338	dma_release_channel(chan);
339}
340
341/**
342 * devm_spi_offload_tx_stream_request_dma_chan - Get the DMA channel info for the TX stream
343 * @dev: Device for devm purposes.
344 * @offload: Offload instance
345 *
346 * This is the DMA channel that will provide data to transfers that use the
347 * %SPI_OFFLOAD_XFER_TX_STREAM offload flag.
348 *
349 * Return: Pointer to DMA channel info, or negative error code
350 */
351struct dma_chan
352*devm_spi_offload_tx_stream_request_dma_chan(struct device *dev,
353					     struct spi_offload *offload)
354{
355	struct dma_chan *chan;
356	int ret;
357
358	if (!offload->ops || !offload->ops->tx_stream_request_dma_chan)
359		return ERR_PTR(-EOPNOTSUPP);
360
361	chan = offload->ops->tx_stream_request_dma_chan(offload);
362	if (IS_ERR(chan))
363		return chan;
364
365	ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan);
366	if (ret)
367		return ERR_PTR(ret);
368
369	return chan;
370}
371EXPORT_SYMBOL_GPL(devm_spi_offload_tx_stream_request_dma_chan);
372
373/**
374 * devm_spi_offload_rx_stream_request_dma_chan - Get the DMA channel info for the RX stream
375 * @dev: Device for devm purposes.
376 * @offload: Offload instance
377 *
378 * This is the DMA channel that will receive data from transfers that use the
379 * %SPI_OFFLOAD_XFER_RX_STREAM offload flag.
380 *
381 * Return: Pointer to DMA channel info, or negative error code
382 */
383struct dma_chan
384*devm_spi_offload_rx_stream_request_dma_chan(struct device *dev,
385					     struct spi_offload *offload)
386{
387	struct dma_chan *chan;
388	int ret;
389
390	if (!offload->ops || !offload->ops->rx_stream_request_dma_chan)
391		return ERR_PTR(-EOPNOTSUPP);
392
393	chan = offload->ops->rx_stream_request_dma_chan(offload);
394	if (IS_ERR(chan))
395		return chan;
396
397	ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan);
398	if (ret)
399		return ERR_PTR(ret);
400
401	return chan;
402}
403EXPORT_SYMBOL_GPL(devm_spi_offload_rx_stream_request_dma_chan);
404
405/* Triggers providers */
406
407static void spi_offload_trigger_unregister(void *data)
408{
409	struct spi_offload_trigger *trigger = data;
410
411	scoped_guard(mutex, &spi_offload_triggers_lock)
412		list_del(&trigger->list);
413
414	scoped_guard(mutex, &trigger->lock) {
415		trigger->priv = NULL;
416		trigger->ops = NULL;
417	}
418
419	kref_put(&trigger->ref, spi_offload_trigger_free);
420}
421
422/**
423 * devm_spi_offload_trigger_register() - Allocate and register an offload trigger
424 * @dev: Device for devm purposes.
425 * @info: Provider-specific trigger info.
426 *
427 * Return: 0 on success, else a negative error code.
428 */
429int devm_spi_offload_trigger_register(struct device *dev,
430				      struct spi_offload_trigger_info *info)
431{
432	struct spi_offload_trigger *trigger;
433
434	if (!info->fwnode || !info->ops || !info->ops->match)
435		return -EINVAL;
436
437	trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
438	if (!trigger)
439		return -ENOMEM;
440
441	kref_init(&trigger->ref);
442	mutex_init(&trigger->lock);
443	trigger->fwnode = fwnode_handle_get(info->fwnode);
444	trigger->ops = info->ops;
445	trigger->priv = info->priv;
446
447	scoped_guard(mutex, &spi_offload_triggers_lock)
448		list_add_tail(&trigger->list, &spi_offload_triggers);
449
450	return devm_add_action_or_reset(dev, spi_offload_trigger_unregister, trigger);
451}
452EXPORT_SYMBOL_GPL(devm_spi_offload_trigger_register);
453
454/**
455 * spi_offload_trigger_get_priv() - Get the private data for the trigger
456 *
457 * @trigger: Offload trigger instance.
458 *
459 * Return: Private data for the trigger.
460 */
461void *spi_offload_trigger_get_priv(struct spi_offload_trigger *trigger)
462{
463	return trigger->priv;
464}
465EXPORT_SYMBOL_GPL(spi_offload_trigger_get_priv);
~/ Read-only file preview.