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