18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * isl29020.c - Intersil ALS Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Intel Corp 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Data sheet at: http://www.intersil.com/data/fn/fn6505.pdf 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/i2c.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 208c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(mutex); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic ssize_t als_sensing_range_show(struct device *dev, 258c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 288c2ecf20Sopenharmony_ci int val; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci val = i2c_smbus_read_byte_data(client, 0x00); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (val < 0) 338c2ecf20Sopenharmony_ci return val; 348c2ecf20Sopenharmony_ci return sprintf(buf, "%d000\n", 1 << (2 * (val & 3))); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic ssize_t als_lux_input_data_show(struct device *dev, 398c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 428c2ecf20Sopenharmony_ci int ret_val, val; 438c2ecf20Sopenharmony_ci unsigned long int lux; 448c2ecf20Sopenharmony_ci int temp; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 478c2ecf20Sopenharmony_ci msleep(100); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci mutex_lock(&mutex); 508c2ecf20Sopenharmony_ci temp = i2c_smbus_read_byte_data(client, 0x02); /* MSB data */ 518c2ecf20Sopenharmony_ci if (temp < 0) { 528c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 538c2ecf20Sopenharmony_ci mutex_unlock(&mutex); 548c2ecf20Sopenharmony_ci return temp; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci ret_val = i2c_smbus_read_byte_data(client, 0x01); /* LSB data */ 588c2ecf20Sopenharmony_ci mutex_unlock(&mutex); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (ret_val < 0) { 618c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 628c2ecf20Sopenharmony_ci return ret_val; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ret_val |= temp << 8; 668c2ecf20Sopenharmony_ci val = i2c_smbus_read_byte_data(client, 0x00); 678c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 688c2ecf20Sopenharmony_ci if (val < 0) 698c2ecf20Sopenharmony_ci return val; 708c2ecf20Sopenharmony_ci lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / 65536; 718c2ecf20Sopenharmony_ci return sprintf(buf, "%ld\n", lux); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic ssize_t als_sensing_range_store(struct device *dev, 758c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 788c2ecf20Sopenharmony_ci int ret_val; 798c2ecf20Sopenharmony_ci unsigned long val; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ret_val = kstrtoul(buf, 10, &val); 828c2ecf20Sopenharmony_ci if (ret_val) 838c2ecf20Sopenharmony_ci return ret_val; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (val < 1 || val > 64000) 868c2ecf20Sopenharmony_ci return -EINVAL; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* Pick the smallest sensor range that will meet our requirements */ 898c2ecf20Sopenharmony_ci if (val <= 1000) 908c2ecf20Sopenharmony_ci val = 1; 918c2ecf20Sopenharmony_ci else if (val <= 4000) 928c2ecf20Sopenharmony_ci val = 2; 938c2ecf20Sopenharmony_ci else if (val <= 16000) 948c2ecf20Sopenharmony_ci val = 3; 958c2ecf20Sopenharmony_ci else 968c2ecf20Sopenharmony_ci val = 4; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci ret_val = i2c_smbus_read_byte_data(client, 0x00); 998c2ecf20Sopenharmony_ci if (ret_val < 0) 1008c2ecf20Sopenharmony_ci return ret_val; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci ret_val &= 0xFC; /*reset the bit before setting them */ 1038c2ecf20Sopenharmony_ci ret_val |= val - 1; 1048c2ecf20Sopenharmony_ci ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (ret_val < 0) 1078c2ecf20Sopenharmony_ci return ret_val; 1088c2ecf20Sopenharmony_ci return count; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void als_set_power_state(struct i2c_client *client, int enable) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci int ret_val; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ret_val = i2c_smbus_read_byte_data(client, 0x00); 1168c2ecf20Sopenharmony_ci if (ret_val < 0) 1178c2ecf20Sopenharmony_ci return; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (enable) 1208c2ecf20Sopenharmony_ci ret_val |= 0x80; 1218c2ecf20Sopenharmony_ci else 1228c2ecf20Sopenharmony_ci ret_val &= 0x7F; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(client, 0x00, ret_val); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR, 1288c2ecf20Sopenharmony_ci als_sensing_range_show, als_sensing_range_store); 1298c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic struct attribute *mid_att_als[] = { 1328c2ecf20Sopenharmony_ci &dev_attr_lux0_sensor_range.attr, 1338c2ecf20Sopenharmony_ci &dev_attr_lux0_input.attr, 1348c2ecf20Sopenharmony_ci NULL 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic const struct attribute_group m_als_gr = { 1388c2ecf20Sopenharmony_ci .name = "isl29020", 1398c2ecf20Sopenharmony_ci .attrs = mid_att_als 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int als_set_default_config(struct i2c_client *client) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci int retval; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0); 1478c2ecf20Sopenharmony_ci if (retval < 0) { 1488c2ecf20Sopenharmony_ci dev_err(&client->dev, "default write failed."); 1498c2ecf20Sopenharmony_ci return retval; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int isl29020_probe(struct i2c_client *client, 1558c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int res; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci res = als_set_default_config(client); 1608c2ecf20Sopenharmony_ci if (res < 0) 1618c2ecf20Sopenharmony_ci return res; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci res = sysfs_create_group(&client->dev.kobj, &m_als_gr); 1648c2ecf20Sopenharmony_ci if (res) { 1658c2ecf20Sopenharmony_ci dev_err(&client->dev, "isl29020: device create file failed\n"); 1668c2ecf20Sopenharmony_ci return res; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name); 1698c2ecf20Sopenharmony_ci als_set_power_state(client, 0); 1708c2ecf20Sopenharmony_ci pm_runtime_enable(&client->dev); 1718c2ecf20Sopenharmony_ci return res; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int isl29020_remove(struct i2c_client *client) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci pm_runtime_disable(&client->dev); 1778c2ecf20Sopenharmony_ci sysfs_remove_group(&client->dev.kobj, &m_als_gr); 1788c2ecf20Sopenharmony_ci return 0; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic const struct i2c_device_id isl29020_id[] = { 1828c2ecf20Sopenharmony_ci { "isl29020", 0 }, 1838c2ecf20Sopenharmony_ci { } 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, isl29020_id); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int isl29020_runtime_suspend(struct device *dev) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 1938c2ecf20Sopenharmony_ci als_set_power_state(client, 0); 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int isl29020_runtime_resume(struct device *dev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 2008c2ecf20Sopenharmony_ci als_set_power_state(client, 1); 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic const struct dev_pm_ops isl29020_pm_ops = { 2058c2ecf20Sopenharmony_ci .runtime_suspend = isl29020_runtime_suspend, 2068c2ecf20Sopenharmony_ci .runtime_resume = isl29020_runtime_resume, 2078c2ecf20Sopenharmony_ci}; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#define ISL29020_PM_OPS (&isl29020_pm_ops) 2108c2ecf20Sopenharmony_ci#else /* CONFIG_PM */ 2118c2ecf20Sopenharmony_ci#define ISL29020_PM_OPS NULL 2128c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic struct i2c_driver isl29020_driver = { 2158c2ecf20Sopenharmony_ci .driver = { 2168c2ecf20Sopenharmony_ci .name = "isl29020", 2178c2ecf20Sopenharmony_ci .pm = ISL29020_PM_OPS, 2188c2ecf20Sopenharmony_ci }, 2198c2ecf20Sopenharmony_ci .probe = isl29020_probe, 2208c2ecf20Sopenharmony_ci .remove = isl29020_remove, 2218c2ecf20Sopenharmony_ci .id_table = isl29020_id, 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cimodule_i2c_driver(isl29020_driver); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com>"); 2278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intersil isl29020 ALS Driver"); 2288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 229