Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0137a337 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branches 'acpi-wdat' and 'acpi-ec'

* acpi-wdat:
  watchdog: wdat_wdt: Fix warning for using 0 as NULL
  watchdog: wdat_wdt: fix return value check in wdat_wdt_probe()
  platform/x86: intel_pmc_ipc: Do not create iTCO watchdog when WDAT table exists
  i2c: i801: Do not create iTCO watchdog when WDAT table exists
  mfd: lpc_ich: Do not create iTCO watchdog when WDAT table exists
  ACPI / watchdog: Add support for WDAT hardware watchdog

* acpi-ec:
  ACPI / EC: Fix issues related to boot_ec
  ACPI / EC: Fix a gap that ECDT EC cannot handle EC events
  ACPI / EC: Fix a memory leakage issue in acpi_ec_add()
  ACPI / EC: Cleanup first_ec/boot_ec code
  ACPI / EC: Enable event freeze mode to improve event handling for suspend process
  ACPI / EC: Add PM operations to improve event handling for suspend process
  ACPI / EC: Add PM operations to improve event handling for resume process
  ACPI / EC: Fix an issue that SCI_EVT cannot be detected after event is enabled
  ACPI / EC: Add EC_FLAGS_QUERY_ENABLED to reveal a hidden logic
  ACPI / EC: Add PM operations for suspend/resume noirq stage
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -461,6 +461,9 @@ source "drivers/acpi/nfit/Kconfig"
source "drivers/acpi/apei/Kconfig"
source "drivers/acpi/dptf/Kconfig"

config ACPI_WATCHDOG
	bool

config ACPI_EXTLOG
	tristate "Extended Error Log support"
	depends on X86_MCE && X86_LOCAL_APIC
+1 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y				+= acpi_lpat.o
acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o
acpi-$(CONFIG_ACPI_WATCHDOG)	+= acpi_watchdog.o

# These are (potentially) separate modules

+123 −0
Original line number Diff line number Diff line
/*
 * ACPI watchdog table parsing support.
 *
 * Copyright (C) 2016, Intel Corporation
 * Author: Mika Westerberg <mika.westerberg@linux.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.
 */

#define pr_fmt(fmt) "ACPI: watchdog: " fmt

#include <linux/acpi.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>

#include "internal.h"

/**
 * Returns true if this system should prefer ACPI based watchdog instead of
 * the native one (which are typically the same hardware).
 */
bool acpi_has_watchdog(void)
{
	struct acpi_table_header hdr;

	if (acpi_disabled)
		return false;

	return ACPI_SUCCESS(acpi_get_table_header(ACPI_SIG_WDAT, 0, &hdr));
}
EXPORT_SYMBOL_GPL(acpi_has_watchdog);

void __init acpi_watchdog_init(void)
{
	const struct acpi_wdat_entry *entries;
	const struct acpi_table_wdat *wdat;
	struct list_head resource_list;
	struct resource_entry *rentry;
	struct platform_device *pdev;
	struct resource *resources;
	size_t nresources = 0;
	acpi_status status;
	int i;

	status = acpi_get_table(ACPI_SIG_WDAT, 0,
				(struct acpi_table_header **)&wdat);
	if (ACPI_FAILURE(status)) {
		/* It is fine if there is no WDAT */
		return;
	}

	/* Watchdog disabled by BIOS */
	if (!(wdat->flags & ACPI_WDAT_ENABLED))
		return;

	/* Skip legacy PCI WDT devices */
	if (wdat->pci_segment != 0xff || wdat->pci_bus != 0xff ||
	    wdat->pci_device != 0xff || wdat->pci_function != 0xff)
		return;

	INIT_LIST_HEAD(&resource_list);

	entries = (struct acpi_wdat_entry *)(wdat + 1);
	for (i = 0; i < wdat->entries; i++) {
		const struct acpi_generic_address *gas;
		struct resource_entry *rentry;
		struct resource res;
		bool found;

		gas = &entries[i].register_region;

		res.start = gas->address;
		if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
			res.flags = IORESOURCE_MEM;
			res.end = res.start + ALIGN(gas->access_width, 4);
		} else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
			res.flags = IORESOURCE_IO;
			res.end = res.start + gas->access_width;
		} else {
			pr_warn("Unsupported address space: %u\n",
				gas->space_id);
			goto fail_free_resource_list;
		}

		found = false;
		resource_list_for_each_entry(rentry, &resource_list) {
			if (resource_contains(rentry->res, &res)) {
				found = true;
				break;
			}
		}

		if (!found) {
			rentry = resource_list_create_entry(NULL, 0);
			if (!rentry)
				goto fail_free_resource_list;

			*rentry->res = res;
			resource_list_add_tail(rentry, &resource_list);
			nresources++;
		}
	}

	resources = kcalloc(nresources, sizeof(*resources), GFP_KERNEL);
	if (!resources)
		goto fail_free_resource_list;

	i = 0;
	resource_list_for_each_entry(rentry, &resource_list)
		resources[i++] = *rentry->res;

	pdev = platform_device_register_simple("wdat_wdt", PLATFORM_DEVID_NONE,
					       resources, nresources);
	if (IS_ERR(pdev))
		pr_err("Failed to create platform device\n");

	kfree(resources);

