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