linux/drivers/iio/light/apds9306.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * APDS-9306/APDS-9306-065 Ambient Light Sensor
 * I2C Address: 0x52
 * Datasheet: https://docs.broadcom.com/doc/AV02-4755EN
 *
 * Copyright (C) 2024 Subhajit Ghosh <[email protected]>
 */

#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
#include <linux/units.h>

#include <linux/iio/iio.h>
#include <linux/iio/iio-gts-helper.h>
#include <linux/iio/events.h>
#include <linux/iio/sysfs.h>

#include <linux/unaligned.h>

#define APDS9306_MAIN_CTRL_REG
#define APDS9306_ALS_MEAS_RATE_REG
#define APDS9306_ALS_GAIN_REG
#define APDS9306_PART_ID_REG
#define APDS9306_MAIN_STATUS_REG
#define APDS9306_CLEAR_DATA_0_REG
#define APDS9306_CLEAR_DATA_1_REG
#define APDS9306_CLEAR_DATA_2_REG
#define APDS9306_ALS_DATA_0_REG
#define APDS9306_ALS_DATA_1_REG
#define APDS9306_ALS_DATA_2_REG
#define APDS9306_INT_CFG_REG
#define APDS9306_INT_PERSISTENCE_REG
#define APDS9306_ALS_THRES_UP_0_REG
#define APDS9306_ALS_THRES_UP_1_REG
#define APDS9306_ALS_THRES_UP_2_REG
#define APDS9306_ALS_THRES_LOW_0_REG
#define APDS9306_ALS_THRES_LOW_1_REG
#define APDS9306_ALS_THRES_LOW_2_REG
#define APDS9306_ALS_THRES_VAR_REG

#define APDS9306_ALS_INT_STAT_MASK
#define APDS9306_ALS_DATA_STAT_MASK

#define APDS9306_ALS_THRES_VAL_MAX
#define APDS9306_ALS_THRES_VAR_NUM_VALS
#define APDS9306_ALS_PERSIST_NUM_VALS
#define APDS9306_ALS_READ_DATA_DELAY_US
#define APDS9306_NUM_REPEAT_RATES
#define APDS9306_INT_SRC_CLEAR
#define APDS9306_INT_SRC_ALS
#define APDS9306_SAMP_FREQ_10HZ

/**
 * struct part_id_gts_multiplier - Part no. and corresponding gts multiplier
 *
 * GTS (Gain Time Scale) are helper functions for Light sensors which along
 * with hardware gains also has gains associated with Integration times.
 *
 * There are two variants of the device with slightly different characteristics,
 * they have same ADC count for different Lux levels as mentioned in the
 * datasheet. This multiplier array is used to store the derived Lux per count
 * value for the two variants to be used by the GTS helper functions.
 *
 * @part_id: Part ID of the device
 * @max_scale_int: Multiplier for iio_init_iio_gts()
 * @max_scale_nano: Multiplier for iio_init_iio_gts()
 */
struct part_id_gts_multiplier {};

/*
 * As per the datasheet, at HW Gain = 3x, Integration time 100mS (32x),
 * typical 2000 ADC counts are observed for 49.8 uW per sq cm (340.134 lux)
 * for apds9306 and 43 uW per sq cm (293.69 lux) for apds9306-065.
 * Assuming lux per count is linear across all integration time ranges.
 *
 * Lux = (raw + offset) * scale; offset can be any value by userspace.
 * HG = Hardware Gain; ITG = Gain by changing integration time.
 * Scale table by IIO GTS Helpers = (1 / HG) * (1 / ITG) * Multiplier.
 *
 * The Lux values provided in the datasheet are at ITG=32x and HG=3x,
 * at typical 2000 count for both variants of the device.
 *
 * Lux per ADC count at 3x and 32x for apds9306 = 340.134 / 2000
 * Lux per ADC count at 3x and 32x for apds9306-065 = 293.69 / 2000
 *
 * The Multiplier for the scale table provided to userspace:
 * IIO GTS scale Multiplier for apds9306 = (340.134 / 2000) * 32 * 3 = 16.326432
 * and for apds9306-065 = (293.69 / 2000) * 32 * 3 = 14.09712
 */
static const struct part_id_gts_multiplier apds9306_gts_mul[] =;

static const int apds9306_repeat_rate_freq[APDS9306_NUM_REPEAT_RATES][2] =;

static const int apds9306_repeat_rate_period[APDS9306_NUM_REPEAT_RATES] =;

