Skip to content

Commit a4c3029

Browse files
justephnunojsa
authored andcommitted
iio: adc: ad7380: add support for adaq4370-4 and adaq4380-4
adaq4370-4 (2MSPS) and adaq4380-4 (4MSPS) are quad-channel precision data acquisition signal chain μModule solutions compatible with the ad738x family, with the following differences: - pin selectable gain in front of each 4 adc - internal reference is 3V derived from refin-supply (5V) - additional supplies This implies that IIO_CHAN_INFO_SCALE can not be shared by type for these new devices. Signed-off-by: Julien Stephan <jstephan@baylibre.com> Reviewed-by: David Lechner <dlechner@baylibre.com> Link: https://patch.msgid.link/20241030-ad7380-add-adaq4380-4-support-v4-4-864ff02babae@baylibre.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
1 parent fd1d1d5 commit a4c3029

File tree

1 file changed

+126
-4
lines changed

1 file changed

+126
-4
lines changed

drivers/iio/adc/ad7380.c

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
* ad7381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7381-4.pdf
1414
* ad7383/4-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-4-ad7384-4.pdf
1515
* ad7386/7/8-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7386-4-7387-4-7388-4.pdf
16+
* adaq4370-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4370-4.pdf
17+
* adaq4380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4380-4.pdf
1618
*/
1719

1820
#include <linux/align.h>
@@ -22,11 +24,14 @@
2224
#include <linux/device.h>
2325
#include <linux/err.h>
2426
#include <linux/kernel.h>
27+
#include <linux/math.h>
2528
#include <linux/module.h>
2629
#include <linux/regmap.h>
2730
#include <linux/regulator/consumer.h>
2831
#include <linux/slab.h>
2932
#include <linux/spi/spi.h>
33+
#include <linux/units.h>
34+
#include <linux/util_macros.h>
3035

3136
#include <linux/iio/buffer.h>
3237
#include <linux/iio/iio.h>
@@ -36,6 +41,8 @@
3641
#define MAX_NUM_CHANNELS 8
3742
/* 2.5V internal reference voltage */
3843
#define AD7380_INTERNAL_REF_MV 2500
44+
/* 3.3V internal reference voltage for ADAQ */
45+
#define ADAQ4380_INTERNAL_REF_MV 3300
3946

4047
/* reading and writing registers is more reliable at lower than max speed */
4148
#define AD7380_REG_WR_SPEED_HZ 10000000
@@ -82,6 +89,7 @@
8289
* supports only 1 SDO line (standard SPI transaction)
8390
*/
8491
#define AD7380_NUM_SDO_LINES 1
92+
#define AD7380_DEFAULT_GAIN_MILLI 1000
8593

8694
struct ad7380_timing_specs {
8795
const unsigned int t_csh_ns; /* CS minimum high time */
@@ -92,10 +100,12 @@ struct ad7380_chip_info {
92100
const struct iio_chan_spec *channels;
93101
unsigned int num_channels;
94102
unsigned int num_simult_channels;
103+
bool has_hardware_gain;
95104
bool has_mux;
96105
const char * const *supplies;
97106
unsigned int num_supplies;
98107
bool external_ref_only;
108+
bool adaq_internal_ref_only;
99109
const char * const *vcm_supplies;
100110
unsigned int num_vcm_supplies;
101111
const unsigned long *available_scan_masks;
@@ -187,11 +197,12 @@ static const struct iio_scan_type ad7380_scan_type_16_u[] = {
187197
},
188198
};
189199

190-
#define AD7380_CHANNEL(index, bits, diff, sign) { \
200+
#define _AD7380_CHANNEL(index, bits, diff, sign, gain) { \
191201
.type = IIO_VOLTAGE, \
192202
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
203+
((gain) ? BIT(IIO_CHAN_INFO_SCALE) : 0) | \
193204
((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \
194-
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
205+
.info_mask_shared_by_type = ((gain) ? 0 : BIT(IIO_CHAN_INFO_SCALE)) | \
195206
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
196207
.info_mask_shared_by_type_available = \
197208
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
@@ -205,6 +216,12 @@ static const struct iio_scan_type ad7380_scan_type_16_u[] = {
205216
.num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits##_##sign), \
206217
}
207218

219+
#define AD7380_CHANNEL(index, bits, diff, sign) \
220+
_AD7380_CHANNEL(index, bits, diff, sign, false)
221+
222+
#define ADAQ4380_CHANNEL(index, bits, diff, sign) \
223+
_AD7380_CHANNEL(index, bits, diff, sign, true)
224+
208225
#define DEFINE_AD7380_2_CHANNEL(name, bits, diff, sign) \
209226
static const struct iio_chan_spec name[] = { \
210227
AD7380_CHANNEL(0, bits, diff, sign), \
@@ -221,6 +238,15 @@ static const struct iio_chan_spec name[] = { \
221238
IIO_CHAN_SOFT_TIMESTAMP(4), \
222239
}
223240

241+
#define DEFINE_ADAQ4380_4_CHANNEL(name, bits, diff, sign) \
242+
static const struct iio_chan_spec name[] = { \
243+
ADAQ4380_CHANNEL(0, bits, diff, sign), \
244+
ADAQ4380_CHANNEL(1, bits, diff, sign), \
245+
ADAQ4380_CHANNEL(2, bits, diff, sign), \
246+
ADAQ4380_CHANNEL(3, bits, diff, sign), \
247+
IIO_CHAN_SOFT_TIMESTAMP(4), \
248+
}
249+
224250
#define DEFINE_AD7380_8_CHANNEL(name, bits, diff, sign) \
225251
static const struct iio_chan_spec name[] = { \
226252
AD7380_CHANNEL(0, bits, diff, sign), \
@@ -239,6 +265,7 @@ DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1, s);
239265
DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1, s);
240266
DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1, s);
241267
DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1, s);
268+
DEFINE_ADAQ4380_4_CHANNEL(adaq4380_4_channels, 16, 1, s);
242269
/* pseudo differential */
243270
DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0, s);
244271
DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0, s);
@@ -257,6 +284,10 @@ static const char * const ad7380_supplies[] = {
257284
"vcc", "vlogic",
258285
};
259286

