1 From 62129a0849d27cc94ced832bcf9dcde283dcbe08 Mon Sep 17 00:00:00 2001
2 From: Tomasz Duszynski <tduszyns@gmail.com>
3 Date: Tue, 15 Jan 2019 20:00:06 +0100
4 Subject: [PATCH] iio: chemical: sps30: allow changing self cleaning period
6 Sensor can periodically trigger self cleaning. Period can be changed by
7 writing a new value to a dedicated attribute. Upon attribute read
8 current period gets returned.
10 Signed-off-by: Tomasz Duszynski <tduszyns@gmail.com>
11 Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
13 Documentation/ABI/testing/sysfs-bus-iio-sps30 | 20 +++
14 drivers/iio/chemical/sps30.c | 143 +++++++++++++++---
15 2 files changed, 145 insertions(+), 18 deletions(-)
17 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-sps30 b/Documentation/ABI/testing/sysfs-bus-iio-sps30
18 index e7ce2c57635e..143df8e89d08 100644
19 --- a/Documentation/ABI/testing/sysfs-bus-iio-sps30
20 +++ b/Documentation/ABI/testing/sysfs-bus-iio-sps30
21 @@ -6,3 +6,23 @@ Description:
22 Writing 1 starts sensor self cleaning. Internal fan accelerates
23 to its maximum speed and keeps spinning for about 10 seconds in
24 order to blow out accumulated dust.
26 +What: /sys/bus/iio/devices/iio:deviceX/cleaning_period
29 +Contact: linux-iio@vger.kernel.org
31 + Sensor is capable of triggering self cleaning periodically.
32 + Period can be changed by writing a new value here. Upon reading
33 + the current one is returned. Units are seconds.
35 + Writing 0 disables periodical self cleaning entirely.
37 +What: /sys/bus/iio/devices/iio:deviceX/cleaning_period_available
40 +Contact: linux-iio@vger.kernel.org
42 + The range of available values in seconds represented as the
43 + minimum value, the step and the maximum value, all enclosed in
45 diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c
46 index f3b4390c8f5c..376fac41ecb5 100644
47 --- a/drivers/iio/chemical/sps30.c
48 +++ b/drivers/iio/chemical/sps30.c
50 * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
52 * I2C slave address: 0x69
55 - * - support for reading/setting auto cleaning interval
58 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
60 #include <linux/iio/sysfs.h>
61 #include <linux/iio/trigger_consumer.h>
62 #include <linux/iio/triggered_buffer.h>
63 +#include <linux/kernel.h>
64 #include <linux/module.h>
66 #define SPS30_CRC8_POLYNOMIAL 0x31
68 #define SPS30_MAX_READ_SIZE 48
69 /* sensor measures reliably up to 3000 ug / m3 */
70 #define SPS30_MAX_PM 3000
71 +/* minimum and maximum self cleaning periods in seconds */
72 +#define SPS30_AUTO_CLEANING_PERIOD_MIN 0
73 +#define SPS30_AUTO_CLEANING_PERIOD_MAX 604800
76 #define SPS30_START_MEAS 0x0010
78 #define SPS30_READ_DATA 0x0300
79 #define SPS30_READ_SERIAL 0xd033
80 #define SPS30_START_FAN_CLEANING 0x5607
81 +#define SPS30_AUTO_CLEANING_PERIOD 0x8004
82 +/* not a sensor command per se, used only to distinguish write from read */
83 +#define SPS30_READ_AUTO_CLEANING_PERIOD 0x8005
87 @@ -45,6 +49,11 @@ enum {
97 struct i2c_client *client;
99 @@ -52,6 +61,7 @@ struct sps30_state {
100 * Must be held whenever sequence of commands is to be executed.
106 DECLARE_CRC8_TABLE(sps30_crc8_table);
107 @@ -107,6 +117,9 @@ static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size)
108 case SPS30_START_FAN_CLEANING:
109 ret = sps30_write_then_read(state, buf, 2, NULL, 0);
111 + case SPS30_READ_AUTO_CLEANING_PERIOD:
112 + buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8;
113 + buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD;
114 case SPS30_READ_DATA_READY_FLAG:
115 case SPS30_READ_DATA:
116 case SPS30_READ_SERIAL:
117 @@ -114,6 +127,15 @@ static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size)
119 ret = sps30_write_then_read(state, buf, 2, buf, size);
121 + case SPS30_AUTO_CLEANING_PERIOD:
124 + buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE);
127 + buf[7] = crc8(sps30_crc8_table, &buf[5], 2, CRC8_INIT_VALUE);
128 + ret = sps30_write_then_read(state, buf, 8, NULL, 0);
133 @@ -170,6 +192,14 @@ static int sps30_do_meas(struct sps30_state *state, s32 *data, int size)
134 int i, ret, tries = 5;
137 + if (state->state == RESET) {
138 + ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0);
142 + state->state = MEASURING;
146 ret = sps30_do_cmd(state, SPS30_READ_DATA_READY_FLAG, tmp, 2);
148 @@ -276,6 +306,24 @@ static int sps30_read_raw(struct iio_dev *indio_dev,
152 +static int sps30_do_cmd_reset(struct sps30_state *state)
156 + ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0);
159 + * Power-on-reset causes sensor to produce some glitch on i2c bus and
160 + * some controllers end up in error state. Recover simply by placing
161 + * some data on the bus, for example STOP_MEAS command, which
162 + * is NOP in this case.
164 + sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
165 + state->state = RESET;
170 static ssize_t start_cleaning_store(struct device *dev,
171 struct device_attribute *attr,
172 const char *buf, size_t len)
173 @@ -296,10 +344,82 @@ static ssize_t start_cleaning_store(struct device *dev,
177 +static ssize_t cleaning_period_show(struct device *dev,
178 + struct device_attribute *attr,
181 + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
182 + struct sps30_state *state = iio_priv(indio_dev);
186 + mutex_lock(&state->lock);
187 + ret = sps30_do_cmd(state, SPS30_READ_AUTO_CLEANING_PERIOD, tmp, 4);
188 + mutex_unlock(&state->lock);
192 + return sprintf(buf, "%d\n", get_unaligned_be32(tmp));
195 +static ssize_t cleaning_period_store(struct device *dev,
196 + struct device_attribute *attr,
197 + const char *buf, size_t len)
199 + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
200 + struct sps30_state *state = iio_priv(indio_dev);
204 + if (kstrtoint(buf, 0, &val))
207 + if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) ||
208 + (val > SPS30_AUTO_CLEANING_PERIOD_MAX))
211 + put_unaligned_be32(val, tmp);
213 + mutex_lock(&state->lock);
214 + ret = sps30_do_cmd(state, SPS30_AUTO_CLEANING_PERIOD, tmp, 0);
216 + mutex_unlock(&state->lock);
223 + * sensor requires reset in order to return up to date self cleaning
226 + ret = sps30_do_cmd_reset(state);
229 + "period changed but reads will return the old value\n");
231 + mutex_unlock(&state->lock);
236 +static ssize_t cleaning_period_available_show(struct device *dev,
237 + struct device_attribute *attr,
240 + return snprintf(buf, PAGE_SIZE, "[%d %d %d]\n",
241 + SPS30_AUTO_CLEANING_PERIOD_MIN, 1,
242 + SPS30_AUTO_CLEANING_PERIOD_MAX);
245 static IIO_DEVICE_ATTR_WO(start_cleaning, 0);
246 +static IIO_DEVICE_ATTR_RW(cleaning_period, 0);
247 +static IIO_DEVICE_ATTR_RO(cleaning_period_available, 0);
249 static struct attribute *sps30_attrs[] = {
250 &iio_dev_attr_start_cleaning.dev_attr.attr,
251 + &iio_dev_attr_cleaning_period.dev_attr.attr,
252 + &iio_dev_attr_cleaning_period_available.dev_attr.attr,
256 @@ -362,6 +482,7 @@ static int sps30_probe(struct i2c_client *client)
257 state = iio_priv(indio_dev);
258 i2c_set_clientdata(client, indio_dev);
259 state->client = client;
260 + state->state = RESET;
261 indio_dev->dev.parent = &client->dev;
262 indio_dev->info = &sps30_info;
263 indio_dev->name = client->name;
264 @@ -373,19 +494,11 @@ static int sps30_probe(struct i2c_client *client)
265 mutex_init(&state->lock);
266 crc8_populate_msb(sps30_crc8_table, SPS30_CRC8_POLYNOMIAL);
268 - ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0);
269 + ret = sps30_do_cmd_reset(state);
271 dev_err(&client->dev, "failed to reset device\n");
276 - * Power-on-reset causes sensor to produce some glitch on i2c bus and
277 - * some controllers end up in error state. Recover simply by placing
278 - * some data on the bus, for example STOP_MEAS command, which
279 - * is NOP in this case.
281 - sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
283 ret = sps30_do_cmd(state, SPS30_READ_SERIAL, buf, sizeof(buf));
285 @@ -395,12 +508,6 @@ static int sps30_probe(struct i2c_client *client)
286 /* returned serial number is already NUL terminated */
287 dev_info(&client->dev, "serial number: %s\n", buf);
289 - ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0);
291 - dev_err(&client->dev, "failed to start measurement\n");
295 ret = devm_add_action_or_reset(&client->dev, sps30_stop_meas, state);