ACPI material for v4.9-rc1

- Update of the ACPICA code in the kernel to upstream revision 20160831 with
    the following major changes:
    * New mechanism for GPE masking.
    * Fixes for issues related to the LoadTable operator and table loading.
    * Fixes for issues related to so-called module-level code (MLC), that is
      AML that doesn't belong to any methods.
    * Change of the return value of the _OSI method to reflect the Windows
      behavior.
    * GAS (Generic Address Structure) support fix related to 32-bit FADT
      addresses.
    * Elimination of unnecessary FADT version 2 support.
    * ACPI tools fixes and cleanups.
    From Bob Moore, Lv Zheng, and Jung-uk Kim.
 
  - ACPI sysfs interface updates to fix GPE handling (on top of the new GPE
    masking mechanism in ACPICA) and issues related to table loading (Lv Zheng).
 
  - New watchdog driver based on the ACPI WDAT (ACPI Watchdog Action Table),
    needed on some platforms to replace the iTCO watchdog that doesn't work there
    and related updates of the intel_pmc_ipc, i2c/i801 and MFD/lcp_ich drivers
    (Mika Westerberg).
 
  - Driver core fix to prevent it from leaking secondary fwnode objects during
    device removal (Lukas Wunner).
 
  - New definitions of built-in properties for UART in ACPI-based x86 SoC drivers
    and a 8250_dw driver quirk for the APM X-Gene SoC (Heikki Krogerus).
 
  - New device ID for the Vulcan SPI controller and constification of local
    strucures in the AMD SoC (APD) ACPI driver (Kamlakant Patel, Julia Lawall).
 
  - Fix for a bug causing the allocation of PCI resorces to fail if
    ACPI-enumerated child platform devices are registered below the PCI
    devices in question (Mika Westerberg).
 
  - Change of the default polarity for PCI legacy IRQs to high on systems
    booting wth ACPI on platforms with a GIC interrupt controller model
    fixing the discrepancy between the specification and HW behavior (Lorenzo
    Pieralisi).
 
  - Fixes for the handling of system suspend/resume in the ACPI EC driver and
    update of that driver to make it cope with the cases when the EC device
    defined in the ECDT has to be used throughout the entire system life cycle
    (Lv Zheng).
 
  - Update of the ACPI CPPC library to allow it to batch requests sent over the
    PCC channel (to reduce overhead), to support the fixed functional hardware
    (FFH) CPPC registers access type, to notify the mailbox framework about TX
    completions when the interrupt flag is set for the PCC mailbox, and to
    support HW-Reduced Communication Subspace type 2 (Ashwin Chaugule, Prashanth
    Prakash, Srinivas Pandruvada, Hoan Tran).
 
  - ACPI button driver fix and documentation update related to the handling of
    laptop lids (Lv Zheng).
 
  - ACPI battery driver initialization fix (Carlos Garnacho).
 
  - ACPI GPIO enumeration documentation update (Mika Westerberg).
 
  - Assorted updates of the core ACPI bus type code (Lukas Wunner, Lv Zheng).
 
  - Assorted cleanups of the ACPI table parsing code and the x86-specific ACPI
    code (Al Stone).
 
  - Fixes for assorted ACPI-related issues found in linux-next (Wei Yongjun).
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABCAAGBQJX8Y5+AAoJEILEb/54YlRx73oP/RiAi86NKjOj+GfYceVe37jn
 6lSqoMugjgTQHRYvYiQCjJ/BR0GzQZqUkz9TAu1Op14+rhTH3OhSfPizzJWCpVfA
 G9l9ZRQNnsKNs14bbYmWtmWduh46dFLVFJqo+M/0H3ZMFZu6Adcb+1SBtXHUoQ6L
 z69ngFxTu3yRvqS4cmm5h7SOx5W2uZZl8zViJW8jgyGhUBStG87gzR6wsYBldGCk
 XFxcaGWBXRccWGAQLSwfs0psQccEooCqbpsDqaUdrK/mI0rsQr88f25ZxEE7Zw7H
 bv3py1cgJBZRq36L7eBGQXjIE7YQey6qG2lug2zsUJWe+vzy2vHjHVJHuBXKKgv3
 txOA6QZx63UgEyN3zFT7K5ek6uOnkKdeE+s+Laj+K/x4V2R6gbtgO011EVcXy+bI
 NvqsO76tfPHpwrn5s1VVc5lcEBEPHKHb+WulHrqhSSU4ivk0gtJDeSI+c8xta6YT
 XwSry5tozDLkG1uEZqkyY1XTlOUAHO8E6YcrlOv2z1+mG7L8OH/vCp1apzgexsZA
 1683AH5cwKc3KaP+4QdKGdxY2BDxb7OTVh3cGy4kAYb6tqQ/vj7vlRiJvtaMBtFw
 xJn3buuagwJzKtgebpA565opvyFAfUX/RNFlTP63aXAefSAgq6KLq70vKFxkIZto
 H1LpUbmiEbuBml8CBGb1
 =xDOQ
 -----END PGP SIGNATURE-----

Merge tag 'acpi-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull ACPI updates from Rafael Wysocki:
 "First off, the ACPICA code in the kernel is updated to upstream
  revision 20160831 that brings in a few bug fixes and cleanups. In
  particular, it is possible to mask GPEs now (and the sysfs interface
  for GPE control is fixed on top of that), problems related to the
  table loading mechanism are fixed and all code related to FADT version
  2 (which has never been part of the ACPI specification) is dropped.

  On the new features front, there is a new watchdog driver based on the
  ACPI WDAT (ACPI Watchdog Action Table), needed on some platforms to
  replace the iTCO watchdog that doesn't work there, and some UART
  devices get new definitions of built-in properties (to be accessed via
  the generic device properties API).

  Also, included is a fix for an ACPI-related PCI resorces allocation
  issue and a few problems in the EC driver and in the button and
  battery drivers are fixed.

  In addition to that, the ACPI CPPC library is updated to make batching
  of requests sent over the PCC channel possible (which reduces the PCC
  usage overhead substantially in some cases) and to support functional
  fixed hardware (FFH) type of CPPC registers access (which will allow
  CPPC to be used on x86 too in the future).

  As usual, there are some assorted fixes and cleanups too.

  Specifics:

   - Update of the ACPICA code in the kernel to upstream revision
     20160831 with the following major changes:

      * New mechanism for GPE masking.
      * Fixes for issues related to the LoadTable operator and table
        loading.
      * Fixes for issues related to so-called module-level code (MLC),
        that is AML that doesn't belong to any methods.
      * Change of the return value of the _OSI method to reflect the
        Windows behavior.
      * GAS (Generic Address Structure) support fix related to 32-bit
        FADT addresses.
      * Elimination of unnecessary FADT version 2 support.
      * ACPI tools fixes and cleanups.

     From Bob Moore, Lv Zheng, and Jung-uk Kim.

   - ACPI sysfs interface updates to fix GPE handling (on top of the new
     GPE masking mechanism in ACPICA) and issues related to table
     loading (Lv Zheng).

   - New watchdog driver based on the ACPI WDAT (ACPI Watchdog Action
     Table), needed on some platforms to replace the iTCO watchdog that
     doesn't work there and related updates of the intel_pmc_ipc,
     i2c/i801 and MFD/lcp_ich drivers (Mika Westerberg).

   - Driver core fix to prevent it from leaking secondary fwnode objects
     during device removal (Lukas Wunner).

   - New definitions of built-in properties for UART in ACPI-based x86
     SoC drivers and a 8250_dw driver quirk for the APM X-Gene SoC
     (Heikki Krogerus).

   - New device ID for the Vulcan SPI controller and constification of
     local strucures in the AMD SoC (APD) ACPI driver (Kamlakant Patel,
     Julia Lawall).

   - Fix for a bug causing the allocation of PCI resorces to fail if
     ACPI-enumerated child platform devices are registered below the PCI
     devices in question (Mika Westerberg).

   - Change of the default polarity for PCI legacy IRQs to high on
     systems booting wth ACPI on platforms with a GIC interrupt
     controller model fixing the discrepancy between the specification
     and HW behavior (Lorenzo Pieralisi).

   - Fixes for the handling of system suspend/resume in the ACPI EC
     driver and update of that driver to make it cope with the cases
     when the EC device defined in the ECDT has to be used throughout
     the entire system life cycle (Lv Zheng).

   - Update of the ACPI CPPC library to allow it to batch requests sent
     over the PCC channel (to reduce overhead), to support the fixed
     functional hardware (FFH) CPPC registers access type, to notify the
     mailbox framework about TX completions when the interrupt flag is
     set for the PCC mailbox, and to support HW-Reduced Communication
     Subspace type 2 (Ashwin Chaugule, Prashanth Prakash, Srinivas
     Pandruvada, Hoan Tran).

   - ACPI button driver fix and documentation update related to the
     handling of laptop lids (Lv Zheng).

   - ACPI battery driver initialization fix (Carlos Garnacho).

   - ACPI GPIO enumeration documentation update (Mika Westerberg).

   - Assorted updates of the core ACPI bus type code (Lukas Wunner, Lv
     Zheng).

   - Assorted cleanups of the ACPI table parsing code and the
     x86-specific ACPI code (Al Stone).

   - Fixes for assorted ACPI-related issues found in linux-next (Wei
     Yongjun)"

* tag 'acpi-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (98 commits)
  ACPI / documentation: Use recommended name in GPIO property names
  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 / bus: Adjust ACPI subsystem initialization for new table loading mode
  ACPICA: Parser: Fix a regression in LoadTable support
  ACPICA: Tables: Fix "UNLOAD" code path lock issues
  ACPI / watchdog: Add support for WDAT hardware watchdog
  ACPI / platform: Pay attention to parent device's resources
  PCI: Add pci_find_resource()
  ACPI / CPPC: Support PCC with interrupt flag
  ACPI / sysfs: Update sysfs signature handling code
  ACPI / sysfs: Fix an issue for LoadTable opcode
  ACPICA: Tables: Fix a regression in acpi_tb_find_table()
  ACPI / tables: Remove duplicated include from tables.c
  ACPI / APD: constify local structures
  x86: ACPI: make variable names clearer in acpi_parse_madt_lapic_entries()
  x86: ACPI: remove extraneous white space after semicolon
  ...
This commit is contained in:
Linus Torvalds 2016-10-03 10:11:58 -07:00
commit 72d39926f0
121 changed files with 4287 additions and 1952 deletions

View File

@ -0,0 +1,96 @@
Special Usage Model of the ACPI Control Method Lid Device
Copyright (C) 2016, Intel Corporation
Author: Lv Zheng <lv.zheng@intel.com>
Abstract:
Platforms containing lids convey lid state (open/close) to OSPMs using a
control method lid device. To implement this, the AML tables issue
Notify(lid_device, 0x80) to notify the OSPMs whenever the lid state has
changed. The _LID control method for the lid device must be implemented to
report the "current" state of the lid as either "opened" or "closed".
For most platforms, both the _LID method and the lid notifications are
reliable. However, there are exceptions. In order to work with these
exceptional buggy platforms, special restrictions and expections should be
taken into account. This document describes the restrictions and the
expections of the Linux ACPI lid device driver.
1. Restrictions of the returning value of the _LID control method
The _LID control method is described to return the "current" lid state.
However the word of "current" has ambiguity, some buggy AML tables return
the lid state upon the last lid notification instead of returning the lid
state upon the last _LID evaluation. There won't be difference when the
_LID control method is evaluated during the runtime, the problem is its
initial returning value. When the AML tables implement this control method
with cached value, the initial returning value is likely not reliable.
There are platforms always retun "closed" as initial lid state.
2. Restrictions of the lid state change notifications
There are buggy AML tables never notifying when the lid device state is
changed to "opened". Thus the "opened" notification is not guaranteed. But
it is guaranteed that the AML tables always notify "closed" when the lid
state is changed to "closed". The "closed" notification is normally used to
trigger some system power saving operations on Windows. Since it is fully
tested, it is reliable from all AML tables.
3. Expections for the userspace users of the ACPI lid device driver
The ACPI button driver exports the lid state to the userspace via the
following file:
/proc/acpi/button/lid/LID0/state
This file actually calls the _LID control method described above. And given
the previous explanation, it is not reliable enough on some platforms. So
it is advised for the userspace program to not to solely rely on this file
to determine the actual lid state.
The ACPI button driver emits the following input event to the userspace:
SW_LID
The ACPI lid device driver is implemented to try to deliver the platform
triggered events to the userspace. However, given the fact that the buggy
firmware cannot make sure "opened"/"closed" events are paired, the ACPI
button driver uses the following 3 modes in order not to trigger issues.
If the userspace hasn't been prepared to ignore the unreliable "opened"
events and the unreliable initial state notification, Linux users can use
the following kernel parameters to handle the possible issues:
A. button.lid_init_state=method:
When this option is specified, the ACPI button driver reports the
initial lid state using the returning value of the _LID control method
and whether the "opened"/"closed" events are paired fully relies on the
firmware implementation.
This option can be used to fix some platforms where the returning value
of the _LID control method is reliable but the initial lid state
notification is missing.
This option is the default behavior during the period the userspace
isn't ready to handle the buggy AML tables.
B. button.lid_init_state=open:
When this option is specified, the ACPI button driver always reports the
initial lid state as "opened" and whether the "opened"/"closed" events
are paired fully relies on the firmware implementation.
This may fix some platforms where the returning value of the _LID
control method is not reliable and the initial lid state notification is
missing.
If the userspace has been prepared to ignore the unreliable "opened" events
and the unreliable initial state notification, Linux users should always
use the following kernel parameter:
C. button.lid_init_state=ignore:
When this option is specified, the ACPI button driver never reports the
initial lid state and there is a compensation mechanism implemented to
ensure that the reliable "closed" notifications can always be delievered
to the userspace by always pairing "closed" input events with complement
"opened" input events. But there is still no guarantee that the "opened"
notifications can be delivered to the userspace when the lid is actually
opens given that some AML tables do not send "opened" notifications
reliably.
In this mode, if everything is correctly implemented by the platform
firmware, the old userspace programs should still work. Otherwise, the
new userspace programs are required to work with the ACPI button driver.
This option will be the default behavior after the userspace is ready to
handle the buggy AML tables.

View File

@ -28,8 +28,8 @@ index, like the ASL example below shows:
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () Package ()
{ {
Package () {"reset-gpio", Package() {^BTH, 1, 1, 0 }}, Package () {"reset-gpios", Package() {^BTH, 1, 1, 0 }},
Package () {"shutdown-gpio", Package() {^BTH, 0, 0, 0 }}, Package () {"shutdown-gpios", Package() {^BTH, 0, 0, 0 }},
} }
}) })
} }
@ -48,7 +48,7 @@ Since ACPI GpioIo() resource does not have a field saying whether it is
active low or high, the "active_low" argument can be used here. Setting active low or high, the "active_low" argument can be used here. Setting
it to 1 marks the GPIO as active low. it to 1 marks the GPIO as active low.
In our Bluetooth example the "reset-gpio" refers to the second GpioIo() In our Bluetooth example the "reset-gpios" refers to the second GpioIo()
resource, second pin in that resource with the GPIO number of 31. resource, second pin in that resource with the GPIO number of 31.
ACPI GPIO Mappings Provided by Drivers ACPI GPIO Mappings Provided by Drivers
@ -83,8 +83,8 @@ static const struct acpi_gpio_params reset_gpio = { 1, 1, false };
static const struct acpi_gpio_params shutdown_gpio = { 0, 0, false }; static const struct acpi_gpio_params shutdown_gpio = { 0, 0, false };
static const struct acpi_gpio_mapping bluetooth_acpi_gpios[] = { static const struct acpi_gpio_mapping bluetooth_acpi_gpios[] = {
{ "reset-gpio", &reset_gpio, 1 }, { "reset-gpios", &reset_gpio, 1 },
{ "shutdown-gpio", &shutdown_gpio, 1 }, { "shutdown-gpios", &shutdown_gpio, 1 },
{ }, { },
}; };

View File

@ -1,6 +1,7 @@
obj-$(CONFIG_ACPI) += boot.o obj-$(CONFIG_ACPI) += boot.o
obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o
obj-$(CONFIG_ACPI_APEI) += apei.o obj-$(CONFIG_ACPI_APEI) += apei.o
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_msr.o
ifneq ($(CONFIG_ACPI_PROCESSOR),) ifneq ($(CONFIG_ACPI_PROCESSOR),)
obj-y += cstate.o obj-y += cstate.o

View File

@ -1031,8 +1031,8 @@ static int __init acpi_parse_madt_lapic_entries(void)
return ret; return ret;
} }
x2count = madt_proc[0].count; count = madt_proc[0].count;
count = madt_proc[1].count; x2count = madt_proc[1].count;
} }
if (!count && !x2count) { if (!count && !x2count) {
printk(KERN_ERR PREFIX "No LAPIC entries present\n"); printk(KERN_ERR PREFIX "No LAPIC entries present\n");
@ -1513,7 +1513,7 @@ void __init acpi_boot_table_init(void)
* If acpi_disabled, bail out * If acpi_disabled, bail out
*/ */
if (acpi_disabled) if (acpi_disabled)
return; return;
/* /*
* Initialize the ACPI boot-time table parser. * Initialize the ACPI boot-time table parser.

View File

@ -0,0 +1,58 @@
/*
* cppc_msr.c: MSR Interface for CPPC
* Copyright (c) 2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <acpi/cppc_acpi.h>
#include <asm/msr.h>
/* Refer to drivers/acpi/cppc_acpi.c for the description of functions */
bool cpc_ffh_supported(void)
{
return true;
}
int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
{
int err;
err = rdmsrl_safe_on_cpu(cpunum, reg->address, val);
if (!err) {
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
reg->bit_offset);
*val &= mask;
*val >>= reg->bit_offset;
}
return err;
}
int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
{
u64 rd_val;
int err;
err = rdmsrl_safe_on_cpu(cpunum, reg->address, &rd_val);
if (!err) {
u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1,
reg->bit_offset);
val <<= reg->bit_offset;
val &= mask;
rd_val &= ~mask;
rd_val |= val;
err = wrmsrl_safe_on_cpu(cpunum, reg->address, rd_val);
}
return err;
}

View File

@ -227,7 +227,6 @@ config ACPI_MCFG
config ACPI_CPPC_LIB config ACPI_CPPC_LIB
bool bool
depends on ACPI_PROCESSOR depends on ACPI_PROCESSOR
depends on !ACPI_CPU_FREQ_PSS
select MAILBOX select MAILBOX
select PCC select PCC
help help
@ -462,6 +461,9 @@ source "drivers/acpi/nfit/Kconfig"
source "drivers/acpi/apei/Kconfig" source "drivers/acpi/apei/Kconfig"
source "drivers/acpi/dptf/Kconfig" source "drivers/acpi/dptf/Kconfig"
config ACPI_WATCHDOG
bool
config ACPI_EXTLOG config ACPI_EXTLOG
tristate "Extended Error Log support" tristate "Extended Error Log support"
depends on X86_MCE && X86_LOCAL_APIC depends on X86_MCE && X86_LOCAL_APIC

View File

@ -56,6 +56,7 @@ acpi-$(CONFIG_ACPI_NUMA) += numa.o
acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
acpi-y += acpi_lpat.o acpi-y += acpi_lpat.o
acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o
acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o
# These are (potentially) separate modules # These are (potentially) separate modules

View File

@ -42,6 +42,7 @@ struct apd_private_data;
struct apd_device_desc { struct apd_device_desc {
unsigned int flags; unsigned int flags;
unsigned int fixed_clk_rate; unsigned int fixed_clk_rate;
struct property_entry *properties;
int (*setup)(struct apd_private_data *pdata); int (*setup)(struct apd_private_data *pdata);
}; };
@ -71,22 +72,35 @@ static int acpi_apd_setup(struct apd_private_data *pdata)
} }
#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE
static struct apd_device_desc cz_i2c_desc = { static const struct apd_device_desc cz_i2c_desc = {
.setup = acpi_apd_setup, .setup = acpi_apd_setup,
.fixed_clk_rate = 133000000, .fixed_clk_rate = 133000000,
}; };
static struct apd_device_desc cz_uart_desc = { static struct property_entry uart_properties[] = {
PROPERTY_ENTRY_U32("reg-io-width", 4),
PROPERTY_ENTRY_U32("reg-shift", 2),
PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
{ },
};
static const struct apd_device_desc cz_uart_desc = {
.setup = acpi_apd_setup, .setup = acpi_apd_setup,
.fixed_clk_rate = 48000000, .fixed_clk_rate = 48000000,
.properties = uart_properties,
}; };
#endif #endif
#ifdef CONFIG_ARM64 #ifdef CONFIG_ARM64
static struct apd_device_desc xgene_i2c_desc = { static const struct apd_device_desc xgene_i2c_desc = {
.setup = acpi_apd_setup, .setup = acpi_apd_setup,
.fixed_clk_rate = 100000000, .fixed_clk_rate = 100000000,
}; };
static const struct apd_device_desc vulcan_spi_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 133000000,
};
#endif #endif
#else #else
@ -125,6 +139,12 @@ static int acpi_apd_create_device(struct acpi_device *adev,
goto err_out; goto err_out;
} }
if (dev_desc->properties) {
ret = device_add_properties(&adev->dev, dev_desc->properties);
if (ret)
goto err_out;
}
adev->driver_data = pdata; adev->driver_data = pdata;
pdev = acpi_create_platform_device(adev); pdev = acpi_create_platform_device(adev);
if (!IS_ERR_OR_NULL(pdev)) if (!IS_ERR_OR_NULL(pdev))
@ -149,6 +169,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
#endif #endif
#ifdef CONFIG_ARM64 #ifdef CONFIG_ARM64
{ "APMC0D0F", APD_ADDR(xgene_i2c_desc) }, { "APMC0D0F", APD_ADDR(xgene_i2c_desc) },
{ "BRCM900D", APD_ADDR(vulcan_spi_desc) },
#endif #endif
{ } { }
}; };

View File

@ -75,6 +75,7 @@ struct lpss_device_desc {
const char *clk_con_id; const char *clk_con_id;
unsigned int prv_offset; unsigned int prv_offset;
size_t prv_size_override; size_t prv_size_override;
struct property_entry *properties;
void (*setup)(struct lpss_private_data *pdata); void (*setup)(struct lpss_private_data *pdata);
}; };
@ -163,11 +164,19 @@ static const struct lpss_device_desc lpt_i2c_dev_desc = {
.prv_offset = 0x800, .prv_offset = 0x800,
}; };
static struct property_entry uart_properties[] = {
PROPERTY_ENTRY_U32("reg-io-width", 4),
PROPERTY_ENTRY_U32("reg-shift", 2),
PROPERTY_ENTRY_BOOL("snps,uart-16550-compatible"),
{ },
};
static const struct lpss_device_desc lpt_uart_dev_desc = { static const struct lpss_device_desc lpt_uart_dev_desc = {
.flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR, .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR,
.clk_con_id = "baudclk", .clk_con_id = "baudclk",
.prv_offset = 0x800, .prv_offset = 0x800,
.setup = lpss_uart_setup, .setup = lpss_uart_setup,
.properties = uart_properties,
}; };
static const struct lpss_device_desc lpt_sdio_dev_desc = { static const struct lpss_device_desc lpt_sdio_dev_desc = {
@ -189,6 +198,7 @@ static const struct lpss_device_desc byt_uart_dev_desc = {
.clk_con_id = "baudclk", .clk_con_id = "baudclk",
.prv_offset = 0x800, .prv_offset = 0x800,
.setup = lpss_uart_setup, .setup = lpss_uart_setup,
.properties = uart_properties,
}; };
static const struct lpss_device_desc bsw_uart_dev_desc = { static const struct lpss_device_desc bsw_uart_dev_desc = {
@ -197,6 +207,7 @@ static const struct lpss_device_desc bsw_uart_dev_desc = {
.clk_con_id = "baudclk", .clk_con_id = "baudclk",
.prv_offset = 0x800, .prv_offset = 0x800,
.setup = lpss_uart_setup, .setup = lpss_uart_setup,
.properties = uart_properties,
}; };
static const struct lpss_device_desc byt_spi_dev_desc = { static const struct lpss_device_desc byt_spi_dev_desc = {
@ -440,6 +451,12 @@ static int acpi_lpss_create_device(struct acpi_device *adev,
goto err_out; goto err_out;
} }
if (dev_desc->properties) {
ret = device_add_properties(&adev->dev, dev_desc->properties);
if (ret)
goto err_out;
}
adev->driver_data = pdata; adev->driver_data = pdata;
pdev = acpi_create_platform_device(adev); pdev = acpi_create_platform_device(adev);
if (!IS_ERR_OR_NULL(pdev)) { if (!IS_ERR_OR_NULL(pdev)) {

View File

@ -17,6 +17,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/pci.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include "internal.h" #include "internal.h"
@ -30,6 +31,22 @@ static const struct acpi_device_id forbidden_id_list[] = {
{"", 0}, {"", 0},
}; };
static void acpi_platform_fill_resource(struct acpi_device *adev,
const struct resource *src, struct resource *dest)
{
struct device *parent;
*dest = *src;
/*
* If the device has parent we need to take its resources into
* account as well because this device might consume part of those.
*/
parent = acpi_get_first_physical_node(adev->parent);
if (parent && dev_is_pci(parent))
dest->parent = pci_find_resource(to_pci_dev(parent), dest);
}
/** /**
* acpi_create_platform_device - Create platform device for ACPI device node * acpi_create_platform_device - Create platform device for ACPI device node
* @adev: ACPI device node to create a platform device for. * @adev: ACPI device node to create a platform device for.
@ -70,7 +87,8 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
} }
count = 0; count = 0;
list_for_each_entry(rentry, &resource_list, node) list_for_each_entry(rentry, &resource_list, node)
resources[count++] = *rentry->res; acpi_platform_fill_resource(adev, rentry->res,
&resources[count++]);
acpi_dev_free_resource_list(&resource_list); acpi_dev_free_resource_list(&resource_list);
} }

View File

@ -0,0 +1,123 @@
/*
* 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);
}

View File

@ -175,6 +175,7 @@ acpi-y += \
utresrc.o \ utresrc.o \
utstate.o \ utstate.o \
utstring.o \ utstring.o \
utstrtoul64.o \
utxface.o \ utxface.o \
utxfinit.o \ utxfinit.o \
utxferror.o \ utxferror.o \

View File

@ -44,7 +44,9 @@
#ifndef _ACAPPS #ifndef _ACAPPS
#define _ACAPPS #define _ACAPPS
#include <stdio.h> #ifdef ACPI_USE_STANDARD_HEADERS
#include <sys/stat.h>
#endif /* ACPI_USE_STANDARD_HEADERS */
/* Common info for tool signons */ /* Common info for tool signons */
@ -81,13 +83,13 @@
/* Macros for usage messages */ /* Macros for usage messages */
#define ACPI_USAGE_HEADER(usage) \ #define ACPI_USAGE_HEADER(usage) \
acpi_os_printf ("Usage: %s\nOptions:\n", usage); printf ("Usage: %s\nOptions:\n", usage);
#define ACPI_USAGE_TEXT(description) \ #define ACPI_USAGE_TEXT(description) \
acpi_os_printf (description); printf (description);
#define ACPI_OPTION(name, description) \ #define ACPI_OPTION(name, description) \
acpi_os_printf (" %-20s%s\n", name, description); printf (" %-20s%s\n", name, description);
/* Check for unexpected exceptions */ /* Check for unexpected exceptions */

View File

@ -155,7 +155,7 @@ acpi_status acpi_db_disassemble_method(char *name);
void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op); void acpi_db_disassemble_aml(char *statements, union acpi_parse_object *op);
void acpi_db_batch_execute(char *count_arg); void acpi_db_evaluate_predefined_names(void);
/* /*
* dbnames - namespace commands * dbnames - namespace commands

View File

@ -85,6 +85,9 @@ acpi_ev_update_gpe_enable_mask(struct acpi_gpe_event_info *gpe_event_info);
acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info); acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info);
acpi_status
acpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked);
acpi_status acpi_status
acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info); acpi_ev_add_gpe_reference(struct acpi_gpe_event_info *gpe_event_info);

View File

@ -317,6 +317,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_ignore_noop_operator, FALSE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_cstyle_disassembly, TRUE); ACPI_INIT_GLOBAL(u8, acpi_gbl_cstyle_disassembly, TRUE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_force_aml_disassembly, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_force_aml_disassembly, FALSE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_dm_opt_verbose, TRUE); ACPI_INIT_GLOBAL(u8, acpi_gbl_dm_opt_verbose, TRUE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_dm_emit_external_opcodes, FALSE);
ACPI_GLOBAL(u8, acpi_gbl_dm_opt_disasm); ACPI_GLOBAL(u8, acpi_gbl_dm_opt_disasm);
ACPI_GLOBAL(u8, acpi_gbl_dm_opt_listing); ACPI_GLOBAL(u8, acpi_gbl_dm_opt_listing);
@ -382,6 +383,7 @@ ACPI_GLOBAL(const char, *acpi_gbl_pld_shape_list[]);
ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_debug_file, NULL); ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_debug_file, NULL);
ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_output_file, NULL); ACPI_INIT_GLOBAL(ACPI_FILE, acpi_gbl_output_file, NULL);
ACPI_INIT_GLOBAL(u8, acpi_gbl_debug_timeout, FALSE);
/* Print buffer */ /* Print buffer */

View File

@ -484,6 +484,7 @@ struct acpi_gpe_event_info {
u8 flags; /* Misc info about this GPE */ u8 flags; /* Misc info about this GPE */
u8 gpe_number; /* This GPE */ u8 gpe_number; /* This GPE */
u8 runtime_count; /* References to a run GPE */ u8 runtime_count; /* References to a run GPE */
u8 disable_for_dispatch; /* Masked during dispatching */
}; };
/* Information about a GPE register pair, one per each status/enable pair in an array */ /* Information about a GPE register pair, one per each status/enable pair in an array */
@ -494,6 +495,7 @@ struct acpi_gpe_register_info {
u16 base_gpe_number; /* Base GPE number for this register */ u16 base_gpe_number; /* Base GPE number for this register */
u8 enable_for_wake; /* GPEs to keep enabled when sleeping */ u8 enable_for_wake; /* GPEs to keep enabled when sleeping */
u8 enable_for_run; /* GPEs to keep enabled when running */ u8 enable_for_run; /* GPEs to keep enabled when running */
u8 mask_for_run; /* GPEs to keep masked when running */
u8 enable_mask; /* Current mask of enabled GPEs */ u8 enable_mask; /* Current mask of enabled GPEs */
}; };

View File

@ -129,6 +129,9 @@ struct acpi_namespace_node *acpi_ns_get_next_node_typed(acpi_object_type type,
acpi_status acpi_status
acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node); acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node);
acpi_status
acpi_ns_execute_table(u32 table_index, struct acpi_namespace_node *start_node);
acpi_status acpi_status
acpi_ns_one_complete_parse(u32 pass_number, acpi_ns_one_complete_parse(u32 pass_number,
u32 table_index, u32 table_index,
@ -295,6 +298,11 @@ acpi_ns_handle_to_pathname(acpi_handle target_handle,
u8 u8
acpi_ns_pattern_match(struct acpi_namespace_node *obj_node, char *search_for); acpi_ns_pattern_match(struct acpi_namespace_node *obj_node, char *search_for);
acpi_status
acpi_ns_get_node_unlocked(struct acpi_namespace_node *prefix_node,
const char *external_pathname,
u32 flags, struct acpi_namespace_node **out_node);
acpi_status acpi_status
acpi_ns_get_node(struct acpi_namespace_node *prefix_node, acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
const char *external_pathname, const char *external_pathname,

View File

@ -78,6 +78,8 @@ extern const u8 acpi_gbl_long_op_index[];
*/ */
acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info); acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info);
acpi_status acpi_ps_execute_table(struct acpi_evaluate_info *info);
/* /*
* psargs - Parse AML opcode arguments * psargs - Parse AML opcode arguments
*/ */

View File

@ -123,6 +123,14 @@ acpi_tb_install_standard_table(acpi_physical_address address,
void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc); void acpi_tb_uninstall_table(struct acpi_table_desc *table_desc);
acpi_status
acpi_tb_load_table(u32 table_index, struct acpi_namespace_node *parent_node);
acpi_status
acpi_tb_install_and_load_table(struct acpi_table_header *table,
acpi_physical_address address,
u8 flags, u8 override, u32 *table_index);
void acpi_tb_terminate(void); void acpi_tb_terminate(void);
acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index); acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index);
@ -155,10 +163,6 @@ void
acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc, acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc,
u8 override, u32 *table_index); u8 override, u32 *table_index);
acpi_status
acpi_tb_install_fixed_table(acpi_physical_address address,
char *signature, u32 *table_index);
acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address); acpi_status acpi_tb_parse_root_table(acpi_physical_address rsdp_address);
/* /*

View File

@ -114,13 +114,25 @@ extern const char *acpi_gbl_pt_decode[];
/* /*
* Common error message prefixes * Common error message prefixes
*/ */
#ifndef ACPI_MSG_ERROR
#define ACPI_MSG_ERROR "ACPI Error: " #define ACPI_MSG_ERROR "ACPI Error: "
#endif
#ifndef ACPI_MSG_EXCEPTION
#define ACPI_MSG_EXCEPTION "ACPI Exception: " #define ACPI_MSG_EXCEPTION "ACPI Exception: "
#endif
#ifndef ACPI_MSG_WARNING
#define ACPI_MSG_WARNING "ACPI Warning: " #define ACPI_MSG_WARNING "ACPI Warning: "
#endif
#ifndef ACPI_MSG_INFO
#define ACPI_MSG_INFO "ACPI: " #define ACPI_MSG_INFO "ACPI: "
#endif
#ifndef ACPI_MSG_BIOS_ERROR
#define ACPI_MSG_BIOS_ERROR "ACPI BIOS Error (bug): " #define ACPI_MSG_BIOS_ERROR "ACPI BIOS Error (bug): "
#endif
#ifndef ACPI_MSG_BIOS_WARNING
#define ACPI_MSG_BIOS_WARNING "ACPI BIOS Warning (bug): " #define ACPI_MSG_BIOS_WARNING "ACPI BIOS Warning (bug): "
#endif
/* /*
* Common message suffix * Common message suffix
@ -184,14 +196,15 @@ void acpi_ut_strlwr(char *src_string);
int acpi_ut_stricmp(char *string1, char *string2); int acpi_ut_stricmp(char *string1, char *string2);
acpi_status acpi_status acpi_ut_strtoul64(char *string, u32 flags, u64 *ret_integer);
acpi_ut_strtoul64(char *string,
u32 base, u32 max_integer_byte_width, u64 *ret_integer);
/* Values for max_integer_byte_width above */ /*
* Values for Flags above
#define ACPI_MAX32_BYTE_WIDTH 4 * Note: LIMIT values correspond to acpi_gbl_integer_byte_width values (4/8)
#define ACPI_MAX64_BYTE_WIDTH 8 */
#define ACPI_STRTOUL_32BIT 0x04 /* 4 bytes */
#define ACPI_STRTOUL_64BIT 0x08 /* 8 bytes */
#define ACPI_STRTOUL_BASE16 0x10 /* Default: Base10/16 */
/* /*
* utglobal - Global data structures and procedures * utglobal - Global data structures and procedures
@ -221,6 +234,8 @@ const char *acpi_ut_get_event_name(u32 event_id);
char acpi_ut_hex_to_ascii_char(u64 integer, u32 position); char acpi_ut_hex_to_ascii_char(u64 integer, u32 position);
acpi_status acpi_ut_ascii_to_hex_byte(char *two_ascii_chars, u8 *return_byte);
u8 acpi_ut_ascii_char_to_hex(int hex_char); u8 acpi_ut_ascii_char_to_hex(int hex_char);
u8 acpi_ut_valid_object_type(acpi_object_type type); u8 acpi_ut_valid_object_type(acpi_object_type type);
@ -317,6 +332,11 @@ acpi_ut_ptr_exit(u32 line_number,
const char *function_name, const char *function_name,
const char *module_name, u32 component_id, u8 *ptr); const char *module_name, u32 component_id, u8 *ptr);
void
acpi_ut_str_exit(u32 line_number,
const char *function_name,
const char *module_name, u32 component_id, const char *string);
void void
acpi_ut_debug_dump_buffer(u8 *buffer, u32 count, u32 display, u32 component_id); acpi_ut_debug_dump_buffer(u8 *buffer, u32 count, u32 display, u32 component_id);
@ -706,25 +726,6 @@ const struct ah_device_id *acpi_ah_match_hardware_id(char *hid);
const char *acpi_ah_match_uuid(u8 *data); const char *acpi_ah_match_uuid(u8 *data);
/*
* utprint - printf/vprintf output functions
*/
const char *acpi_ut_scan_number(const char *string, u64 *number_ptr);
const char *acpi_ut_print_number(char *string, u64 number);
int
acpi_ut_vsnprintf(char *string,
acpi_size size, const char *format, va_list args);
int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...);
#ifdef ACPI_APPLICATION
int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args);
int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...);
#endif
/* /*
* utuuid -- UUID support functions * utuuid -- UUID support functions
*/ */

