diff options
Diffstat (limited to 'drivers/hwmon/lis3lv02d.c')
-rw-r--r-- | drivers/hwmon/lis3lv02d.c | 273 |
1 files changed, 24 insertions, 249 deletions
diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index c002144c76bc..219d2d0d5a62 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c | |||
@@ -3,6 +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 | * | 7 | * |
7 | * 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 |
8 | * 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 |
@@ -39,7 +40,6 @@ | |||
39 | #include "lis3lv02d.h" | 40 | #include "lis3lv02d.h" |
40 | 41 | ||
41 | #define DRIVER_NAME "lis3lv02d" | 42 | #define DRIVER_NAME "lis3lv02d" |
42 | #define ACPI_MDPS_CLASS "accelerometer" | ||
43 | 43 | ||
44 | /* joystick device poll interval in milliseconds */ | 44 | /* joystick device poll interval in milliseconds */ |
45 | #define MDPS_POLL_INTERVAL 50 | 45 | #define MDPS_POLL_INTERVAL 50 |
@@ -55,100 +55,17 @@ | |||
55 | /* Maximum value our axis may get for the input device (signed 12 bits) */ | 55 | /* Maximum value our axis may get for the input device (signed 12 bits) */ |
56 | #define MDPS_MAX_VAL 2048 | 56 | #define MDPS_MAX_VAL 2048 |
57 | 57 | ||
58 | struct axis_conversion { | 58 | struct acpi_lis3lv02d adev; |
59 | s8 x; | 59 | EXPORT_SYMBOL_GPL(adev); |
60 | s8 y; | ||
61 | s8 z; | ||
62 | }; | ||
63 | |||
64 | struct acpi_lis3lv02d { | ||
65 | struct acpi_device *device; /* The ACPI device */ | ||
66 | struct input_dev *idev; /* input device */ | ||
67 | struct task_struct *kthread; /* kthread for input */ | ||
68 | struct mutex lock; | ||
69 | struct platform_device *pdev; /* platform device */ | ||
70 | atomic_t count; /* interrupt count after last read */ | ||
71 | int xcalib; /* calibrated null value for x */ | ||
72 | int ycalib; /* calibrated null value for y */ | ||
73 | int zcalib; /* calibrated null value for z */ | ||
74 | unsigned char is_on; /* whether the device is on or off */ | ||
75 | unsigned char usage; /* usage counter */ | ||
76 | struct axis_conversion ac; /* hw -> logical axis */ | ||
77 | }; | ||
78 | 60 | ||
79 | static struct acpi_lis3lv02d adev; | ||
80 | |||
81 | static int lis3lv02d_remove_fs(void); | ||
82 | static int lis3lv02d_add_fs(struct acpi_device *device); | 61 | static int lis3lv02d_add_fs(struct acpi_device *device); |
83 | 62 | ||
84 | /* For automatic insertion of the module */ | ||
85 | static struct acpi_device_id lis3lv02d_device_ids[] = { | ||
86 | {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ | ||
87 | {"", 0}, | ||
88 | }; | ||
89 | MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); | ||
90 | |||
91 | /** | ||
92 | * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. | ||
93 | * @handle: the handle of the device | ||
94 | * | ||
95 | * Returns AE_OK on success. | ||
96 | */ | ||
97 | static inline acpi_status lis3lv02d_acpi_init(acpi_handle handle) | ||
98 | { | ||
99 | return acpi_evaluate_object(handle, METHOD_NAME__INI, NULL, NULL); | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * lis3lv02d_acpi_read - ACPI ALRD method: read a register | ||
104 | * @handle: the handle of the device | ||
105 | * @reg: the register to read | ||
106 | * @ret: result of the operation | ||
107 | * | ||
108 | * Returns AE_OK on success. | ||
109 | */ | ||
110 | static acpi_status lis3lv02d_acpi_read(acpi_handle handle, int reg, u8 *ret) | ||
111 | { | ||
112 | union acpi_object arg0 = { ACPI_TYPE_INTEGER }; | ||
113 | struct acpi_object_list args = { 1, &arg0 }; | ||
114 | unsigned long long lret; | ||
115 | acpi_status status; | ||
116 | |||
117 | arg0.integer.value = reg; | ||
118 | |||
119 | status = acpi_evaluate_integer(handle, "ALRD", &args, &lret); | ||
120 | *ret = lret; | ||
121 | return status; | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * lis3lv02d_acpi_write - ACPI ALWR method: write to a register | ||
126 | * @handle: the handle of the device | ||
127 | * @reg: the register to write to | ||
128 | * @val: the value to write | ||
129 | * | ||
130 | * Returns AE_OK on success. | ||
131 | */ | ||
132 | static acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) | ||
133 | { | ||
134 | unsigned long long ret; /* Not used when writting */ | ||
135 | union acpi_object in_obj[2]; | ||
136 | struct acpi_object_list args = { 2, in_obj }; | ||
137 | |||
138 | in_obj[0].type = ACPI_TYPE_INTEGER; | ||
139 | in_obj[0].integer.value = reg; | ||
140 | in_obj[1].type = ACPI_TYPE_INTEGER; | ||
141 | in_obj[1].integer.value = val; | ||
142 | |||
143 | return acpi_evaluate_integer(handle, "ALWR", &args, &ret); | ||
144 | } | ||
145 | |||
146 | static s16 lis3lv02d_read_16(acpi_handle handle, int reg) | 63 | static s16 lis3lv02d_read_16(acpi_handle handle, int reg) |
147 | { | 64 | { |
148 | u8 lo, hi; | 65 | u8 lo, hi; |
149 | 66 | ||
150 | lis3lv02d_acpi_read(handle, reg, &lo); | 67 | adev.read(handle, reg, &lo); |
151 | lis3lv02d_acpi_read(handle, reg + 1, &hi); | 68 | adev.read(handle, reg + 1, &hi); |
152 | /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ | 69 | /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ |
153 | return (s16)((hi << 8) | lo); | 70 | return (s16)((hi << 8) | lo); |
154 | } | 71 | } |
@@ -190,54 +107,31 @@ static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) | |||
190 | *z = lis3lv02d_get_axis(adev.ac.z, position); | 107 | *z = lis3lv02d_get_axis(adev.ac.z, position); |
191 | } | 108 | } |
192 | 109 | ||
193 | static inline void lis3lv02d_poweroff(acpi_handle handle) | 110 | void lis3lv02d_poweroff(acpi_handle handle) |
194 | { | 111 | { |
195 | adev.is_on = 0; | 112 | adev.is_on = 0; |
196 | /* disable X,Y,Z axis and power down */ | 113 | /* disable X,Y,Z axis and power down */ |
197 | lis3lv02d_acpi_write(handle, CTRL_REG1, 0x00); | 114 | adev.write(handle, CTRL_REG1, 0x00); |
198 | } | 115 | } |
116 | EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); | ||
199 | 117 | ||
200 | static void lis3lv02d_poweron(acpi_handle handle) | 118 | void lis3lv02d_poweron(acpi_handle handle) |
201 | { | 119 | { |
202 | u8 val; | 120 | u8 val; |
203 | 121 | ||
204 | adev.is_on = 1; | 122 | adev.is_on = 1; |
205 | lis3lv02d_acpi_init(handle); | 123 | adev.init(handle); |
206 | lis3lv02d_acpi_write(handle, FF_WU_CFG, 0); | 124 | adev.write(handle, FF_WU_CFG, 0); |
207 | /* | 125 | /* |
208 | * BDU: LSB and MSB values are not updated until both have been read. | 126 | * BDU: LSB and MSB values are not updated until both have been read. |
209 | * So the value read will always be correct. | 127 | * So the value read will always be correct. |
210 | * IEN: Interrupt for free-fall and DD, not for data-ready. | 128 | * IEN: Interrupt for free-fall and DD, not for data-ready. |
211 | */ | 129 | */ |
212 | lis3lv02d_acpi_read(handle, CTRL_REG2, &val); | 130 | adev.read(handle, CTRL_REG2, &val); |
213 | val |= CTRL2_BDU | CTRL2_IEN; | 131 | val |= CTRL2_BDU | CTRL2_IEN; |
214 | lis3lv02d_acpi_write(handle, CTRL_REG2, val); | 132 | adev.write(handle, CTRL_REG2, val); |
215 | } | ||
216 | |||
217 | #ifdef CONFIG_PM | ||
218 | static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) | ||
219 | { | ||
220 | /* make sure the device is off when we suspend */ | ||
221 | lis3lv02d_poweroff(device->handle); | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int lis3lv02d_resume(struct acpi_device *device) | ||
226 | { | ||
227 | /* put back the device in the right state (ACPI might turn it on) */ | ||
228 | mutex_lock(&adev.lock); | ||
229 | if (adev.usage > 0) | ||
230 | lis3lv02d_poweron(device->handle); | ||
231 | else | ||
232 | lis3lv02d_poweroff(device->handle); | ||
233 | mutex_unlock(&adev.lock); | ||
234 | return 0; | ||
235 | } | 133 | } |
236 | #else | 134 | EXPORT_SYMBOL_GPL(lis3lv02d_poweron); |
237 | #define lis3lv02d_suspend NULL | ||
238 | #define lis3lv02d_resume NULL | ||
239 | #endif | ||
240 | |||
241 | 135 | ||
242 | /* | 136 | /* |
243 | * To be called before starting to use the device. It makes sure that the | 137 | * To be called before starting to use the device. It makes sure that the |
@@ -315,7 +209,7 @@ static inline void lis3lv02d_calibrate_joystick(void) | |||
315 | lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); | 209 | lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); |
316 | } | 210 | } |
317 | 211 | ||
318 | static int lis3lv02d_joystick_enable(void) | 212 | int lis3lv02d_joystick_enable(void) |
319 | { | 213 | { |
320 | int err; | 214 | int err; |
321 | 215 | ||
@@ -349,8 +243,9 @@ static int lis3lv02d_joystick_enable(void) | |||
349 | 243 | ||
350 | return err; | 244 | return err; |
351 | } | 245 | } |
246 | EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); | ||
352 | 247 | ||
353 | static void lis3lv02d_joystick_disable(void) | 248 | void lis3lv02d_joystick_disable(void) |
354 | { | 249 | { |
355 | if (!adev.idev) | 250 | if (!adev.idev) |
356 | return; | 251 | return; |
@@ -358,13 +253,13 @@ static void lis3lv02d_joystick_disable(void) | |||
358 | input_unregister_device(adev.idev); | 253 | input_unregister_device(adev.idev); |
359 | adev.idev = NULL; | 254 | adev.idev = NULL; |
360 | } | 255 | } |
361 | 256 | EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); | |
362 | 257 | ||
363 | /* | 258 | /* |
364 | * Initialise the accelerometer and the various subsystems. | 259 | * Initialise the accelerometer and the various subsystems. |
365 | * Should be rather independant of the bus system. | 260 | * Should be rather independant of the bus system. |
366 | */ | 261 | */ |
367 | static int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) | 262 | int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) |
368 | { | 263 | { |
369 | mutex_init(&dev->lock); | 264 | mutex_init(&dev->lock); |
370 | lis3lv02d_add_fs(dev->device); | 265 | lis3lv02d_add_fs(dev->device); |
@@ -376,93 +271,7 @@ static int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) | |||
376 | lis3lv02d_decrease_use(dev); | 271 | lis3lv02d_decrease_use(dev); |
377 | return 0; | 272 | return 0; |
378 | } | 273 | } |
379 | 274 | EXPORT_SYMBOL_GPL(lis3lv02d_init_device); | |
380 | static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) | ||
381 | { | ||
382 | adev.ac = *((struct axis_conversion *)dmi->driver_data); | ||
383 | printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); | ||
384 | |||
385 | return 1; | ||
386 | } | ||
387 | |||
388 | /* Represents, for each axis seen by userspace, the corresponding hw axis (+1). | ||
389 | * If the value is negative, the opposite of the hw value is used. */ | ||
390 | static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; | ||
391 | static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; | ||
392 | static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; | ||
393 | static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; | ||
394 | static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; | ||
395 | static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; | ||
396 | |||
397 | #define AXIS_DMI_MATCH(_ident, _name, _axis) { \ | ||
398 | .ident = _ident, \ | ||
399 | .callback = lis3lv02d_dmi_matched, \ | ||
400 | .matches = { \ | ||
401 | DMI_MATCH(DMI_PRODUCT_NAME, _name) \ | ||
402 | }, \ | ||
403 | .driver_data = &lis3lv02d_axis_##_axis \ | ||
404 | } | ||
405 | static struct dmi_system_id lis3lv02d_dmi_ids[] = { | ||
406 | /* product names are truncated to match all kinds of a same model */ | ||
407 | AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), | ||
408 | AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), | ||
409 | AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), | ||
410 | AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), | ||
411 | AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), | ||
412 | AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), | ||
413 | AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), | ||
414 | { NULL, } | ||
415 | /* Laptop models without axis info (yet): | ||
416 | * "NC651xx" "HP Compaq 651" | ||
417 | * "NC671xx" "HP Compaq 671" | ||
418 | * "NC6910" "HP Compaq 6910" | ||
419 | * HP Compaq 8710x Notebook PC / Mobile Workstation | ||
420 | * "NC2400" "HP Compaq nc2400" | ||
421 | * "NX74x0" "HP Compaq nx74" | ||
422 | * "NX6325" "HP Compaq nx6325" | ||
423 | * "NC4400" "HP Compaq nc4400" | ||
424 | */ | ||
425 | }; | ||
426 | |||
427 | static int lis3lv02d_add(struct acpi_device *device) | ||
428 | { | ||
429 | u8 val; | ||
430 | |||
431 | if (!device) | ||
432 | return -EINVAL; | ||
433 | |||
434 | adev.device = device; | ||
435 | strcpy(acpi_device_name(device), DRIVER_NAME); | ||
436 | strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); | ||
437 | device->driver_data = &adev; | ||
438 | |||
439 | lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); | ||
440 | if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { | ||
441 | printk(KERN_ERR DRIVER_NAME | ||
442 | ": Accelerometer chip not LIS3LV02D{L,Q}\n"); | ||
443 | } | ||
444 | |||
445 | /* If possible use a "standard" axes order */ | ||
446 | if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { | ||
447 | printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " | ||
448 | "using default axes configuration\n"); | ||
449 | adev.ac = lis3lv02d_axis_normal; | ||
450 | } | ||
451 | |||
452 | return lis3lv02d_init_device(&adev); | ||
453 | } | ||
454 | |||
455 | static int lis3lv02d_remove(struct acpi_device *device, int type) | ||
456 | { | ||
457 | if (!device) | ||
458 | return -EINVAL; | ||
459 | |||
460 | lis3lv02d_joystick_disable(); | ||
461 | lis3lv02d_poweroff(device->handle); | ||
462 | |||
463 | return lis3lv02d_remove_fs(); | ||
464 | } | ||
465 | |||
466 | 275 | ||
467 | /* Sysfs stuff */ | 276 | /* Sysfs stuff */ |
468 | static ssize_t lis3lv02d_position_show(struct device *dev, | 277 | static ssize_t lis3lv02d_position_show(struct device *dev, |
@@ -501,7 +310,7 @@ static ssize_t lis3lv02d_rate_show(struct device *dev, | |||
501 | int val; | 310 | int val; |
502 | 311 | ||
503 | lis3lv02d_increase_use(&adev); | 312 | lis3lv02d_increase_use(&adev); |
504 | lis3lv02d_acpi_read(adev.device->handle, CTRL_REG1, &ctrl); | 313 | adev.read(adev.device->handle, CTRL_REG1, &ctrl); |
505 | lis3lv02d_decrease_use(&adev); | 314 | lis3lv02d_decrease_use(&adev); |
506 | val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; | 315 | val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; |
507 | return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); | 316 | return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); |
@@ -523,6 +332,7 @@ static struct attribute_group lis3lv02d_attribute_group = { | |||
523 | .attrs = lis3lv02d_attributes | 332 | .attrs = lis3lv02d_attributes |
524 | }; | 333 | }; |
525 | 334 | ||
335 | |||
526 | static int lis3lv02d_add_fs(struct acpi_device *device) | 336 | static int lis3lv02d_add_fs(struct acpi_device *device) |
527 | { | 337 | { |
528 | adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); | 338 | adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); |
@@ -532,50 +342,15 @@ static int lis3lv02d_add_fs(struct acpi_device *device) | |||
532 | return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); | 342 | return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); |
533 | } | 343 | } |
534 | 344 | ||
535 | static int lis3lv02d_remove_fs(void) | 345 | int lis3lv02d_remove_fs(void) |
536 | { | 346 | { |
537 | sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); | 347 | sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); |
538 | platform_device_unregister(adev.pdev); | 348 | platform_device_unregister(adev.pdev); |
539 | return 0; | 349 | return 0; |
540 | } | 350 | } |
541 | 351 | EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); | |
542 | /* For the HP MDPS aka 3D Driveguard */ | ||
543 | static struct acpi_driver lis3lv02d_driver = { | ||
544 | .name = DRIVER_NAME, | ||
545 | .class = ACPI_MDPS_CLASS, | ||
546 | .ids = lis3lv02d_device_ids, | ||
547 | .ops = { | ||
548 | .add = lis3lv02d_add, | ||
549 | .remove = lis3lv02d_remove, | ||
550 | .suspend = lis3lv02d_suspend, | ||
551 | .resume = lis3lv02d_resume, | ||
552 | } | ||
553 | }; | ||
554 | |||
555 | static int __init lis3lv02d_init_module(void) | ||
556 | { | ||
557 | int ret; | ||
558 | |||
559 | if (acpi_disabled) | ||
560 | return -ENODEV; | ||
561 | |||
562 | ret = acpi_bus_register_driver(&lis3lv02d_driver); | ||
563 | if (ret < 0) | ||
564 | return ret; | ||
565 | |||
566 | printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); | ||
567 | |||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | static void __exit lis3lv02d_exit_module(void) | ||
572 | { | ||
573 | acpi_bus_unregister_driver(&lis3lv02d_driver); | ||
574 | } | ||
575 | 352 | ||
576 | MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); | 353 | MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); |
577 | MODULE_AUTHOR("Yan Burman and Eric Piel"); | 354 | MODULE_AUTHOR("Yan Burman and Eric Piel"); |
578 | MODULE_LICENSE("GPL"); | 355 | MODULE_LICENSE("GPL"); |
579 | 356 | ||
580 | module_init(lis3lv02d_init_module); | ||
581 | module_exit(lis3lv02d_exit_module); | ||