287+
static const char * const adaq4380_supplies[] = {
288+
"ldo", "vcc", "vlogic", "vs-p", "vs-n", "refin",
289+
};
290+
260291
static const char * const ad7380_2_channel_vcm_supplies[] = {
261292
"aina", "ainb",
262293
};
@@ -347,6 +378,11 @@ static const int ad7380_oversampling_ratios[] = {
347378
1, 2, 4, 8, 16, 32,
348379
};
349380

381+
/* Gains stored as fractions of 1000 so they can be expressed by integers. */
382+
static const int ad7380_gains[] = {
383+
300, 600, 1000, 1600,
384+
};
385+
350386
static const struct ad7380_chip_info ad7380_chip_info = {
351387
.name = "ad7380",
352388
.channels = ad7380_channels,
@@ -516,6 +552,32 @@ static const struct ad7380_chip_info ad7388_4_chip_info = {
516552
.timing_specs = &ad7380_4_timing,
517553
};
518554

555+
static const struct ad7380_chip_info adaq4370_4_chip_info = {
556+
.name = "adaq4370-4",
557+
.channels = adaq4380_4_channels,
558+
.num_channels = ARRAY_SIZE(adaq4380_4_channels),
559+
.num_simult_channels = 4,
560+
.supplies = adaq4380_supplies,
561+
.num_supplies = ARRAY_SIZE(adaq4380_supplies),
562+
.adaq_internal_ref_only = true,
563+
.has_hardware_gain = true,
564+
.available_scan_masks = ad7380_4_channel_scan_masks,
565+
.timing_specs = &ad7380_4_timing,
566+
};
567+
568+
static const struct ad7380_chip_info adaq4380_4_chip_info = {
569+
.name = "adaq4380-4",
570+
.channels = adaq4380_4_channels,
571+
.num_channels = ARRAY_SIZE(adaq4380_4_channels),
572+
.num_simult_channels = 4,
573+
.supplies = adaq4380_supplies,
574+
.num_supplies = ARRAY_SIZE(adaq4380_supplies),
575+
.adaq_internal_ref_only = true,
576+
.has_hardware_gain = true,
577+
.available_scan_masks = ad7380_4_channel_scan_masks,
578+
.timing_specs = &ad7380_4_timing,
579+
};
580+
519581
struct ad7380_state {
520582
const struct ad7380_chip_info *chip_info;
521583
struct spi_device *spi;
@@ -526,6 +588,7 @@ struct ad7380_state {
526588
bool seq;
527589
unsigned int vref_mv;
528590
unsigned int vcm_mv[MAX_NUM_CHANNELS];
591+
unsigned int gain_milli[MAX_NUM_CHANNELS];
529592
/* xfers, message an buffer for reading sample data */
530593
struct spi_transfer normal_xfer[2];
531594
struct spi_message normal_msg;
@@ -876,8 +939,15 @@ static int ad7380_read_raw(struct iio_dev *indio_dev,
876939
* * (2 × VREF) / 2^N, for differential chips
877940
* * VREF / 2^N, for pseudo-differential chips
878941
* where N is the ADC resolution (i.e realbits)
942+
*
943+
* The gain is stored as a fraction of 1000 and, as we need to
944+
* divide vref_mv by the gain, we invert the gain/1000 fraction.
879945
*/
880-
*val = st->vref_mv;
946+
if (st->chip_info->has_hardware_gain)
947+
*val = mult_frac(st->vref_mv, MILLI,
948+
st->gain_milli[chan->scan_index]);
949+
else
950+
*val = st->vref_mv;
881951
*val2 = scan_type->realbits - chan->differential;
882952

883953
return IIO_VAL_FRACTIONAL_LOG2;
@@ -1059,7 +1129,19 @@ static int ad7380_probe(struct spi_device *spi)
10591129
"Failed to enable power supplies\n");
10601130
fsleep(T_POWERUP_US);
10611131

1062-
if (st->chip_info->external_ref_only) {
1132+
if (st->chip_info->adaq_internal_ref_only) {
1133+
/*
1134+
* ADAQ chips use fixed internal reference but still
1135+
* require a specific reference supply to power it.
1136+
* "refin" is already enabled with other power supplies
1137+
* in bulk_get_enable().
1138+
*/
1139+
1140+
st->vref_mv = ADAQ4380_INTERNAL_REF_MV;
1141+
1142+
/* these chips don't have a register bit for this */
1143+
external_ref_en = false;
1144+
} else if (st->chip_info->external_ref_only) {
10631145
ret = devm_regulator_get_enable_read_voltage(dev, "refin");
10641146
if (ret < 0)
10651147
return dev_err_probe(dev, ret,
@@ -1103,6 +1185,42 @@ static int ad7380_probe(struct spi_device *spi)
11031185
st->vcm_mv[i] = ret / 1000;
11041186
}
11051187

1188+
for (i = 0; i < MAX_NUM_CHANNELS; i++)
1189+
st->gain_milli[i] = AD7380_DEFAULT_GAIN_MILLI;
1190+
1191+
if (st->chip_info->has_hardware_gain) {
1192+
device_for_each_child_node_scoped(dev, node) {
1193+
unsigned int channel, gain;
1194+
int gain_idx;
1195+
1196+
ret = fwnode_property_read_u32(node, "reg", &channel);
1197+
if (ret)
1198+
return dev_err_probe(dev, ret,
1199+
"Failed to read reg property\n");
1200+
1201+
if (channel >= st->chip_info->num_channels - 1)
1202+
return dev_err_probe(dev, -EINVAL,
1203+
"Invalid channel number %i\n",
1204+
channel);
1205+
1206+
ret = fwnode_property_read_u32(node, "adi,gain-milli",
1207+
&gain);
1208+
if (ret && ret != -EINVAL)
1209+
return dev_err_probe(dev, ret,
1210+
"Failed to read gain for channel %i\n",
1211+
channel);
1212+
if (ret != -EINVAL) {
1213+
/*
1214+
* Match gain value from dt to one of supported
1215+
* gains
1216+
*/
1217+
gain_idx = find_closest(gain, ad7380_gains,
1218+
ARRAY_SIZE(ad7380_gains));
1219+
st->gain_milli[channel] = ad7380_gains[gain_idx];
1220+
}
1221+
}
1222+
}
1223+
11061224
st->regmap = devm_regmap_init(dev, NULL, st, &ad7380_regmap_config);
11071225
if (IS_ERR(st->regmap))
11081226
return dev_err_probe(dev, PTR_ERR(st->regmap),
@@ -1185,6 +1303,8 @@ static const struct of_device_id ad7380_of_match_table[] = {
11851303
{ .compatible = "adi,ad7386-4", .data = &ad7386_4_chip_info },
11861304
{ .compatible = "adi,ad7387-4", .data = &ad7387_4_chip_info },
11871305
{ .compatible = "adi,ad7388-4", .data = &ad7388_4_chip_info },
1306+
{ .compatible = "adi,adaq4370-4", .data = &adaq4370_4_chip_info },
1307+
{ .compatible = "adi,adaq4380-4", .data = &adaq4380_4_chip_info },
11881308
{ }
11891309
};
11901310

@@ -1203,6 +1323,8 @@ static const struct spi_device_id ad7380_id_table[] = {
12031323
{ "ad7386-4", (kernel_ulong_t)&ad7386_4_chip_info },
12041324
{ "ad7387-4", (kernel_ulong_t)&ad7387_4_chip_info },
12051325
{ "ad7388-4", (kernel_ulong_t)&ad7388_4_chip_info },
1326+
{ "adaq4370-4", (kernel_ulong_t)&adaq4370_4_chip_info },
1327+
{ "adaq4380-4", (kernel_ulong_t)&adaq4380_4_chip_info },
12061328
{ }
12071329
};
12081330
MODULE_DEVICE_TABLE(spi, ad7380_id_table);

0 commit comments

Comments
 (0)