Commit | Line | Data |
---|---|---|
be9e6229 TB |
1 | /** |
2 | * Sensortek STK3310/STK3311 Ambient Light and Proximity Sensor | |
3 | * | |
4 | * Copyright (c) 2015, Intel Corporation. | |
5 | * | |
6 | * This file is subject to the terms and conditions of version 2 of | |
7 | * the GNU General Public License. See the file COPYING in the main | |
8 | * directory of this archive for more details. | |
9 | * | |
10 | * IIO driver for STK3310/STK3311. 7-bit I2C address: 0x48. | |
11 | */ | |
12 | ||
13 | #include <linux/acpi.h> | |
14 | #include <linux/i2c.h> | |
3dd477ac | 15 | #include <linux/interrupt.h> |
be9e6229 TB |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> | |
18 | #include <linux/regmap.h> | |
3dd477ac TB |
19 | #include <linux/gpio/consumer.h> |
20 | #include <linux/iio/events.h> | |
be9e6229 TB |
21 | #include <linux/iio/iio.h> |
22 | #include <linux/iio/sysfs.h> | |
23 | ||
24 | #define STK3310_REG_STATE 0x00 | |
25 | #define STK3310_REG_PSCTRL 0x01 | |
26 | #define STK3310_REG_ALSCTRL 0x02 | |
3dd477ac TB |
27 | #define STK3310_REG_INT 0x04 |
28 | #define STK3310_REG_THDH_PS 0x06 | |
29 | #define STK3310_REG_THDL_PS 0x08 | |
30 | #define STK3310_REG_FLAG 0x10 | |
be9e6229 TB |
31 | #define STK3310_REG_PS_DATA_MSB 0x11 |
32 | #define STK3310_REG_PS_DATA_LSB 0x12 | |
33 | #define STK3310_REG_ALS_DATA_MSB 0x13 | |
34 | #define STK3310_REG_ALS_DATA_LSB 0x14 | |
35 | #define STK3310_REG_ID 0x3E | |
36 | #define STK3310_MAX_REG 0x80 | |
37 | ||
952c3aa3 HK |
38 | #define STK3310_STATE_EN_PS BIT(0) |
39 | #define STK3310_STATE_EN_ALS BIT(1) | |
be9e6229 TB |
40 | #define STK3310_STATE_STANDBY 0x00 |
41 | ||
42 | #define STK3310_CHIP_ID_VAL 0x13 | |
43 | #define STK3311_CHIP_ID_VAL 0x1D | |
3dd477ac | 44 | #define STK3310_PSINT_EN 0x01 |
be9e6229 TB |
45 | #define STK3310_PS_MAX_VAL 0xFFFF |
46 | ||
47 | #define STK3310_DRIVER_NAME "stk3310" | |
48 | #define STK3310_REGMAP_NAME "stk3310_regmap" | |
3dd477ac | 49 | #define STK3310_EVENT "stk3310_event" |
be9e6229 TB |
50 | |
51 | #define STK3310_SCALE_AVAILABLE "6.4 1.6 0.4 0.1" | |
52 | ||
53 | #define STK3310_IT_AVAILABLE \ | |
54 | "0.000185 0.000370 0.000741 0.001480 0.002960 0.005920 0.011840 " \ | |
55 | "0.023680 0.047360 0.094720 0.189440 0.378880 0.757760 1.515520 " \ | |
56 | "3.031040 6.062080" | |
57 | ||
58 | #define STK3310_REGFIELD(name) \ | |
59 | do { \ | |
60 | data->reg_##name = \ | |
61 | devm_regmap_field_alloc(&client->dev, regmap, \ | |
62 | stk3310_reg_field_##name); \ | |
63 | if (IS_ERR(data->reg_##name)) { \ | |
64 | dev_err(&client->dev, "reg field alloc failed.\n"); \ | |
65 | return PTR_ERR(data->reg_##name); \ | |
66 | } \ | |
67 | } while (0) | |
68 | ||
69 | static const struct reg_field stk3310_reg_field_state = | |
70 | REG_FIELD(STK3310_REG_STATE, 0, 2); | |
71 | static const struct reg_field stk3310_reg_field_als_gain = | |
72 | REG_FIELD(STK3310_REG_ALSCTRL, 4, 5); | |
73 | static const struct reg_field stk3310_reg_field_ps_gain = | |
74 | REG_FIELD(STK3310_REG_PSCTRL, 4, 5); | |
75 | static const struct reg_field stk3310_reg_field_als_it = | |
76 | REG_FIELD(STK3310_REG_ALSCTRL, 0, 3); | |
77 | static const struct reg_field stk3310_reg_field_ps_it = | |
78 | REG_FIELD(STK3310_REG_PSCTRL, 0, 3); | |
3dd477ac TB |
79 | static const struct reg_field stk3310_reg_field_int_ps = |
80 | REG_FIELD(STK3310_REG_INT, 0, 2); | |
81 | static const struct reg_field stk3310_reg_field_flag_psint = | |
82 | REG_FIELD(STK3310_REG_FLAG, 4, 4); | |
83 | static const struct reg_field stk3310_reg_field_flag_nf = | |
84 | REG_FIELD(STK3310_REG_FLAG, 0, 0); | |
0f16fc8b TB |
85 | |
86 | /* Estimate maximum proximity values with regard to measurement scale. */ | |
be9e6229 | 87 | static const int stk3310_ps_max[4] = { |
0f16fc8b TB |
88 | STK3310_PS_MAX_VAL / 640, |
89 | STK3310_PS_MAX_VAL / 160, | |
90 | STK3310_PS_MAX_VAL / 40, | |
91 | STK3310_PS_MAX_VAL / 10 | |
be9e6229 TB |
92 | }; |
93 | ||
94 | static const int stk3310_scale_table[][2] = { | |
95 | {6, 400000}, {1, 600000}, {0, 400000}, {0, 100000} | |
96 | }; | |
97 | ||
98 | /* Integration time in seconds, microseconds */ | |
99 | static const int stk3310_it_table[][2] = { | |
100 | {0, 185}, {0, 370}, {0, 741}, {0, 1480}, | |
101 | {0, 2960}, {0, 5920}, {0, 11840}, {0, 23680}, | |
102 | {0, 47360}, {0, 94720}, {0, 189440}, {0, 378880}, | |
103 | {0, 757760}, {1, 515520}, {3, 31040}, {6, 62080}, | |
104 | }; | |
105 | ||
106 | struct stk3310_data { | |
107 | struct i2c_client *client; | |
108 | struct mutex lock; | |
109 | bool als_enabled; | |
110 | bool ps_enabled; | |
3dd477ac | 111 | u64 timestamp; |
be9e6229 TB |
112 | struct regmap *regmap; |
113 | struct regmap_field *reg_state; | |
114 | struct regmap_field *reg_als_gain; | |
115 | struct regmap_field *reg_ps_gain; | |
116 | struct regmap_field *reg_als_it; | |
117 | struct regmap_field *reg_ps_it; | |
3dd477ac TB |
118 | struct regmap_field *reg_int_ps; |
119 | struct regmap_field *reg_flag_psint; | |
120 | struct regmap_field *reg_flag_nf; | |
121 | }; | |
122 | ||
123 | static const struct iio_event_spec stk3310_events[] = { | |
124 | /* Proximity event */ | |
125 | { | |
126 | .type = IIO_EV_TYPE_THRESH, | |
0f16fc8b | 127 | .dir = IIO_EV_DIR_RISING, |
3dd477ac TB |
128 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | |
129 | BIT(IIO_EV_INFO_ENABLE), | |
130 | }, | |
131 | /* Out-of-proximity event */ | |
132 | { | |
133 | .type = IIO_EV_TYPE_THRESH, | |
0f16fc8b | 134 | .dir = IIO_EV_DIR_FALLING, |
3dd477ac TB |
135 | .mask_separate = BIT(IIO_EV_INFO_VALUE) | |
136 | BIT(IIO_EV_INFO_ENABLE), | |
137 | }, | |
be9e6229 TB |
138 | }; |
139 | ||
140 | static const struct iio_chan_spec stk3310_channels[] = { | |
141 | { | |
142 | .type = IIO_LIGHT, | |
143 | .info_mask_separate = | |
144 | BIT(IIO_CHAN_INFO_RAW) | | |
145 | BIT(IIO_CHAN_INFO_SCALE) | | |
146 | BIT(IIO_CHAN_INFO_INT_TIME), | |
147 | }, | |
148 | { | |
149 | .type = IIO_PROXIMITY, | |
150 | .info_mask_separate = | |
151 | BIT(IIO_CHAN_INFO_RAW) | | |
152 | BIT(IIO_CHAN_INFO_SCALE) | | |
153 | BIT(IIO_CHAN_INFO_INT_TIME), | |
3dd477ac TB |
154 | .event_spec = stk3310_events, |
155 | .num_event_specs = ARRAY_SIZE(stk3310_events), | |
be9e6229 TB |
156 | } |
157 | }; | |
158 | ||
159 | static IIO_CONST_ATTR(in_illuminance_scale_available, STK3310_SCALE_AVAILABLE); | |
160 | ||
161 | static IIO_CONST_ATTR(in_proximity_scale_available, STK3310_SCALE_AVAILABLE); | |
162 | ||
163 | static IIO_CONST_ATTR(in_illuminance_integration_time_available, | |
164 | STK3310_IT_AVAILABLE); | |
165 | ||
166 | static IIO_CONST_ATTR(in_proximity_integration_time_available, | |
167 | STK3310_IT_AVAILABLE); | |
168 | ||
169 | static struct attribute *stk3310_attributes[] = { | |
170 | &iio_const_attr_in_illuminance_scale_available.dev_attr.attr, | |
171 | &iio_const_attr_in_proximity_scale_available.dev_attr.attr, | |
172 | &iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr, | |
173 | &iio_const_attr_in_proximity_integration_time_available.dev_attr.attr, | |
174 | NULL, | |
175 | }; | |
176 | ||
177 | static const struct attribute_group stk3310_attribute_group = { | |
178 | .attrs = stk3310_attributes | |
179 | }; | |
180 | ||
181 | static int stk3310_get_index(const int table[][2], int table_size, | |
182 | int val, int val2) | |
183 | { | |
184 | int i; | |
185 | ||
186 | for (i = 0; i < table_size; i++) { | |
187 | if (val == table[i][0] && val2 == table[i][1]) | |
188 | return i; | |
189 | } | |
190 | ||
191 | return -EINVAL; | |
192 | } | |
193 | ||
3dd477ac TB |
194 | static int stk3310_read_event(struct iio_dev *indio_dev, |
195 | const struct iio_chan_spec *chan, | |
196 | enum iio_event_type type, | |
197 | enum iio_event_direction dir, | |
198 | enum iio_event_info info, | |
199 | int *val, int *val2) | |
200 | { | |
201 | u8 reg; | |
423ad0c4 | 202 | __be16 buf; |
3dd477ac | 203 | int ret; |
3dd477ac TB |
204 | struct stk3310_data *data = iio_priv(indio_dev); |
205 | ||
206 | if (info != IIO_EV_INFO_VALUE) | |
207 | return -EINVAL; | |
208 | ||
0f16fc8b | 209 | /* Only proximity interrupts are implemented at the moment. */ |
3dd477ac | 210 | if (dir == IIO_EV_DIR_RISING) |
3dd477ac | 211 | reg = STK3310_REG_THDH_PS; |
0f16fc8b TB |
212 | else if (dir == IIO_EV_DIR_FALLING) |
213 | reg = STK3310_REG_THDL_PS; | |
3dd477ac TB |
214 | else |
215 | return -EINVAL; | |
216 | ||
217 | mutex_lock(&data->lock); | |
218 | ret = regmap_bulk_read(data->regmap, reg, &buf, 2); | |
219 | mutex_unlock(&data->lock); | |
220 | if (ret < 0) { | |
221 | dev_err(&data->client->dev, "register read failed\n"); | |
222 | return ret; | |
223 | } | |
423ad0c4 | 224 | *val = be16_to_cpu(buf); |
3dd477ac TB |
225 | |
226 | return IIO_VAL_INT; | |
227 | } | |
228 | ||
229 | static int stk3310_write_event(struct iio_dev *indio_dev, | |
230 | const struct iio_chan_spec *chan, | |
231 | enum iio_event_type type, | |
232 | enum iio_event_direction dir, | |
233 | enum iio_event_info info, | |
234 | int val, int val2) | |
235 | { | |
236 | u8 reg; | |
423ad0c4 | 237 | __be16 buf; |
3dd477ac TB |
238 | int ret; |
239 | unsigned int index; | |
240 | struct stk3310_data *data = iio_priv(indio_dev); | |
241 | struct i2c_client *client = data->client; | |
242 | ||
7c7a9eea HK |
243 | ret = regmap_field_read(data->reg_ps_gain, &index); |
244 | if (ret < 0) | |
245 | return ret; | |
246 | ||
247 | if (val < 0 || val > stk3310_ps_max[index]) | |
3dd477ac TB |
248 | return -EINVAL; |
249 | ||
250 | if (dir == IIO_EV_DIR_RISING) | |
3dd477ac | 251 | reg = STK3310_REG_THDH_PS; |
0f16fc8b TB |
252 | else if (dir == IIO_EV_DIR_FALLING) |
253 | reg = STK3310_REG_THDL_PS; | |
3dd477ac TB |
254 | else |
255 | return -EINVAL; | |
256 | ||
423ad0c4 | 257 | buf = cpu_to_be16(val); |
3dd477ac TB |
258 | ret = regmap_bulk_write(data->regmap, reg, &buf, 2); |
259 | if (ret < 0) | |
260 | dev_err(&client->dev, "failed to set PS threshold!\n"); | |
261 | ||
262 | return ret; | |
263 | } | |
264 | ||
265 | static int stk3310_read_event_config(struct iio_dev *indio_dev, | |
266 | const struct iio_chan_spec *chan, | |
267 | enum iio_event_type type, | |
268 | enum iio_event_direction dir) | |
269 | { | |
270 | unsigned int event_val; | |
7c7a9eea | 271 | int ret; |
3dd477ac TB |
272 | struct stk3310_data *data = iio_priv(indio_dev); |
273 | ||
7c7a9eea HK |
274 | ret = regmap_field_read(data->reg_int_ps, &event_val); |
275 | if (ret < 0) | |
276 | return ret; | |
3dd477ac TB |
277 | |
278 | return event_val; | |
279 | } | |
280 | ||
281 | static int stk3310_write_event_config(struct iio_dev *indio_dev, | |
282 | const struct iio_chan_spec *chan, | |
283 | enum iio_event_type type, | |
284 | enum iio_event_direction dir, | |
285 | int state) | |
286 | { | |
287 | int ret; | |
288 | struct stk3310_data *data = iio_priv(indio_dev); | |
289 | struct i2c_client *client = data->client; | |
290 | ||
291 | if (state < 0 || state > 7) | |
292 | return -EINVAL; | |
293 | ||
294 | /* Set INT_PS value */ | |
295 | mutex_lock(&data->lock); | |
296 | ret = regmap_field_write(data->reg_int_ps, state); | |
297 | if (ret < 0) | |
298 | dev_err(&client->dev, "failed to set interrupt mode\n"); | |
299 | mutex_unlock(&data->lock); | |
300 | ||
301 | return ret; | |
302 | } | |
303 | ||
be9e6229 TB |
304 | static int stk3310_read_raw(struct iio_dev *indio_dev, |
305 | struct iio_chan_spec const *chan, | |
306 | int *val, int *val2, long mask) | |
307 | { | |
308 | u8 reg; | |
423ad0c4 | 309 | __be16 buf; |
be9e6229 TB |
310 | int ret; |
311 | unsigned int index; | |
312 | struct stk3310_data *data = iio_priv(indio_dev); | |
313 | struct i2c_client *client = data->client; | |
314 | ||
7c7a9eea HK |
315 | if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY) |
316 | return -EINVAL; | |
317 | ||
be9e6229 TB |
318 | switch (mask) { |
319 | case IIO_CHAN_INFO_RAW: | |
320 | if (chan->type == IIO_LIGHT) | |
321 | reg = STK3310_REG_ALS_DATA_MSB; | |
be9e6229 | 322 | else |
7c7a9eea HK |
323 | reg = STK3310_REG_PS_DATA_MSB; |
324 | ||
be9e6229 TB |
325 | mutex_lock(&data->lock); |
326 | ret = regmap_bulk_read(data->regmap, reg, &buf, 2); | |
327 | if (ret < 0) { | |
328 | dev_err(&client->dev, "register read failed\n"); | |
329 | mutex_unlock(&data->lock); | |
330 | return ret; | |
331 | } | |
423ad0c4 | 332 | *val = be16_to_cpu(buf); |
be9e6229 TB |
333 | mutex_unlock(&data->lock); |
334 | return IIO_VAL_INT; | |
335 | case IIO_CHAN_INFO_INT_TIME: | |
336 | if (chan->type == IIO_LIGHT) | |
7c7a9eea | 337 | ret = regmap_field_read(data->reg_als_it, &index); |
be9e6229 | 338 | else |
7c7a9eea HK |
339 | ret = regmap_field_read(data->reg_ps_it, &index); |
340 | if (ret < 0) | |
341 | return ret; | |
342 | ||
be9e6229 TB |
343 | *val = stk3310_it_table[index][0]; |
344 | *val2 = stk3310_it_table[index][1]; | |
345 | return IIO_VAL_INT_PLUS_MICRO; | |
346 | case IIO_CHAN_INFO_SCALE: | |
347 | if (chan->type == IIO_LIGHT) | |
7c7a9eea | 348 | ret = regmap_field_read(data->reg_als_gain, &index); |
be9e6229 | 349 | else |
7c7a9eea HK |
350 | ret = regmap_field_read(data->reg_ps_gain, &index); |
351 | if (ret < 0) | |
352 | return ret; | |
353 | ||
be9e6229 TB |
354 | *val = stk3310_scale_table[index][0]; |
355 | *val2 = stk3310_scale_table[index][1]; | |
356 | return IIO_VAL_INT_PLUS_MICRO; | |
357 | } | |
358 | ||
359 | return -EINVAL; | |
360 | } | |
361 | ||
362 | static int stk3310_write_raw(struct iio_dev *indio_dev, | |
363 | struct iio_chan_spec const *chan, | |
364 | int val, int val2, long mask) | |
365 | { | |
366 | int ret; | |
ed6e75c7 | 367 | int index; |
be9e6229 TB |
368 | struct stk3310_data *data = iio_priv(indio_dev); |
369 | ||
7c7a9eea HK |
370 | if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY) |
371 | return -EINVAL; | |
372 | ||
be9e6229 TB |
373 | switch (mask) { |
374 | case IIO_CHAN_INFO_INT_TIME: | |
375 | index = stk3310_get_index(stk3310_it_table, | |
376 | ARRAY_SIZE(stk3310_it_table), | |
377 | val, val2); | |
378 | if (index < 0) | |
379 | return -EINVAL; | |
380 | mutex_lock(&data->lock); | |
381 | if (chan->type == IIO_LIGHT) | |
382 | ret = regmap_field_write(data->reg_als_it, index); | |
383 | else | |
384 | ret = regmap_field_write(data->reg_ps_it, index); | |
385 | if (ret < 0) | |
386 | dev_err(&data->client->dev, | |
5b958f11 | 387 | "sensor configuration failed\n"); |
be9e6229 TB |
388 | mutex_unlock(&data->lock); |
389 | return ret; | |
390 | ||
391 | case IIO_CHAN_INFO_SCALE: | |
392 | index = stk3310_get_index(stk3310_scale_table, | |
393 | ARRAY_SIZE(stk3310_scale_table), | |
394 | val, val2); | |
395 | if (index < 0) | |
396 | return -EINVAL; | |
397 | mutex_lock(&data->lock); | |
398 | if (chan->type == IIO_LIGHT) | |
399 | ret = regmap_field_write(data->reg_als_gain, index); | |
400 | else | |
401 | ret = regmap_field_write(data->reg_ps_gain, index); | |
402 | if (ret < 0) | |
403 | dev_err(&data->client->dev, | |
5b958f11 | 404 | "sensor configuration failed\n"); |
be9e6229 TB |
405 | mutex_unlock(&data->lock); |
406 | return ret; | |
407 | } | |
408 | ||
409 | return -EINVAL; | |
410 | } | |
411 | ||
412 | static const struct iio_info stk3310_info = { | |
413 | .driver_module = THIS_MODULE, | |
414 | .read_raw = stk3310_read_raw, | |
415 | .write_raw = stk3310_write_raw, | |
416 | .attrs = &stk3310_attribute_group, | |
3dd477ac TB |
417 | .read_event_value = stk3310_read_event, |
418 | .write_event_value = stk3310_write_event, | |
419 | .read_event_config = stk3310_read_event_config, | |
420 | .write_event_config = stk3310_write_event_config, | |
be9e6229 TB |
421 | }; |
422 | ||
423 | static int stk3310_set_state(struct stk3310_data *data, u8 state) | |
424 | { | |
425 | int ret; | |
426 | struct i2c_client *client = data->client; | |
427 | ||
428 | /* 3-bit state; 0b100 is not supported. */ | |
429 | if (state > 7 || state == 4) | |
430 | return -EINVAL; | |
431 | ||
432 | mutex_lock(&data->lock); | |
433 | ret = regmap_field_write(data->reg_state, state); | |
434 | if (ret < 0) { | |
435 | dev_err(&client->dev, "failed to change sensor state\n"); | |
436 | } else if (state != STK3310_STATE_STANDBY) { | |
437 | /* Don't reset the 'enabled' flags if we're going in standby */ | |
952c3aa3 HK |
438 | data->ps_enabled = !!(state & STK3310_STATE_EN_PS); |
439 | data->als_enabled = !!(state & STK3310_STATE_EN_ALS); | |
be9e6229 TB |
440 | } |
441 | mutex_unlock(&data->lock); | |
442 | ||
443 | return ret; | |
444 | } | |
445 | ||
446 | static int stk3310_init(struct iio_dev *indio_dev) | |
447 | { | |
448 | int ret; | |
449 | int chipid; | |
450 | u8 state; | |
451 | struct stk3310_data *data = iio_priv(indio_dev); | |
452 | struct i2c_client *client = data->client; | |
453 | ||
7c7a9eea HK |
454 | ret = regmap_read(data->regmap, STK3310_REG_ID, &chipid); |
455 | if (ret < 0) | |
456 | return ret; | |
457 | ||
be9e6229 TB |
458 | if (chipid != STK3310_CHIP_ID_VAL && |
459 | chipid != STK3311_CHIP_ID_VAL) { | |
460 | dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid); | |
461 | return -ENODEV; | |
462 | } | |
463 | ||
464 | state = STK3310_STATE_EN_ALS | STK3310_STATE_EN_PS; | |
465 | ret = stk3310_set_state(data, state); | |
3dd477ac | 466 | if (ret < 0) { |
be9e6229 | 467 | dev_err(&client->dev, "failed to enable sensor"); |
3dd477ac TB |
468 | return ret; |
469 | } | |
470 | ||
471 | /* Enable PS interrupts */ | |
472 | ret = regmap_field_write(data->reg_int_ps, STK3310_PSINT_EN); | |
473 | if (ret < 0) | |
474 | dev_err(&client->dev, "failed to enable interrupts!\n"); | |
475 | ||
476 | return ret; | |
477 | } | |
478 | ||
be9e6229 TB |
479 | static bool stk3310_is_volatile_reg(struct device *dev, unsigned int reg) |
480 | { | |
481 | switch (reg) { | |
482 | case STK3310_REG_ALS_DATA_MSB: | |
483 | case STK3310_REG_ALS_DATA_LSB: | |
484 | case STK3310_REG_PS_DATA_LSB: | |
485 | case STK3310_REG_PS_DATA_MSB: | |
3dd477ac | 486 | case STK3310_REG_FLAG: |
be9e6229 TB |
487 | return true; |
488 | default: | |
489 | return false; | |
490 | } | |
491 | } | |
492 | ||
493 | static struct regmap_config stk3310_regmap_config = { | |
494 | .name = STK3310_REGMAP_NAME, | |
495 | .reg_bits = 8, | |
496 | .val_bits = 8, | |
497 | .max_register = STK3310_MAX_REG, | |
498 | .cache_type = REGCACHE_RBTREE, | |
499 | .volatile_reg = stk3310_is_volatile_reg, | |
500 | }; | |
501 | ||
502 | static int stk3310_regmap_init(struct stk3310_data *data) | |
503 | { | |
504 | struct regmap *regmap; | |
505 | struct i2c_client *client; | |
506 | ||
507 | client = data->client; | |
508 | regmap = devm_regmap_init_i2c(client, &stk3310_regmap_config); | |
509 | if (IS_ERR(regmap)) { | |
510 | dev_err(&client->dev, "regmap initialization failed.\n"); | |
511 | return PTR_ERR(regmap); | |
512 | } | |
513 | data->regmap = regmap; | |
514 | ||
515 | STK3310_REGFIELD(state); | |
516 | STK3310_REGFIELD(als_gain); | |
517 | STK3310_REGFIELD(ps_gain); | |
518 | STK3310_REGFIELD(als_it); | |
519 | STK3310_REGFIELD(ps_it); | |
3dd477ac TB |
520 | STK3310_REGFIELD(int_ps); |
521 | STK3310_REGFIELD(flag_psint); | |
522 | STK3310_REGFIELD(flag_nf); | |
be9e6229 TB |
523 | |
524 | return 0; | |
525 | } | |
526 | ||
3dd477ac TB |
527 | static irqreturn_t stk3310_irq_handler(int irq, void *private) |
528 | { | |
529 | struct iio_dev *indio_dev = private; | |
530 | struct stk3310_data *data = iio_priv(indio_dev); | |
531 | ||
532 | data->timestamp = iio_get_time_ns(); | |
533 | ||
534 | return IRQ_WAKE_THREAD; | |
535 | } | |
536 | ||
537 | static irqreturn_t stk3310_irq_event_handler(int irq, void *private) | |
538 | { | |
539 | int ret; | |
540 | unsigned int dir; | |
541 | u64 event; | |
542 | ||
543 | struct iio_dev *indio_dev = private; | |
544 | struct stk3310_data *data = iio_priv(indio_dev); | |
545 | ||
546 | /* Read FLAG_NF to figure out what threshold has been met. */ | |
547 | mutex_lock(&data->lock); | |
548 | ret = regmap_field_read(data->reg_flag_nf, &dir); | |
549 | if (ret < 0) { | |
550 | dev_err(&data->client->dev, "register read failed\n"); | |
551 | mutex_unlock(&data->lock); | |
552 | return ret; | |
553 | } | |
554 | event = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1, | |
555 | IIO_EV_TYPE_THRESH, | |
0f16fc8b TB |
556 | (dir ? IIO_EV_DIR_FALLING : |
557 | IIO_EV_DIR_RISING)); | |
3dd477ac TB |
558 | iio_push_event(indio_dev, event, data->timestamp); |
559 | ||
560 | /* Reset the interrupt flag */ | |
561 | ret = regmap_field_write(data->reg_flag_psint, 0); | |
562 | if (ret < 0) | |
563 | dev_err(&data->client->dev, "failed to reset interrupts\n"); | |
564 | mutex_unlock(&data->lock); | |
565 | ||
566 | return IRQ_HANDLED; | |
567 | } | |
568 | ||
be9e6229 TB |
569 | static int stk3310_probe(struct i2c_client *client, |
570 | const struct i2c_device_id *id) | |
571 | { | |
572 | int ret; | |
573 | struct iio_dev *indio_dev; | |
574 | struct stk3310_data *data; | |
575 | ||
576 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
577 | if (!indio_dev) { | |
578 | dev_err(&client->dev, "iio allocation failed!\n"); | |
579 | return -ENOMEM; | |
580 | } | |
581 | ||
582 | data = iio_priv(indio_dev); | |
583 | data->client = client; | |
584 | i2c_set_clientdata(client, indio_dev); | |
585 | mutex_init(&data->lock); | |
586 | ||
587 | ret = stk3310_regmap_init(data); | |
588 | if (ret < 0) | |
589 | return ret; | |
590 | ||
591 | indio_dev->dev.parent = &client->dev; | |
592 | indio_dev->info = &stk3310_info; | |
593 | indio_dev->name = STK3310_DRIVER_NAME; | |
594 | indio_dev->modes = INDIO_DIRECT_MODE; | |
595 | indio_dev->channels = stk3310_channels; | |
596 | indio_dev->num_channels = ARRAY_SIZE(stk3310_channels); | |
597 | ||
598 | ret = stk3310_init(indio_dev); | |
599 | if (ret < 0) | |
600 | return ret; | |
601 | ||
6839c1b0 | 602 | if (client->irq > 0) { |
3dd477ac TB |
603 | ret = devm_request_threaded_irq(&client->dev, client->irq, |
604 | stk3310_irq_handler, | |
605 | stk3310_irq_event_handler, | |
606 | IRQF_TRIGGER_FALLING | | |
607 | IRQF_ONESHOT, | |
608 | STK3310_EVENT, indio_dev); | |
7c7a9eea | 609 | if (ret < 0) { |
3dd477ac | 610 | dev_err(&client->dev, "request irq %d failed\n", |
5b958f11 | 611 | client->irq); |
7c7a9eea HK |
612 | goto err_standby; |
613 | } | |
3dd477ac TB |
614 | } |
615 | ||
037e966f HK |
616 | ret = iio_device_register(indio_dev); |
617 | if (ret < 0) { | |
618 | dev_err(&client->dev, "device_register failed\n"); | |
7c7a9eea | 619 | goto err_standby; |
037e966f HK |
620 | } |
621 | ||
7c7a9eea HK |
622 | return 0; |
623 | ||
624 | err_standby: | |
625 | stk3310_set_state(data, STK3310_STATE_STANDBY); | |
be9e6229 TB |
626 | return ret; |
627 | } | |
628 | ||
629 | static int stk3310_remove(struct i2c_client *client) | |
630 | { | |
631 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
632 | ||
633 | iio_device_unregister(indio_dev); | |
634 | return stk3310_set_state(iio_priv(indio_dev), STK3310_STATE_STANDBY); | |
635 | } | |
636 | ||
637 | #ifdef CONFIG_PM_SLEEP | |
638 | static int stk3310_suspend(struct device *dev) | |
639 | { | |
640 | struct stk3310_data *data; | |
641 | ||
642 | data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); | |
643 | ||
644 | return stk3310_set_state(data, STK3310_STATE_STANDBY); | |
645 | } | |
646 | ||
647 | static int stk3310_resume(struct device *dev) | |
648 | { | |
952c3aa3 | 649 | u8 state = 0; |
be9e6229 TB |
650 | struct stk3310_data *data; |
651 | ||
652 | data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); | |
653 | if (data->ps_enabled) | |
654 | state |= STK3310_STATE_EN_PS; | |
655 | if (data->als_enabled) | |
656 | state |= STK3310_STATE_EN_ALS; | |
657 | ||
658 | return stk3310_set_state(data, state); | |
659 | } | |
660 | ||
661 | static SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume); | |
662 | ||
663 | #define STK3310_PM_OPS (&stk3310_pm_ops) | |
664 | #else | |
665 | #define STK3310_PM_OPS NULL | |
666 | #endif | |
667 | ||
668 | static const struct i2c_device_id stk3310_i2c_id[] = { | |
669 | {"STK3310", 0}, | |
670 | {"STK3311", 0}, | |
671 | {} | |
672 | }; | |
58e446fc | 673 | MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id); |
be9e6229 TB |
674 | |
675 | static const struct acpi_device_id stk3310_acpi_id[] = { | |
676 | {"STK3310", 0}, | |
677 | {"STK3311", 0}, | |
678 | {} | |
679 | }; | |
680 | ||
681 | MODULE_DEVICE_TABLE(acpi, stk3310_acpi_id); | |
682 | ||
683 | static struct i2c_driver stk3310_driver = { | |
684 | .driver = { | |
685 | .name = "stk3310", | |
686 | .pm = STK3310_PM_OPS, | |
687 | .acpi_match_table = ACPI_PTR(stk3310_acpi_id), | |
688 | }, | |
689 | .probe = stk3310_probe, | |
690 | .remove = stk3310_remove, | |
691 | .id_table = stk3310_i2c_id, | |
692 | }; | |
693 | ||
694 | module_i2c_driver(stk3310_driver); | |
695 | ||
696 | MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>"); | |
697 | MODULE_DESCRIPTION("STK3310 Ambient Light and Proximity Sensor driver"); | |
698 | MODULE_LICENSE("GPL v2"); |