18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2017 Analog Devices Inc. 48c2ecf20Sopenharmony_ci * Author: Lars-Peter Clausen <lars@metafoo.de> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/err.h> 88c2ecf20Sopenharmony_ci#include <linux/export.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 138c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h> 148c2ecf20Sopenharmony_ci#include <linux/iio/hw-consumer.h> 158c2ecf20Sopenharmony_ci#include <linux/iio/buffer_impl.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/** 188c2ecf20Sopenharmony_ci * struct iio_hw_consumer - IIO hw consumer block 198c2ecf20Sopenharmony_ci * @buffers: hardware buffers list head. 208c2ecf20Sopenharmony_ci * @channels: IIO provider channels. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_cistruct iio_hw_consumer { 238c2ecf20Sopenharmony_ci struct list_head buffers; 248c2ecf20Sopenharmony_ci struct iio_channel *channels; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct hw_consumer_buffer { 288c2ecf20Sopenharmony_ci struct list_head head; 298c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 308c2ecf20Sopenharmony_ci struct iio_buffer buffer; 318c2ecf20Sopenharmony_ci long scan_mask[]; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic struct hw_consumer_buffer *iio_buffer_to_hw_consumer_buffer( 358c2ecf20Sopenharmony_ci struct iio_buffer *buffer) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci return container_of(buffer, struct hw_consumer_buffer, buffer); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic void iio_hw_buf_release(struct iio_buffer *buffer) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct hw_consumer_buffer *hw_buf = 438c2ecf20Sopenharmony_ci iio_buffer_to_hw_consumer_buffer(buffer); 448c2ecf20Sopenharmony_ci kfree(hw_buf); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const struct iio_buffer_access_funcs iio_hw_buf_access = { 488c2ecf20Sopenharmony_ci .release = &iio_hw_buf_release, 498c2ecf20Sopenharmony_ci .modes = INDIO_BUFFER_HARDWARE, 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic struct hw_consumer_buffer *iio_hw_consumer_get_buffer( 538c2ecf20Sopenharmony_ci struct iio_hw_consumer *hwc, struct iio_dev *indio_dev) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci size_t mask_size = BITS_TO_LONGS(indio_dev->masklength) * sizeof(long); 568c2ecf20Sopenharmony_ci struct hw_consumer_buffer *buf; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci list_for_each_entry(buf, &hwc->buffers, head) { 598c2ecf20Sopenharmony_ci if (buf->indio_dev == indio_dev) 608c2ecf20Sopenharmony_ci return buf; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci buf = kzalloc(sizeof(*buf) + mask_size, GFP_KERNEL); 648c2ecf20Sopenharmony_ci if (!buf) 658c2ecf20Sopenharmony_ci return NULL; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci buf->buffer.access = &iio_hw_buf_access; 688c2ecf20Sopenharmony_ci buf->indio_dev = indio_dev; 698c2ecf20Sopenharmony_ci buf->buffer.scan_mask = buf->scan_mask; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci iio_buffer_init(&buf->buffer); 728c2ecf20Sopenharmony_ci list_add_tail(&buf->head, &hwc->buffers); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return buf; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/** 788c2ecf20Sopenharmony_ci * iio_hw_consumer_alloc() - Allocate IIO hardware consumer 798c2ecf20Sopenharmony_ci * @dev: Pointer to consumer device. 808c2ecf20Sopenharmony_ci * 818c2ecf20Sopenharmony_ci * Returns a valid iio_hw_consumer on success or a ERR_PTR() on failure. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_cistruct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct hw_consumer_buffer *buf; 868c2ecf20Sopenharmony_ci struct iio_hw_consumer *hwc; 878c2ecf20Sopenharmony_ci struct iio_channel *chan; 888c2ecf20Sopenharmony_ci int ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci hwc = kzalloc(sizeof(*hwc), GFP_KERNEL); 918c2ecf20Sopenharmony_ci if (!hwc) 928c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&hwc->buffers); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci hwc->channels = iio_channel_get_all(dev); 978c2ecf20Sopenharmony_ci if (IS_ERR(hwc->channels)) { 988c2ecf20Sopenharmony_ci ret = PTR_ERR(hwc->channels); 998c2ecf20Sopenharmony_ci goto err_free_hwc; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci chan = &hwc->channels[0]; 1038c2ecf20Sopenharmony_ci while (chan->indio_dev) { 1048c2ecf20Sopenharmony_ci buf = iio_hw_consumer_get_buffer(hwc, chan->indio_dev); 1058c2ecf20Sopenharmony_ci if (!buf) { 1068c2ecf20Sopenharmony_ci ret = -ENOMEM; 1078c2ecf20Sopenharmony_ci goto err_put_buffers; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci set_bit(chan->channel->scan_index, buf->buffer.scan_mask); 1108c2ecf20Sopenharmony_ci chan++; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return hwc; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cierr_put_buffers: 1168c2ecf20Sopenharmony_ci list_for_each_entry(buf, &hwc->buffers, head) 1178c2ecf20Sopenharmony_ci iio_buffer_put(&buf->buffer); 1188c2ecf20Sopenharmony_ci iio_channel_release_all(hwc->channels); 1198c2ecf20Sopenharmony_cierr_free_hwc: 1208c2ecf20Sopenharmony_ci kfree(hwc); 1218c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_hw_consumer_alloc); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/** 1268c2ecf20Sopenharmony_ci * iio_hw_consumer_free() - Free IIO hardware consumer 1278c2ecf20Sopenharmony_ci * @hwc: hw consumer to free. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_civoid iio_hw_consumer_free(struct iio_hw_consumer *hwc) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct hw_consumer_buffer *buf, *n; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci iio_channel_release_all(hwc->channels); 1348c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, n, &hwc->buffers, head) 1358c2ecf20Sopenharmony_ci iio_buffer_put(&buf->buffer); 1368c2ecf20Sopenharmony_ci kfree(hwc); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_hw_consumer_free); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic void devm_iio_hw_consumer_release(struct device *dev, void *res) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci iio_hw_consumer_free(*(struct iio_hw_consumer **)res); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/** 1468c2ecf20Sopenharmony_ci * devm_iio_hw_consumer_alloc - Resource-managed iio_hw_consumer_alloc() 1478c2ecf20Sopenharmony_ci * @dev: Pointer to consumer device. 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * Managed iio_hw_consumer_alloc. iio_hw_consumer allocated with this function 1508c2ecf20Sopenharmony_ci * is automatically freed on driver detach. 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * returns pointer to allocated iio_hw_consumer on success, NULL on failure. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistruct iio_hw_consumer *devm_iio_hw_consumer_alloc(struct device *dev) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct iio_hw_consumer **ptr, *iio_hwc; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci ptr = devres_alloc(devm_iio_hw_consumer_release, sizeof(*ptr), 1598c2ecf20Sopenharmony_ci GFP_KERNEL); 1608c2ecf20Sopenharmony_ci if (!ptr) 1618c2ecf20Sopenharmony_ci return NULL; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci iio_hwc = iio_hw_consumer_alloc(dev); 1648c2ecf20Sopenharmony_ci if (IS_ERR(iio_hwc)) { 1658c2ecf20Sopenharmony_ci devres_free(ptr); 1668c2ecf20Sopenharmony_ci } else { 1678c2ecf20Sopenharmony_ci *ptr = iio_hwc; 1688c2ecf20Sopenharmony_ci devres_add(dev, ptr); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return iio_hwc; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_iio_hw_consumer_alloc); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * iio_hw_consumer_enable() - Enable IIO hardware consumer 1778c2ecf20Sopenharmony_ci * @hwc: iio_hw_consumer to enable. 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * Returns 0 on success. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ciint iio_hw_consumer_enable(struct iio_hw_consumer *hwc) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct hw_consumer_buffer *buf; 1848c2ecf20Sopenharmony_ci int ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci list_for_each_entry(buf, &hwc->buffers, head) { 1878c2ecf20Sopenharmony_ci ret = iio_update_buffers(buf->indio_dev, &buf->buffer, NULL); 1888c2ecf20Sopenharmony_ci if (ret) 1898c2ecf20Sopenharmony_ci goto err_disable_buffers; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cierr_disable_buffers: 1958c2ecf20Sopenharmony_ci list_for_each_entry_continue_reverse(buf, &hwc->buffers, head) 1968c2ecf20Sopenharmony_ci iio_update_buffers(buf->indio_dev, NULL, &buf->buffer); 1978c2ecf20Sopenharmony_ci return ret; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_hw_consumer_enable); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/** 2028c2ecf20Sopenharmony_ci * iio_hw_consumer_disable() - Disable IIO hardware consumer 2038c2ecf20Sopenharmony_ci * @hwc: iio_hw_consumer to disable. 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_civoid iio_hw_consumer_disable(struct iio_hw_consumer *hwc) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct hw_consumer_buffer *buf; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci list_for_each_entry(buf, &hwc->buffers, head) 2108c2ecf20Sopenharmony_ci iio_update_buffers(buf->indio_dev, NULL, &buf->buffer); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iio_hw_consumer_disable); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 2158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hardware consumer buffer the IIO framework"); 2168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 217