162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015, 2018 662306a36Sopenharmony_ci * Author: Matt Ranostay <matt.ranostay@konsulko.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * TODO: gesture + proximity calib offsets 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/acpi.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/mutex.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/irq.h> 1962306a36Sopenharmony_ci#include <linux/i2c.h> 2062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2162306a36Sopenharmony_ci#include <linux/regmap.h> 2262306a36Sopenharmony_ci#include <linux/iio/iio.h> 2362306a36Sopenharmony_ci#include <linux/iio/buffer.h> 2462306a36Sopenharmony_ci#include <linux/iio/events.h> 2562306a36Sopenharmony_ci#include <linux/iio/kfifo_buf.h> 2662306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define APDS9960_REGMAP_NAME "apds9960_regmap" 2962306a36Sopenharmony_ci#define APDS9960_DRV_NAME "apds9960" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define APDS9960_REG_RAM_START 0x00 3262306a36Sopenharmony_ci#define APDS9960_REG_RAM_END 0x7f 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define APDS9960_REG_ENABLE 0x80 3562306a36Sopenharmony_ci#define APDS9960_REG_ATIME 0x81 3662306a36Sopenharmony_ci#define APDS9960_REG_WTIME 0x83 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define APDS9960_REG_AILTL 0x84 3962306a36Sopenharmony_ci#define APDS9960_REG_AILTH 0x85 4062306a36Sopenharmony_ci#define APDS9960_REG_AIHTL 0x86 4162306a36Sopenharmony_ci#define APDS9960_REG_AIHTH 0x87 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define APDS9960_REG_PILT 0x89 4462306a36Sopenharmony_ci#define APDS9960_REG_PIHT 0x8b 4562306a36Sopenharmony_ci#define APDS9960_REG_PERS 0x8c 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define APDS9960_REG_CONFIG_1 0x8d 4862306a36Sopenharmony_ci#define APDS9960_REG_PPULSE 0x8e 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define APDS9960_REG_CONTROL 0x8f 5162306a36Sopenharmony_ci#define APDS9960_REG_CONTROL_AGAIN_MASK 0x03 5262306a36Sopenharmony_ci#define APDS9960_REG_CONTROL_PGAIN_MASK 0x0c 5362306a36Sopenharmony_ci#define APDS9960_REG_CONTROL_AGAIN_MASK_SHIFT 0 5462306a36Sopenharmony_ci#define APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT 2 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define APDS9960_REG_CONFIG_2 0x90 5762306a36Sopenharmony_ci#define APDS9960_REG_ID 0x92 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define APDS9960_REG_STATUS 0x93 6062306a36Sopenharmony_ci#define APDS9960_REG_STATUS_PS_INT BIT(5) 6162306a36Sopenharmony_ci#define APDS9960_REG_STATUS_ALS_INT BIT(4) 6262306a36Sopenharmony_ci#define APDS9960_REG_STATUS_GINT BIT(2) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define APDS9960_REG_PDATA 0x9c 6562306a36Sopenharmony_ci#define APDS9960_REG_POFFSET_UR 0x9d 6662306a36Sopenharmony_ci#define APDS9960_REG_POFFSET_DL 0x9e 6762306a36Sopenharmony_ci#define APDS9960_REG_CONFIG_3 0x9f 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define APDS9960_REG_GPENTH 0xa0 7062306a36Sopenharmony_ci#define APDS9960_REG_GEXTH 0xa1 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define APDS9960_REG_GCONF_1 0xa2 7362306a36Sopenharmony_ci#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK 0xc0 7462306a36Sopenharmony_ci#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT 6 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define APDS9960_REG_GCONF_2 0xa3 7762306a36Sopenharmony_ci#define APDS9960_REG_GCONF_2_GGAIN_MASK 0x60 7862306a36Sopenharmony_ci#define APDS9960_REG_GCONF_2_GGAIN_MASK_SHIFT 5 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define APDS9960_REG_GOFFSET_U 0xa4 8162306a36Sopenharmony_ci#define APDS9960_REG_GOFFSET_D 0xa5 8262306a36Sopenharmony_ci#define APDS9960_REG_GPULSE 0xa6 8362306a36Sopenharmony_ci#define APDS9960_REG_GOFFSET_L 0xa7 8462306a36Sopenharmony_ci#define APDS9960_REG_GOFFSET_R 0xa9 8562306a36Sopenharmony_ci#define APDS9960_REG_GCONF_3 0xaa 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define APDS9960_REG_GCONF_4 0xab 8862306a36Sopenharmony_ci#define APDS9960_REG_GFLVL 0xae 8962306a36Sopenharmony_ci#define APDS9960_REG_GSTATUS 0xaf 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define APDS9960_REG_IFORCE 0xe4 9262306a36Sopenharmony_ci#define APDS9960_REG_PICLEAR 0xe5 9362306a36Sopenharmony_ci#define APDS9960_REG_CICLEAR 0xe6 9462306a36Sopenharmony_ci#define APDS9960_REG_AICLEAR 0xe7 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define APDS9960_DEFAULT_PERS 0x33 9762306a36Sopenharmony_ci#define APDS9960_DEFAULT_GPENTH 0x50 9862306a36Sopenharmony_ci#define APDS9960_DEFAULT_GEXTH 0x40 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define APDS9960_MAX_PXS_THRES_VAL 255 10162306a36Sopenharmony_ci#define APDS9960_MAX_ALS_THRES_VAL 0xffff 10262306a36Sopenharmony_ci#define APDS9960_MAX_INT_TIME_IN_US 1000000 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cienum apds9960_als_channel_idx { 10562306a36Sopenharmony_ci IDX_ALS_CLEAR, IDX_ALS_RED, IDX_ALS_GREEN, IDX_ALS_BLUE, 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define APDS9960_REG_ALS_BASE 0x94 10962306a36Sopenharmony_ci#define APDS9960_REG_ALS_CHANNEL(_colour) \ 11062306a36Sopenharmony_ci (APDS9960_REG_ALS_BASE + (IDX_ALS_##_colour * 2)) 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cienum apds9960_gesture_channel_idx { 11362306a36Sopenharmony_ci IDX_DIR_UP, IDX_DIR_DOWN, IDX_DIR_LEFT, IDX_DIR_RIGHT, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define APDS9960_REG_GFIFO_BASE 0xfc 11762306a36Sopenharmony_ci#define APDS9960_REG_GFIFO_DIR(_dir) \ 11862306a36Sopenharmony_ci (APDS9960_REG_GFIFO_BASE + IDX_DIR_##_dir) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct apds9960_data { 12162306a36Sopenharmony_ci struct i2c_client *client; 12262306a36Sopenharmony_ci struct iio_dev *indio_dev; 12362306a36Sopenharmony_ci struct mutex lock; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* regmap fields */ 12662306a36Sopenharmony_ci struct regmap *regmap; 12762306a36Sopenharmony_ci struct regmap_field *reg_int_als; 12862306a36Sopenharmony_ci struct regmap_field *reg_int_ges; 12962306a36Sopenharmony_ci struct regmap_field *reg_int_pxs; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci struct regmap_field *reg_enable_als; 13262306a36Sopenharmony_ci struct regmap_field *reg_enable_ges; 13362306a36Sopenharmony_ci struct regmap_field *reg_enable_pxs; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* state */ 13662306a36Sopenharmony_ci int als_int; 13762306a36Sopenharmony_ci int pxs_int; 13862306a36Sopenharmony_ci int gesture_mode_running; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* gain values */ 14162306a36Sopenharmony_ci int als_gain; 14262306a36Sopenharmony_ci int pxs_gain; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* integration time value in us */ 14562306a36Sopenharmony_ci int als_adc_int_us; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* gesture buffer */ 14862306a36Sopenharmony_ci u8 buffer[4]; /* 4 8-bit channels */ 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic const struct reg_default apds9960_reg_defaults[] = { 15262306a36Sopenharmony_ci /* Default ALS integration time = 2.48ms */ 15362306a36Sopenharmony_ci { APDS9960_REG_ATIME, 0xff }, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic const struct regmap_range apds9960_volatile_ranges[] = { 15762306a36Sopenharmony_ci regmap_reg_range(APDS9960_REG_STATUS, 15862306a36Sopenharmony_ci APDS9960_REG_PDATA), 15962306a36Sopenharmony_ci regmap_reg_range(APDS9960_REG_GFLVL, 16062306a36Sopenharmony_ci APDS9960_REG_GSTATUS), 16162306a36Sopenharmony_ci regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP), 16262306a36Sopenharmony_ci APDS9960_REG_GFIFO_DIR(RIGHT)), 16362306a36Sopenharmony_ci regmap_reg_range(APDS9960_REG_IFORCE, 16462306a36Sopenharmony_ci APDS9960_REG_AICLEAR), 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic const struct regmap_access_table apds9960_volatile_table = { 16862306a36Sopenharmony_ci .yes_ranges = apds9960_volatile_ranges, 16962306a36Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(apds9960_volatile_ranges), 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic const struct regmap_range apds9960_precious_ranges[] = { 17362306a36Sopenharmony_ci regmap_reg_range(APDS9960_REG_RAM_START, APDS9960_REG_RAM_END), 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic const struct regmap_access_table apds9960_precious_table = { 17762306a36Sopenharmony_ci .yes_ranges = apds9960_precious_ranges, 17862306a36Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(apds9960_precious_ranges), 17962306a36Sopenharmony_ci}; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic const struct regmap_range apds9960_readable_ranges[] = { 18262306a36Sopenharmony_ci regmap_reg_range(APDS9960_REG_ENABLE, 18362306a36Sopenharmony_ci APDS9960_REG_GSTATUS), 18462306a36Sopenharmony_ci regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP), 18562306a36Sopenharmony_ci APDS9960_REG_GFIFO_DIR(RIGHT)), 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic const struct regmap_access_table apds9960_readable_table = { 18962306a36Sopenharmony_ci .yes_ranges = apds9960_readable_ranges, 19062306a36Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(apds9960_readable_ranges), 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic const struct regmap_range apds9960_writeable_ranges[] = { 19462306a36Sopenharmony_ci regmap_reg_range(APDS9960_REG_ENABLE, APDS9960_REG_CONFIG_2), 19562306a36Sopenharmony_ci regmap_reg_range(APDS9960_REG_POFFSET_UR, APDS9960_REG_GCONF_4), 19662306a36Sopenharmony_ci regmap_reg_range(APDS9960_REG_IFORCE, APDS9960_REG_AICLEAR), 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic const struct regmap_access_table apds9960_writeable_table = { 20062306a36Sopenharmony_ci .yes_ranges = apds9960_writeable_ranges, 20162306a36Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(apds9960_writeable_ranges), 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic const struct regmap_config apds9960_regmap_config = { 20562306a36Sopenharmony_ci .name = APDS9960_REGMAP_NAME, 20662306a36Sopenharmony_ci .reg_bits = 8, 20762306a36Sopenharmony_ci .val_bits = 8, 20862306a36Sopenharmony_ci .use_single_read = true, 20962306a36Sopenharmony_ci .use_single_write = true, 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci .volatile_table = &apds9960_volatile_table, 21262306a36Sopenharmony_ci .precious_table = &apds9960_precious_table, 21362306a36Sopenharmony_ci .rd_table = &apds9960_readable_table, 21462306a36Sopenharmony_ci .wr_table = &apds9960_writeable_table, 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci .reg_defaults = apds9960_reg_defaults, 21762306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(apds9960_reg_defaults), 21862306a36Sopenharmony_ci .max_register = APDS9960_REG_GFIFO_DIR(RIGHT), 21962306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic const struct iio_event_spec apds9960_pxs_event_spec[] = { 22362306a36Sopenharmony_ci { 22462306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 22562306a36Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 22662306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 22762306a36Sopenharmony_ci }, 22862306a36Sopenharmony_ci { 22962306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 23062306a36Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 23162306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 23262306a36Sopenharmony_ci }, 23362306a36Sopenharmony_ci { 23462306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 23562306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_ENABLE), 23662306a36Sopenharmony_ci }, 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic const struct iio_event_spec apds9960_als_event_spec[] = { 24062306a36Sopenharmony_ci { 24162306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 24262306a36Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 24362306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 24462306a36Sopenharmony_ci }, 24562306a36Sopenharmony_ci { 24662306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 24762306a36Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 24862306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 24962306a36Sopenharmony_ci }, 25062306a36Sopenharmony_ci { 25162306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 25262306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_ENABLE), 25362306a36Sopenharmony_ci }, 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci#define APDS9960_GESTURE_CHANNEL(_dir, _si) { \ 25762306a36Sopenharmony_ci .type = IIO_PROXIMITY, \ 25862306a36Sopenharmony_ci .channel = _si + 1, \ 25962306a36Sopenharmony_ci .scan_index = _si, \ 26062306a36Sopenharmony_ci .indexed = 1, \ 26162306a36Sopenharmony_ci .scan_type = { \ 26262306a36Sopenharmony_ci .sign = 'u', \ 26362306a36Sopenharmony_ci .realbits = 8, \ 26462306a36Sopenharmony_ci .storagebits = 8, \ 26562306a36Sopenharmony_ci }, \ 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci#define APDS9960_INTENSITY_CHANNEL(_colour) { \ 26962306a36Sopenharmony_ci .type = IIO_INTENSITY, \ 27062306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 27162306a36Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 27262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), \ 27362306a36Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_##_colour, \ 27462306a36Sopenharmony_ci .address = APDS9960_REG_ALS_CHANNEL(_colour), \ 27562306a36Sopenharmony_ci .modified = 1, \ 27662306a36Sopenharmony_ci .scan_index = -1, \ 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic const unsigned long apds9960_scan_masks[] = {0xf, 0}; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic const struct iio_chan_spec apds9960_channels[] = { 28262306a36Sopenharmony_ci { 28362306a36Sopenharmony_ci .type = IIO_PROXIMITY, 28462306a36Sopenharmony_ci .address = APDS9960_REG_PDATA, 28562306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 28662306a36Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 28762306a36Sopenharmony_ci .channel = 0, 28862306a36Sopenharmony_ci .indexed = 0, 28962306a36Sopenharmony_ci .scan_index = -1, 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci .event_spec = apds9960_pxs_event_spec, 29262306a36Sopenharmony_ci .num_event_specs = ARRAY_SIZE(apds9960_pxs_event_spec), 29362306a36Sopenharmony_ci }, 29462306a36Sopenharmony_ci /* Gesture Sensor */ 29562306a36Sopenharmony_ci APDS9960_GESTURE_CHANNEL(UP, 0), 29662306a36Sopenharmony_ci APDS9960_GESTURE_CHANNEL(DOWN, 1), 29762306a36Sopenharmony_ci APDS9960_GESTURE_CHANNEL(LEFT, 2), 29862306a36Sopenharmony_ci APDS9960_GESTURE_CHANNEL(RIGHT, 3), 29962306a36Sopenharmony_ci /* ALS */ 30062306a36Sopenharmony_ci { 30162306a36Sopenharmony_ci .type = IIO_INTENSITY, 30262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 30362306a36Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | 30462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), 30562306a36Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_CLEAR, 30662306a36Sopenharmony_ci .address = APDS9960_REG_ALS_CHANNEL(CLEAR), 30762306a36Sopenharmony_ci .modified = 1, 30862306a36Sopenharmony_ci .scan_index = -1, 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci .event_spec = apds9960_als_event_spec, 31162306a36Sopenharmony_ci .num_event_specs = ARRAY_SIZE(apds9960_als_event_spec), 31262306a36Sopenharmony_ci }, 31362306a36Sopenharmony_ci /* RGB Sensor */ 31462306a36Sopenharmony_ci APDS9960_INTENSITY_CHANNEL(RED), 31562306a36Sopenharmony_ci APDS9960_INTENSITY_CHANNEL(GREEN), 31662306a36Sopenharmony_ci APDS9960_INTENSITY_CHANNEL(BLUE), 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* integration time in us */ 32062306a36Sopenharmony_cistatic const int apds9960_int_time[][2] = { 32162306a36Sopenharmony_ci { 28000, 246}, 32262306a36Sopenharmony_ci {100000, 219}, 32362306a36Sopenharmony_ci {200000, 182}, 32462306a36Sopenharmony_ci {700000, 0} 32562306a36Sopenharmony_ci}; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* gain mapping */ 32862306a36Sopenharmony_cistatic const int apds9960_pxs_gain_map[] = {1, 2, 4, 8}; 32962306a36Sopenharmony_cistatic const int apds9960_als_gain_map[] = {1, 4, 16, 64}; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic IIO_CONST_ATTR(proximity_scale_available, "1 2 4 8"); 33262306a36Sopenharmony_cistatic IIO_CONST_ATTR(intensity_scale_available, "1 4 16 64"); 33362306a36Sopenharmony_cistatic IIO_CONST_ATTR_INT_TIME_AVAIL("0.028 0.1 0.2 0.7"); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic struct attribute *apds9960_attributes[] = { 33662306a36Sopenharmony_ci &iio_const_attr_proximity_scale_available.dev_attr.attr, 33762306a36Sopenharmony_ci &iio_const_attr_intensity_scale_available.dev_attr.attr, 33862306a36Sopenharmony_ci &iio_const_attr_integration_time_available.dev_attr.attr, 33962306a36Sopenharmony_ci NULL, 34062306a36Sopenharmony_ci}; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic const struct attribute_group apds9960_attribute_group = { 34362306a36Sopenharmony_ci .attrs = apds9960_attributes, 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic const struct reg_field apds9960_reg_field_int_als = 34762306a36Sopenharmony_ci REG_FIELD(APDS9960_REG_ENABLE, 4, 4); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic const struct reg_field apds9960_reg_field_int_ges = 35062306a36Sopenharmony_ci REG_FIELD(APDS9960_REG_GCONF_4, 1, 1); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic const struct reg_field apds9960_reg_field_int_pxs = 35362306a36Sopenharmony_ci REG_FIELD(APDS9960_REG_ENABLE, 5, 5); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic const struct reg_field apds9960_reg_field_enable_als = 35662306a36Sopenharmony_ci REG_FIELD(APDS9960_REG_ENABLE, 1, 1); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic const struct reg_field apds9960_reg_field_enable_ges = 35962306a36Sopenharmony_ci REG_FIELD(APDS9960_REG_ENABLE, 6, 6); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic const struct reg_field apds9960_reg_field_enable_pxs = 36262306a36Sopenharmony_ci REG_FIELD(APDS9960_REG_ENABLE, 2, 2); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cistatic int apds9960_set_it_time(struct apds9960_data *data, int val2) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci int ret = -EINVAL; 36762306a36Sopenharmony_ci int idx; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(apds9960_int_time); idx++) { 37062306a36Sopenharmony_ci if (apds9960_int_time[idx][0] == val2) { 37162306a36Sopenharmony_ci mutex_lock(&data->lock); 37262306a36Sopenharmony_ci ret = regmap_write(data->regmap, APDS9960_REG_ATIME, 37362306a36Sopenharmony_ci apds9960_int_time[idx][1]); 37462306a36Sopenharmony_ci if (!ret) 37562306a36Sopenharmony_ci data->als_adc_int_us = val2; 37662306a36Sopenharmony_ci mutex_unlock(&data->lock); 37762306a36Sopenharmony_ci break; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int apds9960_set_pxs_gain(struct apds9960_data *data, int val) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci int ret = -EINVAL; 38762306a36Sopenharmony_ci int idx; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(apds9960_pxs_gain_map); idx++) { 39062306a36Sopenharmony_ci if (apds9960_pxs_gain_map[idx] == val) { 39162306a36Sopenharmony_ci /* pxs + gesture gains are mirrored */ 39262306a36Sopenharmony_ci mutex_lock(&data->lock); 39362306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, 39462306a36Sopenharmony_ci APDS9960_REG_CONTROL, 39562306a36Sopenharmony_ci APDS9960_REG_CONTROL_PGAIN_MASK, 39662306a36Sopenharmony_ci idx << APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT); 39762306a36Sopenharmony_ci if (ret) { 39862306a36Sopenharmony_ci mutex_unlock(&data->lock); 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, 40362306a36Sopenharmony_ci APDS9960_REG_GCONF_2, 40462306a36Sopenharmony_ci APDS9960_REG_GCONF_2_GGAIN_MASK, 40562306a36Sopenharmony_ci idx << APDS9960_REG_GCONF_2_GGAIN_MASK_SHIFT); 40662306a36Sopenharmony_ci if (!ret) 40762306a36Sopenharmony_ci data->pxs_gain = idx; 40862306a36Sopenharmony_ci mutex_unlock(&data->lock); 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return ret; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int apds9960_set_als_gain(struct apds9960_data *data, int val) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci int ret = -EINVAL; 41962306a36Sopenharmony_ci int idx; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(apds9960_als_gain_map); idx++) { 42262306a36Sopenharmony_ci if (apds9960_als_gain_map[idx] == val) { 42362306a36Sopenharmony_ci mutex_lock(&data->lock); 42462306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, 42562306a36Sopenharmony_ci APDS9960_REG_CONTROL, 42662306a36Sopenharmony_ci APDS9960_REG_CONTROL_AGAIN_MASK, idx); 42762306a36Sopenharmony_ci if (!ret) 42862306a36Sopenharmony_ci data->als_gain = idx; 42962306a36Sopenharmony_ci mutex_unlock(&data->lock); 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return ret; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci#ifdef CONFIG_PM 43862306a36Sopenharmony_cistatic int apds9960_set_power_state(struct apds9960_data *data, bool on) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct device *dev = &data->client->dev; 44162306a36Sopenharmony_ci int ret = 0; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci mutex_lock(&data->lock); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (on) { 44662306a36Sopenharmony_ci int suspended; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci suspended = pm_runtime_suspended(dev); 44962306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* Allow one integration cycle before allowing a reading */ 45262306a36Sopenharmony_ci if (suspended) 45362306a36Sopenharmony_ci usleep_range(data->als_adc_int_us, 45462306a36Sopenharmony_ci APDS9960_MAX_INT_TIME_IN_US); 45562306a36Sopenharmony_ci } else { 45662306a36Sopenharmony_ci pm_runtime_mark_last_busy(dev); 45762306a36Sopenharmony_ci ret = pm_runtime_put_autosuspend(dev); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci mutex_unlock(&data->lock); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return ret; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci#else 46562306a36Sopenharmony_cistatic int apds9960_set_power_state(struct apds9960_data *data, bool on) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci#endif 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int apds9960_read_raw(struct iio_dev *indio_dev, 47262306a36Sopenharmony_ci struct iio_chan_spec const *chan, 47362306a36Sopenharmony_ci int *val, int *val2, long mask) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 47662306a36Sopenharmony_ci __le16 buf; 47762306a36Sopenharmony_ci int ret = -EINVAL; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (data->gesture_mode_running) 48062306a36Sopenharmony_ci return -EBUSY; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci switch (mask) { 48362306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 48462306a36Sopenharmony_ci apds9960_set_power_state(data, true); 48562306a36Sopenharmony_ci switch (chan->type) { 48662306a36Sopenharmony_ci case IIO_PROXIMITY: 48762306a36Sopenharmony_ci ret = regmap_read(data->regmap, chan->address, val); 48862306a36Sopenharmony_ci if (!ret) 48962306a36Sopenharmony_ci ret = IIO_VAL_INT; 49062306a36Sopenharmony_ci break; 49162306a36Sopenharmony_ci case IIO_INTENSITY: 49262306a36Sopenharmony_ci ret = regmap_bulk_read(data->regmap, chan->address, 49362306a36Sopenharmony_ci &buf, 2); 49462306a36Sopenharmony_ci if (!ret) { 49562306a36Sopenharmony_ci ret = IIO_VAL_INT; 49662306a36Sopenharmony_ci *val = le16_to_cpu(buf); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci default: 50062306a36Sopenharmony_ci ret = -EINVAL; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci apds9960_set_power_state(data, false); 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 50562306a36Sopenharmony_ci /* RGB + ALS sensors only have integration time */ 50662306a36Sopenharmony_ci mutex_lock(&data->lock); 50762306a36Sopenharmony_ci switch (chan->type) { 50862306a36Sopenharmony_ci case IIO_INTENSITY: 50962306a36Sopenharmony_ci *val = 0; 51062306a36Sopenharmony_ci *val2 = data->als_adc_int_us; 51162306a36Sopenharmony_ci ret = IIO_VAL_INT_PLUS_MICRO; 51262306a36Sopenharmony_ci break; 51362306a36Sopenharmony_ci default: 51462306a36Sopenharmony_ci ret = -EINVAL; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci mutex_unlock(&data->lock); 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 51962306a36Sopenharmony_ci mutex_lock(&data->lock); 52062306a36Sopenharmony_ci switch (chan->type) { 52162306a36Sopenharmony_ci case IIO_PROXIMITY: 52262306a36Sopenharmony_ci *val = apds9960_pxs_gain_map[data->pxs_gain]; 52362306a36Sopenharmony_ci ret = IIO_VAL_INT; 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci case IIO_INTENSITY: 52662306a36Sopenharmony_ci *val = apds9960_als_gain_map[data->als_gain]; 52762306a36Sopenharmony_ci ret = IIO_VAL_INT; 52862306a36Sopenharmony_ci break; 52962306a36Sopenharmony_ci default: 53062306a36Sopenharmony_ci ret = -EINVAL; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci mutex_unlock(&data->lock); 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci}; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic int apds9960_write_raw(struct iio_dev *indio_dev, 54062306a36Sopenharmony_ci struct iio_chan_spec const *chan, 54162306a36Sopenharmony_ci int val, int val2, long mask) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci switch (mask) { 54662306a36Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 54762306a36Sopenharmony_ci /* RGB + ALS sensors only have int time */ 54862306a36Sopenharmony_ci switch (chan->type) { 54962306a36Sopenharmony_ci case IIO_INTENSITY: 55062306a36Sopenharmony_ci if (val != 0) 55162306a36Sopenharmony_ci return -EINVAL; 55262306a36Sopenharmony_ci return apds9960_set_it_time(data, val2); 55362306a36Sopenharmony_ci default: 55462306a36Sopenharmony_ci return -EINVAL; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 55762306a36Sopenharmony_ci if (val2 != 0) 55862306a36Sopenharmony_ci return -EINVAL; 55962306a36Sopenharmony_ci switch (chan->type) { 56062306a36Sopenharmony_ci case IIO_PROXIMITY: 56162306a36Sopenharmony_ci return apds9960_set_pxs_gain(data, val); 56262306a36Sopenharmony_ci case IIO_INTENSITY: 56362306a36Sopenharmony_ci return apds9960_set_als_gain(data, val); 56462306a36Sopenharmony_ci default: 56562306a36Sopenharmony_ci return -EINVAL; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci default: 56862306a36Sopenharmony_ci return -EINVAL; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic inline int apds9960_get_thres_reg(const struct iio_chan_spec *chan, 57562306a36Sopenharmony_ci enum iio_event_direction dir, 57662306a36Sopenharmony_ci u8 *reg) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci switch (dir) { 57962306a36Sopenharmony_ci case IIO_EV_DIR_RISING: 58062306a36Sopenharmony_ci switch (chan->type) { 58162306a36Sopenharmony_ci case IIO_PROXIMITY: 58262306a36Sopenharmony_ci *reg = APDS9960_REG_PIHT; 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci case IIO_INTENSITY: 58562306a36Sopenharmony_ci *reg = APDS9960_REG_AIHTL; 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci default: 58862306a36Sopenharmony_ci return -EINVAL; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci case IIO_EV_DIR_FALLING: 59262306a36Sopenharmony_ci switch (chan->type) { 59362306a36Sopenharmony_ci case IIO_PROXIMITY: 59462306a36Sopenharmony_ci *reg = APDS9960_REG_PILT; 59562306a36Sopenharmony_ci break; 59662306a36Sopenharmony_ci case IIO_INTENSITY: 59762306a36Sopenharmony_ci *reg = APDS9960_REG_AILTL; 59862306a36Sopenharmony_ci break; 59962306a36Sopenharmony_ci default: 60062306a36Sopenharmony_ci return -EINVAL; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci default: 60462306a36Sopenharmony_ci return -EINVAL; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic int apds9960_read_event(struct iio_dev *indio_dev, 61162306a36Sopenharmony_ci const struct iio_chan_spec *chan, 61262306a36Sopenharmony_ci enum iio_event_type type, 61362306a36Sopenharmony_ci enum iio_event_direction dir, 61462306a36Sopenharmony_ci enum iio_event_info info, 61562306a36Sopenharmony_ci int *val, int *val2) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci u8 reg; 61862306a36Sopenharmony_ci __le16 buf; 61962306a36Sopenharmony_ci int ret = 0; 62062306a36Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (info != IIO_EV_INFO_VALUE) 62362306a36Sopenharmony_ci return -EINVAL; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci ret = apds9960_get_thres_reg(chan, dir, ®); 62662306a36Sopenharmony_ci if (ret < 0) 62762306a36Sopenharmony_ci return ret; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (chan->type == IIO_PROXIMITY) { 63062306a36Sopenharmony_ci ret = regmap_read(data->regmap, reg, val); 63162306a36Sopenharmony_ci if (ret < 0) 63262306a36Sopenharmony_ci return ret; 63362306a36Sopenharmony_ci } else if (chan->type == IIO_INTENSITY) { 63462306a36Sopenharmony_ci ret = regmap_bulk_read(data->regmap, reg, &buf, 2); 63562306a36Sopenharmony_ci if (ret < 0) 63662306a36Sopenharmony_ci return ret; 63762306a36Sopenharmony_ci *val = le16_to_cpu(buf); 63862306a36Sopenharmony_ci } else 63962306a36Sopenharmony_ci return -EINVAL; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci *val2 = 0; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci return IIO_VAL_INT; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic int apds9960_write_event(struct iio_dev *indio_dev, 64762306a36Sopenharmony_ci const struct iio_chan_spec *chan, 64862306a36Sopenharmony_ci enum iio_event_type type, 64962306a36Sopenharmony_ci enum iio_event_direction dir, 65062306a36Sopenharmony_ci enum iio_event_info info, 65162306a36Sopenharmony_ci int val, int val2) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci u8 reg; 65462306a36Sopenharmony_ci __le16 buf; 65562306a36Sopenharmony_ci int ret = 0; 65662306a36Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (info != IIO_EV_INFO_VALUE) 65962306a36Sopenharmony_ci return -EINVAL; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci ret = apds9960_get_thres_reg(chan, dir, ®); 66262306a36Sopenharmony_ci if (ret < 0) 66362306a36Sopenharmony_ci return ret; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (chan->type == IIO_PROXIMITY) { 66662306a36Sopenharmony_ci if (val < 0 || val > APDS9960_MAX_PXS_THRES_VAL) 66762306a36Sopenharmony_ci return -EINVAL; 66862306a36Sopenharmony_ci ret = regmap_write(data->regmap, reg, val); 66962306a36Sopenharmony_ci if (ret < 0) 67062306a36Sopenharmony_ci return ret; 67162306a36Sopenharmony_ci } else if (chan->type == IIO_INTENSITY) { 67262306a36Sopenharmony_ci if (val < 0 || val > APDS9960_MAX_ALS_THRES_VAL) 67362306a36Sopenharmony_ci return -EINVAL; 67462306a36Sopenharmony_ci buf = cpu_to_le16(val); 67562306a36Sopenharmony_ci ret = regmap_bulk_write(data->regmap, reg, &buf, 2); 67662306a36Sopenharmony_ci if (ret < 0) 67762306a36Sopenharmony_ci return ret; 67862306a36Sopenharmony_ci } else 67962306a36Sopenharmony_ci return -EINVAL; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int apds9960_read_event_config(struct iio_dev *indio_dev, 68562306a36Sopenharmony_ci const struct iio_chan_spec *chan, 68662306a36Sopenharmony_ci enum iio_event_type type, 68762306a36Sopenharmony_ci enum iio_event_direction dir) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci switch (chan->type) { 69262306a36Sopenharmony_ci case IIO_PROXIMITY: 69362306a36Sopenharmony_ci return data->pxs_int; 69462306a36Sopenharmony_ci case IIO_INTENSITY: 69562306a36Sopenharmony_ci return data->als_int; 69662306a36Sopenharmony_ci default: 69762306a36Sopenharmony_ci return -EINVAL; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci return 0; 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int apds9960_write_event_config(struct iio_dev *indio_dev, 70462306a36Sopenharmony_ci const struct iio_chan_spec *chan, 70562306a36Sopenharmony_ci enum iio_event_type type, 70662306a36Sopenharmony_ci enum iio_event_direction dir, 70762306a36Sopenharmony_ci int state) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 71062306a36Sopenharmony_ci int ret; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci state = !!state; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci switch (chan->type) { 71562306a36Sopenharmony_ci case IIO_PROXIMITY: 71662306a36Sopenharmony_ci if (data->pxs_int == state) 71762306a36Sopenharmony_ci return -EINVAL; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci ret = regmap_field_write(data->reg_int_pxs, state); 72062306a36Sopenharmony_ci if (ret) 72162306a36Sopenharmony_ci return ret; 72262306a36Sopenharmony_ci data->pxs_int = state; 72362306a36Sopenharmony_ci apds9960_set_power_state(data, state); 72462306a36Sopenharmony_ci break; 72562306a36Sopenharmony_ci case IIO_INTENSITY: 72662306a36Sopenharmony_ci if (data->als_int == state) 72762306a36Sopenharmony_ci return -EINVAL; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci ret = regmap_field_write(data->reg_int_als, state); 73062306a36Sopenharmony_ci if (ret) 73162306a36Sopenharmony_ci return ret; 73262306a36Sopenharmony_ci data->als_int = state; 73362306a36Sopenharmony_ci apds9960_set_power_state(data, state); 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci default: 73662306a36Sopenharmony_ci return -EINVAL; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return 0; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic const struct iio_info apds9960_info = { 74362306a36Sopenharmony_ci .attrs = &apds9960_attribute_group, 74462306a36Sopenharmony_ci .read_raw = apds9960_read_raw, 74562306a36Sopenharmony_ci .write_raw = apds9960_write_raw, 74662306a36Sopenharmony_ci .read_event_value = apds9960_read_event, 74762306a36Sopenharmony_ci .write_event_value = apds9960_write_event, 74862306a36Sopenharmony_ci .read_event_config = apds9960_read_event_config, 74962306a36Sopenharmony_ci .write_event_config = apds9960_write_event_config, 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci}; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic inline int apds9660_fifo_is_empty(struct apds9960_data *data) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci int cnt; 75662306a36Sopenharmony_ci int ret; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci ret = regmap_read(data->regmap, APDS9960_REG_GFLVL, &cnt); 75962306a36Sopenharmony_ci if (ret) 76062306a36Sopenharmony_ci return ret; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return cnt; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic void apds9960_read_gesture_fifo(struct apds9960_data *data) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci int ret, cnt = 0; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci mutex_lock(&data->lock); 77062306a36Sopenharmony_ci data->gesture_mode_running = 1; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci while (cnt || (cnt = apds9660_fifo_is_empty(data) > 0)) { 77362306a36Sopenharmony_ci ret = regmap_bulk_read(data->regmap, APDS9960_REG_GFIFO_BASE, 77462306a36Sopenharmony_ci &data->buffer, 4); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (ret) 77762306a36Sopenharmony_ci goto err_read; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci iio_push_to_buffers(data->indio_dev, data->buffer); 78062306a36Sopenharmony_ci cnt--; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cierr_read: 78462306a36Sopenharmony_ci data->gesture_mode_running = 0; 78562306a36Sopenharmony_ci mutex_unlock(&data->lock); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic irqreturn_t apds9960_interrupt_handler(int irq, void *private) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct iio_dev *indio_dev = private; 79162306a36Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 79262306a36Sopenharmony_ci int ret, status; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci ret = regmap_read(data->regmap, APDS9960_REG_STATUS, &status); 79562306a36Sopenharmony_ci if (ret < 0) { 79662306a36Sopenharmony_ci dev_err(&data->client->dev, "irq status reg read failed\n"); 79762306a36Sopenharmony_ci return IRQ_HANDLED; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if ((status & APDS9960_REG_STATUS_ALS_INT) && data->als_int) { 80162306a36Sopenharmony_ci iio_push_event(indio_dev, 80262306a36Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, 80362306a36Sopenharmony_ci IIO_EV_TYPE_THRESH, 80462306a36Sopenharmony_ci IIO_EV_DIR_EITHER), 80562306a36Sopenharmony_ci iio_get_time_ns(indio_dev)); 80662306a36Sopenharmony_ci regmap_write(data->regmap, APDS9960_REG_CICLEAR, 1); 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if ((status & APDS9960_REG_STATUS_PS_INT) && data->pxs_int) { 81062306a36Sopenharmony_ci iio_push_event(indio_dev, 81162306a36Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 81262306a36Sopenharmony_ci IIO_EV_TYPE_THRESH, 81362306a36Sopenharmony_ci IIO_EV_DIR_EITHER), 81462306a36Sopenharmony_ci iio_get_time_ns(indio_dev)); 81562306a36Sopenharmony_ci regmap_write(data->regmap, APDS9960_REG_PICLEAR, 1); 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (status & APDS9960_REG_STATUS_GINT) 81962306a36Sopenharmony_ci apds9960_read_gesture_fifo(data); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci return IRQ_HANDLED; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int apds9960_set_powermode(struct apds9960_data *data, bool state) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci return regmap_update_bits(data->regmap, APDS9960_REG_ENABLE, 1, state); 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int apds9960_buffer_postenable(struct iio_dev *indio_dev) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 83262306a36Sopenharmony_ci int ret; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ret = regmap_field_write(data->reg_int_ges, 1); 83562306a36Sopenharmony_ci if (ret) 83662306a36Sopenharmony_ci return ret; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci ret = regmap_field_write(data->reg_enable_ges, 1); 83962306a36Sopenharmony_ci if (ret) 84062306a36Sopenharmony_ci return ret; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci pm_runtime_get_sync(&data->client->dev); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci return 0; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic int apds9960_buffer_predisable(struct iio_dev *indio_dev) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 85062306a36Sopenharmony_ci int ret; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci ret = regmap_field_write(data->reg_enable_ges, 0); 85362306a36Sopenharmony_ci if (ret) 85462306a36Sopenharmony_ci return ret; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ret = regmap_field_write(data->reg_int_ges, 0); 85762306a36Sopenharmony_ci if (ret) 85862306a36Sopenharmony_ci return ret; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci pm_runtime_put_autosuspend(&data->client->dev); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic const struct iio_buffer_setup_ops apds9960_buffer_setup_ops = { 86662306a36Sopenharmony_ci .postenable = apds9960_buffer_postenable, 86762306a36Sopenharmony_ci .predisable = apds9960_buffer_predisable, 86862306a36Sopenharmony_ci}; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic int apds9960_regfield_init(struct apds9960_data *data) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct device *dev = &data->client->dev; 87362306a36Sopenharmony_ci struct regmap *regmap = data->regmap; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci data->reg_int_als = devm_regmap_field_alloc(dev, regmap, 87662306a36Sopenharmony_ci apds9960_reg_field_int_als); 87762306a36Sopenharmony_ci if (IS_ERR(data->reg_int_als)) { 87862306a36Sopenharmony_ci dev_err(dev, "INT ALS reg field init failed\n"); 87962306a36Sopenharmony_ci return PTR_ERR(data->reg_int_als); 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci data->reg_int_ges = devm_regmap_field_alloc(dev, regmap, 88362306a36Sopenharmony_ci apds9960_reg_field_int_ges); 88462306a36Sopenharmony_ci if (IS_ERR(data->reg_int_ges)) { 88562306a36Sopenharmony_ci dev_err(dev, "INT gesture reg field init failed\n"); 88662306a36Sopenharmony_ci return PTR_ERR(data->reg_int_ges); 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci data->reg_int_pxs = devm_regmap_field_alloc(dev, regmap, 89062306a36Sopenharmony_ci apds9960_reg_field_int_pxs); 89162306a36Sopenharmony_ci if (IS_ERR(data->reg_int_pxs)) { 89262306a36Sopenharmony_ci dev_err(dev, "INT pxs reg field init failed\n"); 89362306a36Sopenharmony_ci return PTR_ERR(data->reg_int_pxs); 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci data->reg_enable_als = devm_regmap_field_alloc(dev, regmap, 89762306a36Sopenharmony_ci apds9960_reg_field_enable_als); 89862306a36Sopenharmony_ci if (IS_ERR(data->reg_enable_als)) { 89962306a36Sopenharmony_ci dev_err(dev, "Enable ALS reg field init failed\n"); 90062306a36Sopenharmony_ci return PTR_ERR(data->reg_enable_als); 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci data->reg_enable_ges = devm_regmap_field_alloc(dev, regmap, 90462306a36Sopenharmony_ci apds9960_reg_field_enable_ges); 90562306a36Sopenharmony_ci if (IS_ERR(data->reg_enable_ges)) { 90662306a36Sopenharmony_ci dev_err(dev, "Enable gesture reg field init failed\n"); 90762306a36Sopenharmony_ci return PTR_ERR(data->reg_enable_ges); 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci data->reg_enable_pxs = devm_regmap_field_alloc(dev, regmap, 91162306a36Sopenharmony_ci apds9960_reg_field_enable_pxs); 91262306a36Sopenharmony_ci if (IS_ERR(data->reg_enable_pxs)) { 91362306a36Sopenharmony_ci dev_err(dev, "Enable PXS reg field init failed\n"); 91462306a36Sopenharmony_ci return PTR_ERR(data->reg_enable_pxs); 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic int apds9960_chip_init(struct apds9960_data *data) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci int ret; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* Default IT for ALS of 28 ms */ 92562306a36Sopenharmony_ci ret = apds9960_set_it_time(data, 28000); 92662306a36Sopenharmony_ci if (ret) 92762306a36Sopenharmony_ci return ret; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* Ensure gesture interrupt is OFF */ 93062306a36Sopenharmony_ci ret = regmap_field_write(data->reg_int_ges, 0); 93162306a36Sopenharmony_ci if (ret) 93262306a36Sopenharmony_ci return ret; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* Disable gesture sensor, since polling is useless from user-space */ 93562306a36Sopenharmony_ci ret = regmap_field_write(data->reg_enable_ges, 0); 93662306a36Sopenharmony_ci if (ret) 93762306a36Sopenharmony_ci return ret; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Ensure proximity interrupt is OFF */ 94062306a36Sopenharmony_ci ret = regmap_field_write(data->reg_int_pxs, 0); 94162306a36Sopenharmony_ci if (ret) 94262306a36Sopenharmony_ci return ret; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci /* Enable proximity sensor for polling */ 94562306a36Sopenharmony_ci ret = regmap_field_write(data->reg_enable_pxs, 1); 94662306a36Sopenharmony_ci if (ret) 94762306a36Sopenharmony_ci return ret; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* Ensure ALS interrupt is OFF */ 95062306a36Sopenharmony_ci ret = regmap_field_write(data->reg_int_als, 0); 95162306a36Sopenharmony_ci if (ret) 95262306a36Sopenharmony_ci return ret; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci /* Enable ALS sensor for polling */ 95562306a36Sopenharmony_ci ret = regmap_field_write(data->reg_enable_als, 1); 95662306a36Sopenharmony_ci if (ret) 95762306a36Sopenharmony_ci return ret; 95862306a36Sopenharmony_ci /* 95962306a36Sopenharmony_ci * When enabled trigger an interrupt after 3 readings 96062306a36Sopenharmony_ci * outside threshold for ALS + PXS 96162306a36Sopenharmony_ci */ 96262306a36Sopenharmony_ci ret = regmap_write(data->regmap, APDS9960_REG_PERS, 96362306a36Sopenharmony_ci APDS9960_DEFAULT_PERS); 96462306a36Sopenharmony_ci if (ret) 96562306a36Sopenharmony_ci return ret; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* 96862306a36Sopenharmony_ci * Wait for 4 event outside gesture threshold to prevent interrupt 96962306a36Sopenharmony_ci * flooding. 97062306a36Sopenharmony_ci */ 97162306a36Sopenharmony_ci ret = regmap_update_bits(data->regmap, APDS9960_REG_GCONF_1, 97262306a36Sopenharmony_ci APDS9960_REG_GCONF_1_GFIFO_THRES_MASK, 97362306a36Sopenharmony_ci BIT(0) << APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT); 97462306a36Sopenharmony_ci if (ret) 97562306a36Sopenharmony_ci return ret; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* Default ENTER and EXIT thresholds for the GESTURE engine. */ 97862306a36Sopenharmony_ci ret = regmap_write(data->regmap, APDS9960_REG_GPENTH, 97962306a36Sopenharmony_ci APDS9960_DEFAULT_GPENTH); 98062306a36Sopenharmony_ci if (ret) 98162306a36Sopenharmony_ci return ret; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci ret = regmap_write(data->regmap, APDS9960_REG_GEXTH, 98462306a36Sopenharmony_ci APDS9960_DEFAULT_GEXTH); 98562306a36Sopenharmony_ci if (ret) 98662306a36Sopenharmony_ci return ret; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci return apds9960_set_powermode(data, 1); 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic int apds9960_probe(struct i2c_client *client) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct apds9960_data *data; 99462306a36Sopenharmony_ci struct iio_dev *indio_dev; 99562306a36Sopenharmony_ci int ret; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 99862306a36Sopenharmony_ci if (!indio_dev) 99962306a36Sopenharmony_ci return -ENOMEM; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci indio_dev->info = &apds9960_info; 100262306a36Sopenharmony_ci indio_dev->name = APDS9960_DRV_NAME; 100362306a36Sopenharmony_ci indio_dev->channels = apds9960_channels; 100462306a36Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(apds9960_channels); 100562306a36Sopenharmony_ci indio_dev->available_scan_masks = apds9960_scan_masks; 100662306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, 100962306a36Sopenharmony_ci &apds9960_buffer_setup_ops); 101062306a36Sopenharmony_ci if (ret) 101162306a36Sopenharmony_ci return ret; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci data = iio_priv(indio_dev); 101462306a36Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci data->regmap = devm_regmap_init_i2c(client, &apds9960_regmap_config); 101762306a36Sopenharmony_ci if (IS_ERR(data->regmap)) { 101862306a36Sopenharmony_ci dev_err(&client->dev, "regmap initialization failed.\n"); 101962306a36Sopenharmony_ci return PTR_ERR(data->regmap); 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci data->client = client; 102362306a36Sopenharmony_ci data->indio_dev = indio_dev; 102462306a36Sopenharmony_ci mutex_init(&data->lock); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci ret = pm_runtime_set_active(&client->dev); 102762306a36Sopenharmony_ci if (ret) 102862306a36Sopenharmony_ci goto error_power_down; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci pm_runtime_enable(&client->dev); 103162306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&client->dev, 5000); 103262306a36Sopenharmony_ci pm_runtime_use_autosuspend(&client->dev); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci apds9960_set_power_state(data, true); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci ret = apds9960_regfield_init(data); 103762306a36Sopenharmony_ci if (ret) 103862306a36Sopenharmony_ci goto error_power_down; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci ret = apds9960_chip_init(data); 104162306a36Sopenharmony_ci if (ret) 104262306a36Sopenharmony_ci goto error_power_down; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (client->irq <= 0) { 104562306a36Sopenharmony_ci dev_err(&client->dev, "no valid irq defined\n"); 104662306a36Sopenharmony_ci ret = -EINVAL; 104762306a36Sopenharmony_ci goto error_power_down; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, 105062306a36Sopenharmony_ci NULL, apds9960_interrupt_handler, 105162306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 105262306a36Sopenharmony_ci "apds9960_event", 105362306a36Sopenharmony_ci indio_dev); 105462306a36Sopenharmony_ci if (ret) { 105562306a36Sopenharmony_ci dev_err(&client->dev, "request irq (%d) failed\n", client->irq); 105662306a36Sopenharmony_ci goto error_power_down; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci ret = iio_device_register(indio_dev); 106062306a36Sopenharmony_ci if (ret) 106162306a36Sopenharmony_ci goto error_power_down; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci apds9960_set_power_state(data, false); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci return 0; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cierror_power_down: 106862306a36Sopenharmony_ci apds9960_set_power_state(data, false); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci return ret; 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic void apds9960_remove(struct i2c_client *client) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 107662306a36Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci iio_device_unregister(indio_dev); 107962306a36Sopenharmony_ci pm_runtime_disable(&client->dev); 108062306a36Sopenharmony_ci pm_runtime_set_suspended(&client->dev); 108162306a36Sopenharmony_ci apds9960_set_powermode(data, 0); 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci#ifdef CONFIG_PM 108562306a36Sopenharmony_cistatic int apds9960_runtime_suspend(struct device *dev) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct apds9960_data *data = 108862306a36Sopenharmony_ci iio_priv(i2c_get_clientdata(to_i2c_client(dev))); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci return apds9960_set_powermode(data, 0); 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic int apds9960_runtime_resume(struct device *dev) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci struct apds9960_data *data = 109662306a36Sopenharmony_ci iio_priv(i2c_get_clientdata(to_i2c_client(dev))); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci return apds9960_set_powermode(data, 1); 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci#endif 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_cistatic const struct dev_pm_ops apds9960_pm_ops = { 110362306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 110462306a36Sopenharmony_ci pm_runtime_force_resume) 110562306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(apds9960_runtime_suspend, 110662306a36Sopenharmony_ci apds9960_runtime_resume, NULL) 110762306a36Sopenharmony_ci}; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic const struct i2c_device_id apds9960_id[] = { 111062306a36Sopenharmony_ci { "apds9960", 0 }, 111162306a36Sopenharmony_ci {} 111262306a36Sopenharmony_ci}; 111362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, apds9960_id); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic const struct acpi_device_id apds9960_acpi_match[] = { 111662306a36Sopenharmony_ci { "MSHW0184" }, 111762306a36Sopenharmony_ci { } 111862306a36Sopenharmony_ci}; 111962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, apds9960_acpi_match); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cistatic const struct of_device_id apds9960_of_match[] = { 112262306a36Sopenharmony_ci { .compatible = "avago,apds9960" }, 112362306a36Sopenharmony_ci { } 112462306a36Sopenharmony_ci}; 112562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, apds9960_of_match); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_cistatic struct i2c_driver apds9960_driver = { 112862306a36Sopenharmony_ci .driver = { 112962306a36Sopenharmony_ci .name = APDS9960_DRV_NAME, 113062306a36Sopenharmony_ci .of_match_table = apds9960_of_match, 113162306a36Sopenharmony_ci .pm = &apds9960_pm_ops, 113262306a36Sopenharmony_ci .acpi_match_table = apds9960_acpi_match, 113362306a36Sopenharmony_ci }, 113462306a36Sopenharmony_ci .probe = apds9960_probe, 113562306a36Sopenharmony_ci .remove = apds9960_remove, 113662306a36Sopenharmony_ci .id_table = apds9960_id, 113762306a36Sopenharmony_ci}; 113862306a36Sopenharmony_cimodule_i2c_driver(apds9960_driver); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ciMODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); 114162306a36Sopenharmony_ciMODULE_DESCRIPTION("APDS9960 Gesture/RGB/ALS/Proximity sensor"); 114262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1143