diff options
Diffstat (limited to 'drivers/iio/light')
-rw-r--r-- | drivers/iio/light/Kconfig | 24 | ||||
-rw-r--r-- | drivers/iio/light/Makefile | 2 | ||||
-rw-r--r-- | drivers/iio/light/cm32181.c | 2 | ||||
-rw-r--r-- | drivers/iio/light/cm3232.c | 403 | ||||
-rw-r--r-- | drivers/iio/light/hid-sensor-als.c | 9 | ||||
-rw-r--r-- | drivers/iio/light/hid-sensor-prox.c | 10 | ||||
-rw-r--r-- | drivers/iio/light/jsa1212.c | 471 | ||||
-rw-r--r-- | drivers/iio/light/lm3533-als.c | 2 | ||||
-rw-r--r-- | drivers/iio/light/tcs3414.c | 4 |
9 files changed, 906 insertions, 21 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 5bea821adca..ae68c64bdad 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -48,6 +48,17 @@ config CM32181 To compile this driver as a module, choose M here: the module will be called cm32181. +config CM3232 + depends on I2C + tristate "CM3232 ambient light sensor" + help + Say Y here if you use cm3232. + This option enables ambient light sensor using + Capella Microsystems cm3232 device driver. + + To compile this driver as a module, choose M here: + the module will be called cm3232. + config CM36651 depends on I2C tristate "CM36651 driver" @@ -95,6 +106,9 @@ config HID_SENSOR_ALS Say yes here to build support for the HID SENSOR Ambient light sensor. + To compile this driver as a module, choose M here: the + module will be called hid-sensor-als. + config HID_SENSOR_PROX depends on HID_SENSOR_HUB select IIO_BUFFER @@ -109,6 +123,16 @@ config HID_SENSOR_PROX To compile this driver as a module, choose M here: the module will be called hid-sensor-prox. +config JSA1212 + tristate "JSA1212 ALS and proximity sensor driver" + depends on I2C + help + Say Y here if you want to build a IIO driver for JSA1212 + proximity & ALS sensor device. + + To compile this driver as a module, choose M here: + the module will be called jsa1212. + config SENSORS_LM3533 tristate "LM3533 ambient light sensor" depends on MFD_LM3533 diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 47877a36cc1..b12a5160d9e 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -7,11 +7,13 @@ obj-$(CONFIG_ADJD_S311) += adjd_s311.o obj-$(CONFIG_AL3320A) += al3320a.o obj-$(CONFIG_APDS9300) += apds9300.o obj-$(CONFIG_CM32181) += cm32181.o +obj-$(CONFIG_CM3232) += cm3232.o obj-$(CONFIG_CM36651) += cm36651.o obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o obj-$(CONFIG_ISL29125) += isl29125.o +obj-$(CONFIG_JSA1212) += jsa1212.o obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o obj-$(CONFIG_LTR501) += ltr501.o obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index ad36b294e4d..5d12ae54d08 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -169,7 +169,7 @@ static int cm32181_write_als_it(struct cm32181_chip *cm32181, int val) * @cm32181: pointer of struct cm32181. * * Convert sensor raw data to lux. It depends on integration - * time and claibscale variable. + * time and calibscale variable. * * Return: Positive value is lux, otherwise is error code. */ diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c new file mode 100644 index 00000000000..90e3519a91d --- /dev/null +++ b/drivers/iio/light/cm3232.c @@ -0,0 +1,403 @@ +/* + * CM3232 Ambient Light Sensor + * + * Copyright (C) 2014-2015 Capella Microsystems Inc. + * Author: Kevin Tsai <ktsai@capellamicro.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2, as published + * by the Free Software Foundation. + * + * IIO driver for CM3232 (7-bit I2C slave address 0x10). + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/init.h> + +/* Registers Address */ +#define CM3232_REG_ADDR_CMD 0x00 +#define CM3232_REG_ADDR_ALS 0x50 +#define CM3232_REG_ADDR_ID 0x53 + +#define CM3232_CMD_ALS_DISABLE BIT(0) + +#define CM3232_CMD_ALS_IT_SHIFT 2 +#define CM3232_CMD_ALS_IT_MASK (BIT(2) | BIT(3) | BIT(4)) +#define CM3232_CMD_ALS_IT_DEFAULT (0x01 << CM3232_CMD_ALS_IT_SHIFT) + +#define CM3232_CMD_ALS_RESET BIT(6) + +#define CM3232_CMD_DEFAULT CM3232_CMD_ALS_IT_DEFAULT + +#define CM3232_HW_ID 0x32 +#define CM3232_CALIBSCALE_DEFAULT 100000 +#define CM3232_CALIBSCALE_RESOLUTION 100000 +#define CM3232_MLUX_PER_LUX 1000 + +#define CM3232_MLUX_PER_BIT_DEFAULT 64 +#define CM3232_MLUX_PER_BIT_BASE_IT 100000 + +static const struct { + int val; + int val2; + u8 it; +} cm3232_als_it_scales[] = { + {0, 100000, 0}, /* 0.100000 */ + {0, 200000, 1}, /* 0.200000 */ + {0, 400000, 2}, /* 0.400000 */ + {0, 800000, 3}, /* 0.800000 */ + {1, 600000, 4}, /* 1.600000 */ + {3, 200000, 5}, /* 3.200000 */ +}; + +struct cm3232_als_info { + u8 regs_cmd_default; + u8 hw_id; + int calibscale; + int mlux_per_bit; + int mlux_per_bit_base_it; +}; + +static struct cm3232_als_info cm3232_als_info_default = { + .regs_cmd_default = CM3232_CMD_DEFAULT, + .hw_id = CM3232_HW_ID, + .calibscale = CM3232_CALIBSCALE_DEFAULT, + .mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT, + .mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT, +}; + +struct cm3232_chip { + struct i2c_client *client; + struct cm3232_als_info *als_info; + u8 regs_cmd; + u16 regs_als; +}; + +/** + * cm3232_reg_init() - Initialize CM3232 + * @chip: pointer of struct cm3232_chip. + * + * Check and initialize CM3232 ambient light sensor. + * + * Return: 0 for success; otherwise for error code. + */ +static int cm3232_reg_init(struct cm3232_chip *chip) +{ + struct i2c_client *client = chip->client; + s32 ret; + + chip->als_info = &cm3232_als_info_default; + + /* Identify device */ + ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID); + if (ret < 0) { + dev_err(&chip->client->dev, "Error reading addr_id\n"); + return ret; + } + + if ((ret & 0xFF) != chip->als_info->hw_id) + return -ENODEV; + + /* Disable and reset device */ + chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET; + ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, + chip->regs_cmd); + if (ret < 0) { + dev_err(&chip->client->dev, "Error writing reg_cmd\n"); + return ret; + } + + /* Register default value */ + chip->regs_cmd = chip->als_info->regs_cmd_default; + + /* Configure register */ + ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, + chip->regs_cmd); + if (ret < 0) + dev_err(&chip->client->dev, "Error writing reg_cmd\n"); + + return 0; +} + +/** + * cm3232_read_als_it() - Get sensor integration time + * @chip: pointer of struct cm3232_chip + * @val: pointer of int to load the integration (sec). + * @val2: pointer of int to load the integration time (microsecond). + * + * Report the current integration time. + * + * Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL. + */ +static int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2) +{ + u16 als_it; + int i; + + als_it = chip->regs_cmd; + als_it &= CM3232_CMD_ALS_IT_MASK; + als_it >>= CM3232_CMD_ALS_IT_SHIFT; + for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) { + if (als_it == cm3232_als_it_scales[i].it) { + *val = cm3232_als_it_scales[i].val; + *val2 = cm3232_als_it_scales[i].val2; + return IIO_VAL_INT_PLUS_MICRO; + } + } + + return -EINVAL; +} + +/** + * cm3232_write_als_it() - Write sensor integration time + * @chip: pointer of struct cm3232_chip. + * @val: integration time in second. + * @val2: integration time in microsecond. + * + * Convert integration time to sensor value. + * + * Return: i2c_smbus_write_byte_data command return value. + */ +static int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2) +{ + struct i2c_client *client = chip->client; + u16 als_it, cmd; + int i; + s32 ret; + + for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) { + if (val == cm3232_als_it_scales[i].val && + val2 == cm3232_als_it_scales[i].val2) { + + als_it = cm3232_als_it_scales[i].it; + als_it <<= CM3232_CMD_ALS_IT_SHIFT; + + cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK; + cmd |= als_it; + ret = i2c_smbus_write_byte_data(client, + CM3232_REG_ADDR_CMD, + cmd); + if (ret < 0) + return ret; + chip->regs_cmd = cmd; + return 0; + } + } + return -EINVAL; +} + +/** + * cm3232_get_lux() - report current lux value + * @chip: pointer of struct cm3232_chip. + * + * Convert sensor data to lux. It depends on integration + * time and calibscale variable. + * + * Return: Zero or positive value is lux, otherwise error code. + */ +static int cm3232_get_lux(struct cm3232_chip *chip) +{ + struct i2c_client *client = chip->client; + struct cm3232_als_info *als_info = chip->als_info; + int ret; + int val, val2; + int als_it; + u64 lux; + + /* Calculate mlux per bit based on als_it */ + ret = cm3232_read_als_it(chip, &val, &val2); + if (ret < 0) + return -EINVAL; + als_it = val * 1000000 + val2; + lux = (__force u64)als_info->mlux_per_bit; + lux *= als_info->mlux_per_bit_base_it; + lux = div_u64(lux, als_it); + + ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS); + if (ret < 0) { + dev_err(&client->dev, "Error reading reg_addr_als\n"); + return ret; + } + + chip->regs_als = (u16)ret; + lux *= chip->regs_als; + lux *= als_info->calibscale; + lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION); + lux = div_u64(lux, CM3232_MLUX_PER_LUX); + + if (lux > 0xFFFF) + lux = 0xFFFF; + + return (int)lux; +} + +static int cm3232_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cm3232_chip *chip = iio_priv(indio_dev); + struct cm3232_als_info *als_info = chip->als_info; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret = cm3232_get_lux(chip); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + *val = als_info->calibscale; + return IIO_VAL_INT; + case IIO_CHAN_INFO_INT_TIME: + return cm3232_read_als_it(chip, val, val2); + } + + return -EINVAL; +} + +static int cm3232_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct cm3232_chip *chip = iio_priv(indio_dev); + struct cm3232_als_info *als_info = chip->als_info; + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + als_info->calibscale = val; + return 0; + case IIO_CHAN_INFO_INT_TIME: + return cm3232_write_als_it(chip, val, val2); + } + + return -EINVAL; +} + +/** + * cm3232_get_it_available() - Get available ALS IT value + * @dev: pointer of struct device. + * @attr: pointer of struct device_attribute. + * @buf: pointer of return string buffer. + * + * Display the available integration time in second. + * + * Return: string length. + */ +static ssize_t cm3232_get_it_available(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, len; + + for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ", + cm3232_als_it_scales[i].val, + cm3232_als_it_scales[i].val2); + return len + scnprintf(buf + len, PAGE_SIZE - len, "\n"); +} + +static const struct iio_chan_spec cm3232_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = + BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_INT_TIME), + } +}; + +static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, + S_IRUGO, cm3232_get_it_available, NULL, 0); + +static struct attribute *cm3232_attributes[] = { + &iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group cm3232_attribute_group = { + .attrs = cm3232_attributes +}; + +static const struct iio_info cm3232_info = { + .driver_module = THIS_MODULE, + .read_raw = &cm3232_read_raw, + .write_raw = &cm3232_write_raw, + .attrs = &cm3232_attribute_group, +}; + +static int cm3232_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cm3232_chip *chip; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + chip->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->channels = cm3232_channels; + indio_dev->num_channels = ARRAY_SIZE(cm3232_channels); + indio_dev->info = &cm3232_info; + indio_dev->name = id->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = cm3232_reg_init(chip); + if (ret) { + dev_err(&client->dev, + "%s: register init failed\n", + __func__); + return ret; + } + + return iio_device_register(indio_dev); +} + +static int cm3232_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, + CM3232_CMD_ALS_DISABLE); + + iio_device_unregister(indio_dev); + + return 0; +} + +static const struct i2c_device_id cm3232_id[] = { + {"cm3232", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cm3232_id); + +static const struct of_device_id cm3232_of_match[] = { + {.compatible = "capella,cm3232"}, + {} +}; + +static struct i2c_driver cm3232_driver = { + .driver = { + .name = "cm3232", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(cm3232_of_match), + }, + .id_table = cm3232_id, + .probe = cm3232_probe, + .remove = cm3232_remove, +}; + +module_i2c_driver(cm3232_driver); + +MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>"); +MODULE_DESCRIPTION("CM3232 ambient light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index a5283d75c09..948acfc38b8 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -80,7 +80,6 @@ static int als_read_raw(struct iio_dev *indio_dev, int report_id = -1; u32 address; int ret_type; - s32 poll_value; *val = 0; *val2 = 0; @@ -97,15 +96,8 @@ static int als_read_raw(struct iio_dev *indio_dev, break; } if (report_id >= 0) { - poll_value = hid_sensor_read_poll_value( - &als_state->common_attributes); - if (poll_value < 0) - return -EINVAL; - hid_sensor_power_state(&als_state->common_attributes, true); - msleep_interruptible(poll_value * 2); - *val = sensor_hub_input_attr_get_raw_value( als_state->common_attributes.hsdev, HID_USAGE_SENSOR_ALS, address, @@ -381,6 +373,7 @@ static struct platform_driver hid_als_platform_driver = { .id_table = hid_als_ids, .driver = { .name = KBUILD_MODNAME, + .pm = &hid_sensor_pm_ops, }, .probe = hid_als_probe, .remove = hid_als_remove, diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index f5a514698fd..3ecf79ed08a 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -75,7 +75,6 @@ static int prox_read_raw(struct iio_dev *indio_dev, int report_id = -1; u32 address; int ret_type; - s32 poll_value; *val = 0; *val2 = 0; @@ -92,16 +91,8 @@ static int prox_read_raw(struct iio_dev *indio_dev, break; } if (report_id >= 0) { - poll_value = hid_sensor_read_poll_value( - &prox_state->common_attributes); - if (poll_value < 0) - return -EINVAL; - hid_sensor_power_state(&prox_state->common_attributes, true); - - msleep_interruptible(poll_value * 2); - *val = sensor_hub_input_attr_get_raw_value( prox_state->common_attributes.hsdev, HID_USAGE_SENSOR_PROX, address, @@ -373,6 +364,7 @@ static struct platform_driver hid_prox_platform_driver = { .id_table = hid_prox_ids, .driver = { .name = KBUILD_MODNAME, + .pm = &hid_sensor_pm_ops, }, .probe = hid_prox_probe, .remove = hid_prox_remove, diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c new file mode 100644 index 00000000000..29de7e7d956 --- /dev/null +++ b/drivers/iio/light/jsa1212.c @@ -0,0 +1,471 @@ +/* + * JSA1212 Ambient Light & Proximity Sensor Driver + * + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * JSA1212 I2C slave address: 0x44(ADDR tied to GND), 0x45(ADDR tied to VDD) + * + * TODO: Interrupt support, thresholds, range support. + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/acpi.h> +#include <linux/regmap.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* JSA1212 reg address */ +#define JSA1212_CONF_REG 0x01 +#define JSA1212_INT_REG 0x02 +#define JSA1212_PXS_LT_REG 0x03 +#define JSA1212_PXS_HT_REG 0x04 +#define JSA1212_ALS_TH1_REG 0x05 +#define JSA1212_ALS_TH2_REG 0x06 +#define JSA1212_ALS_TH3_REG 0x07 +#define JSA1212_PXS_DATA_REG 0x08 +#define JSA1212_ALS_DT1_REG 0x09 +#define JSA1212_ALS_DT2_REG 0x0A +#define JSA1212_ALS_RNG_REG 0x0B +#define JSA1212_MAX_REG 0x0C + +/* JSA1212 reg masks */ +#define JSA1212_CONF_MASK 0xFF +#define JSA1212_INT_MASK 0xFF +#define JSA1212_PXS_LT_MASK 0xFF +#define JSA1212_PXS_HT_MASK 0xFF +#define JSA1212_ALS_TH1_MASK 0xFF +#define JSA1212_ALS_TH2_LT_MASK 0x0F +#define JSA1212_ALS_TH2_HT_MASK 0xF0 +#define JSA1212_ALS_TH3_MASK 0xFF +#define JSA1212_PXS_DATA_MASK 0xFF +#define JSA1212_ALS_DATA_MASK 0x0FFF +#define JSA1212_ALS_DT1_MASK 0xFF +#define JSA1212_ALS_DT2_MASK 0x0F +#define JSA1212_ALS_RNG_MASK 0x07 + +/* JSA1212 CONF REG bits */ +#define JSA1212_CONF_PXS_MASK 0x80 +#define JSA1212_CONF_PXS_ENABLE 0x80 +#define JSA1212_CONF_PXS_DISABLE 0x00 +#define JSA1212_CONF_ALS_MASK 0x04 +#define JSA1212_CONF_ALS_ENABLE 0x04 +#define JSA1212_CONF_ALS_DISABLE 0x00 +#define JSA1212_CONF_IRDR_MASK 0x08 +/* Proxmity sensing IRDR current sink settings */ +#define JSA1212_CONF_IRDR_200MA 0x08 +#define JSA1212_CONF_IRDR_100MA 0x00 +#define JSA1212_CONF_PXS_SLP_MASK 0x70 +#define JSA1212_CONF_PXS_SLP_0MS 0x70 +#define JSA1212_CONF_PXS_SLP_12MS 0x60 +#define JSA1212_CONF_PXS_SLP_50MS 0x50 +#define JSA1212_CONF_PXS_SLP_75MS 0x40 +#define JSA1212_CONF_PXS_SLP_100MS 0x30 +#define JSA1212_CONF_PXS_SLP_200MS 0x20 +#define JSA1212_CONF_PXS_SLP_400MS 0x10 +#define JSA1212_CONF_PXS_SLP_800MS 0x00 + +/* JSA1212 INT REG bits */ +#define JSA1212_INT_CTRL_MASK 0x01 +#define JSA1212_INT_CTRL_EITHER 0x00 +#define JSA1212_INT_CTRL_BOTH 0x01 +#define JSA1212_INT_ALS_PRST_MASK 0x06 +#define JSA1212_INT_ALS_PRST_1CONV 0x00 +#define JSA1212_INT_ALS_PRST_4CONV 0x02 +#define JSA1212_INT_ALS_PRST_8CONV 0x04 +#define JSA1212_INT_ALS_PRST_16CONV 0x06 +#define JSA1212_INT_ALS_FLAG_MASK 0x08 +#define JSA1212_INT_ALS_FLAG_CLR 0x00 +#define JSA1212_INT_PXS_PRST_MASK 0x60 +#define JSA1212_INT_PXS_PRST_1CONV 0x00 +#define JSA1212_INT_PXS_PRST_4CONV 0x20 +#define JSA1212_INT_PXS_PRST_8CONV 0x40 +#define JSA1212_INT_PXS_PRST_16CONV 0x60 +#define JSA1212_INT_PXS_FLAG_MASK 0x80 +#define JSA1212_INT_PXS_FLAG_CLR 0x00 + +/* JSA1212 ALS RNG REG bits */ +#define JSA1212_ALS_RNG_0_2048 0x00 +#define JSA1212_ALS_RNG_0_1024 0x01 +#define JSA1212_ALS_RNG_0_512 0x02 +#define JSA1212_ALS_RNG_0_256 0x03 +#define JSA1212_ALS_RNG_0_128 0x04 + +/* JSA1212 INT threshold range */ +#define JSA1212_ALS_TH_MIN 0x0000 +#define JSA1212_ALS_TH_MAX 0x0FFF +#define JSA1212_PXS_TH_MIN 0x00 +#define JSA1212_PXS_TH_MAX 0xFF + +#define JSA1212_ALS_DELAY_MS 200 +#define JSA1212_PXS_DELAY_MS 100 + +#define JSA1212_DRIVER_NAME "jsa1212" +#define JSA1212_REGMAP_NAME "jsa1212_regmap" + +enum jsa1212_op_mode { + JSA1212_OPMODE_ALS_EN, + JSA1212_OPMODE_PXS_EN, +}; + +struct jsa1212_data { + struct i2c_client *client; + struct mutex lock; + u8 als_rng_idx; + bool als_en; /* ALS enable status */ + bool pxs_en; /* proximity enable status */ + struct regmap *regmap; +}; + +/* ALS range idx to val mapping */ +static const int jsa1212_als_range_val[] = {2048, 1024, 512, 256, 128, + 128, 128, 128}; + +/* Enables or disables ALS function based on status */ +static int jsa1212_als_enable(struct jsa1212_data *data, u8 status) +{ + int ret; + + ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG, + JSA1212_CONF_ALS_MASK, + status); + if (ret < 0) + return ret; + + data->als_en = !!status; + + return 0; +} + +/* Enables or disables PXS function based on status */ +static int jsa1212_pxs_enable(struct jsa1212_data *data, u8 status) +{ + int ret; + + ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG, + JSA1212_CONF_PXS_MASK, + status); + if (ret < 0) + return ret; + + data->pxs_en = !!status; + + return 0; +} + +static int jsa1212_read_als_data(struct jsa1212_data *data, + unsigned int *val) +{ + int ret; + __le16 als_data; + + ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE); + if (ret < 0) + return ret; + + /* Delay for data output */ + msleep(JSA1212_ALS_DELAY_MS); + + /* Read 12 bit data */ + ret = regmap_bulk_read(data->regmap, JSA1212_ALS_DT1_REG, &als_data, 2); + if (ret < 0) { + dev_err(&data->client->dev, "als data read err\n"); + goto als_data_read_err; + } + + *val = le16_to_cpu(als_data); + +als_data_read_err: + return jsa1212_als_enable(data, JSA1212_CONF_ALS_DISABLE); +} + +static int jsa1212_read_pxs_data(struct jsa1212_data *data, + unsigned int *val) +{ + int ret; + unsigned int pxs_data; + + ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE); + if (ret < 0) + return ret; + + /* Delay for data output */ + msleep(JSA1212_PXS_DELAY_MS); + + /* Read out all data */ + ret = regmap_read(data->regmap, JSA1212_PXS_DATA_REG, &pxs_data); + if (ret < 0) { + dev_err(&data->client->dev, "pxs data read err\n"); + goto pxs_data_read_err; + } + + *val = pxs_data & JSA1212_PXS_DATA_MASK; + +pxs_data_read_err: + return jsa1212_pxs_enable(data, JSA1212_CONF_PXS_DISABLE); +} + +static int jsa1212_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct jsa1212_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + switch (chan->type) { + case IIO_LIGHT: + ret = jsa1212_read_als_data(data, val); + break; + case IIO_PROXIMITY: + ret = jsa1212_read_pxs_data(data, val); + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + return ret < 0 ? ret : IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_LIGHT: + *val = jsa1212_als_range_val[data->als_rng_idx]; + *val2 = BIT(12); /* Max 12 bit value */ + return IIO_VAL_FRACTIONAL; + default: + break; + } + break; + default: + break; + } + + return -EINVAL; +} + +static const struct iio_chan_spec jsa1212_channels[] = { + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + } +}; + +static const struct iio_info jsa1212_info = { + .driver_module = THIS_MODULE, + .read_raw = &jsa1212_read_raw, +}; + +static int jsa1212_chip_init(struct jsa1212_data *data) +{ + int ret; + + ret = regmap_write(data->regmap, JSA1212_CONF_REG, + (JSA1212_CONF_PXS_SLP_50MS | + JSA1212_CONF_IRDR_200MA)); + if (ret < 0) + return ret; + + ret = regmap_write(data->regmap, JSA1212_INT_REG, + JSA1212_INT_ALS_PRST_4CONV); + if (ret < 0) + return ret; + + data->als_rng_idx = JSA1212_ALS_RNG_0_2048; + + return 0; +} + +static bool jsa1212_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case JSA1212_PXS_DATA_REG: + case JSA1212_ALS_DT1_REG: + case JSA1212_ALS_DT2_REG: + case JSA1212_INT_REG: + return true; + default: + return false; + } +} + +static struct regmap_config jsa1212_regmap_config = { + .name = JSA1212_REGMAP_NAME, + .reg_bits = 8, + .val_bits = 8, + .max_register = JSA1212_MAX_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = jsa1212_is_volatile_reg, +}; + +static int jsa1212_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct jsa1212_data *data; + struct iio_dev *indio_dev; + struct regmap *regmap; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(client, &jsa1212_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Regmap initialization failed.\n"); + return PTR_ERR(regmap); + } + + data = iio_priv(indio_dev); + + i2c_set_clientdata(client, indio_dev); + data->client = client; + data->regmap = regmap; + + mutex_init(&data->lock); + + ret = jsa1212_chip_init(data); + if (ret < 0) + return ret; + + indio_dev->dev.parent = &client->dev; + indio_dev->channels = jsa1212_channels; + indio_dev->num_channels = ARRAY_SIZE(jsa1212_channels); + indio_dev->name = JSA1212_DRIVER_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->info = &jsa1212_info; + + ret = iio_device_register(indio_dev); + if (ret < 0) + dev_err(&client->dev, "%s: register device failed\n", __func__); + + return ret; +} + + /* power off the device */ +static int jsa1212_power_off(struct jsa1212_data *data) +{ + int ret; + + mutex_lock(&data->lock); + + ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG, + JSA1212_CONF_ALS_MASK | + JSA1212_CONF_PXS_MASK, + JSA1212_CONF_ALS_DISABLE | + JSA1212_CONF_PXS_DISABLE); + + if (ret < 0) + dev_err(&data->client->dev, "power off cmd failed\n"); + + mutex_unlock(&data->lock); + + return ret; +} + +static int jsa1212_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct jsa1212_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + return jsa1212_power_off(data); +} + +#ifdef CONFIG_PM_SLEEP +static int jsa1212_suspend(struct device *dev) +{ + struct jsa1212_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return jsa1212_power_off(data); +} + +static int jsa1212_resume(struct device *dev) +{ + int ret = 0; + struct jsa1212_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + mutex_lock(&data->lock); + + if (data->als_en) { + ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE); + if (ret < 0) { + dev_err(dev, "als resume failed\n"); + goto unlock_and_ret; + } + } + + if (data->pxs_en) { + ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE); + if (ret < 0) + dev_err(dev, "pxs resume failed\n"); + } + +unlock_and_ret: + mutex_unlock(&data->lock); + return ret; +} + +static SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend, jsa1212_resume); + +#define JSA1212_PM_OPS (&jsa1212_pm_ops) +#else +#define JSA1212_PM_OPS NULL +#endif + +static const struct acpi_device_id jsa1212_acpi_match[] = { + {"JSA1212", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, jsa1212_acpi_match); + +static const struct i2c_device_id jsa1212_id[] = { + { JSA1212_DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, jsa1212_id); + +static struct i2c_driver jsa1212_driver = { + .driver = { + .name = JSA1212_DRIVER_NAME, + .pm = JSA1212_PM_OPS, + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(jsa1212_acpi_match), + }, + .probe = jsa1212_probe, + .remove = jsa1212_remove, + .id_table = jsa1212_id, +}; +module_i2c_driver(jsa1212_driver); + +MODULE_AUTHOR("Sathya Kuppuswamy <sathyanarayanan.kuppuswamy@linux.intel.com>"); +MODULE_DESCRIPTION("JSA1212 proximity/ambient light sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index ae3c71bdd6c..076bc46fad0 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -657,7 +657,7 @@ static ALS_HYSTERESIS_ATTR_RO(3); #define ILLUMINANCE_ATTR_RO(_name) \ DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL) #define ILLUMINANCE_ATTR_RW(_name) \ - DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR , \ + DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR, \ show_##_name, store_##_name) /* * ALS Zone threshold-event enable diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index a9e449b0be0..71c2bde275a 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -149,8 +149,8 @@ static int tcs3414_read_raw(struct iio_dev *indio_dev, *val = ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT; - *val = tcs3414_scales[i][0]; + i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT; + *val = tcs3414_scales[i][0]; *val2 = tcs3414_scales[i][1]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_INT_TIME: |