Merge branch 'acpi-tables'
* acpi-tables: ACPI: Rename configfs.c to acpi_configfs.c to prevent link error ACPI: add support for loading SSDTs via configfs ACPI: add support for configfs efi / ACPI: load SSTDs from EFI variables spi / ACPI: add support for ACPI reconfigure notifications i2c / ACPI: add support for ACPI reconfigure notifications ACPI: add support for ACPI reconfiguration notifiers ACPI / scan: fix enumeration (visited) flags for bus rescans ACPI / documentation: add SSDT overlays documentation ACPI: ARM64: support for ACPI_TABLE_UPGRADE ACPI / tables: introduce ARCH_HAS_ACPI_TABLE_UPGRADE ACPI / tables: move arch-specific symbol to asm/acpi.h ACPI / tables: table upgrade: refactor function definitions ACPI / tables: table upgrade: use cacheable map for tables Conflicts: arch/arm64/include/asm/acpi.h
This commit is contained in:
commit
d5f017b796
|
@ -0,0 +1,36 @@
|
|||
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
|
|
@ -0,0 +1,172 @@
|
|||
|
||||
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
|
|
@ -1185,6 +1185,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
|
||||
|
|
|
@ -121,4 +121,6 @@ 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*/
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -169,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 */
|
||||
|
|
|
@ -399,10 +399,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)
|
||||
{
|
||||
|
@ -410,9 +406,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)
|
||||
|
@ -1146,7 +1139,7 @@ void __init setup_arch(char **cmdline_p)
|
|||
|
||||
reserve_initrd();
|
||||
|
||||
early_initrd_acpi_init();
|
||||
acpi_table_upgrade();
|
||||
|
||||
vsmp_init();
|
||||
|
||||
|
|
|
@ -311,9 +311,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
|
||||
|
@ -521,4 +524,12 @@ config XPOWER_PMIC_OPREGION
|
|||
|
||||
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
|
||||
|
|
|
@ -99,5 +99,6 @@ 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_ACPI_CONFIGFS) += acpi_configfs.o
|
||||
|
||||
video-objs += acpi_video.o video_detect.o
|
||||
|
|
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* 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");
|
|
@ -990,6 +990,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 +1050,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();
|
||||
|
|
|
@ -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
|
||||
-------------------------------------------------------------------------- */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 @@ next_table:
|
|||
}
|
||||
}
|
||||
#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()
|
||||
*
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -107,12 +107,11 @@ struct acpi_i2c_lookup {
|
|||
acpi_handle device_handle;
|
||||
};
|
||||
|
||||
static int acpi_i2c_find_address(struct acpi_resource *ares, void *data)
|
||||
static int acpi_i2c_fill_info(struct acpi_resource *ares, void *data)
|
||||
{
|
||||
struct acpi_i2c_lookup *lookup = data;
|
||||
struct i2c_board_info *info = lookup->info;
|
||||
struct acpi_resource_i2c_serialbus *sb;
|
||||
acpi_handle adapter_handle;
|
||||
acpi_status status;
|
||||
|
||||
if (info->addr || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
|
||||
|
@ -122,80 +121,102 @@ static int acpi_i2c_find_address(struct acpi_resource *ares, void *data)
|
|||
if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Extract the ResourceSource and make sure that the handle matches
|
||||
* with the I2C adapter handle.
|
||||
*/
|
||||
status = acpi_get_handle(lookup->device_handle,
|
||||
sb->resource_source.string_ptr,
|
||||
&adapter_handle);
|
||||
if (ACPI_SUCCESS(status) && adapter_handle == lookup->adapter_handle) {
|
||||
info->addr = sb->slave_address;
|
||||
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
|
||||
info->flags |= I2C_CLIENT_TEN;
|
||||
}
|
||||
&lookup->adapter_handle);
|
||||
if (!ACPI_SUCCESS(status))
|
||||
return 1;
|
||||
|
||||
info->addr = sb->slave_address;
|
||||
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
|
||||
info->flags |= I2C_CLIENT_TEN;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
|
||||
void *data, void **return_value)
|
||||
static int acpi_i2c_get_info(struct acpi_device *adev,
|
||||
struct i2c_board_info *info,
|
||||
acpi_handle *adapter_handle)
|
||||
{
|
||||
struct i2c_adapter *adapter = data;
|
||||
struct list_head resource_list;
|
||||
struct acpi_i2c_lookup lookup;
|
||||
struct resource_entry *entry;
|
||||
struct i2c_board_info info;
|
||||
struct acpi_device *adev;
|
||||
struct acpi_i2c_lookup lookup;
|
||||
int ret;
|
||||
|
||||
if (acpi_bus_get_device(handle, &adev))
|
||||
return AE_OK;
|
||||
if (acpi_bus_get_status(adev) || !adev->status.present)
|
||||
return AE_OK;
|
||||
if (acpi_bus_get_status(adev) || !adev->status.present ||
|
||||
acpi_device_enumerated(adev))
|
||||
return -EINVAL;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.fwnode = acpi_fwnode_handle(adev);
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->fwnode = acpi_fwnode_handle(adev);
|
||||
|
||||
memset(&lookup, 0, sizeof(lookup));
|
||||
lookup.adapter_handle = ACPI_HANDLE(&adapter->dev);
|
||||
lookup.device_handle = handle;
|
||||
lookup.info = &info;
|
||||
lookup.device_handle = acpi_device_handle(adev);
|
||||
lookup.info = info;
|
||||
|
||||
/*
|
||||
* Look up for I2cSerialBus resource with ResourceSource that
|
||||
* matches with this adapter.
|
||||
*/
|
||||
/* Look up for I2cSerialBus resource */
|
||||
INIT_LIST_HEAD(&resource_list);
|
||||
ret = acpi_dev_get_resources(adev, &resource_list,
|
||||
acpi_i2c_find_address, &lookup);
|
||||
acpi_i2c_fill_info, &lookup);
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
if (ret < 0 || !info.addr)
|
||||
return AE_OK;
|
||||
if (ret < 0 || !info->addr)
|
||||
return -EINVAL;
|
||||
|
||||
*adapter_handle = lookup.adapter_handle;
|
||||
|
||||
/* Then fill IRQ number if any */
|
||||
ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return AE_OK;
|
||||
return -EINVAL;
|
||||
|
||||
resource_list_for_each_entry(entry, &resource_list) {
|
||||
if (resource_type(entry->res) == IORESOURCE_IRQ) {
|
||||
info.irq = entry->res->start;
|
||||
info->irq = entry->res->start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
strlcpy(info->type, dev_name(&adev->dev), sizeof(info->type));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_i2c_register_device(struct i2c_adapter *adapter,
|
||||
struct acpi_device *adev,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
adev->power.flags.ignore_parent = true;
|
||||
strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type));
|
||||
if (!i2c_new_device(adapter, &info)) {
|
||||
acpi_device_set_enumerated(adev);
|
||||
|
||||
if (!i2c_new_device(adapter, info)) {
|
||||
adev->power.flags.ignore_parent = false;
|
||||
dev_err(&adapter->dev,
|
||||
"failed to add I2C device %s from ACPI\n",
|
||||
dev_name(&adev->dev));
|
||||
}
|
||||
}
|
||||
|
||||
static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
|
||||
void *data, void **return_value)
|
||||
{
|
||||
struct i2c_adapter *adapter = data;
|
||||
struct acpi_device *adev;
|
||||
acpi_handle adapter_handle;
|
||||
struct i2c_board_info info;
|
||||
|
||||
if (acpi_bus_get_device(handle, &adev))
|
||||
return AE_OK;
|
||||
|
||||
if (acpi_i2c_get_info(adev, &info, &adapter_handle))
|
||||
return AE_OK;
|
||||
|
||||
if (adapter_handle != ACPI_HANDLE(&adapter->dev))
|
||||
return AE_OK;
|
||||
|
||||
acpi_i2c_register_device(adapter, adev, &info);
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
@ -225,8 +246,80 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap)
|
|||
dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");
|
||||
}
|
||||
|
||||
static int acpi_i2c_match_adapter(struct device *dev, void *data)
|
||||
{
|
||||
struct i2c_adapter *adapter = i2c_verify_adapter(dev);
|
||||
|
||||
if (!adapter)
|
||||
return 0;
|
||||
|
||||
return ACPI_HANDLE(dev) == (acpi_handle)data;
|
||||
}
|
||||
|
||||
static int acpi_i2c_match_device(struct device *dev, void *data)
|
||||
{
|
||||
return ACPI_COMPANION(dev) == data;
|
||||
}
|
||||
|
||||
static struct i2c_adapter *acpi_i2c_find_adapter_by_handle(acpi_handle handle)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
dev = bus_find_device(&i2c_bus_type, NULL, handle,
|
||||
acpi_i2c_match_adapter);
|
||||
return dev ? i2c_verify_adapter(dev) : NULL;
|
||||
}
|
||||
|
||||
static struct i2c_client *acpi_i2c_find_client_by_adev(struct acpi_device *adev)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
dev = bus_find_device(&i2c_bus_type, NULL, adev, acpi_i2c_match_device);
|
||||
return dev ? i2c_verify_client(dev) : NULL;
|
||||
}
|
||||
|
||||
static int acpi_i2c_notify(struct notifier_block *nb, unsigned long value,
|
||||
void *arg)
|
||||
{
|
||||
struct acpi_device *adev = arg;
|
||||
struct i2c_board_info info;
|
||||
acpi_handle adapter_handle;
|
||||
struct i2c_adapter *adapter;
|
||||
struct i2c_client *client;
|
||||
|
||||
switch (value) {
|
||||
case ACPI_RECONFIG_DEVICE_ADD:
|
||||
if (acpi_i2c_get_info(adev, &info, &adapter_handle))
|
||||
break;
|
||||
|
||||
adapter = acpi_i2c_find_adapter_by_handle(adapter_handle);
|
||||
if (!adapter)
|
||||
break;
|
||||
|
||||
acpi_i2c_register_device(adapter, adev, &info);
|
||||
break;
|
||||
case ACPI_RECONFIG_DEVICE_REMOVE:
|
||||
if (!acpi_device_enumerated(adev))
|
||||
break;
|
||||
|
||||
client = acpi_i2c_find_client_by_adev(adev);
|
||||
if (!client)
|
||||
break;
|
||||
|
||||
i2c_unregister_device(client);
|
||||
put_device(&client->dev);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block i2c_acpi_notifier = {
|
||||
.notifier_call = acpi_i2c_notify,
|
||||
};
|
||||
#else /* CONFIG_ACPI */
|
||||
static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { }
|
||||
extern struct notifier_block i2c_acpi_notifier;
|
||||
#endif /* CONFIG_ACPI */
|
||||
|
||||
#ifdef CONFIG_ACPI_I2C_OPREGION
|
||||
|
@ -1089,6 +1182,8 @@ void i2c_unregister_device(struct i2c_client *client)
|
|||
{
|
||||
if (client->dev.of_node)
|
||||
of_node_clear_flag(client->dev.of_node, OF_POPULATED);
|
||||
if (ACPI_COMPANION(&client->dev))
|
||||
acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev));
|
||||
device_unregister(&client->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_unregister_device);
|
||||
|
@ -2117,6 +2212,8 @@ static int __init i2c_init(void)
|
|||
|
||||
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
|
||||
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
|
||||
if (IS_ENABLED(CONFIG_ACPI))
|
||||
WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -2132,6 +2229,8 @@ bus_err:
|
|||
|
||||
static void __exit i2c_exit(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_ACPI))
|
||||
WARN_ON(acpi_reconfig_notifier_unregister(&i2c_acpi_notifier));
|
||||
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
|
||||
WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier));
|
||||
i2c_del_driver(&dummy_driver);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -208,7 +208,6 @@ void acpi_boot_table_init (void);
|
|||
int acpi_mps_check (void);
|
||||
int acpi_numa_init (void);
|
||||
|
||||
void early_acpi_table_init(void *data, size_t size);
|
||||
int acpi_table_init (void);
|
||||
int acpi_table_parse(char *id, acpi_tbl_table_handler handler);
|
||||
int __init acpi_parse_entries(char *id, unsigned long table_size,
|
||||
|
@ -546,6 +545,24 @@ void acpi_walk_dep_device_list(acpi_handle handle);
|
|||
struct platform_device *acpi_create_platform_device(struct acpi_device *);
|
||||
#define ACPI_PTR(_ptr) (_ptr)
|
||||
|
||||
static inline void acpi_device_set_enumerated(struct acpi_device *adev)
|
||||
{
|
||||
adev->flags.visited = true;
|
||||
}
|
||||
|
||||
static inline void acpi_device_clear_enumerated(struct acpi_device *adev)
|
||||
{
|
||||
adev->flags.visited = false;
|
||||
}
|
||||
|
||||
enum acpi_reconfig_event {
|
||||
ACPI_RECONFIG_DEVICE_ADD = 0,
|
||||
ACPI_RECONFIG_DEVICE_REMOVE,
|
||||
};
|
||||
|
||||
int acpi_reconfig_notifier_register(struct notifier_block *nb);
|
||||
int acpi_reconfig_notifier_unregister(struct notifier_block *nb);
|
||||
|
||||
#else /* !CONFIG_ACPI */
|
||||
|
||||
#define acpi_disabled 1
|
||||
|
@ -602,7 +619,6 @@ static inline const char *acpi_dev_name(struct acpi_device *adev)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline void early_acpi_table_init(void *data, size_t size) { }
|
||||
static inline void acpi_early_init(void) { }
|
||||
static inline void acpi_subsystem_init(void) { }
|
||||
|
||||
|
@ -692,6 +708,24 @@ static inline enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
|
|||
|
||||
#define ACPI_PTR(_ptr) (NULL)
|
||||
|
||||
static inline void acpi_device_set_enumerated(struct acpi_device *adev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void acpi_device_clear_enumerated(struct acpi_device *adev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int acpi_reconfig_notifier_register(struct notifier_block *nb)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline int acpi_reconfig_notifier_unregister(struct notifier_block *nb)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#endif /* !CONFIG_ACPI */
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
@ -1011,4 +1045,10 @@ static inline struct fwnode_handle *acpi_get_next_subnode(struct device *dev,
|
|||
#define acpi_probe_device_table(t) ({ int __r = 0; __r;})
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI_TABLE_UPGRADE
|
||||
void acpi_table_upgrade(void);
|
||||
#else
|
||||
static inline void acpi_table_upgrade(void) { }
|
||||
#endif
|
||||
|
||||
#endif /*_LINUX_ACPI_H*/
|
||||
|
|
Loading…
Reference in New Issue