fail_free_resource_list:
	resource_list_free(&resource_list);
}
+379 −89
Original line number Diff line number Diff line
@@ -104,10 +104,12 @@ enum ec_command {
#define ACPI_EC_MAX_QUERIES	16	/* Maximum number of parallel queries */

enum {
	EC_FLAGS_QUERY_ENABLED,		/* Query is enabled */
	EC_FLAGS_QUERY_PENDING,		/* Query is pending */
	EC_FLAGS_QUERY_GUARDING,	/* Guard for SCI_EVT check */
	EC_FLAGS_GPE_HANDLER_INSTALLED,	/* GPE handler installed */
	EC_FLAGS_EC_HANDLER_INSTALLED,	/* OpReg handler installed */
	EC_FLAGS_EVT_HANDLER_INSTALLED, /* _Qxx handlers installed */
	EC_FLAGS_STARTED,		/* Driver is started */
	EC_FLAGS_STOPPED,		/* Driver is stopped */
	EC_FLAGS_COMMAND_STORM,		/* GPE storms occurred to the
@@ -145,6 +147,10 @@ static unsigned int ec_storm_threshold __read_mostly = 8;
module_param(ec_storm_threshold, uint, 0644);
MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm");

static bool ec_freeze_events __read_mostly = true;
module_param(ec_freeze_events, bool, 0644);
MODULE_PARM_DESC(ec_freeze_events, "Disabling event handling during suspend/resume");

struct acpi_ec_query_handler {
	struct list_head node;
	acpi_ec_query_func func;
@@ -179,6 +185,7 @@ static void acpi_ec_event_processor(struct work_struct *work);

struct acpi_ec *boot_ec, *first_ec;
EXPORT_SYMBOL(first_ec);
static bool boot_ec_is_ecdt = false;
static struct workqueue_struct *ec_query_wq;

static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
@@ -239,6 +246,30 @@ static bool acpi_ec_started(struct acpi_ec *ec)
	       !test_bit(EC_FLAGS_STOPPED, &ec->flags);
}

static bool acpi_ec_event_enabled(struct acpi_ec *ec)
{
	/*
	 * There is an OSPM early stage logic. During the early stages
	 * (boot/resume), OSPMs shouldn't enable the event handling, only
	 * the EC transactions are allowed to be performed.
	 */
	if (!test_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
		return false;
	/*
	 * However, disabling the event handling is experimental for late
	 * stage (suspend), and is controlled by the boot parameter of
	 * "ec_freeze_events":
	 * 1. true:  The EC event handling is disabled before entering
	 *           the noirq stage.
	 * 2. false: The EC event handling is automatically disabled as
	 *           soon as the EC driver is stopped.
	 */
	if (ec_freeze_events)
		return acpi_ec_started(ec);
	else
		return test_bit(EC_FLAGS_STARTED, &ec->flags);
}

static bool acpi_ec_flushed(struct acpi_ec *ec)
{
	return ec->reference_count == 1;
@@ -429,7 +460,8 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec)

static void acpi_ec_submit_query(struct acpi_ec *ec)
{
	if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
	if (acpi_ec_event_enabled(ec) &&
	    !test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) {
		ec_dbg_evt("Command(%s) submitted/blocked",
			   acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
		ec->nr_pending_queries++;
@@ -446,6 +478,86 @@ static void acpi_ec_complete_query(struct acpi_ec *ec)
	}
}

static inline void __acpi_ec_enable_event(struct acpi_ec *ec)
{
	if (!test_and_set_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
		ec_log_drv("event unblocked");
	if (!test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
		advance_transaction(ec);
}

static inline void __acpi_ec_disable_event(struct acpi_ec *ec)
{
	if (test_and_clear_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags))
		ec_log_drv("event blocked");
}

/*
 * Process _Q events that might have accumulated in the EC.
 * Run with locked ec mutex.
 */
static void acpi_ec_clear(struct acpi_ec *ec)
{
	int i, status;
	u8 value = 0;

	for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
		status = acpi_ec_query(ec, &value);
		if (status || !value)
			break;
	}
	if (unlikely(i == ACPI_EC_CLEAR_MAX))
		pr_warn("Warning: Maximum of %d stale EC events cleared\n", i);
	else
		pr_info("%d stale EC events cleared\n", i);
}

static void acpi_ec_enable_event(struct acpi_ec *ec)
{
	unsigned long flags;

	spin_lock_irqsave(&ec->lock, flags);
	if (acpi_ec_started(ec))
		__acpi_ec_enable_event(ec);
	spin_unlock_irqrestore(&ec->lock, flags);

	/* Drain additional events if hardware requires that */
	if (EC_FLAGS_CLEAR_ON_RESUME)
		acpi_ec_clear(ec);
}

static bool acpi_ec_query_flushed(struct acpi_ec *ec)
{
	bool flushed;
	unsigned long flags;

	spin_lock_irqsave(&ec->lock, flags);
	flushed = !ec->nr_pending_queries;
	spin_unlock_irqrestore(&ec->lock, flags);
	return flushed;
}

static void __acpi_ec_flush_event(struct acpi_ec *ec)
{
	/*
	 * When ec_freeze_events is true, we need to flush events in
	 * the proper position before entering the noirq stage.
	 */
	wait_event(ec->wait, acpi_ec_query_flushed(ec));
	if (ec_query_wq)
		flush_workqueue(ec_query_wq);
}

static void acpi_ec_disable_event(struct acpi_ec *ec)
{
	unsigned long flags;

	spin_lock_irqsave(&ec->lock, flags);
	__acpi_ec_disable_event(ec);
	spin_unlock_irqrestore(&ec->lock, flags);
	__acpi_ec_flush_event(ec);
}

static bool acpi_ec_guard_event(struct acpi_ec *ec)
{
	bool guarded = true;
@@ -832,27 +944,6 @@ acpi_handle ec_get_handle(void)
}
EXPORT_SYMBOL(ec_get_handle);

/*
 * Process _Q events that might have accumulated in the EC.
 * Run with locked ec mutex.
 */
static void acpi_ec_clear(struct acpi_ec *ec)
{
	int i, status;
	u8 value = 0;

	for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) {
		status = acpi_ec_query(ec, &value);
		if (status || !value)
			break;
	}

	if (unlikely(i == ACPI_EC_CLEAR_MAX))
		pr_warn("Warning: Maximum of %d stale EC events cleared\n", i);
	else
		pr_info("%d stale EC events cleared\n", i);
}

static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
{
	unsigned long flags;
@@ -896,7 +987,8 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending)
		if (!suspending) {
			acpi_ec_complete_request(ec);
			ec_dbg_ref(ec, "Decrease driver");
		}
		} else if (!ec_freeze_events)
			__acpi_ec_disable_event(ec);
		clear_bit(EC_FLAGS_STARTED, &ec->flags);
		clear_bit(EC_FLAGS_STOPPED, &ec->flags);
		ec_log_drv("EC stopped");
@@ -918,20 +1010,6 @@ void acpi_ec_block_transactions(void)
}

