diff options
author | Pavel Machek <pavel@suse.cz> | 2009-01-15 16:51:24 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-15 19:39:41 -0500 |
commit | 9e1c9d865543593ee92ec3a5075f064dec981a96 (patch) | |
tree | b89aa477a142e50fb45553b63fa51a20e67d6e89 /drivers/hwmon/hp_accel.c | |
parent | 219beb291ba9275dd676578724103abed4cfbfe3 (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.c | 68 |
1 files changed, 49 insertions, 19 deletions
diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index 86a0f51d99d..03705240000 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 */ | ||
50 | struct 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 | |||
59 | static 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 | |||
67 | static 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 */ |
49 | static struct acpi_device_id lis3lv02d_device_ids[] = { | 79 | static 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 | ||
158 | static acpi_status hpled_acpi_write(acpi_handle handle, int reg) | 188 | static 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 | |||
170 | static 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 | ||
176 | static struct led_classdev hpled_led = { | 201 | static 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 | ||
182 | static int lis3lv02d_add(struct acpi_device *device) | 211 | static 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 |