lkml.org 
[lkml]   [2024]   [Jan]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 01/13] spi: add core support for controllers with offload capabilities
    Date
    This adds a feature for specialized SPI controllers that can record
    a series of SPI transfers, including tx data, cs assertions, delays,
    etc. and then play them back using a hardware trigger without CPU
    intervention.

    The intended use case for this is with the AXI SPI Engine to capture
    data from ADCs at high rates (MSPS) with a stable sample period.

    Most of the implementation is controller-specific and will be handled by
    drivers that implement the offload_ops callbacks. The API follows a
    prepare/enable pattern that should be familiar to users of the clk
    subsystem.

    Consumers of this API will make calls similar to this:

    /* in probe() */
    offload = spi_offload_get(spi, 0);
    ...

    /* in some setup function */
    ret = spi_offload_prepare(offload, xfers, ARRAY_SIZE(xfers));
    ...

    /* in some enable function */
    ret = spi_offload_enable(offload);
    ...

    /* in corresponding disable function */
    spi_offload_disable(offload);
    ...

    /* in corresponding teardown function */
    spi_offload_unprepare(offload);
    ...

    Signed-off-by: David Lechner <dlechner@baylibre.com>
    ---
    drivers/spi/spi.c | 39 +++++++++++++++
    include/linux/spi/spi.h | 123 ++++++++++++++++++++++++++++++++++++++++++++++++
    2 files changed, 162 insertions(+)

    diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
    index a4b8c07c5951..f1d66b5d5491 100644
    --- a/drivers/spi/spi.c
    +++ b/drivers/spi/spi.c
    @@ -3057,6 +3057,13 @@ static int spi_controller_check_ops(struct spi_controller *ctlr)
    }
    }

    + if (ctlr->offload_ops && !(ctlr->offload_ops->get &&
    + ctlr->offload_ops->prepare &&
    + ctlr->offload_ops->unprepare &&
    + ctlr->offload_ops->enable &&
    + ctlr->offload_ops->disable))
    + return -EINVAL;
    +
    return 0;
    }

    @@ -4448,6 +4455,38 @@ int spi_write_then_read(struct spi_device *spi,
    }
    EXPORT_SYMBOL_GPL(spi_write_then_read);

    +/**
    + * spi_offload_prepare - prepare offload hardware for a transfer
    + * @offload: The offload instance.
    + * @spi: The spi device to use for the transfers.
    + * @xfers: The transfers to be executed.
    + * @num_xfers: The number of transfers.
    + *
    + * Records a series of transfers to be executed later by the offload hardware
    + * trigger.
    + *
    + * Return: 0 on success, else a negative error code.
    + */
    +int spi_offload_prepare(struct spi_offload *offload, struct spi_device *spi,
    + struct spi_transfer *xfers, unsigned int num_xfers)
    +{
    + struct spi_controller *ctlr = offload->controller;
    + struct spi_message msg;
    + int ret;
    +
    + spi_message_init_with_transfers(&msg, xfers, num_xfers);
    +
    + ret = __spi_validate(spi, &msg);
    + if (ret)
    + return ret;
    +
    + msg.spi = spi;
    + ret = ctlr->offload_ops->prepare(offload, &msg);
    +
    + return ret;
    +}
    +EXPORT_SYMBOL_GPL(spi_offload_prepare);
    +
    /*-------------------------------------------------------------------------*/

    #if IS_ENABLED(CONFIG_OF_DYNAMIC)
    diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
    index 5d65a6273dcf..f116dfc1d52c 100644
    --- a/include/linux/spi/spi.h
    +++ b/include/linux/spi/spi.h
    @@ -28,6 +28,8 @@ struct spi_transfer;
    struct spi_controller_mem_ops;
    struct spi_controller_mem_caps;
    struct spi_message;
    +struct spi_controller_offload_ops;
    +struct spi_offload;

    /*
    * INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
    @@ -713,6 +715,9 @@ struct spi_controller {
    const struct spi_controller_mem_ops *mem_ops;
    const struct spi_controller_mem_caps *mem_caps;

    + /* Operations for controllers with offload support. */
    + const struct spi_controller_offload_ops *offload_ops;
    +
    /* GPIO chip select */
    struct gpio_desc **cs_gpiods;
    bool use_gpio_descriptors;
    @@ -1505,6 +1510,124 @@ static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)

    /*---------------------------------------------------------------------------*/

    +/*
    + * Offloading support.
    + *
    + * Some SPI controllers support offloading of SPI transfers. Essentially,
    + * this allows the SPI controller to record SPI transfers and then play them
    + * back later via a hardware trigger.
    + */
    +
    +/**
    + * SPI_OFFLOAD_RX - placeholder for indicating read transfers for offloads
    + *
    + * Assign xfer->rx_buf to this value for any read transfer passed to
    + * spi_offload_prepare(). This will act as a flag to indicate to the offload
    + * that it should do something with the data read during this transfer. What
    + * that something can be is determined by the specific hardware, e.g. it could
    + * be piped to DMA or a DSP, etc.
    + */
    +#define SPI_OFFLOAD_RX_SENTINEL ((void *)1)
    +
    +/**
    + * struct spi_controller_offload_ops - callbacks for offload support
    + *
    + * Drivers for hardware with offload support need to implement all of these
    + * callbacks.
    + */
    +struct spi_controller_offload_ops {
    + /**
    + * @get: Callback to get the offload assigned to the given SPI device.
    + * Index is an index in the offloads array fwnode property of the device.
    + * Implementations must return the pointer to the device or a negative
    + * error code (return -ENODEV rather than NULL if no matching device).
    + */
    + struct spi_offload *(*get)(struct spi_device *spi, unsigned int index);
    + /**
    + * @prepare: Callback to prepare the offload for the given SPI message.
    + * @msg and any of its members (including any xfer->tx_buf) is not
    + * guaranteed to be valid beyond the lifetime of this call.
    + */
    + int (*prepare)(struct spi_offload *offload, struct spi_message *msg);
    + /**
    + * @unprepare: Callback to release any resources used by prepare().
    + */
    + void (*unprepare)(struct spi_offload *offload);
    + /**
    + * @enable: Callback to enable the offload.
    + */
    + int (*enable)(struct spi_offload *offload);
    + /**
    + * @disable: Callback to disable the offload.
    + */
    + void (*disable)(struct spi_offload *offload);
    +};
    +
    +/** struct spi_offload - offload handle */
    +struct spi_offload {
    + /** @controller: The associated SPI controller. */
    + struct spi_controller *controller;
    + /** @dev: The device associated with the offload instance. */
    + struct device *dev;
    + /** @priv: Private instance data used by the SPI controller. */
    + void *priv;
    +};
    +
    +/**
    + * spi_offload_get - gets an offload assigned to the given SPI device
    + * @spi: SPI device.
    + * @index: Index of the offload in the SPI device's fwnode int array.
    + *
    + * The lifetime of the returned offload is tied to the struct spi_controller
    + * instance. Since @spi owns a reference to the controller, most consumers
    + * should not have to do anything extra. But if the offload is passed somewhere
    + * outside of the control of the SPI device driver, then an additional reference
    + * to the controller must be made.
    + *
    + * Return: Pointer to the offload handle or negative error code.
    + */
    +static inline struct spi_offload *spi_offload_get(struct spi_device *spi,
    + unsigned int index)
    +{
    + if (!spi->controller->offload_ops)
    + return ERR_PTR(-EOPNOTSUPP);
    +
    + return spi->controller->offload_ops->get(spi, index);
    +}
    +
    +int spi_offload_prepare(struct spi_offload *offload, struct spi_device *spi,
    + struct spi_transfer *xfers, unsigned int num_xfers);
    +
    +/**
    + * spi_offload_unprepare - releases any resources used by spi_offload_prepare()
    + * @offload: The offload instance.
    + */
    +static inline void spi_offload_unprepare(struct spi_offload *offload)
    +{
    + offload->controller->offload_ops->unprepare(offload);
    +}
    +
    +/**
    + * spi_offload_enable - enables the offload
    + * @offload: The offload instance.
    + * Return: 0 on success or negative error code.
    + */
    +static inline int spi_offload_enable(struct spi_offload *offload)
    +{
    + return offload->controller->offload_ops->enable(offload);
    +}
    +
    +/**
    + * spi_offload_disable - disables the offload
    + * @offload: The offload instance.
    + */
    +static inline void spi_offload_disable(struct spi_offload *offload)
    +{
    + offload->controller->offload_ops->disable(offload);
    +}
    +
    +/*---------------------------------------------------------------------------*/
    +
    /*
    * INTERFACE between board init code and SPI infrastructure.
    *
    --
    2.43.0


    \
     
     \ /
      Last update: 2024-01-10 20:53    [W:2.486 / U:0.060 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site