linux/drivers/iio/light/rohm-bu27034.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * BU27034ANUC ROHM Ambient Light Sensor
 *
 * Copyright (c) 2023, ROHM Semiconductor.
 */

#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/units.h>

#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/iio-gts-helper.h>
#include <linux/iio/kfifo_buf.h>

#define BU27034_REG_SYSTEM_CONTROL
#define BU27034_MASK_SW_RESET
#define BU27034_MASK_PART_ID
#define BU27034_ID
#define BU27034_REG_MODE_CONTROL1
#define BU27034_MASK_MEAS_MODE

#define BU27034_REG_MODE_CONTROL2
#define BU27034_MASK_D01_GAIN

#define BU27034_REG_MODE_CONTROL3
#define BU27034_REG_MODE_CONTROL4
#define BU27034_MASK_MEAS_EN
#define BU27034_MASK_VALID
#define BU27034_NUM_HW_DATA_CHANS
#define BU27034_REG_DATA0_LO
#define BU27034_REG_DATA1_LO
#define BU27034_REG_DATA1_HI
#define BU27034_REG_MANUFACTURER_ID
#define BU27034_REG_MAX

/*
 * The BU27034 does not have interrupt to trigger the data read when a
 * measurement has finished. Hence we poll the VALID bit in a thread. We will
 * try to wake the thread BU27034_MEAS_WAIT_PREMATURE_MS milliseconds before
 * the expected sampling time to prevent the drifting.
 *
 * If we constantly wake up a bit too late we would eventually skip a sample.
 * And because the sleep can't wake up _exactly_ at given time this would be
 * inevitable even if the sensor clock would be perfectly phase-locked to CPU
 * clock - which we can't say is the case.
 *
 * This is still fragile. No matter how big advance do we have, we will still
 * risk of losing a sample because things can in a rainy-day scenario be
 * delayed a lot. Yet, more we reserve the time for polling, more we also lose
 * the performance by spending cycles polling the register. So, selecting this
 * value is a balancing dance between severity of wasting CPU time and severity
 * of losing samples.
 *
 * In most cases losing the samples is not _that_ crucial because light levels
 * tend to change slowly.
 *
 * Other option that was pointed to me would be always sleeping 1/2 of the
 * measurement time, checking the VALID bit and just sleeping again if the bit
 * was not set. That should be pretty tolerant against missing samples due to
 * the scheduling delays while also not wasting much of cycles for polling.
 * Downside is that the time-stamps would be very inaccurate as the wake-up
 * would not really be tied to the sensor toggling the valid bit. This would also
 * result 'jumps' in the time-stamps when the delay drifted so that wake-up was
 * performed during the consecutive wake-ups (Or, when sensor and CPU clocks
 * were very different and scheduling the wake-ups was very close to given
 * timeout - and when the time-outs were very close to the actual sensor
 * sampling, Eg. once in a blue moon, two consecutive time-outs would occur
 * without having a sample ready).
 */
#define BU27034_MEAS_WAIT_PREMATURE_MS
#define BU27034_DATA_WAIT_TIME_US
#define BU27034_TOTAL_DATA_WAIT_TIME_US

#define BU27034_RETRY_LIMIT

enum {};

static const unsigned long bu27034_scan_masks[] =;

/*
 * Available scales with gain 1x - 1024x, timings 55, 100, 200, 400 mS
 * Time impacts to gain: 1x, 2x, 4x, 8x.
 *
 * => Max total gain is HWGAIN * gain by integration time (8 * 1024) = 8192
 * if 1x gain is scale 1, scale for 2x gain is 0.5, 4x => 0.25,
 * ... 8192x => 0.0001220703125 => 122070.3125 nanos
 *
 * Using NANO precision for scale, we must use scale 16x corresponding gain 1x
 * to avoid precision loss. (8x would result scale 976 562.5(nanos).
 */
#define BU27034_SCALE_1X

/* See the data sheet for the "Gain Setting" table */
#define BU27034_GSEL_1X
#define BU27034_GSEL_4X
#define BU27034_GSEL_32X
#define BU27034_GSEL_256X
#define BU27034_GSEL_512X
#define BU27034_GSEL_1024X

/* Available gain settings */
static const struct iio_gain_sel_pair bu27034_gains[] =;

/*
 * Measurement modes are 55, 100, 200 and 400 mS modes - which do have direct
 * multiplying impact to the data register values (similar to gain).
 *
 * This means that if meas-mode is changed for example from 400 => 200,
 * the scale is doubled. Eg, time impact to total gain is x1, x2, x4, x8.
 */
#define BU27034_MEAS_MODE_100MS
#define BU27034_MEAS_MODE_55MS
#define BU27034_MEAS_MODE_200MS
#define BU27034_MEAS_MODE_400MS

static const struct iio_itime_sel_mul bu27034_itimes[] =;

#define BU27034_CHAN_DATA(_name)

static const struct iio_chan_spec bu27034_channels[] =;

struct bu27034_data {};

static const struct regmap_range bu27034_volatile_ranges[] =;

static const struct regmap_access_table bu27034_volatile_regs =;

static const struct regmap_range bu27034_read_only_ranges[] =;

static const struct regmap_access_table bu27034_ro_regs =;

static const struct regmap_config bu27034_regmap =;

struct bu27034_gain_check {};

