1 /*
2  * Copyright (c) 2019 Centaur Analytics, Inc
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_tmp116
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/drivers/sensor/tmp116.h>
13 #include <zephyr/sys/util.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/sys/__assert.h>
16 #include <zephyr/logging/log.h>
17 #include <zephyr/kernel.h>
18 
19 #include "tmp116.h"
20 
21 #define  EEPROM_SIZE_REG sizeof(uint16_t)
22 #define  EEPROM_TMP117_RESERVED (2 * sizeof(uint16_t))
23 #define  EEPROM_MIN_BUSY_MS 7
24 
25 LOG_MODULE_REGISTER(TMP116, CONFIG_SENSOR_LOG_LEVEL);
26 
tmp116_reg_read(const struct device * dev,uint8_t reg,uint16_t * val)27 static int tmp116_reg_read(const struct device *dev, uint8_t reg,
28 			   uint16_t *val)
29 {
30 	const struct tmp116_dev_config *cfg = dev->config;
31 
32 	if (i2c_burst_read_dt(&cfg->bus, reg, (uint8_t *)val, 2)
33 	    < 0) {
34 		return -EIO;
35 	}
36 
37 	*val = sys_be16_to_cpu(*val);
38 
39 	return 0;
40 }
41 
tmp116_reg_write(const struct device * dev,uint8_t reg,uint16_t val)42 static int tmp116_reg_write(const struct device *dev, uint8_t reg,
43 			    uint16_t val)
44 {
45 	const struct tmp116_dev_config *cfg = dev->config;
46 	uint8_t tx_buf[3] = {reg, val >> 8, val & 0xFF};
47 
48 	return i2c_write_dt(&cfg->bus, tx_buf, sizeof(tx_buf));
49 }
50 
check_eeprom_bounds(const struct device * dev,off_t offset,size_t len)51 static bool check_eeprom_bounds(const struct device *dev, off_t offset,
52 			       size_t len)
53 {
54 	struct tmp116_data *drv_data = dev->data;
55 
56 	if ((offset + len) > EEPROM_TMP116_SIZE ||
57 	    offset % EEPROM_SIZE_REG != 0 ||
58 	    len % EEPROM_SIZE_REG != 0) {
59 		return false;
60 	}
61 
62 	/* TMP117 uses EEPROM[2] as temperature offset register */
63 	if (drv_data->id == TMP117_DEVICE_ID &&
64 	    offset <= EEPROM_TMP117_RESERVED &&
65 	    (offset + len) > EEPROM_TMP117_RESERVED) {
66 		return false;
67 	}
68 
69 	return true;
70 }
71 
tmp116_eeprom_write(const struct device * dev,off_t offset,const void * data,size_t len)72 int tmp116_eeprom_write(const struct device *dev, off_t offset,
73 			const void *data, size_t len)
74 {
75 	uint8_t reg;
76 	const uint16_t *src = data;
77 	int res;
78 
79 	if (!check_eeprom_bounds(dev, offset, len)) {
80 		return -EINVAL;
81 	}
82 
83 	res = tmp116_reg_write(dev, TMP116_REG_EEPROM_UL, TMP116_EEPROM_UL_UNLOCK);
84 	if (res) {
85 		return res;
86 	}
87 
88 	for (reg = (offset / 2); reg < offset / 2 + len / 2; reg++) {
89 		uint16_t val = *src;
90 
91 		res = tmp116_reg_write(dev, reg + TMP116_REG_EEPROM1, val);
92 		if (res != 0) {
93 			break;
94 		}
95 
96 		k_sleep(K_MSEC(EEPROM_MIN_BUSY_MS));
97 
98 		do {
99 			res = tmp116_reg_read(dev, TMP116_REG_EEPROM_UL, &val);
100 			if (res != 0) {
101 				break;
102 			}
103 		} while (val & TMP116_EEPROM_UL_BUSY);
104 		src++;
105 
106 		if (res != 0) {
107 			break;
108 		}
109 	}
110 
111 	res = tmp116_reg_write(dev, TMP116_REG_EEPROM_UL, 0);
112 
113 	return res;
114 }
115 
tmp116_eeprom_read(const struct device * dev,off_t offset,void * data,size_t len)116 int tmp116_eeprom_read(const struct device *dev, off_t offset, void *data,
117 		       size_t len)
118 {
119 	uint8_t reg;
120 	uint16_t *dst = data;
121 	int res = 0;
122 
123 	if (!check_eeprom_bounds(dev, offset, len)) {
124 		return -EINVAL;
125 	}
126 
127 	for (reg = (offset / 2); reg < offset / 2 + len / 2; reg++) {
128 		res = tmp116_reg_read(dev, reg + TMP116_REG_EEPROM1, dst);
129 		if (res != 0) {
130 			break;
131 		}
132 		dst++;
133 	}
134 
135 	return res;
136 }
137 
138 
139 /**
140  * @brief Check the Device ID
141  *
142  * @param[in]   dev     Pointer to the device structure
143  * @param[in]	id	Pointer to the variable for storing the device id
144  *
145  * @retval 0 on success
146  * @retval -EIO Otherwise
147  */
tmp116_device_id_check(const struct device * dev,uint16_t * id)148 static inline int tmp116_device_id_check(const struct device *dev, uint16_t *id)
149 {
150 	if (tmp116_reg_read(dev, TMP116_REG_DEVICE_ID, id) != 0) {
151 		LOG_ERR("%s: Failed to get Device ID register!",
152 			dev->name);
153 		return -EIO;
154 	}
155 
156 	if ((*id != TMP116_DEVICE_ID) && (*id != TMP117_DEVICE_ID)) {
157 		LOG_ERR("%s: Failed to match the device IDs!",
158 			dev->name);
159 		return -EINVAL;
160 	}
161 
162 	return 0;
163 }
164 
tmp116_sample_fetch(const struct device * dev,enum sensor_channel chan)165 static int tmp116_sample_fetch(const struct device *dev,
166 			       enum sensor_channel chan)
167 {
168 	struct tmp116_data *drv_data = dev->data;
169 	uint16_t value;
170 	uint16_t cfg_reg = 0;
171 	int rc;
172 
173 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
174 			chan == SENSOR_CHAN_AMBIENT_TEMP);
175 
176 	/* clear sensor values */
177 	drv_data->sample = 0U;
178 
179 	/* Make sure that a data is available */
180 	rc = tmp116_reg_read(dev, TMP116_REG_CFGR, &cfg_reg);
181 	if (rc < 0) {
182 		LOG_ERR("%s, Failed to read from CFGR register",
183 			dev->name);
184 		return rc;
185 	}
186 
187 	if ((cfg_reg & TMP116_CFGR_DATA_READY) == 0) {
188 		LOG_DBG("%s: no data ready", dev->name);
189 		return -EBUSY;
190 	}
191 
192 	/* Get the most recent temperature measurement */
193 	rc = tmp116_reg_read(dev, TMP116_REG_TEMP, &value);
194 	if (rc < 0) {
195 		LOG_ERR("%s: Failed to read from TEMP register!",
196 			dev->name);
197 		return rc;
198 	}
199 
200 	/* store measurements to the driver */
201 	drv_data->sample = (int16_t)value;
202 
203 	return 0;
204 }
205 
tmp116_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)206 static int tmp116_channel_get(const struct device *dev,
207 			      enum sensor_channel chan,
208 			      struct sensor_value *val)
209 {
210 	struct tmp116_data *drv_data = dev->data;
211 	int32_t tmp;
212 
213 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
214 		return -ENOTSUP;
215 	}
216 
217 	/*
218 	 * See datasheet "Temperature Results and Limits" section for more
219 	 * details on processing sample data.
220 	 */
221 	tmp = ((int16_t)drv_data->sample * (int32_t)TMP116_RESOLUTION) / 10;
222 	val->val1 = tmp / 1000000; /* uCelsius */
223 	val->val2 = tmp % 1000000;
224 
225 	return 0;
226 }
227 
tmp116_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)228 static int tmp116_attr_set(const struct device *dev,
229 			   enum sensor_channel chan,
230 			   enum sensor_attribute attr,
231 			   const struct sensor_value *val)
232 {
233 	struct tmp116_data *drv_data = dev->data;
234 	int16_t value;
235 
236 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
237 		return -ENOTSUP;
238 	}
239 
240 	switch (attr) {
241 	case SENSOR_ATTR_OFFSET:
242 		if (drv_data->id != TMP117_DEVICE_ID) {
243 			LOG_ERR("%s: Offset is only supported by TMP117",
244 			dev->name);
245 			return -EINVAL;
246 		}
247 		/*
248 		 * The offset is encoded into the temperature register format.
249 		 */
250 		value = (((val->val1) * 10000000) + ((val->val2) * 10))
251 						/ (int32_t)TMP116_RESOLUTION;
252 
253 		return tmp116_reg_write(dev, TMP117_REG_TEMP_OFFSET, value);
254 
255 	default:
256 		return -ENOTSUP;
257 	}
258 }
259 
tmp116_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)260 static int tmp116_attr_get(const struct device *dev, enum sensor_channel chan,
261 			   enum sensor_attribute attr, struct sensor_value *val)
262 {
263 	uint16_t data;
264 	int rc;
265 
266 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
267 		return -ENOTSUP;
268 	}
269 
270 	switch (attr) {
271 	case SENSOR_ATTR_CONFIGURATION:
272 		rc = tmp116_reg_read(dev, TMP116_REG_CFGR, &data);
273 		if (rc < 0) {
274 			return rc;
275 		}
276 		break;
277 	default:
278 		return -ENOTSUP;
279 	}
280 
281 	val->val1 = data;
282 	val->val2 = 0;
283 
284 	return 0;
285 }
286 
287 static const struct sensor_driver_api tmp116_driver_api = {
288 	.attr_set = tmp116_attr_set,
289 	.attr_get = tmp116_attr_get,
290 	.sample_fetch = tmp116_sample_fetch,
291 	.channel_get = tmp116_channel_get
292 };
293 
tmp116_init(const struct device * dev)294 static int tmp116_init(const struct device *dev)
295 {
296 	struct tmp116_data *drv_data = dev->data;
297 	const struct tmp116_dev_config *cfg = dev->config;
298 	int rc;
299 	uint16_t id;
300 
301 	if (!device_is_ready(cfg->bus.bus)) {
302 		LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name);
303 		return -EINVAL;
304 	}
305 
306 	/* Check the Device ID */
307 	rc = tmp116_device_id_check(dev, &id);
308 	if (rc < 0) {
309 		return rc;
310 	}
311 	LOG_DBG("Got device ID: %x", id);
312 	drv_data->id = id;
313 
314 	return 0;
315 }
316 
317 #define DEFINE_TMP116(_num) \
318 	static struct tmp116_data tmp116_data_##_num; \
319 	static const struct tmp116_dev_config tmp116_config_##_num = { \
320 		.bus = I2C_DT_SPEC_INST_GET(_num) \
321 	}; \
322 	SENSOR_DEVICE_DT_INST_DEFINE(_num, tmp116_init, NULL, \
323 		&tmp116_data_##_num, &tmp116_config_##_num, POST_KERNEL, \
324 		CONFIG_SENSOR_INIT_PRIORITY, &tmp116_driver_api);
325 
326 DT_INST_FOREACH_STATUS_OKAY(DEFINE_TMP116)
327