xref: /linux/drivers/spi/spi-offload.c (revision 700a281905f2a4ccf6f3b2d3cd6985e034b4b021)
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>
21*700a2819SDavid 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;
1118e02d188SDavid Lechner 	int ret;
1128e02d188SDavid Lechner 
1138e02d188SDavid Lechner 	if (!spi || !config)
1148e02d188SDavid Lechner 		return ERR_PTR(-EINVAL);
1158e02d188SDavid Lechner 
1168e02d188SDavid Lechner 	if (!spi->controller->get_offload)
1178e02d188SDavid Lechner 		return ERR_PTR(-ENODEV);
1188e02d188SDavid Lechner 
1198e02d188SDavid Lechner 	resource = kzalloc(sizeof(*resource), GFP_KERNEL);
1208e02d188SDavid Lechner 	if (!resource)
1218e02d188SDavid Lechner 		return ERR_PTR(-ENOMEM);
1228e02d188SDavid Lechner 
1238e02d188SDavid Lechner 	resource->controller = spi->controller;
1248e02d188SDavid Lechner 	resource->offload = spi->controller->get_offload(spi, config);
1258e02d188SDavid Lechner 	if (IS_ERR(resource->offload)) {
1268e02d188SDavid Lechner 		kfree(resource);
1278e02d188SDavid Lechner 		return resource->offload;
1288e02d188SDavid Lechner 	}
1298e02d188SDavid Lechner 
1308e02d188SDavid Lechner 	ret = devm_add_action_or_reset(dev, spi_offload_put, resource);
1318e02d188SDavid Lechner 	if (ret)
1328e02d188SDavid Lechner 		return ERR_PTR(ret);
1338e02d188SDavid Lechner 
1348e02d188SDavid Lechner 	return resource->offload;
1358e02d188SDavid Lechner }
1368e02d188SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_get);
137d7231be4SDavid Lechner 
138d7231be4SDavid Lechner static void spi_offload_trigger_free(struct kref *ref)
139d7231be4SDavid Lechner {
140d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger =
141d7231be4SDavid Lechner 		container_of(ref, struct spi_offload_trigger, ref);
142d7231be4SDavid Lechner 
143d7231be4SDavid Lechner 	mutex_destroy(&trigger->lock);
144d7231be4SDavid Lechner 	fwnode_handle_put(trigger->fwnode);
145d7231be4SDavid Lechner 	kfree(trigger);
146d7231be4SDavid Lechner }
147d7231be4SDavid Lechner 
148d7231be4SDavid Lechner static void spi_offload_trigger_put(void *data)
149d7231be4SDavid Lechner {
150d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger = data;
151d7231be4SDavid Lechner 
152d7231be4SDavid Lechner 	scoped_guard(mutex, &trigger->lock)
153d7231be4SDavid Lechner 		if (trigger->ops && trigger->ops->release)
154d7231be4SDavid Lechner 			trigger->ops->release(trigger);
155d7231be4SDavid Lechner 
156d7231be4SDavid Lechner 	kref_put(&trigger->ref, spi_offload_trigger_free);
157d7231be4SDavid Lechner }
158d7231be4SDavid Lechner 
159d7231be4SDavid Lechner static struct spi_offload_trigger
160d7231be4SDavid Lechner *spi_offload_trigger_get(enum spi_offload_trigger_type type,
161d7231be4SDavid Lechner 			 struct fwnode_reference_args *args)
162d7231be4SDavid Lechner {
163d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger;
164d7231be4SDavid Lechner 	bool match = false;
165d7231be4SDavid Lechner 	int ret;
166d7231be4SDavid Lechner 
167d7231be4SDavid Lechner 	guard(mutex)(&spi_offload_triggers_lock);
168d7231be4SDavid Lechner 
169d7231be4SDavid Lechner 	list_for_each_entry(trigger, &spi_offload_triggers, list) {
170d7231be4SDavid Lechner 		if (trigger->fwnode != args->fwnode)
171d7231be4SDavid Lechner 			continue;
172d7231be4SDavid Lechner 
173d7231be4SDavid Lechner 		match = trigger->ops->match(trigger, type, args->args, args->nargs);
174d7231be4SDavid Lechner 		if (match)
175d7231be4SDavid Lechner 			break;
176d7231be4SDavid Lechner 	}
177d7231be4SDavid Lechner 
178d7231be4SDavid Lechner 	if (!match)
179d7231be4SDavid Lechner 		return ERR_PTR(-EPROBE_DEFER);
180d7231be4SDavid Lechner 
181d7231be4SDavid Lechner 	guard(mutex)(&trigger->lock);
182d7231be4SDavid Lechner 
183d7231be4SDavid Lechner 	if (!trigger->ops)
184d7231be4SDavid Lechner 		return ERR_PTR(-ENODEV);
185d7231be4SDavid Lechner 
186d7231be4SDavid Lechner 	if (trigger->ops->request) {
187d7231be4SDavid Lechner 		ret = trigger->ops->request(trigger, type, args->args, args->nargs);
188d7231be4SDavid Lechner 		if (ret)
189d7231be4SDavid Lechner 			return ERR_PTR(ret);
190d7231be4SDavid Lechner 	}
191d7231be4SDavid Lechner 
192d7231be4SDavid Lechner 	kref_get(&trigger->ref);
193d7231be4SDavid Lechner 
194d7231be4SDavid Lechner 	return trigger;
195d7231be4SDavid Lechner }
196d7231be4SDavid Lechner 
197d7231be4SDavid Lechner /**
198d7231be4SDavid Lechner  * devm_spi_offload_trigger_get() - Get an offload trigger instance
199d7231be4SDavid Lechner  * @dev: Device for devm purposes.
200d7231be4SDavid Lechner  * @offload: Offload instance connected to a trigger.
201d7231be4SDavid Lechner  * @type: Trigger type to get.
202d7231be4SDavid Lechner  *
203d7231be4SDavid Lechner  * Return: Offload trigger instance or error on failure.
204d7231be4SDavid Lechner  */
205d7231be4SDavid Lechner struct spi_offload_trigger
206d7231be4SDavid Lechner *devm_spi_offload_trigger_get(struct device *dev,
207d7231be4SDavid Lechner 			      struct spi_offload *offload,
208d7231be4SDavid Lechner 			      enum spi_offload_trigger_type type)
209d7231be4SDavid Lechner {
210d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger;
211d7231be4SDavid Lechner 	struct fwnode_reference_args args;
212d7231be4SDavid Lechner 	int ret;
213d7231be4SDavid Lechner 
214d7231be4SDavid Lechner 	ret = fwnode_property_get_reference_args(dev_fwnode(offload->provider_dev),
215d7231be4SDavid Lechner 						 "trigger-sources",
216d7231be4SDavid Lechner 						 "#trigger-source-cells", 0, 0,
217d7231be4SDavid Lechner 						 &args);
218d7231be4SDavid Lechner 	if (ret)
219d7231be4SDavid Lechner 		return ERR_PTR(ret);
220d7231be4SDavid Lechner 
221d7231be4SDavid Lechner 	trigger = spi_offload_trigger_get(type, &args);
222d7231be4SDavid Lechner 	fwnode_handle_put(args.fwnode);
223d7231be4SDavid Lechner 	if (IS_ERR(trigger))
224d7231be4SDavid Lechner 		return trigger;
225d7231be4SDavid Lechner 
226d7231be4SDavid Lechner 	ret = devm_add_action_or_reset(dev, spi_offload_trigger_put, trigger);
227d7231be4SDavid Lechner 	if (ret)
228d7231be4SDavid Lechner 		return ERR_PTR(ret);
229d7231be4SDavid Lechner 
230d7231be4SDavid Lechner 	return trigger;
231d7231be4SDavid Lechner }
232d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_trigger_get);
233d7231be4SDavid Lechner 
234d7231be4SDavid Lechner /**
235d7231be4SDavid Lechner  * spi_offload_trigger_validate - Validate the requested trigger
236d7231be4SDavid Lechner  * @trigger: Offload trigger instance
237d7231be4SDavid Lechner  * @config: Trigger config to validate
238d7231be4SDavid Lechner  *
239d7231be4SDavid Lechner  * On success, @config may be modifed to reflect what the hardware can do.
240d7231be4SDavid Lechner  * For example, the frequency of a periodic trigger may be adjusted to the
241d7231be4SDavid Lechner  * nearest supported value.
242d7231be4SDavid Lechner  *
243d7231be4SDavid Lechner  * Callers will likely need to do additional validation of the modified trigger
244d7231be4SDavid Lechner  * parameters.
245d7231be4SDavid Lechner  *
246d7231be4SDavid Lechner  * Return: 0 on success, negative error code on failure.
247d7231be4SDavid Lechner  */
248d7231be4SDavid Lechner int spi_offload_trigger_validate(struct spi_offload_trigger *trigger,
249d7231be4SDavid Lechner 				 struct spi_offload_trigger_config *config)
250d7231be4SDavid Lechner {
251d7231be4SDavid Lechner 	guard(mutex)(&trigger->lock);
252d7231be4SDavid Lechner 
253d7231be4SDavid Lechner 	if (!trigger->ops)
254d7231be4SDavid Lechner 		return -ENODEV;
255d7231be4SDavid Lechner 
256d7231be4SDavid Lechner 	if (!trigger->ops->validate)
257d7231be4SDavid Lechner 		return -EOPNOTSUPP;
258d7231be4SDavid Lechner 
259d7231be4SDavid Lechner 	return trigger->ops->validate(trigger, config);
260d7231be4SDavid Lechner }
261d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(spi_offload_trigger_validate);
262d7231be4SDavid Lechner 
263d7231be4SDavid Lechner /**
264d7231be4SDavid Lechner  * spi_offload_trigger_enable - enables trigger for offload
265d7231be4SDavid Lechner  * @offload: Offload instance
266d7231be4SDavid Lechner  * @trigger: Offload trigger instance
267d7231be4SDavid Lechner  * @config: Trigger config to validate
268d7231be4SDavid Lechner  *
269d7231be4SDavid Lechner  * There must be a prepared offload instance with the specified ID (i.e.
270d7231be4SDavid Lechner  * spi_optimize_message() was called with the same offload assigned to the
271d7231be4SDavid Lechner  * message). This will also reserve the bus for exclusive use by the offload
272d7231be4SDavid Lechner  * instance until the trigger is disabled. Any other attempts to send a
273d7231be4SDavid Lechner  * transfer or lock the bus will fail with -EBUSY during this time.
274d7231be4SDavid Lechner  *
275d7231be4SDavid Lechner  * Calls must be balanced with spi_offload_trigger_disable().
276d7231be4SDavid Lechner  *
277d7231be4SDavid Lechner  * Context: can sleep
278d7231be4SDavid Lechner  * Return: 0 on success, else a negative error code.
279d7231be4SDavid Lechner  */
280d7231be4SDavid Lechner int spi_offload_trigger_enable(struct spi_offload *offload,
281d7231be4SDavid Lechner 			       struct spi_offload_trigger *trigger,
282d7231be4SDavid Lechner 			       struct spi_offload_trigger_config *config)
283d7231be4SDavid Lechner {
284d7231be4SDavid Lechner 	int ret;
285d7231be4SDavid Lechner 
286d7231be4SDavid Lechner 	guard(mutex)(&trigger->lock);
287d7231be4SDavid Lechner 
288d7231be4SDavid Lechner 	if (!trigger->ops)
289d7231be4SDavid Lechner 		return -ENODEV;
290d7231be4SDavid Lechner 
291d7231be4SDavid Lechner 	if (offload->ops && offload->ops->trigger_enable) {
292d7231be4SDavid Lechner 		ret = offload->ops->trigger_enable(offload);
293d7231be4SDavid Lechner 		if (ret)
294d7231be4SDavid Lechner 			return ret;
295d7231be4SDavid Lechner 	}
296d7231be4SDavid Lechner 
297d7231be4SDavid Lechner 	if (trigger->ops->enable) {
298d7231be4SDavid Lechner 		ret = trigger->ops->enable(trigger, config);
299d7231be4SDavid Lechner 		if (ret) {
300d7231be4SDavid Lechner 			if (offload->ops->trigger_disable)
301d7231be4SDavid Lechner 				offload->ops->trigger_disable(offload);
302d7231be4SDavid Lechner 			return ret;
303d7231be4SDavid Lechner 		}
304d7231be4SDavid Lechner 	}
305d7231be4SDavid Lechner 
306d7231be4SDavid Lechner 	return 0;
307d7231be4SDavid Lechner }
308d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(spi_offload_trigger_enable);
309d7231be4SDavid Lechner 
310d7231be4SDavid Lechner /**
311d7231be4SDavid Lechner  * spi_offload_trigger_disable - disables hardware trigger for offload
312d7231be4SDavid Lechner  * @offload: Offload instance
313d7231be4SDavid Lechner  * @trigger: Offload trigger instance
314d7231be4SDavid Lechner  *
315d7231be4SDavid Lechner  * Disables the hardware trigger for the offload instance with the specified ID
316d7231be4SDavid Lechner  * and releases the bus for use by other clients.
317d7231be4SDavid Lechner  *
318d7231be4SDavid Lechner  * Context: can sleep
319d7231be4SDavid Lechner  */
320d7231be4SDavid Lechner void spi_offload_trigger_disable(struct spi_offload *offload,
321d7231be4SDavid Lechner 				 struct spi_offload_trigger *trigger)
322d7231be4SDavid Lechner {
323d7231be4SDavid Lechner 	if (offload->ops && offload->ops->trigger_disable)
324d7231be4SDavid Lechner 		offload->ops->trigger_disable(offload);
325d7231be4SDavid Lechner 
326d7231be4SDavid Lechner 	guard(mutex)(&trigger->lock);
327d7231be4SDavid Lechner 
328d7231be4SDavid Lechner 	if (!trigger->ops)
329d7231be4SDavid Lechner 		return;
330d7231be4SDavid Lechner 
331d7231be4SDavid Lechner 	if (trigger->ops->disable)
332d7231be4SDavid Lechner 		trigger->ops->disable(trigger);
333d7231be4SDavid Lechner }
334d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(spi_offload_trigger_disable);
335d7231be4SDavid Lechner 
336*700a2819SDavid Lechner static void spi_offload_release_dma_chan(void *chan)
337*700a2819SDavid Lechner {
338*700a2819SDavid Lechner 	dma_release_channel(chan);
339*700a2819SDavid Lechner }
340*700a2819SDavid Lechner 
341*700a2819SDavid Lechner /**
342*700a2819SDavid Lechner  * devm_spi_offload_tx_stream_request_dma_chan - Get the DMA channel info for the TX stream
343*700a2819SDavid Lechner  * @dev: Device for devm purposes.
344*700a2819SDavid Lechner  * @offload: Offload instance
345*700a2819SDavid Lechner  *
346*700a2819SDavid Lechner  * This is the DMA channel that will provide data to transfers that use the
347*700a2819SDavid Lechner  * %SPI_OFFLOAD_XFER_TX_STREAM offload flag.
348*700a2819SDavid Lechner  *
349*700a2819SDavid Lechner  * Return: Pointer to DMA channel info, or negative error code
350*700a2819SDavid Lechner  */
351*700a2819SDavid Lechner struct dma_chan
352*700a2819SDavid Lechner *devm_spi_offload_tx_stream_request_dma_chan(struct device *dev,
353*700a2819SDavid Lechner 					     struct spi_offload *offload)
354*700a2819SDavid Lechner {
355*700a2819SDavid Lechner 	struct dma_chan *chan;
356*700a2819SDavid Lechner 	int ret;
357*700a2819SDavid Lechner 
358*700a2819SDavid Lechner 	if (!offload->ops || !offload->ops->tx_stream_request_dma_chan)
359*700a2819SDavid Lechner 		return ERR_PTR(-EOPNOTSUPP);
360*700a2819SDavid Lechner 
361*700a2819SDavid Lechner 	chan = offload->ops->tx_stream_request_dma_chan(offload);
362*700a2819SDavid Lechner 	if (IS_ERR(chan))
363*700a2819SDavid Lechner 		return chan;
364*700a2819SDavid Lechner 
365*700a2819SDavid Lechner 	ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan);
366*700a2819SDavid Lechner 	if (ret)
367*700a2819SDavid Lechner 		return ERR_PTR(ret);
368*700a2819SDavid Lechner 
369*700a2819SDavid Lechner 	return chan;
370*700a2819SDavid Lechner }
371*700a2819SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_tx_stream_request_dma_chan);
372*700a2819SDavid Lechner 
373*700a2819SDavid Lechner /**
374*700a2819SDavid Lechner  * devm_spi_offload_rx_stream_request_dma_chan - Get the DMA channel info for the RX stream
375*700a2819SDavid Lechner  * @dev: Device for devm purposes.
376*700a2819SDavid Lechner  * @offload: Offload instance
377*700a2819SDavid Lechner  *
378*700a2819SDavid Lechner  * This is the DMA channel that will receive data from transfers that use the
379*700a2819SDavid Lechner  * %SPI_OFFLOAD_XFER_RX_STREAM offload flag.
380*700a2819SDavid Lechner  *
381*700a2819SDavid Lechner  * Return: Pointer to DMA channel info, or negative error code
382*700a2819SDavid Lechner  */
383*700a2819SDavid Lechner struct dma_chan
384*700a2819SDavid Lechner *devm_spi_offload_rx_stream_request_dma_chan(struct device *dev,
385*700a2819SDavid Lechner 					     struct spi_offload *offload)
386*700a2819SDavid Lechner {
387*700a2819SDavid Lechner 	struct dma_chan *chan;
388*700a2819SDavid Lechner 	int ret;
389*700a2819SDavid Lechner 
390*700a2819SDavid Lechner 	if (!offload->ops || !offload->ops->rx_stream_request_dma_chan)
391*700a2819SDavid Lechner 		return ERR_PTR(-EOPNOTSUPP);
392*700a2819SDavid Lechner 
393*700a2819SDavid Lechner 	chan = offload->ops->rx_stream_request_dma_chan(offload);
394*700a2819SDavid Lechner 	if (IS_ERR(chan))
395*700a2819SDavid Lechner 		return chan;
396*700a2819SDavid Lechner 
397*700a2819SDavid Lechner 	ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan);
398*700a2819SDavid Lechner 	if (ret)
399*700a2819SDavid Lechner 		return ERR_PTR(ret);
400*700a2819SDavid Lechner 
401*700a2819SDavid Lechner 	return chan;
402*700a2819SDavid Lechner }
403*700a2819SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_rx_stream_request_dma_chan);
404*700a2819SDavid Lechner 
405d7231be4SDavid Lechner /* Triggers providers */
406d7231be4SDavid Lechner 
407d7231be4SDavid Lechner static void spi_offload_trigger_unregister(void *data)
408d7231be4SDavid Lechner {
409d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger = data;
410d7231be4SDavid Lechner 
411d7231be4SDavid Lechner 	scoped_guard(mutex, &spi_offload_triggers_lock)
412d7231be4SDavid Lechner 		list_del(&trigger->list);
413d7231be4SDavid Lechner 
414d7231be4SDavid Lechner 	scoped_guard(mutex, &trigger->lock) {
415d7231be4SDavid Lechner 		trigger->priv = NULL;
416d7231be4SDavid Lechner 		trigger->ops = NULL;
417d7231be4SDavid Lechner 	}
418d7231be4SDavid Lechner 
419d7231be4SDavid Lechner 	kref_put(&trigger->ref, spi_offload_trigger_free);
420d7231be4SDavid Lechner }
421d7231be4SDavid Lechner 
422d7231be4SDavid Lechner /**
423d7231be4SDavid Lechner  * devm_spi_offload_trigger_register() - Allocate and register an offload trigger
424d7231be4SDavid Lechner  * @dev: Device for devm purposes.
425d7231be4SDavid Lechner  * @info: Provider-specific trigger info.
426d7231be4SDavid Lechner  *
427d7231be4SDavid Lechner  * Return: 0 on success, else a negative error code.
428d7231be4SDavid Lechner  */
429d7231be4SDavid Lechner int devm_spi_offload_trigger_register(struct device *dev,
430d7231be4SDavid Lechner 				      struct spi_offload_trigger_info *info)
431d7231be4SDavid Lechner {
432d7231be4SDavid Lechner 	struct spi_offload_trigger *trigger;
433d7231be4SDavid Lechner 
434d7231be4SDavid Lechner 	if (!info->fwnode || !info->ops)
435d7231be4SDavid Lechner 		return -EINVAL;
436d7231be4SDavid Lechner 
437d7231be4SDavid Lechner 	trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
438d7231be4SDavid Lechner 	if (!trigger)
439d7231be4SDavid Lechner 		return -ENOMEM;
440d7231be4SDavid Lechner 
441d7231be4SDavid Lechner 	kref_init(&trigger->ref);
442d7231be4SDavid Lechner 	mutex_init(&trigger->lock);
443d7231be4SDavid Lechner 	trigger->fwnode = fwnode_handle_get(info->fwnode);
444d7231be4SDavid Lechner 	trigger->ops = info->ops;
445d7231be4SDavid Lechner 	trigger->priv = info->priv;
446d7231be4SDavid Lechner 
447d7231be4SDavid Lechner 	scoped_guard(mutex, &spi_offload_triggers_lock)
448d7231be4SDavid Lechner 		list_add_tail(&trigger->list, &spi_offload_triggers);
449d7231be4SDavid Lechner 
450d7231be4SDavid Lechner 	return devm_add_action_or_reset(dev, spi_offload_trigger_unregister, trigger);
451d7231be4SDavid Lechner }
452d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(devm_spi_offload_trigger_register);
453d7231be4SDavid Lechner 
454d7231be4SDavid Lechner /**
455d7231be4SDavid Lechner  * spi_offload_trigger_get_priv() - Get the private data for the trigger
456d7231be4SDavid Lechner  *
457d7231be4SDavid Lechner  * @trigger: Offload trigger instance.
458d7231be4SDavid Lechner  *
459d7231be4SDavid Lechner  * Return: Private data for the trigger.
460d7231be4SDavid Lechner  */
461d7231be4SDavid Lechner void *spi_offload_trigger_get_priv(struct spi_offload_trigger *trigger)
462d7231be4SDavid Lechner {
463d7231be4SDavid Lechner 	return trigger->priv;
464d7231be4SDavid Lechner }
465d7231be4SDavid Lechner EXPORT_SYMBOL_GPL(spi_offload_trigger_get_priv);
466