// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2024 Analog Devices Inc. * Copyright (C) 2024 BayLibre, SAS
*/
/* * SPI Offloading support. * * Some SPI controllers support offloading of SPI transfers. Essentially, this * is the ability for a SPI controller to perform SPI transfers with minimal * or even no CPU intervention, e.g. via a specialized SPI controller with a * hardware trigger or via a conventional SPI controller using a non-Linux MCU * processor core to offload the work.
*/
struct spi_offload_trigger { struct list_head list; struct kref ref; struct fwnode_handle *fwnode; /* synchronizes calling ops and driver registration */ struct mutex lock; /* * If the provider goes away while the consumer still has a reference, * ops and priv will be set to NULL and all calls will fail with -ENODEV.
*/ conststruct spi_offload_trigger_ops *ops; void *priv;
};
/** * devm_spi_offload_alloc() - Allocate offload instance * @dev: Device for devm purposes and assigned to &struct spi_offload.provider_dev * @priv_size: Size of private data to allocate * * Offload providers should use this to allocate offload instances. * * Return: Pointer to new offload instance or error on failure.
*/ struct spi_offload *devm_spi_offload_alloc(struct device *dev,
size_t priv_size)
{ struct spi_offload *offload; void *priv;
offload = devm_kzalloc(dev, sizeof(*offload), GFP_KERNEL); if (!offload) return ERR_PTR(-ENOMEM);
priv = devm_kzalloc(dev, priv_size, GFP_KERNEL); if (!priv) return ERR_PTR(-ENOMEM);
/** * devm_spi_offload_get() - Get an offload instance * @dev: Device for devm purposes * @spi: SPI device to use for the transfers * @config: Offload configuration * * Peripheral drivers call this function to get an offload instance that meets * the requirements specified in @config. If no suitable offload instance is * available, -ENODEV is returned. * * Return: Offload instance or error on failure.
*/ struct spi_offload *devm_spi_offload_get(struct device *dev, struct spi_device *spi, conststruct spi_offload_config *config)
{ struct spi_controller_and_offload *resource; struct spi_offload *offload; int ret;
if (!spi || !config) return ERR_PTR(-EINVAL);
if (!spi->controller->get_offload) return ERR_PTR(-ENODEV);
resource = kzalloc(sizeof(*resource), GFP_KERNEL); if (!resource) return ERR_PTR(-ENOMEM);
/** * spi_offload_trigger_validate - Validate the requested trigger * @trigger: Offload trigger instance * @config: Trigger config to validate * * On success, @config may be modifed to reflect what the hardware can do. * For example, the frequency of a periodic trigger may be adjusted to the * nearest supported value. * * Callers will likely need to do additional validation of the modified trigger * parameters. * * Return: 0 on success, negative error code on failure.
*/ int spi_offload_trigger_validate(struct spi_offload_trigger *trigger, struct spi_offload_trigger_config *config)
{
guard(mutex)(&trigger->lock);
/** * spi_offload_trigger_enable - enables trigger for offload * @offload: Offload instance * @trigger: Offload trigger instance * @config: Trigger config to validate * * There must be a prepared offload instance with the specified ID (i.e. * spi_optimize_message() was called with the same offload assigned to the * message). This will also reserve the bus for exclusive use by the offload * instance until the trigger is disabled. Any other attempts to send a * transfer or lock the bus will fail with -EBUSY during this time. * * Calls must be balanced with spi_offload_trigger_disable(). * * Context: can sleep * Return: 0 on success, else a negative error code.
*/ int spi_offload_trigger_enable(struct spi_offload *offload, struct spi_offload_trigger *trigger, struct spi_offload_trigger_config *config)
{ int ret;
guard(mutex)(&trigger->lock);
if (!trigger->ops) return -ENODEV;
if (offload->ops && offload->ops->trigger_enable) {
ret = offload->ops->trigger_enable(offload); if (ret) return ret;
}
if (trigger->ops->enable) {
ret = trigger->ops->enable(trigger, config); if (ret) { if (offload->ops && offload->ops->trigger_disable)
offload->ops->trigger_disable(offload); return ret;
}
}
/** * spi_offload_trigger_disable - disables hardware trigger for offload * @offload: Offload instance * @trigger: Offload trigger instance * * Disables the hardware trigger for the offload instance with the specified ID * and releases the bus for use by other clients. * * Context: can sleep
*/ void spi_offload_trigger_disable(struct spi_offload *offload, struct spi_offload_trigger *trigger)
{ if (offload->ops && offload->ops->trigger_disable)
offload->ops->trigger_disable(offload);
guard(mutex)(&trigger->lock);
if (!trigger->ops) return;
if (trigger->ops->disable)
trigger->ops->disable(trigger);
}
EXPORT_SYMBOL_GPL(spi_offload_trigger_disable);
/** * devm_spi_offload_tx_stream_request_dma_chan - Get the DMA channel info for the TX stream * @dev: Device for devm purposes. * @offload: Offload instance * * This is the DMA channel that will provide data to transfers that use the * %SPI_OFFLOAD_XFER_TX_STREAM offload flag. * * Return: Pointer to DMA channel info, or negative error code
*/ struct dma_chan
*devm_spi_offload_tx_stream_request_dma_chan(struct device *dev, struct spi_offload *offload)
{ struct dma_chan *chan; int ret;
if (!offload->ops || !offload->ops->tx_stream_request_dma_chan) return ERR_PTR(-EOPNOTSUPP);
chan = offload->ops->tx_stream_request_dma_chan(offload); if (IS_ERR(chan)) return chan;
ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan); if (ret) return ERR_PTR(ret);
/** * devm_spi_offload_rx_stream_request_dma_chan - Get the DMA channel info for the RX stream * @dev: Device for devm purposes. * @offload: Offload instance * * This is the DMA channel that will receive data from transfers that use the * %SPI_OFFLOAD_XFER_RX_STREAM offload flag. * * Return: Pointer to DMA channel info, or negative error code
*/ struct dma_chan
*devm_spi_offload_rx_stream_request_dma_chan(struct device *dev, struct spi_offload *offload)
{ struct dma_chan *chan; int ret;
if (!offload->ops || !offload->ops->rx_stream_request_dma_chan) return ERR_PTR(-EOPNOTSUPP);
chan = offload->ops->rx_stream_request_dma_chan(offload); if (IS_ERR(chan)) return chan;
ret = devm_add_action_or_reset(dev, spi_offload_release_dma_chan, chan); if (ret) return ERR_PTR(ret);
/** * spi_offload_trigger_get_priv() - Get the private data for the trigger * * @trigger: Offload trigger instance. * * Return: Private data for the trigger.
*/ void *spi_offload_trigger_get_priv(struct spi_offload_trigger *trigger)
{ return trigger->priv;
}
EXPORT_SYMBOL_GPL(spi_offload_trigger_get_priv);
Messung V0.5
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.