1 /*
2  * Copyright (c) 2021 Vestas Wind Systems A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT lm77
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/drivers/i2c.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/pm/device.h>
15 #include <zephyr/sys/byteorder.h>
16 
17 LOG_MODULE_REGISTER(lm77, CONFIG_SENSOR_LOG_LEVEL);
18 
19 /*
20  * Only compile in trigger support if enabled in Kconfig and at least one
21  * enabled lm77 devicetree node has the int-gpios property.
22  */
23 #define LM77_TRIGGER_SUPPORT \
24 	(CONFIG_LM77_TRIGGER && DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios))
25 
26 /* LM77 registers */
27 #define LM77_REG_TEMP   0x00U
28 #define LM77_REG_CONFIG 0x01U
29 #define LM77_REG_THYST  0x02U
30 #define LM77_REG_TCRIT  0x03U
31 #define LM77_REG_TLOW   0x04U
32 #define LM77_REG_THIGH  0x05U
33 
34 /* LM77 configuration register bits */
35 union lm77_reg_config {
36 	uint8_t reg;
37 	struct {
38 		uint8_t shutdown : 1;
39 		uint8_t int_mode : 1;
40 		uint8_t tcrita_pol : 1;
41 		uint8_t int_pol : 1;
42 		uint8_t fault_queue : 1;
43 		uint8_t reserved : 3;
44 	} __packed;
45 };
46 
47 struct lm77_config {
48 	struct i2c_dt_spec i2c;
49 	union lm77_reg_config config_dt;
50 #if LM77_TRIGGER_SUPPORT
51 	struct gpio_dt_spec int_gpio;
52 #endif /* LM77_TRIGGER_SUPPORT */
53 };
54 
55 struct lm77_data {
56 	int16_t temp;
57 #if LM77_TRIGGER_SUPPORT
58 	const struct device *dev;
59 	struct k_work_q workq;
60 	struct k_work work;
61 	struct gpio_callback int_gpio_cb;
62 	const struct sensor_trigger *trigger;
63 	sensor_trigger_handler_t trigger_handler;
64 
65 	K_KERNEL_STACK_MEMBER(stack, CONFIG_LM77_TRIGGER_THREAD_STACK_SIZE);
66 #endif /* LM77_TRIGGER_SUPPORT */
67 };
68 
lm77_write_config(const struct device * dev,uint8_t value)69 static int lm77_write_config(const struct device *dev, uint8_t value)
70 {
71 	const struct lm77_config *config = dev->config;
72 	uint8_t buf[2] = { LM77_REG_CONFIG, value };
73 
74 	return i2c_write_dt(&config->i2c, buf, sizeof(buf));
75 }
76 
lm77_read_temp(const struct device * dev,uint8_t reg,int16_t * value)77 static int lm77_read_temp(const struct device *dev, uint8_t reg, int16_t *value)
78 {
79 	const struct lm77_config *config = dev->config;
80 	uint8_t buf[2];
81 	int err;
82 
83 	err = i2c_write_read_dt(&config->i2c, &reg, sizeof(reg), &buf, sizeof(buf));
84 	if (err < 0) {
85 		LOG_ERR("failed to read temperature register 0x%02x (err %d)", reg, err);
86 		return err;
87 	}
88 
89 	*value = sys_get_be16(buf);
90 
91 	return 0;
92 }
93 
lm77_write_temp(const struct device * dev,uint8_t reg,int16_t value)94 static int lm77_write_temp(const struct device *dev, uint8_t reg, int16_t value)
95 {
96 	const struct lm77_config *config = dev->config;
97 	uint8_t buf[3] = { reg, value >> 8, value };
98 
99 	return i2c_write_dt(&config->i2c, buf, sizeof(buf));
100 }
101 
lm77_sensor_value_to_temp(const struct sensor_value * val,int16_t * temp)102 static void lm77_sensor_value_to_temp(const struct sensor_value *val, int16_t *temp)
103 {
104 	/* Integer part in degrees Celsius (LSB = 0.5 degrees Celsius) */
105 	*temp = val->val1 << 1;
106 
107 	/* Fractional part in micro degrees Celsius */
108 	*temp += (val->val2 * 2) / 1000000;
109 
110 	/* Shift up to include "status" bits */
111 	*temp <<= 3;
112 }
113 
lm77_temp_to_sensor_value(int16_t temp,struct sensor_value * val)114 static void lm77_temp_to_sensor_value(int16_t temp, struct sensor_value *val)
115 {
116 	/* Shift down to remove "status" bits (LSB = 0.5 degrees Celsius) */
117 	temp = (temp >> 3) * 5;
118 
119 	/* Integer part in degrees Celsius */
120 	val->val1 = temp / 10;
121 
122 	/* Fractional part in micro degrees Celsius */
123 	val->val2 = temp % 10;
124 	val->val2 *= 100000;
125 }
126 
lm77_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)127 static int lm77_attr_set(const struct device *dev, enum sensor_channel chan,
128 			 enum sensor_attribute attr, const struct sensor_value *val)
129 {
130 	int16_t temp = 0;
131 	uint8_t reg;
132 	int err;
133 
134 	__ASSERT_NO_MSG(val != NULL);
135 
136 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
137 		return -ENOTSUP;
138 	}
139 
140 	switch (attr) {
141 	case SENSOR_ATTR_LOWER_THRESH:
142 		reg = LM77_REG_TLOW;
143 		break;
144 	case SENSOR_ATTR_UPPER_THRESH:
145 		reg = LM77_REG_THIGH;
146 		break;
147 	case SENSOR_ATTR_ALERT:
148 		reg = LM77_REG_TCRIT;
149 		break;
150 	case SENSOR_ATTR_HYSTERESIS:
151 		reg = LM77_REG_THYST;
152 		break;
153 	default:
154 		return -ENOTSUP;
155 	}
156 
157 	lm77_sensor_value_to_temp(val, &temp);
158 
159 	err = lm77_write_temp(dev, reg, temp);
160 	if (err < 0) {
161 		LOG_ERR("failed to write register 0x%02x (err %d)", reg, err);
162 		return err;
163 	}
164 
165 	return 0;
166 }
167 
lm77_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)168 static int lm77_attr_get(const struct device *dev, enum sensor_channel chan,
169 			 enum sensor_attribute attr, struct sensor_value *val)
170 {
171 	int16_t temp;
172 	uint8_t reg;
173 	int err;
174 
175 	__ASSERT_NO_MSG(val != NULL);
176 
177 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
178 		return -ENOTSUP;
179 	}
180 
181 	switch (attr) {
182 	case SENSOR_ATTR_LOWER_THRESH:
183 		reg = LM77_REG_TLOW;
184 		break;
185 	case SENSOR_ATTR_UPPER_THRESH:
186 		reg = LM77_REG_THIGH;
187 		break;
188 	case SENSOR_ATTR_ALERT:
189 		reg = LM77_REG_TCRIT;
190 		break;
191 	case SENSOR_ATTR_HYSTERESIS:
192 		reg = LM77_REG_THYST;
193 		break;
194 	default:
195 		return -ENOTSUP;
196 	}
197 
198 	err = lm77_read_temp(dev, reg, &temp);
199 	if (err < 0) {
200 		LOG_ERR("failed to read register 0x%02x (err %d)", reg, err);
201 		return err;
202 	}
203 
204 	lm77_temp_to_sensor_value(temp, val);
205 
206 	return 0;
207 }
208 
209 #if LM77_TRIGGER_SUPPORT
lm77_trigger_set(const struct device * dev,const struct sensor_trigger * trig,sensor_trigger_handler_t handler)210 static int lm77_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
211 			    sensor_trigger_handler_t handler)
212 {
213 	const struct lm77_config *config = dev->config;
214 	struct lm77_data *data = dev->data;
215 	gpio_flags_t flags;
216 	int err;
217 
218 	__ASSERT_NO_MSG(trig != NULL);
219 
220 	if (trig->type != SENSOR_TRIG_THRESHOLD || trig->chan != SENSOR_CHAN_AMBIENT_TEMP ||
221 	    config->int_gpio.port == NULL) {
222 		return -ENOTSUP;
223 	}
224 
225 	if (handler != NULL) {
226 		flags = GPIO_INT_EDGE_TO_ACTIVE;
227 	} else {
228 		flags = GPIO_INT_DISABLE;
229 	}
230 
231 	err = gpio_pin_interrupt_configure_dt(&config->int_gpio, flags);
232 	if (err < 0) {
233 		LOG_ERR("failed to configure INT GPIO IRQ (err %d)", err);
234 		return err;
235 	}
236 
237 	data->trigger = trig;
238 	data->trigger_handler = handler;
239 
240 	return 0;
241 }
242 
lm77_trigger_work_handler(struct k_work * item)243 static void lm77_trigger_work_handler(struct k_work *item)
244 {
245 	struct lm77_data *data = CONTAINER_OF(item, struct lm77_data, work);
246 	sensor_trigger_handler_t handler = data->trigger_handler;
247 
248 	if (handler != NULL) {
249 		handler(data->dev, (struct sensor_trigger *)data->trigger);
250 	}
251 }
252 
lm77_int_gpio_callback_handler(const struct device * port,struct gpio_callback * cb,gpio_port_pins_t pins)253 static void lm77_int_gpio_callback_handler(const struct device *port,
254 					   struct gpio_callback *cb,
255 					   gpio_port_pins_t pins)
256 {
257 	struct lm77_data *data = CONTAINER_OF(cb, struct lm77_data, int_gpio_cb);
258 
259 	ARG_UNUSED(port);
260 	ARG_UNUSED(pins);
261 
262 	k_work_submit_to_queue(&data->workq, &data->work);
263 }
264 #endif /* LM77_TRIGGER_SUPPORT */
265 
lm77_sample_fetch(const struct device * dev,enum sensor_channel chan)266 static int lm77_sample_fetch(const struct device *dev, enum sensor_channel chan)
267 {
268 	struct lm77_data *data = dev->data;
269 	int16_t temp;
270 	int err;
271 
272 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
273 		return -ENOTSUP;
274 	}
275 
276 	err = lm77_read_temp(dev, LM77_REG_TEMP, &temp);
277 	if (err < 0) {
278 		LOG_ERR("failed to read temperature (err %d)", err);
279 		return err;
280 	}
281 
282 	data->temp = temp;
283 
284 	return 0;
285 }
286 
lm77_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)287 static int lm77_channel_get(const struct device *dev, enum sensor_channel chan,
288 			    struct sensor_value *val)
289 {
290 	struct lm77_data *data = dev->data;
291 
292 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
293 		return -ENOTSUP;
294 	}
295 
296 	lm77_temp_to_sensor_value(data->temp, val);
297 
298 	return 0;
299 }
300 
301 static const struct sensor_driver_api lm77_driver_api = {
302 	.attr_set = lm77_attr_set,
303 	.attr_get = lm77_attr_get,
304 #if LM77_TRIGGER_SUPPORT
305 	.trigger_set = lm77_trigger_set,
306 #endif /* LM77_TRIGGER_SUPPORT */
307 	.sample_fetch = lm77_sample_fetch,
308 	.channel_get = lm77_channel_get,
309 };
310 
lm77_init(const struct device * dev)311 static int lm77_init(const struct device *dev)
312 {
313 	const struct lm77_config *config = dev->config;
314 	int err;
315 #if LM77_TRIGGER_SUPPORT
316 	struct lm77_data *data = dev->data;
317 #endif /* LM77_TRIGGER_SUPPORT */
318 
319 	if (!device_is_ready(config->i2c.bus)) {
320 		LOG_ERR("I2c bus not ready");
321 		return -EINVAL;
322 	}
323 
324 	err = lm77_write_config(dev, config->config_dt.reg);
325 	if (err < 0) {
326 		LOG_ERR("failed to write configuration (err %d)", err);
327 		return err;
328 	}
329 
330 #if LM77_TRIGGER_SUPPORT
331 	data->dev = dev;
332 	k_work_queue_start(&data->workq, data->stack, K_THREAD_STACK_SIZEOF(data->stack),
333 			   CONFIG_LM77_TRIGGER_THREAD_PRIO, NULL);
334 	k_thread_name_set(&data->workq.thread, "lm77_trigger");
335 	k_work_init(&data->work, lm77_trigger_work_handler);
336 
337 	if (config->int_gpio.port != NULL) {
338 		if (!gpio_is_ready_dt(&config->int_gpio)) {
339 			LOG_ERR("INT GPIO not ready");
340 			return -EINVAL;
341 		}
342 
343 		err = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
344 		if (err < 0) {
345 			LOG_ERR("failed to configure INT GPIO (err %d)", err);
346 			return err;
347 		}
348 
349 		gpio_init_callback(&data->int_gpio_cb, lm77_int_gpio_callback_handler,
350 				   BIT(config->int_gpio.pin));
351 
352 		err = gpio_add_callback(config->int_gpio.port, &data->int_gpio_cb);
353 		if (err < 0) {
354 			LOG_ERR("failed to add INT GPIO callback (err %d)", err);
355 			return err;
356 		}
357 	}
358 #endif /* LM77_TRIGGER_SUPPORT */
359 
360 	return 0;
361 }
362 
363 #ifdef CONFIG_PM_DEVICE
lm77_pm_action(const struct device * dev,enum pm_device_action action)364 static int lm77_pm_action(const struct device *dev,
365 			  enum pm_device_action action)
366 {
367 	const struct lm77_config *config = dev->config;
368 	union lm77_reg_config creg = config->config_dt;
369 	int err;
370 
371 	switch (action) {
372 	case PM_DEVICE_ACTION_SUSPEND:
373 		creg.shutdown = 1;
374 		break;
375 	case PM_DEVICE_ACTION_RESUME:
376 		creg.shutdown = 0;
377 		break;
378 	default:
379 		return -ENOTSUP;
380 	}
381 
382 	err = lm77_write_config(dev, creg.reg);
383 	if (err < 0) {
384 		LOG_ERR("failed to write configuration (err %d)", err);
385 		return err;
386 	}
387 
388 	return 0;
389 }
390 #endif /* CONFIG_PM_DEVICE */
391 
392 #if LM77_TRIGGER_SUPPORT
393 #define LM77_INT_GPIO_INIT(n) .int_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, { 0 })
394 #else /* LM77_TRIGGER_SUPPORT */
395 #define LM77_INT_GPIO_INIT(n)
396 #endif /* ! LM77_TRIGGER_SUPPORT */
397 
398 #define LM77_INIT(n)							\
399 	static struct lm77_data lm77_data_##n;				\
400 									\
401 	static const struct lm77_config lm77_config_##n = {		\
402 		.i2c = I2C_DT_SPEC_INST_GET(n),				\
403 		.config_dt = {						\
404 			.shutdown = 0,					\
405 			.int_mode = DT_INST_NODE_HAS_PROP(n, int_gpios), \
406 			.tcrita_pol = DT_INST_PROP(n, tcrita_inverted), \
407 			.int_pol = DT_INST_PROP(n, int_inverted),	\
408 			.fault_queue = DT_INST_PROP(n, enable_fault_queue), \
409 			.reserved = 0,					\
410 		},							\
411 		LM77_INT_GPIO_INIT(n)					\
412 	};								\
413 									\
414 	PM_DEVICE_DT_INST_DEFINE(n, lm77_pm_action);			\
415 									\
416 	SENSOR_DEVICE_DT_INST_DEFINE(n, lm77_init,			\
417 			      PM_DEVICE_DT_INST_GET(n),			\
418 			      &lm77_data_##n,				\
419 			      &lm77_config_##n, POST_KERNEL,		\
420 			      CONFIG_SENSOR_INIT_PRIORITY,		\
421 			      &lm77_driver_api);
422 
423 DT_INST_FOREACH_STATUS_OKAY(LM77_INIT)
424