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 */ 6354667612SMårten Lindahl #define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */ 6454667612SMårten Lindahl #define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */ 65be38866fSTomas Novotny #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 66be38866fSTomas Novotny #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 6754667612SMårten Lindahl #define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */ 68854965b7SAstrid Rost #define VCNL4200_INT_FLAGS 0x0d /* Interrupt register */ 69be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 70be38866fSTomas Novotny 715a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 725a441aadSAngus Ainslie (Purism) 7362a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 74ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 75ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 76ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 77ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 78d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 79d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 80d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 81d35567fcSMathieu Othacehe 82e21b5b1fSMårten Lindahl #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) 83e21b5b1fSMårten Lindahl #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) 8485e2c6a2SMårten Lindahl #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ 8554667612SMårten Lindahl #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ 8654667612SMårten Lindahl #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ 8754667612SMårten Lindahl #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ 88e21b5b1fSMårten Lindahl 89d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 90d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 91d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 92d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 93d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 94d35567fcSMathieu Othacehe 95d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 96d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 97d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 98d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 99d35567fcSMathieu Othacehe 100d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 101d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 102d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 103d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 104d35567fcSMathieu Othacehe 105f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 106f6889c1bSMathieu Othacehe {1, 950000}, 107f6889c1bSMathieu Othacehe {3, 906250}, 108f6889c1bSMathieu Othacehe {7, 812500}, 109f6889c1bSMathieu Othacehe {16, 625000}, 110f6889c1bSMathieu Othacehe {31, 250000}, 111f6889c1bSMathieu Othacehe {62, 500000}, 112f6889c1bSMathieu Othacehe {125, 0}, 113f6889c1bSMathieu Othacehe {250, 0}, 114f6889c1bSMathieu Othacehe }; 11562a1efb9SPeter Meerwald 11685e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = { 11785e2c6a2SMårten Lindahl {0, 100}, 11885e2c6a2SMårten Lindahl {0, 150}, 11985e2c6a2SMårten Lindahl {0, 200}, 12085e2c6a2SMårten Lindahl {0, 250}, 12185e2c6a2SMårten Lindahl {0, 300}, 12285e2c6a2SMårten Lindahl {0, 350}, 12385e2c6a2SMårten Lindahl {0, 400}, 12485e2c6a2SMårten Lindahl {0, 800}, 12585e2c6a2SMårten Lindahl }; 12685e2c6a2SMårten Lindahl 127e55c96daSAstrid Rost static const int vcnl4200_ps_it_times[][2] = { 128e55c96daSAstrid Rost {0, 96}, 129e55c96daSAstrid Rost {0, 144}, 130e55c96daSAstrid Rost {0, 192}, 131e55c96daSAstrid Rost {0, 384}, 132e55c96daSAstrid Rost {0, 768}, 133e55c96daSAstrid Rost {0, 864}, 134e55c96daSAstrid Rost }; 135e55c96daSAstrid Rost 1365e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1375e00708dSGuido Günther 1381ebc787aSTomas Novotny enum vcnl4000_device_ids { 1391ebc787aSTomas Novotny VCNL4000, 14050c50b97STomas Novotny VCNL4010, 1415a441aadSAngus Ainslie (Purism) VCNL4040, 142be38866fSTomas Novotny VCNL4200, 143be38866fSTomas Novotny }; 144be38866fSTomas Novotny 145be38866fSTomas Novotny struct vcnl4200_channel { 146be38866fSTomas Novotny u8 reg; 147be38866fSTomas Novotny ktime_t last_measurement; 148be38866fSTomas Novotny ktime_t sampling_rate; 149be38866fSTomas Novotny struct mutex lock; 1501ebc787aSTomas Novotny }; 1511ebc787aSTomas Novotny 15262a1efb9SPeter Meerwald struct vcnl4000_data { 15362a1efb9SPeter Meerwald struct i2c_client *client; 1541ebc787aSTomas Novotny enum vcnl4000_device_ids id; 1551ebc787aSTomas Novotny int rev; 1561ebc787aSTomas Novotny int al_scale; 15754667612SMårten Lindahl u8 ps_int; /* proximity interrupt mode */ 1581ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 159be38866fSTomas Novotny struct mutex vcnl4000_lock; 160be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 161be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 162f5a98e1fSGuido Günther uint32_t near_level; 16362a1efb9SPeter Meerwald }; 16462a1efb9SPeter Meerwald 1651ebc787aSTomas Novotny struct vcnl4000_chip_spec { 1661ebc787aSTomas Novotny const char *prod; 167d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 168d35567fcSMathieu Othacehe const int num_channels; 169d35567fcSMathieu Othacehe const struct iio_info *info; 170bfb6cfeeSMårten Lindahl const struct iio_buffer_setup_ops *buffer_setup_ops; 1711ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 1721ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 1731ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 1745e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 175bfb6cfeeSMårten Lindahl irqreturn_t (*irq_thread)(int irq, void *priv); 176bfb6cfeeSMårten Lindahl irqreturn_t (*trig_buffer_func)(int irq, void *priv); 177854965b7SAstrid Rost 178854965b7SAstrid Rost u8 int_reg; 179e55c96daSAstrid Rost const int(*ps_it_times)[][2]; 180e55c96daSAstrid Rost const int num_ps_it_times; 1811ebc787aSTomas Novotny }; 1821ebc787aSTomas Novotny 18362a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 1841ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 18550c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 18650c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 1875a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 188be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 18962a1efb9SPeter Meerwald { } 19062a1efb9SPeter Meerwald }; 19162a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 19262a1efb9SPeter Meerwald 1935e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 1945e00708dSGuido Günther { 1955e00708dSGuido Günther /* no suspend op */ 1965e00708dSGuido Günther return 0; 1975e00708dSGuido Günther } 1985e00708dSGuido Günther 1991ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 2001ebc787aSTomas Novotny { 2011ebc787aSTomas Novotny int ret, prod_id; 2021ebc787aSTomas Novotny 2031ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 2041ebc787aSTomas Novotny if (ret < 0) 2051ebc787aSTomas Novotny return ret; 2061ebc787aSTomas Novotny 2071ebc787aSTomas Novotny prod_id = ret >> 4; 20858bf9aceSTomas Novotny switch (prod_id) { 20958bf9aceSTomas Novotny case VCNL4000_PROD_ID: 21058bf9aceSTomas Novotny if (data->id != VCNL4000) 21158bf9aceSTomas Novotny dev_warn(&data->client->dev, 21258bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 21358bf9aceSTomas Novotny break; 21458bf9aceSTomas Novotny case VCNL4010_PROD_ID: 21558bf9aceSTomas Novotny if (data->id != VCNL4010) 21658bf9aceSTomas Novotny dev_warn(&data->client->dev, 21758bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 21858bf9aceSTomas Novotny break; 21958bf9aceSTomas Novotny default: 2201ebc787aSTomas Novotny return -ENODEV; 22158bf9aceSTomas Novotny } 2221ebc787aSTomas Novotny 2231ebc787aSTomas Novotny data->rev = ret & 0xf; 2241ebc787aSTomas Novotny data->al_scale = 250000; 225be38866fSTomas Novotny 2265e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 227be38866fSTomas Novotny }; 228be38866fSTomas Novotny 229e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) 2305e00708dSGuido Günther { 2315e00708dSGuido Günther int ret; 2325e00708dSGuido Günther 233e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 234e21b5b1fSMårten Lindahl 235e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 236e21b5b1fSMårten Lindahl if (ret < 0) 237e21b5b1fSMårten Lindahl goto out; 238e21b5b1fSMårten Lindahl 239e21b5b1fSMårten Lindahl if (en) 240e21b5b1fSMårten Lindahl ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; 241e21b5b1fSMårten Lindahl else 242e21b5b1fSMårten Lindahl ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; 243e21b5b1fSMårten Lindahl 244e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); 245e21b5b1fSMårten Lindahl 246e21b5b1fSMårten Lindahl out: 247e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 248e21b5b1fSMårten Lindahl 249e21b5b1fSMårten Lindahl return ret; 250e21b5b1fSMårten Lindahl } 251e21b5b1fSMårten Lindahl 252e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) 253e21b5b1fSMårten Lindahl { 254e21b5b1fSMårten Lindahl int ret; 255e21b5b1fSMårten Lindahl 256e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 257e21b5b1fSMårten Lindahl 258e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 259e21b5b1fSMårten Lindahl if (ret < 0) 260e21b5b1fSMårten Lindahl goto out; 261e21b5b1fSMårten Lindahl 262e21b5b1fSMårten Lindahl if (en) 263e21b5b1fSMårten Lindahl ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; 264e21b5b1fSMårten Lindahl else 265e21b5b1fSMårten Lindahl ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; 266e21b5b1fSMårten Lindahl 267e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); 268e21b5b1fSMårten Lindahl 269e21b5b1fSMårten Lindahl out: 270e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 271e21b5b1fSMårten Lindahl 272e21b5b1fSMårten Lindahl return ret; 273e21b5b1fSMårten Lindahl } 274e21b5b1fSMårten Lindahl 275e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 276e21b5b1fSMårten Lindahl { 277e21b5b1fSMårten Lindahl int ret; 278e21b5b1fSMårten Lindahl 27954667612SMårten Lindahl /* Do not power down if interrupts are enabled */ 28054667612SMårten Lindahl if (!on && data->ps_int) 28154667612SMårten Lindahl return 0; 28254667612SMårten Lindahl 283e21b5b1fSMårten Lindahl ret = vcnl4000_write_als_enable(data, on); 2845e00708dSGuido Günther if (ret < 0) 2855e00708dSGuido Günther return ret; 2865e00708dSGuido Günther 287e21b5b1fSMårten Lindahl ret = vcnl4000_write_ps_enable(data, on); 2885e00708dSGuido Günther if (ret < 0) 2895e00708dSGuido Günther return ret; 2905e00708dSGuido Günther 2915e00708dSGuido Günther if (on) { 2925e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 2935e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 2945e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 2955e00708dSGuido Günther } 2965e00708dSGuido Günther 2975e00708dSGuido Günther return 0; 2985e00708dSGuido Günther } 2995e00708dSGuido Günther 300be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 301be38866fSTomas Novotny { 3025a441aadSAngus Ainslie (Purism) int ret, id; 303be38866fSTomas Novotny 304be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 305be38866fSTomas Novotny if (ret < 0) 306be38866fSTomas Novotny return ret; 307be38866fSTomas Novotny 3085a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3095a441aadSAngus Ainslie (Purism) 3105a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 3115a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 3125a441aadSAngus Ainslie (Purism) if (ret < 0) 3135a441aadSAngus Ainslie (Purism) return ret; 3145a441aadSAngus Ainslie (Purism) 3155a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3165a441aadSAngus Ainslie (Purism) 3175a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 318be38866fSTomas Novotny return -ENODEV; 3195a441aadSAngus Ainslie (Purism) } 3205a441aadSAngus Ainslie (Purism) 3215a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 322be38866fSTomas Novotny 323be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 32454667612SMårten Lindahl data->ps_int = 0; 325be38866fSTomas Novotny 326be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 327be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 3285a441aadSAngus Ainslie (Purism) switch (id) { 3295a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 330b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 331b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 332b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 333b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 334bc80573eSGuido Günther data->al_scale = 24000; 3355a441aadSAngus Ainslie (Purism) break; 3365a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 3372ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 3382ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 3392ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 3402ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 341bc80573eSGuido Günther data->al_scale = 120000; 3425a441aadSAngus Ainslie (Purism) break; 3435a441aadSAngus Ainslie (Purism) } 344be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 345be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 3461ebc787aSTomas Novotny 3475e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 3485e00708dSGuido Günther if (ret < 0) 3495e00708dSGuido Günther return ret; 3505e00708dSGuido Günther 3511ebc787aSTomas Novotny return 0; 3521ebc787aSTomas Novotny }; 3531ebc787aSTomas Novotny 354816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 355816956c3SMathieu Othacehe { 356816956c3SMathieu Othacehe s32 ret; 357816956c3SMathieu Othacehe 358816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 359816956c3SMathieu Othacehe if (ret < 0) 360816956c3SMathieu Othacehe return ret; 361816956c3SMathieu Othacehe 362816956c3SMathieu Othacehe *val = ret; 363816956c3SMathieu Othacehe return 0; 364816956c3SMathieu Othacehe } 365816956c3SMathieu Othacehe 366816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 367816956c3SMathieu Othacehe { 368816956c3SMathieu Othacehe if (val > U16_MAX) 369816956c3SMathieu Othacehe return -ERANGE; 370816956c3SMathieu Othacehe 371816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 372816956c3SMathieu Othacehe } 373816956c3SMathieu Othacehe 374816956c3SMathieu Othacehe 37562a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 37662a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 37762a1efb9SPeter Meerwald { 37862a1efb9SPeter Meerwald int tries = 20; 37962a1efb9SPeter Meerwald int ret; 38062a1efb9SPeter Meerwald 381be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 382ff34ed6dSPeter Meerwald-Stadler 38362a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 38462a1efb9SPeter Meerwald req_mask); 38562a1efb9SPeter Meerwald if (ret < 0) 386ff34ed6dSPeter Meerwald-Stadler goto fail; 38762a1efb9SPeter Meerwald 38862a1efb9SPeter Meerwald /* wait for data to become ready */ 38962a1efb9SPeter Meerwald while (tries--) { 39062a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 39162a1efb9SPeter Meerwald if (ret < 0) 392ff34ed6dSPeter Meerwald-Stadler goto fail; 39362a1efb9SPeter Meerwald if (ret & rdy_mask) 39462a1efb9SPeter Meerwald break; 39562a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 39662a1efb9SPeter Meerwald } 39762a1efb9SPeter Meerwald 39862a1efb9SPeter Meerwald if (tries < 0) { 39962a1efb9SPeter Meerwald dev_err(&data->client->dev, 40062a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 401ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 402ff34ed6dSPeter Meerwald-Stadler goto fail; 40362a1efb9SPeter Meerwald } 40462a1efb9SPeter Meerwald 405816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 40662a1efb9SPeter Meerwald if (ret < 0) 407ff34ed6dSPeter Meerwald-Stadler goto fail; 40862a1efb9SPeter Meerwald 409be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 41062a1efb9SPeter Meerwald 41162a1efb9SPeter Meerwald return 0; 412ff34ed6dSPeter Meerwald-Stadler 413ff34ed6dSPeter Meerwald-Stadler fail: 414be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 415ff34ed6dSPeter Meerwald-Stadler return ret; 41662a1efb9SPeter Meerwald } 41762a1efb9SPeter Meerwald 418be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 419be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 420be38866fSTomas Novotny { 421be38866fSTomas Novotny int ret; 422be38866fSTomas Novotny s64 delta; 423be38866fSTomas Novotny ktime_t next_measurement; 424be38866fSTomas Novotny 425be38866fSTomas Novotny mutex_lock(&chan->lock); 426be38866fSTomas Novotny 427be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 428be38866fSTomas Novotny chan->sampling_rate); 429be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 430be38866fSTomas Novotny if (delta > 0) 431be38866fSTomas Novotny usleep_range(delta, delta + 500); 432be38866fSTomas Novotny chan->last_measurement = ktime_get(); 433be38866fSTomas Novotny 434be38866fSTomas Novotny mutex_unlock(&chan->lock); 435be38866fSTomas Novotny 436be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 437be38866fSTomas Novotny if (ret < 0) 438be38866fSTomas Novotny return ret; 439be38866fSTomas Novotny 440be38866fSTomas Novotny *val = ret; 441be38866fSTomas Novotny 442be38866fSTomas Novotny return 0; 443be38866fSTomas Novotny } 444be38866fSTomas Novotny 4451ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 4461ebc787aSTomas Novotny { 4471ebc787aSTomas Novotny return vcnl4000_measure(data, 4481ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 4491ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 4501ebc787aSTomas Novotny } 4511ebc787aSTomas Novotny 452be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 453be38866fSTomas Novotny { 454be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 455be38866fSTomas Novotny } 456be38866fSTomas Novotny 4571ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 4581ebc787aSTomas Novotny { 4591ebc787aSTomas Novotny return vcnl4000_measure(data, 4601ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 4611ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 4621ebc787aSTomas Novotny } 4631ebc787aSTomas Novotny 464be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 465be38866fSTomas Novotny { 466be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 467be38866fSTomas Novotny } 468be38866fSTomas Novotny 469f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 470f6889c1bSMathieu Othacehe int *val2) 471f6889c1bSMathieu Othacehe { 472f6889c1bSMathieu Othacehe int ret; 473f6889c1bSMathieu Othacehe 474f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 475f6889c1bSMathieu Othacehe if (ret < 0) 476f6889c1bSMathieu Othacehe return ret; 477f6889c1bSMathieu Othacehe 478f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 479f6889c1bSMathieu Othacehe return -EINVAL; 480f6889c1bSMathieu Othacehe 481f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 482f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 483f6889c1bSMathieu Othacehe 484f6889c1bSMathieu Othacehe return 0; 485f6889c1bSMathieu Othacehe } 486f6889c1bSMathieu Othacehe 487d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 488f5a98e1fSGuido Günther { 489d35567fcSMathieu Othacehe int ret; 490f5a98e1fSGuido Günther 491d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 492d35567fcSMathieu Othacehe if (ret < 0) 493d35567fcSMathieu Othacehe return false; 494d35567fcSMathieu Othacehe 495d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 496f5a98e1fSGuido Günther } 497f5a98e1fSGuido Günther 4985e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 4995e00708dSGuido Günther { 5005e00708dSGuido Günther struct device *dev = &data->client->dev; 5015e00708dSGuido Günther int ret; 5025e00708dSGuido Günther 5035e00708dSGuido Günther if (on) { 504db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 5055e00708dSGuido Günther } else { 5065e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 5075e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 5085e00708dSGuido Günther } 5095e00708dSGuido Günther 5105e00708dSGuido Günther return ret; 5115e00708dSGuido Günther } 5125e00708dSGuido Günther 51385e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) 51485e2c6a2SMårten Lindahl { 51585e2c6a2SMårten Lindahl int ret; 51685e2c6a2SMårten Lindahl 51785e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 51885e2c6a2SMårten Lindahl if (ret < 0) 51985e2c6a2SMårten Lindahl return ret; 52085e2c6a2SMårten Lindahl 52185e2c6a2SMårten Lindahl ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 52285e2c6a2SMårten Lindahl 523e55c96daSAstrid Rost if (ret >= data->chip_spec->num_ps_it_times) 52485e2c6a2SMårten Lindahl return -EINVAL; 52585e2c6a2SMårten Lindahl 526e55c96daSAstrid Rost *val = (*data->chip_spec->ps_it_times)[ret][0]; 527e55c96daSAstrid Rost *val2 = (*data->chip_spec->ps_it_times)[ret][1]; 52885e2c6a2SMårten Lindahl 52985e2c6a2SMårten Lindahl return 0; 53085e2c6a2SMårten Lindahl } 53185e2c6a2SMårten Lindahl 53285e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) 53385e2c6a2SMårten Lindahl { 53485e2c6a2SMårten Lindahl unsigned int i; 53585e2c6a2SMårten Lindahl int ret, index = -1; 53685e2c6a2SMårten Lindahl u16 regval; 53785e2c6a2SMårten Lindahl 538e55c96daSAstrid Rost for (i = 0; i < data->chip_spec->num_ps_it_times; i++) { 539e55c96daSAstrid Rost if (val == (*data->chip_spec->ps_it_times)[i][1]) { 54085e2c6a2SMårten Lindahl index = i; 54185e2c6a2SMårten Lindahl break; 54285e2c6a2SMårten Lindahl } 54385e2c6a2SMårten Lindahl } 54485e2c6a2SMårten Lindahl 54585e2c6a2SMårten Lindahl if (index < 0) 54685e2c6a2SMårten Lindahl return -EINVAL; 54785e2c6a2SMårten Lindahl 548e55c96daSAstrid Rost data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC); 549e55c96daSAstrid Rost 55085e2c6a2SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 55185e2c6a2SMårten Lindahl 55285e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 55385e2c6a2SMårten Lindahl if (ret < 0) 55485e2c6a2SMårten Lindahl goto out; 55585e2c6a2SMårten Lindahl 55685e2c6a2SMårten Lindahl regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | 55785e2c6a2SMårten Lindahl FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); 55885e2c6a2SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 55985e2c6a2SMårten Lindahl regval); 56085e2c6a2SMårten Lindahl 56185e2c6a2SMårten Lindahl out: 56285e2c6a2SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 56385e2c6a2SMårten Lindahl return ret; 56485e2c6a2SMårten Lindahl } 56585e2c6a2SMårten Lindahl 56662a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 56762a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 56862a1efb9SPeter Meerwald int *val, int *val2, long mask) 56962a1efb9SPeter Meerwald { 5705d693139SPeter Meerwald-Stadler int ret; 57162a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 57262a1efb9SPeter Meerwald 57362a1efb9SPeter Meerwald switch (mask) { 57462a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 5755e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 5765e00708dSGuido Günther if (ret < 0) 5775e00708dSGuido Günther return ret; 5785e00708dSGuido Günther 57962a1efb9SPeter Meerwald switch (chan->type) { 58062a1efb9SPeter Meerwald case IIO_LIGHT: 5811ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 5824a818643SGuido Günther if (!ret) 5834a818643SGuido Günther ret = IIO_VAL_INT; 5844a818643SGuido Günther break; 58562a1efb9SPeter Meerwald case IIO_PROXIMITY: 5861ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 5874a818643SGuido Günther if (!ret) 5884a818643SGuido Günther ret = IIO_VAL_INT; 5894a818643SGuido Günther break; 59062a1efb9SPeter Meerwald default: 5914a818643SGuido Günther ret = -EINVAL; 59262a1efb9SPeter Meerwald } 5935e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 5944a818643SGuido Günther return ret; 59562a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 5965d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 5975d693139SPeter Meerwald-Stadler return -EINVAL; 5985d693139SPeter Meerwald-Stadler 59962a1efb9SPeter Meerwald *val = 0; 6001ebc787aSTomas Novotny *val2 = data->al_scale; 6015d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 60285e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 603*2be17b68SAstrid Rost switch (chan->type) { 604*2be17b68SAstrid Rost case IIO_PROXIMITY: 60585e2c6a2SMårten Lindahl ret = vcnl4040_read_ps_it(data, val, val2); 606*2be17b68SAstrid Rost break; 607*2be17b68SAstrid Rost default: 608*2be17b68SAstrid Rost return -EINVAL; 609*2be17b68SAstrid Rost } 61085e2c6a2SMårten Lindahl if (ret < 0) 61185e2c6a2SMårten Lindahl return ret; 61285e2c6a2SMårten Lindahl return IIO_VAL_INT_PLUS_MICRO; 61385e2c6a2SMårten Lindahl default: 61485e2c6a2SMårten Lindahl return -EINVAL; 61585e2c6a2SMårten Lindahl } 61685e2c6a2SMårten Lindahl } 61785e2c6a2SMårten Lindahl 61885e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev, 61985e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 62085e2c6a2SMårten Lindahl int val, int val2, long mask) 62185e2c6a2SMårten Lindahl { 62285e2c6a2SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 62385e2c6a2SMårten Lindahl 62485e2c6a2SMårten Lindahl switch (mask) { 62585e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 62685e2c6a2SMårten Lindahl if (val != 0) 62785e2c6a2SMårten Lindahl return -EINVAL; 628*2be17b68SAstrid Rost switch (chan->type) { 629*2be17b68SAstrid Rost case IIO_PROXIMITY: 63085e2c6a2SMårten Lindahl return vcnl4040_write_ps_it(data, val2); 63185e2c6a2SMårten Lindahl default: 63285e2c6a2SMårten Lindahl return -EINVAL; 63385e2c6a2SMårten Lindahl } 634*2be17b68SAstrid Rost default: 635*2be17b68SAstrid Rost return -EINVAL; 636*2be17b68SAstrid Rost } 63785e2c6a2SMårten Lindahl } 63885e2c6a2SMårten Lindahl 63985e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev, 64085e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 64185e2c6a2SMårten Lindahl const int **vals, int *type, int *length, 64285e2c6a2SMårten Lindahl long mask) 64385e2c6a2SMårten Lindahl { 644e55c96daSAstrid Rost struct vcnl4000_data *data = iio_priv(indio_dev); 645e55c96daSAstrid Rost 64685e2c6a2SMårten Lindahl switch (mask) { 64785e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 648*2be17b68SAstrid Rost switch (chan->type) { 649*2be17b68SAstrid Rost case IIO_PROXIMITY: 650e55c96daSAstrid Rost *vals = (int *)(*data->chip_spec->ps_it_times); 651e55c96daSAstrid Rost *length = 2 * data->chip_spec->num_ps_it_times; 652*2be17b68SAstrid Rost break; 653*2be17b68SAstrid Rost default: 654*2be17b68SAstrid Rost return -EINVAL; 655*2be17b68SAstrid Rost } 656*2be17b68SAstrid Rost *type = IIO_VAL_INT_PLUS_MICRO; 65785e2c6a2SMårten Lindahl return IIO_AVAIL_LIST; 65862a1efb9SPeter Meerwald default: 6595d693139SPeter Meerwald-Stadler return -EINVAL; 66062a1efb9SPeter Meerwald } 66162a1efb9SPeter Meerwald } 66262a1efb9SPeter Meerwald 663d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 664d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 665d35567fcSMathieu Othacehe int *val, int *val2, long mask) 666d35567fcSMathieu Othacehe { 667d35567fcSMathieu Othacehe int ret; 668d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 669d35567fcSMathieu Othacehe 670d35567fcSMathieu Othacehe switch (mask) { 671d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 672d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 673d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 674d35567fcSMathieu Othacehe if (ret) 675d35567fcSMathieu Othacehe return ret; 676d35567fcSMathieu Othacehe 677d35567fcSMathieu Othacehe /* Protect against event capture. */ 678d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 679d35567fcSMathieu Othacehe ret = -EBUSY; 680d35567fcSMathieu Othacehe } else { 681d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 682d35567fcSMathieu Othacehe mask); 683d35567fcSMathieu Othacehe } 684d35567fcSMathieu Othacehe 685d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 686d35567fcSMathieu Othacehe return ret; 687f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 688f6889c1bSMathieu Othacehe switch (chan->type) { 689f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 690f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 691f6889c1bSMathieu Othacehe if (ret < 0) 692f6889c1bSMathieu Othacehe return ret; 693f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 694d35567fcSMathieu Othacehe default: 695d35567fcSMathieu Othacehe return -EINVAL; 696d35567fcSMathieu Othacehe } 697f6889c1bSMathieu Othacehe default: 698f6889c1bSMathieu Othacehe return -EINVAL; 699f6889c1bSMathieu Othacehe } 700f6889c1bSMathieu Othacehe } 701f6889c1bSMathieu Othacehe 702f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 703f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 704f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 705f6889c1bSMathieu Othacehe long mask) 706f6889c1bSMathieu Othacehe { 707f6889c1bSMathieu Othacehe switch (mask) { 708f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 709f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 710f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 711f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 712f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 713f6889c1bSMathieu Othacehe default: 714f6889c1bSMathieu Othacehe return -EINVAL; 715f6889c1bSMathieu Othacehe } 716f6889c1bSMathieu Othacehe } 717f6889c1bSMathieu Othacehe 718f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 719f6889c1bSMathieu Othacehe int val2) 720f6889c1bSMathieu Othacehe { 721f6889c1bSMathieu Othacehe unsigned int i; 722f6889c1bSMathieu Othacehe int index = -1; 723f6889c1bSMathieu Othacehe 724f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 725f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 726f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 727f6889c1bSMathieu Othacehe index = i; 728f6889c1bSMathieu Othacehe break; 729f6889c1bSMathieu Othacehe } 730f6889c1bSMathieu Othacehe } 731f6889c1bSMathieu Othacehe 732f6889c1bSMathieu Othacehe if (index < 0) 733f6889c1bSMathieu Othacehe return -EINVAL; 734f6889c1bSMathieu Othacehe 735f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 736f6889c1bSMathieu Othacehe index); 737f6889c1bSMathieu Othacehe } 738f6889c1bSMathieu Othacehe 739f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 740f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 741f6889c1bSMathieu Othacehe int val, int val2, long mask) 742f6889c1bSMathieu Othacehe { 743f6889c1bSMathieu Othacehe int ret; 744f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 745f6889c1bSMathieu Othacehe 746f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 747f6889c1bSMathieu Othacehe if (ret) 748f6889c1bSMathieu Othacehe return ret; 749f6889c1bSMathieu Othacehe 750f6889c1bSMathieu Othacehe /* Protect against event capture. */ 751f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 752f6889c1bSMathieu Othacehe ret = -EBUSY; 753f6889c1bSMathieu Othacehe goto end; 754f6889c1bSMathieu Othacehe } 755f6889c1bSMathieu Othacehe 756f6889c1bSMathieu Othacehe switch (mask) { 757f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 758f6889c1bSMathieu Othacehe switch (chan->type) { 759f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 760f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 761f6889c1bSMathieu Othacehe goto end; 762f6889c1bSMathieu Othacehe default: 763f6889c1bSMathieu Othacehe ret = -EINVAL; 764f6889c1bSMathieu Othacehe goto end; 765f6889c1bSMathieu Othacehe } 766f6889c1bSMathieu Othacehe default: 767f6889c1bSMathieu Othacehe ret = -EINVAL; 768f6889c1bSMathieu Othacehe goto end; 769f6889c1bSMathieu Othacehe } 770f6889c1bSMathieu Othacehe 771f6889c1bSMathieu Othacehe end: 772f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 773f6889c1bSMathieu Othacehe return ret; 774d35567fcSMathieu Othacehe } 775d35567fcSMathieu Othacehe 776d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 777d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 778d35567fcSMathieu Othacehe enum iio_event_type type, 779d35567fcSMathieu Othacehe enum iio_event_direction dir, 780d35567fcSMathieu Othacehe enum iio_event_info info, 781d35567fcSMathieu Othacehe int *val, int *val2) 782d35567fcSMathieu Othacehe { 783d35567fcSMathieu Othacehe int ret; 784d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 785d35567fcSMathieu Othacehe 786d35567fcSMathieu Othacehe switch (info) { 787d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 788d35567fcSMathieu Othacehe switch (dir) { 789d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 790d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 791d35567fcSMathieu Othacehe val); 792d35567fcSMathieu Othacehe if (ret < 0) 793d35567fcSMathieu Othacehe return ret; 794d35567fcSMathieu Othacehe return IIO_VAL_INT; 795d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 796d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 797d35567fcSMathieu Othacehe val); 798d35567fcSMathieu Othacehe if (ret < 0) 799d35567fcSMathieu Othacehe return ret; 800d35567fcSMathieu Othacehe return IIO_VAL_INT; 801d35567fcSMathieu Othacehe default: 802d35567fcSMathieu Othacehe return -EINVAL; 803d35567fcSMathieu Othacehe } 804d35567fcSMathieu Othacehe default: 805d35567fcSMathieu Othacehe return -EINVAL; 806d35567fcSMathieu Othacehe } 807d35567fcSMathieu Othacehe } 808d35567fcSMathieu Othacehe 809d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 810d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 811d35567fcSMathieu Othacehe enum iio_event_type type, 812d35567fcSMathieu Othacehe enum iio_event_direction dir, 813d35567fcSMathieu Othacehe enum iio_event_info info, 814d35567fcSMathieu Othacehe int val, int val2) 815d35567fcSMathieu Othacehe { 816d35567fcSMathieu Othacehe int ret; 817d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 818d35567fcSMathieu Othacehe 819d35567fcSMathieu Othacehe switch (info) { 820d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 821d35567fcSMathieu Othacehe switch (dir) { 822d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 823d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 824d35567fcSMathieu Othacehe val); 825d35567fcSMathieu Othacehe if (ret < 0) 826d35567fcSMathieu Othacehe return ret; 827d35567fcSMathieu Othacehe return IIO_VAL_INT; 828d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 829d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 830d35567fcSMathieu Othacehe val); 831d35567fcSMathieu Othacehe if (ret < 0) 832d35567fcSMathieu Othacehe return ret; 833d35567fcSMathieu Othacehe return IIO_VAL_INT; 834d35567fcSMathieu Othacehe default: 835d35567fcSMathieu Othacehe return -EINVAL; 836d35567fcSMathieu Othacehe } 837d35567fcSMathieu Othacehe default: 838d35567fcSMathieu Othacehe return -EINVAL; 839d35567fcSMathieu Othacehe } 840d35567fcSMathieu Othacehe } 841d35567fcSMathieu Othacehe 84254667612SMårten Lindahl static int vcnl4040_read_event(struct iio_dev *indio_dev, 84354667612SMårten Lindahl const struct iio_chan_spec *chan, 84454667612SMårten Lindahl enum iio_event_type type, 84554667612SMårten Lindahl enum iio_event_direction dir, 84654667612SMårten Lindahl enum iio_event_info info, 84754667612SMårten Lindahl int *val, int *val2) 84854667612SMårten Lindahl { 84954667612SMårten Lindahl int ret; 85054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 85154667612SMårten Lindahl 852*2be17b68SAstrid Rost switch (chan->type) { 853*2be17b68SAstrid Rost case IIO_PROXIMITY: 854*2be17b68SAstrid Rost switch (info) { 855*2be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 85654667612SMårten Lindahl switch (dir) { 85754667612SMårten Lindahl case IIO_EV_DIR_RISING: 85854667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 85954667612SMårten Lindahl VCNL4040_PS_THDH_LM); 860*2be17b68SAstrid Rost break; 86154667612SMårten Lindahl case IIO_EV_DIR_FALLING: 86254667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 86354667612SMårten Lindahl VCNL4040_PS_THDL_LM); 864*2be17b68SAstrid Rost break; 865*2be17b68SAstrid Rost default: 866*2be17b68SAstrid Rost return -EINVAL; 867*2be17b68SAstrid Rost } 868*2be17b68SAstrid Rost break; 869*2be17b68SAstrid Rost default: 870*2be17b68SAstrid Rost return -EINVAL; 871*2be17b68SAstrid Rost } 872*2be17b68SAstrid Rost break; 873*2be17b68SAstrid Rost default: 874*2be17b68SAstrid Rost return -EINVAL; 875*2be17b68SAstrid Rost } 87654667612SMårten Lindahl if (ret < 0) 87754667612SMårten Lindahl return ret; 87854667612SMårten Lindahl *val = ret; 87954667612SMårten Lindahl return IIO_VAL_INT; 88054667612SMårten Lindahl } 88154667612SMårten Lindahl 88254667612SMårten Lindahl static int vcnl4040_write_event(struct iio_dev *indio_dev, 88354667612SMårten Lindahl const struct iio_chan_spec *chan, 88454667612SMårten Lindahl enum iio_event_type type, 88554667612SMårten Lindahl enum iio_event_direction dir, 88654667612SMårten Lindahl enum iio_event_info info, 88754667612SMårten Lindahl int val, int val2) 88854667612SMårten Lindahl { 88954667612SMårten Lindahl int ret; 89054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 89154667612SMårten Lindahl 892*2be17b68SAstrid Rost switch (chan->type) { 893*2be17b68SAstrid Rost case IIO_PROXIMITY: 894*2be17b68SAstrid Rost switch (info) { 895*2be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 89654667612SMårten Lindahl switch (dir) { 89754667612SMårten Lindahl case IIO_EV_DIR_RISING: 89854667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 899*2be17b68SAstrid Rost VCNL4040_PS_THDH_LM, 900*2be17b68SAstrid Rost val); 901*2be17b68SAstrid Rost break; 90254667612SMårten Lindahl case IIO_EV_DIR_FALLING: 90354667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 904*2be17b68SAstrid Rost VCNL4040_PS_THDL_LM, 905*2be17b68SAstrid Rost val); 906*2be17b68SAstrid Rost break; 90754667612SMårten Lindahl default: 90854667612SMårten Lindahl return -EINVAL; 90954667612SMårten Lindahl } 910*2be17b68SAstrid Rost break; 911*2be17b68SAstrid Rost default: 912*2be17b68SAstrid Rost return -EINVAL; 913*2be17b68SAstrid Rost } 914*2be17b68SAstrid Rost break; 915*2be17b68SAstrid Rost default: 916*2be17b68SAstrid Rost return -EINVAL; 917*2be17b68SAstrid Rost } 918*2be17b68SAstrid Rost if (ret < 0) 919*2be17b68SAstrid Rost return ret; 920*2be17b68SAstrid Rost return IIO_VAL_INT; 92154667612SMårten Lindahl } 92254667612SMårten Lindahl 923d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 924d35567fcSMathieu Othacehe { 925d35567fcSMathieu Othacehe int ret; 926d35567fcSMathieu Othacehe 927d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 928d35567fcSMathieu Othacehe if (ret < 0) 929d35567fcSMathieu Othacehe return false; 930d35567fcSMathieu Othacehe 931d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 932d35567fcSMathieu Othacehe } 933d35567fcSMathieu Othacehe 934d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 935d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 936d35567fcSMathieu Othacehe enum iio_event_type type, 937d35567fcSMathieu Othacehe enum iio_event_direction dir) 938d35567fcSMathieu Othacehe { 939d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 940d35567fcSMathieu Othacehe 941d35567fcSMathieu Othacehe switch (chan->type) { 942d35567fcSMathieu Othacehe case IIO_PROXIMITY: 943d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 944d35567fcSMathieu Othacehe default: 945d35567fcSMathieu Othacehe return -EINVAL; 946d35567fcSMathieu Othacehe } 947d35567fcSMathieu Othacehe } 948d35567fcSMathieu Othacehe 949d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 950d35567fcSMathieu Othacehe { 951d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 952d35567fcSMathieu Othacehe int ret; 953d35567fcSMathieu Othacehe int icr; 954d35567fcSMathieu Othacehe int command; 955d35567fcSMathieu Othacehe 956d35567fcSMathieu Othacehe if (state) { 957d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 958d35567fcSMathieu Othacehe if (ret) 959d35567fcSMathieu Othacehe return ret; 960d35567fcSMathieu Othacehe 961d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 962d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 963d35567fcSMathieu Othacehe 964d35567fcSMathieu Othacehe /* 965d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 966d35567fcSMathieu Othacehe * default. 967d35567fcSMathieu Othacehe */ 968d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 969d35567fcSMathieu Othacehe } else { 970d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 971d35567fcSMathieu Othacehe return 0; 972d35567fcSMathieu Othacehe 973d35567fcSMathieu Othacehe command = 0; 974d35567fcSMathieu Othacehe icr = 0; 975d35567fcSMathieu Othacehe } 976d35567fcSMathieu Othacehe 977d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 978d35567fcSMathieu Othacehe command); 979d35567fcSMathieu Othacehe if (ret < 0) 980d35567fcSMathieu Othacehe goto end; 981d35567fcSMathieu Othacehe 982d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 983d35567fcSMathieu Othacehe 984d35567fcSMathieu Othacehe end: 985d35567fcSMathieu Othacehe if (state) 986d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 987d35567fcSMathieu Othacehe 988d35567fcSMathieu Othacehe return ret; 989d35567fcSMathieu Othacehe } 990d35567fcSMathieu Othacehe 991d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 992d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 993d35567fcSMathieu Othacehe enum iio_event_type type, 994d35567fcSMathieu Othacehe enum iio_event_direction dir, 995d35567fcSMathieu Othacehe int state) 996d35567fcSMathieu Othacehe { 997d35567fcSMathieu Othacehe switch (chan->type) { 998d35567fcSMathieu Othacehe case IIO_PROXIMITY: 999d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 1000d35567fcSMathieu Othacehe default: 1001d35567fcSMathieu Othacehe return -EINVAL; 1002d35567fcSMathieu Othacehe } 1003d35567fcSMathieu Othacehe } 1004d35567fcSMathieu Othacehe 100554667612SMårten Lindahl static int vcnl4040_read_event_config(struct iio_dev *indio_dev, 100654667612SMårten Lindahl const struct iio_chan_spec *chan, 100754667612SMårten Lindahl enum iio_event_type type, 100854667612SMårten Lindahl enum iio_event_direction dir) 100954667612SMårten Lindahl { 101054667612SMårten Lindahl int ret; 101154667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 101254667612SMårten Lindahl 1013*2be17b68SAstrid Rost switch (chan->type) { 1014*2be17b68SAstrid Rost case IIO_PROXIMITY: 101554667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 101654667612SMårten Lindahl if (ret < 0) 101754667612SMårten Lindahl return ret; 101854667612SMårten Lindahl 101954667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); 102054667612SMårten Lindahl 102154667612SMårten Lindahl return (dir == IIO_EV_DIR_RISING) ? 102254667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : 102354667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); 1024*2be17b68SAstrid Rost default: 1025*2be17b68SAstrid Rost return -EINVAL; 1026*2be17b68SAstrid Rost } 102754667612SMårten Lindahl } 102854667612SMårten Lindahl 102954667612SMårten Lindahl static int vcnl4040_write_event_config(struct iio_dev *indio_dev, 103054667612SMårten Lindahl const struct iio_chan_spec *chan, 103154667612SMårten Lindahl enum iio_event_type type, 103254667612SMårten Lindahl enum iio_event_direction dir, int state) 103354667612SMårten Lindahl { 1034*2be17b68SAstrid Rost int ret = -EINVAL; 103554667612SMårten Lindahl u16 val, mask; 103654667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 103754667612SMårten Lindahl 103854667612SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 103954667612SMårten Lindahl 1040*2be17b68SAstrid Rost switch (chan->type) { 1041*2be17b68SAstrid Rost case IIO_PROXIMITY: 104254667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 104354667612SMårten Lindahl if (ret < 0) 104454667612SMårten Lindahl goto out; 104554667612SMårten Lindahl 104654667612SMårten Lindahl if (dir == IIO_EV_DIR_RISING) 104754667612SMårten Lindahl mask = VCNL4040_PS_IF_AWAY; 104854667612SMårten Lindahl else 104954667612SMårten Lindahl mask = VCNL4040_PS_IF_CLOSE; 105054667612SMårten Lindahl 105154667612SMårten Lindahl val = state ? (ret | mask) : (ret & ~mask); 105254667612SMårten Lindahl 105354667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); 1054*2be17b68SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 1055*2be17b68SAstrid Rost val); 1056*2be17b68SAstrid Rost break; 1057*2be17b68SAstrid Rost default: 1058*2be17b68SAstrid Rost break; 1059*2be17b68SAstrid Rost } 106054667612SMårten Lindahl 106154667612SMårten Lindahl out: 106254667612SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 106354667612SMårten Lindahl data->chip_spec->set_power_state(data, data->ps_int != 0); 106454667612SMårten Lindahl 106554667612SMårten Lindahl return ret; 106654667612SMårten Lindahl } 106754667612SMårten Lindahl 106854667612SMårten Lindahl static irqreturn_t vcnl4040_irq_thread(int irq, void *p) 106954667612SMårten Lindahl { 107054667612SMårten Lindahl struct iio_dev *indio_dev = p; 107154667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 107254667612SMårten Lindahl int ret; 107354667612SMårten Lindahl 1074854965b7SAstrid Rost ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg); 107554667612SMårten Lindahl if (ret < 0) 107654667612SMårten Lindahl return IRQ_HANDLED; 107754667612SMårten Lindahl 107854667612SMårten Lindahl if (ret & VCNL4040_PS_IF_CLOSE) { 107954667612SMårten Lindahl iio_push_event(indio_dev, 108054667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 108154667612SMårten Lindahl IIO_EV_TYPE_THRESH, 108254667612SMårten Lindahl IIO_EV_DIR_RISING), 108354667612SMårten Lindahl iio_get_time_ns(indio_dev)); 108454667612SMårten Lindahl } 108554667612SMårten Lindahl 108654667612SMårten Lindahl if (ret & VCNL4040_PS_IF_AWAY) { 108754667612SMårten Lindahl iio_push_event(indio_dev, 108854667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 108954667612SMårten Lindahl IIO_EV_TYPE_THRESH, 109054667612SMårten Lindahl IIO_EV_DIR_FALLING), 109154667612SMårten Lindahl iio_get_time_ns(indio_dev)); 109254667612SMårten Lindahl } 109354667612SMårten Lindahl 109454667612SMårten Lindahl return IRQ_HANDLED; 109554667612SMårten Lindahl } 109654667612SMårten Lindahl 1097d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 1098d35567fcSMathieu Othacehe uintptr_t priv, 1099d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1100d35567fcSMathieu Othacehe char *buf) 1101d35567fcSMathieu Othacehe { 1102d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1103d35567fcSMathieu Othacehe 1104d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 1105d35567fcSMathieu Othacehe } 1106d35567fcSMathieu Othacehe 11073a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 11083a52d32aSMårten Lindahl { 11093a52d32aSMårten Lindahl struct iio_dev *indio_dev = p; 11103a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 11113a52d32aSMårten Lindahl unsigned long isr; 11123a52d32aSMårten Lindahl int ret; 11133a52d32aSMårten Lindahl 11143a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 11153a52d32aSMårten Lindahl if (ret < 0) 11163a52d32aSMårten Lindahl goto end; 11173a52d32aSMårten Lindahl 11183a52d32aSMårten Lindahl isr = ret; 11193a52d32aSMårten Lindahl 11203a52d32aSMårten Lindahl if (isr & VCNL4010_INT_THR) { 11213a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 11223a52d32aSMårten Lindahl iio_push_event(indio_dev, 11233a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 11243a52d32aSMårten Lindahl IIO_PROXIMITY, 11253a52d32aSMårten Lindahl 1, 11263a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 11273a52d32aSMårten Lindahl IIO_EV_DIR_FALLING), 11283a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 11293a52d32aSMårten Lindahl } 11303a52d32aSMårten Lindahl 11313a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 11323a52d32aSMårten Lindahl iio_push_event(indio_dev, 11333a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 11343a52d32aSMårten Lindahl IIO_PROXIMITY, 11353a52d32aSMårten Lindahl 1, 11363a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 11373a52d32aSMårten Lindahl IIO_EV_DIR_RISING), 11383a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 11393a52d32aSMårten Lindahl } 11403a52d32aSMårten Lindahl 11413a52d32aSMårten Lindahl i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 11423a52d32aSMårten Lindahl isr & VCNL4010_INT_THR); 11433a52d32aSMårten Lindahl } 11443a52d32aSMårten Lindahl 11453a52d32aSMårten Lindahl if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 1146f700e55eSMehdi Djait iio_trigger_poll_nested(indio_dev->trig); 11473a52d32aSMårten Lindahl 11483a52d32aSMårten Lindahl end: 11493a52d32aSMårten Lindahl return IRQ_HANDLED; 11503a52d32aSMårten Lindahl } 11513a52d32aSMårten Lindahl 11523a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 11533a52d32aSMårten Lindahl { 11543a52d32aSMårten Lindahl struct iio_poll_func *pf = p; 11553a52d32aSMårten Lindahl struct iio_dev *indio_dev = pf->indio_dev; 11563a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 11573a52d32aSMårten Lindahl const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 11583a52d32aSMårten Lindahl u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 11593a52d32aSMårten Lindahl bool data_read = false; 11603a52d32aSMårten Lindahl unsigned long isr; 11613a52d32aSMårten Lindahl int val = 0; 11623a52d32aSMårten Lindahl int ret; 11633a52d32aSMårten Lindahl 11643a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 11653a52d32aSMårten Lindahl if (ret < 0) 11663a52d32aSMårten Lindahl goto end; 11673a52d32aSMårten Lindahl 11683a52d32aSMårten Lindahl isr = ret; 11693a52d32aSMårten Lindahl 11703a52d32aSMårten Lindahl if (test_bit(0, active_scan_mask)) { 11713a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 11723a52d32aSMårten Lindahl ret = vcnl4000_read_data(data, 11733a52d32aSMårten Lindahl VCNL4000_PS_RESULT_HI, 11743a52d32aSMårten Lindahl &val); 11753a52d32aSMårten Lindahl if (ret < 0) 11763a52d32aSMårten Lindahl goto end; 11773a52d32aSMårten Lindahl 11783a52d32aSMårten Lindahl buffer[0] = val; 11793a52d32aSMårten Lindahl data_read = true; 11803a52d32aSMårten Lindahl } 11813a52d32aSMårten Lindahl } 11823a52d32aSMårten Lindahl 11833a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 11843a52d32aSMårten Lindahl isr & VCNL4010_INT_DRDY); 11853a52d32aSMårten Lindahl if (ret < 0) 11863a52d32aSMårten Lindahl goto end; 11873a52d32aSMårten Lindahl 11883a52d32aSMårten Lindahl if (!data_read) 11893a52d32aSMårten Lindahl goto end; 11903a52d32aSMårten Lindahl 11913a52d32aSMårten Lindahl iio_push_to_buffers_with_timestamp(indio_dev, buffer, 11923a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 11933a52d32aSMårten Lindahl 11943a52d32aSMårten Lindahl end: 11953a52d32aSMårten Lindahl iio_trigger_notify_done(indio_dev->trig); 11963a52d32aSMårten Lindahl return IRQ_HANDLED; 11973a52d32aSMårten Lindahl } 11983a52d32aSMårten Lindahl 11993a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 12003a52d32aSMårten Lindahl { 12013a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 12023a52d32aSMårten Lindahl int ret; 12033a52d32aSMårten Lindahl int cmd; 12043a52d32aSMårten Lindahl 12053a52d32aSMårten Lindahl /* Do not enable the buffer if we are already capturing events. */ 12063a52d32aSMårten Lindahl if (vcnl4010_is_in_periodic_mode(data)) 12073a52d32aSMårten Lindahl return -EBUSY; 12083a52d32aSMårten Lindahl 12093a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 12103a52d32aSMårten Lindahl VCNL4010_INT_PROX_EN); 12113a52d32aSMårten Lindahl if (ret < 0) 12123a52d32aSMårten Lindahl return ret; 12133a52d32aSMårten Lindahl 12143a52d32aSMårten Lindahl cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 12153a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 12163a52d32aSMårten Lindahl } 12173a52d32aSMårten Lindahl 12183a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 12193a52d32aSMårten Lindahl { 12203a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 12213a52d32aSMårten Lindahl int ret; 12223a52d32aSMårten Lindahl 12233a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 12243a52d32aSMårten Lindahl if (ret < 0) 12253a52d32aSMårten Lindahl return ret; 12263a52d32aSMårten Lindahl 12273a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 12283a52d32aSMårten Lindahl } 12293a52d32aSMårten Lindahl 12303a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 12313a52d32aSMårten Lindahl .postenable = &vcnl4010_buffer_postenable, 12323a52d32aSMårten Lindahl .predisable = &vcnl4010_buffer_predisable, 12333a52d32aSMårten Lindahl }; 12343a52d32aSMårten Lindahl 1235d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 1236d35567fcSMathieu Othacehe { 1237d35567fcSMathieu Othacehe .name = "nearlevel", 1238d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 1239d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 1240d35567fcSMathieu Othacehe }, 1241d35567fcSMathieu Othacehe { /* sentinel */ } 1242d35567fcSMathieu Othacehe }; 1243d35567fcSMathieu Othacehe 1244d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 1245d35567fcSMathieu Othacehe { 1246d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1247d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 1248d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1249d35567fcSMathieu Othacehe }, { 1250d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1251d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 1252d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1253d35567fcSMathieu Othacehe }, { 1254d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1255d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 1256d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 1257d35567fcSMathieu Othacehe } 1258d35567fcSMathieu Othacehe }; 1259d35567fcSMathieu Othacehe 126054667612SMårten Lindahl static const struct iio_event_spec vcnl4040_event_spec[] = { 126154667612SMårten Lindahl { 126254667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 126354667612SMårten Lindahl .dir = IIO_EV_DIR_RISING, 126454667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 126554667612SMårten Lindahl }, { 126654667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 126754667612SMårten Lindahl .dir = IIO_EV_DIR_FALLING, 126854667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 126954667612SMårten Lindahl }, 127054667612SMårten Lindahl }; 127154667612SMårten Lindahl 1272d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 1273d35567fcSMathieu Othacehe { 1274d35567fcSMathieu Othacehe .type = IIO_LIGHT, 1275d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1276d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1277d35567fcSMathieu Othacehe }, { 1278d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 1279d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1280d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 1281d35567fcSMathieu Othacehe } 1282d35567fcSMathieu Othacehe }; 1283d35567fcSMathieu Othacehe 1284d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 1285d35567fcSMathieu Othacehe { 1286d35567fcSMathieu Othacehe .type = IIO_LIGHT, 12878fe78d52SMathieu Othacehe .scan_index = -1, 1288d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1289d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1290d35567fcSMathieu Othacehe }, { 1291d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 12928fe78d52SMathieu Othacehe .scan_index = 0, 1293f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1294f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 1295f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1296d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 1297d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 1298d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 12998fe78d52SMathieu Othacehe .scan_type = { 13008fe78d52SMathieu Othacehe .sign = 'u', 13018fe78d52SMathieu Othacehe .realbits = 16, 13028fe78d52SMathieu Othacehe .storagebits = 16, 13038fe78d52SMathieu Othacehe .endianness = IIO_CPU, 1304d35567fcSMathieu Othacehe }, 13058fe78d52SMathieu Othacehe }, 13068fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 1307d35567fcSMathieu Othacehe }; 1308d35567fcSMathieu Othacehe 130985e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = { 131085e2c6a2SMårten Lindahl { 131185e2c6a2SMårten Lindahl .type = IIO_LIGHT, 131285e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 131385e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_SCALE), 131485e2c6a2SMårten Lindahl }, { 131585e2c6a2SMårten Lindahl .type = IIO_PROXIMITY, 131685e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 131785e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_INT_TIME), 131885e2c6a2SMårten Lindahl .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 131985e2c6a2SMårten Lindahl .ext_info = vcnl4000_ext_info, 132054667612SMårten Lindahl .event_spec = vcnl4040_event_spec, 132154667612SMårten Lindahl .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), 132285e2c6a2SMårten Lindahl } 132385e2c6a2SMårten Lindahl }; 132485e2c6a2SMårten Lindahl 132562a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 132662a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 132762a1efb9SPeter Meerwald }; 132862a1efb9SPeter Meerwald 1329d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 1330d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 1331f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 1332f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 1333d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 1334d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 1335d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 1336d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 1337d35567fcSMathieu Othacehe }; 1338d35567fcSMathieu Othacehe 133985e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = { 134085e2c6a2SMårten Lindahl .read_raw = vcnl4000_read_raw, 134185e2c6a2SMårten Lindahl .write_raw = vcnl4040_write_raw, 134254667612SMårten Lindahl .read_event_value = vcnl4040_read_event, 134354667612SMårten Lindahl .write_event_value = vcnl4040_write_event, 134454667612SMårten Lindahl .read_event_config = vcnl4040_read_event_config, 134554667612SMårten Lindahl .write_event_config = vcnl4040_write_event_config, 134685e2c6a2SMårten Lindahl .read_avail = vcnl4040_read_avail, 134785e2c6a2SMårten Lindahl }; 134885e2c6a2SMårten Lindahl 1349d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 1350d35567fcSMathieu Othacehe [VCNL4000] = { 1351d35567fcSMathieu Othacehe .prod = "VCNL4000", 1352d35567fcSMathieu Othacehe .init = vcnl4000_init, 1353d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1354d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1355d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1356d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1357d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1358d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1359d35567fcSMathieu Othacehe }, 1360d35567fcSMathieu Othacehe [VCNL4010] = { 1361d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 1362d35567fcSMathieu Othacehe .init = vcnl4000_init, 1363d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1364d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1365d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1366d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 1367d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 1368d35567fcSMathieu Othacehe .info = &vcnl4010_info, 1369bfb6cfeeSMårten Lindahl .irq_thread = vcnl4010_irq_thread, 1370bfb6cfeeSMårten Lindahl .trig_buffer_func = vcnl4010_trigger_handler, 1371bfb6cfeeSMårten Lindahl .buffer_setup_ops = &vcnl4010_buffer_ops, 1372d35567fcSMathieu Othacehe }, 1373d35567fcSMathieu Othacehe [VCNL4040] = { 1374d35567fcSMathieu Othacehe .prod = "VCNL4040", 1375d35567fcSMathieu Othacehe .init = vcnl4200_init, 1376d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1377d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1378d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 137985e2c6a2SMårten Lindahl .channels = vcnl4040_channels, 138085e2c6a2SMårten Lindahl .num_channels = ARRAY_SIZE(vcnl4040_channels), 138185e2c6a2SMårten Lindahl .info = &vcnl4040_info, 138254667612SMårten Lindahl .irq_thread = vcnl4040_irq_thread, 1383854965b7SAstrid Rost .int_reg = VCNL4040_INT_FLAGS, 1384e55c96daSAstrid Rost .ps_it_times = &vcnl4040_ps_it_times, 1385e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), 1386d35567fcSMathieu Othacehe }, 1387d35567fcSMathieu Othacehe [VCNL4200] = { 1388d35567fcSMathieu Othacehe .prod = "VCNL4200", 1389d35567fcSMathieu Othacehe .init = vcnl4200_init, 1390d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1391d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1392d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1393854965b7SAstrid Rost .channels = vcnl4040_channels, 1394d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1395854965b7SAstrid Rost .info = &vcnl4040_info, 1396854965b7SAstrid Rost .irq_thread = vcnl4040_irq_thread, 1397854965b7SAstrid Rost .int_reg = VCNL4200_INT_FLAGS, 1398e55c96daSAstrid Rost .ps_it_times = &vcnl4200_ps_it_times, 1399e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times), 1400d35567fcSMathieu Othacehe }, 1401d35567fcSMathieu Othacehe }; 1402d35567fcSMathieu Othacehe 14038fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 14048fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 14058fe78d52SMathieu Othacehe }; 14068fe78d52SMathieu Othacehe 14078fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 14088fe78d52SMathieu Othacehe { 14098fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 14108fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 14118fe78d52SMathieu Othacehe struct iio_trigger *trigger; 14128fe78d52SMathieu Othacehe 14138fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 141415ea2878SJonathan Cameron indio_dev->name, 141515ea2878SJonathan Cameron iio_device_id(indio_dev)); 14168fe78d52SMathieu Othacehe if (!trigger) 14178fe78d52SMathieu Othacehe return -ENOMEM; 14188fe78d52SMathieu Othacehe 14198fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 14208fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 14218fe78d52SMathieu Othacehe 14228fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 14238fe78d52SMathieu Othacehe } 14248fe78d52SMathieu Othacehe 1425e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client) 142662a1efb9SPeter Meerwald { 1427e61295e0SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client); 142862a1efb9SPeter Meerwald struct vcnl4000_data *data; 142962a1efb9SPeter Meerwald struct iio_dev *indio_dev; 14301ebc787aSTomas Novotny int ret; 143162a1efb9SPeter Meerwald 14322669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 143362a1efb9SPeter Meerwald if (!indio_dev) 143462a1efb9SPeter Meerwald return -ENOMEM; 143562a1efb9SPeter Meerwald 143662a1efb9SPeter Meerwald data = iio_priv(indio_dev); 143762a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 143862a1efb9SPeter Meerwald data->client = client; 14391ebc787aSTomas Novotny data->id = id->driver_data; 14401ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 144162a1efb9SPeter Meerwald 144242ec40b0SMårten Lindahl mutex_init(&data->vcnl4000_lock); 144342ec40b0SMårten Lindahl 14441ebc787aSTomas Novotny ret = data->chip_spec->init(data); 144562a1efb9SPeter Meerwald if (ret < 0) 14462669d723SPeter Meerwald return ret; 144762a1efb9SPeter Meerwald 1448d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 14491ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 145062a1efb9SPeter Meerwald 1451f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1452f5a98e1fSGuido Günther &data->near_level)) 1453f5a98e1fSGuido Günther data->near_level = 0; 1454f5a98e1fSGuido Günther 1455d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1456d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1457d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 145862a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 145962a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 146062a1efb9SPeter Meerwald 1461bfb6cfeeSMårten Lindahl if (data->chip_spec->trig_buffer_func && 1462bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops) { 14638fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 14648fe78d52SMathieu Othacehe NULL, 1465bfb6cfeeSMårten Lindahl data->chip_spec->trig_buffer_func, 1466bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops); 14678fe78d52SMathieu Othacehe if (ret < 0) { 14688fe78d52SMathieu Othacehe dev_err(&client->dev, 14698fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 14708fe78d52SMathieu Othacehe return ret; 14718fe78d52SMathieu Othacehe } 1472bfb6cfeeSMårten Lindahl } 14738fe78d52SMathieu Othacehe 1474bfb6cfeeSMårten Lindahl if (client->irq && data->chip_spec->irq_thread) { 1475d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1476bfb6cfeeSMårten Lindahl NULL, data->chip_spec->irq_thread, 1477d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1478d35567fcSMathieu Othacehe IRQF_ONESHOT, 1479bfb6cfeeSMårten Lindahl "vcnl4000_irq", 1480d35567fcSMathieu Othacehe indio_dev); 1481d35567fcSMathieu Othacehe if (ret < 0) { 1482d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1483d35567fcSMathieu Othacehe return ret; 1484d35567fcSMathieu Othacehe } 14858fe78d52SMathieu Othacehe 14868fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 14878fe78d52SMathieu Othacehe if (ret < 0) 14888fe78d52SMathieu Othacehe return ret; 1489d35567fcSMathieu Othacehe } 1490d35567fcSMathieu Othacehe 14915e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 14925e00708dSGuido Günther if (ret < 0) 14935e00708dSGuido Günther goto fail_poweroff; 14945e00708dSGuido Günther 14955e00708dSGuido Günther ret = iio_device_register(indio_dev); 14965e00708dSGuido Günther if (ret < 0) 14975e00708dSGuido Günther goto fail_poweroff; 14985e00708dSGuido Günther 14995e00708dSGuido Günther pm_runtime_enable(&client->dev); 15005e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 15015e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 15025e00708dSGuido Günther 15035e00708dSGuido Günther return 0; 15045e00708dSGuido Günther fail_poweroff: 15055e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 15065e00708dSGuido Günther return ret; 150762a1efb9SPeter Meerwald } 150862a1efb9SPeter Meerwald 1509ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1510ebd457d5SAngus Ainslie (Purism) { 1511ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 15121436a78cSMarco Felsch .data = (void *)VCNL4000, 1513ebd457d5SAngus Ainslie (Purism) }, 1514ebd457d5SAngus Ainslie (Purism) { 1515ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 15161436a78cSMarco Felsch .data = (void *)VCNL4010, 1517ebd457d5SAngus Ainslie (Purism) }, 1518ebd457d5SAngus Ainslie (Purism) { 15191436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 15201436a78cSMarco Felsch .data = (void *)VCNL4010, 1521ebd457d5SAngus Ainslie (Purism) }, 1522ebd457d5SAngus Ainslie (Purism) { 15237fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 15247fd1c260SMarco Felsch .data = (void *)VCNL4040, 15257fd1c260SMarco Felsch }, 15267fd1c260SMarco Felsch { 1527ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 15281436a78cSMarco Felsch .data = (void *)VCNL4200, 1529ebd457d5SAngus Ainslie (Purism) }, 1530ebd457d5SAngus Ainslie (Purism) {}, 1531ebd457d5SAngus Ainslie (Purism) }; 1532ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1533ebd457d5SAngus Ainslie (Purism) 1534ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 15355e00708dSGuido Günther { 15365e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 15375e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 1538ab91da2fSUwe Kleine-König int ret; 15395e00708dSGuido Günther 15405e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 15415e00708dSGuido Günther pm_runtime_disable(&client->dev); 15425e00708dSGuido Günther iio_device_unregister(indio_dev); 15435e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 15445e00708dSGuido Günther 1545ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 1546ab91da2fSUwe Kleine-König if (ret) 1547ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 1548ab91da2fSUwe Kleine-König ERR_PTR(ret)); 15495e00708dSGuido Günther } 15505e00708dSGuido Günther 1551cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 15525e00708dSGuido Günther { 15535e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 15545e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 15555e00708dSGuido Günther 15565e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 15575e00708dSGuido Günther } 15585e00708dSGuido Günther 1559cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 15605e00708dSGuido Günther { 15615e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 15625e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 15635e00708dSGuido Günther 15645e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 15655e00708dSGuido Günther } 15665e00708dSGuido Günther 1567cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 1568cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 15695e00708dSGuido Günther 157062a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 157162a1efb9SPeter Meerwald .driver = { 157262a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 1573cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 1574ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 157562a1efb9SPeter Meerwald }, 15767cf15f42SUwe Kleine-König .probe = vcnl4000_probe, 157762a1efb9SPeter Meerwald .id_table = vcnl4000_id, 15785e00708dSGuido Günther .remove = vcnl4000_remove, 157962a1efb9SPeter Meerwald }; 158062a1efb9SPeter Meerwald 158162a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 158262a1efb9SPeter Meerwald 158362a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 15848fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 158562a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 158662a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 1587