Merge branch 'acpi-pm'

* acpi-pm:
  PM / core: Drop run_wake flag from struct dev_pm_info
  PCI / PM: Simplify device wakeup settings code
  PCI / PM: Drop pme_interrupt flag from struct pci_dev
  ACPI / PM: Consolidate device wakeup settings code
  ACPI / PM: Drop run_wake from struct acpi_device_wakeup_flags
  ACPI / sleep: EC-based wakeup from suspend-to-idle on recent systems
  platform: x86: intel-hid: Wake up the system from suspend-to-idle
  platform: x86: intel-vbtn: Wake up the system from suspend-to-idle
  ACPI / PM: Ignore spurious SCI wakeups from suspend-to-idle
  platform/x86: Add driver for ACPI INT0002 Virtual GPIO device
  PCI / PM: Restore PME Enable if skipping wakeup setup
  PM / sleep: Print timing information if debug is enabled
  ACPI / PM: Clean up device wakeup enable/disable code
  ACPI / PM: Change log level of wakeup-related message
  USB / PCI / PM: Allow the PCI core to do the resume cleanup
  ACPI / PM: Run wakeup notify handlers synchronously

Conflicts:
	drivers/base/power/main.c
此提交包含在:
Rafael J. Wysocki
2017-07-03 14:23:09 +02:00
當前提交 8f8e5c3e27
共有 35 個檔案被更改,包括 669 行新增301 行删除

查看文件

@@ -794,6 +794,25 @@ config INTEL_CHT_INT33FE
This driver instantiates i2c-clients for these, so that standard
i2c drivers for these chips can bind to the them.
config INTEL_INT0002_VGPIO
tristate "Intel ACPI INT0002 Virtual GPIO driver"
depends on GPIOLIB && ACPI
select GPIOLIB_IRQCHIP
---help---
Some peripherals on Bay Trail and Cherry Trail platforms signal a
Power Management Event (PME) to the Power Management Controller (PMC)
to wakeup the system. When this happens software needs to explicitly
clear the PME bus 0 status bit in the GPE0a_STS register to avoid an
IRQ storm on IRQ 9.
This is modelled in ACPI through the INT0002 ACPI device, which is
called a "Virtual GPIO controller" in ACPI because it defines the
event handler to call when the PME triggers through _AEI and _L02
methods as would be done for a real GPIO interrupt in ACPI.
To compile this driver as a module, choose M here: the module will
be called intel_int0002_vgpio.
config INTEL_HID_EVENT
tristate "INTEL HID Event"
depends on ACPI

查看文件

@@ -46,6 +46,7 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o
obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o

查看文件

