Commit e663107f authored by Linus Torvalds's avatar Linus Torvalds

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

Pull ACPI updates from Rafael Wysocki:
 "The new feaures here are the support for ACPI overlays (allowing ACPI
  tables to be loaded at any time from EFI variables or via configfs)
  and the LPI (Low-Power Idle) support.  Also notable is the ACPI-based
  NUMA support for ARM64.

  Apart from that we have two new drivers, for the DPTF (Dynamic Power
  and Thermal Framework) power participant device and for the Intel
  Broxton WhiskeyCove PMIC, some more PMIC-related changes, support for
  the Boot Error Record Table (BERT) in APEI and support for
  platform-initiated graceful shutdown.

  Plus two new pieces of documentation and usual assorted fixes and
  cleanups in quite a few places.

  Specifics:

   - Support for ACPI SSDT overlays allowing Secondary System
     Description Tables (SSDTs) to be loaded at any time from EFI
     variables or via configfs (Octavian Purdila, Mika Westerberg).

   - Support for the ACPI LPI (Low-Power Idle) feature introduced in
     ACPI 6.0 and allowing processor idle states to be represented in
     ACPI tables in a hierarchical way (with the help of Processor
     Container objects) and support for ACPI idle states management on
     ARM64, based on LPI (Sudeep Holla).

   - General improvements of ACPI support for NUMA and ARM64 support for
     ACPI-based NUMA (Hanjun Guo, David Daney, Robert Richter).

   - General improvements of the ACPI table upgrade mechanism and ARM64
     support for that feature (Aleksey Makarov, Jon Masters).

   - Support for the Boot Error Record Table (BERT) in APEI and
     improvements of kernel messages printed by the error injection code
     (Huang Ying, Borislav Petkov).

   - New driver for the Intel Broxton WhiskeyCove PMIC operation region
     and support for the REGS operation region on Broxton, PMIC code
     cleanups (Bin Gao, Felipe Balbi, Paul Gortmaker).

   - New driver for the power participant device which is part of the
     Dynamic Power and Thermal Framework (DPTF) and DPTF-related code
     reorganization (Srinivas Pandruvada).

   - Support for the platform-initiated graceful shutdown feature
     introduced in ACPI 6.1 (Prashanth Prakash).

   - ACPI button driver update related to lid input events generated
     automatically on initialization and system resume that have been
     problematic for some time (Lv Zheng).

   - ACPI EC driver cleanups (Lv Zheng).

   - Documentation of the ACPICA release automation process and the
     in-kernel ACPI AML debugger (Lv Zheng).

   - New blacklist entry and two fixes for the ACPI backlight driver
     (Alex Hung, Arvind Yadav, Ralf Gerbig).

   - Cleanups of the ACPI pci_slot driver (Joe Perches, Paul Gortmaker).

   - ACPI CPPC code changes to make it more robust against possible
     defects in ACPI tables and new symbol definitions for PCC (Hoan
     Tran).

   - System reboot code modification to execute the ACPI _PTS (Prepare
     To Sleep) method in addition to _TTS (Ocean He).

   - ACPICA-related change to carry out lock ordering checks in ACPICA
     if ACPICA debug is enabled in the kernel (Lv Zheng).

   - Assorted minor fixes and cleanups (Andy Shevchenko, Baoquan He,
     Bhaktipriya Shridhar, Paul Gortmaker, Rafael Wysocki)"

* tag 'acpi-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (71 commits)
  ACPI: enable ACPI_PROCESSOR_IDLE on ARM64
  arm64: add support for ACPI Low Power Idle(LPI)
  drivers: firmware: psci: initialise idle states using ACPI LPI
  cpuidle: introduce CPU_PM_CPU_IDLE_ENTER macro for ARM{32, 64}
  arm64: cpuidle: drop __init section marker to arm_cpuidle_init
  ACPI / processor_idle: Add support for Low Power Idle(LPI) states
  ACPI / processor_idle: introduce ACPI_PROCESSOR_CSTATE
  ACPI / DPTF: move int340x_thermal.c to the DPTF folder
  ACPI / DPTF: Add DPTF power participant driver
  ACPI / lpat: make it explicitly non-modular
  ACPI / dock: make dock explicitly non-modular
  ACPI / PCI: make pci_slot explicitly non-modular
  ACPI / PMIC: remove modular references from non-modular code
  ACPICA: Linux: Enable ACPI_MUTEX_DEBUG for Linux kernel
  ACPI: Rename configfs.c to acpi_configfs.c to prevent link error
  ACPI / debugger: Add AML debugger documentation
  ACPI: Add documentation describing ACPICA release automation
  ACPI: add support for loading SSDTs via configfs
  ACPI: add support for configfs
  efi / ACPI: load SSTDs from EFI variables
  ...
