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

// SPDX-License-Identifier: GPL-2.0-only
/*
 * ROHM Colour Sensor driver for
 * - BU27008 RGBC sensor
 * - BU27010 RGBC + Flickering sensor
 *
 * Copyright (c) 2023, ROHM Semiconductor.
 */

#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/interrupt.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/iio.h>
#include <linux/iio/iio-gts-helper.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>

/*
 * A word about register address and mask definitions.
 *
 * At a quick glance to the data-sheet register tables, the BU27010 has all the
 * registers that the BU27008 has. On top of that the BU27010 adds couple of new
 * ones.
 *
 * So, all definitions BU27008_REG_* are there also for BU27010 but none of the
 * BU27010_REG_* are present on BU27008. This makes sense as BU27010 just adds
 * some features (Flicker FIFO, more power control) on top of the BU27008.
 *
 * Unfortunately, some of the wheel has been re-invented. Even though the names
 * of the registers have stayed the same, pretty much all of the functionality
 * provided by the registers has changed place. Contents of all MODE_CONTROL
 * registers on BU27008 and BU27010 are different.
 *
 * Chip-specific mapping from register addresses/bits to functionality is done
 * in bu27_chip_data structures.
 */
#define BU27008_REG_SYSTEM_CONTROL
#define BU27008_MASK_SW_RESET
#define BU27008_MASK_PART_ID
#define BU27008_ID
#define BU27008_REG_MODE_CONTROL1
#define BU27008_MASK_MEAS_MODE
#define BU27008_MASK_CHAN_SEL

#define BU27008_REG_MODE_CONTROL2
#define BU27008_MASK_RGBC_GAIN
#define BU27008_MASK_IR_GAIN_LO
#define BU27008_SHIFT_IR_GAIN

#define BU27008_REG_MODE_CONTROL3
#define BU27008_MASK_VALID
#define BU27008_MASK_INT_EN
#define BU27008_INT_EN
#define BU27008_INT_DIS
#define BU27008_MASK_MEAS_EN
#define BU27008_MEAS_EN
#define BU27008_MEAS_DIS

#define BU27008_REG_DATA0_LO
#define BU27008_REG_DATA1_LO
#define BU27008_REG_DATA2_LO
#define BU27008_REG_DATA3_LO
#define BU27008_REG_DATA3_HI
#define BU27008_REG_MANUFACTURER_ID
#define BU27008_REG_MAX

/* BU27010 specific definitions */

#define BU27010_MASK_SW_RESET
#define BU27010_ID
#define BU27010_REG_POWER
#define BU27010_MASK_POWER

#define BU27010_REG_RESET
#define BU27010_MASK_RESET
#define BU27010_RESET_RELEASE

#define BU27010_MASK_MEAS_EN

#define BU27010_MASK_CHAN_SEL
#define BU27010_MASK_MEAS_MODE
#define BU27010_MASK_RGBC_GAIN

#define BU27010_MASK_DATA3_GAIN
#define BU27010_MASK_DATA2_GAIN
#define BU27010_MASK_DATA1_GAIN
#define BU27010_MASK_DATA0_GAIN

#define BU27010_MASK_FLC_MODE
#define BU27010_MASK_FLC_GAIN

#define BU27010_REG_MODE_CONTROL4
/* If flicker is ever to be supported the IRQ must be handled as a field */
#define BU27010_IRQ_DIS_ALL
#define BU27010_DRDY_EN
#define BU27010_MASK_INT_SEL

#define BU27010_REG_MODE_CONTROL5
#define BU27010_MASK_RGB_VALID
#define BU27010_MASK_FLC_VALID
#define BU27010_MASK_WAIT_EN
#define BU27010_MASK_FIFO_EN
#define BU27010_MASK_RGB_EN
#define BU27010_MASK_FLC_EN

#define BU27010_REG_DATA_FLICKER_LO
#define BU27010_MASK_DATA_FLICKER_HI
#define BU27010_REG_FLICKER_COUNT
#define BU27010_REG_FIFO_LEVEL_LO
#define BU27010_MASK_FIFO_LEVEL_HI
#define BU27010_REG_FIFO_DATA_LO
#define BU27010_REG_FIFO_DATA_HI
#define BU27010_MASK_FIFO_DATA_HI
#define BU27010_REG_MANUFACTURER_ID
#define BU27010_REG_MAX

/**
 * enum bu27008_chan_type - BU27008 channel types
 * @BU27008_RED:	Red channel. Always via data0.
 * @BU27008_GREEN:	Green channel. Always via data1.
 * @BU27008_BLUE:	Blue channel. Via data2 (when used).
 * @BU27008_CLEAR:	Clear channel. Via data2 or data3 (when used).
 * @BU27008_IR:		IR channel. Via data3 (when used).
 * @BU27008_LUX:	Illuminance channel, computed using RGB and IR.
 * @BU27008_NUM_CHANS:	Number of channel types.
 */
