diff options
Diffstat (limited to 'drivers/staging/iio/light/isl29018.c')
-rw-r--r-- | drivers/staging/iio/light/isl29018.c | 217 |
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, |