diff options
Diffstat (limited to 'drivers/iio/light/cm32181.c')
-rw-r--r-- | drivers/iio/light/cm32181.c | 271 |
1 files changed, 211 insertions, 60 deletions
diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index 5f4fb5674fa0..160eb3f99795 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -4,11 +4,13 @@ * Author: Kevin Tsai <ktsai@capellamicro.com> */ +#include <linux/acpi.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/mutex.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/interrupt.h> #include <linux/regulator/consumer.h> #include <linux/iio/iio.h> @@ -18,17 +20,24 @@ /* Registers Address */ #define CM32181_REG_ADDR_CMD 0x00 +#define CM32181_REG_ADDR_WH 0x01 +#define CM32181_REG_ADDR_WL 0x02 +#define CM32181_REG_ADDR_TEST 0x03 #define CM32181_REG_ADDR_ALS 0x04 #define CM32181_REG_ADDR_STATUS 0x06 #define CM32181_REG_ADDR_ID 0x07 /* Number of Configurable Registers */ -#define CM32181_CONF_REG_NUM 0x01 +#define CM32181_CONF_REG_NUM 4 /* CMD register */ -#define CM32181_CMD_ALS_ENABLE 0x00 -#define CM32181_CMD_ALS_DISABLE 0x01 -#define CM32181_CMD_ALS_INT_EN 0x02 +#define CM32181_CMD_ALS_DISABLE BIT(0) +#define CM32181_CMD_ALS_INT_EN BIT(1) +#define CM32181_CMD_ALS_THRES_WINDOW BIT(2) + +#define CM32181_CMD_ALS_PERS_SHIFT 4 +#define CM32181_CMD_ALS_PERS_MASK (0x03 << CM32181_CMD_ALS_PERS_SHIFT) +#define CM32181_CMD_ALS_PERS_DEFAULT (0x01 << CM32181_CMD_ALS_PERS_SHIFT) #define CM32181_CMD_ALS_IT_SHIFT 6 #define CM32181_CMD_ALS_IT_MASK (0x0F << CM32181_CMD_ALS_IT_SHIFT) @@ -38,27 +47,133 @@ #define CM32181_CMD_ALS_SM_MASK (0x03 << CM32181_CMD_ALS_SM_SHIFT) #define CM32181_CMD_ALS_SM_DEFAULT (0x01 << CM32181_CMD_ALS_SM_SHIFT) -#define CM32181_MLUX_PER_BIT 5 /* ALS_SM=01 IT=800ms */ -#define CM32181_MLUX_PER_BIT_BASE_IT 800000 /* Based on IT=800ms */ -#define CM32181_CALIBSCALE_DEFAULT 1000 -#define CM32181_CALIBSCALE_RESOLUTION 1000 -#define MLUX_PER_LUX 1000 +#define CM32181_LUX_PER_BIT 500 /* ALS_SM=01 IT=800ms */ +#define CM32181_LUX_PER_BIT_RESOLUTION 100000 +#define CM32181_LUX_PER_BIT_BASE_IT 800000 /* Based on IT=800ms */ +#define CM32181_CALIBSCALE_DEFAULT 100000 +#define CM32181_CALIBSCALE_RESOLUTION 100000 -static const u8 cm32181_reg[CM32181_CONF_REG_NUM] = { - CM32181_REG_ADDR_CMD, -}; +#define SMBUS_ALERT_RESPONSE_ADDRESS 0x0c + +/* CPM0 Index 0: device-id (3218 or 32181), 1: Unknown, 2: init_regs_bitmap */ +#define CPM0_REGS_BITMAP 2 +#define CPM0_HEADER_SIZE 3 -static const int als_it_bits[] = {12, 8, 0, 1, 2, 3}; -static const int als_it_value[] = {25000, 50000, 100000, 200000, 400000, - 800000}; +/* CPM1 Index 0: lux_per_bit, 1: calibscale, 2: resolution (100000) */ +#define CPM1_LUX_PER_BIT 0 +#define CPM1_CALIBSCALE 1 +#define CPM1_SIZE 3 + +/* CM3218 Family */ +static const int cm3218_als_it_bits[] = { 0, 1, 2, 3 }; +static const int cm3218_als_it_values[] = { 100000, 200000, 400000, 800000 }; + +/* CM32181 Family */ +static const int cm32181_als_it_bits[] = { 12, 8, 0, 1, 2, 3 }; +static const int cm32181_als_it_values[] = { + 25000, 50000, 100000, 200000, 400000, 800000 +}; struct cm32181_chip { struct i2c_client *client; + struct device *dev; struct mutex lock; u16 conf_regs[CM32181_CONF_REG_NUM]; + unsigned long init_regs_bitmap; int calibscale; + int lux_per_bit; + int lux_per_bit_base_it; + int num_als_it; + const int *als_it_bits; + const int *als_it_values; }; +static int cm32181_read_als_it(struct cm32181_chip *cm32181, int *val2); + +#ifdef CONFIG_ACPI +/** + * cm32181_acpi_get_cpm() - Get CPM object from ACPI + * @client pointer of struct i2c_client. + * @obj_name pointer of ACPI object name. + * @count maximum size of return array. + * @vals pointer of array for return elements. + * + * Convert ACPI CPM table to array. + * + * Return: -ENODEV for fail. Otherwise is number of elements. + */ +static int cm32181_acpi_get_cpm(struct device *dev, char *obj_name, + u64 *values, int count) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *cpm, *elem; + acpi_handle handle; + acpi_status status; + int i; + + handle = ACPI_HANDLE(dev); + if (!handle) + return -ENODEV; + + status = acpi_evaluate_object(handle, obj_name, NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "object %s not found\n", obj_name); + return -ENODEV; + } + + cpm = buffer.pointer; + if (cpm->package.count > count) + dev_warn(dev, "%s table contains %u values, only using first %d values\n", + obj_name, cpm->package.count, count); + + count = min_t(int, cpm->package.count, count); + for (i = 0; i < count; i++) { + elem = &(cpm->package.elements[i]); + values[i] = elem->integer.value; + } + + kfree(buffer.pointer); + + return count; +} + +static void cm32181_acpi_parse_cpm_tables(struct cm32181_chip *cm32181) +{ + u64 vals[CPM0_HEADER_SIZE + CM32181_CONF_REG_NUM]; + struct device *dev = cm32181->dev; + int i, count; + + count = cm32181_acpi_get_cpm(dev, "CPM0", vals, ARRAY_SIZE(vals)); + if (count <= CPM0_HEADER_SIZE) + return; + + count -= CPM0_HEADER_SIZE; + + cm32181->init_regs_bitmap = vals[CPM0_REGS_BITMAP]; + cm32181->init_regs_bitmap &= GENMASK(count - 1, 0); + for_each_set_bit(i, &cm32181->init_regs_bitmap, count) + cm32181->conf_regs[i] = vals[CPM0_HEADER_SIZE + i]; + + count = cm32181_acpi_get_cpm(dev, "CPM1", vals, ARRAY_SIZE(vals)); + if (count != CPM1_SIZE) + return; + + cm32181->lux_per_bit = vals[CPM1_LUX_PER_BIT]; + + /* Check for uncalibrated devices */ + if (vals[CPM1_CALIBSCALE] == CM32181_CALIBSCALE_DEFAULT) + return; + + cm32181->calibscale = vals[CPM1_CALIBSCALE]; + /* CPM1 lux_per_bit is for the current it value */ + cm32181_read_als_it(cm32181, &cm32181->lux_per_bit_base_it); +} +#else +static void cm32181_acpi_parse_cpm_tables(struct cm32181_chip *cm32181) +{ +} +#endif /* CONFIG_ACPI */ + /** * cm32181_reg_init() - Initialize CM32181 registers * @cm32181: pointer of struct cm32181. @@ -78,18 +193,37 @@ static int cm32181_reg_init(struct cm32181_chip *cm32181) return ret; /* check device ID */ - if ((ret & 0xFF) != 0x81) + switch (ret & 0xFF) { + case 0x18: /* CM3218 */ + cm32181->num_als_it = ARRAY_SIZE(cm3218_als_it_bits); + cm32181->als_it_bits = cm3218_als_it_bits; + cm32181->als_it_values = cm3218_als_it_values; + break; + case 0x81: /* CM32181 */ + case 0x82: /* CM32182, fully compat. with CM32181 */ + cm32181->num_als_it = ARRAY_SIZE(cm32181_als_it_bits); + cm32181->als_it_bits = cm32181_als_it_bits; + cm32181->als_it_values = cm32181_als_it_values; + break; + default: return -ENODEV; + } /* Default Values */ - cm32181->conf_regs[CM32181_REG_ADDR_CMD] = CM32181_CMD_ALS_ENABLE | + cm32181->conf_regs[CM32181_REG_ADDR_CMD] = CM32181_CMD_ALS_IT_DEFAULT | CM32181_CMD_ALS_SM_DEFAULT; + cm32181->init_regs_bitmap = BIT(CM32181_REG_ADDR_CMD); cm32181->calibscale = CM32181_CALIBSCALE_DEFAULT; + cm32181->lux_per_bit = CM32181_LUX_PER_BIT; + cm32181->lux_per_bit_base_it = CM32181_LUX_PER_BIT_BASE_IT; + + if (ACPI_HANDLE(cm32181->dev)) + cm32181_acpi_parse_cpm_tables(cm32181); /* Initialize registers*/ - for (i = 0; i < CM32181_CONF_REG_NUM; i++) { - ret = i2c_smbus_write_word_data(client, cm32181_reg[i], - cm32181->conf_regs[i]); + for_each_set_bit(i, &cm32181->init_regs_bitmap, CM32181_CONF_REG_NUM) { + ret = i2c_smbus_write_word_data(client, i, + cm32181->conf_regs[i]); if (ret < 0) return ret; } @@ -102,7 +236,7 @@ static int cm32181_reg_init(struct cm32181_chip *cm32181) * @cm32181: pointer of struct cm32181 * @val2: pointer of int to load the als_it value. * - * Report the current integartion time by millisecond. + * Report the current integration time in milliseconds. * * Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL. */ @@ -114,9 +248,9 @@ static int cm32181_read_als_it(struct cm32181_chip *cm32181, int *val2) als_it = cm32181->conf_regs[CM32181_REG_ADDR_CMD]; als_it &= CM32181_CMD_ALS_IT_MASK; als_it >>= CM32181_CMD_ALS_IT_SHIFT; - for (i = 0; i < ARRAY_SIZE(als_it_bits); i++) { - if (als_it == als_it_bits[i]) { - *val2 = als_it_value[i]; + for (i = 0; i < cm32181->num_als_it; i++) { + if (als_it == cm32181->als_it_bits[i]) { + *val2 = cm32181->als_it_values[i]; return IIO_VAL_INT_PLUS_MICRO; } } @@ -139,14 +273,14 @@ static int cm32181_write_als_it(struct cm32181_chip *cm32181, int val) u16 als_it; int ret, i, n; - n = ARRAY_SIZE(als_it_value); + n = cm32181->num_als_it; for (i = 0; i < n; i++) - if (val <= als_it_value[i]) + if (val <= cm32181->als_it_values[i]) break; if (i >= n) i = n - 1; - als_it = als_it_bits[i]; + als_it = cm32181->als_it_bits[i]; als_it <<= CM32181_CMD_ALS_IT_SHIFT; mutex_lock(&cm32181->lock); @@ -175,15 +309,15 @@ static int cm32181_get_lux(struct cm32181_chip *cm32181) struct i2c_client *client = cm32181->client; int ret; int als_it; - unsigned long lux; + u64 lux; ret = cm32181_read_als_it(cm32181, &als_it); if (ret < 0) return -EINVAL; - lux = CM32181_MLUX_PER_BIT; - lux *= CM32181_MLUX_PER_BIT_BASE_IT; - lux /= als_it; + lux = cm32181->lux_per_bit; + lux *= cm32181->lux_per_bit_base_it; + lux = div_u64(lux, als_it); ret = i2c_smbus_read_word_data(client, CM32181_REG_ADDR_ALS); if (ret < 0) @@ -191,8 +325,8 @@ static int cm32181_get_lux(struct cm32181_chip *cm32181) lux *= ret; lux *= cm32181->calibscale; - lux /= CM32181_CALIBSCALE_RESOLUTION; - lux /= MLUX_PER_LUX; + lux = div_u64(lux, CM32181_CALIBSCALE_RESOLUTION); + lux = div_u64(lux, CM32181_LUX_PER_BIT_RESOLUTION); if (lux > 0xFFFF) lux = 0xFFFF; @@ -258,11 +392,12 @@ static int cm32181_write_raw(struct iio_dev *indio_dev, static ssize_t cm32181_get_it_available(struct device *dev, struct device_attribute *attr, char *buf) { + struct cm32181_chip *cm32181 = iio_priv(dev_to_iio_dev(dev)); int i, n, len; - n = ARRAY_SIZE(als_it_value); + n = cm32181->num_als_it; for (i = 0, len = 0; i < n; i++) - len += sprintf(buf + len, "0.%06u ", als_it_value[i]); + len += sprintf(buf + len, "0.%06u ", cm32181->als_it_values[i]); return len + sprintf(buf + len, "\n"); } @@ -294,70 +429,86 @@ static const struct iio_info cm32181_info = { .attrs = &cm32181_attribute_group, }; -static int cm32181_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int cm32181_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct cm32181_chip *cm32181; struct iio_dev *indio_dev; int ret; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm32181)); - if (!indio_dev) { - dev_err(&client->dev, "devm_iio_device_alloc failed\n"); + indio_dev = devm_iio_device_alloc(dev, sizeof(*cm32181)); + if (!indio_dev) return -ENOMEM; + + /* + * Some ACPI systems list 2 I2C resources for the CM3218 sensor, the + * SMBus Alert Response Address (ARA, 0x0c) and the actual I2C address. + * Detect this and take the following step to deal with it: + * 1. When a SMBus Alert capable sensor has an Alert asserted, it will + * not respond on its actual I2C address. Read a byte from the ARA + * to clear any pending Alerts. + * 2. Create a "dummy" client for the actual I2C address and + * use that client to communicate with the sensor. + */ + if (ACPI_HANDLE(dev) && client->addr == SMBUS_ALERT_RESPONSE_ADDRESS) { + struct i2c_board_info board_info = { .type = "dummy" }; + + i2c_smbus_read_byte(client); + + client = i2c_acpi_new_device(dev, 1, &board_info); + if (IS_ERR(client)) + return PTR_ERR(client); } cm32181 = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); cm32181->client = client; + cm32181->dev = dev; mutex_init(&cm32181->lock); - indio_dev->dev.parent = &client->dev; + indio_dev->dev.parent = dev; indio_dev->channels = cm32181_channels; indio_dev->num_channels = ARRAY_SIZE(cm32181_channels); indio_dev->info = &cm32181_info; - indio_dev->name = id->name; + indio_dev->name = dev_name(dev); indio_dev->modes = INDIO_DIRECT_MODE; ret = cm32181_reg_init(cm32181); if (ret) { - dev_err(&client->dev, - "%s: register init failed\n", - __func__); + dev_err(dev, "%s: register init failed\n", __func__); return ret; } - ret = devm_iio_device_register(&client->dev, indio_dev); + ret = devm_iio_device_register(dev, indio_dev); if (ret) { - dev_err(&client->dev, - "%s: regist device failed\n", - __func__); + dev_err(dev, "%s: regist device failed\n", __func__); return ret; } return 0; } -static const struct i2c_device_id cm32181_id[] = { - { "cm32181", 0 }, - { } -}; - -MODULE_DEVICE_TABLE(i2c, cm32181_id); - static const struct of_device_id cm32181_of_match[] = { + { .compatible = "capella,cm3218" }, { .compatible = "capella,cm32181" }, { } }; MODULE_DEVICE_TABLE(of, cm32181_of_match); +#ifdef CONFIG_ACPI +static const struct acpi_device_id cm32181_acpi_match[] = { + { "CPLM3218", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cm32181_acpi_match); +#endif + static struct i2c_driver cm32181_driver = { .driver = { .name = "cm32181", - .of_match_table = of_match_ptr(cm32181_of_match), + .acpi_match_table = ACPI_PTR(cm32181_acpi_match), + .of_match_table = cm32181_of_match, }, - .id_table = cm32181_id, - .probe = cm32181_probe, + .probe_new = cm32181_probe, }; module_i2c_driver(cm32181_driver); |