1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2bc0a409cSThierry Reding /*
3a6b5ec88SCrestez Dan Leonard * TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver
4a6b5ec88SCrestez Dan Leonard *
5bc0a409cSThierry Reding * Copyright (C) 2012 Avionic Design GmbH
6a6b5ec88SCrestez Dan Leonard * Copyright (C) 2016 Intel
7bc0a409cSThierry Reding *
8a6b5ec88SCrestez Dan Leonard * Datasheets:
93593cd53SAlexander A. Klimov * https://www.ti.com/lit/ds/symlink/adc081c021.pdf
103593cd53SAlexander A. Klimov * https://www.ti.com/lit/ds/symlink/adc101c021.pdf
113593cd53SAlexander A. Klimov * https://www.ti.com/lit/ds/symlink/adc121c021.pdf
12a6b5ec88SCrestez Dan Leonard *
13a6b5ec88SCrestez Dan Leonard * The devices have a very similar interface and differ mostly in the number of
14a6b5ec88SCrestez Dan Leonard * bits handled. For the 8-bit and 10-bit models the least-significant 4 or 2
15a6b5ec88SCrestez Dan Leonard * bits of value registers are reserved.
16bc0a409cSThierry Reding */
17bc0a409cSThierry Reding
18bc0a409cSThierry Reding #include <linux/err.h>
19bc0a409cSThierry Reding #include <linux/i2c.h>
20bc0a409cSThierry Reding #include <linux/module.h>
21b0873749SJonathan Cameron #include <linux/mod_devicetable.h>
22c9791a94SJonathan Cameron #include <linux/property.h>
23bc0a409cSThierry Reding
24bc0a409cSThierry Reding #include <linux/iio/iio.h>
2508e05d1fSCrestez Dan Leonard #include <linux/iio/buffer.h>
2608e05d1fSCrestez Dan Leonard #include <linux/iio/trigger_consumer.h>
2708e05d1fSCrestez Dan Leonard #include <linux/iio/triggered_buffer.h>
28bc0a409cSThierry Reding #include <linux/regulator/consumer.h>
29bc0a409cSThierry Reding
30bc0a409cSThierry Reding struct adc081c {
31bc0a409cSThierry Reding struct i2c_client *i2c;
32bc0a409cSThierry Reding struct regulator *ref;
33a6b5ec88SCrestez Dan Leonard
34a6b5ec88SCrestez Dan Leonard /* 8, 10 or 12 */
35a6b5ec88SCrestez Dan Leonard int bits;
3654f82df2SJonathan Cameron
3754f82df2SJonathan Cameron /* Ensure natural alignment of buffer elements */
3854f82df2SJonathan Cameron struct {
3954f82df2SJonathan Cameron u16 channel;
4054f82df2SJonathan Cameron s64 ts __aligned(8);
4154f82df2SJonathan Cameron } scan;
42bc0a409cSThierry Reding };
43bc0a409cSThierry Reding
44bc0a409cSThierry Reding #define REG_CONV_RES 0x00
45bc0a409cSThierry Reding
adc081c_read_raw(struct iio_dev * iio,struct iio_chan_spec const * channel,int * value,int * shift,long mask)46bc0a409cSThierry Reding static int adc081c_read_raw(struct iio_dev *iio,
47bc0a409cSThierry Reding struct iio_chan_spec const *channel, int *value,
48bc0a409cSThierry Reding int *shift, long mask)
49bc0a409cSThierry Reding {
50bc0a409cSThierry Reding struct adc081c *adc = iio_priv(iio);
51bc0a409cSThierry Reding int err;
52bc0a409cSThierry Reding
53bc0a409cSThierry Reding switch (mask) {
54bc0a409cSThierry Reding case IIO_CHAN_INFO_RAW:
55bc0a409cSThierry Reding err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES);
56bc0a409cSThierry Reding if (err < 0)
57bc0a409cSThierry Reding return err;
58bc0a409cSThierry Reding
59a6b5ec88SCrestez Dan Leonard *value = (err & 0xFFF) >> (12 - adc->bits);
60bc0a409cSThierry Reding return IIO_VAL_INT;
61bc0a409cSThierry Reding
62bc0a409cSThierry Reding case IIO_CHAN_INFO_SCALE:
63bc0a409cSThierry Reding err = regulator_get_voltage(adc->ref);
64bc0a409cSThierry Reding if (err < 0)
65bc0a409cSThierry Reding return err;
66bc0a409cSThierry Reding
67bc0a409cSThierry Reding *value = err / 1000;
68a6b5ec88SCrestez Dan Leonard *shift = adc->bits;
69bc0a409cSThierry Reding
70bc0a409cSThierry Reding return IIO_VAL_FRACTIONAL_LOG2;
71bc0a409cSThierry Reding
72bc0a409cSThierry Reding default:
73bc0a409cSThierry Reding break;
74bc0a409cSThierry Reding }
75bc0a409cSThierry Reding
76bc0a409cSThierry Reding return -EINVAL;
77bc0a409cSThierry Reding }
78bc0a409cSThierry Reding
7908e05d1fSCrestez Dan Leonard #define ADCxx1C_CHAN(_bits) { \
8008e05d1fSCrestez Dan Leonard .type = IIO_VOLTAGE, \
8108e05d1fSCrestez Dan Leonard .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
8208e05d1fSCrestez Dan Leonard .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
8308e05d1fSCrestez Dan Leonard .scan_type = { \
8408e05d1fSCrestez Dan Leonard .sign = 'u', \
8508e05d1fSCrestez Dan Leonard .realbits = (_bits), \
8608e05d1fSCrestez Dan Leonard .storagebits = 16, \
8708e05d1fSCrestez Dan Leonard .shift = 12 - (_bits), \
8808e05d1fSCrestez Dan Leonard .endianness = IIO_CPU, \
8908e05d1fSCrestez Dan Leonard }, \
9008e05d1fSCrestez Dan Leonard }
9108e05d1fSCrestez Dan Leonard
9208e05d1fSCrestez Dan Leonard #define DEFINE_ADCxx1C_CHANNELS(_name, _bits) \
9308e05d1fSCrestez Dan Leonard static const struct iio_chan_spec _name ## _channels[] = { \
9408e05d1fSCrestez Dan Leonard ADCxx1C_CHAN((_bits)), \
9508e05d1fSCrestez Dan Leonard IIO_CHAN_SOFT_TIMESTAMP(1), \
9608e05d1fSCrestez Dan Leonard }; \
9708e05d1fSCrestez Dan Leonard
9808e05d1fSCrestez Dan Leonard #define ADC081C_NUM_CHANNELS 2
99bc0a409cSThierry Reding
100a6b5ec88SCrestez Dan Leonard struct adcxx1c_model {
10108e05d1fSCrestez Dan Leonard const struct iio_chan_spec* channels;
102a6b5ec88SCrestez Dan Leonard int bits;
103a6b5ec88SCrestez Dan Leonard };
104a6b5ec88SCrestez Dan Leonard
10508e05d1fSCrestez Dan Leonard #define ADCxx1C_MODEL(_name, _bits) \
106a6b5ec88SCrestez Dan Leonard { \
10708e05d1fSCrestez Dan Leonard .channels = _name ## _channels, \
108a6b5ec88SCrestez Dan Leonard .bits = (_bits), \
109a6b5ec88SCrestez Dan Leonard }
110a6b5ec88SCrestez Dan Leonard
11108e05d1fSCrestez Dan Leonard DEFINE_ADCxx1C_CHANNELS(adc081c, 8);
11208e05d1fSCrestez Dan Leonard DEFINE_ADCxx1C_CHANNELS(adc101c, 10);
11308e05d1fSCrestez Dan Leonard DEFINE_ADCxx1C_CHANNELS(adc121c, 12);
11408e05d1fSCrestez Dan Leonard
115a6b5ec88SCrestez Dan Leonard /* Model ids are indexes in _models array */
116a6b5ec88SCrestez Dan Leonard enum adcxx1c_model_id {
117a6b5ec88SCrestez Dan Leonard ADC081C = 0,
118a6b5ec88SCrestez Dan Leonard ADC101C = 1,
119a6b5ec88SCrestez Dan Leonard ADC121C = 2,
120a6b5ec88SCrestez Dan Leonard };
121a6b5ec88SCrestez Dan Leonard
122a6b5ec88SCrestez Dan Leonard static struct adcxx1c_model adcxx1c_models[] = {
12308e05d1fSCrestez Dan Leonard ADCxx1C_MODEL(adc081c, 8),
12408e05d1fSCrestez Dan Leonard ADCxx1C_MODEL(adc101c, 10),
12508e05d1fSCrestez Dan Leonard ADCxx1C_MODEL(adc121c, 12),
126a6b5ec88SCrestez Dan Leonard };
127a6b5ec88SCrestez Dan Leonard
128bc0a409cSThierry Reding static const struct iio_info adc081c_info = {
129bc0a409cSThierry Reding .read_raw = adc081c_read_raw,
130bc0a409cSThierry Reding };
131bc0a409cSThierry Reding
adc081c_trigger_handler(int irq,void * p)13208e05d1fSCrestez Dan Leonard static irqreturn_t adc081c_trigger_handler(int irq, void *p)
13308e05d1fSCrestez Dan Leonard {
13408e05d1fSCrestez Dan Leonard struct iio_poll_func *pf = p;
13508e05d1fSCrestez Dan Leonard struct iio_dev *indio_dev = pf->indio_dev;
13608e05d1fSCrestez Dan Leonard struct adc081c *data = iio_priv(indio_dev);
13708e05d1fSCrestez Dan Leonard int ret;
13808e05d1fSCrestez Dan Leonard
13908e05d1fSCrestez Dan Leonard ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES);
14008e05d1fSCrestez Dan Leonard if (ret < 0)
14108e05d1fSCrestez Dan Leonard goto out;
14254f82df2SJonathan Cameron data->scan.channel = ret;
14354f82df2SJonathan Cameron iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
144bc2b7dabSGregor Boirie iio_get_time_ns(indio_dev));
14508e05d1fSCrestez Dan Leonard out:
14608e05d1fSCrestez Dan Leonard iio_trigger_notify_done(indio_dev->trig);
14708e05d1fSCrestez Dan Leonard return IRQ_HANDLED;
14808e05d1fSCrestez Dan Leonard }
14908e05d1fSCrestez Dan Leonard
adc081c_reg_disable(void * reg)1506c100eb9SJonathan Cameron static void adc081c_reg_disable(void *reg)
1516c100eb9SJonathan Cameron {
1526c100eb9SJonathan Cameron regulator_disable(reg);
1536c100eb9SJonathan Cameron }
1546c100eb9SJonathan Cameron
adc081c_probe(struct i2c_client * client)1554b50867fSUwe Kleine-König static int adc081c_probe(struct i2c_client *client)
156bc0a409cSThierry Reding {
1574b50867fSUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client);
158bc0a409cSThierry Reding struct iio_dev *iio;
159bc0a409cSThierry Reding struct adc081c *adc;
160c9791a94SJonathan Cameron const struct adcxx1c_model *model;
161bc0a409cSThierry Reding int err;
162bc0a409cSThierry Reding
163bc0a409cSThierry Reding if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
164f8d9d3b4SMatt Ranostay return -EOPNOTSUPP;
165bc0a409cSThierry Reding
166c9791a94SJonathan Cameron if (dev_fwnode(&client->dev))
167c9791a94SJonathan Cameron model = device_get_match_data(&client->dev);
168c9791a94SJonathan Cameron else
1697feae871SDan O'Donovan model = &adcxx1c_models[id->driver_data];
1707feae871SDan O'Donovan
17199e94b6dSSachin Kamat iio = devm_iio_device_alloc(&client->dev, sizeof(*adc));
172bc0a409cSThierry Reding if (!iio)
173bc0a409cSThierry Reding return -ENOMEM;
174bc0a409cSThierry Reding
175bc0a409cSThierry Reding adc = iio_priv(iio);
176bc0a409cSThierry Reding adc->i2c = client;
177a6b5ec88SCrestez Dan Leonard adc->bits = model->bits;
178bc0a409cSThierry Reding
17999e94b6dSSachin Kamat adc->ref = devm_regulator_get(&client->dev, "vref");
18099e94b6dSSachin Kamat if (IS_ERR(adc->ref))
18199e94b6dSSachin Kamat return PTR_ERR(adc->ref);
182bc0a409cSThierry Reding
183bc0a409cSThierry Reding err = regulator_enable(adc->ref);
184bc0a409cSThierry Reding if (err < 0)
18599e94b6dSSachin Kamat return err;
186bc0a409cSThierry Reding
1876c100eb9SJonathan Cameron err = devm_add_action_or_reset(&client->dev, adc081c_reg_disable,
1886c100eb9SJonathan Cameron adc->ref);
1896c100eb9SJonathan Cameron if (err)
1906c100eb9SJonathan Cameron return err;
1916c100eb9SJonathan Cameron
192bc0a409cSThierry Reding iio->name = dev_name(&client->dev);
193bc0a409cSThierry Reding iio->modes = INDIO_DIRECT_MODE;
194bc0a409cSThierry Reding iio->info = &adc081c_info;
195bc0a409cSThierry Reding
19608e05d1fSCrestez Dan Leonard iio->channels = model->channels;
19708e05d1fSCrestez Dan Leonard iio->num_channels = ADC081C_NUM_CHANNELS;
19808e05d1fSCrestez Dan Leonard
1996c100eb9SJonathan Cameron err = devm_iio_triggered_buffer_setup(&client->dev, iio, NULL,
2006c100eb9SJonathan Cameron adc081c_trigger_handler, NULL);
20108e05d1fSCrestez Dan Leonard if (err < 0) {
20208e05d1fSCrestez Dan Leonard dev_err(&client->dev, "iio triggered buffer setup failed\n");
203bc0a409cSThierry Reding return err;
204bc0a409cSThierry Reding }
205bc0a409cSThierry Reding
2066c100eb9SJonathan Cameron return devm_iio_device_register(&client->dev, iio);
207bc0a409cSThierry Reding }
208bc0a409cSThierry Reding
209bc0a409cSThierry Reding static const struct i2c_device_id adc081c_id[] = {
210a6b5ec88SCrestez Dan Leonard { "adc081c", ADC081C },
211a6b5ec88SCrestez Dan Leonard { "adc101c", ADC101C },
212a6b5ec88SCrestez Dan Leonard { "adc121c", ADC121C },
213bc0a409cSThierry Reding { }
214bc0a409cSThierry Reding };
215bc0a409cSThierry Reding MODULE_DEVICE_TABLE(i2c, adc081c_id);
216bc0a409cSThierry Reding
217c9791a94SJonathan Cameron static const struct acpi_device_id adc081c_acpi_match[] = {
218c9791a94SJonathan Cameron /* Used on some AAEON boards */
219c9791a94SJonathan Cameron { "ADC081C", (kernel_ulong_t)&adcxx1c_models[ADC081C] },
220c9791a94SJonathan Cameron { }
221c9791a94SJonathan Cameron };
222c9791a94SJonathan Cameron MODULE_DEVICE_TABLE(acpi, adc081c_acpi_match);
223c9791a94SJonathan Cameron
224bc0a409cSThierry Reding static const struct of_device_id adc081c_of_match[] = {
225c9791a94SJonathan Cameron { .compatible = "ti,adc081c", .data = &adcxx1c_models[ADC081C] },
226c9791a94SJonathan Cameron { .compatible = "ti,adc101c", .data = &adcxx1c_models[ADC101C] },
227c9791a94SJonathan Cameron { .compatible = "ti,adc121c", .data = &adcxx1c_models[ADC121C] },
228bc0a409cSThierry Reding { }
229bc0a409cSThierry Reding };
230bc0a409cSThierry Reding MODULE_DEVICE_TABLE(of, adc081c_of_match);
231bc0a409cSThierry Reding
232bc0a409cSThierry Reding static struct i2c_driver adc081c_driver = {
233bc0a409cSThierry Reding .driver = {
234bc0a409cSThierry Reding .name = "adc081c",
235b0873749SJonathan Cameron .of_match_table = adc081c_of_match,
236c9791a94SJonathan Cameron .acpi_match_table = adc081c_acpi_match,
237bc0a409cSThierry Reding },
238*7cf15f42SUwe Kleine-König .probe = adc081c_probe,
239bc0a409cSThierry Reding .id_table = adc081c_id,
240bc0a409cSThierry Reding };
241bc0a409cSThierry Reding module_i2c_driver(adc081c_driver);
242bc0a409cSThierry Reding
243bc0a409cSThierry Reding MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
244a6b5ec88SCrestez Dan Leonard MODULE_DESCRIPTION("Texas Instruments ADC081C/ADC101C/ADC121C driver");
245bc0a409cSThierry Reding MODULE_LICENSE("GPL v2");
246