xref: /linux/drivers/spi/spi-offload.c (revision 1260ed77798502de9c98020040d2995008de10cc)
18e02d188SDavid Lechner // SPDX-License-Identifier: GPL-2.0-only
28e02d188SDavid Lechner /*
38e02d188SDavid Lechner  * Copyright (C) 2024 Analog Devices Inc.
48e02d188SDavid Lechner  * Copyright (C) 2024 BayLibre, SAS
58e02d188SDavid Lechner  */
68e02d188SDavid Lechner 
78e02d188SDavid Lechner /*
88e02d188SDavid Lechner  * SPI Offloading support.
98e02d188SDavid Lechner  *
108e02d188SDavid Lechner  * Some SPI controllers support offloading of SPI transfers. Essentially, this
118e02d188SDavid Lechner  * is the ability for a SPI controller to perform SPI transfers with minimal
128e02d188SDavid Lechner  * or even no CPU intervention, e.g. via a specialized SPI controller with a
138e02d188SDavid Lechner  * hardware trigger or via a conventional SPI controller using a non-Linux MCU
148e02d188SDavid Lechner  * processor core to offload the work.
158e02d188SDavid Lechner  */
168e02d188SDavid Lechner 
178e02d188SDavid Lechner #define DEFAULT_SYMBOL_NAMESPACE "SPI_OFFLOAD"
188e02d188SDavid Lechner 
198e02d188SDavid Lechner #include <linux/cleanup.h>
208e02d188SDavid Lechner #include <linux/device.h>
21700a2819SDavid Lechner #include <linux/dmaengine.h>
228e02d188SDavid Lechner #include <linux/export.h>
23d7231be4SDavid Lechner #include <linux/kref.h>
24d7231be4SDavid Lechner #include <linux/list.h>
258e02d188SDavid Lechner #include <linux/mutex.h>
26d7231be4SDavid Lechner #include <linux/of.h>
27d7231be4SDavid Lechner #include <linux/property.h>
288e02d188SDavid Lechner #include <linux/spi/offload/consumer.h>
298e02d188SDavid Lechner #include <linux/spi/offload/provider.h>
308e02d188SDavid Lechner #include <linux/spi/offload/types.h>
318e02d188SDavid Lechner #include <linux/spi/spi.h>
328e02d188SDavid Lechner #include <linux/types.h>
338e02d188SDavid Lechner 
348e02d188SDavid Lechner struct spi_controller_and_offload {
358e02d188SDavid Lechner 	struct spi_controller *controller;
368e02d188SDavid Lechner 	struct spi_offload *offload;
378e02d188SDavid Lechner };
388e02d188SDavid Lechner 
39d7231be4SDavid Lechner struct spi_offload_trigger {
40d7231be4SDavid Lechner 	struct list_head list;
41d7231be4SDavid Lechner 	struct kref ref;
42d7231be4SDavid Lechner 	struct fwnode_handle *fwnode;
43d7231be4SDavid Lechner 	/* synchronizes calling ops and driver registration */
44d7231be4SDavid Lechner 	struct mutex lock;
45d7231be4SDavid Lechner 	/*
46d7231be4SDavid Lechner 	 * If the provider goes away while the consumer still has a reference,
47d7231be4SDavid Lechner 	 * ops and priv will be set to NULL and all calls will fail with -ENODEV.
48d7231be4SDavid Lechner 	 */
49d7231be4SDavid Lechner 	const struct spi_offload_trigger_ops *ops;
50d7231be4SDavid Lechner 	void *priv;
51d7231be4SDavid Lechner };
52d7231be4SDavid Lechner 
53d7231be4SDavid Lechner static LIST_HEAD(spi_offload_triggers);
54d7231be4SDavid Lechner static DEFINE_MUTEX(spi_offload_triggers_lock);
55d7231be4SDavid Lechner 
568e02d188SDavid Lechner /**
578e02d188SDavid Lechner  * devm_spi_offload_alloc() - Allocate offload instance
588e02d188SDavid Lechner  * @dev: Device for devm purposes and assigned to &struct spi_offload.provider_dev
598e02d188SDavid Lechner  * @priv_size: Size of private data to allocate
608e02d188SDavid Lechner  *
618e02d188SDavid Lechner  * Offload providers should use this to allocate offload instances.
628e02d188SDavid Lechner  *
638e02d188SDavid Lechner  * Return: Pointer to new offload instance or error on failure.
648e02d188SDavid Lechner  */
658e02d188SDavid Lechner struct spi_offload *devm_spi_offload_alloc(struct device *dev,
668e02d188SDavid Lechner 					   size_t priv_size)
678e02d188SDavid Lechner {
688e02d188SDavid Lechner 	struct spi_offload *offload;
698e02d188SDavid Lechner 	void *priv;
708e02d188SDavid Lechner 
718e02d188SDavid Lechner 	offload = devm_kzalloc(dev, sizeof(*offload), GFP_KERNEL);
728e02d188SDavid Lechner 	if (!offload)
738e02d188SDavid Lechner 		return ERR_PTR(-ENOMEM);
748e02d188SDavid Lechner 
758e02d188SDavid Lechner 	priv = devm_kzalloc(dev, priv_size, GFP_KERNEL);
768e02d188SDavid Lechner 	if (!priv)
778e02d188SDavid Lechner 		return ERR_PTR(-ENOMEM);
788e02d188SDavid Lechner 
798e02d188SDavid Lechner 	offload->provider_dev = dev;
808e02d188SDavid Lechner 	offload->priv = priv;
818e02d188SDavid Lechner 
828e02d188SDavid Lechner 	return offload;
838e02d188SDavid Lechner }
848e02d188SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_alloc);
858e02d188SDavid Lechner 
868e02d188SDavid Lechner static void spi_offload_put(void *data)
878e02d188SDavid Lechner {
888e02d188SDavid Lechner 	struct spi_controller_and_offload *resource = data;
898e02d188SDavid Lechner 
908e02d188SDavid Lechner 	resource->controller->put_offload(resource->offload);
918e02d188SDavid Lechner 	kfree(resource);
928e02d188SDavid Lechner }
938e02d188SDavid Lechner 
948e02d188SDavid Lechner /**
958e02d188SDavid Lechner  * devm_spi_offload_get() - Get an offload instance
968e02d188SDavid Lechner  * @dev: Device for devm purposes
978e02d188SDavid Lechner  * @spi: SPI device to use for the transfers
988e02d188SDavid Lechner  * @config: Offload configuration
998e02d188SDavid Lechner  *
1008e02d188SDavid Lechner  * Peripheral drivers call this function to get an offload instance that meets
1018e02d188SDavid Lechner  * the requirements specified in @config. If no suitable offload instance is
1028e02d188SDavid Lechner  * available, -ENODEV is returned.
1038e02d188SDavid Lechner  *
1048e02d188SDavid Lechner  * Return: Offload instance or error on failure.
1058e02d188SDavid Lechner  */
1068e02d188SDavid Lechner struct spi_offload *devm_spi_offload_get(struct device *dev,
1078e02d188SDavid Lechner 					 struct spi_device *spi,
1088e02d188SDavid Lechner 					 const struct spi_offload_config *config)
1098e02d188SDavid Lechner {
1108e02d188SDavid Lechner 	struct spi_controller_and_offload *resource;
111*e957c964SDavid Lechner 	struct spi_offload *offload;
1128e02d188SDavid Lechner 	int ret;
1138e02d188SDavid Lechner 
1148e02d188SDavid Lechner 	if (!spi || !config)
1158e02d188SDavid Lechner 		return ERR_PTR(-EINVAL);
1168e02d188SDavid Lechner 
1178e02d188SDavid Lechner 	if (!spi->controller->get_offload)
1188e02d188SDavid Lechner 		return ERR_PTR(-ENODEV);
1198e02d188SDavid Lechner 
1208e02d188SDavid Lechner 	resource = kzalloc(sizeof(*resource), GFP_KERNEL);
1218e02d188SDavid Lechner 	if (!resource)
1228e02d188SDavid Lechner 		return ERR_PTR(-ENOMEM);
1238e02d188SDavid Lechner 
124*e957c964SDavid Lechner 	offload = spi->controller->get_offload(spi, config);
125*e957c964SDavid Lechner 	if (IS_ERR(offload)) {
1268e02d188SDavid Lechner 		kfree(resource);
127*e957c964SDavid Lechner 		return offload;
1288e02d188SDavid Lechner 	}
1298e02d188SDavid Lechner 
130*e957c964SDavid Lechner 	resource->controller = spi->controller;
131*e957c964SDavid Lechner 	resource->offload = offload;
132*e957c964SDavid Lechner 
1338e02d188SDavid Lechner 	ret = devm_add_action_or_reset(dev, spi_offload_put, resource);
1348e02d188SDavid Lechner 	if (ret)
1358e02d188SDavid Lechner 		return ERR_PTR(ret);
1368e02d188SDavid Lechner 
137*e957c964SDavid Lechner 	return offload;
1388e02d188SDavid Lechner }
1398e02d188SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_get);
140d7231be4SDavid Lechner 
141d7231be4SDavid Lechner static void spi_offload_trigger_free(struct kref *ref)
142d7231be4SDavid Lechner {
143d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger =
144d7231be4SDavid Lechner 		container_of(ref, struct spi_offload_trigger, ref);
145d7231be4SDavid Lechner 
146d7231be4SDavid Lechner 	mutex_destroy(&trigger->lock);
147d7231be4SDavid Lechner 	fwnode_handle_put(trigger->fwnode);
148d7231be4SDavid Lechner 	kfree(trigger);
149d7231be4SDavid Lechner }
150d7231be4SDavid Lechner 
151d7231be4SDavid Lechner static void spi_offload_trigger_put(void *data)
152d7231be4SDavid Lechner {
153d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger = data;
154d7231be4SDavid Lechner 
155d7231be4SDavid Lechner 	scoped_guard(mutex, &trigger->lock)
156d7231be4SDavid Lechner 		if (trigger->ops && trigger->ops->release)
157d7231be4SDavid Lechner 			trigger->ops->release(trigger);
158d7231be4SDavid Lechner 
159d7231be4SDavid Lechner 	kref_put(&trigger->ref, spi_offload_trigger_free);
160d7231be4SDavid Lechner }
161d7231be4SDavid Lechner 
162d7231be4SDavid Lechner static struct spi_offload_trigger
163d7231be4SDavid Lechner *spi_offload_trigger_get(enum spi_offload_trigger_type type,
164d7231be4SDavid Lechner 			 struct fwnode_reference_args *args)
165d7231be4SDavid Lechner {
166d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger;
167d7231be4SDavid Lechner 	bool match = false;
168d7231be4SDavid Lechner 	int ret;
169d7231be4SDavid Lechner 
170d7231be4SDavid Lechner 	guard(mutex)(&spi_offload_triggers_lock);
171d7231be4SDavid Lechner 
172d7231be4SDavid Lechner 	list_for_each_entry(trigger, &spi_offload_triggers, list) {
173d7231be4SDavid Lechner 		if (trigger->fwnode != args->fwnode)
174d7231be4SDavid Lechner 			continue;
175d7231be4SDavid Lechner 
176d7231be4SDavid Lechner 		match = trigger->ops->match(trigger, type, args->args, args->nargs);
177d7231be4SDavid Lechner 		if (match)
178d7231be4SDavid Lechner 			break;
179d7231be4SDavid Lechner 	}
180d7231be4SDavid Lechner 
181d7231be4SDavid Lechner 	if (!match)
182d7231be4SDavid Lechner 		return ERR_PTR(-EPROBE_DEFER);
183d7231be4SDavid Lechner 
184d7231be4SDavid Lechner 	guard(mutex)(&trigger->lock);
185d7231be4SDavid Lechner 
186d7231be4SDavid Lechner 	if (!trigger->ops)
187d7231be4SDavid Lechner 		return ERR_PTR(-ENODEV);
188d7231be4SDavid Lechner 
189d7231be4SDavid Lechner 	if (trigger->ops->request) {
190d7231be4SDavid Lechner 		ret = trigger->ops->request(trigger, type, args->args, args->nargs);
191d7231be4SDavid Lechner 		if (ret)
192d7231be4SDavid Lechner 			return ERR_PTR(ret);
193d7231be4SDavid Lechner 	}
194d7231be4SDavid Lechner 
195d7231be4SDavid Lechner 	kref_get(&trigger->ref);
196d7231be4SDavid Lechner 
197d7231be4SDavid Lechner 	return trigger;
198d7231be4SDavid Lechner }
199d7231be4SDavid Lechner 
200d7231be4SDavid Lechner /**
201d7231be4SDavid Lechner  * devm_spi_offload_trigger_get() - Get an offload trigger instance
202d7231be4SDavid Lechner  * @dev: Device for devm purposes.
203d7231be4SDavid Lechner  * @offload: Offload instance connected to a trigger.
204d7231be4SDavid Lechner  * @type: Trigger type to get.
205d7231be4SDavid Lechner  *
206d7231be4SDavid Lechner  * Return: Offload trigger instance or error on failure.
207d7231be4SDavid Lechner  */
208d7231be4SDavid Lechner struct spi_offload_trigger
209d7231be4SDavid Lechner *devm_spi_offload_trigger_get(struct device *dev,
210d7231be4SDavid Lechner 			      struct spi_offload *offload,
211d7231be4SDavid Lechner 			      enum spi_offload_trigger_type type)
212d7231be4SDavid Lechner {
213d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger;
214d7231be4SDavid Lechner 	struct fwnode_reference_args args;
215d7231be4SDavid Lechner 	int ret;
216d7231be4SDavid Lechner 
217d7231be4SDavid Lechner 	ret = fwnode_property_get_reference_args(dev_fwnode(offload->provider_dev),
218d7231be4SDavid Lechner 						 "trigger-sources",
219d7231be4SDavid Lechner 						 "#trigger-source-cells", 0, 0,
220d7231be4SDavid Lechner 						 &args);
221d7231be4SDavid Lechner 	if (ret)
222d7231be4SDavid Lechner 		return ERR_PTR(ret);
223d7231be4SDavid Lechner 
224d7231be4SDavid Lechner 	trigger = spi_offload_trigger_get(type, &args);
225d7231be4SDavid Lechner 	fwnode_handle_put(args.fwnode);
226d7231be4SDavid Lechner 	if (IS_ERR(trigger))
227d7231be4SDavid Lechner 		return trigger;
228d7231be4SDavid Lechner 
229d7231be4SDavid Lechner 	ret = devm_add_action_or_reset(dev, spi_offload_trigger_put, trigger);
230d7231be4SDavid Lechner 	if (ret)
231d7231be4SDavid Lechner 		return ERR_PTR(ret);
232d7231be4SDavid Lechner 
233d7231be4SDavid Lechner 	return trigger;
234d7231be4SDavid Lechner }
235d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_trigger_get);
236d7231be4SDavid Lechner 
237d7231be4SDavid Lechner /**
238d7231be4SDavid Lechner  * spi_offload_trigger_validate - Validate the requested trigger
239d7231be4SDavid Lechner  * @trigger: Offload trigger instance
240d7231be4SDavid Lechner  * @config: Trigger config to validate
241d7231be4SDavid Lechner  *
242d7231be4SDavid Lechner  * On success, @config may be modifed to reflect what the hardware can do.
243d7231be4SDavid Lechner  * For example, the frequency of a periodic trigger may be adjusted to the
244d7231be4SDavid Lechner  * nearest supported value.
245d7231be4SDavid Lechner  *
246d7231be4SDavid Lechner  * Callers will likely need to do additional validation of the modified trigger
247d7231be4SDavid Lechner  * parameters.
248d7231be4SDavid Lechner  *
249d7231be4SDavid Lechner  * Return: 0 on success, negative error code on failure.
250d7231be4SDavid Lechner  */
251d7231be4SDavid Lechner int spi_offload_trigger_validate(struct spi_offload_trigger *trigger,
252d7231be4SDavid Lechner 				 struct spi_offload_trigger_config *config)
253d7231be4SDavid Lechner {
254d7231be4SDavid Lechner 	guard(mutex)(&trigger->lock);
255d7231be4SDavid Lechner 
256d7231be4SDavid Lechner 	if (!trigger->ops)
257d7231be4SDavid Lechner 		return -ENODEV;
258d7231be4SDavid Lechner 
259d7231be4SDavid Lechner 	if (!trigger->ops->validate)
260d7231be4SDavid Lechner 		return -EOPNOTSUPP;
261d7231be4SDavid Lechner 
262d7231be4SDavid Lechner 	return trigger->ops->validate(trigger, config);
263d7231be4SDavid Lechner }
264d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(spi_offload_trigger_validate);
265d7231be4SDavid Lechner 
266d7231be4SDavid Lechner /**
267d7231be4SDavid Lechner  * spi_offload_trigger_enable - enables trigger for offload
268d7231be4SDavid Lechner  * @offload: Offload instance
269d7231be4SDavid Lechner  * @trigger: Offload trigger instance
270d7231be4SDavid Lechner  * @config: Trigger config to validate
271d7231be4SDavid Lechner  *
272d7231be4SDavid Lechner  * There must be a prepared offload instance with the specified ID (i.e.
273d7231be4SDavid Lechner  * spi_optimize_message() was called with the same offload assigned to the
274d7231be4SDavid Lechner  * message). This will also reserve the bus for exclusive use by the offload
275d7231be4SDavid Lechner  * instance until the trigger is disabled. Any other attempts to send a
276d7231be4SDavid Lechner  * transfer or lock the bus will fail with -EBUSY during this time.
277d7231be4SDavid Lechner  *
278d7231be4SDavid Lechner  * Calls must be balanced with spi_offload_trigger_disable().
279d7231be4SDavid Lechner  *
280d7231be4SDavid Lechner  * Context: can sleep
281d7231be4SDavid Lechner  * Return: 0 on success, else a negative error code.
282d7231be4SDavid Lechner  */
283d7231be4SDavid Lechner int spi_offload_trigger_enable(struct spi_offload *offload,
284d7231be4SDavid Lechner 			       struct spi_offload_trigger *trigger,
285d7231be4SDavid Lechner 			       struct spi_offload_trigger_config *config)
286d7231be4SDavid Lechner {
287d7231be4SDavid Lechner 	int ret;
288d7231be4SDavid Lechner 
289d7231be4SDavid Lechner 	guard(mutex)(&trigger->lock);
290d7231be4SDavid Lechner 
291d7231be4SDavid Lechner 	if (!trigger->ops)
292d7231be4SDavid Lechner 		return -ENODEV;
293d7231be4SDavid Lechner 
294d7231be4SDavid Lechner 	if (offload->ops && offload->ops->trigger_enable) {
295d7231be4SDavid Lechner 		ret = offload->ops->trigger_enable(offload);
296d7231be4SDavid Lechner 		if (ret)
297d7231be4SDavid Lechner 			return ret;
298d7231be4SDavid Lechner 	}
299d7231be4SDavid Lechner 
300d7231be4SDavid Lechner 	if (trigger->ops->enable) {
301d7231be4SDavid Lechner 		ret = trigger->ops->enable(trigger, config);
302d7231be4SDavid Lechner 		if (ret) {
303d7231be4SDavid Lechner 			if (offload->ops->trigger_disable)
304d7231be4SDavid Lechner 				offload->ops->trigger_disable(offload);
305d7231be4SDavid Lechner 			return ret;
306d7231be4SDavid Lechner 		}
307d7231be4SDavid Lechner 	}
308d7231be4SDavid Lechner 
309d7231be4SDavid Lechner 	return 0;
310d7231be4SDavid Lechner }
311d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(spi_offload_trigger_enable);
312d7231be4SDavid Lechner 
313d7231be4SDavid Lechner /**
314d7231be4SDavid Lechner  * spi_offload_trigger_disable - disables hardware trigger for offload
315d7231be4SDavid Lechner  * @offload: Offload instance
316d7231be4SDavid Lechner  * @trigger: Offload trigger instance
317d7231be4SDavid Lechner  *
318d7231be4SDavid Lechner  * Disables the hardware trigger for the offload instance with the specified ID
319d7231be4SDavid Lechner  * and releases the bus for use by other clients.
320d7231be4SDavid Lechner  *
321d7231be4SDavid Lechner  * Context: can sleep
322d7231be4SDavid Lechner  */
323d7231be4SDavid Lechner void spi_offload_trigger_disable(struct spi_offload *offload,
324d7231be4SDavid Lechner 				 struct spi_offload_trigger *trigger)
325d7231be4SDavid Lechner {
326d7231be4SDavid Lechner 	if (offload->ops && offload->ops->trigger_disable)
327d7231be4SDavid Lechner 		offload->ops->trigger_disable(offload);
328d7231be4SDavid Lechner 
329d7231be4SDavid Lechner 	guard(mutex)(&trigger->lock);
330d7231be4SDavid Lechner 
331d7231be4SDavid Lechner 	if (!trigger->ops)
332d7231be4SDavid Lechner 		return;
333d7231be4SDavid Lechner 
334d7231be4SDavid Lechner 	if (trigger->ops->disable)
335d7231be4SDavid Lechner 		trigger->ops->disable(trigger);
336d7231be4SDavid Lechner }
337d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(spi_offload_trigger_disable);
338d7231be4SDavid Lechner 
339700a2819SDavid Lechner static void spi_offload_release_dma_chan(void *chan)
340700a2819SDavid Lechner {
341700a2819SDavid Lechner 	dma_release_channel(chan);
342700a2819SDavid Lechner }
343700a2819SDavid Lechner 
344700a2819SDavid Lechner /**
345700a2819SDavid Lechner  * devm_spi_offload_tx_stream_request_dma_chan - Get the DMA channel info for the TX stream
346700a2819SDavid Lechner  * @dev: Device for devm purposes.
347700a2819SDavid Lechner  * @offload: Offload instance
348700a2819SDavid Lechner  *
349700a2819SDavid Lechner  * This is the DMA channel that will provide data to transfers that use the
350700a2819SDavid Lechner  * %SPI_OFFLOAD_XFER_TX_STREAM offload flag.
351700a2819SDavid Lechner  *
352700a2819SDavid Lechner  * Return: Pointer to DMA channel info, or negative error code
353700a2819SDavid Lechner  */
354700a2819SDavid Lechner struct dma_chan
355700a2819SDavid Lechner *devm_spi_offload_tx_stream_request_dma_chan(struct device *dev,
356700a2819SDavid Lechner 					     struct spi_offload *offload)
357700a2819SDavid Lechner {
358700a2819SDavid Lechner 	struct dma_chan *chan;
359700a2819SDavid Lechner 	int ret;
360700a2819SDavid Lechner 
361700a2819SDavid Lechner 	if (!offload->ops || !offload->ops->tx_stream_request_dma_chan)
362700a2819SDavid Lechner 		return ERR_PTR(-EOPNOTSUPP);
363700a2819SDavid Lechner 
364700a2819SDavid Lechner 	chan = offload->ops->tx_stream_request_dma_chan(offload);
365700a2819SDavid Lechner 	if (IS_ERR(chan))
366700a2819SDavid Lechner 		return chan;
367700a2819SDavid Lechner 
368700a2819SDavid Lechner 	ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan);
369700a2819SDavid Lechner 	if (ret)
370700a2819SDavid Lechner 		return ERR_PTR(ret);
371700a2819SDavid Lechner 
372700a2819SDavid Lechner 	return chan;
373700a2819SDavid Lechner }
374700a2819SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_tx_stream_request_dma_chan);
375700a2819SDavid Lechner 
376700a2819SDavid Lechner /**
377700a2819SDavid Lechner  * devm_spi_offload_rx_stream_request_dma_chan - Get the DMA channel info for the RX stream
378700a2819SDavid Lechner  * @dev: Device for devm purposes.
379700a2819SDavid Lechner  * @offload: Offload instance
380700a2819SDavid Lechner  *
381700a2819SDavid Lechner  * This is the DMA channel that will receive data from transfers that use the
382700a2819SDavid Lechner  * %SPI_OFFLOAD_XFER_RX_STREAM offload flag.
383700a2819SDavid Lechner  *
384700a2819SDavid Lechner  * Return: Pointer to DMA channel info, or negative error code
385700a2819SDavid Lechner  */
386700a2819SDavid Lechner struct dma_chan
387700a2819SDavid Lechner *devm_spi_offload_rx_stream_request_dma_chan(struct device *dev,
388700a2819SDavid Lechner 					     struct spi_offload *offload)
389700a2819SDavid Lechner {
390700a2819SDavid Lechner 	struct dma_chan *chan;
391700a2819SDavid Lechner 	int ret;
392700a2819SDavid Lechner 
393700a2819SDavid Lechner 	if (!offload->ops || !offload->ops->rx_stream_request_dma_chan)
394700a2819SDavid Lechner 		return ERR_PTR(-EOPNOTSUPP);
395700a2819SDavid Lechner 
396700a2819SDavid Lechner 	chan = offload->ops->rx_stream_request_dma_chan(offload);
397700a2819SDavid Lechner 	if (IS_ERR(chan))
398700a2819SDavid Lechner 		return chan;
399700a2819SDavid Lechner 
400700a2819SDavid Lechner 	ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan);
401700a2819SDavid Lechner 	if (ret)
402700a2819SDavid Lechner 		return ERR_PTR(ret);
403700a2819SDavid Lechner 
404700a2819SDavid Lechner 	return chan;
405700a2819SDavid Lechner }
406700a2819SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_rx_stream_request_dma_chan);
407700a2819SDavid Lechner 
408d7231be4SDavid Lechner /* Triggers providers */
409d7231be4SDavid Lechner 
410d7231be4SDavid Lechner static void spi_offload_trigger_unregister(void *data)
411d7231be4SDavid Lechner {
412d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger = data;
413d7231be4SDavid Lechner 
414d7231be4SDavid Lechner 	scoped_guard(mutex, &spi_offload_triggers_lock)
415d7231be4SDavid Lechner 		list_del(&trigger->list);
416d7231be4SDavid Lechner 
417d7231be4SDavid Lechner 	scoped_guard(mutex, &trigger->lock) {
418d7231be4SDavid Lechner 		trigger->priv = NULL;
419d7231be4SDavid Lechner 		trigger->ops = NULL;
420d7231be4SDavid Lechner 	}
421d7231be4SDavid Lechner 
422d7231be4SDavid Lechner 	kref_put(&trigger->ref, spi_offload_trigger_free);
423d7231be4SDavid Lechner }
424d7231be4SDavid Lechner 
425d7231be4SDavid Lechner /**
426d7231be4SDavid Lechner  * devm_spi_offload_trigger_register() - Allocate and register an offload trigger
427d7231be4SDavid Lechner  * @dev: Device for devm purposes.
428d7231be4SDavid Lechner  * @info: Provider-specific trigger info.
429d7231be4SDavid Lechner  *
430d7231be4SDavid Lechner  * Return: 0 on success, else a negative error code.
431d7231be4SDavid Lechner  */
432d7231be4SDavid Lechner int devm_spi_offload_trigger_register(struct device *dev,
433d7231be4SDavid Lechner 				      struct spi_offload_trigger_info *info)
434d7231be4SDavid Lechner {
435d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger;
436d7231be4SDavid Lechner 
437d7231be4SDavid Lechner 	if (!info->fwnode || !info->ops)
438d7231be4SDavid Lechner 		return -EINVAL;
439d7231be4SDavid Lechner 
440d7231be4SDavid Lechner 	trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
441d7231be4SDavid Lechner 	if (!trigger)
442d7231be4SDavid Lechner 		return -ENOMEM;
443d7231be4SDavid Lechner 
444d7231be4SDavid Lechner 	kref_init(&trigger->ref);
445d7231be4SDavid Lechner 	mutex_init(&trigger->lock);
446d7231be4SDavid Lechner 	trigger->fwnode = fwnode_handle_get(info->fwnode);
447d7231be4SDavid Lechner 	trigger->ops = info->ops;
448d7231be4SDavid Lechner 	trigger->priv = info->priv;
449d7231be4SDavid Lechner 
450d7231be4SDavid Lechner 	scoped_guard(mutex, &spi_offload_triggers_lock)
451d7231be4SDavid Lechner 		list_add_tail(&trigger->list, &spi_offload_triggers);
452d7231be4SDavid Lechner 
453d7231be4SDavid Lechner 	return devm_add_action_or_reset(dev, spi_offload_trigger_unregister, trigger);
454d7231be4SDavid Lechner }
455d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_trigger_register);
456d7231be4SDavid Lechner 
457d7231be4SDavid Lechner /**
458d7231be4SDavid Lechner  * spi_offload_trigger_get_priv() - Get the private data for the trigger
459d7231be4SDavid Lechner  *
460d7231be4SDavid Lechner  * @trigger: Offload trigger instance.
461d7231be4SDavid Lechner  *
462d7231be4SDavid Lechner  * Return: Private data for the trigger.
463d7231be4SDavid Lechner  */
464d7231be4SDavid Lechner void *spi_offload_trigger_get_priv(struct spi_offload_trigger *trigger)
465d7231be4SDavid Lechner {
466d7231be4SDavid Lechner 	return trigger->priv;
467d7231be4SDavid Lechner }
468d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(spi_offload_trigger_get_priv);
469