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 			&reg_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, &reg_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