diff options
-rw-r--r-- | drivers/hwmon/Makefile | 2 | ||||
-rw-r--r-- | drivers/hwmon/hp_accel.c | 265 | ||||
-rw-r--r-- | drivers/hwmon/lis3lv02d.c | 273 | ||||
-rw-r--r-- | drivers/hwmon/lis3lv02d.h | 35 |
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 | |||
49 | obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o | 49 | obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o |
50 | obj-$(CONFIG_SENSORS_IT87) += it87.o | 50 | obj-$(CONFIG_SENSORS_IT87) += it87.o |
51 | obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o | 51 | obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o |
52 | obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o | 52 | obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o |
53 | obj-$(CONFIG_SENSORS_LM63) += lm63.o | 53 | obj-$(CONFIG_SENSORS_LM63) += lm63.o |
54 | obj-$(CONFIG_SENSORS_LM70) += lm70.o | 54 | obj-$(CONFIG_SENSORS_LM70) += lm70.o |
55 | obj-$(CONFIG_SENSORS_LM75) += lm75.o | 55 | obj-$(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 */ | ||
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 | |||
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); | ||
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 | ||
150 | struct axis_conversion { | ||
151 | s8 x; | ||
152 | s8 y; | ||
153 | s8 z; | ||
154 | }; | ||
155 | |||
156 | struct 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 | |||
175 | int lis3lv02d_init_device(struct acpi_lis3lv02d *dev); | ||
176 | int lis3lv02d_joystick_enable(void); | ||
177 | void lis3lv02d_joystick_disable(void); | ||
178 | void lis3lv02d_poweroff(acpi_handle handle); | ||
179 | void lis3lv02d_poweron(acpi_handle handle); | ||
180 | int lis3lv02d_remove_fs(void); | ||
181 | |||
182 | extern struct acpi_lis3lv02d adev; | ||