18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IIO multiplexer driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Axentia Technologies AB 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Peter Rosin <peda@axentia.se> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h> 128c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/mux/consumer.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct mux_ext_info_cache { 208c2ecf20Sopenharmony_ci char *data; 218c2ecf20Sopenharmony_ci ssize_t size; 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct mux_child { 258c2ecf20Sopenharmony_ci struct mux_ext_info_cache *ext_info_cache; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct mux { 298c2ecf20Sopenharmony_ci int cached_state; 308c2ecf20Sopenharmony_ci struct mux_control *control; 318c2ecf20Sopenharmony_ci struct iio_channel *parent; 328c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 338c2ecf20Sopenharmony_ci struct iio_chan_spec *chan; 348c2ecf20Sopenharmony_ci struct iio_chan_spec_ext_info *ext_info; 358c2ecf20Sopenharmony_ci struct mux_child *child; 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int iio_mux_select(struct mux *mux, int idx) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct mux_child *child = &mux->child[idx]; 418c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan = &mux->chan[idx]; 428c2ecf20Sopenharmony_ci int ret; 438c2ecf20Sopenharmony_ci int i; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci ret = mux_control_select(mux->control, chan->channel); 468c2ecf20Sopenharmony_ci if (ret < 0) { 478c2ecf20Sopenharmony_ci mux->cached_state = -1; 488c2ecf20Sopenharmony_ci return ret; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (mux->cached_state == chan->channel) 528c2ecf20Sopenharmony_ci return 0; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (chan->ext_info) { 558c2ecf20Sopenharmony_ci for (i = 0; chan->ext_info[i].name; ++i) { 568c2ecf20Sopenharmony_ci const char *attr = chan->ext_info[i].name; 578c2ecf20Sopenharmony_ci struct mux_ext_info_cache *cache; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci cache = &child->ext_info_cache[i]; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (cache->size < 0) 628c2ecf20Sopenharmony_ci continue; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci ret = iio_write_channel_ext_info(mux->parent, attr, 658c2ecf20Sopenharmony_ci cache->data, 668c2ecf20Sopenharmony_ci cache->size); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (ret < 0) { 698c2ecf20Sopenharmony_ci mux_control_deselect(mux->control); 708c2ecf20Sopenharmony_ci mux->cached_state = -1; 718c2ecf20Sopenharmony_ci return ret; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci mux->cached_state = chan->channel; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void iio_mux_deselect(struct mux *mux) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci mux_control_deselect(mux->control); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int mux_read_raw(struct iio_dev *indio_dev, 868c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 878c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct mux *mux = iio_priv(indio_dev); 908c2ecf20Sopenharmony_ci int idx = chan - mux->chan; 918c2ecf20Sopenharmony_ci int ret; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = iio_mux_select(mux, idx); 948c2ecf20Sopenharmony_ci if (ret < 0) 958c2ecf20Sopenharmony_ci return ret; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci switch (mask) { 988c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 998c2ecf20Sopenharmony_ci ret = iio_read_channel_raw(mux->parent, val); 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 1038c2ecf20Sopenharmony_ci ret = iio_read_channel_scale(mux->parent, val, val2); 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci default: 1078c2ecf20Sopenharmony_ci ret = -EINVAL; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci iio_mux_deselect(mux); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int mux_read_avail(struct iio_dev *indio_dev, 1168c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1178c2ecf20Sopenharmony_ci const int **vals, int *type, int *length, 1188c2ecf20Sopenharmony_ci long mask) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct mux *mux = iio_priv(indio_dev); 1218c2ecf20Sopenharmony_ci int idx = chan - mux->chan; 1228c2ecf20Sopenharmony_ci int ret; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci ret = iio_mux_select(mux, idx); 1258c2ecf20Sopenharmony_ci if (ret < 0) 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci switch (mask) { 1298c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1308c2ecf20Sopenharmony_ci *type = IIO_VAL_INT; 1318c2ecf20Sopenharmony_ci ret = iio_read_avail_channel_raw(mux->parent, vals, length); 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci default: 1358c2ecf20Sopenharmony_ci ret = -EINVAL; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci iio_mux_deselect(mux); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int mux_write_raw(struct iio_dev *indio_dev, 1448c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1458c2ecf20Sopenharmony_ci int val, int val2, long mask) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct mux *mux = iio_priv(indio_dev); 1488c2ecf20Sopenharmony_ci int idx = chan - mux->chan; 1498c2ecf20Sopenharmony_ci int ret; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ret = iio_mux_select(mux, idx); 1528c2ecf20Sopenharmony_ci if (ret < 0) 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci switch (mask) { 1568c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1578c2ecf20Sopenharmony_ci ret = iio_write_channel_raw(mux->parent, val); 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci default: 1618c2ecf20Sopenharmony_ci ret = -EINVAL; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci iio_mux_deselect(mux); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic const struct iio_info mux_info = { 1708c2ecf20Sopenharmony_ci .read_raw = mux_read_raw, 1718c2ecf20Sopenharmony_ci .read_avail = mux_read_avail, 1728c2ecf20Sopenharmony_ci .write_raw = mux_write_raw, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic ssize_t mux_read_ext_info(struct iio_dev *indio_dev, uintptr_t private, 1768c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, char *buf) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct mux *mux = iio_priv(indio_dev); 1798c2ecf20Sopenharmony_ci int idx = chan - mux->chan; 1808c2ecf20Sopenharmony_ci ssize_t ret; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ret = iio_mux_select(mux, idx); 1838c2ecf20Sopenharmony_ci if (ret < 0) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ret = iio_read_channel_ext_info(mux->parent, 1878c2ecf20Sopenharmony_ci mux->ext_info[private].name, 1888c2ecf20Sopenharmony_ci buf); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci iio_mux_deselect(mux); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return ret; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic ssize_t mux_write_ext_info(struct iio_dev *indio_dev, uintptr_t private, 1968c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1978c2ecf20Sopenharmony_ci const char *buf, size_t len) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct device *dev = indio_dev->dev.parent; 2008c2ecf20Sopenharmony_ci struct mux *mux = iio_priv(indio_dev); 2018c2ecf20Sopenharmony_ci int idx = chan - mux->chan; 2028c2ecf20Sopenharmony_ci char *new; 2038c2ecf20Sopenharmony_ci ssize_t ret; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (len >= PAGE_SIZE) 2068c2ecf20Sopenharmony_ci return -EINVAL; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci ret = iio_mux_select(mux, idx); 2098c2ecf20Sopenharmony_ci if (ret < 0) 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci new = devm_kmemdup(dev, buf, len + 1, GFP_KERNEL); 2138c2ecf20Sopenharmony_ci if (!new) { 2148c2ecf20Sopenharmony_ci iio_mux_deselect(mux); 2158c2ecf20Sopenharmony_ci return -ENOMEM; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci new[len] = 0; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ret = iio_write_channel_ext_info(mux->parent, 2218c2ecf20Sopenharmony_ci mux->ext_info[private].name, 2228c2ecf20Sopenharmony_ci buf, len); 2238c2ecf20Sopenharmony_ci if (ret < 0) { 2248c2ecf20Sopenharmony_ci iio_mux_deselect(mux); 2258c2ecf20Sopenharmony_ci devm_kfree(dev, new); 2268c2ecf20Sopenharmony_ci return ret; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci devm_kfree(dev, mux->child[idx].ext_info_cache[private].data); 2308c2ecf20Sopenharmony_ci mux->child[idx].ext_info_cache[private].data = new; 2318c2ecf20Sopenharmony_ci mux->child[idx].ext_info_cache[private].size = len; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci iio_mux_deselect(mux); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int mux_configure_channel(struct device *dev, struct mux *mux, 2398c2ecf20Sopenharmony_ci u32 state, const char *label, int idx) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct mux_child *child = &mux->child[idx]; 2428c2ecf20Sopenharmony_ci struct iio_chan_spec *chan = &mux->chan[idx]; 2438c2ecf20Sopenharmony_ci struct iio_chan_spec const *pchan = mux->parent->channel; 2448c2ecf20Sopenharmony_ci char *page = NULL; 2458c2ecf20Sopenharmony_ci int num_ext_info; 2468c2ecf20Sopenharmony_ci int i; 2478c2ecf20Sopenharmony_ci int ret; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci chan->indexed = 1; 2508c2ecf20Sopenharmony_ci chan->output = pchan->output; 2518c2ecf20Sopenharmony_ci chan->datasheet_name = label; 2528c2ecf20Sopenharmony_ci chan->ext_info = mux->ext_info; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = iio_get_channel_type(mux->parent, &chan->type); 2558c2ecf20Sopenharmony_ci if (ret < 0) { 2568c2ecf20Sopenharmony_ci dev_err(dev, "failed to get parent channel type\n"); 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (iio_channel_has_info(pchan, IIO_CHAN_INFO_RAW)) 2618c2ecf20Sopenharmony_ci chan->info_mask_separate |= BIT(IIO_CHAN_INFO_RAW); 2628c2ecf20Sopenharmony_ci if (iio_channel_has_info(pchan, IIO_CHAN_INFO_SCALE)) 2638c2ecf20Sopenharmony_ci chan->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (iio_channel_has_available(pchan, IIO_CHAN_INFO_RAW)) 2668c2ecf20Sopenharmony_ci chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (state >= mux_control_states(mux->control)) { 2698c2ecf20Sopenharmony_ci dev_err(dev, "too many channels\n"); 2708c2ecf20Sopenharmony_ci return -EINVAL; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci chan->channel = state; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci num_ext_info = iio_get_channel_ext_info_count(mux->parent); 2768c2ecf20Sopenharmony_ci if (num_ext_info) { 2778c2ecf20Sopenharmony_ci page = devm_kzalloc(dev, PAGE_SIZE, GFP_KERNEL); 2788c2ecf20Sopenharmony_ci if (!page) 2798c2ecf20Sopenharmony_ci return -ENOMEM; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci child->ext_info_cache = devm_kcalloc(dev, 2828c2ecf20Sopenharmony_ci num_ext_info, 2838c2ecf20Sopenharmony_ci sizeof(*child->ext_info_cache), 2848c2ecf20Sopenharmony_ci GFP_KERNEL); 2858c2ecf20Sopenharmony_ci if (!child->ext_info_cache) 2868c2ecf20Sopenharmony_ci return -ENOMEM; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci for (i = 0; i < num_ext_info; ++i) { 2898c2ecf20Sopenharmony_ci child->ext_info_cache[i].size = -1; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (!pchan->ext_info[i].write) 2928c2ecf20Sopenharmony_ci continue; 2938c2ecf20Sopenharmony_ci if (!pchan->ext_info[i].read) 2948c2ecf20Sopenharmony_ci continue; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ret = iio_read_channel_ext_info(mux->parent, 2978c2ecf20Sopenharmony_ci mux->ext_info[i].name, 2988c2ecf20Sopenharmony_ci page); 2998c2ecf20Sopenharmony_ci if (ret < 0) { 3008c2ecf20Sopenharmony_ci dev_err(dev, "failed to get ext_info '%s'\n", 3018c2ecf20Sopenharmony_ci pchan->ext_info[i].name); 3028c2ecf20Sopenharmony_ci return ret; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci if (ret >= PAGE_SIZE) { 3058c2ecf20Sopenharmony_ci dev_err(dev, "too large ext_info '%s'\n", 3068c2ecf20Sopenharmony_ci pchan->ext_info[i].name); 3078c2ecf20Sopenharmony_ci return -EINVAL; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci child->ext_info_cache[i].data = devm_kmemdup(dev, page, ret + 1, 3118c2ecf20Sopenharmony_ci GFP_KERNEL); 3128c2ecf20Sopenharmony_ci if (!child->ext_info_cache[i].data) 3138c2ecf20Sopenharmony_ci return -ENOMEM; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci child->ext_info_cache[i].data[ret] = 0; 3168c2ecf20Sopenharmony_ci child->ext_info_cache[i].size = ret; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (page) 3208c2ecf20Sopenharmony_ci devm_kfree(dev, page); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* 3268c2ecf20Sopenharmony_ci * Same as of_property_for_each_string(), but also keeps track of the 3278c2ecf20Sopenharmony_ci * index of each string. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci#define of_property_for_each_string_index(np, propname, prop, s, i) \ 3308c2ecf20Sopenharmony_ci for (prop = of_find_property(np, propname, NULL), \ 3318c2ecf20Sopenharmony_ci s = of_prop_next_string(prop, NULL), \ 3328c2ecf20Sopenharmony_ci i = 0; \ 3338c2ecf20Sopenharmony_ci s; \ 3348c2ecf20Sopenharmony_ci s = of_prop_next_string(prop, s), \ 3358c2ecf20Sopenharmony_ci i++) 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int mux_probe(struct platform_device *pdev) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3408c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 3418c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 3428c2ecf20Sopenharmony_ci struct iio_channel *parent; 3438c2ecf20Sopenharmony_ci struct mux *mux; 3448c2ecf20Sopenharmony_ci struct property *prop; 3458c2ecf20Sopenharmony_ci const char *label; 3468c2ecf20Sopenharmony_ci u32 state; 3478c2ecf20Sopenharmony_ci int sizeof_ext_info; 3488c2ecf20Sopenharmony_ci int children; 3498c2ecf20Sopenharmony_ci int sizeof_priv; 3508c2ecf20Sopenharmony_ci int i; 3518c2ecf20Sopenharmony_ci int ret; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (!np) 3548c2ecf20Sopenharmony_ci return -ENODEV; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci parent = devm_iio_channel_get(dev, "parent"); 3578c2ecf20Sopenharmony_ci if (IS_ERR(parent)) 3588c2ecf20Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(parent), 3598c2ecf20Sopenharmony_ci "failed to get parent channel\n"); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci sizeof_ext_info = iio_get_channel_ext_info_count(parent); 3628c2ecf20Sopenharmony_ci if (sizeof_ext_info) { 3638c2ecf20Sopenharmony_ci sizeof_ext_info += 1; /* one extra entry for the sentinel */ 3648c2ecf20Sopenharmony_ci sizeof_ext_info *= sizeof(*mux->ext_info); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci children = 0; 3688c2ecf20Sopenharmony_ci of_property_for_each_string(np, "channels", prop, label) { 3698c2ecf20Sopenharmony_ci if (*label) 3708c2ecf20Sopenharmony_ci children++; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci if (children <= 0) { 3738c2ecf20Sopenharmony_ci dev_err(dev, "not even a single child\n"); 3748c2ecf20Sopenharmony_ci return -EINVAL; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci sizeof_priv = sizeof(*mux); 3788c2ecf20Sopenharmony_ci sizeof_priv += sizeof(*mux->child) * children; 3798c2ecf20Sopenharmony_ci sizeof_priv += sizeof(*mux->chan) * children; 3808c2ecf20Sopenharmony_ci sizeof_priv += sizeof_ext_info; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof_priv); 3838c2ecf20Sopenharmony_ci if (!indio_dev) 3848c2ecf20Sopenharmony_ci return -ENOMEM; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci mux = iio_priv(indio_dev); 3878c2ecf20Sopenharmony_ci mux->child = (struct mux_child *)(mux + 1); 3888c2ecf20Sopenharmony_ci mux->chan = (struct iio_chan_spec *)(mux->child + children); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, indio_dev); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci mux->parent = parent; 3938c2ecf20Sopenharmony_ci mux->cached_state = -1; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci indio_dev->name = dev_name(dev); 3968c2ecf20Sopenharmony_ci indio_dev->info = &mux_info; 3978c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 3988c2ecf20Sopenharmony_ci indio_dev->channels = mux->chan; 3998c2ecf20Sopenharmony_ci indio_dev->num_channels = children; 4008c2ecf20Sopenharmony_ci if (sizeof_ext_info) { 4018c2ecf20Sopenharmony_ci mux->ext_info = devm_kmemdup(dev, 4028c2ecf20Sopenharmony_ci parent->channel->ext_info, 4038c2ecf20Sopenharmony_ci sizeof_ext_info, GFP_KERNEL); 4048c2ecf20Sopenharmony_ci if (!mux->ext_info) 4058c2ecf20Sopenharmony_ci return -ENOMEM; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci for (i = 0; mux->ext_info[i].name; ++i) { 4088c2ecf20Sopenharmony_ci if (parent->channel->ext_info[i].read) 4098c2ecf20Sopenharmony_ci mux->ext_info[i].read = mux_read_ext_info; 4108c2ecf20Sopenharmony_ci if (parent->channel->ext_info[i].write) 4118c2ecf20Sopenharmony_ci mux->ext_info[i].write = mux_write_ext_info; 4128c2ecf20Sopenharmony_ci mux->ext_info[i].private = i; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci mux->control = devm_mux_control_get(dev, NULL); 4178c2ecf20Sopenharmony_ci if (IS_ERR(mux->control)) { 4188c2ecf20Sopenharmony_ci if (PTR_ERR(mux->control) != -EPROBE_DEFER) 4198c2ecf20Sopenharmony_ci dev_err(dev, "failed to get control-mux\n"); 4208c2ecf20Sopenharmony_ci return PTR_ERR(mux->control); 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci i = 0; 4248c2ecf20Sopenharmony_ci of_property_for_each_string_index(np, "channels", prop, label, state) { 4258c2ecf20Sopenharmony_ci if (!*label) 4268c2ecf20Sopenharmony_ci continue; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ret = mux_configure_channel(dev, mux, state, label, i++); 4298c2ecf20Sopenharmony_ci if (ret < 0) 4308c2ecf20Sopenharmony_ci return ret; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ret = devm_iio_device_register(dev, indio_dev); 4348c2ecf20Sopenharmony_ci if (ret) { 4358c2ecf20Sopenharmony_ci dev_err(dev, "failed to register iio device\n"); 4368c2ecf20Sopenharmony_ci return ret; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic const struct of_device_id mux_match[] = { 4438c2ecf20Sopenharmony_ci { .compatible = "io-channel-mux" }, 4448c2ecf20Sopenharmony_ci { /* sentinel */ } 4458c2ecf20Sopenharmony_ci}; 4468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mux_match); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_cistatic struct platform_driver mux_driver = { 4498c2ecf20Sopenharmony_ci .probe = mux_probe, 4508c2ecf20Sopenharmony_ci .driver = { 4518c2ecf20Sopenharmony_ci .name = "iio-mux", 4528c2ecf20Sopenharmony_ci .of_match_table = mux_match, 4538c2ecf20Sopenharmony_ci }, 4548c2ecf20Sopenharmony_ci}; 4558c2ecf20Sopenharmony_cimodule_platform_driver(mux_driver); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IIO multiplexer driver"); 4588c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); 4598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 460