Merge git://www.linux-watchdog.org/linux-watchdog
[platform/upstream/kernel-adaptation-pc.git] / 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 #include <linux/module.h>
26
27 #define BH1780_REG_CONTROL      0x80
28 #define BH1780_REG_PARTID       0x8A
29 #define BH1780_REG_MANFID       0x8B
30 #define BH1780_REG_DLOW 0x8C
31 #define BH1780_REG_DHIGH        0x8D
32
33 #define BH1780_REVMASK          (0xf)
34 #define BH1780_POWMASK          (0x3)
35 #define BH1780_POFF             (0x0)
36 #define BH1780_PON              (0x3)
37
38 /* power on settling time in ms */
39 #define BH1780_PON_DELAY        2
40
41 struct bh1780_data {
42         struct i2c_client *client;
43         int power_state;
44         /* lock for sysfs operations */
45         struct mutex lock;
46 };
47
48 static int bh1780_write(struct bh1780_data *ddata, u8 reg, u8 val, char *msg)
49 {
50         int ret = i2c_smbus_write_byte_data(ddata->client, reg, val);
51         if (ret < 0)
52                 dev_err(&ddata->client->dev,
53                         "i2c_smbus_write_byte_data failed error %d Register (%s)\n",
54                         ret, msg);
55         return ret;
56 }
57
58 static int bh1780_read(struct bh1780_data *ddata, u8 reg, char *msg)
59 {
60         int ret = i2c_smbus_read_byte_data(ddata->client, reg);
61         if (ret < 0)
62                 dev_err(&ddata->client->dev,
63                         "i2c_smbus_read_byte_data failed error %d Register (%s)\n",
64                         ret, msg);
65         return ret;
66 }
67
68 static ssize_t bh1780_show_lux(struct device *dev,
69                                 struct device_attribute *attr, char *buf)
70 {
71         struct platform_device *pdev = to_platform_device(dev);
72         struct bh1780_data *ddata = platform_get_drvdata(pdev);
73         int lsb, msb;
74
75         lsb = bh1780_read(ddata, BH1780_REG_DLOW, "DLOW");
76         if (lsb < 0)
77                 return lsb;
78
79         msb = bh1780_read(ddata, BH1780_REG_DHIGH, "DHIGH");
80         if (msb < 0)
81                 return msb;
82
83         return sprintf(buf, "%d\n", (msb << 8) | lsb);
84 }
85
86 static ssize_t bh1780_show_power_state(struct device *dev,
87                                         struct device_attribute *attr,
88                                         char *buf)
89 {
90         struct platform_device *pdev = to_platform_device(dev);
91         struct bh1780_data *ddata = platform_get_drvdata(pdev);
92         int state;
93
94         state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL");
95         if (state < 0)
96                 return state;
97
98         return sprintf(buf, "%d\n", state & BH1780_POWMASK);
99 }
100
101 static ssize_t bh1780_store_power_state(struct device *dev,
102                                         struct device_attribute *attr,
103                                         const char *buf, size_t count)
104 {
105         struct platform_device *pdev = to_platform_device(dev);
106         struct bh1780_data *ddata = platform_get_drvdata(pdev);
107         unsigned long val;
108         int error;
109
110         error = strict_strtoul(buf, 0, &val);
111         if (error)
112                 return error;
113
114         if (val < BH1780_POFF || val > BH1780_PON)
115                 return -EINVAL;
116
117         mutex_lock(&ddata->lock);
118
119         error = bh1780_write(ddata, BH1780_REG_CONTROL, val, "CONTROL");
120         if (error < 0) {
121                 mutex_unlock(&ddata->lock);
122                 return error;
123         }
124
125         msleep(BH1780_PON_DELAY);
126         ddata->power_state = val;
127         mutex_unlock(&ddata->lock);
128
129         return count;
130 }
131
132 static DEVICE_ATTR(lux, S_IRUGO, bh1780_show_lux, NULL);
133
134 static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
135                 bh1780_show_power_state, bh1780_store_power_state);
136
137 static struct attribute *bh1780_attributes[] = {
138         &dev_attr_power_state.attr,
139         &dev_attr_lux.attr,
140         NULL
141 };
142
143 static const struct attribute_group bh1780_attr_group = {
144         .attrs = bh1780_attributes,
145 };
146
147 static int bh1780_probe(struct i2c_client *client,
148                                                 const struct i2c_device_id *id)
149 {
150         int ret;
151         struct bh1780_data *ddata = NULL;
152         struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
153
154         if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {
155                 ret = -EIO;
156                 goto err_op_failed;
157         }
158
159         ddata = kzalloc(sizeof(struct bh1780_data), GFP_KERNEL);
160         if (ddata == NULL) {
161                 ret = -ENOMEM;
162                 goto err_op_failed;
163         }
164
165         ddata->client = client;
166         i2c_set_clientdata(client, ddata);
167
168         ret = bh1780_read(ddata, BH1780_REG_PARTID, "PART ID");
169         if (ret < 0)
170                 goto err_op_failed;
171
172         dev_info(&client->dev, "Ambient Light Sensor, Rev : %d\n",
173                         (ret & BH1780_REVMASK));
174
175         mutex_init(&ddata->lock);
176
177         ret = sysfs_create_group(&client->dev.kobj, &bh1780_attr_group);
178         if (ret)
179                 goto err_op_failed;
180
181         return 0;
182
183 err_op_failed:
184         kfree(ddata);
185         return ret;
186 }
187
188 static int bh1780_remove(struct i2c_client *client)
189 {
190         struct bh1780_data *ddata;
191
192         ddata = i2c_get_clientdata(client);
193         sysfs_remove_group(&client->dev.kobj, &bh1780_attr_group);
194         kfree(ddata);
195
196         return 0;
197 }
198
199 #ifdef CONFIG_PM
200 static int bh1780_suspend(struct device *dev)
201 {
202         struct bh1780_data *ddata;
203         int state, ret;
204         struct i2c_client *client = to_i2c_client(dev);
205
206         ddata = i2c_get_clientdata(client);
207         state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL");
208         if (state < 0)
209                 return state;
210
211         ddata->power_state = state & BH1780_POWMASK;
212
213         ret = bh1780_write(ddata, BH1780_REG_CONTROL, BH1780_POFF,
214                                 "CONTROL");
215
216         if (ret < 0)
217                 return ret;
218
219         return 0;
220 }
221
222 static int bh1780_resume(struct device *dev)
223 {
224         struct bh1780_data *ddata;
225         int state, ret;
226         struct i2c_client *client = to_i2c_client(dev);
227
228         ddata = i2c_get_clientdata(client);
229         state = ddata->power_state;
230         ret = bh1780_write(ddata, BH1780_REG_CONTROL, state,
231                                 "CONTROL");
232
233         if (ret < 0)
234                 return ret;
235
236         return 0;
237 }
238 static SIMPLE_DEV_PM_OPS(bh1780_pm, bh1780_suspend, bh1780_resume);
239 #define BH1780_PMOPS (&bh1780_pm)
240 #else
241 #define BH1780_PMOPS NULL
242 #endif /* CONFIG_PM */
243
244 static const struct i2c_device_id bh1780_id[] = {
245         { "bh1780", 0 },
246         { },
247 };
248
249 static struct i2c_driver bh1780_driver = {
250         .probe          = bh1780_probe,
251         .remove         = bh1780_remove,
252         .id_table       = bh1780_id,
253         .driver = {
254                 .name = "bh1780",
255                 .pm     = BH1780_PMOPS,
256         },
257 };
258
259 module_i2c_driver(bh1780_driver);
260
261 MODULE_DESCRIPTION("BH1780GLI Ambient Light Sensor Driver");
262 MODULE_LICENSE("GPL");
263 MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");