1 /*
2  * Copyright (c) 2024 Fredrik Gihl
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_tmp114
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/sys/__assert.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/kernel.h>
16 
17 LOG_MODULE_REGISTER(TMP114, CONFIG_SENSOR_LOG_LEVEL);
18 
19 #define TMP114_REG_TEMP		0x0
20 #define TMP114_REG_ALERT	0x2
21 #define TMP114_REG_CFGR		0x3
22 #define TMP114_REG_DEVICE_ID	0xb
23 
24 #define TMP114_RESOLUTION	78125       /* in tens of uCelsius*/
25 #define TMP114_RESOLUTION_DIV	10000000
26 
27 #define TMP114_DEVICE_ID	0x1114
28 
29 #define TMP114_ALERT_DATA_READY  BIT(0)
30 #define TMP114_AVG_MASK          BIT(7)
31 
32 struct tmp114_data {
33 	uint16_t sample;
34 	uint16_t id;
35 };
36 
37 struct tmp114_dev_config {
38 	struct i2c_dt_spec bus;
39 };
40 
tmp114_reg_read(const struct device * dev,uint8_t reg,uint16_t * val)41 static int tmp114_reg_read(const struct device *dev, uint8_t reg,
42 			   uint16_t *val)
43 {
44 	const struct tmp114_dev_config *cfg = dev->config;
45 
46 	if (i2c_burst_read_dt(&cfg->bus, reg, (uint8_t *)val, 2)
47 	    < 0) {
48 		return -EIO;
49 	}
50 
51 	*val = sys_be16_to_cpu(*val);
52 
53 	return 0;
54 }
55 
tmp114_reg_write(const struct device * dev,uint8_t reg,uint16_t val)56 static int tmp114_reg_write(const struct device *dev, uint8_t reg,
57 			    uint16_t val)
58 {
59 	const struct tmp114_dev_config *cfg = dev->config;
60 	uint8_t tx_buf[3] = {reg, val >> 8, val & 0xFF};
61 
62 	return i2c_write_dt(&cfg->bus, tx_buf, sizeof(tx_buf));
63 }
64 
tmp114_device_id_check(const struct device * dev,uint16_t * id)65 static inline int tmp114_device_id_check(const struct device *dev, uint16_t *id)
66 {
67 	if (tmp114_reg_read(dev, TMP114_REG_DEVICE_ID, id) != 0) {
68 		LOG_ERR("%s: Failed to get Device ID register!", dev->name);
69 		return -EIO;
70 	}
71 
72 	if (*id != TMP114_DEVICE_ID) {
73 		LOG_ERR("%s: Failed to match the device ID!", dev->name);
74 		return -EINVAL;
75 	}
76 
77 	return 0;
78 }
79 
tmp114_sample_fetch(const struct device * dev,enum sensor_channel chan)80 static int tmp114_sample_fetch(const struct device *dev,
81 			       enum sensor_channel chan)
82 {
83 	struct tmp114_data *drv_data = dev->data;
84 	uint16_t value;
85 	uint16_t cfg_reg = 0;
86 	int rc;
87 
88 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
89 			chan == SENSOR_CHAN_AMBIENT_TEMP);
90 
91 	/* clear sensor values */
92 	drv_data->sample = 0U;
93 
94 	/* Check alert register to make sure that a data is available */
95 	rc = tmp114_reg_read(dev, TMP114_REG_ALERT, &cfg_reg);
96 	if (rc < 0) {
97 		LOG_ERR("%s, Failed to read from CFGR register", dev->name);
98 		return rc;
99 	}
100 
101 	if ((cfg_reg & TMP114_ALERT_DATA_READY) == 0) {
102 		LOG_DBG("%s: no data ready", dev->name);
103 		return -EBUSY;
104 	}
105 
106 	/* Get the most recent temperature measurement */
107 	rc = tmp114_reg_read(dev, TMP114_REG_TEMP, &value);
108 	if (rc < 0) {
109 		LOG_ERR("%s: Failed to read from TEMP register!", dev->name);
110 		return rc;
111 	}
112 
113 	/* store measurements to the driver */
114 	drv_data->sample = (int16_t)value;
115 
116 	return 0;
117 }
118 
tmp114_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)119 static int tmp114_channel_get(const struct device *dev,
120 			      enum sensor_channel chan,
121 			      struct sensor_value *val)
122 {
123 	struct tmp114_data *drv_data = dev->data;
124 	int32_t tmp;
125 
126 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
127 		return -ENOTSUP;
128 	}
129 
130 	/*
131 	 * See datasheet for tmp114, section 'Temp_Result Register' section
132 	 * for more details on processing sample data.
133 	 */
134 	tmp = ((int16_t)drv_data->sample * (int32_t)TMP114_RESOLUTION) / 10;
135 	val->val1 = tmp / 1000000; /* uCelsius */
136 	val->val2 = tmp % 1000000;
137 
138 	return 0;
139 }
140 
tmp114_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)141 static int tmp114_attr_get(const struct device *dev, enum sensor_channel chan,
142 			   enum sensor_attribute attr, struct sensor_value *val)
143 {
144 	uint16_t data;
145 	int rc;
146 
147 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
148 		return -ENOTSUP;
149 	}
150 
151 	switch (attr) {
152 	case SENSOR_ATTR_CONFIGURATION:
153 		rc = tmp114_reg_read(dev, TMP114_REG_CFGR, &data);
154 		if (rc < 0) {
155 			return rc;
156 		}
157 		break;
158 	default:
159 		return -ENOTSUP;
160 	}
161 
162 	val->val1 = data;
163 	val->val2 = 0;
164 
165 	return 0;
166 }
167 
tmp114_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)168 static int tmp114_attr_set(const struct device *dev,
169 			   enum sensor_channel chan,
170 			   enum sensor_attribute attr,
171 			   const struct sensor_value *val)
172 {
173 	int16_t value;
174 	int rc;
175 
176 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
177 		return -ENOTSUP;
178 	}
179 
180 	switch (attr) {
181 	case SENSOR_ATTR_OVERSAMPLING:
182 		/* Enable the AVG in tmp114. The chip will do 8 avg of 8 samples
183 		 * to get a more accurate value.
184 		 */
185 		rc = tmp114_reg_read(dev, TMP114_REG_CFGR, &value);
186 		if (rc < 0) {
187 			return rc;
188 		}
189 		value = value & ~TMP114_AVG_MASK;
190 		if (val->val1) {
191 			value |= TMP114_AVG_MASK;
192 		}
193 
194 		return tmp114_reg_write(dev, TMP114_REG_CFGR, value);
195 	default:
196 		return -ENOTSUP;
197 	}
198 }
199 
200 static DEVICE_API(sensor, tmp114_driver_api) = {
201 	.attr_get = tmp114_attr_get,
202 	.attr_set = tmp114_attr_set,
203 	.sample_fetch = tmp114_sample_fetch,
204 	.channel_get = tmp114_channel_get
205 };
206 
tmp114_init(const struct device * dev)207 static int tmp114_init(const struct device *dev)
208 {
209 	struct tmp114_data *drv_data = dev->data;
210 	const struct tmp114_dev_config *cfg = dev->config;
211 	int rc;
212 	uint16_t id;
213 
214 	if (!i2c_is_ready_dt(&cfg->bus)) {
215 		LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name);
216 		return -EINVAL;
217 	}
218 
219 	/* Check the Device ID */
220 	rc = tmp114_device_id_check(dev, &id);
221 	if (rc < 0) {
222 		return rc;
223 	}
224 	LOG_INF("Got device ID: %x", id);
225 	drv_data->id = id;
226 
227 	return 0;
228 }
229 
230 #define DEFINE_TMP114(_num) \
231 	static struct tmp114_data tmp114_data_##_num; \
232 	static const struct tmp114_dev_config tmp114_config_##_num = { \
233 		.bus = I2C_DT_SPEC_INST_GET(_num) \
234 	}; \
235 	SENSOR_DEVICE_DT_INST_DEFINE(_num, tmp114_init, NULL, \
236 		&tmp114_data_##_num, &tmp114_config_##_num, POST_KERNEL, \
237 		CONFIG_SENSOR_INIT_PRIORITY, &tmp114_driver_api);
238 
239 DT_INST_FOREACH_STATUS_OKAY(DEFINE_TMP114)
240