1 /*
2  * Copyright (c) 2018 Analog Devices Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT adi_adt7420
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 
17 #include "adt7420.h"
18 
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(ADT7420, CONFIG_SENSOR_LOG_LEVEL);
21 
adt7420_temp_reg_read(const struct device * dev,uint8_t reg,int16_t * val)22 static int adt7420_temp_reg_read(const struct device *dev, uint8_t reg,
23 				 int16_t *val)
24 {
25 	const struct adt7420_dev_config *cfg = dev->config;
26 
27 	if (i2c_burst_read_dt(&cfg->i2c, reg, (uint8_t *) val, 2) < 0) {
28 		return -EIO;
29 	}
30 
31 	*val = sys_be16_to_cpu(*val);
32 
33 	return 0;
34 }
35 
adt7420_temp_reg_write(const struct device * dev,uint8_t reg,int16_t val)36 static int adt7420_temp_reg_write(const struct device *dev, uint8_t reg,
37 				  int16_t val)
38 {
39 	const struct adt7420_dev_config *cfg = dev->config;
40 	uint8_t buf[3] = {reg, val >> 8, val & 0xFF};
41 
42 	return i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
43 }
44 
adt7420_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)45 static int adt7420_attr_set(const struct device *dev,
46 			    enum sensor_channel chan,
47 			    enum sensor_attribute attr,
48 			    const struct sensor_value *val)
49 {
50 	const struct adt7420_dev_config *cfg = dev->config;
51 	uint8_t val8, reg = 0U;
52 	uint16_t rate;
53 	int64_t value;
54 
55 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
56 		return -ENOTSUP;
57 	}
58 
59 	switch (attr) {
60 	case SENSOR_ATTR_SAMPLING_FREQUENCY:
61 		rate = val->val1 * 1000 + val->val2 / 1000; /* rate in mHz */
62 
63 		switch (rate) {
64 		case 240:
65 			val8 = ADT7420_OP_MODE_CONT_CONV;
66 			break;
67 		case 1000:
68 			val8 = ADT7420_OP_MODE_1_SPS;
69 			break;
70 		default:
71 			return -EINVAL;
72 		}
73 
74 		if (i2c_reg_update_byte_dt(&cfg->i2c,
75 					   ADT7420_REG_CONFIG,
76 					   ADT7420_CONFIG_OP_MODE(~0),
77 					   ADT7420_CONFIG_OP_MODE(val8)) < 0) {
78 			LOG_DBG("Failed to set attribute!");
79 			return -EIO;
80 		}
81 
82 		return 0;
83 	case SENSOR_ATTR_UPPER_THRESH:
84 		reg = ADT7420_REG_T_HIGH_MSB;
85 		__fallthrough;
86 	case SENSOR_ATTR_LOWER_THRESH:
87 		if (!reg) {
88 			reg = ADT7420_REG_T_LOW_MSB;
89 		}
90 
91 		if ((val->val1 > 150) || (val->val1 < -40)) {
92 			return -EINVAL;
93 		}
94 
95 		value = (int64_t)val->val1 * 1000000 + val->val2;
96 		value = (value / ADT7420_TEMP_SCALE) << 1;
97 
98 		if (adt7420_temp_reg_write(dev, reg, value) < 0) {
99 			LOG_DBG("Failed to set attribute!");
100 			return -EIO;
101 		}
102 
103 		return 0;
104 	default:
105 		return -ENOTSUP;
106 	}
107 
108 	return 0;
109 }
110 
adt7420_sample_fetch(const struct device * dev,enum sensor_channel chan)111 static int adt7420_sample_fetch(const struct device *dev,
112 				enum sensor_channel chan)
113 {
114 	struct adt7420_data *drv_data = dev->data;
115 	int16_t value;
116 
117 	__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
118 			chan == SENSOR_CHAN_AMBIENT_TEMP);
119 
120 	if (adt7420_temp_reg_read(dev, ADT7420_REG_TEMP_MSB, &value) < 0) {
121 		return -EIO;
122 	}
123 
124 	drv_data->sample = value >> 1; /* use 15-bit only */
125 
126 	return 0;
127 }
128 
adt7420_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)129 static int adt7420_channel_get(const struct device *dev,
130 			       enum sensor_channel chan,
131 			       struct sensor_value *val)
132 {
133 	struct adt7420_data *drv_data = dev->data;
134 	int32_t value;
135 
136 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
137 		return -ENOTSUP;
138 	}
139 
140 	value = (int32_t)drv_data->sample * ADT7420_TEMP_SCALE;
141 	val->val1 = value / 1000000;
142 	val->val2 = value % 1000000;
143 
144 	return 0;
145 }
146 
147 static const struct sensor_driver_api adt7420_driver_api = {
148 	.attr_set = adt7420_attr_set,
149 	.sample_fetch = adt7420_sample_fetch,
150 	.channel_get = adt7420_channel_get,
151 #ifdef CONFIG_ADT7420_TRIGGER
152 	.trigger_set = adt7420_trigger_set,
153 #endif
154 };
155 
adt7420_probe(const struct device * dev)156 static int adt7420_probe(const struct device *dev)
157 {
158 	const struct adt7420_dev_config *cfg = dev->config;
159 	uint8_t value;
160 	int ret;
161 
162 	ret = i2c_reg_read_byte_dt(&cfg->i2c, ADT7420_REG_ID, &value);
163 	if (ret) {
164 		return ret;
165 	}
166 
167 	if (value != ADT7420_DEFAULT_ID) {
168 		return -ENODEV;
169 	}
170 
171 	ret = i2c_reg_write_byte_dt(&cfg->i2c,
172 			ADT7420_REG_CONFIG, ADT7420_CONFIG_RESOLUTION |
173 			ADT7420_CONFIG_OP_MODE(ADT7420_OP_MODE_CONT_CONV));
174 	if (ret) {
175 		return ret;
176 	}
177 
178 	ret = i2c_reg_write_byte_dt(&cfg->i2c,
179 				    ADT7420_REG_HIST, CONFIG_ADT7420_TEMP_HYST);
180 	if (ret) {
181 		return ret;
182 	}
183 	ret = adt7420_temp_reg_write(dev, ADT7420_REG_T_CRIT_MSB,
184 				     (CONFIG_ADT7420_TEMP_CRIT * 1000000 /
185 				     ADT7420_TEMP_SCALE) << 1);
186 	if (ret) {
187 		return ret;
188 	}
189 
190 #ifdef CONFIG_ADT7420_TRIGGER
191 	if (cfg->int_gpio.port) {
192 		ret = adt7420_init_interrupt(dev);
193 		if (ret < 0) {
194 			LOG_ERR("Failed to initialize interrupt!");
195 			return ret;
196 		}
197 	}
198 #endif
199 
200 	return 0;
201 }
202 
adt7420_init(const struct device * dev)203 static int adt7420_init(const struct device *dev)
204 {
205 	const struct adt7420_dev_config *cfg = dev->config;
206 
207 	if (!device_is_ready(cfg->i2c.bus)) {
208 		LOG_ERR("Bus device is not ready");
209 		return -EINVAL;
210 	}
211 
212 	return adt7420_probe(dev);
213 }
214 
215 #define ADT7420_DEFINE(inst)								\
216 	static struct adt7420_data adt7420_data_##inst;					\
217 											\
218 	static const struct adt7420_dev_config adt7420_config_##inst = {		\
219 		.i2c = I2C_DT_SPEC_INST_GET(inst),					\
220 											\
221 	IF_ENABLED(CONFIG_ADT7420_TRIGGER,						\
222 		   (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, { 0 }),))	\
223 	};										\
224 											\
225 	SENSOR_DEVICE_DT_INST_DEFINE(inst, adt7420_init, NULL, &adt7420_data_##inst,	\
226 			      &adt7420_config_##inst, POST_KERNEL,			\
227 			      CONFIG_SENSOR_INIT_PRIORITY, &adt7420_driver_api);	\
228 
229 DT_INST_FOREACH_STATUS_OKAY(ADT7420_DEFINE)
230