void acpi_ec_unblock_transactions(void)
{
	struct acpi_ec *ec = first_ec;

	if (!ec)
		return;

	/* Allow transactions to be carried out again */
	acpi_ec_start(ec, true);

	if (EC_FLAGS_CLEAR_ON_RESUME)
		acpi_ec_clear(ec);
}

void acpi_ec_unblock_transactions_early(void)
{
	/*
	 * Allow transactions to happen again (this function is called from
@@ -1228,13 +1306,21 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address,
static acpi_status
ec_parse_io_ports(struct acpi_resource *resource, void *context);

static struct acpi_ec *make_acpi_ec(void)
static void acpi_ec_free(struct acpi_ec *ec)
{
	if (first_ec == ec)
		first_ec = NULL;
	if (boot_ec == ec)
		boot_ec = NULL;
	kfree(ec);
}

static struct acpi_ec *acpi_ec_alloc(void)
{
	struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);

	if (!ec)
		return NULL;
	ec->flags = 1 << EC_FLAGS_QUERY_PENDING;
	mutex_init(&ec->mutex);
	init_waitqueue_head(&ec->wait);
	INIT_LIST_HEAD(&ec->list);
@@ -1290,7 +1376,12 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
	return AE_CTRL_TERMINATE;
}

static int ec_install_handlers(struct acpi_ec *ec)
/*
 * Note: This function returns an error code only when the address space
 *       handler is not installed, which means "not able to handle
 *       transactions".
 */
static int ec_install_handlers(struct acpi_ec *ec, bool handle_events)
{
	acpi_status status;

@@ -1319,6 +1410,16 @@ static int ec_install_handlers(struct acpi_ec *ec)
		set_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
	}

	if (!handle_events)
		return 0;

	if (!test_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags)) {
		/* Find and register all query methods */
		acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
				    acpi_ec_register_query_methods,
				    NULL, ec, NULL);
		set_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags);
	}
	if (!test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) {
		status = acpi_install_gpe_raw_handler(NULL, ec->gpe,
					  ACPI_GPE_EDGE_TRIGGERED,
@@ -1329,6 +1430,9 @@ static int ec_install_handlers(struct acpi_ec *ec)
			if (test_bit(EC_FLAGS_STARTED, &ec->flags) &&
			    ec->reference_count >= 1)
				acpi_ec_enable_gpe(ec, true);

			/* EC is fully operational, allow queries */
			acpi_ec_enable_event(ec);
		}
	}

