]>
Commit | Line | Data |
---|---|---|
064a7463 DB |
1 | /* |
2 | * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors | |
3 | * Copyright (c) 2013,2014 Uplogix, Inc. | |
4 | * David Barksdale <dbarksdale@uplogix.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | */ | |
15 | ||
16 | /* | |
17 | * The Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors | |
18 | * are i2c devices which have an identical programming interface for | |
19 | * measuring relative humidity and temperature. The Si7013 has an additional | |
20 | * temperature input which this driver does not support. | |
21 | * | |
22 | * Data Sheets: | |
23 | * Si7013: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7013.pdf | |
24 | * Si7020: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf | |
25 | * Si7021: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf | |
26 | */ | |
27 | ||
28 | #include <linux/delay.h> | |
29 | #include <linux/i2c.h> | |
30 | #include <linux/module.h> | |
31 | #include <linux/slab.h> | |
32 | #include <linux/sysfs.h> | |
33 | ||
34 | #include <linux/iio/iio.h> | |
35 | #include <linux/iio/sysfs.h> | |
36 | ||
37 | /* Measure Relative Humidity, Hold Master Mode */ | |
38 | #define SI7020CMD_RH_HOLD 0xE5 | |
39 | /* Measure Temperature, Hold Master Mode */ | |
40 | #define SI7020CMD_TEMP_HOLD 0xE3 | |
41 | /* Software Reset */ | |
42 | #define SI7020CMD_RESET 0xFE | |
43 | ||
44 | static int si7020_read_raw(struct iio_dev *indio_dev, | |
45 | struct iio_chan_spec const *chan, int *val, | |
46 | int *val2, long mask) | |
47 | { | |
e0922e5e | 48 | struct i2c_client *client = iio_priv(indio_dev); |
064a7463 DB |
49 | int ret; |
50 | ||
51 | switch (mask) { | |
52 | case IIO_CHAN_INFO_RAW: | |
e0922e5e | 53 | ret = i2c_smbus_read_word_data(client, |
064a7463 DB |
54 | chan->type == IIO_TEMP ? |
55 | SI7020CMD_TEMP_HOLD : | |
56 | SI7020CMD_RH_HOLD); | |
57 | if (ret < 0) | |
58 | return ret; | |
345b4830 HK |
59 | *val = ret >> 2; |
60 | if (chan->type == IIO_HUMIDITYRELATIVE) | |
61 | *val &= GENMASK(11, 0); | |
064a7463 DB |
62 | return IIO_VAL_INT; |
63 | case IIO_CHAN_INFO_SCALE: | |
64 | if (chan->type == IIO_TEMP) | |
65 | *val = 175720; /* = 175.72 * 1000 */ | |
66 | else | |
67 | *val = 125 * 1000; | |
68 | *val2 = 65536 >> 2; | |
69 | return IIO_VAL_FRACTIONAL; | |
70 | case IIO_CHAN_INFO_OFFSET: | |
71 | /* | |
72 | * Since iio_convert_raw_to_processed_unlocked assumes offset | |
73 | * is an integer we have to round these values and lose | |
74 | * accuracy. | |
75 | * Relative humidity will be 0.0032959% too high and | |
76 | * temperature will be 0.00277344 degrees too high. | |
77 | * This is no big deal because it's within the accuracy of the | |
78 | * sensor. | |
79 | */ | |
80 | if (chan->type == IIO_TEMP) | |
81 | *val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */ | |
82 | else | |
83 | *val = -786; /* = -6 * (65536 >> 2) / 125 */ | |
84 | return IIO_VAL_INT; | |
85 | default: | |
86 | break; | |
87 | } | |
88 | ||
89 | return -EINVAL; | |
90 | } | |
91 | ||
92 | static const struct iio_chan_spec si7020_channels[] = { | |
93 | { | |
94 | .type = IIO_HUMIDITYRELATIVE, | |
95 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
96 | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), | |
97 | }, | |
98 | { | |
99 | .type = IIO_TEMP, | |
100 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
101 | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), | |
102 | } | |
103 | }; | |
104 | ||
105 | static const struct iio_info si7020_info = { | |
106 | .read_raw = si7020_read_raw, | |
107 | .driver_module = THIS_MODULE, | |
108 | }; | |
109 | ||
110 | static int si7020_probe(struct i2c_client *client, | |
111 | const struct i2c_device_id *id) | |
112 | { | |
113 | struct iio_dev *indio_dev; | |
114 | struct i2c_client **data; | |
115 | int ret; | |
116 | ||
117 | if (!i2c_check_functionality(client->adapter, | |
118 | I2C_FUNC_SMBUS_WRITE_BYTE | | |
119 | I2C_FUNC_SMBUS_READ_WORD_DATA)) | |
120 | return -ENODEV; | |
121 | ||
122 | /* Reset device, loads default settings. */ | |
123 | ret = i2c_smbus_write_byte(client, SI7020CMD_RESET); | |
124 | if (ret < 0) | |
125 | return ret; | |
126 | /* Wait the maximum power-up time after software reset. */ | |
127 | msleep(15); | |
128 | ||
129 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*client)); | |
130 | if (!indio_dev) | |
131 | return -ENOMEM; | |
132 | ||
133 | data = iio_priv(indio_dev); | |
134 | *data = client; | |
064a7463 DB |
135 | |
136 | indio_dev->dev.parent = &client->dev; | |
137 | indio_dev->name = dev_name(&client->dev); | |
138 | indio_dev->modes = INDIO_DIRECT_MODE; | |
139 | indio_dev->info = &si7020_info; | |
140 | indio_dev->channels = si7020_channels; | |
141 | indio_dev->num_channels = ARRAY_SIZE(si7020_channels); | |
142 | ||
143 | return devm_iio_device_register(&client->dev, indio_dev); | |
144 | } | |
145 | ||
146 | static const struct i2c_device_id si7020_id[] = { | |
147 | { "si7020", 0 }, | |
148 | { } | |
149 | }; | |
150 | MODULE_DEVICE_TABLE(i2c, si7020_id); | |
151 | ||
152 | static struct i2c_driver si7020_driver = { | |
153 | .driver.name = "si7020", | |
154 | .probe = si7020_probe, | |
155 | .id_table = si7020_id, | |
156 | }; | |
157 | ||
158 | module_i2c_driver(si7020_driver); | |
159 | MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors"); | |
160 | MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); | |
161 | MODULE_LICENSE("GPL"); |