xref: /openbmc/linux/drivers/misc/apds9802als.c (revision f050bb8f)
1873e65bcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
222d96aa5Sanantha /*
322d96aa5Sanantha  * apds9802als.c - apds9802  ALS Driver
422d96aa5Sanantha  *
522d96aa5Sanantha  * Copyright (C) 2009 Intel Corp
622d96aa5Sanantha  *
722d96aa5Sanantha  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
822d96aa5Sanantha  *
922d96aa5Sanantha  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1022d96aa5Sanantha  */
1122d96aa5Sanantha 
1222d96aa5Sanantha #include <linux/module.h>
1322d96aa5Sanantha #include <linux/slab.h>
1422d96aa5Sanantha #include <linux/i2c.h>
1522d96aa5Sanantha #include <linux/err.h>
1622d96aa5Sanantha #include <linux/delay.h>
1722d96aa5Sanantha #include <linux/mutex.h>
1822d96aa5Sanantha #include <linux/sysfs.h>
19f0cfec11SHong Liu #include <linux/pm_runtime.h>
2022d96aa5Sanantha 
2122d96aa5Sanantha #define ALS_MIN_RANGE_VAL 1
2222d96aa5Sanantha #define ALS_MAX_RANGE_VAL 2
2322d96aa5Sanantha #define POWER_STA_ENABLE 1
2422d96aa5Sanantha #define POWER_STA_DISABLE 0
2522d96aa5Sanantha 
2622d96aa5Sanantha #define DRIVER_NAME "apds9802als"
2722d96aa5Sanantha 
2822d96aa5Sanantha struct als_data {
2922d96aa5Sanantha 	struct mutex mutex;
3022d96aa5Sanantha };
3122d96aa5Sanantha 
als_sensing_range_show(struct device * dev,struct device_attribute * attr,char * buf)3222d96aa5Sanantha static ssize_t als_sensing_range_show(struct device *dev,
3322d96aa5Sanantha 			struct device_attribute *attr,  char *buf)
3422d96aa5Sanantha {
3522d96aa5Sanantha 	struct i2c_client *client = to_i2c_client(dev);
3622d96aa5Sanantha 	int  val;
3722d96aa5Sanantha 
3822d96aa5Sanantha 	val = i2c_smbus_read_byte_data(client, 0x81);
3922d96aa5Sanantha 	if (val < 0)
4022d96aa5Sanantha 		return val;
4122d96aa5Sanantha 	if (val & 1)
4222d96aa5Sanantha 		return sprintf(buf, "4095\n");
4322d96aa5Sanantha 	else
4422d96aa5Sanantha 		return sprintf(buf, "65535\n");
4522d96aa5Sanantha }
4622d96aa5Sanantha 
als_wait_for_data_ready(struct device * dev)47f0cfec11SHong Liu static int als_wait_for_data_ready(struct device *dev)
48f0cfec11SHong Liu {
49f0cfec11SHong Liu 	struct i2c_client *client = to_i2c_client(dev);
50f0cfec11SHong Liu 	int ret;
51f0cfec11SHong Liu 	int retry = 10;
52f0cfec11SHong Liu 
53f0cfec11SHong Liu 	do {
54f0cfec11SHong Liu 		msleep(30);
55f0cfec11SHong Liu 		ret = i2c_smbus_read_byte_data(client, 0x86);
56f0cfec11SHong Liu 	} while (!(ret & 0x80) && retry--);
57f0cfec11SHong Liu 
58644a9d3bSAxel Lin 	if (retry < 0) {
59f0cfec11SHong Liu 		dev_warn(dev, "timeout waiting for data ready\n");
60f0cfec11SHong Liu 		return -ETIMEDOUT;
61f0cfec11SHong Liu 	}
62f0cfec11SHong Liu 
63f0cfec11SHong Liu 	return 0;
64f0cfec11SHong Liu }
65f0cfec11SHong Liu 
als_lux0_input_data_show(struct device * dev,struct device_attribute * attr,char * buf)6622d96aa5Sanantha static ssize_t als_lux0_input_data_show(struct device *dev,
6722d96aa5Sanantha 			struct device_attribute *attr, char *buf)
6822d96aa5Sanantha {
6922d96aa5Sanantha 	struct i2c_client *client = to_i2c_client(dev);
7022d96aa5Sanantha 	struct als_data *data = i2c_get_clientdata(client);
71f0cfec11SHong Liu 	int ret_val;
7222d96aa5Sanantha 	int temp;
7322d96aa5Sanantha 
7422d96aa5Sanantha 	/* Protect against parallel reads */
75f0cfec11SHong Liu 	pm_runtime_get_sync(dev);
7622d96aa5Sanantha 	mutex_lock(&data->mutex);
77f0cfec11SHong Liu 
78f0cfec11SHong Liu 	/* clear EOC interrupt status */
79f0cfec11SHong Liu 	i2c_smbus_write_byte(client, 0x40);
80f0cfec11SHong Liu 	/* start measurement */
81f0cfec11SHong Liu 	temp = i2c_smbus_read_byte_data(client, 0x81);
82f0cfec11SHong Liu 	i2c_smbus_write_byte_data(client, 0x81, temp | 0x08);
83f0cfec11SHong Liu 
84f0cfec11SHong Liu 	ret_val = als_wait_for_data_ready(dev);
85f0cfec11SHong Liu 	if (ret_val < 0)
86f0cfec11SHong Liu 		goto failed;
87f0cfec11SHong Liu 
8822d96aa5Sanantha 	temp = i2c_smbus_read_byte_data(client, 0x8C); /* LSB data */
8922d96aa5Sanantha 	if (temp < 0) {
9022d96aa5Sanantha 		ret_val = temp;
9122d96aa5Sanantha 		goto failed;
9222d96aa5Sanantha 	}
9322d96aa5Sanantha 	ret_val = i2c_smbus_read_byte_data(client, 0x8D); /* MSB data */
9422d96aa5Sanantha 	if (ret_val < 0)
9522d96aa5Sanantha 		goto failed;
96f0cfec11SHong Liu 
9722d96aa5Sanantha 	mutex_unlock(&data->mutex);
98f0cfec11SHong Liu 	pm_runtime_put_sync(dev);
99f0cfec11SHong Liu 
100f0cfec11SHong Liu 	temp = (ret_val << 8) | temp;
101f0cfec11SHong Liu 	return sprintf(buf, "%d\n", temp);
10222d96aa5Sanantha failed:
10322d96aa5Sanantha 	mutex_unlock(&data->mutex);
104f0cfec11SHong Liu 	pm_runtime_put_sync(dev);
10522d96aa5Sanantha 	return ret_val;
10622d96aa5Sanantha }
10722d96aa5Sanantha 
als_sensing_range_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)10822d96aa5Sanantha static ssize_t als_sensing_range_store(struct device *dev,
10922d96aa5Sanantha 		struct device_attribute *attr, const  char *buf, size_t count)
11022d96aa5Sanantha {
11122d96aa5Sanantha 	struct i2c_client *client = to_i2c_client(dev);
11222d96aa5Sanantha 	struct als_data *data = i2c_get_clientdata(client);
1131093736bSVasiliy Kulikov 	int ret_val;
11422d96aa5Sanantha 	unsigned long val;
11522d96aa5Sanantha 
116f7b41276SJingoo Han 	ret_val = kstrtoul(buf, 10, &val);
117f7b41276SJingoo Han 	if (ret_val)
118f7b41276SJingoo Han 		return ret_val;
11922d96aa5Sanantha 
12022d96aa5Sanantha 	if (val < 4096)
12122d96aa5Sanantha 		val = 1;
12222d96aa5Sanantha 	else if (val < 65536)
12322d96aa5Sanantha 		val = 2;
12422d96aa5Sanantha 	else
12522d96aa5Sanantha 		return -ERANGE;
12622d96aa5Sanantha 
127f0cfec11SHong Liu 	pm_runtime_get_sync(dev);
128f0cfec11SHong Liu 
12922d96aa5Sanantha 	/* Make sure nobody else reads/modifies/writes 0x81 while we
13022d96aa5Sanantha 	   are active */
13122d96aa5Sanantha 	mutex_lock(&data->mutex);
13222d96aa5Sanantha 
13322d96aa5Sanantha 	ret_val = i2c_smbus_read_byte_data(client, 0x81);
13422d96aa5Sanantha 	if (ret_val < 0)
13522d96aa5Sanantha 		goto fail;
13622d96aa5Sanantha 
13722d96aa5Sanantha 	/* Reset the bits before setting them */
13822d96aa5Sanantha 	ret_val = ret_val & 0xFA;
13922d96aa5Sanantha 
140f0cfec11SHong Liu 	if (val == 1) /* Setting detection range up to 4k LUX */
141f0cfec11SHong Liu 		ret_val = (ret_val | 0x01);
142f0cfec11SHong Liu 	else /* Setting detection range up to 64k LUX*/
143f0cfec11SHong Liu 		ret_val = (ret_val | 0x00);
14422d96aa5Sanantha 
14522d96aa5Sanantha 	ret_val = i2c_smbus_write_byte_data(client, 0x81, ret_val);
146f0cfec11SHong Liu 
14722d96aa5Sanantha 	if (ret_val >= 0) {
14822d96aa5Sanantha 		/* All OK */
14922d96aa5Sanantha 		mutex_unlock(&data->mutex);
150f0cfec11SHong Liu 		pm_runtime_put_sync(dev);
15122d96aa5Sanantha 		return count;
15222d96aa5Sanantha 	}
15322d96aa5Sanantha fail:
15422d96aa5Sanantha 	mutex_unlock(&data->mutex);
155f0cfec11SHong Liu 	pm_runtime_put_sync(dev);
15622d96aa5Sanantha 	return ret_val;
15722d96aa5Sanantha }
15822d96aa5Sanantha 
als_set_power_state(struct i2c_client * client,bool on_off)15922d96aa5Sanantha static int als_set_power_state(struct i2c_client *client, bool on_off)
16022d96aa5Sanantha {
16122d96aa5Sanantha 	int ret_val;
16222d96aa5Sanantha 	struct als_data *data = i2c_get_clientdata(client);
16322d96aa5Sanantha 
16422d96aa5Sanantha 	mutex_lock(&data->mutex);
16522d96aa5Sanantha 	ret_val = i2c_smbus_read_byte_data(client, 0x80);
16622d96aa5Sanantha 	if (ret_val < 0)
16722d96aa5Sanantha 		goto fail;
16822d96aa5Sanantha 	if (on_off)
16922d96aa5Sanantha 		ret_val = ret_val | 0x01;
17022d96aa5Sanantha 	else
17122d96aa5Sanantha 		ret_val = ret_val & 0xFE;
17222d96aa5Sanantha 	ret_val = i2c_smbus_write_byte_data(client, 0x80, ret_val);
17322d96aa5Sanantha fail:
17422d96aa5Sanantha 	mutex_unlock(&data->mutex);
17522d96aa5Sanantha 	return ret_val;
17622d96aa5Sanantha }
17722d96aa5Sanantha 
17822d96aa5Sanantha static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR,
17922d96aa5Sanantha 	als_sensing_range_show, als_sensing_range_store);
18022d96aa5Sanantha static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux0_input_data_show, NULL);
18122d96aa5Sanantha 
18222d96aa5Sanantha static struct attribute *mid_att_als[] = {
18322d96aa5Sanantha 	&dev_attr_lux0_sensor_range.attr,
18422d96aa5Sanantha 	&dev_attr_lux0_input.attr,
18522d96aa5Sanantha 	NULL
18622d96aa5Sanantha };
18722d96aa5Sanantha 
188579e9a30SArvind Yadav static const struct attribute_group m_als_gr = {
18922d96aa5Sanantha 	.name = "apds9802als",
19022d96aa5Sanantha 	.attrs = mid_att_als
19122d96aa5Sanantha };
19222d96aa5Sanantha 
als_set_default_config(struct i2c_client * client)19322d96aa5Sanantha static int als_set_default_config(struct i2c_client *client)
19422d96aa5Sanantha {
19522d96aa5Sanantha 	int ret_val;
19622d96aa5Sanantha 	/* Write the command and then switch on */
19722d96aa5Sanantha 	ret_val = i2c_smbus_write_byte_data(client, 0x80, 0x01);
19822d96aa5Sanantha 	if (ret_val < 0) {
19922d96aa5Sanantha 		dev_err(&client->dev, "failed default switch on write\n");
20022d96aa5Sanantha 		return ret_val;
20122d96aa5Sanantha 	}
202f0cfec11SHong Liu 	/* detection range: 1~64K Lux, maunal measurement */
203f0cfec11SHong Liu 	ret_val = i2c_smbus_write_byte_data(client, 0x81, 0x08);
20422d96aa5Sanantha 	if (ret_val < 0)
20522d96aa5Sanantha 		dev_err(&client->dev, "failed default LUX on write\n");
206f0cfec11SHong Liu 
207f0cfec11SHong Liu 	/*  We always get 0 for the 1st measurement after system power on,
208f0cfec11SHong Liu 	 *  so make sure it is finished before user asks for data.
209f0cfec11SHong Liu 	 */
210f0cfec11SHong Liu 	als_wait_for_data_ready(&client->dev);
211f0cfec11SHong Liu 
21222d96aa5Sanantha 	return ret_val;
21322d96aa5Sanantha }
21422d96aa5Sanantha 
apds9802als_probe(struct i2c_client * client)2159f28b675SUwe Kleine-König static int apds9802als_probe(struct i2c_client *client)
21622d96aa5Sanantha {
21722d96aa5Sanantha 	int res;
21822d96aa5Sanantha 	struct als_data *data;
21922d96aa5Sanantha 
22022d96aa5Sanantha 	data = kzalloc(sizeof(struct als_data), GFP_KERNEL);
22122d96aa5Sanantha 	if (data == NULL) {
22222d96aa5Sanantha 		dev_err(&client->dev, "Memory allocation failed\n");
22322d96aa5Sanantha 		return -ENOMEM;
22422d96aa5Sanantha 	}
22522d96aa5Sanantha 	i2c_set_clientdata(client, data);
22622d96aa5Sanantha 	res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
22722d96aa5Sanantha 	if (res) {
22822d96aa5Sanantha 		dev_err(&client->dev, "device create file failed\n");
22922d96aa5Sanantha 		goto als_error1;
23022d96aa5Sanantha 	}
231f0cfec11SHong Liu 	dev_info(&client->dev, "ALS chip found\n");
23222d96aa5Sanantha 	als_set_default_config(client);
23322d96aa5Sanantha 	mutex_init(&data->mutex);
234f0cfec11SHong Liu 
2354e673599SHong Liu 	pm_runtime_set_active(&client->dev);
236f0cfec11SHong Liu 	pm_runtime_enable(&client->dev);
237f0cfec11SHong Liu 
23822d96aa5Sanantha 	return res;
23922d96aa5Sanantha als_error1:
24022d96aa5Sanantha 	kfree(data);
24122d96aa5Sanantha 	return res;
24222d96aa5Sanantha }
24322d96aa5Sanantha 
apds9802als_remove(struct i2c_client * client)244ed5c2f5fSUwe Kleine-König static void apds9802als_remove(struct i2c_client *client)
24522d96aa5Sanantha {
24622d96aa5Sanantha 	struct als_data *data = i2c_get_clientdata(client);
247f0cfec11SHong Liu 
2484e673599SHong Liu 	pm_runtime_get_sync(&client->dev);
2494e673599SHong Liu 
250f0cfec11SHong Liu 	als_set_power_state(client, false);
25122d96aa5Sanantha 	sysfs_remove_group(&client->dev.kobj, &m_als_gr);
2524e673599SHong Liu 
2534e673599SHong Liu 	pm_runtime_disable(&client->dev);
2544e673599SHong Liu 	pm_runtime_set_suspended(&client->dev);
2554e673599SHong Liu 	pm_runtime_put_noidle(&client->dev);
2564e673599SHong Liu 
25722d96aa5Sanantha 	kfree(data);
25822d96aa5Sanantha }
25922d96aa5Sanantha 
260f0cfec11SHong Liu #ifdef CONFIG_PM
26122d96aa5Sanantha 
apds9802als_suspend(struct device * dev)2621c9354b0SLars-Peter Clausen static int apds9802als_suspend(struct device *dev)
263f0cfec11SHong Liu {
264f0cfec11SHong Liu 	struct i2c_client *client = to_i2c_client(dev);
265f0cfec11SHong Liu 
266f0cfec11SHong Liu 	als_set_power_state(client, false);
267f0cfec11SHong Liu 	return 0;
268f0cfec11SHong Liu }
269f0cfec11SHong Liu 
apds9802als_resume(struct device * dev)2701c9354b0SLars-Peter Clausen static int apds9802als_resume(struct device *dev)
271f0cfec11SHong Liu {
272f0cfec11SHong Liu 	struct i2c_client *client = to_i2c_client(dev);
273f0cfec11SHong Liu 
27422d96aa5Sanantha 	als_set_power_state(client, true);
27522d96aa5Sanantha 	return 0;
27622d96aa5Sanantha }
27722d96aa5Sanantha 
2781c9354b0SLars-Peter Clausen static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend,
2791c9354b0SLars-Peter Clausen 	apds9802als_resume, NULL);
280f0cfec11SHong Liu 
281f0cfec11SHong Liu #define APDS9802ALS_PM_OPS (&apds9802als_pm_ops)
282f0cfec11SHong Liu 
283f0cfec11SHong Liu #else	/* CONFIG_PM */
284f0cfec11SHong Liu #define APDS9802ALS_PM_OPS NULL
285f0cfec11SHong Liu #endif	/* CONFIG_PM */
286f0cfec11SHong Liu 
287006dbb38SArvind Yadav static const struct i2c_device_id apds9802als_id[] = {
28822d96aa5Sanantha 	{ DRIVER_NAME, 0 },
28922d96aa5Sanantha 	{ }
29022d96aa5Sanantha };
29122d96aa5Sanantha 
29222d96aa5Sanantha MODULE_DEVICE_TABLE(i2c, apds9802als_id);
29322d96aa5Sanantha 
29422d96aa5Sanantha static struct i2c_driver apds9802als_driver = {
29522d96aa5Sanantha 	.driver = {
29622d96aa5Sanantha 		.name = DRIVER_NAME,
297f0cfec11SHong Liu 		.pm = APDS9802ALS_PM_OPS,
29822d96aa5Sanantha 	},
299*f050bb8fSUwe Kleine-König 	.probe = apds9802als_probe,
3002d6bed9cSBill Pemberton 	.remove = apds9802als_remove,
30122d96aa5Sanantha 	.id_table = apds9802als_id,
30222d96aa5Sanantha };
30322d96aa5Sanantha 
304a64fe2edSAxel Lin module_i2c_driver(apds9802als_driver);
30522d96aa5Sanantha 
30622d96aa5Sanantha MODULE_AUTHOR("Anantha Narayanan <Anantha.Narayanan@intel.com");
30722d96aa5Sanantha MODULE_DESCRIPTION("Avago apds9802als ALS Driver");
30822d96aa5Sanantha MODULE_LICENSE("GPL v2");
309