1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ADA4250 driver |
4 | * |
5 | * Copyright 2022 Analog Devices Inc. |
6 | */ |
7 | |
8 | #include <linux/bitfield.h> |
9 | #include <linux/bits.h> |
10 | #include <linux/device.h> |
11 | #include <linux/iio/iio.h> |
12 | #include <linux/module.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/regulator/consumer.h> |
15 | #include <linux/spi/spi.h> |
16 | |
17 | #include <asm/unaligned.h> |
18 | |
19 | /* ADA4250 Register Map */ |
20 | #define ADA4250_REG_GAIN_MUX 0x00 |
21 | #define ADA4250_REG_REFBUF_EN 0x01 |
22 | #define ADA4250_REG_RESET 0x02 |
23 | #define ADA4250_REG_SNSR_CAL_VAL 0x04 |
24 | #define ADA4250_REG_SNSR_CAL_CNFG 0x05 |
25 | #define ADA4250_REG_DIE_REV 0x18 |
26 | #define ADA4250_REG_CHIP_ID 0x19 |
27 | |
28 | /* ADA4250_REG_GAIN_MUX Map */ |
29 | #define ADA4250_GAIN_MUX_MSK GENMASK(2, 0) |
30 | |
31 | /* ADA4250_REG_REFBUF Map */ |
32 | #define ADA4250_REFBUF_MSK BIT(0) |
33 | |
34 | /* ADA4250_REG_RESET Map */ |
35 | #define ADA4250_RESET_MSK BIT(0) |
36 | |
37 | /* ADA4250_REG_SNSR_CAL_VAL Map */ |
38 | #define ADA4250_CAL_CFG_BIAS_MSK GENMASK(7, 0) |
39 | |
40 | /* ADA4250_REG_SNSR_CAL_CNFG Bit Definition */ |
41 | #define ADA4250_BIAS_SET_MSK GENMASK(3, 2) |
42 | #define ADA4250_RANGE_SET_MSK GENMASK(1, 0) |
43 | |
44 | /* Miscellaneous definitions */ |
45 | #define ADA4250_CHIP_ID 0x4250 |
46 | #define ADA4250_RANGE1 0 |
47 | #define ADA4250_RANGE4 3 |
48 | |
49 | /* ADA4250 current bias set */ |
50 | enum ada4250_current_bias { |
51 | ADA4250_BIAS_DISABLED, |
52 | ADA4250_BIAS_BANDGAP, |
53 | ADA4250_BIAS_AVDD, |
54 | }; |
55 | |
56 | struct ada4250_state { |
57 | struct spi_device *spi; |
58 | struct regmap *regmap; |
59 | struct regulator *reg; |
60 | /* Protect against concurrent accesses to the device and data content */ |
61 | struct mutex lock; |
62 | u8 bias; |
63 | u8 gain; |
64 | int offset_uv; |
65 | bool refbuf_en; |
66 | }; |
67 | |
68 | /* ADA4250 Current Bias Source Settings: Disabled, Bandgap Reference, AVDD */ |
69 | static const int calibbias_table[] = {0, 1, 2}; |
70 | |
71 | /* ADA4250 Gain (V/V) values: 1, 2, 4, 8, 16, 32, 64, 128 */ |
72 | static const int hwgain_table[] = {1, 2, 4, 8, 16, 32, 64, 128}; |
73 | |
74 | static const struct regmap_config ada4250_regmap_config = { |
75 | .reg_bits = 8, |
76 | .val_bits = 8, |
77 | .read_flag_mask = BIT(7), |
78 | .max_register = 0x1A, |
79 | }; |
80 | |
81 | static int ada4250_set_offset_uv(struct iio_dev *indio_dev, |
82 | const struct iio_chan_spec *chan, |
83 | int offset_uv) |
84 | { |
85 | struct ada4250_state *st = iio_priv(indio_dev); |
86 | |
87 | int i, ret, x[8], max_vos, min_vos, voltage_v, vlsb = 0; |
88 | u8 offset_raw, range = ADA4250_RANGE1; |
89 | u32 lsb_coeff[6] = {1333, 2301, 4283, 8289, 16311, 31599}; |
90 | |
91 | if (st->bias == 0 || st->bias == 3) |
92 | return -EINVAL; |
93 | |
94 | voltage_v = regulator_get_voltage(regulator: st->reg); |
95 | voltage_v = DIV_ROUND_CLOSEST(voltage_v, 1000000); |
96 | |
97 | if (st->bias == ADA4250_BIAS_AVDD) |
98 | x[0] = voltage_v; |
99 | else |
100 | x[0] = 5; |
101 | |
102 | x[1] = 126 * (x[0] - 1); |
103 | |
104 | for (i = 0; i < 6; i++) |
105 | x[i + 2] = DIV_ROUND_CLOSEST(x[1] * 1000, lsb_coeff[i]); |
106 | |
107 | if (st->gain == 0) |
108 | return -EINVAL; |
109 | |
110 | /* |
111 | * Compute Range and Voltage per LSB for the Sensor Offset Calibration |
112 | * Example of computation for Range 1 and Range 2 (Curren Bias Set = AVDD): |
113 | * Range 1 Range 2 |
114 | * Gain | Max Vos(mV) | LSB(mV) | Max Vos(mV) | LSB(mV) | |
115 | * 2 | X1*127 | X1=0.126(AVDD-1) | X1*3*127 | X1*3 | |
116 | * 4 | X2*127 | X2=X1/1.3333 | X2*3*127 | X2*3 | |
117 | * 8 | X3*127 | X3=X1/2.301 | X3*3*127 | X3*3 | |
118 | * 16 | X4*127 | X4=X1/4.283 | X4*3*127 | X4*3 | |
119 | * 32 | X5*127 | X5=X1/8.289 | X5*3*127 | X5*3 | |
120 | * 64 | X6*127 | X6=X1/16.311 | X6*3*127 | X6*3 | |
121 | * 128 | X7*127 | X7=X1/31.599 | X7*3*127 | X7*3 | |
122 | */ |
123 | for (i = ADA4250_RANGE1; i <= ADA4250_RANGE4; i++) { |
124 | max_vos = x[st->gain] * 127 * ((1 << (i + 1)) - 1); |
125 | min_vos = -1 * max_vos; |
126 | if (offset_uv > min_vos && offset_uv < max_vos) { |
127 | range = i; |
128 | vlsb = x[st->gain] * ((1 << (i + 1)) - 1); |
129 | break; |
130 | } |
131 | } |
132 | |
133 | if (vlsb <= 0) |
134 | return -EINVAL; |
135 | |
136 | offset_raw = DIV_ROUND_CLOSEST(abs(offset_uv), vlsb); |
137 | |
138 | mutex_lock(&st->lock); |
139 | ret = regmap_update_bits(map: st->regmap, ADA4250_REG_SNSR_CAL_CNFG, |
140 | ADA4250_RANGE_SET_MSK, |
141 | FIELD_PREP(ADA4250_RANGE_SET_MSK, range)); |
142 | if (ret) |
143 | goto exit; |
144 | |
145 | st->offset_uv = offset_raw * vlsb; |
146 | |
147 | /* |
148 | * To set the offset calibration value, use bits [6:0] and bit 7 as the |
149 | * polarity bit (set to "0" for a negative offset and "1" for a positive |
150 | * offset). |
151 | */ |
152 | if (offset_uv < 0) { |
153 | offset_raw |= BIT(7); |
154 | st->offset_uv *= (-1); |
155 | } |
156 | |
157 | ret = regmap_write(map: st->regmap, ADA4250_REG_SNSR_CAL_VAL, val: offset_raw); |
158 | |
159 | exit: |
160 | mutex_unlock(lock: &st->lock); |
161 | |
162 | return ret; |
163 | } |
164 | |
165 | static int ada4250_read_raw(struct iio_dev *indio_dev, |
166 | struct iio_chan_spec const *chan, |
167 | int *val, int *val2, long info) |
168 | { |
169 | struct ada4250_state *st = iio_priv(indio_dev); |
170 | int ret; |
171 | |
172 | switch (info) { |
173 | case IIO_CHAN_INFO_HARDWAREGAIN: |
174 | ret = regmap_read(map: st->regmap, ADA4250_REG_GAIN_MUX, val); |
175 | if (ret) |
176 | return ret; |
177 | |
178 | *val = BIT(*val); |
179 | |
180 | return IIO_VAL_INT; |
181 | case IIO_CHAN_INFO_OFFSET: |
182 | *val = st->offset_uv; |
183 | |
184 | return IIO_VAL_INT; |
185 | case IIO_CHAN_INFO_CALIBBIAS: |
186 | ret = regmap_read(map: st->regmap, ADA4250_REG_SNSR_CAL_CNFG, val); |
187 | if (ret) |
188 | return ret; |
189 | |
190 | *val = FIELD_GET(ADA4250_BIAS_SET_MSK, *val); |
191 | |
192 | return IIO_VAL_INT; |
193 | case IIO_CHAN_INFO_SCALE: |
194 | *val = 1; |
195 | *val2 = 1000000; |
196 | |
197 | return IIO_VAL_FRACTIONAL; |
198 | default: |
199 | return -EINVAL; |
200 | } |
201 | } |
202 | |
203 | static int ada4250_write_raw(struct iio_dev *indio_dev, |
204 | struct iio_chan_spec const *chan, |
205 | int val, int val2, long info) |
206 | { |
207 | struct ada4250_state *st = iio_priv(indio_dev); |
208 | int ret; |
209 | |
210 | switch (info) { |
211 | case IIO_CHAN_INFO_HARDWAREGAIN: |
212 | ret = regmap_write(map: st->regmap, ADA4250_REG_GAIN_MUX, |
213 | FIELD_PREP(ADA4250_GAIN_MUX_MSK, ilog2(val))); |
214 | if (ret) |
215 | return ret; |
216 | |
217 | st->gain = ilog2(val); |
218 | |
219 | return ret; |
220 | case IIO_CHAN_INFO_OFFSET: |
221 | return ada4250_set_offset_uv(indio_dev, chan, offset_uv: val); |
222 | case IIO_CHAN_INFO_CALIBBIAS: |
223 | ret = regmap_update_bits(map: st->regmap, ADA4250_REG_SNSR_CAL_CNFG, |
224 | ADA4250_BIAS_SET_MSK, |
225 | FIELD_PREP(ADA4250_BIAS_SET_MSK, val)); |
226 | if (ret) |
227 | return ret; |
228 | |
229 | st->bias = val; |
230 | |
231 | return ret; |
232 | default: |
233 | return -EINVAL; |
234 | } |
235 | } |
236 | |
237 | static int ada4250_read_avail(struct iio_dev *indio_dev, |
238 | struct iio_chan_spec const *chan, |
239 | const int **vals, int *type, int *length, |
240 | long mask) |
241 | { |
242 | switch (mask) { |
243 | case IIO_CHAN_INFO_CALIBBIAS: |
244 | *vals = calibbias_table; |
245 | *type = IIO_VAL_INT; |
246 | *length = ARRAY_SIZE(calibbias_table); |
247 | |
248 | return IIO_AVAIL_LIST; |
249 | case IIO_CHAN_INFO_HARDWAREGAIN: |
250 | *vals = hwgain_table; |
251 | *type = IIO_VAL_INT; |
252 | *length = ARRAY_SIZE(hwgain_table); |
253 | |
254 | return IIO_AVAIL_LIST; |
255 | default: |
256 | return -EINVAL; |
257 | } |
258 | } |
259 | |
260 | static int ada4250_reg_access(struct iio_dev *indio_dev, |
261 | unsigned int reg, |
262 | unsigned int write_val, |
263 | unsigned int *read_val) |
264 | { |
265 | struct ada4250_state *st = iio_priv(indio_dev); |
266 | |
267 | if (read_val) |
268 | return regmap_read(map: st->regmap, reg, val: read_val); |
269 | else |
270 | return regmap_write(map: st->regmap, reg, val: write_val); |
271 | } |
272 | |
273 | static const struct iio_info ada4250_info = { |
274 | .read_raw = ada4250_read_raw, |
275 | .write_raw = ada4250_write_raw, |
276 | .read_avail = &ada4250_read_avail, |
277 | .debugfs_reg_access = &ada4250_reg_access, |
278 | }; |
279 | |
280 | static const struct iio_chan_spec ada4250_channels[] = { |
281 | { |
282 | .type = IIO_VOLTAGE, |
283 | .output = 1, |
284 | .indexed = 1, |
285 | .channel = 0, |
286 | .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | |
287 | BIT(IIO_CHAN_INFO_OFFSET) | |
288 | BIT(IIO_CHAN_INFO_CALIBBIAS) | |
289 | BIT(IIO_CHAN_INFO_SCALE), |
290 | .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) | |
291 | BIT(IIO_CHAN_INFO_HARDWAREGAIN), |
292 | } |
293 | }; |
294 | |
295 | static void ada4250_reg_disable(void *data) |
296 | { |
297 | regulator_disable(regulator: data); |
298 | } |
299 | |
300 | static int ada4250_init(struct ada4250_state *st) |
301 | { |
302 | int ret; |
303 | u16 chip_id; |
304 | u8 data[2] __aligned(8) = {}; |
305 | struct spi_device *spi = st->spi; |
306 | |
307 | st->refbuf_en = device_property_read_bool(dev: &spi->dev, propname: "adi,refbuf-enable" ); |
308 | |
309 | st->reg = devm_regulator_get(dev: &spi->dev, id: "avdd" ); |
310 | if (IS_ERR(ptr: st->reg)) |
311 | return dev_err_probe(dev: &spi->dev, err: PTR_ERR(ptr: st->reg), |
312 | fmt: "failed to get the AVDD voltage\n" ); |
313 | |
314 | ret = regulator_enable(regulator: st->reg); |
315 | if (ret) { |
316 | dev_err(&spi->dev, "Failed to enable specified AVDD supply\n" ); |
317 | return ret; |
318 | } |
319 | |
320 | ret = devm_add_action_or_reset(&spi->dev, ada4250_reg_disable, st->reg); |
321 | if (ret) |
322 | return ret; |
323 | |
324 | ret = regmap_write(map: st->regmap, ADA4250_REG_RESET, |
325 | FIELD_PREP(ADA4250_RESET_MSK, 1)); |
326 | if (ret) |
327 | return ret; |
328 | |
329 | ret = regmap_bulk_read(map: st->regmap, ADA4250_REG_CHIP_ID, val: data, val_count: 2); |
330 | if (ret) |
331 | return ret; |
332 | |
333 | chip_id = get_unaligned_le16(p: data); |
334 | |
335 | if (chip_id != ADA4250_CHIP_ID) { |
336 | dev_err(&spi->dev, "Invalid chip ID.\n" ); |
337 | return -EINVAL; |
338 | } |
339 | |
340 | return regmap_write(map: st->regmap, ADA4250_REG_REFBUF_EN, |
341 | FIELD_PREP(ADA4250_REFBUF_MSK, st->refbuf_en)); |
342 | } |
343 | |
344 | static int ada4250_probe(struct spi_device *spi) |
345 | { |
346 | struct iio_dev *indio_dev; |
347 | struct regmap *regmap; |
348 | struct ada4250_state *st; |
349 | int ret; |
350 | |
351 | indio_dev = devm_iio_device_alloc(parent: &spi->dev, sizeof_priv: sizeof(*st)); |
352 | if (!indio_dev) |
353 | return -ENOMEM; |
354 | |
355 | regmap = devm_regmap_init_spi(spi, &ada4250_regmap_config); |
356 | if (IS_ERR(ptr: regmap)) |
357 | return PTR_ERR(ptr: regmap); |
358 | |
359 | st = iio_priv(indio_dev); |
360 | st->regmap = regmap; |
361 | st->spi = spi; |
362 | |
363 | indio_dev->info = &ada4250_info; |
364 | indio_dev->name = "ada4250" ; |
365 | indio_dev->channels = ada4250_channels; |
366 | indio_dev->num_channels = ARRAY_SIZE(ada4250_channels); |
367 | |
368 | mutex_init(&st->lock); |
369 | |
370 | ret = ada4250_init(st); |
371 | if (ret) { |
372 | dev_err(&spi->dev, "ADA4250 init failed\n" ); |
373 | return ret; |
374 | } |
375 | |
376 | return devm_iio_device_register(&spi->dev, indio_dev); |
377 | } |
378 | |
379 | static const struct spi_device_id ada4250_id[] = { |
380 | { "ada4250" , 0 }, |
381 | {} |
382 | }; |
383 | MODULE_DEVICE_TABLE(spi, ada4250_id); |
384 | |
385 | static const struct of_device_id ada4250_of_match[] = { |
386 | { .compatible = "adi,ada4250" }, |
387 | {}, |
388 | }; |
389 | MODULE_DEVICE_TABLE(of, ada4250_of_match); |
390 | |
391 | static struct spi_driver ada4250_driver = { |
392 | .driver = { |
393 | .name = "ada4250" , |
394 | .of_match_table = ada4250_of_match, |
395 | }, |
396 | .probe = ada4250_probe, |
397 | .id_table = ada4250_id, |
398 | }; |
399 | module_spi_driver(ada4250_driver); |
400 | |
401 | MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com" ); |
402 | MODULE_DESCRIPTION("Analog Devices ADA4250" ); |
403 | MODULE_LICENSE("GPL v2" ); |
404 | |