1 // SPDX-License-Identifier: GPL-2.0+
3 // Copyright (c) 2010-2024 Analog Devices Inc.
4 // Copyright (c) 2024 Baylibre, SAS
6 #include <linux/bitfield.h>
7 #include <linux/device.h>
8 #include <linux/module.h>
9 #include <linux/property.h>
10 #include <linux/regulator/consumer.h>
14 const s32 ad3552r_ch_ranges
[AD3552R_MAX_RANGES
][2] = {
15 [AD3552R_CH_OUTPUT_RANGE_0__2P5V
] = { 0, 2500 },
16 [AD3552R_CH_OUTPUT_RANGE_0__5V
] = { 0, 5000 },
17 [AD3552R_CH_OUTPUT_RANGE_0__10V
] = { 0, 10000 },
18 [AD3552R_CH_OUTPUT_RANGE_NEG_5__5V
] = { -5000, 5000 },
19 [AD3552R_CH_OUTPUT_RANGE_NEG_10__10V
] = { -10000, 10000 }
21 EXPORT_SYMBOL_NS_GPL(ad3552r_ch_ranges
, "IIO_AD3552R");
23 const s32 ad3542r_ch_ranges
[AD3542R_MAX_RANGES
][2] = {
24 [AD3542R_CH_OUTPUT_RANGE_0__2P5V
] = { 0, 2500 },
25 [AD3542R_CH_OUTPUT_RANGE_0__3V
] = { 0, 3000 },
26 [AD3542R_CH_OUTPUT_RANGE_0__5V
] = { 0, 5000 },
27 [AD3542R_CH_OUTPUT_RANGE_0__10V
] = { 0, 10000 },
28 [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V
] = { -2500, 7500 },
29 [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V
] = { -5000, 5000 }
31 EXPORT_SYMBOL_NS_GPL(ad3542r_ch_ranges
, "IIO_AD3552R");
33 /* Gain * AD3552R_GAIN_SCALE */
34 static const s32 gains_scaling_table
[] = {
35 [AD3552R_CH_GAIN_SCALING_1
] = 1000,
36 [AD3552R_CH_GAIN_SCALING_0_5
] = 500,
37 [AD3552R_CH_GAIN_SCALING_0_25
] = 250,
38 [AD3552R_CH_GAIN_SCALING_0_125
] = 125
41 u16
ad3552r_calc_custom_gain(u8 p
, u8 n
, s16 goffs
)
43 return FIELD_PREP(AD3552R_MASK_CH_RANGE_OVERRIDE
, 1) |
44 FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_P
, p
) |
45 FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_N
, n
) |
46 FIELD_PREP(AD3552R_MASK_CH_OFFSET_BIT_8
, abs(goffs
)) |
47 FIELD_PREP(AD3552R_MASK_CH_OFFSET_POLARITY
, goffs
< 0);
49 EXPORT_SYMBOL_NS_GPL(ad3552r_calc_custom_gain
, "IIO_AD3552R");
51 static void ad3552r_get_custom_range(struct ad3552r_ch_data
*ch_data
,
52 s32
*v_min
, s32
*v_max
)
54 s64 vref
, tmp
, common
, offset
, gn
, gp
;
56 * From datasheet formula (In Volts):
57 * Vmin = 2.5 + [(GainN + Offset / 1024) * 2.5 * Rfb * 1.03]
58 * Vmax = 2.5 - [(GainP + Offset / 1024) * 2.5 * Rfb * 1.03]
59 * Calculus are converted to milivolts
62 /* 2.5 * 1.03 * 1000 (To mV) */
63 common
= 2575 * ch_data
->rfb
;
64 offset
= ch_data
->gain_offset
;
66 gn
= gains_scaling_table
[ch_data
->n
];
67 tmp
= (1024 * gn
+ AD3552R_GAIN_SCALE
* offset
) * common
;
68 tmp
= div_s64(tmp
, 1024 * AD3552R_GAIN_SCALE
);
71 gp
= gains_scaling_table
[ch_data
->p
];
72 tmp
= (1024 * gp
- AD3552R_GAIN_SCALE
* offset
) * common
;
73 tmp
= div_s64(tmp
, 1024 * AD3552R_GAIN_SCALE
);
77 void ad3552r_calc_gain_and_offset(struct ad3552r_ch_data
*ch_data
,
78 const struct ad3552r_model_data
*model_data
)
80 s32 idx
, v_max
, v_min
, span
, rem
;
83 if (ch_data
->range_override
) {
84 ad3552r_get_custom_range(ch_data
, &v_min
, &v_max
);
88 v_min
= model_data
->ranges_table
[idx
][0];
89 v_max
= model_data
->ranges_table
[idx
][1];
93 * From datasheet formula:
94 * Vout = Span * (D / 65536) + Vmin
95 * Converted to scale and offset:
96 * Scale = Span / 65536
97 * Offset = 65536 * Vmin / Span
99 * Reminders are in micros in order to be printed as
100 * IIO_VAL_INT_PLUS_MICRO
102 span
= v_max
- v_min
;
103 ch_data
->scale_int
= div_s64_rem(span
, 65536, &rem
);
104 /* Do operations in microvolts */
105 ch_data
->scale_dec
= DIV_ROUND_CLOSEST((s64
)rem
* 1000000, 65536);
107 ch_data
->offset_int
= div_s64_rem(v_min
* 65536, span
, &rem
);
108 tmp
= (s64
)rem
* 1000000;
109 ch_data
->offset_dec
= div_s64(tmp
, span
);
111 EXPORT_SYMBOL_NS_GPL(ad3552r_calc_gain_and_offset
, "IIO_AD3552R");
113 int ad3552r_get_ref_voltage(struct device
*dev
, u32
*val
)
118 voltage
= devm_regulator_get_enable_read_voltage(dev
, "vref");
119 if (voltage
< 0 && voltage
!= -ENODEV
)
120 return dev_err_probe(dev
, voltage
,
121 "Error getting vref voltage\n");
123 if (voltage
== -ENODEV
) {
124 if (device_property_read_bool(dev
, "adi,vref-out-en"))
125 *val
= AD3552R_INTERNAL_VREF_PIN_2P5V
;
127 *val
= AD3552R_INTERNAL_VREF_PIN_FLOATING
;
132 if (voltage
> 2500000 + delta
|| voltage
< 2500000 - delta
) {
133 dev_warn(dev
, "vref-supply must be 2.5V");
137 *val
= AD3552R_EXTERNAL_VREF_PIN_INPUT
;
141 EXPORT_SYMBOL_NS_GPL(ad3552r_get_ref_voltage
, "IIO_AD3552R");
143 int ad3552r_get_drive_strength(struct device
*dev
, u32
*val
)
148 err
= device_property_read_u32(dev
, "adi,sdo-drive-strength",
153 if (drive_strength
> 3) {
154 dev_err_probe(dev
, -EINVAL
,
155 "adi,sdo-drive-strength must be less than 4\n");
159 *val
= drive_strength
;
163 EXPORT_SYMBOL_NS_GPL(ad3552r_get_drive_strength
, "IIO_AD3552R");
165 int ad3552r_get_custom_gain(struct device
*dev
, struct fwnode_handle
*child
,
166 u8
*gs_p
, u8
*gs_n
, u16
*rfb
, s16
*goffs
)
170 struct fwnode_handle
*gain_child
__free(fwnode_handle
) =
171 fwnode_get_named_child_node(child
,
172 "custom-output-range-config");
175 return dev_err_probe(dev
, -EINVAL
,
176 "custom-output-range-config mandatory\n");
178 err
= fwnode_property_read_u32(gain_child
, "adi,gain-scaling-p", &val
);
180 return dev_err_probe(dev
, err
,
181 "adi,gain-scaling-p mandatory\n");
184 err
= fwnode_property_read_u32(gain_child
, "adi,gain-scaling-n", &val
);
186 return dev_err_probe(dev
, err
,
187 "adi,gain-scaling-n property mandatory\n");
190 err
= fwnode_property_read_u32(gain_child
, "adi,rfb-ohms", &val
);
192 return dev_err_probe(dev
, err
,
193 "adi,rfb-ohms mandatory\n");
196 err
= fwnode_property_read_u32(gain_child
, "adi,gain-offset", &val
);
198 return dev_err_probe(dev
, err
,
199 "adi,gain-offset mandatory\n");
204 EXPORT_SYMBOL_NS_GPL(ad3552r_get_custom_gain
, "IIO_AD3552R");
206 static int ad3552r_find_range(const struct ad3552r_model_data
*model_info
,
211 for (i
= 0; i
< model_info
->num_ranges
; i
++)
212 if (vals
[0] == model_info
->ranges_table
[i
][0] * 1000 &&
213 vals
[1] == model_info
->ranges_table
[i
][1] * 1000)
219 int ad3552r_get_output_range(struct device
*dev
,
220 const struct ad3552r_model_data
*model_info
,
221 struct fwnode_handle
*child
, u32
*val
)
226 /* This property is optional, so returning -ENOENT if missing */
227 if (!fwnode_property_present(child
, "adi,output-range-microvolt"))
230 ret
= fwnode_property_read_u32_array(child
,
231 "adi,output-range-microvolt",
234 return dev_err_probe(dev
, ret
,
235 "invalid adi,output-range-microvolt\n");
237 ret
= ad3552r_find_range(model_info
, vals
);
239 return dev_err_probe(dev
, ret
,
240 "invalid adi,output-range-microvolt value\n");
246 EXPORT_SYMBOL_NS_GPL(ad3552r_get_output_range
, "IIO_AD3552R");
248 MODULE_DESCRIPTION("ad3552r common functions");
249 MODULE_LICENSE("GPL");