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