diff options
author | Jacek Anaszewski <j.anaszewski@samsung.com> | 2013-09-09 11:23:00 -0400 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2013-09-15 06:07:49 -0400 |
commit | bf29fbeaa13d3350ca71df70c705d8b883c45692 (patch) | |
tree | 27eb56321c6cd7f94d04650e6d863dd2742671bb /drivers/iio/light | |
parent | 02fa18958ce065ccc23ad59cff736031d62e9950 (diff) |
iio: gp2ap020a00f: Add a driver for the device
Add a new driver for the ambient light/proximity sensor
device. The driver exposes three channels: light_clear
light_ir and proximity. It also supports triggered buffer,
high and low ambient light threshold event and proximity
detection events.
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio/light')
-rw-r--r-- | drivers/iio/light/Kconfig | 12 | ||||
-rw-r--r-- | drivers/iio/light/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/light/gp2ap020a00f.c | 1622 |
3 files changed, 1635 insertions, 0 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index e73a1aba990d..0a25ae6b132e 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig | |||
@@ -27,6 +27,18 @@ config APDS9300 | |||
27 | To compile this driver as a module, choose M here: the | 27 | To compile this driver as a module, choose M here: the |
28 | module will be called apds9300. | 28 | module will be called apds9300. |
29 | 29 | ||
30 | config GP2AP020A00F | ||
31 | tristate "Sharp GP2AP020A00F Proximity/ALS sensor" | ||
32 | depends on I2C | ||
33 | select IIO_BUFFER | ||
34 | select IIO_TRIGGERED_BUFFER | ||
35 | help | ||
36 | Say Y here if you have a Sharp GP2AP020A00F proximity/ALS combo-chip | ||
37 | hooked to an I2C bus. | ||
38 | |||
39 | To compile this driver as a module, choose M here: the | ||
40 | module will be called gp2ap020a00f. | ||
41 | |||
30 | config HID_SENSOR_ALS | 42 | config HID_SENSOR_ALS |
31 | depends on HID_SENSOR_HUB | 43 | depends on HID_SENSOR_HUB |
32 | select IIO_BUFFER | 44 | select IIO_BUFFER |
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index fb3114006c26..cef590f2ff00 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile | |||
@@ -5,6 +5,7 @@ | |||
5 | # When adding new entries keep the list in alphabetical order | 5 | # When adding new entries keep the list in alphabetical order |
6 | obj-$(CONFIG_ADJD_S311) += adjd_s311.o | 6 | obj-$(CONFIG_ADJD_S311) += adjd_s311.o |
7 | obj-$(CONFIG_APDS9300) += apds9300.o | 7 | obj-$(CONFIG_APDS9300) += apds9300.o |
8 | obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o | ||
8 | obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o | 9 | obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o |
9 | obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o | 10 | obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o |
10 | obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o | 11 | obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o |
diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c new file mode 100644 index 000000000000..62809b53a7f0 --- /dev/null +++ b/drivers/iio/light/gp2ap020a00f.c | |||
@@ -0,0 +1,1622 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
3 | * Author: Jacek Anaszewski <j.anaszewski@samsung.com> | ||
4 | * | ||
5 | * IIO features supported by the driver: | ||
6 | * | ||
7 | * Read-only raw channels: | ||
8 | * - illiminance_clear [lux] | ||
9 | * - illiminance_ir | ||
10 | * - proximity | ||
11 | * | ||
12 | * Triggered buffer: | ||
13 | * - illiminance_clear | ||
14 | * - illiminance_ir | ||
15 | * - proximity | ||
16 | * | ||
17 | * Events: | ||
18 | * - illuminance_clear (rising and falling) | ||
19 | * - proximity (rising and falling) | ||
20 | * - both falling and rising thresholds for the proximity events | ||
21 | * must be set to the values greater than 0. | ||
22 | * | ||
23 | * The driver supports triggered buffers for all the three | ||
24 | * channels as well as high and low threshold events for the | ||
25 | * illuminance_clear and proxmimity channels. Triggers | ||
26 | * can be enabled simultaneously with both illuminance_clear | ||
27 | * events. Proximity events cannot be enabled simultaneously | ||
28 | * with any triggers or illuminance events. Enabling/disabling | ||
29 | * one of the proximity events automatically enables/disables | ||
30 | * the other one. | ||
31 | * | ||
32 | * This program is free software; you can redistribute it and/or modify | ||
33 | * it under the terms of the GNU General Public License version 2, as | ||
34 | * published by the Free Software Foundation. | ||
35 | */ | ||
36 | |||
37 | #include <linux/debugfs.h> | ||
38 | #include <linux/delay.h> | ||
39 | #include <linux/i2c.h> | ||
40 | #include <linux/interrupt.h> | ||
41 | #include <linux/irq.h> | ||
42 | #include <linux/irq_work.h> | ||
43 | #include <linux/module.h> | ||
44 | #include <linux/mutex.h> | ||
45 | #include <linux/regmap.h> | ||
46 | #include <linux/regulator/consumer.h> | ||
47 | #include <linux/slab.h> | ||
48 | #include <linux/iio/buffer.h> | ||
49 | #include <linux/iio/events.h> | ||
50 | #include <linux/iio/iio.h> | ||
51 | #include <linux/iio/sysfs.h> | ||
52 | #include <linux/iio/trigger.h> | ||
53 | #include <linux/iio/trigger_consumer.h> | ||
54 | #include <linux/iio/triggered_buffer.h> | ||
55 | |||
56 | #define GP2A_I2C_NAME "gp2ap020a00f" | ||
57 | |||
58 | /* Registers */ | ||
59 | #define GP2AP020A00F_OP_REG 0x00 /* Basic operations */ | ||
60 | #define GP2AP020A00F_ALS_REG 0x01 /* ALS related settings */ | ||
61 | #define GP2AP020A00F_PS_REG 0x02 /* PS related settings */ | ||
62 | #define GP2AP020A00F_LED_REG 0x03 /* LED reg */ | ||
63 | #define GP2AP020A00F_TL_L_REG 0x04 /* ALS: Threshold low LSB */ | ||
64 | #define GP2AP020A00F_TL_H_REG 0x05 /* ALS: Threshold low MSB */ | ||
65 | #define GP2AP020A00F_TH_L_REG 0x06 /* ALS: Threshold high LSB */ | ||
66 | #define GP2AP020A00F_TH_H_REG 0x07 /* ALS: Threshold high MSB */ | ||
67 | #define GP2AP020A00F_PL_L_REG 0x08 /* PS: Threshold low LSB */ | ||
68 | #define GP2AP020A00F_PL_H_REG 0x09 /* PS: Threshold low MSB */ | ||
69 | #define GP2AP020A00F_PH_L_REG 0x0a /* PS: Threshold high LSB */ | ||
70 | #define GP2AP020A00F_PH_H_REG 0x0b /* PS: Threshold high MSB */ | ||
71 | #define GP2AP020A00F_D0_L_REG 0x0c /* ALS result: Clear/Illuminance LSB */ | ||
72 | #define GP2AP020A00F_D0_H_REG 0x0d /* ALS result: Clear/Illuminance MSB */ | ||
73 | #define GP2AP020A00F_D1_L_REG 0x0e /* ALS result: IR LSB */ | ||
74 | #define GP2AP020A00F_D1_H_REG 0x0f /* ALS result: IR LSB */ | ||
75 | #define GP2AP020A00F_D2_L_REG 0x10 /* PS result LSB */ | ||
76 | #define GP2AP020A00F_D2_H_REG 0x11 /* PS result MSB */ | ||
77 | #define GP2AP020A00F_NUM_REGS 0x12 /* Number of registers */ | ||
78 | |||
79 | /* OP_REG bits */ | ||
80 | #define GP2AP020A00F_OP3_MASK 0x80 /* Software shutdown */ | ||
81 | #define GP2AP020A00F_OP3_SHUTDOWN 0x00 | ||
82 | #define GP2AP020A00F_OP3_OPERATION 0x80 | ||
83 | #define GP2AP020A00F_OP2_MASK 0x40 /* Auto shutdown/Continuous mode */ | ||
84 | #define GP2AP020A00F_OP2_AUTO_SHUTDOWN 0x00 | ||
85 | #define GP2AP020A00F_OP2_CONT_OPERATION 0x40 | ||
86 | #define GP2AP020A00F_OP_MASK 0x30 /* Operating mode selection */ | ||
87 | #define GP2AP020A00F_OP_ALS_AND_PS 0x00 | ||
88 | #define GP2AP020A00F_OP_ALS 0x10 | ||
89 | #define GP2AP020A00F_OP_PS 0x20 | ||
90 | #define GP2AP020A00F_OP_DEBUG 0x30 | ||
91 | #define GP2AP020A00F_PROX_MASK 0x08 /* PS: detection/non-detection */ | ||
92 | #define GP2AP020A00F_PROX_NON_DETECT 0x00 | ||
93 | #define GP2AP020A00F_PROX_DETECT 0x08 | ||
94 | #define GP2AP020A00F_FLAG_P 0x04 /* PS: interrupt result */ | ||
95 | #define GP2AP020A00F_FLAG_A 0x02 /* ALS: interrupt result */ | ||
96 | #define GP2AP020A00F_TYPE_MASK 0x01 /* Output data type selection */ | ||
97 | #define GP2AP020A00F_TYPE_MANUAL_CALC 0x00 | ||
98 | #define GP2AP020A00F_TYPE_AUTO_CALC 0x01 | ||
99 | |||
100 | /* ALS_REG bits */ | ||
101 | #define GP2AP020A00F_PRST_MASK 0xc0 /* Number of measurement cycles */ | ||
102 | #define GP2AP020A00F_PRST_ONCE 0x00 | ||
103 | #define GP2AP020A00F_PRST_4_CYCLES 0x40 | ||
104 | #define GP2AP020A00F_PRST_8_CYCLES 0x80 | ||
105 | #define GP2AP020A00F_PRST_16_CYCLES 0xc0 | ||
106 | #define GP2AP020A00F_RES_A_MASK 0x38 /* ALS: Resolution */ | ||
107 | #define GP2AP020A00F_RES_A_800ms 0x00 | ||
108 | #define GP2AP020A00F_RES_A_400ms 0x08 | ||
109 | #define GP2AP020A00F_RES_A_200ms 0x10 | ||
110 | #define GP2AP020A00F_RES_A_100ms 0x18 | ||
111 | #define GP2AP020A00F_RES_A_25ms 0x20 | ||
112 | #define GP2AP020A00F_RES_A_6_25ms 0x28 | ||
113 | #define GP2AP020A00F_RES_A_1_56ms 0x30 | ||
114 | #define GP2AP020A00F_RES_A_0_39ms 0x38 | ||
115 | #define GP2AP020A00F_RANGE_A_MASK 0x07 /* ALS: Max measurable range */ | ||
116 | #define GP2AP020A00F_RANGE_A_x1 0x00 | ||
117 | #define GP2AP020A00F_RANGE_A_x2 0x01 | ||
118 | #define GP2AP020A00F_RANGE_A_x4 0x02 | ||
119 | #define GP2AP020A00F_RANGE_A_x8 0x03 | ||
120 | #define GP2AP020A00F_RANGE_A_x16 0x04 | ||
121 | #define GP2AP020A00F_RANGE_A_x32 0x05 | ||
122 | #define GP2AP020A00F_RANGE_A_x64 0x06 | ||
123 | #define GP2AP020A00F_RANGE_A_x128 0x07 | ||
124 | |||
125 | /* PS_REG bits */ | ||
126 | #define GP2AP020A00F_ALC_MASK 0x80 /* Auto light cancel */ | ||
127 | #define GP2AP020A00F_ALC_ON 0x80 | ||
128 | #define GP2AP020A00F_ALC_OFF 0x00 | ||
129 | #define GP2AP020A00F_INTTYPE_MASK 0x40 /* Interrupt type setting */ | ||
130 | #define GP2AP020A00F_INTTYPE_LEVEL 0x00 | ||
131 | #define GP2AP020A00F_INTTYPE_PULSE 0x40 | ||
132 | #define GP2AP020A00F_RES_P_MASK 0x38 /* PS: Resolution */ | ||
133 | #define GP2AP020A00F_RES_P_800ms_x2 0x00 | ||
134 | #define GP2AP020A00F_RES_P_400ms_x2 0x08 | ||
135 | #define GP2AP020A00F_RES_P_200ms_x2 0x10 | ||
136 | #define GP2AP020A00F_RES_P_100ms_x2 0x18 | ||
137 | #define GP2AP020A00F_RES_P_25ms_x2 0x20 | ||
138 | #define GP2AP020A00F_RES_P_6_25ms_x2 0x28 | ||
139 | #define GP2AP020A00F_RES_P_1_56ms_x2 0x30 | ||
140 | #define GP2AP020A00F_RES_P_0_39ms_x2 0x38 | ||
141 | #define GP2AP020A00F_RANGE_P_MASK 0x07 /* PS: Max measurable range */ | ||
142 | #define GP2AP020A00F_RANGE_P_x1 0x00 | ||
143 | #define GP2AP020A00F_RANGE_P_x2 0x01 | ||
144 | #define GP2AP020A00F_RANGE_P_x4 0x02 | ||
145 | #define GP2AP020A00F_RANGE_P_x8 0x03 | ||
146 | #define GP2AP020A00F_RANGE_P_x16 0x04 | ||
147 | #define GP2AP020A00F_RANGE_P_x32 0x05 | ||
148 | #define GP2AP020A00F_RANGE_P_x64 0x06 | ||
149 | #define GP2AP020A00F_RANGE_P_x128 0x07 | ||
150 | |||
151 | /* LED reg bits */ | ||
152 | #define GP2AP020A00F_INTVAL_MASK 0xc0 /* Intermittent operating */ | ||
153 | #define GP2AP020A00F_INTVAL_0 0x00 | ||
154 | #define GP2AP020A00F_INTVAL_4 0x40 | ||
155 | #define GP2AP020A00F_INTVAL_8 0x80 | ||
156 | #define GP2AP020A00F_INTVAL_16 0xc0 | ||
157 | #define GP2AP020A00F_IS_MASK 0x30 /* ILED drive peak current */ | ||
158 | #define GP2AP020A00F_IS_13_8mA 0x00 | ||
159 | #define GP2AP020A00F_IS_27_5mA 0x10 | ||
160 | #define GP2AP020A00F_IS_55mA 0x20 | ||
161 | #define GP2AP020A00F_IS_110mA 0x30 | ||
162 | #define GP2AP020A00F_PIN_MASK 0x0c /* INT terminal setting */ | ||
163 | #define GP2AP020A00F_PIN_ALS_OR_PS 0x00 | ||
164 | #define GP2AP020A00F_PIN_ALS 0x04 | ||
165 | #define GP2AP020A00F_PIN_PS 0x08 | ||
166 | #define GP2AP020A00F_PIN_PS_DETECT 0x0c | ||
167 | #define GP2AP020A00F_FREQ_MASK 0x02 /* LED modulation frequency */ | ||
168 | #define GP2AP020A00F_FREQ_327_5kHz 0x00 | ||
169 | #define GP2AP020A00F_FREQ_81_8kHz 0x02 | ||
170 | #define GP2AP020A00F_RST 0x01 /* Software reset */ | ||
171 | |||
172 | #define GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR 0 | ||
173 | #define GP2AP020A00F_SCAN_MODE_LIGHT_IR 1 | ||
174 | #define GP2AP020A00F_SCAN_MODE_PROXIMITY 2 | ||
175 | #define GP2AP020A00F_CHAN_TIMESTAMP 3 | ||
176 | |||
177 | #define GP2AP020A00F_DATA_READY_TIMEOUT msecs_to_jiffies(1000) | ||
178 | #define GP2AP020A00F_DATA_REG(chan) (GP2AP020A00F_D0_L_REG + \ | ||
179 | (chan) * 2) | ||
180 | #define GP2AP020A00F_THRESH_REG(th_val_id) (GP2AP020A00F_TL_L_REG + \ | ||
181 | (th_val_id) * 2) | ||
182 | #define GP2AP020A00F_THRESH_VAL_ID(reg_addr) ((reg_addr - 4) / 2) | ||
183 | |||
184 | #define GP2AP020A00F_SUBTRACT_MODE 0 | ||
185 | #define GP2AP020A00F_ADD_MODE 1 | ||
186 | |||
187 | #define GP2AP020A00F_MAX_CHANNELS 3 | ||
188 | |||
189 | enum gp2ap020a00f_opmode { | ||
190 | GP2AP020A00F_OPMODE_READ_RAW_CLEAR, | ||
191 | GP2AP020A00F_OPMODE_READ_RAW_IR, | ||
192 | GP2AP020A00F_OPMODE_READ_RAW_PROXIMITY, | ||
193 | GP2AP020A00F_OPMODE_ALS, | ||
194 | GP2AP020A00F_OPMODE_PS, | ||
195 | GP2AP020A00F_OPMODE_ALS_AND_PS, | ||
196 | GP2AP020A00F_OPMODE_PROX_DETECT, | ||
197 | GP2AP020A00F_OPMODE_SHUTDOWN, | ||
198 | GP2AP020A00F_NUM_OPMODES, | ||
199 | }; | ||
200 | |||
201 | enum gp2ap020a00f_cmd { | ||
202 | GP2AP020A00F_CMD_READ_RAW_CLEAR, | ||
203 | GP2AP020A00F_CMD_READ_RAW_IR, | ||
204 | GP2AP020A00F_CMD_READ_RAW_PROXIMITY, | ||
205 | GP2AP020A00F_CMD_TRIGGER_CLEAR_EN, | ||
206 | GP2AP020A00F_CMD_TRIGGER_CLEAR_DIS, | ||
207 | GP2AP020A00F_CMD_TRIGGER_IR_EN, | ||
208 | GP2AP020A00F_CMD_TRIGGER_IR_DIS, | ||
209 | GP2AP020A00F_CMD_TRIGGER_PROX_EN, | ||
210 | GP2AP020A00F_CMD_TRIGGER_PROX_DIS, | ||
211 | GP2AP020A00F_CMD_ALS_HIGH_EV_EN, | ||
212 | GP2AP020A00F_CMD_ALS_HIGH_EV_DIS, | ||
213 | GP2AP020A00F_CMD_ALS_LOW_EV_EN, | ||
214 | GP2AP020A00F_CMD_ALS_LOW_EV_DIS, | ||
215 | GP2AP020A00F_CMD_PROX_HIGH_EV_EN, | ||
216 | GP2AP020A00F_CMD_PROX_HIGH_EV_DIS, | ||
217 | GP2AP020A00F_CMD_PROX_LOW_EV_EN, | ||
218 | GP2AP020A00F_CMD_PROX_LOW_EV_DIS, | ||
219 | }; | ||
220 | |||
221 | enum gp2ap020a00f_flags { | ||
222 | GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, | ||
223 | GP2AP020A00F_FLAG_ALS_IR_TRIGGER, | ||
224 | GP2AP020A00F_FLAG_PROX_TRIGGER, | ||
225 | GP2AP020A00F_FLAG_PROX_RISING_EV, | ||
226 | GP2AP020A00F_FLAG_PROX_FALLING_EV, | ||
227 | GP2AP020A00F_FLAG_ALS_RISING_EV, | ||
228 | GP2AP020A00F_FLAG_ALS_FALLING_EV, | ||
229 | GP2AP020A00F_FLAG_LUX_MODE_HI, | ||
230 | GP2AP020A00F_FLAG_DATA_READY, | ||
231 | }; | ||
232 | |||
233 | enum gp2ap020a00f_thresh_val_id { | ||
234 | GP2AP020A00F_THRESH_TL, | ||
235 | GP2AP020A00F_THRESH_TH, | ||
236 | GP2AP020A00F_THRESH_PL, | ||
237 | GP2AP020A00F_THRESH_PH, | ||
238 | }; | ||
239 | |||
240 | struct gp2ap020a00f_data { | ||
241 | const struct gp2ap020a00f_platform_data *pdata; | ||
242 | struct i2c_client *client; | ||
243 | struct mutex lock; | ||
244 | char *buffer; | ||
245 | struct regulator *vled_reg; | ||
246 | unsigned long flags; | ||
247 | enum gp2ap020a00f_opmode cur_opmode; | ||
248 | struct iio_trigger *trig; | ||
249 | struct regmap *regmap; | ||
250 | unsigned int thresh_val[4]; | ||
251 | u8 debug_reg_addr; | ||
252 | struct irq_work work; | ||
253 | wait_queue_head_t data_ready_queue; | ||
254 | }; | ||
255 | |||
256 | static const u8 gp2ap020a00f_reg_init_tab[] = { | ||
257 | [GP2AP020A00F_OP_REG] = GP2AP020A00F_OP3_SHUTDOWN, | ||
258 | [GP2AP020A00F_ALS_REG] = GP2AP020A00F_RES_A_25ms | | ||
259 | GP2AP020A00F_RANGE_A_x8, | ||
260 | [GP2AP020A00F_PS_REG] = GP2AP020A00F_ALC_ON | | ||
261 | GP2AP020A00F_RES_P_1_56ms_x2 | | ||
262 | GP2AP020A00F_RANGE_P_x4, | ||
263 | [GP2AP020A00F_LED_REG] = GP2AP020A00F_INTVAL_0 | | ||
264 | GP2AP020A00F_IS_110mA | | ||
265 | GP2AP020A00F_FREQ_327_5kHz, | ||
266 | [GP2AP020A00F_TL_L_REG] = 0, | ||
267 | [GP2AP020A00F_TL_H_REG] = 0, | ||
268 | [GP2AP020A00F_TH_L_REG] = 0, | ||
269 | [GP2AP020A00F_TH_H_REG] = 0, | ||
270 | [GP2AP020A00F_PL_L_REG] = 0, | ||
271 | [GP2AP020A00F_PL_H_REG] = 0, | ||
272 | [GP2AP020A00F_PH_L_REG] = 0, | ||
273 | [GP2AP020A00F_PH_H_REG] = 0, | ||
274 | }; | ||
275 | |||
276 | static bool gp2ap020a00f_is_volatile_reg(struct device *dev, unsigned int reg) | ||
277 | { | ||
278 | switch (reg) { | ||
279 | case GP2AP020A00F_OP_REG: | ||
280 | case GP2AP020A00F_D0_L_REG: | ||
281 | case GP2AP020A00F_D0_H_REG: | ||
282 | case GP2AP020A00F_D1_L_REG: | ||
283 | case GP2AP020A00F_D1_H_REG: | ||
284 | case GP2AP020A00F_D2_L_REG: | ||
285 | case GP2AP020A00F_D2_H_REG: | ||
286 | return true; | ||
287 | default: | ||
288 | return false; | ||
289 | } | ||
290 | } | ||
291 | |||
292 | static const struct regmap_config gp2ap020a00f_regmap_config = { | ||
293 | .reg_bits = 8, | ||
294 | .val_bits = 8, | ||
295 | |||
296 | .max_register = GP2AP020A00F_D2_H_REG, | ||
297 | .cache_type = REGCACHE_RBTREE, | ||
298 | |||
299 | .volatile_reg = gp2ap020a00f_is_volatile_reg, | ||
300 | }; | ||
301 | |||
302 | static const struct gp2ap020a00f_mutable_config_regs { | ||
303 | u8 op_reg; | ||
304 | u8 als_reg; | ||
305 | u8 ps_reg; | ||
306 | u8 led_reg; | ||
307 | } opmode_regs_settings[GP2AP020A00F_NUM_OPMODES] = { | ||
308 | [GP2AP020A00F_OPMODE_READ_RAW_CLEAR] = { | ||
309 | GP2AP020A00F_OP_ALS | GP2AP020A00F_OP2_CONT_OPERATION | ||
310 | | GP2AP020A00F_OP3_OPERATION | ||
311 | | GP2AP020A00F_TYPE_AUTO_CALC, | ||
312 | GP2AP020A00F_PRST_ONCE, | ||
313 | GP2AP020A00F_INTTYPE_LEVEL, | ||
314 | GP2AP020A00F_PIN_ALS | ||
315 | }, | ||
316 | [GP2AP020A00F_OPMODE_READ_RAW_IR] = { | ||
317 | GP2AP020A00F_OP_ALS | GP2AP020A00F_OP2_CONT_OPERATION | ||
318 | | GP2AP020A00F_OP3_OPERATION | ||
319 | | GP2AP020A00F_TYPE_MANUAL_CALC, | ||
320 | GP2AP020A00F_PRST_ONCE, | ||
321 | GP2AP020A00F_INTTYPE_LEVEL, | ||
322 | GP2AP020A00F_PIN_ALS | ||
323 | }, | ||
324 | [GP2AP020A00F_OPMODE_READ_RAW_PROXIMITY] = { | ||
325 | GP2AP020A00F_OP_PS | GP2AP020A00F_OP2_CONT_OPERATION | ||
326 | | GP2AP020A00F_OP3_OPERATION | ||
327 | | GP2AP020A00F_TYPE_MANUAL_CALC, | ||
328 | GP2AP020A00F_PRST_ONCE, | ||
329 | GP2AP020A00F_INTTYPE_LEVEL, | ||
330 | GP2AP020A00F_PIN_PS | ||
331 | }, | ||
332 | [GP2AP020A00F_OPMODE_PROX_DETECT] = { | ||
333 | GP2AP020A00F_OP_PS | GP2AP020A00F_OP2_CONT_OPERATION | ||
334 | | GP2AP020A00F_OP3_OPERATION | ||
335 | | GP2AP020A00F_TYPE_MANUAL_CALC, | ||
336 | GP2AP020A00F_PRST_4_CYCLES, | ||
337 | GP2AP020A00F_INTTYPE_PULSE, | ||
338 | GP2AP020A00F_PIN_PS_DETECT | ||
339 | }, | ||
340 | [GP2AP020A00F_OPMODE_ALS] = { | ||
341 | GP2AP020A00F_OP_ALS | GP2AP020A00F_OP2_CONT_OPERATION | ||
342 | | GP2AP020A00F_OP3_OPERATION | ||
343 | | GP2AP020A00F_TYPE_AUTO_CALC, | ||
344 | GP2AP020A00F_PRST_ONCE, | ||
345 | GP2AP020A00F_INTTYPE_LEVEL, | ||
346 | GP2AP020A00F_PIN_ALS | ||
347 | }, | ||
348 | [GP2AP020A00F_OPMODE_PS] = { | ||
349 | GP2AP020A00F_OP_PS | GP2AP020A00F_OP2_CONT_OPERATION | ||
350 | | GP2AP020A00F_OP3_OPERATION | ||
351 | | GP2AP020A00F_TYPE_MANUAL_CALC, | ||
352 | GP2AP020A00F_PRST_4_CYCLES, | ||
353 | GP2AP020A00F_INTTYPE_LEVEL, | ||
354 | GP2AP020A00F_PIN_PS | ||
355 | }, | ||
356 | [GP2AP020A00F_OPMODE_ALS_AND_PS] = { | ||
357 | GP2AP020A00F_OP_ALS_AND_PS | ||
358 | | GP2AP020A00F_OP2_CONT_OPERATION | ||
359 | | GP2AP020A00F_OP3_OPERATION | ||
360 | | GP2AP020A00F_TYPE_AUTO_CALC, | ||
361 | GP2AP020A00F_PRST_4_CYCLES, | ||
362 | GP2AP020A00F_INTTYPE_LEVEL, | ||
363 | GP2AP020A00F_PIN_ALS_OR_PS | ||
364 | }, | ||
365 | [GP2AP020A00F_OPMODE_SHUTDOWN] = { GP2AP020A00F_OP3_SHUTDOWN, }, | ||
366 | }; | ||
367 | |||
368 | static int gp2ap020a00f_set_operation_mode(struct gp2ap020a00f_data *data, | ||
369 | enum gp2ap020a00f_opmode op) | ||
370 | { | ||
371 | unsigned int op_reg_val; | ||
372 | int err; | ||
373 | |||
374 | if (op != GP2AP020A00F_OPMODE_SHUTDOWN) { | ||
375 | err = regmap_read(data->regmap, GP2AP020A00F_OP_REG, | ||
376 | &op_reg_val); | ||
377 | if (err < 0) | ||
378 | return err; | ||
379 | /* | ||
380 | * Shutdown the device if the operation being executed entails | ||
381 | * mode transition. | ||
382 | */ | ||
383 | if ((opmode_regs_settings[op].op_reg & GP2AP020A00F_OP_MASK) != | ||
384 | (op_reg_val & GP2AP020A00F_OP_MASK)) { | ||
385 | /* set shutdown mode */ | ||
386 | err = regmap_update_bits(data->regmap, | ||
387 | GP2AP020A00F_OP_REG, GP2AP020A00F_OP3_MASK, | ||
388 | GP2AP020A00F_OP3_SHUTDOWN); | ||
389 | if (err < 0) | ||
390 | return err; | ||
391 | } | ||
392 | |||
393 | err = regmap_update_bits(data->regmap, GP2AP020A00F_ALS_REG, | ||
394 | GP2AP020A00F_PRST_MASK, opmode_regs_settings[op] | ||
395 | .als_reg); | ||
396 | if (err < 0) | ||
397 | return err; | ||
398 | |||
399 | err = regmap_update_bits(data->regmap, GP2AP020A00F_PS_REG, | ||
400 | GP2AP020A00F_INTTYPE_MASK, opmode_regs_settings[op] | ||
401 | .ps_reg); | ||
402 | if (err < 0) | ||
403 | return err; | ||
404 | |||
405 | err = regmap_update_bits(data->regmap, GP2AP020A00F_LED_REG, | ||
406 | GP2AP020A00F_PIN_MASK, opmode_regs_settings[op] | ||
407 | .led_reg); | ||
408 | if (err < 0) | ||
409 | return err; | ||
410 | } | ||
411 | |||
412 | /* Set OP_REG and apply operation mode (power on / off) */ | ||
413 | err = regmap_update_bits(data->regmap, | ||
414 | GP2AP020A00F_OP_REG, | ||
415 | GP2AP020A00F_OP_MASK | GP2AP020A00F_OP2_MASK | | ||
416 | GP2AP020A00F_OP3_MASK | GP2AP020A00F_TYPE_MASK, | ||
417 | opmode_regs_settings[op].op_reg); | ||
418 | if (err < 0) | ||
419 | return err; | ||
420 | |||
421 | data->cur_opmode = op; | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static bool gp2ap020a00f_als_enabled(struct gp2ap020a00f_data *data) | ||
427 | { | ||
428 | return test_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &data->flags) || | ||
429 | test_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &data->flags) || | ||
430 | test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags) || | ||
431 | test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); | ||
432 | } | ||
433 | |||
434 | static bool gp2ap020a00f_prox_detect_enabled(struct gp2ap020a00f_data *data) | ||
435 | { | ||
436 | return test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags) || | ||
437 | test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); | ||
438 | } | ||
439 | |||
440 | static int gp2ap020a00f_write_event_threshold(struct gp2ap020a00f_data *data, | ||
441 | enum gp2ap020a00f_thresh_val_id th_val_id, | ||
442 | bool enable) | ||
443 | { | ||
444 | __le16 thresh_buf = 0; | ||
445 | unsigned int thresh_reg_val; | ||
446 | |||
447 | if (!enable) | ||
448 | thresh_reg_val = 0; | ||
449 | else if (test_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags) && | ||
450 | th_val_id != GP2AP020A00F_THRESH_PL && | ||
451 | th_val_id != GP2AP020A00F_THRESH_PH) | ||
452 | /* | ||
453 | * For the high lux mode ALS threshold has to be scaled down | ||
454 | * to allow for proper comparison with the output value. | ||
455 | */ | ||
456 | thresh_reg_val = data->thresh_val[th_val_id] / 16; | ||
457 | else | ||
458 | thresh_reg_val = data->thresh_val[th_val_id] > 16000 ? | ||
459 | 16000 : | ||
460 | data->thresh_val[th_val_id]; | ||
461 | |||
462 | thresh_buf = cpu_to_le16(thresh_reg_val); | ||
463 | |||
464 | return regmap_bulk_write(data->regmap, | ||
465 | GP2AP020A00F_THRESH_REG(th_val_id), | ||
466 | (u8 *)&thresh_buf, 2); | ||
467 | } | ||
468 | |||
469 | static int gp2ap020a00f_alter_opmode(struct gp2ap020a00f_data *data, | ||
470 | enum gp2ap020a00f_opmode diff_mode, int add_sub) | ||
471 | { | ||
472 | enum gp2ap020a00f_opmode new_mode; | ||
473 | |||
474 | if (diff_mode != GP2AP020A00F_OPMODE_ALS && | ||
475 | diff_mode != GP2AP020A00F_OPMODE_PS) | ||
476 | return -EINVAL; | ||
477 | |||
478 | if (add_sub == GP2AP020A00F_ADD_MODE) { | ||
479 | if (data->cur_opmode == GP2AP020A00F_OPMODE_SHUTDOWN) | ||
480 | new_mode = diff_mode; | ||
481 | else | ||
482 | new_mode = GP2AP020A00F_OPMODE_ALS_AND_PS; | ||
483 | } else { | ||
484 | if (data->cur_opmode == GP2AP020A00F_OPMODE_ALS_AND_PS) | ||
485 | new_mode = (diff_mode == GP2AP020A00F_OPMODE_ALS) ? | ||
486 | GP2AP020A00F_OPMODE_PS : | ||
487 | GP2AP020A00F_OPMODE_ALS; | ||
488 | else | ||
489 | new_mode = GP2AP020A00F_OPMODE_SHUTDOWN; | ||
490 | } | ||
491 | |||
492 | return gp2ap020a00f_set_operation_mode(data, new_mode); | ||
493 | } | ||
494 | |||
495 | static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, | ||
496 | enum gp2ap020a00f_cmd cmd) | ||
497 | { | ||
498 | int err = 0; | ||
499 | |||
500 | switch (cmd) { | ||
501 | case GP2AP020A00F_CMD_READ_RAW_CLEAR: | ||
502 | if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) | ||
503 | return -EBUSY; | ||
504 | err = gp2ap020a00f_set_operation_mode(data, | ||
505 | GP2AP020A00F_OPMODE_READ_RAW_CLEAR); | ||
506 | break; | ||
507 | case GP2AP020A00F_CMD_READ_RAW_IR: | ||
508 | if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) | ||
509 | return -EBUSY; | ||
510 | err = gp2ap020a00f_set_operation_mode(data, | ||
511 | GP2AP020A00F_OPMODE_READ_RAW_IR); | ||
512 | break; | ||
513 | case GP2AP020A00F_CMD_READ_RAW_PROXIMITY: | ||
514 | if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) | ||
515 | return -EBUSY; | ||
516 | err = gp2ap020a00f_set_operation_mode(data, | ||
517 | GP2AP020A00F_OPMODE_READ_RAW_PROXIMITY); | ||
518 | break; | ||
519 | case GP2AP020A00F_CMD_TRIGGER_CLEAR_EN: | ||
520 | if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) | ||
521 | return -EBUSY; | ||
522 | if (!gp2ap020a00f_als_enabled(data)) | ||
523 | err = gp2ap020a00f_alter_opmode(data, | ||
524 | GP2AP020A00F_OPMODE_ALS, | ||
525 | GP2AP020A00F_ADD_MODE); | ||
526 | set_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &data->flags); | ||
527 | break; | ||
528 | case GP2AP020A00F_CMD_TRIGGER_CLEAR_DIS: | ||
529 | clear_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &data->flags); | ||
530 | if (gp2ap020a00f_als_enabled(data)) | ||
531 | break; | ||
532 | err = gp2ap020a00f_alter_opmode(data, | ||
533 | GP2AP020A00F_OPMODE_ALS, | ||
534 | GP2AP020A00F_SUBTRACT_MODE); | ||
535 | break; | ||
536 | case GP2AP020A00F_CMD_TRIGGER_IR_EN: | ||
537 | if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) | ||
538 | return -EBUSY; | ||
539 | if (!gp2ap020a00f_als_enabled(data)) | ||
540 | err = gp2ap020a00f_alter_opmode(data, | ||
541 | GP2AP020A00F_OPMODE_ALS, | ||
542 | GP2AP020A00F_ADD_MODE); | ||
543 | set_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &data->flags); | ||
544 | break; | ||
545 | case GP2AP020A00F_CMD_TRIGGER_IR_DIS: | ||
546 | clear_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &data->flags); | ||
547 | if (gp2ap020a00f_als_enabled(data)) | ||
548 | break; | ||
549 | err = gp2ap020a00f_alter_opmode(data, | ||
550 | GP2AP020A00F_OPMODE_ALS, | ||
551 | GP2AP020A00F_SUBTRACT_MODE); | ||
552 | break; | ||
553 | case GP2AP020A00F_CMD_TRIGGER_PROX_EN: | ||
554 | if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) | ||
555 | return -EBUSY; | ||
556 | err = gp2ap020a00f_alter_opmode(data, | ||
557 | GP2AP020A00F_OPMODE_PS, | ||
558 | GP2AP020A00F_ADD_MODE); | ||
559 | set_bit(GP2AP020A00F_FLAG_PROX_TRIGGER, &data->flags); | ||
560 | break; | ||
561 | case GP2AP020A00F_CMD_TRIGGER_PROX_DIS: | ||
562 | clear_bit(GP2AP020A00F_FLAG_PROX_TRIGGER, &data->flags); | ||
563 | err = gp2ap020a00f_alter_opmode(data, | ||
564 | GP2AP020A00F_OPMODE_PS, | ||
565 | GP2AP020A00F_SUBTRACT_MODE); | ||
566 | break; | ||
567 | case GP2AP020A00F_CMD_ALS_HIGH_EV_EN: | ||
568 | if (test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags)) | ||
569 | return 0; | ||
570 | if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) | ||
571 | return -EBUSY; | ||
572 | if (!gp2ap020a00f_als_enabled(data)) { | ||
573 | err = gp2ap020a00f_alter_opmode(data, | ||
574 | GP2AP020A00F_OPMODE_ALS, | ||
575 | GP2AP020A00F_ADD_MODE); | ||
576 | if (err < 0) | ||
577 | return err; | ||
578 | } | ||
579 | set_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags); | ||
580 | err = gp2ap020a00f_write_event_threshold(data, | ||
581 | GP2AP020A00F_THRESH_TH, true); | ||
582 | break; | ||
583 | case GP2AP020A00F_CMD_ALS_HIGH_EV_DIS: | ||
584 | if (!test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags)) | ||
585 | return 0; | ||
586 | clear_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags); | ||
587 | if (!gp2ap020a00f_als_enabled(data)) { | ||
588 | err = gp2ap020a00f_alter_opmode(data, | ||
589 | GP2AP020A00F_OPMODE_ALS, | ||
590 | GP2AP020A00F_SUBTRACT_MODE); | ||
591 | if (err < 0) | ||
592 | return err; | ||
593 | } | ||
594 | err = gp2ap020a00f_write_event_threshold(data, | ||
595 | GP2AP020A00F_THRESH_TH, false); | ||
596 | break; | ||
597 | case GP2AP020A00F_CMD_ALS_LOW_EV_EN: | ||
598 | if (test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags)) | ||
599 | return 0; | ||
600 | if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) | ||
601 | return -EBUSY; | ||
602 | if (!gp2ap020a00f_als_enabled(data)) { | ||
603 | err = gp2ap020a00f_alter_opmode(data, | ||
604 | GP2AP020A00F_OPMODE_ALS, | ||
605 | GP2AP020A00F_ADD_MODE); | ||
606 | if (err < 0) | ||
607 | return err; | ||
608 | } | ||
609 | set_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); | ||
610 | err = gp2ap020a00f_write_event_threshold(data, | ||
611 | GP2AP020A00F_THRESH_TL, true); | ||
612 | break; | ||
613 | case GP2AP020A00F_CMD_ALS_LOW_EV_DIS: | ||
614 | if (!test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags)) | ||
615 | return 0; | ||
616 | clear_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); | ||
617 | if (!gp2ap020a00f_als_enabled(data)) { | ||
618 | err = gp2ap020a00f_alter_opmode(data, | ||
619 | GP2AP020A00F_OPMODE_ALS, | ||
620 | GP2AP020A00F_SUBTRACT_MODE); | ||
621 | if (err < 0) | ||
622 | return err; | ||
623 | } | ||
624 | err = gp2ap020a00f_write_event_threshold(data, | ||
625 | GP2AP020A00F_THRESH_TL, false); | ||
626 | break; | ||
627 | case GP2AP020A00F_CMD_PROX_HIGH_EV_EN: | ||
628 | if (test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags)) | ||
629 | return 0; | ||
630 | if (gp2ap020a00f_als_enabled(data) || | ||
631 | data->cur_opmode == GP2AP020A00F_OPMODE_PS) | ||
632 | return -EBUSY; | ||
633 | if (!gp2ap020a00f_prox_detect_enabled(data)) { | ||
634 | err = gp2ap020a00f_set_operation_mode(data, | ||
635 | GP2AP020A00F_OPMODE_PROX_DETECT); | ||
636 | if (err < 0) | ||
637 | return err; | ||
638 | } | ||
639 | set_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); | ||
640 | err = gp2ap020a00f_write_event_threshold(data, | ||
641 | GP2AP020A00F_THRESH_PH, true); | ||
642 | break; | ||
643 | case GP2AP020A00F_CMD_PROX_HIGH_EV_DIS: | ||
644 | if (!test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags)) | ||
645 | return 0; | ||
646 | clear_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); | ||
647 | err = gp2ap020a00f_set_operation_mode(data, | ||
648 | GP2AP020A00F_OPMODE_SHUTDOWN); | ||
649 | if (err < 0) | ||
650 | return err; | ||
651 | err = gp2ap020a00f_write_event_threshold(data, | ||
652 | GP2AP020A00F_THRESH_PH, false); | ||
653 | break; | ||
654 | case GP2AP020A00F_CMD_PROX_LOW_EV_EN: | ||
655 | if (test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags)) | ||
656 | return 0; | ||
657 | if (gp2ap020a00f_als_enabled(data) || | ||
658 | data->cur_opmode == GP2AP020A00F_OPMODE_PS) | ||
659 | return -EBUSY; | ||
660 | if (!gp2ap020a00f_prox_detect_enabled(data)) { | ||
661 | err = gp2ap020a00f_set_operation_mode(data, | ||
662 | GP2AP020A00F_OPMODE_PROX_DETECT); | ||
663 | if (err < 0) | ||
664 | return err; | ||
665 | } | ||
666 | set_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); | ||
667 | err = gp2ap020a00f_write_event_threshold(data, | ||
668 | GP2AP020A00F_THRESH_PL, true); | ||
669 | break; | ||
670 | case GP2AP020A00F_CMD_PROX_LOW_EV_DIS: | ||
671 | if (!test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags)) | ||
672 | return 0; | ||
673 | clear_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); | ||
674 | err = gp2ap020a00f_set_operation_mode(data, | ||
675 | GP2AP020A00F_OPMODE_SHUTDOWN); | ||
676 | if (err < 0) | ||
677 | return err; | ||
678 | err = gp2ap020a00f_write_event_threshold(data, | ||
679 | GP2AP020A00F_THRESH_PL, false); | ||
680 | break; | ||
681 | } | ||
682 | |||
683 | return err; | ||
684 | } | ||
685 | |||
686 | static int wait_conversion_complete_irq(struct gp2ap020a00f_data *data) | ||
687 | { | ||
688 | int ret; | ||
689 | |||
690 | ret = wait_event_timeout(data->data_ready_queue, | ||
691 | test_bit(GP2AP020A00F_FLAG_DATA_READY, | ||
692 | &data->flags), | ||
693 | GP2AP020A00F_DATA_READY_TIMEOUT); | ||
694 | clear_bit(GP2AP020A00F_FLAG_DATA_READY, &data->flags); | ||
695 | |||
696 | return ret > 0 ? 0 : -ETIME; | ||
697 | } | ||
698 | |||
699 | static int gp2ap020a00f_read_output(struct gp2ap020a00f_data *data, | ||
700 | unsigned int output_reg, int *val) | ||
701 | { | ||
702 | u8 reg_buf[2]; | ||
703 | int err; | ||
704 | |||
705 | err = wait_conversion_complete_irq(data); | ||
706 | if (err < 0) | ||
707 | dev_dbg(&data->client->dev, "data ready timeout\n"); | ||
708 | |||
709 | err = regmap_bulk_read(data->regmap, output_reg, reg_buf, 2); | ||
710 | if (err < 0) | ||
711 | return err; | ||
712 | |||
713 | *val = le16_to_cpup((__le16 *)reg_buf); | ||
714 | |||
715 | return err; | ||
716 | } | ||
717 | |||
718 | static bool gp2ap020a00f_adjust_lux_mode(struct gp2ap020a00f_data *data, | ||
719 | int output_val) | ||
720 | { | ||
721 | u8 new_range = 0xff; | ||
722 | int err; | ||
723 | |||
724 | if (!test_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags)) { | ||
725 | if (output_val > 16000) { | ||
726 | set_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags); | ||
727 | new_range = GP2AP020A00F_RANGE_A_x128; | ||
728 | } | ||
729 | } else { | ||
730 | if (output_val < 1000) { | ||
731 | clear_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags); | ||
732 | new_range = GP2AP020A00F_RANGE_A_x8; | ||
733 | } | ||
734 | } | ||
735 | |||
736 | if (new_range != 0xff) { | ||
737 | /* Clear als threshold registers to avoid spurious | ||
738 | * events caused by lux mode transition. | ||
739 | */ | ||
740 | err = gp2ap020a00f_write_event_threshold(data, | ||
741 | GP2AP020A00F_THRESH_TH, false); | ||
742 | if (err < 0) { | ||
743 | dev_err(&data->client->dev, | ||
744 | "Clearing als threshold register failed.\n"); | ||
745 | return false; | ||
746 | } | ||
747 | |||
748 | err = gp2ap020a00f_write_event_threshold(data, | ||
749 | GP2AP020A00F_THRESH_TL, false); | ||
750 | if (err < 0) { | ||
751 | dev_err(&data->client->dev, | ||
752 | "Clearing als threshold register failed.\n"); | ||
753 | return false; | ||
754 | } | ||
755 | |||
756 | /* Change lux mode */ | ||
757 | err = regmap_update_bits(data->regmap, | ||
758 | GP2AP020A00F_OP_REG, | ||
759 | GP2AP020A00F_OP3_MASK, | ||
760 | GP2AP020A00F_OP3_SHUTDOWN); | ||
761 | |||
762 | if (err < 0) { | ||
763 | dev_err(&data->client->dev, | ||
764 | "Shutting down the device failed.\n"); | ||
765 | return false; | ||
766 | } | ||
767 | |||
768 | err = regmap_update_bits(data->regmap, | ||
769 | GP2AP020A00F_ALS_REG, | ||
770 | GP2AP020A00F_RANGE_A_MASK, | ||
771 | new_range); | ||
772 | |||
773 | if (err < 0) { | ||
774 | dev_err(&data->client->dev, | ||
775 | "Adjusting device lux mode failed.\n"); | ||
776 | return false; | ||
777 | } | ||
778 | |||
779 | err = regmap_update_bits(data->regmap, | ||
780 | GP2AP020A00F_OP_REG, | ||
781 | GP2AP020A00F_OP3_MASK, | ||
782 | GP2AP020A00F_OP3_OPERATION); | ||
783 | |||
784 | if (err < 0) { | ||
785 | dev_err(&data->client->dev, | ||
786 | "Powering up the device failed.\n"); | ||
787 | return false; | ||
788 | } | ||
789 | |||
790 | /* Adjust als threshold register values to the new lux mode */ | ||
791 | if (test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags)) { | ||
792 | err = gp2ap020a00f_write_event_threshold(data, | ||
793 | GP2AP020A00F_THRESH_TH, true); | ||
794 | if (err < 0) { | ||
795 | dev_err(&data->client->dev, | ||
796 | "Adjusting als threshold value failed.\n"); | ||
797 | return false; | ||
798 | } | ||
799 | } | ||
800 | |||
801 | if (test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags)) { | ||
802 | err = gp2ap020a00f_write_event_threshold(data, | ||
803 | GP2AP020A00F_THRESH_TL, true); | ||
804 | if (err < 0) { | ||
805 | dev_err(&data->client->dev, | ||
806 | "Adjusting als threshold value failed.\n"); | ||
807 | return false; | ||
808 | } | ||
809 | } | ||
810 | |||
811 | return true; | ||
812 | } | ||
813 | |||
814 | return false; | ||
815 | } | ||
816 | |||
817 | static void gp2ap020a00f_output_to_lux(struct gp2ap020a00f_data *data, | ||
818 | int *output_val) | ||
819 | { | ||
820 | if (test_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags)) | ||
821 | *output_val *= 16; | ||
822 | } | ||
823 | |||
824 | static void gp2ap020a00f_iio_trigger_work(struct irq_work *work) | ||
825 | { | ||
826 | struct gp2ap020a00f_data *data = | ||
827 | container_of(work, struct gp2ap020a00f_data, work); | ||
828 | |||
829 | iio_trigger_poll(data->trig, 0); | ||
830 | } | ||
831 | |||
832 | static irqreturn_t gp2ap020a00f_prox_sensing_handler(int irq, void *data) | ||
833 | { | ||
834 | struct iio_dev *indio_dev = data; | ||
835 | struct gp2ap020a00f_data *priv = iio_priv(indio_dev); | ||
836 | unsigned int op_reg_val; | ||
837 | int ret; | ||
838 | |||
839 | /* Read interrupt flags */ | ||
840 | ret = regmap_read(priv->regmap, GP2AP020A00F_OP_REG, &op_reg_val); | ||
841 | if (ret < 0) | ||
842 | return IRQ_HANDLED; | ||
843 | |||
844 | if (gp2ap020a00f_prox_detect_enabled(priv)) { | ||
845 | if (op_reg_val & GP2AP020A00F_PROX_DETECT) { | ||
846 | iio_push_event(indio_dev, | ||
847 | IIO_UNMOD_EVENT_CODE( | ||
848 | IIO_PROXIMITY, | ||
849 | GP2AP020A00F_SCAN_MODE_PROXIMITY, | ||
850 | IIO_EV_TYPE_ROC, | ||
851 | IIO_EV_DIR_RISING), | ||
852 | iio_get_time_ns()); | ||
853 | } else { | ||
854 | iio_push_event(indio_dev, | ||
855 | IIO_UNMOD_EVENT_CODE( | ||
856 | IIO_PROXIMITY, | ||
857 | GP2AP020A00F_SCAN_MODE_PROXIMITY, | ||
858 | IIO_EV_TYPE_ROC, | ||
859 | IIO_EV_DIR_FALLING), | ||
860 | iio_get_time_ns()); | ||
861 | } | ||
862 | } | ||
863 | |||
864 | return IRQ_HANDLED; | ||
865 | } | ||
866 | |||
867 | static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) | ||
868 | { | ||
869 | struct iio_dev *indio_dev = data; | ||
870 | struct gp2ap020a00f_data *priv = iio_priv(indio_dev); | ||
871 | u8 op_reg_flags, d0_reg_buf[2]; | ||
872 | unsigned int output_val, op_reg_val; | ||
873 | int thresh_val_id, ret; | ||
874 | |||
875 | /* Read interrupt flags */ | ||
876 | ret = regmap_read(priv->regmap, GP2AP020A00F_OP_REG, | ||
877 | &op_reg_val); | ||
878 | if (ret < 0) | ||
879 | goto done; | ||
880 | |||
881 | op_reg_flags = op_reg_val & (GP2AP020A00F_FLAG_A | GP2AP020A00F_FLAG_P | ||
882 | | GP2AP020A00F_PROX_DETECT); | ||
883 | |||
884 | op_reg_val &= (~GP2AP020A00F_FLAG_A & ~GP2AP020A00F_FLAG_P | ||
885 | & ~GP2AP020A00F_PROX_DETECT); | ||
886 | |||
887 | /* Clear interrupt flags (if not in INTTYPE_PULSE mode) */ | ||
888 | if (priv->cur_opmode != GP2AP020A00F_OPMODE_PROX_DETECT) { | ||
889 | ret = regmap_write(priv->regmap, GP2AP020A00F_OP_REG, | ||
890 | op_reg_val); | ||
891 | if (ret < 0) | ||
892 | goto done; | ||
893 | } | ||
894 | |||
895 | if (op_reg_flags & GP2AP020A00F_FLAG_A) { | ||
896 | /* Check D0 register to assess if the lux mode | ||
897 | * transition is required. | ||
898 | */ | ||
899 | ret = regmap_bulk_read(priv->regmap, GP2AP020A00F_D0_L_REG, | ||
900 | d0_reg_buf, 2); | ||
901 | if (ret < 0) | ||
902 | goto done; | ||
903 | |||
904 | output_val = le16_to_cpup((__le16 *)d0_reg_buf); | ||
905 | |||
906 | if (gp2ap020a00f_adjust_lux_mode(priv, output_val)) | ||
907 | goto done; | ||
908 | |||
909 | gp2ap020a00f_output_to_lux(priv, &output_val); | ||
910 | |||
911 | /* | ||
912 | * We need to check output value to distinguish | ||
913 | * between high and low ambient light threshold event. | ||
914 | */ | ||
915 | if (test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &priv->flags)) { | ||
916 | thresh_val_id = | ||
917 | GP2AP020A00F_THRESH_VAL_ID(GP2AP020A00F_TH_L_REG); | ||
918 | if (output_val > priv->thresh_val[thresh_val_id]) | ||
919 | iio_push_event(indio_dev, | ||
920 | IIO_MOD_EVENT_CODE( | ||
921 | IIO_LIGHT, | ||
922 | GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR, | ||
923 | IIO_MOD_LIGHT_CLEAR, | ||
924 | IIO_EV_TYPE_THRESH, | ||
925 | IIO_EV_DIR_RISING), | ||
926 | iio_get_time_ns()); | ||
927 | } | ||
928 | |||
929 | if (test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &priv->flags)) { | ||
930 | thresh_val_id = | ||
931 | GP2AP020A00F_THRESH_VAL_ID(GP2AP020A00F_TL_L_REG); | ||
932 | if (output_val < priv->thresh_val[thresh_val_id]) | ||
933 | iio_push_event(indio_dev, | ||
934 | IIO_MOD_EVENT_CODE( | ||
935 | IIO_LIGHT, | ||
936 | GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR, | ||
937 | IIO_MOD_LIGHT_CLEAR, | ||
938 | IIO_EV_TYPE_THRESH, | ||
939 | IIO_EV_DIR_FALLING), | ||
940 | iio_get_time_ns()); | ||
941 | } | ||
942 | } | ||
943 | |||
944 | if (priv->cur_opmode == GP2AP020A00F_OPMODE_READ_RAW_CLEAR || | ||
945 | priv->cur_opmode == GP2AP020A00F_OPMODE_READ_RAW_IR || | ||
946 | priv->cur_opmode == GP2AP020A00F_OPMODE_READ_RAW_PROXIMITY) { | ||
947 | set_bit(GP2AP020A00F_FLAG_DATA_READY, &priv->flags); | ||
948 | wake_up(&priv->data_ready_queue); | ||
949 | goto done; | ||
950 | } | ||
951 | |||
952 | if (test_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &priv->flags) || | ||
953 | test_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &priv->flags) || | ||
954 | test_bit(GP2AP020A00F_FLAG_PROX_TRIGGER, &priv->flags)) | ||
955 | /* This fires off the trigger. */ | ||
956 | irq_work_queue(&priv->work); | ||
957 | |||
958 | done: | ||
959 | return IRQ_HANDLED; | ||
960 | } | ||
961 | |||
962 | static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data) | ||
963 | { | ||
964 | struct iio_poll_func *pf = data; | ||
965 | struct iio_dev *indio_dev = pf->indio_dev; | ||
966 | struct gp2ap020a00f_data *priv = iio_priv(indio_dev); | ||
967 | size_t d_size = 0; | ||
968 | __le32 light_lux; | ||
969 | int i, out_val, ret; | ||
970 | |||
971 | for_each_set_bit(i, indio_dev->active_scan_mask, | ||
972 | indio_dev->masklength) { | ||
973 | ret = regmap_bulk_read(priv->regmap, | ||
974 | GP2AP020A00F_DATA_REG(i), | ||
975 | &priv->buffer[d_size], 2); | ||
976 | if (ret < 0) | ||
977 | goto done; | ||
978 | |||
979 | if (i == GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR || | ||
980 | i == GP2AP020A00F_SCAN_MODE_LIGHT_IR) { | ||
981 | out_val = le16_to_cpup((__le16 *)&priv->buffer[d_size]); | ||
982 | gp2ap020a00f_output_to_lux(priv, &out_val); | ||
983 | light_lux = cpu_to_le32(out_val); | ||
984 | memcpy(&priv->buffer[d_size], (u8 *)&light_lux, 4); | ||
985 | d_size += 4; | ||
986 | } else { | ||
987 | d_size += 2; | ||
988 | } | ||
989 | } | ||
990 | |||
991 | if (indio_dev->scan_timestamp) { | ||
992 | s64 *timestamp = (s64 *)((u8 *)priv->buffer + | ||
993 | ALIGN(d_size, sizeof(s64))); | ||
994 | *timestamp = pf->timestamp; | ||
995 | } | ||
996 | |||
997 | iio_push_to_buffers(indio_dev, priv->buffer); | ||
998 | done: | ||
999 | iio_trigger_notify_done(indio_dev->trig); | ||
1000 | |||
1001 | return IRQ_HANDLED; | ||
1002 | } | ||
1003 | |||
1004 | static u8 gp2ap020a00f_get_reg_by_event_code(u64 event_code) | ||
1005 | { | ||
1006 | int event_dir = IIO_EVENT_CODE_EXTRACT_DIR(event_code); | ||
1007 | |||
1008 | switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { | ||
1009 | case IIO_PROXIMITY: | ||
1010 | if (event_dir == IIO_EV_DIR_RISING) | ||
1011 | return GP2AP020A00F_PH_L_REG; | ||
1012 | else | ||
1013 | return GP2AP020A00F_PL_L_REG; | ||
1014 | case IIO_LIGHT: | ||
1015 | if (event_dir == IIO_EV_DIR_RISING) | ||
1016 | return GP2AP020A00F_TH_L_REG; | ||
1017 | else | ||
1018 | return GP2AP020A00F_TL_L_REG; | ||
1019 | } | ||
1020 | |||
1021 | return -EINVAL; | ||
1022 | } | ||
1023 | |||
1024 | static int gp2ap020a00f_write_event_val(struct iio_dev *indio_dev, | ||
1025 | u64 event_code, int val) | ||
1026 | { | ||
1027 | struct gp2ap020a00f_data *data = iio_priv(indio_dev); | ||
1028 | bool event_en = false; | ||
1029 | u8 thresh_val_id; | ||
1030 | u8 thresh_reg_l; | ||
1031 | int err = 0; | ||
1032 | |||
1033 | mutex_lock(&data->lock); | ||
1034 | |||
1035 | thresh_reg_l = gp2ap020a00f_get_reg_by_event_code(event_code); | ||
1036 | thresh_val_id = GP2AP020A00F_THRESH_VAL_ID(thresh_reg_l); | ||
1037 | |||
1038 | if (thresh_val_id > GP2AP020A00F_THRESH_PH) { | ||
1039 | err = -EINVAL; | ||
1040 | goto error_unlock; | ||
1041 | } | ||
1042 | |||
1043 | switch (thresh_reg_l) { | ||
1044 | case GP2AP020A00F_TH_L_REG: | ||
1045 | event_en = test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, | ||
1046 | &data->flags); | ||
1047 | break; | ||
1048 | case GP2AP020A00F_TL_L_REG: | ||
1049 | event_en = test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, | ||
1050 | &data->flags); | ||
1051 | break; | ||
1052 | case GP2AP020A00F_PH_L_REG: | ||
1053 | if (val == 0) { | ||
1054 | err = -EINVAL; | ||
1055 | goto error_unlock; | ||
1056 | } | ||
1057 | event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, | ||
1058 | &data->flags); | ||
1059 | break; | ||
1060 | case GP2AP020A00F_PL_L_REG: | ||
1061 | if (val == 0) { | ||
1062 | err = -EINVAL; | ||
1063 | goto error_unlock; | ||
1064 | } | ||
1065 | event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, | ||
1066 | &data->flags); | ||
1067 | break; | ||
1068 | } | ||
1069 | |||
1070 | data->thresh_val[thresh_val_id] = val; | ||
1071 | err = gp2ap020a00f_write_event_threshold(data, thresh_val_id, | ||
1072 | event_en); | ||
1073 | error_unlock: | ||
1074 | mutex_unlock(&data->lock); | ||
1075 | |||
1076 | return err; | ||
1077 | } | ||
1078 | |||
1079 | static int gp2ap020a00f_read_event_val(struct iio_dev *indio_dev, | ||
1080 | u64 event_code, int *val) | ||
1081 | { | ||
1082 | struct gp2ap020a00f_data *data = iio_priv(indio_dev); | ||
1083 | u8 thresh_reg_l; | ||
1084 | int err = 0; | ||
1085 | |||
1086 | mutex_lock(&data->lock); | ||
1087 | |||
1088 | thresh_reg_l = gp2ap020a00f_get_reg_by_event_code(event_code); | ||
1089 | |||
1090 | if (thresh_reg_l > GP2AP020A00F_PH_L_REG) { | ||
1091 | err = -EINVAL; | ||
1092 | goto error_unlock; | ||
1093 | } | ||
1094 | |||
1095 | *val = data->thresh_val[GP2AP020A00F_THRESH_VAL_ID(thresh_reg_l)]; | ||
1096 | |||
1097 | error_unlock: | ||
1098 | mutex_unlock(&data->lock); | ||
1099 | |||
1100 | return err; | ||
1101 | } | ||
1102 | |||
1103 | static int gp2ap020a00f_write_prox_event_config(struct iio_dev *indio_dev, | ||
1104 | u64 event_code, int state) | ||
1105 | { | ||
1106 | struct gp2ap020a00f_data *data = iio_priv(indio_dev); | ||
1107 | enum gp2ap020a00f_cmd cmd_high_ev, cmd_low_ev; | ||
1108 | int err; | ||
1109 | |||
1110 | cmd_high_ev = state ? GP2AP020A00F_CMD_PROX_HIGH_EV_EN : | ||
1111 | GP2AP020A00F_CMD_PROX_HIGH_EV_DIS; | ||
1112 | cmd_low_ev = state ? GP2AP020A00F_CMD_PROX_LOW_EV_EN : | ||
1113 | GP2AP020A00F_CMD_PROX_LOW_EV_DIS; | ||
1114 | |||
1115 | /* | ||
1116 | * In order to enable proximity detection feature in the device | ||
1117 | * both high and low threshold registers have to be written | ||
1118 | * with different values, greater than zero. | ||
1119 | */ | ||
1120 | if (state) { | ||
1121 | if (data->thresh_val[GP2AP020A00F_THRESH_PL] == 0) | ||
1122 | return -EINVAL; | ||
1123 | |||
1124 | if (data->thresh_val[GP2AP020A00F_THRESH_PH] == 0) | ||
1125 | return -EINVAL; | ||
1126 | } | ||
1127 | |||
1128 | err = gp2ap020a00f_exec_cmd(data, cmd_high_ev); | ||
1129 | if (err < 0) | ||
1130 | return err; | ||
1131 | |||
1132 | err = gp2ap020a00f_exec_cmd(data, cmd_low_ev); | ||
1133 | if (err < 0) | ||
1134 | return err; | ||
1135 | |||
1136 | free_irq(data->client->irq, indio_dev); | ||
1137 | |||
1138 | if (state) | ||
1139 | err = request_threaded_irq(data->client->irq, NULL, | ||
1140 | &gp2ap020a00f_prox_sensing_handler, | ||
1141 | IRQF_TRIGGER_RISING | | ||
1142 | IRQF_TRIGGER_FALLING | | ||
1143 | IRQF_ONESHOT, | ||
1144 | "gp2ap020a00f_prox_sensing", | ||
1145 | indio_dev); | ||
1146 | else { | ||
1147 | err = request_threaded_irq(data->client->irq, NULL, | ||
1148 | &gp2ap020a00f_thresh_event_handler, | ||
1149 | IRQF_TRIGGER_FALLING | | ||
1150 | IRQF_ONESHOT, | ||
1151 | "gp2ap020a00f_thresh_event", | ||
1152 | indio_dev); | ||
1153 | } | ||
1154 | |||
1155 | return err; | ||
1156 | } | ||
1157 | |||
1158 | static int gp2ap020a00f_write_event_config(struct iio_dev *indio_dev, | ||
1159 | u64 event_code, int state) | ||
1160 | { | ||
1161 | struct gp2ap020a00f_data *data = iio_priv(indio_dev); | ||
1162 | enum gp2ap020a00f_cmd cmd; | ||
1163 | int err; | ||
1164 | |||
1165 | mutex_lock(&data->lock); | ||
1166 | |||
1167 | switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { | ||
1168 | case IIO_PROXIMITY: | ||
1169 | err = gp2ap020a00f_write_prox_event_config(indio_dev, | ||
1170 | event_code, state); | ||
1171 | break; | ||
1172 | case IIO_LIGHT: | ||
1173 | if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) | ||
1174 | == IIO_EV_DIR_RISING) { | ||
1175 | cmd = state ? GP2AP020A00F_CMD_ALS_HIGH_EV_EN : | ||
1176 | GP2AP020A00F_CMD_ALS_HIGH_EV_DIS; | ||
1177 | err = gp2ap020a00f_exec_cmd(data, cmd); | ||
1178 | } else { | ||
1179 | cmd = state ? GP2AP020A00F_CMD_ALS_LOW_EV_EN : | ||
1180 | GP2AP020A00F_CMD_ALS_LOW_EV_DIS; | ||
1181 | err = gp2ap020a00f_exec_cmd(data, cmd); | ||
1182 | } | ||
1183 | break; | ||
1184 | default: | ||
1185 | err = -EINVAL; | ||
1186 | } | ||
1187 | |||
1188 | mutex_unlock(&data->lock); | ||
1189 | |||
1190 | return err; | ||
1191 | } | ||
1192 | |||
1193 | static int gp2ap020a00f_read_event_config(struct iio_dev *indio_dev, | ||
1194 | u64 event_code) | ||
1195 | { | ||
1196 | struct gp2ap020a00f_data *data = iio_priv(indio_dev); | ||
1197 | int event_en = 0; | ||
1198 | |||
1199 | mutex_lock(&data->lock); | ||
1200 | |||
1201 | switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { | ||
1202 | case IIO_PROXIMITY: | ||
1203 | if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) | ||
1204 | == IIO_EV_DIR_RISING) | ||
1205 | event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, | ||
1206 | &data->flags); | ||
1207 | else | ||
1208 | event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, | ||
1209 | &data->flags); | ||
1210 | break; | ||
1211 | case IIO_LIGHT: | ||
1212 | if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) | ||
1213 | == IIO_EV_DIR_RISING) | ||
1214 | event_en = test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, | ||
1215 | &data->flags); | ||
1216 | else | ||
1217 | event_en = test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, | ||
1218 | &data->flags); | ||
1219 | break; | ||
1220 | } | ||
1221 | |||
1222 | mutex_unlock(&data->lock); | ||
1223 | |||
1224 | return event_en; | ||
1225 | } | ||
1226 | |||
1227 | static int gp2ap020a00f_read_channel(struct gp2ap020a00f_data *data, | ||
1228 | struct iio_chan_spec const *chan, int *val) | ||
1229 | { | ||
1230 | enum gp2ap020a00f_cmd cmd; | ||
1231 | int err; | ||
1232 | |||
1233 | switch (chan->scan_index) { | ||
1234 | case GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR: | ||
1235 | cmd = GP2AP020A00F_CMD_READ_RAW_CLEAR; | ||
1236 | break; | ||
1237 | case GP2AP020A00F_SCAN_MODE_LIGHT_IR: | ||
1238 | cmd = GP2AP020A00F_CMD_READ_RAW_IR; | ||
1239 | break; | ||
1240 | case GP2AP020A00F_SCAN_MODE_PROXIMITY: | ||
1241 | cmd = GP2AP020A00F_CMD_READ_RAW_PROXIMITY; | ||
1242 | break; | ||
1243 | default: | ||
1244 | return -EINVAL; | ||
1245 | } | ||
1246 | |||
1247 | err = gp2ap020a00f_exec_cmd(data, cmd); | ||
1248 | if (err < 0) { | ||
1249 | dev_err(&data->client->dev, | ||
1250 | "gp2ap020a00f_exec_cmd failed\n"); | ||
1251 | goto error_ret; | ||
1252 | } | ||
1253 | |||
1254 | err = gp2ap020a00f_read_output(data, chan->address, val); | ||
1255 | if (err < 0) | ||
1256 | dev_err(&data->client->dev, | ||
1257 | "gp2ap020a00f_read_output failed\n"); | ||
1258 | |||
1259 | err = gp2ap020a00f_set_operation_mode(data, | ||
1260 | GP2AP020A00F_OPMODE_SHUTDOWN); | ||
1261 | if (err < 0) | ||
1262 | dev_err(&data->client->dev, | ||
1263 | "Failed to shut down the device.\n"); | ||
1264 | |||
1265 | if (cmd == GP2AP020A00F_CMD_READ_RAW_CLEAR || | ||
1266 | cmd == GP2AP020A00F_CMD_READ_RAW_IR) | ||
1267 | gp2ap020a00f_output_to_lux(data, val); | ||
1268 | |||
1269 | error_ret: | ||
1270 | return err; | ||
1271 | } | ||
1272 | |||
1273 | static int gp2ap020a00f_read_raw(struct iio_dev *indio_dev, | ||
1274 | struct iio_chan_spec const *chan, | ||
1275 | int *val, int *val2, | ||
1276 | long mask) | ||
1277 | { | ||
1278 | struct gp2ap020a00f_data *data = iio_priv(indio_dev); | ||
1279 | int err = -EINVAL; | ||
1280 | |||
1281 | mutex_lock(&data->lock); | ||
1282 | |||
1283 | switch (mask) { | ||
1284 | case IIO_CHAN_INFO_RAW: | ||
1285 | if (iio_buffer_enabled(indio_dev)) { | ||
1286 | err = -EBUSY; | ||
1287 | goto error_unlock; | ||
1288 | } | ||
1289 | |||
1290 | err = gp2ap020a00f_read_channel(data, chan, val); | ||
1291 | break; | ||
1292 | } | ||
1293 | |||
1294 | error_unlock: | ||
1295 | mutex_unlock(&data->lock); | ||
1296 | |||
1297 | return err < 0 ? err : IIO_VAL_INT; | ||
1298 | } | ||
1299 | |||
1300 | static const struct iio_chan_spec gp2ap020a00f_channels[] = { | ||
1301 | { | ||
1302 | .type = IIO_LIGHT, | ||
1303 | .channel2 = IIO_MOD_LIGHT_CLEAR, | ||
1304 | .modified = 1, | ||
1305 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
1306 | .scan_type = { | ||
1307 | .sign = 'u', | ||
1308 | .realbits = 24, | ||
1309 | .shift = 0, | ||
1310 | .storagebits = 32, | ||
1311 | .endianness = IIO_LE, | ||
1312 | }, | ||
1313 | .scan_index = GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR, | ||
1314 | .address = GP2AP020A00F_D0_L_REG, | ||
1315 | .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, | ||
1316 | IIO_EV_DIR_RISING) | | ||
1317 | IIO_EV_BIT(IIO_EV_TYPE_THRESH, | ||
1318 | IIO_EV_DIR_FALLING), | ||
1319 | }, | ||
1320 | { | ||
1321 | .type = IIO_LIGHT, | ||
1322 | .channel2 = IIO_MOD_LIGHT_IR, | ||
1323 | .modified = 1, | ||
1324 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
1325 | .scan_type = { | ||
1326 | .sign = 'u', | ||
1327 | .realbits = 24, | ||
1328 | .shift = 0, | ||
1329 | .storagebits = 32, | ||
1330 | .endianness = IIO_LE, | ||
1331 | }, | ||
1332 | .scan_index = GP2AP020A00F_SCAN_MODE_LIGHT_IR, | ||
1333 | .address = GP2AP020A00F_D1_L_REG, | ||
1334 | }, | ||
1335 | { | ||
1336 | .type = IIO_PROXIMITY, | ||
1337 | .modified = 0, | ||
1338 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), | ||
1339 | .scan_type = { | ||
1340 | .sign = 'u', | ||
1341 | .realbits = 16, | ||
1342 | .shift = 0, | ||
1343 | .storagebits = 16, | ||
1344 | .endianness = IIO_LE, | ||
1345 | }, | ||
1346 | .scan_index = GP2AP020A00F_SCAN_MODE_PROXIMITY, | ||
1347 | .address = GP2AP020A00F_D2_L_REG, | ||
1348 | .event_mask = IIO_EV_BIT(IIO_EV_TYPE_ROC, | ||
1349 | IIO_EV_DIR_RISING) | | ||
1350 | IIO_EV_BIT(IIO_EV_TYPE_ROC, | ||
1351 | IIO_EV_DIR_FALLING), | ||
1352 | }, | ||
1353 | IIO_CHAN_SOFT_TIMESTAMP(GP2AP020A00F_CHAN_TIMESTAMP), | ||
1354 | }; | ||
1355 | |||
1356 | static const struct iio_info gp2ap020a00f_info = { | ||
1357 | .read_raw = &gp2ap020a00f_read_raw, | ||
1358 | .read_event_value = &gp2ap020a00f_read_event_val, | ||
1359 | .read_event_config = &gp2ap020a00f_read_event_config, | ||
1360 | .write_event_value = &gp2ap020a00f_write_event_val, | ||
1361 | .write_event_config = &gp2ap020a00f_write_event_config, | ||
1362 | .driver_module = THIS_MODULE, | ||
1363 | }; | ||
1364 | |||
1365 | static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) | ||
1366 | { | ||
1367 | struct gp2ap020a00f_data *data = iio_priv(indio_dev); | ||
1368 | int i, err = 0; | ||
1369 | |||
1370 | mutex_lock(&data->lock); | ||
1371 | |||
1372 | /* | ||
1373 | * Enable triggers according to the scan_mask. Enabling either | ||
1374 | * LIGHT_CLEAR or LIGHT_IR scan mode results in enabling ALS | ||
1375 | * module in the device, which generates samples in both D0 (clear) | ||
1376 | * and D1 (ir) registers. As the two registers are bound to the | ||
1377 | * two separate IIO channels they are treated in the driver logic | ||
1378 | * as if they were controlled independently. | ||
1379 | */ | ||
1380 | for_each_set_bit(i, indio_dev->active_scan_mask, | ||
1381 | indio_dev->masklength) { | ||
1382 | switch (i) { | ||
1383 | case GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR: | ||
1384 | err = gp2ap020a00f_exec_cmd(data, | ||
1385 | GP2AP020A00F_CMD_TRIGGER_CLEAR_EN); | ||
1386 | break; | ||
1387 | case GP2AP020A00F_SCAN_MODE_LIGHT_IR: | ||
1388 | err = gp2ap020a00f_exec_cmd(data, | ||
1389 | GP2AP020A00F_CMD_TRIGGER_IR_EN); | ||
1390 | break; | ||
1391 | case GP2AP020A00F_SCAN_MODE_PROXIMITY: | ||
1392 | err = gp2ap020a00f_exec_cmd(data, | ||
1393 | GP2AP020A00F_CMD_TRIGGER_PROX_EN); | ||
1394 | break; | ||
1395 | } | ||
1396 | } | ||
1397 | |||
1398 | if (err < 0) | ||
1399 | goto error_unlock; | ||
1400 | |||
1401 | data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); | ||
1402 | if (!data->buffer) { | ||
1403 | err = -ENOMEM; | ||
1404 | goto error_unlock; | ||
1405 | } | ||
1406 | |||
1407 | err = iio_triggered_buffer_postenable(indio_dev); | ||
1408 | |||
1409 | error_unlock: | ||
1410 | mutex_unlock(&data->lock); | ||
1411 | |||
1412 | return err; | ||
1413 | } | ||
1414 | |||
1415 | static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) | ||
1416 | { | ||
1417 | struct gp2ap020a00f_data *data = iio_priv(indio_dev); | ||
1418 | int i, err; | ||
1419 | |||
1420 | mutex_lock(&data->lock); | ||
1421 | |||
1422 | err = iio_triggered_buffer_predisable(indio_dev); | ||
1423 | if (err < 0) | ||
1424 | goto error_unlock; | ||
1425 | |||
1426 | for_each_set_bit(i, indio_dev->active_scan_mask, | ||
1427 | indio_dev->masklength) { | ||
1428 | switch (i) { | ||
1429 | case GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR: | ||
1430 | err = gp2ap020a00f_exec_cmd(data, | ||
1431 | GP2AP020A00F_CMD_TRIGGER_CLEAR_DIS); | ||
1432 | break; | ||
1433 | case GP2AP020A00F_SCAN_MODE_LIGHT_IR: | ||
1434 | err = gp2ap020a00f_exec_cmd(data, | ||
1435 | GP2AP020A00F_CMD_TRIGGER_IR_DIS); | ||
1436 | break; | ||
1437 | case GP2AP020A00F_SCAN_MODE_PROXIMITY: | ||
1438 | err = gp2ap020a00f_exec_cmd(data, | ||
1439 | GP2AP020A00F_CMD_TRIGGER_PROX_DIS); | ||
1440 | break; | ||
1441 | } | ||
1442 | } | ||
1443 | |||
1444 | if (err == 0) | ||
1445 | kfree(data->buffer); | ||
1446 | |||
1447 | error_unlock: | ||
1448 | mutex_unlock(&data->lock); | ||
1449 | |||
1450 | return err; | ||
1451 | } | ||
1452 | |||
1453 | static const struct iio_buffer_setup_ops gp2ap020a00f_buffer_setup_ops = { | ||
1454 | .preenable = &iio_sw_buffer_preenable, | ||
1455 | .postenable = &gp2ap020a00f_buffer_postenable, | ||
1456 | .predisable = &gp2ap020a00f_buffer_predisable, | ||
1457 | }; | ||
1458 | |||
1459 | static const struct iio_trigger_ops gp2ap020a00f_trigger_ops = { | ||
1460 | .owner = THIS_MODULE, | ||
1461 | }; | ||
1462 | |||
1463 | static int gp2ap020a00f_probe(struct i2c_client *client, | ||
1464 | const struct i2c_device_id *id) | ||
1465 | { | ||
1466 | struct gp2ap020a00f_data *data; | ||
1467 | struct iio_dev *indio_dev; | ||
1468 | struct regmap *regmap; | ||
1469 | int err; | ||
1470 | |||
1471 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | ||
1472 | if (!indio_dev) | ||
1473 | return -ENOMEM; | ||
1474 | |||
1475 | data = iio_priv(indio_dev); | ||
1476 | |||
1477 | data->vled_reg = devm_regulator_get(&client->dev, "vled"); | ||
1478 | if (IS_ERR(data->vled_reg)) | ||
1479 | return PTR_ERR(data->vled_reg); | ||
1480 | |||
1481 | err = regulator_enable(data->vled_reg); | ||
1482 | if (err) | ||
1483 | return err; | ||
1484 | |||
1485 | regmap = devm_regmap_init_i2c(client, &gp2ap020a00f_regmap_config); | ||
1486 | if (IS_ERR(regmap)) { | ||
1487 | dev_err(&client->dev, "Regmap initialization failed.\n"); | ||
1488 | err = PTR_ERR(regmap); | ||
1489 | goto error_regulator_disable; | ||
1490 | } | ||
1491 | |||
1492 | /* Initialize device registers */ | ||
1493 | err = regmap_bulk_write(regmap, GP2AP020A00F_OP_REG, | ||
1494 | gp2ap020a00f_reg_init_tab, | ||
1495 | ARRAY_SIZE(gp2ap020a00f_reg_init_tab)); | ||
1496 | |||
1497 | if (err < 0) { | ||
1498 | dev_err(&client->dev, "Device initialization failed.\n"); | ||
1499 | goto error_regulator_disable; | ||
1500 | } | ||
1501 | |||
1502 | i2c_set_clientdata(client, indio_dev); | ||
1503 | |||
1504 | data->client = client; | ||
1505 | data->cur_opmode = GP2AP020A00F_OPMODE_SHUTDOWN; | ||
1506 | data->regmap = regmap; | ||
1507 | init_waitqueue_head(&data->data_ready_queue); | ||
1508 | |||
1509 | mutex_init(&data->lock); | ||
1510 | indio_dev->dev.parent = &client->dev; | ||
1511 | indio_dev->channels = gp2ap020a00f_channels; | ||
1512 | indio_dev->num_channels = ARRAY_SIZE(gp2ap020a00f_channels); | ||
1513 | indio_dev->info = &gp2ap020a00f_info; | ||
1514 | indio_dev->name = id->name; | ||
1515 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
1516 | |||
1517 | /* Allocate buffer */ | ||
1518 | err = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, | ||
1519 | &gp2ap020a00f_trigger_handler, &gp2ap020a00f_buffer_setup_ops); | ||
1520 | if (err < 0) | ||
1521 | goto error_regulator_disable; | ||
1522 | |||
1523 | /* Allocate trigger */ | ||
1524 | data->trig = devm_iio_trigger_alloc(&client->dev, "%s-trigger", | ||
1525 | indio_dev->name); | ||
1526 | if (data->trig == NULL) { | ||
1527 | err = -ENOMEM; | ||
1528 | dev_err(&indio_dev->dev, "Failed to allocate iio trigger.\n"); | ||
1529 | goto error_uninit_buffer; | ||
1530 | } | ||
1531 | |||
1532 | /* This needs to be requested here for read_raw calls to work. */ | ||
1533 | err = request_threaded_irq(client->irq, NULL, | ||
1534 | &gp2ap020a00f_thresh_event_handler, | ||
1535 | IRQF_TRIGGER_FALLING | | ||
1536 | IRQF_ONESHOT, | ||
1537 | "gp2ap020a00f_als_event", | ||
1538 | indio_dev); | ||
1539 | if (err < 0) { | ||
1540 | dev_err(&client->dev, "Irq request failed.\n"); | ||
1541 | goto error_uninit_buffer; | ||
1542 | } | ||
1543 | |||
1544 | data->trig->ops = &gp2ap020a00f_trigger_ops; | ||
1545 | data->trig->dev.parent = &data->client->dev; | ||
1546 | |||
1547 | init_irq_work(&data->work, gp2ap020a00f_iio_trigger_work); | ||
1548 | |||
1549 | err = iio_trigger_register(data->trig); | ||
1550 | if (err < 0) { | ||
1551 | dev_err(&client->dev, "Failed to register iio trigger.\n"); | ||
1552 | goto error_free_irq; | ||
1553 | } | ||
1554 | |||
1555 | err = iio_device_register(indio_dev); | ||
1556 | if (err < 0) | ||
1557 | goto error_trigger_unregister; | ||
1558 | |||
1559 | return 0; | ||
1560 | |||
1561 | error_trigger_unregister: | ||
1562 | iio_trigger_unregister(data->trig); | ||
1563 | error_free_irq: | ||
1564 | free_irq(client->irq, indio_dev); | ||
1565 | error_uninit_buffer: | ||
1566 | iio_triggered_buffer_cleanup(indio_dev); | ||
1567 | error_regulator_disable: | ||
1568 | regulator_disable(data->vled_reg); | ||
1569 | |||
1570 | return err; | ||
1571 | } | ||
1572 | |||
1573 | static int gp2ap020a00f_remove(struct i2c_client *client) | ||
1574 | { | ||
1575 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | ||
1576 | struct gp2ap020a00f_data *data = iio_priv(indio_dev); | ||
1577 | int err; | ||
1578 | |||
1579 | err = gp2ap020a00f_set_operation_mode(data, | ||
1580 | GP2AP020A00F_OPMODE_SHUTDOWN); | ||
1581 | if (err < 0) | ||
1582 | dev_err(&indio_dev->dev, "Failed to power off the device.\n"); | ||
1583 | |||
1584 | iio_device_unregister(indio_dev); | ||
1585 | iio_trigger_unregister(data->trig); | ||
1586 | free_irq(client->irq, indio_dev); | ||
1587 | iio_triggered_buffer_cleanup(indio_dev); | ||
1588 | regulator_disable(data->vled_reg); | ||
1589 | |||
1590 | return 0; | ||
1591 | } | ||
1592 | |||
1593 | static const struct i2c_device_id gp2ap020a00f_id[] = { | ||
1594 | { GP2A_I2C_NAME, 0 }, | ||
1595 | { } | ||
1596 | }; | ||
1597 | |||
1598 | MODULE_DEVICE_TABLE(i2c, gp2ap020a00f_id); | ||
1599 | |||
1600 | #ifdef CONFIG_OF | ||
1601 | static const struct of_device_id gp2ap020a00f_of_match[] = { | ||
1602 | { .compatible = "sharp,gp2ap020a00f" }, | ||
1603 | { } | ||
1604 | }; | ||
1605 | #endif | ||
1606 | |||
1607 | static struct i2c_driver gp2ap020a00f_driver = { | ||
1608 | .driver = { | ||
1609 | .name = GP2A_I2C_NAME, | ||
1610 | .of_match_table = of_match_ptr(gp2ap020a00f_of_match), | ||
1611 | .owner = THIS_MODULE, | ||
1612 | }, | ||
1613 | .probe = gp2ap020a00f_probe, | ||
1614 | .remove = gp2ap020a00f_remove, | ||
1615 | .id_table = gp2ap020a00f_id, | ||
1616 | }; | ||
1617 | |||
1618 | module_i2c_driver(gp2ap020a00f_driver); | ||
1619 | |||
1620 | MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); | ||
1621 | MODULE_DESCRIPTION("Sharp GP2AP020A00F Proximity/ALS sensor driver"); | ||
1622 | MODULE_LICENSE("GPL v2"); | ||