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