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