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>

authored by

Eric Piel and committed by
Linus Torvalds
cfce41a6 8e961870

+324 -251
+1 -1
drivers/hwmon/Makefile
··· 49 obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o 50 obj-$(CONFIG_SENSORS_IT87) += it87.o 51 obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o 52 - obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o 53 obj-$(CONFIG_SENSORS_LM63) += lm63.o 54 obj-$(CONFIG_SENSORS_LM70) += lm70.o 55 obj-$(CONFIG_SENSORS_LM75) += lm75.o
··· 49 obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o 50 obj-$(CONFIG_SENSORS_IT87) += it87.o 51 obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o 52 + obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o 53 obj-$(CONFIG_SENSORS_LM63) += lm63.o 54 obj-$(CONFIG_SENSORS_LM70) += lm70.o 55 obj-$(CONFIG_SENSORS_LM75) += lm75.o
+265
drivers/hwmon/hp_accel.c
···
··· 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 +
+24 -249
drivers/hwmon/lis3lv02d.c
··· 3 * 4 * Copyright (C) 2007-2008 Yan Burman 5 * Copyright (C) 2008 Eric Piel 6 * 7 * 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 ··· 40 #include "lis3lv02d.h" 41 42 #define DRIVER_NAME "lis3lv02d" 43 - #define ACPI_MDPS_CLASS "accelerometer" 44 45 /* joystick device poll interval in milliseconds */ 46 #define MDPS_POLL_INTERVAL 50 ··· 55 /* Maximum value our axis may get for the input device (signed 12 bits) */ 56 #define MDPS_MAX_VAL 2048 57 58 - struct axis_conversion { 59 - s8 x; 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 - 79 - static struct acpi_lis3lv02d adev; 80 - 81 - static int lis3lv02d_remove_fs(void); 82 static int lis3lv02d_add_fs(struct acpi_device *device); 83 - 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) 147 { 148 u8 lo, hi; 149 150 - lis3lv02d_acpi_read(handle, reg, &lo); 151 - lis3lv02d_acpi_read(handle, reg + 1, &hi); 152 /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ 153 return (s16)((hi << 8) | lo); 154 } ··· 107 *z = lis3lv02d_get_axis(adev.ac.z, position); 108 } 109 110 - static inline void lis3lv02d_poweroff(acpi_handle handle) 111 { 112 adev.is_on = 0; 113 /* disable X,Y,Z axis and power down */ 114 - lis3lv02d_acpi_write(handle, CTRL_REG1, 0x00); 115 } 116 117 - static void lis3lv02d_poweron(acpi_handle handle) 118 { 119 u8 val; 120 121 adev.is_on = 1; 122 - lis3lv02d_acpi_init(handle); 123 - lis3lv02d_acpi_write(handle, FF_WU_CFG, 0); 124 /* 125 * BDU: LSB and MSB values are not updated until both have been read. 126 * So the value read will always be correct. 127 * IEN: Interrupt for free-fall and DD, not for data-ready. 128 */ 129 - lis3lv02d_acpi_read(handle, CTRL_REG2, &val); 130 val |= CTRL2_BDU | CTRL2_IEN; 131 - lis3lv02d_acpi_write(handle, CTRL_REG2, val); 132 } 133 - 134 - #ifdef CONFIG_PM 135 - static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) 136 - { 137 - /* make sure the device is off when we suspend */ 138 - lis3lv02d_poweroff(device->handle); 139 - return 0; 140 - } 141 - 142 - static int lis3lv02d_resume(struct acpi_device *device) 143 - { 144 - /* put back the device in the right state (ACPI might turn it on) */ 145 - mutex_lock(&adev.lock); 146 - if (adev.usage > 0) 147 - lis3lv02d_poweron(device->handle); 148 - else 149 - lis3lv02d_poweroff(device->handle); 150 - mutex_unlock(&adev.lock); 151 - return 0; 152 - } 153 - #else 154 - #define lis3lv02d_suspend NULL 155 - #define lis3lv02d_resume NULL 156 - #endif 157 - 158 159 /* 160 * To be called before starting to use the device. It makes sure that the ··· 209 lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); 210 } 211 212 - static int lis3lv02d_joystick_enable(void) 213 { 214 int err; 215 ··· 243 244 return err; 245 } 246 247 - static void lis3lv02d_joystick_disable(void) 248 { 249 if (!adev.idev) 250 return; ··· 253 input_unregister_device(adev.idev); 254 adev.idev = NULL; 255 } 256 - 257 258 /* 259 * Initialise the accelerometer and the various subsystems. 260 * Should be rather independant of the bus system. 261 */ 262 - static int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) 263 { 264 mutex_init(&dev->lock); 265 lis3lv02d_add_fs(dev->device); ··· 271 lis3lv02d_decrease_use(dev); 272 return 0; 273 } 274 - 275 - static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) 276 - { 277 - adev.ac = *((struct axis_conversion *)dmi->driver_data); 278 - printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); 279 - 280 - return 1; 281 - } 282 - 283 - /* Represents, for each axis seen by userspace, the corresponding hw axis (+1). 284 - * If the value is negative, the opposite of the hw value is used. */ 285 - static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; 286 - static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; 287 - static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; 288 - static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; 289 - static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; 290 - static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; 291 - 292 - #define AXIS_DMI_MATCH(_ident, _name, _axis) { \ 293 - .ident = _ident, \ 294 - .callback = lis3lv02d_dmi_matched, \ 295 - .matches = { \ 296 - DMI_MATCH(DMI_PRODUCT_NAME, _name) \ 297 - }, \ 298 - .driver_data = &lis3lv02d_axis_##_axis \ 299 - } 300 - static struct dmi_system_id lis3lv02d_dmi_ids[] = { 301 - /* product names are truncated to match all kinds of a same model */ 302 - AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), 303 - AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), 304 - AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), 305 - AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), 306 - AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), 307 - AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), 308 - AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), 309 - { NULL, } 310 - /* Laptop models without axis info (yet): 311 - * "NC651xx" "HP Compaq 651" 312 - * "NC671xx" "HP Compaq 671" 313 - * "NC6910" "HP Compaq 6910" 314 - * HP Compaq 8710x Notebook PC / Mobile Workstation 315 - * "NC2400" "HP Compaq nc2400" 316 - * "NX74x0" "HP Compaq nx74" 317 - * "NX6325" "HP Compaq nx6325" 318 - * "NC4400" "HP Compaq nc4400" 319 - */ 320 - }; 321 - 322 - static int lis3lv02d_add(struct acpi_device *device) 323 - { 324 - u8 val; 325 - 326 - if (!device) 327 - return -EINVAL; 328 - 329 - adev.device = device; 330 - strcpy(acpi_device_name(device), DRIVER_NAME); 331 - strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); 332 - device->driver_data = &adev; 333 - 334 - lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); 335 - if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { 336 - printk(KERN_ERR DRIVER_NAME 337 - ": Accelerometer chip not LIS3LV02D{L,Q}\n"); 338 - } 339 - 340 - /* If possible use a "standard" axes order */ 341 - if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { 342 - printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " 343 - "using default axes configuration\n"); 344 - adev.ac = lis3lv02d_axis_normal; 345 - } 346 - 347 - return lis3lv02d_init_device(&adev); 348 - } 349 - 350 - static int lis3lv02d_remove(struct acpi_device *device, int type) 351 - { 352 - if (!device) 353 - return -EINVAL; 354 - 355 - lis3lv02d_joystick_disable(); 356 - lis3lv02d_poweroff(device->handle); 357 - 358 - return lis3lv02d_remove_fs(); 359 - } 360 - 361 362 /* Sysfs stuff */ 363 static ssize_t lis3lv02d_position_show(struct device *dev, ··· 310 int val; 311 312 lis3lv02d_increase_use(&adev); 313 - lis3lv02d_acpi_read(adev.device->handle, CTRL_REG1, &ctrl); 314 lis3lv02d_decrease_use(&adev); 315 val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; 316 return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); ··· 332 .attrs = lis3lv02d_attributes 333 }; 334 335 static int lis3lv02d_add_fs(struct acpi_device *device) 336 { 337 adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); ··· 342 return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); 343 } 344 345 - static int lis3lv02d_remove_fs(void) 346 { 347 sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); 348 platform_device_unregister(adev.pdev); 349 return 0; 350 } 351 - 352 - /* For the HP MDPS aka 3D Driveguard */ 353 - static struct acpi_driver lis3lv02d_driver = { 354 - .name = DRIVER_NAME, 355 - .class = ACPI_MDPS_CLASS, 356 - .ids = lis3lv02d_device_ids, 357 - .ops = { 358 - .add = lis3lv02d_add, 359 - .remove = lis3lv02d_remove, 360 - .suspend = lis3lv02d_suspend, 361 - .resume = lis3lv02d_resume, 362 - } 363 - }; 364 - 365 - static int __init lis3lv02d_init_module(void) 366 - { 367 - int ret; 368 - 369 - if (acpi_disabled) 370 - return -ENODEV; 371 - 372 - ret = acpi_bus_register_driver(&lis3lv02d_driver); 373 - if (ret < 0) 374 - return ret; 375 - 376 - printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); 377 - 378 - return 0; 379 - } 380 - 381 - static void __exit lis3lv02d_exit_module(void) 382 - { 383 - acpi_bus_unregister_driver(&lis3lv02d_driver); 384 - } 385 386 MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); 387 MODULE_AUTHOR("Yan Burman and Eric Piel"); 388 MODULE_LICENSE("GPL"); 389 390 - module_init(lis3lv02d_init_module); 391 - module_exit(lis3lv02d_exit_module);
··· 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 ··· 39 #include "lis3lv02d.h" 40 41 #define DRIVER_NAME "lis3lv02d" 42 43 /* joystick device poll interval in milliseconds */ 44 #define MDPS_POLL_INTERVAL 50 ··· 55 /* Maximum value our axis may get for the input device (signed 12 bits) */ 56 #define MDPS_MAX_VAL 2048 57 58 + struct acpi_lis3lv02d adev; 59 + EXPORT_SYMBOL_GPL(adev); 60 61 static int lis3lv02d_add_fs(struct acpi_device *device); 62 63 static s16 lis3lv02d_read_16(acpi_handle handle, int reg) 64 { 65 u8 lo, hi; 66 67 + adev.read(handle, reg, &lo); 68 + adev.read(handle, reg + 1, &hi); 69 /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ 70 return (s16)((hi << 8) | lo); 71 } ··· 190 *z = lis3lv02d_get_axis(adev.ac.z, position); 191 } 192 193 + void lis3lv02d_poweroff(acpi_handle handle) 194 { 195 adev.is_on = 0; 196 /* disable X,Y,Z axis and power down */ 197 + adev.write(handle, CTRL_REG1, 0x00); 198 } 199 + EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); 200 201 + void lis3lv02d_poweron(acpi_handle handle) 202 { 203 u8 val; 204 205 adev.is_on = 1; 206 + adev.init(handle); 207 + adev.write(handle, FF_WU_CFG, 0); 208 /* 209 * BDU: LSB and MSB values are not updated until both have been read. 210 * So the value read will always be correct. 211 * IEN: Interrupt for free-fall and DD, not for data-ready. 212 */ 213 + adev.read(handle, CTRL_REG2, &val); 214 val |= CTRL2_BDU | CTRL2_IEN; 215 + adev.write(handle, CTRL_REG2, val); 216 } 217 + EXPORT_SYMBOL_GPL(lis3lv02d_poweron); 218 219 /* 220 * To be called before starting to use the device. It makes sure that the ··· 315 lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); 316 } 317 318 + int lis3lv02d_joystick_enable(void) 319 { 320 int err; 321 ··· 349 350 return err; 351 } 352 + EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); 353 354 + void lis3lv02d_joystick_disable(void) 355 { 356 if (!adev.idev) 357 return; ··· 358 input_unregister_device(adev.idev); 359 adev.idev = NULL; 360 } 361 + EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); 362 363 /* 364 * Initialise the accelerometer and the various subsystems. 365 * Should be rather independant of the bus system. 366 */ 367 + int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) 368 { 369 mutex_init(&dev->lock); 370 lis3lv02d_add_fs(dev->device); ··· 376 lis3lv02d_decrease_use(dev); 377 return 0; 378 } 379 + EXPORT_SYMBOL_GPL(lis3lv02d_init_device); 380 381 /* Sysfs stuff */ 382 static ssize_t lis3lv02d_position_show(struct device *dev, ··· 501 int val; 502 503 lis3lv02d_increase_use(&adev); 504 + adev.read(adev.device->handle, CTRL_REG1, &ctrl); 505 lis3lv02d_decrease_use(&adev); 506 val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; 507 return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); ··· 523 .attrs = lis3lv02d_attributes 524 }; 525 526 + 527 static int lis3lv02d_add_fs(struct acpi_device *device) 528 { 529 adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); ··· 532 return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); 533 } 534 535 + int lis3lv02d_remove_fs(void) 536 { 537 sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); 538 platform_device_unregister(adev.pdev); 539 return 0; 540 } 541 + EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); 542 543 MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); 544 MODULE_AUTHOR("Yan Burman and Eric Piel"); 545 MODULE_LICENSE("GPL"); 546
+34 -1
drivers/hwmon/lis3lv02d.h
··· 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 25 * LIS3L02DQ) but not in the HP laptops and they have slightly different registers. 26 - * They can also be connected via I²C. 27 */ 28 29 #define LIS3LV02DL_ID 0x3A /* Also the LIS3LV02DQ */ ··· 147 DD_SRC_IA = 0x40, 148 }; 149
··· 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 25 * LIS3L02DQ) but not in the HP laptops and they have slightly different registers. 26 + * They can also be connected via I²C. 27 */ 28 29 #define LIS3LV02DL_ID 0x3A /* Also the LIS3LV02DQ */ ··· 147 DD_SRC_IA = 0x40, 148 }; 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;