136edc939SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 262a1efb9SPeter Meerwald /* 35a441aadSAngus Ainslie (Purism) * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4040/4200 combined ambient 4d978bfddSPeter Meerwald-Stadler * light and proximity sensor 562a1efb9SPeter Meerwald * 662a1efb9SPeter Meerwald * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net> 75a441aadSAngus Ainslie (Purism) * Copyright 2019 Pursim SPC 88fe78d52SMathieu Othacehe * Copyright 2020 Mathieu Othacehe <m.othacehe@gmail.com> 962a1efb9SPeter Meerwald * 10be38866fSTomas Novotny * IIO driver for: 11be38866fSTomas Novotny * VCNL4000/10/20 (7-bit I2C slave address 0x13) 125a441aadSAngus Ainslie (Purism) * VCNL4040 (7-bit I2C slave address 0x60) 13be38866fSTomas Novotny * VCNL4200 (7-bit I2C slave address 0x51) 1462a1efb9SPeter Meerwald * 1562a1efb9SPeter Meerwald * TODO: 1662a1efb9SPeter Meerwald * allow to adjust IR current 178fe78d52SMathieu Othacehe * interrupts (VCNL4040, VCNL4200) 1862a1efb9SPeter Meerwald */ 1962a1efb9SPeter Meerwald 2085e2c6a2SMårten Lindahl #include <linux/bitfield.h> 2162a1efb9SPeter Meerwald #include <linux/module.h> 2262a1efb9SPeter Meerwald #include <linux/i2c.h> 2362a1efb9SPeter Meerwald #include <linux/err.h> 2462a1efb9SPeter Meerwald #include <linux/delay.h> 255e00708dSGuido Günther #include <linux/pm_runtime.h> 26d35567fcSMathieu Othacehe #include <linux/interrupt.h> 277f865127SAstrid Rost #include <linux/units.h> 2862a1efb9SPeter Meerwald 298fe78d52SMathieu Othacehe #include <linux/iio/buffer.h> 30d35567fcSMathieu Othacehe #include <linux/iio/events.h> 3162a1efb9SPeter Meerwald #include <linux/iio/iio.h> 3262a1efb9SPeter Meerwald #include <linux/iio/sysfs.h> 338fe78d52SMathieu Othacehe #include <linux/iio/trigger.h> 348fe78d52SMathieu Othacehe #include <linux/iio/trigger_consumer.h> 358fe78d52SMathieu Othacehe #include <linux/iio/triggered_buffer.h> 3662a1efb9SPeter Meerwald 3762a1efb9SPeter Meerwald #define VCNL4000_DRV_NAME "vcnl4000" 381ebc787aSTomas Novotny #define VCNL4000_PROD_ID 0x01 391ebc787aSTomas Novotny #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ 405a441aadSAngus Ainslie (Purism) #define VCNL4040_PROD_ID 0x86 41be38866fSTomas Novotny #define VCNL4200_PROD_ID 0x58 4262a1efb9SPeter Meerwald 4362a1efb9SPeter Meerwald #define VCNL4000_COMMAND 0x80 /* Command register */ 4462a1efb9SPeter Meerwald #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 45d35567fcSMathieu Othacehe #define VCNL4010_PROX_RATE 0x82 /* Proximity rate */ 4662a1efb9SPeter Meerwald #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 4762a1efb9SPeter Meerwald #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 48d35567fcSMathieu Othacehe #define VCNL4010_ALS_PARAM 0x84 /* ALS rate */ 4962a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 5062a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 5162a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 5262a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 5362a1efb9SPeter Meerwald #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 54d35567fcSMathieu Othacehe #define VCNL4010_INT_CTRL 0x89 /* Interrupt control */ 5562a1efb9SPeter Meerwald #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 56d35567fcSMathieu Othacehe #define VCNL4010_LOW_THR_HI 0x8a /* Low threshold, MSB */ 57d35567fcSMathieu Othacehe #define VCNL4010_LOW_THR_LO 0x8b /* Low threshold, LSB */ 58d35567fcSMathieu Othacehe #define VCNL4010_HIGH_THR_HI 0x8c /* High threshold, MSB */ 59d35567fcSMathieu Othacehe #define VCNL4010_HIGH_THR_LO 0x8d /* High threshold, LSB */ 60d35567fcSMathieu Othacehe #define VCNL4010_ISR 0x8e /* Interrupt status */ 6162a1efb9SPeter Meerwald 62be38866fSTomas Novotny #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ 63be38866fSTomas Novotny #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ 64*add98466SAstrid Rost #define VCNL4200_PS_CONF3 0x04 /* Proximity configuration */ 6554667612SMårten Lindahl #define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */ 6654667612SMårten Lindahl #define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */ 67bc292aafSAstrid Rost #define VCNL4040_ALS_THDL_LM 0x02 /* Ambient light threshold low */ 68bc292aafSAstrid Rost #define VCNL4040_ALS_THDH_LM 0x01 /* Ambient light threshold high */ 69be38866fSTomas Novotny #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 70be38866fSTomas Novotny #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 7154667612SMårten Lindahl #define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */ 72854965b7SAstrid Rost #define VCNL4200_INT_FLAGS 0x0d /* Interrupt register */ 73be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 74be38866fSTomas Novotny 755a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 765a441aadSAngus Ainslie (Purism) 7762a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 78ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 79ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 80ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 81ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 82d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 83d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 84d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 85d35567fcSMathieu Othacehe 86e21b5b1fSMårten Lindahl #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) 87fea2c97dSAstrid Rost #define VCNL4040_ALS_CONF_IT GENMASK(7, 6) /* Ambient integration time */ 88bc292aafSAstrid Rost #define VCNL4040_ALS_CONF_INT_EN BIT(1) /* Ambient light Interrupt enable */ 897f865127SAstrid Rost #define VCNL4040_ALS_CONF_PERS GENMASK(3, 2) /* Ambient interrupt persistence setting */ 90e21b5b1fSMårten Lindahl #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) 9185e2c6a2SMårten Lindahl #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ 927f865127SAstrid Rost #define VCNL4040_CONF1_PS_PERS GENMASK(5, 4) /* Proximity interrupt persistence setting */ 9354667612SMårten Lindahl #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ 94*add98466SAstrid Rost #define VCNL4040_PS_CONF3_MPS GENMASK(6, 5) /* Proximity multi pulse number */ 9554667612SMårten Lindahl #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ 9654667612SMårten Lindahl #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ 97bc292aafSAstrid Rost #define VCNL4040_ALS_RISING BIT(12) /* Ambient Light cross high threshold */ 98bc292aafSAstrid Rost #define VCNL4040_ALS_FALLING BIT(13) /* Ambient Light cross low threshold */ 99e21b5b1fSMårten Lindahl 100d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 101d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 102d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 103d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 104d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 105d35567fcSMathieu Othacehe 106d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 107d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 108d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 109d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 110d35567fcSMathieu Othacehe 111d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 112d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 113d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 114d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 115d35567fcSMathieu Othacehe 116f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 117f6889c1bSMathieu Othacehe {1, 950000}, 118f6889c1bSMathieu Othacehe {3, 906250}, 119f6889c1bSMathieu Othacehe {7, 812500}, 120f6889c1bSMathieu Othacehe {16, 625000}, 121f6889c1bSMathieu Othacehe {31, 250000}, 122f6889c1bSMathieu Othacehe {62, 500000}, 123f6889c1bSMathieu Othacehe {125, 0}, 124f6889c1bSMathieu Othacehe {250, 0}, 125f6889c1bSMathieu Othacehe }; 12662a1efb9SPeter Meerwald 12785e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = { 12885e2c6a2SMårten Lindahl {0, 100}, 12985e2c6a2SMårten Lindahl {0, 150}, 13085e2c6a2SMårten Lindahl {0, 200}, 13185e2c6a2SMårten Lindahl {0, 250}, 13285e2c6a2SMårten Lindahl {0, 300}, 13385e2c6a2SMårten Lindahl {0, 350}, 13485e2c6a2SMårten Lindahl {0, 400}, 13585e2c6a2SMårten Lindahl {0, 800}, 13685e2c6a2SMårten Lindahl }; 13785e2c6a2SMårten Lindahl 138e55c96daSAstrid Rost static const int vcnl4200_ps_it_times[][2] = { 139e55c96daSAstrid Rost {0, 96}, 140e55c96daSAstrid Rost {0, 144}, 141e55c96daSAstrid Rost {0, 192}, 142e55c96daSAstrid Rost {0, 384}, 143e55c96daSAstrid Rost {0, 768}, 144e55c96daSAstrid Rost {0, 864}, 145e55c96daSAstrid Rost }; 146e55c96daSAstrid Rost 147fea2c97dSAstrid Rost static const int vcnl4040_als_it_times[][2] = { 148fea2c97dSAstrid Rost {0, 80000}, 149fea2c97dSAstrid Rost {0, 160000}, 150fea2c97dSAstrid Rost {0, 320000}, 151fea2c97dSAstrid Rost {0, 640000}, 152fea2c97dSAstrid Rost }; 153fea2c97dSAstrid Rost 154fea2c97dSAstrid Rost static const int vcnl4200_als_it_times[][2] = { 155fea2c97dSAstrid Rost {0, 50000}, 156fea2c97dSAstrid Rost {0, 100000}, 157fea2c97dSAstrid Rost {0, 200000}, 158fea2c97dSAstrid Rost {0, 400000}, 159fea2c97dSAstrid Rost }; 160fea2c97dSAstrid Rost 1617f865127SAstrid Rost static const int vcnl4040_als_persistence[] = {1, 2, 4, 8}; 1627f865127SAstrid Rost static const int vcnl4040_ps_persistence[] = {1, 2, 3, 4}; 163*add98466SAstrid Rost static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8}; 1647f865127SAstrid Rost 1655e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1665e00708dSGuido Günther 1671ebc787aSTomas Novotny enum vcnl4000_device_ids { 1681ebc787aSTomas Novotny VCNL4000, 16950c50b97STomas Novotny VCNL4010, 1705a441aadSAngus Ainslie (Purism) VCNL4040, 171be38866fSTomas Novotny VCNL4200, 172be38866fSTomas Novotny }; 173be38866fSTomas Novotny 174be38866fSTomas Novotny struct vcnl4200_channel { 175be38866fSTomas Novotny u8 reg; 176be38866fSTomas Novotny ktime_t last_measurement; 177be38866fSTomas Novotny ktime_t sampling_rate; 178be38866fSTomas Novotny struct mutex lock; 1791ebc787aSTomas Novotny }; 1801ebc787aSTomas Novotny 18162a1efb9SPeter Meerwald struct vcnl4000_data { 18262a1efb9SPeter Meerwald struct i2c_client *client; 1831ebc787aSTomas Novotny enum vcnl4000_device_ids id; 1841ebc787aSTomas Novotny int rev; 1851ebc787aSTomas Novotny int al_scale; 18654667612SMårten Lindahl u8 ps_int; /* proximity interrupt mode */ 187bc292aafSAstrid Rost u8 als_int; /* ambient light interrupt mode*/ 1881ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 189be38866fSTomas Novotny struct mutex vcnl4000_lock; 190be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 191be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 192f5a98e1fSGuido Günther uint32_t near_level; 19362a1efb9SPeter Meerwald }; 19462a1efb9SPeter Meerwald 1951ebc787aSTomas Novotny struct vcnl4000_chip_spec { 1961ebc787aSTomas Novotny const char *prod; 197d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 198d35567fcSMathieu Othacehe const int num_channels; 199d35567fcSMathieu Othacehe const struct iio_info *info; 200bfb6cfeeSMårten Lindahl const struct iio_buffer_setup_ops *buffer_setup_ops; 2011ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 2021ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 2031ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 2045e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 205bfb6cfeeSMårten Lindahl irqreturn_t (*irq_thread)(int irq, void *priv); 206bfb6cfeeSMårten Lindahl irqreturn_t (*trig_buffer_func)(int irq, void *priv); 207854965b7SAstrid Rost 208854965b7SAstrid Rost u8 int_reg; 209e55c96daSAstrid Rost const int(*ps_it_times)[][2]; 210e55c96daSAstrid Rost const int num_ps_it_times; 211fea2c97dSAstrid Rost const int(*als_it_times)[][2]; 212fea2c97dSAstrid Rost const int num_als_it_times; 213fea2c97dSAstrid Rost const unsigned int ulux_step; 2141ebc787aSTomas Novotny }; 2151ebc787aSTomas Novotny 21662a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 2171ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 21850c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 21950c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 2205a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 221be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 22262a1efb9SPeter Meerwald { } 22362a1efb9SPeter Meerwald }; 22462a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 22562a1efb9SPeter Meerwald 2265e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 2275e00708dSGuido Günther { 2285e00708dSGuido Günther /* no suspend op */ 2295e00708dSGuido Günther return 0; 2305e00708dSGuido Günther } 2315e00708dSGuido Günther 2321ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 2331ebc787aSTomas Novotny { 2341ebc787aSTomas Novotny int ret, prod_id; 2351ebc787aSTomas Novotny 2361ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 2371ebc787aSTomas Novotny if (ret < 0) 2381ebc787aSTomas Novotny return ret; 2391ebc787aSTomas Novotny 2401ebc787aSTomas Novotny prod_id = ret >> 4; 24158bf9aceSTomas Novotny switch (prod_id) { 24258bf9aceSTomas Novotny case VCNL4000_PROD_ID: 24358bf9aceSTomas Novotny if (data->id != VCNL4000) 24458bf9aceSTomas Novotny dev_warn(&data->client->dev, 24558bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 24658bf9aceSTomas Novotny break; 24758bf9aceSTomas Novotny case VCNL4010_PROD_ID: 24858bf9aceSTomas Novotny if (data->id != VCNL4010) 24958bf9aceSTomas Novotny dev_warn(&data->client->dev, 25058bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 25158bf9aceSTomas Novotny break; 25258bf9aceSTomas Novotny default: 2531ebc787aSTomas Novotny return -ENODEV; 25458bf9aceSTomas Novotny } 2551ebc787aSTomas Novotny 2561ebc787aSTomas Novotny data->rev = ret & 0xf; 2571ebc787aSTomas Novotny data->al_scale = 250000; 258be38866fSTomas Novotny 2595e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 260be38866fSTomas Novotny }; 261be38866fSTomas Novotny 262e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) 2635e00708dSGuido Günther { 2645e00708dSGuido Günther int ret; 2655e00708dSGuido Günther 266e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 267e21b5b1fSMårten Lindahl 268e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 269e21b5b1fSMårten Lindahl if (ret < 0) 270e21b5b1fSMårten Lindahl goto out; 271e21b5b1fSMårten Lindahl 272e21b5b1fSMårten Lindahl if (en) 273e21b5b1fSMårten Lindahl ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; 274e21b5b1fSMårten Lindahl else 275e21b5b1fSMårten Lindahl ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; 276e21b5b1fSMårten Lindahl 277e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); 278e21b5b1fSMårten Lindahl 279e21b5b1fSMårten Lindahl out: 280e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 281e21b5b1fSMårten Lindahl 282e21b5b1fSMårten Lindahl return ret; 283e21b5b1fSMårten Lindahl } 284e21b5b1fSMårten Lindahl 285e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) 286e21b5b1fSMårten Lindahl { 287e21b5b1fSMårten Lindahl int ret; 288e21b5b1fSMårten Lindahl 289e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 290e21b5b1fSMårten Lindahl 291e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 292e21b5b1fSMårten Lindahl if (ret < 0) 293e21b5b1fSMårten Lindahl goto out; 294e21b5b1fSMårten Lindahl 295e21b5b1fSMårten Lindahl if (en) 296e21b5b1fSMårten Lindahl ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; 297e21b5b1fSMårten Lindahl else 298e21b5b1fSMårten Lindahl ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; 299e21b5b1fSMårten Lindahl 300e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); 301e21b5b1fSMårten Lindahl 302e21b5b1fSMårten Lindahl out: 303e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 304e21b5b1fSMårten Lindahl 305e21b5b1fSMårten Lindahl return ret; 306e21b5b1fSMårten Lindahl } 307e21b5b1fSMårten Lindahl 308e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 309e21b5b1fSMårten Lindahl { 310e21b5b1fSMårten Lindahl int ret; 311e21b5b1fSMårten Lindahl 31254667612SMårten Lindahl /* Do not power down if interrupts are enabled */ 313bc292aafSAstrid Rost if (!on && (data->ps_int || data->als_int)) 31454667612SMårten Lindahl return 0; 31554667612SMårten Lindahl 316e21b5b1fSMårten Lindahl ret = vcnl4000_write_als_enable(data, on); 3175e00708dSGuido Günther if (ret < 0) 3185e00708dSGuido Günther return ret; 3195e00708dSGuido Günther 320e21b5b1fSMårten Lindahl ret = vcnl4000_write_ps_enable(data, on); 3215e00708dSGuido Günther if (ret < 0) 3225e00708dSGuido Günther return ret; 3235e00708dSGuido Günther 3245e00708dSGuido Günther if (on) { 3255e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 3265e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 3275e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 3285e00708dSGuido Günther } 3295e00708dSGuido Günther 3305e00708dSGuido Günther return 0; 3315e00708dSGuido Günther } 3325e00708dSGuido Günther 333be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 334be38866fSTomas Novotny { 3355a441aadSAngus Ainslie (Purism) int ret, id; 336be38866fSTomas Novotny 337be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 338be38866fSTomas Novotny if (ret < 0) 339be38866fSTomas Novotny return ret; 340be38866fSTomas Novotny 3415a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3425a441aadSAngus Ainslie (Purism) 3435a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 3445a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 3455a441aadSAngus Ainslie (Purism) if (ret < 0) 3465a441aadSAngus Ainslie (Purism) return ret; 3475a441aadSAngus Ainslie (Purism) 3485a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3495a441aadSAngus Ainslie (Purism) 3505a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 351be38866fSTomas Novotny return -ENODEV; 3525a441aadSAngus Ainslie (Purism) } 3535a441aadSAngus Ainslie (Purism) 3545a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 355be38866fSTomas Novotny 356be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 35754667612SMårten Lindahl data->ps_int = 0; 358bc292aafSAstrid Rost data->als_int = 0; 359be38866fSTomas Novotny 360be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 361be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 3625a441aadSAngus Ainslie (Purism) switch (id) { 3635a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 364b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 365b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 366b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 367b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 3685a441aadSAngus Ainslie (Purism) break; 3695a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 3702ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 3712ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 3722ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 3732ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 3745a441aadSAngus Ainslie (Purism) break; 3755a441aadSAngus Ainslie (Purism) } 376fea2c97dSAstrid Rost data->al_scale = data->chip_spec->ulux_step; 377be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 378be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 3791ebc787aSTomas Novotny 3805e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 3815e00708dSGuido Günther if (ret < 0) 3825e00708dSGuido Günther return ret; 3835e00708dSGuido Günther 3841ebc787aSTomas Novotny return 0; 3851ebc787aSTomas Novotny }; 3861ebc787aSTomas Novotny 387816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 388816956c3SMathieu Othacehe { 389816956c3SMathieu Othacehe s32 ret; 390816956c3SMathieu Othacehe 391816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 392816956c3SMathieu Othacehe if (ret < 0) 393816956c3SMathieu Othacehe return ret; 394816956c3SMathieu Othacehe 395816956c3SMathieu Othacehe *val = ret; 396816956c3SMathieu Othacehe return 0; 397816956c3SMathieu Othacehe } 398816956c3SMathieu Othacehe 399816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 400816956c3SMathieu Othacehe { 401816956c3SMathieu Othacehe if (val > U16_MAX) 402816956c3SMathieu Othacehe return -ERANGE; 403816956c3SMathieu Othacehe 404816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 405816956c3SMathieu Othacehe } 406816956c3SMathieu Othacehe 407816956c3SMathieu Othacehe 40862a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 40962a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 41062a1efb9SPeter Meerwald { 41162a1efb9SPeter Meerwald int tries = 20; 41262a1efb9SPeter Meerwald int ret; 41362a1efb9SPeter Meerwald 414be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 415ff34ed6dSPeter Meerwald-Stadler 41662a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 41762a1efb9SPeter Meerwald req_mask); 41862a1efb9SPeter Meerwald if (ret < 0) 419ff34ed6dSPeter Meerwald-Stadler goto fail; 42062a1efb9SPeter Meerwald 42162a1efb9SPeter Meerwald /* wait for data to become ready */ 42262a1efb9SPeter Meerwald while (tries--) { 42362a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 42462a1efb9SPeter Meerwald if (ret < 0) 425ff34ed6dSPeter Meerwald-Stadler goto fail; 42662a1efb9SPeter Meerwald if (ret & rdy_mask) 42762a1efb9SPeter Meerwald break; 42862a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 42962a1efb9SPeter Meerwald } 43062a1efb9SPeter Meerwald 43162a1efb9SPeter Meerwald if (tries < 0) { 43262a1efb9SPeter Meerwald dev_err(&data->client->dev, 43362a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 434ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 435ff34ed6dSPeter Meerwald-Stadler goto fail; 43662a1efb9SPeter Meerwald } 43762a1efb9SPeter Meerwald 438816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 43962a1efb9SPeter Meerwald if (ret < 0) 440ff34ed6dSPeter Meerwald-Stadler goto fail; 44162a1efb9SPeter Meerwald 442be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 44362a1efb9SPeter Meerwald 44462a1efb9SPeter Meerwald return 0; 445ff34ed6dSPeter Meerwald-Stadler 446ff34ed6dSPeter Meerwald-Stadler fail: 447be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 448ff34ed6dSPeter Meerwald-Stadler return ret; 44962a1efb9SPeter Meerwald } 45062a1efb9SPeter Meerwald 451be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 452be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 453be38866fSTomas Novotny { 454be38866fSTomas Novotny int ret; 455be38866fSTomas Novotny s64 delta; 456be38866fSTomas Novotny ktime_t next_measurement; 457be38866fSTomas Novotny 458be38866fSTomas Novotny mutex_lock(&chan->lock); 459be38866fSTomas Novotny 460be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 461be38866fSTomas Novotny chan->sampling_rate); 462be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 463be38866fSTomas Novotny if (delta > 0) 464be38866fSTomas Novotny usleep_range(delta, delta + 500); 465be38866fSTomas Novotny chan->last_measurement = ktime_get(); 466be38866fSTomas Novotny 467be38866fSTomas Novotny mutex_unlock(&chan->lock); 468be38866fSTomas Novotny 469be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 470be38866fSTomas Novotny if (ret < 0) 471be38866fSTomas Novotny return ret; 472be38866fSTomas Novotny 473be38866fSTomas Novotny *val = ret; 474be38866fSTomas Novotny 475be38866fSTomas Novotny return 0; 476be38866fSTomas Novotny } 477be38866fSTomas Novotny 4781ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 4791ebc787aSTomas Novotny { 4801ebc787aSTomas Novotny return vcnl4000_measure(data, 4811ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 4821ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 4831ebc787aSTomas Novotny } 4841ebc787aSTomas Novotny 485be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 486be38866fSTomas Novotny { 487be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 488be38866fSTomas Novotny } 489be38866fSTomas Novotny 4901ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 4911ebc787aSTomas Novotny { 4921ebc787aSTomas Novotny return vcnl4000_measure(data, 4931ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 4941ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 4951ebc787aSTomas Novotny } 4961ebc787aSTomas Novotny 497be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 498be38866fSTomas Novotny { 499be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 500be38866fSTomas Novotny } 501be38866fSTomas Novotny 502f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 503f6889c1bSMathieu Othacehe int *val2) 504f6889c1bSMathieu Othacehe { 505f6889c1bSMathieu Othacehe int ret; 506f6889c1bSMathieu Othacehe 507f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 508f6889c1bSMathieu Othacehe if (ret < 0) 509f6889c1bSMathieu Othacehe return ret; 510f6889c1bSMathieu Othacehe 511f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 512f6889c1bSMathieu Othacehe return -EINVAL; 513f6889c1bSMathieu Othacehe 514f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 515f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 516f6889c1bSMathieu Othacehe 517f6889c1bSMathieu Othacehe return 0; 518f6889c1bSMathieu Othacehe } 519f6889c1bSMathieu Othacehe 520d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 521f5a98e1fSGuido Günther { 522d35567fcSMathieu Othacehe int ret; 523f5a98e1fSGuido Günther 524d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 525d35567fcSMathieu Othacehe if (ret < 0) 526d35567fcSMathieu Othacehe return false; 527d35567fcSMathieu Othacehe 528d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 529f5a98e1fSGuido Günther } 530f5a98e1fSGuido Günther 5315e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 5325e00708dSGuido Günther { 5335e00708dSGuido Günther struct device *dev = &data->client->dev; 5345e00708dSGuido Günther int ret; 5355e00708dSGuido Günther 5365e00708dSGuido Günther if (on) { 537db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 5385e00708dSGuido Günther } else { 5395e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 5405e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 5415e00708dSGuido Günther } 5425e00708dSGuido Günther 5435e00708dSGuido Günther return ret; 5445e00708dSGuido Günther } 5455e00708dSGuido Günther 546fea2c97dSAstrid Rost static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2) 547fea2c97dSAstrid Rost { 548fea2c97dSAstrid Rost int ret; 549fea2c97dSAstrid Rost 550fea2c97dSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 551fea2c97dSAstrid Rost if (ret < 0) 552fea2c97dSAstrid Rost return ret; 553fea2c97dSAstrid Rost 554fea2c97dSAstrid Rost ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 555fea2c97dSAstrid Rost if (ret >= data->chip_spec->num_als_it_times) 556fea2c97dSAstrid Rost return -EINVAL; 557fea2c97dSAstrid Rost 558fea2c97dSAstrid Rost *val = (*data->chip_spec->als_it_times)[ret][0]; 559fea2c97dSAstrid Rost *val2 = (*data->chip_spec->als_it_times)[ret][1]; 560fea2c97dSAstrid Rost 561fea2c97dSAstrid Rost return 0; 562fea2c97dSAstrid Rost } 563fea2c97dSAstrid Rost 564fea2c97dSAstrid Rost static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val) 565fea2c97dSAstrid Rost { 566fea2c97dSAstrid Rost unsigned int i; 567fea2c97dSAstrid Rost int ret; 568fea2c97dSAstrid Rost u16 regval; 569fea2c97dSAstrid Rost 570fea2c97dSAstrid Rost for (i = 0; i < data->chip_spec->num_als_it_times; i++) { 571fea2c97dSAstrid Rost if (val == (*data->chip_spec->als_it_times)[i][1]) 572fea2c97dSAstrid Rost break; 573fea2c97dSAstrid Rost } 574fea2c97dSAstrid Rost 575fea2c97dSAstrid Rost if (i == data->chip_spec->num_als_it_times) 576fea2c97dSAstrid Rost return -EINVAL; 577fea2c97dSAstrid Rost 578fea2c97dSAstrid Rost data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200); 579fea2c97dSAstrid Rost data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step, 580fea2c97dSAstrid Rost (*data->chip_spec->als_it_times)[0][1]), 581fea2c97dSAstrid Rost val); 582fea2c97dSAstrid Rost 583fea2c97dSAstrid Rost mutex_lock(&data->vcnl4000_lock); 584fea2c97dSAstrid Rost 585fea2c97dSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 586fea2c97dSAstrid Rost if (ret < 0) 587fea2c97dSAstrid Rost goto out_unlock; 588fea2c97dSAstrid Rost 589fea2c97dSAstrid Rost regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i); 590fea2c97dSAstrid Rost regval |= (ret & ~VCNL4040_ALS_CONF_IT); 591fea2c97dSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 592fea2c97dSAstrid Rost VCNL4200_AL_CONF, 593fea2c97dSAstrid Rost regval); 594fea2c97dSAstrid Rost 595fea2c97dSAstrid Rost out_unlock: 596fea2c97dSAstrid Rost mutex_unlock(&data->vcnl4000_lock); 597fea2c97dSAstrid Rost return ret; 598fea2c97dSAstrid Rost } 599fea2c97dSAstrid Rost 60085e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) 60185e2c6a2SMårten Lindahl { 60285e2c6a2SMårten Lindahl int ret; 60385e2c6a2SMårten Lindahl 60485e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 60585e2c6a2SMårten Lindahl if (ret < 0) 60685e2c6a2SMårten Lindahl return ret; 60785e2c6a2SMårten Lindahl 60885e2c6a2SMårten Lindahl ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 60985e2c6a2SMårten Lindahl 610e55c96daSAstrid Rost if (ret >= data->chip_spec->num_ps_it_times) 61185e2c6a2SMårten Lindahl return -EINVAL; 61285e2c6a2SMårten Lindahl 613e55c96daSAstrid Rost *val = (*data->chip_spec->ps_it_times)[ret][0]; 614e55c96daSAstrid Rost *val2 = (*data->chip_spec->ps_it_times)[ret][1]; 61585e2c6a2SMårten Lindahl 61685e2c6a2SMårten Lindahl return 0; 61785e2c6a2SMårten Lindahl } 61885e2c6a2SMårten Lindahl 61985e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) 62085e2c6a2SMårten Lindahl { 62185e2c6a2SMårten Lindahl unsigned int i; 62285e2c6a2SMårten Lindahl int ret, index = -1; 62385e2c6a2SMårten Lindahl u16 regval; 62485e2c6a2SMårten Lindahl 625e55c96daSAstrid Rost for (i = 0; i < data->chip_spec->num_ps_it_times; i++) { 626e55c96daSAstrid Rost if (val == (*data->chip_spec->ps_it_times)[i][1]) { 62785e2c6a2SMårten Lindahl index = i; 62885e2c6a2SMårten Lindahl break; 62985e2c6a2SMårten Lindahl } 63085e2c6a2SMårten Lindahl } 63185e2c6a2SMårten Lindahl 63285e2c6a2SMårten Lindahl if (index < 0) 63385e2c6a2SMårten Lindahl return -EINVAL; 63485e2c6a2SMårten Lindahl 635e55c96daSAstrid Rost data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC); 636e55c96daSAstrid Rost 63785e2c6a2SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 63885e2c6a2SMårten Lindahl 63985e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 64085e2c6a2SMårten Lindahl if (ret < 0) 64185e2c6a2SMårten Lindahl goto out; 64285e2c6a2SMårten Lindahl 64385e2c6a2SMårten Lindahl regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | 64485e2c6a2SMårten Lindahl FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); 64585e2c6a2SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 64685e2c6a2SMårten Lindahl regval); 64785e2c6a2SMårten Lindahl 64885e2c6a2SMårten Lindahl out: 64985e2c6a2SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 65085e2c6a2SMårten Lindahl return ret; 65185e2c6a2SMårten Lindahl } 65285e2c6a2SMårten Lindahl 6537f865127SAstrid Rost static ssize_t vcnl4040_read_als_period(struct vcnl4000_data *data, int *val, int *val2) 6547f865127SAstrid Rost { 6557f865127SAstrid Rost int ret, ret_pers, it; 6567f865127SAstrid Rost int64_t val_c; 6577f865127SAstrid Rost 6587f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 6597f865127SAstrid Rost if (ret < 0) 6607f865127SAstrid Rost return ret; 6617f865127SAstrid Rost 6627f865127SAstrid Rost ret_pers = FIELD_GET(VCNL4040_ALS_CONF_PERS, ret); 6637f865127SAstrid Rost if (ret_pers >= ARRAY_SIZE(vcnl4040_als_persistence)) 6647f865127SAstrid Rost return -EINVAL; 6657f865127SAstrid Rost 6667f865127SAstrid Rost it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 6677f865127SAstrid Rost if (it >= data->chip_spec->num_als_it_times) 6687f865127SAstrid Rost return -EINVAL; 6697f865127SAstrid Rost 6707f865127SAstrid Rost val_c = mul_u32_u32((*data->chip_spec->als_it_times)[it][1], 6717f865127SAstrid Rost vcnl4040_als_persistence[ret_pers]); 6727f865127SAstrid Rost *val = div_u64_rem(val_c, MICRO, val2); 6737f865127SAstrid Rost 6747f865127SAstrid Rost return IIO_VAL_INT_PLUS_MICRO; 6757f865127SAstrid Rost } 6767f865127SAstrid Rost 6777f865127SAstrid Rost static ssize_t vcnl4040_write_als_period(struct vcnl4000_data *data, int val, int val2) 6787f865127SAstrid Rost { 6797f865127SAstrid Rost unsigned int i; 6807f865127SAstrid Rost int ret, it; 6817f865127SAstrid Rost u16 regval; 6827f865127SAstrid Rost u64 val_n = mul_u32_u32(val, MICRO) + val2; 6837f865127SAstrid Rost 6847f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 6857f865127SAstrid Rost if (ret < 0) 6867f865127SAstrid Rost return ret; 6877f865127SAstrid Rost 6887f865127SAstrid Rost it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 6897f865127SAstrid Rost if (it >= data->chip_spec->num_als_it_times) 6907f865127SAstrid Rost return -EINVAL; 6917f865127SAstrid Rost 6927f865127SAstrid Rost for (i = 0; i < ARRAY_SIZE(vcnl4040_als_persistence) - 1; i++) { 6937f865127SAstrid Rost if (val_n < mul_u32_u32(vcnl4040_als_persistence[i], 6947f865127SAstrid Rost (*data->chip_spec->als_it_times)[it][1])) 6957f865127SAstrid Rost break; 6967f865127SAstrid Rost } 6977f865127SAstrid Rost 6987f865127SAstrid Rost mutex_lock(&data->vcnl4000_lock); 6997f865127SAstrid Rost 7007f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 7017f865127SAstrid Rost if (ret < 0) 7027f865127SAstrid Rost goto out_unlock; 7037f865127SAstrid Rost 7047f865127SAstrid Rost regval = FIELD_PREP(VCNL4040_ALS_CONF_PERS, i); 7057f865127SAstrid Rost regval |= (ret & ~VCNL4040_ALS_CONF_PERS); 7067f865127SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 7077f865127SAstrid Rost regval); 7087f865127SAstrid Rost 7097f865127SAstrid Rost out_unlock: 7107f865127SAstrid Rost mutex_unlock(&data->vcnl4000_lock); 7117f865127SAstrid Rost return ret; 7127f865127SAstrid Rost } 7137f865127SAstrid Rost 7147f865127SAstrid Rost static ssize_t vcnl4040_read_ps_period(struct vcnl4000_data *data, int *val, int *val2) 7157f865127SAstrid Rost { 7167f865127SAstrid Rost int ret, ret_pers, it; 7177f865127SAstrid Rost 7187f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 7197f865127SAstrid Rost if (ret < 0) 7207f865127SAstrid Rost return ret; 7217f865127SAstrid Rost 7227f865127SAstrid Rost ret_pers = FIELD_GET(VCNL4040_CONF1_PS_PERS, ret); 7237f865127SAstrid Rost if (ret_pers >= ARRAY_SIZE(vcnl4040_ps_persistence)) 7247f865127SAstrid Rost return -EINVAL; 7257f865127SAstrid Rost 7267f865127SAstrid Rost it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 7277f865127SAstrid Rost if (it >= data->chip_spec->num_ps_it_times) 7287f865127SAstrid Rost return -EINVAL; 7297f865127SAstrid Rost 7307f865127SAstrid Rost *val = (*data->chip_spec->ps_it_times)[it][0]; 7317f865127SAstrid Rost *val2 = (*data->chip_spec->ps_it_times)[it][1] * 7327f865127SAstrid Rost vcnl4040_ps_persistence[ret_pers]; 7337f865127SAstrid Rost 7347f865127SAstrid Rost return IIO_VAL_INT_PLUS_MICRO; 7357f865127SAstrid Rost } 7367f865127SAstrid Rost 7377f865127SAstrid Rost static ssize_t vcnl4040_write_ps_period(struct vcnl4000_data *data, int val, int val2) 7387f865127SAstrid Rost { 7397f865127SAstrid Rost int ret, it, i; 7407f865127SAstrid Rost u16 regval; 7417f865127SAstrid Rost 7427f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 7437f865127SAstrid Rost if (ret < 0) 7447f865127SAstrid Rost return ret; 7457f865127SAstrid Rost 7467f865127SAstrid Rost it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 7477f865127SAstrid Rost if (it >= data->chip_spec->num_ps_it_times) 7487f865127SAstrid Rost return -EINVAL; 7497f865127SAstrid Rost 7507f865127SAstrid Rost if (val > 0) 7517f865127SAstrid Rost i = ARRAY_SIZE(vcnl4040_ps_persistence) - 1; 7527f865127SAstrid Rost else { 7537f865127SAstrid Rost for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_persistence) - 1; i++) { 7547f865127SAstrid Rost if (val2 <= vcnl4040_ps_persistence[i] * 7557f865127SAstrid Rost (*data->chip_spec->ps_it_times)[it][1]) 7567f865127SAstrid Rost break; 7577f865127SAstrid Rost } 7587f865127SAstrid Rost } 7597f865127SAstrid Rost 7607f865127SAstrid Rost mutex_lock(&data->vcnl4000_lock); 7617f865127SAstrid Rost 7627f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 7637f865127SAstrid Rost if (ret < 0) 7647f865127SAstrid Rost goto out_unlock; 7657f865127SAstrid Rost 7667f865127SAstrid Rost regval = FIELD_PREP(VCNL4040_CONF1_PS_PERS, i); 7677f865127SAstrid Rost regval |= (ret & ~VCNL4040_CONF1_PS_PERS); 7687f865127SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 7697f865127SAstrid Rost regval); 7707f865127SAstrid Rost 7717f865127SAstrid Rost out_unlock: 7727f865127SAstrid Rost mutex_unlock(&data->vcnl4000_lock); 7737f865127SAstrid Rost return ret; 7747f865127SAstrid Rost } 7757f865127SAstrid Rost 776*add98466SAstrid Rost static ssize_t vcnl4040_read_ps_oversampling_ratio(struct vcnl4000_data *data, int *val) 777*add98466SAstrid Rost { 778*add98466SAstrid Rost int ret; 779*add98466SAstrid Rost 780*add98466SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); 781*add98466SAstrid Rost if (ret < 0) 782*add98466SAstrid Rost return ret; 783*add98466SAstrid Rost 784*add98466SAstrid Rost ret = FIELD_GET(VCNL4040_PS_CONF3_MPS, ret); 785*add98466SAstrid Rost if (ret >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio)) 786*add98466SAstrid Rost return -EINVAL; 787*add98466SAstrid Rost 788*add98466SAstrid Rost *val = vcnl4040_ps_oversampling_ratio[ret]; 789*add98466SAstrid Rost 790*add98466SAstrid Rost return ret; 791*add98466SAstrid Rost } 792*add98466SAstrid Rost 793*add98466SAstrid Rost static ssize_t vcnl4040_write_ps_oversampling_ratio(struct vcnl4000_data *data, int val) 794*add98466SAstrid Rost { 795*add98466SAstrid Rost unsigned int i; 796*add98466SAstrid Rost int ret; 797*add98466SAstrid Rost u16 regval; 798*add98466SAstrid Rost 799*add98466SAstrid Rost for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); i++) { 800*add98466SAstrid Rost if (val == vcnl4040_ps_oversampling_ratio[i]) 801*add98466SAstrid Rost break; 802*add98466SAstrid Rost } 803*add98466SAstrid Rost 804*add98466SAstrid Rost if (i >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio)) 805*add98466SAstrid Rost return -EINVAL; 806*add98466SAstrid Rost 807*add98466SAstrid Rost mutex_lock(&data->vcnl4000_lock); 808*add98466SAstrid Rost 809*add98466SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); 810*add98466SAstrid Rost if (ret < 0) 811*add98466SAstrid Rost goto out_unlock; 812*add98466SAstrid Rost 813*add98466SAstrid Rost regval = FIELD_PREP(VCNL4040_PS_CONF3_MPS, i); 814*add98466SAstrid Rost regval |= (ret & ~VCNL4040_PS_CONF3_MPS); 815*add98466SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, 816*add98466SAstrid Rost regval); 817*add98466SAstrid Rost 818*add98466SAstrid Rost out_unlock: 819*add98466SAstrid Rost mutex_unlock(&data->vcnl4000_lock); 820*add98466SAstrid Rost return ret; 821*add98466SAstrid Rost } 822*add98466SAstrid Rost 82362a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 82462a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 82562a1efb9SPeter Meerwald int *val, int *val2, long mask) 82662a1efb9SPeter Meerwald { 8275d693139SPeter Meerwald-Stadler int ret; 82862a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 82962a1efb9SPeter Meerwald 83062a1efb9SPeter Meerwald switch (mask) { 83162a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 8325e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 8335e00708dSGuido Günther if (ret < 0) 8345e00708dSGuido Günther return ret; 8355e00708dSGuido Günther 83662a1efb9SPeter Meerwald switch (chan->type) { 83762a1efb9SPeter Meerwald case IIO_LIGHT: 8381ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 8394a818643SGuido Günther if (!ret) 8404a818643SGuido Günther ret = IIO_VAL_INT; 8414a818643SGuido Günther break; 84262a1efb9SPeter Meerwald case IIO_PROXIMITY: 8431ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 8444a818643SGuido Günther if (!ret) 8454a818643SGuido Günther ret = IIO_VAL_INT; 8464a818643SGuido Günther break; 84762a1efb9SPeter Meerwald default: 8484a818643SGuido Günther ret = -EINVAL; 84962a1efb9SPeter Meerwald } 8505e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 8514a818643SGuido Günther return ret; 85262a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 8535d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 8545d693139SPeter Meerwald-Stadler return -EINVAL; 8555d693139SPeter Meerwald-Stadler 85662a1efb9SPeter Meerwald *val = 0; 8571ebc787aSTomas Novotny *val2 = data->al_scale; 8585d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 85985e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 8602be17b68SAstrid Rost switch (chan->type) { 861fea2c97dSAstrid Rost case IIO_LIGHT: 862fea2c97dSAstrid Rost ret = vcnl4040_read_als_it(data, val, val2); 863fea2c97dSAstrid Rost break; 8642be17b68SAstrid Rost case IIO_PROXIMITY: 86585e2c6a2SMårten Lindahl ret = vcnl4040_read_ps_it(data, val, val2); 8662be17b68SAstrid Rost break; 8672be17b68SAstrid Rost default: 8682be17b68SAstrid Rost return -EINVAL; 8692be17b68SAstrid Rost } 87085e2c6a2SMårten Lindahl if (ret < 0) 87185e2c6a2SMårten Lindahl return ret; 87285e2c6a2SMårten Lindahl return IIO_VAL_INT_PLUS_MICRO; 873*add98466SAstrid Rost case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 874*add98466SAstrid Rost switch (chan->type) { 875*add98466SAstrid Rost case IIO_PROXIMITY: 876*add98466SAstrid Rost ret = vcnl4040_read_ps_oversampling_ratio(data, val); 877*add98466SAstrid Rost if (ret < 0) 878*add98466SAstrid Rost return ret; 879*add98466SAstrid Rost return IIO_VAL_INT; 880*add98466SAstrid Rost default: 881*add98466SAstrid Rost return -EINVAL; 882*add98466SAstrid Rost } 88385e2c6a2SMårten Lindahl default: 88485e2c6a2SMårten Lindahl return -EINVAL; 88585e2c6a2SMårten Lindahl } 88685e2c6a2SMårten Lindahl } 88785e2c6a2SMårten Lindahl 88885e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev, 88985e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 89085e2c6a2SMårten Lindahl int val, int val2, long mask) 89185e2c6a2SMårten Lindahl { 89285e2c6a2SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 89385e2c6a2SMårten Lindahl 89485e2c6a2SMårten Lindahl switch (mask) { 89585e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 89685e2c6a2SMårten Lindahl if (val != 0) 89785e2c6a2SMårten Lindahl return -EINVAL; 8982be17b68SAstrid Rost switch (chan->type) { 899fea2c97dSAstrid Rost case IIO_LIGHT: 900fea2c97dSAstrid Rost return vcnl4040_write_als_it(data, val2); 9012be17b68SAstrid Rost case IIO_PROXIMITY: 90285e2c6a2SMårten Lindahl return vcnl4040_write_ps_it(data, val2); 90385e2c6a2SMårten Lindahl default: 90485e2c6a2SMårten Lindahl return -EINVAL; 90585e2c6a2SMårten Lindahl } 906*add98466SAstrid Rost case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 907*add98466SAstrid Rost switch (chan->type) { 908*add98466SAstrid Rost case IIO_PROXIMITY: 909*add98466SAstrid Rost return vcnl4040_write_ps_oversampling_ratio(data, val); 910*add98466SAstrid Rost default: 911*add98466SAstrid Rost return -EINVAL; 912*add98466SAstrid Rost } 9132be17b68SAstrid Rost default: 9142be17b68SAstrid Rost return -EINVAL; 9152be17b68SAstrid Rost } 91685e2c6a2SMårten Lindahl } 91785e2c6a2SMårten Lindahl 91885e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev, 91985e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 92085e2c6a2SMårten Lindahl const int **vals, int *type, int *length, 92185e2c6a2SMårten Lindahl long mask) 92285e2c6a2SMårten Lindahl { 923e55c96daSAstrid Rost struct vcnl4000_data *data = iio_priv(indio_dev); 924e55c96daSAstrid Rost 92585e2c6a2SMårten Lindahl switch (mask) { 92685e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 9272be17b68SAstrid Rost switch (chan->type) { 928fea2c97dSAstrid Rost case IIO_LIGHT: 929fea2c97dSAstrid Rost *vals = (int *)(*data->chip_spec->als_it_times); 930fea2c97dSAstrid Rost *length = 2 * data->chip_spec->num_als_it_times; 931fea2c97dSAstrid Rost break; 9322be17b68SAstrid Rost case IIO_PROXIMITY: 933e55c96daSAstrid Rost *vals = (int *)(*data->chip_spec->ps_it_times); 934e55c96daSAstrid Rost *length = 2 * data->chip_spec->num_ps_it_times; 9352be17b68SAstrid Rost break; 9362be17b68SAstrid Rost default: 9372be17b68SAstrid Rost return -EINVAL; 9382be17b68SAstrid Rost } 9392be17b68SAstrid Rost *type = IIO_VAL_INT_PLUS_MICRO; 94085e2c6a2SMårten Lindahl return IIO_AVAIL_LIST; 941*add98466SAstrid Rost case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 942*add98466SAstrid Rost switch (chan->type) { 943*add98466SAstrid Rost case IIO_PROXIMITY: 944*add98466SAstrid Rost *vals = (int *)vcnl4040_ps_oversampling_ratio; 945*add98466SAstrid Rost *length = ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); 946*add98466SAstrid Rost *type = IIO_VAL_INT; 947*add98466SAstrid Rost return IIO_AVAIL_LIST; 948*add98466SAstrid Rost default: 949*add98466SAstrid Rost return -EINVAL; 950*add98466SAstrid Rost } 95162a1efb9SPeter Meerwald default: 9525d693139SPeter Meerwald-Stadler return -EINVAL; 95362a1efb9SPeter Meerwald } 95462a1efb9SPeter Meerwald } 95562a1efb9SPeter Meerwald 956d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 957d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 958d35567fcSMathieu Othacehe int *val, int *val2, long mask) 959d35567fcSMathieu Othacehe { 960d35567fcSMathieu Othacehe int ret; 961d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 962d35567fcSMathieu Othacehe 963d35567fcSMathieu Othacehe switch (mask) { 964d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 965d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 966d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 967d35567fcSMathieu Othacehe if (ret) 968d35567fcSMathieu Othacehe return ret; 969d35567fcSMathieu Othacehe 970d35567fcSMathieu Othacehe /* Protect against event capture. */ 971d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 972d35567fcSMathieu Othacehe ret = -EBUSY; 973d35567fcSMathieu Othacehe } else { 974d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 975d35567fcSMathieu Othacehe mask); 976d35567fcSMathieu Othacehe } 977d35567fcSMathieu Othacehe 978d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 979d35567fcSMathieu Othacehe return ret; 980f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 981f6889c1bSMathieu Othacehe switch (chan->type) { 982f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 983f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 984f6889c1bSMathieu Othacehe if (ret < 0) 985f6889c1bSMathieu Othacehe return ret; 986f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 987d35567fcSMathieu Othacehe default: 988d35567fcSMathieu Othacehe return -EINVAL; 989d35567fcSMathieu Othacehe } 990f6889c1bSMathieu Othacehe default: 991f6889c1bSMathieu Othacehe return -EINVAL; 992f6889c1bSMathieu Othacehe } 993f6889c1bSMathieu Othacehe } 994f6889c1bSMathieu Othacehe 995f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 996f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 997f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 998f6889c1bSMathieu Othacehe long mask) 999f6889c1bSMathieu Othacehe { 1000f6889c1bSMathieu Othacehe switch (mask) { 1001f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 1002f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 1003f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 1004f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 1005f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 1006f6889c1bSMathieu Othacehe default: 1007f6889c1bSMathieu Othacehe return -EINVAL; 1008f6889c1bSMathieu Othacehe } 1009f6889c1bSMathieu Othacehe } 1010f6889c1bSMathieu Othacehe 1011f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 1012f6889c1bSMathieu Othacehe int val2) 1013f6889c1bSMathieu Othacehe { 1014f6889c1bSMathieu Othacehe unsigned int i; 1015f6889c1bSMathieu Othacehe int index = -1; 1016f6889c1bSMathieu Othacehe 1017f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 1018f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 1019f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 1020f6889c1bSMathieu Othacehe index = i; 1021f6889c1bSMathieu Othacehe break; 1022f6889c1bSMathieu Othacehe } 1023f6889c1bSMathieu Othacehe } 1024f6889c1bSMathieu Othacehe 1025f6889c1bSMathieu Othacehe if (index < 0) 1026f6889c1bSMathieu Othacehe return -EINVAL; 1027f6889c1bSMathieu Othacehe 1028f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 1029f6889c1bSMathieu Othacehe index); 1030f6889c1bSMathieu Othacehe } 1031f6889c1bSMathieu Othacehe 1032f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 1033f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 1034f6889c1bSMathieu Othacehe int val, int val2, long mask) 1035f6889c1bSMathieu Othacehe { 1036f6889c1bSMathieu Othacehe int ret; 1037f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1038f6889c1bSMathieu Othacehe 1039f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 1040f6889c1bSMathieu Othacehe if (ret) 1041f6889c1bSMathieu Othacehe return ret; 1042f6889c1bSMathieu Othacehe 1043f6889c1bSMathieu Othacehe /* Protect against event capture. */ 1044f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 1045f6889c1bSMathieu Othacehe ret = -EBUSY; 1046f6889c1bSMathieu Othacehe goto end; 1047f6889c1bSMathieu Othacehe } 1048f6889c1bSMathieu Othacehe 1049f6889c1bSMathieu Othacehe switch (mask) { 1050f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 1051f6889c1bSMathieu Othacehe switch (chan->type) { 1052f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 1053f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 1054f6889c1bSMathieu Othacehe goto end; 1055f6889c1bSMathieu Othacehe default: 1056f6889c1bSMathieu Othacehe ret = -EINVAL; 1057f6889c1bSMathieu Othacehe goto end; 1058f6889c1bSMathieu Othacehe } 1059f6889c1bSMathieu Othacehe default: 1060f6889c1bSMathieu Othacehe ret = -EINVAL; 1061f6889c1bSMathieu Othacehe goto end; 1062f6889c1bSMathieu Othacehe } 1063f6889c1bSMathieu Othacehe 1064f6889c1bSMathieu Othacehe end: 1065f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 1066f6889c1bSMathieu Othacehe return ret; 1067d35567fcSMathieu Othacehe } 1068d35567fcSMathieu Othacehe 1069d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 1070d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1071d35567fcSMathieu Othacehe enum iio_event_type type, 1072d35567fcSMathieu Othacehe enum iio_event_direction dir, 1073d35567fcSMathieu Othacehe enum iio_event_info info, 1074d35567fcSMathieu Othacehe int *val, int *val2) 1075d35567fcSMathieu Othacehe { 1076d35567fcSMathieu Othacehe int ret; 1077d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1078d35567fcSMathieu Othacehe 1079d35567fcSMathieu Othacehe switch (info) { 1080d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 1081d35567fcSMathieu Othacehe switch (dir) { 1082d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 1083d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 1084d35567fcSMathieu Othacehe val); 1085d35567fcSMathieu Othacehe if (ret < 0) 1086d35567fcSMathieu Othacehe return ret; 1087d35567fcSMathieu Othacehe return IIO_VAL_INT; 1088d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 1089d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 1090d35567fcSMathieu Othacehe val); 1091d35567fcSMathieu Othacehe if (ret < 0) 1092d35567fcSMathieu Othacehe return ret; 1093d35567fcSMathieu Othacehe return IIO_VAL_INT; 1094d35567fcSMathieu Othacehe default: 1095d35567fcSMathieu Othacehe return -EINVAL; 1096d35567fcSMathieu Othacehe } 1097d35567fcSMathieu Othacehe default: 1098d35567fcSMathieu Othacehe return -EINVAL; 1099d35567fcSMathieu Othacehe } 1100d35567fcSMathieu Othacehe } 1101d35567fcSMathieu Othacehe 1102d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 1103d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1104d35567fcSMathieu Othacehe enum iio_event_type type, 1105d35567fcSMathieu Othacehe enum iio_event_direction dir, 1106d35567fcSMathieu Othacehe enum iio_event_info info, 1107d35567fcSMathieu Othacehe int val, int val2) 1108d35567fcSMathieu Othacehe { 1109d35567fcSMathieu Othacehe int ret; 1110d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1111d35567fcSMathieu Othacehe 1112d35567fcSMathieu Othacehe switch (info) { 1113d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 1114d35567fcSMathieu Othacehe switch (dir) { 1115d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 1116d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 1117d35567fcSMathieu Othacehe val); 1118d35567fcSMathieu Othacehe if (ret < 0) 1119d35567fcSMathieu Othacehe return ret; 1120d35567fcSMathieu Othacehe return IIO_VAL_INT; 1121d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 1122d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 1123d35567fcSMathieu Othacehe val); 1124d35567fcSMathieu Othacehe if (ret < 0) 1125d35567fcSMathieu Othacehe return ret; 1126d35567fcSMathieu Othacehe return IIO_VAL_INT; 1127d35567fcSMathieu Othacehe default: 1128d35567fcSMathieu Othacehe return -EINVAL; 1129d35567fcSMathieu Othacehe } 1130d35567fcSMathieu Othacehe default: 1131d35567fcSMathieu Othacehe return -EINVAL; 1132d35567fcSMathieu Othacehe } 1133d35567fcSMathieu Othacehe } 1134d35567fcSMathieu Othacehe 113554667612SMårten Lindahl static int vcnl4040_read_event(struct iio_dev *indio_dev, 113654667612SMårten Lindahl const struct iio_chan_spec *chan, 113754667612SMårten Lindahl enum iio_event_type type, 113854667612SMårten Lindahl enum iio_event_direction dir, 113954667612SMårten Lindahl enum iio_event_info info, 114054667612SMårten Lindahl int *val, int *val2) 114154667612SMårten Lindahl { 114254667612SMårten Lindahl int ret; 114354667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 114454667612SMårten Lindahl 11452be17b68SAstrid Rost switch (chan->type) { 1146bc292aafSAstrid Rost case IIO_LIGHT: 1147bc292aafSAstrid Rost switch (info) { 11487f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 11497f865127SAstrid Rost return vcnl4040_read_als_period(data, val, val2); 1150bc292aafSAstrid Rost case IIO_EV_INFO_VALUE: 1151bc292aafSAstrid Rost switch (dir) { 1152bc292aafSAstrid Rost case IIO_EV_DIR_RISING: 1153bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, 1154bc292aafSAstrid Rost VCNL4040_ALS_THDH_LM); 1155bc292aafSAstrid Rost break; 1156bc292aafSAstrid Rost case IIO_EV_DIR_FALLING: 1157bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, 1158bc292aafSAstrid Rost VCNL4040_ALS_THDL_LM); 1159bc292aafSAstrid Rost break; 1160bc292aafSAstrid Rost default: 1161bc292aafSAstrid Rost return -EINVAL; 1162bc292aafSAstrid Rost } 1163bc292aafSAstrid Rost break; 1164bc292aafSAstrid Rost default: 1165bc292aafSAstrid Rost return -EINVAL; 1166bc292aafSAstrid Rost } 1167bc292aafSAstrid Rost break; 11682be17b68SAstrid Rost case IIO_PROXIMITY: 11692be17b68SAstrid Rost switch (info) { 11707f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 11717f865127SAstrid Rost return vcnl4040_read_ps_period(data, val, val2); 11722be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 117354667612SMårten Lindahl switch (dir) { 117454667612SMårten Lindahl case IIO_EV_DIR_RISING: 117554667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 117654667612SMårten Lindahl VCNL4040_PS_THDH_LM); 11772be17b68SAstrid Rost break; 117854667612SMårten Lindahl case IIO_EV_DIR_FALLING: 117954667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 118054667612SMårten Lindahl VCNL4040_PS_THDL_LM); 11812be17b68SAstrid Rost break; 11822be17b68SAstrid Rost default: 11832be17b68SAstrid Rost return -EINVAL; 11842be17b68SAstrid Rost } 11852be17b68SAstrid Rost break; 11862be17b68SAstrid Rost default: 11872be17b68SAstrid Rost return -EINVAL; 11882be17b68SAstrid Rost } 11892be17b68SAstrid Rost break; 11902be17b68SAstrid Rost default: 11912be17b68SAstrid Rost return -EINVAL; 11922be17b68SAstrid Rost } 119354667612SMårten Lindahl if (ret < 0) 119454667612SMårten Lindahl return ret; 119554667612SMårten Lindahl *val = ret; 119654667612SMårten Lindahl return IIO_VAL_INT; 119754667612SMårten Lindahl } 119854667612SMårten Lindahl 119954667612SMårten Lindahl static int vcnl4040_write_event(struct iio_dev *indio_dev, 120054667612SMårten Lindahl const struct iio_chan_spec *chan, 120154667612SMårten Lindahl enum iio_event_type type, 120254667612SMårten Lindahl enum iio_event_direction dir, 120354667612SMårten Lindahl enum iio_event_info info, 120454667612SMårten Lindahl int val, int val2) 120554667612SMårten Lindahl { 120654667612SMårten Lindahl int ret; 120754667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 120854667612SMårten Lindahl 12092be17b68SAstrid Rost switch (chan->type) { 1210bc292aafSAstrid Rost case IIO_LIGHT: 1211bc292aafSAstrid Rost switch (info) { 12127f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 12137f865127SAstrid Rost return vcnl4040_write_als_period(data, val, val2); 1214bc292aafSAstrid Rost case IIO_EV_INFO_VALUE: 1215bc292aafSAstrid Rost switch (dir) { 1216bc292aafSAstrid Rost case IIO_EV_DIR_RISING: 1217bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 1218bc292aafSAstrid Rost VCNL4040_ALS_THDH_LM, 1219bc292aafSAstrid Rost val); 1220bc292aafSAstrid Rost break; 1221bc292aafSAstrid Rost case IIO_EV_DIR_FALLING: 1222bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 1223bc292aafSAstrid Rost VCNL4040_ALS_THDL_LM, 1224bc292aafSAstrid Rost val); 1225bc292aafSAstrid Rost break; 1226bc292aafSAstrid Rost default: 1227bc292aafSAstrid Rost return -EINVAL; 1228bc292aafSAstrid Rost } 1229bc292aafSAstrid Rost break; 1230bc292aafSAstrid Rost default: 1231bc292aafSAstrid Rost return -EINVAL; 1232bc292aafSAstrid Rost } 1233bc292aafSAstrid Rost break; 12342be17b68SAstrid Rost case IIO_PROXIMITY: 12352be17b68SAstrid Rost switch (info) { 12367f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 12377f865127SAstrid Rost return vcnl4040_write_ps_period(data, val, val2); 12382be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 123954667612SMårten Lindahl switch (dir) { 124054667612SMårten Lindahl case IIO_EV_DIR_RISING: 124154667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 12422be17b68SAstrid Rost VCNL4040_PS_THDH_LM, 12432be17b68SAstrid Rost val); 12442be17b68SAstrid Rost break; 124554667612SMårten Lindahl case IIO_EV_DIR_FALLING: 124654667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 12472be17b68SAstrid Rost VCNL4040_PS_THDL_LM, 12482be17b68SAstrid Rost val); 12492be17b68SAstrid Rost break; 125054667612SMårten Lindahl default: 125154667612SMårten Lindahl return -EINVAL; 125254667612SMårten Lindahl } 12532be17b68SAstrid Rost break; 12542be17b68SAstrid Rost default: 12552be17b68SAstrid Rost return -EINVAL; 12562be17b68SAstrid Rost } 12572be17b68SAstrid Rost break; 12582be17b68SAstrid Rost default: 12592be17b68SAstrid Rost return -EINVAL; 12602be17b68SAstrid Rost } 12612be17b68SAstrid Rost if (ret < 0) 12622be17b68SAstrid Rost return ret; 12632be17b68SAstrid Rost return IIO_VAL_INT; 126454667612SMårten Lindahl } 126554667612SMårten Lindahl 1266d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 1267d35567fcSMathieu Othacehe { 1268d35567fcSMathieu Othacehe int ret; 1269d35567fcSMathieu Othacehe 1270d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 1271d35567fcSMathieu Othacehe if (ret < 0) 1272d35567fcSMathieu Othacehe return false; 1273d35567fcSMathieu Othacehe 1274d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 1275d35567fcSMathieu Othacehe } 1276d35567fcSMathieu Othacehe 1277d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 1278d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1279d35567fcSMathieu Othacehe enum iio_event_type type, 1280d35567fcSMathieu Othacehe enum iio_event_direction dir) 1281d35567fcSMathieu Othacehe { 1282d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1283d35567fcSMathieu Othacehe 1284d35567fcSMathieu Othacehe switch (chan->type) { 1285d35567fcSMathieu Othacehe case IIO_PROXIMITY: 1286d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 1287d35567fcSMathieu Othacehe default: 1288d35567fcSMathieu Othacehe return -EINVAL; 1289d35567fcSMathieu Othacehe } 1290d35567fcSMathieu Othacehe } 1291d35567fcSMathieu Othacehe 1292d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 1293d35567fcSMathieu Othacehe { 1294d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1295d35567fcSMathieu Othacehe int ret; 1296d35567fcSMathieu Othacehe int icr; 1297d35567fcSMathieu Othacehe int command; 1298d35567fcSMathieu Othacehe 1299d35567fcSMathieu Othacehe if (state) { 1300d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 1301d35567fcSMathieu Othacehe if (ret) 1302d35567fcSMathieu Othacehe return ret; 1303d35567fcSMathieu Othacehe 1304d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 1305d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 1306d35567fcSMathieu Othacehe 1307d35567fcSMathieu Othacehe /* 1308d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 1309d35567fcSMathieu Othacehe * default. 1310d35567fcSMathieu Othacehe */ 1311d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 1312d35567fcSMathieu Othacehe } else { 1313d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 1314d35567fcSMathieu Othacehe return 0; 1315d35567fcSMathieu Othacehe 1316d35567fcSMathieu Othacehe command = 0; 1317d35567fcSMathieu Othacehe icr = 0; 1318d35567fcSMathieu Othacehe } 1319d35567fcSMathieu Othacehe 1320d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 1321d35567fcSMathieu Othacehe command); 1322d35567fcSMathieu Othacehe if (ret < 0) 1323d35567fcSMathieu Othacehe goto end; 1324d35567fcSMathieu Othacehe 1325d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 1326d35567fcSMathieu Othacehe 1327d35567fcSMathieu Othacehe end: 1328d35567fcSMathieu Othacehe if (state) 1329d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 1330d35567fcSMathieu Othacehe 1331d35567fcSMathieu Othacehe return ret; 1332d35567fcSMathieu Othacehe } 1333d35567fcSMathieu Othacehe 1334d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 1335d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1336d35567fcSMathieu Othacehe enum iio_event_type type, 1337d35567fcSMathieu Othacehe enum iio_event_direction dir, 1338d35567fcSMathieu Othacehe int state) 1339d35567fcSMathieu Othacehe { 1340d35567fcSMathieu Othacehe switch (chan->type) { 1341d35567fcSMathieu Othacehe case IIO_PROXIMITY: 1342d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 1343d35567fcSMathieu Othacehe default: 1344d35567fcSMathieu Othacehe return -EINVAL; 1345d35567fcSMathieu Othacehe } 1346d35567fcSMathieu Othacehe } 1347d35567fcSMathieu Othacehe 134854667612SMårten Lindahl static int vcnl4040_read_event_config(struct iio_dev *indio_dev, 134954667612SMårten Lindahl const struct iio_chan_spec *chan, 135054667612SMårten Lindahl enum iio_event_type type, 135154667612SMårten Lindahl enum iio_event_direction dir) 135254667612SMårten Lindahl { 135354667612SMårten Lindahl int ret; 135454667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 135554667612SMårten Lindahl 13562be17b68SAstrid Rost switch (chan->type) { 1357bc292aafSAstrid Rost case IIO_LIGHT: 1358bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 1359bc292aafSAstrid Rost if (ret < 0) 1360bc292aafSAstrid Rost return ret; 1361bc292aafSAstrid Rost 1362bc292aafSAstrid Rost data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret); 1363bc292aafSAstrid Rost 1364bc292aafSAstrid Rost return data->als_int; 13652be17b68SAstrid Rost case IIO_PROXIMITY: 136654667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 136754667612SMårten Lindahl if (ret < 0) 136854667612SMårten Lindahl return ret; 136954667612SMårten Lindahl 137054667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); 137154667612SMårten Lindahl 137254667612SMårten Lindahl return (dir == IIO_EV_DIR_RISING) ? 137354667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : 137454667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); 13752be17b68SAstrid Rost default: 13762be17b68SAstrid Rost return -EINVAL; 13772be17b68SAstrid Rost } 137854667612SMårten Lindahl } 137954667612SMårten Lindahl 138054667612SMårten Lindahl static int vcnl4040_write_event_config(struct iio_dev *indio_dev, 138154667612SMårten Lindahl const struct iio_chan_spec *chan, 138254667612SMårten Lindahl enum iio_event_type type, 138354667612SMårten Lindahl enum iio_event_direction dir, int state) 138454667612SMårten Lindahl { 13852be17b68SAstrid Rost int ret = -EINVAL; 138654667612SMårten Lindahl u16 val, mask; 138754667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 138854667612SMårten Lindahl 138954667612SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 139054667612SMårten Lindahl 13912be17b68SAstrid Rost switch (chan->type) { 1392bc292aafSAstrid Rost case IIO_LIGHT: 1393bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 1394bc292aafSAstrid Rost if (ret < 0) 1395bc292aafSAstrid Rost goto out; 1396bc292aafSAstrid Rost 1397bc292aafSAstrid Rost mask = VCNL4040_ALS_CONF_INT_EN; 1398bc292aafSAstrid Rost if (state) 1399bc292aafSAstrid Rost val = (ret | mask); 1400bc292aafSAstrid Rost else 1401bc292aafSAstrid Rost val = (ret & ~mask); 1402bc292aafSAstrid Rost 1403bc292aafSAstrid Rost data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val); 1404bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 1405bc292aafSAstrid Rost val); 1406bc292aafSAstrid Rost break; 14072be17b68SAstrid Rost case IIO_PROXIMITY: 140854667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 140954667612SMårten Lindahl if (ret < 0) 141054667612SMårten Lindahl goto out; 141154667612SMårten Lindahl 141254667612SMårten Lindahl if (dir == IIO_EV_DIR_RISING) 141354667612SMårten Lindahl mask = VCNL4040_PS_IF_AWAY; 141454667612SMårten Lindahl else 141554667612SMårten Lindahl mask = VCNL4040_PS_IF_CLOSE; 141654667612SMårten Lindahl 141754667612SMårten Lindahl val = state ? (ret | mask) : (ret & ~mask); 141854667612SMårten Lindahl 141954667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); 14202be17b68SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 14212be17b68SAstrid Rost val); 14222be17b68SAstrid Rost break; 14232be17b68SAstrid Rost default: 14242be17b68SAstrid Rost break; 14252be17b68SAstrid Rost } 142654667612SMårten Lindahl 142754667612SMårten Lindahl out: 142854667612SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 1429bc292aafSAstrid Rost data->chip_spec->set_power_state(data, data->ps_int || data->als_int); 143054667612SMårten Lindahl 143154667612SMårten Lindahl return ret; 143254667612SMårten Lindahl } 143354667612SMårten Lindahl 143454667612SMårten Lindahl static irqreturn_t vcnl4040_irq_thread(int irq, void *p) 143554667612SMårten Lindahl { 143654667612SMårten Lindahl struct iio_dev *indio_dev = p; 143754667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 143854667612SMårten Lindahl int ret; 143954667612SMårten Lindahl 1440854965b7SAstrid Rost ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg); 144154667612SMårten Lindahl if (ret < 0) 144254667612SMårten Lindahl return IRQ_HANDLED; 144354667612SMårten Lindahl 144454667612SMårten Lindahl if (ret & VCNL4040_PS_IF_CLOSE) { 144554667612SMårten Lindahl iio_push_event(indio_dev, 144654667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 144754667612SMårten Lindahl IIO_EV_TYPE_THRESH, 144854667612SMårten Lindahl IIO_EV_DIR_RISING), 144954667612SMårten Lindahl iio_get_time_ns(indio_dev)); 145054667612SMårten Lindahl } 145154667612SMårten Lindahl 145254667612SMårten Lindahl if (ret & VCNL4040_PS_IF_AWAY) { 145354667612SMårten Lindahl iio_push_event(indio_dev, 145454667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 145554667612SMårten Lindahl IIO_EV_TYPE_THRESH, 145654667612SMårten Lindahl IIO_EV_DIR_FALLING), 145754667612SMårten Lindahl iio_get_time_ns(indio_dev)); 145854667612SMårten Lindahl } 145954667612SMårten Lindahl 1460bc292aafSAstrid Rost if (ret & VCNL4040_ALS_FALLING) { 1461bc292aafSAstrid Rost iio_push_event(indio_dev, 1462bc292aafSAstrid Rost IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 1463bc292aafSAstrid Rost IIO_EV_TYPE_THRESH, 1464bc292aafSAstrid Rost IIO_EV_DIR_FALLING), 1465bc292aafSAstrid Rost iio_get_time_ns(indio_dev)); 1466bc292aafSAstrid Rost } 1467bc292aafSAstrid Rost 1468bc292aafSAstrid Rost if (ret & VCNL4040_ALS_RISING) { 1469bc292aafSAstrid Rost iio_push_event(indio_dev, 1470bc292aafSAstrid Rost IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 1471bc292aafSAstrid Rost IIO_EV_TYPE_THRESH, 1472bc292aafSAstrid Rost IIO_EV_DIR_RISING), 1473bc292aafSAstrid Rost iio_get_time_ns(indio_dev)); 1474bc292aafSAstrid Rost } 1475bc292aafSAstrid Rost 147654667612SMårten Lindahl return IRQ_HANDLED; 147754667612SMårten Lindahl } 147854667612SMårten Lindahl 1479d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 1480d35567fcSMathieu Othacehe uintptr_t priv, 1481d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1482d35567fcSMathieu Othacehe char *buf) 1483d35567fcSMathieu Othacehe { 1484d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1485d35567fcSMathieu Othacehe 1486d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 1487d35567fcSMathieu Othacehe } 1488d35567fcSMathieu Othacehe 14893a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 14903a52d32aSMårten Lindahl { 14913a52d32aSMårten Lindahl struct iio_dev *indio_dev = p; 14923a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 14933a52d32aSMårten Lindahl unsigned long isr; 14943a52d32aSMårten Lindahl int ret; 14953a52d32aSMårten Lindahl 14963a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 14973a52d32aSMårten Lindahl if (ret < 0) 14983a52d32aSMårten Lindahl goto end; 14993a52d32aSMårten Lindahl 15003a52d32aSMårten Lindahl isr = ret; 15013a52d32aSMårten Lindahl 15023a52d32aSMårten Lindahl if (isr & VCNL4010_INT_THR) { 15033a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 15043a52d32aSMårten Lindahl iio_push_event(indio_dev, 15053a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 15063a52d32aSMårten Lindahl IIO_PROXIMITY, 15073a52d32aSMårten Lindahl 1, 15083a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 15093a52d32aSMårten Lindahl IIO_EV_DIR_FALLING), 15103a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 15113a52d32aSMårten Lindahl } 15123a52d32aSMårten Lindahl 15133a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 15143a52d32aSMårten Lindahl iio_push_event(indio_dev, 15153a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 15163a52d32aSMårten Lindahl IIO_PROXIMITY, 15173a52d32aSMårten Lindahl 1, 15183a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 15193a52d32aSMårten Lindahl IIO_EV_DIR_RISING), 15203a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 15213a52d32aSMårten Lindahl } 15223a52d32aSMårten Lindahl 15233a52d32aSMårten Lindahl i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 15243a52d32aSMårten Lindahl isr & VCNL4010_INT_THR); 15253a52d32aSMårten Lindahl } 15263a52d32aSMårten Lindahl 15273a52d32aSMårten Lindahl if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 1528f700e55eSMehdi Djait iio_trigger_poll_nested(indio_dev->trig); 15293a52d32aSMårten Lindahl 15303a52d32aSMårten Lindahl end: 15313a52d32aSMårten Lindahl return IRQ_HANDLED; 15323a52d32aSMårten Lindahl } 15333a52d32aSMårten Lindahl 15343a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 15353a52d32aSMårten Lindahl { 15363a52d32aSMårten Lindahl struct iio_poll_func *pf = p; 15373a52d32aSMårten Lindahl struct iio_dev *indio_dev = pf->indio_dev; 15383a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 15393a52d32aSMårten Lindahl const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 15403a52d32aSMårten Lindahl u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 15413a52d32aSMårten Lindahl bool data_read = false; 15423a52d32aSMårten Lindahl unsigned long isr; 15433a52d32aSMårten Lindahl int val = 0; 15443a52d32aSMårten Lindahl int ret; 15453a52d32aSMårten Lindahl 15463a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 15473a52d32aSMårten Lindahl if (ret < 0) 15483a52d32aSMårten Lindahl goto end; 15493a52d32aSMårten Lindahl 15503a52d32aSMårten Lindahl isr = ret; 15513a52d32aSMårten Lindahl 15523a52d32aSMårten Lindahl if (test_bit(0, active_scan_mask)) { 15533a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 15543a52d32aSMårten Lindahl ret = vcnl4000_read_data(data, 15553a52d32aSMårten Lindahl VCNL4000_PS_RESULT_HI, 15563a52d32aSMårten Lindahl &val); 15573a52d32aSMårten Lindahl if (ret < 0) 15583a52d32aSMårten Lindahl goto end; 15593a52d32aSMårten Lindahl 15603a52d32aSMårten Lindahl buffer[0] = val; 15613a52d32aSMårten Lindahl data_read = true; 15623a52d32aSMårten Lindahl } 15633a52d32aSMårten Lindahl } 15643a52d32aSMårten Lindahl 15653a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 15663a52d32aSMårten Lindahl isr & VCNL4010_INT_DRDY); 15673a52d32aSMårten Lindahl if (ret < 0) 15683a52d32aSMårten Lindahl goto end; 15693a52d32aSMårten Lindahl 15703a52d32aSMårten Lindahl if (!data_read) 15713a52d32aSMårten Lindahl goto end; 15723a52d32aSMårten Lindahl 15733a52d32aSMårten Lindahl iio_push_to_buffers_with_timestamp(indio_dev, buffer, 15743a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 15753a52d32aSMårten Lindahl 15763a52d32aSMårten Lindahl end: 15773a52d32aSMårten Lindahl iio_trigger_notify_done(indio_dev->trig); 15783a52d32aSMårten Lindahl return IRQ_HANDLED; 15793a52d32aSMårten Lindahl } 15803a52d32aSMårten Lindahl 15813a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 15823a52d32aSMårten Lindahl { 15833a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 15843a52d32aSMårten Lindahl int ret; 15853a52d32aSMårten Lindahl int cmd; 15863a52d32aSMårten Lindahl 15873a52d32aSMårten Lindahl /* Do not enable the buffer if we are already capturing events. */ 15883a52d32aSMårten Lindahl if (vcnl4010_is_in_periodic_mode(data)) 15893a52d32aSMårten Lindahl return -EBUSY; 15903a52d32aSMårten Lindahl 15913a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 15923a52d32aSMårten Lindahl VCNL4010_INT_PROX_EN); 15933a52d32aSMårten Lindahl if (ret < 0) 15943a52d32aSMårten Lindahl return ret; 15953a52d32aSMårten Lindahl 15963a52d32aSMårten Lindahl cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 15973a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 15983a52d32aSMårten Lindahl } 15993a52d32aSMårten Lindahl 16003a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 16013a52d32aSMårten Lindahl { 16023a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 16033a52d32aSMårten Lindahl int ret; 16043a52d32aSMårten Lindahl 16053a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 16063a52d32aSMårten Lindahl if (ret < 0) 16073a52d32aSMårten Lindahl return ret; 16083a52d32aSMårten Lindahl 16093a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 16103a52d32aSMårten Lindahl } 16113a52d32aSMårten Lindahl 16123a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 16133a52d32aSMårten Lindahl .postenable = &vcnl4010_buffer_postenable, 16143a52d32aSMårten Lindahl .predisable = &vcnl4010_buffer_predisable, 16153a52d32aSMårten Lindahl }; 16163a52d32aSMårten Lindahl 1617d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 1618d35567fcSMathieu Othacehe { 1619d35567fcSMathieu Othacehe .name = "nearlevel", 1620d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 1621d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 1622d35567fcSMathieu Othacehe }, 1623d35567fcSMathieu Othacehe { /* sentinel */ } 1624d35567fcSMathieu Othacehe }; 1625d35567fcSMathieu Othacehe 1626d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 1627d35567fcSMathieu Othacehe { 1628d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1629d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 1630d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1631d35567fcSMathieu Othacehe }, { 1632d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1633d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 1634d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1635d35567fcSMathieu Othacehe }, { 1636d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1637d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 1638d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 1639d35567fcSMathieu Othacehe } 1640d35567fcSMathieu Othacehe }; 1641d35567fcSMathieu Othacehe 16427f865127SAstrid Rost static const struct iio_event_spec vcnl4040_als_event_spec[] = { 16437f865127SAstrid Rost { 16447f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 16457f865127SAstrid Rost .dir = IIO_EV_DIR_RISING, 16467f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_VALUE), 16477f865127SAstrid Rost }, { 16487f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 16497f865127SAstrid Rost .dir = IIO_EV_DIR_FALLING, 16507f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_VALUE), 16517f865127SAstrid Rost }, { 16527f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 16537f865127SAstrid Rost .dir = IIO_EV_DIR_EITHER, 16547f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD), 16557f865127SAstrid Rost }, 16567f865127SAstrid Rost }; 16577f865127SAstrid Rost 165854667612SMårten Lindahl static const struct iio_event_spec vcnl4040_event_spec[] = { 165954667612SMårten Lindahl { 166054667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 166154667612SMårten Lindahl .dir = IIO_EV_DIR_RISING, 166254667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 166354667612SMårten Lindahl }, { 166454667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 166554667612SMårten Lindahl .dir = IIO_EV_DIR_FALLING, 166654667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 16677f865127SAstrid Rost }, { 16687f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 16697f865127SAstrid Rost .dir = IIO_EV_DIR_EITHER, 16707f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_PERIOD), 167154667612SMårten Lindahl }, 167254667612SMårten Lindahl }; 167354667612SMårten Lindahl 1674d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 1675d35567fcSMathieu Othacehe { 1676d35567fcSMathieu Othacehe .type = IIO_LIGHT, 1677d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1678d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1679d35567fcSMathieu Othacehe }, { 1680d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 1681d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1682d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 1683d35567fcSMathieu Othacehe } 1684d35567fcSMathieu Othacehe }; 1685d35567fcSMathieu Othacehe 1686d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 1687d35567fcSMathieu Othacehe { 1688d35567fcSMathieu Othacehe .type = IIO_LIGHT, 16898fe78d52SMathieu Othacehe .scan_index = -1, 1690d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1691d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1692d35567fcSMathieu Othacehe }, { 1693d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 16948fe78d52SMathieu Othacehe .scan_index = 0, 1695f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1696f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 1697f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1698d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 1699d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 1700d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 17018fe78d52SMathieu Othacehe .scan_type = { 17028fe78d52SMathieu Othacehe .sign = 'u', 17038fe78d52SMathieu Othacehe .realbits = 16, 17048fe78d52SMathieu Othacehe .storagebits = 16, 17058fe78d52SMathieu Othacehe .endianness = IIO_CPU, 1706d35567fcSMathieu Othacehe }, 17078fe78d52SMathieu Othacehe }, 17088fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 1709d35567fcSMathieu Othacehe }; 1710d35567fcSMathieu Othacehe 171185e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = { 171285e2c6a2SMårten Lindahl { 171385e2c6a2SMårten Lindahl .type = IIO_LIGHT, 171485e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1715fea2c97dSAstrid Rost BIT(IIO_CHAN_INFO_SCALE) | 1716fea2c97dSAstrid Rost BIT(IIO_CHAN_INFO_INT_TIME), 1717fea2c97dSAstrid Rost .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 17187f865127SAstrid Rost .event_spec = vcnl4040_als_event_spec, 17197f865127SAstrid Rost .num_event_specs = ARRAY_SIZE(vcnl4040_als_event_spec), 172085e2c6a2SMårten Lindahl }, { 172185e2c6a2SMårten Lindahl .type = IIO_PROXIMITY, 172285e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1723*add98466SAstrid Rost BIT(IIO_CHAN_INFO_INT_TIME) | 1724*add98466SAstrid Rost BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 1725*add98466SAstrid Rost .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | 1726*add98466SAstrid Rost BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 172785e2c6a2SMårten Lindahl .ext_info = vcnl4000_ext_info, 172854667612SMårten Lindahl .event_spec = vcnl4040_event_spec, 172954667612SMårten Lindahl .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), 173085e2c6a2SMårten Lindahl } 173185e2c6a2SMårten Lindahl }; 173285e2c6a2SMårten Lindahl 173362a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 173462a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 173562a1efb9SPeter Meerwald }; 173662a1efb9SPeter Meerwald 1737d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 1738d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 1739f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 1740f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 1741d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 1742d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 1743d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 1744d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 1745d35567fcSMathieu Othacehe }; 1746d35567fcSMathieu Othacehe 174785e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = { 174885e2c6a2SMårten Lindahl .read_raw = vcnl4000_read_raw, 174985e2c6a2SMårten Lindahl .write_raw = vcnl4040_write_raw, 175054667612SMårten Lindahl .read_event_value = vcnl4040_read_event, 175154667612SMårten Lindahl .write_event_value = vcnl4040_write_event, 175254667612SMårten Lindahl .read_event_config = vcnl4040_read_event_config, 175354667612SMårten Lindahl .write_event_config = vcnl4040_write_event_config, 175485e2c6a2SMårten Lindahl .read_avail = vcnl4040_read_avail, 175585e2c6a2SMårten Lindahl }; 175685e2c6a2SMårten Lindahl 1757d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 1758d35567fcSMathieu Othacehe [VCNL4000] = { 1759d35567fcSMathieu Othacehe .prod = "VCNL4000", 1760d35567fcSMathieu Othacehe .init = vcnl4000_init, 1761d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1762d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1763d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1764d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1765d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1766d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1767d35567fcSMathieu Othacehe }, 1768d35567fcSMathieu Othacehe [VCNL4010] = { 1769d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 1770d35567fcSMathieu Othacehe .init = vcnl4000_init, 1771d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1772d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1773d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1774d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 1775d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 1776d35567fcSMathieu Othacehe .info = &vcnl4010_info, 1777bfb6cfeeSMårten Lindahl .irq_thread = vcnl4010_irq_thread, 1778bfb6cfeeSMårten Lindahl .trig_buffer_func = vcnl4010_trigger_handler, 1779bfb6cfeeSMårten Lindahl .buffer_setup_ops = &vcnl4010_buffer_ops, 1780d35567fcSMathieu Othacehe }, 1781d35567fcSMathieu Othacehe [VCNL4040] = { 1782d35567fcSMathieu Othacehe .prod = "VCNL4040", 1783d35567fcSMathieu Othacehe .init = vcnl4200_init, 1784d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1785d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1786d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 178785e2c6a2SMårten Lindahl .channels = vcnl4040_channels, 178885e2c6a2SMårten Lindahl .num_channels = ARRAY_SIZE(vcnl4040_channels), 178985e2c6a2SMårten Lindahl .info = &vcnl4040_info, 179054667612SMårten Lindahl .irq_thread = vcnl4040_irq_thread, 1791854965b7SAstrid Rost .int_reg = VCNL4040_INT_FLAGS, 1792e55c96daSAstrid Rost .ps_it_times = &vcnl4040_ps_it_times, 1793e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), 1794fea2c97dSAstrid Rost .als_it_times = &vcnl4040_als_it_times, 1795fea2c97dSAstrid Rost .num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times), 1796fea2c97dSAstrid Rost .ulux_step = 100000, 1797d35567fcSMathieu Othacehe }, 1798d35567fcSMathieu Othacehe [VCNL4200] = { 1799d35567fcSMathieu Othacehe .prod = "VCNL4200", 1800d35567fcSMathieu Othacehe .init = vcnl4200_init, 1801d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1802d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1803d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1804854965b7SAstrid Rost .channels = vcnl4040_channels, 1805d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1806854965b7SAstrid Rost .info = &vcnl4040_info, 1807854965b7SAstrid Rost .irq_thread = vcnl4040_irq_thread, 1808854965b7SAstrid Rost .int_reg = VCNL4200_INT_FLAGS, 1809e55c96daSAstrid Rost .ps_it_times = &vcnl4200_ps_it_times, 1810e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times), 1811fea2c97dSAstrid Rost .als_it_times = &vcnl4200_als_it_times, 1812fea2c97dSAstrid Rost .num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times), 1813fea2c97dSAstrid Rost .ulux_step = 24000, 1814d35567fcSMathieu Othacehe }, 1815d35567fcSMathieu Othacehe }; 1816d35567fcSMathieu Othacehe 18178fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 18188fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 18198fe78d52SMathieu Othacehe }; 18208fe78d52SMathieu Othacehe 18218fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 18228fe78d52SMathieu Othacehe { 18238fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 18248fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 18258fe78d52SMathieu Othacehe struct iio_trigger *trigger; 18268fe78d52SMathieu Othacehe 18278fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 182815ea2878SJonathan Cameron indio_dev->name, 182915ea2878SJonathan Cameron iio_device_id(indio_dev)); 18308fe78d52SMathieu Othacehe if (!trigger) 18318fe78d52SMathieu Othacehe return -ENOMEM; 18328fe78d52SMathieu Othacehe 18338fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 18348fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 18358fe78d52SMathieu Othacehe 18368fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 18378fe78d52SMathieu Othacehe } 18388fe78d52SMathieu Othacehe 1839e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client) 184062a1efb9SPeter Meerwald { 1841e61295e0SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client); 184262a1efb9SPeter Meerwald struct vcnl4000_data *data; 184362a1efb9SPeter Meerwald struct iio_dev *indio_dev; 18441ebc787aSTomas Novotny int ret; 184562a1efb9SPeter Meerwald 18462669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 184762a1efb9SPeter Meerwald if (!indio_dev) 184862a1efb9SPeter Meerwald return -ENOMEM; 184962a1efb9SPeter Meerwald 185062a1efb9SPeter Meerwald data = iio_priv(indio_dev); 185162a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 185262a1efb9SPeter Meerwald data->client = client; 18531ebc787aSTomas Novotny data->id = id->driver_data; 18541ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 185562a1efb9SPeter Meerwald 185642ec40b0SMårten Lindahl mutex_init(&data->vcnl4000_lock); 185742ec40b0SMårten Lindahl 18581ebc787aSTomas Novotny ret = data->chip_spec->init(data); 185962a1efb9SPeter Meerwald if (ret < 0) 18602669d723SPeter Meerwald return ret; 186162a1efb9SPeter Meerwald 1862d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 18631ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 186462a1efb9SPeter Meerwald 1865f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1866f5a98e1fSGuido Günther &data->near_level)) 1867f5a98e1fSGuido Günther data->near_level = 0; 1868f5a98e1fSGuido Günther 1869d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1870d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1871d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 187262a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 187362a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 187462a1efb9SPeter Meerwald 1875bfb6cfeeSMårten Lindahl if (data->chip_spec->trig_buffer_func && 1876bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops) { 18778fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 18788fe78d52SMathieu Othacehe NULL, 1879bfb6cfeeSMårten Lindahl data->chip_spec->trig_buffer_func, 1880bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops); 18818fe78d52SMathieu Othacehe if (ret < 0) { 18828fe78d52SMathieu Othacehe dev_err(&client->dev, 18838fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 18848fe78d52SMathieu Othacehe return ret; 18858fe78d52SMathieu Othacehe } 1886bfb6cfeeSMårten Lindahl } 18878fe78d52SMathieu Othacehe 1888bfb6cfeeSMårten Lindahl if (client->irq && data->chip_spec->irq_thread) { 1889d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1890bfb6cfeeSMårten Lindahl NULL, data->chip_spec->irq_thread, 1891d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1892d35567fcSMathieu Othacehe IRQF_ONESHOT, 1893bfb6cfeeSMårten Lindahl "vcnl4000_irq", 1894d35567fcSMathieu Othacehe indio_dev); 1895d35567fcSMathieu Othacehe if (ret < 0) { 1896d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1897d35567fcSMathieu Othacehe return ret; 1898d35567fcSMathieu Othacehe } 18998fe78d52SMathieu Othacehe 19008fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 19018fe78d52SMathieu Othacehe if (ret < 0) 19028fe78d52SMathieu Othacehe return ret; 1903d35567fcSMathieu Othacehe } 1904d35567fcSMathieu Othacehe 19055e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 19065e00708dSGuido Günther if (ret < 0) 19075e00708dSGuido Günther goto fail_poweroff; 19085e00708dSGuido Günther 19095e00708dSGuido Günther ret = iio_device_register(indio_dev); 19105e00708dSGuido Günther if (ret < 0) 19115e00708dSGuido Günther goto fail_poweroff; 19125e00708dSGuido Günther 19135e00708dSGuido Günther pm_runtime_enable(&client->dev); 19145e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 19155e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 19165e00708dSGuido Günther 19175e00708dSGuido Günther return 0; 19185e00708dSGuido Günther fail_poweroff: 19195e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 19205e00708dSGuido Günther return ret; 192162a1efb9SPeter Meerwald } 192262a1efb9SPeter Meerwald 1923ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1924ebd457d5SAngus Ainslie (Purism) { 1925ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 19261436a78cSMarco Felsch .data = (void *)VCNL4000, 1927ebd457d5SAngus Ainslie (Purism) }, 1928ebd457d5SAngus Ainslie (Purism) { 1929ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 19301436a78cSMarco Felsch .data = (void *)VCNL4010, 1931ebd457d5SAngus Ainslie (Purism) }, 1932ebd457d5SAngus Ainslie (Purism) { 19331436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 19341436a78cSMarco Felsch .data = (void *)VCNL4010, 1935ebd457d5SAngus Ainslie (Purism) }, 1936ebd457d5SAngus Ainslie (Purism) { 19377fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 19387fd1c260SMarco Felsch .data = (void *)VCNL4040, 19397fd1c260SMarco Felsch }, 19407fd1c260SMarco Felsch { 1941ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 19421436a78cSMarco Felsch .data = (void *)VCNL4200, 1943ebd457d5SAngus Ainslie (Purism) }, 1944ebd457d5SAngus Ainslie (Purism) {}, 1945ebd457d5SAngus Ainslie (Purism) }; 1946ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1947ebd457d5SAngus Ainslie (Purism) 1948ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 19495e00708dSGuido Günther { 19505e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 19515e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 1952ab91da2fSUwe Kleine-König int ret; 19535e00708dSGuido Günther 19545e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 19555e00708dSGuido Günther pm_runtime_disable(&client->dev); 19565e00708dSGuido Günther iio_device_unregister(indio_dev); 19575e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 19585e00708dSGuido Günther 1959ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 1960ab91da2fSUwe Kleine-König if (ret) 1961ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 1962ab91da2fSUwe Kleine-König ERR_PTR(ret)); 19635e00708dSGuido Günther } 19645e00708dSGuido Günther 1965cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 19665e00708dSGuido Günther { 19675e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 19685e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 19695e00708dSGuido Günther 19705e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 19715e00708dSGuido Günther } 19725e00708dSGuido Günther 1973cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 19745e00708dSGuido Günther { 19755e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 19765e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 19775e00708dSGuido Günther 19785e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 19795e00708dSGuido Günther } 19805e00708dSGuido Günther 1981cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 1982cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 19835e00708dSGuido Günther 198462a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 198562a1efb9SPeter Meerwald .driver = { 198662a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 1987cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 1988ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 198962a1efb9SPeter Meerwald }, 19907cf15f42SUwe Kleine-König .probe = vcnl4000_probe, 199162a1efb9SPeter Meerwald .id_table = vcnl4000_id, 19925e00708dSGuido Günther .remove = vcnl4000_remove, 199362a1efb9SPeter Meerwald }; 199462a1efb9SPeter Meerwald 199562a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 199662a1efb9SPeter Meerwald 199762a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 19988fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 199962a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 200062a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 2001