1 /*
2  * Copyright (c) 2022, Maxmillion McLaughlin
3  * Copyright (c) 2020, SER Consulting LLC / Steven Riedl
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT microchip_mcp9600
9 
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/i2c.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/logging/log.h>
15 
16 LOG_MODULE_REGISTER(MCP9600, CONFIG_SENSOR_LOG_LEVEL);
17 
18 #define MCP9600_REG_TEMP_HOT 0x00
19 
20 #define MCP9600_REG_TEMP_DIFF 0x01
21 #define MCP9600_REG_TEMP_COLD 0x02
22 #define MCP9600_REG_RAW_ADC   0x03
23 
24 #define MCP9600_REG_STATUS 0x04
25 
26 #define MCP9600_REG_TC_CONFIG  0x05
27 #define MCP9600_REG_DEV_CONFIG 0x06
28 
29 #define MCP9600_REG_A1_CONFIG 0x08
30 #define MCP9600_REG_A2_CONFIG 0x09
31 #define MCP9600_REG_A3_CONFIG 0x0A
32 #define MCP9600_REG_A4_CONFIG 0x0B
33 
34 #define MCP9600_A1_HYST	 0x0C
35 #define MCP9600_A2_HYST	 0x0D
36 #define MCP9600_A3_HYST	 0x0E
37 #define MCP9600_A4_HYST	 0x0F
38 
39 #define MCP9600_A1_LIMIT 0x10
40 #define MCP9600_A2_LIMIT 0x11
41 #define MCP9600_A3_LIMIT 0x12
42 #define MCP9600_A4_LIMIT 0x13
43 
44 #define MCP9600_REG_ID_REVISION 0x20
45 
46 struct mcp9600_data {
47 	int32_t temp;
48 };
49 
50 struct mcp9600_config {
51 	const struct i2c_dt_spec bus;
52 };
53 
mcp9600_reg_read(const struct device * dev,uint8_t start,uint8_t * buf,int size)54 static int mcp9600_reg_read(const struct device *dev, uint8_t start, uint8_t *buf, int size)
55 {
56 	const struct mcp9600_config *cfg = dev->config;
57 
58 	return i2c_burst_read_dt(&cfg->bus, start, buf, size);
59 }
60 
mcp9600_sample_fetch(const struct device * dev,enum sensor_channel chan)61 static int mcp9600_sample_fetch(const struct device *dev, enum sensor_channel chan)
62 {
63 	struct mcp9600_data *data = dev->data;
64 	uint8_t buf[2];
65 	int ret;
66 
67 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
68 		LOG_ERR("Unsupported sensor channel");
69 		return -ENOTSUP;
70 	}
71 
72 	/* read signed 16 bit double-buffered register value */
73 	ret = mcp9600_reg_read(dev, MCP9600_REG_TEMP_HOT, buf, sizeof(buf));
74 	if (ret < 0) {
75 		data->temp = 1;
76 		return ret;
77 	}
78 
79 	/* device's hot junction register is a signed int */
80 	data->temp = (int32_t)(int16_t)(buf[0] << 8) | buf[1];
81 
82 	/* 0.0625C resolution per LSB */
83 	data->temp *= 62500;
84 
85 	return 0;
86 }
87 
mcp9600_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)88 static int mcp9600_channel_get(const struct device *dev, enum sensor_channel chan,
89 			       struct sensor_value *val)
90 {
91 	struct mcp9600_data *data = dev->data;
92 
93 	if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
94 		return -ENOTSUP;
95 	}
96 
97 	if (data->temp == 1) {
98 		return -EINVAL;
99 	}
100 
101 	val->val1 = data->temp / 1000000;
102 	val->val2 = data->temp % 1000000;
103 
104 	return 0;
105 }
106 
107 static DEVICE_API(sensor, mcp9600_api) = {
108 	.sample_fetch = mcp9600_sample_fetch,
109 	.channel_get = mcp9600_channel_get,
110 };
111 
mcp9600_init(const struct device * dev)112 static int mcp9600_init(const struct device *dev)
113 {
114 	const struct mcp9600_config *cfg = dev->config;
115 	uint8_t buf[2];
116 	int ret;
117 
118 	if (!i2c_is_ready_dt(&cfg->bus)) {
119 		LOG_ERR("mcp9600 i2c bus %s not ready", cfg->bus.bus->name);
120 		return -ENODEV;
121 	}
122 
123 	ret = mcp9600_reg_read(dev, MCP9600_REG_ID_REVISION, buf, sizeof(buf));
124 	LOG_DBG("id: 0x%02x version: 0x%02x", buf[0], buf[1]);
125 
126 	return ret;
127 }
128 
129 #define MCP9600_DEFINE(id)                                                                         \
130 	static struct mcp9600_data mcp9600_data_##id;                                              \
131                                                                                                    \
132 	static const struct mcp9600_config mcp9600_config_##id = {                                 \
133 		.bus = I2C_DT_SPEC_INST_GET(id),                                                   \
134 	};                                                                                         \
135                                                                                                    \
136 	SENSOR_DEVICE_DT_INST_DEFINE(id, mcp9600_init, NULL, &mcp9600_data_##id,                   \
137 				     &mcp9600_config_##id, POST_KERNEL,                            \
138 				     CONFIG_SENSOR_INIT_PRIORITY, &mcp9600_api);
139 
140 DT_INST_FOREACH_STATUS_OKAY(MCP9600_DEFINE)
141