| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * ROHM BH1745 digital colour sensor driver
- *
- * Copyright (C) Mudit Sharma <muditsharma.info@gmail.com>
- *
- * 7-bit I2C slave addresses:
- * 0x38 (ADDR pin low)
- * 0x39 (ADDR pin high)
- */
- #include <linux/i2c.h>
- #include <linux/mutex.h>
- #include <linux/util_macros.h>
- #include <linux/iio/events.h>
- #include <linux/regmap.h>
- #include <linux/bits.h>
- #include <linux/bitfield.h>
- #include <linux/iio/iio.h>
- #include <linux/iio/sysfs.h>
- #include <linux/iio/trigger.h>
- #include <linux/iio/trigger_consumer.h>
- #include <linux/iio/triggered_buffer.h>
- #include <linux/iio/iio-gts-helper.h>
- /* BH1745 configuration registers */
- /* System control */
- #define BH1745_SYS_CTRL 0x40
- #define BH1745_SYS_CTRL_SW_RESET BIT(7)
- #define BH1745_SYS_CTRL_INTR_RESET BIT(6)
- #define BH1745_SYS_CTRL_PART_ID_MASK GENMASK(5, 0)
- #define BH1745_PART_ID 0x0B
- /* Mode control 1 */
- #define BH1745_MODE_CTRL1 0x41
- #define BH1745_CTRL1_MEASUREMENT_TIME_MASK GENMASK(2, 0)
- /* Mode control 2 */
- #define BH1745_MODE_CTRL2 0x42
- #define BH1745_CTRL2_RGBC_EN BIT(4)
- #define BH1745_CTRL2_ADC_GAIN_MASK GENMASK(1, 0)
- /* Interrupt */
- #define BH1745_INTR 0x60
- #define BH1745_INTR_STATUS BIT(7)
- #define BH1745_INTR_SOURCE_MASK GENMASK(3, 2)
- #define BH1745_INTR_ENABLE BIT(0)
- #define BH1745_PERSISTENCE 0x61
- /* Threshold high */
- #define BH1745_TH_LSB 0x62
- #define BH1745_TH_MSB 0x63
- /* Threshold low */
- #define BH1745_TL_LSB 0x64
- #define BH1745_TL_MSB 0x65
- /* BH1745 data output regs */
- #define BH1745_RED_LSB 0x50
- #define BH1745_RED_MSB 0x51
- #define BH1745_GREEN_LSB 0x52
- #define BH1745_GREEN_MSB 0x53
- #define BH1745_BLUE_LSB 0x54
- #define BH1745_BLUE_MSB 0x55
- #define BH1745_CLEAR_LSB 0x56
- #define BH1745_CLEAR_MSB 0x57
- #define BH1745_MANU_ID_REG 0x92
- /* From 16x max HW gain and 32x max integration time */
- #define BH1745_MAX_GAIN 512
- enum bh1745_int_source {
- BH1745_INTR_SOURCE_RED,
- BH1745_INTR_SOURCE_GREEN,
- BH1745_INTR_SOURCE_BLUE,
- BH1745_INTR_SOURCE_CLEAR,
- };
- enum bh1745_gain {
- BH1745_ADC_GAIN_1X,
- BH1745_ADC_GAIN_2X,
- BH1745_ADC_GAIN_16X,
- };
- enum bh1745_measurement_time {
- BH1745_MEASUREMENT_TIME_160MS,
- BH1745_MEASUREMENT_TIME_320MS,
- BH1745_MEASUREMENT_TIME_640MS,
- BH1745_MEASUREMENT_TIME_1280MS,
- BH1745_MEASUREMENT_TIME_2560MS,
- BH1745_MEASUREMENT_TIME_5120MS,
- };
- enum bh1745_presistence_value {
- BH1745_PRESISTENCE_UPDATE_TOGGLE,
- BH1745_PRESISTENCE_UPDATE_EACH_MEASUREMENT,
- BH1745_PRESISTENCE_UPDATE_FOUR_MEASUREMENT,
- BH1745_PRESISTENCE_UPDATE_EIGHT_MEASUREMENT,
- };
- static const struct iio_gain_sel_pair bh1745_gain[] = {
- GAIN_SCALE_GAIN(1, BH1745_ADC_GAIN_1X),
- GAIN_SCALE_GAIN(2, BH1745_ADC_GAIN_2X),
- GAIN_SCALE_GAIN(16, BH1745_ADC_GAIN_16X),
- };
- static const struct iio_itime_sel_mul bh1745_itimes[] = {
- GAIN_SCALE_ITIME_US(5120000, BH1745_MEASUREMENT_TIME_5120MS, 32),
- GAIN_SCALE_ITIME_US(2560000, BH1745_MEASUREMENT_TIME_2560MS, 16),
- GAIN_SCALE_ITIME_US(1280000, BH1745_MEASUREMENT_TIME_1280MS, 8),
- GAIN_SCALE_ITIME_US(640000, BH1745_MEASUREMENT_TIME_640MS, 4),
- GAIN_SCALE_ITIME_US(320000, BH1745_MEASUREMENT_TIME_320MS, 2),
- GAIN_SCALE_ITIME_US(160000, BH1745_MEASUREMENT_TIME_160MS, 1),
- };
- struct bh1745_data {
- /*
- * Lock to prevent device setting update or read before
- * related calculations are completed
- */
- struct mutex lock;
- struct regmap *regmap;
- struct device *dev;
- struct iio_trigger *trig;
- struct iio_gts gts;
- };
- static const struct regmap_range bh1745_volatile_ranges[] = {
- regmap_reg_range(BH1745_MODE_CTRL2, BH1745_MODE_CTRL2), /* VALID */
- regmap_reg_range(BH1745_RED_LSB, BH1745_CLEAR_MSB), /* Data */
- regmap_reg_range(BH1745_INTR, BH1745_INTR), /* Interrupt */
- };
- static const struct regmap_access_table bh1745_volatile_regs = {
- .yes_ranges = bh1745_volatile_ranges,
- .n_yes_ranges = ARRAY_SIZE(bh1745_volatile_ranges),
- };
- static const struct regmap_range bh1745_readable_ranges[] = {
- regmap_reg_range(BH1745_SYS_CTRL, BH1745_MODE_CTRL2),
- regmap_reg_range(BH1745_RED_LSB, BH1745_CLEAR_MSB),
- regmap_reg_range(BH1745_INTR, BH1745_INTR),
- regmap_reg_range(BH1745_PERSISTENCE, BH1745_TL_MSB),
- regmap_reg_range(BH1745_MANU_ID_REG, BH1745_MANU_ID_REG),
- };
- static const struct regmap_access_table bh1745_readable_regs = {
- .yes_ranges = bh1745_readable_ranges,
- .n_yes_ranges = ARRAY_SIZE(bh1745_readable_ranges),
- };
- static const struct regmap_range bh1745_writable_ranges[] = {
- regmap_reg_range(BH1745_SYS_CTRL, BH1745_MODE_CTRL2),
- regmap_reg_range(BH1745_INTR, BH1745_INTR),
- regmap_reg_range(BH1745_PERSISTENCE, BH1745_TL_MSB),
- };
- static const struct regmap_access_table bh1745_writable_regs = {
- .yes_ranges = bh1745_writable_ranges,
- .n_yes_ranges = ARRAY_SIZE(bh1745_writable_ranges),
- };
- static const struct regmap_config bh1745_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = BH1745_MANU_ID_REG,
- .cache_type = REGCACHE_RBTREE,
- .volatile_table = &bh1745_volatile_regs,
- .wr_table = &bh1745_writable_regs,
- .rd_table = &bh1745_readable_regs,
- };
- static const struct iio_event_spec bh1745_event_spec[] = {
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_RISING,
- .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
- },
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_FALLING,
- .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
- },
- {
- .type = IIO_EV_TYPE_THRESH,
- .dir = IIO_EV_DIR_EITHER,
- .mask_shared_by_type = BIT(IIO_EV_INFO_PERIOD),
- .mask_separate = BIT(IIO_EV_INFO_ENABLE),
- },
- };
- #define BH1745_CHANNEL(_colour, _si, _addr) \
- { \
- .type = IIO_INTENSITY, .modified = 1, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_INT_TIME), \
- .info_mask_shared_by_all_available = \
- BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_INT_TIME), \
- .event_spec = bh1745_event_spec, \
- .num_event_specs = ARRAY_SIZE(bh1745_event_spec), \
- .channel2 = IIO_MOD_LIGHT_##_colour, .address = _addr, \
- .scan_index = _si, \
- .scan_type = { \
- .sign = 'u', \
- .realbits = 16, \
- .storagebits = 16, \
- .endianness = IIO_CPU, \
- }, \
- }
- static const struct iio_chan_spec bh1745_channels[] = {
- BH1745_CHANNEL(RED, 0, BH1745_RED_LSB),
- BH1745_CHANNEL(GREEN, 1, BH1745_GREEN_LSB),
- BH1745_CHANNEL(BLUE, 2, BH1745_BLUE_LSB),
- BH1745_CHANNEL(CLEAR, 3, BH1745_CLEAR_LSB),
- IIO_CHAN_SOFT_TIMESTAMP(4),
- };
- static int bh1745_reset(struct bh1745_data *data)
- {
- return regmap_set_bits(data->regmap, BH1745_SYS_CTRL,
- BH1745_SYS_CTRL_SW_RESET |
- BH1745_SYS_CTRL_INTR_RESET);
- }
- static int bh1745_power_on(struct bh1745_data *data)
- {
- return regmap_set_bits(data->regmap, BH1745_MODE_CTRL2,
- BH1745_CTRL2_RGBC_EN);
- }
- static void bh1745_power_off(void *data_ptr)
- {
- struct bh1745_data *data = data_ptr;
- struct device *dev = data->dev;
- int ret;
- ret = regmap_clear_bits(data->regmap, BH1745_MODE_CTRL2,
- BH1745_CTRL2_RGBC_EN);
- if (ret)
- dev_err(dev, "Failed to turn off device\n");
- }
- static int bh1745_get_scale(struct bh1745_data *data, int *val, int *val2)
- {
- int ret;
- int value;
- int gain_sel, int_time_sel;
- int gain;
- const struct iio_itime_sel_mul *int_time;
- ret = regmap_read(data->regmap, BH1745_MODE_CTRL2, &value);
- if (ret)
- return ret;
- gain_sel = FIELD_GET(BH1745_CTRL2_ADC_GAIN_MASK, value);
- gain = iio_gts_find_gain_by_sel(&data->gts, gain_sel);
- ret = regmap_read(data->regmap, BH1745_MODE_CTRL1, &value);
- if (ret)
- return ret;
- int_time_sel = FIELD_GET(BH1745_CTRL1_MEASUREMENT_TIME_MASK, value);
- int_time = iio_gts_find_itime_by_sel(&data->gts, int_time_sel);
- return iio_gts_get_scale(&data->gts, gain, int_time->time_us, val,
- val2);
- }
- static int bh1745_set_scale(struct bh1745_data *data, int val)
- {
- struct device *dev = data->dev;
- int ret;
- int value;
- int hw_gain_sel, current_int_time_sel, new_int_time_sel;
- ret = regmap_read(data->regmap, BH1745_MODE_CTRL1, &value);
- if (ret)
- return ret;
- current_int_time_sel = FIELD_GET(BH1745_CTRL1_MEASUREMENT_TIME_MASK,
- value);
- ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
- current_int_time_sel,
- val, 0, &hw_gain_sel);
- if (ret) {
- for (int i = 0; i < data->gts.num_itime; i++) {
- new_int_time_sel = data->gts.itime_table[i].sel;
- if (new_int_time_sel == current_int_time_sel)
- continue;
- ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts,
- new_int_time_sel,
- val, 0,
- &hw_gain_sel);
- if (!ret)
- break;
- }
- if (ret) {
- dev_dbg(dev, "Unsupported scale value requested: %d\n",
- val);
- return -EINVAL;
- }
- ret = regmap_write_bits(data->regmap, BH1745_MODE_CTRL1,
- BH1745_CTRL1_MEASUREMENT_TIME_MASK,
- new_int_time_sel);
- if (ret)
- return ret;
- }
- return regmap_write_bits(data->regmap, BH1745_MODE_CTRL2,
- BH1745_CTRL2_ADC_GAIN_MASK, hw_gain_sel);
- }
- static int bh1745_get_int_time(struct bh1745_data *data, int *val)
- {
- int ret;
- int value;
- int int_time, int_time_sel;
- ret = regmap_read(data->regmap, BH1745_MODE_CTRL1, &value);
- if (ret)
- return ret;
- int_time_sel = FIELD_GET(BH1745_CTRL1_MEASUREMENT_TIME_MASK, value);
- int_time = iio_gts_find_int_time_by_sel(&data->gts, int_time_sel);
- if (int_time < 0)
- return int_time;
- *val = int_time;
- return 0;
- }
- static int bh1745_set_int_time(struct bh1745_data *data, int val, int val2)
- {
- struct device *dev = data->dev;
- int ret;
- int value;
- int current_int_time, current_hwgain_sel, current_hwgain;
- int new_hwgain, new_hwgain_sel, new_int_time_sel;
- int req_int_time = (1000000 * val) + val2;
- if (!iio_gts_valid_time(&data->gts, req_int_time)) {
- dev_dbg(dev, "Unsupported integration time requested: %d\n",
- req_int_time);
- return -EINVAL;
- }
- ret = bh1745_get_int_time(data, ¤t_int_time);
- if (ret)
- return ret;
- if (current_int_time == req_int_time)
- return 0;
- ret = regmap_read(data->regmap, BH1745_MODE_CTRL2, &value);
- if (ret)
- return ret;
- current_hwgain_sel = FIELD_GET(BH1745_CTRL2_ADC_GAIN_MASK, value);
- current_hwgain = iio_gts_find_gain_by_sel(&data->gts,
- current_hwgain_sel);
- ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts, current_hwgain,
- current_int_time,
- req_int_time,
- &new_hwgain);
- if (new_hwgain < 0) {
- dev_dbg(dev, "No corresponding gain for requested integration time\n");
- return ret;
- }
- if (ret) {
- bool in_range;
- new_hwgain = iio_find_closest_gain_low(&data->gts, new_hwgain,
- &in_range);
- if (new_hwgain < 0) {
- new_hwgain = iio_gts_get_min_gain(&data->gts);
- if (new_hwgain < 0)
- return ret;
- }
- if (!in_range)
- dev_dbg(dev, "Optimal gain out of range\n");
- dev_dbg(dev, "Scale changed, new hw_gain %d\n", new_hwgain);
- }
- new_hwgain_sel = iio_gts_find_sel_by_gain(&data->gts, new_hwgain);
- if (new_hwgain_sel < 0)
- return new_hwgain_sel;
- ret = regmap_write_bits(data->regmap, BH1745_MODE_CTRL2,
- BH1745_CTRL2_ADC_GAIN_MASK,
- new_hwgain_sel);
- if (ret)
- return ret;
- new_int_time_sel = iio_gts_find_sel_by_int_time(&data->gts,
- req_int_time);
- if (new_int_time_sel < 0)
- return new_int_time_sel;
- return regmap_write_bits(data->regmap, BH1745_MODE_CTRL1,
- BH1745_CTRL1_MEASUREMENT_TIME_MASK,
- new_int_time_sel);
- }
- static int bh1745_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
- {
- struct bh1745_data *data = iio_priv(indio_dev);
- int ret;
- int value;
- switch (mask) {
- case IIO_CHAN_INFO_RAW:
- iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
- ret = regmap_bulk_read(data->regmap, chan->address,
- &value, 2);
- if (ret)
- return ret;
- *val = value;
- return IIO_VAL_INT;
- }
- unreachable();
- case IIO_CHAN_INFO_SCALE: {
- guard(mutex)(&data->lock);
- ret = bh1745_get_scale(data, val, val2);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- }
- case IIO_CHAN_INFO_INT_TIME: {
- guard(mutex)(&data->lock);
- *val = 0;
- ret = bh1745_get_int_time(data, val2);
- if (ret)
- return 0;
- return IIO_VAL_INT_PLUS_MICRO;
- }
- default:
- return -EINVAL;
- }
- }
- static int bh1745_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long mask)
- {
- struct bh1745_data *data = iio_priv(indio_dev);
- guard(mutex)(&data->lock);
- switch (mask) {
- case IIO_CHAN_INFO_SCALE:
- return bh1745_set_scale(data, val);
- case IIO_CHAN_INFO_INT_TIME:
- return bh1745_set_int_time(data, val, val2);
- default:
- return -EINVAL;
- }
- }
- static int bh1745_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;
- case IIO_CHAN_INFO_INT_TIME:
- return IIO_VAL_INT_PLUS_MICRO;
- default:
- return -EINVAL;
- }
- }
- static int bh1745_read_thresh(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- enum iio_event_type type,
- enum iio_event_direction dir,
- enum iio_event_info info, int *val, int *val2)
- {
- struct bh1745_data *data = iio_priv(indio_dev);
- int ret;
- switch (info) {
- case IIO_EV_INFO_VALUE:
- switch (dir) {
- case IIO_EV_DIR_RISING:
- ret = regmap_bulk_read(data->regmap, BH1745_TH_LSB,
- val, 2);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- case IIO_EV_DIR_FALLING:
- ret = regmap_bulk_read(data->regmap, BH1745_TL_LSB,
- val, 2);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- case IIO_EV_INFO_PERIOD:
- ret = regmap_read(data->regmap, BH1745_PERSISTENCE, val);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- }
- static int bh1745_write_thresh(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- enum iio_event_type type,
- enum iio_event_direction dir,
- enum iio_event_info info, int val, int val2)
- {
- struct bh1745_data *data = iio_priv(indio_dev);
- int ret;
- switch (info) {
- case IIO_EV_INFO_VALUE:
- if (val < 0x0 || val > 0xFFFF)
- return -EINVAL;
- switch (dir) {
- case IIO_EV_DIR_RISING:
- ret = regmap_bulk_write(data->regmap, BH1745_TH_LSB,
- &val, 2);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- case IIO_EV_DIR_FALLING:
- ret = regmap_bulk_write(data->regmap, BH1745_TL_LSB,
- &val, 2);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- case IIO_EV_INFO_PERIOD:
- if (val < BH1745_PRESISTENCE_UPDATE_TOGGLE ||
- val > BH1745_PRESISTENCE_UPDATE_EIGHT_MEASUREMENT)
- return -EINVAL;
- ret = regmap_write(data->regmap, BH1745_PERSISTENCE, val);
- if (ret)
- return ret;
- return IIO_VAL_INT;
- default:
- return -EINVAL;
- }
- }
- static int bh1745_read_event_config(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- enum iio_event_type type,
- enum iio_event_direction dir)
- {
- struct bh1745_data *data = iio_priv(indio_dev);
- int ret;
- int value;
- int int_src;
- ret = regmap_read(data->regmap, BH1745_INTR, &value);
- if (ret)
- return ret;
- if (!FIELD_GET(BH1745_INTR_ENABLE, value))
- return 0;
- int_src = FIELD_GET(BH1745_INTR_SOURCE_MASK, value);
- switch (chan->channel2) {
- case IIO_MOD_LIGHT_RED:
- if (int_src == BH1745_INTR_SOURCE_RED)
- return 1;
- return 0;
- case IIO_MOD_LIGHT_GREEN:
- if (int_src == BH1745_INTR_SOURCE_GREEN)
- return 1;
- return 0;
- case IIO_MOD_LIGHT_BLUE:
- if (int_src == BH1745_INTR_SOURCE_BLUE)
- return 1;
- return 0;
- case IIO_MOD_LIGHT_CLEAR:
- if (int_src == BH1745_INTR_SOURCE_CLEAR)
- return 1;
- return 0;
- default:
- return -EINVAL;
- }
- }
- static int bh1745_write_event_config(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- enum iio_event_type type,
- enum iio_event_direction dir, int state)
- {
- struct bh1745_data *data = iio_priv(indio_dev);
- int value;
- if (state == 0)
- return regmap_clear_bits(data->regmap,
- BH1745_INTR, BH1745_INTR_ENABLE);
- if (state == 1) {
- /* Latch is always enabled when enabling interrupt */
- value = BH1745_INTR_ENABLE;
- switch (chan->channel2) {
- case IIO_MOD_LIGHT_RED:
- return regmap_write(data->regmap, BH1745_INTR,
- value | FIELD_PREP(BH1745_INTR_SOURCE_MASK,
- BH1745_INTR_SOURCE_RED));
- case IIO_MOD_LIGHT_GREEN:
- return regmap_write(data->regmap, BH1745_INTR,
- value | FIELD_PREP(BH1745_INTR_SOURCE_MASK,
- BH1745_INTR_SOURCE_GREEN));
- case IIO_MOD_LIGHT_BLUE:
- return regmap_write(data->regmap, BH1745_INTR,
- value | FIELD_PREP(BH1745_INTR_SOURCE_MASK,
- BH1745_INTR_SOURCE_BLUE));
- case IIO_MOD_LIGHT_CLEAR:
- return regmap_write(data->regmap, BH1745_INTR,
- value | FIELD_PREP(BH1745_INTR_SOURCE_MASK,
- BH1745_INTR_SOURCE_CLEAR));
- default:
- return -EINVAL;
- }
- }
- return -EINVAL;
- }
- static int bh1745_read_avail(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, const int **vals,
- int *type, int *length, long mask)
- {
- struct bh1745_data *data = iio_priv(indio_dev);
- switch (mask) {
- case IIO_CHAN_INFO_INT_TIME:
- return iio_gts_avail_times(&data->gts, vals, type, length);
- case IIO_CHAN_INFO_SCALE:
- return iio_gts_all_avail_scales(&data->gts, vals, type, length);
- default:
- return -EINVAL;
- }
- }
- static const struct iio_info bh1745_info = {
- .read_raw = bh1745_read_raw,
- .write_raw = bh1745_write_raw,
- .write_raw_get_fmt = bh1745_write_raw_get_fmt,
- .read_event_value = bh1745_read_thresh,
- .write_event_value = bh1745_write_thresh,
- .read_event_config = bh1745_read_event_config,
- .write_event_config = bh1745_write_event_config,
- .read_avail = bh1745_read_avail,
- };
- static irqreturn_t bh1745_interrupt_handler(int interrupt, void *p)
- {
- struct iio_dev *indio_dev = p;
- struct bh1745_data *data = iio_priv(indio_dev);
- int ret;
- int value;
- int int_src;
- ret = regmap_read(data->regmap, BH1745_INTR, &value);
- if (ret)
- return IRQ_NONE;
- int_src = FIELD_GET(BH1745_INTR_SOURCE_MASK, value);
- if (value & BH1745_INTR_STATUS) {
- iio_push_event(indio_dev,
- IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, int_src,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_EITHER),
- iio_get_time_ns(indio_dev));
- return IRQ_HANDLED;
- }
- return IRQ_NONE;
- }
- static irqreturn_t bh1745_trigger_handler(int interrupt, void *p)
- {
- struct iio_poll_func *pf = p;
- struct iio_dev *indio_dev = pf->indio_dev;
- struct bh1745_data *data = iio_priv(indio_dev);
- struct {
- u16 chans[4];
- s64 timestamp __aligned(8);
- } scan;
- u16 value;
- int ret;
- int i;
- int j = 0;
- memset(&scan, 0, sizeof(scan));
- iio_for_each_active_channel(indio_dev, i) {
- ret = regmap_bulk_read(data->regmap, BH1745_RED_LSB + 2 * i,
- &value, 2);
- if (ret)
- goto err;
- scan.chans[j++] = value;
- }
- iio_push_to_buffers_with_timestamp(indio_dev, &scan,
- iio_get_time_ns(indio_dev));
- err:
- iio_trigger_notify_done(indio_dev->trig);
- return IRQ_HANDLED;
- }
- static int bh1745_setup_triggered_buffer(struct iio_dev *indio_dev,
- struct device *parent,
- int irq)
- {
- struct bh1745_data *data = iio_priv(indio_dev);
- struct device *dev = data->dev;
- int ret;
- ret = devm_iio_triggered_buffer_setup(parent, indio_dev, NULL,
- bh1745_trigger_handler, NULL);
- if (ret)
- return dev_err_probe(dev, ret,
- "Triggered buffer setup failed\n");
- if (irq) {
- ret = devm_request_threaded_irq(dev, irq, NULL,
- bh1745_interrupt_handler,
- IRQF_ONESHOT,
- "bh1745_interrupt", indio_dev);
- if (ret)
- return dev_err_probe(dev, ret,
- "Request for IRQ failed\n");
- }
- return 0;
- }
- static int bh1745_init(struct bh1745_data *data)
- {
- int ret;
- struct device *dev = data->dev;
- mutex_init(&data->lock);
- ret = devm_iio_init_iio_gts(dev, BH1745_MAX_GAIN, 0, bh1745_gain,
- ARRAY_SIZE(bh1745_gain), bh1745_itimes,
- ARRAY_SIZE(bh1745_itimes), &data->gts);
- if (ret)
- return ret;
- ret = bh1745_reset(data);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to reset sensor\n");
- ret = bh1745_power_on(data);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to turn on sensor\n");
- ret = devm_add_action_or_reset(dev, bh1745_power_off, data);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to add action or reset\n");
- return 0;
- }
- static int bh1745_probe(struct i2c_client *client)
- {
- int ret;
- int value;
- int part_id;
- struct bh1745_data *data;
- struct iio_dev *indio_dev;
- struct device *dev = &client->dev;
- indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
- if (!indio_dev)
- return -ENOMEM;
- indio_dev->info = &bh1745_info;
- indio_dev->name = "bh1745";
- indio_dev->channels = bh1745_channels;
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->num_channels = ARRAY_SIZE(bh1745_channels);
- data = iio_priv(indio_dev);
- data->dev = &client->dev;
- data->regmap = devm_regmap_init_i2c(client, &bh1745_regmap);
- if (IS_ERR(data->regmap))
- return dev_err_probe(dev, PTR_ERR(data->regmap),
- "Failed to initialize Regmap\n");
- ret = regmap_read(data->regmap, BH1745_SYS_CTRL, &value);
- if (ret)
- return ret;
- part_id = FIELD_GET(BH1745_SYS_CTRL_PART_ID_MASK, value);
- if (part_id != BH1745_PART_ID)
- dev_warn(dev, "Unknown part ID 0x%x\n", part_id);
- ret = devm_regulator_get_enable(dev, "vdd");
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to get and enable regulator\n");
- ret = bh1745_init(data);
- if (ret)
- return ret;
- ret = bh1745_setup_triggered_buffer(indio_dev, indio_dev->dev.parent,
- client->irq);
- if (ret)
- return ret;
- ret = devm_iio_device_register(dev, indio_dev);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to register device\n");
- return 0;
- }
- static const struct i2c_device_id bh1745_idtable[] = {
- { "bh1745" },
- { }
- };
- MODULE_DEVICE_TABLE(i2c, bh1745_idtable);
- static const struct of_device_id bh1745_of_match[] = {
- { .compatible = "rohm,bh1745" },
- { }
- };
- MODULE_DEVICE_TABLE(of, bh1745_of_match);
- static struct i2c_driver bh1745_driver = {
- .driver = {
- .name = "bh1745",
- .of_match_table = bh1745_of_match,
- },
- .probe = bh1745_probe,
- .id_table = bh1745_idtable,
- };
- module_i2c_driver(bh1745_driver);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Mudit Sharma <muditsharma.info@gmail.com>");
- MODULE_DESCRIPTION("BH1745 colour sensor driver");
- MODULE_IMPORT_NS(IIO_GTS_HELPER);
|