diff options
Diffstat (limited to 'drivers/platform')
47 files changed, 1619 insertions, 482 deletions
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 970679d0b6f6..ee5f08ea57b6 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -3,6 +3,16 @@ # Platform support for Chrome OS hardware (Chromebooks and Chromeboxes) # +config MFD_CROS_EC + tristate "Platform support for Chrome hardware (transitional)" + select CHROME_PLATFORMS + select CROS_EC + select CONFIG_MFD_CROS_EC_DEV + depends on X86 || ARM || ARM64 || COMPILE_TEST + help + This is a transitional Kconfig option and will be removed after + everyone enables the parts individually. + menuconfig CHROME_PLATFORMS bool "Platform support for Chrome hardware" depends on X86 || ARM || ARM64 || COMPILE_TEST @@ -50,9 +60,22 @@ config CHROMEOS_TBMC To compile this driver as a module, choose M here: the module will be called chromeos_tbmc. +config CROS_EC + tristate "ChromeOS Embedded Controller" + select CROS_EC_PROTO + depends on X86 || ARM || ARM64 || COMPILE_TEST + help + If you say Y here you get support for the ChromeOS Embedded + Controller (EC) providing keyboard, battery and power services. + You also need to enable the driver for the bus you are using. The + protocol for talking to the EC is defined by the bus driver. + + To compile this driver as a module, choose M here: the + module will be called cros_ec. + config CROS_EC_I2C tristate "ChromeOS Embedded Controller (I2C)" - depends on MFD_CROS_EC && I2C + depends on CROS_EC && I2C help If you say Y here, you get support for talking to the ChromeOS @@ -62,7 +85,7 @@ config CROS_EC_I2C config CROS_EC_RPMSG tristate "ChromeOS Embedded Controller (rpmsg)" - depends on MFD_CROS_EC && RPMSG && OF + depends on CROS_EC && RPMSG && OF help If you say Y here, you get support for talking to the ChromeOS EC through rpmsg. This uses a simple byte-level protocol with a @@ -74,7 +97,7 @@ config CROS_EC_RPMSG config CROS_EC_ISHTP tristate "ChromeOS Embedded Controller (ISHTP)" - depends on MFD_CROS_EC + depends on CROS_EC depends on INTEL_ISH_HID help If you say Y here, you get support for talking to the ChromeOS EC @@ -87,7 +110,7 @@ config CROS_EC_ISHTP config CROS_EC_SPI tristate "ChromeOS Embedded Controller (SPI)" - depends on MFD_CROS_EC && SPI + depends on CROS_EC && SPI ---help--- If you say Y here, you get support for talking to the ChromeOS EC @@ -97,7 +120,7 @@ config CROS_EC_SPI config CROS_EC_LPC tristate "ChromeOS Embedded Controller (LPC)" - depends on MFD_CROS_EC && ACPI && (X86 || COMPILE_TEST) + depends on CROS_EC && ACPI && (X86 || COMPILE_TEST) help If you say Y here, you get support for talking to the ChromeOS EC over an LPC bus, including the LPC Microchip EC (MEC) variant. @@ -123,10 +146,21 @@ config CROS_KBD_LED_BACKLIGHT To compile this driver as a module, choose M here: the module will be called cros_kbd_led_backlight. +config CROS_EC_CHARDEV + tristate "ChromeOS EC miscdevice" + depends on MFD_CROS_EC_DEV + default MFD_CROS_EC_DEV + help + This driver adds file operations support to talk with the + ChromeOS EC from userspace via a character device. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_chardev. + config CROS_EC_LIGHTBAR tristate "Chromebook Pixel's lightbar support" - depends on MFD_CROS_EC_CHARDEV - default MFD_CROS_EC_CHARDEV + depends on MFD_CROS_EC_DEV + default MFD_CROS_EC_DEV help This option exposes the Chromebook Pixel's lightbar to userspace. @@ -136,8 +170,8 @@ config CROS_EC_LIGHTBAR config CROS_EC_VBC tristate "ChromeOS EC vboot context support" - depends on MFD_CROS_EC_CHARDEV && OF - default MFD_CROS_EC_CHARDEV + depends on MFD_CROS_EC_DEV && OF + default MFD_CROS_EC_DEV help This option exposes the ChromeOS EC vboot context nvram to userspace. @@ -147,8 +181,8 @@ config CROS_EC_VBC config CROS_EC_DEBUGFS tristate "Export ChromeOS EC internals in DebugFS" - depends on MFD_CROS_EC_CHARDEV && DEBUG_FS - default MFD_CROS_EC_CHARDEV + depends on MFD_CROS_EC_DEV && DEBUG_FS + default MFD_CROS_EC_DEV help This option exposes the ChromeOS EC device internals to userspace. @@ -158,8 +192,8 @@ config CROS_EC_DEBUGFS config CROS_EC_SYSFS tristate "ChromeOS EC control and information through sysfs" - depends on MFD_CROS_EC_CHARDEV && SYSFS - default MFD_CROS_EC_CHARDEV + depends on MFD_CROS_EC_DEV && SYSFS + default MFD_CROS_EC_DEV help This option exposes some sysfs attributes to control and get information from ChromeOS EC. diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index fd0af05cc14c..477ec3d1d1c9 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -6,6 +6,7 @@ CFLAGS_cros_ec_trace.o:= -I$(src) obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o obj-$(CONFIG_CHROMEOS_TBMC) += chromeos_tbmc.o +obj-$(CONFIG_CROS_EC) += cros_ec.o obj-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_CROS_EC_ISHTP) += cros_ec_ishtp.o obj-$(CONFIG_CROS_EC_RPMSG) += cros_ec_rpmsg.o @@ -14,6 +15,7 @@ cros_ec_lpcs-objs := cros_ec_lpc.o cros_ec_lpc_mec.o obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpcs.o obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o cros_ec_trace.o obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o +obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_chardev.o obj-$(CONFIG_CROS_EC_LIGHTBAR) += cros_ec_lightbar.o obj-$(CONFIG_CROS_EC_VBC) += cros_ec_vbc.o obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 7abbb6167766..8723bcf10c93 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -838,18 +838,14 @@ static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop) i2c_dev = &cros_laptop->i2c_peripherals[i]; info = &i2c_dev->board_info; - if (i2c_dev->client) - i2c_unregister_device(i2c_dev->client); - - if (info->properties) - property_entries_free(info->properties); + i2c_unregister_device(i2c_dev->client); + property_entries_free(info->properties); } for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) { acpi_dev = &cros_laptop->acpi_peripherals[i]; - if (acpi_dev->properties) - property_entries_free(acpi_dev->properties); + property_entries_free(acpi_dev->properties); } kfree(cros_laptop->i2c_peripherals); diff --git a/drivers/platform/chrome/chromeos_tbmc.c b/drivers/platform/chrome/chromeos_tbmc.c index ce259ec9f990..d1cf8f3463ce 100644 --- a/drivers/platform/chrome/chromeos_tbmc.c +++ b/drivers/platform/chrome/chromeos_tbmc.c @@ -47,6 +47,7 @@ static __maybe_unused int chromeos_tbmc_resume(struct device *dev) static void chromeos_tbmc_notify(struct acpi_device *adev, u32 event) { + acpi_pm_wakeup_event(&adev->dev); switch (event) { case 0x80: chromeos_tbmc_query_switch(adev, adev->driver_data); @@ -90,6 +91,7 @@ static int chromeos_tbmc_add(struct acpi_device *adev) dev_err(dev, "cannot register input device\n"); return ret; } + device_init_wakeup(dev, true); return 0; } diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c new file mode 100644 index 000000000000..fd77e6fa74c2 --- /dev/null +++ b/drivers/platform/chrome/cros_ec.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ChromeOS EC multi-function device + * + * Copyright (C) 2012 Google, Inc + * + * The ChromeOS EC multi function device is used to mux all the requests + * to the EC device for its multiple features: keyboard controller, + * battery charging and regulator control, firmware update. + */ + +#include <linux/of_platform.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> +#include <linux/suspend.h> +#include <asm/unaligned.h> + +#define CROS_EC_DEV_EC_INDEX 0 +#define CROS_EC_DEV_PD_INDEX 1 + +static struct cros_ec_platform ec_p = { + .ec_name = CROS_EC_DEV_NAME, + .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX), +}; + +static struct cros_ec_platform pd_p = { + .ec_name = CROS_EC_DEV_PD_NAME, + .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX), +}; + +static irqreturn_t ec_irq_thread(int irq, void *data) +{ + struct cros_ec_device *ec_dev = data; + bool wake_event = true; + int ret; + + ret = cros_ec_get_next_event(ec_dev, &wake_event); + + /* + * Signal only if wake host events or any interrupt if + * cros_ec_get_next_event() returned an error (default value for + * wake_event is true) + */ + if (wake_event && device_may_wakeup(ec_dev->dev)) + pm_wakeup_event(ec_dev->dev, 0); + + if (ret > 0) + blocking_notifier_call_chain(&ec_dev->event_notifier, + 0, ec_dev); + return IRQ_HANDLED; +} + +static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event) +{ + int ret; + struct { + struct cros_ec_command msg; + union { + struct ec_params_host_sleep_event req0; + struct ec_params_host_sleep_event_v1 req1; + struct ec_response_host_sleep_event_v1 resp1; + } u; + } __packed buf; + + memset(&buf, 0, sizeof(buf)); + + if (ec_dev->host_sleep_v1) { + buf.u.req1.sleep_event = sleep_event; + buf.u.req1.suspend_params.sleep_timeout_ms = + EC_HOST_SLEEP_TIMEOUT_DEFAULT; + + buf.msg.outsize = sizeof(buf.u.req1); + if ((sleep_event == HOST_SLEEP_EVENT_S3_RESUME) || + (sleep_event == HOST_SLEEP_EVENT_S0IX_RESUME)) + buf.msg.insize = sizeof(buf.u.resp1); + + buf.msg.version = 1; + + } else { + buf.u.req0.sleep_event = sleep_event; + buf.msg.outsize = sizeof(buf.u.req0); + } + + buf.msg.command = EC_CMD_HOST_SLEEP_EVENT; + + ret = cros_ec_cmd_xfer(ec_dev, &buf.msg); + + /* For now, report failure to transition to S0ix with a warning. */ + if (ret >= 0 && ec_dev->host_sleep_v1 && + (sleep_event == HOST_SLEEP_EVENT_S0IX_RESUME)) { + ec_dev->last_resume_result = + buf.u.resp1.resume_response.sleep_transitions; + + WARN_ONCE(buf.u.resp1.resume_response.sleep_transitions & + EC_HOST_RESUME_SLEEP_TIMEOUT, + "EC detected sleep transition timeout. Total slp_s0 transitions: %d", + buf.u.resp1.resume_response.sleep_transitions & + EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK); + } + + return ret; +} + +int cros_ec_register(struct cros_ec_device *ec_dev) +{ + struct device *dev = ec_dev->dev; + int err = 0; + + BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); + + ec_dev->max_request = sizeof(struct ec_params_hello); + ec_dev->max_response = sizeof(struct ec_response_get_protocol_info); + ec_dev->max_passthru = 0; + + ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); + if (!ec_dev->din) + return -ENOMEM; + + ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); + if (!ec_dev->dout) + return -ENOMEM; + + mutex_init(&ec_dev->lock); + + err = cros_ec_query_all(ec_dev); + if (err) { + dev_err(dev, "Cannot identify the EC: error %d\n", err); + return err; + } + + if (ec_dev->irq) { + err = devm_request_threaded_irq(dev, ec_dev->irq, NULL, + ec_irq_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "chromeos-ec", ec_dev); + if (err) { + dev_err(dev, "Failed to request IRQ %d: %d", + ec_dev->irq, err); + return err; + } + } + + /* Register a platform device for the main EC instance */ + ec_dev->ec = platform_device_register_data(ec_dev->dev, "cros-ec-dev", + PLATFORM_DEVID_AUTO, &ec_p, + sizeof(struct cros_ec_platform)); + if (IS_ERR(ec_dev->ec)) { + dev_err(ec_dev->dev, + "Failed to create CrOS EC platform device\n"); + return PTR_ERR(ec_dev->ec); + } + + if (ec_dev->max_passthru) { + /* + * Register a platform device for the PD behind the main EC. + * We make the following assumptions: + * - behind an EC, we have a pd + * - only one device added. + * - the EC is responsive at init time (it is not true for a + * sensor hub). + */ + ec_dev->pd = platform_device_register_data(ec_dev->dev, + "cros-ec-dev", + PLATFORM_DEVID_AUTO, &pd_p, + sizeof(struct cros_ec_platform)); + if (IS_ERR(ec_dev->pd)) { + dev_err(ec_dev->dev, + "Failed to create CrOS PD platform device\n"); + platform_device_unregister(ec_dev->ec); + return PTR_ERR(ec_dev->pd); + } + } + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) { + err = devm_of_platform_populate(dev); + if (err) { + platform_device_unregister(ec_dev->pd); + platform_device_unregister(ec_dev->ec); + dev_err(dev, "Failed to register sub-devices\n"); + return err; + } + } + + /* + * Clear sleep event - this will fail harmlessly on platforms that + * don't implement the sleep event host command. + */ + err = cros_ec_sleep_event(ec_dev, 0); + if (err < 0) + dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec", + err); + + dev_info(dev, "Chrome EC device registered\n"); + + return 0; +} +EXPORT_SYMBOL(cros_ec_register); + +int cros_ec_unregister(struct cros_ec_device *ec_dev) +{ + if (ec_dev->pd) + platform_device_unregister(ec_dev->pd); + platform_device_unregister(ec_dev->ec); + + return 0; +} +EXPORT_SYMBOL(cros_ec_unregister); + +#ifdef CONFIG_PM_SLEEP +int cros_ec_suspend(struct cros_ec_device *ec_dev) +{ + struct device *dev = ec_dev->dev; + int ret; + u8 sleep_event; + + sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ? + HOST_SLEEP_EVENT_S3_SUSPEND : + HOST_SLEEP_EVENT_S0IX_SUSPEND; + + ret = cros_ec_sleep_event(ec_dev, sleep_event); + if (ret < 0) + dev_dbg(ec_dev->dev, "Error %d sending suspend event to ec", + ret); + + if (device_may_wakeup(dev)) + ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); + + disable_irq(ec_dev->irq); + ec_dev->was_wake_device = ec_dev->wake_enabled; + ec_dev->suspended = true; + + return 0; +} +EXPORT_SYMBOL(cros_ec_suspend); + +static void cros_ec_report_events_during_suspend(struct cros_ec_device *ec_dev) +{ + while (ec_dev->mkbp_event_supported && + cros_ec_get_next_event(ec_dev, NULL) > 0) + blocking_notifier_call_chain(&ec_dev->event_notifier, + 1, ec_dev); +} + +int cros_ec_resume(struct cros_ec_device *ec_dev) +{ + int ret; + u8 sleep_event; + + ec_dev->suspended = false; + enable_irq(ec_dev->irq); + + sleep_event = (!IS_ENABLED(CONFIG_ACPI) || pm_suspend_via_firmware()) ? + HOST_SLEEP_EVENT_S3_RESUME : + HOST_SLEEP_EVENT_S0IX_RESUME; + + ret = cros_ec_sleep_event(ec_dev, sleep_event); + if (ret < 0) + dev_dbg(ec_dev->dev, "Error %d sending resume event to ec", + ret); + + if (ec_dev->wake_enabled) { + disable_irq_wake(ec_dev->irq); + ec_dev->wake_enabled = 0; + } + /* + * Let the mfd devices know about events that occur during + * suspend. This way the clients know what to do with them. + */ + cros_ec_report_events_during_suspend(ec_dev); + + + return 0; +} +EXPORT_SYMBOL(cros_ec_resume); + +#endif + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS EC core driver"); diff --git a/drivers/platform/chrome/cros_ec_chardev.c b/drivers/platform/chrome/cros_ec_chardev.c new file mode 100644 index 000000000000..74ded441bb50 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_chardev.c @@ -0,0 +1,419 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Miscellaneous character driver for ChromeOS Embedded Controller + * + * Copyright 2014 Google, Inc. + * Copyright 2019 Google LLC + * + * This file is a rework and part of the code is ported from + * drivers/mfd/cros_ec_dev.c that was originally written by + * Bill Richardson. + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/mfd/cros_ec.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/platform_data/cros_ec_chardev.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/uaccess.h> + +#define DRV_NAME "cros-ec-chardev" + +/* Arbitrary bounded size for the event queue */ +#define CROS_MAX_EVENT_LEN PAGE_SIZE + +struct chardev_data { + struct cros_ec_dev *ec_dev; + struct miscdevice misc; +}; + +struct chardev_priv { + struct cros_ec_dev *ec_dev; + struct notifier_block notifier; + wait_queue_head_t wait_event; + unsigned long event_mask; + struct list_head events; + size_t event_len; +}; + +struct ec_event { + struct list_head node; + size_t size; + u8 event_type; + u8 data[0]; +}; + +static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen) +{ + static const char * const current_image_name[] = { + "unknown", "read-only", "read-write", "invalid", + }; + struct ec_response_get_version *resp; + struct cros_ec_command *msg; + int ret; + + msg = kzalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; + msg->insize = sizeof(*resp); + + ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); + if (ret < 0) { + snprintf(str, maxlen, + "Unknown EC version, returned error: %d\n", + msg->result); + goto exit; + } + + resp = (struct ec_response_get_version *)msg->data; + if (resp->current_image >= ARRAY_SIZE(current_image_name)) + resp->current_image = 3; /* invalid */ + + snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION, + resp->version_string_ro, resp->version_string_rw, + current_image_name[resp->current_image]); + + ret = 0; +exit: + kfree(msg); + return ret; +} + +static int cros_ec_chardev_mkbp_event(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *_notify) +{ + struct chardev_priv *priv = container_of(nb, struct chardev_priv, + notifier); + struct cros_ec_device *ec_dev = priv->ec_dev->ec_dev; + struct ec_event *event; + unsigned long event_bit = 1 << ec_dev->event_data.event_type; + int total_size = sizeof(*event) + ec_dev->event_size; + + if (!(event_bit & priv->event_mask) || + (priv->event_len + total_size) > CROS_MAX_EVENT_LEN) + return NOTIFY_DONE; + + event = kzalloc(total_size, GFP_KERNEL); + if (!event) + return NOTIFY_DONE; + + event->size = ec_dev->event_size; + event->event_type = ec_dev->event_data.event_type; + memcpy(event->data, &ec_dev->event_data.data, ec_dev->event_size); + + spin_lock(&priv->wait_event.lock); + list_add_tail(&event->node, &priv->events); + priv->event_len += total_size; + wake_up_locked(&priv->wait_event); + spin_unlock(&priv->wait_event.lock); + + return NOTIFY_OK; +} + +static struct ec_event *cros_ec_chardev_fetch_event(struct chardev_priv *priv, + bool fetch, bool block) +{ + struct ec_event *event; + int err; + + spin_lock(&priv->wait_event.lock); + if (!block && list_empty(&priv->events)) { + event = ERR_PTR(-EWOULDBLOCK); + goto out; + } + + if (!fetch) { + event = NULL; + goto out; + } + + err = wait_event_interruptible_locked(priv->wait_event, + !list_empty(&priv->events)); + if (err) { + event = ERR_PTR(err); + goto out; + } + + event = list_first_entry(&priv->events, struct ec_event, node); + list_del(&event->node); + priv->event_len -= sizeof(*event) + event->size; + +out: + spin_unlock(&priv->wait_event.lock); + return event; +} + +/* + * Device file ops + */ +static int cros_ec_chardev_open(struct inode *inode, struct file *filp) +{ + struct miscdevice *mdev = filp->private_data; + struct cros_ec_dev *ec_dev = dev_get_drvdata(mdev->parent); + struct chardev_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->ec_dev = ec_dev; + filp->private_data = priv; + INIT_LIST_HEAD(&priv->events); + init_waitqueue_head(&priv->wait_event); + nonseekable_open(inode, filp); + + priv->notifier.notifier_call = cros_ec_chardev_mkbp_event; + ret = blocking_notifier_chain_register(&ec_dev->ec_dev->event_notifier, + &priv->notifier); + if (ret) { + dev_err(ec_dev->dev, "failed to register event notifier\n"); + kfree(priv); + } + + return ret; +} + +static __poll_t cros_ec_chardev_poll(struct file *filp, poll_table *wait) +{ + struct chardev_priv *priv = filp->private_data; + + poll_wait(filp, &priv->wait_event, wait); + + if (list_empty(&priv->events)) + return 0; + + return EPOLLIN | EPOLLRDNORM; +} + +static ssize_t cros_ec_chardev_read(struct file *filp, char __user *buffer, + size_t length, loff_t *offset) +{ + char msg[sizeof(struct ec_response_get_version) + + sizeof(CROS_EC_DEV_VERSION)]; + struct chardev_priv *priv = filp->private_data; + struct cros_ec_dev *ec_dev = priv->ec_dev; + size_t count; + int ret; + + if (priv->event_mask) { /* queued MKBP event */ + struct ec_event *event; + + event = cros_ec_chardev_fetch_event(priv, length != 0, + !(filp->f_flags & O_NONBLOCK)); + if (IS_ERR(event)) + return PTR_ERR(event); + /* + * length == 0 is special - no IO is done but we check + * for error conditions. + */ + if (length == 0) + return 0; + + /* The event is 1 byte of type plus the payload */ + count = min(length, event->size + 1); + ret = copy_to_user(buffer, &event->event_type, count); + kfree(event); + if (ret) /* the copy failed */ + return -EFAULT; + *offset = count; + return count; + } + + /* + * Legacy behavior if no event mask is defined + */ + if (*offset != 0) + return 0; + + ret = ec_get_version(ec_dev, msg, sizeof(msg)); + if (ret) + return ret; + + count = min(length, strlen(msg)); + + if (copy_to_user(buffer, msg, count)) + return -EFAULT; + + *offset = count; + return count; +} + +static int cros_ec_chardev_release(struct inode *inode, struct file *filp) +{ + struct chardev_priv *priv = filp->private_data; + struct cros_ec_dev *ec_dev = priv->ec_dev; + struct ec_event *event, *e; + + blocking_notifier_chain_unregister(&ec_dev->ec_dev->event_notifier, + &priv->notifier); + + list_for_each_entry_safe(event, e, &priv->events, node) { + list_del(&event->node); + kfree(event); + } + kfree(priv); + + return 0; +} + +/* + * Ioctls + */ +static long cros_ec_chardev_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg) +{ + struct cros_ec_command *s_cmd; + struct cros_ec_command u_cmd; + long ret; + + if (copy_from_user(&u_cmd, arg, sizeof(u_cmd))) + return -EFAULT; + + if (u_cmd.outsize > EC_MAX_MSG_BYTES || + u_cmd.insize > EC_MAX_MSG_BYTES) + return -EINVAL; + + 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; + } + + if (u_cmd.outsize != s_cmd->outsize || + u_cmd.insize != s_cmd->insize) { + ret = -EINVAL; + 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) + goto exit; + + if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize)) + ret = -EFAULT; +exit: + kfree(s_cmd); + return ret; +} + +static long cros_ec_chardev_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_dev->cmd_readmem) + return -ENOTTY; + + if (copy_from_user(&s_mem, arg, sizeof(s_mem))) + return -EFAULT; + + num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes, + s_mem.buffer); + if (num <= 0) + return num; + + if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem))) + return -EFAULT; + + return num; +} + +static long cros_ec_chardev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct chardev_priv *priv = filp->private_data; + struct cros_ec_dev *ec = priv->ec_dev; + + if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC) + return -ENOTTY; + + switch (cmd) { + case CROS_EC_DEV_IOCXCMD: + return cros_ec_chardev_ioctl_xcmd(ec, (void __user *)arg); + case CROS_EC_DEV_IOCRDMEM: + return cros_ec_chardev_ioctl_readmem(ec, (void __user *)arg); + case CROS_EC_DEV_IOCEVENTMASK: + priv->event_mask = arg; + return 0; + } + + return -ENOTTY; +} + +static const struct file_operations chardev_fops = { + .open = cros_ec_chardev_open, + .poll = cros_ec_chardev_poll, + .read = cros_ec_chardev_read, + .release = cros_ec_chardev_release, + .unlocked_ioctl = cros_ec_chardev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = cros_ec_chardev_ioctl, +#endif +}; + +static int cros_ec_chardev_probe(struct platform_device *pdev) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_platform *ec_platform = dev_get_platdata(ec_dev->dev); + struct chardev_data *data; + + /* Create a char device: we want to create it anew */ + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->ec_dev = ec_dev; + data->misc.minor = MISC_DYNAMIC_MINOR; + data->misc.fops = &chardev_fops; + data->misc.name = ec_platform->ec_name; + data->misc.parent = pdev->dev.parent; + + dev_set_drvdata(&pdev->dev, data); + + return misc_register(&data->misc); +} + +static int cros_ec_chardev_remove(struct platform_device *pdev) +{ + struct chardev_data *data = dev_get_drvdata(&pdev->dev); + + misc_deregister(&data->misc); + + return 0; +} + +static struct platform_driver cros_ec_chardev_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = cros_ec_chardev_probe, + .remove = cros_ec_chardev_remove, +}; + +module_platform_driver(cros_ec_chardev_driver); + +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_AUTHOR("Enric Balletbo i Serra <enric.balletbo@collabora.com>"); +MODULE_DESCRIPTION("ChromeOS EC Miscellaneous Character Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 8ec1cc2889f2..6ae484989d1f 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -8,9 +8,10 @@ #include <linux/delay.h> #include <linux/fs.h> #include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/poll.h> #include <linux/sched.h> diff --git a/drivers/platform/chrome/cros_ec_i2c.c b/drivers/platform/chrome/cros_ec_i2c.c index 61d75395f86d..9bd97bc8454b 100644 --- a/drivers/platform/chrome/cros_ec_i2c.c +++ b/drivers/platform/chrome/cros_ec_i2c.c @@ -9,8 +9,8 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/interrupt.h> -#include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -307,6 +307,13 @@ static int cros_ec_i2c_probe(struct i2c_client *client, return 0; } +static int cros_ec_i2c_remove(struct i2c_client *client) +{ + struct cros_ec_device *ec_dev = i2c_get_clientdata(client); + + return cros_ec_unregister(ec_dev); +} + #ifdef CONFIG_PM_SLEEP static int cros_ec_i2c_suspend(struct device *dev) { @@ -357,6 +364,7 @@ static struct i2c_driver cros_ec_driver = { .pm = &cros_ec_i2c_pm_ops, }, .probe = cros_ec_i2c_probe, + .remove = cros_ec_i2c_remove, .id_table = cros_ec_i2c_id, }; diff --git a/drivers/platform/chrome/cros_ec_ishtp.c b/drivers/platform/chrome/cros_ec_ishtp.c index e504d255d5ce..25ca2c894b4d 100644 --- a/drivers/platform/chrome/cros_ec_ishtp.c +++ b/drivers/platform/chrome/cros_ec_ishtp.c @@ -8,11 +8,10 @@ // (ISH-TP). #include <linux/delay.h> -#include <linux/mfd/core.h> -#include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/intel-ish-client-if.h> /* @@ -707,7 +706,7 @@ static int cros_ec_ishtp_reset(struct ishtp_cl_device *cl_device) */ static int __maybe_unused cros_ec_ishtp_suspend(struct device *device) { - struct ishtp_cl_device *cl_device = dev_get_drvdata(device); + struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device); struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device); struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); @@ -722,7 +721,7 @@ static int __maybe_unused cros_ec_ishtp_suspend(struct device *device) */ static int __maybe_unused cros_ec_ishtp_resume(struct device *device) { - struct ishtp_cl_device *cl_device = dev_get_drvdata(device); + struct ishtp_cl_device *cl_device = ishtp_dev_to_cl_device(device); struct ishtp_cl *cros_ish_cl = ishtp_get_drvdata(cl_device); struct ishtp_cl_data *client_data = ishtp_get_client_data(cros_ish_cl); diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index 609598bbb6c3..c0f2eec35a48 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -9,8 +9,9 @@ #include <linux/fs.h> #include <linux/kobject.h> #include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> #include <linux/module.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/sched.h> #include <linux/types.h> diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c index 2c44c7f3322a..7d10d909435f 100644 --- a/drivers/platform/chrome/cros_ec_lpc.c +++ b/drivers/platform/chrome/cros_ec_lpc.c @@ -16,9 +16,9 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/interrupt.h> -#include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> #include <linux/module.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/printk.h> #include <linux/suspend.h> @@ -421,6 +421,7 @@ static int cros_ec_lpc_probe(struct platform_device *pdev) static int cros_ec_lpc_remove(struct platform_device *pdev) { + struct cros_ec_device *ec_dev = platform_get_drvdata(pdev); struct acpi_device *adev; adev = ACPI_COMPANION(&pdev->dev); @@ -428,7 +429,7 @@ static int cros_ec_lpc_remove(struct platform_device *pdev) acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY, cros_ec_lpc_acpi_notify); - return 0; + return cros_ec_unregister(ec_dev); } static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = { diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index 3d2325197a68..f659f96bda12 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -3,10 +3,11 @@ // // Copyright (C) 2015 Google, Inc -#include <linux/mfd/cros_ec.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/module.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/slab.h> #include <asm/unaligned.h> diff --git a/drivers/platform/chrome/cros_ec_rpmsg.c b/drivers/platform/chrome/cros_ec_rpmsg.c index 5d3fb2abad1d..0c3738c3244d 100644 --- a/drivers/platform/chrome/cros_ec_rpmsg.c +++ b/drivers/platform/chrome/cros_ec_rpmsg.c @@ -6,9 +6,9 @@ #include <linux/delay.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> #include <linux/of.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/rpmsg.h> #include <linux/slab.h> @@ -41,6 +41,7 @@ struct cros_ec_rpmsg { struct rpmsg_device *rpdev; struct completion xfer_ack; struct work_struct host_event_work; + struct rpmsg_endpoint *ept; }; /** @@ -72,7 +73,6 @@ static int cros_ec_pkt_xfer_rpmsg(struct cros_ec_device *ec_dev, struct cros_ec_command *ec_msg) { struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv; - struct rpmsg_device *rpdev = ec_rpmsg->rpdev; struct ec_host_response *response; unsigned long timeout; int len; @@ -85,7 +85,7 @@ static int cros_ec_pkt_xfer_rpmsg(struct cros_ec_device *ec_dev, dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); reinit_completion(&ec_rpmsg->xfer_ack); - ret = rpmsg_send(rpdev->ept, ec_dev->dout, len); + ret = rpmsg_send(ec_rpmsg->ept, ec_dev->dout, len); if (ret) { dev_err(ec_dev->dev, "rpmsg send failed\n"); return ret; @@ -196,11 +196,24 @@ static int cros_ec_rpmsg_callback(struct rpmsg_device *rpdev, void *data, return 0; } +static struct rpmsg_endpoint * +cros_ec_rpmsg_create_ept(struct rpmsg_device *rpdev) +{ + struct rpmsg_channel_info chinfo = {}; + + strscpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE); + chinfo.src = rpdev->src; + chinfo.dst = RPMSG_ADDR_ANY; + + return rpmsg_create_ept(rpdev, cros_ec_rpmsg_callback, NULL, chinfo); +} + static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev) { struct device *dev = &rpdev->dev; struct cros_ec_rpmsg *ec_rpmsg; struct cros_ec_device *ec_dev; + int ret; ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); if (!ec_dev) @@ -225,7 +238,18 @@ static int cros_ec_rpmsg_probe(struct rpmsg_device *rpdev) INIT_WORK(&ec_rpmsg->host_event_work, cros_ec_rpmsg_host_event_function); - return cros_ec_register(ec_dev); + ec_rpmsg->ept = cros_ec_rpmsg_create_ept(rpdev); + if (!ec_rpmsg->ept) + return -ENOMEM; + + ret = cros_ec_register(ec_dev); + if (ret < 0) { + rpmsg_destroy_ept(ec_rpmsg->ept); + cancel_work_sync(&ec_rpmsg->host_event_work); + return ret; + } + + return 0; } static void cros_ec_rpmsg_remove(struct rpmsg_device *rpdev) @@ -233,9 +257,30 @@ static void cros_ec_rpmsg_remove(struct rpmsg_device *rpdev) struct cros_ec_device *ec_dev = dev_get_drvdata(&rpdev->dev); struct cros_ec_rpmsg *ec_rpmsg = ec_dev->priv; + cros_ec_unregister(ec_dev); + rpmsg_destroy_ept(ec_rpmsg->ept); cancel_work_sync(&ec_rpmsg->host_event_work); } +#ifdef CONFIG_PM_SLEEP +static int cros_ec_rpmsg_suspend(struct device *dev) +{ + struct cros_ec_device *ec_dev = dev_get_drvdata(dev); + + return cros_ec_suspend(ec_dev); +} + +static int cros_ec_rpmsg_resume(struct device *dev) +{ + struct cros_ec_device *ec_dev = dev_get_drvdata(dev); + + return cros_ec_resume(ec_dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_rpmsg_pm_ops, cros_ec_rpmsg_suspend, + cros_ec_rpmsg_resume); + static const struct of_device_id cros_ec_rpmsg_of_match[] = { { .compatible = "google,cros-ec-rpmsg", }, { } @@ -246,10 +291,10 @@ static struct rpmsg_driver cros_ec_driver_rpmsg = { .drv = { .name = "cros-ec-rpmsg", .of_match_table = cros_ec_rpmsg_of_match, + .pm = &cros_ec_rpmsg_pm_ops, }, .probe = cros_ec_rpmsg_probe, .remove = cros_ec_rpmsg_remove, - .callback = cros_ec_rpmsg_callback, }; module_rpmsg_driver(cros_ec_driver_rpmsg); diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c index 006a8ff64057..a831bd5a5b2f 100644 --- a/drivers/platform/chrome/cros_ec_spi.c +++ b/drivers/platform/chrome/cros_ec_spi.c @@ -6,9 +6,9 @@ #include <linux/delay.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> #include <linux/of.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/spi/spi.h> @@ -706,7 +706,7 @@ static int cros_ec_spi_devm_high_pri_alloc(struct device *dev, struct cros_ec_spi *ec_spi) { struct sched_param sched_priority = { - .sched_priority = MAX_RT_PRIO - 1, + .sched_priority = MAX_RT_PRIO / 2, }; int err; @@ -785,6 +785,13 @@ static int cros_ec_spi_probe(struct spi_device *spi) return 0; } +static int cros_ec_spi_remove(struct spi_device *spi) +{ + struct cros_ec_device *ec_dev = spi_get_drvdata(spi); + + return cros_ec_unregister(ec_dev); +} + #ifdef CONFIG_PM_SLEEP static int cros_ec_spi_suspend(struct device *dev) { @@ -823,6 +830,7 @@ static struct spi_driver cros_ec_driver_spi = { .pm = &cros_ec_spi_pm_ops, }, .probe = cros_ec_spi_probe, + .remove = cros_ec_spi_remove, .id_table = cros_ec_spi_id, }; diff --git a/drivers/platform/chrome/cros_ec_sysfs.c b/drivers/platform/chrome/cros_ec_sysfs.c index 3edb237bf8ed..74d36b8d4f46 100644 --- a/drivers/platform/chrome/cros_ec_sysfs.c +++ b/drivers/platform/chrome/cros_ec_sysfs.c @@ -9,8 +9,9 @@ #include <linux/fs.h> #include <linux/kobject.h> #include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> #include <linux/module.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/printk.h> #include <linux/slab.h> diff --git a/drivers/platform/chrome/cros_ec_trace.c b/drivers/platform/chrome/cros_ec_trace.c index 0a76412095a9..6f80ff4532ae 100644 --- a/drivers/platform/chrome/cros_ec_trace.c +++ b/drivers/platform/chrome/cros_ec_trace.c @@ -6,7 +6,7 @@ #define TRACE_SYMBOL(a) {a, #a} // Generate the list using the following script: -// sed -n 's/^#define \(EC_CMD_[[:alnum:]_]*\)\s.*/\tTRACE_SYMBOL(\1), \\/p' include/linux/mfd/cros_ec_commands.h +// sed -n 's/^#define \(EC_CMD_[[:alnum:]_]*\)\s.*/\tTRACE_SYMBOL(\1), \\/p' include/linux/platform_data/cros_ec_commands.h #define EC_CMDS \ TRACE_SYMBOL(EC_CMD_PROTO_VERSION), \ TRACE_SYMBOL(EC_CMD_HELLO), \ diff --git a/drivers/platform/chrome/cros_ec_trace.h b/drivers/platform/chrome/cros_ec_trace.h index 7ae3b89c78b9..0dd4df30fa89 100644 --- a/drivers/platform/chrome/cros_ec_trace.h +++ b/drivers/platform/chrome/cros_ec_trace.h @@ -11,8 +11,10 @@ #if !defined(_CROS_EC_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) #define _CROS_EC_TRACE_H_ +#include <linux/bits.h> #include <linux/types.h> -#include <linux/mfd/cros_ec.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/tracepoint.h> diff --git a/drivers/platform/chrome/cros_ec_vbc.c b/drivers/platform/chrome/cros_ec_vbc.c index 2aaefed87eb4..f11a1283e5c8 100644 --- a/drivers/platform/chrome/cros_ec_vbc.c +++ b/drivers/platform/chrome/cros_ec_vbc.c @@ -7,8 +7,9 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> #include <linux/module.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/slab.h> #define DRV_NAME "cros-ec-vbc" diff --git a/drivers/platform/chrome/cros_usbpd_logger.c b/drivers/platform/chrome/cros_usbpd_logger.c index 7c7b267626a0..2430e8b82810 100644 --- a/drivers/platform/chrome/cros_usbpd_logger.c +++ b/drivers/platform/chrome/cros_usbpd_logger.c @@ -6,10 +6,11 @@ */ #include <linux/ktime.h> -#include <linux/math64.h> #include <linux/mfd/cros_ec.h> -#include <linux/mfd/cros_ec_commands.h> +#include <linux/math64.h> #include <linux/module.h> +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/rtc.h> @@ -209,6 +210,9 @@ static int cros_usbpd_logger_probe(struct platform_device *pd) /* Retrieve PD event logs periodically */ INIT_DELAYED_WORK(&logger->log_work, cros_usbpd_log_check); logger->log_workqueue = create_singlethread_workqueue("cros_usbpd_log"); + if (!logger->log_workqueue) + return -ENOMEM; + queue_delayed_work(logger->log_workqueue, &logger->log_work, CROS_USBPD_LOG_UPDATE_DELAY); diff --git a/drivers/platform/chrome/wilco_ec/telemetry.c b/drivers/platform/chrome/wilco_ec/telemetry.c index 94cdc166c840..b9d03c33d8dc 100644 --- a/drivers/platform/chrome/wilco_ec/telemetry.c +++ b/drivers/platform/chrome/wilco_ec/telemetry.c @@ -9,7 +9,7 @@ * the OS sends a command to the EC via a write() to a char device, * and can read the response with a read(). The write() request is * verified by the driver to ensure that it is performing only one - * of the whitelisted commands, and that no extraneous data is + * of the allowlisted commands, and that no extraneous data is * being transmitted to the EC. The response is passed directly * back to the reader with no modification. * @@ -59,21 +59,10 @@ static DEFINE_IDA(telem_ida); #define WILCO_EC_TELEM_GET_TEMP_INFO 0x95 #define WILCO_EC_TELEM_GET_TEMP_READ 0x2C #define WILCO_EC_TELEM_GET_BATT_EXT_INFO 0x07 +#define WILCO_EC_TELEM_GET_BATT_PPID_INFO 0x8A #define TELEM_ARGS_SIZE_MAX 30 -/** - * struct wilco_ec_telem_request - Telemetry command and arguments sent to EC. - * @command: One of WILCO_EC_TELEM_GET_* command codes. - * @reserved: Must be 0. - * @args: The first N bytes are one of telem_args_get_* structs, the rest is 0. - */ -struct wilco_ec_telem_request { - u8 command; - u8 reserved; - u8 args[TELEM_ARGS_SIZE_MAX]; -} __packed; - /* * The following telem_args_get_* structs are embedded within the |args| field * of wilco_ec_telem_request. @@ -122,6 +111,32 @@ struct telem_args_get_batt_ext_info { u8 var_args[5]; } __packed; +struct telem_args_get_batt_ppid_info { + u8 always1; /* Should always be 1 */ +} __packed; + +/** + * struct wilco_ec_telem_request - Telemetry command and arguments sent to EC. + * @command: One of WILCO_EC_TELEM_GET_* command codes. + * @reserved: Must be 0. + * @args: The first N bytes are one of telem_args_get_* structs, the rest is 0. + */ +struct wilco_ec_telem_request { + u8 command; + u8 reserved; + union { + u8 buf[TELEM_ARGS_SIZE_MAX]; + struct telem_args_get_log get_log; + struct telem_args_get_version get_version; + struct telem_args_get_fan_info get_fan_info; + struct telem_args_get_diag_info get_diag_info; + struct telem_args_get_temp_info get_temp_info; + struct telem_args_get_temp_read get_temp_read; + struct telem_args_get_batt_ext_info get_batt_ext_info; + struct telem_args_get_batt_ppid_info get_batt_ppid_info; + } args; +} __packed; + /** * check_telem_request() - Ensure that a request from userspace is valid. * @rq: Request buffer copied from userspace. @@ -133,7 +148,7 @@ struct telem_args_get_batt_ext_info { * We do not want to allow userspace to send arbitrary telemetry commands to * the EC. Therefore we check to ensure that * 1. The request follows the format of struct wilco_ec_telem_request. - * 2. The supplied command code is one of the whitelisted commands. + * 2. The supplied command code is one of the allowlisted commands. * 3. The request only contains the necessary data for the header and arguments. */ static int check_telem_request(struct wilco_ec_telem_request *rq, @@ -146,25 +161,31 @@ static int check_telem_request(struct wilco_ec_telem_request *rq, switch (rq->command) { case WILCO_EC_TELEM_GET_LOG: - max_size += sizeof(struct telem_args_get_log); + max_size += sizeof(rq->args.get_log); break; case WILCO_EC_TELEM_GET_VERSION: - max_size += sizeof(struct telem_args_get_version); + max_size += sizeof(rq->args.get_version); break; case WILCO_EC_TELEM_GET_FAN_INFO: - max_size += sizeof(struct telem_args_get_fan_info); + max_size += sizeof(rq->args.get_fan_info); break; case WILCO_EC_TELEM_GET_DIAG_INFO: - max_size += sizeof(struct telem_args_get_diag_info); + max_size += sizeof(rq->args.get_diag_info); break; case WILCO_EC_TELEM_GET_TEMP_INFO: - max_size += sizeof(struct telem_args_get_temp_info); + max_size += sizeof(rq->args.get_temp_info); break; case WILCO_EC_TELEM_GET_TEMP_READ: - max_size += sizeof(struct telem_args_get_temp_read); + max_size += sizeof(rq->args.get_temp_read); break; case WILCO_EC_TELEM_GET_BATT_EXT_INFO: - max_size += sizeof(struct telem_args_get_batt_ext_info); + max_size += sizeof(rq->args.get_batt_ext_info); + break; + case WILCO_EC_TELEM_GET_BATT_PPID_INFO: + if (rq->args.get_batt_ppid_info.always1 != 1) + return -EINVAL; + + max_size += sizeof(rq->args.get_batt_ppid_info); break; default: return -EINVAL; @@ -250,6 +271,7 @@ static ssize_t telem_write(struct file *filp, const char __user *buf, if (count > sizeof(sess_data->request)) return -EMSGSIZE; + memset(&sess_data->request, 0, sizeof(sess_data->request)); if (copy_from_user(&sess_data->request, buf, count)) return -EFAULT; ret = check_telem_request(&sess_data->request, count); diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index f85a1b9d129b..706207d192ae 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -642,11 +642,8 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) priv->irq = pdata->irq; } else { priv->irq = platform_get_irq(pdev, 0); - if (priv->irq < 0) { - dev_err(&pdev->dev, "Failed to get platform irq: %d\n", - priv->irq); + if (priv->irq < 0) return priv->irq; - } } priv->regmap = pdata->regmap; diff --git a/drivers/platform/olpc/olpc-xo175-ec.c b/drivers/platform/olpc/olpc-xo175-ec.c index 48d6f0d87583..83ed1fbf73cf 100644 --- a/drivers/platform/olpc/olpc-xo175-ec.c +++ b/drivers/platform/olpc/olpc-xo175-ec.c @@ -736,6 +736,12 @@ static const struct of_device_id olpc_xo175_ec_of_match[] = { }; MODULE_DEVICE_TABLE(of, olpc_xo175_ec_of_match); +static const struct spi_device_id olpc_xo175_ec_id_table[] = { + { "xo1.75-ec", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, olpc_xo175_ec_id_table); + static struct spi_driver olpc_xo175_ec_spi_driver = { .driver = { .name = "olpc-xo175-ec", diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1b67bb578f9f..ae21d08c65e8 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -674,6 +674,7 @@ config EEEPC_LAPTOP config ASUS_WMI tristate "ASUS WMI Driver" depends on ACPI_WMI + depends on ACPI_BATTERY depends on INPUT depends on HWMON depends on BACKLIGHT_CLASS_DEVICE diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 62b54e137231..60c18f21588d 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1881,52 +1881,17 @@ static int __init acer_wmi_enable_rf_button(void) return status; } -#define ACER_WMID_ACCEL_HID "BST0001" - -static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level, - void *ctx, void **retval) -{ - struct acpi_device *dev; - - if (!strcmp(ctx, "SENR")) { - if (acpi_bus_get_device(ah, &dev)) - return AE_OK; - if (strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev))) - return AE_OK; - } else - return AE_OK; - - *(acpi_handle *)retval = ah; - - return AE_CTRL_TERMINATE; -} - -static int __init acer_wmi_get_handle(const char *name, const char *prop, - acpi_handle *ah) -{ - acpi_status status; - acpi_handle handle; - - BUG_ON(!name || !ah); - - handle = NULL; - status = acpi_get_devices(prop, acer_wmi_get_handle_cb, - (void *)name, &handle); - if (ACPI_SUCCESS(status) && handle) { - *ah = handle; - return 0; - } else { - return -ENODEV; - } -} - static int __init acer_wmi_accel_setup(void) { + struct acpi_device *adev; int err; - err = acer_wmi_get_handle("SENR", ACER_WMID_ACCEL_HID, &gsensor_handle); - if (err) - return err; + adev = acpi_dev_get_first_match_dev("BST0001", NULL, -1); + if (!adev) + return -ENODEV; + + gsensor_handle = acpi_device_handle(adev); + acpi_dev_put(adev); interface->capability |= ACER_CAP_ACCEL; diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 2ebde0174937..b361c73636a4 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -402,6 +402,15 @@ static const struct dmi_system_id asus_quirks[] = { }, .driver_data = &quirk_asus_forceals, }, + { + .callback = dmi_matched, + .ident = "ASUSTeK COMPUTER INC. UX430UNR", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "UX430UNR"), + }, + .driver_data = &quirk_asus_forceals, + }, {}, }; diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index ca28d27dae63..821b08e01635 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -26,6 +26,7 @@ #include <linux/rfkill.h> #include <linux/pci.h> #include <linux/pci_hotplug.h> +#include <linux/power_supply.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/debugfs.h> @@ -35,6 +36,8 @@ #include <linux/thermal.h> #include <linux/acpi.h> #include <linux/dmi.h> + +#include <acpi/battery.h> #include <acpi/video.h> #include "asus-wmi.h" @@ -65,6 +68,9 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_MFUN 0x13 #define ASUS_FAN_SFUN_READ 0x06 #define ASUS_FAN_SFUN_WRITE 0x07 + +/* Based on standard hwmon pwmX_enable values */ +#define ASUS_FAN_CTRL_FULLSPEED 0 #define ASUS_FAN_CTRL_MANUAL 1 #define ASUS_FAN_CTRL_AUTO 2 @@ -120,7 +126,7 @@ struct agfn_args { } __packed; /* struct used for calling fan read and write methods */ -struct fan_args { +struct agfn_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 */ @@ -148,6 +154,12 @@ struct asus_rfkill { u32 dev_id; }; +enum fan_type { + FAN_TYPE_NONE = 0, + FAN_TYPE_AGFN, /* deprecated on newer platforms */ + FAN_TYPE_SPEC83, /* starting in Spec 8.3, use CPU_FAN_CTRL */ +}; + struct asus_wmi { int dsts_id; int spec; @@ -178,14 +190,17 @@ 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; + enum fan_type fan_type; + int fan_pwm_mode; + int agfn_pwm; bool fan_boost_mode_available; u8 fan_boost_mode_mask; u8 fan_boost_mode; + // The RSOC controls the maximum charging percentage. + bool battery_rsoc_available; + struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; struct mutex wmi_lock; @@ -292,12 +307,11 @@ static int asus_wmi_evaluate_method_agfn(const struct acpi_buffer args) * 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.pointer = kmemdup(args.pointer, 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); @@ -331,7 +345,6 @@ static int asus_wmi_get_devstate_bits(struct asus_wmi *asus, int err; err = asus_wmi_get_devstate(asus, dev_id, &retval); - if (err < 0) return err; @@ -352,6 +365,105 @@ static int asus_wmi_get_devstate_simple(struct asus_wmi *asus, u32 dev_id) ASUS_WMI_DSTS_STATUS_BIT); } +static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id) +{ + u32 retval; + int status = asus_wmi_get_devstate(asus, dev_id, &retval); + + return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT); +} + +/* Battery ********************************************************************/ + +/* The battery maximum charging percentage */ +static int charge_end_threshold; + +static ssize_t charge_control_end_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value, ret, rv; + + ret = kstrtouint(buf, 10, &value); + if (ret) + return ret; + + if (value < 0 || value > 100) + return -EINVAL; + + ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, value, &rv); + if (ret) + return ret; + + if (rv != 1) + return -EIO; + + /* There isn't any method in the DSDT to read the threshold, so we + * save the threshold. + */ + charge_end_threshold = value; + return count; +} + +static ssize_t charge_control_end_threshold_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", charge_end_threshold); +} + +static DEVICE_ATTR_RW(charge_control_end_threshold); + +static int asus_wmi_battery_add(struct power_supply *battery) +{ + /* The WMI method does not provide a way to specific a battery, so we + * just assume it is the first battery. + */ + if (strcmp(battery->desc->name, "BAT0") != 0) + return -ENODEV; + + if (device_create_file(&battery->dev, + &dev_attr_charge_control_end_threshold)) + return -ENODEV; + + /* The charge threshold is only reset when the system is power cycled, + * and we can't get the current threshold so let set it to 100% when + * a battery is added. + */ + asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, 100, NULL); + charge_end_threshold = 100; + + return 0; +} + +static int asus_wmi_battery_remove(struct power_supply *battery) +{ + device_remove_file(&battery->dev, + &dev_attr_charge_control_end_threshold); + return 0; +} + +static struct acpi_battery_hook battery_hook = { + .add_battery = asus_wmi_battery_add, + .remove_battery = asus_wmi_battery_remove, + .name = "ASUS Battery Extension", +}; + +static void asus_wmi_battery_init(struct asus_wmi *asus) +{ + asus->battery_rsoc_available = false; + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_RSOC)) { + asus->battery_rsoc_available = true; + battery_hook_register(&battery_hook); + } +} + +static void asus_wmi_battery_exit(struct asus_wmi *asus) +{ + if (asus->battery_rsoc_available) + battery_hook_unregister(&battery_hook); +} + /* LEDs ***********************************************************************/ /* @@ -427,15 +539,14 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) if (retval == 0x8000) retval = 0; - if (retval >= 0) { - if (level) - *level = retval & 0x7F; - if (env) - *env = (retval >> 8) & 0x7F; - retval = 0; - } + if (retval < 0) + return retval; - return retval; + if (level) + *level = retval & 0x7F; + if (env) + *env = (retval >> 8) & 0x7F; + return 0; } static void do_kbd_led_set(struct led_classdev *led_cdev, int value) @@ -446,12 +557,7 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) asus = container_of(led_cdev, struct asus_wmi, kbd_led); max_level = asus->kbd_led.max_brightness; - if (value > max_level) - value = max_level; - else if (value < 0) - value = 0; - - asus->kbd_led_wk = value; + asus->kbd_led_wk = clamp_val(value, 0, max_level); kbd_led_update(asus); } @@ -481,7 +587,6 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) asus = container_of(led_cdev, struct asus_wmi, kbd_led); retval = kbd_led_read(asus, &value, NULL); - if (retval < 0) return retval; @@ -497,15 +602,6 @@ static int wlan_led_unknown_state(struct asus_wmi *asus) return result & ASUS_WMI_DSTS_UNKNOWN_BIT; } -static int wlan_led_presence(struct asus_wmi *asus) -{ - u32 result; - - asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result); - - return result & ASUS_WMI_DSTS_PRESENCE_BIT; -} - static void wlan_led_update(struct work_struct *work) { int ctrl_param; @@ -572,15 +668,6 @@ static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev) return result & ASUS_WMI_DSTS_LIGHTBAR_MASK; } -static int lightbar_led_presence(struct asus_wmi *asus) -{ - u32 result; - - asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result); - - return result & ASUS_WMI_DSTS_PRESENCE_BIT; -} - static void asus_wmi_led_exit(struct asus_wmi *asus) { if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) @@ -631,7 +718,8 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - if (wlan_led_presence(asus) && (asus->driver->quirks->wapf > 0)) { + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED) + && (asus->driver->quirks->wapf > 0)) { INIT_WORK(&asus->wlan_led_work, wlan_led_update); asus->wlan_led.name = "asus::wlan"; @@ -648,7 +736,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - if (lightbar_led_presence(asus)) { + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_LIGHTBAR)) { INIT_WORK(&asus->lightbar_led_work, lightbar_led_update); asus->lightbar_led.name = "asus::lightbar"; @@ -771,16 +859,14 @@ static int asus_register_rfkill_notifier(struct asus_wmi *asus, char *node) acpi_handle handle; status = acpi_get_handle(NULL, node, &handle); - - if (ACPI_SUCCESS(status)) { - status = acpi_install_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - asus_rfkill_notify, asus); - if (ACPI_FAILURE(status)) - pr_warn("Failed to register notify on %s\n", node); - } else + if (ACPI_FAILURE(status)) return -ENODEV; + status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + asus_rfkill_notify, asus); + if (ACPI_FAILURE(status)) + pr_warn("Failed to register notify on %s\n", node); + return 0; } @@ -790,15 +876,13 @@ static void asus_unregister_rfkill_notifier(struct asus_wmi *asus, char *node) acpi_handle handle; status = acpi_get_handle(NULL, node, &handle); + if (ACPI_FAILURE(status)) + return; - if (ACPI_SUCCESS(status)) { - status = acpi_remove_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - asus_rfkill_notify); - if (ACPI_FAILURE(status)) - pr_err("Error removing rfkill notify handler %s\n", - node); - } + status = acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + asus_rfkill_notify); + if (ACPI_FAILURE(status)) + pr_err("Error removing rfkill notify handler %s\n", node); } static int asus_get_adapter_status(struct hotplug_slot *hotplug_slot, @@ -1126,10 +1210,10 @@ static void asus_wmi_set_als(void) /* Hwmon device ***************************************************************/ -static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan, +static int asus_agfn_fan_speed_read(struct asus_wmi *asus, int fan, int *speed) { - struct fan_args args = { + struct agfn_fan_args args = { .agfn.len = sizeof(args), .agfn.mfun = ASUS_FAN_MFUN, .agfn.sfun = ASUS_FAN_SFUN_READ, @@ -1153,10 +1237,10 @@ static int asus_hwmon_agfn_fan_speed_read(struct asus_wmi *asus, int fan, return 0; } -static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan, +static int asus_agfn_fan_speed_write(struct asus_wmi *asus, int fan, int *speed) { - struct fan_args args = { + struct agfn_fan_args args = { .agfn.len = sizeof(args), .agfn.mfun = ASUS_FAN_MFUN, .agfn.sfun = ASUS_FAN_SFUN_WRITE, @@ -1176,7 +1260,7 @@ static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan, return -ENXIO; if (speed && fan == 1) - asus->asus_hwmon_pwm = *speed; + asus->agfn_pwm = *speed; return 0; } @@ -1185,77 +1269,60 @@ static int asus_hwmon_agfn_fan_speed_write(struct asus_wmi *asus, int fan, * 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) +static bool asus_wmi_has_agfn_fan(struct asus_wmi *asus) { int status; - int speed = 0; + int speed; + u32 value; - *num_fans = 0; + status = asus_agfn_fan_speed_read(asus, 1, &speed); + if (status != 0) + return false; - status = asus_hwmon_agfn_fan_speed_read(asus, 1, &speed); - if (!status) - *num_fans = 1; + status = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value); + if (status != 0) + return false; - return 0; + /* + * We need to find a better way, probably using sfun, + * bits or spec ... + * Currently we disable it if: + * - ASUS_WMI_UNSUPPORTED_METHOD is returned + * - reverved bits are non-zero + * - sfun and presence bit are not set + */ + return !(value == ASUS_WMI_UNSUPPORTED_METHOD || value & 0xFFF80000 + || (!asus->sfun && !(value & ASUS_WMI_DSTS_PRESENCE_BIT))); } -static int asus_hwmon_fan_set_auto(struct asus_wmi *asus) +static int asus_fan_set_auto(struct asus_wmi *asus) { int status; + u32 retval; - status = asus_hwmon_agfn_fan_speed_write(asus, 0, NULL); - if (status) - return -ENXIO; - - asus->asus_hwmon_fan_manual_mode = false; - - return 0; -} + switch (asus->fan_type) { + case FAN_TYPE_SPEC83: + status = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL, + 0, &retval); + if (status) + return status; -static int asus_hwmon_fan_rpm_show(struct device *dev, int fan) -{ - struct asus_wmi *asus = dev_get_drvdata(dev); - int value; - int ret; + if (retval != 1) + return -EIO; + break; - /* no speed readable on manual mode */ - if (asus->asus_hwmon_fan_manual_mode) - return -ENXIO; + case FAN_TYPE_AGFN: + status = asus_agfn_fan_speed_write(asus, 0, NULL); + if (status) + return -ENXIO; + break; - ret = asus_hwmon_agfn_fan_speed_read(asus, fan+1, &value); - if (ret) { - pr_warn("reading fan speed failed: %d\n", ret); + default: return -ENXIO; } - return value; -} -static void asus_hwmon_pwm_show(struct asus_wmi *asus, int fan, int *value) -{ - int err; - - 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; - - *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; - } + return 0; } static ssize_t pwm1_show(struct device *dev, @@ -1263,9 +1330,33 @@ static ssize_t pwm1_show(struct device *dev, char *buf) { struct asus_wmi *asus = dev_get_drvdata(dev); + int err; int value; - asus_hwmon_pwm_show(asus, 0, &value); + /* If we already set a value then just return it */ + if (asus->agfn_pwm >= 0) + return sprintf(buf, "%d\n", asus->agfn_pwm); + + /* + * If we haven't set already set a value through the AGFN interface, + * we read a current value through the (now-deprecated) FAN_CTRL device. + */ + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_FAN_CTRL, &value); + if (err < 0) + return err; + + 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; + } return sprintf(buf, "%d\n", value); } @@ -1279,17 +1370,16 @@ static ssize_t pwm1_store(struct device *dev, 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); + state = asus_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; + asus->fan_pwm_mode = ASUS_FAN_CTRL_MANUAL; return count; } @@ -1298,10 +1388,37 @@ static ssize_t fan1_input_show(struct device *dev, struct device_attribute *attr, char *buf) { - int value = asus_hwmon_fan_rpm_show(dev, 0); + struct asus_wmi *asus = dev_get_drvdata(dev); + int value; + int ret; - return sprintf(buf, "%d\n", value < 0 ? -1 : value*100); + switch (asus->fan_type) { + case FAN_TYPE_SPEC83: + ret = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL, + &value); + if (ret < 0) + return ret; + + value &= 0xffff; + break; + + case FAN_TYPE_AGFN: + /* no speed readable on manual mode */ + if (asus->fan_pwm_mode == ASUS_FAN_CTRL_MANUAL) + return -ENXIO; + + ret = asus_agfn_fan_speed_read(asus, 1, &value); + if (ret) { + pr_warn("reading fan speed failed: %d\n", ret); + return -ENXIO; + } + break; + + default: + return -ENXIO; + } + return sprintf(buf, "%d\n", value < 0 ? -1 : value*100); } static ssize_t pwm1_enable_show(struct device *dev, @@ -1310,10 +1427,16 @@ static ssize_t pwm1_enable_show(struct device *dev, { 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); + /* + * Just read back the cached pwm mode. + * + * For the CPU_FAN device, the spec indicates that we should be + * able to read the device status and consult bit 19 to see if we + * are in Full On or Automatic mode. However, this does not work + * in practice on X532FL at least (the bit is always 0) and there's + * also nothing in the DSDT to indicate that this behaviour exists. + */ + return sprintf(buf, "%d\n", asus->fan_pwm_mode); } static ssize_t pwm1_enable_store(struct device *dev, @@ -1323,21 +1446,50 @@ static ssize_t pwm1_enable_store(struct device *dev, struct asus_wmi *asus = dev_get_drvdata(dev); int status = 0; int state; + int value; int ret; + u32 retval; 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 (asus->fan_type == FAN_TYPE_SPEC83) { + switch (state) { /* standard documented hwmon values */ + case ASUS_FAN_CTRL_FULLSPEED: + value = 1; + break; + case ASUS_FAN_CTRL_AUTO: + value = 0; + break; + default: + return -EINVAL; + } - if (status) - return status; + ret = asus_wmi_set_devstate(ASUS_WMI_DEVID_CPU_FAN_CTRL, + value, &retval); + if (ret) + return ret; + + if (retval != 1) + return -EIO; + } else if (asus->fan_type == FAN_TYPE_AGFN) { + switch (state) { + case ASUS_FAN_CTRL_MANUAL: + break; + + case ASUS_FAN_CTRL_AUTO: + status = asus_fan_set_auto(asus); + if (status) + return status; + break; + default: + return -EINVAL; + } + } + + asus->fan_pwm_mode = state; return count; } @@ -1357,7 +1509,6 @@ static ssize_t asus_hwmon_temp1(struct device *dev, int err; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_THERMAL_CTRL, &value); - if (err < 0) return err; @@ -1390,59 +1541,33 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj, { struct device *dev = container_of(kobj, struct device, kobj); struct asus_wmi *asus = dev_get_drvdata(dev->parent); - 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 + if (attr == &dev_attr_pwm1.attr) { + if (asus->fan_type != FAN_TYPE_AGFN) + return 0; + } else 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 (asus->fan_type == FAN_TYPE_NONE) + return 0; + } else if (attr == &dev_attr_temp1_input.attr) { + int err = asus_wmi_get_devstate(asus, + ASUS_WMI_DEVID_THERMAL_CTRL, + &value); - if (err < 0 && fan_attr == -1) + if (err < 0) return 0; /* can't return negative here */ - } - if (dev_id == ASUS_WMI_DEVID_FAN_CTRL) { - /* - * We need to find a better way, probably using sfun, - * bits or spec ... - * Currently we disable it if: - * - ASUS_WMI_UNSUPPORTED_METHOD is returned - * - reverved bits are non-zero - * - sfun and presence bit are not set - */ - 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 the temperature value in deci-Kelvin is near the absolute * zero temperature, something is clearly wrong */ if (value == 0 || value == 1) - ok = false; - } else if (fan_attr <= asus->asus_hwmon_num_fans && fan_attr != -1) { - ok = true; - } else { - ok = false; + return 0; } - return ok ? attr->mode : 0; + return attr->mode; } static const struct attribute_group hwmon_attribute_group = { @@ -1468,20 +1593,19 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus) static int asus_wmi_fan_init(struct asus_wmi *asus) { - int status; + asus->fan_type = FAN_TYPE_NONE; + asus->agfn_pwm = -1; - asus->asus_hwmon_pwm = -1; - asus->asus_hwmon_num_fans = -1; - asus->asus_hwmon_fan_manual_mode = false; + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL)) + asus->fan_type = FAN_TYPE_SPEC83; + else if (asus_wmi_has_agfn_fan(asus)) + asus->fan_type = FAN_TYPE_AGFN; - 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; - } + if (asus->fan_type == FAN_TYPE_NONE) + return -ENODEV; - pr_info("Number of fans: %d\n", asus->asus_hwmon_num_fans); + asus_fan_set_auto(asus); + asus->fan_pwm_mode = ASUS_FAN_CTRL_AUTO; return 0; } @@ -1523,7 +1647,6 @@ static int fan_boost_mode_write(struct asus_wmi *asus) pr_info("Set fan boost mode: %u\n", value); err = asus_wmi_set_devstate(ASUS_WMI_DEVID_FAN_BOOST_MODE, value, &retval); - if (err) { pr_warn("Failed to set fan boost mode: %d\n", err); return err; @@ -1606,6 +1729,7 @@ static DEVICE_ATTR_RW(fan_boost_mode); static int read_backlight_power(struct asus_wmi *asus) { int ret; + if (asus->driver->quirks->store_backlight_power) ret = !asus->driver->panel_power; else @@ -1624,7 +1748,6 @@ static int read_brightness_max(struct asus_wmi *asus) int err; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval); - if (err < 0) return err; @@ -1644,7 +1767,6 @@ static int read_brightness(struct backlight_device *bd) int err; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_BRIGHTNESS, &retval); - if (err < 0) return err; @@ -1734,7 +1856,6 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus) return max; power = read_backlight_power(asus); - if (power == -ENODEV) power = FB_BLANK_UNBLANK; else if (power < 0) @@ -1900,7 +2021,6 @@ static void asus_wmi_notify(u32 value, void *context) for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) { code = asus_wmi_get_event_code(value); - if (code < 0) { pr_warn("Failed to get notify code: %d\n", code); return; @@ -1929,7 +2049,6 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) for (i = 0; i < WMI_EVENT_QUEUE_SIZE + 1; i++) { code = asus_wmi_get_event_code(WMI_EVENT_VALUE_ATK); - if (code < 0) { pr_warn("Failed to get event during flush: %d\n", code); return code; @@ -1945,32 +2064,25 @@ static int asus_wmi_notify_queue_flush(struct asus_wmi *asus) /* Sysfs **********************************************************************/ -static int parse_arg(const char *buf, unsigned long count, int *val) -{ - if (!count) - return 0; - if (sscanf(buf, "%i", val) != 1) - return -EINVAL; - return count; -} - static ssize_t store_sys_wmi(struct asus_wmi *asus, int devid, const char *buf, size_t count) { u32 retval; - int rv, err, value; + int err, value; value = asus_wmi_get_devstate_simple(asus, devid); if (value < 0) return value; - rv = parse_arg(buf, count, &value); - err = asus_wmi_set_devstate(devid, value, &retval); + err = kstrtoint(buf, 0, &value); + if (err) + return err; + err = asus_wmi_set_devstate(devid, value, &retval); if (err < 0) return err; - return rv; + return count; } static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf) @@ -2019,8 +2131,10 @@ static ssize_t cpufv_store(struct device *dev, struct device_attribute *attr, { int value, rv; - if (!count || sscanf(buf, "%i", &value) != 1) - return -EINVAL; + rv = kstrtoint(buf, 0, &value); + if (rv) + return rv; + if (value < 0 || value > 2) return -EINVAL; @@ -2181,7 +2295,6 @@ static int show_dsts(struct seq_file *m, void *data) u32 retval = -1; err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval); - if (err < 0) return err; @@ -2198,7 +2311,6 @@ static int show_devs(struct seq_file *m, void *data) err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param, &retval); - if (err < 0) return err; @@ -2334,7 +2446,6 @@ static int asus_wmi_add(struct platform_device *pdev) 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) @@ -2392,6 +2503,8 @@ static int asus_wmi_add(struct platform_device *pdev) goto fail_wmi_handler; } + asus_wmi_battery_init(asus); + asus_wmi_debugfs_init(asus); return 0; @@ -2426,7 +2539,8 @@ static int asus_wmi_remove(struct platform_device *device) asus_wmi_rfkill_exit(asus); asus_wmi_debugfs_exit(asus); asus_wmi_sysfs_exit(asus->platform_device); - asus_hwmon_fan_set_auto(asus); + asus_fan_set_auto(asus); + asus_wmi_battery_exit(asus); kfree(asus); return 0; diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 09dfa6f48a1a..ab610376fdad 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -226,7 +226,7 @@ static const unsigned char pwm_lookup_table[256] = { /* General access */ static u8 ec_read_u8(u8 addr) { - u8 value; + u8 value = 0; ec_read(addr, &value); return value; } diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index acc653f9c16f..6669db2555fb 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -311,11 +311,13 @@ static const struct key_entry dell_wmi_keymap_type_0011[] = { { KE_IGNORE, 0xfff1, { KEY_RESERVED } }, /* Keyboard backlight level changed */ - { KE_IGNORE, 0x01e1, { KEY_RESERVED } }, - { KE_IGNORE, 0x02ea, { KEY_RESERVED } }, - { KE_IGNORE, 0x02eb, { KEY_RESERVED } }, - { KE_IGNORE, 0x02ec, { KEY_RESERVED } }, - { KE_IGNORE, 0x02f6, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_OFF_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_ON_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_AUTO_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_AUTO_25_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_AUTO_50_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_AUTO_75_TOKEN, { KEY_RESERVED } }, + { KE_IGNORE, KBD_LED_AUTO_100_TOKEN, { KEY_RESERVED } }, }; static void dell_wmi_process_key(struct wmi_device *wdev, int type, int code) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 2521e45280b8..6bcbbb375401 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -502,6 +502,17 @@ static DEVICE_ATTR_RO(dock); static DEVICE_ATTR_RO(tablet); static DEVICE_ATTR_RW(postcode); +static struct attribute *hp_wmi_attrs[] = { + &dev_attr_display.attr, + &dev_attr_hddtemp.attr, + &dev_attr_als.attr, + &dev_attr_dock.attr, + &dev_attr_tablet.attr, + &dev_attr_postcode.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hp_wmi); + static void hp_wmi_notify(u32 value, void *context) { struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -678,16 +689,6 @@ static void hp_wmi_input_destroy(void) input_unregister_device(hp_wmi_input_dev); } -static void cleanup_sysfs(struct platform_device *device) -{ - device_remove_file(&device->dev, &dev_attr_display); - device_remove_file(&device->dev, &dev_attr_hddtemp); - device_remove_file(&device->dev, &dev_attr_als); - device_remove_file(&device->dev, &dev_attr_dock); - device_remove_file(&device->dev, &dev_attr_tablet); - device_remove_file(&device->dev, &dev_attr_postcode); -} - static int __init hp_wmi_rfkill_setup(struct platform_device *device) { int err, wireless; @@ -858,8 +859,6 @@ fail: static int __init hp_wmi_bios_setup(struct platform_device *device) { - int err; - /* clear detected rfkill devices */ wifi_rfkill = NULL; bluetooth_rfkill = NULL; @@ -869,35 +868,12 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) if (hp_wmi_rfkill_setup(device)) hp_wmi_rfkill2_setup(device); - err = device_create_file(&device->dev, &dev_attr_display); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_hddtemp); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_als); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_dock); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_tablet); - if (err) - goto add_sysfs_error; - err = device_create_file(&device->dev, &dev_attr_postcode); - if (err) - goto add_sysfs_error; return 0; - -add_sysfs_error: - cleanup_sysfs(device); - return err; } static int __exit hp_wmi_bios_remove(struct platform_device *device) { int i; - cleanup_sysfs(device); for (i = 0; i < rfkill2_count; i++) { rfkill_unregister(rfkill2[i].rfkill); @@ -966,6 +942,7 @@ static struct platform_driver hp_wmi_driver = { .driver = { .name = "hp-wmi", .pm = &hp_wmi_pm_ops, + .dev_groups = hp_wmi_groups, }, .remove = __exit_p(hp_wmi_bios_remove), }; diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 7a2747455237..799cbe2ffcf3 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -240,6 +240,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = { AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), AXIS_DMI_MATCH("HPZBook15", "HP ZBook 15", x_inverted), + AXIS_DMI_MATCH("HPZBook17G5", "HP ZBook 17 G5", x_inverted), AXIS_DMI_MATCH("HPZBook17", "HP ZBook 17", xy_swap_yz_inverted), { NULL, } /* Laptop models without axis info (yet): diff --git a/drivers/platform/x86/i2c-multi-instantiate.c b/drivers/platform/x86/i2c-multi-instantiate.c index 197d8a192721..ea68f6ed66ae 100644 --- a/drivers/platform/x86/i2c-multi-instantiate.c +++ b/drivers/platform/x86/i2c-multi-instantiate.c @@ -81,9 +81,7 @@ static int i2c_multi_inst_probe(struct platform_device *pdev) if (ret < 0) return ret; - multi = devm_kmalloc(dev, - offsetof(struct i2c_multi_inst_data, clients[ret]), - GFP_KERNEL); + multi = devm_kmalloc(dev, struct_size(multi, clients, ret), GFP_KERNEL); if (!multi) return -ENOMEM; @@ -92,7 +90,7 @@ static int i2c_multi_inst_probe(struct platform_device *pdev) for (i = 0; i < multi->num_clients && inst_data[i].type; i++) { memset(&board_info, 0, sizeof(board_info)); strlcpy(board_info.type, inst_data[i].type, I2C_NAME_SIZE); - snprintf(name, sizeof(name), "%s-%s.%d", match->id, + snprintf(name, sizeof(name), "%s-%s.%d", dev_name(dev), inst_data[i].type, i); board_info.dev_name = name; switch (inst_data[i].flags & IRQ_RESOURCE_TYPE) { diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index bc0d55a59015..ef6d4bd77b1a 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -253,35 +253,45 @@ static void intel_button_array_enable(struct device *device, bool enable) static int intel_hid_pm_prepare(struct device *device) { - struct intel_hid_priv *priv = dev_get_drvdata(device); + if (device_may_wakeup(device)) { + struct intel_hid_priv *priv = dev_get_drvdata(device); - priv->wakeup_mode = true; + priv->wakeup_mode = true; + } return 0; } +static void intel_hid_pm_complete(struct device *device) +{ + struct intel_hid_priv *priv = dev_get_drvdata(device); + + priv->wakeup_mode = false; +} + static int intel_hid_pl_suspend_handler(struct device *device) { - if (pm_suspend_via_firmware()) { + intel_button_array_enable(device, false); + + if (!pm_suspend_no_platform()) intel_hid_set_enable(device, false); - intel_button_array_enable(device, false); - } + return 0; } static int intel_hid_pl_resume_handler(struct device *device) { - struct intel_hid_priv *priv = dev_get_drvdata(device); + intel_hid_pm_complete(device); - priv->wakeup_mode = false; - if (pm_resume_via_firmware()) { + if (!pm_suspend_no_platform()) intel_hid_set_enable(device, true); - intel_button_array_enable(device, true); - } + + intel_button_array_enable(device, true); return 0; } static const struct dev_pm_ops intel_hid_pl_pm_ops = { .prepare = intel_hid_pm_prepare, + .complete = intel_hid_pm_complete, .freeze = intel_hid_pl_suspend_handler, .thaw = intel_hid_pl_resume_handler, .restore = intel_hid_pl_resume_handler, @@ -491,6 +501,12 @@ static int intel_hid_probe(struct platform_device *device) } device_init_wakeup(&device->dev, true); + /* + * In order for system wakeup to work, the EC GPE has to be marked as + * a wakeup one, so do that here (this setting will persist, but it has + * no effect until the wakeup mask is set for the EC GPE). + */ + acpi_ec_mark_gpe_for_wake(); return 0; err_remove_notify: diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c index a0d0cecff55f..b74932307d69 100644 --- a/drivers/platform/x86/intel-vbtn.c +++ b/drivers/platform/x86/intel-vbtn.c @@ -176,6 +176,12 @@ static int intel_vbtn_probe(struct platform_device *device) return -EBUSY; device_init_wakeup(&device->dev, true); + /* + * In order for system wakeup to work, the EC GPE has to be marked as + * a wakeup one, so do that here (this setting will persist, but it has + * no effect until the wakeup mask is set for the EC GPE). + */ + acpi_ec_mark_gpe_for_wake(); return 0; } @@ -195,22 +201,30 @@ static int intel_vbtn_remove(struct platform_device *device) static int intel_vbtn_pm_prepare(struct device *dev) { - struct intel_vbtn_priv *priv = dev_get_drvdata(dev); + if (device_may_wakeup(dev)) { + struct intel_vbtn_priv *priv = dev_get_drvdata(dev); - priv->wakeup_mode = true; + priv->wakeup_mode = true; + } return 0; } -static int intel_vbtn_pm_resume(struct device *dev) +static void intel_vbtn_pm_complete(struct device *dev) { struct intel_vbtn_priv *priv = dev_get_drvdata(dev); priv->wakeup_mode = false; +} + +static int intel_vbtn_pm_resume(struct device *dev) +{ + intel_vbtn_pm_complete(dev); return 0; } static const struct dev_pm_ops intel_vbtn_pm_ops = { .prepare = intel_vbtn_pm_prepare, + .complete = intel_vbtn_pm_complete, .resume = intel_vbtn_pm_resume, .restore = intel_vbtn_pm_resume, .thaw = intel_vbtn_pm_resume, diff --git a/drivers/platform/x86/intel_bxtwc_tmu.c b/drivers/platform/x86/intel_bxtwc_tmu.c index 951c105bafc1..7ccf583649e6 100644 --- a/drivers/platform/x86/intel_bxtwc_tmu.c +++ b/drivers/platform/x86/intel_bxtwc_tmu.c @@ -60,11 +60,8 @@ static int bxt_wcove_tmu_probe(struct platform_device *pdev) wctmu->regmap = pmic->regmap; irq = platform_get_irq(pdev, 0); - - if (irq < 0) { - dev_err(&pdev->dev, "invalid irq %d\n", irq); + if (irq < 0) return irq; - } regmap_irq_chip = pmic->irq_chip_data_tmu; virq = regmap_irq_get_virq(regmap_irq_chip, irq); diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe.c index 4fbdff48a4b5..1d5d877b9582 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe.c @@ -34,7 +34,6 @@ enum { INT33FE_NODE_MAX17047, INT33FE_NODE_PI3USB30532, INT33FE_NODE_DISPLAYPORT, - INT33FE_NODE_ROLE_SWITCH, INT33FE_NODE_USB_CONNECTOR, INT33FE_NODE_MAX, }; @@ -45,7 +44,6 @@ struct cht_int33fe_data { struct i2c_client *pi3usb30532; struct fwnode_handle *dp; - struct fwnode_handle *mux; }; static const struct software_node nodes[]; @@ -139,46 +137,10 @@ static const struct software_node nodes[] = { { "max17047", NULL, max17047_props }, { "pi3usb30532" }, { "displayport" }, - { "usb-role-switch" }, { "connector", &nodes[0], usb_connector_props, usb_connector_refs }, { } }; -static int cht_int33fe_setup_mux(struct cht_int33fe_data *data) -{ - struct fwnode_handle *fwnode; - struct device *dev; - struct device *p; - - fwnode = software_node_fwnode(&nodes[INT33FE_NODE_ROLE_SWITCH]); - if (!fwnode) - return -ENODEV; - - /* First finding the platform device */ - p = bus_find_device_by_name(&platform_bus_type, NULL, - "intel_xhci_usb_sw"); - if (!p) - return -EPROBE_DEFER; - - /* Then the mux child device */ - dev = device_find_child_by_name(p, "intel_xhci_usb_sw-role-switch"); - put_device(p); - if (!dev) - return -EPROBE_DEFER; - - /* If there already is a node for the mux, using that one. */ - if (dev->fwnode) - fwnode_remove_software_node(fwnode); - else - dev->fwnode = fwnode; - - data->mux = fwnode_handle_get(dev->fwnode); - put_device(dev); - mux_ref.node = to_software_node(data->mux); - - return 0; -} - static int cht_int33fe_setup_dp(struct cht_int33fe_data *data) { struct fwnode_handle *fwnode; @@ -211,10 +173,9 @@ static void cht_int33fe_remove_nodes(struct cht_int33fe_data *data) { software_node_unregister_nodes(nodes); - if (data->mux) { - fwnode_handle_put(data->mux); + if (mux_ref.node) { + fwnode_handle_put(software_node_fwnode(mux_ref.node)); mux_ref.node = NULL; - data->mux = NULL; } if (data->dp) { @@ -235,14 +196,16 @@ static int cht_int33fe_add_nodes(struct cht_int33fe_data *data) /* The devices that are not created in this driver need extra steps. */ /* - * There is no ACPI device node for the USB role mux, so we need to find - * the mux device and assign our node directly to it. That means we - * depend on the mux driver. This function will return -PROBE_DEFER - * until the mux device is registered. + * There is no ACPI device node for the USB role mux, so we need to wait + * until the mux driver has created software node for the mux device. + * It means we depend on the mux driver. This function will return + * -EPROBE_DEFER until the mux device is registered. */ - ret = cht_int33fe_setup_mux(data); - if (ret) + mux_ref.node = software_node_find_by_name(NULL, "intel-xhci-usb-sw"); + if (!mux_ref.node) { + ret = -EPROBE_DEFER; goto err_remove_nodes; + } /* * The DP connector does have ACPI device node. In this case we can just diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index d9542c661ddc..af233b7b77f2 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -122,7 +122,7 @@ static irqreturn_t int0002_irq(int irq, void *data) generic_handle_irq(irq_find_mapping(chip->irq.domain, GPE0A_PME_B0_VIRT_GPIO_PIN)); - pm_system_wakeup(); + pm_wakeup_hard_event(chip->parent); return IRQ_HANDLED; } @@ -144,6 +144,7 @@ static struct irq_chip int0002_cht_irqchip = { * No set_wake, on CHT the IRQ is typically shared with the ACPI SCI * and we don't want to mess with the ACPI SCI irq settings. */ + .flags = IRQCHIP_SKIP_SET_WAKE, }; static const struct x86_cpu_id int0002_cpu_ids[] = { @@ -152,6 +153,13 @@ static const struct x86_cpu_id int0002_cpu_ids[] = { {} }; +static void int0002_init_irq_valid_mask(struct gpio_chip *chip, + unsigned long *valid_mask, + unsigned int ngpios) +{ + bitmap_clear(valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN); +} + static int int0002_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -166,10 +174,8 @@ static int int0002_probe(struct platform_device *pdev) return -ENODEV; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "Error getting IRQ: %d\n", irq); + if (irq < 0) return irq; - } chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -184,7 +190,7 @@ static int int0002_probe(struct platform_device *pdev) chip->direction_output = int0002_gpio_direction_output; chip->base = -1; chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1; - chip->irq.need_valid_mask = true; + chip->irq.init_valid_mask = int0002_init_irq_valid_mask; ret = devm_gpiochip_add_data(&pdev->dev, chip, NULL); if (ret) { @@ -192,8 +198,6 @@ static int int0002_probe(struct platform_device *pdev) return ret; } - bitmap_clear(chip->irq.valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN); - /* * We manually request the irq here instead of passing a flow-handler * to gpiochip_set_chained_irqchip, because the irq is shared. @@ -216,6 +220,13 @@ static int int0002_probe(struct platform_device *pdev) gpiochip_set_chained_irqchip(chip, irq_chip, irq, NULL); + device_init_wakeup(dev, true); + return 0; +} + +static int int0002_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, false); return 0; } @@ -231,6 +242,7 @@ static struct platform_driver int0002_driver = { .acpi_match_table = int0002_acpi_ids, }, .probe = int0002_probe, + .remove = int0002_remove, }; module_platform_driver(int0002_driver); diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index 235c0b89f824..94a008efb09b 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -806,12 +806,13 @@ static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) #endif /* CONFIG_DEBUG_FS */ static const struct x86_cpu_id intel_pmc_core_ids[] = { - INTEL_CPU_FAM6(SKYLAKE_MOBILE, spt_reg_map), - INTEL_CPU_FAM6(SKYLAKE_DESKTOP, spt_reg_map), - INTEL_CPU_FAM6(KABYLAKE_MOBILE, spt_reg_map), - INTEL_CPU_FAM6(KABYLAKE_DESKTOP, spt_reg_map), - INTEL_CPU_FAM6(CANNONLAKE_MOBILE, cnp_reg_map), - INTEL_CPU_FAM6(ICELAKE_MOBILE, icl_reg_map), + INTEL_CPU_FAM6(SKYLAKE_L, spt_reg_map), + INTEL_CPU_FAM6(SKYLAKE, spt_reg_map), + INTEL_CPU_FAM6(KABYLAKE_L, spt_reg_map), + INTEL_CPU_FAM6(KABYLAKE, spt_reg_map), + INTEL_CPU_FAM6(CANNONLAKE_L, cnp_reg_map), + INTEL_CPU_FAM6(ICELAKE_L, icl_reg_map), + INTEL_CPU_FAM6(ICELAKE_NNPI, icl_reg_map), {} }; @@ -877,10 +878,14 @@ static int pmc_core_probe(struct platform_device *pdev) if (pmcdev->map == &spt_reg_map && !pci_dev_present(pmc_pci_ids)) pmcdev->map = &cnp_reg_map; - if (lpit_read_residency_count_address(&slp_s0_addr)) + if (lpit_read_residency_count_address(&slp_s0_addr)) { pmcdev->base_addr = PMC_BASE_ADDR_DEFAULT; - else + + if (page_is_ram(PHYS_PFN(pmcdev->base_addr))) + return -ENODEV; + } else { pmcdev->base_addr = slp_s0_addr - pmcdev->map->slp_s0_offset; + } pmcdev->regbase = ioremap(pmcdev->base_addr, pmcdev->map->regmap_length); diff --git a/drivers/platform/x86/intel_pmc_core_pltdrv.c b/drivers/platform/x86/intel_pmc_core_pltdrv.c index a8754a6db1b8..6fe829f30997 100644 --- a/drivers/platform/x86/intel_pmc_core_pltdrv.c +++ b/drivers/platform/x86/intel_pmc_core_pltdrv.c @@ -18,8 +18,16 @@ #include <asm/cpu_device_id.h> #include <asm/intel-family.h> +static void intel_pmc_core_release(struct device *dev) +{ + /* Nothing to do. */ +} + static struct platform_device pmc_core_device = { .name = "intel_pmc_core", + .dev = { + .release = intel_pmc_core_release, + }, }; /* @@ -30,12 +38,12 @@ static struct platform_device pmc_core_device = { * other list may grow, but this list should not. */ static const struct x86_cpu_id intel_pmc_core_platform_ids[] = { - INTEL_CPU_FAM6(SKYLAKE_MOBILE, pmc_core_device), - INTEL_CPU_FAM6(SKYLAKE_DESKTOP, pmc_core_device), - INTEL_CPU_FAM6(KABYLAKE_MOBILE, pmc_core_device), - INTEL_CPU_FAM6(KABYLAKE_DESKTOP, pmc_core_device), - INTEL_CPU_FAM6(CANNONLAKE_MOBILE, pmc_core_device), - INTEL_CPU_FAM6(ICELAKE_MOBILE, pmc_core_device), + INTEL_CPU_FAM6(SKYLAKE_L, pmc_core_device), + INTEL_CPU_FAM6(SKYLAKE, pmc_core_device), + INTEL_CPU_FAM6(KABYLAKE_L, pmc_core_device), + INTEL_CPU_FAM6(KABYLAKE, pmc_core_device), + INTEL_CPU_FAM6(CANNONLAKE_L, pmc_core_device), + INTEL_CPU_FAM6(ICELAKE_L, pmc_core_device), {} }; MODULE_DEVICE_TABLE(x86cpu, intel_pmc_core_platform_ids); diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index 55037ff258f8..5c1da2bb1435 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -936,10 +936,8 @@ static int ipc_plat_probe(struct platform_device *pdev) spin_lock_init(&ipcdev.gcr_lock); ipcdev.irq = platform_get_irq(pdev, 0); - if (ipcdev.irq < 0) { - dev_err(&pdev->dev, "Failed to get irq\n"); + if (ipcdev.irq < 0) return -EINVAL; - } ret = ipc_plat_get_res(pdev); if (ret) { diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c index 68d75391db57..3de5a3c66529 100644 --- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c @@ -29,6 +29,8 @@ static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX]; static int punit_msr_white_list[] = { MSR_TURBO_RATIO_LIMIT, MSR_CONFIG_TDP_CONTROL, + MSR_TURBO_RATIO_LIMIT1, + MSR_TURBO_RATIO_LIMIT2, }; struct isst_valid_cmd_ranges { diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c index f7266a115a08..ad8c7c0df4d9 100644 --- a/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c +++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c @@ -132,11 +132,9 @@ static void isst_if_remove(struct pci_dev *pdev) static int __maybe_unused isst_if_suspend(struct device *device) { - struct pci_dev *pdev = to_pci_dev(device); - struct isst_if_device *punit_dev; + struct isst_if_device *punit_dev = dev_get_drvdata(device); int i; - punit_dev = pci_get_drvdata(pdev); for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i) punit_dev->range_0[i] = readl(punit_dev->punit_mmio + mmio_range[0].beg + 4 * i); @@ -149,11 +147,9 @@ static int __maybe_unused isst_if_suspend(struct device *device) static int __maybe_unused isst_if_resume(struct device *device) { - struct pci_dev *pdev = to_pci_dev(device); - struct isst_if_device *punit_dev; + struct isst_if_device *punit_dev = dev_get_drvdata(device); int i; - punit_dev = pci_get_drvdata(pdev); for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i) writel(punit_dev->range_0[i], punit_dev->punit_mmio + mmio_range[0].beg + 4 * i); diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index b0d3110ae378..48b112b4f0b0 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -32,6 +32,8 @@ #define APU2_GPIO_REG_LED3 AMD_FCH_GPIO_REG_GPIO59_DEVSLP1 #define APU2_GPIO_REG_MODESW AMD_FCH_GPIO_REG_GPIO32_GE1 #define APU2_GPIO_REG_SIMSWAP AMD_FCH_GPIO_REG_GPIO33_GE2 +#define APU2_GPIO_REG_MPCIE2 AMD_FCH_GPIO_REG_GPIO59_DEVSLP0 +#define APU2_GPIO_REG_MPCIE3 AMD_FCH_GPIO_REG_GPIO51 /* order in which the gpio lines are defined in the register list */ #define APU2_GPIO_LINE_LED1 0 @@ -39,6 +41,8 @@ #define APU2_GPIO_LINE_LED3 2 #define APU2_GPIO_LINE_MODESW 3 #define APU2_GPIO_LINE_SIMSWAP 4 +#define APU2_GPIO_LINE_MPCIE2 5 +#define APU2_GPIO_LINE_MPCIE3 6 /* gpio device */ @@ -48,6 +52,8 @@ static int apu2_gpio_regs[] = { [APU2_GPIO_LINE_LED3] = APU2_GPIO_REG_LED3, [APU2_GPIO_LINE_MODESW] = APU2_GPIO_REG_MODESW, [APU2_GPIO_LINE_SIMSWAP] = APU2_GPIO_REG_SIMSWAP, + [APU2_GPIO_LINE_MPCIE2] = APU2_GPIO_REG_MPCIE2, + [APU2_GPIO_LINE_MPCIE3] = APU2_GPIO_REG_MPCIE3, }; static const char * const apu2_gpio_names[] = { @@ -56,6 +62,8 @@ static const char * const apu2_gpio_names[] = { [APU2_GPIO_LINE_LED3] = "front-led3", [APU2_GPIO_LINE_MODESW] = "front-button", [APU2_GPIO_LINE_SIMSWAP] = "simswap", + [APU2_GPIO_LINE_MPCIE2] = "mpcie2_reset", + [APU2_GPIO_LINE_MPCIE3] = "mpcie3_reset", }; static const struct amd_fch_gpio_pdata board_apu2 = { @@ -69,7 +77,8 @@ static const struct amd_fch_gpio_pdata board_apu2 = { static const struct gpio_led apu2_leds[] = { { .name = "apu:green:1" }, { .name = "apu:green:2" }, - { .name = "apu:green:3" } + { .name = "apu:green:3" }, + { .name = "apu:simswap" }, }; static const struct gpio_led_platform_data apu2_leds_pdata = { @@ -86,6 +95,8 @@ static struct gpiod_lookup_table gpios_led_table = { NULL, 1, GPIO_ACTIVE_LOW), GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_LINE_LED3, NULL, 2, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX(AMD_FCH_GPIO_DRIVER_NAME, APU2_GPIO_REG_SIMSWAP, + NULL, 3, GPIO_ACTIVE_LOW), } }; @@ -93,7 +104,7 @@ static struct gpiod_lookup_table gpios_led_table = { static struct gpio_keys_button apu2_keys_buttons[] = { { - .code = KEY_SETUP, + .code = KEY_RESTART, .active_low = 1, .desc = "front button", .type = EV_KEY, @@ -255,6 +266,4 @@ MODULE_DESCRIPTION("PC Engines APUv2/APUv3 board GPIO/LED/keys driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(dmi, apu_gpio_dmi_table); MODULE_ALIAS("platform:pcengines-apuv2"); -MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME); -MODULE_SOFTDEP("pre: platform:leds-gpio"); -MODULE_SOFTDEP("pre: platform:gpio_keys_polled"); +MODULE_SOFTDEP("pre: platform:" AMD_FCH_GPIO_DRIVER_NAME " platform:leds-gpio platform:gpio_keys_polled"); diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c index aa53648a2214..07d1b911e72f 100644 --- a/drivers/platform/x86/pmc_atom.c +++ b/drivers/platform/x86/pmc_atom.c @@ -415,6 +415,20 @@ static const struct dmi_system_id critclk_systems[] = { DMI_MATCH(DMI_BOARD_NAME, "CB6363"), }, }, + { + .ident = "SIMATIC IPC227E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"), + DMI_MATCH(DMI_PRODUCT_VERSION, "6ES7647-8B"), + }, + }, + { + .ident = "SIMATIC IPC277E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SIEMENS AG"), + DMI_MATCH(DMI_PRODUCT_VERSION, "6AV7882-0"), + }, + }, { /*sentinel*/ } }; diff --git a/drivers/platform/x86/surfacepro3_button.c b/drivers/platform/x86/surfacepro3_button.c index 47c6d000465a..ec515223f654 100644 --- a/drivers/platform/x86/surfacepro3_button.c +++ b/drivers/platform/x86/surfacepro3_button.c @@ -20,6 +20,12 @@ #define SURFACE_BUTTON_OBJ_NAME "VGBI" #define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons" +#define MSHW0040_DSM_REVISION 0x01 +#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision +static const guid_t MSHW0040_DSM_UUID = + GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, + 0x49, 0x80, 0x35); + #define SURFACE_BUTTON_NOTIFY_TABLET_MODE 0xc8 #define SURFACE_BUTTON_NOTIFY_PRESS_POWER 0xc6 @@ -142,6 +148,44 @@ static int surface_button_resume(struct device *dev) } #endif +/* + * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device + * ID (MSHW0040) for the power/volume buttons. Make sure this is the right + * device by checking for the _DSM method and OEM Platform Revision. + * + * Returns true if the driver should bind to this device, i.e. the device is + * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1. + */ +static bool surface_button_check_MSHW0040(struct acpi_device *dev) +{ + acpi_handle handle = dev->handle; + union acpi_object *result; + u64 oem_platform_rev = 0; // valid revisions are nonzero + + // get OEM platform revision + result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, + MSHW0040_DSM_REVISION, + MSHW0040_DSM_GET_OMPR, + NULL, ACPI_TYPE_INTEGER); + + /* + * If evaluating the _DSM fails, the method is not present. This means + * that we have either MSHW0028 or MSHW0040 on Pro 4 or Book 1, so we + * should use this driver. We use revision 0 indicating it is + * unavailable. + */ + + if (result) { + oem_platform_rev = result->integer.value; + ACPI_FREE(result); + } + + dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev); + + return oem_platform_rev == 0; +} + + static int surface_button_add(struct acpi_device *device) { struct surface_button *button; @@ -154,6 +198,9 @@ static int surface_button_add(struct acpi_device *device) strlen(SURFACE_BUTTON_OBJ_NAME))) return -ENODEV; + if (!surface_button_check_MSHW0040(device)) + return -ENODEV; + button = kzalloc(sizeof(struct surface_button), GFP_KERNEL); if (!button) return -ENOMEM; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 7bde4640ef34..da794dcfdd92 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3647,22 +3647,19 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) goto err_exit; /* Set up key map */ - hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE, - GFP_KERNEL); - if (!hotkey_keycode_map) { - pr_err("failed to allocate memory for key map\n"); - res = -ENOMEM; - goto err_exit; - } - keymap_id = tpacpi_check_quirks(tpacpi_keymap_qtable, ARRAY_SIZE(tpacpi_keymap_qtable)); BUG_ON(keymap_id >= ARRAY_SIZE(tpacpi_keymaps)); dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "using keymap number %lu\n", keymap_id); - memcpy(hotkey_keycode_map, &tpacpi_keymaps[keymap_id], - TPACPI_HOTKEY_MAP_SIZE); + hotkey_keycode_map = kmemdup(&tpacpi_keymaps[keymap_id], + TPACPI_HOTKEY_MAP_SIZE, GFP_KERNEL); + if (!hotkey_keycode_map) { + pr_err("failed to allocate memory for key map\n"); + res = -ENOMEM; + goto err_exit; + } input_set_capability(tpacpi_inputdev, EV_MSC, MSC_SCAN); tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE; @@ -9714,6 +9711,107 @@ static struct ibm_struct battery_driver_data = { .exit = tpacpi_battery_exit, }; +/************************************************************************* + * LCD Shadow subdriver, for the Lenovo PrivacyGuard feature + */ + +static int lcdshadow_state; + +static int lcdshadow_on_off(bool state) +{ + acpi_handle set_shadow_handle; + int output; + + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "SSSS", &set_shadow_handle))) { + pr_warn("Thinkpad ACPI has no %s interface.\n", "SSSS"); + return -EIO; + } + + if (!acpi_evalf(set_shadow_handle, &output, NULL, "dd", (int)state)) + return -EIO; + + lcdshadow_state = state; + return 0; +} + +static int lcdshadow_set(bool on) +{ + if (lcdshadow_state < 0) + return lcdshadow_state; + if (lcdshadow_state == on) + return 0; + return lcdshadow_on_off(on); +} + +static int tpacpi_lcdshadow_init(struct ibm_init_struct *iibm) +{ + acpi_handle get_shadow_handle; + int output; + + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GSSS", &get_shadow_handle))) { + lcdshadow_state = -ENODEV; + return 0; + } + + if (!acpi_evalf(get_shadow_handle, &output, NULL, "dd", 0)) { + lcdshadow_state = -EIO; + return -EIO; + } + if (!(output & 0x10000)) { + lcdshadow_state = -ENODEV; + return 0; + } + lcdshadow_state = output & 0x1; + + return 0; +} + +static void lcdshadow_resume(void) +{ + if (lcdshadow_state >= 0) + lcdshadow_on_off(lcdshadow_state); +} + +static int lcdshadow_read(struct seq_file *m) +{ + if (lcdshadow_state < 0) { + seq_puts(m, "status:\t\tnot supported\n"); + } else { + seq_printf(m, "status:\t\t%d\n", lcdshadow_state); + seq_puts(m, "commands:\t0, 1\n"); + } + + return 0; +} + +static int lcdshadow_write(char *buf) +{ + char *cmd; + int state = -1; + + if (lcdshadow_state < 0) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "0") == 0) + state = 0; + else if (strlencmp(cmd, "1") == 0) + state = 1; + } + + if (state == -1) + return -EINVAL; + + return lcdshadow_set(state); +} + +static struct ibm_struct lcdshadow_driver_data = { + .name = "lcdshadow", + .resume = lcdshadow_resume, + .read = lcdshadow_read, + .write = lcdshadow_write, +}; + /**************************************************************************** **************************************************************************** * @@ -10195,6 +10293,10 @@ static struct ibm_init_struct ibms_init[] __initdata = { .init = tpacpi_battery_init, .data = &battery_driver_data, }, + { + .init = tpacpi_lcdshadow_init, + .data = &lcdshadow_driver_data, + }, }; static int __init set_ibm_param(const char *val, const struct kernel_param *kp) diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 4370e4add83a..1c7d8324ff5c 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -136,6 +136,22 @@ static const struct ts_dmi_data chuwi_vi10_data = { .properties = chuwi_vi10_props, }; +static const struct property_entry chuwi_surbook_mini_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 88), + PROPERTY_ENTRY_U32("touchscreen-min-y", 13), + PROPERTY_ENTRY_U32("touchscreen-size-x", 2040), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1524), + PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-chuwi-surbook-mini.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + { } +}; + +static const struct ts_dmi_data chuwi_surbook_mini_data = { + .acpi_name = "MSSL1680:00", + .properties = chuwi_surbook_mini_props, +}; + static const struct property_entry connect_tablet9_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 9), PROPERTY_ENTRY_U32("touchscreen-min-y", 10), @@ -230,6 +246,24 @@ static const struct ts_dmi_data gp_electronic_t701_data = { .properties = gp_electronic_t701_props, }; +static const struct property_entry irbis_tw90_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1720), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1138), + PROPERTY_ENTRY_U32("touchscreen-min-x", 8), + PROPERTY_ENTRY_U32("touchscreen-min-y", 14), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-irbis_tw90.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data irbis_tw90_data = { + .acpi_name = "MSSL1680:00", + .properties = irbis_tw90_props, +}; + static const struct property_entry itworks_tw891_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 1), PROPERTY_ENTRY_U32("touchscreen-min-y", 5), @@ -647,6 +681,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Chuwi Surbook Mini (CWI540) */ + .driver_data = (void *)&chuwi_surbook_mini_data, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"), + DMI_MATCH(DMI_PRODUCT_NAME, "C3W6_AP108_4G"), + }, + }, + { /* Connect Tablet 9 */ .driver_data = (void *)&connect_tablet9_data, .matches = { @@ -709,6 +751,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Irbis TW90 */ + .driver_data = (void *)&irbis_tw90_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "IRBIS"), + DMI_MATCH(DMI_PRODUCT_NAME, "TW90"), + }, + }, + { /* I.T.Works TW891 */ .driver_data = (void *)&itworks_tw891_data, .matches = { @@ -884,6 +934,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Trekstor Primebook C11B (same touchscreen as the C11) */ + .driver_data = (void *)&trekstor_primebook_c11_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TREKSTOR"), + DMI_MATCH(DMI_PRODUCT_NAME, "PRIMEBOOK C11B"), + }, + }, + { /* Trekstor Primebook C13 */ .driver_data = (void *)&trekstor_primebook_c13_data, .matches = { diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 784cea8572c2..59e9aa0f9643 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -340,9 +340,7 @@ static acpi_status __query_block(struct wmi_block *wblock, u8 instance, * expensive, but have no corresponding WCxx method. So we * should not fail if this happens. */ - if (acpi_has_method(handle, wc_method)) - wc_status = acpi_execute_simple_method(handle, - wc_method, 1); + wc_status = acpi_execute_simple_method(handle, wc_method, 1); } strcpy(method, "WQ"); |