|
@@ -10,6 +10,7 @@
|
|
|
#include <linux/export.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/mutex.h>
|
|
|
+#include <linux/of.h>
|
|
|
|
|
|
#include <linux/iio/iio.h>
|
|
|
#include "iio_core.h"
|
|
@@ -92,6 +93,164 @@ static const struct iio_chan_spec
|
|
|
return chan;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_OF
|
|
|
+
|
|
|
+static int iio_dev_node_match(struct device *dev, void *data)
|
|
|
+{
|
|
|
+ return dev->of_node == data && dev->type == &iio_device_type;
|
|
|
+}
|
|
|
+
|
|
|
+static int __of_iio_channel_get(struct iio_channel *channel,
|
|
|
+ struct device_node *np, int index)
|
|
|
+{
|
|
|
+ struct device *idev;
|
|
|
+ struct iio_dev *indio_dev;
|
|
|
+ int err;
|
|
|
+ struct of_phandle_args iiospec;
|
|
|
+
|
|
|
+ err = of_parse_phandle_with_args(np, "io-channels",
|
|
|
+ "#io-channel-cells",
|
|
|
+ index, &iiospec);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ idev = bus_find_device(&iio_bus_type, NULL, iiospec.np,
|
|
|
+ iio_dev_node_match);
|
|
|
+ of_node_put(iiospec.np);
|
|
|
+ if (idev == NULL)
|
|
|
+ return -EPROBE_DEFER;
|
|
|
+
|
|
|
+ indio_dev = dev_to_iio_dev(idev);
|
|
|
+ channel->indio_dev = indio_dev;
|
|
|
+ index = iiospec.args_count ? iiospec.args[0] : 0;
|
|
|
+ if (index >= indio_dev->num_channels) {
|
|
|
+ return -EINVAL;
|
|
|
+ goto err_put;
|
|
|
+ }
|
|
|
+ channel->channel = &indio_dev->channels[index];
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_put:
|
|
|
+ iio_device_put(indio_dev);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static struct iio_channel *of_iio_channel_get(struct device_node *np, int index)
|
|
|
+{
|
|
|
+ struct iio_channel *channel;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (index < 0)
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+
|
|
|
+ channel = kzalloc(sizeof(*channel), GFP_KERNEL);
|
|
|
+ if (channel == NULL)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+
|
|
|
+ err = __of_iio_channel_get(channel, np, index);
|
|
|
+ if (err)
|
|
|
+ goto err_free_channel;
|
|
|
+
|
|
|
+ return channel;
|
|
|
+
|
|
|
+err_free_channel:
|
|
|
+ kfree(channel);
|
|
|
+ return ERR_PTR(err);
|
|
|
+}
|
|
|
+
|
|
|
+static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
|
|
|
+ const char *name)
|
|
|
+{
|
|
|
+ struct iio_channel *chan = NULL;
|
|
|
+
|
|
|
+ /* Walk up the tree of devices looking for a matching iio channel */
|
|
|
+ while (np) {
|
|
|
+ int index = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * For named iio channels, first look up the name in the
|
|
|
+ * "io-channel-names" property. If it cannot be found, the
|
|
|
+ * index will be an error code, and of_iio_channel_get()
|
|
|
+ * will fail.
|
|
|
+ */
|
|
|
+ if (name)
|
|
|
+ index = of_property_match_string(np, "io-channel-names",
|
|
|
+ name);
|
|
|
+ chan = of_iio_channel_get(np, index);
|
|
|
+ if (!IS_ERR(chan))
|
|
|
+ break;
|
|
|
+ else if (name && index >= 0) {
|
|
|
+ pr_err("ERROR: could not get IIO channel %s:%s(%i)\n",
|
|
|
+ np->full_name, name ? name : "", index);
|
|
|
+ return chan;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * No matching IIO channel found on this node.
|
|
|
+ * If the parent node has a "io-channel-ranges" property,
|
|
|
+ * then we can try one of its channels.
|
|
|
+ */
|
|
|
+ np = np->parent;
|
|
|
+ if (np && !of_get_property(np, "io-channel-ranges", NULL))
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return chan;
|
|
|
+}
|
|
|
+
|
|
|
+static struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
|
|
+{
|
|
|
+ struct iio_channel *chans;
|
|
|
+ int i, mapind, nummaps = 0;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ do {
|
|
|
+ ret = of_parse_phandle_with_args(dev->of_node,
|
|
|
+ "io-channels",
|
|
|
+ "#io-channel-cells",
|
|
|
+ nummaps, NULL);
|
|
|
+ if (ret < 0)
|
|
|
+ break;
|
|
|
+ } while (++nummaps);
|
|
|
+
|
|
|
+ if (nummaps == 0) /* no error, return NULL to search map table */
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* NULL terminated array to save passing size */
|
|
|
+ chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
|
|
|
+ if (chans == NULL)
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
+
|
|
|
+ /* Search for OF matches */
|
|
|
+ for (mapind = 0; mapind < nummaps; mapind++) {
|
|
|
+ ret = __of_iio_channel_get(&chans[mapind], dev->of_node,
|
|
|
+ mapind);
|
|
|
+ if (ret)
|
|
|
+ goto error_free_chans;
|
|
|
+ }
|
|
|
+ return chans;
|
|
|
+
|
|
|
+error_free_chans:
|
|
|
+ for (i = 0; i < mapind; i++)
|
|
|
+ iio_device_put(chans[i].indio_dev);
|
|
|
+ kfree(chans);
|
|
|
+ return ERR_PTR(ret);
|
|
|
+}
|
|
|
+
|
|
|
+#else /* CONFIG_OF */
|
|
|
+
|
|
|
+static inline struct iio_channel *
|
|
|
+of_iio_channel_get_by_name(struct device_node *np, const char *name)
|
|
|
+{
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static inline struct iio_channel *of_iio_channel_get_all(struct device *dev)
|
|
|
+{
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+#endif /* CONFIG_OF */
|
|
|
|
|
|
static struct iio_channel *iio_channel_get_sys(const char *name,
|
|
|
const char *channel_name)
|
|
@@ -150,7 +309,14 @@ struct iio_channel *iio_channel_get(struct device *dev,
|
|
|
const char *channel_name)
|
|
|
{
|
|
|
const char *name = dev ? dev_name(dev) : NULL;
|
|
|
+ struct iio_channel *channel;
|
|
|
|
|
|
+ if (dev) {
|
|
|
+ channel = of_iio_channel_get_by_name(dev->of_node,
|
|
|
+ channel_name);
|
|
|
+ if (channel != NULL)
|
|
|
+ return channel;
|
|
|
+ }
|
|
|
return iio_channel_get_sys(name, channel_name);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(iio_channel_get);
|
|
@@ -173,6 +339,11 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
|
|
|
|
|
|
if (dev == NULL)
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
+
|
|
|
+ chans = of_iio_channel_get_all(dev);
|
|
|
+ if (chans)
|
|
|
+ return chans;
|
|
|
+
|
|
|
name = dev_name(dev);
|
|
|
|
|
|
mutex_lock(&iio_map_list_lock);
|