xref: /openbmc/linux/drivers/misc/isl29020.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1873e65bcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22e85c4ddSKalhan Trisal /*
32e85c4ddSKalhan Trisal  * isl29020.c - Intersil  ALS Driver
42e85c4ddSKalhan Trisal  *
52e85c4ddSKalhan Trisal  * Copyright (C) 2008 Intel Corp
62e85c4ddSKalhan Trisal  *
72e85c4ddSKalhan Trisal  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
82e85c4ddSKalhan Trisal  *
92e85c4ddSKalhan Trisal  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
102e85c4ddSKalhan Trisal  *
112e85c4ddSKalhan Trisal  * Data sheet at: http://www.intersil.com/data/fn/fn6505.pdf
122e85c4ddSKalhan Trisal  */
132e85c4ddSKalhan Trisal 
142e85c4ddSKalhan Trisal #include <linux/module.h>
152e85c4ddSKalhan Trisal #include <linux/slab.h>
162e85c4ddSKalhan Trisal #include <linux/i2c.h>
172e85c4ddSKalhan Trisal #include <linux/err.h>
182e85c4ddSKalhan Trisal #include <linux/delay.h>
192e85c4ddSKalhan Trisal #include <linux/sysfs.h>
202e85c4ddSKalhan Trisal #include <linux/pm_runtime.h>
212e85c4ddSKalhan Trisal 
222e85c4ddSKalhan Trisal static DEFINE_MUTEX(mutex);
232e85c4ddSKalhan Trisal 
als_sensing_range_show(struct device * dev,struct device_attribute * attr,char * buf)242e85c4ddSKalhan Trisal static ssize_t als_sensing_range_show(struct device *dev,
252e85c4ddSKalhan Trisal 			struct device_attribute *attr,  char *buf)
262e85c4ddSKalhan Trisal {
272e85c4ddSKalhan Trisal 	struct i2c_client *client = to_i2c_client(dev);
282e85c4ddSKalhan Trisal 	int  val;
292e85c4ddSKalhan Trisal 
302e85c4ddSKalhan Trisal 	val = i2c_smbus_read_byte_data(client, 0x00);
312e85c4ddSKalhan Trisal 
322e85c4ddSKalhan Trisal 	if (val < 0)
332e85c4ddSKalhan Trisal 		return val;
342e85c4ddSKalhan Trisal 	return sprintf(buf, "%d000\n", 1 << (2 * (val & 3)));
352e85c4ddSKalhan Trisal 
362e85c4ddSKalhan Trisal }
372e85c4ddSKalhan Trisal 
als_lux_input_data_show(struct device * dev,struct device_attribute * attr,char * buf)382e85c4ddSKalhan Trisal static ssize_t als_lux_input_data_show(struct device *dev,
392e85c4ddSKalhan Trisal 			struct device_attribute *attr, char *buf)
402e85c4ddSKalhan Trisal {
412e85c4ddSKalhan Trisal 	struct i2c_client *client = to_i2c_client(dev);
422e85c4ddSKalhan Trisal 	int ret_val, val;
432e85c4ddSKalhan Trisal 	unsigned long int lux;
442e85c4ddSKalhan Trisal 	int temp;
452e85c4ddSKalhan Trisal 
462e85c4ddSKalhan Trisal 	pm_runtime_get_sync(dev);
472e85c4ddSKalhan Trisal 	msleep(100);
482e85c4ddSKalhan Trisal 
492e85c4ddSKalhan Trisal 	mutex_lock(&mutex);
502e85c4ddSKalhan Trisal 	temp = i2c_smbus_read_byte_data(client, 0x02); /* MSB data */
512e85c4ddSKalhan Trisal 	if (temp < 0) {
522e85c4ddSKalhan Trisal 		pm_runtime_put_sync(dev);
532e85c4ddSKalhan Trisal 		mutex_unlock(&mutex);
542e85c4ddSKalhan Trisal 		return temp;
552e85c4ddSKalhan Trisal 	}
562e85c4ddSKalhan Trisal 
572e85c4ddSKalhan Trisal 	ret_val = i2c_smbus_read_byte_data(client, 0x01); /* LSB data */
582e85c4ddSKalhan Trisal 	mutex_unlock(&mutex);
592e85c4ddSKalhan Trisal 
602e85c4ddSKalhan Trisal 	if (ret_val < 0) {
612e85c4ddSKalhan Trisal 		pm_runtime_put_sync(dev);
622e85c4ddSKalhan Trisal 		return ret_val;
632e85c4ddSKalhan Trisal 	}
642e85c4ddSKalhan Trisal 
652e85c4ddSKalhan Trisal 	ret_val |= temp << 8;
662e85c4ddSKalhan Trisal 	val = i2c_smbus_read_byte_data(client, 0x00);
672e85c4ddSKalhan Trisal 	pm_runtime_put_sync(dev);
682e85c4ddSKalhan Trisal 	if (val < 0)
692e85c4ddSKalhan Trisal 		return val;
702e85c4ddSKalhan Trisal 	lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / 65536;
712e85c4ddSKalhan Trisal 	return sprintf(buf, "%ld\n", lux);
722e85c4ddSKalhan Trisal }
732e85c4ddSKalhan Trisal 
als_sensing_range_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)742e85c4ddSKalhan Trisal static ssize_t als_sensing_range_store(struct device *dev,
752e85c4ddSKalhan Trisal 		struct device_attribute *attr, const  char *buf, size_t count)
762e85c4ddSKalhan Trisal {
772e85c4ddSKalhan Trisal 	struct i2c_client *client = to_i2c_client(dev);
7890482e45SDan Carpenter 	int ret_val;
792e85c4ddSKalhan Trisal 	unsigned long val;
802e85c4ddSKalhan Trisal 
81f7b41276SJingoo Han 	ret_val = kstrtoul(buf, 10, &val);
82f7b41276SJingoo Han 	if (ret_val)
83f7b41276SJingoo Han 		return ret_val;
84f7b41276SJingoo Han 
852e85c4ddSKalhan Trisal 	if (val < 1 || val > 64000)
862e85c4ddSKalhan Trisal 		return -EINVAL;
872e85c4ddSKalhan Trisal 
882e85c4ddSKalhan Trisal 	/* Pick the smallest sensor range that will meet our requirements */
892e85c4ddSKalhan Trisal 	if (val <= 1000)
902e85c4ddSKalhan Trisal 		val = 1;
912e85c4ddSKalhan Trisal 	else if (val <= 4000)
922e85c4ddSKalhan Trisal 		val = 2;
932e85c4ddSKalhan Trisal 	else if (val <= 16000)
942e85c4ddSKalhan Trisal 		val = 3;
952e85c4ddSKalhan Trisal 	else
962e85c4ddSKalhan Trisal 		val = 4;
972e85c4ddSKalhan Trisal 
982e85c4ddSKalhan Trisal 	ret_val = i2c_smbus_read_byte_data(client, 0x00);
9990482e45SDan Carpenter 	if (ret_val < 0)
10090482e45SDan Carpenter 		return ret_val;
1012e85c4ddSKalhan Trisal 
1022e85c4ddSKalhan Trisal 	ret_val &= 0xFC; /*reset the bit before setting them */
1032e85c4ddSKalhan Trisal 	ret_val |= val - 1;
1042e85c4ddSKalhan Trisal 	ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val);
1052e85c4ddSKalhan Trisal 
1062e85c4ddSKalhan Trisal 	if (ret_val < 0)
1072e85c4ddSKalhan Trisal 		return ret_val;
1082e85c4ddSKalhan Trisal 	return count;
1092e85c4ddSKalhan Trisal }
1102e85c4ddSKalhan Trisal 
als_set_power_state(struct i2c_client * client,int enable)1112e85c4ddSKalhan Trisal static void als_set_power_state(struct i2c_client *client, int enable)
1122e85c4ddSKalhan Trisal {
1132e85c4ddSKalhan Trisal 	int ret_val;
1142e85c4ddSKalhan Trisal 
1152e85c4ddSKalhan Trisal 	ret_val = i2c_smbus_read_byte_data(client, 0x00);
1162e85c4ddSKalhan Trisal 	if (ret_val < 0)
1172e85c4ddSKalhan Trisal 		return;
1182e85c4ddSKalhan Trisal 
1192e85c4ddSKalhan Trisal 	if (enable)
1202e85c4ddSKalhan Trisal 		ret_val |= 0x80;
1212e85c4ddSKalhan Trisal 	else
1222e85c4ddSKalhan Trisal 		ret_val &= 0x7F;
1232e85c4ddSKalhan Trisal 
1242e85c4ddSKalhan Trisal 	i2c_smbus_write_byte_data(client, 0x00, ret_val);
1252e85c4ddSKalhan Trisal }
1262e85c4ddSKalhan Trisal 
1272e85c4ddSKalhan Trisal static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR,
1282e85c4ddSKalhan Trisal 	als_sensing_range_show, als_sensing_range_store);
1292e85c4ddSKalhan Trisal static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL);
1302e85c4ddSKalhan Trisal 
1312e85c4ddSKalhan Trisal static struct attribute *mid_att_als[] = {
1322e85c4ddSKalhan Trisal 	&dev_attr_lux0_sensor_range.attr,
1332e85c4ddSKalhan Trisal 	&dev_attr_lux0_input.attr,
1342e85c4ddSKalhan Trisal 	NULL
1352e85c4ddSKalhan Trisal };
1362e85c4ddSKalhan Trisal 
13764e6d2c1SArvind Yadav static const struct attribute_group m_als_gr = {
1382e85c4ddSKalhan Trisal 	.name = "isl29020",
1392e85c4ddSKalhan Trisal 	.attrs = mid_att_als
1402e85c4ddSKalhan Trisal };
1412e85c4ddSKalhan Trisal 
als_set_default_config(struct i2c_client * client)1422e85c4ddSKalhan Trisal static int als_set_default_config(struct i2c_client *client)
1432e85c4ddSKalhan Trisal {
1442e85c4ddSKalhan Trisal 	int retval;
1452e85c4ddSKalhan Trisal 
1462e85c4ddSKalhan Trisal 	retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0);
1472e85c4ddSKalhan Trisal 	if (retval < 0) {
1482e85c4ddSKalhan Trisal 		dev_err(&client->dev, "default write failed.");
1492e85c4ddSKalhan Trisal 		return retval;
1502e85c4ddSKalhan Trisal 	}
1512a5ac6f7SJesper Juhl 	return 0;
1522e85c4ddSKalhan Trisal }
1532e85c4ddSKalhan Trisal 
isl29020_probe(struct i2c_client * client)154327e1ad1SUwe Kleine-König static int  isl29020_probe(struct i2c_client *client)
1552e85c4ddSKalhan Trisal {
1562e85c4ddSKalhan Trisal 	int res;
1572e85c4ddSKalhan Trisal 
1582e85c4ddSKalhan Trisal 	res = als_set_default_config(client);
1592e85c4ddSKalhan Trisal 	if (res <  0)
1602e85c4ddSKalhan Trisal 		return res;
1612e85c4ddSKalhan Trisal 
1622e85c4ddSKalhan Trisal 	res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
1632e85c4ddSKalhan Trisal 	if (res) {
1642e85c4ddSKalhan Trisal 		dev_err(&client->dev, "isl29020: device create file failed\n");
1652e85c4ddSKalhan Trisal 		return res;
1662e85c4ddSKalhan Trisal 	}
1672e85c4ddSKalhan Trisal 	dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name);
1682e85c4ddSKalhan Trisal 	als_set_power_state(client, 0);
1692e85c4ddSKalhan Trisal 	pm_runtime_enable(&client->dev);
1702e85c4ddSKalhan Trisal 	return res;
1712e85c4ddSKalhan Trisal }
1722e85c4ddSKalhan Trisal 
isl29020_remove(struct i2c_client * client)173ed5c2f5fSUwe Kleine-König static void isl29020_remove(struct i2c_client *client)
1742e85c4ddSKalhan Trisal {
175efb5bea6SChuhong Yuan 	pm_runtime_disable(&client->dev);
1762e85c4ddSKalhan Trisal 	sysfs_remove_group(&client->dev.kobj, &m_als_gr);
1772e85c4ddSKalhan Trisal }
1782e85c4ddSKalhan Trisal 
17907e4049aSArvind Yadav static const struct i2c_device_id isl29020_id[] = {
1802e85c4ddSKalhan Trisal 	{ "isl29020", 0 },
1812e85c4ddSKalhan Trisal 	{ }
1822e85c4ddSKalhan Trisal };
1832e85c4ddSKalhan Trisal 
1842e85c4ddSKalhan Trisal MODULE_DEVICE_TABLE(i2c, isl29020_id);
1852e85c4ddSKalhan Trisal 
1862e85c4ddSKalhan Trisal #ifdef CONFIG_PM
1872e85c4ddSKalhan Trisal 
isl29020_runtime_suspend(struct device * dev)1882e85c4ddSKalhan Trisal static int isl29020_runtime_suspend(struct device *dev)
1892e85c4ddSKalhan Trisal {
1902e85c4ddSKalhan Trisal 	struct i2c_client *client = to_i2c_client(dev);
1912e85c4ddSKalhan Trisal 	als_set_power_state(client, 0);
1922e85c4ddSKalhan Trisal 	return 0;
1932e85c4ddSKalhan Trisal }
1942e85c4ddSKalhan Trisal 
isl29020_runtime_resume(struct device * dev)1952e85c4ddSKalhan Trisal static int isl29020_runtime_resume(struct device *dev)
1962e85c4ddSKalhan Trisal {
1972e85c4ddSKalhan Trisal 	struct i2c_client *client = to_i2c_client(dev);
1982e85c4ddSKalhan Trisal 	als_set_power_state(client, 1);
1992e85c4ddSKalhan Trisal 	return 0;
2002e85c4ddSKalhan Trisal }
2012e85c4ddSKalhan Trisal 
2022e85c4ddSKalhan Trisal static const struct dev_pm_ops isl29020_pm_ops = {
2032e85c4ddSKalhan Trisal 	.runtime_suspend = isl29020_runtime_suspend,
2042e85c4ddSKalhan Trisal 	.runtime_resume = isl29020_runtime_resume,
2052e85c4ddSKalhan Trisal };
2062e85c4ddSKalhan Trisal 
2072e85c4ddSKalhan Trisal #define ISL29020_PM_OPS (&isl29020_pm_ops)
2082e85c4ddSKalhan Trisal #else	/* CONFIG_PM */
2092e85c4ddSKalhan Trisal #define ISL29020_PM_OPS NULL
2102e85c4ddSKalhan Trisal #endif	/* CONFIG_PM */
2112e85c4ddSKalhan Trisal 
2122e85c4ddSKalhan Trisal static struct i2c_driver isl29020_driver = {
2132e85c4ddSKalhan Trisal 	.driver = {
2142e85c4ddSKalhan Trisal 		.name = "isl29020",
2152e85c4ddSKalhan Trisal 		.pm = ISL29020_PM_OPS,
2162e85c4ddSKalhan Trisal 	},
217*f050bb8fSUwe Kleine-König 	.probe = isl29020_probe,
2182e85c4ddSKalhan Trisal 	.remove = isl29020_remove,
2192e85c4ddSKalhan Trisal 	.id_table = isl29020_id,
2202e85c4ddSKalhan Trisal };
2212e85c4ddSKalhan Trisal 
222a64fe2edSAxel Lin module_i2c_driver(isl29020_driver);
2232e85c4ddSKalhan Trisal 
224b38eeaaeSAxel Lin MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com>");
2252e85c4ddSKalhan Trisal MODULE_DESCRIPTION("Intersil isl29020 ALS Driver");
2262e85c4ddSKalhan Trisal MODULE_LICENSE("GPL v2");
227