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 */ 64add98466SAstrid 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 */ 93*44b90383SMårten Lindahl #define VCNL4040_PS_CONF2_PS_HD BIT(11) /* Proximity high definition */ 9454667612SMårten Lindahl #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ 95add98466SAstrid Rost #define VCNL4040_PS_CONF3_MPS GENMASK(6, 5) /* Proximity multi pulse number */ 96bb33e751SAstrid Rost #define VCNL4040_PS_MS_LED_I GENMASK(10, 8) /* Proximity current */ 9754667612SMårten Lindahl #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ 9854667612SMårten Lindahl #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ 99bc292aafSAstrid Rost #define VCNL4040_ALS_RISING BIT(12) /* Ambient Light cross high threshold */ 100bc292aafSAstrid Rost #define VCNL4040_ALS_FALLING BIT(13) /* Ambient Light cross low threshold */ 101e21b5b1fSMårten Lindahl 102d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 103d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 104d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 105d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 106d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 107d35567fcSMathieu Othacehe 108d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 109d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 110d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 111d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 112d35567fcSMathieu Othacehe 113d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 114d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 115d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 116d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 117d35567fcSMathieu Othacehe 118*44b90383SMårten Lindahl #define VCNL4040_CONF3_PS_MPS_16BITS 3 /* 8 multi pulses */ 119*44b90383SMårten Lindahl #define VCNL4040_CONF3_PS_LED_I_16BITS 3 /* 120 mA */ 120*44b90383SMårten Lindahl 121*44b90383SMårten Lindahl #define VCNL4040_CONF3_PS_SAMPLE_16BITS \ 122*44b90383SMårten Lindahl (FIELD_PREP(VCNL4040_PS_CONF3_MPS, VCNL4040_CONF3_PS_MPS_16BITS) | \ 123*44b90383SMårten Lindahl FIELD_PREP(VCNL4040_PS_MS_LED_I, VCNL4040_CONF3_PS_LED_I_16BITS)) 124*44b90383SMårten Lindahl 125f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 126f6889c1bSMathieu Othacehe {1, 950000}, 127f6889c1bSMathieu Othacehe {3, 906250}, 128f6889c1bSMathieu Othacehe {7, 812500}, 129f6889c1bSMathieu Othacehe {16, 625000}, 130f6889c1bSMathieu Othacehe {31, 250000}, 131f6889c1bSMathieu Othacehe {62, 500000}, 132f6889c1bSMathieu Othacehe {125, 0}, 133f6889c1bSMathieu Othacehe {250, 0}, 134f6889c1bSMathieu Othacehe }; 13562a1efb9SPeter Meerwald 13685e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = { 13785e2c6a2SMårten Lindahl {0, 100}, 13885e2c6a2SMårten Lindahl {0, 150}, 13985e2c6a2SMårten Lindahl {0, 200}, 14085e2c6a2SMårten Lindahl {0, 250}, 14185e2c6a2SMårten Lindahl {0, 300}, 14285e2c6a2SMårten Lindahl {0, 350}, 14385e2c6a2SMårten Lindahl {0, 400}, 14485e2c6a2SMårten Lindahl {0, 800}, 14585e2c6a2SMårten Lindahl }; 14685e2c6a2SMårten Lindahl 147e55c96daSAstrid Rost static const int vcnl4200_ps_it_times[][2] = { 148e55c96daSAstrid Rost {0, 96}, 149e55c96daSAstrid Rost {0, 144}, 150e55c96daSAstrid Rost {0, 192}, 151e55c96daSAstrid Rost {0, 384}, 152e55c96daSAstrid Rost {0, 768}, 153e55c96daSAstrid Rost {0, 864}, 154e55c96daSAstrid Rost }; 155e55c96daSAstrid Rost 156fea2c97dSAstrid Rost static const int vcnl4040_als_it_times[][2] = { 157fea2c97dSAstrid Rost {0, 80000}, 158fea2c97dSAstrid Rost {0, 160000}, 159fea2c97dSAstrid Rost {0, 320000}, 160fea2c97dSAstrid Rost {0, 640000}, 161fea2c97dSAstrid Rost }; 162fea2c97dSAstrid Rost 163fea2c97dSAstrid Rost static const int vcnl4200_als_it_times[][2] = { 164fea2c97dSAstrid Rost {0, 50000}, 165fea2c97dSAstrid Rost {0, 100000}, 166fea2c97dSAstrid Rost {0, 200000}, 167fea2c97dSAstrid Rost {0, 400000}, 168fea2c97dSAstrid Rost }; 169fea2c97dSAstrid Rost 170bb33e751SAstrid Rost static const int vcnl4040_ps_calibbias_ua[][2] = { 171bb33e751SAstrid Rost {0, 50000}, 172bb33e751SAstrid Rost {0, 75000}, 173bb33e751SAstrid Rost {0, 100000}, 174bb33e751SAstrid Rost {0, 120000}, 175bb33e751SAstrid Rost {0, 140000}, 176bb33e751SAstrid Rost {0, 160000}, 177bb33e751SAstrid Rost {0, 180000}, 178bb33e751SAstrid Rost {0, 200000}, 179bb33e751SAstrid Rost }; 180bb33e751SAstrid Rost 1817f865127SAstrid Rost static const int vcnl4040_als_persistence[] = {1, 2, 4, 8}; 1827f865127SAstrid Rost static const int vcnl4040_ps_persistence[] = {1, 2, 3, 4}; 183add98466SAstrid Rost static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8}; 1847f865127SAstrid Rost 1855e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1865e00708dSGuido Günther 1871ebc787aSTomas Novotny enum vcnl4000_device_ids { 1881ebc787aSTomas Novotny VCNL4000, 18950c50b97STomas Novotny VCNL4010, 1905a441aadSAngus Ainslie (Purism) VCNL4040, 191be38866fSTomas Novotny VCNL4200, 192be38866fSTomas Novotny }; 193be38866fSTomas Novotny 194be38866fSTomas Novotny struct vcnl4200_channel { 195be38866fSTomas Novotny u8 reg; 196be38866fSTomas Novotny ktime_t last_measurement; 197be38866fSTomas Novotny ktime_t sampling_rate; 198be38866fSTomas Novotny struct mutex lock; 1991ebc787aSTomas Novotny }; 2001ebc787aSTomas Novotny 20162a1efb9SPeter Meerwald struct vcnl4000_data { 20262a1efb9SPeter Meerwald struct i2c_client *client; 2031ebc787aSTomas Novotny enum vcnl4000_device_ids id; 2041ebc787aSTomas Novotny int rev; 2051ebc787aSTomas Novotny int al_scale; 206*44b90383SMårten Lindahl int ps_scale; 20754667612SMårten Lindahl u8 ps_int; /* proximity interrupt mode */ 208bc292aafSAstrid Rost u8 als_int; /* ambient light interrupt mode*/ 2091ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 210be38866fSTomas Novotny struct mutex vcnl4000_lock; 211be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 212be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 213f5a98e1fSGuido Günther uint32_t near_level; 21462a1efb9SPeter Meerwald }; 21562a1efb9SPeter Meerwald 2161ebc787aSTomas Novotny struct vcnl4000_chip_spec { 2171ebc787aSTomas Novotny const char *prod; 218d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 219d35567fcSMathieu Othacehe const int num_channels; 220d35567fcSMathieu Othacehe const struct iio_info *info; 221bfb6cfeeSMårten Lindahl const struct iio_buffer_setup_ops *buffer_setup_ops; 2221ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 2231ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 2241ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 2255e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 226bfb6cfeeSMårten Lindahl irqreturn_t (*irq_thread)(int irq, void *priv); 227bfb6cfeeSMårten Lindahl irqreturn_t (*trig_buffer_func)(int irq, void *priv); 228854965b7SAstrid Rost 229854965b7SAstrid Rost u8 int_reg; 230e55c96daSAstrid Rost const int(*ps_it_times)[][2]; 231e55c96daSAstrid Rost const int num_ps_it_times; 232fea2c97dSAstrid Rost const int(*als_it_times)[][2]; 233fea2c97dSAstrid Rost const int num_als_it_times; 234fea2c97dSAstrid Rost const unsigned int ulux_step; 2351ebc787aSTomas Novotny }; 2361ebc787aSTomas Novotny 23762a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 2381ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 23950c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 24050c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 2415a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 242be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 24362a1efb9SPeter Meerwald { } 24462a1efb9SPeter Meerwald }; 24562a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 24662a1efb9SPeter Meerwald 2475e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 2485e00708dSGuido Günther { 2495e00708dSGuido Günther /* no suspend op */ 2505e00708dSGuido Günther return 0; 2515e00708dSGuido Günther } 2525e00708dSGuido Günther 2531ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 2541ebc787aSTomas Novotny { 2551ebc787aSTomas Novotny int ret, prod_id; 2561ebc787aSTomas Novotny 2571ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 2581ebc787aSTomas Novotny if (ret < 0) 2591ebc787aSTomas Novotny return ret; 2601ebc787aSTomas Novotny 2611ebc787aSTomas Novotny prod_id = ret >> 4; 26258bf9aceSTomas Novotny switch (prod_id) { 26358bf9aceSTomas Novotny case VCNL4000_PROD_ID: 26458bf9aceSTomas Novotny if (data->id != VCNL4000) 26558bf9aceSTomas Novotny dev_warn(&data->client->dev, 26658bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 26758bf9aceSTomas Novotny break; 26858bf9aceSTomas Novotny case VCNL4010_PROD_ID: 26958bf9aceSTomas Novotny if (data->id != VCNL4010) 27058bf9aceSTomas Novotny dev_warn(&data->client->dev, 27158bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 27258bf9aceSTomas Novotny break; 27358bf9aceSTomas Novotny default: 2741ebc787aSTomas Novotny return -ENODEV; 27558bf9aceSTomas Novotny } 2761ebc787aSTomas Novotny 2771ebc787aSTomas Novotny data->rev = ret & 0xf; 2781ebc787aSTomas Novotny data->al_scale = 250000; 279be38866fSTomas Novotny 2805e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 281be38866fSTomas Novotny }; 282be38866fSTomas Novotny 283e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) 2845e00708dSGuido Günther { 2855e00708dSGuido Günther int ret; 2865e00708dSGuido Günther 287e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 288e21b5b1fSMårten Lindahl 289e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 290e21b5b1fSMårten Lindahl if (ret < 0) 291e21b5b1fSMårten Lindahl goto out; 292e21b5b1fSMårten Lindahl 293e21b5b1fSMårten Lindahl if (en) 294e21b5b1fSMårten Lindahl ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; 295e21b5b1fSMårten Lindahl else 296e21b5b1fSMårten Lindahl ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; 297e21b5b1fSMårten Lindahl 298e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); 299e21b5b1fSMårten Lindahl 300e21b5b1fSMårten Lindahl out: 301e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 302e21b5b1fSMårten Lindahl 303e21b5b1fSMårten Lindahl return ret; 304e21b5b1fSMårten Lindahl } 305e21b5b1fSMårten Lindahl 306e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) 307e21b5b1fSMårten Lindahl { 308e21b5b1fSMårten Lindahl int ret; 309e21b5b1fSMårten Lindahl 310e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 311e21b5b1fSMårten Lindahl 312e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 313e21b5b1fSMårten Lindahl if (ret < 0) 314e21b5b1fSMårten Lindahl goto out; 315e21b5b1fSMårten Lindahl 316e21b5b1fSMårten Lindahl if (en) 317e21b5b1fSMårten Lindahl ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; 318e21b5b1fSMårten Lindahl else 319e21b5b1fSMårten Lindahl ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; 320e21b5b1fSMårten Lindahl 321e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); 322e21b5b1fSMårten Lindahl 323e21b5b1fSMårten Lindahl out: 324e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 325e21b5b1fSMårten Lindahl 326e21b5b1fSMårten Lindahl return ret; 327e21b5b1fSMårten Lindahl } 328e21b5b1fSMårten Lindahl 329e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 330e21b5b1fSMårten Lindahl { 331e21b5b1fSMårten Lindahl int ret; 332e21b5b1fSMårten Lindahl 33354667612SMårten Lindahl /* Do not power down if interrupts are enabled */ 334bc292aafSAstrid Rost if (!on && (data->ps_int || data->als_int)) 33554667612SMårten Lindahl return 0; 33654667612SMårten Lindahl 337e21b5b1fSMårten Lindahl ret = vcnl4000_write_als_enable(data, on); 3385e00708dSGuido Günther if (ret < 0) 3395e00708dSGuido Günther return ret; 3405e00708dSGuido Günther 341e21b5b1fSMårten Lindahl ret = vcnl4000_write_ps_enable(data, on); 3425e00708dSGuido Günther if (ret < 0) 3435e00708dSGuido Günther return ret; 3445e00708dSGuido Günther 3455e00708dSGuido Günther if (on) { 3465e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 3475e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 3485e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 3495e00708dSGuido Günther } 3505e00708dSGuido Günther 3515e00708dSGuido Günther return 0; 3525e00708dSGuido Günther } 3535e00708dSGuido Günther 354be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 355be38866fSTomas Novotny { 3565a441aadSAngus Ainslie (Purism) int ret, id; 357*44b90383SMårten Lindahl u16 regval; 358be38866fSTomas Novotny 359be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 360be38866fSTomas Novotny if (ret < 0) 361be38866fSTomas Novotny return ret; 362be38866fSTomas Novotny 3635a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3645a441aadSAngus Ainslie (Purism) 3655a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 3665a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 3675a441aadSAngus Ainslie (Purism) if (ret < 0) 3685a441aadSAngus Ainslie (Purism) return ret; 3695a441aadSAngus Ainslie (Purism) 3705a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3715a441aadSAngus Ainslie (Purism) 3725a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 373be38866fSTomas Novotny return -ENODEV; 3745a441aadSAngus Ainslie (Purism) } 3755a441aadSAngus Ainslie (Purism) 3765a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 377be38866fSTomas Novotny 378be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 37954667612SMårten Lindahl data->ps_int = 0; 380bc292aafSAstrid Rost data->als_int = 0; 381be38866fSTomas Novotny 382be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 383be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 3845a441aadSAngus Ainslie (Purism) switch (id) { 3855a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 386b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 387b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 388b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 389b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 3905a441aadSAngus Ainslie (Purism) break; 3915a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 3922ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 3932ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 3942ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 3952ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 3965a441aadSAngus Ainslie (Purism) break; 3975a441aadSAngus Ainslie (Purism) } 398fea2c97dSAstrid Rost data->al_scale = data->chip_spec->ulux_step; 399*44b90383SMårten Lindahl data->ps_scale = 16; 400be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 401be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 4021ebc787aSTomas Novotny 403*44b90383SMårten Lindahl /* Use 16 bits proximity sensor readings */ 404*44b90383SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 405*44b90383SMårten Lindahl if (ret < 0) 406*44b90383SMårten Lindahl return ret; 407*44b90383SMårten Lindahl 408*44b90383SMårten Lindahl regval = ret | VCNL4040_PS_CONF2_PS_HD; 409*44b90383SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 410*44b90383SMårten Lindahl regval); 411*44b90383SMårten Lindahl if (ret < 0) 412*44b90383SMårten Lindahl return ret; 413*44b90383SMårten Lindahl 414*44b90383SMårten Lindahl /* Align proximity sensor sample rate to 16 bits data width */ 415*44b90383SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); 416*44b90383SMårten Lindahl if (ret < 0) 417*44b90383SMårten Lindahl return ret; 418*44b90383SMårten Lindahl 419*44b90383SMårten Lindahl regval = ret | VCNL4040_CONF3_PS_SAMPLE_16BITS; 420*44b90383SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, 421*44b90383SMårten Lindahl regval); 422*44b90383SMårten Lindahl if (ret < 0) 423*44b90383SMårten Lindahl return ret; 424*44b90383SMårten Lindahl 4255e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 4265e00708dSGuido Günther if (ret < 0) 4275e00708dSGuido Günther return ret; 4285e00708dSGuido Günther 4291ebc787aSTomas Novotny return 0; 4301ebc787aSTomas Novotny }; 4311ebc787aSTomas Novotny 432816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 433816956c3SMathieu Othacehe { 434816956c3SMathieu Othacehe s32 ret; 435816956c3SMathieu Othacehe 436816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 437816956c3SMathieu Othacehe if (ret < 0) 438816956c3SMathieu Othacehe return ret; 439816956c3SMathieu Othacehe 440816956c3SMathieu Othacehe *val = ret; 441816956c3SMathieu Othacehe return 0; 442816956c3SMathieu Othacehe } 443816956c3SMathieu Othacehe 444816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 445816956c3SMathieu Othacehe { 446816956c3SMathieu Othacehe if (val > U16_MAX) 447816956c3SMathieu Othacehe return -ERANGE; 448816956c3SMathieu Othacehe 449816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 450816956c3SMathieu Othacehe } 451816956c3SMathieu Othacehe 452816956c3SMathieu Othacehe 45362a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 45462a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 45562a1efb9SPeter Meerwald { 45662a1efb9SPeter Meerwald int tries = 20; 45762a1efb9SPeter Meerwald int ret; 45862a1efb9SPeter Meerwald 459be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 460ff34ed6dSPeter Meerwald-Stadler 46162a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 46262a1efb9SPeter Meerwald req_mask); 46362a1efb9SPeter Meerwald if (ret < 0) 464ff34ed6dSPeter Meerwald-Stadler goto fail; 46562a1efb9SPeter Meerwald 46662a1efb9SPeter Meerwald /* wait for data to become ready */ 46762a1efb9SPeter Meerwald while (tries--) { 46862a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 46962a1efb9SPeter Meerwald if (ret < 0) 470ff34ed6dSPeter Meerwald-Stadler goto fail; 47162a1efb9SPeter Meerwald if (ret & rdy_mask) 47262a1efb9SPeter Meerwald break; 47362a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 47462a1efb9SPeter Meerwald } 47562a1efb9SPeter Meerwald 47662a1efb9SPeter Meerwald if (tries < 0) { 47762a1efb9SPeter Meerwald dev_err(&data->client->dev, 47862a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 479ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 480ff34ed6dSPeter Meerwald-Stadler goto fail; 48162a1efb9SPeter Meerwald } 48262a1efb9SPeter Meerwald 483816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 48462a1efb9SPeter Meerwald if (ret < 0) 485ff34ed6dSPeter Meerwald-Stadler goto fail; 48662a1efb9SPeter Meerwald 487be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 48862a1efb9SPeter Meerwald 48962a1efb9SPeter Meerwald return 0; 490ff34ed6dSPeter Meerwald-Stadler 491ff34ed6dSPeter Meerwald-Stadler fail: 492be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 493ff34ed6dSPeter Meerwald-Stadler return ret; 49462a1efb9SPeter Meerwald } 49562a1efb9SPeter Meerwald 496be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 497be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 498be38866fSTomas Novotny { 499be38866fSTomas Novotny int ret; 500be38866fSTomas Novotny s64 delta; 501be38866fSTomas Novotny ktime_t next_measurement; 502be38866fSTomas Novotny 503be38866fSTomas Novotny mutex_lock(&chan->lock); 504be38866fSTomas Novotny 505be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 506be38866fSTomas Novotny chan->sampling_rate); 507be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 508be38866fSTomas Novotny if (delta > 0) 509be38866fSTomas Novotny usleep_range(delta, delta + 500); 510be38866fSTomas Novotny chan->last_measurement = ktime_get(); 511be38866fSTomas Novotny 512be38866fSTomas Novotny mutex_unlock(&chan->lock); 513be38866fSTomas Novotny 514be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 515be38866fSTomas Novotny if (ret < 0) 516be38866fSTomas Novotny return ret; 517be38866fSTomas Novotny 518be38866fSTomas Novotny *val = ret; 519be38866fSTomas Novotny 520be38866fSTomas Novotny return 0; 521be38866fSTomas Novotny } 522be38866fSTomas Novotny 5231ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 5241ebc787aSTomas Novotny { 5251ebc787aSTomas Novotny return vcnl4000_measure(data, 5261ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 5271ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 5281ebc787aSTomas Novotny } 5291ebc787aSTomas Novotny 530be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 531be38866fSTomas Novotny { 532be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 533be38866fSTomas Novotny } 534be38866fSTomas Novotny 5351ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 5361ebc787aSTomas Novotny { 5371ebc787aSTomas Novotny return vcnl4000_measure(data, 5381ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 5391ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 5401ebc787aSTomas Novotny } 5411ebc787aSTomas Novotny 542be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 543be38866fSTomas Novotny { 544be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 545be38866fSTomas Novotny } 546be38866fSTomas Novotny 547f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 548f6889c1bSMathieu Othacehe int *val2) 549f6889c1bSMathieu Othacehe { 550f6889c1bSMathieu Othacehe int ret; 551f6889c1bSMathieu Othacehe 552f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 553f6889c1bSMathieu Othacehe if (ret < 0) 554f6889c1bSMathieu Othacehe return ret; 555f6889c1bSMathieu Othacehe 556f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 557f6889c1bSMathieu Othacehe return -EINVAL; 558f6889c1bSMathieu Othacehe 559f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 560f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 561f6889c1bSMathieu Othacehe 562f6889c1bSMathieu Othacehe return 0; 563f6889c1bSMathieu Othacehe } 564f6889c1bSMathieu Othacehe 565d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 566f5a98e1fSGuido Günther { 567d35567fcSMathieu Othacehe int ret; 568f5a98e1fSGuido Günther 569d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 570d35567fcSMathieu Othacehe if (ret < 0) 571d35567fcSMathieu Othacehe return false; 572d35567fcSMathieu Othacehe 573d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 574f5a98e1fSGuido Günther } 575f5a98e1fSGuido Günther 5765e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 5775e00708dSGuido Günther { 5785e00708dSGuido Günther struct device *dev = &data->client->dev; 5795e00708dSGuido Günther int ret; 5805e00708dSGuido Günther 5815e00708dSGuido Günther if (on) { 582db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 5835e00708dSGuido Günther } else { 5845e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 5855e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 5865e00708dSGuido Günther } 5875e00708dSGuido Günther 5885e00708dSGuido Günther return ret; 5895e00708dSGuido Günther } 5905e00708dSGuido Günther 591fea2c97dSAstrid Rost static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2) 592fea2c97dSAstrid Rost { 593fea2c97dSAstrid Rost int ret; 594fea2c97dSAstrid Rost 595fea2c97dSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 596fea2c97dSAstrid Rost if (ret < 0) 597fea2c97dSAstrid Rost return ret; 598fea2c97dSAstrid Rost 599fea2c97dSAstrid Rost ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 600fea2c97dSAstrid Rost if (ret >= data->chip_spec->num_als_it_times) 601fea2c97dSAstrid Rost return -EINVAL; 602fea2c97dSAstrid Rost 603fea2c97dSAstrid Rost *val = (*data->chip_spec->als_it_times)[ret][0]; 604fea2c97dSAstrid Rost *val2 = (*data->chip_spec->als_it_times)[ret][1]; 605fea2c97dSAstrid Rost 606fea2c97dSAstrid Rost return 0; 607fea2c97dSAstrid Rost } 608fea2c97dSAstrid Rost 609fea2c97dSAstrid Rost static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val) 610fea2c97dSAstrid Rost { 611fea2c97dSAstrid Rost unsigned int i; 612fea2c97dSAstrid Rost int ret; 613fea2c97dSAstrid Rost u16 regval; 614fea2c97dSAstrid Rost 615fea2c97dSAstrid Rost for (i = 0; i < data->chip_spec->num_als_it_times; i++) { 616fea2c97dSAstrid Rost if (val == (*data->chip_spec->als_it_times)[i][1]) 617fea2c97dSAstrid Rost break; 618fea2c97dSAstrid Rost } 619fea2c97dSAstrid Rost 620fea2c97dSAstrid Rost if (i == data->chip_spec->num_als_it_times) 621fea2c97dSAstrid Rost return -EINVAL; 622fea2c97dSAstrid Rost 623fea2c97dSAstrid Rost data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200); 624fea2c97dSAstrid Rost data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step, 625fea2c97dSAstrid Rost (*data->chip_spec->als_it_times)[0][1]), 626fea2c97dSAstrid Rost val); 627fea2c97dSAstrid Rost 628fea2c97dSAstrid Rost mutex_lock(&data->vcnl4000_lock); 629fea2c97dSAstrid Rost 630fea2c97dSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 631fea2c97dSAstrid Rost if (ret < 0) 632fea2c97dSAstrid Rost goto out_unlock; 633fea2c97dSAstrid Rost 634fea2c97dSAstrid Rost regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i); 635fea2c97dSAstrid Rost regval |= (ret & ~VCNL4040_ALS_CONF_IT); 636fea2c97dSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 637fea2c97dSAstrid Rost VCNL4200_AL_CONF, 638fea2c97dSAstrid Rost regval); 639fea2c97dSAstrid Rost 640fea2c97dSAstrid Rost out_unlock: 641fea2c97dSAstrid Rost mutex_unlock(&data->vcnl4000_lock); 642fea2c97dSAstrid Rost return ret; 643fea2c97dSAstrid Rost } 644fea2c97dSAstrid Rost 64585e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) 64685e2c6a2SMårten Lindahl { 64785e2c6a2SMårten Lindahl int ret; 64885e2c6a2SMårten Lindahl 64985e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 65085e2c6a2SMårten Lindahl if (ret < 0) 65185e2c6a2SMårten Lindahl return ret; 65285e2c6a2SMårten Lindahl 65385e2c6a2SMårten Lindahl ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 65485e2c6a2SMårten Lindahl 655e55c96daSAstrid Rost if (ret >= data->chip_spec->num_ps_it_times) 65685e2c6a2SMårten Lindahl return -EINVAL; 65785e2c6a2SMårten Lindahl 658e55c96daSAstrid Rost *val = (*data->chip_spec->ps_it_times)[ret][0]; 659e55c96daSAstrid Rost *val2 = (*data->chip_spec->ps_it_times)[ret][1]; 66085e2c6a2SMårten Lindahl 66185e2c6a2SMårten Lindahl return 0; 66285e2c6a2SMårten Lindahl } 66385e2c6a2SMårten Lindahl 66485e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) 66585e2c6a2SMårten Lindahl { 66685e2c6a2SMårten Lindahl unsigned int i; 66785e2c6a2SMårten Lindahl int ret, index = -1; 66885e2c6a2SMårten Lindahl u16 regval; 66985e2c6a2SMårten Lindahl 670e55c96daSAstrid Rost for (i = 0; i < data->chip_spec->num_ps_it_times; i++) { 671e55c96daSAstrid Rost if (val == (*data->chip_spec->ps_it_times)[i][1]) { 67285e2c6a2SMårten Lindahl index = i; 67385e2c6a2SMårten Lindahl break; 67485e2c6a2SMårten Lindahl } 67585e2c6a2SMårten Lindahl } 67685e2c6a2SMårten Lindahl 67785e2c6a2SMårten Lindahl if (index < 0) 67885e2c6a2SMårten Lindahl return -EINVAL; 67985e2c6a2SMårten Lindahl 680e55c96daSAstrid Rost data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC); 681e55c96daSAstrid Rost 68285e2c6a2SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 68385e2c6a2SMårten Lindahl 68485e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 68585e2c6a2SMårten Lindahl if (ret < 0) 68685e2c6a2SMårten Lindahl goto out; 68785e2c6a2SMårten Lindahl 68885e2c6a2SMårten Lindahl regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | 68985e2c6a2SMårten Lindahl FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); 69085e2c6a2SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 69185e2c6a2SMårten Lindahl regval); 69285e2c6a2SMårten Lindahl 69385e2c6a2SMårten Lindahl out: 69485e2c6a2SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 69585e2c6a2SMårten Lindahl return ret; 69685e2c6a2SMårten Lindahl } 69785e2c6a2SMårten Lindahl 6987f865127SAstrid Rost static ssize_t vcnl4040_read_als_period(struct vcnl4000_data *data, int *val, int *val2) 6997f865127SAstrid Rost { 7007f865127SAstrid Rost int ret, ret_pers, it; 7017f865127SAstrid Rost int64_t val_c; 7027f865127SAstrid Rost 7037f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 7047f865127SAstrid Rost if (ret < 0) 7057f865127SAstrid Rost return ret; 7067f865127SAstrid Rost 7077f865127SAstrid Rost ret_pers = FIELD_GET(VCNL4040_ALS_CONF_PERS, ret); 7087f865127SAstrid Rost if (ret_pers >= ARRAY_SIZE(vcnl4040_als_persistence)) 7097f865127SAstrid Rost return -EINVAL; 7107f865127SAstrid Rost 7117f865127SAstrid Rost it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 7127f865127SAstrid Rost if (it >= data->chip_spec->num_als_it_times) 7137f865127SAstrid Rost return -EINVAL; 7147f865127SAstrid Rost 7157f865127SAstrid Rost val_c = mul_u32_u32((*data->chip_spec->als_it_times)[it][1], 7167f865127SAstrid Rost vcnl4040_als_persistence[ret_pers]); 7177f865127SAstrid Rost *val = div_u64_rem(val_c, MICRO, val2); 7187f865127SAstrid Rost 7197f865127SAstrid Rost return IIO_VAL_INT_PLUS_MICRO; 7207f865127SAstrid Rost } 7217f865127SAstrid Rost 7227f865127SAstrid Rost static ssize_t vcnl4040_write_als_period(struct vcnl4000_data *data, int val, int val2) 7237f865127SAstrid Rost { 7247f865127SAstrid Rost unsigned int i; 7257f865127SAstrid Rost int ret, it; 7267f865127SAstrid Rost u16 regval; 7277f865127SAstrid Rost u64 val_n = mul_u32_u32(val, MICRO) + val2; 7287f865127SAstrid Rost 7297f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 7307f865127SAstrid Rost if (ret < 0) 7317f865127SAstrid Rost return ret; 7327f865127SAstrid Rost 7337f865127SAstrid Rost it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 7347f865127SAstrid Rost if (it >= data->chip_spec->num_als_it_times) 7357f865127SAstrid Rost return -EINVAL; 7367f865127SAstrid Rost 7377f865127SAstrid Rost for (i = 0; i < ARRAY_SIZE(vcnl4040_als_persistence) - 1; i++) { 7387f865127SAstrid Rost if (val_n < mul_u32_u32(vcnl4040_als_persistence[i], 7397f865127SAstrid Rost (*data->chip_spec->als_it_times)[it][1])) 7407f865127SAstrid Rost break; 7417f865127SAstrid Rost } 7427f865127SAstrid Rost 7437f865127SAstrid Rost mutex_lock(&data->vcnl4000_lock); 7447f865127SAstrid Rost 7457f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 7467f865127SAstrid Rost if (ret < 0) 7477f865127SAstrid Rost goto out_unlock; 7487f865127SAstrid Rost 7497f865127SAstrid Rost regval = FIELD_PREP(VCNL4040_ALS_CONF_PERS, i); 7507f865127SAstrid Rost regval |= (ret & ~VCNL4040_ALS_CONF_PERS); 7517f865127SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 7527f865127SAstrid Rost regval); 7537f865127SAstrid Rost 7547f865127SAstrid Rost out_unlock: 7557f865127SAstrid Rost mutex_unlock(&data->vcnl4000_lock); 7567f865127SAstrid Rost return ret; 7577f865127SAstrid Rost } 7587f865127SAstrid Rost 7597f865127SAstrid Rost static ssize_t vcnl4040_read_ps_period(struct vcnl4000_data *data, int *val, int *val2) 7607f865127SAstrid Rost { 7617f865127SAstrid Rost int ret, ret_pers, it; 7627f865127SAstrid Rost 7637f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 7647f865127SAstrid Rost if (ret < 0) 7657f865127SAstrid Rost return ret; 7667f865127SAstrid Rost 7677f865127SAstrid Rost ret_pers = FIELD_GET(VCNL4040_CONF1_PS_PERS, ret); 7687f865127SAstrid Rost if (ret_pers >= ARRAY_SIZE(vcnl4040_ps_persistence)) 7697f865127SAstrid Rost return -EINVAL; 7707f865127SAstrid Rost 7717f865127SAstrid Rost it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 7727f865127SAstrid Rost if (it >= data->chip_spec->num_ps_it_times) 7737f865127SAstrid Rost return -EINVAL; 7747f865127SAstrid Rost 7757f865127SAstrid Rost *val = (*data->chip_spec->ps_it_times)[it][0]; 7767f865127SAstrid Rost *val2 = (*data->chip_spec->ps_it_times)[it][1] * 7777f865127SAstrid Rost vcnl4040_ps_persistence[ret_pers]; 7787f865127SAstrid Rost 7797f865127SAstrid Rost return IIO_VAL_INT_PLUS_MICRO; 7807f865127SAstrid Rost } 7817f865127SAstrid Rost 7827f865127SAstrid Rost static ssize_t vcnl4040_write_ps_period(struct vcnl4000_data *data, int val, int val2) 7837f865127SAstrid Rost { 7847f865127SAstrid Rost int ret, it, i; 7857f865127SAstrid Rost u16 regval; 7867f865127SAstrid Rost 7877f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 7887f865127SAstrid Rost if (ret < 0) 7897f865127SAstrid Rost return ret; 7907f865127SAstrid Rost 7917f865127SAstrid Rost it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 7927f865127SAstrid Rost if (it >= data->chip_spec->num_ps_it_times) 7937f865127SAstrid Rost return -EINVAL; 7947f865127SAstrid Rost 7957f865127SAstrid Rost if (val > 0) 7967f865127SAstrid Rost i = ARRAY_SIZE(vcnl4040_ps_persistence) - 1; 7977f865127SAstrid Rost else { 7987f865127SAstrid Rost for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_persistence) - 1; i++) { 7997f865127SAstrid Rost if (val2 <= vcnl4040_ps_persistence[i] * 8007f865127SAstrid Rost (*data->chip_spec->ps_it_times)[it][1]) 8017f865127SAstrid Rost break; 8027f865127SAstrid Rost } 8037f865127SAstrid Rost } 8047f865127SAstrid Rost 8057f865127SAstrid Rost mutex_lock(&data->vcnl4000_lock); 8067f865127SAstrid Rost 8077f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 8087f865127SAstrid Rost if (ret < 0) 8097f865127SAstrid Rost goto out_unlock; 8107f865127SAstrid Rost 8117f865127SAstrid Rost regval = FIELD_PREP(VCNL4040_CONF1_PS_PERS, i); 8127f865127SAstrid Rost regval |= (ret & ~VCNL4040_CONF1_PS_PERS); 8137f865127SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 8147f865127SAstrid Rost regval); 8157f865127SAstrid Rost 8167f865127SAstrid Rost out_unlock: 8177f865127SAstrid Rost mutex_unlock(&data->vcnl4000_lock); 8187f865127SAstrid Rost return ret; 8197f865127SAstrid Rost } 8207f865127SAstrid Rost 821add98466SAstrid Rost static ssize_t vcnl4040_read_ps_oversampling_ratio(struct vcnl4000_data *data, int *val) 822add98466SAstrid Rost { 823add98466SAstrid Rost int ret; 824add98466SAstrid Rost 825add98466SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); 826add98466SAstrid Rost if (ret < 0) 827add98466SAstrid Rost return ret; 828add98466SAstrid Rost 829add98466SAstrid Rost ret = FIELD_GET(VCNL4040_PS_CONF3_MPS, ret); 830add98466SAstrid Rost if (ret >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio)) 831add98466SAstrid Rost return -EINVAL; 832add98466SAstrid Rost 833add98466SAstrid Rost *val = vcnl4040_ps_oversampling_ratio[ret]; 834add98466SAstrid Rost 835add98466SAstrid Rost return ret; 836add98466SAstrid Rost } 837add98466SAstrid Rost 838add98466SAstrid Rost static ssize_t vcnl4040_write_ps_oversampling_ratio(struct vcnl4000_data *data, int val) 839add98466SAstrid Rost { 840add98466SAstrid Rost unsigned int i; 841add98466SAstrid Rost int ret; 842add98466SAstrid Rost u16 regval; 843add98466SAstrid Rost 844add98466SAstrid Rost for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); i++) { 845add98466SAstrid Rost if (val == vcnl4040_ps_oversampling_ratio[i]) 846add98466SAstrid Rost break; 847add98466SAstrid Rost } 848add98466SAstrid Rost 849add98466SAstrid Rost if (i >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio)) 850add98466SAstrid Rost return -EINVAL; 851add98466SAstrid Rost 852add98466SAstrid Rost mutex_lock(&data->vcnl4000_lock); 853add98466SAstrid Rost 854add98466SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); 855add98466SAstrid Rost if (ret < 0) 856add98466SAstrid Rost goto out_unlock; 857add98466SAstrid Rost 858add98466SAstrid Rost regval = FIELD_PREP(VCNL4040_PS_CONF3_MPS, i); 859add98466SAstrid Rost regval |= (ret & ~VCNL4040_PS_CONF3_MPS); 860add98466SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, 861add98466SAstrid Rost regval); 862add98466SAstrid Rost 863add98466SAstrid Rost out_unlock: 864add98466SAstrid Rost mutex_unlock(&data->vcnl4000_lock); 865add98466SAstrid Rost return ret; 866add98466SAstrid Rost } 867add98466SAstrid Rost 868bb33e751SAstrid Rost static ssize_t vcnl4040_read_ps_calibbias(struct vcnl4000_data *data, int *val, int *val2) 869bb33e751SAstrid Rost { 870bb33e751SAstrid Rost int ret; 871bb33e751SAstrid Rost 872bb33e751SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); 873bb33e751SAstrid Rost if (ret < 0) 874bb33e751SAstrid Rost return ret; 875bb33e751SAstrid Rost 876bb33e751SAstrid Rost ret = FIELD_GET(VCNL4040_PS_MS_LED_I, ret); 877bb33e751SAstrid Rost if (ret >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua)) 878bb33e751SAstrid Rost return -EINVAL; 879bb33e751SAstrid Rost 880bb33e751SAstrid Rost *val = vcnl4040_ps_calibbias_ua[ret][0]; 881bb33e751SAstrid Rost *val2 = vcnl4040_ps_calibbias_ua[ret][1]; 882bb33e751SAstrid Rost 883bb33e751SAstrid Rost return ret; 884bb33e751SAstrid Rost } 885bb33e751SAstrid Rost 886bb33e751SAstrid Rost static ssize_t vcnl4040_write_ps_calibbias(struct vcnl4000_data *data, int val) 887bb33e751SAstrid Rost { 888bb33e751SAstrid Rost unsigned int i; 889bb33e751SAstrid Rost int ret; 890bb33e751SAstrid Rost u16 regval; 891bb33e751SAstrid Rost 892bb33e751SAstrid Rost for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_calibbias_ua); i++) { 893bb33e751SAstrid Rost if (val == vcnl4040_ps_calibbias_ua[i][1]) 894bb33e751SAstrid Rost break; 895bb33e751SAstrid Rost } 896bb33e751SAstrid Rost 897bb33e751SAstrid Rost if (i >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua)) 898bb33e751SAstrid Rost return -EINVAL; 899bb33e751SAstrid Rost 900bb33e751SAstrid Rost mutex_lock(&data->vcnl4000_lock); 901bb33e751SAstrid Rost 902bb33e751SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); 903bb33e751SAstrid Rost if (ret < 0) 904bb33e751SAstrid Rost goto out_unlock; 905bb33e751SAstrid Rost 906bb33e751SAstrid Rost regval = (ret & ~VCNL4040_PS_MS_LED_I); 907bb33e751SAstrid Rost regval |= FIELD_PREP(VCNL4040_PS_MS_LED_I, i); 908bb33e751SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, 909bb33e751SAstrid Rost regval); 910bb33e751SAstrid Rost 911bb33e751SAstrid Rost out_unlock: 912bb33e751SAstrid Rost mutex_unlock(&data->vcnl4000_lock); 913bb33e751SAstrid Rost return ret; 914bb33e751SAstrid Rost } 915bb33e751SAstrid Rost 91662a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 91762a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 91862a1efb9SPeter Meerwald int *val, int *val2, long mask) 91962a1efb9SPeter Meerwald { 9205d693139SPeter Meerwald-Stadler int ret; 92162a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 92262a1efb9SPeter Meerwald 92362a1efb9SPeter Meerwald switch (mask) { 92462a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 9255e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 9265e00708dSGuido Günther if (ret < 0) 9275e00708dSGuido Günther return ret; 9285e00708dSGuido Günther 92962a1efb9SPeter Meerwald switch (chan->type) { 93062a1efb9SPeter Meerwald case IIO_LIGHT: 9311ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 9324a818643SGuido Günther if (!ret) 9334a818643SGuido Günther ret = IIO_VAL_INT; 9344a818643SGuido Günther break; 93562a1efb9SPeter Meerwald case IIO_PROXIMITY: 9361ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 937*44b90383SMårten Lindahl *val2 = data->ps_scale; 9384a818643SGuido Günther if (!ret) 939*44b90383SMårten Lindahl ret = IIO_VAL_FRACTIONAL; 9404a818643SGuido Günther break; 94162a1efb9SPeter Meerwald default: 9424a818643SGuido Günther ret = -EINVAL; 94362a1efb9SPeter Meerwald } 9445e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 9454a818643SGuido Günther return ret; 94662a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 9475d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 9485d693139SPeter Meerwald-Stadler return -EINVAL; 9495d693139SPeter Meerwald-Stadler 95062a1efb9SPeter Meerwald *val = 0; 9511ebc787aSTomas Novotny *val2 = data->al_scale; 9525d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 95385e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 9542be17b68SAstrid Rost switch (chan->type) { 955fea2c97dSAstrid Rost case IIO_LIGHT: 956fea2c97dSAstrid Rost ret = vcnl4040_read_als_it(data, val, val2); 957fea2c97dSAstrid Rost break; 9582be17b68SAstrid Rost case IIO_PROXIMITY: 95985e2c6a2SMårten Lindahl ret = vcnl4040_read_ps_it(data, val, val2); 9602be17b68SAstrid Rost break; 9612be17b68SAstrid Rost default: 9622be17b68SAstrid Rost return -EINVAL; 9632be17b68SAstrid Rost } 96485e2c6a2SMårten Lindahl if (ret < 0) 96585e2c6a2SMårten Lindahl return ret; 96685e2c6a2SMårten Lindahl return IIO_VAL_INT_PLUS_MICRO; 967add98466SAstrid Rost case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 968add98466SAstrid Rost switch (chan->type) { 969add98466SAstrid Rost case IIO_PROXIMITY: 970add98466SAstrid Rost ret = vcnl4040_read_ps_oversampling_ratio(data, val); 971add98466SAstrid Rost if (ret < 0) 972add98466SAstrid Rost return ret; 973add98466SAstrid Rost return IIO_VAL_INT; 974add98466SAstrid Rost default: 975add98466SAstrid Rost return -EINVAL; 976add98466SAstrid Rost } 977bb33e751SAstrid Rost case IIO_CHAN_INFO_CALIBBIAS: 978bb33e751SAstrid Rost switch (chan->type) { 979bb33e751SAstrid Rost case IIO_PROXIMITY: 980bb33e751SAstrid Rost ret = vcnl4040_read_ps_calibbias(data, val, val2); 981bb33e751SAstrid Rost if (ret < 0) 982bb33e751SAstrid Rost return ret; 983bb33e751SAstrid Rost return IIO_VAL_INT_PLUS_MICRO; 984bb33e751SAstrid Rost default: 985bb33e751SAstrid Rost return -EINVAL; 986bb33e751SAstrid Rost } 98785e2c6a2SMårten Lindahl default: 98885e2c6a2SMårten Lindahl return -EINVAL; 98985e2c6a2SMårten Lindahl } 99085e2c6a2SMårten Lindahl } 99185e2c6a2SMårten Lindahl 99285e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev, 99385e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 99485e2c6a2SMårten Lindahl int val, int val2, long mask) 99585e2c6a2SMårten Lindahl { 99685e2c6a2SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 99785e2c6a2SMårten Lindahl 99885e2c6a2SMårten Lindahl switch (mask) { 99985e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 100085e2c6a2SMårten Lindahl if (val != 0) 100185e2c6a2SMårten Lindahl return -EINVAL; 10022be17b68SAstrid Rost switch (chan->type) { 1003fea2c97dSAstrid Rost case IIO_LIGHT: 1004fea2c97dSAstrid Rost return vcnl4040_write_als_it(data, val2); 10052be17b68SAstrid Rost case IIO_PROXIMITY: 100685e2c6a2SMårten Lindahl return vcnl4040_write_ps_it(data, val2); 100785e2c6a2SMårten Lindahl default: 100885e2c6a2SMårten Lindahl return -EINVAL; 100985e2c6a2SMårten Lindahl } 1010add98466SAstrid Rost case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 1011add98466SAstrid Rost switch (chan->type) { 1012add98466SAstrid Rost case IIO_PROXIMITY: 1013add98466SAstrid Rost return vcnl4040_write_ps_oversampling_ratio(data, val); 1014add98466SAstrid Rost default: 1015add98466SAstrid Rost return -EINVAL; 1016add98466SAstrid Rost } 1017bb33e751SAstrid Rost case IIO_CHAN_INFO_CALIBBIAS: 1018bb33e751SAstrid Rost switch (chan->type) { 1019bb33e751SAstrid Rost case IIO_PROXIMITY: 1020bb33e751SAstrid Rost return vcnl4040_write_ps_calibbias(data, val2); 1021bb33e751SAstrid Rost default: 1022bb33e751SAstrid Rost return -EINVAL; 1023bb33e751SAstrid Rost } 10242be17b68SAstrid Rost default: 10252be17b68SAstrid Rost return -EINVAL; 10262be17b68SAstrid Rost } 102785e2c6a2SMårten Lindahl } 102885e2c6a2SMårten Lindahl 102985e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev, 103085e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 103185e2c6a2SMårten Lindahl const int **vals, int *type, int *length, 103285e2c6a2SMårten Lindahl long mask) 103385e2c6a2SMårten Lindahl { 1034e55c96daSAstrid Rost struct vcnl4000_data *data = iio_priv(indio_dev); 1035e55c96daSAstrid Rost 103685e2c6a2SMårten Lindahl switch (mask) { 103785e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 10382be17b68SAstrid Rost switch (chan->type) { 1039fea2c97dSAstrid Rost case IIO_LIGHT: 1040fea2c97dSAstrid Rost *vals = (int *)(*data->chip_spec->als_it_times); 1041fea2c97dSAstrid Rost *length = 2 * data->chip_spec->num_als_it_times; 1042fea2c97dSAstrid Rost break; 10432be17b68SAstrid Rost case IIO_PROXIMITY: 1044e55c96daSAstrid Rost *vals = (int *)(*data->chip_spec->ps_it_times); 1045e55c96daSAstrid Rost *length = 2 * data->chip_spec->num_ps_it_times; 10462be17b68SAstrid Rost break; 10472be17b68SAstrid Rost default: 10482be17b68SAstrid Rost return -EINVAL; 10492be17b68SAstrid Rost } 10502be17b68SAstrid Rost *type = IIO_VAL_INT_PLUS_MICRO; 105185e2c6a2SMårten Lindahl return IIO_AVAIL_LIST; 1052add98466SAstrid Rost case IIO_CHAN_INFO_OVERSAMPLING_RATIO: 1053add98466SAstrid Rost switch (chan->type) { 1054add98466SAstrid Rost case IIO_PROXIMITY: 1055add98466SAstrid Rost *vals = (int *)vcnl4040_ps_oversampling_ratio; 1056add98466SAstrid Rost *length = ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); 1057add98466SAstrid Rost *type = IIO_VAL_INT; 1058add98466SAstrid Rost return IIO_AVAIL_LIST; 1059add98466SAstrid Rost default: 1060add98466SAstrid Rost return -EINVAL; 1061add98466SAstrid Rost } 1062bb33e751SAstrid Rost case IIO_CHAN_INFO_CALIBBIAS: 1063bb33e751SAstrid Rost switch (chan->type) { 1064bb33e751SAstrid Rost case IIO_PROXIMITY: 1065bb33e751SAstrid Rost *vals = (int *)vcnl4040_ps_calibbias_ua; 1066bb33e751SAstrid Rost *length = 2 * ARRAY_SIZE(vcnl4040_ps_calibbias_ua); 1067bb33e751SAstrid Rost *type = IIO_VAL_INT_PLUS_MICRO; 1068bb33e751SAstrid Rost return IIO_AVAIL_LIST; 1069bb33e751SAstrid Rost default: 1070bb33e751SAstrid Rost return -EINVAL; 1071bb33e751SAstrid Rost } 107262a1efb9SPeter Meerwald default: 10735d693139SPeter Meerwald-Stadler return -EINVAL; 107462a1efb9SPeter Meerwald } 107562a1efb9SPeter Meerwald } 107662a1efb9SPeter Meerwald 1077d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 1078d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 1079d35567fcSMathieu Othacehe int *val, int *val2, long mask) 1080d35567fcSMathieu Othacehe { 1081d35567fcSMathieu Othacehe int ret; 1082d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1083d35567fcSMathieu Othacehe 1084d35567fcSMathieu Othacehe switch (mask) { 1085d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 1086d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 1087d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 1088d35567fcSMathieu Othacehe if (ret) 1089d35567fcSMathieu Othacehe return ret; 1090d35567fcSMathieu Othacehe 1091d35567fcSMathieu Othacehe /* Protect against event capture. */ 1092d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 1093d35567fcSMathieu Othacehe ret = -EBUSY; 1094d35567fcSMathieu Othacehe } else { 1095d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 1096d35567fcSMathieu Othacehe mask); 1097d35567fcSMathieu Othacehe } 1098d35567fcSMathieu Othacehe 1099d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 1100d35567fcSMathieu Othacehe return ret; 1101f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 1102f6889c1bSMathieu Othacehe switch (chan->type) { 1103f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 1104f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 1105f6889c1bSMathieu Othacehe if (ret < 0) 1106f6889c1bSMathieu Othacehe return ret; 1107f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 1108d35567fcSMathieu Othacehe default: 1109d35567fcSMathieu Othacehe return -EINVAL; 1110d35567fcSMathieu Othacehe } 1111f6889c1bSMathieu Othacehe default: 1112f6889c1bSMathieu Othacehe return -EINVAL; 1113f6889c1bSMathieu Othacehe } 1114f6889c1bSMathieu Othacehe } 1115f6889c1bSMathieu Othacehe 1116f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 1117f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 1118f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 1119f6889c1bSMathieu Othacehe long mask) 1120f6889c1bSMathieu Othacehe { 1121f6889c1bSMathieu Othacehe switch (mask) { 1122f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 1123f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 1124f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 1125f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 1126f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 1127f6889c1bSMathieu Othacehe default: 1128f6889c1bSMathieu Othacehe return -EINVAL; 1129f6889c1bSMathieu Othacehe } 1130f6889c1bSMathieu Othacehe } 1131f6889c1bSMathieu Othacehe 1132f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 1133f6889c1bSMathieu Othacehe int val2) 1134f6889c1bSMathieu Othacehe { 1135f6889c1bSMathieu Othacehe unsigned int i; 1136f6889c1bSMathieu Othacehe int index = -1; 1137f6889c1bSMathieu Othacehe 1138f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 1139f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 1140f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 1141f6889c1bSMathieu Othacehe index = i; 1142f6889c1bSMathieu Othacehe break; 1143f6889c1bSMathieu Othacehe } 1144f6889c1bSMathieu Othacehe } 1145f6889c1bSMathieu Othacehe 1146f6889c1bSMathieu Othacehe if (index < 0) 1147f6889c1bSMathieu Othacehe return -EINVAL; 1148f6889c1bSMathieu Othacehe 1149f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 1150f6889c1bSMathieu Othacehe index); 1151f6889c1bSMathieu Othacehe } 1152f6889c1bSMathieu Othacehe 1153f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 1154f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 1155f6889c1bSMathieu Othacehe int val, int val2, long mask) 1156f6889c1bSMathieu Othacehe { 1157f6889c1bSMathieu Othacehe int ret; 1158f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1159f6889c1bSMathieu Othacehe 1160f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 1161f6889c1bSMathieu Othacehe if (ret) 1162f6889c1bSMathieu Othacehe return ret; 1163f6889c1bSMathieu Othacehe 1164f6889c1bSMathieu Othacehe /* Protect against event capture. */ 1165f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 1166f6889c1bSMathieu Othacehe ret = -EBUSY; 1167f6889c1bSMathieu Othacehe goto end; 1168f6889c1bSMathieu Othacehe } 1169f6889c1bSMathieu Othacehe 1170f6889c1bSMathieu Othacehe switch (mask) { 1171f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 1172f6889c1bSMathieu Othacehe switch (chan->type) { 1173f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 1174f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 1175f6889c1bSMathieu Othacehe goto end; 1176f6889c1bSMathieu Othacehe default: 1177f6889c1bSMathieu Othacehe ret = -EINVAL; 1178f6889c1bSMathieu Othacehe goto end; 1179f6889c1bSMathieu Othacehe } 1180f6889c1bSMathieu Othacehe default: 1181f6889c1bSMathieu Othacehe ret = -EINVAL; 1182f6889c1bSMathieu Othacehe goto end; 1183f6889c1bSMathieu Othacehe } 1184f6889c1bSMathieu Othacehe 1185f6889c1bSMathieu Othacehe end: 1186f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 1187f6889c1bSMathieu Othacehe return ret; 1188d35567fcSMathieu Othacehe } 1189d35567fcSMathieu Othacehe 1190d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 1191d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1192d35567fcSMathieu Othacehe enum iio_event_type type, 1193d35567fcSMathieu Othacehe enum iio_event_direction dir, 1194d35567fcSMathieu Othacehe enum iio_event_info info, 1195d35567fcSMathieu Othacehe int *val, int *val2) 1196d35567fcSMathieu Othacehe { 1197d35567fcSMathieu Othacehe int ret; 1198d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1199d35567fcSMathieu Othacehe 1200d35567fcSMathieu Othacehe switch (info) { 1201d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 1202d35567fcSMathieu Othacehe switch (dir) { 1203d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 1204d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 1205d35567fcSMathieu Othacehe val); 1206d35567fcSMathieu Othacehe if (ret < 0) 1207d35567fcSMathieu Othacehe return ret; 1208d35567fcSMathieu Othacehe return IIO_VAL_INT; 1209d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 1210d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 1211d35567fcSMathieu Othacehe val); 1212d35567fcSMathieu Othacehe if (ret < 0) 1213d35567fcSMathieu Othacehe return ret; 1214d35567fcSMathieu Othacehe return IIO_VAL_INT; 1215d35567fcSMathieu Othacehe default: 1216d35567fcSMathieu Othacehe return -EINVAL; 1217d35567fcSMathieu Othacehe } 1218d35567fcSMathieu Othacehe default: 1219d35567fcSMathieu Othacehe return -EINVAL; 1220d35567fcSMathieu Othacehe } 1221d35567fcSMathieu Othacehe } 1222d35567fcSMathieu Othacehe 1223d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 1224d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1225d35567fcSMathieu Othacehe enum iio_event_type type, 1226d35567fcSMathieu Othacehe enum iio_event_direction dir, 1227d35567fcSMathieu Othacehe enum iio_event_info info, 1228d35567fcSMathieu Othacehe int val, int val2) 1229d35567fcSMathieu Othacehe { 1230d35567fcSMathieu Othacehe int ret; 1231d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1232d35567fcSMathieu Othacehe 1233d35567fcSMathieu Othacehe switch (info) { 1234d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 1235d35567fcSMathieu Othacehe switch (dir) { 1236d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 1237d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 1238d35567fcSMathieu Othacehe val); 1239d35567fcSMathieu Othacehe if (ret < 0) 1240d35567fcSMathieu Othacehe return ret; 1241d35567fcSMathieu Othacehe return IIO_VAL_INT; 1242d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 1243d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 1244d35567fcSMathieu Othacehe val); 1245d35567fcSMathieu Othacehe if (ret < 0) 1246d35567fcSMathieu Othacehe return ret; 1247d35567fcSMathieu Othacehe return IIO_VAL_INT; 1248d35567fcSMathieu Othacehe default: 1249d35567fcSMathieu Othacehe return -EINVAL; 1250d35567fcSMathieu Othacehe } 1251d35567fcSMathieu Othacehe default: 1252d35567fcSMathieu Othacehe return -EINVAL; 1253d35567fcSMathieu Othacehe } 1254d35567fcSMathieu Othacehe } 1255d35567fcSMathieu Othacehe 125654667612SMårten Lindahl static int vcnl4040_read_event(struct iio_dev *indio_dev, 125754667612SMårten Lindahl const struct iio_chan_spec *chan, 125854667612SMårten Lindahl enum iio_event_type type, 125954667612SMårten Lindahl enum iio_event_direction dir, 126054667612SMårten Lindahl enum iio_event_info info, 126154667612SMårten Lindahl int *val, int *val2) 126254667612SMårten Lindahl { 126354667612SMårten Lindahl int ret; 126454667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 126554667612SMårten Lindahl 12662be17b68SAstrid Rost switch (chan->type) { 1267bc292aafSAstrid Rost case IIO_LIGHT: 1268bc292aafSAstrid Rost switch (info) { 12697f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 12707f865127SAstrid Rost return vcnl4040_read_als_period(data, val, val2); 1271bc292aafSAstrid Rost case IIO_EV_INFO_VALUE: 1272bc292aafSAstrid Rost switch (dir) { 1273bc292aafSAstrid Rost case IIO_EV_DIR_RISING: 1274bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, 1275bc292aafSAstrid Rost VCNL4040_ALS_THDH_LM); 1276bc292aafSAstrid Rost break; 1277bc292aafSAstrid Rost case IIO_EV_DIR_FALLING: 1278bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, 1279bc292aafSAstrid Rost VCNL4040_ALS_THDL_LM); 1280bc292aafSAstrid Rost break; 1281bc292aafSAstrid Rost default: 1282bc292aafSAstrid Rost return -EINVAL; 1283bc292aafSAstrid Rost } 1284bc292aafSAstrid Rost break; 1285bc292aafSAstrid Rost default: 1286bc292aafSAstrid Rost return -EINVAL; 1287bc292aafSAstrid Rost } 1288bc292aafSAstrid Rost break; 12892be17b68SAstrid Rost case IIO_PROXIMITY: 12902be17b68SAstrid Rost switch (info) { 12917f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 12927f865127SAstrid Rost return vcnl4040_read_ps_period(data, val, val2); 12932be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 129454667612SMårten Lindahl switch (dir) { 129554667612SMårten Lindahl case IIO_EV_DIR_RISING: 129654667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 129754667612SMårten Lindahl VCNL4040_PS_THDH_LM); 12982be17b68SAstrid Rost break; 129954667612SMårten Lindahl case IIO_EV_DIR_FALLING: 130054667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 130154667612SMårten Lindahl VCNL4040_PS_THDL_LM); 13022be17b68SAstrid Rost break; 13032be17b68SAstrid Rost default: 13042be17b68SAstrid Rost return -EINVAL; 13052be17b68SAstrid Rost } 13062be17b68SAstrid Rost break; 13072be17b68SAstrid Rost default: 13082be17b68SAstrid Rost return -EINVAL; 13092be17b68SAstrid Rost } 13102be17b68SAstrid Rost break; 13112be17b68SAstrid Rost default: 13122be17b68SAstrid Rost return -EINVAL; 13132be17b68SAstrid Rost } 131454667612SMårten Lindahl if (ret < 0) 131554667612SMårten Lindahl return ret; 131654667612SMårten Lindahl *val = ret; 131754667612SMårten Lindahl return IIO_VAL_INT; 131854667612SMårten Lindahl } 131954667612SMårten Lindahl 132054667612SMårten Lindahl static int vcnl4040_write_event(struct iio_dev *indio_dev, 132154667612SMårten Lindahl const struct iio_chan_spec *chan, 132254667612SMårten Lindahl enum iio_event_type type, 132354667612SMårten Lindahl enum iio_event_direction dir, 132454667612SMårten Lindahl enum iio_event_info info, 132554667612SMårten Lindahl int val, int val2) 132654667612SMårten Lindahl { 132754667612SMårten Lindahl int ret; 132854667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 132954667612SMårten Lindahl 13302be17b68SAstrid Rost switch (chan->type) { 1331bc292aafSAstrid Rost case IIO_LIGHT: 1332bc292aafSAstrid Rost switch (info) { 13337f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 13347f865127SAstrid Rost return vcnl4040_write_als_period(data, val, val2); 1335bc292aafSAstrid Rost case IIO_EV_INFO_VALUE: 1336bc292aafSAstrid Rost switch (dir) { 1337bc292aafSAstrid Rost case IIO_EV_DIR_RISING: 1338bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 1339bc292aafSAstrid Rost VCNL4040_ALS_THDH_LM, 1340bc292aafSAstrid Rost val); 1341bc292aafSAstrid Rost break; 1342bc292aafSAstrid Rost case IIO_EV_DIR_FALLING: 1343bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 1344bc292aafSAstrid Rost VCNL4040_ALS_THDL_LM, 1345bc292aafSAstrid Rost val); 1346bc292aafSAstrid Rost break; 1347bc292aafSAstrid Rost default: 1348bc292aafSAstrid Rost return -EINVAL; 1349bc292aafSAstrid Rost } 1350bc292aafSAstrid Rost break; 1351bc292aafSAstrid Rost default: 1352bc292aafSAstrid Rost return -EINVAL; 1353bc292aafSAstrid Rost } 1354bc292aafSAstrid Rost break; 13552be17b68SAstrid Rost case IIO_PROXIMITY: 13562be17b68SAstrid Rost switch (info) { 13577f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 13587f865127SAstrid Rost return vcnl4040_write_ps_period(data, val, val2); 13592be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 136054667612SMårten Lindahl switch (dir) { 136154667612SMårten Lindahl case IIO_EV_DIR_RISING: 136254667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 13632be17b68SAstrid Rost VCNL4040_PS_THDH_LM, 13642be17b68SAstrid Rost val); 13652be17b68SAstrid Rost break; 136654667612SMårten Lindahl case IIO_EV_DIR_FALLING: 136754667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 13682be17b68SAstrid Rost VCNL4040_PS_THDL_LM, 13692be17b68SAstrid Rost val); 13702be17b68SAstrid Rost break; 137154667612SMårten Lindahl default: 137254667612SMårten Lindahl return -EINVAL; 137354667612SMårten Lindahl } 13742be17b68SAstrid Rost break; 13752be17b68SAstrid Rost default: 13762be17b68SAstrid Rost return -EINVAL; 13772be17b68SAstrid Rost } 13782be17b68SAstrid Rost break; 13792be17b68SAstrid Rost default: 13802be17b68SAstrid Rost return -EINVAL; 13812be17b68SAstrid Rost } 13822be17b68SAstrid Rost if (ret < 0) 13832be17b68SAstrid Rost return ret; 13842be17b68SAstrid Rost return IIO_VAL_INT; 138554667612SMårten Lindahl } 138654667612SMårten Lindahl 1387d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 1388d35567fcSMathieu Othacehe { 1389d35567fcSMathieu Othacehe int ret; 1390d35567fcSMathieu Othacehe 1391d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 1392d35567fcSMathieu Othacehe if (ret < 0) 1393d35567fcSMathieu Othacehe return false; 1394d35567fcSMathieu Othacehe 1395d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 1396d35567fcSMathieu Othacehe } 1397d35567fcSMathieu Othacehe 1398d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 1399d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1400d35567fcSMathieu Othacehe enum iio_event_type type, 1401d35567fcSMathieu Othacehe enum iio_event_direction dir) 1402d35567fcSMathieu Othacehe { 1403d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1404d35567fcSMathieu Othacehe 1405d35567fcSMathieu Othacehe switch (chan->type) { 1406d35567fcSMathieu Othacehe case IIO_PROXIMITY: 1407d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 1408d35567fcSMathieu Othacehe default: 1409d35567fcSMathieu Othacehe return -EINVAL; 1410d35567fcSMathieu Othacehe } 1411d35567fcSMathieu Othacehe } 1412d35567fcSMathieu Othacehe 1413d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 1414d35567fcSMathieu Othacehe { 1415d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1416d35567fcSMathieu Othacehe int ret; 1417d35567fcSMathieu Othacehe int icr; 1418d35567fcSMathieu Othacehe int command; 1419d35567fcSMathieu Othacehe 1420d35567fcSMathieu Othacehe if (state) { 1421d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 1422d35567fcSMathieu Othacehe if (ret) 1423d35567fcSMathieu Othacehe return ret; 1424d35567fcSMathieu Othacehe 1425d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 1426d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 1427d35567fcSMathieu Othacehe 1428d35567fcSMathieu Othacehe /* 1429d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 1430d35567fcSMathieu Othacehe * default. 1431d35567fcSMathieu Othacehe */ 1432d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 1433d35567fcSMathieu Othacehe } else { 1434d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 1435d35567fcSMathieu Othacehe return 0; 1436d35567fcSMathieu Othacehe 1437d35567fcSMathieu Othacehe command = 0; 1438d35567fcSMathieu Othacehe icr = 0; 1439d35567fcSMathieu Othacehe } 1440d35567fcSMathieu Othacehe 1441d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 1442d35567fcSMathieu Othacehe command); 1443d35567fcSMathieu Othacehe if (ret < 0) 1444d35567fcSMathieu Othacehe goto end; 1445d35567fcSMathieu Othacehe 1446d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 1447d35567fcSMathieu Othacehe 1448d35567fcSMathieu Othacehe end: 1449d35567fcSMathieu Othacehe if (state) 1450d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 1451d35567fcSMathieu Othacehe 1452d35567fcSMathieu Othacehe return ret; 1453d35567fcSMathieu Othacehe } 1454d35567fcSMathieu Othacehe 1455d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 1456d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1457d35567fcSMathieu Othacehe enum iio_event_type type, 1458d35567fcSMathieu Othacehe enum iio_event_direction dir, 1459d35567fcSMathieu Othacehe int state) 1460d35567fcSMathieu Othacehe { 1461d35567fcSMathieu Othacehe switch (chan->type) { 1462d35567fcSMathieu Othacehe case IIO_PROXIMITY: 1463d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 1464d35567fcSMathieu Othacehe default: 1465d35567fcSMathieu Othacehe return -EINVAL; 1466d35567fcSMathieu Othacehe } 1467d35567fcSMathieu Othacehe } 1468d35567fcSMathieu Othacehe 146954667612SMårten Lindahl static int vcnl4040_read_event_config(struct iio_dev *indio_dev, 147054667612SMårten Lindahl const struct iio_chan_spec *chan, 147154667612SMårten Lindahl enum iio_event_type type, 147254667612SMårten Lindahl enum iio_event_direction dir) 147354667612SMårten Lindahl { 147454667612SMårten Lindahl int ret; 147554667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 147654667612SMårten Lindahl 14772be17b68SAstrid Rost switch (chan->type) { 1478bc292aafSAstrid Rost case IIO_LIGHT: 1479bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 1480bc292aafSAstrid Rost if (ret < 0) 1481bc292aafSAstrid Rost return ret; 1482bc292aafSAstrid Rost 1483bc292aafSAstrid Rost data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret); 1484bc292aafSAstrid Rost 1485bc292aafSAstrid Rost return data->als_int; 14862be17b68SAstrid Rost case IIO_PROXIMITY: 148754667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 148854667612SMårten Lindahl if (ret < 0) 148954667612SMårten Lindahl return ret; 149054667612SMårten Lindahl 149154667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); 149254667612SMårten Lindahl 149354667612SMårten Lindahl return (dir == IIO_EV_DIR_RISING) ? 149454667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : 149554667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); 14962be17b68SAstrid Rost default: 14972be17b68SAstrid Rost return -EINVAL; 14982be17b68SAstrid Rost } 149954667612SMårten Lindahl } 150054667612SMårten Lindahl 150154667612SMårten Lindahl static int vcnl4040_write_event_config(struct iio_dev *indio_dev, 150254667612SMårten Lindahl const struct iio_chan_spec *chan, 150354667612SMårten Lindahl enum iio_event_type type, 150454667612SMårten Lindahl enum iio_event_direction dir, int state) 150554667612SMårten Lindahl { 15062be17b68SAstrid Rost int ret = -EINVAL; 150754667612SMårten Lindahl u16 val, mask; 150854667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 150954667612SMårten Lindahl 151054667612SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 151154667612SMårten Lindahl 15122be17b68SAstrid Rost switch (chan->type) { 1513bc292aafSAstrid Rost case IIO_LIGHT: 1514bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 1515bc292aafSAstrid Rost if (ret < 0) 1516bc292aafSAstrid Rost goto out; 1517bc292aafSAstrid Rost 1518bc292aafSAstrid Rost mask = VCNL4040_ALS_CONF_INT_EN; 1519bc292aafSAstrid Rost if (state) 1520bc292aafSAstrid Rost val = (ret | mask); 1521bc292aafSAstrid Rost else 1522bc292aafSAstrid Rost val = (ret & ~mask); 1523bc292aafSAstrid Rost 1524bc292aafSAstrid Rost data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val); 1525bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 1526bc292aafSAstrid Rost val); 1527bc292aafSAstrid Rost break; 15282be17b68SAstrid Rost case IIO_PROXIMITY: 152954667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 153054667612SMårten Lindahl if (ret < 0) 153154667612SMårten Lindahl goto out; 153254667612SMårten Lindahl 153354667612SMårten Lindahl if (dir == IIO_EV_DIR_RISING) 153454667612SMårten Lindahl mask = VCNL4040_PS_IF_AWAY; 153554667612SMårten Lindahl else 153654667612SMårten Lindahl mask = VCNL4040_PS_IF_CLOSE; 153754667612SMårten Lindahl 153854667612SMårten Lindahl val = state ? (ret | mask) : (ret & ~mask); 153954667612SMårten Lindahl 154054667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); 15412be17b68SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 15422be17b68SAstrid Rost val); 15432be17b68SAstrid Rost break; 15442be17b68SAstrid Rost default: 15452be17b68SAstrid Rost break; 15462be17b68SAstrid Rost } 154754667612SMårten Lindahl 154854667612SMårten Lindahl out: 154954667612SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 155054667612SMårten Lindahl 155154667612SMårten Lindahl return ret; 155254667612SMårten Lindahl } 155354667612SMårten Lindahl 155454667612SMårten Lindahl static irqreturn_t vcnl4040_irq_thread(int irq, void *p) 155554667612SMårten Lindahl { 155654667612SMårten Lindahl struct iio_dev *indio_dev = p; 155754667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 155854667612SMårten Lindahl int ret; 155954667612SMårten Lindahl 1560854965b7SAstrid Rost ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg); 156154667612SMårten Lindahl if (ret < 0) 156254667612SMårten Lindahl return IRQ_HANDLED; 156354667612SMårten Lindahl 156454667612SMårten Lindahl if (ret & VCNL4040_PS_IF_CLOSE) { 156554667612SMårten Lindahl iio_push_event(indio_dev, 156654667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 156754667612SMårten Lindahl IIO_EV_TYPE_THRESH, 156854667612SMårten Lindahl IIO_EV_DIR_RISING), 156954667612SMårten Lindahl iio_get_time_ns(indio_dev)); 157054667612SMårten Lindahl } 157154667612SMårten Lindahl 157254667612SMårten Lindahl if (ret & VCNL4040_PS_IF_AWAY) { 157354667612SMårten Lindahl iio_push_event(indio_dev, 157454667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 157554667612SMårten Lindahl IIO_EV_TYPE_THRESH, 157654667612SMårten Lindahl IIO_EV_DIR_FALLING), 157754667612SMårten Lindahl iio_get_time_ns(indio_dev)); 157854667612SMårten Lindahl } 157954667612SMårten Lindahl 1580bc292aafSAstrid Rost if (ret & VCNL4040_ALS_FALLING) { 1581bc292aafSAstrid Rost iio_push_event(indio_dev, 1582bc292aafSAstrid Rost IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 1583bc292aafSAstrid Rost IIO_EV_TYPE_THRESH, 1584bc292aafSAstrid Rost IIO_EV_DIR_FALLING), 1585bc292aafSAstrid Rost iio_get_time_ns(indio_dev)); 1586bc292aafSAstrid Rost } 1587bc292aafSAstrid Rost 1588bc292aafSAstrid Rost if (ret & VCNL4040_ALS_RISING) { 1589bc292aafSAstrid Rost iio_push_event(indio_dev, 1590bc292aafSAstrid Rost IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 1591bc292aafSAstrid Rost IIO_EV_TYPE_THRESH, 1592bc292aafSAstrid Rost IIO_EV_DIR_RISING), 1593bc292aafSAstrid Rost iio_get_time_ns(indio_dev)); 1594bc292aafSAstrid Rost } 1595bc292aafSAstrid Rost 159654667612SMårten Lindahl return IRQ_HANDLED; 159754667612SMårten Lindahl } 159854667612SMårten Lindahl 1599d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 1600d35567fcSMathieu Othacehe uintptr_t priv, 1601d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1602d35567fcSMathieu Othacehe char *buf) 1603d35567fcSMathieu Othacehe { 1604d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1605d35567fcSMathieu Othacehe 1606d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 1607d35567fcSMathieu Othacehe } 1608d35567fcSMathieu Othacehe 16093a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 16103a52d32aSMårten Lindahl { 16113a52d32aSMårten Lindahl struct iio_dev *indio_dev = p; 16123a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 16133a52d32aSMårten Lindahl unsigned long isr; 16143a52d32aSMårten Lindahl int ret; 16153a52d32aSMårten Lindahl 16163a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 16173a52d32aSMårten Lindahl if (ret < 0) 16183a52d32aSMårten Lindahl goto end; 16193a52d32aSMårten Lindahl 16203a52d32aSMårten Lindahl isr = ret; 16213a52d32aSMårten Lindahl 16223a52d32aSMårten Lindahl if (isr & VCNL4010_INT_THR) { 16233a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 16243a52d32aSMårten Lindahl iio_push_event(indio_dev, 16253a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 16263a52d32aSMårten Lindahl IIO_PROXIMITY, 16273a52d32aSMårten Lindahl 1, 16283a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 16293a52d32aSMårten Lindahl IIO_EV_DIR_FALLING), 16303a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 16313a52d32aSMårten Lindahl } 16323a52d32aSMårten Lindahl 16333a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 16343a52d32aSMårten Lindahl iio_push_event(indio_dev, 16353a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 16363a52d32aSMårten Lindahl IIO_PROXIMITY, 16373a52d32aSMårten Lindahl 1, 16383a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 16393a52d32aSMårten Lindahl IIO_EV_DIR_RISING), 16403a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 16413a52d32aSMårten Lindahl } 16423a52d32aSMårten Lindahl 16433a52d32aSMårten Lindahl i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 16443a52d32aSMårten Lindahl isr & VCNL4010_INT_THR); 16453a52d32aSMårten Lindahl } 16463a52d32aSMårten Lindahl 16473a52d32aSMårten Lindahl if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 1648f700e55eSMehdi Djait iio_trigger_poll_nested(indio_dev->trig); 16493a52d32aSMårten Lindahl 16503a52d32aSMårten Lindahl end: 16513a52d32aSMårten Lindahl return IRQ_HANDLED; 16523a52d32aSMårten Lindahl } 16533a52d32aSMårten Lindahl 16543a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 16553a52d32aSMårten Lindahl { 16563a52d32aSMårten Lindahl struct iio_poll_func *pf = p; 16573a52d32aSMårten Lindahl struct iio_dev *indio_dev = pf->indio_dev; 16583a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 16593a52d32aSMårten Lindahl const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 16603a52d32aSMårten Lindahl u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 16613a52d32aSMårten Lindahl bool data_read = false; 16623a52d32aSMårten Lindahl unsigned long isr; 16633a52d32aSMårten Lindahl int val = 0; 16643a52d32aSMårten Lindahl int ret; 16653a52d32aSMårten Lindahl 16663a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 16673a52d32aSMårten Lindahl if (ret < 0) 16683a52d32aSMårten Lindahl goto end; 16693a52d32aSMårten Lindahl 16703a52d32aSMårten Lindahl isr = ret; 16713a52d32aSMårten Lindahl 16723a52d32aSMårten Lindahl if (test_bit(0, active_scan_mask)) { 16733a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 16743a52d32aSMårten Lindahl ret = vcnl4000_read_data(data, 16753a52d32aSMårten Lindahl VCNL4000_PS_RESULT_HI, 16763a52d32aSMårten Lindahl &val); 16773a52d32aSMårten Lindahl if (ret < 0) 16783a52d32aSMårten Lindahl goto end; 16793a52d32aSMårten Lindahl 16803a52d32aSMårten Lindahl buffer[0] = val; 16813a52d32aSMårten Lindahl data_read = true; 16823a52d32aSMårten Lindahl } 16833a52d32aSMårten Lindahl } 16843a52d32aSMårten Lindahl 16853a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 16863a52d32aSMårten Lindahl isr & VCNL4010_INT_DRDY); 16873a52d32aSMårten Lindahl if (ret < 0) 16883a52d32aSMårten Lindahl goto end; 16893a52d32aSMårten Lindahl 16903a52d32aSMårten Lindahl if (!data_read) 16913a52d32aSMårten Lindahl goto end; 16923a52d32aSMårten Lindahl 16933a52d32aSMårten Lindahl iio_push_to_buffers_with_timestamp(indio_dev, buffer, 16943a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 16953a52d32aSMårten Lindahl 16963a52d32aSMårten Lindahl end: 16973a52d32aSMårten Lindahl iio_trigger_notify_done(indio_dev->trig); 16983a52d32aSMårten Lindahl return IRQ_HANDLED; 16993a52d32aSMårten Lindahl } 17003a52d32aSMårten Lindahl 17013a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 17023a52d32aSMårten Lindahl { 17033a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 17043a52d32aSMårten Lindahl int ret; 17053a52d32aSMårten Lindahl int cmd; 17063a52d32aSMårten Lindahl 17073a52d32aSMårten Lindahl /* Do not enable the buffer if we are already capturing events. */ 17083a52d32aSMårten Lindahl if (vcnl4010_is_in_periodic_mode(data)) 17093a52d32aSMårten Lindahl return -EBUSY; 17103a52d32aSMårten Lindahl 17113a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 17123a52d32aSMårten Lindahl VCNL4010_INT_PROX_EN); 17133a52d32aSMårten Lindahl if (ret < 0) 17143a52d32aSMårten Lindahl return ret; 17153a52d32aSMårten Lindahl 17163a52d32aSMårten Lindahl cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 17173a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 17183a52d32aSMårten Lindahl } 17193a52d32aSMårten Lindahl 17203a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 17213a52d32aSMårten Lindahl { 17223a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 17233a52d32aSMårten Lindahl int ret; 17243a52d32aSMårten Lindahl 17253a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 17263a52d32aSMårten Lindahl if (ret < 0) 17273a52d32aSMårten Lindahl return ret; 17283a52d32aSMårten Lindahl 17293a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 17303a52d32aSMårten Lindahl } 17313a52d32aSMårten Lindahl 17323a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 17333a52d32aSMårten Lindahl .postenable = &vcnl4010_buffer_postenable, 17343a52d32aSMårten Lindahl .predisable = &vcnl4010_buffer_predisable, 17353a52d32aSMårten Lindahl }; 17363a52d32aSMårten Lindahl 1737d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 1738d35567fcSMathieu Othacehe { 1739d35567fcSMathieu Othacehe .name = "nearlevel", 1740d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 1741d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 1742d35567fcSMathieu Othacehe }, 1743d35567fcSMathieu Othacehe { /* sentinel */ } 1744d35567fcSMathieu Othacehe }; 1745d35567fcSMathieu Othacehe 1746d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 1747d35567fcSMathieu Othacehe { 1748d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1749d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 1750d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1751d35567fcSMathieu Othacehe }, { 1752d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1753d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 1754d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1755d35567fcSMathieu Othacehe }, { 1756d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1757d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 1758d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 1759d35567fcSMathieu Othacehe } 1760d35567fcSMathieu Othacehe }; 1761d35567fcSMathieu Othacehe 17627f865127SAstrid Rost static const struct iio_event_spec vcnl4040_als_event_spec[] = { 17637f865127SAstrid Rost { 17647f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 17657f865127SAstrid Rost .dir = IIO_EV_DIR_RISING, 17667f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_VALUE), 17677f865127SAstrid Rost }, { 17687f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 17697f865127SAstrid Rost .dir = IIO_EV_DIR_FALLING, 17707f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_VALUE), 17717f865127SAstrid Rost }, { 17727f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 17737f865127SAstrid Rost .dir = IIO_EV_DIR_EITHER, 17747f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD), 17757f865127SAstrid Rost }, 17767f865127SAstrid Rost }; 17777f865127SAstrid Rost 177854667612SMårten Lindahl static const struct iio_event_spec vcnl4040_event_spec[] = { 177954667612SMårten Lindahl { 178054667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 178154667612SMårten Lindahl .dir = IIO_EV_DIR_RISING, 178254667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 178354667612SMårten Lindahl }, { 178454667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 178554667612SMårten Lindahl .dir = IIO_EV_DIR_FALLING, 178654667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 17877f865127SAstrid Rost }, { 17887f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 17897f865127SAstrid Rost .dir = IIO_EV_DIR_EITHER, 17907f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_PERIOD), 179154667612SMårten Lindahl }, 179254667612SMårten Lindahl }; 179354667612SMårten Lindahl 1794d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 1795d35567fcSMathieu Othacehe { 1796d35567fcSMathieu Othacehe .type = IIO_LIGHT, 1797d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1798d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1799d35567fcSMathieu Othacehe }, { 1800d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 1801d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1802d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 1803d35567fcSMathieu Othacehe } 1804d35567fcSMathieu Othacehe }; 1805d35567fcSMathieu Othacehe 1806d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 1807d35567fcSMathieu Othacehe { 1808d35567fcSMathieu Othacehe .type = IIO_LIGHT, 18098fe78d52SMathieu Othacehe .scan_index = -1, 1810d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1811d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1812d35567fcSMathieu Othacehe }, { 1813d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 18148fe78d52SMathieu Othacehe .scan_index = 0, 1815f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1816f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 1817f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1818d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 1819d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 1820d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 18218fe78d52SMathieu Othacehe .scan_type = { 18228fe78d52SMathieu Othacehe .sign = 'u', 18238fe78d52SMathieu Othacehe .realbits = 16, 18248fe78d52SMathieu Othacehe .storagebits = 16, 18258fe78d52SMathieu Othacehe .endianness = IIO_CPU, 1826d35567fcSMathieu Othacehe }, 18278fe78d52SMathieu Othacehe }, 18288fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 1829d35567fcSMathieu Othacehe }; 1830d35567fcSMathieu Othacehe 183185e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = { 183285e2c6a2SMårten Lindahl { 183385e2c6a2SMårten Lindahl .type = IIO_LIGHT, 183485e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1835fea2c97dSAstrid Rost BIT(IIO_CHAN_INFO_SCALE) | 1836fea2c97dSAstrid Rost BIT(IIO_CHAN_INFO_INT_TIME), 1837fea2c97dSAstrid Rost .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 18387f865127SAstrid Rost .event_spec = vcnl4040_als_event_spec, 18397f865127SAstrid Rost .num_event_specs = ARRAY_SIZE(vcnl4040_als_event_spec), 184085e2c6a2SMårten Lindahl }, { 184185e2c6a2SMårten Lindahl .type = IIO_PROXIMITY, 184285e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1843add98466SAstrid Rost BIT(IIO_CHAN_INFO_INT_TIME) | 1844bb33e751SAstrid Rost BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | 1845bb33e751SAstrid Rost BIT(IIO_CHAN_INFO_CALIBBIAS), 1846add98466SAstrid Rost .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | 1847bb33e751SAstrid Rost BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | 1848bb33e751SAstrid Rost BIT(IIO_CHAN_INFO_CALIBBIAS), 184985e2c6a2SMårten Lindahl .ext_info = vcnl4000_ext_info, 185054667612SMårten Lindahl .event_spec = vcnl4040_event_spec, 185154667612SMårten Lindahl .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), 185285e2c6a2SMårten Lindahl } 185385e2c6a2SMårten Lindahl }; 185485e2c6a2SMårten Lindahl 185562a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 185662a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 185762a1efb9SPeter Meerwald }; 185862a1efb9SPeter Meerwald 1859d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 1860d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 1861f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 1862f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 1863d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 1864d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 1865d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 1866d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 1867d35567fcSMathieu Othacehe }; 1868d35567fcSMathieu Othacehe 186985e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = { 187085e2c6a2SMårten Lindahl .read_raw = vcnl4000_read_raw, 187185e2c6a2SMårten Lindahl .write_raw = vcnl4040_write_raw, 187254667612SMårten Lindahl .read_event_value = vcnl4040_read_event, 187354667612SMårten Lindahl .write_event_value = vcnl4040_write_event, 187454667612SMårten Lindahl .read_event_config = vcnl4040_read_event_config, 187554667612SMårten Lindahl .write_event_config = vcnl4040_write_event_config, 187685e2c6a2SMårten Lindahl .read_avail = vcnl4040_read_avail, 187785e2c6a2SMårten Lindahl }; 187885e2c6a2SMårten Lindahl 1879d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 1880d35567fcSMathieu Othacehe [VCNL4000] = { 1881d35567fcSMathieu Othacehe .prod = "VCNL4000", 1882d35567fcSMathieu Othacehe .init = vcnl4000_init, 1883d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1884d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1885d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1886d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1887d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1888d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1889d35567fcSMathieu Othacehe }, 1890d35567fcSMathieu Othacehe [VCNL4010] = { 1891d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 1892d35567fcSMathieu Othacehe .init = vcnl4000_init, 1893d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1894d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1895d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1896d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 1897d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 1898d35567fcSMathieu Othacehe .info = &vcnl4010_info, 1899bfb6cfeeSMårten Lindahl .irq_thread = vcnl4010_irq_thread, 1900bfb6cfeeSMårten Lindahl .trig_buffer_func = vcnl4010_trigger_handler, 1901bfb6cfeeSMårten Lindahl .buffer_setup_ops = &vcnl4010_buffer_ops, 1902d35567fcSMathieu Othacehe }, 1903d35567fcSMathieu Othacehe [VCNL4040] = { 1904d35567fcSMathieu Othacehe .prod = "VCNL4040", 1905d35567fcSMathieu Othacehe .init = vcnl4200_init, 1906d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1907d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1908d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 190985e2c6a2SMårten Lindahl .channels = vcnl4040_channels, 191085e2c6a2SMårten Lindahl .num_channels = ARRAY_SIZE(vcnl4040_channels), 191185e2c6a2SMårten Lindahl .info = &vcnl4040_info, 191254667612SMårten Lindahl .irq_thread = vcnl4040_irq_thread, 1913854965b7SAstrid Rost .int_reg = VCNL4040_INT_FLAGS, 1914e55c96daSAstrid Rost .ps_it_times = &vcnl4040_ps_it_times, 1915e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), 1916fea2c97dSAstrid Rost .als_it_times = &vcnl4040_als_it_times, 1917fea2c97dSAstrid Rost .num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times), 1918fea2c97dSAstrid Rost .ulux_step = 100000, 1919d35567fcSMathieu Othacehe }, 1920d35567fcSMathieu Othacehe [VCNL4200] = { 1921d35567fcSMathieu Othacehe .prod = "VCNL4200", 1922d35567fcSMathieu Othacehe .init = vcnl4200_init, 1923d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1924d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1925d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1926854965b7SAstrid Rost .channels = vcnl4040_channels, 1927d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1928854965b7SAstrid Rost .info = &vcnl4040_info, 1929854965b7SAstrid Rost .irq_thread = vcnl4040_irq_thread, 1930854965b7SAstrid Rost .int_reg = VCNL4200_INT_FLAGS, 1931e55c96daSAstrid Rost .ps_it_times = &vcnl4200_ps_it_times, 1932e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times), 1933fea2c97dSAstrid Rost .als_it_times = &vcnl4200_als_it_times, 1934fea2c97dSAstrid Rost .num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times), 1935fea2c97dSAstrid Rost .ulux_step = 24000, 1936d35567fcSMathieu Othacehe }, 1937d35567fcSMathieu Othacehe }; 1938d35567fcSMathieu Othacehe 19398fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 19408fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 19418fe78d52SMathieu Othacehe }; 19428fe78d52SMathieu Othacehe 19438fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 19448fe78d52SMathieu Othacehe { 19458fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 19468fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 19478fe78d52SMathieu Othacehe struct iio_trigger *trigger; 19488fe78d52SMathieu Othacehe 19498fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 195015ea2878SJonathan Cameron indio_dev->name, 195115ea2878SJonathan Cameron iio_device_id(indio_dev)); 19528fe78d52SMathieu Othacehe if (!trigger) 19538fe78d52SMathieu Othacehe return -ENOMEM; 19548fe78d52SMathieu Othacehe 19558fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 19568fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 19578fe78d52SMathieu Othacehe 19588fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 19598fe78d52SMathieu Othacehe } 19608fe78d52SMathieu Othacehe 1961e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client) 196262a1efb9SPeter Meerwald { 1963e61295e0SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client); 196462a1efb9SPeter Meerwald struct vcnl4000_data *data; 196562a1efb9SPeter Meerwald struct iio_dev *indio_dev; 19661ebc787aSTomas Novotny int ret; 196762a1efb9SPeter Meerwald 19682669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 196962a1efb9SPeter Meerwald if (!indio_dev) 197062a1efb9SPeter Meerwald return -ENOMEM; 197162a1efb9SPeter Meerwald 197262a1efb9SPeter Meerwald data = iio_priv(indio_dev); 197362a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 197462a1efb9SPeter Meerwald data->client = client; 19751ebc787aSTomas Novotny data->id = id->driver_data; 19761ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 197762a1efb9SPeter Meerwald 197842ec40b0SMårten Lindahl mutex_init(&data->vcnl4000_lock); 197942ec40b0SMårten Lindahl 19801ebc787aSTomas Novotny ret = data->chip_spec->init(data); 198162a1efb9SPeter Meerwald if (ret < 0) 19822669d723SPeter Meerwald return ret; 198362a1efb9SPeter Meerwald 1984d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 19851ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 198662a1efb9SPeter Meerwald 1987f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1988f5a98e1fSGuido Günther &data->near_level)) 1989f5a98e1fSGuido Günther data->near_level = 0; 1990f5a98e1fSGuido Günther 1991d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1992d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1993d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 199462a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 199562a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 199662a1efb9SPeter Meerwald 1997bfb6cfeeSMårten Lindahl if (data->chip_spec->trig_buffer_func && 1998bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops) { 19998fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 20008fe78d52SMathieu Othacehe NULL, 2001bfb6cfeeSMårten Lindahl data->chip_spec->trig_buffer_func, 2002bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops); 20038fe78d52SMathieu Othacehe if (ret < 0) { 20048fe78d52SMathieu Othacehe dev_err(&client->dev, 20058fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 20068fe78d52SMathieu Othacehe return ret; 20078fe78d52SMathieu Othacehe } 2008bfb6cfeeSMårten Lindahl } 20098fe78d52SMathieu Othacehe 2010bfb6cfeeSMårten Lindahl if (client->irq && data->chip_spec->irq_thread) { 2011d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 2012bfb6cfeeSMårten Lindahl NULL, data->chip_spec->irq_thread, 2013d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 2014d35567fcSMathieu Othacehe IRQF_ONESHOT, 2015bfb6cfeeSMårten Lindahl "vcnl4000_irq", 2016d35567fcSMathieu Othacehe indio_dev); 2017d35567fcSMathieu Othacehe if (ret < 0) { 2018d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 2019d35567fcSMathieu Othacehe return ret; 2020d35567fcSMathieu Othacehe } 20218fe78d52SMathieu Othacehe 20228fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 20238fe78d52SMathieu Othacehe if (ret < 0) 20248fe78d52SMathieu Othacehe return ret; 2025d35567fcSMathieu Othacehe } 2026d35567fcSMathieu Othacehe 20275e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 20285e00708dSGuido Günther if (ret < 0) 20295e00708dSGuido Günther goto fail_poweroff; 20305e00708dSGuido Günther 20315e00708dSGuido Günther ret = iio_device_register(indio_dev); 20325e00708dSGuido Günther if (ret < 0) 20335e00708dSGuido Günther goto fail_poweroff; 20345e00708dSGuido Günther 20355e00708dSGuido Günther pm_runtime_enable(&client->dev); 20365e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 20375e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 20385e00708dSGuido Günther 20395e00708dSGuido Günther return 0; 20405e00708dSGuido Günther fail_poweroff: 20415e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 20425e00708dSGuido Günther return ret; 204362a1efb9SPeter Meerwald } 204462a1efb9SPeter Meerwald 2045ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 2046ebd457d5SAngus Ainslie (Purism) { 2047ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 20481436a78cSMarco Felsch .data = (void *)VCNL4000, 2049ebd457d5SAngus Ainslie (Purism) }, 2050ebd457d5SAngus Ainslie (Purism) { 2051ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 20521436a78cSMarco Felsch .data = (void *)VCNL4010, 2053ebd457d5SAngus Ainslie (Purism) }, 2054ebd457d5SAngus Ainslie (Purism) { 20551436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 20561436a78cSMarco Felsch .data = (void *)VCNL4010, 2057ebd457d5SAngus Ainslie (Purism) }, 2058ebd457d5SAngus Ainslie (Purism) { 20597fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 20607fd1c260SMarco Felsch .data = (void *)VCNL4040, 20617fd1c260SMarco Felsch }, 20627fd1c260SMarco Felsch { 2063ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 20641436a78cSMarco Felsch .data = (void *)VCNL4200, 2065ebd457d5SAngus Ainslie (Purism) }, 2066ebd457d5SAngus Ainslie (Purism) {}, 2067ebd457d5SAngus Ainslie (Purism) }; 2068ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 2069ebd457d5SAngus Ainslie (Purism) 2070ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 20715e00708dSGuido Günther { 20725e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 20735e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 2074ab91da2fSUwe Kleine-König int ret; 20755e00708dSGuido Günther 20765e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 20775e00708dSGuido Günther pm_runtime_disable(&client->dev); 20785e00708dSGuido Günther iio_device_unregister(indio_dev); 20795e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 20805e00708dSGuido Günther 2081ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 2082ab91da2fSUwe Kleine-König if (ret) 2083ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 2084ab91da2fSUwe Kleine-König ERR_PTR(ret)); 20855e00708dSGuido Günther } 20865e00708dSGuido Günther 2087cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 20885e00708dSGuido Günther { 20895e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 20905e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 20915e00708dSGuido Günther 20925e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 20935e00708dSGuido Günther } 20945e00708dSGuido Günther 2095cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 20965e00708dSGuido Günther { 20975e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 20985e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 20995e00708dSGuido Günther 21005e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 21015e00708dSGuido Günther } 21025e00708dSGuido Günther 2103cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 2104cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 21055e00708dSGuido Günther 210662a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 210762a1efb9SPeter Meerwald .driver = { 210862a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 2109cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 2110ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 211162a1efb9SPeter Meerwald }, 21127cf15f42SUwe Kleine-König .probe = vcnl4000_probe, 211362a1efb9SPeter Meerwald .id_table = vcnl4000_id, 21145e00708dSGuido Günther .remove = vcnl4000_remove, 211562a1efb9SPeter Meerwald }; 211662a1efb9SPeter Meerwald 211762a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 211862a1efb9SPeter Meerwald 211962a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 21208fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 212162a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 212262a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 2123