ca3e23b8fcad0081a203fd01f8fe843128c6568b
[oweals/openwrt.git] /
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
5
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.
9
10 Signed-off-by: Tomasz Duszynski <tduszyns@gmail.com>
11 Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
12 ---
13  Documentation/ABI/testing/sysfs-bus-iio-sps30 |  20 +++
14  drivers/iio/chemical/sps30.c                  | 143 +++++++++++++++---
15  2 files changed, 145 insertions(+), 18 deletions(-)
16
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.
25 +
26 +What:          /sys/bus/iio/devices/iio:deviceX/cleaning_period
27 +Date:          January 2019
28 +KernelVersion: 5.1
29 +Contact:       linux-iio@vger.kernel.org
30 +Description:
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.
34 +
35 +               Writing 0 disables periodical self cleaning entirely.
36 +
37 +What:          /sys/bus/iio/devices/iio:deviceX/cleaning_period_available
38 +Date:          January 2019
39 +KernelVersion: 5.1
40 +Contact:       linux-iio@vger.kernel.org
41 +Description:
42 +               The range of available values in seconds represented as the
43 +               minimum value, the step and the maximum value, all enclosed in
44 +               square brackets.
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
49 @@ -5,9 +5,6 @@
50   * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
51   *
52   * I2C slave address: 0x69
53 - *
54 - * TODO:
55 - *  - support for reading/setting auto cleaning interval
56   */
57  
58  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
59 @@ -21,6 +18,7 @@
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>
65  
66  #define SPS30_CRC8_POLYNOMIAL 0x31
67 @@ -28,6 +26,9 @@
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
74  
75  /* SPS30 commands */
76  #define SPS30_START_MEAS 0x0010
77 @@ -37,6 +38,9 @@
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
84  
85  enum {
86         PM1,
87 @@ -45,6 +49,11 @@ enum {
88         PM10,
89  };
90  
91 +enum {
92 +       RESET,
93 +       MEASURING,
94 +};
95 +
96  struct sps30_state {
97         struct i2c_client *client;
98         /*
99 @@ -52,6 +61,7 @@ struct sps30_state {
100          * Must be held whenever sequence of commands is to be executed.
101          */
102         struct mutex lock;
103 +       int state;
104  };
105  
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);
110                 break;
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)
118                 size += size / 2;
119                 ret = sps30_write_then_read(state, buf, 2, buf, size);
120                 break;
121 +       case SPS30_AUTO_CLEANING_PERIOD:
122 +               buf[2] = data[0];
123 +               buf[3] = data[1];
124 +               buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE);
125 +               buf[5] = data[2];
126 +               buf[6] = data[3];
127 +               buf[7] = crc8(sps30_crc8_table, &buf[5], 2, CRC8_INIT_VALUE);
128 +               ret = sps30_write_then_read(state, buf, 8, NULL, 0);
129 +               break;
130         }
131  
132         if (ret)
133 @@ -170,6 +192,14 @@ static int sps30_do_meas(struct sps30_state *state, s32 *data, int size)
134         int i, ret, tries = 5;
135         u8 tmp[16];
136  
137 +       if (state->state == RESET) {
138 +               ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0);
139 +               if (ret)
140 +                       return ret;
141 +
142 +               state->state = MEASURING;
143 +       }
144 +
145         while (tries--) {
146                 ret = sps30_do_cmd(state, SPS30_READ_DATA_READY_FLAG, tmp, 2);
147                 if (ret)
148 @@ -276,6 +306,24 @@ static int sps30_read_raw(struct iio_dev *indio_dev,
149         return -EINVAL;
150  }
151  
152 +static int sps30_do_cmd_reset(struct sps30_state *state)
153 +{
154 +       int ret;
155 +
156 +       ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0);
157 +       msleep(300);
158 +       /*
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.
163 +        */
164 +       sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
165 +       state->state = RESET;
166 +
167 +       return ret;
168 +}
169 +
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,
174         return len;
175  }
176  
177 +static ssize_t cleaning_period_show(struct device *dev,
178 +                                     struct device_attribute *attr,
179 +                                     char *buf)
180 +{
181 +       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
182 +       struct sps30_state *state = iio_priv(indio_dev);
183 +       u8 tmp[4];
184 +       int ret;
185 +
186 +       mutex_lock(&state->lock);
187 +       ret = sps30_do_cmd(state, SPS30_READ_AUTO_CLEANING_PERIOD, tmp, 4);
188 +       mutex_unlock(&state->lock);
189 +       if (ret)
190 +               return ret;
191 +
192 +       return sprintf(buf, "%d\n", get_unaligned_be32(tmp));
193 +}
194 +
195 +static ssize_t cleaning_period_store(struct device *dev,
196 +                                      struct device_attribute *attr,
197 +                                      const char *buf, size_t len)
198 +{
199 +       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
200 +       struct sps30_state *state = iio_priv(indio_dev);
201 +       int val, ret;
202 +       u8 tmp[4];
203 +
204 +       if (kstrtoint(buf, 0, &val))
205 +               return -EINVAL;
206 +
207 +       if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) ||
208 +           (val > SPS30_AUTO_CLEANING_PERIOD_MAX))
209 +               return -EINVAL;
210 +
211 +       put_unaligned_be32(val, tmp);
212 +
213 +       mutex_lock(&state->lock);
214 +       ret = sps30_do_cmd(state, SPS30_AUTO_CLEANING_PERIOD, tmp, 0);
215 +       if (ret) {
216 +               mutex_unlock(&state->lock);
217 +               return ret;
218 +       }
219 +
220 +       msleep(20);
221 +
222 +       /*
223 +        * sensor requires reset in order to return up to date self cleaning
224 +        * period
225 +        */
226 +       ret = sps30_do_cmd_reset(state);
227 +       if (ret)
228 +               dev_warn(dev,
229 +                        "period changed but reads will return the old value\n");
230 +
231 +       mutex_unlock(&state->lock);
232 +
233 +       return len;
234 +}
235 +
236 +static ssize_t cleaning_period_available_show(struct device *dev,
237 +                                             struct device_attribute *attr,
238 +                                             char *buf)
239 +{
240 +       return snprintf(buf, PAGE_SIZE, "[%d %d %d]\n",
241 +                       SPS30_AUTO_CLEANING_PERIOD_MIN, 1,
242 +                       SPS30_AUTO_CLEANING_PERIOD_MAX);
243 +}
244 +
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);
248  
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,
253         NULL
254  };
255  
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);
267  
268 -       ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0);
269 +       ret = sps30_do_cmd_reset(state);
270         if (ret) {
271                 dev_err(&client->dev, "failed to reset device\n");
272                 return ret;
273         }
274 -       msleep(300);
275 -       /*
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.
280 -        */
281 -       sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
282  
283         ret = sps30_do_cmd(state, SPS30_READ_SERIAL, buf, sizeof(buf));
284         if (ret) {
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);
288  
289 -       ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0);
290 -       if (ret) {
291 -               dev_err(&client->dev, "failed to start measurement\n");
292 -               return ret;
293 -       }
294 -
295         ret = devm_add_action_or_reset(&client->dev, sps30_stop_meas, state);
296         if (ret)
297                 return ret;