enum bu27008_chan_type {};

/**
 * enum bu27008_chan - BU27008 physical data channel
 * @BU27008_DATA0:		Always red.
 * @BU27008_DATA1:		Always green.
 * @BU27008_DATA2:		Blue or clear.
 * @BU27008_DATA3:		IR or clear.
 * @BU27008_NUM_HW_CHANS:	Number of physical channels
 */
enum bu27008_chan {};

/* We can always measure red and green at same time */
#define ALWAYS_SCANNABLE

/* We use these data channel configs. Ensure scan_masks below follow them too */
#define BU27008_BLUE2_CLEAR3
#define BU27008_CLEAR2_IR3
#define BU27008_BLUE2_IR3

static const unsigned long bu27008_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
 *
 * Max amplification is (HWGAIN * MAX integration-time multiplier) 1024 * 8
 * = 8192. With NANO scale we get rid of accuracy loss when we start with the
 * scale 16.0 for HWGAIN1, INT-TIME 55 mS. This way the nano scale for MAX
 * total gain 8192 will be 1953125
 */
#define BU27008_SCALE_1X

/*
 * On BU27010 available scales with gain 1x - 4096x,
 * timings 55, 100, 200, 400 mS. Time impacts to gain: 1x, 2x, 4x, 8x.
 *
 * => Max total gain is HWGAIN * gain by integration time (8 * 4096)
 *
 * Using NANO precision for scale we must use scale 64x corresponding gain 1x
 * to avoid precision loss.
 */
#define BU27010_SCALE_1X

/* See the data sheet for the "Gain Setting" table */
#define BU27008_GSEL_1X
#define BU27008_GSEL_4X
#define BU27008_GSEL_8X
#define BU27008_GSEL_16X
#define BU27008_GSEL_32X
#define BU27008_GSEL_64X
#define BU27008_GSEL_256X
#define BU27008_GSEL_512X
#define BU27008_GSEL_1024X

static const struct iio_gain_sel_pair bu27008_gains[] =;

static const struct iio_gain_sel_pair bu27008_gains_ir[] =;

#define BU27010_GSEL_1X
#define BU27010_GSEL_4X
#define BU27010_GSEL_16X
#define BU27010_GSEL_64X
#define BU27010_GSEL_256X
#define BU27010_GSEL_1024X
#define BU27010_GSEL_4096X

static const struct iio_gain_sel_pair bu27010_gains[] =;

static const struct iio_gain_sel_pair bu27010_gains_ir[] =;

#define BU27008_MEAS_MODE_100MS
#define BU27008_MEAS_MODE_55MS
#define BU27008_MEAS_MODE_200MS
#define BU27008_MEAS_MODE_400MS

#define BU27010_MEAS_MODE_100MS
#define BU27010_MEAS_MODE_55MS
#define BU27010_MEAS_MODE_200MS
#define BU27010_MEAS_MODE_400MS

#define BU27008_MEAS_TIME_MAX_MS

static const struct iio_itime_sel_mul bu27008_itimes[] =;

static const struct iio_itime_sel_mul bu27010_itimes[] =;

/*
 * All the RGBC channels share the same gain.
 * IR gain can be fine-tuned from the gain set for the RGBC by 2 bit, but this
 * would yield quite complex gain setting. Especially since not all bit
 * compinations are supported. And in any case setting GAIN for RGBC will
 * always also change the IR-gain.
 *
 * On top of this, the selector '0' which corresponds to hw-gain 1X on RGBC,
 * corresponds to gain 2X on IR. Rest of the selctors correspond to same gains
 * though. This, however, makes it not possible to use shared gain for all
 * RGBC and IR settings even though they are all changed at the one go.
 */
#define BU27008_CHAN(color, data, separate_avail)

/* For raw reads we always configure DATA3 for CLEAR */
static const struct iio_chan_spec bu27008_channels[] =;

struct bu27008_data;

struct bu27_chip_data {};

struct bu27008_data {};

static const struct regmap_range bu27008_volatile_ranges[] =;

static const struct regmap_range bu27010_volatile_ranges[] =;

static const struct regmap_access_table bu27008_volatile_regs =;

static const struct regmap_access_table bu27010_volatile_regs =;

static const struct regmap_range bu27008_read_only_ranges[] =;

static const struct regmap_range bu27010_read_only_ranges[] =;

static const struct regmap_access_table bu27008_ro_regs =;

static const struct regmap_access_table bu27010_ro_regs =;

static const struct regmap_config bu27008_regmap =;

static const struct regmap_config bu27010_regmap =;

static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
{}

static int bu27010_write_gain_sel(struct bu27008_data *data, int sel)
{}

static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel)
{}

static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel)
{}

static int bu27008_chip_init(struct bu27008_data *data)
{}

