aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHong Liu <hong.liu@intel.com>2010-10-26 17:22:42 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-10-26 19:52:15 -0400
commitf0cfec11180973e4f4b2b6909623e47eaaf7ecfe (patch)
tree5ffda7ed37b00be575d0568c9fda4453685cadc0
parent22d96aa59cf120db3584e4c3365554cae77d2441 (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>
-rw-r--r--drivers/misc/apds9802als.c167
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
42struct als_data { 41struct 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
60static 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
63static ssize_t als_lux0_input_data_show(struct device *dev, 79static 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);
84failed: 115failed:
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 }
130fail: 165fail:
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
135static 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
147static int als_set_power_state(struct i2c_client *client, bool on_off) 171static 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
166static 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
189static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR, 190static 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);
191static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux0_input_data_show, NULL); 192static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux0_input_data_show, NULL);
192static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
193 als_power_status_show, als_power_status_store);
194 193
195static struct attribute *mid_att_als[] = { 194static 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
223static int apds9802als_probe(struct i2c_client *client, 227static 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;
246als_error1: 253als_error1:
247 i2c_set_clientdata(client, NULL); 254 i2c_set_clientdata(client, NULL);
@@ -252,11 +259,14 @@ als_error1:
252static int apds9802als_remove(struct i2c_client *client) 259static 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
260static int apds9802als_suspend(struct i2c_client *client, pm_message_t mesg) 270static 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
266static int apds9802als_resume(struct i2c_client *client) 276static 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
285static 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
293static 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
301static 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
275static struct i2c_device_id apds9802als_id[] = { 314static 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
282static struct i2c_driver apds9802als_driver = { 321static 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,