1 /*
2  * Copyright (c) 2016 Firmwave
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ti_tmp112
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/sys/byteorder.h>
12 #include <zephyr/sys/util.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/drivers/sensor.h>
15 #include <zephyr/sys/__assert.h>
16 #include <zephyr/logging/log.h>
17 #include "tmp112.h"
18 
19 LOG_MODULE_REGISTER(TMP112, CONFIG_SENSOR_LOG_LEVEL);
20 
tmp112_reg_read(const struct tmp112_config * cfg,uint8_t reg,uint16_t * val)21 static int tmp112_reg_read(const struct tmp112_config *cfg, uint8_t reg, uint16_t *val)
22 {
23 	if (i2c_burst_read_dt(&cfg->bus, reg, (uint8_t *)val, sizeof(*val)) < 0) {
24 		return -EIO;
25 	}
26 
27 	*val = sys_be16_to_cpu(*val);
28 
29 	return 0;
30 }
31 
tmp112_reg_write(const struct tmp112_config * cfg,uint8_t reg,uint16_t val)32 static int tmp112_reg_write(const struct tmp112_config *cfg, uint8_t reg, uint16_t val)
33 {
34 	uint8_t buf[3];
35 
36 	buf[0] = reg;
37 	sys_put_be16(val, &buf[1]);
38 
39 	return i2c_write_dt(&cfg->bus, buf, sizeof(buf));
40 }
41 
set_config_flags(struct tmp112_data * data,uint16_t mask,uint16_t value)42 static uint16_t set_config_flags(struct tmp112_data *data, uint16_t mask, uint16_t value)
43 {
44 	return (data->config_reg & ~mask) | (value & mask);
45 }
46 
tmp112_update_config(const struct device * dev,uint16_t mask,uint16_t val)47 static int tmp112_update_config(const struct device *dev, uint16_t mask, uint16_t val)
48 {
49 	int rc;
50 	struct tmp112_data *data = dev->data;
51 	const uint16_t new_val = set_config_flags(data, mask, val);
52 
53 	rc = tmp112_reg_write(dev->config, TMP112_REG_CONFIG, new_val);
54 	if (rc == 0) {
55 		data->config_reg = new_val;
56 	}
57 
58 	return rc;
59 }
60 
tmp112_set_threshold(const struct device * dev,uint8_t reg,int64_t micro_c)61 static int tmp112_set_threshold(const struct device *dev, uint8_t reg, int64_t micro_c)
62 {
63 	struct tmp112_data *drv_data = dev->data;
64 	int64_t v;
65 	uint16_t reg_value;
66 
67 	v = DIV_ROUND_CLOSEST(micro_c, TMP112_TEMP_SCALE);
68 
69 	if (drv_data->config_reg & TMP112_CONFIG_EM) {
70 		if (!IN_RANGE(v, TMP112_TEMP_MIN_EM, TMP112_TEMP_MAX_EM)) {
71 			return -EINVAL;
72 		}
73 		reg_value = (uint16_t)v << TMP112_DATA_EXTENDED_SHIFT;
74 	} else {
75 		if (!IN_RANGE(v, TMP112_TEMP_MIN, TMP112_TEMP_MAX)) {
76 			return -EINVAL;
77 		}
78 		reg_value = (uint16_t)v << TMP112_DATA_NORMAL_SHIFT;
79 	}
80 
81 	return tmp112_reg_write(dev->config, reg, reg_value);
82 }
83 
tmp112_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)84 static int tmp112_attr_set(const struct device *dev, enum sensor_channel chan,
85 			   enum sensor_attribute attr, const struct sensor_value *val)
86 {
87 	uint16_t value;
88 	uint16_t cr;
89 
90 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
91 		return -ENOTSUP;
92 	}
93 
94 	switch (attr) {
95 #if CONFIG_TMP112_FULL_SCALE_RUNTIME
96 	case SENSOR_ATTR_FULL_SCALE:
97 		/* the sensor supports two ranges -55 to 128 and -55 to 150 */
98 		/* the value contains the upper limit */
99 		if (val->val1 == 128) {
100 			value = 0x0000;
101 		} else if (val->val1 == 150) {
102 			value = TMP112_CONFIG_EM;
103 		} else {
104 			return -ENOTSUP;
105 		}
106 
107 		if (tmp112_update_config(dev, TMP112_CONFIG_EM, value) < 0) {
108 			LOG_DBG("Failed to set attribute!");
109 			return -EIO;
110 		}
111 		break;
112 #endif
113 
114 #if CONFIG_TMP112_SAMPLING_FREQUENCY_RUNTIME
115 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
116 		/* conversion rate in mHz */
117 		cr = val->val1 * 1000 + val->val2 / 1000;
118 
119 		/* the sensor supports 0.25Hz, 1Hz, 4Hz and 8Hz */
120 		/* conversion rate */
121 		switch (cr) {
122 		case 250:
123 			value = TMP112_CONV_RATE(TMP112_CONV_RATE_025);
124 			break;
125 
126 		case 1000:
127 			value = TMP112_CONV_RATE(TMP112_CONV_RATE_1000);
128 			break;
129 
130 		case 4000:
131 			value = TMP112_CONV_RATE(TMP112_CONV_RATE_4);
132 			break;
133 
134 		case 8000:
135 			value = TMP112_CONV_RATE(TMP112_CONV_RATE_8);
136 			break;
137 
138 		default:
139 			return -ENOTSUP;
140 		}
141 
142 		if (tmp112_update_config(dev, TMP112_CONV_RATE_MASK, value) < 0) {
143 			LOG_DBG("Failed to set attribute!");
144 			return -EIO;
145 		}
146 
147 		break;
148 #endif
149 
150 	case SENSOR_ATTR_LOWER_THRESH:
151 		return tmp112_set_threshold(dev, TMP112_REG_TLOW, sensor_value_to_micro(val));
152 
153 	case SENSOR_ATTR_UPPER_THRESH:
154 		return tmp112_set_threshold(dev, TMP112_REG_THIGH, sensor_value_to_micro(val));
155 
156 	default:
157 		return -ENOTSUP;
158 	}
159 
160 	return 0;
161 }
162 
tmp112_sample_fetch(const struct device * dev,enum sensor_channel chan)163 static int tmp112_sample_fetch(const struct device *dev, enum sensor_channel chan)
164 {
165 	struct tmp112_data *drv_data = dev->data;
166 	const struct tmp112_config *cfg = dev->config;
167 	uint16_t val;
168 
169 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP);
170 
171 	if (tmp112_reg_read(cfg, TMP112_REG_TEMPERATURE, &val) < 0) {
172 		return -EIO;
173 	}
174 
175 	if (val & TMP112_DATA_EXTENDED) {
176 		drv_data->sample = arithmetic_shift_right((int16_t)val, TMP112_DATA_EXTENDED_SHIFT);
177 	} else {
178 		drv_data->sample = arithmetic_shift_right((int16_t)val, TMP112_DATA_NORMAL_SHIFT);
179 	}
180 
181 	return 0;
182 }
183 
tmp112_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)184 static int tmp112_channel_get(const struct device *dev, enum sensor_channel chan,
185 			      struct sensor_value *val)
186 {
187 	struct tmp112_data *drv_data = dev->data;
188 
189 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
190 		return -ENOTSUP;
191 	}
192 
193 	return sensor_value_from_micro(val, (int32_t)drv_data->sample * TMP112_TEMP_SCALE);
194 }
195 
196 static DEVICE_API(sensor, tmp112_driver_api) = {
197 	.attr_set = tmp112_attr_set,
198 	.sample_fetch = tmp112_sample_fetch,
199 	.channel_get = tmp112_channel_get,
200 };
201 
tmp112_init(const struct device * dev)202 int tmp112_init(const struct device *dev)
203 {
204 	const struct tmp112_config *cfg = dev->config;
205 	struct tmp112_data *data = dev->data;
206 	int ret;
207 
208 	if (!device_is_ready(cfg->bus.bus)) {
209 		LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name);
210 		return -EINVAL;
211 	}
212 
213 	data->config_reg = TMP112_CONV_RATE(cfg->cr) | TMP112_CONV_RES_MASK |
214 			   (cfg->extended_mode ? TMP112_CONFIG_EM : 0);
215 
216 	ret = tmp112_update_config(dev, 0, 0);
217 	if (ret) {
218 		LOG_ERR("Failed to set configuration (%d)", ret);
219 		return ret;
220 	}
221 
222 	ret = tmp112_set_threshold(dev, TMP112_REG_TLOW, cfg->t_low_micro_c);
223 	if (ret) {
224 		LOG_ERR("Failed to set tLow threshold (%d)", ret);
225 		return ret;
226 	}
227 
228 	ret = tmp112_set_threshold(dev, TMP112_REG_THIGH, cfg->t_high_micro_c);
229 	if (ret) {
230 		LOG_ERR("Failed to set tHigh threshold (%d)", ret);
231 		return ret;
232 	}
233 
234 	return 0;
235 }
236 
237 #define TMP112_INST(inst)                                                                          \
238 	static struct tmp112_data tmp112_data_##inst;                                              \
239 	static const struct tmp112_config tmp112_config_##inst = {                                 \
240 		.bus = I2C_DT_SPEC_INST_GET(inst),                                                 \
241 		.cr = DT_INST_ENUM_IDX(inst, conversion_rate),                                     \
242 		.t_low_micro_c = DT_INST_PROP(inst, t_low_micro_c),                                \
243 		.t_high_micro_c = DT_INST_PROP(inst, t_high_micro_c),                              \
244 		.extended_mode = DT_INST_PROP(inst, extended_mode),                                \
245 	};                                                                                         \
246                                                                                                    \
247 	SENSOR_DEVICE_DT_INST_DEFINE(inst, tmp112_init, NULL, &tmp112_data_##inst,                 \
248 				     &tmp112_config_##inst, POST_KERNEL,                           \
249 				     CONFIG_SENSOR_INIT_PRIORITY, &tmp112_driver_api);
250 
251 DT_INST_FOREACH_STATUS_OKAY(TMP112_INST)
252