View File

@ -277,9 +277,10 @@ acpi_db_convert_to_object(acpi_object_type type,
default: default:
object->type = ACPI_TYPE_INTEGER; object->type = ACPI_TYPE_INTEGER;
status = status = acpi_ut_strtoul64(string,
acpi_ut_strtoul64(string, 16, acpi_gbl_integer_byte_width, (acpi_gbl_integer_byte_width |
&object->integer.value); ACPI_STRTOUL_BASE16),
&object->integer.value);
break; break;
} }

View File

@ -392,43 +392,49 @@ acpi_db_execute(char *name, char **args, acpi_object_type *types, u32 flags)
acpi_db_execution_walk, NULL, NULL, acpi_db_execution_walk, NULL, NULL,
NULL); NULL);
return; return;
} else {
name_string = ACPI_ALLOCATE(strlen(name) + 1);
if (!name_string) {
return;
}
memset(&acpi_gbl_db_method_info, 0,
sizeof(struct acpi_db_method_info));
strcpy(name_string, name);
acpi_ut_strupr(name_string);
acpi_gbl_db_method_info.name = name_string;
acpi_gbl_db_method_info.args = args;
acpi_gbl_db_method_info.types = types;
acpi_gbl_db_method_info.flags = flags;
return_obj.pointer = NULL;
return_obj.length = ACPI_ALLOCATE_BUFFER;
status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
if (ACPI_FAILURE(status)) {
ACPI_FREE(name_string);
return;
}
/* Get the NS node, determines existence also */
status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
&acpi_gbl_db_method_info.method);
if (ACPI_SUCCESS(status)) {
status =
acpi_db_execute_method(&acpi_gbl_db_method_info,
&return_obj);
}
ACPI_FREE(name_string);
} }
name_string = ACPI_ALLOCATE(strlen(name) + 1);
if (!name_string) {
return;
}
memset(&acpi_gbl_db_method_info, 0, sizeof(struct acpi_db_method_info));
strcpy(name_string, name);
acpi_ut_strupr(name_string);
/* Subcommand to Execute all predefined names in the namespace */
if (!strncmp(name_string, "PREDEF", 6)) {
acpi_db_evaluate_predefined_names();
ACPI_FREE(name_string);
return;
}
acpi_gbl_db_method_info.name = name_string;
acpi_gbl_db_method_info.args = args;
acpi_gbl_db_method_info.types = types;
acpi_gbl_db_method_info.flags = flags;
return_obj.pointer = NULL;
return_obj.length = ACPI_ALLOCATE_BUFFER;
status = acpi_db_execute_setup(&acpi_gbl_db_method_info);
if (ACPI_FAILURE(status)) {
ACPI_FREE(name_string);
return;
}
/* Get the NS node, determines existence also */
status = acpi_get_handle(NULL, acpi_gbl_db_method_info.pathname,
&acpi_gbl_db_method_info.method);
if (ACPI_SUCCESS(status)) {
status = acpi_db_execute_method(&acpi_gbl_db_method_info,
&return_obj);
}
ACPI_FREE(name_string);
/* /*
* Allow any handlers in separate threads to complete. * Allow any handlers in separate threads to complete.
* (Such as Notify handlers invoked from AML executed above). * (Such as Notify handlers invoked from AML executed above).

View File

@ -46,14 +46,12 @@
#include "accommon.h" #include "accommon.h"
#include "acdebug.h" #include "acdebug.h"
#include "actables.h" #include "actables.h"
#include <stdio.h>
#ifdef ACPI_APPLICATION
#include "acapps.h"
#endif
#define _COMPONENT ACPI_CA_DEBUGGER #define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME("dbfileio") ACPI_MODULE_NAME("dbfileio")
#ifdef ACPI_APPLICATION
#include "acapps.h"
#ifdef ACPI_DEBUGGER #ifdef ACPI_DEBUGGER
/******************************************************************************* /*******************************************************************************
* *
@ -69,8 +67,6 @@ ACPI_MODULE_NAME("dbfileio")
void acpi_db_close_debug_file(void) void acpi_db_close_debug_file(void)
{ {
#ifdef ACPI_APPLICATION
if (acpi_gbl_debug_file) { if (acpi_gbl_debug_file) {
fclose(acpi_gbl_debug_file); fclose(acpi_gbl_debug_file);
acpi_gbl_debug_file = NULL; acpi_gbl_debug_file = NULL;
@ -78,7 +74,6 @@ void acpi_db_close_debug_file(void)
acpi_os_printf("Debug output file %s closed\n", acpi_os_printf("Debug output file %s closed\n",
acpi_gbl_db_debug_filename); acpi_gbl_db_debug_filename);
} }
#endif
} }
/******************************************************************************* /*******************************************************************************
@ -96,8 +91,6 @@ void acpi_db_close_debug_file(void)
void acpi_db_open_debug_file(char *name) void acpi_db_open_debug_file(char *name)
{ {
#ifdef ACPI_APPLICATION
acpi_db_close_debug_file(); acpi_db_close_debug_file();
acpi_gbl_debug_file = fopen(name, "w+"); acpi_gbl_debug_file = fopen(name, "w+");
if (!acpi_gbl_debug_file) { if (!acpi_gbl_debug_file) {
@ -109,8 +102,6 @@ void acpi_db_open_debug_file(char *name)
strncpy(acpi_gbl_db_debug_filename, name, strncpy(acpi_gbl_db_debug_filename, name,
sizeof(acpi_gbl_db_debug_filename)); sizeof(acpi_gbl_db_debug_filename));
acpi_gbl_db_output_to_file = TRUE; acpi_gbl_db_output_to_file = TRUE;
#endif
} }
#endif #endif
@ -152,12 +143,13 @@ acpi_status acpi_db_load_tables(struct acpi_new_table_desc *list_head)
return (status); return (status);
} }
fprintf(stderr, acpi_os_printf
"Acpi table [%4.4s] successfully installed and loaded\n", ("Acpi table [%4.4s] successfully installed and loaded\n",
table->signature); table->signature);
table_list_head = table_list_head->next; table_list_head = table_list_head->next;
} }
return (AE_OK); return (AE_OK);
} }
#endif

View File

@ -286,6 +286,8 @@ static const struct acpi_db_command_help acpi_gbl_db_command_help[] = {
{1, " \"Ascii String\"", "String method argument\n"}, {1, " \"Ascii String\"", "String method argument\n"},
{1, " (Hex Byte List)", "Buffer method argument\n"}, {1, " (Hex Byte List)", "Buffer method argument\n"},
{1, " [Package Element List]", "Package method argument\n"}, {1, " [Package Element List]", "Package method argument\n"},
{5, " Execute predefined",
"Execute all predefined (public) methods\n"},
{1, " Go", "Allow method to run to completion\n"}, {1, " Go", "Allow method to run to completion\n"},
{1, " Information", "Display info about the current method\n"}, {1, " Information", "Display info about the current method\n"},
{1, " Into", "Step into (not over) a method call\n"}, {1, " Into", "Step into (not over) a method call\n"},

View File

@ -52,6 +52,11 @@
#define _COMPONENT ACPI_CA_DEBUGGER #define _COMPONENT ACPI_CA_DEBUGGER
ACPI_MODULE_NAME("dbmethod") ACPI_MODULE_NAME("dbmethod")
/* Local prototypes */
static acpi_status
acpi_db_walk_for_execute(acpi_handle obj_handle,
u32 nesting_level, void *context, void **return_value);
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_db_set_method_breakpoint * FUNCTION: acpi_db_set_method_breakpoint
@ -66,6 +71,7 @@ ACPI_MODULE_NAME("dbmethod")
* AML offset * AML offset
* *
******************************************************************************/ ******************************************************************************/
void void
acpi_db_set_method_breakpoint(char *location, acpi_db_set_method_breakpoint(char *location,
struct acpi_walk_state *walk_state, struct acpi_walk_state *walk_state,
@ -367,3 +373,129 @@ acpi_status acpi_db_disassemble_method(char *name)
acpi_ut_release_owner_id(&obj_desc->method.owner_id); acpi_ut_release_owner_id(&obj_desc->method.owner_id);
return (AE_OK); return (AE_OK);
} }
/*******************************************************************************
*
* FUNCTION: acpi_db_walk_for_execute
*
* PARAMETERS: Callback from walk_namespace
*
* RETURN: Status
*
* DESCRIPTION: Batch execution module. Currently only executes predefined
* ACPI names.
*
******************************************************************************/
static acpi_status
acpi_db_walk_for_execute(acpi_handle obj_handle,
u32 nesting_level, void *context, void **return_value)
{
struct acpi_namespace_node *node =
(struct acpi_namespace_node *)obj_handle;
struct acpi_db_execute_walk *info =
(struct acpi_db_execute_walk *)context;
struct acpi_buffer return_obj;
acpi_status status;
char *pathname;
u32 i;
struct acpi_device_info *obj_info;
struct acpi_object_list param_objects;
union acpi_object params[ACPI_METHOD_NUM_ARGS];
const union acpi_predefined_info *predefined;
predefined = acpi_ut_match_predefined_method(node->name.ascii);
if (!predefined) {
return (AE_OK);
}
if (node->type == ACPI_TYPE_LOCAL_SCOPE) {
return (AE_OK);
}
pathname = acpi_ns_get_external_pathname(node);
if (!pathname) {
return (AE_OK);
}
/* Get the object info for number of method parameters */
status = acpi_get_object_info(obj_handle, &obj_info);
if (ACPI_FAILURE(status)) {
return (status);
}
param_objects.pointer = NULL;
param_objects.count = 0;
if (obj_info->type == ACPI_TYPE_METHOD) {
/* Setup default parameters */
for (i = 0; i < obj_info->param_count; i++) {
params[i].type = ACPI_TYPE_INTEGER;
params[i].integer.value = 1;
}
param_objects.pointer = params;
param_objects.count = obj_info->param_count;
}
ACPI_FREE(obj_info);
return_obj.pointer = NULL;
return_obj.length = ACPI_ALLOCATE_BUFFER;
/* Do the actual method execution */
acpi_gbl_method_executing = TRUE;
status = acpi_evaluate_object(node, NULL, &param_objects, &return_obj);
acpi_os_printf("%-32s returned %s\n", pathname,
acpi_format_exception(status));
acpi_gbl_method_executing = FALSE;
ACPI_FREE(pathname);
/* Ignore status from method execution */
status = AE_OK;
/* Update count, check if we have executed enough methods */
info->count++;
if (info->count >= info->max_count) {
status = AE_CTRL_TERMINATE;
}
return (status);
}
/*******************************************************************************
*
* FUNCTION: acpi_db_evaluate_predefined_names
*
* PARAMETERS: None
*
* RETURN: None
*
* DESCRIPTION: Namespace batch execution. Execute predefined names in the
* namespace, up to the max count, if specified.
*
******************************************************************************/
void acpi_db_evaluate_predefined_names(void)
{
struct acpi_db_execute_walk info;
info.count = 0;
info.max_count = ACPI_UINT32_MAX;
/* Search all nodes in namespace */
(void)acpi_walk_namespace(ACPI_TYPE_ANY, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX, acpi_db_walk_for_execute,
NULL, (void *)&info, NULL);
acpi_os_printf("Evaluated %u predefined names in the namespace\n",
info.count);
}

View File

@ -142,11 +142,11 @@ void acpi_db_decode_internal_object(union acpi_operand_object *obj_desc)
case ACPI_TYPE_STRING: case ACPI_TYPE_STRING:
acpi_os_printf("(%u) \"%.24s", acpi_os_printf("(%u) \"%.60s",
obj_desc->string.length, obj_desc->string.length,
obj_desc->string.pointer); obj_desc->string.pointer);
if (obj_desc->string.length > 24) { if (obj_desc->string.length > 60) {
acpi_os_printf("..."); acpi_os_printf("...");
} else { } else {
acpi_os_printf("\""); acpi_os_printf("\"");

View File

@ -99,11 +99,14 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
"Method auto-serialization parse [%4.4s] %p\n", "Method auto-serialization parse [%4.4s] %p\n",
acpi_ut_get_node_name(node), node)); acpi_ut_get_node_name(node), node));
acpi_ex_enter_interpreter();
/* Create/Init a root op for the method parse tree */ /* Create/Init a root op for the method parse tree */
op = acpi_ps_alloc_op(AML_METHOD_OP, obj_desc->method.aml_start); op = acpi_ps_alloc_op(AML_METHOD_OP, obj_desc->method.aml_start);
if (!op) { if (!op) {
return_ACPI_STATUS(AE_NO_MEMORY); status = AE_NO_MEMORY;
goto unlock;
} }
acpi_ps_set_name(op, node->name.integer); acpi_ps_set_name(op, node->name.integer);
@ -115,7 +118,8 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
acpi_ds_create_walk_state(node->owner_id, NULL, NULL, NULL); acpi_ds_create_walk_state(node->owner_id, NULL, NULL, NULL);
if (!walk_state) { if (!walk_state) {
acpi_ps_free_op(op); acpi_ps_free_op(op);
return_ACPI_STATUS(AE_NO_MEMORY); status = AE_NO_MEMORY;
goto unlock;
} }
status = acpi_ds_init_aml_walk(walk_state, op, node, status = acpi_ds_init_aml_walk(walk_state, op, node,
@ -134,6 +138,8 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node,
status = acpi_ps_parse_aml(walk_state); status = acpi_ps_parse_aml(walk_state);
acpi_ps_delete_parse_tree(op); acpi_ps_delete_parse_tree(op);
unlock:
acpi_ex_exit_interpreter();
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -757,8 +763,10 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
/* Delete any direct children of (created by) this method */ /* Delete any direct children of (created by) this method */
(void)acpi_ex_exit_interpreter();
acpi_ns_delete_namespace_subtree(walk_state-> acpi_ns_delete_namespace_subtree(walk_state->
method_node); method_node);
(void)acpi_ex_enter_interpreter();
/* /*
* Delete any objects that were created by this method * Delete any objects that were created by this method
@ -769,9 +777,11 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
*/ */
if (method_desc->method. if (method_desc->method.
info_flags & ACPI_METHOD_MODIFIED_NAMESPACE) { info_flags & ACPI_METHOD_MODIFIED_NAMESPACE) {
(void)acpi_ex_exit_interpreter();
acpi_ns_delete_namespace_by_owner(method_desc-> acpi_ns_delete_namespace_by_owner(method_desc->
method. method.
owner_id); owner_id);
(void)acpi_ex_enter_interpreter();
method_desc->method.info_flags &= method_desc->method.info_flags &=
~ACPI_METHOD_MODIFIED_NAMESPACE; ~ACPI_METHOD_MODIFIED_NAMESPACE;
} }

View File

@ -565,15 +565,14 @@ acpi_ds_create_operand(struct acpi_walk_state *walk_state,
status = AE_OK; status = AE_OK;
} else if (parent_op->common.aml_opcode == } else if (parent_op->common.aml_opcode ==
AML_EXTERNAL_OP) { AML_EXTERNAL_OP) {
/*
/* TBD: May only be temporary */ * This opcode should never appear here. It is used only
* by AML disassemblers and is surrounded by an If(0)
obj_desc = * by the ASL compiler.
acpi_ut_create_string_object((acpi_size)name_length); *
* Therefore, if we see it here, it is a serious error.
strncpy(obj_desc->string.pointer, */
name_string, name_length); status = AE_AML_BAD_OPCODE;
status = AE_OK;
} else { } else {
/* /*
* We just plain didn't find it -- which is a * We just plain didn't find it -- which is a

View File

@ -133,7 +133,8 @@ acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state,
* Result of predicate evaluation must be an Integer * Result of predicate evaluation must be an Integer
* object. Implicitly convert the argument if necessary. * object. Implicitly convert the argument if necessary.
*/ */
status = acpi_ex_convert_to_integer(obj_desc, &local_obj_desc, 16); status = acpi_ex_convert_to_integer(obj_desc, &local_obj_desc,
ACPI_STRTOUL_BASE16);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
goto cleanup; goto cleanup;
} }

View File

@ -605,16 +605,13 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
acpi_ex_exit_interpreter();
} }
acpi_ex_exit_interpreter();
status = status =
acpi_ev_initialize_region acpi_ev_initialize_region
(acpi_ns_get_attached_object(node), FALSE); (acpi_ns_get_attached_object(node), FALSE);
if (walk_state->method_node) { acpi_ex_enter_interpreter();
acpi_ex_enter_interpreter();
}
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
/* /*

View File

@ -128,6 +128,60 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info)
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
/*******************************************************************************
*
* FUNCTION: acpi_ev_mask_gpe
*
* PARAMETERS: gpe_event_info - GPE to be blocked/unblocked
* is_masked - Whether the GPE is masked or not
*
* RETURN: Status
*
* DESCRIPTION: Unconditionally mask/unmask a GPE during runtime.
*
******************************************************************************/
acpi_status
acpi_ev_mask_gpe(struct acpi_gpe_event_info *gpe_event_info, u8 is_masked)
{
struct acpi_gpe_register_info *gpe_register_info;
u32 register_bit;
ACPI_FUNCTION_TRACE(ev_mask_gpe);
gpe_register_info = gpe_event_info->register_info;
if (!gpe_register_info) {
return_ACPI_STATUS(AE_NOT_EXIST);
}
register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info);
/* Perform the action */
if (is_masked) {
if (register_bit & gpe_register_info->mask_for_run) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
(void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
ACPI_SET_BIT(gpe_register_info->mask_for_run, (u8)register_bit);
} else {
if (!(register_bit & gpe_register_info->mask_for_run)) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
ACPI_CLEAR_BIT(gpe_register_info->mask_for_run,
(u8)register_bit);
if (gpe_event_info->runtime_count
&& !gpe_event_info->disable_for_dispatch) {
(void)acpi_hw_low_set_gpe(gpe_event_info,
ACPI_GPE_ENABLE);
}
}
return_ACPI_STATUS(AE_OK);
}
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_ev_add_gpe_reference * FUNCTION: acpi_ev_add_gpe_reference
@ -674,6 +728,7 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info *gpe_event_info)
* in the event_info. * in the event_info.
*/ */
(void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE); (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE);
gpe_event_info->disable_for_dispatch = FALSE;
return (AE_OK); return (AE_OK);
} }
@ -737,6 +792,8 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device,
} }
} }
gpe_event_info->disable_for_dispatch = TRUE;
/* /*
* Dispatch the GPE to either an installed handler or the control * Dispatch the GPE to either an installed handler or the control
* method associated with this GPE (_Lxx or _Exx). If a handler * method associated with this GPE (_Lxx or _Exx). If a handler

View File

@ -323,7 +323,9 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
struct acpi_gpe_walk_info *walk_info = struct acpi_gpe_walk_info *walk_info =
ACPI_CAST_PTR(struct acpi_gpe_walk_info, context); ACPI_CAST_PTR(struct acpi_gpe_walk_info, context);
struct acpi_gpe_event_info *gpe_event_info; struct acpi_gpe_event_info *gpe_event_info;
acpi_status status;
u32 gpe_number; u32 gpe_number;
u8 temp_gpe_number;
char name[ACPI_NAME_SIZE + 1]; char name[ACPI_NAME_SIZE + 1];
u8 type; u8 type;
@ -377,8 +379,8 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
/* 4) The last two characters of the name are the hex GPE Number */ /* 4) The last two characters of the name are the hex GPE Number */
gpe_number = strtoul(&name[2], NULL, 16); status = acpi_ut_ascii_to_hex_byte(&name[2], &temp_gpe_number);
if (gpe_number == ACPI_UINT32_MAX) { if (ACPI_FAILURE(status)) {
/* Conversion failed; invalid method, just ignore it */ /* Conversion failed; invalid method, just ignore it */
@ -390,6 +392,7 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle,
/* Ensure that we have a valid GPE number for this GPE block */ /* Ensure that we have a valid GPE number for this GPE block */
gpe_number = (u32)temp_gpe_number;
gpe_event_info = gpe_event_info =
acpi_ev_low_get_gpe_info(gpe_number, walk_info->gpe_block); acpi_ev_low_get_gpe_info(gpe_number, walk_info->gpe_block);
if (!gpe_event_info) { if (!gpe_event_info) {

View File

@ -553,7 +553,8 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,
* *
* See acpi_ns_exec_module_code * See acpi_ns_exec_module_code
*/ */
if (obj_desc->method. if (!acpi_gbl_parse_table_as_term_list &&
obj_desc->method.
info_flags & ACPI_METHOD_MODULE_LEVEL) { info_flags & ACPI_METHOD_MODULE_LEVEL) {
handler_obj = handler_obj =
obj_desc->method.dispatch.handler; obj_desc->method.dispatch.handler;

View File

@ -235,11 +235,13 @@ acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action)
case ACPI_GPE_ENABLE: case ACPI_GPE_ENABLE:
status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE);
gpe_event_info->disable_for_dispatch = FALSE;
break; break;
case ACPI_GPE_DISABLE: case ACPI_GPE_DISABLE:
status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE);
gpe_event_info->disable_for_dispatch = TRUE;
break; break;
default: default:
@ -255,6 +257,47 @@ unlock_and_exit:
ACPI_EXPORT_SYMBOL(acpi_set_gpe) ACPI_EXPORT_SYMBOL(acpi_set_gpe)
/*******************************************************************************
*
* FUNCTION: acpi_mask_gpe
*
* PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1
* gpe_number - GPE level within the GPE block
* is_masked - Whether the GPE is masked or not
*
* RETURN: Status
*
* DESCRIPTION: Unconditionally mask/unmask the an individual GPE, ex., to
* prevent a GPE flooding.
*
******************************************************************************/
acpi_status acpi_mask_gpe(acpi_handle gpe_device, u32 gpe_number, u8 is_masked)
{
struct acpi_gpe_event_info *gpe_event_info;
acpi_status status;
acpi_cpu_flags flags;
ACPI_FUNCTION_TRACE(acpi_mask_gpe);
flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
/* Ensure that we have a valid GPE number */
gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number);
if (!gpe_event_info) {
status = AE_BAD_PARAMETER;
goto unlock_and_exit;
}
status = acpi_ev_mask_gpe(gpe_event_info, is_masked);
unlock_and_exit:
acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL(acpi_mask_gpe)
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_mark_gpe_for_wake * FUNCTION: acpi_mark_gpe_for_wake

View File

@ -156,7 +156,7 @@ acpi_ex_do_concatenate(union acpi_operand_object *operand0,
status = status =
acpi_ex_convert_to_integer(local_operand1, &temp_operand1, acpi_ex_convert_to_integer(local_operand1, &temp_operand1,
16); ACPI_STRTOUL_BASE16);
break; break;
case ACPI_TYPE_BUFFER: case ACPI_TYPE_BUFFER:

View File

@ -55,9 +55,7 @@ ACPI_MODULE_NAME("exconfig")
/* Local prototypes */ /* Local prototypes */
static acpi_status static acpi_status
acpi_ex_add_table(u32 table_index, acpi_ex_add_table(u32 table_index, union acpi_operand_object **ddb_handle);
struct acpi_namespace_node *parent_node,
union acpi_operand_object **ddb_handle);
static acpi_status static acpi_status
acpi_ex_region_read(union acpi_operand_object *obj_desc, acpi_ex_region_read(union acpi_operand_object *obj_desc,
@ -79,13 +77,9 @@ acpi_ex_region_read(union acpi_operand_object *obj_desc,
******************************************************************************/ ******************************************************************************/
static acpi_status static acpi_status
acpi_ex_add_table(u32 table_index, acpi_ex_add_table(u32 table_index, union acpi_operand_object **ddb_handle)
struct acpi_namespace_node *parent_node,
union acpi_operand_object **ddb_handle)
{ {
union acpi_operand_object *obj_desc; union acpi_operand_object *obj_desc;
acpi_status status;
acpi_owner_id owner_id;
ACPI_FUNCTION_TRACE(ex_add_table); ACPI_FUNCTION_TRACE(ex_add_table);
@ -100,39 +94,8 @@ acpi_ex_add_table(u32 table_index,
obj_desc->common.flags |= AOPOBJ_DATA_VALID; obj_desc->common.flags |= AOPOBJ_DATA_VALID;
obj_desc->reference.class = ACPI_REFCLASS_TABLE; obj_desc->reference.class = ACPI_REFCLASS_TABLE;
*ddb_handle = obj_desc;
/* Install the new table into the local data structures */
obj_desc->reference.value = table_index; obj_desc->reference.value = table_index;
*ddb_handle = obj_desc;
/* Add the table to the namespace */
status = acpi_ns_load_table(table_index, parent_node);
if (ACPI_FAILURE(status)) {
acpi_ut_remove_reference(obj_desc);
*ddb_handle = NULL;
return_ACPI_STATUS(status);
}
/* Execute any module-level code that was found in the table */
acpi_ex_exit_interpreter();
if (acpi_gbl_group_module_level_code) {
acpi_ns_exec_module_code_list();
}
acpi_ex_enter_interpreter();
/*
* Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is
* responsible for discovering any new wake GPEs by running _PRW methods
* that may have been loaded by this table.
*/
status = acpi_tb_get_owner_id(table_index, &owner_id);
if (ACPI_SUCCESS(status)) {
acpi_ev_update_gpes(owner_id);
}
return_ACPI_STATUS(AE_OK); return_ACPI_STATUS(AE_OK);
} }
@ -159,16 +122,17 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
struct acpi_namespace_node *start_node; struct acpi_namespace_node *start_node;
struct acpi_namespace_node *parameter_node = NULL; struct acpi_namespace_node *parameter_node = NULL;
union acpi_operand_object *ddb_handle; union acpi_operand_object *ddb_handle;
struct acpi_table_header *table;
u32 table_index; u32 table_index;
ACPI_FUNCTION_TRACE(ex_load_table_op); ACPI_FUNCTION_TRACE(ex_load_table_op);
/* Find the ACPI table in the RSDT/XSDT */ /* Find the ACPI table in the RSDT/XSDT */
acpi_ex_exit_interpreter();
status = acpi_tb_find_table(operand[0]->string.pointer, status = acpi_tb_find_table(operand[0]->string.pointer,
operand[1]->string.pointer, operand[1]->string.pointer,
operand[2]->string.pointer, &table_index); operand[2]->string.pointer, &table_index);
acpi_ex_enter_interpreter();
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND) { if (status != AE_NOT_FOUND) {
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
@ -197,9 +161,10 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
* Find the node referenced by the root_path_string. This is the * Find the node referenced by the root_path_string. This is the
* location within the namespace where the table will be loaded. * location within the namespace where the table will be loaded.
*/ */
status = status = acpi_ns_get_node_unlocked(start_node,
acpi_ns_get_node(start_node, operand[3]->string.pointer, operand[3]->string.pointer,
ACPI_NS_SEARCH_PARENT, &parent_node); ACPI_NS_SEARCH_PARENT,
&parent_node);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -219,9 +184,10 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
/* Find the node referenced by the parameter_path_string */ /* Find the node referenced by the parameter_path_string */
status = status = acpi_ns_get_node_unlocked(start_node,
acpi_ns_get_node(start_node, operand[4]->string.pointer, operand[4]->string.pointer,
ACPI_NS_SEARCH_PARENT, &parameter_node); ACPI_NS_SEARCH_PARENT,
&parameter_node);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -229,7 +195,15 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
/* Load the table into the namespace */ /* Load the table into the namespace */
status = acpi_ex_add_table(table_index, parent_node, &ddb_handle); ACPI_INFO(("Dynamic OEM Table Load:"));
acpi_ex_exit_interpreter();
status = acpi_tb_load_table(table_index, parent_node);
acpi_ex_enter_interpreter();
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
status = acpi_ex_add_table(table_index, &ddb_handle);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -252,19 +226,6 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
} }
} }
status = acpi_get_table_by_index(table_index, &table);
if (ACPI_SUCCESS(status)) {
ACPI_INFO(("Dynamic OEM Table Load:"));
acpi_tb_print_table_header(0, table);
}
/* Invoke table handler if present */
if (acpi_gbl_table_handler) {
(void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
acpi_gbl_table_handler_context);
}
*return_desc = ddb_handle; *return_desc = ddb_handle;
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -475,13 +436,12 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
/* Install the new table into the local data structures */ /* Install the new table into the local data structures */
ACPI_INFO(("Dynamic OEM Table Load:")); ACPI_INFO(("Dynamic OEM Table Load:"));
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); acpi_ex_exit_interpreter();
status =
status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table), acpi_tb_install_and_load_table(table, ACPI_PTR_TO_PHYSADDR(table),
ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL, ACPI_TABLE_ORIGIN_INTERNAL_VIRTUAL,
TRUE, TRUE, &table_index); TRUE, &table_index);
acpi_ex_enter_interpreter();
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
/* Delete allocated table buffer */ /* Delete allocated table buffer */
@ -490,17 +450,6 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
/*
* Note: Now table is "INSTALLED", it must be validated before
* loading.
*/
status =
acpi_tb_validate_table(&acpi_gbl_root_table_list.
tables[table_index]);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* /*
* Add the table to the namespace. * Add the table to the namespace.
* *
@ -508,8 +457,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
* This appears to go against the ACPI specification, but we do it for * This appears to go against the ACPI specification, but we do it for
* compatibility with other ACPI implementations. * compatibility with other ACPI implementations.
*/ */
status = status = acpi_ex_add_table(table_index, &ddb_handle);
acpi_ex_add_table(table_index, acpi_gbl_root_node, &ddb_handle);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
/* On error, table_ptr was deallocated above */ /* On error, table_ptr was deallocated above */
@ -532,14 +480,6 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
/* Remove the reference by added by acpi_ex_store above */ /* Remove the reference by added by acpi_ex_store above */
acpi_ut_remove_reference(ddb_handle); acpi_ut_remove_reference(ddb_handle);
/* Invoke table handler if present */
if (acpi_gbl_table_handler) {
(void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
acpi_gbl_table_handler_context);
}
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -592,10 +532,17 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
table_index = table_desc->reference.value; table_index = table_desc->reference.value;
/*
* Release the interpreter lock so that the table lock won't have
* strict order requirement against it.
*/
acpi_ex_exit_interpreter();
/* Ensure the table is still loaded */ /* Ensure the table is still loaded */
if (!acpi_tb_is_table_loaded(table_index)) { if (!acpi_tb_is_table_loaded(table_index)) {
return_ACPI_STATUS(AE_NOT_EXIST); status = AE_NOT_EXIST;
goto lock_and_exit;
} }
/* Invoke table handler if present */ /* Invoke table handler if present */
@ -613,16 +560,24 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
status = acpi_tb_delete_namespace_by_owner(table_index); status = acpi_tb_delete_namespace_by_owner(table_index);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); goto lock_and_exit;
} }
(void)acpi_tb_release_owner_id(table_index); (void)acpi_tb_release_owner_id(table_index);
acpi_tb_set_table_loaded_flag(table_index, FALSE); acpi_tb_set_table_loaded_flag(table_index, FALSE);
lock_and_exit:
/* Re-acquire the interpreter lock */
acpi_ex_enter_interpreter();
/* /*
* Invalidate the handle. We do this because the handle may be stored * Invalidate the handle. We do this because the handle may be stored
* in a named object and may not be actually deleted until much later. * in a named object and may not be actually deleted until much later.
*/ */
ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID; if (ACPI_SUCCESS(status)) {
return_ACPI_STATUS(AE_OK); ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID;
}
return_ACPI_STATUS(status);
} }

