| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * opt3001.c - Texas Instruments OPT3001 Light Sensor | |
| * | |
| * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com | |
| * | |
| * Author: Andreas Dannenberg <dannenberg@ti.com> | |
| * Based on previous work from: Felipe Balbi <balbi@ti.com> | |
| */ | |
| #include <linux/array_size.h> | |
| #include <linux/bits.h> | |
| #include <linux/cleanup.h> | |
| #include <linux/delay.h> | |
| #include <linux/dev_printk.h> | |
| #include <linux/errno.h> | |
| #include <linux/i2c.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/jiffies.h> | |
| #include <linux/module.h> | |
| #include <linux/mutex.h> | |
| #include <linux/types.h> | |
| #include <linux/wait.h> | |
| #include <linux/iio/events.h> | |
| #include <linux/iio/iio.h> | |
| #include <linux/iio/sysfs.h> | |
| #define OPT3001_RESULT 0x00 | |
| #define OPT3001_CONFIGURATION 0x01 | |
| #define OPT3001_LOW_LIMIT 0x02 | |
| #define OPT3001_HIGH_LIMIT 0x03 | |
| #define OPT3001_MANUFACTURER_ID 0x7e | |
| #define OPT3001_DEVICE_ID 0x7f | |
| #define OPT3001_CONFIGURATION_RN_MASK GENMASK(15, 12) | |
| #define OPT3001_CONFIGURATION_RN_AUTO (0xc << 12) | |
| #define OPT3001_CONFIGURATION_CT BIT(11) | |
| #define OPT3001_CONFIGURATION_M_MASK GENMASK(10, 9) | |
| #define OPT3001_CONFIGURATION_M_SHUTDOWN (0 << 9) | |
| #define OPT3001_CONFIGURATION_M_SINGLE (1 << 9) | |
| #define OPT3001_CONFIGURATION_M_CONTINUOUS (2 << 9) /* also 3 << 9 */ | |
| #define OPT3001_CONFIGURATION_CRF BIT(7) | |
| #define OPT3001_CONFIGURATION_FH BIT(6) | |
| #define OPT3001_CONFIGURATION_FL BIT(5) | |
| #define OPT3001_CONFIGURATION_L BIT(4) | |
| #define OPT3001_CONFIGURATION_POL BIT(3) | |
| #define OPT3001_CONFIGURATION_ME BIT(2) | |
| #define OPT3001_CONFIGURATION_FC_MASK GENMASK(1, 0) | |
| /* The end-of-conversion enable is located in the low-limit register */ | |
| #define OPT3001_LOW_LIMIT_EOC_ENABLE 0xc000 | |
| #define OPT3001_REG_EXPONENT(n) ((n) >> 12) | |
| #define OPT3001_REG_MANTISSA(n) ((n) & 0xfff) | |
| #define OPT3001_INT_TIME_LONG 800000 | |
| #define OPT3001_INT_TIME_SHORT 100000 | |
| /* | |
| * Time to wait for conversion result to be ready. The device datasheet | |
| * sect. 6.5 states results are ready after total integration time plus 3ms. | |
| * This results in worst-case max values of 113ms or 883ms, respectively. | |
| * Add some slack to be on the safe side. | |
| */ | |
| #define OPT3001_RESULT_READY_SHORT 150 | |
| #define OPT3001_RESULT_READY_LONG 1000 | |
| struct opt3001_scale { | |
| int val; | |
| int val2; | |
| }; | |
| struct opt3001_chip_info { | |
| const struct iio_chan_spec (*channels)[2]; | |
| enum iio_chan_type chan_type; | |
| int num_channels; | |
| const struct opt3001_scale (*scales)[12]; | |
| /* | |
| * Factor as specified by conversion equation in datasheet. | |
| * eg. 0.01 (scaled to integer 10) for opt3001. | |
| */ | |
| int factor_whole; | |
| /* | |
| * Factor to compensate for potentially scaled factor_whole. | |
| */ | |
| int factor_integer; | |
| /* | |
| * Factor used to align decimal part of processed value to six decimal | |
| * places. | |
| */ | |
| int factor_decimal; | |
| bool has_id; | |
| }; | |
| struct opt3001 { | |
| struct i2c_client *client; | |
| /* | |
| * Ensure data capture and read-modify-write sequences are | |
| * not interrupted. | |
| */ | |
| struct mutex lock; | |
| /* Allows for IRQs to bypass locking mechanism */ | |
| bool ok_to_ignore_lock; | |
| bool result_ready; | |
| wait_queue_head_t result_ready_queue; | |
| u16 result; | |
| const struct opt3001_chip_info *chip_info; | |
| u32 int_time; | |
| u32 mode; | |
| u16 high_thresh_mantissa; | |
| u16 low_thresh_mantissa; | |
| u8 high_thresh_exp; | |
| u8 low_thresh_exp; | |
| bool use_irq; | |
| }; | |
| static const struct opt3001_scale opt3001_scales[] = { | |
| { | |
| .val = 40, | |
| .val2 = 950000, | |
| }, | |
| { | |
| .val = 81, | |
| .val2 = 900000, | |
| }, | |
| { | |
| .val = 163, | |
| .val2 = 800000, | |
| }, | |
| { | |
| .val = 327, | |
| .val2 = 600000, | |
| }, | |
| { | |
| .val = 655, | |
| .val2 = 200000, | |
| }, | |
| { | |
| .val = 1310, | |
| .val2 = 400000, | |
| }, | |
| { | |
| .val = 2620, | |
| .val2 = 800000, | |
| }, | |
| { | |
| .val = 5241, | |
| .val2 = 600000, | |
| }, | |
| { | |
| .val = 10483, | |
| .val2 = 200000, | |
| }, | |
| { | |
| .val = 20966, | |
| .val2 = 400000, | |
| }, | |
| { | |
| .val = 41932, | |
| .val2 = 800000, | |
| }, | |
| { | |
| .val = 83865, | |
| .val2 = 600000, | |
| }, | |
| }; | |
| static const struct opt3001_scale opt3002_scales[] = { | |
| { | |
| .val = 4914, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 9828, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 19656, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 39312, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 78624, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 157248, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 314496, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 628992, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 1257984, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 2515968, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 5031936, | |
| .val2 = 0, | |
| }, | |
| { | |
| .val = 10063872, | |
| .val2 = 0, | |
| }, | |
| }; | |
| static int opt3001_find_scale(const struct opt3001 *opt, int val, int val2, | |
| u8 *exponent) | |
| { | |
| for (unsigned int i = 0; i < ARRAY_SIZE(*opt->chip_info->scales); i++) { | |
| const struct opt3001_scale *scale = &(*opt->chip_info->scales)[i]; | |
| /* | |
| * Compare the integer and micro parts to determine value scale. | |
| */ | |
| if (val < scale->val || | |
| (val == scale->val && val2 <= scale->val2)) { | |
| *exponent = i; | |
| return 0; | |
| } | |
| } | |
| return -EINVAL; | |
| } | |
| static void opt3001_to_iio_ret(struct opt3001 *opt, u8 exponent, u16 mantissa, | |
| int *val, int *val2) | |
| { | |
| int ret; | |
| int whole = opt->chip_info->factor_whole; | |
| int integer = opt->chip_info->factor_integer; | |
| int decimal = opt->chip_info->factor_decimal; | |
| ret = whole * (mantissa << exponent); | |
| *val = ret / integer; | |
| *val2 = (ret - (*val * integer)) * decimal; | |
| } | |
| static void opt3001_set_mode(struct opt3001 *opt, u16 *reg, u16 mode) | |
| { | |
| *reg &= ~OPT3001_CONFIGURATION_M_MASK; | |
| *reg |= mode; | |
| opt->mode = mode; | |
| } | |
| static IIO_CONST_ATTR_INT_TIME_AVAIL("0.1 0.8"); | |
| static struct attribute *opt3001_attributes[] = { | |
| &iio_const_attr_integration_time_available.dev_attr.attr, | |
| NULL | |
| }; | |
| static const struct attribute_group opt3001_attribute_group = { | |
| .attrs = opt3001_attributes, | |
| }; | |
| static const struct iio_event_spec opt3001_event_spec[] = { | |
| { | |
| .type = IIO_EV_TYPE_THRESH, | |
| .dir = IIO_EV_DIR_RISING, | |
| .mask_separate = BIT(IIO_EV_INFO_VALUE) | | |
| BIT(IIO_EV_INFO_ENABLE), | |
| }, | |
| { | |
| .type = IIO_EV_TYPE_THRESH, | |
| .dir = IIO_EV_DIR_FALLING, | |
| .mask_separate = BIT(IIO_EV_INFO_VALUE) | | |
| BIT(IIO_EV_INFO_ENABLE), | |
| }, | |
| }; | |
| static const struct iio_chan_spec opt3001_channels[] = { | |
| { | |
| .type = IIO_LIGHT, | |
| .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | | |
| BIT(IIO_CHAN_INFO_INT_TIME), | |
| .event_spec = opt3001_event_spec, | |
| .num_event_specs = ARRAY_SIZE(opt3001_event_spec), | |
| }, | |
| IIO_CHAN_SOFT_TIMESTAMP(1), | |
| }; | |
| static const struct iio_chan_spec opt3002_channels[] = { | |
| { | |
| .type = IIO_INTENSITY, | |
| .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
| BIT(IIO_CHAN_INFO_INT_TIME), | |
| .event_spec = opt3001_event_spec, | |
| .num_event_specs = ARRAY_SIZE(opt3001_event_spec), | |
| }, | |
| IIO_CHAN_SOFT_TIMESTAMP(1), | |
| }; | |
| static int opt3001_start_conversion(struct opt3001 *opt) | |
| { | |
| struct i2c_client *client = opt->client; | |
| struct device *dev = &client->dev; | |
| u16 reg; | |
| int ret; | |
| /* Reset data-ready indicator flag */ | |
| opt->result_ready = false; | |
| /* Configure for single-conversion mode and start a new conversion */ | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to read register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| return ret; | |
| } | |
| reg = ret; | |
| opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SINGLE); | |
| ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); | |
| if (ret) | |
| dev_err(dev, "failed to write register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| return ret; | |
| } | |
| static int opt3001_get_processed_irq(struct opt3001 *opt) | |
| { | |
| struct i2c_client *client = opt->client; | |
| struct device *dev = &client->dev; | |
| u16 value; | |
| int ret; | |
| /* | |
| * Enable the end-of-conversion interrupt mechanism. Note that doing so | |
| * will overwrite the low-level limit value however we will restore this | |
| * value later on. | |
| */ | |
| ret = i2c_smbus_write_word_swapped(client, | |
| OPT3001_LOW_LIMIT, | |
| OPT3001_LOW_LIMIT_EOC_ENABLE); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to write register %02x\n", | |
| OPT3001_LOW_LIMIT); | |
| return ret; | |
| } | |
| /* Allow IRQ to access the device despite lock being set */ | |
| opt->ok_to_ignore_lock = true; | |
| ret = opt3001_start_conversion(opt); | |
| if (ret) | |
| goto err; | |
| if (wait_event_timeout(opt->result_ready_queue, opt->result_ready, | |
| msecs_to_jiffies(OPT3001_RESULT_READY_LONG))) | |
| ret = 0; | |
| else | |
| ret = -ETIMEDOUT; | |
| err: | |
| opt->ok_to_ignore_lock = false; | |
| if (ret < 0) | |
| return ret; | |
| /* | |
| * Disable the end-of-conversion interrupt mechanism by restoring the | |
| * low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note | |
| * that selectively clearing those enable bits would affect the actual | |
| * limit value due to bit-overlap and therefore can't be done. | |
| */ | |
| value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa; | |
| ret = i2c_smbus_write_word_swapped(client, OPT3001_LOW_LIMIT, value); | |
| if (ret) | |
| dev_err(dev, "failed to write register %02x\n", | |
| OPT3001_LOW_LIMIT); | |
| return ret; | |
| } | |
| static int opt3001_get_processed_noirq(struct opt3001 *opt) | |
| { | |
| struct i2c_client *client = opt->client; | |
| struct device *dev = &client->dev; | |
| int ret; | |
| ret = opt3001_start_conversion(opt); | |
| if (ret) | |
| return ret; | |
| if (opt->int_time == OPT3001_INT_TIME_SHORT) | |
| msleep(OPT3001_RESULT_READY_SHORT); | |
| else | |
| msleep(OPT3001_RESULT_READY_LONG); | |
| /* Check result ready flag */ | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to read register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| return ret; | |
| } | |
| if (!(ret & OPT3001_CONFIGURATION_CRF)) | |
| return -ETIMEDOUT; | |
| /* Obtain value */ | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to read register %02x\n", | |
| OPT3001_RESULT); | |
| return ret; | |
| } | |
| opt->result = ret; | |
| opt->result_ready = true; | |
| return 0; | |
| } | |
| static int opt3001_get_processed(struct opt3001 *opt, int *val, int *val2) | |
| { | |
| u16 mantissa; | |
| u8 exponent; | |
| int ret; | |
| if (opt->use_irq) | |
| ret = opt3001_get_processed_irq(opt); | |
| else | |
| ret = opt3001_get_processed_noirq(opt); | |
| if (ret) | |
| return ret; | |
| exponent = OPT3001_REG_EXPONENT(opt->result); | |
| mantissa = OPT3001_REG_MANTISSA(opt->result); | |
| opt3001_to_iio_ret(opt, exponent, mantissa, val, val2); | |
| return IIO_VAL_INT_PLUS_MICRO; | |
| } | |
| static int opt3001_get_int_time(struct opt3001 *opt, int *val, int *val2) | |
| { | |
| *val = 0; | |
| *val2 = opt->int_time; | |
| return IIO_VAL_INT_PLUS_MICRO; | |
| } | |
| static int opt3001_set_int_time(struct opt3001 *opt, int time) | |
| { | |
| struct i2c_client *client = opt->client; | |
| struct device *dev = &client->dev; | |
| int ret; | |
| u16 reg; | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to read register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| return ret; | |
| } | |
| reg = ret; | |
| switch (time) { | |
| case OPT3001_INT_TIME_SHORT: | |
| reg &= ~OPT3001_CONFIGURATION_CT; | |
| opt->int_time = OPT3001_INT_TIME_SHORT; | |
| break; | |
| case OPT3001_INT_TIME_LONG: | |
| reg |= OPT3001_CONFIGURATION_CT; | |
| opt->int_time = OPT3001_INT_TIME_LONG; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| return i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); | |
| } | |
| static int opt3001_read_raw(struct iio_dev *iio, | |
| struct iio_chan_spec const *chan, | |
| int *val, int *val2, long mask) | |
| { | |
| struct opt3001 *opt = iio_priv(iio); | |
| if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) | |
| return -EBUSY; | |
| if (chan->type != opt->chip_info->chan_type) | |
| return -EINVAL; | |
| guard(mutex)(&opt->lock); | |
| switch (mask) { | |
| case IIO_CHAN_INFO_RAW: | |
| case IIO_CHAN_INFO_PROCESSED: | |
| return opt3001_get_processed(opt, val, val2); | |
| case IIO_CHAN_INFO_INT_TIME: | |
| return opt3001_get_int_time(opt, val, val2); | |
| default: | |
| return -EINVAL; | |
| } | |
| } | |
| static int opt3001_write_raw(struct iio_dev *iio, | |
| struct iio_chan_spec const *chan, | |
| int val, int val2, long mask) | |
| { | |
| struct opt3001 *opt = iio_priv(iio); | |
| if (opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) | |
| return -EBUSY; | |
| if (chan->type != opt->chip_info->chan_type) | |
| return -EINVAL; | |
| if (mask != IIO_CHAN_INFO_INT_TIME) | |
| return -EINVAL; | |
| if (val != 0) | |
| return -EINVAL; | |
| guard(mutex)(&opt->lock); | |
| return opt3001_set_int_time(opt, val2); | |
| } | |
| static int opt3001_read_event_value(struct iio_dev *iio, | |
| 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 opt3001 *opt = iio_priv(iio); | |
| guard(mutex)(&opt->lock); | |
| switch (dir) { | |
| case IIO_EV_DIR_RISING: | |
| opt3001_to_iio_ret(opt, opt->high_thresh_exp, | |
| opt->high_thresh_mantissa, val, val2); | |
| return IIO_VAL_INT_PLUS_MICRO; | |
| case IIO_EV_DIR_FALLING: | |
| opt3001_to_iio_ret(opt, opt->low_thresh_exp, | |
| opt->low_thresh_mantissa, val, val2); | |
| return IIO_VAL_INT_PLUS_MICRO; | |
| default: | |
| return -EINVAL; | |
| } | |
| } | |
| static int opt3001_write_event_value(struct iio_dev *iio, | |
| 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 opt3001 *opt = iio_priv(iio); | |
| struct i2c_client *client = opt->client; | |
| struct device *dev = &client->dev; | |
| int ret; | |
| int whole; | |
| int integer; | |
| int decimal; | |
| u16 mantissa; | |
| u16 value; | |
| u16 reg; | |
| u8 exponent; | |
| if (val < 0) | |
| return -EINVAL; | |
| guard(mutex)(&opt->lock); | |
| ret = opt3001_find_scale(opt, val, val2, &exponent); | |
| if (ret < 0) { | |
| dev_err(dev, "can't find scale for %d.%06u\n", val, val2); | |
| return ret; | |
| } | |
| whole = opt->chip_info->factor_whole; | |
| integer = opt->chip_info->factor_integer; | |
| decimal = opt->chip_info->factor_decimal; | |
| mantissa = (((val * integer) + (val2 / decimal)) / whole) >> exponent; | |
| value = (exponent << 12) | mantissa; | |
| switch (dir) { | |
| case IIO_EV_DIR_RISING: | |
| reg = OPT3001_HIGH_LIMIT; | |
| opt->high_thresh_mantissa = mantissa; | |
| opt->high_thresh_exp = exponent; | |
| break; | |
| case IIO_EV_DIR_FALLING: | |
| reg = OPT3001_LOW_LIMIT; | |
| opt->low_thresh_mantissa = mantissa; | |
| opt->low_thresh_exp = exponent; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| ret = i2c_smbus_write_word_swapped(client, reg, value); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to write register %02x\n", reg); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| static int opt3001_read_event_config(struct iio_dev *iio, | |
| const struct iio_chan_spec *chan, | |
| enum iio_event_type type, | |
| enum iio_event_direction dir) | |
| { | |
| struct opt3001 *opt = iio_priv(iio); | |
| return opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS; | |
| } | |
| static int opt3001_write_event_config(struct iio_dev *iio, | |
| const struct iio_chan_spec *chan, | |
| enum iio_event_type type, | |
| enum iio_event_direction dir, | |
| bool state) | |
| { | |
| struct opt3001 *opt = iio_priv(iio); | |
| struct i2c_client *client = opt->client; | |
| struct device *dev = &client->dev; | |
| int ret; | |
| u16 mode; | |
| u16 reg; | |
| if (state && opt->mode == OPT3001_CONFIGURATION_M_CONTINUOUS) | |
| return 0; | |
| if (!state && opt->mode == OPT3001_CONFIGURATION_M_SHUTDOWN) | |
| return 0; | |
| guard(mutex)(&opt->lock); | |
| mode = state ? OPT3001_CONFIGURATION_M_CONTINUOUS | |
| : OPT3001_CONFIGURATION_M_SHUTDOWN; | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to read register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| return ret; | |
| } | |
| reg = ret; | |
| opt3001_set_mode(opt, ®, mode); | |
| ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to write register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| static const struct iio_info opt3001_info = { | |
| .attrs = &opt3001_attribute_group, | |
| .read_raw = opt3001_read_raw, | |
| .write_raw = opt3001_write_raw, | |
| .read_event_value = opt3001_read_event_value, | |
| .write_event_value = opt3001_write_event_value, | |
| .read_event_config = opt3001_read_event_config, | |
| .write_event_config = opt3001_write_event_config, | |
| }; | |
| static int opt3001_read_id(struct opt3001 *opt) | |
| { | |
| struct i2c_client *client = opt->client; | |
| struct device *dev = &client->dev; | |
| char manufacturer[2]; | |
| u16 device_id; | |
| int ret; | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_MANUFACTURER_ID); | |
| if (ret < 0) | |
| return dev_err_probe(dev, ret, "failed to read register %02x\n", | |
| OPT3001_MANUFACTURER_ID); | |
| manufacturer[0] = ret >> 8; | |
| manufacturer[1] = ret & 0xff; | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_DEVICE_ID); | |
| if (ret < 0) | |
| return dev_err_probe(dev, ret, "failed to read register %02x\n", | |
| OPT3001_DEVICE_ID); | |
| device_id = ret; | |
| dev_info(dev, "Found %c%c OPT%04x\n", manufacturer[0], manufacturer[1], | |
| device_id); | |
| return 0; | |
| } | |
| static void opt3001_power_off(void *data) | |
| { | |
| struct opt3001 *opt = data; | |
| struct i2c_client *client = opt->client; | |
| struct device *dev = &client->dev; | |
| u16 reg_val; | |
| int ret; | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to read register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| return; | |
| } | |
| reg_val = ret; | |
| opt3001_set_mode(opt, ®_val, OPT3001_CONFIGURATION_M_SHUTDOWN); | |
| ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg_val); | |
| if (ret < 0) | |
| dev_err(dev, "failed to write to register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| } | |
| static int opt3001_configure(struct opt3001 *opt) | |
| { | |
| struct i2c_client *client = opt->client; | |
| struct device *dev = &client->dev; | |
| int ret; | |
| u16 reg; | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); | |
| if (ret < 0) | |
| return dev_err_probe(dev, ret, "failed to read register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| reg = ret; | |
| /* Enable automatic full-scale setting mode */ | |
| reg &= ~OPT3001_CONFIGURATION_RN_MASK; | |
| reg |= OPT3001_CONFIGURATION_RN_AUTO; | |
| /* Reflect status of the device's integration time setting */ | |
| if (reg & OPT3001_CONFIGURATION_CT) | |
| opt->int_time = OPT3001_INT_TIME_LONG; | |
| else | |
| opt->int_time = OPT3001_INT_TIME_SHORT; | |
| /* Ensure device is in shutdown initially */ | |
| opt3001_set_mode(opt, ®, OPT3001_CONFIGURATION_M_SHUTDOWN); | |
| /* Configure for latched window-style comparison operation */ | |
| reg |= OPT3001_CONFIGURATION_L; | |
| reg &= ~OPT3001_CONFIGURATION_POL; | |
| reg &= ~OPT3001_CONFIGURATION_ME; | |
| reg &= ~OPT3001_CONFIGURATION_FC_MASK; | |
| ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION, reg); | |
| if (ret < 0) | |
| return dev_err_probe(dev, ret, "failed to write register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| ret = devm_add_action_or_reset(dev, opt3001_power_off, opt); | |
| if (ret) | |
| return dev_err_probe(dev, ret, | |
| "failed to register power off function\n"); | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_LOW_LIMIT); | |
| if (ret < 0) | |
| return dev_err_probe(dev, ret, "failed to read register %02x\n", | |
| OPT3001_LOW_LIMIT); | |
| opt->low_thresh_mantissa = OPT3001_REG_MANTISSA(ret); | |
| opt->low_thresh_exp = OPT3001_REG_EXPONENT(ret); | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_HIGH_LIMIT); | |
| if (ret < 0) | |
| return dev_err_probe(dev, ret, "failed to read register %02x\n", | |
| OPT3001_HIGH_LIMIT); | |
| opt->high_thresh_mantissa = OPT3001_REG_MANTISSA(ret); | |
| opt->high_thresh_exp = OPT3001_REG_EXPONENT(ret); | |
| return 0; | |
| } | |
| static irqreturn_t opt3001_irq(int irq, void *_iio) | |
| { | |
| struct iio_dev *iio = _iio; | |
| struct opt3001 *opt = iio_priv(iio); | |
| struct i2c_client *client = opt->client; | |
| struct device *dev = &client->dev; | |
| int ret; | |
| bool wake_result_ready_queue = false; | |
| enum iio_chan_type chan_type = opt->chip_info->chan_type; | |
| bool ok_to_ignore_lock = opt->ok_to_ignore_lock; | |
| if (!ok_to_ignore_lock) | |
| mutex_lock(&opt->lock); | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_CONFIGURATION); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to read register %02x\n", | |
| OPT3001_CONFIGURATION); | |
| goto out; | |
| } | |
| if ((ret & OPT3001_CONFIGURATION_M_MASK) == | |
| OPT3001_CONFIGURATION_M_CONTINUOUS) { | |
| if (ret & OPT3001_CONFIGURATION_FH) | |
| iio_push_event(iio, | |
| IIO_UNMOD_EVENT_CODE(chan_type, 0, | |
| IIO_EV_TYPE_THRESH, | |
| IIO_EV_DIR_RISING), | |
| iio_get_time_ns(iio)); | |
| if (ret & OPT3001_CONFIGURATION_FL) | |
| iio_push_event(iio, | |
| IIO_UNMOD_EVENT_CODE(chan_type, 0, | |
| IIO_EV_TYPE_THRESH, | |
| IIO_EV_DIR_FALLING), | |
| iio_get_time_ns(iio)); | |
| } else if (ret & OPT3001_CONFIGURATION_CRF) { | |
| ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT); | |
| if (ret < 0) { | |
| dev_err(dev, "failed to read register %02x\n", | |
| OPT3001_RESULT); | |
| goto out; | |
| } | |
| opt->result = ret; | |
| opt->result_ready = true; | |
| wake_result_ready_queue = true; | |
| } | |
| out: | |
| if (!ok_to_ignore_lock) | |
| mutex_unlock(&opt->lock); | |
| if (wake_result_ready_queue) | |
| wake_up(&opt->result_ready_queue); | |
| return IRQ_HANDLED; | |
| } | |
| static int opt3001_probe(struct i2c_client *client) | |
| { | |
| struct device *dev = &client->dev; | |
| struct iio_dev *iio; | |
| struct opt3001 *opt; | |
| int irq = client->irq; | |
| int ret; | |
| iio = devm_iio_device_alloc(dev, sizeof(*opt)); | |
| if (!iio) | |
| return -ENOMEM; | |
| opt = iio_priv(iio); | |
| opt->client = client; | |
| opt->chip_info = i2c_get_match_data(client); | |
| ret = devm_mutex_init(dev, &opt->lock); | |
| if (ret) | |
| return ret; | |
| init_waitqueue_head(&opt->result_ready_queue); | |
| if (opt->chip_info->has_id) { | |
| ret = opt3001_read_id(opt); | |
| if (ret) | |
| return ret; | |
| } | |
| ret = opt3001_configure(opt); | |
| if (ret) | |
| return ret; | |
| iio->name = client->name; | |
| iio->channels = *opt->chip_info->channels; | |
| iio->num_channels = opt->chip_info->num_channels; | |
| iio->modes = INDIO_DIRECT_MODE; | |
| iio->info = &opt3001_info; | |
| /* Make use of INT pin only if valid IRQ no. is given */ | |
| if (irq > 0) { | |
| ret = devm_request_threaded_irq(dev, irq, NULL, opt3001_irq, | |
| IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | |
| "opt3001", iio); | |
| if (ret) | |
| return ret; | |
| opt->use_irq = true; | |
| } else { | |
| dev_dbg(dev, "enabling interrupt-less operation\n"); | |
| } | |
| return devm_iio_device_register(dev, iio); | |
| } | |
| static const struct opt3001_chip_info opt3001_chip_information = { | |
| .channels = &opt3001_channels, | |
| .chan_type = IIO_LIGHT, | |
| .num_channels = ARRAY_SIZE(opt3001_channels), | |
| .scales = &opt3001_scales, | |
| .factor_whole = 10, | |
| .factor_integer = 1000, | |
| .factor_decimal = 1000, | |
| .has_id = true, | |
| }; | |
| static const struct opt3001_chip_info opt3002_chip_information = { | |
| .channels = &opt3002_channels, | |
| .chan_type = IIO_INTENSITY, | |
| .num_channels = ARRAY_SIZE(opt3002_channels), | |
| .scales = &opt3002_scales, | |
| .factor_whole = 12, | |
| .factor_integer = 10, | |
| .factor_decimal = 100000, | |
| .has_id = false, | |
| }; | |
| static const struct i2c_device_id opt3001_id[] = { | |
| { .name = "opt3001", .driver_data = (kernel_ulong_t)&opt3001_chip_information }, | |
| { .name = "opt3002", .driver_data = (kernel_ulong_t)&opt3002_chip_information }, | |
| { } /* Terminating Entry */ | |
| }; | |
| MODULE_DEVICE_TABLE(i2c, opt3001_id); | |
| static const struct of_device_id opt3001_of_match[] = { | |
| { .compatible = "ti,opt3001", .data = &opt3001_chip_information }, | |
| { .compatible = "ti,opt3002", .data = &opt3002_chip_information }, | |
| { } | |
| }; | |
| MODULE_DEVICE_TABLE(of, opt3001_of_match); | |
| static struct i2c_driver opt3001_driver = { | |
| .probe = opt3001_probe, | |
| .id_table = opt3001_id, | |
| .driver = { | |
| .name = "opt3001", | |
| .of_match_table = opt3001_of_match, | |
| }, | |
| }; | |
| module_i2c_driver(opt3001_driver); | |
| MODULE_LICENSE("GPL v2"); | |
| MODULE_AUTHOR("Andreas Dannenberg <dannenberg@ti.com>"); | |
| MODULE_DESCRIPTION("Texas Instruments OPT3001 Light Sensor Driver"); |