Commit | Line | Data |
---|---|---|
366a3270 TB |
1 | /** |
2 | * Aosong AM2315 relative humidity and temperature | |
3 | * | |
4 | * Copyright (c) 2016, Intel Corporation. | |
5 | * | |
6 | * This file is subject to the terms and conditions of version 2 of | |
7 | * the GNU General Public License. See the file COPYING in the main | |
8 | * directory of this archive for more details. | |
9 | * | |
10 | * 7-bit I2C address: 0x5C. | |
11 | */ | |
12 | ||
13 | #include <linux/acpi.h> | |
14 | #include <linux/delay.h> | |
15 | #include <linux/i2c.h> | |
16 | #include <linux/kernel.h> | |
17 | #include <linux/module.h> | |
0d96d5ea | 18 | #include <linux/iio/buffer.h> |
366a3270 TB |
19 | #include <linux/iio/iio.h> |
20 | #include <linux/iio/sysfs.h> | |
0d96d5ea TB |
21 | #include <linux/iio/trigger_consumer.h> |
22 | #include <linux/iio/triggered_buffer.h> | |
366a3270 TB |
23 | |
24 | #define AM2315_REG_HUM_MSB 0x00 | |
25 | #define AM2315_REG_HUM_LSB 0x01 | |
26 | #define AM2315_REG_TEMP_MSB 0x02 | |
27 | #define AM2315_REG_TEMP_LSB 0x03 | |
28 | ||
29 | #define AM2315_FUNCTION_READ 0x03 | |
30 | #define AM2315_HUM_OFFSET 2 | |
31 | #define AM2315_TEMP_OFFSET 4 | |
0d96d5ea | 32 | #define AM2315_ALL_CHANNEL_MASK GENMASK(1, 0) |
366a3270 TB |
33 | |
34 | #define AM2315_DRIVER_NAME "am2315" | |
35 | ||
36 | struct am2315_data { | |
37 | struct i2c_client *client; | |
38 | struct mutex lock; | |
0d96d5ea | 39 | s16 buffer[8]; /* 2x16-bit channels + 2x16 padding + 4x16 timestamp */ |
366a3270 TB |
40 | }; |
41 | ||
42 | struct am2315_sensor_data { | |
43 | s16 hum_data; | |
44 | s16 temp_data; | |
45 | }; | |
46 | ||
47 | static const struct iio_chan_spec am2315_channels[] = { | |
48 | { | |
49 | .type = IIO_HUMIDITYRELATIVE, | |
50 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
0d96d5ea TB |
51 | BIT(IIO_CHAN_INFO_SCALE), |
52 | .scan_index = 0, | |
53 | .scan_type = { | |
54 | .sign = 's', | |
55 | .realbits = 16, | |
56 | .storagebits = 16, | |
57 | .endianness = IIO_CPU, | |
58 | }, | |
366a3270 TB |
59 | }, |
60 | { | |
61 | .type = IIO_TEMP, | |
62 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
0d96d5ea TB |
63 | BIT(IIO_CHAN_INFO_SCALE), |
64 | .scan_index = 1, | |
65 | .scan_type = { | |
66 | .sign = 's', | |
67 | .realbits = 16, | |
68 | .storagebits = 16, | |
69 | .endianness = IIO_CPU, | |
70 | }, | |
366a3270 | 71 | }, |
0d96d5ea | 72 | IIO_CHAN_SOFT_TIMESTAMP(2), |
366a3270 TB |
73 | }; |
74 | ||
75 | /* CRC calculation algorithm, as specified in the datasheet (page 13). */ | |
76 | static u16 am2315_crc(u8 *data, u8 nr_bytes) | |
77 | { | |
78 | int i; | |
79 | u16 crc = 0xffff; | |
80 | ||
81 | while (nr_bytes--) { | |
82 | crc ^= *data++; | |
83 | for (i = 0; i < 8; i++) { | |
84 | if (crc & 0x01) { | |
85 | crc >>= 1; | |
86 | crc ^= 0xA001; | |
87 | } else { | |
88 | crc >>= 1; | |
89 | } | |
90 | } | |
91 | } | |
92 | ||
93 | return crc; | |
94 | } | |
95 | ||
96 | /* Simple function that sends a few bytes to the device to wake it up. */ | |
97 | static void am2315_ping(struct i2c_client *client) | |
98 | { | |
99 | i2c_smbus_read_byte_data(client, AM2315_REG_HUM_MSB); | |
100 | } | |
101 | ||
102 | static int am2315_read_data(struct am2315_data *data, | |
103 | struct am2315_sensor_data *sensor_data) | |
104 | { | |
105 | int ret; | |
106 | /* tx_buf format: <function code> <start addr> <nr of regs to read> */ | |
107 | u8 tx_buf[3] = { AM2315_FUNCTION_READ, AM2315_REG_HUM_MSB, 4 }; | |
108 | /* | |
109 | * rx_buf format: | |
110 | * <function code> <number of registers read> | |
111 | * <humidity MSB> <humidity LSB> <temp MSB> <temp LSB> | |
112 | * <CRC LSB> <CRC MSB> | |
113 | */ | |
114 | u8 rx_buf[8]; | |
115 | u16 crc; | |
116 | ||
117 | /* First wake up the device. */ | |
118 | am2315_ping(data->client); | |
119 | ||
120 | mutex_lock(&data->lock); | |
121 | ret = i2c_master_send(data->client, tx_buf, sizeof(tx_buf)); | |
122 | if (ret < 0) { | |
123 | dev_err(&data->client->dev, "failed to send read request\n"); | |
124 | goto exit_unlock; | |
125 | } | |
126 | /* Wait 2-3 ms, then read back the data sent by the device. */ | |
127 | usleep_range(2000, 3000); | |
128 | /* Do a bulk data read, then pick out what we need. */ | |
129 | ret = i2c_master_recv(data->client, rx_buf, sizeof(rx_buf)); | |
130 | if (ret < 0) { | |
131 | dev_err(&data->client->dev, "failed to read sensor data\n"); | |
132 | goto exit_unlock; | |
133 | } | |
134 | mutex_unlock(&data->lock); | |
135 | /* | |
136 | * Do a CRC check on the data and compare it to the value | |
137 | * calculated by the device. | |
138 | */ | |
139 | crc = am2315_crc(rx_buf, sizeof(rx_buf) - 2); | |
140 | if ((crc & 0xff) != rx_buf[6] || (crc >> 8) != rx_buf[7]) { | |
141 | dev_err(&data->client->dev, "failed to verify sensor data\n"); | |
142 | return -EIO; | |
143 | } | |
144 | ||
145 | sensor_data->hum_data = (rx_buf[AM2315_HUM_OFFSET] << 8) | | |
146 | rx_buf[AM2315_HUM_OFFSET + 1]; | |
147 | sensor_data->temp_data = (rx_buf[AM2315_TEMP_OFFSET] << 8) | | |
148 | rx_buf[AM2315_TEMP_OFFSET + 1]; | |
149 | ||
150 | return ret; | |
151 | ||
152 | exit_unlock: | |
153 | mutex_unlock(&data->lock); | |
154 | return ret; | |
155 | } | |
156 | ||
0d96d5ea TB |
157 | static irqreturn_t am2315_trigger_handler(int irq, void *p) |
158 | { | |
159 | int i; | |
160 | int ret; | |
161 | int bit; | |
162 | struct iio_poll_func *pf = p; | |
163 | struct iio_dev *indio_dev = pf->indio_dev; | |
164 | struct am2315_data *data = iio_priv(indio_dev); | |
165 | struct am2315_sensor_data sensor_data; | |
166 | ||
167 | ret = am2315_read_data(data, &sensor_data); | |
1cb0d06a | 168 | if (ret < 0) |
0d96d5ea | 169 | goto err; |
0d96d5ea TB |
170 | |
171 | mutex_lock(&data->lock); | |
172 | if (*(indio_dev->active_scan_mask) == AM2315_ALL_CHANNEL_MASK) { | |
173 | data->buffer[0] = sensor_data.hum_data; | |
174 | data->buffer[1] = sensor_data.temp_data; | |
175 | } else { | |
176 | i = 0; | |
177 | for_each_set_bit(bit, indio_dev->active_scan_mask, | |
178 | indio_dev->masklength) { | |
179 | data->buffer[i] = (bit ? sensor_data.temp_data : | |
180 | sensor_data.hum_data); | |
181 | i++; | |
182 | } | |
183 | } | |
184 | mutex_unlock(&data->lock); | |
185 | ||
186 | iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, | |
187 | pf->timestamp); | |
188 | err: | |
189 | iio_trigger_notify_done(indio_dev->trig); | |
190 | return IRQ_HANDLED; | |
191 | } | |
192 | ||
366a3270 TB |
193 | static int am2315_read_raw(struct iio_dev *indio_dev, |
194 | struct iio_chan_spec const *chan, | |
195 | int *val, int *val2, long mask) | |
196 | { | |
197 | int ret; | |
198 | struct am2315_sensor_data sensor_data; | |
199 | struct am2315_data *data = iio_priv(indio_dev); | |
200 | ||
201 | switch (mask) { | |
202 | case IIO_CHAN_INFO_RAW: | |
203 | ret = am2315_read_data(data, &sensor_data); | |
204 | if (ret < 0) | |
205 | return ret; | |
206 | *val = (chan->type == IIO_HUMIDITYRELATIVE) ? | |
207 | sensor_data.hum_data : sensor_data.temp_data; | |
208 | return IIO_VAL_INT; | |
209 | case IIO_CHAN_INFO_SCALE: | |
210 | *val = 100; | |
211 | return IIO_VAL_INT; | |
212 | } | |
213 | ||
214 | return -EINVAL; | |
215 | } | |
216 | ||
217 | static const struct iio_info am2315_info = { | |
366a3270 TB |
218 | .read_raw = am2315_read_raw, |
219 | }; | |
220 | ||
221 | static int am2315_probe(struct i2c_client *client, | |
222 | const struct i2c_device_id *id) | |
223 | { | |
0d96d5ea | 224 | int ret; |
366a3270 TB |
225 | struct iio_dev *indio_dev; |
226 | struct am2315_data *data; | |
227 | ||
228 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
229 | if (!indio_dev) { | |
230 | dev_err(&client->dev, "iio allocation failed!\n"); | |
231 | return -ENOMEM; | |
232 | } | |
233 | ||
234 | data = iio_priv(indio_dev); | |
235 | data->client = client; | |
236 | i2c_set_clientdata(client, indio_dev); | |
237 | mutex_init(&data->lock); | |
238 | ||
239 | indio_dev->dev.parent = &client->dev; | |
240 | indio_dev->info = &am2315_info; | |
241 | indio_dev->name = AM2315_DRIVER_NAME; | |
242 | indio_dev->modes = INDIO_DIRECT_MODE; | |
243 | indio_dev->channels = am2315_channels; | |
244 | indio_dev->num_channels = ARRAY_SIZE(am2315_channels); | |
245 | ||
3c68858d | 246 | ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time, |
0d96d5ea TB |
247 | am2315_trigger_handler, NULL); |
248 | if (ret < 0) { | |
249 | dev_err(&client->dev, "iio triggered buffer setup failed\n"); | |
250 | return ret; | |
251 | } | |
252 | ||
253 | ret = iio_device_register(indio_dev); | |
254 | if (ret < 0) | |
255 | goto err_buffer_cleanup; | |
256 | ||
257 | return 0; | |
258 | ||
259 | err_buffer_cleanup: | |
260 | iio_triggered_buffer_cleanup(indio_dev); | |
261 | return ret; | |
366a3270 TB |
262 | } |
263 | ||
264 | static int am2315_remove(struct i2c_client *client) | |
265 | { | |
266 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
267 | ||
268 | iio_device_unregister(indio_dev); | |
0d96d5ea | 269 | iio_triggered_buffer_cleanup(indio_dev); |
366a3270 TB |
270 | |
271 | return 0; | |
272 | } | |
273 | ||
274 | static const struct i2c_device_id am2315_i2c_id[] = { | |
275 | {"am2315", 0}, | |
276 | {} | |
277 | }; | |
cdd469ad | 278 | MODULE_DEVICE_TABLE(i2c, am2315_i2c_id); |
366a3270 TB |
279 | |
280 | static const struct acpi_device_id am2315_acpi_id[] = { | |
281 | {"AOS2315", 0}, | |
282 | {} | |
283 | }; | |
284 | ||
285 | MODULE_DEVICE_TABLE(acpi, am2315_acpi_id); | |
286 | ||
287 | static struct i2c_driver am2315_driver = { | |
288 | .driver = { | |
289 | .name = "am2315", | |
290 | .acpi_match_table = ACPI_PTR(am2315_acpi_id), | |
291 | }, | |
292 | .probe = am2315_probe, | |
293 | .remove = am2315_remove, | |
294 | .id_table = am2315_i2c_id, | |
295 | }; | |
296 | ||
297 | module_i2c_driver(am2315_driver); | |
298 | ||
299 | MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>"); | |
300 | MODULE_DESCRIPTION("Aosong AM2315 relative humidity and temperature"); | |
301 | MODULE_LICENSE("GPL v2"); |