Commit 7efe15f2a4cd9d40826d31d7d5ef56094f8b65f5
Committed by
Linus Torvalds
1 parent
37ed19d5cc
Exists in
master
and in
4 other branches
drivers/misc: ROHM BH1780GLI ambient light sensor driver
Add support for ROHM BH1780GLI Ambient light sensor. BH1780 supports I2C interface. Driver supports read/update of power state and read of lux value (through SYSFS). Writing value 3 to power_state enables the sensor and current lux value could be read. Currently this driver follows the same sysfs convention as supported by drivers/misc/isl29003.c. Signed-off-by: Hemanth V <hemanthv@ti.com> Reviewed-by: Daniel Mack <daniel@caiaq.de> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Cc: Jean Delvare <khali@linux-fr.org> Cc: Wolfram Sang <w.sang@pengutronix.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 3 changed files with 284 additions and 0 deletions Side-by-side Diff
drivers/misc/Kconfig
... | ... | @@ -304,6 +304,16 @@ |
304 | 304 | This driver can also be built as a module. If so, the module |
305 | 305 | will be called tsl2550. |
306 | 306 | |
307 | +config SENSORS_BH1780 | |
308 | + tristate "ROHM BH1780GLI ambient light sensor" | |
309 | + depends on I2C && SYSFS | |
310 | + help | |
311 | + If you say yes here you get support for the ROHM BH1780GLI | |
312 | + ambient light sensor. | |
313 | + | |
314 | + This driver can also be built as a module. If so, the module | |
315 | + will be called bh1780gli. | |
316 | + | |
307 | 317 | config EP93XX_PWM |
308 | 318 | tristate "EP93xx PWM support" |
309 | 319 | depends on ARCH_EP93XX |
drivers/misc/Makefile
... | ... | @@ -14,6 +14,7 @@ |
14 | 14 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o |
15 | 15 | obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o |
16 | 16 | obj-$(CONFIG_PHANTOM) += phantom.o |
17 | +obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o | |
17 | 18 | obj-$(CONFIG_SGI_IOC4) += ioc4.o |
18 | 19 | obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o |
19 | 20 | obj-$(CONFIG_KGDB_TESTS) += kgdbts.o |
drivers/misc/bh1780gli.c
1 | +/* | |
2 | + * bh1780gli.c | |
3 | + * ROHM Ambient Light Sensor Driver | |
4 | + * | |
5 | + * Copyright (C) 2010 Texas Instruments | |
6 | + * Author: Hemanth V <hemanthv@ti.com> | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify it | |
9 | + * under the terms of the GNU General Public License version 2 as published by | |
10 | + * the Free Software Foundation. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | + * more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public License along with | |
18 | + * this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | + */ | |
20 | +#include <linux/i2c.h> | |
21 | +#include <linux/slab.h> | |
22 | +#include <linux/mutex.h> | |
23 | +#include <linux/platform_device.h> | |
24 | +#include <linux/delay.h> | |
25 | + | |
26 | +#define BH1780_REG_CONTROL 0x80 | |
27 | +#define BH1780_REG_PARTID 0x8A | |
28 | +#define BH1780_REG_MANFID 0x8B | |
29 | +#define BH1780_REG_DLOW 0x8C | |
30 | +#define BH1780_REG_DHIGH 0x8D | |
31 | + | |
32 | +#define BH1780_REVMASK (0xf) | |
33 | +#define BH1780_POWMASK (0x3) | |
34 | +#define BH1780_POFF (0x0) | |
35 | +#define BH1780_PON (0x3) | |
36 | + | |
37 | +/* power on settling time in ms */ | |
38 | +#define BH1780_PON_DELAY 2 | |
39 | + | |
40 | +struct bh1780_data { | |
41 | + struct i2c_client *client; | |
42 | + int power_state; | |
43 | + /* lock for sysfs operations */ | |
44 | + struct mutex lock; | |
45 | +}; | |
46 | + | |
47 | +static int bh1780_write(struct bh1780_data *ddata, u8 reg, u8 val, char *msg) | |
48 | +{ | |
49 | + int ret = i2c_smbus_write_byte_data(ddata->client, reg, val); | |
50 | + if (ret < 0) | |
51 | + dev_err(&ddata->client->dev, | |
52 | + "i2c_smbus_write_byte_data failed error %d\ | |
53 | + Register (%s)\n", ret, msg); | |
54 | + return ret; | |
55 | +} | |
56 | + | |
57 | +static int bh1780_read(struct bh1780_data *ddata, u8 reg, char *msg) | |
58 | +{ | |
59 | + int ret = i2c_smbus_read_byte_data(ddata->client, reg); | |
60 | + if (ret < 0) | |
61 | + dev_err(&ddata->client->dev, | |
62 | + "i2c_smbus_read_byte_data failed error %d\ | |
63 | + Register (%s)\n", ret, msg); | |
64 | + return ret; | |
65 | +} | |
66 | + | |
67 | +static ssize_t bh1780_show_lux(struct device *dev, | |
68 | + struct device_attribute *attr, char *buf) | |
69 | +{ | |
70 | + struct platform_device *pdev = to_platform_device(dev); | |
71 | + struct bh1780_data *ddata = platform_get_drvdata(pdev); | |
72 | + int lsb, msb; | |
73 | + | |
74 | + lsb = bh1780_read(ddata, BH1780_REG_DLOW, "DLOW"); | |
75 | + if (lsb < 0) | |
76 | + return lsb; | |
77 | + | |
78 | + msb = bh1780_read(ddata, BH1780_REG_DHIGH, "DHIGH"); | |
79 | + if (msb < 0) | |
80 | + return msb; | |
81 | + | |
82 | + return sprintf(buf, "%d\n", (msb << 8) | lsb); | |
83 | +} | |
84 | + | |
85 | +static ssize_t bh1780_show_power_state(struct device *dev, | |
86 | + struct device_attribute *attr, | |
87 | + char *buf) | |
88 | +{ | |
89 | + struct platform_device *pdev = to_platform_device(dev); | |
90 | + struct bh1780_data *ddata = platform_get_drvdata(pdev); | |
91 | + int state; | |
92 | + | |
93 | + state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL"); | |
94 | + if (state < 0) | |
95 | + return state; | |
96 | + | |
97 | + return sprintf(buf, "%d\n", state & BH1780_POWMASK); | |
98 | +} | |
99 | + | |
100 | +static ssize_t bh1780_store_power_state(struct device *dev, | |
101 | + struct device_attribute *attr, | |
102 | + const char *buf, size_t count) | |
103 | +{ | |
104 | + struct platform_device *pdev = to_platform_device(dev); | |
105 | + struct bh1780_data *ddata = platform_get_drvdata(pdev); | |
106 | + unsigned long val; | |
107 | + int error; | |
108 | + | |
109 | + error = strict_strtoul(buf, 0, &val); | |
110 | + if (error) | |
111 | + return error; | |
112 | + | |
113 | + if (val < BH1780_POFF || val > BH1780_PON) | |
114 | + return -EINVAL; | |
115 | + | |
116 | + mutex_lock(&ddata->lock); | |
117 | + | |
118 | + error = bh1780_write(ddata, BH1780_REG_CONTROL, val, "CONTROL"); | |
119 | + if (error < 0) { | |
120 | + mutex_unlock(&ddata->lock); | |
121 | + return error; | |
122 | + } | |
123 | + | |
124 | + msleep(BH1780_PON_DELAY); | |
125 | + ddata->power_state = val; | |
126 | + mutex_unlock(&ddata->lock); | |
127 | + | |
128 | + return count; | |
129 | +} | |
130 | + | |
131 | +static DEVICE_ATTR(lux, S_IRUGO, bh1780_show_lux, NULL); | |
132 | + | |
133 | +static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, | |
134 | + bh1780_show_power_state, bh1780_store_power_state); | |
135 | + | |
136 | +static struct attribute *bh1780_attributes[] = { | |
137 | + &dev_attr_power_state.attr, | |
138 | + &dev_attr_lux.attr, | |
139 | + NULL | |
140 | +}; | |
141 | + | |
142 | +static const struct attribute_group bh1780_attr_group = { | |
143 | + .attrs = bh1780_attributes, | |
144 | +}; | |
145 | + | |
146 | +static int __devinit bh1780_probe(struct i2c_client *client, | |
147 | + const struct i2c_device_id *id) | |
148 | +{ | |
149 | + int ret; | |
150 | + struct bh1780_data *ddata = NULL; | |
151 | + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | |
152 | + | |
153 | + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) { | |
154 | + ret = -EIO; | |
155 | + goto err_op_failed; | |
156 | + } | |
157 | + | |
158 | + ddata = kzalloc(sizeof(struct bh1780_data), GFP_KERNEL); | |
159 | + if (ddata == NULL) { | |
160 | + ret = -ENOMEM; | |
161 | + goto err_op_failed; | |
162 | + } | |
163 | + | |
164 | + ddata->client = client; | |
165 | + i2c_set_clientdata(client, ddata); | |
166 | + | |
167 | + ret = bh1780_read(ddata, BH1780_REG_PARTID, "PART ID"); | |
168 | + if (ret < 0) | |
169 | + goto err_op_failed; | |
170 | + | |
171 | + dev_info(&client->dev, "Ambient Light Sensor, Rev : %d\n", | |
172 | + (ret & BH1780_REVMASK)); | |
173 | + | |
174 | + mutex_init(&ddata->lock); | |
175 | + | |
176 | + ret = sysfs_create_group(&client->dev.kobj, &bh1780_attr_group); | |
177 | + if (ret) | |
178 | + goto err_op_failed; | |
179 | + | |
180 | + return 0; | |
181 | + | |
182 | +err_op_failed: | |
183 | + kfree(ddata); | |
184 | + return ret; | |
185 | +} | |
186 | + | |
187 | +static int __devexit bh1780_remove(struct i2c_client *client) | |
188 | +{ | |
189 | + struct bh1780_data *ddata; | |
190 | + | |
191 | + ddata = i2c_get_clientdata(client); | |
192 | + sysfs_remove_group(&client->dev.kobj, &bh1780_attr_group); | |
193 | + i2c_set_clientdata(client, NULL); | |
194 | + kfree(ddata); | |
195 | + | |
196 | + return 0; | |
197 | +} | |
198 | + | |
199 | +#ifdef CONFIG_PM | |
200 | +static int bh1780_suspend(struct i2c_client *client, pm_message_t mesg) | |
201 | +{ | |
202 | + struct bh1780_data *ddata; | |
203 | + int state, ret; | |
204 | + | |
205 | + ddata = i2c_get_clientdata(client); | |
206 | + state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL"); | |
207 | + if (state < 0) | |
208 | + return state; | |
209 | + | |
210 | + ddata->power_state = state & BH1780_POWMASK; | |
211 | + | |
212 | + ret = bh1780_write(ddata, BH1780_REG_CONTROL, BH1780_POFF, | |
213 | + "CONTROL"); | |
214 | + | |
215 | + if (ret < 0) | |
216 | + return ret; | |
217 | + | |
218 | + return 0; | |
219 | +} | |
220 | + | |
221 | +static int bh1780_resume(struct i2c_client *client) | |
222 | +{ | |
223 | + struct bh1780_data *ddata; | |
224 | + int state, ret; | |
225 | + | |
226 | + ddata = i2c_get_clientdata(client); | |
227 | + state = ddata->power_state; | |
228 | + | |
229 | + ret = bh1780_write(ddata, BH1780_REG_CONTROL, state, | |
230 | + "CONTROL"); | |
231 | + | |
232 | + if (ret < 0) | |
233 | + return ret; | |
234 | + | |
235 | + return 0; | |
236 | +} | |
237 | +#else | |
238 | +#define bh1780_suspend NULL | |
239 | +#define bh1780_resume NULL | |
240 | +#endif /* CONFIG_PM */ | |
241 | + | |
242 | +static const struct i2c_device_id bh1780_id[] = { | |
243 | + { "bh1780", 0 }, | |
244 | + { }, | |
245 | +}; | |
246 | + | |
247 | +static struct i2c_driver bh1780_driver = { | |
248 | + .probe = bh1780_probe, | |
249 | + .remove = bh1780_remove, | |
250 | + .id_table = bh1780_id, | |
251 | + .suspend = bh1780_suspend, | |
252 | + .resume = bh1780_resume, | |
253 | + .driver = { | |
254 | + .name = "bh1780" | |
255 | + }, | |
256 | +}; | |
257 | + | |
258 | +static int __init bh1780_init(void) | |
259 | +{ | |
260 | + return i2c_add_driver(&bh1780_driver); | |
261 | +} | |
262 | + | |
263 | +static void __exit bh1780_exit(void) | |
264 | +{ | |
265 | + i2c_del_driver(&bh1780_driver); | |
266 | +} | |
267 | + | |
268 | +module_init(bh1780_init) | |
269 | +module_exit(bh1780_exit) | |
270 | + | |
271 | +MODULE_DESCRIPTION("BH1780GLI Ambient Light Sensor Driver"); | |
272 | +MODULE_LICENSE("GPL"); | |
273 | +MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>"); |