aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorDaniel Baluta <daniel.baluta@intel.com>2014-10-09 02:57:00 -0400
committerJonathan Cameron <jic23@kernel.org>2014-09-10 15:25:56 -0400
commitf007d7f1431ca9bc184782e704a7da460385d335 (patch)
tree6ce851c2317d5828bb80be7813bef11eceda53b9 /drivers/iio
parentaf14afd2abef9e01761ec6eb79dd7c0833a243f0 (diff)
iio: Add Dyna-Image AL3320A ambient light sensor driver
Minimal implementation. This driver provides raw illuminance readings. This is based on drivers/hwmon/al3320.c (*) driver from msm tree written by Tsechih Lin <Tsechih_Lin@asus.com> * https://android.googlesource.com/kernel/msm.git Signed-off-by: Daniel Baluta <daniel.baluta@intel.com> Reviewed-by: Hartmut Knaack <knaack.h@gmx.de> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/light/Kconfig10
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/al3320a.c232
3 files changed, 243 insertions, 0 deletions
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index bf05ca5b0a57..5bea821adcae 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -17,6 +17,16 @@ config ADJD_S311
17 This driver can also be built as a module. If so, the module 17 This driver can also be built as a module. If so, the module
18 will be called adjd_s311. 18 will be called adjd_s311.
19 19
20config AL3320A
21 tristate "AL3320A ambient light sensor"
22 depends on I2C
23 help
24 Say Y here if you want to build a driver for the Dyna Image AL3320A
25 ambient light sensor.
26
27 To compile this driver as a module, choose M here: the
28 module will be called al3320a.
29
20config APDS9300 30config APDS9300
21 tristate "APDS9300 ambient light sensor" 31 tristate "APDS9300 ambient light sensor"
22 depends on I2C 32 depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 8b8c09f9c1f8..47877a36cc12 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -4,6 +4,7 @@
4 4
5# When adding new entries keep the list in alphabetical order 5# When adding new entries keep the list in alphabetical order
6obj-$(CONFIG_ADJD_S311) += adjd_s311.o 6obj-$(CONFIG_ADJD_S311) += adjd_s311.o
7obj-$(CONFIG_AL3320A) += al3320a.o
7obj-$(CONFIG_APDS9300) += apds9300.o 8obj-$(CONFIG_APDS9300) += apds9300.o
8obj-$(CONFIG_CM32181) += cm32181.o 9obj-$(CONFIG_CM32181) += cm32181.o
9obj-$(CONFIG_CM36651) += cm36651.o 10obj-$(CONFIG_CM36651) += cm36651.o
diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c
new file mode 100644
index 000000000000..6aac6513fd41
--- /dev/null
+++ b/drivers/iio/light/al3320a.c
@@ -0,0 +1,232 @@
1/*
2 * AL3320A - Dyna Image Ambient Light Sensor
3 *
4 * Copyright (c) 2014, Intel Corporation.
5 *
6 * This file is subject to the terms and conditions of version 2 of
7 * the GNU General Public License. See the file COPYING in the main
8 * directory of this archive for more details.
9 *
10 * IIO driver for AL3320A (7-bit I2C slave address 0x1C).
11 *
12 * TODO: interrupt support, thresholds
13 *
14 */
15
16#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/i2c.h>
19
20#include <linux/iio/iio.h>
21#include <linux/iio/sysfs.h>
22
23#define AL3320A_DRV_NAME "al3320a"
24
25#define AL3320A_REG_CONFIG 0x00
26#define AL3320A_REG_STATUS 0x01
27#define AL3320A_REG_INT 0x02
28#define AL3320A_REG_WAIT 0x06
29#define AL3320A_REG_CONFIG_RANGE 0x07
30#define AL3320A_REG_PERSIST 0x08
31#define AL3320A_REG_MEAN_TIME 0x09
32#define AL3320A_REG_ADUMMY 0x0A
33#define AL3320A_REG_DATA_LOW 0x22
34
35#define AL3320A_REG_LOW_THRESH_LOW 0x30
36#define AL3320A_REG_LOW_THRESH_HIGH 0x31
37#define AL3320A_REG_HIGH_THRESH_LOW 0x32
38#define AL3320A_REG_HIGH_THRESH_HIGH 0x33
39
40#define AL3320A_CONFIG_DISABLE 0x00
41#define AL3320A_CONFIG_ENABLE 0x01
42
43#define AL3320A_GAIN_SHIFT 1
44#define AL3320A_GAIN_MASK (BIT(2) | BIT(1))
45
46/* chip params default values */
47#define AL3320A_DEFAULT_MEAN_TIME 4
48#define AL3320A_DEFAULT_WAIT_TIME 0 /* no waiting */
49
50#define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01"
51
52enum al3320a_range {
53 AL3320A_RANGE_1, /* 33.28 Klx */
54 AL3320A_RANGE_2, /* 8.32 Klx */
55 AL3320A_RANGE_3, /* 2.08 Klx */
56 AL3320A_RANGE_4 /* 0.65 Klx */
57};
58
59static const int al3320a_scales[][2] = {
60 {0, 512000}, {0, 128000}, {0, 32000}, {0, 10000}
61};
62
63struct al3320a_data {
64 struct i2c_client *client;
65};
66
67static const struct iio_chan_spec al3320a_channels[] = {
68 {
69 .type = IIO_LIGHT,
70 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
71 BIT(IIO_CHAN_INFO_SCALE),
72 }
73};
74
75static IIO_CONST_ATTR(in_illuminance_scale_available, AL3320A_SCALE_AVAILABLE);
76
77static struct attribute *al3320a_attributes[] = {
78 &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
79 NULL,
80};
81
82static const struct attribute_group al3320a_attribute_group = {
83 .attrs = al3320a_attributes,
84};
85
86static int al3320a_init(struct al3320a_data *data)
87{
88 int ret;
89
90 /* power on */
91 ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG,
92 AL3320A_CONFIG_ENABLE);
93 if (ret < 0)
94 return ret;
95
96 ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
97 AL3320A_RANGE_3 << AL3320A_GAIN_SHIFT);
98 if (ret < 0)
99 return ret;
100
101 ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME,
102 AL3320A_DEFAULT_MEAN_TIME);
103 if (ret < 0)
104 return ret;
105
106 ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT,
107 AL3320A_DEFAULT_WAIT_TIME);
108 if (ret < 0)
109 return ret;
110
111 return 0;
112}
113
114static int al3320a_read_raw(struct iio_dev *indio_dev,
115 struct iio_chan_spec const *chan, int *val,
116 int *val2, long mask)
117{
118 struct al3320a_data *data = iio_priv(indio_dev);
119 int ret;
120
121 switch (mask) {
122 case IIO_CHAN_INFO_RAW:
123 /*
124 * ALS ADC value is stored in two adjacent registers:
125 * - low byte of output is stored at AL3320A_REG_DATA_LOW
126 * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
127 */
128 ret = i2c_smbus_read_word_data(data->client,
129 AL3320A_REG_DATA_LOW);
130 if (ret < 0)
131 return ret;
132 *val = ret;
133 return IIO_VAL_INT;
134 case IIO_CHAN_INFO_SCALE:
135 ret = i2c_smbus_read_byte_data(data->client,
136 AL3320A_REG_CONFIG_RANGE);
137 if (ret < 0)
138 return ret;
139
140 ret = (ret & AL3320A_GAIN_MASK) >> AL3320A_GAIN_SHIFT;
141 *val = al3320a_scales[ret][0];
142 *val2 = al3320a_scales[ret][1];
143
144 return IIO_VAL_INT_PLUS_MICRO;
145 }
146 return -EINVAL;
147}
148
149static int al3320a_write_raw(struct iio_dev *indio_dev,
150 struct iio_chan_spec const *chan, int val,
151 int val2, long mask)
152{
153 struct al3320a_data *data = iio_priv(indio_dev);
154 int i;
155
156 switch (mask) {
157 case IIO_CHAN_INFO_SCALE:
158 for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
159 if (val == al3320a_scales[i][0] &&
160 val2 == al3320a_scales[i][1])
161 return i2c_smbus_write_byte_data(data->client,
162 AL3320A_REG_CONFIG_RANGE,
163 i << AL3320A_GAIN_SHIFT);
164 }
165 break;
166 }
167 return -EINVAL;
168}
169
170static const struct iio_info al3320a_info = {
171 .driver_module = THIS_MODULE,
172 .read_raw = al3320a_read_raw,
173 .write_raw = al3320a_write_raw,
174 .attrs = &al3320a_attribute_group,
175};
176
177static int al3320a_probe(struct i2c_client *client,
178 const struct i2c_device_id *id)
179{
180 struct al3320a_data *data;
181 struct iio_dev *indio_dev;
182 int ret;
183
184 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
185 if (!indio_dev)
186 return -ENOMEM;
187
188 data = iio_priv(indio_dev);
189 i2c_set_clientdata(client, indio_dev);
190 data->client = client;
191
192 indio_dev->dev.parent = &client->dev;
193 indio_dev->info = &al3320a_info;
194 indio_dev->name = AL3320A_DRV_NAME;
195 indio_dev->channels = al3320a_channels;
196 indio_dev->num_channels = ARRAY_SIZE(al3320a_channels);
197 indio_dev->modes = INDIO_DIRECT_MODE;
198
199 ret = al3320a_init(data);
200 if (ret < 0) {
201 dev_err(&client->dev, "al3320a chip init failed\n");
202 return ret;
203 }
204 return devm_iio_device_register(&client->dev, indio_dev);
205}
206
207static int al3320a_remove(struct i2c_client *client)
208{
209 return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG,
210 AL3320A_CONFIG_DISABLE);
211}
212
213static const struct i2c_device_id al3320a_id[] = {
214 {"al3320a", 0},
215 {}
216};
217MODULE_DEVICE_TABLE(i2c, al3320a_id);
218
219static struct i2c_driver al3320a_driver = {
220 .driver = {
221 .name = AL3320A_DRV_NAME,
222 },
223 .probe = al3320a_probe,
224 .remove = al3320a_remove,
225 .id_table = al3320a_id,
226};
227
228module_i2c_driver(al3320a_driver);
229
230MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
231MODULE_DESCRIPTION("AL3320A Ambient Light Sensor driver");
232MODULE_LICENSE("GPL v2");