// 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
* Copyright 2020 Mathieu Othacehe <m.othacehe@gmail.com>
*
* 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
* interrupts (VCNL4040, VCNL4200)
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
#include <linux/interrupt.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.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 VCNL4010_PROX_RATE 0x82 /* Proximity rate */
#define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */
#define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */
#define VCNL4010_ALS_PARAM 0x84 /* ALS rate */
#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 VCNL4010_INT_CTRL 0x89 /* Interrupt control */
#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
#define VCNL4010_LOW_THR_HI 0x8a /* Low threshold, MSB */
#define VCNL4010_LOW_THR_LO 0x8b /* Low threshold, LSB */
#define VCNL4010_HIGH_THR_HI 0x8c /* High threshold, MSB */
#define VCNL4010_HIGH_THR_LO 0x8d /* High threshold, LSB */
#define VCNL4010_ISR 0x8e /* Interrupt status */
#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_ALS_EN BIT(2) /* start ALS measurement */
#define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */
#define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */
/* Bit masks for interrupt registers. */
#define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */
#define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */
#define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */
#define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */
#define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */
#define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */
#define VCNL4010_INT_ALS 2 /* ALS data ready */
#define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */
#define VCNL4010_INT_THR \
(BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH))
#define VCNL4010_INT_DRDY \
(BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS))
static const int vcnl4010_prox_sampling_frequency[][2] = {
{1, 950000},
{3, 906250},
{7, 812500},
{16, 625000},
{31, 250000},
{62, 500000},
{125, 0},
{250, 0},
};
#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;
struct iio_chan_spec const *channels;
const int num_channels;
const struct iio_info *info;
bool irq_support;
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 <