@@ -1363,23 +1467,104 @@ static void ec_remove_handlers(struct acpi_ec *ec)
			pr_err("failed to remove gpe handler\n");
		clear_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags);
	}
	if (test_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags)) {
		acpi_ec_remove_query_handlers(ec, true, 0);
		clear_bit(EC_FLAGS_EVT_HANDLER_INSTALLED, &ec->flags);
	}
}

static struct acpi_ec *acpi_ec_alloc(void)
static int acpi_ec_setup(struct acpi_ec *ec, bool handle_events)
{
	struct acpi_ec *ec;
	int ret;

	/* Check for boot EC */
	if (boot_ec) {
		ec = boot_ec;
		boot_ec = NULL;
		ec_remove_handlers(ec);
		if (first_ec == ec)
			first_ec = NULL;
	} else {
		ec = make_acpi_ec();
	ret = ec_install_handlers(ec, handle_events);
	if (ret)
		return ret;

	/* First EC capable of handling transactions */
	if (!first_ec) {
		first_ec = ec;
		acpi_handle_info(first_ec->handle, "Used as first EC\n");
	}
	return ec;

	acpi_handle_info(ec->handle,
			 "GPE=0x%lx, EC_CMD/EC_SC=0x%lx, EC_DATA=0x%lx\n",
			 ec->gpe, ec->command_addr, ec->data_addr);
	return ret;
}

static int acpi_config_boot_ec(struct acpi_ec *ec, acpi_handle handle,
			       bool handle_events, bool is_ecdt)
{
	int ret;

	/*
	 * Changing the ACPI handle results in a re-configuration of the
	 * boot EC. And if it happens after the namespace initialization,
	 * it causes _REG evaluations.
	 */
	if (boot_ec && boot_ec->handle != handle)
		ec_remove_handlers(boot_ec);

	/* Unset old boot EC */
	if (boot_ec != ec)
		acpi_ec_free(boot_ec);

	/*
	 * ECDT device creation is split into acpi_ec_ecdt_probe() and
	 * acpi_ec_ecdt_start(). This function takes care of completing the
	 * ECDT parsing logic as the handle update should be performed
	 * between the installation/uninstallation of the handlers.
	 */
	if (ec->handle != handle)
		ec->handle = handle;

	ret = acpi_ec_setup(ec, handle_events);
	if (ret)
		return ret;

	/* Set new boot EC */
	if (!boot_ec) {
		boot_ec = ec;
		boot_ec_is_ecdt = is_ecdt;
	}

	acpi_handle_info(boot_ec->handle,
			 "Used as boot %s EC to handle transactions%s\n",
			 is_ecdt ? "ECDT" : "DSDT",
			 handle_events ? " and events" : "");
	return ret;
}

static bool acpi_ec_ecdt_get_handle(acpi_handle *phandle)
{
	struct acpi_table_ecdt *ecdt_ptr;
	acpi_status status;
	acpi_handle handle;

	status = acpi_get_table(ACPI_SIG_ECDT, 1,
				(struct acpi_table_header **)&ecdt_ptr);
	if (ACPI_FAILURE(status))
		return false;

	status = acpi_get_handle(NULL, ecdt_ptr->id, &handle);
	if (ACPI_FAILURE(status))
		return false;

	*phandle = handle;
	return true;
}

static bool acpi_is_boot_ec(struct acpi_ec *ec)
{
	if (!boot_ec)
		return false;
	if (ec->handle == boot_ec->handle &&
	    ec->gpe == boot_ec->gpe &&
	    ec->command_addr == boot_ec->command_addr &&
	    ec->data_addr == boot_ec->data_addr)
		return true;
	return false;
}

static int acpi_ec_add(struct acpi_device *device)
@@ -1395,16 +1580,21 @@ static int acpi_ec_add(struct acpi_device *device)
		return -ENOMEM;
	if (ec_parse_device(device->handle, 0, ec, NULL) !=
		AE_CTRL_TERMINATE) {
			kfree(ec);
			return -EINVAL;
			ret = -EINVAL;
			goto err_alloc;
	}

	/* Find and register all query methods */
	acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1,
			    acpi_ec_register_query_methods, NULL, ec, NULL);
	if (acpi_is_boot_ec(ec)) {
		boot_ec_is_ecdt = false;
		acpi_handle_debug(ec->handle, "duplicated.\n");
		acpi_ec_free(ec);
		ec = boot_ec;
		ret = acpi_config_boot_ec(ec, ec->handle, true, false);
	} else
		ret = acpi_ec_setup(ec, true);
	if (ret)
		goto err_query;

	if (!first_ec)
		first_ec = ec;
	device->driver_data = ec;

	ret = !!request_region(ec->data_addr, 1, "EC data");
