aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/hp_accel.c
diff options
context:
space:
mode:
authorEric Piel <eric.piel@tremplin-utc.net>2009-01-09 19:41:01 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-01-09 19:54:42 -0500
commitcfce41a6d643c001d416ead960caf04fae2d609a (patch)
tree9a80d781c2387c2ad59e5d31ca1fdd7408d0ef89 /drivers/hwmon/hp_accel.c
parent8e961870bb9804110d5c8211d5d9d500451c4518 (diff)
LIS3LV02D: separate the core from HP ACPI API
The sensor can be accessed via various buses. In particular, SPI, I²C and, on HP laptops, via a specific ACPI API (the only one currently supported). Separate this latest platform from the core of the sensor driver to allow support for the other bus type. The second, and more direct goal is actually to be able to merge this part with the hp-disk-leds driver, which has the same ACPI PNP number. Signed-off-by: Pavel Machek <pavel@suse.cz> Signed-off-by: Eric Piel <eric.piel@tremplin-utc.net> 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.c265
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 */
48static struct acpi_device_id lis3lv02d_device_ids[] = {
49 {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */
50 {"", 0},
51};
52MODULE_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 */
61acpi_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 */
74acpi_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 */
96acpi_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
110static 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. */
120static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3};
121static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3};
122static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3};
123static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3};
124static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3};
125static 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}
135static 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
158static 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
189static 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
202static 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
209static 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 */
226static 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
238static 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
254static void __exit lis3lv02d_exit_module(void)
255{
256 acpi_bus_unregister_driver(&lis3lv02d_driver);
257}
258
259MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS");
260MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek");
261MODULE_LICENSE("GPL");
262
263module_init(lis3lv02d_init_module);
264module_exit(lis3lv02d_exit_module);
265