lv0104cs.c (11769B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * lv0104cs.c: LV0104CS Ambient Light Sensor Driver 4 * 5 * Copyright (C) 2018 6 * Author: Jeff LaBundy <jeff@labundy.com> 7 * 8 * 7-bit I2C slave address: 0x13 9 * 10 * Link to data sheet: https://www.onsemi.com/pub/Collateral/LV0104CS-D.PDF 11 */ 12 13#include <linux/kernel.h> 14#include <linux/module.h> 15#include <linux/i2c.h> 16#include <linux/err.h> 17#include <linux/mutex.h> 18#include <linux/delay.h> 19#include <linux/iio/iio.h> 20#include <linux/iio/sysfs.h> 21 22#define LV0104CS_REGVAL_MEASURE 0xE0 23#define LV0104CS_REGVAL_SLEEP 0x00 24 25#define LV0104CS_SCALE_0_25X 0 26#define LV0104CS_SCALE_1X 1 27#define LV0104CS_SCALE_2X 2 28#define LV0104CS_SCALE_8X 3 29#define LV0104CS_SCALE_SHIFT 3 30 31#define LV0104CS_INTEG_12_5MS 0 32#define LV0104CS_INTEG_100MS 1 33#define LV0104CS_INTEG_200MS 2 34#define LV0104CS_INTEG_SHIFT 1 35 36#define LV0104CS_CALIBSCALE_UNITY 31 37 38struct lv0104cs_private { 39 struct i2c_client *client; 40 struct mutex lock; 41 u8 calibscale; 42 u8 scale; 43 u8 int_time; 44}; 45 46struct lv0104cs_mapping { 47 int val; 48 int val2; 49 u8 regval; 50}; 51 52static const struct lv0104cs_mapping lv0104cs_calibscales[] = { 53 { 0, 666666, 0x81 }, 54 { 0, 800000, 0x82 }, 55 { 0, 857142, 0x83 }, 56 { 0, 888888, 0x84 }, 57 { 0, 909090, 0x85 }, 58 { 0, 923076, 0x86 }, 59 { 0, 933333, 0x87 }, 60 { 0, 941176, 0x88 }, 61 { 0, 947368, 0x89 }, 62 { 0, 952380, 0x8A }, 63 { 0, 956521, 0x8B }, 64 { 0, 960000, 0x8C }, 65 { 0, 962962, 0x8D }, 66 { 0, 965517, 0x8E }, 67 { 0, 967741, 0x8F }, 68 { 0, 969696, 0x90 }, 69 { 0, 971428, 0x91 }, 70 { 0, 972972, 0x92 }, 71 { 0, 974358, 0x93 }, 72 { 0, 975609, 0x94 }, 73 { 0, 976744, 0x95 }, 74 { 0, 977777, 0x96 }, 75 { 0, 978723, 0x97 }, 76 { 0, 979591, 0x98 }, 77 { 0, 980392, 0x99 }, 78 { 0, 981132, 0x9A }, 79 { 0, 981818, 0x9B }, 80 { 0, 982456, 0x9C }, 81 { 0, 983050, 0x9D }, 82 { 0, 983606, 0x9E }, 83 { 0, 984126, 0x9F }, 84 { 1, 0, 0x80 }, 85 { 1, 16129, 0xBF }, 86 { 1, 16666, 0xBE }, 87 { 1, 17241, 0xBD }, 88 { 1, 17857, 0xBC }, 89 { 1, 18518, 0xBB }, 90 { 1, 19230, 0xBA }, 91 { 1, 20000, 0xB9 }, 92 { 1, 20833, 0xB8 }, 93 { 1, 21739, 0xB7 }, 94 { 1, 22727, 0xB6 }, 95 { 1, 23809, 0xB5 }, 96 { 1, 24999, 0xB4 }, 97 { 1, 26315, 0xB3 }, 98 { 1, 27777, 0xB2 }, 99 { 1, 29411, 0xB1 }, 100 { 1, 31250, 0xB0 }, 101 { 1, 33333, 0xAF }, 102 { 1, 35714, 0xAE }, 103 { 1, 38461, 0xAD }, 104 { 1, 41666, 0xAC }, 105 { 1, 45454, 0xAB }, 106 { 1, 50000, 0xAA }, 107 { 1, 55555, 0xA9 }, 108 { 1, 62500, 0xA8 }, 109 { 1, 71428, 0xA7 }, 110 { 1, 83333, 0xA6 }, 111 { 1, 100000, 0xA5 }, 112 { 1, 125000, 0xA4 }, 113 { 1, 166666, 0xA3 }, 114 { 1, 250000, 0xA2 }, 115 { 1, 500000, 0xA1 }, 116}; 117 118static const struct lv0104cs_mapping lv0104cs_scales[] = { 119 { 0, 250000, LV0104CS_SCALE_0_25X << LV0104CS_SCALE_SHIFT }, 120 { 1, 0, LV0104CS_SCALE_1X << LV0104CS_SCALE_SHIFT }, 121 { 2, 0, LV0104CS_SCALE_2X << LV0104CS_SCALE_SHIFT }, 122 { 8, 0, LV0104CS_SCALE_8X << LV0104CS_SCALE_SHIFT }, 123}; 124 125static const struct lv0104cs_mapping lv0104cs_int_times[] = { 126 { 0, 12500, LV0104CS_INTEG_12_5MS << LV0104CS_INTEG_SHIFT }, 127 { 0, 100000, LV0104CS_INTEG_100MS << LV0104CS_INTEG_SHIFT }, 128 { 0, 200000, LV0104CS_INTEG_200MS << LV0104CS_INTEG_SHIFT }, 129}; 130 131static int lv0104cs_write_reg(struct i2c_client *client, u8 regval) 132{ 133 int ret; 134 135 ret = i2c_master_send(client, (char *)®val, sizeof(regval)); 136 if (ret < 0) 137 return ret; 138 if (ret != sizeof(regval)) 139 return -EIO; 140 141 return 0; 142} 143 144static int lv0104cs_read_adc(struct i2c_client *client, u16 *adc_output) 145{ 146 __be16 regval; 147 int ret; 148 149 ret = i2c_master_recv(client, (char *)®val, sizeof(regval)); 150 if (ret < 0) 151 return ret; 152 if (ret != sizeof(regval)) 153 return -EIO; 154 155 *adc_output = be16_to_cpu(regval); 156 157 return 0; 158} 159 160static int lv0104cs_get_lux(struct lv0104cs_private *lv0104cs, 161 int *val, int *val2) 162{ 163 u8 regval = LV0104CS_REGVAL_MEASURE; 164 u16 adc_output; 165 int ret; 166 167 regval |= lv0104cs_scales[lv0104cs->scale].regval; 168 regval |= lv0104cs_int_times[lv0104cs->int_time].regval; 169 ret = lv0104cs_write_reg(lv0104cs->client, regval); 170 if (ret) 171 return ret; 172 173 /* wait for integration time to pass (with margin) */ 174 switch (lv0104cs->int_time) { 175 case LV0104CS_INTEG_12_5MS: 176 msleep(50); 177 break; 178 179 case LV0104CS_INTEG_100MS: 180 msleep(150); 181 break; 182 183 case LV0104CS_INTEG_200MS: 184 msleep(250); 185 break; 186 187 default: 188 return -EINVAL; 189 } 190 191 ret = lv0104cs_read_adc(lv0104cs->client, &adc_output); 192 if (ret) 193 return ret; 194 195 ret = lv0104cs_write_reg(lv0104cs->client, LV0104CS_REGVAL_SLEEP); 196 if (ret) 197 return ret; 198 199 /* convert ADC output to lux */ 200 switch (lv0104cs->scale) { 201 case LV0104CS_SCALE_0_25X: 202 *val = adc_output * 4; 203 *val2 = 0; 204 return 0; 205 206 case LV0104CS_SCALE_1X: 207 *val = adc_output; 208 *val2 = 0; 209 return 0; 210 211 case LV0104CS_SCALE_2X: 212 *val = adc_output / 2; 213 *val2 = (adc_output % 2) * 500000; 214 return 0; 215 216 case LV0104CS_SCALE_8X: 217 *val = adc_output / 8; 218 *val2 = (adc_output % 8) * 125000; 219 return 0; 220 221 default: 222 return -EINVAL; 223 } 224} 225 226static int lv0104cs_read_raw(struct iio_dev *indio_dev, 227 struct iio_chan_spec const *chan, 228 int *val, int *val2, long mask) 229{ 230 struct lv0104cs_private *lv0104cs = iio_priv(indio_dev); 231 int ret; 232 233 if (chan->type != IIO_LIGHT) 234 return -EINVAL; 235 236 mutex_lock(&lv0104cs->lock); 237 238 switch (mask) { 239 case IIO_CHAN_INFO_PROCESSED: 240 ret = lv0104cs_get_lux(lv0104cs, val, val2); 241 if (ret) 242 goto err_mutex; 243 ret = IIO_VAL_INT_PLUS_MICRO; 244 break; 245 246 case IIO_CHAN_INFO_CALIBSCALE: 247 *val = lv0104cs_calibscales[lv0104cs->calibscale].val; 248 *val2 = lv0104cs_calibscales[lv0104cs->calibscale].val2; 249 ret = IIO_VAL_INT_PLUS_MICRO; 250 break; 251 252 case IIO_CHAN_INFO_SCALE: 253 *val = lv0104cs_scales[lv0104cs->scale].val; 254 *val2 = lv0104cs_scales[lv0104cs->scale].val2; 255 ret = IIO_VAL_INT_PLUS_MICRO; 256 break; 257 258 case IIO_CHAN_INFO_INT_TIME: 259 *val = lv0104cs_int_times[lv0104cs->int_time].val; 260 *val2 = lv0104cs_int_times[lv0104cs->int_time].val2; 261 ret = IIO_VAL_INT_PLUS_MICRO; 262 break; 263 264 default: 265 ret = -EINVAL; 266 } 267 268err_mutex: 269 mutex_unlock(&lv0104cs->lock); 270 271 return ret; 272} 273 274static int lv0104cs_set_calibscale(struct lv0104cs_private *lv0104cs, 275 int val, int val2) 276{ 277 int calibscale = val * 1000000 + val2; 278 int floor, ceil, mid; 279 int ret, i, index; 280 281 /* round to nearest quantized calibscale (sensitivity) */ 282 for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales) - 1; i++) { 283 floor = lv0104cs_calibscales[i].val * 1000000 284 + lv0104cs_calibscales[i].val2; 285 ceil = lv0104cs_calibscales[i + 1].val * 1000000 286 + lv0104cs_calibscales[i + 1].val2; 287 mid = (floor + ceil) / 2; 288 289 /* round down */ 290 if (calibscale >= floor && calibscale < mid) { 291 index = i; 292 break; 293 } 294 295 /* round up */ 296 if (calibscale >= mid && calibscale <= ceil) { 297 index = i + 1; 298 break; 299 } 300 } 301 302 if (i == ARRAY_SIZE(lv0104cs_calibscales) - 1) 303 return -EINVAL; 304 305 mutex_lock(&lv0104cs->lock); 306 307 /* set calibscale (sensitivity) */ 308 ret = lv0104cs_write_reg(lv0104cs->client, 309 lv0104cs_calibscales[index].regval); 310 if (ret) 311 goto err_mutex; 312 313 lv0104cs->calibscale = index; 314 315err_mutex: 316 mutex_unlock(&lv0104cs->lock); 317 318 return ret; 319} 320 321static int lv0104cs_set_scale(struct lv0104cs_private *lv0104cs, 322 int val, int val2) 323{ 324 int i; 325 326 /* hard matching */ 327 for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) { 328 if (val != lv0104cs_scales[i].val) 329 continue; 330 331 if (val2 == lv0104cs_scales[i].val2) 332 break; 333 } 334 335 if (i == ARRAY_SIZE(lv0104cs_scales)) 336 return -EINVAL; 337 338 mutex_lock(&lv0104cs->lock); 339 lv0104cs->scale = i; 340 mutex_unlock(&lv0104cs->lock); 341 342 return 0; 343} 344 345static int lv0104cs_set_int_time(struct lv0104cs_private *lv0104cs, 346 int val, int val2) 347{ 348 int i; 349 350 /* hard matching */ 351 for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) { 352 if (val != lv0104cs_int_times[i].val) 353 continue; 354 355 if (val2 == lv0104cs_int_times[i].val2) 356 break; 357 } 358 359 if (i == ARRAY_SIZE(lv0104cs_int_times)) 360 return -EINVAL; 361 362 mutex_lock(&lv0104cs->lock); 363 lv0104cs->int_time = i; 364 mutex_unlock(&lv0104cs->lock); 365 366 return 0; 367} 368 369static int lv0104cs_write_raw(struct iio_dev *indio_dev, 370 struct iio_chan_spec const *chan, 371 int val, int val2, long mask) 372{ 373 struct lv0104cs_private *lv0104cs = iio_priv(indio_dev); 374 375 if (chan->type != IIO_LIGHT) 376 return -EINVAL; 377 378 switch (mask) { 379 case IIO_CHAN_INFO_CALIBSCALE: 380 return lv0104cs_set_calibscale(lv0104cs, val, val2); 381 382 case IIO_CHAN_INFO_SCALE: 383 return lv0104cs_set_scale(lv0104cs, val, val2); 384 385 case IIO_CHAN_INFO_INT_TIME: 386 return lv0104cs_set_int_time(lv0104cs, val, val2); 387 388 default: 389 return -EINVAL; 390 } 391} 392 393static ssize_t lv0104cs_show_calibscale_avail(struct device *dev, 394 struct device_attribute *attr, char *buf) 395{ 396 ssize_t len = 0; 397 int i; 398 399 for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales); i++) { 400 len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ", 401 lv0104cs_calibscales[i].val, 402 lv0104cs_calibscales[i].val2); 403 } 404 405 buf[len - 1] = '\n'; 406 407 return len; 408} 409 410static ssize_t lv0104cs_show_scale_avail(struct device *dev, 411 struct device_attribute *attr, char *buf) 412{ 413 ssize_t len = 0; 414 int i; 415 416 for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) { 417 len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ", 418 lv0104cs_scales[i].val, 419 lv0104cs_scales[i].val2); 420 } 421 422 buf[len - 1] = '\n'; 423 424 return len; 425} 426 427static ssize_t lv0104cs_show_int_time_avail(struct device *dev, 428 struct device_attribute *attr, char *buf) 429{ 430 ssize_t len = 0; 431 int i; 432 433 for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) { 434 len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ", 435 lv0104cs_int_times[i].val, 436 lv0104cs_int_times[i].val2); 437 } 438 439 buf[len - 1] = '\n'; 440 441 return len; 442} 443 444static IIO_DEVICE_ATTR(calibscale_available, 0444, 445 lv0104cs_show_calibscale_avail, NULL, 0); 446static IIO_DEVICE_ATTR(scale_available, 0444, 447 lv0104cs_show_scale_avail, NULL, 0); 448static IIO_DEV_ATTR_INT_TIME_AVAIL(lv0104cs_show_int_time_avail); 449 450static struct attribute *lv0104cs_attributes[] = { 451 &iio_dev_attr_calibscale_available.dev_attr.attr, 452 &iio_dev_attr_scale_available.dev_attr.attr, 453 &iio_dev_attr_integration_time_available.dev_attr.attr, 454 NULL 455}; 456 457static const struct attribute_group lv0104cs_attribute_group = { 458 .attrs = lv0104cs_attributes, 459}; 460 461static const struct iio_info lv0104cs_info = { 462 .attrs = &lv0104cs_attribute_group, 463 .read_raw = &lv0104cs_read_raw, 464 .write_raw = &lv0104cs_write_raw, 465}; 466 467static const struct iio_chan_spec lv0104cs_channels[] = { 468 { 469 .type = IIO_LIGHT, 470 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | 471 BIT(IIO_CHAN_INFO_CALIBSCALE) | 472 BIT(IIO_CHAN_INFO_SCALE) | 473 BIT(IIO_CHAN_INFO_INT_TIME), 474 }, 475}; 476 477static int lv0104cs_probe(struct i2c_client *client, 478 const struct i2c_device_id *id) 479{ 480 struct iio_dev *indio_dev; 481 struct lv0104cs_private *lv0104cs; 482 int ret; 483 484 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*lv0104cs)); 485 if (!indio_dev) 486 return -ENOMEM; 487 488 lv0104cs = iio_priv(indio_dev); 489 490 i2c_set_clientdata(client, lv0104cs); 491 lv0104cs->client = client; 492 493 mutex_init(&lv0104cs->lock); 494 495 lv0104cs->calibscale = LV0104CS_CALIBSCALE_UNITY; 496 lv0104cs->scale = LV0104CS_SCALE_1X; 497 lv0104cs->int_time = LV0104CS_INTEG_200MS; 498 499 ret = lv0104cs_write_reg(lv0104cs->client, 500 lv0104cs_calibscales[LV0104CS_CALIBSCALE_UNITY].regval); 501 if (ret) 502 return ret; 503 504 indio_dev->modes = INDIO_DIRECT_MODE; 505 indio_dev->channels = lv0104cs_channels; 506 indio_dev->num_channels = ARRAY_SIZE(lv0104cs_channels); 507 indio_dev->name = client->name; 508 indio_dev->info = &lv0104cs_info; 509 510 return devm_iio_device_register(&client->dev, indio_dev); 511} 512 513static const struct i2c_device_id lv0104cs_id[] = { 514 { "lv0104cs", 0 }, 515 { } 516}; 517MODULE_DEVICE_TABLE(i2c, lv0104cs_id); 518 519static struct i2c_driver lv0104cs_i2c_driver = { 520 .driver = { 521 .name = "lv0104cs", 522 }, 523 .id_table = lv0104cs_id, 524 .probe = lv0104cs_probe, 525}; 526module_i2c_driver(lv0104cs_i2c_driver); 527 528MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>"); 529MODULE_DESCRIPTION("LV0104CS Ambient Light Sensor Driver"); 530MODULE_LICENSE("GPL");