summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/platform
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/platform')
-rw-r--r--kernel/drivers/platform/chrome/Kconfig7
-rw-r--r--kernel/drivers/platform/chrome/Makefile4
-rw-r--r--kernel/drivers/platform/chrome/chromeos_laptop.c8
-rw-r--r--kernel/drivers/platform/chrome/cros_ec_dev.c196
-rw-r--r--kernel/drivers/platform/chrome/cros_ec_dev.h7
-rw-r--r--kernel/drivers/platform/chrome/cros_ec_lightbar.c232
-rw-r--r--kernel/drivers/platform/chrome/cros_ec_lpc.c106
-rw-r--r--kernel/drivers/platform/chrome/cros_ec_proto.c382
-rw-r--r--kernel/drivers/platform/chrome/cros_ec_sysfs.c178
-rw-r--r--kernel/drivers/platform/chrome/cros_ec_vbc.c137
-rw-r--r--kernel/drivers/platform/goldfish/goldfish_pipe.c20
-rw-r--r--kernel/drivers/platform/goldfish/pdev_bus.c12
-rw-r--r--kernel/drivers/platform/olpc/olpc-ec.c13
-rw-r--r--kernel/drivers/platform/x86/Kconfig79
-rw-r--r--kernel/drivers/platform/x86/Makefile4
-rw-r--r--kernel/drivers/platform/x86/acer-wmi.c103
-rw-r--r--kernel/drivers/platform/x86/acerhdf.c12
-rw-r--r--kernel/drivers/platform/x86/apple-gmux.c6
-rw-r--r--kernel/drivers/platform/x86/asus-laptop.c7
-rw-r--r--kernel/drivers/platform/x86/asus-nb-wmi.c18
-rw-r--r--kernel/drivers/platform/x86/asus-wmi.c382
-rw-r--r--kernel/drivers/platform/x86/compal-laptop.c47
-rw-r--r--kernel/drivers/platform/x86/dell-laptop.c448
-rw-r--r--kernel/drivers/platform/x86/dell-rbtn.c423
-rw-r--r--kernel/drivers/platform/x86/dell-rbtn.h24
-rw-r--r--kernel/drivers/platform/x86/dell-wmi.c3
-rw-r--r--kernel/drivers/platform/x86/eeepc-laptop.c5
-rw-r--r--kernel/drivers/platform/x86/fujitsu-laptop.c6
-rw-r--r--kernel/drivers/platform/x86/hp-wireless.c7
-rw-r--r--kernel/drivers/platform/x86/ibm_rtl.c2
-rw-r--r--kernel/drivers/platform/x86/ideapad-laptop.c69
-rw-r--r--kernel/drivers/platform/x86/intel_ips.c2
-rw-r--r--kernel/drivers/platform/x86/intel_menlow.c8
-rw-r--r--kernel/drivers/platform/x86/intel_mid_powerbtn.c10
-rw-r--r--kernel/drivers/platform/x86/intel_mid_thermal.c9
-rw-r--r--kernel/drivers/platform/x86/intel_oaktrail.c7
-rw-r--r--kernel/drivers/platform/x86/intel_pmc_ipc.c779
-rw-r--r--kernel/drivers/platform/x86/intel_scu_ipc.c195
-rw-r--r--kernel/drivers/platform/x86/intel_scu_ipcutil.c2
-rw-r--r--kernel/drivers/platform/x86/msi-laptop.c6
-rw-r--r--kernel/drivers/platform/x86/msi-wmi.c4
-rw-r--r--kernel/drivers/platform/x86/pvpanic.c10
-rw-r--r--kernel/drivers/platform/x86/samsung-laptop.c27
-rw-r--r--kernel/drivers/platform/x86/sony-laptop.c20
-rw-r--r--kernel/drivers/platform/x86/surfacepro3_button.c216
-rw-r--r--kernel/drivers/platform/x86/tc1100-wmi.c2
-rw-r--r--kernel/drivers/platform/x86/thinkpad_acpi.c10
-rw-r--r--kernel/drivers/platform/x86/toshiba-wmi.c138
-rw-r--r--kernel/drivers/platform/x86/toshiba_acpi.c1081
-rw-r--r--kernel/drivers/platform/x86/toshiba_bluetooth.c174
-rw-r--r--kernel/drivers/platform/x86/toshiba_haps.c32
-rw-r--r--kernel/drivers/platform/x86/wmi.c51
52 files changed, 4316 insertions, 1414 deletions
diff --git a/kernel/drivers/platform/chrome/Kconfig b/kernel/drivers/platform/chrome/Kconfig
index 2a6531a5f..d03df4a60 100644
--- a/kernel/drivers/platform/chrome/Kconfig
+++ b/kernel/drivers/platform/chrome/Kconfig
@@ -4,7 +4,7 @@
menuconfig CHROME_PLATFORMS
bool "Platform support for Chrome hardware"
- depends on X86 || ARM
+ depends on X86 || ARM || ARM64 || COMPILE_TEST
---help---
Say Y here to get to see options for platform support for
various Chromebooks and Chromeboxes. This option alone does
@@ -59,4 +59,9 @@ 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_PROTO
+ bool
+ help
+ ChromeOS EC communication protocol helpers.
+
endif # CHROMEOS_PLATFORMS
diff --git a/kernel/drivers/platform/chrome/Makefile b/kernel/drivers/platform/chrome/Makefile
index bd8d8601e..bc498bda8 100644
--- a/kernel/drivers/platform/chrome/Makefile
+++ b/kernel/drivers/platform/chrome/Makefile
@@ -1,6 +1,8 @@
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_devs-objs := cros_ec_dev.o cros_ec_sysfs.o \
+ cros_ec_lightbar.o cros_ec_vbc.o
obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o
obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o
+obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o
diff --git a/kernel/drivers/platform/chrome/chromeos_laptop.c b/kernel/drivers/platform/chrome/chromeos_laptop.c
index a04019ab9..2b441e9ae 100644
--- a/kernel/drivers/platform/chrome/chromeos_laptop.c
+++ b/kernel/drivers/platform/chrome/chromeos_laptop.c
@@ -23,7 +23,7 @@
#include <linux/dmi.h>
#include <linux/i2c.h>
-#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/platform_data/atmel_mxt_ts.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -47,8 +47,8 @@ static const char *i2c_adapter_names[] = {
"SMBus I801 adapter",
"i915 gmbus vga",
"i915 gmbus panel",
- "i2c-designware-pci",
- "i2c-designware-pci",
+ "Synopsys DesignWare I2C adapter",
+ "Synopsys DesignWare I2C adapter",
};
/* Keep this enum consistent with i2c_adapter_names */
@@ -111,6 +111,7 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = {
.irqflags = IRQF_TRIGGER_FALLING,
.t19_num_keys = ARRAY_SIZE(mxt_t19_keys),
.t19_keymap = mxt_t19_keys,
+ .suspend_mode = MXT_SUSPEND_T9_CTRL,
};
static struct i2c_board_info atmel_224s_tp_device = {
@@ -121,6 +122,7 @@ static struct i2c_board_info atmel_224s_tp_device = {
static struct mxt_platform_data atmel_1664s_platform_data = {
.irqflags = IRQF_TRIGGER_FALLING,
+ .suspend_mode = MXT_SUSPEND_T9_CTRL,
};
static struct i2c_board_info atmel_1664s_device = {
diff --git a/kernel/drivers/platform/chrome/cros_ec_dev.c b/kernel/drivers/platform/chrome/cros_ec_dev.c
index 6090d0b28..d45cd254e 100644
--- a/kernel/drivers/platform/chrome/cros_ec_dev.c
+++ b/kernel/drivers/platform/chrome/cros_ec_dev.c
@@ -20,44 +20,60 @@
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <linux/uaccess.h>
#include "cros_ec_dev.h"
/* Device variables */
#define CROS_MAX_DEV 128
-static struct class *cros_class;
static int ec_major;
+static const struct attribute_group *cros_ec_groups[] = {
+ &cros_ec_attr_group,
+ &cros_ec_lightbar_attr_group,
+ &cros_ec_vbc_attr_group,
+ NULL,
+};
+
+static struct class cros_class = {
+ .owner = THIS_MODULE,
+ .name = "chromeos",
+ .dev_groups = cros_ec_groups,
+};
+
/* Basic communication */
-static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
+static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
{
struct ec_response_get_version *resp;
static const char * const current_image_name[] = {
"unknown", "read-only", "read-write", "invalid",
};
- struct cros_ec_command msg = {
- .version = 0,
- .command = EC_CMD_GET_VERSION,
- .outdata = { 0 },
- .outsize = 0,
- .indata = { 0 },
- .insize = sizeof(*resp),
- };
+ struct cros_ec_command *msg;
int ret;
- ret = cros_ec_cmd_xfer(ec, &msg);
+ msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
+ msg->insize = sizeof(*resp);
+ msg->outsize = 0;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
+ goto exit;
- if (msg.result != EC_RES_SUCCESS) {
+ if (msg->result != EC_RES_SUCCESS) {
snprintf(str, maxlen,
"%s\nUnknown EC version: EC returned %d\n",
- CROS_EC_DEV_VERSION, msg.result);
- return 0;
+ CROS_EC_DEV_VERSION, msg->result);
+ ret = -EINVAL;
+ goto exit;
}
- resp = (struct ec_response_get_version *)msg.indata;
+ resp = (struct ec_response_get_version *)msg->data;
if (resp->current_image >= ARRAY_SIZE(current_image_name))
resp->current_image = 3; /* invalid */
@@ -65,14 +81,19 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
resp->version_string_ro, resp->version_string_rw,
current_image_name[resp->current_image]);
- return 0;
+ ret = 0;
+exit:
+ kfree(msg);
+ return ret;
}
/* Device file ops */
static int ec_device_open(struct inode *inode, struct file *filp)
{
- filp->private_data = container_of(inode->i_cdev,
- struct cros_ec_device, cdev);
+ struct cros_ec_dev *ec = container_of(inode->i_cdev,
+ struct cros_ec_dev, cdev);
+ filp->private_data = ec;
+ nonseekable_open(inode, filp);
return 0;
}
@@ -84,7 +105,7 @@ static int ec_device_release(struct inode *inode, struct file *filp)
static ssize_t ec_device_read(struct file *filp, char __user *buffer,
size_t length, loff_t *offset)
{
- struct cros_ec_device *ec = filp->private_data;
+ struct cros_ec_dev *ec = filp->private_data;
char msg[sizeof(struct ec_response_get_version) +
sizeof(CROS_EC_DEV_VERSION)];
size_t count;
@@ -107,38 +128,53 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer,
}
/* Ioctls */
-static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg)
+static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
{
long ret;
- struct cros_ec_command s_cmd = { };
+ struct cros_ec_command u_cmd;
+ struct cros_ec_command *s_cmd;
- if (copy_from_user(&s_cmd, arg, sizeof(s_cmd)))
+ if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
return -EFAULT;
- ret = cros_ec_cmd_xfer(ec, &s_cmd);
+ s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
+ GFP_KERNEL);
+ if (!s_cmd)
+ return -ENOMEM;
+
+ if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ s_cmd->command += ec->cmd_offset;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
/* Only copy data to userland if data was received. */
if (ret < 0)
- return ret;
-
- if (copy_to_user(arg, &s_cmd, sizeof(s_cmd)))
- return -EFAULT;
+ goto exit;
- return 0;
+ if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + u_cmd.insize))
+ ret = -EFAULT;
+exit:
+ kfree(s_cmd);
+ return ret;
}
-static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
+static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg)
{
+ struct cros_ec_device *ec_dev = ec->ec_dev;
struct cros_ec_readmem s_mem = { };
long num;
/* Not every platform supports direct reads */
- if (!ec->cmd_readmem)
+ if (!ec_dev->cmd_readmem)
return -ENOTTY;
if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
return -EFAULT;
- num = ec->cmd_readmem(ec, s_mem.offset, s_mem.bytes, s_mem.buffer);
+ num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes,
+ s_mem.buffer);
if (num <= 0)
return num;
@@ -151,7 +187,7 @@ static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)
static long ec_device_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
- struct cros_ec_device *ec = filp->private_data;
+ struct cros_ec_dev *ec = filp->private_data;
if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
return -ENOTTY;
@@ -174,48 +210,90 @@ static const struct file_operations fops = {
.unlocked_ioctl = ec_device_ioctl,
};
+static void __remove(struct device *dev)
+{
+ struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev,
+ class_dev);
+ kfree(ec);
+}
+
static int ec_device_probe(struct platform_device *pdev)
{
- struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
- int retval = -ENOTTY;
- dev_t devno = MKDEV(ec_major, 0);
+ int retval = -ENOMEM;
+ struct device *dev = &pdev->dev;
+ struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
+ dev_t devno = MKDEV(ec_major, pdev->id);
+ struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
- /* Instantiate it (and remember the EC) */
+ if (!ec)
+ return retval;
+
+ dev_set_drvdata(dev, ec);
+ ec->ec_dev = dev_get_drvdata(dev->parent);
+ ec->dev = dev;
+ ec->cmd_offset = ec_platform->cmd_offset;
+ device_initialize(&ec->class_dev);
cdev_init(&ec->cdev, &fops);
+ /*
+ * Add the character device
+ * Link cdev to the class device to be sure device is not used
+ * before unbinding it.
+ */
+ ec->cdev.kobj.parent = &ec->class_dev.kobj;
retval = cdev_add(&ec->cdev, devno, 1);
if (retval) {
- dev_err(&pdev->dev, ": failed to add character device\n");
- return retval;
+ dev_err(dev, ": failed to add character device\n");
+ goto cdev_add_failed;
}
- ec->vdev = device_create(cros_class, NULL, devno, ec,
- CROS_EC_DEV_NAME);
- if (IS_ERR(ec->vdev)) {
- retval = PTR_ERR(ec->vdev);
- dev_err(&pdev->dev, ": failed to create device\n");
- cdev_del(&ec->cdev);
- return retval;
+ /*
+ * Add the class device
+ * Link to the character device for creating the /dev entry
+ * in devtmpfs.
+ */
+ ec->class_dev.devt = ec->cdev.dev;
+ ec->class_dev.class = &cros_class;
+ ec->class_dev.parent = dev;
+ ec->class_dev.release = __remove;
+
+ retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
+ if (retval) {
+ dev_err(dev, "dev_set_name failed => %d\n", retval);
+ goto set_named_failed;
}
- /* Initialize extra interfaces */
- ec_dev_sysfs_init(ec);
- ec_dev_lightbar_init(ec);
+ retval = device_add(&ec->class_dev);
+ if (retval) {
+ dev_err(dev, "device_register failed => %d\n", retval);
+ goto dev_reg_failed;
+ }
return 0;
+
+dev_reg_failed:
+set_named_failed:
+ dev_set_drvdata(dev, NULL);
+ cdev_del(&ec->cdev);
+cdev_add_failed:
+ kfree(ec);
+ return retval;
}
static int ec_device_remove(struct platform_device *pdev)
{
- struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
-
- ec_dev_lightbar_remove(ec);
- ec_dev_sysfs_remove(ec);
- device_destroy(cros_class, MKDEV(ec_major, 0));
+ struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
cdev_del(&ec->cdev);
+ device_unregister(&ec->class_dev);
return 0;
}
+static const struct platform_device_id cros_ec_id[] = {
+ { "cros-ec-ctl", 0 },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_id);
+
static struct platform_driver cros_ec_dev_driver = {
.driver = {
.name = "cros-ec-ctl",
@@ -229,10 +307,10 @@ static int __init cros_ec_dev_init(void)
int ret;
dev_t dev = 0;
- cros_class = class_create(THIS_MODULE, "chromeos");
- if (IS_ERR(cros_class)) {
+ ret = class_register(&cros_class);
+ if (ret) {
pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
- return PTR_ERR(cros_class);
+ return ret;
}
/* Get a range of minor numbers (starting with 0) to work with */
@@ -254,7 +332,7 @@ static int __init cros_ec_dev_init(void)
failed_devreg:
unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
failed_chrdevreg:
- class_destroy(cros_class);
+ class_unregister(&cros_class);
return ret;
}
@@ -262,7 +340,7 @@ static void __exit cros_ec_dev_exit(void)
{
platform_driver_unregister(&cros_ec_dev_driver);
unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
- class_destroy(cros_class);
+ class_unregister(&cros_class);
}
module_init(cros_ec_dev_init);
diff --git a/kernel/drivers/platform/chrome/cros_ec_dev.h b/kernel/drivers/platform/chrome/cros_ec_dev.h
index 45d67f7e5..bfd2c84c3 100644
--- a/kernel/drivers/platform/chrome/cros_ec_dev.h
+++ b/kernel/drivers/platform/chrome/cros_ec_dev.h
@@ -24,7 +24,6 @@
#include <linux/types.h>
#include <linux/mfd/cros_ec.h>
-#define CROS_EC_DEV_NAME "cros_ec"
#define CROS_EC_DEV_VERSION "1.0.0"
/*
@@ -44,10 +43,4 @@ 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)
-void ec_dev_sysfs_init(struct cros_ec_device *);
-void ec_dev_sysfs_remove(struct cros_ec_device *);
-
-void ec_dev_lightbar_init(struct cros_ec_device *);
-void ec_dev_lightbar_remove(struct cros_ec_device *);
-
#endif /* _CROS_EC_DEV_H_ */
diff --git a/kernel/drivers/platform/chrome/cros_ec_lightbar.c b/kernel/drivers/platform/chrome/cros_ec_lightbar.c
index b4ff47a90..ff7640575 100644
--- a/kernel/drivers/platform/chrome/cros_ec_lightbar.c
+++ b/kernel/drivers/platform/chrome/cros_ec_lightbar.c
@@ -31,6 +31,7 @@
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/uaccess.h>
+#include <linux/slab.h>
#include "cros_ec_dev.h"
@@ -91,55 +92,81 @@ out:
return ret;
}
-#define INIT_MSG(P, R) { \
- .command = EC_CMD_LIGHTBAR_CMD, \
- .outsize = sizeof(*P), \
- .insize = sizeof(*R), \
- }
+static struct cros_ec_command *alloc_lightbar_cmd_msg(struct cros_ec_dev *ec)
+{
+ struct cros_ec_command *msg;
+ int len;
+
+ len = max(sizeof(struct ec_params_lightbar),
+ sizeof(struct ec_response_lightbar));
+
+ msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
+ if (!msg)
+ return NULL;
+
+ msg->version = 0;
+ msg->command = EC_CMD_LIGHTBAR_CMD + ec->cmd_offset;
+ msg->outsize = sizeof(struct ec_params_lightbar);
+ msg->insize = sizeof(struct ec_response_lightbar);
+
+ return msg;
+}
-static int get_lightbar_version(struct cros_ec_device *ec,
+static int get_lightbar_version(struct cros_ec_dev *ec,
uint32_t *ver_ptr, uint32_t *flg_ptr)
{
struct ec_params_lightbar *param;
struct ec_response_lightbar *resp;
- struct cros_ec_command msg = INIT_MSG(param, resp);
+ struct cros_ec_command *msg;
int ret;
- param = (struct ec_params_lightbar *)msg.outdata;
- param->cmd = LIGHTBAR_CMD_VERSION;
- ret = cros_ec_cmd_xfer(ec, &msg);
- if (ret < 0)
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
return 0;
- switch (msg.result) {
+ param = (struct ec_params_lightbar *)msg->data;
+ param->cmd = LIGHTBAR_CMD_VERSION;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0) {
+ ret = 0;
+ goto exit;
+ }
+
+ switch (msg->result) {
case EC_RES_INVALID_PARAM:
/* Pixel had no version command. */
if (ver_ptr)
*ver_ptr = 0;
if (flg_ptr)
*flg_ptr = 0;
- return 1;
+ ret = 1;
+ goto exit;
case EC_RES_SUCCESS:
- resp = (struct ec_response_lightbar *)msg.indata;
+ resp = (struct ec_response_lightbar *)msg->data;
/* Future devices w/lightbars should implement this command */
if (ver_ptr)
*ver_ptr = resp->version.num;
if (flg_ptr)
*flg_ptr = resp->version.flags;
- return 1;
+ ret = 1;
+ goto exit;
}
/* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */
- return 0;
+ ret = 0;
+exit:
+ kfree(msg);
+ return ret;
}
static ssize_t version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- uint32_t version, flags;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ uint32_t version = 0, flags = 0;
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
int ret;
ret = lb_throttle();
@@ -158,30 +185,39 @@ static ssize_t brightness_store(struct device *dev,
const char *buf, size_t count)
{
struct ec_params_lightbar *param;
- struct ec_response_lightbar *resp;
- struct cros_ec_command msg = INIT_MSG(param, resp);
+ struct cros_ec_command *msg;
int ret;
unsigned int val;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
if (kstrtouint(buf, 0, &val))
return -EINVAL;
- param = (struct ec_params_lightbar *)msg.outdata;
- param->cmd = LIGHTBAR_CMD_BRIGHTNESS;
- param->brightness.num = val;
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_lightbar *)msg->data;
+ param->cmd = LIGHTBAR_CMD_SET_BRIGHTNESS;
+ param->set_brightness.num = val;
ret = lb_throttle();
if (ret)
- return ret;
+ goto exit;
- ret = cros_ec_cmd_xfer(ec, &msg);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
+ goto exit;
- if (msg.result != EC_RES_SUCCESS)
- return -EINVAL;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = -EINVAL;
+ goto exit;
+ }
- return count;
+ ret = count;
+exit:
+ kfree(msg);
+ return ret;
}
@@ -196,12 +232,16 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ec_params_lightbar *param;
- struct ec_response_lightbar *resp;
- struct cros_ec_command msg = INIT_MSG(param, resp);
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_command *msg;
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
unsigned int val[4];
int ret, i = 0, j = 0, ok = 0;
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
do {
/* Skip any whitespace */
while (*buf && isspace(*buf))
@@ -212,15 +252,15 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
ret = sscanf(buf, "%i", &val[i++]);
if (ret == 0)
- return -EINVAL;
+ goto exit;
if (i == 4) {
- param = (struct ec_params_lightbar *)msg.outdata;
- param->cmd = LIGHTBAR_CMD_RGB;
- param->rgb.led = val[0];
- param->rgb.red = val[1];
- param->rgb.green = val[2];
- param->rgb.blue = val[3];
+ param = (struct ec_params_lightbar *)msg->data;
+ param->cmd = LIGHTBAR_CMD_SET_RGB;
+ param->set_rgb.led = val[0];
+ param->set_rgb.red = val[1];
+ param->set_rgb.green = val[2];
+ param->set_rgb.blue = val[3];
/*
* Throttle only the first of every four transactions,
* so that the user can update all four LEDs at once.
@@ -228,15 +268,15 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
if ((j++ % 4) == 0) {
ret = lb_throttle();
if (ret)
- return ret;
+ goto exit;
}
- ret = cros_ec_cmd_xfer(ec, &msg);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
+ goto exit;
- if (msg.result != EC_RES_SUCCESS)
- return -EINVAL;
+ if (msg->result != EC_RES_SUCCESS)
+ goto exit;
i = 0;
ok = 1;
@@ -248,6 +288,8 @@ static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr,
} while (*buf);
+exit:
+ kfree(msg);
return (ok && i == 0) ? count : -EINVAL;
}
@@ -261,41 +303,52 @@ static ssize_t sequence_show(struct device *dev,
{
struct ec_params_lightbar *param;
struct ec_response_lightbar *resp;
- struct cros_ec_command msg = INIT_MSG(param, resp);
+ struct cros_ec_command *msg;
int ret;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
- param = (struct ec_params_lightbar *)msg.outdata;
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_lightbar *)msg->data;
param->cmd = LIGHTBAR_CMD_GET_SEQ;
ret = lb_throttle();
if (ret)
- return ret;
+ goto exit;
- ret = cros_ec_cmd_xfer(ec, &msg);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
+ goto exit;
- if (msg.result != EC_RES_SUCCESS)
- return scnprintf(buf, PAGE_SIZE,
- "ERROR: EC returned %d\n", msg.result);
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = scnprintf(buf, PAGE_SIZE,
+ "ERROR: EC returned %d\n", msg->result);
+ goto exit;
+ }
- resp = (struct ec_response_lightbar *)msg.indata;
+ resp = (struct ec_response_lightbar *)msg->data;
if (resp->get_seq.num >= ARRAY_SIZE(seqname))
- return scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
+ ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num);
else
- return scnprintf(buf, PAGE_SIZE, "%s\n",
- seqname[resp->get_seq.num]);
+ ret = scnprintf(buf, PAGE_SIZE, "%s\n",
+ seqname[resp->get_seq.num]);
+
+exit:
+ kfree(msg);
+ return ret;
}
static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ec_params_lightbar *param;
- struct ec_response_lightbar *resp;
- struct cros_ec_command msg = INIT_MSG(param, resp);
+ struct cros_ec_command *msg;
unsigned int num;
int ret, len;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
for (len = 0; len < count; len++)
if (!isalnum(buf[len]))
@@ -311,21 +364,30 @@ static ssize_t sequence_store(struct device *dev, struct device_attribute *attr,
return ret;
}
- param = (struct ec_params_lightbar *)msg.outdata;
+ msg = alloc_lightbar_cmd_msg(ec);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_lightbar *)msg->data;
param->cmd = LIGHTBAR_CMD_SEQ;
param->seq.num = num;
ret = lb_throttle();
if (ret)
- return ret;
+ goto exit;
- ret = cros_ec_cmd_xfer(ec, &msg);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
+ goto exit;
- if (msg.result != EC_RES_SUCCESS)
- return -EINVAL;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = -EINVAL;
+ goto exit;
+ }
- return count;
+ ret = count;
+exit:
+ kfree(msg);
+ return ret;
}
/* Module initialization */
@@ -343,25 +405,27 @@ static struct attribute *__lb_cmds_attrs[] = {
&dev_attr_sequence.attr,
NULL,
};
-static struct attribute_group lb_cmds_attr_group = {
- .name = "lightbar",
- .attrs = __lb_cmds_attrs,
-};
-void ec_dev_lightbar_init(struct cros_ec_device *ec)
+static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
{
- int ret = 0;
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
+ struct platform_device *pdev = container_of(ec->dev,
+ struct platform_device, dev);
+ if (pdev->id != 0)
+ return 0;
/* Only instantiate this stuff if the EC has a lightbar */
- if (!get_lightbar_version(ec, NULL, NULL))
- return;
-
- ret = sysfs_create_group(&ec->vdev->kobj, &lb_cmds_attr_group);
- if (ret)
- pr_warn("sysfs_create_group() failed: %d\n", ret);
+ if (get_lightbar_version(ec, NULL, NULL))
+ return a->mode;
+ else
+ return 0;
}
-void ec_dev_lightbar_remove(struct cros_ec_device *ec)
-{
- sysfs_remove_group(&ec->vdev->kobj, &lb_cmds_attr_group);
-}
+struct attribute_group cros_ec_lightbar_attr_group = {
+ .name = "lightbar",
+ .attrs = __lb_cmds_attrs,
+ .is_visible = cros_ec_lightbar_attrs_are_visible,
+};
diff --git a/kernel/drivers/platform/chrome/cros_ec_lpc.c b/kernel/drivers/platform/chrome/cros_ec_lpc.c
index 8f9ac4d7b..f9a245465 100644
--- a/kernel/drivers/platform/chrome/cros_ec_lpc.c
+++ b/kernel/drivers/platform/chrome/cros_ec_lpc.c
@@ -46,6 +46,77 @@ static int ec_response_timed_out(void)
return 1;
}
+static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec,
+ struct cros_ec_command *msg)
+{
+ struct ec_host_request *request;
+ struct ec_host_response response;
+ u8 sum = 0;
+ int i;
+ 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);
+
+ request = (struct ec_host_request *)ec->dout;
+
+ /* Here we go */
+ outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD);
+
+ if (ec_response_timed_out()) {
+ dev_warn(ec->dev, "EC responsed timed out\n");
+ ret = -EIO;
+ goto done;
+ }
+
+ /* Check result */
+ msg->result = inb(EC_LPC_ADDR_HOST_DATA);
+ 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];
+ }
+
+ msg->result = response.result;
+
+ if (response.data_len > msg->insize) {
+ dev_err(ec->dev,
+ "packet too long (%d bytes, expected %d)",
+ response.data_len, msg->insize);
+ ret = -EMSGSIZE;
+ goto done;
+ }
+
+ /* 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];
+ }
+
+ if (sum) {
+ dev_err(ec->dev,
+ "bad packet checksum %02x\n",
+ response.checksum);
+ ret = -EBADMSG;
+ goto done;
+ }
+
+ /* Return actual amount of data received */
+ ret = response.data_len;
+done:
+ return ret;
+}
+
static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
struct cros_ec_command *msg)
{
@@ -73,8 +144,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
/* Copy data and update checksum */
for (i = 0; i < msg->outsize; i++) {
- outb(msg->outdata[i], EC_LPC_ADDR_HOST_PARAM + i);
- csum += msg->outdata[i];
+ outb(msg->data[i], EC_LPC_ADDR_HOST_PARAM + i);
+ csum += msg->data[i];
}
/* Finalize checksum and write args */
@@ -95,19 +166,9 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
/* Check result */
msg->result = inb(EC_LPC_ADDR_HOST_DATA);
-
- switch (msg->result) {
- case EC_RES_SUCCESS:
- break;
- case EC_RES_IN_PROGRESS:
- ret = -EAGAIN;
- dev_dbg(ec->dev, "command 0x%02x in progress\n",
- msg->command);
+ ret = cros_ec_check_result(ec, msg);
+ if (ret)
goto done;
- default:
- dev_dbg(ec->dev, "command 0x%02x returned %d\n",
- msg->command, msg->result);
- }
/* Read back args */
args.flags = inb(EC_LPC_ADDR_HOST_ARGS);
@@ -129,8 +190,8 @@ static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
/* Read response and update checksum */
for (i = 0; i < args.data_size; i++) {
- msg->indata[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
- csum += msg->indata[i];
+ msg->data[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
+ csum += msg->data[i];
}
/* Verify checksum */
@@ -212,11 +273,13 @@ static int cros_ec_lpc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ec_dev);
ec_dev->dev = dev;
- ec_dev->ec_name = pdev->name;
ec_dev->phys_name = dev_name(dev);
- ec_dev->parent = dev;
ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc;
+ ec_dev->pkt_xfer = cros_ec_pkt_xfer_lpc;
ec_dev->cmd_readmem = cros_ec_lpc_readmem;
+ ec_dev->din_size = sizeof(struct ec_host_response) +
+ sizeof(struct ec_response_get_protocol_info);
+ ec_dev->dout_size = sizeof(struct ec_host_request);
ret = cros_ec_register(ec_dev);
if (ret) {
@@ -257,6 +320,13 @@ static struct dmi_system_id cros_ec_lpc_dmi_table[] __initdata = {
},
},
{
+ /* x86-samus, the Chromebook Pixel 2. */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Samus"),
+ },
+ },
+ {
/* x86-peppy, the Acer C720 Chromebook. */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
diff --git a/kernel/drivers/platform/chrome/cros_ec_proto.c b/kernel/drivers/platform/chrome/cros_ec_proto.c
new file mode 100644
index 000000000..990308ca3
--- /dev/null
+++ b/kernel/drivers/platform/chrome/cros_ec_proto.c
@@ -0,0 +1,382 @@
+/*
+ * ChromeOS EC communication protocol helper functions
+ *
+ * Copyright (C) 2015 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.
+ *
+ */
+
+#include <linux/mfd/cros_ec.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#define EC_COMMAND_RETRIES 50
+
+static int prepare_packet(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ struct ec_host_request *request;
+ u8 *out;
+ int i;
+ u8 csum = 0;
+
+ BUG_ON(ec_dev->proto_version != EC_HOST_REQUEST_VERSION);
+ BUG_ON(msg->outsize + sizeof(*request) > ec_dev->dout_size);
+
+ out = ec_dev->dout;
+ request = (struct ec_host_request *)out;
+ request->struct_version = EC_HOST_REQUEST_VERSION;
+ request->checksum = 0;
+ request->command = msg->command;
+ request->command_version = msg->version;
+ request->reserved = 0;
+ request->data_len = msg->outsize;
+
+ for (i = 0; i < sizeof(*request); i++)
+ csum += out[i];
+
+ /* Copy data and update checksum */
+ memcpy(out + sizeof(*request), msg->data, msg->outsize);
+ for (i = 0; i < msg->outsize; i++)
+ csum += msg->data[i];
+
+ request->checksum = -csum;
+
+ return sizeof(*request) + msg->outsize;
+}
+
+static int send_command(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ int ret;
+
+ if (ec_dev->proto_version > 2)
+ ret = ec_dev->pkt_xfer(ec_dev, msg);
+ else
+ ret = ec_dev->cmd_xfer(ec_dev, msg);
+
+ if (msg->result == EC_RES_IN_PROGRESS) {
+ int i;
+ struct cros_ec_command *status_msg;
+ struct ec_response_get_comms_status *status;
+
+ status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status),
+ GFP_KERNEL);
+ if (!status_msg)
+ return -ENOMEM;
+
+ status_msg->version = 0;
+ status_msg->command = EC_CMD_GET_COMMS_STATUS;
+ status_msg->insize = sizeof(*status);
+ status_msg->outsize = 0;
+
+ /*
+ * Query the EC's status until it's no longer busy or
+ * we encounter an error.
+ */
+ for (i = 0; i < EC_COMMAND_RETRIES; i++) {
+ usleep_range(10000, 11000);
+
+ ret = ec_dev->cmd_xfer(ec_dev, status_msg);
+ if (ret < 0)
+ break;
+
+ msg->result = status_msg->result;
+ if (status_msg->result != EC_RES_SUCCESS)
+ break;
+
+ status = (struct ec_response_get_comms_status *)
+ status_msg->data;
+ if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
+ break;
+ }
+
+ kfree(status_msg);
+ }
+
+ return ret;
+}
+
+int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ u8 *out;
+ u8 csum;
+ int i;
+
+ if (ec_dev->proto_version > 2)
+ return prepare_packet(ec_dev, msg);
+
+ BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE);
+ out = ec_dev->dout;
+ out[0] = EC_CMD_VERSION0 + msg->version;
+ out[1] = msg->command;
+ out[2] = msg->outsize;
+ csum = out[0] + out[1] + out[2];
+ for (i = 0; i < msg->outsize; i++)
+ csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i];
+ out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = csum;
+
+ return EC_MSG_TX_PROTO_BYTES + msg->outsize;
+}
+EXPORT_SYMBOL(cros_ec_prepare_tx);
+
+int cros_ec_check_result(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ switch (msg->result) {
+ case EC_RES_SUCCESS:
+ return 0;
+ case EC_RES_IN_PROGRESS:
+ dev_dbg(ec_dev->dev, "command 0x%02x in progress\n",
+ msg->command);
+ return -EAGAIN;
+ default:
+ dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n",
+ msg->command, msg->result);
+ return 0;
+ }
+}
+EXPORT_SYMBOL(cros_ec_check_result);
+
+static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev,
+ int devidx,
+ struct cros_ec_command *msg)
+{
+ /*
+ * Try using v3+ to query for supported protocols. If this
+ * command fails, fall back to v2. Returns the highest protocol
+ * supported by the EC.
+ * Also sets the max request/response/passthru size.
+ */
+ int ret;
+
+ if (!ec_dev->pkt_xfer)
+ return -EPROTONOSUPPORT;
+
+ memset(msg, 0, sizeof(*msg));
+ msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO;
+ msg->insize = sizeof(struct ec_response_get_protocol_info);
+
+ ret = send_command(ec_dev, msg);
+
+ if (ret < 0) {
+ dev_dbg(ec_dev->dev,
+ "failed to check for EC[%d] protocol version: %d\n",
+ devidx, ret);
+ return ret;
+ }
+
+ if (devidx > 0 && msg->result == EC_RES_INVALID_COMMAND)
+ return -ENODEV;
+ else if (msg->result != EC_RES_SUCCESS)
+ return msg->result;
+
+ return 0;
+}
+
+static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev)
+{
+ struct cros_ec_command *msg;
+ struct ec_params_hello *hello_params;
+ struct ec_response_hello *hello_response;
+ int ret;
+ int len = max(sizeof(*hello_params), sizeof(*hello_response));
+
+ msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = EC_CMD_HELLO;
+ hello_params = (struct ec_params_hello *)msg->data;
+ msg->outsize = sizeof(*hello_params);
+ hello_response = (struct ec_response_hello *)msg->data;
+ msg->insize = sizeof(*hello_response);
+
+ hello_params->in_data = 0xa0b0c0d0;
+
+ ret = send_command(ec_dev, msg);
+
+ if (ret < 0) {
+ dev_dbg(ec_dev->dev,
+ "EC failed to respond to v2 hello: %d\n",
+ ret);
+ goto exit;
+ } else if (msg->result != EC_RES_SUCCESS) {
+ dev_err(ec_dev->dev,
+ "EC responded to v2 hello with error: %d\n",
+ msg->result);
+ ret = msg->result;
+ goto exit;
+ } else if (hello_response->out_data != 0xa1b2c3d4) {
+ dev_err(ec_dev->dev,
+ "EC responded to v2 hello with bad result: %u\n",
+ hello_response->out_data);
+ ret = -EBADMSG;
+ goto exit;
+ }
+
+ ret = 0;
+
+ exit:
+ kfree(msg);
+ return ret;
+}
+
+int cros_ec_query_all(struct cros_ec_device *ec_dev)
+{
+ struct device *dev = ec_dev->dev;
+ struct cros_ec_command *proto_msg;
+ struct ec_response_get_protocol_info *proto_info;
+ int ret;
+
+ proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info),
+ GFP_KERNEL);
+ if (!proto_msg)
+ return -ENOMEM;
+
+ /* First try sending with proto v3. */
+ ec_dev->proto_version = 3;
+ ret = cros_ec_host_command_proto_query(ec_dev, 0, proto_msg);
+
+ if (ret == 0) {
+ proto_info = (struct ec_response_get_protocol_info *)
+ proto_msg->data;
+ ec_dev->max_request = proto_info->max_request_packet_size -
+ sizeof(struct ec_host_request);
+ ec_dev->max_response = proto_info->max_response_packet_size -
+ sizeof(struct ec_host_response);
+ ec_dev->proto_version =
+ min(EC_HOST_REQUEST_VERSION,
+ fls(proto_info->protocol_versions) - 1);
+ dev_dbg(ec_dev->dev,
+ "using proto v%u\n",
+ ec_dev->proto_version);
+
+ ec_dev->din_size = ec_dev->max_response +
+ sizeof(struct ec_host_response) +
+ EC_MAX_RESPONSE_OVERHEAD;
+ ec_dev->dout_size = ec_dev->max_request +
+ sizeof(struct ec_host_request) +
+ EC_MAX_REQUEST_OVERHEAD;
+
+ /*
+ * Check for PD
+ */
+ ret = cros_ec_host_command_proto_query(ec_dev, 1, proto_msg);
+
+ if (ret) {
+ dev_dbg(ec_dev->dev, "no PD chip found: %d\n", ret);
+ ec_dev->max_passthru = 0;
+ } else {
+ dev_dbg(ec_dev->dev, "found PD chip\n");
+ ec_dev->max_passthru =
+ proto_info->max_request_packet_size -
+ sizeof(struct ec_host_request);
+ }
+ } else {
+ /* Try querying with a v2 hello message. */
+ ec_dev->proto_version = 2;
+ ret = cros_ec_host_command_proto_query_v2(ec_dev);
+
+ if (ret == 0) {
+ /* V2 hello succeeded. */
+ dev_dbg(ec_dev->dev, "falling back to proto v2\n");
+
+ ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE;
+ ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE;
+ ec_dev->max_passthru = 0;
+ ec_dev->pkt_xfer = NULL;
+ ec_dev->din_size = EC_MSG_BYTES;
+ ec_dev->dout_size = EC_MSG_BYTES;
+ } else {
+ /*
+ * It's possible for a test to occur too early when
+ * the EC isn't listening. If this happens, we'll
+ * test later when the first command is run.
+ */
+ ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN;
+ dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret);
+ goto exit;
+ }
+ }
+
+ devm_kfree(dev, ec_dev->din);
+ devm_kfree(dev, ec_dev->dout);
+
+ ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
+ if (!ec_dev->din) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL);
+ if (!ec_dev->dout) {
+ devm_kfree(dev, ec_dev->din);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+exit:
+ kfree(proto_msg);
+ return ret;
+}
+EXPORT_SYMBOL(cros_ec_query_all);
+
+int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
+ struct cros_ec_command *msg)
+{
+ int ret;
+
+ mutex_lock(&ec_dev->lock);
+ if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) {
+ ret = cros_ec_query_all(ec_dev);
+ if (ret) {
+ dev_err(ec_dev->dev,
+ "EC version unknown and query failed; aborting command\n");
+ mutex_unlock(&ec_dev->lock);
+ return ret;
+ }
+ }
+
+ if (msg->insize > ec_dev->max_response) {
+ dev_dbg(ec_dev->dev, "clamping message receive buffer\n");
+ msg->insize = ec_dev->max_response;
+ }
+
+ if (msg->command < EC_CMD_PASSTHRU_OFFSET(1)) {
+ if (msg->outsize > ec_dev->max_request) {
+ dev_err(ec_dev->dev,
+ "request of size %u is too big (max: %u)\n",
+ msg->outsize,
+ ec_dev->max_request);
+ mutex_unlock(&ec_dev->lock);
+ return -EMSGSIZE;
+ }
+ } else {
+ if (msg->outsize > ec_dev->max_passthru) {
+ dev_err(ec_dev->dev,
+ "passthru rq of size %u is too big (max: %u)\n",
+ msg->outsize,
+ ec_dev->max_passthru);
+ mutex_unlock(&ec_dev->lock);
+ return -EMSGSIZE;
+ }
+ }
+ ret = send_command(ec_dev, msg);
+ mutex_unlock(&ec_dev->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(cros_ec_cmd_xfer);
diff --git a/kernel/drivers/platform/chrome/cros_ec_sysfs.c b/kernel/drivers/platform/chrome/cros_ec_sysfs.c
index fb62ab6cc..f3baf9973 100644
--- a/kernel/drivers/platform/chrome/cros_ec_sysfs.c
+++ b/kernel/drivers/platform/chrome/cros_ec_sysfs.c
@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
+#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/types.h>
#include <linux/uaccess.h>
@@ -66,13 +67,19 @@ static ssize_t store_ec_reboot(struct device *dev,
{"hibernate", EC_REBOOT_HIBERNATE, 0},
{"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN},
};
- struct cros_ec_command msg = { 0 };
- struct ec_params_reboot_ec *param =
- (struct ec_params_reboot_ec *)msg.outdata;
+ struct cros_ec_command *msg;
+ struct ec_params_reboot_ec *param;
int got_cmd = 0, offset = 0;
int i;
int ret;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
+
+ msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ param = (struct ec_params_reboot_ec *)msg->data;
param->flags = 0;
while (1) {
@@ -100,19 +107,26 @@ static ssize_t store_ec_reboot(struct device *dev,
offset++;
}
- if (!got_cmd)
- return -EINVAL;
-
- msg.command = EC_CMD_REBOOT_EC;
- msg.outsize = sizeof(param);
- ret = cros_ec_cmd_xfer(ec, &msg);
- if (ret < 0)
- return ret;
- if (msg.result != EC_RES_SUCCESS) {
- dev_dbg(ec->dev, "EC result %d\n", msg.result);
- return -EINVAL;
+ if (!got_cmd) {
+ count = -EINVAL;
+ goto exit;
}
+ msg->version = 0;
+ msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset;
+ msg->outsize = sizeof(*param);
+ msg->insize = 0;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0) {
+ count = ret;
+ goto exit;
+ }
+ if (msg->result != EC_RES_SUCCESS) {
+ dev_dbg(ec->dev, "EC result %d\n", msg->result);
+ count = -EINVAL;
+ }
+exit:
+ kfree(msg);
return count;
}
@@ -123,22 +137,33 @@ static ssize_t show_ec_version(struct device *dev,
struct ec_response_get_version *r_ver;
struct ec_response_get_chip_info *r_chip;
struct ec_response_board_version *r_board;
- struct cros_ec_command msg = { 0 };
+ struct cros_ec_command *msg;
int ret;
int count = 0;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
+
+ msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
/* Get versions. RW may change. */
- msg.command = EC_CMD_GET_VERSION;
- msg.insize = sizeof(*r_ver);
- ret = cros_ec_cmd_xfer(ec, &msg);
- if (ret < 0)
- return ret;
- if (msg.result != EC_RES_SUCCESS)
- return scnprintf(buf, PAGE_SIZE,
- "ERROR: EC returned %d\n", msg.result);
+ msg->version = 0;
+ msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
+ msg->insize = sizeof(*r_ver);
+ msg->outsize = 0;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0) {
+ count = ret;
+ goto exit;
+ }
+ if (msg->result != EC_RES_SUCCESS) {
+ count = scnprintf(buf, PAGE_SIZE,
+ "ERROR: EC returned %d\n", msg->result);
+ goto exit;
+ }
- r_ver = (struct ec_response_get_version *)msg.indata;
+ r_ver = (struct ec_response_get_version *)msg->data;
/* Strings should be null-terminated, but let's be sure. */
r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0';
r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0';
@@ -152,33 +177,33 @@ static ssize_t show_ec_version(struct device *dev,
image_names[r_ver->current_image] : "?"));
/* Get build info. */
- msg.command = EC_CMD_GET_BUILD_INFO;
- msg.insize = sizeof(msg.indata);
- ret = cros_ec_cmd_xfer(ec, &msg);
+ msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset;
+ msg->insize = EC_HOST_PARAM_SIZE;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Build info: XFER ERROR %d\n", ret);
- else if (msg.result != EC_RES_SUCCESS)
+ else if (msg->result != EC_RES_SUCCESS)
count += scnprintf(buf + count, PAGE_SIZE - count,
- "Build info: EC error %d\n", msg.result);
+ "Build info: EC error %d\n", msg->result);
else {
- msg.indata[sizeof(msg.indata) - 1] = '\0';
+ msg->data[sizeof(msg->data) - 1] = '\0';
count += scnprintf(buf + count, PAGE_SIZE - count,
- "Build info: %s\n", msg.indata);
+ "Build info: %s\n", msg->data);
}
/* Get chip info. */
- msg.command = EC_CMD_GET_CHIP_INFO;
- msg.insize = sizeof(*r_chip);
- ret = cros_ec_cmd_xfer(ec, &msg);
+ msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset;
+ msg->insize = sizeof(*r_chip);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Chip info: XFER ERROR %d\n", ret);
- else if (msg.result != EC_RES_SUCCESS)
+ else if (msg->result != EC_RES_SUCCESS)
count += scnprintf(buf + count, PAGE_SIZE - count,
- "Chip info: EC error %d\n", msg.result);
+ "Chip info: EC error %d\n", msg->result);
else {
- r_chip = (struct ec_response_get_chip_info *)msg.indata;
+ r_chip = (struct ec_response_get_chip_info *)msg->data;
r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0';
r_chip->name[sizeof(r_chip->name) - 1] = '\0';
@@ -192,23 +217,25 @@ static ssize_t show_ec_version(struct device *dev,
}
/* Get board version */
- msg.command = EC_CMD_GET_BOARD_VERSION;
- msg.insize = sizeof(*r_board);
- ret = cros_ec_cmd_xfer(ec, &msg);
+ msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset;
+ msg->insize = sizeof(*r_board);
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Board version: XFER ERROR %d\n", ret);
- else if (msg.result != EC_RES_SUCCESS)
+ else if (msg->result != EC_RES_SUCCESS)
count += scnprintf(buf + count, PAGE_SIZE - count,
- "Board version: EC error %d\n", msg.result);
+ "Board version: EC error %d\n", msg->result);
else {
- r_board = (struct ec_response_board_version *)msg.indata;
+ r_board = (struct ec_response_board_version *)msg->data;
count += scnprintf(buf + count, PAGE_SIZE - count,
"Board version: %d\n",
r_board->board_version);
}
+exit:
+ kfree(msg);
return count;
}
@@ -216,27 +243,39 @@ static ssize_t show_ec_flashinfo(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ec_response_flash_info *resp;
- struct cros_ec_command msg = { 0 };
+ struct cros_ec_command *msg;
int ret;
- struct cros_ec_device *ec = dev_get_drvdata(dev);
+ struct cros_ec_dev *ec = container_of(dev,
+ struct cros_ec_dev, class_dev);
+
+ msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
/* The flash info shouldn't ever change, but ask each time anyway. */
- msg.command = EC_CMD_FLASH_INFO;
- msg.insize = sizeof(*resp);
- ret = cros_ec_cmd_xfer(ec, &msg);
+ msg->version = 0;
+ msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset;
+ msg->insize = sizeof(*resp);
+ msg->outsize = 0;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
if (ret < 0)
- return ret;
- if (msg.result != EC_RES_SUCCESS)
- return scnprintf(buf, PAGE_SIZE,
- "ERROR: EC returned %d\n", msg.result);
-
- resp = (struct ec_response_flash_info *)msg.indata;
-
- return scnprintf(buf, PAGE_SIZE,
- "FlashSize %d\nWriteSize %d\n"
- "EraseSize %d\nProtectSize %d\n",
- resp->flash_size, resp->write_block_size,
- resp->erase_block_size, resp->protect_block_size);
+ goto exit;
+ if (msg->result != EC_RES_SUCCESS) {
+ ret = scnprintf(buf, PAGE_SIZE,
+ "ERROR: EC returned %d\n", msg->result);
+ goto exit;
+ }
+
+ resp = (struct ec_response_flash_info *)msg->data;
+
+ ret = scnprintf(buf, PAGE_SIZE,
+ "FlashSize %d\nWriteSize %d\n"
+ "EraseSize %d\nProtectSize %d\n",
+ resp->flash_size, resp->write_block_size,
+ resp->erase_block_size, resp->protect_block_size);
+exit:
+ kfree(msg);
+ return ret;
}
/* Module initialization */
@@ -252,20 +291,7 @@ static struct attribute *__ec_attrs[] = {
NULL,
};
-static struct attribute_group ec_attr_group = {
+struct attribute_group cros_ec_attr_group = {
.attrs = __ec_attrs,
};
-void ec_dev_sysfs_init(struct cros_ec_device *ec)
-{
- int error;
-
- error = sysfs_create_group(&ec->vdev->kobj, &ec_attr_group);
- if (error)
- pr_warn("failed to create group: %d\n", error);
-}
-
-void ec_dev_sysfs_remove(struct cros_ec_device *ec)
-{
- sysfs_remove_group(&ec->vdev->kobj, &ec_attr_group);
-}
diff --git a/kernel/drivers/platform/chrome/cros_ec_vbc.c b/kernel/drivers/platform/chrome/cros_ec_vbc.c
new file mode 100644
index 000000000..564a0d08c
--- /dev/null
+++ b/kernel/drivers/platform/chrome/cros_ec_vbc.c
@@ -0,0 +1,137 @@
+/*
+ * cros_ec_vbc - Expose the vboot context nvram to userspace
+ *
+ * Copyright (C) 2015 Collabora Ltd.
+ *
+ * based on vendor driver,
+ *
+ * Copyright (C) 2012 The Chromium OS Authors
+ *
+ * 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.
+ */
+
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/slab.h>
+
+static ssize_t vboot_context_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *att, char *buf,
+ loff_t pos, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev,
+ class_dev);
+ struct cros_ec_device *ecdev = ec->ec_dev;
+ struct ec_params_vbnvcontext *params;
+ struct cros_ec_command *msg;
+ int err;
+ const size_t para_sz = sizeof(params->op);
+ const size_t resp_sz = sizeof(struct ec_response_vbnvcontext);
+ const size_t payload = max(para_sz, resp_sz);
+
+ msg = kmalloc(sizeof(*msg) + payload, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ /* NB: we only kmalloc()ated enough space for the op field */
+ params = (struct ec_params_vbnvcontext *)msg->data;
+ params->op = EC_VBNV_CONTEXT_OP_READ;
+
+ msg->version = EC_VER_VBNV_CONTEXT;
+ msg->command = EC_CMD_VBNV_CONTEXT;
+ msg->outsize = para_sz;
+ msg->insize = resp_sz;
+
+ err = cros_ec_cmd_xfer(ecdev, msg);
+ if (err < 0) {
+ dev_err(dev, "Error sending read request: %d\n", err);
+ kfree(msg);
+ return err;
+ }
+
+ memcpy(buf, msg->data, resp_sz);
+
+ kfree(msg);
+ return resp_sz;
+}
+
+static ssize_t vboot_context_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t pos, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev,
+ class_dev);
+ struct cros_ec_device *ecdev = ec->ec_dev;
+ struct ec_params_vbnvcontext *params;
+ struct cros_ec_command *msg;
+ int err;
+ const size_t para_sz = sizeof(*params);
+ const size_t data_sz = sizeof(params->block);
+
+ /* Only write full values */
+ if (count != data_sz)
+ return -EINVAL;
+
+ msg = kmalloc(sizeof(*msg) + para_sz, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ params = (struct ec_params_vbnvcontext *)msg->data;
+ params->op = EC_VBNV_CONTEXT_OP_WRITE;
+ memcpy(params->block, buf, data_sz);
+
+ msg->version = EC_VER_VBNV_CONTEXT;
+ msg->command = EC_CMD_VBNV_CONTEXT;
+ msg->outsize = para_sz;
+ msg->insize = 0;
+
+ err = cros_ec_cmd_xfer(ecdev, msg);
+ if (err < 0) {
+ dev_err(dev, "Error sending write request: %d\n", err);
+ kfree(msg);
+ return err;
+ }
+
+ kfree(msg);
+ return data_sz;
+}
+
+static umode_t cros_ec_vbc_is_visible(struct kobject *kobj,
+ struct bin_attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev,
+ class_dev);
+ struct device_node *np = ec->ec_dev->dev->of_node;
+
+ if (IS_ENABLED(CONFIG_OF) && np) {
+ if (of_property_read_bool(np, "google,has-vbc-nvram"))
+ return a->attr.mode;
+ }
+
+ return 0;
+}
+
+static BIN_ATTR_RW(vboot_context, 16);
+
+static struct bin_attribute *cros_ec_vbc_bin_attrs[] = {
+ &bin_attr_vboot_context,
+ NULL
+};
+
+struct attribute_group cros_ec_vbc_attr_group = {
+ .name = "vbc",
+ .bin_attrs = cros_ec_vbc_bin_attrs,
+ .is_bin_visible = cros_ec_vbc_is_visible,
+};
diff --git a/kernel/drivers/platform/goldfish/goldfish_pipe.c b/kernel/drivers/platform/goldfish/goldfish_pipe.c
index d9a09d963..e7a29e275 100644
--- a/kernel/drivers/platform/goldfish/goldfish_pipe.c
+++ b/kernel/drivers/platform/goldfish/goldfish_pipe.c
@@ -158,8 +158,8 @@ static u32 goldfish_cmd_status(struct goldfish_pipe *pipe, u32 cmd)
struct goldfish_pipe_dev *dev = pipe->dev;
spin_lock_irqsave(&dev->lock, flags);
- gf_write64((u64)(unsigned long)pipe, dev->base + PIPE_REG_CHANNEL,
- dev->base + PIPE_REG_CHANNEL_HIGH);
+ gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
+ dev->base + PIPE_REG_CHANNEL_HIGH);
writel(cmd, dev->base + PIPE_REG_COMMAND);
status = readl(dev->base + PIPE_REG_STATUS);
spin_unlock_irqrestore(&dev->lock, flags);
@@ -172,8 +172,8 @@ static void goldfish_cmd(struct goldfish_pipe *pipe, u32 cmd)
struct goldfish_pipe_dev *dev = pipe->dev;
spin_lock_irqsave(&dev->lock, flags);
- gf_write64((u64)(unsigned long)pipe, dev->base + PIPE_REG_CHANNEL,
- dev->base + PIPE_REG_CHANNEL_HIGH);
+ gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
+ dev->base + PIPE_REG_CHANNEL_HIGH);
writel(cmd, dev->base + PIPE_REG_COMMAND);
spin_unlock_irqrestore(&dev->lock, flags);
}
@@ -282,7 +282,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
return -EIO;
/* Null reads or writes succeeds */
- if (unlikely(bufflen) == 0)
+ if (unlikely(bufflen == 0))
return 0;
/* Check the buffer range for access */
@@ -327,12 +327,12 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer,
spin_lock_irqsave(&dev->lock, irq_flags);
if (access_with_param(dev, CMD_WRITE_BUFFER + cmd_offset,
address, avail, pipe, &status)) {
- gf_write64((u64)(unsigned long)pipe,
- dev->base + PIPE_REG_CHANNEL,
- dev->base + PIPE_REG_CHANNEL_HIGH);
+ gf_write_ptr(pipe, dev->base + PIPE_REG_CHANNEL,
+ dev->base + PIPE_REG_CHANNEL_HIGH);
writel(avail, dev->base + PIPE_REG_SIZE);
- gf_write64(address, dev->base + PIPE_REG_ADDRESS,
- dev->base + PIPE_REG_ADDRESS_HIGH);
+ gf_write_ptr((void *)address,
+ dev->base + PIPE_REG_ADDRESS,
+ dev->base + PIPE_REG_ADDRESS_HIGH);
writel(CMD_WRITE_BUFFER + cmd_offset,
dev->base + PIPE_REG_COMMAND);
status = readl(dev->base + PIPE_REG_STATUS);
diff --git a/kernel/drivers/platform/goldfish/pdev_bus.c b/kernel/drivers/platform/goldfish/pdev_bus.c
index 8c43589c3..1f52462f4 100644
--- a/kernel/drivers/platform/goldfish/pdev_bus.c
+++ b/kernel/drivers/platform/goldfish/pdev_bus.c
@@ -220,20 +220,10 @@ free_resources:
return ret;
}
-static int goldfish_pdev_bus_remove(struct platform_device *pdev)
-{
- iounmap(pdev_bus_base);
- free_irq(pdev_bus_irq, pdev);
- release_mem_region(pdev_bus_addr, pdev_bus_len);
- return 0;
-}
-
static struct platform_driver goldfish_pdev_bus_driver = {
.probe = goldfish_pdev_bus_probe,
- .remove = goldfish_pdev_bus_remove,
.driver = {
.name = "goldfish_pdev_bus"
}
};
-
-module_platform_driver(goldfish_pdev_bus_driver);
+builtin_platform_driver(goldfish_pdev_bus_driver);
diff --git a/kernel/drivers/platform/olpc/olpc-ec.c b/kernel/drivers/platform/olpc/olpc-ec.c
index f9119525f..f99b183d5 100644
--- a/kernel/drivers/platform/olpc/olpc-ec.c
+++ b/kernel/drivers/platform/olpc/olpc-ec.c
@@ -192,18 +192,15 @@ static ssize_t ec_dbgfs_cmd_write(struct file *file, const char __user *buf,
for (i = 0; i <= ec_cmd_bytes; i++)
ec_cmd[i] = ec_cmd_int[i];
- pr_debug("olpc-ec: debugfs cmd 0x%02x with %d args %02x %02x %02x %02x %02x, want %d returns\n",
- ec_cmd[0], ec_cmd_bytes, ec_cmd[1], ec_cmd[2],
- ec_cmd[3], ec_cmd[4], ec_cmd[5], ec_dbgfs_resp_bytes);
+ pr_debug("olpc-ec: debugfs cmd 0x%02x with %d args %5ph, want %d returns\n",
+ ec_cmd[0], ec_cmd_bytes, ec_cmd + 1,
+ ec_dbgfs_resp_bytes);
olpc_ec_cmd(ec_cmd[0], (ec_cmd_bytes == 0) ? NULL : &ec_cmd[1],
ec_cmd_bytes, ec_dbgfs_resp, ec_dbgfs_resp_bytes);
- pr_debug("olpc-ec: response %02x %02x %02x %02x %02x %02x %02x %02x (%d bytes expected)\n",
- ec_dbgfs_resp[0], ec_dbgfs_resp[1], ec_dbgfs_resp[2],
- ec_dbgfs_resp[3], ec_dbgfs_resp[4], ec_dbgfs_resp[5],
- ec_dbgfs_resp[6], ec_dbgfs_resp[7],
- ec_dbgfs_resp_bytes);
+ pr_debug("olpc-ec: response %8ph (%d bytes expected)\n",
+ ec_dbgfs_resp, ec_dbgfs_resp_bytes);
out:
mutex_unlock(&ec_dbgfs_lock);
diff --git a/kernel/drivers/platform/x86/Kconfig b/kernel/drivers/platform/x86/Kconfig
index f9f205cb1..1089eaa02 100644
--- a/kernel/drivers/platform/x86/Kconfig
+++ b/kernel/drivers/platform/x86/Kconfig
@@ -71,9 +71,10 @@ config ASUS_LAPTOP
depends on ACPI
select LEDS_CLASS
select NEW_LEDS
- select BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_CLASS_DEVICE
depends on INPUT
depends on RFKILL || RFKILL = n
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
select INPUT_POLLDEV
---help---
@@ -95,6 +96,7 @@ config DELL_LAPTOP
depends on X86
depends on DCDBAS
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL || RFKILL = n
depends on SERIO_I8042
select POWER_SUPPLY
@@ -109,6 +111,7 @@ config DELL_WMI
tristate "Dell WMI extras"
depends on ACPI_WMI
depends on INPUT
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
---help---
Say Y here if you want to support WMI-based hotkeys on Dell laptops.
@@ -138,12 +141,29 @@ config DELL_SMO8800
To compile this driver as a module, choose M here: the module will
be called dell-smo8800.
+config DELL_RBTN
+ tristate "Dell Airplane Mode Switch driver"
+ depends on ACPI
+ depends on INPUT
+ depends on RFKILL
+ ---help---
+ Say Y here if you want to support Dell Airplane Mode Switch ACPI
+ device on Dell laptops. Sometimes it has names: DELLABCE or DELRBTN.
+ This driver register rfkill device or input hotkey device depending
+ on hardware type (hw switch slider or keyboard toggle button). For
+ rfkill devices it receive HW switch events and set correct hard
+ rfkill state.
+
+ To compile this driver as a module, choose M here: the module will
+ be called dell-rbtn.
+
config FUJITSU_LAPTOP
tristate "Fujitsu Laptop Extras"
depends on ACPI
depends on INPUT
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on LEDS_CLASS || LEDS_CLASS=n
---help---
This is a driver for laptops built by Fujitsu:
@@ -247,6 +267,7 @@ config MSI_LAPTOP
tristate "MSI Laptop Extras"
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL
depends on INPUT && SERIO_I8042
select INPUT_SPARSEKMAP
@@ -280,6 +301,7 @@ config COMPAL_LAPTOP
tristate "Compal (and others) Laptop Extras"
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL
depends on HWMON
depends on POWER_SUPPLY
@@ -287,8 +309,8 @@ config COMPAL_LAPTOP
This is a driver for laptops built by Compal, and some models by
other brands (e.g. Dell, Toshiba).
- It adds support for rfkill, Bluetooth, WLAN and LCD brightness
- control.
+ It adds support for rfkill, Bluetooth, WLAN, LCD brightness, hwmon
+ and battery charging level control.
For a (possibly incomplete) list of supported laptops, please refer
to: Documentation/platform/x86-laptop-drivers.txt
@@ -296,7 +318,8 @@ config COMPAL_LAPTOP
config SONY_LAPTOP
tristate "Sony Laptop Extras"
depends on ACPI
- select BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
+ depends on BACKLIGHT_CLASS_DEVICE
depends on INPUT
depends on RFKILL
---help---
@@ -321,6 +344,8 @@ config IDEAPAD_LAPTOP
depends on RFKILL && INPUT
depends on SERIO_I8042
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
+ depends on ACPI_WMI || ACPI_WMI = n
select INPUT_SPARSEKMAP
help
This is a driver for Lenovo IdeaPad netbooks contains drivers for
@@ -331,8 +356,8 @@ config THINKPAD_ACPI
depends on ACPI
depends on INPUT
depends on RFKILL || RFKILL = n
- select BACKLIGHT_LCD_SUPPORT
- select BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
+ depends on BACKLIGHT_CLASS_DEVICE
select HWMON
select NVRAM
select NEW_LEDS
@@ -500,8 +525,9 @@ config EEEPC_LAPTOP
depends on ACPI
depends on INPUT
depends on RFKILL || RFKILL = n
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on HOTPLUG_PCI
- select BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_CLASS_DEVICE
select HWMON
select LEDS_CLASS
select NEW_LEDS
@@ -587,6 +613,7 @@ config MSI_WMI
depends on ACPI_WMI
depends on INPUT
depends on BACKLIGHT_CLASS_DEVICE
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
help
Say Y here if you want to support WMI-based hotkeys on MSI laptops.
@@ -612,7 +639,6 @@ config ACPI_TOSHIBA
select NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
depends on INPUT
- depends on RFKILL || RFKILL = n
depends on SERIO_I8042 || SERIO_I8042 = n
depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_POLLDEV
@@ -643,6 +669,7 @@ config ACPI_TOSHIBA
config TOSHIBA_BT_RFKILL
tristate "Toshiba Bluetooth RFKill switch support"
depends on ACPI
+ depends on RFKILL || RFKILL = n
---help---
This driver adds support for Bluetooth events for the RFKill
switch on modern Toshiba laptops with full ACPI support and
@@ -660,7 +687,7 @@ config TOSHIBA_HAPS
depends on ACPI
---help---
This driver adds support for the built-in accelerometer
- found on recent Toshiba laptops equiped with HID TOS620A
+ found on recent Toshiba laptops equipped with HID TOS620A
device.
This driver receives ACPI notify events 0x80 when the sensor
@@ -669,11 +696,29 @@ config TOSHIBA_HAPS
been stabilized.
Also provides sysfs entries to get/set the desired protection
- level and reseting the HDD protection interface.
+ level and resetting the HDD protection interface.
If you have a recent Toshiba laptop with a built-in accelerometer
device, say Y.
+config TOSHIBA_WMI
+ tristate "Toshiba WMI Hotkeys Driver (EXPERIMENTAL)"
+ default n
+ depends on ACPI_WMI
+ depends on INPUT
+ select INPUT_SPARSEKMAP
+ ---help---
+ This driver adds hotkey monitoring support to some Toshiba models
+ that manage the hotkeys via WMI events.
+
+ WARNING: This driver is incomplete as it lacks a proper keymap and the
+ *notify function only prints the ACPI event type value. Be warned that
+ you will need to provide some information if you have a Toshiba model
+ with WMI event hotkeys and want to help with the develpment of this
+ driver.
+
+ If you have a WMI-based hotkeys Toshiba laptop, say Y or M here.
+
config ACPI_CMPC
tristate "CMPC Laptop Extras"
depends on X86 && ACPI
@@ -824,6 +869,7 @@ config MXM_WMI
config INTEL_OAKTRAIL
tristate "Intel Oaktrail Platform Extras"
depends on ACPI
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on RFKILL && BACKLIGHT_CLASS_DEVICE && ACPI
---help---
Intel Oaktrail platform need this driver to provide interfaces to
@@ -885,4 +931,17 @@ config PVPANIC
a paravirtualized device provided by QEMU; it lets a virtual machine
(guest) communicate panic events to the host.
+config INTEL_PMC_IPC
+ tristate "Intel PMC IPC Driver"
+ depends on ACPI
+ ---help---
+ This driver provides support for PMC control on some Intel platforms.
+ The PMC is an ARC processor which defines IPC commands for communication
+ with other entities in the CPU.
+
+config SURFACE_PRO3_BUTTON
+ tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3 tablet"
+ depends on ACPI && INPUT
+ ---help---
+ This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3 tablet.
endif # X86_PLATFORM_DEVICES
diff --git a/kernel/drivers/platform/x86/Makefile b/kernel/drivers/platform/x86/Makefile
index f82232b1f..3ca78a3eb 100644
--- a/kernel/drivers/platform/x86/Makefile
+++ b/kernel/drivers/platform/x86/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
+obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
obj-$(CONFIG_ACERHDF) += acerhdf.o
obj-$(CONFIG_HP_ACCEL) += hp_accel.o
@@ -39,6 +40,7 @@ obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
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_SCU_IPC) += intel_scu_ipc.o
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
@@ -58,3 +60,5 @@ obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
+obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o
+obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o
diff --git a/kernel/drivers/platform/x86/acer-wmi.c b/kernel/drivers/platform/x86/acer-wmi.c
index 3ac29a1e8..1062fa42f 100644
--- a/kernel/drivers/platform/x86/acer-wmi.c
+++ b/kernel/drivers/platform/x86/acer-wmi.c
@@ -807,6 +807,7 @@ static const struct acpi_device_id norfkill_ids[] __initconst = {
{ "IBM0068", 0},
{ "LEN0068", 0},
{ "SNY5001", 0}, /* sony-laptop in charge */
+ { "HPQ6601", 0},
{ "", 0},
};
@@ -1661,58 +1662,6 @@ static void acer_rfkill_exit(void)
return;
}
-/*
- * sysfs interface
- */
-static ssize_t show_bool_threeg(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- u32 result; \
- acpi_status status;
-
- pr_info("This threeg sysfs will be removed in 2014 - used by: %s\n",
- current->comm);
- status = get_u32(&result, ACER_CAP_THREEG);
- if (ACPI_SUCCESS(status))
- return sprintf(buf, "%u\n", result);
- return sprintf(buf, "Read error\n");
-}
-
-static ssize_t set_bool_threeg(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- u32 tmp = simple_strtoul(buf, NULL, 10);
- acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
- pr_info("This threeg sysfs will be removed in 2014 - used by: %s\n",
- current->comm);
- if (ACPI_FAILURE(status))
- return -EINVAL;
- return count;
-}
-static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg,
- set_bool_threeg);
-
-static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- pr_info("This interface sysfs will be removed in 2014 - used by: %s\n",
- current->comm);
- switch (interface->type) {
- case ACER_AMW0:
- return sprintf(buf, "AMW0\n");
- case ACER_AMW0_V2:
- return sprintf(buf, "AMW0 v2\n");
- case ACER_WMID:
- return sprintf(buf, "WMID\n");
- case ACER_WMID_v2:
- return sprintf(buf, "WMID v2\n");
- default:
- return sprintf(buf, "Error!\n");
- }
-}
-
-static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL);
-
static void acer_wmi_notify(u32 value, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -2126,39 +2075,6 @@ static struct platform_driver acer_platform_driver = {
static struct platform_device *acer_platform_device;
-static int remove_sysfs(struct platform_device *device)
-{
- if (has_cap(ACER_CAP_THREEG))
- device_remove_file(&device->dev, &dev_attr_threeg);
-
- device_remove_file(&device->dev, &dev_attr_interface);
-
- return 0;
-}
-
-static int __init create_sysfs(void)
-{
- int retval = -ENOMEM;
-
- if (has_cap(ACER_CAP_THREEG)) {
- retval = device_create_file(&acer_platform_device->dev,
- &dev_attr_threeg);
- if (retval)
- goto error_sysfs;
- }
-
- retval = device_create_file(&acer_platform_device->dev,
- &dev_attr_interface);
- if (retval)
- goto error_sysfs;
-
- return 0;
-
-error_sysfs:
- remove_sysfs(acer_platform_device);
- return retval;
-}
-
static void remove_debugfs(void)
{
debugfs_remove(interface->debug.devices);
@@ -2246,14 +2162,10 @@ static int __init acer_wmi_init(void)
set_quirks();
if (dmi_check_system(video_vendor_dmi_table))
- acpi_video_dmi_promote_vendor();
- if (acpi_video_backlight_support()) {
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+
+ if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
interface->capability &= ~ACER_CAP_BRIGHTNESS;
- pr_info("Brightness must be controlled by acpi video driver\n");
- } else {
- pr_info("Disabling ACPI video driver\n");
- acpi_video_unregister_backlight();
- }
if (wmi_has_guid(WMID_GUID3)) {
if (ec_raw_mode) {
@@ -2293,10 +2205,6 @@ static int __init acer_wmi_init(void)
if (err)
goto error_device_add;
- err = create_sysfs();
- if (err)
- goto error_create_sys;
-
if (wmi_has_guid(WMID_GUID2)) {
interface->debug.wmid_devices = get_wmid_devices();
err = create_debugfs();
@@ -2310,8 +2218,6 @@ static int __init acer_wmi_init(void)
return 0;
error_create_debugfs:
- remove_sysfs(acer_platform_device);
-error_create_sys:
platform_device_del(acer_platform_device);
error_device_add:
platform_device_put(acer_platform_device);
@@ -2334,7 +2240,6 @@ static void __exit acer_wmi_exit(void)
if (has_cap(ACER_CAP_ACCEL))
acer_wmi_accel_destroy();
- remove_sysfs(acer_platform_device);
remove_debugfs();
platform_device_unregister(acer_platform_device);
platform_driver_unregister(&acer_platform_driver);
diff --git a/kernel/drivers/platform/x86/acerhdf.c b/kernel/drivers/platform/x86/acerhdf.c
index 594c918b5..460fa6708 100644
--- a/kernel/drivers/platform/x86/acerhdf.c
+++ b/kernel/drivers/platform/x86/acerhdf.c
@@ -346,8 +346,7 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal)
* as late as the polling interval is since we can't do that in the respective
* accessors of the module parameters.
*/
-static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal,
- unsigned long *t)
+static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, int *t)
{
int temp, err = 0;
@@ -372,7 +371,8 @@ static int acerhdf_bind(struct thermal_zone_device *thermal,
return 0;
if (thermal_zone_bind_cooling_device(thermal, 0, cdev,
- THERMAL_NO_LIMIT, THERMAL_NO_LIMIT)) {
+ THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
+ THERMAL_WEIGHT_DEFAULT)) {
pr_err("error binding cooling dev\n");
return -EINVAL;
}
@@ -452,7 +452,7 @@ static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip,
}
static int acerhdf_get_trip_hyst(struct thermal_zone_device *thermal, int trip,
- unsigned long *temp)
+ int *temp)
{
if (trip != 0)
return -EINVAL;
@@ -463,7 +463,7 @@ static int acerhdf_get_trip_hyst(struct thermal_zone_device *thermal, int trip,
}
static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
- unsigned long *temp)
+ int *temp)
{
if (trip == 0)
*temp = fanon;
@@ -476,7 +476,7 @@ static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
}
static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal,
- unsigned long *temperature)
+ int *temperature)
{
*temperature = ACERHDF_TEMP_CRIT;
return 0;
diff --git a/kernel/drivers/platform/x86/apple-gmux.c b/kernel/drivers/platform/x86/apple-gmux.c
index 680871500..976efeb3f 100644
--- a/kernel/drivers/platform/x86/apple-gmux.c
+++ b/kernel/drivers/platform/x86/apple-gmux.c
@@ -346,7 +346,7 @@ gmux_active_client(struct apple_gmux_data *gmux_data)
return VGA_SWITCHEROO_DIS;
}
-static struct vga_switcheroo_handler gmux_handler = {
+static const struct vga_switcheroo_handler gmux_handler = {
.switchto = gmux_switchto,
.power_state = gmux_set_power_state,
.get_client_id = gmux_get_client_id,
@@ -550,8 +550,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
* backlight control and supports more levels than other options.
* Disable the other backlight choices.
*/
- acpi_video_dmi_promote_vendor();
- acpi_video_unregister();
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
apple_bl_unregister();
gmux_data->power_state = VGA_SWITCHEROO_ON;
@@ -645,7 +644,6 @@ static void gmux_remove(struct pnp_dev *pnp)
apple_gmux_data = NULL;
kfree(gmux_data);
- acpi_video_dmi_demote_vendor();
acpi_video_register();
apple_bl_register();
}
diff --git a/kernel/drivers/platform/x86/asus-laptop.c b/kernel/drivers/platform/x86/asus-laptop.c
index 46b274693..f2b5d0a8a 100644
--- a/kernel/drivers/platform/x86/asus-laptop.c
+++ b/kernel/drivers/platform/x86/asus-laptop.c
@@ -54,6 +54,7 @@
#include <linux/slab.h>
#include <linux/dmi.h>
#include <linux/acpi.h>
+#include <acpi/video.h>
#define ASUS_LAPTOP_VERSION "0.42"
@@ -331,6 +332,7 @@ static const struct key_entry asus_keymap[] = {
{KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */
{KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */
{KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
+ {KE_KEY, 0x6A, { KEY_TOUCHPAD_TOGGLE } }, /* Lock Touchpad Fn + F9 */
{KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, /* Lock Touchpad */
{KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */
{KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */
@@ -1884,12 +1886,11 @@ static int asus_acpi_add(struct acpi_device *device)
if (result)
goto fail_platform;
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
result = asus_backlight_init(asus);
if (result)
goto fail_backlight;
- } else
- pr_info("Backlight controlled by ACPI video driver\n");
+ }
result = asus_input_init(asus);
if (result)
diff --git a/kernel/drivers/platform/x86/asus-nb-wmi.c b/kernel/drivers/platform/x86/asus-nb-wmi.c
index abdaed34c..131fee2b0 100644
--- a/kernel/drivers/platform/x86/asus-nb-wmi.c
+++ b/kernel/drivers/platform/x86/asus-nb-wmi.c
@@ -128,6 +128,24 @@ static const struct dmi_system_id asus_quirks[] = {
},
{
.callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X456UA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X456UA"),
+ },
+ .driver_data = &quirk_asus_wapf4,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X456UF",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X456UF"),
+ },
+ .driver_data = &quirk_asus_wapf4,
+ },
+ {
+ .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X501U",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
diff --git a/kernel/drivers/platform/x86/asus-wmi.c b/kernel/drivers/platform/x86/asus-wmi.c
index 7543a56e0..f96f7b865 100644
--- a/kernel/drivers/platform/x86/asus-wmi.c
+++ b/kernel/drivers/platform/x86/asus-wmi.c
@@ -78,6 +78,7 @@ MODULE_LICENSE("GPL");
#define ASUS_WMI_METHODID_GPID 0x44495047 /* Get Panel ID?? (Resol) */
#define ASUS_WMI_METHODID_QMOD 0x444F4D51 /* Quiet MODe */
#define ASUS_WMI_METHODID_SPLV 0x4C425053 /* Set Panel Light Value */
+#define ASUS_WMI_METHODID_AGFN 0x4E464741 /* FaN? */
#define ASUS_WMI_METHODID_SFUN 0x4E554653 /* FUNCtionalities */
#define ASUS_WMI_METHODID_SDSP 0x50534453 /* Set DiSPlay output */
#define ASUS_WMI_METHODID_GDSP 0x50534447 /* Get DiSPlay output */
@@ -150,12 +151,38 @@ MODULE_LICENSE("GPL");
#define ASUS_WMI_DSTS_BRIGHTNESS_MASK 0x000000FF
#define ASUS_WMI_DSTS_MAX_BRIGTH_MASK 0x0000FF00
+#define ASUS_FAN_DESC "cpu_fan"
+#define ASUS_FAN_MFUN 0x13
+#define ASUS_FAN_SFUN_READ 0x06
+#define ASUS_FAN_SFUN_WRITE 0x07
+#define ASUS_FAN_CTRL_MANUAL 1
+#define ASUS_FAN_CTRL_AUTO 2
+
struct bios_args {
u32 arg0;
u32 arg1;
} __packed;
/*
+ * Struct that's used for all methods called via AGFN. Naming is
+ * identically to the AML code.
+ */
+struct agfn_args {
+ u16 mfun; /* probably "Multi-function" to be called */
+ u16 sfun; /* probably "Sub-function" to be called */
+ u16 len; /* size of the hole struct, including subfunction fields */
+ u8 stas; /* not used by now */
+ u8 err; /* zero on success */
+} __packed;
+
+/* struct used for calling fan read and write methods */
+struct fan_args {
+ struct agfn_args agfn; /* common fields */
+ u8 fan; /* fan number: 0: set auto mode 1: 1st fan */
+ u32 speed; /* read: RPM/100 - write: 0-255 */
+} __packed;
+
+/*
* <platform>/ - debugfs root directory
* dev_id - current dev_id
* ctrl_param - current ctrl_param
@@ -204,6 +231,10 @@ struct asus_wmi {
struct asus_rfkill gps;
struct asus_rfkill uwb;
+ bool asus_hwmon_fan_manual_mode;
+ int asus_hwmon_num_fans;
+ int asus_hwmon_pwm;
+
struct hotplug_slot *hotplug_slot;
struct mutex hotplug_lock;
struct mutex wmi_lock;
@@ -294,6 +325,36 @@ exit:
return 0;
}
+static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args)
+{
+ struct acpi_buffer input;
+ u64 phys_addr;
+ u32 retval;
+ u32 status = -1;
+
+ /*
+ * Copy to dma capable address otherwise memory corruption occurs as
+ * bios has to be able to access it.
+ */
+ input.pointer = kzalloc(args.length, GFP_DMA | GFP_KERNEL);
+ input.length = args.length;
+ if (!input.pointer)
+ return -ENOMEM;
+ phys_addr = virt_to_phys(input.pointer);
+ memcpy(input.pointer, args.pointer, args.length);
+
+ status = asus_wmi_evaluate_method(ASUS_WMI_METHODID_AGFN,
+ phys_addr, 0, &retval);
+ if (!status)
+ memcpy(args.pointer, input.pointer, args.length);
+
+ kfree(input.pointer);
+ if (status)
+ return -ENXIO;
+
+ return retval;
+}
+
static int asus_wmi_get_devstate(struct asus_wmi *asus, u32 dev_id, u32 *retval)
{
return asus_wmi_evaluate_method(asus->dsts_id, dev_id, 0, retval);
@@ -521,7 +582,7 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)
static int asus_wmi_led_init(struct asus_wmi *asus)
{
- int rv = 0;
+ int rv = 0, led_val;
asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
if (!asus->led_workqueue)
@@ -541,9 +602,11 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
goto error;
}
- if (kbd_led_read(asus, NULL, NULL) >= 0) {
+ led_val = kbd_led_read(asus, NULL, NULL);
+ if (led_val >= 0) {
INIT_WORK(&asus->kbd_led_work, kbd_led_update);
+ asus->kbd_led_wk = led_val;
asus->kbd_led.name = "asus::kbd_backlight";
asus->kbd_led.brightness_set = kbd_led_set;
asus->kbd_led.brightness_get = kbd_led_get;
@@ -1022,35 +1085,228 @@ exit:
/*
* Hwmon device
*/
-static ssize_t asus_hwmon_pwm1(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan,
+ int *speed)
+{
+ struct fan_args args = {
+ .agfn.len = sizeof(args),
+ .agfn.mfun = ASUS_FAN_MFUN,
+ .agfn.sfun = ASUS_FAN_SFUN_READ,
+ .fan = fan,
+ .speed = 0,
+ };
+ struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
+ int status;
+
+ if (fan != 1)
+ return -EINVAL;
+
+ status = asus_wmi_evaluate_method_agfn(input);
+
+ if (status || args.agfn.err)
+ return -ENXIO;
+
+ if (speed)
+ *speed = args.speed;
+
+ return 0;
+}
+
+static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan,
+ int *speed)
+{
+ struct fan_args args = {
+ .agfn.len = sizeof(args),
+ .agfn.mfun = ASUS_FAN_MFUN,
+ .agfn.sfun = ASUS_FAN_SFUN_WRITE,
+ .fan = fan,
+ .speed = speed ? *speed : 0,
+ };
+ struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
+ int status;
+
+ /* 1: for setting 1st fan's speed 0: setting auto mode */
+ if (fan != 1 && fan != 0)
+ return -EINVAL;
+
+ status = asus_wmi_evaluate_method_agfn(input);
+
+ if (status || args.agfn.err)
+ return -ENXIO;
+
+ if (speed && fan == 1)
+ asus->asus_hwmon_pwm = *speed;
+
+ return 0;
+}
+
+/*
+ * Check if we can read the speed of one fan. If true we assume we can also
+ * control it.
+ */
+static int asus_hwmon_get_fan_number(struct asus_wmi *asus, int *num_fans)
+{
+ int status;
+ int speed = 0;
+
+ *num_fans = 0;
+
+ status = asus_hwmon_agfn_fan_speed_read(asus, 1, &speed);
+ if (!status)
+ *num_fans = 1;
+
+ return 0;
+}
+
+static int asus_hwmon_fan_set_auto(struct asus_wmi *asus)
+{
+ int status;
+
+ status = asus_hwmon_agfn_fan_speed_write(asus, 0, NULL);
+ if (status)
+ return -ENXIO;
+
+ asus->asus_hwmon_fan_manual_mode = false;
+
+ return 0;
+}
+
+static int asus_hwmon_fan_rpm_show(struct device *dev, int fan)
{
struct asus_wmi *asus = dev_get_drvdata(dev);
- u32 value;
+ int value;
+ int ret;
+
+ /* no speed readable on manual mode */
+ if (asus->asus_hwmon_fan_manual_mode)
+ return -ENXIO;
+
+ ret = asus_hwmon_agfn_fan_speed_read(asus, fan+1, &value);
+ if (ret) {
+ pr_warn("reading fan speed failed: %d\n", ret);
+ return -ENXIO;
+ }
+
+ return value;
+}
+
+static void asus_hwmon_pwm_show(struct asus_wmi *asus, int fan, int *value)
+{
int err;
- err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value);
+ if (asus->asus_hwmon_pwm >= 0) {
+ *value = asus->asus_hwmon_pwm;
+ return;
+ }
+ err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, value);
if (err < 0)
- return err;
+ return;
- value &= 0xFF;
-
- if (value == 1) /* Low Speed */
- value = 85;
- else if (value == 2)
- value = 170;
- else if (value == 3)
- value = 255;
- else if (value != 0) {
- pr_err("Unknown fan speed %#x\n", value);
- value = -1;
+ *value &= 0xFF;
+
+ if (*value == 1) /* Low Speed */
+ *value = 85;
+ else if (*value == 2)
+ *value = 170;
+ else if (*value == 3)
+ *value = 255;
+ else if (*value) {
+ pr_err("Unknown fan speed %#x\n", *value);
+ *value = -1;
}
+}
+
+static ssize_t pwm1_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+ int value;
+
+ asus_hwmon_pwm_show(asus, 0, &value);
return sprintf(buf, "%d\n", value);
}
+static ssize_t pwm1_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count) {
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+ int value;
+ int state;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &value);
+
+ if (ret)
+ return ret;
+
+ value = clamp(value, 0, 255);
+
+ state = asus_hwmon_agfn_fan_speed_write(asus, 1, &value);
+ if (state)
+ pr_warn("Setting fan speed failed: %d\n", state);
+ else
+ asus->asus_hwmon_fan_manual_mode = true;
+
+ return count;
+}
+
+static ssize_t fan1_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int value = asus_hwmon_fan_rpm_show(dev, 0);
+
+ return sprintf(buf, "%d\n", value < 0 ? -1 : value*100);
+
+}
+
+static ssize_t pwm1_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+
+ if (asus->asus_hwmon_fan_manual_mode)
+ return sprintf(buf, "%d\n", ASUS_FAN_CTRL_MANUAL);
+
+ return sprintf(buf, "%d\n", ASUS_FAN_CTRL_AUTO);
+}
+
+static ssize_t pwm1_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct asus_wmi *asus = dev_get_drvdata(dev);
+ int status = 0;
+ int state;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &state);
+
+ if (ret)
+ return ret;
+
+ if (state == ASUS_FAN_CTRL_MANUAL)
+ asus->asus_hwmon_fan_manual_mode = true;
+ else
+ status = asus_hwmon_fan_set_auto(asus);
+
+ if (status)
+ return status;
+
+ return count;
+}
+
+static ssize_t fan1_label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", ASUS_FAN_DESC);
+}
+
static ssize_t asus_hwmon_temp1(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1064,16 +1320,26 @@ static ssize_t asus_hwmon_temp1(struct device *dev,
if (err < 0)
return err;
- value = KELVIN_TO_CELSIUS((value & 0xFFFF)) * 1000;
+ value = DECI_KELVIN_TO_CELSIUS((value & 0xFFFF)) * 1000;
return sprintf(buf, "%d\n", value);
}
-static DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL);
+/* Fan1 */
+static DEVICE_ATTR_RW(pwm1);
+static DEVICE_ATTR_RW(pwm1_enable);
+static DEVICE_ATTR_RO(fan1_input);
+static DEVICE_ATTR_RO(fan1_label);
+
+/* Temperature */
static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);
static struct attribute *hwmon_attributes[] = {
&dev_attr_pwm1.attr,
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_label.attr,
+
&dev_attr_temp1_input.attr,
NULL
};
@@ -1084,19 +1350,28 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
struct device *dev = container_of(kobj, struct device, kobj);
struct platform_device *pdev = to_platform_device(dev->parent);
struct asus_wmi *asus = platform_get_drvdata(pdev);
- bool ok = true;
int dev_id = -1;
+ int fan_attr = -1;
u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
+ bool ok = true;
if (attr == &dev_attr_pwm1.attr)
dev_id = ASUS_WMI_DEVID_FAN_CTRL;
else if (attr == &dev_attr_temp1_input.attr)
dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
+
+ if (attr == &dev_attr_fan1_input.attr
+ || attr == &dev_attr_fan1_label.attr
+ || attr == &dev_attr_pwm1.attr
+ || attr == &dev_attr_pwm1_enable.attr) {
+ fan_attr = 1;
+ }
+
if (dev_id != -1) {
int err = asus_wmi_get_devstate(asus, dev_id, &value);
- if (err < 0)
+ if (err < 0 && fan_attr == -1)
return 0; /* can't return negative here */
}
@@ -1112,10 +1387,16 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
if (value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000
|| (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT)))
ok = false;
+ else
+ ok = fan_attr <= asus->asus_hwmon_num_fans;
} else if (dev_id == ASUS_WMI_DEVID_THERMAL_CTRL) {
/* If value is zero, something is clearly wrong */
- if (value == 0)
+ if (!value)
ok = false;
+ } else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) {
+ ok = true;
+ } else {
+ ok = false;
}
return ok ? attr->mode : 0;
@@ -1364,7 +1645,7 @@ static void asus_wmi_notify(u32 value, void *context)
code = ASUS_WMI_BRN_DOWN;
if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
asus_wmi_backlight_notify(asus, orig_code);
goto exit;
}
@@ -1401,7 +1682,7 @@ static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid,
int rv, err, value;
value = asus_wmi_get_devstate_simple(asus, devid);
- if (value == -ENODEV) /* Check device presence */
+ if (value < 0)
return value;
rv = parse_arg(buf, count, &value);
@@ -1723,6 +2004,25 @@ error_debugfs:
return -ENOMEM;
}
+static int asus_wmi_fan_init(struct asus_wmi *asus)
+{
+ int status;
+
+ asus->asus_hwmon_pwm = -1;
+ asus->asus_hwmon_num_fans = -1;
+ asus->asus_hwmon_fan_manual_mode = false;
+
+ status = asus_hwmon_get_fan_number(asus, &asus->asus_hwmon_num_fans);
+ if (status) {
+ asus->asus_hwmon_num_fans = 0;
+ pr_warn("Could not determine number of fans: %d\n", status);
+ return -ENXIO;
+ }
+
+ pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans);
+ return 0;
+}
+
/*
* WMI Driver
*/
@@ -1756,6 +2056,9 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_input;
+ err = asus_wmi_fan_init(asus); /* probably no problems on error */
+ asus_hwmon_fan_set_auto(asus);
+
err = asus_wmi_hwmon_init(asus);
if (err)
goto fail_hwmon;
@@ -1772,17 +2075,16 @@ static int asus_wmi_add(struct platform_device *pdev)
stop this from showing up */
chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
if (chassis_type && !strcmp(chassis_type, "3"))
- acpi_video_dmi_promote_vendor();
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+
if (asus->driver->quirks->wmi_backlight_power)
- acpi_video_dmi_promote_vendor();
- if (!acpi_video_backlight_support()) {
- pr_info("Disabling ACPI video driver\n");
- acpi_video_unregister();
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
err = asus_wmi_backlight_init(asus);
if (err && err != -ENODEV)
goto fail_backlight;
- } else
- pr_info("Backlight controlled by ACPI video driver\n");
+ }
status = wmi_install_notify_handler(asus->driver->event_guid,
asus_wmi_notify, asus);
@@ -1832,6 +2134,7 @@ static int asus_wmi_remove(struct platform_device *device)
asus_wmi_rfkill_exit(asus);
asus_wmi_debugfs_exit(asus);
asus_wmi_platform_exit(asus);
+ asus_hwmon_fan_set_auto(asus);
kfree(asus);
return 0;
@@ -1859,6 +2162,16 @@ static int asus_hotk_thaw(struct device *device)
return 0;
}
+static int asus_hotk_resume(struct device *device)
+{
+ struct asus_wmi *asus = dev_get_drvdata(device);
+
+ if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
+
+ return 0;
+}
+
static int asus_hotk_restore(struct device *device)
{
struct asus_wmi *asus = dev_get_drvdata(device);
@@ -1889,6 +2202,8 @@ static int asus_hotk_restore(struct device *device)
bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB);
rfkill_set_sw_state(asus->uwb.rfkill, bl);
}
+ if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
+ queue_work(asus->led_workqueue, &asus->kbd_led_work);
return 0;
}
@@ -1896,6 +2211,7 @@ static int asus_hotk_restore(struct device *device)
static const struct dev_pm_ops asus_pm_ops = {
.thaw = asus_hotk_thaw,
.restore = asus_hotk_restore,
+ .resume = asus_hotk_resume,
};
static int asus_wmi_probe(struct platform_device *pdev)
diff --git a/kernel/drivers/platform/x86/compal-laptop.c b/kernel/drivers/platform/x86/compal-laptop.c
index b4e94471f..e1c2b6d4b 100644
--- a/kernel/drivers/platform/x86/compal-laptop.c
+++ b/kernel/drivers/platform/x86/compal-laptop.c
@@ -82,7 +82,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/power_supply.h>
#include <linux/fb.h>
-
+#include <acpi/video.h>
/* ======= */
/* Defines */
@@ -151,6 +151,8 @@
#define BAT_STATUS2 0xF1
#define BAT_STOP_CHARGE1 0xF2
#define BAT_STOP_CHARGE2 0xF3
+#define BAT_CHARGE_LIMIT 0x03
+#define BAT_CHARGE_LIMIT_MAX 100
#define BAT_S0_DISCHARGE (1 << 0)
#define BAT_S0_DISCHRG_CRITICAL (1 << 2)
@@ -601,6 +603,12 @@ static int bat_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CHARGE_NOW:
val->intval = ec_read_u16(BAT_CHARGE_NOW) * 1000;
break;
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+ val->intval = ec_read_u8(BAT_CHARGE_LIMIT);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
+ val->intval = BAT_CHARGE_LIMIT_MAX;
+ break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = ec_read_u8(BAT_CAPACITY);
break;
@@ -634,6 +642,36 @@ static int bat_get_property(struct power_supply *psy,
return 0;
}
+static int bat_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ int level;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+ level = val->intval;
+ if (level < 0 || level > BAT_CHARGE_LIMIT_MAX)
+ return -EINVAL;
+ if (ec_write(BAT_CHARGE_LIMIT, level) < 0)
+ return -EIO;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int bat_writeable_property(struct power_supply *psy,
+ enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+ return 1;
+ default:
+ return 0;
+ }
+}
@@ -726,6 +764,8 @@ static enum power_supply_property compal_bat_properties[] = {
POWER_SUPPLY_PROP_POWER_NOW,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
+ POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
@@ -880,11 +920,12 @@ static const struct power_supply_desc psy_bat_desc = {
.properties = compal_bat_properties,
.num_properties = ARRAY_SIZE(compal_bat_properties),
.get_property = bat_get_property,
+ .set_property = bat_set_property,
+ .property_is_writeable = bat_writeable_property,
};
static void initialize_power_supply_data(struct compal_data *data)
{
-
ec_read_sequence(BAT_MANUFACTURER_NAME_ADDR,
data->bat_manufacturer_name,
BAT_MANUFACTURER_NAME_LEN);
@@ -959,7 +1000,7 @@ static int __init compal_init(void)
return -ENODEV;
}
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
diff --git a/kernel/drivers/platform/x86/dell-laptop.c b/kernel/drivers/platform/x86/dell-laptop.c
index 2c1d5f543..aaeeae81e 100644
--- a/kernel/drivers/platform/x86/dell-laptop.c
+++ b/kernel/drivers/platform/x86/dell-laptop.c
@@ -31,7 +31,9 @@
#include <linux/slab.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <acpi/video.h>
#include "../../firmware/dcdbas.h"
+#include "dell-rbtn.h"
#define BRIGHTNESS_TOKEN 0x7d
#define KBD_LED_OFF_TOKEN 0x01E1
@@ -307,12 +309,15 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
static struct calling_interface_buffer *buffer;
static DEFINE_MUTEX(buffer_mutex);
-static int hwswitch_state;
+static void clear_buffer(void)
+{
+ memset(buffer, 0, sizeof(struct calling_interface_buffer));
+}
static void get_buffer(void)
{
mutex_lock(&buffer_mutex);
- memset(buffer, 0, sizeof(struct calling_interface_buffer));
+ clear_buffer();
}
static void release_buffer(void)
@@ -421,66 +426,166 @@ static inline int dell_smi_error(int value)
}
}
-/* Derived from information in DellWirelessCtl.cpp:
- Class 17, select 11 is radio control. It returns an array of 32-bit values.
-
- Input byte 0 = 0: Wireless information
-
- result[0]: return code
- result[1]:
- Bit 0: Hardware switch supported
- Bit 1: Wifi locator supported
- Bit 2: Wifi is supported
- Bit 3: Bluetooth is supported
- Bit 4: WWAN is supported
- Bit 5: Wireless keyboard supported
- Bits 6-7: Reserved
- Bit 8: Wifi is installed
- Bit 9: Bluetooth is installed
- Bit 10: WWAN is installed
- Bits 11-15: Reserved
- Bit 16: Hardware switch is on
- Bit 17: Wifi is blocked
- Bit 18: Bluetooth is blocked
- Bit 19: WWAN is blocked
- Bits 20-31: Reserved
- result[2]: NVRAM size in bytes
- result[3]: NVRAM format version number
-
- Input byte 0 = 2: Wireless switch configuration
- result[0]: return code
- result[1]:
- Bit 0: Wifi controlled by switch
- Bit 1: Bluetooth controlled by switch
- Bit 2: WWAN controlled by switch
- Bits 3-6: Reserved
- Bit 7: Wireless switch config locked
- Bit 8: Wifi locator enabled
- Bits 9-14: Reserved
- Bit 15: Wifi locator setting locked
- Bits 16-31: Reserved
-*/
+/*
+ * Derived from information in smbios-wireless-ctl:
+ *
+ * cbSelect 17, Value 11
+ *
+ * Return Wireless Info
+ * cbArg1, byte0 = 0x00
+ *
+ * cbRes1 Standard return codes (0, -1, -2)
+ * cbRes2 Info bit flags:
+ *
+ * 0 Hardware switch supported (1)
+ * 1 WiFi locator supported (1)
+ * 2 WLAN supported (1)
+ * 3 Bluetooth (BT) supported (1)
+ * 4 WWAN supported (1)
+ * 5 Wireless KBD supported (1)
+ * 6 Uw b supported (1)
+ * 7 WiGig supported (1)
+ * 8 WLAN installed (1)
+ * 9 BT installed (1)
+ * 10 WWAN installed (1)
+ * 11 Uw b installed (1)
+ * 12 WiGig installed (1)
+ * 13-15 Reserved (0)
+ * 16 Hardware (HW) switch is On (1)
+ * 17 WLAN disabled (1)
+ * 18 BT disabled (1)
+ * 19 WWAN disabled (1)
+ * 20 Uw b disabled (1)
+ * 21 WiGig disabled (1)
+ * 20-31 Reserved (0)
+ *
+ * cbRes3 NVRAM size in bytes
+ * cbRes4, byte 0 NVRAM format version number
+ *
+ *
+ * Set QuickSet Radio Disable Flag
+ * cbArg1, byte0 = 0x01
+ * cbArg1, byte1
+ * Radio ID value:
+ * 0 Radio Status
+ * 1 WLAN ID
+ * 2 BT ID
+ * 3 WWAN ID
+ * 4 UWB ID
+ * 5 WIGIG ID
+ * cbArg1, byte2 Flag bits:
+ * 0 QuickSet disables radio (1)
+ * 1-7 Reserved (0)
+ *
+ * cbRes1 Standard return codes (0, -1, -2)
+ * cbRes2 QuickSet (QS) radio disable bit map:
+ * 0 QS disables WLAN
+ * 1 QS disables BT
+ * 2 QS disables WWAN
+ * 3 QS disables UWB
+ * 4 QS disables WIGIG
+ * 5-31 Reserved (0)
+ *
+ * Wireless Switch Configuration
+ * cbArg1, byte0 = 0x02
+ *
+ * cbArg1, byte1
+ * Subcommand:
+ * 0 Get config
+ * 1 Set config
+ * 2 Set WiFi locator enable/disable
+ * cbArg1,byte2
+ * Switch settings (if byte 1==1):
+ * 0 WLAN sw itch control (1)
+ * 1 BT sw itch control (1)
+ * 2 WWAN sw itch control (1)
+ * 3 UWB sw itch control (1)
+ * 4 WiGig sw itch control (1)
+ * 5-7 Reserved (0)
+ * cbArg1, byte2 Enable bits (if byte 1==2):
+ * 0 Enable WiFi locator (1)
+ *
+ * cbRes1 Standard return codes (0, -1, -2)
+ * cbRes2 QuickSet radio disable bit map:
+ * 0 WLAN controlled by sw itch (1)
+ * 1 BT controlled by sw itch (1)
+ * 2 WWAN controlled by sw itch (1)
+ * 3 UWB controlled by sw itch (1)
+ * 4 WiGig controlled by sw itch (1)
+ * 5-6 Reserved (0)
+ * 7 Wireless sw itch config locked (1)
+ * 8 WiFi locator enabled (1)
+ * 9-14 Reserved (0)
+ * 15 WiFi locator setting locked (1)
+ * 16-31 Reserved (0)
+ *
+ * Read Local Config Data (LCD)
+ * cbArg1, byte0 = 0x10
+ * cbArg1, byte1 NVRAM index low byte
+ * cbArg1, byte2 NVRAM index high byte
+ * cbRes1 Standard return codes (0, -1, -2)
+ * cbRes2 4 bytes read from LCD[index]
+ * cbRes3 4 bytes read from LCD[index+4]
+ * cbRes4 4 bytes read from LCD[index+8]
+ *
+ * Write Local Config Data (LCD)
+ * cbArg1, byte0 = 0x11
+ * cbArg1, byte1 NVRAM index low byte
+ * cbArg1, byte2 NVRAM index high byte
+ * cbArg2 4 bytes to w rite at LCD[index]
+ * cbArg3 4 bytes to w rite at LCD[index+4]
+ * cbArg4 4 bytes to w rite at LCD[index+8]
+ * cbRes1 Standard return codes (0, -1, -2)
+ *
+ * Populate Local Config Data from NVRAM
+ * cbArg1, byte0 = 0x12
+ * cbRes1 Standard return codes (0, -1, -2)
+ *
+ * Commit Local Config Data to NVRAM
+ * cbArg1, byte0 = 0x13
+ * cbRes1 Standard return codes (0, -1, -2)
+ */
static int dell_rfkill_set(void *data, bool blocked)
{
int disable = blocked ? 1 : 0;
unsigned long radio = (unsigned long)data;
int hwswitch_bit = (unsigned long)data - 1;
+ int hwswitch;
+ int status;
+ int ret;
get_buffer();
+
+ dell_send_request(buffer, 17, 11);
+ ret = buffer->output[0];
+ status = buffer->output[1];
+
+ if (ret != 0)
+ goto out;
+
+ clear_buffer();
+
+ buffer->input[0] = 0x2;
dell_send_request(buffer, 17, 11);
+ ret = buffer->output[0];
+ hwswitch = buffer->output[1];
/* If the hardware switch controls this radio, and the hardware
switch is disabled, always disable the radio */
- if ((hwswitch_state & BIT(hwswitch_bit)) &&
- !(buffer->output[1] & BIT(16)))
+ if (ret == 0 && (hwswitch & BIT(hwswitch_bit)) &&
+ (status & BIT(0)) && !(status & BIT(16)))
disable = 1;
+ clear_buffer();
+
buffer->input[0] = (1 | (radio<<8) | (disable << 16));
dell_send_request(buffer, 17, 11);
+ ret = buffer->output[0];
+ out:
release_buffer();
- return 0;
+ return dell_smi_error(ret);
}
/* Must be called with the buffer held */
@@ -490,6 +595,7 @@ static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
if (status & BIT(0)) {
/* Has hw-switch, sync sw_state to BIOS */
int block = rfkill_blocked(rfkill);
+ clear_buffer();
buffer->input[0] = (1 | (radio << 8) | (block << 16));
dell_send_request(buffer, 17, 11);
} else {
@@ -499,23 +605,43 @@ static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
}
static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
- int status)
+ int status, int hwswitch)
{
- if (hwswitch_state & (BIT(radio - 1)))
+ if (hwswitch & (BIT(radio - 1)))
rfkill_set_hw_state(rfkill, !(status & BIT(16)));
}
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
{
+ int radio = ((unsigned long)data & 0xF);
+ int hwswitch;
int status;
+ int ret;
get_buffer();
+
dell_send_request(buffer, 17, 11);
+ ret = buffer->output[0];
status = buffer->output[1];
- dell_rfkill_update_hw_state(rfkill, (unsigned long)data, status);
+ if (ret != 0 || !(status & BIT(0))) {
+ release_buffer();
+ return;
+ }
+
+ clear_buffer();
+
+ buffer->input[0] = 0x2;
+ dell_send_request(buffer, 17, 11);
+ ret = buffer->output[0];
+ hwswitch = buffer->output[1];
release_buffer();
+
+ if (ret != 0)
+ return;
+
+ dell_rfkill_update_hw_state(rfkill, radio, status, hwswitch);
}
static const struct rfkill_ops dell_rfkill_ops = {
@@ -527,13 +653,27 @@ static struct dentry *dell_laptop_dir;
static int dell_debugfs_show(struct seq_file *s, void *data)
{
+ int hwswitch_state;
+ int hwswitch_ret;
int status;
+ int ret;
get_buffer();
+
dell_send_request(buffer, 17, 11);
+ ret = buffer->output[0];
status = buffer->output[1];
+
+ clear_buffer();
+
+ buffer->input[0] = 0x2;
+ dell_send_request(buffer, 17, 11);
+ hwswitch_ret = buffer->output[0];
+ hwswitch_state = buffer->output[1];
+
release_buffer();
+ seq_printf(s, "return:\t%d\n", ret);
seq_printf(s, "status:\t0x%X\n", status);
seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
status & BIT(0));
@@ -547,12 +687,21 @@ static int dell_debugfs_show(struct seq_file *s, void *data)
(status & BIT(4)) >> 4);
seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
(status & BIT(5)) >> 5);
+ seq_printf(s, "Bit 6 : UWB supported: %lu\n",
+ (status & BIT(6)) >> 6);
+ seq_printf(s, "Bit 7 : WiGig supported: %lu\n",
+ (status & BIT(7)) >> 7);
seq_printf(s, "Bit 8 : Wifi is installed: %lu\n",
(status & BIT(8)) >> 8);
seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n",
(status & BIT(9)) >> 9);
seq_printf(s, "Bit 10: WWAN is installed: %lu\n",
(status & BIT(10)) >> 10);
+ seq_printf(s, "Bit 11: UWB installed: %lu\n",
+ (status & BIT(11)) >> 11);
+ seq_printf(s, "Bit 12: WiGig installed: %lu\n",
+ (status & BIT(12)) >> 12);
+
seq_printf(s, "Bit 16: Hardware switch is on: %lu\n",
(status & BIT(16)) >> 16);
seq_printf(s, "Bit 17: Wifi is blocked: %lu\n",
@@ -561,14 +710,23 @@ static int dell_debugfs_show(struct seq_file *s, void *data)
(status & BIT(18)) >> 18);
seq_printf(s, "Bit 19: WWAN is blocked: %lu\n",
(status & BIT(19)) >> 19);
+ seq_printf(s, "Bit 20: UWB is blocked: %lu\n",
+ (status & BIT(20)) >> 20);
+ seq_printf(s, "Bit 21: WiGig is blocked: %lu\n",
+ (status & BIT(21)) >> 21);
- seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
+ seq_printf(s, "\nhwswitch_return:\t%d\n", hwswitch_ret);
+ seq_printf(s, "hwswitch_state:\t0x%X\n", hwswitch_state);
seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n",
hwswitch_state & BIT(0));
seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
(hwswitch_state & BIT(1)) >> 1);
seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n",
(hwswitch_state & BIT(2)) >> 2);
+ seq_printf(s, "Bit 3 : UWB controlled by switch: %lu\n",
+ (hwswitch_state & BIT(3)) >> 3);
+ seq_printf(s, "Bit 4 : WiGig controlled by switch: %lu\n",
+ (hwswitch_state & BIT(4)) >> 4);
seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n",
(hwswitch_state & BIT(7)) >> 7);
seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n",
@@ -594,25 +752,43 @@ static const struct file_operations dell_debugfs_fops = {
static void dell_update_rfkill(struct work_struct *ignored)
{
+ int hwswitch = 0;
int status;
+ int ret;
get_buffer();
+
dell_send_request(buffer, 17, 11);
+ ret = buffer->output[0];
status = buffer->output[1];
+ if (ret != 0)
+ goto out;
+
+ clear_buffer();
+
+ buffer->input[0] = 0x2;
+ dell_send_request(buffer, 17, 11);
+ ret = buffer->output[0];
+
+ if (ret == 0 && (status & BIT(0)))
+ hwswitch = buffer->output[1];
+
if (wifi_rfkill) {
- dell_rfkill_update_hw_state(wifi_rfkill, 1, status);
+ dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
}
if (bluetooth_rfkill) {
- dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status);
+ dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
+ hwswitch);
dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
}
if (wwan_rfkill) {
- dell_rfkill_update_hw_state(wwan_rfkill, 3, status);
+ dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
}
+ out:
release_buffer();
}
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
@@ -641,6 +817,20 @@ static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
return false;
}
+static int (*dell_rbtn_notifier_register_func)(struct notifier_block *);
+static int (*dell_rbtn_notifier_unregister_func)(struct notifier_block *);
+
+static int dell_laptop_rbtn_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ schedule_delayed_work(&dell_rfkill_work, 0);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block dell_laptop_rbtn_notifier = {
+ .notifier_call = dell_laptop_rbtn_notifier_call,
+};
+
static int __init dell_setup_rfkill(void)
{
int status, ret, whitelisted;
@@ -660,21 +850,17 @@ static int __init dell_setup_rfkill(void)
get_buffer();
dell_send_request(buffer, 17, 11);
+ ret = buffer->output[0];
status = buffer->output[1];
- buffer->input[0] = 0x2;
- dell_send_request(buffer, 17, 11);
- hwswitch_state = buffer->output[1];
release_buffer();
- if (!(status & BIT(0))) {
- if (force_rfkill) {
- /* No hwsitch, clear all hw-controlled bits */
- hwswitch_state &= ~7;
- } else {
- /* rfkill is only tested on laptops with a hwswitch */
- return 0;
- }
- }
+ /* dell wireless info smbios call is not supported */
+ if (ret != 0)
+ return 0;
+
+ /* rfkill is only tested on laptops with a hwswitch */
+ if (!(status & BIT(0)) && !force_rfkill)
+ return 0;
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
@@ -717,10 +903,62 @@ static int __init dell_setup_rfkill(void)
goto err_wwan;
}
- ret = i8042_install_filter(dell_laptop_i8042_filter);
- if (ret) {
- pr_warn("Unable to install key filter\n");
+ /*
+ * Dell Airplane Mode Switch driver (dell-rbtn) supports ACPI devices
+ * which can receive events from HW slider switch.
+ *
+ * Dell SMBIOS on whitelisted models supports controlling radio devices
+ * but does not support receiving HW button switch events. We can use
+ * i8042 filter hook function to receive keyboard data and handle
+ * keycode for HW button.
+ *
+ * So if it is possible we will use Dell Airplane Mode Switch ACPI
+ * driver for receiving HW events and Dell SMBIOS for setting rfkill
+ * states. If ACPI driver or device is not available we will fallback to
+ * i8042 filter hook function.
+ *
+ * To prevent duplicate rfkill devices which control and do same thing,
+ * dell-rbtn driver will automatically remove its own rfkill devices
+ * once function dell_rbtn_notifier_register() is called.
+ */
+
+ dell_rbtn_notifier_register_func =
+ symbol_request(dell_rbtn_notifier_register);
+ if (dell_rbtn_notifier_register_func) {
+ dell_rbtn_notifier_unregister_func =
+ symbol_request(dell_rbtn_notifier_unregister);
+ if (!dell_rbtn_notifier_unregister_func) {
+ symbol_put(dell_rbtn_notifier_register);
+ dell_rbtn_notifier_register_func = NULL;
+ }
+ }
+
+ if (dell_rbtn_notifier_register_func) {
+ ret = dell_rbtn_notifier_register_func(
+ &dell_laptop_rbtn_notifier);
+ symbol_put(dell_rbtn_notifier_register);
+ dell_rbtn_notifier_register_func = NULL;
+ if (ret != 0) {
+ symbol_put(dell_rbtn_notifier_unregister);
+ dell_rbtn_notifier_unregister_func = NULL;
+ }
+ } else {
+ pr_info("Symbols from dell-rbtn acpi driver are not available\n");
+ ret = -ENODEV;
+ }
+
+ if (ret == 0) {
+ pr_info("Using dell-rbtn acpi driver for receiving events\n");
+ } else if (ret != -ENODEV) {
+ pr_warn("Unable to register dell rbtn notifier\n");
goto err_filter;
+ } else {
+ ret = i8042_install_filter(dell_laptop_i8042_filter);
+ if (ret) {
+ pr_warn("Unable to install key filter\n");
+ goto err_filter;
+ }
+ pr_info("Using i8042 filter function for receiving events\n");
}
return 0;
@@ -743,6 +981,14 @@ err_wifi:
static void dell_cleanup_rfkill(void)
{
+ if (dell_rbtn_notifier_unregister_func) {
+ dell_rbtn_notifier_unregister_func(&dell_laptop_rbtn_notifier);
+ symbol_put(dell_rbtn_notifier_unregister);
+ dell_rbtn_notifier_unregister_func = NULL;
+ } else {
+ i8042_remove_filter(dell_laptop_i8042_filter);
+ }
+ cancel_delayed_work_sync(&dell_rfkill_work);
if (wifi_rfkill) {
rfkill_unregister(wifi_rfkill);
rfkill_destroy(wifi_rfkill);
@@ -759,47 +1005,50 @@ static void dell_cleanup_rfkill(void)
static int dell_send_intensity(struct backlight_device *bd)
{
- int ret = 0;
+ int token;
+ int ret;
+
+ token = find_token_location(BRIGHTNESS_TOKEN);
+ if (token == -1)
+ return -ENODEV;
get_buffer();
- buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
+ buffer->input[0] = token;
buffer->input[1] = bd->props.brightness;
- if (buffer->input[0] == -1) {
- ret = -ENODEV;
- goto out;
- }
-
if (power_supply_is_system_supplied() > 0)
dell_send_request(buffer, 1, 2);
else
dell_send_request(buffer, 1, 1);
- out:
+ ret = dell_smi_error(buffer->output[0]);
+
release_buffer();
return ret;
}
static int dell_get_intensity(struct backlight_device *bd)
{
- int ret = 0;
+ int token;
+ int ret;
- get_buffer();
- buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
+ token = find_token_location(BRIGHTNESS_TOKEN);
+ if (token == -1)
+ return -ENODEV;
- if (buffer->input[0] == -1) {
- ret = -ENODEV;
- goto out;
- }
+ get_buffer();
+ buffer->input[0] = token;
if (power_supply_is_system_supplied() > 0)
dell_send_request(buffer, 0, 2);
else
dell_send_request(buffer, 0, 1);
- ret = buffer->output[1];
+ if (buffer->output[0])
+ ret = dell_smi_error(buffer->output[0]);
+ else
+ ret = buffer->output[1];
- out:
release_buffer();
return ret;
}
@@ -1863,6 +2112,7 @@ static void kbd_led_exit(void)
static int __init dell_init(void)
{
int max_intensity = 0;
+ int token;
int ret;
if (!dmi_check_system(dell_device_table))
@@ -1918,21 +2168,18 @@ static int __init dell_init(void)
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
&dell_debugfs_fops);
-#ifdef CONFIG_ACPI
- /* In the event of an ACPI backlight being available, don't
- * register the platform controller.
- */
- if (acpi_video_backlight_support())
+ if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return 0;
-#endif
- get_buffer();
- buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
- if (buffer->input[0] != -1) {
+ token = find_token_location(BRIGHTNESS_TOKEN);
+ if (token != -1) {
+ get_buffer();
+ buffer->input[0] = token;
dell_send_request(buffer, 0, 2);
- max_intensity = buffer->output[3];
+ if (buffer->output[0] == 0)
+ max_intensity = buffer->output[3];
+ release_buffer();
}
- release_buffer();
if (max_intensity) {
struct backlight_properties props;
@@ -1959,8 +2206,6 @@ static int __init dell_init(void)
return 0;
fail_backlight:
- i8042_remove_filter(dell_laptop_i8042_filter);
- cancel_delayed_work_sync(&dell_rfkill_work);
dell_cleanup_rfkill();
fail_rfkill:
free_page((unsigned long)buffer);
@@ -1981,8 +2226,6 @@ static void __exit dell_exit(void)
if (quirks && quirks->touchpad_led)
touchpad_led_exit();
kbd_led_exit();
- i8042_remove_filter(dell_laptop_i8042_filter);
- cancel_delayed_work_sync(&dell_rfkill_work);
backlight_device_unregister(dell_backlight_device);
dell_cleanup_rfkill();
if (platform_device) {
@@ -1993,7 +2236,14 @@ static void __exit dell_exit(void)
free_page((unsigned long)buffer);
}
-module_init(dell_init);
+/* dell-rbtn.c driver export functions which will not work correctly (and could
+ * cause kernel crash) if they are called before dell-rbtn.c init code. This is
+ * not problem when dell-rbtn.c is compiled as external module. When both files
+ * (dell-rbtn.c and dell-laptop.c) are compiled statically into kernel, then we
+ * need to ensure that dell_init() will be called after initializing dell-rbtn.
+ * This can be achieved by late_initcall() instead module_init().
+ */
+late_initcall(dell_init);
module_exit(dell_exit);
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
diff --git a/kernel/drivers/platform/x86/dell-rbtn.c b/kernel/drivers/platform/x86/dell-rbtn.c
new file mode 100644
index 000000000..cd410e392
--- /dev/null
+++ b/kernel/drivers/platform/x86/dell-rbtn.c
@@ -0,0 +1,423 @@
+/*
+ Dell Airplane Mode Switch driver
+ Copyright (C) 2014-2015 Pali Rohár <pali.rohar@gmail.com>
+
+ 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.
+*/
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/rfkill.h>
+#include <linux/input.h>
+
+enum rbtn_type {
+ RBTN_UNKNOWN,
+ RBTN_TOGGLE,
+ RBTN_SLIDER,
+};
+
+struct rbtn_data {
+ enum rbtn_type type;
+ struct rfkill *rfkill;
+ struct input_dev *input_dev;
+};
+
+
+/*
+ * acpi functions
+ */
+
+static enum rbtn_type rbtn_check(struct acpi_device *device)
+{
+ unsigned long long output;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(device->handle, "CRBT", NULL, &output);
+ if (ACPI_FAILURE(status))
+ return RBTN_UNKNOWN;
+
+ switch (output) {
+ case 0:
+ case 1:
+ return RBTN_TOGGLE;
+ case 2:
+ case 3:
+ return RBTN_SLIDER;
+ default:
+ return RBTN_UNKNOWN;
+ }
+}
+
+static int rbtn_get(struct acpi_device *device)
+{
+ unsigned long long output;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(device->handle, "GRBT", NULL, &output);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ return !output;
+}
+
+static int rbtn_acquire(struct acpi_device *device, bool enable)
+{
+ struct acpi_object_list input;
+ union acpi_object param;
+ acpi_status status;
+
+ param.type = ACPI_TYPE_INTEGER;
+ param.integer.value = enable;
+ input.count = 1;
+ input.pointer = &param;
+
+ status = acpi_evaluate_object(device->handle, "ARBT", &input, NULL);
+ if (ACPI_FAILURE(status))
+ return -EINVAL;
+
+ return 0;
+}
+
+
+/*
+ * rfkill device
+ */
+
+static void rbtn_rfkill_query(struct rfkill *rfkill, void *data)
+{
+ struct acpi_device *device = data;
+ int state;
+
+ state = rbtn_get(device);
+ if (state < 0)
+ return;
+
+ rfkill_set_states(rfkill, state, state);
+}
+
+static int rbtn_rfkill_set_block(void *data, bool blocked)
+{
+ /* NOTE: setting soft rfkill state is not supported */
+ return -EINVAL;
+}
+
+static struct rfkill_ops rbtn_ops = {
+ .query = rbtn_rfkill_query,
+ .set_block = rbtn_rfkill_set_block,
+};
+
+static int rbtn_rfkill_init(struct acpi_device *device)
+{
+ struct rbtn_data *rbtn_data = device->driver_data;
+ int ret;
+
+ if (rbtn_data->rfkill)
+ return 0;
+
+ /*
+ * NOTE: rbtn controls all radio devices, not only WLAN
+ * but rfkill interface does not support "ANY" type
+ * so "WLAN" type is used
+ */
+ rbtn_data->rfkill = rfkill_alloc("dell-rbtn", &device->dev,
+ RFKILL_TYPE_WLAN, &rbtn_ops, device);
+ if (!rbtn_data->rfkill)
+ return -ENOMEM;
+
+ ret = rfkill_register(rbtn_data->rfkill);
+ if (ret) {
+ rfkill_destroy(rbtn_data->rfkill);
+ rbtn_data->rfkill = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rbtn_rfkill_exit(struct acpi_device *device)
+{
+ struct rbtn_data *rbtn_data = device->driver_data;
+
+ if (!rbtn_data->rfkill)
+ return;
+
+ rfkill_unregister(rbtn_data->rfkill);
+ rfkill_destroy(rbtn_data->rfkill);
+ rbtn_data->rfkill = NULL;
+}
+
+static void rbtn_rfkill_event(struct acpi_device *device)
+{
+ struct rbtn_data *rbtn_data = device->driver_data;
+
+ if (rbtn_data->rfkill)
+ rbtn_rfkill_query(rbtn_data->rfkill, device);
+}
+
+
+/*
+ * input device
+ */
+
+static int rbtn_input_init(struct rbtn_data *rbtn_data)
+{
+ int ret;
+
+ rbtn_data->input_dev = input_allocate_device();
+ if (!rbtn_data->input_dev)
+ return -ENOMEM;
+
+ rbtn_data->input_dev->name = "DELL Wireless hotkeys";
+ rbtn_data->input_dev->phys = "dellabce/input0";
+ rbtn_data->input_dev->id.bustype = BUS_HOST;
+ rbtn_data->input_dev->evbit[0] = BIT(EV_KEY);
+ set_bit(KEY_RFKILL, rbtn_data->input_dev->keybit);
+
+ ret = input_register_device(rbtn_data->input_dev);
+ if (ret) {
+ input_free_device(rbtn_data->input_dev);
+ rbtn_data->input_dev = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+static void rbtn_input_exit(struct rbtn_data *rbtn_data)
+{
+ input_unregister_device(rbtn_data->input_dev);
+ rbtn_data->input_dev = NULL;
+}
+
+static void rbtn_input_event(struct rbtn_data *rbtn_data)
+{
+ input_report_key(rbtn_data->input_dev, KEY_RFKILL, 1);
+ input_sync(rbtn_data->input_dev);
+ input_report_key(rbtn_data->input_dev, KEY_RFKILL, 0);
+ input_sync(rbtn_data->input_dev);
+}
+
+
+/*
+ * acpi driver
+ */
+
+static int rbtn_add(struct acpi_device *device);
+static int rbtn_remove(struct acpi_device *device);
+static void rbtn_notify(struct acpi_device *device, u32 event);
+
+static const struct acpi_device_id rbtn_ids[] = {
+ { "DELRBTN", 0 },
+ { "DELLABCE", 0 },
+ { "", 0 },
+};
+
+static struct acpi_driver rbtn_driver = {
+ .name = "dell-rbtn",
+ .ids = rbtn_ids,
+ .ops = {
+ .add = rbtn_add,
+ .remove = rbtn_remove,
+ .notify = rbtn_notify,
+ },
+ .owner = THIS_MODULE,
+};
+
+
+/*
+ * notifier export functions
+ */
+
+static bool auto_remove_rfkill = true;
+
+static ATOMIC_NOTIFIER_HEAD(rbtn_chain_head);
+
+static int rbtn_inc_count(struct device *dev, void *data)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct rbtn_data *rbtn_data = device->driver_data;
+ int *count = data;
+
+ if (rbtn_data->type == RBTN_SLIDER)
+ (*count)++;
+
+ return 0;
+}
+
+static int rbtn_switch_dev(struct device *dev, void *data)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct rbtn_data *rbtn_data = device->driver_data;
+ bool enable = data;
+
+ if (rbtn_data->type != RBTN_SLIDER)
+ return 0;
+
+ if (enable)
+ rbtn_rfkill_init(device);
+ else
+ rbtn_rfkill_exit(device);
+
+ return 0;
+}
+
+int dell_rbtn_notifier_register(struct notifier_block *nb)
+{
+ bool first;
+ int count;
+ int ret;
+
+ count = 0;
+ ret = driver_for_each_device(&rbtn_driver.drv, NULL, &count,
+ rbtn_inc_count);
+ if (ret || count == 0)
+ return -ENODEV;
+
+ first = !rbtn_chain_head.head;
+
+ ret = atomic_notifier_chain_register(&rbtn_chain_head, nb);
+ if (ret != 0)
+ return ret;
+
+ if (auto_remove_rfkill && first)
+ ret = driver_for_each_device(&rbtn_driver.drv, NULL,
+ (void *)false, rbtn_switch_dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dell_rbtn_notifier_register);
+
+int dell_rbtn_notifier_unregister(struct notifier_block *nb)
+{
+ int ret;
+
+ ret = atomic_notifier_chain_unregister(&rbtn_chain_head, nb);
+ if (ret != 0)
+ return ret;
+
+ if (auto_remove_rfkill && !rbtn_chain_head.head)
+ ret = driver_for_each_device(&rbtn_driver.drv, NULL,
+ (void *)true, rbtn_switch_dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister);
+
+
+/*
+ * acpi driver functions
+ */
+
+static int rbtn_add(struct acpi_device *device)
+{
+ struct rbtn_data *rbtn_data;
+ enum rbtn_type type;
+ int ret = 0;
+
+ type = rbtn_check(device);
+ if (type == RBTN_UNKNOWN) {
+ dev_info(&device->dev, "Unknown device type\n");
+ return -EINVAL;
+ }
+
+ ret = rbtn_acquire(device, true);
+ if (ret < 0) {
+ dev_err(&device->dev, "Cannot enable device\n");
+ return ret;
+ }
+
+ rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL);
+ if (!rbtn_data)
+ return -ENOMEM;
+
+ rbtn_data->type = type;
+ device->driver_data = rbtn_data;
+
+ switch (rbtn_data->type) {
+ case RBTN_TOGGLE:
+ ret = rbtn_input_init(rbtn_data);
+ break;
+ case RBTN_SLIDER:
+ if (auto_remove_rfkill && rbtn_chain_head.head)
+ ret = 0;
+ else
+ ret = rbtn_rfkill_init(device);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+
+}
+
+static int rbtn_remove(struct acpi_device *device)
+{
+ struct rbtn_data *rbtn_data = device->driver_data;
+
+ switch (rbtn_data->type) {
+ case RBTN_TOGGLE:
+ rbtn_input_exit(rbtn_data);
+ break;
+ case RBTN_SLIDER:
+ rbtn_rfkill_exit(device);
+ break;
+ default:
+ break;
+ }
+
+ rbtn_acquire(device, false);
+ device->driver_data = NULL;
+
+ return 0;
+}
+
+static void rbtn_notify(struct acpi_device *device, u32 event)
+{
+ struct rbtn_data *rbtn_data = device->driver_data;
+
+ if (event != 0x80) {
+ dev_info(&device->dev, "Received unknown event (0x%x)\n",
+ event);
+ return;
+ }
+
+ switch (rbtn_data->type) {
+ case RBTN_TOGGLE:
+ rbtn_input_event(rbtn_data);
+ break;
+ case RBTN_SLIDER:
+ rbtn_rfkill_event(device);
+ atomic_notifier_call_chain(&rbtn_chain_head, event, device);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*
+ * module functions
+ */
+
+module_acpi_driver(rbtn_driver);
+
+module_param(auto_remove_rfkill, bool, 0444);
+
+MODULE_PARM_DESC(auto_remove_rfkill, "Automatically remove rfkill devices when "
+ "other modules start receiving events "
+ "from this module and re-add them when "
+ "the last module stops receiving events "
+ "(default true)");
+MODULE_DEVICE_TABLE(acpi, rbtn_ids);
+MODULE_DESCRIPTION("Dell Airplane Mode Switch driver");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/platform/x86/dell-rbtn.h b/kernel/drivers/platform/x86/dell-rbtn.h
new file mode 100644
index 000000000..c59cc6b8e
--- /dev/null
+++ b/kernel/drivers/platform/x86/dell-rbtn.h
@@ -0,0 +1,24 @@
+/*
+ Dell Airplane Mode Switch driver
+ Copyright (C) 2014-2015 Pali Rohár <pali.rohar@gmail.com>
+
+ 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.
+*/
+
+#ifndef _DELL_RBTN_H_
+#define _DELL_RBTN_H_
+
+struct notifier_block;
+
+int dell_rbtn_notifier_register(struct notifier_block *nb);
+int dell_rbtn_notifier_unregister(struct notifier_block *nb);
+
+#endif
diff --git a/kernel/drivers/platform/x86/dell-wmi.c b/kernel/drivers/platform/x86/dell-wmi.c
index 6512a06bc..f2d77fe69 100644
--- a/kernel/drivers/platform/x86/dell-wmi.c
+++ b/kernel/drivers/platform/x86/dell-wmi.c
@@ -35,6 +35,7 @@
#include <linux/acpi.h>
#include <linux/string.h>
#include <linux/dmi.h>
+#include <acpi/video.h>
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
@@ -397,7 +398,7 @@ static int __init dell_wmi_init(void)
}
dmi_walk(find_hk_type, NULL);
- acpi_video = acpi_video_backlight_support();
+ acpi_video = acpi_video_get_backlight_type() != acpi_backlight_vendor;
err = dell_wmi_input_setup();
if (err)
diff --git a/kernel/drivers/platform/x86/eeepc-laptop.c b/kernel/drivers/platform/x86/eeepc-laptop.c
index 844c2096b..8cdf315f9 100644
--- a/kernel/drivers/platform/x86/eeepc-laptop.c
+++ b/kernel/drivers/platform/x86/eeepc-laptop.c
@@ -37,6 +37,7 @@
#include <linux/pci_hotplug.h>
#include <linux/leds.h>
#include <linux/dmi.h>
+#include <acpi/video.h>
#define EEEPC_LAPTOP_VERSION "0.1"
#define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver"
@@ -1433,12 +1434,10 @@ static int eeepc_acpi_add(struct acpi_device *device)
if (result)
goto fail_platform;
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
result = eeepc_backlight_init(eeepc);
if (result)
goto fail_backlight;
- } else {
- pr_info("Backlight controlled by ACPI video driver\n");
}
result = eeepc_input_init(eeepc);
diff --git a/kernel/drivers/platform/x86/fujitsu-laptop.c b/kernel/drivers/platform/x86/fujitsu-laptop.c
index 2a9afa261..1c62caff9 100644
--- a/kernel/drivers/platform/x86/fujitsu-laptop.c
+++ b/kernel/drivers/platform/x86/fujitsu-laptop.c
@@ -72,6 +72,7 @@
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
#include <linux/leds.h>
#endif
+#include <acpi/video.h>
#define FUJITSU_DRIVER_VERSION "0.6.0"
@@ -1099,7 +1100,7 @@ static int __init fujitsu_init(void)
/* Register backlight stuff */
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
@@ -1137,8 +1138,7 @@ static int __init fujitsu_init(void)
}
/* Sync backlight power status (needs FUJ02E3 device, hence deferred) */
-
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3)
fujitsu->bl_device->props.power = FB_BLANK_POWERDOWN;
else
diff --git a/kernel/drivers/platform/x86/hp-wireless.c b/kernel/drivers/platform/x86/hp-wireless.c
index 4e4cc8bd7..988eedbd7 100644
--- a/kernel/drivers/platform/x86/hp-wireless.c
+++ b/kernel/drivers/platform/x86/hp-wireless.c
@@ -114,14 +114,9 @@ static int __init hpwl_init(void)
pr_info("Initializing HPQ6001 module\n");
err = acpi_bus_register_driver(&hpwl_driver);
- if (err) {
+ if (err)
pr_err("Unable to register HP wireless control driver.\n");
- goto error_acpi_register;
- }
-
- return 0;
-error_acpi_register:
return err;
}
diff --git a/kernel/drivers/platform/x86/ibm_rtl.c b/kernel/drivers/platform/x86/ibm_rtl.c
index 97c2be195..c62e5e11c 100644
--- a/kernel/drivers/platform/x86/ibm_rtl.c
+++ b/kernel/drivers/platform/x86/ibm_rtl.c
@@ -33,7 +33,7 @@
#include <linux/mutex.h>
#include <asm/bios_ebda.h>
-#include <asm-generic/io-64-nonatomic-lo-hi.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
static bool force;
module_param(force, bool, 0);
diff --git a/kernel/drivers/platform/x86/ideapad-laptop.c b/kernel/drivers/platform/x86/ideapad-laptop.c
index cd78f1166..d78ee151c 100644
--- a/kernel/drivers/platform/x86/ideapad-laptop.c
+++ b/kernel/drivers/platform/x86/ideapad-laptop.c
@@ -38,6 +38,7 @@
#include <linux/i8042.h>
#include <linux/dmi.h>
#include <linux/device.h>
+#include <acpi/video.h>
#define IDEAPAD_RFKILL_DEV_NUM (3)
@@ -46,6 +47,10 @@
#define CFG_WIFI_BIT (18)
#define CFG_CAMERA_BIT (19)
+#if IS_ENABLED(CONFIG_ACPI_WMI)
+static const char ideapad_wmi_fnesc_event[] = "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6";
+#endif
+
enum {
VPCCMD_R_VPC1 = 0x10,
VPCCMD_R_BL_MAX,
@@ -566,6 +571,8 @@ static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 65, { KEY_PROG4 } },
{ KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
{ KE_KEY, 67, { KEY_TOUCHPAD_ON } },
+ { KE_KEY, 128, { KEY_ESC } },
+
{ KE_END, 0 },
};
@@ -824,6 +831,19 @@ static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
}
}
+#if IS_ENABLED(CONFIG_ACPI_WMI)
+static void ideapad_wmi_notify(u32 value, void *context)
+{
+ switch (value) {
+ case 128:
+ ideapad_input_report(context, value);
+ break;
+ default:
+ pr_info("Unknown WMI event %u\n", value);
+ }
+}
+#endif
+
/*
* Some ideapads don't have a hardware rfkill switch, reading VPCCMD_R_RF
* always results in 0 on these models, causing ideapad_laptop to wrongly
@@ -845,6 +865,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
},
},
{
+ .ident = "Lenovo ideapad Y700-17ISK",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-17ISK"),
+ },
+ },
+ {
.ident = "Lenovo Yoga 2 11 / 13 / Pro",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
@@ -852,17 +879,38 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
},
},
{
- .ident = "Lenovo Yoga 3 14",
+ .ident = "Lenovo Yoga 2 11 / 13 / Pro",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_BOARD_NAME, "Yoga2"),
+ },
+ },
+ {
+ .ident = "Lenovo Yoga 3 1170 / 1470",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 3 14"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 3"),
},
},
{
.ident = "Lenovo Yoga 3 Pro 1370",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3 Pro-1370"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3"),
+ },
+ },
+ {
+ .ident = "Lenovo Yoga 700",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 700"),
+ },
+ },
+ {
+ .ident = "Lenovo Yoga 900",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 900"),
},
},
{}
@@ -918,7 +966,7 @@ static int ideapad_acpi_add(struct platform_device *pdev)
ideapad_sync_rfk_state(priv);
ideapad_sync_touchpad_state(priv);
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
ret = ideapad_backlight_init(priv);
if (ret && ret != -ENODEV)
goto backlight_failed;
@@ -927,8 +975,18 @@ static int ideapad_acpi_add(struct platform_device *pdev)
ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
if (ret)
goto notification_failed;
+#if IS_ENABLED(CONFIG_ACPI_WMI)
+ ret = wmi_install_notify_handler(ideapad_wmi_fnesc_event, ideapad_wmi_notify, priv);
+ if (ret != AE_OK && ret != AE_NOT_EXIST)
+ goto notification_failed_wmi;
+#endif
return 0;
+#if IS_ENABLED(CONFIG_ACPI_WMI)
+notification_failed_wmi:
+ acpi_remove_notify_handler(priv->adev->handle,
+ ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
+#endif
notification_failed:
ideapad_backlight_exit(priv);
backlight_failed:
@@ -947,6 +1005,9 @@ static int ideapad_acpi_remove(struct platform_device *pdev)
struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
int i;
+#if IS_ENABLED(CONFIG_ACPI_WMI)
+ wmi_remove_notify_handler(ideapad_wmi_fnesc_event);
+#endif
acpi_remove_notify_handler(priv->adev->handle,
ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
ideapad_backlight_exit(priv);
diff --git a/kernel/drivers/platform/x86/intel_ips.c b/kernel/drivers/platform/x86/intel_ips.c
index e2065e06a..55663b3d7 100644
--- a/kernel/drivers/platform/x86/intel_ips.c
+++ b/kernel/drivers/platform/x86/intel_ips.c
@@ -78,7 +78,7 @@
#include <asm/processor.h>
#include "intel_ips.h"
-#include <asm-generic/io-64-nonatomic-lo-hi.h>
+#include <linux/io-64-nonatomic-lo-hi.h>
#define PCI_DEVICE_ID_INTEL_THERMAL_SENSOR 0x3b32
diff --git a/kernel/drivers/platform/x86/intel_menlow.c b/kernel/drivers/platform/x86/intel_menlow.c
index e8b46d2c4..0a919d816 100644
--- a/kernel/drivers/platform/x86/intel_menlow.c
+++ b/kernel/drivers/platform/x86/intel_menlow.c
@@ -315,7 +315,7 @@ static ssize_t aux0_show(struct device *dev,
result = sensor_get_auxtrip(attr->handle, 0, &value);
- return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
+ return result ? result : sprintf(buf, "%lu", DECI_KELVIN_TO_CELSIUS(value));
}
static ssize_t aux1_show(struct device *dev,
@@ -327,7 +327,7 @@ static ssize_t aux1_show(struct device *dev,
result = sensor_get_auxtrip(attr->handle, 1, &value);
- return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
+ return result ? result : sprintf(buf, "%lu", DECI_KELVIN_TO_CELSIUS(value));
}
static ssize_t aux0_store(struct device *dev,
@@ -345,7 +345,7 @@ static ssize_t aux0_store(struct device *dev,
if (value < 0)
return -EINVAL;
- result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));
+ result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_DECI_KELVIN(value));
return result ? result : count;
}
@@ -364,7 +364,7 @@ static ssize_t aux1_store(struct device *dev,
if (value < 0)
return -EINVAL;
- result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));
+ result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_DECI_KELVIN(value));
return result ? result : count;
}
diff --git a/kernel/drivers/platform/x86/intel_mid_powerbtn.c b/kernel/drivers/platform/x86/intel_mid_powerbtn.c
index 22606d6b2..1fc0de870 100644
--- a/kernel/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/kernel/drivers/platform/x86/intel_mid_powerbtn.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/mfd/intel_msic.h>
+#include <linux/pm_wakeirq.h>
#define DRIVER_NAME "msic_power_btn"
@@ -76,14 +77,17 @@ static int mfld_pb_probe(struct platform_device *pdev)
input_set_capability(input, EV_KEY, KEY_POWER);
- error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_NO_SUSPEND,
- DRIVER_NAME, input);
+ error = request_threaded_irq(irq, NULL, mfld_pb_isr, 0,
+ DRIVER_NAME, input);
if (error) {
dev_err(&pdev->dev, "Unable to request irq %d for mfld power"
"button\n", irq);
goto err_free_input;
}
+ device_init_wakeup(&pdev->dev, true);
+ dev_pm_set_wake_irq(&pdev->dev, irq);
+
error = input_register_device(input);
if (error) {
dev_err(&pdev->dev, "Unable to register input dev, error "
@@ -124,6 +128,8 @@ static int mfld_pb_remove(struct platform_device *pdev)
struct input_dev *input = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
+ dev_pm_clear_wake_irq(&pdev->dev);
+ device_init_wakeup(&pdev->dev, false);
free_irq(irq, input);
input_unregister_device(input);
diff --git a/kernel/drivers/platform/x86/intel_mid_thermal.c b/kernel/drivers/platform/x86/intel_mid_thermal.c
index 0944e834a..9f713b832 100644
--- a/kernel/drivers/platform/x86/intel_mid_thermal.c
+++ b/kernel/drivers/platform/x86/intel_mid_thermal.c
@@ -132,7 +132,7 @@ static int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max)
* to achieve very close approximate temp value with less than
* 0.5C error
*/
-static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp)
+static int adc_to_temp(int direct, uint16_t adc_val, int *tp)
{
int temp;
@@ -174,14 +174,13 @@ static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp)
*
* Can sleep
*/
-static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp)
+static int mid_read_temp(struct thermal_zone_device *tzd, int *temp)
{
struct thermal_device_info *td_info = tzd->devdata;
uint16_t adc_val, addr;
uint8_t data = 0;
int ret;
- unsigned long curr_temp;
-
+ int curr_temp;
addr = td_info->chnl_addr;
@@ -453,7 +452,7 @@ static SIMPLE_DEV_PM_OPS(mid_thermal_pm,
*
* Can sleep
*/
-static int read_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp)
+static int read_curr_temp(struct thermal_zone_device *tzd, int *temp)
{
WARN_ON(tzd == NULL);
return mid_read_temp(tzd, temp);
diff --git a/kernel/drivers/platform/x86/intel_oaktrail.c b/kernel/drivers/platform/x86/intel_oaktrail.c
index 8037c8b46..6aa33c4a8 100644
--- a/kernel/drivers/platform/x86/intel_oaktrail.c
+++ b/kernel/drivers/platform/x86/intel_oaktrail.c
@@ -50,6 +50,7 @@
#include <linux/platform_device.h>
#include <linux/dmi.h>
#include <linux/rfkill.h>
+#include <acpi/video.h>
#define DRIVER_NAME "intel_oaktrail"
#define DRIVER_VERSION "0.4ac1"
@@ -343,13 +344,11 @@ static int __init oaktrail_init(void)
goto err_device_add;
}
- if (!acpi_video_backlight_support()) {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
ret = oaktrail_backlight_init();
if (ret)
goto err_backlight;
-
- } else
- pr_info("Backlight controlled by ACPI video driver\n");
+ }
ret = oaktrail_rfkill_init();
if (ret) {
diff --git a/kernel/drivers/platform/x86/intel_pmc_ipc.c b/kernel/drivers/platform/x86/intel_pmc_ipc.c
new file mode 100644
index 000000000..28b2a12bb
--- /dev/null
+++ b/kernel/drivers/platform/x86/intel_pmc_ipc.c
@@ -0,0 +1,779 @@
+/*
+ * intel_pmc_ipc.c: Driver for the Intel PMC IPC mechanism
+ *
+ * (C) Copyright 2014-2015 Intel Corporation
+ *
+ * This driver is based on Intel SCU IPC driver(intel_scu_opc.c) by
+ * Sreedhara DS <sreedhara.ds@intel.com>
+ *
+ * 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; version 2
+ * of the License.
+ *
+ * PMC running in ARC processor communicates with other entity running in IA
+ * core through IPC mechanism which in turn messaging between IA core ad PMC.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/pm.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_qos.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/sched.h>
+#include <linux/atomic.h>
+#include <linux/notifier.h>
+#include <linux/suspend.h>
+#include <linux/acpi.h>
+#include <asm/intel_pmc_ipc.h>
+#include <linux/platform_data/itco_wdt.h>
+
+/*
+ * IPC registers
+ * The IA write to IPC_CMD command register triggers an interrupt to the ARC,
+ * The ARC handles the interrupt and services it, writing optional data to
+ * the IPC1 registers, updates the IPC_STS response register with the status.
+ */
+#define IPC_CMD 0x0
+#define IPC_CMD_MSI 0x100
+#define IPC_CMD_SIZE 16
+#define IPC_CMD_SUBCMD 12
+#define IPC_STATUS 0x04
+#define IPC_STATUS_IRQ 0x4
+#define IPC_STATUS_ERR 0x2
+#define IPC_STATUS_BUSY 0x1
+#define IPC_SPTR 0x08
+#define IPC_DPTR 0x0C
+#define IPC_WRITE_BUFFER 0x80
+#define IPC_READ_BUFFER 0x90
+
+/*
+ * 16-byte buffer for sending data associated with IPC command.
+ */
+#define IPC_DATA_BUFFER_SIZE 16
+
+#define IPC_LOOP_CNT 3000000
+#define IPC_MAX_SEC 3
+
+#define IPC_TRIGGER_MODE_IRQ true
+
+/* exported resources from IFWI */
+#define PLAT_RESOURCE_IPC_INDEX 0
+#define PLAT_RESOURCE_IPC_SIZE 0x1000
+#define PLAT_RESOURCE_GCR_SIZE 0x1000
+#define PLAT_RESOURCE_PUNIT_DATA_INDEX 1
+#define PLAT_RESOURCE_PUNIT_INTER_INDEX 2
+#define PLAT_RESOURCE_ACPI_IO_INDEX 0
+
+/*
+ * BIOS does not create an ACPI device for each PMC function,
+ * but exports multiple resources from one ACPI device(IPC) for
+ * multiple functions. This driver is responsible to create a
+ * platform device and to export resources for those functions.
+ */
+#define TCO_DEVICE_NAME "iTCO_wdt"
+#define SMI_EN_OFFSET 0x30
+#define SMI_EN_SIZE 4
+#define TCO_BASE_OFFSET 0x60
+#define TCO_REGS_SIZE 16
+#define PUNIT_DEVICE_NAME "intel_punit_ipc"
+
+static const int iTCO_version = 3;
+
+static struct intel_pmc_ipc_dev {
+ struct device *dev;
+ void __iomem *ipc_base;
+ bool irq_mode;
+ int irq;
+ int cmd;
+ struct completion cmd_complete;
+
+ /* The following PMC BARs share the same ACPI device with the IPC */
+ resource_size_t acpi_io_base;
+ int acpi_io_size;
+ struct platform_device *tco_dev;
+
+ /* gcr */
+ resource_size_t gcr_base;
+ int gcr_size;
+
+ /* punit */
+ resource_size_t punit_base;
+ int punit_size;
+ resource_size_t punit_base2;
+ int punit_size2;
+ struct platform_device *punit_dev;
+} ipcdev;
+
+static char *ipc_err_sources[] = {
+ [IPC_ERR_NONE] =
+ "no error",
+ [IPC_ERR_CMD_NOT_SUPPORTED] =
+ "command not supported",
+ [IPC_ERR_CMD_NOT_SERVICED] =
+ "command not serviced",
+ [IPC_ERR_UNABLE_TO_SERVICE] =
+ "unable to service",
+ [IPC_ERR_CMD_INVALID] =
+ "command invalid",
+ [IPC_ERR_CMD_FAILED] =
+ "command failed",
+ [IPC_ERR_EMSECURITY] =
+ "Invalid Battery",
+ [IPC_ERR_UNSIGNEDKERNEL] =
+ "Unsigned kernel",
+};
+
+/* Prevent concurrent calls to the PMC */
+static DEFINE_MUTEX(ipclock);
+
+static inline void ipc_send_command(u32 cmd)
+{
+ ipcdev.cmd = cmd;
+ if (ipcdev.irq_mode) {
+ reinit_completion(&ipcdev.cmd_complete);
+ cmd |= IPC_CMD_MSI;
+ }
+ writel(cmd, ipcdev.ipc_base + IPC_CMD);
+}
+
+static inline u32 ipc_read_status(void)
+{
+ return readl(ipcdev.ipc_base + IPC_STATUS);
+}
+
+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)
+{
+ return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
+}
+
+static inline u32 ipc_data_readl(u32 offset)
+{
+ return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
+}
+
+static int intel_pmc_ipc_check_status(void)
+{
+ int status;
+ int ret = 0;
+
+ if (ipcdev.irq_mode) {
+ if (0 == wait_for_completion_timeout(
+ &ipcdev.cmd_complete, IPC_MAX_SEC * HZ))
+ ret = -ETIMEDOUT;
+ } else {
+ int loop_count = IPC_LOOP_CNT;
+
+ while ((ipc_read_status() & IPC_STATUS_BUSY) && --loop_count)
+ udelay(1);
+ if (loop_count == 0)
+ ret = -ETIMEDOUT;
+ }
+
+ status = ipc_read_status();
+ if (ret == -ETIMEDOUT) {
+ dev_err(ipcdev.dev,
+ "IPC timed out, TS=0x%x, CMD=0x%x\n",
+ status, ipcdev.cmd);
+ return ret;
+ }
+
+ if (status & IPC_STATUS_ERR) {
+ int i;
+
+ ret = -EIO;
+ i = (status >> IPC_CMD_SIZE) & 0xFF;
+ if (i < ARRAY_SIZE(ipc_err_sources))
+ dev_err(ipcdev.dev,
+ "IPC failed: %s, STS=0x%x, CMD=0x%x\n",
+ ipc_err_sources[i], status, ipcdev.cmd);
+ else
+ dev_err(ipcdev.dev,
+ "IPC failed: unknown, STS=0x%x, CMD=0x%x\n",
+ status, ipcdev.cmd);
+ if ((i == IPC_ERR_UNSIGNEDKERNEL) || (i == IPC_ERR_EMSECURITY))
+ ret = -EACCES;
+ }
+
+ return ret;
+}
+
+/**
+ * intel_pmc_ipc_simple_command() - Simple IPC command
+ * @cmd: IPC command code.
+ * @sub: IPC command sub type.
+ *
+ * Send a simple IPC command to PMC when don't need to specify
+ * input/output data and source/dest pointers.
+ *
+ * Return: an IPC error code or 0 on success.
+ */
+int intel_pmc_ipc_simple_command(int cmd, int sub)
+{
+ int ret;
+
+ mutex_lock(&ipclock);
+ if (ipcdev.dev == NULL) {
+ mutex_unlock(&ipclock);
+ return -ENODEV;
+ }
+ ipc_send_command(sub << IPC_CMD_SUBCMD | cmd);
+ ret = intel_pmc_ipc_check_status();
+ mutex_unlock(&ipclock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_ipc_simple_command);
+
+/**
+ * intel_pmc_ipc_raw_cmd() - IPC command with data and pointers
+ * @cmd: IPC command code.
+ * @sub: IPC command sub type.
+ * @in: input data of this IPC command.
+ * @inlen: input data length in bytes.
+ * @out: output data of this IPC command.
+ * @outlen: output data length in dwords.
+ * @sptr: data writing to SPTR register.
+ * @dptr: data writing to DPTR register.
+ *
+ * Send an IPC command to PMC with input/output data and source/dest pointers.
+ *
+ * Return: an IPC error code or 0 on success.
+ */
+int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, u32 *out,
+ u32 outlen, u32 dptr, u32 sptr)
+{
+ u32 wbuf[4] = { 0 };
+ int ret;
+ int i;
+
+ if (inlen > IPC_DATA_BUFFER_SIZE || outlen > IPC_DATA_BUFFER_SIZE / 4)
+ return -EINVAL;
+
+ mutex_lock(&ipclock);
+ if (ipcdev.dev == NULL) {
+ mutex_unlock(&ipclock);
+ return -ENODEV;
+ }
+ memcpy(wbuf, in, inlen);
+ writel(dptr, ipcdev.ipc_base + IPC_DPTR);
+ writel(sptr, ipcdev.ipc_base + IPC_SPTR);
+ /* The input data register is 32bit register and inlen is in Byte */
+ for (i = 0; i < ((inlen + 3) / 4); i++)
+ ipc_data_writel(wbuf[i], 4 * i);
+ ipc_send_command((inlen << IPC_CMD_SIZE) |
+ (sub << IPC_CMD_SUBCMD) | cmd);
+ ret = intel_pmc_ipc_check_status();
+ if (!ret) {
+ /* out is read from 32bit register and outlen is in 32bit */
+ for (i = 0; i < outlen; i++)
+ *out++ = ipc_data_readl(4 * i);
+ }
+ mutex_unlock(&ipclock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(intel_pmc_ipc_raw_cmd);
+
+/**
+ * intel_pmc_ipc_command() - IPC command with input/output data
+ * @cmd: IPC command code.
+ * @sub: IPC command sub type.
+ * @in: input data of this IPC command.
+ * @inlen: input data length in bytes.
+ * @out: output data of this IPC command.
+ * @outlen: output data length in dwords.
+ *
+ * Send an IPC command to PMC with input/output data.
+ *
+ * Return: an IPC error code or 0 on success.
+ */
+int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
+ u32 *out, u32 outlen)
+{
+ return intel_pmc_ipc_raw_cmd(cmd, sub, in, inlen, out, outlen, 0, 0);
+}
+EXPORT_SYMBOL_GPL(intel_pmc_ipc_command);
+
+static irqreturn_t ioc(int irq, void *dev_id)
+{
+ int status;
+
+ if (ipcdev.irq_mode) {
+ status = ipc_read_status();
+ writel(status | IPC_STATUS_IRQ, ipcdev.ipc_base + IPC_STATUS);
+ }
+ complete(&ipcdev.cmd_complete);
+
+ return IRQ_HANDLED;
+}
+
+static int ipc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ resource_size_t pci_resource;
+ int ret;
+ int len;
+
+ ipcdev.dev = &pci_dev_get(pdev)->dev;
+ ipcdev.irq_mode = IPC_TRIGGER_MODE_IRQ;
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ ret = pci_request_regions(pdev, "intel_pmc_ipc");
+ 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(&ipcdev.cmd_complete);
+
+ if (request_irq(pdev->irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
+ dev_err(&pdev->dev, "Failed to request irq\n");
+ return -EBUSY;
+ }
+
+ 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;
+ }
+
+ return ret;
+}
+
+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;
+}
+
+static const struct pci_device_id ipc_pci_ids[] = {
+ {PCI_VDEVICE(INTEL, 0x0a94), 0},
+ {PCI_VDEVICE(INTEL, 0x1a94), 0},
+ { 0,}
+};
+MODULE_DEVICE_TABLE(pci, ipc_pci_ids);
+
+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,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int subcmd;
+ int cmd;
+ int ret;
+
+ ret = sscanf(buf, "%d %d", &cmd, &subcmd);
+ if (ret != 2) {
+ dev_err(dev, "Error args\n");
+ return -EINVAL;
+ }
+
+ ret = intel_pmc_ipc_simple_command(cmd, subcmd);
+ if (ret) {
+ dev_err(dev, "command %d error with %d\n", cmd, ret);
+ return ret;
+ }
+ return (ssize_t)count;
+}
+
+static ssize_t intel_pmc_ipc_northpeak_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ int subcmd;
+ int ret;
+
+ if (kstrtoul(buf, 0, &val))
+ return -EINVAL;
+
+ if (val)
+ subcmd = 1;
+ else
+ subcmd = 0;
+ ret = intel_pmc_ipc_simple_command(PMC_IPC_NORTHPEAK_CTRL, subcmd);
+ if (ret) {
+ dev_err(dev, "command north %d error with %d\n", subcmd, ret);
+ return ret;
+ }
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR(simplecmd, S_IWUSR,
+ NULL, intel_pmc_ipc_simple_cmd_store);
+static DEVICE_ATTR(northpeak, S_IWUSR,
+ NULL, intel_pmc_ipc_northpeak_store);
+
+static struct attribute *intel_ipc_attrs[] = {
+ &dev_attr_northpeak.attr,
+ &dev_attr_simplecmd.attr,
+ NULL
+};
+
+static const struct attribute_group intel_ipc_group = {
+ .attrs = intel_ipc_attrs,
+};
+
+#define PUNIT_RESOURCE_INTER 1
+static struct resource punit_res[] = {
+ /* Punit */
+ {
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+#define TCO_RESOURCE_ACPI_IO 0
+#define TCO_RESOURCE_SMI_EN_IO 1
+#define TCO_RESOURCE_GCR_MEM 2
+static struct resource tco_res[] = {
+ /* ACPI - TCO */
+ {
+ .flags = IORESOURCE_IO,
+ },
+ /* ACPI - SMI */
+ {
+ .flags = IORESOURCE_IO,
+ },
+ /* GCS */
+ {
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct itco_wdt_platform_data tco_info = {
+ .name = "Apollo Lake SoC",
+ .version = 3,
+};
+
+static int ipc_create_punit_device(void)
+{
+ struct platform_device *pdev;
+ struct resource *res;
+ int ret;
+
+ pdev = platform_device_alloc(PUNIT_DEVICE_NAME, -1);
+ if (!pdev) {
+ dev_err(ipcdev.dev, "Failed to alloc punit platform device\n");
+ return -ENOMEM;
+ }
+
+ pdev->dev.parent = ipcdev.dev;
+
+ res = punit_res;
+ res->start = ipcdev.punit_base;
+ res->end = res->start + ipcdev.punit_size - 1;
+
+ res = punit_res + PUNIT_RESOURCE_INTER;
+ res->start = ipcdev.punit_base2;
+ res->end = res->start + ipcdev.punit_size2 - 1;
+
+ ret = platform_device_add_resources(pdev, punit_res,
+ ARRAY_SIZE(punit_res));
+ if (ret) {
+ dev_err(ipcdev.dev, "Failed to add platform punit resources\n");
+ goto err;
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ dev_err(ipcdev.dev, "Failed to add punit platform device\n");
+ goto err;
+ }
+ ipcdev.punit_dev = pdev;
+
+ return 0;
+err:
+ platform_device_put(pdev);
+ return ret;
+}
+
+static int ipc_create_tco_device(void)
+{
+ struct platform_device *pdev;
+ struct resource *res;
+ int ret;
+
+ pdev = platform_device_alloc(TCO_DEVICE_NAME, -1);
+ if (!pdev) {
+ dev_err(ipcdev.dev, "Failed to alloc tco platform device\n");
+ return -ENOMEM;
+ }
+
+ pdev->dev.parent = ipcdev.dev;
+
+ res = tco_res + TCO_RESOURCE_ACPI_IO;
+ res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET;
+ res->end = res->start + TCO_REGS_SIZE - 1;
+
+ res = tco_res + TCO_RESOURCE_SMI_EN_IO;
+ res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET;
+ res->end = res->start + SMI_EN_SIZE - 1;
+
+ res = tco_res + TCO_RESOURCE_GCR_MEM;
+ res->start = ipcdev.gcr_base;
+ res->end = res->start + ipcdev.gcr_size - 1;
+
+ ret = platform_device_add_resources(pdev, tco_res, ARRAY_SIZE(tco_res));
+ if (ret) {
+ dev_err(ipcdev.dev, "Failed to add tco platform resources\n");
+ goto err;
+ }
+
+ ret = platform_device_add_data(pdev, &tco_info, sizeof(tco_info));
+ if (ret) {
+ dev_err(ipcdev.dev, "Failed to add tco platform data\n");
+ goto err;
+ }
+
+ ret = platform_device_add(pdev);
+ if (ret) {
+ dev_err(ipcdev.dev, "Failed to add tco platform device\n");
+ goto err;
+ }
+ ipcdev.tco_dev = pdev;
+
+ return 0;
+err:
+ platform_device_put(pdev);
+ return ret;
+}
+
+static int ipc_create_pmc_devices(void)
+{
+ int ret;
+
+ ret = ipc_create_tco_device();
+ if (ret) {
+ dev_err(ipcdev.dev, "Failed to add tco platform device\n");
+ return ret;
+ }
+ ret = ipc_create_punit_device();
+ if (ret) {
+ dev_err(ipcdev.dev, "Failed to add punit platform device\n");
+ platform_device_unregister(ipcdev.tco_dev);
+ }
+ return ret;
+}
+
+static int ipc_plat_get_res(struct platform_device *pdev)
+{
+ struct resource *res;
+ void __iomem *addr;
+ int size;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO,
+ PLAT_RESOURCE_ACPI_IO_INDEX);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get io resource\n");
+ return -ENXIO;
+ }
+ size = resource_size(res);
+ ipcdev.acpi_io_base = res->start;
+ ipcdev.acpi_io_size = size;
+ dev_info(&pdev->dev, "io res: %llx %x\n",
+ (long long)res->start, (int)resource_size(res));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM,
+ PLAT_RESOURCE_PUNIT_DATA_INDEX);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get punit resource\n");
+ return -ENXIO;
+ }
+ size = resource_size(res);
+ ipcdev.punit_base = res->start;
+ ipcdev.punit_size = size;
+ dev_info(&pdev->dev, "punit data res: %llx %x\n",
+ (long long)res->start, (int)resource_size(res));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM,
+ PLAT_RESOURCE_PUNIT_INTER_INDEX);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get punit inter resource\n");
+ return -ENXIO;
+ }
+ size = resource_size(res);
+ ipcdev.punit_base2 = res->start;
+ ipcdev.punit_size2 = size;
+ dev_info(&pdev->dev, "punit interface res: %llx %x\n",
+ (long long)res->start, (int)resource_size(res));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM,
+ PLAT_RESOURCE_IPC_INDEX);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get ipc resource\n");
+ return -ENXIO;
+ }
+ size = PLAT_RESOURCE_IPC_SIZE;
+ 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_base = res->start + size;
+ ipcdev.gcr_size = PLAT_RESOURCE_GCR_SIZE;
+ dev_info(&pdev->dev, "ipc res: %llx %x\n",
+ (long long)res->start, (int)resource_size(res));
+
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id ipc_acpi_ids[] = {
+ { "INT34D2", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, ipc_acpi_ids);
+#endif
+
+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);
+
+ ipcdev.irq = platform_get_irq(pdev, 0);
+ if (ipcdev.irq < 0) {
+ dev_err(&pdev->dev, "Failed to get irq\n");
+ return -EINVAL;
+ }
+
+ ret = ipc_plat_get_res(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request resource\n");
+ return ret;
+ }
+
+ ret = ipc_create_pmc_devices();
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create pmc devices\n");
+ goto err_device;
+ }
+
+ if (request_irq(ipcdev.irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
+ dev_err(&pdev->dev, "Failed to request irq\n");
+ ret = -EBUSY;
+ goto err_irq;
+ }
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &intel_ipc_group);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to create sysfs group %d\n",
+ ret);
+ goto err_sys;
+ }
+
+ return 0;
+err_sys:
+ free_irq(ipcdev.irq, &ipcdev);
+err_irq:
+ platform_device_unregister(ipcdev.tco_dev);
+ platform_device_unregister(ipcdev.punit_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);
+ 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);
+ platform_device_unregister(ipcdev.tco_dev);
+ platform_device_unregister(ipcdev.punit_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);
+ ipcdev.dev = NULL;
+ return 0;
+}
+
+static struct platform_driver ipc_plat_driver = {
+ .remove = ipc_plat_remove,
+ .probe = ipc_plat_probe,
+ .driver = {
+ .name = "pmc-ipc-plat",
+ .acpi_match_table = ACPI_PTR(ipc_acpi_ids),
+ },
+};
+
+static int __init intel_pmc_ipc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&ipc_plat_driver);
+ if (ret) {
+ pr_err("Failed to register PMC ipc platform driver\n");
+ return ret;
+ }
+ ret = pci_register_driver(&ipc_pci_driver);
+ if (ret) {
+ pr_err("Failed to register PMC ipc pci driver\n");
+ platform_driver_unregister(&ipc_plat_driver);
+ return ret;
+ }
+ return ret;
+}
+
+static void __exit intel_pmc_ipc_exit(void)
+{
+ pci_unregister_driver(&ipc_pci_driver);
+ platform_driver_unregister(&ipc_plat_driver);
+}
+
+MODULE_AUTHOR("Zha Qipeng <qipeng.zha@intel.com>");
+MODULE_DESCRIPTION("Intel PMC IPC driver");
+MODULE_LICENSE("GPL");
+
+/* Some modules are dependent on this, so init earlier */
+fs_initcall(intel_pmc_ipc_init);
+module_exit(intel_pmc_ipc_exit);
diff --git a/kernel/drivers/platform/x86/intel_scu_ipc.c b/kernel/drivers/platform/x86/intel_scu_ipc.c
index 001b199a8..f94b73054 100644
--- a/kernel/drivers/platform/x86/intel_scu_ipc.c
+++ b/kernel/drivers/platform/x86/intel_scu_ipc.c
@@ -92,11 +92,8 @@ static struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = {
.irq_mode = 0,
};
-static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id);
-static void ipc_remove(struct pci_dev *pdev);
-
struct intel_scu_ipc_dev {
- struct pci_dev *pdev;
+ struct device *dev;
void __iomem *ipc_base;
void __iomem *i2c_base;
struct completion cmd_complete;
@@ -118,28 +115,30 @@ static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
/*
+ * Send ipc command
* Command Register (Write Only):
* A write to this register results in an interrupt to the SCU core processor
* Format:
* |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)|
*/
-static inline void ipc_command(u32 cmd) /* Send ipc command */
+static inline void ipc_command(struct intel_scu_ipc_dev *scu, u32 cmd)
{
- if (ipcdev.irq_mode) {
- reinit_completion(&ipcdev.cmd_complete);
- writel(cmd | IPC_IOC, ipcdev.ipc_base);
+ if (scu->irq_mode) {
+ reinit_completion(&scu->cmd_complete);
+ writel(cmd | IPC_IOC, scu->ipc_base);
}
- writel(cmd, ipcdev.ipc_base);
+ writel(cmd, scu->ipc_base);
}
/*
+ * Write ipc data
* IPC Write Buffer (Write Only):
* 16-byte buffer for sending data associated with IPC command to
* SCU. Size of the data is specified in the IPC_COMMAND_REG register
*/
-static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */
+static inline void ipc_data_writel(struct intel_scu_ipc_dev *scu, u32 data, u32 offset)
{
- writel(data, ipcdev.ipc_base + 0x80 + offset);
+ writel(data, scu->ipc_base + 0x80 + offset);
}
/*
@@ -149,35 +148,37 @@ static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */
* Format:
* |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)|
*/
-static inline u8 ipc_read_status(void)
+static inline u8 ipc_read_status(struct intel_scu_ipc_dev *scu)
{
- return __raw_readl(ipcdev.ipc_base + 0x04);
+ return __raw_readl(scu->ipc_base + 0x04);
}
-static inline u8 ipc_data_readb(u32 offset) /* Read ipc byte data */
+/* Read ipc byte data */
+static inline u8 ipc_data_readb(struct intel_scu_ipc_dev *scu, u32 offset)
{
- return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
+ return readb(scu->ipc_base + IPC_READ_BUFFER + offset);
}
-static inline u32 ipc_data_readl(u32 offset) /* Read ipc u32 data */
+/* Read ipc u32 data */
+static inline u32 ipc_data_readl(struct intel_scu_ipc_dev *scu, u32 offset)
{
- return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
+ return readl(scu->ipc_base + IPC_READ_BUFFER + offset);
}
/* Wait till scu status is busy */
-static inline int busy_loop(void)
+static inline int busy_loop(struct intel_scu_ipc_dev *scu)
{
- u32 status = ipc_read_status();
+ u32 status = ipc_read_status(scu);
u32 loop_count = 100000;
/* break if scu doesn't reset busy bit after huge retry */
while ((status & BIT(0)) && --loop_count) {
udelay(1); /* scu processing time is in few u secods */
- status = ipc_read_status();
+ status = ipc_read_status(scu);
}
if (status & BIT(0)) {
- dev_err(&ipcdev.pdev->dev, "IPC timed out");
+ dev_err(scu->dev, "IPC timed out");
return -ETIMEDOUT;
}
@@ -188,42 +189,42 @@ static inline int busy_loop(void)
}
/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */
-static inline int ipc_wait_for_interrupt(void)
+static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
{
int status;
- if (!wait_for_completion_timeout(&ipcdev.cmd_complete, 3 * HZ)) {
- struct device *dev = &ipcdev.pdev->dev;
- dev_err(dev, "IPC timed out\n");
+ if (!wait_for_completion_timeout(&scu->cmd_complete, 3 * HZ)) {
+ dev_err(scu->dev, "IPC timed out\n");
return -ETIMEDOUT;
}
- status = ipc_read_status();
+ status = ipc_read_status(scu);
if (status & BIT(1))
return -EIO;
return 0;
}
-static int intel_scu_ipc_check_status(void)
+static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
{
- return ipcdev.irq_mode ? ipc_wait_for_interrupt() : busy_loop();
+ return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
}
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
{
+ struct intel_scu_ipc_dev *scu = &ipcdev;
int nc;
u32 offset = 0;
int err;
- u8 cbuf[IPC_WWBUF_SIZE] = { };
+ u8 cbuf[IPC_WWBUF_SIZE];
u32 *wbuf = (u32 *)&cbuf;
- mutex_lock(&ipclock);
-
memset(cbuf, 0, sizeof(cbuf));
- if (ipcdev.pdev == NULL) {
+ mutex_lock(&ipclock);
+
+ if (scu->dev == NULL) {
mutex_unlock(&ipclock);
return -ENODEV;
}
@@ -235,27 +236,27 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
if (id == IPC_CMD_PCNTRL_R) {
for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
- ipc_data_writel(wbuf[nc], offset);
- ipc_command((count * 2) << 16 | id << 12 | 0 << 8 | op);
+ ipc_data_writel(scu, wbuf[nc], offset);
+ ipc_command(scu, (count * 2) << 16 | id << 12 | 0 << 8 | op);
} else if (id == IPC_CMD_PCNTRL_W) {
for (nc = 0; nc < count; nc++, offset += 1)
cbuf[offset] = data[nc];
for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
- ipc_data_writel(wbuf[nc], offset);
- ipc_command((count * 3) << 16 | id << 12 | 0 << 8 | op);
+ ipc_data_writel(scu, wbuf[nc], offset);
+ ipc_command(scu, (count * 3) << 16 | id << 12 | 0 << 8 | op);
} else if (id == IPC_CMD_PCNTRL_M) {
cbuf[offset] = data[0];
cbuf[offset + 1] = data[1];
- ipc_data_writel(wbuf[0], 0); /* Write wbuff */
- ipc_command(4 << 16 | id << 12 | 0 << 8 | op);
+ ipc_data_writel(scu, wbuf[0], 0); /* Write wbuff */
+ ipc_command(scu, 4 << 16 | id << 12 | 0 << 8 | op);
}
- err = intel_scu_ipc_check_status();
+ err = intel_scu_ipc_check_status(scu);
if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
/* Workaround: values are read as 0 without memcpy_fromio */
- memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16);
+ memcpy_fromio(cbuf, scu->ipc_base + 0x90, 16);
for (nc = 0; nc < count; nc++)
- data[nc] = ipc_data_readb(nc);
+ data[nc] = ipc_data_readb(scu, nc);
}
mutex_unlock(&ipclock);
return err;
@@ -436,15 +437,16 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
*/
int intel_scu_ipc_simple_command(int cmd, int sub)
{
+ struct intel_scu_ipc_dev *scu = &ipcdev;
int err;
mutex_lock(&ipclock);
- if (ipcdev.pdev == NULL) {
+ if (scu->dev == NULL) {
mutex_unlock(&ipclock);
return -ENODEV;
}
- ipc_command(sub << 12 | cmd);
- err = intel_scu_ipc_check_status();
+ ipc_command(scu, sub << 12 | cmd);
+ err = intel_scu_ipc_check_status(scu);
mutex_unlock(&ipclock);
return err;
}
@@ -465,23 +467,24 @@ EXPORT_SYMBOL(intel_scu_ipc_simple_command);
int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
u32 *out, int outlen)
{
+ struct intel_scu_ipc_dev *scu = &ipcdev;
int i, err;
mutex_lock(&ipclock);
- if (ipcdev.pdev == NULL) {
+ if (scu->dev == NULL) {
mutex_unlock(&ipclock);
return -ENODEV;
}
for (i = 0; i < inlen; i++)
- ipc_data_writel(*in++, 4 * i);
+ ipc_data_writel(scu, *in++, 4 * i);
- ipc_command((inlen << 16) | (sub << 12) | cmd);
- err = intel_scu_ipc_check_status();
+ ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
+ err = intel_scu_ipc_check_status(scu);
if (!err) {
for (i = 0; i < outlen; i++)
- *out++ = ipc_data_readl(4 * i);
+ *out++ = ipc_data_readl(scu, 4 * i);
}
mutex_unlock(&ipclock);
@@ -507,25 +510,26 @@ EXPORT_SYMBOL(intel_scu_ipc_command);
*/
int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)
{
+ struct intel_scu_ipc_dev *scu = &ipcdev;
u32 cmd = 0;
mutex_lock(&ipclock);
- if (ipcdev.pdev == NULL) {
+ if (scu->dev == NULL) {
mutex_unlock(&ipclock);
return -ENODEV;
}
cmd = (addr >> 24) & 0xFF;
if (cmd == IPC_I2C_READ) {
- writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR);
+ writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
/* Write not getting updated without delay */
mdelay(1);
- *data = readl(ipcdev.i2c_base + I2C_DATA_ADDR);
+ *data = readl(scu->i2c_base + I2C_DATA_ADDR);
} else if (cmd == IPC_I2C_WRITE) {
- writel(*data, ipcdev.i2c_base + I2C_DATA_ADDR);
+ writel(*data, scu->i2c_base + I2C_DATA_ADDR);
mdelay(1);
- writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR);
+ writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
} else {
- dev_err(&ipcdev.pdev->dev,
+ dev_err(scu->dev,
"intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd);
mutex_unlock(&ipclock);
@@ -545,63 +549,65 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
*/
static irqreturn_t ioc(int irq, void *dev_id)
{
- if (ipcdev.irq_mode)
- complete(&ipcdev.cmd_complete);
+ struct intel_scu_ipc_dev *scu = dev_id;
+
+ if (scu->irq_mode)
+ complete(&scu->cmd_complete);
return IRQ_HANDLED;
}
/**
* ipc_probe - probe an Intel SCU IPC
- * @dev: the PCI device matching
+ * @pdev: the PCI device matching
* @id: entry in the match table
*
* Enable and install an intel SCU IPC. This appears in the PCI space
* but uses some hard coded addresses as well.
*/
-static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
+static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
+ int platform; /* Platform type */
int err;
+ struct intel_scu_ipc_dev *scu = &ipcdev;
struct intel_scu_ipc_pdata_t *pdata;
- resource_size_t base;
- if (ipcdev.pdev) /* We support only one SCU */
+ platform = intel_mid_identify_cpu();
+ if (platform == 0)
+ return -ENODEV;
+
+ if (scu->dev) /* We support only one SCU */
return -EBUSY;
pdata = (struct intel_scu_ipc_pdata_t *)id->driver_data;
- ipcdev.pdev = pci_dev_get(dev);
- ipcdev.irq_mode = pdata->irq_mode;
+ scu->dev = &pdev->dev;
+ scu->irq_mode = pdata->irq_mode;
- err = pci_enable_device(dev);
+ err = pcim_enable_device(pdev);
if (err)
return err;
- err = pci_request_regions(dev, "intel_scu_ipc");
+ err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
if (err)
return err;
- base = pci_resource_start(dev, 0);
- if (!base)
- return -ENOMEM;
+ init_completion(&scu->cmd_complete);
- init_completion(&ipcdev.cmd_complete);
+ err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
+ scu);
+ if (err)
+ return err;
- if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev))
- return -EBUSY;
+ scu->ipc_base = pcim_iomap_table(pdev)[0];
- ipcdev.ipc_base = ioremap_nocache(base, pci_resource_len(dev, 0));
- if (!ipcdev.ipc_base)
+ scu->i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
+ if (!scu->i2c_base)
return -ENOMEM;
- ipcdev.i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
- if (!ipcdev.i2c_base) {
- iounmap(ipcdev.ipc_base);
- return -ENOMEM;
- }
-
intel_scu_devices_create();
+ pci_set_drvdata(pdev, scu);
return 0;
}
@@ -617,12 +623,13 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
*/
static void ipc_remove(struct pci_dev *pdev)
{
- free_irq(pdev->irq, &ipcdev);
- pci_release_regions(pdev);
- pci_dev_put(ipcdev.pdev);
- iounmap(ipcdev.ipc_base);
- iounmap(ipcdev.i2c_base);
- ipcdev.pdev = NULL;
+ struct intel_scu_ipc_dev *scu = pci_get_drvdata(pdev);
+
+ mutex_lock(&ipclock);
+ scu->dev = NULL;
+ mutex_unlock(&ipclock);
+
+ iounmap(scu->i2c_base);
intel_scu_devices_destroy();
}
@@ -652,24 +659,8 @@ static struct pci_driver ipc_driver = {
.remove = ipc_remove,
};
-static int __init intel_scu_ipc_init(void)
-{
- int platform; /* Platform type */
-
- platform = intel_mid_identify_cpu();
- if (platform == 0)
- return -ENODEV;
- return pci_register_driver(&ipc_driver);
-}
-
-static void __exit intel_scu_ipc_exit(void)
-{
- pci_unregister_driver(&ipc_driver);
-}
+module_pci_driver(ipc_driver);
MODULE_AUTHOR("Sreedhara DS <sreedhara.ds@intel.com>");
MODULE_DESCRIPTION("Intel SCU IPC driver");
MODULE_LICENSE("GPL");
-
-module_init(intel_scu_ipc_init);
-module_exit(intel_scu_ipc_exit);
diff --git a/kernel/drivers/platform/x86/intel_scu_ipcutil.c b/kernel/drivers/platform/x86/intel_scu_ipcutil.c
index 02bc5a634..aa4542414 100644
--- a/kernel/drivers/platform/x86/intel_scu_ipcutil.c
+++ b/kernel/drivers/platform/x86/intel_scu_ipcutil.c
@@ -49,7 +49,7 @@ struct scu_ipc_data {
static int scu_reg_access(u32 cmd, struct scu_ipc_data *data)
{
- int count = data->count;
+ unsigned int count = data->count;
if (count == 0 || count == 3 || count > 4)
return -EINVAL;
diff --git a/kernel/drivers/platform/x86/msi-laptop.c b/kernel/drivers/platform/x86/msi-laptop.c
index 085987730..423177046 100644
--- a/kernel/drivers/platform/x86/msi-laptop.c
+++ b/kernel/drivers/platform/x86/msi-laptop.c
@@ -64,6 +64,7 @@
#include <linux/i8042.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <acpi/video.h>
#define MSI_DRIVER_VERSION "0.5"
@@ -1069,9 +1070,8 @@ static int __init msi_init(void)
/* Register backlight stuff */
- if (!quirks->old_ec_model || acpi_video_backlight_support()) {
- pr_info("Brightness ignored, must be controlled by ACPI video driver\n");
- } else {
+ if (quirks->old_ec_model ||
+ acpi_video_get_backlight_type() == acpi_backlight_vendor) {
struct backlight_properties props;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
diff --git a/kernel/drivers/platform/x86/msi-wmi.c b/kernel/drivers/platform/x86/msi-wmi.c
index 6d2bac0c4..978e6d640 100644
--- a/kernel/drivers/platform/x86/msi-wmi.c
+++ b/kernel/drivers/platform/x86/msi-wmi.c
@@ -29,6 +29,7 @@
#include <linux/backlight.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <acpi/video.h>
MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver");
@@ -320,7 +321,8 @@ static int __init msi_wmi_init(void)
break;
}
- if (wmi_has_guid(MSIWMI_BIOS_GUID) && !acpi_video_backlight_support()) {
+ if (wmi_has_guid(MSIWMI_BIOS_GUID) &&
+ acpi_video_get_backlight_type() == acpi_backlight_vendor) {
err = msi_wmi_backlight_setup();
if (err) {
pr_err("Unable to setup backlight device\n");
diff --git a/kernel/drivers/platform/x86/pvpanic.c b/kernel/drivers/platform/x86/pvpanic.c
index 073a90a63..fd86daba7 100644
--- a/kernel/drivers/platform/x86/pvpanic.c
+++ b/kernel/drivers/platform/x86/pvpanic.c
@@ -92,13 +92,13 @@ pvpanic_walk_resources(struct acpi_resource *res, void *context)
static int pvpanic_add(struct acpi_device *device)
{
- acpi_status status;
- u64 ret;
+ int ret;
- status = acpi_evaluate_integer(device->handle, "_STA", NULL,
- &ret);
+ ret = acpi_bus_get_status(device);
+ if (ret < 0)
+ return ret;
- if (ACPI_FAILURE(status) || (ret & 0x0B) != 0x0B)
+ if (!device->status.enabled || !device->status.functional)
return -ENODEV;
acpi_walk_resources(device->handle, METHOD_NAME__CRS,
diff --git a/kernel/drivers/platform/x86/samsung-laptop.c b/kernel/drivers/platform/x86/samsung-laptop.c
index 9e701b225..8c146e2b6 100644
--- a/kernel/drivers/platform/x86/samsung-laptop.c
+++ b/kernel/drivers/platform/x86/samsung-laptop.c
@@ -1720,27 +1720,14 @@ static int __init samsung_init(void)
samsung->handle_backlight = true;
samsung->quirks = quirks;
-
#ifdef CONFIG_ACPI
if (samsung->quirks->broken_acpi_video)
- acpi_video_dmi_promote_vendor();
-
- /* Don't handle backlight here if the acpi video already handle it */
- if (acpi_video_backlight_support()) {
- samsung->handle_backlight = false;
- } else if (samsung->quirks->broken_acpi_video) {
- pr_info("Disabling ACPI video driver\n");
- acpi_video_unregister();
- }
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
+ if (samsung->quirks->use_native_backlight)
+ acpi_video_set_dmi_backlight_type(acpi_backlight_native);
- if (samsung->quirks->use_native_backlight) {
- pr_info("Using native backlight driver\n");
- /* Tell acpi-video to not handle the backlight */
- acpi_video_dmi_promote_vendor();
- acpi_video_unregister();
- /* And also do not handle it ourselves */
+ if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
samsung->handle_backlight = false;
- }
#endif
ret = samsung_platform_init(samsung);
@@ -1751,12 +1738,6 @@ static int __init samsung_init(void)
if (ret)
goto error_sabi;
-#ifdef CONFIG_ACPI
- /* Only log that if we are really on a sabi platform */
- if (acpi_video_backlight_support())
- pr_info("Backlight controlled by ACPI video driver\n");
-#endif
-
ret = samsung_sysfs_init(samsung);
if (ret)
goto error_sysfs;
diff --git a/kernel/drivers/platform/x86/sony-laptop.c b/kernel/drivers/platform/x86/sony-laptop.c
index e51c1e753..f73c29558 100644
--- a/kernel/drivers/platform/x86/sony-laptop.c
+++ b/kernel/drivers/platform/x86/sony-laptop.c
@@ -69,6 +69,7 @@
#include <linux/miscdevice.h>
#endif
#include <asm/uaccess.h>
+#include <acpi/video.h>
#define dprintk(fmt, ...) \
do { \
@@ -1203,6 +1204,8 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
{
u32 real_ev = event;
u8 ev_type = 0;
+ int ret;
+
dprintk("sony_nc_notify, event: 0x%.2x\n", event);
if (event >= 0x90) {
@@ -1224,13 +1227,12 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
case 0x0100:
case 0x0127:
ev_type = HOTKEY;
- real_ev = sony_nc_hotkeys_decode(event, handle);
+ ret = sony_nc_hotkeys_decode(event, handle);
- if (real_ev > 0)
- sony_laptop_report_input_event(real_ev);
- else
- /* restore the original event for reporting */
- real_ev = event;
+ if (ret > 0) {
+ sony_laptop_report_input_event(ret);
+ real_ev = ret;
+ }
break;
@@ -3198,12 +3200,8 @@ static int sony_nc_add(struct acpi_device *device)
sony_nc_function_setup(device, sony_pf_device);
}
- /* setup input devices and helper fifo */
- if (acpi_video_backlight_support()) {
- pr_info("brightness ignored, must be controlled by ACPI video driver\n");
- } else {
+ if (acpi_video_get_backlight_type() == acpi_backlight_vendor)
sony_nc_backlight_setup();
- }
/* create sony_pf sysfs attributes related to the SNC device */
for (item = sony_nc_values; item->name; ++item) {
diff --git a/kernel/drivers/platform/x86/surfacepro3_button.c b/kernel/drivers/platform/x86/surfacepro3_button.c
new file mode 100644
index 000000000..f7dade3fd
--- /dev/null
+++ b/kernel/drivers/platform/x86/surfacepro3_button.c
@@ -0,0 +1,216 @@
+/*
+ * power/home/volume button support for
+ * Microsoft Surface Pro 3 tablet.
+ *
+ * Copyright (c) 2015 Intel Corporation.
+ * 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
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/acpi.h>
+#include <acpi/button.h>
+
+#define SURFACE_BUTTON_HID "MSHW0028"
+#define SURFACE_BUTTON_OBJ_NAME "VGBI"
+#define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3 Buttons"
+
+#define SURFACE_BUTTON_NOTIFY_PRESS_POWER 0xc6
+#define SURFACE_BUTTON_NOTIFY_RELEASE_POWER 0xc7
+
+#define SURFACE_BUTTON_NOTIFY_PRESS_HOME 0xc4
+#define SURFACE_BUTTON_NOTIFY_RELEASE_HOME 0xc5
+
+#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP 0xc0
+#define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP 0xc1
+
+#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN 0xc2
+#define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN 0xc3
+
+ACPI_MODULE_NAME("surface pro 3 button");
+
+MODULE_AUTHOR("Chen Yu");
+MODULE_DESCRIPTION("Surface Pro3 Button Driver");
+MODULE_LICENSE("GPL v2");
+
+/*
+ * Power button, Home button, Volume buttons support is supposed to
+ * be covered by drivers/input/misc/soc_button_array.c, which is implemented
+ * according to "Windows ACPI Design Guide for SoC Platforms".
+ * However surface pro3 seems not to obey the specs, instead it uses
+ * device VGBI(MSHW0028) for dispatching the events.
+ * We choose acpi_driver rather than platform_driver/i2c_driver because
+ * although VGBI has an i2c resource connected to i2c controller, it
+ * is not embedded in any i2c controller's scope, thus neither platform_device
+ * will be created, nor i2c_client will be enumerated, we have to use
+ * acpi_driver.
+ */
+static const struct acpi_device_id surface_button_device_ids[] = {
+ {SURFACE_BUTTON_HID, 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, surface_button_device_ids);
+
+struct surface_button {
+ unsigned int type;
+ struct input_dev *input;
+ char phys[32]; /* for input device */
+ unsigned long pushed;
+ bool suspended;
+};
+
+static void surface_button_notify(struct acpi_device *device, u32 event)
+{
+ struct surface_button *button = acpi_driver_data(device);
+ struct input_dev *input;
+ int key_code = KEY_RESERVED;
+ bool pressed = false;
+
+ switch (event) {
+ /* Power button press,release handle */
+ case SURFACE_BUTTON_NOTIFY_PRESS_POWER:
+ pressed = true;
+ /*fall through*/
+ case SURFACE_BUTTON_NOTIFY_RELEASE_POWER:
+ key_code = KEY_POWER;
+ break;
+ /* Home button press,release handle */
+ case SURFACE_BUTTON_NOTIFY_PRESS_HOME:
+ pressed = true;
+ /*fall through*/
+ case SURFACE_BUTTON_NOTIFY_RELEASE_HOME:
+ key_code = KEY_LEFTMETA;
+ break;
+ /* Volume up button press,release handle */
+ case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP:
+ pressed = true;
+ /*fall through*/
+ case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP:
+ key_code = KEY_VOLUMEUP;
+ break;
+ /* Volume down button press,release handle */
+ case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN:
+ pressed = true;
+ /*fall through*/
+ case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN:
+ key_code = KEY_VOLUMEDOWN;
+ break;
+ default:
+ dev_info_ratelimited(&device->dev,
+ "Unsupported event [0x%x]\n", event);
+ break;
+ }
+ input = button->input;
+ if (KEY_RESERVED == key_code)
+ return;
+ if (pressed)
+ pm_wakeup_event(&device->dev, 0);
+ if (button->suspended)
+ return;
+ input_report_key(input, key_code, pressed?1:0);
+ input_sync(input);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int surface_button_suspend(struct device *dev)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct surface_button *button = acpi_driver_data(device);
+
+ button->suspended = true;
+ return 0;
+}
+
+static int surface_button_resume(struct device *dev)
+{
+ struct acpi_device *device = to_acpi_device(dev);
+ struct surface_button *button = acpi_driver_data(device);
+
+ button->suspended = false;
+ return 0;
+}
+#endif
+
+static int surface_button_add(struct acpi_device *device)
+{
+ struct surface_button *button;
+ struct input_dev *input;
+ const char *hid = acpi_device_hid(device);
+ char *name;
+ int error;
+
+ if (strncmp(acpi_device_bid(device), SURFACE_BUTTON_OBJ_NAME,
+ strlen(SURFACE_BUTTON_OBJ_NAME)))
+ return -ENODEV;
+
+ button = kzalloc(sizeof(struct surface_button), GFP_KERNEL);
+ if (!button)
+ return -ENOMEM;
+
+ device->driver_data = button;
+ button->input = input = input_allocate_device();
+ if (!input) {
+ error = -ENOMEM;
+ goto err_free_button;
+ }
+
+ name = acpi_device_name(device);
+ strcpy(name, SURFACE_BUTTON_DEVICE_NAME);
+ snprintf(button->phys, sizeof(button->phys), "%s/buttons", hid);
+
+ input->name = name;
+ input->phys = button->phys;
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = &device->dev;
+ input_set_capability(input, EV_KEY, KEY_POWER);
+ input_set_capability(input, EV_KEY, KEY_LEFTMETA);
+ input_set_capability(input, EV_KEY, KEY_VOLUMEUP);
+ input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN);
+
+ error = input_register_device(input);
+ if (error)
+ goto err_free_input;
+ dev_info(&device->dev,
+ "%s [%s]\n", name, acpi_device_bid(device));
+ return 0;
+
+ err_free_input:
+ input_free_device(input);
+ err_free_button:
+ kfree(button);
+ return error;
+}
+
+static int surface_button_remove(struct acpi_device *device)
+{
+ struct surface_button *button = acpi_driver_data(device);
+
+ input_unregister_device(button->input);
+ kfree(button);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(surface_button_pm,
+ surface_button_suspend, surface_button_resume);
+
+static struct acpi_driver surface_button_driver = {
+ .name = "surface_pro3_button",
+ .class = "SurfacePro3",
+ .ids = surface_button_device_ids,
+ .ops = {
+ .add = surface_button_add,
+ .remove = surface_button_remove,
+ .notify = surface_button_notify,
+ },
+ .drv.pm = &surface_button_pm,
+};
+
+module_acpi_driver(surface_button_driver);
diff --git a/kernel/drivers/platform/x86/tc1100-wmi.c b/kernel/drivers/platform/x86/tc1100-wmi.c
index e36542564..89aa976f0 100644
--- a/kernel/drivers/platform/x86/tc1100-wmi.c
+++ b/kernel/drivers/platform/x86/tc1100-wmi.c
@@ -82,7 +82,7 @@ static int get_state(u32 *out, u8 instance)
tmp = 0;
}
- if (result.length > 0 && result.pointer)
+ if (result.length > 0)
kfree(result.pointer);
switch (instance) {
diff --git a/kernel/drivers/platform/x86/thinkpad_acpi.c b/kernel/drivers/platform/x86/thinkpad_acpi.c
index 28f328136..0bed4733c 100644
--- a/kernel/drivers/platform/x86/thinkpad_acpi.c
+++ b/kernel/drivers/platform/x86/thinkpad_acpi.c
@@ -83,6 +83,7 @@
#include <sound/control.h>
#include <sound/initval.h>
#include <asm/uaccess.h>
+#include <acpi/video.h>
/* ThinkPad CMOS commands */
#define TP_CMOS_VOLUME_DOWN 0
@@ -401,7 +402,7 @@ static const char *str_supported(int is_supported);
#else
static inline const char *str_supported(int is_supported) { return ""; }
#define vdbg_printk(a_dbg_level, format, arg...) \
- no_printk(format, ##arg)
+ do { if (0) no_printk(format, ##arg); } while (0)
#endif
static void tpacpi_log_usertask(const char * const what)
@@ -3487,7 +3488,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
/* Do not issue duplicate brightness change events to
* userspace. tpacpi_detect_brightness_capabilities() must have
* been called before this point */
- if (acpi_video_backlight_support()) {
+ 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");
@@ -6458,8 +6459,7 @@ static void __init tpacpi_detect_brightness_capabilities(void)
pr_info("detected a 8-level brightness capable ThinkPad\n");
break;
default:
- pr_err("Unsupported brightness interface, "
- "please contact %s\n", TPACPI_MAIL);
+ pr_info("Unsupported brightness interface\n");
tp_features.bright_unkfw = 1;
bright_maxlvl = b - 1;
}
@@ -6491,7 +6491,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
return 1;
}
- if (acpi_video_backlight_support()) {
+ 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");
diff --git a/kernel/drivers/platform/x86/toshiba-wmi.c b/kernel/drivers/platform/x86/toshiba-wmi.c
new file mode 100644
index 000000000..feac4576b
--- /dev/null
+++ b/kernel/drivers/platform/x86/toshiba-wmi.c
@@ -0,0 +1,138 @@
+/*
+ * toshiba_wmi.c - Toshiba WMI Hotkey Driver
+ *
+ * Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com>
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/acpi.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+
+MODULE_AUTHOR("Azael Avalos");
+MODULE_DESCRIPTION("Toshiba WMI Hotkey Driver");
+MODULE_LICENSE("GPL");
+
+#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
+
+MODULE_ALIAS("wmi:"TOSHIBA_WMI_EVENT_GUID);
+
+static struct input_dev *toshiba_wmi_input_dev;
+
+static const struct key_entry toshiba_wmi_keymap[] __initconst = {
+ /* TODO: Add keymap values once found... */
+ /*{ KE_KEY, 0x00, { KEY_ } },*/
+ { KE_END, 0 }
+};
+
+static void toshiba_wmi_notify(u32 value, void *context)
+{
+ struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ acpi_status status;
+
+ status = wmi_get_event_data(value, &response);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Bad event status 0x%x\n", status);
+ return;
+ }
+
+ obj = (union acpi_object *)response.pointer;
+ if (!obj)
+ return;
+
+ /* TODO: Add proper checks once we have data */
+ pr_debug("Unknown event received, obj type %x\n", obj->type);
+
+ kfree(response.pointer);
+}
+
+static int __init toshiba_wmi_input_setup(void)
+{
+ acpi_status status;
+ int err;
+
+ toshiba_wmi_input_dev = input_allocate_device();
+ if (!toshiba_wmi_input_dev)
+ return -ENOMEM;
+
+ toshiba_wmi_input_dev->name = "Toshiba WMI hotkeys";
+ toshiba_wmi_input_dev->phys = "wmi/input0";
+ toshiba_wmi_input_dev->id.bustype = BUS_HOST;
+
+ err = sparse_keymap_setup(toshiba_wmi_input_dev,
+ toshiba_wmi_keymap, NULL);
+ if (err)
+ goto err_free_dev;
+
+ status = wmi_install_notify_handler(TOSHIBA_WMI_EVENT_GUID,
+ toshiba_wmi_notify, NULL);
+ if (ACPI_FAILURE(status)) {
+ err = -EIO;
+ goto err_free_keymap;
+ }
+
+ err = input_register_device(toshiba_wmi_input_dev);
+ if (err)
+ goto err_remove_notifier;
+
+ return 0;
+
+ err_remove_notifier:
+ wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID);
+ err_free_keymap:
+ sparse_keymap_free(toshiba_wmi_input_dev);
+ err_free_dev:
+ input_free_device(toshiba_wmi_input_dev);
+ return err;
+}
+
+static void toshiba_wmi_input_destroy(void)
+{
+ wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID);
+ sparse_keymap_free(toshiba_wmi_input_dev);
+ input_unregister_device(toshiba_wmi_input_dev);
+}
+
+static int __init toshiba_wmi_init(void)
+{
+ int ret;
+
+ if (!wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
+ return -ENODEV;
+
+ ret = toshiba_wmi_input_setup();
+ if (ret) {
+ pr_err("Failed to setup input device\n");
+ return ret;
+ }
+
+ pr_info("Toshiba WMI Hotkey Driver\n");
+
+ return 0;
+}
+
+static void __exit toshiba_wmi_exit(void)
+{
+ if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
+ toshiba_wmi_input_destroy();
+}
+
+module_init(toshiba_wmi_init);
+module_exit(toshiba_wmi_exit);
diff --git a/kernel/drivers/platform/x86/toshiba_acpi.c b/kernel/drivers/platform/x86/toshiba_acpi.c
index 9956b9902..b0f62141e 100644
--- a/kernel/drivers/platform/x86/toshiba_acpi.c
+++ b/kernel/drivers/platform/x86/toshiba_acpi.c
@@ -31,7 +31,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define TOSHIBA_ACPI_VERSION "0.21"
+#define TOSHIBA_ACPI_VERSION "0.23"
#define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h>
@@ -41,7 +41,6 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/backlight.h>
-#include <linux/rfkill.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/leds.h>
@@ -51,6 +50,8 @@
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/toshiba.h>
#include <acpi/video.h>
MODULE_AUTHOR("John Belmonte");
@@ -82,7 +83,7 @@ MODULE_LICENSE("GPL");
#define TCI_WORDS 6
-/* operations */
+/* Operations */
#define HCI_SET 0xff00
#define HCI_GET 0xfe00
#define SCI_OPEN 0xf100
@@ -90,8 +91,9 @@ MODULE_LICENSE("GPL");
#define SCI_GET 0xf300
#define SCI_SET 0xf400
-/* return codes */
+/* Return codes */
#define TOS_SUCCESS 0x0000
+#define TOS_SUCCESS2 0x0001
#define TOS_OPEN_CLOSE_OK 0x0044
#define TOS_FAILURE 0x1000
#define TOS_NOT_SUPPORTED 0x8000
@@ -105,14 +107,13 @@ MODULE_LICENSE("GPL");
#define TOS_NOT_INITIALIZED 0x8d50
#define TOS_NOT_INSTALLED 0x8e00
-/* registers */
+/* Registers */
#define HCI_FAN 0x0004
#define HCI_TR_BACKLIGHT 0x0005
#define HCI_SYSTEM_EVENT 0x0016
#define HCI_VIDEO_OUT 0x001c
#define HCI_HOTKEY_EVENT 0x001e
#define HCI_LCD_BRIGHTNESS 0x002a
-#define HCI_WIRELESS 0x0056
#define HCI_ACCELEROMETER 0x006d
#define HCI_KBD_ILLUMINATION 0x0095
#define HCI_ECO_MODE 0x0097
@@ -127,10 +128,10 @@ MODULE_LICENSE("GPL");
#define SCI_TOUCHPAD 0x050e
#define SCI_KBD_FUNCTION_KEYS 0x0522
-/* field definitions */
+/* Field definitions */
#define HCI_ACCEL_MASK 0x7fff
#define HCI_HOTKEY_DISABLE 0x0b
-#define HCI_HOTKEY_ENABLE 0x09
+#define HCI_HOTKEY_ENABLE 0x01
#define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10
#define HCI_LCD_BRIGHTNESS_BITS 3
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
@@ -141,10 +142,6 @@ MODULE_LICENSE("GPL");
#define HCI_VIDEO_OUT_LCD 0x1
#define HCI_VIDEO_OUT_CRT 0x2
#define HCI_VIDEO_OUT_TV 0x4
-#define HCI_WIRELESS_KILL_SWITCH 0x01
-#define HCI_WIRELESS_BT_PRESENT 0x0f
-#define HCI_WIRELESS_BT_ATTACH 0x40
-#define HCI_WIRELESS_BT_POWER 0x80
#define SCI_KBD_MODE_MASK 0x1f
#define SCI_KBD_MODE_FNZ 0x1
#define SCI_KBD_MODE_AUTO 0x2
@@ -165,13 +162,13 @@ MODULE_LICENSE("GPL");
struct toshiba_acpi_dev {
struct acpi_device *acpi_dev;
const char *method_hci;
- struct rfkill *bt_rfk;
struct input_dev *hotkey_dev;
struct work_struct hotkey_work;
struct backlight_device *backlight_dev;
struct led_classdev led_dev;
struct led_classdev kbd_led;
struct led_classdev eco_led;
+ struct miscdevice miscdev;
int force_fan;
int last_key_event;
@@ -191,7 +188,6 @@ struct toshiba_acpi_dev {
unsigned int info_supported:1;
unsigned int tr_backlight_supported:1;
unsigned int kbd_illum_supported:1;
- unsigned int kbd_led_registered:1;
unsigned int touchpad_supported:1;
unsigned int eco_supported:1;
unsigned int accelerometer_supported:1;
@@ -202,8 +198,11 @@ struct toshiba_acpi_dev {
unsigned int panel_power_on_supported:1;
unsigned int usb_three_supported:1;
unsigned int sysfs_created:1;
+ unsigned int special_functions;
- struct mutex mutex;
+ bool kbd_led_registered;
+ bool illumination_led_registered;
+ bool eco_led_registered;
};
static struct toshiba_acpi_dev *toshiba_acpi;
@@ -252,16 +251,16 @@ static const struct key_entry toshiba_acpi_keymap[] = {
};
static const struct key_entry toshiba_acpi_alt_keymap[] = {
- { KE_KEY, 0x157, { KEY_MUTE } },
{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
{ KE_KEY, 0x103, { KEY_ZOOMIN } },
{ KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } },
{ KE_KEY, 0x139, { KEY_ZOOMRESET } },
- { KE_KEY, 0x13e, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x13c, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0x13d, { KEY_BRIGHTNESSUP } },
- { KE_KEY, 0x158, { KEY_WLAN } },
+ { KE_KEY, 0x13e, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x13f, { KEY_TOUCHPAD_TOGGLE } },
+ { KE_KEY, 0x157, { KEY_MUTE } },
+ { KE_KEY, 0x158, { KEY_WLAN } },
{ KE_END, 0 },
};
@@ -330,13 +329,13 @@ static acpi_status tci_raw(struct toshiba_acpi_dev *dev,
}
/*
- * Common hci tasks (get or set one or two value)
+ * Common hci tasks
*
* In addition to the ACPI status, the HCI system returns a result which
* may be useful (such as "not supported").
*/
-static u32 hci_write1(struct toshiba_acpi_dev *dev, u32 reg, u32 in1)
+static u32 hci_write(struct toshiba_acpi_dev *dev, u32 reg, u32 in1)
{
u32 in[TCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 };
u32 out[TCI_WORDS];
@@ -345,7 +344,7 @@ static u32 hci_write1(struct toshiba_acpi_dev *dev, u32 reg, u32 in1)
return ACPI_SUCCESS(status) ? out[0] : TOS_FAILURE;
}
-static u32 hci_read1(struct toshiba_acpi_dev *dev, u32 reg, u32 *out1)
+static u32 hci_read(struct toshiba_acpi_dev *dev, u32 reg, u32 *out1)
{
u32 in[TCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 };
u32 out[TCI_WORDS];
@@ -359,31 +358,6 @@ static u32 hci_read1(struct toshiba_acpi_dev *dev, u32 reg, u32 *out1)
return out[0];
}
-static u32 hci_write2(struct toshiba_acpi_dev *dev, u32 reg, u32 in1, u32 in2)
-{
- u32 in[TCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 };
- u32 out[TCI_WORDS];
- acpi_status status = tci_raw(dev, in, out);
-
- return ACPI_SUCCESS(status) ? out[0] : TOS_FAILURE;
-}
-
-static u32 hci_read2(struct toshiba_acpi_dev *dev,
- u32 reg, u32 *out1, u32 *out2)
-{
- u32 in[TCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 };
- u32 out[TCI_WORDS];
- acpi_status status = tci_raw(dev, in, out);
-
- if (ACPI_FAILURE(status))
- return TOS_FAILURE;
-
- *out1 = out[2];
- *out2 = out[3];
-
- return out[0];
-}
-
/*
* Common sci tasks
*/
@@ -395,7 +369,7 @@ static int sci_open(struct toshiba_acpi_dev *dev)
acpi_status status;
status = tci_raw(dev, in, out);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to open SCI failed\n");
return 0;
}
@@ -433,7 +407,7 @@ static void sci_close(struct toshiba_acpi_dev *dev)
acpi_status status;
status = tci_raw(dev, in, out);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to close SCI failed\n");
return;
}
@@ -470,26 +444,24 @@ static u32 sci_write(struct toshiba_acpi_dev *dev, u32 reg, u32 in1)
}
/* Illumination support */
-static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
+static void toshiba_illumination_available(struct toshiba_acpi_dev *dev)
{
u32 in[TCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 };
u32 out[TCI_WORDS];
acpi_status status;
+ dev->illumination_supported = 0;
+ dev->illumination_led_registered = false;
+
if (!sci_open(dev))
- return 0;
+ return;
status = tci_raw(dev, in, out);
sci_close(dev);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+ if (ACPI_FAILURE(status))
pr_err("ACPI call to query Illumination support failed\n");
- return 0;
- } else if (out[0] == TOS_NOT_SUPPORTED) {
- pr_info("Illumination device not available\n");
- return 0;
- }
-
- return 1;
+ else if (out[0] == TOS_SUCCESS)
+ dev->illumination_supported = 1;
}
static void toshiba_illumination_set(struct led_classdev *cdev,
@@ -497,7 +469,8 @@ static void toshiba_illumination_set(struct led_classdev *cdev,
{
struct toshiba_acpi_dev *dev = container_of(cdev,
struct toshiba_acpi_dev, led_dev);
- u32 state, result;
+ u32 result;
+ u32 state;
/* First request : initialize communication. */
if (!sci_open(dev))
@@ -507,13 +480,8 @@ static void toshiba_illumination_set(struct led_classdev *cdev,
state = brightness ? 1 : 0;
result = sci_write(dev, SCI_ILLUMINATION, state);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call for illumination failed\n");
- return;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("Illumination not supported\n");
- return;
- }
}
static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
@@ -522,18 +490,17 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
struct toshiba_acpi_dev, led_dev);
u32 state, result;
- /* First request : initialize communication. */
+ /* First request : initialize communication. */
if (!sci_open(dev))
return LED_OFF;
/* Check the illumination */
result = sci_read(dev, SCI_ILLUMINATION, &state);
sci_close(dev);
- if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) {
+ if (result == TOS_FAILURE) {
pr_err("ACPI call for illumination failed\n");
return LED_OFF;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("Illumination not supported\n");
+ } else if (result != TOS_SUCCESS) {
return LED_OFF;
}
@@ -541,41 +508,40 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
}
/* KBD Illumination */
-static int toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev)
+static void toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev)
{
u32 in[TCI_WORDS] = { SCI_GET, SCI_KBD_ILLUM_STATUS, 0, 0, 0, 0 };
u32 out[TCI_WORDS];
acpi_status status;
+ dev->kbd_illum_supported = 0;
+ dev->kbd_led_registered = false;
+
if (!sci_open(dev))
- return 0;
+ return;
status = tci_raw(dev, in, out);
sci_close(dev);
- if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) {
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to query kbd illumination support failed\n");
- return 0;
- } else if (out[0] == TOS_NOT_SUPPORTED) {
- pr_info("Keyboard illumination not available\n");
- return 0;
+ } else if (out[0] == TOS_SUCCESS) {
+ /*
+ * Check for keyboard backlight timeout max value,
+ * previous kbd backlight implementation set this to
+ * 0x3c0003, and now the new implementation set this
+ * to 0x3c001a, use this to distinguish between them.
+ */
+ if (out[3] == SCI_KBD_TIME_MAX)
+ dev->kbd_type = 2;
+ else
+ dev->kbd_type = 1;
+ /* Get the current keyboard backlight mode */
+ dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK;
+ /* Get the current time (1-60 seconds) */
+ dev->kbd_time = out[2] >> HCI_MISC_SHIFT;
+ /* Flag as supported */
+ dev->kbd_illum_supported = 1;
}
-
- /*
- * Check for keyboard backlight timeout max value,
- * previous kbd backlight implementation set this to
- * 0x3c0003, and now the new implementation set this
- * to 0x3c001a, use this to distinguish between them.
- */
- if (out[3] == SCI_KBD_TIME_MAX)
- dev->kbd_type = 2;
- else
- dev->kbd_type = 1;
- /* Get the current keyboard backlight mode */
- dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK;
- /* Get the current time (1-60 seconds) */
- dev->kbd_time = out[2] >> HCI_MISC_SHIFT;
-
- return 1;
}
static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
@@ -587,15 +553,12 @@ static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
result = sci_write(dev, SCI_KBD_ILLUM_STATUS, time);
sci_close(dev);
- if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to set KBD backlight status failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("Keyboard backlight status not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- }
- return 0;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time)
@@ -607,30 +570,27 @@ static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time)
result = sci_read(dev, SCI_KBD_ILLUM_STATUS, time);
sci_close(dev);
- if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to get KBD backlight status failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("Keyboard backlight status not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- }
- return 0;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev)
{
struct toshiba_acpi_dev *dev = container_of(cdev,
struct toshiba_acpi_dev, kbd_led);
- u32 state, result;
+ u32 result;
+ u32 state;
/* Check the keyboard backlight state */
- result = hci_read1(dev, HCI_KBD_ILLUMINATION, &state);
- if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) {
+ result = hci_read(dev, HCI_KBD_ILLUMINATION, &state);
+ if (result == TOS_FAILURE) {
pr_err("ACPI call to get the keyboard backlight failed\n");
return LED_OFF;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("Keyboard backlight not supported\n");
+ } else if (result != TOS_SUCCESS) {
return LED_OFF;
}
@@ -642,18 +602,14 @@ static void toshiba_kbd_backlight_set(struct led_classdev *cdev,
{
struct toshiba_acpi_dev *dev = container_of(cdev,
struct toshiba_acpi_dev, kbd_led);
- u32 state, result;
+ u32 result;
+ u32 state;
/* Set the keyboard backlight state */
state = brightness ? 1 : 0;
- result = hci_write1(dev, HCI_KBD_ILLUMINATION, state);
- if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) {
+ result = hci_write(dev, HCI_KBD_ILLUMINATION, state);
+ if (result == TOS_FAILURE)
pr_err("ACPI call to set KBD Illumination mode failed\n");
- return;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("Keyboard backlight not supported\n");
- return;
- }
}
/* TouchPad support */
@@ -666,14 +622,12 @@ static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state)
result = sci_write(dev, SCI_TOUCHPAD, state);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to set the touchpad failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- }
- return 0;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state)
@@ -685,28 +639,27 @@ static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state)
result = sci_read(dev, SCI_TOUCHPAD, state);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to query the touchpad failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- }
- return 0;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
/* Eco Mode support */
-static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
+static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
{
acpi_status status;
u32 in[TCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 0, 0, 0 };
u32 out[TCI_WORDS];
+ dev->eco_supported = 0;
+ dev->eco_led_registered = false;
+
status = tci_raw(dev, in, out);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get ECO led failed\n");
- } else if (out[0] == TOS_NOT_INSTALLED) {
- pr_info("ECO led not installed");
} else if (out[0] == TOS_INPUT_DATA_ERROR) {
/*
* If we receive 0x8300 (Input Data Error), it means that the
@@ -719,13 +672,11 @@ static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
*/
in[3] = 1;
status = tci_raw(dev, in, out);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE)
+ if (ACPI_FAILURE(status))
pr_err("ACPI call to get ECO led failed\n");
else if (out[0] == TOS_SUCCESS)
- return 1;
+ dev->eco_supported = 1;
}
-
- return 0;
}
static enum led_brightness
@@ -738,9 +689,11 @@ toshiba_eco_mode_get_status(struct led_classdev *cdev)
acpi_status status;
status = tci_raw(dev, in, out);
- if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) {
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get ECO led failed\n");
return LED_OFF;
+ } else if (out[0] != TOS_SUCCESS) {
+ return LED_OFF;
}
return out[2] ? LED_FULL : LED_OFF;
@@ -758,41 +711,32 @@ static void toshiba_eco_mode_set_status(struct led_classdev *cdev,
/* Switch the Eco Mode led on/off */
in[2] = (brightness) ? 1 : 0;
status = tci_raw(dev, in, out);
- if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) {
+ if (ACPI_FAILURE(status))
pr_err("ACPI call to set ECO led failed\n");
- return;
- }
}
/* Accelerometer support */
-static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev)
+static void toshiba_accelerometer_available(struct toshiba_acpi_dev *dev)
{
u32 in[TCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 };
u32 out[TCI_WORDS];
acpi_status status;
+ dev->accelerometer_supported = 0;
+
/*
* Check if the accelerometer call exists,
* this call also serves as initialization
*/
status = tci_raw(dev, in, out);
- if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) {
+ if (ACPI_FAILURE(status))
pr_err("ACPI call to query the accelerometer failed\n");
- return -EIO;
- } else if (out[0] == TOS_DATA_NOT_AVAILABLE ||
- out[0] == TOS_NOT_INITIALIZED) {
- pr_err("Accelerometer not initialized\n");
- return -EIO;
- } else if (out[0] == TOS_NOT_SUPPORTED) {
- pr_info("Accelerometer not supported\n");
- return -ENODEV;
- }
-
- return 0;
+ else if (out[0] == TOS_SUCCESS)
+ dev->accelerometer_supported = 1;
}
static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
- u32 *xy, u32 *z)
+ u32 *xy, u32 *z)
{
u32 in[TCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 };
u32 out[TCI_WORDS];
@@ -800,15 +744,18 @@ static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
/* Check the Accelerometer status */
status = tci_raw(dev, in, out);
- if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) {
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to query the accelerometer failed\n");
return -EIO;
+ } else if (out[0] == TOS_NOT_SUPPORTED) {
+ return -ENODEV;
+ } else if (out[0] == TOS_SUCCESS) {
+ *xy = out[2];
+ *z = out[4];
+ return 0;
}
- *xy = out[2];
- *z = out[4];
-
- return 0;
+ return -EIO;
}
/* Sleep (Charge and Music) utilities support */
@@ -818,19 +765,17 @@ static void toshiba_usb_sleep_charge_available(struct toshiba_acpi_dev *dev)
u32 out[TCI_WORDS];
acpi_status status;
- /* Set the feature to "not supported" in case of error */
dev->usb_sleep_charge_supported = 0;
if (!sci_open(dev))
return;
status = tci_raw(dev, in, out);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get USB Sleep and Charge mode failed\n");
sci_close(dev);
return;
} else if (out[0] == TOS_NOT_SUPPORTED) {
- pr_info("USB Sleep and Charge not supported\n");
sci_close(dev);
return;
} else if (out[0] == TOS_SUCCESS) {
@@ -839,25 +784,15 @@ static void toshiba_usb_sleep_charge_available(struct toshiba_acpi_dev *dev)
in[5] = SCI_USB_CHARGE_BAT_LVL;
status = tci_raw(dev, in, out);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+ sci_close(dev);
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get USB Sleep and Charge mode failed\n");
- sci_close(dev);
- return;
- } else if (out[0] == TOS_NOT_SUPPORTED) {
- pr_info("USB Sleep and Charge not supported\n");
- sci_close(dev);
- return;
} else if (out[0] == TOS_SUCCESS) {
dev->usbsc_bat_level = out[2];
- /*
- * If we reach this point, it means that the laptop has support
- * for this feature and all values are initialized.
- * Set it as supported.
- */
+ /* Flag as supported */
dev->usb_sleep_charge_supported = 1;
}
- sci_close(dev);
}
static int toshiba_usb_sleep_charge_get(struct toshiba_acpi_dev *dev,
@@ -870,17 +805,12 @@ static int toshiba_usb_sleep_charge_get(struct toshiba_acpi_dev *dev,
result = sci_read(dev, SCI_USB_SLEEP_CHARGE, mode);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to set USB S&C mode failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("USB Sleep and Charge not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- } else if (result == TOS_INPUT_DATA_ERROR) {
- return -EIO;
- }
- return 0;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
static int toshiba_usb_sleep_charge_set(struct toshiba_acpi_dev *dev,
@@ -893,17 +823,12 @@ static int toshiba_usb_sleep_charge_set(struct toshiba_acpi_dev *dev,
result = sci_write(dev, SCI_USB_SLEEP_CHARGE, mode);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to set USB S&C mode failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("USB Sleep and Charge not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- } else if (result == TOS_INPUT_DATA_ERROR) {
- return -EIO;
- }
- return 0;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
static int toshiba_sleep_functions_status_get(struct toshiba_acpi_dev *dev,
@@ -919,19 +844,16 @@ static int toshiba_sleep_functions_status_get(struct toshiba_acpi_dev *dev,
in[5] = SCI_USB_CHARGE_BAT_LVL;
status = tci_raw(dev, in, out);
sci_close(dev);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get USB S&C battery level failed\n");
- return -EIO;
} else if (out[0] == TOS_NOT_SUPPORTED) {
- pr_info("USB Sleep and Charge not supported\n");
return -ENODEV;
- } else if (out[0] == TOS_INPUT_DATA_ERROR) {
- return -EIO;
+ } else if (out[0] == TOS_SUCCESS) {
+ *mode = out[2];
+ return 0;
}
- *mode = out[2];
-
- return 0;
+ return -EIO;
}
static int toshiba_sleep_functions_status_set(struct toshiba_acpi_dev *dev,
@@ -948,17 +870,12 @@ static int toshiba_sleep_functions_status_set(struct toshiba_acpi_dev *dev,
in[5] = SCI_USB_CHARGE_BAT_LVL;
status = tci_raw(dev, in, out);
sci_close(dev);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+ if (ACPI_FAILURE(status))
pr_err("ACPI call to set USB S&C battery level failed\n");
- return -EIO;
- } else if (out[0] == TOS_NOT_SUPPORTED) {
- pr_info("USB Sleep and Charge not supported\n");
+ else if (out[0] == TOS_NOT_SUPPORTED)
return -ENODEV;
- } else if (out[0] == TOS_INPUT_DATA_ERROR) {
- return -EIO;
- }
- return 0;
+ return out[0] == TOS_SUCCESS ? 0 : -EIO;
}
static int toshiba_usb_rapid_charge_get(struct toshiba_acpi_dev *dev,
@@ -974,18 +891,16 @@ static int toshiba_usb_rapid_charge_get(struct toshiba_acpi_dev *dev,
in[5] = SCI_USB_CHARGE_RAPID_DSP;
status = tci_raw(dev, in, out);
sci_close(dev);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get USB Rapid Charge failed\n");
- return -EIO;
- } else if (out[0] == TOS_NOT_SUPPORTED ||
- out[0] == TOS_INPUT_DATA_ERROR) {
- pr_info("USB Rapid Charge not supported\n");
+ } else if (out[0] == TOS_NOT_SUPPORTED) {
return -ENODEV;
+ } else if (out[0] == TOS_SUCCESS || out[0] == TOS_SUCCESS2) {
+ *state = out[2];
+ return 0;
}
- *state = out[2];
-
- return 0;
+ return -EIO;
}
static int toshiba_usb_rapid_charge_set(struct toshiba_acpi_dev *dev,
@@ -1002,17 +917,12 @@ static int toshiba_usb_rapid_charge_set(struct toshiba_acpi_dev *dev,
in[5] = SCI_USB_CHARGE_RAPID_DSP;
status = tci_raw(dev, in, out);
sci_close(dev);
- if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) {
+ if (ACPI_FAILURE(status))
pr_err("ACPI call to set USB Rapid Charge failed\n");
- return -EIO;
- } else if (out[0] == TOS_NOT_SUPPORTED) {
- pr_info("USB Rapid Charge not supported\n");
+ else if (out[0] == TOS_NOT_SUPPORTED)
return -ENODEV;
- } else if (out[0] == TOS_INPUT_DATA_ERROR) {
- return -EIO;
- }
- return 0;
+ return (out[0] == TOS_SUCCESS || out[0] == TOS_SUCCESS2) ? 0 : -EIO;
}
static int toshiba_usb_sleep_music_get(struct toshiba_acpi_dev *dev, u32 *state)
@@ -1024,17 +934,12 @@ static int toshiba_usb_sleep_music_get(struct toshiba_acpi_dev *dev, u32 *state)
result = sci_read(dev, SCI_USB_SLEEP_MUSIC, state);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to get Sleep and Music failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("Sleep and Music not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- } else if (result == TOS_INPUT_DATA_ERROR) {
- return -EIO;
- }
- return 0;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
static int toshiba_usb_sleep_music_set(struct toshiba_acpi_dev *dev, u32 state)
@@ -1046,17 +951,12 @@ static int toshiba_usb_sleep_music_set(struct toshiba_acpi_dev *dev, u32 state)
result = sci_write(dev, SCI_USB_SLEEP_MUSIC, state);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to set Sleep and Music failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("Sleep and Music not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- } else if (result == TOS_INPUT_DATA_ERROR) {
- return -EIO;
- }
- return 0;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
/* Keyboard function keys */
@@ -1069,15 +969,12 @@ static int toshiba_function_keys_get(struct toshiba_acpi_dev *dev, u32 *mode)
result = sci_read(dev, SCI_KBD_FUNCTION_KEYS, mode);
sci_close(dev);
- if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to get KBD function keys failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("KBD function keys not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- }
- return 0;
+ return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO;
}
static int toshiba_function_keys_set(struct toshiba_acpi_dev *dev, u32 mode)
@@ -1089,15 +986,12 @@ static int toshiba_function_keys_set(struct toshiba_acpi_dev *dev, u32 mode)
result = sci_write(dev, SCI_KBD_FUNCTION_KEYS, mode);
sci_close(dev);
- if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to set KBD function keys failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("KBD function keys not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- }
- return 0;
+ return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO;
}
/* Panel Power ON */
@@ -1110,17 +1004,12 @@ static int toshiba_panel_power_on_get(struct toshiba_acpi_dev *dev, u32 *state)
result = sci_read(dev, SCI_PANEL_POWER_ON, state);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to get Panel Power ON failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("Panel Power on not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- } else if (result == TOS_INPUT_DATA_ERROR) {
- return -EIO;
- }
- return 0;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
static int toshiba_panel_power_on_set(struct toshiba_acpi_dev *dev, u32 state)
@@ -1132,17 +1021,12 @@ static int toshiba_panel_power_on_set(struct toshiba_acpi_dev *dev, u32 state)
result = sci_write(dev, SCI_PANEL_POWER_ON, state);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to set Panel Power ON failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("Panel Power ON not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- } else if (result == TOS_INPUT_DATA_ERROR) {
- return -EIO;
- }
- return 0;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
/* USB Three */
@@ -1155,17 +1039,12 @@ static int toshiba_usb_three_get(struct toshiba_acpi_dev *dev, u32 *state)
result = sci_read(dev, SCI_USB_THREE, state);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to get USB 3 failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("USB 3 not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- } else if (result == TOS_INPUT_DATA_ERROR) {
- return -EIO;
- }
- return 0;
+ return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO;
}
static int toshiba_usb_three_set(struct toshiba_acpi_dev *dev, u32 state)
@@ -1177,172 +1056,85 @@ static int toshiba_usb_three_set(struct toshiba_acpi_dev *dev, u32 state)
result = sci_write(dev, SCI_USB_THREE, state);
sci_close(dev);
- if (result == TOS_FAILURE) {
+ if (result == TOS_FAILURE)
pr_err("ACPI call to set USB 3 failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("USB 3 not supported\n");
+ else if (result == TOS_NOT_SUPPORTED)
return -ENODEV;
- } else if (result == TOS_INPUT_DATA_ERROR) {
- return -EIO;
- }
- return 0;
+ return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO;
}
/* Hotkey Event type */
static int toshiba_hotkey_event_type_get(struct toshiba_acpi_dev *dev,
u32 *type)
{
- u32 val1 = 0x03;
- u32 val2 = 0;
- u32 result;
+ u32 in[TCI_WORDS] = { HCI_GET, HCI_SYSTEM_INFO, 0x03, 0, 0, 0 };
+ u32 out[TCI_WORDS];
+ acpi_status status;
- result = hci_read2(dev, HCI_SYSTEM_INFO, &val1, &val2);
- if (result == TOS_FAILURE) {
+ status = tci_raw(dev, in, out);
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI call to get System type failed\n");
- return -EIO;
- } else if (result == TOS_NOT_SUPPORTED) {
- pr_info("System type not supported\n");
+ } else if (out[0] == TOS_NOT_SUPPORTED) {
return -ENODEV;
+ } else if (out[0] == TOS_SUCCESS) {
+ *type = out[3];
+ return 0;
}
- *type = val2;
-
- return 0;
-}
-
-/* Bluetooth rfkill handlers */
-
-static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present)
-{
- u32 hci_result;
- u32 value, value2;
-
- value = 0;
- value2 = 0;
- hci_result = hci_read2(dev, HCI_WIRELESS, &value, &value2);
- if (hci_result == TOS_SUCCESS)
- *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false;
-
- return hci_result;
-}
-
-static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state)
-{
- u32 hci_result;
- u32 value, value2;
-
- value = 0;
- value2 = 0x0001;
- hci_result = hci_read2(dev, HCI_WIRELESS, &value, &value2);
-
- *radio_state = value & HCI_WIRELESS_KILL_SWITCH;
- return hci_result;
-}
-
-static int bt_rfkill_set_block(void *data, bool blocked)
-{
- struct toshiba_acpi_dev *dev = data;
- u32 result1, result2;
- u32 value;
- int err;
- bool radio_state;
-
- value = (blocked == false);
-
- mutex_lock(&dev->mutex);
- if (hci_get_radio_state(dev, &radio_state) != TOS_SUCCESS) {
- err = -EIO;
- goto out;
- }
-
- if (!radio_state) {
- err = 0;
- goto out;
- }
-
- result1 = hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER);
- result2 = hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH);
-
- if (result1 != TOS_SUCCESS || result2 != TOS_SUCCESS)
- err = -EIO;
- else
- err = 0;
- out:
- mutex_unlock(&dev->mutex);
- return err;
+ return -EIO;
}
-static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
+/* Transflective Backlight */
+static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, u32 *status)
{
- bool new_rfk_state;
- bool value;
- u32 hci_result;
- struct toshiba_acpi_dev *dev = data;
-
- mutex_lock(&dev->mutex);
-
- hci_result = hci_get_radio_state(dev, &value);
- if (hci_result != TOS_SUCCESS) {
- /* Can't do anything useful */
- mutex_unlock(&dev->mutex);
- return;
- }
+ u32 result = hci_read(dev, HCI_TR_BACKLIGHT, status);
- new_rfk_state = value;
-
- mutex_unlock(&dev->mutex);
+ if (result == TOS_FAILURE)
+ pr_err("ACPI call to get Transflective Backlight failed\n");
+ else if (result == TOS_NOT_SUPPORTED)
+ return -ENODEV;
- if (rfkill_set_hw_state(rfkill, !new_rfk_state))
- bt_rfkill_set_block(data, true);
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
-static const struct rfkill_ops toshiba_rfk_ops = {
- .set_block = bt_rfkill_set_block,
- .poll = bt_rfkill_poll,
-};
-
-static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, bool *enabled)
+static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, u32 status)
{
- u32 hci_result;
- u32 status;
+ u32 result = hci_write(dev, HCI_TR_BACKLIGHT, !status);
- hci_result = hci_read1(dev, HCI_TR_BACKLIGHT, &status);
- *enabled = !status;
- return hci_result == TOS_SUCCESS ? 0 : -EIO;
-}
-
-static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable)
-{
- u32 hci_result;
- u32 value = !enable;
+ if (result == TOS_FAILURE)
+ pr_err("ACPI call to set Transflective Backlight failed\n");
+ else if (result == TOS_NOT_SUPPORTED)
+ return -ENODEV;
- hci_result = hci_write1(dev, HCI_TR_BACKLIGHT, value);
- return hci_result == TOS_SUCCESS ? 0 : -EIO;
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
-static struct proc_dir_entry *toshiba_proc_dir /*= 0*/;
+static struct proc_dir_entry *toshiba_proc_dir;
+/* LCD Brightness */
static int __get_lcd_brightness(struct toshiba_acpi_dev *dev)
{
- u32 hci_result;
+ u32 result;
u32 value;
int brightness = 0;
if (dev->tr_backlight_supported) {
- bool enabled;
- int ret = get_tr_backlight_status(dev, &enabled);
+ int ret = get_tr_backlight_status(dev, &value);
if (ret)
return ret;
- if (enabled)
+ if (value)
return 0;
brightness++;
}
- hci_result = hci_read1(dev, HCI_LCD_BRIGHTNESS, &value);
- if (hci_result == TOS_SUCCESS)
+ result = hci_read(dev, HCI_LCD_BRIGHTNESS, &value);
+ if (result == TOS_FAILURE)
+ pr_err("ACPI call to get LCD Brightness failed\n");
+ else if (result == TOS_NOT_SUPPORTED)
+ return -ENODEV;
+ if (result == TOS_SUCCESS)
return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT);
return -EIO;
@@ -1358,8 +1150,8 @@ static int get_lcd_brightness(struct backlight_device *bd)
static int lcd_proc_show(struct seq_file *m, void *v)
{
struct toshiba_acpi_dev *dev = m->private;
- int value;
int levels;
+ int value;
if (!dev->backlight_dev)
return -ENODEV;
@@ -1373,6 +1165,7 @@ static int lcd_proc_show(struct seq_file *m, void *v)
}
pr_err("Error reading LCD brightness\n");
+
return -EIO;
}
@@ -1383,11 +1176,10 @@ static int lcd_proc_open(struct inode *inode, struct file *file)
static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
{
- u32 hci_result;
+ u32 result;
if (dev->tr_backlight_supported) {
- bool enable = !value;
- int ret = set_tr_backlight_status(dev, enable);
+ int ret = set_tr_backlight_status(dev, !value);
if (ret)
return ret;
@@ -1396,8 +1188,13 @@ static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
}
value = value << HCI_LCD_BRIGHTNESS_SHIFT;
- hci_result = hci_write1(dev, HCI_LCD_BRIGHTNESS, value);
- return hci_result == TOS_SUCCESS ? 0 : -EIO;
+ result = hci_write(dev, HCI_LCD_BRIGHTNESS, value);
+ if (result == TOS_FAILURE)
+ pr_err("ACPI call to set LCD Brightness failed\n");
+ else if (result == TOS_NOT_SUPPORTED)
+ return -ENODEV;
+
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
static int set_lcd_status(struct backlight_device *bd)
@@ -1413,24 +1210,22 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
char cmd[42];
size_t len;
- int value;
- int ret;
int levels = dev->backlight_dev->props.max_brightness + 1;
+ int value;
len = min(count, sizeof(cmd) - 1);
if (copy_from_user(cmd, buf, len))
return -EFAULT;
cmd[len] = '\0';
- if (sscanf(cmd, " brightness : %i", &value) == 1 &&
- value >= 0 && value < levels) {
- ret = set_lcd_brightness(dev, value);
- if (ret == 0)
- ret = count;
- } else {
- ret = -EINVAL;
- }
- return ret;
+ if (sscanf(cmd, " brightness : %i", &value) != 1 &&
+ value < 0 && value > levels)
+ return -EINVAL;
+
+ if (set_lcd_brightness(dev, value))
+ return -EIO;
+
+ return count;
}
static const struct file_operations lcd_proc_fops = {
@@ -1442,22 +1237,25 @@ static const struct file_operations lcd_proc_fops = {
.write = lcd_proc_write,
};
+/* Video-Out */
static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status)
{
- u32 hci_result;
+ u32 result = hci_read(dev, HCI_VIDEO_OUT, status);
- hci_result = hci_read1(dev, HCI_VIDEO_OUT, status);
- return hci_result == TOS_SUCCESS ? 0 : -EIO;
+ if (result == TOS_FAILURE)
+ pr_err("ACPI call to get Video-Out failed\n");
+ else if (result == TOS_NOT_SUPPORTED)
+ return -ENODEV;
+
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
static int video_proc_show(struct seq_file *m, void *v)
{
struct toshiba_acpi_dev *dev = m->private;
u32 value;
- int ret;
- ret = get_video_status(dev, &value);
- if (!ret) {
+ if (!get_video_status(dev, &value)) {
int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
@@ -1465,9 +1263,10 @@ static int video_proc_show(struct seq_file *m, void *v)
seq_printf(m, "lcd_out: %d\n", is_lcd);
seq_printf(m, "crt_out: %d\n", is_crt);
seq_printf(m, "tv_out: %d\n", is_tv);
+ return 0;
}
- return ret;
+ return -EIO;
}
static int video_proc_open(struct inode *inode, struct file *file)
@@ -1479,13 +1278,14 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
- char *cmd, *buffer;
- int ret;
- int value;
+ char *buffer;
+ char *cmd;
int remain = count;
int lcd_out = -1;
int crt_out = -1;
int tv_out = -1;
+ int value;
+ int ret;
u32 video_out;
cmd = kmalloc(count + 1, GFP_KERNEL);
@@ -1531,12 +1331,13 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
_set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out);
/*
* To avoid unnecessary video disruption, only write the new
- * video setting if something changed. */
+ * video setting if something changed.
+ */
if (new_video_out != video_out)
ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out);
}
- return ret ? ret : count;
+ return ret ? -EIO : count;
}
static const struct file_operations video_proc_fops = {
@@ -1548,27 +1349,43 @@ static const struct file_operations video_proc_fops = {
.write = video_proc_write,
};
+/* Fan status */
static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status)
{
- u32 hci_result;
+ u32 result = hci_read(dev, HCI_FAN, status);
- hci_result = hci_read1(dev, HCI_FAN, status);
- return hci_result == TOS_SUCCESS ? 0 : -EIO;
+ if (result == TOS_FAILURE)
+ pr_err("ACPI call to get Fan status failed\n");
+ else if (result == TOS_NOT_SUPPORTED)
+ return -ENODEV;
+
+ return result == TOS_SUCCESS ? 0 : -EIO;
+}
+
+static int set_fan_status(struct toshiba_acpi_dev *dev, u32 status)
+{
+ u32 result = hci_write(dev, HCI_FAN, status);
+
+ if (result == TOS_FAILURE)
+ pr_err("ACPI call to set Fan status failed\n");
+ else if (result == TOS_NOT_SUPPORTED)
+ return -ENODEV;
+
+ return result == TOS_SUCCESS ? 0 : -EIO;
}
static int fan_proc_show(struct seq_file *m, void *v)
{
struct toshiba_acpi_dev *dev = m->private;
- int ret;
u32 value;
- ret = get_fan_status(dev, &value);
- if (!ret) {
- seq_printf(m, "running: %d\n", (value > 0));
- seq_printf(m, "force_on: %d\n", dev->force_fan);
- }
+ if (get_fan_status(dev, &value))
+ return -EIO;
- return ret;
+ seq_printf(m, "running: %d\n", (value > 0));
+ seq_printf(m, "force_on: %d\n", dev->force_fan);
+
+ return 0;
}
static int fan_proc_open(struct inode *inode, struct file *file)
@@ -1583,23 +1400,20 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf,
char cmd[42];
size_t len;
int value;
- u32 hci_result;
len = min(count, sizeof(cmd) - 1);
if (copy_from_user(cmd, buf, len))
return -EFAULT;
cmd[len] = '\0';
- if (sscanf(cmd, " force_on : %i", &value) == 1 &&
- value >= 0 && value <= 1) {
- hci_result = hci_write1(dev, HCI_FAN, value);
- if (hci_result == TOS_SUCCESS)
- dev->force_fan = value;
- else
- return -EIO;
- } else {
+ if (sscanf(cmd, " force_on : %i", &value) != 1 &&
+ value != 0 && value != 1)
return -EINVAL;
- }
+
+ if (set_fan_status(dev, value))
+ return -EIO;
+
+ dev->force_fan = value;
return count;
}
@@ -1616,32 +1430,10 @@ static const struct file_operations fan_proc_fops = {
static int keys_proc_show(struct seq_file *m, void *v)
{
struct toshiba_acpi_dev *dev = m->private;
- u32 hci_result;
- u32 value;
-
- if (!dev->key_event_valid && dev->system_event_supported) {
- hci_result = hci_read1(dev, HCI_SYSTEM_EVENT, &value);
- if (hci_result == TOS_SUCCESS) {
- dev->key_event_valid = 1;
- dev->last_key_event = value;
- } else if (hci_result == TOS_FIFO_EMPTY) {
- /* Better luck next time */
- } else if (hci_result == TOS_NOT_SUPPORTED) {
- /*
- * This is a workaround for an unresolved issue on
- * some machines where system events sporadically
- * become disabled.
- */
- hci_result = hci_write1(dev, HCI_SYSTEM_EVENT, 1);
- pr_notice("Re-enabled hotkeys\n");
- } else {
- pr_err("Error reading hotkey status\n");
- return -EIO;
- }
- }
seq_printf(m, "hotkey_ready: %d\n", dev->key_event_valid);
seq_printf(m, "hotkey: 0x%04x\n", dev->last_key_event);
+
return 0;
}
@@ -1758,7 +1550,6 @@ static ssize_t fan_store(struct device *dev,
const char *buf, size_t count)
{
struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
- u32 result;
int state;
int ret;
@@ -1769,11 +1560,9 @@ static ssize_t fan_store(struct device *dev,
if (state != 0 && state != 1)
return -EINVAL;
- result = hci_write1(toshiba, HCI_FAN, state);
- if (result == TOS_FAILURE)
- return -EIO;
- else if (result == TOS_NOT_SUPPORTED)
- return -ENODEV;
+ ret = set_fan_status(toshiba, state);
+ if (ret)
+ return ret;
return count;
}
@@ -1799,7 +1588,6 @@ static ssize_t kbd_backlight_mode_store(struct device *dev,
{
struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
int mode;
- int time;
int ret;
@@ -1830,7 +1618,7 @@ static ssize_t kbd_backlight_mode_store(struct device *dev,
/* Only make a change if the actual mode has changed */
if (toshiba->kbd_mode != mode) {
/* Shift the time to "base time" (0x3c0000 == 60 seconds) */
- time = toshiba->kbd_time << HCI_MISC_SHIFT;
+ int time = toshiba->kbd_time << HCI_MISC_SHIFT;
/* OR the "base time" to the actual method format */
if (toshiba->kbd_type == 1) {
@@ -1881,10 +1669,10 @@ static ssize_t available_kbd_modes_show(struct device *dev,
struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
if (toshiba->kbd_type == 1)
- return sprintf(buf, "%x %x\n",
+ return sprintf(buf, "0x%x 0x%x\n",
SCI_KBD_MODE_FNZ, SCI_KBD_MODE_AUTO);
- return sprintf(buf, "%x %x %x\n",
+ return sprintf(buf, "0x%x 0x%x 0x%x\n",
SCI_KBD_MODE_AUTO, SCI_KBD_MODE_ON, SCI_KBD_MODE_OFF);
}
static DEVICE_ATTR_RO(available_kbd_modes);
@@ -2379,6 +2167,81 @@ static struct attribute_group toshiba_attr_group = {
};
/*
+ * Misc device
+ */
+static int toshiba_acpi_smm_bridge(SMMRegisters *regs)
+{
+ u32 in[TCI_WORDS] = { regs->eax, regs->ebx, regs->ecx,
+ regs->edx, regs->esi, regs->edi };
+ u32 out[TCI_WORDS];
+ acpi_status status;
+
+ status = tci_raw(toshiba_acpi, in, out);
+ if (ACPI_FAILURE(status)) {
+ pr_err("ACPI call to query SMM registers failed\n");
+ return -EIO;
+ }
+
+ /* Fillout the SMM struct with the TCI call results */
+ regs->eax = out[0];
+ regs->ebx = out[1];
+ regs->ecx = out[2];
+ regs->edx = out[3];
+ regs->esi = out[4];
+ regs->edi = out[5];
+
+ return 0;
+}
+
+static long toshiba_acpi_ioctl(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+ SMMRegisters __user *argp = (SMMRegisters __user *)arg;
+ SMMRegisters regs;
+ int ret;
+
+ if (!argp)
+ return -EINVAL;
+
+ switch (cmd) {
+ case TOSH_SMM:
+ if (copy_from_user(&regs, argp, sizeof(SMMRegisters)))
+ return -EFAULT;
+ ret = toshiba_acpi_smm_bridge(&regs);
+ if (ret)
+ return ret;
+ if (copy_to_user(argp, &regs, sizeof(SMMRegisters)))
+ return -EFAULT;
+ break;
+ case TOSHIBA_ACPI_SCI:
+ if (copy_from_user(&regs, argp, sizeof(SMMRegisters)))
+ return -EFAULT;
+ /* Ensure we are being called with a SCI_{GET, SET} register */
+ if (regs.eax != SCI_GET && regs.eax != SCI_SET)
+ return -EINVAL;
+ if (!sci_open(toshiba_acpi))
+ return -EIO;
+ ret = toshiba_acpi_smm_bridge(&regs);
+ sci_close(toshiba_acpi);
+ if (ret)
+ return ret;
+ if (copy_to_user(argp, &regs, sizeof(SMMRegisters)))
+ return -EFAULT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct file_operations toshiba_acpi_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = toshiba_acpi_ioctl,
+ .llseek = noop_llseek,
+};
+
+/*
* Hotkeys
*/
static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
@@ -2391,7 +2254,16 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
if (ACPI_FAILURE(status))
return -ENODEV;
- result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
+ /*
+ * Enable the "Special Functions" mode only if they are
+ * supported and if they are activated.
+ */
+ if (dev->kbd_function_keys_supported && dev->special_functions)
+ result = hci_write(dev, HCI_HOTKEY_EVENT,
+ HCI_HOTKEY_SPECIAL_FUNCTIONS);
+ else
+ result = hci_write(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
+
if (result == TOS_FAILURE)
return -EIO;
else if (result == TOS_NOT_SUPPORTED)
@@ -2400,20 +2272,6 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
return 0;
}
-static void toshiba_acpi_enable_special_functions(struct toshiba_acpi_dev *dev)
-{
- u32 result;
-
- /*
- * Re-activate the hotkeys, but this time, we are using the
- * "Special Functions" mode.
- */
- result = hci_write1(dev, HCI_HOTKEY_EVENT,
- HCI_HOTKEY_SPECIAL_FUNCTIONS);
- if (result != TOS_SUCCESS)
- pr_err("Could not enable the Special Function mode\n");
-}
-
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
{
@@ -2478,22 +2336,28 @@ static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
static void toshiba_acpi_process_hotkeys(struct toshiba_acpi_dev *dev)
{
- u32 hci_result, value;
- int retries = 3;
- int scancode;
-
if (dev->info_supported) {
- scancode = toshiba_acpi_query_hotkey(dev);
- if (scancode < 0)
+ int scancode = toshiba_acpi_query_hotkey(dev);
+
+ if (scancode < 0) {
pr_err("Failed to query hotkey event\n");
- else if (scancode != 0)
+ } else if (scancode != 0) {
toshiba_acpi_report_hotkey(dev, scancode);
+ dev->key_event_valid = 1;
+ dev->last_key_event = scancode;
+ }
} else if (dev->system_event_supported) {
+ u32 result;
+ u32 value;
+ int retries = 3;
+
do {
- hci_result = hci_read1(dev, HCI_SYSTEM_EVENT, &value);
- switch (hci_result) {
+ result = hci_read(dev, HCI_SYSTEM_EVENT, &value);
+ switch (result) {
case TOS_SUCCESS:
toshiba_acpi_report_hotkey(dev, (int)value);
+ dev->key_event_valid = 1;
+ dev->last_key_event = value;
break;
case TOS_NOT_SUPPORTED:
/*
@@ -2501,15 +2365,15 @@ static void toshiba_acpi_process_hotkeys(struct toshiba_acpi_dev *dev)
* issue on some machines where system events
* sporadically become disabled.
*/
- hci_result =
- hci_write1(dev, HCI_SYSTEM_EVENT, 1);
- pr_notice("Re-enabled hotkeys\n");
+ result = hci_write(dev, HCI_SYSTEM_EVENT, 1);
+ if (result == TOS_SUCCESS)
+ pr_notice("Re-enabled hotkeys\n");
/* Fall through */
default:
retries--;
break;
}
- } while (retries && hci_result != TOS_FIFO_EMPTY);
+ } while (retries && result != TOS_FIFO_EMPTY);
}
}
@@ -2517,20 +2381,19 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
{
const struct key_entry *keymap = toshiba_acpi_keymap;
acpi_handle ec_handle;
- u32 events_type;
- u32 hci_result;
int error;
+ if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) {
+ pr_info("WMI event detected, hotkeys will not be monitored\n");
+ return 0;
+ }
+
error = toshiba_acpi_enable_hotkeys(dev);
if (error)
return error;
- error = toshiba_hotkey_event_type_get(dev, &events_type);
- if (error) {
- pr_err("Unable to query Hotkey Event Type\n");
- return error;
- }
- dev->hotkey_event_type = events_type;
+ if (toshiba_hotkey_event_type_get(dev, &dev->hotkey_event_type))
+ pr_notice("Unable to query Hotkey Event Type\n");
dev->hotkey_dev = input_allocate_device();
if (!dev->hotkey_dev)
@@ -2540,14 +2403,15 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
dev->hotkey_dev->phys = "toshiba_acpi/input0";
dev->hotkey_dev->id.bustype = BUS_HOST;
- if (events_type == HCI_SYSTEM_TYPE1 ||
+ if (dev->hotkey_event_type == HCI_SYSTEM_TYPE1 ||
!dev->kbd_function_keys_supported)
keymap = toshiba_acpi_keymap;
- else if (events_type == HCI_SYSTEM_TYPE2 ||
+ else if (dev->hotkey_event_type == HCI_SYSTEM_TYPE2 ||
dev->kbd_function_keys_supported)
keymap = toshiba_acpi_alt_keymap;
else
- pr_info("Unknown event type received %x\n", events_type);
+ pr_info("Unknown event type received %x\n",
+ dev->hotkey_event_type);
error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL);
if (error)
goto err_free_dev;
@@ -2578,11 +2442,8 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
*/
if (acpi_has_method(dev->acpi_dev->handle, "INFO"))
dev->info_supported = 1;
- else {
- hci_result = hci_write1(dev, HCI_SYSTEM_EVENT, 1);
- if (hci_result == TOS_SUCCESS)
- dev->system_event_supported = 1;
- }
+ else if (hci_write(dev, HCI_SYSTEM_EVENT, 1) == TOS_SUCCESS)
+ dev->system_event_supported = 1;
if (!dev->info_supported && !dev->system_event_supported) {
pr_warn("No hotkey query interface found\n");
@@ -2613,7 +2474,6 @@ static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
struct backlight_properties props;
int brightness;
int ret;
- bool enabled;
/*
* Some machines don't support the backlight methods at all, and
@@ -2624,30 +2484,31 @@ static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
brightness = __get_lcd_brightness(dev);
if (brightness < 0)
return 0;
+ /*
+ * If transflective backlight is supported and the brightness is zero
+ * (lowest brightness level), the set_lcd_brightness function will
+ * activate the transflective backlight, making the LCD appear to be
+ * turned off, simply increment the brightness level to avoid that.
+ */
+ if (dev->tr_backlight_supported && brightness == 0)
+ brightness++;
ret = set_lcd_brightness(dev, brightness);
if (ret) {
pr_debug("Backlight method is read-only, disabling backlight support\n");
return 0;
}
- /* Determine whether or not BIOS supports transflective backlight */
- ret = get_tr_backlight_status(dev, &enabled);
- dev->tr_backlight_supported = !ret;
-
/*
* Tell acpi-video-detect code to prefer vendor backlight on all
* systems with transflective backlight and on dmi matched systems.
*/
if (dev->tr_backlight_supported ||
dmi_check_system(toshiba_vendor_backlight_dmi))
- acpi_video_dmi_promote_vendor();
+ acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
- if (acpi_video_backlight_support())
+ if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return 0;
- /* acpi-video may have loaded before we called dmi_promote_vendor() */
- acpi_video_unregister_backlight();
-
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
@@ -2672,10 +2533,52 @@ static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
return 0;
}
+static void print_supported_features(struct toshiba_acpi_dev *dev)
+{
+ pr_info("Supported laptop features:");
+
+ if (dev->hotkey_dev)
+ pr_cont(" hotkeys");
+ if (dev->backlight_dev)
+ pr_cont(" backlight");
+ if (dev->video_supported)
+ pr_cont(" video-out");
+ if (dev->fan_supported)
+ pr_cont(" fan");
+ if (dev->tr_backlight_supported)
+ pr_cont(" transflective-backlight");
+ if (dev->illumination_supported)
+ pr_cont(" illumination");
+ if (dev->kbd_illum_supported)
+ pr_cont(" keyboard-backlight");
+ if (dev->touchpad_supported)
+ pr_cont(" touchpad");
+ if (dev->eco_supported)
+ pr_cont(" eco-led");
+ if (dev->accelerometer_supported)
+ pr_cont(" accelerometer-axes");
+ if (dev->usb_sleep_charge_supported)
+ pr_cont(" usb-sleep-charge");
+ if (dev->usb_rapid_charge_supported)
+ pr_cont(" usb-rapid-charge");
+ if (dev->usb_sleep_music_supported)
+ pr_cont(" usb-sleep-music");
+ if (dev->kbd_function_keys_supported)
+ pr_cont(" special-function-keys");
+ if (dev->panel_power_on_supported)
+ pr_cont(" panel-power-on");
+ if (dev->usb_three_supported)
+ pr_cont(" usb3");
+
+ pr_cont("\n");
+}
+
static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
{
struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
+ misc_deregister(&dev->miscdev);
+
remove_toshiba_proc_entries(dev);
if (dev->sysfs_created)
@@ -2692,20 +2595,15 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
sparse_keymap_free(dev->hotkey_dev);
}
- if (dev->bt_rfk) {
- rfkill_unregister(dev->bt_rfk);
- rfkill_destroy(dev->bt_rfk);
- }
-
backlight_device_unregister(dev->backlight_dev);
- if (dev->illumination_supported)
+ if (dev->illumination_led_registered)
led_classdev_unregister(&dev->led_dev);
if (dev->kbd_led_registered)
led_classdev_unregister(&dev->kbd_led);
- if (dev->eco_supported)
+ if (dev->eco_led_registered)
led_classdev_unregister(&dev->eco_led);
if (toshiba_acpi)
@@ -2731,9 +2629,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
{
struct toshiba_acpi_dev *dev;
const char *hci_method;
- u32 special_functions;
u32 dummy;
- bool bt_present;
int ret = 0;
if (toshiba_acpi)
@@ -2753,6 +2649,17 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
return -ENOMEM;
dev->acpi_dev = acpi_dev;
dev->method_hci = hci_method;
+ dev->miscdev.minor = MISC_DYNAMIC_MINOR;
+ dev->miscdev.name = "toshiba_acpi";
+ dev->miscdev.fops = &toshiba_acpi_fops;
+
+ ret = misc_register(&dev->miscdev);
+ if (ret) {
+ pr_err("Failed to register miscdevice\n");
+ kfree(dev);
+ return ret;
+ }
+
acpi_dev->driver_data = dev;
dev_set_drvdata(&acpi_dev->dev, dev);
@@ -2763,58 +2670,42 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
* with the new keyboard layout, query for its presence to help
* determine the keymap layout to use.
*/
- ret = toshiba_function_keys_get(dev, &special_functions);
+ ret = toshiba_function_keys_get(dev, &dev->special_functions);
dev->kbd_function_keys_supported = !ret;
+ dev->hotkey_event_type = 0;
if (toshiba_acpi_setup_keyboard(dev))
pr_info("Unable to activate hotkeys\n");
- mutex_init(&dev->mutex);
+ /* Determine whether or not BIOS supports transflective backlight */
+ ret = get_tr_backlight_status(dev, &dummy);
+ dev->tr_backlight_supported = !ret;
ret = toshiba_acpi_setup_backlight(dev);
if (ret)
goto error;
- /* Register rfkill switch for Bluetooth */
- if (hci_get_bt_present(dev, &bt_present) == TOS_SUCCESS && bt_present) {
- dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth",
- &acpi_dev->dev,
- RFKILL_TYPE_BLUETOOTH,
- &toshiba_rfk_ops,
- dev);
- if (!dev->bt_rfk) {
- pr_err("unable to allocate rfkill device\n");
- ret = -ENOMEM;
- goto error;
- }
-
- ret = rfkill_register(dev->bt_rfk);
- if (ret) {
- pr_err("unable to register rfkill device\n");
- rfkill_destroy(dev->bt_rfk);
- goto error;
- }
- }
-
- if (toshiba_illumination_available(dev)) {
+ toshiba_illumination_available(dev);
+ if (dev->illumination_supported) {
dev->led_dev.name = "toshiba::illumination";
dev->led_dev.max_brightness = 1;
dev->led_dev.brightness_set = toshiba_illumination_set;
dev->led_dev.brightness_get = toshiba_illumination_get;
if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev))
- dev->illumination_supported = 1;
+ dev->illumination_led_registered = true;
}
- if (toshiba_eco_mode_available(dev)) {
+ toshiba_eco_mode_available(dev);
+ if (dev->eco_supported) {
dev->eco_led.name = "toshiba::eco_mode";
dev->eco_led.max_brightness = 1;
dev->eco_led.brightness_set = toshiba_eco_mode_set_status;
dev->eco_led.brightness_get = toshiba_eco_mode_get_status;
if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led))
- dev->eco_supported = 1;
+ dev->eco_led_registered = true;
}
- dev->kbd_illum_supported = toshiba_kbd_illum_available(dev);
+ toshiba_kbd_illum_available(dev);
/*
* Only register the LED if KBD illumination is supported
* and the keyboard backlight operation mode is set to FN-Z
@@ -2825,14 +2716,13 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led))
- dev->kbd_led_registered = 1;
+ dev->kbd_led_registered = true;
}
ret = toshiba_touchpad_get(dev, &dummy);
dev->touchpad_supported = !ret;
- ret = toshiba_accelerometer_supported(dev);
- dev->accelerometer_supported = !ret;
+ toshiba_accelerometer_available(dev);
toshiba_usb_sleep_charge_available(dev);
@@ -2854,12 +2744,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
ret = get_fan_status(dev, &dummy);
dev->fan_supported = !ret;
- /*
- * Enable the "Special Functions" mode only if they are
- * supported and if they are activated.
- */
- if (dev->kbd_function_keys_supported && special_functions)
- toshiba_acpi_enable_special_functions(dev);
+ print_supported_features(dev);
ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
&toshiba_attr_group);
@@ -2887,6 +2772,14 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
switch (event) {
case 0x80: /* Hotkeys and some system events */
+ /*
+ * Machines with this WMI GUID aren't supported due to bugs in
+ * their AML.
+ *
+ * Return silently to avoid triggering a netlink event.
+ */
+ if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
+ return;
toshiba_acpi_process_hotkeys(dev);
break;
case 0x81: /* Dock events */
@@ -2930,10 +2823,14 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
static int toshiba_acpi_suspend(struct device *device)
{
struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
- u32 result;
- if (dev->hotkey_dev)
- result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE);
+ if (dev->hotkey_dev) {
+ u32 result;
+
+ result = hci_write(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE);
+ if (result != TOS_SUCCESS)
+ pr_info("Unable to disable hotkeys\n");
+ }
return 0;
}
@@ -2941,10 +2838,10 @@ static int toshiba_acpi_suspend(struct device *device)
static int toshiba_acpi_resume(struct device *device)
{
struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
- int error;
if (dev->hotkey_dev) {
- error = toshiba_acpi_enable_hotkeys(dev);
+ int error = toshiba_acpi_enable_hotkeys(dev);
+
if (error)
pr_info("Unable to re-enable hotkeys\n");
}
@@ -2973,14 +2870,6 @@ static int __init toshiba_acpi_init(void)
{
int ret;
- /*
- * Machines with this WMI guid aren't supported due to bugs in
- * their AML. This check relies on wmi initializing before
- * toshiba_acpi to guarantee guids have been identified.
- */
- if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
- return -ENODEV;
-
toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
if (!toshiba_proc_dir) {
pr_err("Unable to create proc dir " PROC_TOSHIBA "\n");
diff --git a/kernel/drivers/platform/x86/toshiba_bluetooth.c b/kernel/drivers/platform/x86/toshiba_bluetooth.c
index 249800763..c5e45089a 100644
--- a/kernel/drivers/platform/x86/toshiba_bluetooth.c
+++ b/kernel/drivers/platform/x86/toshiba_bluetooth.c
@@ -10,12 +10,6 @@
* 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.
- *
- * Note the Toshiba Bluetooth RFKill switch seems to be a strange
- * fish. It only provides a BT event when the switch is flipped to
- * the 'on' position. When flipping it to 'off', the USB device is
- * simply pulled away underneath us, without any BT event being
- * delivered.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -25,6 +19,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/acpi.h>
+#include <linux/rfkill.h>
#define BT_KILLSWITCH_MASK 0x01
#define BT_PLUGGED_MASK 0x40
@@ -34,6 +29,15 @@ MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>");
MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver");
MODULE_LICENSE("GPL");
+struct toshiba_bluetooth_dev {
+ struct acpi_device *acpi_dev;
+ struct rfkill *rfk;
+
+ bool killswitch;
+ bool plugged;
+ bool powered;
+};
+
static int toshiba_bt_rfkill_add(struct acpi_device *device);
static int toshiba_bt_rfkill_remove(struct acpi_device *device);
static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event);
@@ -95,41 +99,12 @@ static int toshiba_bluetooth_status(acpi_handle handle)
return -ENXIO;
}
- pr_info("Bluetooth status %llu\n", status);
-
return status;
}
static int toshiba_bluetooth_enable(acpi_handle handle)
{
acpi_status result;
- bool killswitch;
- bool powered;
- bool plugged;
- int status;
-
- /*
- * Query ACPI to verify RFKill switch is set to 'on'.
- * If not, we return silently, no need to report it as
- * an error.
- */
- status = toshiba_bluetooth_status(handle);
- if (status < 0)
- return status;
-
- killswitch = (status & BT_KILLSWITCH_MASK) ? true : false;
- powered = (status & BT_POWER_MASK) ? true : false;
- plugged = (status & BT_PLUGGED_MASK) ? true : false;
-
- if (!killswitch)
- return 0;
- /*
- * This check ensures to only enable the device if it is powered
- * off or detached, as some recent devices somehow pass the killswitch
- * test, causing a loop enabling/disabling the device, see bug 93911.
- */
- if (powered || plugged)
- return 0;
result = acpi_evaluate_object(handle, "AUSB", NULL, NULL);
if (ACPI_FAILURE(result)) {
@@ -165,20 +140,102 @@ static int toshiba_bluetooth_disable(acpi_handle handle)
return 0;
}
+/* Helper function */
+static int toshiba_bluetooth_sync_status(struct toshiba_bluetooth_dev *bt_dev)
+{
+ int status;
+
+ status = toshiba_bluetooth_status(bt_dev->acpi_dev->handle);
+ if (status < 0) {
+ pr_err("Could not sync bluetooth device status\n");
+ return status;
+ }
+
+ bt_dev->killswitch = (status & BT_KILLSWITCH_MASK) ? true : false;
+ bt_dev->plugged = (status & BT_PLUGGED_MASK) ? true : false;
+ bt_dev->powered = (status & BT_POWER_MASK) ? true : false;
+
+ pr_debug("Bluetooth status %d killswitch %d plugged %d powered %d\n",
+ status, bt_dev->killswitch, bt_dev->plugged, bt_dev->powered);
+
+ return 0;
+}
+
+/* RFKill handlers */
+static int bt_rfkill_set_block(void *data, bool blocked)
+{
+ struct toshiba_bluetooth_dev *bt_dev = data;
+ int ret;
+
+ ret = toshiba_bluetooth_sync_status(bt_dev);
+ if (ret)
+ return ret;
+
+ if (!bt_dev->killswitch)
+ return 0;
+
+ if (blocked)
+ ret = toshiba_bluetooth_disable(bt_dev->acpi_dev->handle);
+ else
+ ret = toshiba_bluetooth_enable(bt_dev->acpi_dev->handle);
+
+ return ret;
+}
+
+static void bt_rfkill_poll(struct rfkill *rfkill, void *data)
+{
+ struct toshiba_bluetooth_dev *bt_dev = data;
+
+ if (toshiba_bluetooth_sync_status(bt_dev))
+ return;
+
+ /*
+ * Note the Toshiba Bluetooth RFKill switch seems to be a strange
+ * fish. It only provides a BT event when the switch is flipped to
+ * the 'on' position. When flipping it to 'off', the USB device is
+ * simply pulled away underneath us, without any BT event being
+ * delivered.
+ */
+ rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
+}
+
+static const struct rfkill_ops rfk_ops = {
+ .set_block = bt_rfkill_set_block,
+ .poll = bt_rfkill_poll,
+};
+
+/* ACPI driver functions */
static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event)
{
- toshiba_bluetooth_enable(device->handle);
+ struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device);
+
+ if (toshiba_bluetooth_sync_status(bt_dev))
+ return;
+
+ rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
}
#ifdef CONFIG_PM_SLEEP
static int toshiba_bt_resume(struct device *dev)
{
- return toshiba_bluetooth_enable(to_acpi_device(dev)->handle);
+ struct toshiba_bluetooth_dev *bt_dev;
+ int ret;
+
+ bt_dev = acpi_driver_data(to_acpi_device(dev));
+
+ ret = toshiba_bluetooth_sync_status(bt_dev);
+ if (ret)
+ return ret;
+
+ rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
+
+ return 0;
}
#endif
static int toshiba_bt_rfkill_add(struct acpi_device *device)
{
+ struct toshiba_bluetooth_dev *bt_dev;
int result;
result = toshiba_bluetooth_present(device->handle);
@@ -187,17 +244,54 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device)
pr_info("Toshiba ACPI Bluetooth device driver\n");
- /* Enable the BT device */
- result = toshiba_bluetooth_enable(device->handle);
- if (result)
+ bt_dev = kzalloc(sizeof(*bt_dev), GFP_KERNEL);
+ if (!bt_dev)
+ return -ENOMEM;
+ bt_dev->acpi_dev = device;
+ device->driver_data = bt_dev;
+ dev_set_drvdata(&device->dev, bt_dev);
+
+ result = toshiba_bluetooth_sync_status(bt_dev);
+ if (result) {
+ kfree(bt_dev);
return result;
+ }
+
+ bt_dev->rfk = rfkill_alloc("Toshiba Bluetooth",
+ &device->dev,
+ RFKILL_TYPE_BLUETOOTH,
+ &rfk_ops,
+ bt_dev);
+ if (!bt_dev->rfk) {
+ pr_err("Unable to allocate rfkill device\n");
+ kfree(bt_dev);
+ return -ENOMEM;
+ }
+
+ rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch);
+
+ result = rfkill_register(bt_dev->rfk);
+ if (result) {
+ pr_err("Unable to register rfkill device\n");
+ rfkill_destroy(bt_dev->rfk);
+ kfree(bt_dev);
+ }
return result;
}
static int toshiba_bt_rfkill_remove(struct acpi_device *device)
{
+ struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device);
+
/* clean up */
+ if (bt_dev->rfk) {
+ rfkill_unregister(bt_dev->rfk);
+ rfkill_destroy(bt_dev->rfk);
+ }
+
+ kfree(bt_dev);
+
return toshiba_bluetooth_disable(device->handle);
}
diff --git a/kernel/drivers/platform/x86/toshiba_haps.c b/kernel/drivers/platform/x86/toshiba_haps.c
index 65300b6a8..7f2afc6b5 100644
--- a/kernel/drivers/platform/x86/toshiba_haps.c
+++ b/kernel/drivers/platform/x86/toshiba_haps.c
@@ -78,15 +78,20 @@ static ssize_t protection_level_store(struct device *dev,
const char *buf, size_t count)
{
struct toshiba_haps_dev *haps = dev_get_drvdata(dev);
- int level, ret;
-
- if (sscanf(buf, "%d", &level) != 1 || level < 0 || level > 3)
- return -EINVAL;
+ int level;
+ int ret;
- /* Set the sensor level.
- * Acceptable levels are:
+ ret = kstrtoint(buf, 0, &level);
+ if (ret)
+ return ret;
+ /*
+ * Check for supported levels, which can be:
* 0 - Disabled | 1 - Low | 2 - Medium | 3 - High
*/
+ if (level < 0 || level > 3)
+ return -EINVAL;
+
+ /* Set the sensor level */
ret = toshiba_haps_protection_level(haps->acpi_dev->handle, level);
if (ret != 0)
return ret;
@@ -95,15 +100,21 @@ static ssize_t protection_level_store(struct device *dev,
return count;
}
+static DEVICE_ATTR_RW(protection_level);
static ssize_t reset_protection_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct toshiba_haps_dev *haps = dev_get_drvdata(dev);
- int reset, ret;
+ int reset;
+ int ret;
- if (sscanf(buf, "%d", &reset) != 1 || reset != 1)
+ ret = kstrtoint(buf, 0, &reset);
+ if (ret)
+ return ret;
+ /* The only accepted value is 1 */
+ if (reset != 1)
return -EINVAL;
/* Reset the protection interface */
@@ -113,10 +124,7 @@ static ssize_t reset_protection_store(struct device *dev,
return count;
}
-
-static DEVICE_ATTR(protection_level, S_IRUGO | S_IWUSR,
- protection_level_show, protection_level_store);
-static DEVICE_ATTR(reset_protection, S_IWUSR, NULL, reset_protection_store);
+static DEVICE_ATTR_WO(reset_protection);
static struct attribute *haps_attributes[] = {
&dev_attr_protection_level.attr,
diff --git a/kernel/drivers/platform/x86/wmi.c b/kernel/drivers/platform/x86/wmi.c
index aac47573f..eb391a281 100644
--- a/kernel/drivers/platform/x86/wmi.c
+++ b/kernel/drivers/platform/x86/wmi.c
@@ -194,34 +194,6 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest)
return true;
}
-/*
- * Convert a raw GUID to the ACII string representation
- */
-static int wmi_gtoa(const char *in, char *out)
-{
- int i;
-
- for (i = 3; i >= 0; i--)
- out += sprintf(out, "%02X", in[i] & 0xFF);
-
- out += sprintf(out, "-");
- out += sprintf(out, "%02X", in[5] & 0xFF);
- out += sprintf(out, "%02X", in[4] & 0xFF);
- out += sprintf(out, "-");
- out += sprintf(out, "%02X", in[7] & 0xFF);
- out += sprintf(out, "%02X", in[6] & 0xFF);
- out += sprintf(out, "-");
- out += sprintf(out, "%02X", in[8] & 0xFF);
- out += sprintf(out, "%02X", in[9] & 0xFF);
- out += sprintf(out, "-");
-
- for (i = 10; i <= 15; i++)
- out += sprintf(out, "%02X", in[i] & 0xFF);
-
- *out = '\0';
- return 0;
-}
-
static bool find_guid(const char *guid_string, struct wmi_block **out)
{
char tmp[16], guid_input[16];
@@ -457,11 +429,7 @@ EXPORT_SYMBOL_GPL(wmi_set_block);
static void wmi_dump_wdg(const struct guid_block *g)
{
- char guid_string[37];
-
- wmi_gtoa(g->guid, guid_string);
-
- pr_info("%s:\n", guid_string);
+ 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);
@@ -661,7 +629,6 @@ EXPORT_SYMBOL_GPL(wmi_has_guid);
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- char guid_string[37];
struct wmi_block *wblock;
wblock = dev_get_drvdata(dev);
@@ -670,9 +637,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
return strlen(buf);
}
- wmi_gtoa(wblock->gblock.guid, guid_string);
-
- return sprintf(buf, "wmi:%s\n", guid_string);
+ return sprintf(buf, "wmi:%pUL\n", wblock->gblock.guid);
}
static DEVICE_ATTR_RO(modalias);
@@ -695,7 +660,7 @@ static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
if (!wblock)
return -ENOMEM;
- wmi_gtoa(wblock->gblock.guid, guid_string);
+ sprintf(guid_string, "%pUL", wblock->gblock.guid);
strcpy(&env->buf[env->buflen - 1], "wmi:");
memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
@@ -721,12 +686,9 @@ static struct class wmi_class = {
static int wmi_create_device(const struct guid_block *gblock,
struct wmi_block *wblock, acpi_handle handle)
{
- char guid_string[37];
-
wblock->dev.class = &wmi_class;
- wmi_gtoa(gblock->guid, guid_string);
- dev_set_name(&wblock->dev, "%s", guid_string);
+ dev_set_name(&wblock->dev, "%pUL", gblock->guid);
dev_set_drvdata(&wblock->dev, wblock);
@@ -877,7 +839,6 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
struct guid_block *block;
struct wmi_block *wblock;
struct list_head *p;
- char guid_string[37];
list_for_each(p, &wmi_block_list) {
wblock = list_entry(p, struct wmi_block, list);
@@ -888,8 +849,8 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
if (wblock->handler)
wblock->handler(event, wblock->handler_data);
if (debug_event) {
- wmi_gtoa(wblock->gblock.guid, guid_string);
- pr_info("DEBUG Event GUID: %s\n", guid_string);
+ pr_info("DEBUG Event GUID: %pUL\n",
+ wblock->gblock.guid);
}
acpi_bus_generate_netlink_event(