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