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