aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authoranantha <anantha.narayanan@intel.com>2010-10-26 17:22:41 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-26 19:52:15 -0400
commit22d96aa59cf120db3584e4c3365554cae77d2441 (patch)
treec43dd27c61139ee599bfe34ccaa7b14382d74b8a /drivers
parent8a233f01b7d7dd587f85fa581274f168f1e88bb8 (diff)
drivers/misc/apds9802als.c: ALS drivers for the apds9802als
This adds support for the ADPS9802ALS sensor. Cleanup by Alan Cox - move mutexes to cover more things - report I/O errors back to user space - report range and values in LUX Signed-off-by: Anantha Narayanan <anantha.narayanan@intel.com> [The 4K and 64K in the hw spec actually means 4095 (12bit) and 65535 (16bit).] Signed-off-by: Hong Liu <hong.liu@intel.com> [Updated to match the ALS light API interface convention from Samu] Signed-off-by: Alan Cox <alan@linux.intel.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/misc/Kconfig10
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/apds9802als.c308
3 files changed, 319 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 477a9434b9c0..6d7665ce6f4a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -285,6 +285,16 @@ config SGI_GRU_DEBUG
285 This option enables addition debugging code for the SGI GRU driver. If 285 This option enables addition debugging code for the SGI GRU driver. If
286 you are unsure, say N. 286 you are unsure, say N.
287 287
288config APDS9802ALS
289 tristate "Medfield Avago APDS9802 ALS Sensor module"
290 depends on I2C
291 help
292 If you say yes here you get support for the ALS APDS9802 ambient
293 light sensor.
294
295 This driver can also be built as a module. If so, the module
296 will be called apds9802als.
297
288config ISL29003 298config ISL29003
289 tristate "Intersil ISL29003 ambient light sensor" 299 tristate "Intersil ISL29003 ambient light sensor"
290 depends on I2C && SYSFS 300 depends on I2C && SYSFS
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 2ecdd32d1357..4be5c6fc5ef4 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_SGI_XP) += sgi-xp/
25obj-$(CONFIG_SGI_GRU) += sgi-gru/ 25obj-$(CONFIG_SGI_GRU) += sgi-gru/
26obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o 26obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o
27obj-$(CONFIG_HP_ILO) += hpilo.o 27obj-$(CONFIG_HP_ILO) += hpilo.o
28obj-$(CONFIG_APDS9802ALS) += apds9802als.o
28obj-$(CONFIG_ISL29003) += isl29003.o 29obj-$(CONFIG_ISL29003) += isl29003.o
29obj-$(CONFIG_ISL29020) += isl29020.o 30obj-$(CONFIG_ISL29020) += isl29020.o
30obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o 31obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c
new file mode 100644
index 000000000000..fbe49602f396
--- /dev/null
+++ b/drivers/misc/apds9802als.c
@@ -0,0 +1,308 @@
1/*
2 * apds9802als.c - apds9802 ALS Driver
3 *
4 * Copyright (C) 2009 Intel Corp
5 *
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21 *
22 */
23
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/slab.h>
27#include <linux/i2c.h>
28#include <linux/err.h>
29#include <linux/delay.h>
30#include <linux/mutex.h>
31#include <linux/sysfs.h>
32#include <linux/hwmon-sysfs.h>
33
34#define ALS_MIN_RANGE_VAL 1
35#define ALS_MAX_RANGE_VAL 2
36#define POWER_STA_ENABLE 1
37#define POWER_STA_DISABLE 0
38#define APDS9802ALS_I2C_ADDR 0x29
39
40#define DRIVER_NAME "apds9802als"
41
42struct als_data {
43 struct device *hwmon_dev;
44 bool needresume;
45 struct mutex mutex;
46};
47
48static ssize_t als_sensing_range_show(struct device *dev,
49 struct device_attribute *attr, char *buf)
50{
51 struct i2c_client *client = to_i2c_client(dev);
52 int val;
53
54 val = i2c_smbus_read_byte_data(client, 0x81);
55 if (val < 0)
56 return val;
57 if (val & 1)
58 return sprintf(buf, "4095\n");
59 else
60 return sprintf(buf, "65535\n");
61}
62
63static ssize_t als_lux0_input_data_show(struct device *dev,
64 struct device_attribute *attr, char *buf)
65{
66 struct i2c_client *client = to_i2c_client(dev);
67 struct als_data *data = i2c_get_clientdata(client);
68 unsigned int ret_val;
69 int temp;
70
71 /* Protect against parallel reads */
72 mutex_lock(&data->mutex);
73 temp = i2c_smbus_read_byte_data(client, 0x8C);/*LSB data*/
74 if (temp < 0) {
75 ret_val = temp;
76 goto failed;
77 }
78 ret_val = i2c_smbus_read_byte_data(client, 0x8D);/*MSB data*/
79 if (ret_val < 0)
80 goto failed;
81 mutex_unlock(&data->mutex);
82 ret_val = (ret_val << 8) | temp;
83 return sprintf(buf, "%d\n", ret_val);
84failed:
85 mutex_unlock(&data->mutex);
86 return ret_val;
87}
88
89static ssize_t als_sensing_range_store(struct device *dev,
90 struct device_attribute *attr, const char *buf, size_t count)
91{
92 struct i2c_client *client = to_i2c_client(dev);
93 struct als_data *data = i2c_get_clientdata(client);
94 unsigned int ret_val;
95 unsigned long val;
96
97 if (strict_strtoul(buf, 10, &val))
98 return -EINVAL;
99
100 if (val < 4096)
101 val = 1;
102 else if (val < 65536)
103 val = 2;
104 else
105 return -ERANGE;
106
107 /* Make sure nobody else reads/modifies/writes 0x81 while we
108 are active */
109
110 mutex_lock(&data->mutex);
111
112 ret_val = i2c_smbus_read_byte_data(client, 0x81);
113 if (ret_val < 0)
114 goto fail;
115
116 /* Reset the bits before setting them */
117 ret_val = ret_val & 0xFA;
118
119 if (val == 1) /* Setting the continous measurement up to 4k LUX */
120 ret_val = (ret_val | 0x05);
121 else /* Setting the continous measurement up to 64k LUX*/
122 ret_val = (ret_val | 0x04);
123
124 ret_val = i2c_smbus_write_byte_data(client, 0x81, ret_val);
125 if (ret_val >= 0) {
126 /* All OK */
127 mutex_unlock(&data->mutex);
128 return count;
129 }
130fail:
131 mutex_unlock(&data->mutex);
132 return ret_val;
133}
134
135static ssize_t als_power_status_show(struct device *dev,
136 struct device_attribute *attr, char *buf)
137{
138 struct i2c_client *client = to_i2c_client(dev);
139 int ret_val;
140 ret_val = i2c_smbus_read_byte_data(client, 0x80);
141 if (ret_val < 0)
142 return ret_val;
143 ret_val = ret_val & 0x01;
144 return sprintf(buf, "%d\n", ret_val);
145}
146
147static int als_set_power_state(struct i2c_client *client, bool on_off)
148{
149 int ret_val;
150 struct als_data *data = i2c_get_clientdata(client);
151
152 mutex_lock(&data->mutex);
153 ret_val = i2c_smbus_read_byte_data(client, 0x80);
154 if (ret_val < 0)
155 goto fail;
156 if (on_off)
157 ret_val = ret_val | 0x01;
158 else
159 ret_val = ret_val & 0xFE;
160 ret_val = i2c_smbus_write_byte_data(client, 0x80, ret_val);
161fail:
162 mutex_unlock(&data->mutex);
163 return ret_val;
164}
165
166static ssize_t als_power_status_store(struct device *dev,
167 struct device_attribute *attr, const char *buf, size_t count)
168{
169 struct i2c_client *client = to_i2c_client(dev);
170 struct als_data *data = i2c_get_clientdata(client);
171 unsigned long val;
172 int ret_val;
173
174 if (strict_strtoul(buf, 10, &val))
175 return -EINVAL;
176 if (val == POWER_STA_ENABLE) {
177 ret_val = als_set_power_state(client, true);
178 data->needresume = true;
179 } else if (val == POWER_STA_DISABLE) {
180 ret_val = als_set_power_state(client, false);
181 data->needresume = false;
182 } else
183 return -EINVAL;
184 if (ret_val < 0)
185 return ret_val;
186 return count;
187}
188
189static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR,
190 als_sensing_range_show, als_sensing_range_store);
191static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux0_input_data_show, NULL);
192static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
193 als_power_status_show, als_power_status_store);
194
195static struct attribute *mid_att_als[] = {
196 &dev_attr_lux0_sensor_range.attr,
197 &dev_attr_lux0_input.attr,
198 &dev_attr_power_state.attr,
199 NULL
200};
201
202static struct attribute_group m_als_gr = {
203 .name = "apds9802als",
204 .attrs = mid_att_als
205};
206
207static int als_set_default_config(struct i2c_client *client)
208{
209 int ret_val;
210 /* Write the command and then switch on */
211 ret_val = i2c_smbus_write_byte_data(client, 0x80, 0x01);
212 if (ret_val < 0) {
213 dev_err(&client->dev, "failed default switch on write\n");
214 return ret_val;
215 }
216 /* Continous from 1Lux to 64k Lux */
217 ret_val = i2c_smbus_write_byte_data(client, 0x81, 0x04);
218 if (ret_val < 0)
219 dev_err(&client->dev, "failed default LUX on write\n");
220 return ret_val;
221}
222
223static int apds9802als_probe(struct i2c_client *client,
224 const struct i2c_device_id *id)
225{
226 int res;
227 struct als_data *data;
228
229 data = kzalloc(sizeof(struct als_data), GFP_KERNEL);
230 if (data == NULL) {
231 dev_err(&client->dev, "Memory allocation failed\n");
232 return -ENOMEM;
233 }
234 i2c_set_clientdata(client, data);
235 res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
236 if (res) {
237 dev_err(&client->dev, "device create file failed\n");
238 goto als_error1;
239 }
240 dev_info(&client->dev,
241 "%s apds9802als: ALS chip found\n", client->name);
242 als_set_default_config(client);
243 data->needresume = true;
244 mutex_init(&data->mutex);
245 return res;
246als_error1:
247 i2c_set_clientdata(client, NULL);
248 kfree(data);
249 return res;
250}
251
252static int apds9802als_remove(struct i2c_client *client)
253{
254 struct als_data *data = i2c_get_clientdata(client);
255 sysfs_remove_group(&client->dev.kobj, &m_als_gr);
256 kfree(data);
257 return 0;
258}
259
260static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg)
261{
262 als_set_power_state(client, false);
263 return 0;
264}
265
266static int apds9802als_resume(struct i2c_client *client)
267{
268 struct als_data *data = i2c_get_clientdata(client);
269
270 if (data->needresume == true)
271 als_set_power_state(client, true);
272 return 0;
273}
274
275static struct i2c_device_id apds9802als_id[] = {
276 { DRIVER_NAME, 0 },
277 { }
278};
279
280MODULE_DEVICE_TABLE(i2c, apds9802als_id);
281
282static struct i2c_driver apds9802als_driver = {
283 .driver = {
284 .name = DRIVER_NAME,
285 .owner = THIS_MODULE,
286 },
287 .probe = apds9802als_probe,
288 .remove = apds9802als_remove,
289 .suspend = apds9802als_suspend,
290 .resume = apds9802als_resume,
291 .id_table = apds9802als_id,
292};
293
294static int __init sensor_apds9802als_init(void)
295{
296 return i2c_add_driver(&apds9802als_driver);
297}
298
299static void __exit sensor_apds9802als_exit(void)
300{
301 i2c_del_driver(&apds9802als_driver);
302}
303module_init(sensor_apds9802als_init);
304module_exit(sensor_apds9802als_exit);
305
306MODULE_AUTHOR("Anantha Narayanan <Anantha.Narayanan@intel.com");
307MODULE_DESCRIPTION("Avago apds9802als ALS Driver");
308MODULE_LICENSE("GPL v2");