Skip to content

Commit c890a5b

Browse files
spectrum70gregkh
authored andcommitted
iio: dac: ad3552r: extract common code (no changes in behavior intended)
[ Upstream commit f665d7d ] Extracting common code, to share common code to be used later by the AXI driver version (ad3552r-axi.c). Signed-off-by: Angelo Dureghello <adureghello@baylibre.com> Reviewed-by: David Lechner <dlechner@baylibre.com> Link: https://patch.msgid.link/20241028-wip-bl-ad3552r-axi-v0-iio-testing-v9-6-f6960b4f9719@kernel-space.org Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 0a8ac8f commit c890a5b

File tree

4 files changed

+501
-372
lines changed

4 files changed

+501
-372
lines changed

drivers/iio/dac/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55

66
# When adding new entries keep the list in alphabetical order
7-
obj-$(CONFIG_AD3552R) += ad3552r.o
7+
obj-$(CONFIG_AD3552R) += ad3552r.o ad3552r-common.o
88
obj-$(CONFIG_AD5360) += ad5360.o
99
obj-$(CONFIG_AD5380) += ad5380.o
1010
obj-$(CONFIG_AD5421) += ad5421.o

drivers/iio/dac/ad3552r-common.c

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
//
3+
// Copyright (c) 2010-2024 Analog Devices Inc.
4+
// Copyright (c) 2024 Baylibre, SAS
5+
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>
11+
12+
#include "ad3552r.h"
13+
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 }
20+
};
21+
EXPORT_SYMBOL_NS_GPL(ad3552r_ch_ranges, IIO_AD3552R);
22+
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 }
30+
};
31+
EXPORT_SYMBOL_NS_GPL(ad3542r_ch_ranges, IIO_AD3552R);
32+
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
39+
};
40+
41+
u16 ad3552r_calc_custom_gain(u8 p, u8 n, s16 goffs)
42+
{
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);
48+
}
49+
EXPORT_SYMBOL_NS_GPL(ad3552r_calc_custom_gain, IIO_AD3552R);
50+
51+
static void ad3552r_get_custom_range(struct ad3552r_ch_data *ch_data,
52+
s32 *v_min, s32 *v_max)
53+
{
54+
s64 vref, tmp, common, offset, gn, gp;
55+
/*
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
60+
*/
61+
vref = 2500;
62+
/* 2.5 * 1.03 * 1000 (To mV) */
63+
common = 2575 * ch_data->rfb;
64+
offset = ch_data->gain_offset;
65+
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);
69+
*v_max = vref + tmp;
70+
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);
74+
*v_min = vref - tmp;
75+
}
76+
77+
void ad3552r_calc_gain_and_offset(struct ad3552r_ch_data *ch_data,
78+
const struct ad3552r_model_data *model_data)
79+
{
80+
s32 idx, v_max, v_min, span, rem;
81+
s64 tmp;
82+
83+
if (ch_data->range_override) {
84+
ad3552r_get_custom_range(ch_data, &v_min, &v_max);
85+
} else {
86+
/* Normal range */
87+
idx = ch_data->range;
88+
v_min = model_data->ranges_table[idx][0];
89+
v_max = model_data->ranges_table[idx][1];
90+
}
91+
92+
/*
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
98+
*
99+
* Reminders are in micros in order to be printed as
100+
* IIO_VAL_INT_PLUS_MICRO
101+
*/
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);
106+
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);
110+
}
111+
EXPORT_SYMBOL_NS_GPL(ad3552r_calc_gain_and_offset, IIO_AD3552R);
112+
113+
int ad3552r_get_ref_voltage(struct device *dev, u32 *val)
114+
{
115+
int voltage;
116+
int delta = 100000;
117+
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");
122+
123+
if (voltage == -ENODEV) {
124+
if (device_property_read_bool(dev, "adi,vref-out-en"))
125+
*val = AD3552R_INTERNAL_VREF_PIN_2P5V;
126+
else
127+
*val = AD3552R_INTERNAL_VREF_PIN_FLOATING;
128+
129+
return 0;
130+
}
131+
132+
if (voltage > 2500000 + delta || voltage < 2500000 - delta) {
133+
dev_warn(dev, "vref-supply must be 2.5V");
134+
return -EINVAL;
135+
}
136+
137+
*val = AD3552R_EXTERNAL_VREF_PIN_INPUT;
138+
139+
return 0;
140+
}
141+
EXPORT_SYMBOL_NS_GPL(ad3552r_get_ref_voltage, IIO_AD3552R);
142+
143+
int ad3552r_get_drive_strength(struct device *dev, u32 *val)
144+
{
145+
int err;
146+
u32 drive_strength;
147+
148+
err = device_property_read_u32(dev, "adi,sdo-drive-strength",
149+
&drive_strength);
150+
if (err)
151+
return err;
152+
153+
if (drive_strength > 3) {
154+
dev_err_probe(dev, -EINVAL,
155+
"adi,sdo-drive-strength must be less than 4\n");
156+
return -EINVAL;
157+
}
158+
159+
*val = drive_strength;
160+
161+
return 0;
162+
}
163+
EXPORT_SYMBOL_NS_GPL(ad3552r_get_drive_strength, IIO_AD3552R);
164+
165+
int ad3552r_get_custom_gain(struct device *dev, struct fwnode_handle *child,
166+
u8 *gs_p, u8 *gs_n, u16 *rfb, s16 *goffs)
167+
{
168+
int err;
169+
u32 val;
170+
struct fwnode_handle *gain_child __free(fwnode_handle) =
171+
fwnode_get_named_child_node(child,
172+
"custom-output-range-config");
173+
174+
if (!gain_child)
175+
return dev_err_probe(dev, -EINVAL,
176+
"custom-output-range-config mandatory\n");
177+
178+
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
179+
if (err)
180+
return dev_err_probe(dev, err,
181+
"adi,gain-scaling-p mandatory\n");
182+
*gs_p = val;
183+
184+
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
185+
if (err)
186+
return dev_err_probe(dev, err,
187+
"adi,gain-scaling-n property mandatory\n");
188+
*gs_n = val;
189+
190+
err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
191+
if (err)
192+
return dev_err_probe(dev, err,
193+
"adi,rfb-ohms mandatory\n");
194+
*rfb = val;
195+
196+
err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val);
197+
if (err)
198+
return dev_err_probe(dev, err,
199+
"adi,gain-offset mandatory\n");
200+
*goffs = val;
201+
202+
return 0;
203+
}
204+
EXPORT_SYMBOL_NS_GPL(ad3552r_get_custom_gain, IIO_AD3552R);
205+
206+
static int ad3552r_find_range(const struct ad3552r_model_data *model_info,
207+
s32 *vals)
208+
{
209+
int i;
210+
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)
214+
return i;
215+
216+
return -EINVAL;
217+
}
218+
219+
int ad3552r_get_output_range(struct device *dev,
220+
const struct ad3552r_model_data *model_info,
221+
struct fwnode_handle *child, u32 *val)
222+
{
223+
int ret;
224+
s32 vals[2];
225+
226+
/* This property is optional, so returning -ENOENT if missing */
227+
if (!fwnode_property_present(child, "adi,output-range-microvolt"))
228+
return -ENOENT;
229+
230+
ret = fwnode_property_read_u32_array(child,
231+
"adi,output-range-microvolt",
232+
vals, 2);
233+
if (ret)
234+
return dev_err_probe(dev, ret,
235+
"invalid adi,output-range-microvolt\n");
236+
237+
ret = ad3552r_find_range(model_info, vals);
238+
if (ret < 0)
239+
return dev_err_probe(dev, ret,
240+
"invalid adi,output-range-microvolt value\n");
241+
242+
*val = ret;
243+
244+
return 0;
245+
}
246+
EXPORT_SYMBOL_NS_GPL(ad3552r_get_output_range, IIO_AD3552R);
247+
248+
MODULE_DESCRIPTION("ad3552r common functions");
249+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)