]> Git Repo - linux.git/blob - drivers/iio/humidity/ens210.c
Linux 6.14-rc3
[linux.git] / drivers / iio / humidity / ens210.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * ens210.c - Support for ScioSense ens210 temperature & humidity sensor family
4  *
5  * (7-bit I2C slave address 0x43 ENS210)
6  * (7-bit I2C slave address 0x43 ENS210A)
7  * (7-bit I2C slave address 0x44 ENS211)
8  * (7-bit I2C slave address 0x45 ENS212)
9  * (7-bit I2C slave address 0x46 ENS213A)
10  * (7-bit I2C slave address 0x47 ENS215)
11  *
12  * Datasheet:
13  *  https://www.sciosense.com/wp-content/uploads/2024/04/ENS21x-Datasheet.pdf
14  *  https://www.sciosense.com/wp-content/uploads/2023/12/ENS210-Datasheet.pdf
15  */
16
17 #include <linux/crc7.h>
18 #include <linux/delay.h>
19 #include <linux/i2c.h>
20 #include <linux/iio/iio.h>
21 #include <linux/mod_devicetable.h>
22 #include <linux/module.h>
23 #include <linux/types.h>
24
25 #include <linux/unaligned.h>
26
27 /* register definitions */
28 #define ENS210_REG_PART_ID              0x00
29 #define ENS210_REG_DIE_REV              0x02
30 #define ENS210_REG_UID                  0x04
31 #define ENS210_REG_SYS_CTRL             0x10
32 #define ENS210_REG_SYS_STAT             0x11
33 #define ENS210_REG_SENS_RUN             0x21
34 #define ENS210_REG_SENS_START           0x22
35 #define ENS210_REG_SENS_STOP            0x23
36 #define ENS210_REG_SENS_STAT            0x24
37 #define ENS210_REG_T_VAL                0x30
38 #define ENS210_REG_H_VAL                0x33
39
40 /* value definitions */
41 #define ENS210_SENS_START_T_START               BIT(0)
42 #define ENS210_SENS_START_H_START               BIT(1)
43
44 #define ENS210_SENS_STAT_T_ACTIVE               BIT(0)
45 #define ENS210_SENS_STAT_H_ACTIVE               BIT(1)
46
47 #define ENS210_SYS_CTRL_LOW_POWER_ENABLE        BIT(0)
48 #define ENS210_SYS_CTRL_SYS_RESET               BIT(7)
49
50 #define ENS210_SYS_STAT_SYS_ACTIVE              BIT(0)
51
52 enum ens210_partnumber {
53         ENS210  = 0x0210,
54         ENS210A = 0xa210,
55         ENS211  = 0x0211,
56         ENS212  = 0x0212,
57         ENS213A = 0xa213,
58         ENS215  = 0x0215,
59 };
60
61 /**
62  * struct ens210_chip_info - Humidity/Temperature chip specific information
63  * @name:               name of device
64  * @part_id:            chip identifier
65  * @conv_time_msec:     time for conversion calculation in m/s
66  */
67 struct ens210_chip_info {
68         const char *name;
69         enum ens210_partnumber part_id;
70         unsigned int conv_time_msec;
71 };
72
73 /**
74  * struct ens210_data - Humidity/Temperature sensor device structure
75  * @client:     i2c client
76  * @chip_info:  chip specific information
77  * @lock:       lock protecting against simultaneous callers of get_measurement
78  *              since multiple uninterrupted transactions are required
79  */
80 struct ens210_data {
81         struct i2c_client *client;
82         const struct ens210_chip_info *chip_info;
83         struct mutex lock;
84 };
85
86 /* calculate 17-bit crc7 */
87 static u8 ens210_crc7(u32 val)
88 {
89         unsigned int val_be = (val & 0x1ffff) >> 0x8;
90
91         return crc7_be(0xde, (u8 *)&val_be, 3) >> 1;
92 }
93
94 static int ens210_get_measurement(struct iio_dev *indio_dev, bool temp, int *val)
95 {
96         struct ens210_data *data = iio_priv(indio_dev);
97         struct device *dev = &data->client->dev;
98         u32 regval;
99         u8 regval_le[3];
100         int ret;
101
102         /* assert read */
103         ret = i2c_smbus_write_byte_data(data->client, ENS210_REG_SENS_START,
104                                         temp ? ENS210_SENS_START_T_START :
105                                                ENS210_SENS_START_H_START);
106         if (ret)
107                 return ret;
108
109         /* wait for conversion to be ready */
110         msleep(data->chip_info->conv_time_msec);
111
112         ret = i2c_smbus_read_byte_data(data->client, ENS210_REG_SENS_STAT);
113         if (ret < 0)
114                 return ret;
115
116         /* perform read */
117         ret = i2c_smbus_read_i2c_block_data(
118                 data->client, temp ? ENS210_REG_T_VAL : ENS210_REG_H_VAL, 3,
119                 regval_le);
120         if (ret < 0) {
121                 dev_err(dev, "failed to read register");
122                 return -EIO;
123         }
124         if (ret != 3) {
125                 dev_err(dev, "expected 3 bytes, received %d\n", ret);
126                 return -EIO;
127         }
128
129         regval = get_unaligned_le24(regval_le);
130         if (ens210_crc7(regval) != ((regval >> 17) & 0x7f)) {
131                 dev_err(dev, "invalid crc\n");
132                 return -EIO;
133         }
134
135         if (!((regval >> 16) & 0x1)) {
136                 dev_err(dev, "data is not valid");
137                 return -EIO;
138         }
139
140         *val = regval & GENMASK(15, 0);
141         return IIO_VAL_INT;
142 }
143
144 static int ens210_read_raw(struct iio_dev *indio_dev,
145                            struct iio_chan_spec const *channel, int *val,
146                            int *val2, long mask)
147 {
148         struct ens210_data *data = iio_priv(indio_dev);
149         int ret;
150
151         switch (mask) {
152         case IIO_CHAN_INFO_RAW:
153                 scoped_guard(mutex, &data->lock) {
154                         ret = ens210_get_measurement(
155                                 indio_dev, channel->type == IIO_TEMP, val);
156                         if (ret)
157                                 return ret;
158                         return IIO_VAL_INT;
159                 }
160                 return -EINVAL; /* compiler warning workaround */
161         case IIO_CHAN_INFO_SCALE:
162                 if (channel->type == IIO_TEMP) {
163                         *val = 15;
164                         *val2 = 625000;
165                 } else {
166                         *val = 1;
167                         *val2 = 953125;
168                 }
169                 return IIO_VAL_INT_PLUS_MICRO;
170         case IIO_CHAN_INFO_OFFSET:
171                 *val = -17481;
172                 *val2 = 600000;
173                 return IIO_VAL_INT_PLUS_MICRO;
174         default:
175                 return -EINVAL;
176         }
177 }
178
179 static const struct iio_chan_spec ens210_channels[] = {
180         {
181                 .type = IIO_TEMP,
182                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
183                                       BIT(IIO_CHAN_INFO_SCALE) |
184                                       BIT(IIO_CHAN_INFO_OFFSET),
185         },
186         {
187                 .type = IIO_HUMIDITYRELATIVE,
188                 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
189                                       BIT(IIO_CHAN_INFO_SCALE),
190         }
191 };
192
193 static const struct iio_info ens210_info = {
194         .read_raw = ens210_read_raw,
195 };
196
197 static int ens210_probe(struct i2c_client *client)
198 {
199         struct ens210_data *data;
200         struct iio_dev *indio_dev;
201         uint16_t part_id;
202         int ret;
203
204         if (!i2c_check_functionality(client->adapter,
205                                      I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
206                                      I2C_FUNC_SMBUS_WRITE_BYTE |
207                                      I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
208                 return dev_err_probe(&client->dev, -EOPNOTSUPP,
209                         "adapter does not support some i2c transactions\n");
210         }
211
212         indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
213         if (!indio_dev)
214                 return -ENOMEM;
215
216         data = iio_priv(indio_dev);
217         data->client = client;
218         mutex_init(&data->lock);
219         data->chip_info = i2c_get_match_data(client);
220
221         ret = devm_regulator_get_enable(&client->dev, "vdd");
222         if (ret)
223                 return ret;
224
225         /* reset device */
226         ret = i2c_smbus_write_byte_data(client, ENS210_REG_SYS_CTRL,
227                                         ENS210_SYS_CTRL_SYS_RESET);
228         if (ret)
229                 return ret;
230
231         /* wait for device to become active */
232         usleep_range(4000, 5000);
233
234         /* disable low power mode */
235         ret = i2c_smbus_write_byte_data(client, ENS210_REG_SYS_CTRL, 0x00);
236         if (ret)
237                 return ret;
238
239         /* wait for device to finish */
240         usleep_range(4000, 5000);
241
242         /* get part_id */
243         ret = i2c_smbus_read_word_data(client, ENS210_REG_PART_ID);
244         if (ret < 0)
245                 return ret;
246         part_id = ret;
247
248         if (part_id != data->chip_info->part_id) {
249                 dev_info(&client->dev,
250                          "Part ID does not match (0x%04x != 0x%04x)\n", part_id,
251                          data->chip_info->part_id);
252         }
253
254         /* reenable low power */
255         ret = i2c_smbus_write_byte_data(client, ENS210_REG_SYS_CTRL,
256                                         ENS210_SYS_CTRL_LOW_POWER_ENABLE);
257         if (ret)
258                 return ret;
259
260         indio_dev->name = data->chip_info->name;
261         indio_dev->modes = INDIO_DIRECT_MODE;
262         indio_dev->channels = ens210_channels;
263         indio_dev->num_channels = ARRAY_SIZE(ens210_channels);
264         indio_dev->info = &ens210_info;
265
266         return devm_iio_device_register(&client->dev, indio_dev);
267 }
268
269 static const struct ens210_chip_info ens210_chip_info_data = {
270         .name = "ens210",
271         .part_id = ENS210,
272         .conv_time_msec = 130,
273 };
274
275 static const struct ens210_chip_info ens210a_chip_info_data = {
276         .name = "ens210a",
277         .part_id = ENS210A,
278         .conv_time_msec = 130,
279 };
280
281 static const struct ens210_chip_info ens211_chip_info_data = {
282         .name = "ens211",
283         .part_id = ENS211,
284         .conv_time_msec = 32,
285 };
286
287 static const struct ens210_chip_info ens212_chip_info_data = {
288         .name = "ens212",
289         .part_id = ENS212,
290         .conv_time_msec = 32,
291 };
292
293 static const struct ens210_chip_info ens213a_chip_info_data = {
294         .name = "ens213a",
295         .part_id = ENS213A,
296         .conv_time_msec = 130,
297 };
298
299 static const struct ens210_chip_info ens215_chip_info_data = {
300         .name = "ens215",
301         .part_id = ENS215,
302         .conv_time_msec = 130,
303 };
304
305 static const struct of_device_id ens210_of_match[] = {
306         { .compatible = "sciosense,ens210", .data = &ens210_chip_info_data },
307         { .compatible = "sciosense,ens210a", .data = &ens210a_chip_info_data },
308         { .compatible = "sciosense,ens211", .data = &ens211_chip_info_data },
309         { .compatible = "sciosense,ens212", .data = &ens212_chip_info_data },
310         { .compatible = "sciosense,ens213a", .data = &ens213a_chip_info_data },
311         { .compatible = "sciosense,ens215", .data = &ens215_chip_info_data },
312         { }
313 };
314 MODULE_DEVICE_TABLE(of, ens210_of_match);
315
316 static const struct i2c_device_id ens210_id_table[] = {
317         { "ens210", (kernel_ulong_t)&ens210_chip_info_data },
318         { "ens210a", (kernel_ulong_t)&ens210a_chip_info_data },
319         { "ens211", (kernel_ulong_t)&ens211_chip_info_data },
320         { "ens212", (kernel_ulong_t)&ens212_chip_info_data },
321         { "ens213a", (kernel_ulong_t)&ens213a_chip_info_data },
322         { "ens215", (kernel_ulong_t)&ens215_chip_info_data },
323         { }
324 };
325 MODULE_DEVICE_TABLE(i2c, ens210_id_table);
326
327 static struct i2c_driver ens210_driver = {
328         .probe = ens210_probe,
329         .id_table = ens210_id_table,
330         .driver = {
331                 .name = "ens210",
332                 .of_match_table = ens210_of_match,
333         },
334 };
335 module_i2c_driver(ens210_driver);
336
337 MODULE_DESCRIPTION("ScioSense ENS210 temperature and humidity sensor driver");
338 MODULE_AUTHOR("Joshua Felmeden <[email protected]>");
339 MODULE_LICENSE("GPL");
This page took 0.049894 seconds and 4 git commands to generate.