1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2a980e046SJonathan Cameron /* The industrial I/O core in kernel channel mapping 3a980e046SJonathan Cameron * 4a980e046SJonathan Cameron * Copyright (c) 2011 Jonathan Cameron 5a980e046SJonathan Cameron */ 6a980e046SJonathan Cameron #include <linux/err.h> 7a980e046SJonathan Cameron #include <linux/export.h> 81e64b9c5SNuno Sá #include <linux/property.h> 9a980e046SJonathan Cameron #include <linux/slab.h> 10a980e046SJonathan Cameron #include <linux/mutex.h> 11a980e046SJonathan Cameron 12a980e046SJonathan Cameron #include <linux/iio/iio.h> 13b804e2b7SJonathan Cameron #include <linux/iio/iio-opaque.h> 14a980e046SJonathan Cameron #include "iio_core.h" 15a980e046SJonathan Cameron #include <linux/iio/machine.h> 16a980e046SJonathan Cameron #include <linux/iio/driver.h> 17a980e046SJonathan Cameron #include <linux/iio/consumer.h> 18a980e046SJonathan Cameron 19a980e046SJonathan Cameron struct iio_map_internal { 20a980e046SJonathan Cameron struct iio_dev *indio_dev; 21a980e046SJonathan Cameron struct iio_map *map; 22a980e046SJonathan Cameron struct list_head l; 23a980e046SJonathan Cameron }; 24a980e046SJonathan Cameron 25a980e046SJonathan Cameron static LIST_HEAD(iio_map_list); 26a980e046SJonathan Cameron static DEFINE_MUTEX(iio_map_list_lock); 27a980e046SJonathan Cameron 28cc9fb60eSLino Sanfilippo static int iio_map_array_unregister_locked(struct iio_dev *indio_dev) 29cc9fb60eSLino Sanfilippo { 30cc9fb60eSLino Sanfilippo int ret = -ENODEV; 31cc9fb60eSLino Sanfilippo struct iio_map_internal *mapi, *next; 32cc9fb60eSLino Sanfilippo 33cc9fb60eSLino Sanfilippo list_for_each_entry_safe(mapi, next, &iio_map_list, l) { 34cc9fb60eSLino Sanfilippo if (indio_dev == mapi->indio_dev) { 35cc9fb60eSLino Sanfilippo list_del(&mapi->l); 36cc9fb60eSLino Sanfilippo kfree(mapi); 37cc9fb60eSLino Sanfilippo ret = 0; 38cc9fb60eSLino Sanfilippo } 39cc9fb60eSLino Sanfilippo } 40cc9fb60eSLino Sanfilippo return ret; 41cc9fb60eSLino Sanfilippo } 42cc9fb60eSLino Sanfilippo 43a980e046SJonathan Cameron int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps) 44a980e046SJonathan Cameron { 45a980e046SJonathan Cameron int i = 0, ret = 0; 46a980e046SJonathan Cameron struct iio_map_internal *mapi; 47a980e046SJonathan Cameron 48a980e046SJonathan Cameron if (maps == NULL) 49a980e046SJonathan Cameron return 0; 50a980e046SJonathan Cameron 51a980e046SJonathan Cameron mutex_lock(&iio_map_list_lock); 52a980e046SJonathan Cameron while (maps[i].consumer_dev_name != NULL) { 53a980e046SJonathan Cameron mapi = kzalloc(sizeof(*mapi), GFP_KERNEL); 54a980e046SJonathan Cameron if (mapi == NULL) { 55a980e046SJonathan Cameron ret = -ENOMEM; 56a980e046SJonathan Cameron goto error_ret; 57a980e046SJonathan Cameron } 58a980e046SJonathan Cameron mapi->map = &maps[i]; 59a980e046SJonathan Cameron mapi->indio_dev = indio_dev; 60bc4b2a51SGaurav Gupta list_add_tail(&mapi->l, &iio_map_list); 61a980e046SJonathan Cameron i++; 62a980e046SJonathan Cameron } 63a980e046SJonathan Cameron error_ret: 6434fce6caSLino Sanfilippo if (ret) 6534fce6caSLino Sanfilippo iio_map_array_unregister_locked(indio_dev); 66a980e046SJonathan Cameron mutex_unlock(&iio_map_list_lock); 67a980e046SJonathan Cameron 68a980e046SJonathan Cameron return ret; 69a980e046SJonathan Cameron } 70a980e046SJonathan Cameron EXPORT_SYMBOL_GPL(iio_map_array_register); 71a980e046SJonathan Cameron 72a980e046SJonathan Cameron 736cb2afd7SGuenter Roeck /* 746cb2afd7SGuenter Roeck * Remove all map entries associated with the given iio device 75a980e046SJonathan Cameron */ 766cb2afd7SGuenter Roeck int iio_map_array_unregister(struct iio_dev *indio_dev) 77a980e046SJonathan Cameron { 78cc9fb60eSLino Sanfilippo int ret; 79a980e046SJonathan Cameron 80a980e046SJonathan Cameron mutex_lock(&iio_map_list_lock); 81cc9fb60eSLino Sanfilippo ret = iio_map_array_unregister_locked(indio_dev); 82a980e046SJonathan Cameron mutex_unlock(&iio_map_list_lock); 83cc9fb60eSLino Sanfilippo 84a980e046SJonathan Cameron return ret; 85a980e046SJonathan Cameron } 86a980e046SJonathan Cameron EXPORT_SYMBOL_GPL(iio_map_array_unregister); 87a980e046SJonathan Cameron 8825c02edfSAlexandru Ardelean static void iio_map_array_unregister_cb(void *indio_dev) 8925c02edfSAlexandru Ardelean { 9025c02edfSAlexandru Ardelean iio_map_array_unregister(indio_dev); 9125c02edfSAlexandru Ardelean } 9225c02edfSAlexandru Ardelean 9325c02edfSAlexandru Ardelean int devm_iio_map_array_register(struct device *dev, struct iio_dev *indio_dev, struct iio_map *maps) 9425c02edfSAlexandru Ardelean { 9525c02edfSAlexandru Ardelean int ret; 9625c02edfSAlexandru Ardelean 9725c02edfSAlexandru Ardelean ret = iio_map_array_register(indio_dev, maps); 9825c02edfSAlexandru Ardelean if (ret) 9925c02edfSAlexandru Ardelean return ret; 10025c02edfSAlexandru Ardelean 10125c02edfSAlexandru Ardelean return devm_add_action_or_reset(dev, iio_map_array_unregister_cb, indio_dev); 10225c02edfSAlexandru Ardelean } 10325c02edfSAlexandru Ardelean EXPORT_SYMBOL_GPL(devm_iio_map_array_register); 10425c02edfSAlexandru Ardelean 105a980e046SJonathan Cameron static const struct iio_chan_spec 106314be14bSJonathan Cameron *iio_chan_spec_from_name(const struct iio_dev *indio_dev, const char *name) 107a980e046SJonathan Cameron { 108a980e046SJonathan Cameron int i; 109a980e046SJonathan Cameron const struct iio_chan_spec *chan = NULL; 110a980e046SJonathan Cameron 111a980e046SJonathan Cameron for (i = 0; i < indio_dev->num_channels; i++) 112a980e046SJonathan Cameron if (indio_dev->channels[i].datasheet_name && 113a980e046SJonathan Cameron strcmp(name, indio_dev->channels[i].datasheet_name) == 0) { 114a980e046SJonathan Cameron chan = &indio_dev->channels[i]; 115a980e046SJonathan Cameron break; 116a980e046SJonathan Cameron } 117a980e046SJonathan Cameron return chan; 118a980e046SJonathan Cameron } 119a980e046SJonathan Cameron 120acd82567SIvan T. Ivanov /** 1211e64b9c5SNuno Sá * __fwnode_iio_simple_xlate - translate iiospec to the IIO channel index 122acd82567SIvan T. Ivanov * @indio_dev: pointer to the iio_dev structure 123acd82567SIvan T. Ivanov * @iiospec: IIO specifier as found in the device tree 124acd82567SIvan T. Ivanov * 125acd82567SIvan T. Ivanov * This is simple translation function, suitable for the most 1:1 mapped 126acd82567SIvan T. Ivanov * channels in IIO chips. This function performs only one sanity check: 127acd82567SIvan T. Ivanov * whether IIO index is less than num_channels (that is specified in the 128acd82567SIvan T. Ivanov * iio_dev). 129acd82567SIvan T. Ivanov */ 1301e64b9c5SNuno Sá static int __fwnode_iio_simple_xlate(struct iio_dev *indio_dev, 1311e64b9c5SNuno Sá const struct fwnode_reference_args *iiospec) 132acd82567SIvan T. Ivanov { 1331e64b9c5SNuno Sá if (!iiospec->nargs) 134acd82567SIvan T. Ivanov return 0; 135acd82567SIvan T. Ivanov 1361f202725SStefan Wahren if (iiospec->args[0] >= indio_dev->num_channels) { 1371e64b9c5SNuno Sá dev_err(&indio_dev->dev, "invalid channel index %llu\n", 1381f202725SStefan Wahren iiospec->args[0]); 139acd82567SIvan T. Ivanov return -EINVAL; 1401f202725SStefan Wahren } 141acd82567SIvan T. Ivanov 142acd82567SIvan T. Ivanov return iiospec->args[0]; 143acd82567SIvan T. Ivanov } 144acd82567SIvan T. Ivanov 1451e64b9c5SNuno Sá static int __fwnode_iio_channel_get(struct iio_channel *channel, 1461e64b9c5SNuno Sá struct fwnode_handle *fwnode, int index) 1471e64b9c5SNuno Sá { 1481e64b9c5SNuno Sá struct fwnode_reference_args iiospec; 14917d82b47SGuenter Roeck struct device *idev; 15017d82b47SGuenter Roeck struct iio_dev *indio_dev; 15117d82b47SGuenter Roeck int err; 15217d82b47SGuenter Roeck 1531e64b9c5SNuno Sá err = fwnode_property_get_reference_args(fwnode, "io-channels", 1541e64b9c5SNuno Sá "#io-channel-cells", 0, 15517d82b47SGuenter Roeck index, &iiospec); 15617d82b47SGuenter Roeck if (err) 15717d82b47SGuenter Roeck return err; 15817d82b47SGuenter Roeck 1591e64b9c5SNuno Sá idev = bus_find_device_by_fwnode(&iio_bus_type, iiospec.fwnode); 16079c3e848SNuno Sá if (idev == NULL) { 1611e64b9c5SNuno Sá fwnode_handle_put(iiospec.fwnode); 16217d82b47SGuenter Roeck return -EPROBE_DEFER; 16379c3e848SNuno Sá } 16417d82b47SGuenter Roeck 16517d82b47SGuenter Roeck indio_dev = dev_to_iio_dev(idev); 16617d82b47SGuenter Roeck channel->indio_dev = indio_dev; 167*b22bc4d6SNuno Sá if (indio_dev->info->fwnode_xlate) 1681e64b9c5SNuno Sá index = indio_dev->info->fwnode_xlate(indio_dev, &iiospec); 169acd82567SIvan T. Ivanov else 1701e64b9c5SNuno Sá index = __fwnode_iio_simple_xlate(indio_dev, &iiospec); 1711e64b9c5SNuno Sá fwnode_handle_put(iiospec.fwnode); 172acd82567SIvan T. Ivanov if (index < 0) 17317d82b47SGuenter Roeck goto err_put; 17417d82b47SGuenter Roeck channel->channel = &indio_dev->channels[index]; 17517d82b47SGuenter Roeck 17617d82b47SGuenter Roeck return 0; 17717d82b47SGuenter Roeck 17817d82b47SGuenter Roeck err_put: 17917d82b47SGuenter Roeck iio_device_put(indio_dev); 180acd82567SIvan T. Ivanov return index; 18117d82b47SGuenter Roeck } 18217d82b47SGuenter Roeck 1831e64b9c5SNuno Sá static struct iio_channel *fwnode_iio_channel_get(struct fwnode_handle *fwnode, 1841e64b9c5SNuno Sá int index) 18517d82b47SGuenter Roeck { 18617d82b47SGuenter Roeck struct iio_channel *channel; 18717d82b47SGuenter Roeck int err; 18817d82b47SGuenter Roeck 18917d82b47SGuenter Roeck if (index < 0) 19017d82b47SGuenter Roeck return ERR_PTR(-EINVAL); 19117d82b47SGuenter Roeck 19217d82b47SGuenter Roeck channel = kzalloc(sizeof(*channel), GFP_KERNEL); 19317d82b47SGuenter Roeck if (channel == NULL) 19417d82b47SGuenter Roeck return ERR_PTR(-ENOMEM); 19517d82b47SGuenter Roeck 1961e64b9c5SNuno Sá err = __fwnode_iio_channel_get(channel, fwnode, index); 19717d82b47SGuenter Roeck if (err) 19817d82b47SGuenter Roeck goto err_free_channel; 19917d82b47SGuenter Roeck 20017d82b47SGuenter Roeck return channel; 20117d82b47SGuenter Roeck 20217d82b47SGuenter Roeck err_free_channel: 20317d82b47SGuenter Roeck kfree(channel); 20417d82b47SGuenter Roeck return ERR_PTR(err); 20517d82b47SGuenter Roeck } 20617d82b47SGuenter Roeck 2071e64b9c5SNuno Sá static struct iio_channel * 2081e64b9c5SNuno Sá __fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, const char *name) 20917d82b47SGuenter Roeck { 210ed5e5ed4SNuno Sá struct iio_channel *chan; 21117d82b47SGuenter Roeck int index = 0; 21217d82b47SGuenter Roeck 21317d82b47SGuenter Roeck /* 21417d82b47SGuenter Roeck * For named iio channels, first look up the name in the 21517d82b47SGuenter Roeck * "io-channel-names" property. If it cannot be found, the 2161e64b9c5SNuno Sá * index will be an error code, and fwnode_iio_channel_get() 21717d82b47SGuenter Roeck * will fail. 21817d82b47SGuenter Roeck */ 21917d82b47SGuenter Roeck if (name) 2201e64b9c5SNuno Sá index = fwnode_property_match_string(fwnode, "io-channel-names", 2211e64b9c5SNuno Sá name); 222d6bb09eaSNuno Sá 2231e64b9c5SNuno Sá chan = fwnode_iio_channel_get(fwnode, index); 224872687f6SJohannes Pointner if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER) 225ed5e5ed4SNuno Sá return chan; 226ed5e5ed4SNuno Sá if (name) { 227ed5e5ed4SNuno Sá if (index >= 0) { 2281e64b9c5SNuno Sá pr_err("ERROR: could not get IIO channel %pfw:%s(%i)\n", 2291e64b9c5SNuno Sá fwnode, name, index); 230ed5e5ed4SNuno Sá /* 231ed5e5ed4SNuno Sá * In this case, we found 'name' in 'io-channel-names' 232ed5e5ed4SNuno Sá * but somehow we still fail so that we should not proceed 233ed5e5ed4SNuno Sá * with any other lookup. Hence, explicitly return -EINVAL 234ed5e5ed4SNuno Sá * (maybe not the better error code) so that the caller 235ed5e5ed4SNuno Sá * won't do a system lookup. 236ed5e5ed4SNuno Sá */ 237ed5e5ed4SNuno Sá return ERR_PTR(-EINVAL); 238ed5e5ed4SNuno Sá } 239ed5e5ed4SNuno Sá /* 2401e64b9c5SNuno Sá * If index < 0, then fwnode_property_get_reference_args() fails 2411e64b9c5SNuno Sá * with -EINVAL or -ENOENT (ACPI case) which is expected. We 2421e64b9c5SNuno Sá * should not proceed if we get any other error. 243ed5e5ed4SNuno Sá */ 2441e64b9c5SNuno Sá if (PTR_ERR(chan) != -EINVAL && PTR_ERR(chan) != -ENOENT) 245ed5e5ed4SNuno Sá return chan; 246ed5e5ed4SNuno Sá } else if (PTR_ERR(chan) != -ENOENT) { 247ed5e5ed4SNuno Sá /* 248ed5e5ed4SNuno Sá * if !name, then we should only proceed the lookup if 2491e64b9c5SNuno Sá * fwnode_property_get_reference_args() returns -ENOENT. 250ed5e5ed4SNuno Sá */ 251ed5e5ed4SNuno Sá return chan; 25217d82b47SGuenter Roeck } 25317d82b47SGuenter Roeck 254d6bb09eaSNuno Sá /* so we continue the lookup */ 255d6bb09eaSNuno Sá return ERR_PTR(-ENODEV); 256d6bb09eaSNuno Sá } 257d6bb09eaSNuno Sá 2581e64b9c5SNuno Sá struct iio_channel *fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, 259d6bb09eaSNuno Sá const char *name) 260d6bb09eaSNuno Sá { 2611e64b9c5SNuno Sá struct fwnode_handle *parent; 262d6bb09eaSNuno Sá struct iio_channel *chan; 263d6bb09eaSNuno Sá 264d6bb09eaSNuno Sá /* Walk up the tree of devices looking for a matching iio channel */ 2651e64b9c5SNuno Sá chan = __fwnode_iio_channel_get_by_name(fwnode, name); 266d6bb09eaSNuno Sá if (!IS_ERR(chan) || PTR_ERR(chan) != -ENODEV) 267d6bb09eaSNuno Sá return chan; 268d6bb09eaSNuno Sá 26917d82b47SGuenter Roeck /* 27017d82b47SGuenter Roeck * No matching IIO channel found on this node. 27117d82b47SGuenter Roeck * If the parent node has a "io-channel-ranges" property, 27217d82b47SGuenter Roeck * then we can try one of its channels. 27317d82b47SGuenter Roeck */ 2741e64b9c5SNuno Sá fwnode_for_each_parent_node(fwnode, parent) { 2751e64b9c5SNuno Sá if (!fwnode_property_present(parent, "io-channel-ranges")) { 2761e64b9c5SNuno Sá fwnode_handle_put(parent); 277ed5e5ed4SNuno Sá return ERR_PTR(-ENODEV); 2781e64b9c5SNuno Sá } 279d6bb09eaSNuno Sá 2801e64b9c5SNuno Sá chan = __fwnode_iio_channel_get_by_name(fwnode, name); 2811e64b9c5SNuno Sá if (!IS_ERR(chan) || PTR_ERR(chan) != -ENODEV) { 2821e64b9c5SNuno Sá fwnode_handle_put(parent); 283d6bb09eaSNuno Sá return chan; 2841e64b9c5SNuno Sá } 28517d82b47SGuenter Roeck } 286a2c12493SAdam Thomson 287ed5e5ed4SNuno Sá return ERR_PTR(-ENODEV); 28817d82b47SGuenter Roeck } 2891e64b9c5SNuno Sá EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name); 29017d82b47SGuenter Roeck 2911e64b9c5SNuno Sá static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev) 29217d82b47SGuenter Roeck { 2931e64b9c5SNuno Sá struct fwnode_handle *fwnode = dev_fwnode(dev); 29417d82b47SGuenter Roeck struct iio_channel *chans; 29517d82b47SGuenter Roeck int i, mapind, nummaps = 0; 29617d82b47SGuenter Roeck int ret; 29717d82b47SGuenter Roeck 29817d82b47SGuenter Roeck do { 2991e64b9c5SNuno Sá ret = fwnode_property_get_reference_args(fwnode, "io-channels", 3001e64b9c5SNuno Sá "#io-channel-cells", 0, 30117d82b47SGuenter Roeck nummaps, NULL); 30217d82b47SGuenter Roeck if (ret < 0) 30317d82b47SGuenter Roeck break; 30417d82b47SGuenter Roeck } while (++nummaps); 30517d82b47SGuenter Roeck 306ed5e5ed4SNuno Sá if (nummaps == 0) 307ed5e5ed4SNuno Sá return ERR_PTR(-ENODEV); 30817d82b47SGuenter Roeck 30917d82b47SGuenter Roeck /* NULL terminated array to save passing size */ 31017d82b47SGuenter Roeck chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); 31117d82b47SGuenter Roeck if (chans == NULL) 31217d82b47SGuenter Roeck return ERR_PTR(-ENOMEM); 31317d82b47SGuenter Roeck 3141e64b9c5SNuno Sá /* Search for FW matches */ 31517d82b47SGuenter Roeck for (mapind = 0; mapind < nummaps; mapind++) { 3161e64b9c5SNuno Sá ret = __fwnode_iio_channel_get(&chans[mapind], fwnode, mapind); 31717d82b47SGuenter Roeck if (ret) 31817d82b47SGuenter Roeck goto error_free_chans; 31917d82b47SGuenter Roeck } 32017d82b47SGuenter Roeck return chans; 32117d82b47SGuenter Roeck 32217d82b47SGuenter Roeck error_free_chans: 32317d82b47SGuenter Roeck for (i = 0; i < mapind; i++) 32417d82b47SGuenter Roeck iio_device_put(chans[i].indio_dev); 32517d82b47SGuenter Roeck kfree(chans); 32617d82b47SGuenter Roeck return ERR_PTR(ret); 32717d82b47SGuenter Roeck } 32817d82b47SGuenter Roeck 3295aa57f0aSGuenter Roeck static struct iio_channel *iio_channel_get_sys(const char *name, 3305aa57f0aSGuenter Roeck const char *channel_name) 331a980e046SJonathan Cameron { 332a980e046SJonathan Cameron struct iio_map_internal *c_i = NULL, *c = NULL; 333a980e046SJonathan Cameron struct iio_channel *channel; 3343183bac1SKim, Milo int err; 335a980e046SJonathan Cameron 336a980e046SJonathan Cameron if (name == NULL && channel_name == NULL) 337a980e046SJonathan Cameron return ERR_PTR(-ENODEV); 338a980e046SJonathan Cameron 339a980e046SJonathan Cameron /* first find matching entry the channel map */ 340a980e046SJonathan Cameron mutex_lock(&iio_map_list_lock); 341a980e046SJonathan Cameron list_for_each_entry(c_i, &iio_map_list, l) { 342a980e046SJonathan Cameron if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) || 343a980e046SJonathan Cameron (channel_name && 344a980e046SJonathan Cameron strcmp(channel_name, c_i->map->consumer_channel) != 0)) 345a980e046SJonathan Cameron continue; 346a980e046SJonathan Cameron c = c_i; 3471875ffd2SLars-Peter Clausen iio_device_get(c->indio_dev); 348a980e046SJonathan Cameron break; 349a980e046SJonathan Cameron } 350a980e046SJonathan Cameron mutex_unlock(&iio_map_list_lock); 351a980e046SJonathan Cameron if (c == NULL) 352a980e046SJonathan Cameron return ERR_PTR(-ENODEV); 353a980e046SJonathan Cameron 3542cc412b5SKim, Milo channel = kzalloc(sizeof(*channel), GFP_KERNEL); 3553183bac1SKim, Milo if (channel == NULL) { 3563183bac1SKim, Milo err = -ENOMEM; 357801c4b5cSKim, Milo goto error_no_mem; 3583183bac1SKim, Milo } 359a980e046SJonathan Cameron 360a980e046SJonathan Cameron channel->indio_dev = c->indio_dev; 361a980e046SJonathan Cameron 362b2b79ffaSKim, Milo if (c->map->adc_channel_label) { 363a980e046SJonathan Cameron channel->channel = 364a980e046SJonathan Cameron iio_chan_spec_from_name(channel->indio_dev, 365a980e046SJonathan Cameron c->map->adc_channel_label); 366a980e046SJonathan Cameron 3673183bac1SKim, Milo if (channel->channel == NULL) { 3683183bac1SKim, Milo err = -EINVAL; 369b2b79ffaSKim, Milo goto error_no_chan; 370b2b79ffaSKim, Milo } 3713183bac1SKim, Milo } 372b2b79ffaSKim, Milo 373a980e046SJonathan Cameron return channel; 374b2b79ffaSKim, Milo 375b2b79ffaSKim, Milo error_no_chan: 376b2b79ffaSKim, Milo kfree(channel); 377801c4b5cSKim, Milo error_no_mem: 378801c4b5cSKim, Milo iio_device_put(c->indio_dev); 3793183bac1SKim, Milo return ERR_PTR(err); 380a980e046SJonathan Cameron } 3815aa57f0aSGuenter Roeck 3825aa57f0aSGuenter Roeck struct iio_channel *iio_channel_get(struct device *dev, 3835aa57f0aSGuenter Roeck const char *channel_name) 3845aa57f0aSGuenter Roeck { 3855aa57f0aSGuenter Roeck const char *name = dev ? dev_name(dev) : NULL; 38617d82b47SGuenter Roeck struct iio_channel *channel; 3875aa57f0aSGuenter Roeck 38817d82b47SGuenter Roeck if (dev) { 3891e64b9c5SNuno Sá channel = fwnode_iio_channel_get_by_name(dev_fwnode(dev), 39017d82b47SGuenter Roeck channel_name); 391ed5e5ed4SNuno Sá if (!IS_ERR(channel) || PTR_ERR(channel) != -ENODEV) 39217d82b47SGuenter Roeck return channel; 39317d82b47SGuenter Roeck } 394a2c12493SAdam Thomson 3955aa57f0aSGuenter Roeck return iio_channel_get_sys(name, channel_name); 3965aa57f0aSGuenter Roeck } 397314be14bSJonathan Cameron EXPORT_SYMBOL_GPL(iio_channel_get); 398a980e046SJonathan Cameron 399314be14bSJonathan Cameron void iio_channel_release(struct iio_channel *channel) 400a980e046SJonathan Cameron { 401d81dac3cSDan Carpenter if (!channel) 402d81dac3cSDan Carpenter return; 4031875ffd2SLars-Peter Clausen iio_device_put(channel->indio_dev); 404a980e046SJonathan Cameron kfree(channel); 405a980e046SJonathan Cameron } 406314be14bSJonathan Cameron EXPORT_SYMBOL_GPL(iio_channel_release); 407a980e046SJonathan Cameron 4087349e8a3SYicong Yang static void devm_iio_channel_free(void *iio_channel) 4098bf872d8SLaxman Dewangan { 4107349e8a3SYicong Yang iio_channel_release(iio_channel); 4118bf872d8SLaxman Dewangan } 4128bf872d8SLaxman Dewangan 4138bf872d8SLaxman Dewangan struct iio_channel *devm_iio_channel_get(struct device *dev, 4148bf872d8SLaxman Dewangan const char *channel_name) 4158bf872d8SLaxman Dewangan { 4167349e8a3SYicong Yang struct iio_channel *channel; 4177349e8a3SYicong Yang int ret; 4188bf872d8SLaxman Dewangan 4198bf872d8SLaxman Dewangan channel = iio_channel_get(dev, channel_name); 4207349e8a3SYicong Yang if (IS_ERR(channel)) 4218bf872d8SLaxman Dewangan return channel; 4228bf872d8SLaxman Dewangan 4237349e8a3SYicong Yang ret = devm_add_action_or_reset(dev, devm_iio_channel_free, channel); 4247349e8a3SYicong Yang if (ret) 4257349e8a3SYicong Yang return ERR_PTR(ret); 4268bf872d8SLaxman Dewangan 4278bf872d8SLaxman Dewangan return channel; 4288bf872d8SLaxman Dewangan } 4298bf872d8SLaxman Dewangan EXPORT_SYMBOL_GPL(devm_iio_channel_get); 4308bf872d8SLaxman Dewangan 4311e64b9c5SNuno Sá struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev, 4321e64b9c5SNuno Sá struct fwnode_handle *fwnode, 4336e39b145SDmitry Baryshkov const char *channel_name) 4346e39b145SDmitry Baryshkov { 4357349e8a3SYicong Yang struct iio_channel *channel; 4367349e8a3SYicong Yang int ret; 4376e39b145SDmitry Baryshkov 4381e64b9c5SNuno Sá channel = fwnode_iio_channel_get_by_name(fwnode, channel_name); 4397349e8a3SYicong Yang if (IS_ERR(channel)) 4406e39b145SDmitry Baryshkov return channel; 4416e39b145SDmitry Baryshkov 4427349e8a3SYicong Yang ret = devm_add_action_or_reset(dev, devm_iio_channel_free, channel); 4437349e8a3SYicong Yang if (ret) 4447349e8a3SYicong Yang return ERR_PTR(ret); 4456e39b145SDmitry Baryshkov 4466e39b145SDmitry Baryshkov return channel; 4476e39b145SDmitry Baryshkov } 4481e64b9c5SNuno Sá EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name); 4496e39b145SDmitry Baryshkov 450ca7d98dbSGuenter Roeck struct iio_channel *iio_channel_get_all(struct device *dev) 451a980e046SJonathan Cameron { 452ca7d98dbSGuenter Roeck const char *name; 453a980e046SJonathan Cameron struct iio_channel *chans; 454a980e046SJonathan Cameron struct iio_map_internal *c = NULL; 455a980e046SJonathan Cameron int nummaps = 0; 456a980e046SJonathan Cameron int mapind = 0; 457a980e046SJonathan Cameron int i, ret; 458a980e046SJonathan Cameron 459ca7d98dbSGuenter Roeck if (dev == NULL) 460a980e046SJonathan Cameron return ERR_PTR(-EINVAL); 46117d82b47SGuenter Roeck 4621e64b9c5SNuno Sá chans = fwnode_iio_channel_get_all(dev); 463ed5e5ed4SNuno Sá /* 464ed5e5ed4SNuno Sá * We only want to carry on if the error is -ENODEV. Anything else 465ed5e5ed4SNuno Sá * should be reported up the stack. 466ed5e5ed4SNuno Sá */ 467ed5e5ed4SNuno Sá if (!IS_ERR(chans) || PTR_ERR(chans) != -ENODEV) 46817d82b47SGuenter Roeck return chans; 46917d82b47SGuenter Roeck 470ca7d98dbSGuenter Roeck name = dev_name(dev); 471a980e046SJonathan Cameron 472a980e046SJonathan Cameron mutex_lock(&iio_map_list_lock); 473a980e046SJonathan Cameron /* first count the matching maps */ 474a980e046SJonathan Cameron list_for_each_entry(c, &iio_map_list, l) 475a980e046SJonathan Cameron if (name && strcmp(name, c->map->consumer_dev_name) != 0) 476a980e046SJonathan Cameron continue; 477a980e046SJonathan Cameron else 478a980e046SJonathan Cameron nummaps++; 479a980e046SJonathan Cameron 480a980e046SJonathan Cameron if (nummaps == 0) { 481a980e046SJonathan Cameron ret = -ENODEV; 482a980e046SJonathan Cameron goto error_ret; 483a980e046SJonathan Cameron } 484a980e046SJonathan Cameron 485a980e046SJonathan Cameron /* NULL terminated array to save passing size */ 4866396bb22SKees Cook chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); 487a980e046SJonathan Cameron if (chans == NULL) { 488a980e046SJonathan Cameron ret = -ENOMEM; 489a980e046SJonathan Cameron goto error_ret; 490a980e046SJonathan Cameron } 491a980e046SJonathan Cameron 492a980e046SJonathan Cameron /* for each map fill in the chans element */ 493a980e046SJonathan Cameron list_for_each_entry(c, &iio_map_list, l) { 494a980e046SJonathan Cameron if (name && strcmp(name, c->map->consumer_dev_name) != 0) 495a980e046SJonathan Cameron continue; 496a980e046SJonathan Cameron chans[mapind].indio_dev = c->indio_dev; 4970464415dSJonathan Cameron chans[mapind].data = c->map->consumer_data; 498a980e046SJonathan Cameron chans[mapind].channel = 499a980e046SJonathan Cameron iio_chan_spec_from_name(chans[mapind].indio_dev, 500a980e046SJonathan Cameron c->map->adc_channel_label); 501a980e046SJonathan Cameron if (chans[mapind].channel == NULL) { 502a980e046SJonathan Cameron ret = -EINVAL; 503a980e046SJonathan Cameron goto error_free_chans; 504a980e046SJonathan Cameron } 5051875ffd2SLars-Peter Clausen iio_device_get(chans[mapind].indio_dev); 506a980e046SJonathan Cameron mapind++; 507a980e046SJonathan Cameron } 508a980e046SJonathan Cameron if (mapind == 0) { 509a980e046SJonathan Cameron ret = -ENODEV; 510a980e046SJonathan Cameron goto error_free_chans; 511a980e046SJonathan Cameron } 512e59b9afeSDan Carpenter mutex_unlock(&iio_map_list_lock); 513e59b9afeSDan Carpenter 514a980e046SJonathan Cameron return chans; 515a980e046SJonathan Cameron 516a980e046SJonathan Cameron error_free_chans: 517a980e046SJonathan Cameron for (i = 0; i < nummaps; i++) 5181875ffd2SLars-Peter Clausen iio_device_put(chans[i].indio_dev); 519a980e046SJonathan Cameron kfree(chans); 520a980e046SJonathan Cameron error_ret: 521a980e046SJonathan Cameron mutex_unlock(&iio_map_list_lock); 522a980e046SJonathan Cameron 523a980e046SJonathan Cameron return ERR_PTR(ret); 524a980e046SJonathan Cameron } 525314be14bSJonathan Cameron EXPORT_SYMBOL_GPL(iio_channel_get_all); 526a980e046SJonathan Cameron 527314be14bSJonathan Cameron void iio_channel_release_all(struct iio_channel *channels) 528a980e046SJonathan Cameron { 529a980e046SJonathan Cameron struct iio_channel *chan = &channels[0]; 530a980e046SJonathan Cameron 531a980e046SJonathan Cameron while (chan->indio_dev) { 5321875ffd2SLars-Peter Clausen iio_device_put(chan->indio_dev); 533a980e046SJonathan Cameron chan++; 534a980e046SJonathan Cameron } 535a980e046SJonathan Cameron kfree(channels); 536a980e046SJonathan Cameron } 537314be14bSJonathan Cameron EXPORT_SYMBOL_GPL(iio_channel_release_all); 538a980e046SJonathan Cameron 5397349e8a3SYicong Yang static void devm_iio_channel_free_all(void *iio_channels) 540efc2c013SLaxman Dewangan { 5417349e8a3SYicong Yang iio_channel_release_all(iio_channels); 542efc2c013SLaxman Dewangan } 543efc2c013SLaxman Dewangan 544efc2c013SLaxman Dewangan struct iio_channel *devm_iio_channel_get_all(struct device *dev) 545efc2c013SLaxman Dewangan { 5467349e8a3SYicong Yang struct iio_channel *channels; 5477349e8a3SYicong Yang int ret; 548efc2c013SLaxman Dewangan 549efc2c013SLaxman Dewangan channels = iio_channel_get_all(dev); 5507349e8a3SYicong Yang if (IS_ERR(channels)) 551efc2c013SLaxman Dewangan return channels; 552efc2c013SLaxman Dewangan 5537349e8a3SYicong Yang ret = devm_add_action_or_reset(dev, devm_iio_channel_free_all, 5547349e8a3SYicong Yang channels); 5557349e8a3SYicong Yang if (ret) 5567349e8a3SYicong Yang return ERR_PTR(ret); 557efc2c013SLaxman Dewangan 558efc2c013SLaxman Dewangan return channels; 559efc2c013SLaxman Dewangan } 560efc2c013SLaxman Dewangan EXPORT_SYMBOL_GPL(devm_iio_channel_get_all); 561efc2c013SLaxman Dewangan 56248e44ce0SLars-Peter Clausen static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, 56348e44ce0SLars-Peter Clausen enum iio_chan_info_enum info) 56448e44ce0SLars-Peter Clausen { 56548e44ce0SLars-Peter Clausen int unused; 5669fbfb4b3SSrinivas Pandruvada int vals[INDIO_MAX_RAW_ELEMENTS]; 5679fbfb4b3SSrinivas Pandruvada int ret; 5689fbfb4b3SSrinivas Pandruvada int val_len = 2; 56948e44ce0SLars-Peter Clausen 57048e44ce0SLars-Peter Clausen if (val2 == NULL) 57148e44ce0SLars-Peter Clausen val2 = &unused; 57248e44ce0SLars-Peter Clausen 57365de7654SFabien Proriol if (!iio_channel_has_info(chan->channel, info)) 57465de7654SFabien Proriol return -EINVAL; 57565de7654SFabien Proriol 5769fbfb4b3SSrinivas Pandruvada if (chan->indio_dev->info->read_raw_multi) { 5779fbfb4b3SSrinivas Pandruvada ret = chan->indio_dev->info->read_raw_multi(chan->indio_dev, 5789fbfb4b3SSrinivas Pandruvada chan->channel, INDIO_MAX_RAW_ELEMENTS, 5799fbfb4b3SSrinivas Pandruvada vals, &val_len, info); 5809fbfb4b3SSrinivas Pandruvada *val = vals[0]; 5819fbfb4b3SSrinivas Pandruvada *val2 = vals[1]; 5829fbfb4b3SSrinivas Pandruvada } else 5839fbfb4b3SSrinivas Pandruvada ret = chan->indio_dev->info->read_raw(chan->indio_dev, 5849fbfb4b3SSrinivas Pandruvada chan->channel, val, val2, info); 5859fbfb4b3SSrinivas Pandruvada 5869fbfb4b3SSrinivas Pandruvada return ret; 58748e44ce0SLars-Peter Clausen } 58848e44ce0SLars-Peter Clausen 589314be14bSJonathan Cameron int iio_read_channel_raw(struct iio_channel *chan, int *val) 590a980e046SJonathan Cameron { 591b804e2b7SJonathan Cameron struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); 59248e44ce0SLars-Peter Clausen int ret; 593a980e046SJonathan Cameron 594b804e2b7SJonathan Cameron mutex_lock(&iio_dev_opaque->info_exist_lock); 595a980e046SJonathan Cameron if (chan->indio_dev->info == NULL) { 596a980e046SJonathan Cameron ret = -ENODEV; 597a980e046SJonathan Cameron goto err_unlock; 598a980e046SJonathan Cameron } 599a980e046SJonathan Cameron 60048e44ce0SLars-Peter Clausen ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); 601a980e046SJonathan Cameron err_unlock: 602b804e2b7SJonathan Cameron mutex_unlock(&iio_dev_opaque->info_exist_lock); 603a980e046SJonathan Cameron 604a980e046SJonathan Cameron return ret; 605a980e046SJonathan Cameron } 606314be14bSJonathan Cameron EXPORT_SYMBOL_GPL(iio_read_channel_raw); 607a980e046SJonathan Cameron 608476d4af2SSebastian Reichel int iio_read_channel_average_raw(struct iio_channel *chan, int *val) 609476d4af2SSebastian Reichel { 610b804e2b7SJonathan Cameron struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); 611476d4af2SSebastian Reichel int ret; 612476d4af2SSebastian Reichel 613b804e2b7SJonathan Cameron mutex_lock(&iio_dev_opaque->info_exist_lock); 614476d4af2SSebastian Reichel if (chan->indio_dev->info == NULL) { 615476d4af2SSebastian Reichel ret = -ENODEV; 616476d4af2SSebastian Reichel goto err_unlock; 617476d4af2SSebastian Reichel } 618476d4af2SSebastian Reichel 619476d4af2SSebastian Reichel ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); 620476d4af2SSebastian Reichel err_unlock: 621b804e2b7SJonathan Cameron mutex_unlock(&iio_dev_opaque->info_exist_lock); 622476d4af2SSebastian Reichel 623476d4af2SSebastian Reichel return ret; 624476d4af2SSebastian Reichel } 625476d4af2SSebastian Reichel EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); 626476d4af2SSebastian Reichel 62748e44ce0SLars-Peter Clausen static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, 62848e44ce0SLars-Peter Clausen int raw, int *processed, unsigned int scale) 62948e44ce0SLars-Peter Clausen { 630ca851233SLiam Beguin int scale_type, scale_val, scale_val2; 631ca851233SLiam Beguin int offset_type, offset_val, offset_val2; 63248e44ce0SLars-Peter Clausen s64 raw64 = raw; 63348e44ce0SLars-Peter Clausen 634ca851233SLiam Beguin offset_type = iio_channel_read(chan, &offset_val, &offset_val2, 635ca851233SLiam Beguin IIO_CHAN_INFO_OFFSET); 636ca851233SLiam Beguin if (offset_type >= 0) { 637ca851233SLiam Beguin switch (offset_type) { 638ca851233SLiam Beguin case IIO_VAL_INT: 639ca851233SLiam Beguin break; 640ca851233SLiam Beguin case IIO_VAL_INT_PLUS_MICRO: 641ca851233SLiam Beguin case IIO_VAL_INT_PLUS_NANO: 642ca851233SLiam Beguin /* 643ca851233SLiam Beguin * Both IIO_VAL_INT_PLUS_MICRO and IIO_VAL_INT_PLUS_NANO 644ca851233SLiam Beguin * implicitely truncate the offset to it's integer form. 645ca851233SLiam Beguin */ 646ca851233SLiam Beguin break; 647ca851233SLiam Beguin case IIO_VAL_FRACTIONAL: 648ca851233SLiam Beguin offset_val /= offset_val2; 649ca851233SLiam Beguin break; 650ca851233SLiam Beguin case IIO_VAL_FRACTIONAL_LOG2: 651ca851233SLiam Beguin offset_val >>= offset_val2; 652ca851233SLiam Beguin break; 653ca851233SLiam Beguin default: 654ca851233SLiam Beguin return -EINVAL; 655ca851233SLiam Beguin } 656ca851233SLiam Beguin 657ca851233SLiam Beguin raw64 += offset_val; 658ca851233SLiam Beguin } 65948e44ce0SLars-Peter Clausen 66048e44ce0SLars-Peter Clausen scale_type = iio_channel_read(chan, &scale_val, &scale_val2, 66148e44ce0SLars-Peter Clausen IIO_CHAN_INFO_SCALE); 662adc8ec5fSLinus Walleij if (scale_type < 0) { 663adc8ec5fSLinus Walleij /* 66414b457fdSLiam Beguin * If no channel scaling is available apply consumer scale to 66514b457fdSLiam Beguin * raw value and return. 666adc8ec5fSLinus Walleij */ 66714b457fdSLiam Beguin *processed = raw * scale; 668adc8ec5fSLinus Walleij return 0; 669adc8ec5fSLinus Walleij } 67048e44ce0SLars-Peter Clausen 67148e44ce0SLars-Peter Clausen switch (scale_type) { 67248e44ce0SLars-Peter Clausen case IIO_VAL_INT: 6731bca97ffSLiam Beguin *processed = raw64 * scale_val * scale; 67448e44ce0SLars-Peter Clausen break; 67548e44ce0SLars-Peter Clausen case IIO_VAL_INT_PLUS_MICRO: 67648e44ce0SLars-Peter Clausen if (scale_val2 < 0) 67748e44ce0SLars-Peter Clausen *processed = -raw64 * scale_val; 67848e44ce0SLars-Peter Clausen else 67948e44ce0SLars-Peter Clausen *processed = raw64 * scale_val; 68048e44ce0SLars-Peter Clausen *processed += div_s64(raw64 * (s64)scale_val2 * scale, 68148e44ce0SLars-Peter Clausen 1000000LL); 68248e44ce0SLars-Peter Clausen break; 68348e44ce0SLars-Peter Clausen case IIO_VAL_INT_PLUS_NANO: 68448e44ce0SLars-Peter Clausen if (scale_val2 < 0) 68548e44ce0SLars-Peter Clausen *processed = -raw64 * scale_val; 68648e44ce0SLars-Peter Clausen else 68748e44ce0SLars-Peter Clausen *processed = raw64 * scale_val; 68848e44ce0SLars-Peter Clausen *processed += div_s64(raw64 * (s64)scale_val2 * scale, 68948e44ce0SLars-Peter Clausen 1000000000LL); 69048e44ce0SLars-Peter Clausen break; 69148e44ce0SLars-Peter Clausen case IIO_VAL_FRACTIONAL: 69248e44ce0SLars-Peter Clausen *processed = div_s64(raw64 * (s64)scale_val * scale, 69348e44ce0SLars-Peter Clausen scale_val2); 69448e44ce0SLars-Peter Clausen break; 695103d9fb9SLars-Peter Clausen case IIO_VAL_FRACTIONAL_LOG2: 696103d9fb9SLars-Peter Clausen *processed = (raw64 * (s64)scale_val * scale) >> scale_val2; 697103d9fb9SLars-Peter Clausen break; 69848e44ce0SLars-Peter Clausen default: 69948e44ce0SLars-Peter Clausen return -EINVAL; 70048e44ce0SLars-Peter Clausen } 70148e44ce0SLars-Peter Clausen 70248e44ce0SLars-Peter Clausen return 0; 70348e44ce0SLars-Peter Clausen } 70448e44ce0SLars-Peter Clausen 70548e44ce0SLars-Peter Clausen int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, 70648e44ce0SLars-Peter Clausen int *processed, unsigned int scale) 70748e44ce0SLars-Peter Clausen { 708b804e2b7SJonathan Cameron struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); 70948e44ce0SLars-Peter Clausen int ret; 71048e44ce0SLars-Peter Clausen 711b804e2b7SJonathan Cameron mutex_lock(&iio_dev_opaque->info_exist_lock); 71248e44ce0SLars-Peter Clausen if (chan->indio_dev->info == NULL) { 71348e44ce0SLars-Peter Clausen ret = -ENODEV; 71448e44ce0SLars-Peter Clausen goto err_unlock; 71548e44ce0SLars-Peter Clausen } 71648e44ce0SLars-Peter Clausen 71748e44ce0SLars-Peter Clausen ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed, 71848e44ce0SLars-Peter Clausen scale); 71948e44ce0SLars-Peter Clausen err_unlock: 720b804e2b7SJonathan Cameron mutex_unlock(&iio_dev_opaque->info_exist_lock); 72148e44ce0SLars-Peter Clausen 72248e44ce0SLars-Peter Clausen return ret; 72348e44ce0SLars-Peter Clausen } 72448e44ce0SLars-Peter Clausen EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed); 72548e44ce0SLars-Peter Clausen 72634739a21SArnaud Pouliquen int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, 7270023e67dSMatt Ranostay enum iio_chan_info_enum attribute) 7280023e67dSMatt Ranostay { 729b804e2b7SJonathan Cameron struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); 7300023e67dSMatt Ranostay int ret; 7310023e67dSMatt Ranostay 732b804e2b7SJonathan Cameron mutex_lock(&iio_dev_opaque->info_exist_lock); 7330023e67dSMatt Ranostay if (chan->indio_dev->info == NULL) { 7340023e67dSMatt Ranostay ret = -ENODEV; 7350023e67dSMatt Ranostay goto err_unlock; 7360023e67dSMatt Ranostay } 7370023e67dSMatt Ranostay 7380023e67dSMatt Ranostay ret = iio_channel_read(chan, val, val2, attribute); 7390023e67dSMatt Ranostay err_unlock: 740b804e2b7SJonathan Cameron mutex_unlock(&iio_dev_opaque->info_exist_lock); 7410023e67dSMatt Ranostay 7420023e67dSMatt Ranostay return ret; 7430023e67dSMatt Ranostay } 74434739a21SArnaud Pouliquen EXPORT_SYMBOL_GPL(iio_read_channel_attribute); 7450023e67dSMatt Ranostay 7460023e67dSMatt Ranostay int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2) 7470023e67dSMatt Ranostay { 7480023e67dSMatt Ranostay return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET); 7490023e67dSMatt Ranostay } 7500023e67dSMatt Ranostay EXPORT_SYMBOL_GPL(iio_read_channel_offset); 7510023e67dSMatt Ranostay 752635ef601SLinus Walleij int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, 753635ef601SLinus Walleij unsigned int scale) 75448e44ce0SLars-Peter Clausen { 755b804e2b7SJonathan Cameron struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); 75648e44ce0SLars-Peter Clausen int ret; 75748e44ce0SLars-Peter Clausen 758b804e2b7SJonathan Cameron mutex_lock(&iio_dev_opaque->info_exist_lock); 75948e44ce0SLars-Peter Clausen if (chan->indio_dev->info == NULL) { 76048e44ce0SLars-Peter Clausen ret = -ENODEV; 76148e44ce0SLars-Peter Clausen goto err_unlock; 76248e44ce0SLars-Peter Clausen } 76348e44ce0SLars-Peter Clausen 76448e44ce0SLars-Peter Clausen if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) { 76548e44ce0SLars-Peter Clausen ret = iio_channel_read(chan, val, NULL, 76648e44ce0SLars-Peter Clausen IIO_CHAN_INFO_PROCESSED); 767b3b64e2cSLinus Walleij if (ret < 0) 768635ef601SLinus Walleij goto err_unlock; 769635ef601SLinus Walleij *val *= scale; 77048e44ce0SLars-Peter Clausen } else { 77148e44ce0SLars-Peter Clausen ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); 77248e44ce0SLars-Peter Clausen if (ret < 0) 77348e44ce0SLars-Peter Clausen goto err_unlock; 774635ef601SLinus Walleij ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, 775635ef601SLinus Walleij scale); 77648e44ce0SLars-Peter Clausen } 77748e44ce0SLars-Peter Clausen 77848e44ce0SLars-Peter Clausen err_unlock: 779b804e2b7SJonathan Cameron mutex_unlock(&iio_dev_opaque->info_exist_lock); 78048e44ce0SLars-Peter Clausen 78148e44ce0SLars-Peter Clausen return ret; 78248e44ce0SLars-Peter Clausen } 783635ef601SLinus Walleij EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale); 784635ef601SLinus Walleij 785635ef601SLinus Walleij int iio_read_channel_processed(struct iio_channel *chan, int *val) 786635ef601SLinus Walleij { 787635ef601SLinus Walleij /* This is just a special case with scale factor 1 */ 788635ef601SLinus Walleij return iio_read_channel_processed_scale(chan, val, 1); 789635ef601SLinus Walleij } 79048e44ce0SLars-Peter Clausen EXPORT_SYMBOL_GPL(iio_read_channel_processed); 79148e44ce0SLars-Peter Clausen 792314be14bSJonathan Cameron int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) 793a980e046SJonathan Cameron { 7940023e67dSMatt Ranostay return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE); 795a980e046SJonathan Cameron } 796314be14bSJonathan Cameron EXPORT_SYMBOL_GPL(iio_read_channel_scale); 797a980e046SJonathan Cameron 79800c5f80cSPeter Rosin static int iio_channel_read_avail(struct iio_channel *chan, 79900c5f80cSPeter Rosin const int **vals, int *type, int *length, 80000c5f80cSPeter Rosin enum iio_chan_info_enum info) 80100c5f80cSPeter Rosin { 80200c5f80cSPeter Rosin if (!iio_channel_has_available(chan->channel, info)) 80300c5f80cSPeter Rosin return -EINVAL; 80400c5f80cSPeter Rosin 80500c5f80cSPeter Rosin return chan->indio_dev->info->read_avail(chan->indio_dev, chan->channel, 80600c5f80cSPeter Rosin vals, type, length, info); 80700c5f80cSPeter Rosin } 80800c5f80cSPeter Rosin 8099f421096SArtur Rojek int iio_read_avail_channel_attribute(struct iio_channel *chan, 8109f421096SArtur Rojek const int **vals, int *type, int *length, 8119f421096SArtur Rojek enum iio_chan_info_enum attribute) 8129f421096SArtur Rojek { 813b804e2b7SJonathan Cameron struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); 8149f421096SArtur Rojek int ret; 8159f421096SArtur Rojek 816b804e2b7SJonathan Cameron mutex_lock(&iio_dev_opaque->info_exist_lock); 8179f421096SArtur Rojek if (!chan->indio_dev->info) { 8189f421096SArtur Rojek ret = -ENODEV; 8199f421096SArtur Rojek goto err_unlock; 8209f421096SArtur Rojek } 8219f421096SArtur Rojek 8229f421096SArtur Rojek ret = iio_channel_read_avail(chan, vals, type, length, attribute); 8239f421096SArtur Rojek err_unlock: 824b804e2b7SJonathan Cameron mutex_unlock(&iio_dev_opaque->info_exist_lock); 8259f421096SArtur Rojek 8269f421096SArtur Rojek return ret; 8279f421096SArtur Rojek } 8289f421096SArtur Rojek EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute); 8299f421096SArtur Rojek 83000c5f80cSPeter Rosin int iio_read_avail_channel_raw(struct iio_channel *chan, 83100c5f80cSPeter Rosin const int **vals, int *length) 83200c5f80cSPeter Rosin { 83300c5f80cSPeter Rosin int ret; 83400c5f80cSPeter Rosin int type; 83500c5f80cSPeter Rosin 83689388ca4SArtur Rojek ret = iio_read_avail_channel_attribute(chan, vals, &type, length, 83789388ca4SArtur Rojek IIO_CHAN_INFO_RAW); 83800c5f80cSPeter Rosin 839c773f700SPeter Rosin if (ret >= 0 && type != IIO_VAL_INT) 84000c5f80cSPeter Rosin /* raw values are assumed to be IIO_VAL_INT */ 84100c5f80cSPeter Rosin ret = -EINVAL; 84200c5f80cSPeter Rosin 84300c5f80cSPeter Rosin return ret; 84400c5f80cSPeter Rosin } 84500c5f80cSPeter Rosin EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw); 84600c5f80cSPeter Rosin 84700c5f80cSPeter Rosin static int iio_channel_read_max(struct iio_channel *chan, 84800c5f80cSPeter Rosin int *val, int *val2, int *type, 84900c5f80cSPeter Rosin enum iio_chan_info_enum info) 85000c5f80cSPeter Rosin { 85100c5f80cSPeter Rosin int unused; 85200c5f80cSPeter Rosin const int *vals; 85300c5f80cSPeter Rosin int length; 85400c5f80cSPeter Rosin int ret; 85500c5f80cSPeter Rosin 85600c5f80cSPeter Rosin if (!val2) 85700c5f80cSPeter Rosin val2 = &unused; 85800c5f80cSPeter Rosin 85900c5f80cSPeter Rosin ret = iio_channel_read_avail(chan, &vals, type, &length, info); 86000c5f80cSPeter Rosin switch (ret) { 86100c5f80cSPeter Rosin case IIO_AVAIL_RANGE: 86200c5f80cSPeter Rosin switch (*type) { 86300c5f80cSPeter Rosin case IIO_VAL_INT: 86400c5f80cSPeter Rosin *val = vals[2]; 86500c5f80cSPeter Rosin break; 86600c5f80cSPeter Rosin default: 86700c5f80cSPeter Rosin *val = vals[4]; 86800c5f80cSPeter Rosin *val2 = vals[5]; 86900c5f80cSPeter Rosin } 87000c5f80cSPeter Rosin return 0; 87100c5f80cSPeter Rosin 87200c5f80cSPeter Rosin case IIO_AVAIL_LIST: 87300c5f80cSPeter Rosin if (length <= 0) 87400c5f80cSPeter Rosin return -EINVAL; 87500c5f80cSPeter Rosin switch (*type) { 87600c5f80cSPeter Rosin case IIO_VAL_INT: 87700c5f80cSPeter Rosin *val = vals[--length]; 87800c5f80cSPeter Rosin while (length) { 87900c5f80cSPeter Rosin if (vals[--length] > *val) 88000c5f80cSPeter Rosin *val = vals[length]; 88100c5f80cSPeter Rosin } 88200c5f80cSPeter Rosin break; 88300c5f80cSPeter Rosin default: 88400c5f80cSPeter Rosin /* FIXME: learn about max for other iio values */ 88500c5f80cSPeter Rosin return -EINVAL; 88600c5f80cSPeter Rosin } 88700c5f80cSPeter Rosin return 0; 88800c5f80cSPeter Rosin 88900c5f80cSPeter Rosin default: 89000c5f80cSPeter Rosin return ret; 89100c5f80cSPeter Rosin } 89200c5f80cSPeter Rosin } 89300c5f80cSPeter Rosin 89400c5f80cSPeter Rosin int iio_read_max_channel_raw(struct iio_channel *chan, int *val) 89500c5f80cSPeter Rosin { 896b804e2b7SJonathan Cameron struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); 89700c5f80cSPeter Rosin int ret; 89800c5f80cSPeter Rosin int type; 89900c5f80cSPeter Rosin 900b804e2b7SJonathan Cameron mutex_lock(&iio_dev_opaque->info_exist_lock); 90100c5f80cSPeter Rosin if (!chan->indio_dev->info) { 90200c5f80cSPeter Rosin ret = -ENODEV; 90300c5f80cSPeter Rosin goto err_unlock; 90400c5f80cSPeter Rosin } 90500c5f80cSPeter Rosin 90600c5f80cSPeter Rosin ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); 90700c5f80cSPeter Rosin err_unlock: 908b804e2b7SJonathan Cameron mutex_unlock(&iio_dev_opaque->info_exist_lock); 90900c5f80cSPeter Rosin 91000c5f80cSPeter Rosin return ret; 91100c5f80cSPeter Rosin } 91200c5f80cSPeter Rosin EXPORT_SYMBOL_GPL(iio_read_max_channel_raw); 91300c5f80cSPeter Rosin 914314be14bSJonathan Cameron int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) 915a980e046SJonathan Cameron { 916b804e2b7SJonathan Cameron struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); 917a980e046SJonathan Cameron int ret = 0; 918a980e046SJonathan Cameron /* Need to verify underlying driver has not gone away */ 919a980e046SJonathan Cameron 920b804e2b7SJonathan Cameron mutex_lock(&iio_dev_opaque->info_exist_lock); 921a980e046SJonathan Cameron if (chan->indio_dev->info == NULL) { 922a980e046SJonathan Cameron ret = -ENODEV; 923a980e046SJonathan Cameron goto err_unlock; 924a980e046SJonathan Cameron } 925a980e046SJonathan Cameron 926a980e046SJonathan Cameron *type = chan->channel->type; 927a980e046SJonathan Cameron err_unlock: 928b804e2b7SJonathan Cameron mutex_unlock(&iio_dev_opaque->info_exist_lock); 929a980e046SJonathan Cameron 930a980e046SJonathan Cameron return ret; 931a980e046SJonathan Cameron } 932314be14bSJonathan Cameron EXPORT_SYMBOL_GPL(iio_get_channel_type); 933f9380e71SDmitry Eremin-Solenikov 934f9380e71SDmitry Eremin-Solenikov static int iio_channel_write(struct iio_channel *chan, int val, int val2, 935f9380e71SDmitry Eremin-Solenikov enum iio_chan_info_enum info) 936f9380e71SDmitry Eremin-Solenikov { 937f9380e71SDmitry Eremin-Solenikov return chan->indio_dev->info->write_raw(chan->indio_dev, 938f9380e71SDmitry Eremin-Solenikov chan->channel, val, val2, info); 939f9380e71SDmitry Eremin-Solenikov } 940f9380e71SDmitry Eremin-Solenikov 94134739a21SArnaud Pouliquen int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2, 94234739a21SArnaud Pouliquen enum iio_chan_info_enum attribute) 943f9380e71SDmitry Eremin-Solenikov { 944b804e2b7SJonathan Cameron struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); 945f9380e71SDmitry Eremin-Solenikov int ret; 946f9380e71SDmitry Eremin-Solenikov 947b804e2b7SJonathan Cameron mutex_lock(&iio_dev_opaque->info_exist_lock); 948f9380e71SDmitry Eremin-Solenikov if (chan->indio_dev->info == NULL) { 949f9380e71SDmitry Eremin-Solenikov ret = -ENODEV; 950f9380e71SDmitry Eremin-Solenikov goto err_unlock; 951f9380e71SDmitry Eremin-Solenikov } 952f9380e71SDmitry Eremin-Solenikov 95334739a21SArnaud Pouliquen ret = iio_channel_write(chan, val, val2, attribute); 954f9380e71SDmitry Eremin-Solenikov err_unlock: 955b804e2b7SJonathan Cameron mutex_unlock(&iio_dev_opaque->info_exist_lock); 956f9380e71SDmitry Eremin-Solenikov 957f9380e71SDmitry Eremin-Solenikov return ret; 958f9380e71SDmitry Eremin-Solenikov } 95934739a21SArnaud Pouliquen EXPORT_SYMBOL_GPL(iio_write_channel_attribute); 96034739a21SArnaud Pouliquen 96134739a21SArnaud Pouliquen int iio_write_channel_raw(struct iio_channel *chan, int val) 96234739a21SArnaud Pouliquen { 96334739a21SArnaud Pouliquen return iio_write_channel_attribute(chan, val, 0, IIO_CHAN_INFO_RAW); 96434739a21SArnaud Pouliquen } 965f9380e71SDmitry Eremin-Solenikov EXPORT_SYMBOL_GPL(iio_write_channel_raw); 9668a848e75SPeter Rosin 9678a848e75SPeter Rosin unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan) 9688a848e75SPeter Rosin { 9698a848e75SPeter Rosin const struct iio_chan_spec_ext_info *ext_info; 9708a848e75SPeter Rosin unsigned int i = 0; 9718a848e75SPeter Rosin 9728a848e75SPeter Rosin if (!chan->channel->ext_info) 9738a848e75SPeter Rosin return i; 9748a848e75SPeter Rosin 9758a848e75SPeter Rosin for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++) 9768a848e75SPeter Rosin ++i; 9778a848e75SPeter Rosin 9788a848e75SPeter Rosin return i; 9798a848e75SPeter Rosin } 9808a848e75SPeter Rosin EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count); 9818a848e75SPeter Rosin 9828a848e75SPeter Rosin static const struct iio_chan_spec_ext_info *iio_lookup_ext_info( 9838a848e75SPeter Rosin const struct iio_channel *chan, 9848a848e75SPeter Rosin const char *attr) 9858a848e75SPeter Rosin { 9868a848e75SPeter Rosin const struct iio_chan_spec_ext_info *ext_info; 9878a848e75SPeter Rosin 9888a848e75SPeter Rosin if (!chan->channel->ext_info) 9898a848e75SPeter Rosin return NULL; 9908a848e75SPeter Rosin 9918a848e75SPeter Rosin for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) { 9928a848e75SPeter Rosin if (!strcmp(attr, ext_info->name)) 9938a848e75SPeter Rosin return ext_info; 9948a848e75SPeter Rosin } 9958a848e75SPeter Rosin 9968a848e75SPeter Rosin return NULL; 9978a848e75SPeter Rosin } 9988a848e75SPeter Rosin 9998a848e75SPeter Rosin ssize_t iio_read_channel_ext_info(struct iio_channel *chan, 10008a848e75SPeter Rosin const char *attr, char *buf) 10018a848e75SPeter Rosin { 10028a848e75SPeter Rosin const struct iio_chan_spec_ext_info *ext_info; 10038a848e75SPeter Rosin 10048a848e75SPeter Rosin ext_info = iio_lookup_ext_info(chan, attr); 10058a848e75SPeter Rosin if (!ext_info) 10068a848e75SPeter Rosin return -EINVAL; 10078a848e75SPeter Rosin 10088a848e75SPeter Rosin return ext_info->read(chan->indio_dev, ext_info->private, 10098a848e75SPeter Rosin chan->channel, buf); 10108a848e75SPeter Rosin } 10118a848e75SPeter Rosin EXPORT_SYMBOL_GPL(iio_read_channel_ext_info); 10128a848e75SPeter Rosin 10138a848e75SPeter Rosin ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, 10148a848e75SPeter Rosin const char *buf, size_t len) 10158a848e75SPeter Rosin { 10168a848e75SPeter Rosin const struct iio_chan_spec_ext_info *ext_info; 10178a848e75SPeter Rosin 10188a848e75SPeter Rosin ext_info = iio_lookup_ext_info(chan, attr); 10198a848e75SPeter Rosin if (!ext_info) 10208a848e75SPeter Rosin return -EINVAL; 10218a848e75SPeter Rosin 10228a848e75SPeter Rosin return ext_info->write(chan->indio_dev, ext_info->private, 10238a848e75SPeter Rosin chan->channel, buf, len); 10248a848e75SPeter Rosin } 10258a848e75SPeter Rosin EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); 1026