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> 2762a1efb9SPeter Meerwald 288fe78d52SMathieu Othacehe #include <linux/iio/buffer.h> 29d35567fcSMathieu Othacehe #include <linux/iio/events.h> 3062a1efb9SPeter Meerwald #include <linux/iio/iio.h> 3162a1efb9SPeter Meerwald #include <linux/iio/sysfs.h> 328fe78d52SMathieu Othacehe #include <linux/iio/trigger.h> 338fe78d52SMathieu Othacehe #include <linux/iio/trigger_consumer.h> 348fe78d52SMathieu Othacehe #include <linux/iio/triggered_buffer.h> 3562a1efb9SPeter Meerwald 3662a1efb9SPeter Meerwald #define VCNL4000_DRV_NAME "vcnl4000" 371ebc787aSTomas Novotny #define VCNL4000_PROD_ID 0x01 381ebc787aSTomas Novotny #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ 395a441aadSAngus Ainslie (Purism) #define VCNL4040_PROD_ID 0x86 40be38866fSTomas Novotny #define VCNL4200_PROD_ID 0x58 4162a1efb9SPeter Meerwald 4262a1efb9SPeter Meerwald #define VCNL4000_COMMAND 0x80 /* Command register */ 4362a1efb9SPeter Meerwald #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 44d35567fcSMathieu Othacehe #define VCNL4010_PROX_RATE 0x82 /* Proximity rate */ 4562a1efb9SPeter Meerwald #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 4662a1efb9SPeter Meerwald #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 47d35567fcSMathieu Othacehe #define VCNL4010_ALS_PARAM 0x84 /* ALS rate */ 4862a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 4962a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 5062a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 5162a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 5262a1efb9SPeter Meerwald #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 53d35567fcSMathieu Othacehe #define VCNL4010_INT_CTRL 0x89 /* Interrupt control */ 5462a1efb9SPeter Meerwald #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 55d35567fcSMathieu Othacehe #define VCNL4010_LOW_THR_HI 0x8a /* Low threshold, MSB */ 56d35567fcSMathieu Othacehe #define VCNL4010_LOW_THR_LO 0x8b /* Low threshold, LSB */ 57d35567fcSMathieu Othacehe #define VCNL4010_HIGH_THR_HI 0x8c /* High threshold, MSB */ 58d35567fcSMathieu Othacehe #define VCNL4010_HIGH_THR_LO 0x8d /* High threshold, LSB */ 59d35567fcSMathieu Othacehe #define VCNL4010_ISR 0x8e /* Interrupt status */ 6062a1efb9SPeter Meerwald 61be38866fSTomas Novotny #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ 62be38866fSTomas Novotny #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ 63be38866fSTomas Novotny #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 64be38866fSTomas Novotny #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 65be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 66be38866fSTomas Novotny 675a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 685a441aadSAngus Ainslie (Purism) 6962a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 70ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 71ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 72ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 73ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 74d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 75d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 76d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 77d35567fcSMathieu Othacehe 78e21b5b1fSMårten Lindahl #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) 79e21b5b1fSMårten Lindahl #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) 8085e2c6a2SMårten Lindahl #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ 81e21b5b1fSMårten Lindahl 82d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 83d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 84d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 85d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 86d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 87d35567fcSMathieu Othacehe 88d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 89d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 90d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 91d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 92d35567fcSMathieu Othacehe 93d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 94d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 95d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 96d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 97d35567fcSMathieu Othacehe 98f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 99f6889c1bSMathieu Othacehe {1, 950000}, 100f6889c1bSMathieu Othacehe {3, 906250}, 101f6889c1bSMathieu Othacehe {7, 812500}, 102f6889c1bSMathieu Othacehe {16, 625000}, 103f6889c1bSMathieu Othacehe {31, 250000}, 104f6889c1bSMathieu Othacehe {62, 500000}, 105f6889c1bSMathieu Othacehe {125, 0}, 106f6889c1bSMathieu Othacehe {250, 0}, 107f6889c1bSMathieu Othacehe }; 10862a1efb9SPeter Meerwald 10985e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = { 11085e2c6a2SMårten Lindahl {0, 100}, 11185e2c6a2SMårten Lindahl {0, 150}, 11285e2c6a2SMårten Lindahl {0, 200}, 11385e2c6a2SMårten Lindahl {0, 250}, 11485e2c6a2SMårten Lindahl {0, 300}, 11585e2c6a2SMårten Lindahl {0, 350}, 11685e2c6a2SMårten Lindahl {0, 400}, 11785e2c6a2SMårten Lindahl {0, 800}, 11885e2c6a2SMårten Lindahl }; 11985e2c6a2SMårten Lindahl 1205e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1215e00708dSGuido Günther 1221ebc787aSTomas Novotny enum vcnl4000_device_ids { 1231ebc787aSTomas Novotny VCNL4000, 12450c50b97STomas Novotny VCNL4010, 1255a441aadSAngus Ainslie (Purism) VCNL4040, 126be38866fSTomas Novotny VCNL4200, 127be38866fSTomas Novotny }; 128be38866fSTomas Novotny 129be38866fSTomas Novotny struct vcnl4200_channel { 130be38866fSTomas Novotny u8 reg; 131be38866fSTomas Novotny ktime_t last_measurement; 132be38866fSTomas Novotny ktime_t sampling_rate; 133be38866fSTomas Novotny struct mutex lock; 1341ebc787aSTomas Novotny }; 1351ebc787aSTomas Novotny 13662a1efb9SPeter Meerwald struct vcnl4000_data { 13762a1efb9SPeter Meerwald struct i2c_client *client; 1381ebc787aSTomas Novotny enum vcnl4000_device_ids id; 1391ebc787aSTomas Novotny int rev; 1401ebc787aSTomas Novotny int al_scale; 1411ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 142be38866fSTomas Novotny struct mutex vcnl4000_lock; 143be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 144be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 145f5a98e1fSGuido Günther uint32_t near_level; 14662a1efb9SPeter Meerwald }; 14762a1efb9SPeter Meerwald 1481ebc787aSTomas Novotny struct vcnl4000_chip_spec { 1491ebc787aSTomas Novotny const char *prod; 150d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 151d35567fcSMathieu Othacehe const int num_channels; 152d35567fcSMathieu Othacehe const struct iio_info *info; 153*bfb6cfeeSMårten Lindahl const struct iio_buffer_setup_ops *buffer_setup_ops; 1541ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 1551ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 1561ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 1575e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 158*bfb6cfeeSMårten Lindahl irqreturn_t (*irq_thread)(int irq, void *priv); 159*bfb6cfeeSMårten Lindahl irqreturn_t (*trig_buffer_func)(int irq, void *priv); 1601ebc787aSTomas Novotny }; 1611ebc787aSTomas Novotny 16262a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 1631ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 16450c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 16550c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 1665a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 167be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 16862a1efb9SPeter Meerwald { } 16962a1efb9SPeter Meerwald }; 17062a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 17162a1efb9SPeter Meerwald 1725e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 1735e00708dSGuido Günther { 1745e00708dSGuido Günther /* no suspend op */ 1755e00708dSGuido Günther return 0; 1765e00708dSGuido Günther } 1775e00708dSGuido Günther 1781ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 1791ebc787aSTomas Novotny { 1801ebc787aSTomas Novotny int ret, prod_id; 1811ebc787aSTomas Novotny 1821ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 1831ebc787aSTomas Novotny if (ret < 0) 1841ebc787aSTomas Novotny return ret; 1851ebc787aSTomas Novotny 1861ebc787aSTomas Novotny prod_id = ret >> 4; 18758bf9aceSTomas Novotny switch (prod_id) { 18858bf9aceSTomas Novotny case VCNL4000_PROD_ID: 18958bf9aceSTomas Novotny if (data->id != VCNL4000) 19058bf9aceSTomas Novotny dev_warn(&data->client->dev, 19158bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 19258bf9aceSTomas Novotny break; 19358bf9aceSTomas Novotny case VCNL4010_PROD_ID: 19458bf9aceSTomas Novotny if (data->id != VCNL4010) 19558bf9aceSTomas Novotny dev_warn(&data->client->dev, 19658bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 19758bf9aceSTomas Novotny break; 19858bf9aceSTomas Novotny default: 1991ebc787aSTomas Novotny return -ENODEV; 20058bf9aceSTomas Novotny } 2011ebc787aSTomas Novotny 2021ebc787aSTomas Novotny data->rev = ret & 0xf; 2031ebc787aSTomas Novotny data->al_scale = 250000; 204be38866fSTomas Novotny mutex_init(&data->vcnl4000_lock); 205be38866fSTomas Novotny 2065e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 207be38866fSTomas Novotny }; 208be38866fSTomas Novotny 209e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) 2105e00708dSGuido Günther { 2115e00708dSGuido Günther int ret; 2125e00708dSGuido Günther 213e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 214e21b5b1fSMårten Lindahl 215e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 216e21b5b1fSMårten Lindahl if (ret < 0) 217e21b5b1fSMårten Lindahl goto out; 218e21b5b1fSMårten Lindahl 219e21b5b1fSMårten Lindahl if (en) 220e21b5b1fSMårten Lindahl ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; 221e21b5b1fSMårten Lindahl else 222e21b5b1fSMårten Lindahl ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; 223e21b5b1fSMårten Lindahl 224e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); 225e21b5b1fSMårten Lindahl 226e21b5b1fSMårten Lindahl out: 227e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 228e21b5b1fSMårten Lindahl 229e21b5b1fSMårten Lindahl return ret; 230e21b5b1fSMårten Lindahl } 231e21b5b1fSMårten Lindahl 232e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) 233e21b5b1fSMårten Lindahl { 234e21b5b1fSMårten Lindahl int ret; 235e21b5b1fSMårten Lindahl 236e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 237e21b5b1fSMårten Lindahl 238e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 239e21b5b1fSMårten Lindahl if (ret < 0) 240e21b5b1fSMårten Lindahl goto out; 241e21b5b1fSMårten Lindahl 242e21b5b1fSMårten Lindahl if (en) 243e21b5b1fSMårten Lindahl ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; 244e21b5b1fSMårten Lindahl else 245e21b5b1fSMårten Lindahl ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; 246e21b5b1fSMårten Lindahl 247e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); 248e21b5b1fSMårten Lindahl 249e21b5b1fSMårten Lindahl out: 250e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 251e21b5b1fSMårten Lindahl 252e21b5b1fSMårten Lindahl return ret; 253e21b5b1fSMårten Lindahl } 254e21b5b1fSMårten Lindahl 255e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 256e21b5b1fSMårten Lindahl { 257e21b5b1fSMårten Lindahl int ret; 258e21b5b1fSMårten Lindahl 259e21b5b1fSMårten Lindahl ret = vcnl4000_write_als_enable(data, on); 2605e00708dSGuido Günther if (ret < 0) 2615e00708dSGuido Günther return ret; 2625e00708dSGuido Günther 263e21b5b1fSMårten Lindahl ret = vcnl4000_write_ps_enable(data, on); 2645e00708dSGuido Günther if (ret < 0) 2655e00708dSGuido Günther return ret; 2665e00708dSGuido Günther 2675e00708dSGuido Günther if (on) { 2685e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 2695e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 2705e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 2715e00708dSGuido Günther } 2725e00708dSGuido Günther 2735e00708dSGuido Günther return 0; 2745e00708dSGuido Günther } 2755e00708dSGuido Günther 276be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 277be38866fSTomas Novotny { 2785a441aadSAngus Ainslie (Purism) int ret, id; 279be38866fSTomas Novotny 280be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 281be38866fSTomas Novotny if (ret < 0) 282be38866fSTomas Novotny return ret; 283be38866fSTomas Novotny 2845a441aadSAngus Ainslie (Purism) id = ret & 0xff; 2855a441aadSAngus Ainslie (Purism) 2865a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 2875a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 2885a441aadSAngus Ainslie (Purism) if (ret < 0) 2895a441aadSAngus Ainslie (Purism) return ret; 2905a441aadSAngus Ainslie (Purism) 2915a441aadSAngus Ainslie (Purism) id = ret & 0xff; 2925a441aadSAngus Ainslie (Purism) 2935a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 294be38866fSTomas Novotny return -ENODEV; 2955a441aadSAngus Ainslie (Purism) } 2965a441aadSAngus Ainslie (Purism) 2975a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 298be38866fSTomas Novotny 299be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 300be38866fSTomas Novotny 301be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 302be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 3035a441aadSAngus Ainslie (Purism) switch (id) { 3045a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 305b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 306b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 307b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 308b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 309bc80573eSGuido Günther data->al_scale = 24000; 3105a441aadSAngus Ainslie (Purism) break; 3115a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 3122ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 3132ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 3142ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 3152ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 316bc80573eSGuido Günther data->al_scale = 120000; 3175a441aadSAngus Ainslie (Purism) break; 3185a441aadSAngus Ainslie (Purism) } 319be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 320be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 3211ebc787aSTomas Novotny 3225e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 3235e00708dSGuido Günther if (ret < 0) 3245e00708dSGuido Günther return ret; 3255e00708dSGuido Günther 3261ebc787aSTomas Novotny return 0; 3271ebc787aSTomas Novotny }; 3281ebc787aSTomas Novotny 329816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 330816956c3SMathieu Othacehe { 331816956c3SMathieu Othacehe s32 ret; 332816956c3SMathieu Othacehe 333816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 334816956c3SMathieu Othacehe if (ret < 0) 335816956c3SMathieu Othacehe return ret; 336816956c3SMathieu Othacehe 337816956c3SMathieu Othacehe *val = ret; 338816956c3SMathieu Othacehe return 0; 339816956c3SMathieu Othacehe } 340816956c3SMathieu Othacehe 341816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 342816956c3SMathieu Othacehe { 343816956c3SMathieu Othacehe if (val > U16_MAX) 344816956c3SMathieu Othacehe return -ERANGE; 345816956c3SMathieu Othacehe 346816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 347816956c3SMathieu Othacehe } 348816956c3SMathieu Othacehe 349816956c3SMathieu Othacehe 35062a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 35162a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 35262a1efb9SPeter Meerwald { 35362a1efb9SPeter Meerwald int tries = 20; 35462a1efb9SPeter Meerwald int ret; 35562a1efb9SPeter Meerwald 356be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 357ff34ed6dSPeter Meerwald-Stadler 35862a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 35962a1efb9SPeter Meerwald req_mask); 36062a1efb9SPeter Meerwald if (ret < 0) 361ff34ed6dSPeter Meerwald-Stadler goto fail; 36262a1efb9SPeter Meerwald 36362a1efb9SPeter Meerwald /* wait for data to become ready */ 36462a1efb9SPeter Meerwald while (tries--) { 36562a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 36662a1efb9SPeter Meerwald if (ret < 0) 367ff34ed6dSPeter Meerwald-Stadler goto fail; 36862a1efb9SPeter Meerwald if (ret & rdy_mask) 36962a1efb9SPeter Meerwald break; 37062a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 37162a1efb9SPeter Meerwald } 37262a1efb9SPeter Meerwald 37362a1efb9SPeter Meerwald if (tries < 0) { 37462a1efb9SPeter Meerwald dev_err(&data->client->dev, 37562a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 376ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 377ff34ed6dSPeter Meerwald-Stadler goto fail; 37862a1efb9SPeter Meerwald } 37962a1efb9SPeter Meerwald 380816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 38162a1efb9SPeter Meerwald if (ret < 0) 382ff34ed6dSPeter Meerwald-Stadler goto fail; 38362a1efb9SPeter Meerwald 384be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 38562a1efb9SPeter Meerwald 38662a1efb9SPeter Meerwald return 0; 387ff34ed6dSPeter Meerwald-Stadler 388ff34ed6dSPeter Meerwald-Stadler fail: 389be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 390ff34ed6dSPeter Meerwald-Stadler return ret; 39162a1efb9SPeter Meerwald } 39262a1efb9SPeter Meerwald 393be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 394be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 395be38866fSTomas Novotny { 396be38866fSTomas Novotny int ret; 397be38866fSTomas Novotny s64 delta; 398be38866fSTomas Novotny ktime_t next_measurement; 399be38866fSTomas Novotny 400be38866fSTomas Novotny mutex_lock(&chan->lock); 401be38866fSTomas Novotny 402be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 403be38866fSTomas Novotny chan->sampling_rate); 404be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 405be38866fSTomas Novotny if (delta > 0) 406be38866fSTomas Novotny usleep_range(delta, delta + 500); 407be38866fSTomas Novotny chan->last_measurement = ktime_get(); 408be38866fSTomas Novotny 409be38866fSTomas Novotny mutex_unlock(&chan->lock); 410be38866fSTomas Novotny 411be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 412be38866fSTomas Novotny if (ret < 0) 413be38866fSTomas Novotny return ret; 414be38866fSTomas Novotny 415be38866fSTomas Novotny *val = ret; 416be38866fSTomas Novotny 417be38866fSTomas Novotny return 0; 418be38866fSTomas Novotny } 419be38866fSTomas Novotny 4201ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 4211ebc787aSTomas Novotny { 4221ebc787aSTomas Novotny return vcnl4000_measure(data, 4231ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 4241ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 4251ebc787aSTomas Novotny } 4261ebc787aSTomas Novotny 427be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 428be38866fSTomas Novotny { 429be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 430be38866fSTomas Novotny } 431be38866fSTomas Novotny 4321ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 4331ebc787aSTomas Novotny { 4341ebc787aSTomas Novotny return vcnl4000_measure(data, 4351ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 4361ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 4371ebc787aSTomas Novotny } 4381ebc787aSTomas Novotny 439be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 440be38866fSTomas Novotny { 441be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 442be38866fSTomas Novotny } 443be38866fSTomas Novotny 444f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 445f6889c1bSMathieu Othacehe int *val2) 446f6889c1bSMathieu Othacehe { 447f6889c1bSMathieu Othacehe int ret; 448f6889c1bSMathieu Othacehe 449f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 450f6889c1bSMathieu Othacehe if (ret < 0) 451f6889c1bSMathieu Othacehe return ret; 452f6889c1bSMathieu Othacehe 453f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 454f6889c1bSMathieu Othacehe return -EINVAL; 455f6889c1bSMathieu Othacehe 456f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 457f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 458f6889c1bSMathieu Othacehe 459f6889c1bSMathieu Othacehe return 0; 460f6889c1bSMathieu Othacehe } 461f6889c1bSMathieu Othacehe 462d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 463f5a98e1fSGuido Günther { 464d35567fcSMathieu Othacehe int ret; 465f5a98e1fSGuido Günther 466d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 467d35567fcSMathieu Othacehe if (ret < 0) 468d35567fcSMathieu Othacehe return false; 469d35567fcSMathieu Othacehe 470d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 471f5a98e1fSGuido Günther } 472f5a98e1fSGuido Günther 4735e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 4745e00708dSGuido Günther { 4755e00708dSGuido Günther struct device *dev = &data->client->dev; 4765e00708dSGuido Günther int ret; 4775e00708dSGuido Günther 4785e00708dSGuido Günther if (on) { 479db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 4805e00708dSGuido Günther } else { 4815e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 4825e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 4835e00708dSGuido Günther } 4845e00708dSGuido Günther 4855e00708dSGuido Günther return ret; 4865e00708dSGuido Günther } 4875e00708dSGuido Günther 48885e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) 48985e2c6a2SMårten Lindahl { 49085e2c6a2SMårten Lindahl int ret; 49185e2c6a2SMårten Lindahl 49285e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 49385e2c6a2SMårten Lindahl if (ret < 0) 49485e2c6a2SMårten Lindahl return ret; 49585e2c6a2SMårten Lindahl 49685e2c6a2SMårten Lindahl ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 49785e2c6a2SMårten Lindahl 49885e2c6a2SMårten Lindahl if (ret >= ARRAY_SIZE(vcnl4040_ps_it_times)) 49985e2c6a2SMårten Lindahl return -EINVAL; 50085e2c6a2SMårten Lindahl 50185e2c6a2SMårten Lindahl *val = vcnl4040_ps_it_times[ret][0]; 50285e2c6a2SMårten Lindahl *val2 = vcnl4040_ps_it_times[ret][1]; 50385e2c6a2SMårten Lindahl 50485e2c6a2SMårten Lindahl return 0; 50585e2c6a2SMårten Lindahl } 50685e2c6a2SMårten Lindahl 50785e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) 50885e2c6a2SMårten Lindahl { 50985e2c6a2SMårten Lindahl unsigned int i; 51085e2c6a2SMårten Lindahl int ret, index = -1; 51185e2c6a2SMårten Lindahl u16 regval; 51285e2c6a2SMårten Lindahl 51385e2c6a2SMårten Lindahl for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_it_times); i++) { 51485e2c6a2SMårten Lindahl if (val == vcnl4040_ps_it_times[i][1]) { 51585e2c6a2SMårten Lindahl index = i; 51685e2c6a2SMårten Lindahl break; 51785e2c6a2SMårten Lindahl } 51885e2c6a2SMårten Lindahl } 51985e2c6a2SMårten Lindahl 52085e2c6a2SMårten Lindahl if (index < 0) 52185e2c6a2SMårten Lindahl return -EINVAL; 52285e2c6a2SMårten Lindahl 52385e2c6a2SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 52485e2c6a2SMårten Lindahl 52585e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 52685e2c6a2SMårten Lindahl if (ret < 0) 52785e2c6a2SMårten Lindahl goto out; 52885e2c6a2SMårten Lindahl 52985e2c6a2SMårten Lindahl regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | 53085e2c6a2SMårten Lindahl FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); 53185e2c6a2SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 53285e2c6a2SMårten Lindahl regval); 53385e2c6a2SMårten Lindahl 53485e2c6a2SMårten Lindahl out: 53585e2c6a2SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 53685e2c6a2SMårten Lindahl return ret; 53785e2c6a2SMårten Lindahl } 53885e2c6a2SMårten Lindahl 53962a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 54062a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 54162a1efb9SPeter Meerwald int *val, int *val2, long mask) 54262a1efb9SPeter Meerwald { 5435d693139SPeter Meerwald-Stadler int ret; 54462a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 54562a1efb9SPeter Meerwald 54662a1efb9SPeter Meerwald switch (mask) { 54762a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 5485e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 5495e00708dSGuido Günther if (ret < 0) 5505e00708dSGuido Günther return ret; 5515e00708dSGuido Günther 55262a1efb9SPeter Meerwald switch (chan->type) { 55362a1efb9SPeter Meerwald case IIO_LIGHT: 5541ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 5554a818643SGuido Günther if (!ret) 5564a818643SGuido Günther ret = IIO_VAL_INT; 5574a818643SGuido Günther break; 55862a1efb9SPeter Meerwald case IIO_PROXIMITY: 5591ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 5604a818643SGuido Günther if (!ret) 5614a818643SGuido Günther ret = IIO_VAL_INT; 5624a818643SGuido Günther break; 56362a1efb9SPeter Meerwald default: 5644a818643SGuido Günther ret = -EINVAL; 56562a1efb9SPeter Meerwald } 5665e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 5674a818643SGuido Günther return ret; 56862a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 5695d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 5705d693139SPeter Meerwald-Stadler return -EINVAL; 5715d693139SPeter Meerwald-Stadler 57262a1efb9SPeter Meerwald *val = 0; 5731ebc787aSTomas Novotny *val2 = data->al_scale; 5745d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 57585e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 57685e2c6a2SMårten Lindahl if (chan->type != IIO_PROXIMITY) 57785e2c6a2SMårten Lindahl return -EINVAL; 57885e2c6a2SMårten Lindahl ret = vcnl4040_read_ps_it(data, val, val2); 57985e2c6a2SMårten Lindahl if (ret < 0) 58085e2c6a2SMårten Lindahl return ret; 58185e2c6a2SMårten Lindahl return IIO_VAL_INT_PLUS_MICRO; 58285e2c6a2SMårten Lindahl default: 58385e2c6a2SMårten Lindahl return -EINVAL; 58485e2c6a2SMårten Lindahl } 58585e2c6a2SMårten Lindahl } 58685e2c6a2SMårten Lindahl 58785e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev, 58885e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 58985e2c6a2SMårten Lindahl int val, int val2, long mask) 59085e2c6a2SMårten Lindahl { 59185e2c6a2SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 59285e2c6a2SMårten Lindahl 59385e2c6a2SMårten Lindahl switch (mask) { 59485e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 59585e2c6a2SMårten Lindahl if (val != 0) 59685e2c6a2SMårten Lindahl return -EINVAL; 59785e2c6a2SMårten Lindahl if (chan->type != IIO_PROXIMITY) 59885e2c6a2SMårten Lindahl return -EINVAL; 59985e2c6a2SMårten Lindahl return vcnl4040_write_ps_it(data, val2); 60085e2c6a2SMårten Lindahl default: 60185e2c6a2SMårten Lindahl return -EINVAL; 60285e2c6a2SMårten Lindahl } 60385e2c6a2SMårten Lindahl } 60485e2c6a2SMårten Lindahl 60585e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev, 60685e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 60785e2c6a2SMårten Lindahl const int **vals, int *type, int *length, 60885e2c6a2SMårten Lindahl long mask) 60985e2c6a2SMårten Lindahl { 61085e2c6a2SMårten Lindahl switch (mask) { 61185e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 61285e2c6a2SMårten Lindahl *vals = (int *)vcnl4040_ps_it_times; 61385e2c6a2SMårten Lindahl *type = IIO_VAL_INT_PLUS_MICRO; 61485e2c6a2SMårten Lindahl *length = 2 * ARRAY_SIZE(vcnl4040_ps_it_times); 61585e2c6a2SMårten Lindahl return IIO_AVAIL_LIST; 61662a1efb9SPeter Meerwald default: 6175d693139SPeter Meerwald-Stadler return -EINVAL; 61862a1efb9SPeter Meerwald } 61962a1efb9SPeter Meerwald } 62062a1efb9SPeter Meerwald 621d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 622d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 623d35567fcSMathieu Othacehe int *val, int *val2, long mask) 624d35567fcSMathieu Othacehe { 625d35567fcSMathieu Othacehe int ret; 626d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 627d35567fcSMathieu Othacehe 628d35567fcSMathieu Othacehe switch (mask) { 629d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 630d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 631d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 632d35567fcSMathieu Othacehe if (ret) 633d35567fcSMathieu Othacehe return ret; 634d35567fcSMathieu Othacehe 635d35567fcSMathieu Othacehe /* Protect against event capture. */ 636d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 637d35567fcSMathieu Othacehe ret = -EBUSY; 638d35567fcSMathieu Othacehe } else { 639d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 640d35567fcSMathieu Othacehe mask); 641d35567fcSMathieu Othacehe } 642d35567fcSMathieu Othacehe 643d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 644d35567fcSMathieu Othacehe return ret; 645f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 646f6889c1bSMathieu Othacehe switch (chan->type) { 647f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 648f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 649f6889c1bSMathieu Othacehe if (ret < 0) 650f6889c1bSMathieu Othacehe return ret; 651f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 652d35567fcSMathieu Othacehe default: 653d35567fcSMathieu Othacehe return -EINVAL; 654d35567fcSMathieu Othacehe } 655f6889c1bSMathieu Othacehe default: 656f6889c1bSMathieu Othacehe return -EINVAL; 657f6889c1bSMathieu Othacehe } 658f6889c1bSMathieu Othacehe } 659f6889c1bSMathieu Othacehe 660f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 661f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 662f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 663f6889c1bSMathieu Othacehe long mask) 664f6889c1bSMathieu Othacehe { 665f6889c1bSMathieu Othacehe switch (mask) { 666f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 667f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 668f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 669f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 670f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 671f6889c1bSMathieu Othacehe default: 672f6889c1bSMathieu Othacehe return -EINVAL; 673f6889c1bSMathieu Othacehe } 674f6889c1bSMathieu Othacehe } 675f6889c1bSMathieu Othacehe 676f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 677f6889c1bSMathieu Othacehe int val2) 678f6889c1bSMathieu Othacehe { 679f6889c1bSMathieu Othacehe unsigned int i; 680f6889c1bSMathieu Othacehe int index = -1; 681f6889c1bSMathieu Othacehe 682f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 683f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 684f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 685f6889c1bSMathieu Othacehe index = i; 686f6889c1bSMathieu Othacehe break; 687f6889c1bSMathieu Othacehe } 688f6889c1bSMathieu Othacehe } 689f6889c1bSMathieu Othacehe 690f6889c1bSMathieu Othacehe if (index < 0) 691f6889c1bSMathieu Othacehe return -EINVAL; 692f6889c1bSMathieu Othacehe 693f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 694f6889c1bSMathieu Othacehe index); 695f6889c1bSMathieu Othacehe } 696f6889c1bSMathieu Othacehe 697f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 698f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 699f6889c1bSMathieu Othacehe int val, int val2, long mask) 700f6889c1bSMathieu Othacehe { 701f6889c1bSMathieu Othacehe int ret; 702f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 703f6889c1bSMathieu Othacehe 704f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 705f6889c1bSMathieu Othacehe if (ret) 706f6889c1bSMathieu Othacehe return ret; 707f6889c1bSMathieu Othacehe 708f6889c1bSMathieu Othacehe /* Protect against event capture. */ 709f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 710f6889c1bSMathieu Othacehe ret = -EBUSY; 711f6889c1bSMathieu Othacehe goto end; 712f6889c1bSMathieu Othacehe } 713f6889c1bSMathieu Othacehe 714f6889c1bSMathieu Othacehe switch (mask) { 715f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 716f6889c1bSMathieu Othacehe switch (chan->type) { 717f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 718f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 719f6889c1bSMathieu Othacehe goto end; 720f6889c1bSMathieu Othacehe default: 721f6889c1bSMathieu Othacehe ret = -EINVAL; 722f6889c1bSMathieu Othacehe goto end; 723f6889c1bSMathieu Othacehe } 724f6889c1bSMathieu Othacehe default: 725f6889c1bSMathieu Othacehe ret = -EINVAL; 726f6889c1bSMathieu Othacehe goto end; 727f6889c1bSMathieu Othacehe } 728f6889c1bSMathieu Othacehe 729f6889c1bSMathieu Othacehe end: 730f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 731f6889c1bSMathieu Othacehe return ret; 732d35567fcSMathieu Othacehe } 733d35567fcSMathieu Othacehe 734d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 735d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 736d35567fcSMathieu Othacehe enum iio_event_type type, 737d35567fcSMathieu Othacehe enum iio_event_direction dir, 738d35567fcSMathieu Othacehe enum iio_event_info info, 739d35567fcSMathieu Othacehe int *val, int *val2) 740d35567fcSMathieu Othacehe { 741d35567fcSMathieu Othacehe int ret; 742d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 743d35567fcSMathieu Othacehe 744d35567fcSMathieu Othacehe switch (info) { 745d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 746d35567fcSMathieu Othacehe switch (dir) { 747d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 748d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 749d35567fcSMathieu Othacehe val); 750d35567fcSMathieu Othacehe if (ret < 0) 751d35567fcSMathieu Othacehe return ret; 752d35567fcSMathieu Othacehe return IIO_VAL_INT; 753d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 754d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 755d35567fcSMathieu Othacehe val); 756d35567fcSMathieu Othacehe if (ret < 0) 757d35567fcSMathieu Othacehe return ret; 758d35567fcSMathieu Othacehe return IIO_VAL_INT; 759d35567fcSMathieu Othacehe default: 760d35567fcSMathieu Othacehe return -EINVAL; 761d35567fcSMathieu Othacehe } 762d35567fcSMathieu Othacehe default: 763d35567fcSMathieu Othacehe return -EINVAL; 764d35567fcSMathieu Othacehe } 765d35567fcSMathieu Othacehe } 766d35567fcSMathieu Othacehe 767d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 768d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 769d35567fcSMathieu Othacehe enum iio_event_type type, 770d35567fcSMathieu Othacehe enum iio_event_direction dir, 771d35567fcSMathieu Othacehe enum iio_event_info info, 772d35567fcSMathieu Othacehe int val, int val2) 773d35567fcSMathieu Othacehe { 774d35567fcSMathieu Othacehe int ret; 775d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 776d35567fcSMathieu Othacehe 777d35567fcSMathieu Othacehe switch (info) { 778d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 779d35567fcSMathieu Othacehe switch (dir) { 780d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 781d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 782d35567fcSMathieu Othacehe val); 783d35567fcSMathieu Othacehe if (ret < 0) 784d35567fcSMathieu Othacehe return ret; 785d35567fcSMathieu Othacehe return IIO_VAL_INT; 786d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 787d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 788d35567fcSMathieu Othacehe val); 789d35567fcSMathieu Othacehe if (ret < 0) 790d35567fcSMathieu Othacehe return ret; 791d35567fcSMathieu Othacehe return IIO_VAL_INT; 792d35567fcSMathieu Othacehe default: 793d35567fcSMathieu Othacehe return -EINVAL; 794d35567fcSMathieu Othacehe } 795d35567fcSMathieu Othacehe default: 796d35567fcSMathieu Othacehe return -EINVAL; 797d35567fcSMathieu Othacehe } 798d35567fcSMathieu Othacehe } 799d35567fcSMathieu Othacehe 800d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 801d35567fcSMathieu Othacehe { 802d35567fcSMathieu Othacehe int ret; 803d35567fcSMathieu Othacehe 804d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 805d35567fcSMathieu Othacehe if (ret < 0) 806d35567fcSMathieu Othacehe return false; 807d35567fcSMathieu Othacehe 808d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 809d35567fcSMathieu Othacehe } 810d35567fcSMathieu Othacehe 811d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 812d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 813d35567fcSMathieu Othacehe enum iio_event_type type, 814d35567fcSMathieu Othacehe enum iio_event_direction dir) 815d35567fcSMathieu Othacehe { 816d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 817d35567fcSMathieu Othacehe 818d35567fcSMathieu Othacehe switch (chan->type) { 819d35567fcSMathieu Othacehe case IIO_PROXIMITY: 820d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 821d35567fcSMathieu Othacehe default: 822d35567fcSMathieu Othacehe return -EINVAL; 823d35567fcSMathieu Othacehe } 824d35567fcSMathieu Othacehe } 825d35567fcSMathieu Othacehe 826d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 827d35567fcSMathieu Othacehe { 828d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 829d35567fcSMathieu Othacehe int ret; 830d35567fcSMathieu Othacehe int icr; 831d35567fcSMathieu Othacehe int command; 832d35567fcSMathieu Othacehe 833d35567fcSMathieu Othacehe if (state) { 834d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 835d35567fcSMathieu Othacehe if (ret) 836d35567fcSMathieu Othacehe return ret; 837d35567fcSMathieu Othacehe 838d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 839d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 840d35567fcSMathieu Othacehe 841d35567fcSMathieu Othacehe /* 842d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 843d35567fcSMathieu Othacehe * default. 844d35567fcSMathieu Othacehe */ 845d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 846d35567fcSMathieu Othacehe } else { 847d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 848d35567fcSMathieu Othacehe return 0; 849d35567fcSMathieu Othacehe 850d35567fcSMathieu Othacehe command = 0; 851d35567fcSMathieu Othacehe icr = 0; 852d35567fcSMathieu Othacehe } 853d35567fcSMathieu Othacehe 854d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 855d35567fcSMathieu Othacehe command); 856d35567fcSMathieu Othacehe if (ret < 0) 857d35567fcSMathieu Othacehe goto end; 858d35567fcSMathieu Othacehe 859d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 860d35567fcSMathieu Othacehe 861d35567fcSMathieu Othacehe end: 862d35567fcSMathieu Othacehe if (state) 863d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 864d35567fcSMathieu Othacehe 865d35567fcSMathieu Othacehe return ret; 866d35567fcSMathieu Othacehe } 867d35567fcSMathieu Othacehe 868d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 869d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 870d35567fcSMathieu Othacehe enum iio_event_type type, 871d35567fcSMathieu Othacehe enum iio_event_direction dir, 872d35567fcSMathieu Othacehe int state) 873d35567fcSMathieu Othacehe { 874d35567fcSMathieu Othacehe switch (chan->type) { 875d35567fcSMathieu Othacehe case IIO_PROXIMITY: 876d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 877d35567fcSMathieu Othacehe default: 878d35567fcSMathieu Othacehe return -EINVAL; 879d35567fcSMathieu Othacehe } 880d35567fcSMathieu Othacehe } 881d35567fcSMathieu Othacehe 882d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 883d35567fcSMathieu Othacehe uintptr_t priv, 884d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 885d35567fcSMathieu Othacehe char *buf) 886d35567fcSMathieu Othacehe { 887d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 888d35567fcSMathieu Othacehe 889d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 890d35567fcSMathieu Othacehe } 891d35567fcSMathieu Othacehe 8923a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 8933a52d32aSMårten Lindahl { 8943a52d32aSMårten Lindahl struct iio_dev *indio_dev = p; 8953a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 8963a52d32aSMårten Lindahl unsigned long isr; 8973a52d32aSMårten Lindahl int ret; 8983a52d32aSMårten Lindahl 8993a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 9003a52d32aSMårten Lindahl if (ret < 0) 9013a52d32aSMårten Lindahl goto end; 9023a52d32aSMårten Lindahl 9033a52d32aSMårten Lindahl isr = ret; 9043a52d32aSMårten Lindahl 9053a52d32aSMårten Lindahl if (isr & VCNL4010_INT_THR) { 9063a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 9073a52d32aSMårten Lindahl iio_push_event(indio_dev, 9083a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 9093a52d32aSMårten Lindahl IIO_PROXIMITY, 9103a52d32aSMårten Lindahl 1, 9113a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 9123a52d32aSMårten Lindahl IIO_EV_DIR_FALLING), 9133a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 9143a52d32aSMårten Lindahl } 9153a52d32aSMårten Lindahl 9163a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 9173a52d32aSMårten Lindahl iio_push_event(indio_dev, 9183a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 9193a52d32aSMårten Lindahl IIO_PROXIMITY, 9203a52d32aSMårten Lindahl 1, 9213a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 9223a52d32aSMårten Lindahl IIO_EV_DIR_RISING), 9233a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 9243a52d32aSMårten Lindahl } 9253a52d32aSMårten Lindahl 9263a52d32aSMårten Lindahl i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 9273a52d32aSMårten Lindahl isr & VCNL4010_INT_THR); 9283a52d32aSMårten Lindahl } 9293a52d32aSMårten Lindahl 9303a52d32aSMårten Lindahl if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 9313a52d32aSMårten Lindahl iio_trigger_poll_chained(indio_dev->trig); 9323a52d32aSMårten Lindahl 9333a52d32aSMårten Lindahl end: 9343a52d32aSMårten Lindahl return IRQ_HANDLED; 9353a52d32aSMårten Lindahl } 9363a52d32aSMårten Lindahl 9373a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 9383a52d32aSMårten Lindahl { 9393a52d32aSMårten Lindahl struct iio_poll_func *pf = p; 9403a52d32aSMårten Lindahl struct iio_dev *indio_dev = pf->indio_dev; 9413a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 9423a52d32aSMårten Lindahl const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 9433a52d32aSMårten Lindahl u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 9443a52d32aSMårten Lindahl bool data_read = false; 9453a52d32aSMårten Lindahl unsigned long isr; 9463a52d32aSMårten Lindahl int val = 0; 9473a52d32aSMårten Lindahl int ret; 9483a52d32aSMårten Lindahl 9493a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 9503a52d32aSMårten Lindahl if (ret < 0) 9513a52d32aSMårten Lindahl goto end; 9523a52d32aSMårten Lindahl 9533a52d32aSMårten Lindahl isr = ret; 9543a52d32aSMårten Lindahl 9553a52d32aSMårten Lindahl if (test_bit(0, active_scan_mask)) { 9563a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 9573a52d32aSMårten Lindahl ret = vcnl4000_read_data(data, 9583a52d32aSMårten Lindahl VCNL4000_PS_RESULT_HI, 9593a52d32aSMårten Lindahl &val); 9603a52d32aSMårten Lindahl if (ret < 0) 9613a52d32aSMårten Lindahl goto end; 9623a52d32aSMårten Lindahl 9633a52d32aSMårten Lindahl buffer[0] = val; 9643a52d32aSMårten Lindahl data_read = true; 9653a52d32aSMårten Lindahl } 9663a52d32aSMårten Lindahl } 9673a52d32aSMårten Lindahl 9683a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 9693a52d32aSMårten Lindahl isr & VCNL4010_INT_DRDY); 9703a52d32aSMårten Lindahl if (ret < 0) 9713a52d32aSMårten Lindahl goto end; 9723a52d32aSMårten Lindahl 9733a52d32aSMårten Lindahl if (!data_read) 9743a52d32aSMårten Lindahl goto end; 9753a52d32aSMårten Lindahl 9763a52d32aSMårten Lindahl iio_push_to_buffers_with_timestamp(indio_dev, buffer, 9773a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 9783a52d32aSMårten Lindahl 9793a52d32aSMårten Lindahl end: 9803a52d32aSMårten Lindahl iio_trigger_notify_done(indio_dev->trig); 9813a52d32aSMårten Lindahl return IRQ_HANDLED; 9823a52d32aSMårten Lindahl } 9833a52d32aSMårten Lindahl 9843a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 9853a52d32aSMårten Lindahl { 9863a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 9873a52d32aSMårten Lindahl int ret; 9883a52d32aSMårten Lindahl int cmd; 9893a52d32aSMårten Lindahl 9903a52d32aSMårten Lindahl /* Do not enable the buffer if we are already capturing events. */ 9913a52d32aSMårten Lindahl if (vcnl4010_is_in_periodic_mode(data)) 9923a52d32aSMårten Lindahl return -EBUSY; 9933a52d32aSMårten Lindahl 9943a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 9953a52d32aSMårten Lindahl VCNL4010_INT_PROX_EN); 9963a52d32aSMårten Lindahl if (ret < 0) 9973a52d32aSMårten Lindahl return ret; 9983a52d32aSMårten Lindahl 9993a52d32aSMårten Lindahl cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 10003a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 10013a52d32aSMårten Lindahl } 10023a52d32aSMårten Lindahl 10033a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 10043a52d32aSMårten Lindahl { 10053a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 10063a52d32aSMårten Lindahl int ret; 10073a52d32aSMårten Lindahl 10083a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 10093a52d32aSMårten Lindahl if (ret < 0) 10103a52d32aSMårten Lindahl return ret; 10113a52d32aSMårten Lindahl 10123a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 10133a52d32aSMårten Lindahl } 10143a52d32aSMårten Lindahl 10153a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 10163a52d32aSMårten Lindahl .postenable = &vcnl4010_buffer_postenable, 10173a52d32aSMårten Lindahl .predisable = &vcnl4010_buffer_predisable, 10183a52d32aSMårten Lindahl }; 10193a52d32aSMårten Lindahl 1020d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 1021d35567fcSMathieu Othacehe { 1022d35567fcSMathieu Othacehe .name = "nearlevel", 1023d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 1024d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 1025d35567fcSMathieu Othacehe }, 1026d35567fcSMathieu Othacehe { /* sentinel */ } 1027d35567fcSMathieu Othacehe }; 1028d35567fcSMathieu Othacehe 1029d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 1030d35567fcSMathieu Othacehe { 1031d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1032d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 1033d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1034d35567fcSMathieu Othacehe }, { 1035d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1036d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 1037d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1038d35567fcSMathieu Othacehe }, { 1039d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1040d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 1041d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 1042d35567fcSMathieu Othacehe } 1043d35567fcSMathieu Othacehe }; 1044d35567fcSMathieu Othacehe 1045d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 1046d35567fcSMathieu Othacehe { 1047d35567fcSMathieu Othacehe .type = IIO_LIGHT, 1048d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1049d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1050d35567fcSMathieu Othacehe }, { 1051d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 1052d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1053d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 1054d35567fcSMathieu Othacehe } 1055d35567fcSMathieu Othacehe }; 1056d35567fcSMathieu Othacehe 1057d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 1058d35567fcSMathieu Othacehe { 1059d35567fcSMathieu Othacehe .type = IIO_LIGHT, 10608fe78d52SMathieu Othacehe .scan_index = -1, 1061d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1062d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1063d35567fcSMathieu Othacehe }, { 1064d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 10658fe78d52SMathieu Othacehe .scan_index = 0, 1066f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1067f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 1068f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1069d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 1070d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 1071d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 10728fe78d52SMathieu Othacehe .scan_type = { 10738fe78d52SMathieu Othacehe .sign = 'u', 10748fe78d52SMathieu Othacehe .realbits = 16, 10758fe78d52SMathieu Othacehe .storagebits = 16, 10768fe78d52SMathieu Othacehe .endianness = IIO_CPU, 1077d35567fcSMathieu Othacehe }, 10788fe78d52SMathieu Othacehe }, 10798fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 1080d35567fcSMathieu Othacehe }; 1081d35567fcSMathieu Othacehe 108285e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = { 108385e2c6a2SMårten Lindahl { 108485e2c6a2SMårten Lindahl .type = IIO_LIGHT, 108585e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 108685e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_SCALE), 108785e2c6a2SMårten Lindahl }, { 108885e2c6a2SMårten Lindahl .type = IIO_PROXIMITY, 108985e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 109085e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_INT_TIME), 109185e2c6a2SMårten Lindahl .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 109285e2c6a2SMårten Lindahl .ext_info = vcnl4000_ext_info, 109385e2c6a2SMårten Lindahl } 109485e2c6a2SMårten Lindahl }; 109585e2c6a2SMårten Lindahl 109662a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 109762a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 109862a1efb9SPeter Meerwald }; 109962a1efb9SPeter Meerwald 1100d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 1101d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 1102f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 1103f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 1104d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 1105d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 1106d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 1107d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 1108d35567fcSMathieu Othacehe }; 1109d35567fcSMathieu Othacehe 111085e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = { 111185e2c6a2SMårten Lindahl .read_raw = vcnl4000_read_raw, 111285e2c6a2SMårten Lindahl .write_raw = vcnl4040_write_raw, 111385e2c6a2SMårten Lindahl .read_avail = vcnl4040_read_avail, 111485e2c6a2SMårten Lindahl }; 111585e2c6a2SMårten Lindahl 1116d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 1117d35567fcSMathieu Othacehe [VCNL4000] = { 1118d35567fcSMathieu Othacehe .prod = "VCNL4000", 1119d35567fcSMathieu Othacehe .init = vcnl4000_init, 1120d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1121d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1122d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1123d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1124d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1125d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1126d35567fcSMathieu Othacehe }, 1127d35567fcSMathieu Othacehe [VCNL4010] = { 1128d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 1129d35567fcSMathieu Othacehe .init = vcnl4000_init, 1130d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1131d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1132d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1133d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 1134d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 1135d35567fcSMathieu Othacehe .info = &vcnl4010_info, 1136*bfb6cfeeSMårten Lindahl .irq_thread = vcnl4010_irq_thread, 1137*bfb6cfeeSMårten Lindahl .trig_buffer_func = vcnl4010_trigger_handler, 1138*bfb6cfeeSMårten Lindahl .buffer_setup_ops = &vcnl4010_buffer_ops, 1139d35567fcSMathieu Othacehe }, 1140d35567fcSMathieu Othacehe [VCNL4040] = { 1141d35567fcSMathieu Othacehe .prod = "VCNL4040", 1142d35567fcSMathieu Othacehe .init = vcnl4200_init, 1143d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1144d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1145d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 114685e2c6a2SMårten Lindahl .channels = vcnl4040_channels, 114785e2c6a2SMårten Lindahl .num_channels = ARRAY_SIZE(vcnl4040_channels), 114885e2c6a2SMårten Lindahl .info = &vcnl4040_info, 1149d35567fcSMathieu Othacehe }, 1150d35567fcSMathieu Othacehe [VCNL4200] = { 1151d35567fcSMathieu Othacehe .prod = "VCNL4200", 1152d35567fcSMathieu Othacehe .init = vcnl4200_init, 1153d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1154d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1155d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1156d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1157d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1158d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1159d35567fcSMathieu Othacehe }, 1160d35567fcSMathieu Othacehe }; 1161d35567fcSMathieu Othacehe 11628fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 11638fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 11648fe78d52SMathieu Othacehe }; 11658fe78d52SMathieu Othacehe 11668fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 11678fe78d52SMathieu Othacehe { 11688fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 11698fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 11708fe78d52SMathieu Othacehe struct iio_trigger *trigger; 11718fe78d52SMathieu Othacehe 11728fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 117315ea2878SJonathan Cameron indio_dev->name, 117415ea2878SJonathan Cameron iio_device_id(indio_dev)); 11758fe78d52SMathieu Othacehe if (!trigger) 11768fe78d52SMathieu Othacehe return -ENOMEM; 11778fe78d52SMathieu Othacehe 11788fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 11798fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 11808fe78d52SMathieu Othacehe 11818fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 11828fe78d52SMathieu Othacehe } 11838fe78d52SMathieu Othacehe 1184e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client) 118562a1efb9SPeter Meerwald { 1186e61295e0SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client); 118762a1efb9SPeter Meerwald struct vcnl4000_data *data; 118862a1efb9SPeter Meerwald struct iio_dev *indio_dev; 11891ebc787aSTomas Novotny int ret; 119062a1efb9SPeter Meerwald 11912669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 119262a1efb9SPeter Meerwald if (!indio_dev) 119362a1efb9SPeter Meerwald return -ENOMEM; 119462a1efb9SPeter Meerwald 119562a1efb9SPeter Meerwald data = iio_priv(indio_dev); 119662a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 119762a1efb9SPeter Meerwald data->client = client; 11981ebc787aSTomas Novotny data->id = id->driver_data; 11991ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 120062a1efb9SPeter Meerwald 12011ebc787aSTomas Novotny ret = data->chip_spec->init(data); 120262a1efb9SPeter Meerwald if (ret < 0) 12032669d723SPeter Meerwald return ret; 120462a1efb9SPeter Meerwald 1205d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 12061ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 120762a1efb9SPeter Meerwald 1208f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1209f5a98e1fSGuido Günther &data->near_level)) 1210f5a98e1fSGuido Günther data->near_level = 0; 1211f5a98e1fSGuido Günther 1212d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1213d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1214d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 121562a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 121662a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 121762a1efb9SPeter Meerwald 1218*bfb6cfeeSMårten Lindahl if (data->chip_spec->trig_buffer_func && 1219*bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops) { 12208fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 12218fe78d52SMathieu Othacehe NULL, 1222*bfb6cfeeSMårten Lindahl data->chip_spec->trig_buffer_func, 1223*bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops); 12248fe78d52SMathieu Othacehe if (ret < 0) { 12258fe78d52SMathieu Othacehe dev_err(&client->dev, 12268fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 12278fe78d52SMathieu Othacehe return ret; 12288fe78d52SMathieu Othacehe } 1229*bfb6cfeeSMårten Lindahl } 12308fe78d52SMathieu Othacehe 1231*bfb6cfeeSMårten Lindahl if (client->irq && data->chip_spec->irq_thread) { 1232d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1233*bfb6cfeeSMårten Lindahl NULL, data->chip_spec->irq_thread, 1234d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1235d35567fcSMathieu Othacehe IRQF_ONESHOT, 1236*bfb6cfeeSMårten Lindahl "vcnl4000_irq", 1237d35567fcSMathieu Othacehe indio_dev); 1238d35567fcSMathieu Othacehe if (ret < 0) { 1239d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1240d35567fcSMathieu Othacehe return ret; 1241d35567fcSMathieu Othacehe } 12428fe78d52SMathieu Othacehe 12438fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 12448fe78d52SMathieu Othacehe if (ret < 0) 12458fe78d52SMathieu Othacehe return ret; 1246d35567fcSMathieu Othacehe } 1247d35567fcSMathieu Othacehe 12485e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 12495e00708dSGuido Günther if (ret < 0) 12505e00708dSGuido Günther goto fail_poweroff; 12515e00708dSGuido Günther 12525e00708dSGuido Günther ret = iio_device_register(indio_dev); 12535e00708dSGuido Günther if (ret < 0) 12545e00708dSGuido Günther goto fail_poweroff; 12555e00708dSGuido Günther 12565e00708dSGuido Günther pm_runtime_enable(&client->dev); 12575e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 12585e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 12595e00708dSGuido Günther 12605e00708dSGuido Günther return 0; 12615e00708dSGuido Günther fail_poweroff: 12625e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 12635e00708dSGuido Günther return ret; 126462a1efb9SPeter Meerwald } 126562a1efb9SPeter Meerwald 1266ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1267ebd457d5SAngus Ainslie (Purism) { 1268ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 12691436a78cSMarco Felsch .data = (void *)VCNL4000, 1270ebd457d5SAngus Ainslie (Purism) }, 1271ebd457d5SAngus Ainslie (Purism) { 1272ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 12731436a78cSMarco Felsch .data = (void *)VCNL4010, 1274ebd457d5SAngus Ainslie (Purism) }, 1275ebd457d5SAngus Ainslie (Purism) { 12761436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 12771436a78cSMarco Felsch .data = (void *)VCNL4010, 1278ebd457d5SAngus Ainslie (Purism) }, 1279ebd457d5SAngus Ainslie (Purism) { 12807fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 12817fd1c260SMarco Felsch .data = (void *)VCNL4040, 12827fd1c260SMarco Felsch }, 12837fd1c260SMarco Felsch { 1284ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 12851436a78cSMarco Felsch .data = (void *)VCNL4200, 1286ebd457d5SAngus Ainslie (Purism) }, 1287ebd457d5SAngus Ainslie (Purism) {}, 1288ebd457d5SAngus Ainslie (Purism) }; 1289ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1290ebd457d5SAngus Ainslie (Purism) 1291ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 12925e00708dSGuido Günther { 12935e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 12945e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 1295ab91da2fSUwe Kleine-König int ret; 12965e00708dSGuido Günther 12975e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 12985e00708dSGuido Günther pm_runtime_disable(&client->dev); 12995e00708dSGuido Günther iio_device_unregister(indio_dev); 13005e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 13015e00708dSGuido Günther 1302ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 1303ab91da2fSUwe Kleine-König if (ret) 1304ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 1305ab91da2fSUwe Kleine-König ERR_PTR(ret)); 13065e00708dSGuido Günther } 13075e00708dSGuido Günther 1308cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 13095e00708dSGuido Günther { 13105e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 13115e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 13125e00708dSGuido Günther 13135e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 13145e00708dSGuido Günther } 13155e00708dSGuido Günther 1316cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 13175e00708dSGuido Günther { 13185e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 13195e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 13205e00708dSGuido Günther 13215e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 13225e00708dSGuido Günther } 13235e00708dSGuido Günther 1324cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 1325cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 13265e00708dSGuido Günther 132762a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 132862a1efb9SPeter Meerwald .driver = { 132962a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 1330cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 1331ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 133262a1efb9SPeter Meerwald }, 1333e61295e0SUwe Kleine-König .probe_new = vcnl4000_probe, 133462a1efb9SPeter Meerwald .id_table = vcnl4000_id, 13355e00708dSGuido Günther .remove = vcnl4000_remove, 133662a1efb9SPeter Meerwald }; 133762a1efb9SPeter Meerwald 133862a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 133962a1efb9SPeter Meerwald 134062a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 13418fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 134262a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 134362a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 1344