File Index
Files and directories
Sign in
FILE

ltc2309.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/adc/ltc2309.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * The LTC2309 is an 8-Channel, 12-Bit SAR ADC with an I2C Interface.
  4 *
  5 * Datasheet:
  6 * https://www.analog.com/media/en/technical-documentation/data-sheets/2309fd.pdf
  7 *
  8 * Copyright (c) 2023, Liam Beguin <liambeguin@gmail.com>
  9 */
 10#include <linux/bitfield.h>
 11#include <linux/i2c.h>
 12#include <linux/iio/iio.h>
 13#include <linux/kernel.h>
 14#include <linux/module.h>
 15#include <linux/mutex.h>
 16#include <linux/regulator/consumer.h>
 17
 18#define LTC2309_ADC_RESOLUTION	12
 19#define LTC2309_INTERNAL_REF_MV 4096
 20
 21#define LTC2309_DIN_CH_MASK	GENMASK(7, 4)
 22#define LTC2309_DIN_SDN		BIT(7)
 23#define LTC2309_DIN_OSN		BIT(6)
 24#define LTC2309_DIN_S1		BIT(5)
 25#define LTC2309_DIN_S0		BIT(4)
 26#define LTC2309_DIN_UNI		BIT(3)
 27#define LTC2309_DIN_SLEEP	BIT(2)
 28
 29/**
 30 * struct ltc2309 - internal device data structure
 31 * @dev:	Device reference
 32 * @client:	I2C reference
 33 * @lock:	Lock to serialize data access
 34 * @vref_mv:	Internal voltage reference
 35 */
 36struct ltc2309 {
 37	struct device		*dev;
 38	struct i2c_client	*client;
 39	struct mutex		lock; /* serialize data access */
 40	int			vref_mv;
 41};
 42
 43/* Order matches expected channel address, See datasheet Table 1. */
 44enum ltc2309_channels {
 45	LTC2309_CH0_CH1 = 0,
 46	LTC2309_CH2_CH3,
 47	LTC2309_CH4_CH5,
 48	LTC2309_CH6_CH7,
 49	LTC2309_CH1_CH0,
 50	LTC2309_CH3_CH2,
 51	LTC2309_CH5_CH4,
 52	LTC2309_CH7_CH6,
 53	LTC2309_CH0,
 54	LTC2309_CH2,
 55	LTC2309_CH4,
 56	LTC2309_CH6,
 57	LTC2309_CH1,
 58	LTC2309_CH3,
 59	LTC2309_CH5,
 60	LTC2309_CH7,
 61};
 62
 63#define LTC2309_CHAN(_chan, _addr) {				\
 64	.type = IIO_VOLTAGE,					\
 65	.indexed = 1,						\
 66	.address = _addr,					\
 67	.channel = _chan,					\
 68	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
 69	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
 70}
 71
 72#define LTC2309_DIFF_CHAN(_chan, _chan2, _addr) {		\
 73	.type = IIO_VOLTAGE,					\
 74	.differential = 1,					\
 75	.indexed = 1,						\
 76	.address = _addr,					\
 77	.channel = _chan,					\
 78	.channel2 = _chan2,					\
 79	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
 80	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
 81}
 82
 83static const struct iio_chan_spec ltc2309_channels[] = {
 84	LTC2309_CHAN(0, LTC2309_CH0),
 85	LTC2309_CHAN(1, LTC2309_CH1),
 86	LTC2309_CHAN(2, LTC2309_CH2),
 87	LTC2309_CHAN(3, LTC2309_CH3),
 88	LTC2309_CHAN(4, LTC2309_CH4),
 89	LTC2309_CHAN(5, LTC2309_CH5),
 90	LTC2309_CHAN(6, LTC2309_CH6),
 91	LTC2309_CHAN(7, LTC2309_CH7),
 92	LTC2309_DIFF_CHAN(0, 1, LTC2309_CH0_CH1),
 93	LTC2309_DIFF_CHAN(2, 3, LTC2309_CH2_CH3),
 94	LTC2309_DIFF_CHAN(4, 5, LTC2309_CH4_CH5),
 95	LTC2309_DIFF_CHAN(6, 7, LTC2309_CH6_CH7),
 96	LTC2309_DIFF_CHAN(1, 0, LTC2309_CH1_CH0),
 97	LTC2309_DIFF_CHAN(3, 2, LTC2309_CH3_CH2),
 98	LTC2309_DIFF_CHAN(5, 4, LTC2309_CH5_CH4),
 99	LTC2309_DIFF_CHAN(7, 6, LTC2309_CH7_CH6),
100};
101
102static int ltc2309_read_raw_channel(struct ltc2309 *ltc2309,
103				    unsigned long address, int *val)
104{
105	int ret;
106	__be16 buf;
107	u8 din;
108
109	din = FIELD_PREP(LTC2309_DIN_CH_MASK, address & 0x0f) |
110		FIELD_PREP(LTC2309_DIN_UNI, 1) |
111		FIELD_PREP(LTC2309_DIN_SLEEP, 0);
112
113	ret = i2c_smbus_write_byte(ltc2309->client, din);
114	if (ret < 0) {
115		dev_err(ltc2309->dev, "i2c command failed: %pe\n",
116			ERR_PTR(ret));
117		return ret;
118	}
119
120	ret = i2c_master_recv(ltc2309->client, (char *)&buf, 2);
121	if (ret < 0) {
122		dev_err(ltc2309->dev, "i2c read failed: %pe\n", ERR_PTR(ret));
123		return ret;
124	}
125
126	*val = be16_to_cpu(buf) >> 4;
127
128	return ret;
129}
130
131static int ltc2309_read_raw(struct iio_dev *indio_dev,
132			    struct iio_chan_spec const *chan, int *val,
133			    int *val2, long mask)
134{
135	struct ltc2309 *ltc2309 = iio_priv(indio_dev);
136	int ret;
137
138	switch (mask) {
139	case IIO_CHAN_INFO_RAW:
140		mutex_lock(&ltc2309->lock);
141		ret = ltc2309_read_raw_channel(ltc2309, chan->address, val);
142		mutex_unlock(&ltc2309->lock);
143		if (ret < 0)
144			return -EINVAL;
145		return IIO_VAL_INT;
146	case IIO_CHAN_INFO_SCALE:
147		*val = ltc2309->vref_mv;
148		*val2 = LTC2309_ADC_RESOLUTION;
149		return IIO_VAL_FRACTIONAL_LOG2;
150	default:
151		return -EINVAL;
152	}
153}
154
155static const struct iio_info ltc2309_info = {
156	.read_raw = ltc2309_read_raw,
157};
158
159static int ltc2309_probe(struct i2c_client *client)
160{
161	struct iio_dev *indio_dev;
162	struct ltc2309 *ltc2309;
163	int ret;
164
165	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*ltc2309));
166	if (!indio_dev)
167		return -ENOMEM;
168
169	ltc2309 = iio_priv(indio_dev);
170	ltc2309->dev = &indio_dev->dev;
171	ltc2309->client = client;
172
173	indio_dev->name = "ltc2309";
174	indio_dev->modes = INDIO_DIRECT_MODE;
175	indio_dev->channels = ltc2309_channels;
176	indio_dev->num_channels = ARRAY_SIZE(ltc2309_channels);
177	indio_dev->info = &ltc2309_info;
178
179	ret = devm_regulator_get_enable_read_voltage(&client->dev, "vref");
180	if (ret < 0 && ret != -ENODEV)
181		return dev_err_probe(ltc2309->dev, ret,
182				     "failed to get vref voltage\n");
183
184	ltc2309->vref_mv = ret == -ENODEV ? LTC2309_INTERNAL_REF_MV : ret / 1000;
185
186	mutex_init(&ltc2309->lock);
187
188	return devm_iio_device_register(&client->dev, indio_dev);
189}
190
191static const struct of_device_id ltc2309_of_match[] = {
192	{ .compatible = "lltc,ltc2309" },
193	{ }
194};
195MODULE_DEVICE_TABLE(of, ltc2309_of_match);
196
197static const struct i2c_device_id ltc2309_id[] = {
198	{ "ltc2309" },
199	{ }
200};
201MODULE_DEVICE_TABLE(i2c, ltc2309_id);
202
203static struct i2c_driver ltc2309_driver = {
204	.driver = {
205		.name = "ltc2309",
206		.of_match_table = ltc2309_of_match,
207	},
208	.probe		= ltc2309_probe,
209	.id_table	= ltc2309_id,
210};
211module_i2c_driver(ltc2309_driver);
212
213MODULE_AUTHOR("Liam Beguin <liambeguin@gmail.com>");
214MODULE_DESCRIPTION("Linear Technology LTC2309 ADC");
215MODULE_LICENSE("GPL v2");
~/ Read-only file preview.