Merge 6.5-rc6 into char-misc-next
[platform/kernel/linux-starfive.git] / drivers / iio / light / rohm-bu27008.c
index b50bf89..6a6d778 100644 (file)
@@ -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.
  */
 #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)
 #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
@@ -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);
+static const struct regmap_config bu27010_regmap = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
 
-       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;
-}
+       .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;
@@ -669,7 +1093,7 @@ static int bu27008_set_scale(struct bu27008_data *data,
                        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);
@@ -762,10 +1186,10 @@ 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 = {
@@ -777,46 +1201,18 @@ static const struct iio_info bu27008_info = {
        .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");
 
@@ -852,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;
 
@@ -872,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 = {
@@ -952,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;
@@ -967,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, &reg);
        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;
 
@@ -995,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;
 
@@ -1021,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);
@@ -1036,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);