// SPDX-License-Identifier: GPL-2.0-only
/*
* Framework to handle complex IIO aggregate devices.
*
* The typical architecture is to have one device as the frontend device which
* can be "linked" against one or multiple backend devices. All the IIO and
* userspace interface is expected to be registers/managed by the frontend
* device which will callback into the backends when needed (to get/set some
* configuration that it does not directly control).
*
* -------------------------------------------------------
* ------------------ | ------------ ------------ ------- FPGA|
* | ADC |------------------------| | ADC CORE |---------| DMA CORE |------| RAM | |
* | (Frontend/IIO) | Serial Data (eg: LVDS) | |(backend) |---------| |------| | |
* | |------------------------| ------------ ------------ ------- |
* ------------------ -------------------------------------------------------
*
* The framework interface is pretty simple:
* - Backends should register themselves with devm_iio_backend_register()
* - Frontend devices should get backends with devm_iio_backend_get()
*
* Also to note that the primary target for this framework are converters like
* ADC/DACs so iio_backend_ops will have some operations typical of converter
* devices. On top of that, this is "generic" for all IIO which means any kind
* of device can make use of the framework. That said, If the iio_backend_ops
* struct begins to grow out of control, we can always refactor things so that
* the industrialio-backend.c is only left with the really generic stuff. Then,
* we can build on top of it depending on the needs.
*
* Copyright (C) 2023-2024 Analog Devices Inc.
*/
#define dev_fmt(fmt) "iio-backend: " fmt
#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/stringify.h>
#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;
const char *name;
unsigned int cached_reg_addr;
/*
* This index is relative to the frontend. Meaning that for
* frontends with multiple backends, this will be the index of this
* backend. Used for the debugfs directory name.
*/
u8 idx;
};
/*
* Helper struct for requesting buffers. This ensures that we have all data
* that we need to free the buffer in a device managed action.
*/
struct iio_backend_buffer_pair {
struct iio_backend *back;
struct iio_buffer *buffer;
};
static LIST_HEAD(iio_back_list);
static DEFINE_MUTEX(iio_back_lock);
/*
* Helper macros to call backend ops. Makes sure the option is supported.
*/
#define iio_backend_check_op(back, op) ({ \
struct iio_backend *____back = back; \
int ____ret = 0; \
\
if (!____back->ops->op) \
____ret = -EOPNOTSUPP; \
\
____ret; \
})
#define iio_backend_op_call(back, op, args...) ({ \
struct iio_backend *__back = back; \
int __ret; \
\
__ret = iio_backend_check_op(__back, op); \
if (!__ret) \
__ret = __back->ops->op(__back, ##args); \
\
__ret; \
})
#define iio_backend_ptr_op_call(back, op, args...) ({ \
struct iio_backend *__back = back; \
void *ptr_err; \
int __ret; \
\
__ret = iio_backend_check_op(__back, op); \
if (__ret) \
ptr_err = ERR_PTR(__ret); \
else \
ptr_err = __back->ops->op(__back, ##args); \
\
ptr_err; \
})
#define iio_backend_void_op_call(back, op, args...) { \
struct iio_backend *__back = back; \
int __ret; \
\
__ret = iio_backend_check_op(__back, op); \
if (!__ret) \
__back->ops->op(__back, ##args); \
else \
dev_dbg(__back->dev, "Op(%s) not implemented\n",\
__stringify(op)); \
}
static ssize_t iio_backend_debugfs_read_reg(struct file *file,
char __user *userbuf,
size_t count, loff_t *ppos)
{
struct iio_backend *back = file->private_data;
char read_buf[20];
unsigned int val;
int ret, len;
ret = iio_backend_op_call(back, debugfs_reg_access,
back->cached_reg_addr, 0, &val);
if (ret)
return ret;
len = scnprintf(read_buf, sizeof(read_buf), "0x%X\n", val);
return simple_read_from_buffer(userbuf, count, ppos, read_buf, len);
}
static ssize_t iio_backend_debugfs_write_reg(struct file *file,
const char __user *userbuf,
size_t count, loff_t *ppos)
{
struct iio_backend *back = file->private_data;
unsigned int val;
char buf[80];
ssize_t rc;
int ret;
if (count >= sizeof(buf))
return -ENOSPC;
rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, userbuf, count);
if (rc < 0)
return rc;
buf[rc] = '\0&