18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015, 2018 68c2ecf20Sopenharmony_ci * Author: Matt Ranostay <matt.ranostay@konsulko.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * TODO: gesture + proximity calib offsets 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/irq.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 208c2ecf20Sopenharmony_ci#include <linux/regmap.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 228c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 238c2ecf20Sopenharmony_ci#include <linux/iio/events.h> 248c2ecf20Sopenharmony_ci#include <linux/iio/kfifo_buf.h> 258c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define APDS9960_REGMAP_NAME "apds9960_regmap" 288c2ecf20Sopenharmony_ci#define APDS9960_DRV_NAME "apds9960" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define APDS9960_REG_RAM_START 0x00 318c2ecf20Sopenharmony_ci#define APDS9960_REG_RAM_END 0x7f 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define APDS9960_REG_ENABLE 0x80 348c2ecf20Sopenharmony_ci#define APDS9960_REG_ATIME 0x81 358c2ecf20Sopenharmony_ci#define APDS9960_REG_WTIME 0x83 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define APDS9960_REG_AILTL 0x84 388c2ecf20Sopenharmony_ci#define APDS9960_REG_AILTH 0x85 398c2ecf20Sopenharmony_ci#define APDS9960_REG_AIHTL 0x86 408c2ecf20Sopenharmony_ci#define APDS9960_REG_AIHTH 0x87 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define APDS9960_REG_PILT 0x89 438c2ecf20Sopenharmony_ci#define APDS9960_REG_PIHT 0x8b 448c2ecf20Sopenharmony_ci#define APDS9960_REG_PERS 0x8c 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define APDS9960_REG_CONFIG_1 0x8d 478c2ecf20Sopenharmony_ci#define APDS9960_REG_PPULSE 0x8e 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define APDS9960_REG_CONTROL 0x8f 508c2ecf20Sopenharmony_ci#define APDS9960_REG_CONTROL_AGAIN_MASK 0x03 518c2ecf20Sopenharmony_ci#define APDS9960_REG_CONTROL_PGAIN_MASK 0x0c 528c2ecf20Sopenharmony_ci#define APDS9960_REG_CONTROL_AGAIN_MASK_SHIFT 0 538c2ecf20Sopenharmony_ci#define APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT 2 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define APDS9960_REG_CONFIG_2 0x90 568c2ecf20Sopenharmony_ci#define APDS9960_REG_ID 0x92 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define APDS9960_REG_STATUS 0x93 598c2ecf20Sopenharmony_ci#define APDS9960_REG_STATUS_PS_INT BIT(5) 608c2ecf20Sopenharmony_ci#define APDS9960_REG_STATUS_ALS_INT BIT(4) 618c2ecf20Sopenharmony_ci#define APDS9960_REG_STATUS_GINT BIT(2) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define APDS9960_REG_PDATA 0x9c 648c2ecf20Sopenharmony_ci#define APDS9960_REG_POFFSET_UR 0x9d 658c2ecf20Sopenharmony_ci#define APDS9960_REG_POFFSET_DL 0x9e 668c2ecf20Sopenharmony_ci#define APDS9960_REG_CONFIG_3 0x9f 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define APDS9960_REG_GPENTH 0xa0 698c2ecf20Sopenharmony_ci#define APDS9960_REG_GEXTH 0xa1 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_1 0xa2 728c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK 0xc0 738c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT 6 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_2 0xa3 768c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_2_GGAIN_MASK 0x60 778c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_2_GGAIN_MASK_SHIFT 5 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define APDS9960_REG_GOFFSET_U 0xa4 808c2ecf20Sopenharmony_ci#define APDS9960_REG_GOFFSET_D 0xa5 818c2ecf20Sopenharmony_ci#define APDS9960_REG_GPULSE 0xa6 828c2ecf20Sopenharmony_ci#define APDS9960_REG_GOFFSET_L 0xa7 838c2ecf20Sopenharmony_ci#define APDS9960_REG_GOFFSET_R 0xa9 848c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_3 0xaa 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define APDS9960_REG_GCONF_4 0xab 878c2ecf20Sopenharmony_ci#define APDS9960_REG_GFLVL 0xae 888c2ecf20Sopenharmony_ci#define APDS9960_REG_GSTATUS 0xaf 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define APDS9960_REG_IFORCE 0xe4 918c2ecf20Sopenharmony_ci#define APDS9960_REG_PICLEAR 0xe5 928c2ecf20Sopenharmony_ci#define APDS9960_REG_CICLEAR 0xe6 938c2ecf20Sopenharmony_ci#define APDS9960_REG_AICLEAR 0xe7 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define APDS9960_DEFAULT_PERS 0x33 968c2ecf20Sopenharmony_ci#define APDS9960_DEFAULT_GPENTH 0x50 978c2ecf20Sopenharmony_ci#define APDS9960_DEFAULT_GEXTH 0x40 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci#define APDS9960_MAX_PXS_THRES_VAL 255 1008c2ecf20Sopenharmony_ci#define APDS9960_MAX_ALS_THRES_VAL 0xffff 1018c2ecf20Sopenharmony_ci#define APDS9960_MAX_INT_TIME_IN_US 1000000 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cienum apds9960_als_channel_idx { 1048c2ecf20Sopenharmony_ci IDX_ALS_CLEAR, IDX_ALS_RED, IDX_ALS_GREEN, IDX_ALS_BLUE, 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define APDS9960_REG_ALS_BASE 0x94 1088c2ecf20Sopenharmony_ci#define APDS9960_REG_ALS_CHANNEL(_colour) \ 1098c2ecf20Sopenharmony_ci (APDS9960_REG_ALS_BASE + (IDX_ALS_##_colour * 2)) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cienum apds9960_gesture_channel_idx { 1128c2ecf20Sopenharmony_ci IDX_DIR_UP, IDX_DIR_DOWN, IDX_DIR_LEFT, IDX_DIR_RIGHT, 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define APDS9960_REG_GFIFO_BASE 0xfc 1168c2ecf20Sopenharmony_ci#define APDS9960_REG_GFIFO_DIR(_dir) \ 1178c2ecf20Sopenharmony_ci (APDS9960_REG_GFIFO_BASE + IDX_DIR_##_dir) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistruct apds9960_data { 1208c2ecf20Sopenharmony_ci struct i2c_client *client; 1218c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 1228c2ecf20Sopenharmony_ci struct mutex lock; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* regmap fields */ 1258c2ecf20Sopenharmony_ci struct regmap *regmap; 1268c2ecf20Sopenharmony_ci struct regmap_field *reg_int_als; 1278c2ecf20Sopenharmony_ci struct regmap_field *reg_int_ges; 1288c2ecf20Sopenharmony_ci struct regmap_field *reg_int_pxs; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci struct regmap_field *reg_enable_als; 1318c2ecf20Sopenharmony_ci struct regmap_field *reg_enable_ges; 1328c2ecf20Sopenharmony_ci struct regmap_field *reg_enable_pxs; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* state */ 1358c2ecf20Sopenharmony_ci int als_int; 1368c2ecf20Sopenharmony_ci int pxs_int; 1378c2ecf20Sopenharmony_ci int gesture_mode_running; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* gain values */ 1408c2ecf20Sopenharmony_ci int als_gain; 1418c2ecf20Sopenharmony_ci int pxs_gain; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* integration time value in us */ 1448c2ecf20Sopenharmony_ci int als_adc_int_us; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* gesture buffer */ 1478c2ecf20Sopenharmony_ci u8 buffer[4]; /* 4 8-bit channels */ 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic const struct reg_default apds9960_reg_defaults[] = { 1518c2ecf20Sopenharmony_ci /* Default ALS integration time = 2.48ms */ 1528c2ecf20Sopenharmony_ci { APDS9960_REG_ATIME, 0xff }, 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic const struct regmap_range apds9960_volatile_ranges[] = { 1568c2ecf20Sopenharmony_ci regmap_reg_range(APDS9960_REG_STATUS, 1578c2ecf20Sopenharmony_ci APDS9960_REG_PDATA), 1588c2ecf20Sopenharmony_ci regmap_reg_range(APDS9960_REG_GFLVL, 1598c2ecf20Sopenharmony_ci APDS9960_REG_GSTATUS), 1608c2ecf20Sopenharmony_ci regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP), 1618c2ecf20Sopenharmony_ci APDS9960_REG_GFIFO_DIR(RIGHT)), 1628c2ecf20Sopenharmony_ci regmap_reg_range(APDS9960_REG_IFORCE, 1638c2ecf20Sopenharmony_ci APDS9960_REG_AICLEAR), 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct regmap_access_table apds9960_volatile_table = { 1678c2ecf20Sopenharmony_ci .yes_ranges = apds9960_volatile_ranges, 1688c2ecf20Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(apds9960_volatile_ranges), 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic const struct regmap_range apds9960_precious_ranges[] = { 1728c2ecf20Sopenharmony_ci regmap_reg_range(APDS9960_REG_RAM_START, APDS9960_REG_RAM_END), 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic const struct regmap_access_table apds9960_precious_table = { 1768c2ecf20Sopenharmony_ci .yes_ranges = apds9960_precious_ranges, 1778c2ecf20Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(apds9960_precious_ranges), 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic const struct regmap_range apds9960_readable_ranges[] = { 1818c2ecf20Sopenharmony_ci regmap_reg_range(APDS9960_REG_ENABLE, 1828c2ecf20Sopenharmony_ci APDS9960_REG_GSTATUS), 1838c2ecf20Sopenharmony_ci regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP), 1848c2ecf20Sopenharmony_ci APDS9960_REG_GFIFO_DIR(RIGHT)), 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const struct regmap_access_table apds9960_readable_table = { 1888c2ecf20Sopenharmony_ci .yes_ranges = apds9960_readable_ranges, 1898c2ecf20Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(apds9960_readable_ranges), 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic const struct regmap_range apds9960_writeable_ranges[] = { 1938c2ecf20Sopenharmony_ci regmap_reg_range(APDS9960_REG_ENABLE, APDS9960_REG_CONFIG_2), 1948c2ecf20Sopenharmony_ci regmap_reg_range(APDS9960_REG_POFFSET_UR, APDS9960_REG_GCONF_4), 1958c2ecf20Sopenharmony_ci regmap_reg_range(APDS9960_REG_IFORCE, APDS9960_REG_AICLEAR), 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic const struct regmap_access_table apds9960_writeable_table = { 1998c2ecf20Sopenharmony_ci .yes_ranges = apds9960_writeable_ranges, 2008c2ecf20Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(apds9960_writeable_ranges), 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic const struct regmap_config apds9960_regmap_config = { 2048c2ecf20Sopenharmony_ci .name = APDS9960_REGMAP_NAME, 2058c2ecf20Sopenharmony_ci .reg_bits = 8, 2068c2ecf20Sopenharmony_ci .val_bits = 8, 2078c2ecf20Sopenharmony_ci .use_single_read = true, 2088c2ecf20Sopenharmony_ci .use_single_write = true, 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci .volatile_table = &apds9960_volatile_table, 2118c2ecf20Sopenharmony_ci .precious_table = &apds9960_precious_table, 2128c2ecf20Sopenharmony_ci .rd_table = &apds9960_readable_table, 2138c2ecf20Sopenharmony_ci .wr_table = &apds9960_writeable_table, 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci .reg_defaults = apds9960_reg_defaults, 2168c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(apds9960_reg_defaults), 2178c2ecf20Sopenharmony_ci .max_register = APDS9960_REG_GFIFO_DIR(RIGHT), 2188c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic const struct iio_event_spec apds9960_pxs_event_spec[] = { 2228c2ecf20Sopenharmony_ci { 2238c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 2248c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 2258c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE) | 2268c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 2278c2ecf20Sopenharmony_ci }, 2288c2ecf20Sopenharmony_ci { 2298c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 2308c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 2318c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE) | 2328c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 2338c2ecf20Sopenharmony_ci }, 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic const struct iio_event_spec apds9960_als_event_spec[] = { 2378c2ecf20Sopenharmony_ci { 2388c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 2398c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 2408c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE) | 2418c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 2428c2ecf20Sopenharmony_ci }, 2438c2ecf20Sopenharmony_ci { 2448c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 2458c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 2468c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE) | 2478c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 2488c2ecf20Sopenharmony_ci }, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci#define APDS9960_GESTURE_CHANNEL(_dir, _si) { \ 2528c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, \ 2538c2ecf20Sopenharmony_ci .channel = _si + 1, \ 2548c2ecf20Sopenharmony_ci .scan_index = _si, \ 2558c2ecf20Sopenharmony_ci .indexed = 1, \ 2568c2ecf20Sopenharmony_ci .scan_type = { \ 2578c2ecf20Sopenharmony_ci .sign = 'u', \ 2588c2ecf20Sopenharmony_ci .realbits = 8, \ 2598c2ecf20Sopenharmony_ci .storagebits = 8, \ 2608c2ecf20Sopenharmony_ci }, \ 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci#define APDS9960_INTENSITY_CHANNEL(_colour) { \ 2648c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, \ 2658c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 2668c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 2678c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), \ 2688c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_##_colour, \ 2698c2ecf20Sopenharmony_ci .address = APDS9960_REG_ALS_CHANNEL(_colour), \ 2708c2ecf20Sopenharmony_ci .modified = 1, \ 2718c2ecf20Sopenharmony_ci .scan_index = -1, \ 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic const unsigned long apds9960_scan_masks[] = {0xf, 0}; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct iio_chan_spec apds9960_channels[] = { 2778c2ecf20Sopenharmony_ci { 2788c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 2798c2ecf20Sopenharmony_ci .address = APDS9960_REG_PDATA, 2808c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 2818c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 2828c2ecf20Sopenharmony_ci .channel = 0, 2838c2ecf20Sopenharmony_ci .indexed = 0, 2848c2ecf20Sopenharmony_ci .scan_index = -1, 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci .event_spec = apds9960_pxs_event_spec, 2878c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(apds9960_pxs_event_spec), 2888c2ecf20Sopenharmony_ci }, 2898c2ecf20Sopenharmony_ci /* Gesture Sensor */ 2908c2ecf20Sopenharmony_ci APDS9960_GESTURE_CHANNEL(UP, 0), 2918c2ecf20Sopenharmony_ci APDS9960_GESTURE_CHANNEL(DOWN, 1), 2928c2ecf20Sopenharmony_ci APDS9960_GESTURE_CHANNEL(LEFT, 2), 2938c2ecf20Sopenharmony_ci APDS9960_GESTURE_CHANNEL(RIGHT, 3), 2948c2ecf20Sopenharmony_ci /* ALS */ 2958c2ecf20Sopenharmony_ci { 2968c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 2978c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 2988c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | 2998c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), 3008c2ecf20Sopenharmony_ci .channel2 = IIO_MOD_LIGHT_CLEAR, 3018c2ecf20Sopenharmony_ci .address = APDS9960_REG_ALS_CHANNEL(CLEAR), 3028c2ecf20Sopenharmony_ci .modified = 1, 3038c2ecf20Sopenharmony_ci .scan_index = -1, 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci .event_spec = apds9960_als_event_spec, 3068c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(apds9960_als_event_spec), 3078c2ecf20Sopenharmony_ci }, 3088c2ecf20Sopenharmony_ci /* RGB Sensor */ 3098c2ecf20Sopenharmony_ci APDS9960_INTENSITY_CHANNEL(RED), 3108c2ecf20Sopenharmony_ci APDS9960_INTENSITY_CHANNEL(GREEN), 3118c2ecf20Sopenharmony_ci APDS9960_INTENSITY_CHANNEL(BLUE), 3128c2ecf20Sopenharmony_ci}; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* integration time in us */ 3158c2ecf20Sopenharmony_cistatic const int apds9960_int_time[][2] = { 3168c2ecf20Sopenharmony_ci { 28000, 246}, 3178c2ecf20Sopenharmony_ci {100000, 219}, 3188c2ecf20Sopenharmony_ci {200000, 182}, 3198c2ecf20Sopenharmony_ci {700000, 0} 3208c2ecf20Sopenharmony_ci}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci/* gain mapping */ 3238c2ecf20Sopenharmony_cistatic const int apds9960_pxs_gain_map[] = {1, 2, 4, 8}; 3248c2ecf20Sopenharmony_cistatic const int apds9960_als_gain_map[] = {1, 4, 16, 64}; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(proximity_scale_available, "1 2 4 8"); 3278c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR(intensity_scale_available, "1 4 16 64"); 3288c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR_INT_TIME_AVAIL("0.028 0.1 0.2 0.7"); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic struct attribute *apds9960_attributes[] = { 3318c2ecf20Sopenharmony_ci &iio_const_attr_proximity_scale_available.dev_attr.attr, 3328c2ecf20Sopenharmony_ci &iio_const_attr_intensity_scale_available.dev_attr.attr, 3338c2ecf20Sopenharmony_ci &iio_const_attr_integration_time_available.dev_attr.attr, 3348c2ecf20Sopenharmony_ci NULL, 3358c2ecf20Sopenharmony_ci}; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic const struct attribute_group apds9960_attribute_group = { 3388c2ecf20Sopenharmony_ci .attrs = apds9960_attributes, 3398c2ecf20Sopenharmony_ci}; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_int_als = 3428c2ecf20Sopenharmony_ci REG_FIELD(APDS9960_REG_ENABLE, 4, 4); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_int_ges = 3458c2ecf20Sopenharmony_ci REG_FIELD(APDS9960_REG_GCONF_4, 1, 1); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_int_pxs = 3488c2ecf20Sopenharmony_ci REG_FIELD(APDS9960_REG_ENABLE, 5, 5); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_enable_als = 3518c2ecf20Sopenharmony_ci REG_FIELD(APDS9960_REG_ENABLE, 1, 1); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_enable_ges = 3548c2ecf20Sopenharmony_ci REG_FIELD(APDS9960_REG_ENABLE, 6, 6); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic const struct reg_field apds9960_reg_field_enable_pxs = 3578c2ecf20Sopenharmony_ci REG_FIELD(APDS9960_REG_ENABLE, 2, 2); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int apds9960_set_it_time(struct apds9960_data *data, int val2) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci int ret = -EINVAL; 3628c2ecf20Sopenharmony_ci int idx; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(apds9960_int_time); idx++) { 3658c2ecf20Sopenharmony_ci if (apds9960_int_time[idx][0] == val2) { 3668c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 3678c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, APDS9960_REG_ATIME, 3688c2ecf20Sopenharmony_ci apds9960_int_time[idx][1]); 3698c2ecf20Sopenharmony_ci if (!ret) 3708c2ecf20Sopenharmony_ci data->als_adc_int_us = val2; 3718c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 3728c2ecf20Sopenharmony_ci break; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return ret; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int apds9960_set_pxs_gain(struct apds9960_data *data, int val) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci int ret = -EINVAL; 3828c2ecf20Sopenharmony_ci int idx; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(apds9960_pxs_gain_map); idx++) { 3858c2ecf20Sopenharmony_ci if (apds9960_pxs_gain_map[idx] == val) { 3868c2ecf20Sopenharmony_ci /* pxs + gesture gains are mirrored */ 3878c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 3888c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 3898c2ecf20Sopenharmony_ci APDS9960_REG_CONTROL, 3908c2ecf20Sopenharmony_ci APDS9960_REG_CONTROL_PGAIN_MASK, 3918c2ecf20Sopenharmony_ci idx << APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT); 3928c2ecf20Sopenharmony_ci if (ret) { 3938c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 3988c2ecf20Sopenharmony_ci APDS9960_REG_GCONF_2, 3998c2ecf20Sopenharmony_ci APDS9960_REG_GCONF_2_GGAIN_MASK, 4008c2ecf20Sopenharmony_ci idx << APDS9960_REG_GCONF_2_GGAIN_MASK_SHIFT); 4018c2ecf20Sopenharmony_ci if (!ret) 4028c2ecf20Sopenharmony_ci data->pxs_gain = idx; 4038c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return ret; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int apds9960_set_als_gain(struct apds9960_data *data, int val) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci int ret = -EINVAL; 4148c2ecf20Sopenharmony_ci int idx; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(apds9960_als_gain_map); idx++) { 4178c2ecf20Sopenharmony_ci if (apds9960_als_gain_map[idx] == val) { 4188c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 4198c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, 4208c2ecf20Sopenharmony_ci APDS9960_REG_CONTROL, 4218c2ecf20Sopenharmony_ci APDS9960_REG_CONTROL_AGAIN_MASK, idx); 4228c2ecf20Sopenharmony_ci if (!ret) 4238c2ecf20Sopenharmony_ci data->als_gain = idx; 4248c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return ret; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4338c2ecf20Sopenharmony_cistatic int apds9960_set_power_state(struct apds9960_data *data, bool on) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct device *dev = &data->client->dev; 4368c2ecf20Sopenharmony_ci int ret = 0; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (on) { 4418c2ecf20Sopenharmony_ci int suspended; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci suspended = pm_runtime_suspended(dev); 4448c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Allow one integration cycle before allowing a reading */ 4478c2ecf20Sopenharmony_ci if (suspended) 4488c2ecf20Sopenharmony_ci usleep_range(data->als_adc_int_us, 4498c2ecf20Sopenharmony_ci APDS9960_MAX_INT_TIME_IN_US); 4508c2ecf20Sopenharmony_ci } else { 4518c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 4528c2ecf20Sopenharmony_ci ret = pm_runtime_put_autosuspend(dev); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return ret; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci#else 4608c2ecf20Sopenharmony_cistatic int apds9960_set_power_state(struct apds9960_data *data, bool on) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci return 0; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci#endif 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic int apds9960_read_raw(struct iio_dev *indio_dev, 4678c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 4688c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 4718c2ecf20Sopenharmony_ci __le16 buf; 4728c2ecf20Sopenharmony_ci int ret = -EINVAL; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (data->gesture_mode_running) 4758c2ecf20Sopenharmony_ci return -EBUSY; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci switch (mask) { 4788c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 4798c2ecf20Sopenharmony_ci apds9960_set_power_state(data, true); 4808c2ecf20Sopenharmony_ci switch (chan->type) { 4818c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 4828c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, chan->address, val); 4838c2ecf20Sopenharmony_ci if (!ret) 4848c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci case IIO_INTENSITY: 4878c2ecf20Sopenharmony_ci ret = regmap_bulk_read(data->regmap, chan->address, 4888c2ecf20Sopenharmony_ci &buf, 2); 4898c2ecf20Sopenharmony_ci if (!ret) { 4908c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 4918c2ecf20Sopenharmony_ci *val = le16_to_cpu(buf); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci default: 4958c2ecf20Sopenharmony_ci ret = -EINVAL; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci apds9960_set_power_state(data, false); 4988c2ecf20Sopenharmony_ci break; 4998c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 5008c2ecf20Sopenharmony_ci /* RGB + ALS sensors only have integration time */ 5018c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 5028c2ecf20Sopenharmony_ci switch (chan->type) { 5038c2ecf20Sopenharmony_ci case IIO_INTENSITY: 5048c2ecf20Sopenharmony_ci *val = 0; 5058c2ecf20Sopenharmony_ci *val2 = data->als_adc_int_us; 5068c2ecf20Sopenharmony_ci ret = IIO_VAL_INT_PLUS_MICRO; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci default: 5098c2ecf20Sopenharmony_ci ret = -EINVAL; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 5128c2ecf20Sopenharmony_ci break; 5138c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 5148c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 5158c2ecf20Sopenharmony_ci switch (chan->type) { 5168c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 5178c2ecf20Sopenharmony_ci *val = apds9960_pxs_gain_map[data->pxs_gain]; 5188c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci case IIO_INTENSITY: 5218c2ecf20Sopenharmony_ci *val = apds9960_als_gain_map[data->als_gain]; 5228c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci default: 5258c2ecf20Sopenharmony_ci ret = -EINVAL; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return ret; 5328c2ecf20Sopenharmony_ci}; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int apds9960_write_raw(struct iio_dev *indio_dev, 5358c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 5368c2ecf20Sopenharmony_ci int val, int val2, long mask) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci switch (mask) { 5418c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 5428c2ecf20Sopenharmony_ci /* RGB + ALS sensors only have int time */ 5438c2ecf20Sopenharmony_ci switch (chan->type) { 5448c2ecf20Sopenharmony_ci case IIO_INTENSITY: 5458c2ecf20Sopenharmony_ci if (val != 0) 5468c2ecf20Sopenharmony_ci return -EINVAL; 5478c2ecf20Sopenharmony_ci return apds9960_set_it_time(data, val2); 5488c2ecf20Sopenharmony_ci default: 5498c2ecf20Sopenharmony_ci return -EINVAL; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 5528c2ecf20Sopenharmony_ci if (val2 != 0) 5538c2ecf20Sopenharmony_ci return -EINVAL; 5548c2ecf20Sopenharmony_ci switch (chan->type) { 5558c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 5568c2ecf20Sopenharmony_ci return apds9960_set_pxs_gain(data, val); 5578c2ecf20Sopenharmony_ci case IIO_INTENSITY: 5588c2ecf20Sopenharmony_ci return apds9960_set_als_gain(data, val); 5598c2ecf20Sopenharmony_ci default: 5608c2ecf20Sopenharmony_ci return -EINVAL; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci default: 5638c2ecf20Sopenharmony_ci return -EINVAL; 5648c2ecf20Sopenharmony_ci }; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci return 0; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic inline int apds9960_get_thres_reg(const struct iio_chan_spec *chan, 5708c2ecf20Sopenharmony_ci enum iio_event_direction dir, 5718c2ecf20Sopenharmony_ci u8 *reg) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci switch (dir) { 5748c2ecf20Sopenharmony_ci case IIO_EV_DIR_RISING: 5758c2ecf20Sopenharmony_ci switch (chan->type) { 5768c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 5778c2ecf20Sopenharmony_ci *reg = APDS9960_REG_PIHT; 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci case IIO_INTENSITY: 5808c2ecf20Sopenharmony_ci *reg = APDS9960_REG_AIHTL; 5818c2ecf20Sopenharmony_ci break; 5828c2ecf20Sopenharmony_ci default: 5838c2ecf20Sopenharmony_ci return -EINVAL; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci break; 5868c2ecf20Sopenharmony_ci case IIO_EV_DIR_FALLING: 5878c2ecf20Sopenharmony_ci switch (chan->type) { 5888c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 5898c2ecf20Sopenharmony_ci *reg = APDS9960_REG_PILT; 5908c2ecf20Sopenharmony_ci break; 5918c2ecf20Sopenharmony_ci case IIO_INTENSITY: 5928c2ecf20Sopenharmony_ci *reg = APDS9960_REG_AILTL; 5938c2ecf20Sopenharmony_ci break; 5948c2ecf20Sopenharmony_ci default: 5958c2ecf20Sopenharmony_ci return -EINVAL; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci default: 5998c2ecf20Sopenharmony_ci return -EINVAL; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci return 0; 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic int apds9960_read_event(struct iio_dev *indio_dev, 6068c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 6078c2ecf20Sopenharmony_ci enum iio_event_type type, 6088c2ecf20Sopenharmony_ci enum iio_event_direction dir, 6098c2ecf20Sopenharmony_ci enum iio_event_info info, 6108c2ecf20Sopenharmony_ci int *val, int *val2) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci u8 reg; 6138c2ecf20Sopenharmony_ci __le16 buf; 6148c2ecf20Sopenharmony_ci int ret = 0; 6158c2ecf20Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (info != IIO_EV_INFO_VALUE) 6188c2ecf20Sopenharmony_ci return -EINVAL; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci ret = apds9960_get_thres_reg(chan, dir, ®); 6218c2ecf20Sopenharmony_ci if (ret < 0) 6228c2ecf20Sopenharmony_ci return ret; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (chan->type == IIO_PROXIMITY) { 6258c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, reg, val); 6268c2ecf20Sopenharmony_ci if (ret < 0) 6278c2ecf20Sopenharmony_ci return ret; 6288c2ecf20Sopenharmony_ci } else if (chan->type == IIO_INTENSITY) { 6298c2ecf20Sopenharmony_ci ret = regmap_bulk_read(data->regmap, reg, &buf, 2); 6308c2ecf20Sopenharmony_ci if (ret < 0) 6318c2ecf20Sopenharmony_ci return ret; 6328c2ecf20Sopenharmony_ci *val = le16_to_cpu(buf); 6338c2ecf20Sopenharmony_ci } else 6348c2ecf20Sopenharmony_ci return -EINVAL; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci *val2 = 0; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return IIO_VAL_INT; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic int apds9960_write_event(struct iio_dev *indio_dev, 6428c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 6438c2ecf20Sopenharmony_ci enum iio_event_type type, 6448c2ecf20Sopenharmony_ci enum iio_event_direction dir, 6458c2ecf20Sopenharmony_ci enum iio_event_info info, 6468c2ecf20Sopenharmony_ci int val, int val2) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci u8 reg; 6498c2ecf20Sopenharmony_ci __le16 buf; 6508c2ecf20Sopenharmony_ci int ret = 0; 6518c2ecf20Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (info != IIO_EV_INFO_VALUE) 6548c2ecf20Sopenharmony_ci return -EINVAL; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci ret = apds9960_get_thres_reg(chan, dir, ®); 6578c2ecf20Sopenharmony_ci if (ret < 0) 6588c2ecf20Sopenharmony_ci return ret; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (chan->type == IIO_PROXIMITY) { 6618c2ecf20Sopenharmony_ci if (val < 0 || val > APDS9960_MAX_PXS_THRES_VAL) 6628c2ecf20Sopenharmony_ci return -EINVAL; 6638c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, reg, val); 6648c2ecf20Sopenharmony_ci if (ret < 0) 6658c2ecf20Sopenharmony_ci return ret; 6668c2ecf20Sopenharmony_ci } else if (chan->type == IIO_INTENSITY) { 6678c2ecf20Sopenharmony_ci if (val < 0 || val > APDS9960_MAX_ALS_THRES_VAL) 6688c2ecf20Sopenharmony_ci return -EINVAL; 6698c2ecf20Sopenharmony_ci buf = cpu_to_le16(val); 6708c2ecf20Sopenharmony_ci ret = regmap_bulk_write(data->regmap, reg, &buf, 2); 6718c2ecf20Sopenharmony_ci if (ret < 0) 6728c2ecf20Sopenharmony_ci return ret; 6738c2ecf20Sopenharmony_ci } else 6748c2ecf20Sopenharmony_ci return -EINVAL; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci return 0; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic int apds9960_read_event_config(struct iio_dev *indio_dev, 6808c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 6818c2ecf20Sopenharmony_ci enum iio_event_type type, 6828c2ecf20Sopenharmony_ci enum iio_event_direction dir) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci switch (chan->type) { 6878c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 6888c2ecf20Sopenharmony_ci return data->pxs_int; 6898c2ecf20Sopenharmony_ci case IIO_INTENSITY: 6908c2ecf20Sopenharmony_ci return data->als_int; 6918c2ecf20Sopenharmony_ci default: 6928c2ecf20Sopenharmony_ci return -EINVAL; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci return 0; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic int apds9960_write_event_config(struct iio_dev *indio_dev, 6998c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 7008c2ecf20Sopenharmony_ci enum iio_event_type type, 7018c2ecf20Sopenharmony_ci enum iio_event_direction dir, 7028c2ecf20Sopenharmony_ci int state) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 7058c2ecf20Sopenharmony_ci int ret; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci state = !!state; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci switch (chan->type) { 7108c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 7118c2ecf20Sopenharmony_ci if (data->pxs_int == state) 7128c2ecf20Sopenharmony_ci return -EINVAL; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_int_pxs, state); 7158c2ecf20Sopenharmony_ci if (ret) 7168c2ecf20Sopenharmony_ci return ret; 7178c2ecf20Sopenharmony_ci data->pxs_int = state; 7188c2ecf20Sopenharmony_ci apds9960_set_power_state(data, state); 7198c2ecf20Sopenharmony_ci break; 7208c2ecf20Sopenharmony_ci case IIO_INTENSITY: 7218c2ecf20Sopenharmony_ci if (data->als_int == state) 7228c2ecf20Sopenharmony_ci return -EINVAL; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_int_als, state); 7258c2ecf20Sopenharmony_ci if (ret) 7268c2ecf20Sopenharmony_ci return ret; 7278c2ecf20Sopenharmony_ci data->als_int = state; 7288c2ecf20Sopenharmony_ci apds9960_set_power_state(data, state); 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci default: 7318c2ecf20Sopenharmony_ci return -EINVAL; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci return 0; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic const struct iio_info apds9960_info = { 7388c2ecf20Sopenharmony_ci .attrs = &apds9960_attribute_group, 7398c2ecf20Sopenharmony_ci .read_raw = apds9960_read_raw, 7408c2ecf20Sopenharmony_ci .write_raw = apds9960_write_raw, 7418c2ecf20Sopenharmony_ci .read_event_value = apds9960_read_event, 7428c2ecf20Sopenharmony_ci .write_event_value = apds9960_write_event, 7438c2ecf20Sopenharmony_ci .read_event_config = apds9960_read_event_config, 7448c2ecf20Sopenharmony_ci .write_event_config = apds9960_write_event_config, 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci}; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic inline int apds9660_fifo_is_empty(struct apds9960_data *data) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci int cnt; 7518c2ecf20Sopenharmony_ci int ret; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, APDS9960_REG_GFLVL, &cnt); 7548c2ecf20Sopenharmony_ci if (ret) 7558c2ecf20Sopenharmony_ci return ret; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci return cnt; 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic void apds9960_read_gesture_fifo(struct apds9960_data *data) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci int ret, cnt = 0; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 7658c2ecf20Sopenharmony_ci data->gesture_mode_running = 1; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci while (cnt || (cnt = apds9660_fifo_is_empty(data) > 0)) { 7688c2ecf20Sopenharmony_ci ret = regmap_bulk_read(data->regmap, APDS9960_REG_GFIFO_BASE, 7698c2ecf20Sopenharmony_ci &data->buffer, 4); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (ret) 7728c2ecf20Sopenharmony_ci goto err_read; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci iio_push_to_buffers(data->indio_dev, data->buffer); 7758c2ecf20Sopenharmony_ci cnt--; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cierr_read: 7798c2ecf20Sopenharmony_ci data->gesture_mode_running = 0; 7808c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_cistatic irqreturn_t apds9960_interrupt_handler(int irq, void *private) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = private; 7868c2ecf20Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 7878c2ecf20Sopenharmony_ci int ret, status; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci ret = regmap_read(data->regmap, APDS9960_REG_STATUS, &status); 7908c2ecf20Sopenharmony_ci if (ret < 0) { 7918c2ecf20Sopenharmony_ci dev_err(&data->client->dev, "irq status reg read failed\n"); 7928c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if ((status & APDS9960_REG_STATUS_ALS_INT) && data->als_int) { 7968c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 7978c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, 7988c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 7998c2ecf20Sopenharmony_ci IIO_EV_DIR_EITHER), 8008c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 8018c2ecf20Sopenharmony_ci regmap_write(data->regmap, APDS9960_REG_CICLEAR, 1); 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if ((status & APDS9960_REG_STATUS_PS_INT) && data->pxs_int) { 8058c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 8068c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 8078c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 8088c2ecf20Sopenharmony_ci IIO_EV_DIR_EITHER), 8098c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 8108c2ecf20Sopenharmony_ci regmap_write(data->regmap, APDS9960_REG_PICLEAR, 1); 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (status & APDS9960_REG_STATUS_GINT) 8148c2ecf20Sopenharmony_ci apds9960_read_gesture_fifo(data); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic int apds9960_set_powermode(struct apds9960_data *data, bool state) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci return regmap_update_bits(data->regmap, APDS9960_REG_ENABLE, 1, state); 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic int apds9960_buffer_postenable(struct iio_dev *indio_dev) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 8278c2ecf20Sopenharmony_ci int ret; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_int_ges, 1); 8308c2ecf20Sopenharmony_ci if (ret) 8318c2ecf20Sopenharmony_ci return ret; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_enable_ges, 1); 8348c2ecf20Sopenharmony_ci if (ret) 8358c2ecf20Sopenharmony_ci return ret; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci pm_runtime_get_sync(&data->client->dev); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci return 0; 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_cistatic int apds9960_buffer_predisable(struct iio_dev *indio_dev) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 8458c2ecf20Sopenharmony_ci int ret; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_enable_ges, 0); 8488c2ecf20Sopenharmony_ci if (ret) 8498c2ecf20Sopenharmony_ci return ret; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_int_ges, 0); 8528c2ecf20Sopenharmony_ci if (ret) 8538c2ecf20Sopenharmony_ci return ret; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&data->client->dev); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci return 0; 8588c2ecf20Sopenharmony_ci} 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_cistatic const struct iio_buffer_setup_ops apds9960_buffer_setup_ops = { 8618c2ecf20Sopenharmony_ci .postenable = apds9960_buffer_postenable, 8628c2ecf20Sopenharmony_ci .predisable = apds9960_buffer_predisable, 8638c2ecf20Sopenharmony_ci}; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic int apds9960_regfield_init(struct apds9960_data *data) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct device *dev = &data->client->dev; 8688c2ecf20Sopenharmony_ci struct regmap *regmap = data->regmap; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci data->reg_int_als = devm_regmap_field_alloc(dev, regmap, 8718c2ecf20Sopenharmony_ci apds9960_reg_field_int_als); 8728c2ecf20Sopenharmony_ci if (IS_ERR(data->reg_int_als)) { 8738c2ecf20Sopenharmony_ci dev_err(dev, "INT ALS reg field init failed\n"); 8748c2ecf20Sopenharmony_ci return PTR_ERR(data->reg_int_als); 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci data->reg_int_ges = devm_regmap_field_alloc(dev, regmap, 8788c2ecf20Sopenharmony_ci apds9960_reg_field_int_ges); 8798c2ecf20Sopenharmony_ci if (IS_ERR(data->reg_int_ges)) { 8808c2ecf20Sopenharmony_ci dev_err(dev, "INT gesture reg field init failed\n"); 8818c2ecf20Sopenharmony_ci return PTR_ERR(data->reg_int_ges); 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci data->reg_int_pxs = devm_regmap_field_alloc(dev, regmap, 8858c2ecf20Sopenharmony_ci apds9960_reg_field_int_pxs); 8868c2ecf20Sopenharmony_ci if (IS_ERR(data->reg_int_pxs)) { 8878c2ecf20Sopenharmony_ci dev_err(dev, "INT pxs reg field init failed\n"); 8888c2ecf20Sopenharmony_ci return PTR_ERR(data->reg_int_pxs); 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci data->reg_enable_als = devm_regmap_field_alloc(dev, regmap, 8928c2ecf20Sopenharmony_ci apds9960_reg_field_enable_als); 8938c2ecf20Sopenharmony_ci if (IS_ERR(data->reg_enable_als)) { 8948c2ecf20Sopenharmony_ci dev_err(dev, "Enable ALS reg field init failed\n"); 8958c2ecf20Sopenharmony_ci return PTR_ERR(data->reg_enable_als); 8968c2ecf20Sopenharmony_ci } 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci data->reg_enable_ges = devm_regmap_field_alloc(dev, regmap, 8998c2ecf20Sopenharmony_ci apds9960_reg_field_enable_ges); 9008c2ecf20Sopenharmony_ci if (IS_ERR(data->reg_enable_ges)) { 9018c2ecf20Sopenharmony_ci dev_err(dev, "Enable gesture reg field init failed\n"); 9028c2ecf20Sopenharmony_ci return PTR_ERR(data->reg_enable_ges); 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci data->reg_enable_pxs = devm_regmap_field_alloc(dev, regmap, 9068c2ecf20Sopenharmony_ci apds9960_reg_field_enable_pxs); 9078c2ecf20Sopenharmony_ci if (IS_ERR(data->reg_enable_pxs)) { 9088c2ecf20Sopenharmony_ci dev_err(dev, "Enable PXS reg field init failed\n"); 9098c2ecf20Sopenharmony_ci return PTR_ERR(data->reg_enable_pxs); 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci return 0; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic int apds9960_chip_init(struct apds9960_data *data) 9168c2ecf20Sopenharmony_ci{ 9178c2ecf20Sopenharmony_ci int ret; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* Default IT for ALS of 28 ms */ 9208c2ecf20Sopenharmony_ci ret = apds9960_set_it_time(data, 28000); 9218c2ecf20Sopenharmony_ci if (ret) 9228c2ecf20Sopenharmony_ci return ret; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* Ensure gesture interrupt is OFF */ 9258c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_int_ges, 0); 9268c2ecf20Sopenharmony_ci if (ret) 9278c2ecf20Sopenharmony_ci return ret; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* Disable gesture sensor, since polling is useless from user-space */ 9308c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_enable_ges, 0); 9318c2ecf20Sopenharmony_ci if (ret) 9328c2ecf20Sopenharmony_ci return ret; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* Ensure proximity interrupt is OFF */ 9358c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_int_pxs, 0); 9368c2ecf20Sopenharmony_ci if (ret) 9378c2ecf20Sopenharmony_ci return ret; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* Enable proximity sensor for polling */ 9408c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_enable_pxs, 1); 9418c2ecf20Sopenharmony_ci if (ret) 9428c2ecf20Sopenharmony_ci return ret; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* Ensure ALS interrupt is OFF */ 9458c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_int_als, 0); 9468c2ecf20Sopenharmony_ci if (ret) 9478c2ecf20Sopenharmony_ci return ret; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci /* Enable ALS sensor for polling */ 9508c2ecf20Sopenharmony_ci ret = regmap_field_write(data->reg_enable_als, 1); 9518c2ecf20Sopenharmony_ci if (ret) 9528c2ecf20Sopenharmony_ci return ret; 9538c2ecf20Sopenharmony_ci /* 9548c2ecf20Sopenharmony_ci * When enabled trigger an interrupt after 3 readings 9558c2ecf20Sopenharmony_ci * outside threshold for ALS + PXS 9568c2ecf20Sopenharmony_ci */ 9578c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, APDS9960_REG_PERS, 9588c2ecf20Sopenharmony_ci APDS9960_DEFAULT_PERS); 9598c2ecf20Sopenharmony_ci if (ret) 9608c2ecf20Sopenharmony_ci return ret; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci /* 9638c2ecf20Sopenharmony_ci * Wait for 4 event outside gesture threshold to prevent interrupt 9648c2ecf20Sopenharmony_ci * flooding. 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_ci ret = regmap_update_bits(data->regmap, APDS9960_REG_GCONF_1, 9678c2ecf20Sopenharmony_ci APDS9960_REG_GCONF_1_GFIFO_THRES_MASK, 9688c2ecf20Sopenharmony_ci BIT(0) << APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT); 9698c2ecf20Sopenharmony_ci if (ret) 9708c2ecf20Sopenharmony_ci return ret; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* Default ENTER and EXIT thresholds for the GESTURE engine. */ 9738c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, APDS9960_REG_GPENTH, 9748c2ecf20Sopenharmony_ci APDS9960_DEFAULT_GPENTH); 9758c2ecf20Sopenharmony_ci if (ret) 9768c2ecf20Sopenharmony_ci return ret; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci ret = regmap_write(data->regmap, APDS9960_REG_GEXTH, 9798c2ecf20Sopenharmony_ci APDS9960_DEFAULT_GEXTH); 9808c2ecf20Sopenharmony_ci if (ret) 9818c2ecf20Sopenharmony_ci return ret; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci return apds9960_set_powermode(data, 1); 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cistatic int apds9960_probe(struct i2c_client *client, 9878c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci struct apds9960_data *data; 9908c2ecf20Sopenharmony_ci struct iio_buffer *buffer; 9918c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 9928c2ecf20Sopenharmony_ci int ret; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 9958c2ecf20Sopenharmony_ci if (!indio_dev) 9968c2ecf20Sopenharmony_ci return -ENOMEM; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci buffer = devm_iio_kfifo_allocate(&client->dev); 9998c2ecf20Sopenharmony_ci if (!buffer) 10008c2ecf20Sopenharmony_ci return -ENOMEM; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci iio_device_attach_buffer(indio_dev, buffer); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci indio_dev->info = &apds9960_info; 10058c2ecf20Sopenharmony_ci indio_dev->name = APDS9960_DRV_NAME; 10068c2ecf20Sopenharmony_ci indio_dev->channels = apds9960_channels; 10078c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(apds9960_channels); 10088c2ecf20Sopenharmony_ci indio_dev->available_scan_masks = apds9960_scan_masks; 10098c2ecf20Sopenharmony_ci indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); 10108c2ecf20Sopenharmony_ci indio_dev->setup_ops = &apds9960_buffer_setup_ops; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 10138c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci data->regmap = devm_regmap_init_i2c(client, &apds9960_regmap_config); 10168c2ecf20Sopenharmony_ci if (IS_ERR(data->regmap)) { 10178c2ecf20Sopenharmony_ci dev_err(&client->dev, "regmap initialization failed.\n"); 10188c2ecf20Sopenharmony_ci return PTR_ERR(data->regmap); 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci data->client = client; 10228c2ecf20Sopenharmony_ci data->indio_dev = indio_dev; 10238c2ecf20Sopenharmony_ci mutex_init(&data->lock); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci ret = pm_runtime_set_active(&client->dev); 10268c2ecf20Sopenharmony_ci if (ret) 10278c2ecf20Sopenharmony_ci goto error_power_down; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci pm_runtime_enable(&client->dev); 10308c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&client->dev, 5000); 10318c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&client->dev); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci apds9960_set_power_state(data, true); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci ret = apds9960_regfield_init(data); 10368c2ecf20Sopenharmony_ci if (ret) 10378c2ecf20Sopenharmony_ci goto error_power_down; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci ret = apds9960_chip_init(data); 10408c2ecf20Sopenharmony_ci if (ret) 10418c2ecf20Sopenharmony_ci goto error_power_down; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (client->irq <= 0) { 10448c2ecf20Sopenharmony_ci dev_err(&client->dev, "no valid irq defined\n"); 10458c2ecf20Sopenharmony_ci ret = -EINVAL; 10468c2ecf20Sopenharmony_ci goto error_power_down; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, 10498c2ecf20Sopenharmony_ci NULL, apds9960_interrupt_handler, 10508c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 10518c2ecf20Sopenharmony_ci "apds9960_event", 10528c2ecf20Sopenharmony_ci indio_dev); 10538c2ecf20Sopenharmony_ci if (ret) { 10548c2ecf20Sopenharmony_ci dev_err(&client->dev, "request irq (%d) failed\n", client->irq); 10558c2ecf20Sopenharmony_ci goto error_power_down; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci ret = iio_device_register(indio_dev); 10598c2ecf20Sopenharmony_ci if (ret) 10608c2ecf20Sopenharmony_ci goto error_power_down; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci apds9960_set_power_state(data, false); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci return 0; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_cierror_power_down: 10678c2ecf20Sopenharmony_ci apds9960_set_power_state(data, false); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci return ret; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic int apds9960_remove(struct i2c_client *client) 10738c2ecf20Sopenharmony_ci{ 10748c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 10758c2ecf20Sopenharmony_ci struct apds9960_data *data = iio_priv(indio_dev); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 10788c2ecf20Sopenharmony_ci pm_runtime_disable(&client->dev); 10798c2ecf20Sopenharmony_ci pm_runtime_set_suspended(&client->dev); 10808c2ecf20Sopenharmony_ci apds9960_set_powermode(data, 0); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci return 0; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 10868c2ecf20Sopenharmony_cistatic int apds9960_runtime_suspend(struct device *dev) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci struct apds9960_data *data = 10898c2ecf20Sopenharmony_ci iio_priv(i2c_get_clientdata(to_i2c_client(dev))); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci return apds9960_set_powermode(data, 0); 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic int apds9960_runtime_resume(struct device *dev) 10958c2ecf20Sopenharmony_ci{ 10968c2ecf20Sopenharmony_ci struct apds9960_data *data = 10978c2ecf20Sopenharmony_ci iio_priv(i2c_get_clientdata(to_i2c_client(dev))); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci return apds9960_set_powermode(data, 1); 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci#endif 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic const struct dev_pm_ops apds9960_pm_ops = { 11048c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 11058c2ecf20Sopenharmony_ci pm_runtime_force_resume) 11068c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(apds9960_runtime_suspend, 11078c2ecf20Sopenharmony_ci apds9960_runtime_resume, NULL) 11088c2ecf20Sopenharmony_ci}; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_cistatic const struct i2c_device_id apds9960_id[] = { 11118c2ecf20Sopenharmony_ci { "apds9960", 0 }, 11128c2ecf20Sopenharmony_ci {} 11138c2ecf20Sopenharmony_ci}; 11148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, apds9960_id); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_cistatic const struct of_device_id apds9960_of_match[] = { 11178c2ecf20Sopenharmony_ci { .compatible = "avago,apds9960" }, 11188c2ecf20Sopenharmony_ci { } 11198c2ecf20Sopenharmony_ci}; 11208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, apds9960_of_match); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_cistatic struct i2c_driver apds9960_driver = { 11238c2ecf20Sopenharmony_ci .driver = { 11248c2ecf20Sopenharmony_ci .name = APDS9960_DRV_NAME, 11258c2ecf20Sopenharmony_ci .of_match_table = apds9960_of_match, 11268c2ecf20Sopenharmony_ci .pm = &apds9960_pm_ops, 11278c2ecf20Sopenharmony_ci }, 11288c2ecf20Sopenharmony_ci .probe = apds9960_probe, 11298c2ecf20Sopenharmony_ci .remove = apds9960_remove, 11308c2ecf20Sopenharmony_ci .id_table = apds9960_id, 11318c2ecf20Sopenharmony_ci}; 11328c2ecf20Sopenharmony_cimodule_i2c_driver(apds9960_driver); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); 11358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("APDS9960 Gesture/RGB/ALS/Proximity sensor"); 11368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1137