@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/input/sparse-keymap.h>
#include <linux/acpi.h>
#include <linux/suspend.h>
#include <acpi/acpi_bus.h>
MODULE_LICENSE("GPL");
@@ -75,6 +76,7 @@ static const struct key_entry intel_array_keymap[] = {
struct intel_hid_priv {
struct input_dev *input_dev;
struct input_dev *array;
bool wakeup_mode;
};
static int intel_hid_set_enable(struct device *device, bool enable)
@@ -116,23 +118,37 @@ static void intel_button_array_enable(struct device *device, bool enable)
dev_warn(device, "failed to set button capability\n");
}
static int intel_hid_pm_prepare(struct device *device)
{
struct intel_hid_priv *priv = dev_get_drvdata(device);
priv->wakeup_mode = true;
return 0;
}
static int intel_hid_pl_suspend_handler(struct device *device)
{
intel_hid_set_enable(device, false);
intel_button_array_enable(device, false);
if (pm_suspend_via_firmware()) {
intel_hid_set_enable(device, false);
intel_button_array_enable(device, false);
}
return 0;
}
static int intel_hid_pl_resume_handler(struct device *device)
{
intel_hid_set_enable(device, true);
intel_button_array_enable(device, true);
struct intel_hid_priv *priv = dev_get_drvdata(device);
priv->wakeup_mode = false;
if (pm_resume_via_firmware()) {
intel_hid_set_enable(device, true);
intel_button_array_enable(device, true);
}
return 0;
}
static const struct dev_pm_ops intel_hid_pl_pm_ops = {
.prepare = intel_hid_pm_prepare,
.freeze = intel_hid_pl_suspend_handler,
.thaw = intel_hid_pl_resume_handler,
.restore = intel_hid_pl_resume_handler,
@@ -186,6 +202,19 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
unsigned long long ev_index;
acpi_status status;
if (priv->wakeup_mode) {
/* Wake up on 5-button array events only. */
if (event == 0xc0 || !priv->array)
return;
if (sparse_keymap_entry_from_scancode(priv->array, event))
pm_wakeup_hard_event(&device->dev);
else
dev_info(&device->dev, "unknown event 0x%x\n", event);
return;
}
/* 0xC0 is for HID events, other values are for 5 button array */
if (event != 0xc0) {
if (!priv->array ||
@@ -270,6 +299,7 @@ static int intel_hid_probe(struct platform_device *device)
"failed to enable HID power button\n");
}
device_init_wakeup(&device->dev, true);
return 0;
err_remove_notify:

查看文件

@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/input/sparse-keymap.h>
#include <linux/acpi.h>
#include <linux/suspend.h>
#include <acpi/acpi_bus.h>
MODULE_LICENSE("GPL");
@@ -46,6 +47,7 @@ static const struct key_entry intel_vbtn_keymap[] = {
struct intel_vbtn_priv {
struct input_dev *input_dev;
bool wakeup_mode;
};
static int intel_vbtn_input_setup(struct platform_device *device)
@@ -73,9 +75,15 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
struct platform_device *device = context;
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
if (!sparse_keymap_report_event(priv->input_dev, event, 1, true))
dev_info(&device->dev, "unknown event index 0x%x\n",
event);
if (priv->wakeup_mode) {
if (sparse_keymap_entry_from_scancode(priv->input_dev, event)) {
pm_wakeup_hard_event(&device->dev);
return;
}
} else if (sparse_keymap_report_event(priv->input_dev, event, 1, true)) {
return;
}
dev_info(&device->dev, "unknown event index 0x%x\n", event);
}
static int intel_vbtn_probe(struct platform_device *device)
@@ -109,6 +117,7 @@ static int intel_vbtn_probe(struct platform_device *device)
if (ACPI_FAILURE(status))
return -EBUSY;
device_init_wakeup(&device->dev, true);
return 0;
}
@@ -125,10 +134,34 @@ static int intel_vbtn_remove(struct platform_device *device)
return 0;
}
static int intel_vbtn_pm_prepare(struct device *dev)
{
struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
priv->wakeup_mode = true;
return 0;
}
static int intel_vbtn_pm_resume(struct device *dev)
{
struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
priv->wakeup_mode = false;
return 0;
}
static const struct dev_pm_ops intel_vbtn_pm_ops = {
.prepare = intel_vbtn_pm_prepare,
.resume = intel_vbtn_pm_resume,
.restore = intel_vbtn_pm_resume,
.thaw = intel_vbtn_pm_resume,
};
static struct platform_driver intel_vbtn_pl_driver = {
.driver = {
.name = "intel-vbtn",
.acpi_match_table = intel_vbtn_ids,
.pm = &intel_vbtn_pm_ops,
},
.probe = intel_vbtn_probe,
.remove = intel_vbtn_remove,

查看文件

@@ -0,0 +1,219 @@
/*
* Intel INT0002 "Virtual GPIO" driver
*
* Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com>
*
* Loosely based on android x86 kernel code which is:
*
* Copyright (c) 2014, Intel Corporation.
*
* Author: Dyut Kumar Sil <dyut.k.sil@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Some peripherals on Bay Trail and Cherry Trail platforms signal a Power
* Management Event (PME) to the Power Management Controller (PMC) to wakeup
* the system. When this happens software needs to clear the PME bus 0 status
* bit in the GPE0a_STS register to avoid an IRQ storm on IRQ 9.
*
* This is modelled in ACPI through the INT0002 ACPI device, which is
* called a "Virtual GPIO controller" in ACPI because it defines the event
* handler to call when the PME triggers through _AEI and _L02 / _E02
* methods as would be done for a real GPIO interrupt in ACPI. Note this
* is a hack to define an AML event handler for the PME while using existing
* ACPI mechanisms, this is not a real GPIO at all.
*
* This driver will bind to the INT0002 device, and register as a GPIO
* controller, letting gpiolib-acpi.c call the _L02 handler as it would
* for a real GPIO controller.
*/
#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
#define DRV_NAME "INT0002 Virtual GPIO"
/* For some reason the virtual GPIO pin tied to the GPE is numbered pin 2 */
#define GPE0A_PME_B0_VIRT_GPIO_PIN 2
#define GPE0A_PME_B0_STS_BIT BIT(13)
#define GPE0A_PME_B0_EN_BIT BIT(13)
#define GPE0A_STS_PORT 0x420
#define GPE0A_EN_PORT 0x428
#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
static const struct x86_cpu_id int0002_cpu_ids[] = {
/*
* Limit ourselves to Cherry Trail for now, until testing shows we
* need to handle the INT0002 device on Baytrail too.
* ICPU(INTEL_FAM6_ATOM_SILVERMONT1), * Valleyview, Bay Trail *
*/
ICPU(INTEL_FAM6_ATOM_AIRMONT), /* Braswell, Cherry Trail */
{}
};
/*
* As this is not a real GPIO at all, but just a hack to model an event in
* ACPI the get / set functions are dummy functions.
*/
static int int0002_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
return 0;
}
static void int0002_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
}
static int int0002_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
return 0;
}
static void int0002_irq_ack(struct irq_data *data)
{
outl(GPE0A_PME_B0_STS_BIT, GPE0A_STS_PORT);
}
static void int0002_irq_unmask(struct irq_data *data)
{
u32 gpe_en_reg;
gpe_en_reg = inl(GPE0A_EN_PORT);
gpe_en_reg |= GPE0A_PME_B0_EN_BIT;
outl(gpe_en_reg, GPE0A_EN_PORT);
}
static void int0002_irq_mask(struct irq_data *data)
{
u32 gpe_en_reg;
gpe_en_reg = inl(GPE0A_EN_PORT);
gpe_en_reg &= ~GPE0A_PME_B0_EN_BIT;
outl(gpe_en_reg, GPE0A_EN_PORT);
}
static irqreturn_t int0002_irq(int irq, void *data)
{
struct gpio_chip *chip = data;
u32 gpe_sts_reg;
gpe_sts_reg = inl(GPE0A_STS_PORT);
if (!(gpe_sts_reg & GPE0A_PME_B0_STS_BIT))
return IRQ_NONE;
generic_handle_irq(irq_find_mapping(chip->irqdomain,
GPE0A_PME_B0_VIRT_GPIO_PIN));
pm_system_wakeup();
return IRQ_HANDLED;
}
static struct irq_chip int0002_irqchip = {
.name = DRV_NAME,
.irq_ack = int0002_irq_ack,
.irq_mask = int0002_irq_mask,
.irq_unmask = int0002_irq_unmask,
};
static int int0002_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct x86_cpu_id *cpu_id;
struct gpio_chip *chip;
int irq, ret;
/* Menlow has a different INT0002 device? <sigh> */
cpu_id = x86_match_cpu(int0002_cpu_ids);
if (!cpu_id)
return -ENODEV;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "Error getting IRQ: %d\n", irq);
return irq;
}
chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->label = DRV_NAME;
chip->parent = dev;
chip->owner = THIS_MODULE;
chip->get = int0002_gpio_get;
chip->set = int0002_gpio_set;
chip->direction_input = int0002_gpio_get;
chip->direction_output = int0002_gpio_direction_output;
chip->base = -1;
chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1;
chip->irq_need_valid_mask = true;
ret = devm_gpiochip_add_data(&pdev->dev, chip, NULL);
if (ret) {
dev_err(dev, "Error adding gpio chip: %d\n", ret);
return ret;
}
bitmap_clear(chip->irq_valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN);
/*
* We manually request the irq here instead of passing a flow-handler
* to gpiochip_set_chained_irqchip, because the irq is shared.
*/
ret = devm_request_irq(dev, irq, int0002_irq,
IRQF_SHARED | IRQF_NO_THREAD, "INT0002", chip);
if (ret) {
dev_err(dev, "Error requesting IRQ %d: %d\n", irq, ret);
return ret;
}
ret = gpiochip_irqchip_add(chip, &int0002_irqchip, 0, handle_edge_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_err(dev, "Error adding irqchip: %d\n", ret);
return ret;
}
gpiochip_set_chained_irqchip(chip, &int0002_irqchip, irq, NULL);
return 0;
}
static const struct acpi_device_id int0002_acpi_ids[] = {
{ "INT0002", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, int0002_acpi_ids);
static struct platform_driver int0002_driver = {
.driver = {
.name = DRV_NAME,
.acpi_match_table = int0002_acpi_ids,
},
.probe = int0002_probe,
};
module_platform_driver(int0002_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Intel INT0002 Virtual GPIO driver");
MODULE_LICENSE("GPL");