diff options
Diffstat (limited to 'drivers/iio/light/ltr501.c')
| -rw-r--r-- | drivers/iio/light/ltr501.c | 343 |
1 files changed, 195 insertions, 148 deletions
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 5a3fcb127cd2..022e0693983b 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * ltr501.c - Support for Lite-On LTR501 ambient light and proximity sensor + * Support for Lite-On LTR501 and similar ambient light and proximity sensors. * * Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net> * @@ -10,11 +10,12 @@ */ #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/i2c.h> #include <linux/err.h> #include <linux/delay.h> #include <linux/regmap.h> -#include <linux/acpi.h> +#include <linux/regulator/consumer.h> #include <linux/iio/iio.h> #include <linux/iio/events.h> @@ -23,8 +24,6 @@ #include <linux/iio/buffer.h> #include <linux/iio/triggered_buffer.h> -#define LTR501_DRV_NAME "ltr501" - #define LTR501_ALS_CONTR 0x80 /* ALS operation mode, SW reset */ #define LTR501_PS_CONTR 0x81 /* PS operation mode */ #define LTR501_PS_MEAS_RATE 0x84 /* measurement rate*/ @@ -32,9 +31,12 @@ #define LTR501_PART_ID 0x86 #define LTR501_MANUFAC_ID 0x87 #define LTR501_ALS_DATA1 0x88 /* 16-bit, little endian */ +#define LTR501_ALS_DATA1_UPPER 0x89 /* upper 8 bits of LTR501_ALS_DATA1 */ #define LTR501_ALS_DATA0 0x8a /* 16-bit, little endian */ +#define LTR501_ALS_DATA0_UPPER 0x8b /* upper 8 bits of LTR501_ALS_DATA0 */ #define LTR501_ALS_PS_STATUS 0x8c #define LTR501_PS_DATA 0x8d /* 16-bit, little endian */ +#define LTR501_PS_DATA_UPPER 0x8e /* upper 8 bits of LTR501_PS_DATA */ #define LTR501_INTR 0x8f /* output mode, polarity, mode */ #define LTR501_PS_THRESH_UP 0x90 /* 11 bit, ps upper threshold */ #define LTR501_PS_THRESH_LOW 0x92 /* 11 bit, ps lower threshold */ @@ -61,8 +63,6 @@ #define LTR501_ALS_DEF_PERIOD 500000 #define LTR501_PS_DEF_PERIOD 100000 -#define LTR501_REGMAP_NAME "ltr501_regmap" - #define LTR501_LUX_CONV(vis_coeff, vis_data, ir_coeff, ir_data) \ ((vis_coeff * vis_data) - (ir_coeff * ir_data)) @@ -94,6 +94,7 @@ enum { ltr501 = 0, ltr559, ltr301, + ltr303, }; struct ltr501_gain { @@ -149,7 +150,7 @@ struct ltr501_chip_info { struct ltr501_data { struct i2c_client *client; struct mutex lock_als, lock_ps; - struct ltr501_chip_info *chip_info; + const struct ltr501_chip_info *chip_info; u8 als_contr, ps_contr; int als_period, ps_period; /* period in micro seconds */ struct regmap *regmap; @@ -160,6 +161,7 @@ struct ltr501_data { struct regmap_field *reg_ps_rate; struct regmap_field *reg_als_prst; struct regmap_field *reg_ps_prst; + uint32_t near_level; }; static const struct ltr501_samp_table ltr501_als_samp_table[] = { @@ -406,18 +408,19 @@ static int ltr501_read_als(const struct ltr501_data *data, __le16 buf[2]) static int ltr501_read_ps(const struct ltr501_data *data) { - int ret, status; + __le16 status; + int ret; ret = ltr501_drdy(data, LTR501_STATUS_PS_RDY); if (ret < 0) return ret; ret = regmap_bulk_read(data->regmap, LTR501_PS_DATA, - &status, 2); + &status, sizeof(status)); if (ret < 0) return ret; - return status; + return le16_to_cpu(status); } static int ltr501_read_intr_prst(const struct ltr501_data *data, @@ -518,6 +521,25 @@ static int ltr501_write_intr_prst(struct ltr501_data *data, return -EINVAL; } +static ssize_t ltr501_read_near_level(struct iio_dev *indio_dev, + uintptr_t priv, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ltr501_data *data = iio_priv(indio_dev); + + return sprintf(buf, "%u\n", data->near_level); +} + +static const struct iio_chan_spec_ext_info ltr501_ext_info[] = { + { + .name = "nearlevel", + .shared = IIO_SEPARATE, + .read = ltr501_read_near_level, + }, + { } +}; + static const struct iio_event_spec ltr501_als_event_spec[] = { { .type = IIO_EV_TYPE_THRESH, @@ -602,6 +624,7 @@ static const struct iio_chan_spec ltr501_channels[] = { }, .event_spec = ltr501_pxs_event_spec, .num_event_specs = ARRAY_SIZE(ltr501_pxs_event_spec), + .ext_info = ltr501_ext_info, }, IIO_CHAN_SOFT_TIMESTAMP(3), }; @@ -619,6 +642,36 @@ static const struct iio_chan_spec ltr301_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(2), }; +static int ltr501_read_info_raw(struct ltr501_data *data, + struct iio_chan_spec const *chan, + int *val) +{ + __le16 buf[2]; + int ret; + + switch (chan->type) { + case IIO_INTENSITY: + mutex_lock(&data->lock_als); + ret = ltr501_read_als(data, buf); + mutex_unlock(&data->lock_als); + if (ret < 0) + return ret; + *val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ? + buf[0] : buf[1]); + return IIO_VAL_INT; + case IIO_PROXIMITY: + mutex_lock(&data->lock_ps); + ret = ltr501_read_ps(data); + mutex_unlock(&data->lock_ps); + if (ret < 0) + return ret; + *val = ret & LTR501_PS_DATA_MASK; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + static int ltr501_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -631,14 +684,13 @@ static int ltr501_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_PROCESSED: switch (chan->type) { case IIO_LIGHT: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; mutex_lock(&data->lock_als); ret = ltr501_read_als(data, buf); mutex_unlock(&data->lock_als); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); if (ret < 0) return ret; *val = ltr501_calculate_lux(le16_to_cpu(buf[1]), @@ -648,36 +700,12 @@ static int ltr501_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_RAW: - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; - switch (chan->type) { - case IIO_INTENSITY: - mutex_lock(&data->lock_als); - ret = ltr501_read_als(data, buf); - mutex_unlock(&data->lock_als); - if (ret < 0) - break; - *val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ? - buf[0] : buf[1]); - ret = IIO_VAL_INT; - break; - case IIO_PROXIMITY: - mutex_lock(&data->lock_ps); - ret = ltr501_read_ps(data); - mutex_unlock(&data->lock_ps); - if (ret < 0) - break; - *val = ret & LTR501_PS_DATA_MASK; - ret = IIO_VAL_INT; - break; - default: - ret = -EINVAL; - break; - } + ret = ltr501_read_info_raw(data, chan, val); - iio_device_release_direct_mode(indio_dev); + iio_device_release_direct(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: @@ -729,17 +757,13 @@ static int ltr501_get_gain_index(const struct ltr501_gain *gain, int size, return -1; } -static int ltr501_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) +static int __ltr501_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) { struct ltr501_data *data = iio_priv(indio_dev); int i, ret, freq_val, freq_val2; - struct ltr501_chip_info *info = data->chip_info; - - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; + const struct ltr501_chip_info *info = data->chip_info; switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -748,53 +772,43 @@ static int ltr501_write_raw(struct iio_dev *indio_dev, i = ltr501_get_gain_index(info->als_gain, info->als_gain_tbl_size, val, val2); - if (i < 0) { - ret = -EINVAL; - break; - } + if (i < 0) + return -EINVAL; data->als_contr &= ~info->als_gain_mask; data->als_contr |= i << info->als_gain_shift; - ret = regmap_write(data->regmap, LTR501_ALS_CONTR, - data->als_contr); - break; + return regmap_write(data->regmap, LTR501_ALS_CONTR, + data->als_contr); case IIO_PROXIMITY: i = ltr501_get_gain_index(info->ps_gain, info->ps_gain_tbl_size, val, val2); - if (i < 0) { - ret = -EINVAL; - break; - } + if (i < 0) + return -EINVAL; + data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK; data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT; - ret = regmap_write(data->regmap, LTR501_PS_CONTR, - data->ps_contr); - break; + return regmap_write(data->regmap, LTR501_PS_CONTR, + data->ps_contr); default: - ret = -EINVAL; - break; + return -EINVAL; } - break; case IIO_CHAN_INFO_INT_TIME: switch (chan->type) { case IIO_INTENSITY: - if (val != 0) { - ret = -EINVAL; - break; - } + if (val != 0) + return -EINVAL; + mutex_lock(&data->lock_als); ret = ltr501_set_it_time(data, val2); mutex_unlock(&data->lock_als); - break; + return ret; default: - ret = -EINVAL; - break; + return -EINVAL; } - break; case IIO_CHAN_INFO_SAMP_FREQ: switch (chan->type) { @@ -802,50 +816,61 @@ static int ltr501_write_raw(struct iio_dev *indio_dev, ret = ltr501_als_read_samp_freq(data, &freq_val, &freq_val2); if (ret < 0) - break; + return ret; ret = ltr501_als_write_samp_freq(data, val, val2); if (ret < 0) - break; + return ret; /* update persistence count when changing frequency */ ret = ltr501_write_intr_prst(data, chan->type, 0, data->als_period); if (ret < 0) - ret = ltr501_als_write_samp_freq(data, freq_val, - freq_val2); - break; + /* Do not ovewrite error */ + ltr501_als_write_samp_freq(data, freq_val, + freq_val2); + return ret; case IIO_PROXIMITY: ret = ltr501_ps_read_samp_freq(data, &freq_val, &freq_val2); if (ret < 0) - break; + return ret; ret = ltr501_ps_write_samp_freq(data, val, val2); if (ret < 0) - break; + return ret; /* update persistence count when changing frequency */ ret = ltr501_write_intr_prst(data, chan->type, 0, data->ps_period); if (ret < 0) - ret = ltr501_ps_write_samp_freq(data, freq_val, - freq_val2); - break; + /* Do not overwrite error */ + ltr501_ps_write_samp_freq(data, freq_val, + freq_val2); + return ret; default: - ret = -EINVAL; - break; + return -EINVAL; } - break; - default: - ret = -EINVAL; - break; + return -EINVAL; } +} + +static int ltr501_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret = __ltr501_write_raw(indio_dev, chan, val, val2, mask); + + iio_device_release_direct(indio_dev); - iio_device_release_direct_mode(indio_dev); return ret; } @@ -1050,15 +1075,11 @@ static int ltr501_read_event_config(struct iio_dev *indio_dev, static int ltr501_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, - enum iio_event_direction dir, int state) + enum iio_event_direction dir, bool state) { struct ltr501_data *data = iio_priv(indio_dev); int ret; - /* only 1 and 0 are valid inputs */ - if (state != 1 && state != 0) - return -EINVAL; - switch (chan->type) { case IIO_INTENSITY: mutex_lock(&data->lock_als); @@ -1082,7 +1103,7 @@ static ssize_t ltr501_show_proximity_scale_avail(struct device *dev, char *buf) { struct ltr501_data *data = iio_priv(dev_to_iio_dev(dev)); - struct ltr501_chip_info *info = data->chip_info; + const struct ltr501_chip_info *info = data->chip_info; ssize_t len = 0; int i; @@ -1104,7 +1125,7 @@ static ssize_t ltr501_show_intensity_scale_avail(struct device *dev, char *buf) { struct ltr501_data *data = iio_priv(dev_to_iio_dev(dev)); - struct ltr501_chip_info *info = data->chip_info; + const struct ltr501_chip_info *info = data->chip_info; ssize_t len = 0; int i; @@ -1184,7 +1205,7 @@ static const struct iio_info ltr301_info = { .write_event_config = <r501_write_event_config, }; -static struct ltr501_chip_info ltr501_chip_info_tbl[] = { +static const struct ltr501_chip_info ltr501_chip_info_tbl[] = { [ltr501] = { .partid = 0x08, .als_gain = ltr501_als_gain_tbl, @@ -1205,7 +1226,7 @@ static struct ltr501_chip_info ltr501_chip_info_tbl[] = { .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl), .ps_gain = ltr559_ps_gain_tbl, .ps_gain_tbl_size = ARRAY_SIZE(ltr559_ps_gain_tbl), - .als_mode_active = BIT(1), + .als_mode_active = BIT(0), .als_gain_mask = BIT(2) | BIT(3) | BIT(4), .als_gain_shift = 2, .info = <r501_info, @@ -1225,6 +1246,18 @@ static struct ltr501_chip_info ltr501_chip_info_tbl[] = { .channels = ltr301_channels, .no_channels = ARRAY_SIZE(ltr301_channels), }, + [ltr303] = { + .partid = 0x0A, + .als_gain = ltr559_als_gain_tbl, + .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl), + .als_mode_active = BIT(0), + .als_gain_mask = BIT(2) | BIT(3) | BIT(4), + .als_gain_shift = 2, + .info = <r301_info, + .info_no_irq = <r301_info_no_irq, + .channels = ltr301_channels, + .no_channels = ARRAY_SIZE(ltr301_channels), + }, }; static int ltr501_write_contr(struct ltr501_data *data, u8 als_val, u8 ps_val) @@ -1243,14 +1276,15 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct ltr501_data *data = iio_priv(indio_dev); - u16 buf[8]; + struct { + u16 channels[3]; + aligned_s64 ts; + } scan = { }; __le16 als_buf[2]; u8 mask = 0; int j = 0; int ret, psdata; - memset(buf, 0, sizeof(buf)); - /* figure out which data needs to be ready */ if (test_bit(0, indio_dev->active_scan_mask) || test_bit(1, indio_dev->active_scan_mask)) @@ -1266,11 +1300,11 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p) ret = regmap_bulk_read(data->regmap, LTR501_ALS_DATA1, als_buf, sizeof(als_buf)); if (ret < 0) - return ret; + goto done; if (test_bit(0, indio_dev->active_scan_mask)) - buf[j++] = le16_to_cpu(als_buf[1]); + scan.channels[j++] = le16_to_cpu(als_buf[1]); if (test_bit(1, indio_dev->active_scan_mask)) - buf[j++] = le16_to_cpu(als_buf[0]); + scan.channels[j++] = le16_to_cpu(als_buf[0]); } if (mask & LTR501_STATUS_PS_RDY) { @@ -1278,11 +1312,11 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p) &psdata, 2); if (ret < 0) goto done; - buf[j++] = psdata & LTR501_PS_DATA_MASK; + scan.channels[j++] = psdata & LTR501_PS_DATA_MASK; } - iio_push_to_buffers_with_timestamp(indio_dev, buf, - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -1351,9 +1385,12 @@ static bool ltr501_is_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case LTR501_ALS_DATA1: + case LTR501_ALS_DATA1_UPPER: case LTR501_ALS_DATA0: + case LTR501_ALS_DATA0_UPPER: case LTR501_ALS_PS_STATUS: case LTR501_PS_DATA: + case LTR501_PS_DATA_UPPER: return true; default: return false; @@ -1361,11 +1398,11 @@ static bool ltr501_is_volatile_reg(struct device *dev, unsigned int reg) } static const struct regmap_config ltr501_regmap_config = { - .name = LTR501_REGMAP_NAME, + .name = "ltr501_regmap", .reg_bits = 8, .val_bits = 8, .max_register = LTR501_MAX_REG, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = ltr501_is_volatile_reg, }; @@ -1376,25 +1413,17 @@ static int ltr501_powerdown(struct ltr501_data *data) data->ps_contr & ~LTR501_CONTR_ACTIVE); } -static const char *ltr501_match_acpi_device(struct device *dev, int *chip_idx) -{ - const struct acpi_device_id *id; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return NULL; - *chip_idx = id->driver_data; - return dev_name(dev); -} - -static int ltr501_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ltr501_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); + static const char * const regulator_names[] = { "vdd", "vddio" }; struct ltr501_data *data; struct iio_dev *indio_dev; struct regmap *regmap; - int ret, partid, chip_idx = 0; - const char *name = NULL; + const void *ddata = NULL; + int partid, chip_idx; + const char *name; + int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -1413,6 +1442,13 @@ static int ltr501_probe(struct i2c_client *client, mutex_init(&data->lock_als); mutex_init(&data->lock_ps); + ret = devm_regulator_bulk_get_enable(&client->dev, + ARRAY_SIZE(regulator_names), + regulator_names); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to get regulators\n"); + data->reg_it = devm_regmap_field_alloc(&client->dev, regmap, reg_field_it); if (IS_ERR(data->reg_it)) { @@ -1469,18 +1505,22 @@ static int ltr501_probe(struct i2c_client *client, if (id) { name = id->name; chip_idx = id->driver_data; - } else if (ACPI_HANDLE(&client->dev)) { - name = ltr501_match_acpi_device(&client->dev, &chip_idx); } else { - return -ENODEV; + name = iio_get_acpi_device_name_and_data(&client->dev, &ddata); + chip_idx = (intptr_t)ddata; } + if (!name) + return -ENODEV; data->chip_info = <r501_chip_info_tbl[chip_idx]; if ((partid >> 4) != data->chip_info->partid) return -ENODEV; - indio_dev->dev.parent = &client->dev; + if (device_property_read_u32(&client->dev, "proximity-near-level", + &data->near_level)) + data->near_level = 0; + indio_dev->info = data->chip_info->info; indio_dev->channels = data->chip_info->channels; indio_dev->num_channels = data->chip_info->no_channels; @@ -1525,18 +1565,15 @@ powerdown_on_error: return ret; } -static int ltr501_remove(struct i2c_client *client) +static void ltr501_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); ltr501_powerdown(iio_priv(indio_dev)); - - return 0; } -#ifdef CONFIG_PM_SLEEP static int ltr501_suspend(struct device *dev) { struct ltr501_data *data = iio_priv(i2c_get_clientdata( @@ -1552,33 +1589,43 @@ static int ltr501_resume(struct device *dev) return ltr501_write_contr(data, data->als_contr, data->ps_contr); } -#endif -static SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume); static const struct acpi_device_id ltr_acpi_match[] = { - {"LTER0501", ltr501}, - {"LTER0559", ltr559}, - {"LTER0301", ltr301}, - { }, + { "LTER0301", ltr301 }, + /* https://www.catalog.update.microsoft.com/Search.aspx?q=lter0303 */ + { "LTER0303", ltr303 }, + { } }; MODULE_DEVICE_TABLE(acpi, ltr_acpi_match); static const struct i2c_device_id ltr501_id[] = { - { "ltr501", ltr501}, - { "ltr559", ltr559}, - { "ltr301", ltr301}, + { "ltr501", ltr501 }, + { "ltr559", ltr559 }, + { "ltr301", ltr301 }, + { "ltr303", ltr303 }, { } }; MODULE_DEVICE_TABLE(i2c, ltr501_id); +static const struct of_device_id ltr501_of_match[] = { + { .compatible = "liteon,ltr501", }, + { .compatible = "liteon,ltr559", }, + { .compatible = "liteon,ltr301", }, + { .compatible = "liteon,ltr303", }, + { } +}; +MODULE_DEVICE_TABLE(of, ltr501_of_match); + static struct i2c_driver ltr501_driver = { .driver = { - .name = LTR501_DRV_NAME, - .pm = <r501_pm_ops, - .acpi_match_table = ACPI_PTR(ltr_acpi_match), + .name = "ltr501", + .of_match_table = ltr501_of_match, + .pm = pm_sleep_ptr(<r501_pm_ops), + .acpi_match_table = ltr_acpi_match, }, - .probe = ltr501_probe, + .probe = ltr501_probe, .remove = ltr501_remove, .id_table = ltr501_id, }; |
