xref: /openbmc/linux/drivers/misc/apds9802als.c (revision 1093736b)
122d96aa5Sanantha /*
222d96aa5Sanantha  * apds9802als.c - apds9802  ALS Driver
322d96aa5Sanantha  *
422d96aa5Sanantha  * Copyright (C) 2009 Intel Corp
522d96aa5Sanantha  *
622d96aa5Sanantha  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
722d96aa5Sanantha  *
822d96aa5Sanantha  * This program is free software; you can redistribute it and/or modify
922d96aa5Sanantha  * it under the terms of the GNU General Public License as published by
1022d96aa5Sanantha  * the Free Software Foundation; version 2 of the License.
1122d96aa5Sanantha  *
1222d96aa5Sanantha  * This program is distributed in the hope that it will be useful, but
1322d96aa5Sanantha  * WITHOUT ANY WARRANTY; without even the implied warranty of
1422d96aa5Sanantha  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1522d96aa5Sanantha  * General Public License for more details.
1622d96aa5Sanantha  *
1722d96aa5Sanantha  * You should have received a copy of the GNU General Public License along
1822d96aa5Sanantha  * with this program; if not, write to the Free Software Foundation, Inc.,
1922d96aa5Sanantha  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
2022d96aa5Sanantha  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2122d96aa5Sanantha  *
2222d96aa5Sanantha  */
2322d96aa5Sanantha 
2422d96aa5Sanantha #include <linux/module.h>
2522d96aa5Sanantha #include <linux/init.h>
2622d96aa5Sanantha #include <linux/slab.h>
2722d96aa5Sanantha #include <linux/i2c.h>
2822d96aa5Sanantha #include <linux/err.h>
2922d96aa5Sanantha #include <linux/delay.h>
3022d96aa5Sanantha #include <linux/mutex.h>
3122d96aa5Sanantha #include <linux/sysfs.h>
32f0cfec11SHong Liu #include <linux/pm_runtime.h>
3322d96aa5Sanantha 
3422d96aa5Sanantha #define ALS_MIN_RANGE_VAL 1
3522d96aa5Sanantha #define ALS_MAX_RANGE_VAL 2
3622d96aa5Sanantha #define POWER_STA_ENABLE 1
3722d96aa5Sanantha #define POWER_STA_DISABLE 0
3822d96aa5Sanantha 
3922d96aa5Sanantha #define DRIVER_NAME "apds9802als"
4022d96aa5Sanantha 
4122d96aa5Sanantha struct als_data {
4222d96aa5Sanantha 	struct mutex mutex;
4322d96aa5Sanantha };
4422d96aa5Sanantha 
4522d96aa5Sanantha static ssize_t als_sensing_range_show(struct device *dev,
4622d96aa5Sanantha 			struct device_attribute *attr,  char *buf)
4722d96aa5Sanantha {
4822d96aa5Sanantha 	struct i2c_client *client = to_i2c_client(dev);
4922d96aa5Sanantha 	int  val;
5022d96aa5Sanantha 
5122d96aa5Sanantha 	val = i2c_smbus_read_byte_data(client, 0x81);
5222d96aa5Sanantha 	if (val < 0)
5322d96aa5Sanantha 		return val;
5422d96aa5Sanantha 	if (val & 1)
5522d96aa5Sanantha 		return sprintf(buf, "4095\n");
5622d96aa5Sanantha 	else
5722d96aa5Sanantha 		return sprintf(buf, "65535\n");
5822d96aa5Sanantha }
5922d96aa5Sanantha 
60f0cfec11SHong Liu static int als_wait_for_data_ready(struct device *dev)
61f0cfec11SHong Liu {
62f0cfec11SHong Liu 	struct i2c_client *client = to_i2c_client(dev);
63f0cfec11SHong Liu 	int ret;
64f0cfec11SHong Liu 	int retry = 10;
65f0cfec11SHong Liu 
66f0cfec11SHong Liu 	do {
67f0cfec11SHong Liu 		msleep(30);
68f0cfec11SHong Liu 		ret = i2c_smbus_read_byte_data(client, 0x86);
69f0cfec11SHong Liu 	} while (!(ret & 0x80) && retry--);
70f0cfec11SHong Liu 
71f0cfec11SHong Liu 	if (!retry) {
72f0cfec11SHong Liu 		dev_warn(dev, "timeout waiting for data ready\n");
73f0cfec11SHong Liu 		return -ETIMEDOUT;
74f0cfec11SHong Liu 	}
75f0cfec11SHong Liu 
76f0cfec11SHong Liu 	return 0;
77f0cfec11SHong Liu }
78f0cfec11SHong Liu 
7922d96aa5Sanantha static ssize_t als_lux0_input_data_show(struct device *dev,
8022d96aa5Sanantha 			struct device_attribute *attr, char *buf)
8122d96aa5Sanantha {
8222d96aa5Sanantha 	struct i2c_client *client = to_i2c_client(dev);
8322d96aa5Sanantha 	struct als_data *data = i2c_get_clientdata(client);
84f0cfec11SHong Liu 	int ret_val;
8522d96aa5Sanantha 	int temp;
8622d96aa5Sanantha 
8722d96aa5Sanantha 	/* Protect against parallel reads */
88f0cfec11SHong Liu 	pm_runtime_get_sync(dev);
8922d96aa5Sanantha 	mutex_lock(&data->mutex);
90f0cfec11SHong Liu 
91f0cfec11SHong Liu 	/* clear EOC interrupt status */
92f0cfec11SHong Liu 	i2c_smbus_write_byte(client, 0x40);
93f0cfec11SHong Liu 	/* start measurement */
94f0cfec11SHong Liu 	temp = i2c_smbus_read_byte_data(client, 0x81);
95f0cfec11SHong Liu 	i2c_smbus_write_byte_data(client, 0x81, temp | 0x08);
96f0cfec11SHong Liu 
97f0cfec11SHong Liu 	ret_val = als_wait_for_data_ready(dev);
98f0cfec11SHong Liu 	if (ret_val < 0)
99f0cfec11SHong Liu 		goto failed;
100f0cfec11SHong Liu 
10122d96aa5Sanantha 	temp = i2c_smbus_read_byte_data(client, 0x8C); /* LSB data */
10222d96aa5Sanantha 	if (temp < 0) {
10322d96aa5Sanantha 		ret_val = temp;
10422d96aa5Sanantha 		goto failed;
10522d96aa5Sanantha 	}
10622d96aa5Sanantha 	ret_val = i2c_smbus_read_byte_data(client, 0x8D); /* MSB data */
10722d96aa5Sanantha 	if (ret_val < 0)
10822d96aa5Sanantha 		goto failed;
109f0cfec11SHong Liu 
11022d96aa5Sanantha 	mutex_unlock(&data->mutex);
111f0cfec11SHong Liu 	pm_runtime_put_sync(dev);
112f0cfec11SHong Liu 
113f0cfec11SHong Liu 	temp = (ret_val << 8) | temp;
114f0cfec11SHong Liu 	return sprintf(buf, "%d\n", temp);
11522d96aa5Sanantha failed:
11622d96aa5Sanantha 	mutex_unlock(&data->mutex);
117f0cfec11SHong Liu 	pm_runtime_put_sync(dev);
11822d96aa5Sanantha 	return ret_val;
11922d96aa5Sanantha }
12022d96aa5Sanantha 
12122d96aa5Sanantha static ssize_t als_sensing_range_store(struct device *dev,
12222d96aa5Sanantha 		struct device_attribute *attr, const  char *buf, size_t count)
12322d96aa5Sanantha {
12422d96aa5Sanantha 	struct i2c_client *client = to_i2c_client(dev);
12522d96aa5Sanantha 	struct als_data *data = i2c_get_clientdata(client);
1261093736bSVasiliy Kulikov 	int ret_val;
12722d96aa5Sanantha 	unsigned long val;
12822d96aa5Sanantha 
12922d96aa5Sanantha 	if (strict_strtoul(buf, 10, &val))
13022d96aa5Sanantha 		return -EINVAL;
13122d96aa5Sanantha 
13222d96aa5Sanantha 	if (val < 4096)
13322d96aa5Sanantha 		val = 1;
13422d96aa5Sanantha 	else if (val < 65536)
13522d96aa5Sanantha 		val = 2;
13622d96aa5Sanantha 	else
13722d96aa5Sanantha 		return -ERANGE;
13822d96aa5Sanantha 
139f0cfec11SHong Liu 	pm_runtime_get_sync(dev);
140f0cfec11SHong Liu 
14122d96aa5Sanantha 	/* Make sure nobody else reads/modifies/writes 0x81 while we
14222d96aa5Sanantha 	   are active */
14322d96aa5Sanantha 	mutex_lock(&data->mutex);
14422d96aa5Sanantha 
14522d96aa5Sanantha 	ret_val = i2c_smbus_read_byte_data(client, 0x81);
14622d96aa5Sanantha 	if (ret_val < 0)
14722d96aa5Sanantha 		goto fail;
14822d96aa5Sanantha 
14922d96aa5Sanantha 	/* Reset the bits before setting them */
15022d96aa5Sanantha 	ret_val = ret_val & 0xFA;
15122d96aa5Sanantha 
152f0cfec11SHong Liu 	if (val == 1) /* Setting detection range up to 4k LUX */
153f0cfec11SHong Liu 		ret_val = (ret_val | 0x01);
154f0cfec11SHong Liu 	else /* Setting detection range up to 64k LUX*/
155f0cfec11SHong Liu 		ret_val = (ret_val | 0x00);
15622d96aa5Sanantha 
15722d96aa5Sanantha 	ret_val = i2c_smbus_write_byte_data(client, 0x81, ret_val);
158f0cfec11SHong Liu 
15922d96aa5Sanantha 	if (ret_val >= 0) {
16022d96aa5Sanantha 		/* All OK */
16122d96aa5Sanantha 		mutex_unlock(&data->mutex);
162f0cfec11SHong Liu 		pm_runtime_put_sync(dev);
16322d96aa5Sanantha 		return count;
16422d96aa5Sanantha 	}
16522d96aa5Sanantha fail:
16622d96aa5Sanantha 	mutex_unlock(&data->mutex);
167f0cfec11SHong Liu 	pm_runtime_put_sync(dev);
16822d96aa5Sanantha 	return ret_val;
16922d96aa5Sanantha }
17022d96aa5Sanantha 
17122d96aa5Sanantha static int als_set_power_state(struct i2c_client *client, bool on_off)
17222d96aa5Sanantha {
17322d96aa5Sanantha 	int ret_val;
17422d96aa5Sanantha 	struct als_data *data = i2c_get_clientdata(client);
17522d96aa5Sanantha 
17622d96aa5Sanantha 	mutex_lock(&data->mutex);
17722d96aa5Sanantha 	ret_val = i2c_smbus_read_byte_data(client, 0x80);
17822d96aa5Sanantha 	if (ret_val < 0)
17922d96aa5Sanantha 		goto fail;
18022d96aa5Sanantha 	if (on_off)
18122d96aa5Sanantha 		ret_val = ret_val | 0x01;
18222d96aa5Sanantha 	else
18322d96aa5Sanantha 		ret_val = ret_val & 0xFE;
18422d96aa5Sanantha 	ret_val = i2c_smbus_write_byte_data(client, 0x80, ret_val);
18522d96aa5Sanantha fail:
18622d96aa5Sanantha 	mutex_unlock(&data->mutex);
18722d96aa5Sanantha 	return ret_val;
18822d96aa5Sanantha }
18922d96aa5Sanantha 
19022d96aa5Sanantha static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR,
19122d96aa5Sanantha 	als_sensing_range_show, als_sensing_range_store);
19222d96aa5Sanantha static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux0_input_data_show, NULL);
19322d96aa5Sanantha 
19422d96aa5Sanantha static struct attribute *mid_att_als[] = {
19522d96aa5Sanantha 	&dev_attr_lux0_sensor_range.attr,
19622d96aa5Sanantha 	&dev_attr_lux0_input.attr,
19722d96aa5Sanantha 	NULL
19822d96aa5Sanantha };
19922d96aa5Sanantha 
20022d96aa5Sanantha static struct attribute_group m_als_gr = {
20122d96aa5Sanantha 	.name = "apds9802als",
20222d96aa5Sanantha 	.attrs = mid_att_als
20322d96aa5Sanantha };
20422d96aa5Sanantha 
20522d96aa5Sanantha static int als_set_default_config(struct i2c_client *client)
20622d96aa5Sanantha {
20722d96aa5Sanantha 	int ret_val;
20822d96aa5Sanantha 	/* Write the command and then switch on */
20922d96aa5Sanantha 	ret_val = i2c_smbus_write_byte_data(client, 0x80, 0x01);
21022d96aa5Sanantha 	if (ret_val < 0) {
21122d96aa5Sanantha 		dev_err(&client->dev, "failed default switch on write\n");
21222d96aa5Sanantha 		return ret_val;
21322d96aa5Sanantha 	}
214f0cfec11SHong Liu 	/* detection range: 1~64K Lux, maunal measurement */
215f0cfec11SHong Liu 	ret_val = i2c_smbus_write_byte_data(client, 0x81, 0x08);
21622d96aa5Sanantha 	if (ret_val < 0)
21722d96aa5Sanantha 		dev_err(&client->dev, "failed default LUX on write\n");
218f0cfec11SHong Liu 
219f0cfec11SHong Liu 	/*  We always get 0 for the 1st measurement after system power on,
220f0cfec11SHong Liu 	 *  so make sure it is finished before user asks for data.
221f0cfec11SHong Liu 	 */
222f0cfec11SHong Liu 	als_wait_for_data_ready(&client->dev);
223f0cfec11SHong Liu 
22422d96aa5Sanantha 	return ret_val;
22522d96aa5Sanantha }
22622d96aa5Sanantha 
22722d96aa5Sanantha static int apds9802als_probe(struct i2c_client *client,
22822d96aa5Sanantha 			     const struct i2c_device_id *id)
22922d96aa5Sanantha {
23022d96aa5Sanantha 	int res;
23122d96aa5Sanantha 	struct als_data *data;
23222d96aa5Sanantha 
23322d96aa5Sanantha 	data = kzalloc(sizeof(struct als_data), GFP_KERNEL);
23422d96aa5Sanantha 	if (data == NULL) {
23522d96aa5Sanantha 		dev_err(&client->dev, "Memory allocation failed\n");
23622d96aa5Sanantha 		return -ENOMEM;
23722d96aa5Sanantha 	}
23822d96aa5Sanantha 	i2c_set_clientdata(client, data);
23922d96aa5Sanantha 	res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
24022d96aa5Sanantha 	if (res) {
24122d96aa5Sanantha 		dev_err(&client->dev, "device create file failed\n");
24222d96aa5Sanantha 		goto als_error1;
24322d96aa5Sanantha 	}
244f0cfec11SHong Liu 	dev_info(&client->dev, "ALS chip found\n");
24522d96aa5Sanantha 	als_set_default_config(client);
24622d96aa5Sanantha 	mutex_init(&data->mutex);
247f0cfec11SHong Liu 
248f0cfec11SHong Liu 	pm_runtime_enable(&client->dev);
249f0cfec11SHong Liu 	pm_runtime_get(&client->dev);
250f0cfec11SHong Liu 	pm_runtime_put(&client->dev);
251f0cfec11SHong Liu 
25222d96aa5Sanantha 	return res;
25322d96aa5Sanantha als_error1:
25422d96aa5Sanantha 	i2c_set_clientdata(client, NULL);
25522d96aa5Sanantha 	kfree(data);
25622d96aa5Sanantha 	return res;
25722d96aa5Sanantha }
25822d96aa5Sanantha 
25922d96aa5Sanantha static int apds9802als_remove(struct i2c_client *client)
26022d96aa5Sanantha {
26122d96aa5Sanantha 	struct als_data *data = i2c_get_clientdata(client);
262f0cfec11SHong Liu 
263f0cfec11SHong Liu 	als_set_power_state(client, false);
26422d96aa5Sanantha 	sysfs_remove_group(&client->dev.kobj, &m_als_gr);
26522d96aa5Sanantha 	kfree(data);
26622d96aa5Sanantha 	return 0;
26722d96aa5Sanantha }
26822d96aa5Sanantha 
269f0cfec11SHong Liu #ifdef CONFIG_PM
27022d96aa5Sanantha static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg)
27122d96aa5Sanantha {
27222d96aa5Sanantha 	als_set_power_state(client, false);
27322d96aa5Sanantha 	return 0;
27422d96aa5Sanantha }
27522d96aa5Sanantha 
27622d96aa5Sanantha static int apds9802als_resume(struct i2c_client *client)
27722d96aa5Sanantha {
278f0cfec11SHong Liu 	als_set_default_config(client);
27922d96aa5Sanantha 
280f0cfec11SHong Liu 	pm_runtime_get(&client->dev);
281f0cfec11SHong Liu 	pm_runtime_put(&client->dev);
282f0cfec11SHong Liu 	return 0;
283f0cfec11SHong Liu }
284f0cfec11SHong Liu 
285f0cfec11SHong Liu static int apds9802als_runtime_suspend(struct device *dev)
286f0cfec11SHong Liu {
287f0cfec11SHong Liu 	struct i2c_client *client = to_i2c_client(dev);
288f0cfec11SHong Liu 
289f0cfec11SHong Liu 	als_set_power_state(client, false);
290f0cfec11SHong Liu 	return 0;
291f0cfec11SHong Liu }
292f0cfec11SHong Liu 
293f0cfec11SHong Liu static int apds9802als_runtime_resume(struct device *dev)
294f0cfec11SHong Liu {
295f0cfec11SHong Liu 	struct i2c_client *client = to_i2c_client(dev);
296f0cfec11SHong Liu 
29722d96aa5Sanantha 	als_set_power_state(client, true);
29822d96aa5Sanantha 	return 0;
29922d96aa5Sanantha }
30022d96aa5Sanantha 
301f0cfec11SHong Liu static const struct dev_pm_ops apds9802als_pm_ops = {
302f0cfec11SHong Liu 	.runtime_suspend = apds9802als_runtime_suspend,
303f0cfec11SHong Liu 	.runtime_resume = apds9802als_runtime_resume,
304f0cfec11SHong Liu };
305f0cfec11SHong Liu 
306f0cfec11SHong Liu #define APDS9802ALS_PM_OPS (&apds9802als_pm_ops)
307f0cfec11SHong Liu 
308f0cfec11SHong Liu #else	/* CONFIG_PM */
309f0cfec11SHong Liu #define apds9802als_suspend NULL
310f0cfec11SHong Liu #define apds9802als_resume NULL
311f0cfec11SHong Liu #define APDS9802ALS_PM_OPS NULL
312f0cfec11SHong Liu #endif	/* CONFIG_PM */
313f0cfec11SHong Liu 
31422d96aa5Sanantha static struct i2c_device_id apds9802als_id[] = {
31522d96aa5Sanantha 	{ DRIVER_NAME, 0 },
31622d96aa5Sanantha 	{ }
31722d96aa5Sanantha };
31822d96aa5Sanantha 
31922d96aa5Sanantha MODULE_DEVICE_TABLE(i2c, apds9802als_id);
32022d96aa5Sanantha 
32122d96aa5Sanantha static struct i2c_driver apds9802als_driver = {
32222d96aa5Sanantha 	.driver = {
32322d96aa5Sanantha 		.name = DRIVER_NAME,
324f0cfec11SHong Liu 		.pm = APDS9802ALS_PM_OPS,
32522d96aa5Sanantha 	},
32622d96aa5Sanantha 	.probe = apds9802als_probe,
32722d96aa5Sanantha 	.remove = apds9802als_remove,
32822d96aa5Sanantha 	.suspend = apds9802als_suspend,
32922d96aa5Sanantha 	.resume = apds9802als_resume,
33022d96aa5Sanantha 	.id_table = apds9802als_id,
33122d96aa5Sanantha };
33222d96aa5Sanantha 
33322d96aa5Sanantha static int __init sensor_apds9802als_init(void)
33422d96aa5Sanantha {
33522d96aa5Sanantha 	return i2c_add_driver(&apds9802als_driver);
33622d96aa5Sanantha }
33722d96aa5Sanantha 
33822d96aa5Sanantha static void  __exit sensor_apds9802als_exit(void)
33922d96aa5Sanantha {
34022d96aa5Sanantha 	i2c_del_driver(&apds9802als_driver);
34122d96aa5Sanantha }
34222d96aa5Sanantha module_init(sensor_apds9802als_init);
34322d96aa5Sanantha module_exit(sensor_apds9802als_exit);
34422d96aa5Sanantha 
34522d96aa5Sanantha MODULE_AUTHOR("Anantha Narayanan <Anantha.Narayanan@intel.com");
34622d96aa5Sanantha MODULE_DESCRIPTION("Avago apds9802als ALS Driver");
34722d96aa5Sanantha MODULE_LICENSE("GPL v2");
348