Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers/misc/apds9802als.c: ALS drivers for the apds9802als

This adds support for the ADPS9802ALS sensor.

Cleanup by Alan Cox
- move mutexes to cover more things
- report I/O errors back to user space
- report range and values in LUX

Signed-off-by: Anantha Narayanan <anantha.narayanan@intel.com>
[The 4K and 64K in the hw spec actually means 4095 (12bit) and 65535 (16bit).]
Signed-off-by: Hong Liu <hong.liu@intel.com>
[Updated to match the ALS light API interface convention from Samu]
Signed-off-by: Alan Cox <alan@linux.intel.com>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

anantha and committed by
Linus Torvalds
22d96aa5 8a233f01

+319
+10
drivers/misc/Kconfig
··· 285 285 This option enables addition debugging code for the SGI GRU driver. If 286 286 you are unsure, say N. 287 287 288 + config APDS9802ALS 289 + tristate "Medfield Avago APDS9802 ALS Sensor module" 290 + depends on I2C 291 + help 292 + If you say yes here you get support for the ALS APDS9802 ambient 293 + light sensor. 294 + 295 + This driver can also be built as a module. If so, the module 296 + will be called apds9802als. 297 + 288 298 config ISL29003 289 299 tristate "Intersil ISL29003 ambient light sensor" 290 300 depends on I2C && SYSFS
+1
drivers/misc/Makefile
··· 25 25 obj-$(CONFIG_SGI_GRU) += sgi-gru/ 26 26 obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o 27 27 obj-$(CONFIG_HP_ILO) += hpilo.o 28 + obj-$(CONFIG_APDS9802ALS) += apds9802als.o 28 29 obj-$(CONFIG_ISL29003) += isl29003.o 29 30 obj-$(CONFIG_ISL29020) += isl29020.o 30 31 obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
+308
drivers/misc/apds9802als.c
··· 1 + /* 2 + * apds9802als.c - apds9802 ALS Driver 3 + * 4 + * Copyright (C) 2009 Intel Corp 5 + * 6 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License as published by 10 + * the Free Software Foundation; version 2 of the License. 11 + * 12 + * This program is distributed in the hope that it will be useful, but 13 + * WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 + * General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License along 18 + * with this program; if not, write to the Free Software Foundation, Inc., 19 + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 21 + * 22 + */ 23 + 24 + #include <linux/module.h> 25 + #include <linux/init.h> 26 + #include <linux/slab.h> 27 + #include <linux/i2c.h> 28 + #include <linux/err.h> 29 + #include <linux/delay.h> 30 + #include <linux/mutex.h> 31 + #include <linux/sysfs.h> 32 + #include <linux/hwmon-sysfs.h> 33 + 34 + #define ALS_MIN_RANGE_VAL 1 35 + #define ALS_MAX_RANGE_VAL 2 36 + #define POWER_STA_ENABLE 1 37 + #define POWER_STA_DISABLE 0 38 + #define APDS9802ALS_I2C_ADDR 0x29 39 + 40 + #define DRIVER_NAME "apds9802als" 41 + 42 + struct als_data { 43 + struct device *hwmon_dev; 44 + bool needresume; 45 + struct mutex mutex; 46 + }; 47 + 48 + static ssize_t als_sensing_range_show(struct device *dev, 49 + struct device_attribute *attr, char *buf) 50 + { 51 + struct i2c_client *client = to_i2c_client(dev); 52 + int val; 53 + 54 + val = i2c_smbus_read_byte_data(client, 0x81); 55 + if (val < 0) 56 + return val; 57 + if (val & 1) 58 + return sprintf(buf, "4095\n"); 59 + else 60 + return sprintf(buf, "65535\n"); 61 + } 62 + 63 + static ssize_t als_lux0_input_data_show(struct device *dev, 64 + struct device_attribute *attr, char *buf) 65 + { 66 + struct i2c_client *client = to_i2c_client(dev); 67 + struct als_data *data = i2c_get_clientdata(client); 68 + unsigned int ret_val; 69 + int temp; 70 + 71 + /* Protect against parallel reads */ 72 + mutex_lock(&data->mutex); 73 + temp = i2c_smbus_read_byte_data(client, 0x8C);/*LSB data*/ 74 + if (temp < 0) { 75 + ret_val = temp; 76 + goto failed; 77 + } 78 + ret_val = i2c_smbus_read_byte_data(client, 0x8D);/*MSB data*/ 79 + if (ret_val < 0) 80 + goto failed; 81 + mutex_unlock(&data->mutex); 82 + ret_val = (ret_val << 8) | temp; 83 + return sprintf(buf, "%d\n", ret_val); 84 + failed: 85 + mutex_unlock(&data->mutex); 86 + return ret_val; 87 + } 88 + 89 + static ssize_t als_sensing_range_store(struct device *dev, 90 + struct device_attribute *attr, const char *buf, size_t count) 91 + { 92 + struct i2c_client *client = to_i2c_client(dev); 93 + struct als_data *data = i2c_get_clientdata(client); 94 + unsigned int ret_val; 95 + unsigned long val; 96 + 97 + if (strict_strtoul(buf, 10, &val)) 98 + return -EINVAL; 99 + 100 + if (val < 4096) 101 + val = 1; 102 + else if (val < 65536) 103 + val = 2; 104 + else 105 + return -ERANGE; 106 + 107 + /* Make sure nobody else reads/modifies/writes 0x81 while we 108 + are active */ 109 + 110 + mutex_lock(&data->mutex); 111 + 112 + ret_val = i2c_smbus_read_byte_data(client, 0x81); 113 + if (ret_val < 0) 114 + goto fail; 115 + 116 + /* Reset the bits before setting them */ 117 + ret_val = ret_val & 0xFA; 118 + 119 + if (val == 1) /* Setting the continous measurement up to 4k LUX */ 120 + ret_val = (ret_val | 0x05); 121 + else /* Setting the continous measurement up to 64k LUX*/ 122 + ret_val = (ret_val | 0x04); 123 + 124 + ret_val = i2c_smbus_write_byte_data(client, 0x81, ret_val); 125 + if (ret_val >= 0) { 126 + /* All OK */ 127 + mutex_unlock(&data->mutex); 128 + return count; 129 + } 130 + fail: 131 + mutex_unlock(&data->mutex); 132 + return ret_val; 133 + } 134 + 135 + static ssize_t als_power_status_show(struct device *dev, 136 + struct device_attribute *attr, char *buf) 137 + { 138 + struct i2c_client *client = to_i2c_client(dev); 139 + int ret_val; 140 + ret_val = i2c_smbus_read_byte_data(client, 0x80); 141 + if (ret_val < 0) 142 + return ret_val; 143 + ret_val = ret_val & 0x01; 144 + return sprintf(buf, "%d\n", ret_val); 145 + } 146 + 147 + static int als_set_power_state(struct i2c_client *client, bool on_off) 148 + { 149 + int ret_val; 150 + struct als_data *data = i2c_get_clientdata(client); 151 + 152 + mutex_lock(&data->mutex); 153 + ret_val = i2c_smbus_read_byte_data(client, 0x80); 154 + if (ret_val < 0) 155 + goto fail; 156 + if (on_off) 157 + ret_val = ret_val | 0x01; 158 + else 159 + ret_val = ret_val & 0xFE; 160 + ret_val = i2c_smbus_write_byte_data(client, 0x80, ret_val); 161 + fail: 162 + mutex_unlock(&data->mutex); 163 + return ret_val; 164 + } 165 + 166 + static ssize_t als_power_status_store(struct device *dev, 167 + struct device_attribute *attr, const char *buf, size_t count) 168 + { 169 + struct i2c_client *client = to_i2c_client(dev); 170 + struct als_data *data = i2c_get_clientdata(client); 171 + unsigned long val; 172 + int ret_val; 173 + 174 + if (strict_strtoul(buf, 10, &val)) 175 + return -EINVAL; 176 + if (val == POWER_STA_ENABLE) { 177 + ret_val = als_set_power_state(client, true); 178 + data->needresume = true; 179 + } else if (val == POWER_STA_DISABLE) { 180 + ret_val = als_set_power_state(client, false); 181 + data->needresume = false; 182 + } else 183 + return -EINVAL; 184 + if (ret_val < 0) 185 + return ret_val; 186 + return count; 187 + } 188 + 189 + static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR, 190 + als_sensing_range_show, als_sensing_range_store); 191 + static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux0_input_data_show, NULL); 192 + static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, 193 + als_power_status_show, als_power_status_store); 194 + 195 + static struct attribute *mid_att_als[] = { 196 + &dev_attr_lux0_sensor_range.attr, 197 + &dev_attr_lux0_input.attr, 198 + &dev_attr_power_state.attr, 199 + NULL 200 + }; 201 + 202 + static struct attribute_group m_als_gr = { 203 + .name = "apds9802als", 204 + .attrs = mid_att_als 205 + }; 206 + 207 + static int als_set_default_config(struct i2c_client *client) 208 + { 209 + int ret_val; 210 + /* Write the command and then switch on */ 211 + ret_val = i2c_smbus_write_byte_data(client, 0x80, 0x01); 212 + if (ret_val < 0) { 213 + dev_err(&client->dev, "failed default switch on write\n"); 214 + return ret_val; 215 + } 216 + /* Continous from 1Lux to 64k Lux */ 217 + ret_val = i2c_smbus_write_byte_data(client, 0x81, 0x04); 218 + if (ret_val < 0) 219 + dev_err(&client->dev, "failed default LUX on write\n"); 220 + return ret_val; 221 + } 222 + 223 + static int apds9802als_probe(struct i2c_client *client, 224 + const struct i2c_device_id *id) 225 + { 226 + int res; 227 + struct als_data *data; 228 + 229 + data = kzalloc(sizeof(struct als_data), GFP_KERNEL); 230 + if (data == NULL) { 231 + dev_err(&client->dev, "Memory allocation failed\n"); 232 + return -ENOMEM; 233 + } 234 + i2c_set_clientdata(client, data); 235 + res = sysfs_create_group(&client->dev.kobj, &m_als_gr); 236 + if (res) { 237 + dev_err(&client->dev, "device create file failed\n"); 238 + goto als_error1; 239 + } 240 + dev_info(&client->dev, 241 + "%s apds9802als: ALS chip found\n", client->name); 242 + als_set_default_config(client); 243 + data->needresume = true; 244 + mutex_init(&data->mutex); 245 + return res; 246 + als_error1: 247 + i2c_set_clientdata(client, NULL); 248 + kfree(data); 249 + return res; 250 + } 251 + 252 + static int apds9802als_remove(struct i2c_client *client) 253 + { 254 + struct als_data *data = i2c_get_clientdata(client); 255 + sysfs_remove_group(&client->dev.kobj, &m_als_gr); 256 + kfree(data); 257 + return 0; 258 + } 259 + 260 + static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg) 261 + { 262 + als_set_power_state(client, false); 263 + return 0; 264 + } 265 + 266 + static int apds9802als_resume(struct i2c_client *client) 267 + { 268 + struct als_data *data = i2c_get_clientdata(client); 269 + 270 + if (data->needresume == true) 271 + als_set_power_state(client, true); 272 + return 0; 273 + } 274 + 275 + static struct i2c_device_id apds9802als_id[] = { 276 + { DRIVER_NAME, 0 }, 277 + { } 278 + }; 279 + 280 + MODULE_DEVICE_TABLE(i2c, apds9802als_id); 281 + 282 + static struct i2c_driver apds9802als_driver = { 283 + .driver = { 284 + .name = DRIVER_NAME, 285 + .owner = THIS_MODULE, 286 + }, 287 + .probe = apds9802als_probe, 288 + .remove = apds9802als_remove, 289 + .suspend = apds9802als_suspend, 290 + .resume = apds9802als_resume, 291 + .id_table = apds9802als_id, 292 + }; 293 + 294 + static int __init sensor_apds9802als_init(void) 295 + { 296 + return i2c_add_driver(&apds9802als_driver); 297 + } 298 + 299 + static void __exit sensor_apds9802als_exit(void) 300 + { 301 + i2c_del_driver(&apds9802als_driver); 302 + } 303 + module_init(sensor_apds9802als_init); 304 + module_exit(sensor_apds9802als_exit); 305 + 306 + MODULE_AUTHOR("Anantha Narayanan <Anantha.Narayanan@intel.com"); 307 + MODULE_DESCRIPTION("Avago apds9802als ALS Driver"); 308 + MODULE_LICENSE("GPL v2");