xref: /OK3568_Linux_fs/kernel/drivers/iio/light/al3320a.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * AL3320A - Dyna Image Ambient Light Sensor
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2014, Intel Corporation.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * IIO driver for AL3320A (7-bit I2C slave address 0x1C).
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * TODO: interrupt support, thresholds
10*4882a593Smuzhiyun  * When the driver will get support for interrupt handling, then interrupt
11*4882a593Smuzhiyun  * will need to be disabled before turning sensor OFF in order to avoid
12*4882a593Smuzhiyun  * potential races with the interrupt handling.
13*4882a593Smuzhiyun  */
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <linux/bitfield.h>
16*4882a593Smuzhiyun #include <linux/i2c.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/of.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include <linux/iio/iio.h>
21*4882a593Smuzhiyun #include <linux/iio/sysfs.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #define AL3320A_DRV_NAME "al3320a"
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #define AL3320A_REG_CONFIG		0x00
26*4882a593Smuzhiyun #define AL3320A_REG_STATUS		0x01
27*4882a593Smuzhiyun #define AL3320A_REG_INT			0x02
28*4882a593Smuzhiyun #define AL3320A_REG_WAIT		0x06
29*4882a593Smuzhiyun #define AL3320A_REG_CONFIG_RANGE	0x07
30*4882a593Smuzhiyun #define AL3320A_REG_PERSIST		0x08
31*4882a593Smuzhiyun #define AL3320A_REG_MEAN_TIME		0x09
32*4882a593Smuzhiyun #define AL3320A_REG_ADUMMY		0x0A
33*4882a593Smuzhiyun #define AL3320A_REG_DATA_LOW		0x22
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #define AL3320A_REG_LOW_THRESH_LOW	0x30
36*4882a593Smuzhiyun #define AL3320A_REG_LOW_THRESH_HIGH	0x31
37*4882a593Smuzhiyun #define AL3320A_REG_HIGH_THRESH_LOW	0x32
38*4882a593Smuzhiyun #define AL3320A_REG_HIGH_THRESH_HIGH	0x33
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun #define AL3320A_CONFIG_DISABLE		0x00
41*4882a593Smuzhiyun #define AL3320A_CONFIG_ENABLE		0x01
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #define AL3320A_GAIN_MASK		GENMASK(2, 1)
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun /* chip params default values */
46*4882a593Smuzhiyun #define AL3320A_DEFAULT_MEAN_TIME	4
47*4882a593Smuzhiyun #define AL3320A_DEFAULT_WAIT_TIME	0 /* no waiting */
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun #define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01"
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun enum al3320a_range {
52*4882a593Smuzhiyun 	AL3320A_RANGE_1, /* 33.28 Klx */
53*4882a593Smuzhiyun 	AL3320A_RANGE_2, /* 8.32 Klx  */
54*4882a593Smuzhiyun 	AL3320A_RANGE_3, /* 2.08 Klx  */
55*4882a593Smuzhiyun 	AL3320A_RANGE_4  /* 0.65 Klx  */
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun static const int al3320a_scales[][2] = {
59*4882a593Smuzhiyun 	{0, 512000}, {0, 128000}, {0, 32000}, {0, 10000}
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun struct al3320a_data {
63*4882a593Smuzhiyun 	struct i2c_client *client;
64*4882a593Smuzhiyun };
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun static const struct iio_chan_spec al3320a_channels[] = {
67*4882a593Smuzhiyun 	{
68*4882a593Smuzhiyun 		.type	= IIO_LIGHT,
69*4882a593Smuzhiyun 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
70*4882a593Smuzhiyun 				      BIT(IIO_CHAN_INFO_SCALE),
71*4882a593Smuzhiyun 	}
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun static IIO_CONST_ATTR(in_illuminance_scale_available, AL3320A_SCALE_AVAILABLE);
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun static struct attribute *al3320a_attributes[] = {
77*4882a593Smuzhiyun 	&iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
78*4882a593Smuzhiyun 	NULL,
79*4882a593Smuzhiyun };
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun static const struct attribute_group al3320a_attribute_group = {
82*4882a593Smuzhiyun 	.attrs = al3320a_attributes,
83*4882a593Smuzhiyun };
84*4882a593Smuzhiyun 
al3320a_set_pwr(struct i2c_client * client,bool pwr)85*4882a593Smuzhiyun static int al3320a_set_pwr(struct i2c_client *client, bool pwr)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun 	u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE;
88*4882a593Smuzhiyun 	return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
al3320a_set_pwr_off(void * _data)91*4882a593Smuzhiyun static void al3320a_set_pwr_off(void *_data)
92*4882a593Smuzhiyun {
93*4882a593Smuzhiyun 	struct al3320a_data *data = _data;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	al3320a_set_pwr(data->client, false);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun 
al3320a_init(struct al3320a_data * data)98*4882a593Smuzhiyun static int al3320a_init(struct al3320a_data *data)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun 	int ret;
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	ret = al3320a_set_pwr(data->client, true);
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	if (ret < 0)
105*4882a593Smuzhiyun 		return ret;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
108*4882a593Smuzhiyun 					FIELD_PREP(AL3320A_GAIN_MASK,
109*4882a593Smuzhiyun 						   AL3320A_RANGE_3));
110*4882a593Smuzhiyun 	if (ret < 0)
111*4882a593Smuzhiyun 		return ret;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME,
114*4882a593Smuzhiyun 					AL3320A_DEFAULT_MEAN_TIME);
115*4882a593Smuzhiyun 	if (ret < 0)
116*4882a593Smuzhiyun 		return ret;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT,
119*4882a593Smuzhiyun 					AL3320A_DEFAULT_WAIT_TIME);
120*4882a593Smuzhiyun 	if (ret < 0)
121*4882a593Smuzhiyun 		return ret;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	return 0;
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun 
al3320a_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)126*4882a593Smuzhiyun static int al3320a_read_raw(struct iio_dev *indio_dev,
127*4882a593Smuzhiyun 			    struct iio_chan_spec const *chan, int *val,
128*4882a593Smuzhiyun 			    int *val2, long mask)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun 	struct al3320a_data *data = iio_priv(indio_dev);
131*4882a593Smuzhiyun 	int ret;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	switch (mask) {
134*4882a593Smuzhiyun 	case IIO_CHAN_INFO_RAW:
135*4882a593Smuzhiyun 		/*
136*4882a593Smuzhiyun 		 * ALS ADC value is stored in two adjacent registers:
137*4882a593Smuzhiyun 		 * - low byte of output is stored at AL3320A_REG_DATA_LOW
138*4882a593Smuzhiyun 		 * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
139*4882a593Smuzhiyun 		 */
140*4882a593Smuzhiyun 		ret = i2c_smbus_read_word_data(data->client,
141*4882a593Smuzhiyun 					       AL3320A_REG_DATA_LOW);
142*4882a593Smuzhiyun 		if (ret < 0)
143*4882a593Smuzhiyun 			return ret;
144*4882a593Smuzhiyun 		*val = ret;
145*4882a593Smuzhiyun 		return IIO_VAL_INT;
146*4882a593Smuzhiyun 	case IIO_CHAN_INFO_SCALE:
147*4882a593Smuzhiyun 		ret = i2c_smbus_read_byte_data(data->client,
148*4882a593Smuzhiyun 					       AL3320A_REG_CONFIG_RANGE);
149*4882a593Smuzhiyun 		if (ret < 0)
150*4882a593Smuzhiyun 			return ret;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 		ret = FIELD_GET(AL3320A_GAIN_MASK, ret);
153*4882a593Smuzhiyun 		*val = al3320a_scales[ret][0];
154*4882a593Smuzhiyun 		*val2 = al3320a_scales[ret][1];
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 		return IIO_VAL_INT_PLUS_MICRO;
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun 	return -EINVAL;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun 
al3320a_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)161*4882a593Smuzhiyun static int al3320a_write_raw(struct iio_dev *indio_dev,
162*4882a593Smuzhiyun 			     struct iio_chan_spec const *chan, int val,
163*4882a593Smuzhiyun 			     int val2, long mask)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun 	struct al3320a_data *data = iio_priv(indio_dev);
166*4882a593Smuzhiyun 	int i;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	switch (mask) {
169*4882a593Smuzhiyun 	case IIO_CHAN_INFO_SCALE:
170*4882a593Smuzhiyun 		for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
171*4882a593Smuzhiyun 			if (val != al3320a_scales[i][0] ||
172*4882a593Smuzhiyun 			    val2 != al3320a_scales[i][1])
173*4882a593Smuzhiyun 				continue;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 			return i2c_smbus_write_byte_data(data->client,
176*4882a593Smuzhiyun 					AL3320A_REG_CONFIG_RANGE,
177*4882a593Smuzhiyun 					FIELD_PREP(AL3320A_GAIN_MASK, i));
178*4882a593Smuzhiyun 		}
179*4882a593Smuzhiyun 		break;
180*4882a593Smuzhiyun 	}
181*4882a593Smuzhiyun 	return -EINVAL;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun static const struct iio_info al3320a_info = {
185*4882a593Smuzhiyun 	.read_raw	= al3320a_read_raw,
186*4882a593Smuzhiyun 	.write_raw	= al3320a_write_raw,
187*4882a593Smuzhiyun 	.attrs		= &al3320a_attribute_group,
188*4882a593Smuzhiyun };
189*4882a593Smuzhiyun 
al3320a_probe(struct i2c_client * client,const struct i2c_device_id * id)190*4882a593Smuzhiyun static int al3320a_probe(struct i2c_client *client,
191*4882a593Smuzhiyun 			 const struct i2c_device_id *id)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun 	struct al3320a_data *data;
194*4882a593Smuzhiyun 	struct iio_dev *indio_dev;
195*4882a593Smuzhiyun 	int ret;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
198*4882a593Smuzhiyun 	if (!indio_dev)
199*4882a593Smuzhiyun 		return -ENOMEM;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	data = iio_priv(indio_dev);
202*4882a593Smuzhiyun 	i2c_set_clientdata(client, indio_dev);
203*4882a593Smuzhiyun 	data->client = client;
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	indio_dev->info = &al3320a_info;
206*4882a593Smuzhiyun 	indio_dev->name = AL3320A_DRV_NAME;
207*4882a593Smuzhiyun 	indio_dev->channels = al3320a_channels;
208*4882a593Smuzhiyun 	indio_dev->num_channels = ARRAY_SIZE(al3320a_channels);
209*4882a593Smuzhiyun 	indio_dev->modes = INDIO_DIRECT_MODE;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	ret = al3320a_init(data);
212*4882a593Smuzhiyun 	if (ret < 0) {
213*4882a593Smuzhiyun 		dev_err(&client->dev, "al3320a chip init failed\n");
214*4882a593Smuzhiyun 		return ret;
215*4882a593Smuzhiyun 	}
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	ret = devm_add_action_or_reset(&client->dev,
218*4882a593Smuzhiyun 					al3320a_set_pwr_off,
219*4882a593Smuzhiyun 					data);
220*4882a593Smuzhiyun 	if (ret < 0)
221*4882a593Smuzhiyun 		return ret;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	return devm_iio_device_register(&client->dev, indio_dev);
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun 
al3320a_suspend(struct device * dev)226*4882a593Smuzhiyun static int __maybe_unused al3320a_suspend(struct device *dev)
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun 	return al3320a_set_pwr(to_i2c_client(dev), false);
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun 
al3320a_resume(struct device * dev)231*4882a593Smuzhiyun static int __maybe_unused al3320a_resume(struct device *dev)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun 	return al3320a_set_pwr(to_i2c_client(dev), true);
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, al3320a_resume);
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun static const struct i2c_device_id al3320a_id[] = {
239*4882a593Smuzhiyun 	{"al3320a", 0},
240*4882a593Smuzhiyun 	{}
241*4882a593Smuzhiyun };
242*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, al3320a_id);
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun static const struct of_device_id al3320a_of_match[] = {
245*4882a593Smuzhiyun 	{ .compatible = "dynaimage,al3320a", },
246*4882a593Smuzhiyun 	{},
247*4882a593Smuzhiyun };
248*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, al3320a_of_match);
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun static struct i2c_driver al3320a_driver = {
251*4882a593Smuzhiyun 	.driver = {
252*4882a593Smuzhiyun 		.name = AL3320A_DRV_NAME,
253*4882a593Smuzhiyun 		.of_match_table = al3320a_of_match,
254*4882a593Smuzhiyun 		.pm = &al3320a_pm_ops,
255*4882a593Smuzhiyun 	},
256*4882a593Smuzhiyun 	.probe		= al3320a_probe,
257*4882a593Smuzhiyun 	.id_table	= al3320a_id,
258*4882a593Smuzhiyun };
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun module_i2c_driver(al3320a_driver);
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
263*4882a593Smuzhiyun MODULE_DESCRIPTION("AL3320A Ambient Light Sensor driver");
264*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
265