diff options
| author | Hong Liu <hong.liu@intel.com> | 2010-10-26 17:22:42 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-26 19:52:15 -0400 |
| commit | f0cfec11180973e4f4b2b6909623e47eaaf7ecfe (patch) | |
| tree | 5ffda7ed37b00be575d0568c9fda4453685cadc0 /drivers/misc | |
| parent | 22d96aa59cf120db3584e4c3365554cae77d2441 (diff) | |
drivers/misc/apds9802als.c: add runtime PM support
Update the driver for the needed runtime power features. Remove the old
user controlled power functions.
[akpm@linux-foundation.org: put PM code under CONFIG_PM]
Signed-off-by: Hong Liu <hong.liu@intel.com>
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/misc')
| -rw-r--r-- | drivers/misc/apds9802als.c | 167 |
1 files changed, 103 insertions, 64 deletions
diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index fbe49602f396..f9b91ba8900c 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c | |||
| @@ -29,19 +29,16 @@ | |||
| 29 | #include <linux/delay.h> | 29 | #include <linux/delay.h> |
| 30 | #include <linux/mutex.h> | 30 | #include <linux/mutex.h> |
| 31 | #include <linux/sysfs.h> | 31 | #include <linux/sysfs.h> |
| 32 | #include <linux/hwmon-sysfs.h> | 32 | #include <linux/pm_runtime.h> |
| 33 | 33 | ||
| 34 | #define ALS_MIN_RANGE_VAL 1 | 34 | #define ALS_MIN_RANGE_VAL 1 |
| 35 | #define ALS_MAX_RANGE_VAL 2 | 35 | #define ALS_MAX_RANGE_VAL 2 |
| 36 | #define POWER_STA_ENABLE 1 | 36 | #define POWER_STA_ENABLE 1 |
| 37 | #define POWER_STA_DISABLE 0 | 37 | #define POWER_STA_DISABLE 0 |
| 38 | #define APDS9802ALS_I2C_ADDR 0x29 | ||
| 39 | 38 | ||
| 40 | #define DRIVER_NAME "apds9802als" | 39 | #define DRIVER_NAME "apds9802als" |
| 41 | 40 | ||
| 42 | struct als_data { | 41 | struct als_data { |
| 43 | struct device *hwmon_dev; | ||
| 44 | bool needresume; | ||
| 45 | struct mutex mutex; | 42 | struct mutex mutex; |
| 46 | }; | 43 | }; |
| 47 | 44 | ||
| @@ -60,29 +57,64 @@ static ssize_t als_sensing_range_show(struct device *dev, | |||
| 60 | return sprintf(buf, "65535\n"); | 57 | return sprintf(buf, "65535\n"); |
| 61 | } | 58 | } |
| 62 | 59 | ||
| 60 | static int als_wait_for_data_ready(struct device *dev) | ||
| 61 | { | ||
| 62 | struct i2c_client *client = to_i2c_client(dev); | ||
| 63 | int ret; | ||
| 64 | int retry = 10; | ||
| 65 | |||
| 66 | do { | ||
| 67 | msleep(30); | ||
| 68 | ret = i2c_smbus_read_byte_data(client, 0x86); | ||
| 69 | } while (!(ret & 0x80) && retry--); | ||
| 70 | |||
| 71 | if (!retry) { | ||
| 72 | dev_warn(dev, "timeout waiting for data ready\n"); | ||
| 73 | return -ETIMEDOUT; | ||
| 74 | } | ||
| 75 | |||
| 76 | return 0; | ||
| 77 | } | ||
| 78 | |||
| 63 | static ssize_t als_lux0_input_data_show(struct device *dev, | 79 | static ssize_t als_lux0_input_data_show(struct device *dev, |
| 64 | struct device_attribute *attr, char *buf) | 80 | struct device_attribute *attr, char *buf) |
| 65 | { | 81 | { |
| 66 | struct i2c_client *client = to_i2c_client(dev); | 82 | struct i2c_client *client = to_i2c_client(dev); |
| 67 | struct als_data *data = i2c_get_clientdata(client); | 83 | struct als_data *data = i2c_get_clientdata(client); |
| 68 | unsigned int ret_val; | 84 | int ret_val; |
| 69 | int temp; | 85 | int temp; |
| 70 | 86 | ||
| 71 | /* Protect against parallel reads */ | 87 | /* Protect against parallel reads */ |
| 88 | pm_runtime_get_sync(dev); | ||
| 72 | mutex_lock(&data->mutex); | 89 | mutex_lock(&data->mutex); |
| 73 | temp = i2c_smbus_read_byte_data(client, 0x8C);/*LSB data*/ | 90 | |
| 91 | /* clear EOC interrupt status */ | ||
| 92 | i2c_smbus_write_byte(client, 0x40); | ||
| 93 | /* start measurement */ | ||
| 94 | temp = i2c_smbus_read_byte_data(client, 0x81); | ||
| 95 | i2c_smbus_write_byte_data(client, 0x81, temp | 0x08); | ||
| 96 | |||
| 97 | ret_val = als_wait_for_data_ready(dev); | ||
| 98 | if (ret_val < 0) | ||
| 99 | goto failed; | ||
| 100 | |||
| 101 | temp = i2c_smbus_read_byte_data(client, 0x8C); /* LSB data */ | ||
| 74 | if (temp < 0) { | 102 | if (temp < 0) { |
| 75 | ret_val = temp; | 103 | ret_val = temp; |
| 76 | goto failed; | 104 | goto failed; |
| 77 | } | 105 | } |
| 78 | ret_val = i2c_smbus_read_byte_data(client, 0x8D);/*MSB data*/ | 106 | ret_val = i2c_smbus_read_byte_data(client, 0x8D); /* MSB data */ |
| 79 | if (ret_val < 0) | 107 | if (ret_val < 0) |
| 80 | goto failed; | 108 | goto failed; |
| 109 | |||
| 81 | mutex_unlock(&data->mutex); | 110 | mutex_unlock(&data->mutex); |
| 82 | ret_val = (ret_val << 8) | temp; | 111 | pm_runtime_put_sync(dev); |
| 83 | return sprintf(buf, "%d\n", ret_val); | 112 | |
| 113 | temp = (ret_val << 8) | temp; | ||
| 114 | return sprintf(buf, "%d\n", temp); | ||
| 84 | failed: | 115 | failed: |
| 85 | mutex_unlock(&data->mutex); | 116 | mutex_unlock(&data->mutex); |
| 117 | pm_runtime_put_sync(dev); | ||
| 86 | return ret_val; | 118 | return ret_val; |
| 87 | } | 119 | } |
| 88 | 120 | ||
| @@ -104,9 +136,10 @@ static ssize_t als_sensing_range_store(struct device *dev, | |||
| 104 | else | 136 | else |
| 105 | return -ERANGE; | 137 | return -ERANGE; |
| 106 | 138 | ||
| 139 | pm_runtime_get_sync(dev); | ||
| 140 | |||
| 107 | /* Make sure nobody else reads/modifies/writes 0x81 while we | 141 | /* Make sure nobody else reads/modifies/writes 0x81 while we |
| 108 | are active */ | 142 | are active */ |
| 109 | |||
| 110 | mutex_lock(&data->mutex); | 143 | mutex_lock(&data->mutex); |
| 111 | 144 | ||
| 112 | ret_val = i2c_smbus_read_byte_data(client, 0x81); | 145 | ret_val = i2c_smbus_read_byte_data(client, 0x81); |
| @@ -116,34 +149,25 @@ static ssize_t als_sensing_range_store(struct device *dev, | |||
| 116 | /* Reset the bits before setting them */ | 149 | /* Reset the bits before setting them */ |
| 117 | ret_val = ret_val & 0xFA; | 150 | ret_val = ret_val & 0xFA; |
| 118 | 151 | ||
| 119 | if (val == 1) /* Setting the continous measurement up to 4k LUX */ | 152 | if (val == 1) /* Setting detection range up to 4k LUX */ |
| 120 | ret_val = (ret_val | 0x05); | 153 | ret_val = (ret_val | 0x01); |
| 121 | else /* Setting the continous measurement up to 64k LUX*/ | 154 | else /* Setting detection range up to 64k LUX*/ |
| 122 | ret_val = (ret_val | 0x04); | 155 | ret_val = (ret_val | 0x00); |
| 123 | 156 | ||
| 124 | ret_val = i2c_smbus_write_byte_data(client, 0x81, ret_val); | 157 | ret_val = i2c_smbus_write_byte_data(client, 0x81, ret_val); |
| 158 | |||
| 125 | if (ret_val >= 0) { | 159 | if (ret_val >= 0) { |
| 126 | /* All OK */ | 160 | /* All OK */ |
| 127 | mutex_unlock(&data->mutex); | 161 | mutex_unlock(&data->mutex); |
| 162 | pm_runtime_put_sync(dev); | ||
| 128 | return count; | 163 | return count; |
| 129 | } | 164 | } |
| 130 | fail: | 165 | fail: |
| 131 | mutex_unlock(&data->mutex); | 166 | mutex_unlock(&data->mutex); |
| 167 | pm_runtime_put_sync(dev); | ||
| 132 | return ret_val; | 168 | return ret_val; |
| 133 | } | 169 | } |
| 134 | 170 | ||
| 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) | 171 | static int als_set_power_state(struct i2c_client *client, bool on_off) |
| 148 | { | 172 | { |
| 149 | int ret_val; | 173 | int ret_val; |
| @@ -163,39 +187,13 @@ fail: | |||
| 163 | return ret_val; | 187 | return ret_val; |
| 164 | } | 188 | } |
| 165 | 189 | ||
| 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 | static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR, |
| 190 | als_sensing_range_show, als_sensing_range_store); | 191 | 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(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 | 193 | ||
| 195 | static struct attribute *mid_att_als[] = { | 194 | static struct attribute *mid_att_als[] = { |
| 196 | &dev_attr_lux0_sensor_range.attr, | 195 | &dev_attr_lux0_sensor_range.attr, |
| 197 | &dev_attr_lux0_input.attr, | 196 | &dev_attr_lux0_input.attr, |
| 198 | &dev_attr_power_state.attr, | ||
| 199 | NULL | 197 | NULL |
| 200 | }; | 198 | }; |
| 201 | 199 | ||
| @@ -213,15 +211,21 @@ static int als_set_default_config(struct i2c_client *client) | |||
| 213 | dev_err(&client->dev, "failed default switch on write\n"); | 211 | dev_err(&client->dev, "failed default switch on write\n"); |
| 214 | return ret_val; | 212 | return ret_val; |
| 215 | } | 213 | } |
| 216 | /* Continous from 1Lux to 64k Lux */ | 214 | /* detection range: 1~64K Lux, maunal measurement */ |
| 217 | ret_val = i2c_smbus_write_byte_data(client, 0x81, 0x04); | 215 | ret_val = i2c_smbus_write_byte_data(client, 0x81, 0x08); |
| 218 | if (ret_val < 0) | 216 | if (ret_val < 0) |
| 219 | dev_err(&client->dev, "failed default LUX on write\n"); | 217 | dev_err(&client->dev, "failed default LUX on write\n"); |
| 218 | |||
| 219 | /* We always get 0 for the 1st measurement after system power on, | ||
| 220 | * so make sure it is finished before user asks for data. | ||
| 221 | */ | ||
| 222 | als_wait_for_data_ready(&client->dev); | ||
| 223 | |||
| 220 | return ret_val; | 224 | return ret_val; |
| 221 | } | 225 | } |
| 222 | 226 | ||
| 223 | static int apds9802als_probe(struct i2c_client *client, | 227 | static int apds9802als_probe(struct i2c_client *client, |
| 224 | const struct i2c_device_id *id) | 228 | const struct i2c_device_id *id) |
| 225 | { | 229 | { |
| 226 | int res; | 230 | int res; |
| 227 | struct als_data *data; | 231 | struct als_data *data; |
| @@ -237,11 +241,14 @@ static int apds9802als_probe(struct i2c_client *client, | |||
| 237 | dev_err(&client->dev, "device create file failed\n"); | 241 | dev_err(&client->dev, "device create file failed\n"); |
| 238 | goto als_error1; | 242 | goto als_error1; |
| 239 | } | 243 | } |
| 240 | dev_info(&client->dev, | 244 | dev_info(&client->dev, "ALS chip found\n"); |
| 241 | "%s apds9802als: ALS chip found\n", client->name); | ||
| 242 | als_set_default_config(client); | 245 | als_set_default_config(client); |
| 243 | data->needresume = true; | ||
| 244 | mutex_init(&data->mutex); | 246 | mutex_init(&data->mutex); |
| 247 | |||
| 248 | pm_runtime_enable(&client->dev); | ||
| 249 | pm_runtime_get(&client->dev); | ||
| 250 | pm_runtime_put(&client->dev); | ||
| 251 | |||
| 245 | return res; | 252 | return res; |
| 246 | als_error1: | 253 | als_error1: |
| 247 | i2c_set_clientdata(client, NULL); | 254 | i2c_set_clientdata(client, NULL); |
| @@ -252,11 +259,14 @@ als_error1: | |||
| 252 | static int apds9802als_remove(struct i2c_client *client) | 259 | static int apds9802als_remove(struct i2c_client *client) |
| 253 | { | 260 | { |
| 254 | struct als_data *data = i2c_get_clientdata(client); | 261 | struct als_data *data = i2c_get_clientdata(client); |
| 262 | |||
| 263 | als_set_power_state(client, false); | ||
| 255 | sysfs_remove_group(&client->dev.kobj, &m_als_gr); | 264 | sysfs_remove_group(&client->dev.kobj, &m_als_gr); |
| 256 | kfree(data); | 265 | kfree(data); |
| 257 | return 0; | 266 | return 0; |
| 258 | } | 267 | } |
| 259 | 268 | ||
| 269 | #ifdef CONFIG_PM | ||
| 260 | static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg) | 270 | static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg) |
| 261 | { | 271 | { |
| 262 | als_set_power_state(client, false); | 272 | als_set_power_state(client, false); |
| @@ -265,13 +275,42 @@ static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg) | |||
| 265 | 275 | ||
| 266 | static int apds9802als_resume(struct i2c_client *client) | 276 | static int apds9802als_resume(struct i2c_client *client) |
| 267 | { | 277 | { |
| 268 | struct als_data *data = i2c_get_clientdata(client); | 278 | als_set_default_config(client); |
| 269 | 279 | ||
| 270 | if (data->needresume == true) | 280 | pm_runtime_get(&client->dev); |
| 271 | als_set_power_state(client, true); | 281 | pm_runtime_put(&client->dev); |
| 272 | return 0; | 282 | return 0; |
| 273 | } | 283 | } |
| 274 | 284 | ||
| 285 | static int apds9802als_runtime_suspend(struct device *dev) | ||
| 286 | { | ||
| 287 | struct i2c_client *client = to_i2c_client(dev); | ||
| 288 | |||
| 289 | als_set_power_state(client, false); | ||
| 290 | return 0; | ||
| 291 | } | ||
| 292 | |||
| 293 | static int apds9802als_runtime_resume(struct device *dev) | ||
| 294 | { | ||
| 295 | struct i2c_client *client = to_i2c_client(dev); | ||
| 296 | |||
| 297 | als_set_power_state(client, true); | ||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | static const struct dev_pm_ops apds9802als_pm_ops = { | ||
| 302 | .runtime_suspend = apds9802als_runtime_suspend, | ||
| 303 | .runtime_resume = apds9802als_runtime_resume, | ||
| 304 | }; | ||
| 305 | |||
| 306 | #define APDS9802ALS_PM_OPS (&apds9802als_pm_ops) | ||
| 307 | |||
| 308 | #else /* CONFIG_PM */ | ||
| 309 | #define apds9802als_suspend NULL | ||
| 310 | #define apds9802als_resume NULL | ||
| 311 | #define APDS9802ALS_PM_OPS NULL | ||
| 312 | #endif /* CONFIG_PM */ | ||
| 313 | |||
| 275 | static struct i2c_device_id apds9802als_id[] = { | 314 | static struct i2c_device_id apds9802als_id[] = { |
| 276 | { DRIVER_NAME, 0 }, | 315 | { DRIVER_NAME, 0 }, |
| 277 | { } | 316 | { } |
| @@ -281,8 +320,8 @@ MODULE_DEVICE_TABLE(i2c, apds9802als_id); | |||
| 281 | 320 | ||
| 282 | static struct i2c_driver apds9802als_driver = { | 321 | static struct i2c_driver apds9802als_driver = { |
| 283 | .driver = { | 322 | .driver = { |
| 284 | .name = DRIVER_NAME, | 323 | .name = DRIVER_NAME, |
| 285 | .owner = THIS_MODULE, | 324 | .pm = APDS9802ALS_PM_OPS, |
| 286 | }, | 325 | }, |
| 287 | .probe = apds9802als_probe, | 326 | .probe = apds9802als_probe, |
| 288 | .remove = apds9802als_remove, | 327 | .remove = apds9802als_remove, |
