xref: /openbmc/linux/drivers/misc/isl29003.c (revision f050bb8f)
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