1 /* 2 * CM3232 Ambient Light Sensor 3 * 4 * Copyright (C) 2014-2015 Capella Microsystems Inc. 5 * Author: Kevin Tsai <ktsai@capellamicro.com> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2, as published 9 * by the Free Software Foundation. 10 * 11 * IIO driver for CM3232 (7-bit I2C slave address 0x10). 12 */ 13 14 #include <linux/i2c.h> 15 #include <linux/module.h> 16 #include <linux/iio/iio.h> 17 #include <linux/iio/sysfs.h> 18 #include <linux/init.h> 19 20 /* Registers Address */ 21 #define CM3232_REG_ADDR_CMD 0x00 22 #define CM3232_REG_ADDR_ALS 0x50 23 #define CM3232_REG_ADDR_ID 0x53 24 25 #define CM3232_CMD_ALS_DISABLE BIT(0) 26 27 #define CM3232_CMD_ALS_IT_SHIFT 2 28 #define CM3232_CMD_ALS_IT_MASK (BIT(2) | BIT(3) | BIT(4)) 29 #define CM3232_CMD_ALS_IT_DEFAULT (0x01 << CM3232_CMD_ALS_IT_SHIFT) 30 31 #define CM3232_CMD_ALS_RESET BIT(6) 32 33 #define CM3232_CMD_DEFAULT CM3232_CMD_ALS_IT_DEFAULT 34 35 #define CM3232_HW_ID 0x32 36 #define CM3232_CALIBSCALE_DEFAULT 100000 37 #define CM3232_CALIBSCALE_RESOLUTION 100000 38 #define CM3232_MLUX_PER_LUX 1000 39 40 #define CM3232_MLUX_PER_BIT_DEFAULT 64 41 #define CM3232_MLUX_PER_BIT_BASE_IT 100000 42 43 static const struct { 44 int val; 45 int val2; 46 u8 it; 47 } cm3232_als_it_scales[] = { 48 {0, 100000, 0}, /* 0.100000 */ 49 {0, 200000, 1}, /* 0.200000 */ 50 {0, 400000, 2}, /* 0.400000 */ 51 {0, 800000, 3}, /* 0.800000 */ 52 {1, 600000, 4}, /* 1.600000 */ 53 {3, 200000, 5}, /* 3.200000 */ 54 }; 55 56 struct cm3232_als_info { 57 u8 regs_cmd_default; 58 u8 hw_id; 59 int calibscale; 60 int mlux_per_bit; 61 int mlux_per_bit_base_it; 62 }; 63 64 static struct cm3232_als_info cm3232_als_info_default = { 65 .regs_cmd_default = CM3232_CMD_DEFAULT, 66 .hw_id = CM3232_HW_ID, 67 .calibscale = CM3232_CALIBSCALE_DEFAULT, 68 .mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT, 69 .mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT, 70 }; 71 72 struct cm3232_chip { 73 struct i2c_client *client; 74 struct cm3232_als_info *als_info; 75 u8 regs_cmd; 76 u16 regs_als; 77 }; 78 79 /** 80 * cm3232_reg_init() - Initialize CM3232 81 * @chip: pointer of struct cm3232_chip. 82 * 83 * Check and initialize CM3232 ambient light sensor. 84 * 85 * Return: 0 for success; otherwise for error code. 86 */ 87 static int cm3232_reg_init(struct cm3232_chip *chip) 88 { 89 struct i2c_client *client = chip->client; 90 s32 ret; 91 92 chip->als_info = &cm3232_als_info_default; 93 94 /* Identify device */ 95 ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID); 96 if (ret < 0) { 97 dev_err(&chip->client->dev, "Error reading addr_id\n"); 98 return ret; 99 } 100 101 if ((ret & 0xFF) != chip->als_info->hw_id) 102 return -ENODEV; 103 104 /* Disable and reset device */ 105 chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET; 106 ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 107 chip->regs_cmd); 108 if (ret < 0) { 109 dev_err(&chip->client->dev, "Error writing reg_cmd\n"); 110 return ret; 111 } 112 113 /* Register default value */ 114 chip->regs_cmd = chip->als_info->regs_cmd_default; 115 116 /* Configure register */ 117 ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 118 chip->regs_cmd); 119 if (ret < 0) 120 dev_err(&chip->client->dev, "Error writing reg_cmd\n"); 121 122 return ret; 123 } 124 125 /** 126 * cm3232_read_als_it() - Get sensor integration time 127 * @chip: pointer of struct cm3232_chip 128 * @val: pointer of int to load the integration (sec). 129 * @val2: pointer of int to load the integration time (microsecond). 130 * 131 * Report the current integration time. 132 * 133 * Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL. 134 */ 135 static int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2) 136 { 137 u16 als_it; 138 int i; 139 140 als_it = chip->regs_cmd; 141 als_it &= CM3232_CMD_ALS_IT_MASK; 142 als_it >>= CM3232_CMD_ALS_IT_SHIFT; 143 for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) { 144 if (als_it == cm3232_als_it_scales[i].it) { 145 *val = cm3232_als_it_scales[i].val; 146 *val2 = cm3232_als_it_scales[i].val2; 147 return IIO_VAL_INT_PLUS_MICRO; 148 } 149 } 150 151 return -EINVAL; 152 } 153 154 /** 155 * cm3232_write_als_it() - Write sensor integration time 156 * @chip: pointer of struct cm3232_chip. 157 * @val: integration time in second. 158 * @val2: integration time in microsecond. 159 * 160 * Convert integration time to sensor value. 161 * 162 * Return: i2c_smbus_write_byte_data command return value. 163 */ 164 static int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2) 165 { 166 struct i2c_client *client = chip->client; 167 u16 als_it, cmd; 168 int i; 169 s32 ret; 170 171 for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) { 172 if (val == cm3232_als_it_scales[i].val && 173 val2 == cm3232_als_it_scales[i].val2) { 174 175 als_it = cm3232_als_it_scales[i].it; 176 als_it <<= CM3232_CMD_ALS_IT_SHIFT; 177 178 cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK; 179 cmd |= als_it; 180 ret = i2c_smbus_write_byte_data(client, 181 CM3232_REG_ADDR_CMD, 182 cmd); 183 if (ret < 0) 184 return ret; 185 chip->regs_cmd = cmd; 186 return 0; 187 } 188 } 189 return -EINVAL; 190 } 191 192 /** 193 * cm3232_get_lux() - report current lux value 194 * @chip: pointer of struct cm3232_chip. 195 * 196 * Convert sensor data to lux. It depends on integration 197 * time and calibscale variable. 198 * 199 * Return: Zero or positive value is lux, otherwise error code. 200 */ 201 static int cm3232_get_lux(struct cm3232_chip *chip) 202 { 203 struct i2c_client *client = chip->client; 204 struct cm3232_als_info *als_info = chip->als_info; 205 int ret; 206 int val, val2; 207 int als_it; 208 u64 lux; 209 210 /* Calculate mlux per bit based on als_it */ 211 ret = cm3232_read_als_it(chip, &val, &val2); 212 if (ret < 0) 213 return -EINVAL; 214 als_it = val * 1000000 + val2; 215 lux = (__force u64)als_info->mlux_per_bit; 216 lux *= als_info->mlux_per_bit_base_it; 217 lux = div_u64(lux, als_it); 218 219 ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS); 220 if (ret < 0) { 221 dev_err(&client->dev, "Error reading reg_addr_als\n"); 222 return ret; 223 } 224 225 chip->regs_als = (u16)ret; 226 lux *= chip->regs_als; 227 lux *= als_info->calibscale; 228 lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION); 229 lux = div_u64(lux, CM3232_MLUX_PER_LUX); 230 231 if (lux > 0xFFFF) 232 lux = 0xFFFF; 233 234 return (int)lux; 235 } 236 237 static int cm3232_read_raw(struct iio_dev *indio_dev, 238 struct iio_chan_spec const *chan, 239 int *val, int *val2, long mask) 240 { 241 struct cm3232_chip *chip = iio_priv(indio_dev); 242 struct cm3232_als_info *als_info = chip->als_info; 243 int ret; 244 245 switch (mask) { 246 case IIO_CHAN_INFO_PROCESSED: 247 ret = cm3232_get_lux(chip); 248 if (ret < 0) 249 return ret; 250 *val = ret; 251 return IIO_VAL_INT; 252 case IIO_CHAN_INFO_CALIBSCALE: 253 *val = als_info->calibscale; 254 return IIO_VAL_INT; 255 case IIO_CHAN_INFO_INT_TIME: 256 return cm3232_read_als_it(chip, val, val2); 257 } 258 259 return -EINVAL; 260 } 261 262 static int cm3232_write_raw(struct iio_dev *indio_dev, 263 struct iio_chan_spec const *chan, 264 int val, int val2, long mask) 265 { 266 struct cm3232_chip *chip = iio_priv(indio_dev); 267 struct cm3232_als_info *als_info = chip->als_info; 268 269 switch (mask) { 270 case IIO_CHAN_INFO_CALIBSCALE: 271 als_info->calibscale = val; 272 return 0; 273 case IIO_CHAN_INFO_INT_TIME: 274 return cm3232_write_als_it(chip, val, val2); 275 } 276 277 return -EINVAL; 278 } 279 280 /** 281 * cm3232_get_it_available() - Get available ALS IT value 282 * @dev: pointer of struct device. 283 * @attr: pointer of struct device_attribute. 284 * @buf: pointer of return string buffer. 285 * 286 * Display the available integration time in second. 287 * 288 * Return: string length. 289 */ 290 static ssize_t cm3232_get_it_available(struct device *dev, 291 struct device_attribute *attr, char *buf) 292 { 293 int i, len; 294 295 for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) 296 len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ", 297 cm3232_als_it_scales[i].val, 298 cm3232_als_it_scales[i].val2); 299 return len + scnprintf(buf + len, PAGE_SIZE - len, "\n"); 300 } 301 302 static const struct iio_chan_spec cm3232_channels[] = { 303 { 304 .type = IIO_LIGHT, 305 .info_mask_separate = 306 BIT(IIO_CHAN_INFO_PROCESSED) | 307 BIT(IIO_CHAN_INFO_CALIBSCALE) | 308 BIT(IIO_CHAN_INFO_INT_TIME), 309 } 310 }; 311 312 static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, 313 S_IRUGO, cm3232_get_it_available, NULL, 0); 314 315 static struct attribute *cm3232_attributes[] = { 316 &iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr, 317 NULL, 318 }; 319 320 static const struct attribute_group cm3232_attribute_group = { 321 .attrs = cm3232_attributes 322 }; 323 324 static const struct iio_info cm3232_info = { 325 .read_raw = &cm3232_read_raw, 326 .write_raw = &cm3232_write_raw, 327 .attrs = &cm3232_attribute_group, 328 }; 329 330 static int cm3232_probe(struct i2c_client *client, 331 const struct i2c_device_id *id) 332 { 333 struct cm3232_chip *chip; 334 struct iio_dev *indio_dev; 335 int ret; 336 337 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); 338 if (!indio_dev) 339 return -ENOMEM; 340 341 chip = iio_priv(indio_dev); 342 i2c_set_clientdata(client, indio_dev); 343 chip->client = client; 344 345 indio_dev->dev.parent = &client->dev; 346 indio_dev->channels = cm3232_channels; 347 indio_dev->num_channels = ARRAY_SIZE(cm3232_channels); 348 indio_dev->info = &cm3232_info; 349 indio_dev->name = id->name; 350 indio_dev->modes = INDIO_DIRECT_MODE; 351 352 ret = cm3232_reg_init(chip); 353 if (ret) { 354 dev_err(&client->dev, 355 "%s: register init failed\n", 356 __func__); 357 return ret; 358 } 359 360 return iio_device_register(indio_dev); 361 } 362 363 static int cm3232_remove(struct i2c_client *client) 364 { 365 struct iio_dev *indio_dev = i2c_get_clientdata(client); 366 367 i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 368 CM3232_CMD_ALS_DISABLE); 369 370 iio_device_unregister(indio_dev); 371 372 return 0; 373 } 374 375 static const struct i2c_device_id cm3232_id[] = { 376 {"cm3232", 0}, 377 {} 378 }; 379 380 #ifdef CONFIG_PM_SLEEP 381 static int cm3232_suspend(struct device *dev) 382 { 383 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 384 struct cm3232_chip *chip = iio_priv(indio_dev); 385 struct i2c_client *client = chip->client; 386 int ret; 387 388 chip->regs_cmd |= CM3232_CMD_ALS_DISABLE; 389 ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 390 chip->regs_cmd); 391 392 return ret; 393 } 394 395 static int cm3232_resume(struct device *dev) 396 { 397 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 398 struct cm3232_chip *chip = iio_priv(indio_dev); 399 struct i2c_client *client = chip->client; 400 int ret; 401 402 chip->regs_cmd &= ~CM3232_CMD_ALS_DISABLE; 403 ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 404 chip->regs_cmd | CM3232_CMD_ALS_RESET); 405 406 return ret; 407 } 408 409 static const struct dev_pm_ops cm3232_pm_ops = { 410 SET_SYSTEM_SLEEP_PM_OPS(cm3232_suspend, cm3232_resume)}; 411 #endif 412 413 MODULE_DEVICE_TABLE(i2c, cm3232_id); 414 415 static const struct of_device_id cm3232_of_match[] = { 416 {.compatible = "capella,cm3232"}, 417 {} 418 }; 419 MODULE_DEVICE_TABLE(of, cm3232_of_match); 420 421 static struct i2c_driver cm3232_driver = { 422 .driver = { 423 .name = "cm3232", 424 .of_match_table = of_match_ptr(cm3232_of_match), 425 #ifdef CONFIG_PM_SLEEP 426 .pm = &cm3232_pm_ops, 427 #endif 428 }, 429 .id_table = cm3232_id, 430 .probe = cm3232_probe, 431 .remove = cm3232_remove, 432 }; 433 434 module_i2c_driver(cm3232_driver); 435 436 MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>"); 437 MODULE_DESCRIPTION("CM3232 ambient light sensor driver"); 438 MODULE_LICENSE("GPL"); 439