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