diff options
Diffstat (limited to 'drivers/hwmon/hp_accel.c')
-rw-r--r-- | drivers/hwmon/hp_accel.c | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c new file mode 100644 index 000000000000..bf8d40580577 --- /dev/null +++ b/drivers/hwmon/hp_accel.c | |||
@@ -0,0 +1,265 @@ | |||
1 | /* | ||
2 | * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS | ||
3 | * | ||
4 | * Copyright (C) 2007-2008 Yan Burman | ||
5 | * Copyright (C) 2008 Eric Piel | ||
6 | * Copyright (C) 2008 Pavel Machek | ||
7 | * | ||
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 | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/dmi.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/types.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <linux/input.h> | ||
31 | #include <linux/kthread.h> | ||
32 | #include <linux/semaphore.h> | ||
33 | #include <linux/delay.h> | ||
34 | #include <linux/wait.h> | ||
35 | #include <linux/poll.h> | ||
36 | #include <linux/freezer.h> | ||
37 | #include <linux/version.h> | ||
38 | #include <linux/uaccess.h> | ||
39 | #include <acpi/acpi_drivers.h> | ||
40 | #include <asm/atomic.h> | ||
41 | #include "lis3lv02d.h" | ||
42 | |||
43 | #define DRIVER_NAME "lis3lv02d" | ||
44 | #define ACPI_MDPS_CLASS "accelerometer" | ||
45 | |||
46 | |||
47 | /* For automatic insertion of the module */ | ||
48 | static struct acpi_device_id lis3lv02d_device_ids[] = { | ||
49 | {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ | ||
50 | {"", 0}, | ||
51 | }; | ||
52 | MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); | ||
53 | |||
54 | |||
55 | /** | ||
56 | * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. | ||
57 | * @handle: the handle of the device | ||
58 | * | ||
59 | * Returns AE_OK on success. | ||
60 | */ | ||
61 | acpi_status lis3lv02d_acpi_init(acpi_handle handle) | ||
62 | { | ||
63 | return acpi_evaluate_object(handle, METHOD_NAME__INI, NULL, NULL); | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * lis3lv02d_acpi_read - ACPI ALRD method: read a register | ||
68 | * @handle: the handle of the device | ||
69 | * @reg: the register to read | ||
70 | * @ret: result of the operation | ||
71 | * | ||
72 | * Returns AE_OK on success. | ||
73 | */ | ||
74 | acpi_status lis3lv02d_acpi_read(acpi_handle handle, int reg, u8 *ret) | ||
75 | { | ||
76 | union acpi_object arg0 = { ACPI_TYPE_INTEGER }; | ||
77 | struct acpi_object_list args = { 1, &arg0 }; | ||
78 | unsigned long long lret; | ||
79 | acpi_status status; | ||
80 | |||
81 | arg0.integer.value = reg; | ||
82 | |||
83 | status = acpi_evaluate_integer(handle, "ALRD", &args, &lret); | ||
84 | *ret = lret; | ||
85 | return status; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * lis3lv02d_acpi_write - ACPI ALWR method: write to a register | ||
90 | * @handle: the handle of the device | ||
91 | * @reg: the register to write to | ||
92 | * @val: the value to write | ||
93 | * | ||
94 | * Returns AE_OK on success. | ||
95 | */ | ||
96 | acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) | ||
97 | { | ||
98 | unsigned long long ret; /* Not used when writting */ | ||
99 | union acpi_object in_obj[2]; | ||
100 | struct acpi_object_list args = { 2, in_obj }; | ||
101 | |||
102 | in_obj[0].type = ACPI_TYPE_INTEGER; | ||
103 | in_obj[0].integer.value = reg; | ||
104 | in_obj[1].type = ACPI_TYPE_INTEGER; | ||
105 | in_obj[1].integer.value = val; | ||
106 | |||
107 | return acpi_evaluate_integer(handle, "ALWR", &args, &ret); | ||
108 | } | ||
109 | |||
110 | static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) | ||
111 | { | ||
112 | adev.ac = *((struct axis_conversion *)dmi->driver_data); | ||
113 | printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); | ||
114 | |||
115 | return 1; | ||
116 | } | ||
117 | |||
118 | /* Represents, for each axis seen by userspace, the corresponding hw axis (+1). | ||
119 | * If the value is negative, the opposite of the hw value is used. */ | ||
120 | static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; | ||
121 | static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; | ||
122 | static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; | ||
123 | static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; | ||
124 | static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; | ||
125 | static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; | ||
126 | |||
127 | #define AXIS_DMI_MATCH(_ident, _name, _axis) { \ | ||
128 | .ident = _ident, \ | ||
129 | .callback = lis3lv02d_dmi_matched, \ | ||
130 | .matches = { \ | ||
131 | DMI_MATCH(DMI_PRODUCT_NAME, _name) \ | ||
132 | }, \ | ||
133 | .driver_data = &lis3lv02d_axis_##_axis \ | ||
134 | } | ||
135 | static struct dmi_system_id lis3lv02d_dmi_ids[] = { | ||
136 | /* product names are truncated to match all kinds of a same model */ | ||
137 | AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), | ||
138 | AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), | ||
139 | AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), | ||
140 | AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), | ||
141 | AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), | ||
142 | AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), | ||
143 | AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), | ||
144 | { NULL, } | ||
145 | /* Laptop models without axis info (yet): | ||
146 | * "NC651xx" "HP Compaq 651" | ||
147 | * "NC671xx" "HP Compaq 671" | ||
148 | * "NC6910" "HP Compaq 6910" | ||
149 | * HP Compaq 8710x Notebook PC / Mobile Workstation | ||
150 | * "NC2400" "HP Compaq nc2400" | ||
151 | * "NX74x0" "HP Compaq nx74" | ||
152 | * "NX6325" "HP Compaq nx6325" | ||
153 | * "NC4400" "HP Compaq nc4400" | ||
154 | */ | ||
155 | }; | ||
156 | |||
157 | |||
158 | static int lis3lv02d_add(struct acpi_device *device) | ||
159 | { | ||
160 | u8 val; | ||
161 | |||
162 | if (!device) | ||
163 | return -EINVAL; | ||
164 | |||
165 | adev.device = device; | ||
166 | adev.init = lis3lv02d_acpi_init; | ||
167 | adev.read = lis3lv02d_acpi_read; | ||
168 | adev.write = lis3lv02d_acpi_write; | ||
169 | strcpy(acpi_device_name(device), DRIVER_NAME); | ||
170 | strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); | ||
171 | device->driver_data = &adev; | ||
172 | |||
173 | lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); | ||
174 | if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { | ||
175 | printk(KERN_ERR DRIVER_NAME | ||
176 | ": Accelerometer chip not LIS3LV02D{L,Q}\n"); | ||
177 | } | ||
178 | |||
179 | /* If possible use a "standard" axes order */ | ||
180 | if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { | ||
181 | printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " | ||
182 | "using default axes configuration\n"); | ||
183 | adev.ac = lis3lv02d_axis_normal; | ||
184 | } | ||
185 | |||
186 | return lis3lv02d_init_device(&adev); | ||
187 | } | ||
188 | |||
189 | static int lis3lv02d_remove(struct acpi_device *device, int type) | ||
190 | { | ||
191 | if (!device) | ||
192 | return -EINVAL; | ||
193 | |||
194 | lis3lv02d_joystick_disable(); | ||
195 | lis3lv02d_poweroff(device->handle); | ||
196 | |||
197 | return lis3lv02d_remove_fs(); | ||
198 | } | ||
199 | |||
200 | |||
201 | #ifdef CONFIG_PM | ||
202 | static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) | ||
203 | { | ||
204 | /* make sure the device is off when we suspend */ | ||
205 | lis3lv02d_poweroff(device->handle); | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int lis3lv02d_resume(struct acpi_device *device) | ||
210 | { | ||
211 | /* put back the device in the right state (ACPI might turn it on) */ | ||
212 | mutex_lock(&adev.lock); | ||
213 | if (adev.usage > 0) | ||
214 | lis3lv02d_poweron(device->handle); | ||
215 | else | ||
216 | lis3lv02d_poweroff(device->handle); | ||
217 | mutex_unlock(&adev.lock); | ||
218 | return 0; | ||
219 | } | ||
220 | #else | ||
221 | #define lis3lv02d_suspend NULL | ||
222 | #define lis3lv02d_resume NULL | ||
223 | #endif | ||
224 | |||
225 | /* For the HP MDPS aka 3D Driveguard */ | ||
226 | static struct acpi_driver lis3lv02d_driver = { | ||
227 | .name = DRIVER_NAME, | ||
228 | .class = ACPI_MDPS_CLASS, | ||
229 | .ids = lis3lv02d_device_ids, | ||
230 | .ops = { | ||
231 | .add = lis3lv02d_add, | ||
232 | .remove = lis3lv02d_remove, | ||
233 | .suspend = lis3lv02d_suspend, | ||
234 | .resume = lis3lv02d_resume, | ||
235 | } | ||
236 | }; | ||
237 | |||
238 | static int __init lis3lv02d_init_module(void) | ||
239 | { | ||
240 | int ret; | ||
241 | |||
242 | if (acpi_disabled) | ||
243 | return -ENODEV; | ||
244 | |||
245 | ret = acpi_bus_register_driver(&lis3lv02d_driver); | ||
246 | if (ret < 0) | ||
247 | return ret; | ||
248 | |||
249 | printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static void __exit lis3lv02d_exit_module(void) | ||
255 | { | ||
256 | acpi_bus_unregister_driver(&lis3lv02d_driver); | ||
257 | } | ||
258 | |||
259 | MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS"); | ||
260 | MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); | ||
261 | MODULE_LICENSE("GPL"); | ||
262 | |||
263 | module_init(lis3lv02d_init_module); | ||
264 | module_exit(lis3lv02d_exit_module); | ||
265 | |||