diff options
author | Peter Meerwald-Stadler <pmeerw@pmeerw.net> | 2016-09-18 10:41:30 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2016-09-18 14:56:39 -0400 |
commit | ac45e57f15903747ccd5a14cebfbd673ebcbdd6f (patch) | |
tree | 949f4caa449ba3f04f24a77510425c7cbe93487f | |
parent | 03b262f2bbf43b82eaef82ffb3bc671d5b5c8da1 (diff) |
iio: light: Add driver for Silabs si1132, si1141/2/3 and si1145/6/7 ambient light, uv index and proximity sensors
The si114x supports x=1,2,3 IR LEDs for proximity sensing together with
visible and IR ambient light sensing (ALS).
Newer parts (si1132, si1145/6/7) can measure UV light and compute an UV
index
This was tested on si1143 and si1145
Signed-off-by: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
Signed-off-by: Crestez Dan Leonard <leonard.crestez@intel.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r-- | drivers/iio/light/Kconfig | 13 | ||||
-rw-r--r-- | drivers/iio/light/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/light/si1145.c | 1404 |
3 files changed, 1418 insertions, 0 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 0db0a0d1bba0..ba2e64d7ee58 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig | |||
@@ -267,6 +267,19 @@ config PA12203001 | |||
267 | This driver can also be built as a module. If so, the module | 267 | This driver can also be built as a module. If so, the module |
268 | will be called pa12203001. | 268 | will be called pa12203001. |
269 | 269 | ||
270 | config SI1145 | ||
271 | tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proximity sensor" | ||
272 | depends on I2C | ||
273 | select IIO_BUFFER | ||
274 | select IIO_TRIGGERED_BUFFER | ||
275 | help | ||
276 | Say Y here if you want to build a driver for the Silicon Labs SI1132 or | ||
277 | SI1141/2/3/5/6/7 combined ambient light, UV index and proximity sensor | ||
278 | chips. | ||
279 | |||
280 | To compile this driver as a module, choose M here: the module will be | ||
281 | called si1145. | ||
282 | |||
270 | config STK3310 | 283 | config STK3310 |
271 | tristate "STK3310 ALS and proximity sensor" | 284 | tristate "STK3310 ALS and proximity sensor" |
272 | depends on I2C | 285 | depends on I2C |
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 6f2a3c62de27..c5768df87a17 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile | |||
@@ -26,6 +26,7 @@ obj-$(CONFIG_OPT3001) += opt3001.o | |||
26 | obj-$(CONFIG_PA12203001) += pa12203001.o | 26 | obj-$(CONFIG_PA12203001) += pa12203001.o |
27 | obj-$(CONFIG_RPR0521) += rpr0521.o | 27 | obj-$(CONFIG_RPR0521) += rpr0521.o |
28 | obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o | 28 | obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o |
29 | obj-$(CONFIG_SI1145) += si1145.o | ||
29 | obj-$(CONFIG_STK3310) += stk3310.o | 30 | obj-$(CONFIG_STK3310) += stk3310.o |
30 | obj-$(CONFIG_TCS3414) += tcs3414.o | 31 | obj-$(CONFIG_TCS3414) += tcs3414.o |
31 | obj-$(CONFIG_TCS3472) += tcs3472.o | 32 | obj-$(CONFIG_TCS3472) += tcs3472.o |
diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c new file mode 100644 index 000000000000..096034c126a4 --- /dev/null +++ b/drivers/iio/light/si1145.c | |||
@@ -0,0 +1,1404 @@ | |||
1 | /* | ||
2 | * si1145.c - Support for Silabs SI1132 and SI1141/2/3/5/6/7 combined ambient | ||
3 | * light, UV index and proximity sensors | ||
4 | * | ||
5 | * Copyright 2014-16 Peter Meerwald-Stadler <pmeerw@pmeerw.net> | ||
6 | * Copyright 2016 Crestez Dan Leonard <leonard.crestez@intel.com> | ||
7 | * | ||
8 | * This file is subject to the terms and conditions of version 2 of | ||
9 | * the GNU General Public License. See the file COPYING in the main | ||
10 | * directory of this archive for more details. | ||
11 | * | ||
12 | * SI1132 (7-bit I2C slave address 0x60) | ||
13 | * SI1141/2/3 (7-bit I2C slave address 0x5a) | ||
14 | * SI1145/6/6 (7-bit I2C slave address 0x60) | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/irq.h> | ||
23 | #include <linux/gpio.h> | ||
24 | |||
25 | #include <linux/iio/iio.h> | ||
26 | #include <linux/iio/sysfs.h> | ||
27 | #include <linux/iio/trigger.h> | ||
28 | #include <linux/iio/trigger_consumer.h> | ||
29 | #include <linux/iio/triggered_buffer.h> | ||
30 | #include <linux/iio/buffer.h> | ||
31 | #include <linux/util_macros.h> | ||
32 | |||
33 | #define SI1145_REG_PART_ID 0x00 | ||
34 | #define SI1145_REG_REV_ID 0x01 | ||
35 | #define SI1145_REG_SEQ_ID 0x02 | ||
36 | #define SI1145_REG_INT_CFG 0x03 | ||
37 | #define SI1145_REG_IRQ_ENABLE 0x04 | ||
38 | #define SI1145_REG_IRQ_MODE 0x05 | ||
39 | #define SI1145_REG_HW_KEY 0x07 | ||
40 | #define SI1145_REG_MEAS_RATE 0x08 | ||
41 | #define SI1145_REG_PS_LED21 0x0f | ||
42 | #define SI1145_REG_PS_LED3 0x10 | ||
43 | #define SI1145_REG_UCOEF1 0x13 | ||
44 | #define SI1145_REG_UCOEF2 0x14 | ||
45 | #define SI1145_REG_UCOEF3 0x15 | ||
46 | #define SI1145_REG_UCOEF4 0x16 | ||
47 | #define SI1145_REG_PARAM_WR 0x17 | ||
48 | #define SI1145_REG_COMMAND 0x18 | ||
49 | #define SI1145_REG_RESPONSE 0x20 | ||
50 | #define SI1145_REG_IRQ_STATUS 0x21 | ||
51 | #define SI1145_REG_ALSVIS_DATA 0x22 | ||
52 | #define SI1145_REG_ALSIR_DATA 0x24 | ||
53 | #define SI1145_REG_PS1_DATA 0x26 | ||
54 | #define SI1145_REG_PS2_DATA 0x28 | ||
55 | #define SI1145_REG_PS3_DATA 0x2a | ||
56 | #define SI1145_REG_AUX_DATA 0x2c | ||
57 | #define SI1145_REG_PARAM_RD 0x2e | ||
58 | #define SI1145_REG_CHIP_STAT 0x30 | ||
59 | |||
60 | #define SI1145_UCOEF1_DEFAULT 0x7b | ||
61 | #define SI1145_UCOEF2_DEFAULT 0x6b | ||
62 | #define SI1145_UCOEF3_DEFAULT 0x01 | ||
63 | #define SI1145_UCOEF4_DEFAULT 0x00 | ||
64 | |||
65 | /* Helper to figure out PS_LED register / shift per channel */ | ||
66 | #define SI1145_PS_LED_REG(ch) \ | ||
67 | (((ch) == 2) ? SI1145_REG_PS_LED3 : SI1145_REG_PS_LED21) | ||
68 | #define SI1145_PS_LED_SHIFT(ch) \ | ||
69 | (((ch) == 1) ? 4 : 0) | ||
70 | |||
71 | /* Parameter offsets */ | ||
72 | #define SI1145_PARAM_CHLIST 0x01 | ||
73 | #define SI1145_PARAM_PSLED12_SELECT 0x02 | ||
74 | #define SI1145_PARAM_PSLED3_SELECT 0x03 | ||
75 | #define SI1145_PARAM_PS_ENCODING 0x05 | ||
76 | #define SI1145_PARAM_ALS_ENCODING 0x06 | ||
77 | #define SI1145_PARAM_PS1_ADC_MUX 0x07 | ||
78 | #define SI1145_PARAM_PS2_ADC_MUX 0x08 | ||
79 | #define SI1145_PARAM_PS3_ADC_MUX 0x09 | ||
80 | #define SI1145_PARAM_PS_ADC_COUNTER 0x0a | ||
81 | #define SI1145_PARAM_PS_ADC_GAIN 0x0b | ||
82 | #define SI1145_PARAM_PS_ADC_MISC 0x0c | ||
83 | #define SI1145_PARAM_ALS_ADC_MUX 0x0d | ||
84 | #define SI1145_PARAM_ALSIR_ADC_MUX 0x0e | ||
85 | #define SI1145_PARAM_AUX_ADC_MUX 0x0f | ||
86 | #define SI1145_PARAM_ALSVIS_ADC_COUNTER 0x10 | ||
87 | #define SI1145_PARAM_ALSVIS_ADC_GAIN 0x11 | ||
88 | #define SI1145_PARAM_ALSVIS_ADC_MISC 0x12 | ||
89 | #define SI1145_PARAM_LED_RECOVERY 0x1c | ||
90 | #define SI1145_PARAM_ALSIR_ADC_COUNTER 0x1d | ||
91 | #define SI1145_PARAM_ALSIR_ADC_GAIN 0x1e | ||
92 | #define SI1145_PARAM_ALSIR_ADC_MISC 0x1f | ||
93 | #define SI1145_PARAM_ADC_OFFSET 0x1a | ||
94 | |||
95 | /* Channel enable masks for CHLIST parameter */ | ||
96 | #define SI1145_CHLIST_EN_PS1 BIT(0) | ||
97 | #define SI1145_CHLIST_EN_PS2 BIT(1) | ||
98 | #define SI1145_CHLIST_EN_PS3 BIT(2) | ||
99 | #define SI1145_CHLIST_EN_ALSVIS BIT(4) | ||
100 | #define SI1145_CHLIST_EN_ALSIR BIT(5) | ||
101 | #define SI1145_CHLIST_EN_AUX BIT(6) | ||
102 | #define SI1145_CHLIST_EN_UV BIT(7) | ||
103 | |||
104 | /* Proximity measurement mode for ADC_MISC parameter */ | ||
105 | #define SI1145_PS_ADC_MODE_NORMAL BIT(2) | ||
106 | /* Signal range mask for ADC_MISC parameter */ | ||
107 | #define SI1145_ADC_MISC_RANGE BIT(5) | ||
108 | |||
109 | /* Commands for REG_COMMAND */ | ||
110 | #define SI1145_CMD_NOP 0x00 | ||
111 | #define SI1145_CMD_RESET 0x01 | ||
112 | #define SI1145_CMD_PS_FORCE 0x05 | ||
113 | #define SI1145_CMD_ALS_FORCE 0x06 | ||
114 | #define SI1145_CMD_PSALS_FORCE 0x07 | ||
115 | #define SI1145_CMD_PS_PAUSE 0x09 | ||
116 | #define SI1145_CMD_ALS_PAUSE 0x0a | ||
117 | #define SI1145_CMD_PSALS_PAUSE 0x0b | ||
118 | #define SI1145_CMD_PS_AUTO 0x0d | ||
119 | #define SI1145_CMD_ALS_AUTO 0x0e | ||
120 | #define SI1145_CMD_PSALS_AUTO 0x0f | ||
121 | #define SI1145_CMD_PARAM_QUERY 0x80 | ||
122 | #define SI1145_CMD_PARAM_SET 0xa0 | ||
123 | |||
124 | #define SI1145_RSP_INVALID_SETTING 0x80 | ||
125 | #define SI1145_RSP_COUNTER_MASK 0x0F | ||
126 | |||
127 | /* Minimum sleep after each command to ensure it's received */ | ||
128 | #define SI1145_COMMAND_MINSLEEP_MS 5 | ||
129 | /* Return -ETIMEDOUT after this long */ | ||
130 | #define SI1145_COMMAND_TIMEOUT_MS 25 | ||
131 | |||
132 | /* Interrupt configuration masks for INT_CFG register */ | ||
133 | #define SI1145_INT_CFG_OE BIT(0) /* enable interrupt */ | ||
134 | #define SI1145_INT_CFG_MODE BIT(1) /* auto reset interrupt pin */ | ||
135 | |||
136 | /* Interrupt enable masks for IRQ_ENABLE register */ | ||
137 | #define SI1145_MASK_ALL_IE (BIT(4) | BIT(3) | BIT(2) | BIT(0)) | ||
138 | |||
139 | #define SI1145_MUX_TEMP 0x65 | ||
140 | #define SI1145_MUX_VDD 0x75 | ||
141 | |||
142 | /* Proximity LED current; see Table 2 in datasheet */ | ||
143 | #define SI1145_LED_CURRENT_45mA 0x04 | ||
144 | |||
145 | enum { | ||
146 | SI1132, | ||
147 | SI1141, | ||
148 | SI1142, | ||
149 | SI1143, | ||
150 | SI1145, | ||
151 | SI1146, | ||
152 | SI1147, | ||
153 | }; | ||
154 | |||
155 | struct si1145_part_info { | ||
156 | u8 part; | ||
157 | const struct iio_info *iio_info; | ||
158 | const struct iio_chan_spec *channels; | ||
159 | unsigned int num_channels; | ||
160 | unsigned int num_leds; | ||
161 | bool uncompressed_meas_rate; | ||
162 | }; | ||
163 | |||
164 | /** | ||
165 | * struct si1145_data - si1145 chip state data | ||
166 | * @client: I2C client | ||
167 | * @lock: mutex to protect shared state. | ||
168 | * @cmdlock: Low-level mutex to protect command execution only | ||
169 | * @rsp_seq: Next expected response number or -1 if counter reset required | ||
170 | * @scan_mask: Saved scan mask to avoid duplicate set_chlist | ||
171 | * @autonomous: If automatic measurements are active (for buffer support) | ||
172 | * @part_info: Part information | ||
173 | * @trig: Pointer to iio trigger | ||
174 | * @meas_rate: Value of MEAS_RATE register. Only set in HW in auto mode | ||
175 | */ | ||
176 | struct si1145_data { | ||
177 | struct i2c_client *client; | ||
178 | struct mutex lock; | ||
179 | struct mutex cmdlock; | ||
180 | int rsp_seq; | ||
181 | const struct si1145_part_info *part_info; | ||
182 | unsigned long scan_mask; | ||
183 | bool autonomous; | ||
184 | struct iio_trigger *trig; | ||
185 | int meas_rate; | ||
186 | }; | ||
187 | |||
188 | /** | ||
189 | * __si1145_command_reset() - Send CMD_NOP and wait for response 0 | ||
190 | * | ||
191 | * Does not modify data->rsp_seq | ||
192 | * | ||
193 | * Return: 0 on success and -errno on error. | ||
194 | */ | ||
195 | static int __si1145_command_reset(struct si1145_data *data) | ||
196 | { | ||
197 | struct device *dev = &data->client->dev; | ||
198 | unsigned long stop_jiffies; | ||
199 | int ret; | ||
200 | |||
201 | ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND, | ||
202 | SI1145_CMD_NOP); | ||
203 | if (ret < 0) | ||
204 | return ret; | ||
205 | msleep(SI1145_COMMAND_MINSLEEP_MS); | ||
206 | |||
207 | stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000; | ||
208 | while (true) { | ||
209 | ret = i2c_smbus_read_byte_data(data->client, | ||
210 | SI1145_REG_RESPONSE); | ||
211 | if (ret <= 0) | ||
212 | return ret; | ||
213 | if (time_after(jiffies, stop_jiffies)) { | ||
214 | dev_warn(dev, "timeout on reset\n"); | ||
215 | return -ETIMEDOUT; | ||
216 | } | ||
217 | msleep(SI1145_COMMAND_MINSLEEP_MS); | ||
218 | continue; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * si1145_command() - Execute a command and poll the response register | ||
224 | * | ||
225 | * All conversion overflows are reported as -EOVERFLOW | ||
226 | * INVALID_SETTING is reported as -EINVAL | ||
227 | * Timeouts are reported as -ETIMEDOUT | ||
228 | * | ||
229 | * Return: 0 on success or -errno on failure | ||
230 | */ | ||
231 | static int si1145_command(struct si1145_data *data, u8 cmd) | ||
232 | { | ||
233 | struct device *dev = &data->client->dev; | ||
234 | unsigned long stop_jiffies; | ||
235 | int ret; | ||
236 | |||
237 | mutex_lock(&data->cmdlock); | ||
238 | |||
239 | if (data->rsp_seq < 0) { | ||
240 | ret = __si1145_command_reset(data); | ||
241 | if (ret < 0) { | ||
242 | dev_err(dev, "failed to reset command counter, ret=%d\n", | ||
243 | ret); | ||
244 | goto out; | ||
245 | } | ||
246 | data->rsp_seq = 0; | ||
247 | } | ||
248 | |||
249 | ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND, cmd); | ||
250 | if (ret) { | ||
251 | dev_warn(dev, "failed to write command, ret=%d\n", ret); | ||
252 | goto out; | ||
253 | } | ||
254 | /* Sleep a little to ensure the command is received */ | ||
255 | msleep(SI1145_COMMAND_MINSLEEP_MS); | ||
256 | |||
257 | stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000; | ||
258 | while (true) { | ||
259 | ret = i2c_smbus_read_byte_data(data->client, | ||
260 | SI1145_REG_RESPONSE); | ||
261 | if (ret < 0) { | ||
262 | dev_warn(dev, "failed to read response, ret=%d\n", ret); | ||
263 | break; | ||
264 | } | ||
265 | |||
266 | if ((ret & ~SI1145_RSP_COUNTER_MASK) == 0) { | ||
267 | if (ret == data->rsp_seq) { | ||
268 | if (time_after(jiffies, stop_jiffies)) { | ||
269 | dev_warn(dev, "timeout on command %#02hhx\n", | ||
270 | cmd); | ||
271 | ret = -ETIMEDOUT; | ||
272 | break; | ||
273 | } | ||
274 | msleep(SI1145_COMMAND_MINSLEEP_MS); | ||
275 | continue; | ||
276 | } | ||
277 | if (ret == ((data->rsp_seq + 1) & | ||
278 | SI1145_RSP_COUNTER_MASK)) { | ||
279 | data->rsp_seq = ret; | ||
280 | ret = 0; | ||
281 | break; | ||
282 | } | ||
283 | dev_warn(dev, "unexpected response counter %d instead of %d\n", | ||
284 | ret, (data->rsp_seq + 1) & | ||
285 | SI1145_RSP_COUNTER_MASK); | ||
286 | ret = -EIO; | ||
287 | } else { | ||
288 | if (ret == SI1145_RSP_INVALID_SETTING) { | ||
289 | dev_warn(dev, "INVALID_SETTING error on command %#02hhx\n", | ||
290 | cmd); | ||
291 | ret = -EINVAL; | ||
292 | } else { | ||
293 | /* All overflows are treated identically */ | ||
294 | dev_dbg(dev, "overflow, ret=%d, cmd=%#02hhx\n", | ||
295 | ret, cmd); | ||
296 | ret = -EOVERFLOW; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | /* Force a counter reset next time */ | ||
301 | data->rsp_seq = -1; | ||
302 | break; | ||
303 | } | ||
304 | |||
305 | out: | ||
306 | mutex_unlock(&data->cmdlock); | ||
307 | |||
308 | return ret; | ||
309 | } | ||
310 | |||
311 | static int si1145_param_update(struct si1145_data *data, u8 op, u8 param, | ||
312 | u8 value) | ||
313 | { | ||
314 | int ret; | ||
315 | |||
316 | ret = i2c_smbus_write_byte_data(data->client, | ||
317 | SI1145_REG_PARAM_WR, value); | ||
318 | if (ret < 0) | ||
319 | return ret; | ||
320 | |||
321 | return si1145_command(data, op | (param & 0x1F)); | ||
322 | } | ||
323 | |||
324 | static int si1145_param_set(struct si1145_data *data, u8 param, u8 value) | ||
325 | { | ||
326 | return si1145_param_update(data, SI1145_CMD_PARAM_SET, param, value); | ||
327 | } | ||
328 | |||
329 | /* Set param. Returns negative errno or current value */ | ||
330 | static int si1145_param_query(struct si1145_data *data, u8 param) | ||
331 | { | ||
332 | int ret; | ||
333 | |||
334 | ret = si1145_command(data, SI1145_CMD_PARAM_QUERY | (param & 0x1F)); | ||
335 | if (ret < 0) | ||
336 | return ret; | ||
337 | |||
338 | return i2c_smbus_read_byte_data(data->client, SI1145_REG_PARAM_RD); | ||
339 | } | ||
340 | |||
341 | /* Expand 8 bit compressed value to 16 bit, see Silabs AN498 */ | ||
342 | static u16 si1145_uncompress(u8 x) | ||
343 | { | ||
344 | u16 result = 0; | ||
345 | u8 exponent = 0; | ||
346 | |||
347 | if (x < 8) | ||
348 | return 0; | ||
349 | |||
350 | exponent = (x & 0xf0) >> 4; | ||
351 | result = 0x10 | (x & 0x0f); | ||
352 | |||
353 | if (exponent >= 4) | ||
354 | return result << (exponent - 4); | ||
355 | return result >> (4 - exponent); | ||
356 | } | ||
357 | |||
358 | /* Compress 16 bit value to 8 bit, see Silabs AN498 */ | ||
359 | static u8 si1145_compress(u16 x) | ||
360 | { | ||
361 | u32 exponent = 0; | ||
362 | u32 significand = 0; | ||
363 | u32 tmp = x; | ||
364 | |||
365 | if (x == 0x0000) | ||
366 | return 0x00; | ||
367 | if (x == 0x0001) | ||
368 | return 0x08; | ||
369 | |||
370 | while (1) { | ||
371 | tmp >>= 1; | ||
372 | exponent += 1; | ||
373 | if (tmp == 1) | ||
374 | break; | ||
375 | } | ||
376 | |||
377 | if (exponent < 5) { | ||
378 | significand = x << (4 - exponent); | ||
379 | return (exponent << 4) | (significand & 0xF); | ||
380 | } | ||
381 | |||
382 | significand = x >> (exponent - 5); | ||
383 | if (significand & 1) { | ||
384 | significand += 2; | ||
385 | if (significand & 0x0040) { | ||
386 | exponent += 1; | ||
387 | significand >>= 1; | ||
388 | } | ||
389 | } | ||
390 | |||
391 | return (exponent << 4) | ((significand >> 1) & 0xF); | ||
392 | } | ||
393 | |||
394 | /* Write meas_rate in hardware */ | ||
395 | static int si1145_set_meas_rate(struct si1145_data *data, int interval) | ||
396 | { | ||
397 | if (data->part_info->uncompressed_meas_rate) | ||
398 | return i2c_smbus_write_word_data(data->client, | ||
399 | SI1145_REG_MEAS_RATE, interval); | ||
400 | else | ||
401 | return i2c_smbus_write_byte_data(data->client, | ||
402 | SI1145_REG_MEAS_RATE, interval); | ||
403 | } | ||
404 | |||
405 | static int si1145_read_samp_freq(struct si1145_data *data, int *val, int *val2) | ||
406 | { | ||
407 | *val = 32000; | ||
408 | if (data->part_info->uncompressed_meas_rate) | ||
409 | *val2 = data->meas_rate; | ||
410 | else | ||
411 | *val2 = si1145_uncompress(data->meas_rate); | ||
412 | return IIO_VAL_FRACTIONAL; | ||
413 | } | ||
414 | |||
415 | /* Set the samp freq in driver private data */ | ||
416 | static int si1145_store_samp_freq(struct si1145_data *data, int val) | ||
417 | { | ||
418 | int ret = 0; | ||
419 | int meas_rate; | ||
420 | |||
421 | if (val <= 0 || val > 32000) | ||
422 | return -ERANGE; | ||
423 | meas_rate = 32000 / val; | ||
424 | |||
425 | mutex_lock(&data->lock); | ||
426 | if (data->autonomous) { | ||
427 | ret = si1145_set_meas_rate(data, meas_rate); | ||
428 | if (ret) | ||
429 | goto out; | ||
430 | } | ||
431 | if (data->part_info->uncompressed_meas_rate) | ||
432 | data->meas_rate = meas_rate; | ||
433 | else | ||
434 | data->meas_rate = si1145_compress(meas_rate); | ||
435 | |||
436 | out: | ||
437 | mutex_unlock(&data->lock); | ||
438 | |||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | static irqreturn_t si1145_trigger_handler(int irq, void *private) | ||
443 | { | ||
444 | struct iio_poll_func *pf = private; | ||
445 | struct iio_dev *indio_dev = pf->indio_dev; | ||
446 | struct si1145_data *data = iio_priv(indio_dev); | ||
447 | /* | ||
448 | * Maximum buffer size: | ||
449 | * 6*2 bytes channels data + 4 bytes alignment + | ||
450 | * 8 bytes timestamp | ||
451 | */ | ||
452 | u8 buffer[24]; | ||
453 | int i, j = 0; | ||
454 | int ret; | ||
455 | u8 irq_status = 0; | ||
456 | |||
457 | if (!data->autonomous) { | ||
458 | ret = si1145_command(data, SI1145_CMD_PSALS_FORCE); | ||
459 | if (ret < 0 && ret != -EOVERFLOW) | ||
460 | goto done; | ||
461 | } else { | ||
462 | irq_status = ret = i2c_smbus_read_byte_data(data->client, | ||
463 | SI1145_REG_IRQ_STATUS); | ||
464 | if (ret < 0) | ||
465 | goto done; | ||
466 | if (!(irq_status & SI1145_MASK_ALL_IE)) | ||
467 | goto done; | ||
468 | } | ||
469 | |||
470 | for_each_set_bit(i, indio_dev->active_scan_mask, | ||
471 | indio_dev->masklength) { | ||
472 | int run = 1; | ||
473 | |||
474 | while (i + run < indio_dev->masklength) { | ||
475 | if (!test_bit(i + run, indio_dev->active_scan_mask)) | ||
476 | break; | ||
477 | if (indio_dev->channels[i + run].address != | ||
478 | indio_dev->channels[i].address + 2 * run) | ||
479 | break; | ||
480 | run++; | ||
481 | } | ||
482 | |||
483 | ret = i2c_smbus_read_i2c_block_data_or_emulated( | ||
484 | data->client, indio_dev->channels[i].address, | ||
485 | sizeof(u16) * run, &buffer[j]); | ||
486 | if (ret < 0) | ||
487 | goto done; | ||
488 | j += run * sizeof(u16); | ||
489 | i += run - 1; | ||
490 | } | ||
491 | |||
492 | if (data->autonomous) { | ||
493 | ret = i2c_smbus_write_byte_data(data->client, | ||
494 | SI1145_REG_IRQ_STATUS, | ||
495 | irq_status & SI1145_MASK_ALL_IE); | ||
496 | if (ret < 0) | ||
497 | goto done; | ||
498 | } | ||
499 | |||
500 | iio_push_to_buffers_with_timestamp(indio_dev, buffer, | ||
501 | iio_get_time_ns(indio_dev)); | ||
502 | |||
503 | done: | ||
504 | iio_trigger_notify_done(indio_dev->trig); | ||
505 | return IRQ_HANDLED; | ||
506 | } | ||
507 | |||
508 | static int si1145_set_chlist(struct iio_dev *indio_dev, unsigned long scan_mask) | ||
509 | { | ||
510 | struct si1145_data *data = iio_priv(indio_dev); | ||
511 | u8 reg = 0, mux; | ||
512 | int ret; | ||
513 | int i; | ||
514 | |||
515 | /* channel list already set, no need to reprogram */ | ||
516 | if (data->scan_mask == scan_mask) | ||
517 | return 0; | ||
518 | |||
519 | for_each_set_bit(i, &scan_mask, indio_dev->masklength) { | ||
520 | switch (indio_dev->channels[i].address) { | ||
521 | case SI1145_REG_ALSVIS_DATA: | ||
522 | reg |= SI1145_CHLIST_EN_ALSVIS; | ||
523 | break; | ||
524 | case SI1145_REG_ALSIR_DATA: | ||
525 | reg |= SI1145_CHLIST_EN_ALSIR; | ||
526 | break; | ||
527 | case SI1145_REG_PS1_DATA: | ||
528 | reg |= SI1145_CHLIST_EN_PS1; | ||
529 | break; | ||
530 | case SI1145_REG_PS2_DATA: | ||
531 | reg |= SI1145_CHLIST_EN_PS2; | ||
532 | break; | ||
533 | case SI1145_REG_PS3_DATA: | ||
534 | reg |= SI1145_CHLIST_EN_PS3; | ||
535 | break; | ||
536 | case SI1145_REG_AUX_DATA: | ||
537 | switch (indio_dev->channels[i].type) { | ||
538 | case IIO_UVINDEX: | ||
539 | reg |= SI1145_CHLIST_EN_UV; | ||
540 | break; | ||
541 | default: | ||
542 | reg |= SI1145_CHLIST_EN_AUX; | ||
543 | if (indio_dev->channels[i].type == IIO_TEMP) | ||
544 | mux = SI1145_MUX_TEMP; | ||
545 | else | ||
546 | mux = SI1145_MUX_VDD; | ||
547 | ret = si1145_param_set(data, | ||
548 | SI1145_PARAM_AUX_ADC_MUX, mux); | ||
549 | if (ret < 0) | ||
550 | return ret; | ||
551 | |||
552 | break; | ||
553 | } | ||
554 | } | ||
555 | } | ||
556 | |||
557 | data->scan_mask = scan_mask; | ||
558 | ret = si1145_param_set(data, SI1145_PARAM_CHLIST, reg); | ||
559 | |||
560 | return ret < 0 ? ret : 0; | ||
561 | } | ||
562 | |||
563 | static int si1145_measure(struct iio_dev *indio_dev, | ||
564 | struct iio_chan_spec const *chan) | ||
565 | { | ||
566 | struct si1145_data *data = iio_priv(indio_dev); | ||
567 | u8 cmd; | ||
568 | int ret; | ||
569 | |||
570 | ret = si1145_set_chlist(indio_dev, BIT(chan->scan_index)); | ||
571 | if (ret < 0) | ||
572 | return ret; | ||
573 | |||
574 | cmd = (chan->type == IIO_PROXIMITY) ? SI1145_CMD_PS_FORCE : | ||
575 | SI1145_CMD_ALS_FORCE; | ||
576 | ret = si1145_command(data, cmd); | ||
577 | if (ret < 0 && ret != -EOVERFLOW) | ||
578 | return ret; | ||
579 | |||
580 | return i2c_smbus_read_word_data(data->client, chan->address); | ||
581 | } | ||
582 | |||
583 | /* | ||
584 | * Conversion between iio scale and ADC_GAIN values | ||
585 | * These could be further adjusted but proximity/intensity are dimensionless | ||
586 | */ | ||
587 | static const int si1145_proximity_scale_available[] = { | ||
588 | 128, 64, 32, 16, 8, 4}; | ||
589 | static const int si1145_intensity_scale_available[] = { | ||
590 | 128, 64, 32, 16, 8, 4, 2, 1}; | ||
591 | static IIO_CONST_ATTR(in_proximity_scale_available, | ||
592 | "128 64 32 16 8 4"); | ||
593 | static IIO_CONST_ATTR(in_intensity_scale_available, | ||
594 | "128 64 32 16 8 4 2 1"); | ||
595 | static IIO_CONST_ATTR(in_intensity_ir_scale_available, | ||
596 | "128 64 32 16 8 4 2 1"); | ||
597 | |||
598 | static int si1145_scale_from_adcgain(int regval) | ||
599 | { | ||
600 | return 128 >> regval; | ||
601 | } | ||
602 | |||
603 | static int si1145_proximity_adcgain_from_scale(int val, int val2) | ||
604 | { | ||
605 | val = find_closest_descending(val, si1145_proximity_scale_available, | ||
606 | ARRAY_SIZE(si1145_proximity_scale_available)); | ||
607 | if (val < 0 || val > 5 || val2 != 0) | ||
608 | return -EINVAL; | ||
609 | |||
610 | return val; | ||
611 | } | ||
612 | |||
613 | static int si1145_intensity_adcgain_from_scale(int val, int val2) | ||
614 | { | ||
615 | val = find_closest_descending(val, si1145_intensity_scale_available, | ||
616 | ARRAY_SIZE(si1145_intensity_scale_available)); | ||
617 | if (val < 0 || val > 7 || val2 != 0) | ||
618 | return -EINVAL; | ||
619 | |||
620 | return val; | ||
621 | } | ||
622 | |||
623 | static int si1145_read_raw(struct iio_dev *indio_dev, | ||
624 | struct iio_chan_spec const *chan, | ||
625 | int *val, int *val2, long mask) | ||
626 | { | ||
627 | struct si1145_data *data = iio_priv(indio_dev); | ||
628 | int ret; | ||
629 | u8 reg; | ||
630 | |||
631 | switch (mask) { | ||
632 | case IIO_CHAN_INFO_RAW: | ||
633 | switch (chan->type) { | ||
634 | case IIO_INTENSITY: | ||
635 | case IIO_PROXIMITY: | ||
636 | case IIO_VOLTAGE: | ||
637 | case IIO_TEMP: | ||
638 | case IIO_UVINDEX: | ||
639 | ret = iio_device_claim_direct_mode(indio_dev); | ||
640 | if (ret) | ||
641 | return ret; | ||
642 | ret = si1145_measure(indio_dev, chan); | ||
643 | iio_device_release_direct_mode(indio_dev); | ||
644 | |||
645 | if (ret < 0) | ||
646 | return ret; | ||
647 | |||
648 | *val = ret; | ||
649 | |||
650 | return IIO_VAL_INT; | ||
651 | case IIO_CURRENT: | ||
652 | ret = i2c_smbus_read_byte_data(data->client, | ||
653 | SI1145_PS_LED_REG(chan->channel)); | ||
654 | if (ret < 0) | ||
655 | return ret; | ||
656 | |||
657 | *val = (ret >> SI1145_PS_LED_SHIFT(chan->channel)) | ||
658 | & 0x0f; | ||
659 | |||
660 | return IIO_VAL_INT; | ||
661 | default: | ||
662 | return -EINVAL; | ||
663 | } | ||
664 | case IIO_CHAN_INFO_SCALE: | ||
665 | switch (chan->type) { | ||
666 | case IIO_PROXIMITY: | ||
667 | reg = SI1145_PARAM_PS_ADC_GAIN; | ||
668 | break; | ||
669 | case IIO_INTENSITY: | ||
670 | if (chan->channel2 == IIO_MOD_LIGHT_IR) | ||
671 | reg = SI1145_PARAM_ALSIR_ADC_GAIN; | ||
672 | else | ||
673 | reg = SI1145_PARAM_ALSVIS_ADC_GAIN; | ||
674 | break; | ||
675 | case IIO_TEMP: | ||
676 | *val = 28; | ||
677 | *val2 = 571429; | ||
678 | return IIO_VAL_INT_PLUS_MICRO; | ||
679 | case IIO_UVINDEX: | ||
680 | *val = 0; | ||
681 | *val2 = 10000; | ||
682 | return IIO_VAL_INT_PLUS_MICRO; | ||
683 | default: | ||
684 | return -EINVAL; | ||
685 | } | ||
686 | |||
687 | ret = si1145_param_query(data, reg); | ||
688 | if (ret < 0) | ||
689 | return ret; | ||
690 | |||
691 | *val = si1145_scale_from_adcgain(ret & 0x07); | ||
692 | |||
693 | return IIO_VAL_INT; | ||
694 | case IIO_CHAN_INFO_OFFSET: | ||
695 | switch (chan->type) { | ||
696 | case IIO_TEMP: | ||
697 | /* | ||
698 | * -ADC offset - ADC counts @ 25°C - | ||
699 | * 35 * ADC counts / °C | ||
700 | */ | ||
701 | *val = -256 - 11136 + 25 * 35; | ||
702 | return IIO_VAL_INT; | ||
703 | default: | ||
704 | /* | ||
705 | * All ADC measurements have are by default offset | ||
706 | * by -256 | ||
707 | * See AN498 5.6.3 | ||
708 | */ | ||
709 | ret = si1145_param_query(data, SI1145_PARAM_ADC_OFFSET); | ||
710 | if (ret < 0) | ||
711 | return ret; | ||
712 | *val = -si1145_uncompress(ret); | ||
713 | return IIO_VAL_INT; | ||
714 | } | ||
715 | case IIO_CHAN_INFO_SAMP_FREQ: | ||
716 | return si1145_read_samp_freq(data, val, val2); | ||
717 | default: | ||
718 | return -EINVAL; | ||
719 | } | ||
720 | } | ||
721 | |||
722 | static int si1145_write_raw(struct iio_dev *indio_dev, | ||
723 | struct iio_chan_spec const *chan, | ||
724 | int val, int val2, long mask) | ||
725 | { | ||
726 | struct si1145_data *data = iio_priv(indio_dev); | ||
727 | u8 reg1, reg2, shift; | ||
728 | int ret; | ||
729 | |||
730 | switch (mask) { | ||
731 | case IIO_CHAN_INFO_SCALE: | ||
732 | switch (chan->type) { | ||
733 | case IIO_PROXIMITY: | ||
734 | val = si1145_proximity_adcgain_from_scale(val, val2); | ||
735 | if (val < 0) | ||
736 | return val; | ||
737 | reg1 = SI1145_PARAM_PS_ADC_GAIN; | ||
738 | reg2 = SI1145_PARAM_PS_ADC_COUNTER; | ||
739 | break; | ||
740 | case IIO_INTENSITY: | ||
741 | val = si1145_intensity_adcgain_from_scale(val, val2); | ||
742 | if (val < 0) | ||
743 | return val; | ||
744 | if (chan->channel2 == IIO_MOD_LIGHT_IR) { | ||
745 | reg1 = SI1145_PARAM_ALSIR_ADC_GAIN; | ||
746 | reg2 = SI1145_PARAM_ALSIR_ADC_COUNTER; | ||
747 | } else { | ||
748 | reg1 = SI1145_PARAM_ALSVIS_ADC_GAIN; | ||
749 | reg2 = SI1145_PARAM_ALSVIS_ADC_COUNTER; | ||
750 | } | ||
751 | break; | ||
752 | default: | ||
753 | return -EINVAL; | ||
754 | } | ||
755 | |||
756 | ret = iio_device_claim_direct_mode(indio_dev); | ||
757 | if (ret) | ||
758 | return ret; | ||
759 | |||
760 | ret = si1145_param_set(data, reg1, val); | ||
761 | if (ret < 0) { | ||
762 | iio_device_release_direct_mode(indio_dev); | ||
763 | return ret; | ||
764 | } | ||
765 | /* Set recovery period to one's complement of gain */ | ||
766 | ret = si1145_param_set(data, reg2, (~val & 0x07) << 4); | ||
767 | iio_device_release_direct_mode(indio_dev); | ||
768 | return ret; | ||
769 | case IIO_CHAN_INFO_RAW: | ||
770 | if (chan->type != IIO_CURRENT) | ||
771 | return -EINVAL; | ||
772 | |||
773 | if (val < 0 || val > 15 || val2 != 0) | ||
774 | return -EINVAL; | ||
775 | |||
776 | reg1 = SI1145_PS_LED_REG(chan->channel); | ||
777 | shift = SI1145_PS_LED_SHIFT(chan->channel); | ||
778 | |||
779 | ret = iio_device_claim_direct_mode(indio_dev); | ||
780 | if (ret) | ||
781 | return ret; | ||
782 | |||
783 | ret = i2c_smbus_read_byte_data(data->client, reg1); | ||
784 | if (ret < 0) { | ||
785 | iio_device_release_direct_mode(indio_dev); | ||
786 | return ret; | ||
787 | } | ||
788 | ret = i2c_smbus_write_byte_data(data->client, reg1, | ||
789 | (ret & ~(0x0f << shift)) | | ||
790 | ((val & 0x0f) << shift)); | ||
791 | iio_device_release_direct_mode(indio_dev); | ||
792 | return ret; | ||
793 | case IIO_CHAN_INFO_SAMP_FREQ: | ||
794 | return si1145_store_samp_freq(data, val); | ||
795 | default: | ||
796 | return -EINVAL; | ||
797 | } | ||
798 | } | ||
799 | |||
800 | #define SI1145_ST { \ | ||
801 | .sign = 'u', \ | ||
802 | .realbits = 16, \ | ||
803 | .storagebits = 16, \ | ||
804 | .endianness = IIO_LE, \ | ||
805 | } | ||
806 | |||
807 | #define SI1145_INTENSITY_CHANNEL(_si) { \ | ||
808 | .type = IIO_INTENSITY, \ | ||
809 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | ||
810 | BIT(IIO_CHAN_INFO_OFFSET) | \ | ||
811 | BIT(IIO_CHAN_INFO_SCALE), \ | ||
812 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | ||
813 | .scan_type = SI1145_ST, \ | ||
814 | .scan_index = _si, \ | ||
815 | .address = SI1145_REG_ALSVIS_DATA, \ | ||
816 | } | ||
817 | |||
818 | #define SI1145_INTENSITY_IR_CHANNEL(_si) { \ | ||
819 | .type = IIO_INTENSITY, \ | ||
820 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | ||
821 | BIT(IIO_CHAN_INFO_OFFSET) | \ | ||
822 | BIT(IIO_CHAN_INFO_SCALE), \ | ||
823 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | ||
824 | .modified = 1, \ | ||
825 | .channel2 = IIO_MOD_LIGHT_IR, \ | ||
826 | .scan_type = SI1145_ST, \ | ||
827 | .scan_index = _si, \ | ||
828 | .address = SI1145_REG_ALSIR_DATA, \ | ||
829 | } | ||
830 | |||
831 | #define SI1145_TEMP_CHANNEL(_si) { \ | ||
832 | .type = IIO_TEMP, \ | ||
833 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | ||
834 | BIT(IIO_CHAN_INFO_OFFSET) | \ | ||
835 | BIT(IIO_CHAN_INFO_SCALE), \ | ||
836 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | ||
837 | .scan_type = SI1145_ST, \ | ||
838 | .scan_index = _si, \ | ||
839 | .address = SI1145_REG_AUX_DATA, \ | ||
840 | } | ||
841 | |||
842 | #define SI1145_UV_CHANNEL(_si) { \ | ||
843 | .type = IIO_UVINDEX, \ | ||
844 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | ||
845 | BIT(IIO_CHAN_INFO_SCALE), \ | ||
846 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | ||
847 | .scan_type = SI1145_ST, \ | ||
848 | .scan_index = _si, \ | ||
849 | .address = SI1145_REG_AUX_DATA, \ | ||
850 | } | ||
851 | |||
852 | #define SI1145_PROXIMITY_CHANNEL(_si, _ch) { \ | ||
853 | .type = IIO_PROXIMITY, \ | ||
854 | .indexed = 1, \ | ||
855 | .channel = _ch, \ | ||
856 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | ||
857 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ | ||
858 | BIT(IIO_CHAN_INFO_OFFSET), \ | ||
859 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | ||
860 | .scan_type = SI1145_ST, \ | ||
861 | .scan_index = _si, \ | ||
862 | .address = SI1145_REG_PS1_DATA + _ch * 2, \ | ||
863 | } | ||
864 | |||
865 | #define SI1145_VOLTAGE_CHANNEL(_si) { \ | ||
866 | .type = IIO_VOLTAGE, \ | ||
867 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | ||
868 | .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | ||
869 | .scan_type = SI1145_ST, \ | ||
870 | .scan_index = _si, \ | ||
871 | .address = SI1145_REG_AUX_DATA, \ | ||
872 | } | ||
873 | |||
874 | #define SI1145_CURRENT_CHANNEL(_ch) { \ | ||
875 | .type = IIO_CURRENT, \ | ||
876 | .indexed = 1, \ | ||
877 | .channel = _ch, \ | ||
878 | .output = 1, \ | ||
879 | .scan_index = -1, \ | ||
880 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | ||
881 | } | ||
882 | |||
883 | static const struct iio_chan_spec si1132_channels[] = { | ||
884 | SI1145_INTENSITY_CHANNEL(0), | ||
885 | SI1145_INTENSITY_IR_CHANNEL(1), | ||
886 | SI1145_TEMP_CHANNEL(2), | ||
887 | SI1145_VOLTAGE_CHANNEL(3), | ||
888 | SI1145_UV_CHANNEL(4), | ||
889 | IIO_CHAN_SOFT_TIMESTAMP(6), | ||
890 | }; | ||
891 | |||
892 | static const struct iio_chan_spec si1141_channels[] = { | ||
893 | SI1145_INTENSITY_CHANNEL(0), | ||
894 | SI1145_INTENSITY_IR_CHANNEL(1), | ||
895 | SI1145_PROXIMITY_CHANNEL(2, 0), | ||
896 | SI1145_TEMP_CHANNEL(3), | ||
897 | SI1145_VOLTAGE_CHANNEL(4), | ||
898 | IIO_CHAN_SOFT_TIMESTAMP(5), | ||
899 | SI1145_CURRENT_CHANNEL(0), | ||
900 | }; | ||
901 | |||
902 | static const struct iio_chan_spec si1142_channels[] = { | ||
903 | SI1145_INTENSITY_CHANNEL(0), | ||
904 | SI1145_INTENSITY_IR_CHANNEL(1), | ||
905 | SI1145_PROXIMITY_CHANNEL(2, 0), | ||
906 | SI1145_PROXIMITY_CHANNEL(3, 1), | ||
907 | SI1145_TEMP_CHANNEL(4), | ||
908 | SI1145_VOLTAGE_CHANNEL(5), | ||
909 | IIO_CHAN_SOFT_TIMESTAMP(6), | ||
910 | SI1145_CURRENT_CHANNEL(0), | ||
911 | SI1145_CURRENT_CHANNEL(1), | ||
912 | }; | ||
913 | |||
914 | static const struct iio_chan_spec si1143_channels[] = { | ||
915 | SI1145_INTENSITY_CHANNEL(0), | ||
916 | SI1145_INTENSITY_IR_CHANNEL(1), | ||
917 | SI1145_PROXIMITY_CHANNEL(2, 0), | ||
918 | SI1145_PROXIMITY_CHANNEL(3, 1), | ||
919 | SI1145_PROXIMITY_CHANNEL(4, 2), | ||
920 | SI1145_TEMP_CHANNEL(5), | ||
921 | SI1145_VOLTAGE_CHANNEL(6), | ||
922 | IIO_CHAN_SOFT_TIMESTAMP(7), | ||
923 | SI1145_CURRENT_CHANNEL(0), | ||
924 | SI1145_CURRENT_CHANNEL(1), | ||
925 | SI1145_CURRENT_CHANNEL(2), | ||
926 | }; | ||
927 | |||
928 | static const struct iio_chan_spec si1145_channels[] = { | ||
929 | SI1145_INTENSITY_CHANNEL(0), | ||
930 | SI1145_INTENSITY_IR_CHANNEL(1), | ||
931 | SI1145_PROXIMITY_CHANNEL(2, 0), | ||
932 | SI1145_TEMP_CHANNEL(3), | ||
933 | SI1145_VOLTAGE_CHANNEL(4), | ||
934 | SI1145_UV_CHANNEL(5), | ||
935 | IIO_CHAN_SOFT_TIMESTAMP(6), | ||
936 | SI1145_CURRENT_CHANNEL(0), | ||
937 | }; | ||
938 | |||
939 | static const struct iio_chan_spec si1146_channels[] = { | ||
940 | SI1145_INTENSITY_CHANNEL(0), | ||
941 | SI1145_INTENSITY_IR_CHANNEL(1), | ||
942 | SI1145_TEMP_CHANNEL(2), | ||
943 | SI1145_VOLTAGE_CHANNEL(3), | ||
944 | SI1145_UV_CHANNEL(4), | ||
945 | SI1145_PROXIMITY_CHANNEL(5, 0), | ||
946 | SI1145_PROXIMITY_CHANNEL(6, 1), | ||
947 | IIO_CHAN_SOFT_TIMESTAMP(7), | ||
948 | SI1145_CURRENT_CHANNEL(0), | ||
949 | SI1145_CURRENT_CHANNEL(1), | ||
950 | }; | ||
951 | |||
952 | static const struct iio_chan_spec si1147_channels[] = { | ||
953 | SI1145_INTENSITY_CHANNEL(0), | ||
954 | SI1145_INTENSITY_IR_CHANNEL(1), | ||
955 | SI1145_PROXIMITY_CHANNEL(2, 0), | ||
956 | SI1145_PROXIMITY_CHANNEL(3, 1), | ||
957 | SI1145_PROXIMITY_CHANNEL(4, 2), | ||
958 | SI1145_TEMP_CHANNEL(5), | ||
959 | SI1145_VOLTAGE_CHANNEL(6), | ||
960 | SI1145_UV_CHANNEL(7), | ||
961 | IIO_CHAN_SOFT_TIMESTAMP(8), | ||
962 | SI1145_CURRENT_CHANNEL(0), | ||
963 | SI1145_CURRENT_CHANNEL(1), | ||
964 | SI1145_CURRENT_CHANNEL(2), | ||
965 | }; | ||
966 | |||
967 | static struct attribute *si1132_attributes[] = { | ||
968 | &iio_const_attr_in_intensity_scale_available.dev_attr.attr, | ||
969 | &iio_const_attr_in_intensity_ir_scale_available.dev_attr.attr, | ||
970 | NULL, | ||
971 | }; | ||
972 | |||
973 | static struct attribute *si114x_attributes[] = { | ||
974 | &iio_const_attr_in_intensity_scale_available.dev_attr.attr, | ||
975 | &iio_const_attr_in_intensity_ir_scale_available.dev_attr.attr, | ||
976 | &iio_const_attr_in_proximity_scale_available.dev_attr.attr, | ||
977 | NULL, | ||
978 | }; | ||
979 | |||
980 | static const struct attribute_group si1132_attribute_group = { | ||
981 | .attrs = si1132_attributes, | ||
982 | }; | ||
983 | |||
984 | static const struct attribute_group si114x_attribute_group = { | ||
985 | .attrs = si114x_attributes, | ||
986 | }; | ||
987 | |||
988 | |||
989 | static const struct iio_info si1132_info = { | ||
990 | .read_raw = si1145_read_raw, | ||
991 | .write_raw = si1145_write_raw, | ||
992 | .driver_module = THIS_MODULE, | ||
993 | .attrs = &si1132_attribute_group, | ||
994 | }; | ||
995 | |||
996 | static const struct iio_info si114x_info = { | ||
997 | .read_raw = si1145_read_raw, | ||
998 | .write_raw = si1145_write_raw, | ||
999 | .driver_module = THIS_MODULE, | ||
1000 | .attrs = &si114x_attribute_group, | ||
1001 | }; | ||
1002 | |||
1003 | #define SI1145_PART(id, iio_info, chans, leds, uncompressed_meas_rate) \ | ||
1004 | {id, iio_info, chans, ARRAY_SIZE(chans), leds, uncompressed_meas_rate} | ||
1005 | |||
1006 | static const struct si1145_part_info si1145_part_info[] = { | ||
1007 | [SI1132] = SI1145_PART(0x32, &si1132_info, si1132_channels, 0, true), | ||
1008 | [SI1141] = SI1145_PART(0x41, &si114x_info, si1141_channels, 1, false), | ||
1009 | [SI1142] = SI1145_PART(0x42, &si114x_info, si1142_channels, 2, false), | ||
1010 | [SI1143] = SI1145_PART(0x43, &si114x_info, si1143_channels, 3, false), | ||
1011 | [SI1145] = SI1145_PART(0x45, &si114x_info, si1145_channels, 1, true), | ||
1012 | [SI1146] = SI1145_PART(0x46, &si114x_info, si1146_channels, 2, true), | ||
1013 | [SI1147] = SI1145_PART(0x47, &si114x_info, si1147_channels, 3, true), | ||
1014 | }; | ||
1015 | |||
1016 | static int si1145_initialize(struct si1145_data *data) | ||
1017 | { | ||
1018 | struct i2c_client *client = data->client; | ||
1019 | int ret; | ||
1020 | |||
1021 | ret = i2c_smbus_write_byte_data(client, SI1145_REG_COMMAND, | ||
1022 | SI1145_CMD_RESET); | ||
1023 | if (ret < 0) | ||
1024 | return ret; | ||
1025 | msleep(SI1145_COMMAND_TIMEOUT_MS); | ||
1026 | |||
1027 | /* Hardware key, magic value */ | ||
1028 | ret = i2c_smbus_write_byte_data(client, SI1145_REG_HW_KEY, 0x17); | ||
1029 | if (ret < 0) | ||
1030 | return ret; | ||
1031 | msleep(SI1145_COMMAND_TIMEOUT_MS); | ||
1032 | |||
1033 | /* Turn off autonomous mode */ | ||
1034 | ret = si1145_set_meas_rate(data, 0); | ||
1035 | if (ret < 0) | ||
1036 | return ret; | ||
1037 | |||
1038 | /* Initialize sampling freq to 10 Hz */ | ||
1039 | ret = si1145_store_samp_freq(data, 10); | ||
1040 | if (ret < 0) | ||
1041 | return ret; | ||
1042 | |||
1043 | /* Set LED currents to 45 mA; have 4 bits, see Table 2 in datasheet */ | ||
1044 | switch (data->part_info->num_leds) { | ||
1045 | case 3: | ||
1046 | ret = i2c_smbus_write_byte_data(client, | ||
1047 | SI1145_REG_PS_LED3, | ||
1048 | SI1145_LED_CURRENT_45mA); | ||
1049 | if (ret < 0) | ||
1050 | return ret; | ||
1051 | /* fallthrough */ | ||
1052 | case 2: | ||
1053 | ret = i2c_smbus_write_byte_data(client, | ||
1054 | SI1145_REG_PS_LED21, | ||
1055 | (SI1145_LED_CURRENT_45mA << 4) | | ||
1056 | SI1145_LED_CURRENT_45mA); | ||
1057 | break; | ||
1058 | case 1: | ||
1059 | ret = i2c_smbus_write_byte_data(client, | ||
1060 | SI1145_REG_PS_LED21, | ||
1061 | SI1145_LED_CURRENT_45mA); | ||
1062 | break; | ||
1063 | default: | ||
1064 | ret = 0; | ||
1065 | break; | ||
1066 | } | ||
1067 | if (ret < 0) | ||
1068 | return ret; | ||
1069 | |||
1070 | /* Set normal proximity measurement mode */ | ||
1071 | ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_MISC, | ||
1072 | SI1145_PS_ADC_MODE_NORMAL); | ||
1073 | if (ret < 0) | ||
1074 | return ret; | ||
1075 | |||
1076 | ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_GAIN, 0x01); | ||
1077 | if (ret < 0) | ||
1078 | return ret; | ||
1079 | |||
1080 | /* ADC_COUNTER should be one complement of ADC_GAIN */ | ||
1081 | ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_COUNTER, 0x06 << 4); | ||
1082 | if (ret < 0) | ||
1083 | return ret; | ||
1084 | |||
1085 | /* Set ALS visible measurement mode */ | ||
1086 | ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_MISC, | ||
1087 | SI1145_ADC_MISC_RANGE); | ||
1088 | if (ret < 0) | ||
1089 | return ret; | ||
1090 | |||
1091 | ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_GAIN, 0x03); | ||
1092 | if (ret < 0) | ||
1093 | return ret; | ||
1094 | |||
1095 | ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_COUNTER, | ||
1096 | 0x04 << 4); | ||
1097 | if (ret < 0) | ||
1098 | return ret; | ||
1099 | |||
1100 | /* Set ALS IR measurement mode */ | ||
1101 | ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_MISC, | ||
1102 | SI1145_ADC_MISC_RANGE); | ||
1103 | if (ret < 0) | ||
1104 | return ret; | ||
1105 | |||
1106 | ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_GAIN, 0x01); | ||
1107 | if (ret < 0) | ||
1108 | return ret; | ||
1109 | |||
1110 | ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_COUNTER, | ||
1111 | 0x06 << 4); | ||
1112 | if (ret < 0) | ||
1113 | return ret; | ||
1114 | |||
1115 | /* | ||
1116 | * Initialize UCOEF to default values in datasheet | ||
1117 | * These registers are normally zero on reset | ||
1118 | */ | ||
1119 | if (data->part_info == &si1145_part_info[SI1132] || | ||
1120 | data->part_info == &si1145_part_info[SI1145] || | ||
1121 | data->part_info == &si1145_part_info[SI1146] || | ||
1122 | data->part_info == &si1145_part_info[SI1147]) { | ||
1123 | ret = i2c_smbus_write_byte_data(data->client, | ||
1124 | SI1145_REG_UCOEF1, | ||
1125 | SI1145_UCOEF1_DEFAULT); | ||
1126 | if (ret < 0) | ||
1127 | return ret; | ||
1128 | ret = i2c_smbus_write_byte_data(data->client, | ||
1129 | SI1145_REG_UCOEF2, SI1145_UCOEF2_DEFAULT); | ||
1130 | if (ret < 0) | ||
1131 | return ret; | ||
1132 | ret = i2c_smbus_write_byte_data(data->client, | ||
1133 | SI1145_REG_UCOEF3, SI1145_UCOEF3_DEFAULT); | ||
1134 | if (ret < 0) | ||
1135 | return ret; | ||
1136 | ret = i2c_smbus_write_byte_data(data->client, | ||
1137 | SI1145_REG_UCOEF4, SI1145_UCOEF4_DEFAULT); | ||
1138 | if (ret < 0) | ||
1139 | return ret; | ||
1140 | } | ||
1141 | |||
1142 | return 0; | ||
1143 | } | ||
1144 | |||
1145 | /* | ||
1146 | * Program the channels we want to measure with CMD_PSALS_AUTO. No need for | ||
1147 | * _postdisable as we stop with CMD_PSALS_PAUSE; single measurement (direct) | ||
1148 | * mode reprograms the channels list anyway... | ||
1149 | */ | ||
1150 | static int si1145_buffer_preenable(struct iio_dev *indio_dev) | ||
1151 | { | ||
1152 | struct si1145_data *data = iio_priv(indio_dev); | ||
1153 | int ret; | ||
1154 | |||
1155 | mutex_lock(&data->lock); | ||
1156 | ret = si1145_set_chlist(indio_dev, *indio_dev->active_scan_mask); | ||
1157 | mutex_unlock(&data->lock); | ||
1158 | |||
1159 | return ret; | ||
1160 | } | ||
1161 | |||
1162 | static bool si1145_validate_scan_mask(struct iio_dev *indio_dev, | ||
1163 | const unsigned long *scan_mask) | ||
1164 | { | ||
1165 | struct si1145_data *data = iio_priv(indio_dev); | ||
1166 | unsigned int count = 0; | ||
1167 | int i; | ||
1168 | |||
1169 | /* Check that at most one AUX channel is enabled */ | ||
1170 | for_each_set_bit(i, scan_mask, data->part_info->num_channels) { | ||
1171 | if (indio_dev->channels[i].address == SI1145_REG_AUX_DATA) | ||
1172 | count++; | ||
1173 | } | ||
1174 | |||
1175 | return count <= 1; | ||
1176 | } | ||
1177 | |||
1178 | static const struct iio_buffer_setup_ops si1145_buffer_setup_ops = { | ||
1179 | .preenable = si1145_buffer_preenable, | ||
1180 | .postenable = iio_triggered_buffer_postenable, | ||
1181 | .predisable = iio_triggered_buffer_predisable, | ||
1182 | .validate_scan_mask = si1145_validate_scan_mask, | ||
1183 | }; | ||
1184 | |||
1185 | /** | ||
1186 | * si1145_trigger_set_state() - Set trigger state | ||
1187 | * | ||
1188 | * When not using triggers interrupts are disabled and measurement rate is | ||
1189 | * set to zero in order to minimize power consumption. | ||
1190 | */ | ||
1191 | static int si1145_trigger_set_state(struct iio_trigger *trig, bool state) | ||
1192 | { | ||
1193 | struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); | ||
1194 | struct si1145_data *data = iio_priv(indio_dev); | ||
1195 | int err = 0, ret; | ||
1196 | |||
1197 | mutex_lock(&data->lock); | ||
1198 | |||
1199 | if (state) { | ||
1200 | data->autonomous = true; | ||
1201 | err = i2c_smbus_write_byte_data(data->client, | ||
1202 | SI1145_REG_INT_CFG, SI1145_INT_CFG_OE); | ||
1203 | if (err < 0) | ||
1204 | goto disable; | ||
1205 | err = i2c_smbus_write_byte_data(data->client, | ||
1206 | SI1145_REG_IRQ_ENABLE, SI1145_MASK_ALL_IE); | ||
1207 | if (err < 0) | ||
1208 | goto disable; | ||
1209 | err = si1145_set_meas_rate(data, data->meas_rate); | ||
1210 | if (err < 0) | ||
1211 | goto disable; | ||
1212 | err = si1145_command(data, SI1145_CMD_PSALS_AUTO); | ||
1213 | if (err < 0) | ||
1214 | goto disable; | ||
1215 | } else { | ||
1216 | disable: | ||
1217 | /* Disable as much as possible skipping errors */ | ||
1218 | ret = si1145_command(data, SI1145_CMD_PSALS_PAUSE); | ||
1219 | if (ret < 0 && !err) | ||
1220 | err = ret; | ||
1221 | ret = si1145_set_meas_rate(data, 0); | ||
1222 | if (ret < 0 && !err) | ||
1223 | err = ret; | ||
1224 | ret = i2c_smbus_write_byte_data(data->client, | ||
1225 | SI1145_REG_IRQ_ENABLE, 0); | ||
1226 | if (ret < 0 && !err) | ||
1227 | err = ret; | ||
1228 | ret = i2c_smbus_write_byte_data(data->client, | ||
1229 | SI1145_REG_INT_CFG, 0); | ||
1230 | if (ret < 0 && !err) | ||
1231 | err = ret; | ||
1232 | data->autonomous = false; | ||
1233 | } | ||
1234 | |||
1235 | mutex_unlock(&data->lock); | ||
1236 | return err; | ||
1237 | } | ||
1238 | |||
1239 | static const struct iio_trigger_ops si1145_trigger_ops = { | ||
1240 | .owner = THIS_MODULE, | ||
1241 | .set_trigger_state = si1145_trigger_set_state, | ||
1242 | }; | ||
1243 | |||
1244 | static int si1145_probe_trigger(struct iio_dev *indio_dev) | ||
1245 | { | ||
1246 | struct si1145_data *data = iio_priv(indio_dev); | ||
1247 | struct i2c_client *client = data->client; | ||
1248 | struct iio_trigger *trig; | ||
1249 | int ret; | ||
1250 | |||
1251 | trig = devm_iio_trigger_alloc(&client->dev, | ||
1252 | "%s-dev%d", indio_dev->name, indio_dev->id); | ||
1253 | if (!trig) | ||
1254 | return -ENOMEM; | ||
1255 | |||
1256 | trig->dev.parent = &client->dev; | ||
1257 | trig->ops = &si1145_trigger_ops; | ||
1258 | iio_trigger_set_drvdata(trig, indio_dev); | ||
1259 | |||
1260 | ret = devm_request_irq(&client->dev, client->irq, | ||
1261 | iio_trigger_generic_data_rdy_poll, | ||
1262 | IRQF_TRIGGER_FALLING, | ||
1263 | "si1145_irq", | ||
1264 | trig); | ||
1265 | if (ret < 0) { | ||
1266 | dev_err(&client->dev, "irq request failed\n"); | ||
1267 | return ret; | ||
1268 | } | ||
1269 | |||
1270 | ret = iio_trigger_register(trig); | ||
1271 | if (ret) | ||
1272 | return ret; | ||
1273 | |||
1274 | data->trig = trig; | ||
1275 | indio_dev->trig = iio_trigger_get(data->trig); | ||
1276 | |||
1277 | return 0; | ||
1278 | } | ||
1279 | |||
1280 | static void si1145_remove_trigger(struct iio_dev *indio_dev) | ||
1281 | { | ||
1282 | struct si1145_data *data = iio_priv(indio_dev); | ||
1283 | |||
1284 | if (data->trig) { | ||
1285 | iio_trigger_unregister(data->trig); | ||
1286 | data->trig = NULL; | ||
1287 | } | ||
1288 | } | ||
1289 | |||
1290 | static int si1145_probe(struct i2c_client *client, | ||
1291 | const struct i2c_device_id *id) | ||
1292 | { | ||
1293 | struct si1145_data *data; | ||
1294 | struct iio_dev *indio_dev; | ||
1295 | u8 part_id, rev_id, seq_id; | ||
1296 | int ret; | ||
1297 | |||
1298 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | ||
1299 | if (!indio_dev) | ||
1300 | return -ENOMEM; | ||
1301 | |||
1302 | data = iio_priv(indio_dev); | ||
1303 | i2c_set_clientdata(client, indio_dev); | ||
1304 | data->client = client; | ||
1305 | data->part_info = &si1145_part_info[id->driver_data]; | ||
1306 | |||
1307 | part_id = ret = i2c_smbus_read_byte_data(data->client, | ||
1308 | SI1145_REG_PART_ID); | ||
1309 | if (ret < 0) | ||
1310 | return ret; | ||
1311 | rev_id = ret = i2c_smbus_read_byte_data(data->client, | ||
1312 | SI1145_REG_REV_ID); | ||
1313 | if (ret < 0) | ||
1314 | return ret; | ||
1315 | seq_id = ret = i2c_smbus_read_byte_data(data->client, | ||
1316 | SI1145_REG_SEQ_ID); | ||
1317 | if (ret < 0) | ||
1318 | return ret; | ||
1319 | dev_info(&client->dev, "device ID part %#02hhx rev %#02hhx seq %#02hhx\n", | ||
1320 | part_id, rev_id, seq_id); | ||
1321 | if (part_id != data->part_info->part) { | ||
1322 | dev_err(&client->dev, "part ID mismatch got %#02hhx, expected %#02x\n", | ||
1323 | part_id, data->part_info->part); | ||
1324 | return -ENODEV; | ||
1325 | } | ||
1326 | |||
1327 | indio_dev->dev.parent = &client->dev; | ||
1328 | indio_dev->name = id->name; | ||
1329 | indio_dev->channels = data->part_info->channels; | ||
1330 | indio_dev->num_channels = data->part_info->num_channels; | ||
1331 | indio_dev->info = data->part_info->iio_info; | ||
1332 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
1333 | |||
1334 | mutex_init(&data->lock); | ||
1335 | mutex_init(&data->cmdlock); | ||
1336 | |||
1337 | ret = si1145_initialize(data); | ||
1338 | if (ret < 0) | ||
1339 | return ret; | ||
1340 | |||
1341 | ret = iio_triggered_buffer_setup(indio_dev, NULL, | ||
1342 | si1145_trigger_handler, &si1145_buffer_setup_ops); | ||
1343 | if (ret < 0) | ||
1344 | return ret; | ||
1345 | |||
1346 | if (client->irq) { | ||
1347 | ret = si1145_probe_trigger(indio_dev); | ||
1348 | if (ret < 0) | ||
1349 | goto error_free_buffer; | ||
1350 | } else { | ||
1351 | dev_info(&client->dev, "no irq, using polling\n"); | ||
1352 | } | ||
1353 | |||
1354 | ret = iio_device_register(indio_dev); | ||
1355 | if (ret < 0) | ||
1356 | goto error_free_trigger; | ||
1357 | |||
1358 | return 0; | ||
1359 | |||
1360 | error_free_trigger: | ||
1361 | si1145_remove_trigger(indio_dev); | ||
1362 | error_free_buffer: | ||
1363 | iio_triggered_buffer_cleanup(indio_dev); | ||
1364 | |||
1365 | return ret; | ||
1366 | } | ||
1367 | |||
1368 | static const struct i2c_device_id si1145_ids[] = { | ||
1369 | { "si1132", SI1132 }, | ||
1370 | { "si1141", SI1141 }, | ||
1371 | { "si1142", SI1142 }, | ||
1372 | { "si1143", SI1143 }, | ||
1373 | { "si1145", SI1145 }, | ||
1374 | { "si1146", SI1146 }, | ||
1375 | { "si1147", SI1147 }, | ||
1376 | { } | ||
1377 | }; | ||
1378 | MODULE_DEVICE_TABLE(i2c, si1145_ids); | ||
1379 | |||
1380 | static int si1145_remove(struct i2c_client *client) | ||
1381 | { | ||
1382 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | ||
1383 | |||
1384 | iio_device_unregister(indio_dev); | ||
1385 | si1145_remove_trigger(indio_dev); | ||
1386 | iio_triggered_buffer_cleanup(indio_dev); | ||
1387 | |||
1388 | return 0; | ||
1389 | } | ||
1390 | |||
1391 | static struct i2c_driver si1145_driver = { | ||
1392 | .driver = { | ||
1393 | .name = "si1145", | ||
1394 | }, | ||
1395 | .probe = si1145_probe, | ||
1396 | .remove = si1145_remove, | ||
1397 | .id_table = si1145_ids, | ||
1398 | }; | ||
1399 | |||
1400 | module_i2c_driver(si1145_driver); | ||
1401 | |||
1402 | MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>"); | ||
1403 | MODULE_DESCRIPTION("Silabs SI1132 and SI1141/2/3/5/6/7 proximity, ambient light and UV index sensor driver"); | ||
1404 | MODULE_LICENSE("GPL"); | ||