1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_ina3221
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 "ina3221.h"
18 
19 LOG_MODULE_REGISTER(INA3221, CONFIG_SENSOR_LOG_LEVEL);
20 
21 #define MAX_RETRIES 10
22 
reg_read(const struct device * dev,uint8_t reg_addr,uint16_t * reg_data)23 static int reg_read(const struct device *dev, uint8_t reg_addr, uint16_t *reg_data)
24 {
25 	const struct ina3221_config *cfg = dev->config;
26 	uint8_t rx_buf[2];
27 	int rc;
28 
29 	rc = i2c_write_read_dt(&cfg->bus, &reg_addr, sizeof(reg_addr), rx_buf, sizeof(rx_buf));
30 
31 	*reg_data = sys_get_be16(rx_buf);
32 
33 	return rc;
34 }
35 
reg_write(const struct device * dev,uint8_t addr,uint16_t reg_data)36 static int reg_write(const struct device *dev, uint8_t addr, uint16_t reg_data)
37 {
38 	const struct ina3221_config *cfg = dev->config;
39 	uint8_t tx_buf[3];
40 
41 	tx_buf[0] = addr;
42 	sys_put_be16(reg_data, &tx_buf[1]);
43 
44 	return i2c_write_dt(&cfg->bus, tx_buf, sizeof(tx_buf));
45 }
46 
ina3221_init(const struct device * dev)47 static int ina3221_init(const struct device *dev)
48 {
49 	int ret;
50 	const struct ina3221_config *cfg = dev->config;
51 	struct ina3221_data *data = dev->data;
52 	uint16_t reg_data;
53 
54 	__ASSERT_NO_MSG(cfg->avg_mode < 8);
55 	__ASSERT_NO_MSG(cfg->conv_time_bus < 8);
56 	__ASSERT_NO_MSG(cfg->conv_time_shunt < 8);
57 
58 	/* select first enabled channel */
59 	for (size_t i = 0; i < 3; ++i) {
60 		if (cfg->enable_channel[i]) {
61 			data->selected_channel = i;
62 			break;
63 		}
64 	}
65 
66 	/* check bus */
67 	if (!i2c_is_ready_dt(&cfg->bus)) {
68 		LOG_ERR("Device not ready.");
69 		return -ENODEV;
70 	}
71 
72 	/* check connection */
73 	ret = reg_read(dev, INA3221_MANUF_ID, &reg_data);
74 	if (ret) {
75 		LOG_ERR("No answer from sensor.");
76 		return ret;
77 	}
78 	if (reg_data != INA3221_MANUF_ID_VALUE) {
79 		LOG_ERR("Unexpected manufacturer ID: 0x%04x", reg_data);
80 		return -EFAULT;
81 	}
82 	ret = reg_read(dev, INA3221_CHIP_ID, &reg_data);
83 	if (ret) {
84 		return ret;
85 	}
86 	if (reg_data != INA3221_CHIP_ID_VALUE) {
87 		LOG_ERR("Unexpected chip ID: 0x%04x", reg_data);
88 		return -EFAULT;
89 	}
90 
91 	/* reset */
92 	ret = reg_read(dev, INA3221_CONFIG, &reg_data);
93 	if (ret) {
94 		return ret;
95 	}
96 	reg_data |= INA3221_CONFIG_RST;
97 	ret = reg_write(dev, INA3221_CONFIG, reg_data);
98 	if (ret) {
99 		return ret;
100 	}
101 
102 	/* configure */
103 	reg_data = (cfg->conv_time_shunt << 3) | (cfg->conv_time_bus << 6) | (cfg->avg_mode << 9) |
104 		   (INA3221_CONFIG_CH1 * cfg->enable_channel[0]) |
105 		   (INA3221_CONFIG_CH2 * cfg->enable_channel[1]) |
106 		   (INA3221_CONFIG_CH3 * cfg->enable_channel[2]);
107 
108 	ret = reg_write(dev, INA3221_CONFIG, reg_data);
109 	if (ret) {
110 		return ret;
111 	}
112 
113 	return 0;
114 }
115 
start_measurement(const struct device * dev,bool bus,bool shunt)116 static int start_measurement(const struct device *dev, bool bus, bool shunt)
117 {
118 	int ret;
119 	uint16_t reg_data;
120 
121 	ret = reg_read(dev, INA3221_CONFIG, &reg_data);
122 	if (ret) {
123 		return ret;
124 	}
125 
126 	reg_data &= ~(INA3221_CONFIG_BUS | INA3221_CONFIG_SHUNT);
127 	reg_data |= (INA3221_CONFIG_BUS * bus) | (INA3221_CONFIG_SHUNT * shunt);
128 
129 	ret = reg_write(dev, INA3221_CONFIG, reg_data);
130 	if (ret) {
131 		return ret;
132 	}
133 	return 0;
134 }
135 
measurement_ready(const struct device * dev)136 static bool measurement_ready(const struct device *dev)
137 {
138 	int ret;
139 	uint16_t reg_data;
140 
141 	ret = reg_read(dev, INA3221_MASK_ENABLE, &reg_data);
142 	if (ret) {
143 		return false;
144 	}
145 	return reg_data & INA3221_MASK_ENABLE_CONVERSION_READY;
146 }
147 
ina3221_sample_fetch(const struct device * dev,enum sensor_channel chan)148 static int ina3221_sample_fetch(const struct device *dev, enum sensor_channel chan)
149 {
150 	struct ina3221_data *data = dev->data;
151 	const struct ina3221_config *cfg = dev->config;
152 	bool measurement_successful = false;
153 	k_timeout_t measurement_time = K_NO_WAIT;
154 	int ret;
155 
156 	/* Trigger measurement and wait for completion */
157 	if (chan == SENSOR_CHAN_VOLTAGE) {
158 		ret = start_measurement(dev, true, false);
159 		if (ret) {
160 			return ret;
161 		}
162 		measurement_time =
163 			K_USEC(avg_mode_samples[cfg->avg_mode] *
164 			       conv_time_us[cfg->conv_time_bus]);
165 	} else if (chan == SENSOR_CHAN_CURRENT) {
166 		ret = start_measurement(dev, false, true);
167 		if (ret) {
168 			return ret;
169 		}
170 		measurement_time =
171 			K_USEC(avg_mode_samples[cfg->avg_mode] *
172 			       conv_time_us[cfg->conv_time_shunt]);
173 	} else if (chan == SENSOR_CHAN_POWER || chan == SENSOR_CHAN_ALL) {
174 		ret = start_measurement(dev, true, true);
175 		if (ret) {
176 			return ret;
177 		}
178 		measurement_time =
179 			K_USEC(avg_mode_samples[cfg->avg_mode] *
180 			       conv_time_us[MAX(cfg->conv_time_shunt, cfg->conv_time_bus)]);
181 	} else {
182 		return -ENOTSUP;
183 	}
184 
185 	for (size_t i = 0; i < MAX_RETRIES; ++i) {
186 		k_sleep(measurement_time);
187 		if (measurement_ready(dev)) {
188 			measurement_successful = true;
189 			break;
190 		}
191 	}
192 
193 	if (!measurement_successful) {
194 		LOG_ERR("Measurement timed out.");
195 		return -EFAULT;
196 	}
197 
198 	for (size_t i = 0; i < 3; ++i) {
199 		if (!cfg->enable_channel[i]) {
200 			continue;
201 		}
202 		if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POWER ||
203 		    chan == SENSOR_CHAN_VOLTAGE) {
204 			ret = reg_read(dev, INA3221_BUS_V1 + 2 * i, &(data->v_bus[i]));
205 			if (ret) {
206 				return ret;
207 			}
208 			data->v_bus[i] = data->v_bus[i] >> 3;
209 		}
210 		if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_POWER ||
211 		    chan == SENSOR_CHAN_CURRENT) {
212 			ret = reg_read(dev, INA3221_SHUNT_V1 + 2 * i, &(data->v_shunt[i]));
213 			if (ret) {
214 				return ret;
215 			}
216 			data->v_shunt[i] = data->v_shunt[i] >> 3;
217 		}
218 	}
219 
220 	return ret;
221 }
222 
ina3221_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)223 static int ina3221_channel_get(const struct device *dev, enum sensor_channel chan,
224 			       struct sensor_value *val)
225 {
226 	const struct ina3221_config *cfg = dev->config;
227 	struct ina3221_data *data = dev->data;
228 	float result;
229 
230 	switch (chan) {
231 	case SENSOR_CHAN_VOLTAGE:
232 		result = data->v_bus[data->selected_channel] * INA3221_BUS_VOLTAGE_LSB;
233 		break;
234 	case SENSOR_CHAN_CURRENT:
235 		result = data->v_shunt[data->selected_channel] * INA3221_SHUNT_VOLTAGE_LSB /
236 			 (cfg->shunt_r[data->selected_channel] / 1000.0f);
237 		break;
238 	case SENSOR_CHAN_POWER:
239 		result = data->v_bus[data->selected_channel] * INA3221_BUS_VOLTAGE_LSB *
240 			 data->v_shunt[data->selected_channel] * INA3221_SHUNT_VOLTAGE_LSB /
241 			 (cfg->shunt_r[data->selected_channel] / 1000.0f);
242 		break;
243 	default:
244 		LOG_DBG("Channel not supported by device!");
245 		return -ENOTSUP;
246 	}
247 
248 	return sensor_value_from_float(val, result);
249 }
250 
ina3221_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)251 static int ina3221_attr_set(const struct device *dev, enum sensor_channel chan,
252 			    enum sensor_attribute attr, const struct sensor_value *val)
253 {
254 	ARG_UNUSED(chan);
255 
256 	if (attr != SENSOR_ATTR_INA3221_SELECTED_CHANNEL) {
257 		return -ENOTSUP;
258 	}
259 
260 	struct ina3221_data *data = dev->data;
261 
262 	if (val->val1 < 1 || val->val1 > 3) {
263 		return -EINVAL;
264 	}
265 
266 	data->selected_channel = val->val1 - 1;
267 	return 0;
268 }
269 
270 static DEVICE_API(sensor, ina3221_api) = {
271 	.sample_fetch = ina3221_sample_fetch,
272 	.channel_get = ina3221_channel_get,
273 	.attr_set = ina3221_attr_set,
274 };
275 
276 #define INST_DT_INA3221(index)                                                                     \
277 	static const struct ina3221_config ina3221_config_##index = {                              \
278 		.bus = I2C_DT_SPEC_INST_GET(index),                                                \
279 		.avg_mode = DT_INST_PROP(index, avg_mode),                                         \
280 		.conv_time_bus = DT_INST_PROP(index, conv_time_bus),                               \
281 		.conv_time_shunt = DT_INST_PROP(index, conv_time_shunt),                           \
282 		.enable_channel = DT_INST_PROP(index, enable_channel),                             \
283 		.shunt_r = DT_INST_PROP(index, shunt_resistors),                                   \
284 	};                                                                                         \
285 	static struct ina3221_data ina3221_data_##index;                                           \
286                                                                                                    \
287 	SENSOR_DEVICE_DT_INST_DEFINE(index, ina3221_init, NULL, &ina3221_data_##index,             \
288 			      &ina3221_config_##index, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,   \
289 			      &ina3221_api);
290 
291 DT_INST_FOREACH_STATUS_OKAY(INST_DT_INA3221);
292