|
@@ -1,5 +1,5 @@
|
|
|
/*
|
|
|
- * A iio driver for the light sensor ISL 29018.
|
|
|
+ * A iio driver for the light sensor ISL 29018/29023/29035.
|
|
|
*
|
|
|
* IIO driver for monitoring ambient light intensity in luxi, proximity
|
|
|
* sensing and infrared sensing.
|
|
@@ -58,10 +58,18 @@
|
|
|
#define ISL29018_TEST_SHIFT 0
|
|
|
#define ISL29018_TEST_MASK (0xFF << ISL29018_TEST_SHIFT)
|
|
|
|
|
|
+#define ISL29035_REG_DEVICE_ID 0x0F
|
|
|
+#define ISL29035_DEVICE_ID_SHIFT 0x03
|
|
|
+#define ISL29035_DEVICE_ID_MASK (0x7 << ISL29035_DEVICE_ID_SHIFT)
|
|
|
+#define ISL29035_DEVICE_ID 0x5
|
|
|
+#define ISL29035_BOUT_SHIFT 0x07
|
|
|
+#define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT)
|
|
|
+
|
|
|
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;
|
|
@@ -407,23 +415,35 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+#define ISL29018_LIGHT_CHANNEL { \
|
|
|
+ .type = IIO_LIGHT, \
|
|
|
+ .indexed = 1, \
|
|
|
+ .channel = 0, \
|
|
|
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
|
|
|
+ BIT(IIO_CHAN_INFO_CALIBSCALE), \
|
|
|
+}
|
|
|
+
|
|
|
+#define ISL29018_IR_CHANNEL { \
|
|
|
+ .type = IIO_INTENSITY, \
|
|
|
+ .modified = 1, \
|
|
|
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
|
|
+ .channel2 = IIO_MOD_LIGHT_IR, \
|
|
|
+}
|
|
|
+
|
|
|
+#define ISL29018_PROXIMITY_CHANNEL { \
|
|
|
+ .type = IIO_PROXIMITY, \
|
|
|
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
|
|
+}
|
|
|
+
|
|
|
static const struct iio_chan_spec isl29018_channels[] = {
|
|
|
- {
|
|
|
- .type = IIO_LIGHT,
|
|
|
- .indexed = 1,
|
|
|
- .channel = 0,
|
|
|
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
|
|
- BIT(IIO_CHAN_INFO_CALIBSCALE),
|
|
|
- }, {
|
|
|
- .type = IIO_INTENSITY,
|
|
|
- .modified = 1,
|
|
|
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
|
|
- .channel2 = IIO_MOD_LIGHT_IR,
|
|
|
- }, {
|
|
|
- /* Unindexed in current ABI. But perhaps it should be. */
|
|
|
- .type = IIO_PROXIMITY,
|
|
|
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
|
|
- }
|
|
|
+ ISL29018_LIGHT_CHANNEL,
|
|
|
+ ISL29018_IR_CHANNEL,
|
|
|
+ ISL29018_PROXIMITY_CHANNEL,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct iio_chan_spec isl29023_channels[] = {
|
|
|
+ ISL29018_LIGHT_CHANNEL,
|
|
|
+ ISL29018_IR_CHANNEL,
|
|
|
};
|
|
|
|
|
|
static IIO_DEVICE_ATTR(range, S_IRUGO | S_IWUSR, show_range, store_range, 0);
|
|
@@ -447,16 +467,63 @@ static struct attribute *isl29018_attributes[] = {
|
|
|
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),
|
|
|
+ NULL
|
|
|
+};
|
|
|
+
|
|
|
static const struct attribute_group isl29018_group = {
|
|
|
.attrs = isl29018_attributes,
|
|
|
};
|
|
|
|
|
|
+static const struct attribute_group isl29023_group = {
|
|
|
+ .attrs = isl29023_attributes,
|
|
|
+};
|
|
|
+
|
|
|
+static int isl29035_detect(struct isl29018_chip *chip)
|
|
|
+{
|
|
|
+ int status;
|
|
|
+ unsigned int id;
|
|
|
+
|
|
|
+ status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
|
|
|
+ if (status < 0) {
|
|
|
+ dev_err(chip->dev,
|
|
|
+ "Error reading ID register with error %d\n",
|
|
|
+ status);
|
|
|
+ return status;
|
|
|
+ }
|
|
|
+
|
|
|
+ id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
|
|
|
+
|
|
|
+ if (id != ISL29035_DEVICE_ID)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ /* clear out brownout bit */
|
|
|
+ return regmap_update_bits(chip->regmap, ISL29035_REG_DEVICE_ID,
|
|
|
+ ISL29035_BOUT_MASK, 0);
|
|
|
+}
|
|
|
+
|
|
|
+enum {
|
|
|
+ isl29018,
|
|
|
+ isl29023,
|
|
|
+ isl29035,
|
|
|
+};
|
|
|
+
|
|
|
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);
|
|
|
+ if (status < 0)
|
|
|
+ return status;
|
|
|
+ }
|
|
|
+
|
|
|
/* Code added per Intersil Application Note 1534:
|
|
|
* When VDD sinks to approximately 1.8V or below, some of
|
|
|
* the part's registers may change their state. When VDD
|
|
@@ -517,6 +584,13 @@ static const struct iio_info isl29018_info = {
|
|
|
.write_raw = &isl29018_write_raw,
|
|
|
};
|
|
|
|
|
|
+static const struct iio_info isl29023_info = {
|
|
|
+ .attrs = &isl29023_group,
|
|
|
+ .driver_module = THIS_MODULE,
|
|
|
+ .read_raw = &isl29018_read_raw,
|
|
|
+ .write_raw = &isl29018_write_raw,
|
|
|
+};
|
|
|
+
|
|
|
static bool is_volatile_reg(struct device *dev, unsigned int reg)
|
|
|
{
|
|
|
switch (reg) {
|
|
@@ -524,6 +598,7 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg)
|
|
|
case ISL29018_REG_ADD_DATA_MSB:
|
|
|
case ISL29018_REG_ADD_COMMAND1:
|
|
|
case ISL29018_REG_TEST:
|
|
|
+ case ISL29035_REG_DEVICE_ID:
|
|
|
return true;
|
|
|
default:
|
|
|
return false;
|
|
@@ -543,6 +618,44 @@ static const struct regmap_config isl29018_regmap_config = {
|
|
|
.cache_type = REGCACHE_RBTREE,
|
|
|
};
|
|
|
|
|
|
+/* isl29035_regmap_config: regmap configuration for ISL29035 */
|
|
|
+static const struct regmap_config isl29035_regmap_config = {
|
|
|
+ .reg_bits = 8,
|
|
|
+ .val_bits = 8,
|
|
|
+ .volatile_reg = is_volatile_reg,
|
|
|
+ .max_register = ISL29035_REG_DEVICE_ID,
|
|
|
+ .num_reg_defaults_raw = ISL29035_REG_DEVICE_ID + 1,
|
|
|
+ .cache_type = REGCACHE_RBTREE,
|
|
|
+};
|
|
|
+
|
|
|
+struct chip_info {
|
|
|
+ const struct iio_chan_spec *channels;
|
|
|
+ int num_channels;
|
|
|
+ const struct iio_info *indio_info;
|
|
|
+ const struct regmap_config *regmap_cfg;
|
|
|
+};
|
|
|
+
|
|
|
+static const struct chip_info chip_info_tbl[] = {
|
|
|
+ [isl29018] = {
|
|
|
+ .channels = isl29018_channels,
|
|
|
+ .num_channels = ARRAY_SIZE(isl29018_channels),
|
|
|
+ .indio_info = &isl29018_info,
|
|
|
+ .regmap_cfg = &isl29018_regmap_config,
|
|
|
+ },
|
|
|
+ [isl29023] = {
|
|
|
+ .channels = isl29023_channels,
|
|
|
+ .num_channels = ARRAY_SIZE(isl29023_channels),
|
|
|
+ .indio_info = &isl29023_info,
|
|
|
+ .regmap_cfg = &isl29018_regmap_config,
|
|
|
+ },
|
|
|
+ [isl29035] = {
|
|
|
+ .channels = isl29023_channels,
|
|
|
+ .num_channels = ARRAY_SIZE(isl29023_channels),
|
|
|
+ .indio_info = &isl29023_info,
|
|
|
+ .regmap_cfg = &isl29035_regmap_config,
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
static int isl29018_probe(struct i2c_client *client,
|
|
|
const struct i2c_device_id *id)
|
|
|
{
|
|
@@ -562,13 +675,15 @@ static int isl29018_probe(struct i2c_client *client,
|
|
|
|
|
|
mutex_init(&chip->lock);
|
|
|
|
|
|
+ chip->type = id->driver_data;
|
|
|
chip->lux_scale = 1;
|
|
|
chip->lux_uscale = 0;
|
|
|
chip->range = 1000;
|
|
|
chip->adc_bit = 16;
|
|
|
chip->suspended = false;
|
|
|
|
|
|
- chip->regmap = devm_regmap_init_i2c(client, &isl29018_regmap_config);
|
|
|
+ chip->regmap = devm_regmap_init_i2c(client,
|
|
|
+ chip_info_tbl[id->driver_data].regmap_cfg);
|
|
|
if (IS_ERR(chip->regmap)) {
|
|
|
err = PTR_ERR(chip->regmap);
|
|
|
dev_err(chip->dev, "regmap initialization failed: %d\n", err);
|
|
@@ -579,9 +694,9 @@ static int isl29018_probe(struct i2c_client *client,
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
|
- indio_dev->info = &isl29018_info;
|
|
|
- indio_dev->channels = isl29018_channels;
|
|
|
- indio_dev->num_channels = ARRAY_SIZE(isl29018_channels);
|
|
|
+ indio_dev->info = chip_info_tbl[id->driver_data].indio_info;
|
|
|
+ indio_dev->channels = chip_info_tbl[id->driver_data].channels;
|
|
|
+ indio_dev->num_channels = chip_info_tbl[id->driver_data].num_channels;
|
|
|
indio_dev->name = id->name;
|
|
|
indio_dev->dev.parent = &client->dev;
|
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
@@ -633,7 +748,9 @@ static SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume);
|
|
|
#endif
|
|
|
|
|
|
static const struct i2c_device_id isl29018_id[] = {
|
|
|
- {"isl29018", 0},
|
|
|
+ {"isl29018", isl29018},
|
|
|
+ {"isl29023", isl29023},
|
|
|
+ {"isl29035", isl29035},
|
|
|
{}
|
|
|
};
|
|
|
|
|
@@ -641,6 +758,8 @@ MODULE_DEVICE_TABLE(i2c, isl29018_id);
|
|
|
|
|
|
static const struct of_device_id isl29018_of_match[] = {
|
|
|
{ .compatible = "isil,isl29018", },
|
|
|
+ { .compatible = "isil,isl29023", },
|
|
|
+ { .compatible = "isil,isl29035", },
|
|
|
{ },
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(of, isl29018_of_match);
|