linux/drivers/iio/light/vcnl4000.c
Greg Kroah-Hartman 4e1d96306d First set of new IIO device support, features and cleanup for the 5.8 cycle
Usual mix of new drivers, new support in old drivers and lots of minor
 cleanup. Yaml conversions also continue to trickle in (plenty more to go!)
 
 New device support
 * ad7476
   - Add ad7091 support (ID only)
 * ad9467
   - New driver for this 200/250 MSPS adi-axi-adc and SPI attached ADC.
 * adi-axi-adc
   - New driver to support ADIs generic AXI ADC IP core, used to provide
     high speed interfaces to ADCs (JESD204B/C or parallel interfaces). Usually
     paired with a device using a slow configuration interface (spi etc)
     Includes DT bindings and some fixes for fpga headers.
 * bmg160
   - Add support for BMI088 (ID only)
 * max1241
   - New driver for this ADC.
 * st_sensors
   - Add support for LIS2HH12 accelerometer
 * sx9310
   - New driver supporting sx9310 and sx9311 proximity sensors.
 
 Yaml DT binding conversions
 * rockchip-saradc (including adding some missing parts)
 * stm32-dac
 * tsl2563
 * vcnl4000
 
 Features
 * st_lsm6dsx
   - Add LIS3MDL as a possible sensor hub device.
 * vcnl4000
   - Add new concept of near level (from DT) to provide to userspace which
     often needs to have some calibrated concept of 'near'.
 
 Cleanups, minor fixes etc.
 * core
   - Use snprintf for functions where strings are built and there is
     potential for overflow.
   - Correct docs to indicate mlock should not be used directly by drivers.
   - Fix up accidental dropping of a patch to use bitmap_zalloc.
   - Stop allowing enabling of buffers with no channels enabled.
   - Drop unused 'stufftoread' from iio_buffer.
   - Drop scan_el_attrs form iio_buffer as unused.
   - Reorder sanity checks in __iio_device_register to fail earlier.
   - Drop all the devm_ runregister / free functions from IIO as they
     were never used and encourage poor design.
 * dma-buffer
   - Tidy up includes.
 * dma-engine-buffer
   - Provide dev-managed allocator.
   - Fix an issue with printing a size_t
 * cross subsystem (kxsd9, bmg160, mpu3050, bmi160, mpu6050, bmc150)
   - Replace some unnecessary casts of error ptrs and whilst there.
     use the %pe printf parameter to print them in a more useful fashion.
 * cross subsystem
   - Drop casts in calls to regmap_bulk_read as they make no sense.
   - Use devm_platform_ioremap_resource to reduce boilerplate.
   - Fix typos in Analog Devices.
 * counters/104-quad
   - Add Syed Nayyar Waris as an additional maintainer.
 * ad7476
   - Generate CONVST signal internally rather than requiring external
     trigger.  Add sysfs read back as can now do so.
   - use devm_add_action_or_reset to tidy up error and remove handling.
 * ad7793
   - Switch to read_avail from explicit attribute. Mostly done to avoid
     confusing people with a - sign (without surounding spaces) that
     was correct but checkpatch didn't like.
 * adis library
   - Add missing newlines at end of error messages.
 * adis16400
   - Use DEFINE_DEBUGS_ATTRIBUTE rather than DEFINE_SIMPLE_ATTR.
 * adis16460
   - Use DEFINE_DEBUGS_ATTRIBUTE rather than DEFINE_SIMPLE_ATTR.
 * ad_sigma_delta
   - Move some channel definitions into individual drivers to avoid
     having to deal with complex options.
 * ak8974
   - Silence an error on deffered probe.
 * bmp280
   - Harden against IRQ before registration.
   - Convert to read_avail instead of opencoding the equivalent.
   - Explicitly mark GPIO as optional.
   - Cleanup casts.
   - Remove line breaks from strings.
 * htts221
   - Replace direct access to platform_data with dev_get_platdata().
   - Use device properties rather than device tree ones to allow ACPI
     probing.
   - Casting cleanups.
 * intel_mrfld_adc
   - Don't use unaligned accessor for aligned data.
 * isl29125
   - Reorder buer pre and post hooks to allow for coming core rework.
 * ltc2983
   - Remove comp to bool.
 * max1363
   - Stop using mlock from the iio_dev directly in favour of a local lock
     with clearly defined scope.
 * max30100
   - Use generic device properties to allow ACPI probe.
 * mpu6050
   - Convert to i2c_new_client_device.
   - Add debugfs register access.
 * st_lsm6dsx
   - Provide means of configuring full scale on slave device if supported.
   - Drop include of st_sensors header to get one value. Its not otherwise
     used by this driver.
 * st-sensors
   - Replace direct access to platform_data with dev_get_platdata().
   - Casting cleanups.
   - Avoid splitting strings.
 * st_uvis25
   - Casting cleanups.
 * tsl2563
   - Typo fix.
 * tsl2772
   - scnprintf in a non obvious string building usecase. Note also 'fixes'
     a wrong calculation of remaining space that couldn't actually cause
     any trouble as there was lots of room.
 * xilinx-xadc
   - Fix Lars-Peter spelling his own name wrong :) + additional typos.
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAl6gicARHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0Foj3ig//XRAAHRSfzXFpvYxITVKDIGwP/giYdp91
 OflGw/0aOZFExr72ENLmCArXZPOT2qAZyNNoDNvuJ9SkcVDf4bT3w9gYwWtJ1sd7
 Hhh+1JpPspSo6vS85FMjm37UDfSbmYqB6wC57cZ5pKQIiJwi6W2hGsyOoOXmfLLc
 57d3mlg4g3KLl9Hl4UTU84gE1ushKQgNxVH21Y71pOTa8p+xbWWcspWjR2Jf+kVZ
 qPr2+2rP/AjeAVLu9SKfdBiczYQCNR3uFWKkOKsBE9fq3sbcN6NT5DktrSJUH+qU
 zfUGmShCEP908/9fARazXQZcTOX1tIUOnrpHG9l54GNPh3S2Qk9xGaZxvQy5sguC
 AmC4p+MaIMrMcgWMk9po+nE5INJq7HBV/0jnADG307wEhFrKrjh/2PtP4f9k/Mcv
 sHB7K1+dcJAUwFb5gccdDBfmKMnwfnGS8OFGLXZZKBIDqSvptmIs/pmavjEPgqhV
 GwGTDPyZKeyDnyhTXGafxii03Q2I62Zte5l4jcZ+q8Q4UMxQovEkX6IvUpXwA0Kk
 eoxedbgdvzxrDNiNrXa0k6CLzqSQW+aVHgJG+HEig4IXQse8zXb11HCs35zq5r4j
 JI2UjLk3zUHLiNR/Ir9fKAbPwWSWv3IWkfmFEEUWvUFjM+Xot1Hg+61vBTFKo4iK
 EIqqPZmd5Mk=
 =DyTF
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-5.8a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next

