Commit | Line | Data |
---|---|---|
53d3ae20 | 1 | // SPDX-License-Identifier: GPL-2.0 |
3a11fbb0 TD |
2 | /* |
3 | * ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensor driver | |
4 | * | |
5 | * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> | |
6 | * | |
3a11fbb0 TD |
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 bh1750_chip_info_tbl[] = { | |
65 | [BH1710] = { 140, 1022, 300, 400, 250000000, 2, 0x001F, 0x03E0 }, | |
66 | [BH1721] = { 140, 1020, 300, 400, 250000000, 2, 0x0010, 0x03E0 }, | |
67 | [BH1750] = { 31, 254, 69, 1740, 57500000, 1, 0x001F, 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(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(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(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(data->client, BH1750_ONE_TIME_H_RES_MODE); | |
117 | if (ret < 0) | |
118 | return ret; | |
119 | ||
120 | usleep_range(delay + 15000, delay + 40000); | |
121 | ||
122 | ret = i2c_master_recv(data->client, (char *)&result, 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(&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, val2); | |
181 | mutex_unlock(&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(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 + len, PAGE_SIZE - len, "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 | ||
c2869498 | 212 | static const struct attribute_group bh1750_attribute_group = { |
3a11fbb0 TD |
213 | .attrs = bh1750_attributes, |
214 | }; | |
215 | ||
216 | static const struct iio_info bh1750_info = { | |
3a11fbb0 TD |
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 | const struct i2c_device_id *id) | |
233 | { | |
234 | int ret, usec; | |
235 | struct bh1750_data *data; | |
236 | struct iio_dev *indio_dev; | |
237 | ||
238 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | | |
239 | I2C_FUNC_SMBUS_WRITE_BYTE)) | |
f8d9d3b4 | 240 | return -EOPNOTSUPP; |
3a11fbb0 TD |
241 | |
242 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | |
243 | if (!indio_dev) | |
244 | return -ENOMEM; | |
245 | ||
246 | data = iio_priv(indio_dev); | |
247 | i2c_set_clientdata(client, 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->dev.parent = &client->dev; | |
258 | indio_dev->info = &bh1750_info; | |
259 | indio_dev->name = id->name; | |
260 | indio_dev->channels = bh1750_channels; | |
261 | indio_dev->num_channels = ARRAY_SIZE(bh1750_channels); | |
262 | indio_dev->modes = INDIO_DIRECT_MODE; | |
263 | ||
264 | return iio_device_register(indio_dev); | |
265 | } | |
266 | ||
267 | static int bh1750_remove(struct i2c_client *client) | |
268 | { | |
269 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | |
270 | struct bh1750_data *data = iio_priv(indio_dev); | |
271 | ||
272 | iio_device_unregister(indio_dev); | |
273 | ||
274 | mutex_lock(&data->lock); | |
275 | i2c_smbus_write_byte(client, BH1750_POWER_DOWN); | |
276 | mutex_unlock(&data->lock); | |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
daae7861 | 281 | static int __maybe_unused bh1750_suspend(struct device *dev) |
3a11fbb0 TD |
282 | { |
283 | int ret; | |
284 | struct bh1750_data *data = | |
285 | iio_priv(i2c_get_clientdata(to_i2c_client(dev))); | |
286 | ||
287 | /* | |
288 | * This is mainly for BH1721 which doesn't enter power down | |
289 | * mode automatically. | |
290 | */ | |
291 | mutex_lock(&data->lock); | |
292 | ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN); | |
293 | mutex_unlock(&data->lock); | |
294 | ||
295 | return ret; | |
296 | } | |
297 | ||
298 | static SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL); | |
3a11fbb0 TD |
299 | |
300 | static const struct i2c_device_id bh1750_id[] = { | |
301 | { "bh1710", BH1710 }, | |
302 | { "bh1715", BH1750 }, | |
303 | { "bh1721", BH1721 }, | |
304 | { "bh1750", BH1750 }, | |
305 | { "bh1751", BH1750 }, | |
306 | { } | |
307 | }; | |
308 | MODULE_DEVICE_TABLE(i2c, bh1750_id); | |
309 | ||
5851b499 | 310 | static const struct of_device_id bh1750_of_match[] = { |
311 | { .compatible = "rohm,bh1710", }, | |
312 | { .compatible = "rohm,bh1715", }, | |
313 | { .compatible = "rohm,bh1721", }, | |
314 | { .compatible = "rohm,bh1750", }, | |
315 | { .compatible = "rohm,bh1751", }, | |
316 | { } | |
317 | }; | |
318 | MODULE_DEVICE_TABLE(of, bh1750_of_match); | |
319 | ||
3a11fbb0 TD |
320 | static struct i2c_driver bh1750_driver = { |
321 | .driver = { | |
322 | .name = "bh1750", | |
5851b499 | 323 | .of_match_table = bh1750_of_match, |
daae7861 | 324 | .pm = &bh1750_pm_ops, |
3a11fbb0 TD |
325 | }, |
326 | .probe = bh1750_probe, | |
327 | .remove = bh1750_remove, | |
328 | .id_table = bh1750_id, | |
329 | ||
330 | }; | |
331 | module_i2c_driver(bh1750_driver); | |
332 | ||
333 | MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); | |
334 | MODULE_DESCRIPTION("ROHM BH1710/BH1715/BH1721/BH1750/BH1751 als driver"); | |
335 | MODULE_LICENSE("GPL v2"); |