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> 27*7f865127SAstrid 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 */ 6454667612SMårten Lindahl #define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */ 6554667612SMårten Lindahl #define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */ 66bc292aafSAstrid Rost #define VCNL4040_ALS_THDL_LM 0x02 /* Ambient light threshold low */ 67bc292aafSAstrid Rost #define VCNL4040_ALS_THDH_LM 0x01 /* Ambient light threshold high */ 68be38866fSTomas Novotny #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 69be38866fSTomas Novotny #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 7054667612SMårten Lindahl #define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */ 71854965b7SAstrid Rost #define VCNL4200_INT_FLAGS 0x0d /* Interrupt register */ 72be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 73be38866fSTomas Novotny 745a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 755a441aadSAngus Ainslie (Purism) 7662a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 77ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 78ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 79ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 80ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 81d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 82d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 83d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 84d35567fcSMathieu Othacehe 85e21b5b1fSMårten Lindahl #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) 86fea2c97dSAstrid Rost #define VCNL4040_ALS_CONF_IT GENMASK(7, 6) /* Ambient integration time */ 87bc292aafSAstrid Rost #define VCNL4040_ALS_CONF_INT_EN BIT(1) /* Ambient light Interrupt enable */ 88*7f865127SAstrid Rost #define VCNL4040_ALS_CONF_PERS GENMASK(3, 2) /* Ambient interrupt persistence setting */ 89e21b5b1fSMårten Lindahl #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) 9085e2c6a2SMårten Lindahl #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ 91*7f865127SAstrid Rost #define VCNL4040_CONF1_PS_PERS GENMASK(5, 4) /* Proximity interrupt persistence setting */ 9254667612SMårten Lindahl #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ 9354667612SMårten Lindahl #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ 9454667612SMårten Lindahl #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ 95bc292aafSAstrid Rost #define VCNL4040_ALS_RISING BIT(12) /* Ambient Light cross high threshold */ 96bc292aafSAstrid Rost #define VCNL4040_ALS_FALLING BIT(13) /* Ambient Light cross low threshold */ 97e21b5b1fSMårten Lindahl 98d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 99d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 100d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 101d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 102d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 103d35567fcSMathieu Othacehe 104d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 105d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 106d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 107d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 108d35567fcSMathieu Othacehe 109d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 110d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 111d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 112d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 113d35567fcSMathieu Othacehe 114f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 115f6889c1bSMathieu Othacehe {1, 950000}, 116f6889c1bSMathieu Othacehe {3, 906250}, 117f6889c1bSMathieu Othacehe {7, 812500}, 118f6889c1bSMathieu Othacehe {16, 625000}, 119f6889c1bSMathieu Othacehe {31, 250000}, 120f6889c1bSMathieu Othacehe {62, 500000}, 121f6889c1bSMathieu Othacehe {125, 0}, 122f6889c1bSMathieu Othacehe {250, 0}, 123f6889c1bSMathieu Othacehe }; 12462a1efb9SPeter Meerwald 12585e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = { 12685e2c6a2SMårten Lindahl {0, 100}, 12785e2c6a2SMårten Lindahl {0, 150}, 12885e2c6a2SMårten Lindahl {0, 200}, 12985e2c6a2SMårten Lindahl {0, 250}, 13085e2c6a2SMårten Lindahl {0, 300}, 13185e2c6a2SMårten Lindahl {0, 350}, 13285e2c6a2SMårten Lindahl {0, 400}, 13385e2c6a2SMårten Lindahl {0, 800}, 13485e2c6a2SMårten Lindahl }; 13585e2c6a2SMårten Lindahl 136e55c96daSAstrid Rost static const int vcnl4200_ps_it_times[][2] = { 137e55c96daSAstrid Rost {0, 96}, 138e55c96daSAstrid Rost {0, 144}, 139e55c96daSAstrid Rost {0, 192}, 140e55c96daSAstrid Rost {0, 384}, 141e55c96daSAstrid Rost {0, 768}, 142e55c96daSAstrid Rost {0, 864}, 143e55c96daSAstrid Rost }; 144e55c96daSAstrid Rost 145fea2c97dSAstrid Rost static const int vcnl4040_als_it_times[][2] = { 146fea2c97dSAstrid Rost {0, 80000}, 147fea2c97dSAstrid Rost {0, 160000}, 148fea2c97dSAstrid Rost {0, 320000}, 149fea2c97dSAstrid Rost {0, 640000}, 150fea2c97dSAstrid Rost }; 151fea2c97dSAstrid Rost 152fea2c97dSAstrid Rost static const int vcnl4200_als_it_times[][2] = { 153fea2c97dSAstrid Rost {0, 50000}, 154fea2c97dSAstrid Rost {0, 100000}, 155fea2c97dSAstrid Rost {0, 200000}, 156fea2c97dSAstrid Rost {0, 400000}, 157fea2c97dSAstrid Rost }; 158fea2c97dSAstrid Rost 159*7f865127SAstrid Rost static const int vcnl4040_als_persistence[] = {1, 2, 4, 8}; 160*7f865127SAstrid Rost static const int vcnl4040_ps_persistence[] = {1, 2, 3, 4}; 161*7f865127SAstrid Rost 1625e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1635e00708dSGuido Günther 1641ebc787aSTomas Novotny enum vcnl4000_device_ids { 1651ebc787aSTomas Novotny VCNL4000, 16650c50b97STomas Novotny VCNL4010, 1675a441aadSAngus Ainslie (Purism) VCNL4040, 168be38866fSTomas Novotny VCNL4200, 169be38866fSTomas Novotny }; 170be38866fSTomas Novotny 171be38866fSTomas Novotny struct vcnl4200_channel { 172be38866fSTomas Novotny u8 reg; 173be38866fSTomas Novotny ktime_t last_measurement; 174be38866fSTomas Novotny ktime_t sampling_rate; 175be38866fSTomas Novotny struct mutex lock; 1761ebc787aSTomas Novotny }; 1771ebc787aSTomas Novotny 17862a1efb9SPeter Meerwald struct vcnl4000_data { 17962a1efb9SPeter Meerwald struct i2c_client *client; 1801ebc787aSTomas Novotny enum vcnl4000_device_ids id; 1811ebc787aSTomas Novotny int rev; 1821ebc787aSTomas Novotny int al_scale; 18354667612SMårten Lindahl u8 ps_int; /* proximity interrupt mode */ 184bc292aafSAstrid Rost u8 als_int; /* ambient light interrupt mode*/ 1851ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 186be38866fSTomas Novotny struct mutex vcnl4000_lock; 187be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 188be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 189f5a98e1fSGuido Günther uint32_t near_level; 19062a1efb9SPeter Meerwald }; 19162a1efb9SPeter Meerwald 1921ebc787aSTomas Novotny struct vcnl4000_chip_spec { 1931ebc787aSTomas Novotny const char *prod; 194d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 195d35567fcSMathieu Othacehe const int num_channels; 196d35567fcSMathieu Othacehe const struct iio_info *info; 197bfb6cfeeSMårten Lindahl const struct iio_buffer_setup_ops *buffer_setup_ops; 1981ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 1991ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 2001ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 2015e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 202bfb6cfeeSMårten Lindahl irqreturn_t (*irq_thread)(int irq, void *priv); 203bfb6cfeeSMårten Lindahl irqreturn_t (*trig_buffer_func)(int irq, void *priv); 204854965b7SAstrid Rost 205854965b7SAstrid Rost u8 int_reg; 206e55c96daSAstrid Rost const int(*ps_it_times)[][2]; 207e55c96daSAstrid Rost const int num_ps_it_times; 208fea2c97dSAstrid Rost const int(*als_it_times)[][2]; 209fea2c97dSAstrid Rost const int num_als_it_times; 210fea2c97dSAstrid Rost const unsigned int ulux_step; 2111ebc787aSTomas Novotny }; 2121ebc787aSTomas Novotny 21362a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 2141ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 21550c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 21650c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 2175a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 218be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 21962a1efb9SPeter Meerwald { } 22062a1efb9SPeter Meerwald }; 22162a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 22262a1efb9SPeter Meerwald 2235e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 2245e00708dSGuido Günther { 2255e00708dSGuido Günther /* no suspend op */ 2265e00708dSGuido Günther return 0; 2275e00708dSGuido Günther } 2285e00708dSGuido Günther 2291ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 2301ebc787aSTomas Novotny { 2311ebc787aSTomas Novotny int ret, prod_id; 2321ebc787aSTomas Novotny 2331ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 2341ebc787aSTomas Novotny if (ret < 0) 2351ebc787aSTomas Novotny return ret; 2361ebc787aSTomas Novotny 2371ebc787aSTomas Novotny prod_id = ret >> 4; 23858bf9aceSTomas Novotny switch (prod_id) { 23958bf9aceSTomas Novotny case VCNL4000_PROD_ID: 24058bf9aceSTomas Novotny if (data->id != VCNL4000) 24158bf9aceSTomas Novotny dev_warn(&data->client->dev, 24258bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 24358bf9aceSTomas Novotny break; 24458bf9aceSTomas Novotny case VCNL4010_PROD_ID: 24558bf9aceSTomas Novotny if (data->id != VCNL4010) 24658bf9aceSTomas Novotny dev_warn(&data->client->dev, 24758bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 24858bf9aceSTomas Novotny break; 24958bf9aceSTomas Novotny default: 2501ebc787aSTomas Novotny return -ENODEV; 25158bf9aceSTomas Novotny } 2521ebc787aSTomas Novotny 2531ebc787aSTomas Novotny data->rev = ret & 0xf; 2541ebc787aSTomas Novotny data->al_scale = 250000; 255be38866fSTomas Novotny 2565e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 257be38866fSTomas Novotny }; 258be38866fSTomas Novotny 259e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) 2605e00708dSGuido Günther { 2615e00708dSGuido Günther int ret; 2625e00708dSGuido Günther 263e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 264e21b5b1fSMårten Lindahl 265e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 266e21b5b1fSMårten Lindahl if (ret < 0) 267e21b5b1fSMårten Lindahl goto out; 268e21b5b1fSMårten Lindahl 269e21b5b1fSMårten Lindahl if (en) 270e21b5b1fSMårten Lindahl ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; 271e21b5b1fSMårten Lindahl else 272e21b5b1fSMårten Lindahl ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; 273e21b5b1fSMårten Lindahl 274e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); 275e21b5b1fSMårten Lindahl 276e21b5b1fSMårten Lindahl out: 277e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 278e21b5b1fSMårten Lindahl 279e21b5b1fSMårten Lindahl return ret; 280e21b5b1fSMårten Lindahl } 281e21b5b1fSMårten Lindahl 282e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) 283e21b5b1fSMårten Lindahl { 284e21b5b1fSMårten Lindahl int ret; 285e21b5b1fSMårten Lindahl 286e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 287e21b5b1fSMårten Lindahl 288e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 289e21b5b1fSMårten Lindahl if (ret < 0) 290e21b5b1fSMårten Lindahl goto out; 291e21b5b1fSMårten Lindahl 292e21b5b1fSMårten Lindahl if (en) 293e21b5b1fSMårten Lindahl ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; 294e21b5b1fSMårten Lindahl else 295e21b5b1fSMårten Lindahl ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; 296e21b5b1fSMårten Lindahl 297e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); 298e21b5b1fSMårten Lindahl 299e21b5b1fSMårten Lindahl out: 300e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 301e21b5b1fSMårten Lindahl 302e21b5b1fSMårten Lindahl return ret; 303e21b5b1fSMårten Lindahl } 304e21b5b1fSMårten Lindahl 305e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 306e21b5b1fSMårten Lindahl { 307e21b5b1fSMårten Lindahl int ret; 308e21b5b1fSMårten Lindahl 30954667612SMårten Lindahl /* Do not power down if interrupts are enabled */ 310bc292aafSAstrid Rost if (!on && (data->ps_int || data->als_int)) 31154667612SMårten Lindahl return 0; 31254667612SMårten Lindahl 313e21b5b1fSMårten Lindahl ret = vcnl4000_write_als_enable(data, on); 3145e00708dSGuido Günther if (ret < 0) 3155e00708dSGuido Günther return ret; 3165e00708dSGuido Günther 317e21b5b1fSMårten Lindahl ret = vcnl4000_write_ps_enable(data, on); 3185e00708dSGuido Günther if (ret < 0) 3195e00708dSGuido Günther return ret; 3205e00708dSGuido Günther 3215e00708dSGuido Günther if (on) { 3225e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 3235e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 3245e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 3255e00708dSGuido Günther } 3265e00708dSGuido Günther 3275e00708dSGuido Günther return 0; 3285e00708dSGuido Günther } 3295e00708dSGuido Günther 330be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 331be38866fSTomas Novotny { 3325a441aadSAngus Ainslie (Purism) int ret, id; 333be38866fSTomas Novotny 334be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 335be38866fSTomas Novotny if (ret < 0) 336be38866fSTomas Novotny return ret; 337be38866fSTomas Novotny 3385a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3395a441aadSAngus Ainslie (Purism) 3405a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 3415a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 3425a441aadSAngus Ainslie (Purism) if (ret < 0) 3435a441aadSAngus Ainslie (Purism) return ret; 3445a441aadSAngus Ainslie (Purism) 3455a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3465a441aadSAngus Ainslie (Purism) 3475a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 348be38866fSTomas Novotny return -ENODEV; 3495a441aadSAngus Ainslie (Purism) } 3505a441aadSAngus Ainslie (Purism) 3515a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 352be38866fSTomas Novotny 353be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 35454667612SMårten Lindahl data->ps_int = 0; 355bc292aafSAstrid Rost data->als_int = 0; 356be38866fSTomas Novotny 357be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 358be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 3595a441aadSAngus Ainslie (Purism) switch (id) { 3605a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 361b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 362b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 363b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 364b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 3655a441aadSAngus Ainslie (Purism) break; 3665a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 3672ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 3682ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 3692ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 3702ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 3715a441aadSAngus Ainslie (Purism) break; 3725a441aadSAngus Ainslie (Purism) } 373fea2c97dSAstrid Rost data->al_scale = data->chip_spec->ulux_step; 374be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 375be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 3761ebc787aSTomas Novotny 3775e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 3785e00708dSGuido Günther if (ret < 0) 3795e00708dSGuido Günther return ret; 3805e00708dSGuido Günther 3811ebc787aSTomas Novotny return 0; 3821ebc787aSTomas Novotny }; 3831ebc787aSTomas Novotny 384816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 385816956c3SMathieu Othacehe { 386816956c3SMathieu Othacehe s32 ret; 387816956c3SMathieu Othacehe 388816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 389816956c3SMathieu Othacehe if (ret < 0) 390816956c3SMathieu Othacehe return ret; 391816956c3SMathieu Othacehe 392816956c3SMathieu Othacehe *val = ret; 393816956c3SMathieu Othacehe return 0; 394816956c3SMathieu Othacehe } 395816956c3SMathieu Othacehe 396816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 397816956c3SMathieu Othacehe { 398816956c3SMathieu Othacehe if (val > U16_MAX) 399816956c3SMathieu Othacehe return -ERANGE; 400816956c3SMathieu Othacehe 401816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 402816956c3SMathieu Othacehe } 403816956c3SMathieu Othacehe 404816956c3SMathieu Othacehe 40562a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 40662a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 40762a1efb9SPeter Meerwald { 40862a1efb9SPeter Meerwald int tries = 20; 40962a1efb9SPeter Meerwald int ret; 41062a1efb9SPeter Meerwald 411be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 412ff34ed6dSPeter Meerwald-Stadler 41362a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 41462a1efb9SPeter Meerwald req_mask); 41562a1efb9SPeter Meerwald if (ret < 0) 416ff34ed6dSPeter Meerwald-Stadler goto fail; 41762a1efb9SPeter Meerwald 41862a1efb9SPeter Meerwald /* wait for data to become ready */ 41962a1efb9SPeter Meerwald while (tries--) { 42062a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 42162a1efb9SPeter Meerwald if (ret < 0) 422ff34ed6dSPeter Meerwald-Stadler goto fail; 42362a1efb9SPeter Meerwald if (ret & rdy_mask) 42462a1efb9SPeter Meerwald break; 42562a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 42662a1efb9SPeter Meerwald } 42762a1efb9SPeter Meerwald 42862a1efb9SPeter Meerwald if (tries < 0) { 42962a1efb9SPeter Meerwald dev_err(&data->client->dev, 43062a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 431ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 432ff34ed6dSPeter Meerwald-Stadler goto fail; 43362a1efb9SPeter Meerwald } 43462a1efb9SPeter Meerwald 435816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 43662a1efb9SPeter Meerwald if (ret < 0) 437ff34ed6dSPeter Meerwald-Stadler goto fail; 43862a1efb9SPeter Meerwald 439be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 44062a1efb9SPeter Meerwald 44162a1efb9SPeter Meerwald return 0; 442ff34ed6dSPeter Meerwald-Stadler 443ff34ed6dSPeter Meerwald-Stadler fail: 444be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 445ff34ed6dSPeter Meerwald-Stadler return ret; 44662a1efb9SPeter Meerwald } 44762a1efb9SPeter Meerwald 448be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 449be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 450be38866fSTomas Novotny { 451be38866fSTomas Novotny int ret; 452be38866fSTomas Novotny s64 delta; 453be38866fSTomas Novotny ktime_t next_measurement; 454be38866fSTomas Novotny 455be38866fSTomas Novotny mutex_lock(&chan->lock); 456be38866fSTomas Novotny 457be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 458be38866fSTomas Novotny chan->sampling_rate); 459be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 460be38866fSTomas Novotny if (delta > 0) 461be38866fSTomas Novotny usleep_range(delta, delta + 500); 462be38866fSTomas Novotny chan->last_measurement = ktime_get(); 463be38866fSTomas Novotny 464be38866fSTomas Novotny mutex_unlock(&chan->lock); 465be38866fSTomas Novotny 466be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 467be38866fSTomas Novotny if (ret < 0) 468be38866fSTomas Novotny return ret; 469be38866fSTomas Novotny 470be38866fSTomas Novotny *val = ret; 471be38866fSTomas Novotny 472be38866fSTomas Novotny return 0; 473be38866fSTomas Novotny } 474be38866fSTomas Novotny 4751ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 4761ebc787aSTomas Novotny { 4771ebc787aSTomas Novotny return vcnl4000_measure(data, 4781ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 4791ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 4801ebc787aSTomas Novotny } 4811ebc787aSTomas Novotny 482be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 483be38866fSTomas Novotny { 484be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 485be38866fSTomas Novotny } 486be38866fSTomas Novotny 4871ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 4881ebc787aSTomas Novotny { 4891ebc787aSTomas Novotny return vcnl4000_measure(data, 4901ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 4911ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 4921ebc787aSTomas Novotny } 4931ebc787aSTomas Novotny 494be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 495be38866fSTomas Novotny { 496be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 497be38866fSTomas Novotny } 498be38866fSTomas Novotny 499f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 500f6889c1bSMathieu Othacehe int *val2) 501f6889c1bSMathieu Othacehe { 502f6889c1bSMathieu Othacehe int ret; 503f6889c1bSMathieu Othacehe 504f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 505f6889c1bSMathieu Othacehe if (ret < 0) 506f6889c1bSMathieu Othacehe return ret; 507f6889c1bSMathieu Othacehe 508f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 509f6889c1bSMathieu Othacehe return -EINVAL; 510f6889c1bSMathieu Othacehe 511f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 512f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 513f6889c1bSMathieu Othacehe 514f6889c1bSMathieu Othacehe return 0; 515f6889c1bSMathieu Othacehe } 516f6889c1bSMathieu Othacehe 517d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 518f5a98e1fSGuido Günther { 519d35567fcSMathieu Othacehe int ret; 520f5a98e1fSGuido Günther 521d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 522d35567fcSMathieu Othacehe if (ret < 0) 523d35567fcSMathieu Othacehe return false; 524d35567fcSMathieu Othacehe 525d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 526f5a98e1fSGuido Günther } 527f5a98e1fSGuido Günther 5285e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 5295e00708dSGuido Günther { 5305e00708dSGuido Günther struct device *dev = &data->client->dev; 5315e00708dSGuido Günther int ret; 5325e00708dSGuido Günther 5335e00708dSGuido Günther if (on) { 534db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 5355e00708dSGuido Günther } else { 5365e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 5375e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 5385e00708dSGuido Günther } 5395e00708dSGuido Günther 5405e00708dSGuido Günther return ret; 5415e00708dSGuido Günther } 5425e00708dSGuido Günther 543fea2c97dSAstrid Rost static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2) 544fea2c97dSAstrid Rost { 545fea2c97dSAstrid Rost int ret; 546fea2c97dSAstrid Rost 547fea2c97dSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 548fea2c97dSAstrid Rost if (ret < 0) 549fea2c97dSAstrid Rost return ret; 550fea2c97dSAstrid Rost 551fea2c97dSAstrid Rost ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 552fea2c97dSAstrid Rost if (ret >= data->chip_spec->num_als_it_times) 553fea2c97dSAstrid Rost return -EINVAL; 554fea2c97dSAstrid Rost 555fea2c97dSAstrid Rost *val = (*data->chip_spec->als_it_times)[ret][0]; 556fea2c97dSAstrid Rost *val2 = (*data->chip_spec->als_it_times)[ret][1]; 557fea2c97dSAstrid Rost 558fea2c97dSAstrid Rost return 0; 559fea2c97dSAstrid Rost } 560fea2c97dSAstrid Rost 561fea2c97dSAstrid Rost static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val) 562fea2c97dSAstrid Rost { 563fea2c97dSAstrid Rost unsigned int i; 564fea2c97dSAstrid Rost int ret; 565fea2c97dSAstrid Rost u16 regval; 566fea2c97dSAstrid Rost 567fea2c97dSAstrid Rost for (i = 0; i < data->chip_spec->num_als_it_times; i++) { 568fea2c97dSAstrid Rost if (val == (*data->chip_spec->als_it_times)[i][1]) 569fea2c97dSAstrid Rost break; 570fea2c97dSAstrid Rost } 571fea2c97dSAstrid Rost 572fea2c97dSAstrid Rost if (i == data->chip_spec->num_als_it_times) 573fea2c97dSAstrid Rost return -EINVAL; 574fea2c97dSAstrid Rost 575fea2c97dSAstrid Rost data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200); 576fea2c97dSAstrid Rost data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step, 577fea2c97dSAstrid Rost (*data->chip_spec->als_it_times)[0][1]), 578fea2c97dSAstrid Rost val); 579fea2c97dSAstrid Rost 580fea2c97dSAstrid Rost mutex_lock(&data->vcnl4000_lock); 581fea2c97dSAstrid Rost 582fea2c97dSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 583fea2c97dSAstrid Rost if (ret < 0) 584fea2c97dSAstrid Rost goto out_unlock; 585fea2c97dSAstrid Rost 586fea2c97dSAstrid Rost regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i); 587fea2c97dSAstrid Rost regval |= (ret & ~VCNL4040_ALS_CONF_IT); 588fea2c97dSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 589fea2c97dSAstrid Rost VCNL4200_AL_CONF, 590fea2c97dSAstrid Rost regval); 591fea2c97dSAstrid Rost 592fea2c97dSAstrid Rost out_unlock: 593fea2c97dSAstrid Rost mutex_unlock(&data->vcnl4000_lock); 594fea2c97dSAstrid Rost return ret; 595fea2c97dSAstrid Rost } 596fea2c97dSAstrid Rost 59785e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) 59885e2c6a2SMårten Lindahl { 59985e2c6a2SMårten Lindahl int ret; 60085e2c6a2SMårten Lindahl 60185e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 60285e2c6a2SMårten Lindahl if (ret < 0) 60385e2c6a2SMårten Lindahl return ret; 60485e2c6a2SMårten Lindahl 60585e2c6a2SMårten Lindahl ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 60685e2c6a2SMårten Lindahl 607e55c96daSAstrid Rost if (ret >= data->chip_spec->num_ps_it_times) 60885e2c6a2SMårten Lindahl return -EINVAL; 60985e2c6a2SMårten Lindahl 610e55c96daSAstrid Rost *val = (*data->chip_spec->ps_it_times)[ret][0]; 611e55c96daSAstrid Rost *val2 = (*data->chip_spec->ps_it_times)[ret][1]; 61285e2c6a2SMårten Lindahl 61385e2c6a2SMårten Lindahl return 0; 61485e2c6a2SMårten Lindahl } 61585e2c6a2SMårten Lindahl 61685e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) 61785e2c6a2SMårten Lindahl { 61885e2c6a2SMårten Lindahl unsigned int i; 61985e2c6a2SMårten Lindahl int ret, index = -1; 62085e2c6a2SMårten Lindahl u16 regval; 62185e2c6a2SMårten Lindahl 622e55c96daSAstrid Rost for (i = 0; i < data->chip_spec->num_ps_it_times; i++) { 623e55c96daSAstrid Rost if (val == (*data->chip_spec->ps_it_times)[i][1]) { 62485e2c6a2SMårten Lindahl index = i; 62585e2c6a2SMårten Lindahl break; 62685e2c6a2SMårten Lindahl } 62785e2c6a2SMårten Lindahl } 62885e2c6a2SMårten Lindahl 62985e2c6a2SMårten Lindahl if (index < 0) 63085e2c6a2SMårten Lindahl return -EINVAL; 63185e2c6a2SMårten Lindahl 632e55c96daSAstrid Rost data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC); 633e55c96daSAstrid Rost 63485e2c6a2SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 63585e2c6a2SMårten Lindahl 63685e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 63785e2c6a2SMårten Lindahl if (ret < 0) 63885e2c6a2SMårten Lindahl goto out; 63985e2c6a2SMårten Lindahl 64085e2c6a2SMårten Lindahl regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | 64185e2c6a2SMårten Lindahl FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); 64285e2c6a2SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 64385e2c6a2SMårten Lindahl regval); 64485e2c6a2SMårten Lindahl 64585e2c6a2SMårten Lindahl out: 64685e2c6a2SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 64785e2c6a2SMårten Lindahl return ret; 64885e2c6a2SMårten Lindahl } 64985e2c6a2SMårten Lindahl 650*7f865127SAstrid Rost static ssize_t vcnl4040_read_als_period(struct vcnl4000_data *data, int *val, int *val2) 651*7f865127SAstrid Rost { 652*7f865127SAstrid Rost int ret, ret_pers, it; 653*7f865127SAstrid Rost int64_t val_c; 654*7f865127SAstrid Rost 655*7f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 656*7f865127SAstrid Rost if (ret < 0) 657*7f865127SAstrid Rost return ret; 658*7f865127SAstrid Rost 659*7f865127SAstrid Rost ret_pers = FIELD_GET(VCNL4040_ALS_CONF_PERS, ret); 660*7f865127SAstrid Rost if (ret_pers >= ARRAY_SIZE(vcnl4040_als_persistence)) 661*7f865127SAstrid Rost return -EINVAL; 662*7f865127SAstrid Rost 663*7f865127SAstrid Rost it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 664*7f865127SAstrid Rost if (it >= data->chip_spec->num_als_it_times) 665*7f865127SAstrid Rost return -EINVAL; 666*7f865127SAstrid Rost 667*7f865127SAstrid Rost val_c = mul_u32_u32((*data->chip_spec->als_it_times)[it][1], 668*7f865127SAstrid Rost vcnl4040_als_persistence[ret_pers]); 669*7f865127SAstrid Rost *val = div_u64_rem(val_c, MICRO, val2); 670*7f865127SAstrid Rost 671*7f865127SAstrid Rost return IIO_VAL_INT_PLUS_MICRO; 672*7f865127SAstrid Rost } 673*7f865127SAstrid Rost 674*7f865127SAstrid Rost static ssize_t vcnl4040_write_als_period(struct vcnl4000_data *data, int val, int val2) 675*7f865127SAstrid Rost { 676*7f865127SAstrid Rost unsigned int i; 677*7f865127SAstrid Rost int ret, it; 678*7f865127SAstrid Rost u16 regval; 679*7f865127SAstrid Rost u64 val_n = mul_u32_u32(val, MICRO) + val2; 680*7f865127SAstrid Rost 681*7f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 682*7f865127SAstrid Rost if (ret < 0) 683*7f865127SAstrid Rost return ret; 684*7f865127SAstrid Rost 685*7f865127SAstrid Rost it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 686*7f865127SAstrid Rost if (it >= data->chip_spec->num_als_it_times) 687*7f865127SAstrid Rost return -EINVAL; 688*7f865127SAstrid Rost 689*7f865127SAstrid Rost for (i = 0; i < ARRAY_SIZE(vcnl4040_als_persistence) - 1; i++) { 690*7f865127SAstrid Rost if (val_n < mul_u32_u32(vcnl4040_als_persistence[i], 691*7f865127SAstrid Rost (*data->chip_spec->als_it_times)[it][1])) 692*7f865127SAstrid Rost break; 693*7f865127SAstrid Rost } 694*7f865127SAstrid Rost 695*7f865127SAstrid Rost mutex_lock(&data->vcnl4000_lock); 696*7f865127SAstrid Rost 697*7f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 698*7f865127SAstrid Rost if (ret < 0) 699*7f865127SAstrid Rost goto out_unlock; 700*7f865127SAstrid Rost 701*7f865127SAstrid Rost regval = FIELD_PREP(VCNL4040_ALS_CONF_PERS, i); 702*7f865127SAstrid Rost regval |= (ret & ~VCNL4040_ALS_CONF_PERS); 703*7f865127SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 704*7f865127SAstrid Rost regval); 705*7f865127SAstrid Rost 706*7f865127SAstrid Rost out_unlock: 707*7f865127SAstrid Rost mutex_unlock(&data->vcnl4000_lock); 708*7f865127SAstrid Rost return ret; 709*7f865127SAstrid Rost } 710*7f865127SAstrid Rost 711*7f865127SAstrid Rost static ssize_t vcnl4040_read_ps_period(struct vcnl4000_data *data, int *val, int *val2) 712*7f865127SAstrid Rost { 713*7f865127SAstrid Rost int ret, ret_pers, it; 714*7f865127SAstrid Rost 715*7f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 716*7f865127SAstrid Rost if (ret < 0) 717*7f865127SAstrid Rost return ret; 718*7f865127SAstrid Rost 719*7f865127SAstrid Rost ret_pers = FIELD_GET(VCNL4040_CONF1_PS_PERS, ret); 720*7f865127SAstrid Rost if (ret_pers >= ARRAY_SIZE(vcnl4040_ps_persistence)) 721*7f865127SAstrid Rost return -EINVAL; 722*7f865127SAstrid Rost 723*7f865127SAstrid Rost it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 724*7f865127SAstrid Rost if (it >= data->chip_spec->num_ps_it_times) 725*7f865127SAstrid Rost return -EINVAL; 726*7f865127SAstrid Rost 727*7f865127SAstrid Rost *val = (*data->chip_spec->ps_it_times)[it][0]; 728*7f865127SAstrid Rost *val2 = (*data->chip_spec->ps_it_times)[it][1] * 729*7f865127SAstrid Rost vcnl4040_ps_persistence[ret_pers]; 730*7f865127SAstrid Rost 731*7f865127SAstrid Rost return IIO_VAL_INT_PLUS_MICRO; 732*7f865127SAstrid Rost } 733*7f865127SAstrid Rost 734*7f865127SAstrid Rost static ssize_t vcnl4040_write_ps_period(struct vcnl4000_data *data, int val, int val2) 735*7f865127SAstrid Rost { 736*7f865127SAstrid Rost int ret, it, i; 737*7f865127SAstrid Rost u16 regval; 738*7f865127SAstrid Rost 739*7f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 740*7f865127SAstrid Rost if (ret < 0) 741*7f865127SAstrid Rost return ret; 742*7f865127SAstrid Rost 743*7f865127SAstrid Rost it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 744*7f865127SAstrid Rost if (it >= data->chip_spec->num_ps_it_times) 745*7f865127SAstrid Rost return -EINVAL; 746*7f865127SAstrid Rost 747*7f865127SAstrid Rost if (val > 0) 748*7f865127SAstrid Rost i = ARRAY_SIZE(vcnl4040_ps_persistence) - 1; 749*7f865127SAstrid Rost else { 750*7f865127SAstrid Rost for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_persistence) - 1; i++) { 751*7f865127SAstrid Rost if (val2 <= vcnl4040_ps_persistence[i] * 752*7f865127SAstrid Rost (*data->chip_spec->ps_it_times)[it][1]) 753*7f865127SAstrid Rost break; 754*7f865127SAstrid Rost } 755*7f865127SAstrid Rost } 756*7f865127SAstrid Rost 757*7f865127SAstrid Rost mutex_lock(&data->vcnl4000_lock); 758*7f865127SAstrid Rost 759*7f865127SAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 760*7f865127SAstrid Rost if (ret < 0) 761*7f865127SAstrid Rost goto out_unlock; 762*7f865127SAstrid Rost 763*7f865127SAstrid Rost regval = FIELD_PREP(VCNL4040_CONF1_PS_PERS, i); 764*7f865127SAstrid Rost regval |= (ret & ~VCNL4040_CONF1_PS_PERS); 765*7f865127SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 766*7f865127SAstrid Rost regval); 767*7f865127SAstrid Rost 768*7f865127SAstrid Rost out_unlock: 769*7f865127SAstrid Rost mutex_unlock(&data->vcnl4000_lock); 770*7f865127SAstrid Rost return ret; 771*7f865127SAstrid Rost } 772*7f865127SAstrid Rost 77362a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 77462a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 77562a1efb9SPeter Meerwald int *val, int *val2, long mask) 77662a1efb9SPeter Meerwald { 7775d693139SPeter Meerwald-Stadler int ret; 77862a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 77962a1efb9SPeter Meerwald 78062a1efb9SPeter Meerwald switch (mask) { 78162a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 7825e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 7835e00708dSGuido Günther if (ret < 0) 7845e00708dSGuido Günther return ret; 7855e00708dSGuido Günther 78662a1efb9SPeter Meerwald switch (chan->type) { 78762a1efb9SPeter Meerwald case IIO_LIGHT: 7881ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 7894a818643SGuido Günther if (!ret) 7904a818643SGuido Günther ret = IIO_VAL_INT; 7914a818643SGuido Günther break; 79262a1efb9SPeter Meerwald case IIO_PROXIMITY: 7931ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 7944a818643SGuido Günther if (!ret) 7954a818643SGuido Günther ret = IIO_VAL_INT; 7964a818643SGuido Günther break; 79762a1efb9SPeter Meerwald default: 7984a818643SGuido Günther ret = -EINVAL; 79962a1efb9SPeter Meerwald } 8005e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 8014a818643SGuido Günther return ret; 80262a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 8035d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 8045d693139SPeter Meerwald-Stadler return -EINVAL; 8055d693139SPeter Meerwald-Stadler 80662a1efb9SPeter Meerwald *val = 0; 8071ebc787aSTomas Novotny *val2 = data->al_scale; 8085d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 80985e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 8102be17b68SAstrid Rost switch (chan->type) { 811fea2c97dSAstrid Rost case IIO_LIGHT: 812fea2c97dSAstrid Rost ret = vcnl4040_read_als_it(data, val, val2); 813fea2c97dSAstrid Rost break; 8142be17b68SAstrid Rost case IIO_PROXIMITY: 81585e2c6a2SMårten Lindahl ret = vcnl4040_read_ps_it(data, val, val2); 8162be17b68SAstrid Rost break; 8172be17b68SAstrid Rost default: 8182be17b68SAstrid Rost return -EINVAL; 8192be17b68SAstrid Rost } 82085e2c6a2SMårten Lindahl if (ret < 0) 82185e2c6a2SMårten Lindahl return ret; 82285e2c6a2SMårten Lindahl return IIO_VAL_INT_PLUS_MICRO; 82385e2c6a2SMårten Lindahl default: 82485e2c6a2SMårten Lindahl return -EINVAL; 82585e2c6a2SMårten Lindahl } 82685e2c6a2SMårten Lindahl } 82785e2c6a2SMårten Lindahl 82885e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev, 82985e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 83085e2c6a2SMårten Lindahl int val, int val2, long mask) 83185e2c6a2SMårten Lindahl { 83285e2c6a2SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 83385e2c6a2SMårten Lindahl 83485e2c6a2SMårten Lindahl switch (mask) { 83585e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 83685e2c6a2SMårten Lindahl if (val != 0) 83785e2c6a2SMårten Lindahl return -EINVAL; 8382be17b68SAstrid Rost switch (chan->type) { 839fea2c97dSAstrid Rost case IIO_LIGHT: 840fea2c97dSAstrid Rost return vcnl4040_write_als_it(data, val2); 8412be17b68SAstrid Rost case IIO_PROXIMITY: 84285e2c6a2SMårten Lindahl return vcnl4040_write_ps_it(data, val2); 84385e2c6a2SMårten Lindahl default: 84485e2c6a2SMårten Lindahl return -EINVAL; 84585e2c6a2SMårten Lindahl } 8462be17b68SAstrid Rost default: 8472be17b68SAstrid Rost return -EINVAL; 8482be17b68SAstrid Rost } 84985e2c6a2SMårten Lindahl } 85085e2c6a2SMårten Lindahl 85185e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev, 85285e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 85385e2c6a2SMårten Lindahl const int **vals, int *type, int *length, 85485e2c6a2SMårten Lindahl long mask) 85585e2c6a2SMårten Lindahl { 856e55c96daSAstrid Rost struct vcnl4000_data *data = iio_priv(indio_dev); 857e55c96daSAstrid Rost 85885e2c6a2SMårten Lindahl switch (mask) { 85985e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 8602be17b68SAstrid Rost switch (chan->type) { 861fea2c97dSAstrid Rost case IIO_LIGHT: 862fea2c97dSAstrid Rost *vals = (int *)(*data->chip_spec->als_it_times); 863fea2c97dSAstrid Rost *length = 2 * data->chip_spec->num_als_it_times; 864fea2c97dSAstrid Rost break; 8652be17b68SAstrid Rost case IIO_PROXIMITY: 866e55c96daSAstrid Rost *vals = (int *)(*data->chip_spec->ps_it_times); 867e55c96daSAstrid Rost *length = 2 * data->chip_spec->num_ps_it_times; 8682be17b68SAstrid Rost break; 8692be17b68SAstrid Rost default: 8702be17b68SAstrid Rost return -EINVAL; 8712be17b68SAstrid Rost } 8722be17b68SAstrid Rost *type = IIO_VAL_INT_PLUS_MICRO; 87385e2c6a2SMårten Lindahl return IIO_AVAIL_LIST; 87462a1efb9SPeter Meerwald default: 8755d693139SPeter Meerwald-Stadler return -EINVAL; 87662a1efb9SPeter Meerwald } 87762a1efb9SPeter Meerwald } 87862a1efb9SPeter Meerwald 879d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 880d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 881d35567fcSMathieu Othacehe int *val, int *val2, long mask) 882d35567fcSMathieu Othacehe { 883d35567fcSMathieu Othacehe int ret; 884d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 885d35567fcSMathieu Othacehe 886d35567fcSMathieu Othacehe switch (mask) { 887d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 888d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 889d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 890d35567fcSMathieu Othacehe if (ret) 891d35567fcSMathieu Othacehe return ret; 892d35567fcSMathieu Othacehe 893d35567fcSMathieu Othacehe /* Protect against event capture. */ 894d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 895d35567fcSMathieu Othacehe ret = -EBUSY; 896d35567fcSMathieu Othacehe } else { 897d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 898d35567fcSMathieu Othacehe mask); 899d35567fcSMathieu Othacehe } 900d35567fcSMathieu Othacehe 901d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 902d35567fcSMathieu Othacehe return ret; 903f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 904f6889c1bSMathieu Othacehe switch (chan->type) { 905f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 906f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 907f6889c1bSMathieu Othacehe if (ret < 0) 908f6889c1bSMathieu Othacehe return ret; 909f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 910d35567fcSMathieu Othacehe default: 911d35567fcSMathieu Othacehe return -EINVAL; 912d35567fcSMathieu Othacehe } 913f6889c1bSMathieu Othacehe default: 914f6889c1bSMathieu Othacehe return -EINVAL; 915f6889c1bSMathieu Othacehe } 916f6889c1bSMathieu Othacehe } 917f6889c1bSMathieu Othacehe 918f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 919f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 920f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 921f6889c1bSMathieu Othacehe long mask) 922f6889c1bSMathieu Othacehe { 923f6889c1bSMathieu Othacehe switch (mask) { 924f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 925f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 926f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 927f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 928f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 929f6889c1bSMathieu Othacehe default: 930f6889c1bSMathieu Othacehe return -EINVAL; 931f6889c1bSMathieu Othacehe } 932f6889c1bSMathieu Othacehe } 933f6889c1bSMathieu Othacehe 934f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 935f6889c1bSMathieu Othacehe int val2) 936f6889c1bSMathieu Othacehe { 937f6889c1bSMathieu Othacehe unsigned int i; 938f6889c1bSMathieu Othacehe int index = -1; 939f6889c1bSMathieu Othacehe 940f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 941f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 942f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 943f6889c1bSMathieu Othacehe index = i; 944f6889c1bSMathieu Othacehe break; 945f6889c1bSMathieu Othacehe } 946f6889c1bSMathieu Othacehe } 947f6889c1bSMathieu Othacehe 948f6889c1bSMathieu Othacehe if (index < 0) 949f6889c1bSMathieu Othacehe return -EINVAL; 950f6889c1bSMathieu Othacehe 951f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 952f6889c1bSMathieu Othacehe index); 953f6889c1bSMathieu Othacehe } 954f6889c1bSMathieu Othacehe 955f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 956f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 957f6889c1bSMathieu Othacehe int val, int val2, long mask) 958f6889c1bSMathieu Othacehe { 959f6889c1bSMathieu Othacehe int ret; 960f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 961f6889c1bSMathieu Othacehe 962f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 963f6889c1bSMathieu Othacehe if (ret) 964f6889c1bSMathieu Othacehe return ret; 965f6889c1bSMathieu Othacehe 966f6889c1bSMathieu Othacehe /* Protect against event capture. */ 967f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 968f6889c1bSMathieu Othacehe ret = -EBUSY; 969f6889c1bSMathieu Othacehe goto end; 970f6889c1bSMathieu Othacehe } 971f6889c1bSMathieu Othacehe 972f6889c1bSMathieu Othacehe switch (mask) { 973f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 974f6889c1bSMathieu Othacehe switch (chan->type) { 975f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 976f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 977f6889c1bSMathieu Othacehe goto end; 978f6889c1bSMathieu Othacehe default: 979f6889c1bSMathieu Othacehe ret = -EINVAL; 980f6889c1bSMathieu Othacehe goto end; 981f6889c1bSMathieu Othacehe } 982f6889c1bSMathieu Othacehe default: 983f6889c1bSMathieu Othacehe ret = -EINVAL; 984f6889c1bSMathieu Othacehe goto end; 985f6889c1bSMathieu Othacehe } 986f6889c1bSMathieu Othacehe 987f6889c1bSMathieu Othacehe end: 988f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 989f6889c1bSMathieu Othacehe return ret; 990d35567fcSMathieu Othacehe } 991d35567fcSMathieu Othacehe 992d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 993d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 994d35567fcSMathieu Othacehe enum iio_event_type type, 995d35567fcSMathieu Othacehe enum iio_event_direction dir, 996d35567fcSMathieu Othacehe enum iio_event_info info, 997d35567fcSMathieu Othacehe int *val, int *val2) 998d35567fcSMathieu Othacehe { 999d35567fcSMathieu Othacehe int ret; 1000d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1001d35567fcSMathieu Othacehe 1002d35567fcSMathieu Othacehe switch (info) { 1003d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 1004d35567fcSMathieu Othacehe switch (dir) { 1005d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 1006d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 1007d35567fcSMathieu Othacehe val); 1008d35567fcSMathieu Othacehe if (ret < 0) 1009d35567fcSMathieu Othacehe return ret; 1010d35567fcSMathieu Othacehe return IIO_VAL_INT; 1011d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 1012d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 1013d35567fcSMathieu Othacehe val); 1014d35567fcSMathieu Othacehe if (ret < 0) 1015d35567fcSMathieu Othacehe return ret; 1016d35567fcSMathieu Othacehe return IIO_VAL_INT; 1017d35567fcSMathieu Othacehe default: 1018d35567fcSMathieu Othacehe return -EINVAL; 1019d35567fcSMathieu Othacehe } 1020d35567fcSMathieu Othacehe default: 1021d35567fcSMathieu Othacehe return -EINVAL; 1022d35567fcSMathieu Othacehe } 1023d35567fcSMathieu Othacehe } 1024d35567fcSMathieu Othacehe 1025d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 1026d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1027d35567fcSMathieu Othacehe enum iio_event_type type, 1028d35567fcSMathieu Othacehe enum iio_event_direction dir, 1029d35567fcSMathieu Othacehe enum iio_event_info info, 1030d35567fcSMathieu Othacehe int val, int val2) 1031d35567fcSMathieu Othacehe { 1032d35567fcSMathieu Othacehe int ret; 1033d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1034d35567fcSMathieu Othacehe 1035d35567fcSMathieu Othacehe switch (info) { 1036d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 1037d35567fcSMathieu Othacehe switch (dir) { 1038d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 1039d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 1040d35567fcSMathieu Othacehe val); 1041d35567fcSMathieu Othacehe if (ret < 0) 1042d35567fcSMathieu Othacehe return ret; 1043d35567fcSMathieu Othacehe return IIO_VAL_INT; 1044d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 1045d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 1046d35567fcSMathieu Othacehe val); 1047d35567fcSMathieu Othacehe if (ret < 0) 1048d35567fcSMathieu Othacehe return ret; 1049d35567fcSMathieu Othacehe return IIO_VAL_INT; 1050d35567fcSMathieu Othacehe default: 1051d35567fcSMathieu Othacehe return -EINVAL; 1052d35567fcSMathieu Othacehe } 1053d35567fcSMathieu Othacehe default: 1054d35567fcSMathieu Othacehe return -EINVAL; 1055d35567fcSMathieu Othacehe } 1056d35567fcSMathieu Othacehe } 1057d35567fcSMathieu Othacehe 105854667612SMårten Lindahl static int vcnl4040_read_event(struct iio_dev *indio_dev, 105954667612SMårten Lindahl const struct iio_chan_spec *chan, 106054667612SMårten Lindahl enum iio_event_type type, 106154667612SMårten Lindahl enum iio_event_direction dir, 106254667612SMårten Lindahl enum iio_event_info info, 106354667612SMårten Lindahl int *val, int *val2) 106454667612SMårten Lindahl { 106554667612SMårten Lindahl int ret; 106654667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 106754667612SMårten Lindahl 10682be17b68SAstrid Rost switch (chan->type) { 1069bc292aafSAstrid Rost case IIO_LIGHT: 1070bc292aafSAstrid Rost switch (info) { 1071*7f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 1072*7f865127SAstrid Rost return vcnl4040_read_als_period(data, val, val2); 1073bc292aafSAstrid Rost case IIO_EV_INFO_VALUE: 1074bc292aafSAstrid Rost switch (dir) { 1075bc292aafSAstrid Rost case IIO_EV_DIR_RISING: 1076bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, 1077bc292aafSAstrid Rost VCNL4040_ALS_THDH_LM); 1078bc292aafSAstrid Rost break; 1079bc292aafSAstrid Rost case IIO_EV_DIR_FALLING: 1080bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, 1081bc292aafSAstrid Rost VCNL4040_ALS_THDL_LM); 1082bc292aafSAstrid Rost break; 1083bc292aafSAstrid Rost default: 1084bc292aafSAstrid Rost return -EINVAL; 1085bc292aafSAstrid Rost } 1086bc292aafSAstrid Rost break; 1087bc292aafSAstrid Rost default: 1088bc292aafSAstrid Rost return -EINVAL; 1089bc292aafSAstrid Rost } 1090bc292aafSAstrid Rost break; 10912be17b68SAstrid Rost case IIO_PROXIMITY: 10922be17b68SAstrid Rost switch (info) { 1093*7f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 1094*7f865127SAstrid Rost return vcnl4040_read_ps_period(data, val, val2); 10952be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 109654667612SMårten Lindahl switch (dir) { 109754667612SMårten Lindahl case IIO_EV_DIR_RISING: 109854667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 109954667612SMårten Lindahl VCNL4040_PS_THDH_LM); 11002be17b68SAstrid Rost break; 110154667612SMårten Lindahl case IIO_EV_DIR_FALLING: 110254667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 110354667612SMårten Lindahl VCNL4040_PS_THDL_LM); 11042be17b68SAstrid Rost break; 11052be17b68SAstrid Rost default: 11062be17b68SAstrid Rost return -EINVAL; 11072be17b68SAstrid Rost } 11082be17b68SAstrid Rost break; 11092be17b68SAstrid Rost default: 11102be17b68SAstrid Rost return -EINVAL; 11112be17b68SAstrid Rost } 11122be17b68SAstrid Rost break; 11132be17b68SAstrid Rost default: 11142be17b68SAstrid Rost return -EINVAL; 11152be17b68SAstrid Rost } 111654667612SMårten Lindahl if (ret < 0) 111754667612SMårten Lindahl return ret; 111854667612SMårten Lindahl *val = ret; 111954667612SMårten Lindahl return IIO_VAL_INT; 112054667612SMårten Lindahl } 112154667612SMårten Lindahl 112254667612SMårten Lindahl static int vcnl4040_write_event(struct iio_dev *indio_dev, 112354667612SMårten Lindahl const struct iio_chan_spec *chan, 112454667612SMårten Lindahl enum iio_event_type type, 112554667612SMårten Lindahl enum iio_event_direction dir, 112654667612SMårten Lindahl enum iio_event_info info, 112754667612SMårten Lindahl int val, int val2) 112854667612SMårten Lindahl { 112954667612SMårten Lindahl int ret; 113054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 113154667612SMårten Lindahl 11322be17b68SAstrid Rost switch (chan->type) { 1133bc292aafSAstrid Rost case IIO_LIGHT: 1134bc292aafSAstrid Rost switch (info) { 1135*7f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 1136*7f865127SAstrid Rost return vcnl4040_write_als_period(data, val, val2); 1137bc292aafSAstrid Rost case IIO_EV_INFO_VALUE: 1138bc292aafSAstrid Rost switch (dir) { 1139bc292aafSAstrid Rost case IIO_EV_DIR_RISING: 1140bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 1141bc292aafSAstrid Rost VCNL4040_ALS_THDH_LM, 1142bc292aafSAstrid Rost val); 1143bc292aafSAstrid Rost break; 1144bc292aafSAstrid Rost case IIO_EV_DIR_FALLING: 1145bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 1146bc292aafSAstrid Rost VCNL4040_ALS_THDL_LM, 1147bc292aafSAstrid Rost val); 1148bc292aafSAstrid Rost break; 1149bc292aafSAstrid Rost default: 1150bc292aafSAstrid Rost return -EINVAL; 1151bc292aafSAstrid Rost } 1152bc292aafSAstrid Rost break; 1153bc292aafSAstrid Rost default: 1154bc292aafSAstrid Rost return -EINVAL; 1155bc292aafSAstrid Rost } 1156bc292aafSAstrid Rost break; 11572be17b68SAstrid Rost case IIO_PROXIMITY: 11582be17b68SAstrid Rost switch (info) { 1159*7f865127SAstrid Rost case IIO_EV_INFO_PERIOD: 1160*7f865127SAstrid Rost return vcnl4040_write_ps_period(data, val, val2); 11612be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 116254667612SMårten Lindahl switch (dir) { 116354667612SMårten Lindahl case IIO_EV_DIR_RISING: 116454667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 11652be17b68SAstrid Rost VCNL4040_PS_THDH_LM, 11662be17b68SAstrid Rost val); 11672be17b68SAstrid Rost break; 116854667612SMårten Lindahl case IIO_EV_DIR_FALLING: 116954667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 11702be17b68SAstrid Rost VCNL4040_PS_THDL_LM, 11712be17b68SAstrid Rost val); 11722be17b68SAstrid Rost break; 117354667612SMårten Lindahl default: 117454667612SMårten Lindahl return -EINVAL; 117554667612SMårten Lindahl } 11762be17b68SAstrid Rost break; 11772be17b68SAstrid Rost default: 11782be17b68SAstrid Rost return -EINVAL; 11792be17b68SAstrid Rost } 11802be17b68SAstrid Rost break; 11812be17b68SAstrid Rost default: 11822be17b68SAstrid Rost return -EINVAL; 11832be17b68SAstrid Rost } 11842be17b68SAstrid Rost if (ret < 0) 11852be17b68SAstrid Rost return ret; 11862be17b68SAstrid Rost return IIO_VAL_INT; 118754667612SMårten Lindahl } 118854667612SMårten Lindahl 1189d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 1190d35567fcSMathieu Othacehe { 1191d35567fcSMathieu Othacehe int ret; 1192d35567fcSMathieu Othacehe 1193d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 1194d35567fcSMathieu Othacehe if (ret < 0) 1195d35567fcSMathieu Othacehe return false; 1196d35567fcSMathieu Othacehe 1197d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 1198d35567fcSMathieu Othacehe } 1199d35567fcSMathieu Othacehe 1200d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 1201d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1202d35567fcSMathieu Othacehe enum iio_event_type type, 1203d35567fcSMathieu Othacehe enum iio_event_direction dir) 1204d35567fcSMathieu Othacehe { 1205d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1206d35567fcSMathieu Othacehe 1207d35567fcSMathieu Othacehe switch (chan->type) { 1208d35567fcSMathieu Othacehe case IIO_PROXIMITY: 1209d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 1210d35567fcSMathieu Othacehe default: 1211d35567fcSMathieu Othacehe return -EINVAL; 1212d35567fcSMathieu Othacehe } 1213d35567fcSMathieu Othacehe } 1214d35567fcSMathieu Othacehe 1215d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 1216d35567fcSMathieu Othacehe { 1217d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1218d35567fcSMathieu Othacehe int ret; 1219d35567fcSMathieu Othacehe int icr; 1220d35567fcSMathieu Othacehe int command; 1221d35567fcSMathieu Othacehe 1222d35567fcSMathieu Othacehe if (state) { 1223d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 1224d35567fcSMathieu Othacehe if (ret) 1225d35567fcSMathieu Othacehe return ret; 1226d35567fcSMathieu Othacehe 1227d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 1228d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 1229d35567fcSMathieu Othacehe 1230d35567fcSMathieu Othacehe /* 1231d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 1232d35567fcSMathieu Othacehe * default. 1233d35567fcSMathieu Othacehe */ 1234d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 1235d35567fcSMathieu Othacehe } else { 1236d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 1237d35567fcSMathieu Othacehe return 0; 1238d35567fcSMathieu Othacehe 1239d35567fcSMathieu Othacehe command = 0; 1240d35567fcSMathieu Othacehe icr = 0; 1241d35567fcSMathieu Othacehe } 1242d35567fcSMathieu Othacehe 1243d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 1244d35567fcSMathieu Othacehe command); 1245d35567fcSMathieu Othacehe if (ret < 0) 1246d35567fcSMathieu Othacehe goto end; 1247d35567fcSMathieu Othacehe 1248d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 1249d35567fcSMathieu Othacehe 1250d35567fcSMathieu Othacehe end: 1251d35567fcSMathieu Othacehe if (state) 1252d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 1253d35567fcSMathieu Othacehe 1254d35567fcSMathieu Othacehe return ret; 1255d35567fcSMathieu Othacehe } 1256d35567fcSMathieu Othacehe 1257d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 1258d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1259d35567fcSMathieu Othacehe enum iio_event_type type, 1260d35567fcSMathieu Othacehe enum iio_event_direction dir, 1261d35567fcSMathieu Othacehe int state) 1262d35567fcSMathieu Othacehe { 1263d35567fcSMathieu Othacehe switch (chan->type) { 1264d35567fcSMathieu Othacehe case IIO_PROXIMITY: 1265d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 1266d35567fcSMathieu Othacehe default: 1267d35567fcSMathieu Othacehe return -EINVAL; 1268d35567fcSMathieu Othacehe } 1269d35567fcSMathieu Othacehe } 1270d35567fcSMathieu Othacehe 127154667612SMårten Lindahl static int vcnl4040_read_event_config(struct iio_dev *indio_dev, 127254667612SMårten Lindahl const struct iio_chan_spec *chan, 127354667612SMårten Lindahl enum iio_event_type type, 127454667612SMårten Lindahl enum iio_event_direction dir) 127554667612SMårten Lindahl { 127654667612SMårten Lindahl int ret; 127754667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 127854667612SMårten Lindahl 12792be17b68SAstrid Rost switch (chan->type) { 1280bc292aafSAstrid Rost case IIO_LIGHT: 1281bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 1282bc292aafSAstrid Rost if (ret < 0) 1283bc292aafSAstrid Rost return ret; 1284bc292aafSAstrid Rost 1285bc292aafSAstrid Rost data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret); 1286bc292aafSAstrid Rost 1287bc292aafSAstrid Rost return data->als_int; 12882be17b68SAstrid Rost case IIO_PROXIMITY: 128954667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 129054667612SMårten Lindahl if (ret < 0) 129154667612SMårten Lindahl return ret; 129254667612SMårten Lindahl 129354667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); 129454667612SMårten Lindahl 129554667612SMårten Lindahl return (dir == IIO_EV_DIR_RISING) ? 129654667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : 129754667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); 12982be17b68SAstrid Rost default: 12992be17b68SAstrid Rost return -EINVAL; 13002be17b68SAstrid Rost } 130154667612SMårten Lindahl } 130254667612SMårten Lindahl 130354667612SMårten Lindahl static int vcnl4040_write_event_config(struct iio_dev *indio_dev, 130454667612SMårten Lindahl const struct iio_chan_spec *chan, 130554667612SMårten Lindahl enum iio_event_type type, 130654667612SMårten Lindahl enum iio_event_direction dir, int state) 130754667612SMårten Lindahl { 13082be17b68SAstrid Rost int ret = -EINVAL; 130954667612SMårten Lindahl u16 val, mask; 131054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 131154667612SMårten Lindahl 131254667612SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 131354667612SMårten Lindahl 13142be17b68SAstrid Rost switch (chan->type) { 1315bc292aafSAstrid Rost case IIO_LIGHT: 1316bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 1317bc292aafSAstrid Rost if (ret < 0) 1318bc292aafSAstrid Rost goto out; 1319bc292aafSAstrid Rost 1320bc292aafSAstrid Rost mask = VCNL4040_ALS_CONF_INT_EN; 1321bc292aafSAstrid Rost if (state) 1322bc292aafSAstrid Rost val = (ret | mask); 1323bc292aafSAstrid Rost else 1324bc292aafSAstrid Rost val = (ret & ~mask); 1325bc292aafSAstrid Rost 1326bc292aafSAstrid Rost data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val); 1327bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 1328bc292aafSAstrid Rost val); 1329bc292aafSAstrid Rost break; 13302be17b68SAstrid Rost case IIO_PROXIMITY: 133154667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 133254667612SMårten Lindahl if (ret < 0) 133354667612SMårten Lindahl goto out; 133454667612SMårten Lindahl 133554667612SMårten Lindahl if (dir == IIO_EV_DIR_RISING) 133654667612SMårten Lindahl mask = VCNL4040_PS_IF_AWAY; 133754667612SMårten Lindahl else 133854667612SMårten Lindahl mask = VCNL4040_PS_IF_CLOSE; 133954667612SMårten Lindahl 134054667612SMårten Lindahl val = state ? (ret | mask) : (ret & ~mask); 134154667612SMårten Lindahl 134254667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); 13432be17b68SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 13442be17b68SAstrid Rost val); 13452be17b68SAstrid Rost break; 13462be17b68SAstrid Rost default: 13472be17b68SAstrid Rost break; 13482be17b68SAstrid Rost } 134954667612SMårten Lindahl 135054667612SMårten Lindahl out: 135154667612SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 1352bc292aafSAstrid Rost data->chip_spec->set_power_state(data, data->ps_int || data->als_int); 135354667612SMårten Lindahl 135454667612SMårten Lindahl return ret; 135554667612SMårten Lindahl } 135654667612SMårten Lindahl 135754667612SMårten Lindahl static irqreturn_t vcnl4040_irq_thread(int irq, void *p) 135854667612SMårten Lindahl { 135954667612SMårten Lindahl struct iio_dev *indio_dev = p; 136054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 136154667612SMårten Lindahl int ret; 136254667612SMårten Lindahl 1363854965b7SAstrid Rost ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg); 136454667612SMårten Lindahl if (ret < 0) 136554667612SMårten Lindahl return IRQ_HANDLED; 136654667612SMårten Lindahl 136754667612SMårten Lindahl if (ret & VCNL4040_PS_IF_CLOSE) { 136854667612SMårten Lindahl iio_push_event(indio_dev, 136954667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 137054667612SMårten Lindahl IIO_EV_TYPE_THRESH, 137154667612SMårten Lindahl IIO_EV_DIR_RISING), 137254667612SMårten Lindahl iio_get_time_ns(indio_dev)); 137354667612SMårten Lindahl } 137454667612SMårten Lindahl 137554667612SMårten Lindahl if (ret & VCNL4040_PS_IF_AWAY) { 137654667612SMårten Lindahl iio_push_event(indio_dev, 137754667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 137854667612SMårten Lindahl IIO_EV_TYPE_THRESH, 137954667612SMårten Lindahl IIO_EV_DIR_FALLING), 138054667612SMårten Lindahl iio_get_time_ns(indio_dev)); 138154667612SMårten Lindahl } 138254667612SMårten Lindahl 1383bc292aafSAstrid Rost if (ret & VCNL4040_ALS_FALLING) { 1384bc292aafSAstrid Rost iio_push_event(indio_dev, 1385bc292aafSAstrid Rost IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 1386bc292aafSAstrid Rost IIO_EV_TYPE_THRESH, 1387bc292aafSAstrid Rost IIO_EV_DIR_FALLING), 1388bc292aafSAstrid Rost iio_get_time_ns(indio_dev)); 1389bc292aafSAstrid Rost } 1390bc292aafSAstrid Rost 1391bc292aafSAstrid Rost if (ret & VCNL4040_ALS_RISING) { 1392bc292aafSAstrid Rost iio_push_event(indio_dev, 1393bc292aafSAstrid Rost IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 1394bc292aafSAstrid Rost IIO_EV_TYPE_THRESH, 1395bc292aafSAstrid Rost IIO_EV_DIR_RISING), 1396bc292aafSAstrid Rost iio_get_time_ns(indio_dev)); 1397bc292aafSAstrid Rost } 1398bc292aafSAstrid Rost 139954667612SMårten Lindahl return IRQ_HANDLED; 140054667612SMårten Lindahl } 140154667612SMårten Lindahl 1402d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 1403d35567fcSMathieu Othacehe uintptr_t priv, 1404d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1405d35567fcSMathieu Othacehe char *buf) 1406d35567fcSMathieu Othacehe { 1407d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1408d35567fcSMathieu Othacehe 1409d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 1410d35567fcSMathieu Othacehe } 1411d35567fcSMathieu Othacehe 14123a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 14133a52d32aSMårten Lindahl { 14143a52d32aSMårten Lindahl struct iio_dev *indio_dev = p; 14153a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 14163a52d32aSMårten Lindahl unsigned long isr; 14173a52d32aSMårten Lindahl int ret; 14183a52d32aSMårten Lindahl 14193a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 14203a52d32aSMårten Lindahl if (ret < 0) 14213a52d32aSMårten Lindahl goto end; 14223a52d32aSMårten Lindahl 14233a52d32aSMårten Lindahl isr = ret; 14243a52d32aSMårten Lindahl 14253a52d32aSMårten Lindahl if (isr & VCNL4010_INT_THR) { 14263a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 14273a52d32aSMårten Lindahl iio_push_event(indio_dev, 14283a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 14293a52d32aSMårten Lindahl IIO_PROXIMITY, 14303a52d32aSMårten Lindahl 1, 14313a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 14323a52d32aSMårten Lindahl IIO_EV_DIR_FALLING), 14333a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 14343a52d32aSMårten Lindahl } 14353a52d32aSMårten Lindahl 14363a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 14373a52d32aSMårten Lindahl iio_push_event(indio_dev, 14383a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 14393a52d32aSMårten Lindahl IIO_PROXIMITY, 14403a52d32aSMårten Lindahl 1, 14413a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 14423a52d32aSMårten Lindahl IIO_EV_DIR_RISING), 14433a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 14443a52d32aSMårten Lindahl } 14453a52d32aSMårten Lindahl 14463a52d32aSMårten Lindahl i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 14473a52d32aSMårten Lindahl isr & VCNL4010_INT_THR); 14483a52d32aSMårten Lindahl } 14493a52d32aSMårten Lindahl 14503a52d32aSMårten Lindahl if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 1451f700e55eSMehdi Djait iio_trigger_poll_nested(indio_dev->trig); 14523a52d32aSMårten Lindahl 14533a52d32aSMårten Lindahl end: 14543a52d32aSMårten Lindahl return IRQ_HANDLED; 14553a52d32aSMårten Lindahl } 14563a52d32aSMårten Lindahl 14573a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 14583a52d32aSMårten Lindahl { 14593a52d32aSMårten Lindahl struct iio_poll_func *pf = p; 14603a52d32aSMårten Lindahl struct iio_dev *indio_dev = pf->indio_dev; 14613a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 14623a52d32aSMårten Lindahl const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 14633a52d32aSMårten Lindahl u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 14643a52d32aSMårten Lindahl bool data_read = false; 14653a52d32aSMårten Lindahl unsigned long isr; 14663a52d32aSMårten Lindahl int val = 0; 14673a52d32aSMårten Lindahl int ret; 14683a52d32aSMårten Lindahl 14693a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 14703a52d32aSMårten Lindahl if (ret < 0) 14713a52d32aSMårten Lindahl goto end; 14723a52d32aSMårten Lindahl 14733a52d32aSMårten Lindahl isr = ret; 14743a52d32aSMårten Lindahl 14753a52d32aSMårten Lindahl if (test_bit(0, active_scan_mask)) { 14763a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 14773a52d32aSMårten Lindahl ret = vcnl4000_read_data(data, 14783a52d32aSMårten Lindahl VCNL4000_PS_RESULT_HI, 14793a52d32aSMårten Lindahl &val); 14803a52d32aSMårten Lindahl if (ret < 0) 14813a52d32aSMårten Lindahl goto end; 14823a52d32aSMårten Lindahl 14833a52d32aSMårten Lindahl buffer[0] = val; 14843a52d32aSMårten Lindahl data_read = true; 14853a52d32aSMårten Lindahl } 14863a52d32aSMårten Lindahl } 14873a52d32aSMårten Lindahl 14883a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 14893a52d32aSMårten Lindahl isr & VCNL4010_INT_DRDY); 14903a52d32aSMårten Lindahl if (ret < 0) 14913a52d32aSMårten Lindahl goto end; 14923a52d32aSMårten Lindahl 14933a52d32aSMårten Lindahl if (!data_read) 14943a52d32aSMårten Lindahl goto end; 14953a52d32aSMårten Lindahl 14963a52d32aSMårten Lindahl iio_push_to_buffers_with_timestamp(indio_dev, buffer, 14973a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 14983a52d32aSMårten Lindahl 14993a52d32aSMårten Lindahl end: 15003a52d32aSMårten Lindahl iio_trigger_notify_done(indio_dev->trig); 15013a52d32aSMårten Lindahl return IRQ_HANDLED; 15023a52d32aSMårten Lindahl } 15033a52d32aSMårten Lindahl 15043a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 15053a52d32aSMårten Lindahl { 15063a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 15073a52d32aSMårten Lindahl int ret; 15083a52d32aSMårten Lindahl int cmd; 15093a52d32aSMårten Lindahl 15103a52d32aSMårten Lindahl /* Do not enable the buffer if we are already capturing events. */ 15113a52d32aSMårten Lindahl if (vcnl4010_is_in_periodic_mode(data)) 15123a52d32aSMårten Lindahl return -EBUSY; 15133a52d32aSMårten Lindahl 15143a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 15153a52d32aSMårten Lindahl VCNL4010_INT_PROX_EN); 15163a52d32aSMårten Lindahl if (ret < 0) 15173a52d32aSMårten Lindahl return ret; 15183a52d32aSMårten Lindahl 15193a52d32aSMårten Lindahl cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 15203a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 15213a52d32aSMårten Lindahl } 15223a52d32aSMårten Lindahl 15233a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 15243a52d32aSMårten Lindahl { 15253a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 15263a52d32aSMårten Lindahl int ret; 15273a52d32aSMårten Lindahl 15283a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 15293a52d32aSMårten Lindahl if (ret < 0) 15303a52d32aSMårten Lindahl return ret; 15313a52d32aSMårten Lindahl 15323a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 15333a52d32aSMårten Lindahl } 15343a52d32aSMårten Lindahl 15353a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 15363a52d32aSMårten Lindahl .postenable = &vcnl4010_buffer_postenable, 15373a52d32aSMårten Lindahl .predisable = &vcnl4010_buffer_predisable, 15383a52d32aSMårten Lindahl }; 15393a52d32aSMårten Lindahl 1540d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 1541d35567fcSMathieu Othacehe { 1542d35567fcSMathieu Othacehe .name = "nearlevel", 1543d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 1544d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 1545d35567fcSMathieu Othacehe }, 1546d35567fcSMathieu Othacehe { /* sentinel */ } 1547d35567fcSMathieu Othacehe }; 1548d35567fcSMathieu Othacehe 1549d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 1550d35567fcSMathieu Othacehe { 1551d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1552d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 1553d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1554d35567fcSMathieu Othacehe }, { 1555d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1556d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 1557d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1558d35567fcSMathieu Othacehe }, { 1559d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1560d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 1561d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 1562d35567fcSMathieu Othacehe } 1563d35567fcSMathieu Othacehe }; 1564d35567fcSMathieu Othacehe 1565*7f865127SAstrid Rost static const struct iio_event_spec vcnl4040_als_event_spec[] = { 1566*7f865127SAstrid Rost { 1567*7f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 1568*7f865127SAstrid Rost .dir = IIO_EV_DIR_RISING, 1569*7f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_VALUE), 1570*7f865127SAstrid Rost }, { 1571*7f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 1572*7f865127SAstrid Rost .dir = IIO_EV_DIR_FALLING, 1573*7f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_VALUE), 1574*7f865127SAstrid Rost }, { 1575*7f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 1576*7f865127SAstrid Rost .dir = IIO_EV_DIR_EITHER, 1577*7f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD), 1578*7f865127SAstrid Rost }, 1579*7f865127SAstrid Rost }; 1580*7f865127SAstrid Rost 158154667612SMårten Lindahl static const struct iio_event_spec vcnl4040_event_spec[] = { 158254667612SMårten Lindahl { 158354667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 158454667612SMårten Lindahl .dir = IIO_EV_DIR_RISING, 158554667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 158654667612SMårten Lindahl }, { 158754667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 158854667612SMårten Lindahl .dir = IIO_EV_DIR_FALLING, 158954667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 1590*7f865127SAstrid Rost }, { 1591*7f865127SAstrid Rost .type = IIO_EV_TYPE_THRESH, 1592*7f865127SAstrid Rost .dir = IIO_EV_DIR_EITHER, 1593*7f865127SAstrid Rost .mask_separate = BIT(IIO_EV_INFO_PERIOD), 159454667612SMårten Lindahl }, 159554667612SMårten Lindahl }; 159654667612SMårten Lindahl 1597d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 1598d35567fcSMathieu Othacehe { 1599d35567fcSMathieu Othacehe .type = IIO_LIGHT, 1600d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1601d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1602d35567fcSMathieu Othacehe }, { 1603d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 1604d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1605d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 1606d35567fcSMathieu Othacehe } 1607d35567fcSMathieu Othacehe }; 1608d35567fcSMathieu Othacehe 1609d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 1610d35567fcSMathieu Othacehe { 1611d35567fcSMathieu Othacehe .type = IIO_LIGHT, 16128fe78d52SMathieu Othacehe .scan_index = -1, 1613d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1614d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1615d35567fcSMathieu Othacehe }, { 1616d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 16178fe78d52SMathieu Othacehe .scan_index = 0, 1618f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1619f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 1620f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1621d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 1622d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 1623d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 16248fe78d52SMathieu Othacehe .scan_type = { 16258fe78d52SMathieu Othacehe .sign = 'u', 16268fe78d52SMathieu Othacehe .realbits = 16, 16278fe78d52SMathieu Othacehe .storagebits = 16, 16288fe78d52SMathieu Othacehe .endianness = IIO_CPU, 1629d35567fcSMathieu Othacehe }, 16308fe78d52SMathieu Othacehe }, 16318fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 1632d35567fcSMathieu Othacehe }; 1633d35567fcSMathieu Othacehe 163485e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = { 163585e2c6a2SMårten Lindahl { 163685e2c6a2SMårten Lindahl .type = IIO_LIGHT, 163785e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1638fea2c97dSAstrid Rost BIT(IIO_CHAN_INFO_SCALE) | 1639fea2c97dSAstrid Rost BIT(IIO_CHAN_INFO_INT_TIME), 1640fea2c97dSAstrid Rost .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 1641*7f865127SAstrid Rost .event_spec = vcnl4040_als_event_spec, 1642*7f865127SAstrid Rost .num_event_specs = ARRAY_SIZE(vcnl4040_als_event_spec), 164385e2c6a2SMårten Lindahl }, { 164485e2c6a2SMårten Lindahl .type = IIO_PROXIMITY, 164585e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 164685e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_INT_TIME), 164785e2c6a2SMårten Lindahl .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 164885e2c6a2SMårten Lindahl .ext_info = vcnl4000_ext_info, 164954667612SMårten Lindahl .event_spec = vcnl4040_event_spec, 165054667612SMårten Lindahl .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), 165185e2c6a2SMårten Lindahl } 165285e2c6a2SMårten Lindahl }; 165385e2c6a2SMårten Lindahl 165462a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 165562a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 165662a1efb9SPeter Meerwald }; 165762a1efb9SPeter Meerwald 1658d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 1659d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 1660f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 1661f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 1662d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 1663d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 1664d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 1665d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 1666d35567fcSMathieu Othacehe }; 1667d35567fcSMathieu Othacehe 166885e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = { 166985e2c6a2SMårten Lindahl .read_raw = vcnl4000_read_raw, 167085e2c6a2SMårten Lindahl .write_raw = vcnl4040_write_raw, 167154667612SMårten Lindahl .read_event_value = vcnl4040_read_event, 167254667612SMårten Lindahl .write_event_value = vcnl4040_write_event, 167354667612SMårten Lindahl .read_event_config = vcnl4040_read_event_config, 167454667612SMårten Lindahl .write_event_config = vcnl4040_write_event_config, 167585e2c6a2SMårten Lindahl .read_avail = vcnl4040_read_avail, 167685e2c6a2SMårten Lindahl }; 167785e2c6a2SMårten Lindahl 1678d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 1679d35567fcSMathieu Othacehe [VCNL4000] = { 1680d35567fcSMathieu Othacehe .prod = "VCNL4000", 1681d35567fcSMathieu Othacehe .init = vcnl4000_init, 1682d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1683d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1684d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1685d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1686d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1687d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1688d35567fcSMathieu Othacehe }, 1689d35567fcSMathieu Othacehe [VCNL4010] = { 1690d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 1691d35567fcSMathieu Othacehe .init = vcnl4000_init, 1692d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1693d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1694d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1695d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 1696d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 1697d35567fcSMathieu Othacehe .info = &vcnl4010_info, 1698bfb6cfeeSMårten Lindahl .irq_thread = vcnl4010_irq_thread, 1699bfb6cfeeSMårten Lindahl .trig_buffer_func = vcnl4010_trigger_handler, 1700bfb6cfeeSMårten Lindahl .buffer_setup_ops = &vcnl4010_buffer_ops, 1701d35567fcSMathieu Othacehe }, 1702d35567fcSMathieu Othacehe [VCNL4040] = { 1703d35567fcSMathieu Othacehe .prod = "VCNL4040", 1704d35567fcSMathieu Othacehe .init = vcnl4200_init, 1705d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1706d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1707d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 170885e2c6a2SMårten Lindahl .channels = vcnl4040_channels, 170985e2c6a2SMårten Lindahl .num_channels = ARRAY_SIZE(vcnl4040_channels), 171085e2c6a2SMårten Lindahl .info = &vcnl4040_info, 171154667612SMårten Lindahl .irq_thread = vcnl4040_irq_thread, 1712854965b7SAstrid Rost .int_reg = VCNL4040_INT_FLAGS, 1713e55c96daSAstrid Rost .ps_it_times = &vcnl4040_ps_it_times, 1714e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), 1715fea2c97dSAstrid Rost .als_it_times = &vcnl4040_als_it_times, 1716fea2c97dSAstrid Rost .num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times), 1717fea2c97dSAstrid Rost .ulux_step = 100000, 1718d35567fcSMathieu Othacehe }, 1719d35567fcSMathieu Othacehe [VCNL4200] = { 1720d35567fcSMathieu Othacehe .prod = "VCNL4200", 1721d35567fcSMathieu Othacehe .init = vcnl4200_init, 1722d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1723d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1724d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1725854965b7SAstrid Rost .channels = vcnl4040_channels, 1726d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1727854965b7SAstrid Rost .info = &vcnl4040_info, 1728854965b7SAstrid Rost .irq_thread = vcnl4040_irq_thread, 1729854965b7SAstrid Rost .int_reg = VCNL4200_INT_FLAGS, 1730e55c96daSAstrid Rost .ps_it_times = &vcnl4200_ps_it_times, 1731e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times), 1732fea2c97dSAstrid Rost .als_it_times = &vcnl4200_als_it_times, 1733fea2c97dSAstrid Rost .num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times), 1734fea2c97dSAstrid Rost .ulux_step = 24000, 1735d35567fcSMathieu Othacehe }, 1736d35567fcSMathieu Othacehe }; 1737d35567fcSMathieu Othacehe 17388fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 17398fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 17408fe78d52SMathieu Othacehe }; 17418fe78d52SMathieu Othacehe 17428fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 17438fe78d52SMathieu Othacehe { 17448fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 17458fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 17468fe78d52SMathieu Othacehe struct iio_trigger *trigger; 17478fe78d52SMathieu Othacehe 17488fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 174915ea2878SJonathan Cameron indio_dev->name, 175015ea2878SJonathan Cameron iio_device_id(indio_dev)); 17518fe78d52SMathieu Othacehe if (!trigger) 17528fe78d52SMathieu Othacehe return -ENOMEM; 17538fe78d52SMathieu Othacehe 17548fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 17558fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 17568fe78d52SMathieu Othacehe 17578fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 17588fe78d52SMathieu Othacehe } 17598fe78d52SMathieu Othacehe 1760e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client) 176162a1efb9SPeter Meerwald { 1762e61295e0SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client); 176362a1efb9SPeter Meerwald struct vcnl4000_data *data; 176462a1efb9SPeter Meerwald struct iio_dev *indio_dev; 17651ebc787aSTomas Novotny int ret; 176662a1efb9SPeter Meerwald 17672669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 176862a1efb9SPeter Meerwald if (!indio_dev) 176962a1efb9SPeter Meerwald return -ENOMEM; 177062a1efb9SPeter Meerwald 177162a1efb9SPeter Meerwald data = iio_priv(indio_dev); 177262a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 177362a1efb9SPeter Meerwald data->client = client; 17741ebc787aSTomas Novotny data->id = id->driver_data; 17751ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 177662a1efb9SPeter Meerwald 177742ec40b0SMårten Lindahl mutex_init(&data->vcnl4000_lock); 177842ec40b0SMårten Lindahl 17791ebc787aSTomas Novotny ret = data->chip_spec->init(data); 178062a1efb9SPeter Meerwald if (ret < 0) 17812669d723SPeter Meerwald return ret; 178262a1efb9SPeter Meerwald 1783d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 17841ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 178562a1efb9SPeter Meerwald 1786f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1787f5a98e1fSGuido Günther &data->near_level)) 1788f5a98e1fSGuido Günther data->near_level = 0; 1789f5a98e1fSGuido Günther 1790d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1791d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1792d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 179362a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 179462a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 179562a1efb9SPeter Meerwald 1796bfb6cfeeSMårten Lindahl if (data->chip_spec->trig_buffer_func && 1797bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops) { 17988fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 17998fe78d52SMathieu Othacehe NULL, 1800bfb6cfeeSMårten Lindahl data->chip_spec->trig_buffer_func, 1801bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops); 18028fe78d52SMathieu Othacehe if (ret < 0) { 18038fe78d52SMathieu Othacehe dev_err(&client->dev, 18048fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 18058fe78d52SMathieu Othacehe return ret; 18068fe78d52SMathieu Othacehe } 1807bfb6cfeeSMårten Lindahl } 18088fe78d52SMathieu Othacehe 1809bfb6cfeeSMårten Lindahl if (client->irq && data->chip_spec->irq_thread) { 1810d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1811bfb6cfeeSMårten Lindahl NULL, data->chip_spec->irq_thread, 1812d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1813d35567fcSMathieu Othacehe IRQF_ONESHOT, 1814bfb6cfeeSMårten Lindahl "vcnl4000_irq", 1815d35567fcSMathieu Othacehe indio_dev); 1816d35567fcSMathieu Othacehe if (ret < 0) { 1817d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1818d35567fcSMathieu Othacehe return ret; 1819d35567fcSMathieu Othacehe } 18208fe78d52SMathieu Othacehe 18218fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 18228fe78d52SMathieu Othacehe if (ret < 0) 18238fe78d52SMathieu Othacehe return ret; 1824d35567fcSMathieu Othacehe } 1825d35567fcSMathieu Othacehe 18265e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 18275e00708dSGuido Günther if (ret < 0) 18285e00708dSGuido Günther goto fail_poweroff; 18295e00708dSGuido Günther 18305e00708dSGuido Günther ret = iio_device_register(indio_dev); 18315e00708dSGuido Günther if (ret < 0) 18325e00708dSGuido Günther goto fail_poweroff; 18335e00708dSGuido Günther 18345e00708dSGuido Günther pm_runtime_enable(&client->dev); 18355e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 18365e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 18375e00708dSGuido Günther 18385e00708dSGuido Günther return 0; 18395e00708dSGuido Günther fail_poweroff: 18405e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 18415e00708dSGuido Günther return ret; 184262a1efb9SPeter Meerwald } 184362a1efb9SPeter Meerwald 1844ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1845ebd457d5SAngus Ainslie (Purism) { 1846ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 18471436a78cSMarco Felsch .data = (void *)VCNL4000, 1848ebd457d5SAngus Ainslie (Purism) }, 1849ebd457d5SAngus Ainslie (Purism) { 1850ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 18511436a78cSMarco Felsch .data = (void *)VCNL4010, 1852ebd457d5SAngus Ainslie (Purism) }, 1853ebd457d5SAngus Ainslie (Purism) { 18541436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 18551436a78cSMarco Felsch .data = (void *)VCNL4010, 1856ebd457d5SAngus Ainslie (Purism) }, 1857ebd457d5SAngus Ainslie (Purism) { 18587fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 18597fd1c260SMarco Felsch .data = (void *)VCNL4040, 18607fd1c260SMarco Felsch }, 18617fd1c260SMarco Felsch { 1862ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 18631436a78cSMarco Felsch .data = (void *)VCNL4200, 1864ebd457d5SAngus Ainslie (Purism) }, 1865ebd457d5SAngus Ainslie (Purism) {}, 1866ebd457d5SAngus Ainslie (Purism) }; 1867ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1868ebd457d5SAngus Ainslie (Purism) 1869ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 18705e00708dSGuido Günther { 18715e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 18725e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 1873ab91da2fSUwe Kleine-König int ret; 18745e00708dSGuido Günther 18755e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 18765e00708dSGuido Günther pm_runtime_disable(&client->dev); 18775e00708dSGuido Günther iio_device_unregister(indio_dev); 18785e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 18795e00708dSGuido Günther 1880ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 1881ab91da2fSUwe Kleine-König if (ret) 1882ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 1883ab91da2fSUwe Kleine-König ERR_PTR(ret)); 18845e00708dSGuido Günther } 18855e00708dSGuido Günther 1886cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 18875e00708dSGuido Günther { 18885e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 18895e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 18905e00708dSGuido Günther 18915e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 18925e00708dSGuido Günther } 18935e00708dSGuido Günther 1894cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 18955e00708dSGuido Günther { 18965e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 18975e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 18985e00708dSGuido Günther 18995e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 19005e00708dSGuido Günther } 19015e00708dSGuido Günther 1902cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 1903cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 19045e00708dSGuido Günther 190562a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 190662a1efb9SPeter Meerwald .driver = { 190762a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 1908cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 1909ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 191062a1efb9SPeter Meerwald }, 19117cf15f42SUwe Kleine-König .probe = vcnl4000_probe, 191262a1efb9SPeter Meerwald .id_table = vcnl4000_id, 19135e00708dSGuido Günther .remove = vcnl4000_remove, 191462a1efb9SPeter Meerwald }; 191562a1efb9SPeter Meerwald 191662a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 191762a1efb9SPeter Meerwald 191862a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 19198fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 192062a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 192162a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 1922