Jonathan writes:

First set of new IIO device support, features and cleanup for the 5.8 cycle

Usual mix of new drivers, new support in old drivers and lots of minor
cleanup. Yaml conversions also continue to trickle in (plenty more to go!)

New device support
* ad7476
  - Add ad7091 support (ID only)
* ad9467
  - New driver for this 200/250 MSPS adi-axi-adc and SPI attached ADC.
* adi-axi-adc
  - New driver to support ADIs generic AXI ADC IP core, used to provide
    high speed interfaces to ADCs (JESD204B/C or parallel interfaces). Usually
    paired with a device using a slow configuration interface (spi etc)
    Includes DT bindings and some fixes for fpga headers.
* bmg160
  - Add support for BMI088 (ID only)
* max1241
  - New driver for this ADC.
* st_sensors
  - Add support for LIS2HH12 accelerometer
* sx9310
  - New driver supporting sx9310 and sx9311 proximity sensors.

Yaml DT binding conversions
* rockchip-saradc (including adding some missing parts)
* stm32-dac
* tsl2563
* vcnl4000

Features
* st_lsm6dsx
  - Add LIS3MDL as a possible sensor hub device.
* vcnl4000
  - Add new concept of near level (from DT) to provide to userspace which
    often needs to have some calibrated concept of 'near'.

Cleanups, minor fixes etc.
* core
  - Use snprintf for functions where strings are built and there is
    potential for overflow.
  - Correct docs to indicate mlock should not be used directly by drivers.
  - Fix up accidental dropping of a patch to use bitmap_zalloc.
  - Stop allowing enabling of buffers with no channels enabled.
  - Drop unused 'stufftoread' from iio_buffer.
  - Drop scan_el_attrs form iio_buffer as unused.
  - Reorder sanity checks in __iio_device_register to fail earlier.
  - Drop all the devm_ runregister / free functions from IIO as they
    were never used and encourage poor design.
