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/ina230.h>
12 #include <zephyr/ztest.h>
13 
14 #include <ina230_emul.h>
15 #include <ina230.h>
16 
17 enum ina23x_ids {
18 	INA230,
19 	INA236
20 };
21 
22 struct ina230_fixture {
23 	const struct device *dev;
24 	const struct emul *mock;
25 	const uint16_t current_lsb_uA;
26 	const uint16_t rshunt_uOhms;
27 	const uint16_t config;
28 	const enum ina23x_ids dev_type;
29 };
30 
31 /**
32  * @brief Verify devicetree default configuration is correct.
33  */
ZTEST(ina230_0,test_default_config)34 ZTEST(ina230_0, test_default_config)
35 {
36 	const struct ina230_config *config;
37 	const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(ina230_default_test));
38 
39 	zassert_not_null(dev);
40 	config = dev->config;
41 	zassert_not_null(config);
42 
43 	/* confirm default DT configuration */
44 	uint16_t expected = 0x0127;
45 
46 	zexpect_equal(expected, config->config, "0x%x != config (0x%x)",
47 		expected, config->config);
48 }
49 
test_datasheet_example(struct ina230_fixture * fixture)50 static void test_datasheet_example(struct ina230_fixture *fixture)
51 {
52 	struct sensor_value sensor_val;
53 	uint16_t raw_voltage, raw_current, raw_power;
54 	double actual;
55 
56 	/* only run test for datasheet example of 1mA current LSB and 2 mOhm shunt */
57 	if (fixture->current_lsb_uA != 1000 || fixture->rshunt_uOhms != 2000) {
58 		ztest_test_skip();
59 	}
60 
61 	if (fixture->dev_type == INA230) {
62 		raw_voltage = 9584;
63 		raw_current = 10000;
64 		raw_power = 4792;
65 	} else {
66 		raw_voltage = 7487;
67 		raw_current = 10000;
68 		raw_power = 3744;
69 	}
70 
71 	ina230_mock_set_register(fixture->mock->data, INA230_REG_BUS_VOLT, raw_voltage);
72 	ina230_mock_set_register(fixture->mock->data, INA230_REG_CURRENT, raw_current);
73 	ina230_mock_set_register(fixture->mock->data, INA230_REG_POWER, raw_power);
74 	zassert_ok(sensor_sample_fetch(fixture->dev));
75 
76 	zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_VOLTAGE, &sensor_val));
77 	actual = sensor_value_to_double(&sensor_val);
78 	zexpect_within(11.98, actual, 1.25e-3, "Expected 11.98 V, got %.6f V", actual);
79 
80 	zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_CURRENT, &sensor_val));
81 	actual = sensor_value_to_double(&sensor_val);
82 	zexpect_within(10.0, actual, 1e-3, "Expected 10 A, got %.6f V", actual);
83 
84 	zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_POWER, &sensor_val));
85 	actual = sensor_value_to_double(&sensor_val);
86 	zexpect_within(119.82, actual, 25e-3, "Expected 119.82 W, got %.6f W", actual);
87 }
88 
89 /**
90  * @brief Verify bus voltages for all ina230 nodes in DT
91  *
92  * @param fixture
93  */
test_shunt_cal(struct ina230_fixture * fixture)94 static void test_shunt_cal(struct ina230_fixture *fixture)
95 {
96 	/* Confirm SHUNT_CAL register which is 5120e-6 / Current_LSB * Rshunt */
97 	double shunt_cal = 5120e-6 / (fixture->current_lsb_uA * 1e-6 *
98 		fixture->rshunt_uOhms * 1e-6);
99 
100 	uint32_t shunt_register_actual;
101 	uint16_t shunt_register_expected = (uint16_t)shunt_cal;
102 
103 	zassert_ok(ina230_mock_get_register(fixture->mock->data, INA230_REG_CALIB,
104 		&shunt_register_actual));
105 	zexpect_within(shunt_register_expected, shunt_register_actual, 1,
106 		"Expected %d, got %d", shunt_register_expected, shunt_register_actual);
107 }
108 
test_current(struct ina230_fixture * fixture)109 static void test_current(struct ina230_fixture *fixture)
110 {
111 	/* 16-bit signed value for current register */
112 	const int16_t current_reg_vectors[] = {
113 		32767,
114 		1000,
115 		100,
116 		1,
117 		0,
118 		-1,
119 		-100,
120 		-1000,
121 		-32768,
122 	};
123 
124 	for (int idx = 0; idx < ARRAY_SIZE(current_reg_vectors); idx++) {
125 		struct sensor_value sensor_val;
126 		int16_t current_register = current_reg_vectors[idx];
127 		double current_expected_A = fixture->current_lsb_uA * 1e-6 * current_register;
128 
129 		/* set current reading */
130 		ina230_mock_set_register(fixture->mock->data, INA230_REG_CURRENT, current_register);
131 
132 		/* Verify sensor value is correct */
133 		zassert_ok(sensor_sample_fetch(fixture->dev));
134 		zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_CURRENT, &sensor_val));
135 		double current_actual_A = sensor_value_to_double(&sensor_val);
136 
137 		zexpect_within(current_expected_A, current_actual_A, fixture->current_lsb_uA*1e-6,
138 			"Expected %.6f A, got %.6f A", current_expected_A, current_actual_A);
139 	}
140 }
141 
test_bus_voltage(struct ina230_fixture * fixture)142 static void test_bus_voltage(struct ina230_fixture *fixture)
143 {
144 	zassert_not_null(fixture->mock);
145 
146 	/* 16-bit signed value for voltage register (but always positive) 1.25 mV/bit */
147 	const int16_t voltage_reg_vectors[] = {
148 		32767,
149 		28800,	/* 36V maximum voltage */
150 		1000,
151 		100,
152 		1,
153 		0,
154 	};
155 
156 	double bitres = fixture->dev_type == INA236 ? 1.6e-3 : 1.25e-3;
157 
158 	for (int idx = 0; idx < ARRAY_SIZE(voltage_reg_vectors); idx++) {
159 		struct sensor_value sensor_val;
160 
161 		ina230_mock_set_register(fixture->mock->data, INA230_REG_BUS_VOLT,
162 			voltage_reg_vectors[idx]);
163 
164 		/* Verify sensor value is correct */
165 		zassert_ok(sensor_sample_fetch(fixture->dev));
166 		zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_VOLTAGE, &sensor_val));
167 
168 		double voltage_actual_V = sensor_value_to_double(&sensor_val);
169 		double voltage_expected_V = voltage_reg_vectors[idx] * bitres;
170 
171 		zexpect_within(voltage_expected_V, voltage_actual_V, 1e-6,
172 			"Expected %.6f A, got %.6f A", voltage_expected_V, voltage_actual_V);
173 	}
174 }
175 
test_power(struct ina230_fixture * fixture)176 static void test_power(struct ina230_fixture *fixture)
177 {
178 	/* 16-bit unsigned value for power register */
179 	const uint16_t power_reg_vectors[] = {
180 		65535,
181 		32767,
182 		10000,
183 		1000,
184 		100,
185 		1,
186 		0,
187 	};
188 
189 	int scale = fixture->dev_type == INA236 ? 32 : 25;
190 
191 	for (int idx = 0; idx < ARRAY_SIZE(power_reg_vectors); idx++) {
192 		struct sensor_value sensor_val;
193 		uint32_t power_register = power_reg_vectors[idx];
194 
195 		/* power is power_register * SCALE * current_lsb */
196 		double power_expected_W = power_register * scale * fixture->current_lsb_uA * 1e-6;
197 
198 		/* set current reading */
199 		ina230_mock_set_register(fixture->mock->data, INA230_REG_POWER, power_register);
200 
201 		/* Verify sensor value is correct */
202 		zassert_ok(sensor_sample_fetch(fixture->dev));
203 		zassert_ok(sensor_channel_get(fixture->dev, SENSOR_CHAN_POWER, &sensor_val));
204 		double power_actual_W = sensor_value_to_double(&sensor_val);
205 
206 		zexpect_within(power_expected_W, power_actual_W, 1e-6,
207 			       "Expected %.6f W, got %.6f W for %d", power_expected_W,
208 			       power_actual_W, power_register);
209 	}
210 }
211 
212 /* Create a test fixture for each enabled ina230 device node */
213 #define INA230_FIXTURE_ENTRY(inst, v)                                                              \
214 	static struct ina230_fixture fixture_23##v##_##inst = {                                    \
215 		.dev = DEVICE_DT_INST_GET(inst),                                                   \
216 		.mock = EMUL_DT_GET(DT_DRV_INST(inst)),                                            \
217 		.current_lsb_uA = DT_INST_PROP(inst, current_lsb_microamps),                       \
218 		.rshunt_uOhms = DT_INST_PROP(inst, rshunt_micro_ohms),                             \
219 		.config = DT_INST_PROP(inst, config),                                              \
220 		.dev_type = INA23##v,                                                              \
221 	}
222 
223 /* Create a test suite for each enabled ina230 device node */
224 #define INA230_TESTS(inst, v)                                                                      \
225 	INA230_FIXTURE_ENTRY(inst, v);                                                             \
226 	ZTEST(ina23##v##_##inst, test_datasheet_example)                                           \
227 	{                                                                                          \
228 		test_datasheet_example(&fixture_23##v##_##inst);                                   \
229 	}                                                                                          \
230 	ZTEST(ina23##v##_##inst, test_shunt_cal)                                                   \
231 	{                                                                                          \
232 		test_shunt_cal(&fixture_23##v##_##inst);                                           \
233 	}                                                                                          \
234 	ZTEST(ina23##v##_##inst, test_current)                                                     \
235 	{                                                                                          \
236 		test_current(&fixture_23##v##_##inst);                                             \
237 	}                                                                                          \
238 	ZTEST(ina23##v##_##inst, test_bus_voltage)                                                 \
239 	{                                                                                          \
240 		test_bus_voltage(&fixture_23##v##_##inst);                                         \
241 	}                                                                                          \
242 	ZTEST(ina23##v##_##inst, test_power)                                                       \
243 	{                                                                                          \
244 		test_power(&fixture_23##v##_##inst);                                               \
245 	}                                                                                          \
246 	ZTEST_SUITE(ina23##v##_##inst, NULL, NULL, NULL, NULL, NULL);
247 
248 #undef DT_DRV_COMPAT
249 #define DT_DRV_COMPAT ti_ina230
250 DT_INST_FOREACH_STATUS_OKAY_VARGS(INA230_TESTS, 0)
251 
252 #undef DT_DRV_COMPAT
253 #define DT_DRV_COMPAT ti_ina236
254 DT_INST_FOREACH_STATUS_OKAY_VARGS(INA230_TESTS, 6)
255