File Index
Files and directories
Sign in
FILE

noa1305.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iio/light/noa1305.c C
  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_INTEGR_TIME_MASK	0x07
 33#define NOA1305_REG_INT_SELECT		0x3
 34#define   NOA1305_INT_SEL_ACTIVE_HIGH	0x01
 35#define   NOA1305_INT_SEL_ACTIVE_LOW	0x02
 36#define   NOA1305_INT_SEL_INACTIVE	0x03
 37#define NOA1305_REG_INT_THRESH_LSB	0x4
 38#define NOA1305_REG_INT_THRESH_MSB	0x5
 39#define NOA1305_REG_ALS_DATA_LSB	0x6
 40#define NOA1305_REG_ALS_DATA_MSB	0x7
 41#define NOA1305_REG_DEVICE_ID_LSB	0x8
 42#define NOA1305_REG_DEVICE_ID_MSB	0x9
 43
 44#define NOA1305_DEVICE_ID	0x0519
 45#define NOA1305_DRIVER_NAME	"noa1305"
 46
 47static int noa1305_scale_available[] = {
 48	100, 8 * 77,		/* 800 ms */
 49	100, 4 * 77,		/* 400 ms */
 50	100, 2 * 77,		/* 200 ms */
 51	100, 1 * 77,		/* 100 ms */
 52	1000, 5 * 77,		/* 50 ms */
 53	10000, 25 * 77,		/* 25 ms */
 54	100000, 125 * 77,	/* 12.5 ms */
 55	1000000, 625 * 77,	/* 6.25 ms */
 56};
 57
 58static int noa1305_int_time_available[] = {
 59	0, 800000,		/* 800 ms */
 60	0, 400000,		/* 400 ms */
 61	0, 200000,		/* 200 ms */
 62	0, 100000,		/* 100 ms */
 63	0, 50000,		/* 50 ms */
 64	0, 25000,		/* 25 ms */
 65	0, 12500,		/* 12.5 ms */
 66	0, 6250,		/* 6.25 ms */
 67};
 68
 69struct noa1305_priv {
 70	struct i2c_client *client;
 71	struct regmap *regmap;
 72};
 73
 74static int noa1305_measure(struct noa1305_priv *priv, int *val)
 75{
 76	__le16 data;
 77	int ret;
 78
 79	ret = regmap_bulk_read(priv->regmap, NOA1305_REG_ALS_DATA_LSB, &data,
 80			       2);
 81	if (ret < 0)
 82		return ret;
 83
 84	*val = le16_to_cpu(data);
 85
 86	return IIO_VAL_INT;
 87}
 88
 89static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2)
 90{
 91	int data;
 92	int ret;
 93
 94	ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);
 95	if (ret < 0)
 96		return ret;
 97
 98	/*
 99	 * Lux = count / (<Integration Constant> * <Integration Time>)
100	 *
101	 * Integration Constant = 7.7
102	 * Integration Time in Seconds
103	 */
104	data &= NOA1305_INTEGR_TIME_MASK;
105	*val = noa1305_scale_available[2 * data + 0];
106	*val2 = noa1305_scale_available[2 * data + 1];
107
108	return IIO_VAL_FRACTIONAL;
109}
110
111static int noa1305_int_time(struct noa1305_priv *priv, int *val, int *val2)
112{
113	int data;
114	int ret;
115
116	ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);
117	if (ret < 0)
118		return ret;
119
120	data &= NOA1305_INTEGR_TIME_MASK;
121	*val = noa1305_int_time_available[2 * data + 0];
122	*val2 = noa1305_int_time_available[2 * data + 1];
123
124	return IIO_VAL_INT_PLUS_MICRO;
125}
126
127static const struct iio_chan_spec noa1305_channels[] = {
128	{
129		.type = IIO_LIGHT,
130		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
131		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
132		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
133		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
134		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
135	}
136};
137
138static int noa1305_read_avail(struct iio_dev *indio_dev,
139			      struct iio_chan_spec const *chan,
140			      const int **vals, int *type,
141			      int *length, long mask)
142{
143	if (chan->type != IIO_LIGHT)
144		return -EINVAL;
145
146	switch (mask) {
147	case IIO_CHAN_INFO_SCALE:
148		*vals = noa1305_scale_available;
149		*length = ARRAY_SIZE(noa1305_scale_available);
150		*type = IIO_VAL_FRACTIONAL;
151		return IIO_AVAIL_LIST;
152	case IIO_CHAN_INFO_INT_TIME:
153		*vals = noa1305_int_time_available;
154		*length = ARRAY_SIZE(noa1305_int_time_available);
155		*type = IIO_VAL_INT_PLUS_MICRO;
156		return IIO_AVAIL_LIST;
157	default:
158		return -EINVAL;
159	}
160}
161
162static int noa1305_read_raw(struct iio_dev *indio_dev,
163			    struct iio_chan_spec const *chan,
164			    int *val, int *val2, long mask)
165{
166	struct noa1305_priv *priv = iio_priv(indio_dev);
167
168	if (chan->type != IIO_LIGHT)
169		return -EINVAL;
170
171	switch (mask) {
172	case IIO_CHAN_INFO_RAW:
173		return noa1305_measure(priv, val);
174	case IIO_CHAN_INFO_SCALE:
175		return noa1305_scale(priv, val, val2);
176	case IIO_CHAN_INFO_INT_TIME:
177		return noa1305_int_time(priv, val, val2);
178	default:
179		return -EINVAL;
180	}
181}
182
183static int noa1305_write_raw(struct iio_dev *indio_dev,
184			     struct iio_chan_spec const *chan,
185			     int val, int val2, long mask)
186{
187	struct noa1305_priv *priv = iio_priv(indio_dev);
188	int i;
189
190	if (chan->type != IIO_LIGHT)
191		return -EINVAL;
192
193	if (mask != IIO_CHAN_INFO_INT_TIME)
194		return -EINVAL;
195
196	if (val)	/* >= 1s integration time not supported */
197		return -EINVAL;
198
199	/* Look up integration time register settings and write it if found. */
200	for (i = 0; i < ARRAY_SIZE(noa1305_int_time_available) / 2; i++)
201		if (noa1305_int_time_available[2 * i + 1] == val2)
202			return regmap_write(priv->regmap, NOA1305_REG_INTEGRATION_TIME, i);
203
204	return -EINVAL;
205}
206
207static const struct iio_info noa1305_info = {
208	.read_avail = noa1305_read_avail,
209	.read_raw = noa1305_read_raw,
210	.write_raw = noa1305_write_raw,
211};
212
213static bool noa1305_writable_reg(struct device *dev, unsigned int reg)
214{
215	switch (reg) {
216	case NOA1305_REG_POWER_CONTROL:
217	case NOA1305_REG_RESET:
218	case NOA1305_REG_INTEGRATION_TIME:
219	case NOA1305_REG_INT_SELECT:
220	case NOA1305_REG_INT_THRESH_LSB:
221	case NOA1305_REG_INT_THRESH_MSB:
222		return true;
223	default:
224		return false;
225	}
226}
227
228static const struct regmap_config noa1305_regmap_config = {
229	.name = NOA1305_DRIVER_NAME,
230	.reg_bits = 8,
231	.val_bits = 8,
232	.max_register = NOA1305_REG_DEVICE_ID_MSB,
233	.writeable_reg = noa1305_writable_reg,
234};
235
236static int noa1305_probe(struct i2c_client *client)
237{
238	struct noa1305_priv *priv;
239	struct iio_dev *indio_dev;
240	struct regmap *regmap;
241	__le16 data;
242	unsigned int dev_id;
243	int ret;
244
245	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*priv));
246	if (!indio_dev)
247		return -ENOMEM;
248
249	regmap = devm_regmap_init_i2c(client, &noa1305_regmap_config);
250	if (IS_ERR(regmap)) {
251		dev_err(&client->dev, "Regmap initialization failed.\n");
252		return PTR_ERR(regmap);
253	}
254
255	priv = iio_priv(indio_dev);
256
257	ret = devm_regulator_get_enable(&client->dev, "vin");
258	if (ret)
259		return dev_err_probe(&client->dev, ret,
260				     "get regulator vin failed\n");
261
262	i2c_set_clientdata(client, indio_dev);
263	priv->client = client;
264	priv->regmap = regmap;
265
266	ret = regmap_bulk_read(regmap, NOA1305_REG_DEVICE_ID_LSB, &data, 2);
267	if (ret < 0) {
268		dev_err(&client->dev, "ID reading failed: %d\n", ret);
269		return ret;
270	}
271
272	dev_id = le16_to_cpu(data);
273	if (dev_id != NOA1305_DEVICE_ID) {
274		dev_err(&client->dev, "Unknown device ID: 0x%x\n", dev_id);
275		return -ENODEV;
276	}
277
278	ret = regmap_write(regmap, NOA1305_REG_POWER_CONTROL,
279			   NOA1305_POWER_CONTROL_ON);
280	if (ret < 0) {
281		dev_err(&client->dev, "Enabling power control failed\n");
282		return ret;
283	}
284
285	ret = regmap_write(regmap, NOA1305_REG_RESET, NOA1305_RESET_RESET);
286	if (ret < 0) {
287		dev_err(&client->dev, "Device reset failed\n");
288		return ret;
289	}
290
291	ret = regmap_write(regmap, NOA1305_REG_INTEGRATION_TIME,
292			   NOA1305_INTEGR_TIME_800MS);
293	if (ret < 0) {
294		dev_err(&client->dev, "Setting integration time failed\n");
295		return ret;
296	}
297
298	indio_dev->info = &noa1305_info;
299	indio_dev->channels = noa1305_channels;
300	indio_dev->num_channels = ARRAY_SIZE(noa1305_channels);
301	indio_dev->name = NOA1305_DRIVER_NAME;
302	indio_dev->modes = INDIO_DIRECT_MODE;
303
304	ret = devm_iio_device_register(&client->dev, indio_dev);
305	if (ret)
306		dev_err(&client->dev, "registering device failed\n");
307
308	return ret;
309}
310
311static const struct of_device_id noa1305_of_match[] = {
312	{ .compatible = "onnn,noa1305" },
313	{ }
314};
315MODULE_DEVICE_TABLE(of, noa1305_of_match);
316
317static const struct i2c_device_id noa1305_ids[] = {
318	{ "noa1305" },
319	{ }
320};
321MODULE_DEVICE_TABLE(i2c, noa1305_ids);
322
323static struct i2c_driver noa1305_driver = {
324	.driver = {
325		.name		= NOA1305_DRIVER_NAME,
326		.of_match_table	= noa1305_of_match,
327	},
328	.probe		= noa1305_probe,
329	.id_table	= noa1305_ids,
330};
331
332module_i2c_driver(noa1305_driver);
333
334MODULE_AUTHOR("Sergei Miroshnichenko <sergeimir@emcraft.com>");
335MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.com");
336MODULE_DESCRIPTION("ON Semiconductor NOA1305 ambient light sensor");
337MODULE_LICENSE("GPL");
~/ Read-only file preview.