Commit | Line | Data |
---|---|---|
bc0a409c | 1 | /* |
a6b5ec88 CDL |
2 | * TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver |
3 | * | |
bc0a409c | 4 | * Copyright (C) 2012 Avionic Design GmbH |
a6b5ec88 | 5 | * Copyright (C) 2016 Intel |
bc0a409c TR |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
a6b5ec88 CDL |
10 | * |
11 | * Datasheets: | |
12 | * http://www.ti.com/lit/ds/symlink/adc081c021.pdf | |
13 | * http://www.ti.com/lit/ds/symlink/adc101c021.pdf | |
14 | * http://www.ti.com/lit/ds/symlink/adc121c021.pdf | |
15 | * | |
16 | * The devices have a very similar interface and differ mostly in the number of | |
17 | * bits handled. For the 8-bit and 10-bit models the least-significant 4 or 2 | |
18 | * bits of value registers are reserved. | |
bc0a409c TR |
19 | */ |
20 | ||
21 | #include <linux/err.h> | |
22 | #include <linux/i2c.h> | |
23 | #include <linux/module.h> | |
1f100e80 | 24 | #include <linux/of.h> |
bc0a409c TR |
25 | |
26 | #include <linux/iio/iio.h> | |
08e05d1f CDL |
27 | #include <linux/iio/buffer.h> |
28 | #include <linux/iio/trigger_consumer.h> | |
29 | #include <linux/iio/triggered_buffer.h> | |
bc0a409c TR |
30 | #include <linux/regulator/consumer.h> |
31 | ||
32 | struct adc081c { | |
33 | struct i2c_client *i2c; | |
34 | struct regulator *ref; | |
a6b5ec88 CDL |
35 | |
36 | /* 8, 10 or 12 */ | |
37 | int bits; | |
bc0a409c TR |
38 | }; |
39 | ||
40 | #define REG_CONV_RES 0x00 | |
41 | ||
42 | static int adc081c_read_raw(struct iio_dev *iio, | |
43 | struct iio_chan_spec const *channel, int *value, | |
44 | int *shift, long mask) | |
45 | { | |
46 | struct adc081c *adc = iio_priv(iio); | |
47 | int err; | |
48 | ||
49 | switch (mask) { | |
50 | case IIO_CHAN_INFO_RAW: | |
51 | err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES); | |
52 | if (err < 0) | |
53 | return err; | |
54 | ||
a6b5ec88 | 55 | *value = (err & 0xFFF) >> (12 - adc->bits); |
bc0a409c TR |
56 | return IIO_VAL_INT; |
57 | ||
58 | case IIO_CHAN_INFO_SCALE: | |
59 | err = regulator_get_voltage(adc->ref); | |
60 | if (err < 0) | |
61 | return err; | |
62 | ||
63 | *value = err / 1000; | |
a6b5ec88 | 64 | *shift = adc->bits; |
bc0a409c TR |
65 | |
66 | return IIO_VAL_FRACTIONAL_LOG2; | |
67 | ||
68 | default: | |
69 | break; | |
70 | } | |
71 | ||
72 | return -EINVAL; | |
73 | } | |
74 | ||
08e05d1f CDL |
75 | #define ADCxx1C_CHAN(_bits) { \ |
76 | .type = IIO_VOLTAGE, \ | |
77 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
78 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
79 | .scan_type = { \ | |
80 | .sign = 'u', \ | |
81 | .realbits = (_bits), \ | |
82 | .storagebits = 16, \ | |
83 | .shift = 12 - (_bits), \ | |
84 | .endianness = IIO_CPU, \ | |
85 | }, \ | |
86 | } | |
87 | ||
88 | #define DEFINE_ADCxx1C_CHANNELS(_name, _bits) \ | |
89 | static const struct iio_chan_spec _name ## _channels[] = { \ | |
90 | ADCxx1C_CHAN((_bits)), \ | |
91 | IIO_CHAN_SOFT_TIMESTAMP(1), \ | |
92 | }; \ | |
93 | ||
94 | #define ADC081C_NUM_CHANNELS 2 | |
bc0a409c | 95 | |
a6b5ec88 | 96 | struct adcxx1c_model { |
08e05d1f | 97 | const struct iio_chan_spec* channels; |
a6b5ec88 CDL |
98 | int bits; |
99 | }; | |
100 | ||
08e05d1f | 101 | #define ADCxx1C_MODEL(_name, _bits) \ |
a6b5ec88 | 102 | { \ |
08e05d1f | 103 | .channels = _name ## _channels, \ |
a6b5ec88 CDL |
104 | .bits = (_bits), \ |
105 | } | |
106 | ||
08e05d1f CDL |
107 | DEFINE_ADCxx1C_CHANNELS(adc081c, 8); |
108 | DEFINE_ADCxx1C_CHANNELS(adc101c, 10); | |
109 | DEFINE_ADCxx1C_CHANNELS(adc121c, 12); | |
110 | ||
a6b5ec88 CDL |
111 | /* Model ids are indexes in _models array */ |
112 | enum adcxx1c_model_id { | |
113 | ADC081C = 0, | |
114 | ADC101C = 1, | |
115 | ADC121C = 2, | |
116 | }; | |
117 | ||
118 | static struct adcxx1c_model adcxx1c_models[] = { | |
08e05d1f CDL |
119 | ADCxx1C_MODEL(adc081c, 8), |
120 | ADCxx1C_MODEL(adc101c, 10), | |
121 | ADCxx1C_MODEL(adc121c, 12), | |
a6b5ec88 CDL |
122 | }; |
123 | ||
bc0a409c TR |
124 | static const struct iio_info adc081c_info = { |
125 | .read_raw = adc081c_read_raw, | |
126 | .driver_module = THIS_MODULE, | |
127 | }; | |
128 | ||
08e05d1f CDL |
129 | static irqreturn_t adc081c_trigger_handler(int irq, void *p) |
130 | { | |
131 | struct iio_poll_func *pf = p; | |
132 | struct iio_dev *indio_dev = pf->indio_dev; | |
133 | struct adc081c *data = iio_priv(indio_dev); | |
134 | u16 buf[8]; /* 2 bytes data + 6 bytes padding + 8 bytes timestamp */ | |
135 | int ret; | |
136 | ||
137 | ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES); | |
138 | if (ret < 0) | |
139 | goto out; | |
140 | buf[0] = ret; | |
141 | iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); | |
142 | out: | |
143 | iio_trigger_notify_done(indio_dev->trig); | |
144 | return IRQ_HANDLED; | |
145 | } | |
146 | ||
bc0a409c TR |
147 | static int adc081c_probe(struct i2c_client *client, |
148 | const struct i2c_device_id *id) | |
149 | { | |
150 | struct iio_dev *iio; | |
151 | struct adc081c *adc; | |
a6b5ec88 | 152 | struct adcxx1c_model *model = &adcxx1c_models[id->driver_data]; |
bc0a409c TR |
153 | int err; |
154 | ||
155 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) | |
f8d9d3b4 | 156 | return -EOPNOTSUPP; |
bc0a409c | 157 | |
99e94b6d | 158 | iio = devm_iio_device_alloc(&client->dev, sizeof(*adc)); |
bc0a409c TR |
159 | if (!iio) |
160 | return -ENOMEM; | |
161 | ||
162 | adc = iio_priv(iio); | |
163 | adc->i2c = client; | |
a6b5ec88 | 164 | adc->bits = model->bits; |
bc0a409c | 165 | |
99e94b6d SK |
166 | adc->ref = devm_regulator_get(&client->dev, "vref"); |
167 | if (IS_ERR(adc->ref)) | |
168 | return PTR_ERR(adc->ref); | |
bc0a409c TR |
169 | |
170 | err = regulator_enable(adc->ref); | |
171 | if (err < 0) | |
99e94b6d | 172 | return err; |
bc0a409c TR |
173 | |
174 | iio->dev.parent = &client->dev; | |
175 | iio->name = dev_name(&client->dev); | |
176 | iio->modes = INDIO_DIRECT_MODE; | |
177 | iio->info = &adc081c_info; | |
178 | ||
08e05d1f CDL |
179 | iio->channels = model->channels; |
180 | iio->num_channels = ADC081C_NUM_CHANNELS; | |
181 | ||
182 | err = iio_triggered_buffer_setup(iio, NULL, adc081c_trigger_handler, NULL); | |
183 | if (err < 0) { | |
184 | dev_err(&client->dev, "iio triggered buffer setup failed\n"); | |
185 | goto err_regulator_disable; | |
186 | } | |
bc0a409c TR |
187 | |
188 | err = iio_device_register(iio); | |
189 | if (err < 0) | |
08e05d1f | 190 | goto err_buffer_cleanup; |
bc0a409c TR |
191 | |
192 | i2c_set_clientdata(client, iio); | |
193 | ||
194 | return 0; | |
195 | ||
08e05d1f CDL |
196 | err_buffer_cleanup: |
197 | iio_triggered_buffer_cleanup(iio); | |
198 | err_regulator_disable: | |
bc0a409c | 199 | regulator_disable(adc->ref); |
bc0a409c TR |
200 | |
201 | return err; | |
202 | } | |
203 | ||
204 | static int adc081c_remove(struct i2c_client *client) | |
205 | { | |
206 | struct iio_dev *iio = i2c_get_clientdata(client); | |
207 | struct adc081c *adc = iio_priv(iio); | |
208 | ||
209 | iio_device_unregister(iio); | |
08e05d1f | 210 | iio_triggered_buffer_cleanup(iio); |
bc0a409c | 211 | regulator_disable(adc->ref); |
bc0a409c TR |
212 | |
213 | return 0; | |
214 | } | |
215 | ||
216 | static const struct i2c_device_id adc081c_id[] = { | |
a6b5ec88 CDL |
217 | { "adc081c", ADC081C }, |
218 | { "adc101c", ADC101C }, | |
219 | { "adc121c", ADC121C }, | |
bc0a409c TR |
220 | { } |
221 | }; | |
222 | MODULE_DEVICE_TABLE(i2c, adc081c_id); | |
223 | ||
224 | #ifdef CONFIG_OF | |
225 | static const struct of_device_id adc081c_of_match[] = { | |
226 | { .compatible = "ti,adc081c" }, | |
a6b5ec88 CDL |
227 | { .compatible = "ti,adc101c" }, |
228 | { .compatible = "ti,adc121c" }, | |
bc0a409c TR |
229 | { } |
230 | }; | |
231 | MODULE_DEVICE_TABLE(of, adc081c_of_match); | |
232 | #endif | |
233 | ||
234 | static struct i2c_driver adc081c_driver = { | |
235 | .driver = { | |
236 | .name = "adc081c", | |
bc0a409c TR |
237 | .of_match_table = of_match_ptr(adc081c_of_match), |
238 | }, | |
239 | .probe = adc081c_probe, | |
240 | .remove = adc081c_remove, | |
241 | .id_table = adc081c_id, | |
242 | }; | |
243 | module_i2c_driver(adc081c_driver); | |
244 | ||
245 | MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); | |
a6b5ec88 | 246 | MODULE_DESCRIPTION("Texas Instruments ADC081C/ADC101C/ADC121C driver"); |
bc0a409c | 247 | MODULE_LICENSE("GPL v2"); |