iio: backend: add new functionality
authorNuno Sa <nuno.sa@analog.com>
Fri, 19 Apr 2024 08:25:41 +0000 (10:25 +0200)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Sat, 20 Apr 2024 14:41:32 +0000 (15:41 +0100)
This adds the needed backend ops for supporting a backend inerfacing
with an high speed dac. The new ops are:

* data_source_set();
* set_sampling_freq();
* extend_chan_spec();
* ext_info_set();
* ext_info_get().

Also to note the new helpers that are meant to be used by the backends
when extending an IIO channel (adding extended info):

* iio_backend_ext_info_set();
* iio_backend_ext_info_get().

Signed-off-by: Nuno Sa <nuno.sa@analog.com>
Link: https://lore.kernel.org/r/20240419-iio-backend-axi-dac-v4-8-5ca45b4de294@analog.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/industrialio-backend.c
include/linux/iio/backend.h

index 2fea2bbbe47fd00b476bcb9299febe70a6335f26..f08ed6d70ae590a90b688d6ccc475a3fde8e6e2d 100644 (file)
 #include <linux/types.h>
 
 #include <linux/iio/backend.h>
+#include <linux/iio/iio.h>
 
 struct iio_backend {
        struct list_head entry;
        const struct iio_backend_ops *ops;
+       struct device *frontend_dev;
        struct device *dev;
        struct module *owner;
        void *priv;
@@ -186,6 +188,44 @@ int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
 }
 EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND);
 
+/**
+ * iio_backend_data_source_set - Select data source
+ * @back:      Backend device
+ * @chan:      Channel number
+ * @data:      Data source
+ *
+ * A given backend may have different sources to stream/sync data. This allows
+ * to choose that source.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+                               enum iio_backend_data_source data)
+{
+       if (data >= IIO_BACKEND_DATA_SOURCE_MAX)
+               return -EINVAL;
+
+       return iio_backend_op_call(back, data_source_set, chan, data);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_set, IIO_BACKEND);
+
+/**
+ * iio_backend_set_sampling_freq - Set channel sampling rate
+ * @back:              Backend device
+ * @chan:              Channel number
+ * @sample_rate_hz:    Sample rate
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+                                 u64 sample_rate_hz)
+{
+       return iio_backend_op_call(back, set_sample_rate, chan, sample_rate_hz);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_set_sampling_freq, IIO_BACKEND);
+
 static void iio_backend_free_buffer(void *arg)
 {
        struct iio_backend_buffer_pair *pair = arg;
@@ -231,6 +271,143 @@ int devm_iio_backend_request_buffer(struct device *dev,
 }
 EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND);
 
+static struct iio_backend *iio_backend_from_indio_dev_parent(const struct device *dev)
+{
+       struct iio_backend *back = ERR_PTR(-ENODEV), *iter;
+
+       /*
+        * We deliberately go through all backends even after finding a match.
+        * The reason is that we want to catch frontend devices which have more
+        * than one backend in which case returning the first we find is bogus.
+        * For those cases, frontends need to explicitly define
+        * get_iio_backend() in struct iio_info.
+        */
+       guard(mutex)(&iio_back_lock);
+       list_for_each_entry(iter, &iio_back_list, entry) {
+               if (dev == iter->frontend_dev) {
+                       if (!IS_ERR(back)) {
+                               dev_warn(dev,
+                                        "Multiple backends! get_iio_backend() needs to be implemented");
+                               return ERR_PTR(-ENODEV);
+                       }
+
+                       back = iter;
+               }
+       }
+
+       return back;
+}
+
+/**
+ * iio_backend_ext_info_get - IIO ext_info read callback
+ * @indio_dev: IIO device
+ * @private:   Data private to the driver
+ * @chan:      IIO channel
+ * @buf:       Buffer where to place the attribute data
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (through iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not have
+ * a way to get the backend from indio_dev). This is the getter.
+ *
+ * RETURNS:
+ * Number of bytes written to buf, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+                                const struct iio_chan_spec *chan, char *buf)
+{
+       struct iio_backend *back;
+
+       /*
+        * The below should work for the majority of the cases. It will not work
+        * when one frontend has multiple backends in which case we'll need a
+        * new callback in struct iio_info so we can directly request the proper
+        * backend from the frontend. Anyways, let's only introduce new options
+        * when really needed...
+        */
+       back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent);
+       if (IS_ERR(back))
+               return PTR_ERR(back);
+
+       return iio_backend_op_call(back, ext_info_get, private, chan, buf);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, IIO_BACKEND);
+
+/**
+ * iio_backend_ext_info_set - IIO ext_info write callback
+ * @indio_dev: IIO device
+ * @private:   Data private to the driver
+ * @chan:      IIO channel
+ * @buf:       Buffer holding the sysfs attribute
+ * @len:       Buffer length
+ *
+ * This helper is intended to be used by backends that extend an IIO channel
+ * (trough iio_backend_extend_chan_spec()) with extended info. In that case,
+ * backends are not supposed to give their own callbacks (as they would not have
+ * a way to get the backend from indio_dev). This is the setter.
+ *
+ * RETURNS:
+ * Buffer length on success, negative error number on failure.
+ */
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+                                const struct iio_chan_spec *chan,
+                                const char *buf, size_t len)
+{
+       struct iio_backend *back;
+
+       back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent);
+       if (IS_ERR(back))
+               return PTR_ERR(back);
+
+       return iio_backend_op_call(back, ext_info_set, private, chan, buf, len);
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND);
+
+/**
+ * iio_backend_extend_chan_spec - Extend an IIO channel
+ * @indio_dev: IIO device
+ * @back:      Backend device
+ * @chan:      IIO channel
+ *
+ * Some backends may have their own functionalities and hence capable of
+ * extending a frontend's channel.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+                                struct iio_backend *back,
+                                struct iio_chan_spec *chan)
+{
+       const struct iio_chan_spec_ext_info *frontend_ext_info = chan->ext_info;
+       const struct iio_chan_spec_ext_info *back_ext_info;
+       int ret;
+
+       ret = iio_backend_op_call(back, extend_chan_spec, chan);
+       if (ret)
+               return ret;
+       /*
+        * Let's keep things simple for now. Don't allow to overwrite the
+        * frontend's extended info. If ever needed, we can support appending
+        * it.
+        */
+       if (frontend_ext_info && chan->ext_info != frontend_ext_info)
+               return -EOPNOTSUPP;
+       if (!chan->ext_info)
+               return 0;
+
+       /* Don't allow backends to get creative and force their own handlers */
+       for (back_ext_info = chan->ext_info; back_ext_info->name; back_ext_info++) {
+               if (back_ext_info->read != iio_backend_ext_info_get)
+                       return -EINVAL;
+               if (back_ext_info->write != iio_backend_ext_info_set)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);
+
 static void iio_backend_release(void *arg)
 {
        struct iio_backend *back = arg;
@@ -263,6 +440,8 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back)
                                     "Could not link to supplier(%s)\n",
                                     dev_name(back->dev));
 
