aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/hp_accel.c
diff options
context:
space:
mode:
authorPavel Machek <pavel@suse.cz>2009-01-15 16:51:24 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-01-15 19:39:41 -0500
commit9e1c9d865543593ee92ec3a5075f064dec981a96 (patch)
treeb89aa477a142e50fb45553b63fa51a20e67d6e89 /drivers/hwmon/hp_accel.c
parent219beb291ba9275dd676578724103abed4cfbfe3 (diff)
hp_accel: do not call ACPI from invalid context
The LED on HP notebooks is connected through ACPI. That unfortunately means that it needs to be delayed by using schedule_work() to avoid calling the ACPI interpreter from an invalid context. [akpm@linux-foundation.org: use flush_work() rather than sort-of reimplementing it] Signed-off-by: Pavel Machek <pavel@suse.cz> Cc: Éric Piel <eric.piel@tremplin-utc.net> Cc: Len Brown <lenb@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/hwmon/hp_accel.c')
-rw-r--r--drivers/hwmon/hp_accel.c68
1 files changed, 49 insertions, 19 deletions
diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c
index 86a0f51d99d6..03705240000f 100644
--- a/drivers/hwmon/hp_accel.c
+++ b/drivers/hwmon/hp_accel.c
@@ -3,7 +3,7 @@
3 * 3 *
4 * Copyright (C) 2007-2008 Yan Burman 4 * Copyright (C) 2007-2008 Yan Burman
5 * Copyright (C) 2008 Eric Piel 5 * Copyright (C) 2008 Eric Piel
6 * Copyright (C) 2008 Pavel Machek 6 * Copyright (C) 2008-2009 Pavel Machek
7 * 7 *
8 * This program is free software; you can redistribute it and/or modify 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 9 * it under the terms of the GNU General Public License as published by
@@ -44,6 +44,36 @@
44#define DRIVER_NAME "lis3lv02d" 44#define DRIVER_NAME "lis3lv02d"
45#define ACPI_MDPS_CLASS "accelerometer" 45#define ACPI_MDPS_CLASS "accelerometer"
46 46
47/* Delayed LEDs infrastructure ------------------------------------ */
48
49/* Special LED class that can defer work */
50struct delayed_led_classdev {
51 struct led_classdev led_classdev;
52 struct work_struct work;
53 enum led_brightness new_brightness;
54
55 unsigned int led; /* For driver */
56 void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value);
57};
58
59static inline void delayed_set_status_worker(struct work_struct *work)
60{
61 struct delayed_led_classdev *data =
62 container_of(work, struct delayed_led_classdev, work);
63
64 data->set_brightness(data, data->new_brightness);
65}
66
67static inline void delayed_sysfs_set(struct led_classdev *led_cdev,
68 enum led_brightness brightness)
69{
70 struct delayed_led_classdev *data = container_of(led_cdev,
71 struct delayed_led_classdev, led_classdev);
72 data->new_brightness = brightness;
73 schedule_work(&data->work);
74}
75
76/* HP-specific accelerometer driver ------------------------------------ */
47 77
48/* For automatic insertion of the module */ 78/* For automatic insertion of the module */
49static struct acpi_device_id lis3lv02d_device_ids[] = { 79static struct acpi_device_id lis3lv02d_device_ids[] = {
@@ -155,28 +185,27 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = {
155 */ 185 */
156}; 186};
157 187
158static acpi_status hpled_acpi_write(acpi_handle handle, int reg) 188static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value)
159{ 189{
190 acpi_handle handle = adev.device->handle;
160 unsigned long long ret; /* Not used when writing */ 191 unsigned long long ret; /* Not used when writing */
161 union acpi_object in_obj[1]; 192 union acpi_object in_obj[1];
162 struct acpi_object_list args = { 1, in_obj }; 193 struct acpi_object_list args = { 1, in_obj };
163 194
164 in_obj[0].type = ACPI_TYPE_INTEGER; 195 in_obj[0].type = ACPI_TYPE_INTEGER;
165 in_obj[0].integer.value = reg; 196 in_obj[0].integer.value = !!value;
166 197
167 return acpi_evaluate_integer(handle, "ALED", &args, &ret); 198 acpi_evaluate_integer(handle, "ALED", &args, &ret);
168}
169
170static void hpled_set(struct led_classdev *led_cdev,
171 enum led_brightness value)
172{
173 hpled_acpi_write(adev.device->handle, !!value);
174} 199}
175 200
176static struct led_classdev hpled_led = { 201static struct delayed_led_classdev hpled_led = {
177 .name = "hp:red:hddprotection", 202 .led_classdev = {
178 .default_trigger = "none", 203 .name = "hp::hddprotect",
179 .brightness_set = hpled_set, 204 .default_trigger = "none",
205 .brightness_set = delayed_sysfs_set,
206 .flags = LED_CORE_SUSPENDRESUME,
207 },
208 .set_brightness = hpled_set,
180}; 209};
181 210
182static int lis3lv02d_add(struct acpi_device *device) 211static int lis3lv02d_add(struct acpi_device *device)
@@ -208,13 +237,15 @@ static int lis3lv02d_add(struct acpi_device *device)
208 adev.ac = lis3lv02d_axis_normal; 237 adev.ac = lis3lv02d_axis_normal;
209 } 238 }
210 239
211 ret = led_classdev_register(NULL, &hpled_led); 240 INIT_WORK(&hpled_led.work, delayed_set_status_worker);
241 ret = led_classdev_register(NULL, &hpled_led.led_classdev);
212 if (ret) 242 if (ret)
213 return ret; 243 return ret;
214 244
215 ret = lis3lv02d_init_device(&adev); 245 ret = lis3lv02d_init_device(&adev);
216 if (ret) { 246 if (ret) {
217 led_classdev_unregister(&hpled_led); 247 flush_work(&hpled_led.work);
248 led_classdev_unregister(&hpled_led.led_classdev);
218 return ret; 249 return ret;
219 } 250 }
220 251
@@ -229,7 +260,8 @@ static int lis3lv02d_remove(struct acpi_device *device, int type)
229 lis3lv02d_joystick_disable(); 260 lis3lv02d_joystick_disable();
230 lis3lv02d_poweroff(device->handle); 261 lis3lv02d_poweroff(device->handle);
231 262
232 led_classdev_unregister(&hpled_led); 263 flush_work(&hpled_led.work);
264 led_classdev_unregister(&hpled_led.led_classdev);
233 265
234 return lis3lv02d_remove_fs(); 266 return lis3lv02d_remove_fs();
235} 267}
@@ -240,7 +272,6 @@ static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state)
240{ 272{
241 /* make sure the device is off when we suspend */ 273 /* make sure the device is off when we suspend */
242 lis3lv02d_poweroff(device->handle); 274 lis3lv02d_poweroff(device->handle);
243 led_classdev_suspend(&hpled_led);
244 return 0; 275 return 0;
245} 276}
246 277
@@ -253,7 +284,6 @@ static int lis3lv02d_resume(struct acpi_device *device)
253 else 284 else
254 lis3lv02d_poweroff(device->handle); 285 lis3lv02d_poweroff(device->handle);
255 mutex_unlock(&adev.lock); 286 mutex_unlock(&adev.lock);
256 led_classdev_resume(&hpled_led);
257 return 0; 287 return 0;
258} 288}
259#else 289#else