[PATCH v2 2/3] iio: light: bu27008: add chip info

From: Matti Vaittinen
Date: Fri Jul 07 2023 - 07:23:20 EST


The ROHM BU27010 RGB + flickering sensor is in many regards similar to
the BU27008. Prepare for adding support for BU27010 by allowing
chip-specific properties to be brought from the of_device_id data.

Signed-off-by: Matti Vaittinen <mazziesaccount@xxxxxxxxx>

---
Revision history:
v1 => v2:
- Move all generic refactoring from next patch to this one so that the
next one will only contain the BU27010 specific additions.
- reorder bu27008 chip-data assignments to match the struct member
placement
- Cleanup
---
drivers/iio/light/rohm-bu27008.c | 323 ++++++++++++++++++++-----------
1 file changed, 208 insertions(+), 115 deletions(-)

diff --git a/drivers/iio/light/rohm-bu27008.c b/drivers/iio/light/rohm-bu27008.c
index b50bf8973d9a..08e2b1194bf4 100644
--- a/drivers/iio/light/rohm-bu27008.c
+++ b/drivers/iio/light/rohm-bu27008.c
@@ -211,7 +211,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;
@@ -282,51 +310,6 @@ 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;
- }
-
- *gain = ret;
-
- return 0;
-}
-
static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
{
int regval;
@@ -368,6 +351,123 @@ static int bu27008_write_gain_sel(struct bu27008_data *data, int sel)
BU27008_MASK_RGBC_GAIN, 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 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 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 +476,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 +484,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 +556,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 +634,13 @@ static int bu27008_try_set_int_time(struct bu27008_data *data, int int_time_new)
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 +653,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 +674,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 +690,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 +785,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 +878,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 +893,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 +940,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 +960,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 +1040,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 +1050,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;

@@ -993,14 +1086,14 @@ static int bu27008_probe(struct i2c_client *i2c)
data->dev = dev;
data->irq = i2c->irq;

- idev->channels = bu27008_channels;
+ idev->channels = &bu27008_channels[0];
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 +1114,7 @@ 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 },
{ }
};
MODULE_DEVICE_TABLE(of, bu27008_of_match);
--
2.40.1


--
Matti Vaittinen, Linux device drivers
ROHM Semiconductors, Finland SWDC
Kiviharjunlenkki 1E
90220 OULU
FINLAND

~~~ "I don't think so," said Rene Descartes. Just then he vanished ~~~
Simon says - in Latin please.
~~~ "non cogito me" dixit Rene Descarte, deinde evanescavit ~~~
Thanks to Simon Glass for the translation =]

Attachment: signature.asc
Description: PGP signature