aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/iio/light/isl29018.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/iio/light/isl29018.c')
-rw-r--r--drivers/staging/iio/light/isl29018.c217
1 files changed, 185 insertions, 32 deletions
diff --git a/drivers/staging/iio/light/isl29018.c b/drivers/staging/iio/light/isl29018.c
index 3660a43b5f08..a3489187aeb0 100644
--- a/drivers/staging/iio/light/isl29018.c
+++ b/drivers/staging/iio/light/isl29018.c
@@ -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.
@@ -30,6 +30,7 @@
#include <linux/slab.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <linux/acpi.h>
#define CONVERSION_TIME_MS 100
@@ -58,10 +59,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 +416,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 +468,63 @@ static struct attribute *isl29018_attributes[] = {
NULL
};
-static const struct attribute_group isl29108_group = {
+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;
- int new_adc_bit;
+ 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
@@ -479,8 +547,8 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
*/
status = regmap_write(chip->regmap, ISL29018_REG_TEST, 0x0);
if (status < 0) {
- dev_err(chip->dev, "Failed to clear isl29018 TEST reg."
- "(%d)\n", status);
+ dev_err(chip->dev, "Failed to clear isl29018 TEST reg.(%d)\n",
+ status);
return status;
}
@@ -490,12 +558,12 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
*/
status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1, 0);
if (status < 0) {
- dev_err(chip->dev, "Failed to clear isl29018 CMD1 reg."
- "(%d)\n", status);
+ dev_err(chip->dev, "Failed to clear isl29018 CMD1 reg.(%d)\n",
+ status);
return status;
}
- msleep(1); /* per data sheet, page 10 */
+ usleep_range(1000, 2000); /* per data sheet, page 10 */
/* set defaults */
status = isl29018_set_range(chip, chip->range, &new_range);
@@ -510,8 +578,15 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
return 0;
}
-static const struct iio_info isl29108_info = {
- .attrs = &isl29108_group,
+static const struct iio_info isl29018_info = {
+ .attrs = &isl29018_group,
+ .driver_module = THIS_MODULE,
+ .read_raw = &isl29018_read_raw,
+ .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,
@@ -524,6 +599,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,12 +619,66 @@ 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 const char *isl29018_match_acpi_device(struct device *dev, int *data)
+{
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+
+ if (!id)
+ return NULL;
+
+ *data = (int) id->driver_data;
+
+ return dev_name(dev);
+}
+
static int isl29018_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct isl29018_chip *chip;
struct iio_dev *indio_dev;
int err;
+ const char *name = NULL;
+ int dev_id = 0;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
if (indio_dev == NULL) {
@@ -560,15 +690,25 @@ static int isl29018_probe(struct i2c_client *client,
i2c_set_clientdata(client, indio_dev);
chip->dev = &client->dev;
+ if (id) {
+ name = id->name;
+ dev_id = id->driver_data;
+ }
+
+ if (ACPI_HANDLE(&client->dev))
+ name = isl29018_match_acpi_device(&client->dev, &dev_id);
+
mutex_init(&chip->lock);
+ chip->type = dev_id;
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[dev_id].regmap_cfg);
if (IS_ERR(chip->regmap)) {
err = PTR_ERR(chip->regmap);
dev_err(chip->dev, "regmap initialization failed: %d\n", err);
@@ -579,10 +719,10 @@ static int isl29018_probe(struct i2c_client *client,
if (err)
return err;
- indio_dev->info = &isl29108_info;
- indio_dev->channels = isl29018_channels;
- indio_dev->num_channels = ARRAY_SIZE(isl29018_channels);
- indio_dev->name = id->name;
+ indio_dev->info = chip_info_tbl[dev_id].indio_info;
+ indio_dev->channels = chip_info_tbl[dev_id].channels;
+ indio_dev->num_channels = chip_info_tbl[dev_id].num_channels;
+ indio_dev->name = name;
indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
err = devm_iio_device_register(&client->dev, indio_dev);
@@ -632,8 +772,18 @@ static SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume);
#define ISL29018_PM_OPS NULL
#endif
+static const struct acpi_device_id isl29018_acpi_match[] = {
+ {"ISL29018", isl29018},
+ {"ISL29023", isl29023},
+ {"ISL29035", isl29035},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, isl29018_acpi_match);
+
static const struct i2c_device_id isl29018_id[] = {
- {"isl29018", 0},
+ {"isl29018", isl29018},
+ {"isl29023", isl29023},
+ {"isl29035", isl29035},
{}
};
@@ -641,6 +791,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);
@@ -649,6 +801,7 @@ static struct i2c_driver isl29018_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "isl29018",
+ .acpi_match_table = ACPI_PTR(isl29018_acpi_match),
.pm = ISL29018_PM_OPS,
.owner = THIS_MODULE,
.of_match_table = isl29018_of_match,