diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-11-13 17:10:13 -0800 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2017-11-13 17:10:13 -0800 |
commit | c25141062a82ae8bddced1b3ce2b57a1c0efabe0 (patch) | |
tree | 105edf10059bc0c4f2f00338b0c861b813d1bb1a /drivers/platform | |
parent | 26dd633e437dca218547ccbeacc71fe8a620b6f6 (diff) | |
parent | c1b433e04ef9c0a1c4d65bfe918472ffa334dff4 (diff) |
Merge branch 'next' into for-linus
Prepare input updates for 4.15 merge window.
Diffstat (limited to 'drivers/platform')
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); |