1 /*
2 * Copyright (c) 2021 Leonard Pollak
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ti_ina219
8
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/pm/device.h>
15 #include <zephyr/sys/byteorder.h>
16
17 #include "ina219.h"
18
19 LOG_MODULE_REGISTER(INA219, CONFIG_SENSOR_LOG_LEVEL);
20
ina219_reg_read(const struct device * dev,uint8_t reg_addr,uint16_t * reg_data)21 static int ina219_reg_read(const struct device *dev,
22 uint8_t reg_addr,
23 uint16_t *reg_data)
24 {
25 const struct ina219_config *cfg = dev->config;
26 uint8_t rx_buf[2];
27 int rc;
28
29 rc = i2c_write_read_dt(&cfg->bus,
30 ®_addr, sizeof(reg_addr),
31 rx_buf, sizeof(rx_buf));
32
33 *reg_data = sys_get_be16(rx_buf);
34
35 return rc;
36 }
37
ina219_reg_write(const struct device * dev,uint8_t addr,uint16_t reg_data)38 static int ina219_reg_write(const struct device *dev,
39 uint8_t addr,
40 uint16_t reg_data)
41 {
42 const struct ina219_config *cfg = dev->config;
43 uint8_t tx_buf[3];
44
45 tx_buf[0] = addr;
46 sys_put_be16(reg_data, &tx_buf[1]);
47
48 return i2c_write_dt(&cfg->bus, tx_buf, sizeof(tx_buf));
49 }
50
ina219_reg_field_update(const struct device * dev,uint8_t addr,uint16_t mask,uint16_t field)51 static int ina219_reg_field_update(const struct device *dev,
52 uint8_t addr,
53 uint16_t mask,
54 uint16_t field)
55 {
56 uint16_t reg_data;
57 int rc;
58
59 rc = ina219_reg_read(dev, addr, ®_data);
60 if (rc) {
61 return rc;
62 }
63
64 reg_data = (reg_data & ~mask) | field;
65
66 return ina219_reg_write(dev, addr, reg_data);
67 }
68
ina219_set_msr_delay(const struct device * dev)69 static int ina219_set_msr_delay(const struct device *dev)
70 {
71 const struct ina219_config *cfg = dev->config;
72 struct ina219_data *data = dev->data;
73
74 data->msr_delay = ina219_conv_delay(cfg->badc) +
75 ina219_conv_delay(cfg->sadc);
76 return 0;
77 }
78
ina219_set_config(const struct device * dev)79 static int ina219_set_config(const struct device *dev)
80 {
81 const struct ina219_config *cfg = dev->config;
82 uint16_t reg_data;
83
84 reg_data = (cfg->brng & INA219_BRNG_MASK) << INA219_BRNG_SHIFT |
85 (cfg->pg & INA219_PG_MASK) << INA219_PG_SHIFT |
86 (cfg->badc & INA219_ADC_MASK) << INA219_BADC_SHIFT |
87 (cfg->sadc & INA219_ADC_MASK) << INA219_SADC_SHIFT |
88 (cfg->mode & INA219_MODE_NORMAL);
89
90 return ina219_reg_write(dev, INA219_REG_CONF, reg_data);
91 }
92
ina219_set_calib(const struct device * dev)93 static int ina219_set_calib(const struct device *dev)
94 {
95 const struct ina219_config *cfg = dev->config;
96 uint16_t cal;
97
98 cal = INA219_SCALING_FACTOR / ((cfg->r_shunt) * (cfg->current_lsb));
99
100 return ina219_reg_write(dev, INA219_REG_CALIB, cal);
101 }
102
ina219_sample_fetch(const struct device * dev,enum sensor_channel chan)103 static int ina219_sample_fetch(const struct device *dev,
104 enum sensor_channel chan)
105 {
106 struct ina219_data *data = dev->data;
107 uint16_t status;
108 uint16_t tmp;
109 int rc;
110
111 if (chan != SENSOR_CHAN_ALL &&
112 chan != SENSOR_CHAN_VOLTAGE &&
113 chan != SENSOR_CHAN_POWER &&
114 chan != SENSOR_CHAN_CURRENT) {
115 return -ENOTSUP;
116 }
117
118 /* Trigger measurement and wait for completion */
119 rc = ina219_reg_field_update(dev,
120 INA219_REG_CONF,
121 INA219_MODE_MASK,
122 INA219_MODE_NORMAL);
123 if (rc) {
124 LOG_ERR("Failed to start measurement.");
125 return rc;
126 }
127
128 k_sleep(K_USEC(data->msr_delay));
129
130 rc = ina219_reg_read(dev, INA219_REG_V_BUS, &status);
131 if (rc) {
132 LOG_ERR("Failed to read device status.");
133 return rc;
134 }
135
136 while (!(INA219_CNVR_RDY(status))) {
137 rc = ina219_reg_read(dev, INA219_REG_V_BUS, &status);
138 if (rc) {
139 LOG_ERR("Failed to read device status.");
140 return rc;
141 }
142 k_sleep(K_USEC(INA219_WAIT_MSR_RETRY));
143 }
144
145 /* Check for overflow */
146 if (INA219_OVF_STATUS(status)) {
147 LOG_WRN("Power and/or Current calculations are out of range.");
148 }
149
150 if (chan == SENSOR_CHAN_ALL ||
151 chan == SENSOR_CHAN_VOLTAGE) {
152
153 rc = ina219_reg_read(dev, INA219_REG_V_BUS, &tmp);
154 if (rc) {
155 LOG_ERR("Error reading bus voltage.");
156 return rc;
157 }
158 data->v_bus = INA219_VBUS_GET(tmp);
159 }
160
161 if (chan == SENSOR_CHAN_ALL ||
162 chan == SENSOR_CHAN_POWER) {
163
164 rc = ina219_reg_read(dev, INA219_REG_POWER, &tmp);
165 if (rc) {
166 LOG_ERR("Error reading power register.");
167 return rc;
168 }
169 data->power = tmp;
170 }
171
172 if (chan == SENSOR_CHAN_ALL ||
173 chan == SENSOR_CHAN_CURRENT) {
174
175 rc = ina219_reg_read(dev, INA219_REG_CURRENT, &tmp);
176 if (rc) {
177 LOG_ERR("Error reading current register.");
178 return rc;
179 }
180 data->current = tmp;
181 }
182
183 return rc;
184 }
185
ina219_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)186 static int ina219_channel_get(const struct device *dev,
187 enum sensor_channel chan,
188 struct sensor_value *val)
189 {
190 const struct ina219_config *cfg = dev->config;
191 struct ina219_data *data = dev->data;
192 double tmp;
193 int8_t sign = 1;
194
195 switch (chan) {
196 case SENSOR_CHAN_VOLTAGE:
197 tmp = data->v_bus * INA219_V_BUS_MUL;
198 break;
199 case SENSOR_CHAN_POWER:
200 tmp = data->power * cfg->current_lsb * INA219_POWER_MUL * INA219_SI_MUL;
201 break;
202 case SENSOR_CHAN_CURRENT:
203 if (INA219_SIGN_BIT(data->current)) {
204 data->current = ~data->current + 1;
205 sign = -1;
206 }
207 tmp = sign * data->current * cfg->current_lsb * INA219_SI_MUL;
208 break;
209 default:
210 LOG_DBG("Channel not supported by device!");
211 return -ENOTSUP;
212 }
213
214 return sensor_value_from_double(val, tmp);
215 }
216
217 #ifdef CONFIG_PM_DEVICE
ina219_pm_action(const struct device * dev,enum pm_device_action action)218 static int ina219_pm_action(const struct device *dev,
219 enum pm_device_action action)
220 {
221 uint16_t reg_val;
222
223 switch (action) {
224 case PM_DEVICE_ACTION_RESUME:
225 return ina219_init(dev);
226 case PM_DEVICE_ACTION_SUSPEND:
227 reg_val = INA219_MODE_SLEEP;
228 break;
229 case PM_DEVICE_ACTION_TURN_OFF:
230 reg_val = INA219_MODE_OFF;
231 break;
232 default:
233 return -ENOTSUP;
234 }
235
236 return ina219_reg_field_update(dev,
237 INA219_REG_CONF,
238 INA219_MODE_MASK,
239 reg_val);
240 }
241 #endif /* CONFIG_PM_DEVICE */
242
ina219_init(const struct device * dev)243 static int ina219_init(const struct device *dev)
244 {
245 const struct ina219_config *cfg = dev->config;
246 int rc;
247
248 if (!device_is_ready(cfg->bus.bus)) {
249 LOG_ERR("Device not ready.");
250 return -ENODEV;
251 }
252
253 rc = ina219_reg_write(dev, INA219_REG_CONF, INA219_RST);
254 if (rc) {
255 LOG_ERR("Could not reset device.");
256 return rc;
257 }
258
259 rc = ina219_set_config(dev);
260 if (rc) {
261 LOG_ERR("Could not set configuration data.");
262 return rc;
263 }
264
265 rc = ina219_set_calib(dev);
266 if (rc) {
267 LOG_DBG("Could not set calibration data.");
268 return rc;
269 }
270
271 /* Set measurement delay */
272 rc = ina219_set_msr_delay(dev);
273 if (rc) {
274 LOG_ERR("Could not get measurement delay.");
275 return rc;
276 }
277
278 k_sleep(K_USEC(INA219_WAIT_STARTUP));
279
280 return 0;
281 }
282
283 static const struct sensor_driver_api ina219_api = {
284 .sample_fetch = ina219_sample_fetch,
285 .channel_get = ina219_channel_get,
286 };
287
288
289 #define INA219_INIT(n) \
290 static struct ina219_data ina219_data_##n; \
291 \
292 static const struct ina219_config ina219_config_##n = { \
293 .bus = I2C_DT_SPEC_INST_GET(n), \
294 .current_lsb = DT_INST_PROP(n, lsb_microamp), \
295 .r_shunt = DT_INST_PROP(n, shunt_milliohm), \
296 .brng = DT_INST_PROP(n, brng), \
297 .pg = DT_INST_PROP(n, pg), \
298 .badc = DT_INST_PROP(n, badc), \
299 .sadc = DT_INST_PROP(n, sadc), \
300 .mode = INA219_MODE_NORMAL \
301 }; \
302 \
303 PM_DEVICE_DT_INST_DEFINE(n, ina219_pm_action); \
304 \
305 SENSOR_DEVICE_DT_INST_DEFINE(n, \
306 ina219_init, \
307 PM_DEVICE_DT_INST_GET(n), \
308 &ina219_data_##n, \
309 &ina219_config_##n, \
310 POST_KERNEL, \
311 CONFIG_SENSOR_INIT_PRIORITY, \
312 &ina219_api);
313
314 DT_INST_FOREACH_STATUS_OKAY(INA219_INIT)
315