+       back->frontend_dev = dev;
+
        dev_dbg(dev, "Found backend(%s) device\n", dev_name(back->dev));
 
        return 0;
index a6d79381866ece88d6e5e2e003a523fec112c3e3..9d144631134db01eb4fd668cf6cccd831752ee67 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <linux/types.h>
 
+struct iio_chan_spec;
 struct fwnode_handle;
 struct iio_backend;
 struct device;
@@ -15,6 +16,26 @@ enum iio_backend_data_type {
        IIO_BACKEND_DATA_TYPE_MAX
 };
 
+enum iio_backend_data_source {
+       IIO_BACKEND_INTERNAL_CONTINUOS_WAVE,
+       IIO_BACKEND_EXTERNAL,
+       IIO_BACKEND_DATA_SOURCE_MAX
+};
+
+/**
+ * IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
+ * @_name:     Attribute name
+ * @_shared:   Whether the attribute is shared between all channels
+ * @_what:     Data private to the driver
+ */
+#define IIO_BACKEND_EX_INFO(_name, _shared, _what) {   \
+       .name = (_name),                                \
+       .shared = (_shared),                            \
+       .read =  iio_backend_ext_info_get,              \
+       .write = iio_backend_ext_info_set,              \
+       .private = (_what),                             \
+}
+
 /**
  * struct iio_backend_data_fmt - Backend data format
  * @type:              Data type.
@@ -35,8 +56,13 @@ struct iio_backend_data_fmt {
  * @chan_enable:       Enable one channel.
  * @chan_disable:      Disable one channel.
  * @data_format_set:   Configure the data format for a specific channel.
+ * @data_source_set:   Configure the data source for a specific channel.
+ * @set_sample_rate:   Configure the sampling rate for a specific channel.
  * @request_buffer:    Request an IIO buffer.
  * @free_buffer:       Free an IIO buffer.
+ * @extend_chan_spec:  Extend an IIO channel.
+ * @ext_info_set:      Extended info setter.
+ * @ext_info_get:      Extended info getter.
  **/
 struct iio_backend_ops {
        int (*enable)(struct iio_backend *back);
@@ -45,10 +71,21 @@ struct iio_backend_ops {
        int (*chan_disable)(struct iio_backend *back, unsigned int chan);
        int (*data_format_set)(struct iio_backend *back, unsigned int chan,
                               const struct iio_backend_data_fmt *data);
+       int (*data_source_set)(struct iio_backend *back, unsigned int chan,
+                              enum iio_backend_data_source data);
+       int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
+                              u64 sample_rate_hz);
        struct iio_buffer *(*request_buffer)(struct iio_backend *back,
                                             struct iio_dev *indio_dev);
        void (*free_buffer)(struct iio_backend *back,
                            struct iio_buffer *buffer);
+       int (*extend_chan_spec)(struct iio_backend *back,
+                               struct iio_chan_spec *chan);
+       int (*ext_info_set)(struct iio_backend *back, uintptr_t private,
+                           const struct iio_chan_spec *chan,
+                           const char *buf, size_t len);
+       int (*ext_info_get)(struct iio_backend *back, uintptr_t private,
+                           const struct iio_chan_spec *chan, char *buf);
 };
 
 int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan);
@@ -56,10 +93,22 @@ int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan);
 int devm_iio_backend_enable(struct device *dev, struct iio_backend *back);
 int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
                                const struct iio_backend_data_fmt *data);
+int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
+                               enum iio_backend_data_source data);
+int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
+                                 u64 sample_rate_hz);
 int devm_iio_backend_request_buffer(struct device *dev,
                                    struct iio_backend *back,
                                    struct iio_dev *indio_dev);
+ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
+                                const struct iio_chan_spec *chan,
+                                const char *buf, size_t len);
+ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
+                                const struct iio_chan_spec *chan, char *buf);
 
+int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
+                                struct iio_backend *back,
+                                struct iio_chan_spec *chan);
 void *iio_backend_get_priv(const struct iio_backend *conv);
 struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name);
 struct iio_backend *