Blame view
drivers/hwmon/hp_accel.c
11.7 KB
cfce41a6d LIS3LV02D: separa... |
1 2 3 4 5 |
/* * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS * * Copyright (C) 2007-2008 Yan Burman * Copyright (C) 2008 Eric Piel |
9e1c9d865 hp_accel: do not ... |
6 |
* Copyright (C) 2008-2009 Pavel Machek |
cfce41a6d LIS3LV02D: separa... |
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/dmi.h> #include <linux/module.h> #include <linux/types.h> #include <linux/platform_device.h> #include <linux/interrupt.h> |
cfce41a6d LIS3LV02D: separa... |
30 31 32 33 |
#include <linux/delay.h> #include <linux/wait.h> #include <linux/poll.h> #include <linux/freezer.h> |
cfce41a6d LIS3LV02D: separa... |
34 |
#include <linux/uaccess.h> |
9e0c79782 lis3lv02d: merge ... |
35 |
#include <linux/leds.h> |
cfce41a6d LIS3LV02D: separa... |
36 37 38 39 40 41 |
#include <acpi/acpi_drivers.h> #include <asm/atomic.h> #include "lis3lv02d.h" #define DRIVER_NAME "lis3lv02d" #define ACPI_MDPS_CLASS "accelerometer" |
9e1c9d865 hp_accel: do not ... |
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
/* Delayed LEDs infrastructure ------------------------------------ */ /* Special LED class that can defer work */ struct delayed_led_classdev { struct led_classdev led_classdev; struct work_struct work; enum led_brightness new_brightness; unsigned int led; /* For driver */ void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); }; static inline void delayed_set_status_worker(struct work_struct *work) { struct delayed_led_classdev *data = container_of(work, struct delayed_led_classdev, work); data->set_brightness(data, data->new_brightness); } static inline void delayed_sysfs_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct delayed_led_classdev *data = container_of(led_cdev, struct delayed_led_classdev, led_classdev); data->new_brightness = brightness; schedule_work(&data->work); } /* HP-specific accelerometer driver ------------------------------------ */ |
cfce41a6d LIS3LV02D: separa... |
72 73 74 75 76 77 78 79 80 81 82 |
/* For automatic insertion of the module */ static struct acpi_device_id lis3lv02d_device_ids[] = { {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ {"", 0}, }; MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); /** * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. |
a38da2ed7 lis3: solve depen... |
83 |
* @lis3: pointer to the device struct |
cfce41a6d LIS3LV02D: separa... |
84 |
* |
a38da2ed7 lis3: solve depen... |
85 |
* Returns 0 on success. |
cfce41a6d LIS3LV02D: separa... |
86 |
*/ |
a38da2ed7 lis3: solve depen... |
87 |
int lis3lv02d_acpi_init(struct lis3lv02d *lis3) |
cfce41a6d LIS3LV02D: separa... |
88 |
{ |
a38da2ed7 lis3: solve depen... |
89 90 91 92 93 94 |
struct acpi_device *dev = lis3->bus_priv; if (acpi_evaluate_object(dev->handle, METHOD_NAME__INI, NULL, NULL) != AE_OK) return -EINVAL; return 0; |
cfce41a6d LIS3LV02D: separa... |
95 96 97 98 |
} /** * lis3lv02d_acpi_read - ACPI ALRD method: read a register |
a38da2ed7 lis3: solve depen... |
99 |
* @lis3: pointer to the device struct |
cfce41a6d LIS3LV02D: separa... |
100 101 102 |
* @reg: the register to read * @ret: result of the operation * |
a38da2ed7 lis3: solve depen... |
103 |
* Returns 0 on success. |
cfce41a6d LIS3LV02D: separa... |
104 |
*/ |
a38da2ed7 lis3: solve depen... |
105 |
int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) |
cfce41a6d LIS3LV02D: separa... |
106 |
{ |
a38da2ed7 lis3: solve depen... |
107 |
struct acpi_device *dev = lis3->bus_priv; |
cfce41a6d LIS3LV02D: separa... |
108 109 110 111 112 113 |
union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list args = { 1, &arg0 }; unsigned long long lret; acpi_status status; arg0.integer.value = reg; |
a38da2ed7 lis3: solve depen... |
114 |
status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); |
cfce41a6d LIS3LV02D: separa... |
115 |
*ret = lret; |
a38da2ed7 lis3: solve depen... |
116 |
return (status != AE_OK) ? -EINVAL : 0; |
cfce41a6d LIS3LV02D: separa... |
117 118 119 120 |
} /** * lis3lv02d_acpi_write - ACPI ALWR method: write to a register |
a38da2ed7 lis3: solve depen... |
121 |
* @lis3: pointer to the device struct |
cfce41a6d LIS3LV02D: separa... |
122 123 124 |
* @reg: the register to write to * @val: the value to write * |
a38da2ed7 lis3: solve depen... |
125 |
* Returns 0 on success. |
cfce41a6d LIS3LV02D: separa... |
126 |
*/ |
a38da2ed7 lis3: solve depen... |
127 |
int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) |
cfce41a6d LIS3LV02D: separa... |
128 |
{ |
a38da2ed7 lis3: solve depen... |
129 |
struct acpi_device *dev = lis3->bus_priv; |
cfce41a6d LIS3LV02D: separa... |
130 131 132 133 134 135 136 137 |
unsigned long long ret; /* Not used when writting */ union acpi_object in_obj[2]; struct acpi_object_list args = { 2, in_obj }; in_obj[0].type = ACPI_TYPE_INTEGER; in_obj[0].integer.value = reg; in_obj[1].type = ACPI_TYPE_INTEGER; in_obj[1].integer.value = val; |
a38da2ed7 lis3: solve depen... |
138 139 140 141 |
if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) return -EINVAL; return 0; |
cfce41a6d LIS3LV02D: separa... |
142 143 144 145 |
} static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) { |
be84cfc58 hp_accel: adev is... |
146 |
lis3_dev.ac = *((struct axis_conversion *)dmi->driver_data); |
cfce41a6d LIS3LV02D: separa... |
147 148 149 150 151 152 153 154 155 156 157 158 |
printk(KERN_INFO DRIVER_NAME ": hardware type %s found. ", dmi->ident); return 1; } /* Represents, for each axis seen by userspace, the corresponding hw axis (+1). * If the value is negative, the opposite of the hw value is used. */ static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; |
0093716e6 lis3: add three n... |
159 |
static struct axis_conversion lis3lv02d_axis_xy_swap = {2, 1, 3}; |
cfce41a6d LIS3LV02D: separa... |
160 |
static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; |
80eda5fb5 lis3lv02d: add ax... |
161 |
static struct axis_conversion lis3lv02d_axis_xy_rotated_left_usd = {-2, 1, -3}; |
cfce41a6d LIS3LV02D: separa... |
162 |
static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; |
6bfef2b3c lis3lv02d: add ax... |
163 |
static struct axis_conversion lis3lv02d_axis_xy_rotated_right = {2, -1, 3}; |
87357d277 lis3lv02d: add ax... |
164 |
static struct axis_conversion lis3lv02d_axis_xy_swap_yz_inverted = {2, -1, -3}; |
cfce41a6d LIS3LV02D: separa... |
165 166 167 168 169 170 171 172 173 |
#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ .ident = _ident, \ .callback = lis3lv02d_dmi_matched, \ .matches = { \ DMI_MATCH(DMI_PRODUCT_NAME, _name) \ }, \ .driver_data = &lis3lv02d_axis_##_axis \ } |
9ccf3b5e8 lis3lv02d: add ax... |
174 175 176 177 178 179 180 181 182 183 184 185 |
#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ _class2, _name2, \ _axis) { \ .ident = _ident, \ .callback = lis3lv02d_dmi_matched, \ .matches = { \ DMI_MATCH(DMI_##_class1, _name1), \ DMI_MATCH(DMI_##_class2, _name2), \ }, \ .driver_data = &lis3lv02d_axis_##_axis \ } |
cfce41a6d LIS3LV02D: separa... |
186 187 188 189 190 191 192 |
static struct dmi_system_id lis3lv02d_dmi_ids[] = { /* product names are truncated to match all kinds of a same model */ AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), |
0093716e6 lis3: add three n... |
193 |
AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap), |
cfce41a6d LIS3LV02D: separa... |
194 195 |
AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), |
9d7639d33 hp_accel: add two... |
196 |
AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), |
80eda5fb5 lis3lv02d: add ax... |
197 |
AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), |
a03018ff7 lis3: better supp... |
198 199 |
AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), |
6bfef2b3c lis3lv02d: add ax... |
200 |
AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), |
0093716e6 lis3: add three n... |
201 202 203 |
AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), |
af19611c4 lis3: add support... |
204 |
AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), |
9ccf3b5e8 lis3lv02d: add ax... |
205 206 207 208 209 210 211 212 213 214 |
/* Intel-based HP Pavilion dv5 */ AXIS_DMI_MATCH2("HPDV5_I", PRODUCT_NAME, "HP Pavilion dv5", BOARD_NAME, "3603", x_inverted), /* AMD-based HP Pavilion dv5 */ AXIS_DMI_MATCH2("HPDV5_A", PRODUCT_NAME, "HP Pavilion dv5", BOARD_NAME, "3600", y_inverted), |
9d7639d33 hp_accel: add two... |
215 |
AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), |
12a324b6a hp_accel: axis co... |
216 |
AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), |
2545f038f lis3: add support... |
217 |
AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), |
9bd14a839 lis3: add support... |
218 219 220 221 |
AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), |
4e70598c3 hp_accel: add qui... |
222 223 |
AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), AXIS_DMI_MATCH("Mini5102", "HP Mini 5102", xy_rotated_left_usd), |
cfce41a6d LIS3LV02D: separa... |
224 225 |
{ NULL, } /* Laptop models without axis info (yet): |
cfce41a6d LIS3LV02D: separa... |
226 |
* "NC6910" "HP Compaq 6910" |
cfce41a6d LIS3LV02D: separa... |
227 228 229 230 231 232 |
* "NC2400" "HP Compaq nc2400" * "NX74x0" "HP Compaq nx74" * "NX6325" "HP Compaq nx6325" * "NC4400" "HP Compaq nc4400" */ }; |
9e1c9d865 hp_accel: do not ... |
233 |
static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) |
9e0c79782 lis3lv02d: merge ... |
234 |
{ |
a38da2ed7 lis3: solve depen... |
235 |
struct acpi_device *dev = lis3_dev.bus_priv; |
9e0c79782 lis3lv02d: merge ... |
236 237 238 239 240 |
unsigned long long ret; /* Not used when writing */ union acpi_object in_obj[1]; struct acpi_object_list args = { 1, in_obj }; in_obj[0].type = ACPI_TYPE_INTEGER; |
9e1c9d865 hp_accel: do not ... |
241 |
in_obj[0].integer.value = !!value; |
9e0c79782 lis3lv02d: merge ... |
242 |
|
a38da2ed7 lis3: solve depen... |
243 |
acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); |
9e0c79782 lis3lv02d: merge ... |
244 |
} |
9e1c9d865 hp_accel: do not ... |
245 246 247 248 249 250 251 252 |
static struct delayed_led_classdev hpled_led = { .led_classdev = { .name = "hp::hddprotect", .default_trigger = "none", .brightness_set = delayed_sysfs_set, .flags = LED_CORE_SUSPENDRESUME, }, .set_brightness = hpled_set, |
9e0c79782 lis3lv02d: merge ... |
253 |
}; |
cfce41a6d LIS3LV02D: separa... |
254 |
|
ef2cfc790 hp accelerometer:... |
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
static acpi_status lis3lv02d_get_resource(struct acpi_resource *resource, void *context) { if (resource->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { struct acpi_resource_extended_irq *irq; u32 *device_irq = context; irq = &resource->data.extended_irq; *device_irq = irq->interrupts[0]; } return AE_OK; } static void lis3lv02d_enum_resources(struct acpi_device *device) { acpi_status status; status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, |
be84cfc58 hp_accel: adev is... |
274 |
lis3lv02d_get_resource, &lis3_dev.irq); |
ef2cfc790 hp accelerometer:... |
275 276 277 278 |
if (ACPI_FAILURE(status)) printk(KERN_DEBUG DRIVER_NAME ": Error getting resources "); } |
cfce41a6d LIS3LV02D: separa... |
279 280 |
static int lis3lv02d_add(struct acpi_device *device) { |
9e0c79782 lis3lv02d: merge ... |
281 |
int ret; |
cfce41a6d LIS3LV02D: separa... |
282 283 284 |
if (!device) return -EINVAL; |
a38da2ed7 lis3: solve depen... |
285 |
lis3_dev.bus_priv = device; |
be84cfc58 hp_accel: adev is... |
286 287 288 |
lis3_dev.init = lis3lv02d_acpi_init; lis3_dev.read = lis3lv02d_acpi_read; lis3_dev.write = lis3lv02d_acpi_write; |
cfce41a6d LIS3LV02D: separa... |
289 290 |
strcpy(acpi_device_name(device), DRIVER_NAME); strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); |
be84cfc58 hp_accel: adev is... |
291 |
device->driver_data = &lis3_dev; |
cfce41a6d LIS3LV02D: separa... |
292 |
|
a38da2ed7 lis3: solve depen... |
293 294 |
/* obtain IRQ number of our device from ACPI */ lis3lv02d_enum_resources(device); |
cfce41a6d LIS3LV02D: separa... |
295 296 297 298 299 300 |
/* If possible use a "standard" axes order */ if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " "using default axes configuration "); |
be84cfc58 hp_accel: adev is... |
301 |
lis3_dev.ac = lis3lv02d_axis_normal; |
cfce41a6d LIS3LV02D: separa... |
302 |
} |
a38da2ed7 lis3: solve depen... |
303 304 |
/* call the core layer do its init */ ret = lis3lv02d_init_device(&lis3_dev); |
9e0c79782 lis3lv02d: merge ... |
305 306 |
if (ret) return ret; |
a38da2ed7 lis3: solve depen... |
307 308 |
INIT_WORK(&hpled_led.work, delayed_set_status_worker); ret = led_classdev_register(NULL, &hpled_led.led_classdev); |
9e0c79782 lis3lv02d: merge ... |
309 |
if (ret) { |
a38da2ed7 lis3: solve depen... |
310 311 |
lis3lv02d_joystick_disable(); lis3lv02d_poweroff(&lis3_dev); |
9e1c9d865 hp_accel: do not ... |
312 |
flush_work(&hpled_led.work); |
9e0c79782 lis3lv02d: merge ... |
313 314 315 316 |
return ret; } return ret; |
cfce41a6d LIS3LV02D: separa... |
317 318 319 320 321 322 323 324 |
} static int lis3lv02d_remove(struct acpi_device *device, int type) { if (!device) return -EINVAL; lis3lv02d_joystick_disable(); |
a38da2ed7 lis3: solve depen... |
325 |
lis3lv02d_poweroff(&lis3_dev); |
cfce41a6d LIS3LV02D: separa... |
326 |
|
9e1c9d865 hp_accel: do not ... |
327 |
led_classdev_unregister(&hpled_led.led_classdev); |
06efbeb4a hp_accel: fix rac... |
328 |
flush_work(&hpled_led.work); |
9e0c79782 lis3lv02d: merge ... |
329 |
|
a002ee896 lis3: remove auto... |
330 |
return lis3lv02d_remove_fs(&lis3_dev); |
cfce41a6d LIS3LV02D: separa... |
331 332 333 334 335 336 337 |
} #ifdef CONFIG_PM static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) { /* make sure the device is off when we suspend */ |
a38da2ed7 lis3: solve depen... |
338 |
lis3lv02d_poweroff(&lis3_dev); |
cfce41a6d LIS3LV02D: separa... |
339 340 341 342 343 |
return 0; } static int lis3lv02d_resume(struct acpi_device *device) { |
a002ee896 lis3: remove auto... |
344 |
lis3lv02d_poweron(&lis3_dev); |
cfce41a6d LIS3LV02D: separa... |
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
return 0; } #else #define lis3lv02d_suspend NULL #define lis3lv02d_resume NULL #endif /* For the HP MDPS aka 3D Driveguard */ static struct acpi_driver lis3lv02d_driver = { .name = DRIVER_NAME, .class = ACPI_MDPS_CLASS, .ids = lis3lv02d_device_ids, .ops = { .add = lis3lv02d_add, .remove = lis3lv02d_remove, .suspend = lis3lv02d_suspend, .resume = lis3lv02d_resume, } }; static int __init lis3lv02d_init_module(void) { int ret; if (acpi_disabled) return -ENODEV; ret = acpi_bus_register_driver(&lis3lv02d_driver); if (ret < 0) return ret; printk(KERN_INFO DRIVER_NAME " driver loaded. "); return 0; } static void __exit lis3lv02d_exit_module(void) { acpi_bus_unregister_driver(&lis3lv02d_driver); } |
9e0c79782 lis3lv02d: merge ... |
386 |
MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); |
cfce41a6d LIS3LV02D: separa... |
387 388 389 390 391 |
MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); MODULE_LICENSE("GPL"); module_init(lis3lv02d_init_module); module_exit(lis3lv02d_exit_module); |