xref: /openbmc/linux/drivers/iio/light/noa1305.c (revision 7cf15f42)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Support for ON Semiconductor NOA1305 ambient light sensor
4  *
5  * Copyright (C) 2016 Emcraft Systems
6  * Copyright (C) 2019 Collabora Ltd.
7  */
8 
9 #include <linux/delay.h>
10 #include <linux/err.h>
11 #include <linux/i2c.h>
12 #include <linux/iio/iio.h>
13 #include <linux/iio/sysfs.h>
14 #include <linux/module.h>
15 #include <linux/regmap.h>
16 #include <linux/regulator/consumer.h>
17 
18 #define NOA1305_REG_POWER_CONTROL	0x0
19 #define   NOA1305_POWER_CONTROL_DOWN	0x00
20 #define   NOA1305_POWER_CONTROL_ON	0x08
21 #define NOA1305_REG_RESET		0x1
22 #define   NOA1305_RESET_RESET		0x10
23 #define NOA1305_REG_INTEGRATION_TIME	0x2
24 #define   NOA1305_INTEGR_TIME_800MS	0x00
25 #define   NOA1305_INTEGR_TIME_400MS	0x01
26 #define   NOA1305_INTEGR_TIME_200MS	0x02
27 #define   NOA1305_INTEGR_TIME_100MS	0x03
28 #define   NOA1305_INTEGR_TIME_50MS	0x04
29 #define   NOA1305_INTEGR_TIME_25MS	0x05
30 #define   NOA1305_INTEGR_TIME_12_5MS	0x06
31 #define   NOA1305_INTEGR_TIME_6_25MS	0x07
32 #define NOA1305_REG_INT_SELECT		0x3
33 #define   NOA1305_INT_SEL_ACTIVE_HIGH	0x01
34 #define   NOA1305_INT_SEL_ACTIVE_LOW	0x02
35 #define   NOA1305_INT_SEL_INACTIVE	0x03
36 #define NOA1305_REG_INT_THRESH_LSB	0x4
37 #define NOA1305_REG_INT_THRESH_MSB	0x5
38 #define NOA1305_REG_ALS_DATA_LSB	0x6
39 #define NOA1305_REG_ALS_DATA_MSB	0x7
40 #define NOA1305_REG_DEVICE_ID_LSB	0x8
41 #define NOA1305_REG_DEVICE_ID_MSB	0x9
42 
43 #define NOA1305_DEVICE_ID	0x0519
44 #define NOA1305_DRIVER_NAME	"noa1305"
45 
46 struct noa1305_priv {
47 	struct i2c_client *client;
48 	struct regmap *regmap;
49 };
50 
noa1305_measure(struct noa1305_priv * priv)51 static int noa1305_measure(struct noa1305_priv *priv)
52 {
53 	__le16 data;
54 	int ret;
55 
56 	ret = regmap_bulk_read(priv->regmap, NOA1305_REG_ALS_DATA_LSB, &data,
57 			       2);
58 	if (ret < 0)
59 		return ret;
60 
61 	return le16_to_cpu(data);
62 }
63 
noa1305_scale(struct noa1305_priv * priv,int * val,int * val2)64 static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2)
65 {
66 	int data;
67 	int ret;
68 
69 	ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);
70 	if (ret < 0)
71 		return ret;
72 
73 	/*
74 	 * Lux = count / (<Integration Constant> * <Integration Time>)
75 	 *
76 	 * Integration Constant = 7.7
77 	 * Integration Time in Seconds
78 	 */
79 	switch (data) {
80 	case NOA1305_INTEGR_TIME_800MS:
81 		*val = 100;
82 		*val2 = 77 * 8;
83 		break;
84 	case NOA1305_INTEGR_TIME_400MS:
85 		*val = 100;
86 		*val2 = 77 * 4;
87 		break;
88 	case NOA1305_INTEGR_TIME_200MS:
89 		*val = 100;
90 		*val2 = 77 * 2;
91 		break;
92 	case NOA1305_INTEGR_TIME_100MS:
93 		*val = 100;
94 		*val2 = 77;
95 		break;
96 	case NOA1305_INTEGR_TIME_50MS:
97 		*val = 1000;
98 		*val2 = 77 * 5;
99 		break;
100 	case NOA1305_INTEGR_TIME_25MS:
101 		*val = 10000;
102 		*val2 = 77 * 25;
103 		break;
104 	case NOA1305_INTEGR_TIME_12_5MS:
105 		*val = 100000;
106 		*val2 = 77 * 125;
107 		break;
108 	case NOA1305_INTEGR_TIME_6_25MS:
109 		*val = 1000000;
110 		*val2 = 77 * 625;
111 		break;
112 	default:
113 		return -EINVAL;
114 	}
115 
116 	return IIO_VAL_FRACTIONAL;
117 }
118 
119 static const struct iio_chan_spec noa1305_channels[] = {
120 	{
121 		.type = IIO_LIGHT,
122 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
123 		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
124 	}
125 };
126 
noa1305_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)127 static int noa1305_read_raw(struct iio_dev *indio_dev,
128 				struct iio_chan_spec const *chan,
129 				int *val, int *val2, long mask)
130 {
131 	int ret = -EINVAL;
132 	struct noa1305_priv *priv = iio_priv(indio_dev);
133 
134 	switch (mask) {
135 	case IIO_CHAN_INFO_RAW:
136 		switch (chan->type) {
137 		case IIO_LIGHT:
138 			ret = noa1305_measure(priv);
139 			if (ret < 0)
140 				return ret;
141 			*val = ret;
142 			return IIO_VAL_INT;
143 		default:
144 			break;
145 		}
146 		break;
147 	case IIO_CHAN_INFO_SCALE:
148 		switch (chan->type) {
149 		case IIO_LIGHT:
150 			return noa1305_scale(priv, val, val2);
151 		default:
152 			break;
153 		}
154 		break;
155 	default:
156 		break;
157 	}
158 
159 	return ret;
160 }
161 
162 static const struct iio_info noa1305_info = {
163 	.read_raw = noa1305_read_raw,
164 };
165 
noa1305_writable_reg(struct device * dev,unsigned int reg)166 static bool noa1305_writable_reg(struct device *dev, unsigned int reg)
167 {
168 	switch (reg) {
169 	case NOA1305_REG_POWER_CONTROL:
170 	case NOA1305_REG_RESET:
171 	case NOA1305_REG_INTEGRATION_TIME:
172 	case NOA1305_REG_INT_SELECT:
173 	case NOA1305_REG_INT_THRESH_LSB:
174 	case NOA1305_REG_INT_THRESH_MSB:
175 		return true;
176 	default:
177 		return false;
178 	}
179 }
180 
181 static const struct regmap_config noa1305_regmap_config = {
182 	.name = NOA1305_DRIVER_NAME,
183 	.reg_bits = 8,
184 	.val_bits = 8,
185 	.max_register = NOA1305_REG_DEVICE_ID_MSB,
186 	.writeable_reg = noa1305_writable_reg,
187 };
188 
noa1305_probe(struct i2c_client * client)189 static int noa1305_probe(struct i2c_client *client)
190 {
191 	struct noa1305_priv *priv;
192 	struct iio_dev *indio_dev;
193 	struct regmap *regmap;
194 	__le16 data;
195 	unsigned int dev_id;
196 	int ret;
197 
198 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*priv));
199 	if (!indio_dev)
200 		return -ENOMEM;
201 
202 	regmap = devm_regmap_init_i2c(client, &noa1305_regmap_config);
203 	if (IS_ERR(regmap)) {
204 		dev_err(&client->dev, "Regmap initialization failed.\n");
205 		return PTR_ERR(regmap);
206 	}
207 
208 	priv = iio_priv(indio_dev);
209 
210 	ret = devm_regulator_get_enable(&client->dev, "vin");
211 	if (ret)
212 		return dev_err_probe(&client->dev, ret,
213 				     "get regulator vin failed\n");
214 
215 	i2c_set_clientdata(client, indio_dev);
216 	priv->client = client;
217 	priv->regmap = regmap;
218 
219 	ret = regmap_bulk_read(regmap, NOA1305_REG_DEVICE_ID_LSB, &data, 2);
220 	if (ret < 0) {
221 		dev_err(&client->dev, "ID reading failed: %d\n", ret);
222 		return ret;
223 	}
224 
225 	dev_id = le16_to_cpu(data);
226 	if (dev_id != NOA1305_DEVICE_ID) {
227 		dev_err(&client->dev, "Unknown device ID: 0x%x\n", dev_id);
228 		return -ENODEV;
229 	}
230 
231 	ret = regmap_write(regmap, NOA1305_REG_POWER_CONTROL,
232 			   NOA1305_POWER_CONTROL_ON);
233 	if (ret < 0) {
234 		dev_err(&client->dev, "Enabling power control failed\n");
235 		return ret;
236 	}
237 
238 	ret = regmap_write(regmap, NOA1305_REG_RESET, NOA1305_RESET_RESET);
239 	if (ret < 0) {
240 		dev_err(&client->dev, "Device reset failed\n");
241 		return ret;
242 	}
243 
244 	ret = regmap_write(regmap, NOA1305_REG_INTEGRATION_TIME,
245 			   NOA1305_INTEGR_TIME_800MS);
246 	if (ret < 0) {
247 		dev_err(&client->dev, "Setting integration time failed\n");
248 		return ret;
249 	}
250 
251 	indio_dev->info = &noa1305_info;
252 	indio_dev->channels = noa1305_channels;
253 	indio_dev->num_channels = ARRAY_SIZE(noa1305_channels);
254 	indio_dev->name = NOA1305_DRIVER_NAME;
255 	indio_dev->modes = INDIO_DIRECT_MODE;
256 
257 	ret = devm_iio_device_register(&client->dev, indio_dev);
258 	if (ret)
259 		dev_err(&client->dev, "registering device failed\n");
260 
261 	return ret;
262 }
263 
264 static const struct of_device_id noa1305_of_match[] = {
265 	{ .compatible = "onnn,noa1305" },
266 	{ }
267 };
268 MODULE_DEVICE_TABLE(of, noa1305_of_match);
269 
270 static const struct i2c_device_id noa1305_ids[] = {
271 	{ "noa1305", 0 },
272 	{ }
273 };
274 MODULE_DEVICE_TABLE(i2c, noa1305_ids);
275 
276 static struct i2c_driver noa1305_driver = {
277 	.driver = {
278 		.name		= NOA1305_DRIVER_NAME,
279 		.of_match_table	= noa1305_of_match,
280 	},
281 	.probe		= noa1305_probe,
282 	.id_table	= noa1305_ids,
283 };
284 
285 module_i2c_driver(noa1305_driver);
286 
287 MODULE_AUTHOR("Sergei Miroshnichenko <sergeimir@emcraft.com>");
288 MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.com");
289 MODULE_DESCRIPTION("ON Semiconductor NOA1305 ambient light sensor");
290 MODULE_LICENSE("GPL");
291