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 862a1efb9SPeter Meerwald * 9be38866fSTomas Novotny * IIO driver for: 10be38866fSTomas Novotny * VCNL4000/10/20 (7-bit I2C slave address 0x13) 115a441aadSAngus Ainslie (Purism) * VCNL4040 (7-bit I2C slave address 0x60) 12be38866fSTomas Novotny * VCNL4200 (7-bit I2C slave address 0x51) 1362a1efb9SPeter Meerwald * 1462a1efb9SPeter Meerwald * TODO: 1562a1efb9SPeter Meerwald * allow to adjust IR current 1662a1efb9SPeter Meerwald * proximity threshold and event handling 17d978bfddSPeter Meerwald-Stadler * periodic ALS/proximity measurement (VCNL4010/20) 185a441aadSAngus Ainslie (Purism) * interrupts (VCNL4010/20/40, VCNL4200) 1962a1efb9SPeter Meerwald */ 2062a1efb9SPeter Meerwald 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> 2662a1efb9SPeter Meerwald 2762a1efb9SPeter Meerwald #include <linux/iio/iio.h> 2862a1efb9SPeter Meerwald #include <linux/iio/sysfs.h> 2962a1efb9SPeter Meerwald 3062a1efb9SPeter Meerwald #define VCNL4000_DRV_NAME "vcnl4000" 311ebc787aSTomas Novotny #define VCNL4000_PROD_ID 0x01 321ebc787aSTomas Novotny #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ 335a441aadSAngus Ainslie (Purism) #define VCNL4040_PROD_ID 0x86 34be38866fSTomas Novotny #define VCNL4200_PROD_ID 0x58 3562a1efb9SPeter Meerwald 3662a1efb9SPeter Meerwald #define VCNL4000_COMMAND 0x80 /* Command register */ 3762a1efb9SPeter Meerwald #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 3862a1efb9SPeter Meerwald #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 3962a1efb9SPeter Meerwald #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 4062a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 4162a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 4262a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 4362a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 4462a1efb9SPeter Meerwald #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 4562a1efb9SPeter Meerwald #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 4662a1efb9SPeter Meerwald 47be38866fSTomas Novotny #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ 48be38866fSTomas Novotny #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ 49be38866fSTomas Novotny #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 50be38866fSTomas Novotny #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 51be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 52be38866fSTomas Novotny 535a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 545a441aadSAngus Ainslie (Purism) 5562a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 56ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 57ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 58ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 59ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 6062a1efb9SPeter Meerwald 615e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 625e00708dSGuido Günther 631ebc787aSTomas Novotny enum vcnl4000_device_ids { 641ebc787aSTomas Novotny VCNL4000, 6550c50b97STomas Novotny VCNL4010, 665a441aadSAngus Ainslie (Purism) VCNL4040, 67be38866fSTomas Novotny VCNL4200, 68be38866fSTomas Novotny }; 69be38866fSTomas Novotny 70be38866fSTomas Novotny struct vcnl4200_channel { 71be38866fSTomas Novotny u8 reg; 72be38866fSTomas Novotny ktime_t last_measurement; 73be38866fSTomas Novotny ktime_t sampling_rate; 74be38866fSTomas Novotny struct mutex lock; 751ebc787aSTomas Novotny }; 761ebc787aSTomas Novotny 7762a1efb9SPeter Meerwald struct vcnl4000_data { 7862a1efb9SPeter Meerwald struct i2c_client *client; 791ebc787aSTomas Novotny enum vcnl4000_device_ids id; 801ebc787aSTomas Novotny int rev; 811ebc787aSTomas Novotny int al_scale; 821ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 83be38866fSTomas Novotny struct mutex vcnl4000_lock; 84be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 85be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 86f5a98e1fSGuido Günther uint32_t near_level; 8762a1efb9SPeter Meerwald }; 8862a1efb9SPeter Meerwald 891ebc787aSTomas Novotny struct vcnl4000_chip_spec { 901ebc787aSTomas Novotny const char *prod; 911ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 921ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 931ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 945e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 951ebc787aSTomas Novotny }; 961ebc787aSTomas Novotny 9762a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 981ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 9950c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 10050c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 1015a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 102be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 10362a1efb9SPeter Meerwald { } 10462a1efb9SPeter Meerwald }; 10562a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 10662a1efb9SPeter Meerwald 1075e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 1085e00708dSGuido Günther { 1095e00708dSGuido Günther /* no suspend op */ 1105e00708dSGuido Günther return 0; 1115e00708dSGuido Günther } 1125e00708dSGuido Günther 1131ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 1141ebc787aSTomas Novotny { 1151ebc787aSTomas Novotny int ret, prod_id; 1161ebc787aSTomas Novotny 1171ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 1181ebc787aSTomas Novotny if (ret < 0) 1191ebc787aSTomas Novotny return ret; 1201ebc787aSTomas Novotny 1211ebc787aSTomas Novotny prod_id = ret >> 4; 12258bf9aceSTomas Novotny switch (prod_id) { 12358bf9aceSTomas Novotny case VCNL4000_PROD_ID: 12458bf9aceSTomas Novotny if (data->id != VCNL4000) 12558bf9aceSTomas Novotny dev_warn(&data->client->dev, 12658bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 12758bf9aceSTomas Novotny break; 12858bf9aceSTomas Novotny case VCNL4010_PROD_ID: 12958bf9aceSTomas Novotny if (data->id != VCNL4010) 13058bf9aceSTomas Novotny dev_warn(&data->client->dev, 13158bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 13258bf9aceSTomas Novotny break; 13358bf9aceSTomas Novotny default: 1341ebc787aSTomas Novotny return -ENODEV; 13558bf9aceSTomas Novotny } 1361ebc787aSTomas Novotny 1371ebc787aSTomas Novotny data->rev = ret & 0xf; 1381ebc787aSTomas Novotny data->al_scale = 250000; 139be38866fSTomas Novotny mutex_init(&data->vcnl4000_lock); 140be38866fSTomas Novotny 1415e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 142be38866fSTomas Novotny }; 143be38866fSTomas Novotny 1445e00708dSGuido Günther static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 1455e00708dSGuido Günther { 1465e00708dSGuido Günther u16 val = on ? 0 /* power on */ : 1 /* shut down */; 1475e00708dSGuido Günther int ret; 1485e00708dSGuido Günther 1495e00708dSGuido Günther ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val); 1505e00708dSGuido Günther if (ret < 0) 1515e00708dSGuido Günther return ret; 1525e00708dSGuido Günther 1535e00708dSGuido Günther ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); 1545e00708dSGuido Günther if (ret < 0) 1555e00708dSGuido Günther return ret; 1565e00708dSGuido Günther 1575e00708dSGuido Günther if (on) { 1585e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 1595e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 1605e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 1615e00708dSGuido Günther } 1625e00708dSGuido Günther 1635e00708dSGuido Günther return 0; 1645e00708dSGuido Günther } 1655e00708dSGuido Günther 166be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 167be38866fSTomas Novotny { 1685a441aadSAngus Ainslie (Purism) int ret, id; 169be38866fSTomas Novotny 170be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 171be38866fSTomas Novotny if (ret < 0) 172be38866fSTomas Novotny return ret; 173be38866fSTomas Novotny 1745a441aadSAngus Ainslie (Purism) id = ret & 0xff; 1755a441aadSAngus Ainslie (Purism) 1765a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 1775a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 1785a441aadSAngus Ainslie (Purism) if (ret < 0) 1795a441aadSAngus Ainslie (Purism) return ret; 1805a441aadSAngus Ainslie (Purism) 1815a441aadSAngus Ainslie (Purism) id = ret & 0xff; 1825a441aadSAngus Ainslie (Purism) 1835a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 184be38866fSTomas Novotny return -ENODEV; 1855a441aadSAngus Ainslie (Purism) } 1865a441aadSAngus Ainslie (Purism) 1875a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 188be38866fSTomas Novotny 189be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 190be38866fSTomas Novotny 191be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 192be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 1935a441aadSAngus Ainslie (Purism) switch (id) { 1945a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 195b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 196b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 197b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 198b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 199bc80573eSGuido Günther data->al_scale = 24000; 2005a441aadSAngus Ainslie (Purism) break; 2015a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 2022ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 2032ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 2042ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 2052ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 206bc80573eSGuido Günther data->al_scale = 120000; 2075a441aadSAngus Ainslie (Purism) break; 2085a441aadSAngus Ainslie (Purism) } 209be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 210be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 2111ebc787aSTomas Novotny 2125e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 2135e00708dSGuido Günther if (ret < 0) 2145e00708dSGuido Günther return ret; 2155e00708dSGuido Günther 2161ebc787aSTomas Novotny return 0; 2171ebc787aSTomas Novotny }; 2181ebc787aSTomas Novotny 219*816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 220*816956c3SMathieu Othacehe { 221*816956c3SMathieu Othacehe s32 ret; 222*816956c3SMathieu Othacehe 223*816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 224*816956c3SMathieu Othacehe if (ret < 0) 225*816956c3SMathieu Othacehe return ret; 226*816956c3SMathieu Othacehe 227*816956c3SMathieu Othacehe *val = ret; 228*816956c3SMathieu Othacehe return 0; 229*816956c3SMathieu Othacehe } 230*816956c3SMathieu Othacehe 231*816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 232*816956c3SMathieu Othacehe { 233*816956c3SMathieu Othacehe if (val > U16_MAX) 234*816956c3SMathieu Othacehe return -ERANGE; 235*816956c3SMathieu Othacehe 236*816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 237*816956c3SMathieu Othacehe } 238*816956c3SMathieu Othacehe 239*816956c3SMathieu Othacehe 24062a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 24162a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 24262a1efb9SPeter Meerwald { 24362a1efb9SPeter Meerwald int tries = 20; 24462a1efb9SPeter Meerwald int ret; 24562a1efb9SPeter Meerwald 246be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 247ff34ed6dSPeter Meerwald-Stadler 24862a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 24962a1efb9SPeter Meerwald req_mask); 25062a1efb9SPeter Meerwald if (ret < 0) 251ff34ed6dSPeter Meerwald-Stadler goto fail; 25262a1efb9SPeter Meerwald 25362a1efb9SPeter Meerwald /* wait for data to become ready */ 25462a1efb9SPeter Meerwald while (tries--) { 25562a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 25662a1efb9SPeter Meerwald if (ret < 0) 257ff34ed6dSPeter Meerwald-Stadler goto fail; 25862a1efb9SPeter Meerwald if (ret & rdy_mask) 25962a1efb9SPeter Meerwald break; 26062a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 26162a1efb9SPeter Meerwald } 26262a1efb9SPeter Meerwald 26362a1efb9SPeter Meerwald if (tries < 0) { 26462a1efb9SPeter Meerwald dev_err(&data->client->dev, 26562a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 266ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 267ff34ed6dSPeter Meerwald-Stadler goto fail; 26862a1efb9SPeter Meerwald } 26962a1efb9SPeter Meerwald 270*816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 27162a1efb9SPeter Meerwald if (ret < 0) 272ff34ed6dSPeter Meerwald-Stadler goto fail; 27362a1efb9SPeter Meerwald 274be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 27562a1efb9SPeter Meerwald 27662a1efb9SPeter Meerwald return 0; 277ff34ed6dSPeter Meerwald-Stadler 278ff34ed6dSPeter Meerwald-Stadler fail: 279be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 280ff34ed6dSPeter Meerwald-Stadler return ret; 28162a1efb9SPeter Meerwald } 28262a1efb9SPeter Meerwald 283be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 284be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 285be38866fSTomas Novotny { 286be38866fSTomas Novotny int ret; 287be38866fSTomas Novotny s64 delta; 288be38866fSTomas Novotny ktime_t next_measurement; 289be38866fSTomas Novotny 290be38866fSTomas Novotny mutex_lock(&chan->lock); 291be38866fSTomas Novotny 292be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 293be38866fSTomas Novotny chan->sampling_rate); 294be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 295be38866fSTomas Novotny if (delta > 0) 296be38866fSTomas Novotny usleep_range(delta, delta + 500); 297be38866fSTomas Novotny chan->last_measurement = ktime_get(); 298be38866fSTomas Novotny 299be38866fSTomas Novotny mutex_unlock(&chan->lock); 300be38866fSTomas Novotny 301be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 302be38866fSTomas Novotny if (ret < 0) 303be38866fSTomas Novotny return ret; 304be38866fSTomas Novotny 305be38866fSTomas Novotny *val = ret; 306be38866fSTomas Novotny 307be38866fSTomas Novotny return 0; 308be38866fSTomas Novotny } 309be38866fSTomas Novotny 3101ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 3111ebc787aSTomas Novotny { 3121ebc787aSTomas Novotny return vcnl4000_measure(data, 3131ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 3141ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 3151ebc787aSTomas Novotny } 3161ebc787aSTomas Novotny 317be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 318be38866fSTomas Novotny { 319be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 320be38866fSTomas Novotny } 321be38866fSTomas Novotny 3221ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 3231ebc787aSTomas Novotny { 3241ebc787aSTomas Novotny return vcnl4000_measure(data, 3251ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 3261ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 3271ebc787aSTomas Novotny } 3281ebc787aSTomas Novotny 329be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 330be38866fSTomas Novotny { 331be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 332be38866fSTomas Novotny } 333be38866fSTomas Novotny 3341ebc787aSTomas Novotny static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 3351ebc787aSTomas Novotny [VCNL4000] = { 3361ebc787aSTomas Novotny .prod = "VCNL4000", 3371ebc787aSTomas Novotny .init = vcnl4000_init, 3381ebc787aSTomas Novotny .measure_light = vcnl4000_measure_light, 3391ebc787aSTomas Novotny .measure_proximity = vcnl4000_measure_proximity, 3405e00708dSGuido Günther .set_power_state = vcnl4000_set_power_state, 3411ebc787aSTomas Novotny }, 34250c50b97STomas Novotny [VCNL4010] = { 34350c50b97STomas Novotny .prod = "VCNL4010/4020", 34450c50b97STomas Novotny .init = vcnl4000_init, 34550c50b97STomas Novotny .measure_light = vcnl4000_measure_light, 34650c50b97STomas Novotny .measure_proximity = vcnl4000_measure_proximity, 3475e00708dSGuido Günther .set_power_state = vcnl4000_set_power_state, 34850c50b97STomas Novotny }, 3495a441aadSAngus Ainslie (Purism) [VCNL4040] = { 3505a441aadSAngus Ainslie (Purism) .prod = "VCNL4040", 3515a441aadSAngus Ainslie (Purism) .init = vcnl4200_init, 3525a441aadSAngus Ainslie (Purism) .measure_light = vcnl4200_measure_light, 3535a441aadSAngus Ainslie (Purism) .measure_proximity = vcnl4200_measure_proximity, 3545e00708dSGuido Günther .set_power_state = vcnl4200_set_power_state, 3555a441aadSAngus Ainslie (Purism) }, 356be38866fSTomas Novotny [VCNL4200] = { 357be38866fSTomas Novotny .prod = "VCNL4200", 358be38866fSTomas Novotny .init = vcnl4200_init, 359be38866fSTomas Novotny .measure_light = vcnl4200_measure_light, 360be38866fSTomas Novotny .measure_proximity = vcnl4200_measure_proximity, 3615e00708dSGuido Günther .set_power_state = vcnl4200_set_power_state, 362be38866fSTomas Novotny }, 3631ebc787aSTomas Novotny }; 3641ebc787aSTomas Novotny 365f5a98e1fSGuido Günther static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 366f5a98e1fSGuido Günther uintptr_t priv, 367f5a98e1fSGuido Günther const struct iio_chan_spec *chan, 368f5a98e1fSGuido Günther char *buf) 369f5a98e1fSGuido Günther { 370f5a98e1fSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 371f5a98e1fSGuido Günther 372f5a98e1fSGuido Günther return sprintf(buf, "%u\n", data->near_level); 373f5a98e1fSGuido Günther } 374f5a98e1fSGuido Günther 375f5a98e1fSGuido Günther static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 376f5a98e1fSGuido Günther { 377f5a98e1fSGuido Günther .name = "nearlevel", 378f5a98e1fSGuido Günther .shared = IIO_SEPARATE, 379f5a98e1fSGuido Günther .read = vcnl4000_read_near_level, 380f5a98e1fSGuido Günther }, 381f5a98e1fSGuido Günther { /* sentinel */ } 382f5a98e1fSGuido Günther }; 383f5a98e1fSGuido Günther 38462a1efb9SPeter Meerwald static const struct iio_chan_spec vcnl4000_channels[] = { 38562a1efb9SPeter Meerwald { 38662a1efb9SPeter Meerwald .type = IIO_LIGHT, 387bb7c5940SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 388bb7c5940SJonathan Cameron BIT(IIO_CHAN_INFO_SCALE), 38962a1efb9SPeter Meerwald }, { 39062a1efb9SPeter Meerwald .type = IIO_PROXIMITY, 391bb7c5940SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 392f5a98e1fSGuido Günther .ext_info = vcnl4000_ext_info, 39362a1efb9SPeter Meerwald } 39462a1efb9SPeter Meerwald }; 39562a1efb9SPeter Meerwald 3965e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 3975e00708dSGuido Günther { 3985e00708dSGuido Günther struct device *dev = &data->client->dev; 3995e00708dSGuido Günther int ret; 4005e00708dSGuido Günther 4015e00708dSGuido Günther if (on) { 4025e00708dSGuido Günther ret = pm_runtime_get_sync(dev); 4035e00708dSGuido Günther if (ret < 0) 4045e00708dSGuido Günther pm_runtime_put_noidle(dev); 4055e00708dSGuido Günther } else { 4065e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 4075e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 4085e00708dSGuido Günther } 4095e00708dSGuido Günther 4105e00708dSGuido Günther return ret; 4115e00708dSGuido Günther } 4125e00708dSGuido Günther 41362a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 41462a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 41562a1efb9SPeter Meerwald int *val, int *val2, long mask) 41662a1efb9SPeter Meerwald { 4175d693139SPeter Meerwald-Stadler int ret; 41862a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 41962a1efb9SPeter Meerwald 42062a1efb9SPeter Meerwald switch (mask) { 42162a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 4225e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 4235e00708dSGuido Günther if (ret < 0) 4245e00708dSGuido Günther return ret; 4255e00708dSGuido Günther 42662a1efb9SPeter Meerwald switch (chan->type) { 42762a1efb9SPeter Meerwald case IIO_LIGHT: 4281ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 4294a818643SGuido Günther if (!ret) 4304a818643SGuido Günther ret = IIO_VAL_INT; 4314a818643SGuido Günther break; 43262a1efb9SPeter Meerwald case IIO_PROXIMITY: 4331ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 4344a818643SGuido Günther if (!ret) 4354a818643SGuido Günther ret = IIO_VAL_INT; 4364a818643SGuido Günther break; 43762a1efb9SPeter Meerwald default: 4384a818643SGuido Günther ret = -EINVAL; 43962a1efb9SPeter Meerwald } 4405e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 4414a818643SGuido Günther return ret; 44262a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 4435d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 4445d693139SPeter Meerwald-Stadler return -EINVAL; 4455d693139SPeter Meerwald-Stadler 44662a1efb9SPeter Meerwald *val = 0; 4471ebc787aSTomas Novotny *val2 = data->al_scale; 4485d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 44962a1efb9SPeter Meerwald default: 4505d693139SPeter Meerwald-Stadler return -EINVAL; 45162a1efb9SPeter Meerwald } 45262a1efb9SPeter Meerwald } 45362a1efb9SPeter Meerwald 45462a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 45562a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 45662a1efb9SPeter Meerwald }; 45762a1efb9SPeter Meerwald 458fc52692cSGreg Kroah-Hartman static int vcnl4000_probe(struct i2c_client *client, 45962a1efb9SPeter Meerwald const struct i2c_device_id *id) 46062a1efb9SPeter Meerwald { 46162a1efb9SPeter Meerwald struct vcnl4000_data *data; 46262a1efb9SPeter Meerwald struct iio_dev *indio_dev; 4631ebc787aSTomas Novotny int ret; 46462a1efb9SPeter Meerwald 4652669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 46662a1efb9SPeter Meerwald if (!indio_dev) 46762a1efb9SPeter Meerwald return -ENOMEM; 46862a1efb9SPeter Meerwald 46962a1efb9SPeter Meerwald data = iio_priv(indio_dev); 47062a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 47162a1efb9SPeter Meerwald data->client = client; 4721ebc787aSTomas Novotny data->id = id->driver_data; 4731ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 47462a1efb9SPeter Meerwald 4751ebc787aSTomas Novotny ret = data->chip_spec->init(data); 47662a1efb9SPeter Meerwald if (ret < 0) 4772669d723SPeter Meerwald return ret; 47862a1efb9SPeter Meerwald 479d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 4801ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 48162a1efb9SPeter Meerwald 482f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 483f5a98e1fSGuido Günther &data->near_level)) 484f5a98e1fSGuido Günther data->near_level = 0; 485f5a98e1fSGuido Günther 48662a1efb9SPeter Meerwald indio_dev->dev.parent = &client->dev; 48762a1efb9SPeter Meerwald indio_dev->info = &vcnl4000_info; 48862a1efb9SPeter Meerwald indio_dev->channels = vcnl4000_channels; 48962a1efb9SPeter Meerwald indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels); 49062a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 49162a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 49262a1efb9SPeter Meerwald 4935e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 4945e00708dSGuido Günther if (ret < 0) 4955e00708dSGuido Günther goto fail_poweroff; 4965e00708dSGuido Günther 4975e00708dSGuido Günther ret = iio_device_register(indio_dev); 4985e00708dSGuido Günther if (ret < 0) 4995e00708dSGuido Günther goto fail_poweroff; 5005e00708dSGuido Günther 5015e00708dSGuido Günther pm_runtime_enable(&client->dev); 5025e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 5035e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 5045e00708dSGuido Günther 5055e00708dSGuido Günther return 0; 5065e00708dSGuido Günther fail_poweroff: 5075e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 5085e00708dSGuido Günther return ret; 50962a1efb9SPeter Meerwald } 51062a1efb9SPeter Meerwald 511ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 512ebd457d5SAngus Ainslie (Purism) { 513ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 5141436a78cSMarco Felsch .data = (void *)VCNL4000, 515ebd457d5SAngus Ainslie (Purism) }, 516ebd457d5SAngus Ainslie (Purism) { 517ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 5181436a78cSMarco Felsch .data = (void *)VCNL4010, 519ebd457d5SAngus Ainslie (Purism) }, 520ebd457d5SAngus Ainslie (Purism) { 5211436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 5221436a78cSMarco Felsch .data = (void *)VCNL4010, 523ebd457d5SAngus Ainslie (Purism) }, 524ebd457d5SAngus Ainslie (Purism) { 5257fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 5267fd1c260SMarco Felsch .data = (void *)VCNL4040, 5277fd1c260SMarco Felsch }, 5287fd1c260SMarco Felsch { 529ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 5301436a78cSMarco Felsch .data = (void *)VCNL4200, 531ebd457d5SAngus Ainslie (Purism) }, 532ebd457d5SAngus Ainslie (Purism) {}, 533ebd457d5SAngus Ainslie (Purism) }; 534ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 535ebd457d5SAngus Ainslie (Purism) 5365e00708dSGuido Günther static int vcnl4000_remove(struct i2c_client *client) 5375e00708dSGuido Günther { 5385e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 5395e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 5405e00708dSGuido Günther 5415e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 5425e00708dSGuido Günther pm_runtime_disable(&client->dev); 5435e00708dSGuido Günther iio_device_unregister(indio_dev); 5445e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 5455e00708dSGuido Günther 5465e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 5475e00708dSGuido Günther } 5485e00708dSGuido Günther 5495e00708dSGuido Günther static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev) 5505e00708dSGuido Günther { 5515e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 5525e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 5535e00708dSGuido Günther 5545e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 5555e00708dSGuido Günther } 5565e00708dSGuido Günther 5575e00708dSGuido Günther static int __maybe_unused vcnl4000_runtime_resume(struct device *dev) 5585e00708dSGuido Günther { 5595e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 5605e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 5615e00708dSGuido Günther 5625e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 5635e00708dSGuido Günther } 5645e00708dSGuido Günther 5655e00708dSGuido Günther static const struct dev_pm_ops vcnl4000_pm_ops = { 5665e00708dSGuido Günther SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 5675e00708dSGuido Günther pm_runtime_force_resume) 5685e00708dSGuido Günther SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend, 5695e00708dSGuido Günther vcnl4000_runtime_resume, NULL) 5705e00708dSGuido Günther }; 5715e00708dSGuido Günther 57262a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 57362a1efb9SPeter Meerwald .driver = { 57462a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 5755e00708dSGuido Günther .pm = &vcnl4000_pm_ops, 576ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 57762a1efb9SPeter Meerwald }, 57862a1efb9SPeter Meerwald .probe = vcnl4000_probe, 57962a1efb9SPeter Meerwald .id_table = vcnl4000_id, 5805e00708dSGuido Günther .remove = vcnl4000_remove, 58162a1efb9SPeter Meerwald }; 58262a1efb9SPeter Meerwald 58362a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 58462a1efb9SPeter Meerwald 58562a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 58662a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 58762a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 588