/**
 * struct apds9306_regfields - apds9306 regmap fields definitions
 *
 * @sw_reset: Software reset regfield
 * @en: Enable regfield
 * @intg_time: Resolution regfield
 * @repeat_rate: Measurement Rate regfield
 * @gain: Hardware gain regfield
 * @int_src: Interrupt channel regfield
 * @int_thresh_var_en: Interrupt variance threshold regfield
 * @int_en: Interrupt enable regfield
 * @int_persist_val: Interrupt persistence regfield
 * @int_thresh_var_val: Interrupt threshold variance value regfield
 */
struct apds9306_regfields {};

/**
 * struct apds9306_data - apds9306 private data and registers definitions
 *
 * @dev: Pointer to the device structure
 * @gts: IIO Gain Time Scale structure
 * @mutex: Lock for protecting adc reads, device settings changes where
 *         some calculations are required before or after setting or
 *         getting the raw settings values from regmap writes or reads
 *         respectively.
 * @regmap: Regmap structure pointer
 * @rf: Regmap register fields structure
 * @nlux_per_count: Nano lux per ADC count for a particular model
 * @read_data_available: Flag set by IRQ handler for ADC data available
 */
struct apds9306_data {};

/*
 * Available scales with gain 1x - 18x, timings 3.125, 25, 50, 100, 200, 400 ms
 * Time impacts to gain: 1x, 8x, 16x, 32x, 64x, 128x
 */
#define APDS9306_GSEL_1X
#define APDS9306_GSEL_3X
#define APDS9306_GSEL_6X
#define APDS9306_GSEL_9X
#define APDS9306_GSEL_18X

static const struct iio_gain_sel_pair apds9306_gains[] =;

#define APDS9306_MEAS_MODE_400MS
#define APDS9306_MEAS_MODE_200MS
#define APDS9306_MEAS_MODE_100MS
#define APDS9306_MEAS_MODE_50MS
#define APDS9306_MEAS_MODE_25MS
#define APDS9306_MEAS_MODE_3125US

static const struct iio_itime_sel_mul apds9306_itimes[] =;

static const struct iio_event_spec apds9306_event_spec[] =;

static const struct iio_chan_spec apds9306_channels_with_events[] =;

static const struct iio_chan_spec apds9306_channels_without_events[] =;

/* INT_PERSISTENCE available */
static IIO_CONST_ATTR(thresh_either_period_available, "[0 1 15]");

/* ALS_THRESH_VAR available */
static IIO_CONST_ATTR(thresh_adaptive_either_values_available, "[0 1 7]");

static struct attribute *apds9306_event_attributes[] =;

static const struct attribute_group apds9306_event_attr_group =;

static const struct regmap_range apds9306_readable_ranges[] =;

static const struct regmap_range apds9306_writable_ranges[] =;

static const struct regmap_range apds9306_volatile_ranges[] =;

static const struct regmap_range apds9306_precious_ranges[] =;

static const struct regmap_access_table apds9306_readable_table =;

static const struct regmap_access_table apds9306_writable_table =;

static const struct regmap_access_table apds9306_volatile_table =;

static const struct regmap_access_table apds9306_precious_table =;

static const struct regmap_config apds9306_regmap =;

static const struct reg_field apds9306_rf_sw_reset =;

static const struct reg_field apds9306_rf_en =;

static const struct reg_field apds9306_rf_intg_time =;

static const struct reg_field apds9306_rf_repeat_rate =;

static const struct reg_field apds9306_rf_gain =;

static const struct reg_field apds9306_rf_int_src =;

static const struct reg_field apds9306_rf_int_thresh_var_en =;

static const struct reg_field apds9306_rf_int_en =;

static const struct reg_field apds9306_rf_int_persist_val =;

static const struct reg_field apds9306_rf_int_thresh_var_val =;

static int apds9306_regfield_init(struct apds9306_data *data)
{}

static int apds9306_power_state(struct apds9306_data *data, bool state)
{}

static int apds9306_read_data(struct apds9306_data *data, int *val, int reg)
{}

static int apds9306_intg_time_get(struct apds9306_data *data, int *val2)
{}

static int apds9306_intg_time_set(struct apds9306_data *data, int val2)
{}

static int apds9306_sampling_freq_get(struct apds9306_data *data, int *val,
				      int *val2)
{}

static int apds9306_sampling_freq_set(struct apds9306_data *data, int val,
				      int val2)
{}

static int apds9306_scale_get(struct apds9306_data *data, int *val, int *val2)
{}

static int apds9306_scale_set(struct apds9306_data *data, int val, int val2)
{}

