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