1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * AL3010 - Dyna Image Ambient Light Sensor
4 *
5 * Copyright (c) 2014, Intel Corporation.
6 * Copyright (c) 2016, Dyna-Image Corp.
7 * Copyright (c) 2020, David Heidelberg, Michał Mirosław, Dmitry Osipenko
8 *
9 * IIO driver for AL3010 (7-bit I2C slave address 0x1C).
10 *
11 * TODO: interrupt support, thresholds
12 * When the driver will get support for interrupt handling, then interrupt
13 * will need to be disabled before turning sensor OFF in order to avoid
14 * potential races with the interrupt handling.
15 */
16
17 #include <linux/bitfield.h>
18 #include <linux/i2c.h>
19 #include <linux/module.h>
20 #include <linux/regmap.h>
21
22 #include <linux/iio/iio.h>
23 #include <linux/iio/sysfs.h>
24
25 #define AL3010_REG_SYSTEM 0x00
26 #define AL3010_REG_DATA_LOW 0x0c
27 #define AL3010_REG_CONFIG 0x10
28
29 #define AL3010_CONFIG_DISABLE 0x00
30 #define AL3010_CONFIG_ENABLE 0x01
31
32 #define AL3010_GAIN_MASK GENMASK(6,4)
33
34 #define AL3010_SCALE_AVAILABLE "1.1872 0.2968 0.0742 0.018"
35
36 enum al3xxxx_range {
37 AL3XXX_RANGE_1, /* 77806 lx */
38 AL3XXX_RANGE_2, /* 19542 lx */
39 AL3XXX_RANGE_3, /* 4863 lx */
40 AL3XXX_RANGE_4 /* 1216 lx */
41 };
42
43 static const int al3010_scales[][2] = {
44 { 1, 187200 }, { 0, 296800 }, { 0, 74200 }, { 0, 18600 },
45 };
46
47 static const struct regmap_config al3010_regmap_config = {
48 .reg_bits = 8,
49 .val_bits = 8,
50 .max_register = AL3010_REG_CONFIG,
51 };
52
53 struct al3010_data {
54 struct regmap *regmap;
55 };
56
57 static const struct iio_chan_spec al3010_channels[] = {
58 {
59 .type = IIO_LIGHT,
60 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
61 BIT(IIO_CHAN_INFO_SCALE),
62 }
63 };
64
65 static IIO_CONST_ATTR(in_illuminance_scale_available, AL3010_SCALE_AVAILABLE);
66
67 static struct attribute *al3010_attributes[] = {
68 &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
69 NULL,
70 };
71
72 static const struct attribute_group al3010_attribute_group = {
73 .attrs = al3010_attributes,
74 };
75
al3010_set_pwr_on(struct al3010_data * data)76 static int al3010_set_pwr_on(struct al3010_data *data)
77 {
78 return regmap_write(data->regmap, AL3010_REG_SYSTEM, AL3010_CONFIG_ENABLE);
79 }
80
al3010_set_pwr_off(void * _data)81 static void al3010_set_pwr_off(void *_data)
82 {
83 struct al3010_data *data = _data;
84 struct device *dev = regmap_get_device(data->regmap);
85 int ret;
86
87 ret = regmap_write(data->regmap, AL3010_REG_SYSTEM, AL3010_CONFIG_DISABLE);
88 if (ret)
89 dev_err(dev, "failed to write system register\n");
90 }
91
al3010_init(struct al3010_data * data)92 static int al3010_init(struct al3010_data *data)
93 {
94 struct device *dev = regmap_get_device(data->regmap);
95 int ret;
96
97 ret = al3010_set_pwr_on(data);
98 if (ret)
99 return ret;
100
101 ret = devm_add_action_or_reset(dev, al3010_set_pwr_off, data);
102 if (ret)
103 return ret;
104 return regmap_write(data->regmap, AL3010_REG_CONFIG,
105 FIELD_PREP(AL3010_GAIN_MASK, AL3XXX_RANGE_3));
106 }
107
al3010_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)108 static int al3010_read_raw(struct iio_dev *indio_dev,
109 struct iio_chan_spec const *chan, int *val,
110 int *val2, long mask)
111 {
112 struct al3010_data *data = iio_priv(indio_dev);
113 int ret, gain;
114 __le16 raw;
115
116 switch (mask) {
117 case IIO_CHAN_INFO_RAW:
118 /*
119 * ALS ADC value is stored in two adjacent registers:
120 * - low byte of output is stored at AL3010_REG_DATA_LOW
121 * - high byte of output is stored at AL3010_REG_DATA_LOW + 1
122 */
123 ret = regmap_bulk_read(data->regmap, AL3010_REG_DATA_LOW,
124 &raw, sizeof(raw));
125 if (ret)
126 return ret;
127
128 *val = le16_to_cpu(raw);
129
130 return IIO_VAL_INT;
131 case IIO_CHAN_INFO_SCALE:
132 ret = regmap_read(data->regmap, AL3010_REG_CONFIG, &gain);
133 if (ret)
134 return ret;
135
136 gain = FIELD_GET(AL3010_GAIN_MASK, gain);
137 *val = al3010_scales[gain][0];
138 *val2 = al3010_scales[gain][1];
139
140 return IIO_VAL_INT_PLUS_MICRO;
141 }
142 return -EINVAL;
143 }
144
al3010_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)145 static int al3010_write_raw(struct iio_dev *indio_dev,
146 struct iio_chan_spec const *chan, int val,
147 int val2, long mask)
148 {
149 struct al3010_data *data = iio_priv(indio_dev);
150 unsigned int i;
151
152 switch (mask) {
153 case IIO_CHAN_INFO_SCALE:
154 for (i = 0; i < ARRAY_SIZE(al3010_scales); i++) {
155 if (val != al3010_scales[i][0] ||
156 val2 != al3010_scales[i][1])
157 continue;
158
159 return regmap_write(data->regmap, AL3010_REG_CONFIG,
160 FIELD_PREP(AL3010_GAIN_MASK, i));
161 }
162 break;
163 }
164 return -EINVAL;
165 }
166
167 static const struct iio_info al3010_info = {
168 .read_raw = al3010_read_raw,
169 .write_raw = al3010_write_raw,
170 .attrs = &al3010_attribute_group,
171 };
172
al3010_probe(struct i2c_client * client)173 static int al3010_probe(struct i2c_client *client)
174 {
175 struct al3010_data *data;
176 struct device *dev = &client->dev;
177 struct iio_dev *indio_dev;
178 int ret;
179
180 indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
181 if (!indio_dev)
182 return -ENOMEM;
183
184 data = iio_priv(indio_dev);
185 i2c_set_clientdata(client, indio_dev);
186 data->regmap = devm_regmap_init_i2c(client, &al3010_regmap_config);
187 if (IS_ERR(data->regmap))
188 return dev_err_probe(dev, PTR_ERR(data->regmap),
189 "cannot allocate regmap\n");
190
191 indio_dev->info = &al3010_info;
192 indio_dev->name = "al3010";
193 indio_dev->channels = al3010_channels;
194 indio_dev->num_channels = ARRAY_SIZE(al3010_channels);
195 indio_dev->modes = INDIO_DIRECT_MODE;
196
197 ret = al3010_init(data);
198 if (ret)
199 return dev_err_probe(dev, ret, "failed to init ALS\n");
200
201 return devm_iio_device_register(dev, indio_dev);
202 }
203
al3010_suspend(struct device * dev)204 static int al3010_suspend(struct device *dev)
205 {
206 struct al3010_data *data = iio_priv(dev_get_drvdata(dev));
207
208 al3010_set_pwr_off(data);
209 return 0;
210 }
211
al3010_resume(struct device * dev)212 static int al3010_resume(struct device *dev)
213 {
214 struct al3010_data *data = iio_priv(dev_get_drvdata(dev));
215
216 return al3010_set_pwr_on(data);
217 }
218
219 static DEFINE_SIMPLE_DEV_PM_OPS(al3010_pm_ops, al3010_suspend, al3010_resume);
220
221 static const struct i2c_device_id al3010_id[] = {
222 { .name = "al3010" },
223 { }
224 };
225 MODULE_DEVICE_TABLE(i2c, al3010_id);
226
227 static const struct of_device_id al3010_of_match[] = {
228 { .compatible = "dynaimage,al3010", },
229 { }
230 };
231 MODULE_DEVICE_TABLE(of, al3010_of_match);
232
233 static struct i2c_driver al3010_driver = {
234 .driver = {
235 .name = "al3010",
236 .of_match_table = al3010_of_match,
237 .pm = pm_sleep_ptr(&al3010_pm_ops),
238 },
239 .probe = al3010_probe,
240 .id_table = al3010_id,
241 };
242 module_i2c_driver(al3010_driver);
243
244 MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
245 MODULE_AUTHOR("David Heidelberg <david@ixit.cz>");
246 MODULE_DESCRIPTION("AL3010 Ambient Light Sensor driver");
247 MODULE_LICENSE("GPL v2");
248