1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Sensirion SPS30 particulate matter sensor driver |
4 | * |
5 | * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/crc8.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/iio/buffer.h> |
12 | #include <linux/iio/iio.h> |
13 | #include <linux/iio/sysfs.h> |
14 | #include <linux/iio/trigger_consumer.h> |
15 | #include <linux/iio/triggered_buffer.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | |
19 | #include "sps30.h" |
20 | |
21 | /* sensor measures reliably up to 3000 ug / m3 */ |
22 | #define SPS30_MAX_PM 3000 |
23 | /* minimum and maximum self cleaning periods in seconds */ |
24 | #define SPS30_AUTO_CLEANING_PERIOD_MIN 0 |
25 | #define SPS30_AUTO_CLEANING_PERIOD_MAX 604800 |
26 | |
27 | enum { |
28 | PM1, |
29 | PM2P5, |
30 | PM4, |
31 | PM10, |
32 | }; |
33 | |
34 | enum { |
35 | RESET, |
36 | MEASURING, |
37 | }; |
38 | |
39 | static s32 sps30_float_to_int_clamped(__be32 *fp) |
40 | { |
41 | int val = be32_to_cpup(p: fp); |
42 | int mantissa = val & GENMASK(22, 0); |
43 | /* this is fine since passed float is always non-negative */ |
44 | int exp = val >> 23; |
45 | int fraction, shift; |
46 | |
47 | /* special case 0 */ |
48 | if (!exp && !mantissa) |
49 | return 0; |
50 | |
51 | exp -= 127; |
52 | if (exp < 0) { |
53 | /* return values ranging from 1 to 99 */ |
54 | return ((((1 << 23) + mantissa) * 100) >> 23) >> (-exp); |
55 | } |
56 | |
57 | /* return values ranging from 100 to 300000 */ |
58 | shift = 23 - exp; |
59 | val = (1 << exp) + (mantissa >> shift); |
60 | if (val >= SPS30_MAX_PM) |
61 | return SPS30_MAX_PM * 100; |
62 | |
63 | fraction = mantissa & GENMASK(shift - 1, 0); |
64 | |
65 | return val * 100 + ((fraction * 100) >> shift); |
66 | } |
67 | |
68 | static int sps30_do_meas(struct sps30_state *state, s32 *data, int size) |
69 | { |
70 | int i, ret; |
71 | |
72 | if (state->state == RESET) { |
73 | ret = state->ops->start_meas(state); |
74 | if (ret) |
75 | return ret; |
76 | |
77 | state->state = MEASURING; |
78 | } |
79 | |
80 | ret = state->ops->read_meas(state, (__be32 *)data, size); |
81 | if (ret) |
82 | return ret; |
83 | |
84 | for (i = 0; i < size; i++) |
85 | data[i] = sps30_float_to_int_clamped(fp: (__be32 *)&data[i]); |
86 | |
87 | return 0; |
88 | } |
89 | |
90 | static int sps30_do_reset(struct sps30_state *state) |
91 | { |
92 | int ret; |
93 | |
94 | ret = state->ops->reset(state); |
95 | if (ret) |
96 | return ret; |
97 | |
98 | state->state = RESET; |
99 | |
100 | return 0; |
101 | } |
102 | |
103 | static irqreturn_t sps30_trigger_handler(int irq, void *p) |
104 | { |
105 | struct iio_poll_func *pf = p; |
106 | struct iio_dev *indio_dev = pf->indio_dev; |
107 | struct sps30_state *state = iio_priv(indio_dev); |
108 | int ret; |
109 | struct { |
110 | s32 data[4]; /* PM1, PM2P5, PM4, PM10 */ |
111 | s64 ts; |
112 | } scan; |
113 | |
114 | mutex_lock(&state->lock); |
115 | ret = sps30_do_meas(state, data: scan.data, ARRAY_SIZE(scan.data)); |
116 | mutex_unlock(lock: &state->lock); |
117 | if (ret) |
118 | goto err; |
119 | |
120 | iio_push_to_buffers_with_timestamp(indio_dev, data: &scan, |
121 | timestamp: iio_get_time_ns(indio_dev)); |
122 | err: |
123 | iio_trigger_notify_done(trig: indio_dev->trig); |
124 | |
125 | return IRQ_HANDLED; |
126 | } |
127 | |
128 | static int sps30_read_raw(struct iio_dev *indio_dev, |
129 | struct iio_chan_spec const *chan, |
130 | int *val, int *val2, long mask) |
131 | { |
132 | struct sps30_state *state = iio_priv(indio_dev); |
133 | int data[4], ret = -EINVAL; |
134 | |
135 | switch (mask) { |
136 | case IIO_CHAN_INFO_PROCESSED: |
137 | switch (chan->type) { |
138 | case IIO_MASSCONCENTRATION: |
139 | mutex_lock(&state->lock); |
140 | /* read up to the number of bytes actually needed */ |
141 | switch (chan->channel2) { |
142 | case IIO_MOD_PM1: |
143 | ret = sps30_do_meas(state, data, size: 1); |
144 | break; |
145 | case IIO_MOD_PM2P5: |
146 | ret = sps30_do_meas(state, data, size: 2); |
147 | break; |
148 | case IIO_MOD_PM4: |
149 | ret = sps30_do_meas(state, data, size: 3); |
150 | break; |
151 | case IIO_MOD_PM10: |
152 | ret = sps30_do_meas(state, data, size: 4); |
153 | break; |
154 | } |
155 | mutex_unlock(lock: &state->lock); |
156 | if (ret) |
157 | return ret; |
158 | |
159 | *val = data[chan->address] / 100; |
160 | *val2 = (data[chan->address] % 100) * 10000; |
161 | |
162 | return IIO_VAL_INT_PLUS_MICRO; |
163 | default: |
164 | return -EINVAL; |
165 | } |
166 | case IIO_CHAN_INFO_SCALE: |
167 | switch (chan->type) { |
168 | case IIO_MASSCONCENTRATION: |
169 | switch (chan->channel2) { |
170 | case IIO_MOD_PM1: |
171 | case IIO_MOD_PM2P5: |
172 | case IIO_MOD_PM4: |
173 | case IIO_MOD_PM10: |
174 | *val = 0; |
175 | *val2 = 10000; |
176 | |
177 | return IIO_VAL_INT_PLUS_MICRO; |
178 | default: |
179 | return -EINVAL; |
180 | } |
181 | default: |
182 | return -EINVAL; |
183 | } |
184 | } |
185 | |
186 | return -EINVAL; |
187 | } |
188 | |
189 | static ssize_t start_cleaning_store(struct device *dev, |
190 | struct device_attribute *attr, |
191 | const char *buf, size_t len) |
192 | { |
193 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
194 | struct sps30_state *state = iio_priv(indio_dev); |
195 | int val, ret; |
196 | |
197 | if (kstrtoint(s: buf, base: 0, res: &val) || val != 1) |
198 | return -EINVAL; |
199 | |
200 | mutex_lock(&state->lock); |
201 | ret = state->ops->clean_fan(state); |
202 | mutex_unlock(lock: &state->lock); |
203 | if (ret) |
204 | return ret; |
205 | |
206 | return len; |
207 | } |
208 | |
209 | static ssize_t cleaning_period_show(struct device *dev, |
210 | struct device_attribute *attr, |
211 | char *buf) |
212 | { |
213 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
214 | struct sps30_state *state = iio_priv(indio_dev); |
215 | __be32 val; |
216 | int ret; |
217 | |
218 | mutex_lock(&state->lock); |
219 | ret = state->ops->read_cleaning_period(state, &val); |
220 | mutex_unlock(lock: &state->lock); |
221 | if (ret) |
222 | return ret; |
223 | |
224 | return sysfs_emit(buf, fmt: "%d\n" , be32_to_cpu(val)); |
225 | } |
226 | |
227 | static ssize_t cleaning_period_store(struct device *dev, struct device_attribute *attr, |
228 | const char *buf, size_t len) |
229 | { |
230 | struct iio_dev *indio_dev = dev_to_iio_dev(dev); |
231 | struct sps30_state *state = iio_priv(indio_dev); |
232 | int val, ret; |
233 | |
234 | if (kstrtoint(s: buf, base: 0, res: &val)) |
235 | return -EINVAL; |
236 | |
237 | if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) || |
238 | (val > SPS30_AUTO_CLEANING_PERIOD_MAX)) |
239 | return -EINVAL; |
240 | |
241 | mutex_lock(&state->lock); |
242 | ret = state->ops->write_cleaning_period(state, cpu_to_be32(val)); |
243 | if (ret) { |
244 | mutex_unlock(lock: &state->lock); |
245 | return ret; |
246 | } |
247 | |
248 | msleep(msecs: 20); |
249 | |
250 | /* |
251 | * sensor requires reset in order to return up to date self cleaning |
252 | * period |
253 | */ |
254 | ret = sps30_do_reset(state); |
255 | if (ret) |
256 | dev_warn(dev, |
257 | "period changed but reads will return the old value\n" ); |
258 | |
259 | mutex_unlock(lock: &state->lock); |
260 | |
261 | return len; |
262 | } |
263 | |
264 | static ssize_t cleaning_period_available_show(struct device *dev, |
265 | struct device_attribute *attr, |
266 | char *buf) |
267 | { |
268 | return sysfs_emit(buf, fmt: "[%d %d %d]\n" , |
269 | SPS30_AUTO_CLEANING_PERIOD_MIN, 1, |
270 | SPS30_AUTO_CLEANING_PERIOD_MAX); |
271 | } |
272 | |
273 | static IIO_DEVICE_ATTR_WO(start_cleaning, 0); |
274 | static IIO_DEVICE_ATTR_RW(cleaning_period, 0); |
275 | static IIO_DEVICE_ATTR_RO(cleaning_period_available, 0); |
276 | |
277 | static struct attribute *sps30_attrs[] = { |
278 | &iio_dev_attr_start_cleaning.dev_attr.attr, |
279 | &iio_dev_attr_cleaning_period.dev_attr.attr, |
280 | &iio_dev_attr_cleaning_period_available.dev_attr.attr, |
281 | NULL |
282 | }; |
283 | |
284 | static const struct attribute_group sps30_attr_group = { |
285 | .attrs = sps30_attrs, |
286 | }; |
287 | |
288 | static const struct iio_info sps30_info = { |
289 | .attrs = &sps30_attr_group, |
290 | .read_raw = sps30_read_raw, |
291 | }; |
292 | |
293 | #define SPS30_CHAN(_index, _mod) { \ |
294 | .type = IIO_MASSCONCENTRATION, \ |
295 | .modified = 1, \ |
296 | .channel2 = IIO_MOD_ ## _mod, \ |
297 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ |
298 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ |
299 | .address = _mod, \ |
300 | .scan_index = _index, \ |
301 | .scan_type = { \ |
302 | .sign = 'u', \ |
303 | .realbits = 19, \ |
304 | .storagebits = 32, \ |
305 | .endianness = IIO_CPU, \ |
306 | }, \ |
307 | } |
308 | |
309 | static const struct iio_chan_spec sps30_channels[] = { |
310 | SPS30_CHAN(0, PM1), |
311 | SPS30_CHAN(1, PM2P5), |
312 | SPS30_CHAN(2, PM4), |
313 | SPS30_CHAN(3, PM10), |
314 | IIO_CHAN_SOFT_TIMESTAMP(4), |
315 | }; |
316 | |
317 | static void sps30_devm_stop_meas(void *data) |
318 | { |
319 | struct sps30_state *state = data; |
320 | |
321 | if (state->state == MEASURING) |
322 | state->ops->stop_meas(state); |
323 | } |
324 | |
325 | static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 }; |
326 | |
327 | int sps30_probe(struct device *dev, const char *name, void *priv, const struct sps30_ops *ops) |
328 | { |
329 | struct iio_dev *indio_dev; |
330 | struct sps30_state *state; |
331 | int ret; |
332 | |
333 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*state)); |
334 | if (!indio_dev) |
335 | return -ENOMEM; |
336 | |
337 | dev_set_drvdata(dev, data: indio_dev); |
338 | |
339 | state = iio_priv(indio_dev); |
340 | state->dev = dev; |
341 | state->priv = priv; |
342 | state->ops = ops; |
343 | mutex_init(&state->lock); |
344 | |
345 | indio_dev->info = &sps30_info; |
346 | indio_dev->name = name; |
347 | indio_dev->channels = sps30_channels; |
348 | indio_dev->num_channels = ARRAY_SIZE(sps30_channels); |
349 | indio_dev->modes = INDIO_DIRECT_MODE; |
350 | indio_dev->available_scan_masks = sps30_scan_masks; |
351 | |
352 | ret = sps30_do_reset(state); |
353 | if (ret) { |
354 | dev_err(dev, "failed to reset device\n" ); |
355 | return ret; |
356 | } |
357 | |
358 | ret = state->ops->show_info(state); |
359 | if (ret) { |
360 | dev_err(dev, "failed to read device info\n" ); |
361 | return ret; |
362 | } |
363 | |
364 | ret = devm_add_action_or_reset(dev, sps30_devm_stop_meas, state); |
365 | if (ret) |
366 | return ret; |
367 | |
368 | ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, |
369 | sps30_trigger_handler, NULL); |
370 | if (ret) |
371 | return ret; |
372 | |
373 | return devm_iio_device_register(dev, indio_dev); |
374 | } |
375 | EXPORT_SYMBOL_NS_GPL(sps30_probe, IIO_SPS30); |
376 | |
377 | MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>" ); |
378 | MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver" ); |
379 | MODULE_LICENSE("GPL v2" ); |
380 | |