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 0; 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 .driver_module = THIS_MODULE, 326 .read_raw = &cm3232_read_raw, 327 .write_raw = &cm3232_write_raw, 328 .attrs = &cm3232_attribute_group, 329 }; 330 331 static int cm3232_probe(struct i2c_client *client, 332 const struct i2c_device_id *id) 333 { 334 struct cm3232_chip *chip; 335 struct iio_dev *indio_dev; 336 int ret; 337 338 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); 339 if (!indio_dev) 340 return -ENOMEM; 341 342 chip = iio_priv(indio_dev); 343 i2c_set_clientdata(client, indio_dev); 344 chip->client = client; 345 346 indio_dev->dev.parent = &client->dev; 347 indio_dev->channels = cm3232_channels; 348 indio_dev->num_channels = ARRAY_SIZE(cm3232_channels); 349 indio_dev->info = &cm3232_info; 350 indio_dev->name = id->name; 351 indio_dev->modes = INDIO_DIRECT_MODE; 352 353 ret = cm3232_reg_init(chip); 354 if (ret) { 355 dev_err(&client->dev, 356 "%s: register init failed\n", 357 __func__); 358 return ret; 359 } 360 361 return iio_device_register(indio_dev); 362 } 363 364 static int cm3232_remove(struct i2c_client *client) 365 { 366 struct iio_dev *indio_dev = i2c_get_clientdata(client); 367 368 i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, 369 CM3232_CMD_ALS_DISABLE); 370 371 iio_device_unregister(indio_dev); 372 373 return 0; 374 } 375 376 static const struct i2c_device_id cm3232_id[] = { 377 {"cm3232", 0}, 378 {} 379 }; 380 381 MODULE_DEVICE_TABLE(i2c, cm3232_id); 382 383 static const struct of_device_id cm3232_of_match[] = { 384 {.compatible = "capella,cm3232"}, 385 {} 386 }; 387 388 static struct i2c_driver cm3232_driver = { 389 .driver = { 390 .name = "cm3232", 391 .owner = THIS_MODULE, 392 .of_match_table = of_match_ptr(cm3232_of_match), 393 }, 394 .id_table = cm3232_id, 395 .probe = cm3232_probe, 396 .remove = cm3232_remove, 397 }; 398 399 module_i2c_driver(cm3232_driver); 400 401 MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>"); 402 MODULE_DESCRIPTION("CM3232 ambient light sensor driver"); 403 MODULE_LICENSE("GPL"); 404