1 /*
2 * Copyright 2024 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /* TODO: Add functionality for Trigger. */
8
9 #define DT_DRV_COMPAT ti_ina226
10
11 #include <zephyr/logging/log.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/dt-bindings/sensor/ina226.h>
14 #include <zephyr/sys/byteorder.h>
15
16 #include <zephyr/sys/util_macro.h>
17 #include <stdint.h>
18 #include <zephyr/device.h>
19 #include <zephyr/drivers/gpio.h>
20 #include <zephyr/drivers/i2c.h>
21
22 /* Device register addresses. */
23 #define INA226_REG_CONFIG 0x00
24 #define INA226_REG_SHUNT_VOLT 0x01
25 #define INA226_REG_BUS_VOLT 0x02
26 #define INA226_REG_POWER 0x03
27 #define INA226_REG_CURRENT 0x04
28 #define INA226_REG_CALIB 0x05
29 #define INA226_REG_MASK 0x06
30 #define INA226_REG_ALERT 0x07
31 #define INA226_REG_MANUFACTURER_ID 0xFE
32 #define INA226_REG_DEVICE_ID 0xFF
33
34 /* Device register values. */
35 #define INA226_MANUFACTURER_ID 0x5449
36 #define INA226_DEVICE_ID 0x2260
37
38 struct ina226_data {
39 const struct device *dev;
40 int16_t current;
41 uint16_t bus_voltage;
42 uint16_t power;
43 #ifdef CONFIG_INA226_VSHUNT
44 int16_t shunt_voltage;
45 #endif
46 enum sensor_channel chan;
47 };
48
49 struct ina226_config {
50 const struct i2c_dt_spec bus;
51 uint16_t config;
52 uint32_t current_lsb;
53 uint16_t cal;
54 };
55
56 LOG_MODULE_REGISTER(INA226, CONFIG_SENSOR_LOG_LEVEL);
57
58 /** @brief Calibration scaling value (scaled by 10^-5) */
59 #define INA226_CAL_SCALING 512ULL
60
61 /** @brief The LSB value for the bus voltage register, in microvolts/LSB. */
62 #define INA226_BUS_VOLTAGE_TO_uV(x) ((x) * 1250U)
63
64 /** @brief The LSB value for the shunt voltage register, in microvolts/LSB. */
65 #define INA226_SHUNT_VOLTAGE_TO_uV(x) ((x) * 2500U / 1000U)
66
67 /** @brief Power scaling (need factor of 0.2) */
68 #define INA226_POWER_TO_uW(x) ((x) * 25ULL)
69
70
ina226_reg_read_16(const struct i2c_dt_spec * bus,uint8_t reg,uint16_t * val)71 int ina226_reg_read_16(const struct i2c_dt_spec *bus, uint8_t reg, uint16_t *val)
72 {
73 uint8_t data[2];
74 int ret;
75
76 ret = i2c_burst_read_dt(bus, reg, data, sizeof(data));
77 if (ret < 0) {
78 return ret;
79 }
80
81 *val = sys_get_be16(data);
82
83 return ret;
84 }
85
ina226_reg_write(const struct i2c_dt_spec * bus,uint8_t reg,uint16_t val)86 int ina226_reg_write(const struct i2c_dt_spec *bus, uint8_t reg, uint16_t val)
87 {
88 uint8_t tx_buf[3];
89
90 tx_buf[0] = reg;
91 sys_put_be16(val, &tx_buf[1]);
92
93 return i2c_write_dt(bus, tx_buf, sizeof(tx_buf));
94 }
95
micro_s32_to_sensor_value(struct sensor_value * val,int32_t value_microX)96 static void micro_s32_to_sensor_value(struct sensor_value *val, int32_t value_microX)
97 {
98 val->val1 = value_microX / 1000000L;
99 val->val2 = value_microX % 1000000L;
100 }
101
ina226_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)102 static int ina226_channel_get(const struct device *dev, enum sensor_channel chan,
103 struct sensor_value *val)
104 {
105 const struct ina226_data *data = dev->data;
106 const struct ina226_config *config = dev->config;
107
108 switch (chan) {
109 case SENSOR_CHAN_VOLTAGE:
110 micro_s32_to_sensor_value(val, INA226_BUS_VOLTAGE_TO_uV(data->bus_voltage));
111 break;
112 case SENSOR_CHAN_CURRENT:
113 /* see datasheet "Current and Power calculations" section */
114 micro_s32_to_sensor_value(val, data->current * config->current_lsb);
115 break;
116 case SENSOR_CHAN_POWER:
117 /* power in uW is power_reg * current_lsb * 0.2 */
118 micro_s32_to_sensor_value(val,
119 INA226_POWER_TO_uW((uint32_t)data->power * config->current_lsb));
120 break;
121 #ifdef CONFIG_INA226_VSHUNT
122 case SENSOR_CHAN_VSHUNT:
123 micro_s32_to_sensor_value(val, INA226_SHUNT_VOLTAGE_TO_uV(data->shunt_voltage));
124 break;
125 #endif /* CONFIG_INA226_VSHUNT */
126 default:
127 return -ENOTSUP;
128 }
129
130 return 0;
131 }
132
ina226_read_data(const struct device * dev)133 static int ina226_read_data(const struct device *dev)
134 {
135 struct ina226_data *data = dev->data;
136 const struct ina226_config *config = dev->config;
137 int ret;
138
139 if ((data->chan == SENSOR_CHAN_ALL) || (data->chan == SENSOR_CHAN_VOLTAGE)) {
140 ret = ina226_reg_read_16(&config->bus, INA226_REG_BUS_VOLT, &data->bus_voltage);
141 if (ret < 0) {
142 LOG_ERR("Failed to read bus voltage");
143 return ret;
144 }
145 }
146
147 if ((data->chan == SENSOR_CHAN_ALL) || (data->chan == SENSOR_CHAN_CURRENT)) {
148 ret = ina226_reg_read_16(&config->bus, INA226_REG_CURRENT, &data->current);
149 if (ret < 0) {
150 LOG_ERR("Failed to read current");
151 return ret;
152 }
153 }
154
155 if ((data->chan == SENSOR_CHAN_ALL) || (data->chan == SENSOR_CHAN_POWER)) {
156 ret = ina226_reg_read_16(&config->bus, INA226_REG_POWER, &data->power);
157 if (ret < 0) {
158 LOG_ERR("Failed to read power");
159 return ret;
160 }
161 }
162
163 #ifdef CONFIG_INA226_VSHUNT
164 if ((data->chan == SENSOR_CHAN_ALL) || (data->chan == SENSOR_CHAN_VSHUNT)) {
165 ret = ina226_reg_read_16(&config->bus, INA226_REG_SHUNT_VOLT, &data->shunt_voltage);
166 if (ret < 0) {
167 LOG_ERR("Failed to read shunt voltage");
168 return ret;
169 }
170 }
171 #endif /* CONFIG_INA226_VSHUNT */
172
173 return 0;
174 }
175
ina226_sample_fetch(const struct device * dev,enum sensor_channel chan)176 static int ina226_sample_fetch(const struct device *dev, enum sensor_channel chan)
177 {
178 struct ina226_data *data = dev->data;
179
180 if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_VOLTAGE
181 && chan != SENSOR_CHAN_CURRENT && chan != SENSOR_CHAN_POWER
182 #ifdef CONFIG_INA226_VSHUNT
183 && chan != SENSOR_CHAN_VSHUNT
184 #endif /* CONFIG_INA226_VSHUNT */
185 ) {
186 return -ENOTSUP;
187 }
188
189 data->chan = chan;
190
191 return ina226_read_data(dev);
192 }
193
ina226_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)194 static int ina226_attr_set(const struct device *dev, enum sensor_channel chan,
195 enum sensor_attribute attr, const struct sensor_value *val)
196 {
197 const struct ina226_config *config = dev->config;
198 uint16_t data = val->val1;
199
200 switch (attr) {
201 case SENSOR_ATTR_CONFIGURATION:
202 return ina226_reg_write(&config->bus, INA226_REG_CONFIG, data);
203 case SENSOR_ATTR_CALIBRATION:
204 return ina226_reg_write(&config->bus, INA226_REG_CALIB, data);
205 default:
206 LOG_ERR("INA226 attribute not supported.");
207 return -ENOTSUP;
208 }
209 }
210
ina226_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)211 static int ina226_attr_get(const struct device *dev, enum sensor_channel chan,
212 enum sensor_attribute attr, struct sensor_value *val)
213 {
214 const struct ina226_config *config = dev->config;
215 uint16_t data;
216 int ret;
217
218 switch (attr) {
219 case SENSOR_ATTR_CONFIGURATION:
220 ret = ina226_reg_read_16(&config->bus, INA226_REG_CONFIG, &data);
221 if (ret < 0) {
222 return ret;
223 }
224 break;
225 case SENSOR_ATTR_CALIBRATION:
226 ret = ina226_reg_read_16(&config->bus, INA226_REG_CALIB, &data);
227 if (ret < 0) {
228 return ret;
229 }
230 break;
231 default:
232 LOG_ERR("INA226 attribute not supported.");
233 return -ENOTSUP;
234 }
235
236 val->val1 = data;
237 val->val2 = 0;
238
239 return 0;
240 }
241
ina226_calibrate(const struct device * dev)242 static int ina226_calibrate(const struct device *dev)
243 {
244 const struct ina226_config *config = dev->config;
245 int ret;
246
247 ret = ina226_reg_write(&config->bus, INA226_REG_CALIB, config->cal);
248 if (ret < 0) {
249 return ret;
250 }
251
252 return 0;
253 }
254
ina226_init(const struct device * dev)255 static int ina226_init(const struct device *dev)
256 {
257 struct ina226_data *data = dev->data;
258 const struct ina226_config *config = dev->config;
259 uint16_t id;
260 int ret;
261
262 if (!i2c_is_ready_dt(&config->bus)) {
263 LOG_ERR("I2C bus %s is not ready", config->bus.bus->name);
264 return -ENODEV;
265 }
266
267 data->dev = dev;
268
269 ret = ina226_reg_read_16(&config->bus, INA226_REG_MANUFACTURER_ID, &id);
270 if (ret < 0) {
271 LOG_ERR("Failed to read manufacturer register.");
272 return ret;
273 }
274
275 if (id != INA226_MANUFACTURER_ID) {
276 LOG_ERR("Manufacturer ID doesn't match.");
277 return -ENODEV;
278 }
279
280 ret = ina226_reg_read_16(&config->bus, INA226_REG_DEVICE_ID, &id);
281 if (ret < 0) {
282 LOG_ERR("Failed to read device register.");
283 return ret;
284 }
285
286 if (id != INA226_DEVICE_ID) {
287 LOG_ERR("Device ID doesn't match.");
288 return -ENODEV;
289 }
290
291 ret = ina226_reg_write(&config->bus, INA226_REG_CONFIG, config->config);
292 if (ret < 0) {
293 LOG_ERR("Failed to write configuration register.");
294 return ret;
295 }
296
297 ret = ina226_calibrate(dev);
298 if (ret < 0) {
299 LOG_ERR("Failed to write calibration register.");
300 return ret;
301 }
302
303 return 0;
304 }
305
306 static DEVICE_API(sensor, ina226_driver_api) = {
307 .attr_set = ina226_attr_set,
308 .attr_get = ina226_attr_get,
309 .sample_fetch = ina226_sample_fetch,
310 .channel_get = ina226_channel_get,
311 };
312
313 #define INA226_DRIVER_INIT(inst) \
314 static struct ina226_data ina226_data_##inst; \
315 static const struct ina226_config ina226_config_##inst = { \
316 .bus = I2C_DT_SPEC_INST_GET(inst), \
317 .current_lsb = DT_INST_PROP(inst, current_lsb_microamps), \
318 .cal = INA226_CAL_SCALING * 10000000ULL / \
319 (DT_INST_PROP(inst, current_lsb_microamps) * \
320 DT_INST_PROP(inst, rshunt_micro_ohms)), \
321 .config = (DT_INST_ENUM_IDX(inst, avg_count) << 9) | \
322 (DT_INST_ENUM_IDX(inst, vbus_conversion_time_us) << 6) | \
323 (DT_INST_ENUM_IDX(inst, vshunt_conversion_time_us) << 3) | \
324 DT_INST_ENUM_IDX(inst, operating_mode), \
325 }; \
326 SENSOR_DEVICE_DT_INST_DEFINE(inst, \
327 &ina226_init, \
328 NULL, \
329 &ina226_data_##inst, \
330 &ina226_config_##inst, \
331 POST_KERNEL, \
332 CONFIG_SENSOR_INIT_PRIORITY, \
333 &ina226_driver_api);
334
335 DT_INST_FOREACH_STATUS_OKAY(INA226_DRIVER_INIT)
336