Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: light: bd27008: Support BD27010 RGB

The ROHM BU27010 is an RGBC sensor with a flickering detection FIFO. The
RGBC+IR sensor functionality is largely similar to what the BU27008 has.
There are some notable things though:
- gain setting is once again new and exotic. Now, there is 6bit gain
setting where 4 of the bits are common to all channels and 2 bits
can be configured separately for each channel. The BU27010 has
similar "1X on other channels vs 2X on IR when selector is 0x0"
gain design as BU27008 had. So, we use same gain setting policy for
BU27010 as we did for BU27008 - driver sets same gain selector for all
channels but shows the gains separately for all channels so users
can (at least in theory) detect this 1X vs 2X madness...
- BU27010 has suffled all the control register bitfields to new
addresses and bit positions while still keeping the register naming
same.
- Some more power/reset control is added.
- FIFO for "flickering detection" is added.

The control register suffling made this slightly nasty. Still, it is
easier for maintenance perspective to add the BU27010 support in BU27008
driver because - even though the bit positions/addresses were changed -
most of the driver structure can be re-used. Writing own driver for
BU27010 would mean plenty of duplicate code albeit a tad more clarity.

The flickering FIFO is not supported by the driver.

Add BU27010 RGBC+IR support to rohm-bu27008 driver.

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Link: https://lore.kernel.org/r/111cd217ccece1c1f16ab4287532dc4e1ddb8a3f.1690958450.git.mazziesaccount@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Matti Vaittinen and committed by
Jonathan Cameron
fdb48f9d ccca97fb