View File

@ -124,9 +124,9 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc,
* of ACPI 3.0) is that the to_integer() operator allows both decimal * of ACPI 3.0) is that the to_integer() operator allows both decimal
* and hexadecimal strings (hex prefixed with "0x"). * and hexadecimal strings (hex prefixed with "0x").
*/ */
status = acpi_ut_strtoul64((char *)pointer, flags, status = acpi_ut_strtoul64(ACPI_CAST_PTR(char, pointer),
acpi_gbl_integer_byte_width, (acpi_gbl_integer_byte_width |
&result); flags), &result);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -632,7 +632,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type,
*/ */
status = status =
acpi_ex_convert_to_integer(source_desc, result_desc, acpi_ex_convert_to_integer(source_desc, result_desc,
16); ACPI_STRTOUL_BASE16);
break; break;
case ACPI_TYPE_STRING: case ACPI_TYPE_STRING:

View File

@ -327,8 +327,8 @@ acpi_ex_do_logical_op(u16 opcode,
switch (operand0->common.type) { switch (operand0->common.type) {
case ACPI_TYPE_INTEGER: case ACPI_TYPE_INTEGER:
status = status = acpi_ex_convert_to_integer(operand1, &local_operand1,
acpi_ex_convert_to_integer(operand1, &local_operand1, 16); ACPI_STRTOUL_BASE16);
break; break;
case ACPI_TYPE_STRING: case ACPI_TYPE_STRING:

View File

@ -521,9 +521,10 @@ acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state)
case AML_TO_INTEGER_OP: /* to_integer (Data, Result) */ case AML_TO_INTEGER_OP: /* to_integer (Data, Result) */
/* Perform "explicit" conversion */
status = status =
acpi_ex_convert_to_integer(operand[0], &return_desc, acpi_ex_convert_to_integer(operand[0], &return_desc, 0);
ACPI_ANY_BASE);
if (return_desc == operand[0]) { if (return_desc == operand[0]) {
/* No conversion performed, add ref to handle return value */ /* No conversion performed, add ref to handle return value */
@ -890,14 +891,16 @@ acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state)
* Field, so we need to resolve the node to a value. * Field, so we need to resolve the node to a value.
*/ */
status = status =
acpi_ns_get_node(walk_state->scope_info-> acpi_ns_get_node_unlocked(walk_state->
scope.node, scope_info->scope.
operand[0]->string.pointer, node,
ACPI_NS_SEARCH_PARENT, operand[0]->
ACPI_CAST_INDIRECT_PTR string.pointer,
(struct ACPI_NS_SEARCH_PARENT,
acpi_namespace_node, ACPI_CAST_INDIRECT_PTR
&return_desc)); (struct
acpi_namespace_node,
&return_desc));
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
goto cleanup; goto cleanup;
} }

View File

@ -410,12 +410,13 @@ acpi_ex_resolve_operands(u16 opcode,
case ARGI_INTEGER: case ARGI_INTEGER:
/* /*
* Need an operand of type ACPI_TYPE_INTEGER, * Need an operand of type ACPI_TYPE_INTEGER, but we can
* But we can implicitly convert from a STRING or BUFFER * implicitly convert from a STRING or BUFFER.
* aka - "Implicit Source Operand Conversion" *
* Known as "Implicit Source Operand Conversion"
*/ */
status = status = acpi_ex_convert_to_integer(obj_desc, stack_ptr,
acpi_ex_convert_to_integer(obj_desc, stack_ptr, 16); ACPI_STRTOUL_BASE16);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
if (status == AE_TYPE) { if (status == AE_TYPE) {
ACPI_ERROR((AE_INFO, ACPI_ERROR((AE_INFO,

View File

@ -201,7 +201,6 @@ acpi_ex_start_trace_method(struct acpi_namespace_node *method_node,
union acpi_operand_object *obj_desc, union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state) struct acpi_walk_state *walk_state)
{ {
acpi_status status;
char *pathname = NULL; char *pathname = NULL;
u8 enabled = FALSE; u8 enabled = FALSE;
@ -211,11 +210,6 @@ acpi_ex_start_trace_method(struct acpi_namespace_node *method_node,
pathname = acpi_ns_get_normalized_pathname(method_node, TRUE); pathname = acpi_ns_get_normalized_pathname(method_node, TRUE);
} }
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
goto exit;
}
enabled = acpi_ex_interpreter_trace_enabled(pathname); enabled = acpi_ex_interpreter_trace_enabled(pathname);
if (enabled && !acpi_gbl_trace_method_object) { if (enabled && !acpi_gbl_trace_method_object) {
acpi_gbl_trace_method_object = obj_desc; acpi_gbl_trace_method_object = obj_desc;
@ -233,9 +227,6 @@ acpi_ex_start_trace_method(struct acpi_namespace_node *method_node,
} }
} }
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
exit:
if (enabled) { if (enabled) {
ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, TRUE, ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, TRUE,
obj_desc ? obj_desc->method.aml_start : NULL, obj_desc ? obj_desc->method.aml_start : NULL,
@ -267,7 +258,6 @@ acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node,
union acpi_operand_object *obj_desc, union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state) struct acpi_walk_state *walk_state)
{ {
acpi_status status;
char *pathname = NULL; char *pathname = NULL;
u8 enabled; u8 enabled;
@ -277,26 +267,14 @@ acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node,
pathname = acpi_ns_get_normalized_pathname(method_node, TRUE); pathname = acpi_ns_get_normalized_pathname(method_node, TRUE);
} }
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
goto exit_path;
}
enabled = acpi_ex_interpreter_trace_enabled(NULL); enabled = acpi_ex_interpreter_trace_enabled(NULL);
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
if (enabled) { if (enabled) {
ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, FALSE, ACPI_TRACE_POINT(ACPI_TRACE_AML_METHOD, FALSE,
obj_desc ? obj_desc->method.aml_start : NULL, obj_desc ? obj_desc->method.aml_start : NULL,
pathname); pathname);
} }
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
goto exit_path;
}
/* Check whether the tracer should be stopped */ /* Check whether the tracer should be stopped */
if (acpi_gbl_trace_method_object == obj_desc) { if (acpi_gbl_trace_method_object == obj_desc) {
@ -312,9 +290,6 @@ acpi_ex_stop_trace_method(struct acpi_namespace_node *method_node,
acpi_gbl_trace_method_object = NULL; acpi_gbl_trace_method_object = NULL;
} }
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
exit_path:
if (pathname) { if (pathname) {
ACPI_FREE(pathname); ACPI_FREE(pathname);
} }

View File

@ -94,6 +94,10 @@ void acpi_ex_enter_interpreter(void)
ACPI_ERROR((AE_INFO, ACPI_ERROR((AE_INFO,
"Could not acquire AML Interpreter mutex")); "Could not acquire AML Interpreter mutex"));
} }
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
ACPI_ERROR((AE_INFO, "Could not acquire AML Namespace mutex"));
}
return_VOID; return_VOID;
} }
@ -127,6 +131,10 @@ void acpi_ex_exit_interpreter(void)
ACPI_FUNCTION_TRACE(ex_exit_interpreter); ACPI_FUNCTION_TRACE(ex_exit_interpreter);
status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
ACPI_ERROR((AE_INFO, "Could not release AML Namespace mutex"));
}
status = acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); status = acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
ACPI_ERROR((AE_INFO, ACPI_ERROR((AE_INFO,

View File

@ -98,7 +98,7 @@ acpi_status
acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action)
{ {
struct acpi_gpe_register_info *gpe_register_info; struct acpi_gpe_register_info *gpe_register_info;
acpi_status status; acpi_status status = AE_OK;
u32 enable_mask; u32 enable_mask;
u32 register_bit; u32 register_bit;
@ -148,9 +148,14 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action)
return (AE_BAD_PARAMETER); return (AE_BAD_PARAMETER);
} }
/* Write the updated enable mask */ if (!(register_bit & gpe_register_info->mask_for_run)) {
status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); /* Write the updated enable mask */
status =
acpi_hw_write(enable_mask,
&gpe_register_info->enable_address);
}
return (status); return (status);
} }
@ -242,6 +247,12 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info *gpe_event_info,
local_event_status |= ACPI_EVENT_FLAG_ENABLED; local_event_status |= ACPI_EVENT_FLAG_ENABLED;
} }
/* GPE currently masked? (masked for runtime?) */
if (register_bit & gpe_register_info->mask_for_run) {
local_event_status |= ACPI_EVENT_FLAG_MASKED;
}
/* GPE enabled for wake? */ /* GPE enabled for wake? */
if (register_bit & gpe_register_info->enable_for_wake) { if (register_bit & gpe_register_info->enable_for_wake) {
@ -397,6 +408,7 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
u32 i; u32 i;
acpi_status status; acpi_status status;
struct acpi_gpe_register_info *gpe_register_info; struct acpi_gpe_register_info *gpe_register_info;
u8 enable_mask;
/* NOTE: assumes that all GPEs are currently disabled */ /* NOTE: assumes that all GPEs are currently disabled */
@ -410,9 +422,10 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
/* Enable all "runtime" GPEs in this register */ /* Enable all "runtime" GPEs in this register */
enable_mask = gpe_register_info->enable_for_run &
~gpe_register_info->mask_for_run;
status = status =
acpi_hw_gpe_enable_write(gpe_register_info->enable_for_run, acpi_hw_gpe_enable_write(enable_mask, gpe_register_info);
gpe_register_info);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return (status); return (status);
} }

View File

@ -108,9 +108,9 @@ acpi_status acpi_ns_root_initialize(void)
} }
status = status =
acpi_ns_lookup(NULL, (char *)init_val->name, init_val->type, acpi_ns_lookup(NULL, ACPI_CAST_PTR(char, init_val->name),
ACPI_IMODE_LOAD_PASS2, ACPI_NS_NO_UPSEARCH, init_val->type, ACPI_IMODE_LOAD_PASS2,
NULL, &new_node); ACPI_NS_NO_UPSEARCH, NULL, &new_node);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, ACPI_EXCEPTION((AE_INFO, status,
"Could not create predefined name %s", "Could not create predefined name %s",

View File

@ -79,7 +79,6 @@ acpi_ns_convert_to_integer(union acpi_operand_object *original_object,
/* String-to-Integer conversion */ /* String-to-Integer conversion */
status = acpi_ut_strtoul64(original_object->string.pointer, status = acpi_ut_strtoul64(original_object->string.pointer,
ACPI_ANY_BASE,
acpi_gbl_integer_byte_width, &value); acpi_gbl_integer_byte_width, &value);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return (status); return (status);

View File

@ -338,7 +338,7 @@ acpi_ns_dump_one_object(acpi_handle obj_handle,
case ACPI_TYPE_STRING: case ACPI_TYPE_STRING:
acpi_os_printf("Len %.2X ", obj_desc->string.length); acpi_os_printf("Len %.2X ", obj_desc->string.length);
acpi_ut_print_string(obj_desc->string.pointer, 32); acpi_ut_print_string(obj_desc->string.pointer, 80);
acpi_os_printf("\n"); acpi_os_printf("\n");
break; break;

View File

@ -46,6 +46,7 @@
#include "acnamesp.h" #include "acnamesp.h"
#include "acdispat.h" #include "acdispat.h"
#include "actables.h" #include "actables.h"
#include "acinterp.h"
#define _COMPONENT ACPI_NAMESPACE #define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nsload") ACPI_MODULE_NAME("nsload")
@ -78,20 +79,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
ACPI_FUNCTION_TRACE(ns_load_table); ACPI_FUNCTION_TRACE(ns_load_table);
/*
* Parse the table and load the namespace with all named
* objects found within. Control methods are NOT parsed
* at this time. In fact, the control methods cannot be
* parsed until the entire namespace is loaded, because
* if a control method makes a forward reference (call)
* to another control method, we can't continue parsing
* because we don't know how many arguments to parse next!
*/
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* If table already loaded into namespace, just return */ /* If table already loaded into namespace, just return */
if (acpi_tb_is_table_loaded(table_index)) { if (acpi_tb_is_table_loaded(table_index)) {
@ -107,6 +94,15 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
goto unlock; goto unlock;
} }
/*
* Parse the table and load the namespace with all named
* objects found within. Control methods are NOT parsed
* at this time. In fact, the control methods cannot be
* parsed until the entire namespace is loaded, because
* if a control method makes a forward reference (call)
* to another control method, we can't continue parsing
* because we don't know how many arguments to parse next!
*/
status = acpi_ns_parse_table(table_index, node); status = acpi_ns_parse_table(table_index, node);
if (ACPI_SUCCESS(status)) { if (ACPI_SUCCESS(status)) {
acpi_tb_set_table_loaded_flag(table_index, TRUE); acpi_tb_set_table_loaded_flag(table_index, TRUE);
@ -120,7 +116,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
* exist. This target of Scope must already exist in the * exist. This target of Scope must already exist in the
* namespace, as per the ACPI specification. * namespace, as per the ACPI specification.
*/ */
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
acpi_ns_delete_namespace_by_owner(acpi_gbl_root_table_list. acpi_ns_delete_namespace_by_owner(acpi_gbl_root_table_list.
tables[table_index].owner_id); tables[table_index].owner_id);
@ -129,8 +124,6 @@ acpi_ns_load_table(u32 table_index, struct acpi_namespace_node *node)
} }
unlock: unlock:
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -162,7 +155,8 @@ unlock:
* other ACPI implementations. Optionally, the execution can be deferred * other ACPI implementations. Optionally, the execution can be deferred
* until later, see acpi_initialize_objects. * until later, see acpi_initialize_objects.
*/ */
if (!acpi_gbl_group_module_level_code) { if (!acpi_gbl_parse_table_as_term_list
&& !acpi_gbl_group_module_level_code) {
acpi_ns_exec_module_code_list(); acpi_ns_exec_module_code_list();
} }

View File

@ -47,10 +47,101 @@
#include "acparser.h" #include "acparser.h"
#include "acdispat.h" #include "acdispat.h"
#include "actables.h" #include "actables.h"
#include "acinterp.h"
#define _COMPONENT ACPI_NAMESPACE #define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nsparse") ACPI_MODULE_NAME("nsparse")
/*******************************************************************************
*
* FUNCTION: ns_execute_table
*
* PARAMETERS: table_desc - An ACPI table descriptor for table to parse
* start_node - Where to enter the table into the namespace
*
* RETURN: Status
*
* DESCRIPTION: Load ACPI/AML table by executing the entire table as a
* term_list.
*
******************************************************************************/
acpi_status
acpi_ns_execute_table(u32 table_index, struct acpi_namespace_node *start_node)
{
acpi_status status;
struct acpi_table_header *table;
acpi_owner_id owner_id;
struct acpi_evaluate_info *info = NULL;
u32 aml_length;
u8 *aml_start;
union acpi_operand_object *method_obj = NULL;
ACPI_FUNCTION_TRACE(ns_execute_table);
status = acpi_get_table_by_index(table_index, &table);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* Table must consist of at least a complete header */
if (table->length < sizeof(struct acpi_table_header)) {
return_ACPI_STATUS(AE_BAD_HEADER);
}
aml_start = (u8 *)table + sizeof(struct acpi_table_header);
aml_length = table->length - sizeof(struct acpi_table_header);
status = acpi_tb_get_owner_id(table_index, &owner_id);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* Create, initialize, and link a new temporary method object */
method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD);
if (!method_obj) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
/* Allocate the evaluation information block */
info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
if (!info) {
status = AE_NO_MEMORY;
goto cleanup;
}
ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
"Create table code block: %p\n", method_obj));
method_obj->method.aml_start = aml_start;
method_obj->method.aml_length = aml_length;
method_obj->method.owner_id = owner_id;
method_obj->method.info_flags |= ACPI_METHOD_MODULE_LEVEL;
info->pass_number = ACPI_IMODE_EXECUTE;
info->node = start_node;
info->obj_desc = method_obj;
info->node_flags = info->node->flags;
info->full_pathname = acpi_ns_get_normalized_pathname(info->node, TRUE);
if (!info->full_pathname) {
status = AE_NO_MEMORY;
goto cleanup;
}
status = acpi_ps_execute_table(info);
cleanup:
if (info) {
ACPI_FREE(info->full_pathname);
info->full_pathname = NULL;
}
ACPI_FREE(info);
acpi_ut_remove_reference(method_obj);
return_ACPI_STATUS(status);
}
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: ns_one_complete_parse * FUNCTION: ns_one_complete_parse
@ -63,6 +154,7 @@ ACPI_MODULE_NAME("nsparse")
* DESCRIPTION: Perform one complete parse of an ACPI/AML table. * DESCRIPTION: Perform one complete parse of an ACPI/AML table.
* *
******************************************************************************/ ******************************************************************************/
acpi_status acpi_status
acpi_ns_one_complete_parse(u32 pass_number, acpi_ns_one_complete_parse(u32 pass_number,
u32 table_index, u32 table_index,
@ -143,7 +235,9 @@ acpi_ns_one_complete_parse(u32 pass_number,
ACPI_DEBUG_PRINT((ACPI_DB_PARSE, ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
"*PARSE* pass %u parse\n", pass_number)); "*PARSE* pass %u parse\n", pass_number));
acpi_ex_enter_interpreter();
status = acpi_ps_parse_aml(walk_state); status = acpi_ps_parse_aml(walk_state);
acpi_ex_exit_interpreter();
cleanup: cleanup:
acpi_ps_delete_parse_tree(parse_root); acpi_ps_delete_parse_tree(parse_root);
@ -170,38 +264,47 @@ acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node)
ACPI_FUNCTION_TRACE(ns_parse_table); ACPI_FUNCTION_TRACE(ns_parse_table);
/* if (acpi_gbl_parse_table_as_term_list) {
* AML Parse, pass 1 ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start load pass\n"));
*
* In this pass, we load most of the namespace. Control methods
* are not parsed until later. A parse tree is not created. Instead,
* each Parser Op subtree is deleted when it is finished. This saves
* a great deal of memory, and allows a small cache of parse objects
* to service the entire parse. The second pass of the parse then
* performs another complete parse of the AML.
*/
ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 1\n"));
status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS1, status = acpi_ns_execute_table(table_index, start_node);
table_index, start_node); if (ACPI_FAILURE(status)) {
if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status);
return_ACPI_STATUS(status); }
} } else {
/*
* AML Parse, pass 1
*
* In this pass, we load most of the namespace. Control methods
* are not parsed until later. A parse tree is not created.
* Instead, each Parser Op subtree is deleted when it is finished.
* This saves a great deal of memory, and allows a small cache of
* parse objects to service the entire parse. The second pass of
* the parse then performs another complete parse of the AML.
*/
ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 1\n"));
/* status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS1,
* AML Parse, pass 2 table_index, start_node);
* if (ACPI_FAILURE(status)) {
* In this pass, we resolve forward references and other things return_ACPI_STATUS(status);
* that could not be completed during the first pass. }
* Another complete parse of the AML is performed, but the
* overhead of this is compensated for by the fact that the /*
* parse objects are all cached. * AML Parse, pass 2
*/ *
ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 2\n")); * In this pass, we resolve forward references and other things
status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS2, * that could not be completed during the first pass.
table_index, start_node); * Another complete parse of the AML is performed, but the
if (ACPI_FAILURE(status)) { * overhead of this is compensated for by the fact that the
return_ACPI_STATUS(status); * parse objects are all cached.
*/
ACPI_DEBUG_PRINT((ACPI_DB_PARSE, "**** Start pass 2\n"));
status = acpi_ns_one_complete_parse(ACPI_IMODE_LOAD_PASS2,
table_index, start_node);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
} }
return_ACPI_STATUS(status); return_ACPI_STATUS(status);

View File

@ -662,7 +662,7 @@ u32 acpi_ns_opens_scope(acpi_object_type type)
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_ns_get_node * FUNCTION: acpi_ns_get_node_unlocked
* *
* PARAMETERS: *pathname - Name to be found, in external (ASL) format. The * PARAMETERS: *pathname - Name to be found, in external (ASL) format. The
* \ (backslash) and ^ (carat) prefixes, and the * \ (backslash) and ^ (carat) prefixes, and the
@ -678,20 +678,21 @@ u32 acpi_ns_opens_scope(acpi_object_type type)
* DESCRIPTION: Look up a name relative to a given scope and return the * DESCRIPTION: Look up a name relative to a given scope and return the
* corresponding Node. NOTE: Scope can be null. * corresponding Node. NOTE: Scope can be null.
* *
* MUTEX: Locks namespace * MUTEX: Doesn't locks namespace
* *
******************************************************************************/ ******************************************************************************/
acpi_status acpi_status
acpi_ns_get_node(struct acpi_namespace_node *prefix_node, acpi_ns_get_node_unlocked(struct acpi_namespace_node *prefix_node,
const char *pathname, const char *pathname,
u32 flags, struct acpi_namespace_node **return_node) u32 flags, struct acpi_namespace_node **return_node)
{ {
union acpi_generic_state scope_info; union acpi_generic_state scope_info;
acpi_status status; acpi_status status;
char *internal_path; char *internal_path;
ACPI_FUNCTION_TRACE_PTR(ns_get_node, ACPI_CAST_PTR(char, pathname)); ACPI_FUNCTION_TRACE_PTR(ns_get_node_unlocked,
ACPI_CAST_PTR(char, pathname));
/* Simplest case is a null pathname */ /* Simplest case is a null pathname */
@ -718,13 +719,6 @@ acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
/* Must lock namespace during lookup */
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
goto cleanup;
}
/* Setup lookup scope (search starting point) */ /* Setup lookup scope (search starting point) */
scope_info.scope.node = prefix_node; scope_info.scope.node = prefix_node;
@ -740,9 +734,49 @@ acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
pathname, acpi_format_exception(status))); pathname, acpi_format_exception(status)));
} }
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
cleanup:
ACPI_FREE(internal_path); ACPI_FREE(internal_path);
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
/*******************************************************************************
*
* FUNCTION: acpi_ns_get_node
*
* PARAMETERS: *pathname - Name to be found, in external (ASL) format. The
* \ (backslash) and ^ (carat) prefixes, and the
* . (period) to separate segments are supported.
* prefix_node - Root of subtree to be searched, or NS_ALL for the
* root of the name space. If Name is fully
* qualified (first s8 is '\'), the passed value
* of Scope will not be accessed.
* flags - Used to indicate whether to perform upsearch or
* not.
* return_node - Where the Node is returned
*
* DESCRIPTION: Look up a name relative to a given scope and return the
* corresponding Node. NOTE: Scope can be null.
*
* MUTEX: Locks namespace
*
******************************************************************************/
acpi_status
acpi_ns_get_node(struct acpi_namespace_node *prefix_node,
const char *pathname,
u32 flags, struct acpi_namespace_node **return_node)
{
acpi_status status;
ACPI_FUNCTION_TRACE_PTR(ns_get_node, ACPI_CAST_PTR(char, pathname));
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
status = acpi_ns_get_node_unlocked(prefix_node, pathname,
flags, return_node);
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return_ACPI_STATUS(status);
}

View File

