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