aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/isl29020.c
diff options
context:
space:
mode:
authorKalhan Trisal <kalhan.trisal@intel.com>2010-10-26 17:22:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-26 19:52:15 -0400
commit2e85c4ddd3f32d3e1da51f4129473399e505ffa3 (patch)
tree309e11ed73bca83507388edcd1c903453f9d48e0 /drivers/misc/isl29020.c
parent93e2f585c149b5056d5c5eaffcaf747bbe9c3015 (diff)
drivers/misc/isl29020.c: ambient light sensor
The LS driver will read the latest Lux measurement based upon the light brightness and will report the LUX output through sysfs interface. This hardware isn't quite the same as the ISL29003 so has a different driver. [akpm@linux-foundation.org: put PM code under #ifdef CONFIG_PM] Signed-off-by: Kalhan Trisal <kalhan.trisal@intel.com> [Runtime power management support added] Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> [Fixes to runtime PM] Signed-off-by: Liu Hong <hong.liu@intel.com> [Cleanups and added checks for I2C errors, reworked the API to match the saner one agreed for other sensors] Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/misc/isl29020.c')
-rw-r--r--drivers/misc/isl29020.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c
new file mode 100644
index 000000000000..34fe835921c4
--- /dev/null
+++ b/drivers/misc/isl29020.c
@@ -0,0 +1,248 @@
1/*
2 * isl29020.c - Intersil ALS Driver
3 *
4 * Copyright (C) 2008 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 * Data sheet at: http://www.intersil.com/data/fn/fn6505.pdf
23 */
24
25#include <linux/module.h>
26#include <linux/init.h>
27#include <linux/slab.h>
28#include <linux/i2c.h>
29#include <linux/err.h>
30#include <linux/delay.h>
31#include <linux/sysfs.h>
32#include <linux/pm_runtime.h>
33
34static DEFINE_MUTEX(mutex);
35
36static ssize_t als_sensing_range_show(struct device *dev,
37 struct device_attribute *attr, char *buf)
38{
39 struct i2c_client *client = to_i2c_client(dev);
40 int val;
41
42 val = i2c_smbus_read_byte_data(client, 0x00);
43
44 if (val < 0)
45 return val;
46 return sprintf(buf, "%d000\n", 1 << (2 * (val & 3)));
47
48}
49
50static ssize_t als_lux_input_data_show(struct device *dev,
51 struct device_attribute *attr, char *buf)
52{
53 struct i2c_client *client = to_i2c_client(dev);
54 int ret_val, val;
55 unsigned long int lux;
56 int temp;
57
58 pm_runtime_get_sync(dev);
59 msleep(100);
60
61 mutex_lock(&mutex);
62 temp = i2c_smbus_read_byte_data(client, 0x02); /* MSB data */
63 if (temp < 0) {
64 pm_runtime_put_sync(dev);
65 mutex_unlock(&mutex);
66 return temp;
67 }
68
69 ret_val = i2c_smbus_read_byte_data(client, 0x01); /* LSB data */
70 mutex_unlock(&mutex);
71
72 if (ret_val < 0) {
73 pm_runtime_put_sync(dev);
74 return ret_val;
75 }
76
77 ret_val |= temp << 8;
78 val = i2c_smbus_read_byte_data(client, 0x00);
79 pm_runtime_put_sync(dev);
80 if (val < 0)
81 return val;
82 lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / 65536;
83 return sprintf(buf, "%ld\n", lux);
84}
85
86static ssize_t als_sensing_range_store(struct device *dev,
87 struct device_attribute *attr, const char *buf, size_t count)
88{
89 struct i2c_client *client = to_i2c_client(dev);
90 unsigned int ret_val;
91 unsigned long val;
92
93 if (strict_strtoul(buf, 10, &val))
94 return -EINVAL;
95 if (val < 1 || val > 64000)
96 return -EINVAL;
97
98 /* Pick the smallest sensor range that will meet our requirements */
99 if (val <= 1000)
100 val = 1;
101 else if (val <= 4000)
102 val = 2;
103 else if (val <= 16000)
104 val = 3;
105 else
106 val = 4;
107
108 ret_val = i2c_smbus_read_byte_data(client, 0x00);
109
110 ret_val &= 0xFC; /*reset the bit before setting them */
111 ret_val |= val - 1;
112 ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val);
113
114 if (ret_val < 0)
115 return ret_val;
116 return count;
117}
118
119static void als_set_power_state(struct i2c_client *client, int enable)
120{
121 int ret_val;
122
123 ret_val = i2c_smbus_read_byte_data(client, 0x00);
124 if (ret_val < 0)
125 return;
126
127 if (enable)
128 ret_val |= 0x80;
129 else
130 ret_val &= 0x7F;
131
132 i2c_smbus_write_byte_data(client, 0x00, ret_val);
133}
134
135static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR,
136 als_sensing_range_show, als_sensing_range_store);
137static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL);
138
139static struct attribute *mid_att_als[] = {
140 &dev_attr_lux0_sensor_range.attr,
141 &dev_attr_lux0_input.attr,
142 NULL
143};
144
145static struct attribute_group m_als_gr = {
146 .name = "isl29020",
147 .attrs = mid_att_als
148};
149
150static int als_set_default_config(struct i2c_client *client)
151{
152 int retval;
153
154 retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0);
155 if (retval < 0) {
156 dev_err(&client->dev, "default write failed.");
157 return retval;
158 }
159 return 0;;
160}
161
162static int isl29020_probe(struct i2c_client *client,
163 const struct i2c_device_id *id)
164{
165 int res;
166
167 res = als_set_default_config(client);
168 if (res < 0)
169 return res;
170
171 res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
172 if (res) {
173 dev_err(&client->dev, "isl29020: device create file failed\n");
174 return res;
175 }
176 dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name);
177 als_set_power_state(client, 0);
178 pm_runtime_enable(&client->dev);
179 return res;
180}
181
182static int isl29020_remove(struct i2c_client *client)
183{
184 struct als_data *data = i2c_get_clientdata(client);
185 sysfs_remove_group(&client->dev.kobj, &m_als_gr);
186 kfree(data);
187 return 0;
188}
189
190static struct i2c_device_id isl29020_id[] = {
191 { "isl29020", 0 },
192 { }
193};
194
195MODULE_DEVICE_TABLE(i2c, isl29020_id);
196
197#ifdef CONFIG_PM
198
199static int isl29020_runtime_suspend(struct device *dev)
200{
201 struct i2c_client *client = to_i2c_client(dev);
202 als_set_power_state(client, 0);
203 return 0;
204}
205
206static int isl29020_runtime_resume(struct device *dev)
207{
208 struct i2c_client *client = to_i2c_client(dev);
209 als_set_power_state(client, 1);
210 return 0;
211}
212
213static const struct dev_pm_ops isl29020_pm_ops = {
214 .runtime_suspend = isl29020_runtime_suspend,
215 .runtime_resume = isl29020_runtime_resume,
216};
217
218#define ISL29020_PM_OPS (&isl29020_pm_ops)
219#else /* CONFIG_PM */
220#define ISL29020_PM_OPS NULL
221#endif /* CONFIG_PM */
222
223static struct i2c_driver isl29020_driver = {
224 .driver = {
225 .name = "isl29020",
226 .pm = ISL29020_PM_OPS,
227 },
228 .probe = isl29020_probe,
229 .remove = isl29020_remove,
230 .id_table = isl29020_id,
231};
232
233static int __init sensor_isl29020_init(void)
234{
235 return i2c_add_driver(&isl29020_driver);
236}
237
238static void __exit sensor_isl29020_exit(void)
239{
240 i2c_del_driver(&isl29020_driver);
241}
242
243module_init(sensor_isl29020_init);
244module_exit(sensor_isl29020_exit);
245
246MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
247MODULE_DESCRIPTION("Intersil isl29020 ALS Driver");
248MODULE_LICENSE("GPL v2");