@ -537,9 +537,11 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)
/* Either the method parse or actual execution failed */ /* Either the method parse or actual execution failed */
acpi_ex_exit_interpreter();
ACPI_ERROR_METHOD("Method parse/execution failed", ACPI_ERROR_METHOD("Method parse/execution failed",
walk_state->method_node, NULL, walk_state->method_node, NULL,
status); status);
acpi_ex_enter_interpreter();
/* Check for possible multi-thread reentrancy problem */ /* Check for possible multi-thread reentrancy problem */
@ -571,7 +573,9 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)
* cleanup to do * cleanup to do
*/ */
if (((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) == if (((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) ==
ACPI_PARSE_EXECUTE) || (ACPI_FAILURE(status))) { ACPI_PARSE_EXECUTE &&
!(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) ||
(ACPI_FAILURE(status))) {
acpi_ds_terminate_control_method(walk_state-> acpi_ds_terminate_control_method(walk_state->
method_desc, method_desc,
walk_state); walk_state);

View File

@ -250,6 +250,90 @@ cleanup:
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
/*******************************************************************************
*
* FUNCTION: acpi_ps_execute_table
*
* PARAMETERS: info - Method info block, contains:
* node - Node to where the is entered into the
* namespace
* obj_desc - Pseudo method object describing the AML
* code of the entire table
* pass_number - Parse or execute pass
*
* RETURN: Status
*
* DESCRIPTION: Execute a table
*
******************************************************************************/
acpi_status acpi_ps_execute_table(struct acpi_evaluate_info *info)
{
acpi_status status;
union acpi_parse_object *op = NULL;
struct acpi_walk_state *walk_state = NULL;
ACPI_FUNCTION_TRACE(ps_execute_table);
/* Create and init a Root Node */
op = acpi_ps_create_scope_op(info->obj_desc->method.aml_start);
if (!op) {
status = AE_NO_MEMORY;
goto cleanup;
}
/* Create and initialize a new walk state */
walk_state =
acpi_ds_create_walk_state(info->obj_desc->method.owner_id, NULL,
NULL, NULL);
if (!walk_state) {
status = AE_NO_MEMORY;
goto cleanup;
}
status = acpi_ds_init_aml_walk(walk_state, op, info->node,
info->obj_desc->method.aml_start,
info->obj_desc->method.aml_length, info,
info->pass_number);
if (ACPI_FAILURE(status)) {
goto cleanup;
}
if (info->obj_desc->method.info_flags & ACPI_METHOD_MODULE_LEVEL) {
walk_state->parse_flags |= ACPI_PARSE_MODULE_LEVEL;
}
/* Info->Node is the default location to load the table */
if (info->node && info->node != acpi_gbl_root_node) {
status =
acpi_ds_scope_stack_push(info->node, ACPI_TYPE_METHOD,
walk_state);
if (ACPI_FAILURE(status)) {
goto cleanup;
}
}
/*
* Parse the AML, walk_state will be deleted by parse_aml
*/
acpi_ex_enter_interpreter();
status = acpi_ps_parse_aml(walk_state);
acpi_ex_exit_interpreter();
walk_state = NULL;
cleanup:
if (walk_state) {
acpi_ds_delete_walk_state(walk_state);
}
if (op) {
acpi_ps_delete_parse_tree(op);
}
return_ACPI_STATUS(status);
}
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_ps_update_parameter_list * FUNCTION: acpi_ps_update_parameter_list

View File

@ -45,6 +45,7 @@
#include "accommon.h" #include "accommon.h"
#include "acnamesp.h" #include "acnamesp.h"
#include "actables.h" #include "actables.h"
#include "acevents.h"
#define _COMPONENT ACPI_TABLES #define _COMPONENT ACPI_TABLES
ACPI_MODULE_NAME("tbdata") ACPI_MODULE_NAME("tbdata")
@ -613,17 +614,12 @@ acpi_status acpi_tb_delete_namespace_by_owner(u32 table_index)
* lock may block, and also since the execution of a namespace walk * lock may block, and also since the execution of a namespace walk
* must be allowed to use the interpreter. * must be allowed to use the interpreter.
*/ */
(void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock); status = acpi_ut_acquire_write_lock(&acpi_gbl_namespace_rw_lock);
acpi_ns_delete_namespace_by_owner(owner_id);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
acpi_ns_delete_namespace_by_owner(owner_id);
acpi_ut_release_write_lock(&acpi_gbl_namespace_rw_lock); acpi_ut_release_write_lock(&acpi_gbl_namespace_rw_lock);
status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -771,3 +767,142 @@ void acpi_tb_set_table_loaded_flag(u32 table_index, u8 is_loaded)
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
} }
/*******************************************************************************
*
* FUNCTION: acpi_tb_load_table
*
* PARAMETERS: table_index - Table index
* parent_node - Where table index is returned
*
* RETURN: Status
*
* DESCRIPTION: Load an ACPI table
*
******************************************************************************/
acpi_status
acpi_tb_load_table(u32 table_index, struct acpi_namespace_node *parent_node)
{
struct acpi_table_header *table;
acpi_status status;
acpi_owner_id owner_id;
ACPI_FUNCTION_TRACE(tb_load_table);
/*
* Note: Now table is "INSTALLED", it must be validated before
* using.
*/
status = acpi_get_table_by_index(table_index, &table);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
status = acpi_ns_load_table(table_index, parent_node);
/* Execute any module-level code that was found in the table */
if (!acpi_gbl_parse_table_as_term_list
&& acpi_gbl_group_module_level_code) {
acpi_ns_exec_module_code_list();
}
/*
* Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is
* responsible for discovering any new wake GPEs by running _PRW methods
* that may have been loaded by this table.
*/
status = acpi_tb_get_owner_id(table_index, &owner_id);
if (ACPI_SUCCESS(status)) {
acpi_ev_update_gpes(owner_id);
}
/* Invoke table handler if present */
if (acpi_gbl_table_handler) {
(void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
acpi_gbl_table_handler_context);
}
return_ACPI_STATUS(status);
}
/*******************************************************************************
*
* FUNCTION: acpi_tb_install_and_load_table
*
* PARAMETERS: table - Pointer to the table
* address - Physical address of the table
* flags - Allocation flags of the table
* table_index - Where table index is returned
*
* RETURN: Status
*
* DESCRIPTION: Install and load an ACPI table
*
******************************************************************************/
acpi_status
acpi_tb_install_and_load_table(struct acpi_table_header *table,
acpi_physical_address address,
u8 flags, u8 override, u32 *table_index)
{
acpi_status status;
u32 i;
acpi_owner_id owner_id;
ACPI_FUNCTION_TRACE(acpi_load_table);
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
/* Install the table and load it into the namespace */
status = acpi_tb_install_standard_table(address, flags, TRUE,
override, &i);
if (ACPI_FAILURE(status)) {
goto unlock_and_exit;
}
/*
* Note: Now table is "INSTALLED", it must be validated before
* using.
*/
status = acpi_tb_validate_table(&acpi_gbl_root_table_list.tables[i]);
if (ACPI_FAILURE(status)) {
goto unlock_and_exit;
}
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
status = acpi_ns_load_table(i, acpi_gbl_root_node);
/* Execute any module-level code that was found in the table */
if (!acpi_gbl_parse_table_as_term_list
&& acpi_gbl_group_module_level_code) {
acpi_ns_exec_module_code_list();
}
/*
* Update GPEs for any new _Lxx/_Exx methods. Ignore errors. The host is
* responsible for discovering any new wake GPEs by running _PRW methods
* that may have been loaded by this table.
*/
status = acpi_tb_get_owner_id(i, &owner_id);
if (ACPI_SUCCESS(status)) {
acpi_ev_update_gpes(owner_id);
}
/* Invoke table handler if present */
if (acpi_gbl_table_handler) {
(void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
acpi_gbl_table_handler_context);
}
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
unlock_and_exit:
*table_index = i;
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return_ACPI_STATUS(status);
}

View File

@ -344,23 +344,27 @@ void acpi_tb_parse_fadt(void)
/* Obtain the DSDT and FACS tables via their addresses within the FADT */ /* Obtain the DSDT and FACS tables via their addresses within the FADT */
acpi_tb_install_fixed_table((acpi_physical_address)acpi_gbl_FADT.Xdsdt, acpi_tb_install_standard_table((acpi_physical_address)acpi_gbl_FADT.
ACPI_SIG_DSDT, &acpi_gbl_dsdt_index); Xdsdt,
ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
FALSE, TRUE, &acpi_gbl_dsdt_index);
/* If Hardware Reduced flag is set, there is no FACS */ /* If Hardware Reduced flag is set, there is no FACS */
if (!acpi_gbl_reduced_hardware) { if (!acpi_gbl_reduced_hardware) {
if (acpi_gbl_FADT.facs) { if (acpi_gbl_FADT.facs) {
acpi_tb_install_fixed_table((acpi_physical_address) acpi_tb_install_standard_table((acpi_physical_address)
acpi_gbl_FADT.facs, acpi_gbl_FADT.facs,
ACPI_SIG_FACS, ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
&acpi_gbl_facs_index); FALSE, TRUE,
&acpi_gbl_facs_index);
} }
if (acpi_gbl_FADT.Xfacs) { if (acpi_gbl_FADT.Xfacs) {
acpi_tb_install_fixed_table((acpi_physical_address) acpi_tb_install_standard_table((acpi_physical_address)
acpi_gbl_FADT.Xfacs, acpi_gbl_FADT.Xfacs,
ACPI_SIG_FACS, ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL,
&acpi_gbl_xfacs_index); FALSE, TRUE,
&acpi_gbl_xfacs_index);
} }
} }
} }
@ -476,17 +480,19 @@ static void acpi_tb_convert_fadt(void)
u32 i; u32 i;
/* /*
* For ACPI 1.0 FADTs (revision 1 or 2), ensure that reserved fields which * For ACPI 1.0 FADTs (revision 1), ensure that reserved fields which
* should be zero are indeed zero. This will workaround BIOSs that * should be zero are indeed zero. This will workaround BIOSs that
* inadvertently place values in these fields. * inadvertently place values in these fields.
* *
* The ACPI 1.0 reserved fields that will be zeroed are the bytes located * The ACPI 1.0 reserved fields that will be zeroed are the bytes located
* at offset 45, 55, 95, and the word located at offset 109, 110. * at offset 45, 55, 95, and the word located at offset 109, 110.
* *
* Note: The FADT revision value is unreliable. Only the length can be * Note: The FADT revision value is unreliable because of BIOS errors.
* trusted. * The table length is instead used as the final word on the version.
*
* Note: FADT revision 3 is the ACPI 2.0 version of the FADT.
*/ */
if (acpi_gbl_FADT.header.length <= ACPI_FADT_V2_SIZE) { if (acpi_gbl_FADT.header.length <= ACPI_FADT_V3_SIZE) {
acpi_gbl_FADT.preferred_profile = 0; acpi_gbl_FADT.preferred_profile = 0;
acpi_gbl_FADT.pstate_control = 0; acpi_gbl_FADT.pstate_control = 0;
acpi_gbl_FADT.cst_control = 0; acpi_gbl_FADT.cst_control = 0;
@ -552,76 +558,72 @@ static void acpi_tb_convert_fadt(void)
* *
* Address32 zero, Address64 [don't care] - Use Address64 * Address32 zero, Address64 [don't care] - Use Address64
* *
* No override: if acpi_gbl_use32_bit_fadt_addresses is FALSE, and:
* Address32 non-zero, Address64 zero - Copy/use Address32 * Address32 non-zero, Address64 zero - Copy/use Address32
* Address32 non-zero == Address64 non-zero - Use Address64 * Address32 non-zero == Address64 non-zero - Use Address64
* Address32 non-zero != Address64 non-zero - Warning, use Address64 * Address32 non-zero != Address64 non-zero - Warning, use Address64
* *
* Override: if acpi_gbl_use32_bit_fadt_addresses is TRUE, and: * Override: if acpi_gbl_use32_bit_fadt_addresses is TRUE, and:
* Address32 non-zero, Address64 zero - Copy/use Address32
* Address32 non-zero == Address64 non-zero - Copy/use Address32
* Address32 non-zero != Address64 non-zero - Warning, copy/use Address32 * Address32 non-zero != Address64 non-zero - Warning, copy/use Address32
* *
* Note: space_id is always I/O for 32-bit legacy address fields * Note: space_id is always I/O for 32-bit legacy address fields
*/ */
if (address32) { if (address32) {
if (!address64->address) { if (address64->address) {
if (address64->address != (u64)address32) {
/* 64-bit address is zero, use 32-bit address */ /* Address mismatch */
acpi_tb_init_generic_address(address64, ACPI_BIOS_WARNING((AE_INFO,
ACPI_ADR_SPACE_SYSTEM_IO, "32/64X address mismatch in FADT/%s: "
*ACPI_ADD_PTR(u8, "0x%8.8X/0x%8.8X%8.8X, using %u-bit address",
&acpi_gbl_FADT, name, address32,
fadt_info_table ACPI_FORMAT_UINT64
[i]. (address64->address),
length), acpi_gbl_use32_bit_fadt_addresses
(u64)address32, ? 32 : 64));
name, flags); }
} else if (address64->address != (u64)address32) {
/* Address mismatch */ /*
* For each extended field, check for length mismatch
ACPI_BIOS_WARNING((AE_INFO, * between the legacy length field and the corresponding
"32/64X address mismatch in FADT/%s: " * 64-bit X length field.
"0x%8.8X/0x%8.8X%8.8X, using %u-bit address", * Note: If the legacy length field is > 0xFF bits, ignore
name, address32, * this check. (GPE registers can be larger than the
ACPI_FORMAT_UINT64 * 64-bit GAS structure can accomodate, 0xFF bits).
(address64->address), */
acpi_gbl_use32_bit_fadt_addresses if ((ACPI_MUL_8(length) <= ACPI_UINT8_MAX) &&
? 32 : 64)); (address64->bit_width !=
ACPI_MUL_8(length))) {
if (acpi_gbl_use32_bit_fadt_addresses) { ACPI_BIOS_WARNING((AE_INFO,
"32/64X length mismatch in FADT/%s: %u/%u",
/* 32-bit address override */ name,
ACPI_MUL_8(length),
acpi_tb_init_generic_address(address64, address64->
ACPI_ADR_SPACE_SYSTEM_IO, bit_width));
*ACPI_ADD_PTR
(u8,
&acpi_gbl_FADT,
fadt_info_table
[i].
length),
(u64)
address32,
name,
flags);
} }
} }
}
/* /*
* For each extended field, check for length mismatch between the * Hardware register access code always uses the 64-bit fields.
* legacy length field and the corresponding 64-bit X length field. * So if the 64-bit field is zero or is to be overridden,
* Note: If the legacy length field is > 0xFF bits, ignore this * initialize it with the 32-bit fields.
* check. (GPE registers can be larger than the 64-bit GAS structure * Note that when the 32-bit address favor is specified, the
* can accomodate, 0xFF bits). * 64-bit fields are always re-initialized so that
*/ * access_size/bit_width/bit_offset fields can be correctly
if (address64->address && * configured to the values to trigger a 32-bit compatible
(ACPI_MUL_8(length) <= ACPI_UINT8_MAX) && * access mode in the hardware register access code.
(address64->bit_width != ACPI_MUL_8(length))) { */
ACPI_BIOS_WARNING((AE_INFO, if (!address64->address
"32/64X length mismatch in FADT/%s: %u/%u", || acpi_gbl_use32_bit_fadt_addresses) {
name, ACPI_MUL_8(length), acpi_tb_init_generic_address(address64,
address64->bit_width)); ACPI_ADR_SPACE_SYSTEM_IO,
length,
(u64)address32,
name, flags);
}
} }
if (fadt_info_table[i].flags & ACPI_FADT_REQUIRED) { if (fadt_info_table[i].flags & ACPI_FADT_REQUIRED) {

View File

@ -68,7 +68,7 @@ acpi_status
acpi_tb_find_table(char *signature, acpi_tb_find_table(char *signature,
char *oem_id, char *oem_table_id, u32 *table_index) char *oem_id, char *oem_table_id, u32 *table_index)
{ {
acpi_status status; acpi_status status = AE_OK;
struct acpi_table_header header; struct acpi_table_header header;
u32 i; u32 i;
@ -96,6 +96,7 @@ acpi_tb_find_table(char *signature,
/* Search for the table */ /* Search for the table */
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) {
if (memcmp(&(acpi_gbl_root_table_list.tables[i].signature), if (memcmp(&(acpi_gbl_root_table_list.tables[i].signature),
header.signature, ACPI_NAME_SIZE)) { header.signature, ACPI_NAME_SIZE)) {
@ -115,7 +116,7 @@ acpi_tb_find_table(char *signature,
acpi_tb_validate_table(&acpi_gbl_root_table_list. acpi_tb_validate_table(&acpi_gbl_root_table_list.
tables[i]); tables[i]);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); goto unlock_and_exit;
} }
if (!acpi_gbl_root_table_list.tables[i].pointer) { if (!acpi_gbl_root_table_list.tables[i].pointer) {
@ -144,9 +145,12 @@ acpi_tb_find_table(char *signature,
ACPI_DEBUG_PRINT((ACPI_DB_TABLES, ACPI_DEBUG_PRINT((ACPI_DB_TABLES,
"Found table [%4.4s]\n", "Found table [%4.4s]\n",
header.signature)); header.signature));
return_ACPI_STATUS(AE_OK); goto unlock_and_exit;
} }
} }
status = AE_NOT_FOUND;
return_ACPI_STATUS(AE_NOT_FOUND); unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return_ACPI_STATUS(status);
} }

View File

@ -155,68 +155,6 @@ acpi_tb_install_table_with_override(struct acpi_table_desc *new_table_desc,
} }
} }
/*******************************************************************************
*
* FUNCTION: acpi_tb_install_fixed_table
*
* PARAMETERS: address - Physical address of DSDT or FACS
* signature - Table signature, NULL if no need to
* match
* table_index - Where the table index is returned
*
* RETURN: Status
*
* DESCRIPTION: Install a fixed ACPI table (DSDT/FACS) into the global data
* structure.
*
******************************************************************************/
acpi_status
acpi_tb_install_fixed_table(acpi_physical_address address,
char *signature, u32 *table_index)
{
struct acpi_table_desc new_table_desc;
acpi_status status;
ACPI_FUNCTION_TRACE(tb_install_fixed_table);
if (!address) {
ACPI_ERROR((AE_INFO,
"Null physical address for ACPI table [%s]",
signature));
return (AE_NO_MEMORY);
}
/* Fill a table descriptor for validation */
status = acpi_tb_acquire_temp_table(&new_table_desc, address,
ACPI_TABLE_ORIGIN_INTERNAL_PHYSICAL);
if (ACPI_FAILURE(status)) {
ACPI_ERROR((AE_INFO,
"Could not acquire table length at %8.8X%8.8X",
ACPI_FORMAT_UINT64(address)));
return_ACPI_STATUS(status);
}
/* Validate and verify a table before installation */
status = acpi_tb_verify_temp_table(&new_table_desc, signature);
if (ACPI_FAILURE(status)) {
goto release_and_exit;
}
/* Add the table to the global root table list */
acpi_tb_install_table_with_override(&new_table_desc, TRUE, table_index);
release_and_exit:
/* Release the temporary table descriptor */
acpi_tb_release_temp_table(&new_table_desc);
return_ACPI_STATUS(status);
}
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_tb_install_standard_table * FUNCTION: acpi_tb_install_standard_table
@ -230,8 +168,7 @@ release_and_exit:
* *
* RETURN: Status * RETURN: Status
* *
* DESCRIPTION: This function is called to install an ACPI table that is * DESCRIPTION: This function is called to verify and install an ACPI table.
* neither DSDT nor FACS (a "standard" table.)
* When this function is called by "Load" or "LoadTable" opcodes, * When this function is called by "Load" or "LoadTable" opcodes,
* or by acpi_load_table() API, the "Reload" parameter is set. * or by acpi_load_table() API, the "Reload" parameter is set.
* After sucessfully returning from this function, table is * After sucessfully returning from this function, table is
@ -364,6 +301,14 @@ acpi_tb_install_standard_table(acpi_physical_address address,
acpi_tb_install_table_with_override(&new_table_desc, override, acpi_tb_install_table_with_override(&new_table_desc, override,
table_index); table_index);
/* Invoke table handler if present */
if (acpi_gbl_table_handler) {
(void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_INSTALL,
new_table_desc.pointer,
acpi_gbl_table_handler_context);
}
release_and_exit: release_and_exit:
/* Release the temporary table descriptor */ /* Release the temporary table descriptor */

View File

@ -252,7 +252,8 @@ acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size)
* *
******************************************************************************/ ******************************************************************************/
acpi_status __init acpi_tb_parse_root_table(acpi_physical_address rsdp_address) acpi_status ACPI_INIT_FUNCTION
acpi_tb_parse_root_table(acpi_physical_address rsdp_address)
{ {
struct acpi_table_rsdp *rsdp; struct acpi_table_rsdp *rsdp;
u32 table_entry_size; u32 table_entry_size;

View File

@ -98,7 +98,7 @@ acpi_status acpi_allocate_root_table(u32 initial_table_count)
* *
******************************************************************************/ ******************************************************************************/
acpi_status __init acpi_status ACPI_INIT_FUNCTION
acpi_initialize_tables(struct acpi_table_desc *initial_table_array, acpi_initialize_tables(struct acpi_table_desc *initial_table_array,
u32 initial_table_count, u8 allow_resize) u32 initial_table_count, u8 allow_resize)
{ {
@ -164,7 +164,7 @@ ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_tables)
* kernel. * kernel.
* *
******************************************************************************/ ******************************************************************************/
acpi_status __init acpi_reallocate_root_table(void) acpi_status ACPI_INIT_FUNCTION acpi_reallocate_root_table(void)
{ {
acpi_status status; acpi_status status;

View File

@ -63,7 +63,7 @@ ACPI_MODULE_NAME("tbxfload")
* DESCRIPTION: Load the ACPI tables from the RSDT/XSDT * DESCRIPTION: Load the ACPI tables from the RSDT/XSDT
* *
******************************************************************************/ ******************************************************************************/
acpi_status __init acpi_load_tables(void) acpi_status ACPI_INIT_FUNCTION acpi_load_tables(void)
{ {
acpi_status status; acpi_status status;
@ -103,7 +103,8 @@ acpi_status __init acpi_load_tables(void)
"While loading namespace from ACPI tables")); "While loading namespace from ACPI tables"));
} }
if (!acpi_gbl_group_module_level_code) { if (acpi_gbl_parse_table_as_term_list
|| !acpi_gbl_group_module_level_code) {
/* /*
* Initialize the objects that remain uninitialized. This * Initialize the objects that remain uninitialized. This
* runs the executable AML that may be part of the * runs the executable AML that may be part of the
@ -188,11 +189,11 @@ acpi_status acpi_tb_load_namespace(void)
memcpy(&acpi_gbl_original_dsdt_header, acpi_gbl_DSDT, memcpy(&acpi_gbl_original_dsdt_header, acpi_gbl_DSDT,
sizeof(struct acpi_table_header)); sizeof(struct acpi_table_header));
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
/* Load and parse tables */ /* Load and parse tables */
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
status = acpi_ns_load_table(acpi_gbl_dsdt_index, acpi_gbl_root_node); status = acpi_ns_load_table(acpi_gbl_dsdt_index, acpi_gbl_root_node);
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "[DSDT] table load failed")); ACPI_EXCEPTION((AE_INFO, status, "[DSDT] table load failed"));
tables_failed++; tables_failed++;
@ -202,7 +203,6 @@ acpi_status acpi_tb_load_namespace(void)
/* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */ /* Load any SSDT or PSDT tables. Note: Loop leaves tables locked */
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) {
table = &acpi_gbl_root_table_list.tables[i]; table = &acpi_gbl_root_table_list.tables[i];
@ -220,6 +220,7 @@ acpi_status acpi_tb_load_namespace(void)
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
status = acpi_ns_load_table(i, acpi_gbl_root_node); status = acpi_ns_load_table(i, acpi_gbl_root_node);
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, ACPI_EXCEPTION((AE_INFO, status,
"(%4.4s:%8.8s) while loading table", "(%4.4s:%8.8s) while loading table",
@ -235,8 +236,6 @@ acpi_status acpi_tb_load_namespace(void)
} else { } else {
tables_loaded++; tables_loaded++;
} }
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
} }
if (!tables_failed) { if (!tables_failed) {
@ -272,7 +271,7 @@ unlock_and_exit:
* *
******************************************************************************/ ******************************************************************************/
acpi_status __init acpi_status ACPI_INIT_FUNCTION
acpi_install_table(acpi_physical_address address, u8 physical) acpi_install_table(acpi_physical_address address, u8 physical)
{ {
acpi_status status; acpi_status status;
@ -324,49 +323,13 @@ acpi_status acpi_load_table(struct acpi_table_header *table)
return_ACPI_STATUS(AE_BAD_PARAMETER); return_ACPI_STATUS(AE_BAD_PARAMETER);
} }
/* Must acquire the interpreter lock during this operation */
status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* Install the table and load it into the namespace */ /* Install the table and load it into the namespace */
ACPI_INFO(("Host-directed Dynamic ACPI Table Load:")); ACPI_INFO(("Host-directed Dynamic ACPI Table Load:"));
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
status = acpi_tb_install_standard_table(ACPI_PTR_TO_PHYSADDR(table),
ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
TRUE, FALSE, &table_index);
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) {
goto unlock_and_exit;
}
/*
* Note: Now table is "INSTALLED", it must be validated before
* using.
*/
status = status =
acpi_tb_validate_table(&acpi_gbl_root_table_list. acpi_tb_install_and_load_table(table, ACPI_PTR_TO_PHYSADDR(table),
tables[table_index]); ACPI_TABLE_ORIGIN_EXTERNAL_VIRTUAL,
if (ACPI_FAILURE(status)) { FALSE, &table_index);
goto unlock_and_exit;
}
status = acpi_ns_load_table(table_index, acpi_gbl_root_node);
/* Invoke table handler if present */
if (acpi_gbl_table_handler) {
(void)acpi_gbl_table_handler(ACPI_TABLE_EVENT_LOAD, table,
acpi_gbl_table_handler_context);
}
unlock_and_exit:
(void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER);
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -415,9 +378,9 @@ acpi_status acpi_unload_parent_table(acpi_handle object)
return_ACPI_STATUS(AE_TYPE); return_ACPI_STATUS(AE_TYPE);
} }
/* Must acquire the interpreter lock during this operation */ /* Must acquire the table lock during this operation */
status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); status = acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }
@ -444,8 +407,10 @@ acpi_status acpi_unload_parent_table(acpi_handle object)
/* Ensure the table is actually loaded */ /* Ensure the table is actually loaded */
(void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
if (!acpi_tb_is_table_loaded(i)) { if (!acpi_tb_is_table_loaded(i)) {
status = AE_NOT_EXIST; status = AE_NOT_EXIST;
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
break; break;
} }
@ -471,10 +436,11 @@ acpi_status acpi_unload_parent_table(acpi_handle object)
status = acpi_tb_release_owner_id(i); status = acpi_tb_release_owner_id(i);
acpi_tb_set_table_loaded_flag(i, FALSE); acpi_tb_set_table_loaded_flag(i, FALSE);
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
break; break;
} }
(void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES);
return_ACPI_STATUS(status); return_ACPI_STATUS(status);
} }

View File

@ -142,7 +142,8 @@ acpi_status acpi_tb_validate_rsdp(struct acpi_table_rsdp *rsdp)
* *
******************************************************************************/ ******************************************************************************/
acpi_status __init acpi_find_root_pointer(acpi_physical_address *table_address) acpi_status ACPI_INIT_FUNCTION
acpi_find_root_pointer(acpi_physical_address *table_address)
{ {
u8 *table_ptr; u8 *table_ptr;
u8 *mem_rover; u8 *mem_rover;
@ -244,6 +245,8 @@ acpi_status __init acpi_find_root_pointer(acpi_physical_address *table_address)
return_ACPI_STATUS(AE_NOT_FOUND); return_ACPI_STATUS(AE_NOT_FOUND);
} }
ACPI_EXPORT_SYMBOL_INIT(acpi_find_root_pointer)
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_tb_scan_memory_for_rsdp * FUNCTION: acpi_tb_scan_memory_for_rsdp

View File

@ -77,7 +77,6 @@ acpi_ut_add_address_range(acpi_adr_space_type space_id,
u32 length, struct acpi_namespace_node *region_node) u32 length, struct acpi_namespace_node *region_node)
{ {
struct acpi_address_range *range_info; struct acpi_address_range *range_info;
acpi_status status;
ACPI_FUNCTION_TRACE(ut_add_address_range); ACPI_FUNCTION_TRACE(ut_add_address_range);
@ -97,12 +96,6 @@ acpi_ut_add_address_range(acpi_adr_space_type space_id,
range_info->end_address = (address + length - 1); range_info->end_address = (address + length - 1);
range_info->region_node = region_node; range_info->region_node = region_node;
status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
if (ACPI_FAILURE(status)) {
ACPI_FREE(range_info);
return_ACPI_STATUS(status);
}
range_info->next = acpi_gbl_address_range_list[space_id]; range_info->next = acpi_gbl_address_range_list[space_id];
acpi_gbl_address_range_list[space_id] = range_info; acpi_gbl_address_range_list[space_id] = range_info;
@ -112,7 +105,6 @@ acpi_ut_add_address_range(acpi_adr_space_type space_id,
ACPI_FORMAT_UINT64(address), ACPI_FORMAT_UINT64(address),
ACPI_FORMAT_UINT64(range_info->end_address))); ACPI_FORMAT_UINT64(range_info->end_address)));
(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
return_ACPI_STATUS(AE_OK); return_ACPI_STATUS(AE_OK);
} }

View File

@ -239,8 +239,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
u8 buf_char; u8 buf_char;
if (!buffer) { if (!buffer) {
acpi_ut_file_printf(file, fprintf(file, "Null Buffer Pointer in DumpBuffer!\n");
"Null Buffer Pointer in DumpBuffer!\n");
return; return;
} }
@ -254,7 +253,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
/* Print current offset */ /* Print current offset */
acpi_ut_file_printf(file, "%6.4X: ", (base_offset + i)); fprintf(file, "%6.4X: ", (base_offset + i));
/* Print 16 hex chars */ /* Print 16 hex chars */
@ -263,8 +262,7 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
/* Dump fill spaces */ /* Dump fill spaces */
acpi_ut_file_printf(file, "%*s", fprintf(file, "%*s", ((display * 2) + 1), " ");
((display * 2) + 1), " ");
j += display; j += display;
continue; continue;
} }
@ -273,34 +271,34 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
case DB_BYTE_DISPLAY: case DB_BYTE_DISPLAY:
default: /* Default is BYTE display */ default: /* Default is BYTE display */
acpi_ut_file_printf(file, "%02X ", fprintf(file, "%02X ",
buffer[(acpi_size)i + j]); buffer[(acpi_size)i + j]);
break; break;
case DB_WORD_DISPLAY: case DB_WORD_DISPLAY:
ACPI_MOVE_16_TO_32(&temp32, ACPI_MOVE_16_TO_32(&temp32,
&buffer[(acpi_size)i + j]); &buffer[(acpi_size)i + j]);
acpi_ut_file_printf(file, "%04X ", temp32); fprintf(file, "%04X ", temp32);
break; break;
case DB_DWORD_DISPLAY: case DB_DWORD_DISPLAY:
ACPI_MOVE_32_TO_32(&temp32, ACPI_MOVE_32_TO_32(&temp32,
&buffer[(acpi_size)i + j]); &buffer[(acpi_size)i + j]);
acpi_ut_file_printf(file, "%08X ", temp32); fprintf(file, "%08X ", temp32);
break; break;
case DB_QWORD_DISPLAY: case DB_QWORD_DISPLAY:
ACPI_MOVE_32_TO_32(&temp32, ACPI_MOVE_32_TO_32(&temp32,
&buffer[(acpi_size)i + j]); &buffer[(acpi_size)i + j]);
acpi_ut_file_printf(file, "%08X", temp32); fprintf(file, "%08X", temp32);
ACPI_MOVE_32_TO_32(&temp32, ACPI_MOVE_32_TO_32(&temp32,
&buffer[(acpi_size)i + j + &buffer[(acpi_size)i + j +
4]); 4]);
acpi_ut_file_printf(file, "%08X ", temp32); fprintf(file, "%08X ", temp32);
break; break;
} }
@ -311,24 +309,24 @@ acpi_ut_dump_buffer_to_file(ACPI_FILE file,
* Print the ASCII equivalent characters but watch out for the bad * Print the ASCII equivalent characters but watch out for the bad
* unprintable ones (printable chars are 0x20 through 0x7E) * unprintable ones (printable chars are 0x20 through 0x7E)
*/ */
acpi_ut_file_printf(file, " "); fprintf(file, " ");
for (j = 0; j < 16; j++) { for (j = 0; j < 16; j++) {
if (i + j >= count) { if (i + j >= count) {
acpi_ut_file_printf(file, "\n"); fprintf(file, "\n");
return; return;
} }
buf_char = buffer[(acpi_size)i + j]; buf_char = buffer[(acpi_size)i + j];
if (isprint(buf_char)) { if (isprint(buf_char)) {
acpi_ut_file_printf(file, "%c", buf_char); fprintf(file, "%c", buf_char);
} else { } else {
acpi_ut_file_printf(file, "."); fprintf(file, ".");
} }
} }
/* Done with that line. */ /* Done with that line. */
acpi_ut_file_printf(file, "\n"); fprintf(file, "\n");
i += 16; i += 16;
} }

View File

@ -560,6 +560,43 @@ acpi_ut_ptr_exit(u32 line_number,
} }
} }
/*******************************************************************************
*
* FUNCTION: acpi_ut_str_exit
*
* PARAMETERS: line_number - Caller's line number
* function_name - Caller's procedure name
* module_name - Caller's module name
* component_id - Caller's component ID
* string - String to display
*
* RETURN: None
*
* DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is
* set in debug_level. Prints exit value also.
*
******************************************************************************/
void
acpi_ut_str_exit(u32 line_number,
const char *function_name,
const char *module_name, u32 component_id, const char *string)
{
/* Check if enabled up-front for performance */
if (ACPI_IS_DEBUG_ENABLED(ACPI_LV_FUNCTIONS, component_id)) {
acpi_debug_print(ACPI_LV_FUNCTIONS,
line_number, function_name, module_name,
component_id, "%s %s\n",
acpi_gbl_function_exit_prefix, string);
}
if (acpi_gbl_nesting_level) {
acpi_gbl_nesting_level--;
}
}
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_trace_point * FUNCTION: acpi_trace_point
@ -591,27 +628,3 @@ acpi_trace_point(acpi_trace_event_type type, u8 begin, u8 *aml, char *pathname)
ACPI_EXPORT_SYMBOL(acpi_trace_point) ACPI_EXPORT_SYMBOL(acpi_trace_point)
#endif #endif
#ifdef ACPI_APPLICATION
/*******************************************************************************
*
* FUNCTION: acpi_log_error
*
* PARAMETERS: format - Printf format field
* ... - Optional printf arguments
*
* RETURN: None
*
* DESCRIPTION: Print error message to the console, used by applications.
*
******************************************************************************/
void ACPI_INTERNAL_VAR_XFACE acpi_log_error(const char *format, ...)
{
va_list args;
va_start(args, format);
(void)acpi_ut_file_vprintf(ACPI_FILE_ERR, format, args);
va_end(args);
}
ACPI_EXPORT_SYMBOL(acpi_log_error)
#endif

View File

@ -253,7 +253,7 @@ const char *acpi_ut_get_object_type_name(union acpi_operand_object *obj_desc)
return_PTR("Invalid object"); return_PTR("Invalid object");
} }
return_PTR(acpi_ut_get_type_name(obj_desc->common.type)); return_STR(acpi_ut_get_type_name(obj_desc->common.type));
} }
/******************************************************************************* /*******************************************************************************

View File

@ -73,11 +73,43 @@ char acpi_ut_hex_to_ascii_char(u64 integer, u32 position)
return (acpi_gbl_hex_to_ascii[(integer >> position) & 0xF]); return (acpi_gbl_hex_to_ascii[(integer >> position) & 0xF]);
} }
/*******************************************************************************
*
* FUNCTION: acpi_ut_ascii_to_hex_byte
*
* PARAMETERS: two_ascii_chars - Pointer to two ASCII characters
* return_byte - Where converted byte is returned
*
* RETURN: Status and converted hex byte
*
* DESCRIPTION: Perform ascii-to-hex translation, exactly two ASCII characters
* to a single converted byte value.
*
******************************************************************************/
acpi_status acpi_ut_ascii_to_hex_byte(char *two_ascii_chars, u8 *return_byte)
{
/* Both ASCII characters must be valid hex digits */
if (!isxdigit((int)two_ascii_chars[0]) ||
!isxdigit((int)two_ascii_chars[1])) {
return (AE_BAD_HEX_CONSTANT);
}
*return_byte =
acpi_ut_ascii_char_to_hex(two_ascii_chars[1]) |
(acpi_ut_ascii_char_to_hex(two_ascii_chars[0]) << 4);
return (AE_OK);
}
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_ut_ascii_char_to_hex * FUNCTION: acpi_ut_ascii_char_to_hex
* *
* PARAMETERS: hex_char - Hex character in Ascii * PARAMETERS: hex_char - Hex character in Ascii. Must be:
* 0-9 or A-F or a-f
* *
* RETURN: The binary value of the ascii/hex character * RETURN: The binary value of the ascii/hex character
* *
@ -88,13 +120,19 @@ char acpi_ut_hex_to_ascii_char(u64 integer, u32 position)
u8 acpi_ut_ascii_char_to_hex(int hex_char) u8 acpi_ut_ascii_char_to_hex(int hex_char)
{ {
if (hex_char <= 0x39) { /* Values 0-9 */
return ((u8)(hex_char - 0x30));
if (hex_char <= '9') {
return ((u8)(hex_char - '0'));
} }
if (hex_char <= 0x46) { /* Upper case A-F */
if (hex_char <= 'F') {
return ((u8)(hex_char - 0x37)); return ((u8)(hex_char - 0x37));
} }
/* Lower case a-f */
return ((u8)(hex_char - 0x57)); return ((u8)(hex_char - 0x57));
} }

View File

@ -206,7 +206,7 @@ acpi_status acpi_ut_init_globals(void)
acpi_gbl_next_owner_id_offset = 0; acpi_gbl_next_owner_id_offset = 0;
acpi_gbl_debugger_configuration = DEBUGGER_THREADING; acpi_gbl_debugger_configuration = DEBUGGER_THREADING;
acpi_gbl_osi_mutex = NULL; acpi_gbl_osi_mutex = NULL;
acpi_gbl_max_loop_iterations = 0xFFFF; acpi_gbl_max_loop_iterations = ACPI_MAX_LOOP_COUNT;
/* Hardware oriented */ /* Hardware oriented */

View File