static int bu27010_chip_init(struct bu27008_data *data)
{}

static const struct bu27_chip_data bu27010_chip =;

static const struct bu27_chip_data bu27008_chip =;

#define BU27008_MAX_VALID_RESULT_WAIT_US
#define BU27008_VALID_RESULT_WAIT_QUANTA_US

static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val)
{}

static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain)
{}

static int bu27008_set_gain(struct bu27008_data *data, int gain)
{}

static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel)
{}

static int bu27008_set_int_time_sel(struct bu27008_data *data, int sel)
{}

static int bu27008_get_int_time_us(struct bu27008_data *data)
{}

static int _bu27008_get_scale(struct bu27008_data *data, bool ir, int *val,
			      int *val2)
{}

static int bu27008_get_scale(struct bu27008_data *data, bool ir, int *val,
			     int *val2)
{}

static int bu27008_set_int_time(struct bu27008_data *data, int time)
{}

/* Try to change the time so that the scale is maintained */
static int bu27008_try_set_int_time(struct bu27008_data *data, int int_time_new)
{}

static int bu27008_meas_set(struct bu27008_data *data, bool enable)
{}

static int bu27008_chan_cfg(struct bu27008_data *data,
			    struct iio_chan_spec const *chan)
{}

static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev,
			    struct iio_chan_spec const *chan, int *val, int *val2)
{}

#define BU27008_LUX_DATA_RED
#define BU27008_LUX_DATA_GREEN
#define BU27008_LUX_DATA_BLUE
#define BU27008_LUX_DATA_IR
#define LUX_DATA_SIZE

static int bu27008_read_lux_chans(struct bu27008_data *data, unsigned int time,
				  __le16 *chan_data)
{}

/*
 * Following equation for computing lux out of register values was given by
 * ROHM HW colleagues;
 *
 * Red = RedData*1024 / Gain * 20 / meas_mode
 * Green = GreenData* 1024 / Gain * 20 / meas_mode
 * Blue = BlueData* 1024 / Gain * 20 / meas_mode
 * IR = IrData* 1024 / Gain * 20 / meas_mode
 *
 * where meas_mode is the integration time in mS / 10
 *
 * IRratio = (IR > 0.18 * Green) ? 0 : 1
 *
 * Lx = max(c1*Red + c2*Green + c3*Blue,0)
 *
 * for
 * IRratio 0: c1 = -0.00002237, c2 = 0.0003219, c3 = -0.000120371
 * IRratio 1: c1 = -0.00001074, c2 = 0.000305415, c3 = -0.000129367
 */

/*
 * The max chan data is 0xffff. When we multiply it by 1024 * 20, we'll get
 * 0x4FFFB000 which still fits in 32-bit integer. This won't overflow.
 */
#define NORM_CHAN_DATA_FOR_LX_CALC(chan, gain, time)
static u64 bu27008_calc_nlux(struct bu27008_data *data, __le16 *lux_data,
		unsigned int gain, unsigned int gain_ir, unsigned int time)
{}

static int bu27008_get_time_n_gains(struct bu27008_data *data,
		unsigned int *gain, unsigned int *gain_ir, unsigned int *time)
{}

struct bu27008_buf {};

static int bu27008_buffer_fill_lux(struct bu27008_data *data,
				   struct bu27008_buf *raw)
{}

static int bu27008_read_lux(struct bu27008_data *data, struct iio_dev *idev,
			    struct iio_chan_spec const *chan,
			    int *val, int *val2)
{}

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

/* Called if the new scale could not be supported with existing int-time */
static int bu27008_try_find_new_time_gain(struct bu27008_data *data, int val,
					  int val2, int *gain_sel)
{}

static int bu27008_set_scale(struct bu27008_data *data,
			     struct iio_chan_spec const *chan,
			     int val, int val2)
{}

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

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

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

static int bu27008_update_scan_mode(struct iio_dev *idev,
				    const unsigned long *scan_mask)
{}

static const struct iio_info bu27008_info =;

static int bu27008_trigger_set_state(struct iio_trigger *trig, bool state)
{}

static void bu27008_trigger_reenable(struct iio_trigger *trig)
{}

static const struct iio_trigger_ops bu27008_trigger_ops =;

static irqreturn_t bu27008_trigger_handler(int irq, void *p)
{}

static int bu27008_buffer_preenable(struct iio_dev *idev)
{}

static int bu27008_buffer_postdisable(struct iio_dev *idev)
{}

static const struct iio_buffer_setup_ops bu27008_buffer_ops =;

static irqreturn_t bu27008_data_rdy_poll(int irq, void *private)
{}

static int bu27008_setup_trigger(struct bu27008_data *data, struct iio_dev *idev)
{}

static int bu27008_probe(struct i2c_client *i2c)
{}

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

static struct i2c_driver bu27008_i2c_driver =;
module_i2c_driver();

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