1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Sensirion SPS30 particulate matter sensor i2c driver |
4 | * |
5 | * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> |
6 | * |
7 | * I2C slave address: 0x69 |
8 | */ |
9 | #include <asm/unaligned.h> |
10 | #include <linux/crc8.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/device.h> |
13 | #include <linux/errno.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/mod_devicetable.h> |
16 | #include <linux/module.h> |
17 | #include <linux/types.h> |
18 | |
19 | #include "sps30.h" |
20 | |
21 | #define SPS30_I2C_CRC8_POLYNOMIAL 0x31 |
22 | /* max number of bytes needed to store PM measurements or serial string */ |
23 | #define SPS30_I2C_MAX_BUF_SIZE 48 |
24 | |
25 | DECLARE_CRC8_TABLE(sps30_i2c_crc8_table); |
26 | |
27 | #define SPS30_I2C_START_MEAS 0x0010 |
28 | #define SPS30_I2C_STOP_MEAS 0x0104 |
29 | #define SPS30_I2C_READ_MEAS 0x0300 |
30 | #define SPS30_I2C_MEAS_READY 0x0202 |
31 | #define SPS30_I2C_RESET 0xd304 |
32 | #define SPS30_I2C_CLEAN_FAN 0x5607 |
33 | #define SPS30_I2C_PERIOD 0x8004 |
34 | #define SPS30_I2C_READ_SERIAL 0xd033 |
35 | #define SPS30_I2C_READ_VERSION 0xd100 |
36 | |
37 | static int sps30_i2c_xfer(struct sps30_state *state, unsigned char *txbuf, size_t txsize, |
38 | unsigned char *rxbuf, size_t rxsize) |
39 | { |
40 | struct i2c_client *client = to_i2c_client(state->dev); |
41 | int ret; |
42 | |
43 | /* |
44 | * Sensor does not support repeated start so instead of |
45 | * sending two i2c messages in a row we just send one by one. |
46 | */ |
47 | ret = i2c_master_send(client, buf: txbuf, count: txsize); |
48 | if (ret < 0) |
49 | return ret; |
50 | if (ret != txsize) |
51 | return -EIO; |
52 | |
53 | if (!rxsize) |
54 | return 0; |
55 | |
56 | ret = i2c_master_recv(client, buf: rxbuf, count: rxsize); |
57 | if (ret < 0) |
58 | return ret; |
59 | if (ret != rxsize) |
60 | return -EIO; |
61 | |
62 | return 0; |
63 | } |
64 | |
65 | static int sps30_i2c_command(struct sps30_state *state, u16 cmd, void *arg, size_t arg_size, |
66 | void *rsp, size_t rsp_size) |
67 | { |
68 | /* |
69 | * Internally sensor stores measurements in a following manner: |
70 | * |
71 | * PM1: upper two bytes, crc8, lower two bytes, crc8 |
72 | * PM2P5: upper two bytes, crc8, lower two bytes, crc8 |
73 | * PM4: upper two bytes, crc8, lower two bytes, crc8 |
74 | * PM10: upper two bytes, crc8, lower two bytes, crc8 |
75 | * |
76 | * What follows next are number concentration measurements and |
77 | * typical particle size measurement which we omit. |
78 | */ |
79 | unsigned char buf[SPS30_I2C_MAX_BUF_SIZE]; |
80 | unsigned char *tmp; |
81 | unsigned char crc; |
82 | size_t i; |
83 | int ret; |
84 | |
85 | put_unaligned_be16(val: cmd, p: buf); |
86 | i = 2; |
87 | |
88 | if (rsp) { |
89 | /* each two bytes are followed by a crc8 */ |
90 | rsp_size += rsp_size / 2; |
91 | } else { |
92 | tmp = arg; |
93 | |
94 | while (arg_size) { |
95 | buf[i] = *tmp++; |
96 | buf[i + 1] = *tmp++; |
97 | buf[i + 2] = crc8(table: sps30_i2c_crc8_table, pdata: buf + i, nbytes: 2, CRC8_INIT_VALUE); |
98 | arg_size -= 2; |
99 | i += 3; |
100 | } |
101 | } |
102 | |
103 | ret = sps30_i2c_xfer(state, txbuf: buf, txsize: i, rxbuf: buf, rxsize: rsp_size); |
104 | if (ret) |
105 | return ret; |
106 | |
107 | /* validate received data and strip off crc bytes */ |
108 | tmp = rsp; |
109 | for (i = 0; i < rsp_size; i += 3) { |
110 | crc = crc8(table: sps30_i2c_crc8_table, pdata: buf + i, nbytes: 2, CRC8_INIT_VALUE); |
111 | if (crc != buf[i + 2]) { |
112 | dev_err(state->dev, "data integrity check failed\n" ); |
113 | return -EIO; |
114 | } |
115 | |
116 | *tmp++ = buf[i]; |
117 | *tmp++ = buf[i + 1]; |
118 | } |
119 | |
120 | return 0; |
121 | } |
122 | |
123 | static int sps30_i2c_start_meas(struct sps30_state *state) |
124 | { |
125 | /* request BE IEEE754 formatted data */ |
126 | unsigned char buf[] = { 0x03, 0x00 }; |
127 | |
128 | return sps30_i2c_command(state, SPS30_I2C_START_MEAS, arg: buf, arg_size: sizeof(buf), NULL, rsp_size: 0); |
129 | } |
130 | |
131 | static int sps30_i2c_stop_meas(struct sps30_state *state) |
132 | { |
133 | return sps30_i2c_command(state, SPS30_I2C_STOP_MEAS, NULL, arg_size: 0, NULL, rsp_size: 0); |
134 | } |
135 | |
136 | static int sps30_i2c_reset(struct sps30_state *state) |
137 | { |
138 | int ret; |
139 | |
140 | ret = sps30_i2c_command(state, SPS30_I2C_RESET, NULL, arg_size: 0, NULL, rsp_size: 0); |
141 | msleep(msecs: 500); |
142 | /* |
143 | * Power-on-reset causes sensor to produce some glitch on i2c bus and |
144 | * some controllers end up in error state. Recover simply by placing |
145 | * some data on the bus, for example STOP_MEAS command, which |
146 | * is NOP in this case. |
147 | */ |
148 | sps30_i2c_stop_meas(state); |
149 | |
150 | return ret; |
151 | } |
152 | |
153 | static bool sps30_i2c_meas_ready(struct sps30_state *state) |
154 | { |
155 | unsigned char buf[2]; |
156 | int ret; |
157 | |
158 | ret = sps30_i2c_command(state, SPS30_I2C_MEAS_READY, NULL, arg_size: 0, rsp: buf, rsp_size: sizeof(buf)); |
159 | if (ret) |
160 | return false; |
161 | |
162 | return buf[1]; |
163 | } |
164 | |
165 | static int sps30_i2c_read_meas(struct sps30_state *state, __be32 *meas, size_t num) |
166 | { |
167 | /* measurements are ready within a second */ |
168 | if (msleep_interruptible(msecs: 1000)) |
169 | return -EINTR; |
170 | |
171 | if (!sps30_i2c_meas_ready(state)) |
172 | return -ETIMEDOUT; |
173 | |
174 | return sps30_i2c_command(state, SPS30_I2C_READ_MEAS, NULL, arg_size: 0, rsp: meas, rsp_size: sizeof(num) * num); |
175 | } |
176 | |
177 | static int sps30_i2c_clean_fan(struct sps30_state *state) |
178 | { |
179 | return sps30_i2c_command(state, SPS30_I2C_CLEAN_FAN, NULL, arg_size: 0, NULL, rsp_size: 0); |
180 | } |
181 | |
182 | static int sps30_i2c_read_cleaning_period(struct sps30_state *state, __be32 *period) |
183 | { |
184 | return sps30_i2c_command(state, SPS30_I2C_PERIOD, NULL, arg_size: 0, rsp: period, rsp_size: sizeof(*period)); |
185 | } |
186 | |
187 | static int sps30_i2c_write_cleaning_period(struct sps30_state *state, __be32 period) |
188 | { |
189 | return sps30_i2c_command(state, SPS30_I2C_PERIOD, arg: &period, arg_size: sizeof(period), NULL, rsp_size: 0); |
190 | } |
191 | |
192 | static int sps30_i2c_show_info(struct sps30_state *state) |
193 | { |
194 | /* extra nul just in case */ |
195 | unsigned char buf[32 + 1] = { 0x00 }; |
196 | int ret; |
197 | |
198 | ret = sps30_i2c_command(state, SPS30_I2C_READ_SERIAL, NULL, arg_size: 0, rsp: buf, rsp_size: sizeof(buf) - 1); |
199 | if (ret) |
200 | return ret; |
201 | |
202 | dev_info(state->dev, "serial number: %s\n" , buf); |
203 | |
204 | ret = sps30_i2c_command(state, SPS30_I2C_READ_VERSION, NULL, arg_size: 0, rsp: buf, rsp_size: 2); |
205 | if (ret) |
206 | return ret; |
207 | |
208 | dev_info(state->dev, "fw version: %u.%u\n" , buf[0], buf[1]); |
209 | |
210 | return 0; |
211 | } |
212 | |
213 | static const struct sps30_ops sps30_i2c_ops = { |
214 | .start_meas = sps30_i2c_start_meas, |
215 | .stop_meas = sps30_i2c_stop_meas, |
216 | .read_meas = sps30_i2c_read_meas, |
217 | .reset = sps30_i2c_reset, |
218 | .clean_fan = sps30_i2c_clean_fan, |
219 | .read_cleaning_period = sps30_i2c_read_cleaning_period, |
220 | .write_cleaning_period = sps30_i2c_write_cleaning_period, |
221 | .show_info = sps30_i2c_show_info, |
222 | }; |
223 | |
224 | static int sps30_i2c_probe(struct i2c_client *client) |
225 | { |
226 | if (!i2c_check_functionality(adap: client->adapter, I2C_FUNC_I2C)) |
227 | return -EOPNOTSUPP; |
228 | |
229 | crc8_populate_msb(table: sps30_i2c_crc8_table, SPS30_I2C_CRC8_POLYNOMIAL); |
230 | |
231 | return sps30_probe(dev: &client->dev, name: client->name, NULL, ops: &sps30_i2c_ops); |
232 | } |
233 | |
234 | static const struct i2c_device_id sps30_i2c_id[] = { |
235 | { "sps30" }, |
236 | { } |
237 | }; |
238 | MODULE_DEVICE_TABLE(i2c, sps30_i2c_id); |
239 | |
240 | static const struct of_device_id sps30_i2c_of_match[] = { |
241 | { .compatible = "sensirion,sps30" }, |
242 | { } |
243 | }; |
244 | MODULE_DEVICE_TABLE(of, sps30_i2c_of_match); |
245 | |
246 | static struct i2c_driver sps30_i2c_driver = { |
247 | .driver = { |
248 | .name = KBUILD_MODNAME, |
249 | .of_match_table = sps30_i2c_of_match, |
250 | }, |
251 | .id_table = sps30_i2c_id, |
252 | .probe = sps30_i2c_probe, |
253 | }; |
254 | module_i2c_driver(sps30_i2c_driver); |
255 | |
256 | MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>" ); |
257 | MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver" ); |
258 | MODULE_LICENSE("GPL v2" ); |
259 | MODULE_IMPORT_NS(IIO_SPS30); |
260 | |