@ -48,8 +48,8 @@
ACPI_MODULE_NAME("utnonansi") ACPI_MODULE_NAME("utnonansi")
/* /*
* Non-ANSI C library functions - strlwr, strupr, stricmp, and a 64-bit * Non-ANSI C library functions - strlwr, strupr, stricmp, and "safe"
* version of strtoul. * string functions.
*/ */
/******************************************************************************* /*******************************************************************************
* *
@ -200,356 +200,3 @@ acpi_ut_safe_strncat(char *dest,
return (FALSE); return (FALSE);
} }
#endif #endif
/*******************************************************************************
*
* FUNCTION: acpi_ut_strtoul64
*
* PARAMETERS: string - Null terminated string
* base - Radix of the string: 16 or 10 or
* ACPI_ANY_BASE
* max_integer_byte_width - Maximum allowable integer,in bytes:
* 4 or 8 (32 or 64 bits)
* ret_integer - Where the converted integer is
* returned
*
* RETURN: Status and Converted value
*
* DESCRIPTION: Convert a string into an unsigned value. Performs either a
* 32-bit or 64-bit conversion, depending on the input integer
* size (often the current mode of the interpreter).
*
* NOTES: Negative numbers are not supported, as they are not supported
* by ACPI.
*
* acpi_gbl_integer_byte_width should be set to the proper width.
* For the core ACPICA code, this width depends on the DSDT
* version. For iASL, the default byte width is always 8 for the
* parser, but error checking is performed later to flag cases
* where a 64-bit constant is defined in a 32-bit DSDT/SSDT.
*
* Does not support Octal strings, not needed at this time.
*
******************************************************************************/
acpi_status
acpi_ut_strtoul64(char *string,
u32 base, u32 max_integer_byte_width, u64 *ret_integer)
{
u32 this_digit = 0;
u64 return_value = 0;
u64 quotient;
u64 dividend;
u8 valid_digits = 0;
u8 sign_of0x = 0;
u8 term = 0;
ACPI_FUNCTION_TRACE_STR(ut_strtoul64, string);
switch (base) {
case ACPI_ANY_BASE:
case 10:
case 16:
break;
default:
/* Invalid Base */
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
if (!string) {
goto error_exit;
}
/* Skip over any white space in the buffer */
while ((*string) && (isspace((int)*string) || *string == '\t')) {
string++;
}
if (base == ACPI_ANY_BASE) {
/*
* Base equal to ACPI_ANY_BASE means 'Either decimal or hex'.
* We need to determine if it is decimal or hexadecimal.
*/
if ((*string == '0') && (tolower((int)*(string + 1)) == 'x')) {
sign_of0x = 1;
base = 16;
/* Skip over the leading '0x' */
string += 2;
} else {
base = 10;
}
}
/* Any string left? Check that '0x' is not followed by white space. */
if (!(*string) || isspace((int)*string) || *string == '\t') {
if (base == ACPI_ANY_BASE) {
goto error_exit;
} else {
goto all_done;
}
}
/*
* Perform a 32-bit or 64-bit conversion, depending upon the input
* byte width
*/
dividend = (max_integer_byte_width <= ACPI_MAX32_BYTE_WIDTH) ?
ACPI_UINT32_MAX : ACPI_UINT64_MAX;
/* Main loop: convert the string to a 32- or 64-bit integer */
while (*string) {
if (isdigit((int)*string)) {
/* Convert ASCII 0-9 to Decimal value */
this_digit = ((u8)*string) - '0';
} else if (base == 10) {
/* Digit is out of range; possible in to_integer case only */
term = 1;
} else {
this_digit = (u8)toupper((int)*string);
if (isxdigit((int)this_digit)) {
/* Convert ASCII Hex char to value */
this_digit = this_digit - 'A' + 10;
} else {
term = 1;
}
}
if (term) {
if (base == ACPI_ANY_BASE) {
goto error_exit;
} else {
break;
}
} else if ((valid_digits == 0) && (this_digit == 0)
&& !sign_of0x) {
/* Skip zeros */
string++;
continue;
}
valid_digits++;
if (sign_of0x && ((valid_digits > 16) ||
((valid_digits > 8)
&& (max_integer_byte_width <=
ACPI_MAX32_BYTE_WIDTH)))) {
/*
* This is to_integer operation case.
* No restrictions for string-to-integer conversion,
* see ACPI spec.
*/
goto error_exit;
}
/* Divide the digit into the correct position */
(void)acpi_ut_short_divide((dividend - (u64)this_digit), base,
&quotient, NULL);
if (return_value > quotient) {
if (base == ACPI_ANY_BASE) {
goto error_exit;
} else {
break;
}
}
return_value *= base;
return_value += this_digit;
string++;
}
/* All done, normal exit */
all_done:
ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n",
ACPI_FORMAT_UINT64(return_value)));
*ret_integer = return_value;
return_ACPI_STATUS(AE_OK);
error_exit:
/* Base was set/validated above (10 or 16) */
if (base == 10) {
return_ACPI_STATUS(AE_BAD_DECIMAL_CONSTANT);
} else {
return_ACPI_STATUS(AE_BAD_HEX_CONSTANT);
}
}
#ifdef _OBSOLETE_FUNCTIONS
/* Removed: 01/2016 */
/*******************************************************************************
*
* FUNCTION: strtoul64
*
* PARAMETERS: string - Null terminated string
* terminater - Where a pointer to the terminating byte
* is returned
* base - Radix of the string
*
* RETURN: Converted value
*
* DESCRIPTION: Convert a string into an unsigned value.
*
******************************************************************************/
acpi_status strtoul64(char *string, u32 base, u64 *ret_integer)
{
u32 index;
u32 sign;
u64 return_value = 0;
acpi_status status = AE_OK;
*ret_integer = 0;
switch (base) {
case 0:
case 8:
case 10:
case 16:
break;
default:
/*
* The specified Base parameter is not in the domain of
* this function:
*/
return (AE_BAD_PARAMETER);
}
/* Skip over any white space in the buffer: */
while (isspace((int)*string) || *string == '\t') {
++string;
}
/*
* The buffer may contain an optional plus or minus sign.
* If it does, then skip over it but remember what is was:
*/
if (*string == '-') {
sign = ACPI_SIGN_NEGATIVE;
++string;
} else if (*string == '+') {
++string;
sign = ACPI_SIGN_POSITIVE;
} else {
sign = ACPI_SIGN_POSITIVE;
}
/*
* If the input parameter Base is zero, then we need to
* determine if it is octal, decimal, or hexadecimal:
*/
if (base == 0) {
if (*string == '0') {
if (tolower((int)*(++string)) == 'x') {
base = 16;
++string;
} else {
base = 8;
}
} else {
base = 10;
}
}
/*
* For octal and hexadecimal bases, skip over the leading
* 0 or 0x, if they are present.
*/
if (base == 8 && *string == '0') {
string++;
}
if (base == 16 && *string == '0' && tolower((int)*(++string)) == 'x') {
string++;
}
/* Main loop: convert the string to an unsigned long */
while (*string) {
if (isdigit((int)*string)) {
index = ((u8)*string) - '0';
} else {
index = (u8)toupper((int)*string);
if (isupper((int)index)) {
index = index - 'A' + 10;
} else {
goto error_exit;
}
}
if (index >= base) {
goto error_exit;
}
/* Check to see if value is out of range: */
if (return_value > ((ACPI_UINT64_MAX - (u64)index) / (u64)base)) {
goto error_exit;
} else {
return_value *= base;
return_value += index;
}
++string;
}
/* If a minus sign was present, then "the conversion is negated": */
if (sign == ACPI_SIGN_NEGATIVE) {
return_value = (ACPI_UINT32_MAX - return_value) + 1;
}
*ret_integer = return_value;
return (status);
error_exit:
switch (base) {
case 8:
status = AE_BAD_OCTAL_CONSTANT;
break;
case 10:
status = AE_BAD_DECIMAL_CONSTANT;
break;
case 16:
status = AE_BAD_HEX_CONSTANT;
break;
default:
/* Base validated above */
break;
}
return (status);
}
#endif

View File

@ -390,11 +390,22 @@ struct acpi_interface_info *acpi_ut_get_interface(acpi_string interface_name)
* PARAMETERS: walk_state - Current walk state * PARAMETERS: walk_state - Current walk state
* *
* RETURN: Status * RETURN: Status
* Integer: TRUE (0) if input string is matched
* FALSE (-1) if string is not matched
* *
* DESCRIPTION: Implementation of the _OSI predefined control method. When * DESCRIPTION: Implementation of the _OSI predefined control method. When
* an invocation of _OSI is encountered in the system AML, * an invocation of _OSI is encountered in the system AML,
* control is transferred to this function. * control is transferred to this function.
* *
* (August 2016)
* Note: _OSI is now defined to return "Ones" to indicate a match, for
* compatibility with other ACPI implementations. On a 32-bit DSDT, Ones
* is 0xFFFFFFFF. On a 64-bit DSDT, Ones is 0xFFFFFFFFFFFFFFFF
* (ACPI_UINT64_MAX).
*
* This function always returns ACPI_UINT64_MAX for TRUE, and later code
* will truncate this to 32 bits if necessary.
*
******************************************************************************/ ******************************************************************************/
acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state) acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
@ -404,7 +415,7 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
struct acpi_interface_info *interface_info; struct acpi_interface_info *interface_info;
acpi_interface_handler interface_handler; acpi_interface_handler interface_handler;
acpi_status status; acpi_status status;
u32 return_value; u64 return_value;
ACPI_FUNCTION_TRACE(ut_osi_implementation); ACPI_FUNCTION_TRACE(ut_osi_implementation);
@ -444,7 +455,7 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
acpi_gbl_osi_data = interface_info->value; acpi_gbl_osi_data = interface_info->value;
} }
return_value = ACPI_UINT32_MAX; return_value = ACPI_UINT64_MAX;
} }
acpi_os_release_mutex(acpi_gbl_osi_mutex); acpi_os_release_mutex(acpi_gbl_osi_mutex);
@ -456,9 +467,10 @@ acpi_status acpi_ut_osi_implementation(struct acpi_walk_state *walk_state)
*/ */
interface_handler = acpi_gbl_interface_handler; interface_handler = acpi_gbl_interface_handler;
if (interface_handler) { if (interface_handler) {
return_value = if (interface_handler
interface_handler(string_desc->string.pointer, (string_desc->string.pointer, (u32)return_value)) {
return_value); return_value = ACPI_UINT64_MAX;
}
} }
ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO,

View File

@ -176,8 +176,6 @@ void acpi_ut_get_expected_return_types(char *buffer, u32 expected_btypes)
******************************************************************************/ ******************************************************************************/
#if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP) #if (defined ACPI_ASL_COMPILER || defined ACPI_HELP_APP)
#include <stdio.h>
#include <string.h>
/* Local prototypes */ /* Local prototypes */

View File

