diff options
Diffstat (limited to 'kernel/drivers/staging/iio/light/isl29018.c')
-rw-r--r-- | kernel/drivers/staging/iio/light/isl29018.c | 296 |
1 files changed, 164 insertions, 132 deletions
diff --git a/kernel/drivers/staging/iio/light/isl29018.c b/kernel/drivers/staging/iio/light/isl29018.c index a3489187a..bbf7e35cb 100644 --- a/kernel/drivers/staging/iio/light/isl29018.c +++ b/kernel/drivers/staging/iio/light/isl29018.c @@ -66,58 +66,113 @@ #define ISL29035_BOUT_SHIFT 0x07 #define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT) +#define ISL29018_INT_TIME_AVAIL "0.090000 0.005630 0.000351 0.000021" +#define ISL29023_INT_TIME_AVAIL "0.090000 0.005600 0.000352 0.000022" +#define ISL29035_INT_TIME_AVAIL "0.105000 0.006500 0.000410 0.000025" + +static const char * const int_time_avail[] = { + ISL29018_INT_TIME_AVAIL, + ISL29023_INT_TIME_AVAIL, + ISL29035_INT_TIME_AVAIL, +}; + +enum isl29018_int_time { + ISL29018_INT_TIME_16, + ISL29018_INT_TIME_12, + ISL29018_INT_TIME_8, + ISL29018_INT_TIME_4, +}; + +static const unsigned int isl29018_int_utimes[3][4] = { + {90000, 5630, 351, 21}, + {90000, 5600, 352, 22}, + {105000, 6500, 410, 25}, +}; + +static const struct isl29018_scale { + unsigned int scale; + unsigned int uscale; +} isl29018_scales[4][4] = { + { {0, 15258}, {0, 61035}, {0, 244140}, {0, 976562} }, + { {0, 244140}, {0, 976562}, {3, 906250}, {15, 625000} }, + { {3, 906250}, {15, 625000}, {62, 500000}, {250, 0} }, + { {62, 500000}, {250, 0}, {1000, 0}, {4000, 0} } +}; + struct isl29018_chip { struct device *dev; struct regmap *regmap; struct mutex lock; int type; - unsigned int lux_scale; - unsigned int lux_uscale; - unsigned int range; - unsigned int adc_bit; + unsigned int calibscale; + unsigned int ucalibscale; + unsigned int int_time; + struct isl29018_scale scale; int prox_scheme; bool suspended; }; -static int isl29018_set_range(struct isl29018_chip *chip, unsigned long range, - unsigned int *new_range) +static int isl29018_set_integration_time(struct isl29018_chip *chip, + unsigned int utime) { - static const unsigned long supp_ranges[] = {1000, 4000, 16000, 64000}; - int i; + int i, ret; + unsigned int int_time, new_int_time; - for (i = 0; i < ARRAY_SIZE(supp_ranges); ++i) { - if (range <= supp_ranges[i]) { - *new_range = (unsigned int)supp_ranges[i]; + for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) { + if (utime == isl29018_int_utimes[chip->type][i]) { + new_int_time = i; break; } } - if (i >= ARRAY_SIZE(supp_ranges)) + if (i >= ARRAY_SIZE(isl29018_int_utimes[chip->type])) return -EINVAL; - return regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII, - COMMANDII_RANGE_MASK, i << COMMANDII_RANGE_SHIFT); + ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII, + COMMANDII_RESOLUTION_MASK, + i << COMMANDII_RESOLUTION_SHIFT); + if (ret < 0) + return ret; + + /* keep the same range when integration time changes */ + int_time = chip->int_time; + for (i = 0; i < ARRAY_SIZE(isl29018_scales[int_time]); ++i) { + if (chip->scale.scale == isl29018_scales[int_time][i].scale && + chip->scale.uscale == isl29018_scales[int_time][i].uscale) { + chip->scale = isl29018_scales[new_int_time][i]; + break; + } + } + chip->int_time = new_int_time; + + return 0; } -static int isl29018_set_resolution(struct isl29018_chip *chip, - unsigned long adcbit, unsigned int *conf_adc_bit) +static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale) { - static const unsigned long supp_adcbit[] = {16, 12, 8, 4}; - int i; + int i, ret; + struct isl29018_scale new_scale; - for (i = 0; i < ARRAY_SIZE(supp_adcbit); ++i) { - if (adcbit >= supp_adcbit[i]) { - *conf_adc_bit = (unsigned int)supp_adcbit[i]; + for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) { + if (scale == isl29018_scales[chip->int_time][i].scale && + uscale == isl29018_scales[chip->int_time][i].uscale) { + new_scale = isl29018_scales[chip->int_time][i]; break; } } - if (i >= ARRAY_SIZE(supp_adcbit)) + if (i >= ARRAY_SIZE(isl29018_scales[chip->int_time])) return -EINVAL; - return regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII, - COMMANDII_RESOLUTION_MASK, - i << COMMANDII_RESOLUTION_SHIFT); + ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII, + COMMANDII_RANGE_MASK, + i << COMMANDII_RANGE_SHIFT); + if (ret < 0) + return ret; + + chip->scale = new_scale; + + return 0; } static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode) @@ -156,22 +211,17 @@ static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode) static int isl29018_read_lux(struct isl29018_chip *chip, int *lux) { int lux_data; - unsigned int data_x_range, lux_unshifted; + unsigned int data_x_range; lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE); if (lux_data < 0) return lux_data; - /* To support fractional scaling, separate the unshifted lux - * into two calculations: int scaling and micro-scaling. - * lux_uscale ranges from 0-999999, so about 20 bits. Split - * the /1,000,000 in two to reduce the risk of over/underflow. - */ - data_x_range = lux_data * chip->range; - lux_unshifted = data_x_range * chip->lux_scale; - lux_unshifted += data_x_range / 1000 * chip->lux_uscale / 1000; - *lux = lux_unshifted >> chip->adc_bit; + data_x_range = lux_data * chip->scale.scale + + lux_data * chip->scale.uscale / 1000000; + *lux = data_x_range * chip->calibscale + + data_x_range * chip->ucalibscale / 1000000; return 0; } @@ -229,86 +279,37 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme, return 0; } -/* Sysfs interface */ -/* range */ -static ssize_t show_range(struct device *dev, +static ssize_t show_scale_available(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct isl29018_chip *chip = iio_priv(indio_dev); + int i, len = 0; - return sprintf(buf, "%u\n", chip->range); -} + for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) + len += sprintf(buf + len, "%d.%06d ", + isl29018_scales[chip->int_time][i].scale, + isl29018_scales[chip->int_time][i].uscale); -static ssize_t store_range(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct isl29018_chip *chip = iio_priv(indio_dev); - int status; - unsigned long lval; - unsigned int new_range; - - if (kstrtoul(buf, 10, &lval)) - return -EINVAL; + buf[len - 1] = '\n'; - if (!(lval == 1000UL || lval == 4000UL || - lval == 16000UL || lval == 64000UL)) { - dev_err(dev, "The range is not supported\n"); - return -EINVAL; - } - - mutex_lock(&chip->lock); - status = isl29018_set_range(chip, lval, &new_range); - if (status < 0) { - mutex_unlock(&chip->lock); - dev_err(dev, - "Error in setting max range with err %d\n", status); - return status; - } - chip->range = new_range; - mutex_unlock(&chip->lock); - - return count; + return len; } -/* resolution */ -static ssize_t show_resolution(struct device *dev, +static ssize_t show_int_time_available(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct isl29018_chip *chip = iio_priv(indio_dev); + int i, len = 0; - return sprintf(buf, "%u\n", chip->adc_bit); -} - -static ssize_t store_resolution(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct isl29018_chip *chip = iio_priv(indio_dev); - int status; - unsigned int val; - unsigned int new_adc_bit; - - if (kstrtouint(buf, 10, &val)) - return -EINVAL; - if (!(val == 4 || val == 8 || val == 12 || val == 16)) { - dev_err(dev, "The resolution is not supported\n"); - return -EINVAL; - } + for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) + len += sprintf(buf + len, "0.%06d ", + isl29018_int_utimes[chip->type][i]); - mutex_lock(&chip->lock); - status = isl29018_set_resolution(chip, val, &new_adc_bit); - if (status < 0) { - mutex_unlock(&chip->lock); - dev_err(dev, "Error in setting resolution\n"); - return status; - } - chip->adc_bit = new_adc_bit; - mutex_unlock(&chip->lock); + buf[len - 1] = '\n'; - return count; + return len; } /* proximity scheme */ @@ -357,11 +358,29 @@ static int isl29018_write_raw(struct iio_dev *indio_dev, int ret = -EINVAL; mutex_lock(&chip->lock); - if (mask == IIO_CHAN_INFO_CALIBSCALE && chan->type == IIO_LIGHT) { - chip->lux_scale = val; - /* With no write_raw_get_fmt(), val2 is a MICRO fraction. */ - chip->lux_uscale = val2; - ret = 0; + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + if (chan->type == IIO_LIGHT) { + chip->calibscale = val; + chip->ucalibscale = val2; + ret = 0; + } + break; + case IIO_CHAN_INFO_INT_TIME: + if (chan->type == IIO_LIGHT) { + if (val) { + mutex_unlock(&chip->lock); + return -EINVAL; + } + ret = isl29018_set_integration_time(chip, val2); + } + break; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_LIGHT) + ret = isl29018_set_scale(chip, val, val2); + break; + default: + break; } mutex_unlock(&chip->lock); @@ -402,10 +421,24 @@ static int isl29018_read_raw(struct iio_dev *indio_dev, if (!ret) ret = IIO_VAL_INT; break; + case IIO_CHAN_INFO_INT_TIME: + if (chan->type == IIO_LIGHT) { + *val = 0; + *val2 = isl29018_int_utimes[chip->type][chip->int_time]; + ret = IIO_VAL_INT_PLUS_MICRO; + } + break; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_LIGHT) { + *val = chip->scale.scale; + *val2 = chip->scale.uscale; + ret = IIO_VAL_INT_PLUS_MICRO; + } + break; case IIO_CHAN_INFO_CALIBSCALE: if (chan->type == IIO_LIGHT) { - *val = chip->lux_scale; - *val2 = chip->lux_uscale; + *val = chip->calibscale; + *val2 = chip->ucalibscale; ret = IIO_VAL_INT_PLUS_MICRO; } break; @@ -421,7 +454,9 @@ static int isl29018_read_raw(struct iio_dev *indio_dev, .indexed = 1, \ .channel = 0, \ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \ - BIT(IIO_CHAN_INFO_CALIBSCALE), \ + BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME), \ } #define ISL29018_IR_CHANNEL { \ @@ -447,32 +482,27 @@ static const struct iio_chan_spec isl29023_channels[] = { ISL29018_IR_CHANNEL, }; -static IIO_DEVICE_ATTR(range, S_IRUGO | S_IWUSR, show_range, store_range, 0); -static IIO_CONST_ATTR(range_available, "1000 4000 16000 64000"); -static IIO_CONST_ATTR(adc_resolution_available, "4 8 12 16"); -static IIO_DEVICE_ATTR(adc_resolution, S_IRUGO | S_IWUSR, - show_resolution, store_resolution, 0); +static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, S_IRUGO, + show_int_time_available, NULL, 0); +static IIO_DEVICE_ATTR(in_illuminance_scale_available, S_IRUGO, + show_scale_available, NULL, 0); static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression, S_IRUGO | S_IWUSR, show_prox_infrared_suppression, store_prox_infrared_suppression, 0); #define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) -#define ISL29018_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr) + static struct attribute *isl29018_attributes[] = { - ISL29018_DEV_ATTR(range), - ISL29018_CONST_ATTR(range_available), - ISL29018_DEV_ATTR(adc_resolution), - ISL29018_CONST_ATTR(adc_resolution_available), + ISL29018_DEV_ATTR(in_illuminance_scale_available), + ISL29018_DEV_ATTR(in_illuminance_integration_time_available), ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_suppression), NULL }; static struct attribute *isl29023_attributes[] = { - ISL29018_DEV_ATTR(range), - ISL29018_CONST_ATTR(range_available), - ISL29018_DEV_ATTR(adc_resolution), - ISL29018_CONST_ATTR(adc_resolution_available), + ISL29018_DEV_ATTR(in_illuminance_scale_available), + ISL29018_DEV_ATTR(in_illuminance_integration_time_available), NULL }; @@ -516,8 +546,6 @@ enum { static int isl29018_chip_init(struct isl29018_chip *chip) { int status; - unsigned int new_adc_bit; - unsigned int new_range; if (chip->type == isl29035) { status = isl29035_detect(chip); @@ -566,14 +594,19 @@ static int isl29018_chip_init(struct isl29018_chip *chip) usleep_range(1000, 2000); /* per data sheet, page 10 */ /* set defaults */ - status = isl29018_set_range(chip, chip->range, &new_range); + status = isl29018_set_scale(chip, chip->scale.scale, + chip->scale.uscale); if (status < 0) { dev_err(chip->dev, "Init of isl29018 fails\n"); return status; } - status = isl29018_set_resolution(chip, chip->adc_bit, - &new_adc_bit); + status = isl29018_set_integration_time(chip, + isl29018_int_utimes[chip->type][chip->int_time]); + if (status < 0) { + dev_err(chip->dev, "Init of isl29018 fails\n"); + return status; + } return 0; } @@ -681,7 +714,7 @@ static int isl29018_probe(struct i2c_client *client, int dev_id = 0; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); - if (indio_dev == NULL) { + if (!indio_dev) { dev_err(&client->dev, "iio allocation fails\n"); return -ENOMEM; } @@ -701,10 +734,10 @@ static int isl29018_probe(struct i2c_client *client, mutex_init(&chip->lock); chip->type = dev_id; - chip->lux_scale = 1; - chip->lux_uscale = 0; - chip->range = 1000; - chip->adc_bit = 16; + chip->calibscale = 1; + chip->ucalibscale = 0; + chip->int_time = ISL29018_INT_TIME_16; + chip->scale = isl29018_scales[chip->int_time][0]; chip->suspended = false; chip->regmap = devm_regmap_init_i2c(client, @@ -803,7 +836,6 @@ static struct i2c_driver isl29018_driver = { .name = "isl29018", .acpi_match_table = ACPI_PTR(isl29018_acpi_match), .pm = ISL29018_PM_OPS, - .owner = THIS_MODULE, .of_match_table = isl29018_of_match, }, .probe = isl29018_probe, |