]>
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 | { | |
e765537a | 48 | struct i2c_client **client = iio_priv(indio_dev); |
064a7463 DB |
49 | int ret; |
50 | ||
51 | switch (mask) { | |
52 | case IIO_CHAN_INFO_RAW: | |
0d2f6fd3 CL |
53 | ret = i2c_smbus_read_word_swapped(*client, |
54 | chan->type == IIO_TEMP ? | |
55 | SI7020CMD_TEMP_HOLD : | |
56 | SI7020CMD_RH_HOLD); | |
064a7463 DB |
57 | if (ret < 0) |
58 | return ret; | |
345b4830 | 59 | *val = ret >> 2; |
ecf7e207 NC |
60 | /* |
61 | * Humidity values can slightly exceed the 0-100%RH | |
62 | * range and should be corrected by software | |
63 | */ | |
345b4830 | 64 | if (chan->type == IIO_HUMIDITYRELATIVE) |
ecf7e207 | 65 | *val = clamp_val(*val, 786, 13893); |
064a7463 DB |
66 | return IIO_VAL_INT; |
67 | case IIO_CHAN_INFO_SCALE: | |
68 | if (chan->type == IIO_TEMP) | |
69 | *val = 175720; /* = 175.72 * 1000 */ | |
70 | else | |
71 | *val = 125 * 1000; | |
72 | *val2 = 65536 >> 2; | |
73 | return IIO_VAL_FRACTIONAL; | |
74 | case IIO_CHAN_INFO_OFFSET: | |
75 | /* | |
76 | * Since iio_convert_raw_to_processed_unlocked assumes offset | |
77 | * is an integer we have to round these values and lose | |
78 | * accuracy. | |
79 | * Relative humidity will be 0.0032959% too high and | |
80 | * temperature will be 0.00277344 degrees too high. | |
81 | * This is no big deal because it's within the accuracy of the | |
82 | * sensor. | |
83 | */ | |
84 | if (chan->type == IIO_TEMP) | |
85 | *val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */ | |
86 | else | |
87 | *val = -786; /* = -6 * (65536 >> 2) / 125 */ | |
88 | return IIO_VAL_INT; | |
89 | default: | |
90 | break; | |
91 | } | |
92 | ||
93 | return -EINVAL; | |
94 | } | |
95 | ||
96 | static const struct iio_chan_spec si7020_channels[] = { | |
97 | { | |
98 | .type = IIO_HUMIDITYRELATIVE, | |
99 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
100 | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), | |
101 | }, | |
102 | { | |
103 | .type = IIO_TEMP, | |
104 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
105 | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), | |
106 | } | |
107 | }; | |
108 | ||
109 | static const struct iio_info si7020_info = { | |
110 | .read_raw = si7020_read_raw, | |
111 | .driver_module = THIS_MODULE, | |
112 | }; | |
113 | ||
114 | static int si7020_probe(struct i2c_client *client, | |
115 | const struct i2c_device_id *id) | |
116 | { | |
117 | struct iio_dev *indio_dev; | |
118 | struct i2c_client **data; | |
119 | int ret; | |
120 | ||
121 | if (!i2c_check_functionality(client->adapter, | |
122 | I2C_FUNC_SMBUS_WRITE_BYTE | | |
123 | I2C_FUNC_SMBUS_READ_WORD_DATA)) | |
f8d9d3b4 | 124 | return -EOPNOTSUPP; |
064a7463 DB |
125 | |
126 | /* Reset device, loads default settings. */ | |
127 | ret = i2c_smbus_write_byte(client, SI7020CMD_RESET); | |
128 | if (ret < 0) | |
129 | return ret; | |
130 | /* Wait the maximum power-up time after software reset. */ | |
131 | msleep(15); | |
132 | ||
e01becba | 133 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); |
064a7463 DB |
134 | if (!indio_dev) |
135 | return -ENOMEM; | |
136 | ||
137 | data = iio_priv(indio_dev); | |
138 | *data = client; | |
064a7463 DB |
139 | |
140 | indio_dev->dev.parent = &client->dev; | |
141 | indio_dev->name = dev_name(&client->dev); | |
142 | indio_dev->modes = INDIO_DIRECT_MODE; | |
143 | indio_dev->info = &si7020_info; | |
144 | indio_dev->channels = si7020_channels; | |
145 | indio_dev->num_channels = ARRAY_SIZE(si7020_channels); | |
146 | ||
147 | return devm_iio_device_register(&client->dev, indio_dev); | |
148 | } | |
149 | ||
150 | static const struct i2c_device_id si7020_id[] = { | |
151 | { "si7020", 0 }, | |
920dad0c | 152 | { "th06", 0 }, |
064a7463 DB |
153 | { } |
154 | }; | |
155 | MODULE_DEVICE_TABLE(i2c, si7020_id); | |
156 | ||
b0d80198 PK |
157 | static const struct of_device_id si7020_dt_ids[] = { |
158 | { .compatible = "silabs,si7020" }, | |
159 | { } | |
160 | }; | |
161 | MODULE_DEVICE_TABLE(of, si7020_dt_ids); | |
162 | ||
064a7463 | 163 | static struct i2c_driver si7020_driver = { |
b0d80198 PK |
164 | .driver = { |
165 | .name = "si7020", | |
166 | .of_match_table = of_match_ptr(si7020_dt_ids), | |
167 | }, | |
064a7463 DB |
168 | .probe = si7020_probe, |
169 | .id_table = si7020_id, | |
170 | }; | |
171 | ||
172 | module_i2c_driver(si7020_driver); | |
173 | MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors"); | |
174 | MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); | |
175 | MODULE_LICENSE("GPL"); |