diff options
Diffstat (limited to 'drivers/iio/light/ltr501.c')
-rw-r--r-- | drivers/iio/light/ltr501.c | 185 |
1 files changed, 134 insertions, 51 deletions
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 71f99d2a22c1..74a1ccda8b9c 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> * @@ -15,6 +15,7 @@ #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> @@ -32,9 +33,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 */ @@ -94,6 +98,7 @@ enum { ltr501 = 0, ltr559, ltr301, + ltr303, }; struct ltr501_gain { @@ -101,12 +106,12 @@ struct ltr501_gain { int uscale; }; -static struct ltr501_gain ltr501_als_gain_tbl[] = { +static const struct ltr501_gain ltr501_als_gain_tbl[] = { {1, 0}, {0, 5000}, }; -static struct ltr501_gain ltr559_als_gain_tbl[] = { +static const struct ltr501_gain ltr559_als_gain_tbl[] = { {1, 0}, {0, 500000}, {0, 250000}, @@ -117,14 +122,14 @@ static struct ltr501_gain ltr559_als_gain_tbl[] = { {0, 10000}, }; -static struct ltr501_gain ltr501_ps_gain_tbl[] = { +static const struct ltr501_gain ltr501_ps_gain_tbl[] = { {1, 0}, {0, 250000}, {0, 125000}, {0, 62500}, }; -static struct ltr501_gain ltr559_ps_gain_tbl[] = { +static const struct ltr501_gain ltr559_ps_gain_tbl[] = { {0, 62500}, /* x16 gain */ {0, 31250}, /* x32 gain */ {0, 15625}, /* bits X1 are for x64 gain */ @@ -133,9 +138,9 @@ static struct ltr501_gain ltr559_ps_gain_tbl[] = { struct ltr501_chip_info { u8 partid; - struct ltr501_gain *als_gain; + const struct ltr501_gain *als_gain; int als_gain_tbl_size; - struct ltr501_gain *ps_gain; + const struct ltr501_gain *ps_gain; int ps_gain_tbl_size; u8 als_mode_active; u8 als_gain_mask; @@ -148,8 +153,9 @@ struct ltr501_chip_info { struct ltr501_data { struct i2c_client *client; + struct regulator_bulk_data regulators[2]; 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 +166,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[] = { @@ -192,7 +199,7 @@ static int ltr501_match_samp_freq(const struct ltr501_samp_table *tab, return -EINVAL; } -static int ltr501_als_read_samp_freq(struct ltr501_data *data, +static int ltr501_als_read_samp_freq(const struct ltr501_data *data, int *val, int *val2) { int ret, i; @@ -210,7 +217,7 @@ static int ltr501_als_read_samp_freq(struct ltr501_data *data, return IIO_VAL_INT_PLUS_MICRO; } -static int ltr501_ps_read_samp_freq(struct ltr501_data *data, +static int ltr501_ps_read_samp_freq(const struct ltr501_data *data, int *val, int *val2) { int ret, i; @@ -266,7 +273,7 @@ static int ltr501_ps_write_samp_freq(struct ltr501_data *data, return ret; } -static int ltr501_als_read_samp_period(struct ltr501_data *data, int *val) +static int ltr501_als_read_samp_period(const struct ltr501_data *data, int *val) { int ret, i; @@ -282,7 +289,7 @@ static int ltr501_als_read_samp_period(struct ltr501_data *data, int *val) return IIO_VAL_INT; } -static int ltr501_ps_read_samp_period(struct ltr501_data *data, int *val) +static int ltr501_ps_read_samp_period(const struct ltr501_data *data, int *val) { int ret, i; @@ -321,7 +328,7 @@ static unsigned long ltr501_calculate_lux(u16 vis_data, u16 ir_data) return lux / 1000; } -static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask) +static int ltr501_drdy(const struct ltr501_data *data, u8 drdy_mask) { int tries = 100; int ret, status; @@ -373,7 +380,8 @@ static int ltr501_set_it_time(struct ltr501_data *data, int it) } /* read int time in micro seconds */ -static int ltr501_read_it_time(struct ltr501_data *data, int *val, int *val2) +static int ltr501_read_it_time(const struct ltr501_data *data, + int *val, int *val2) { int ret, index; @@ -391,7 +399,7 @@ static int ltr501_read_it_time(struct ltr501_data *data, int *val, int *val2) return IIO_VAL_INT_PLUS_MICRO; } -static int ltr501_read_als(struct ltr501_data *data, __le16 buf[2]) +static int ltr501_read_als(const struct ltr501_data *data, __le16 buf[2]) { int ret; @@ -403,23 +411,24 @@ static int ltr501_read_als(struct ltr501_data *data, __le16 buf[2]) buf, 2 * sizeof(__le16)); } -static int ltr501_read_ps(struct ltr501_data *data) +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(struct ltr501_data *data, +static int ltr501_read_intr_prst(const struct ltr501_data *data, enum iio_chan_type type, int *val2) { @@ -517,6 +526,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, + }, + { /* sentinel */ } +}; + static const struct iio_event_spec ltr501_als_event_spec[] = { { .type = IIO_EV_TYPE_THRESH, @@ -601,6 +629,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), }; @@ -716,7 +745,7 @@ static int ltr501_read_raw(struct iio_dev *indio_dev, return -EINVAL; } -static int ltr501_get_gain_index(struct ltr501_gain *gain, int size, +static int ltr501_get_gain_index(const struct ltr501_gain *gain, int size, int val, int val2) { int i; @@ -734,7 +763,7 @@ static int ltr501_write_raw(struct iio_dev *indio_dev, { struct ltr501_data *data = iio_priv(indio_dev); int i, ret, freq_val, freq_val2; - struct ltr501_chip_info *info = data->chip_info; + const struct ltr501_chip_info *info = data->chip_info; ret = iio_device_claim_direct_mode(indio_dev); if (ret) @@ -848,14 +877,14 @@ static int ltr501_write_raw(struct iio_dev *indio_dev, return ret; } -static int ltr501_read_thresh(struct iio_dev *indio_dev, +static int ltr501_read_thresh(const struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, enum iio_event_info info, int *val, int *val2) { - struct ltr501_data *data = iio_priv(indio_dev); + const struct ltr501_data *data = iio_priv(indio_dev); int ret, thresh_data; switch (chan->type) { @@ -1081,7 +1110,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; @@ -1103,7 +1132,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; @@ -1183,7 +1212,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, @@ -1204,7 +1233,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, @@ -1224,6 +1253,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) @@ -1242,13 +1283,16 @@ 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]; + s64 ts __aligned(8); + } scan; __le16 als_buf[2]; u8 mask = 0; int j = 0; int ret, psdata; - memset(buf, 0, sizeof(buf)); + memset(&scan, 0, sizeof(scan)); /* figure out which data needs to be ready */ if (test_bit(0, indio_dev->active_scan_mask) || @@ -1263,13 +1307,13 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p) if (mask & LTR501_STATUS_ALS_RDY) { ret = regmap_bulk_read(data->regmap, LTR501_ALS_DATA1, - (u8 *)als_buf, sizeof(als_buf)); + 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) { @@ -1277,10 +1321,10 @@ 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_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); done: @@ -1350,16 +1394,19 @@ 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; } } -static struct regmap_config ltr501_regmap_config = { +static const struct regmap_config ltr501_regmap_config = { .name = LTR501_REGMAP_NAME, .reg_bits = 8, .val_bits = 8, @@ -1368,6 +1415,13 @@ static struct regmap_config ltr501_regmap_config = { .volatile_reg = ltr501_is_volatile_reg, }; +static void ltr501_disable_regulators(void *d) +{ + struct ltr501_data *data = d; + + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); +} + static int ltr501_powerdown(struct ltr501_data *data) { return ltr501_write_contr(data, data->als_contr & @@ -1412,6 +1466,25 @@ static int ltr501_probe(struct i2c_client *client, mutex_init(&data->lock_als); mutex_init(&data->lock_ps); + data->regulators[0].supply = "vdd"; + data->regulators[1].supply = "vddio"; + ret = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators), + data->regulators); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&client->dev, + ltr501_disable_regulators, data); + if (ret) + return ret; + data->reg_it = devm_regmap_field_alloc(&client->dev, regmap, reg_field_it); if (IS_ERR(data->reg_it)) { @@ -1479,7 +1552,10 @@ static int ltr501_probe(struct i2c_client *client, 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; @@ -1524,18 +1600,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( @@ -1551,30 +1624,40 @@ 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}, + { "LTER0501", ltr501 }, + { "LTER0559", ltr559 }, + { "LTER0301", ltr301 }, { }, }; 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, + .of_match_table = ltr501_of_match, + .pm = pm_sleep_ptr(<r501_pm_ops), .acpi_match_table = ACPI_PTR(ltr_acpi_match), }, .probe = ltr501_probe, |