File Index
Files and directories
Sign in
FILE

ad7606_par.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/adc/ad7606_par.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * AD7606 Parallel Interface ADC driver
  4 *
  5 * Copyright 2011 - 2024 Analog Devices Inc.
  6 * Copyright 2024 BayLibre SAS.
  7 */
  8
  9#include <linux/err.h>
 10#include <linux/gpio/consumer.h>
 11#include <linux/io.h>
 12#include <linux/mod_devicetable.h>
 13#include <linux/module.h>
 14#include <linux/platform_device.h>
 15#include <linux/property.h>
 16#include <linux/types.h>
 17
 18#include <linux/iio/backend.h>
 19#include <linux/iio/iio.h>
 20
 21#include "ad7606.h"
 22#include "ad7606_bus_iface.h"
 23
 24static int ad7606_par_bus_update_scan_mode(struct iio_dev *indio_dev,
 25					   const unsigned long *scan_mask)
 26{
 27	struct ad7606_state *st = iio_priv(indio_dev);
 28	unsigned int c, ret;
 29
 30	for (c = 0; c < indio_dev->num_channels; c++) {
 31		if (test_bit(c, scan_mask))
 32			ret = iio_backend_chan_enable(st->back, c);
 33		else
 34			ret = iio_backend_chan_disable(st->back, c);
 35		if (ret)
 36			return ret;
 37	}
 38
 39	return 0;
 40}
 41
 42static int ad7606_par_bus_setup_iio_backend(struct device *dev,
 43					    struct iio_dev *indio_dev)
 44{
 45	struct ad7606_state *st = iio_priv(indio_dev);
 46	unsigned int c;
 47	int ret;
 48	struct iio_backend_data_fmt data = {
 49		.sign_extend = true,
 50		.enable = true,
 51	};
 52
 53	st->back = devm_iio_backend_get(dev, NULL);
 54	if (IS_ERR(st->back))
 55		return PTR_ERR(st->back);
 56
 57	/* If the device is iio_backend powered the PWM is mandatory */
 58	if (!st->cnvst_pwm)
 59		return dev_err_probe(st->dev, -EINVAL,
 60				     "A PWM is mandatory when using backend.\n");
 61
 62	ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
 63	if (ret)
 64		return ret;
 65
 66	ret = devm_iio_backend_enable(dev, st->back);
 67	if (ret)
 68		return ret;
 69
 70	for (c = 0; c < indio_dev->num_channels; c++) {
 71		ret = iio_backend_data_format_set(st->back, c, &data);
 72		if (ret)
 73			return ret;
 74	}
 75
 76	return 0;
 77}
 78
 79static int ad7606_par_bus_reg_read(struct ad7606_state *st, unsigned int addr)
 80{
 81	struct ad7606_platform_data *pdata = st->dev->platform_data;
 82	int val, ret;
 83
 84	ret = pdata->bus_reg_read(st->back, addr, &val);
 85	if (ret)
 86		return ret;
 87
 88	return val;
 89}
 90
 91static int ad7606_par_bus_reg_write(struct ad7606_state *st, unsigned int addr,
 92				    unsigned int val)
 93{
 94	struct ad7606_platform_data *pdata = st->dev->platform_data;
 95
 96	return pdata->bus_reg_write(st->back, addr, val);
 97}
 98
 99static const struct ad7606_bus_ops ad7606_bi_bops = {
100	.iio_backend_config = ad7606_par_bus_setup_iio_backend,
101	.update_scan_mode = ad7606_par_bus_update_scan_mode,
102	.reg_read = ad7606_par_bus_reg_read,
103	.reg_write = ad7606_par_bus_reg_write,
104};
105
106static int ad7606_par16_read_block(struct device *dev,
107				   int count, void *buf)
108{
109	struct iio_dev *indio_dev = dev_get_drvdata(dev);
110	struct ad7606_state *st = iio_priv(indio_dev);
111
112
113	/*
114	 * On the parallel interface, the frstdata signal is set to high while
115	 * and after reading the sample of the first channel and low for all
116	 * other channels.  This can be used to check that the incoming data is
117	 * correctly aligned.  During normal operation the data should never
118	 * become unaligned, but some glitch or electrostatic discharge might
119	 * cause an extra read or clock cycle.  Monitoring the frstdata signal
120	 * allows to recover from such failure situations.
121	 */
122	int num = count;
123	u16 *_buf = buf;
124
125	if (st->gpio_frstdata) {
126		insw((unsigned long)st->base_address, _buf, 1);
127		if (!gpiod_get_value(st->gpio_frstdata)) {
128			ad7606_reset(st);
129			return -EIO;
130		}
131		_buf++;
132		num--;
133	}
134	insw((unsigned long)st->base_address, _buf, num);
135	return 0;
136}
137
138static const struct ad7606_bus_ops ad7606_par16_bops = {
139	.read_block = ad7606_par16_read_block,
140};
141
142static int ad7606_par8_read_block(struct device *dev,
143				  int count, void *buf)
144{
145	struct iio_dev *indio_dev = dev_get_drvdata(dev);
146	struct ad7606_state *st = iio_priv(indio_dev);
147	/*
148	 * On the parallel interface, the frstdata signal is set to high while
149	 * and after reading the sample of the first channel and low for all
150	 * other channels.  This can be used to check that the incoming data is
151	 * correctly aligned.  During normal operation the data should never
152	 * become unaligned, but some glitch or electrostatic discharge might
153	 * cause an extra read or clock cycle.  Monitoring the frstdata signal
154	 * allows to recover from such failure situations.
155	 */
156	int num = count;
157	u16 *_buf = buf;
158
159	if (st->gpio_frstdata) {
160		insb((unsigned long)st->base_address, _buf, 2);
161		if (!gpiod_get_value(st->gpio_frstdata)) {
162			ad7606_reset(st);
163			return -EIO;
164		}
165		_buf++;
166		num--;
167	}
168	insb((unsigned long)st->base_address, _buf, num * 2);
169
170	return 0;
171}
172
173static const struct ad7606_bus_ops ad7606_par8_bops = {
174	.read_block = ad7606_par8_read_block,
175};
176
177static int ad7606_par_probe(struct platform_device *pdev)
178{
179	const struct ad7606_chip_info *chip_info;
180	const struct platform_device_id *id;
181	struct resource *res;
182	void __iomem *addr;
183	resource_size_t remap_size;
184	int irq;
185
186	/*
187	 * If a firmware node is available (ACPI or DT), platform_device_id is null
188	 * and we must use get_match_data.
189	 */
190	if (dev_fwnode(&pdev->dev)) {
191		chip_info = device_get_match_data(&pdev->dev);
192		if (device_property_present(&pdev->dev, "io-backends"))
193			/*
194			 * If a backend is available ,call the core probe with backend
195			 * bops, otherwise use the former bops.
196			 */
197			return ad7606_probe(&pdev->dev, 0, NULL,
198					    chip_info,
199					    &ad7606_bi_bops);
200	} else {
201		id = platform_get_device_id(pdev);
202		chip_info = (const struct ad7606_chip_info *)id->driver_data;
203	}
204
205	irq = platform_get_irq(pdev, 0);
206	if (irq < 0)
207		return irq;
208
209	addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
210	if (IS_ERR(addr))
211		return PTR_ERR(addr);
212
213	remap_size = resource_size(res);
214
215	return ad7606_probe(&pdev->dev, irq, addr, chip_info,
216			    remap_size > 1 ? &ad7606_par16_bops :
217			    &ad7606_par8_bops);
218}
219
220static const struct platform_device_id ad7606_driver_ids[] = {
221	{ .name	= "ad7605-4", .driver_data = (kernel_ulong_t)&ad7605_4_info, },
222	{ .name	= "ad7606-4", .driver_data = (kernel_ulong_t)&ad7606_4_info, },
223	{ .name	= "ad7606-6", .driver_data = (kernel_ulong_t)&ad7606_6_info, },
224	{ .name	= "ad7606-8", .driver_data = (kernel_ulong_t)&ad7606_8_info, },
225	{ .name	= "ad7606b", .driver_data = (kernel_ulong_t)&ad7606b_info, },
226	{ .name = "ad7606c-16", .driver_data = (kernel_ulong_t)&ad7606c_16_info },
227	{ .name = "ad7606c-18", .driver_data = (kernel_ulong_t)&ad7606c_18_info },
228	{ .name	= "ad7607", .driver_data = (kernel_ulong_t)&ad7607_info, },
229	{ .name	= "ad7608", .driver_data = (kernel_ulong_t)&ad7608_info, },
230	{ .name	= "ad7609", .driver_data = (kernel_ulong_t)&ad7609_info, },
231	{ }
232};
233MODULE_DEVICE_TABLE(platform, ad7606_driver_ids);
234
235static const struct of_device_id ad7606_of_match[] = {
236	{ .compatible = "adi,ad7605-4", .data = &ad7605_4_info },
237	{ .compatible = "adi,ad7606-4", .data = &ad7606_4_info },
238	{ .compatible = "adi,ad7606-6", .data = &ad7606_6_info },
239	{ .compatible = "adi,ad7606-8", .data = &ad7606_8_info },
240	{ .compatible = "adi,ad7606b", .data = &ad7606b_info },
241	{ .compatible = "adi,ad7606c-16", .data = &ad7606c_16_info },
242	{ .compatible = "adi,ad7606c-18", .data = &ad7606c_18_info },
243	{ .compatible = "adi,ad7607", .data = &ad7607_info },
244	{ .compatible = "adi,ad7608", .data = &ad7608_info },
245	{ .compatible = "adi,ad7609", .data = &ad7609_info },
246	{ }
247};
248MODULE_DEVICE_TABLE(of, ad7606_of_match);
249
250static struct platform_driver ad7606_driver = {
251	.probe = ad7606_par_probe,
252	.id_table = ad7606_driver_ids,
253	.driver = {
254		.name = "ad7606",
255		.pm = AD7606_PM_OPS,
256		.of_match_table = ad7606_of_match,
257	},
258};
259module_platform_driver(ad7606_driver);
260
261MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
262MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
263MODULE_LICENSE("GPL v2");
264MODULE_IMPORT_NS("IIO_AD7606");
265MODULE_IMPORT_NS("IIO_BACKEND");
~/ Read-only file preview.