// SPDX-License-Identifier: GPL-2.0-only /* * BU27034 ROHM Ambient Light Sensor * * Copyright (c) 2023, ROHM Semiconductor. * https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/light/bu27034nuc-e.pdf */ #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_MASK_D2_GAIN_HI … #define BU27034_MASK_D2_GAIN_LO … #define BU27034_REG_MODE_CONTROL3 … #define BU27034_REG_MODE_CONTROL4 … #define BU27034_MASK_MEAS_EN … #define BU27034_MASK_VALID … #define BU27034_REG_DATA0_LO … #define BU27034_REG_DATA1_LO … #define BU27034_REG_DATA2_LO … #define BU27034_REG_DATA2_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 - 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) = 32768 * * Using NANO precision for scale we must use scale 64x corresponding gain 1x * to avoid precision loss. (32x 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_16X … #define BU27034_GSEL_32X … #define BU27034_GSEL_64X … #define BU27034_GSEL_256X … #define BU27034_GSEL_512X … #define BU27034_GSEL_1024X … #define BU27034_GSEL_2048X … #define BU27034_GSEL_4096X … /* Available gain settings */ static const struct iio_gain_sel_pair bu27034_gains[] = …; /* * The IC has 5 modes for sampling time. 5 mS mode is exceptional as it limits * the data collection to data0-channel only and cuts the supported range to * 10 bit. It is not supported by the driver. * * "normal" modes are 55, 100, 200 and 400 mS modes - which do have direct * multiplying impact to the 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, _ch2) … 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 < 0.87): * lx = 0.004521097 * D1 - 0.002663996 * D0 + * 0.00012213 * D1 * D1 / D0 * * => 115.7400832 * ch1 / gain1 / mt - * 68.1982976 * ch0 / gain0 / mt + * 0.00012213 * 25600 * (ch1 / gain1 / mt) * 25600 * * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt) * * A = 0.00012213 * 25600 * (ch1 /gain1 / mt) * 25600 * * (ch1 /gain1 / mt) / (25600 * ch0 / gain0 / mt) * => 0.00012213 * 25600 * (ch1 /gain1 / mt) * * (ch1 /gain1 / mt) / (ch0 / gain0 / mt) * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) / * (ch0 / gain0) * => 0.00012213 * 25600 * (ch1 / gain1) * (ch1 /gain1 / mt) * * gain0 / ch0 * => 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt /ch0 * * lx = (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) / * mt + A * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0) / * mt + 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / mt / * ch0 * * => (115.7400832 * ch1 / gain1 - 68.1982976 * ch0 / gain0 + * 3.126528 * ch1 * ch1 * gain0 / gain1 / gain1 / ch0) / * mt * * For (0.87 <= D1/D0 < 1.00) * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 0.87) * (0.385) + 1) * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 * * 100 * ch1 / gain1 / mt) * ((D1/D0 - 0.87) * (0.385) + 1) * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * * ((D1/D0 - 0.87) * (0.385) + 1) * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * * (0.385 * D1/D0 - 0.66505) * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * * (0.385 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) - 0.66505) * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * * (9856 * ch1 / gain1 / mt / (25600 * ch0 / gain0 / mt) + 0.66505) * => 13.118336 * ch1 / (gain1 * mt) * + 22.66064768 * ch0 / (gain0 * mt) * + 8931.90144 * ch1 * ch1 * gain0 / * (25600 * ch0 * gain1 * gain1 * mt) * + 0.602694912 * ch1 / (gain1 * mt) * * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) * + 22.66064768 * ch0 / gain0 * + 13.721030912 * ch1 / gain1 * ] / mt * * For (D1/D0 >= 1.00) * * lx = (0.001331* D0 + 0.0000354 * D1) * ((D1/D0 – 2.0) * (-0.05) + 1) * => (0.001331* D0 + 0.0000354 * D1) * (-0.05D1/D0 + 1.1) * => (0.001331 * 256 * 100 * ch0 / gain0 / mt + 0.0000354 * 256 * * 100 * ch1 / gain1 / mt) * (-0.05D1/D0 + 1.1) * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * * (-0.05 * 256 * 100 * ch1 / gain1 / mt / (256 * 100 * ch0 / gain0 / mt) + 1.1) * => (34.0736 * ch0 / gain0 / mt + 0.90624 * ch1 / gain1 / mt) * * (-1280 * ch1 / (gain1 * mt * 25600 * ch0 / gain0 / mt) + 1.1) * => (34.0736 * ch0 * -1280 * ch1 * gain0 * mt /( gain0 * mt * gain1 * mt * 25600 * ch0) * + 34.0736 * 1.1 * ch0 / (gain0 * mt) * + 0.90624 * ch1 * -1280 * ch1 *gain0 * mt / (gain1 * mt *gain1 * mt * 25600 * ch0) * + 1.1 * 0.90624 * ch1 / (gain1 * mt) * => -43614.208 * ch1 / (gain1 * mt * 25600) * + 37.48096 ch0 / (gain0 * mt) * - 1159.9872 * ch1 * ch1 * gain0 / (gain1 * gain1 * mt * 25600 * ch0) * + 0.996864 ch1 / (gain1 * mt) * => [ * - 0.045312 * ch1 * ch1 * gain0 / (gain1 * gain1 * ch0) * - 0.706816 * ch1 / gain1 * + 37.48096 ch0 /gain0 * ] * mt * * * So, the first case (D1/D0 < 0.87) can be computed to a form: * * lx = (3.126528 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + * 115.7400832 * ch1 / gain1 + * -68.1982976 * ch0 / gain0 * / mt * * Second case (0.87 <= D1/D0 < 1.00) goes to form: * * => [0.3489024 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + * 13.721030912 * ch1 / gain1 + * 22.66064768 * ch0 / gain0 * ] / mt * * Third case (D1/D0 >= 1.00) goes to form: * => [-0.045312 * ch1 * ch1 * gain0 / (ch0 * gain1 * gain1) + * -0.706816 * ch1 / gain1 + * 37.48096 ch0 /(gain0 * ] / 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 = 3.126528, * B = 115.7400832 * C = -68.1982976 * * For case 2: * A = 0.3489024 * B = 13.721030912 * C = 22.66064768 * * For case 3: * A = -0.045312 * B = -0.706816 * C = 37.48096 */ 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 < 0.87) * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 3.45 + 1) * else if (D1/D0 < 1) * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 0.87) * 0.385 + 1) * else * lx = (0.001331 * D0 + 0.0000354 * D1) * ((D1 / D0 - 2) * -0.05 + 1) * * 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(…);