* dma-buffer
  - Tidy up includes.
* dma-engine-buffer
  - Provide dev-managed allocator.
  - Fix an issue with printing a size_t
* cross subsystem (kxsd9, bmg160, mpu3050, bmi160, mpu6050, bmc150)
  - Replace some unnecessary casts of error ptrs and whilst there.
    use the %pe printf parameter to print them in a more useful fashion.
* cross subsystem
  - Drop casts in calls to regmap_bulk_read as they make no sense.
  - Use devm_platform_ioremap_resource to reduce boilerplate.
  - Fix typos in Analog Devices.
* counters/104-quad
  - Add Syed Nayyar Waris as an additional maintainer.
* ad7476
  - Generate CONVST signal internally rather than requiring external
    trigger.  Add sysfs read back as can now do so.
  - use devm_add_action_or_reset to tidy up error and remove handling.
* ad7793
  - Switch to read_avail from explicit attribute. Mostly done to avoid
    confusing people with a - sign (without surounding spaces) that
    was correct but checkpatch didn't like.
* adis library
  - Add missing newlines at end of error messages.
* adis16400
  - Use DEFINE_DEBUGS_ATTRIBUTE rather than DEFINE_SIMPLE_ATTR.
* adis16460
  - Use DEFINE_DEBUGS_ATTRIBUTE rather than DEFINE_SIMPLE_ATTR.
* ad_sigma_delta
  - Move some channel definitions into individual drivers to avoid
    having to deal with complex options.
* ak8974
  - Silence an error on deffered probe.
* bmp280
  - Harden against IRQ before registration.
  - Convert to read_avail instead of opencoding the equivalent.
  - Explicitly mark GPIO as optional.
  - Cleanup casts.
  - Remove line breaks from strings.
* htts221
  - Replace direct access to platform_data with dev_get_platdata().
  - Use device properties rather than device tree ones to allow ACPI
    probing.
  - Casting cleanups.
* intel_mrfld_adc
  - Don't use unaligned accessor for aligned data.
* isl29125
  - Reorder buer pre and post hooks to allow for coming core rework.
* ltc2983
  - Remove comp to bool.
* max1363
  - Stop using mlock from the iio_dev directly in favour of a local lock
    with clearly defined scope.
* max30100
  - Use generic device properties to allow ACPI probe.
* mpu6050
  - Convert to i2c_new_client_device.
  - Add debugfs register access.
* st_lsm6dsx
  - Provide means of configuring full scale on slave device if supported.
  - Drop include of st_sensors header to get one value. Its not otherwise
    used by this driver.
* st-sensors
  - Replace direct access to platform_data with dev_get_platdata().
  - Casting cleanups.
  - Avoid splitting strings.
* st_uvis25
  - Casting cleanups.
* tsl2563
  - Typo fix.
* tsl2772
  - scnprintf in a non obvious string building usecase. Note also 'fixes'
    a wrong calculation of remaining space that couldn't actually cause
    any trouble as there was lots of room.
* xilinx-xadc
  - Fix Lars-Peter spelling his own name wrong :) + additional typos.

* tag 'iio-for-5.8a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (101 commits)
  iio: magnetometer: ak8974: Silence deferred-probe error
  Documentation: ABI: document IIO in_proximity_nearlevel file
  iio: vcnl4000: Export near level property for proximity sensor
  dt-bindings: iio: light: vcnl4000: Add proximity-near-level
  dt-bindings: iio: Introduce common properties for iio sensors
  dt-bindings: iio: vcnl4000: convert bindings to YAML format
  iio: Fix misspellings of "Analog Devices"
  iio: light: isl29125: fix iio_triggered_buffer_{predisable,postenable} positions
  iio: adc: fsl-imx25-gcq: Use devm_platform_ioremap_resource
  iio: adc: at91-adc: Use devm_platform_ioremap_resource
  iio: adc: sun4i-gpadc-iio: Use devm_platform_ioremap_resource
  iio:light:ltr501: Drop unnecessary cast of parameter in regmap_bulk_read
  iio:magn:mmc35240: Drop unnecessary casts of val parameter in regmap_bulk*
  iio:imu:mpu6050: Tidy up parameters to regmap_bulk functions.
  iio:chemical:bme680: Tidy up parameters to regmap_bulk_read
  iio:chemical:atlas-sensor: Drop unnecessary explicit casts in regmap_bulk_read calls
  iio:accel:mxc4005: Drop unnecessary explicit casts in regmap_bulk_read calls
  iio: imu: st_lsm6dsx: drop huge include in sensor-hub driver
  iio: buffer: drop devm_iio_kfifo_free() API call
  iio: buffer: drop devm_iio_hw_consumer_free() API call
  ...
