1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 /*
4  * Copyright (c) Rajat Khandelwal <rajat.khandelwal@linux.intel.com>
5  *
6  * Maxim MAX30208 digital temperature sensor with 0.1°C accuracy
7  * (7-bit I2C slave address (0x50 - 0x53))
8  */
9 
10 #include <linux/bitops.h>
11 #include <linux/delay.h>
12 #include <linux/iio/iio.h>
13 #include <linux/i2c.h>
14 #include <linux/module.h>
15 #include <linux/types.h>
16 
17 #define MAX30208_STATUS			0x00
18 #define MAX30208_STATUS_TEMP_RDY	BIT(0)
19 #define MAX30208_INT_ENABLE		0x01
20 #define MAX30208_INT_ENABLE_TEMP_RDY	BIT(0)
21 
22 #define MAX30208_FIFO_OVF_CNTR		0x06
23 #define MAX30208_FIFO_DATA_CNTR		0x07
24 #define MAX30208_FIFO_DATA		0x08
25 
26 #define MAX30208_FIFO_CONFIG		0x0a
27 #define MAX30208_FIFO_CONFIG_RO		BIT(1)
28 
29 #define MAX30208_SYSTEM_CTRL		0x0c
30 #define MAX30208_SYSTEM_CTRL_RESET	0x01
31 
32 #define MAX30208_TEMP_SENSOR_SETUP	0x14
33 #define MAX30208_TEMP_SENSOR_SETUP_CONV	BIT(0)
34 
35 struct max30208_data {
36 	struct i2c_client *client;
37 	struct iio_dev *indio_dev;
38 	struct mutex lock; /* Lock to prevent concurrent reads of temperature readings */
39 };
40 
41 static const struct iio_chan_spec max30208_channels[] = {
42 	{
43 		.type = IIO_TEMP,
44 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
45 	},
46 };
47 
48 /**
49  * max30208_request() - Request a reading
50  * @data: Struct comprising member elements of the device
51  *
52  * Requests a reading from the device and waits until the conversion is ready.
53  */
54 static int max30208_request(struct max30208_data *data)
55 {
56 	/*
57 	 * Sensor can take up to 500 ms to respond so execute a total of
58 	 * 10 retries to give the device sufficient time.
59 	 */
60 	int retries = 10;
61 	u8 regval;
62 	int ret;
63 
64 	ret = i2c_smbus_read_byte_data(data->client, MAX30208_TEMP_SENSOR_SETUP);
65 	if (ret < 0)
66 		return ret;
67 
68 	regval = ret | MAX30208_TEMP_SENSOR_SETUP_CONV;
69 
70 	ret = i2c_smbus_write_byte_data(data->client, MAX30208_TEMP_SENSOR_SETUP, regval);
71 	if (ret)
72 		return ret;
73 
74 	while (retries--) {
75 		ret = i2c_smbus_read_byte_data(data->client, MAX30208_STATUS);
76 		if (ret < 0)
77 			return ret;
78 
79 		if (ret & MAX30208_STATUS_TEMP_RDY)
80 			return 0;
81 
82 		msleep(50);
83 	}
84 	dev_err(&data->client->dev, "Temperature conversion failed\n");
85 
86 	return -ETIMEDOUT;
87 }
88 
89 static int max30208_update_temp(struct max30208_data *data)
90 {
91 	u8 data_count;
92 	int ret;
93 
94 	mutex_lock(&data->lock);
95 
96 	ret = max30208_request(data);
97 	if (ret)
98 		goto unlock;
99 
100 	ret = i2c_smbus_read_byte_data(data->client, MAX30208_FIFO_OVF_CNTR);
101 	if (ret < 0)
102 		goto unlock;
103 	else if (!ret) {
104 		ret = i2c_smbus_read_byte_data(data->client, MAX30208_FIFO_DATA_CNTR);
105 		if (ret < 0)
106 			goto unlock;
107 
108 		data_count = ret;
109 	} else
110 		data_count = 1;
111 
112 	while (data_count) {
113 		ret = i2c_smbus_read_word_swapped(data->client, MAX30208_FIFO_DATA);
114 		if (ret < 0)
115 			goto unlock;
116 
117 		data_count--;
118 	}
119 
120 unlock:
121 	mutex_unlock(&data->lock);
122 	return ret;
123 }
124 
125 /**
126  * max30208_config_setup() - Set up FIFO configuration register
127  * @data: Struct comprising member elements of the device
128  *
129  * Sets the rollover bit to '1' to enable overwriting FIFO during overflow.
130  */
131 static int max30208_config_setup(struct max30208_data *data)
132 {
133 	u8 regval;
134 	int ret;
135 
136 	ret = i2c_smbus_read_byte_data(data->client, MAX30208_FIFO_CONFIG);
137 	if (ret < 0)
138 		return ret;
139 
140 	regval = ret | MAX30208_FIFO_CONFIG_RO;
141 
142 	ret = i2c_smbus_write_byte_data(data->client, MAX30208_FIFO_CONFIG, regval);
143 	if (ret)
144 		return ret;
145 
146 	return 0;
147 }
148 
149 static int max30208_read(struct iio_dev *indio_dev,
150 			 struct iio_chan_spec const *chan,
151 			 int *val, int *val2, long mask)
152 {
153 	struct max30208_data *data = iio_priv(indio_dev);
154 	int ret;
155 
156 	switch (mask) {
157 	case IIO_CHAN_INFO_RAW:
158 		ret = max30208_update_temp(data);
159 		if (ret < 0)
160 			return ret;
161 
162 		*val = sign_extend32(ret, 15);
163 		return IIO_VAL_INT;
164 
165 	case IIO_CHAN_INFO_SCALE:
166 		*val = 5;
167 		return IIO_VAL_INT;
168 
169 	default:
170 		return -EINVAL;
171 	}
172 }
173 
174 static const struct iio_info max30208_info = {
175 	.read_raw = max30208_read,
176 };
177 
178 static int max30208_probe(struct i2c_client *i2c)
179 {
180 	struct device *dev = &i2c->dev;
181 	struct max30208_data *data;
182 	struct iio_dev *indio_dev;
183 	int ret;
184 
185 	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
186 	if (!indio_dev)
187 		return -ENOMEM;
188 
189 	data = iio_priv(indio_dev);
190 	data->client = i2c;
191 	mutex_init(&data->lock);
192 
193 	indio_dev->name = "max30208";
194 	indio_dev->channels = max30208_channels;
195 	indio_dev->num_channels = ARRAY_SIZE(max30208_channels);
196 	indio_dev->info = &max30208_info;
197 	indio_dev->modes = INDIO_DIRECT_MODE;
198 
199 	ret = i2c_smbus_write_byte_data(data->client, MAX30208_SYSTEM_CTRL,
200 					MAX30208_SYSTEM_CTRL_RESET);
201 	if (ret) {
202 		dev_err(dev, "Failure in performing reset\n");
203 		return ret;
204 	}
205 
206 	msleep(50);
207 
208 	ret = max30208_config_setup(data);
209 	if (ret)
210 		return ret;
211 
212 	ret = devm_iio_device_register(dev, indio_dev);
213 	if (ret) {
214 		dev_err(dev, "Failed to register IIO device\n");
215 		return ret;
216 	}
217 
218 	return 0;
219 }
220 
221 static const struct i2c_device_id max30208_id_table[] = {
222 	{ "max30208" },
223 	{ }
224 };
225 MODULE_DEVICE_TABLE(i2c, max30208_id_table);
226 
227 static const struct acpi_device_id max30208_acpi_match[] = {
228 	{ "MAX30208" },
229 	{ }
230 };
231 MODULE_DEVICE_TABLE(acpi, max30208_acpi_match);
232 
233 static const struct of_device_id max30208_of_match[] = {
234 	{ .compatible = "maxim,max30208" },
235 	{ }
236 };
237 MODULE_DEVICE_TABLE(of, max30208_of_match);
238 
239 static struct i2c_driver max30208_driver = {
240 	.driver = {
241 		.name = "max30208",
242 		.of_match_table = max30208_of_match,
243 		.acpi_match_table = max30208_acpi_match,
244 	},
245 	.probe_new = max30208_probe,
246 	.id_table = max30208_id_table,
247 };
248 module_i2c_driver(max30208_driver);
249 
250 MODULE_AUTHOR("Rajat Khandelwal <rajat.khandelwal@linux.intel.com>");
251 MODULE_DESCRIPTION("Maxim MAX30208 digital temperature sensor");
252 MODULE_LICENSE("GPL");
253