174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23cdbbeebSDaniel Mack /*
33cdbbeebSDaniel Mack * isl29003.c - Linux kernel module for
43cdbbeebSDaniel Mack * Intersil ISL29003 ambient light sensor
53cdbbeebSDaniel Mack *
69b5db89eSMauro Carvalho Chehab * See file:Documentation/misc-devices/isl29003.rst
73cdbbeebSDaniel Mack *
83cdbbeebSDaniel Mack * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
93cdbbeebSDaniel Mack *
103cdbbeebSDaniel Mack * Based on code written by
113cdbbeebSDaniel Mack * Rodolfo Giometti <giometti@linux.it>
123cdbbeebSDaniel Mack * Eurotech S.p.A. <info@eurotech.it>
133cdbbeebSDaniel Mack */
143cdbbeebSDaniel Mack
153cdbbeebSDaniel Mack #include <linux/module.h>
163cdbbeebSDaniel Mack #include <linux/slab.h>
173cdbbeebSDaniel Mack #include <linux/i2c.h>
183cdbbeebSDaniel Mack #include <linux/mutex.h>
193cdbbeebSDaniel Mack #include <linux/delay.h>
203cdbbeebSDaniel Mack
213cdbbeebSDaniel Mack #define ISL29003_DRV_NAME "isl29003"
223cdbbeebSDaniel Mack #define DRIVER_VERSION "1.0"
233cdbbeebSDaniel Mack
243cdbbeebSDaniel Mack #define ISL29003_REG_COMMAND 0x00
253cdbbeebSDaniel Mack #define ISL29003_ADC_ENABLED (1 << 7)
263cdbbeebSDaniel Mack #define ISL29003_ADC_PD (1 << 6)
273cdbbeebSDaniel Mack #define ISL29003_TIMING_INT (1 << 5)
283cdbbeebSDaniel Mack #define ISL29003_MODE_SHIFT (2)
293cdbbeebSDaniel Mack #define ISL29003_MODE_MASK (0x3 << ISL29003_MODE_SHIFT)
303cdbbeebSDaniel Mack #define ISL29003_RES_SHIFT (0)
313cdbbeebSDaniel Mack #define ISL29003_RES_MASK (0x3 << ISL29003_RES_SHIFT)
323cdbbeebSDaniel Mack
333cdbbeebSDaniel Mack #define ISL29003_REG_CONTROL 0x01
343cdbbeebSDaniel Mack #define ISL29003_INT_FLG (1 << 5)
353cdbbeebSDaniel Mack #define ISL29003_RANGE_SHIFT (2)
363cdbbeebSDaniel Mack #define ISL29003_RANGE_MASK (0x3 << ISL29003_RANGE_SHIFT)
373cdbbeebSDaniel Mack #define ISL29003_INT_PERSISTS_SHIFT (0)
383cdbbeebSDaniel Mack #define ISL29003_INT_PERSISTS_MASK (0xf << ISL29003_INT_PERSISTS_SHIFT)
393cdbbeebSDaniel Mack
403cdbbeebSDaniel Mack #define ISL29003_REG_IRQ_THRESH_HI 0x02
413cdbbeebSDaniel Mack #define ISL29003_REG_IRQ_THRESH_LO 0x03
423cdbbeebSDaniel Mack #define ISL29003_REG_LSB_SENSOR 0x04
433cdbbeebSDaniel Mack #define ISL29003_REG_MSB_SENSOR 0x05
443cdbbeebSDaniel Mack #define ISL29003_REG_LSB_TIMER 0x06
453cdbbeebSDaniel Mack #define ISL29003_REG_MSB_TIMER 0x07
463cdbbeebSDaniel Mack
473cdbbeebSDaniel Mack #define ISL29003_NUM_CACHABLE_REGS 4
483cdbbeebSDaniel Mack
493cdbbeebSDaniel Mack struct isl29003_data {
503cdbbeebSDaniel Mack struct i2c_client *client;
513cdbbeebSDaniel Mack struct mutex lock;
523cdbbeebSDaniel Mack u8 reg_cache[ISL29003_NUM_CACHABLE_REGS];
5374614f8dSDaniel Mack u8 power_state_before_suspend;
543cdbbeebSDaniel Mack };
553cdbbeebSDaniel Mack
563cdbbeebSDaniel Mack static int gain_range[] = {
573cdbbeebSDaniel Mack 1000, 4000, 16000, 64000
583cdbbeebSDaniel Mack };
593cdbbeebSDaniel Mack
603cdbbeebSDaniel Mack /*
613cdbbeebSDaniel Mack * register access helpers
623cdbbeebSDaniel Mack */
633cdbbeebSDaniel Mack
__isl29003_read_reg(struct i2c_client * client,u32 reg,u8 mask,u8 shift)643cdbbeebSDaniel Mack static int __isl29003_read_reg(struct i2c_client *client,
653cdbbeebSDaniel Mack u32 reg, u8 mask, u8 shift)
663cdbbeebSDaniel Mack {
673cdbbeebSDaniel Mack struct isl29003_data *data = i2c_get_clientdata(client);
6805eec0c9SDhaval Shah
693cdbbeebSDaniel Mack return (data->reg_cache[reg] & mask) >> shift;
703cdbbeebSDaniel Mack }
713cdbbeebSDaniel Mack
__isl29003_write_reg(struct i2c_client * client,u32 reg,u8 mask,u8 shift,u8 val)723cdbbeebSDaniel Mack static int __isl29003_write_reg(struct i2c_client *client,
733cdbbeebSDaniel Mack u32 reg, u8 mask, u8 shift, u8 val)
743cdbbeebSDaniel Mack {
753cdbbeebSDaniel Mack struct isl29003_data *data = i2c_get_clientdata(client);
763cdbbeebSDaniel Mack int ret = 0;
773cdbbeebSDaniel Mack u8 tmp;
783cdbbeebSDaniel Mack
793cdbbeebSDaniel Mack if (reg >= ISL29003_NUM_CACHABLE_REGS)
803cdbbeebSDaniel Mack return -EINVAL;
813cdbbeebSDaniel Mack
823cdbbeebSDaniel Mack mutex_lock(&data->lock);
833cdbbeebSDaniel Mack
843cdbbeebSDaniel Mack tmp = data->reg_cache[reg];
853cdbbeebSDaniel Mack tmp &= ~mask;
863cdbbeebSDaniel Mack tmp |= val << shift;
873cdbbeebSDaniel Mack
883cdbbeebSDaniel Mack ret = i2c_smbus_write_byte_data(client, reg, tmp);
893cdbbeebSDaniel Mack if (!ret)
903cdbbeebSDaniel Mack data->reg_cache[reg] = tmp;
913cdbbeebSDaniel Mack
923cdbbeebSDaniel Mack mutex_unlock(&data->lock);
933cdbbeebSDaniel Mack return ret;
943cdbbeebSDaniel Mack }
953cdbbeebSDaniel Mack
963cdbbeebSDaniel Mack /*
973cdbbeebSDaniel Mack * internally used functions
983cdbbeebSDaniel Mack */
993cdbbeebSDaniel Mack
1003cdbbeebSDaniel Mack /* range */
isl29003_get_range(struct i2c_client * client)1013cdbbeebSDaniel Mack static int isl29003_get_range(struct i2c_client *client)
1023cdbbeebSDaniel Mack {
1033cdbbeebSDaniel Mack return __isl29003_read_reg(client, ISL29003_REG_CONTROL,
1043cdbbeebSDaniel Mack ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT);
1053cdbbeebSDaniel Mack }
1063cdbbeebSDaniel Mack
isl29003_set_range(struct i2c_client * client,int range)1073cdbbeebSDaniel Mack static int isl29003_set_range(struct i2c_client *client, int range)
1083cdbbeebSDaniel Mack {
1093cdbbeebSDaniel Mack return __isl29003_write_reg(client, ISL29003_REG_CONTROL,
1103cdbbeebSDaniel Mack ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT, range);
1113cdbbeebSDaniel Mack }
1123cdbbeebSDaniel Mack
1133cdbbeebSDaniel Mack /* resolution */
isl29003_get_resolution(struct i2c_client * client)1143cdbbeebSDaniel Mack static int isl29003_get_resolution(struct i2c_client *client)
1153cdbbeebSDaniel Mack {
1163cdbbeebSDaniel Mack return __isl29003_read_reg(client, ISL29003_REG_COMMAND,
1173cdbbeebSDaniel Mack ISL29003_RES_MASK, ISL29003_RES_SHIFT);
1183cdbbeebSDaniel Mack }
1193cdbbeebSDaniel Mack
isl29003_set_resolution(struct i2c_client * client,int res)1203cdbbeebSDaniel Mack static int isl29003_set_resolution(struct i2c_client *client, int res)
1213cdbbeebSDaniel Mack {
1223cdbbeebSDaniel Mack return __isl29003_write_reg(client, ISL29003_REG_COMMAND,
1233cdbbeebSDaniel Mack ISL29003_RES_MASK, ISL29003_RES_SHIFT, res);
1243cdbbeebSDaniel Mack }
1253cdbbeebSDaniel Mack
1263cdbbeebSDaniel Mack /* mode */
isl29003_get_mode(struct i2c_client * client)1273cdbbeebSDaniel Mack static int isl29003_get_mode(struct i2c_client *client)
1283cdbbeebSDaniel Mack {
1293cdbbeebSDaniel Mack return __isl29003_read_reg(client, ISL29003_REG_COMMAND,
130a73a0712SZhou Xingxing ISL29003_MODE_MASK, ISL29003_MODE_SHIFT);
1313cdbbeebSDaniel Mack }
1323cdbbeebSDaniel Mack
isl29003_set_mode(struct i2c_client * client,int mode)1333cdbbeebSDaniel Mack static int isl29003_set_mode(struct i2c_client *client, int mode)
1343cdbbeebSDaniel Mack {
1353cdbbeebSDaniel Mack return __isl29003_write_reg(client, ISL29003_REG_COMMAND,
136a73a0712SZhou Xingxing ISL29003_MODE_MASK, ISL29003_MODE_SHIFT, mode);
1373cdbbeebSDaniel Mack }
1383cdbbeebSDaniel Mack
1393cdbbeebSDaniel Mack /* power_state */
isl29003_set_power_state(struct i2c_client * client,int state)1403cdbbeebSDaniel Mack static int isl29003_set_power_state(struct i2c_client *client, int state)
1413cdbbeebSDaniel Mack {
1423cdbbeebSDaniel Mack return __isl29003_write_reg(client, ISL29003_REG_COMMAND,
1433cdbbeebSDaniel Mack ISL29003_ADC_ENABLED | ISL29003_ADC_PD, 0,
1443cdbbeebSDaniel Mack state ? ISL29003_ADC_ENABLED : ISL29003_ADC_PD);
1453cdbbeebSDaniel Mack }
1463cdbbeebSDaniel Mack
isl29003_get_power_state(struct i2c_client * client)1473cdbbeebSDaniel Mack static int isl29003_get_power_state(struct i2c_client *client)
1483cdbbeebSDaniel Mack {
1493cdbbeebSDaniel Mack struct isl29003_data *data = i2c_get_clientdata(client);
1503cdbbeebSDaniel Mack u8 cmdreg = data->reg_cache[ISL29003_REG_COMMAND];
15105eec0c9SDhaval Shah
1523cdbbeebSDaniel Mack return ~cmdreg & ISL29003_ADC_PD;
1533cdbbeebSDaniel Mack }
1543cdbbeebSDaniel Mack
isl29003_get_adc_value(struct i2c_client * client)1553cdbbeebSDaniel Mack static int isl29003_get_adc_value(struct i2c_client *client)
1563cdbbeebSDaniel Mack {
1573cdbbeebSDaniel Mack struct isl29003_data *data = i2c_get_clientdata(client);
1583cdbbeebSDaniel Mack int lsb, msb, range, bitdepth;
1593cdbbeebSDaniel Mack
1603cdbbeebSDaniel Mack mutex_lock(&data->lock);
1613cdbbeebSDaniel Mack lsb = i2c_smbus_read_byte_data(client, ISL29003_REG_LSB_SENSOR);
1623cdbbeebSDaniel Mack
1633cdbbeebSDaniel Mack if (lsb < 0) {
1643cdbbeebSDaniel Mack mutex_unlock(&data->lock);
1653cdbbeebSDaniel Mack return lsb;
1663cdbbeebSDaniel Mack }
1673cdbbeebSDaniel Mack
1683cdbbeebSDaniel Mack msb = i2c_smbus_read_byte_data(client, ISL29003_REG_MSB_SENSOR);
1693cdbbeebSDaniel Mack mutex_unlock(&data->lock);
1703cdbbeebSDaniel Mack
1713cdbbeebSDaniel Mack if (msb < 0)
1723cdbbeebSDaniel Mack return msb;
1733cdbbeebSDaniel Mack
1743cdbbeebSDaniel Mack range = isl29003_get_range(client);
1753cdbbeebSDaniel Mack bitdepth = (4 - isl29003_get_resolution(client)) * 4;
1763cdbbeebSDaniel Mack return (((msb << 8) | lsb) * gain_range[range]) >> bitdepth;
1773cdbbeebSDaniel Mack }
1783cdbbeebSDaniel Mack
1793cdbbeebSDaniel Mack /*
1803cdbbeebSDaniel Mack * sysfs layer
1813cdbbeebSDaniel Mack */
1823cdbbeebSDaniel Mack
1833cdbbeebSDaniel Mack /* range */
isl29003_show_range(struct device * dev,struct device_attribute * attr,char * buf)1843cdbbeebSDaniel Mack static ssize_t isl29003_show_range(struct device *dev,
1853cdbbeebSDaniel Mack struct device_attribute *attr, char *buf)
1863cdbbeebSDaniel Mack {
1873cdbbeebSDaniel Mack struct i2c_client *client = to_i2c_client(dev);
18805eec0c9SDhaval Shah
189cc3db79bSBo Liu return sysfs_emit(buf, "%i\n", isl29003_get_range(client));
1903cdbbeebSDaniel Mack }
1913cdbbeebSDaniel Mack
isl29003_store_range(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1923cdbbeebSDaniel Mack static ssize_t isl29003_store_range(struct device *dev,
1933cdbbeebSDaniel Mack struct device_attribute *attr,
1943cdbbeebSDaniel Mack const char *buf, size_t count)
1953cdbbeebSDaniel Mack {
1963cdbbeebSDaniel Mack struct i2c_client *client = to_i2c_client(dev);
1973cdbbeebSDaniel Mack unsigned long val;
1983cdbbeebSDaniel Mack int ret;
1993cdbbeebSDaniel Mack
200f7b41276SJingoo Han ret = kstrtoul(buf, 10, &val);
201f7b41276SJingoo Han if (ret)
202f7b41276SJingoo Han return ret;
203f7b41276SJingoo Han
204f7b41276SJingoo Han if (val > 3)
2053cdbbeebSDaniel Mack return -EINVAL;
2063cdbbeebSDaniel Mack
2073cdbbeebSDaniel Mack ret = isl29003_set_range(client, val);
2083cdbbeebSDaniel Mack if (ret < 0)
2093cdbbeebSDaniel Mack return ret;
2103cdbbeebSDaniel Mack
2113cdbbeebSDaniel Mack return count;
2123cdbbeebSDaniel Mack }
2133cdbbeebSDaniel Mack
2143cdbbeebSDaniel Mack static DEVICE_ATTR(range, S_IWUSR | S_IRUGO,
2153cdbbeebSDaniel Mack isl29003_show_range, isl29003_store_range);
2163cdbbeebSDaniel Mack
2173cdbbeebSDaniel Mack
2183cdbbeebSDaniel Mack /* resolution */
isl29003_show_resolution(struct device * dev,struct device_attribute * attr,char * buf)2193cdbbeebSDaniel Mack static ssize_t isl29003_show_resolution(struct device *dev,
2203cdbbeebSDaniel Mack struct device_attribute *attr,
2213cdbbeebSDaniel Mack char *buf)
2223cdbbeebSDaniel Mack {
2233cdbbeebSDaniel Mack struct i2c_client *client = to_i2c_client(dev);
22405eec0c9SDhaval Shah
225cc3db79bSBo Liu return sysfs_emit(buf, "%d\n", isl29003_get_resolution(client));
2263cdbbeebSDaniel Mack }
2273cdbbeebSDaniel Mack
isl29003_store_resolution(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)2283cdbbeebSDaniel Mack static ssize_t isl29003_store_resolution(struct device *dev,
2293cdbbeebSDaniel Mack struct device_attribute *attr,
2303cdbbeebSDaniel Mack const char *buf, size_t count)
2313cdbbeebSDaniel Mack {
2323cdbbeebSDaniel Mack struct i2c_client *client = to_i2c_client(dev);
2333cdbbeebSDaniel Mack unsigned long val;
2343cdbbeebSDaniel Mack int ret;
2353cdbbeebSDaniel Mack
236f7b41276SJingoo Han ret = kstrtoul(buf, 10, &val);
237f7b41276SJingoo Han if (ret)
238f7b41276SJingoo Han return ret;
239f7b41276SJingoo Han
240f7b41276SJingoo Han if (val > 3)
2413cdbbeebSDaniel Mack return -EINVAL;
2423cdbbeebSDaniel Mack
2433cdbbeebSDaniel Mack ret = isl29003_set_resolution(client, val);
2443cdbbeebSDaniel Mack if (ret < 0)
2453cdbbeebSDaniel Mack return ret;
2463cdbbeebSDaniel Mack
2473cdbbeebSDaniel Mack return count;
2483cdbbeebSDaniel Mack }
2493cdbbeebSDaniel Mack
2503cdbbeebSDaniel Mack static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO,
2513cdbbeebSDaniel Mack isl29003_show_resolution, isl29003_store_resolution);
2523cdbbeebSDaniel Mack
2533cdbbeebSDaniel Mack /* mode */
isl29003_show_mode(struct device * dev,struct device_attribute * attr,char * buf)2543cdbbeebSDaniel Mack static ssize_t isl29003_show_mode(struct device *dev,
2553cdbbeebSDaniel Mack struct device_attribute *attr, char *buf)
2563cdbbeebSDaniel Mack {
2573cdbbeebSDaniel Mack struct i2c_client *client = to_i2c_client(dev);
25805eec0c9SDhaval Shah
259cc3db79bSBo Liu return sysfs_emit(buf, "%d\n", isl29003_get_mode(client));
2603cdbbeebSDaniel Mack }
2613cdbbeebSDaniel Mack
isl29003_store_mode(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)2623cdbbeebSDaniel Mack static ssize_t isl29003_store_mode(struct device *dev,
2633cdbbeebSDaniel Mack struct device_attribute *attr, const char *buf, size_t count)
2643cdbbeebSDaniel Mack {
2653cdbbeebSDaniel Mack struct i2c_client *client = to_i2c_client(dev);
2663cdbbeebSDaniel Mack unsigned long val;
2673cdbbeebSDaniel Mack int ret;
2683cdbbeebSDaniel Mack
269f7b41276SJingoo Han ret = kstrtoul(buf, 10, &val);
270f7b41276SJingoo Han if (ret)
271f7b41276SJingoo Han return ret;
272f7b41276SJingoo Han
273f7b41276SJingoo Han if (val > 2)
2743cdbbeebSDaniel Mack return -EINVAL;
2753cdbbeebSDaniel Mack
2763cdbbeebSDaniel Mack ret = isl29003_set_mode(client, val);
2773cdbbeebSDaniel Mack if (ret < 0)
2783cdbbeebSDaniel Mack return ret;
2793cdbbeebSDaniel Mack
2803cdbbeebSDaniel Mack return count;
2813cdbbeebSDaniel Mack }
2823cdbbeebSDaniel Mack
2833cdbbeebSDaniel Mack static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
2843cdbbeebSDaniel Mack isl29003_show_mode, isl29003_store_mode);
2853cdbbeebSDaniel Mack
2863cdbbeebSDaniel Mack
2873cdbbeebSDaniel Mack /* power state */
isl29003_show_power_state(struct device * dev,struct device_attribute * attr,char * buf)2883cdbbeebSDaniel Mack static ssize_t isl29003_show_power_state(struct device *dev,
2893cdbbeebSDaniel Mack struct device_attribute *attr,
2903cdbbeebSDaniel Mack char *buf)
2913cdbbeebSDaniel Mack {
2923cdbbeebSDaniel Mack struct i2c_client *client = to_i2c_client(dev);
29305eec0c9SDhaval Shah
294cc3db79bSBo Liu return sysfs_emit(buf, "%d\n", isl29003_get_power_state(client));
2953cdbbeebSDaniel Mack }
2963cdbbeebSDaniel Mack
isl29003_store_power_state(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)2973cdbbeebSDaniel Mack static ssize_t isl29003_store_power_state(struct device *dev,
2983cdbbeebSDaniel Mack struct device_attribute *attr,
2993cdbbeebSDaniel Mack const char *buf, size_t count)
3003cdbbeebSDaniel Mack {
3013cdbbeebSDaniel Mack struct i2c_client *client = to_i2c_client(dev);
3023cdbbeebSDaniel Mack unsigned long val;
3033cdbbeebSDaniel Mack int ret;
3043cdbbeebSDaniel Mack
305f7b41276SJingoo Han ret = kstrtoul(buf, 10, &val);
306f7b41276SJingoo Han if (ret)
307f7b41276SJingoo Han return ret;
308f7b41276SJingoo Han
309f7b41276SJingoo Han if (val > 1)
3103cdbbeebSDaniel Mack return -EINVAL;
3113cdbbeebSDaniel Mack
3123cdbbeebSDaniel Mack ret = isl29003_set_power_state(client, val);
3133cdbbeebSDaniel Mack return ret ? ret : count;
3143cdbbeebSDaniel Mack }
3153cdbbeebSDaniel Mack
3163cdbbeebSDaniel Mack static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
3173cdbbeebSDaniel Mack isl29003_show_power_state, isl29003_store_power_state);
3183cdbbeebSDaniel Mack
3193cdbbeebSDaniel Mack
3203cdbbeebSDaniel Mack /* lux */
isl29003_show_lux(struct device * dev,struct device_attribute * attr,char * buf)3213cdbbeebSDaniel Mack static ssize_t isl29003_show_lux(struct device *dev,
3223cdbbeebSDaniel Mack struct device_attribute *attr, char *buf)
3233cdbbeebSDaniel Mack {
3243cdbbeebSDaniel Mack struct i2c_client *client = to_i2c_client(dev);
3253cdbbeebSDaniel Mack
3263cdbbeebSDaniel Mack /* No LUX data if not operational */
3273cdbbeebSDaniel Mack if (!isl29003_get_power_state(client))
3283cdbbeebSDaniel Mack return -EBUSY;
3293cdbbeebSDaniel Mack
330cc3db79bSBo Liu return sysfs_emit(buf, "%d\n", isl29003_get_adc_value(client));
3313cdbbeebSDaniel Mack }
3323cdbbeebSDaniel Mack
3333cdbbeebSDaniel Mack static DEVICE_ATTR(lux, S_IRUGO, isl29003_show_lux, NULL);
3343cdbbeebSDaniel Mack
3353cdbbeebSDaniel Mack static struct attribute *isl29003_attributes[] = {
3363cdbbeebSDaniel Mack &dev_attr_range.attr,
3373cdbbeebSDaniel Mack &dev_attr_resolution.attr,
3383cdbbeebSDaniel Mack &dev_attr_mode.attr,
3393cdbbeebSDaniel Mack &dev_attr_power_state.attr,
3403cdbbeebSDaniel Mack &dev_attr_lux.attr,
3413cdbbeebSDaniel Mack NULL
3423cdbbeebSDaniel Mack };
3433cdbbeebSDaniel Mack
3443cdbbeebSDaniel Mack static const struct attribute_group isl29003_attr_group = {
3453cdbbeebSDaniel Mack .attrs = isl29003_attributes,
3463cdbbeebSDaniel Mack };
3473cdbbeebSDaniel Mack
isl29003_init_client(struct i2c_client * client)3483cdbbeebSDaniel Mack static int isl29003_init_client(struct i2c_client *client)
3493cdbbeebSDaniel Mack {
3503cdbbeebSDaniel Mack struct isl29003_data *data = i2c_get_clientdata(client);
3513cdbbeebSDaniel Mack int i;
3523cdbbeebSDaniel Mack
3533cdbbeebSDaniel Mack /* read all the registers once to fill the cache.
3543cdbbeebSDaniel Mack * if one of the reads fails, we consider the init failed */
3553cdbbeebSDaniel Mack for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) {
3563cdbbeebSDaniel Mack int v = i2c_smbus_read_byte_data(client, i);
35705eec0c9SDhaval Shah
3583cdbbeebSDaniel Mack if (v < 0)
3593cdbbeebSDaniel Mack return -ENODEV;
3603cdbbeebSDaniel Mack
3613cdbbeebSDaniel Mack data->reg_cache[i] = v;
3623cdbbeebSDaniel Mack }
3633cdbbeebSDaniel Mack
3643cdbbeebSDaniel Mack /* set defaults */
3653cdbbeebSDaniel Mack isl29003_set_range(client, 0);
3663cdbbeebSDaniel Mack isl29003_set_resolution(client, 0);
3673cdbbeebSDaniel Mack isl29003_set_mode(client, 0);
3683cdbbeebSDaniel Mack isl29003_set_power_state(client, 0);
3693cdbbeebSDaniel Mack
3703cdbbeebSDaniel Mack return 0;
3713cdbbeebSDaniel Mack }
3723cdbbeebSDaniel Mack
3733cdbbeebSDaniel Mack /*
3743cdbbeebSDaniel Mack * I2C layer
3753cdbbeebSDaniel Mack */
3763cdbbeebSDaniel Mack
isl29003_probe(struct i2c_client * client)377db687ce7SUwe Kleine-König static int isl29003_probe(struct i2c_client *client)
3783cdbbeebSDaniel Mack {
3791a0911a0SWolfram Sang struct i2c_adapter *adapter = client->adapter;
3803cdbbeebSDaniel Mack struct isl29003_data *data;
3813cdbbeebSDaniel Mack int err = 0;
3823cdbbeebSDaniel Mack
3833cdbbeebSDaniel Mack if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
3843cdbbeebSDaniel Mack return -EIO;
3853cdbbeebSDaniel Mack
3863cdbbeebSDaniel Mack data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL);
3873cdbbeebSDaniel Mack if (!data)
3883cdbbeebSDaniel Mack return -ENOMEM;
3893cdbbeebSDaniel Mack
3903cdbbeebSDaniel Mack data->client = client;
3913cdbbeebSDaniel Mack i2c_set_clientdata(client, data);
3923cdbbeebSDaniel Mack mutex_init(&data->lock);
3933cdbbeebSDaniel Mack
3943cdbbeebSDaniel Mack /* initialize the ISL29003 chip */
3953cdbbeebSDaniel Mack err = isl29003_init_client(client);
3963cdbbeebSDaniel Mack if (err)
3973cdbbeebSDaniel Mack goto exit_kfree;
3983cdbbeebSDaniel Mack
3993cdbbeebSDaniel Mack /* register sysfs hooks */
4003cdbbeebSDaniel Mack err = sysfs_create_group(&client->dev.kobj, &isl29003_attr_group);
4013cdbbeebSDaniel Mack if (err)
4023cdbbeebSDaniel Mack goto exit_kfree;
4033cdbbeebSDaniel Mack
4043cdbbeebSDaniel Mack dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION);
4053cdbbeebSDaniel Mack return 0;
4063cdbbeebSDaniel Mack
4073cdbbeebSDaniel Mack exit_kfree:
4083cdbbeebSDaniel Mack kfree(data);
4093cdbbeebSDaniel Mack return err;
4103cdbbeebSDaniel Mack }
4113cdbbeebSDaniel Mack
isl29003_remove(struct i2c_client * client)412ed5c2f5fSUwe Kleine-König static void isl29003_remove(struct i2c_client *client)
4133cdbbeebSDaniel Mack {
4143cdbbeebSDaniel Mack sysfs_remove_group(&client->dev.kobj, &isl29003_attr_group);
4153cdbbeebSDaniel Mack isl29003_set_power_state(client, 0);
4163cdbbeebSDaniel Mack kfree(i2c_get_clientdata(client));
4173cdbbeebSDaniel Mack }
4183cdbbeebSDaniel Mack
419873bf4f4SLars-Peter Clausen #ifdef CONFIG_PM_SLEEP
isl29003_suspend(struct device * dev)420873bf4f4SLars-Peter Clausen static int isl29003_suspend(struct device *dev)
4213cdbbeebSDaniel Mack {
422873bf4f4SLars-Peter Clausen struct i2c_client *client = to_i2c_client(dev);
42374614f8dSDaniel Mack struct isl29003_data *data = i2c_get_clientdata(client);
42474614f8dSDaniel Mack
42574614f8dSDaniel Mack data->power_state_before_suspend = isl29003_get_power_state(client);
4263cdbbeebSDaniel Mack return isl29003_set_power_state(client, 0);
4273cdbbeebSDaniel Mack }
4283cdbbeebSDaniel Mack
isl29003_resume(struct device * dev)429873bf4f4SLars-Peter Clausen static int isl29003_resume(struct device *dev)
4303cdbbeebSDaniel Mack {
4313cdbbeebSDaniel Mack int i;
432873bf4f4SLars-Peter Clausen struct i2c_client *client = to_i2c_client(dev);
4333cdbbeebSDaniel Mack struct isl29003_data *data = i2c_get_clientdata(client);
4343cdbbeebSDaniel Mack
4353cdbbeebSDaniel Mack /* restore registers from cache */
4363cdbbeebSDaniel Mack for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++)
43774614f8dSDaniel Mack if (i2c_smbus_write_byte_data(client, i, data->reg_cache[i]))
4383cdbbeebSDaniel Mack return -EIO;
4393cdbbeebSDaniel Mack
44074614f8dSDaniel Mack return isl29003_set_power_state(client,
44174614f8dSDaniel Mack data->power_state_before_suspend);
4423cdbbeebSDaniel Mack }
4433cdbbeebSDaniel Mack
444873bf4f4SLars-Peter Clausen static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume);
445873bf4f4SLars-Peter Clausen #define ISL29003_PM_OPS (&isl29003_pm_ops)
446873bf4f4SLars-Peter Clausen
4473cdbbeebSDaniel Mack #else
448873bf4f4SLars-Peter Clausen #define ISL29003_PM_OPS NULL
449873bf4f4SLars-Peter Clausen #endif /* CONFIG_PM_SLEEP */
4503cdbbeebSDaniel Mack
4513cdbbeebSDaniel Mack static const struct i2c_device_id isl29003_id[] = {
4523cdbbeebSDaniel Mack { "isl29003", 0 },
4533cdbbeebSDaniel Mack {}
4543cdbbeebSDaniel Mack };
4553cdbbeebSDaniel Mack MODULE_DEVICE_TABLE(i2c, isl29003_id);
4563cdbbeebSDaniel Mack
4573cdbbeebSDaniel Mack static struct i2c_driver isl29003_driver = {
4583cdbbeebSDaniel Mack .driver = {
4593cdbbeebSDaniel Mack .name = ISL29003_DRV_NAME,
460873bf4f4SLars-Peter Clausen .pm = ISL29003_PM_OPS,
4613cdbbeebSDaniel Mack },
462*f050bb8fSUwe Kleine-König .probe = isl29003_probe,
4632d6bed9cSBill Pemberton .remove = isl29003_remove,
4643cdbbeebSDaniel Mack .id_table = isl29003_id,
4653cdbbeebSDaniel Mack };
4663cdbbeebSDaniel Mack
467a64fe2edSAxel Lin module_i2c_driver(isl29003_driver);
4683cdbbeebSDaniel Mack
4693cdbbeebSDaniel Mack MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
4703cdbbeebSDaniel Mack MODULE_DESCRIPTION("ISL29003 ambient light sensor driver");
4713cdbbeebSDaniel Mack MODULE_LICENSE("GPL v2");
4723cdbbeebSDaniel Mack MODULE_VERSION(DRIVER_VERSION);
473