parents 6453dbdd 54d0b14a
What: /config/acpi
Date: July 2016
KernelVersion: 4.8
Contact: linux-acpi@vger.kernel.org
Description:
This represents the ACPI subsystem entry point directory. It
contains sub-groups corresponding to ACPI configurable options.
What: /config/acpi/table
Date: July 2016
KernelVersion: 4.8
Description:
This group contains the configuration for user defined ACPI
tables. The attributes of a user define table are:
aml - a binary attribute that the user can use to
fill in the ACPI aml definitions. Once the aml
data is written to this file and the file is
closed the table will be loaded and ACPI devices
will be enumerated. To check if the operation is
successful the user must check the error code
for close(). If the operation is successful,
subsequent writes to this attribute will fail.
The rest of the attributes are read-only and are valid only
after the table has been loaded by filling the aml entry:
signature - ASCII table signature
length - length of table in bytes, including the header
revision - ACPI Specification minor version number
oem_id - ASCII OEM identification
oem_table_id - ASCII OEM table identification
oem_revision - OEM revision number
asl_compiler_id - ASCII ASL compiler vendor ID
asl_compiler_revision - ASL compiler version
The AML Debugger
Copyright (C) 2016, Intel Corporation
Author: Lv Zheng <lv.zheng@intel.com>
This document describes the usage of the AML debugger embedded in the Linux
kernel.
1. Build the debugger
The following kernel configuration items are required to enable the AML
debugger interface from the Linux kernel:
CONFIG_ACPI_DEBUGGER=y
CONFIG_ACPI_DEBUGGER_USER=m
The userspace utlities can be built from the kernel source tree using
the following commands:
$ cd tools
$ make acpi
The resultant userspace tool binary is then located at:
tools/acpi/power/acpi/acpidbg/acpidbg
It can be installed to system directories by running "make install" (as a
sufficiently privileged user).
2. Start the userspace debugger interface
After booting the kernel with the debugger built-in, the debugger can be
started by using the following commands:
# mount -t debugfs none /sys/kernel/debug
# modprobe acpi_dbg
# tools/acpi/power/acpi/acpidbg/acpidbg
That spawns the interactive AML debugger environment where you can execute
debugger commands.
The commands are documented in the "ACPICA Overview and Programmer Reference"
that can be downloaded from
https://acpica.org/documentation
The detailed debugger commands reference is located in Chapter 12 "ACPICA
Debugger Reference". The "help" command can be used for a quick reference.
3. Stop the userspace debugger interface
The interactive debugger interface can be closed by pressing Ctrl+C or using
the "quit" or "exit" commands. When finished, unload the module with:
# rmmod acpi_dbg
The module unloading may fail if there is an acpidbg instance running.
4. Run the debugger in a script
It may be useful to run the AML debugger in a test script. "acpidbg" supports
this in a special "batch" mode. For example, the following command outputs
the entire ACPI namespace:
# acpidbg -b "namespace"
This diff is collapsed.
In order to support ACPI open-ended hardware configurations (e.g. development
boards) we need a way to augment the ACPI configuration provided by the firmware
image. A common example is connecting sensors on I2C / SPI buses on development
boards.
Although this can be accomplished by creating a kernel platform driver or
recompiling the firmware image with updated ACPI tables, neither is practical:
the former proliferates board specific kernel code while the latter requires
access to firmware tools which are often not publicly available.
Because ACPI supports external references in AML code a more practical
way to augment firmware ACPI configuration is by dynamically loading
user defined SSDT tables that contain the board specific information.
For example, to enumerate a Bosch BMA222E accelerometer on the I2C bus of the
Minnowboard MAX development board exposed via the LSE connector [1], the
following ASL code can be used:
DefinitionBlock ("minnowmax.aml", "SSDT", 1, "Vendor", "Accel", 0x00000003)
{
External (\_SB.I2C6, DeviceObj)
Scope (\_SB.I2C6)
{
Device (STAC)
{
Name (_ADR, Zero)
Name (_HID, "BMA222E")
Method (_CRS, 0, Serialized)
{
Name (RBUF, ResourceTemplate ()
{
I2cSerialBus (0x0018, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.I2C6", 0x00,
ResourceConsumer, ,)
GpioInt (Edge, ActiveHigh, Exclusive, PullDown, 0x0000,
"\\_SB.GPO2", 0x00, ResourceConsumer, , )
{ // Pin list
0
}
})
Return (RBUF)
}
}
}
}
which can then be compiled to AML binary format:
$ iasl minnowmax.asl
Intel ACPI Component Architecture
ASL Optimizing Compiler version 20140214-64 [Mar 29 2014]
Copyright (c) 2000 - 2014 Intel Corporation
ASL Input: minnomax.asl - 30 lines, 614 bytes, 7 keywords
AML Output: minnowmax.aml - 165 bytes, 6 named objects, 1 executable opcodes
[1] http://wiki.minnowboard.org/MinnowBoard_MAX#Low_Speed_Expansion_Connector_.28Top.29
The resulting AML code can then be loaded by the kernel using one of the methods
below.
== Loading ACPI SSDTs from initrd ==
This option allows loading of user defined SSDTs from initrd and it is useful
when the system does not support EFI or when there is not enough EFI storage.
It works in a similar way with initrd based ACPI tables override/upgrade: SSDT
aml code must be placed in the first, uncompressed, initrd under the
"kernel/firmware/acpi" path. Multiple files can be used and this will translate
in loading multiple tables. Only SSDT and OEM tables are allowed. See
initrd_table_override.txt for more details.
Here is an example:
# Add the raw ACPI tables to an uncompressed cpio archive.
# They must be put into a /kernel/firmware/acpi directory inside the
# cpio archive.
# The uncompressed cpio archive must be the first.
# Other, typically compressed cpio archives, must be
# concatenated on top of the uncompressed one.
mkdir -p kernel/firmware/acpi
cp ssdt.aml kernel/firmware/acpi
# Create the uncompressed cpio archive and concatenate the original initrd
# on top:
find kernel | cpio -H newc --create > /boot/instrumented_initrd
cat /boot/initrd >>/boot/instrumented_initrd
== Loading ACPI SSDTs from EFI variables ==
This is the preferred method, when EFI is supported on the platform, because it
allows a persistent, OS independent way of storing the user defined SSDTs. There
is also work underway to implement EFI support for loading user defined SSDTs
and using this method will make it easier to convert to the EFI loading
mechanism when that will arrive.
In order to load SSDTs from an EFI variable the efivar_ssdt kernel command line
parameter can be used. The argument for the option is the variable name to
use. If there are multiple variables with the same name but with different
vendor GUIDs, all of them will be loaded.
In order to store the AML code in an EFI variable the efivarfs filesystem can be
used. It is enabled and mounted by default in /sys/firmware/efi/efivars in all
recent distribution.
Creating a new file in /sys/firmware/efi/efivars will automatically create a new
EFI variable. Updating a file in /sys/firmware/efi/efivars will update the EFI
variable. Please note that the file name needs to be specially formatted as
"Name-GUID" and that the first 4 bytes in the file (little-endian format)
represent the attributes of the EFI variable (see EFI_VARIABLE_MASK in
include/linux/efi.h). Writing to the file must also be done with one write
operation.
For example, you can use the following bash script to create/update an EFI
variable with the content from a given file:
#!/bin/sh -e
while ! [ -z "$1" ]; do
case "$1" in
"-f") filename="$2"; shift;;
"-g") guid="$2"; shift;;
*) name="$1";;
esac
shift
done
usage()
{
echo "Syntax: ${0##*/} -f filename [ -g guid ] name"
exit 1
}
[ -n "$name" -a -f "$filename" ] || usage
EFIVARFS="/sys/firmware/efi/efivars"
[ -d "$EFIVARFS" ] || exit 2
if stat -tf $EFIVARFS | grep -q -v de5e81e4; then
mount -t efivarfs none $EFIVARFS
fi
# try to pick up an existing GUID
[ -n "$guid" ] || guid=$(find "$EFIVARFS" -name "$name-*" | head -n1 | cut -f2- -d-)
# use a randomly generated GUID
[ -n "$guid" ] || guid="$(cat /proc/sys/kernel/random/uuid)"
# efivarfs expects all of the data in one write
tmp=$(mktemp)
/bin/echo -ne "\007\000\000\000" | cat - $filename > $tmp
dd if=$tmp of="$EFIVARFS/$name-$guid" bs=$(stat -c %s $tmp)
rm $tmp
== Loading ACPI SSDTs from configfs ==
This option allows loading of user defined SSDTs from userspace via the configfs
interface. The CONFIG_ACPI_CONFIGFS option must be select and configfs must be
mounted. In the following examples, we assume that configfs has been mounted in
/config.
New tables can be loading by creating new directories in /config/acpi/table/ and
writing the SSDT aml code in the aml attribute:
cd /config/acpi/table
mkdir my_ssdt
cat ~/ssdt.aml > my_ssdt/aml
......@@ -582,6 +582,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
bootmem_debug [KNL] Enable bootmem allocator debug messages.
bert_disable [ACPI]
Disable BERT OS support on buggy BIOSes.
bttv.card= [HW,V4L] bttv (bt848 + bt878 based grabber cards)
bttv.radio= Most important insmod options are available as
kernel args too.
......@@ -1193,6 +1196,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Address Range Mirroring feature even if your box
doesn't support it.
efivar_ssdt= [EFI; X86] Name of an EFI variable that contains an SSDT
that is to be dynamically loaded by Linux. If there are
multiple variables with the same name but with different
vendor GUIDs, all of them will be loaded. See
Documentation/acpi/ssdt-overlays.txt for details.
eisa_irq_edge= [PARISC,HW]
See header of drivers/parisc/eisa.c.
......
......@@ -288,6 +288,7 @@ F: include/linux/acpi.h
F: include/acpi/
F: Documentation/acpi/
F: Documentation/ABI/testing/sysfs-bus-acpi
F: Documentation/ABI/testing/configfs-acpi
F: drivers/pci/*acpi*
F: drivers/pci/*/*acpi*
F: drivers/pci/*/*/*acpi*
......
......@@ -4,6 +4,7 @@ config ARM64
select ACPI_GENERIC_GSI if ACPI
select ACPI_REDUCED_HARDWARE_ONLY if ACPI
select ARCH_HAS_DEVMEM_IS_ALLOWED
select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_ELF_RANDOMIZE
select ARCH_HAS_GCOV_PROFILE_ALL
......
......@@ -113,4 +113,14 @@ static inline const char *acpi_get_enable_method(int cpu)
pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr);
#endif
#ifdef CONFIG_ACPI_NUMA
int arm64_acpi_numa_init(void);
int acpi_numa_get_nid(unsigned int cpu, u64 hwid);
#else
static inline int arm64_acpi_numa_init(void) { return -ENOSYS; }
static inline int acpi_numa_get_nid(unsigned int cpu, u64 hwid) { return NUMA_NO_NODE; }
#endif /* CONFIG_ACPI_NUMA */
#define ACPI_TABLE_UPGRADE_MAX_PHYS MEMBLOCK_ALLOC_ACCESSIBLE
#endif /*_ASM_ACPI_H*/
......@@ -5,6 +5,8 @@
#ifdef CONFIG_NUMA
#define NR_NODE_MEMBLKS (MAX_NUMNODES * 2)
/* currently, arm64 implements flat NUMA topology */
#define parent_node(node) (node)
......
......@@ -42,6 +42,7 @@ arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o
arm64-obj-$(CONFIG_PCI) += pci.o
arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
arm64-obj-$(CONFIG_ACPI) += acpi.o
arm64-obj-$(CONFIG_ACPI_NUMA) += acpi_numa.o
arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o
arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o
arm64-obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
......
/*
* ACPI 5.1 based NUMA setup for ARM64
* Lots of code was borrowed from arch/x86/mm/srat.c
*
* Copyright 2004 Andi Kleen, SuSE Labs.
* Copyright (C) 2013-2016, Linaro Ltd.
* Author: Hanjun Guo <hanjun.guo@linaro.org>
*
* Reads the ACPI SRAT table to figure out what memory belongs to which CPUs.
*
* Called from acpi_numa_init while reading the SRAT and SLIT tables.
* Assumes all memory regions belonging to a single proximity domain
* are in one chunk. Holes between them will be included in the node.
*/
#define pr_fmt(fmt) "ACPI: NUMA: " fmt
#include <linux/acpi.h>
#include <linux/bitmap.h>
#include <linux/bootmem.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/memblock.h>
#include <linux/mmzone.h>
#include <linux/module.h>
#include <linux/topology.h>
#include <acpi/processor.h>
#include <asm/numa.h>
static int cpus_in_srat;
struct __node_cpu_hwid {
u32 node_id; /* logical node containing this CPU */
u64 cpu_hwid; /* MPIDR for this CPU */
};
static struct __node_cpu_hwid early_node_cpu_hwid[NR_CPUS] = {
[0 ... NR_CPUS - 1] = {NUMA_NO_NODE, PHYS_CPUID_INVALID} };
int acpi_numa_get_nid(unsigned int cpu, u64 hwid)
{
int i;
for (i = 0; i < cpus_in_srat; i++) {
if (hwid == early_node_cpu_hwid[i].cpu_hwid)
return early_node_cpu_hwid[i].node_id;
}
return NUMA_NO_NODE;
}
/* Callback for Proximity Domain -> ACPI processor UID mapping */
void __init acpi_numa_gicc_affinity_init(struct acpi_srat_gicc_affinity *pa)
{
int pxm, node;
phys_cpuid_t mpidr;
if (srat_disabled())
return;
if (pa->header.length < sizeof(struct acpi_srat_gicc_affinity)) {
pr_err("SRAT: Invalid SRAT header length: %d\n",
pa->header.length);
bad_srat();
return;
}
if (!(pa->flags & ACPI_SRAT_GICC_ENABLED))
return;
if (cpus_in_srat >= NR_CPUS) {
pr_warn_once("SRAT: cpu_to_node_map[%d] is too small, may not be able to use all cpus\n",
NR_CPUS);
return;
}
pxm = pa->proximity_domain;
node = acpi_map_pxm_to_node(pxm);
if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
pr_err("SRAT: Too many proximity domains %d\n", pxm);
bad_srat();
return;
}
mpidr = acpi_map_madt_entry(pa->acpi_processor_uid);
if (mpidr == PHYS_CPUID_INVALID) {
pr_err("SRAT: PXM %d with ACPI ID %d has no valid MPIDR in MADT\n",
pxm, pa->acpi_processor_uid);
bad_srat();
return;
}
early_node_cpu_hwid[cpus_in_srat].node_id = node;
early_node_cpu_hwid[cpus_in_srat].cpu_hwid = mpidr;
node_set(node, numa_nodes_parsed);
cpus_in_srat++;
pr_info("SRAT: PXM %d -> MPIDR 0x%Lx -> Node %d\n",
pxm, mpidr, node);
}
int __init arm64_acpi_numa_init(void)
{
int ret;
ret = acpi_numa_init();
if (ret)
return ret;
return srat_disabled() ? -EINVAL : 0;
}
......@@ -9,13 +9,16 @@
* published by the Free Software Foundation.
*/
#include <linux/acpi.h>
#include <linux/cpuidle.h>
#include <linux/cpu_pm.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <asm/cpuidle.h>
#include <asm/cpu_ops.h>
int __init arm_cpuidle_init(unsigned int cpu)
int arm_cpuidle_init(unsigned int cpu)
{
int ret = -EOPNOTSUPP;
......@@ -39,3 +42,18 @@ int arm_cpuidle_suspend(int index)
return cpu_ops[cpu]->cpu_suspend(index);
}
#ifdef CONFIG_ACPI
#include <acpi/processor.h>
int acpi_processor_ffh_lpi_probe(unsigned int cpu)
{
return arm_cpuidle_init(cpu);
}
int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
{
return CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, lpi->index);
}
#endif
......@@ -260,11 +260,13 @@ void __init setup_arch(char **cmdline_p)
efi_init();
arm64_memblock_init();
paging_init();
acpi_table_upgrade();
/* Parse the ACPI tables for possible boot-time configuration */
acpi_boot_table_init();
paging_init();
if (acpi_disabled)
unflatten_device_tree();
......
......@@ -560,6 +560,8 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
*/
acpi_set_mailbox_entry(cpu_count, processor);
early_map_cpu_to_node(cpu_count, acpi_numa_get_nid(cpu_count, hwid));
cpu_count++;
}
......
......@@ -17,6 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/acpi.h>
#include <linux/bootmem.h>
#include <linux/memblock.h>
#include <linux/module.h>
......@@ -29,7 +30,7 @@ static int cpu_to_node_map[NR_CPUS] = { [0 ... NR_CPUS-1] = NUMA_NO_NODE };
static int numa_distance_cnt;
static u8 *numa_distance;
static int numa_off;
static bool numa_off;
static __init int numa_parse_early_param(char *opt)
{
......@@ -37,7 +38,7 @@ static __init int numa_parse_early_param(char *opt)
return -EINVAL;
if (!strncmp(opt, "off", 3)) {
pr_info("%s\n", "NUMA turned off");
numa_off = 1;
numa_off = true;
}
return 0;
}
......@@ -131,25 +132,25 @@ void __init early_map_cpu_to_node(unsigned int cpu, int nid)
* numa_add_memblk - Set node id to memblk
* @nid: NUMA node ID of the new memblk
* @start: Start address of the new memblk
* @size: Size of the new memblk
* @end: End address of the new memblk
*
* RETURNS:
* 0 on success, -errno on failure.
*/
int __init numa_add_memblk(int nid, u64 start, u64 size)
int __init numa_add_memblk(int nid, u64 start, u64 end)
{
int ret;
ret = memblock_set_node(start, size, &memblock.memory, nid);
ret = memblock_set_node(start, (end - start), &memblock.memory, nid);
if (ret < 0) {
pr_err("NUMA: memblock [0x%llx - 0x%llx] failed to add on node %d\n",
start, (start + size - 1), nid);
start, (end - 1), nid);
return ret;
}
node_set(nid, numa_nodes_parsed);
pr_info("NUMA: Adding memblock [0x%llx - 0x%llx] on node %d\n",
start, (start + size - 1), nid);
start, (end - 1), nid);
return ret;
}
......@@ -362,12 +363,15 @@ static int __init dummy_numa_init(void)
int ret;
struct memblock_region *mblk;
pr_info("%s\n", "No NUMA configuration found");
if (numa_off)
pr_info("NUMA disabled\n"); /* Forced off on command line. */
else
pr_info("No NUMA configuration found\n");
pr_info("NUMA: Faking a node at [mem %#018Lx-%#018Lx]\n",
0LLU, PFN_PHYS(max_pfn) - 1);
for_each_memblock(memory, mblk) {
ret = numa_add_memblk(0, mblk->base, mblk->size);
ret = numa_add_memblk(0, mblk->base, mblk->base + mblk->size);
if (!ret)
continue;
......@@ -375,7 +379,7 @@ static int __init dummy_numa_init(void)
return ret;
}
numa_off = 1;
numa_off = true;
return 0;
}
......@@ -388,7 +392,9 @@ static int __init dummy_numa_init(void)
void __init arm64_numa_init(void)
{
if (!numa_off) {
if (!numa_init(of_numa_init))
if (!acpi_disabled && !numa_init(arm64_acpi_numa_init))
return;
if (acpi_disabled && !numa_init(of_numa_init))
return;
}
......
......@@ -140,6 +140,9 @@ static inline void per_cpu_scan_finalize(int min_cpus, int reserve_cpus)
}
}
}
extern void acpi_numa_fixup(void);
#endif /* CONFIG_ACPI_NUMA */
#endif /*__KERNEL__*/
......
......@@ -524,7 +524,7 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
return 0;
}
void __init acpi_numa_arch_fixup(void)
void __init acpi_numa_fixup(void)
{
int i, j, node_from, node_to;
......
......@@ -552,6 +552,7 @@ setup_arch (char **cmdline_p)
early_acpi_boot_init();
# ifdef CONFIG_ACPI_NUMA
acpi_numa_init();
acpi_numa_fixup();
# ifdef CONFIG_ACPI_HOTPLUG_CPU
prefill_possible_map();
# endif
......
......@@ -22,6 +22,7 @@ config X86
select ANON_INODES
select ARCH_CLOCKSOURCE_DATA
select ARCH_DISCARD_MEMBLOCK
select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS
select ARCH_HAS_DEVMEM_IS_ALLOWED
......
......@@ -145,7 +145,6 @@ static inline void disable_acpi(void) { }
#define ARCH_HAS_POWER_INIT 1
#ifdef CONFIG_ACPI_NUMA
extern int acpi_numa;
extern int x86_acpi_numa_init(void);
#endif /* CONFIG_ACPI_NUMA */
......@@ -170,4 +169,6 @@ static inline pgprot_t arch_apei_get_mem_attribute(phys_addr_t addr)
}
#endif
#define ACPI_TABLE_UPGRADE_MAX_PHYS (max_low_pfn_mapped << PAGE_SHIFT)
#endif /* _ASM_X86_ACPI_H */
......@@ -400,10 +400,6 @@ static void __init reserve_initrd(void)
memblock_free(ramdisk_image, ramdisk_end - ramdisk_image);
}
static void __init early_initrd_acpi_init(void)
{
early_acpi_table_init((void *)initrd_start, initrd_end - initrd_start);
}
#else
static void __init early_reserve_initrd(void)
{
......@@ -411,9 +407,6 @@ static void __init early_reserve_initrd(void)
static void __init reserve_initrd(void)
{
}
static void __init early_initrd_acpi_init(void)
{
}
#endif /* CONFIG_BLK_DEV_INITRD */
static void __init parse_setup_data(void)
......@@ -1149,7 +1142,7 @@ void __init setup_arch(char **cmdline_p)
reserve_initrd();
early_initrd_acpi_init();
acpi_table_upgrade();
vsmp_init();
......
/* Common code for 32 and 64-bit NUMA */
#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
......@@ -15,7 +16,6 @@
#include <asm/e820.h>
#include <asm/proto.h>
#include <asm/dma.h>
#include <asm/acpi.h>
#include <asm/amd_nb.h>
#include "numa_internal.h"
......
......@@ -15,8 +15,6 @@
#include <linux/bitmap.h>
#include <linux/module.h>
#include <linux/topology.h>
#include <linux/bootmem.h>
#include <linux/memblock.h>
#include <linux/mm.h>
#include <asm/proto.h>
#include <asm/numa.h>
......@@ -24,51 +22,6 @@
#include <asm/apic.h>
#include <asm/uv/uv.h>
int acpi_numa __initdata;
static __init int setup_node(int pxm)
{
return acpi_map_pxm_to_node(pxm);
}
static __init void bad_srat(void)
{
printk(KERN_ERR "SRAT: SRAT not used.\n");
acpi_numa = -1;
}
static __init inline int srat_disabled(void)
{
return acpi_numa < 0;
}
/*
* Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for
* I/O localities since SRAT does not list them. I/O localities are
* not supported at this point.
*/
void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
{
int i, j;
for (i = 0; i < slit->locality_count; i++) {
const int from_node = pxm_to_node(i);
if (from_node == NUMA_NO_NODE)
continue;
for (j = 0; j < slit->locality_count; j++) {
const int to_node = pxm_to_node(j);
if (to_node == NUMA_NO_NODE)
continue;
numa_set_distance(from_node, to_node,
slit->entry[slit->locality_count * i + j]);
}
}
}
/* Callback for Proximity Domain -> x2APIC mapping */
void __init
acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
......@@ -91,7 +44,7 @@ acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
pxm, apic_id);
return;
}
node = setup_node(pxm);
node = acpi_map_pxm_to_node(pxm);
if (node < 0) {
printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm);
bad_srat();
......@@ -104,7 +57,6 @@ acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
}
set_apicid_to_node(apic_id, node);
node_set(node, numa_nodes_parsed);
acpi_numa = 1;
printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%04x -> Node %u\n",
pxm, apic_id, node);
}
......@@ -127,7 +79,7 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
pxm = pa->proximity_domain_lo;
if (acpi_srat_revision >= 2)
pxm |= *((unsigned int*)pa->proximity_domain_hi) << 8;
node = setup_node(pxm);
node = acpi_map_pxm_to_node(pxm);
if (node < 0) {
printk(KERN_ERR "SRAT: Too many proximity domains %x\n", pxm);
bad_srat();
......@@ -146,74 +98,10 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
set_apicid_to_node(apic_id, node);
node_set(node, numa_nodes_parsed);
acpi_numa = 1;
printk(KERN_INFO "SRAT: PXM %u -> APIC 0x%02x -> Node %u\n",
pxm, apic_id, node);
}
#ifdef CONFIG_MEMORY_HOTPLUG
static inline int save_add_info(void) {return 1;}
#else
static inline int save_add_info(void) {return 0;}
#endif
/* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
int __init
acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
{
u64 start, end;
u32 hotpluggable;
int node, pxm;
if (srat_disabled())
goto out_err;
if (ma->header.length != sizeof(struct acpi_srat_mem_affinity))
goto out_err_bad_srat;
if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0)
goto out_err;
hotpluggable = ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE;
if (hotpluggable && !save_add_info())
goto out_err;
start = ma->base_address;
end = start + ma->length;
pxm = ma->proximity_domain;
if (acpi_srat_revision <= 1)
pxm &= 0xff;
node = setup_node(pxm);
if (node < 0) {
printk(KERN_ERR "SRAT: Too many proximity domains.\n");
goto out_err_bad_srat;
}
if (numa_add_memblk(node, start, end) < 0)
goto out_err_bad_srat;
node_set(node, numa_nodes_parsed);
pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s\n",
node, pxm,
(unsigned long long) start, (unsigned long long) end - 1,
hotpluggable ? " hotplug" : "",
ma->flags & ACPI_SRAT_MEM_NON_VOLATILE ? " non-volatile" : "");
/* Mark hotplug range in memblock. */
if (hotpluggable && memblock_mark_hotplug(start, ma->length))
pr_warn("SRAT: Failed to mark hotplug range [mem %#010Lx-%#010Lx] in memblock\n",
(unsigned long long)start, (unsigned long long)end - 1);
max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1));
return 0;
out_err_bad_srat:
bad_srat();
out_err:
return -1;
}
void __init acpi_numa_arch_fixup(void) {}
int __init x86_acpi_numa_init(void)
{
int ret;
......
......@@ -213,6 +213,10 @@ config ACPI_CPU_FREQ_PSS
bool
select THERMAL
config ACPI_PROCESSOR_CSTATE
def_bool y
depends on IA64 || X86
config ACPI_PROCESSOR_IDLE
bool
select CPU_IDLE
......@@ -234,7 +238,7 @@ config ACPI_CPPC_LIB
config ACPI_PROCESSOR
tristate "Processor"
depends on X86 || IA64 || ARM64
select ACPI_PROCESSOR_IDLE if X86 || IA64
select ACPI_PROCESSOR_IDLE
select ACPI_CPU_FREQ_PSS if X86 || IA64
default y
help
......@@ -291,8 +295,8 @@ config ACPI_THERMAL
config ACPI_NUMA
bool "NUMA support"
depends on NUMA
depends on (X86 || IA64)
default y if IA64_GENERIC || IA64_SGI_SN2
depends on (X86 || IA64 || ARM64)
default y if IA64_GENERIC || IA64_SGI_SN2 || ARM64
config ACPI_CUSTOM_DSDT_FILE
string "Custom DSDT Table file to include"
......@@ -311,9 +315,12 @@ config ACPI_CUSTOM_DSDT
bool
default ACPI_CUSTOM_DSDT_FILE != ""
config ARCH_HAS_ACPI_TABLE_UPGRADE
def_bool n
config ACPI_TABLE_UPGRADE
bool "Allow upgrading ACPI tables via initrd"
depends on BLK_DEV_INITRD && X86
depends on BLK_DEV_INITRD && ARCH_HAS_ACPI_TABLE_UPGRADE
default y
help
This option provides functionality to upgrade arbitrary ACPI tables
......@@ -475,6 +482,7 @@ config ACPI_NFIT_DEBUG
issue.
source "drivers/acpi/apei/Kconfig"
source "drivers/acpi/dptf/Kconfig"
config ACPI_EXTLOG
tristate "Extended Error Log support"
......@@ -519,6 +527,20 @@ config XPOWER_PMIC_OPREGION
help
This config adds ACPI operation region support for XPower AXP288 PMIC.
config BXT_WC_PMIC_OPREGION
bool "ACPI operation region support for BXT WhiskeyCove PMIC"
depends on INTEL_SOC_PMIC
help
This config adds ACPI operation region support for BXT WhiskeyCove PMIC.
endif
config ACPI_CONFIGFS
tristate "ACPI configfs support"
select CONFIGFS_FS
help
Select this option to enable support for ACPI configuration from
userspace. The configurable ACPI groups will be visible under
/config/acpi, assuming configfs is mounted under /config.
endif # ACPI
......@@ -44,7 +44,6 @@ acpi-y += acpi_lpss.o acpi_apd.o
acpi-y += acpi_platform.o
acpi-y += acpi_pnp.o
acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o
acpi-y += int340x_thermal.o
acpi-y += power.o
acpi-y += event.o
acpi-$(CONFIG_ACPI_REDUCED_HARDWARE_ONLY) += evged.o
......@@ -99,5 +98,9 @@ obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o
obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o
obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o
obj-$(CONFIG_BXT_WC_PMIC_OPREGION) += pmic/intel_pmic_bxtwc.o
obj-$(CONFIG_ACPI_CONFIGFS) += acpi_configfs.o
video-objs += acpi_video.o video_detect.o
obj-y += dptf/
/*
* ACPI configfs support
*
* Copyright (c) 2016 Intel Corporation
*
* 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 configfs: " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/configfs.h>
#include <linux/acpi.h>
static struct config_group *acpi_table_group;
struct acpi_table {
struct config_item cfg;
struct acpi_table_header *header;
};
static ssize_t acpi_table_aml_write(struct config_item *cfg,
const void *data, size_t size)
{
const struct acpi_table_header *header = data;
struct acpi_table *table;
int ret;
table = container_of(cfg, struct acpi_table, cfg);
if (table->header) {
pr_err("table already loaded\n");
return -EBUSY;
}
if (header->length != size) {
pr_err("invalid table length\n");
return -EINVAL;
}
if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) {
pr_err("invalid table signature\n");
return -EINVAL;
}
table = container_of(cfg, struct acpi_table, cfg);
table->header = kmemdup(header, header->length, GFP_KERNEL);
if (!table->header)
return -ENOMEM;
ret = acpi_load_table(table->header);
if (ret) {
kfree(table->header);
table->header = NULL;
}
return ret;
}
static inline struct acpi_table_header *get_header(struct config_item *cfg)
{
struct acpi_table *table = container_of(cfg, struct acpi_table, cfg);
if (!table->header)
pr_err("table not loaded\n");
return table->header;
}
static ssize_t acpi_table_aml_read(struct config_item *cfg,
void *data, size_t size)
{
struct acpi_table_header *h = get_header(cfg);
if (!h)
return -EINVAL;
if (data)
memcpy(data, h, h->length);
return h->length;
}
#define MAX_ACPI_TABLE_SIZE (128 * 1024)
CONFIGFS_BIN_ATTR(acpi_table_, aml, NULL, MAX_ACPI_TABLE_SIZE);
struct configfs_bin_attribute *acpi_table_bin_attrs[] = {
&acpi_table_attr_aml,
NULL,
};
ssize_t acpi_table_signature_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
if (!h)
return -EINVAL;
return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->signature);
}
ssize_t acpi_table_length_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
if (!h)
return -EINVAL;
return sprintf(str, "%d\n", h->length);
}
ssize_t acpi_table_revision_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
if (!h)
return -EINVAL;
return sprintf(str, "%d\n", h->revision);
}
ssize_t acpi_table_oem_id_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
if (!h)
return -EINVAL;
return sprintf(str, "%.*s\n", ACPI_OEM_ID_SIZE, h->oem_id);
}
ssize_t acpi_table_oem_table_id_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
if (!h)
return -EINVAL;
return sprintf(str, "%.*s\n", ACPI_OEM_TABLE_ID_SIZE, h->oem_table_id);
}
ssize_t acpi_table_oem_revision_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
if (!h)
return -EINVAL;
return sprintf(str, "%d\n", h->oem_revision);
}
ssize_t acpi_table_asl_compiler_id_show(struct config_item *cfg, char *str)
{
struct acpi_table_header *h = get_header(cfg);
if (!h)
return -EINVAL;
return sprintf(str, "%.*s\n", ACPI_NAME_SIZE, h->asl_compiler_id);
}
ssize_t acpi_table_asl_compiler_revision_show(struct config_item *cfg,
char *str)
{
struct acpi_table_header *h = get_header(cfg);
if (!h)
return -EINVAL;
return sprintf(str, "%d\n", h->asl_compiler_revision);
}
CONFIGFS_ATTR_RO(acpi_table_, signature);
CONFIGFS_ATTR_RO(acpi_table_, length);
CONFIGFS_ATTR_RO(acpi_table_, revision);
CONFIGFS_ATTR_RO(acpi_table_, oem_id);
CONFIGFS_ATTR_RO(acpi_table_, oem_table_id);
CONFIGFS_ATTR_RO(acpi_table_, oem_revision);
CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_id);
CONFIGFS_ATTR_RO(acpi_table_, asl_compiler_revision);
struct configfs_attribute *acpi_table_attrs[] = {
&acpi_table_attr_signature,
&acpi_table_attr_length,
&acpi_table_attr_revision,
&acpi_table_attr_oem_id,
&acpi_table_attr_oem_table_id,
&acpi_table_attr_oem_revision,
&acpi_table_attr_asl_compiler_id,
&acpi_table_attr_asl_compiler_revision,
NULL,
};
static struct config_item_type acpi_table_type = {
.ct_owner = THIS_MODULE,
.ct_bin_attrs = acpi_table_bin_attrs,
.ct_attrs = acpi_table_attrs,
};
static struct config_item *acpi_table_make_item(struct config_group *group,
const char *name)
{
struct acpi_table *table;
table = kzalloc(sizeof(*table), GFP_KERNEL);
if (!table)
return ERR_PTR(-ENOMEM);
config_item_init_type_name(&table->cfg, name, &acpi_table_type);
return &table->cfg;
}
struct configfs_group_operations acpi_table_group_ops = {
.make_item = acpi_table_make_item,
};
static struct config_item_type acpi_tables_type = {
.ct_owner = THIS_MODULE,
.ct_group_ops = &acpi_table_group_ops,
};
static struct config_item_type acpi_root_group_type = {
.ct_owner = THIS_MODULE,
};
static struct configfs_subsystem acpi_configfs = {
.su_group = {
.cg_item = {
.ci_namebuf = "acpi",
.ci_type = &acpi_root_group_type,
},
},
.su_mutex = __MUTEX_INITIALIZER(acpi_configfs.su_mutex),
};
static int __init acpi_configfs_init(void)
{
int ret;
struct config_group *root = &acpi_configfs.su_group;
config_group_init(root);
ret = configfs_register_subsystem(&acpi_configfs);
if (ret)
return ret;
acpi_table_group = configfs_register_default_group(root, "table",
&acpi_tables_type);
return PTR_ERR_OR_ZERO(acpi_table_group);
}
module_init(acpi_configfs_init);
static void __exit acpi_configfs_exit(void)
{
configfs_unregister_default_group(acpi_table_group);
configfs_unregister_subsystem(&acpi_configfs);
}
module_exit(acpi_configfs_exit);
MODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>");
MODULE_DESCRIPTION("ACPI configfs support");
MODULE_LICENSE("GPL v2");
......@@ -13,7 +13,7 @@
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/export.h>
#include <linux/acpi.h>
#include <acpi/acpi_lpat.h>
......@@ -157,5 +157,3 @@ void acpi_lpat_free_conversion_table(struct acpi_lpat_conversion_table
}
}
EXPORT_SYMBOL_GPL(acpi_lpat_free_conversion_table);
MODULE_LICENSE("GPL");
......@@ -1246,6 +1246,9 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video)
union acpi_object *dod = NULL;
union acpi_object *obj;
if (!video->cap._DOD)
return AE_NOT_EXIST;
status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer);
if (!ACPI_SUCCESS(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD"));
......
......@@ -3,4 +3,4 @@ obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o
obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o
obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o
apei-y := apei-base.o hest.o erst.o
apei-y := apei-base.o hest.o erst.o bert.o
/*
* apei-internal.h - ACPI Platform Error Interface internal
* definations.
* definitions.
*/
#ifndef APEI_INTERNAL_H
......
/*
* APEI Boot Error Record Table (BERT) support
*
* Copyright 2011 Intel Corp.
* Author: Huang Ying <ying.huang@intel.com>
*
* Under normal circumstances, when a hardware error occurs, the error
* handler receives control and processes the error. This gives OSPM a
* chance to process the error condition, report it, and optionally attempt
* recovery. In some cases, the system is unable to process an error.
* For example, system firmware or a management controller may choose to
* reset the system or the system might experience an uncontrolled crash
* or reset.The boot error source is used to report unhandled errors that
* occurred in a previous boot. This mechanism is described in the BERT
* table.
*
* For more information about BERT, please refer to ACPI Specification
* version 4.0, section 17.3.1
*
* This file is licensed under GPLv2.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/io.h>
#include "apei-internal.h"
#undef pr_fmt
#define pr_fmt(fmt) "BERT: " fmt
static int bert_disable;
static void __init bert_print_all(struct acpi_bert_region *region,
unsigned int region_len)
{
struct acpi_hest_generic_status *estatus =
(struct acpi_hest_generic_status *)region;
int remain = region_len;
u32 estatus_len;
if (!estatus->block_status)
return;
while (remain > sizeof(struct acpi_bert_region)) {
if (cper_estatus_check(estatus)) {
pr_err(FW_BUG "Invalid error record.\n");
return;
}
estatus_len = cper_estatus_len(estatus);
if (remain < estatus_len) {
pr_err(FW_BUG "Truncated status block (length: %u).\n",
estatus_len);
return;
}
pr_info_once("Error records from previous boot:\n");
cper_estatus_print(KERN_INFO HW_ERR, estatus);
/*
* Because the boot error source is "one-time polled" type,
* clear Block Status of current Generic Error Status Block,
* once it's printed.
*/
estatus->block_status = 0;
estatus = (void *)estatus + estatus_len;
/* No more error records. */
if (!estatus->block_status)
return;
remain -= estatus_len;
}
}
static int __init setup_bert_disable(char *str)
{
bert_disable = 1;
return 0;
}
__setup("bert_disable", setup_bert_disable);
static int __init bert_check_table(struct acpi_table_bert *bert_tab)
{
if (bert_tab->header.length < sizeof(struct acpi_table_bert) ||
bert_tab->region_length < sizeof(struct acpi_bert_region))
return -EINVAL;
return 0;
}
static int __init bert_init(void)
{
struct acpi_bert_region *boot_error_region;
struct acpi_table_bert *bert_tab;
unsigned int region_len;
acpi_status status;
int rc = 0;
if (acpi_disabled)
return 0;
if (bert_disable) {
pr_info("Boot Error Record Table support is disabled.\n");
return 0;
}
status = acpi_get_table(ACPI_SIG_BERT, 0, (struct acpi_table_header **)&bert_tab);
if (status == AE_NOT_FOUND)
return 0;
if (ACPI_FAILURE(status)) {
pr_err("get table failed, %s.\n", acpi_format_exception(status));
return -EINVAL;
}
rc = bert_check_table(bert_tab);
if (rc) {
pr_err(FW_BUG "table invalid.\n");
return rc;
}
region_len = bert_tab->region_length;
if (!request_mem_region(bert_tab->address, region_len, "APEI BERT")) {
pr_err("Can't request iomem region <%016llx-%016llx>.\n",
(unsigned long long)bert_tab->address,
(unsigned long long)bert_tab->address + region_len - 1);
return -EIO;
}
boot_error_region = ioremap_cache(bert_tab->address, region_len);
if (boot_error_region) {
bert_print_all(boot_error_region, region_len);
iounmap(boot_error_region);
} else {
rc = -ENOMEM;
}
release_mem_region(bert_tab->address, region_len);
return rc;
}
late_initcall(bert_init);
......@@ -33,7 +33,8 @@
#include "apei-internal.h"
#define EINJ_PFX "EINJ: "
#undef pr_fmt
#define pr_fmt(fmt) "EINJ: " fmt
#define SPIN_UNIT 100 /* 100ns */
/* Firmware should respond within 1 milliseconds */
......@@ -179,8 +180,7 @@ static int einj_get_available_error_type(u32 *type)
static int einj_timedout(u64 *t)
{
if ((s64)*t < SPIN_UNIT) {
pr_warning(FW_WARN EINJ_PFX
"Firmware does not respond in time\n");
pr_warning(FW_WARN "Firmware does not respond in time\n");
return 1;
}
*t -= SPIN_UNIT;
......@@ -307,8 +307,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
r = request_mem_region(trigger_paddr, sizeof(*trigger_tab),
"APEI EINJ Trigger Table");
if (!r) {
pr_err(EINJ_PFX
"Can not request [mem %#010llx-%#010llx] for Trigger table\n",
pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n",
(unsigned long long)trigger_paddr,
(unsigned long long)trigger_paddr +
sizeof(*trigger_tab) - 1);
......@@ -316,13 +315,12 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
}
trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab));
if (!trigger_tab) {
pr_err(EINJ_PFX "Failed to map trigger table!\n");
pr_err("Failed to map trigger table!\n");
goto out_rel_header;
}
rc = einj_check_trigger_header(trigger_tab);
if (rc) {
pr_warning(FW_BUG EINJ_PFX
"The trigger error action table is invalid\n");
pr_warning(FW_BUG "Invalid trigger error action table.\n");
goto out_rel_header;
}
......@@ -336,8 +334,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
table_size - sizeof(*trigger_tab),
"APEI EINJ Trigger Table");
if (!r) {
pr_err(EINJ_PFX
"Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n",
pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n",
(unsigned long long)trigger_paddr + sizeof(*trigger_tab),
(unsigned long long)trigger_paddr + table_size - 1);
goto out_rel_header;
......@@ -345,7 +342,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
iounmap(trigger_tab);
trigger_tab = ioremap_cache(trigger_paddr, table_size);
if (!trigger_tab) {
pr_err(EINJ_PFX "Failed to map trigger table!\n");
pr_err("Failed to map trigger table!\n");
goto out_rel_entry;
}
trigger_entry = (struct acpi_whea_header *)
......@@ -695,34 +692,42 @@ static int __init einj_init(void)
struct dentry *fentry;
struct apei_exec_context ctx;
if (acpi_disabled)
if (acpi_disabled) {
pr_warn("ACPI disabled.\n");
return -ENODEV;
}
status = acpi_get_table(ACPI_SIG_EINJ, 0,
(struct acpi_table_header **)&einj_tab);
if (status == AE_NOT_FOUND)
if (status == AE_NOT_FOUND) {
pr_warn("EINJ table not found.\n");
return -ENODEV;
}
else if (ACPI_FAILURE(status)) {
const char *msg = acpi_format_exception(status);
pr_err(EINJ_PFX "Failed to get table, %s\n", msg);
pr_err("Failed to get EINJ table: %s\n",
acpi_format_exception(status));
return -EINVAL;
}
rc = einj_check_table(einj_tab);
if (rc) {
pr_warning(FW_BUG EINJ_PFX "EINJ table is invalid\n");
pr_warn(FW_BUG "Invalid EINJ table.n");
return -EINVAL;
}
rc = -ENOMEM;
einj_debug_dir = debugfs_create_dir("einj", apei_get_debugfs_dir());
if (!einj_debug_dir)
if (!einj_debug_dir) {
pr_err("Error creating debugfs node.\n");
goto err_cleanup;
}
fentry = debugfs_create_file("available_error_type", S_IRUSR,
einj_debug_dir, NULL,
&available_error_type_fops);
if (!fentry)
goto err_cleanup;
fentry = debugfs_create_file("error_type", S_IRUSR | S_IWUSR,
einj_debug_dir, NULL, &error_type_fops);
if (!fentry)
......@@ -735,14 +740,22 @@ static int __init einj_init(void)
apei_resources_init(&einj_resources);
einj_exec_ctx_init(&ctx);
rc = apei_exec_collect_resources(&ctx, &einj_resources);
if (rc)
if (rc) {
pr_err("Error collecting EINJ resources.\n");
goto err_fini;
}
rc = apei_resources_request(&einj_resources, "APEI EINJ");
if (rc)
if (rc) {
pr_err("Error requesting memory/port resources.\n");
goto err_fini;
}
rc = apei_exec_pre_map_gars(&ctx);
if (rc)
if (rc) {
pr_err("Error pre-mapping GARs.\n");
goto err_release;
}
rc = -ENOMEM;
einj_param = einj_get_parameter_address();
......@@ -787,7 +800,7 @@ static int __init einj_init(void)
goto err_unmap;
}
pr_info(EINJ_PFX "Error INJection is initialized.\n");
pr_info("Error INJection is initialized.\n");
return 0;
......@@ -798,6 +811,7 @@ static int __init einj_init(void)
sizeof(struct einj_parameter);
acpi_os_unmap_iomem(einj_param, size);
pr_err("Error creating param extension debugfs nodes.\n");
}
apei_exec_post_unmap_gars(&ctx);
err_release:
......@@ -805,6 +819,7 @@ static int __init einj_init(void)
err_fini:
apei_resources_fini(&einj_resources);
err_cleanup:
pr_err("Error creating primary debugfs nodes.\n");
debugfs_remove_recursive(einj_debug_dir);
return rc;
......
......@@ -30,6 +30,9 @@
#include <linux/acpi.h>
#include <linux/slab.h>
#include <linux/regulator/machine.h>
#include <linux/workqueue.h>
#include <linux/reboot.h>
#include <linux/delay.h>
#ifdef CONFIG_X86
#include <asm/mpspec.h>
#endif
......@@ -174,22 +177,17 @@ void acpi_bus_detach_private_data(acpi_handle handle)
EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data);
static void acpi_print_osc_error(acpi_handle handle,
struct acpi_osc_context *context, char *error)
struct acpi_osc_context *context, char *error)
{
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
int i;
if (ACPI_FAILURE(acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer)))
printk(KERN_DEBUG "%s: %s\n", context->uuid_str, error);
else {
printk(KERN_DEBUG "%s (%s): %s\n",
(char *)buffer.pointer, context->uuid_str, error);
kfree(buffer.pointer);
}
printk(KERN_DEBUG "_OSC request data:");
acpi_handle_debug(handle, "(%s): %s\n", context->uuid_str, error);
pr_debug("_OSC request data:");
for (i = 0; i < context->cap.length; i += sizeof(u32))
printk(" %x", *((u32 *)(context->cap.pointer + i)));
printk("\n");
pr_debug(" %x", *((u32 *)(context->cap.pointer + i)));
pr_debug("\n");
}
acpi_status acpi_str_to_uuid(char *str, u8 *uuid)
......@@ -302,6 +300,14 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context)
EXPORT_SYMBOL(acpi_run_osc);
bool osc_sb_apei_support_acked;
/*
* ACPI 6.0 Section 8.4.4.2 Idle State Coordination
* OSPM supports platform coordinated low power idle(LPI) states
*/
bool osc_pc_lpi_support_confirmed;
EXPORT_SYMBOL_GPL(osc_pc_lpi_support_confirmed);
static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48";
static void acpi_bus_osc_support(void)
{
......@@ -322,6 +328,7 @@ static void acpi_bus_osc_support(void)
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT;
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT;
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT;
if (!ghes_disable)
capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT;
......@@ -329,9 +336,12 @@ static void acpi_bus_osc_support(void)
return;
if (ACPI_SUCCESS(acpi_run_osc(handle, &context))) {
u32 *capbuf_ret = context.ret.pointer;
if (context.ret.length > OSC_SUPPORT_DWORD)
if (context.ret.length > OSC_SUPPORT_DWORD) {
osc_sb_apei_support_acked =
capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT;
osc_pc_lpi_support_confirmed =
capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT;
}
kfree(context.ret.pointer);
}
/* do we need to check other returned cap? Sounds no */
......@@ -475,6 +485,56 @@ static void acpi_device_remove_notify_handler(struct acpi_device *device)
acpi_device_notify);
}
/* Handle events targeting \_SB device (at present only graceful shutdown) */
#define ACPI_SB_NOTIFY_SHUTDOWN_REQUEST 0x81
#define ACPI_SB_INDICATE_INTERVAL 10000
static void sb_notify_work(struct work_struct *dummy)
{
acpi_handle sb_handle;
orderly_poweroff(true);
/*
* After initiating graceful shutdown, the ACPI spec requires OSPM
* to evaluate _OST method once every 10seconds to indicate that
* the shutdown is in progress
*/
acpi_get_handle(NULL, "\\_SB", &sb_handle);
while (1) {
pr_info("Graceful shutdown in progress.\n");
acpi_evaluate_ost(sb_handle, ACPI_OST_EC_OSPM_SHUTDOWN,
ACPI_OST_SC_OS_SHUTDOWN_IN_PROGRESS, NULL);
msleep(ACPI_SB_INDICATE_INTERVAL);
}
}
static void acpi_sb_notify(acpi_handle handle, u32 event, void *data)
{
static DECLARE_WORK(acpi_sb_work, sb_notify_work);
if (event == ACPI_SB_NOTIFY_SHUTDOWN_REQUEST) {
if (!work_busy(&acpi_sb_work))
schedule_work(&acpi_sb_work);
} else
pr_warn("event %x is not supported by \\_SB device\n", event);
}
static int __init acpi_setup_sb_notify_handler(void)
{
acpi_handle sb_handle;
if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &sb_handle)))
return -ENXIO;
if (ACPI_FAILURE(acpi_install_notify_handler(sb_handle, ACPI_DEVICE_NOTIFY,
acpi_sb_notify, NULL)))
return -EINVAL;
return 0;
}
/* --------------------------------------------------------------------------
Device Matching
-------------------------------------------------------------------------- */
......@@ -961,8 +1021,7 @@ void __init acpi_early_init(void)
/**
* acpi_subsystem_init - Finalize the early initialization of ACPI.
*
* Switch over the platform to the ACPI mode (if possible), initialize the
* handling of ACPI events, install the interrupt and global lock handlers.
* Switch over the platform to the ACPI mode (if possible).
*
* Doing this too early is generally unsafe, but at the same time it needs to be
* done before all things that really depend on ACPI. The right spot appears to
......@@ -990,6 +1049,13 @@ void __init acpi_subsystem_init(void)
}
}
static acpi_status acpi_bus_table_handler(u32 event, void *table, void *context)
{
acpi_scan_table_handler(event, table, context);
return acpi_sysfs_table_handler(event, table, context);
}
static int __init acpi_bus_init(void)
{
int result;
......@@ -1043,6 +1109,8 @@ static int __init acpi_bus_init(void)
* _PDC control method may load dynamic SSDT tables,
* and we need to install the table handler before that.
*/
status = acpi_install_table_handler(acpi_bus_table_handler, NULL);
acpi_sysfs_init();
acpi_early_processor_set_pdc();
......@@ -1124,6 +1192,7 @@ static int __init acpi_init(void)
acpi_sleep_proc_init();
acpi_wakeup_device_init();
acpi_debugger_init();
acpi_setup_sb_notify_handler();
return 0;
}
......
......@@ -53,6 +53,10 @@
#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
#define ACPI_BUTTON_TYPE_LID 0x05
#define ACPI_BUTTON_LID_INIT_IGNORE 0x00
#define ACPI_BUTTON_LID_INIT_OPEN 0x01
#define ACPI_BUTTON_LID_INIT_METHOD 0x02
#define _COMPONENT ACPI_BUTTON_COMPONENT
ACPI_MODULE_NAME("button");
......@@ -105,6 +109,7 @@ struct acpi_button {
static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
static struct acpi_device *lid_device;
static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
/* --------------------------------------------------------------------------
FS Interface (/proc)
......@@ -113,16 +118,52 @@ static struct acpi_device *lid_device;
static struct proc_dir_entry *acpi_button_dir;
static struct proc_dir_entry *acpi_lid_dir;
static int acpi_lid_evaluate_state(struct acpi_device *device)
{
unsigned long long lid_state;
acpi_status status;
status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state);
if (ACPI_FAILURE(status))
return -ENODEV;
return lid_state ? 1 : 0;
}
static int acpi_lid_notify_state(struct acpi_device *device, int state)
{
struct acpi_button *button = acpi_driver_data(device);
int ret;
/* input layer checks if event is redundant */
input_report_switch(button->input, SW_LID, !state);
input_sync(button->input);
if (state)
pm_wakeup_event(&device->dev, 0);
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
if (ret == NOTIFY_DONE)
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
device);
if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
/*
* It is also regarded as success if the notifier_chain
* returns NOTIFY_OK or NOTIFY_DONE.
*/
ret = 0;
}
return ret;
}
static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
{
struct acpi_device *device = seq->private;
acpi_status status;
unsigned long long state;
int state;
status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state);
state = acpi_lid_evaluate_state(device);
seq_printf(seq, "state: %s\n",
ACPI_FAILURE(status) ? "unsupported" :
(state ? "open" : "closed"));
state < 0 ? "unsupported" : (state ? "open" : "closed"));
return 0;
}
......@@ -231,51 +272,37 @@ EXPORT_SYMBOL(acpi_lid_notifier_unregister);
int acpi_lid_open(void)
{
acpi_status status;
unsigned long long state;
if (!lid_device)
return -ENODEV;
status = acpi_evaluate_integer(lid_device->handle, "_LID", NULL,
&state);
if (ACPI_FAILURE(status))
return -ENODEV;
return !!state;
return acpi_lid_evaluate_state(lid_device);
}
EXPORT_SYMBOL(acpi_lid_open);
static int acpi_lid_send_state(struct acpi_device *device)
static int acpi_lid_update_state(struct acpi_device *device)
{
struct acpi_button *button = acpi_driver_data(device);
unsigned long long state;
acpi_status status;
int ret;
status = acpi_evaluate_integer(device->handle, "_LID", NULL, &state);
if (ACPI_FAILURE(status))
return -ENODEV;
int state;
/* input layer checks if event is redundant */
input_report_switch(button->input, SW_LID, !state);
input_sync(button->input);
state = acpi_lid_evaluate_state(device);
if (state < 0)
return state;
if (state)
pm_wakeup_event(&device->dev, 0);
return acpi_lid_notify_state(device, state);
}
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
if (ret == NOTIFY_DONE)
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
device);
if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
/*
* It is also regarded as success if the notifier_chain
* returns NOTIFY_OK or NOTIFY_DONE.
*/
ret = 0;
static void acpi_lid_initialize_state(struct acpi_device *device)
{
switch (lid_init_state) {
case ACPI_BUTTON_LID_INIT_OPEN:
(void)acpi_lid_notify_state(device, 1);
break;
case ACPI_BUTTON_LID_INIT_METHOD:
(void)acpi_lid_update_state(device);
break;
case ACPI_BUTTON_LID_INIT_IGNORE:
default:
break;
}
return ret;
}
static void acpi_button_notify(struct acpi_device *device, u32 event)
......@@ -290,7 +317,7 @@ static void acpi_button_notify(struct acpi_device *device, u32 event)
case ACPI_BUTTON_NOTIFY_STATUS:
input = button->input;
if (button->type == ACPI_BUTTON_TYPE_LID) {
acpi_lid_send_state(device);
acpi_lid_update_state(device);
} else {
int keycode;
......@@ -335,7 +362,7 @@ static int acpi_button_resume(struct device *dev)
button->suspended = false;
if (button->type == ACPI_BUTTON_TYPE_LID)
return acpi_lid_send_state(device);
acpi_lid_initialize_state(device);
return 0;
}
#endif
......@@ -416,7 +443,7 @@ static int acpi_button_add(struct acpi_device *device)
if (error)
goto err_remove_fs;
if (button->type == ACPI_BUTTON_TYPE_LID) {
acpi_lid_send_state(device);
acpi_lid_initialize_state(device);
/*
* This assumes there's only one lid device, or if there are
* more we only care about the last one...
......@@ -446,4 +473,42 @@ static int acpi_button_remove(struct acpi_device *device)
return 0;
}
static int param_set_lid_init_state(const char *val, struct kernel_param *kp)
{
int result = 0;
if (!strncmp(val, "open", sizeof("open") - 1)) {
lid_init_state = ACPI_BUTTON_LID_INIT_OPEN;
pr_info("Notify initial lid state as open\n");
} else if (!strncmp(val, "method", sizeof("method") - 1)) {
lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
pr_info("Notify initial lid state with _LID return value\n");
} else if (!strncmp(val, "ignore", sizeof("ignore") - 1)) {
lid_init_state = ACPI_BUTTON_LID_INIT_IGNORE;
pr_info("Do not notify initial lid state\n");
} else
result = -EINVAL;
return result;
}
static int param_get_lid_init_state(char *buffer, struct kernel_param *kp)
{
switch (lid_init_state) {
case ACPI_BUTTON_LID_INIT_OPEN:
return sprintf(buffer, "open");
case ACPI_BUTTON_LID_INIT_METHOD:
return sprintf(buffer, "method");
case ACPI_BUTTON_LID_INIT_IGNORE:
return sprintf(buffer, "ignore");
default:
return sprintf(buffer, "invalid");
}
return 0;
}
module_param_call(lid_init_state,
param_set_lid_init_state, param_get_lid_init_state,
NULL, 0644);
MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");
module_acpi_driver(acpi_button_driver);
......@@ -299,8 +299,10 @@ int acpi_get_psd_map(struct cpudata **all_cpu_data)
continue;
cpc_ptr = per_cpu(cpc_desc_ptr, i);
if (!cpc_ptr)
continue;
if (!cpc_ptr) {
retval = -EFAULT;
goto err_ret;
}
pdomain = &(cpc_ptr->domain_info);
cpumask_set_cpu(i, pr->shared_cpu_map);
......@@ -322,8 +324,10 @@ int acpi_get_psd_map(struct cpudata **all_cpu_data)
continue;
match_cpc_ptr = per_cpu(cpc_desc_ptr, j);
if (!match_cpc_ptr)
continue;
if (!match_cpc_ptr) {
retval = -EFAULT;
goto err_ret;
}
match_pdomain = &(match_cpc_ptr->domain_info);
if (match_pdomain->domain != pdomain->domain)
......@@ -353,8 +357,10 @@ int acpi_get_psd_map(struct cpudata **all_cpu_data)
continue;
match_cpc_ptr = per_cpu(cpc_desc_ptr, j);
if (!match_cpc_ptr)
continue;
if (!match_cpc_ptr) {
retval = -EFAULT;
goto err_ret;
}
match_pdomain = &(match_cpc_ptr->domain_info);
if (match_pdomain->domain != pdomain->domain)
......@@ -595,9 +601,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
/* Store CPU Logical ID */
cpc_ptr->cpu_id = pr->id;
/* Plug it into this CPUs CPC descriptor. */
per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr;
/* Parse PSD data for this CPU */
ret = acpi_get_psd(cpc_ptr, handle);
if (ret)
......@@ -610,6 +613,9 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
goto out_free;
}
/* Plug PSD data into this CPUs CPC descriptor. */
per_cpu(cpc_desc_ptr, pr->id) = cpc_ptr;
/* Everything looks okay */
pr_debug("Parsed CPC struct for CPU: %d\n", pr->id);
......
......@@ -21,7 +21,7 @@
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/types.h>
......@@ -33,12 +33,7 @@
#include "internal.h"
#define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver"
ACPI_MODULE_NAME("dock");
MODULE_AUTHOR("Kristen Carlson Accardi");
MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION);
MODULE_LICENSE("GPL");
static bool immediate_undock = 1;
module_param(immediate_undock, bool, 0644);
......
config DPTF_POWER
tristate "DPTF Platform Power Participant"
depends on X86
help
This driver adds support for Dynamic Platform and Thermal Framework
(DPTF) Platform Power Participant device (INT3407) support.
This participant is responsible for exposing platform telemetry:
max_platform_power
platform_power_source
adapter_rating
battery_steady_power
charger_type
To compile this driver as a module, choose M here:
the module will be called dptf_power.
obj-$(CONFIG_ACPI) += int340x_thermal.o
obj-$(CONFIG_DPTF_POWER) += dptf_power.o
ccflags-y += -Idrivers/acpi
/*
* dptf_power: DPTF platform power driver
* 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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
/*
* Presentation of attributes which are defined for INT3407. They are:
* PMAX : Maximum platform powe
* PSRC : Platform power source
* ARTG : Adapter rating
* CTYP : Charger type
* PBSS : Battery steady power
*/
#define DPTF_POWER_SHOW(name, object) \
static ssize_t name##_show(struct device *dev,\
struct device_attribute *attr,\
char *buf)\
{\
struct platform_device *pdev = to_platform_device(dev);\
struct acpi_device *acpi_dev = platform_get_drvdata(pdev);\
unsigned long long val;\
acpi_status status;\
\
status = acpi_evaluate_integer(acpi_dev->handle, #object,\
NULL, &val);\
if (ACPI_SUCCESS(status))\
return sprintf(buf, "%d\n", (int)val);\
else \
return -EINVAL;\
}
DPTF_POWER_SHOW(max_platform_power_mw, PMAX)
DPTF_POWER_SHOW(platform_power_source, PSRC)
DPTF_POWER_SHOW(adapter_rating_mw, ARTG)
DPTF_POWER_SHOW(battery_steady_power_mw, PBSS)
DPTF_POWER_SHOW(charger_type, CTYP)
static DEVICE_ATTR_RO(max_platform_power_mw);
static DEVICE_ATTR_RO(platform_power_source);
static DEVICE_ATTR_RO(adapter_rating_mw);
static DEVICE_ATTR_RO(battery_steady_power_mw);
static DEVICE_ATTR_RO(charger_type);
static struct attribute *dptf_power_attrs[] = {
&dev_attr_max_platform_power_mw.attr,
&dev_attr_platform_power_source.attr,
&dev_attr_adapter_rating_mw.attr,
&dev_attr_battery_steady_power_mw.attr,
&dev_attr_charger_type.attr,
NULL
};
static struct attribute_group dptf_power_attribute_group = {
.attrs = dptf_power_attrs,
.name = "dptf_power"
};
static int dptf_power_add(struct platform_device *pdev)
{
struct acpi_device *acpi_dev;
acpi_status status;
unsigned long long ptype;
int result;
acpi_dev = ACPI_COMPANION(&(pdev->dev));
if (!acpi_dev)
return -ENODEV;
status = acpi_evaluate_integer(acpi_dev->handle, "PTYP", NULL, &ptype);
if (ACPI_FAILURE(status))
return -ENODEV;
if (ptype != 0x11)
return -ENODEV;
result = sysfs_create_group(&pdev->dev.kobj,
&dptf_power_attribute_group);
if (result)
return result;
platform_set_drvdata(pdev, acpi_dev);
return 0;
}
static int dptf_power_remove(struct platform_device *pdev)
{
sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group);
return 0;
}
static const struct acpi_device_id int3407_device_ids[] = {
{"INT3407", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, int3407_device_ids);
static struct platform_driver dptf_power_driver = {
.probe = dptf_power_add,
.remove = dptf_power_remove,
.driver = {
.name = "DPTF Platform Power",
.acpi_match_table = int3407_device_ids,
},
};
module_platform_driver(dptf_power_driver);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ACPI DPTF platform power driver");
......@@ -1359,13 +1359,9 @@ static void ec_remove_handlers(struct acpi_ec *ec)
}
}
static int acpi_ec_add(struct acpi_device *device)
static struct acpi_ec *acpi_ec_alloc(void)
{
struct acpi_ec *ec = NULL;
int ret;
strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_EC_CLASS);
struct acpi_ec *ec;
/* Check for boot EC */
if (boot_ec) {
......@@ -1376,9 +1372,21 @@ static int acpi_ec_add(struct acpi_device *device)
first_ec = NULL;
} else {
ec = make_acpi_ec();
if (!ec)
return -ENOMEM;
}
return ec;
}
static int acpi_ec_add(struct acpi_device *device)
{
struct acpi_ec *ec = NULL;
int ret;
strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_EC_CLASS);
ec = acpi_ec_alloc();
if (!ec)
return -ENOMEM;
if (ec_parse_device(device->handle, 0, ec, NULL) !=
AE_CTRL_TERMINATE) {
kfree(ec);
......@@ -1465,27 +1473,31 @@ static const struct acpi_device_id ec_device_ids[] = {
int __init acpi_ec_dsdt_probe(void)
{
acpi_status status;
struct acpi_ec *ec;
int ret;
if (boot_ec)
return 0;
ec = acpi_ec_alloc();
if (!ec)
return -ENOMEM;
/*
* Finding EC from DSDT if there is no ECDT EC available. When this
* function is invoked, ACPI tables have been fully loaded, we can
* walk namespace now.
*/
boot_ec = make_acpi_ec();
if (!boot_ec)
return -ENOMEM;
status = acpi_get_devices(ec_device_ids[0].id,
ec_parse_device, boot_ec, NULL);
if (ACPI_FAILURE(status) || !boot_ec->handle)
return -ENODEV;
if (!ec_install_handlers(boot_ec)) {
first_ec = boot_ec;
return 0;
ec_parse_device, ec, NULL);
if (ACPI_FAILURE(status) || !ec->handle) {
ret = -ENODEV;
goto error;
}
return -EFAULT;
ret = ec_install_handlers(ec);
error:
if (ret)
kfree(ec);
else
first_ec = boot_ec = ec;
return ret;
}
#if 0
......@@ -1529,6 +1541,11 @@ static int ec_clear_on_resume(const struct dmi_system_id *id)
return 0;
}
/*
* Some ECDTs contain wrong register addresses.
* MSI MS-171F
* https://bugzilla.kernel.org/show_bug.cgi?id=12461
*/
static int ec_correct_ecdt(const struct dmi_system_id *id)
{
pr_debug("Detected system needing ECDT address correction.\n");
......@@ -1537,16 +1554,6 @@ static int ec_correct_ecdt(const struct dmi_system_id *id)
}
static struct dmi_system_id ec_dmi_table[] __initdata = {
{
ec_correct_ecdt, "Asus L4R", {
DMI_MATCH(DMI_BIOS_VERSION, "1008.006"),
DMI_MATCH(DMI_PRODUCT_NAME, "L4R"),
DMI_MATCH(DMI_BOARD_NAME, "L4R") }, NULL},
{
ec_correct_ecdt, "Asus M6R", {
DMI_MATCH(DMI_BIOS_VERSION, "0207"),
DMI_MATCH(DMI_PRODUCT_NAME, "M6R"),
DMI_MATCH(DMI_BOARD_NAME, "M6R") }, NULL},
{
ec_correct_ecdt, "MSI MS-171F", {
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"),
......@@ -1559,12 +1566,13 @@ static struct dmi_system_id ec_dmi_table[] __initdata = {
int __init acpi_ec_ecdt_probe(void)
{
int ret = 0;
int ret;
acpi_status status;
struct acpi_table_ecdt *ecdt_ptr;
struct acpi_ec *ec;
boot_ec = make_acpi_ec();
if (!boot_ec)
ec = acpi_ec_alloc();
if (!ec)
return -ENOMEM;
/*
* Generate a boot ec context
......@@ -1588,28 +1596,20 @@ int __init acpi_ec_ecdt_probe(void)
pr_info("EC description table is found, configuring boot EC\n");
if (EC_FLAGS_CORRECT_ECDT) {
/*
* Asus L4R, Asus M6R
* https://bugzilla.kernel.org/show_bug.cgi?id=9399
* MSI MS-171F
* https://bugzilla.kernel.org/show_bug.cgi?id=12461
*/
boot_ec->command_addr = ecdt_ptr->data.address;
boot_ec->data_addr = ecdt_ptr->control.address;
ec->command_addr = ecdt_ptr->data.address;
ec->data_addr = ecdt_ptr->control.address;
} else {
boot_ec->command_addr = ecdt_ptr->control.address;
boot_ec->data_addr = ecdt_ptr->data.address;
ec->command_addr = ecdt_ptr->control.address;
ec->data_addr = ecdt_ptr->data.address;
}
boot_ec->gpe = ecdt_ptr->gpe;
boot_ec->handle = ACPI_ROOT_OBJECT;
ret = ec_install_handlers(boot_ec);
if (!ret)
first_ec = boot_ec;
ec->gpe = ecdt_ptr->gpe;
ec->handle = ACPI_ROOT_OBJECT;
ret = ec_install_handlers(ec);
error:
if (ret) {
kfree(boot_ec);
boot_ec = NULL;
}
if (ret)
kfree(ec);
else
first_ec = boot_ec = ec;
return ret;
}
......
......@@ -87,6 +87,9 @@ bool acpi_queue_hotplug_work(struct work_struct *work);
void acpi_device_hotplug(struct acpi_device *adev, u32 src);
bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context);
void acpi_scan_table_handler(u32 event, void *table, void *context);
/* --------------------------------------------------------------------------
Device Node Initialization / Removal
-------------------------------------------------------------------------- */
......
......@@ -18,22 +18,21 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
#define pr_fmt(fmt) "ACPI: " fmt
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/bootmem.h>
#include <linux/memblock.h>
#include <linux/numa.h>
#include <linux/nodemask.h>
#include <linux/topology.h>
#define PREFIX "ACPI: "
#define ACPI_NUMA 0x80000000
#define _COMPONENT ACPI_NUMA
ACPI_MODULE_NAME("numa");
static nodemask_t nodes_found_map = NODE_MASK_NONE;
/* maps to convert between proximity domain and logical node ID */
......@@ -43,6 +42,7 @@ static int node_to_pxm_map[MAX_NUMNODES]
= { [0 ... MAX_NUMNODES - 1] = PXM_INVAL };
unsigned char acpi_srat_revision __initdata;
int acpi_numa __initdata;
int pxm_to_node(int pxm)
{
......@@ -128,68 +128,63 @@ EXPORT_SYMBOL(acpi_map_pxm_to_online_node);
static void __init
acpi_table_print_srat_entry(struct acpi_subtable_header *header)
{
ACPI_FUNCTION_NAME("acpi_table_print_srat_entry");
if (!header)
return;
switch (header->type) {
case ACPI_SRAT_TYPE_CPU_AFFINITY:
#ifdef ACPI_DEBUG_OUTPUT
{
struct acpi_srat_cpu_affinity *p =
(struct acpi_srat_cpu_affinity *)header;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n",
p->apic_id, p->local_sapic_eid,
p->proximity_domain_lo,
(p->flags & ACPI_SRAT_CPU_ENABLED)?
"enabled" : "disabled"));
pr_debug("SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n",
p->apic_id, p->local_sapic_eid,
p->proximity_domain_lo,
(p->flags & ACPI_SRAT_CPU_ENABLED) ?
"enabled" : "disabled");
}
#endif /* ACPI_DEBUG_OUTPUT */
break;
case ACPI_SRAT_TYPE_MEMORY_AFFINITY:
#ifdef ACPI_DEBUG_OUTPUT
{
struct acpi_srat_mem_affinity *p =
(struct acpi_srat_mem_affinity *)header;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"SRAT Memory (0x%lx length 0x%lx) in proximity domain %d %s%s%s\n",
(unsigned long)p->base_address,
(unsigned long)p->length,
p->proximity_domain,
(p->flags & ACPI_SRAT_MEM_ENABLED)?
"enabled" : "disabled",
(p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)?
" hot-pluggable" : "",
(p->flags & ACPI_SRAT_MEM_NON_VOLATILE)?
" non-volatile" : ""));
pr_debug("SRAT Memory (0x%lx length 0x%lx) in proximity domain %d %s%s%s\n",
(unsigned long)p->base_address,
(unsigned long)p->length,
p->proximity_domain,
(p->flags & ACPI_SRAT_MEM_ENABLED) ?
"enabled" : "disabled",
(p->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) ?
" hot-pluggable" : "",
(p->flags & ACPI_SRAT_MEM_NON_VOLATILE) ?
" non-volatile" : "");
}
#endif /* ACPI_DEBUG_OUTPUT */
break;
case ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY:
#ifdef ACPI_DEBUG_OUTPUT
{
struct acpi_srat_x2apic_cpu_affinity *p =
(struct acpi_srat_x2apic_cpu_affinity *)header;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"SRAT Processor (x2apicid[0x%08x]) in"
" proximity domain %d %s\n",
p->apic_id,
p->proximity_domain,
(p->flags & ACPI_SRAT_CPU_ENABLED) ?
"enabled" : "disabled"));
pr_debug("SRAT Processor (x2apicid[0x%08x]) in proximity domain %d %s\n",
p->apic_id,
p->proximity_domain,
(p->flags & ACPI_SRAT_CPU_ENABLED) ?
"enabled" : "disabled");
}
#endif /* ACPI_DEBUG_OUTPUT */
break;
case ACPI_SRAT_TYPE_GICC_AFFINITY:
{
struct acpi_srat_gicc_affinity *p =
(struct acpi_srat_gicc_affinity *)header;
pr_debug("SRAT Processor (acpi id[0x%04x]) in proximity domain %d %s\n",
p->acpi_processor_uid,
p->proximity_domain,
(p->flags & ACPI_SRAT_GICC_ENABLED) ?
"enabled" : "disabled");
}
break;
default:
printk(KERN_WARNING PREFIX
"Found unsupported SRAT entry (type = 0x%x)\n",
header->type);
pr_warn("Found unsupported SRAT entry (type = 0x%x)\n",
header->type);
break;
}
}
......@@ -217,12 +212,117 @@ static int __init slit_valid(struct acpi_table_slit *slit)
return 1;
}
void __init bad_srat(void)
{
pr_err("SRAT: SRAT not used.\n");
acpi_numa = -1;
}
int __init srat_disabled(void)
{
return acpi_numa < 0;
}
#if defined(CONFIG_X86) || defined(CONFIG_ARM64)
/*
* Callback for SLIT parsing. pxm_to_node() returns NUMA_NO_NODE for
* I/O localities since SRAT does not list them. I/O localities are
* not supported at this point.
*/
void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
{
int i, j;
for (i = 0; i < slit->locality_count; i++) {
const int from_node = pxm_to_node(i);
if (from_node == NUMA_NO_NODE)
continue;
for (j = 0; j < slit->locality_count; j++) {
const int to_node = pxm_to_node(j);
if (to_node == NUMA_NO_NODE)
continue;
numa_set_distance(from_node, to_node,
slit->entry[slit->locality_count * i + j]);
}
}
}
/*
* Default callback for parsing of the Proximity Domain <-> Memory
* Area mappings
*/
int __init
acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
{
u64 start, end;
u32 hotpluggable;
int node, pxm;
if (srat_disabled())
goto out_err;
if (ma->header.length < sizeof(struct acpi_srat_mem_affinity)) {
pr_err("SRAT: Unexpected header length: %d\n",
ma->header.length);
goto out_err_bad_srat;
}
if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0)
goto out_err;
hotpluggable = ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE;
if (hotpluggable && !IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
goto out_err;
start = ma->base_address;
end = start + ma->length;
pxm = ma->proximity_domain;
if (acpi_srat_revision <= 1)
pxm &= 0xff;
node = acpi_map_pxm_to_node(pxm);
if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
pr_err("SRAT: Too many proximity domains.\n");
goto out_err_bad_srat;
}
if (numa_add_memblk(node, start, end) < 0) {
pr_err("SRAT: Failed to add memblk to node %u [mem %#010Lx-%#010Lx]\n",
node, (unsigned long long) start,
(unsigned long long) end - 1);
goto out_err_bad_srat;
}
node_set(node, numa_nodes_parsed);
pr_info("SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]%s%s\n",
node, pxm,
(unsigned long long) start, (unsigned long long) end - 1,
hotpluggable ? " hotplug" : "",
ma->flags & ACPI_SRAT_MEM_NON_VOLATILE ? " non-volatile" : "");
/* Mark hotplug range in memblock. */
if (hotpluggable && memblock_mark_hotplug(start, ma->length))
pr_warn("SRAT: Failed to mark hotplug range [mem %#010Lx-%#010Lx] in memblock\n",
(unsigned long long)start, (unsigned long long)end - 1);
max_possible_pfn = max(max_possible_pfn, PFN_UP(end - 1));
return 0;
out_err_bad_srat:
bad_srat();
out_err:
return -EINVAL;
}
#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */
static int __init acpi_parse_slit(struct acpi_table_header *table)
{
struct acpi_table_slit *slit = (struct acpi_table_slit *)table;
if (!slit_valid(slit)) {
printk(KERN_INFO "ACPI: SLIT table looks invalid. Not used.\n");
pr_info("SLIT table looks invalid. Not used.\n");
return -EINVAL;
}
acpi_numa_slit_init(slit);
......@@ -233,12 +333,9 @@ static int __init acpi_parse_slit(struct acpi_table_header *table)
void __init __weak
acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa)
{
printk(KERN_WARNING PREFIX
"Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id);
return;
pr_warn("Found unsupported x2apic [0x%08x] SRAT entry\n", pa->apic_id);
}
static int __init
acpi_parse_x2apic_affinity(struct acpi_subtable_header *header,
const unsigned long end)
......@@ -275,6 +372,24 @@ acpi_parse_processor_affinity(struct acpi_subtable_header *header,
return 0;
}
static int __init
acpi_parse_gicc_affinity(struct acpi_subtable_header *header,
const unsigned long end)
{
struct acpi_srat_gicc_affinity *processor_affinity;
processor_affinity = (struct acpi_srat_gicc_affinity *)header;
if (!processor_affinity)
return -EINVAL;
acpi_table_print_srat_entry(header);
/* let architecture-dependent part to do it */
acpi_numa_gicc_affinity_init(processor_affinity);
return 0;
}
static int __initdata parsed_numa_memblks;
static int __init
......@@ -319,6 +434,9 @@ int __init acpi_numa_init(void)
{
int cnt = 0;
if (acpi_disabled)
return -EINVAL;
/*
* Should not limit number with cpu num that is from NR_CPUS or nr_cpus=
* SRAT cpu entries could have different order with that in MADT.
......@@ -327,13 +445,15 @@ int __init acpi_numa_init(void)
/* SRAT: Static Resource Affinity Table */
if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) {
struct acpi_subtable_proc srat_proc[2];
struct acpi_subtable_proc srat_proc[3];
memset(srat_proc, 0, sizeof(srat_proc));
srat_proc[0].id = ACPI_SRAT_TYPE_CPU_AFFINITY;
srat_proc[0].handler = acpi_parse_processor_affinity;
srat_proc[1].id = ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY;
srat_proc[1].handler = acpi_parse_x2apic_affinity;
srat_proc[2].id = ACPI_SRAT_TYPE_GICC_AFFINITY;
srat_proc[2].handler = acpi_parse_gicc_affinity;
acpi_table_parse_entries_array(ACPI_SIG_SRAT,
sizeof(struct acpi_table_srat),
......@@ -347,8 +467,6 @@ int __init acpi_numa_init(void)
/* SLIT: System Locality Information Table */
acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit);
acpi_numa_arch_fixup();
if (cnt < 0)
return cnt;
else if (!parsed_numa_memblks)
......
......@@ -22,8 +22,9 @@
* General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
......@@ -33,30 +34,11 @@
#include <linux/dmi.h>
#include <linux/pci-acpi.h>
static bool debug;
static int check_sta_before_sun;
#define DRIVER_VERSION "0.1"
#define DRIVER_AUTHOR "Alex Chiang <achiang@hp.com>"
#define DRIVER_DESC "ACPI PCI Slot Detection Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
module_param(debug, bool, 0644);
#define _COMPONENT ACPI_PCI_COMPONENT
ACPI_MODULE_NAME("pci_slot");
#define MY_NAME "pci_slot"
#define err(format, arg...) pr_err("%s: " format , MY_NAME , ## arg)
#define info(format, arg...) pr_info("%s: " format , MY_NAME , ## arg)
#define dbg(format, arg...) \
do { \
if (debug) \
pr_debug("%s: " format, MY_NAME , ## arg); \
} while (0)
#define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */
struct acpi_pci_slot {
......@@ -76,7 +58,7 @@ check_slot(acpi_handle handle, unsigned long long *sun)
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
dbg("Checking slot on path: %s\n", (char *)buffer.pointer);
pr_debug("Checking slot on path: %s\n", (char *)buffer.pointer);
if (check_sta_before_sun) {
/* If SxFy doesn't have _STA, we just assume it's there */
......@@ -87,14 +69,16 @@ check_slot(acpi_handle handle, unsigned long long *sun)
status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
if (ACPI_FAILURE(status)) {
dbg("_ADR returned %d on %s\n", status, (char *)buffer.pointer);
pr_debug("_ADR returned %d on %s\n",
status, (char *)buffer.pointer);
goto out;
}
/* No _SUN == not a slot == bail */
status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
if (ACPI_FAILURE(status)) {
dbg("_SUN returned %d on %s\n", status, (char *)buffer.pointer);
pr_debug("_SUN returned %d on %s\n",
status, (char *)buffer.pointer);
goto out;
}
......@@ -132,15 +116,13 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
}
slot = kmalloc(sizeof(*slot), GFP_KERNEL);
if (!slot) {
err("%s: cannot allocate memory\n", __func__);
if (!slot)
return AE_OK;
}
snprintf(name, sizeof(name), "%llu", sun);
pci_slot = pci_create_slot(pci_bus, device, name, NULL);
if (IS_ERR(pci_slot)) {
err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
pr_err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
kfree(slot);
return AE_OK;
}
......@@ -150,8 +132,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
get_device(&pci_bus->dev);
dbg("pci_slot: %p, pci_bus: %x, device: %d, name: %s\n",
pci_slot, pci_bus->number, device, name);
pr_debug("%p, pci_bus: %x, device: %d, name: %s\n",
pci_slot, pci_bus->number, device, name);
return AE_OK;
}
......@@ -186,7 +168,8 @@ void acpi_pci_slot_remove(struct pci_bus *bus)
static int do_sta_before_sun(const struct dmi_system_id *d)
{
info("%s detected: will evaluate _STA before calling _SUN\n", d->ident);
pr_info("%s detected: will evaluate _STA before calling _SUN\n",
d->ident);
check_sta_before_sun = 1;
return 0;
}
......
......@@ -13,7 +13,7 @@
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/export.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
#include <acpi/acpi_lpat.h>
......@@ -21,12 +21,19 @@
#define PMIC_POWER_OPREGION_ID 0x8d
#define PMIC_THERMAL_OPREGION_ID 0x8c
#define PMIC_REGS_OPREGION_ID 0x8f
struct intel_pmic_regs_handler_ctx {
unsigned int val;
u16 addr;
};
struct intel_pmic_opregion {
struct mutex lock;
struct acpi_lpat_conversion_table *lpat_table;
struct regmap *regmap;
struct intel_pmic_opregion_data *data;
struct intel_pmic_regs_handler_ctx ctx;
};
static int pmic_get_reg_bit(int address, struct pmic_table *table,
......@@ -131,7 +138,7 @@ static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg,
}
static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg,
u32 function, u64 *value)
int bit, u32 function, u64 *value)
{
struct intel_pmic_opregion_data *d = opregion->data;
struct regmap *regmap = opregion->regmap;
......@@ -140,12 +147,12 @@ static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg,
return -ENXIO;
if (function == ACPI_READ)
return d->get_policy(regmap, reg, value);
return d->get_policy(regmap, reg, bit, value);
if (*value != 0 && *value != 1)
return -EINVAL;
return d->update_policy(regmap, reg, *value);
return d->update_policy(regmap, reg, bit, *value);
}
static bool pmic_thermal_is_temp(int address)
......@@ -170,13 +177,13 @@ static acpi_status intel_pmic_thermal_handler(u32 function,
{
struct intel_pmic_opregion *opregion = region_context;
struct intel_pmic_opregion_data *d = opregion->data;
int reg, result;
int reg, bit, result;
if (bits != 32 || !value64)
return AE_BAD_PARAMETER;
result = pmic_get_reg_bit(address, d->thermal_table,
d->thermal_table_count, &reg, NULL);
d->thermal_table_count, &reg, &bit);
if (result == -ENOENT)
return AE_BAD_PARAMETER;
......@@ -187,7 +194,8 @@ static acpi_status intel_pmic_thermal_handler(u32 function,
else if (pmic_thermal_is_aux(address))
result = pmic_thermal_aux(opregion, reg, function, value64);
else if (pmic_thermal_is_pen(address))
result = pmic_thermal_pen(opregion, reg, function, value64);
result = pmic_thermal_pen(opregion, reg, bit,
function, value64);
else
result = -EINVAL;
......@@ -203,6 +211,48 @@ static acpi_status intel_pmic_thermal_handler(u32 function,
return AE_OK;
}
static acpi_status intel_pmic_regs_handler(u32 function,
acpi_physical_address address, u32 bits, u64 *value64,
void *handler_context, void *region_context)
{
struct intel_pmic_opregion *opregion = region_context;
int result = 0;
switch (address) {
case 0:
return AE_OK;
case 1:
opregion->ctx.addr |= (*value64 & 0xff) << 8;
return AE_OK;
case 2:
opregion->ctx.addr |= *value64 & 0xff;
return AE_OK;
case 3:
opregion->ctx.val = *value64 & 0xff;
return AE_OK;
case 4:
if (*value64) {
result = regmap_write(opregion->regmap, opregion->ctx.addr,
opregion->ctx.val);
} else {
result = regmap_read(opregion->regmap, opregion->ctx.addr,
&opregion->ctx.val);
if (result == 0)
*value64 = opregion->ctx.val;
}
memset(&opregion->ctx, 0x00, sizeof(opregion->ctx));
}
if (result < 0) {
if (result == -EINVAL)
return AE_BAD_PARAMETER;
else
return AE_ERROR;
}
return AE_OK;
}
int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,
struct regmap *regmap,
struct intel_pmic_opregion_data *d)
......@@ -242,16 +292,30 @@ int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,
acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID,
intel_pmic_power_handler);
ret = -ENODEV;
goto out_error;
goto out_remove_power_handler;
}
status = acpi_install_address_space_handler(handle,
PMIC_REGS_OPREGION_ID, intel_pmic_regs_handler, NULL,
opregion);
if (ACPI_FAILURE(status)) {
ret = -ENODEV;
goto out_remove_thermal_handler;
}
opregion->data = d;
return 0;
out_remove_thermal_handler:
acpi_remove_address_space_handler(handle, PMIC_THERMAL_OPREGION_ID,
intel_pmic_thermal_handler);
out_remove_power_handler:
acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID,
intel_pmic_power_handler);
out_error:
acpi_lpat_free_conversion_table(opregion->lpat_table);
return ret;
}
EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler);
MODULE_LICENSE("GPL");
......@@ -12,8 +12,8 @@ struct intel_pmic_opregion_data {
int (*update_power)(struct regmap *r, int reg, int bit, bool on);
int (*get_raw_temp)(struct regmap *r, int reg);
int (*update_aux)(struct regmap *r, int reg, int raw_temp);
int (*get_policy)(struct regmap *r, int reg, u64 *value);
int (*update_policy)(struct regmap *r, int reg, int enable);
int (*get_policy)(struct regmap *r, int reg, int bit, u64 *value);
int (*update_policy)(struct regmap *r, int reg, int bit, int enable);
struct pmic_table *power_table;
int power_table_count;
struct pmic_table *thermal_table;
......
/*
* intel_pmic_bxtwc.c - Intel BXT WhiskeyCove PMIC operation region driver
*
* Copyright (C) 2015 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/mfd/intel_soc_pmic.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include "intel_pmic.h"
#define WHISKEY_COVE_ALRT_HIGH_BIT_MASK 0x0F
#define WHISKEY_COVE_ADC_HIGH_BIT(x) (((x & 0x0F) << 8))
#define WHISKEY_COVE_ADC_CURSRC(x) (((x & 0xF0) >> 4))
#define VR_MODE_DISABLED 0
#define VR_MODE_AUTO BIT(0)
#define VR_MODE_NORMAL BIT(1)
#define VR_MODE_SWITCH BIT(2)
#define VR_MODE_ECO (BIT(0)|BIT(1))
#define VSWITCH2_OUTPUT BIT(5)
#define VSWITCH1_OUTPUT BIT(4)
#define VUSBPHY_CHARGE BIT(1)
static struct pmic_table power_table[] = {
{
.address = 0x0,
.reg = 0x63,
.bit = VR_MODE_AUTO,
}, /* VDD1 -> VDD1CNT */
{
.address = 0x04,
.reg = 0x65,
.bit = VR_MODE_AUTO,
}, /* VDD2 -> VDD2CNT */
{
.address = 0x08,
.reg = 0x67,
.bit = VR_MODE_AUTO,
}, /* VDD3 -> VDD3CNT */
{
.address = 0x0c,
.reg = 0x6d,
.bit = VR_MODE_AUTO,
}, /* VLFX -> VFLEXCNT */
{
.address = 0x10,
.reg = 0x6f,
.bit = VR_MODE_NORMAL,
}, /* VP1A -> VPROG1ACNT */
{
.address = 0x14,
.reg = 0x70,
.bit = VR_MODE_NORMAL,
}, /* VP1B -> VPROG1BCNT */
{
.address = 0x18,
.reg = 0x71,
.bit = VR_MODE_NORMAL,
}, /* VP1C -> VPROG1CCNT */
{
.address = 0x1c,
.reg = 0x72,
.bit = VR_MODE_NORMAL,
}, /* VP1D -> VPROG1DCNT */
{
.address = 0x20,
.reg = 0x73,
.bit = VR_MODE_NORMAL,
}, /* VP2A -> VPROG2ACNT */
{
.address = 0x24,
.reg = 0x74,
.bit = VR_MODE_NORMAL,
}, /* VP2B -> VPROG2BCNT */
{
.address = 0x28,
.reg = 0x75,
.bit = VR_MODE_NORMAL,
}, /* VP2C -> VPROG2CCNT */
{
.address = 0x2c,
.reg = 0x76,
.bit = VR_MODE_NORMAL,
}, /* VP3A -> VPROG3ACNT */
{
.address = 0x30,
.reg = 0x77,
.bit = VR_MODE_NORMAL,
}, /* VP3B -> VPROG3BCNT */
{
.address = 0x34,
.reg = 0x78,
.bit = VSWITCH2_OUTPUT,
}, /* VSW2 -> VLD0CNT Bit 5*/
{
.address = 0x38,
.reg = 0x78,
.bit = VSWITCH1_OUTPUT,
}, /* VSW1 -> VLD0CNT Bit 4 */
{
.address = 0x3c,
.reg = 0x78,
.bit = VUSBPHY_CHARGE,
}, /* VUPY -> VLDOCNT Bit 1 */
{
.address = 0x40,
.reg = 0x7b,
.bit = VR_MODE_NORMAL,
}, /* VRSO -> VREFSOCCNT*/
{
.address = 0x44,
.reg = 0xA0,
.bit = VR_MODE_NORMAL,
}, /* VP1E -> VPROG1ECNT */
{
.address = 0x48,
.reg = 0xA1,
.bit = VR_MODE_NORMAL,
}, /* VP1F -> VPROG1FCNT */
{
.address = 0x4c,
.reg = 0xA2,
.bit = VR_MODE_NORMAL,
}, /* VP2D -> VPROG2DCNT */
{
.address = 0x50,
.reg = 0xA3,
.bit = VR_MODE_NORMAL,
}, /* VP4A -> VPROG4ACNT */
{
.address = 0x54,
.reg = 0xA4,
.bit = VR_MODE_NORMAL,
}, /* VP4B -> VPROG4BCNT */
{
.address = 0x58,
.reg = 0xA5,
.bit = VR_MODE_NORMAL,
}, /* VP4C -> VPROG4CCNT */
{
.address = 0x5c,
.reg = 0xA6,
.bit = VR_MODE_NORMAL,
}, /* VP4D -> VPROG4DCNT */
{
.address = 0x60,
.reg = 0xA7,
.bit = VR_MODE_NORMAL,
}, /* VP5A -> VPROG5ACNT */
{
.address = 0x64,
.reg = 0xA8,
.bit = VR_MODE_NORMAL,
}, /* VP5B -> VPROG5BCNT */
{
.address = 0x68,
.reg = 0xA9,
.bit = VR_MODE_NORMAL,
}, /* VP6A -> VPROG6ACNT */
{
.address = 0x6c,
.reg = 0xAA,
.bit = VR_MODE_NORMAL,
}, /* VP6B -> VPROG6BCNT */
{
.address = 0x70,
.reg = 0x36,
.bit = BIT(2),
}, /* SDWN_N -> MODEMCTRL Bit 2 */
{
.address = 0x74,
.reg = 0x36,
.bit = BIT(0),
} /* MOFF -> MODEMCTRL Bit 0 */
};
static struct pmic_table thermal_table[] = {
{
.address = 0x00,
.reg = 0x4F39
},
{
.address = 0x04,
.reg = 0x4F24
},
{
.address = 0x08,
.reg = 0x4F26
},
{
.address = 0x0c,
.reg = 0x4F3B
},
{
.address = 0x10,
.reg = 0x4F28
},
{
.address = 0x14,
.reg = 0x4F2A
},
{
.address = 0x18,
.reg = 0x4F3D
},
{
.address = 0x1c,
.reg = 0x4F2C
},
{
.address = 0x20,
.reg = 0x4F2E
},
{
.address = 0x24,
.reg = 0x4F3F
},
{
.address = 0x28,
.reg = 0x4F30
},
{
.address = 0x30,
.reg = 0x4F41
},
{
.address = 0x34,
.reg = 0x4F32
},
{
.address = 0x3c,
.reg = 0x4F43
},
{
.address = 0x40,
.reg = 0x4F34
},
{
.address = 0x48,
.reg = 0x4F6A,
.bit = 0,
},
{
.address = 0x4C,
.reg = 0x4F6A,
.bit = 1
},
{
.address = 0x50,
.reg = 0x4F6A,
.bit = 2
},
{
.address = 0x54,
.reg = 0x4F6A,
.bit = 4
},
{
.address = 0x58,
.reg = 0x4F6A,
.bit = 5
},
{
.address = 0x5C,
.reg = 0x4F6A,
.bit = 3
},
};
static int intel_bxtwc_pmic_get_power(struct regmap *regmap, int reg,
int bit, u64 *value)
{
int data;
if (regmap_read(regmap, reg, &data))
return -EIO;
*value = (data & bit) ? 1 : 0;
return 0;
}
static int intel_bxtwc_pmic_update_power(struct regmap *regmap, int reg,
int bit, bool on)
{
u8 val, mask = bit;
if (on)
val = 0xFF;
else
val = 0x0;
return regmap_update_bits(regmap, reg, mask, val);
}
static int intel_bxtwc_pmic_get_raw_temp(struct regmap *regmap, int reg)
{
unsigned int val, adc_val, reg_val;
u8 temp_l, temp_h, cursrc;
unsigned long rlsb;
static const unsigned long rlsb_array[] = {
0, 260420, 130210, 65100, 32550, 16280,
8140, 4070, 2030, 0, 260420, 130210 };
if (regmap_read(regmap, reg, &val))
return -EIO;
temp_l = (u8) val;
if (regmap_read(regmap, (reg - 1), &val))
return -EIO;
temp_h = (u8) val;
reg_val = temp_l | WHISKEY_COVE_ADC_HIGH_BIT(temp_h);
cursrc = WHISKEY_COVE_ADC_CURSRC(temp_h);
rlsb = rlsb_array[cursrc];
adc_val = reg_val * rlsb / 1000;
return adc_val;
}
static int
intel_bxtwc_pmic_update_aux(struct regmap *regmap, int reg, int raw)
{
u32 bsr_num;
u16 resi_val, count = 0, thrsh = 0;
u8 alrt_h, alrt_l, cursel = 0;
bsr_num = raw;
bsr_num /= (1 << 5);
count = fls(bsr_num) - 1;
cursel = clamp_t(s8, (count - 7), 0, 7);
thrsh = raw / (1 << (4 + cursel));
resi_val = (cursel << 9) | thrsh;
alrt_h = (resi_val >> 8) & WHISKEY_COVE_ALRT_HIGH_BIT_MASK;
if (regmap_update_bits(regmap,
reg - 1,
WHISKEY_COVE_ALRT_HIGH_BIT_MASK,
alrt_h))
return -EIO;
alrt_l = (u8)resi_val;
return regmap_write(regmap, reg, alrt_l);
}
static int
intel_bxtwc_pmic_get_policy(struct regmap *regmap, int reg, int bit, u64 *value)
{
u8 mask = BIT(bit);
unsigned int val;
if (regmap_read(regmap, reg, &val))
return -EIO;
*value = (val & mask) >> bit;
return 0;
}
static int
intel_bxtwc_pmic_update_policy(struct regmap *regmap,
int reg, int bit, int enable)
{
u8 mask = BIT(bit), val = enable << bit;
return regmap_update_bits(regmap, reg, mask, val);
}
static struct intel_pmic_opregion_data intel_bxtwc_pmic_opregion_data = {
.get_power = intel_bxtwc_pmic_get_power,
.update_power = intel_bxtwc_pmic_update_power,
.get_raw_temp = intel_bxtwc_pmic_get_raw_temp,
.update_aux = intel_bxtwc_pmic_update_aux,
.get_policy = intel_bxtwc_pmic_get_policy,
.update_policy = intel_bxtwc_pmic_update_policy,
.power_table = power_table,
.power_table_count = ARRAY_SIZE(power_table),
.thermal_table = thermal_table,
.thermal_table_count = ARRAY_SIZE(thermal_table),
};
static int intel_bxtwc_pmic_opregion_probe(struct platform_device *pdev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
return intel_pmic_install_opregion_handler(&pdev->dev,
ACPI_HANDLE(pdev->dev.parent),
pmic->regmap,
&intel_bxtwc_pmic_opregion_data);
}
static struct platform_device_id bxt_wc_opregion_id_table[] = {
{ .name = "bxt_wcove_region" },
{},
};
static struct platform_driver intel_bxtwc_pmic_opregion_driver = {
.probe = intel_bxtwc_pmic_opregion_probe,
.driver = {
.name = "bxt_whiskey_cove_pmic",
},
.id_table = bxt_wc_opregion_id_table,
};
static int __init intel_bxtwc_pmic_opregion_driver_init(void)
{
return platform_driver_register(&intel_bxtwc_pmic_opregion_driver);
}
device_initcall(intel_bxtwc_pmic_opregion_driver_init);
......@@ -141,7 +141,8 @@ static int intel_crc_pmic_update_aux(struct regmap *regmap, int reg, int raw)
regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8) ? -EIO : 0;
}
static int intel_crc_pmic_get_policy(struct regmap *regmap, int reg, u64 *value)
static int intel_crc_pmic_get_policy(struct regmap *regmap,
int reg, int bit, u64 *value)
{
int pen;
......@@ -152,7 +153,7 @@ static int intel_crc_pmic_get_policy(struct regmap *regmap, int reg, u64 *value)
}
static int intel_crc_pmic_update_policy(struct regmap *regmap,
int reg, int enable)
int reg, int bit, int enable)
{
int alert0;
......
......@@ -13,7 +13,7 @@
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/mfd/axp20x.h>
#include <linux/regmap.h>
......@@ -262,7 +262,4 @@ static int __init intel_xpower_pmic_opregion_driver_init(void)
{
return platform_driver_register(&intel_xpower_pmic_opregion_driver);
}
module_init(intel_xpower_pmic_opregion_driver_init);
MODULE_DESCRIPTION("XPower AXP288 ACPI operation region driver");
MODULE_LICENSE("GPL");
device_initcall(intel_xpower_pmic_opregion_driver_init);
......@@ -108,13 +108,12 @@ static int map_gicc_mpidr(struct acpi_subtable_header *entry,
return -EINVAL;
}
static phys_cpuid_t map_madt_entry(int type, u32 acpi_id)
static phys_cpuid_t map_madt_entry(struct acpi_table_madt *madt,
int type, u32 acpi_id)
{
unsigned long madt_end, entry;
phys_cpuid_t phys_id = PHYS_CPUID_INVALID; /* CPU hardware ID */
struct acpi_table_madt *madt;
madt = get_madt_table();
if (!madt)
return phys_id;
......@@ -145,6 +144,25 @@ static phys_cpuid_t map_madt_entry(int type, u32 acpi_id)
return phys_id;
}
phys_cpuid_t __init acpi_map_madt_entry(u32 acpi_id)
{
struct acpi_table_madt *madt = NULL;
acpi_size tbl_size;
phys_cpuid_t rv;
acpi_get_table_with_size(ACPI_SIG_MADT, 0,
(struct acpi_table_header **)&madt,
&tbl_size);
if (!madt)
return PHYS_CPUID_INVALID;
rv = map_madt_entry(madt, 1, acpi_id);
early_acpi_os_unmap_memory(madt, tbl_size);
return rv;
}
static phys_cpuid_t map_mat_entry(acpi_handle handle, int type, u32 acpi_id)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
......@@ -185,7 +203,7 @@ phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id)
phys_id = map_mat_entry(handle, type, acpi_id);
if (invalid_phys_cpuid(phys_id))
phys_id = map_madt_entry(type, acpi_id);
phys_id = map_madt_entry(get_madt_table(), type, acpi_id);
return phys_id;
}
......
......@@ -90,7 +90,7 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data)
pr->performance_platform_limit);
break;
case ACPI_PROCESSOR_NOTIFY_POWER:
acpi_processor_cst_has_changed(pr);
acpi_processor_power_state_has_changed(pr);
acpi_bus_generate_netlink_event(device->pnp.device_class,
dev_name(&device->dev), event, 0);
break;
......
This diff is collapsed.
......@@ -494,6 +494,8 @@ static void acpi_device_del(struct acpi_device *device)
device_del(&device->dev);
}
static BLOCKING_NOTIFIER_HEAD(acpi_reconfig_chain);
static LIST_HEAD(acpi_device_del_list);
static DEFINE_MUTEX(acpi_device_del_lock);
......@@ -514,6 +516,9 @@ static void acpi_device_del_work_fn(struct work_struct *work_not_used)
mutex_unlock(&acpi_device_del_lock);
blocking_notifier_call_chain(&acpi_reconfig_chain,
ACPI_RECONFIG_DEVICE_REMOVE, adev);
acpi_device_del(adev);
/*
* Drop references to all power resources that might have been
......@@ -1406,7 +1411,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
acpi_bus_get_flags(device);
device->flags.match_driver = false;
device->flags.initialized = true;
device->flags.visited = false;
acpi_device_clear_enumerated(device);
device_initialize(&device->dev);
dev_set_uevent_suppress(&device->dev, true);
acpi_init_coherency(device);
......@@ -1676,15 +1681,20 @@ static void acpi_default_enumeration(struct acpi_device *device)
bool is_spi_i2c_slave = false;
/*
* Do not enemerate SPI/I2C slaves as they will be enuerated by their
* Do not enumerate SPI/I2C slaves as they will be enumerated by their
* respective parents.
*/
INIT_LIST_HEAD(&resource_list);
acpi_dev_get_resources(device, &resource_list, acpi_check_spi_i2c_slave,
&is_spi_i2c_slave);
acpi_dev_free_resource_list(&resource_list);
if (!is_spi_i2c_slave)
if (!is_spi_i2c_slave) {
acpi_create_platform_device(device);
acpi_device_set_enumerated(device);
} else {
blocking_notifier_call_chain(&acpi_reconfig_chain,
ACPI_RECONFIG_DEVICE_ADD, device);
}
}
static const struct acpi_device_id generic_device_ids[] = {
......@@ -1751,7 +1761,7 @@ static void acpi_bus_attach(struct acpi_device *device)
acpi_bus_get_status(device);
/* Skip devices that are not present. */
if (!acpi_device_is_present(device)) {
device->flags.visited = false;
acpi_device_clear_enumerated(device);
device->flags.power_manageable = 0;
return;
}
......@@ -1766,7 +1776,7 @@ static void acpi_bus_attach(struct acpi_device *device)
device->flags.initialized = true;
}
device->flags.visited = false;
ret = acpi_scan_attach_handler(device);
if (ret < 0)
return;
......@@ -1780,7 +1790,6 @@ static void acpi_bus_attach(struct acpi_device *device)
if (!ret && device->pnp.type.platform_id)
acpi_default_enumeration(device);
}
device->flags.visited = true;
ok:
list_for_each_entry(child, &device->children, node)
......@@ -1872,7 +1881,7 @@ void acpi_bus_trim(struct acpi_device *adev)
*/
acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
adev->flags.initialized = false;
adev->flags.visited = false;
acpi_device_clear_enumerated(adev);
}
EXPORT_SYMBOL_GPL(acpi_bus_trim);
......@@ -1916,6 +1925,8 @@ static int acpi_bus_scan_fixed(void)
return result < 0 ? result : 0;
}
static bool acpi_scan_initialized;
int __init acpi_scan_init(void)
{
int result;
......@@ -1960,6 +1971,8 @@ int __init acpi_scan_init(void)
acpi_update_all_gpes();
acpi_scan_initialized = true;
out:
mutex_unlock(&acpi_scan_lock);
return result;
......@@ -2003,3 +2016,57 @@ int __init __acpi_probe_device_table(struct acpi_probe_entry *ap_head, int nr)
return count;
}
struct acpi_table_events_work {
struct work_struct work;
void *table;
u32 event;
};
static void acpi_table_events_fn(struct work_struct *work)
{
struct acpi_table_events_work *tew;
tew = container_of(work, struct acpi_table_events_work, work);
if (tew->event == ACPI_TABLE_EVENT_LOAD) {
acpi_scan_lock_acquire();
acpi_bus_scan(ACPI_ROOT_OBJECT);
acpi_scan_lock_release();
}
kfree(tew);
}
void acpi_scan_table_handler(u32 event, void *table, void *context)
{
struct acpi_table_events_work *tew;
if (!acpi_scan_initialized)
return;
if (event != ACPI_TABLE_EVENT_LOAD)
return;
tew = kmalloc(sizeof(*tew), GFP_KERNEL);
if (!tew)
return;
INIT_WORK(&tew->work, acpi_table_events_fn);
tew->table = table;
tew->event = event;
schedule_work(&tew->work);
}
int acpi_reconfig_notifier_register(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&acpi_reconfig_chain, nb);
}
EXPORT_SYMBOL(acpi_reconfig_notifier_register);
int acpi_reconfig_notifier_unregister(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&acpi_reconfig_chain, nb);
}
EXPORT_SYMBOL(acpi_reconfig_notifier_unregister);
......@@ -47,15 +47,32 @@ static void acpi_sleep_tts_switch(u32 acpi_state)
}
}
static int tts_notify_reboot(struct notifier_block *this,
static void acpi_sleep_pts_switch(u32 acpi_state)
{
acpi_status status;
status = acpi_execute_simple_method(NULL, "\\_PTS", acpi_state);
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
/*
* OS can't evaluate the _PTS object correctly. Some warning
* message will be printed. But it won't break anything.
*/
printk(KERN_NOTICE "Failure in evaluating _PTS object\n");
}
}
static int sleep_notify_reboot(struct notifier_block *this,
unsigned long code, void *x)
{
acpi_sleep_tts_switch(ACPI_STATE_S5);
acpi_sleep_pts_switch(ACPI_STATE_S5);
return NOTIFY_DONE;
}
static struct notifier_block tts_notifier = {
.notifier_call = tts_notify_reboot,
static struct notifier_block sleep_notifier = {
.notifier_call = sleep_notify_reboot,
.next = NULL,
.priority = 0,
};
......@@ -899,9 +916,9 @@ int __init acpi_sleep_init(void)
pr_info(PREFIX "(supports%s)\n", supported);
/*
* Register the tts_notifier to reboot notifier list so that the _TTS
* object can also be evaluated when the system enters S5.
* Register the sleep_notifier to reboot notifier list so that the _TTS
* and _PTS object can also be evaluated when the system enters S5.
*/
register_reboot_notifier(&tts_notifier);
register_reboot_notifier(&sleep_notifier);
return 0;
}
......@@ -378,8 +378,7 @@ static void acpi_table_attr_init(struct acpi_table_attr *table_attr,
return;
}
static acpi_status
acpi_sysfs_table_handler(u32 event, void *table, void *context)
acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context)
{
struct acpi_table_attr *table_attr;
......@@ -452,9 +451,8 @@ static int acpi_tables_sysfs_init(void)
kobject_uevent(tables_kobj, KOBJ_ADD);
kobject_uevent(dynamic_tables_kobj, KOBJ_ADD);
status = acpi_install_table_handler(acpi_sysfs_table_handler, NULL);
return ACPI_FAILURE(status) ? -EINVAL : 0;
return 0;
err_dynamic_tables:
kobject_put(tables_kobj);
err:
......
......@@ -34,6 +34,8 @@
#include <linux/bootmem.h>
#include <linux/earlycpio.h>
#include <linux/memblock.h>
#include <linux/initrd.h>
#include <linux/acpi.h>
#include "internal.h"
#ifdef CONFIG_ACPI_CUSTOM_DSDT
......@@ -481,8 +483,10 @@ static DECLARE_BITMAP(acpi_initrd_installed, NR_ACPI_INITRD_TABLES);
#define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT)
static void __init acpi_table_initrd_init(void *data, size_t size)
void __init acpi_table_upgrade(void)
{
void *data = (void *)initrd_start;
size_t size = initrd_end - initrd_start;
int sig, no, table_nr = 0, total_offset = 0;
long offset = 0;
struct acpi_table_header *table;
......@@ -540,7 +544,7 @@ static void __init acpi_table_initrd_init(void *data, size_t size)
return;
acpi_tables_addr =
memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT,
memblock_find_in_range(0, ACPI_TABLE_UPGRADE_MAX_PHYS,
all_tables_size, PAGE_SIZE);
if (!acpi_tables_addr) {
WARN_ON(1);
......@@ -578,10 +582,10 @@ static void __init acpi_table_initrd_init(void *data, size_t size)
clen = size;
if (clen > MAP_CHUNK_SIZE - slop)
clen = MAP_CHUNK_SIZE - slop;
dest_p = early_ioremap(dest_addr & PAGE_MASK,
clen + slop);
dest_p = early_memremap(dest_addr & PAGE_MASK,
clen + slop);
memcpy(dest_p + slop, src_p, clen);
early_iounmap(dest_p, clen + slop);
early_memunmap(dest_p, clen + slop);
src_p += clen;
dest_addr += clen;
size -= clen;
......@@ -696,10 +700,6 @@ static void __init acpi_table_initrd_scan(void)
}
}
#else
static void __init acpi_table_initrd_init(void *data, size_t size)
{
}
static acpi_status
acpi_table_initrd_override(struct acpi_table_header *existing_table,
acpi_physical_address *address,
......@@ -742,11 +742,6 @@ acpi_os_table_override(struct acpi_table_header *existing_table,
return AE_OK;
}
void __init early_acpi_table_init(void *data, size_t size)
{
acpi_table_initrd_init(data, size);
}
/*
* acpi_table_init()
*
......
......@@ -1259,7 +1259,8 @@ static int __init acpi_thermal_init(void)
return -ENODEV;
}
acpi_thermal_pm_queue = create_workqueue("acpi_thermal_pm");
acpi_thermal_pm_queue = alloc_workqueue("acpi_thermal_pm",
WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
if (!acpi_thermal_pm_queue)
return -ENODEV;
......
......@@ -167,6 +167,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"),
},
},
{
.callback = video_detect_force_video,
.ident = "ThinkPad X201T",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201T"),
},
},
/* The native backlight controls do not work on some older machines */
{
......
......@@ -36,26 +36,12 @@
static int arm_enter_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
int ret;
if (!idx) {
cpu_do_idle();
return idx;
}
ret = cpu_pm_enter();
if (!ret) {
/*
* Pass idle state index to cpu_suspend which in turn will
* call the CPU ops suspend protocol with idle index as a
* parameter.
*/
ret = arm_cpuidle_suspend(idx);
cpu_pm_exit();
}
return ret ? -1 : idx;
/*
* Pass idle state index to arm_cpuidle_suspend which in turn
* will call the CPU ops suspend protocol with idle index as a
* parameter.
*/
return CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, idx);
}
static struct cpuidle_driver arm_idle_driver = {
......
......@@ -24,6 +24,9 @@
#include <linux/of_fdt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/ucs2_string.h>
#include <asm/early_ioremap.h>
......@@ -195,6 +198,96 @@ static void generic_ops_unregister(void)
efivars_unregister(&generic_efivars);
}
#if IS_ENABLED(CONFIG_ACPI)
#define EFIVAR_SSDT_NAME_MAX 16
static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata;
static int __init efivar_ssdt_setup(char *str)
{
if (strlen(str) < sizeof(efivar_ssdt))
memcpy(efivar_ssdt, str, strlen(str));
else
pr_warn("efivar_ssdt: name too long: %s\n", str);
return 0;
}
__setup("efivar_ssdt=", efivar_ssdt_setup);
static __init int efivar_ssdt_iter(efi_char16_t *name, efi_guid_t vendor,
unsigned long name_size, void *data)
{
struct efivar_entry *entry;
struct list_head *list = data;
char utf8_name[EFIVAR_SSDT_NAME_MAX];
int limit = min_t(unsigned long, EFIVAR_SSDT_NAME_MAX, name_size);
ucs2_as_utf8(utf8_name, name, limit - 1);
if (strncmp(utf8_name, efivar_ssdt, limit) != 0)
return 0;
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return 0;
memcpy(entry->var.VariableName, name, name_size);
memcpy(&entry->var.VendorGuid, &vendor, sizeof(efi_guid_t));
efivar_entry_add(entry, list);
return 0;
}
static __init int efivar_ssdt_load(void)
{
LIST_HEAD(entries);
struct efivar_entry *entry, *aux;
unsigned long size;
void *data;
int ret;
ret = efivar_init(efivar_ssdt_iter, &entries, true, &entries);
list_for_each_entry_safe(entry, aux, &entries, list) {
pr_info("loading SSDT from variable %s-%pUl\n", efivar_ssdt,
&entry->var.VendorGuid);
list_del(&entry->list);
ret = efivar_entry_size(entry, &size);
if (ret) {
pr_err("failed to get var size\n");
goto free_entry;
}
data = kmalloc(size, GFP_KERNEL);
if (!data)
goto free_entry;
ret = efivar_entry_get(entry, NULL, &size, data);
if (ret) {
pr_err("failed to get var data\n");
goto free_data;
}
ret = acpi_load_table(data);
if (ret) {
pr_err("failed to load table: %d\n", ret);
goto free_data;
}
goto free_entry;
free_data:
kfree(data);
free_entry:
kfree(entry);
}
return ret;
}
#else
static inline int efivar_ssdt_load(void) { return 0; }
#endif
/*
* We register the efi subsystem with the firmware subsystem and the
* efivars subsystem with the efi subsystem, if the system was booted with
......@@ -218,6 +311,9 @@ static int __init efisubsys_init(void)
if (error)
goto err_put;
if (efi_enabled(EFI_RUNTIME_SERVICES))
efivar_ssdt_load();
error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
if (error) {
pr_err("efi: Sysfs attribute export failed with error %d.\n",
......
......@@ -13,6 +13,7 @@
#define pr_fmt(fmt) "psci: " fmt
#include <linux/acpi.h>
#include <linux/arm-smccc.h>
#include <linux/cpuidle.h>
#include <linux/errno.h>
......@@ -256,13 +257,6 @@ static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
u32 *psci_states;
struct device_node *state_node;
/*
* If the PSCI cpu_suspend function hook has not been initialized
* idle states must not be enabled, so bail out
*/
if (!psci_ops.cpu_suspend)
return -EOPNOTSUPP;
/* Count idle states */
while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
count))) {
......@@ -310,11 +304,69 @@ static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
return ret;
}
#ifdef CONFIG_ACPI
#include <acpi/processor.h>
static int __maybe_unused psci_acpi_cpu_init_idle(unsigned int cpu)
{
int i, count;
u32 *psci_states;
struct acpi_lpi_state *lpi;
struct acpi_processor *pr = per_cpu(processors, cpu);
if (unlikely(!pr || !pr->flags.has_lpi))
return -EINVAL;
count = pr->power.count - 1;
if (count <= 0)
return -ENODEV;
psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
if (!psci_states)
return -ENOMEM;
for (i = 0; i < count; i++) {
u32 state;
lpi = &pr->power.lpi_states[i + 1];
/*
* Only bits[31:0] represent a PSCI power_state while
* bits[63:32] must be 0x0 as per ARM ACPI FFH Specification
*/
state = lpi->address;
if (!psci_power_state_is_valid(state)) {
pr_warn("Invalid PSCI power state %#x\n", state);
kfree(psci_states);
return -EINVAL;
}
psci_states[i] = state;
}
/* Idle states parsed correctly, initialize per-cpu pointer */
per_cpu(psci_power_state, cpu) = psci_states;
return 0;
}
#else
static int __maybe_unused psci_acpi_cpu_init_idle(unsigned int cpu)
{
return -EINVAL;
}
#endif
int psci_cpu_init_idle(unsigned int cpu)
{
struct device_node *cpu_node;
int ret;
/*
* If the PSCI cpu_suspend function hook has not been initialized
* idle states must not be enabled, so bail out
*/
if (!psci_ops.cpu_suspend)
return -EOPNOTSUPP;
if (!acpi_disabled)
return psci_acpi_cpu_init_idle(cpu);
cpu_node = of_get_cpu_node(cpu, NULL);
if (!cpu_node)
return -ENODEV;
......
This diff is collapsed.
......@@ -91,8 +91,8 @@ static int __init of_numa_parse_memory_nodes(void)
pr_debug("NUMA: base = %llx len = %llx, node = %u\n",
rsrc.start, rsrc.end - rsrc.start + 1, nid);
r = numa_add_memblk(nid, rsrc.start,
rsrc.end - rsrc.start + 1);
r = numa_add_memblk(nid, rsrc.start, rsrc.end + 1);
if (r)
break;
}
......
......@@ -622,6 +622,8 @@ void spi_unregister_device(struct spi_device *spi)
if (spi->dev.of_node)
of_node_clear_flag(spi->dev.of_node, OF_POPULATED);
if (ACPI_COMPANION(&spi->dev))
acpi_device_clear_enumerated(ACPI_COMPANION(&spi->dev));
device_unregister(&spi->dev);
}
EXPORT_SYMBOL_GPL(spi_unregister_device);
......@@ -1646,18 +1648,15 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
return 1;
}
static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
void *data, void **return_value)
static acpi_status acpi_register_spi_device(struct spi_master *master,
struct acpi_device *adev)
{
struct spi_master *master = data;
struct list_head resource_list;
struct acpi_device *adev;
struct spi_device *spi;
int ret;
if (acpi_bus_get_device(handle, &adev))
return AE_OK;
if (acpi_bus_get_status(adev) || !adev->status.present)
if (acpi_bus_get_status(adev) || !adev->status.present ||
acpi_device_enumerated(adev))
return AE_OK;
spi = spi_alloc_device(master);
......@@ -1683,6 +1682,8 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
if (spi->irq < 0)
spi->irq = acpi_dev_gpio_irq_get(adev, 0);
acpi_device_set_enumerated(adev);
adev->power.flags.ignore_parent = true;
strlcpy(spi->modalias, acpi_device_hid(adev), sizeof(spi->modalias));
if (spi_add_device(spi)) {
......@@ -1695,6 +1696,18 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
return AE_OK;
}
static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
void *data, void **return_value)
{
struct spi_master *master = data;
struct acpi_device *adev;
if (acpi_bus_get_device(handle, &adev))
return AE_OK;
return acpi_register_spi_device(master, adev);
}
static void acpi_register_spi_devices(struct spi_master *master)
{
acpi_status status;
......@@ -3107,6 +3120,77 @@ static struct notifier_block spi_of_notifier = {
extern struct notifier_block spi_of_notifier;
#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
#if IS_ENABLED(CONFIG_ACPI)
static int spi_acpi_master_match(struct device *dev, const void *data)
{
return ACPI_COMPANION(dev->parent) == data;
}
static int spi_acpi_device_match(struct device *dev, void *data)
{
return ACPI_COMPANION(dev) == data;
}
static struct spi_master *acpi_spi_find_master_by_adev(struct acpi_device *adev)
{
struct device *dev;
dev = class_find_device(&spi_master_class, NULL, adev,
spi_acpi_master_match);
if (!dev)
return NULL;
return container_of(dev, struct spi_master, dev);
}
static struct spi_device *acpi_spi_find_device_by_adev(struct acpi_device *adev)
{
struct device *dev;
dev = bus_find_device(&spi_bus_type, NULL, adev, spi_acpi_device_match);
return dev ? to_spi_device(dev) : NULL;
}
static int acpi_spi_notify(struct notifier_block *nb, unsigned long value,
void *arg)
{
struct acpi_device *adev = arg;
struct spi_master *master;
struct spi_device *spi;
switch (value) {
case ACPI_RECONFIG_DEVICE_ADD:
master = acpi_spi_find_master_by_adev(adev->parent);
if (!master)
break;
acpi_register_spi_device(master, adev);
put_device(&master->dev);
break;
case ACPI_RECONFIG_DEVICE_REMOVE:
if (!acpi_device_enumerated(adev))
break;
spi = acpi_spi_find_device_by_adev(adev);
if (!spi)
break;
spi_unregister_device(spi);
put_device(&spi->dev);
break;
}
return NOTIFY_OK;
}
static struct notifier_block spi_acpi_notifier = {
.notifier_call = acpi_spi_notify,
};
#else
extern struct notifier_block spi_acpi_notifier;
#endif
static int __init spi_init(void)
{
int status;
......@@ -3127,6 +3211,8 @@ static int __init spi_init(void)
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
if (IS_ENABLED(CONFIG_ACPI))
WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));
return 0;
......
......@@ -15,6 +15,10 @@ extern int pxm_to_node(int);
extern int node_to_pxm(int);
extern int acpi_map_pxm_to_node(int);
extern unsigned char acpi_srat_revision;
extern int acpi_numa __initdata;
extern void bad_srat(void);
extern int srat_disabled(void);
#endif /* CONFIG_ACPI_NUMA */
#endif /* __ACP_NUMA_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment