10f079547SJeff LaBundy // SPDX-License-Identifier: GPL-2.0+ 20f079547SJeff LaBundy /* 30f079547SJeff LaBundy * lv0104cs.c: LV0104CS Ambient Light Sensor Driver 40f079547SJeff LaBundy * 50f079547SJeff LaBundy * Copyright (C) 2018 60f079547SJeff LaBundy * Author: Jeff LaBundy <jeff@labundy.com> 70f079547SJeff LaBundy * 80f079547SJeff LaBundy * 7-bit I2C slave address: 0x13 90f079547SJeff LaBundy * 103593cd53SAlexander A. Klimov * Link to data sheet: https://www.onsemi.com/pub/Collateral/LV0104CS-D.PDF 110f079547SJeff LaBundy */ 120f079547SJeff LaBundy 130f079547SJeff LaBundy #include <linux/kernel.h> 140f079547SJeff LaBundy #include <linux/module.h> 150f079547SJeff LaBundy #include <linux/i2c.h> 160f079547SJeff LaBundy #include <linux/err.h> 170f079547SJeff LaBundy #include <linux/mutex.h> 180f079547SJeff LaBundy #include <linux/delay.h> 190f079547SJeff LaBundy #include <linux/iio/iio.h> 200f079547SJeff LaBundy #include <linux/iio/sysfs.h> 210f079547SJeff LaBundy 220f079547SJeff LaBundy #define LV0104CS_REGVAL_MEASURE 0xE0 230f079547SJeff LaBundy #define LV0104CS_REGVAL_SLEEP 0x00 240f079547SJeff LaBundy 250f079547SJeff LaBundy #define LV0104CS_SCALE_0_25X 0 260f079547SJeff LaBundy #define LV0104CS_SCALE_1X 1 270f079547SJeff LaBundy #define LV0104CS_SCALE_2X 2 280f079547SJeff LaBundy #define LV0104CS_SCALE_8X 3 290f079547SJeff LaBundy #define LV0104CS_SCALE_SHIFT 3 300f079547SJeff LaBundy 310f079547SJeff LaBundy #define LV0104CS_INTEG_12_5MS 0 320f079547SJeff LaBundy #define LV0104CS_INTEG_100MS 1 330f079547SJeff LaBundy #define LV0104CS_INTEG_200MS 2 340f079547SJeff LaBundy #define LV0104CS_INTEG_SHIFT 1 350f079547SJeff LaBundy 360f079547SJeff LaBundy #define LV0104CS_CALIBSCALE_UNITY 31 370f079547SJeff LaBundy 380f079547SJeff LaBundy struct lv0104cs_private { 390f079547SJeff LaBundy struct i2c_client *client; 400f079547SJeff LaBundy struct mutex lock; 410f079547SJeff LaBundy u8 calibscale; 420f079547SJeff LaBundy u8 scale; 430f079547SJeff LaBundy u8 int_time; 440f079547SJeff LaBundy }; 450f079547SJeff LaBundy 460f079547SJeff LaBundy struct lv0104cs_mapping { 470f079547SJeff LaBundy int val; 480f079547SJeff LaBundy int val2; 490f079547SJeff LaBundy u8 regval; 500f079547SJeff LaBundy }; 510f079547SJeff LaBundy 520f079547SJeff LaBundy static const struct lv0104cs_mapping lv0104cs_calibscales[] = { 530f079547SJeff LaBundy { 0, 666666, 0x81 }, 540f079547SJeff LaBundy { 0, 800000, 0x82 }, 550f079547SJeff LaBundy { 0, 857142, 0x83 }, 560f079547SJeff LaBundy { 0, 888888, 0x84 }, 570f079547SJeff LaBundy { 0, 909090, 0x85 }, 580f079547SJeff LaBundy { 0, 923076, 0x86 }, 590f079547SJeff LaBundy { 0, 933333, 0x87 }, 600f079547SJeff LaBundy { 0, 941176, 0x88 }, 610f079547SJeff LaBundy { 0, 947368, 0x89 }, 620f079547SJeff LaBundy { 0, 952380, 0x8A }, 630f079547SJeff LaBundy { 0, 956521, 0x8B }, 640f079547SJeff LaBundy { 0, 960000, 0x8C }, 650f079547SJeff LaBundy { 0, 962962, 0x8D }, 660f079547SJeff LaBundy { 0, 965517, 0x8E }, 670f079547SJeff LaBundy { 0, 967741, 0x8F }, 680f079547SJeff LaBundy { 0, 969696, 0x90 }, 690f079547SJeff LaBundy { 0, 971428, 0x91 }, 700f079547SJeff LaBundy { 0, 972972, 0x92 }, 710f079547SJeff LaBundy { 0, 974358, 0x93 }, 720f079547SJeff LaBundy { 0, 975609, 0x94 }, 730f079547SJeff LaBundy { 0, 976744, 0x95 }, 740f079547SJeff LaBundy { 0, 977777, 0x96 }, 750f079547SJeff LaBundy { 0, 978723, 0x97 }, 760f079547SJeff LaBundy { 0, 979591, 0x98 }, 770f079547SJeff LaBundy { 0, 980392, 0x99 }, 780f079547SJeff LaBundy { 0, 981132, 0x9A }, 790f079547SJeff LaBundy { 0, 981818, 0x9B }, 800f079547SJeff LaBundy { 0, 982456, 0x9C }, 810f079547SJeff LaBundy { 0, 983050, 0x9D }, 820f079547SJeff LaBundy { 0, 983606, 0x9E }, 830f079547SJeff LaBundy { 0, 984126, 0x9F }, 840f079547SJeff LaBundy { 1, 0, 0x80 }, 850f079547SJeff LaBundy { 1, 16129, 0xBF }, 860f079547SJeff LaBundy { 1, 16666, 0xBE }, 870f079547SJeff LaBundy { 1, 17241, 0xBD }, 880f079547SJeff LaBundy { 1, 17857, 0xBC }, 890f079547SJeff LaBundy { 1, 18518, 0xBB }, 900f079547SJeff LaBundy { 1, 19230, 0xBA }, 910f079547SJeff LaBundy { 1, 20000, 0xB9 }, 920f079547SJeff LaBundy { 1, 20833, 0xB8 }, 930f079547SJeff LaBundy { 1, 21739, 0xB7 }, 940f079547SJeff LaBundy { 1, 22727, 0xB6 }, 950f079547SJeff LaBundy { 1, 23809, 0xB5 }, 960f079547SJeff LaBundy { 1, 24999, 0xB4 }, 970f079547SJeff LaBundy { 1, 26315, 0xB3 }, 980f079547SJeff LaBundy { 1, 27777, 0xB2 }, 990f079547SJeff LaBundy { 1, 29411, 0xB1 }, 1000f079547SJeff LaBundy { 1, 31250, 0xB0 }, 1010f079547SJeff LaBundy { 1, 33333, 0xAF }, 1020f079547SJeff LaBundy { 1, 35714, 0xAE }, 1030f079547SJeff LaBundy { 1, 38461, 0xAD }, 1040f079547SJeff LaBundy { 1, 41666, 0xAC }, 1050f079547SJeff LaBundy { 1, 45454, 0xAB }, 1060f079547SJeff LaBundy { 1, 50000, 0xAA }, 1070f079547SJeff LaBundy { 1, 55555, 0xA9 }, 1080f079547SJeff LaBundy { 1, 62500, 0xA8 }, 1090f079547SJeff LaBundy { 1, 71428, 0xA7 }, 1100f079547SJeff LaBundy { 1, 83333, 0xA6 }, 1110f079547SJeff LaBundy { 1, 100000, 0xA5 }, 1120f079547SJeff LaBundy { 1, 125000, 0xA4 }, 1130f079547SJeff LaBundy { 1, 166666, 0xA3 }, 1140f079547SJeff LaBundy { 1, 250000, 0xA2 }, 1150f079547SJeff LaBundy { 1, 500000, 0xA1 }, 1160f079547SJeff LaBundy }; 1170f079547SJeff LaBundy 1180f079547SJeff LaBundy static const struct lv0104cs_mapping lv0104cs_scales[] = { 1190f079547SJeff LaBundy { 0, 250000, LV0104CS_SCALE_0_25X << LV0104CS_SCALE_SHIFT }, 1200f079547SJeff LaBundy { 1, 0, LV0104CS_SCALE_1X << LV0104CS_SCALE_SHIFT }, 1210f079547SJeff LaBundy { 2, 0, LV0104CS_SCALE_2X << LV0104CS_SCALE_SHIFT }, 1220f079547SJeff LaBundy { 8, 0, LV0104CS_SCALE_8X << LV0104CS_SCALE_SHIFT }, 1230f079547SJeff LaBundy }; 1240f079547SJeff LaBundy 1250f079547SJeff LaBundy static const struct lv0104cs_mapping lv0104cs_int_times[] = { 1260f079547SJeff LaBundy { 0, 12500, LV0104CS_INTEG_12_5MS << LV0104CS_INTEG_SHIFT }, 1270f079547SJeff LaBundy { 0, 100000, LV0104CS_INTEG_100MS << LV0104CS_INTEG_SHIFT }, 1280f079547SJeff LaBundy { 0, 200000, LV0104CS_INTEG_200MS << LV0104CS_INTEG_SHIFT }, 1290f079547SJeff LaBundy }; 1300f079547SJeff LaBundy 1310f079547SJeff LaBundy static int lv0104cs_write_reg(struct i2c_client *client, u8 regval) 1320f079547SJeff LaBundy { 1330f079547SJeff LaBundy int ret; 1340f079547SJeff LaBundy 1350f079547SJeff LaBundy ret = i2c_master_send(client, (char *)®val, sizeof(regval)); 1360f079547SJeff LaBundy if (ret < 0) 1370f079547SJeff LaBundy return ret; 1380f079547SJeff LaBundy if (ret != sizeof(regval)) 1390f079547SJeff LaBundy return -EIO; 1400f079547SJeff LaBundy 1410f079547SJeff LaBundy return 0; 1420f079547SJeff LaBundy } 1430f079547SJeff LaBundy 1440f079547SJeff LaBundy static int lv0104cs_read_adc(struct i2c_client *client, u16 *adc_output) 1450f079547SJeff LaBundy { 1460f079547SJeff LaBundy __be16 regval; 1470f079547SJeff LaBundy int ret; 1480f079547SJeff LaBundy 1490f079547SJeff LaBundy ret = i2c_master_recv(client, (char *)®val, sizeof(regval)); 1500f079547SJeff LaBundy if (ret < 0) 1510f079547SJeff LaBundy return ret; 1520f079547SJeff LaBundy if (ret != sizeof(regval)) 1530f079547SJeff LaBundy return -EIO; 1540f079547SJeff LaBundy 1550f079547SJeff LaBundy *adc_output = be16_to_cpu(regval); 1560f079547SJeff LaBundy 1570f079547SJeff LaBundy return 0; 1580f079547SJeff LaBundy } 1590f079547SJeff LaBundy 1600f079547SJeff LaBundy static int lv0104cs_get_lux(struct lv0104cs_private *lv0104cs, 1610f079547SJeff LaBundy int *val, int *val2) 1620f079547SJeff LaBundy { 1630f079547SJeff LaBundy u8 regval = LV0104CS_REGVAL_MEASURE; 1640f079547SJeff LaBundy u16 adc_output; 1650f079547SJeff LaBundy int ret; 1660f079547SJeff LaBundy 1670f079547SJeff LaBundy regval |= lv0104cs_scales[lv0104cs->scale].regval; 1680f079547SJeff LaBundy regval |= lv0104cs_int_times[lv0104cs->int_time].regval; 1690f079547SJeff LaBundy ret = lv0104cs_write_reg(lv0104cs->client, regval); 1700f079547SJeff LaBundy if (ret) 1710f079547SJeff LaBundy return ret; 1720f079547SJeff LaBundy 1730f079547SJeff LaBundy /* wait for integration time to pass (with margin) */ 1740f079547SJeff LaBundy switch (lv0104cs->int_time) { 1750f079547SJeff LaBundy case LV0104CS_INTEG_12_5MS: 1760f079547SJeff LaBundy msleep(50); 1770f079547SJeff LaBundy break; 1780f079547SJeff LaBundy 1790f079547SJeff LaBundy case LV0104CS_INTEG_100MS: 1800f079547SJeff LaBundy msleep(150); 1810f079547SJeff LaBundy break; 1820f079547SJeff LaBundy 1830f079547SJeff LaBundy case LV0104CS_INTEG_200MS: 1840f079547SJeff LaBundy msleep(250); 1850f079547SJeff LaBundy break; 1860f079547SJeff LaBundy 1870f079547SJeff LaBundy default: 1880f079547SJeff LaBundy return -EINVAL; 1890f079547SJeff LaBundy } 1900f079547SJeff LaBundy 1910f079547SJeff LaBundy ret = lv0104cs_read_adc(lv0104cs->client, &adc_output); 1920f079547SJeff LaBundy if (ret) 1930f079547SJeff LaBundy return ret; 1940f079547SJeff LaBundy 1950f079547SJeff LaBundy ret = lv0104cs_write_reg(lv0104cs->client, LV0104CS_REGVAL_SLEEP); 1960f079547SJeff LaBundy if (ret) 1970f079547SJeff LaBundy return ret; 1980f079547SJeff LaBundy 1990f079547SJeff LaBundy /* convert ADC output to lux */ 2000f079547SJeff LaBundy switch (lv0104cs->scale) { 2010f079547SJeff LaBundy case LV0104CS_SCALE_0_25X: 2020f079547SJeff LaBundy *val = adc_output * 4; 2030f079547SJeff LaBundy *val2 = 0; 2040f079547SJeff LaBundy return 0; 2050f079547SJeff LaBundy 2060f079547SJeff LaBundy case LV0104CS_SCALE_1X: 2070f079547SJeff LaBundy *val = adc_output; 2080f079547SJeff LaBundy *val2 = 0; 2090f079547SJeff LaBundy return 0; 2100f079547SJeff LaBundy 2110f079547SJeff LaBundy case LV0104CS_SCALE_2X: 2120f079547SJeff LaBundy *val = adc_output / 2; 2130f079547SJeff LaBundy *val2 = (adc_output % 2) * 500000; 2140f079547SJeff LaBundy return 0; 2150f079547SJeff LaBundy 2160f079547SJeff LaBundy case LV0104CS_SCALE_8X: 2170f079547SJeff LaBundy *val = adc_output / 8; 2180f079547SJeff LaBundy *val2 = (adc_output % 8) * 125000; 2190f079547SJeff LaBundy return 0; 2200f079547SJeff LaBundy 2210f079547SJeff LaBundy default: 2220f079547SJeff LaBundy return -EINVAL; 2230f079547SJeff LaBundy } 2240f079547SJeff LaBundy } 2250f079547SJeff LaBundy 2260f079547SJeff LaBundy static int lv0104cs_read_raw(struct iio_dev *indio_dev, 2270f079547SJeff LaBundy struct iio_chan_spec const *chan, 2280f079547SJeff LaBundy int *val, int *val2, long mask) 2290f079547SJeff LaBundy { 2300f079547SJeff LaBundy struct lv0104cs_private *lv0104cs = iio_priv(indio_dev); 2310f079547SJeff LaBundy int ret; 2320f079547SJeff LaBundy 2330f079547SJeff LaBundy if (chan->type != IIO_LIGHT) 2340f079547SJeff LaBundy return -EINVAL; 2350f079547SJeff LaBundy 2360f079547SJeff LaBundy mutex_lock(&lv0104cs->lock); 2370f079547SJeff LaBundy 2380f079547SJeff LaBundy switch (mask) { 2390f079547SJeff LaBundy case IIO_CHAN_INFO_PROCESSED: 2400f079547SJeff LaBundy ret = lv0104cs_get_lux(lv0104cs, val, val2); 2410f079547SJeff LaBundy if (ret) 2420f079547SJeff LaBundy goto err_mutex; 2430f079547SJeff LaBundy ret = IIO_VAL_INT_PLUS_MICRO; 2440f079547SJeff LaBundy break; 2450f079547SJeff LaBundy 2460f079547SJeff LaBundy case IIO_CHAN_INFO_CALIBSCALE: 2470f079547SJeff LaBundy *val = lv0104cs_calibscales[lv0104cs->calibscale].val; 2480f079547SJeff LaBundy *val2 = lv0104cs_calibscales[lv0104cs->calibscale].val2; 2490f079547SJeff LaBundy ret = IIO_VAL_INT_PLUS_MICRO; 2500f079547SJeff LaBundy break; 2510f079547SJeff LaBundy 2520f079547SJeff LaBundy case IIO_CHAN_INFO_SCALE: 2530f079547SJeff LaBundy *val = lv0104cs_scales[lv0104cs->scale].val; 2540f079547SJeff LaBundy *val2 = lv0104cs_scales[lv0104cs->scale].val2; 2550f079547SJeff LaBundy ret = IIO_VAL_INT_PLUS_MICRO; 2560f079547SJeff LaBundy break; 2570f079547SJeff LaBundy 2580f079547SJeff LaBundy case IIO_CHAN_INFO_INT_TIME: 2590f079547SJeff LaBundy *val = lv0104cs_int_times[lv0104cs->int_time].val; 2600f079547SJeff LaBundy *val2 = lv0104cs_int_times[lv0104cs->int_time].val2; 2610f079547SJeff LaBundy ret = IIO_VAL_INT_PLUS_MICRO; 2620f079547SJeff LaBundy break; 2630f079547SJeff LaBundy 2640f079547SJeff LaBundy default: 2650f079547SJeff LaBundy ret = -EINVAL; 2660f079547SJeff LaBundy } 2670f079547SJeff LaBundy 2680f079547SJeff LaBundy err_mutex: 2690f079547SJeff LaBundy mutex_unlock(&lv0104cs->lock); 2700f079547SJeff LaBundy 2710f079547SJeff LaBundy return ret; 2720f079547SJeff LaBundy } 2730f079547SJeff LaBundy 2740f079547SJeff LaBundy static int lv0104cs_set_calibscale(struct lv0104cs_private *lv0104cs, 2750f079547SJeff LaBundy int val, int val2) 2760f079547SJeff LaBundy { 2770f079547SJeff LaBundy int calibscale = val * 1000000 + val2; 2780f079547SJeff LaBundy int floor, ceil, mid; 2790f079547SJeff LaBundy int ret, i, index; 2800f079547SJeff LaBundy 2810f079547SJeff LaBundy /* round to nearest quantized calibscale (sensitivity) */ 2820f079547SJeff LaBundy for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales) - 1; i++) { 2830f079547SJeff LaBundy floor = lv0104cs_calibscales[i].val * 1000000 2840f079547SJeff LaBundy + lv0104cs_calibscales[i].val2; 2850f079547SJeff LaBundy ceil = lv0104cs_calibscales[i + 1].val * 1000000 2860f079547SJeff LaBundy + lv0104cs_calibscales[i + 1].val2; 2870f079547SJeff LaBundy mid = (floor + ceil) / 2; 2880f079547SJeff LaBundy 2890f079547SJeff LaBundy /* round down */ 2900f079547SJeff LaBundy if (calibscale >= floor && calibscale < mid) { 2910f079547SJeff LaBundy index = i; 2920f079547SJeff LaBundy break; 2930f079547SJeff LaBundy } 2940f079547SJeff LaBundy 2950f079547SJeff LaBundy /* round up */ 2960f079547SJeff LaBundy if (calibscale >= mid && calibscale <= ceil) { 2970f079547SJeff LaBundy index = i + 1; 2980f079547SJeff LaBundy break; 2990f079547SJeff LaBundy } 3000f079547SJeff LaBundy } 3010f079547SJeff LaBundy 3020f079547SJeff LaBundy if (i == ARRAY_SIZE(lv0104cs_calibscales) - 1) 3030f079547SJeff LaBundy return -EINVAL; 3040f079547SJeff LaBundy 3050f079547SJeff LaBundy mutex_lock(&lv0104cs->lock); 3060f079547SJeff LaBundy 3070f079547SJeff LaBundy /* set calibscale (sensitivity) */ 3080f079547SJeff LaBundy ret = lv0104cs_write_reg(lv0104cs->client, 3090f079547SJeff LaBundy lv0104cs_calibscales[index].regval); 3100f079547SJeff LaBundy if (ret) 3110f079547SJeff LaBundy goto err_mutex; 3120f079547SJeff LaBundy 3130f079547SJeff LaBundy lv0104cs->calibscale = index; 3140f079547SJeff LaBundy 3150f079547SJeff LaBundy err_mutex: 3160f079547SJeff LaBundy mutex_unlock(&lv0104cs->lock); 3170f079547SJeff LaBundy 3180f079547SJeff LaBundy return ret; 3190f079547SJeff LaBundy } 3200f079547SJeff LaBundy 3210f079547SJeff LaBundy static int lv0104cs_set_scale(struct lv0104cs_private *lv0104cs, 3220f079547SJeff LaBundy int val, int val2) 3230f079547SJeff LaBundy { 3240f079547SJeff LaBundy int i; 3250f079547SJeff LaBundy 3260f079547SJeff LaBundy /* hard matching */ 3270f079547SJeff LaBundy for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) { 3280f079547SJeff LaBundy if (val != lv0104cs_scales[i].val) 3290f079547SJeff LaBundy continue; 3300f079547SJeff LaBundy 3310f079547SJeff LaBundy if (val2 == lv0104cs_scales[i].val2) 3320f079547SJeff LaBundy break; 3330f079547SJeff LaBundy } 3340f079547SJeff LaBundy 3350f079547SJeff LaBundy if (i == ARRAY_SIZE(lv0104cs_scales)) 3360f079547SJeff LaBundy return -EINVAL; 3370f079547SJeff LaBundy 3380f079547SJeff LaBundy mutex_lock(&lv0104cs->lock); 3390f079547SJeff LaBundy lv0104cs->scale = i; 3400f079547SJeff LaBundy mutex_unlock(&lv0104cs->lock); 3410f079547SJeff LaBundy 3420f079547SJeff LaBundy return 0; 3430f079547SJeff LaBundy } 3440f079547SJeff LaBundy 3450f079547SJeff LaBundy static int lv0104cs_set_int_time(struct lv0104cs_private *lv0104cs, 3460f079547SJeff LaBundy int val, int val2) 3470f079547SJeff LaBundy { 3480f079547SJeff LaBundy int i; 3490f079547SJeff LaBundy 3500f079547SJeff LaBundy /* hard matching */ 3510f079547SJeff LaBundy for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) { 3520f079547SJeff LaBundy if (val != lv0104cs_int_times[i].val) 3530f079547SJeff LaBundy continue; 3540f079547SJeff LaBundy 3550f079547SJeff LaBundy if (val2 == lv0104cs_int_times[i].val2) 3560f079547SJeff LaBundy break; 3570f079547SJeff LaBundy } 3580f079547SJeff LaBundy 3590f079547SJeff LaBundy if (i == ARRAY_SIZE(lv0104cs_int_times)) 3600f079547SJeff LaBundy return -EINVAL; 3610f079547SJeff LaBundy 3620f079547SJeff LaBundy mutex_lock(&lv0104cs->lock); 3630f079547SJeff LaBundy lv0104cs->int_time = i; 3640f079547SJeff LaBundy mutex_unlock(&lv0104cs->lock); 3650f079547SJeff LaBundy 3660f079547SJeff LaBundy return 0; 3670f079547SJeff LaBundy } 3680f079547SJeff LaBundy 3690f079547SJeff LaBundy static int lv0104cs_write_raw(struct iio_dev *indio_dev, 3700f079547SJeff LaBundy struct iio_chan_spec const *chan, 3710f079547SJeff LaBundy int val, int val2, long mask) 3720f079547SJeff LaBundy { 3730f079547SJeff LaBundy struct lv0104cs_private *lv0104cs = iio_priv(indio_dev); 3740f079547SJeff LaBundy 3750f079547SJeff LaBundy if (chan->type != IIO_LIGHT) 3760f079547SJeff LaBundy return -EINVAL; 3770f079547SJeff LaBundy 3780f079547SJeff LaBundy switch (mask) { 3790f079547SJeff LaBundy case IIO_CHAN_INFO_CALIBSCALE: 3800f079547SJeff LaBundy return lv0104cs_set_calibscale(lv0104cs, val, val2); 3810f079547SJeff LaBundy 3820f079547SJeff LaBundy case IIO_CHAN_INFO_SCALE: 3830f079547SJeff LaBundy return lv0104cs_set_scale(lv0104cs, val, val2); 3840f079547SJeff LaBundy 3850f079547SJeff LaBundy case IIO_CHAN_INFO_INT_TIME: 3860f079547SJeff LaBundy return lv0104cs_set_int_time(lv0104cs, val, val2); 3870f079547SJeff LaBundy 3880f079547SJeff LaBundy default: 3890f079547SJeff LaBundy return -EINVAL; 3900f079547SJeff LaBundy } 3910f079547SJeff LaBundy } 3920f079547SJeff LaBundy 3930f079547SJeff LaBundy static ssize_t lv0104cs_show_calibscale_avail(struct device *dev, 3940f079547SJeff LaBundy struct device_attribute *attr, char *buf) 3950f079547SJeff LaBundy { 3960f079547SJeff LaBundy ssize_t len = 0; 3970f079547SJeff LaBundy int i; 3980f079547SJeff LaBundy 3990f079547SJeff LaBundy for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales); i++) { 4000f079547SJeff LaBundy len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ", 4010f079547SJeff LaBundy lv0104cs_calibscales[i].val, 4020f079547SJeff LaBundy lv0104cs_calibscales[i].val2); 4030f079547SJeff LaBundy } 4040f079547SJeff LaBundy 4050f079547SJeff LaBundy buf[len - 1] = '\n'; 4060f079547SJeff LaBundy 4070f079547SJeff LaBundy return len; 4080f079547SJeff LaBundy } 4090f079547SJeff LaBundy 4100f079547SJeff LaBundy static ssize_t lv0104cs_show_scale_avail(struct device *dev, 4110f079547SJeff LaBundy struct device_attribute *attr, char *buf) 4120f079547SJeff LaBundy { 4130f079547SJeff LaBundy ssize_t len = 0; 4140f079547SJeff LaBundy int i; 4150f079547SJeff LaBundy 4160f079547SJeff LaBundy for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) { 4170f079547SJeff LaBundy len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ", 4180f079547SJeff LaBundy lv0104cs_scales[i].val, 4190f079547SJeff LaBundy lv0104cs_scales[i].val2); 4200f079547SJeff LaBundy } 4210f079547SJeff LaBundy 4220f079547SJeff LaBundy buf[len - 1] = '\n'; 4230f079547SJeff LaBundy 4240f079547SJeff LaBundy return len; 4250f079547SJeff LaBundy } 4260f079547SJeff LaBundy 4270f079547SJeff LaBundy static ssize_t lv0104cs_show_int_time_avail(struct device *dev, 4280f079547SJeff LaBundy struct device_attribute *attr, char *buf) 4290f079547SJeff LaBundy { 4300f079547SJeff LaBundy ssize_t len = 0; 4310f079547SJeff LaBundy int i; 4320f079547SJeff LaBundy 4330f079547SJeff LaBundy for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) { 4340f079547SJeff LaBundy len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ", 4350f079547SJeff LaBundy lv0104cs_int_times[i].val, 4360f079547SJeff LaBundy lv0104cs_int_times[i].val2); 4370f079547SJeff LaBundy } 4380f079547SJeff LaBundy 4390f079547SJeff LaBundy buf[len - 1] = '\n'; 4400f079547SJeff LaBundy 4410f079547SJeff LaBundy return len; 4420f079547SJeff LaBundy } 4430f079547SJeff LaBundy 4440f079547SJeff LaBundy static IIO_DEVICE_ATTR(calibscale_available, 0444, 4450f079547SJeff LaBundy lv0104cs_show_calibscale_avail, NULL, 0); 4460f079547SJeff LaBundy static IIO_DEVICE_ATTR(scale_available, 0444, 4470f079547SJeff LaBundy lv0104cs_show_scale_avail, NULL, 0); 4480f079547SJeff LaBundy static IIO_DEV_ATTR_INT_TIME_AVAIL(lv0104cs_show_int_time_avail); 4490f079547SJeff LaBundy 4500f079547SJeff LaBundy static struct attribute *lv0104cs_attributes[] = { 4510f079547SJeff LaBundy &iio_dev_attr_calibscale_available.dev_attr.attr, 4520f079547SJeff LaBundy &iio_dev_attr_scale_available.dev_attr.attr, 4530f079547SJeff LaBundy &iio_dev_attr_integration_time_available.dev_attr.attr, 4540f079547SJeff LaBundy NULL 4550f079547SJeff LaBundy }; 4560f079547SJeff LaBundy 4570f079547SJeff LaBundy static const struct attribute_group lv0104cs_attribute_group = { 4580f079547SJeff LaBundy .attrs = lv0104cs_attributes, 4590f079547SJeff LaBundy }; 4600f079547SJeff LaBundy 4610f079547SJeff LaBundy static const struct iio_info lv0104cs_info = { 4620f079547SJeff LaBundy .attrs = &lv0104cs_attribute_group, 4630f079547SJeff LaBundy .read_raw = &lv0104cs_read_raw, 4640f079547SJeff LaBundy .write_raw = &lv0104cs_write_raw, 4650f079547SJeff LaBundy }; 4660f079547SJeff LaBundy 4670f079547SJeff LaBundy static const struct iio_chan_spec lv0104cs_channels[] = { 4680f079547SJeff LaBundy { 4690f079547SJeff LaBundy .type = IIO_LIGHT, 4700f079547SJeff LaBundy .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | 4710f079547SJeff LaBundy BIT(IIO_CHAN_INFO_CALIBSCALE) | 4720f079547SJeff LaBundy BIT(IIO_CHAN_INFO_SCALE) | 4730f079547SJeff LaBundy BIT(IIO_CHAN_INFO_INT_TIME), 4740f079547SJeff LaBundy }, 4750f079547SJeff LaBundy }; 4760f079547SJeff LaBundy 4770f079547SJeff LaBundy static int lv0104cs_probe(struct i2c_client *client, 4780f079547SJeff LaBundy const struct i2c_device_id *id) 4790f079547SJeff LaBundy { 4800f079547SJeff LaBundy struct iio_dev *indio_dev; 4810f079547SJeff LaBundy struct lv0104cs_private *lv0104cs; 4820f079547SJeff LaBundy int ret; 4830f079547SJeff LaBundy 4840f079547SJeff LaBundy indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*lv0104cs)); 4850f079547SJeff LaBundy if (!indio_dev) 4860f079547SJeff LaBundy return -ENOMEM; 4870f079547SJeff LaBundy 4880f079547SJeff LaBundy lv0104cs = iio_priv(indio_dev); 4890f079547SJeff LaBundy 4900f079547SJeff LaBundy i2c_set_clientdata(client, lv0104cs); 4910f079547SJeff LaBundy lv0104cs->client = client; 4920f079547SJeff LaBundy 4930f079547SJeff LaBundy mutex_init(&lv0104cs->lock); 4940f079547SJeff LaBundy 4950f079547SJeff LaBundy lv0104cs->calibscale = LV0104CS_CALIBSCALE_UNITY; 4960f079547SJeff LaBundy lv0104cs->scale = LV0104CS_SCALE_1X; 4970f079547SJeff LaBundy lv0104cs->int_time = LV0104CS_INTEG_200MS; 4980f079547SJeff LaBundy 4990f079547SJeff LaBundy ret = lv0104cs_write_reg(lv0104cs->client, 5000f079547SJeff LaBundy lv0104cs_calibscales[LV0104CS_CALIBSCALE_UNITY].regval); 5010f079547SJeff LaBundy if (ret) 5020f079547SJeff LaBundy return ret; 5030f079547SJeff LaBundy 5040f079547SJeff LaBundy indio_dev->modes = INDIO_DIRECT_MODE; 5050f079547SJeff LaBundy indio_dev->channels = lv0104cs_channels; 5060f079547SJeff LaBundy indio_dev->num_channels = ARRAY_SIZE(lv0104cs_channels); 5070f079547SJeff LaBundy indio_dev->name = client->name; 5080f079547SJeff LaBundy indio_dev->info = &lv0104cs_info; 5090f079547SJeff LaBundy 5100f079547SJeff LaBundy return devm_iio_device_register(&client->dev, indio_dev); 5110f079547SJeff LaBundy } 5120f079547SJeff LaBundy 5130f079547SJeff LaBundy static const struct i2c_device_id lv0104cs_id[] = { 5140f079547SJeff LaBundy { "lv0104cs", 0 }, 5150f079547SJeff LaBundy { } 5160f079547SJeff LaBundy }; 5170f079547SJeff LaBundy MODULE_DEVICE_TABLE(i2c, lv0104cs_id); 5180f079547SJeff LaBundy 5190f079547SJeff LaBundy static struct i2c_driver lv0104cs_i2c_driver = { 5200f079547SJeff LaBundy .driver = { 5210f079547SJeff LaBundy .name = "lv0104cs", 5220f079547SJeff LaBundy }, 5230f079547SJeff LaBundy .id_table = lv0104cs_id, 5240f079547SJeff LaBundy .probe = lv0104cs_probe, 5250f079547SJeff LaBundy }; 5260f079547SJeff LaBundy module_i2c_driver(lv0104cs_i2c_driver); 5270f079547SJeff LaBundy 5280f079547SJeff LaBundy MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>"); 5290f079547SJeff LaBundy MODULE_DESCRIPTION("LV0104CS Ambient Light Sensor Driver"); 5300f079547SJeff LaBundy MODULE_LICENSE("GPL"); 531