static int apds9306_event_period_get(struct apds9306_data *data, int *val)
{}

static int apds9306_event_period_set(struct apds9306_data *data, int val)
{}

static int apds9306_event_thresh_get(struct apds9306_data *data, int dir,
				     int *val)
{}

static int apds9306_event_thresh_set(struct apds9306_data *data, int dir,
				     int val)
{}

static int apds9306_event_thresh_adaptive_get(struct apds9306_data *data, int *val)
{}

static int apds9306_event_thresh_adaptive_set(struct apds9306_data *data, int val)
{}

static int apds9306_read_raw(struct iio_dev *indio_dev,
			     struct iio_chan_spec const *chan, int *val,
			     int *val2, long mask)
{
	struct apds9306_data *data = iio_priv(indio_dev);
	int ret, reg;

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		if (chan->channel2 == IIO_MOD_LIGHT_CLEAR)
			reg = APDS9306_CLEAR_DATA_0_REG;
		else
			reg = APDS9306_ALS_DATA_0_REG;
		/*
		 * Changing device parameters during adc operation, resets
		 * the ADC which has to avoided.
		 */
		ret = iio_device_claim_direct_mode(indio_dev);
		if (ret)
			return ret;
		ret = apds9306_read_data(data, val, reg);
		iio_device_release_direct_mode(indio_dev);
		if (ret)
			return ret;

		return IIO_VAL_INT;
	case IIO_CHAN_INFO_INT_TIME:
		ret = apds9306_intg_time_get(data, val2);
		if (ret)
			return ret;
		*val = 0;

		return IIO_VAL_INT_PLUS_MICRO;
	case IIO_CHAN_INFO_SAMP_FREQ:
		ret = apds9306_sampling_freq_get(data, val, val2);
		if (ret)
			return ret;

		return IIO_VAL_INT_PLUS_MICRO;
	case IIO_CHAN_INFO_SCALE:
		ret = apds9306_scale_get(data, val, val2);
		if (ret)
			return ret;

		return IIO_VAL_INT_PLUS_NANO;
	default:
		return -EINVAL;
	}
};

static int apds9306_read_avail(struct iio_dev *indio_dev,
			       struct iio_chan_spec const *chan,
			       const int **vals, int *type, int *length,
			       long mask)
{}

static int apds9306_write_raw_get_fmt(struct iio_dev *indio_dev,
				      struct iio_chan_spec const *chan,
				      long mask)
{}

static int apds9306_write_raw(struct iio_dev *indio_dev,
			      struct iio_chan_spec const *chan, int val,
			      int val2, long mask)
{}

static irqreturn_t apds9306_irq_handler(int irq, void *priv)
{}

static int apds9306_read_event(struct iio_dev *indio_dev,
			       const struct iio_chan_spec *chan,
			       enum iio_event_type type,
			       enum iio_event_direction dir,
			       enum iio_event_info info,
			       int *val, int *val2)
{}

static int apds9306_write_event(struct iio_dev *indio_dev,
				const struct iio_chan_spec *chan,
				enum iio_event_type type,
				enum iio_event_direction dir,
				enum iio_event_info info,
				int val, int val2)
{}

static int apds9306_read_event_config(struct iio_dev *indio_dev,
				      const struct iio_chan_spec *chan,
				      enum iio_event_type type,
				      enum iio_event_direction dir)
{}

static int apds9306_write_event_config(struct iio_dev *indio_dev,
				       const struct iio_chan_spec *chan,
				       enum iio_event_type type,
				       enum iio_event_direction dir,
				       int state)
{}

static const struct iio_info apds9306_info_no_events =;

static const struct iio_info apds9306_info =;

static int apds9306_init_iio_gts(struct apds9306_data *data)
{}

static void apds9306_powerdown(void *ptr)
{}

static int apds9306_device_init(struct apds9306_data *data)
{}

static int apds9306_pm_init(struct apds9306_data *data)
{}

static int apds9306_probe(struct i2c_client *client)
{}

static int apds9306_runtime_suspend(struct device *dev)
{}

static int apds9306_runtime_resume(struct device *dev)
{}

static DEFINE_RUNTIME_DEV_PM_OPS(apds9306_pm_ops,
				 apds9306_runtime_suspend,
				 apds9306_runtime_resume,
				 NULL);

static const struct of_device_id apds9306_of_match[] =;
MODULE_DEVICE_TABLE(of, apds9306_of_match);

static struct i2c_driver apds9306_driver =;
module_i2c_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();
MODULE_IMPORT_NS();