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