0001
0002
0003
0004
0005
0006
0007
0008
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
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},
0047 {0, 200000, 1},
0048 {0, 400000, 2},
0049 {0, 800000, 3},
0050 {1, 600000, 4},
0051 {3, 200000, 5},
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
0079
0080
0081
0082
0083
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
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
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
0112 chip->regs_cmd = chip->als_info->regs_cmd_default;
0113
0114
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
0125
0126
0127
0128
0129
0130
0131
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
0154
0155
0156
0157
0158
0159
0160
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
0192
0193
0194
0195
0196
0197
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
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
0280
0281
0282
0283
0284
0285
0286
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");