Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * CM3232 Ambient Light Sensor
0004  *
0005  * Copyright (C) 2014-2015 Capella Microsystems Inc.
0006  * Author: Kevin Tsai <ktsai@capellamicro.com>
0007  *
0008  * IIO driver for CM3232 (7-bit I2C slave address 0x10).
0009  */
0010 
0011 #include <linux/i2c.h>
0012 #include <linux/module.h>
0013 #include <linux/mod_devicetable.h>
0014 #include <linux/iio/iio.h>
0015 #include <linux/iio/sysfs.h>
0016 #include <linux/init.h>
0017 
0018 /* Registers Address */
0019 #define CM3232_REG_ADDR_CMD     0x00
0020 #define CM3232_REG_ADDR_ALS     0x50
0021 #define CM3232_REG_ADDR_ID      0x53
0022 
0023 #define CM3232_CMD_ALS_DISABLE      BIT(0)
0024 
0025 #define CM3232_CMD_ALS_IT_SHIFT     2
0026 #define CM3232_CMD_ALS_IT_MASK      (BIT(2) | BIT(3) | BIT(4))
0027 #define CM3232_CMD_ALS_IT_DEFAULT   (0x01 << CM3232_CMD_ALS_IT_SHIFT)
0028 
0029 #define CM3232_CMD_ALS_RESET        BIT(6)
0030 
0031 #define CM3232_CMD_DEFAULT      CM3232_CMD_ALS_IT_DEFAULT
0032 
0033 #define CM3232_HW_ID            0x32
0034 #define CM3232_CALIBSCALE_DEFAULT   100000
0035 #define CM3232_CALIBSCALE_RESOLUTION    100000
0036 #define CM3232_MLUX_PER_LUX     1000
0037 
0038 #define CM3232_MLUX_PER_BIT_DEFAULT 64
0039 #define CM3232_MLUX_PER_BIT_BASE_IT 100000
0040 
0041 static const struct {
0042     int val;
0043     int val2;
0044     u8 it;
0045 } cm3232_als_it_scales[] = {
0046     {0, 100000, 0}, /* 0.100000 */
0047     {0, 200000, 1}, /* 0.200000 */
0048     {0, 400000, 2}, /* 0.400000 */
0049     {0, 800000, 3}, /* 0.800000 */
0050     {1, 600000, 4}, /* 1.600000 */
0051     {3, 200000, 5}, /* 3.200000 */
0052 };
0053 
0054 struct cm3232_als_info {
0055     u8 regs_cmd_default;
0056     u8 hw_id;
0057     int calibscale;
0058     int mlux_per_bit;
0059     int mlux_per_bit_base_it;
0060 };
0061 
0062 static struct cm3232_als_info cm3232_als_info_default = {
0063     .regs_cmd_default = CM3232_CMD_DEFAULT,
0064     .hw_id = CM3232_HW_ID,
0065     .calibscale = CM3232_CALIBSCALE_DEFAULT,
0066     .mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT,
0067     .mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT,
0068 };
0069 
0070 struct cm3232_chip {
0071     struct i2c_client *client;
0072     struct cm3232_als_info *als_info;
0073     u8 regs_cmd;
0074     u16 regs_als;
0075 };
0076 
0077 /**
0078  * cm3232_reg_init() - Initialize CM3232
0079  * @chip:   pointer of struct cm3232_chip.
0080  *
0081  * Check and initialize CM3232 ambient light sensor.
0082  *
0083  * Return: 0 for success; otherwise for error code.
0084  */
0085 static int cm3232_reg_init(struct cm3232_chip *chip)
0086 {
0087     struct i2c_client *client = chip->client;
0088     s32 ret;
0089 
0090     chip->als_info = &cm3232_als_info_default;
0091 
0092     /* Identify device */
0093     ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID);
0094     if (ret < 0) {
0095         dev_err(&chip->client->dev, "Error reading addr_id\n");
0096         return ret;
0097     }
0098 
0099     if ((ret & 0xFF) != chip->als_info->hw_id)
0100         return -ENODEV;
0101 
0102     /* Disable and reset device */
0103     chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET;
0104     ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
0105                     chip->regs_cmd);
0106     if (ret < 0) {
0107         dev_err(&chip->client->dev, "Error writing reg_cmd\n");
0108         return ret;
0109     }
0110 
0111     /* Register default value */
0112     chip->regs_cmd = chip->als_info->regs_cmd_default;
0113 
0114     /* Configure register */
0115     ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
0116                     chip->regs_cmd);
0117     if (ret < 0)
0118         dev_err(&chip->client->dev, "Error writing reg_cmd\n");
0119 
0120     return ret;
0121 }
0122 
0123 /**
0124  *  cm3232_read_als_it() - Get sensor integration time
0125  *  @chip:  pointer of struct cm3232_chip
0126  *  @val:   pointer of int to load the integration (sec).
0127  *  @val2:  pointer of int to load the integration time (microsecond).
0128  *
0129  *  Report the current integration time.
0130  *
0131  *  Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
0132  */
0133 static int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2)
0134 {
0135     u16 als_it;
0136     int i;
0137 
0138     als_it = chip->regs_cmd;
0139     als_it &= CM3232_CMD_ALS_IT_MASK;
0140     als_it >>= CM3232_CMD_ALS_IT_SHIFT;
0141     for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
0142         if (als_it == cm3232_als_it_scales[i].it) {
0143             *val = cm3232_als_it_scales[i].val;
0144             *val2 = cm3232_als_it_scales[i].val2;
0145             return IIO_VAL_INT_PLUS_MICRO;
0146         }
0147     }
0148 
0149     return -EINVAL;
0150 }
0151 
0152 /**
0153  * cm3232_write_als_it() - Write sensor integration time
0154  * @chip:   pointer of struct cm3232_chip.
0155  * @val:    integration time in second.
0156  * @val2:   integration time in microsecond.
0157  *
0158  * Convert integration time to sensor value.
0159  *
0160  * Return: i2c_smbus_write_byte_data command return value.
0161  */
0162 static int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2)
0163 {
0164     struct i2c_client *client = chip->client;
0165     u16 als_it, cmd;
0166     int i;
0167     s32 ret;
0168 
0169     for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
0170         if (val == cm3232_als_it_scales[i].val &&
0171             val2 == cm3232_als_it_scales[i].val2) {
0172 
0173             als_it = cm3232_als_it_scales[i].it;
0174             als_it <<= CM3232_CMD_ALS_IT_SHIFT;
0175 
0176             cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK;
0177             cmd |= als_it;
0178             ret = i2c_smbus_write_byte_data(client,
0179                             CM3232_REG_ADDR_CMD,
0180                             cmd);
0181             if (ret < 0)
0182                 return ret;
0183             chip->regs_cmd = cmd;
0184             return 0;
0185         }
0186     }
0187     return -EINVAL;
0188 }
0189 
0190 /**
0191  * cm3232_get_lux() - report current lux value
0192  * @chip:   pointer of struct cm3232_chip.
0193  *
0194  * Convert sensor data to lux.  It depends on integration
0195  * time and calibscale variable.
0196  *
0197  * Return: Zero or positive value is lux, otherwise error code.
0198  */
0199 static int cm3232_get_lux(struct cm3232_chip *chip)
0200 {
0201     struct i2c_client *client = chip->client;
0202     struct cm3232_als_info *als_info = chip->als_info;
0203     int ret;
0204     int val, val2;
0205     int als_it;
0206     u64 lux;
0207 
0208     /* Calculate mlux per bit based on als_it */
0209     ret = cm3232_read_als_it(chip, &val, &val2);
0210     if (ret < 0)
0211         return -EINVAL;
0212     als_it = val * 1000000 + val2;
0213     lux = (__force u64)als_info->mlux_per_bit;
0214     lux *= als_info->mlux_per_bit_base_it;
0215     lux = div_u64(lux, als_it);
0216 
0217     ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS);
0218     if (ret < 0) {
0219         dev_err(&client->dev, "Error reading reg_addr_als\n");
0220         return ret;
0221     }
0222 
0223     chip->regs_als = (u16)ret;
0224     lux *= chip->regs_als;
0225     lux *= als_info->calibscale;
0226     lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION);
0227     lux = div_u64(lux, CM3232_MLUX_PER_LUX);
0228 
0229     if (lux > 0xFFFF)
0230         lux = 0xFFFF;
0231 
0232     return (int)lux;
0233 }
0234 
0235 static int cm3232_read_raw(struct iio_dev *indio_dev,
0236             struct iio_chan_spec const *chan,
0237             int *val, int *val2, long mask)
0238 {
0239     struct cm3232_chip *chip = iio_priv(indio_dev);
0240     struct cm3232_als_info *als_info = chip->als_info;
0241     int ret;
0242 
0243     switch (mask) {
0244     case IIO_CHAN_INFO_PROCESSED:
0245         ret = cm3232_get_lux(chip);
0246         if (ret < 0)
0247             return ret;
0248         *val = ret;
0249         return IIO_VAL_INT;
0250     case IIO_CHAN_INFO_CALIBSCALE:
0251         *val = als_info->calibscale;
0252         return IIO_VAL_INT;
0253     case IIO_CHAN_INFO_INT_TIME:
0254         return cm3232_read_als_it(chip, val, val2);
0255     }
0256 
0257     return -EINVAL;
0258 }
0259 
0260 static int cm3232_write_raw(struct iio_dev *indio_dev,
0261             struct iio_chan_spec const *chan,
0262             int val, int val2, long mask)
0263 {
0264     struct cm3232_chip *chip = iio_priv(indio_dev);
0265     struct cm3232_als_info *als_info = chip->als_info;
0266 
0267     switch (mask) {
0268     case IIO_CHAN_INFO_CALIBSCALE:
0269         als_info->calibscale = val;
0270         return 0;
0271     case IIO_CHAN_INFO_INT_TIME:
0272         return cm3232_write_als_it(chip, val, val2);
0273     }
0274 
0275     return -EINVAL;
0276 }
0277 
0278 /**
0279  * cm3232_get_it_available() - Get available ALS IT value
0280  * @dev:    pointer of struct device.
0281  * @attr:   pointer of struct device_attribute.
0282  * @buf:    pointer of return string buffer.
0283  *
0284  * Display the available integration time in second.
0285  *
0286  * Return: string length.
0287  */
0288 static ssize_t cm3232_get_it_available(struct device *dev,
0289             struct device_attribute *attr, char *buf)
0290 {
0291     int i, len;
0292 
0293     for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++)
0294         len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
0295             cm3232_als_it_scales[i].val,
0296             cm3232_als_it_scales[i].val2);
0297     return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
0298 }
0299 
0300 static const struct iio_chan_spec cm3232_channels[] = {
0301     {
0302         .type = IIO_LIGHT,
0303         .info_mask_separate =
0304             BIT(IIO_CHAN_INFO_PROCESSED) |
0305             BIT(IIO_CHAN_INFO_CALIBSCALE) |
0306             BIT(IIO_CHAN_INFO_INT_TIME),
0307     }
0308 };
0309 
0310 static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
0311             S_IRUGO, cm3232_get_it_available, NULL, 0);
0312 
0313 static struct attribute *cm3232_attributes[] = {
0314     &iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
0315     NULL,
0316 };
0317 
0318 static const struct attribute_group cm3232_attribute_group = {
0319     .attrs = cm3232_attributes
0320 };
0321 
0322 static const struct iio_info cm3232_info = {
0323     .read_raw       = &cm3232_read_raw,
0324     .write_raw      = &cm3232_write_raw,
0325     .attrs          = &cm3232_attribute_group,
0326 };
0327 
0328 static int cm3232_probe(struct i2c_client *client,
0329             const struct i2c_device_id *id)
0330 {
0331     struct cm3232_chip *chip;
0332     struct iio_dev *indio_dev;
0333     int ret;
0334 
0335     indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
0336     if (!indio_dev)
0337         return -ENOMEM;
0338 
0339     chip = iio_priv(indio_dev);
0340     i2c_set_clientdata(client, indio_dev);
0341     chip->client = client;
0342 
0343     indio_dev->channels = cm3232_channels;
0344     indio_dev->num_channels = ARRAY_SIZE(cm3232_channels);
0345     indio_dev->info = &cm3232_info;
0346     indio_dev->name = id->name;
0347     indio_dev->modes = INDIO_DIRECT_MODE;
0348 
0349     ret = cm3232_reg_init(chip);
0350     if (ret) {
0351         dev_err(&client->dev,
0352             "%s: register init failed\n",
0353             __func__);
0354         return ret;
0355     }
0356 
0357     return iio_device_register(indio_dev);
0358 }
0359 
0360 static int cm3232_remove(struct i2c_client *client)
0361 {
0362     struct iio_dev *indio_dev = i2c_get_clientdata(client);
0363 
0364     i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
0365         CM3232_CMD_ALS_DISABLE);
0366 
0367     iio_device_unregister(indio_dev);
0368 
0369     return 0;
0370 }
0371 
0372 static const struct i2c_device_id cm3232_id[] = {
0373     {"cm3232", 0},
0374     {}
0375 };
0376 
0377 static int cm3232_suspend(struct device *dev)
0378 {
0379     struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
0380     struct cm3232_chip *chip = iio_priv(indio_dev);
0381     struct i2c_client *client = chip->client;
0382     int ret;
0383 
0384     chip->regs_cmd |= CM3232_CMD_ALS_DISABLE;
0385     ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
0386                     chip->regs_cmd);
0387 
0388     return ret;
0389 }
0390 
0391 static int cm3232_resume(struct device *dev)
0392 {
0393     struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
0394     struct cm3232_chip *chip = iio_priv(indio_dev);
0395     struct i2c_client *client = chip->client;
0396     int ret;
0397 
0398     chip->regs_cmd &= ~CM3232_CMD_ALS_DISABLE;
0399     ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
0400                     chip->regs_cmd | CM3232_CMD_ALS_RESET);
0401 
0402     return ret;
0403 }
0404 
0405 static DEFINE_SIMPLE_DEV_PM_OPS(cm3232_pm_ops, cm3232_suspend, cm3232_resume);
0406 
0407 MODULE_DEVICE_TABLE(i2c, cm3232_id);
0408 
0409 static const struct of_device_id cm3232_of_match[] = {
0410     {.compatible = "capella,cm3232"},
0411     {}
0412 };
0413 MODULE_DEVICE_TABLE(of, cm3232_of_match);
0414 
0415 static struct i2c_driver cm3232_driver = {
0416     .driver = {
0417         .name   = "cm3232",
0418         .of_match_table = cm3232_of_match,
0419         .pm = pm_sleep_ptr(&cm3232_pm_ops),
0420     },
0421     .id_table   = cm3232_id,
0422     .probe      = cm3232_probe,
0423     .remove     = cm3232_remove,
0424 };
0425 
0426 module_i2c_driver(cm3232_driver);
0427 
0428 MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>");
0429 MODULE_DESCRIPTION("CM3232 ambient light sensor driver");
0430 MODULE_LICENSE("GPL");