diff options
Diffstat (limited to 'drivers/iio/light/rohm-bu27008.c')
-rw-r--r-- | drivers/iio/light/rohm-bu27008.c | 652 |
1 files changed, 535 insertions, 117 deletions
diff --git a/drivers/iio/light/rohm-bu27008.c b/drivers/iio/light/rohm-bu27008.c index 489902bed7f0..6a6d77805091 100644 --- a/drivers/iio/light/rohm-bu27008.c +++ b/drivers/iio/light/rohm-bu27008.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * BU27008 ROHM Colour Sensor + * ROHM Colour Sensor driver for + * - BU27008 RGBC sensor + * - BU27010 RGBC + Flickering sensor * * Copyright (c) 2023, ROHM Semiconductor. */ @@ -22,6 +24,25 @@ #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 0x40 #define BU27008_MASK_SW_RESET BIT(7) #define BU27008_MASK_PART_ID GENMASK(5, 0) @@ -52,6 +73,56 @@ #define BU27008_REG_MANUFACTURER_ID 0x92 #define BU27008_REG_MAX BU27008_REG_MANUFACTURER_ID +/* BU27010 specific definitions */ + +#define BU27010_MASK_SW_RESET BIT(7) +#define BU27010_ID 0x1b +#define BU27010_REG_POWER 0x3e +#define BU27010_MASK_POWER BIT(0) + +#define BU27010_REG_RESET 0x3f +#define BU27010_MASK_RESET BIT(0) +#define BU27010_RESET_RELEASE BU27010_MASK_RESET + +#define BU27010_MASK_MEAS_EN BIT(1) + +#define BU27010_MASK_CHAN_SEL GENMASK(7, 6) +#define BU27010_MASK_MEAS_MODE GENMASK(5, 4) +#define BU27010_MASK_RGBC_GAIN GENMASK(3, 0) + +#define BU27010_MASK_DATA3_GAIN GENMASK(7, 6) +#define BU27010_MASK_DATA2_GAIN GENMASK(5, 4) +#define BU27010_MASK_DATA1_GAIN GENMASK(3, 2) +#define BU27010_MASK_DATA0_GAIN GENMASK(1, 0) + +#define BU27010_MASK_FLC_MODE BIT(7) +#define BU27010_MASK_FLC_GAIN GENMASK(4, 0) + +#define BU27010_REG_MODE_CONTROL4 0x44 +/* If flicker is ever to be supported the IRQ must be handled as a field */ +#define BU27010_IRQ_DIS_ALL GENMASK(1, 0) +#define BU27010_DRDY_EN BIT(0) +#define BU27010_MASK_INT_SEL GENMASK(1, 0) + +#define BU27010_REG_MODE_CONTROL5 0x45 +#define BU27010_MASK_RGB_VALID BIT(7) +#define BU27010_MASK_FLC_VALID BIT(6) +#define BU27010_MASK_WAIT_EN BIT(3) +#define BU27010_MASK_FIFO_EN BIT(2) +#define BU27010_MASK_RGB_EN BIT(1) +#define BU27010_MASK_FLC_EN BIT(0) + +#define BU27010_REG_DATA_FLICKER_LO 0x56 +#define BU27010_MASK_DATA_FLICKER_HI GENMASK(2, 0) +#define BU27010_REG_FLICKER_COUNT 0x5a +#define BU27010_REG_FIFO_LEVEL_LO 0x5b +#define BU27010_MASK_FIFO_LEVEL_HI BIT(0) +#define BU27010_REG_FIFO_DATA_LO 0x5d +#define BU27010_REG_FIFO_DATA_HI 0x5e +#define BU27010_MASK_FIFO_DATA_HI GENMASK(2, 0) +#define BU27010_REG_MANUFACTURER_ID 0x92 +#define BU27010_REG_MAX BU27010_REG_MANUFACTURER_ID + /** * enum bu27008_chan_type - BU27008 channel types * @BU27008_RED: Red channel. Always via data0. @@ -117,6 +188,17 @@ static const unsigned long bu27008_scan_masks[] = { */ #define BU27008_SCALE_1X 16 +/* + * 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 64 + /* See the data sheet for the "Gain Setting" table */ #define BU27008_GSEL_1X 0x00 #define BU27008_GSEL_4X 0x08 @@ -152,10 +234,44 @@ static const struct iio_gain_sel_pair bu27008_gains_ir[] = { GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X), }; +#define BU27010_GSEL_1X 0x00 /* 000000 */ +#define BU27010_GSEL_4X 0x08 /* 001000 */ +#define BU27010_GSEL_16X 0x09 /* 001001 */ +#define BU27010_GSEL_64X 0x0e /* 001110 */ +#define BU27010_GSEL_256X 0x1e /* 011110 */ +#define BU27010_GSEL_1024X 0x2e /* 101110 */ +#define BU27010_GSEL_4096X 0x3f /* 111111 */ + +static const struct iio_gain_sel_pair bu27010_gains[] = { + GAIN_SCALE_GAIN(1, BU27010_GSEL_1X), + GAIN_SCALE_GAIN(4, BU27010_GSEL_4X), + GAIN_SCALE_GAIN(16, BU27010_GSEL_16X), + GAIN_SCALE_GAIN(64, BU27010_GSEL_64X), + GAIN_SCALE_GAIN(256, BU27010_GSEL_256X), + GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X), + GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X), +}; + +static const struct iio_gain_sel_pair bu27010_gains_ir[] = { + GAIN_SCALE_GAIN(2, BU27010_GSEL_1X), + GAIN_SCALE_GAIN(4, BU27010_GSEL_4X), + GAIN_SCALE_GAIN(16, BU27010_GSEL_16X), + GAIN_SCALE_GAIN(64, BU27010_GSEL_64X), + GAIN_SCALE_GAIN(256, BU27010_GSEL_256X), + GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X), + GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X), +}; + #define BU27008_MEAS_MODE_100MS 0x00 #define BU27008_MEAS_MODE_55MS 0x01 #define BU27008_MEAS_MODE_200MS 0x02 #define BU27008_MEAS_MODE_400MS 0x04 + +#define BU27010_MEAS_MODE_100MS 0x00 +#define BU27010_MEAS_MODE_55MS 0x03 +#define BU27010_MEAS_MODE_200MS 0x01 +#define BU27010_MEAS_MODE_400MS 0x02 + #define BU27008_MEAS_TIME_MAX_MS 400 static const struct iio_itime_sel_mul bu27008_itimes[] = { @@ -165,6 +281,13 @@ static const struct iio_itime_sel_mul bu27008_itimes[] = { GAIN_SCALE_ITIME_US(55000, BU27008_MEAS_MODE_55MS, 1), }; +static const struct iio_itime_sel_mul bu27010_itimes[] = { + GAIN_SCALE_ITIME_US(400000, BU27010_MEAS_MODE_400MS, 8), + GAIN_SCALE_ITIME_US(200000, BU27010_MEAS_MODE_200MS, 4), + GAIN_SCALE_ITIME_US(100000, BU27010_MEAS_MODE_100MS, 2), + GAIN_SCALE_ITIME_US(55000, BU27010_MEAS_MODE_55MS, 1), +}; + /* * 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 @@ -190,7 +313,7 @@ static const struct iio_itime_sel_mul bu27008_itimes[] = { .address = BU27008_REG_##data##_LO, \ .scan_index = BU27008_##color, \ .scan_type = { \ - .sign = 's', \ + .sign = 'u', \ .realbits = 16, \ .storagebits = 16, \ .endianness = IIO_LE, \ @@ -211,7 +334,35 @@ static const struct iio_chan_spec bu27008_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(BU27008_NUM_CHANS), }; +struct bu27008_data; + +struct bu27_chip_data { + const char *name; + int (*chip_init)(struct bu27008_data *data); + int (*get_gain_sel)(struct bu27008_data *data, int *sel); + int (*write_gain_sel)(struct bu27008_data *data, int sel); + const struct regmap_config *regmap_cfg; + const struct iio_gain_sel_pair *gains; + const struct iio_gain_sel_pair *gains_ir; + const struct iio_itime_sel_mul *itimes; + int num_gains; + int num_gains_ir; + int num_itimes; + int scale1x; + + int drdy_en_reg; + int drdy_en_mask; + int meas_en_reg; + int meas_en_mask; + int valid_reg; + int chan_sel_reg; + int chan_sel_mask; + int int_time_mask; + u8 part_id; +}; + struct bu27008_data { + const struct bu27_chip_data *cd; struct regmap *regmap; struct iio_trigger *trig; struct device *dev; @@ -240,11 +391,29 @@ static const struct regmap_range bu27008_volatile_ranges[] = { }, }; +static const struct regmap_range bu27010_volatile_ranges[] = { + { + .range_min = BU27010_REG_RESET, /* RSTB */ + .range_max = BU27008_REG_SYSTEM_CONTROL, /* RESET */ + }, { + .range_min = BU27010_REG_MODE_CONTROL5, /* VALID bits */ + .range_max = BU27010_REG_MODE_CONTROL5, + }, { + .range_min = BU27008_REG_DATA0_LO, + .range_max = BU27010_REG_FIFO_DATA_HI, + }, +}; + static const struct regmap_access_table bu27008_volatile_regs = { .yes_ranges = &bu27008_volatile_ranges[0], .n_yes_ranges = ARRAY_SIZE(bu27008_volatile_ranges), }; +static const struct regmap_access_table bu27010_volatile_regs = { + .yes_ranges = &bu27010_volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bu27010_volatile_ranges), +}; + static const struct regmap_range bu27008_read_only_ranges[] = { { .range_min = BU27008_REG_DATA0_LO, @@ -255,11 +424,26 @@ static const struct regmap_range bu27008_read_only_ranges[] = { }, }; +static const struct regmap_range bu27010_read_only_ranges[] = { + { + .range_min = BU27008_REG_DATA0_LO, + .range_max = BU27010_REG_FIFO_DATA_HI, + }, { + .range_min = BU27010_REG_MANUFACTURER_ID, + .range_max = BU27010_REG_MANUFACTURER_ID, + } +}; + static const struct regmap_access_table bu27008_ro_regs = { .no_ranges = &bu27008_read_only_ranges[0], .n_no_ranges = ARRAY_SIZE(bu27008_read_only_ranges), }; +static const struct regmap_access_table bu27010_ro_regs = { + .no_ranges = &bu27010_read_only_ranges[0], + .n_no_ranges = ARRAY_SIZE(bu27010_read_only_ranges), +}; + static const struct regmap_config bu27008_regmap = { .reg_bits = 8, .val_bits = 8, @@ -282,50 +466,16 @@ static const struct regmap_config bu27008_regmap = { .disable_locking = true, }; -#define BU27008_MAX_VALID_RESULT_WAIT_US 50000 -#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000 - -static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val) -{ - int ret, valid; - __le16 tmp; - - ret = regmap_read_poll_timeout(data->regmap, BU27008_REG_MODE_CONTROL3, - valid, (valid & BU27008_MASK_VALID), - BU27008_VALID_RESULT_WAIT_QUANTA_US, - BU27008_MAX_VALID_RESULT_WAIT_US); - if (ret) - return ret; - - ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp)); - if (ret) - dev_err(data->dev, "Reading channel data failed\n"); - - *val = le16_to_cpu(tmp); - - return ret; -} - -static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain) -{ - int ret, sel; - - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, &sel); - if (ret) - return ret; - - sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, sel); - - ret = iio_gts_find_gain_by_sel(gts, sel); - if (ret < 0) { - dev_err(data->dev, "unknown gain value 0x%x\n", sel); - return ret; - } +static const struct regmap_config bu27010_regmap = { + .reg_bits = 8, + .val_bits = 8, - *gain = ret; - - return 0; -} + .max_register = BU27010_REG_MAX, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &bu27010_volatile_regs, + .wr_table = &bu27010_ro_regs, + .disable_locking = true, +}; static int bu27008_write_gain_sel(struct bu27008_data *data, int sel) { @@ -368,6 +518,264 @@ static int bu27008_write_gain_sel(struct bu27008_data *data, int sel) BU27008_MASK_RGBC_GAIN, regval); } +static int bu27010_write_gain_sel(struct bu27008_data *data, int sel) +{ + unsigned int regval; + int ret, chan_selector; + + /* + * Gain 'selector' is composed of two registers. Selector is 6bit value, + * 4 high bits being the RGBC gain fieild in MODE_CONTROL1 register and + * two low bits being the channel specific gain in MODE_CONTROL2. + * + * Let's take the 4 high bits of whole 6 bit selector, and prepare + * the MODE_CONTROL1 value (RGBC gain part). + */ + regval = FIELD_PREP(BU27010_MASK_RGBC_GAIN, (sel >> 2)); + + ret = regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, + BU27010_MASK_RGBC_GAIN, regval); + if (ret) + return ret; + + /* + * Two low two bits of the selector must be written for all 4 + * channels in the MODE_CONTROL2 register. Copy these two bits for + * all channels. + */ + chan_selector = sel & GENMASK(1, 0); + + regval = FIELD_PREP(BU27010_MASK_DATA0_GAIN, chan_selector); + regval |= FIELD_PREP(BU27010_MASK_DATA1_GAIN, chan_selector); + regval |= FIELD_PREP(BU27010_MASK_DATA2_GAIN, chan_selector); + regval |= FIELD_PREP(BU27010_MASK_DATA3_GAIN, chan_selector); + + return regmap_write(data->regmap, BU27008_REG_MODE_CONTROL2, regval); +} + +static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel) +{ + int ret; + + /* + * If we always "lock" the gain selectors for all channels to prevent + * unsupported configs, then it does not matter which channel is used + * we can just return selector from any of them. + * + * This, however is not true if we decide to support only 4X and 16X + * and then individual gains for channels. Currently this is not the + * case. + * + * If we some day decide to support individual gains, then we need to + * have channel information here. + */ + + ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel); + if (ret) + return ret; + + *sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, *sel); + + return 0; +} + +static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel) +{ + int ret, tmp; + + /* + * We always "lock" the gain selectors for all channels to prevent + * unsupported configs. It does not matter which channel is used + * we can just return selector from any of them. + * + * Read the channel0 gain. + */ + ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel); + if (ret) + return ret; + + *sel = FIELD_GET(BU27010_MASK_DATA0_GAIN, *sel); + + /* Read the shared gain */ + ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &tmp); + if (ret) + return ret; + + /* + * The gain selector is made as a combination of common RGBC gain and + * the channel specific gain. The channel specific gain forms the low + * bits of selector and RGBC gain is appended right after it. + * + * Compose the selector from channel0 gain and shared RGBC gain. + */ + *sel |= FIELD_GET(BU27010_MASK_RGBC_GAIN, tmp) << fls(BU27010_MASK_DATA0_GAIN); + + return ret; +} + +static int bu27008_chip_init(struct bu27008_data *data) +{ + int ret; + + ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, + BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); + + /* + * The data-sheet does not tell how long performing the IC reset takes. + * However, the data-sheet says the minimum time it takes the IC to be + * able to take inputs after power is applied, is 100 uS. I'd assume + * > 1 mS is enough. + */ + msleep(1); + + ret = regmap_reinit_cache(data->regmap, data->cd->regmap_cfg); + if (ret) + dev_err(data->dev, "Failed to reinit reg cache\n"); + + return ret; +} + +static int bu27010_chip_init(struct bu27008_data *data) +{ + int ret; + + ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, + BU27010_MASK_SW_RESET, BU27010_MASK_SW_RESET); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); + + msleep(1); + + /* Power ON*/ + ret = regmap_write_bits(data->regmap, BU27010_REG_POWER, + BU27010_MASK_POWER, BU27010_MASK_POWER); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor power-on failed\n"); + + msleep(1); + + /* Release blocks from reset */ + ret = regmap_write_bits(data->regmap, BU27010_REG_RESET, + BU27010_MASK_RESET, BU27010_RESET_RELEASE); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor powering failed\n"); + + msleep(1); + + /* + * The IRQ enabling on BU27010 is done in a peculiar way. The IRQ + * enabling is not a bit mask where individual IRQs could be enabled but + * a field which values are: + * 00 => IRQs disabled + * 01 => Data-ready (RGBC/IR) + * 10 => Data-ready (flicker) + * 11 => Flicker FIFO + * + * So, only one IRQ can be enabled at a time and enabling for example + * flicker FIFO would automagically disable data-ready IRQ. + * + * Currently the driver does not support the flicker. Hence, we can + * just treat the RGBC data-ready as single bit which can be enabled / + * disabled. This works for as long as the second bit in the field + * stays zero. Here we ensure it gets zeroed. + */ + return regmap_clear_bits(data->regmap, BU27010_REG_MODE_CONTROL4, + BU27010_IRQ_DIS_ALL); +} + +static const struct bu27_chip_data bu27010_chip = { + .name = "bu27010", + .chip_init = bu27010_chip_init, + .get_gain_sel = bu27010_get_gain_sel, + .write_gain_sel = bu27010_write_gain_sel, + .regmap_cfg = &bu27010_regmap, + .gains = &bu27010_gains[0], + .gains_ir = &bu27010_gains_ir[0], + .itimes = &bu27010_itimes[0], + .num_gains = ARRAY_SIZE(bu27010_gains), + .num_gains_ir = ARRAY_SIZE(bu27010_gains_ir), + .num_itimes = ARRAY_SIZE(bu27010_itimes), + .scale1x = BU27010_SCALE_1X, + .drdy_en_reg = BU27010_REG_MODE_CONTROL4, + .drdy_en_mask = BU27010_DRDY_EN, + .meas_en_reg = BU27010_REG_MODE_CONTROL5, + .meas_en_mask = BU27010_MASK_MEAS_EN, + .valid_reg = BU27010_REG_MODE_CONTROL5, + .chan_sel_reg = BU27008_REG_MODE_CONTROL1, + .chan_sel_mask = BU27010_MASK_CHAN_SEL, + .int_time_mask = BU27010_MASK_MEAS_MODE, + .part_id = BU27010_ID, +}; + +static const struct bu27_chip_data bu27008_chip = { + .name = "bu27008", + .chip_init = bu27008_chip_init, + .get_gain_sel = bu27008_get_gain_sel, + .write_gain_sel = bu27008_write_gain_sel, + .regmap_cfg = &bu27008_regmap, + .gains = &bu27008_gains[0], + .gains_ir = &bu27008_gains_ir[0], + .itimes = &bu27008_itimes[0], + .num_gains = ARRAY_SIZE(bu27008_gains), + .num_gains_ir = ARRAY_SIZE(bu27008_gains_ir), + .num_itimes = ARRAY_SIZE(bu27008_itimes), + .scale1x = BU27008_SCALE_1X, + .drdy_en_reg = BU27008_REG_MODE_CONTROL3, + .drdy_en_mask = BU27008_MASK_INT_EN, + .valid_reg = BU27008_REG_MODE_CONTROL3, + .meas_en_reg = BU27008_REG_MODE_CONTROL3, + .meas_en_mask = BU27008_MASK_MEAS_EN, + .chan_sel_reg = BU27008_REG_MODE_CONTROL3, + .chan_sel_mask = BU27008_MASK_CHAN_SEL, + .int_time_mask = BU27008_MASK_MEAS_MODE, + .part_id = BU27008_ID, +}; + +#define BU27008_MAX_VALID_RESULT_WAIT_US 50000 +#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000 + +static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val) +{ + int ret, valid; + __le16 tmp; + + ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg, + valid, (valid & BU27008_MASK_VALID), + BU27008_VALID_RESULT_WAIT_QUANTA_US, + BU27008_MAX_VALID_RESULT_WAIT_US); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp)); + if (ret) + dev_err(data->dev, "Reading channel data failed\n"); + + *val = le16_to_cpu(tmp); + + return ret; +} + +static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain) +{ + int ret, sel; + + ret = data->cd->get_gain_sel(data, &sel); + if (ret) + return ret; + + ret = iio_gts_find_gain_by_sel(gts, sel); + if (ret < 0) { + dev_err(data->dev, "unknown gain value 0x%x\n", sel); + return ret; + } + + *gain = ret; + + return 0; +} + static int bu27008_set_gain(struct bu27008_data *data, int gain) { int ret; @@ -376,7 +784,7 @@ static int bu27008_set_gain(struct bu27008_data *data, int gain) if (ret < 0) return ret; - return bu27008_write_gain_sel(data, ret); + return data->cd->write_gain_sel(data, ret); } static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel) @@ -384,15 +792,23 @@ static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel) int ret, val; ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &val); - *sel = FIELD_GET(BU27008_MASK_MEAS_MODE, val); + if (ret) + return ret; - return ret; + val &= data->cd->int_time_mask; + val >>= ffs(data->cd->int_time_mask) - 1; + + *sel = val; + + return 0; } static int bu27008_set_int_time_sel(struct bu27008_data *data, int sel) { + sel <<= ffs(data->cd->int_time_mask) - 1; + return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, - BU27008_MASK_MEAS_MODE, sel); + data->cd->int_time_mask, sel); } static int bu27008_get_int_time_us(struct bu27008_data *data) @@ -448,8 +864,7 @@ static int bu27008_set_int_time(struct bu27008_data *data, int time) if (ret < 0) return ret; - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, - BU27008_MASK_MEAS_MODE, ret); + return bu27008_set_int_time_sel(data, ret); } /* Try to change the time so that the scale is maintained */ @@ -527,10 +942,13 @@ unlock_out: return ret; } -static int bu27008_meas_set(struct bu27008_data *data, int state) +static int bu27008_meas_set(struct bu27008_data *data, bool enable) { - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3, - BU27008_MASK_MEAS_EN, state); + if (enable) + return regmap_set_bits(data->regmap, data->cd->meas_en_reg, + data->cd->meas_en_mask); + return regmap_clear_bits(data->regmap, data->cd->meas_en_reg, + data->cd->meas_en_mask); } static int bu27008_chan_cfg(struct bu27008_data *data, @@ -543,9 +961,15 @@ static int bu27008_chan_cfg(struct bu27008_data *data, else chan_sel = BU27008_CLEAR2_IR3; - chan_sel = FIELD_PREP(BU27008_MASK_CHAN_SEL, chan_sel); + /* + * prepare bitfield for channel sel. The FIELD_PREP works only when + * mask is constant. In our case the mask is assigned based on the + * chip type. Hence the open-coded FIELD_PREP here. We don't bother + * zeroing the irrelevant bits though - update_bits takes care of that. + */ + chan_sel <<= ffs(data->cd->chan_sel_mask) - 1; - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3, + return regmap_update_bits(data->regmap, data->cd->chan_sel_reg, BU27008_MASK_CHAN_SEL, chan_sel); } @@ -558,7 +982,7 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev, if (ret) return ret; - ret = bu27008_meas_set(data, BU27008_MEAS_EN); + ret = bu27008_meas_set(data, true); if (ret) return ret; @@ -574,7 +998,7 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev, if (!ret) ret = IIO_VAL_INT; - if (bu27008_meas_set(data, BU27008_MEAS_DIS)) + if (bu27008_meas_set(data, false)) dev_warn(data->dev, "measurement disabling failed\n"); return ret; @@ -633,7 +1057,7 @@ static int bu27008_try_find_new_time_gain(struct bu27008_data *data, int val, for (i = 0; i < data->gts.num_itime; i++) { new_time_sel = data->gts.itime_table[i].sel; ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, - new_time_sel, val, val2 * 1000, gain_sel); + new_time_sel, val, val2, gain_sel); if (!ret) break; } @@ -662,14 +1086,14 @@ static int bu27008_set_scale(struct bu27008_data *data, goto unlock_out; ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, time_sel, - val, val2 * 1000, &gain_sel); + val, val2, &gain_sel); if (ret) { ret = bu27008_try_find_new_time_gain(data, val, val2, &gain_sel); if (ret) goto unlock_out; } - ret = bu27008_write_gain_sel(data, gain_sel); + ret = data->cd->write_gain_sel(data, gain_sel); unlock_out: mutex_unlock(&data->mutex); @@ -677,6 +1101,21 @@ unlock_out: return ret; } +static int bu27008_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + static int bu27008_write_raw(struct iio_dev *idev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -747,60 +1186,33 @@ static int bu27008_update_scan_mode(struct iio_dev *idev, chan_sel = BU27008_CLEAR2_IR3; } - chan_sel = FIELD_PREP(BU27008_MASK_CHAN_SEL, chan_sel); + chan_sel <<= ffs(data->cd->chan_sel_mask) - 1; - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3, - BU27008_MASK_CHAN_SEL, chan_sel); + return regmap_update_bits(data->regmap, data->cd->chan_sel_reg, + data->cd->chan_sel_mask, chan_sel); } static const struct iio_info bu27008_info = { .read_raw = &bu27008_read_raw, .write_raw = &bu27008_write_raw, + .write_raw_get_fmt = &bu27008_write_raw_get_fmt, .read_avail = &bu27008_read_avail, .update_scan_mode = bu27008_update_scan_mode, .validate_trigger = iio_validate_own_trigger, }; -static int bu27008_chip_init(struct bu27008_data *data) -{ - int ret; - - ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, - BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET); - if (ret) - return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); - - /* - * The data-sheet does not tell how long performing the IC reset takes. - * However, the data-sheet says the minimum time it takes the IC to be - * able to take inputs after power is applied, is 100 uS. I'd assume - * > 1 mS is enough. - */ - msleep(1); - - ret = regmap_reinit_cache(data->regmap, &bu27008_regmap); - if (ret) - dev_err(data->dev, "Failed to reinit reg cache\n"); - - return ret; -} - -static int bu27008_set_drdy_irq(struct bu27008_data *data, int state) -{ - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3, - BU27008_MASK_INT_EN, state); -} - -static int bu27008_trigger_set_state(struct iio_trigger *trig, - bool state) +static int bu27008_trigger_set_state(struct iio_trigger *trig, bool state) { struct bu27008_data *data = iio_trigger_get_drvdata(trig); int ret; + if (state) - ret = bu27008_set_drdy_irq(data, BU27008_INT_EN); + ret = regmap_set_bits(data->regmap, data->cd->drdy_en_reg, + data->cd->drdy_en_mask); else - ret = bu27008_set_drdy_irq(data, BU27008_INT_DIS); + ret = regmap_clear_bits(data->regmap, data->cd->drdy_en_reg, + data->cd->drdy_en_mask); if (ret) dev_err(data->dev, "Failed to set trigger state\n"); @@ -836,7 +1248,7 @@ static irqreturn_t bu27008_trigger_handler(int irq, void *p) * After some measurements, it seems reading the * BU27008_REG_MODE_CONTROL3 debounces the IRQ line */ - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL3, &dummy); + ret = regmap_read(data->regmap, data->cd->valid_reg, &dummy); if (ret < 0) goto err_read; @@ -856,14 +1268,14 @@ static int bu27008_buffer_preenable(struct iio_dev *idev) { struct bu27008_data *data = iio_priv(idev); - return bu27008_meas_set(data, BU27008_MEAS_EN); + return bu27008_meas_set(data, true); } static int bu27008_buffer_postdisable(struct iio_dev *idev) { struct bu27008_data *data = iio_priv(idev); - return bu27008_meas_set(data, BU27008_MEAS_DIS); + return bu27008_meas_set(data, false); } static const struct iio_buffer_setup_ops bu27008_buffer_ops = { @@ -936,11 +1348,6 @@ static int bu27008_probe(struct i2c_client *i2c) struct iio_dev *idev; int ret; - regmap = devm_regmap_init_i2c(i2c, &bu27008_regmap); - if (IS_ERR(regmap)) - return dev_err_probe(dev, PTR_ERR(regmap), - "Failed to initialize Regmap\n"); - idev = devm_iio_device_alloc(dev, sizeof(*data)); if (!idev) return -ENOMEM; @@ -951,24 +1358,34 @@ static int bu27008_probe(struct i2c_client *i2c) data = iio_priv(idev); + data->cd = device_get_match_data(&i2c->dev); + if (!data->cd) + return -ENODEV; + + regmap = devm_regmap_init_i2c(i2c, data->cd->regmap_cfg); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialize Regmap\n"); + + ret = regmap_read(regmap, BU27008_REG_SYSTEM_CONTROL, ®); if (ret) return dev_err_probe(dev, ret, "Failed to access sensor\n"); part_id = FIELD_GET(BU27008_MASK_PART_ID, reg); - if (part_id != BU27008_ID) + if (part_id != data->cd->part_id) dev_warn(dev, "unknown device 0x%x\n", part_id); - ret = devm_iio_init_iio_gts(dev, BU27008_SCALE_1X, 0, bu27008_gains, - ARRAY_SIZE(bu27008_gains), bu27008_itimes, - ARRAY_SIZE(bu27008_itimes), &data->gts); + ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains, + data->cd->num_gains, data->cd->itimes, + data->cd->num_itimes, &data->gts); if (ret) return ret; - ret = devm_iio_init_iio_gts(dev, BU27008_SCALE_1X, 0, bu27008_gains_ir, - ARRAY_SIZE(bu27008_gains_ir), bu27008_itimes, - ARRAY_SIZE(bu27008_itimes), &data->gts_ir); + ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains_ir, + data->cd->num_gains_ir, data->cd->itimes, + data->cd->num_itimes, &data->gts_ir); if (ret) return ret; @@ -979,12 +1396,12 @@ static int bu27008_probe(struct i2c_client *i2c) idev->channels = bu27008_channels; idev->num_channels = ARRAY_SIZE(bu27008_channels); - idev->name = "bu27008"; + idev->name = data->cd->name; idev->info = &bu27008_info; idev->modes = INDIO_DIRECT_MODE; idev->available_scan_masks = bu27008_scan_masks; - ret = bu27008_chip_init(data); + ret = data->cd->chip_init(data); if (ret) return ret; @@ -1005,7 +1422,8 @@ static int bu27008_probe(struct i2c_client *i2c) } static const struct of_device_id bu27008_of_match[] = { - { .compatible = "rohm,bu27008" }, + { .compatible = "rohm,bu27008", .data = &bu27008_chip }, + { .compatible = "rohm,bu27010", .data = &bu27010_chip }, { } }; MODULE_DEVICE_TABLE(of, bu27008_of_match); @@ -1020,7 +1438,7 @@ static struct i2c_driver bu27008_i2c_driver = { }; module_i2c_driver(bu27008_i2c_driver); -MODULE_DESCRIPTION("ROHM BU27008 colour sensor driver"); +MODULE_DESCRIPTION("ROHM BU27008 and BU27010 colour sensor driver"); MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(IIO_GTS_HELPER); |