@@ -1412,20 +1602,17 @@ static int acpi_ec_add(struct acpi_device *device)
	ret = !!request_region(ec->command_addr, 1, "EC cmd");
	WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr);

	pr_info("GPE = 0x%lx, I/O: command/status = 0x%lx, data = 0x%lx\n",
			  ec->gpe, ec->command_addr, ec->data_addr);

	ret = ec_install_handlers(ec);

	/* Reprobe devices depending on the EC */
	acpi_walk_dep_device_list(ec->handle);
	acpi_handle_debug(ec->handle, "enumerated.\n");
	return 0;

	/* EC is fully operational, allow queries */
	clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);

	/* Clear stale _Q events if hardware might require that */
	if (EC_FLAGS_CLEAR_ON_RESUME)
		acpi_ec_clear(ec);
err_query:
	if (ec != boot_ec)
		acpi_ec_remove_query_handlers(ec, true, 0);
err_alloc:
	if (ec != boot_ec)
		acpi_ec_free(ec);
	return ret;
}

@@ -1437,14 +1624,13 @@ static int acpi_ec_remove(struct acpi_device *device)
		return -EINVAL;

	ec = acpi_driver_data(device);
	ec_remove_handlers(ec);
	acpi_ec_remove_query_handlers(ec, true, 0);
	release_region(ec->data_addr, 1);
	release_region(ec->command_addr, 1);
	device->driver_data = NULL;
	if (ec == first_ec)
		first_ec = NULL;
	kfree(ec);
	if (ec != boot_ec) {
		ec_remove_handlers(ec);
		acpi_ec_free(ec);
	}
	return 0;
}

