aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/hp_accel.c265
-rw-r--r--drivers/hwmon/lis3lv02d.c273
-rw-r--r--drivers/hwmon/lis3lv02d.h35
4 files changed, 324 insertions, 251 deletions
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8fd124eff646..19cb1ace3eb4 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -49,7 +49,7 @@ obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
49obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o 49obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
50obj-$(CONFIG_SENSORS_IT87) += it87.o 50obj-$(CONFIG_SENSORS_IT87) += it87.o
51obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o 51obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
52obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o 52obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o
53obj-$(CONFIG_SENSORS_LM63) += lm63.o 53obj-$(CONFIG_SENSORS_LM63) += lm63.o
54obj-$(CONFIG_SENSORS_LM70) += lm70.o 54obj-$(CONFIG_SENSORS_LM70) += lm70.o
55obj-$(CONFIG_SENSORS_LM75) += lm75.o 55obj-$(CONFIG_SENSORS_LM75) += lm75.o
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
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
58struct axis_conversion { 58struct acpi_lis3lv02d adev;
59 s8 x; 59EXPORT_SYMBOL_GPL(adev);
60 s8 y;
61 s8 z;
62};
63
64struct 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
79static struct acpi_lis3lv02d adev;
80
81static int lis3lv02d_remove_fs(void);
82static int lis3lv02d_add_fs(struct acpi_device *device); 61static int lis3lv02d_add_fs(struct acpi_device *device);
83 62
84/* For automatic insertion of the module */
85static struct acpi_device_id lis3lv02d_device_ids[] = {
86 {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */
87 {"", 0},
88};
89MODULE_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 */
97static 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 */
110static 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 */
132static 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
146static s16 lis3lv02d_read_16(acpi_handle handle, int reg) 63static 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
193static inline void lis3lv02d_poweroff(acpi_handle handle) 110void 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}
116EXPORT_SYMBOL_GPL(lis3lv02d_poweroff);
199 117
200static void lis3lv02d_poweron(acpi_handle handle) 118void 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
218static 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
225static 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 134EXPORT_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
318static int lis3lv02d_joystick_enable(void) 212int 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}
246EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable);
352 247
353static void lis3lv02d_joystick_disable(void) 248void 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 256EXPORT_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 */
367static int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) 262int 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 274EXPORT_SYMBOL_GPL(lis3lv02d_init_device);
380static 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. */
390static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3};
391static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3};
392static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3};
393static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3};
394static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3};
395static 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}
405static 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
427static 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
455static 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 */
468static ssize_t lis3lv02d_position_show(struct device *dev, 277static 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
526static int lis3lv02d_add_fs(struct acpi_device *device) 336static 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
535static int lis3lv02d_remove_fs(void) 345int 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 351EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs);
542/* For the HP MDPS aka 3D Driveguard */
543static 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
555static 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
571static void __exit lis3lv02d_exit_module(void)
572{
573 acpi_bus_unregister_driver(&lis3lv02d_driver);
574}
575 352
576MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); 353MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver");
577MODULE_AUTHOR("Yan Burman and Eric Piel"); 354MODULE_AUTHOR("Yan Burman and Eric Piel");
578MODULE_LICENSE("GPL"); 355MODULE_LICENSE("GPL");
579 356
580module_init(lis3lv02d_init_module);
581module_exit(lis3lv02d_exit_module);
diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h
index 330cfc60e948..223f1c0763bb 100644
--- a/drivers/hwmon/lis3lv02d.h
+++ b/drivers/hwmon/lis3lv02d.h
@@ -23,7 +23,7 @@
23 * The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ that seems to 23 * The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ that seems to
24 * be connected via SPI. There exists also several similar chips (such as LIS302DL or 24 * be connected via SPI. There exists also several similar chips (such as LIS302DL or
25 * LIS3L02DQ) but not in the HP laptops and they have slightly different registers. 25 * LIS3L02DQ) but not in the HP laptops and they have slightly different registers.
26 * They can also be connected via I²C. 26 * They can also be connected via I²C.
27 */ 27 */
28 28
29#define LIS3LV02DL_ID 0x3A /* Also the LIS3LV02DQ */ 29#define LIS3LV02DL_ID 0x3A /* Also the LIS3LV02DQ */
@@ -147,3 +147,36 @@ enum lis3lv02d_dd_src {
147 DD_SRC_IA = 0x40, 147 DD_SRC_IA = 0x40,
148}; 148};
149 149
150struct axis_conversion {
151 s8 x;
152 s8 y;
153 s8 z;
154};
155
156struct acpi_lis3lv02d {
157 struct acpi_device *device; /* The ACPI device */
158 acpi_status (*init) (acpi_handle handle);
159 acpi_status (*write) (acpi_handle handle, int reg, u8 val);
160 acpi_status (*read) (acpi_handle handle, int reg, u8 *ret);
161
162 struct input_dev *idev; /* input device */
163 struct task_struct *kthread; /* kthread for input */
164 struct mutex lock;
165 struct platform_device *pdev; /* platform device */
166 atomic_t count; /* interrupt count after last read */
167 int xcalib; /* calibrated null value for x */
168 int ycalib; /* calibrated null value for y */
169 int zcalib; /* calibrated null value for z */
170 unsigned char is_on; /* whether the device is on or off */
171 unsigned char usage; /* usage counter */
172 struct axis_conversion ac; /* hw -> logical axis */
173};
174
175int lis3lv02d_init_device(struct acpi_lis3lv02d *dev);
176int lis3lv02d_joystick_enable(void);
177void lis3lv02d_joystick_disable(void);
178void lis3lv02d_poweroff(acpi_handle handle);
179void lis3lv02d_poweron(acpi_handle handle);
180int lis3lv02d_remove_fs(void);
181
182extern struct acpi_lis3lv02d adev;