2020-04-23 11:06:48 +02:00

570 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4040/4200 combined ambient
* light and proximity sensor
*
* Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
* Copyright 2019 Pursim SPC
*
* IIO driver for:
* VCNL4000/10/20 (7-bit I2C slave address 0x13)
* VCNL4040 (7-bit I2C slave address 0x60)
* VCNL4200 (7-bit I2C slave address 0x51)
*
* TODO:
* allow to adjust IR current
* proximity threshold and event handling
* periodic ALS/proximity measurement (VCNL4010/20)
* interrupts (VCNL4010/20/40, VCNL4200)
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define VCNL4000_DRV_NAME "vcnl4000"
#define VCNL4000_PROD_ID 0x01
#define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */
#define VCNL4040_PROD_ID 0x86
#define VCNL4200_PROD_ID 0x58
#define VCNL4000_COMMAND 0x80 /* Command register */
#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
#define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */
#define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */
#define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */
#define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */
#define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */
#define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */
#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
#define VCNL4200_PS_DATA 0x08 /* Proximity data */
#define VCNL4200_AL_DATA 0x09 /* Ambient light data */
#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */
#define VCNL4040_DEV_ID 0x0c /* Device ID and version */
/* Bit masks for COMMAND register */
#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
#define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */
enum vcnl4000_device_ids {
VCNL4000,
VCNL4010,
VCNL4040,
VCNL4200,
};
struct vcnl4200_channel {
u8 reg;
ktime_t last_measurement;
ktime_t sampling_rate;
struct mutex lock;
};
struct vcnl4000_data {
struct i2c_client *client;
enum vcnl4000_device_ids id;
int rev;
int al_scale;
const struct vcnl4000_chip_spec *chip_spec;
struct mutex vcnl4000_lock;
struct vcnl4200_channel vcnl4200_al;
struct vcnl4200_channel vcnl4200_ps;
uint32_t near_level;
};
struct vcnl4000_chip_spec {
const char *prod;
int (*init)(struct vcnl4000_data *data);
int (*measure_light)(struct vcnl4000_data *data, int *val);
int (*measure_proximity)(struct vcnl4000_data *data, int *val);
int (*set_power_state)(struct vcnl4000_data *data, bool on);
};
static const struct i2c_device_id vcnl4000_id[] = {
{ "vcnl4000", VCNL4000 },
{ "vcnl4010", VCNL4010 },
{ "vcnl4020", VCNL4010 },
{ "vcnl4040", VCNL4040 },
{ "vcnl4200", VCNL4200 },
{ }
};
MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on)
{
/* no suspend op */
return 0;
}
static int vcnl4000_init(struct vcnl4000_data *data)
{
int ret, prod_id;
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
if (ret < 0)
return ret;
prod_id = ret >> 4;
switch (prod_id) {
case VCNL4000_PROD_ID:
if (data->id != VCNL4000)
dev_warn(&data->client->dev,
"wrong device id, use vcnl4000");
break;
case VCNL4010_PROD_ID:
if (data->id != VCNL4010)
dev_warn(&data->client->dev,
"wrong device id, use vcnl4010/4020");
break;
default:
return -ENODEV;
}
data->rev = ret & 0xf;
data->al_scale = 250000;
mutex_init(&data->vcnl4000_lock);
return data->chip_spec->set_power_state(data, true);
};
static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
{
u16 val = on ? 0 /* power on */ : 1 /* shut down */;
int ret;
ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val);
if (ret < 0)
return ret;
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
if (ret < 0)
return ret;
if (on) {
/* Wait at least one integration cycle before fetching data */
data->vcnl4200_al.last_measurement = ktime_get();
data->vcnl4200_ps.last_measurement = ktime_get();
}
return 0;
}
static int vcnl4200_init(struct vcnl4000_data *data)
{
int ret, id;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
if (ret < 0)
return ret;
id = ret & 0xff;
if (id != VCNL4200_PROD_ID) {
ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID);
if (ret < 0)
return ret;
id = ret & 0xff;
if (id != VCNL4040_PROD_ID)
return -ENODEV;
}
dev_dbg(&data->client->dev, "device id 0x%x", id);
data->rev = (ret >> 8) & 0xf;
data->vcnl4200_al.reg = VCNL4200_AL_DATA;
data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
switch (id) {
case VCNL4200_PROD_ID:
/* Default wait time is 50ms, add 20% tolerance. */
data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000);
/* Default wait time is 4.8ms, add 20% tolerance. */
data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000);
data->al_scale = 24000;
break;
case VCNL4040_PROD_ID:
/* Default wait time is 80ms, add 20% tolerance. */
data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000);
/* Default wait time is 5ms, add 20% tolerance. */
data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000);
data->al_scale = 120000;
break;
}
mutex_init(&data->vcnl4200_al.lock);
mutex_init(&data->vcnl4200_ps.lock);
ret = data->chip_spec->set_power_state(data, true);
if (ret < 0)
return ret;
return 0;
};
static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
u8 rdy_mask, u8 data_reg, int *val)
{
int tries = 20;
__be16 buf;
int ret;
mutex_lock(&data->vcnl4000_lock);
ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
req_mask);
if (ret < 0)
goto fail;
/* wait for data to become ready */
while (tries--) {
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
if (ret < 0)
goto fail;
if (ret & rdy_mask)
break;
msleep(20); /* measurement takes up to 100 ms */
}
if (tries < 0) {
dev_err(&data->client->dev,
"vcnl4000_measure() failed, data not ready\n");
ret = -EIO;
goto fail;
}
ret = i2c_smbus_read_i2c_block_data(data->client,
data_reg, sizeof(buf), (u8 *) &buf);
if (ret < 0)
goto fail;
mutex_unlock(&data->vcnl4000_lock);
*val = be16_to_cpu(buf);
return 0;
fail:
mutex_unlock(&data->vcnl4000_lock);
return ret;
}
static int vcnl4200_measure(struct vcnl4000_data *data,
struct vcnl4200_channel *chan, int *val)
{
int ret;
s64 delta;
ktime_t next_measurement;
mutex_lock(&chan->lock);
next_measurement = ktime_add(chan->last_measurement,
chan->sampling_rate);
delta = ktime_us_delta(next_measurement, ktime_get());
if (delta > 0)
usleep_range(delta, delta + 500);
chan->last_measurement = ktime_get();
mutex_unlock(&chan->lock);
ret = i2c_smbus_read_word_data(data->client, chan->reg);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
{
return vcnl4000_measure(data,
VCNL4000_AL_OD, VCNL4000_AL_RDY,
VCNL4000_AL_RESULT_HI, val);
}
static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
{
return vcnl4200_measure(data, &data->vcnl4200_al, val);
}
static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
{
return vcnl4000_measure(data,
VCNL4000_PS_OD, VCNL4000_PS_RDY,
VCNL4000_PS_RESULT_HI, val);
}
static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
{
return vcnl4200_measure(data, &data->vcnl4200_ps, val);
}
static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
[VCNL4000] = {
.prod = "VCNL4000",
.init = vcnl4000_init,
.measure_light = vcnl4000_measure_light,
.measure_proximity = vcnl4000_measure_proximity,
.set_power_state = vcnl4000_set_power_state,
},
[VCNL4010] = {
.prod = "VCNL4010/4020",
.init = vcnl4000_init,
.measure_light = vcnl4000_measure_light,
.measure_proximity = vcnl4000_measure_proximity,
.set_power_state = vcnl4000_set_power_state,
},
[VCNL4040] = {
.prod = "VCNL4040",
.init = vcnl4200_init,
.measure_light = vcnl4200_measure_light,
.measure_proximity = vcnl4200_measure_proximity,
.set_power_state = vcnl4200_set_power_state,
},
[VCNL4200] = {
.prod = "VCNL4200",
.init = vcnl4200_init,
.measure_light = vcnl4200_measure_light,
.measure_proximity = vcnl4200_measure_proximity,
.set_power_state = vcnl4200_set_power_state,
},
};
static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev,
uintptr_t priv,
const struct iio_chan_spec *chan,
char *buf)
{
struct vcnl4000_data *data = iio_priv(indio_dev);
return sprintf(buf, "%u\n", data->near_level);
}
static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = {
{
.name = "nearlevel",
.shared = IIO_SEPARATE,
.read = vcnl4000_read_near_level,
},
{ /* sentinel */ }
};
static const struct iio_chan_spec vcnl4000_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
}, {
.type = IIO_PROXIMITY,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.ext_info = vcnl4000_ext_info,
}
};
static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
{
struct device *dev = &data->client->dev;
int ret;
if (on) {
ret = pm_runtime_get_sync(dev);
if (ret < 0)
pm_runtime_put_noidle(dev);
} else {
pm_runtime_mark_last_busy(dev);
ret = pm_runtime_put_autosuspend(dev);
}
return ret;
}
static int vcnl4000_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = vcnl4000_set_pm_runtime_state(data, true);
if (ret < 0)
return ret;
switch (chan->type) {
case IIO_LIGHT:
ret = data->chip_spec->measure_light(data, val);
if (!ret)
ret = IIO_VAL_INT;
break;
case IIO_PROXIMITY:
ret = data->chip_spec->measure_proximity(data, val);
if (!ret)
ret = IIO_VAL_INT;
break;
default:
ret = -EINVAL;
}
vcnl4000_set_pm_runtime_state(data, false);
return ret;
case IIO_CHAN_INFO_SCALE:
if (chan->type != IIO_LIGHT)
return -EINVAL;
*val = 0;
*val2 = data->al_scale;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static const struct iio_info vcnl4000_info = {
.read_raw = vcnl4000_read_raw,
};
static int vcnl4000_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct vcnl4000_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->id = id->driver_data;
data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
ret = data->chip_spec->init(data);
if (ret < 0)
return ret;
dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
data->chip_spec->prod, data->rev);
if (device_property_read_u32(&client->dev, "proximity-near-level",
&data->near_level))
data->near_level = 0;
indio_dev->dev.parent = &client->dev;
indio_dev->info = &vcnl4000_info;
indio_dev->channels = vcnl4000_channels;
indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels);
indio_dev->name = VCNL4000_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
goto fail_poweroff;
ret = iio_device_register(indio_dev);
if (ret < 0)
goto fail_poweroff;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
return 0;
fail_poweroff:
data->chip_spec->set_power_state(data, false);
return ret;
}
static const struct of_device_id vcnl_4000_of_match[] = {
{
.compatible = "vishay,vcnl4000",
.data = (void *)VCNL4000,
},
{
.compatible = "vishay,vcnl4010",
.data = (void *)VCNL4010,
},
{
.compatible = "vishay,vcnl4020",
.data = (void *)VCNL4010,
},
{
.compatible = "vishay,vcnl4040",
.data = (void *)VCNL4040,
},
{
.compatible = "vishay,vcnl4200",
.data = (void *)VCNL4200,
},
{},
};
MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
static int vcnl4000_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct vcnl4000_data *data = iio_priv(indio_dev);
pm_runtime_dont_use_autosuspend(&client->dev);
pm_runtime_disable(&client->dev);
iio_device_unregister(indio_dev);
pm_runtime_set_suspended(&client->dev);
return data->chip_spec->set_power_state(data, false);
}
static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct vcnl4000_data *data = iio_priv(indio_dev);
return data->chip_spec->set_power_state(data, false);
}
static int __maybe_unused vcnl4000_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct vcnl4000_data *data = iio_priv(indio_dev);
return data->chip_spec->set_power_state(data, true);
}
static const struct dev_pm_ops vcnl4000_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend,
vcnl4000_runtime_resume, NULL)
};
static struct i2c_driver vcnl4000_driver = {
.driver = {
.name = VCNL4000_DRV_NAME,
.pm = &vcnl4000_pm_ops,
.of_match_table = vcnl_4000_of_match,
},
.probe = vcnl4000_probe,
.id_table = vcnl4000_id,
.remove = vcnl4000_remove,
};
module_i2c_driver(vcnl4000_driver);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
MODULE_LICENSE("GPL");