xref: /linux/drivers/iio/light/al3000a.c (revision c26f4fbd58375bd6ef74f95eb73d61762ad97c59)
1d531b9f7SSvyatoslav Ryhel // SPDX-License-Identifier: GPL-2.0-only
2d531b9f7SSvyatoslav Ryhel #include <linux/array_size.h>
3d531b9f7SSvyatoslav Ryhel #include <linux/bitfield.h>
4d531b9f7SSvyatoslav Ryhel #include <linux/device.h>
5d531b9f7SSvyatoslav Ryhel #include <linux/err.h>
6d531b9f7SSvyatoslav Ryhel #include <linux/i2c.h>
7d531b9f7SSvyatoslav Ryhel #include <linux/mod_devicetable.h>
8d531b9f7SSvyatoslav Ryhel #include <linux/module.h>
9d531b9f7SSvyatoslav Ryhel #include <linux/pm.h>
10d531b9f7SSvyatoslav Ryhel #include <linux/regmap.h>
11d531b9f7SSvyatoslav Ryhel #include <linux/regulator/consumer.h>
12d531b9f7SSvyatoslav Ryhel #include <linux/types.h>
13d531b9f7SSvyatoslav Ryhel 
14d531b9f7SSvyatoslav Ryhel #include <linux/iio/iio.h>
15d531b9f7SSvyatoslav Ryhel 
16d531b9f7SSvyatoslav Ryhel #define AL3000A_REG_SYSTEM		0x00
17d531b9f7SSvyatoslav Ryhel #define AL3000A_REG_DATA		0x05
18d531b9f7SSvyatoslav Ryhel 
19d531b9f7SSvyatoslav Ryhel #define AL3000A_CONFIG_ENABLE		0x00
20d531b9f7SSvyatoslav Ryhel #define AL3000A_CONFIG_DISABLE		0x0b
21d531b9f7SSvyatoslav Ryhel #define AL3000A_CONFIG_RESET		0x0f
22d531b9f7SSvyatoslav Ryhel #define AL3000A_GAIN_MASK		GENMASK(5, 0)
23d531b9f7SSvyatoslav Ryhel 
24d531b9f7SSvyatoslav Ryhel /*
25d531b9f7SSvyatoslav Ryhel  * These are pre-calculated lux values based on possible output of sensor
26d531b9f7SSvyatoslav Ryhel  * (range 0x00 - 0x3F)
27d531b9f7SSvyatoslav Ryhel  */
28d531b9f7SSvyatoslav Ryhel static const u32 lux_table[] = {
29d531b9f7SSvyatoslav Ryhel 	1, 1, 1, 2, 2, 2, 3, 4,					/* 0 - 7 */
30d531b9f7SSvyatoslav Ryhel 	4, 5, 6, 7, 9, 11, 13, 16,				/* 8 - 15 */
31d531b9f7SSvyatoslav Ryhel 	19, 22, 27, 32, 39, 46, 56, 67,				/* 16 - 23 */
32d531b9f7SSvyatoslav Ryhel 	80, 96, 116, 139, 167, 200, 240, 289,			/* 24 - 31 */
33d531b9f7SSvyatoslav Ryhel 	347, 416, 499, 600, 720, 864, 1037, 1245,		/* 32 - 39 */
34d531b9f7SSvyatoslav Ryhel 	1495, 1795, 2155, 2587, 3105, 3728, 4475, 5373,		/* 40 - 47 */
35d531b9f7SSvyatoslav Ryhel 	6450, 7743, 9296, 11160, 13397, 16084, 19309, 23180,	/* 48 - 55 */
36d531b9f7SSvyatoslav Ryhel 	27828, 33408, 40107, 48148, 57803, 69393, 83306, 100000 /* 56 - 63 */
37d531b9f7SSvyatoslav Ryhel };
38d531b9f7SSvyatoslav Ryhel 
39d531b9f7SSvyatoslav Ryhel static const struct regmap_config al3000a_regmap_config = {
40d531b9f7SSvyatoslav Ryhel 	.reg_bits = 8,
41d531b9f7SSvyatoslav Ryhel 	.val_bits = 8,
42d531b9f7SSvyatoslav Ryhel 	.max_register = AL3000A_REG_DATA,
43d531b9f7SSvyatoslav Ryhel };
44d531b9f7SSvyatoslav Ryhel 
45d531b9f7SSvyatoslav Ryhel struct al3000a_data {
46d531b9f7SSvyatoslav Ryhel 	struct regmap *regmap;
47d531b9f7SSvyatoslav Ryhel 	struct regulator *vdd_supply;
48d531b9f7SSvyatoslav Ryhel };
49d531b9f7SSvyatoslav Ryhel 
50d531b9f7SSvyatoslav Ryhel static const struct iio_chan_spec al3000a_channels[] = {
51d531b9f7SSvyatoslav Ryhel 	{
52d531b9f7SSvyatoslav Ryhel 		.type = IIO_LIGHT,
53d531b9f7SSvyatoslav Ryhel 		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
54d531b9f7SSvyatoslav Ryhel 	},
55d531b9f7SSvyatoslav Ryhel };
56d531b9f7SSvyatoslav Ryhel 
al3000a_set_pwr_on(struct al3000a_data * data)57d531b9f7SSvyatoslav Ryhel static int al3000a_set_pwr_on(struct al3000a_data *data)
58d531b9f7SSvyatoslav Ryhel {
59d531b9f7SSvyatoslav Ryhel 	struct device *dev = regmap_get_device(data->regmap);
60d531b9f7SSvyatoslav Ryhel 	int ret;
61d531b9f7SSvyatoslav Ryhel 
62d531b9f7SSvyatoslav Ryhel 	ret = regulator_enable(data->vdd_supply);
63d531b9f7SSvyatoslav Ryhel 	if (ret) {
64d531b9f7SSvyatoslav Ryhel 		dev_err(dev, "failed to enable vdd power supply\n");
65d531b9f7SSvyatoslav Ryhel 		return ret;
66d531b9f7SSvyatoslav Ryhel 	}
67d531b9f7SSvyatoslav Ryhel 
68d531b9f7SSvyatoslav Ryhel 	return regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_ENABLE);
69d531b9f7SSvyatoslav Ryhel }
70d531b9f7SSvyatoslav Ryhel 
al3000a_set_pwr_off(void * _data)71d531b9f7SSvyatoslav Ryhel static void al3000a_set_pwr_off(void *_data)
72d531b9f7SSvyatoslav Ryhel {
73d531b9f7SSvyatoslav Ryhel 	struct al3000a_data *data = _data;
74d531b9f7SSvyatoslav Ryhel 	struct device *dev = regmap_get_device(data->regmap);
75d531b9f7SSvyatoslav Ryhel 	int ret;
76d531b9f7SSvyatoslav Ryhel 
77d531b9f7SSvyatoslav Ryhel 	ret = regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_DISABLE);
78d531b9f7SSvyatoslav Ryhel 	if (ret)
79d531b9f7SSvyatoslav Ryhel 		dev_err(dev, "failed to write system register\n");
80d531b9f7SSvyatoslav Ryhel 
81d531b9f7SSvyatoslav Ryhel 	ret = regulator_disable(data->vdd_supply);
82d531b9f7SSvyatoslav Ryhel 	if (ret)
83d531b9f7SSvyatoslav Ryhel 		dev_err(dev, "failed to disable vdd power supply\n");
84d531b9f7SSvyatoslav Ryhel }
85d531b9f7SSvyatoslav Ryhel 
al3000a_init(struct al3000a_data * data)86d531b9f7SSvyatoslav Ryhel static int al3000a_init(struct al3000a_data *data)
87d531b9f7SSvyatoslav Ryhel {
88c0461f8eSDavid Heidelberg 	struct device *dev = regmap_get_device(data->regmap);
89d531b9f7SSvyatoslav Ryhel 	int ret;
90d531b9f7SSvyatoslav Ryhel 
91d531b9f7SSvyatoslav Ryhel 	ret = al3000a_set_pwr_on(data);
92d531b9f7SSvyatoslav Ryhel 	if (ret)
93d531b9f7SSvyatoslav Ryhel 		return ret;
94d531b9f7SSvyatoslav Ryhel 
95c0461f8eSDavid Heidelberg 	ret = devm_add_action_or_reset(dev, al3000a_set_pwr_off, data);
96c0461f8eSDavid Heidelberg 	if (ret)
97c0461f8eSDavid Heidelberg 		return dev_err_probe(dev, ret, "failed to add action\n");
98c0461f8eSDavid Heidelberg 
99d531b9f7SSvyatoslav Ryhel 	ret = regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_RESET);
100d531b9f7SSvyatoslav Ryhel 	if (ret)
101d531b9f7SSvyatoslav Ryhel 		return ret;
102d531b9f7SSvyatoslav Ryhel 
103d531b9f7SSvyatoslav Ryhel 	return regmap_write(data->regmap, AL3000A_REG_SYSTEM, AL3000A_CONFIG_ENABLE);
104d531b9f7SSvyatoslav Ryhel }
105d531b9f7SSvyatoslav Ryhel 
al3000a_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)106d531b9f7SSvyatoslav Ryhel static int al3000a_read_raw(struct iio_dev *indio_dev,
107d531b9f7SSvyatoslav Ryhel 			    struct iio_chan_spec const *chan, int *val,
108d531b9f7SSvyatoslav Ryhel 			    int *val2, long mask)
109d531b9f7SSvyatoslav Ryhel {
110d531b9f7SSvyatoslav Ryhel 	struct al3000a_data *data = iio_priv(indio_dev);
111d531b9f7SSvyatoslav Ryhel 	int ret, gain;
112d531b9f7SSvyatoslav Ryhel 
113d531b9f7SSvyatoslav Ryhel 	switch (mask) {
114d531b9f7SSvyatoslav Ryhel 	case IIO_CHAN_INFO_PROCESSED:
115d531b9f7SSvyatoslav Ryhel 		ret = regmap_read(data->regmap, AL3000A_REG_DATA, &gain);
116d531b9f7SSvyatoslav Ryhel 		if (ret)
117d531b9f7SSvyatoslav Ryhel 			return ret;
118d531b9f7SSvyatoslav Ryhel 
119d531b9f7SSvyatoslav Ryhel 		*val = lux_table[gain & AL3000A_GAIN_MASK];
120d531b9f7SSvyatoslav Ryhel 
121d531b9f7SSvyatoslav Ryhel 		return IIO_VAL_INT;
122d531b9f7SSvyatoslav Ryhel 	default:
123d531b9f7SSvyatoslav Ryhel 		return -EINVAL;
124d531b9f7SSvyatoslav Ryhel 	}
125d531b9f7SSvyatoslav Ryhel }
126d531b9f7SSvyatoslav Ryhel 
127d531b9f7SSvyatoslav Ryhel static const struct iio_info al3000a_info = {
128d531b9f7SSvyatoslav Ryhel 	.read_raw = al3000a_read_raw,
129d531b9f7SSvyatoslav Ryhel };
130d531b9f7SSvyatoslav Ryhel 
al3000a_probe(struct i2c_client * client)131d531b9f7SSvyatoslav Ryhel static int al3000a_probe(struct i2c_client *client)
132d531b9f7SSvyatoslav Ryhel {
133d531b9f7SSvyatoslav Ryhel 	struct al3000a_data *data;
134d531b9f7SSvyatoslav Ryhel 	struct device *dev = &client->dev;
135d531b9f7SSvyatoslav Ryhel 	struct iio_dev *indio_dev;
136d531b9f7SSvyatoslav Ryhel 	int ret;
137d531b9f7SSvyatoslav Ryhel 
138d531b9f7SSvyatoslav Ryhel 	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
139d531b9f7SSvyatoslav Ryhel 	if (!indio_dev)
140d531b9f7SSvyatoslav Ryhel 		return -ENOMEM;
141d531b9f7SSvyatoslav Ryhel 
142d531b9f7SSvyatoslav Ryhel 	data = iio_priv(indio_dev);
143d531b9f7SSvyatoslav Ryhel 	i2c_set_clientdata(client, indio_dev);
144d531b9f7SSvyatoslav Ryhel 
145d531b9f7SSvyatoslav Ryhel 	data->regmap = devm_regmap_init_i2c(client, &al3000a_regmap_config);
146d531b9f7SSvyatoslav Ryhel 	if (IS_ERR(data->regmap))
147d531b9f7SSvyatoslav Ryhel 		return dev_err_probe(dev, PTR_ERR(data->regmap),
148d531b9f7SSvyatoslav Ryhel 				     "cannot allocate regmap\n");
149d531b9f7SSvyatoslav Ryhel 
150d531b9f7SSvyatoslav Ryhel 	data->vdd_supply = devm_regulator_get(dev, "vdd");
151d531b9f7SSvyatoslav Ryhel 	if (IS_ERR(data->vdd_supply))
152d531b9f7SSvyatoslav Ryhel 		return dev_err_probe(dev, PTR_ERR(data->vdd_supply),
153d531b9f7SSvyatoslav Ryhel 				     "failed to get vdd regulator\n");
154d531b9f7SSvyatoslav Ryhel 
155d531b9f7SSvyatoslav Ryhel 	indio_dev->info = &al3000a_info;
156d531b9f7SSvyatoslav Ryhel 	indio_dev->name = "al3000a";
157d531b9f7SSvyatoslav Ryhel 	indio_dev->channels = al3000a_channels;
158d531b9f7SSvyatoslav Ryhel 	indio_dev->num_channels = ARRAY_SIZE(al3000a_channels);
159d531b9f7SSvyatoslav Ryhel 	indio_dev->modes = INDIO_DIRECT_MODE;
160d531b9f7SSvyatoslav Ryhel 
161d531b9f7SSvyatoslav Ryhel 	ret = al3000a_init(data);
162d531b9f7SSvyatoslav Ryhel 	if (ret)
163d531b9f7SSvyatoslav Ryhel 		return dev_err_probe(dev, ret, "failed to init ALS\n");
164d531b9f7SSvyatoslav Ryhel 
165d531b9f7SSvyatoslav Ryhel 	return devm_iio_device_register(dev, indio_dev);
166d531b9f7SSvyatoslav Ryhel }
167d531b9f7SSvyatoslav Ryhel 
al3000a_suspend(struct device * dev)168d531b9f7SSvyatoslav Ryhel static int al3000a_suspend(struct device *dev)
169d531b9f7SSvyatoslav Ryhel {
170d531b9f7SSvyatoslav Ryhel 	struct al3000a_data *data = iio_priv(dev_get_drvdata(dev));
171d531b9f7SSvyatoslav Ryhel 
172d531b9f7SSvyatoslav Ryhel 	al3000a_set_pwr_off(data);
173d531b9f7SSvyatoslav Ryhel 	return 0;
174d531b9f7SSvyatoslav Ryhel }
175d531b9f7SSvyatoslav Ryhel 
al3000a_resume(struct device * dev)176d531b9f7SSvyatoslav Ryhel static int al3000a_resume(struct device *dev)
177d531b9f7SSvyatoslav Ryhel {
178d531b9f7SSvyatoslav Ryhel 	struct al3000a_data *data = iio_priv(dev_get_drvdata(dev));
179d531b9f7SSvyatoslav Ryhel 
180d531b9f7SSvyatoslav Ryhel 	return al3000a_set_pwr_on(data);
181d531b9f7SSvyatoslav Ryhel }
182d531b9f7SSvyatoslav Ryhel 
183d531b9f7SSvyatoslav Ryhel static DEFINE_SIMPLE_DEV_PM_OPS(al3000a_pm_ops, al3000a_suspend, al3000a_resume);
184d531b9f7SSvyatoslav Ryhel 
185d531b9f7SSvyatoslav Ryhel static const struct i2c_device_id al3000a_id[] = {
186d531b9f7SSvyatoslav Ryhel 	{ "al3000a" },
187d531b9f7SSvyatoslav Ryhel 	{ }
188d531b9f7SSvyatoslav Ryhel };
189d531b9f7SSvyatoslav Ryhel MODULE_DEVICE_TABLE(i2c, al3000a_id);
190d531b9f7SSvyatoslav Ryhel 
191d531b9f7SSvyatoslav Ryhel static const struct of_device_id al3000a_of_match[] = {
192d531b9f7SSvyatoslav Ryhel 	{ .compatible = "dynaimage,al3000a" },
193*70788d26SDavid Lechner 	{ }
194d531b9f7SSvyatoslav Ryhel };
195d531b9f7SSvyatoslav Ryhel MODULE_DEVICE_TABLE(of, al3000a_of_match);
196d531b9f7SSvyatoslav Ryhel 
197d531b9f7SSvyatoslav Ryhel static struct i2c_driver al3000a_driver = {
198d531b9f7SSvyatoslav Ryhel 	.driver = {
199d531b9f7SSvyatoslav Ryhel 		.name = "al3000a",
200d531b9f7SSvyatoslav Ryhel 		.of_match_table = al3000a_of_match,
201d531b9f7SSvyatoslav Ryhel 		.pm = pm_sleep_ptr(&al3000a_pm_ops),
202d531b9f7SSvyatoslav Ryhel 	},
203d531b9f7SSvyatoslav Ryhel 	.probe = al3000a_probe,
204d531b9f7SSvyatoslav Ryhel 	.id_table = al3000a_id,
205d531b9f7SSvyatoslav Ryhel };
206d531b9f7SSvyatoslav Ryhel module_i2c_driver(al3000a_driver);
207d531b9f7SSvyatoslav Ryhel 
208d531b9f7SSvyatoslav Ryhel MODULE_AUTHOR("Svyatolsav Ryhel <clamor95@gmail.com>");
209d531b9f7SSvyatoslav Ryhel MODULE_DESCRIPTION("al3000a Ambient Light Sensor driver");
210d531b9f7SSvyatoslav Ryhel MODULE_LICENSE("GPL");
211