+311 -2
+311 -2
drivers/iio/light/rohm-bu27008.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* 3 - * BU27008 ROHM Colour Sensor 3 + * ROHM Colour Sensor driver for 4 + * - BU27008 RGBC sensor 5 + * - BU27010 RGBC + Flickering sensor 4 6 * 5 7 * Copyright (c) 2023, ROHM Semiconductor. 6 8 */ ··· 24 22 #include <linux/iio/trigger_consumer.h> 25 23 #include <linux/iio/triggered_buffer.h> 26 24 25 + /* 26 + * A word about register address and mask definitions. 27 + * 28 + * At a quick glance to the data-sheet register tables, the BU27010 has all the 29 + * registers that the BU27008 has. On top of that the BU27010 adds couple of new 30 + * ones. 31 + * 32 + * So, all definitions BU27008_REG_* are there also for BU27010 but none of the 33 + * BU27010_REG_* are present on BU27008. This makes sense as BU27010 just adds 34 + * some features (Flicker FIFO, more power control) on top of the BU27008. 35 + * 36 + * Unfortunately, some of the wheel has been re-invented. Even though the names 37 + * of the registers have stayed the same, pretty much all of the functionality 38 + * provided by the registers has changed place. Contents of all MODE_CONTROL 39 + * registers on BU27008 and BU27010 are different. 40 + * 41 + * Chip-specific mapping from register addresses/bits to functionality is done 42 + * in bu27_chip_data structures. 43 + */ 27 44 #define BU27008_REG_SYSTEM_CONTROL 0x40 28 45 #define BU27008_MASK_SW_RESET BIT(7) 29 46 #define BU27008_MASK_PART_ID GENMASK(5, 0) ··· 72 51 #define BU27008_REG_DATA3_HI 0x57 73 52 #define BU27008_REG_MANUFACTURER_ID 0x92 74 53 #define BU27008_REG_MAX BU27008_REG_MANUFACTURER_ID 54 + 55 + /* BU27010 specific definitions */ 56 + 57 + #define BU27010_MASK_SW_RESET BIT(7) 58 + #define BU27010_ID 0x1b 59 + #define BU27010_REG_POWER 0x3e 60 + #define BU27010_MASK_POWER BIT(0) 61 + 62 + #define BU27010_REG_RESET 0x3f 63 + #define BU27010_MASK_RESET BIT(0) 64 + #define BU27010_RESET_RELEASE BU27010_MASK_RESET 65 + 66 + #define BU27010_MASK_MEAS_EN BIT(1) 67 + 68 + #define BU27010_MASK_CHAN_SEL GENMASK(7, 6) 69 + #define BU27010_MASK_MEAS_MODE GENMASK(5, 4) 70 + #define BU27010_MASK_RGBC_GAIN GENMASK(3, 0) 71 + 72 + #define BU27010_MASK_DATA3_GAIN GENMASK(7, 6) 73 + #define BU27010_MASK_DATA2_GAIN GENMASK(5, 4) 74 + #define BU27010_MASK_DATA1_GAIN GENMASK(3, 2) 75 + #define BU27010_MASK_DATA0_GAIN GENMASK(1, 0) 76 + 77 + #define BU27010_MASK_FLC_MODE BIT(7) 78 + #define BU27010_MASK_FLC_GAIN GENMASK(4, 0) 79 + 80 + #define BU27010_REG_MODE_CONTROL4 0x44 81 + /* If flicker is ever to be supported the IRQ must be handled as a field */ 82 + #define BU27010_IRQ_DIS_ALL GENMASK(1, 0) 83 + #define BU27010_DRDY_EN BIT(0) 84 + #define BU27010_MASK_INT_SEL GENMASK(1, 0) 85 + 86 + #define BU27010_REG_MODE_CONTROL5 0x45 87 + #define BU27010_MASK_RGB_VALID BIT(7) 88 + #define BU27010_MASK_FLC_VALID BIT(6) 89 + #define BU27010_MASK_WAIT_EN BIT(3) 90 + #define BU27010_MASK_FIFO_EN BIT(2) 91 + #define BU27010_MASK_RGB_EN BIT(1) 92 + #define BU27010_MASK_FLC_EN BIT(0) 93 + 94 + #define BU27010_REG_DATA_FLICKER_LO 0x56 95 + #define BU27010_MASK_DATA_FLICKER_HI GENMASK(2, 0) 96 + #define BU27010_REG_FLICKER_COUNT 0x5a 97 + #define BU27010_REG_FIFO_LEVEL_LO 0x5b 98 + #define BU27010_MASK_FIFO_LEVEL_HI BIT(0) 99 + #define BU27010_REG_FIFO_DATA_LO 0x5d 100 + #define BU27010_REG_FIFO_DATA_HI 0x5e 101 + #define BU27010_MASK_FIFO_DATA_HI GENMASK(2, 0) 102 + #define BU27010_REG_MANUFACTURER_ID 0x92 103 + #define BU27010_REG_MAX BU27010_REG_MANUFACTURER_ID 75 104 76 105 /** 77 106 * enum bu27008_chan_type - BU27008 channel types ··· 188 117 */ 189 118 #define BU27008_SCALE_1X 16 190 119 120 + /* 121 + * On BU27010 available scales with gain 1x - 4096x, 122 + * timings 55, 100, 200, 400 mS. Time impacts to gain: 1x, 2x, 4x, 8x. 123 + * 124 + * => Max total gain is HWGAIN * gain by integration time (8 * 4096) 125 + * 126 + * Using NANO precision for scale we must use scale 64x corresponding gain 1x 127 + * to avoid precision loss. 128 + */ 129 + #define BU27010_SCALE_1X 64 130 + 191 131 /* See the data sheet for the "Gain Setting" table */ 192 132 #define BU27008_GSEL_1X 0x00 193 133 #define BU27008_GSEL_4X 0x08 ··· 234 152 GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X), 235 153 }; 236 154 155 + #define BU27010_GSEL_1X 0x00 /* 000000 */ 156 + #define BU27010_GSEL_4X 0x08 /* 001000 */ 157 + #define BU27010_GSEL_16X 0x09 /* 001001 */ 158 + #define BU27010_GSEL_64X 0x0e /* 001110 */ 159 + #define BU27010_GSEL_256X 0x1e /* 011110 */ 160 + #define BU27010_GSEL_1024X 0x2e /* 101110 */ 161 + #define BU27010_GSEL_4096X 0x3f /* 111111 */ 162 + 163 + static const struct iio_gain_sel_pair bu27010_gains[] = { 164 + GAIN_SCALE_GAIN(1, BU27010_GSEL_1X), 165 + GAIN_SCALE_GAIN(4, BU27010_GSEL_4X), 166 + GAIN_SCALE_GAIN(16, BU27010_GSEL_16X), 167 + GAIN_SCALE_GAIN(64, BU27010_GSEL_64X), 168 + GAIN_SCALE_GAIN(256, BU27010_GSEL_256X), 169 + GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X), 170 + GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X), 171 + }; 172 + 173 + static const struct iio_gain_sel_pair bu27010_gains_ir[] = { 174 + GAIN_SCALE_GAIN(2, BU27010_GSEL_1X), 175 + GAIN_SCALE_GAIN(4, BU27010_GSEL_4X), 176 + GAIN_SCALE_GAIN(16, BU27010_GSEL_16X), 177 + GAIN_SCALE_GAIN(64, BU27010_GSEL_64X), 178 + GAIN_SCALE_GAIN(256, BU27010_GSEL_256X), 179 + GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X), 180 + GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X), 181 + }; 182 + 237 183 #define BU27008_MEAS_MODE_100MS 0x00 238 184 #define BU27008_MEAS_MODE_55MS 0x01 239 185 #define BU27008_MEAS_MODE_200MS 0x02 240 186 #define BU27008_MEAS_MODE_400MS 0x04 187 + 188 + #define BU27010_MEAS_MODE_100MS 0x00 189 + #define BU27010_MEAS_MODE_55MS 0x03 190 + #define BU27010_MEAS_MODE_200MS 0x01 191 + #define BU27010_MEAS_MODE_400MS 0x02 192 + 241 193 #define BU27008_MEAS_TIME_MAX_MS 400 242 194 243 195 static const struct iio_itime_sel_mul bu27008_itimes[] = { ··· 279 163 GAIN_SCALE_ITIME_US(200000, BU27008_MEAS_MODE_200MS, 4), 280 164 GAIN_SCALE_ITIME_US(100000, BU27008_MEAS_MODE_100MS, 2), 281 165 GAIN_SCALE_ITIME_US(55000, BU27008_MEAS_MODE_55MS, 1), 166 + }; 167 + 168 + static const struct iio_itime_sel_mul bu27010_itimes[] = { 169 + GAIN_SCALE_ITIME_US(400000, BU27010_MEAS_MODE_400MS, 8), 170 + GAIN_SCALE_ITIME_US(200000, BU27010_MEAS_MODE_200MS, 4), 171 + GAIN_SCALE_ITIME_US(100000, BU27010_MEAS_MODE_100MS, 2), 172 + GAIN_SCALE_ITIME_US(55000, BU27010_MEAS_MODE_55MS, 1), 282 173 }; 283 174 284 175 /* ··· 391 268 }, 392 269 }; 393 270 271 + static const struct regmap_range bu27010_volatile_ranges[] = { 272 + { 273 + .range_min = BU27010_REG_RESET, /* RSTB */ 274 + .range_max = BU27008_REG_SYSTEM_CONTROL, /* RESET */ 275 + }, { 276 + .range_min = BU27010_REG_MODE_CONTROL5, /* VALID bits */ 277 + .range_max = BU27010_REG_MODE_CONTROL5, 278 + }, { 279 + .range_min = BU27008_REG_DATA0_LO, 280 + .range_max = BU27010_REG_FIFO_DATA_HI, 281 + }, 282 + }; 283 + 394 284 static const struct regmap_access_table bu27008_volatile_regs = { 395 285 .yes_ranges = &bu27008_volatile_ranges[0], 396 286 .n_yes_ranges = ARRAY_SIZE(bu27008_volatile_ranges), 287 + }; 288 + 289 + static const struct regmap_access_table bu27010_volatile_regs = { 290 + .yes_ranges = &bu27010_volatile_ranges[0], 291 + .n_yes_ranges = ARRAY_SIZE(bu27010_volatile_ranges), 397 292 }; 398 293 399 294 static const struct regmap_range bu27008_read_only_ranges[] = { ··· 424 283 }, 425 284 }; 426 285 286 + static const struct regmap_range bu27010_read_only_ranges[] = { 287 + { 288 + .range_min = BU27008_REG_DATA0_LO, 289 + .range_max = BU27010_REG_FIFO_DATA_HI, 290 + }, { 291 + .range_min = BU27010_REG_MANUFACTURER_ID, 292 + .range_max = BU27010_REG_MANUFACTURER_ID, 293 + } 294 + }; 295 + 427 296 static const struct regmap_access_table bu27008_ro_regs = { 428 297 .no_ranges = &bu27008_read_only_ranges[0], 429 298 .n_no_ranges = ARRAY_SIZE(bu27008_read_only_ranges), 299 + }; 300 + 301 + static const struct regmap_access_table bu27010_ro_regs = { 302 + .no_ranges = &bu27010_read_only_ranges[0], 303 + .n_no_ranges = ARRAY_SIZE(bu27010_read_only_ranges), 430 304 }; 431 305 432 306 static const struct regmap_config bu27008_regmap = { ··· 463 307 * read-modify-write cycle (eg. regmap_update_bits()). Please, revise 464 308 * this when adding features to the driver. 465 309 */ 310 + .disable_locking = true, 311 + }; 312 + 313 + static const struct regmap_config bu27010_regmap = { 314 + .reg_bits = 8, 315 + .val_bits = 8, 316 + 317 + .max_register = BU27010_REG_MAX, 318 + .cache_type = REGCACHE_RBTREE, 319 + .volatile_table = &bu27010_volatile_regs, 320 + .wr_table = &bu27010_ro_regs, 466 321 .disable_locking = true, 467 322 }; 468 323 ··· 518 351 BU27008_MASK_RGBC_GAIN, regval); 519 352 } 520 353 354 + static int bu27010_write_gain_sel(struct bu27008_data *data, int sel) 355 + { 356 + unsigned int regval; 357 + int ret, chan_selector; 358 + 359 + /* 360 + * Gain 'selector' is composed of two registers. Selector is 6bit value, 361 + * 4 high bits being the RGBC gain fieild in MODE_CONTROL1 register and 362 + * two low bits being the channel specific gain in MODE_CONTROL2. 363 + * 364 + * Let's take the 4 high bits of whole 6 bit selector, and prepare 365 + * the MODE_CONTROL1 value (RGBC gain part). 366 + */ 367 + regval = FIELD_PREP(BU27010_MASK_RGBC_GAIN, (sel >> 2)); 368 + 369 + ret = regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, 370 + BU27010_MASK_RGBC_GAIN, regval); 371 + if (ret) 372 + return ret; 373 + 374 + /* 375 + * Two low two bits of the selector must be written for all 4 376 + * channels in the MODE_CONTROL2 register. Copy these two bits for 377 + * all channels. 378 + */ 379 + chan_selector = sel & GENMASK(1, 0); 380 + 381 + regval = FIELD_PREP(BU27010_MASK_DATA0_GAIN, chan_selector); 382 + regval |= FIELD_PREP(BU27010_MASK_DATA1_GAIN, chan_selector); 383 + regval |= FIELD_PREP(BU27010_MASK_DATA2_GAIN, chan_selector); 384 + regval |= FIELD_PREP(BU27010_MASK_DATA3_GAIN, chan_selector); 385 + 386 + return regmap_write(data->regmap, BU27008_REG_MODE_CONTROL2, regval); 387 + } 388 + 521 389 static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel) 522 390 { 523 391 int ret; ··· 579 377 return 0; 580 378 } 581 379 380 + static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel) 381 + { 382 + int ret, tmp; 383 + 384 + /* 385 + * We always "lock" the gain selectors for all channels to prevent 386 + * unsupported configs. It does not matter which channel is used 387 + * we can just return selector from any of them. 388 + * 389 + * Read the channel0 gain. 390 + */ 391 + ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel); 392 + if (ret) 393 + return ret; 394 + 395 + *sel = FIELD_GET(BU27010_MASK_DATA0_GAIN, *sel); 396 + 397 + /* Read the shared gain */ 398 + ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &tmp); 399 + if (ret) 400 + return ret; 401 + 402 + /* 403 + * The gain selector is made as a combination of common RGBC gain and 404 + * the channel specific gain. The channel specific gain forms the low 405 + * bits of selector and RGBC gain is appended right after it. 406 + * 407 + * Compose the selector from channel0 gain and shared RGBC gain. 408 + */ 409 + *sel |= FIELD_GET(BU27010_MASK_RGBC_GAIN, tmp) << fls(BU27010_MASK_DATA0_GAIN); 410 + 411 + return ret; 412 + } 413 + 582 414 static int bu27008_chip_init(struct bu27008_data *data) 583 415 { 584 416 int ret; ··· 636 400 637 401 return ret; 638 402 } 403 + 404 + static int bu27010_chip_init(struct bu27008_data *data) 405 + { 406 + int ret; 407 + 408 + ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, 409 + BU27010_MASK_SW_RESET, BU27010_MASK_SW_RESET); 410 + if (ret) 411 + return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); 412 + 413 + msleep(1); 414 + 415 + /* Power ON*/ 416 + ret = regmap_write_bits(data->regmap, BU27010_REG_POWER, 417 + BU27010_MASK_POWER, BU27010_MASK_POWER); 418 + if (ret) 419 + return dev_err_probe(data->dev, ret, "Sensor power-on failed\n"); 420 + 421 + msleep(1); 422 + 423 + /* Release blocks from reset */ 424 + ret = regmap_write_bits(data->regmap, BU27010_REG_RESET, 425 + BU27010_MASK_RESET, BU27010_RESET_RELEASE); 426 + if (ret) 427 + return dev_err_probe(data->dev, ret, "Sensor powering failed\n"); 428 + 429 + msleep(1); 430 + 431 + /* 432 + * The IRQ enabling on BU27010 is done in a peculiar way. The IRQ 433 + * enabling is not a bit mask where individual IRQs could be enabled but 434 + * a field which values are: 435 + * 00 => IRQs disabled 436 + * 01 => Data-ready (RGBC/IR) 437 + * 10 => Data-ready (flicker) 438 + * 11 => Flicker FIFO 439 + * 440 + * So, only one IRQ can be enabled at a time and enabling for example 441 + * flicker FIFO would automagically disable data-ready IRQ. 442 + * 443 + * Currently the driver does not support the flicker. Hence, we can 444 + * just treat the RGBC data-ready as single bit which can be enabled / 445 + * disabled. This works for as long as the second bit in the field 446 + * stays zero. Here we ensure it gets zeroed. 447 + */ 448 + return regmap_clear_bits(data->regmap, BU27010_REG_MODE_CONTROL4, 449 + BU27010_IRQ_DIS_ALL); 450 + } 451 + 452 + static const struct bu27_chip_data bu27010_chip = { 453 + .name = "bu27010", 454 + .chip_init = bu27010_chip_init, 455 + .get_gain_sel = bu27010_get_gain_sel, 456 + .write_gain_sel = bu27010_write_gain_sel, 457 + .regmap_cfg = &bu27010_regmap, 458 + .gains = &bu27010_gains[0], 459 + .gains_ir = &bu27010_gains_ir[0], 460 + .itimes = &bu27010_itimes[0], 461 + .num_gains = ARRAY_SIZE(bu27010_gains), 462 + .num_gains_ir = ARRAY_SIZE(bu27010_gains_ir), 463 + .num_itimes = ARRAY_SIZE(bu27010_itimes), 464 + .scale1x = BU27010_SCALE_1X, 465 + .drdy_en_reg = BU27010_REG_MODE_CONTROL4, 466 + .drdy_en_mask = BU27010_DRDY_EN, 467 + .meas_en_reg = BU27010_REG_MODE_CONTROL5, 468 + .meas_en_mask = BU27010_MASK_MEAS_EN, 469 + .valid_reg = BU27010_REG_MODE_CONTROL5, 470 + .chan_sel_reg = BU27008_REG_MODE_CONTROL1, 471 + .chan_sel_mask = BU27010_MASK_CHAN_SEL, 472 + .int_time_mask = BU27010_MASK_MEAS_MODE, 473 + .part_id = BU27010_ID, 474 + }; 639 475 640 476 static const struct bu27_chip_data bu27008_chip = { 641 477 .name = "bu27008", ··· 1407 1099 1408 1100 static const struct of_device_id bu27008_of_match[] = { 1409 1101 { .compatible = "rohm,bu27008", .data = &bu27008_chip }, 1102 + { .compatible = "rohm,bu27010", .data = &bu27010_chip }, 1410 1103 { } 1411 1104 }; 1412 1105 MODULE_DEVICE_TABLE(of, bu27008_of_match); ··· 1422 1113 }; 1423 1114 module_i2c_driver(bu27008_i2c_driver); 1424 1115 1425 - MODULE_DESCRIPTION("ROHM BU27008 colour sensor driver"); 1116 + MODULE_DESCRIPTION("ROHM BU27008 and BU27010 colour sensor driver"); 1426 1117 MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); 1427 1118 MODULE_LICENSE("GPL"); 1428 1119 MODULE_IMPORT_NS(IIO_GTS_HELPER);