]>
Commit | Line | Data |
---|---|---|
3a11fbb0 TD |
1 | /* |
2 | * ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensor driver | |
3 | * | |
4 | * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * Data sheets: | |
11 | * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1710fvc-e.pdf | |
12 | * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1715fvc-e.pdf | |
13 | * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1721fvc-e.pdf | |
14 | * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1750fvi-e.pdf | |
15 | * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1751fvi-e.pdf | |
16 | * | |
17 | * 7-bit I2C slave addresses: | |
18 | * 0x23 (ADDR pin low) | |
19 | * 0x5C (ADDR pin high) | |
20 | * | |
21 | */ | |
22 | ||
23 | #include <linux/delay.h> | |
24 | #include <linux/i2c.h> | |
25 | #include <linux/iio/iio.h> | |
26 | #include <linux/iio/sysfs.h> | |
27 | #include <linux/module.h> | |
28 | ||
29 | #define BH1750_POWER_DOWN 0x00 | |
30 | #define BH1750_ONE_TIME_H_RES_MODE 0x20 /* auto-mode for BH1721 */ | |
31 | #define BH1750_CHANGE_INT_TIME_H_BIT 0x40 | |
32 | #define BH1750_CHANGE_INT_TIME_L_BIT 0x60 | |
33 | ||
34 | enum { | |
35 | BH1710, | |
36 | BH1721, | |
37 | BH1750, | |
38 | }; | |
39 | ||
40 | struct bh1750_chip_info; | |
41 | struct bh1750_data { | |
42 | struct i2c_client *client; | |
43 | struct mutex lock; | |
44 | const struct bh1750_chip_info *chip_info; | |
45 | u16 mtreg; | |
46 | }; | |
47 | ||
48 | struct bh1750_chip_info { | |
49 | u16 mtreg_min; | |
50 | u16 mtreg_max; | |
51 | u16 mtreg_default; | |
52 | int mtreg_to_usec; | |
53 | int mtreg_to_scale; | |
54 | ||
55 | /* | |
56 | * For BH1710/BH1721 all possible integration time values won't fit | |
57 | * into one page so displaying is limited to every second one. | |
58 | * Note, that user can still write proper values which were not | |
59 | * listed. | |
60 | */ | |
61 | int inc; | |
62 | ||
63 | u16 int_time_low_mask; | |
64 | u16 int_time_high_mask; | |
eb92f596 | 65 | }; |
3a11fbb0 | 66 | |
eb92f596 | 67 | static const struct bh1750_chip_info bh1750_chip_info_tbl[] = { |
3a11fbb0 TD |
68 | [BH1710] = { 140, 1022, 300, 400, 250000000, 2, 0x001F, 0x03E0 }, |
69 | [BH1721] = { 140, 1020, 300, 400, 250000000, 2, 0x0010, 0x03E0 }, | |
70 | [BH1750] = { 31, 254, 69, 1740, 57500000, 1, 0x001F, 0x00E0 }, | |
71 | }; | |
72 | ||
73 | static int bh1750_change_int_time(struct bh1750_data *data, int usec) | |
74 | { | |
75 | int ret; | |
76 | u16 val; | |
77 | u8 regval; | |
78 | const struct bh1750_chip_info *chip_info = data->chip_info; | |
79 | ||
80 | if ((usec % chip_info->mtreg_to_usec) != 0) | |
81 | return -EINVAL; | |
82 | ||
83 | val = usec / chip_info->mtreg_to_usec; | |
84 | if (val < chip_info->mtreg_min || val > chip_info->mtreg_max) | |
85 | return -EINVAL; | |
86 | ||
87 | ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN); | |
88 | if (ret < 0) | |
89 | return ret; | |
90 | ||
91 | regval = (val & chip_info->int_time_high_mask) >> 5; | |
92 | ret = i2c_smbus_write_byte(data->client, | |
93 | BH1750_CHANGE_INT_TIME_H_BIT | regval); | |
94 | if (ret < 0) | |
95 | return ret; | |
96 | ||
97 | regval = val & chip_info->int_time_low_mask; | |
98 | ret = i2c_smbus_write_byte(data->client, | |
99 | BH1750_CHANGE_INT_TIME_L_BIT | regval); | |
100 | if (ret < 0) | |
101 | return ret; | |
102 | ||
103 | data->mtreg = val; | |
104 | ||
105 | return 0; | |
106 | } | |
107 | ||
108 | static int bh1750_read(struct bh1750_data *data, int *val) | |
109 | { | |
110 | int ret; | |
111 | __be16 result; | |
112 | const struct bh1750_chip_info *chip_info = data->chip_info; | |
113 | unsigned long delay = chip_info->mtreg_to_usec * data->mtreg; | |
114 | ||
115 | /* | |
116 | * BH1721 will enter continuous mode on receiving this command. | |
117 | * Note, that this eliminates need for bh1750_resume(). | |
118 | */ | |
119 | ret = i2c_smbus_write_byte(data->client, BH1750_ONE_TIME_H_RES_MODE); | |
120 | if (ret < 0) | |
121 | return ret; | |
122 | ||
123 | usleep_range(delay + 15000, delay + 40000); | |
124 | ||
125 | ret = i2c_master_recv(data->client, (char *)&result, 2); | |
126 | if (ret < 0) | |
127 | return ret; | |
128 | ||
129 | *val = be16_to_cpu(result); | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
134 | static int bh1750_read_raw(struct iio_dev *indio_dev, | |
135 | struct iio_chan_spec const *chan, | |
136 | int *val, int *val2, long mask) | |
137 | { | |
138 | int ret, tmp; | |
139 | struct bh1750_data *data = iio_priv(indio_dev); | |
140 | const struct bh1750_chip_info *chip_info = data->chip_info; | |
141 | ||
142 | switch (mask) { | |
143 | case IIO_CHAN_INFO_RAW: | |
144 | switch (chan->type) { | |
145 | case IIO_LIGHT: | |
146 | mutex_lock(&data->lock); | |
147 | ret = bh1750_read(data, val); | |
148 | mutex_unlock(&data->lock); | |
149 | if (ret < 0) | |
150 | return ret; | |
151 | ||
152 | return IIO_VAL_INT; | |
153 | default: | |
154 | return -EINVAL; | |
155 | } | |
156 | case IIO_CHAN_INFO_SCALE: | |
157 | tmp = chip_info->mtreg_to_scale / data->mtreg; | |
158 | *val = tmp / 1000000; | |
159 | *val2 = tmp % 1000000; | |
160 | return IIO_VAL_INT_PLUS_MICRO; | |
161 | case IIO_CHAN_INFO_INT_TIME: | |
162 | *val = 0; | |
163 | *val2 = chip_info->mtreg_to_usec * data->mtreg; | |
164 | return IIO_VAL_INT_PLUS_MICRO; | |
165 | default: | |
166 | return -EINVAL; | |
167 | } | |
168 | } | |
169 | ||
170 | static int bh1750_write_raw(struct iio_dev *indio_dev, | |
171 | struct iio_chan_spec const *chan, | |
172 | int val, int val2, long mask) | |
173 | { | |
174 | int ret; | |
175 | struct bh1750_data *data = iio_priv(indio_dev); | |
176 | ||
177 | switch (mask) { | |
178 | case IIO_CHAN_INFO_INT_TIME: | |
179 | if (val != 0) | |
180 | return -EINVAL; | |
181 | ||
182 | mutex_lock(&data->lock); | |
183 | ret = bh1750_change_int_time(data, val2); | |
184 | mutex_unlock(&data->lock); | |
185 | return ret; | |
186 | default: | |
187 | return -EINVAL; | |
188 | } | |
189 | } | |
190 | ||
191 | static ssize_t bh1750_show_int_time_available(struct device *dev, | |
192 | struct device_attribute *attr, char *buf) | |
193 | { | |
194 | int i; | |
195 | size_t len = 0; | |
196 | struct bh1750_data *data = iio_priv(dev_to_iio_dev(dev)); | |
197 | const struct bh1750_chip_info *chip_info = data->chip_info; | |
198 | ||
199 | for (i = chip_info->mtreg_min; i <= chip_info->mtreg_max; i += chip_info->inc) | |
200 | len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06d ", | |
201 | chip_info->mtreg_to_usec * i); | |
202 | ||
203 | buf[len - 1] = '\n'; | |
204 | ||
205 | return len; | |
206 | } | |
207 | ||
208 | static IIO_DEV_ATTR_INT_TIME_AVAIL(bh1750_show_int_time_available); | |
209 | ||
210 | static struct attribute *bh1750_attributes[] = { | |
211 | &iio_dev_attr_integration_time_available.dev_attr.attr, | |
212 | NULL, | |
213 | }; | |
214 | ||
c2869498 | 215 | static const struct attribute_group bh1750_attribute_group = { |
3a11fbb0 TD |
216 | .attrs = bh1750_attributes, |
217 | }; | |
218 | ||
219 | static const struct iio_info bh1750_info = { | |
3a11fbb0 TD |
220 | .attrs = &bh1750_attribute_group, |
221 | .read_raw = bh1750_read_raw, | |
222 | .write_raw = bh1750_write_raw, | |
223 | }; | |
224 | ||
225 | static const struct iio_chan_spec bh1750_channels[] = { | |
226 | { | |
227 | .type = IIO_LIGHT, | |
228 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | | |
229 | BIT(IIO_CHAN_INFO_SCALE) | | |
230 | BIT(IIO_CHAN_INFO_INT_TIME) | |
231 | } | |
232 | }; | |
233 | ||
234 | static int bh1750_probe(struct i2c_client *client, | |
235 | const struct i2c_device_id *id) | |
236 | { | |
237 | int ret, usec; | |
238 | struct bh1750_data *data; | |
239 | struct iio_dev *indio_dev; | |
240 | ||
241 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | | |
242 | I2C_FUNC_SMBUS_WRITE_BYTE)) | |
f8d9d3b4 | 243 | return -EOPNOTSUPP; |
3a11fbb0 TD |
244 | |
245 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
246 | if (!indio_dev) | |
247 | return -ENOMEM; | |
248 | ||
249 | data = iio_priv(indio_dev); | |
250 | i2c_set_clientdata(client, indio_dev); | |
251 | data->client = client; | |
252 | data->chip_info = &bh1750_chip_info_tbl[id->driver_data]; | |
253 | ||
254 | usec = data->chip_info->mtreg_to_usec * data->chip_info->mtreg_default; | |
255 | ret = bh1750_change_int_time(data, usec); | |
256 | if (ret < 0) | |
257 | return ret; | |
258 | ||
259 | mutex_init(&data->lock); | |
260 | indio_dev->dev.parent = &client->dev; | |
261 | indio_dev->info = &bh1750_info; | |
262 | indio_dev->name = id->name; | |
263 | indio_dev->channels = bh1750_channels; | |
264 | indio_dev->num_channels = ARRAY_SIZE(bh1750_channels); | |
265 | indio_dev->modes = INDIO_DIRECT_MODE; | |
266 | ||
267 | return iio_device_register(indio_dev); | |
268 | } | |
269 | ||
270 | static int bh1750_remove(struct i2c_client *client) | |
271 | { | |
272 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
273 | struct bh1750_data *data = iio_priv(indio_dev); | |
274 | ||
275 | iio_device_unregister(indio_dev); | |
276 | ||
277 | mutex_lock(&data->lock); | |
278 | i2c_smbus_write_byte(client, BH1750_POWER_DOWN); | |
279 | mutex_unlock(&data->lock); | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
284 | #ifdef CONFIG_PM_SLEEP | |
285 | static int bh1750_suspend(struct device *dev) | |
286 | { | |
287 | int ret; | |
288 | struct bh1750_data *data = | |
289 | iio_priv(i2c_get_clientdata(to_i2c_client(dev))); | |
290 | ||
291 | /* | |
292 | * This is mainly for BH1721 which doesn't enter power down | |
293 | * mode automatically. | |
294 | */ | |
295 | mutex_lock(&data->lock); | |
296 | ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN); | |
297 | mutex_unlock(&data->lock); | |
298 | ||
299 | return ret; | |
300 | } | |
301 | ||
302 | static SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL); | |
303 | #define BH1750_PM_OPS (&bh1750_pm_ops) | |
304 | #else | |
305 | #define BH1750_PM_OPS NULL | |
306 | #endif | |
307 | ||
308 | static const struct i2c_device_id bh1750_id[] = { | |
309 | { "bh1710", BH1710 }, | |
310 | { "bh1715", BH1750 }, | |
311 | { "bh1721", BH1721 }, | |
312 | { "bh1750", BH1750 }, | |
313 | { "bh1751", BH1750 }, | |
314 | { } | |
315 | }; | |
316 | MODULE_DEVICE_TABLE(i2c, bh1750_id); | |
317 | ||
318 | static struct i2c_driver bh1750_driver = { | |
319 | .driver = { | |
320 | .name = "bh1750", | |
3a11fbb0 TD |
321 | .pm = BH1750_PM_OPS, |
322 | }, | |
323 | .probe = bh1750_probe, | |
324 | .remove = bh1750_remove, | |
325 | .id_table = bh1750_id, | |
326 | ||
327 | }; | |
328 | module_i2c_driver(bh1750_driver); | |
329 | ||
330 | MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); | |
331 | MODULE_DESCRIPTION("ROHM BH1710/BH1715/BH1721/BH1750/BH1751 als driver"); | |
332 | MODULE_LICENSE("GPL v2"); |