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