1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ROHM 1780GLI Ambient Light Sensor Driver |
4 | * |
5 | * Copyright (C) 2016 Linaro Ltd. |
6 | * Author: Linus Walleij <linus.walleij@linaro.org> |
7 | * Loosely based on the previous BH1780 ALS misc driver |
8 | * Copyright (C) 2010 Texas Instruments |
9 | * Author: Hemanth V <hemanthv@ti.com> |
10 | */ |
11 | #include <linux/i2c.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/module.h> |
16 | #include <linux/mod_devicetable.h> |
17 | #include <linux/pm_runtime.h> |
18 | #include <linux/iio/iio.h> |
19 | #include <linux/iio/sysfs.h> |
20 | #include <linux/bitops.h> |
21 | |
22 | #define BH1780_CMD_BIT BIT(7) |
23 | #define BH1780_REG_CONTROL 0x00 |
24 | #define BH1780_REG_PARTID 0x0A |
25 | #define BH1780_REG_MANFID 0x0B |
26 | #define BH1780_REG_DLOW 0x0C |
27 | #define BH1780_REG_DHIGH 0x0D |
28 | |
29 | #define BH1780_REVMASK GENMASK(3,0) |
30 | #define BH1780_POWMASK GENMASK(1,0) |
31 | #define BH1780_POFF (0x0) |
32 | #define BH1780_PON (0x3) |
33 | |
34 | /* power on settling time in ms */ |
35 | #define BH1780_PON_DELAY 2 |
36 | /* max time before value available in ms */ |
37 | #define BH1780_INTERVAL 250 |
38 | |
39 | struct bh1780_data { |
40 | struct i2c_client *client; |
41 | }; |
42 | |
43 | static int bh1780_write(struct bh1780_data *bh1780, u8 reg, u8 val) |
44 | { |
45 | int ret = i2c_smbus_write_byte_data(client: bh1780->client, |
46 | BH1780_CMD_BIT | reg, |
47 | value: val); |
48 | if (ret < 0) |
49 | dev_err(&bh1780->client->dev, |
50 | "i2c_smbus_write_byte_data failed error " |
51 | "%d, register %01x\n" , |
52 | ret, reg); |
53 | return ret; |
54 | } |
55 | |
56 | static int bh1780_read(struct bh1780_data *bh1780, u8 reg) |
57 | { |
58 | int ret = i2c_smbus_read_byte_data(client: bh1780->client, |
59 | BH1780_CMD_BIT | reg); |
60 | if (ret < 0) |
61 | dev_err(&bh1780->client->dev, |
62 | "i2c_smbus_read_byte_data failed error " |
63 | "%d, register %01x\n" , |
64 | ret, reg); |
65 | return ret; |
66 | } |
67 | |
68 | static int bh1780_read_word(struct bh1780_data *bh1780, u8 reg) |
69 | { |
70 | int ret = i2c_smbus_read_word_data(client: bh1780->client, |
71 | BH1780_CMD_BIT | reg); |
72 | if (ret < 0) |
73 | dev_err(&bh1780->client->dev, |
74 | "i2c_smbus_read_word_data failed error " |
75 | "%d, register %01x\n" , |
76 | ret, reg); |
77 | return ret; |
78 | } |
79 | |
80 | static int bh1780_debugfs_reg_access(struct iio_dev *indio_dev, |
81 | unsigned int reg, unsigned int writeval, |
82 | unsigned int *readval) |
83 | { |
84 | struct bh1780_data *bh1780 = iio_priv(indio_dev); |
85 | int ret; |
86 | |
87 | if (!readval) |
88 | return bh1780_write(bh1780, reg: (u8)reg, val: (u8)writeval); |
89 | |
90 | ret = bh1780_read(bh1780, reg: (u8)reg); |
91 | if (ret < 0) |
92 | return ret; |
93 | |
94 | *readval = ret; |
95 | |
96 | return 0; |
97 | } |
98 | |
99 | static int bh1780_read_raw(struct iio_dev *indio_dev, |
100 | struct iio_chan_spec const *chan, |
101 | int *val, int *val2, long mask) |
102 | { |
103 | struct bh1780_data *bh1780 = iio_priv(indio_dev); |
104 | int value; |
105 | |
106 | switch (mask) { |
107 | case IIO_CHAN_INFO_RAW: |
108 | switch (chan->type) { |
109 | case IIO_LIGHT: |
110 | pm_runtime_get_sync(dev: &bh1780->client->dev); |
111 | value = bh1780_read_word(bh1780, BH1780_REG_DLOW); |
112 | if (value < 0) |
113 | return value; |
114 | pm_runtime_mark_last_busy(dev: &bh1780->client->dev); |
115 | pm_runtime_put_autosuspend(dev: &bh1780->client->dev); |
116 | *val = value; |
117 | |
118 | return IIO_VAL_INT; |
119 | default: |
120 | return -EINVAL; |
121 | } |
122 | case IIO_CHAN_INFO_INT_TIME: |
123 | *val = 0; |
124 | *val2 = BH1780_INTERVAL * 1000; |
125 | return IIO_VAL_INT_PLUS_MICRO; |
126 | default: |
127 | return -EINVAL; |
128 | } |
129 | } |
130 | |
131 | static const struct iio_info bh1780_info = { |
132 | .read_raw = bh1780_read_raw, |
133 | .debugfs_reg_access = bh1780_debugfs_reg_access, |
134 | }; |
135 | |
136 | static const struct iio_chan_spec bh1780_channels[] = { |
137 | { |
138 | .type = IIO_LIGHT, |
139 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
140 | BIT(IIO_CHAN_INFO_INT_TIME) |
141 | } |
142 | }; |
143 | |
144 | static int bh1780_probe(struct i2c_client *client) |
145 | { |
146 | int ret; |
147 | struct bh1780_data *bh1780; |
148 | struct i2c_adapter *adapter = client->adapter; |
149 | struct iio_dev *indio_dev; |
150 | |
151 | if (!i2c_check_functionality(adap: adapter, I2C_FUNC_SMBUS_BYTE)) |
152 | return -EIO; |
153 | |
154 | indio_dev = devm_iio_device_alloc(parent: &client->dev, sizeof_priv: sizeof(*bh1780)); |
155 | if (!indio_dev) |
156 | return -ENOMEM; |
157 | |
158 | bh1780 = iio_priv(indio_dev); |
159 | bh1780->client = client; |
160 | i2c_set_clientdata(client, data: indio_dev); |
161 | |
162 | /* Power up the device */ |
163 | ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_PON); |
164 | if (ret < 0) |
165 | return ret; |
166 | msleep(BH1780_PON_DELAY); |
167 | pm_runtime_get_noresume(dev: &client->dev); |
168 | pm_runtime_set_active(dev: &client->dev); |
169 | pm_runtime_enable(dev: &client->dev); |
170 | |
171 | ret = bh1780_read(bh1780, BH1780_REG_PARTID); |
172 | if (ret < 0) |
173 | goto out_disable_pm; |
174 | dev_info(&client->dev, |
175 | "Ambient Light Sensor, Rev : %lu\n" , |
176 | (ret & BH1780_REVMASK)); |
177 | |
178 | /* |
179 | * As the device takes 250 ms to even come up with a fresh |
180 | * measurement after power-on, do not shut it down unnecessarily. |
181 | * Set autosuspend to a five seconds. |
182 | */ |
183 | pm_runtime_set_autosuspend_delay(dev: &client->dev, delay: 5000); |
184 | pm_runtime_use_autosuspend(dev: &client->dev); |
185 | pm_runtime_put(dev: &client->dev); |
186 | |
187 | indio_dev->info = &bh1780_info; |
188 | indio_dev->name = "bh1780" ; |
189 | indio_dev->channels = bh1780_channels; |
190 | indio_dev->num_channels = ARRAY_SIZE(bh1780_channels); |
191 | indio_dev->modes = INDIO_DIRECT_MODE; |
192 | |
193 | ret = iio_device_register(indio_dev); |
194 | if (ret) |
195 | goto out_disable_pm; |
196 | return 0; |
197 | |
198 | out_disable_pm: |
199 | pm_runtime_put_noidle(dev: &client->dev); |
200 | pm_runtime_disable(dev: &client->dev); |
201 | return ret; |
202 | } |
203 | |
204 | static void bh1780_remove(struct i2c_client *client) |
205 | { |
206 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
207 | struct bh1780_data *bh1780 = iio_priv(indio_dev); |
208 | int ret; |
209 | |
210 | iio_device_unregister(indio_dev); |
211 | pm_runtime_get_sync(dev: &client->dev); |
212 | pm_runtime_put_noidle(dev: &client->dev); |
213 | pm_runtime_disable(dev: &client->dev); |
214 | ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_POFF); |
215 | if (ret < 0) |
216 | dev_err(&client->dev, "failed to power off (%pe)\n" , |
217 | ERR_PTR(ret)); |
218 | } |
219 | |
220 | static int bh1780_runtime_suspend(struct device *dev) |
221 | { |
222 | struct i2c_client *client = to_i2c_client(dev); |
223 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
224 | struct bh1780_data *bh1780 = iio_priv(indio_dev); |
225 | int ret; |
226 | |
227 | ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_POFF); |
228 | if (ret < 0) { |
229 | dev_err(dev, "failed to runtime suspend\n" ); |
230 | return ret; |
231 | } |
232 | |
233 | return 0; |
234 | } |
235 | |
236 | static int bh1780_runtime_resume(struct device *dev) |
237 | { |
238 | struct i2c_client *client = to_i2c_client(dev); |
239 | struct iio_dev *indio_dev = i2c_get_clientdata(client); |
240 | struct bh1780_data *bh1780 = iio_priv(indio_dev); |
241 | int ret; |
242 | |
243 | ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_PON); |
244 | if (ret < 0) { |
245 | dev_err(dev, "failed to runtime resume\n" ); |
246 | return ret; |
247 | } |
248 | |
249 | /* Wait for power on, then for a value to be available */ |
250 | msleep(BH1780_PON_DELAY + BH1780_INTERVAL); |
251 | |
252 | return 0; |
253 | } |
254 | |
255 | static DEFINE_RUNTIME_DEV_PM_OPS(bh1780_dev_pm_ops, bh1780_runtime_suspend, |
256 | bh1780_runtime_resume, NULL); |
257 | |
258 | static const struct i2c_device_id bh1780_id[] = { |
259 | { "bh1780" , 0 }, |
260 | { }, |
261 | }; |
262 | |
263 | MODULE_DEVICE_TABLE(i2c, bh1780_id); |
264 | |
265 | static const struct of_device_id of_bh1780_match[] = { |
266 | { .compatible = "rohm,bh1780gli" , }, |
267 | {}, |
268 | }; |
269 | MODULE_DEVICE_TABLE(of, of_bh1780_match); |
270 | |
271 | static struct i2c_driver bh1780_driver = { |
272 | .probe = bh1780_probe, |
273 | .remove = bh1780_remove, |
274 | .id_table = bh1780_id, |
275 | .driver = { |
276 | .name = "bh1780" , |
277 | .pm = pm_ptr(&bh1780_dev_pm_ops), |
278 | .of_match_table = of_bh1780_match, |
279 | }, |
280 | }; |
281 | |
282 | module_i2c_driver(bh1780_driver); |
283 | |
284 | MODULE_DESCRIPTION("ROHM BH1780GLI Ambient Light Sensor Driver" ); |
285 | MODULE_LICENSE("GPL" ); |
286 | MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>" ); |
287 | |