@@ -1486,9 +1672,8 @@ int __init acpi_ec_dsdt_probe(void)
	if (!ec)
		return -ENOMEM;
	/*
	 * Finding EC from DSDT if there is no ECDT EC available. When this
	 * function is invoked, ACPI tables have been fully loaded, we can
	 * walk namespace now.
	 * At this point, the namespace is initialized, so start to find
	 * the namespace objects.
	 */
	status = acpi_get_devices(ec_device_ids[0].id,
				  ec_parse_device, ec, NULL);
@@ -1496,16 +1681,47 @@ int __init acpi_ec_dsdt_probe(void)
		ret = -ENODEV;
		goto error;
	}
	ret = ec_install_handlers(ec);

	/*
	 * When the DSDT EC is available, always re-configure boot EC to
	 * have _REG evaluated. _REG can only be evaluated after the
	 * namespace initialization.
	 * At this point, the GPE is not fully initialized, so do not to
	 * handle the events.
	 */
	ret = acpi_config_boot_ec(ec, ec->handle, false, false);
error:
	if (ret)
		kfree(ec);
	else
		first_ec = boot_ec = ec;
		acpi_ec_free(ec);
	return ret;
}

/*
 * If the DSDT EC is not functioning, we still need to prepare a fully
 * functioning ECDT EC first in order to handle the events.
 * https://bugzilla.kernel.org/show_bug.cgi?id=115021
 */
int __init acpi_ec_ecdt_start(void)
{
	acpi_handle handle;

	if (!boot_ec)
		return -ENODEV;
	/*
	 * The DSDT EC should have already been started in
	 * acpi_ec_add().
	 */
	if (!boot_ec_is_ecdt)
		return -ENODEV;

	/*
	 * At this point, the namespace and the GPE is initialized, so
	 * start to find the namespace objects and handle the events.
	 */
	if (!acpi_ec_ecdt_get_handle(&handle))
		return -ENODEV;
	return acpi_config_boot_ec(boot_ec, handle, true, true);
}

#if 0
/*
 * Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not
@@ -1600,7 +1816,6 @@ int __init acpi_ec_ecdt_probe(void)
		goto error;
	}

	pr_info("EC description table is found, configuring boot EC\n");
	if (EC_FLAGS_CORRECT_ECDT) {
		ec->command_addr = ecdt_ptr->data.address;
		ec->data_addr = ecdt_ptr->control.address;
@@ -1609,16 +1824,90 @@ int __init acpi_ec_ecdt_probe(void)
		ec->data_addr = ecdt_ptr->data.address;
	}
	ec->gpe = ecdt_ptr->gpe;
	ec->handle = ACPI_ROOT_OBJECT;
	ret = ec_install_handlers(ec);

	/*
	 * At this point, the namespace is not initialized, so do not find
	 * the namespace objects, or handle the events.
	 */
	ret = acpi_config_boot_ec(ec, ACPI_ROOT_OBJECT, false, true);