@ -336,7 +336,7 @@ static char *acpi_ut_format_number(char *string,
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_ut_vsnprintf * FUNCTION: vsnprintf
* *
* PARAMETERS: string - String with boundary * PARAMETERS: string - String with boundary
* size - Boundary of the string * size - Boundary of the string
@ -349,9 +349,7 @@ static char *acpi_ut_format_number(char *string,
* *
******************************************************************************/ ******************************************************************************/
int int vsnprintf(char *string, acpi_size size, const char *format, va_list args)
acpi_ut_vsnprintf(char *string,
acpi_size size, const char *format, va_list args)
{ {
u8 base; u8 base;
u8 type; u8 type;
@ -586,7 +584,7 @@ acpi_ut_vsnprintf(char *string,
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_ut_snprintf * FUNCTION: snprintf
* *
* PARAMETERS: string - String with boundary * PARAMETERS: string - String with boundary
* size - Boundary of the string * size - Boundary of the string
@ -598,13 +596,38 @@ acpi_ut_vsnprintf(char *string,
* *
******************************************************************************/ ******************************************************************************/
int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...) int snprintf(char *string, acpi_size size, const char *format, ...)
{ {
va_list args; va_list args;
int length; int length;
va_start(args, format); va_start(args, format);
length = acpi_ut_vsnprintf(string, size, format, args); length = vsnprintf(string, size, format, args);
va_end(args);
return (length);
}
/*******************************************************************************
*
* FUNCTION: sprintf
*
* PARAMETERS: string - String with boundary
* Format, ... - Standard printf format
*
* RETURN: Number of bytes actually written.
*
* DESCRIPTION: Formatted output to a string.
*
******************************************************************************/
int sprintf(char *string, const char *format, ...)
{
va_list args;
int length;
va_start(args, format);
length = vsnprintf(string, ACPI_UINT32_MAX, format, args);
va_end(args); va_end(args);
return (length); return (length);
@ -613,7 +636,59 @@ int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...)
#ifdef ACPI_APPLICATION #ifdef ACPI_APPLICATION
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_ut_file_vprintf * FUNCTION: vprintf
*
* PARAMETERS: format - Standard printf format
* args - Argument list
*
* RETURN: Number of bytes actually written.
*
* DESCRIPTION: Formatted output to stdout using argument list pointer.
*
******************************************************************************/
int vprintf(const char *format, va_list args)
{
acpi_cpu_flags flags;
int length;
flags = acpi_os_acquire_lock(acpi_gbl_print_lock);
length = vsnprintf(acpi_gbl_print_buffer,
sizeof(acpi_gbl_print_buffer), format, args);
(void)fwrite(acpi_gbl_print_buffer, length, 1, ACPI_FILE_OUT);
acpi_os_release_lock(acpi_gbl_print_lock, flags);
return (length);
}
/*******************************************************************************
*
* FUNCTION: printf
*
* PARAMETERS: Format, ... - Standard printf format
*
* RETURN: Number of bytes actually written.
*
* DESCRIPTION: Formatted output to stdout.
*
******************************************************************************/
int printf(const char *format, ...)
{
va_list args;
int length;
va_start(args, format);
length = vprintf(format, args);
va_end(args);
return (length);
}
/*******************************************************************************
*
* FUNCTION: vfprintf
* *
* PARAMETERS: file - File descriptor * PARAMETERS: file - File descriptor
* format - Standard printf format * format - Standard printf format
@ -625,16 +700,16 @@ int acpi_ut_snprintf(char *string, acpi_size size, const char *format, ...)
* *
******************************************************************************/ ******************************************************************************/
int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args) int vfprintf(FILE * file, const char *format, va_list args)
{ {
acpi_cpu_flags flags; acpi_cpu_flags flags;
int length; int length;
flags = acpi_os_acquire_lock(acpi_gbl_print_lock); flags = acpi_os_acquire_lock(acpi_gbl_print_lock);
length = acpi_ut_vsnprintf(acpi_gbl_print_buffer, length = vsnprintf(acpi_gbl_print_buffer,
sizeof(acpi_gbl_print_buffer), format, args); sizeof(acpi_gbl_print_buffer), format, args);
(void)acpi_os_write_file(file, acpi_gbl_print_buffer, length, 1); (void)fwrite(acpi_gbl_print_buffer, length, 1, file);
acpi_os_release_lock(acpi_gbl_print_lock, flags); acpi_os_release_lock(acpi_gbl_print_lock, flags);
return (length); return (length);
@ -642,7 +717,7 @@ int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args)
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_ut_file_printf * FUNCTION: fprintf
* *
* PARAMETERS: file - File descriptor * PARAMETERS: file - File descriptor
* Format, ... - Standard printf format * Format, ... - Standard printf format
@ -653,13 +728,13 @@ int acpi_ut_file_vprintf(ACPI_FILE file, const char *format, va_list args)
* *
******************************************************************************/ ******************************************************************************/
int acpi_ut_file_printf(ACPI_FILE file, const char *format, ...) int fprintf(FILE * file, const char *format, ...)
{ {
va_list args; va_list args;
int length; int length;
va_start(args, format); va_start(args, format);
length = acpi_ut_file_vprintf(file, format, args); length = vfprintf(file, format, args);
va_end(args); va_end(args);
return (length); return (length);

View File

@ -0,0 +1,348 @@
/*******************************************************************************
*
* Module Name: utstrtoul64 - string to 64-bit integer support
*
******************************************************************************/
/*
* Copyright (C) 2000 - 2016, Intel Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
* substantially similar to the "NO WARRANTY" disclaimer below
* ("Disclaimer") and any redistribution must be conditioned upon
* including a substantially similar Disclaimer requirement for further
* binary redistribution.
* 3. Neither the names of the above-listed copyright holders nor the names
* of any contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*
* NO WARRANTY
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*/
#include <acpi/acpi.h>
#include "accommon.h"
/*******************************************************************************
*
* The functions in this module satisfy the need for 64-bit string-to-integer
* conversions on both 32-bit and 64-bit platforms.
*
******************************************************************************/
#define _COMPONENT ACPI_UTILITIES
ACPI_MODULE_NAME("utstrtoul64")
/* Local prototypes */
static u64 acpi_ut_strtoul_base10(char *string, u32 flags);
static u64 acpi_ut_strtoul_base16(char *string, u32 flags);
/*******************************************************************************
*
* String conversion rules as written in the ACPI specification. The error
* conditions and behavior are different depending on the type of conversion.
*
*
* Implicit data type conversion: string-to-integer
* --------------------------------------------------
*
* Base is always 16. This is the ACPI_STRTOUL_BASE16 case.
*
* Example:
* Add ("BA98", Arg0, Local0)
*
* The integer is initialized to the value zero.
* The ASCII string is interpreted as a hexadecimal constant.
*
* 1) A "0x" prefix is not allowed. However, ACPICA allows this for
* compatibility with previous ACPICA. (NO ERROR)
*
* 2) Terminates when the size of an integer is reached (32 or 64 bits).
* (NO ERROR)
*
* 3) The first non-hex character terminates the conversion without error.
* (NO ERROR)
*
* 4) Conversion of a null (zero-length) string to an integer is not
* allowed. However, ACPICA allows this for compatibility with previous
* ACPICA. This conversion returns the value 0. (NO ERROR)
*
*
* Explicit data type conversion: to_integer() with string operand
* ---------------------------------------------------------------
*
* Base is either 10 (default) or 16 (with 0x prefix)
*
* Examples:
* to_integer ("1000")
* to_integer ("0xABCD")
*
* 1) Can be (must be) either a decimal or hexadecimal numeric string.
* A hex value must be prefixed by "0x" or it is interpreted as a decimal.
*
* 2) The value must not exceed the maximum of an integer value. ACPI spec
* states the behavior is "unpredictable", so ACPICA matches the behavior
* of the implicit conversion case.(NO ERROR)
*
* 3) Behavior on the first non-hex character is not specified by the ACPI
* spec, so ACPICA matches the behavior of the implicit conversion case
* and terminates. (NO ERROR)
*
* 4) A null (zero-length) string is illegal.
* However, ACPICA allows this for compatibility with previous ACPICA.
* This conversion returns the value 0. (NO ERROR)
*
******************************************************************************/
/*******************************************************************************
*
* FUNCTION: acpi_ut_strtoul64
*
* PARAMETERS: string - Null terminated input string
* flags - Conversion info, see below
* return_value - Where the converted integer is
* returned
*
* RETURN: Status and Converted value
*
* DESCRIPTION: Convert a string into an unsigned value. Performs either a
* 32-bit or 64-bit conversion, depending on the input integer
* size in Flags (often the current mode of the interpreter).
*
* Values for Flags:
* ACPI_STRTOUL_32BIT - Max integer value is 32 bits
* ACPI_STRTOUL_64BIT - Max integer value is 64 bits
* ACPI_STRTOUL_BASE16 - Input string is hexadecimal. Default
* is 10/16 based on string prefix (0x).
*
* NOTES:
* Negative numbers are not supported, as they are not supported by ACPI.
*
* Supports only base 16 or base 10 strings/values. Does not
* support Octal strings, as these are not supported by ACPI.
*
* Current users of this support:
*
* interpreter - Implicit and explicit conversions, GPE method names
* debugger - Command line input string conversion
* iASL - Main parser, conversion of constants to integers
* iASL - Data Table Compiler parser (constant math expressions)
* iASL - Preprocessor (constant math expressions)
* acpi_dump - Input table addresses
* acpi_exec - Testing of the acpi_ut_strtoul64 function
*
* Note concerning callers:
* acpi_gbl_integer_byte_width can be used to set the 32/64 limit. If used,
* this global should be set to the proper width. For the core ACPICA code,
* this width depends on the DSDT version. For iASL, the default byte
* width is always 8 for the parser, but error checking is performed later
* to flag cases where a 64-bit constant is defined in a 32-bit DSDT/SSDT.
*
******************************************************************************/
acpi_status acpi_ut_strtoul64(char *string, u32 flags, u64 *return_value)
{
acpi_status status = AE_OK;
u32 base;
ACPI_FUNCTION_TRACE_STR(ut_strtoul64, string);
/* Parameter validation */
if (!string || !return_value) {
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
*return_value = 0;
/* Check for zero-length string, returns 0 */
if (*string == 0) {
return_ACPI_STATUS(AE_OK);
}
/* Skip over any white space at start of string */
while (isspace((int)*string)) {
string++;
}
/* End of string? return 0 */
if (*string == 0) {
return_ACPI_STATUS(AE_OK);
}
/*
* 1) The "0x" prefix indicates base 16. Per the ACPI specification,
* the "0x" prefix is only allowed for implicit (non-strict) conversions.
* However, we always allow it for compatibility with older ACPICA.
*/
if ((*string == ACPI_ASCII_ZERO) &&
(tolower((int)*(string + 1)) == 'x')) {
string += 2; /* Go past the 0x */
if (*string == 0) {
return_ACPI_STATUS(AE_OK); /* Return value 0 */
}
base = 16;
}
/* 2) Force to base 16 (implicit conversion case) */
else if (flags & ACPI_STRTOUL_BASE16) {
base = 16;
}
/* 3) Default fallback is to Base 10 */
else {
base = 10;
}
/* Skip all leading zeros */
while (*string == ACPI_ASCII_ZERO) {
string++;
if (*string == 0) {
return_ACPI_STATUS(AE_OK); /* Return value 0 */
}
}
/* Perform the base 16 or 10 conversion */
if (base == 16) {
*return_value = acpi_ut_strtoul_base16(string, flags);
} else {
*return_value = acpi_ut_strtoul_base10(string, flags);
}
return_ACPI_STATUS(status);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_strtoul_base10
*
* PARAMETERS: string - Null terminated input string
* flags - Conversion info
*
* RETURN: 64-bit converted integer
*
* DESCRIPTION: Performs a base 10 conversion of the input string to an
* integer value, either 32 or 64 bits.
* Note: String must be valid and non-null.
*
******************************************************************************/
static u64 acpi_ut_strtoul_base10(char *string, u32 flags)
{
int ascii_digit;
u64 next_value;
u64 return_value = 0;
/* Main loop: convert each ASCII byte in the input string */
while (*string) {
ascii_digit = *string;
if (!isdigit(ascii_digit)) {
/* Not ASCII 0-9, terminate */
goto exit;
}
/* Convert and insert (add) the decimal digit */
next_value =
(return_value * 10) + (ascii_digit - ACPI_ASCII_ZERO);
/* Check for overflow (32 or 64 bit) - return current converted value */
if (((flags & ACPI_STRTOUL_32BIT) && (next_value > ACPI_UINT32_MAX)) || (next_value < return_value)) { /* 64-bit overflow case */
goto exit;
}
return_value = next_value;
string++;
}
exit:
return (return_value);
}
/*******************************************************************************
*
* FUNCTION: acpi_ut_strtoul_base16
*
* PARAMETERS: string - Null terminated input string
* flags - conversion info
*
* RETURN: 64-bit converted integer
*
* DESCRIPTION: Performs a base 16 conversion of the input string to an
* integer value, either 32 or 64 bits.
* Note: String must be valid and non-null.
*
******************************************************************************/
static u64 acpi_ut_strtoul_base16(char *string, u32 flags)
{
int ascii_digit;
u32 valid_digits = 1;
u64 return_value = 0;
/* Main loop: convert each ASCII byte in the input string */
while (*string) {
/* Check for overflow (32 or 64 bit) - return current converted value */
if ((valid_digits > 16) ||
((valid_digits > 8) && (flags & ACPI_STRTOUL_32BIT))) {
goto exit;
}
ascii_digit = *string;
if (!isxdigit(ascii_digit)) {
/* Not Hex ASCII A-F, a-f, or 0-9, terminate */
goto exit;
}
/* Convert and insert the hex digit */
return_value =
(return_value << 4) |
acpi_ut_ascii_char_to_hex(ascii_digit);
string++;
valid_digits++;
}
exit:
return (return_value);
}

View File

@ -95,13 +95,11 @@ acpi_ut_create_list(const char *list_name,
{ {
struct acpi_memory_list *cache; struct acpi_memory_list *cache;
cache = acpi_os_allocate(sizeof(struct acpi_memory_list)); cache = acpi_os_allocate_zeroed(sizeof(struct acpi_memory_list));
if (!cache) { if (!cache) {
return (AE_NO_MEMORY); return (AE_NO_MEMORY);
} }
memset(cache, 0, sizeof(struct acpi_memory_list));
cache->list_name = list_name; cache->list_name = list_name;
cache->object_size = object_size; cache->object_size = object_size;

View File

@ -61,7 +61,7 @@ ACPI_MODULE_NAME("utxface")
* DESCRIPTION: Shutdown the ACPICA subsystem and release all resources. * DESCRIPTION: Shutdown the ACPICA subsystem and release all resources.
* *
******************************************************************************/ ******************************************************************************/
acpi_status __init acpi_terminate(void) acpi_status ACPI_INIT_FUNCTION acpi_terminate(void)
{ {
acpi_status status; acpi_status status;

View File

@ -69,7 +69,7 @@ void ae_do_object_overrides(void);
* *
******************************************************************************/ ******************************************************************************/
acpi_status __init acpi_initialize_subsystem(void) acpi_status ACPI_INIT_FUNCTION acpi_initialize_subsystem(void)
{ {
acpi_status status; acpi_status status;
@ -141,7 +141,7 @@ ACPI_EXPORT_SYMBOL_INIT(acpi_initialize_subsystem)
* Puts system into ACPI mode if it isn't already. * Puts system into ACPI mode if it isn't already.
* *
******************************************************************************/ ******************************************************************************/
acpi_status __init acpi_enable_subsystem(u32 flags) acpi_status ACPI_INIT_FUNCTION acpi_enable_subsystem(u32 flags)
{ {
acpi_status status = AE_OK; acpi_status status = AE_OK;
@ -239,7 +239,7 @@ ACPI_EXPORT_SYMBOL_INIT(acpi_enable_subsystem)
* objects and executing AML code for Regions, buffers, etc. * objects and executing AML code for Regions, buffers, etc.
* *
******************************************************************************/ ******************************************************************************/
acpi_status __init acpi_initialize_objects(u32 flags) acpi_status ACPI_INIT_FUNCTION acpi_initialize_objects(u32 flags)
{ {
acpi_status status = AE_OK; acpi_status status = AE_OK;
@ -265,7 +265,8 @@ acpi_status __init acpi_initialize_objects(u32 flags)
* all of the tables have been loaded. It is a legacy option and is * all of the tables have been loaded. It is a legacy option and is
* not compatible with other ACPI implementations. See acpi_ns_load_table. * not compatible with other ACPI implementations. See acpi_ns_load_table.
*/ */
if (acpi_gbl_group_module_level_code) { if (!acpi_gbl_parse_table_as_term_list
&& acpi_gbl_group_module_level_code) {
acpi_ns_exec_module_code_list(); acpi_ns_exec_module_code_list();
/* /*

View File

@ -733,15 +733,17 @@ static int acpi_battery_update(struct acpi_battery *battery, bool resume)
return result; return result;
acpi_battery_init_alarm(battery); acpi_battery_init_alarm(battery);
} }
result = acpi_battery_get_state(battery);
if (result)
return result;
acpi_battery_quirks(battery);
if (!battery->bat) { if (!battery->bat) {
result = sysfs_add_battery(battery); result = sysfs_add_battery(battery);
if (result) if (result)
return result; return result;
} }
result = acpi_battery_get_state(battery);
if (result)
return result;
acpi_battery_quirks(battery);
/* /*
* Wakeup the system if battery is critical low * Wakeup the system if battery is critical low

View File

@ -985,7 +985,8 @@ void __init acpi_early_init(void)
goto error0; goto error0;
} }
if (acpi_gbl_group_module_level_code) { if (!acpi_gbl_parse_table_as_term_list &&
acpi_gbl_group_module_level_code) {
status = acpi_load_tables(); status = acpi_load_tables();
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX printk(KERN_ERR PREFIX
@ -1074,7 +1075,8 @@ static int __init acpi_bus_init(void)
status = acpi_ec_ecdt_probe(); status = acpi_ec_ecdt_probe();
/* Ignore result. Not having an ECDT is not fatal. */ /* Ignore result. Not having an ECDT is not fatal. */
if (!acpi_gbl_group_module_level_code) { if (acpi_gbl_parse_table_as_term_list ||
!acpi_gbl_group_module_level_code) {
status = acpi_load_tables(); status = acpi_load_tables();
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
printk(KERN_ERR PREFIX printk(KERN_ERR PREFIX

View File

@ -19,6 +19,8 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
#define pr_fmt(fmt) "ACPI : button: " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
@ -104,6 +106,8 @@ struct acpi_button {
struct input_dev *input; struct input_dev *input;
char phys[32]; /* for input device */ char phys[32]; /* for input device */
unsigned long pushed; unsigned long pushed;
int last_state;
ktime_t last_time;
bool suspended; bool suspended;
}; };
@ -111,6 +115,10 @@ static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
static struct acpi_device *lid_device; static struct acpi_device *lid_device;
static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
static unsigned long lid_report_interval __read_mostly = 500;
module_param(lid_report_interval, ulong, 0644);
MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
FS Interface (/proc) FS Interface (/proc)
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
@ -134,10 +142,79 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state)
{ {
struct acpi_button *button = acpi_driver_data(device); struct acpi_button *button = acpi_driver_data(device);
int ret; int ret;
ktime_t next_report;
bool do_update;
/* input layer checks if event is redundant */ /*
input_report_switch(button->input, SW_LID, !state); * In lid_init_state=ignore mode, if user opens/closes lid
input_sync(button->input); * frequently with "open" missing, and "last_time" is also updated
* frequently, "close" cannot be delivered to the userspace.
* So "last_time" is only updated after a timeout or an actual
* switch.
*/
if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
button->last_state != !!state)
do_update = true;
else
do_update = false;
next_report = ktime_add(button->last_time,
ms_to_ktime(lid_report_interval));
if (button->last_state == !!state &&
ktime_after(ktime_get(), next_report)) {
/* Complain the buggy firmware */
pr_warn_once("The lid device is not compliant to SW_LID.\n");
/*
* Send the unreliable complement switch event:
*
* On most platforms, the lid device is reliable. However
* there are exceptions:
* 1. Platforms returning initial lid state as "close" by
* default after booting/resuming:
* https://bugzilla.kernel.org/show_bug.cgi?id=89211
* https://bugzilla.kernel.org/show_bug.cgi?id=106151
* 2. Platforms never reporting "open" events:
* https://bugzilla.kernel.org/show_bug.cgi?id=106941
* On these buggy platforms, the usage model of the ACPI
* lid device actually is:
* 1. The initial returning value of _LID may not be
* reliable.
* 2. The open event may not be reliable.
* 3. The close event is reliable.
*
* But SW_LID is typed as input switch event, the input
* layer checks if the event is redundant. Hence if the
* state is not switched, the userspace cannot see this
* platform triggered reliable event. By inserting a
* complement switch event, it then is guaranteed that the
* platform triggered reliable one can always be seen by
* the userspace.
*/
if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
do_update = true;
/*
* Do generate complement switch event for "close"
* as "close" is reliable and wrong "open" won't
* trigger unexpected behaviors.
* Do not generate complement switch event for
* "open" as "open" is not reliable and wrong
* "close" will trigger unexpected behaviors.
*/
if (!state) {
input_report_switch(button->input,
SW_LID, state);
input_sync(button->input);
}
}
}
/* Send the platform triggered reliable event */
if (do_update) {
input_report_switch(button->input, SW_LID, !state);
input_sync(button->input);
button->last_state = !!state;
button->last_time = ktime_get();
}
if (state) if (state)
pm_wakeup_event(&device->dev, 0); pm_wakeup_event(&device->dev, 0);
@ -411,6 +488,8 @@ static int acpi_button_add(struct acpi_device *device)
strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID); strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
sprintf(class, "%s/%s", sprintf(class, "%s/%s",
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
button->last_state = !!acpi_lid_evaluate_state(device);
button->last_time = ktime_get();
} else { } else {
printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid); printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
error = -ENODEV; error = -ENODEV;

View File

@ -40,15 +40,48 @@
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/rwsem.h>
#include <linux/wait.h>
#include <acpi/cppc_acpi.h> #include <acpi/cppc_acpi.h>
/*
* Lock to provide mutually exclusive access to the PCC struct cppc_pcc_data {
* channel. e.g. When the remote updates the shared region struct mbox_chan *pcc_channel;
* with new data, the reader needs to be protected from void __iomem *pcc_comm_addr;
* other CPUs activity on the same channel. int pcc_subspace_idx;
*/ bool pcc_channel_acquired;
static DEFINE_SPINLOCK(pcc_lock); ktime_t deadline;
unsigned int pcc_mpar, pcc_mrtt, pcc_nominal;
bool pending_pcc_write_cmd; /* Any pending/batched PCC write cmds? */
bool platform_owns_pcc; /* Ownership of PCC subspace */
unsigned int pcc_write_cnt; /* Running count of PCC write commands */
/*
* Lock to provide controlled access to the PCC channel.
*
* For performance critical usecases(currently cppc_set_perf)
* We need to take read_lock and check if channel belongs to OSPM
* before reading or writing to PCC subspace
* We need to take write_lock before transferring the channel
* ownership to the platform via a Doorbell
* This allows us to batch a number of CPPC requests if they happen
* to originate in about the same time
*
* For non-performance critical usecases(init)
* Take write_lock for all purposes which gives exclusive access
*/
struct rw_semaphore pcc_lock;
/* Wait queue for CPUs whose requests were batched */
wait_queue_head_t pcc_write_wait_q;
};
/* Structure to represent the single PCC channel */
static struct cppc_pcc_data pcc_data = {
.pcc_subspace_idx = -1,
.platform_owns_pcc = true,
};
/* /*
* The cpc_desc structure contains the ACPI register details * The cpc_desc structure contains the ACPI register details
@ -59,18 +92,25 @@ static DEFINE_SPINLOCK(pcc_lock);
*/ */
static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr); static DEFINE_PER_CPU(struct cpc_desc *, cpc_desc_ptr);
/* This layer handles all the PCC specifics for CPPC. */
static struct mbox_chan *pcc_channel;
static void __iomem *pcc_comm_addr;
static u64 comm_base_addr;
static int pcc_subspace_idx = -1;
static bool pcc_channel_acquired;
static ktime_t deadline;
static unsigned int pcc_mpar, pcc_mrtt;
/* pcc mapped address + header size + offset within PCC subspace */ /* pcc mapped address + header size + offset within PCC subspace */
#define GET_PCC_VADDR(offs) (pcc_comm_addr + 0x8 + (offs)) #define GET_PCC_VADDR(offs) (pcc_data.pcc_comm_addr + 0x8 + (offs))
/* Check if a CPC regsiter is in PCC */
#define CPC_IN_PCC(cpc) ((cpc)->type == ACPI_TYPE_BUFFER && \
(cpc)->cpc_entry.reg.space_id == \
ACPI_ADR_SPACE_PLATFORM_COMM)
/* Evalutes to True if reg is a NULL register descriptor */
#define IS_NULL_REG(reg) ((reg)->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && \
(reg)->address == 0 && \
(reg)->bit_width == 0 && \
(reg)->bit_offset == 0 && \
(reg)->access_width == 0)
/* Evalutes to True if an optional cpc field is supported */
#define CPC_SUPPORTED(cpc) ((cpc)->type == ACPI_TYPE_INTEGER ? \
!!(cpc)->cpc_entry.int_value : \
!IS_NULL_REG(&(cpc)->cpc_entry.reg))
/* /*
* Arbitrary Retries in case the remote processor is slow to respond * Arbitrary Retries in case the remote processor is slow to respond
* to PCC commands. Keeping it high enough to cover emulators where * to PCC commands. Keeping it high enough to cover emulators where
@ -78,11 +118,79 @@ static unsigned int pcc_mpar, pcc_mrtt;
*/ */
#define NUM_RETRIES 500 #define NUM_RETRIES 500
static int check_pcc_chan(void) struct cppc_attr {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj,
struct attribute *attr, char *buf);
ssize_t (*store)(struct kobject *kobj,
struct attribute *attr, const char *c, ssize_t count);
};
#define define_one_cppc_ro(_name) \
static struct cppc_attr _name = \
__ATTR(_name, 0444, show_##_name, NULL)
#define to_cpc_desc(a) container_of(a, struct cpc_desc, kobj)
static ssize_t show_feedback_ctrs(struct kobject *kobj,
struct attribute *attr, char *buf)
{ {
int ret = -EIO; struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_comm_addr; struct cppc_perf_fb_ctrs fb_ctrs = {0};
ktime_t next_deadline = ktime_add(ktime_get(), deadline);
cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
return scnprintf(buf, PAGE_SIZE, "ref:%llu del:%llu\n",
fb_ctrs.reference, fb_ctrs.delivered);
}
define_one_cppc_ro(feedback_ctrs);
static ssize_t show_reference_perf(struct kobject *kobj,
struct attribute *attr, char *buf)
{
struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
struct cppc_perf_fb_ctrs fb_ctrs = {0};
cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
return scnprintf(buf, PAGE_SIZE, "%llu\n",
fb_ctrs.reference_perf);
}
define_one_cppc_ro(reference_perf);
static ssize_t show_wraparound_time(struct kobject *kobj,
struct attribute *attr, char *buf)
{
struct cpc_desc *cpc_ptr = to_cpc_desc(kobj);
struct cppc_perf_fb_ctrs fb_ctrs = {0};
cppc_get_perf_ctrs(cpc_ptr->cpu_id, &fb_ctrs);
return scnprintf(buf, PAGE_SIZE, "%llu\n", fb_ctrs.ctr_wrap_time);
}
define_one_cppc_ro(wraparound_time);
static struct attribute *cppc_attrs[] = {
&feedback_ctrs.attr,
&reference_perf.attr,
&wraparound_time.attr,
NULL
};
static struct kobj_type cppc_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.default_attrs = cppc_attrs,
};
static int check_pcc_chan(bool chk_err_bit)
{
int ret = -EIO, status = 0;
struct acpi_pcct_shared_memory __iomem *generic_comm_base = pcc_data.pcc_comm_addr;
ktime_t next_deadline = ktime_add(ktime_get(), pcc_data.deadline);
if (!pcc_data.platform_owns_pcc)
return 0;
/* Retry in case the remote processor was too slow to catch up. */ /* Retry in case the remote processor was too slow to catch up. */
while (!ktime_after(ktime_get(), next_deadline)) { while (!ktime_after(ktime_get(), next_deadline)) {
@ -91,8 +199,11 @@ static int check_pcc_chan(void)
* platform and should have set the command completion bit when * platform and should have set the command completion bit when
* PCC can be used by OSPM * PCC can be used by OSPM
*/ */
if (readw_relaxed(&generic_comm_base->status) & PCC_CMD_COMPLETE) { status = readw_relaxed(&generic_comm_base->status);
if (status & PCC_CMD_COMPLETE_MASK) {
ret = 0; ret = 0;
if (chk_err_bit && (status & PCC_ERROR_MASK))
ret = -EIO;
break; break;
} }
/* /*
@ -102,14 +213,23 @@ static int check_pcc_chan(void)
udelay(3); udelay(3);
} }
if (likely(!ret))
pcc_data.platform_owns_pcc = false;
else
pr_err("PCC check channel failed. Status=%x\n", status);
return ret; return ret;
} }
/*
* This function transfers the ownership of the PCC to the platform
* So it must be called while holding write_lock(pcc_lock)
*/
static int send_pcc_cmd(u16 cmd) static int send_pcc_cmd(u16 cmd)
{ {
int ret = -EIO; int ret = -EIO, i;
struct acpi_pcct_shared_memory *generic_comm_base = struct acpi_pcct_shared_memory *generic_comm_base =
(struct acpi_pcct_shared_memory *) pcc_comm_addr; (struct acpi_pcct_shared_memory *) pcc_data.pcc_comm_addr;
static ktime_t last_cmd_cmpl_time, last_mpar_reset; static ktime_t last_cmd_cmpl_time, last_mpar_reset;
static int mpar_count; static int mpar_count;
unsigned int time_delta; unsigned int time_delta;
@ -119,20 +239,29 @@ static int send_pcc_cmd(u16 cmd)
* the channel before writing to PCC space * the channel before writing to PCC space
*/ */
if (cmd == CMD_READ) { if (cmd == CMD_READ) {
ret = check_pcc_chan(); /*
* If there are pending cpc_writes, then we stole the channel
* before write completion, so first send a WRITE command to
* platform
*/
if (pcc_data.pending_pcc_write_cmd)
send_pcc_cmd(CMD_WRITE);
ret = check_pcc_chan(false);
if (ret) if (ret)
return ret; goto end;
} } else /* CMD_WRITE */
pcc_data.pending_pcc_write_cmd = FALSE;
/* /*
* Handle the Minimum Request Turnaround Time(MRTT) * Handle the Minimum Request Turnaround Time(MRTT)
* "The minimum amount of time that OSPM must wait after the completion * "The minimum amount of time that OSPM must wait after the completion
* of a command before issuing the next command, in microseconds" * of a command before issuing the next command, in microseconds"
*/ */
if (pcc_mrtt) { if (pcc_data.pcc_mrtt) {
time_delta = ktime_us_delta(ktime_get(), last_cmd_cmpl_time); time_delta = ktime_us_delta(ktime_get(), last_cmd_cmpl_time);
if (pcc_mrtt > time_delta) if (pcc_data.pcc_mrtt > time_delta)
udelay(pcc_mrtt - time_delta); udelay(pcc_data.pcc_mrtt - time_delta);
} }
/* /*
@ -146,15 +275,16 @@ static int send_pcc_cmd(u16 cmd)
* not send the request to the platform after hitting the MPAR limit in * not send the request to the platform after hitting the MPAR limit in
* any 60s window * any 60s window
*/ */
if (pcc_mpar) { if (pcc_data.pcc_mpar) {
if (mpar_count == 0) { if (mpar_count == 0) {
time_delta = ktime_ms_delta(ktime_get(), last_mpar_reset); time_delta = ktime_ms_delta(ktime_get(), last_mpar_reset);
if (time_delta < 60 * MSEC_PER_SEC) { if (time_delta < 60 * MSEC_PER_SEC) {
pr_debug("PCC cmd not sent due to MPAR limit"); pr_debug("PCC cmd not sent due to MPAR limit");
return -EIO; ret = -EIO;
goto end;
} }
last_mpar_reset = ktime_get(); last_mpar_reset = ktime_get();
mpar_count = pcc_mpar; mpar_count = pcc_data.pcc_mpar;
} }
mpar_count--; mpar_count--;
} }
@ -165,33 +295,43 @@ static int send_pcc_cmd(u16 cmd)
/* Flip CMD COMPLETE bit */ /* Flip CMD COMPLETE bit */
writew_relaxed(0, &generic_comm_base->status); writew_relaxed(0, &generic_comm_base->status);
pcc_data.platform_owns_pcc = true;
/* Ring doorbell */ /* Ring doorbell */
ret = mbox_send_message(pcc_channel, &cmd); ret = mbox_send_message(pcc_data.pcc_channel, &cmd);
if (ret < 0) { if (ret < 0) {
pr_err("Err sending PCC mbox message. cmd:%d, ret:%d\n", pr_err("Err sending PCC mbox message. cmd:%d, ret:%d\n",
cmd, ret); cmd, ret);
return ret; goto end;
} }
/* /* wait for completion and check for PCC errro bit */
* For READs we need to ensure the cmd completed to ensure ret = check_pcc_chan(true);
* the ensuing read()s can proceed. For WRITEs we dont care
* because the actual write()s are done before coming here if (pcc_data.pcc_mrtt)
* and the next READ or WRITE will check if the channel last_cmd_cmpl_time = ktime_get();
* is busy/free at the entry of this call.
* if (pcc_data.pcc_channel->mbox->txdone_irq)
* If Minimum Request Turnaround Time is non-zero, we need mbox_chan_txdone(pcc_data.pcc_channel, ret);
* to record the completion time of both READ and WRITE else
* command for proper handling of MRTT, so we need to check mbox_client_txdone(pcc_data.pcc_channel, ret);
* for pcc_mrtt in addition to CMD_READ
*/ end:
if (cmd == CMD_READ || pcc_mrtt) { if (cmd == CMD_WRITE) {
ret = check_pcc_chan(); if (unlikely(ret)) {
if (pcc_mrtt) for_each_possible_cpu(i) {
last_cmd_cmpl_time = ktime_get(); struct cpc_desc *desc = per_cpu(cpc_desc_ptr, i);
if (!desc)
continue;
if (desc->write_cmd_id == pcc_data.pcc_write_cnt)
desc->write_cmd_status = ret;
}
}
pcc_data.pcc_write_cnt++;
wake_up_all(&pcc_data.pcc_write_wait_q);
} }
mbox_client_txdone(pcc_channel, ret);
return ret; return ret;
} }
@ -272,13 +412,13 @@ end:
* *
* Return: 0 for success or negative value for err. * Return: 0 for success or negative value for err.
*/ */
int acpi_get_psd_map(struct cpudata **all_cpu_data) int acpi_get_psd_map(struct cppc_cpudata **all_cpu_data)
{ {
int count_target; int count_target;
int retval = 0; int retval = 0;
unsigned int i, j; unsigned int i, j;
cpumask_var_t covered_cpus; cpumask_var_t covered_cpus;
struct cpudata *pr, *match_pr; struct cppc_cpudata *pr, *match_pr;
struct acpi_psd_package *pdomain; struct acpi_psd_package *pdomain;
struct acpi_psd_package *match_pdomain; struct acpi_psd_package *match_pdomain;
struct cpc_desc *cpc_ptr, *match_cpc_ptr; struct cpc_desc *cpc_ptr, *match_cpc_ptr;
@ -394,14 +534,13 @@ EXPORT_SYMBOL_GPL(acpi_get_psd_map);
static int register_pcc_channel(int pcc_subspace_idx) static int register_pcc_channel(int pcc_subspace_idx)
{ {
struct acpi_pcct_hw_reduced *cppc_ss; struct acpi_pcct_hw_reduced *cppc_ss;
unsigned int len;
u64 usecs_lat; u64 usecs_lat;
if (pcc_subspace_idx >= 0) { if (pcc_subspace_idx >= 0) {
pcc_channel = pcc_mbox_request_channel(&cppc_mbox_cl, pcc_data.pcc_channel = pcc_mbox_request_channel(&cppc_mbox_cl,
pcc_subspace_idx); pcc_subspace_idx);
if (IS_ERR(pcc_channel)) { if (IS_ERR(pcc_data.pcc_channel)) {
pr_err("Failed to find PCC communication channel\n"); pr_err("Failed to find PCC communication channel\n");
return -ENODEV; return -ENODEV;
} }
@ -412,43 +551,50 @@ static int register_pcc_channel(int pcc_subspace_idx)
* PCC channels) and stored pointers to the * PCC channels) and stored pointers to the
* subspace communication region in con_priv. * subspace communication region in con_priv.
*/ */
cppc_ss = pcc_channel->con_priv; cppc_ss = (pcc_data.pcc_channel)->con_priv;
if (!cppc_ss) { if (!cppc_ss) {
pr_err("No PCC subspace found for CPPC\n"); pr_err("No PCC subspace found for CPPC\n");
return -ENODEV; return -ENODEV;
} }
/*
* This is the shared communication region
* for the OS and Platform to communicate over.
*/
comm_base_addr = cppc_ss->base_address;
len = cppc_ss->length;
/* /*
* cppc_ss->latency is just a Nominal value. In reality * cppc_ss->latency is just a Nominal value. In reality
* the remote processor could be much slower to reply. * the remote processor could be much slower to reply.
* So add an arbitrary amount of wait on top of Nominal. * So add an arbitrary amount of wait on top of Nominal.
*/ */
usecs_lat = NUM_RETRIES * cppc_ss->latency; usecs_lat = NUM_RETRIES * cppc_ss->latency;
deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC); pcc_data.deadline = ns_to_ktime(usecs_lat * NSEC_PER_USEC);
pcc_mrtt = cppc_ss->min_turnaround_time; pcc_data.pcc_mrtt = cppc_ss->min_turnaround_time;
pcc_mpar = cppc_ss->max_access_rate; pcc_data.pcc_mpar = cppc_ss->max_access_rate;
pcc_data.pcc_nominal = cppc_ss->latency;
pcc_comm_addr = acpi_os_ioremap(comm_base_addr, len); pcc_data.pcc_comm_addr = acpi_os_ioremap(cppc_ss->base_address, cppc_ss->length);
if (!pcc_comm_addr) { if (!pcc_data.pcc_comm_addr) {
pr_err("Failed to ioremap PCC comm region mem\n"); pr_err("Failed to ioremap PCC comm region mem\n");
return -ENOMEM; return -ENOMEM;
} }
/* Set flag so that we dont come here for each CPU. */ /* Set flag so that we dont come here for each CPU. */
pcc_channel_acquired = true; pcc_data.pcc_channel_acquired = true;
} }
return 0; return 0;
} }
/**
* cpc_ffh_supported() - check if FFH reading supported
*
* Check if the architecture has support for functional fixed hardware
* read/write capability.
*
* Return: true for supported, false for not supported
*/
bool __weak cpc_ffh_supported(void)
{
return false;
}
/* /*
* An example CPC table looks like the following. * An example CPC table looks like the following.
* *
@ -507,6 +653,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
union acpi_object *out_obj, *cpc_obj; union acpi_object *out_obj, *cpc_obj;
struct cpc_desc *cpc_ptr; struct cpc_desc *cpc_ptr;
struct cpc_reg *gas_t; struct cpc_reg *gas_t;
struct device *cpu_dev;
acpi_handle handle = pr->handle; acpi_handle handle = pr->handle;
unsigned int num_ent, i, cpc_rev; unsigned int num_ent, i, cpc_rev;
acpi_status status; acpi_status status;
@ -545,6 +692,8 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
goto out_free; goto out_free;
} }
cpc_ptr->num_entries = num_ent;
/* Second entry should be revision. */ /* Second entry should be revision. */
cpc_obj = &out_obj->package.elements[1]; cpc_obj = &out_obj->package.elements[1];
if (cpc_obj->type == ACPI_TYPE_INTEGER) { if (cpc_obj->type == ACPI_TYPE_INTEGER) {
@ -579,16 +728,27 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
* so extract it only once. * so extract it only once.
*/ */
if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { if (gas_t->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) {
if (pcc_subspace_idx < 0) if (pcc_data.pcc_subspace_idx < 0)
pcc_subspace_idx = gas_t->access_width; pcc_data.pcc_subspace_idx = gas_t->access_width;
else if (pcc_subspace_idx != gas_t->access_width) { else if (pcc_data.pcc_subspace_idx != gas_t->access_width) {
pr_debug("Mismatched PCC ids.\n"); pr_debug("Mismatched PCC ids.\n");
goto out_free; goto out_free;
} }
} else if (gas_t->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) { } else if (gas_t->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
/* Support only PCC and SYS MEM type regs */ if (gas_t->address) {
pr_debug("Unsupported register type: %d\n", gas_t->space_id); void __iomem *addr;
goto out_free;
addr = ioremap(gas_t->address, gas_t->bit_width/8);
if (!addr)
goto out_free;
cpc_ptr->cpc_regs[i-2].sys_mem_vaddr = addr;
}
} else {
if (gas_t->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE || !cpc_ffh_supported()) {
/* Support only PCC ,SYS MEM and FFH type regs */
pr_debug("Unsupported register type: %d\n", gas_t->space_id);
goto out_free;
}
} }
cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER; cpc_ptr->cpc_regs[i-2].type = ACPI_TYPE_BUFFER;
@ -607,10 +767,13 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
goto out_free; goto out_free;
/* Register PCC channel once for all CPUs. */ /* Register PCC channel once for all CPUs. */
if (!pcc_channel_acquired) { if (!pcc_data.pcc_channel_acquired) {
ret = register_pcc_channel(pcc_subspace_idx); ret = register_pcc_channel(pcc_data.pcc_subspace_idx);
if (ret) if (ret)
goto out_free; goto out_free;
init_rwsem(&pcc_data.pcc_lock);
init_waitqueue_head(&pcc_data.pcc_write_wait_q);
} }
/* Plug PSD data into this CPUs CPC descriptor. */ /* Plug PSD data into this CPUs CPC descriptor. */
@ -619,10 +782,27 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
/* Everything looks okay */ /* Everything looks okay */
pr_debug("Parsed CPC struct for CPU: %d\n", pr->id); pr_debug("Parsed CPC struct for CPU: %d\n", pr->id);
/* Add per logical CPU nodes for reading its feedback counters. */
cpu_dev = get_cpu_device(pr->id);
if (!cpu_dev)
goto out_free;
ret = kobject_init_and_add(&cpc_ptr->kobj, &cppc_ktype, &cpu_dev->kobj,
"acpi_cppc");
if (ret)
goto out_free;
kfree(output.pointer); kfree(output.pointer);
return 0; return 0;
out_free: out_free:
/* Free all the mapped sys mem areas for this CPU */
for (i = 2; i < cpc_ptr->num_entries; i++) {
void __iomem *addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
if (addr)
iounmap(addr);
}
kfree(cpc_ptr); kfree(cpc_ptr);
out_buf_free: out_buf_free:
@ -640,26 +820,82 @@ EXPORT_SYMBOL_GPL(acpi_cppc_processor_probe);
void acpi_cppc_processor_exit(struct acpi_processor *pr) void acpi_cppc_processor_exit(struct acpi_processor *pr)
{ {
struct cpc_desc *cpc_ptr; struct cpc_desc *cpc_ptr;
unsigned int i;
void __iomem *addr;
cpc_ptr = per_cpu(cpc_desc_ptr, pr->id); cpc_ptr = per_cpu(cpc_desc_ptr, pr->id);
/* Free all the mapped sys mem areas for this CPU */
for (i = 2; i < cpc_ptr->num_entries; i++) {
addr = cpc_ptr->cpc_regs[i-2].sys_mem_vaddr;
if (addr)
iounmap(addr);
}
kobject_put(&cpc_ptr->kobj);
kfree(cpc_ptr); kfree(cpc_ptr);
} }
EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit); EXPORT_SYMBOL_GPL(acpi_cppc_processor_exit);
/**
* cpc_read_ffh() - Read FFH register
* @cpunum: cpu number to read
* @reg: cppc register information
* @val: place holder for return value
*
* Read bit_width bits from a specified address and bit_offset
*
* Return: 0 for success and error code
*/
int __weak cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val)
{
return -ENOTSUPP;
}
/**
* cpc_write_ffh() - Write FFH register
* @cpunum: cpu number to write
* @reg: cppc register information
* @val: value to write
*
* Write value of bit_width bits to a specified address and bit_offset
*
* Return: 0 for success and error code
*/
int __weak cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val)
{
return -ENOTSUPP;
}
/* /*
* Since cpc_read and cpc_write are called while holding pcc_lock, it should be * Since cpc_read and cpc_write are called while holding pcc_lock, it should be
* as fast as possible. We have already mapped the PCC subspace during init, so * as fast as possible. We have already mapped the PCC subspace during init, so
* we can directly write to it. * we can directly write to it.
*/ */
static int cpc_read(struct cpc_reg *reg, u64 *val) static int cpc_read(int cpu, struct cpc_register_resource *reg_res, u64 *val)
{ {
int ret_val = 0; int ret_val = 0;
void __iomem *vaddr = 0;
struct cpc_reg *reg = &reg_res->cpc_entry.reg;
if (reg_res->type == ACPI_TYPE_INTEGER) {
*val = reg_res->cpc_entry.int_value;
return ret_val;
}
*val = 0; *val = 0;
if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM)
void __iomem *vaddr = GET_PCC_VADDR(reg->address); vaddr = GET_PCC_VADDR(reg->address);
else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
vaddr = reg_res->sys_mem_vaddr;
else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
return cpc_read_ffh(cpu, reg, val);
else
return acpi_os_read_memory((acpi_physical_address)reg->address,
val, reg->bit_width);
switch (reg->bit_width) { switch (reg->bit_width) {
case 8: case 8:
*val = readb_relaxed(vaddr); *val = readb_relaxed(vaddr);
break; break;
@ -674,23 +910,30 @@ static int cpc_read(struct cpc_reg *reg, u64 *val)
break; break;
default: default:
pr_debug("Error: Cannot read %u bit width from PCC\n", pr_debug("Error: Cannot read %u bit width from PCC\n",
reg->bit_width); reg->bit_width);
ret_val = -EFAULT; ret_val = -EFAULT;
} }
} else
ret_val = acpi_os_read_memory((acpi_physical_address)reg->address,
val, reg->bit_width);
return ret_val; return ret_val;
} }
static int cpc_write(struct cpc_reg *reg, u64 val) static int cpc_write(int cpu, struct cpc_register_resource *reg_res, u64 val)
{ {
int ret_val = 0; int ret_val = 0;
void __iomem *vaddr = 0;
struct cpc_reg *reg = &reg_res->cpc_entry.reg;
if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { if (reg->space_id == ACPI_ADR_SPACE_PLATFORM_COMM)
void __iomem *vaddr = GET_PCC_VADDR(reg->address); vaddr = GET_PCC_VADDR(reg->address);
else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
vaddr = reg_res->sys_mem_vaddr;
else if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE)
return cpc_write_ffh(cpu, reg, val);
else
return acpi_os_write_memory((acpi_physical_address)reg->address,
val, reg->bit_width);
switch (reg->bit_width) { switch (reg->bit_width) {
case 8: case 8:
writeb_relaxed(val, vaddr); writeb_relaxed(val, vaddr);
break; break;
@ -705,13 +948,11 @@ static int cpc_write(struct cpc_reg *reg, u64 val)
break; break;
default: default:
pr_debug("Error: Cannot write %u bit width to PCC\n", pr_debug("Error: Cannot write %u bit width to PCC\n",
reg->bit_width); reg->bit_width);
ret_val = -EFAULT; ret_val = -EFAULT;
break; break;
} }
} else
ret_val = acpi_os_write_memory((acpi_physical_address)reg->address,
val, reg->bit_width);
return ret_val; return ret_val;
} }
@ -727,8 +968,8 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
struct cpc_register_resource *highest_reg, *lowest_reg, *ref_perf, struct cpc_register_resource *highest_reg, *lowest_reg, *ref_perf,
*nom_perf; *nom_perf;
u64 high, low, ref, nom; u64 high, low, nom;
int ret = 0; int ret = 0, regs_in_pcc = 0;
if (!cpc_desc) { if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpunum); pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
@ -740,13 +981,11 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
ref_perf = &cpc_desc->cpc_regs[REFERENCE_PERF]; ref_perf = &cpc_desc->cpc_regs[REFERENCE_PERF];
nom_perf = &cpc_desc->cpc_regs[NOMINAL_PERF]; nom_perf = &cpc_desc->cpc_regs[NOMINAL_PERF];
spin_lock(&pcc_lock);
/* Are any of the regs PCC ?*/ /* Are any of the regs PCC ?*/
if ((highest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || if (CPC_IN_PCC(highest_reg) || CPC_IN_PCC(lowest_reg) ||
(lowest_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || CPC_IN_PCC(ref_perf) || CPC_IN_PCC(nom_perf)) {
(ref_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || regs_in_pcc = 1;
(nom_perf->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { down_write(&pcc_data.pcc_lock);
/* Ring doorbell once to update PCC subspace */ /* Ring doorbell once to update PCC subspace */
if (send_pcc_cmd(CMD_READ) < 0) { if (send_pcc_cmd(CMD_READ) < 0) {
ret = -EIO; ret = -EIO;
@ -754,26 +993,21 @@ int cppc_get_perf_caps(int cpunum, struct cppc_perf_caps *perf_caps)
} }
} }
cpc_read(&highest_reg->cpc_entry.reg, &high); cpc_read(cpunum, highest_reg, &high);
perf_caps->highest_perf = high; perf_caps->highest_perf = high;
cpc_read(&lowest_reg->cpc_entry.reg, &low); cpc_read(cpunum, lowest_reg, &low);
perf_caps->lowest_perf = low; perf_caps->lowest_perf = low;
cpc_read(&ref_perf->cpc_entry.reg, &ref); cpc_read(cpunum, nom_perf, &nom);
perf_caps->reference_perf = ref;
cpc_read(&nom_perf->cpc_entry.reg, &nom);
perf_caps->nominal_perf = nom; perf_caps->nominal_perf = nom;
if (!ref)
perf_caps->reference_perf = perf_caps->nominal_perf;
if (!high || !low || !nom) if (!high || !low || !nom)
ret = -EFAULT; ret = -EFAULT;
out_err: out_err:
spin_unlock(&pcc_lock); if (regs_in_pcc)
up_write(&pcc_data.pcc_lock);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(cppc_get_perf_caps); EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
@ -788,9 +1022,10 @@ EXPORT_SYMBOL_GPL(cppc_get_perf_caps);
int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs) int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
{ {
struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum); struct cpc_desc *cpc_desc = per_cpu(cpc_desc_ptr, cpunum);
struct cpc_register_resource *delivered_reg, *reference_reg; struct cpc_register_resource *delivered_reg, *reference_reg,
u64 delivered, reference; *ref_perf_reg, *ctr_wrap_reg;
int ret = 0; u64 delivered, reference, ref_perf, ctr_wrap_time;
int ret = 0, regs_in_pcc = 0;
if (!cpc_desc) { if (!cpc_desc) {
pr_debug("No CPC descriptor for CPU:%d\n", cpunum); pr_debug("No CPC descriptor for CPU:%d\n", cpunum);
@ -799,12 +1034,21 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR]; delivered_reg = &cpc_desc->cpc_regs[DELIVERED_CTR];
reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR]; reference_reg = &cpc_desc->cpc_regs[REFERENCE_CTR];
ref_perf_reg = &cpc_desc->cpc_regs[REFERENCE_PERF];
ctr_wrap_reg = &cpc_desc->cpc_regs[CTR_WRAP_TIME];
spin_lock(&pcc_lock); /*
* If refernce perf register is not supported then we should
* use the nominal perf value
*/
if (!CPC_SUPPORTED(ref_perf_reg))
ref_perf_reg = &cpc_desc->cpc_regs[NOMINAL_PERF];
/* Are any of the regs PCC ?*/ /* Are any of the regs PCC ?*/
if ((delivered_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) || if (CPC_IN_PCC(delivered_reg) || CPC_IN_PCC(reference_reg) ||
(reference_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM)) { CPC_IN_PCC(ctr_wrap_reg) || CPC_IN_PCC(ref_perf_reg)) {
down_write(&pcc_data.pcc_lock);
regs_in_pcc = 1;
/* Ring doorbell once to update PCC subspace */ /* Ring doorbell once to update PCC subspace */
if (send_pcc_cmd(CMD_READ) < 0) { if (send_pcc_cmd(CMD_READ) < 0) {
ret = -EIO; ret = -EIO;
@ -812,25 +1056,31 @@ int cppc_get_perf_ctrs(int cpunum, struct cppc_perf_fb_ctrs *perf_fb_ctrs)
} }
} }
cpc_read(&delivered_reg->cpc_entry.reg, &delivered); cpc_read(cpunum, delivered_reg, &delivered);
cpc_read(&reference_reg->cpc_entry.reg, &reference); cpc_read(cpunum, reference_reg, &reference);
cpc_read(cpunum, ref_perf_reg, &ref_perf);
if (!delivered || !reference) { /*
* Per spec, if ctr_wrap_time optional register is unsupported, then the
* performance counters are assumed to never wrap during the lifetime of
* platform
*/
ctr_wrap_time = (u64)(~((u64)0));
if (CPC_SUPPORTED(ctr_wrap_reg))
cpc_read(cpunum, ctr_wrap_reg, &ctr_wrap_time);
if (!delivered || !reference || !ref_perf) {
ret = -EFAULT; ret = -EFAULT;
goto out_err; goto out_err;
} }
perf_fb_ctrs->delivered = delivered; perf_fb_ctrs->delivered = delivered;
perf_fb_ctrs->reference = reference; perf_fb_ctrs->reference = reference;
perf_fb_ctrs->reference_perf = ref_perf;
perf_fb_ctrs->delivered -= perf_fb_ctrs->prev_delivered; perf_fb_ctrs->ctr_wrap_time = ctr_wrap_time;
perf_fb_ctrs->reference -= perf_fb_ctrs->prev_reference;
perf_fb_ctrs->prev_delivered = delivered;
perf_fb_ctrs->prev_reference = reference;
out_err: out_err:
spin_unlock(&pcc_lock); if (regs_in_pcc)
up_write(&pcc_data.pcc_lock);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs); EXPORT_SYMBOL_GPL(cppc_get_perf_ctrs);
@ -855,30 +1105,142 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF]; desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
spin_lock(&pcc_lock); /*
* This is Phase-I where we want to write to CPC registers
/* If this is PCC reg, check if channel is free before writing */ * -> We want all CPUs to be able to execute this phase in parallel
if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { *
ret = check_pcc_chan(); * Since read_lock can be acquired by multiple CPUs simultaneously we
if (ret) * achieve that goal here
goto busy_channel; */
if (CPC_IN_PCC(desired_reg)) {
down_read(&pcc_data.pcc_lock); /* BEGIN Phase-I */
if (pcc_data.platform_owns_pcc) {
ret = check_pcc_chan(false);
if (ret) {
up_read(&pcc_data.pcc_lock);
return ret;
}
}
/*
* Update the pending_write to make sure a PCC CMD_READ will not
* arrive and steal the channel during the switch to write lock
*/
pcc_data.pending_pcc_write_cmd = true;
cpc_desc->write_cmd_id = pcc_data.pcc_write_cnt;
cpc_desc->write_cmd_status = 0;
} }
/* /*
* Skip writing MIN/MAX until Linux knows how to come up with * Skip writing MIN/MAX until Linux knows how to come up with
* useful values. * useful values.
*/ */
cpc_write(&desired_reg->cpc_entry.reg, perf_ctrls->desired_perf); cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
/* Is this a PCC reg ?*/ if (CPC_IN_PCC(desired_reg))
if (desired_reg->cpc_entry.reg.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { up_read(&pcc_data.pcc_lock); /* END Phase-I */
/* Ring doorbell so Remote can get our perf request. */ /*
if (send_pcc_cmd(CMD_WRITE) < 0) * This is Phase-II where we transfer the ownership of PCC to Platform
ret = -EIO; *
* Short Summary: Basically if we think of a group of cppc_set_perf
* requests that happened in short overlapping interval. The last CPU to
* come out of Phase-I will enter Phase-II and ring the doorbell.
*
* We have the following requirements for Phase-II:
* 1. We want to execute Phase-II only when there are no CPUs
* currently executing in Phase-I
* 2. Once we start Phase-II we want to avoid all other CPUs from
* entering Phase-I.
* 3. We want only one CPU among all those who went through Phase-I
* to run phase-II
*
* If write_trylock fails to get the lock and doesn't transfer the
* PCC ownership to the platform, then one of the following will be TRUE
* 1. There is at-least one CPU in Phase-I which will later execute
* write_trylock, so the CPUs in Phase-I will be responsible for
* executing the Phase-II.
* 2. Some other CPU has beaten this CPU to successfully execute the
* write_trylock and has already acquired the write_lock. We know for a
* fact it(other CPU acquiring the write_lock) couldn't have happened
* before this CPU's Phase-I as we held the read_lock.
* 3. Some other CPU executing pcc CMD_READ has stolen the
* down_write, in which case, send_pcc_cmd will check for pending
* CMD_WRITE commands by checking the pending_pcc_write_cmd.
* So this CPU can be certain that its request will be delivered
* So in all cases, this CPU knows that its request will be delivered
* by another CPU and can return
*
* After getting the down_write we still need to check for
* pending_pcc_write_cmd to take care of the following scenario
* The thread running this code could be scheduled out between
* Phase-I and Phase-II. Before it is scheduled back on, another CPU
* could have delivered the request to Platform by triggering the
* doorbell and transferred the ownership of PCC to platform. So this
* avoids triggering an unnecessary doorbell and more importantly before
* triggering the doorbell it makes sure that the PCC channel ownership
* is still with OSPM.
* pending_pcc_write_cmd can also be cleared by a different CPU, if
* there was a pcc CMD_READ waiting on down_write and it steals the lock
* before the pcc CMD_WRITE is completed. pcc_send_cmd checks for this
* case during a CMD_READ and if there are pending writes it delivers
* the write command before servicing the read command
*/
if (CPC_IN_PCC(desired_reg)) {
if (down_write_trylock(&pcc_data.pcc_lock)) { /* BEGIN Phase-II */
/* Update only if there are pending write commands */
if (pcc_data.pending_pcc_write_cmd)
send_pcc_cmd(CMD_WRITE);
up_write(&pcc_data.pcc_lock); /* END Phase-II */
} else
/* Wait until pcc_write_cnt is updated by send_pcc_cmd */
wait_event(pcc_data.pcc_write_wait_q,
cpc_desc->write_cmd_id != pcc_data.pcc_write_cnt);
/* send_pcc_cmd updates the status in case of failure */
ret = cpc_desc->write_cmd_status;
} }
busy_channel:
spin_unlock(&pcc_lock);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(cppc_set_perf); EXPORT_SYMBOL_GPL(cppc_set_perf);
/**
* cppc_get_transition_latency - returns frequency transition latency in ns
*
* ACPI CPPC does not explicitly specifiy how a platform can specify the
* transition latency for perfromance change requests. The closest we have
* is the timing information from the PCCT tables which provides the info
* on the number and frequency of PCC commands the platform can handle.
*/
unsigned int cppc_get_transition_latency(int cpu_num)
{
/*
* Expected transition latency is based on the PCCT timing values
* Below are definition from ACPI spec:
* pcc_nominal- Expected latency to process a command, in microseconds
* pcc_mpar - The maximum number of periodic requests that the subspace
* channel can support, reported in commands per minute. 0
* indicates no limitation.
* pcc_mrtt - The minimum amount of time that OSPM must wait after the
* completion of a command before issuing the next command,
* in microseconds.
*/
unsigned int latency_ns = 0;
struct cpc_desc *cpc_desc;
struct cpc_register_resource *desired_reg;
cpc_desc = per_cpu(cpc_desc_ptr, cpu_num);
if (!cpc_desc)
return CPUFREQ_ETERNAL;
desired_reg = &cpc_desc->cpc_regs[DESIRED_PERF];
if (!CPC_IN_PCC(desired_reg))
return CPUFREQ_ETERNAL;
if (pcc_data.pcc_mpar)
latency_ns = 60 * (1000 * 1000 * 1000 / pcc_data.pcc_mpar);
latency_ns = max(latency_ns, pcc_data.pcc_nominal * 1000);
latency_ns = max(latency_ns, pcc_data.pcc_mrtt * 1000);
return latency_ns;
}
EXPORT_SYMBOL_GPL(cppc_get_transition_latency);

View File

@ -104,10 +104,12 @@ enum ec_command {
#define ACPI_EC_MAX_QUERIES 16 /* Maximum number of parallel queries */ #define ACPI_EC_MAX_QUERIES 16 /* Maximum number of parallel queries */
enum { enum {
EC_FLAGS_QUERY_ENABLED, /* Query is enabled */
EC_FLAGS_QUERY_PENDING, /* Query is pending */ EC_FLAGS_QUERY_PENDING, /* Query is pending */
EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */ EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */
EC_FLAGS_GPE_HANDLER_INSTALLED, /* GPE handler installed */ EC_FLAGS_GPE_HANDLER_INSTALLED, /* GPE handler installed */
EC_FLAGS_EC_HANDLER_INSTALLED, /* OpReg 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_STARTED, /* Driver is started */
EC_FLAGS_STOPPED, /* Driver is stopped */ EC_FLAGS_STOPPED, /* Driver is stopped */
EC_FLAGS_COMMAND_STORM, /* GPE storms occurred to the 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_param(ec_storm_threshold, uint, 0644);
MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm"); 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 acpi_ec_query_handler {
struct list_head node; struct list_head node;
acpi_ec_query_func func; 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; struct acpi_ec *boot_ec, *first_ec;
EXPORT_SYMBOL(first_ec); EXPORT_SYMBOL(first_ec);
static bool boot_ec_is_ecdt = false;
static struct workqueue_struct *ec_query_wq; static struct workqueue_struct *ec_query_wq;
static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ 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); !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) static bool acpi_ec_flushed(struct acpi_ec *ec)
{ {
return ec->reference_count == 1; 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) 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", ec_dbg_evt("Command(%s) submitted/blocked",
acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY));
ec->nr_pending_queries++; 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) static bool acpi_ec_guard_event(struct acpi_ec *ec)
{ {
bool guarded = true; bool guarded = true;
@ -832,27 +944,6 @@ acpi_handle ec_get_handle(void)
} }
EXPORT_SYMBOL(ec_get_handle); 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) static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
{ {
unsigned long flags; unsigned long flags;
@ -896,7 +987,8 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending)
if (!suspending) { if (!suspending) {
acpi_ec_complete_request(ec); acpi_ec_complete_request(ec);
ec_dbg_ref(ec, "Decrease driver"); 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_STARTED, &ec->flags);
clear_bit(EC_FLAGS_STOPPED, &ec->flags); clear_bit(EC_FLAGS_STOPPED, &ec->flags);
ec_log_drv("EC stopped"); ec_log_drv("EC stopped");
@ -918,20 +1010,6 @@ void acpi_ec_block_transactions(void)
} }
void acpi_ec_unblock_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 * 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 static acpi_status
ec_parse_io_ports(struct acpi_resource *resource, void *context); 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); struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL);
if (!ec) if (!ec)
return NULL; return NULL;
ec->flags = 1 << EC_FLAGS_QUERY_PENDING;
mutex_init(&ec->mutex); mutex_init(&ec->mutex);
init_waitqueue_head(&ec->wait); init_waitqueue_head(&ec->wait);
INIT_LIST_HEAD(&ec->list); 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; 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; 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); 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)) { if (!test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) {
status = acpi_install_gpe_raw_handler(NULL, ec->gpe, status = acpi_install_gpe_raw_handler(NULL, ec->gpe,
ACPI_GPE_EDGE_TRIGGERED, 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) && if (test_bit(EC_FLAGS_STARTED, &ec->flags) &&
ec->reference_count >= 1) ec->reference_count >= 1)
acpi_ec_enable_gpe(ec, true); 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"); pr_err("failed to remove gpe handler\n");
clear_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags); 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 */ ret = ec_install_handlers(ec, handle_events);
if (boot_ec) { if (ret)
ec = boot_ec; return ret;
boot_ec = NULL;
ec_remove_handlers(ec); /* First EC capable of handling transactions */
if (first_ec == ec) if (!first_ec) {
first_ec = NULL; first_ec = ec;
} else { acpi_handle_info(first_ec->handle, "Used as first EC\n");
ec = make_acpi_ec();
} }
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) static int acpi_ec_add(struct acpi_device *device)
@ -1395,16 +1580,21 @@ static int acpi_ec_add(struct acpi_device *device)
return -ENOMEM; return -ENOMEM;
if (ec_parse_device(device->handle, 0, ec, NULL) != if (ec_parse_device(device->handle, 0, ec, NULL) !=
AE_CTRL_TERMINATE) { AE_CTRL_TERMINATE) {
kfree(ec); ret = -EINVAL;
return -EINVAL; goto err_alloc;
} }
/* Find and register all query methods */ if (acpi_is_boot_ec(ec)) {
acpi_walk_namespace(ACPI_TYPE_METHOD, ec->handle, 1, boot_ec_is_ecdt = false;
acpi_ec_register_query_methods, NULL, ec, NULL); 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; device->driver_data = ec;
ret = !!request_region(ec->data_addr, 1, "EC data"); 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"); ret = !!request_region(ec->command_addr, 1, "EC cmd");
WARN(!ret, "Could not request EC cmd io port 0x%lx", ec->command_addr); 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 */ /* Reprobe devices depending on the EC */
acpi_walk_dep_device_list(ec->handle); acpi_walk_dep_device_list(ec->handle);
acpi_handle_debug(ec->handle, "enumerated.\n");
return 0;
/* EC is fully operational, allow queries */ err_query:
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); if (ec != boot_ec)
acpi_ec_remove_query_handlers(ec, true, 0);
/* Clear stale _Q events if hardware might require that */ err_alloc:
if (EC_FLAGS_CLEAR_ON_RESUME) if (ec != boot_ec)
acpi_ec_clear(ec); acpi_ec_free(ec);
return ret; return ret;
} }
@ -1437,14 +1624,13 @@ static int acpi_ec_remove(struct acpi_device *device)
return -EINVAL; return -EINVAL;
ec = acpi_driver_data(device); 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->data_addr, 1);
release_region(ec->command_addr, 1); release_region(ec->command_addr, 1);
device->driver_data = NULL; device->driver_data = NULL;
if (ec == first_ec) if (ec != boot_ec) {
first_ec = NULL; ec_remove_handlers(ec);
kfree(ec); acpi_ec_free(ec);
}
return 0; return 0;
} }
@ -1486,9 +1672,8 @@ int __init acpi_ec_dsdt_probe(void)
if (!ec) if (!ec)
return -ENOMEM; return -ENOMEM;
/* /*
* Finding EC from DSDT if there is no ECDT EC available. When this * At this point, the namespace is initialized, so start to find
* function is invoked, ACPI tables have been fully loaded, we can * the namespace objects.
* walk namespace now.
*/ */
status = acpi_get_devices(ec_device_ids[0].id, status = acpi_get_devices(ec_device_ids[0].id,
ec_parse_device, ec, NULL); ec_parse_device, ec, NULL);
@ -1496,16 +1681,47 @@ int __init acpi_ec_dsdt_probe(void)
ret = -ENODEV; ret = -ENODEV;
goto error; 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: error:
if (ret) if (ret)
kfree(ec); acpi_ec_free(ec);
else
first_ec = boot_ec = ec;
return ret; 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 #if 0
/* /*
* Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not * 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; goto error;
} }
pr_info("EC description table is found, configuring boot EC\n");
if (EC_FLAGS_CORRECT_ECDT) { if (EC_FLAGS_CORRECT_ECDT) {
ec->command_addr = ecdt_ptr->data.address; ec->command_addr = ecdt_ptr->data.address;
ec->data_addr = ecdt_ptr->control.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->data_addr = ecdt_ptr->data.address;
} }
ec->gpe = ecdt_ptr->gpe; 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: error:
if (ret) if (ret)
kfree(ec); acpi_ec_free(ec);
else
first_ec = boot_ec = ec;
return ret; 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) static int param_set_event_clearing(const char *val, struct kernel_param *kp)
{ {
int result = 0; int result = 0;
@ -1664,6 +1953,7 @@ static struct acpi_driver acpi_ec_driver = {
.add = acpi_ec_add, .add = acpi_ec_add,
.remove = acpi_ec_remove, .remove = acpi_ec_remove,
}, },
.drv.pm = &acpi_ec_pm,
}; };
static inline int acpi_ec_query_init(void) static inline int acpi_ec_query_init(void)

View File

@ -116,7 +116,6 @@ bool acpi_device_is_present(struct acpi_device *adev);
bool acpi_device_is_battery(struct acpi_device *adev); bool acpi_device_is_battery(struct acpi_device *adev);
bool acpi_device_is_first_physical_node(struct acpi_device *adev, bool acpi_device_is_first_physical_node(struct acpi_device *adev,
const struct device *dev); const struct device *dev);
struct device *acpi_get_first_physical_node(struct acpi_device *adev);
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
Device Matching and Notification Device Matching and Notification
@ -174,6 +173,8 @@ struct acpi_ec {
struct work_struct work; struct work_struct work;
unsigned long timestamp; unsigned long timestamp;
unsigned long nr_pending_queries; unsigned long nr_pending_queries;
bool saved_busy_polling;
unsigned int saved_polling_guard;
}; };
extern struct acpi_ec *first_ec; extern struct acpi_ec *first_ec;
@ -185,9 +186,9 @@ typedef int (*acpi_ec_query_func) (void *data);
int acpi_ec_init(void); int acpi_ec_init(void);
int acpi_ec_ecdt_probe(void); int acpi_ec_ecdt_probe(void);
int acpi_ec_dsdt_probe(void); int acpi_ec_dsdt_probe(void);
int acpi_ec_ecdt_start(void);
void acpi_ec_block_transactions(void); void acpi_ec_block_transactions(void);
void acpi_ec_unblock_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, int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
acpi_handle handle, acpi_ec_query_func func, acpi_handle handle, acpi_ec_query_func func,
void *data); void *data);
@ -225,4 +226,14 @@ static inline void suspend_nvs_restore(void) {}
void acpi_init_properties(struct acpi_device *adev); void acpi_init_properties(struct acpi_device *adev);
void acpi_free_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_ */ #endif /* _ACPI_INTERNAL_H_ */

View File

@ -411,7 +411,15 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
int gsi; int gsi;
u8 pin; u8 pin;
int triggering = ACPI_LEVEL_SENSITIVE; int triggering = ACPI_LEVEL_SENSITIVE;
int polarity = ACPI_ACTIVE_LOW; /*
* On ARM systems with the GIC interrupt model, level interrupts
* are always polarity high by specification; PCI legacy
* IRQs lines are inverted before reaching the interrupt
* controller and must therefore be considered active high
* as default.
*/
int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ?
ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW;
char *link = NULL; char *link = NULL;
char link_desc[16]; char link_desc[16];
int rc; int rc;

View File

@ -245,8 +245,8 @@ static int __acpi_processor_start(struct acpi_device *device)
return 0; return 0;
result = acpi_cppc_processor_probe(pr); result = acpi_cppc_processor_probe(pr);
if (result) if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
return -ENODEV; dev_warn(&device->dev, "CPPC data invalid or not present\n");
if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
acpi_processor_power_init(pr); acpi_processor_power_init(pr);

View File

@ -2002,6 +2002,7 @@ int __init acpi_scan_init(void)
acpi_pnp_init(); acpi_pnp_init();
acpi_int340x_thermal_init(); acpi_int340x_thermal_init();
acpi_amba_init(); acpi_amba_init();
acpi_watchdog_init();
acpi_scan_add_handler(&generic_device_handler); acpi_scan_add_handler(&generic_device_handler);
@ -2044,6 +2045,7 @@ int __init acpi_scan_init(void)
} }
acpi_update_all_gpes(); acpi_update_all_gpes();
acpi_ec_ecdt_start();
acpi_scan_initialized = true; acpi_scan_initialized = true;

View File

@ -572,7 +572,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status); acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);
if (pwr_btn_status & ACPI_EVENT_FLAG_SET) { if (pwr_btn_status & ACPI_EVENT_FLAG_STATUS_SET) {
acpi_clear_event(ACPI_EVENT_POWER_BUTTON); acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
/* Flag for later */ /* Flag for later */
pwr_btn_event_pending = true; pwr_btn_event_pending = true;
@ -586,7 +586,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
*/ */
acpi_disable_all_gpes(); acpi_disable_all_gpes();
/* Allow EC transactions to happen. */ /* Allow EC transactions to happen. */
acpi_ec_unblock_transactions_early(); acpi_ec_unblock_transactions();
suspend_nvs_restore(); suspend_nvs_restore();
@ -784,7 +784,7 @@ static void acpi_hibernation_leave(void)
/* Restore the NVS memory area */ /* Restore the NVS memory area */
suspend_nvs_restore(); suspend_nvs_restore();
/* Allow EC transactions to happen. */ /* Allow EC transactions to happen. */
acpi_ec_unblock_transactions_early(); acpi_ec_unblock_transactions();
} }
static void acpi_pm_thaw(void) static void acpi_pm_thaw(void)

View File

@ -314,10 +314,14 @@ static struct kobject *tables_kobj;
static struct kobject *dynamic_tables_kobj; static struct kobject *dynamic_tables_kobj;
static struct kobject *hotplug_kobj; static struct kobject *hotplug_kobj;
#define ACPI_MAX_TABLE_INSTANCES 999
#define ACPI_INST_SIZE 4 /* including trailing 0 */
struct acpi_table_attr { struct acpi_table_attr {
struct bin_attribute attr; struct bin_attribute attr;
char name[8]; char name[ACPI_NAME_SIZE];
int instance; int instance;
char filename[ACPI_NAME_SIZE+ACPI_INST_SIZE];
struct list_head node; struct list_head node;
}; };
@ -329,14 +333,9 @@ static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj,
container_of(bin_attr, struct acpi_table_attr, attr); container_of(bin_attr, struct acpi_table_attr, attr);
struct acpi_table_header *table_header = NULL; struct acpi_table_header *table_header = NULL;
acpi_status status; acpi_status status;
char name[ACPI_NAME_SIZE];
if (strncmp(table_attr->name, "NULL", 4)) status = acpi_get_table(table_attr->name, table_attr->instance,
memcpy(name, table_attr->name, ACPI_NAME_SIZE); &table_header);
else
memcpy(name, "\0\0\0\0", 4);
status = acpi_get_table(name, table_attr->instance, &table_header);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -ENODEV; return -ENODEV;
@ -344,38 +343,45 @@ static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj,
table_header, table_header->length); table_header, table_header->length);
} }
static void acpi_table_attr_init(struct acpi_table_attr *table_attr, static int acpi_table_attr_init(struct kobject *tables_obj,
struct acpi_table_header *table_header) struct acpi_table_attr *table_attr,
struct acpi_table_header *table_header)
{ {
struct acpi_table_header *header = NULL; struct acpi_table_header *header = NULL;
struct acpi_table_attr *attr = NULL; struct acpi_table_attr *attr = NULL;
char instance_str[ACPI_INST_SIZE];
sysfs_attr_init(&table_attr->attr.attr); sysfs_attr_init(&table_attr->attr.attr);
if (table_header->signature[0] != '\0') ACPI_MOVE_NAME(table_attr->name, table_header->signature);
memcpy(table_attr->name, table_header->signature,
ACPI_NAME_SIZE);
else
memcpy(table_attr->name, "NULL", 4);
list_for_each_entry(attr, &acpi_table_attr_list, node) { list_for_each_entry(attr, &acpi_table_attr_list, node) {
if (!memcmp(table_attr->name, attr->name, ACPI_NAME_SIZE)) if (ACPI_COMPARE_NAME(table_attr->name, attr->name))
if (table_attr->instance < attr->instance) if (table_attr->instance < attr->instance)
table_attr->instance = attr->instance; table_attr->instance = attr->instance;
} }
table_attr->instance++; table_attr->instance++;
if (table_attr->instance > ACPI_MAX_TABLE_INSTANCES) {
pr_warn("%4.4s: too many table instances\n",
table_attr->name);
return -ERANGE;
}
ACPI_MOVE_NAME(table_attr->filename, table_header->signature);
table_attr->filename[ACPI_NAME_SIZE] = '\0';
if (table_attr->instance > 1 || (table_attr->instance == 1 && if (table_attr->instance > 1 || (table_attr->instance == 1 &&
!acpi_get_table !acpi_get_table
(table_header->signature, 2, &header))) (table_header->signature, 2, &header))) {
sprintf(table_attr->name + ACPI_NAME_SIZE, "%d", snprintf(instance_str, sizeof(instance_str), "%u",
table_attr->instance); table_attr->instance);
strcat(table_attr->filename, instance_str);
}
table_attr->attr.size = table_header->length; table_attr->attr.size = table_header->length;
table_attr->attr.read = acpi_table_show; table_attr->attr.read = acpi_table_show;
table_attr->attr.attr.name = table_attr->name; table_attr->attr.attr.name = table_attr->filename;
table_attr->attr.attr.mode = 0400; table_attr->attr.attr.mode = 0400;
return; return sysfs_create_bin_file(tables_obj, &table_attr->attr);
} }
acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context) acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context)
@ -383,21 +389,22 @@ acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context)
struct acpi_table_attr *table_attr; struct acpi_table_attr *table_attr;
switch (event) { switch (event) {
case ACPI_TABLE_EVENT_LOAD: case ACPI_TABLE_EVENT_INSTALL:
table_attr = table_attr =
kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL); kzalloc(sizeof(struct acpi_table_attr), GFP_KERNEL);
if (!table_attr) if (!table_attr)
return AE_NO_MEMORY; return AE_NO_MEMORY;
acpi_table_attr_init(table_attr, table); if (acpi_table_attr_init(dynamic_tables_kobj,
if (sysfs_create_bin_file(dynamic_tables_kobj, table_attr, table)) {
&table_attr->attr)) {
kfree(table_attr); kfree(table_attr);
return AE_ERROR; return AE_ERROR;
} else }
list_add_tail(&table_attr->node, &acpi_table_attr_list); list_add_tail(&table_attr->node, &acpi_table_attr_list);
break; break;
case ACPI_TABLE_EVENT_LOAD:
case ACPI_TABLE_EVENT_UNLOAD: case ACPI_TABLE_EVENT_UNLOAD:
case ACPI_TABLE_EVENT_UNINSTALL:
/* /*
* we do not need to do anything right now * we do not need to do anything right now
* because the table is not deleted from the * because the table is not deleted from the
@ -435,13 +442,12 @@ static int acpi_tables_sysfs_init(void)
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
continue; continue;
table_attr = NULL;
table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL); table_attr = kzalloc(sizeof(*table_attr), GFP_KERNEL);
if (!table_attr) if (!table_attr)
return -ENOMEM; return -ENOMEM;
acpi_table_attr_init(table_attr, table_header); ret = acpi_table_attr_init(tables_kobj,
ret = sysfs_create_bin_file(tables_kobj, &table_attr->attr); table_attr, table_header);
if (ret) { if (ret) {
kfree(table_attr); kfree(table_attr);
return ret; return ret;
@ -597,14 +603,27 @@ static ssize_t counter_show(struct kobject *kobj,
if (result) if (result)
goto end; goto end;
if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) if (status & ACPI_EVENT_FLAG_ENABLE_SET)
size += sprintf(buf + size, " invalid"); size += sprintf(buf + size, " EN");
else if (status & ACPI_EVENT_FLAG_ENABLED)
size += sprintf(buf + size, " enabled");
else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED)
size += sprintf(buf + size, " wake_enabled");
else else
size += sprintf(buf + size, " disabled"); size += sprintf(buf + size, " ");
if (status & ACPI_EVENT_FLAG_STATUS_SET)
size += sprintf(buf + size, " STS");
else
size += sprintf(buf + size, " ");
if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER))
size += sprintf(buf + size, " invalid ");
else if (status & ACPI_EVENT_FLAG_ENABLED)
size += sprintf(buf + size, " enabled ");
else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED)
size += sprintf(buf + size, " wake_enabled");
else
size += sprintf(buf + size, " disabled ");
if (status & ACPI_EVENT_FLAG_MASKED)
size += sprintf(buf + size, " masked ");
else
size += sprintf(buf + size, " unmasked");
end: end:
size += sprintf(buf + size, "\n"); size += sprintf(buf + size, "\n");
@ -655,8 +674,12 @@ static ssize_t counter_set(struct kobject *kobj,
!(status & ACPI_EVENT_FLAG_ENABLED)) !(status & ACPI_EVENT_FLAG_ENABLED))
result = acpi_enable_gpe(handle, index); result = acpi_enable_gpe(handle, index);
else if (!strcmp(buf, "clear\n") && else if (!strcmp(buf, "clear\n") &&
(status & ACPI_EVENT_FLAG_SET)) (status & ACPI_EVENT_FLAG_STATUS_SET))
result = acpi_clear_gpe(handle, index); result = acpi_clear_gpe(handle, index);
else if (!strcmp(buf, "mask\n"))
result = acpi_mask_gpe(handle, index, TRUE);
else if (!strcmp(buf, "unmask\n"))
result = acpi_mask_gpe(handle, index, FALSE);
else if (!kstrtoul(buf, 0, &tmp)) else if (!kstrtoul(buf, 0, &tmp))
all_counters[index].count = tmp; all_counters[index].count = tmp;
else else
@ -664,13 +687,13 @@ static ssize_t counter_set(struct kobject *kobj,
} else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) { } else if (index < num_gpes + ACPI_NUM_FIXED_EVENTS) {
int event = index - num_gpes; int event = index - num_gpes;
if (!strcmp(buf, "disable\n") && if (!strcmp(buf, "disable\n") &&
(status & ACPI_EVENT_FLAG_ENABLED)) (status & ACPI_EVENT_FLAG_ENABLE_SET))
result = acpi_disable_event(event, ACPI_NOT_ISR); result = acpi_disable_event(event, ACPI_NOT_ISR);
else if (!strcmp(buf, "enable\n") && else if (!strcmp(buf, "enable\n") &&
!(status & ACPI_EVENT_FLAG_ENABLED)) !(status & ACPI_EVENT_FLAG_ENABLE_SET))
result = acpi_enable_event(event, ACPI_NOT_ISR); result = acpi_enable_event(event, ACPI_NOT_ISR);
else if (!strcmp(buf, "clear\n") && else if (!strcmp(buf, "clear\n") &&
(status & ACPI_EVENT_FLAG_SET)) (status & ACPI_EVENT_FLAG_STATUS_SET))
result = acpi_clear_event(event); result = acpi_clear_event(event);
else if (!kstrtoul(buf, 0, &tmp)) else if (!kstrtoul(buf, 0, &tmp))
all_counters[index].count = tmp; all_counters[index].count = tmp;

View File

@ -35,7 +35,6 @@
#include <linux/earlycpio.h> #include <linux/earlycpio.h>
#include <linux/memblock.h> #include <linux/memblock.h>
#include <linux/initrd.h> #include <linux/initrd.h>
#include <linux/acpi.h>
#include "internal.h" #include "internal.h"
#ifdef CONFIG_ACPI_CUSTOM_DSDT #ifdef CONFIG_ACPI_CUSTOM_DSDT
@ -246,6 +245,7 @@ acpi_parse_entries_array(char *id, unsigned long table_size,
struct acpi_subtable_header *entry; struct acpi_subtable_header *entry;
unsigned long table_end; unsigned long table_end;
int count = 0; int count = 0;
int errs = 0;
int i; int i;
if (acpi_disabled) if (acpi_disabled)
@ -278,10 +278,12 @@ acpi_parse_entries_array(char *id, unsigned long table_size,
if (entry->type != proc[i].id) if (entry->type != proc[i].id)
continue; continue;
if (!proc[i].handler || if (!proc[i].handler ||
proc[i].handler(entry, table_end)) (!errs && proc[i].handler(entry, table_end))) {
return -EINVAL; errs++;
continue;
}
proc->count++; proc[i].count++;
break; break;
} }
if (i != proc_num) if (i != proc_num)
@ -301,11 +303,11 @@ acpi_parse_entries_array(char *id, unsigned long table_size,
} }
if (max_entries && count > max_entries) { if (max_entries && count > max_entries) {
pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", pr_warn("[%4.4s:0x%02x] found the maximum %i entries\n",
id, proc->id, count - max_entries, count); id, proc->id, count);
} }
return count; return errs ? -EINVAL : count;
} }
int __init int __init

View File

@ -1266,6 +1266,7 @@ void device_del(struct device *dev)
bus_remove_device(dev); bus_remove_device(dev);
device_pm_remove(dev); device_pm_remove(dev);
driver_deferred_probe_del(dev); driver_deferred_probe_del(dev);
device_remove_properties(dev);
/* Notify the platform of the removal, in case they /* Notify the platform of the removal, in case they
* need to do anything... * need to do anything...

View File

@ -39,7 +39,7 @@
* performance capabilities, desired performance level * performance capabilities, desired performance level
* requested etc. * requested etc.
*/ */
static struct cpudata **all_cpu_data; static struct cppc_cpudata **all_cpu_data;
/* Capture the max KHz from DMI */ /* Capture the max KHz from DMI */
static u64 cppc_dmi_max_khz; static u64 cppc_dmi_max_khz;
@ -78,7 +78,7 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation) unsigned int relation)
{ {
struct cpudata *cpu; struct cppc_cpudata *cpu;
struct cpufreq_freqs freqs; struct cpufreq_freqs freqs;
int ret = 0; int ret = 0;
@ -108,7 +108,7 @@ static int cppc_verify_policy(struct cpufreq_policy *policy)
static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)
{ {
int cpu_num = policy->cpu; int cpu_num = policy->cpu;
struct cpudata *cpu = all_cpu_data[cpu_num]; struct cppc_cpudata *cpu = all_cpu_data[cpu_num];
int ret; int ret;
cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf; cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf;
@ -121,7 +121,7 @@ static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy)
static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
{ {
struct cpudata *cpu; struct cppc_cpudata *cpu;
unsigned int cpu_num = policy->cpu; unsigned int cpu_num = policy->cpu;
int ret = 0; int ret = 0;
@ -142,6 +142,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->max = cppc_dmi_max_khz; policy->max = cppc_dmi_max_khz;
policy->cpuinfo.min_freq = policy->min; policy->cpuinfo.min_freq = policy->min;
policy->cpuinfo.max_freq = policy->max; policy->cpuinfo.max_freq = policy->max;
policy->cpuinfo.transition_latency = cppc_get_transition_latency(cpu_num);
policy->shared_type = cpu->shared_type; policy->shared_type = cpu->shared_type;
if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY)
@ -179,7 +180,7 @@ static struct cpufreq_driver cppc_cpufreq_driver = {
static int __init cppc_cpufreq_init(void) static int __init cppc_cpufreq_init(void)
{ {
int i, ret = 0; int i, ret = 0;
struct cpudata *cpu; struct cppc_cpudata *cpu;
if (acpi_disabled) if (acpi_disabled)
return -ENODEV; return -ENODEV;
@ -189,7 +190,7 @@ static int __init cppc_cpufreq_init(void)
return -ENOMEM; return -ENOMEM;
for_each_possible_cpu(i) { for_each_possible_cpu(i) {
all_cpu_data[i] = kzalloc(sizeof(struct cpudata), GFP_KERNEL); all_cpu_data[i] = kzalloc(sizeof(struct cppc_cpudata), GFP_KERNEL);
if (!all_cpu_data[i]) if (!all_cpu_data[i])
goto out; goto out;
@ -220,7 +221,7 @@ out:
static void __exit cppc_cpufreq_exit(void) static void __exit cppc_cpufreq_exit(void)
{ {
struct cpudata *cpu; struct cppc_cpudata *cpu;
int i; int i;
cpufreq_unregister_driver(&cppc_cpufreq_driver); cpufreq_unregister_driver(&cppc_cpufreq_driver);

View File

@ -1486,7 +1486,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
priv->features |= FEATURE_IRQ; priv->features |= FEATURE_IRQ;
priv->features |= FEATURE_SMBUS_PEC; priv->features |= FEATURE_SMBUS_PEC;
priv->features |= FEATURE_BLOCK_BUFFER; priv->features |= FEATURE_BLOCK_BUFFER;
priv->features |= FEATURE_TCO; /* If we have ACPI based watchdog use that instead */
if (!acpi_has_watchdog())
priv->features |= FEATURE_TCO;
priv->features |= FEATURE_HOST_NOTIFY; priv->features |= FEATURE_HOST_NOTIFY;
break; break;

View File

@ -59,6 +59,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mailbox_controller.h> #include <linux/mailbox_controller.h>
@ -68,11 +69,16 @@
#include "mailbox.h" #include "mailbox.h"
#define MAX_PCC_SUBSPACES 256 #define MAX_PCC_SUBSPACES 256
#define MBOX_IRQ_NAME "pcc-mbox"
static struct mbox_chan *pcc_mbox_channels; static struct mbox_chan *pcc_mbox_channels;
/* Array of cached virtual address for doorbell registers */ /* Array of cached virtual address for doorbell registers */
static void __iomem **pcc_doorbell_vaddr; static void __iomem **pcc_doorbell_vaddr;
/* Array of cached virtual address for doorbell ack registers */
static void __iomem **pcc_doorbell_ack_vaddr;
/* Array of doorbell interrupts */
static int *pcc_doorbell_irq;
static struct mbox_controller pcc_mbox_ctrl = {}; static struct mbox_controller pcc_mbox_ctrl = {};
/** /**
@ -91,79 +97,6 @@ static struct mbox_chan *get_pcc_channel(int id)
return &pcc_mbox_channels[id]; return &pcc_mbox_channels[id];
} }
/**
* pcc_mbox_request_channel - PCC clients call this function to
* request a pointer to their PCC subspace, from which they
* can get the details of communicating with the remote.
* @cl: Pointer to Mailbox client, so we know where to bind the
* Channel.
* @subspace_id: The PCC Subspace index as parsed in the PCC client
* ACPI package. This is used to lookup the array of PCC
* subspaces as parsed by the PCC Mailbox controller.
*
* Return: Pointer to the Mailbox Channel if successful or
* ERR_PTR.
*/
struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl,
int subspace_id)
{
struct device *dev = pcc_mbox_ctrl.dev;
struct mbox_chan *chan;
unsigned long flags;
/*
* Each PCC Subspace is a Mailbox Channel.
* The PCC Clients get their PCC Subspace ID
* from their own tables and pass it here.
* This returns a pointer to the PCC subspace
* for the Client to operate on.
*/
chan = get_pcc_channel(subspace_id);
if (IS_ERR(chan) || chan->cl) {
dev_err(dev, "Channel not found for idx: %d\n", subspace_id);
return ERR_PTR(-EBUSY);
}
spin_lock_irqsave(&chan->lock, flags);
chan->msg_free = 0;
chan->msg_count = 0;
chan->active_req = NULL;
chan->cl = cl;
init_completion(&chan->tx_complete);
if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone)
chan->txdone_method |= TXDONE_BY_ACK;
spin_unlock_irqrestore(&chan->lock, flags);
return chan;
}
EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
/**
* pcc_mbox_free_channel - Clients call this to free their Channel.
*
* @chan: Pointer to the mailbox channel as returned by
* pcc_mbox_request_channel()
*/
void pcc_mbox_free_channel(struct mbox_chan *chan)
{
unsigned long flags;
if (!chan || !chan->cl)
return;
spin_lock_irqsave(&chan->lock, flags);
chan->cl = NULL;
chan->active_req = NULL;
if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK))
chan->txdone_method = TXDONE_BY_POLL;
spin_unlock_irqrestore(&chan->lock, flags);
}
EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
/* /*
* PCC can be used with perf critical drivers such as CPPC * PCC can be used with perf critical drivers such as CPPC
* So it makes sense to locally cache the virtual address and * So it makes sense to locally cache the virtual address and
@ -224,6 +157,167 @@ static int write_register(void __iomem *vaddr, u64 val, unsigned int bit_width)
return ret_val; return ret_val;
} }
/**
* pcc_map_interrupt - Map a PCC subspace GSI to a linux IRQ number
* @interrupt: GSI number.
* @flags: interrupt flags
*
* Returns: a valid linux IRQ number on success
* 0 or -EINVAL on failure
*/
static int pcc_map_interrupt(u32 interrupt, u32 flags)
{
int trigger, polarity;
if (!interrupt)
return 0;
trigger = (flags & ACPI_PCCT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
: ACPI_LEVEL_SENSITIVE;
polarity = (flags & ACPI_PCCT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
: ACPI_ACTIVE_HIGH;
return acpi_register_gsi(NULL, interrupt, trigger, polarity);
}
/**
* pcc_mbox_irq - PCC mailbox interrupt handler
*/
static irqreturn_t pcc_mbox_irq(int irq, void *p)
{
struct acpi_generic_address *doorbell_ack;
struct acpi_pcct_hw_reduced *pcct_ss;
struct mbox_chan *chan = p;
u64 doorbell_ack_preserve;
u64 doorbell_ack_write;
u64 doorbell_ack_val;
int ret;
pcct_ss = chan->con_priv;
mbox_chan_received_data(chan, NULL);
if (pcct_ss->header.type == ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_hw_reduced_type2 *pcct2_ss = chan->con_priv;
u32 id = chan - pcc_mbox_channels;
doorbell_ack = &pcct2_ss->doorbell_ack_register;
doorbell_ack_preserve = pcct2_ss->ack_preserve_mask;
doorbell_ack_write = pcct2_ss->ack_write_mask;
ret = read_register(pcc_doorbell_ack_vaddr[id],
&doorbell_ack_val,
doorbell_ack->bit_width);
if (ret)
return IRQ_NONE;
ret = write_register(pcc_doorbell_ack_vaddr[id],
(doorbell_ack_val & doorbell_ack_preserve)
| doorbell_ack_write,
doorbell_ack->bit_width);
if (ret)
return IRQ_NONE;
}
return IRQ_HANDLED;
}
/**
* pcc_mbox_request_channel - PCC clients call this function to
* request a pointer to their PCC subspace, from which they
* can get the details of communicating with the remote.
* @cl: Pointer to Mailbox client, so we know where to bind the
* Channel.
* @subspace_id: The PCC Subspace index as parsed in the PCC client
* ACPI package. This is used to lookup the array of PCC
* subspaces as parsed by the PCC Mailbox controller.
*
* Return: Pointer to the Mailbox Channel if successful or
* ERR_PTR.
*/
struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl,
int subspace_id)
{
struct device *dev = pcc_mbox_ctrl.dev;
struct mbox_chan *chan;
unsigned long flags;
/*
* Each PCC Subspace is a Mailbox Channel.
* The PCC Clients get their PCC Subspace ID
* from their own tables and pass it here.
* This returns a pointer to the PCC subspace
* for the Client to operate on.
*/
chan = get_pcc_channel(subspace_id);
if (IS_ERR(chan) || chan->cl) {
dev_err(dev, "Channel not found for idx: %d\n", subspace_id);
return ERR_PTR(-EBUSY);
}
spin_lock_irqsave(&chan->lock, flags);
chan->msg_free = 0;
chan->msg_count = 0;
chan->active_req = NULL;
chan->cl = cl;
init_completion(&chan->tx_complete);
if (chan->txdone_method == TXDONE_BY_POLL && cl->knows_txdone)
chan->txdone_method |= TXDONE_BY_ACK;
if (pcc_doorbell_irq[subspace_id] > 0) {
int rc;
rc = devm_request_irq(dev, pcc_doorbell_irq[subspace_id],
pcc_mbox_irq, 0, MBOX_IRQ_NAME, chan);
if (unlikely(rc)) {
dev_err(dev, "failed to register PCC interrupt %d\n",
pcc_doorbell_irq[subspace_id]);
chan = ERR_PTR(rc);
}
}
spin_unlock_irqrestore(&chan->lock, flags);
return chan;
}
EXPORT_SYMBOL_GPL(pcc_mbox_request_channel);
/**
* pcc_mbox_free_channel - Clients call this to free their Channel.
*
* @chan: Pointer to the mailbox channel as returned by
* pcc_mbox_request_channel()
*/
void pcc_mbox_free_channel(struct mbox_chan *chan)
{
u32 id = chan - pcc_mbox_channels;
unsigned long flags;
if (!chan || !chan->cl)
return;
if (id >= pcc_mbox_ctrl.num_chans) {
pr_debug("pcc_mbox_free_channel: Invalid mbox_chan passed\n");
return;
}
spin_lock_irqsave(&chan->lock, flags);
chan->cl = NULL;
chan->active_req = NULL;
if (chan->txdone_method == (TXDONE_BY_POLL | TXDONE_BY_ACK))
chan->txdone_method = TXDONE_BY_POLL;
if (pcc_doorbell_irq[id] > 0)
devm_free_irq(chan->mbox->dev, pcc_doorbell_irq[id], chan);
spin_unlock_irqrestore(&chan->lock, flags);
}
EXPORT_SYMBOL_GPL(pcc_mbox_free_channel);
/** /**
* pcc_send_data - Called from Mailbox Controller code. Used * pcc_send_data - Called from Mailbox Controller code. Used
* here only to ring the channel doorbell. The PCC client * here only to ring the channel doorbell. The PCC client
@ -296,8 +390,10 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header,
if (pcc_mbox_ctrl.num_chans <= MAX_PCC_SUBSPACES) { if (pcc_mbox_ctrl.num_chans <= MAX_PCC_SUBSPACES) {
pcct_ss = (struct acpi_pcct_hw_reduced *) header; pcct_ss = (struct acpi_pcct_hw_reduced *) header;
if (pcct_ss->header.type != if ((pcct_ss->header.type !=
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE) { ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE)
&& (pcct_ss->header.type !=
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2)) {
pr_err("Incorrect PCC Subspace type detected\n"); pr_err("Incorrect PCC Subspace type detected\n");
return -EINVAL; return -EINVAL;
} }
@ -306,6 +402,43 @@ static int parse_pcc_subspace(struct acpi_subtable_header *header,
return 0; return 0;
} }
/**
* pcc_parse_subspace_irq - Parse the PCC IRQ and PCC ACK register
* There should be one entry per PCC client.
* @id: PCC subspace index.
* @pcct_ss: Pointer to the ACPI subtable header under the PCCT.
*
* Return: 0 for Success, else errno.
*
* This gets called for each entry in the PCC table.
*/
static int pcc_parse_subspace_irq(int id,
struct acpi_pcct_hw_reduced *pcct_ss)
{
pcc_doorbell_irq[id] = pcc_map_interrupt(pcct_ss->doorbell_interrupt,
(u32)pcct_ss->flags);
if (pcc_doorbell_irq[id] <= 0) {
pr_err("PCC GSI %d not registered\n",
pcct_ss->doorbell_interrupt);
return -EINVAL;
}
if (pcct_ss->header.type
== ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2) {
struct acpi_pcct_hw_reduced_type2 *pcct2_ss = (void *)pcct_ss;
pcc_doorbell_ack_vaddr[id] = acpi_os_ioremap(
pcct2_ss->doorbell_ack_register.address,
pcct2_ss->doorbell_ack_register.bit_width / 8);
if (!pcc_doorbell_ack_vaddr[id]) {
pr_err("Failed to ioremap PCC ACK register\n");
return -ENOMEM;
}
}
return 0;
}
/** /**
* acpi_pcc_probe - Parse the ACPI tree for the PCCT. * acpi_pcc_probe - Parse the ACPI tree for the PCCT.
* *
@ -316,7 +449,9 @@ static int __init acpi_pcc_probe(void)
acpi_size pcct_tbl_header_size; acpi_size pcct_tbl_header_size;
struct acpi_table_header *pcct_tbl; struct acpi_table_header *pcct_tbl;
struct acpi_subtable_header *pcct_entry; struct acpi_subtable_header *pcct_entry;
int count, i; struct acpi_table_pcct *acpi_pcct_tbl;
int count, i, rc;
int sum = 0;
acpi_status status = AE_OK; acpi_status status = AE_OK;
/* Search for PCCT */ /* Search for PCCT */
@ -333,37 +468,66 @@ static int __init acpi_pcc_probe(void)
sizeof(struct acpi_table_pcct), sizeof(struct acpi_table_pcct),
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE, ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE,
parse_pcc_subspace, MAX_PCC_SUBSPACES); parse_pcc_subspace, MAX_PCC_SUBSPACES);
sum += (count > 0) ? count : 0;
if (count <= 0) { count = acpi_table_parse_entries(ACPI_SIG_PCCT,
sizeof(struct acpi_table_pcct),
ACPI_PCCT_TYPE_HW_REDUCED_SUBSPACE_TYPE2,
parse_pcc_subspace, MAX_PCC_SUBSPACES);
sum += (count > 0) ? count : 0;
if (sum == 0 || sum >= MAX_PCC_SUBSPACES) {
pr_err("Error parsing PCC subspaces from PCCT\n"); pr_err("Error parsing PCC subspaces from PCCT\n");
return -EINVAL; return -EINVAL;
} }
pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) * pcc_mbox_channels = kzalloc(sizeof(struct mbox_chan) *
count, GFP_KERNEL); sum, GFP_KERNEL);
if (!pcc_mbox_channels) { if (!pcc_mbox_channels) {
pr_err("Could not allocate space for PCC mbox channels\n"); pr_err("Could not allocate space for PCC mbox channels\n");
return -ENOMEM; return -ENOMEM;
} }
pcc_doorbell_vaddr = kcalloc(count, sizeof(void *), GFP_KERNEL); pcc_doorbell_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL);
if (!pcc_doorbell_vaddr) { if (!pcc_doorbell_vaddr) {
kfree(pcc_mbox_channels); rc = -ENOMEM;
return -ENOMEM; goto err_free_mbox;
}
pcc_doorbell_ack_vaddr = kcalloc(sum, sizeof(void *), GFP_KERNEL);
if (!pcc_doorbell_ack_vaddr) {
rc = -ENOMEM;
goto err_free_db_vaddr;
}
pcc_doorbell_irq = kcalloc(sum, sizeof(int), GFP_KERNEL);
if (!pcc_doorbell_irq) {
rc = -ENOMEM;
goto err_free_db_ack_vaddr;
} }
/* Point to the first PCC subspace entry */ /* Point to the first PCC subspace entry */
pcct_entry = (struct acpi_subtable_header *) ( pcct_entry = (struct acpi_subtable_header *) (
(unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct)); (unsigned long) pcct_tbl + sizeof(struct acpi_table_pcct));
for (i = 0; i < count; i++) { acpi_pcct_tbl = (struct acpi_table_pcct *) pcct_tbl;
if (acpi_pcct_tbl->flags & ACPI_PCCT_DOORBELL)
pcc_mbox_ctrl.txdone_irq = true;
for (i = 0; i < sum; i++) {
struct acpi_generic_address *db_reg; struct acpi_generic_address *db_reg;
struct acpi_pcct_hw_reduced *pcct_ss; struct acpi_pcct_hw_reduced *pcct_ss;
pcc_mbox_channels[i].con_priv = pcct_entry; pcc_mbox_channels[i].con_priv = pcct_entry;
pcct_ss = (struct acpi_pcct_hw_reduced *) pcct_entry;
if (pcc_mbox_ctrl.txdone_irq) {
rc = pcc_parse_subspace_irq(i, pcct_ss);
if (rc < 0)
goto err;
}
/* If doorbell is in system memory cache the virt address */ /* If doorbell is in system memory cache the virt address */
pcct_ss = (struct acpi_pcct_hw_reduced *)pcct_entry;
db_reg = &pcct_ss->doorbell_register; db_reg = &pcct_ss->doorbell_register;
if (db_reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) if (db_reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY)
pcc_doorbell_vaddr[i] = acpi_os_ioremap(db_reg->address, pcc_doorbell_vaddr[i] = acpi_os_ioremap(db_reg->address,
@ -372,11 +536,21 @@ static int __init acpi_pcc_probe(void)
((unsigned long) pcct_entry + pcct_entry->length); ((unsigned long) pcct_entry + pcct_entry->length);
} }
pcc_mbox_ctrl.num_chans = count; pcc_mbox_ctrl.num_chans = sum;
pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans); pr_info("Detected %d PCC Subspaces\n", pcc_mbox_ctrl.num_chans);
return 0; return 0;
err:
kfree(pcc_doorbell_irq);
err_free_db_ack_vaddr:
kfree(pcc_doorbell_ack_vaddr);
err_free_db_vaddr:
kfree(pcc_doorbell_vaddr);
err_free_mbox:
kfree(pcc_mbox_channels);
return rc;
} }
/** /**

View File

@ -984,6 +984,10 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
int ret; int ret;
struct resource *res; struct resource *res;
/* If we have ACPI based watchdog use that instead */
if (acpi_has_watchdog())
return -ENODEV;
/* Setup power management base register */ /* Setup power management base register */
pci_read_config_dword(dev, priv->abase, &base_addr_cfg); pci_read_config_dword(dev, priv->abase, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80; base_addr = base_addr_cfg & 0x0000ff80;

View File

@ -479,6 +479,30 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev,
} }
EXPORT_SYMBOL(pci_find_parent_resource); EXPORT_SYMBOL(pci_find_parent_resource);
/**
* pci_find_resource - Return matching PCI device resource
* @dev: PCI device to query
* @res: Resource to look for
*
* Goes over standard PCI resources (BARs) and checks if the given resource
* is partially or fully contained in any of them. In that case the
* matching resource is returned, %NULL otherwise.
*/
struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res)
{
int i;
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
struct resource *r = &dev->resource[i];
if (r->start && resource_contains(r, res))
return r;
}
return NULL;
}
EXPORT_SYMBOL(pci_find_resource);
/** /**
* pci_find_pcie_root_port - return PCIe Root Port * pci_find_pcie_root_port - return PCIe Root Port
* @dev: PCI device to query * @dev: PCI device to query

View File

@ -651,11 +651,15 @@ static int ipc_create_pmc_devices(void)
{ {
int ret; int ret;
ret = ipc_create_tco_device(); /* If we have ACPI based watchdog use that instead */
if (ret) { if (!acpi_has_watchdog()) {
dev_err(ipcdev.dev, "Failed to add tco platform device\n"); ret = ipc_create_tco_device();
return ret; if (ret) {
dev_err(ipcdev.dev, "Failed to add tco platform device\n");
return ret;
}
} }
ret = ipc_create_punit_device(); ret = ipc_create_punit_device();
if (ret) { if (ret) {
dev_err(ipcdev.dev, "Failed to add punit platform device\n"); dev_err(ipcdev.dev, "Failed to add punit platform device\n");

View File

@ -298,12 +298,17 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
p->serial_out = dw8250_serial_out32be; p->serial_out = dw8250_serial_out32be;
} }
} else if (has_acpi_companion(p->dev)) { } else if (has_acpi_companion(p->dev)) {
p->iotype = UPIO_MEM32; const struct acpi_device_id *id;
p->regshift = 2;
p->serial_in = dw8250_serial_in32; id = acpi_match_device(p->dev->driver->acpi_match_table,
p->dev);
if (id && !strcmp(id->id, "APMC0D08")) {
p->iotype = UPIO_MEM32;
p->regshift = 2;
p->serial_in = dw8250_serial_in32;
data->uart_16550_compatible = true;
}
p->set_termios = dw8250_set_termios; p->set_termios = dw8250_set_termios;
/* So far none of there implement the Busy Functionality */
data->uart_16550_compatible = true;
} }
/* Platforms with iDMA */ /* Platforms with iDMA */

View File

@ -152,6 +152,19 @@ config TANGOX_WATCHDOG
This driver can be built as a module. The module name is tangox_wdt. This driver can be built as a module. The module name is tangox_wdt.
config WDAT_WDT
tristate "ACPI Watchdog Action Table (WDAT)"
depends on ACPI
select ACPI_WATCHDOG
help
This driver adds support for systems with ACPI Watchdog Action
Table (WDAT) table. Servers typically have this but it can be
found on some desktop machines as well. This driver will take
over the native iTCO watchdog driver found on many Intel CPUs.
To compile this driver as module, choose M here: the module will
be called wdat_wdt.
config WM831X_WATCHDOG config WM831X_WATCHDOG
tristate "WM831x watchdog" tristate "WM831x watchdog"
depends on MFD_WM831X depends on MFD_WM831X

View File

@ -202,6 +202,7 @@ obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o
obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o
obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o
obj-$(CONFIG_WDAT_WDT) += wdat_wdt.o
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o

526
drivers/watchdog/wdat_wdt.c Normal file
View File

@ -0,0 +1,526 @@
/*
* ACPI Hardware Watchdog (WDAT) driver.
*
* 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.
*/
#include <linux/acpi.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/watchdog.h>
#define MAX_WDAT_ACTIONS ACPI_WDAT_ACTION_RESERVED
/**
* struct wdat_instruction - Single ACPI WDAT instruction
* @entry: Copy of the ACPI table instruction
* @reg: Register the instruction is accessing
* @node: Next instruction in action sequence
*/
struct wdat_instruction {
struct acpi_wdat_entry entry;
void __iomem *reg;
struct list_head node;
};
/**
* struct wdat_wdt - ACPI WDAT watchdog device
* @pdev: Parent platform device
* @wdd: Watchdog core device
* @period: How long is one watchdog period in ms
* @stopped_in_sleep: Is this watchdog stopped by the firmware in S1-S5
* @stopped: Was the watchdog stopped by the driver in suspend
* @actions: An array of instruction lists indexed by an action number from
* the WDAT table. There can be %NULL entries for not implemented
* actions.
*/
struct wdat_wdt {
struct platform_device *pdev;
struct watchdog_device wdd;
unsigned int period;
bool stopped_in_sleep;
bool stopped;
struct list_head *instructions[MAX_WDAT_ACTIONS];
};
#define to_wdat_wdt(wdd) container_of(wdd, struct wdat_wdt, wdd)
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static int wdat_wdt_read(struct wdat_wdt *wdat,
const struct wdat_instruction *instr, u32 *value)
{
const struct acpi_generic_address *gas = &instr->entry.register_region;
switch (gas->access_width) {
case 1:
*value = ioread8(instr->reg);
break;
case 2:
*value = ioread16(instr->reg);
break;
case 3:
*value = ioread32(instr->reg);
break;
default:
return -EINVAL;
}
dev_dbg(&wdat->pdev->dev, "Read %#x from 0x%08llx\n", *value,
gas->address);
return 0;
}
static int wdat_wdt_write(struct wdat_wdt *wdat,
const struct wdat_instruction *instr, u32 value)
{
const struct acpi_generic_address *gas = &instr->entry.register_region;
switch (gas->access_width) {
case 1:
iowrite8((u8)value, instr->reg);
break;
case 2:
iowrite16((u16)value, instr->reg);
break;
case 3:
iowrite32(value, instr->reg);
break;
default:
return -EINVAL;
}
dev_dbg(&wdat->pdev->dev, "Wrote %#x to 0x%08llx\n", value,
gas->address);
return 0;
}
static int wdat_wdt_run_action(struct wdat_wdt *wdat, unsigned int action,
u32 param, u32 *retval)
{
struct wdat_instruction *instr;
if (action >= ARRAY_SIZE(wdat->instructions))
return -EINVAL;
if (!wdat->instructions[action])
return -EOPNOTSUPP;
dev_dbg(&wdat->pdev->dev, "Running action %#x\n", action);
/* Run each instruction sequentially */
list_for_each_entry(instr, wdat->instructions[action], node) {
const struct acpi_wdat_entry *entry = &instr->entry;
const struct acpi_generic_address *gas;
u32 flags, value, mask, x, y;
bool preserve;
int ret;
gas = &entry->register_region;
preserve = entry->instruction & ACPI_WDAT_PRESERVE_REGISTER;
flags = entry->instruction & ~ACPI_WDAT_PRESERVE_REGISTER;
value = entry->value;
mask = entry->mask;
switch (flags) {
case ACPI_WDAT_READ_VALUE:
ret = wdat_wdt_read(wdat, instr, &x);
if (ret)
return ret;
x >>= gas->bit_offset;
x &= mask;
if (retval)
*retval = x == value;
break;
case ACPI_WDAT_READ_COUNTDOWN:
ret = wdat_wdt_read(wdat, instr, &x);
if (ret)
return ret;
x >>= gas->bit_offset;
x &= mask;
if (retval)
*retval = x;
break;
case ACPI_WDAT_WRITE_VALUE:
x = value & mask;
x <<= gas->bit_offset;
if (preserve) {
ret = wdat_wdt_read(wdat, instr, &y);
if (ret)
return ret;
y = y & ~(mask << gas->bit_offset);
x |= y;
}
ret = wdat_wdt_write(wdat, instr, x);
if (ret)
return ret;
break;
case ACPI_WDAT_WRITE_COUNTDOWN:
x = param;
x &= mask;
x <<= gas->bit_offset;
if (preserve) {
ret = wdat_wdt_read(wdat, instr, &y);
if (ret)
return ret;
y = y & ~(mask << gas->bit_offset);
x |= y;
}
ret = wdat_wdt_write(wdat, instr, x);
if (ret)
return ret;
break;
default:
dev_err(&wdat->pdev->dev, "Unknown instruction: %u\n",
flags);
return -EINVAL;
}
}
return 0;
}
static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat)
{
int ret;
/*
* WDAT specification says that the watchdog is required to reboot
* the system when it fires. However, it also states that it is
* recommeded to make it configurable through hardware register. We
* enable reboot now if it is configrable, just in case.
*/
ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL);
if (ret && ret != -EOPNOTSUPP) {
dev_err(&wdat->pdev->dev,
"Failed to enable reboot when watchdog triggers\n");
return ret;
}
return 0;
}
static void wdat_wdt_boot_status(struct wdat_wdt *wdat)
{
u32 boot_status = 0;
int ret;
ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_STATUS, 0, &boot_status);
if (ret && ret != -EOPNOTSUPP) {
dev_err(&wdat->pdev->dev, "Failed to read boot status\n");
return;
}
if (boot_status)
wdat->wdd.bootstatus = WDIOF_CARDRESET;
/* Clear the boot status in case BIOS did not do it */
ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STATUS, 0, NULL);
if (ret && ret != -EOPNOTSUPP)
dev_err(&wdat->pdev->dev, "Failed to clear boot status\n");
}
static void wdat_wdt_set_running(struct wdat_wdt *wdat)
{
u32 running = 0;
int ret;
ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_RUNNING_STATE, 0,
&running);
if (ret && ret != -EOPNOTSUPP)
dev_err(&wdat->pdev->dev, "Failed to read running state\n");
if (running)
set_bit(WDOG_HW_RUNNING, &wdat->wdd.status);
}
static int wdat_wdt_start(struct watchdog_device *wdd)
{
return wdat_wdt_run_action(to_wdat_wdt(wdd),
ACPI_WDAT_SET_RUNNING_STATE, 0, NULL);
}
static int wdat_wdt_stop(struct watchdog_device *wdd)
{
return wdat_wdt_run_action(to_wdat_wdt(wdd),
ACPI_WDAT_SET_STOPPED_STATE, 0, NULL);
}
static int wdat_wdt_ping(struct watchdog_device *wdd)
{
return wdat_wdt_run_action(to_wdat_wdt(wdd), ACPI_WDAT_RESET, 0, NULL);
}
static int wdat_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct wdat_wdt *wdat = to_wdat_wdt(wdd);
unsigned int periods;
int ret;
periods = timeout * 1000 / wdat->period;
ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_COUNTDOWN, periods, NULL);
if (!ret)
wdd->timeout = timeout;
return ret;
}
static unsigned int wdat_wdt_get_timeleft(struct watchdog_device *wdd)
{
struct wdat_wdt *wdat = to_wdat_wdt(wdd);
u32 periods = 0;
wdat_wdt_run_action(wdat, ACPI_WDAT_GET_COUNTDOWN, 0, &periods);
return periods * wdat->period / 1000;
}
static const struct watchdog_info wdat_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = "wdat_wdt",
};
static const struct watchdog_ops wdat_wdt_ops = {
.owner = THIS_MODULE,
.start = wdat_wdt_start,
.stop = wdat_wdt_stop,
.ping = wdat_wdt_ping,
.set_timeout = wdat_wdt_set_timeout,
.get_timeleft = wdat_wdt_get_timeleft,
};
static int wdat_wdt_probe(struct platform_device *pdev)
{
const struct acpi_wdat_entry *entries;
const struct acpi_table_wdat *tbl;
struct wdat_wdt *wdat;
struct resource *res;
void __iomem **regs;
acpi_status status;
int i, ret;
status = acpi_get_table(ACPI_SIG_WDAT, 0,
(struct acpi_table_header **)&tbl);
if (ACPI_FAILURE(status))
return -ENODEV;
wdat = devm_kzalloc(&pdev->dev, sizeof(*wdat), GFP_KERNEL);
if (!wdat)
return -ENOMEM;
regs = devm_kcalloc(&pdev->dev, pdev->num_resources, sizeof(*regs),
GFP_KERNEL);
if (!regs)
return -ENOMEM;
/* WDAT specification wants to have >= 1ms period */
if (tbl->timer_period < 1)
return -EINVAL;
if (tbl->min_count > tbl->max_count)
return -EINVAL;
wdat->period = tbl->timer_period;
wdat->wdd.min_hw_heartbeat_ms = wdat->period * tbl->min_count;
wdat->wdd.max_hw_heartbeat_ms = wdat->period * tbl->max_count;
wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED;
wdat->wdd.info = &wdat_wdt_info;
wdat->wdd.ops = &wdat_wdt_ops;
wdat->pdev = pdev;
/* Request and map all resources */
for (i = 0; i < pdev->num_resources; i++) {
void __iomem *reg;
res = &pdev->resource[i];
if (resource_type(res) == IORESOURCE_MEM) {
reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(reg))
return PTR_ERR(reg);
} else if (resource_type(res) == IORESOURCE_IO) {
reg = devm_ioport_map(&pdev->dev, res->start, 1);
if (!reg)
return -ENOMEM;
} else {
dev_err(&pdev->dev, "Unsupported resource\n");
return -EINVAL;
}
regs[i] = reg;
}
entries = (struct acpi_wdat_entry *)(tbl + 1);
for (i = 0; i < tbl->entries; i++) {
const struct acpi_generic_address *gas;
struct wdat_instruction *instr;
struct list_head *instructions;
unsigned int action;
struct resource r;
int j;
action = entries[i].action;
if (action >= MAX_WDAT_ACTIONS) {
dev_dbg(&pdev->dev, "Skipping unknown action: %u\n",
action);
continue;
}
instr = devm_kzalloc(&pdev->dev, sizeof(*instr), GFP_KERNEL);
if (!instr)
return -ENOMEM;
INIT_LIST_HEAD(&instr->node);
instr->entry = entries[i];
gas = &entries[i].register_region;
memset(&r, 0, sizeof(r));
r.start = gas->address;
r.end = r.start + gas->access_width;
if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
r.flags = IORESOURCE_MEM;
} else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
r.flags = IORESOURCE_IO;
} else {
dev_dbg(&pdev->dev, "Unsupported address space: %d\n",
gas->space_id);
continue;
}
/* Find the matching resource */
for (j = 0; j < pdev->num_resources; j++) {
res = &pdev->resource[j];
if (resource_contains(res, &r)) {
instr->reg = regs[j] + r.start - res->start;
break;
}
}
if (!instr->reg) {
dev_err(&pdev->dev, "I/O resource not found\n");
return -EINVAL;
}
instructions = wdat->instructions[action];
if (!instructions) {
instructions = devm_kzalloc(&pdev->dev,
sizeof(*instructions), GFP_KERNEL);
if (!instructions)
return -ENOMEM;
INIT_LIST_HEAD(instructions);
wdat->instructions[action] = instructions;
}
list_add_tail(&instr->node, instructions);
}
wdat_wdt_boot_status(wdat);
wdat_wdt_set_running(wdat);
ret = wdat_wdt_enable_reboot(wdat);
if (ret)
return ret;
platform_set_drvdata(pdev, wdat);
watchdog_set_nowayout(&wdat->wdd, nowayout);
return devm_watchdog_register_device(&pdev->dev, &wdat->wdd);
}
#ifdef CONFIG_PM_SLEEP
static int wdat_wdt_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct wdat_wdt *wdat = platform_get_drvdata(pdev);
int ret;
if (!watchdog_active(&wdat->wdd))
return 0;
/*
* We need to stop the watchdog if firmare is not doing it or if we
* are going suspend to idle (where firmware is not involved). If
* firmware is stopping the watchdog we kick it here one more time
* to give it some time.
*/
wdat->stopped = false;
if (acpi_target_system_state() == ACPI_STATE_S0 ||
!wdat->stopped_in_sleep) {
ret = wdat_wdt_stop(&wdat->wdd);
if (!ret)
wdat->stopped = true;
} else {
ret = wdat_wdt_ping(&wdat->wdd);
}
return ret;
}
static int wdat_wdt_resume_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct wdat_wdt *wdat = platform_get_drvdata(pdev);
int ret;
if (!watchdog_active(&wdat->wdd))
return 0;
if (!wdat->stopped) {
/*
* Looks like the boot firmware reinitializes the watchdog
* before it hands off to the OS on resume from sleep so we
* stop and reprogram the watchdog here.
*/
ret = wdat_wdt_stop(&wdat->wdd);
if (ret)
return ret;
ret = wdat_wdt_set_timeout(&wdat->wdd, wdat->wdd.timeout);
if (ret)
return ret;
ret = wdat_wdt_enable_reboot(wdat);
if (ret)
return ret;
}
return wdat_wdt_start(&wdat->wdd);
}
#endif
static const struct dev_pm_ops wdat_wdt_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(wdat_wdt_suspend_noirq,
wdat_wdt_resume_noirq)
};
static struct platform_driver wdat_wdt_driver = {
.probe = wdat_wdt_probe,
.driver = {
.name = "wdat_wdt",
.pm = &wdat_wdt_pm_ops,
},
};
module_platform_driver(wdat_wdt_driver);
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
MODULE_DESCRIPTION("ACPI Hardware Watchdog (WDAT) driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:wdat_wdt");

