162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2007-2008 Yan Burman
662306a36Sopenharmony_ci *  Copyright (C) 2008 Eric Piel
762306a36Sopenharmony_ci *  Copyright (C) 2008-2009 Pavel Machek
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/dmi.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/types.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/interrupt.h>
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci#include <linux/wait.h>
2162306a36Sopenharmony_ci#include <linux/poll.h>
2262306a36Sopenharmony_ci#include <linux/freezer.h>
2362306a36Sopenharmony_ci#include <linux/uaccess.h>
2462306a36Sopenharmony_ci#include <linux/leds.h>
2562306a36Sopenharmony_ci#include <linux/atomic.h>
2662306a36Sopenharmony_ci#include <linux/acpi.h>
2762306a36Sopenharmony_ci#include <linux/i8042.h>
2862306a36Sopenharmony_ci#include <linux/serio.h>
2962306a36Sopenharmony_ci#include "../../../misc/lis3lv02d/lis3lv02d.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Delayed LEDs infrastructure ------------------------------------ */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* Special LED class that can defer work */
3462306a36Sopenharmony_cistruct delayed_led_classdev {
3562306a36Sopenharmony_ci	struct led_classdev led_classdev;
3662306a36Sopenharmony_ci	struct work_struct work;
3762306a36Sopenharmony_ci	enum led_brightness new_brightness;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	unsigned int led;		/* For driver */
4062306a36Sopenharmony_ci	void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value);
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic inline void delayed_set_status_worker(struct work_struct *work)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct delayed_led_classdev *data =
4662306a36Sopenharmony_ci			container_of(work, struct delayed_led_classdev, work);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	data->set_brightness(data, data->new_brightness);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic inline void delayed_sysfs_set(struct led_classdev *led_cdev,
5262306a36Sopenharmony_ci			      enum led_brightness brightness)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct delayed_led_classdev *data = container_of(led_cdev,
5562306a36Sopenharmony_ci			     struct delayed_led_classdev, led_classdev);
5662306a36Sopenharmony_ci	data->new_brightness = brightness;
5762306a36Sopenharmony_ci	schedule_work(&data->work);
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* HP-specific accelerometer driver ------------------------------------ */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* e0 25, e0 26, e0 27, e0 28 are scan codes that the accelerometer with acpi id
6362306a36Sopenharmony_ci * HPQ6000 sends through the keyboard bus */
6462306a36Sopenharmony_ci#define ACCEL_1 0x25
6562306a36Sopenharmony_ci#define ACCEL_2 0x26
6662306a36Sopenharmony_ci#define ACCEL_3 0x27
6762306a36Sopenharmony_ci#define ACCEL_4 0x28
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* For automatic insertion of the module */
7062306a36Sopenharmony_cistatic const struct acpi_device_id lis3lv02d_device_ids[] = {
7162306a36Sopenharmony_ci	{"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */
7262306a36Sopenharmony_ci	{"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */
7362306a36Sopenharmony_ci	{"HPQ6007", 0}, /* HP Mobile Data Protection System PNP */
7462306a36Sopenharmony_ci	{"", 0},
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/**
7962306a36Sopenharmony_ci * lis3lv02d_acpi_init - initialize the device for ACPI
8062306a36Sopenharmony_ci * @lis3: pointer to the device struct
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * Returns 0 on success.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_cistatic int lis3lv02d_acpi_init(struct lis3lv02d *lis3)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	return 0;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci/**
9062306a36Sopenharmony_ci * lis3lv02d_acpi_read - ACPI ALRD method: read a register
9162306a36Sopenharmony_ci * @lis3: pointer to the device struct
9262306a36Sopenharmony_ci * @reg:    the register to read
9362306a36Sopenharmony_ci * @ret:    result of the operation
9462306a36Sopenharmony_ci *
9562306a36Sopenharmony_ci * Returns 0 on success.
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_cistatic int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct acpi_device *dev = lis3->bus_priv;
10062306a36Sopenharmony_ci	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
10162306a36Sopenharmony_ci	struct acpi_object_list args = { 1, &arg0 };
10262306a36Sopenharmony_ci	unsigned long long lret;
10362306a36Sopenharmony_ci	acpi_status status;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	arg0.integer.value = reg;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	status = acpi_evaluate_integer(dev->handle, "ALRD", &args, &lret);
10862306a36Sopenharmony_ci	if (ACPI_FAILURE(status))
10962306a36Sopenharmony_ci		return -EINVAL;
11062306a36Sopenharmony_ci	*ret = lret;
11162306a36Sopenharmony_ci	return 0;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/**
11562306a36Sopenharmony_ci * lis3lv02d_acpi_write - ACPI ALWR method: write to a register
11662306a36Sopenharmony_ci * @lis3: pointer to the device struct
11762306a36Sopenharmony_ci * @reg:    the register to write to
11862306a36Sopenharmony_ci * @val:    the value to write
11962306a36Sopenharmony_ci *
12062306a36Sopenharmony_ci * Returns 0 on success.
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_cistatic int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct acpi_device *dev = lis3->bus_priv;
12562306a36Sopenharmony_ci	unsigned long long ret; /* Not used when writting */
12662306a36Sopenharmony_ci	union acpi_object in_obj[2];
12762306a36Sopenharmony_ci	struct acpi_object_list args = { 2, in_obj };
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	in_obj[0].type          = ACPI_TYPE_INTEGER;
13062306a36Sopenharmony_ci	in_obj[0].integer.value = reg;
13162306a36Sopenharmony_ci	in_obj[1].type          = ACPI_TYPE_INTEGER;
13262306a36Sopenharmony_ci	in_obj[1].integer.value = val;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (acpi_evaluate_integer(dev->handle, "ALWR", &args, &ret) != AE_OK)
13562306a36Sopenharmony_ci		return -EINVAL;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return 0;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	lis3_dev.ac = *((union axis_conversion *)dmi->driver_data);
14362306a36Sopenharmony_ci	pr_info("hardware type %s found\n", dmi->ident);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return 1;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/* Represents, for each axis seen by userspace, the corresponding hw axis (+1).
14962306a36Sopenharmony_ci * If the value is negative, the opposite of the hw value is used. */
15062306a36Sopenharmony_ci#define DEFINE_CONV(name, x, y, z)			      \
15162306a36Sopenharmony_ci	static union axis_conversion lis3lv02d_axis_##name = \
15262306a36Sopenharmony_ci		{ .as_array = { x, y, z } }
15362306a36Sopenharmony_ciDEFINE_CONV(normal, 1, 2, 3);
15462306a36Sopenharmony_ciDEFINE_CONV(y_inverted, 1, -2, 3);
15562306a36Sopenharmony_ciDEFINE_CONV(x_inverted, -1, 2, 3);
15662306a36Sopenharmony_ciDEFINE_CONV(x_inverted_usd, -1, 2, -3);
15762306a36Sopenharmony_ciDEFINE_CONV(z_inverted, 1, 2, -3);
15862306a36Sopenharmony_ciDEFINE_CONV(xy_swap, 2, 1, 3);
15962306a36Sopenharmony_ciDEFINE_CONV(xy_rotated_left, -2, 1, 3);
16062306a36Sopenharmony_ciDEFINE_CONV(xy_rotated_left_usd, -2, 1, -3);
16162306a36Sopenharmony_ciDEFINE_CONV(xy_swap_inverted, -2, -1, 3);
16262306a36Sopenharmony_ciDEFINE_CONV(xy_rotated_right, 2, -1, 3);
16362306a36Sopenharmony_ciDEFINE_CONV(xy_swap_yz_inverted, 2, -1, -3);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci#define AXIS_DMI_MATCH(_ident, _name, _axis) {		\
16662306a36Sopenharmony_ci	.ident = _ident,				\
16762306a36Sopenharmony_ci	.callback = lis3lv02d_dmi_matched,		\
16862306a36Sopenharmony_ci	.matches = {					\
16962306a36Sopenharmony_ci		DMI_MATCH(DMI_PRODUCT_NAME, _name)	\
17062306a36Sopenharmony_ci	},						\
17162306a36Sopenharmony_ci	.driver_data = &lis3lv02d_axis_##_axis		\
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci#define AXIS_DMI_MATCH2(_ident, _class1, _name1,	\
17562306a36Sopenharmony_ci				_class2, _name2,	\
17662306a36Sopenharmony_ci				_axis) {		\
17762306a36Sopenharmony_ci	.ident = _ident,				\
17862306a36Sopenharmony_ci	.callback = lis3lv02d_dmi_matched,		\
17962306a36Sopenharmony_ci	.matches = {					\
18062306a36Sopenharmony_ci		DMI_MATCH(DMI_##_class1, _name1),	\
18162306a36Sopenharmony_ci		DMI_MATCH(DMI_##_class2, _name2),	\
18262306a36Sopenharmony_ci	},						\
18362306a36Sopenharmony_ci	.driver_data = &lis3lv02d_axis_##_axis		\
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_cistatic const struct dmi_system_id lis3lv02d_dmi_ids[] = {
18662306a36Sopenharmony_ci	/* product names are truncated to match all kinds of a same model */
18762306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted),
18862306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted),
18962306a36Sopenharmony_ci	AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted),
19062306a36Sopenharmony_ci	AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted),
19162306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted),
19262306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC2710", "HP Compaq 2710", xy_swap),
19362306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted),
19462306a36Sopenharmony_ci	AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left),
19562306a36Sopenharmony_ci	AXIS_DMI_MATCH("HP2140", "HP 2140", xy_swap_inverted),
19662306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC653x", "HP Compaq 653", xy_rotated_left_usd),
19762306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC6730b", "HP Compaq 6730b", xy_rotated_left_usd),
19862306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC6730s", "HP Compaq 6730s", xy_swap),
19962306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC651xx", "HP Compaq 651", xy_rotated_right),
20062306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC6710x", "HP Compaq 6710", xy_swap_yz_inverted),
20162306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted),
20262306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right),
20362306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap),
20462306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC854xx", "HP EliteBook 854", y_inverted),
20562306a36Sopenharmony_ci	AXIS_DMI_MATCH("NC273xx", "HP EliteBook 273", y_inverted),
20662306a36Sopenharmony_ci	/* Intel-based HP Pavilion dv5 */
20762306a36Sopenharmony_ci	AXIS_DMI_MATCH2("HPDV5_I",
20862306a36Sopenharmony_ci			PRODUCT_NAME, "HP Pavilion dv5",
20962306a36Sopenharmony_ci			BOARD_NAME, "3603",
21062306a36Sopenharmony_ci			x_inverted),
21162306a36Sopenharmony_ci	/* AMD-based HP Pavilion dv5 */
21262306a36Sopenharmony_ci	AXIS_DMI_MATCH2("HPDV5_A",
21362306a36Sopenharmony_ci			PRODUCT_NAME, "HP Pavilion dv5",
21462306a36Sopenharmony_ci			BOARD_NAME, "3600",
21562306a36Sopenharmony_ci			y_inverted),
21662306a36Sopenharmony_ci	AXIS_DMI_MATCH("DV7", "HP Pavilion dv7", x_inverted),
21762306a36Sopenharmony_ci	AXIS_DMI_MATCH("HP8710", "HP Compaq 8710", y_inverted),
21862306a36Sopenharmony_ci	AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted),
21962306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left),
22062306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd),
22162306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted),
22262306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left),
22362306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB450G0", "HP ProBook 450 G0", x_inverted),
22462306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted),
22562306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
22662306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted),
22762306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB655x", "HP ProBook 655", xy_swap_inverted),
22862306a36Sopenharmony_ci	AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd),
22962306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap),
23062306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap),
23162306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap),
23262306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted),
23362306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted),
23462306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPZBook17G5", "HP ZBook 17 G5", x_inverted),
23562306a36Sopenharmony_ci	AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted),
23662306a36Sopenharmony_ci	{ NULL, }
23762306a36Sopenharmony_ci/* Laptop models without axis info (yet):
23862306a36Sopenharmony_ci * "NC6910" "HP Compaq 6910"
23962306a36Sopenharmony_ci * "NC2400" "HP Compaq nc2400"
24062306a36Sopenharmony_ci * "NX74x0" "HP Compaq nx74"
24162306a36Sopenharmony_ci * "NX6325" "HP Compaq nx6325"
24262306a36Sopenharmony_ci * "NC4400" "HP Compaq nc4400"
24362306a36Sopenharmony_ci */
24462306a36Sopenharmony_ci};
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct acpi_device *dev = lis3_dev.bus_priv;
24962306a36Sopenharmony_ci	unsigned long long ret; /* Not used when writing */
25062306a36Sopenharmony_ci	union acpi_object in_obj[1];
25162306a36Sopenharmony_ci	struct acpi_object_list args = { 1, in_obj };
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	in_obj[0].type          = ACPI_TYPE_INTEGER;
25462306a36Sopenharmony_ci	in_obj[0].integer.value = !!value;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	acpi_evaluate_integer(dev->handle, "ALED", &args, &ret);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic struct delayed_led_classdev hpled_led = {
26062306a36Sopenharmony_ci	.led_classdev = {
26162306a36Sopenharmony_ci		.name			= "hp::hddprotect",
26262306a36Sopenharmony_ci		.default_trigger	= "none",
26362306a36Sopenharmony_ci		.brightness_set		= delayed_sysfs_set,
26462306a36Sopenharmony_ci		.flags                  = LED_CORE_SUSPENDRESUME,
26562306a36Sopenharmony_ci	},
26662306a36Sopenharmony_ci	.set_brightness = hpled_set,
26762306a36Sopenharmony_ci};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic bool hp_accel_i8042_filter(unsigned char data, unsigned char str,
27062306a36Sopenharmony_ci				  struct serio *port)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	static bool extended;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (str & I8042_STR_AUXDATA)
27562306a36Sopenharmony_ci		return false;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (data == 0xe0) {
27862306a36Sopenharmony_ci		extended = true;
27962306a36Sopenharmony_ci		return true;
28062306a36Sopenharmony_ci	} else if (unlikely(extended)) {
28162306a36Sopenharmony_ci		extended = false;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		switch (data) {
28462306a36Sopenharmony_ci		case ACCEL_1:
28562306a36Sopenharmony_ci		case ACCEL_2:
28662306a36Sopenharmony_ci		case ACCEL_3:
28762306a36Sopenharmony_ci		case ACCEL_4:
28862306a36Sopenharmony_ci			return true;
28962306a36Sopenharmony_ci		default:
29062306a36Sopenharmony_ci			serio_interrupt(port, 0xe0, 0);
29162306a36Sopenharmony_ci			return false;
29262306a36Sopenharmony_ci		}
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return false;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int lis3lv02d_probe(struct platform_device *device)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	int ret;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	lis3_dev.bus_priv = ACPI_COMPANION(&device->dev);
30362306a36Sopenharmony_ci	lis3_dev.init = lis3lv02d_acpi_init;
30462306a36Sopenharmony_ci	lis3_dev.read = lis3lv02d_acpi_read;
30562306a36Sopenharmony_ci	lis3_dev.write = lis3lv02d_acpi_write;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* obtain IRQ number of our device from ACPI */
30862306a36Sopenharmony_ci	ret = platform_get_irq_optional(device, 0);
30962306a36Sopenharmony_ci	if (ret > 0)
31062306a36Sopenharmony_ci		lis3_dev.irq = ret;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* If possible use a "standard" axes order */
31362306a36Sopenharmony_ci	if (lis3_dev.ac.x && lis3_dev.ac.y && lis3_dev.ac.z) {
31462306a36Sopenharmony_ci		pr_info("Using custom axes %d,%d,%d\n",
31562306a36Sopenharmony_ci			lis3_dev.ac.x, lis3_dev.ac.y, lis3_dev.ac.z);
31662306a36Sopenharmony_ci	} else if (dmi_check_system(lis3lv02d_dmi_ids) == 0) {
31762306a36Sopenharmony_ci		pr_info("laptop model unknown, using default axes configuration\n");
31862306a36Sopenharmony_ci		lis3_dev.ac = lis3lv02d_axis_normal;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* call the core layer do its init */
32262306a36Sopenharmony_ci	ret = lis3lv02d_init_device(&lis3_dev);
32362306a36Sopenharmony_ci	if (ret)
32462306a36Sopenharmony_ci		return ret;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	/* filter to remove HPQ6000 accelerometer data
32762306a36Sopenharmony_ci	 * from keyboard bus stream */
32862306a36Sopenharmony_ci	if (strstr(dev_name(&device->dev), "HPQ6000"))
32962306a36Sopenharmony_ci		i8042_install_filter(hp_accel_i8042_filter);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	INIT_WORK(&hpled_led.work, delayed_set_status_worker);
33262306a36Sopenharmony_ci	ret = led_classdev_register(NULL, &hpled_led.led_classdev);
33362306a36Sopenharmony_ci	if (ret) {
33462306a36Sopenharmony_ci		i8042_remove_filter(hp_accel_i8042_filter);
33562306a36Sopenharmony_ci		lis3lv02d_joystick_disable(&lis3_dev);
33662306a36Sopenharmony_ci		lis3lv02d_poweroff(&lis3_dev);
33762306a36Sopenharmony_ci		flush_work(&hpled_led.work);
33862306a36Sopenharmony_ci		lis3lv02d_remove_fs(&lis3_dev);
33962306a36Sopenharmony_ci		return ret;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return ret;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void lis3lv02d_remove(struct platform_device *device)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	i8042_remove_filter(hp_accel_i8042_filter);
34862306a36Sopenharmony_ci	lis3lv02d_joystick_disable(&lis3_dev);
34962306a36Sopenharmony_ci	lis3lv02d_poweroff(&lis3_dev);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	led_classdev_unregister(&hpled_led.led_classdev);
35262306a36Sopenharmony_ci	flush_work(&hpled_led.work);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	lis3lv02d_remove_fs(&lis3_dev);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic int __maybe_unused lis3lv02d_suspend(struct device *dev)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	/* make sure the device is off when we suspend */
36062306a36Sopenharmony_ci	lis3lv02d_poweroff(&lis3_dev);
36162306a36Sopenharmony_ci	return 0;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic int __maybe_unused lis3lv02d_resume(struct device *dev)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	lis3lv02d_poweron(&lis3_dev);
36762306a36Sopenharmony_ci	return 0;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/* For the HP MDPS aka 3D Driveguard */
37362306a36Sopenharmony_cistatic struct platform_driver lis3lv02d_driver = {
37462306a36Sopenharmony_ci	.probe	= lis3lv02d_probe,
37562306a36Sopenharmony_ci	.remove_new = lis3lv02d_remove,
37662306a36Sopenharmony_ci	.driver	= {
37762306a36Sopenharmony_ci		.name	= "hp_accel",
37862306a36Sopenharmony_ci		.pm	= &hp_accel_pm,
37962306a36Sopenharmony_ci		.acpi_match_table = lis3lv02d_device_ids,
38062306a36Sopenharmony_ci	},
38162306a36Sopenharmony_ci};
38262306a36Sopenharmony_cimodule_platform_driver(lis3lv02d_driver);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ciMODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED.");
38562306a36Sopenharmony_ciMODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek");
38662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
387