1 /*
2 * Copyright (c) 2023 North River Systems Ltd
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include <zephyr/device.h>
7 #include <zephyr/devicetree.h>
8 #include <zephyr/drivers/emul.h>
9 #include <zephyr/drivers/i2c_emul.h>
10 #include <zephyr/drivers/sensor.h>
11 #include <zephyr/dt-bindings/sensor/ina237.h>
12 #include <zephyr/ztest.h>
13
14 #include <ina237_emul.h>
15 #include <ina237.h>
16
17 struct ina237_fixture {
18 const struct device *dev;
19 const struct emul *mock;
20 const uint16_t current_lsb_uA;
21 const uint16_t rshunt_uOhms;
22 const uint16_t config;
23 };
24
25 /**
26 * @brief Verify devicetree default configuration is correct.
27 */
ZTEST(ina237_0,test_default_config)28 ZTEST(ina237_0, test_default_config)
29 {
30 const struct ina237_config *config;
31 const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(ina237_default_test));
32
33 zassert_not_null(dev);
34 config = dev->config;
35 zassert_not_null(config);
36
37 /* confirm default DT settings */
38 zexpect_equal(0xFB68, config->adc_config,
39 "0xFB68 != adc_config (0x%x)", config->adc_config);
40 zexpect_equal(0x0000, config->config);
41 }
42
43 /**
44 * @brief Verify bus voltages for all ina237 nodes in DT
45 *
46 * @param fixture
47 */
48
test_shunt_cal(struct ina237_fixture * fixture)49 static void test_shunt_cal(struct ina237_fixture *fixture)
50 {
51 /* Confirm SHUNT_CAL register which is 819.2e6 * Current_LSB * Rshunt */
52 double shunt_cal = 819.2e6 * fixture->current_lsb_uA * 1e-6 * fixture->rshunt_uOhms * 1e-6;
53
54 if (fixture->config & INA237_CFG_HIGH_PRECISION) {
55 /* High precision mode */
56 shunt_cal *= 4;
57 }
58
59 uint32_t shunt_register_actual;
60 uint16_t shunt_register_expected = (uint16_t)shunt_cal;
61
62 zassert_ok(ina237_mock_get_register(fixture->mock->data, INA237_REG_CALIB,
63 &shunt_register_actual));
64 zexpect_within(shunt_register_expected, shunt_register_actual, 1,
65 "Expected %d, got %d", shunt_register_expected, shunt_register_actual);
66 }
67
test_current(struct ina237_fixture * fixture)68 static void test_current(struct ina237_fixture *fixture)
69 {
70 /* 16-bit signed value for current register */
71 const int16_t current_reg_vectors[] = {
72 32767,
73 1000,
74 100,
75 1,
76 0,
77 -1,
78 -100,
79 -1000,
80 -32768,
81 };
82
83 for (int idx = 0; idx < ARRAY_SIZE(current_reg_vectors); idx++) {
84 struct sensor_value sensor_val;
85 int16_t current_register = current_reg_vectors[idx];
86 double current_expected_A = fixture->current_lsb_uA * 1e-6 * current_register;
87
88 /* set current reading */
89 ina237_mock_set_register(fixture->mock->data, INA237_REG_CURRENT, current_register);
90
91 /* Verify sensor value is correct */
92 zassert_ok(sensor_sample_fetch(fixture->dev));
93 zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_CURRENT, &sensor_val));
94 double current_actual_A = sensor_value_to_double(&sensor_val);
95
96 zexpect_within(current_expected_A, current_actual_A, fixture->current_lsb_uA*1e-6,
97 "Expected %.6f A, got %.6f A", current_expected_A, current_actual_A);
98 }
99 }
100
test_bus_voltage(struct ina237_fixture * fixture)101 static void test_bus_voltage(struct ina237_fixture *fixture)
102 {
103 zassert_not_null(fixture->mock);
104
105 /* 16-bit signed value for voltage register (but always positive) 3.125 mV/bit */
106 const int16_t voltage_reg_vectors[] = {
107 32767,
108 27200, /* 85V maximum voltage */
109 1000,
110 100,
111 1,
112 0,
113 };
114
115 for (int idx = 0; idx < ARRAY_SIZE(voltage_reg_vectors); idx++) {
116 struct sensor_value sensor_val;
117
118 ina237_mock_set_register(fixture->mock->data, INA237_REG_BUS_VOLT,
119 voltage_reg_vectors[idx]);
120
121 /* Verify sensor value is correct */
122 zassert_ok(sensor_sample_fetch(fixture->dev));
123 zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_VOLTAGE, &sensor_val));
124
125 double voltage_actual_V = sensor_value_to_double(&sensor_val);
126 double voltage_expected_V = voltage_reg_vectors[idx] * 3.125e-3;
127
128 zexpect_within(voltage_expected_V, voltage_actual_V, 1e-6,
129 "Expected %.6f A, got %.6f A", voltage_expected_V, voltage_actual_V);
130 }
131 }
132
test_power(struct ina237_fixture * fixture)133 static void test_power(struct ina237_fixture *fixture)
134 {
135 /* 24-bit unsigned value for power register */
136 const uint32_t power_reg_vectors[] = {
137 16777215,
138 65535,
139 32767,
140 1000,
141 100,
142 1,
143 0,
144 };
145
146 for (int idx = 0; idx < ARRAY_SIZE(power_reg_vectors); idx++) {
147 struct sensor_value sensor_val;
148 uint32_t power_register = power_reg_vectors[idx];
149
150 /* power is 0.2 * current_lsb * register */
151 double power_expected_W = 0.2 * fixture->current_lsb_uA * 1e-6 * power_register;
152
153 /* set current reading */
154 ina237_mock_set_register(fixture->mock->data, INA237_REG_POWER, power_register);
155
156 /* Verify sensor value is correct */
157 zassert_ok(sensor_sample_fetch(fixture->dev));
158 zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_POWER, &sensor_val));
159 double power_actual_W = sensor_value_to_double(&sensor_val);
160
161 zexpect_within(power_expected_W, power_actual_W, 1e-6,
162 "Expected %.6f C, got %.6f C", power_expected_W, power_actual_W);
163 }
164 }
165
test_temperature(struct ina237_fixture * fixture)166 static void test_temperature(struct ina237_fixture *fixture)
167 {
168 zassert_not_null(fixture->mock);
169
170 /* 12-bit signed value with bottom 4-bits reserved - 125 mDegC / bit */
171 const int16_t temp_reg_vectors[] = {
172 16000, /* 125C */
173 1000,
174 100,
175 1,
176 0,
177 -100,
178 -1000,
179 -5120, /* -40C */
180 };
181
182 for (int idx = 0; idx < ARRAY_SIZE(temp_reg_vectors); idx++) {
183 struct sensor_value sensor_val;
184
185 ina237_mock_set_register(fixture->mock->data, INA237_REG_DIETEMP,
186 temp_reg_vectors[idx]);
187
188 /* Verify sensor value is correct */
189 zassert_ok(sensor_sample_fetch(fixture->dev));
190 zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_DIE_TEMP, &sensor_val));
191
192 double temp_actual_degC = sensor_value_to_double(&sensor_val);
193 double temp_expected_degC = (temp_reg_vectors[idx] / 16) * 125e-3;
194
195 zexpect_within(temp_expected_degC, temp_actual_degC, 125e-3,
196 "Expected %.6f A, got %.6f A", temp_expected_degC, temp_actual_degC);
197 }
198 }
199
test_vshunt(struct ina237_fixture * fixture)200 static void test_vshunt(struct ina237_fixture *fixture)
201 {
202 zassert_not_null(fixture->mock);
203
204 /* 16-bit signed value for voltage register (but always positive) 3.125 mV/bit */
205 const int16_t vshunt_reg_vectors[] = {
206 32767, /* maximum shunt voltage of 163.84 mV or 40.96 mV */
207 27200,
208 1000,
209 100,
210 1,
211 0,
212 -1,
213 -100,
214 -1000,
215 -32768, /* minimum shunt voltage of -163.84 mV or -40.96 mV */
216 };
217
218 for (int idx = 0; idx < ARRAY_SIZE(vshunt_reg_vectors); idx++) {
219 struct sensor_value sensor_val;
220
221 ina237_mock_set_register(fixture->mock->data, INA237_REG_SHUNT_VOLT,
222 vshunt_reg_vectors[idx]);
223
224 /* Verify sensor value is correct */
225 zassert_ok(sensor_sample_fetch_chan(fixture->dev, SENSOR_CHAN_VSHUNT));
226 zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_VSHUNT, &sensor_val));
227
228 double vshunt_actual_mV = sensor_value_to_double(&sensor_val);
229 double vshunt_expected_mV = vshunt_reg_vectors[idx];
230
231 if (fixture->config & INA237_CFG_HIGH_PRECISION) {
232 /* High precision mode - 1.25 uV/bit = 1250 nV/bit*/
233 vshunt_expected_mV *= 1000 * 1.250e-6;
234 } else {
235 /* Standard precision mode - 5 uV/bit = 5000 nV/bit */
236 vshunt_expected_mV *= 1000 * 5e-6;
237 }
238
239 zexpect_within(vshunt_expected_mV, vshunt_actual_mV, 1e-9,
240 "For %d, Expected %.6f mV, got %.6f mV", vshunt_reg_vectors[idx],
241 vshunt_expected_mV, vshunt_actual_mV);
242 }
243 }
244
245 /* Create a test fixture for each enabled ina237 device node */
246 #define DT_DRV_COMPAT ti_ina237
247 #define INA237_FIXTURE_ENTRY(inst) \
248 { \
249 .dev = DEVICE_DT_INST_GET(inst), \
250 .mock = EMUL_DT_GET(DT_DRV_INST(inst)), \
251 .current_lsb_uA = DT_INST_PROP(inst, current_lsb_microamps), \
252 .rshunt_uOhms = DT_INST_PROP(inst, rshunt_micro_ohms), \
253 .config = DT_INST_PROP(inst, config), \
254 },
255
256 static struct ina237_fixture fixtures[] = {
257 DT_INST_FOREACH_STATUS_OKAY(INA237_FIXTURE_ENTRY)
258 };
259
260 /* Create a test suite for each enabled ina237 device node */
261 #define INA237_TESTS(inst) \
262 ZTEST(ina237_##inst, test_shunt_cal) { test_shunt_cal(&fixtures[inst]); } \
263 ZTEST(ina237_##inst, test_current) { test_current(&fixtures[inst]); } \
264 ZTEST(ina237_##inst, test_bus_voltage) { test_bus_voltage(&fixtures[inst]); } \
265 ZTEST(ina237_##inst, test_power) { test_power(&fixtures[inst]); } \
266 ZTEST(ina237_##inst, test_temperature) { test_temperature(&fixtures[inst]); } \
267 ZTEST(ina237_##inst, test_vshunt) { test_vshunt(&fixtures[inst]); } \
268 ZTEST_SUITE(ina237_##inst, NULL, NULL, NULL, NULL, NULL);
269
270 DT_INST_FOREACH_STATUS_OKAY(INA237_TESTS)
271