summaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2017-11-13 17:10:13 -0800
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2017-11-13 17:10:13 -0800
commitc25141062a82ae8bddced1b3ce2b57a1c0efabe0 (patch)
tree105edf10059bc0c4f2f00338b0c861b813d1bb1a /drivers/platform
parent26dd633e437dca218547ccbeacc71fe8a620b6f6 (diff)
parentc1b433e04ef9c0a1c4d65bfe918472ffa334dff4 (diff)
Merge branch 'next' into for-linus
Prepare input updates for 4.15 merge window.
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/Makefile1
-rw-r--r--drivers/platform/chrome/Kconfig14
-rw-r--r--drivers/platform/chrome/Makefile8
-rw-r--r--drivers/platform/chrome/chromeos_laptop.c2
-rw-r--r--drivers/platform/chrome/chromeos_pstore.c2
-rw-r--r--drivers/platform/chrome/cros_ec_debugfs.c401
-rw-r--r--drivers/platform/chrome/cros_ec_debugfs.h27
-rw-r--r--drivers/platform/chrome/cros_ec_dev.c40
-rw-r--r--drivers/platform/chrome/cros_ec_dev.h6
-rw-r--r--drivers/platform/chrome/cros_ec_lightbar.c197
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c170
-rw-r--r--drivers/platform/chrome/cros_ec_lpc_mec.c140
-rw-r--r--drivers/platform/chrome/cros_ec_lpc_reg.c133
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c116
-rw-r--r--drivers/platform/goldfish/goldfish_pipe.c2
-rw-r--r--drivers/platform/mips/cpu_hwmon.c136
-rw-r--r--drivers/platform/x86/Kconfig49
-rw-r--r--drivers/platform/x86/Makefile4
-rw-r--r--drivers/platform/x86/acer-wmi.c15
-rw-r--r--drivers/platform/x86/acerhdf.c2
-rw-r--r--drivers/platform/x86/alienware-wmi.c54
-rw-r--r--drivers/platform/x86/asus-wmi.c8
-rw-r--r--drivers/platform/x86/compal-laptop.c4
-rw-r--r--drivers/platform/x86/dell-laptop.c6
-rw-r--r--drivers/platform/x86/dell-rbtn.c31
-rw-r--r--drivers/platform/x86/dell-wmi-led.c2
-rw-r--r--drivers/platform/x86/dell-wmi.c205
-rw-r--r--drivers/platform/x86/eeepc-laptop.c2
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c560
-rw-r--r--drivers/platform/x86/hdaps.c2
-rw-r--r--drivers/platform/x86/hp-wmi.c30
-rw-r--r--drivers/platform/x86/ibm_rtl.c4
-rw-r--r--drivers/platform/x86/ideapad-laptop.c212
-rw-r--r--drivers/platform/x86/intel-hid.c55
-rw-r--r--drivers/platform/x86/intel-vbtn.c43
-rw-r--r--drivers/platform/x86/intel_bxtwc_tmu.c4
-rw-r--r--drivers/platform/x86/intel_cht_int33fe.c8
-rw-r--r--drivers/platform/x86/intel_int0002_vgpio.c219
-rw-r--r--drivers/platform/x86/intel_menlow.c2
-rw-r--r--drivers/platform/x86/intel_mid_powerbtn.c10
-rw-r--r--drivers/platform/x86/intel_oaktrail.c2
-rw-r--r--drivers/platform/x86/intel_pmc_core.c31
-rw-r--r--drivers/platform/x86/intel_pmc_core.h30
-rw-r--r--drivers/platform/x86/intel_pmc_ipc.c117
-rw-r--r--drivers/platform/x86/intel_scu_ipc.c6
-rw-r--r--drivers/platform/x86/intel_telemetry_debugfs.c34
-rw-r--r--drivers/platform/x86/intel_telemetry_pltdrv.c36
-rw-r--r--drivers/platform/x86/mlx-platform.c2
-rw-r--r--drivers/platform/x86/msi-laptop.c6
-rw-r--r--drivers/platform/x86/msi-wmi.c2
-rw-r--r--drivers/platform/x86/mxm-wmi.c4
-rw-r--r--drivers/platform/x86/panasonic-laptop.c6
-rw-r--r--drivers/platform/x86/peaq-wmi.c100
-rw-r--r--drivers/platform/x86/samsung-laptop.c10
-rw-r--r--drivers/platform/x86/samsung-q10.c2
-rw-r--r--drivers/platform/x86/silead_dmi.c102
-rw-r--r--drivers/platform/x86/sony-laptop.c6
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c312
-rw-r--r--drivers/platform/x86/topstar-laptop.c1
-rw-r--r--drivers/platform/x86/toshiba-wmi.c2
-rw-r--r--drivers/platform/x86/toshiba_acpi.c13
-rw-r--r--drivers/platform/x86/toshiba_haps.c2
-rw-r--r--drivers/platform/x86/wmi-bmof.c125
-rw-r--r--drivers/platform/x86/wmi.c692
64 files changed, 3463 insertions, 1106 deletions
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index ca2692510733..d3a6630266a0 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for linux/drivers/platform
#
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 76bdae1a93bb..0ad6e290bbda 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -49,7 +49,7 @@ config CROS_EC_CHARDEV
config CROS_EC_LPC
tristate "ChromeOS Embedded Controller (LPC)"
- depends on MFD_CROS_EC && (X86 || COMPILE_TEST)
+ depends on MFD_CROS_EC && ACPI && (X86 || COMPILE_TEST)
help
If you say Y here, you get support for talking to the ChromeOS EC
over an LPC bus. This uses a simple byte-level protocol with a
@@ -59,6 +59,18 @@ config CROS_EC_LPC
To compile this driver as a module, choose M here: the
module will be called cros_ec_lpc.
+config CROS_EC_LPC_MEC
+ bool "ChromeOS Embedded Controller LPC Microchip EC (MEC) variant"
+ depends on CROS_EC_LPC
+ default n
+ help
+ If you say Y here, a variant LPC protocol for the Microchip EC
+ will be used. Note that this variant is not backward compatible
+ with non-Microchip ECs.
+
+ If you have a ChromeOS Embedded Controller Microchip EC variant
+ choose Y here.
+
config CROS_EC_PROTO
bool
help
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index 4f3462783a3c..a077b1f0211d 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -1,9 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o
cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o \
- cros_ec_lightbar.o cros_ec_vbc.o
+ cros_ec_lightbar.o cros_ec_vbc.o \
+ cros_ec_debugfs.o
obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o
-obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o
+cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_reg.o
+cros_ec_lpcs-$(CONFIG_CROS_EC_LPC_MEC) += cros_ec_lpc_mec.o
+obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o
obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o
obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o
diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c
index e8a44a9bc916..d8599736a41a 100644
--- a/drivers/platform/chrome/chromeos_laptop.c
+++ b/drivers/platform/chrome/chromeos_laptop.c
@@ -518,7 +518,7 @@ static struct chromeos_laptop cr48 = {
.callback = chromeos_laptop_dmi_matched, \
.driver_data = (void *)&board_
-static struct dmi_system_id chromeos_laptop_dmi_table[] __initdata = {
+static const struct dmi_system_id chromeos_laptop_dmi_table[] __initconst = {
{
.ident = "Samsung Series 5 550",
.matches = {
diff --git a/drivers/platform/chrome/chromeos_pstore.c b/drivers/platform/chrome/chromeos_pstore.c
index 308a853ac4f1..b0693fdec8c6 100644
--- a/drivers/platform/chrome/chromeos_pstore.c
+++ b/drivers/platform/chrome/chromeos_pstore.c
@@ -14,7 +14,7 @@
#include <linux/platform_device.h>
#include <linux/pstore_ram.h>
-static struct dmi_system_id chromeos_pstore_dmi_table[] __initdata = {
+static const struct dmi_system_id chromeos_pstore_dmi_table[] __initconst = {
{
/*
* Today all Chromebooks/boxes ship with Google_* as version and
diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c
new file mode 100644
index 000000000000..4cc66f405760
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_debugfs.c
@@ -0,0 +1,401 @@
+/*
+ * cros_ec_debugfs - debug logs for Chrome OS EC
+ *
+ * Copyright 2015 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/circ_buf.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/mutex.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include "cros_ec_dev.h"
+#include "cros_ec_debugfs.h"
+
+#define LOG_SHIFT 14
+#define LOG_SIZE (1 << LOG_SHIFT)
+#define LOG_POLL_SEC 10
+
+#define CIRC_ADD(idx, size, value) (((idx) + (value)) & ((size) - 1))
+
+/* struct cros_ec_debugfs - ChromeOS EC debugging information
+ *
+ * @ec: EC device this debugfs information belongs to
+ * @dir: dentry for debugfs files
+ * @log_buffer: circular buffer for console log information
+ * @read_msg: preallocated EC command and buffer to read console log
+ * @log_mutex: mutex to protect circular buffer
+ * @log_wq: waitqueue for log readers
+ * @log_poll_work: recurring task to poll EC for new console log data
+ * @panicinfo_blob: panicinfo debugfs blob
+ */
+struct cros_ec_debugfs {
+ struct cros_ec_dev *ec;
+ struct dentry *dir;
+ /* EC log */
+ struct circ_buf log_buffer;
+ struct cros_ec_command *read_msg;
+ struct mutex log_mutex;
+ wait_queue_head_t log_wq;
+ struct delayed_work log_poll_work;
+ /* EC panicinfo */
+ struct debugfs_blob_wrapper panicinfo_blob;
+};
+
+/*
+ * We need to make sure that the EC log buffer on the UART is large enough,
+ * so that it is unlikely enough to overlow within LOG_POLL_SEC.
+ */
+static void cros_ec_console_log_work(struct work_struct *__work)
+{
+ struct cros_ec_debugfs *debug_info =
+ container_of(to_delayed_work(__work),
+ struct cros_ec_debugfs,
+ log_poll_work);
+ struct cros_ec_dev *ec = debug_info->ec;
+ struct circ_buf *cb = &debug_info->log_buffer;
+ struct cros_ec_command snapshot_msg = {
+ .command = EC_CMD_CONSOLE_SNAPSHOT + ec->cmd_offset,
+ };
+
+ struct ec_params_console_read_v1 *read_params =
+ (struct ec_params_console_read_v1 *)debug_info->read_msg->data;
+ uint8_t *ec_buffer = (uint8_t *)debug_info->read_msg->data;
+ int idx;
+ int buf_space;
+ int ret;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, &snapshot_msg);
+ if (ret < 0) {
+ dev_err(ec->dev, "EC communication failed\n");
+ goto resched;
+ }
+ if (snapshot_msg.result != EC_RES_SUCCESS) {
+ dev_err(ec->dev, "EC failed to snapshot the console log\n");
+ goto resched;
+ }
+
+ /* Loop until we have read everything, or there's an error. */
+ mutex_lock(&debug_info->log_mutex);
+ buf_space = CIRC_SPACE(cb->head, cb->tail, LOG_SIZE);
+
+ while (1) {
+ if (!buf_space) {
+ dev_info_once(ec->dev,
+ "Some logs may have been dropped...\n");
+ break;
+ }
+
+ memset(read_params, '\0', sizeof(*read_params));
+ read_params->subcmd = CONSOLE_READ_RECENT;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, debug_info->read_msg);
+ if (ret < 0) {
+ dev_err(ec->dev, "EC communication failed\n");
+ break;
+ }
+ if (debug_info->read_msg->result != EC_RES_SUCCESS) {
+ dev_err(ec->dev,
+ "EC failed to read the console log\n");
+ break;
+ }
+
+ /* If the buffer is empty, we're done here. */
+ if (ret == 0 || ec_buffer[0] == '\0')
+ break;
+
+ idx = 0;
+ while (idx < ret && ec_buffer[idx] != '\0' && buf_space > 0) {
+ cb->buf[cb->head] = ec_buffer[idx];
+ cb->head = CIRC_ADD(cb->head, LOG_SIZE, 1);
+ idx++;
+ buf_space--;
+ }
+
+ wake_up(&debug_info->log_wq);
+ }
+
+ mutex_unlock(&debug_info->log_mutex);
+
+resched:
+ schedule_delayed_work(&debug_info->log_poll_work,
+ msecs_to_jiffies(LOG_POLL_SEC * 1000));
+}
+
+static int cros_ec_console_log_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t cros_ec_console_log_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct cros_ec_debugfs *debug_info = file->private_data;
+ struct circ_buf *cb = &debug_info->log_buffer;
+ ssize_t ret;
+
+ mutex_lock(&debug_info->log_mutex);
+
+ while (!CIRC_CNT(cb->head, cb->tail, LOG_SIZE)) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ goto error;
+ }
+
+ mutex_unlock(&debug_info->log_mutex);
+
+ ret = wait_event_interruptible(debug_info->log_wq,
+ CIRC_CNT(cb->head, cb->tail, LOG_SIZE));
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&debug_info->log_mutex);
+ }
+
+ /* Only copy until the end of the circular buffer, and let userspace
+ * retry to get the rest of the data.
+ */
+ ret = min_t(size_t, CIRC_CNT_TO_END(cb->head, cb->tail, LOG_SIZE),
+ count);
+
+ if (copy_to_user(buf, cb->buf + cb->tail, ret)) {
+ ret = -EFAULT;
+ goto error;
+ }
+
+ cb->tail = CIRC_ADD(cb->tail, LOG_SIZE, ret);
+
+error:
+ mutex_unlock(&debug_info->log_mutex);
+ return ret;
+}
+
+static unsigned int cros_ec_console_log_poll(struct file *file,
+ poll_table *wait)
+{
+ struct cros_ec_debugfs *debug_info = file->private_data;
+ unsigned int mask = 0;
+
+ poll_wait(file, &debug_info->log_wq, wait);
+
+ mutex_lock(&debug_info->log_mutex);
+ if (CIRC_CNT(debug_info->log_buffer.head,
+ debug_info->log_buffer.tail,
+ LOG_SIZE))
+ mask |= POLLIN | POLLRDNORM;
+ mutex_unlock(&debug_info->log_mutex);
+
+ return mask;
+}
+
+static int cros_ec_console_log_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+const struct file_operations cros_ec_console_log_fops = {
+ .owner = THIS_MODULE,
+ .open = cros_ec_console_log_open,
+ .read = cros_ec_console_log_read,
+ .llseek = no_llseek,
+ .poll = cros_ec_console_log_poll,
+ .release = cros_ec_console_log_release,
+};
+
+static int ec_read_version_supported(struct cros_ec_dev *ec)
+{
+ struct ec_params_get_cmd_versions_v1 *params;
+ struct ec_response_get_cmd_versions *response;
+ int ret;
+
+ struct cros_ec_command *msg;
+
+ msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*response)),
+ GFP_KERNEL);
+ if (!msg)
+ return 0;
+
+ msg->command = EC_CMD_GET_CMD_VERSIONS + ec->cmd_offset;
+ msg->outsize = sizeof(*params);
+ msg->insize = sizeof(*response);
+
+ params = (struct ec_params_get_cmd_versions_v1 *)msg->data;
+ params->cmd = EC_CMD_CONSOLE_READ;
+ response = (struct ec_response_get_cmd_versions *)msg->data;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg) >= 0 &&
+ msg->result == EC_RES_SUCCESS &&
+ (response->version_mask & EC_VER_MASK(1));
+
+ kfree(msg);
+
+ return ret;
+}
+
+static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info)
+{
+ struct cros_ec_dev *ec = debug_info->ec;
+ char *buf;
+ int read_params_size;
+ int read_response_size;
+
+ if (!ec_read_version_supported(ec)) {
+ dev_warn(ec->dev,
+ "device does not support reading the console log\n");
+ return 0;
+ }
+
+ buf = devm_kzalloc(ec->dev, LOG_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ read_params_size = sizeof(struct ec_params_console_read_v1);
+ read_response_size = ec->ec_dev->max_response;
+ debug_info->read_msg = devm_kzalloc(ec->dev,
+ sizeof(*debug_info->read_msg) +
+ max(read_params_size, read_response_size), GFP_KERNEL);
+ if (!debug_info->read_msg)
+ return -ENOMEM;
+
+ debug_info->read_msg->version = 1;
+ debug_info->read_msg->command = EC_CMD_CONSOLE_READ + ec->cmd_offset;
+ debug_info->read_msg->outsize = read_params_size;
+ debug_info->read_msg->insize = read_response_size;
+
+ debug_info->log_buffer.buf = buf;
+ debug_info->log_buffer.head = 0;
+ debug_info->log_buffer.tail = 0;
+
+ mutex_init(&debug_info->log_mutex);
+ init_waitqueue_head(&debug_info->log_wq);
+
+ if (!debugfs_create_file("console_log",
+ S_IFREG | S_IRUGO,
+ debug_info->dir,
+ debug_info,
+ &cros_ec_console_log_fops))
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&debug_info->log_poll_work,
+ cros_ec_console_log_work);
+ schedule_delayed_work(&debug_info->log_poll_work, 0);
+
+ return 0;
+}
+
+static void cros_ec_cleanup_console_log(struct cros_ec_debugfs *debug_info)
+{
+ if (debug_info->log_buffer.buf) {
+ cancel_delayed_work_sync(&debug_info->log_poll_work);
+ mutex_destroy(&debug_info->log_mutex);
+ }
+}
+
+static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info)
+{
+ struct cros_ec_device *ec_dev = debug_info->ec->ec_dev;
+ int ret;
+ struct cros_ec_command *msg;
+ int insize;
+
+ insize = ec_dev->max_response;
+
+ msg = devm_kzalloc(debug_info->ec->dev,
+ sizeof(*msg) + insize, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->command = EC_CMD_GET_PANIC_INFO;
+ msg->insize = insize;
+
+ ret = cros_ec_cmd_xfer(ec_dev, msg);
+ if (ret < 0) {
+ dev_warn(debug_info->ec->dev, "Cannot read panicinfo.\n");
+ ret = 0;
+ goto free;
+ }
+
+ /* No panic data */
+ if (ret == 0)
+ goto free;
+
+ debug_info->panicinfo_blob.data = msg->data;
+ debug_info->panicinfo_blob.size = ret;
+
+ if (!debugfs_create_blob("panicinfo",
+ S_IFREG | S_IRUGO,
+ debug_info->dir,
+ &debug_info->panicinfo_blob)) {
+ ret = -ENOMEM;
+ goto free;
+ }
+
+ return 0;
+
+free:
+ devm_kfree(debug_info->ec->dev, msg);
+ return ret;
+}
+
+int cros_ec_debugfs_init(struct cros_ec_dev *ec)
+{
+ struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
+ const char *name = ec_platform->ec_name;
+ struct cros_ec_debugfs *debug_info;
+ int ret;
+
+ debug_info = devm_kzalloc(ec->dev, sizeof(*debug_info), GFP_KERNEL);
+ if (!debug_info)
+ return -ENOMEM;
+
+ debug_info->ec = ec;
+ debug_info->dir = debugfs_create_dir(name, NULL);
+ if (!debug_info->dir)
+ return -ENOMEM;
+
+ ret = cros_ec_create_panicinfo(debug_info);
+ if (ret)
+ goto remove_debugfs;
+
+ ret = cros_ec_create_console_log(debug_info);
+ if (ret)
+ goto remove_debugfs;
+
+ ec->debug_info = debug_info;
+
+ return 0;
+
+remove_debugfs:
+ debugfs_remove_recursive(debug_info->dir);
+ return ret;
+}
+
+void cros_ec_debugfs_remove(struct cros_ec_dev *ec)
+{
+ if (!ec->debug_info)
+ return;
+
+ debugfs_remove_recursive(ec->debug_info->dir);
+ cros_ec_cleanup_console_log(ec->debug_info);
+}
diff --git a/drivers/platform/chrome/cros_ec_debugfs.h b/drivers/platform/chrome/cros_ec_debugfs.h
new file mode 100644
index 000000000000..1ff3a50aa1b8
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_debugfs.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _DRV_CROS_EC_DEBUGFS_H_
+#define _DRV_CROS_EC_DEBUGFS_H_
+
+#include "cros_ec_dev.h"
+
+/* debugfs stuff */
+int cros_ec_debugfs_init(struct cros_ec_dev *ec);
+void cros_ec_debugfs_remove(struct cros_ec_dev *ec);
+
+#endif /* _DRV_CROS_EC_DEBUGFS_H_ */
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
index 6aa120cd0574..cf6c4f0846b8 100644
--- a/drivers/platform/chrome/cros_ec_dev.c
+++ b/drivers/platform/chrome/cros_ec_dev.c
@@ -21,9 +21,11 @@
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#include "cros_ec_debugfs.h"
#include "cros_ec_dev.h"
/* Device variables */
@@ -427,10 +429,16 @@ static int ec_device_probe(struct platform_device *pdev)
goto failed;
}
+ if (cros_ec_debugfs_init(ec))
+ dev_warn(dev, "failed to create debugfs directory\n");
+
/* check whether this EC is a sensor hub. */
if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
cros_ec_sensors_register(ec);
+ /* Take control of the lightbar from the EC. */
+ lb_manual_suspend_ctrl(ec, 1);
+
return 0;
failed:
@@ -441,6 +449,12 @@ failed:
static int ec_device_remove(struct platform_device *pdev)
{
struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
+
+ /* Let the EC take over the lightbar again. */
+ lb_manual_suspend_ctrl(ec, 0);
+
+ cros_ec_debugfs_remove(ec);
+
cdev_del(&ec->cdev);
device_unregister(&ec->class_dev);
return 0;
@@ -452,9 +466,35 @@ static const struct platform_device_id cros_ec_id[] = {
};
MODULE_DEVICE_TABLE(platform, cros_ec_id);
+static __maybe_unused int ec_device_suspend(struct device *dev)
+{
+ struct cros_ec_dev *ec = dev_get_drvdata(dev);
+
+ lb_suspend(ec);
+
+ return 0;
+}
+
+static __maybe_unused int ec_device_resume(struct device *dev)
+{
+ struct cros_ec_dev *ec = dev_get_drvdata(dev);
+
+ lb_resume(ec);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cros_ec_dev_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = ec_device_suspend,
+ .resume = ec_device_resume,
+#endif
+};
+
static struct platform_driver cros_ec_dev_driver = {
.driver = {
.name = "cros-ec-ctl",
+ .pm = &cros_ec_dev_pm_ops,
},
.probe = ec_device_probe,
.remove = ec_device_remove,
diff --git a/drivers/platform/chrome/cros_ec_dev.h b/drivers/platform/chrome/cros_ec_dev.h
index bfd2c84c3571..45e9453608c5 100644
--- a/drivers/platform/chrome/cros_ec_dev.h
+++ b/drivers/platform/chrome/cros_ec_dev.h
@@ -43,4 +43,10 @@ struct cros_ec_readmem {
#define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command)
#define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem)
+/* Lightbar utilities */
+extern bool ec_has_lightbar(struct cros_ec_dev *ec);
+extern int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable);
+extern int lb_suspend(struct cros_ec_dev *ec);
+extern int lb_resume(struct cros_ec_dev *ec);
+
#endif /* _CROS_EC_DEV_H_ */
diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c
index 8df3d447cacf..fd2b047a2748 100644
--- a/drivers/platform/chrome/cros_ec_lightbar.c
+++ b/drivers/platform/chrome/cros_ec_lightbar.c
@@ -38,6 +38,13 @@
/* Rate-limit the lightbar interface to prevent DoS. */
static unsigned long lb_interval_jiffies = 50 * HZ / 1000;
+/*
+ * Whether or not we have given userspace control of the lightbar.
+ * If this is true, we won't do anything during suspend/resume.
+ */
+static bool userspace_control;
+static struct cros_ec_dev *ec_with_lightbar;
+
static ssize_t interval_msec_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -295,7 +302,8 @@ exit:
static char const *seqname[] = {
"ERROR", "S5", "S3", "S0", "S5S3", "S3S0",
- "S0S3", "S3S5", "STOP", "RUN", "PULSE", "TEST", "KONAMI",
+ "S0S3", "S3S5", "STOP", "RUN", "KONAMI",
+ "TAP", "PROGRAM",
};
static ssize_t sequence_show(struct device *dev,
@@ -340,6 +348,89 @@ exit:
return ret;
}
+static int lb_send_empty_cmd(struct cros_ec_dev *ec, uint8_t cmd)
+{
+ struct ec_params_lightbar *param;
+ struct cros_ec_command *msg;
+ int ret;
+
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_lightbar *)msg->data;
+ param->cmd = cmd;
+
+ ret = lb_throttle();
+ if (ret)
+ goto error;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0)
+ goto error;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = -EINVAL;
+ goto error;
+ }
+ ret = 0;
+error:
+ kfree(msg);
+
+ return ret;
+}
+
+int lb_manual_suspend_ctrl(struct cros_ec_dev *ec, uint8_t enable)
+{
+ struct ec_params_lightbar *param;
+ struct cros_ec_command *msg;
+ int ret;
+
+ if (ec != ec_with_lightbar)
+ return 0;
+
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_lightbar *)msg->data;
+
+ param->cmd = LIGHTBAR_CMD_MANUAL_SUSPEND_CTRL;
+ param->manual_suspend_ctrl.enable = enable;
+
+ ret = lb_throttle();
+ if (ret)
+ goto error;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0)
+ goto error;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = -EINVAL;
+ goto error;
+ }
+ ret = 0;
+error:
+ kfree(msg);
+
+ return ret;
+}
+
+int lb_suspend(struct cros_ec_dev *ec)
+{
+ if (userspace_control || ec != ec_with_lightbar)
+ return 0;
+
+ return lb_send_empty_cmd(ec, LIGHTBAR_CMD_SUSPEND);
+}
+
+int lb_resume(struct cros_ec_dev *ec)
+{
+ if (userspace_control || ec != ec_with_lightbar)
+ return 0;
+
+ return lb_send_empty_cmd(ec, LIGHTBAR_CMD_RESUME);
+}
+
static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -390,6 +481,93 @@ exit:
return ret;
}
+static ssize_t program_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int extra_bytes, max_size, ret;
+ struct ec_params_lightbar *param;
+ struct cros_ec_command *msg;
+ struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev,
+ class_dev);
+
+ /*
+ * We might need to reject the program for size reasons. The EC
+ * enforces a maximum program size, but we also don't want to try
+ * and send a program that is too big for the protocol. In order
+ * to ensure the latter, we also need to ensure we have extra bytes
+ * to represent the rest of the packet.
+ */
+ extra_bytes = sizeof(*param) - sizeof(param->set_program.data);
+ max_size = min(EC_LB_PROG_LEN, ec->ec_dev->max_request - extra_bytes);
+ if (count > max_size) {
+ dev_err(dev, "Program is %u bytes, too long to send (max: %u)",
+ (unsigned int)count, max_size);
+
+ return -EINVAL;
+ }
+
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
+ ret = lb_throttle();
+ if (ret)
+ goto exit;
+
+ dev_info(dev, "Copying %zu byte program to EC", count);
+
+ param = (struct ec_params_lightbar *)msg->data;
+ param->cmd = LIGHTBAR_CMD_SET_PROGRAM;
+
+ param->set_program.size = count;
+ memcpy(param->set_program.data, buf, count);
+
+ /*
+ * We need to set the message size manually or else it will use
+ * EC_LB_PROG_LEN. This might be too long, and the program
+ * is unlikely to use all of the space.
+ */
+ msg->outsize = count + extra_bytes;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0)
+ goto exit;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = count;
+exit:
+ kfree(msg);
+
+ return ret;
+}
+
+static ssize_t userspace_control_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%d\n", userspace_control);
+}
+
+static ssize_t userspace_control_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ bool enable;
+ int ret;
+
+ ret = strtobool(buf, &enable);
+ if (ret < 0)
+ return ret;
+
+ userspace_control = enable;
+
+ return count;
+}
+
/* Module initialization */
static DEVICE_ATTR_RW(interval_msec);
@@ -397,15 +575,25 @@ static DEVICE_ATTR_RO(version);
static DEVICE_ATTR_WO(brightness);
static DEVICE_ATTR_WO(led_rgb);
static DEVICE_ATTR_RW(sequence);
+static DEVICE_ATTR_WO(program);
+static DEVICE_ATTR_RW(userspace_control);
+
static struct attribute *__lb_cmds_attrs[] = {
&dev_attr_interval_msec.attr,
&dev_attr_version.attr,
&dev_attr_brightness.attr,
&dev_attr_led_rgb.attr,
&dev_attr_sequence.attr,
+ &dev_attr_program.attr,
+ &dev_attr_userspace_control.attr,
NULL,
};
+bool ec_has_lightbar(struct cros_ec_dev *ec)
+{
+ return !!get_lightbar_version(ec, NULL, NULL);
+}
+
static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
struct attribute *a, int n)
{
@@ -422,10 +610,11 @@ static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
return 0;
/* Only instantiate this stuff if the EC has a lightbar */
- if (get_lightbar_version(ec, NULL, NULL))
+ if (ec_has_lightbar(ec)) {
+ ec_with_lightbar = ec;
return a->mode;
- else
- return 0;
+ }
+ return 0;
}
struct attribute_group cros_ec_lightbar_attr_group = {
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
index f9a245465fd0..1baf720faf69 100644
--- a/drivers/platform/chrome/cros_ec_lpc.c
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -21,24 +21,29 @@
* expensive.
*/
+#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/mfd/cros_ec.h>
#include <linux/mfd/cros_ec_commands.h>
+#include <linux/mfd/cros_ec_lpc_reg.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
-#define DRV_NAME "cros_ec_lpc"
+#define DRV_NAME "cros_ec_lpcs"
+#define ACPI_DRV_NAME "GOOG0004"
static int ec_response_timed_out(void)
{
unsigned long one_second = jiffies + HZ;
+ u8 data;
usleep_range(200, 300);
do {
- if (!(inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK))
+ if (!(cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_CMD, 1, &data) &
+ EC_LPC_STATUS_BUSY_MASK))
return 0;
usleep_range(100, 200);
} while (time_before(jiffies, one_second));
@@ -51,21 +56,20 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
{
struct ec_host_request *request;
struct ec_host_response response;
- u8 sum = 0;
- int i;
+ u8 sum;
int ret = 0;
u8 *dout;
ret = cros_ec_prepare_tx(ec, msg);
/* Write buffer */
- for (i = 0; i < ret; i++)
- outb(ec->dout[i], EC_LPC_ADDR_HOST_PACKET + i);
+ cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout);
request = (struct ec_host_request *)ec->dout;
/* Here we go */
- outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
+ sum = EC_COMMAND_PROTOCOL_3;
+ cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum);
if (ec_response_timed_out()) {
dev_warn(ec->dev, "EC responsed timed out\n");
@@ -74,17 +78,15 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
}
/* Check result */
- msg->result = inb(EC_LPC_ADDR_HOST_DATA);
+ msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum);
ret = cros_ec_check_result(ec, msg);
if (ret)
goto done;
/* Read back response */
dout = (u8 *)&response;
- for (i = 0; i < sizeof(response); i++) {
- dout[i] = inb(EC_LPC_ADDR_HOST_PACKET + i);
- sum += dout[i];
- }
+ sum = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET, sizeof(response),
+ dout);
msg->result = response.result;
@@ -97,11 +99,9 @@ static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
}
/* Read response and process checksum */
- for (i = 0; i < response.data_len; i++) {
- msg->data[i] =
- inb(EC_LPC_ADDR_HOST_PACKET + sizeof(response) + i);
- sum += msg->data[i];
- }
+ sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET +
+ sizeof(response), response.data_len,
+ msg->data);
if (sum) {
dev_err(ec->dev,
@@ -121,8 +121,7 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
struct cros_ec_command *msg)
{
struct ec_lpc_host_args args;
- int csum;
- int i;
+ u8 sum;
int ret = 0;
if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE ||
@@ -139,24 +138,20 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
args.data_size = msg->outsize;
/* Initialize checksum */
- csum = msg->command + args.flags +
- args.command_version + args.data_size;
+ sum = msg->command + args.flags + args.command_version + args.data_size;
/* Copy data and update checksum */
- for (i = 0; i < msg->outsize; i++) {
- outb(msg->data[i], EC_LPC_ADDR_HOST_PARAM + i);
- csum += msg->data[i];
- }
+ sum += cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PARAM, msg->outsize,
+ msg->data);
/* Finalize checksum and write args */
- args.checksum = csum & 0xFF;
- outb(args.flags, EC_LPC_ADDR_HOST_ARGS);
- outb(args.command_version, EC_LPC_ADDR_HOST_ARGS + 1);
- outb(args.data_size, EC_LPC_ADDR_HOST_ARGS + 2);
- outb(args.checksum, EC_LPC_ADDR_HOST_ARGS + 3);
+ args.checksum = sum;
+ cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args),
+ (u8 *)&args);
/* Here we go */
- outb(msg->command, EC_LPC_ADDR_HOST_CMD);
+ sum = msg->command;
+ cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum);
if (ec_response_timed_out()) {
dev_warn(ec->dev, "EC responsed timed out\n");
@@ -165,16 +160,14 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
}
/* Check result */
- msg->result = inb(EC_LPC_ADDR_HOST_DATA);
+ msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum);
ret = cros_ec_check_result(ec, msg);
if (ret)
goto done;
/* Read back args */
- args.flags = inb(EC_LPC_ADDR_HOST_ARGS);
- args.command_version = inb(EC_LPC_ADDR_HOST_ARGS + 1);
- args.data_size = inb(EC_LPC_ADDR_HOST_ARGS + 2);
- args.checksum = inb(EC_LPC_ADDR_HOST_ARGS + 3);
+ cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args),
+ (u8 *)&args);
if (args.data_size > msg->insize) {
dev_err(ec->dev,
@@ -185,20 +178,17 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
}
/* Start calculating response checksum */
- csum = msg->command + args.flags +
- args.command_version + args.data_size;
+ sum = msg->command + args.flags + args.command_version + args.data_size;
/* Read response and update checksum */
- for (i = 0; i < args.data_size; i++) {
- msg->data[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
- csum += msg->data[i];
- }
+ sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PARAM, args.data_size,
+ msg->data);
/* Verify checksum */
- if (args.checksum != (csum & 0xFF)) {
+ if (args.checksum != sum) {
dev_err(ec->dev,
"bad packet checksum, expected %02x, got %02x\n",
- args.checksum, csum & 0xFF);
+ args.checksum, sum);
ret = -EBADMSG;
goto done;
}
@@ -222,14 +212,13 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
/* fixed length */
if (bytes) {
- for (; cnt < bytes; i++, s++, cnt++)
- *s = inb(EC_LPC_ADDR_MEMMAP + i);
- return cnt;
+ cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + offset, bytes, s);
+ return bytes;
}
/* string */
for (; i < EC_MEMMAP_SIZE; i++, s++) {
- *s = inb(EC_LPC_ADDR_MEMMAP + i);
+ cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + i, 1, s);
cnt++;
if (!*s)
break;
@@ -238,10 +227,23 @@ static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
return cnt;
}
+static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
+{
+ struct cros_ec_device *ec_dev = data;
+
+ if (ec_dev->mkbp_event_supported &&
+ cros_ec_get_next_event(ec_dev, NULL) > 0)
+ blocking_notifier_call_chain(&ec_dev->event_notifier, 0,
+ ec_dev);
+}
+
static int cros_ec_lpc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct acpi_device *adev;
+ acpi_status status;
struct cros_ec_device *ec_dev;
+ u8 buf[2];
int ret;
if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE,
@@ -250,8 +252,8 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
return -EBUSY;
}
- if ((inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) != 'E') ||
- (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C')) {
+ cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf);
+ if (buf[0] != 'E' || buf[1] != 'C') {
dev_err(dev, "EC ID not detected\n");
return -ENODEV;
}
@@ -287,12 +289,33 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
return ret;
}
+ /*
+ * Connect a notify handler to process MKBP messages if we have a
+ * companion ACPI device.
+ */
+ adev = ACPI_COMPANION(dev);
+ if (adev) {
+ status = acpi_install_notify_handler(adev->handle,
+ ACPI_ALL_NOTIFY,
+ cros_ec_lpc_acpi_notify,
+ ec_dev);
+ if (ACPI_FAILURE(status))
+ dev_warn(dev, "Failed to register notifier %08x\n",
+ status);
+ }
+
return 0;
}
static int cros_ec_lpc_remove(struct platform_device *pdev)
{
struct cros_ec_device *ec_dev;
+ struct acpi_device *adev;
+
+ adev = ACPI_COMPANION(&pdev->dev);
+ if (adev)
+ acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY,
+ cros_ec_lpc_acpi_notify);
ec_dev = platform_get_drvdata(pdev);
cros_ec_remove(ec_dev);
@@ -300,7 +323,13 @@ static int cros_ec_lpc_remove(struct platform_device *pdev)
return 0;
}
-static struct dmi_system_id cros_ec_lpc_dmi_table[] __initdata = {
+static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = {
+ { ACPI_DRV_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids);
+
+static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = {
{
/*
* Today all Chromebooks/boxes ship with Google_* as version and
@@ -337,18 +366,36 @@ static struct dmi_system_id cros_ec_lpc_dmi_table[] __initdata = {
};
MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table);
+#ifdef CONFIG_PM_SLEEP
+static int cros_ec_lpc_suspend(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
+
+ return cros_ec_suspend(ec_dev);
+}
+
+static int cros_ec_lpc_resume(struct device *dev)
+{
+ struct cros_ec_device *ec_dev = dev_get_drvdata(dev);
+
+ return cros_ec_resume(ec_dev);
+}
+#endif
+
+const struct dev_pm_ops cros_ec_lpc_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(cros_ec_lpc_suspend, cros_ec_lpc_resume)
+};
+
static struct platform_driver cros_ec_lpc_driver = {
.driver = {
.name = DRV_NAME,
+ .acpi_match_table = cros_ec_lpc_acpi_device_ids,
+ .pm = &cros_ec_lpc_pm_ops,
},
.probe = cros_ec_lpc_probe,
.remove = cros_ec_lpc_remove,
};
-static struct platform_device cros_ec_lpc_device = {
- .name = DRV_NAME
-};
-
static int __init cros_ec_lpc_init(void)
{
int ret;
@@ -358,18 +405,13 @@ static int __init cros_ec_lpc_init(void)
return -ENODEV;
}
+ cros_ec_lpc_reg_init();
+
/* Register the driver */
ret = platform_driver_register(&cros_ec_lpc_driver);
if (ret) {
pr_err(DRV_NAME ": can't register driver: %d\n", ret);
- return ret;
- }
-
- /* Register the device, and it'll get hooked up automatically */
- ret = platform_device_register(&cros_ec_lpc_device);
- if (ret) {
- pr_err(DRV_NAME ": can't register device: %d\n", ret);
- platform_driver_unregister(&cros_ec_lpc_driver);
+ cros_ec_lpc_reg_destroy();
return ret;
}
@@ -378,8 +420,8 @@ static int __init cros_ec_lpc_init(void)
static void __exit cros_ec_lpc_exit(void)
{
- platform_device_unregister(&cros_ec_lpc_device);
platform_driver_unregister(&cros_ec_lpc_driver);
+ cros_ec_lpc_reg_destroy();
}
module_init(cros_ec_lpc_init);
diff --git a/drivers/platform/chrome/cros_ec_lpc_mec.c b/drivers/platform/chrome/cros_ec_lpc_mec.c
new file mode 100644
index 000000000000..2eda2c2fc210
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_lpc_mec.c
@@ -0,0 +1,140 @@
+/*
+ * cros_ec_lpc_mec - LPC variant I/O for Microchip EC
+ *
+ * Copyright (C) 2016 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This driver uses the Chrome OS EC byte-level message-based protocol for
+ * communicating the keyboard state (which keys are pressed) from a keyboard EC
+ * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing,
+ * but everything else (including deghosting) is done here. The main
+ * motivation for this is to keep the EC firmware as simple as possible, since
+ * it cannot be easily upgraded and EC flash/IRAM space is relatively
+ * expensive.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/mfd/cros_ec_lpc_mec.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+/*
+ * This mutex must be held while accessing the EMI unit. We can't rely on the
+ * EC mutex because memmap data may be accessed without it being held.
+ */
+static struct mutex io_mutex;
+
+/*
+ * cros_ec_lpc_mec_emi_write_address
+ *
+ * Initialize EMI read / write at a given address.
+ *
+ * @addr: Starting read / write address
+ * @access_type: Type of access, typically 32-bit auto-increment
+ */
+static void cros_ec_lpc_mec_emi_write_address(u16 addr,
+ enum cros_ec_lpc_mec_emi_access_mode access_type)
+{
+ /* Address relative to start of EMI range */
+ addr -= MEC_EMI_RANGE_START;
+ outb((addr & 0xfc) | access_type, MEC_EMI_EC_ADDRESS_B0);
+ outb((addr >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1);
+}
+
+/*
+ * cros_ec_lpc_io_bytes_mec - Read / write bytes to MEC EMI port
+ *
+ * @io_type: MEC_IO_READ or MEC_IO_WRITE, depending on request
+ * @offset: Base read / write address
+ * @length: Number of bytes to read / write
+ * @buf: Destination / source buffer
+ *
+ * @return 8-bit checksum of all bytes read / written
+ */
+u8 cros_ec_lpc_io_bytes_mec(enum cros_ec_lpc_mec_io_type io_type,
+ unsigned int offset, unsigned int length,
+ u8 *buf)
+{
+ int i = 0;
+ int io_addr;
+ u8 sum = 0;
+ enum cros_ec_lpc_mec_emi_access_mode access, new_access;
+
+ /*
+ * Long access cannot be used on misaligned data since reading B0 loads
+ * the data register and writing B3 flushes.
+ */
+ if (offset & 0x3 || length < 4)
+ access = ACCESS_TYPE_BYTE;
+ else
+ access = ACCESS_TYPE_LONG_AUTO_INCREMENT;
+
+ mutex_lock(&io_mutex);
+
+ /* Initialize I/O at desired address */
+ cros_ec_lpc_mec_emi_write_address(offset, access);
+
+ /* Skip bytes in case of misaligned offset */
+ io_addr = MEC_EMI_EC_DATA_B0 + (offset & 0x3);
+ while (i < length) {
+ while (io_addr <= MEC_EMI_EC_DATA_B3) {
+ if (io_type == MEC_IO_READ)
+ buf[i] = inb(io_addr++);
+ else
+ outb(buf[i], io_addr++);
+
+ sum += buf[i++];
+ offset++;
+
+ /* Extra bounds check in case of misaligned length */
+ if (i == length)
+ goto done;
+ }
+
+ /*
+ * Use long auto-increment access except for misaligned write,
+ * since writing B3 triggers the flush.
+ */
+ if (length - i < 4 && io_type == MEC_IO_WRITE)
+ new_access = ACCESS_TYPE_BYTE;
+ else
+ new_access = ACCESS_TYPE_LONG_AUTO_INCREMENT;
+
+ if (new_access != access ||
+ access != ACCESS_TYPE_LONG_AUTO_INCREMENT) {
+ access = new_access;
+ cros_ec_lpc_mec_emi_write_address(offset, access);
+ }
+
+ /* Access [B0, B3] on each loop pass */
+ io_addr = MEC_EMI_EC_DATA_B0;
+ }
+
+done:
+ mutex_unlock(&io_mutex);
+
+ return sum;
+}
+EXPORT_SYMBOL(cros_ec_lpc_io_bytes_mec);
+
+void cros_ec_lpc_mec_init(void)
+{
+ mutex_init(&io_mutex);
+}
+EXPORT_SYMBOL(cros_ec_lpc_mec_init);
+
+void cros_ec_lpc_mec_destroy(void)
+{
+ mutex_destroy(&io_mutex);
+}
+EXPORT_SYMBOL(cros_ec_lpc_mec_destroy);
diff --git a/drivers/platform/chrome/cros_ec_lpc_reg.c b/drivers/platform/chrome/cros_ec_lpc_reg.c
new file mode 100644
index 000000000000..dcc7a3e30604
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_lpc_reg.c
@@ -0,0 +1,133 @@
+/*
+ * cros_ec_lpc_reg - LPC access to the Chrome OS Embedded Controller
+ *
+ * Copyright (C) 2016 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This driver uses the Chrome OS EC byte-level message-based protocol for
+ * communicating the keyboard state (which keys are pressed) from a keyboard EC
+ * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing,
+ * but everything else (including deghosting) is done here. The main
+ * motivation for this is to keep the EC firmware as simple as possible, since
+ * it cannot be easily upgraded and EC flash/IRAM space is relatively
+ * expensive.
+ */
+
+#include <linux/io.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/mfd/cros_ec_lpc_mec.h>
+
+static u8 lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest)
+{
+ int i;
+ int sum = 0;
+
+ for (i = 0; i < length; ++i) {
+ dest[i] = inb(offset + i);
+ sum += dest[i];
+ }
+
+ /* Return checksum of all bytes read */
+ return sum;
+}
+
+static u8 lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
+{
+ int i;
+ int sum = 0;
+
+ for (i = 0; i < length; ++i) {
+ outb(msg[i], offset + i);
+ sum += msg[i];
+ }
+
+ /* Return checksum of all bytes written */
+ return sum;
+}
+
+#ifdef CONFIG_CROS_EC_LPC_MEC
+
+u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest)
+{
+ if (length == 0)
+ return 0;
+
+ /* Access desired range through EMI interface */
+ if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) {
+ /* Ensure we don't straddle EMI region */
+ if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END))
+ return 0;
+
+ return cros_ec_lpc_io_bytes_mec(MEC_IO_READ, offset, length,
+ dest);
+ }
+
+ if (WARN_ON(offset + length > MEC_EMI_RANGE_START &&
+ offset < MEC_EMI_RANGE_START))
+ return 0;
+
+ return lpc_read_bytes(offset, length, dest);
+}
+
+u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
+{
+ if (length == 0)
+ return 0;
+
+ /* Access desired range through EMI interface */
+ if (offset >= MEC_EMI_RANGE_START && offset <= MEC_EMI_RANGE_END) {
+ /* Ensure we don't straddle EMI region */
+ if (WARN_ON(offset + length - 1 > MEC_EMI_RANGE_END))
+ return 0;
+
+ return cros_ec_lpc_io_bytes_mec(MEC_IO_WRITE, offset, length,
+ msg);
+ }
+
+ if (WARN_ON(offset + length > MEC_EMI_RANGE_START &&
+ offset < MEC_EMI_RANGE_START))
+ return 0;
+
+ return lpc_write_bytes(offset, length, msg);
+}
+
+void cros_ec_lpc_reg_init(void)
+{
+ cros_ec_lpc_mec_init();
+}
+
+void cros_ec_lpc_reg_destroy(void)
+{
+ cros_ec_lpc_mec_destroy();
+}
+
+#else /* CONFIG_CROS_EC_LPC_MEC */
+
+u8 cros_ec_lpc_read_bytes(unsigned int offset, unsigned int length, u8 *dest)
+{
+ return lpc_read_bytes(offset, length, dest);
+}
+
+u8 cros_ec_lpc_write_bytes(unsigned int offset, unsigned int length, u8 *msg)
+{
+ return lpc_write_bytes(offset, length, msg);
+}
+
+void cros_ec_lpc_reg_init(void)
+{
+}
+
+void cros_ec_lpc_reg_destroy(void)
+{
+}
+
+#endif /* CONFIG_CROS_EC_LPC_MEC */
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index ed5dee744c74..8dfa7fcb1248 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -150,6 +150,40 @@ int cros_ec_check_result(struct cros_ec_device *ec_dev,
}
EXPORT_SYMBOL(cros_ec_check_result);
+/*
+ * cros_ec_get_host_event_wake_mask
+ *
+ * Get the mask of host events that cause wake from suspend.
+ *
+ * @ec_dev: EC device to call
+ * @msg: message structure to use
+ * @mask: result when function returns >=0.
+ *
+ * LOCKING:
+ * the caller has ec_dev->lock mutex, or the caller knows there is
+ * no other command in progress.
+ */
+static int cros_ec_get_host_event_wake_mask(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg,
+ uint32_t *mask)
+{
+ struct ec_response_host_event_mask *r;
+ int ret;
+
+ msg->command = EC_CMD_HOST_EVENT_GET_WAKE_MASK;
+ msg->version = 0;
+ msg->outsize = 0;
+ msg->insize = sizeof(*r);
+
+ ret = send_command(ec_dev, msg);
+ if (ret > 0) {
+ r = (struct ec_response_host_event_mask *)msg->data;
+ *mask = r->mask;
+ }
+
+ return ret;
+}
+
static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev,
int devidx,
struct cros_ec_command *msg)
@@ -235,6 +269,22 @@ static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev)
return ret;
}
+/*
+ * cros_ec_get_host_command_version_mask
+ *
+ * Get the version mask of a given command.
+ *
+ * @ec_dev: EC device to call
+ * @msg: message structure to use
+ * @cmd: command to get the version of.
+ * @mask: result when function returns 0.
+ *
+ * @return 0 on success, error code otherwise
+ *
+ * LOCKING:
+ * the caller has ec_dev->lock mutex or the caller knows there is
+ * no other command in progress.
+ */
static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev,
u16 cmd, u32 *mask)
{
@@ -256,7 +306,7 @@ static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev,
pver = (struct ec_params_get_cmd_versions *)msg->data;
pver->cmd = cmd;
- ret = cros_ec_cmd_xfer(ec_dev, msg);
+ ret = send_command(ec_dev, msg);
if (ret > 0) {
rver = (struct ec_response_get_cmd_versions *)msg->data;
*mask = rver->version_mask;
@@ -371,6 +421,17 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev)
else
ec_dev->mkbp_event_supported = 1;
+ /*
+ * Get host event wake mask, assume all events are wake events
+ * if unavailable.
+ */
+ ret = cros_ec_get_host_event_wake_mask(ec_dev, proto_msg,
+ &ec_dev->host_event_wake_mask);
+ if (ret < 0)
+ ec_dev->host_event_wake_mask = U32_MAX;
+
+ ret = 0;
+
exit:
kfree(proto_msg);
return ret;
@@ -486,11 +547,54 @@ static int get_keyboard_state_event(struct cros_ec_device *ec_dev)
return ec_dev->event_size;
}
-int cros_ec_get_next_event(struct cros_ec_device *ec_dev)
+int cros_ec_get_next_event(struct cros_ec_device *ec_dev, bool *wake_event)
{
- if (ec_dev->mkbp_event_supported)
- return get_next_event(ec_dev);
- else
- return get_keyboard_state_event(ec_dev);
+ u32 host_event;
+ int ret;
+
+ if (!ec_dev->mkbp_event_supported) {
+ ret = get_keyboard_state_event(ec_dev);
+ if (ret < 0)
+ return ret;
+
+ if (wake_event)
+ *wake_event = true;
+
+ return ret;
+ }
+
+ ret = get_next_event(ec_dev);
+ if (ret < 0)
+ return ret;
+
+ if (wake_event) {
+ host_event = cros_ec_get_host_event(ec_dev);
+
+ /* Consider non-host_event as wake event */
+ *wake_event = !host_event ||
+ !!(host_event & ec_dev->host_event_wake_mask);
+ }
+
+ return ret;
}
EXPORT_SYMBOL(cros_ec_get_next_event);
+
+u32 cros_ec_get_host_event(struct cros_ec_device *ec_dev)
+{
+ u32 host_event;
+
+ BUG_ON(!ec_dev->mkbp_event_supported);
+
+ if (ec_dev->event_data.event_type != EC_MKBP_EVENT_HOST_EVENT)
+ return 0;
+
+ if (ec_dev->event_size != sizeof(host_event)) {
+ dev_warn(ec_dev->dev, "Invalid host event size\n");
+ return 0;
+ }
+
+ host_event = get_unaligned_le32(&ec_dev->event_data.data.host_event);
+
+ return host_event;
+}
+EXPORT_SYMBOL(cros_ec_get_host_event);
diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c
index 5f3672153b12..0578d34eec3f 100644
--- a/drivers/platform/goldfish/goldfish_pipe.c
+++ b/drivers/platform/goldfish/goldfish_pipe.c
@@ -266,7 +266,7 @@ struct goldfish_pipe_dev {
unsigned char __iomem *base;
};
-struct goldfish_pipe_dev pipe_dev[1] = {};
+static struct goldfish_pipe_dev pipe_dev[1] = {};
static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd)
{
diff --git a/drivers/platform/mips/cpu_hwmon.c b/drivers/platform/mips/cpu_hwmon.c
index 4300a558d0f3..322de58eebaf 100644
--- a/drivers/platform/mips/cpu_hwmon.c
+++ b/drivers/platform/mips/cpu_hwmon.c
@@ -17,17 +17,27 @@
*/
int loongson3_cpu_temp(int cpu)
{
- u32 reg;
+ u32 reg, prid_rev;
reg = LOONGSON_CHIPTEMP(cpu);
- if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1)
+ prid_rev = read_c0_prid() & PRID_REV_MASK;
+ switch (prid_rev) {
+ case PRID_REV_LOONGSON3A_R1:
reg = (reg >> 8) & 0xff;
- else
+ break;
+ case PRID_REV_LOONGSON3A_R2:
+ case PRID_REV_LOONGSON3B_R1:
+ case PRID_REV_LOONGSON3B_R2:
reg = ((reg >> 8) & 0xff) - 100;
-
+ break;
+ case PRID_REV_LOONGSON3A_R3:
+ reg = (reg & 0xffff)*731/0x4000 - 273;
+ break;
+ }
return (int)reg * 1000;
}
+static int nr_packages;
static struct device *cpu_hwmon_dev;
static ssize_t get_hwmon_name(struct device *dev,
@@ -51,88 +61,74 @@ static ssize_t get_hwmon_name(struct device *dev,
return sprintf(buf, "cpu-hwmon\n");
}
-static ssize_t get_cpu0_temp(struct device *dev,
- struct device_attribute *attr, char *buf);
-static ssize_t get_cpu1_temp(struct device *dev,
+static ssize_t get_cpu_temp(struct device *dev,
struct device_attribute *attr, char *buf);
-static ssize_t cpu0_temp_label(struct device *dev,
+static ssize_t cpu_temp_label(struct device *dev,
struct device_attribute *attr, char *buf);
-static ssize_t cpu1_temp_label(struct device *dev,
- struct device_attribute *attr, char *buf);
-
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_cpu0_temp, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, cpu0_temp_label, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, get_cpu1_temp, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, cpu1_temp_label, NULL, 2);
-static const struct attribute *hwmon_cputemp1[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_label.dev_attr.attr,
- NULL
-};
-
-static const struct attribute *hwmon_cputemp2[] = {
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp2_label.dev_attr.attr,
- NULL
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_cpu_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, cpu_temp_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, get_cpu_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, cpu_temp_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, get_cpu_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, cpu_temp_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, get_cpu_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, cpu_temp_label, NULL, 4);
+
+static const struct attribute *hwmon_cputemp[4][3] = {
+ {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_label.dev_attr.attr,
+ NULL
+ },
+ {
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_label.dev_attr.attr,
+ NULL
+ }
};
-static ssize_t cpu0_temp_label(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "CPU 0 Temperature\n");
-}
-
-static ssize_t cpu1_temp_label(struct device *dev,
+static ssize_t cpu_temp_label(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "CPU 1 Temperature\n");
+ int id = (to_sensor_dev_attr(attr))->index - 1;
+ return sprintf(buf, "CPU %d Temperature\n", id);
}
-static ssize_t get_cpu0_temp(struct device *dev,
+static ssize_t get_cpu_temp(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int value = loongson3_cpu_temp(0);
- return sprintf(buf, "%d\n", value);
-}
-
-static ssize_t get_cpu1_temp(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int value = loongson3_cpu_temp(1);
+ int id = (to_sensor_dev_attr(attr))->index - 1;
+ int value = loongson3_cpu_temp(id);
return sprintf(buf, "%d\n", value);
}
static int create_sysfs_cputemp_files(struct kobject *kobj)
{
- int ret;
-
- ret = sysfs_create_files(kobj, hwmon_cputemp1);
- if (ret)
- goto sysfs_create_temp1_fail;
-
- if (loongson_sysconf.nr_cpus <= loongson_sysconf.cores_per_package)
- return 0;
+ int i, ret = 0;
- ret = sysfs_create_files(kobj, hwmon_cputemp2);
- if (ret)
- goto sysfs_create_temp2_fail;
+ for (i=0; i<nr_packages; i++)
+ ret = sysfs_create_files(kobj, hwmon_cputemp[i]);
- return 0;
-
-sysfs_create_temp2_fail:
- sysfs_remove_files(kobj, hwmon_cputemp1);
-
-sysfs_create_temp1_fail:
- return -1;
+ return ret;
}
static void remove_sysfs_cputemp_files(struct kobject *kobj)
{
- sysfs_remove_files(&cpu_hwmon_dev->kobj, hwmon_cputemp1);
+ int i;
- if (loongson_sysconf.nr_cpus > loongson_sysconf.cores_per_package)
- sysfs_remove_files(&cpu_hwmon_dev->kobj, hwmon_cputemp2);
+ for (i=0; i<nr_packages; i++)
+ sysfs_remove_files(kobj, hwmon_cputemp[i]);
}
#define CPU_THERMAL_THRESHOLD 90000
@@ -140,8 +136,15 @@ static struct delayed_work thermal_work;
static void do_thermal_timer(struct work_struct *work)
{
- int value = loongson3_cpu_temp(0);
- if (value <= CPU_THERMAL_THRESHOLD)
+ int i, value, temp_max = 0;
+
+ for (i=0; i<nr_packages; i++) {
+ value = loongson3_cpu_temp(i);
+ if (value > temp_max)
+ temp_max = value;
+ }
+
+ if (temp_max <= CPU_THERMAL_THRESHOLD)
schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000));
else
orderly_poweroff(true);
@@ -160,6 +163,9 @@ static int __init loongson_hwmon_init(void)
goto fail_hwmon_device_register;
}
+ nr_packages = loongson_sysconf.nr_cpus /
+ loongson_sysconf.cores_per_package;
+
ret = sysfs_create_group(&cpu_hwmon_dev->kobj,
&cpu_hwmon_attribute_group);
if (ret) {
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 8489020ecf44..80b87954f6dd 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -195,16 +195,6 @@ config FUJITSU_LAPTOP
If you have a Fujitsu laptop, say Y or M here.
-config FUJITSU_LAPTOP_DEBUG
- bool "Verbose debug mode for Fujitsu Laptop Extras"
- depends on FUJITSU_LAPTOP
- default n
- ---help---
- Enables extra debug output from the fujitsu extras driver, at the
- expense of a slight increase in driver size.
-
- If you are not sure, say N here.
-
config FUJITSU_TABLET
tristate "Fujitsu Tablet Extras"
depends on ACPI
@@ -656,6 +646,18 @@ config ACPI_WMI
It is safe to enable this driver even if your DSDT doesn't define
any ACPI-WMI devices.
+config WMI_BMOF
+ tristate "WMI embedded Binary MOF driver"
+ depends on ACPI_WMI
+ default ACPI_WMI
+ ---help---
+ Say Y here if you want to be able to read a firmware-embedded
+ WMI Binary MOF data. Using this requires userspace tools and may be
+ rather tedious.
+
+ To compile this driver as a module, choose M here: the module will
+ be called wmi-bmof.
+
config MSI_WMI
tristate "MSI WMI extras"
depends on ACPI_WMI
@@ -669,6 +671,14 @@ config MSI_WMI
To compile this driver as a module, choose M here: the module will
be called msi-wmi.
+config PEAQ_WMI
+ tristate "PEAQ 2-in-1 WMI hotkey driver"
+ depends on ACPI_WMI
+ depends on INPUT
+ select INPUT_POLLDEV
+ help
+ Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s.
+
config TOPSTAR_LAPTOP
tristate "Topstar Laptop Extras"
depends on ACPI
@@ -794,6 +804,25 @@ config INTEL_CHT_INT33FE
This driver instantiates i2c-clients for these, so that standard
i2c drivers for these chips can bind to the them.
+config INTEL_INT0002_VGPIO
+ tristate "Intel ACPI INT0002 Virtual GPIO driver"
+ depends on GPIOLIB && ACPI
+ select GPIOLIB_IRQCHIP
+ ---help---
+ Some peripherals on Bay Trail and Cherry Trail platforms signal a
+ Power Management Event (PME) to the Power Management Controller (PMC)
+ to wakeup the system. When this happens software needs to explicitly
+ clear the PME bus 0 status bit in the GPE0a_STS register to avoid an
+ IRQ storm on IRQ 9.
+
+ This is modelled in ACPI through the INT0002 ACPI device, which is
+ called a "Virtual GPIO controller" in ACPI because it defines the
+ event handler to call when the PME triggers through _AEI and _L02
+ methods as would be done for a real GPIO interrupt in ACPI.
+
+ To compile this driver as a module, choose M here: the module will
+ be called intel_int0002_vgpio.
+
config INTEL_HID_EVENT
tristate "INTEL HID Event"
depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 182a3ed6605a..f9e3ae683bbe 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Makefile for linux/drivers/platform/x86
# x86 Platform-Specific Drivers
@@ -35,8 +36,10 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
obj-$(CONFIG_ACPI_WMI) += wmi.o
obj-$(CONFIG_MSI_WMI) += msi-wmi.o
+obj-$(CONFIG_PEAQ_WMI) += peaq-wmi.o
obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
+obj-$(CONFIG_WMI_BMOF) += wmi-bmof.o
# toshiba_acpi must link after wmi to ensure that wmi devices are found
# before toshiba_acpi initializes
@@ -46,6 +49,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o
+obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 79fa5ab3fd00..1be71f956d5c 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -149,6 +149,8 @@ struct event_return_value {
#define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */
#define ACER_WMID3_GDS_WIMAX (1<<7) /* WiMAX */
#define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */
+#define ACER_WMID3_GDS_RFBTN (1<<14) /* RF Button */
+
#define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */
/* Hotkey Customized Setting and Acer Application Status.
@@ -221,6 +223,7 @@ struct hotkey_function_type_aa {
#define ACER_CAP_BRIGHTNESS (1<<3)
#define ACER_CAP_THREEG (1<<4)
#define ACER_CAP_ACCEL (1<<5)
+#define ACER_CAP_RFBTN (1<<6)
#define ACER_CAP_ANY (0xFFFFFFFF)
/*
@@ -700,7 +703,7 @@ struct acpi_buffer *result)
input.length = sizeof(struct wmab_args);
input.pointer = (u8 *)regbuf;
- status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
+ status = wmi_evaluate_method(AMW0_GUID1, 0, 1, &input, result);
return status;
}
@@ -965,7 +968,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out)
u32 tmp = 0;
acpi_status status;
- status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
+ status = wmi_evaluate_method(WMID_GUID1, 0, method_id, &input, &result);
if (ACPI_FAILURE(status))
return status;
@@ -1264,6 +1267,10 @@ static void __init type_aa_dmi_decode(const struct dmi_header *header, void *d)
interface->capability |= ACER_CAP_THREEG;
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH)
interface->capability |= ACER_CAP_BLUETOOTH;
+ if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_RFBTN) {
+ interface->capability |= ACER_CAP_RFBTN;
+ commun_func_bitmap &= ~ACER_WMID3_GDS_RFBTN;
+ }
commun_fn_key_number = type_aa->commun_fn_key_number;
}
@@ -1275,7 +1282,7 @@ static acpi_status __init WMID_set_capabilities(void)
acpi_status status;
u32 devices;
- status = wmi_query_block(WMID_GUID2, 1, &out);
+ status = wmi_query_block(WMID_GUID2, 0, &out);
if (ACPI_FAILURE(status))
return status;
@@ -2018,7 +2025,7 @@ static u32 get_wmid_devices(void)
acpi_status status;
u32 devices = 0;
- status = wmi_query_block(WMID_GUID2, 1, &out);
+ status = wmi_query_block(WMID_GUID2, 0, &out);
if (ACPI_FAILURE(status))
return 0;
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index 2acdb0d6ea89..ea22591ee66f 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -557,7 +557,7 @@ err_out:
}
/* bind fan callbacks to fan device */
-static struct thermal_cooling_device_ops acerhdf_cooling_ops = {
+static const struct thermal_cooling_device_ops acerhdf_cooling_ops = {
.get_max_state = acerhdf_get_max_state,
.get_cur_state = acerhdf_get_cur_state,
.set_cur_state = acerhdf_set_cur_state,
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c
index d6b34923fb4e..4eb8e1a472b2 100644
--- a/drivers/platform/x86/alienware-wmi.c
+++ b/drivers/platform/x86/alienware-wmi.c
@@ -255,12 +255,13 @@ static int parse_rgb(const char *buf, struct platform_zone *zone)
static struct platform_zone *match_zone(struct device_attribute *attr)
{
- int i;
- for (i = 0; i < quirks->num_zones; i++) {
- if ((struct device_attribute *)zone_data[i].attr == attr) {
+ u8 zone;
+
+ for (zone = 0; zone < quirks->num_zones; zone++) {
+ if ((struct device_attribute *)zone_data[zone].attr == attr) {
pr_debug("alienware-wmi: matched zone location: %d\n",
- zone_data[i].location);
- return &zone_data[i];
+ zone_data[zone].location);
+ return &zone_data[zone];
}
}
return NULL;
@@ -303,7 +304,7 @@ static int alienware_update_led(struct platform_zone *zone)
}
pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
- status = wmi_evaluate_method(guid, 1, method_id, &input, NULL);
+ status = wmi_evaluate_method(guid, 0, method_id, &input, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: zone set failure: %u\n", status);
return ACPI_FAILURE(status);
@@ -352,7 +353,7 @@ static int wmax_brightness(int brightness)
};
input.length = (acpi_size) sizeof(args);
input.pointer = &args;
- status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
WMAX_METHOD_BRIGHTNESS, &input, NULL);
if (ACPI_FAILURE(status))
pr_err("alienware-wmi: brightness set failure: %u\n", status);
@@ -420,7 +421,7 @@ static DEVICE_ATTR(lighting_control_state, 0644, show_control_state,
static int alienware_zone_init(struct platform_device *dev)
{
- int i;
+ u8 zone;
char buffer[10];
char *name;
@@ -457,19 +458,19 @@ static int alienware_zone_init(struct platform_device *dev)
if (!zone_data)
return -ENOMEM;
- for (i = 0; i < quirks->num_zones; i++) {
- sprintf(buffer, "zone%02X", i);
+ for (zone = 0; zone < quirks->num_zones; zone++) {
+ sprintf(buffer, "zone%02hhX", zone);
name = kstrdup(buffer, GFP_KERNEL);
if (name == NULL)
return 1;
- sysfs_attr_init(&zone_dev_attrs[i].attr);
- zone_dev_attrs[i].attr.name = name;
- zone_dev_attrs[i].attr.mode = 0644;
- zone_dev_attrs[i].show = zone_show;
- zone_dev_attrs[i].store = zone_set;
- zone_data[i].location = i;
- zone_attrs[i] = &zone_dev_attrs[i].attr;
- zone_data[i].attr = &zone_dev_attrs[i];
+ sysfs_attr_init(&zone_dev_attrs[zone].attr);
+ zone_dev_attrs[zone].attr.name = name;
+ zone_dev_attrs[zone].attr.mode = 0644;
+ zone_dev_attrs[zone].show = zone_show;
+ zone_dev_attrs[zone].store = zone_set;
+ zone_data[zone].location = zone;
+ zone_attrs[zone] = &zone_dev_attrs[zone].attr;
+ zone_data[zone].attr = &zone_dev_attrs[zone];
}
zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr;
zone_attribute_group.attrs = zone_attrs;
@@ -481,12 +482,13 @@ static int alienware_zone_init(struct platform_device *dev)
static void alienware_zone_exit(struct platform_device *dev)
{
+ u8 zone;
+
sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group);
led_classdev_unregister(&global_led);
if (zone_dev_attrs) {
- int i;
- for (i = 0; i < quirks->num_zones; i++)
- kfree(zone_dev_attrs[i].attr.name);
+ for (zone = 0; zone < quirks->num_zones; zone++)
+ kfree(zone_dev_attrs[zone].attr.name);
}
kfree(zone_dev_attrs);
kfree(zone_data);
@@ -506,10 +508,10 @@ static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
if (out_data != NULL) {
output.length = ACPI_ALLOCATE_BUFFER;
output.pointer = NULL;
- status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
command, &input, &output);
} else
- status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 0,
command, &input, NULL);
if (ACPI_SUCCESS(status) && out_data != NULL) {
@@ -604,7 +606,7 @@ static struct attribute *hdmi_attrs[] = {
NULL,
};
-static struct attribute_group hdmi_attribute_group = {
+static const struct attribute_group hdmi_attribute_group = {
.name = "hdmi",
.attrs = hdmi_attrs,
};
@@ -660,7 +662,7 @@ static struct attribute *amplifier_attrs[] = {
NULL,
};
-static struct attribute_group amplifier_attribute_group = {
+static const struct attribute_group amplifier_attribute_group = {
.name = "amplifier",
.attrs = amplifier_attrs,
};
@@ -741,7 +743,7 @@ static struct attribute *deepsleep_attrs[] = {
NULL,
};
-static struct attribute_group deepsleep_attribute_group = {
+static const struct attribute_group deepsleep_attribute_group = {
.name = "deepsleep",
.attrs = deepsleep_attrs,
};
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 6c7d86074b38..48e1541dc8d4 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -299,7 +299,7 @@ static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
union acpi_object *obj;
u32 tmp = 0;
- status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id,
+ status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 0, method_id,
&input, &output);
if (ACPI_FAILURE(status))
@@ -1433,7 +1433,7 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
return ok ? attr->mode : 0;
}
-static struct attribute_group hwmon_attribute_group = {
+static const struct attribute_group hwmon_attribute_group = {
.is_visible = asus_hwmon_sysfs_is_visible,
.attrs = hwmon_attributes
};
@@ -1821,7 +1821,7 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
return ok ? attr->mode : 0;
}
-static struct attribute_group platform_attribute_group = {
+static const struct attribute_group platform_attribute_group = {
.is_visible = asus_sysfs_is_visible,
.attrs = platform_attributes
};
@@ -1946,7 +1946,7 @@ static int show_call(struct seq_file *m, void *data)
acpi_status status;
status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID,
- 1, asus->debug.method_id,
+ 0, asus->debug.method_id,
&input, &output);
if (ACPI_FAILURE(status))
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
index e1c2b6d4b24a..6bcb750e1865 100644
--- a/drivers/platform/x86/compal-laptop.c
+++ b/drivers/platform/x86/compal-laptop.c
@@ -718,7 +718,7 @@ static struct attribute *compal_platform_attrs[] = {
&dev_attr_wake_up_mouse.attr,
NULL
};
-static struct attribute_group compal_platform_attr_group = {
+static const struct attribute_group compal_platform_attr_group = {
.attrs = compal_platform_attrs
};
@@ -805,7 +805,7 @@ static int dmi_check_cb_extra(const struct dmi_system_id *id)
return 1;
}
-static struct dmi_system_id __initdata compal_dmi_table[] = {
+static const struct dmi_system_id compal_dmi_table[] __initconst = {
{
.ident = "FL90/IFL90",
.matches = {
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index ec202094bd50..f42159fd2031 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -1510,7 +1510,11 @@ static void kbd_init(void)
ret = kbd_init_info();
kbd_init_tokens();
- if (kbd_token_bits != 0 || ret == 0)
+ /*
+ * Only supports keyboard backlight when it has at least two modes.
+ */
+ if ((ret == 0 && (kbd_info.levels != 0 || kbd_mode_levels_count >= 2))
+ || kbd_get_valid_token_counts() >= 2)
kbd_led_present = true;
}
diff --git a/drivers/platform/x86/dell-rbtn.c b/drivers/platform/x86/dell-rbtn.c
index dcd9f40a4b18..f3afe778001e 100644
--- a/drivers/platform/x86/dell-rbtn.c
+++ b/drivers/platform/x86/dell-rbtn.c
@@ -110,7 +110,7 @@ static int rbtn_rfkill_set_block(void *data, bool blocked)
return -EINVAL;
}
-static struct rfkill_ops rbtn_ops = {
+static const struct rfkill_ops rbtn_ops = {
.query = rbtn_rfkill_query,
.set_block = rbtn_rfkill_set_block,
};
@@ -221,16 +221,27 @@ static const struct acpi_device_id rbtn_ids[] = {
/*
* This driver can also handle the "DELLABC6" device that
- * appears on the XPS 13 9350, but that device is disabled
- * by the DSDT unless booted with acpi_osi="!Windows 2012"
- * acpi_osi="!Windows 2013". Even if we boot that and bind
- * the driver, we seem to have inconsistent behavior in
- * which NetworkManager can get out of sync with the rfkill
- * state.
+ * appears on the XPS 13 9350, but that device is disabled by
+ * the DSDT unless booted with acpi_osi="!Windows 2012"
+ * acpi_osi="!Windows 2013".
*
- * On the XPS 13 9350 and similar laptops, we're not supposed to
- * use DELLABC6 at all. Instead, we handle the rfkill button
- * via the intel-hid driver.
+ * According to Mario at Dell:
+ *
+ * DELLABC6 is a custom interface that was created solely to
+ * have airplane mode support for Windows 7. For Windows 10
+ * the proper interface is to use that which is handled by
+ * intel-hid. A OEM airplane mode driver is not used.
+ *
+ * Since the kernel doesn't identify as Windows 7 it would be
+ * incorrect to do attempt to use that interface.
+ *
+ * Even if we override _OSI and bind to DELLABC6, we end up with
+ * inconsistent behavior in which userspace can get out of sync
+ * with the rfkill state as it conflicts with events from
+ * intel-hid.
+ *
+ * The upshot is that it is better to just ignore DELLABC6
+ * devices.
*/
{ "", 0 },
diff --git a/drivers/platform/x86/dell-wmi-led.c b/drivers/platform/x86/dell-wmi-led.c
index a0c7e99530ef..5bedaf7f0633 100644
--- a/drivers/platform/x86/dell-wmi-led.c
+++ b/drivers/platform/x86/dell-wmi-led.c
@@ -68,7 +68,7 @@ static int dell_led_perform_fn(u8 length, u8 result_code, u8 device_id,
input.length = sizeof(struct bios_args);
input.pointer = &args;
- status = wmi_evaluate_method(DELL_LED_BIOS_GUID, 1, 1, &input, &output);
+ status = wmi_evaluate_method(DELL_LED_BIOS_GUID, 0, 1, &input, &output);
if (ACPI_FAILURE(status))
return status;
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index 8a64c7967753..28d9f8696081 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -36,6 +36,7 @@
#include <linux/acpi.h>
#include <linux/string.h>
#include <linux/dmi.h>
+#include <linux/wmi.h>
#include <acpi/video.h>
#include "dell-smbios.h"
@@ -47,12 +48,16 @@ MODULE_LICENSE("GPL");
#define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
-static u32 dell_wmi_interface_version;
static bool wmi_requires_smbios_request;
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID);
+struct dell_wmi_priv {
+ struct input_dev *input_dev;
+ u32 interface_version;
+};
+
static int __init dmi_matched(const struct dmi_system_id *dmi)
{
wmi_requires_smbios_request = 1;
@@ -86,7 +91,7 @@ static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {
* notifications (rather than requests for change) or are also sent
* via the keyboard controller so should not be sent again.
*/
-static const struct key_entry dell_wmi_keymap_type_0000[] __initconst = {
+static const struct key_entry dell_wmi_keymap_type_0000[] = {
{ KE_IGNORE, 0x003a, { KEY_CAPSLOCK } },
/* Key code is followed by brightness level */
@@ -207,7 +212,7 @@ struct dell_dmi_results {
};
/* Uninitialized entries here are KEY_RESERVED == 0. */
-static const u16 bios_to_linux_keycode[256] __initconst = {
+static const u16 bios_to_linux_keycode[256] = {
[0] = KEY_MEDIA,
[1] = KEY_NEXTSONG,
[2] = KEY_PLAYPAUSE,
@@ -256,7 +261,7 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
* These are applied if the 0xB2 DMI hotkey table is present and doesn't
* override them.
*/
-static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {
+static const struct key_entry dell_wmi_keymap_type_0010[] = {
/* Fn-lock */
{ KE_IGNORE, 0x151, { KEY_RESERVED } },
@@ -272,7 +277,12 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {
/* RGB keyboard backlight control */
{ KE_IGNORE, 0x154, { KEY_RESERVED } },
- /* Stealth mode toggle */
+ /*
+ * Stealth mode toggle. This will "disable all lights and sounds".
+ * The action is performed by the BIOS and EC; the WMI event is just
+ * a notification. On the XPS 13 9350, this is Fn+F7, and there's
+ * a BIOS setting to enable and disable the hotkey.
+ */
{ KE_IGNORE, 0x155, { KEY_RESERVED } },
/* Rugged magnetic dock attach/detach events */
@@ -289,7 +299,7 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {
/*
* Keymap for WMI events of type 0x0011
*/
-static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = {
+static const struct key_entry dell_wmi_keymap_type_0011[] = {
/* Battery unplugged */
{ KE_IGNORE, 0xfff0, { KEY_RESERVED } },
@@ -304,13 +314,12 @@ static const struct key_entry dell_wmi_keymap_type_0011[] __initconst = {
{ KE_IGNORE, 0x02f6, { KEY_RESERVED } },
};
-static struct input_dev *dell_wmi_input_dev;
-
-static void dell_wmi_process_key(int type, int code)
+static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code)
{
+ struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
const struct key_entry *key;
- key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev,
+ key = sparse_keymap_entry_from_scancode(priv->input_dev,
(type << 16) | code);
if (!key) {
pr_info("Unknown key with type 0x%04x and code 0x%04x pressed\n",
@@ -333,33 +342,19 @@ static void dell_wmi_process_key(int type, int code)
dell_laptop_call_notifier(
DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED, NULL);
- sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
+ sparse_keymap_report_entry(priv->input_dev, key, 1, true);
}
-static void dell_wmi_notify(u32 value, void *context)
+static void dell_wmi_notify(struct wmi_device *wdev,
+ union acpi_object *obj)
{
- struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *obj;
- acpi_status status;
- acpi_size buffer_size;
+ struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
u16 *buffer_entry, *buffer_end;
+ acpi_size buffer_size;
int len, i;
- status = wmi_get_event_data(value, &response);
- if (status != AE_OK) {
- pr_warn("bad event status 0x%x\n", status);
- return;
- }
-
- obj = (union acpi_object *)response.pointer;
- if (!obj) {
- pr_warn("no response\n");
- return;
- }
-
if (obj->type != ACPI_TYPE_BUFFER) {
pr_warn("bad response type %x\n", obj->type);
- kfree(obj);
return;
}
@@ -382,7 +377,7 @@ static void dell_wmi_notify(u32 value, void *context)
* So to prevent reading garbage from buffer we will process only first
* one event on devices with WMI interface version 0.
*/
- if (dell_wmi_interface_version == 0 && buffer_entry < buffer_end)
+ if (priv->interface_version == 0 && buffer_entry < buffer_end)
if (buffer_end > buffer_entry + buffer_entry[0] + 1)
buffer_end = buffer_entry + buffer_entry[0] + 1;
@@ -404,13 +399,14 @@ static void dell_wmi_notify(u32 value, void *context)
switch (buffer_entry[1]) {
case 0x0000: /* One key pressed or event occurred */
if (len > 2)
- dell_wmi_process_key(0x0000, buffer_entry[2]);
+ dell_wmi_process_key(wdev, 0x0000,
+ buffer_entry[2]);
/* Other entries could contain additional information */
break;
case 0x0010: /* Sequence of keys pressed */
case 0x0011: /* Sequence of events occurred */
for (i = 2; i < len; ++i)
- dell_wmi_process_key(buffer_entry[1],
+ dell_wmi_process_key(wdev, buffer_entry[1],
buffer_entry[i]);
break;
default: /* Unknown event */
@@ -423,7 +419,6 @@ static void dell_wmi_notify(u32 value, void *context)
}
- kfree(obj);
}
static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
@@ -437,9 +432,7 @@ static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
return false;
}
-static void __init handle_dmi_entry(const struct dmi_header *dm,
- void *opaque)
-
+static void handle_dmi_entry(const struct dmi_header *dm, void *opaque)
{
struct dell_dmi_results *results = opaque;
struct dell_bios_hotkey_table *table;
@@ -449,6 +442,7 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
if (results->err || results->keymap)
return; /* We already found the hotkey table. */
+ /* The Dell hotkey table is type 0xB2. Scan until we find it. */
if (dm->type != 0xb2)
return;
@@ -509,19 +503,20 @@ static void __init handle_dmi_entry(const struct dmi_header *dm,
results->keymap_size = pos;
}
-static int __init dell_wmi_input_setup(void)
+static int dell_wmi_input_setup(struct wmi_device *wdev)
{
+ struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
struct dell_dmi_results dmi_results = {};
struct key_entry *keymap;
int err, i, pos = 0;
- dell_wmi_input_dev = input_allocate_device();
- if (!dell_wmi_input_dev)
+ priv->input_dev = input_allocate_device();
+ if (!priv->input_dev)
return -ENOMEM;
- dell_wmi_input_dev->name = "Dell WMI hotkeys";
- dell_wmi_input_dev->phys = "wmi/input0";
- dell_wmi_input_dev->id.bustype = BUS_HOST;
+ priv->input_dev->name = "Dell WMI hotkeys";
+ priv->input_dev->id.bustype = BUS_HOST;
+ priv->input_dev->dev.parent = &wdev->dev;
if (dmi_walk(handle_dmi_entry, &dmi_results)) {
/*
@@ -596,7 +591,7 @@ static int __init dell_wmi_input_setup(void)
keymap[pos].type = KE_END;
- err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
+ err = sparse_keymap_setup(priv->input_dev, keymap, NULL);
/*
* Sparse keymap library makes a copy of keymap so we don't need the
* original one that was allocated.
@@ -605,17 +600,24 @@ static int __init dell_wmi_input_setup(void)
if (err)
goto err_free_dev;
- err = input_register_device(dell_wmi_input_dev);
+ err = input_register_device(priv->input_dev);
if (err)
goto err_free_dev;
return 0;
err_free_dev:
- input_free_device(dell_wmi_input_dev);
+ input_free_device(priv->input_dev);
return err;
}
+static void dell_wmi_input_destroy(struct wmi_device *wdev)
+{
+ struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+
+ input_unregister_device(priv->input_dev);
+}
+
/*
* Descriptor buffer is 128 byte long and contains:
*
@@ -625,61 +627,67 @@ static int __init dell_wmi_input_setup(void)
* WMI Interface Version 8 4 <version>
* WMI buffer length 12 4 4096
*/
-static int __init dell_wmi_check_descriptor_buffer(void)
+static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev)
{
- struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *obj;
- acpi_status status;
+ struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
+ union acpi_object *obj = NULL;
+ struct wmi_device *desc_dev;
u32 *buffer;
+ int ret;
- status = wmi_query_block(DELL_DESCRIPTOR_GUID, 0, &out);
- if (ACPI_FAILURE(status)) {
- pr_err("Cannot read Dell descriptor buffer - %d\n", status);
- return status;
+ desc_dev = wmidev_get_other_guid(wdev, DELL_DESCRIPTOR_GUID);
+ if (!desc_dev) {
+ dev_err(&wdev->dev, "Dell WMI descriptor does not exist\n");
+ return -ENODEV;
}
- obj = (union acpi_object *)out.pointer;
+ obj = wmidev_block_query(desc_dev, 0);
if (!obj) {
- pr_err("Dell descriptor buffer is empty\n");
- return -EINVAL;
+ dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
+ ret = -EIO;
+ goto out;
}
if (obj->type != ACPI_TYPE_BUFFER) {
- pr_err("Cannot read Dell descriptor buffer\n");
- kfree(obj);
- return -EINVAL;
+ dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
+ ret = -EINVAL;
+ goto out;
}
if (obj->buffer.length != 128) {
- pr_err("Dell descriptor buffer has invalid length (%d)\n",
+ dev_err(&wdev->dev,
+ "Dell descriptor buffer has invalid length (%d)\n",
obj->buffer.length);
if (obj->buffer.length < 16) {
- kfree(obj);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
}
buffer = (u32 *)obj->buffer.pointer;
if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720)
- pr_warn("Dell descriptor buffer has invalid signature (%*ph)\n",
+ dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%*ph)\n",
8, buffer);
if (buffer[2] != 0 && buffer[2] != 1)
- pr_warn("Dell descriptor buffer has unknown version (%d)\n",
+ dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%d)\n",
buffer[2]);
if (buffer[3] != 4096)
- pr_warn("Dell descriptor buffer has invalid buffer length (%d)\n",
+ dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%d)\n",
buffer[3]);
- dell_wmi_interface_version = buffer[2];
+ priv->interface_version = buffer[2];
+ ret = 0;
- pr_info("Detected Dell WMI interface version %u\n",
- dell_wmi_interface_version);
+ dev_info(&wdev->dev, "Detected Dell WMI interface version %u\n",
+ priv->interface_version);
+out:
kfree(obj);
- return 0;
+ put_device(&desc_dev->dev);
+ return ret;
}
/*
@@ -714,32 +722,47 @@ static int dell_wmi_events_set_enabled(bool enable)
return dell_smbios_error(ret);
}
-static int __init dell_wmi_init(void)
+static int dell_wmi_probe(struct wmi_device *wdev)
{
+ struct dell_wmi_priv *priv;
int err;
- acpi_status status;
- if (!wmi_has_guid(DELL_EVENT_GUID) ||
- !wmi_has_guid(DELL_DESCRIPTOR_GUID)) {
- pr_warn("Dell WMI GUID were not found\n");
- return -ENODEV;
- }
+ priv = devm_kzalloc(
+ &wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ dev_set_drvdata(&wdev->dev, priv);
- err = dell_wmi_check_descriptor_buffer();
+ err = dell_wmi_check_descriptor_buffer(wdev);
if (err)
return err;
- err = dell_wmi_input_setup();
- if (err)
- return err;
+ return dell_wmi_input_setup(wdev);
+}
- status = wmi_install_notify_handler(DELL_EVENT_GUID,
- dell_wmi_notify, NULL);
- if (ACPI_FAILURE(status)) {
- input_unregister_device(dell_wmi_input_dev);
- pr_err("Unable to register notify handler - %d\n", status);
- return -ENODEV;
- }
+static int dell_wmi_remove(struct wmi_device *wdev)
+{
+ dell_wmi_input_destroy(wdev);
+ return 0;
+}
+static const struct wmi_device_id dell_wmi_id_table[] = {
+ { .guid_string = DELL_EVENT_GUID },
+ { },
+};
+
+static struct wmi_driver dell_wmi_driver = {
+ .driver = {
+ .name = "dell-wmi",
+ },
+ .id_table = dell_wmi_id_table,
+ .probe = dell_wmi_probe,
+ .remove = dell_wmi_remove,
+ .notify = dell_wmi_notify,
+};
+
+static int __init dell_wmi_init(void)
+{
+ int err;
dmi_check_system(dell_wmi_smbios_list);
@@ -747,13 +770,11 @@ static int __init dell_wmi_init(void)
err = dell_wmi_events_set_enabled(true);
if (err) {
pr_err("Failed to enable WMI events\n");
- wmi_remove_notify_handler(DELL_EVENT_GUID);
- input_unregister_device(dell_wmi_input_dev);
return err;
}
}
- return 0;
+ return wmi_driver_register(&dell_wmi_driver);
}
module_init(dell_wmi_init);
@@ -761,7 +782,7 @@ static void __exit dell_wmi_exit(void)
{
if (wmi_requires_smbios_request)
dell_wmi_events_set_enabled(false);
- wmi_remove_notify_handler(DELL_EVENT_GUID);
- input_unregister_device(dell_wmi_input_dev);
+
+ wmi_driver_unregister(&dell_wmi_driver);
}
module_exit(dell_wmi_exit);
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 2426399e1e04..5a681962899c 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -445,7 +445,7 @@ static struct attribute *platform_attributes[] = {
NULL
};
-static struct attribute_group platform_attribute_group = {
+static const struct attribute_group platform_attribute_group = {
.attrs = platform_attributes
};
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index 7f49d92914c9..56a8195096a2 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -112,25 +112,8 @@
#define MAX_HOTKEY_RINGBUFFER_SIZE 100
#define RINGBUFFERSIZE 40
-/* Debugging */
-#define FUJLAPTOP_DBG_ERROR 0x0001
-#define FUJLAPTOP_DBG_WARN 0x0002
-#define FUJLAPTOP_DBG_INFO 0x0004
-#define FUJLAPTOP_DBG_TRACE 0x0008
-
-#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
-#define vdbg_printk(a_dbg_level, format, arg...) \
- do { if (dbg_level & a_dbg_level) \
- printk(KERN_DEBUG pr_fmt("%s: " format), __func__, ## arg); \
- } while (0)
-#else
-#define vdbg_printk(a_dbg_level, format, arg...) \
- do { } while (0)
-#endif
-
/* Device controlling the backlight and associated keys */
struct fujitsu_bl {
- acpi_handle acpi_handle;
struct input_dev *input;
char phys[32];
struct backlight_device *bl_device;
@@ -144,8 +127,6 @@ static bool disable_brightness_adjust;
/* Device used to access hotkeys and other features on the laptop */
struct fujitsu_laptop {
- acpi_handle acpi_handle;
- struct acpi_device *dev;
struct input_dev *input;
char phys[32];
struct platform_device *pf_device;
@@ -155,15 +136,12 @@ struct fujitsu_laptop {
int flags_state;
};
-static struct fujitsu_laptop *fujitsu_laptop;
-
-#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
-static u32 dbg_level = 0x03;
-#endif
+static struct acpi_device *fext;
/* Fujitsu ACPI interface function */
-static int call_fext_func(int func, int op, int feature, int state)
+static int call_fext_func(struct acpi_device *device,
+ int func, int op, int feature, int state)
{
union acpi_object params[4] = {
{ .integer.type = ACPI_TYPE_INTEGER, .integer.value = func },
@@ -175,28 +153,30 @@ static int call_fext_func(int func, int op, int feature, int state)
unsigned long long value;
acpi_status status;
- status = acpi_evaluate_integer(fujitsu_laptop->acpi_handle, "FUNC",
- &arg_list, &value);
+ status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list,
+ &value);
if (ACPI_FAILURE(status)) {
- vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate FUNC\n");
+ acpi_handle_err(device->handle, "Failed to evaluate FUNC\n");
return -ENODEV;
}
- vdbg_printk(FUJLAPTOP_DBG_TRACE, "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
- func, op, feature, state, (int)value);
+ acpi_handle_debug(device->handle,
+ "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
+ func, op, feature, state, (int)value);
return value;
}
/* Hardware access for LCD brightness control */
-static int set_lcd_level(int level)
+static int set_lcd_level(struct acpi_device *device, int level)
{
+ struct fujitsu_bl *priv = acpi_driver_data(device);
acpi_status status;
char *method;
switch (use_alt_lcd_levels) {
case -1:
- if (acpi_has_method(fujitsu_bl->acpi_handle, "SBL2"))
+ if (acpi_has_method(device->handle, "SBL2"))
method = "SBL2";
else
method = "SBLL";
@@ -209,74 +189,79 @@ static int set_lcd_level(int level)
break;
}
- vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via %s [%d]\n",
- method, level);
+ acpi_handle_debug(device->handle, "set lcd level via %s [%d]\n", method,
+ level);
- if (level < 0 || level >= fujitsu_bl->max_brightness)
+ if (level < 0 || level >= priv->max_brightness)
return -EINVAL;
- status = acpi_execute_simple_method(fujitsu_bl->acpi_handle, method,
- level);
+ status = acpi_execute_simple_method(device->handle, method, level);
if (ACPI_FAILURE(status)) {
- vdbg_printk(FUJLAPTOP_DBG_ERROR, "Failed to evaluate %s\n",
- method);
+ acpi_handle_err(device->handle, "Failed to evaluate %s\n",
+ method);
return -ENODEV;
}
- fujitsu_bl->brightness_level = level;
+ priv->brightness_level = level;
return 0;
}
-static int get_lcd_level(void)
+static int get_lcd_level(struct acpi_device *device)
{
+ struct fujitsu_bl *priv = acpi_driver_data(device);
unsigned long long state = 0;
acpi_status status = AE_OK;
- vdbg_printk(FUJLAPTOP_DBG_TRACE, "get lcd level via GBLL\n");
+ acpi_handle_debug(device->handle, "get lcd level via GBLL\n");
- status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "GBLL", NULL,
- &state);
+ status = acpi_evaluate_integer(device->handle, "GBLL", NULL, &state);
if (ACPI_FAILURE(status))
return 0;
- fujitsu_bl->brightness_level = state & 0x0fffffff;
+ priv->brightness_level = state & 0x0fffffff;
- return fujitsu_bl->brightness_level;
+ return priv->brightness_level;
}
-static int get_max_brightness(void)
+static int get_max_brightness(struct acpi_device *device)
{
+ struct fujitsu_bl *priv = acpi_driver_data(device);
unsigned long long state = 0;
acpi_status status = AE_OK;
- vdbg_printk(FUJLAPTOP_DBG_TRACE, "get max lcd level via RBLL\n");
+ acpi_handle_debug(device->handle, "get max lcd level via RBLL\n");
- status = acpi_evaluate_integer(fujitsu_bl->acpi_handle, "RBLL", NULL,
- &state);
+ status = acpi_evaluate_integer(device->handle, "RBLL", NULL, &state);
if (ACPI_FAILURE(status))
return -1;
- fujitsu_bl->max_brightness = state;
+ priv->max_brightness = state;
- return fujitsu_bl->max_brightness;
+ return priv->max_brightness;
}
/* Backlight device stuff */
static int bl_get_brightness(struct backlight_device *b)
{
- return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level();
+ struct acpi_device *device = bl_get_data(b);
+
+ return b->props.power == FB_BLANK_POWERDOWN ? 0 : get_lcd_level(device);
}
static int bl_update_status(struct backlight_device *b)
{
- if (b->props.power == FB_BLANK_POWERDOWN)
- call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
- else
- call_fext_func(FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
+ struct acpi_device *device = bl_get_data(b);
+
+ if (fext) {
+ if (b->props.power == FB_BLANK_POWERDOWN)
+ call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x3);
+ else
+ call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x0);
+ }
- return set_lcd_level(b->props.brightness);
+ return set_lcd_level(device, b->props.brightness);
}
static const struct backlight_ops fujitsu_bl_ops = {
@@ -287,9 +272,11 @@ static const struct backlight_ops fujitsu_bl_ops = {
static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- if (!(fujitsu_laptop->flags_supported & FLAG_LID))
+ struct fujitsu_laptop *priv = dev_get_drvdata(dev);
+
+ if (!(priv->flags_supported & FLAG_LID))
return sprintf(buf, "unknown\n");
- if (fujitsu_laptop->flags_state & FLAG_LID)
+ if (priv->flags_state & FLAG_LID)
return sprintf(buf, "open\n");
else
return sprintf(buf, "closed\n");
@@ -298,9 +285,11 @@ static ssize_t lid_show(struct device *dev, struct device_attribute *attr,
static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- if (!(fujitsu_laptop->flags_supported & FLAG_DOCK))
+ struct fujitsu_laptop *priv = dev_get_drvdata(dev);
+
+ if (!(priv->flags_supported & FLAG_DOCK))
return sprintf(buf, "unknown\n");
- if (fujitsu_laptop->flags_state & FLAG_DOCK)
+ if (priv->flags_state & FLAG_DOCK)
return sprintf(buf, "docked\n");
else
return sprintf(buf, "undocked\n");
@@ -309,9 +298,11 @@ static ssize_t dock_show(struct device *dev, struct device_attribute *attr,
static ssize_t radios_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- if (!(fujitsu_laptop->flags_supported & FLAG_RFKILL))
+ struct fujitsu_laptop *priv = dev_get_drvdata(dev);
+
+ if (!(priv->flags_supported & FLAG_RFKILL))
return sprintf(buf, "unknown\n");
- if (fujitsu_laptop->flags_state & FLAG_RFKILL)
+ if (priv->flags_state & FLAG_RFKILL)
return sprintf(buf, "on\n");
else
return sprintf(buf, "killed\n");
@@ -328,7 +319,7 @@ static struct attribute *fujitsu_pf_attributes[] = {
NULL
};
-static struct attribute_group fujitsu_pf_attribute_group = {
+static const struct attribute_group fujitsu_pf_attribute_group = {
.attrs = fujitsu_pf_attributes
};
@@ -348,89 +339,76 @@ static const struct key_entry keymap_backlight[] = {
static int acpi_fujitsu_bl_input_setup(struct acpi_device *device)
{
- struct fujitsu_bl *fujitsu_bl = acpi_driver_data(device);
+ struct fujitsu_bl *priv = acpi_driver_data(device);
int ret;
- fujitsu_bl->input = devm_input_allocate_device(&device->dev);
- if (!fujitsu_bl->input)
+ priv->input = devm_input_allocate_device(&device->dev);
+ if (!priv->input)
return -ENOMEM;
- snprintf(fujitsu_bl->phys, sizeof(fujitsu_bl->phys),
- "%s/video/input0", acpi_device_hid(device));
+ snprintf(priv->phys, sizeof(priv->phys), "%s/video/input0",
+ acpi_device_hid(device));
- fujitsu_bl->input->name = acpi_device_name(device);
- fujitsu_bl->input->phys = fujitsu_bl->phys;
- fujitsu_bl->input->id.bustype = BUS_HOST;
- fujitsu_bl->input->id.product = 0x06;
+ priv->input->name = acpi_device_name(device);
+ priv->input->phys = priv->phys;
+ priv->input->id.bustype = BUS_HOST;
+ priv->input->id.product = 0x06;
- ret = sparse_keymap_setup(fujitsu_bl->input, keymap_backlight, NULL);
+ ret = sparse_keymap_setup(priv->input, keymap_backlight, NULL);
if (ret)
return ret;
- return input_register_device(fujitsu_bl->input);
+ return input_register_device(priv->input);
}
static int fujitsu_backlight_register(struct acpi_device *device)
{
+ struct fujitsu_bl *priv = acpi_driver_data(device);
const struct backlight_properties props = {
- .brightness = fujitsu_bl->brightness_level,
- .max_brightness = fujitsu_bl->max_brightness - 1,
+ .brightness = priv->brightness_level,
+ .max_brightness = priv->max_brightness - 1,
.type = BACKLIGHT_PLATFORM
};
struct backlight_device *bd;
bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop",
- &device->dev, NULL,
+ &device->dev, device,
&fujitsu_bl_ops, &props);
if (IS_ERR(bd))
return PTR_ERR(bd);
- fujitsu_bl->bl_device = bd;
+ priv->bl_device = bd;
return 0;
}
static int acpi_fujitsu_bl_add(struct acpi_device *device)
{
- int state = 0;
+ struct fujitsu_bl *priv;
int error;
if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return -ENODEV;
- if (!device)
- return -EINVAL;
+ priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
- fujitsu_bl->acpi_handle = device->handle;
- sprintf(acpi_device_name(device), "%s", ACPI_FUJITSU_BL_DEVICE_NAME);
- sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
- device->driver_data = fujitsu_bl;
+ fujitsu_bl = priv;
+ strcpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
+ device->driver_data = priv;
error = acpi_fujitsu_bl_input_setup(device);
if (error)
return error;
- error = acpi_bus_update_power(fujitsu_bl->acpi_handle, &state);
- if (error) {
- pr_err("Error reading power state\n");
- return error;
- }
-
- pr_info("ACPI: %s [%s] (%s)\n",
- acpi_device_name(device), acpi_device_bid(device),
- !device->power.state ? "on" : "off");
-
- if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
- vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
- if (ACPI_FAILURE
- (acpi_evaluate_object
- (device->handle, METHOD_NAME__INI, NULL, NULL)))
- pr_err("_INI Method failed\n");
- }
+ pr_info("ACPI: %s [%s]\n",
+ acpi_device_name(device), acpi_device_bid(device));
- if (get_max_brightness() <= 0)
- fujitsu_bl->max_brightness = FUJITSU_LCD_N_LEVELS;
- get_lcd_level();
+ if (get_max_brightness(device) <= 0)
+ priv->max_brightness = FUJITSU_LCD_N_LEVELS;
+ get_lcd_level(device);
error = fujitsu_backlight_register(device);
if (error)
@@ -443,32 +421,30 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device)
static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
{
- struct input_dev *input;
+ struct fujitsu_bl *priv = acpi_driver_data(device);
int oldb, newb;
- input = fujitsu_bl->input;
-
if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
- vdbg_printk(FUJLAPTOP_DBG_WARN,
- "unsupported event [0x%x]\n", event);
- sparse_keymap_report_event(input, -1, 1, true);
+ acpi_handle_info(device->handle, "unsupported event [0x%x]\n",
+ event);
+ sparse_keymap_report_event(priv->input, -1, 1, true);
return;
}
- oldb = fujitsu_bl->brightness_level;
- get_lcd_level();
- newb = fujitsu_bl->brightness_level;
+ oldb = priv->brightness_level;
+ get_lcd_level(device);
+ newb = priv->brightness_level;
- vdbg_printk(FUJLAPTOP_DBG_TRACE, "brightness button event [%i -> %i]\n",
- oldb, newb);
+ acpi_handle_debug(device->handle,
+ "brightness button event [%i -> %i]\n", oldb, newb);
if (oldb == newb)
return;
if (!disable_brightness_adjust)
- set_lcd_level(newb);
+ set_lcd_level(device, newb);
- sparse_keymap_report_event(input, oldb < newb, 1, true);
+ sparse_keymap_report_event(priv->input, oldb < newb, 1, true);
}
/* ACPI device for hotkey handling */
@@ -541,42 +517,44 @@ static const struct dmi_system_id fujitsu_laptop_dmi_table[] = {
static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device)
{
- struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device);
+ struct fujitsu_laptop *priv = acpi_driver_data(device);
int ret;
- fujitsu_laptop->input = devm_input_allocate_device(&device->dev);
- if (!fujitsu_laptop->input)
+ priv->input = devm_input_allocate_device(&device->dev);
+ if (!priv->input)
return -ENOMEM;
- snprintf(fujitsu_laptop->phys, sizeof(fujitsu_laptop->phys),
- "%s/video/input0", acpi_device_hid(device));
+ snprintf(priv->phys, sizeof(priv->phys), "%s/input0",
+ acpi_device_hid(device));
- fujitsu_laptop->input->name = acpi_device_name(device);
- fujitsu_laptop->input->phys = fujitsu_laptop->phys;
- fujitsu_laptop->input->id.bustype = BUS_HOST;
- fujitsu_laptop->input->id.product = 0x06;
+ priv->input->name = acpi_device_name(device);
+ priv->input->phys = priv->phys;
+ priv->input->id.bustype = BUS_HOST;
dmi_check_system(fujitsu_laptop_dmi_table);
- ret = sparse_keymap_setup(fujitsu_laptop->input, keymap, NULL);
+ ret = sparse_keymap_setup(priv->input, keymap, NULL);
if (ret)
return ret;
- return input_register_device(fujitsu_laptop->input);
+ return input_register_device(priv->input);
}
-static int fujitsu_laptop_platform_add(void)
+static int fujitsu_laptop_platform_add(struct acpi_device *device)
{
+ struct fujitsu_laptop *priv = acpi_driver_data(device);
int ret;
- fujitsu_laptop->pf_device = platform_device_alloc("fujitsu-laptop", -1);
- if (!fujitsu_laptop->pf_device)
+ priv->pf_device = platform_device_alloc("fujitsu-laptop", -1);
+ if (!priv->pf_device)
return -ENOMEM;
- ret = platform_device_add(fujitsu_laptop->pf_device);
+ platform_set_drvdata(priv->pf_device, priv);
+
+ ret = platform_device_add(priv->pf_device);
if (ret)
goto err_put_platform_device;
- ret = sysfs_create_group(&fujitsu_laptop->pf_device->dev.kobj,
+ ret = sysfs_create_group(&priv->pf_device->dev.kobj,
&fujitsu_pf_attribute_group);
if (ret)
goto err_del_platform_device;
@@ -584,23 +562,26 @@ static int fujitsu_laptop_platform_add(void)
return 0;
err_del_platform_device:
- platform_device_del(fujitsu_laptop->pf_device);
+ platform_device_del(priv->pf_device);
err_put_platform_device:
- platform_device_put(fujitsu_laptop->pf_device);
+ platform_device_put(priv->pf_device);
return ret;
}
-static void fujitsu_laptop_platform_remove(void)
+static void fujitsu_laptop_platform_remove(struct acpi_device *device)
{
- sysfs_remove_group(&fujitsu_laptop->pf_device->dev.kobj,
+ struct fujitsu_laptop *priv = acpi_driver_data(device);
+
+ sysfs_remove_group(&priv->pf_device->dev.kobj,
&fujitsu_pf_attribute_group);
- platform_device_unregister(fujitsu_laptop->pf_device);
+ platform_device_unregister(priv->pf_device);
}
static int logolamp_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
+ struct acpi_device *device = to_acpi_device(cdev->dev->parent);
int poweron = FUNC_LED_ON, always = FUNC_LED_ON;
int ret;
@@ -610,132 +591,134 @@ static int logolamp_set(struct led_classdev *cdev,
if (brightness < LED_FULL)
always = FUNC_LED_OFF;
- ret = call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
+ ret = call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron);
if (ret < 0)
return ret;
- return call_fext_func(FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
+ return call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always);
}
static enum led_brightness logolamp_get(struct led_classdev *cdev)
{
+ struct acpi_device *device = to_acpi_device(cdev->dev->parent);
int ret;
- ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
+ ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0);
if (ret == FUNC_LED_ON)
return LED_FULL;
- ret = call_fext_func(FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
+ ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0);
if (ret == FUNC_LED_ON)
return LED_HALF;
return LED_OFF;
}
-static struct led_classdev logolamp_led = {
- .name = "fujitsu::logolamp",
- .brightness_set_blocking = logolamp_set,
- .brightness_get = logolamp_get
-};
-
static int kblamps_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
+ struct acpi_device *device = to_acpi_device(cdev->dev->parent);
+
if (brightness >= LED_FULL)
- return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
+ return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
FUNC_LED_ON);
else
- return call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
+ return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS,
FUNC_LED_OFF);
}
static enum led_brightness kblamps_get(struct led_classdev *cdev)
{
+ struct acpi_device *device = to_acpi_device(cdev->dev->parent);
enum led_brightness brightness = LED_OFF;
- if (call_fext_func(FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
+ if (call_fext_func(device,
+ FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON)
brightness = LED_FULL;
return brightness;
}
-static struct led_classdev kblamps_led = {
- .name = "fujitsu::kblamps",
- .brightness_set_blocking = kblamps_set,
- .brightness_get = kblamps_get
-};
-
static int radio_led_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
+ struct acpi_device *device = to_acpi_device(cdev->dev->parent);
+
if (brightness >= LED_FULL)
- return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON,
+ return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
RADIO_LED_ON);
else
- return call_fext_func(FUNC_FLAGS, 0x5, RADIO_LED_ON, 0x0);
+ return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON,
+ 0x0);
}
static enum led_brightness radio_led_get(struct led_classdev *cdev)
{
+ struct acpi_device *device = to_acpi_device(cdev->dev->parent);
enum led_brightness brightness = LED_OFF;
- if (call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
+ if (call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON)
brightness = LED_FULL;
return brightness;
}
-static struct led_classdev radio_led = {
- .name = "fujitsu::radio_led",
- .brightness_set_blocking = radio_led_set,
- .brightness_get = radio_led_get,
- .default_trigger = "rfkill-any"
-};
-
static int eco_led_set(struct led_classdev *cdev,
enum led_brightness brightness)
{
+ struct acpi_device *device = to_acpi_device(cdev->dev->parent);
int curr;
- curr = call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0);
+ curr = call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0);
if (brightness >= LED_FULL)
- return call_fext_func(FUNC_LEDS, 0x1, ECO_LED,
+ return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
curr | ECO_LED_ON);
else
- return call_fext_func(FUNC_LEDS, 0x1, ECO_LED,
+ return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED,
curr & ~ECO_LED_ON);
}
static enum led_brightness eco_led_get(struct led_classdev *cdev)
{
+ struct acpi_device *device = to_acpi_device(cdev->dev->parent);
enum led_brightness brightness = LED_OFF;
- if (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
+ if (call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON)
brightness = LED_FULL;
return brightness;
}
-static struct led_classdev eco_led = {
- .name = "fujitsu::eco_led",
- .brightness_set_blocking = eco_led_set,
- .brightness_get = eco_led_get
-};
-
static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
{
+ struct led_classdev *led;
int result;
- if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
- result = devm_led_classdev_register(&device->dev,
- &logolamp_led);
+ if (call_fext_func(device,
+ FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
+ led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->name = "fujitsu::logolamp";
+ led->brightness_set_blocking = logolamp_set;
+ led->brightness_get = logolamp_get;
+ result = devm_led_classdev_register(&device->dev, led);
if (result)
return result;
}
- if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
- (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
- result = devm_led_classdev_register(&device->dev, &kblamps_led);
+ if ((call_fext_func(device,
+ FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) &&
+ (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) {
+ led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->name = "fujitsu::kblamps";
+ led->brightness_set_blocking = kblamps_set;
+ led->brightness_get = kblamps_get;
+ result = devm_led_classdev_register(&device->dev, led);
if (result)
return result;
}
@@ -746,8 +729,16 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
* to also have an RF LED. Therefore use bit 24 as an indicator
* that an RF LED is present.
*/
- if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
- result = devm_led_classdev_register(&device->dev, &radio_led);
+ if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
+ led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->name = "fujitsu::radio_led";
+ led->brightness_set_blocking = radio_led_set;
+ led->brightness_get = radio_led_get;
+ led->default_trigger = "rfkill-any";
+ result = devm_led_classdev_register(&device->dev, led);
if (result)
return result;
}
@@ -757,9 +748,17 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
* bit 14 seems to indicate presence of said led as well.
* Confirm by testing the status.
*/
- if ((call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
- (call_fext_func(FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
- result = devm_led_classdev_register(&device->dev, &eco_led);
+ if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) &&
+ (call_fext_func(device,
+ FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) {
+ led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
+ if (!led)
+ return -ENOMEM;
+
+ led->name = "fujitsu::eco_led";
+ led->brightness_set_blocking = eco_led_set;
+ led->brightness_get = eco_led_get;
+ result = devm_led_classdev_register(&device->dev, led);
if (result)
return result;
}
@@ -769,23 +768,25 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
static int acpi_fujitsu_laptop_add(struct acpi_device *device)
{
- int state = 0;
+ struct fujitsu_laptop *priv;
int error;
int i;
- if (!device)
- return -EINVAL;
+ priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
- fujitsu_laptop->acpi_handle = device->handle;
- sprintf(acpi_device_name(device), "%s",
- ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
- sprintf(acpi_device_class(device), "%s", ACPI_FUJITSU_CLASS);
- device->driver_data = fujitsu_laptop;
+ WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended.");
+ fext = device;
+
+ strcpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME);
+ strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
+ device->driver_data = priv;
/* kfifo */
- spin_lock_init(&fujitsu_laptop->fifo_lock);
- error = kfifo_alloc(&fujitsu_laptop->fifo, RINGBUFFERSIZE * sizeof(int),
- GFP_KERNEL);
+ spin_lock_init(&priv->fifo_lock);
+ error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
+ GFP_KERNEL);
if (error) {
pr_err("kfifo_alloc failed\n");
goto err_stop;
@@ -795,51 +796,36 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
if (error)
goto err_free_fifo;
- error = acpi_bus_update_power(fujitsu_laptop->acpi_handle, &state);
- if (error) {
- pr_err("Error reading power state\n");
- goto err_free_fifo;
- }
-
- pr_info("ACPI: %s [%s] (%s)\n",
- acpi_device_name(device), acpi_device_bid(device),
- !device->power.state ? "on" : "off");
-
- fujitsu_laptop->dev = device;
-
- if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
- vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
- if (ACPI_FAILURE
- (acpi_evaluate_object
- (device->handle, METHOD_NAME__INI, NULL, NULL)))
- pr_err("_INI Method failed\n");
- }
+ pr_info("ACPI: %s [%s]\n",
+ acpi_device_name(device), acpi_device_bid(device));
i = 0;
- while (call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
+ while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0
&& (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
; /* No action, result is discarded */
- vdbg_printk(FUJLAPTOP_DBG_INFO, "Discarded %i ringbuffer entries\n", i);
+ acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n",
+ i);
- fujitsu_laptop->flags_supported =
- call_fext_func(FUNC_FLAGS, 0x0, 0x0, 0x0);
+ priv->flags_supported = call_fext_func(device, FUNC_FLAGS, 0x0, 0x0,
+ 0x0);
/* Make sure our bitmask of supported functions is cleared if the
RFKILL function block is not implemented, like on the S7020. */
- if (fujitsu_laptop->flags_supported == UNSUPPORTED_CMD)
- fujitsu_laptop->flags_supported = 0;
+ if (priv->flags_supported == UNSUPPORTED_CMD)
+ priv->flags_supported = 0;
- if (fujitsu_laptop->flags_supported)
- fujitsu_laptop->flags_state =
- call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0);
+ if (priv->flags_supported)
+ priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
+ 0x0);
/* Suspect this is a keymap of the application panel, print it */
- pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0));
+ acpi_handle_info(device->handle, "BTNI: [0x%x]\n",
+ call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0));
/* Sync backlight power status */
- if (fujitsu_bl->bl_device &&
+ if (fujitsu_bl && fujitsu_bl->bl_device &&
acpi_video_get_backlight_type() == acpi_backlight_vendor) {
- if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
+ if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
else
fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
@@ -849,103 +835,100 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
if (error)
goto err_free_fifo;
- error = fujitsu_laptop_platform_add();
+ error = fujitsu_laptop_platform_add(device);
if (error)
goto err_free_fifo;
return 0;
err_free_fifo:
- kfifo_free(&fujitsu_laptop->fifo);
+ kfifo_free(&priv->fifo);
err_stop:
return error;
}
static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
{
- struct fujitsu_laptop *fujitsu_laptop = acpi_driver_data(device);
+ struct fujitsu_laptop *priv = acpi_driver_data(device);
- fujitsu_laptop_platform_remove();
+ fujitsu_laptop_platform_remove(device);
- kfifo_free(&fujitsu_laptop->fifo);
+ kfifo_free(&priv->fifo);
return 0;
}
-static void acpi_fujitsu_laptop_press(int scancode)
+static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
{
- struct input_dev *input = fujitsu_laptop->input;
+ struct fujitsu_laptop *priv = acpi_driver_data(device);
int status;
- status = kfifo_in_locked(&fujitsu_laptop->fifo,
- (unsigned char *)&scancode, sizeof(scancode),
- &fujitsu_laptop->fifo_lock);
+ status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
+ sizeof(scancode), &priv->fifo_lock);
if (status != sizeof(scancode)) {
- vdbg_printk(FUJLAPTOP_DBG_WARN,
- "Could not push scancode [0x%x]\n", scancode);
+ dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n",
+ scancode);
return;
}
- sparse_keymap_report_event(input, scancode, 1, false);
- vdbg_printk(FUJLAPTOP_DBG_TRACE,
- "Push scancode into ringbuffer [0x%x]\n", scancode);
+ sparse_keymap_report_event(priv->input, scancode, 1, false);
+ dev_dbg(&priv->input->dev, "Push scancode into ringbuffer [0x%x]\n",
+ scancode);
}
-static void acpi_fujitsu_laptop_release(void)
+static void acpi_fujitsu_laptop_release(struct acpi_device *device)
{
- struct input_dev *input = fujitsu_laptop->input;
+ struct fujitsu_laptop *priv = acpi_driver_data(device);
int scancode, status;
while (true) {
- status = kfifo_out_locked(&fujitsu_laptop->fifo,
+ status = kfifo_out_locked(&priv->fifo,
(unsigned char *)&scancode,
- sizeof(scancode),
- &fujitsu_laptop->fifo_lock);
+ sizeof(scancode), &priv->fifo_lock);
if (status != sizeof(scancode))
return;
- sparse_keymap_report_event(input, scancode, 0, false);
- vdbg_printk(FUJLAPTOP_DBG_TRACE,
- "Pop scancode from ringbuffer [0x%x]\n", scancode);
+ sparse_keymap_report_event(priv->input, scancode, 0, false);
+ dev_dbg(&priv->input->dev,
+ "Pop scancode from ringbuffer [0x%x]\n", scancode);
}
}
static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
{
- struct input_dev *input;
+ struct fujitsu_laptop *priv = acpi_driver_data(device);
int scancode, i = 0;
unsigned int irb;
- input = fujitsu_laptop->input;
-
if (event != ACPI_FUJITSU_NOTIFY_CODE1) {
- vdbg_printk(FUJLAPTOP_DBG_WARN,
- "Unsupported event [0x%x]\n", event);
- sparse_keymap_report_event(input, -1, 1, true);
+ acpi_handle_info(device->handle, "Unsupported event [0x%x]\n",
+ event);
+ sparse_keymap_report_event(priv->input, -1, 1, true);
return;
}
- if (fujitsu_laptop->flags_supported)
- fujitsu_laptop->flags_state =
- call_fext_func(FUNC_FLAGS, 0x4, 0x0, 0x0);
+ if (priv->flags_supported)
+ priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0,
+ 0x0);
- while ((irb = call_fext_func(FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&
+ while ((irb = call_fext_func(device,
+ FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 &&
i++ < MAX_HOTKEY_RINGBUFFER_SIZE) {
scancode = irb & 0x4ff;
- if (sparse_keymap_entry_from_scancode(input, scancode))
- acpi_fujitsu_laptop_press(scancode);
+ if (sparse_keymap_entry_from_scancode(priv->input, scancode))
+ acpi_fujitsu_laptop_press(device, scancode);
else if (scancode == 0)
- acpi_fujitsu_laptop_release();
+ acpi_fujitsu_laptop_release(device);
else
- vdbg_printk(FUJLAPTOP_DBG_WARN,
- "Unknown GIRB result [%x]\n", irb);
+ acpi_handle_info(device->handle,
+ "Unknown GIRB result [%x]\n", irb);
}
/* On some models (first seen on the Skylake-based Lifebook
* E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
* handled in software; its state is queried using FUNC_FLAGS
*/
- if ((fujitsu_laptop->flags_supported & BIT(26)) &&
- (call_fext_func(FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26)))
- sparse_keymap_report_event(input, BIT(26), 1, true);
+ if ((priv->flags_supported & BIT(26)) &&
+ (call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26)))
+ sparse_keymap_report_event(priv->input, BIT(26), 1, true);
}
/* Initialization */
@@ -992,16 +975,9 @@ static int __init fujitsu_init(void)
{
int ret;
- if (acpi_disabled)
- return -ENODEV;
-
- fujitsu_bl = kzalloc(sizeof(struct fujitsu_bl), GFP_KERNEL);
- if (!fujitsu_bl)
- return -ENOMEM;
-
ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver);
if (ret)
- goto err_free_fujitsu_bl;
+ return ret;
/* Register platform stuff */
@@ -1011,28 +987,18 @@ static int __init fujitsu_init(void)
/* Register laptop driver */
- fujitsu_laptop = kzalloc(sizeof(struct fujitsu_laptop), GFP_KERNEL);
- if (!fujitsu_laptop) {
- ret = -ENOMEM;
- goto err_unregister_platform_driver;
- }
-
ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver);
if (ret)
- goto err_free_fujitsu_laptop;
+ goto err_unregister_platform_driver;
pr_info("driver " FUJITSU_DRIVER_VERSION " successfully loaded\n");
return 0;
-err_free_fujitsu_laptop:
- kfree(fujitsu_laptop);
err_unregister_platform_driver:
platform_driver_unregister(&fujitsu_pf_driver);
err_unregister_acpi:
acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
-err_free_fujitsu_bl:
- kfree(fujitsu_bl);
return ret;
}
@@ -1041,14 +1007,10 @@ static void __exit fujitsu_cleanup(void)
{
acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver);
- kfree(fujitsu_laptop);
-
platform_driver_unregister(&fujitsu_pf_driver);
acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver);
- kfree(fujitsu_bl);
-
pr_info("driver unloaded\n");
}
@@ -1059,10 +1021,6 @@ module_param(use_alt_lcd_levels, int, 0644);
MODULE_PARM_DESC(use_alt_lcd_levels, "Interface used for setting LCD brightness level (-1 = auto, 0 = force SBLL, 1 = force SBL2)");
module_param(disable_brightness_adjust, bool, 0644);
MODULE_PARM_DESC(disable_brightness_adjust, "Disable LCD brightness adjustment");
-#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
-module_param_named(debug, dbg_level, uint, 0644);
-MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
-#endif
MODULE_AUTHOR("Jonathan Woithe, Peter Gruber, Tony Vroon");
MODULE_DESCRIPTION("Fujitsu laptop extras support");
diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c
index 458e6c948c11..c26baf77938e 100644
--- a/drivers/platform/x86/hdaps.c
+++ b/drivers/platform/x86/hdaps.c
@@ -514,7 +514,7 @@ static int __init hdaps_dmi_match_invert(const struct dmi_system_id *id)
"ThinkPad T42p", so the order of the entries matters.
If your ThinkPad is not recognized, please update to latest
BIOS. This is especially the case for some R52 ThinkPads. */
-static struct dmi_system_id __initdata hdaps_whitelist[] = {
+static const struct dmi_system_id hdaps_whitelist[] __initconst = {
HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p", HDAPS_BOTH_AXES),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"),
HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"),
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index 0df4209648d1..b4ed3dc983d5 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -107,13 +107,6 @@ enum hp_wmi_hardware_mask {
HPWMI_TABLET_MASK = 0x04,
};
-#define BIOS_ARGS_INIT(write, ctype, size) \
- (struct bios_args) { .signature = 0x55434553, \
- .command = (write) ? 0x2 : 0x1, \
- .commandtype = (ctype), \
- .datasize = (size), \
- .data = 0 }
-
struct bios_return {
u32 sigpass;
u32 return_code;
@@ -188,6 +181,22 @@ struct rfkill2_device {
static int rfkill2_count;
static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
+/* map output size to the corresponding WMI method id */
+static inline int encode_outsize_for_pvsz(int outsize)
+{
+ if (outsize > 4096)
+ return -EINVAL;
+ if (outsize > 1024)
+ return 5;
+ if (outsize > 128)
+ return 4;
+ if (outsize > 4)
+ return 3;
+ if (outsize > 0)
+ return 2;
+ return 1;
+}
+
/*
* hp_wmi_perform_query
*
@@ -211,6 +220,7 @@ static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
void *buffer, int insize, int outsize)
{
+ int mid;
struct bios_return *bios_return;
int actual_outsize;
union acpi_object *obj;
@@ -225,11 +235,15 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
int ret = 0;
+ mid = encode_outsize_for_pvsz(outsize);
+ if (WARN_ON(mid < 0))
+ return mid;
+
if (WARN_ON(insize > sizeof(args.data)))
return -EINVAL;
memcpy(&args.data, buffer, insize);
- wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
+ wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output);
obj = output.pointer;
diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c
index c62e5e11ca4b..18d55cee5bcd 100644
--- a/drivers/platform/x86/ibm_rtl.c
+++ b/drivers/platform/x86/ibm_rtl.c
@@ -103,7 +103,7 @@ static void rtl_port_unmap(void __iomem *addr)
static int ibm_rtl_write(u8 value)
{
int ret = 0, count = 0;
- static u32 cmd_port_val;
+ u32 cmd_port_val;
RTL_DEBUG("%s(%d)\n", __func__, value);
@@ -227,7 +227,7 @@ static void rtl_teardown_sysfs(void) {
}
-static struct dmi_system_id __initdata ibm_rtl_dmi_table[] = {
+static const struct dmi_system_id ibm_rtl_dmi_table[] __initconst = {
{ \
.matches = { \
DMI_MATCH(DMI_SYS_VENDOR, "IBM"), \
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 24ca9fbe31cc..fe98d4ac0df3 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -42,6 +42,8 @@
#define IDEAPAD_RFKILL_DEV_NUM (3)
+#define BM_CONSERVATION_BIT (5)
+
#define CFG_BT_BIT (16)
#define CFG_3G_BIT (17)
#define CFG_WIFI_BIT (18)
@@ -55,6 +57,11 @@ static const char *const ideapad_wmi_fnesc_events[] = {
#endif
enum {
+ BMCMD_CONSERVATION_ON = 3,
+ BMCMD_CONSERVATION_OFF = 5,
+};
+
+enum {
VPCCMD_R_VPC1 = 0x10,
VPCCMD_R_BL_MAX,
VPCCMD_R_BL,
@@ -123,6 +130,23 @@ static int read_method_int(acpi_handle handle, const char *method, int *val)
}
}
+static int method_gbmd(acpi_handle handle, unsigned long *ret)
+{
+ int result, val;
+
+ result = read_method_int(handle, "GBMD", &val);
+ *ret = val;
+ return result;
+}
+
+static int method_sbmc(acpi_handle handle, int cmd)
+{
+ acpi_status status;
+
+ status = acpi_execute_simple_method(handle, "SBMC", cmd);
+ return ACPI_FAILURE(status) ? -1 : 0;
+}
+
static int method_vpcr(acpi_handle handle, int cmd, int *ret)
{
acpi_status status;
@@ -250,6 +274,13 @@ static int debugfs_status_show(struct seq_file *s, void *data)
if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
seq_printf(s, "Camera status:\t%s(%lu)\n",
value ? "On" : "Off", value);
+ seq_puts(s, "=====================\n");
+
+ if (!method_gbmd(priv->adev->handle, &value)) {
+ seq_printf(s, "Conservation mode:\t%s(%lu)\n",
+ test_bit(BM_CONSERVATION_BIT, &value) ? "On" : "Off",
+ value);
+ }
return 0;
}
@@ -423,9 +454,78 @@ static ssize_t store_ideapad_fan(struct device *dev,
static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
+static ssize_t touchpad_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+ unsigned long result;
+
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &result))
+ return sprintf(buf, "-1\n");
+ return sprintf(buf, "%lu\n", result);
+}
+
+/* Switch to RO for now: It might be revisited in the future */
+static ssize_t __maybe_unused touchpad_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+ bool state;
+ int ret;
+
+ ret = kstrtobool(buf, &state);
+ if (ret)
+ return ret;
+
+ ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, state);
+ if (ret < 0)
+ return -EIO;
+ return count;
+}
+
+static DEVICE_ATTR_RO(touchpad);
+
+static ssize_t conservation_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+ unsigned long result;
+
+ if (method_gbmd(priv->adev->handle, &result))
+ return sprintf(buf, "-1\n");
+ return sprintf(buf, "%u\n", test_bit(BM_CONSERVATION_BIT, &result));
+}
+
+static ssize_t conservation_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+ bool state;
+ int ret;
+
+ ret = kstrtobool(buf, &state);
+ if (ret)
+ return ret;
+
+ ret = method_sbmc(priv->adev->handle, state ?
+ BMCMD_CONSERVATION_ON :
+ BMCMD_CONSERVATION_OFF);
+ if (ret < 0)
+ return -EIO;
+ return count;
+}
+
+static DEVICE_ATTR_RW(conservation_mode);
+
static struct attribute *ideapad_attributes[] = {
&dev_attr_camera_power.attr,
&dev_attr_fan_mode.attr,
+ &dev_attr_touchpad.attr,
+ &dev_attr_conservation_mode.attr,
NULL
};
@@ -443,6 +543,9 @@ static umode_t ideapad_is_visible(struct kobject *kobj,
unsigned long value;
supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
&value);
+ } else if (attr == &dev_attr_conservation_mode.attr) {
+ supported = acpi_has_method(priv->adev->handle, "GBMD") &&
+ acpi_has_method(priv->adev->handle, "SBMC");
} else
supported = true;
@@ -478,7 +581,7 @@ static int ideapad_rfk_set(void *data, bool blocked)
return write_ec_cmd(priv->priv->adev->handle, opcode, !blocked);
}
-static struct rfkill_ops ideapad_rfk_ops = {
+static const struct rfkill_ops ideapad_rfk_ops = {
.set_block = ideapad_rfk_set,
};
@@ -810,7 +913,6 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
case 8:
case 7:
case 6:
- case 1:
ideapad_input_report(priv, vpc_bit);
break;
case 5:
@@ -828,6 +930,13 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
case 0:
ideapad_check_special_buttons(priv);
break;
+ case 1:
+ /* Some IdeaPads report event 1 every ~20
+ * seconds while on battery power; some
+ * report this when changing to/from tablet
+ * mode. Squelch this event.
+ */
+ break;
default:
pr_info("Unknown event: %lu\n", vpc_bit);
}
@@ -869,17 +978,94 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
},
},
{
+ .ident = "Lenovo V310-14IKB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-14IKB"),
+ },
+ },
+ {
+ .ident = "Lenovo V310-14ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-14ISK"),
+ },
+ },
+ {
+ .ident = "Lenovo V310-15IKB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15IKB"),
+ },
+ },
+ {
.ident = "Lenovo V310-15ISK",
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15ISK"),
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15ISK"),
+ },
+ },
+ {
+ .ident = "Lenovo V510-15IKB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V510-15IKB"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 300-15IBR",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300-15IBR"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 300-15IKB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300-15IKB"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 300S-11IBR",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 300S-11BR"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 310-15ABR",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15ABR"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 310-15IAP",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IAP"),
},
},
{
.ident = "Lenovo ideapad 310-15IKB",
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IKB"),
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IKB"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad 310-15ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15ISK"),
+ },
+ },
+ {
+ .ident = "Lenovo ideapad Y700-14ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-14ISK"),
},
},
{
@@ -911,6 +1097,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
},
},
{
+ .ident = "Lenovo Legion Y520-15IKBN",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y520-15IKBN"),
+ },
+ },
+ {
+ .ident = "Lenovo Legion Y720-15IKBN",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Y720-15IKBN"),
+ },
+ },
+ {
.ident = "Lenovo Yoga 2 11 / 13 / Pro",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c
index 63ba2cbd04c2..e34fd70b67af 100644
--- a/drivers/platform/x86/intel-hid.c
+++ b/drivers/platform/x86/intel-hid.c
@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/input/sparse-keymap.h>
#include <linux/acpi.h>
+#include <linux/suspend.h>
#include <acpi/acpi_bus.h>
MODULE_LICENSE("GPL");
@@ -75,6 +76,7 @@ static const struct key_entry intel_array_keymap[] = {
struct intel_hid_priv {
struct input_dev *input_dev;
struct input_dev *array;
+ bool wakeup_mode;
};
static int intel_hid_set_enable(struct device *device, bool enable)
@@ -116,23 +118,37 @@ static void intel_button_array_enable(struct device *device, bool enable)
dev_warn(device, "failed to set button capability\n");
}
-static int intel_hid_pl_suspend_handler(struct device *device)
+static int intel_hid_pm_prepare(struct device *device)
{
- intel_hid_set_enable(device, false);
- intel_button_array_enable(device, false);
+ struct intel_hid_priv *priv = dev_get_drvdata(device);
+
+ priv->wakeup_mode = true;
+ return 0;
+}
+static int intel_hid_pl_suspend_handler(struct device *device)
+{
+ if (pm_suspend_via_firmware()) {
+ intel_hid_set_enable(device, false);
+ intel_button_array_enable(device, false);
+ }
return 0;
}
static int intel_hid_pl_resume_handler(struct device *device)
{
- intel_hid_set_enable(device, true);
- intel_button_array_enable(device, true);
+ struct intel_hid_priv *priv = dev_get_drvdata(device);
+ priv->wakeup_mode = false;
+ if (pm_resume_via_firmware()) {
+ intel_hid_set_enable(device, true);
+ intel_button_array_enable(device, true);
+ }
return 0;
}
static const struct dev_pm_ops intel_hid_pl_pm_ops = {
+ .prepare = intel_hid_pm_prepare,
.freeze = intel_hid_pl_suspend_handler,
.thaw = intel_hid_pl_resume_handler,
.restore = intel_hid_pl_resume_handler,
@@ -186,11 +202,35 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
unsigned long long ev_index;
acpi_status status;
+ if (priv->wakeup_mode) {
+ /*
+ * Needed for wakeup from suspend-to-idle to work on some
+ * platforms that don't expose the 5-button array, but still
+ * send notifies with the power button event code to this
+ * device object on power button actions while suspended.
+ */
+ if (event == 0xce)
+ goto wakeup;
+
+ /* Wake up on 5-button array events only. */
+ if (event == 0xc0 || !priv->array)
+ return;
+
+ if (!sparse_keymap_entry_from_scancode(priv->array, event)) {
+ dev_info(&device->dev, "unknown event 0x%x\n", event);
+ return;
+ }
+
+wakeup:
+ pm_wakeup_hard_event(&device->dev);
+ return;
+ }
+
/* 0xC0 is for HID events, other values are for 5 button array */
if (event != 0xc0) {
if (!priv->array ||
!sparse_keymap_report_event(priv->array, event, 1, true))
- dev_info(&device->dev, "unknown event 0x%x\n", event);
+ dev_dbg(&device->dev, "unknown event 0x%x\n", event);
return;
}
@@ -201,7 +241,7 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
}
if (!sparse_keymap_report_event(priv->input_dev, ev_index, 1, true))
- dev_info(&device->dev, "unknown event index 0x%llx\n",
+ dev_dbg(&device->dev, "unknown event index 0x%llx\n",
ev_index);
}
@@ -270,6 +310,7 @@ static int intel_hid_probe(struct platform_device *device)
"failed to enable HID power button\n");
}
+ device_init_wakeup(&device->dev, true);
return 0;
err_remove_notify:
diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c
index c2035e121ac2..58c5ff36523a 100644
--- a/drivers/platform/x86/intel-vbtn.c
+++ b/drivers/platform/x86/intel-vbtn.c
@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/input/sparse-keymap.h>
#include <linux/acpi.h>
+#include <linux/suspend.h>
#include <acpi/acpi_bus.h>
MODULE_LICENSE("GPL");
@@ -35,8 +36,8 @@ static const struct acpi_device_id intel_vbtn_ids[] = {
/* In theory, these are HID usages. */
static const struct key_entry intel_vbtn_keymap[] = {
- { KE_IGNORE, 0xC0, { KEY_POWER } }, /* power key press */
- { KE_KEY, 0xC1, { KEY_POWER } }, /* power key release */
+ { KE_KEY, 0xC0, { KEY_POWER } }, /* power key press */
+ { KE_IGNORE, 0xC1, { KEY_POWER } }, /* power key release */
{ KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* volume-up key press */
{ KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* volume-up key release */
{ KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* volume-down key press */
@@ -46,6 +47,7 @@ static const struct key_entry intel_vbtn_keymap[] = {
struct intel_vbtn_priv {
struct input_dev *input_dev;
+ bool wakeup_mode;
};
static int intel_vbtn_input_setup(struct platform_device *device)
@@ -73,9 +75,15 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
struct platform_device *device = context;
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
- if (!sparse_keymap_report_event(priv->input_dev, event, 1, true))
- dev_info(&device->dev, "unknown event index 0x%x\n",
- event);
+ if (priv->wakeup_mode) {
+ if (sparse_keymap_entry_from_scancode(priv->input_dev, event)) {
+ pm_wakeup_hard_event(&device->dev);
+ return;
+ }
+ } else if (sparse_keymap_report_event(priv->input_dev, event, 1, true)) {
+ return;
+ }
+ dev_dbg(&device->dev, "unknown event index 0x%x\n", event);
}
static int intel_vbtn_probe(struct platform_device *device)
@@ -109,6 +117,7 @@ static int intel_vbtn_probe(struct platform_device *device)
if (ACPI_FAILURE(status))
return -EBUSY;
+ device_init_wakeup(&device->dev, true);
return 0;
}
@@ -125,10 +134,34 @@ static int intel_vbtn_remove(struct platform_device *device)
return 0;
}
+static int intel_vbtn_pm_prepare(struct device *dev)
+{
+ struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
+
+ priv->wakeup_mode = true;
+ return 0;
+}
+
+static int intel_vbtn_pm_resume(struct device *dev)
+{
+ struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
+
+ priv->wakeup_mode = false;
+ return 0;
+}
+
+static const struct dev_pm_ops intel_vbtn_pm_ops = {
+ .prepare = intel_vbtn_pm_prepare,
+ .resume = intel_vbtn_pm_resume,
+ .restore = intel_vbtn_pm_resume,
+ .thaw = intel_vbtn_pm_resume,
+};
+
static struct platform_driver intel_vbtn_pl_driver = {
.driver = {
.name = "intel-vbtn",
.acpi_match_table = intel_vbtn_ids,
+ .pm = &intel_vbtn_pm_ops,
},
.probe = intel_vbtn_probe,
.remove = intel_vbtn_remove,
diff --git a/drivers/platform/x86/intel_bxtwc_tmu.c b/drivers/platform/x86/intel_bxtwc_tmu.c
index e202abd5b0df..ea865d4ca220 100644
--- a/drivers/platform/x86/intel_bxtwc_tmu.c
+++ b/drivers/platform/x86/intel_bxtwc_tmu.c
@@ -92,10 +92,6 @@ static int bxt_wcove_tmu_probe(struct platform_device *pdev)
}
wctmu->irq = virq;
- /* Enable TMU interrupts */
- regmap_update_bits(wctmu->regmap, BXTWC_MIRQLVL1,
- BXTWC_MIRQLVL1_MTMU, 0);
-
/* Unmask TMU second level Wake & System alarm */
regmap_update_bits(wctmu->regmap, BXTWC_MTMUIRQ_REG,
BXTWC_TMU_ALRM_MASK, 0);
diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c
index 6a1b2ca5b6fe..da706e2c4232 100644
--- a/drivers/platform/x86/intel_cht_int33fe.c
+++ b/drivers/platform/x86/intel_cht_int33fe.c
@@ -34,6 +34,13 @@ struct cht_int33fe_data {
struct i2c_client *pi3usb30532;
};
+static const char * const max17047_suppliers[] = { "bq24190-charger" };
+
+static const struct property_entry max17047_props[] = {
+ PROPERTY_ENTRY_STRING_ARRAY("supplied-from", max17047_suppliers),
+ { }
+};
+
static int cht_int33fe_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@@ -70,6 +77,7 @@ static int cht_int33fe_probe(struct i2c_client *client)
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
+ board_info.properties = max17047_props;
data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
if (!data->max17047)
diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c
new file mode 100644
index 000000000000..92dc230ef5b2
--- /dev/null
+++ b/drivers/platform/x86/intel_int0002_vgpio.c
@@ -0,0 +1,219 @@
+/*
+ * Intel INT0002 "Virtual GPIO" driver
+ *
+ * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Loosely based on android x86 kernel code which is:
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * Author: Dyut Kumar Sil <dyut.k.sil@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Some peripherals on Bay Trail and Cherry Trail platforms signal a Power
+ * Management Event (PME) to the Power Management Controller (PMC) to wakeup
+ * the system. When this happens software needs to clear the PME bus 0 status
+ * bit in the GPE0a_STS register to avoid an IRQ storm on IRQ 9.
+ *
+ * This is modelled in ACPI through the INT0002 ACPI device, which is
+ * called a "Virtual GPIO controller" in ACPI because it defines the event
+ * handler to call when the PME triggers through _AEI and _L02 / _E02
+ * methods as would be done for a real GPIO interrupt in ACPI. Note this
+ * is a hack to define an AML event handler for the PME while using existing
+ * ACPI mechanisms, this is not a real GPIO at all.
+ *
+ * This driver will bind to the INT0002 device, and register as a GPIO
+ * controller, letting gpiolib-acpi.c call the _L02 handler as it would
+ * for a real GPIO controller.
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitmap.h>
+#include <linux/gpio/driver.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+#define DRV_NAME "INT0002 Virtual GPIO"
+
+/* For some reason the virtual GPIO pin tied to the GPE is numbered pin 2 */
+#define GPE0A_PME_B0_VIRT_GPIO_PIN 2
+
+#define GPE0A_PME_B0_STS_BIT BIT(13)
+#define GPE0A_PME_B0_EN_BIT BIT(13)
+#define GPE0A_STS_PORT 0x420
+#define GPE0A_EN_PORT 0x428
+
+#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
+
+static const struct x86_cpu_id int0002_cpu_ids[] = {
+/*
+ * Limit ourselves to Cherry Trail for now, until testing shows we
+ * need to handle the INT0002 device on Baytrail too.
+ * ICPU(INTEL_FAM6_ATOM_SILVERMONT1), * Valleyview, Bay Trail *
+ */
+ ICPU(INTEL_FAM6_ATOM_AIRMONT), /* Braswell, Cherry Trail */
+ {}
+};
+
+/*
+ * As this is not a real GPIO at all, but just a hack to model an event in
+ * ACPI the get / set functions are dummy functions.
+ */
+
+static int int0002_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ return 0;
+}
+
+static void int0002_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+}
+
+static int int0002_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ return 0;
+}
+
+static void int0002_irq_ack(struct irq_data *data)
+{
+ outl(GPE0A_PME_B0_STS_BIT, GPE0A_STS_PORT);
+}
+
+static void int0002_irq_unmask(struct irq_data *data)
+{
+ u32 gpe_en_reg;
+
+ gpe_en_reg = inl(GPE0A_EN_PORT);
+ gpe_en_reg |= GPE0A_PME_B0_EN_BIT;
+ outl(gpe_en_reg, GPE0A_EN_PORT);
+}
+
+static void int0002_irq_mask(struct irq_data *data)
+{
+ u32 gpe_en_reg;
+
+ gpe_en_reg = inl(GPE0A_EN_PORT);
+ gpe_en_reg &= ~GPE0A_PME_B0_EN_BIT;
+ outl(gpe_en_reg, GPE0A_EN_PORT);
+}
+
+static irqreturn_t int0002_irq(int irq, void *data)
+{
+ struct gpio_chip *chip = data;
+ u32 gpe_sts_reg;
+
+ gpe_sts_reg = inl(GPE0A_STS_PORT);
+ if (!(gpe_sts_reg & GPE0A_PME_B0_STS_BIT))
+ return IRQ_NONE;
+
+ generic_handle_irq(irq_find_mapping(chip->irqdomain,
+ GPE0A_PME_B0_VIRT_GPIO_PIN));
+
+ pm_system_wakeup();
+
+ return IRQ_HANDLED;
+}
+
+static struct irq_chip int0002_irqchip = {
+ .name = DRV_NAME,
+ .irq_ack = int0002_irq_ack,
+ .irq_mask = int0002_irq_mask,
+ .irq_unmask = int0002_irq_unmask,
+};
+
+static int int0002_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct x86_cpu_id *cpu_id;
+ struct gpio_chip *chip;
+ int irq, ret;
+
+ /* Menlow has a different INT0002 device? <sigh> */
+ cpu_id = x86_match_cpu(int0002_cpu_ids);
+ if (!cpu_id)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "Error getting IRQ: %d\n", irq);
+ return irq;
+ }
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->label = DRV_NAME;
+ chip->parent = dev;
+ chip->owner = THIS_MODULE;
+ chip->get = int0002_gpio_get;
+ chip->set = int0002_gpio_set;
+ chip->direction_input = int0002_gpio_get;
+ chip->direction_output = int0002_gpio_direction_output;
+ chip->base = -1;
+ chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1;
+ chip->irq_need_valid_mask = true;
+
+ ret = devm_gpiochip_add_data(&pdev->dev, chip, NULL);
+ if (ret) {
+ dev_err(dev, "Error adding gpio chip: %d\n", ret);
+ return ret;
+ }
+
+ bitmap_clear(chip->irq_valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN);
+
+ /*
+ * We manually request the irq here instead of passing a flow-handler
+ * to gpiochip_set_chained_irqchip, because the irq is shared.
+ */
+ ret = devm_request_irq(dev, irq, int0002_irq,
+ IRQF_SHARED | IRQF_NO_THREAD, "INT0002", chip);
+ if (ret) {
+ dev_err(dev, "Error requesting IRQ %d: %d\n", irq, ret);
+ return ret;
+ }
+
+ ret = gpiochip_irqchip_add(chip, &int0002_irqchip, 0, handle_edge_irq,
+ IRQ_TYPE_NONE);
+ if (ret) {
+ dev_err(dev, "Error adding irqchip: %d\n", ret);
+ return ret;
+ }
+
+ gpiochip_set_chained_irqchip(chip, &int0002_irqchip, irq, NULL);
+
+ return 0;
+}
+
+static const struct acpi_device_id int0002_acpi_ids[] = {
+ { "INT0002", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, int0002_acpi_ids);
+
+static struct platform_driver int0002_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .acpi_match_table = int0002_acpi_ids,
+ },
+ .probe = int0002_probe,
+};
+
+module_platform_driver(int0002_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Intel INT0002 Virtual GPIO driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c
index cbe01021c939..ef9b0af8cdd3 100644
--- a/drivers/platform/x86/intel_menlow.c
+++ b/drivers/platform/x86/intel_menlow.c
@@ -142,7 +142,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
return 0;
}
-static struct thermal_cooling_device_ops memory_cooling_ops = {
+static const struct thermal_cooling_device_ops memory_cooling_ops = {
.get_max_state = memory_get_max_bandwidth,
.get_cur_state = memory_get_cur_bandwidth,
.set_cur_state = memory_set_cur_bandwidth,
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index 871cfa682519..d79fbf924b13 100644
--- a/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/drivers/platform/x86/intel_mid_powerbtn.c
@@ -108,13 +108,13 @@ static irqreturn_t mid_pb_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static struct mid_pb_ddata mfld_ddata = {
+static const struct mid_pb_ddata mfld_ddata = {
.mirqlvl1_addr = INTEL_MSIC_IRQLVL1MSK,
.pbstat_addr = INTEL_MSIC_PBSTATUS,
.pbstat_mask = MSIC_PB_LEVEL,
};
-static struct mid_pb_ddata mrfld_ddata = {
+static const struct mid_pb_ddata mrfld_ddata = {
.mirqlvl1_addr = BCOVE_IRQLVL1MSK,
.pbstat_addr = BCOVE_PBSTATUS,
.pbstat_mask = BCOVE_PB_LEVEL,
@@ -142,8 +142,10 @@ static int mid_pb_probe(struct platform_device *pdev)
if (!id)
return -ENODEV;
- if (irq < 0)
- return -EINVAL;
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
+ return irq;
+ }
input = devm_input_allocate_device(&pdev->dev);
if (!input)
diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c
index 6aa33c4a809f..5747f63c8d9f 100644
--- a/drivers/platform/x86/intel_oaktrail.c
+++ b/drivers/platform/x86/intel_oaktrail.c
@@ -299,7 +299,7 @@ static int dmi_check_cb(const struct dmi_system_id *id)
return 0;
}
-static struct dmi_system_id __initdata oaktrail_dmi_table[] = {
+static const struct dmi_system_id oaktrail_dmi_table[] __initconst = {
{
.ident = "OakTrail platform",
.matches = {
diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c
index 914bcd2edbde..17e08b42b0a9 100644
--- a/drivers/platform/x86/intel_pmc_core.c
+++ b/drivers/platform/x86/intel_pmc_core.c
@@ -110,6 +110,13 @@ static const struct pmc_reg_map spt_reg_map = {
.pfear_sts = spt_pfear_map,
.mphy_sts = spt_mphy_map,
.pll_sts = spt_pll_map,
+ .slp_s0_offset = SPT_PMC_SLP_S0_RES_COUNTER_OFFSET,
+ .ltr_ignore_offset = SPT_PMC_LTR_IGNORE_OFFSET,
+ .regmap_length = SPT_PMC_MMIO_REG_LEN,
+ .ppfear0_offset = SPT_PMC_XRAM_PPFEAR0A,
+ .ppfear_buckets = SPT_PPFEAR_NUM_ENTRIES,
+ .pm_cfg_offset = SPT_PMC_PM_CFG_OFFSET,
+ .pm_read_disable_bit = SPT_PMC_READ_DISABLE_BIT,
};
static const struct pci_device_id pmc_pci_ids[] = {
@@ -157,12 +164,13 @@ static inline u32 pmc_core_adjust_slp_s0_step(u32 value)
int intel_pmc_slp_s0_counter_read(u32 *data)
{
struct pmc_dev *pmcdev = &pmc;
+ const struct pmc_reg_map *map = pmcdev->map;
u32 value;
if (!pmcdev->has_slp_s0_res)
return -EACCES;
- value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
+ value = pmc_core_reg_read(pmcdev, map->slp_s0_offset);
*data = pmc_core_adjust_slp_s0_step(value);
return 0;
@@ -172,9 +180,10 @@ EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read);
static int pmc_core_dev_state_get(void *data, u64 *val)
{
struct pmc_dev *pmcdev = data;
+ const struct pmc_reg_map *map = pmcdev->map;
u32 value;
- value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET);
+ value = pmc_core_reg_read(pmcdev, map->slp_s0_offset);
*val = pmc_core_adjust_slp_s0_step(value);
return 0;
@@ -187,8 +196,8 @@ static int pmc_core_check_read_lock_bit(void)
struct pmc_dev *pmcdev = &pmc;
u32 value;
- value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_CFG_OFFSET);
- return value & BIT(SPT_PMC_READ_DISABLE_BIT);
+ value = pmc_core_reg_read(pmcdev, pmcdev->map->pm_cfg_offset);
+ return value & BIT(pmcdev->map->pm_read_disable_bit);
}
#if IS_ENABLED(CONFIG_DEBUG_FS)
@@ -204,12 +213,13 @@ static int pmc_core_ppfear_sts_show(struct seq_file *s, void *unused)
{
struct pmc_dev *pmcdev = s->private;
const struct pmc_bit_map *map = pmcdev->map->pfear_sts;
- u8 pf_regs[NUM_ENTRIES];
+ u8 pf_regs[PPFEAR_MAX_NUM_ENTRIES];
int index, iter;
- iter = SPT_PMC_XRAM_PPFEAR0A;
+ iter = pmcdev->map->ppfear0_offset;
- for (index = 0; index < NUM_ENTRIES; index++, iter++)
+ for (index = 0; index < pmcdev->map->ppfear_buckets &&
+ index < PPFEAR_MAX_NUM_ENTRIES; index++, iter++)
pf_regs[index] = pmc_core_reg_read_byte(pmcdev, iter);
for (index = 0; map[index].name; index++)
@@ -376,6 +386,7 @@ static ssize_t pmc_core_ltr_ignore_write(struct file *file, const char __user
*userbuf, size_t count, loff_t *ppos)
{
struct pmc_dev *pmcdev = &pmc;
+ const struct pmc_reg_map *map = pmcdev->map;
u32 val, buf_size, fd;
int err = 0;
@@ -392,9 +403,9 @@ static ssize_t pmc_core_ltr_ignore_write(struct file *file, const char __user
goto out_unlock;
}
- fd = pmc_core_reg_read(pmcdev, SPT_PMC_LTR_IGNORE_OFFSET);
+ fd = pmc_core_reg_read(pmcdev, map->ltr_ignore_offset);
fd |= (1U << val);
- pmc_core_reg_write(pmcdev, SPT_PMC_LTR_IGNORE_OFFSET, fd);
+ pmc_core_reg_write(pmcdev, map->ltr_ignore_offset, fd);
out_unlock:
mutex_unlock(&pmcdev->lock);
@@ -530,8 +541,8 @@ static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
mutex_init(&pmcdev->lock);
- pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit();
pmcdev->map = map;
+ pmcdev->pmc_xram_read_bit = pmc_core_check_read_lock_bit();
err = pmc_core_dbgfs_register(pmcdev);
if (err < 0)
diff --git a/drivers/platform/x86/intel_pmc_core.h b/drivers/platform/x86/intel_pmc_core.h
index 5a48e7728479..3d225a9cc09f 100644
--- a/drivers/platform/x86/intel_pmc_core.h
+++ b/drivers/platform/x86/intel_pmc_core.h
@@ -38,7 +38,8 @@
#define SPT_PMC_SLP_S0_RES_COUNTER_STEP 0x64
#define PMC_BASE_ADDR_MASK ~(SPT_PMC_MMIO_REG_LEN - 1)
#define MTPMC_MASK 0xffff0000
-#define NUM_ENTRIES 5
+#define PPFEAR_MAX_NUM_ENTRIES 5
+#define SPT_PPFEAR_NUM_ENTRIES 5
#define SPT_PMC_READ_DISABLE_BIT 0x16
#define SPT_PMC_MSG_FULL_STS_BIT 0x18
#define NUM_RETRIES 100
@@ -126,10 +127,37 @@ struct pmc_bit_map {
u32 bit_mask;
};
+/**
+ * struct pmc_reg_map - Structure used to define parameter unique to a
+ PCH family
+ * @pfear_sts: Maps name of IP block to PPFEAR* bit
+ * @mphy_sts: Maps name of MPHY lane to MPHY status lane status bit
+ * @pll_sts: Maps name of PLL to corresponding bit status
+ * @slp_s0_offset: PWRMBASE offset to read SLP_S0 residency
+ * @ltr_ignore_offset: PWRMBASE offset to read/write LTR ignore bit
+ * @base_address: Base address of PWRMBASE defined in BIOS writer guide
+ * @regmap_length: Length of memory to map from PWRMBASE address to access
+ * @ppfear0_offset: PWRMBASE offset to to read PPFEAR*
+ * @ppfear_buckets: Number of 8 bits blocks to read all IP blocks from
+ * PPFEAR
+ * @pm_cfg_offset: PWRMBASE offset to PM_CFG register
+ * @pm_read_disable_bit: Bit index to read PMC_READ_DISABLE
+ *
+ * Each PCH has unique set of register offsets and bit indexes. This structure
+ * captures them to have a common implementation.
+ */
struct pmc_reg_map {
const struct pmc_bit_map *pfear_sts;
const struct pmc_bit_map *mphy_sts;
const struct pmc_bit_map *pll_sts;
+ const u32 slp_s0_offset;
+ const u32 ltr_ignore_offset;
+ const u32 base_address;
+ const int regmap_length;
+ const u32 ppfear0_offset;
+ const int ppfear_buckets;
+ const u32 pm_cfg_offset;
+ const int pm_read_disable_bit;
};
/**
diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c
index e4d4dfe3e1d1..e03fa31446ca 100644
--- a/drivers/platform/x86/intel_pmc_ipc.c
+++ b/drivers/platform/x86/intel_pmc_ipc.c
@@ -33,6 +33,7 @@
#include <linux/suspend.h>
#include <linux/acpi.h>
#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/spinlock.h>
#include <asm/intel_pmc_ipc.h>
@@ -131,6 +132,7 @@ static struct intel_pmc_ipc_dev {
/* gcr */
void __iomem *gcr_mem_base;
bool has_gcr_regs;
+ spinlock_t gcr_lock;
/* punit */
struct platform_device *punit_dev;
@@ -186,7 +188,7 @@ static inline void ipc_data_writel(u32 data, u32 offset)
writel(data, ipcdev.ipc_base + IPC_WRITE_BUFFER + offset);
}
-static inline u8 ipc_data_readb(u32 offset)
+static inline u8 __maybe_unused ipc_data_readb(u32 offset)
{
return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
}
@@ -225,17 +227,17 @@ int intel_pmc_gcr_read(u32 offset, u32 *data)
{
int ret;
- mutex_lock(&ipclock);
+ spin_lock(&ipcdev.gcr_lock);
ret = is_gcr_valid(offset);
if (ret < 0) {
- mutex_unlock(&ipclock);
+ spin_unlock(&ipcdev.gcr_lock);
return ret;
}
*data = readl(ipcdev.gcr_mem_base + offset);
- mutex_unlock(&ipclock);
+ spin_unlock(&ipcdev.gcr_lock);
return 0;
}
@@ -255,17 +257,17 @@ int intel_pmc_gcr_write(u32 offset, u32 data)
{
int ret;
- mutex_lock(&ipclock);
+ spin_lock(&ipcdev.gcr_lock);
ret = is_gcr_valid(offset);
if (ret < 0) {
- mutex_unlock(&ipclock);
+ spin_unlock(&ipcdev.gcr_lock);
return ret;
}
writel(data, ipcdev.gcr_mem_base + offset);
- mutex_unlock(&ipclock);
+ spin_unlock(&ipcdev.gcr_lock);
return 0;
}
@@ -287,7 +289,7 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
u32 new_val;
int ret = 0;
- mutex_lock(&ipclock);
+ spin_lock(&ipcdev.gcr_lock);
ret = is_gcr_valid(offset);
if (ret < 0)
@@ -309,7 +311,7 @@ int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
}
gcr_ipc_unlock:
- mutex_unlock(&ipclock);
+ spin_unlock(&ipcdev.gcr_lock);
return ret;
}
EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
@@ -480,52 +482,41 @@ static irqreturn_t ioc(int irq, void *dev_id)
static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
- resource_size_t pci_resource;
+ struct intel_pmc_ipc_dev *pmc = &ipcdev;
int ret;
- int len;
- ipcdev.dev = &pci_dev_get(pdev)->dev;
- ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
+ /* Only one PMC is supported */
+ if (pmc->dev)
+ return -EBUSY;
- ret = pci_enable_device(pdev);
+ pmc->irq_mode = IPC_TRIGGER_MODE_IRQ;
+
+ spin_lock_init(&ipcdev.gcr_lock);
+
+ ret = pcim_enable_device(pdev);
if (ret)
return ret;
- ret = pci_request_regions(pdev, "intel_pmc_ipc");
+ ret = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
if (ret)
return ret;
- pci_resource = pci_resource_start(pdev, 0);
- len = pci_resource_len(pdev, 0);
- if (!pci_resource || !len) {
- dev_err(&pdev->dev, "Failed to get resource\n");
- return -ENOMEM;
- }
+ init_completion(&pmc->cmd_complete);
- init_completion(&ipcdev.cmd_complete);
+ pmc->ipc_base = pcim_iomap_table(pdev)[0];
- if (request_irq(pdev->irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
+ ret = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_pmc_ipc",
+ pmc);
+ if (ret) {
dev_err(&pdev->dev, "Failed to request irq\n");
- return -EBUSY;
+ return ret;
}
- ipcdev.ipc_base = ioremap_nocache(pci_resource, len);
- if (!ipcdev.ipc_base) {
- dev_err(&pdev->dev, "Failed to ioremap ipc base\n");
- free_irq(pdev->irq, &ipcdev);
- ret = -ENOMEM;
- }
+ pmc->dev = &pdev->dev;
- return ret;
-}
+ pci_set_drvdata(pdev, pmc);
-static void ipc_pci_remove(struct pci_dev *pdev)
-{
- free_irq(pdev->irq, &ipcdev);
- pci_release_regions(pdev);
- pci_dev_put(pdev);
- iounmap(ipcdev.ipc_base);
- ipcdev.dev = NULL;
+ return 0;
}
static const struct pci_device_id ipc_pci_ids[] = {
@@ -540,7 +531,6 @@ static struct pci_driver ipc_pci_driver = {
.name = "intel_pmc_ipc",
.id_table = ipc_pci_ids,
.probe = ipc_pci_probe,
- .remove = ipc_pci_remove,
};
static ssize_t intel_pmc_ipc_simple_cmd_store(struct device *dev,
@@ -850,17 +840,12 @@ static int ipc_plat_get_res(struct platform_device *pdev)
return -ENXIO;
}
size = PLAT_RESOURCE_IPC_SIZE + PLAT_RESOURCE_GCR_SIZE;
+ res->end = res->start + size - 1;
+
+ addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
- if (!request_mem_region(res->start, size, pdev->name)) {
- dev_err(&pdev->dev, "Failed to request ipc resource\n");
- return -EBUSY;
- }
- addr = ioremap_nocache(res->start, size);
- if (!addr) {
- dev_err(&pdev->dev, "I/O memory remapping failed\n");
- release_mem_region(res->start, size);
- return -ENOMEM;
- }
ipcdev.ipc_base = addr;
ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
@@ -917,12 +902,12 @@ MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
static int ipc_plat_probe(struct platform_device *pdev)
{
- struct resource *res;
int ret;
ipcdev.dev = &pdev->dev;
ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
init_completion(&ipcdev.cmd_complete);
+ spin_lock_init(&ipcdev.gcr_lock);
ipcdev.irq = platform_get_irq(pdev, 0);
if (ipcdev.irq < 0) {
@@ -939,11 +924,11 @@ static int ipc_plat_probe(struct platform_device *pdev)
ret = ipc_create_pmc_devices();
if (ret) {
dev_err(&pdev->dev, "Failed to create pmc devices\n");
- goto err_device;
+ return ret;
}
- if (request_irq(ipcdev.irq, ioc, IRQF_NO_SUSPEND,
- "intel_pmc_ipc", &ipcdev)) {
+ if (devm_request_irq(&pdev->dev, ipcdev.irq, ioc, IRQF_NO_SUSPEND,
+ "intel_pmc_ipc", &ipcdev)) {
dev_err(&pdev->dev, "Failed to request irq\n");
ret = -EBUSY;
goto err_irq;
@@ -960,40 +945,22 @@ static int ipc_plat_probe(struct platform_device *pdev)
return 0;
err_sys:
- free_irq(ipcdev.irq, &ipcdev);
+ devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
err_irq:
platform_device_unregister(ipcdev.tco_dev);
platform_device_unregister(ipcdev.punit_dev);
platform_device_unregister(ipcdev.telemetry_dev);
-err_device:
- iounmap(ipcdev.ipc_base);
- res = platform_get_resource(pdev, IORESOURCE_MEM,
- PLAT_RESOURCE_IPC_INDEX);
- if (res) {
- release_mem_region(res->start,
- PLAT_RESOURCE_IPC_SIZE +
- PLAT_RESOURCE_GCR_SIZE);
- }
+
return ret;
}
static int ipc_plat_remove(struct platform_device *pdev)
{
- struct resource *res;
-
sysfs_remove_group(&pdev->dev.kobj, &intel_ipc_group);
- free_irq(ipcdev.irq, &ipcdev);
+ devm_free_irq(&pdev->dev, ipcdev.irq, &ipcdev);
platform_device_unregister(ipcdev.tco_dev);
platform_device_unregister(ipcdev.punit_dev);
platform_device_unregister(ipcdev.telemetry_dev);
- iounmap(ipcdev.ipc_base);
- res = platform_get_resource(pdev, IORESOURCE_MEM,
- PLAT_RESOURCE_IPC_INDEX);
- if (res) {
- release_mem_region(res->start,
- PLAT_RESOURCE_IPC_SIZE +
- PLAT_RESOURCE_GCR_SIZE);
- }
ipcdev.dev = NULL;
return 0;
}
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index f7cf981502cd..2c85f75e32b0 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -72,20 +72,20 @@ struct intel_scu_ipc_pdata_t {
u8 irq_mode;
};
-static struct intel_scu_ipc_pdata_t intel_scu_ipc_lincroft_pdata = {
+static const struct intel_scu_ipc_pdata_t intel_scu_ipc_lincroft_pdata = {
.i2c_base = 0xff12b000,
.i2c_len = 0x10,
.irq_mode = 0,
};
/* Penwell and Cloverview */
-static struct intel_scu_ipc_pdata_t intel_scu_ipc_penwell_pdata = {
+static const struct intel_scu_ipc_pdata_t intel_scu_ipc_penwell_pdata = {
.i2c_base = 0xff12b000,
.i2c_len = 0x10,
.irq_mode = 1,
};
-static struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = {
+static const struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = {
.i2c_base = 0xff00d000,
.i2c_len = 0x10,
.irq_mode = 0,
diff --git a/drivers/platform/x86/intel_telemetry_debugfs.c b/drivers/platform/x86/intel_telemetry_debugfs.c
index 4cc2f4ea0a25..d4fc42b4cbeb 100644
--- a/drivers/platform/x86/intel_telemetry_debugfs.c
+++ b/drivers/platform/x86/intel_telemetry_debugfs.c
@@ -331,6 +331,7 @@ static struct telemetry_debugfs_conf telem_apl_debugfs_conf = {
static const struct x86_cpu_id telemetry_debugfs_cpu_ids[] = {
TELEM_DEBUGFS_CPU(INTEL_FAM6_ATOM_GOLDMONT, telem_apl_debugfs_conf),
+ TELEM_DEBUGFS_CPU(INTEL_FAM6_ATOM_GEMINI_LAKE, telem_apl_debugfs_conf),
{}
};
@@ -710,6 +711,24 @@ static const struct file_operations telem_socstate_ops = {
.release = single_release,
};
+static int telem_s0ix_res_get(void *data, u64 *val)
+{
+ u64 s0ix_total_res;
+ int ret;
+
+ ret = intel_pmc_s0ix_counter_read(&s0ix_total_res);
+ if (ret) {
+ pr_err("Failed to read S0ix residency");
+ return ret;
+ }
+
+ *val = s0ix_total_res;
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(telem_s0ix_fops, telem_s0ix_res_get, NULL, "%llu\n");
+
static int telem_pss_trc_verb_show(struct seq_file *s, void *unused)
{
u32 verbosity;
@@ -938,7 +957,7 @@ static struct notifier_block pm_notifier = {
static int __init telemetry_debugfs_init(void)
{
const struct x86_cpu_id *id;
- int err = -ENOMEM;
+ int err;
struct dentry *f;
/* Only APL supported for now */
@@ -958,11 +977,10 @@ static int __init telemetry_debugfs_init(void)
register_pm_notifier(&pm_notifier);
+ err = -ENOMEM;
debugfs_conf->telemetry_dbg_dir = debugfs_create_dir("telemetry", NULL);
- if (!debugfs_conf->telemetry_dbg_dir) {
- err = -ENOMEM;
+ if (!debugfs_conf->telemetry_dbg_dir)
goto out_pm;
- }
f = debugfs_create_file("pss_info", S_IFREG | S_IRUGO,
debugfs_conf->telemetry_dbg_dir, NULL,
@@ -988,6 +1006,14 @@ static int __init telemetry_debugfs_init(void)
goto out;
}
+ f = debugfs_create_file("s0ix_residency_usec", S_IFREG | S_IRUGO,
+ debugfs_conf->telemetry_dbg_dir,
+ NULL, &telem_s0ix_fops);
+ if (!f) {
+ pr_err("s0ix_residency_usec debugfs register failed\n");
+ goto out;
+ }
+
f = debugfs_create_file("pss_trace_verbosity", S_IFREG | S_IRUGO,
debugfs_conf->telemetry_dbg_dir, NULL,
&telem_pss_trc_verb_ops);
diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c
index 6ebdbd2b04fc..e0424d5a795a 100644
--- a/drivers/platform/x86/intel_telemetry_pltdrv.c
+++ b/drivers/platform/x86/intel_telemetry_pltdrv.c
@@ -46,7 +46,6 @@
#define TELEM_SAMPLING_DEFAULT_PERIOD 0xD
#define TELEM_MAX_EVENTS_SRAM 28
-#define TELEM_MAX_OS_ALLOCATED_EVENTS 20
#define TELEM_SSRAM_STARTTIME_OFFSET 8
#define TELEM_SSRAM_EVTLOG_OFFSET 16
@@ -153,6 +152,30 @@ static struct telemetry_evtmap
{"PC2_AND_MEM_SHALLOW_IDLE_RES", 0x1D40},
};
+static struct telemetry_evtmap
+ telemetry_glk_pss_default_events[TELEM_MAX_OS_ALLOCATED_EVENTS] = {
+ {"IA_CORE0_C6_RES", 0x0400},
+ {"IA_CORE0_C6_CTR", 0x0000},
+ {"IA_MODULE0_C7_RES", 0x0410},
+ {"IA_MODULE0_C7_CTR", 0x000C},
+ {"IA_C0_RES", 0x0805},
+ {"PCS_LTR", 0x2801},
+ {"PSTATES", 0x2802},
+ {"SOC_S0I3_RES", 0x0407},
+ {"SOC_S0I3_CTR", 0x0008},
+ {"PCS_S0I3_CTR", 0x0007},
+ {"PCS_C1E_RES", 0x0414},
+ {"PCS_IDLE_STATUS", 0x2806},
+ {"IA_PERF_LIMITS", 0x280B},
+ {"GT_PERF_LIMITS", 0x280C},
+ {"PCS_WAKEUP_S0IX_CTR", 0x0025},
+ {"PCS_IDLE_BLOCKED", 0x2C00},
+ {"PCS_S0IX_BLOCKED", 0x2C01},
+ {"PCS_S0IX_WAKE_REASONS", 0x2C02},
+ {"PCS_LTR_BLOCKING", 0x2C03},
+ {"PC2_AND_MEM_SHALLOW_IDLE_RES", 0x1D40},
+};
+
/* APL specific Data */
static struct telemetry_plt_config telem_apl_config = {
.pss_config = {
@@ -163,8 +186,19 @@ static struct telemetry_plt_config telem_apl_config = {
},
};
+/* GLK specific Data */
+static struct telemetry_plt_config telem_glk_config = {
+ .pss_config = {
+ .telem_evts = telemetry_glk_pss_default_events,
+ },
+ .ioss_config = {
+ .telem_evts = telemetry_apl_ioss_default_events,
+ },
+};
+
static const struct x86_cpu_id telemetry_cpu_ids[] = {
TELEM_CPU(INTEL_FAM6_ATOM_GOLDMONT, telem_apl_config),
+ TELEM_CPU(INTEL_FAM6_ATOM_GEMINI_LAKE, telem_glk_config),
{}
};
diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c
index 8f98c211b440..4f3de2a8c4df 100644
--- a/drivers/platform/x86/mlx-platform.c
+++ b/drivers/platform/x86/mlx-platform.c
@@ -247,7 +247,7 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
return 1;
};
-static struct dmi_system_id mlxplat_dmi_table[] __initdata = {
+static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
{
.callback = mlxplat_dmi_default_matched,
.matches = {
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index 9e90827c176a..d5bfcc602090 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -563,11 +563,11 @@ static struct attribute *msipf_old_attributes[] = {
NULL
};
-static struct attribute_group msipf_attribute_group = {
+static const struct attribute_group msipf_attribute_group = {
.attrs = msipf_attributes
};
-static struct attribute_group msipf_old_attribute_group = {
+static const struct attribute_group msipf_old_attribute_group = {
.attrs = msipf_old_attributes
};
@@ -605,7 +605,7 @@ static int dmi_check_cb(const struct dmi_system_id *dmi)
return 1;
}
-static struct dmi_system_id __initdata msi_dmi_table[] = {
+static const struct dmi_system_id msi_dmi_table[] __initconst = {
{
.ident = "MSI S270",
.matches = {
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c
index f6209b739ec0..620138236c89 100644
--- a/drivers/platform/x86/msi-wmi.c
+++ b/drivers/platform/x86/msi-wmi.c
@@ -184,7 +184,7 @@ static const struct backlight_ops msi_backlight_ops = {
static void msi_wmi_notify(u32 value, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
- static struct key_entry *key;
+ struct key_entry *key;
union acpi_object *obj;
acpi_status status;
diff --git a/drivers/platform/x86/mxm-wmi.c b/drivers/platform/x86/mxm-wmi.c
index f4bad83053a9..35d8b9a939f9 100644
--- a/drivers/platform/x86/mxm-wmi.c
+++ b/drivers/platform/x86/mxm-wmi.c
@@ -53,7 +53,7 @@ int mxm_wmi_call_mxds(int adapter)
printk("calling mux switch %d\n", adapter);
- status = wmi_evaluate_method(MXM_WMMX_GUID, 0x1, adapter, &input,
+ status = wmi_evaluate_method(MXM_WMMX_GUID, 0x0, adapter, &input,
&output);
if (ACPI_FAILURE(status))
@@ -78,7 +78,7 @@ int mxm_wmi_call_mxmx(int adapter)
printk("calling mux switch %d\n", adapter);
- status = wmi_evaluate_method(MXM_WMMX_GUID, 0x1, adapter, &input,
+ status = wmi_evaluate_method(MXM_WMMX_GUID, 0x0, adapter, &input,
&output);
if (ACPI_FAILURE(status))
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 975f4e100dbd..5c39b3211709 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -228,10 +228,6 @@ struct pcc_acpi {
struct backlight_device *backlight;
};
-struct pcc_keyinput {
- struct acpi_hotkey *hotkey;
-};
-
/* method access functions */
static int acpi_pcc_write_sset(struct pcc_acpi *pcc, int func, int val)
{
@@ -441,7 +437,7 @@ static struct attribute *pcc_sysfs_entries[] = {
NULL,
};
-static struct attribute_group pcc_attr_group = {
+static const struct attribute_group pcc_attr_group = {
.name = NULL, /* put in device directory */
.attrs = pcc_sysfs_entries,
};
diff --git a/drivers/platform/x86/peaq-wmi.c b/drivers/platform/x86/peaq-wmi.c
new file mode 100644
index 000000000000..bc98ef95514a
--- /dev/null
+++ b/drivers/platform/x86/peaq-wmi.c
@@ -0,0 +1,100 @@
+/*
+ * PEAQ 2-in-1 WMI hotkey driver
+ * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/acpi.h>
+#include <linux/input-polldev.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define PEAQ_DOLBY_BUTTON_GUID "ABBC0F6F-8EA1-11D1-00A0-C90629100000"
+#define PEAQ_DOLBY_BUTTON_METHOD_ID 5
+#define PEAQ_POLL_INTERVAL_MS 250
+#define PEAQ_POLL_IGNORE_MS 500
+#define PEAQ_POLL_MAX_MS 1000
+
+MODULE_ALIAS("wmi:"PEAQ_DOLBY_BUTTON_GUID);
+
+static unsigned int peaq_ignore_events_counter;
+static struct input_polled_dev *peaq_poll_dev;
+
+/*
+ * The Dolby button (yes really a Dolby button) causes an ACPI variable to get
+ * set on both press and release. The WMI method checks and clears that flag.
+ * So for a press + release we will get back One from the WMI method either once
+ * (if polling after the release) or twice (polling between press and release).
+ * We ignore events for 0.5s after the first event to avoid reporting 2 presses.
+ */
+static void peaq_wmi_poll(struct input_polled_dev *dev)
+{
+ union acpi_object obj;
+ acpi_status status;
+ u32 dummy = 0;
+
+ struct acpi_buffer input = { sizeof(dummy), &dummy };
+ struct acpi_buffer output = { sizeof(obj), &obj };
+
+ status = wmi_evaluate_method(PEAQ_DOLBY_BUTTON_GUID, 0,
+ PEAQ_DOLBY_BUTTON_METHOD_ID,
+ &input, &output);
+ if (ACPI_FAILURE(status))
+ return;
+
+ if (obj.type != ACPI_TYPE_INTEGER) {
+ dev_err(&peaq_poll_dev->input->dev,
+ "Error WMBC did not return an integer\n");
+ return;
+ }
+
+ if (peaq_ignore_events_counter && peaq_ignore_events_counter--)
+ return;
+
+ if (obj.integer.value) {
+ input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 1);
+ input_sync(peaq_poll_dev->input);
+ input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 0);
+ input_sync(peaq_poll_dev->input);
+ peaq_ignore_events_counter = max(1u,
+ PEAQ_POLL_IGNORE_MS / peaq_poll_dev->poll_interval);
+ }
+}
+
+static int __init peaq_wmi_init(void)
+{
+ if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID))
+ return -ENODEV;
+
+ peaq_poll_dev = input_allocate_polled_device();
+ if (!peaq_poll_dev)
+ return -ENOMEM;
+
+ peaq_poll_dev->poll = peaq_wmi_poll;
+ peaq_poll_dev->poll_interval = PEAQ_POLL_INTERVAL_MS;
+ peaq_poll_dev->poll_interval_max = PEAQ_POLL_MAX_MS;
+ peaq_poll_dev->input->name = "PEAQ WMI hotkeys";
+ peaq_poll_dev->input->phys = "wmi/input0";
+ peaq_poll_dev->input->id.bustype = BUS_HOST;
+ input_set_capability(peaq_poll_dev->input, EV_KEY, KEY_SOUND);
+
+ return input_register_polled_device(peaq_poll_dev);
+}
+
+static void __exit peaq_wmi_exit(void)
+{
+ if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID))
+ return;
+
+ input_unregister_polled_device(peaq_poll_dev);
+}
+
+module_init(peaq_wmi_init);
+module_exit(peaq_wmi_exit);
+
+MODULE_DESCRIPTION("PEAQ 2-in-1 WMI hotkey driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index 8c146e2b6727..d3cb26f6df73 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -591,7 +591,7 @@ static int seclinux_rfkill_set(void *data, bool blocked)
!blocked);
}
-static struct rfkill_ops seclinux_rfkill_ops = {
+static const struct rfkill_ops seclinux_rfkill_ops = {
.set_block = seclinux_rfkill_set,
};
@@ -651,7 +651,7 @@ static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv)
rfkill_set_sw_state(rfkill, !ret);
}
-static struct rfkill_ops swsmi_rfkill_ops = {
+static const struct rfkill_ops swsmi_rfkill_ops = {
.set_block = swsmi_rfkill_set,
.query = swsmi_rfkill_query,
};
@@ -1232,7 +1232,7 @@ static umode_t samsung_sysfs_is_visible(struct kobject *kobj,
return ok ? attr->mode : 0;
}
-static struct attribute_group platform_attribute_group = {
+static const struct attribute_group platform_attribute_group = {
.is_visible = samsung_sysfs_is_visible,
.attrs = platform_attributes
};
@@ -1446,9 +1446,9 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung)
const struct sabi_config *config = NULL;
const struct sabi_commands *commands;
unsigned int ifaceP;
+ int loca = 0xffff;
int ret = 0;
int i;
- int loca;
samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);
if (!samsung->f0000_segment) {
@@ -1567,7 +1567,7 @@ static int __init samsung_dmi_matched(const struct dmi_system_id *d)
return 0;
}
-static struct dmi_system_id __initdata samsung_dmi_table[] = {
+static const struct dmi_system_id samsung_dmi_table[] __initconst = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c
index e6aac725a0af..a2fb7fbc3273 100644
--- a/drivers/platform/x86/samsung-q10.c
+++ b/drivers/platform/x86/samsung-q10.c
@@ -95,7 +95,7 @@ static int __init dmi_check_callback(const struct dmi_system_id *id)
return 1;
}
-static struct dmi_system_id __initdata samsungq10_dmi_table[] = {
+static const struct dmi_system_id samsungq10_dmi_table[] __initconst = {
{
.ident = "Samsung Q10",
.matches = {
diff --git a/drivers/platform/x86/silead_dmi.c b/drivers/platform/x86/silead_dmi.c
index a3a57d93cf06..1157a7b646d6 100644
--- a/drivers/platform/x86/silead_dmi.c
+++ b/drivers/platform/x86/silead_dmi.c
@@ -80,6 +80,62 @@ static const struct silead_ts_dmi_data surftab_wintron70_st70416_6_data = {
.properties = surftab_wintron70_st70416_6_props,
};
+static const struct property_entry gp_electronic_t701_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 960),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 640),
+ PROPERTY_ENTRY_STRING("firmware-name",
+ "gsl1680-gp-electronic-t701.fw"),
+ { }
+};
+
+static const struct silead_ts_dmi_data gp_electronic_t701_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = gp_electronic_t701_props,
+};
+
+static const struct property_entry pipo_w2s_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1660),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 880),
+ PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ PROPERTY_ENTRY_STRING("firmware-name",
+ "gsl1680-pipo-w2s.fw"),
+ { }
+};
+
+static const struct silead_ts_dmi_data pipo_w2s_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = pipo_w2s_props,
+};
+
+static const struct property_entry pov_mobii_wintab_p800w_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1800),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1150),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ PROPERTY_ENTRY_STRING("firmware-name",
+ "gsl3692-pov-mobii-wintab-p800w.fw"),
+ { }
+};
+
+static const struct silead_ts_dmi_data pov_mobii_wintab_p800w_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = pov_mobii_wintab_p800w_props,
+};
+
+static const struct property_entry itworks_tw891_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1600),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 890),
+ PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ PROPERTY_ENTRY_STRING("firmware-name", "gsl3670-itworks-tw891.fw"),
+ { }
+};
+
+static const struct silead_ts_dmi_data itworks_tw891_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = itworks_tw891_props,
+};
+
static const struct dmi_system_id silead_ts_dmi_table[] = {
{
/* CUBE iwork8 Air */
@@ -117,6 +173,52 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
DMI_MATCH(DMI_BIOS_VERSION, "TREK.G.WI71C.JGBMRBA04"),
},
},
+ {
+ /* Ployer Momo7w (same hardware as the Trekstor ST70416-6) */
+ .driver_data = (void *)&surftab_wintron70_st70416_6_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Shenzhen PLOYER"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MOMO7W"),
+ /* Exact match, different versions need different fw */
+ DMI_MATCH(DMI_BIOS_VERSION, "MOMO.G.WI71C.MABMRBA02"),
+ },
+ },
+ {
+ /* GP-electronic T701 */
+ .driver_data = (void *)&gp_electronic_t701_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
+ DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
+ },
+ },
+ {
+ /* Pipo W2S */
+ .driver_data = (void *)&pipo_w2s_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "PIPO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "W2S"),
+ },
+ },
+ {
+ /* Point of View mobii wintab p800w */
+ .driver_data = (void *)&pov_mobii_wintab_p800w_data,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ DMI_MATCH(DMI_BIOS_VERSION, "3BAIR1013"),
+ /* Above matches are too generic, add bios-date match */
+ DMI_MATCH(DMI_BIOS_DATE, "08/22/2014"),
+ },
+ },
+ {
+ /* I.T.Works TW891 */
+ .driver_data = (void *)&itworks_tw891_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TW891"),
+ },
+ },
{ },
};
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index aa2ee51d3547..a16cea2be9c3 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -222,7 +222,7 @@ struct sony_laptop_keypress {
/* Correspondance table between sonypi events
* and input layer indexes in the keymap
*/
-static int sony_laptop_input_index[] = {
+static const int sony_laptop_input_index[] = {
-1, /* 0 no event */
-1, /* 1 SONYPI_EVENT_JOGDIAL_DOWN */
-1, /* 2 SONYPI_EVENT_JOGDIAL_UP */
@@ -4032,7 +4032,7 @@ static struct attribute *spic_attributes[] = {
NULL
};
-static struct attribute_group spic_attribute_group = {
+static const struct attribute_group spic_attribute_group = {
.attrs = spic_attributes
};
@@ -4880,7 +4880,7 @@ static struct acpi_driver sony_pic_driver = {
.drv.pm = &sony_pic_pm,
};
-static struct dmi_system_id __initdata sonypi_dmi_table[] = {
+static const struct dmi_system_id sonypi_dmi_table[] __initconst = {
{
.ident = "Sony Vaio",
.matches = {
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 7b6cb0c69b02..2242d6035d9e 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -24,7 +24,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#define TPACPI_VERSION "0.25"
-#define TPACPI_SYSFS_VERSION 0x020700
+#define TPACPI_SYSFS_VERSION 0x030000
/*
* Changelog:
@@ -590,8 +590,8 @@ static int acpi_evalf(acpi_handle handle,
break;
/* add more types as needed */
default:
- pr_err("acpi_evalf() called "
- "with invalid format character '%c'\n", c);
+ pr_err("acpi_evalf() called with invalid format character '%c'\n",
+ c);
va_end(ap);
return 0;
}
@@ -619,8 +619,8 @@ static int acpi_evalf(acpi_handle handle,
break;
/* add more types as needed */
default:
- pr_err("acpi_evalf() called "
- "with invalid format character '%c'\n", res_type);
+ pr_err("acpi_evalf() called with invalid format character '%c'\n",
+ res_type);
return 0;
}
@@ -790,8 +790,8 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm)
ibm->acpi->type, dispatch_acpi_notify, ibm);
if (ACPI_FAILURE(status)) {
if (status == AE_ALREADY_EXISTS) {
- pr_notice("another device driver is already "
- "handling %s events\n", ibm->name);
+ pr_notice("another device driver is already handling %s events\n",
+ ibm->name);
} else {
pr_err("acpi_install_notify_handler(%s) failed: %s\n",
ibm->name, acpi_format_exception(status));
@@ -1095,8 +1095,7 @@ static void printk_deprecated_attribute(const char * const what,
const char * const details)
{
tpacpi_log_usertask("deprecated sysfs attribute");
- pr_warn("WARNING: sysfs attribute %s is deprecated and "
- "will be removed. %s\n",
+ pr_warn("WARNING: sysfs attribute %s is deprecated and will be removed. %s\n",
what, details);
}
@@ -1438,25 +1437,20 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf)
*/
/* interface_version --------------------------------------------------- */
-static ssize_t tpacpi_driver_interface_version_show(
- struct device_driver *drv,
- char *buf)
+static ssize_t interface_version_show(struct device_driver *drv, char *buf)
{
return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);
}
-
-static DRIVER_ATTR(interface_version, S_IRUGO,
- tpacpi_driver_interface_version_show, NULL);
+static DRIVER_ATTR_RO(interface_version);
/* debug_level --------------------------------------------------------- */
-static ssize_t tpacpi_driver_debug_show(struct device_driver *drv,
- char *buf)
+static ssize_t debug_level_show(struct device_driver *drv, char *buf)
{
return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);
}
-static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,
- const char *buf, size_t count)
+static ssize_t debug_level_store(struct device_driver *drv, const char *buf,
+ size_t count)
{
unsigned long t;
@@ -1467,34 +1461,28 @@ static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,
return count;
}
-
-static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
- tpacpi_driver_debug_show, tpacpi_driver_debug_store);
+static DRIVER_ATTR_RW(debug_level);
/* version ------------------------------------------------------------- */
-static ssize_t tpacpi_driver_version_show(struct device_driver *drv,
- char *buf)
+static ssize_t version_show(struct device_driver *drv, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s v%s\n",
TPACPI_DESC, TPACPI_VERSION);
}
-
-static DRIVER_ATTR(version, S_IRUGO,
- tpacpi_driver_version_show, NULL);
+static DRIVER_ATTR_RO(version);
/* --------------------------------------------------------------------- */
#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
/* wlsw_emulstate ------------------------------------------------------ */
-static ssize_t tpacpi_driver_wlsw_emulstate_show(struct device_driver *drv,
- char *buf)
+static ssize_t wlsw_emulstate_show(struct device_driver *drv, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wlsw_emulstate);
}
-static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv,
- const char *buf, size_t count)
+static ssize_t wlsw_emulstate_store(struct device_driver *drv, const char *buf,
+ size_t count)
{
unsigned long t;
@@ -1508,22 +1496,16 @@ static ssize_t tpacpi_driver_wlsw_emulstate_store(struct device_driver *drv,
return count;
}
-
-static DRIVER_ATTR(wlsw_emulstate, S_IWUSR | S_IRUGO,
- tpacpi_driver_wlsw_emulstate_show,
- tpacpi_driver_wlsw_emulstate_store);
+static DRIVER_ATTR_RW(wlsw_emulstate);
/* bluetooth_emulstate ------------------------------------------------- */
-static ssize_t tpacpi_driver_bluetooth_emulstate_show(
- struct device_driver *drv,
- char *buf)
+static ssize_t bluetooth_emulstate_show(struct device_driver *drv, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_bluetooth_emulstate);
}
-static ssize_t tpacpi_driver_bluetooth_emulstate_store(
- struct device_driver *drv,
- const char *buf, size_t count)
+static ssize_t bluetooth_emulstate_store(struct device_driver *drv,
+ const char *buf, size_t count)
{
unsigned long t;
@@ -1534,22 +1516,16 @@ static ssize_t tpacpi_driver_bluetooth_emulstate_store(
return count;
}
-
-static DRIVER_ATTR(bluetooth_emulstate, S_IWUSR | S_IRUGO,
- tpacpi_driver_bluetooth_emulstate_show,
- tpacpi_driver_bluetooth_emulstate_store);
+static DRIVER_ATTR_RW(bluetooth_emulstate);
/* wwan_emulstate ------------------------------------------------- */
-static ssize_t tpacpi_driver_wwan_emulstate_show(
- struct device_driver *drv,
- char *buf)
+static ssize_t wwan_emulstate_show(struct device_driver *drv, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_wwan_emulstate);
}
-static ssize_t tpacpi_driver_wwan_emulstate_store(
- struct device_driver *drv,
- const char *buf, size_t count)
+static ssize_t wwan_emulstate_store(struct device_driver *drv, const char *buf,
+ size_t count)
{
unsigned long t;
@@ -1560,22 +1536,16 @@ static ssize_t tpacpi_driver_wwan_emulstate_store(
return count;
}
-
-static DRIVER_ATTR(wwan_emulstate, S_IWUSR | S_IRUGO,
- tpacpi_driver_wwan_emulstate_show,
- tpacpi_driver_wwan_emulstate_store);
+static DRIVER_ATTR_RW(wwan_emulstate);
/* uwb_emulstate ------------------------------------------------- */
-static ssize_t tpacpi_driver_uwb_emulstate_show(
- struct device_driver *drv,
- char *buf)
+static ssize_t uwb_emulstate_show(struct device_driver *drv, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_uwb_emulstate);
}
-static ssize_t tpacpi_driver_uwb_emulstate_store(
- struct device_driver *drv,
- const char *buf, size_t count)
+static ssize_t uwb_emulstate_store(struct device_driver *drv, const char *buf,
+ size_t count)
{
unsigned long t;
@@ -1586,10 +1556,7 @@ static ssize_t tpacpi_driver_uwb_emulstate_store(
return count;
}
-
-static DRIVER_ATTR(uwb_emulstate, S_IWUSR | S_IRUGO,
- tpacpi_driver_uwb_emulstate_show,
- tpacpi_driver_uwb_emulstate_store);
+static DRIVER_ATTR_RW(uwb_emulstate);
#endif
/* --------------------------------------------------------------------- */
@@ -1828,8 +1795,7 @@ static void __init tpacpi_check_outdated_fw(void)
* best if the user upgrades the firmware anyway.
*/
pr_warn("WARNING: Outdated ThinkPad BIOS/EC firmware\n");
- pr_warn("WARNING: This firmware may be missing critical bug "
- "fixes and/or important features\n");
+ pr_warn("WARNING: This firmware may be missing critical bug fixes and/or important features\n");
}
}
@@ -2198,8 +2164,7 @@ static int hotkey_mask_set(u32 mask)
* a given event.
*/
if (!hotkey_mask_get() && !rc && (fwmask & ~hotkey_acpi_mask)) {
- pr_notice("asked for hotkey mask 0x%08x, but "
- "firmware forced it to 0x%08x\n",
+ pr_notice("asked for hotkey mask 0x%08x, but firmware forced it to 0x%08x\n",
fwmask, hotkey_acpi_mask);
}
@@ -2224,11 +2189,9 @@ static int hotkey_user_mask_set(const u32 mask)
(mask == 0xffff || mask == 0xffffff ||
mask == 0xffffffff)) {
tp_warned.hotkey_mask_ff = 1;
- pr_notice("setting the hotkey mask to 0x%08x is likely "
- "not the best way to go about it\n", mask);
- pr_notice("please consider using the driver defaults, "
- "and refer to up-to-date thinkpad-acpi "
- "documentation\n");
+ pr_notice("setting the hotkey mask to 0x%08x is likely not the best way to go about it\n",
+ mask);
+ pr_notice("please consider using the driver defaults, and refer to up-to-date thinkpad-acpi documentation\n");
}
/* Try to enable what the user asked for, plus whatever we need.
@@ -2603,17 +2566,14 @@ static void hotkey_poll_setup(const bool may_warn)
NULL, TPACPI_NVRAM_KTHREAD_NAME);
if (IS_ERR(tpacpi_hotkey_task)) {
tpacpi_hotkey_task = NULL;
- pr_err("could not create kernel thread "
- "for hotkey polling\n");
+ pr_err("could not create kernel thread for hotkey polling\n");
}
}
} else {
hotkey_poll_stop_sync();
if (may_warn && (poll_driver_mask || poll_user_mask) &&
hotkey_poll_freq == 0) {
- pr_notice("hot keys 0x%08x and/or events 0x%08x "
- "require polling, which is currently "
- "disabled\n",
+ pr_notice("hot keys 0x%08x and/or events 0x%08x require polling, which is currently disabled\n",
poll_user_mask, poll_driver_mask);
}
}
@@ -2840,12 +2800,10 @@ static ssize_t hotkey_source_mask_store(struct device *dev,
mutex_unlock(&hotkey_mutex);
if (rc < 0)
- pr_err("hotkey_source_mask: "
- "failed to update the firmware event mask!\n");
+ pr_err("hotkey_source_mask: failed to update the firmware event mask!\n");
if (r_ev)
- pr_notice("hotkey_source_mask: "
- "some important events were disabled: 0x%04x\n",
+ pr_notice("hotkey_source_mask: some important events were disabled: 0x%04x\n",
r_ev);
tpacpi_disclose_usertask("hotkey_source_mask", "set to 0x%08lx\n", t);
@@ -3106,8 +3064,7 @@ static void hotkey_exit(void)
if (((tp_features.hotkey_mask &&
hotkey_mask_set(hotkey_orig_mask)) |
hotkey_status_set(false)) != 0)
- pr_err("failed to restore hot key mask "
- "to BIOS defaults\n");
+ pr_err("failed to restore hot key mask to BIOS defaults\n");
}
static void __init hotkey_unmap(const unsigned int scancode)
@@ -3619,11 +3576,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
* userspace. tpacpi_detect_brightness_capabilities() must have
* been called before this point */
if (acpi_video_get_backlight_type() != acpi_backlight_vendor) {
- pr_info("This ThinkPad has standard ACPI backlight "
- "brightness control, supported by the ACPI "
- "video driver\n");
- pr_notice("Disabling thinkpad-acpi brightness events "
- "by default...\n");
+ pr_info("This ThinkPad has standard ACPI backlight brightness control, supported by the ACPI video driver\n");
+ pr_notice("Disabling thinkpad-acpi brightness events by default...\n");
/* Disable brightness up/down on Lenovo thinkpads when
* ACPI is handling them, otherwise it is plain impossible
@@ -3792,7 +3746,7 @@ static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)
TP_ACPI_HOTKEYSCAN_EXTENDED_START -
TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) {
pr_info("Unhandled adaptive keyboard key: 0x%x\n",
- scancode);
+ scancode);
return false;
}
keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY +
@@ -3989,14 +3943,12 @@ static bool hotkey_notify_6xxx(const u32 hkey,
/* recommended action: immediate sleep/hibernate */
break;
case TP_HKEY_EV_ALARM_SENSOR_HOT:
- pr_crit("THERMAL ALARM: "
- "a sensor reports something is too hot!\n");
+ pr_crit("THERMAL ALARM: a sensor reports something is too hot!\n");
/* recommended action: warn user through gui, that */
/* some internal component is too hot */
break;
case TP_HKEY_EV_ALARM_SENSOR_XHOT:
- pr_alert("THERMAL EMERGENCY: "
- "a sensor reports something is extremely hot!\n");
+ pr_alert("THERMAL EMERGENCY: a sensor reports something is extremely hot!\n");
/* recommended action: immediate sleep/hibernate */
break;
case TP_HKEY_EV_AC_CHANGED:
@@ -4121,8 +4073,8 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
}
if (!known_ev) {
pr_notice("unhandled HKEY event 0x%04x\n", hkey);
- pr_notice("please report the conditions when this "
- "event happened to %s\n", TPACPI_MAIL);
+ pr_notice("please report the conditions when this event happened to %s\n",
+ TPACPI_MAIL);
}
/* netlink events */
@@ -4156,8 +4108,7 @@ static void hotkey_resume(void)
if (hotkey_status_set(true) < 0 ||
hotkey_mask_set(hotkey_acpi_mask) < 0)
- pr_err("error while attempting to reset the event "
- "firmware interface\n");
+ pr_err("error while attempting to reset the event firmware interface\n");
tpacpi_send_radiosw_update();
hotkey_tablet_mode_notify_change();
@@ -4209,12 +4160,8 @@ static void hotkey_enabledisable_warn(bool enable)
{
tpacpi_log_usertask("procfs hotkey enable/disable");
if (!WARN((tpacpi_lifecycle == TPACPI_LIFE_RUNNING || !enable),
- pr_fmt("hotkey enable/disable functionality has been "
- "removed from the driver. "
- "Hotkeys are always enabled.\n")))
- pr_err("Please remove the hotkey=enable module "
- "parameter, it is deprecated. "
- "Hotkeys are always enabled.\n");
+ pr_fmt("hotkey enable/disable functionality has been removed from the driver. Hotkeys are always enabled.\n")))
+ pr_err("Please remove the hotkey=enable module parameter, it is deprecated. Hotkeys are always enabled.\n");
}
static int hotkey_write(char *buf)
@@ -4872,8 +4819,7 @@ static void video_exit(void)
dbg_printk(TPACPI_DBG_EXIT,
"restoring original video autoswitch mode\n");
if (video_autosw_set(video_orig_autosw))
- pr_err("error while trying to restore original "
- "video autoswitch mode\n");
+ pr_err("error while trying to restore original video autoswitch mode\n");
}
static int video_outputsw_get(void)
@@ -5963,8 +5909,7 @@ static int __init led_init(struct ibm_init_struct *iibm)
}
#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
- pr_notice("warning: userspace override of important "
- "firmware LEDs is enabled\n");
+ pr_notice("warning: userspace override of important firmware LEDs is enabled\n");
#endif
return 0;
}
@@ -5993,8 +5938,7 @@ static int led_read(struct seq_file *m)
}
}
- seq_printf(m, "commands:\t"
- "<led> on, <led> off, <led> blink (<led> is 0-15)\n");
+ seq_printf(m, "commands:\t<led> on, <led> off, <led> blink (<led> is 0-15)\n");
return 0;
}
@@ -6367,13 +6311,10 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
if (ta1 == 0) {
/* This is sheer paranoia, but we handle it anyway */
if (acpi_tmp7) {
- pr_err("ThinkPad ACPI EC access misbehaving, "
- "falling back to ACPI TMPx access "
- "mode\n");
+ pr_err("ThinkPad ACPI EC access misbehaving, falling back to ACPI TMPx access mode\n");
thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
} else {
- pr_err("ThinkPad ACPI EC access misbehaving, "
- "disabling thermal sensors access\n");
+ pr_err("ThinkPad ACPI EC access misbehaving, disabling thermal sensors access\n");
thermal_read_mode = TPACPI_THERMAL_NONE;
}
} else {
@@ -6401,7 +6342,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
switch (thermal_read_mode) {
case TPACPI_THERMAL_TPEC_16:
- res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
+ res = sysfs_create_group(&tpacpi_hwmon->kobj,
&thermal_temp_input16_group);
if (res)
return res;
@@ -6409,7 +6350,7 @@ static int __init thermal_init(struct ibm_init_struct *iibm)
case TPACPI_THERMAL_TPEC_8:
case TPACPI_THERMAL_ACPI_TMP07:
case TPACPI_THERMAL_ACPI_UPDT:
- res = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
+ res = sysfs_create_group(&tpacpi_hwmon->kobj,
&thermal_temp_input8_group);
if (res)
return res;
@@ -6426,13 +6367,13 @@ static void thermal_exit(void)
{
switch (thermal_read_mode) {
case TPACPI_THERMAL_TPEC_16:
- sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
+ sysfs_remove_group(&tpacpi_hwmon->kobj,
&thermal_temp_input16_group);
break;
case TPACPI_THERMAL_TPEC_8:
case TPACPI_THERMAL_ACPI_TMP07:
case TPACPI_THERMAL_ACPI_UPDT:
- sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
+ sysfs_remove_group(&tpacpi_hwmon->kobj,
&thermal_temp_input8_group);
break;
case TPACPI_THERMAL_NONE:
@@ -6852,26 +6793,20 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
if (!brightness_enable) {
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
- "brightness support disabled by "
- "module parameter\n");
+ "brightness support disabled by module parameter\n");
return 1;
}
if (acpi_video_get_backlight_type() != acpi_backlight_vendor) {
if (brightness_enable > 1) {
- pr_info("Standard ACPI backlight interface "
- "available, not loading native one\n");
+ pr_info("Standard ACPI backlight interface available, not loading native one\n");
return 1;
} else if (brightness_enable == 1) {
- pr_warn("Cannot enable backlight brightness support, "
- "ACPI is already handling it. Refer to the "
- "acpi_backlight kernel parameter.\n");
+ pr_warn("Cannot enable backlight brightness support, ACPI is already handling it. Refer to the acpi_backlight kernel parameter.\n");
return 1;
}
} else if (tp_features.bright_acpimode && brightness_enable > 1) {
- pr_notice("Standard ACPI backlight interface not "
- "available, thinkpad_acpi native "
- "brightness control enabled\n");
+ pr_notice("Standard ACPI backlight interface not available, thinkpad_acpi native brightness control enabled\n");
}
/*
@@ -6922,10 +6857,10 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
"brightness is supported\n");
if (quirks & TPACPI_BRGHT_Q_ASK) {
- pr_notice("brightness: will use unverified default: "
- "brightness_mode=%d\n", brightness_mode);
- pr_notice("brightness: please report to %s whether it works well "
- "or not on your ThinkPad\n", TPACPI_MAIL);
+ pr_notice("brightness: will use unverified default: brightness_mode=%d\n",
+ brightness_mode);
+ pr_notice("brightness: please report to %s whether it works well or not on your ThinkPad\n",
+ TPACPI_MAIL);
}
/* Added by mistake in early 2007. Probably useless, but it could
@@ -6935,8 +6870,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
backlight_update_status(ibm_backlight_device);
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_BRGHT,
- "brightness: registering brightness hotkeys "
- "as change notification\n");
+ "brightness: registering brightness hotkeys as change notification\n");
tpacpi_hotkey_driver_mask_set(hotkey_driver_mask
| TP_ACPI_HKEY_BRGHTUP_MASK
| TP_ACPI_HKEY_BRGHTDWN_MASK);
@@ -7599,8 +7533,8 @@ static int __init volume_init(struct ibm_init_struct *iibm)
return -EINVAL;
if (volume_mode == TPACPI_VOL_MODE_UCMS_STEP) {
- pr_err("UCMS step volume mode not implemented, "
- "please contact %s\n", TPACPI_MAIL);
+ pr_err("UCMS step volume mode not implemented, please contact %s\n",
+ TPACPI_MAIL);
return 1;
}
@@ -7613,8 +7547,7 @@ static int __init volume_init(struct ibm_init_struct *iibm)
*/
if (!alsa_enable) {
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_MIXER,
- "ALSA mixer disabled by parameter, "
- "not loading volume subdriver...\n");
+ "ALSA mixer disabled by parameter, not loading volume subdriver...\n");
return 1;
}
@@ -7706,12 +7639,9 @@ static int volume_read(struct seq_file *m)
if (volume_control_allowed) {
seq_printf(m, "commands:\tunmute, mute\n");
if (!tp_features.mixer_no_level_control) {
- seq_printf(m,
- "commands:\tup, down\n");
- seq_printf(m,
- "commands:\tlevel <level>"
- " (<level> is 0-%d)\n",
- TP_EC_VOLUME_MAX);
+ seq_printf(m, "commands:\tup, down\n");
+ seq_printf(m, "commands:\tlevel <level> (<level> is 0-%d)\n",
+ TP_EC_VOLUME_MAX);
}
}
}
@@ -7734,10 +7664,8 @@ static int volume_write(char *buf)
if (!volume_control_allowed && tpacpi_lifecycle != TPACPI_LIFE_INIT) {
if (unlikely(!tp_warned.volume_ctrl_forbidden)) {
tp_warned.volume_ctrl_forbidden = 1;
- pr_notice("Console audio control in monitor mode, "
- "changes are not allowed\n");
- pr_notice("Use the volume_control=1 module parameter "
- "to enable volume control\n");
+ pr_notice("Console audio control in monitor mode, changes are not allowed\n");
+ pr_notice("Use the volume_control=1 module parameter to enable volume control\n");
}
return -EPERM;
}
@@ -8019,8 +7947,7 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
static void fan_quirk1_setup(void)
{
if (fan_control_initial_status == 0x07) {
- pr_notice("fan_init: initial fan status is unknown, "
- "assuming it is in auto mode\n");
+ pr_notice("fan_init: initial fan status is unknown, assuming it is in auto mode\n");
tp_features.fan_ctrl_status_undef = 1;
}
}
@@ -8417,8 +8344,8 @@ static void fan_watchdog_fire(struct work_struct *ignored)
pr_notice("fan watchdog: enabling fan\n");
rc = fan_set_enable();
if (rc < 0) {
- pr_err("fan watchdog: error %d while enabling fan, "
- "will try again later...\n", -rc);
+ pr_err("fan watchdog: error %d while enabling fan, will try again later...\n",
+ rc);
/* reschedule for later */
fan_watchdog_reset();
}
@@ -8606,14 +8533,13 @@ static ssize_t fan_fan2_input_show(struct device *dev,
static DEVICE_ATTR(fan2_input, S_IRUGO, fan_fan2_input_show, NULL);
/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
-static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
- char *buf)
+static ssize_t fan_watchdog_show(struct device_driver *drv, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);
}
-static ssize_t fan_fan_watchdog_store(struct device_driver *drv,
- const char *buf, size_t count)
+static ssize_t fan_watchdog_store(struct device_driver *drv, const char *buf,
+ size_t count)
{
unsigned long t;
@@ -8630,9 +8556,7 @@ static ssize_t fan_fan_watchdog_store(struct device_driver *drv,
return count;
}
-
-static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
- fan_fan_watchdog_show, fan_fan_watchdog_store);
+static DRIVER_ATTR_RW(fan_watchdog);
/* --------------------------------------------------------------------- */
static struct attribute *fan_attributes[] = {
@@ -8715,8 +8639,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
"secondary fan support enabled\n");
}
} else {
- pr_err("ThinkPad ACPI EC access misbehaving, "
- "fan status and control unavailable\n");
+ pr_err("ThinkPad ACPI EC access misbehaving, fan status and control unavailable\n");
return 1;
}
}
@@ -8773,7 +8696,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
fan_attributes[ARRAY_SIZE(fan_attributes)-2] =
&dev_attr_fan2_input.attr;
}
- rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
+ rc = sysfs_create_group(&tpacpi_hwmon->kobj,
&fan_attr_group);
if (rc < 0)
return rc;
@@ -8781,7 +8704,7 @@ static int __init fan_init(struct ibm_init_struct *iibm)
rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
&driver_attr_fan_watchdog);
if (rc < 0) {
- sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
+ sysfs_remove_group(&tpacpi_hwmon->kobj,
&fan_attr_group);
return rc;
}
@@ -8796,7 +8719,7 @@ static void fan_exit(void)
"cancelling any pending fan watchdog tasks\n");
/* FIXME: can we really do this unconditionally? */
- sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group);
+ sysfs_remove_group(&tpacpi_hwmon->kobj, &fan_attr_group);
driver_remove_file(&tpacpi_hwmon_pdriver.driver,
&driver_attr_fan_watchdog);
@@ -8815,8 +8738,8 @@ static void fan_suspend(void)
fan_control_resume_level = 0;
rc = fan_get_status_safe(&fan_control_resume_level);
if (rc < 0)
- pr_notice("failed to read fan level for later "
- "restore during resume: %d\n", rc);
+ pr_notice("failed to read fan level for later restore during resume: %d\n",
+ rc);
/* if it is undefined, don't attempt to restore it.
* KEEP THIS LAST */
@@ -8935,20 +8858,17 @@ static int fan_read(struct seq_file *m)
break;
default:
- seq_printf(m, " (<level> is 0-7, "
- "auto, disengaged, full-speed)\n");
+ seq_printf(m, " (<level> is 0-7, auto, disengaged, full-speed)\n");
break;
}
}
if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
seq_printf(m, "commands:\tenable, disable\n"
- "commands:\twatchdog <timeout> (<timeout> "
- "is 0 (off), 1-120 (seconds))\n");
+ "commands:\twatchdog <timeout> (<timeout> is 0 (off), 1-120 (seconds))\n");
if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
- seq_printf(m, "commands:\tspeed <speed>"
- " (<speed> is 0-65535)\n");
+ seq_printf(m, "commands:\tspeed <speed> (<speed> is 0-65535)\n");
return 0;
}
@@ -9229,16 +9149,6 @@ static void hotkey_driver_event(const unsigned int scancode)
tpacpi_driver_event(TP_HKEY_EV_HOTKEY_BASE + scancode);
}
-/* sysfs name ---------------------------------------------------------- */
-static ssize_t thinkpad_acpi_pdev_name_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%s\n", TPACPI_NAME);
-}
-
-static DEVICE_ATTR(name, S_IRUGO, thinkpad_acpi_pdev_name_show, NULL);
-
/* --------------------------------------------------------------------- */
/* /proc support */
@@ -9474,8 +9384,7 @@ static int __must_check __init get_thinkpad_model_data(
tp->ec_release = (ec_fw_string[4] << 8)
| ec_fw_string[5];
} else {
- pr_notice("ThinkPad firmware release %s "
- "doesn't match the known patterns\n",
+ pr_notice("ThinkPad firmware release %s doesn't match the known patterns\n",
ec_fw_string);
pr_notice("please report this to %s\n",
TPACPI_MAIL);
@@ -9670,8 +9579,7 @@ MODULE_PARM_DESC(debug, "Sets debug level bit-mask");
module_param(force_load, bool, 0444);
MODULE_PARM_DESC(force_load,
- "Attempts to load the driver even on a "
- "mis-identified ThinkPad when true");
+ "Attempts to load the driver even on a mis-identified ThinkPad when true");
module_param_named(fan_control, fan_control_allowed, bool, 0444);
MODULE_PARM_DESC(fan_control,
@@ -9679,8 +9587,7 @@ MODULE_PARM_DESC(fan_control,
module_param_named(brightness_mode, brightness_mode, uint, 0444);
MODULE_PARM_DESC(brightness_mode,
- "Selects brightness control strategy: "
- "0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM");
+ "Selects brightness control strategy: 0=auto, 1=EC, 2=UCMS, 3=EC+NVRAM");
module_param(brightness_enable, uint, 0444);
MODULE_PARM_DESC(brightness_enable,
@@ -9689,18 +9596,15 @@ MODULE_PARM_DESC(brightness_enable,
#ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT
module_param_named(volume_mode, volume_mode, uint, 0444);
MODULE_PARM_DESC(volume_mode,
- "Selects volume control strategy: "
- "0=auto, 1=EC, 2=N/A, 3=EC+NVRAM");
+ "Selects volume control strategy: 0=auto, 1=EC, 2=N/A, 3=EC+NVRAM");
module_param_named(volume_capabilities, volume_capabilities, uint, 0444);
MODULE_PARM_DESC(volume_capabilities,
- "Selects the mixer capabilites: "
- "0=auto, 1=volume and mute, 2=mute only");
+ "Selects the mixer capabilites: 0=auto, 1=volume and mute, 2=mute only");
module_param_named(volume_control, volume_control_allowed, bool, 0444);
MODULE_PARM_DESC(volume_control,
- "Enables software override for the console audio "
- "control when true");
+ "Enables software override for the console audio control when true");
module_param_named(software_mute, software_mute_requested, bool, 0444);
MODULE_PARM_DESC(software_mute,
@@ -9715,10 +9619,10 @@ module_param_named(enable, alsa_enable, bool, 0444);
MODULE_PARM_DESC(enable, "Enable the ALSA interface for the ACPI EC Mixer");
#endif /* CONFIG_THINKPAD_ACPI_ALSA_SUPPORT */
+/* The module parameter can't be read back, that's why 0 is used here */
#define TPACPI_PARAM(feature) \
module_param_call(feature, set_ibm_param, NULL, NULL, 0); \
- MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command " \
- "at module load, see documentation")
+ MODULE_PARM_DESC(feature, "Simulates thinkpad-acpi procfs command at module load, see documentation")
TPACPI_PARAM(hotkey);
TPACPI_PARAM(bluetooth);
@@ -9782,8 +9686,6 @@ static void thinkpad_acpi_module_exit(void)
if (tpacpi_hwmon)
hwmon_device_unregister(tpacpi_hwmon);
- if (tp_features.sensors_pdev_attrs_registered)
- device_remove_file(&tpacpi_sensors_pdev->dev, &dev_attr_name);
if (tpacpi_sensors_pdev)
platform_device_unregister(tpacpi_sensors_pdev);
if (tpacpi_pdev)
@@ -9904,14 +9806,10 @@ static int __init thinkpad_acpi_module_init(void)
thinkpad_acpi_module_exit();
return ret;
}
- ret = device_create_file(&tpacpi_sensors_pdev->dev, &dev_attr_name);
- if (ret) {
- pr_err("unable to create sysfs hwmon device attributes\n");
- thinkpad_acpi_module_exit();
- return ret;
- }
tp_features.sensors_pdev_attrs_registered = 1;
- tpacpi_hwmon = hwmon_device_register(&tpacpi_sensors_pdev->dev);
+ tpacpi_hwmon = hwmon_device_register_with_groups(
+ &tpacpi_sensors_pdev->dev, TPACPI_NAME, NULL, NULL);
+
if (IS_ERR(tpacpi_hwmon)) {
ret = PTR_ERR(tpacpi_hwmon);
tpacpi_hwmon = NULL;
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c
index 70205d222da9..1032c00b907b 100644
--- a/drivers/platform/x86/topstar-laptop.c
+++ b/drivers/platform/x86/topstar-laptop.c
@@ -162,6 +162,7 @@ static int acpi_topstar_remove(struct acpi_device *device)
}
static const struct acpi_device_id topstar_device_ids[] = {
+ { "TPS0001", 0 },
{ "TPSACPI01", 0 },
{ "", 0 },
};
diff --git a/drivers/platform/x86/toshiba-wmi.c b/drivers/platform/x86/toshiba-wmi.c
index 440528676170..03d7620cd6d7 100644
--- a/drivers/platform/x86/toshiba-wmi.c
+++ b/drivers/platform/x86/toshiba-wmi.c
@@ -64,7 +64,7 @@ static void toshiba_wmi_notify(u32 value, void *context)
kfree(response.pointer);
}
-static struct dmi_system_id toshiba_wmi_dmi_table[] __initdata = {
+static const struct dmi_system_id toshiba_wmi_dmi_table[] __initconst = {
{
.ident = "Toshiba laptop",
.matches = {
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index d0daf75cbed1..bb1dcd7fbdeb 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -1502,14 +1502,9 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
int ret;
u32 video_out;
- cmd = kmalloc(count + 1, GFP_KERNEL);
- if (!cmd)
- return -ENOMEM;
- if (copy_from_user(cmd, buf, count)) {
- kfree(cmd);
- return -EFAULT;
- }
- cmd[count] = '\0';
+ cmd = memdup_user_nul(buf, count);
+ if (IS_ERR(cmd))
+ return PTR_ERR(cmd);
buffer = cmd;
@@ -2424,7 +2419,7 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
return exists ? attr->mode : 0;
}
-static struct attribute_group toshiba_attr_group = {
+static const struct attribute_group toshiba_attr_group = {
.is_visible = toshiba_sysfs_is_visible,
.attrs = toshiba_attributes,
};
diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c
index b3dec521e2b6..fb2736602558 100644
--- a/drivers/platform/x86/toshiba_haps.c
+++ b/drivers/platform/x86/toshiba_haps.c
@@ -132,7 +132,7 @@ static struct attribute *haps_attributes[] = {
NULL,
};
-static struct attribute_group haps_attr_group = {
+static const struct attribute_group haps_attr_group = {
.attrs = haps_attributes,
};
diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c
new file mode 100644
index 000000000000..c4530ba715e8
--- /dev/null
+++ b/drivers/platform/x86/wmi-bmof.c
@@ -0,0 +1,125 @@
+/*
+ * WMI embedded Binary MOF driver
+ *
+ * Copyright (c) 2015 Andrew Lutomirski
+ * Copyright (C) 2017 VMware, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/wmi.h>
+
+#define WMI_BMOF_GUID "05901221-D566-11D1-B2F0-00A0C9062910"
+
+struct bmof_priv {
+ union acpi_object *bmofdata;
+ struct bin_attribute bmof_bin_attr;
+};
+
+static ssize_t
+read_bmof(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct bmof_priv *priv =
+ container_of(attr, struct bmof_priv, bmof_bin_attr);
+
+ if (off < 0)
+ return -EINVAL;
+
+ if (off >= priv->bmofdata->buffer.length)
+ return 0;
+
+ if (count > priv->bmofdata->buffer.length - off)
+ count = priv->bmofdata->buffer.length - off;
+
+ memcpy(buf, priv->bmofdata->buffer.pointer + off, count);
+ return count;
+}
+
+static int wmi_bmof_probe(struct wmi_device *wdev)
+{
+ struct bmof_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&wdev->dev, sizeof(struct bmof_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&wdev->dev, priv);
+
+ priv->bmofdata = wmidev_block_query(wdev, 0);
+ if (!priv->bmofdata) {
+ dev_err(&wdev->dev, "failed to read Binary MOF\n");
+ return -EIO;
+ }
+
+ if (priv->bmofdata->type != ACPI_TYPE_BUFFER) {
+ dev_err(&wdev->dev, "Binary MOF is not a buffer\n");
+ ret = -EIO;
+ goto err_free;
+ }
+
+ sysfs_bin_attr_init(&priv->bmof_bin_attr);
+ priv->bmof_bin_attr.attr.name = "bmof";
+ priv->bmof_bin_attr.attr.mode = 0400;
+ priv->bmof_bin_attr.read = read_bmof;
+ priv->bmof_bin_attr.size = priv->bmofdata->buffer.length;
+
+ ret = sysfs_create_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr);
+ if (ret)
+ goto err_free;
+
+ return 0;
+
+ err_free:
+ kfree(priv->bmofdata);
+ return ret;
+}
+
+static int wmi_bmof_remove(struct wmi_device *wdev)
+{
+ struct bmof_priv *priv = dev_get_drvdata(&wdev->dev);
+
+ sysfs_remove_bin_file(&wdev->dev.kobj, &priv->bmof_bin_attr);
+ kfree(priv->bmofdata);
+ return 0;
+}
+
+static const struct wmi_device_id wmi_bmof_id_table[] = {
+ { .guid_string = WMI_BMOF_GUID },
+ { },
+};
+
+static struct wmi_driver wmi_bmof_driver = {
+ .driver = {
+ .name = "wmi-bmof",
+ },
+ .probe = wmi_bmof_probe,
+ .remove = wmi_bmof_remove,
+ .id_table = wmi_bmof_id_table,
+};
+
+module_wmi_driver(wmi_bmof_driver);
+
+MODULE_ALIAS("wmi:" WMI_BMOF_GUID);
+MODULE_AUTHOR("Andrew Lutomirski <luto@kernel.org>");
+MODULE_DESCRIPTION("WMI embedded Binary MOF driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index ceeb8c188ef3..0765b1797d4c 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -8,6 +8,10 @@
* Copyright (c) 2001-2007 Anton Altaparmakov
* Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
*
+ * WMI bus infrastructure by Andrew Lutomirski and Darren Hart:
+ * Copyright (C) 2015 Andrew Lutomirski
+ * Copyright (C) 2017 VMware, Inc. All Rights Reserved.
+ *
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
@@ -37,6 +41,8 @@
#include <linux/acpi.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/wmi.h>
#include <linux/uuid.h>
ACPI_MODULE_NAME("wmi");
@@ -44,8 +50,6 @@ MODULE_AUTHOR("Carlos Corbacho");
MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
MODULE_LICENSE("GPL");
-#define ACPI_WMI_CLASS "wmi"
-
static LIST_HEAD(wmi_block_list);
struct guid_block {
@@ -62,12 +66,14 @@ struct guid_block {
};
struct wmi_block {
+ struct wmi_device dev;
struct list_head list;
struct guid_block gblock;
- acpi_handle handle;
+ struct acpi_device *acpi_device;
wmi_notify_handler handler;
void *handler_data;
- struct device dev;
+
+ bool read_takes_no_args;
};
@@ -90,9 +96,8 @@ module_param(debug_dump_wdg, bool, 0444);
MODULE_PARM_DESC(debug_dump_wdg,
"Dump available WMI interfaces [0/1]");
-static int acpi_wmi_remove(struct acpi_device *device);
-static int acpi_wmi_add(struct acpi_device *device);
-static void acpi_wmi_notify(struct acpi_device *device, u32 event);
+static int acpi_wmi_remove(struct platform_device *device);
+static int acpi_wmi_probe(struct platform_device *device);
static const struct acpi_device_id wmi_device_ids[] = {
{"PNP0C14", 0},
@@ -101,15 +106,13 @@ static const struct acpi_device_id wmi_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
-static struct acpi_driver acpi_wmi_driver = {
- .name = "wmi",
- .class = ACPI_WMI_CLASS,
- .ids = wmi_device_ids,
- .ops = {
- .add = acpi_wmi_add,
- .remove = acpi_wmi_remove,
- .notify = acpi_wmi_notify,
+static struct platform_driver acpi_wmi_driver = {
+ .driver = {
+ .name = "acpi-wmi",
+ .acpi_match_table = wmi_device_ids,
},
+ .probe = acpi_wmi_probe,
+ .remove = acpi_wmi_remove,
};
/*
@@ -139,6 +142,30 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
return false;
}
+static int get_subobj_info(acpi_handle handle, const char *pathname,
+ struct acpi_device_info **info)
+{
+ struct acpi_device_info *dummy_info, **info_ptr;
+ acpi_handle subobj_handle;
+ acpi_status status;
+
+ status = acpi_get_handle(handle, (char *)pathname, &subobj_handle);
+ if (status == AE_NOT_FOUND)
+ return -ENOENT;
+ else if (ACPI_FAILURE(status))
+ return -EIO;
+
+ info_ptr = info ? info : &dummy_info;
+ status = acpi_get_object_info(subobj_handle, info_ptr);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ if (!info)
+ kfree(dummy_info);
+
+ return 0;
+}
+
static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
{
struct guid_block *block = NULL;
@@ -147,7 +174,7 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
acpi_handle handle;
block = &wblock->gblock;
- handle = wblock->handle;
+ handle = wblock->acpi_device->handle;
snprintf(method, 5, "WE%02X", block->notify_id);
status = acpi_execute_simple_method(handle, method, enable);
@@ -186,12 +213,12 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
return AE_ERROR;
block = &wblock->gblock;
- handle = wblock->handle;
+ handle = wblock->acpi_device->handle;
if (!(block->flags & ACPI_WMI_METHOD))
return AE_BAD_DATA;
- if (block->instance_count < instance)
+ if (block->instance_count <= instance)
return AE_BAD_PARAMETER;
input.count = 2;
@@ -221,19 +248,10 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
}
EXPORT_SYMBOL_GPL(wmi_evaluate_method);
-/**
- * wmi_query_block - Return contents of a WMI block
- * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
- * @instance: Instance index
- * &out: Empty buffer to return the contents of the data block to
- *
- * Return the contents of an ACPI-WMI data block to a buffer
- */
-acpi_status wmi_query_block(const char *guid_string, u8 instance,
-struct acpi_buffer *out)
+static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
+ struct acpi_buffer *out)
{
struct guid_block *block = NULL;
- struct wmi_block *wblock = NULL;
acpi_handle handle;
acpi_status status, wc_status = AE_ERROR;
struct acpi_object_list input;
@@ -241,16 +259,13 @@ struct acpi_buffer *out)
char method[5];
char wc_method[5] = "WC";
- if (!guid_string || !out)
+ if (!out)
return AE_BAD_PARAMETER;
- if (!find_guid(guid_string, &wblock))
- return AE_ERROR;
-
block = &wblock->gblock;
- handle = wblock->handle;
+ handle = wblock->acpi_device->handle;
- if (block->instance_count < instance)
+ if (block->instance_count <= instance)
return AE_BAD_PARAMETER;
/* Check GUID is a data block */
@@ -262,6 +277,9 @@ struct acpi_buffer *out)
wq_params[0].type = ACPI_TYPE_INTEGER;
wq_params[0].integer.value = instance;
+ if (instance == 0 && wblock->read_takes_no_args)
+ input.count = 0;
+
/*
* If ACPI_WMI_EXPENSIVE, call the relevant WCxx method first to
* enable collection.
@@ -294,8 +312,59 @@ struct acpi_buffer *out)
return status;
}
+
+/**
+ * wmi_query_block - Return contents of a WMI block (deprecated)
+ * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
+ * @instance: Instance index
+ * &out: Empty buffer to return the contents of the data block to
+ *
+ * Return the contents of an ACPI-WMI data block to a buffer
+ */
+acpi_status wmi_query_block(const char *guid_string, u8 instance,
+ struct acpi_buffer *out)
+{
+ struct wmi_block *wblock;
+
+ if (!guid_string)
+ return AE_BAD_PARAMETER;
+
+ if (!find_guid(guid_string, &wblock))
+ return AE_ERROR;
+
+ return __query_block(wblock, instance, out);
+}
EXPORT_SYMBOL_GPL(wmi_query_block);
+union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance)
+{
+ struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev);
+
+ if (ACPI_FAILURE(__query_block(wblock, instance, &out)))
+ return NULL;
+
+ return (union acpi_object *)out.pointer;
+}
+EXPORT_SYMBOL_GPL(wmidev_block_query);
+
+struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev,
+ const char *guid_string)
+{
+ struct wmi_block *this_wb = container_of(wdev, struct wmi_block, dev);
+ struct wmi_block *other_wb;
+
+ if (!find_guid(guid_string, &other_wb))
+ return NULL;
+
+ if (other_wb->acpi_device != this_wb->acpi_device)
+ return NULL;
+
+ get_device(&other_wb->dev.dev);
+ return &other_wb->dev;
+}
+EXPORT_SYMBOL_GPL(wmidev_get_other_guid);
+
/**
* wmi_set_block - Write to a WMI block
* @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
@@ -305,7 +374,7 @@ EXPORT_SYMBOL_GPL(wmi_query_block);
* Write the contents of the input buffer to an ACPI-WMI data block
*/
acpi_status wmi_set_block(const char *guid_string, u8 instance,
-const struct acpi_buffer *in)
+ const struct acpi_buffer *in)
{
struct guid_block *block = NULL;
struct wmi_block *wblock = NULL;
@@ -321,9 +390,9 @@ const struct acpi_buffer *in)
return AE_ERROR;
block = &wblock->gblock;
- handle = wblock->handle;
+ handle = wblock->acpi_device->handle;
- if (block->instance_count < instance)
+ if (block->instance_count <= instance)
return AE_BAD_PARAMETER;
/* Check GUID is a data block */
@@ -352,9 +421,10 @@ EXPORT_SYMBOL_GPL(wmi_set_block);
static void wmi_dump_wdg(const struct guid_block *g)
{
pr_info("%pUL:\n", g->guid);
- pr_info("\tobject_id: %c%c\n", g->object_id[0], g->object_id[1]);
- pr_info("\tnotify_id: %02X\n", g->notify_id);
- pr_info("\treserved: %02X\n", g->reserved);
+ if (g->flags & ACPI_WMI_EVENT)
+ pr_info("\tnotify_id: 0x%02X\n", g->notify_id);
+ else
+ pr_info("\tobject_id: %2pE\n", g->object_id);
pr_info("\tinstance_count: %d\n", g->instance_count);
pr_info("\tflags: %#x", g->flags);
if (g->flags) {
@@ -525,8 +595,8 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
if ((gblock->flags & ACPI_WMI_EVENT) &&
(gblock->notify_id == event))
- return acpi_evaluate_object(wblock->handle, "_WED",
- &input, out);
+ return acpi_evaluate_object(wblock->acpi_device->handle,
+ "_WED", &input, out);
}
return AE_NOT_FOUND;
@@ -545,99 +615,320 @@ bool wmi_has_guid(const char *guid_string)
}
EXPORT_SYMBOL_GPL(wmi_has_guid);
+static struct wmi_block *dev_to_wblock(struct device *dev)
+{
+ return container_of(dev, struct wmi_block, dev.dev);
+}
+
+static struct wmi_device *dev_to_wdev(struct device *dev)
+{
+ return container_of(dev, struct wmi_device, dev);
+}
+
/*
* sysfs interface
*/
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct wmi_block *wblock;
-
- wblock = dev_get_drvdata(dev);
- if (!wblock) {
- strcat(buf, "\n");
- return strlen(buf);
- }
+ struct wmi_block *wblock = dev_to_wblock(dev);
return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid);
}
static DEVICE_ATTR_RO(modalias);
+static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct wmi_block *wblock = dev_to_wblock(dev);
+
+ return sprintf(buf, "%pUL\n", wblock->gblock.guid);
+}
+static DEVICE_ATTR_RO(guid);
+
+static ssize_t instance_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wmi_block *wblock = dev_to_wblock(dev);
+
+ return sprintf(buf, "%d\n", (int)wblock->gblock.instance_count);
+}
+static DEVICE_ATTR_RO(instance_count);
+
+static ssize_t expensive_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wmi_block *wblock = dev_to_wblock(dev);
+
+ return sprintf(buf, "%d\n",
+ (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0);
+}
+static DEVICE_ATTR_RO(expensive);
+
static struct attribute *wmi_attrs[] = {
&dev_attr_modalias.attr,
+ &dev_attr_guid.attr,
+ &dev_attr_instance_count.attr,
+ &dev_attr_expensive.attr,
NULL,
};
ATTRIBUTE_GROUPS(wmi);
-static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
+static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
- char guid_string[37];
+ struct wmi_block *wblock = dev_to_wblock(dev);
- struct wmi_block *wblock;
+ return sprintf(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id);
+}
+static DEVICE_ATTR_RO(notify_id);
+
+static struct attribute *wmi_event_attrs[] = {
+ &dev_attr_notify_id.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(wmi_event);
+
+static ssize_t object_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct wmi_block *wblock = dev_to_wblock(dev);
+
+ return sprintf(buf, "%c%c\n", wblock->gblock.object_id[0],
+ wblock->gblock.object_id[1]);
+}
+static DEVICE_ATTR_RO(object_id);
+
+static ssize_t setable_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct wmi_device *wdev = dev_to_wdev(dev);
+
+ return sprintf(buf, "%d\n", (int)wdev->setable);
+}
+static DEVICE_ATTR_RO(setable);
- if (add_uevent_var(env, "MODALIAS="))
+static struct attribute *wmi_data_attrs[] = {
+ &dev_attr_object_id.attr,
+ &dev_attr_setable.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(wmi_data);
+
+static struct attribute *wmi_method_attrs[] = {
+ &dev_attr_object_id.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(wmi_method);
+
+static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct wmi_block *wblock = dev_to_wblock(dev);
+
+ if (add_uevent_var(env, "MODALIAS=wmi:%pUL", wblock->gblock.guid))
return -ENOMEM;
- wblock = dev_get_drvdata(dev);
- if (!wblock)
+ if (add_uevent_var(env, "WMI_GUID=%pUL", wblock->gblock.guid))
return -ENOMEM;
- sprintf(guid_string, "%pUL", wblock->gblock.guid);
+ return 0;
+}
- strcpy(&env->buf[env->buflen - 1], "wmi:");
- memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
- env->buflen += 40;
+static void wmi_dev_release(struct device *dev)
+{
+ struct wmi_block *wblock = dev_to_wblock(dev);
+
+ kfree(wblock);
+}
+
+static int wmi_dev_match(struct device *dev, struct device_driver *driver)
+{
+ struct wmi_driver *wmi_driver =
+ container_of(driver, struct wmi_driver, driver);
+ struct wmi_block *wblock = dev_to_wblock(dev);
+ const struct wmi_device_id *id = wmi_driver->id_table;
+
+ while (id->guid_string) {
+ uuid_le driver_guid;
+
+ if (WARN_ON(uuid_le_to_bin(id->guid_string, &driver_guid)))
+ continue;
+ if (!memcmp(&driver_guid, wblock->gblock.guid, 16))
+ return 1;
+
+ id++;
+ }
return 0;
}
-static void wmi_dev_free(struct device *dev)
+static int wmi_dev_probe(struct device *dev)
{
- struct wmi_block *wmi_block = container_of(dev, struct wmi_block, dev);
+ struct wmi_block *wblock = dev_to_wblock(dev);
+ struct wmi_driver *wdriver =
+ container_of(dev->driver, struct wmi_driver, driver);
+ int ret = 0;
+
+ if (ACPI_FAILURE(wmi_method_enable(wblock, 1)))
+ dev_warn(dev, "failed to enable device -- probing anyway\n");
+
+ if (wdriver->probe) {
+ ret = wdriver->probe(dev_to_wdev(dev));
+ if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0)))
+ dev_warn(dev, "failed to disable device\n");
+ }
+
+ return ret;
+}
+
+static int wmi_dev_remove(struct device *dev)
+{
+ struct wmi_block *wblock = dev_to_wblock(dev);
+ struct wmi_driver *wdriver =
+ container_of(dev->driver, struct wmi_driver, driver);
+ int ret = 0;
+
+ if (wdriver->remove)
+ ret = wdriver->remove(dev_to_wdev(dev));
+
+ if (ACPI_FAILURE(wmi_method_enable(wblock, 0)))
+ dev_warn(dev, "failed to disable device\n");
- kfree(wmi_block);
+ return ret;
}
-static struct class wmi_class = {
+static struct class wmi_bus_class = {
+ .name = "wmi_bus",
+};
+
+static struct bus_type wmi_bus_type = {
.name = "wmi",
- .dev_release = wmi_dev_free,
- .dev_uevent = wmi_dev_uevent,
.dev_groups = wmi_groups,
+ .match = wmi_dev_match,
+ .uevent = wmi_dev_uevent,
+ .probe = wmi_dev_probe,
+ .remove = wmi_dev_remove,
+};
+
+static struct device_type wmi_type_event = {
+ .name = "event",
+ .groups = wmi_event_groups,
+ .release = wmi_dev_release,
};
-static int wmi_create_device(const struct guid_block *gblock,
- struct wmi_block *wblock, acpi_handle handle)
+static struct device_type wmi_type_method = {
+ .name = "method",
+ .groups = wmi_method_groups,
+ .release = wmi_dev_release,
+};
+
+static struct device_type wmi_type_data = {
+ .name = "data",
+ .groups = wmi_data_groups,
+ .release = wmi_dev_release,
+};
+
+static int wmi_create_device(struct device *wmi_bus_dev,
+ const struct guid_block *gblock,
+ struct wmi_block *wblock,
+ struct acpi_device *device)
{
- wblock->dev.class = &wmi_class;
+ struct acpi_device_info *info;
+ char method[5];
+ int result;
- dev_set_name(&wblock->dev, "%pUL", gblock->guid);
+ if (gblock->flags & ACPI_WMI_EVENT) {
+ wblock->dev.dev.type = &wmi_type_event;
+ goto out_init;
+ }
- dev_set_drvdata(&wblock->dev, wblock);
+ if (gblock->flags & ACPI_WMI_METHOD) {
+ wblock->dev.dev.type = &wmi_type_method;
+ goto out_init;
+ }
- return device_register(&wblock->dev);
+ /*
+ * Data Block Query Control Method (WQxx by convention) is
+ * required per the WMI documentation. If it is not present,
+ * we ignore this data block.
+ */
+ strcpy(method, "WQ");
+ strncat(method, wblock->gblock.object_id, 2);
+ result = get_subobj_info(device->handle, method, &info);
+
+ if (result) {
+ dev_warn(wmi_bus_dev,
+ "%s data block query control method not found",
+ method);
+ return result;
+ }
+
+ wblock->dev.dev.type = &wmi_type_data;
+
+ /*
+ * The Microsoft documentation specifically states:
+ *
+ * Data blocks registered with only a single instance
+ * can ignore the parameter.
+ *
+ * ACPICA will get mad at us if we call the method with the wrong number
+ * of arguments, so check what our method expects. (On some Dell
+ * laptops, WQxx may not be a method at all.)
+ */
+ if (info->type != ACPI_TYPE_METHOD || info->param_count == 0)
+ wblock->read_takes_no_args = true;
+
+ kfree(info);
+
+ strcpy(method, "WS");
+ strncat(method, wblock->gblock.object_id, 2);
+ result = get_subobj_info(device->handle, method, NULL);
+
+ if (result == 0)
+ wblock->dev.setable = true;
+
+ out_init:
+ wblock->dev.dev.bus = &wmi_bus_type;
+ wblock->dev.dev.parent = wmi_bus_dev;
+
+ dev_set_name(&wblock->dev.dev, "%pUL", gblock->guid);
+
+ device_initialize(&wblock->dev.dev);
+
+ return 0;
}
-static void wmi_free_devices(void)
+static void wmi_free_devices(struct acpi_device *device)
{
struct wmi_block *wblock, *next;
/* Delete devices for all the GUIDs */
list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
- list_del(&wblock->list);
- if (wblock->dev.class)
- device_unregister(&wblock->dev);
- else
- kfree(wblock);
+ if (wblock->acpi_device == device) {
+ list_del(&wblock->list);
+ device_unregister(&wblock->dev.dev);
+ }
}
}
-static bool guid_already_parsed(const char *guid_string)
+static bool guid_already_parsed(struct acpi_device *device,
+ const u8 *guid)
{
struct wmi_block *wblock;
- list_for_each_entry(wblock, &wmi_block_list, list)
- if (memcmp(wblock->gblock.guid, guid_string, 16) == 0)
+ list_for_each_entry(wblock, &wmi_block_list, list) {
+ if (memcmp(wblock->gblock.guid, guid, 16) == 0) {
+ /*
+ * Because we historically didn't track the relationship
+ * between GUIDs and ACPI nodes, we don't know whether
+ * we need to suppress GUIDs that are unique on a
+ * given node but duplicated across nodes.
+ */
+ dev_warn(&device->dev, "duplicate WMI GUID %pUL (first instance was on %s)\n",
+ guid, dev_name(&wblock->acpi_device->dev));
return true;
+ }
+ }
return false;
}
@@ -645,17 +936,17 @@ static bool guid_already_parsed(const char *guid_string)
/*
* Parse the _WDG method for the GUID data blocks
*/
-static int parse_wdg(acpi_handle handle)
+static int parse_wdg(struct device *wmi_bus_dev, struct acpi_device *device)
{
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
- union acpi_object *obj;
const struct guid_block *gblock;
- struct wmi_block *wblock;
+ struct wmi_block *wblock, *next;
+ union acpi_object *obj;
acpi_status status;
- int retval;
+ int retval = 0;
u32 i, total;
- status = acpi_evaluate_object(handle, "_WDG", NULL, &out);
+ status = acpi_evaluate_object(device->handle, "_WDG", NULL, &out);
if (ACPI_FAILURE(status))
return -ENXIO;
@@ -675,25 +966,28 @@ static int parse_wdg(acpi_handle handle)
if (debug_dump_wdg)
wmi_dump_wdg(&gblock[i]);
+ /*
+ * Some WMI devices, like those for nVidia hooks, have a
+ * duplicate GUID. It's not clear what we should do in this
+ * case yet, so for now, we'll just ignore the duplicate
+ * for device creation.
+ */
+ if (guid_already_parsed(device, gblock[i].guid))
+ continue;
+
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
- if (!wblock)
- return -ENOMEM;
+ if (!wblock) {
+ retval = -ENOMEM;
+ break;
+ }
- wblock->handle = handle;
+ wblock->acpi_device = device;
wblock->gblock = gblock[i];
- /*
- Some WMI devices, like those for nVidia hooks, have a
- duplicate GUID. It's not clear what we should do in this
- case yet, so for now, we'll just ignore the duplicate
- for device creation.
- */
- if (!guid_already_parsed(gblock[i].guid)) {
- retval = wmi_create_device(&gblock[i], wblock, handle);
- if (retval) {
- wmi_free_devices();
- goto out_free_pointer;
- }
+ retval = wmi_create_device(wmi_bus_dev, &gblock[i], wblock, device);
+ if (retval) {
+ kfree(wblock);
+ continue;
}
list_add_tail(&wblock->list, &wmi_block_list);
@@ -704,11 +998,27 @@ static int parse_wdg(acpi_handle handle)
}
}
- retval = 0;
+ /*
+ * Now that all of the devices are created, add them to the
+ * device tree and probe subdrivers.
+ */
+ list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
+ if (wblock->acpi_device != device)
+ continue;
+
+ retval = device_add(&wblock->dev.dev);
+ if (retval) {
+ dev_err(wmi_bus_dev, "failed to register %pULL\n",
+ wblock->gblock.guid);
+ if (debug_event)
+ wmi_method_enable(wblock, 0);
+ list_del(&wblock->list);
+ put_device(&wblock->dev.dev);
+ }
+ }
out_free_pointer:
kfree(out.pointer);
-
return retval;
}
@@ -756,67 +1066,168 @@ acpi_wmi_ec_space_handler(u32 function, acpi_physical_address address,
}
}
-static void acpi_wmi_notify(struct acpi_device *device, u32 event)
+static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
+ void *context)
{
struct guid_block *block;
struct wmi_block *wblock;
struct list_head *p;
+ bool found_it = false;
list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock;
- if ((block->flags & ACPI_WMI_EVENT) &&
- (block->notify_id == event)) {
- if (wblock->handler)
- wblock->handler(event, wblock->handler_data);
- if (debug_event) {
- pr_info("DEBUG Event GUID: %pUL\n",
- wblock->gblock.guid);
- }
-
- acpi_bus_generate_netlink_event(
- device->pnp.device_class, dev_name(&device->dev),
- event, 0);
+ if (wblock->acpi_device->handle == handle &&
+ (block->flags & ACPI_WMI_EVENT) &&
+ (block->notify_id == event))
+ {
+ found_it = true;
break;
}
}
+
+ if (!found_it)
+ return;
+
+ /* If a driver is bound, then notify the driver. */
+ if (wblock->dev.dev.driver) {
+ struct wmi_driver *driver;
+ struct acpi_object_list input;
+ union acpi_object params[1];
+ struct acpi_buffer evdata = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+
+ driver = container_of(wblock->dev.dev.driver,
+ struct wmi_driver, driver);
+
+ input.count = 1;
+ input.pointer = params;
+ params[0].type = ACPI_TYPE_INTEGER;
+ params[0].integer.value = event;
+
+ status = acpi_evaluate_object(wblock->acpi_device->handle,
+ "_WED", &input, &evdata);
+ if (ACPI_FAILURE(status)) {
+ dev_warn(&wblock->dev.dev,
+ "failed to get event data\n");
+ return;
+ }
+
+ if (driver->notify)
+ driver->notify(&wblock->dev,
+ (union acpi_object *)evdata.pointer);
+
+ kfree(evdata.pointer);
+ } else if (wblock->handler) {
+ /* Legacy handler */
+ wblock->handler(event, wblock->handler_data);
+ }
+
+ if (debug_event) {
+ pr_info("DEBUG Event GUID: %pUL\n",
+ wblock->gblock.guid);
+ }
+
+ acpi_bus_generate_netlink_event(
+ wblock->acpi_device->pnp.device_class,
+ dev_name(&wblock->dev.dev),
+ event, 0);
+
}
-static int acpi_wmi_remove(struct acpi_device *device)
+static int acpi_wmi_remove(struct platform_device *device)
{
- acpi_remove_address_space_handler(device->handle,
+ struct acpi_device *acpi_device = ACPI_COMPANION(&device->dev);
+
+ acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY,
+ acpi_wmi_notify_handler);
+ acpi_remove_address_space_handler(acpi_device->handle,
ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
- wmi_free_devices();
+ wmi_free_devices(acpi_device);
+ device_unregister((struct device *)dev_get_drvdata(&device->dev));
return 0;
}
-static int acpi_wmi_add(struct acpi_device *device)
+static int acpi_wmi_probe(struct platform_device *device)
{
+ struct acpi_device *acpi_device;
+ struct device *wmi_bus_dev;
acpi_status status;
int error;
- status = acpi_install_address_space_handler(device->handle,
+ acpi_device = ACPI_COMPANION(&device->dev);
+ if (!acpi_device) {
+ dev_err(&device->dev, "ACPI companion is missing\n");
+ return -ENODEV;
+ }
+
+ status = acpi_install_address_space_handler(acpi_device->handle,
ACPI_ADR_SPACE_EC,
&acpi_wmi_ec_space_handler,
NULL, NULL);
if (ACPI_FAILURE(status)) {
- pr_err("Error installing EC region handler\n");
+ dev_err(&device->dev, "Error installing EC region handler\n");
return -ENODEV;
}
- error = parse_wdg(device->handle);
+ status = acpi_install_notify_handler(acpi_device->handle,
+ ACPI_DEVICE_NOTIFY,
+ acpi_wmi_notify_handler,
+ NULL);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&device->dev, "Error installing notify handler\n");
+ error = -ENODEV;
+ goto err_remove_ec_handler;
+ }
+
+ wmi_bus_dev = device_create(&wmi_bus_class, &device->dev, MKDEV(0, 0),
+ NULL, "wmi_bus-%s", dev_name(&device->dev));
+ if (IS_ERR(wmi_bus_dev)) {
+ error = PTR_ERR(wmi_bus_dev);
+ goto err_remove_notify_handler;
+ }
+ dev_set_drvdata(&device->dev, wmi_bus_dev);
+
+ error = parse_wdg(wmi_bus_dev, acpi_device);
if (error) {
- acpi_remove_address_space_handler(device->handle,
- ACPI_ADR_SPACE_EC,
- &acpi_wmi_ec_space_handler);
pr_err("Failed to parse WDG method\n");
- return error;
+ goto err_remove_busdev;
}
return 0;
+
+err_remove_busdev:
+ device_unregister(wmi_bus_dev);
+
+err_remove_notify_handler:
+ acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY,
+ acpi_wmi_notify_handler);
+
+err_remove_ec_handler:
+ acpi_remove_address_space_handler(acpi_device->handle,
+ ACPI_ADR_SPACE_EC,
+ &acpi_wmi_ec_space_handler);
+
+ return error;
+}
+
+int __must_check __wmi_driver_register(struct wmi_driver *driver,
+ struct module *owner)
+{
+ driver->driver.owner = owner;
+ driver->driver.bus = &wmi_bus_type;
+
+ return driver_register(&driver->driver);
}
+EXPORT_SYMBOL(__wmi_driver_register);
+
+void wmi_driver_unregister(struct wmi_driver *driver)
+{
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL(wmi_driver_unregister);
static int __init acpi_wmi_init(void)
{
@@ -825,27 +1236,36 @@ static int __init acpi_wmi_init(void)
if (acpi_disabled)
return -ENODEV;
- error = class_register(&wmi_class);
+ error = class_register(&wmi_bus_class);
if (error)
return error;
- error = acpi_bus_register_driver(&acpi_wmi_driver);
+ error = bus_register(&wmi_bus_type);
+ if (error)
+ goto err_unreg_class;
+
+ error = platform_driver_register(&acpi_wmi_driver);
if (error) {
pr_err("Error loading mapper\n");
- class_unregister(&wmi_class);
- return error;
+ goto err_unreg_bus;
}
- pr_info("Mapper loaded\n");
return 0;
+
+err_unreg_bus:
+ bus_unregister(&wmi_bus_type);
+
+err_unreg_class:
+ class_unregister(&wmi_bus_class);
+
+ return error;
}
static void __exit acpi_wmi_exit(void)
{
- acpi_bus_unregister_driver(&acpi_wmi_driver);
- class_unregister(&wmi_class);
-
- pr_info("Mapper unloaded\n");
+ platform_driver_unregister(&acpi_wmi_driver);
+ class_unregister(&wmi_bus_class);
+ bus_unregister(&wmi_bus_type);
}
subsys_initcall(acpi_wmi_init);