summaryrefslogtreecommitdiffstats
path: root/drivers/iio/light/tsl2772.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-10-29 18:38:10 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2018-10-29 18:38:10 +0100
commit738b04fba18d35cd352b7b15afefb8a7b798648e (patch)
tree07fabb1a920af5c92bd35e10f9821cf56c8de3e4 /drivers/iio/light/tsl2772.c
parentMerge tag 'mailbox-v4.20' of git://git.linaro.org/landing-teams/working/fujit... (diff)
parentstaging: gasket: Fix sparse "incorrect type in assignment" warnings. (diff)
downloadlinux-738b04fba18d35cd352b7b15afefb8a7b798648e.tar.xz
linux-738b04fba18d35cd352b7b15afefb8a7b798648e.zip
Merge tag 'staging-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging/IIO driver updates from Greg KH: "Here is the big staging and IIO driver pull request for 4.20-rc1. There are lots of things here, we ended up adding more lines than removing, thanks to a large influx of Comedi National Instrument device support. Someday soon we need to get comedi out of staging... Other than the comedi drivers, the "big" things here are: - new iio drivers - delete dgnc driver (no one used it and no one had the hardware anymore) - vbox driver updates and fixes - erofs fixes - tons and tons of tiny checkpatch fixes for almost all staging drivers All of these have been in linux-next, with the last few happening a bit "late" due to them getting stuck on my laptop during travel to the Mantainers summit" * tag 'staging-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (690 commits) staging: gasket: Fix sparse "incorrect type in assignment" warnings. staging: gasket: remove debug logs for callback invocation staging: gasket: remove debug logs in page table mapping calls staging: rtl8188eu: core: Use sizeof(*p) instead of sizeof(struct P) for memory allocation staging: ks7010: Remove extra blank line staging: gasket: Remove extra blank line staging: media: davinci_vpfe: Fix spelling mistake in enum staging: speakup: Add a pair of braces staging: wlan-ng: Replace long int with long staging: MAINTAINERS: remove obsolete IPX staging directory staging: MAINTAINERS: remove NCP filesystem entry staging: rtl8188eu: cleanup comparsions to false staging: gasket: Update device virtual address comment staging: gasket: sysfs: fix attribute release comment staging: gasket: apex: fix sysfs_show staging: gasket: page_table: simplify gasket_components_to_dev_address staging: gasket: page_table: fix comment in components_to_dev_address staging: gasket: page table: fixup error path allocating coherent mem staging: gasket: page_table: rearrange gasket_page_table_entry staging: gasket: page_table: remove unnecessary PTE status set to free ...
Diffstat (limited to 'drivers/iio/light/tsl2772.c')
-rw-r--r--drivers/iio/light/tsl2772.c194
1 files changed, 191 insertions, 3 deletions
diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c
index df5b2a0da96c..83cece921843 100644
--- a/drivers/iio/light/tsl2772.c
+++ b/drivers/iio/light/tsl2772.c
@@ -20,6 +20,7 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/platform_data/tsl2772.h>
+#include <linux/regulator/consumer.h>
/* Cal defs */
#define PROX_STAT_CAL 0
@@ -107,6 +108,11 @@
#define TSL2772_ALS_GAIN_TRIM_MIN 250
#define TSL2772_ALS_GAIN_TRIM_MAX 4000
+#define TSL2772_MAX_PROX_LEDS 2
+
+#define TSL2772_BOOT_MIN_SLEEP_TIME 10000
+#define TSL2772_BOOT_MAX_SLEEP_TIME 28000
+
/* Device family members */
enum {
tsl2571,
@@ -118,7 +124,8 @@ enum {
tsl2672,
tmd2672,
tsl2772,
- tmd2772
+ tmd2772,
+ apds9930,
};
enum {
@@ -141,11 +148,21 @@ struct tsl2772_chip_info {
const struct iio_info *info;
};
+static const int tsl2772_led_currents[][2] = {
+ { 100000, TSL2772_100_mA },
+ { 50000, TSL2772_50_mA },
+ { 25000, TSL2772_25_mA },
+ { 13000, TSL2772_13_mA },
+ { 0, 0 }
+};
+
struct tsl2772_chip {
kernel_ulong_t id;
struct mutex prox_mutex;
struct mutex als_mutex;
struct i2c_client *client;
+ struct regulator *vdd_supply;
+ struct regulator *vddio_supply;
u16 prox_data;
struct tsl2772_als_info als_cur_info;
struct tsl2772_settings settings;
@@ -197,6 +214,12 @@ static const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
{ 0, 0 },
};
+static const struct tsl2772_lux apds9930_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
+ { 52000, 96824 },
+ { 38792, 67132 },
+ { 0, 0 },
+};
+
static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = {
[tsl2571] = tsl2x71_lux_table,
[tsl2671] = tsl2x71_lux_table,
@@ -208,6 +231,7 @@ static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = {
[tmd2672] = tmd2x72_lux_table,
[tsl2772] = tsl2x72_lux_table,
[tmd2772] = tmd2x72_lux_table,
+ [apds9930] = apds9930_lux_table,
};
static const struct tsl2772_settings tsl2772_default_settings = {
@@ -258,6 +282,7 @@ static const int tsl2772_int_time_avail[][6] = {
[tmd2672] = { 0, 2730, 0, 2730, 0, 699000 },
[tsl2772] = { 0, 2730, 0, 2730, 0, 699000 },
[tmd2772] = { 0, 2730, 0, 2730, 0, 699000 },
+ [apds9930] = { 0, 2730, 0, 2730, 0, 699000 },
};
static int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 };
@@ -283,7 +308,8 @@ static const u8 device_channel_config[] = {
[tsl2672] = PRX2,
[tmd2672] = PRX2,
[tsl2772] = ALSPRX2,
- [tmd2772] = ALSPRX2
+ [tmd2772] = ALSPRX2,
+ [apds9930] = ALSPRX2,
};
static int tsl2772_read_status(struct tsl2772_chip *chip)
@@ -497,6 +523,7 @@ static int tsl2772_get_prox(struct iio_dev *indio_dev)
case tmd2672:
case tsl2772:
case tmd2772:
+ case apds9930:
if (!(ret & TSL2772_STA_PRX_VALID)) {
ret = -EINVAL;
goto prox_poll_err;
@@ -515,6 +542,75 @@ prox_poll_err:
return ret;
}
+static int tsl2772_read_prox_led_current(struct tsl2772_chip *chip)
+{
+ struct device_node *of_node = chip->client->dev.of_node;
+ int ret, tmp, i;
+
+ ret = of_property_read_u32(of_node, "led-max-microamp", &tmp);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; tsl2772_led_currents[i][0] != 0; i++) {
+ if (tmp == tsl2772_led_currents[i][0]) {
+ chip->settings.prox_power = tsl2772_led_currents[i][1];
+ return 0;
+ }
+ }
+
+ dev_err(&chip->client->dev, "Invalid value %d for led-max-microamp\n",
+ tmp);
+
+ return -EINVAL;
+
+}
+
+static int tsl2772_read_prox_diodes(struct tsl2772_chip *chip)
+{
+ struct device_node *of_node = chip->client->dev.of_node;
+ int i, ret, num_leds, prox_diode_mask;
+ u32 leds[TSL2772_MAX_PROX_LEDS];
+
+ ret = of_property_count_u32_elems(of_node, "amstaos,proximity-diodes");
+ if (ret < 0)
+ return ret;
+
+ num_leds = ret;
+ if (num_leds > TSL2772_MAX_PROX_LEDS)
+ num_leds = TSL2772_MAX_PROX_LEDS;
+
+ ret = of_property_read_u32_array(of_node, "amstaos,proximity-diodes",
+ leds, num_leds);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "Invalid value for amstaos,proximity-diodes: %d.\n",
+ ret);
+ return ret;
+ }
+
+ prox_diode_mask = 0;
+ for (i = 0; i < num_leds; i++) {
+ if (leds[i] == 0)
+ prox_diode_mask |= TSL2772_DIODE0;
+ else if (leds[i] == 1)
+ prox_diode_mask |= TSL2772_DIODE1;
+ else {
+ dev_err(&chip->client->dev,
+ "Invalid value %d in amstaos,proximity-diodes.\n",
+ leds[i]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void tsl2772_parse_dt(struct tsl2772_chip *chip)
+{
+ tsl2772_read_prox_led_current(chip);
+ tsl2772_read_prox_diodes(chip);
+}
+
/**
* tsl2772_defaults() - Populates the device nominal operating parameters
* with those provided by a 'platform' data struct or
@@ -541,6 +637,8 @@ static void tsl2772_defaults(struct tsl2772_chip *chip)
memcpy(chip->tsl2772_device_lux,
tsl2772_default_lux_table_group[chip->id],
TSL2772_DEFAULT_TABLE_BYTES);
+
+ tsl2772_parse_dt(chip);
}
/**
@@ -595,6 +693,52 @@ static int tsl2772_als_calibrate(struct iio_dev *indio_dev)
return ret;
}
+static void tsl2772_disable_regulators_action(void *_data)
+{
+ struct tsl2772_chip *chip = _data;
+
+ regulator_disable(chip->vdd_supply);
+ regulator_disable(chip->vddio_supply);
+}
+
+static int tsl2772_enable_regulator(struct tsl2772_chip *chip,
+ struct regulator *regulator)
+{
+ int ret;
+
+ ret = regulator_enable(regulator);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "Failed to enable regulator: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct regulator *tsl2772_get_regulator(struct tsl2772_chip *chip,
+ char *name)
+{
+ struct regulator *regulator;
+ int ret;
+
+ regulator = devm_regulator_get(&chip->client->dev, name);
+ if (IS_ERR(regulator)) {
+ if (PTR_ERR(regulator) != -EPROBE_DEFER)
+ dev_err(&chip->client->dev,
+ "Failed to get %s regulator %d\n",
+ name, (int)PTR_ERR(regulator));
+
+ return regulator;
+ }
+
+ ret = tsl2772_enable_regulator(chip, regulator);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return regulator;
+}
+
static int tsl2772_chip_on(struct iio_dev *indio_dev)
{
struct tsl2772_chip *chip = iio_priv(indio_dev);
@@ -1260,6 +1404,7 @@ static int tsl2772_device_id_verif(int id, int target)
case tmd2672:
case tsl2772:
case tmd2772:
+ case apds9930:
return (id & 0xf0) == SWORDFISH_ID;
}
@@ -1652,6 +1797,27 @@ static int tsl2772_probe(struct i2c_client *clientp,
chip->client = clientp;
i2c_set_clientdata(clientp, indio_dev);
+ chip->vddio_supply = tsl2772_get_regulator(chip, "vddio");
+ if (IS_ERR(chip->vddio_supply))
+ return PTR_ERR(chip->vddio_supply);
+
+ chip->vdd_supply = tsl2772_get_regulator(chip, "vdd");
+ if (IS_ERR(chip->vdd_supply)) {
+ regulator_disable(chip->vddio_supply);
+ return PTR_ERR(chip->vdd_supply);
+ }
+
+ ret = devm_add_action(&clientp->dev, tsl2772_disable_regulators_action,
+ chip);
+ if (ret < 0) {
+ tsl2772_disable_regulators_action(chip);
+ dev_err(&clientp->dev, "Failed to setup regulator cleanup action %d\n",
+ ret);
+ return ret;
+ }
+
+ usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME);
+
ret = i2c_smbus_read_byte_data(chip->client,
TSL2772_CMD_REG | TSL2772_CHIPID);
if (ret < 0)
@@ -1725,13 +1891,33 @@ static int tsl2772_probe(struct i2c_client *clientp,
static int tsl2772_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2772_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ ret = tsl2772_chip_off(indio_dev);
+ regulator_disable(chip->vdd_supply);
+ regulator_disable(chip->vddio_supply);
- return tsl2772_chip_off(indio_dev);
+ return ret;
}
static int tsl2772_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2772_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ ret = tsl2772_enable_regulator(chip, chip->vddio_supply);
+ if (ret < 0)
+ return ret;
+
+ ret = tsl2772_enable_regulator(chip, chip->vdd_supply);
+ if (ret < 0) {
+ regulator_disable(chip->vddio_supply);
+ return ret;
+ }
+
+ usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME);
return tsl2772_chip_on(indio_dev);
}
@@ -1758,6 +1944,7 @@ static const struct i2c_device_id tsl2772_idtable[] = {
{ "tmd2672", tmd2672 },
{ "tsl2772", tsl2772 },
{ "tmd2772", tmd2772 },
+ { "apds9930", apds9930},
{}
};
@@ -1774,6 +1961,7 @@ static const struct of_device_id tsl2772_of_match[] = {
{ .compatible = "amstaos,tmd2672" },
{ .compatible = "amstaos,tsl2772" },
{ .compatible = "amstaos,tmd2772" },
+ { .compatible = "avago,apds9930" },
{}
};
MODULE_DEVICE_TABLE(of, tsl2772_of_match);