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