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; | ||
