1cfce41a6d643c001d416ead960caf04fae2d609aEric Piel/* 2cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS 3cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * 4cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * Copyright (C) 2007-2008 Yan Burman 5cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * Copyright (C) 2008 Eric Piel 69e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek * Copyright (C) 2008-2009 Pavel Machek 7cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * 8cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * This program is free software; you can redistribute it and/or modify 9cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * it under the terms of the GNU General Public License as published by 10cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * the Free Software Foundation; either version 2 of the License, or 11cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * (at your option) any later version. 12cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * 13cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * This program is distributed in the hope that it will be useful, 14cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * but WITHOUT ANY WARRANTY; without even the implied warranty of 15cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * GNU General Public License for more details. 17cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * 18cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * You should have received a copy of the GNU General Public License 19cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * along with this program; if not, write to the Free Software 20cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21cfce41a6d643c001d416ead960caf04fae2d609aEric Piel */ 22cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 2353ab0e6888925ae3ebe931c060120b0f11c1923cJoe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2453ab0e6888925ae3ebe931c060120b0f11c1923cJoe Perches 25cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/kernel.h> 26cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/init.h> 27cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/dmi.h> 28cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/module.h> 29cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/types.h> 30cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/platform_device.h> 31cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/interrupt.h> 32cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/delay.h> 33cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/wait.h> 34cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/poll.h> 35cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/freezer.h> 36cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <linux/uaccess.h> 379e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel#include <linux/leds.h> 38efcfed9bad88be8193ee6a1b8e72d7381e7b0e0eJean Delvare#include <linux/atomic.h> 39cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#include <acpi/acpi_drivers.h> 40ff606677f6a47c63329cf8e6c7cf978c29f2d736Jean Delvare#include "../../misc/lis3lv02d/lis3lv02d.h" 41cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 4296b4b9bfea28e38c38d26ca47e82ac0fbe2f28b6Jean Delvare#define DRIVER_NAME "hp_accel" 43cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#define ACPI_MDPS_CLASS "accelerometer" 44cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 459e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek/* Delayed LEDs infrastructure ------------------------------------ */ 469e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek 479e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek/* Special LED class that can defer work */ 489e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machekstruct delayed_led_classdev { 499e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek struct led_classdev led_classdev; 509e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek struct work_struct work; 519e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek enum led_brightness new_brightness; 529e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek 539e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek unsigned int led; /* For driver */ 549e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); 559e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek}; 569e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek 579e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machekstatic inline void delayed_set_status_worker(struct work_struct *work) 589e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek{ 599e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek struct delayed_led_classdev *data = 609e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek container_of(work, struct delayed_led_classdev, work); 619e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek 629e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek data->set_brightness(data, data->new_brightness); 639e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek} 649e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek 659e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machekstatic inline void delayed_sysfs_set(struct led_classdev *led_cdev, 669e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek enum led_brightness brightness) 679e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek{ 689e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek struct delayed_led_classdev *data = container_of(led_cdev, 699e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek struct delayed_led_classdev, led_classdev); 709e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek data->new_brightness = brightness; 719e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek schedule_work(&data->work); 729e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek} 739e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek 749e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek/* HP-specific accelerometer driver ------------------------------------ */ 75cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 76cfce41a6d643c001d416ead960caf04fae2d609aEric Piel/* For automatic insertion of the module */ 77cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstatic struct acpi_device_id lis3lv02d_device_ids[] = { 78cfce41a6d643c001d416ead960caf04fae2d609aEric Piel {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ 7924570d2293d8874cf0041616af4dc92929f30a7cTakashi Iwai {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ 80cfce41a6d643c001d416ead960caf04fae2d609aEric Piel {"", 0}, 81cfce41a6d643c001d416ead960caf04fae2d609aEric Piel}; 82cfce41a6d643c001d416ead960caf04fae2d609aEric PielMODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); 83cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 84cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 85cfce41a6d643c001d416ead960caf04fae2d609aEric Piel/** 86cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. 87a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack * @lis3: pointer to the device struct 88cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * 89a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack * Returns 0 on success. 90cfce41a6d643c001d416ead960caf04fae2d609aEric Piel */ 91a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mackint lis3lv02d_acpi_init(struct lis3lv02d *lis3) 92cfce41a6d643c001d416ead960caf04fae2d609aEric Piel{ 93a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack struct acpi_device *dev = lis3->bus_priv; 94a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack if (acpi_evaluate_object(dev->handle, METHOD_NAME__INI, 95a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack NULL, NULL) != AE_OK) 96a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack return -EINVAL; 97a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack 98a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack return 0; 99cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 100cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 101cfce41a6d643c001d416ead960caf04fae2d609aEric Piel/** 102cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * lis3lv02d_acpi_read - ACPI ALRD method: read a register 103a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack * @lis3: pointer to the device struct 104cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * @reg: the register to read 105cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * @ret: result of the operation 106cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * 107a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack * Returns 0 on success. 108cfce41a6d643c001d416ead960caf04fae2d609aEric Piel */ 109a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mackint lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) 110cfce41a6d643c001d416ead960caf04fae2d609aEric Piel{ 111a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack struct acpi_device *dev = lis3->bus_priv; 112cfce41a6d643c001d416ead960caf04fae2d609aEric Piel union acpi_object arg0 = { ACPI_TYPE_INTEGER }; 113cfce41a6d643c001d416ead960caf04fae2d609aEric Piel struct acpi_object_list args = { 1, &arg0 }; 114cfce41a6d643c001d416ead960caf04fae2d609aEric Piel unsigned long long lret; 115cfce41a6d643c001d416ead960caf04fae2d609aEric Piel acpi_status status; 116cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 117cfce41a6d643c001d416ead960caf04fae2d609aEric Piel arg0.integer.value = reg; 118cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 119a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret); 120cfce41a6d643c001d416ead960caf04fae2d609aEric Piel *ret = lret; 121a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack return (status != AE_OK) ? -EINVAL : 0; 122cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 123cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 124cfce41a6d643c001d416ead960caf04fae2d609aEric Piel/** 125cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * lis3lv02d_acpi_write - ACPI ALWR method: write to a register 126a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack * @lis3: pointer to the device struct 127cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * @reg: the register to write to 128cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * @val: the value to write 129cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * 130a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack * Returns 0 on success. 131cfce41a6d643c001d416ead960caf04fae2d609aEric Piel */ 132a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mackint lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) 133cfce41a6d643c001d416ead960caf04fae2d609aEric Piel{ 134a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack struct acpi_device *dev = lis3->bus_priv; 135cfce41a6d643c001d416ead960caf04fae2d609aEric Piel unsigned long long ret; /* Not used when writting */ 136cfce41a6d643c001d416ead960caf04fae2d609aEric Piel union acpi_object in_obj[2]; 137cfce41a6d643c001d416ead960caf04fae2d609aEric Piel struct acpi_object_list args = { 2, in_obj }; 138cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 139cfce41a6d643c001d416ead960caf04fae2d609aEric Piel in_obj[0].type = ACPI_TYPE_INTEGER; 140cfce41a6d643c001d416ead960caf04fae2d609aEric Piel in_obj[0].integer.value = reg; 141cfce41a6d643c001d416ead960caf04fae2d609aEric Piel in_obj[1].type = ACPI_TYPE_INTEGER; 142cfce41a6d643c001d416ead960caf04fae2d609aEric Piel in_obj[1].integer.value = val; 143cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 144a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK) 145a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack return -EINVAL; 146a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack 147a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack return 0; 148cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 149cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 150cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstatic int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) 151cfce41a6d643c001d416ead960caf04fae2d609aEric Piel{ 1522ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai lis3_dev.ac = *((union axis_conversion *)dmi->driver_data); 15353ab0e6888925ae3ebe931c060120b0f11c1923cJoe Perches pr_info("hardware type %s found\n", dmi->ident); 154cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 155cfce41a6d643c001d416ead960caf04fae2d609aEric Piel return 1; 156cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 157cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 158cfce41a6d643c001d416ead960caf04fae2d609aEric Piel/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). 159cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * If the value is negative, the opposite of the hw value is used. */ 1602ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai#define DEFINE_CONV(name, x, y, z) \ 1612ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai static union axis_conversion lis3lv02d_axis_##name = \ 1622ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai { .as_array = { x, y, z } } 1632ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiDEFINE_CONV(normal, 1, 2, 3); 1642ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiDEFINE_CONV(y_inverted, 1, -2, 3); 1652ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiDEFINE_CONV(x_inverted, -1, 2, 3); 1662ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiDEFINE_CONV(z_inverted, 1, 2, -3); 1672ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiDEFINE_CONV(xy_swap, 2, 1, 3); 1682ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiDEFINE_CONV(xy_rotated_left, -2, 1, 3); 1692ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiDEFINE_CONV(xy_rotated_left_usd, -2, 1, -3); 1702ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiDEFINE_CONV(xy_swap_inverted, -2, -1, 3); 1712ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiDEFINE_CONV(xy_rotated_right, 2, -1, 3); 1722ee321440e3a594dcdd9981e68e5e302447047a2Takashi IwaiDEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3); 173cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 174cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ 175cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .ident = _ident, \ 176cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .callback = lis3lv02d_dmi_matched, \ 177cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .matches = { \ 178cfce41a6d643c001d416ead960caf04fae2d609aEric Piel DMI_MATCH(DMI_PRODUCT_NAME, _name) \ 179cfce41a6d643c001d416ead960caf04fae2d609aEric Piel }, \ 180cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .driver_data = &lis3lv02d_axis_##_axis \ 181cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 1829ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta 1839ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta#define AXIS_DMI_MATCH2(_ident, _class1, _name1, \ 1849ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta _class2, _name2, \ 1859ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta _axis) { \ 1869ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta .ident = _ident, \ 1879ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta .callback = lis3lv02d_dmi_matched, \ 1889ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta .matches = { \ 1899ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta DMI_MATCH(DMI_##_class1, _name1), \ 1909ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta DMI_MATCH(DMI_##_class2, _name2), \ 1919ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta }, \ 1929ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta .driver_data = &lis3lv02d_axis_##_axis \ 1939ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta} 194cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstatic struct dmi_system_id lis3lv02d_dmi_ids[] = { 195cfce41a6d643c001d416ead960caf04fae2d609aEric Piel /* product names are truncated to match all kinds of a same model */ 196cfce41a6d643c001d416ead960caf04fae2d609aEric Piel AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), 197cfce41a6d643c001d416ead960caf04fae2d609aEric Piel AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), 198cfce41a6d643c001d416ead960caf04fae2d609aEric Piel AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), 199cfce41a6d643c001d416ead960caf04fae2d609aEric Piel AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), 200cfce41a6d643c001d416ead960caf04fae2d609aEric Piel AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), 2010093716e6dd18dad554bef81cc788a4c50d32a09Eric Piel AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap), 202cfce41a6d643c001d416ead960caf04fae2d609aEric Piel AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), 203cfce41a6d643c001d416ead960caf04fae2d609aEric Piel AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), 2049d7639d33a348d5637daa50bba27497e9f5dac64Pavel Machek AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted), 20580eda5fb5885d601ed2c712a0530770c0a23cc04Eric Piel AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd), 206a03018ff712e34dbebeae33c1b6b1306c2f9561bPavel Herrmann AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd), 207a03018ff712e34dbebeae33c1b6b1306c2f9561bPavel Herrmann AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap), 2086bfef2b3cfe1144bd1dc3aface9c63a3f06732a7Jiri Tersel AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right), 2090093716e6dd18dad554bef81cc788a4c50d32a09Eric Piel AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted), 2100093716e6dd18dad554bef81cc788a4c50d32a09Eric Piel AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), 2110093716e6dd18dad554bef81cc788a4c50d32a09Eric Piel AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), 212af19611c4dad0dc5139a81d4a5ca352d21f904d6Éric Piel AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), 213cdeaf62255d171dd159f0bdc453efd7ff31c6916Éric Piel AXIS_DMI_MATCH("NC854xx", "HP EliteBook 854", y_inverted), 214a17b81beeb76a7cc3551fc17d7aa73b303dee52cÉric Piel AXIS_DMI_MATCH("NC273xx", "HP EliteBook 273", y_inverted), 2159ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta /* Intel-based HP Pavilion dv5 */ 2169ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta AXIS_DMI_MATCH2("HPDV5_I", 2179ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta PRODUCT_NAME, "HP Pavilion dv5", 2189ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta BOARD_NAME, "3603", 2199ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta x_inverted), 2209ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta /* AMD-based HP Pavilion dv5 */ 2219ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta AXIS_DMI_MATCH2("HPDV5_A", 2229ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta PRODUCT_NAME, "HP Pavilion dv5", 2239ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta BOARD_NAME, "3600", 2249ccf3b5e8409927835c4d38cb2f380c9e4349e76Giuseppe Bilotta y_inverted), 2259d7639d33a348d5637daa50bba27497e9f5dac64Pavel Machek AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted), 22612a324b6a3758f04a952b99d75a625732d767585Luca Cappa AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted), 2272545f038f4af0ff9945d47c10f988418dda50140Ian E. Morgan AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted), 2289bd14a839d2ec703c56593a7209f2310c16d6478Takashi Iwai AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left), 2299bd14a839d2ec703c56593a7209f2310c16d6478Takashi Iwai AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left), 2309bd14a839d2ec703c56593a7209f2310c16d6478Takashi Iwai AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), 2319bd14a839d2ec703c56593a7209f2310c16d6478Takashi Iwai AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), 2324e70598c3b56e6fec551454c495d4d4025834749Takashi Iwai AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), 233d0b6a971ed034054897df42f3e3000b4f57cccc5Éric Piel AXIS_DMI_MATCH("HPB655x", "HP ProBook 655", xy_swap_inverted), 23437394050b5be0fe87f96ed8848f11c3c2cd4d556Masanari Iida AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), 23585003c3850f4f0b1391df324058a7c5fa0927d25Takashi Iwai AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap), 23685003c3850f4f0b1391df324058a7c5fa0927d25Takashi Iwai AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap), 23785003c3850f4f0b1391df324058a7c5fa0927d25Takashi Iwai AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), 23885003c3850f4f0b1391df324058a7c5fa0927d25Takashi Iwai AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), 239cfce41a6d643c001d416ead960caf04fae2d609aEric Piel { NULL, } 240cfce41a6d643c001d416ead960caf04fae2d609aEric Piel/* Laptop models without axis info (yet): 241cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * "NC6910" "HP Compaq 6910" 242cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * "NC2400" "HP Compaq nc2400" 243cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * "NX74x0" "HP Compaq nx74" 244cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * "NX6325" "HP Compaq nx6325" 245cfce41a6d643c001d416ead960caf04fae2d609aEric Piel * "NC4400" "HP Compaq nc4400" 246cfce41a6d643c001d416ead960caf04fae2d609aEric Piel */ 247cfce41a6d643c001d416ead960caf04fae2d609aEric Piel}; 248cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 2499e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machekstatic void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) 2509e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel{ 251a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack struct acpi_device *dev = lis3_dev.bus_priv; 2529e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel unsigned long long ret; /* Not used when writing */ 2539e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel union acpi_object in_obj[1]; 2549e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel struct acpi_object_list args = { 1, in_obj }; 2559e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel 2569e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel in_obj[0].type = ACPI_TYPE_INTEGER; 2579e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek in_obj[0].integer.value = !!value; 2589e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel 259a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack acpi_evaluate_integer(dev->handle, "ALED", &args, &ret); 2609e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel} 2619e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel 2629e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machekstatic struct delayed_led_classdev hpled_led = { 2639e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek .led_classdev = { 2649e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek .name = "hp::hddprotect", 2659e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek .default_trigger = "none", 2669e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek .brightness_set = delayed_sysfs_set, 2679e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek .flags = LED_CORE_SUSPENDRESUME, 2689e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek }, 2699e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek .set_brightness = hpled_set, 2709e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel}; 271cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 272ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machekstatic acpi_status 273ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Macheklis3lv02d_get_resource(struct acpi_resource *resource, void *context) 274ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek{ 275ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek if (resource->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { 276ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek struct acpi_resource_extended_irq *irq; 277ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek u32 *device_irq = context; 278ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek 279ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek irq = &resource->data.extended_irq; 280ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek *device_irq = irq->interrupts[0]; 281ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek } 282ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek 283ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek return AE_OK; 284ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek} 285ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek 286ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machekstatic void lis3lv02d_enum_resources(struct acpi_device *device) 287ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek{ 288ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek acpi_status status; 289ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek 290ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, 291be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek lis3lv02d_get_resource, &lis3_dev.irq); 292ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek if (ACPI_FAILURE(status)) 293ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek printk(KERN_DEBUG DRIVER_NAME ": Error getting resources\n"); 294ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek} 295ef2cfc790bf5f0ff189b01eabc0f4feb5e8524dfPavel Machek 296cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstatic int lis3lv02d_add(struct acpi_device *device) 297cfce41a6d643c001d416ead960caf04fae2d609aEric Piel{ 2989e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel int ret; 299cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 300cfce41a6d643c001d416ead960caf04fae2d609aEric Piel if (!device) 301cfce41a6d643c001d416ead960caf04fae2d609aEric Piel return -EINVAL; 302cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 303a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack lis3_dev.bus_priv = device; 304be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek lis3_dev.init = lis3lv02d_acpi_init; 305be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek lis3_dev.read = lis3lv02d_acpi_read; 306be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek lis3_dev.write = lis3lv02d_acpi_write; 307cfce41a6d643c001d416ead960caf04fae2d609aEric Piel strcpy(acpi_device_name(device), DRIVER_NAME); 308cfce41a6d643c001d416ead960caf04fae2d609aEric Piel strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); 309be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek device->driver_data = &lis3_dev; 310cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 311a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack /* obtain IRQ number of our device from ACPI */ 312a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack lis3lv02d_enum_resources(device); 313cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 314cfce41a6d643c001d416ead960caf04fae2d609aEric Piel /* If possible use a "standard" axes order */ 3152ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) { 31653ab0e6888925ae3ebe931c060120b0f11c1923cJoe Perches pr_info("Using custom axes %d,%d,%d\n", 31753ab0e6888925ae3ebe931c060120b0f11c1923cJoe Perches lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z); 3182ee321440e3a594dcdd9981e68e5e302447047a2Takashi Iwai } else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { 31953ab0e6888925ae3ebe931c060120b0f11c1923cJoe Perches pr_info("laptop model unknown, using default axes configuration\n"); 320be84cfc588b19f14764d78556dc7b630ee8c914cPavel Machek lis3_dev.ac = lis3lv02d_axis_normal; 321cfce41a6d643c001d416ead960caf04fae2d609aEric Piel } 322cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 323a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack /* call the core layer do its init */ 324a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack ret = lis3lv02d_init_device(&lis3_dev); 3259e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel if (ret) 3269e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel return ret; 3279e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel 328a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack INIT_WORK(&hpled_led.work, delayed_set_status_worker); 329a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack ret = led_classdev_register(NULL, &hpled_led.led_classdev); 3309e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel if (ret) { 331e1e5687d75ef0ea5cbae63df48ff2fdcb5306f66Éric Piel lis3lv02d_joystick_disable(&lis3_dev); 332a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack lis3lv02d_poweroff(&lis3_dev); 3339e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek flush_work(&hpled_led.work); 3349e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel return ret; 3359e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel } 3369e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel 3379e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel return ret; 338cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 339cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 340cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstatic int lis3lv02d_remove(struct acpi_device *device, int type) 341cfce41a6d643c001d416ead960caf04fae2d609aEric Piel{ 342cfce41a6d643c001d416ead960caf04fae2d609aEric Piel if (!device) 343cfce41a6d643c001d416ead960caf04fae2d609aEric Piel return -EINVAL; 344cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 345e1e5687d75ef0ea5cbae63df48ff2fdcb5306f66Éric Piel lis3lv02d_joystick_disable(&lis3_dev); 346a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack lis3lv02d_poweroff(&lis3_dev); 347cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 3489e1c9d865543593ee92ec3a5075f064dec981a96Pavel Machek led_classdev_unregister(&hpled_led.led_classdev); 34906efbeb4a47b6f865e1c9d175ab9d6e90b69ae9eOliver Neukum flush_work(&hpled_led.work); 3509e0c79782143a816ba7d7f0f6e195091a97053f6Eric Piel 351a002ee896dfd08ce9fba44e9ae513c9094699a27Eric Piel return lis3lv02d_remove_fs(&lis3_dev); 352cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 353cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 354cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 355cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#ifdef CONFIG_PM 356cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstatic int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) 357cfce41a6d643c001d416ead960caf04fae2d609aEric Piel{ 358cfce41a6d643c001d416ead960caf04fae2d609aEric Piel /* make sure the device is off when we suspend */ 359a38da2ed74f628c1f3e907c772be21a66eccab9cDaniel Mack lis3lv02d_poweroff(&lis3_dev); 360cfce41a6d643c001d416ead960caf04fae2d609aEric Piel return 0; 361cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 362cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 363cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstatic int lis3lv02d_resume(struct acpi_device *device) 364cfce41a6d643c001d416ead960caf04fae2d609aEric Piel{ 3651510dd5954be5070e46b155eb32362dc73d9e9cbÉric Piel return lis3lv02d_poweron(&lis3_dev); 366cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 367cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#else 368cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#define lis3lv02d_suspend NULL 369cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#define lis3lv02d_resume NULL 370cfce41a6d643c001d416ead960caf04fae2d609aEric Piel#endif 371cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 372cfce41a6d643c001d416ead960caf04fae2d609aEric Piel/* For the HP MDPS aka 3D Driveguard */ 373cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstatic struct acpi_driver lis3lv02d_driver = { 374cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .name = DRIVER_NAME, 375cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .class = ACPI_MDPS_CLASS, 376cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .ids = lis3lv02d_device_ids, 377cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .ops = { 378cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .add = lis3lv02d_add, 379cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .remove = lis3lv02d_remove, 380cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .suspend = lis3lv02d_suspend, 381cfce41a6d643c001d416ead960caf04fae2d609aEric Piel .resume = lis3lv02d_resume, 382cfce41a6d643c001d416ead960caf04fae2d609aEric Piel } 383cfce41a6d643c001d416ead960caf04fae2d609aEric Piel}; 384cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 385cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstatic int __init lis3lv02d_init_module(void) 386cfce41a6d643c001d416ead960caf04fae2d609aEric Piel{ 387cfce41a6d643c001d416ead960caf04fae2d609aEric Piel int ret; 388cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 389cfce41a6d643c001d416ead960caf04fae2d609aEric Piel if (acpi_disabled) 390cfce41a6d643c001d416ead960caf04fae2d609aEric Piel return -ENODEV; 391cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 392cfce41a6d643c001d416ead960caf04fae2d609aEric Piel ret = acpi_bus_register_driver(&lis3lv02d_driver); 393cfce41a6d643c001d416ead960caf04fae2d609aEric Piel if (ret < 0) 394cfce41a6d643c001d416ead960caf04fae2d609aEric Piel return ret; 395cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 39653ab0e6888925ae3ebe931c060120b0f11c1923cJoe Perches pr_info("driver loaded\n"); 397cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 398cfce41a6d643c001d416ead960caf04fae2d609aEric Piel return 0; 399cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 400cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 401cfce41a6d643c001d416ead960caf04fae2d609aEric Pielstatic void __exit lis3lv02d_exit_module(void) 402cfce41a6d643c001d416ead960caf04fae2d609aEric Piel{ 403cfce41a6d643c001d416ead960caf04fae2d609aEric Piel acpi_bus_unregister_driver(&lis3lv02d_driver); 404cfce41a6d643c001d416ead960caf04fae2d609aEric Piel} 405cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 4069e0c79782143a816ba7d7f0f6e195091a97053f6Eric PielMODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); 407cfce41a6d643c001d416ead960caf04fae2d609aEric PielMODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); 408cfce41a6d643c001d416ead960caf04fae2d609aEric PielMODULE_LICENSE("GPL"); 409cfce41a6d643c001d416ead960caf04fae2d609aEric Piel 410cfce41a6d643c001d416ead960caf04fae2d609aEric Pielmodule_init(lis3lv02d_init_module); 411cfce41a6d643c001d416ead960caf04fae2d609aEric Pielmodule_exit(lis3lv02d_exit_module); 412