static int bu27034_get_gain_sel(struct bu27034_data *data, int chan)
{}

static int bu27034_get_gain(struct bu27034_data *data, int chan, int *gain)
{}

static int bu27034_get_int_time(struct bu27034_data *data)
{}

static int _bu27034_get_scale(struct bu27034_data *data, int channel, int *val,
			      int *val2)
{}

static int bu27034_get_scale(struct bu27034_data *data, int channel, int *val,
			      int *val2)
{}

/* Caller should hold the lock to protect lux reading */
static int bu27034_write_gain_sel(struct bu27034_data *data, int chan, int sel)
{}

static int bu27034_set_gain(struct bu27034_data *data, int chan, int gain)
{}

/* Caller should hold the lock to protect data->int_time */
static int bu27034_set_int_time(struct bu27034_data *data, int time)
{}

/*
 * We try to change the time in such way that the scale is maintained for
 * given channels by adjusting gain so that it compensates the time change.
 */
static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us)
{}

static int bu27034_set_scale(struct bu27034_data *data, int chan,
			    int val, int val2)
{}

/*
 * for (D1/D0 < 1.5):
 *    lx = (0.001193 * D0 + (-0.0000747) * D1) * ((D1/D0 – 1.5) * (0.25) + 1)
 *
 *    => -0.000745625 * D0 + 0.0002515625 * D1 + -0.000018675 * D1 * D1 / D0
 *
 *    => (6.44 * ch1 / gain1 + 19.088 * ch0 / gain0 -
 *       0.47808 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0) /
 *       mt
 *
 * Else
 *    lx = 0.001193 * D0 - 0.0000747 * D1
 *
 *    => (1.91232 * ch1 / gain1 + 30.5408 * ch0 / gain0 +
 *        [0 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0] ) /
 *        mt
 *
 * This can be unified to format:
 * lx = [
 *	 A * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) +
 *	 B * ch1 / gain1 +
 *	 C * ch0 / gain0
 *	] / mt
 *
 * For case 1:
 * A = -0.47808,
 * B = 6.44,
 * C = 19.088
 *
 * For case 2:
 * A = 0
 * B = 1.91232
 * C = 30.5408
 */

struct bu27034_lx_coeff {};

static inline u64 gain_mul_div_helper(u64 val, unsigned int gain,
				      unsigned int div)
{}

static u64 bu27034_fixp_calc_t1_64bit(unsigned int coeff, unsigned int ch0,
				      unsigned int ch1, unsigned int gain0,
				      unsigned int gain1)
{}

static u64 bu27034_fixp_calc_t1(unsigned int coeff, unsigned int ch0,
				unsigned int ch1, unsigned int gain0,
				unsigned int gain1)
{}

static u64 bu27034_fixp_calc_t23(unsigned int coeff, unsigned int ch,
				 unsigned int gain)
{}

static int bu27034_fixp_calc_lx(unsigned int ch0, unsigned int ch1,
				unsigned int gain0, unsigned int gain1,
				unsigned int meastime, int coeff_idx)
{}

static bool bu27034_has_valid_sample(struct bu27034_data *data)
{}

/*
 * Reading the register where VALID bit is clears this bit. (So does changing
 * any gain / integration time configuration registers) The bit gets
 * set when we have acquired new data. We use this bit to indicate data
 * validity.
 */
static void bu27034_invalidate_read_data(struct bu27034_data *data)
{}

static int bu27034_read_result(struct bu27034_data *data, int chan, int *res)
{}

static int bu27034_get_result_unlocked(struct bu27034_data *data, __le16 *res,
				       int size)
{}

static int bu27034_meas_set(struct bu27034_data *data, bool en)
{}

static int bu27034_get_single_result(struct bu27034_data *data, int chan,
				     int *val)
{}

/*
 * The formula given by vendor for computing luxes out of data0 and data1
 * (in open air) is as follows:
 *
 * Let's mark:
 * D0 = data0/ch0_gain/meas_time_ms * 25600
 * D1 = data1/ch1_gain/meas_time_ms * 25600
 *
 * Then:
 * If (D1/D0 < 1.5)
 *    lx = (0.001193 * D0 + (-0.0000747) * D1) * ((D1 / D0 – 1.5) * 0.25 + 1)
 * Else
 *    lx = (0.001193 * D0 + (-0.0000747) * D1)
 *
 * We use it here. Users who have for example some colored lens
 * need to modify the calculation but I hope this gives a starting point for
 * those working with such devices.
 */

static int bu27034_calc_mlux(struct bu27034_data *data, __le16 *res, int *val)
{}

static int bu27034_get_mlux(struct bu27034_data *data, int chan, int *val)
{}

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

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

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

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

static const struct iio_info bu27034_info =;

static int bu27034_chip_init(struct bu27034_data *data)
{}

static int bu27034_wait_for_data(struct bu27034_data *data)
{}

static int bu27034_buffer_thread(void *arg)
{}

static int bu27034_buffer_enable(struct iio_dev *idev)
{}

static int bu27034_buffer_disable(struct iio_dev *idev)
{}

static const struct iio_buffer_setup_ops bu27034_buffer_ops =;

static int bu27034_probe(struct i2c_client *i2c)
{}

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

static struct i2c_driver bu27034_i2c_driver =;
module_i2c_driver();

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