View File

@ -144,6 +144,10 @@
#define ACPI_ADDRESS_RANGE_MAX 2 #define ACPI_ADDRESS_RANGE_MAX 2
/* Maximum number of While() loops before abort */
#define ACPI_MAX_LOOP_COUNT 0xFFFF
/****************************************************************************** /******************************************************************************
* *
* ACPI Specification constants (Do not change unless the specification changes) * ACPI Specification constants (Do not change unless the specification changes)

View File

@ -366,7 +366,7 @@
ACPI_TRACE_ENTRY (name, acpi_ut_trace_u32, u32, value) ACPI_TRACE_ENTRY (name, acpi_ut_trace_u32, u32, value)
#define ACPI_FUNCTION_TRACE_STR(name, string) \ #define ACPI_FUNCTION_TRACE_STR(name, string) \
ACPI_TRACE_ENTRY (name, acpi_ut_trace_str, char *, string) ACPI_TRACE_ENTRY (name, acpi_ut_trace_str, const char *, string)
#define ACPI_FUNCTION_ENTRY() \ #define ACPI_FUNCTION_ENTRY() \
acpi_ut_track_stack_ptr() acpi_ut_track_stack_ptr()
@ -425,6 +425,9 @@
#define return_PTR(pointer) \ #define return_PTR(pointer) \
ACPI_TRACE_EXIT (acpi_ut_ptr_exit, void *, pointer) ACPI_TRACE_EXIT (acpi_ut_ptr_exit, void *, pointer)
#define return_STR(string) \
ACPI_TRACE_EXIT (acpi_ut_str_exit, const char *, string)
#define return_VALUE(value) \ #define return_VALUE(value) \
ACPI_TRACE_EXIT (acpi_ut_value_exit, u64, value) ACPI_TRACE_EXIT (acpi_ut_value_exit, u64, value)
@ -478,6 +481,7 @@
#define return_VOID return #define return_VOID return
#define return_ACPI_STATUS(s) return(s) #define return_ACPI_STATUS(s) return(s)
#define return_PTR(s) return(s) #define return_PTR(s) return(s)
#define return_STR(s) return(s)
#define return_VALUE(s) return(s) #define return_VALUE(s) return(s)
#define return_UINT8(s) return(s) #define return_UINT8(s) return(s)
#define return_UINT32(s) return(s) #define return_UINT32(s) return(s)

View File

@ -371,6 +371,12 @@ acpi_status acpi_os_wait_command_ready(void);
acpi_status acpi_os_notify_command_complete(void); acpi_status acpi_os_notify_command_complete(void);
#endif #endif
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_trace_point
void
acpi_os_trace_point(acpi_trace_event_type type,
u8 begin, u8 *aml, char *pathname);
#endif
/* /*
* Obtain ACPI table(s) * Obtain ACPI table(s)
*/ */
@ -416,41 +422,4 @@ char *acpi_os_get_next_filename(void *dir_handle);
void acpi_os_close_directory(void *dir_handle); void acpi_os_close_directory(void *dir_handle);
#endif #endif
/*
* File I/O and related support
*/
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_open_file
ACPI_FILE acpi_os_open_file(const char *path, u8 modes);
#endif
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_close_file
void acpi_os_close_file(ACPI_FILE file);
#endif
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_read_file
int
acpi_os_read_file(ACPI_FILE file,
void *buffer, acpi_size size, acpi_size count);
#endif
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_write_file
int
acpi_os_write_file(ACPI_FILE file,
void *buffer, acpi_size size, acpi_size count);
#endif
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_get_file_offset
long acpi_os_get_file_offset(ACPI_FILE file);
#endif
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_set_file_offset
acpi_status acpi_os_set_file_offset(ACPI_FILE file, long offset, u8 from);
#endif
#ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_trace_point
void
acpi_os_trace_point(acpi_trace_event_type type,
u8 begin, u8 *aml, char *pathname);
#endif
#endif /* __ACPIOSXF_H__ */ #endif /* __ACPIOSXF_H__ */

Some files were not shown because too many files have changed in this diff Show More