Commit 87800c43 authored by Nuno Sa's avatar Nuno Sa Committed by Jonathan Cameron

iio: backend: add new functionality

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: default avatarNuno Sa <nuno.sa@analog.com>
Link: https://lore.kernel.org/r/20240419-iio-backend-axi-dac-v4-8-5ca45b4de294@analog.comSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent a33486d3
......@@ -43,10 +43,12 @@
#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;
......
......@@ -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 *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment