diff options
author | Kevin Tsai <ktsai@capellamicro.com> | 2013-12-23 20:53:00 -0500 |
---|---|---|
committer | Jonathan Cameron <jic23@kernel.org> | 2014-01-01 07:03:32 -0500 |
commit | 971672c0b3ccd16ce53299f1ccac0253fc6d7bf6 (patch) | |
tree | 7d902d60e69c24ca2b84ce566e7e0b88a7c08c90 | |
parent | a6e8e3a470bf96575eebfa9dbfda6e1ec8929fb8 (diff) |
iio: add Capella CM32181 ambient light sensor driver.
Add Capella Microsystem CM32181 Ambient Light Sensor IIO driver.
This driver will convert raw data to lux value under open-air
condition. Change the calibscale based on the cover material.
Signed-off-by: Kevin Tsai <ktsai@capellamicro.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>
-rw-r--r-- | Documentation/devicetree/bindings/i2c/trivial-devices.txt | 1 | ||||
-rw-r--r-- | drivers/iio/light/Kconfig | 11 | ||||
-rw-r--r-- | drivers/iio/light/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/light/cm32181.c | 379 |
4 files changed, 392 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index b1cb3415e6f1..c65f71cfaa5c 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt | |||
@@ -16,6 +16,7 @@ adt7461 +/-1C TDM Extended Temp Range I.C | |||
16 | at,24c08 i2c serial eeprom (24cxx) | 16 | at,24c08 i2c serial eeprom (24cxx) |
17 | atmel,24c02 i2c serial eeprom (24cxx) | 17 | atmel,24c02 i2c serial eeprom (24cxx) |
18 | atmel,at97sc3204t i2c trusted platform module (TPM) | 18 | atmel,at97sc3204t i2c trusted platform module (TPM) |
19 | capella,cm32181 CM32181: Ambient Light Sensor | ||
19 | catalyst,24c32 i2c serial eeprom | 20 | catalyst,24c32 i2c serial eeprom |
20 | dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock | 21 | dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock |
21 | dallas,ds1338 I2C RTC with 56-Byte NV RAM | 22 | dallas,ds1338 I2C RTC with 56-Byte NV RAM |
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index a022f27c6690..d12b2a0dbfbc 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig | |||
@@ -27,6 +27,17 @@ config APDS9300 | |||
27 | To compile this driver as a module, choose M here: the | 27 | To compile this driver as a module, choose M here: the |
28 | module will be called apds9300. | 28 | module will be called apds9300. |
29 | 29 | ||
30 | config CM32181 | ||
31 | depends on I2C | ||
32 | tristate "CM32181 driver" | ||
33 | help | ||
34 | Say Y here if you use cm32181. | ||
35 | This option enables ambient light sensor using | ||
36 | Capella cm32181 device driver. | ||
37 | |||
38 | To compile this driver as a module, choose M here: | ||
39 | the module will be called cm32181. | ||
40 | |||
30 | config CM36651 | 41 | config CM36651 |
31 | depends on I2C | 42 | depends on I2C |
32 | tristate "CM36651 driver" | 43 | tristate "CM36651 driver" |
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index daa327f39e04..60e35ac07ff0 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile | |||
@@ -5,6 +5,7 @@ | |||
5 | # When adding new entries keep the list in alphabetical order | 5 | # When adding new entries keep the list in alphabetical order |
6 | obj-$(CONFIG_ADJD_S311) += adjd_s311.o | 6 | obj-$(CONFIG_ADJD_S311) += adjd_s311.o |
7 | obj-$(CONFIG_APDS9300) += apds9300.o | 7 | obj-$(CONFIG_APDS9300) += apds9300.o |
8 | obj-$(CONFIG_CM32181) += cm32181.o | ||
8 | obj-$(CONFIG_CM36651) += cm36651.o | 9 | obj-$(CONFIG_CM36651) += cm36651.o |
9 | obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o | 10 | obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o |
10 | obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o | 11 | obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o |
diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c new file mode 100644 index 000000000000..f17b4e6183c6 --- /dev/null +++ b/drivers/iio/light/cm32181.c | |||
@@ -0,0 +1,379 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Capella Microsystems Inc. | ||
3 | * Author: Kevin Tsai <ktsai@capellamicro.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2, as published | ||
7 | * by the Free Software Foundation. | ||
8 | */ | ||
9 | |||
10 | #include <linux/delay.h> | ||
11 | #include <linux/err.h> | ||
12 | #include <linux/i2c.h> | ||
13 | #include <linux/mutex.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/regulator/consumer.h> | ||
17 | #include <linux/iio/iio.h> | ||
18 | #include <linux/iio/sysfs.h> | ||
19 | #include <linux/iio/events.h> | ||
20 | #include <linux/init.h> | ||
21 | |||
22 | /* Registers Address */ | ||
23 | #define CM32181_REG_ADDR_CMD 0x00 | ||
24 | #define CM32181_REG_ADDR_ALS 0x04 | ||
25 | #define CM32181_REG_ADDR_STATUS 0x06 | ||
26 | #define CM32181_REG_ADDR_ID 0x07 | ||
27 | |||
28 | /* Number of Configurable Registers */ | ||
29 | #define CM32181_CONF_REG_NUM 0x01 | ||
30 | |||
31 | /* CMD register */ | ||
32 | #define CM32181_CMD_ALS_ENABLE 0x00 | ||
33 | #define CM32181_CMD_ALS_DISABLE 0x01 | ||
34 | #define CM32181_CMD_ALS_INT_EN 0x02 | ||
35 | |||
36 | #define CM32181_CMD_ALS_IT_SHIFT 6 | ||
37 | #define CM32181_CMD_ALS_IT_MASK (0x0F << CM32181_CMD_ALS_IT_SHIFT) | ||
38 | #define CM32181_CMD_ALS_IT_DEFAULT (0x00 << CM32181_CMD_ALS_IT_SHIFT) | ||
39 | |||
40 | #define CM32181_CMD_ALS_SM_SHIFT 11 | ||
41 | #define CM32181_CMD_ALS_SM_MASK (0x03 << CM32181_CMD_ALS_SM_SHIFT) | ||
42 | #define CM32181_CMD_ALS_SM_DEFAULT (0x01 << CM32181_CMD_ALS_SM_SHIFT) | ||
43 | |||
44 | #define CM32181_MLUX_PER_BIT 5 /* ALS_SM=01 IT=800ms */ | ||
45 | #define CM32181_MLUX_PER_BIT_BASE_IT 800000 /* Based on IT=800ms */ | ||
46 | #define CM32181_CALIBSCALE_DEFAULT 1000 | ||
47 | #define CM32181_CALIBSCALE_RESOLUTION 1000 | ||
48 | #define MLUX_PER_LUX 1000 | ||
49 | |||
50 | static const u8 cm32181_reg[CM32181_CONF_REG_NUM] = { | ||
51 | CM32181_REG_ADDR_CMD, | ||
52 | }; | ||
53 | |||
54 | static const int als_it_bits[] = {12, 8, 0, 1, 2, 3}; | ||
55 | static const int als_it_value[] = {25000, 50000, 100000, 200000, 400000, | ||
56 | 800000}; | ||
57 | |||
58 | struct cm32181_chip { | ||
59 | struct i2c_client *client; | ||
60 | struct mutex lock; | ||
61 | u16 conf_regs[CM32181_CONF_REG_NUM]; | ||
62 | int calibscale; | ||
63 | }; | ||
64 | |||
65 | /** | ||
66 | * cm32181_reg_init() - Initialize CM32181 registers | ||
67 | * @cm32181: pointer of struct cm32181. | ||
68 | * | ||
69 | * Initialize CM32181 ambient light sensor register to default values. | ||
70 | * | ||
71 | * Return: 0 for success; otherwise for error code. | ||
72 | */ | ||
73 | static int cm32181_reg_init(struct cm32181_chip *cm32181) | ||
74 | { | ||
75 | struct i2c_client *client = cm32181->client; | ||
76 | int i; | ||
77 | s32 ret; | ||
78 | |||
79 | ret = i2c_smbus_read_word_data(client, CM32181_REG_ADDR_ID); | ||
80 | if (ret < 0) | ||
81 | return ret; | ||
82 | |||
83 | /* check device ID */ | ||
84 | if ((ret & 0xFF) != 0x81) | ||
85 | return -ENODEV; | ||
86 | |||
87 | /* Default Values */ | ||
88 | cm32181->conf_regs[CM32181_REG_ADDR_CMD] = CM32181_CMD_ALS_ENABLE | | ||
89 | CM32181_CMD_ALS_IT_DEFAULT | CM32181_CMD_ALS_SM_DEFAULT; | ||
90 | cm32181->calibscale = CM32181_CALIBSCALE_DEFAULT; | ||
91 | |||
92 | /* Initialize registers*/ | ||
93 | for (i = 0; i < CM32181_CONF_REG_NUM; i++) { | ||
94 | ret = i2c_smbus_write_word_data(client, cm32181_reg[i], | ||
95 | cm32181->conf_regs[i]); | ||
96 | if (ret < 0) | ||
97 | return ret; | ||
98 | } | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * cm32181_read_als_it() - Get sensor integration time (ms) | ||
105 | * @cm32181: pointer of struct cm32181 | ||
106 | * @val: pointer of int to load the als_it value. | ||
107 | * | ||
108 | * Report the current integartion time by millisecond. | ||
109 | * | ||
110 | * Return: IIO_VAL_INT for success, otherwise -EINVAL. | ||
111 | */ | ||
112 | static int cm32181_read_als_it(struct cm32181_chip *cm32181, int *val) | ||
113 | { | ||
114 | u16 als_it; | ||
115 | int i; | ||
116 | |||
117 | als_it = cm32181->conf_regs[CM32181_REG_ADDR_CMD]; | ||
118 | als_it &= CM32181_CMD_ALS_IT_MASK; | ||
119 | als_it >>= CM32181_CMD_ALS_IT_SHIFT; | ||
120 | for (i = 0; i < ARRAY_SIZE(als_it_bits); i++) { | ||
121 | if (als_it == als_it_bits[i]) { | ||
122 | *val = als_it_value[i]; | ||
123 | return IIO_VAL_INT; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | return -EINVAL; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * cm32181_write_als_it() - Write sensor integration time | ||
132 | * @cm32181: pointer of struct cm32181. | ||
133 | * @val: integration time by millisecond. | ||
134 | * | ||
135 | * Convert integration time (ms) to sensor value. | ||
136 | * | ||
137 | * Return: i2c_smbus_write_word_data command return value. | ||
138 | */ | ||
139 | static int cm32181_write_als_it(struct cm32181_chip *cm32181, int val) | ||
140 | { | ||
141 | struct i2c_client *client = cm32181->client; | ||
142 | u16 als_it; | ||
143 | int ret, i, n; | ||
144 | |||
145 | n = ARRAY_SIZE(als_it_value); | ||
146 | for (i = 0; i < n; i++) | ||
147 | if (val <= als_it_value[i]) | ||
148 | break; | ||
149 | if (i >= n) | ||
150 | i = n - 1; | ||
151 | |||
152 | als_it = als_it_bits[i]; | ||
153 | als_it <<= CM32181_CMD_ALS_IT_SHIFT; | ||
154 | |||
155 | mutex_lock(&cm32181->lock); | ||
156 | cm32181->conf_regs[CM32181_REG_ADDR_CMD] &= | ||
157 | ~CM32181_CMD_ALS_IT_MASK; | ||
158 | cm32181->conf_regs[CM32181_REG_ADDR_CMD] |= | ||
159 | als_it; | ||
160 | ret = i2c_smbus_write_word_data(client, CM32181_REG_ADDR_CMD, | ||
161 | cm32181->conf_regs[CM32181_REG_ADDR_CMD]); | ||
162 | mutex_unlock(&cm32181->lock); | ||
163 | |||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | /** | ||
168 | * cm32181_get_lux() - report current lux value | ||
169 | * @cm32181: pointer of struct cm32181. | ||
170 | * | ||
171 | * Convert sensor raw data to lux. It depends on integration | ||
172 | * time and claibscale variable. | ||
173 | * | ||
174 | * Return: Positive value is lux, otherwise is error code. | ||
175 | */ | ||
176 | static int cm32181_get_lux(struct cm32181_chip *cm32181) | ||
177 | { | ||
178 | struct i2c_client *client = cm32181->client; | ||
179 | int ret; | ||
180 | int als_it; | ||
181 | unsigned long lux; | ||
182 | |||
183 | ret = cm32181_read_als_it(cm32181, &als_it); | ||
184 | if (ret < 0) | ||
185 | return -EINVAL; | ||
186 | |||
187 | lux = CM32181_MLUX_PER_BIT; | ||
188 | lux *= CM32181_MLUX_PER_BIT_BASE_IT; | ||
189 | lux /= als_it; | ||
190 | |||
191 | ret = i2c_smbus_read_word_data(client, CM32181_REG_ADDR_ALS); | ||
192 | if (ret < 0) | ||
193 | return ret; | ||
194 | |||
195 | lux *= ret; | ||
196 | lux *= cm32181->calibscale; | ||
197 | lux /= CM32181_CALIBSCALE_RESOLUTION; | ||
198 | lux /= MLUX_PER_LUX; | ||
199 | |||
200 | if (lux > 0xFFFF) | ||
201 | lux = 0xFFFF; | ||
202 | |||
203 | return lux; | ||
204 | } | ||
205 | |||
206 | static int cm32181_read_raw(struct iio_dev *indio_dev, | ||
207 | struct iio_chan_spec const *chan, | ||
208 | int *val, int *val2, long mask) | ||
209 | { | ||
210 | struct cm32181_chip *cm32181 = iio_priv(indio_dev); | ||
211 | int ret; | ||
212 | |||
213 | switch (mask) { | ||
214 | case IIO_CHAN_INFO_PROCESSED: | ||
215 | ret = cm32181_get_lux(cm32181); | ||
216 | if (ret < 0) | ||
217 | return ret; | ||
218 | *val = ret; | ||
219 | return IIO_VAL_INT; | ||
220 | case IIO_CHAN_INFO_CALIBSCALE: | ||
221 | *val = cm32181->calibscale; | ||
222 | return IIO_VAL_INT; | ||
223 | case IIO_CHAN_INFO_INT_TIME: | ||
224 | ret = cm32181_read_als_it(cm32181, val); | ||
225 | return ret; | ||
226 | } | ||
227 | |||
228 | return -EINVAL; | ||
229 | } | ||
230 | |||
231 | static int cm32181_write_raw(struct iio_dev *indio_dev, | ||
232 | struct iio_chan_spec const *chan, | ||
233 | int val, int val2, long mask) | ||
234 | { | ||
235 | struct cm32181_chip *cm32181 = iio_priv(indio_dev); | ||
236 | int ret; | ||
237 | |||
238 | switch (mask) { | ||
239 | case IIO_CHAN_INFO_CALIBSCALE: | ||
240 | cm32181->calibscale = val; | ||
241 | return val; | ||
242 | case IIO_CHAN_INFO_INT_TIME: | ||
243 | ret = cm32181_write_als_it(cm32181, val); | ||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | return -EINVAL; | ||
248 | } | ||
249 | |||
250 | /** | ||
251 | * cm32181_get_it_available() - Get available ALS IT value | ||
252 | * @dev: pointer of struct device. | ||
253 | * @attr: pointer of struct device_attribute. | ||
254 | * @buf: pointer of return string buffer. | ||
255 | * | ||
256 | * Display the available integration time values by millisecond. | ||
257 | * | ||
258 | * Return: string length. | ||
259 | */ | ||
260 | static ssize_t cm32181_get_it_available(struct device *dev, | ||
261 | struct device_attribute *attr, char *buf) | ||
262 | { | ||
263 | int i, n, len; | ||
264 | |||
265 | n = ARRAY_SIZE(als_it_value); | ||
266 | for (i = 0, len = 0; i < n; i++) | ||
267 | len += sprintf(buf + len, "%d ", als_it_value[i]); | ||
268 | return len + sprintf(buf + len, "\n"); | ||
269 | } | ||
270 | |||
271 | static const struct iio_chan_spec cm32181_channels[] = { | ||
272 | { | ||
273 | .type = IIO_LIGHT, | ||
274 | .info_mask_separate = | ||
275 | BIT(IIO_CHAN_INFO_PROCESSED) | | ||
276 | BIT(IIO_CHAN_INFO_CALIBSCALE) | | ||
277 | BIT(IIO_CHAN_INFO_INT_TIME), | ||
278 | } | ||
279 | }; | ||
280 | |||
281 | static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, | ||
282 | S_IRUGO, cm32181_get_it_available, NULL, 0); | ||
283 | |||
284 | static struct attribute *cm32181_attributes[] = { | ||
285 | &iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr, | ||
286 | NULL, | ||
287 | }; | ||
288 | |||
289 | static const struct attribute_group cm32181_attribute_group = { | ||
290 | .attrs = cm32181_attributes | ||
291 | }; | ||
292 | |||
293 | static const struct iio_info cm32181_info = { | ||
294 | .driver_module = THIS_MODULE, | ||
295 | .read_raw = &cm32181_read_raw, | ||
296 | .write_raw = &cm32181_write_raw, | ||
297 | .attrs = &cm32181_attribute_group, | ||
298 | }; | ||
299 | |||
300 | static int cm32181_probe(struct i2c_client *client, | ||
301 | const struct i2c_device_id *id) | ||
302 | { | ||
303 | struct cm32181_chip *cm32181; | ||
304 | struct iio_dev *indio_dev; | ||
305 | int ret; | ||
306 | |||
307 | indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm32181)); | ||
308 | if (!indio_dev) { | ||
309 | dev_err(&client->dev, "devm_iio_device_alloc failed\n"); | ||
310 | return -ENOMEM; | ||
311 | } | ||
312 | |||
313 | cm32181 = iio_priv(indio_dev); | ||
314 | i2c_set_clientdata(client, indio_dev); | ||
315 | cm32181->client = client; | ||
316 | |||
317 | mutex_init(&cm32181->lock); | ||
318 | indio_dev->dev.parent = &client->dev; | ||
319 | indio_dev->channels = cm32181_channels; | ||
320 | indio_dev->num_channels = ARRAY_SIZE(cm32181_channels); | ||
321 | indio_dev->info = &cm32181_info; | ||
322 | indio_dev->name = id->name; | ||
323 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
324 | |||
325 | ret = cm32181_reg_init(cm32181); | ||
326 | if (ret) { | ||
327 | dev_err(&client->dev, | ||
328 | "%s: register init failed\n", | ||
329 | __func__); | ||
330 | return ret; | ||
331 | } | ||
332 | |||
333 | ret = iio_device_register(indio_dev); | ||
334 | if (ret) { | ||
335 | dev_err(&client->dev, | ||
336 | "%s: regist device failed\n", | ||
337 | __func__); | ||
338 | return ret; | ||
339 | } | ||
340 | |||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static int cm32181_remove(struct i2c_client *client) | ||
345 | { | ||
346 | struct iio_dev *indio_dev = i2c_get_clientdata(client); | ||
347 | |||
348 | iio_device_unregister(indio_dev); | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | static const struct i2c_device_id cm32181_id[] = { | ||
353 | { "cm32181", 0 }, | ||
354 | { } | ||
355 | }; | ||
356 | |||
357 | MODULE_DEVICE_TABLE(i2c, cm32181_id); | ||
358 | |||
359 | static const struct of_device_id cm32181_of_match[] = { | ||
360 | { .compatible = "capella,cm32181" }, | ||
361 | { } | ||
362 | }; | ||
363 | |||
364 | static struct i2c_driver cm32181_driver = { | ||
365 | .driver = { | ||
366 | .name = "cm32181", | ||
367 | .of_match_table = of_match_ptr(cm32181_of_match), | ||
368 | .owner = THIS_MODULE, | ||
369 | }, | ||
370 | .id_table = cm32181_id, | ||
371 | .probe = cm32181_probe, | ||
372 | .remove = cm32181_remove, | ||
373 | }; | ||
374 | |||
375 | module_i2c_driver(cm32181_driver); | ||
376 | |||
377 | MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>"); | ||
378 | MODULE_DESCRIPTION("CM32181 ambient light sensor driver"); | ||
379 | MODULE_LICENSE("GPL"); | ||