mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'platform-drivers-x86-v4.19-1' of git://git.infradead.org/linux-platform-drivers-x86
Pull x86 platform driver updates from Andy Shevchenko: - The driver for Silead touchscreen configurations has been renamed from silead_dmi to touchscreen_dmi since it starts supporting other touchscreens which require some DMI quirks It also gets expanded to cover cases for Chuwi Vi10, ONDA V891W, Connect Tablet 9, Onda V820w, and Cube KNote i1101 tablets. - Another bunch of changes is related to Mellanox platform code to allow user space to communicate with Mellanox for system control and monitoring purposes. The driver notifies user on hotplug device signal receiving. - ASUS WMI drivers recognize lid flip action on UX360, and correctly toggles airplane mode LED. In addition the keyboard backlight toggle gets support. - ThinkPad ACPI driver enables support for calculator key (on at least P52). It also has been fixed to support three characters model designators, which are used for modern laptops. Earlier the battery, marked as BAT1, on ThinkPad laptops has not been configured properly, which is fixed. On the opposite the multi-battery configurations now probed correctly. - Dell SMBIOS driver starts working on some Dell servers which do not support token interface. The regression with backlight detection has also been fixed. In order to support dock mode on some laptops, Intel virtual button driver has been fixed. The last but not least is the fix to Intel HID driver due to changes in Dell systems that prevented to use power button. * tag 'platform-drivers-x86-v4.19-1' of git://git.infradead.org/linux-platform-drivers-x86: (47 commits) platform/x86: acer-wmi: Silence "unsupported" message a bit platform/x86: intel_punit_ipc: fix build errors platform/x86: ideapad: Add Y520-15IKBM and Y720-15IKBM to no_hw_rfkill platform/x86: asus-nb-wmi: Add keymap entry for lid flip action on UX360 platform/x86: acer-wmi: refactor function has_cap platform/x86: thinkpad_acpi: Fix multi-battery bug platform/x86: thinkpad_acpi: extend battery quirk coverage platform/x86: touchscreen_dmi: Add info for the Cube KNote i1101 tablet platform/x86: mlx-platform: Fix copy-paste error in mlxplat_init() platform/x86: mlx-platform: Remove unused define platform/x86: mlx-platform: Change mlxreg-io configuration for MSN274x systems Documentation/ABI: Add new attribute for mlxreg-io sysfs interfaces platform/x86: mlx-platform: Allow mlxreg-io driver activation for more systems platform/x86: mlx-platform: Add ASIC hotplug device configuration platform/mellanox: mlxreg-hotplug: Add hotplug hwmon uevent notification platform/mellanox: mlxreg-hotplug: Improve mechanism of ASIC health discovery platform/x86: mlx-platform: Add mlxreg-fan platform driver activation platform/x86: dell-laptop: Fix backlight detection platform/x86: toshiba_acpi: Fix defined but not used build warnings platform/x86: thinkpad_acpi: Support battery quirk ...
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/
|
||||
asic_health
|
||||
|
||||
Date: June 2018
|
||||
KernelVersion: 4.19
|
||||
Contact: Vadim Pasternak <vadimpmellanox.com>
|
||||
Description: This file shows ASIC health status. The possible values are:
|
||||
0 - health failed, 2 - health OK, 3 - ASIC in booting state.
|
||||
|
||||
The files are read only.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/
|
||||
cpld1_version
|
||||
cpld2_version
|
||||
|
||||
Date: June 2018
|
||||
KernelVersion: 4.19
|
||||
Contact: Vadim Pasternak <vadimpmellanox.com>
|
||||
Description: These files show with which CPLD versions have been burned
|
||||
on carrier and switch boards.
|
||||
|
||||
The files are read only.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/select_iio
|
||||
Date: June 2018
|
||||
KernelVersion: 4.19
|
||||
Contact: Vadim Pasternak <vadimpmellanox.com>
|
||||
Description: This file allows iio devices selection.
|
||||
|
||||
Attribute select_iio can be written with 0 or with 1. It
|
||||
selects which one of iio devices can be accessed.
|
||||
|
||||
The file is read/write.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu1_on
|
||||
/sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu2_on
|
||||
/sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pwr_cycle
|
||||
/sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pwr_down
|
||||
Date: June 2018
|
||||
KernelVersion: 4.19
|
||||
Contact: Vadim Pasternak <vadimpmellanox.com>
|
||||
Description: These files allow asserting system power cycling, switching
|
||||
power supply units on and off and system's main power domain
|
||||
shutdown.
|
||||
Expected behavior:
|
||||
When pwr_cycle is written 1: auxiliary power domain will go
|
||||
down and after short period (about 1 second) up.
|
||||
When psu1_on or psu2_on is written 1, related unit will be
|
||||
disconnected from the power source, when written 0 - connected.
|
||||
If both are written 1 - power supplies main power domain will
|
||||
go down.
|
||||
When pwr_down is written 1, system's main power domain will go
|
||||
down.
|
||||
|
||||
The files are write only.
|
||||
|
||||
What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/
|
||||
reset_aux_pwr_or_ref
|
||||
reset_asic_thermal
|
||||
reset_hotswap_or_halt
|
||||
reset_hotswap_or_wd
|
||||
reset_fw_reset
|
||||
reset_long_pb
|
||||
reset_main_pwr_fail
|
||||
reset_short_pb
|
||||
reset_sw_reset
|
||||
Date: June 2018
|
||||
KernelVersion: 4.19
|
||||
Contact: Vadim Pasternak <vadimpmellanox.com>
|
||||
Description: These files show the system reset cause, as following: power
|
||||
auxiliary outage or power refresh, ASIC thermal shutdown, halt,
|
||||
hotswap, watchdog, firmware reset, long press power button,
|
||||
short press power button, software reset. Value 1 in file means
|
||||
this is reset cause, 0 - otherwise. Only one of the above
|
||||
causes could be 1 at the same time, representing only last
|
||||
reset cause.
|
||||
|
||||
The files are read only.
|
||||
+1
-1
@@ -13207,7 +13207,7 @@ L: linux-input@vger.kernel.org
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/input/touchscreen/silead.c
|
||||
F: drivers/platform/x86/silead_dmi.c
|
||||
F: drivers/platform/x86/touchscreen_dmi.c
|
||||
|
||||
SILICON MOTION SM712 FRAME BUFFER DRIVER
|
||||
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
|
||||
|
||||
@@ -23,4 +23,15 @@ config MLXREG_HOTPLUG
|
||||
This driver handles hot-plug events for the power suppliers, power
|
||||
cables and fans on the wide range Mellanox IB and Ethernet systems.
|
||||
|
||||
config MLXREG_IO
|
||||
tristate "Mellanox platform register access driver support"
|
||||
depends on REGMAP
|
||||
depends on HWMON
|
||||
help
|
||||
This driver allows access to Mellanox programmable device register
|
||||
space through sysfs interface. The sets of registers for sysfs access
|
||||
are defined per system type bases and include the registers related
|
||||
to system resets operation, system reset causes monitoring and some
|
||||
kinds of mux selection.
|
||||
|
||||
endif # MELLANOX_PLATFORM
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
# Mellanox Platform-Specific Drivers
|
||||
#
|
||||
obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o
|
||||
obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o
|
||||
|
||||
@@ -50,9 +50,8 @@
|
||||
#define MLXREG_HOTPLUG_MASK_OFF 2
|
||||
#define MLXREG_HOTPLUG_AGGR_MASK_OFF 1
|
||||
|
||||
/* ASIC health parameters. */
|
||||
#define MLXREG_HOTPLUG_HEALTH_MASK 0x02
|
||||
#define MLXREG_HOTPLUG_RST_CNTR 3
|
||||
/* ASIC good health mask. */
|
||||
#define MLXREG_HOTPLUG_GOOD_HEALTH_MASK 0x02
|
||||
|
||||
#define MLXREG_HOTPLUG_ATTRS_MAX 24
|
||||
#define MLXREG_HOTPLUG_NOT_ASSERT 3
|
||||
@@ -103,6 +102,9 @@ static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
|
||||
{
|
||||
struct mlxreg_core_hotplug_platform_data *pdata;
|
||||
|
||||
/* Notify user by sending hwmon uevent. */
|
||||
kobject_uevent(&priv->hwmon->kobj, KOBJ_CHANGE);
|
||||
|
||||
/*
|
||||
* Return if adapter number is negative. It could be in case hotplug
|
||||
* event is not associated with hotplug device.
|
||||
@@ -134,8 +136,13 @@ static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mlxreg_hotplug_device_destroy(struct mlxreg_core_data *data)
|
||||
static void
|
||||
mlxreg_hotplug_device_destroy(struct mlxreg_hotplug_priv_data *priv,
|
||||
struct mlxreg_core_data *data)
|
||||
{
|
||||
/* Notify user by sending hwmon uevent. */
|
||||
kobject_uevent(&priv->hwmon->kobj, KOBJ_CHANGE);
|
||||
|
||||
if (data->hpdev.client) {
|
||||
i2c_unregister_device(data->hpdev.client);
|
||||
data->hpdev.client = NULL;
|
||||
@@ -278,14 +285,14 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
|
||||
data = item->data + bit;
|
||||
if (regval & BIT(bit)) {
|
||||
if (item->inversed)
|
||||
mlxreg_hotplug_device_destroy(data);
|
||||
mlxreg_hotplug_device_destroy(priv, data);
|
||||
else
|
||||
mlxreg_hotplug_device_create(priv, data);
|
||||
} else {
|
||||
if (item->inversed)
|
||||
mlxreg_hotplug_device_create(priv, data);
|
||||
else
|
||||
mlxreg_hotplug_device_destroy(data);
|
||||
mlxreg_hotplug_device_destroy(priv, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,21 +332,40 @@ mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv,
|
||||
goto out;
|
||||
|
||||
regval &= data->mask;
|
||||
item->cache = regval;
|
||||
if (regval == MLXREG_HOTPLUG_HEALTH_MASK) {
|
||||
if ((data->health_cntr++ == MLXREG_HOTPLUG_RST_CNTR) ||
|
||||
!priv->after_probe) {
|
||||
|
||||
if (item->cache == regval)
|
||||
goto ack_event;
|
||||
|
||||
/*
|
||||
* ASIC health indication is provided through two bits. Bits
|
||||
* value 0x2 indicates that ASIC reached the good health, value
|
||||
* 0x0 indicates ASIC the bad health or dormant state and value
|
||||
* 0x3 indicates the booting state. During ASIC reset it should
|
||||
* pass the following states: dormant -> booting -> good.
|
||||
*/
|
||||
if (regval == MLXREG_HOTPLUG_GOOD_HEALTH_MASK) {
|
||||
if (!data->attached) {
|
||||
/*
|
||||
* ASIC is in steady state. Connect associated
|
||||
* device, if configured.
|
||||
*/
|
||||
mlxreg_hotplug_device_create(priv, data);
|
||||
data->attached = true;
|
||||
}
|
||||
} else {
|
||||
if (data->attached) {
|
||||
mlxreg_hotplug_device_destroy(data);
|
||||
/*
|
||||
* ASIC health is failed after ASIC has been
|
||||
* in steady state. Disconnect associated
|
||||
* device, if it has been connected.
|
||||
*/
|
||||
mlxreg_hotplug_device_destroy(priv, data);
|
||||
data->attached = false;
|
||||
data->health_cntr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
item->cache = regval;
|
||||
ack_event:
|
||||
/* Acknowledge event. */
|
||||
ret = regmap_write(priv->regmap, data->reg +
|
||||
MLXREG_HOTPLUG_EVENT_OFF, 0);
|
||||
@@ -551,7 +577,7 @@ static void mlxreg_hotplug_unset_irq(struct mlxreg_hotplug_priv_data *priv)
|
||||
/* Remove all the attached devices in group. */
|
||||
count = item->count;
|
||||
for (j = 0; j < count; j++, data++)
|
||||
mlxreg_hotplug_device_destroy(data);
|
||||
mlxreg_hotplug_device_destroy(priv, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,10 +642,6 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev)
|
||||
disable_irq(priv->irq);
|
||||
spin_lock_init(&priv->lock);
|
||||
INIT_DELAYED_WORK(&priv->dwork_irq, mlxreg_hotplug_work_handler);
|
||||
/* Perform initial interrupts setup. */
|
||||
mlxreg_hotplug_set_irq(priv);
|
||||
|
||||
priv->after_probe = true;
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
err = mlxreg_hotplug_attr_init(priv);
|
||||
@@ -637,6 +659,10 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(priv->hwmon);
|
||||
}
|
||||
|
||||
/* Perform initial interrupts setup. */
|
||||
mlxreg_hotplug_set_irq(priv);
|
||||
priv->after_probe = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Mellanox register access driver
|
||||
*
|
||||
* Copyright (C) 2018 Mellanox Technologies
|
||||
* Copyright (C) 2018 Vadim Pasternak <vadimp@mellanox.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_data/mlxreg.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* Attribute parameters. */
|
||||
#define MLXREG_IO_ATT_SIZE 10
|
||||
#define MLXREG_IO_ATT_NUM 48
|
||||
|
||||
/**
|
||||
* struct mlxreg_io_priv_data - driver's private data:
|
||||
*
|
||||
* @pdev: platform device;
|
||||
* @pdata: platform data;
|
||||
* @hwmon: hwmon device;
|
||||
* @mlxreg_io_attr: sysfs attributes array;
|
||||
* @mlxreg_io_dev_attr: sysfs sensor device attribute array;
|
||||
* @group: sysfs attribute group;
|
||||
* @groups: list of sysfs attribute group for hwmon registration;
|
||||
*/
|
||||
struct mlxreg_io_priv_data {
|
||||
struct platform_device *pdev;
|
||||
struct mlxreg_core_platform_data *pdata;
|
||||
struct device *hwmon;
|
||||
struct attribute *mlxreg_io_attr[MLXREG_IO_ATT_NUM + 1];
|
||||
struct sensor_device_attribute mlxreg_io_dev_attr[MLXREG_IO_ATT_NUM];
|
||||
struct attribute_group group;
|
||||
const struct attribute_group *groups[2];
|
||||
};
|
||||
|
||||
static int
|
||||
mlxreg_io_get_reg(void *regmap, struct mlxreg_core_data *data, u32 in_val,
|
||||
bool rw_flag, u32 *regval)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(regmap, data->reg, regval);
|
||||
if (ret)
|
||||
goto access_error;
|
||||
|
||||
/*
|
||||
* There are three kinds of attributes: single bit, full register's
|
||||
* bits and bit sequence. For the first kind field mask indicates which
|
||||
* bits are not related and field bit is set zero. For the second kind
|
||||
* field mask is set to zero and field bit is set with all bits one.
|
||||
* No special handling for such kind of attributes - pass value as is.
|
||||
* For the third kind, field mask indicates which bits are related and
|
||||
* field bit is set to the first bit number (from 1 to 32) is the bit
|
||||
* sequence.
|
||||
*/
|
||||
if (!data->bit) {
|
||||
/* Single bit. */
|
||||
if (rw_flag) {
|
||||
/* For show: expose effective bit value as 0 or 1. */
|
||||
*regval = !!(*regval & ~data->mask);
|
||||
} else {
|
||||
/* For store: set effective bit value. */
|
||||
*regval &= data->mask;
|
||||
if (in_val)
|
||||
*regval |= ~data->mask;
|
||||
}
|
||||
} else if (data->mask) {
|
||||
/* Bit sequence. */
|
||||
if (rw_flag) {
|
||||
/* For show: mask and shift right. */
|
||||
*regval = ror32(*regval & data->mask, (data->bit - 1));
|
||||
} else {
|
||||
/* For store: shift to the position and mask. */
|
||||
in_val = rol32(in_val, data->bit - 1) & data->mask;
|
||||
/* Clear relevant bits and set them to new value. */
|
||||
*regval = (*regval & ~data->mask) | in_val;
|
||||
}
|
||||
}
|
||||
|
||||
access_error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
mlxreg_io_attr_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct mlxreg_io_priv_data *priv = dev_get_drvdata(dev);
|
||||
int index = to_sensor_dev_attr(attr)->index;
|
||||
struct mlxreg_core_data *data = priv->pdata->data + index;
|
||||
u32 regval = 0;
|
||||
int ret;
|
||||
|
||||
ret = mlxreg_io_get_reg(priv->pdata->regmap, data, 0, true, ®val);
|
||||
if (ret)
|
||||
goto access_error;
|
||||
|
||||
return sprintf(buf, "%u\n", regval);
|
||||
|
||||
access_error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
mlxreg_io_attr_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct mlxreg_io_priv_data *priv = dev_get_drvdata(dev);
|
||||
int index = to_sensor_dev_attr(attr)->index;
|
||||
struct mlxreg_core_data *data = priv->pdata->data + index;
|
||||
u32 input_val, regval;
|
||||
int ret;
|
||||
|
||||
if (len > MLXREG_IO_ATT_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Convert buffer to input value. */
|
||||
ret = kstrtou32(buf, len, &input_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mlxreg_io_get_reg(priv->pdata->regmap, data, input_val, false,
|
||||
®val);
|
||||
if (ret)
|
||||
goto access_error;
|
||||
|
||||
ret = regmap_write(priv->pdata->regmap, data->reg, regval);
|
||||
if (ret)
|
||||
goto access_error;
|
||||
|
||||
return len;
|
||||
|
||||
access_error:
|
||||
dev_err(&priv->pdev->dev, "Bus access error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct device_attribute mlxreg_io_devattr_rw = {
|
||||
.show = mlxreg_io_attr_show,
|
||||
.store = mlxreg_io_attr_store,
|
||||
};
|
||||
|
||||
static int mlxreg_io_attr_init(struct mlxreg_io_priv_data *priv)
|
||||
{
|
||||
int i;
|
||||
|
||||
priv->group.attrs = devm_kcalloc(&priv->pdev->dev,
|
||||
priv->pdata->counter,
|
||||
sizeof(struct attribute *),
|
||||
GFP_KERNEL);
|
||||
if (!priv->group.attrs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < priv->pdata->counter; i++) {
|
||||
priv->mlxreg_io_attr[i] =
|
||||
&priv->mlxreg_io_dev_attr[i].dev_attr.attr;
|
||||
memcpy(&priv->mlxreg_io_dev_attr[i].dev_attr,
|
||||
&mlxreg_io_devattr_rw, sizeof(struct device_attribute));
|
||||
|
||||
/* Set attribute name as a label. */
|
||||
priv->mlxreg_io_attr[i]->name =
|
||||
devm_kasprintf(&priv->pdev->dev, GFP_KERNEL,
|
||||
priv->pdata->data[i].label);
|
||||
|
||||
if (!priv->mlxreg_io_attr[i]->name) {
|
||||
dev_err(&priv->pdev->dev, "Memory allocation failed for sysfs attribute %d.\n",
|
||||
i + 1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->mlxreg_io_dev_attr[i].dev_attr.attr.mode =
|
||||
priv->pdata->data[i].mode;
|
||||
priv->mlxreg_io_dev_attr[i].dev_attr.attr.name =
|
||||
priv->mlxreg_io_attr[i]->name;
|
||||
priv->mlxreg_io_dev_attr[i].index = i;
|
||||
sysfs_attr_init(&priv->mlxreg_io_dev_attr[i].dev_attr.attr);
|
||||
}
|
||||
|
||||
priv->group.attrs = priv->mlxreg_io_attr;
|
||||
priv->groups[0] = &priv->group;
|
||||
priv->groups[1] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlxreg_io_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mlxreg_io_priv_data *priv;
|
||||
int err;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!priv->pdata) {
|
||||
dev_err(&pdev->dev, "Failed to get platform data.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->pdev = pdev;
|
||||
|
||||
err = mlxreg_io_attr_init(priv);
|
||||
if (err) {
|
||||
dev_err(&priv->pdev->dev, "Failed to allocate attributes: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev,
|
||||
"mlxreg_io",
|
||||
priv,
|
||||
priv->groups);
|
||||
if (IS_ERR(priv->hwmon)) {
|
||||
dev_err(&pdev->dev, "Failed to register hwmon device %ld\n",
|
||||
PTR_ERR(priv->hwmon));
|
||||
return PTR_ERR(priv->hwmon);
|
||||
}
|
||||
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mlxreg_io_driver = {
|
||||
.driver = {
|
||||
.name = "mlxreg-io",
|
||||
},
|
||||
.probe = mlxreg_io_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(mlxreg_io_driver);
|
||||
|
||||
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
|
||||
MODULE_DESCRIPTION("Mellanox regmap I/O access driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:mlxreg-io");
|
||||
@@ -1196,16 +1196,16 @@ config INTEL_TURBO_MAX_3
|
||||
This driver is only required when the system is not using Hardware
|
||||
P-States (HWP). In HWP mode, priority can be read from ACPI tables.
|
||||
|
||||
config SILEAD_DMI
|
||||
bool "Tablets with Silead touchscreens"
|
||||
config TOUCHSCREEN_DMI
|
||||
bool "DMI based touchscreen configuration info"
|
||||
depends on ACPI && DMI && I2C=y && TOUCHSCREEN_SILEAD
|
||||
---help---
|
||||
Certain ACPI based tablets with Silead touchscreens do not have
|
||||
enough data in ACPI tables for the touchscreen driver to handle
|
||||
the touchscreen properly, as OEMs expected the data to be baked
|
||||
into the tablet model specific version of the driver shipped
|
||||
with the OS-image for the device. This option supplies the missing
|
||||
information. Enable this for x86 tablets with Silead touchscreens.
|
||||
Certain ACPI based tablets with e.g. Silead or Chipone touchscreens
|
||||
do not have enough data in ACPI tables for the touchscreen driver to
|
||||
handle the touchscreen properly, as OEMs expect the data to be baked
|
||||
into the tablet model specific version of the driver shipped with the
|
||||
the OS-image for the device. This option supplies the missing info.
|
||||
Enable this for x86 tablets with Silead or Chipone touchscreens.
|
||||
|
||||
config INTEL_CHTDC_TI_PWRBTN
|
||||
tristate "Intel Cherry Trail Dollar Cove TI power button driver"
|
||||
|
||||
@@ -78,7 +78,7 @@ obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
|
||||
obj-$(CONFIG_PVPANIC) += pvpanic.o
|
||||
obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
|
||||
obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o
|
||||
obj-$(CONFIG_SILEAD_DMI) += silead_dmi.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DMI) += touchscreen_dmi.o
|
||||
obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
|
||||
obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o
|
||||
obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
|
||||
|
||||
@@ -672,10 +672,7 @@ static void __init find_quirks(void)
|
||||
|
||||
static bool has_cap(u32 cap)
|
||||
{
|
||||
if ((interface->capability & cap) != 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
return interface->capability & cap;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2216,7 +2213,7 @@ static int __init acer_wmi_init(void)
|
||||
if (wmi_has_guid(AMW0_GUID1) &&
|
||||
!dmi_check_system(amw0_whitelist) &&
|
||||
quirks == &quirk_unknown) {
|
||||
pr_err("Unsupported machine has AMW0_GUID1, unable to load\n");
|
||||
pr_debug("Unsupported machine has AMW0_GUID1, unable to load\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
@@ -496,6 +496,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
|
||||
{ KE_KEY, 0xC4, { KEY_KBDILLUMUP } },
|
||||
{ KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },
|
||||
{ KE_IGNORE, 0xC6, }, /* Ambient Light Sensor notification */
|
||||
{ KE_KEY, 0xFA, { KEY_PROG2 } }, /* Lid flip action */
|
||||
{ KE_END, 0},
|
||||
};
|
||||
|
||||
|
||||
@@ -52,13 +52,12 @@ static const struct acpi_device_id device_ids[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, device_ids);
|
||||
|
||||
static u64 asus_wireless_method(acpi_handle handle, const char *method,
|
||||
int param)
|
||||
static acpi_status asus_wireless_method(acpi_handle handle, const char *method,
|
||||
int param, u64 *ret)
|
||||
{
|
||||
struct acpi_object_list p;
|
||||
union acpi_object obj;
|
||||
acpi_status s;
|
||||
u64 ret;
|
||||
|
||||
acpi_handle_debug(handle, "Evaluating method %s, parameter %#x\n",
|
||||
method, param);
|
||||
@@ -67,24 +66,27 @@ static u64 asus_wireless_method(acpi_handle handle, const char *method,
|
||||
p.count = 1;
|
||||
p.pointer = &obj;
|
||||
|
||||
s = acpi_evaluate_integer(handle, (acpi_string) method, &p, &ret);
|
||||
s = acpi_evaluate_integer(handle, (acpi_string) method, &p, ret);
|
||||
if (ACPI_FAILURE(s))
|
||||
acpi_handle_err(handle,
|
||||
"Failed to eval method %s, param %#x (%d)\n",
|
||||
method, param, s);
|
||||
acpi_handle_debug(handle, "%s returned %#llx\n", method, ret);
|
||||
return ret;
|
||||
else
|
||||
acpi_handle_debug(handle, "%s returned %#llx\n", method, *ret);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static enum led_brightness led_state_get(struct led_classdev *led)
|
||||
{
|
||||
struct asus_wireless_data *data;
|
||||
int s;
|
||||
acpi_status s;
|
||||
u64 ret;
|
||||
|
||||
data = container_of(led, struct asus_wireless_data, led);
|
||||
s = asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
|
||||
data->hswc_params->status);
|
||||
if (s == data->hswc_params->on)
|
||||
data->hswc_params->status, &ret);
|
||||
if (ACPI_SUCCESS(s) && ret == data->hswc_params->on)
|
||||
return LED_FULL;
|
||||
return LED_OFF;
|
||||
}
|
||||
@@ -92,10 +94,11 @@ static enum led_brightness led_state_get(struct led_classdev *led)
|
||||
static void led_state_update(struct work_struct *work)
|
||||
{
|
||||
struct asus_wireless_data *data;
|
||||
u64 ret;
|
||||
|
||||
data = container_of(work, struct asus_wireless_data, led_work);
|
||||
asus_wireless_method(acpi_device_handle(data->adev), "HSWC",
|
||||
data->led_state);
|
||||
data->led_state, &ret);
|
||||
}
|
||||
|
||||
static void led_state_set(struct led_classdev *led, enum led_brightness value)
|
||||
@@ -167,6 +170,7 @@ static int asus_wireless_add(struct acpi_device *adev)
|
||||
data->led.brightness_get = led_state_get;
|
||||
data->led.flags = LED_CORE_SUSPENDRESUME;
|
||||
data->led.max_brightness = 1;
|
||||
data->led.default_trigger = "rfkill-none";
|
||||
err = devm_led_classdev_register(&adev->dev, &data->led);
|
||||
if (err)
|
||||
destroy_workqueue(data->wq);
|
||||
|
||||
@@ -67,6 +67,7 @@ MODULE_LICENSE("GPL");
|
||||
#define NOTIFY_BRNDOWN_MAX 0x2e
|
||||
#define NOTIFY_KBD_BRTUP 0xc4
|
||||
#define NOTIFY_KBD_BRTDWN 0xc5
|
||||
#define NOTIFY_KBD_BRTTOGGLE 0xc7
|
||||
|
||||
/* WMI Methods */
|
||||
#define ASUS_WMI_METHODID_SPEC 0x43455053 /* BIOS SPECification */
|
||||
@@ -470,6 +471,7 @@ static void kbd_led_update(struct work_struct *work)
|
||||
ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F);
|
||||
|
||||
asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL);
|
||||
led_classdev_notify_brightness_hw_changed(&asus->kbd_led, asus->kbd_led_wk);
|
||||
}
|
||||
|
||||
static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
|
||||
@@ -500,15 +502,16 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void kbd_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
static void do_kbd_led_set(struct led_classdev *led_cdev, int value)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
int max_level;
|
||||
|
||||
asus = container_of(led_cdev, struct asus_wmi, kbd_led);
|
||||
max_level = asus->kbd_led.max_brightness;
|
||||
|
||||
if (value > asus->kbd_led.max_brightness)
|
||||
value = asus->kbd_led.max_brightness;
|
||||
if (value > max_level)
|
||||
value = max_level;
|
||||
else if (value < 0)
|
||||
value = 0;
|
||||
|
||||
@@ -516,6 +519,12 @@ static void kbd_led_set(struct led_classdev *led_cdev,
|
||||
queue_work(asus->led_workqueue, &asus->kbd_led_work);
|
||||
}
|
||||
|
||||
static void kbd_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
do_kbd_led_set(led_cdev, value);
|
||||
}
|
||||
|
||||
static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct asus_wmi *asus;
|
||||
@@ -666,6 +675,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
|
||||
|
||||
asus->kbd_led_wk = led_val;
|
||||
asus->kbd_led.name = "asus::kbd_backlight";
|
||||
asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED;
|
||||
asus->kbd_led.brightness_set = kbd_led_set;
|
||||
asus->kbd_led.brightness_get = kbd_led_get;
|
||||
asus->kbd_led.max_brightness = 3;
|
||||
@@ -1754,6 +1764,22 @@ static void asus_wmi_notify(u32 value, void *context)
|
||||
}
|
||||
}
|
||||
|
||||
if (code == NOTIFY_KBD_BRTUP) {
|
||||
do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk + 1);
|
||||
goto exit;
|
||||
}
|
||||
if (code == NOTIFY_KBD_BRTDWN) {
|
||||
do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk - 1);
|
||||
goto exit;
|
||||
}
|
||||
if (code == NOTIFY_KBD_BRTTOGGLE) {
|
||||
if (asus->kbd_led_wk == asus->kbd_led.max_brightness)
|
||||
do_kbd_led_set(&asus->kbd_led, 0);
|
||||
else
|
||||
do_kbd_led_set(&asus->kbd_led, asus->kbd_led_wk + 1);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (is_display_toggle(code) &&
|
||||
asus->driver->quirks->no_display_toggle)
|
||||
goto exit;
|
||||
|
||||
@@ -212,6 +212,12 @@ int dell_smbios_call_filter(struct device *d,
|
||||
if ((buffer->cmd_class == CLASS_TOKEN_READ ||
|
||||
buffer->cmd_class == CLASS_TOKEN_WRITE) &&
|
||||
buffer->cmd_select < 3) {
|
||||
/* tokens enabled ? */
|
||||
if (!da_tokens) {
|
||||
dev_dbg(d, "no token support on this system\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* find the matching token ID */
|
||||
for (i = 0; i < da_num_tokens; i++) {
|
||||
if (da_tokens[i].location != buffer->input[0])
|
||||
@@ -315,6 +321,9 @@ struct calling_interface_token *dell_smbios_find_token(int tokenid)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!da_tokens)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < da_num_tokens; i++) {
|
||||
if (da_tokens[i].tokenID == tokenid)
|
||||
return &da_tokens[i];
|
||||
@@ -565,11 +574,6 @@ static int __init dell_smbios_init(void)
|
||||
|
||||
dmi_walk(find_tokens, NULL);
|
||||
|
||||
if (!da_tokens) {
|
||||
pr_info("Unable to find dmi tokens\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = platform_driver_register(&platform_driver);
|
||||
if (ret)
|
||||
goto fail_platform_driver;
|
||||
@@ -583,13 +587,6 @@ static int __init dell_smbios_init(void)
|
||||
if (ret)
|
||||
goto fail_platform_device_add;
|
||||
|
||||
/* duplicate tokens will cause problems building sysfs files */
|
||||
zero_duplicates(&platform_device->dev);
|
||||
|
||||
ret = build_tokens_sysfs(platform_device);
|
||||
if (ret)
|
||||
goto fail_create_group;
|
||||
|
||||
/* register backends */
|
||||
wmi = init_dell_smbios_wmi();
|
||||
if (wmi)
|
||||
@@ -600,7 +597,16 @@ static int __init dell_smbios_init(void)
|
||||
if (wmi && smm) {
|
||||
pr_err("No SMBIOS backends available (wmi: %d, smm: %d)\n",
|
||||
wmi, smm);
|
||||
goto fail_sysfs;
|
||||
goto fail_create_group;
|
||||
}
|
||||
|
||||
if (da_tokens) {
|
||||
/* duplicate tokens will cause problems building sysfs files */
|
||||
zero_duplicates(&platform_device->dev);
|
||||
|
||||
ret = build_tokens_sysfs(platform_device);
|
||||
if (ret)
|
||||
goto fail_sysfs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -628,7 +634,8 @@ static void __exit dell_smbios_exit(void)
|
||||
exit_dell_smbios_smm();
|
||||
mutex_lock(&smbios_mutex);
|
||||
if (platform_device) {
|
||||
free_group(platform_device);
|
||||
if (da_tokens)
|
||||
free_group(platform_device);
|
||||
platform_device_unregister(platform_device);
|
||||
platform_driver_unregister(&platform_driver);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
static int da_command_address;
|
||||
static int da_command_code;
|
||||
static struct calling_interface_buffer *buffer;
|
||||
struct platform_device *platform_device;
|
||||
static struct platform_device *platform_device;
|
||||
static DEFINE_MUTEX(smm_mutex);
|
||||
|
||||
static const struct dmi_system_id dell_device_table[] __initconst = {
|
||||
@@ -82,7 +82,7 @@ static void find_cmd_address(const struct dmi_header *dm, void *dummy)
|
||||
}
|
||||
}
|
||||
|
||||
int dell_smbios_smm_call(struct calling_interface_buffer *input)
|
||||
static int dell_smbios_smm_call(struct calling_interface_buffer *input)
|
||||
{
|
||||
struct smi_cmd command;
|
||||
size_t size;
|
||||
|
||||
@@ -82,7 +82,7 @@ static int run_smbios_call(struct wmi_device *wdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dell_smbios_wmi_call(struct calling_interface_buffer *buffer)
|
||||
static int dell_smbios_wmi_call(struct calling_interface_buffer *buffer)
|
||||
{
|
||||
struct wmi_smbios_priv *priv;
|
||||
size_t difference;
|
||||
|
||||
@@ -1133,10 +1133,17 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lenovo Legion Y520-15IKBN",
|
||||
.ident = "Lenovo Legion Y520-15IKB",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y520-15IKBN"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y520-15IKB"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lenovo Y520-15IKBM",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y520-15IKBM"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1153,6 +1160,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y720-15IKBN"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lenovo Y720-15IKBM",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y720-15IKBM"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lenovo Yoga 2 11 / 13 / Pro",
|
||||
.matches = {
|
||||
|
||||
@@ -96,13 +96,140 @@ struct intel_hid_priv {
|
||||
bool wakeup_mode;
|
||||
};
|
||||
|
||||
#define HID_EVENT_FILTER_UUID "eeec56b3-4442-408f-a792-4edd4d758054"
|
||||
|
||||
enum intel_hid_dsm_fn_codes {
|
||||
INTEL_HID_DSM_FN_INVALID,
|
||||
INTEL_HID_DSM_BTNL_FN,
|
||||
INTEL_HID_DSM_HDMM_FN,
|
||||
INTEL_HID_DSM_HDSM_FN,
|
||||
INTEL_HID_DSM_HDEM_FN,
|
||||
INTEL_HID_DSM_BTNS_FN,
|
||||
INTEL_HID_DSM_BTNE_FN,
|
||||
INTEL_HID_DSM_HEBC_V1_FN,
|
||||
INTEL_HID_DSM_VGBS_FN,
|
||||
INTEL_HID_DSM_HEBC_V2_FN,
|
||||
INTEL_HID_DSM_FN_MAX
|
||||
};
|
||||
|
||||
static const char *intel_hid_dsm_fn_to_method[INTEL_HID_DSM_FN_MAX] = {
|
||||
NULL,
|
||||
"BTNL",
|
||||
"HDMM",
|
||||
"HDSM",
|
||||
"HDEM",
|
||||
"BTNS",
|
||||
"BTNE",
|
||||
"HEBC",
|
||||
"VGBS",
|
||||
"HEBC"
|
||||
};
|
||||
|
||||
static unsigned long long intel_hid_dsm_fn_mask;
|
||||
static guid_t intel_dsm_guid;
|
||||
|
||||
static bool intel_hid_execute_method(acpi_handle handle,
|
||||
enum intel_hid_dsm_fn_codes fn_index,
|
||||
unsigned long long arg)
|
||||
{
|
||||
union acpi_object *obj, argv4, req;
|
||||
acpi_status status;
|
||||
char *method_name;
|
||||
|
||||
if (fn_index <= INTEL_HID_DSM_FN_INVALID ||
|
||||
fn_index >= INTEL_HID_DSM_FN_MAX)
|
||||
return false;
|
||||
|
||||
method_name = (char *)intel_hid_dsm_fn_to_method[fn_index];
|
||||
|
||||
if (!(intel_hid_dsm_fn_mask & fn_index))
|
||||
goto skip_dsm_exec;
|
||||
|
||||
/* All methods expects a package with one integer element */
|
||||
req.type = ACPI_TYPE_INTEGER;
|
||||
req.integer.value = arg;
|
||||
|
||||
argv4.type = ACPI_TYPE_PACKAGE;
|
||||
argv4.package.count = 1;
|
||||
argv4.package.elements = &req;
|
||||
|
||||
obj = acpi_evaluate_dsm(handle, &intel_dsm_guid, 1, fn_index, &argv4);
|
||||
if (obj) {
|
||||
acpi_handle_debug(handle, "Exec DSM Fn code: %d[%s] success\n",
|
||||
fn_index, method_name);
|
||||
ACPI_FREE(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
skip_dsm_exec:
|
||||
status = acpi_execute_simple_method(handle, method_name, arg);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool intel_hid_evaluate_method(acpi_handle handle,
|
||||
enum intel_hid_dsm_fn_codes fn_index,
|
||||
unsigned long long *result)
|
||||
{
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
char *method_name;
|
||||
|
||||
if (fn_index <= INTEL_HID_DSM_FN_INVALID ||
|
||||
fn_index >= INTEL_HID_DSM_FN_MAX)
|
||||
return false;
|
||||
|
||||
method_name = (char *)intel_hid_dsm_fn_to_method[fn_index];
|
||||
|
||||
if (!(intel_hid_dsm_fn_mask & fn_index))
|
||||
goto skip_dsm_eval;
|
||||
|
||||
obj = acpi_evaluate_dsm_typed(handle, &intel_dsm_guid,
|
||||
1, fn_index,
|
||||
NULL, ACPI_TYPE_INTEGER);
|
||||
if (obj) {
|
||||
*result = obj->integer.value;
|
||||
acpi_handle_debug(handle,
|
||||
"Eval DSM Fn code: %d[%s] results: 0x%llx\n",
|
||||
fn_index, method_name, *result);
|
||||
ACPI_FREE(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
skip_dsm_eval:
|
||||
status = acpi_evaluate_integer(handle, method_name, NULL, result);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void intel_hid_init_dsm(acpi_handle handle)
|
||||
{
|
||||
union acpi_object *obj;
|
||||
|
||||
guid_parse(HID_EVENT_FILTER_UUID, &intel_dsm_guid);
|
||||
|
||||
obj = acpi_evaluate_dsm_typed(handle, &intel_dsm_guid, 1, 0, NULL,
|
||||
ACPI_TYPE_BUFFER);
|
||||
if (obj) {
|
||||
intel_hid_dsm_fn_mask = *obj->buffer.pointer;
|
||||
ACPI_FREE(obj);
|
||||
}
|
||||
|
||||
acpi_handle_debug(handle, "intel_hid_dsm_fn_mask = %llx\n",
|
||||
intel_hid_dsm_fn_mask);
|
||||
}
|
||||
|
||||
static int intel_hid_set_enable(struct device *device, bool enable)
|
||||
{
|
||||
acpi_status status;
|
||||
acpi_handle handle = ACPI_HANDLE(device);
|
||||
|
||||
status = acpi_execute_simple_method(ACPI_HANDLE(device), "HDSM",
|
||||
enable);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
/* Enable|disable features - power button is always enabled */
|
||||
if (!intel_hid_execute_method(handle, INTEL_HID_DSM_HDSM_FN,
|
||||
enable)) {
|
||||
dev_warn(device, "failed to %sable hotkeys\n",
|
||||
enable ? "en" : "dis");
|
||||
return -EIO;
|
||||
@@ -129,9 +256,8 @@ static void intel_button_array_enable(struct device *device, bool enable)
|
||||
}
|
||||
|
||||
/* Enable|disable features - power button is always enabled */
|
||||
status = acpi_execute_simple_method(handle, "BTNE",
|
||||
enable ? button_cap : 1);
|
||||
if (ACPI_FAILURE(status))
|
||||
if (!intel_hid_execute_method(handle, INTEL_HID_DSM_BTNE_FN,
|
||||
enable ? button_cap : 1))
|
||||
dev_warn(device, "failed to set button capability\n");
|
||||
}
|
||||
|
||||
@@ -217,7 +343,6 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
|
||||
struct platform_device *device = context;
|
||||
struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
|
||||
unsigned long long ev_index;
|
||||
acpi_status status;
|
||||
|
||||
if (priv->wakeup_mode) {
|
||||
/*
|
||||
@@ -269,8 +394,8 @@ wakeup:
|
||||
return;
|
||||
}
|
||||
|
||||
status = acpi_evaluate_integer(handle, "HDEM", NULL, &ev_index);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDEM_FN,
|
||||
&ev_index)) {
|
||||
dev_warn(&device->dev, "failed to get event index\n");
|
||||
return;
|
||||
}
|
||||
@@ -284,17 +409,24 @@ static bool button_array_present(struct platform_device *device)
|
||||
{
|
||||
acpi_handle handle = ACPI_HANDLE(&device->dev);
|
||||
unsigned long long event_cap;
|
||||
acpi_status status;
|
||||
bool supported = false;
|
||||
|
||||
status = acpi_evaluate_integer(handle, "HEBC", NULL, &event_cap);
|
||||
if (ACPI_SUCCESS(status) && (event_cap & 0x20000))
|
||||
supported = true;
|
||||
if (intel_hid_evaluate_method(handle, INTEL_HID_DSM_HEBC_V2_FN,
|
||||
&event_cap)) {
|
||||
/* Check presence of 5 button array or v2 power button */
|
||||
if (event_cap & 0x60000)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (intel_hid_evaluate_method(handle, INTEL_HID_DSM_HEBC_V1_FN,
|
||||
&event_cap)) {
|
||||
if (event_cap & 0x20000)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dmi_check_system(button_array_table))
|
||||
supported = true;
|
||||
return true;
|
||||
|
||||
return supported;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int intel_hid_probe(struct platform_device *device)
|
||||
@@ -305,8 +437,9 @@ static int intel_hid_probe(struct platform_device *device)
|
||||
acpi_status status;
|
||||
int err;
|
||||
|
||||
status = acpi_evaluate_integer(handle, "HDMM", NULL, &mode);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
intel_hid_init_dsm(handle);
|
||||
|
||||
if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) {
|
||||
dev_warn(&device->dev, "failed to read mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
@@ -352,13 +485,16 @@ static int intel_hid_probe(struct platform_device *device)
|
||||
goto err_remove_notify;
|
||||
|
||||
if (priv->array) {
|
||||
unsigned long long dummy;
|
||||
|
||||
intel_button_array_enable(&device->dev, true);
|
||||
|
||||
/* Call button load method to enable HID power button */
|
||||
status = acpi_evaluate_object(handle, "BTNL", NULL, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_BTNL_FN,
|
||||
&dummy)) {
|
||||
dev_warn(&device->dev,
|
||||
"failed to enable HID power button\n");
|
||||
}
|
||||
}
|
||||
|
||||
device_init_wakeup(&device->dev, true);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
/* When NOT in tablet mode, VGBS returns with the flag 0x40 */
|
||||
#define TABLET_MODE_FLAG 0x40
|
||||
#define DOCK_MODE_FLAG 0x80
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("AceLan Kao");
|
||||
@@ -38,6 +39,8 @@ static const struct key_entry intel_vbtn_keymap[] = {
|
||||
{ KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* volume-down key release */
|
||||
{ KE_KEY, 0xC8, { KEY_ROTATE_LOCK_TOGGLE } }, /* rotate-lock key press */
|
||||
{ KE_KEY, 0xC9, { KEY_ROTATE_LOCK_TOGGLE } }, /* rotate-lock key release */
|
||||
{ KE_SW, 0xCA, { .sw = { SW_DOCK, 1 } } }, /* Docked */
|
||||
{ KE_SW, 0xCB, { .sw = { SW_DOCK, 0 } } }, /* Undocked */
|
||||
{ KE_SW, 0xCC, { .sw = { SW_TABLET_MODE, 1 } } }, /* Tablet */
|
||||
{ KE_SW, 0xCD, { .sw = { SW_TABLET_MODE, 0 } } }, /* Laptop */
|
||||
{ KE_END },
|
||||
@@ -121,6 +124,8 @@ static void detect_tablet_mode(struct platform_device *device)
|
||||
|
||||
m = !(obj->integer.value & TABLET_MODE_FLAG);
|
||||
input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
|
||||
m = (obj->integer.value & DOCK_MODE_FLAG) ? 1 : 0;
|
||||
input_report_switch(priv->input_dev, SW_DOCK, m);
|
||||
out:
|
||||
kfree(vgbs_output.pointer);
|
||||
}
|
||||
|
||||
@@ -858,10 +858,7 @@ static u16 read_mgtv(struct ips_driver *ips)
|
||||
|
||||
static u16 read_ptv(struct ips_driver *ips)
|
||||
{
|
||||
u16 val, slope, offset;
|
||||
|
||||
slope = (ips->pta_val & PTA_SLOPE_MASK) >> PTA_SLOPE_SHIFT;
|
||||
offset = ips->pta_val & PTA_OFFSET_MASK;
|
||||
u16 val;
|
||||
|
||||
val = thm_readw(THM_PTV) & PTV_MASK;
|
||||
|
||||
|
||||
@@ -196,9 +196,67 @@ static const struct pmc_bit_map cnp_pfear_map[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct pmc_bit_map cnp_slps0_dbg0_map[] = {
|
||||
{"AUDIO_D3", BIT(0)},
|
||||
{"OTG_D3", BIT(1)},
|
||||
{"XHCI_D3", BIT(2)},
|
||||
{"LPIO_D3", BIT(3)},
|
||||
{"SDX_D3", BIT(4)},
|
||||
{"SATA_D3", BIT(5)},
|
||||
{"UFS0_D3", BIT(6)},
|
||||
{"UFS1_D3", BIT(7)},
|
||||
{"EMMC_D3", BIT(8)},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct pmc_bit_map cnp_slps0_dbg1_map[] = {
|
||||
{"SDIO_PLL_OFF", BIT(0)},
|
||||
{"USB2_PLL_OFF", BIT(1)},
|
||||
{"AUDIO_PLL_OFF", BIT(2)},
|
||||
{"OC_PLL_OFF", BIT(3)},
|
||||
{"MAIN_PLL_OFF", BIT(4)},
|
||||
{"XOSC_OFF", BIT(5)},
|
||||
{"LPC_CLKS_GATED", BIT(6)},
|
||||
{"PCIE_CLKREQS_IDLE", BIT(7)},
|
||||
{"AUDIO_ROSC_OFF", BIT(8)},
|
||||
{"HPET_XOSC_CLK_REQ", BIT(9)},
|
||||
{"PMC_ROSC_SLOW_CLK", BIT(10)},
|
||||
{"AON2_ROSC_GATED", BIT(11)},
|
||||
{"CLKACKS_DEASSERTED", BIT(12)},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct pmc_bit_map cnp_slps0_dbg2_map[] = {
|
||||
{"MPHY_CORE_GATED", BIT(0)},
|
||||
{"CSME_GATED", BIT(1)},
|
||||
{"USB2_SUS_GATED", BIT(2)},
|
||||
{"DYN_FLEX_IO_IDLE", BIT(3)},
|
||||
{"GBE_NO_LINK", BIT(4)},
|
||||
{"THERM_SEN_DISABLED", BIT(5)},
|
||||
{"PCIE_LOW_POWER", BIT(6)},
|
||||
{"ISH_VNNAON_REQ_ACT", BIT(7)},
|
||||
{"ISH_VNN_REQ_ACT", BIT(8)},
|
||||
{"CNV_VNNAON_REQ_ACT", BIT(9)},
|
||||
{"CNV_VNN_REQ_ACT", BIT(10)},
|
||||
{"NPK_VNNON_REQ_ACT", BIT(11)},
|
||||
{"PMSYNC_STATE_IDLE", BIT(12)},
|
||||
{"ALST_GT_THRES", BIT(13)},
|
||||
{"PMC_ARC_PG_READY", BIT(14)},
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct pmc_bit_map *cnp_slps0_dbg_maps[] = {
|
||||
cnp_slps0_dbg0_map,
|
||||
cnp_slps0_dbg1_map,
|
||||
cnp_slps0_dbg2_map,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct pmc_reg_map cnp_reg_map = {
|
||||
.pfear_sts = cnp_pfear_map,
|
||||
.slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET,
|
||||
.slps0_dbg_maps = cnp_slps0_dbg_maps,
|
||||
.slps0_dbg_offset = CNP_PMC_SLPS0_DBG_OFFSET,
|
||||
.ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET,
|
||||
.regmap_length = CNP_PMC_MMIO_REG_LEN,
|
||||
.ppfear0_offset = CNP_PMC_HOST_PPFEAR0A,
|
||||
@@ -252,6 +310,8 @@ static int pmc_core_check_read_lock_bit(void)
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DEBUG_FS)
|
||||
static bool slps0_dbg_latch;
|
||||
|
||||
static void pmc_core_display_map(struct seq_file *s, int index,
|
||||
u8 pf_reg, const struct pmc_bit_map *pf_map)
|
||||
{
|
||||
@@ -481,6 +541,57 @@ static const struct file_operations pmc_core_ltr_ignore_ops = {
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void pmc_core_slps0_dbg_latch(struct pmc_dev *pmcdev, bool reset)
|
||||
{
|
||||
const struct pmc_reg_map *map = pmcdev->map;
|
||||
u32 fd;
|
||||
|
||||
mutex_lock(&pmcdev->lock);
|
||||
|
||||
if (!reset && !slps0_dbg_latch)
|
||||
goto out_unlock;
|
||||
|
||||
fd = pmc_core_reg_read(pmcdev, map->slps0_dbg_offset);
|
||||
if (reset)
|
||||
fd &= ~CNP_PMC_LATCH_SLPS0_EVENTS;
|
||||
else
|
||||
fd |= CNP_PMC_LATCH_SLPS0_EVENTS;
|
||||
pmc_core_reg_write(pmcdev, map->slps0_dbg_offset, fd);
|
||||
|
||||
slps0_dbg_latch = 0;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&pmcdev->lock);
|
||||
}
|
||||
|
||||
static int pmc_core_slps0_dbg_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct pmc_dev *pmcdev = s->private;
|
||||
const struct pmc_bit_map **maps = pmcdev->map->slps0_dbg_maps;
|
||||
const struct pmc_bit_map *map;
|
||||
int offset;
|
||||
u32 data;
|
||||
|
||||
pmc_core_slps0_dbg_latch(pmcdev, false);
|
||||
offset = pmcdev->map->slps0_dbg_offset;
|
||||
while (*maps) {
|
||||
map = *maps;
|
||||
data = pmc_core_reg_read(pmcdev, offset);
|
||||
offset += 4;
|
||||
while (map->name) {
|
||||
seq_printf(s, "SLP_S0_DBG: %-32s\tState: %s\n",
|
||||
map->name,
|
||||
data & map->bit_mask ?
|
||||
"Yes" : "No");
|
||||
++map;
|
||||
}
|
||||
++maps;
|
||||
}
|
||||
pmc_core_slps0_dbg_latch(pmcdev, true);
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(pmc_core_slps0_dbg);
|
||||
|
||||
static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
|
||||
{
|
||||
debugfs_remove_recursive(pmcdev->dbgfs_dir);
|
||||
@@ -514,6 +625,15 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
|
||||
0444, dir, pmcdev,
|
||||
&pmc_core_mphy_pg_ops);
|
||||
|
||||
if (pmcdev->map->slps0_dbg_maps) {
|
||||
debugfs_create_file("slp_s0_debug_status", 0444,
|
||||
dir, pmcdev,
|
||||
&pmc_core_slps0_dbg_fops);
|
||||
|
||||
debugfs_create_bool("slp_s0_dbg_latch", 0644,
|
||||
dir, &slps0_dbg_latch);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user