error:
	if (ret)
		kfree(ec);
	else
		first_ec = boot_ec = ec;
		acpi_ec_free(ec);
	return ret;
}

#ifdef CONFIG_PM_SLEEP
static void acpi_ec_enter_noirq(struct acpi_ec *ec)
{
	unsigned long flags;

	if (ec == first_ec) {
		spin_lock_irqsave(&ec->lock, flags);
		ec->saved_busy_polling = ec_busy_polling;
		ec->saved_polling_guard = ec_polling_guard;
		ec_busy_polling = true;
		ec_polling_guard = 0;
		ec_log_drv("interrupt blocked");
		spin_unlock_irqrestore(&ec->lock, flags);
	}
}

static void acpi_ec_leave_noirq(struct acpi_ec *ec)
{
	unsigned long flags;

	if (ec == first_ec) {
		spin_lock_irqsave(&ec->lock, flags);
		ec_busy_polling = ec->saved_busy_polling;
		ec_polling_guard = ec->saved_polling_guard;
		ec_log_drv("interrupt unblocked");
		spin_unlock_irqrestore(&ec->lock, flags);
	}
}

static int acpi_ec_suspend_noirq(struct device *dev)
{
	struct acpi_ec *ec =
		acpi_driver_data(to_acpi_device(dev));

	acpi_ec_enter_noirq(ec);
	return 0;
}

static int acpi_ec_resume_noirq(struct device *dev)
{
	struct acpi_ec *ec =
		acpi_driver_data(to_acpi_device(dev));

	acpi_ec_leave_noirq(ec);
	return 0;
}

static int acpi_ec_suspend(struct device *dev)
{
	struct acpi_ec *ec =
		acpi_driver_data(to_acpi_device(dev));

	if (ec_freeze_events)
		acpi_ec_disable_event(ec);
	return 0;
}

static int acpi_ec_resume(struct device *dev)
{
	struct acpi_ec *ec =
		acpi_driver_data(to_acpi_device(dev));

	acpi_ec_enable_event(ec);
	return 0;
}
#endif

static const struct dev_pm_ops acpi_ec_pm = {
	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq)
	SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume)
};

static int param_set_event_clearing(const char *val, struct kernel_param *kp)
{
	int result = 0;
@@ -1664,6 +1953,7 @@ static struct acpi_driver acpi_ec_driver = {
		.add = acpi_ec_add,
		.remove = acpi_ec_remove,
		},
	.drv.pm = &acpi_ec_pm,
};

static inline int acpi_ec_query_init(void)
+13 −1
Original line number Diff line number Diff line
@@ -173,6 +173,8 @@ struct acpi_ec {
	struct work_struct work;
	unsigned long timestamp;
	unsigned long nr_pending_queries;
	bool saved_busy_polling;
	unsigned int saved_polling_guard;
};

extern struct acpi_ec *first_ec;
@@ -184,9 +186,9 @@ typedef int (*acpi_ec_query_func) (void *data);
int acpi_ec_init(void);
int acpi_ec_ecdt_probe(void);
int acpi_ec_dsdt_probe(void);
int acpi_ec_ecdt_start(void);
void acpi_ec_block_transactions(void);
void acpi_ec_unblock_transactions(void);
void acpi_ec_unblock_transactions_early(void);
int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
			      acpi_handle handle, acpi_ec_query_func func,
			      void *data);
@@ -224,4 +226,14 @@ static inline void suspend_nvs_restore(void) {}
void acpi_init_properties(struct acpi_device *adev);
void acpi_free_properties(struct acpi_device *adev);

/*--------------------------------------------------------------------------
				Watchdog
  -------------------------------------------------------------------------- */

#ifdef CONFIG_ACPI_WATCHDOG
void acpi_watchdog_init(void);
#else
static inline void acpi_watchdog_init(void) {}
#endif

#endif /* _ACPI_INTERNAL_H_ */
Loading