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, |