diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-09 18:42:47 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-02-09 18:42:47 -0800 |
| commit | d84e173311c4f0b0300755e6445f3224d252eeed (patch) | |
| tree | b15bd9b06d52b38b60bfc7c997238f6e636e5405 | |
| parent | 4adc13ed7c281c16152a700e47b65d17de07321a (diff) | |
| parent | dfa5dc3ad3b15a519101f134ed76c068526004e4 (diff) | |
Merge tag 'acpi-6.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull ACPI updates from Rafael Wysocki:
"This one is significantly larger than previous ACPI support pull
requests because several significant updates have coincided in it.
First, there is a routine ACPICA code update, to upstream version
20251212, but this time it covers new ACPI 6.6 material that has not
been covered yet. Among other things, it includes definitions of a few
new ACPI tables and updates of some others, like the GICv5 MADT
structures and ARM IORT IWB node definitions that are used for adding
GICv5 ACPI probing on ARM (that technically is IRQ subsystem material,
but it depends on the ACPICA changes, so it is included here). The
latter alone adds a few hundred lines of new code.
Second, there is an update of ACPI _OSC handling including a fix that
prevents failures from occurring in some corner cases due to careless
handling of _OSC error bits.
On top of that, the "system resource" ACPI device objects with the
PNP0C01 and PNP0C02 are now going to be handled by the ACPI core
device enumeration code instead of handing them over to the legacy PNP
system driver which causes device enumeration issues to occur. Some of
those issues have been worked around in device drivers and elsewhere
and those workarounds should not be necessary any more, so they are
going away.
Moreover, the time has come to convert all "core ACPI" device drivers
that were still using struct acpi_driver objects for device binding
into proper platform drivers that use struct platform_driver for this
purpose. These updates are accompanied by some requisite core ACPI
device enumeration code changes.
Next, there are ACPI APEI updates, including changes to avoid excess
overhead in the NMI handler and in SEA on the ARM side, changes to
unify ACPI-based HW error tracing and logging, and changes to prevent
APEI code from reaching out of its allocated memory.
There are also some ACPI power management updates, mostly related to
the ACPI cpuidle support in the processor driver, suspend-to-idle
handling on systems with ACPI support and to ACPI PM of devices.
In addition to the above, bugs are fixed and the code is cleaned up in
assorted places all over.
Specifics:
- Update the ACPICA code in the kernel to upstream version 20251212
which includes the following changes:
* Add support for new ACPI table DTPR (Michal Camacho Romero)
* Release objects with acpi_ut_delete_object_desc() (Zilin Guan)
* Add UUIDs for Microsoft fan extensions and UUIDs associated with
TPM 2.0 devices (Armin Wolf)
* Fix NULL pointer dereference in acpi_ev_address_space_dispatch()
(Alexey Simakov)
* Add KEYP ACPI table definition (Dave Jiang)
* Add support for the Microsoft display mux _OSI string (Armin
Wolf)
* Add definitions for the IOVT ACPI table (Xianglai Li)
* Abort AML bytecode execution on AML_FATAL_OP (Armin Wolf)
* Include all fields in subtable type1 for PPTT (Ben Horgan)
* Add GICv5 MADT structures and Arm IORT IWB node definitions
(Jose Marinho)
* Update Parameter Block structure for RAS2 and add a new flag in
Memory Affinity Structure for SRAT (Pawel Chmielewski)
* Add _VDM (Voltage Domain) object (Pawel Chmielewski)
- Add support for GICv5 ACPI probing on ARM which is based on the
GICv5 MADT structures and ARM IORT IWB node definitions recently
added to ACPICA (Lorenzo Pieralisi)
- Rework ACPI PM notification setup for PCI root buses and modify the
ACPI PM setup for devices to register wakeup source objects under
physical (that is, PCI, platform, etc.) devices instead of doing
that under their ACPI companions (Rafael Wysocki)
- Adjust debug messages regarding postponed ACPI PM printed during
system resume to be more accurate (Rafael Wysocki)
- Remove dead code from lps0_device_attach() (Gergo Koteles)
- Start to invoke Microsoft Function 9 (Turn On Display) of the Low-
Power S0 Idle (LPS0) _DSM in the suspend-to-idle resume flow on
systems with ACPI LPS0 support to address a functional issue on
Lenovo Yoga Slim 7i Aura (15ILL9), where system fans and keyboard
backlights fail to resume after suspend (Jakob Riemenschneider)
- Add sysfs attribute cid for exposing _CID lists under ACPI device
objects (Rafael Wysocki)
- Replace sprintf() with sysfs_emit() in all of the core ACPI sysfs
interface code (Sumeet Pawnikar)
- Use acpi_get_local_u64_address() in the code implementing ACPI
support for PCI to evaluate _ADR instead of evaluating that object
directly (Andy Shevchenko)
- Add JWIPC JVC9100 to irq1_level_low_skip_override[] to unbreak
serial IRQs on that system (Ai Chao)
- Fix handling of _OSC errors in acpi_run_osc() to avoid failures on
systems where _OSC error bits are set even though the _OSC return
buffer contains acknowledged feature bits (Rafael Wysocki)
- Clean up and rearrange \_SB._OSC handling for general platform
features and USB4 features to avoid code duplication and
unnecessary memory management overhead (Rafael Wysocki)
- Make the ACPI core device enumeration code handle PNP0C01 and
PNP0C02 ("system resource") device objects directly instead of
letting the legacy PNP system driver handle them to avoid device
enumeration issues on systems where PNP0C02 is present in the _CID
list under ACPI device objects with a _HID matching a proper device
driver in Linux (Rafael Wysocki)
- Drop workarounds for the known device enumeration issues related to
_CID lists containing PNP0C02 (Rafael Wysocki)
- Drop outdated comment regarding removed function in the ACPI-based
device enumeration code (Julia Lawall)
- Make PRP0001 device matching work as expected for ACPI device
objects using it as a _HID for board development and similar
purposes (Kartik Rajput)
- Use async schedule function in acpi_scan_clear_dep_fn() to avoid
races with user space initialization on some systems (Yicong Yang)
- Add a piece of documentation explaining why binding drivers
directly to ACPI device objects is not a good idea in general and
why it is desirable to convert drivers doing so into proper
platform drivers that use struct platform_driver for device binding
(Rafael Wysocki)
- Convert multiple "core ACPI" drivers, including the NFIT ACPI
device driver, the generic ACPI button drivers, the generic ACPI
thermal zone driver, the ACPI hardware event device (HED) driver,
the ACPI EC driver, the ACPI SMBUS HC driver, the ACPI Smart
Battery Subsystem (SBS) driver, and the ACPI backlight (video)
driver to proper platform drivers that use struct platform_driver
for device binding (Rafael Wysocki)
- Use acpi_get_local_u64_address() in the ACPI backlight (video)
driver to evaluate _ADR instead of evaluating that object directly
(Andy Shevchenko)
- Convert the generic ACPI battery driver to a proper platform driver
using struct platform_driver for device binding (Rafael Wysocki)
- Fix incorrect charging status when current is zero in the generic
ACPI battery driver (Ata İlhan Köktürk)
- Use LIST_HEAD() for initializing a stack-allocated list in the
generic ACPI watchdog device driver (Can Peng)
- Rework the ACPI idle driver initialization to register it directly
from the common initialization code instead of doing that from a
CPU hotplug "online" callback and clean it up (Huisong Li, Rafael
Wysocki)
- Fix a possible NULL pointer dereference in
acpi_processor_errata_piix4() (Tuo Li)
- Make read-only array non_mmio_desc[] static const (Colin Ian King)
- Prevent the APEI GHES support code on ARM from accessing memory out
of bounds or going past the ARM processor CPER record buffer (Mauro
Carvalho Chehab)
- Prevent cper_print_fw_err() from dumping the entire memory on
systems with defective firmware (Mauro Carvalho Chehab)
- Improve ghes_notify_nmi() status check to avoid unnecessary
overhead in the NMI handler by carrying out all of the requisite
preparations and the NMI registration time (Tony Luck)
- Refactor the GHES driver by extracting common functionality into
reusable helper functions to reduce code duplication and improve
the ghes_notify_sea() status check in analogy with the previous
ghes_notify_nmi() status check improvement (Shuai Xue)
- Make ELOG and GHES log and trace consistently and support the CPER
CXL protocol analogously (Fabio De Francesco)
- Disable KASAN instrumentation in the APEI GHES driver when compile
testing with clang < 18 (Nathan Chancellor)
- Let ghes_edac be the preferred driver to load on __ZX__ and _BYO_
systems by extending the platform detection list in the APEI GHES
driver (Tony W Wang-oc)
- Clean up cppc_perf_caps and cppc_perf_ctrls structs and rename EPP
constants for clarity in the ACPI CPPC library (Sumit Gupta)"
* tag 'acpi-6.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (117 commits)
ACPI: battery: fix incorrect charging status when current is zero
ACPI: scan: Use async schedule function in acpi_scan_clear_dep_fn()
ACPI: x86: s2idle: Invoke Microsoft _DSM Function 9 (Turn On Display)
ACPI: APEI: GHES: Add ghes_edac support for __ZX__ and _BYO_ systems
ACPI: APEI: GHES: Disable KASAN instrumentation when compile testing with clang < 18
ACPI: sysfs: Replace sprintf() with sysfs_emit()
ACPI: CPPC: Rename EPP constants for clarity
ACPI: CPPC: Clean up cppc_perf_caps and cppc_perf_ctrls structs
ACPI: processor: idle: Rework the handling of acpi_processor_ffh_lpi_probe()
ACPI: processor: idle: Convert acpi_processor_setup_cpuidle_dev() to void
ACPI: processor: idle: Convert acpi_processor_setup_cpuidle_states() to void
irqchip/gic-v5: Add ACPI IWB probing
irqchip/gic-v5: Add ACPI ITS probing
irqchip/gic-v5: Add ACPI IRS probing
irqchip/gic-v5: Split IRS probing into OF and generic portions
PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic
irqdomain: Add parent field to struct irqchip_fwid
ACPI: PCI: simplify code with acpi_get_local_u64_address()
ACPI: video: simplify code with acpi_get_local_u64_address()
ACPI: PM: Adjust messages regarding postponed ACPI PM
...
72 files changed, 2573 insertions, 1111 deletions
diff --git a/Documentation/driver-api/acpi/acpi-drivers.rst b/Documentation/driver-api/acpi/acpi-drivers.rst new file mode 100644 index 000000000000..b1fbbddb8b4f --- /dev/null +++ b/Documentation/driver-api/acpi/acpi-drivers.rst @@ -0,0 +1,80 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: <isonum.txt> + +========================================= +Why using ACPI drivers is not a good idea +========================================= + +:Copyright: |copy| 2026, Intel Corporation + +:Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> + +Even though binding drivers directly to struct acpi_device objects, also +referred to as "ACPI device nodes", allows basic functionality to be provided +at least in some cases, there are problems with it, related to general +consistency, sysfs layout, power management operation ordering, and code +cleanliness. + +First of all, ACPI device nodes represent firmware entities rather than +hardware and in many cases they provide auxiliary information on devices +enumerated independently (like PCI devices or CPUs). It is therefore generally +questionable to assign resources to them because the entities represented by +them do not decode addresses in the memory or I/O address spaces and do not +generate interrupts or similar (all of that is done by hardware). + +Second, as a general rule, a struct acpi_device can only be a parent of another +struct acpi_device. If that is not the case, the location of the child device +in the device hierarchy is at least confusing and it may not be straightforward +to identify the piece of hardware providing functionality represented by it. +However, binding a driver directly to an ACPI device node may cause that to +happen if the given driver registers input devices or wakeup sources under it, +for example. + +Next, using system suspend and resume callbacks directly on ACPI device nodes +is also questionable because it may cause ordering problems to appear. Namely, +ACPI device nodes are registered before enumerating hardware corresponding to +them and they land on the PM list in front of the majority of other device +objects. Consequently, the execution ordering of their PM callbacks may be +different from what is generally expected. Also, in general, dependencies +returned by _DEP objects do not affect ACPI device nodes themselves, but the +"physical" devices associated with them, which potentially is one more source +of inconsistency related to treating ACPI device nodes as "real" device +representation. + +All of the above means that binding drivers to ACPI device nodes should +generally be avoided and so struct acpi_driver objects should not be used. + +Moreover, a device ID is necessary to bind a driver directly to an ACPI device +node, but device IDs are not generally associated with all of them. Some of +them contain alternative information allowing the corresponding pieces of +hardware to be identified, for example represeted by an _ADR object return +value, and device IDs are not used in those cases. In consequence, confusingly +enough, binding an ACPI driver to an ACPI device node may even be impossible. + +When that happens, the piece of hardware corresponding to the given ACPI device +node is represented by another device object, like a struct pci_dev, and the +ACPI device node is the "ACPI companion" of that device, accessible through its +fwnode pointer used by the ACPI_COMPANION() macro. The ACPI companion holds +additional information on the device configuration and possibly some "recipes" +on device manipulation in the form of AML (ACPI Machine Language) bytecode +provided by the platform firmware. Thus the role of the ACPI device node is +similar to the role of a struct device_node on a system where Device Tree is +used for platform description. + +For consistency, this approach has been extended to the cases in which ACPI +device IDs are used. Namely, in those cases, an additional device object is +created to represent the piece of hardware corresponding to a given ACPI device +node. By default, it is a platform device, but it may also be a PNP device, a +CPU device, or another type of device, depending on what the given piece of +hardware actually is. There are even cases in which multiple devices are +"backed" or "accompanied" by one ACPI device node (e.g. ACPI device nodes +corresponding to GPUs that may provide firmware interfaces for backlight +brightness control in addition to GPU configuration information). + +This means that it really should never be necessary to bind a driver directly to +an ACPI device node because there is a "proper" device object representing the +corresponding piece of hardware that can be bound to by a "proper" driver using +the given ACPI device node as the device's ACPI companion. Thus, in principle, +there is no reason to use ACPI drivers and if they all were replaced with other +driver types (for example, platform drivers), some code could be dropped and +some complexity would go away. diff --git a/Documentation/driver-api/acpi/index.rst b/Documentation/driver-api/acpi/index.rst index ace0008e54c2..2b10d83f9994 100644 --- a/Documentation/driver-api/acpi/index.rst +++ b/Documentation/driver-api/acpi/index.rst @@ -7,3 +7,4 @@ ACPI Support linuxized-acpica scan_handlers + acpi-drivers diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ca00a5dbcf75..df0ff0764d0d 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -494,6 +494,8 @@ config ACPI_EXTLOG tristate "Extended Error Log support" depends on X86_MCE && X86_LOCAL_APIC && EDAC select UEFI_CPER + select ACPI_APEI + select ACPI_APEI_GHES help Certain usages such as Predictive Failure Analysis (PFA) require more information about the error than what can be described in diff --git a/drivers/acpi/acpi_extlog.c b/drivers/acpi/acpi_extlog.c index f6b9562779de..7ad3b36013cc 100644 --- a/drivers/acpi/acpi_extlog.c +++ b/drivers/acpi/acpi_extlog.c @@ -12,6 +12,7 @@ #include <linux/ratelimit.h> #include <linux/edac.h> #include <linux/ras.h> +#include <cxl/event.h> #include <acpi/ghes.h> #include <asm/cpu.h> #include <asm/mce.h> @@ -132,6 +133,53 @@ static int print_extlog_rcd(const char *pfx, return 1; } +static void extlog_print_pcie(struct cper_sec_pcie *pcie_err, + int severity) +{ +#ifdef ACPI_APEI_PCIEAER + struct aer_capability_regs *aer; + struct pci_dev *pdev; + unsigned int devfn; + unsigned int bus; + int aer_severity; + int domain; + + if (!(pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && + pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO)) + return; + + aer_severity = cper_severity_to_aer(severity); + aer = (struct aer_capability_regs *)pcie_err->aer_info; + domain = pcie_err->device_id.segment; + bus = pcie_err->device_id.bus; + devfn = PCI_DEVFN(pcie_err->device_id.device, + pcie_err->device_id.function); + pdev = pci_get_domain_bus_and_slot(domain, bus, devfn); + if (!pdev) + return; + + pci_print_aer(pdev, aer_severity, aer); + pci_dev_put(pdev); +#endif +} + +static void +extlog_cxl_cper_handle_prot_err(struct cxl_cper_sec_prot_err *prot_err, + int severity) +{ +#ifdef ACPI_APEI_PCIEAER + struct cxl_cper_prot_err_work_data wd; + + if (cxl_cper_sec_prot_err_valid(prot_err)) + return; + + if (cxl_cper_setup_prot_err_work_data(&wd, prot_err, severity)) + return; + + cxl_cper_handle_prot_err(&wd); +#endif +} + static int extlog_print(struct notifier_block *nb, unsigned long val, void *data) { @@ -183,6 +231,22 @@ static int extlog_print(struct notifier_block *nb, unsigned long val, if (gdata->error_data_length >= sizeof(*mem)) trace_extlog_mem_event(mem, err_seq, fru_id, fru_text, (u8)gdata->error_severity); + } else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) { + struct cxl_cper_sec_prot_err *prot_err = + acpi_hest_get_payload(gdata); + + extlog_cxl_cper_handle_prot_err(prot_err, + gdata->error_severity); + } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) { + struct cper_sec_pcie *pcie_err = acpi_hest_get_payload(gdata); + + extlog_print_pcie(pcie_err, gdata->error_severity); + } else { + void *err = acpi_hest_get_payload(gdata); + + log_non_standard_event(sec_type, fru_id, fru_text, + gdata->error_severity, err, + gdata->error_data_length); } } diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 48d15dd785f6..0ec1afc744f5 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -114,13 +114,11 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, struct platform_device *pdev = NULL; struct platform_device_info pdevinfo; const struct acpi_device_id *match; - struct resource_entry *rentry; - struct list_head resource_list; struct resource *resources = NULL; - int count; + int count = 0; /* If the ACPI node already has a physical device attached, skip it. */ - if (adev->physical_node_count) + if (adev->physical_node_count && !adev->pnp.type.backlight) return NULL; match = acpi_match_acpi_device(forbidden_id_list, adev); @@ -137,22 +135,28 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, } } - INIT_LIST_HEAD(&resource_list); - count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); - if (count < 0) - return NULL; - if (count > 0) { - resources = kcalloc(count, sizeof(*resources), GFP_KERNEL); - if (!resources) { + if (adev->device_type == ACPI_BUS_TYPE_DEVICE && !adev->pnp.type.backlight) { + LIST_HEAD(resource_list); + + count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); + if (count < 0) + return ERR_PTR(-ENODATA); + + if (count > 0) { + struct resource_entry *rentry; + + resources = kcalloc(count, sizeof(*resources), GFP_KERNEL); + if (!resources) { + acpi_dev_free_resource_list(&resource_list); + return ERR_PTR(-ENOMEM); + } + count = 0; + list_for_each_entry(rentry, &resource_list, node) + acpi_platform_fill_resource(adev, rentry->res, + &resources[count++]); + acpi_dev_free_resource_list(&resource_list); - return ERR_PTR(-ENOMEM); } - count = 0; - list_for_each_entry(rentry, &resource_list, node) - acpi_platform_fill_resource(adev, rentry->res, - &resources[count++]); - - acpi_dev_free_resource_list(&resource_list); } memset(&pdevinfo, 0, sizeof(pdevinfo)); diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index 4ad88187dc7a..85d9f78619a2 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c @@ -125,9 +125,6 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = { {"PNP0401"}, /* ECP Printer Port */ /* apple-gmux */ {"APP000B"}, - /* system */ - {"PNP0c02"}, /* General ID for reserving resources */ - {"PNP0c01"}, /* memory controller */ /* rtc_cmos */ {"PNP0b00"}, {"PNP0b01"}, @@ -346,24 +343,10 @@ static bool acpi_pnp_match(const char *idstr, const struct acpi_device_id **matc return false; } -/* - * If one of the device IDs below is present in the list of device IDs of a - * given ACPI device object, the PNP scan handler will not attach to that - * object, because there is a proper non-PNP driver in the kernel for the - * device represented by it. - */ -static const struct acpi_device_id acpi_nonpnp_device_ids[] = { - {"INT3F0D"}, - {"INTC1080"}, - {"INTC1081"}, - {"INTC1099"}, - {""}, -}; - static int acpi_pnp_attach(struct acpi_device *adev, const struct acpi_device_id *id) { - return !!acpi_match_device_ids(adev, acpi_nonpnp_device_ids); + return true; } static struct acpi_scan_handler acpi_pnp_handler = { diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 7ec1dc04fd11..85096ce7b658 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -50,6 +50,7 @@ static int acpi_processor_errata_piix4(struct pci_dev *dev) { u8 value1 = 0; u8 value2 = 0; + struct pci_dev *ide_dev = NULL, *isa_dev = NULL; if (!dev) @@ -107,12 +108,12 @@ static int acpi_processor_errata_piix4(struct pci_dev *dev) * each IDE controller's DMA status to make sure we catch all * DMA activity. */ - dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + ide_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, PCI_ANY_ID, PCI_ANY_ID, NULL); - if (dev) { - errata.piix4.bmisx = pci_resource_start(dev, 4); - pci_dev_put(dev); + if (ide_dev) { + errata.piix4.bmisx = pci_resource_start(ide_dev, 4); + pci_dev_put(ide_dev); } /* @@ -124,24 +125,25 @@ static int acpi_processor_errata_piix4(struct pci_dev *dev) * disable C3 support if this is enabled, as some legacy * devices won't operate well if fast DMA is disabled. */ - dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + isa_dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, PCI_ANY_ID, PCI_ANY_ID, NULL); - if (dev) { - pci_read_config_byte(dev, 0x76, &value1); - pci_read_config_byte(dev, 0x77, &value2); + if (isa_dev) { + pci_read_config_byte(isa_dev, 0x76, &value1); + pci_read_config_byte(isa_dev, 0x77, &value2); if ((value1 & 0x80) || (value2 & 0x80)) errata.piix4.fdma = 1; - pci_dev_put(dev); + pci_dev_put(isa_dev); } break; } - if (errata.piix4.bmisx) - dev_dbg(&dev->dev, "Bus master activity detection (BM-IDE) erratum enabled\n"); - if (errata.piix4.fdma) - dev_dbg(&dev->dev, "Type-F DMA livelock erratum (C3 disabled)\n"); + if (ide_dev) + dev_dbg(&ide_dev->dev, "Bus master activity detection (BM-IDE) erratum enabled\n"); + + if (isa_dev) + dev_dbg(&isa_dev->dev, "Type-F DMA livelock erratum (C3 disabled)\n"); return 0; } diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index be8e7e18abca..69469757b965 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -21,6 +21,7 @@ #include <linux/sort.h> #include <linux/pci.h> #include <linux/pci_ids.h> +#include <linux/platform_device.h> #include <linux/slab.h> #include <linux/dmi.h> #include <linux/suspend.h> @@ -76,8 +77,8 @@ static int register_count; static DEFINE_MUTEX(register_count_mutex); static DEFINE_MUTEX(video_list_lock); static LIST_HEAD(video_bus_head); -static int acpi_video_bus_add(struct acpi_device *device); -static void acpi_video_bus_remove(struct acpi_device *device); +static int acpi_video_bus_probe(struct platform_device *pdev); +static void acpi_video_bus_remove(struct platform_device *pdev); static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data); /* @@ -98,14 +99,13 @@ static const struct acpi_device_id video_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, video_device_ids); -static struct acpi_driver acpi_video_bus = { - .name = "video", - .class = ACPI_VIDEO_CLASS, - .ids = video_device_ids, - .ops = { - .add = acpi_video_bus_add, - .remove = acpi_video_bus_remove, - }, +static struct platform_driver acpi_video_bus = { + .probe = acpi_video_bus_probe, + .remove = acpi_video_bus_remove, + .driver = { + .name = "acpi-video", + .acpi_match_table = video_device_ids, + }, }; struct acpi_video_bus_flags { @@ -1134,13 +1134,11 @@ static int acpi_video_bus_get_one_device(struct acpi_device *device, void *arg) struct acpi_video_bus *video = arg; struct acpi_video_device_attrib *attribute; struct acpi_video_device *data; - unsigned long long device_id; - acpi_status status; int device_type; + u64 device_id; - status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); /* Skip devices without _ADR instead of failing. */ - if (ACPI_FAILURE(status)) + if (acpi_get_local_u64_address(device->handle, &device_id)) goto exit; data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); @@ -1540,14 +1538,11 @@ static int acpi_video_bus_stop_devices(struct acpi_video_bus *video) static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct acpi_video_bus *video = acpi_driver_data(device); + struct acpi_video_bus *video = data; + struct acpi_device *device = video->device; struct input_dev *input; int keycode = 0; - if (!video || !video->input) - return; - input = video->input; switch (event) { @@ -1891,7 +1886,8 @@ static void acpi_video_dev_add_notify_handler(struct acpi_video_device *device) device->flags.notify = 1; } -static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video) +static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video, + struct platform_device *pdev) { struct input_dev *input; struct acpi_video_device *dev; @@ -1914,7 +1910,7 @@ static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video) input->phys = video->phys; input->id.bustype = BUS_HOST; input->id.product = 0x06; - input->dev.parent = &video->device->dev; + input->dev.parent = &pdev->dev; input->evbit[0] = BIT(EV_KEY); set_bit(KEY_SWITCHVIDEOMODE, input->keybit); set_bit(KEY_VIDEO_NEXT, input->keybit); @@ -1986,8 +1982,9 @@ static int acpi_video_bus_put_devices(struct acpi_video_bus *video) static int instance; -static int acpi_video_bus_add(struct acpi_device *device) +static int acpi_video_bus_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_video_bus *video; bool auto_detect; int error; @@ -2024,6 +2021,8 @@ static int acpi_video_bus_add(struct acpi_device *device) instance++; } + platform_set_drvdata(pdev, video); + video->device = device; strscpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); strscpy(acpi_device_class(device), ACPI_VIDEO_CLASS); @@ -2071,12 +2070,12 @@ static int acpi_video_bus_add(struct acpi_device *device) !auto_detect) acpi_video_bus_register_backlight(video); - error = acpi_video_bus_add_notify_handler(video); + error = acpi_video_bus_add_notify_handler(video, pdev); if (error) goto err_del; error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_video_bus_notify, device); + acpi_video_bus_notify, video); if (error) goto err_remove; @@ -2099,15 +2098,10 @@ err_free_video: return error; } -static void acpi_video_bus_remove(struct acpi_device *device) +static void acpi_video_bus_remove(struct platform_device *pdev) { - struct acpi_video_bus *video = NULL; - - - if (!device || !acpi_driver_data(device)) - return; - - video = acpi_driver_data(device); + struct acpi_video_bus *video = platform_get_drvdata(pdev); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, acpi_video_bus_notify); @@ -2170,7 +2164,7 @@ int acpi_video_register(void) dmi_check_system(video_dmi_table); - ret = acpi_bus_register_driver(&acpi_video_bus); + ret = platform_driver_register(&acpi_video_bus); if (ret) goto leave; @@ -2190,7 +2184,7 @@ void acpi_video_unregister(void) { mutex_lock(®ister_count_mutex); if (register_count) { - acpi_bus_unregister_driver(&acpi_video_bus); + platform_driver_unregister(&acpi_video_bus); register_count = 0; may_report_brightness_keys = false; } diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c index 14b24157799c..709993c535d1 100644 --- a/drivers/acpi/acpi_watchdog.c +++ b/drivers/acpi/acpi_watchdog.c @@ -103,7 +103,7 @@ void __init acpi_watchdog_init(void) { const struct acpi_wdat_entry *entries; const struct acpi_table_wdat *wdat; - struct list_head resource_list; + LIST_HEAD(resource_list); struct resource_entry *rentry; struct platform_device *pdev; struct resource *resources; @@ -125,8 +125,6 @@ void __init acpi_watchdog_init(void) wdat->pci_device != 0xff || wdat->pci_function != 0xff) goto fail_put_wdat; - INIT_LIST_HEAD(&resource_list); - entries = (struct acpi_wdat_entry *)(wdat + 1); for (i = 0; i < wdat->entries; i++) { const struct acpi_generic_address *gas; diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index da2c45880cc7..5f70b196e0aa 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -587,6 +587,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each (var Ints) */ PACKAGE_INFO(ACPI_PTYPE2_MIN, ACPI_RTYPE_INTEGER, 5, 0, 0, 0), + {{"_VDM", METHOD_0ARGS, + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + {{"_HRV", METHOD_0ARGS, METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c index fa3475da7ea9..b6198f73c81d 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -163,7 +163,9 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, return_ACPI_STATUS(AE_NOT_EXIST); } - if (region_obj->region.space_id == ACPI_ADR_SPACE_PLATFORM_COMM) { + if (field_obj + && region_obj->region.space_id == + ACPI_ADR_SPACE_PLATFORM_COMM) { struct acpi_pcc_info *ctx = handler_desc->address_space.context; diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c index bf08110ed6d2..2fc8070814e3 100644 --- a/drivers/acpi/acpica/exoparg3.c +++ b/drivers/acpi/acpica/exoparg3.c @@ -10,6 +10,7 @@ #include <acpi/acpi.h> #include "accommon.h" #include "acinterp.h" +#include <acpi/acoutput.h> #include "acparser.h" #include "amlcode.h" @@ -51,8 +52,7 @@ ACPI_MODULE_NAME("exoparg3") acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state) { union acpi_operand_object **operand = &walk_state->operands[0]; - struct acpi_signal_fatal_info *fatal; - acpi_status status = AE_OK; + struct acpi_signal_fatal_info fatal; ACPI_FUNCTION_TRACE_STR(ex_opcode_3A_0T_0R, acpi_ps_get_opcode_name(walk_state->opcode)); @@ -60,28 +60,30 @@ acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state) switch (walk_state->opcode) { case AML_FATAL_OP: /* Fatal (fatal_type fatal_code fatal_arg) */ - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "FatalOp: Type %X Code %X Arg %X " - "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", - (u32)operand[0]->integer.value, - (u32)operand[1]->integer.value, - (u32)operand[2]->integer.value)); - - fatal = ACPI_ALLOCATE(sizeof(struct acpi_signal_fatal_info)); - if (fatal) { - fatal->type = (u32) operand[0]->integer.value; - fatal->code = (u32) operand[1]->integer.value; - fatal->argument = (u32) operand[2]->integer.value; - } + fatal.type = (u32)operand[0]->integer.value; + fatal.code = (u32)operand[1]->integer.value; + fatal.argument = (u32)operand[2]->integer.value; - /* Always signal the OS! */ + ACPI_BIOS_ERROR((AE_INFO, + "Fatal ACPI BIOS error (Type 0x%X Code 0x%X Arg 0x%X)\n", + fatal.type, fatal.code, fatal.argument)); - status = acpi_os_signal(ACPI_SIGNAL_FATAL, fatal); + /* Always signal the OS! */ - /* Might return while OS is shutting down, just continue */ + acpi_os_signal(ACPI_SIGNAL_FATAL, &fatal); - ACPI_FREE(fatal); - goto cleanup; +#ifndef ACPI_CONTINUE_ON_FATAL + /* + * Might return while OS is shutting down, so abort the AML execution + * by returning an error. + */ + return_ACPI_STATUS(AE_ERROR); +#else + /* + * The alstests require that the Fatal() opcode does not return an error. + */ + return_ACPI_STATUS(AE_OK); +#endif case AML_EXTERNAL_OP: /* @@ -93,21 +95,16 @@ acpi_status acpi_ex_opcode_3A_0T_0R(struct acpi_walk_state *walk_state) * wrong if an external opcode ever gets here. */ ACPI_ERROR((AE_INFO, "Executed External Op")); - status = AE_OK; - goto cleanup; + + return_ACPI_STATUS(AE_OK); default: ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", walk_state->opcode)); - status = AE_AML_BAD_OPCODE; - goto cleanup; + return_ACPI_STATUS(AE_AML_BAD_OPCODE); } - -cleanup: - - return_ACPI_STATUS(status); } /******************************************************************************* diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index 1db831545ec8..b6895a48ae68 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -601,7 +601,7 @@ acpi_status acpi_install_method(u8 *buffer) error_exit: ACPI_FREE(aml_buffer); - ACPI_FREE(method_obj); + acpi_ut_delete_object_desc(method_obj); return (status); } ACPI_EXPORT_SYMBOL(acpi_install_method) diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c index 272e46208263..8362204b57b5 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -148,7 +148,7 @@ union acpi_operand_object *acpi_ut_create_package_object(u32 count) package_elements = ACPI_ALLOCATE_ZEROED(((acpi_size)count + 1) * sizeof(void *)); if (!package_elements) { - ACPI_FREE(package_desc); + acpi_ut_delete_object_desc(package_desc); return_PTR(NULL); } diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c index f6ac16729e42..88d04183ad0a 100644 --- a/drivers/acpi/acpica/utosi.c +++ b/drivers/acpi/acpica/utosi.c @@ -92,7 +92,11 @@ static struct acpi_interface_info acpi_default_supported_interfaces[] = { {"Processor Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, {"3.0 Thermal Model", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, {"3.0 _SCP Extensions", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, - {"Processor Aggregator Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0} + {"Processor Aggregator Device", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0}, + + /* See https://learn.microsoft.com/en-us/windows-hardware/drivers/display/automatic-display-switch */ + + {"DisplayMux", NULL, ACPI_OSI_OPTIONAL_FEATURE, 0} }; /******************************************************************************* diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile index 2c474e6477e1..1a0b85923cd4 100644 --- a/drivers/acpi/apei/Makefile +++ b/drivers/acpi/apei/Makefile @@ -1,6 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_ACPI_APEI) += apei.o obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o +# clang versions prior to 18 may blow out the stack with KASAN +ifeq ($(CONFIG_COMPILE_TEST)_$(CONFIG_CC_IS_CLANG)_$(call clang-min-version, 180000),y_y_) +KASAN_SANITIZE_ghes.o := n +endif +obj-$(CONFIG_ACPI_APEI_PCIEAER) += ghes_helpers.o obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o einj-y := einj-core.o einj-$(CONFIG_ACPI_APEI_EINJ_CXL) += einj-cxl.o diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c index 305c240a303f..f5bfdffe1e43 100644 --- a/drivers/acpi/apei/einj-core.c +++ b/drivers/acpi/apei/einj-core.c @@ -679,7 +679,7 @@ static bool is_allowed_range(u64 base_addr, u64 size) * region intersects with known resource. So do an allow list check for * IORES_DESCs that definitely or most likely not MMIO. */ - int non_mmio_desc[] = { + static const int non_mmio_desc[] = { IORES_DESC_CRASH_KERNEL, IORES_DESC_ACPI_TABLES, IORES_DESC_ACPI_NV_STORAGE, diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 0dc767392a6c..f96aede5d9a3 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -29,6 +29,7 @@ #include <linux/cper.h> #include <linux/cleanup.h> #include <linux/platform_device.h> +#include <linux/minmax.h> #include <linux/mutex.h> #include <linux/ratelimit.h> #include <linux/vmalloc.h> @@ -294,6 +295,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) error_block_length = GHES_ESTATUS_MAX_SIZE; } ghes->estatus = kmalloc(error_block_length, GFP_KERNEL); + ghes->estatus_length = error_block_length; if (!ghes->estatus) { rc = -ENOMEM; goto err_unmap_status_addr; @@ -365,13 +367,15 @@ static int __ghes_check_estatus(struct ghes *ghes, struct acpi_hest_generic_status *estatus) { u32 len = cper_estatus_len(estatus); + u32 max_len = min(ghes->generic->error_block_length, + ghes->estatus_length); if (len < sizeof(*estatus)) { pr_warn_ratelimited(FW_WARN GHES_PFX "Truncated error status block!\n"); return -EIO; } - if (len > ghes->generic->error_block_length) { + if (!len || len > max_len) { pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid error status block length!\n"); return -EIO; } @@ -552,21 +556,45 @@ static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, { struct cper_sec_proc_arm *err = acpi_hest_get_payload(gdata); int flags = sync ? MF_ACTION_REQUIRED : 0; + int length = gdata->error_data_length; char error_type[120]; bool queued = false; int sec_sev, i; char *p; sec_sev = ghes_severity(gdata->error_severity); - log_arm_hw_error(err, sec_sev); + if (length >= sizeof(*err)) { + log_arm_hw_error(err, sec_sev); + } else { + pr_warn(FW_BUG "arm error length: %d\n", length); + pr_warn(FW_BUG "length is too small\n"); + pr_warn(FW_BUG "firmware-generated error record is incorrect\n"); + return false; + } + if (sev != GHES_SEV_RECOVERABLE || sec_sev != GHES_SEV_RECOVERABLE) return false; p = (char *)(err + 1); + length -= sizeof(err); + for (i = 0; i < err->err_info_num; i++) { - struct cper_arm_err_info *err_info = (struct cper_arm_err_info *)p; - bool is_cache = err_info->type & CPER_ARM_CACHE_ERROR; - bool has_pa = (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADDR); + struct cper_arm_err_info *err_info; + bool is_cache, has_pa; + + /* Ensure we have enough data for the error info header */ + if (length < sizeof(*err_info)) + break; + + err_info = (struct cper_arm_err_info *)p; + + /* Validate the claimed length before using it */ + length -= err_info->length; + if (length < 0) + break; + + is_cache = err_info->type & CPER_ARM_CACHE_ERROR; + has_pa = (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADDR); /* * The field (err_info->error_info & BIT(26)) is fixed to set to @@ -711,53 +739,17 @@ static void cxl_cper_post_prot_err(struct cxl_cper_sec_prot_err *prot_err, { #ifdef CONFIG_ACPI_APEI_PCIEAER struct cxl_cper_prot_err_work_data wd; - u8 *dvsec_start, *cap_start; - if (!(prot_err->valid_bits & PROT_ERR_VALID_AGENT_ADDRESS)) { - pr_err_ratelimited("CXL CPER invalid agent type\n"); + if (cxl_cper_sec_prot_err_valid(prot_err)) return; - } - - if (!(prot_err->valid_bits & PROT_ERR_VALID_ERROR_LOG)) { - pr_err_ratelimited("CXL CPER invalid protocol error log\n"); - return; - } - - if (prot_err->err_len != sizeof(struct cxl_ras_capability_regs)) { - pr_err_ratelimited("CXL CPER invalid RAS Cap size (%u)\n", - prot_err->err_len); - return; - } - - if (!(prot_err->valid_bits & PROT_ERR_VALID_SERIAL_NUMBER)) - pr_warn(FW_WARN "CXL CPER no device serial number\n"); guard(spinlock_irqsave)(&cxl_cper_prot_err_work_lock); if (!cxl_cper_prot_err_work) return; - switch (prot_err->agent_type) { - case RCD: - case DEVICE: - case LD: - case FMLD: - case RP: - case DSP: - case USP: - memcpy(&wd.prot_err, prot_err, sizeof(wd.prot_err)); - - dvsec_start = (u8 *)(prot_err + 1); - cap_start = dvsec_start + prot_err->dvsec_len; - - memcpy(&wd.ras_cap, cap_start, sizeof(wd.ras_cap)); - wd.severity = cper_severity_to_aer(severity); - break; - default: - pr_err_ratelimited("CXL CPER invalid agent type: %d\n", - prot_err->agent_type); + if (cxl_cper_setup_prot_err_work_data(&wd, prot_err, severity)) return; - } if (!kfifo_put(&cxl_cper_prot_err_fifo, wd)) { pr_err_ratelimited("CXL CPER kfifo overflow\n"); @@ -1406,6 +1398,71 @@ static int ghes_in_nmi_spool_from_list(struct list_head *rcu_list, return ret; } +/** + * ghes_has_active_errors - Check if there are active errors in error sources + * @ghes_list: List of GHES entries to check for active errors + * + * This function iterates through all GHES entries in the given list and + * checks if any of them has active error status by reading the error + * status register. + * + * Return: true if at least one source has active error, false otherwise. + */ +static bool __maybe_unused ghes_has_active_errors(struct list_head *ghes_list) +{ + struct ghes *ghes; + + guard(rcu)(); + list_for_each_entry_rcu(ghes, ghes_list, list) { + if (ghes->error_status_vaddr && + readl(ghes->error_status_vaddr)) + return true; + } + + return false; +} + +/** + * ghes_map_error_status - Map error status address to virtual address + * @ghes: pointer to GHES structure + * + * Reads the error status address from ACPI HEST table and maps it to a virtual + * address that can be accessed by the kernel. + * + * Return: 0 on success, error code on failure. + */ +static int __maybe_unused ghes_map_error_status(struct ghes *ghes) +{ + struct acpi_hest_generic *g = ghes->generic; + u64 paddr; + int rc; + + rc = apei_read(&paddr, &g->error_status_address); + if (rc) + return rc; + + ghes->error_status_vaddr = + acpi_os_ioremap(paddr, sizeof(ghes->estatus->block_status)); + if (!ghes->error_status_vaddr) + return -EINVAL; + + return 0; +} + +/** + * ghes_unmap_error_status - Unmap error status virtual address + * @ghes: pointer to GHES structure + * + * Unmaps the error status address if it was previously mapped. + */ +static void __maybe_unused ghes_unmap_error_status(struct ghes *ghes) +{ + if (ghes->error_status_vaddr) { + iounmap(ghes->error_status_vaddr); + ghes->error_status_vaddr = NULL; + } +} + #ifdef CONFIG_ACPI_APEI_SEA static LIST_HEAD(ghes_sea); @@ -1418,6 +1475,9 @@ int ghes_notify_sea(void) static DEFINE_RAW_SPINLOCK(ghes_notify_lock_sea); int rv; + if (!ghes_has_active_errors(&ghes_sea)) + return -ENOENT; + raw_spin_lock(&ghes_notify_lock_sea); rv = ghes_in_nmi_spool_from_list(&ghes_sea, FIX_APEI_GHES_SEA); raw_spin_unlock(&ghes_notify_lock_sea); @@ -1425,11 +1485,19 @@ int ghes_notify_sea(void) return rv; } -static void ghes_sea_add(struct ghes *ghes) +static int ghes_sea_add(struct ghes *ghes) { + int rc; + + rc = ghes_map_error_status(ghes); + if (rc) + return rc; + mutex_lock(&ghes_list_mutex); list_add_rcu(&ghes->list, &ghes_sea); mutex_unlock(&ghes_list_mutex); + + return 0; } static void ghes_sea_remove(struct ghes *ghes) @@ -1437,10 +1505,11 @@ static void ghes_sea_remove(struct ghes *ghes) mutex_lock(&ghes_list_mutex); list_del_rcu(&ghes->list); mutex_unlock(&ghes_list_mutex); + ghes_unmap_error_status(ghes); synchronize_rcu(); } #else /* CONFIG_ACPI_APEI_SEA */ -static inline void ghes_sea_add(struct ghes *ghes) { } +static inline int ghes_sea_add(struct ghes *ghes) { return -EINVAL; } static inline void ghes_sea_remove(struct ghes *ghes) { } #endif /* CONFIG_ACPI_APEI_SEA */ @@ -1458,6 +1527,9 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) static DEFINE_RAW_SPINLOCK(ghes_notify_lock_nmi); int ret = NMI_DONE; + if (!ghes_has_active_errors(&ghes_nmi)) + return ret; + if (!atomic_add_unless(&ghes_in_nmi, 1, 1)) return ret; @@ -1470,13 +1542,21 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) return ret; } -static void ghes_nmi_add(struct ghes *ghes) +static int ghes_nmi_add(struct ghes *ghes) { + int rc; + + rc = ghes_map_error_status(ghes); + if (rc) + return rc; + mutex_lock(&ghes_list_mutex); if (list_empty(&ghes_nmi)) register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes"); list_add_rcu(&ghes->list, &ghes_nmi); mutex_unlock(&ghes_list_mutex); + + return 0; } static void ghes_nmi_remove(struct ghes *ghes) @@ -1486,6 +1566,9 @@ static void ghes_nmi_remove(struct ghes *ghes) if (list_empty(&ghes_nmi)) unregister_nmi_handler(NMI_LOCAL, "ghes"); mutex_unlock(&ghes_list_mutex); + + ghes_unmap_error_status(ghes); + /* * To synchronize with NMI handler, ghes can only be * freed after NMI handler finishes. @@ -1493,7 +1576,7 @@ static void ghes_nmi_remove(struct ghes *ghes) synchronize_rcu(); } #else /* CONFIG_HAVE_ACPI_APEI_NMI */ -static inline void ghes_nmi_add(struct ghes *ghes) { } +static inline int ghes_nmi_add(struct ghes *ghes) { return -EINVAL; } static inline void ghes_nmi_remove(struct ghes *ghes) { } #endif /* CONFIG_HAVE_ACPI_APEI_NMI */ @@ -1658,10 +1741,14 @@ static int ghes_probe(struct platform_device *ghes_dev) break; case ACPI_HEST_NOTIFY_SEA: - ghes_sea_add(ghes); + rc = ghes_sea_add(ghes); + if (rc) + goto err; break; case ACPI_HEST_NOTIFY_NMI: - ghes_nmi_add(ghes); + rc = ghes_nmi_add(ghes); + if (rc) + goto err; break; case ACPI_HEST_NOTIFY_SOFTWARE_DELEGATED: rc = apei_sdei_register_ghes(ghes); @@ -1810,6 +1897,8 @@ void __init acpi_ghes_init(void) */ static struct acpi_platform_list plat_list[] = { {"HPE ", "Server ", 0, ACPI_SIG_FADT, all_versions}, + {"__ZX__", "EDK2 ", 3, ACPI_SIG_FADT, greater_than_or_equal}, + {"_BYO_ ", "BYOSOFT ", 3, ACPI_SIG_FADT, greater_than_or_equal}, { } /* End */ }; diff --git a/drivers/acpi/apei/ghes_helpers.c b/drivers/acpi/apei/ghes_helpers.c new file mode 100644 index 000000000000..bc7111b740af --- /dev/null +++ b/drivers/acpi/apei/ghes_helpers.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright(c) 2025 Intel Corporation. All rights reserved + +#include <linux/printk.h> +#include <linux/aer.h> +#include <cxl/event.h> + +int cxl_cper_sec_prot_err_valid(struct cxl_cper_sec_prot_err *prot_err) +{ + if (!(prot_err->valid_bits & PROT_ERR_VALID_AGENT_ADDRESS)) { + pr_err_ratelimited("CXL CPER invalid agent type\n"); + return -EINVAL; + } + + if (!(prot_err->valid_bits & PROT_ERR_VALID_ERROR_LOG)) { + pr_err_ratelimited("CXL CPER invalid protocol error log\n"); + return -EINVAL; + } + + if (prot_err->err_len != sizeof(struct cxl_ras_capability_regs)) { + pr_err_ratelimited("CXL CPER invalid RAS Cap size (%u)\n", + prot_err->err_len); + return -EINVAL; + } + + if ((prot_err->agent_type == RCD || prot_err->agent_type == DEVICE || + prot_err->agent_type == LD || prot_err->agent_type == FMLD) && + !(prot_err->valid_bits & PROT_ERR_VALID_SERIAL_NUMBER)) + pr_warn_ratelimited(FW_WARN + "CXL CPER no device serial number\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(cxl_cper_sec_prot_err_valid); + +int cxl_cper_setup_prot_err_work_data(struct cxl_cper_prot_err_work_data *wd, + struct cxl_cper_sec_prot_err *prot_err, + int severity) +{ + u8 *dvsec_start, *cap_start; + + switch (prot_err->agent_type) { + case RCD: + case DEVICE: + case LD: + case FMLD: + case RP: + case DSP: + case USP: + memcpy(&wd->prot_err, prot_err, sizeof(wd->prot_err)); + + dvsec_start = (u8 *)(prot_err + 1); + cap_start = dvsec_start + prot_err->dvsec_len; + + memcpy(&wd->ras_cap, cap_start, sizeof(wd->ras_cap)); + wd->severity = cper_severity_to_aer(severity); + break; + default: + pr_err_ratelimited("CXL CPER invalid agent type: %d\n", + prot_err->agent_type); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(cxl_cper_setup_prot_err_work_data); diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index 65f0f56ad753..ed827b2fc437 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -264,39 +264,47 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node, struct device *dev = context; acpi_status status = AE_NOT_FOUND; - if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { + if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || + node->type == ACPI_IORT_NODE_IWB) { struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_device *adev; struct acpi_iort_named_component *ncomp; - struct device *nc_dev = dev; + struct acpi_iort_iwb *iwb; + struct device *cdev = dev; + struct acpi_device *adev; + const char *device_name; /* * Walk the device tree to find a device with an * ACPI companion; there is no point in scanning - * IORT for a device matching a named component if + * IORT for a device matching a named component or IWB if * the device does not have an ACPI companion to * start with. */ do { - adev = ACPI_COMPANION(nc_dev); + adev = ACPI_COMPANION(cdev); if (adev) break; - nc_dev = nc_dev->parent; - } while (nc_dev); + cdev = cdev->parent; + } while (cdev); if (!adev) goto out; status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf); if (ACPI_FAILURE(status)) { - dev_warn(nc_dev, "Can't get device full path name\n"); + dev_warn(cdev, "Can't get device full path name\n"); goto out; } - ncomp = (struct acpi_iort_named_component *)node->node_data; - status = !strcmp(ncomp->device_name, buf.pointer) ? - AE_OK : AE_NOT_FOUND; + if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) { + ncomp = (struct acpi_iort_named_component *)node->node_data; + device_name = ncomp->device_name; + } else { + iwb = (struct acpi_iort_iwb *)node->node_data; + device_name = iwb->device_name; + } + status = !strcmp(device_name, buf.pointer) ? AE_OK : AE_NOT_FOUND; acpi_os_free(buf.pointer); } else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { struct acpi_iort_root_complex *pci_rc; @@ -317,12 +325,28 @@ out: return status; } +static acpi_status iort_match_iwb_callback(struct acpi_iort_node *node, void *context) +{ + struct acpi_iort_iwb *iwb; + u32 *id = context; + + if (node->type != ACPI_IORT_NODE_IWB) + return AE_NOT_FOUND; + + iwb = (struct acpi_iort_iwb *)node->node_data; + if (iwb->iwb_index != *id) + return AE_NOT_FOUND; + + return AE_OK; +} + static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, u32 *rid_out, bool check_overlap) { /* Single mapping does not care for input id */ if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { if (type == ACPI_IORT_NODE_NAMED_COMPONENT || + type == ACPI_IORT_NODE_IWB || type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) { *rid_out = map->output_base; return 0; @@ -392,6 +416,7 @@ static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) { if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || + node->type == ACPI_IORT_NODE_IWB || node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || node->type == ACPI_IORT_NODE_SMMU_V3 || node->type == ACPI_IORT_NODE_PMCG) { @@ -562,9 +587,14 @@ static struct acpi_iort_node *iort_find_dev_node(struct device *dev) return node; /* * if not, then it should be a platform device defined in - * DSDT/SSDT (with Named Component node in IORT) + * DSDT/SSDT (with Named Component node in IORT) or an + * IWB device in the DSDT/SSDT. */ - return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, + node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, + iort_match_node_callback, dev); + if (node) + return node; + return iort_scan_node(ACPI_IORT_NODE_IWB, iort_match_node_callback, dev); } @@ -595,45 +625,45 @@ u32 iort_msi_map_id(struct device *dev, u32 input_id) } /** - * iort_pmsi_get_dev_id() - Get the device id for a device + * iort_msi_xlate() - Map a MSI input ID for a device * @dev: The device for which the mapping is to be done. - * @dev_id: The device ID found. + * @input_id: The device input ID. + * @fwnode: Pointer to store the fwnode. * - * Returns: 0 for successful find a dev id, -ENODEV on error + * Returns: mapped MSI ID on success, input ID otherwise + * On success, the fwnode pointer is initialized to the MSI + * controller fwnode handle. */ -int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) +u32 iort_msi_xlate(struct device *dev, u32 input_id, struct fwnode_handle **fwnode) { - int i, index; + struct acpi_iort_its_group *its; struct acpi_iort_node *node; + u32 dev_id; node = iort_find_dev_node(dev); if (!node) - return -ENODEV; + return input_id; - index = iort_get_id_mapping_index(node); - /* if there is a valid index, go get the dev_id directly */ - if (index >= 0) { - if (iort_node_get_id(node, dev_id, index)) - return 0; - } else { - for (i = 0; i < node->mapping_count; i++) { - if (iort_node_map_platform_id(node, dev_id, - IORT_MSI_TYPE, i)) - return 0; - } - } + node = iort_node_map_id(node, input_id, &dev_id, IORT_MSI_TYPE); + if (!node) + return input_id; - return -ENODEV; + /* Move to ITS specific data */ + its = (struct acpi_iort_its_group *)node->node_data; + + *fwnode = iort_find_domain_token(its->identifiers[0]); + + return dev_id; } -static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) +int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base) { struct iort_its_msi_chip *its_msi_chip; int ret = -ENODEV; spin_lock(&iort_msi_chip_lock); list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) { - if (its_msi_chip->translation_id == its_id) { + if (its_msi_chip->fw_node == node) { *base = its_msi_chip->base_addr; ret = 0; break; @@ -644,6 +674,62 @@ static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) return ret; } +static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base) +{ + struct fwnode_handle *fwnode = iort_find_domain_token(its_id); + + if (!fwnode) + return -ENODEV; + + return iort_its_translate_pa(fwnode, base); +} + +/** + * iort_pmsi_get_msi_info() - Get the device id and translate frame PA for a device + * @dev: The device for which the mapping is to be done. + * @dev_id: The device ID found. + * @pa: optional pointer to store translate frame address. + * + * Returns: 0 for successful devid and pa retrieval, -ENODEV on error + */ +int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa) +{ + struct acpi_iort_node *node, *parent = NULL; + struct acpi_iort_its_group *its; + int i, index; + + node = iort_find_dev_node(dev); + if (!node) + return -ENODEV; + + index = iort_get_id_mapping_index(node); + /* if there is a valid index, go get the dev_id directly */ + if (index >= 0) { + parent = iort_node_get_id(node, dev_id, index); + } else { + for (i = 0; i < node->mapping_count; i++) { + parent = iort_node_map_platform_id(node, dev_id, + IORT_MSI_TYPE, i); + if (parent) + break; + } + } + + if (!parent) + return -ENODEV; + + if (pa) { + int ret; + + its = (struct acpi_iort_its_group *)node->node_data; + ret = iort_find_its_base(its->identifiers[0], pa); + if (ret) + return ret; + } + + return 0; +} + /** * iort_dev_find_its_id() - Find the ITS identifier for a device * @dev: The device. @@ -703,6 +789,35 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 id, return irq_find_matching_fwnode(handle, bus_token); } +struct fwnode_handle *iort_iwb_handle(u32 iwb_id) +{ + struct fwnode_handle *fwnode; + struct acpi_iort_node *node; + struct acpi_device *device; + struct acpi_iort_iwb *iwb; + acpi_status status; + acpi_handle handle; + + /* find its associated IWB node */ + node = iort_scan_node(ACPI_IORT_NODE_IWB, iort_match_iwb_callback, &iwb_id); + if (!node) + return NULL; + + iwb = (struct acpi_iort_iwb *)node->node_data; + status = acpi_get_handle(NULL, iwb->device_name, &handle); + if (ACPI_FAILURE(status)) + return NULL; + + device = acpi_get_acpi_dev(handle); + if (!device) + return NULL; + + fwnode = acpi_fwnode_handle(device); + acpi_put_acpi_dev(device); + + return fwnode; +} + static void iort_set_device_domain(struct device *dev, struct acpi_iort_node *node) { @@ -763,8 +878,14 @@ static struct irq_domain *iort_get_platform_device_domain(struct device *dev) /* find its associated iort node */ node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT, iort_match_node_callback, dev); - if (!node) - return NULL; + if (!node) { + /* find its associated iort node */ + node = iort_scan_node(ACPI_IORT_NODE_IWB, + iort_match_node_callback, dev); + + if (!node) + return NULL; + } /* then find its msi parent node */ for (i = 0; i < node->mapping_count; i++) { diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 34181fa52e93..3bbddd6f622c 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -17,6 +17,7 @@ #include <linux/list.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/platform_device.h> #include <linux/slab.h> #include <linux/suspend.h> #include <linux/types.h> @@ -211,7 +212,14 @@ static int acpi_battery_get_property(struct power_supply *psy, if (battery->state & ACPI_BATTERY_STATE_DISCHARGING) val->intval = acpi_battery_handle_discharging(battery); else if (battery->state & ACPI_BATTERY_STATE_CHARGING) - val->intval = POWER_SUPPLY_STATUS_CHARGING; + /* Validate the status by checking the current. */ + if (battery->rate_now != ACPI_BATTERY_VALUE_UNKNOWN && + battery->rate_now == 0) { + /* On charge but no current (0W/0mA). */ + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } else if (battery->state & ACPI_BATTERY_STATE_CHARGE_LIMITING) val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; else if (acpi_battery_is_charged(battery)) @@ -1054,8 +1062,8 @@ static void acpi_battery_refresh(struct acpi_battery *battery) /* Driver Interface */ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct acpi_battery *battery = acpi_driver_data(device); + struct acpi_battery *battery = data; + struct acpi_device *device = battery->device; struct power_supply *old; if (!battery) @@ -1208,26 +1216,26 @@ static void sysfs_battery_cleanup(struct acpi_battery *battery) sysfs_remove_battery(battery); } -static int acpi_battery_add(struct acpi_device *device) +static int acpi_battery_probe(struct platform_device *pdev) { - int result = 0; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_battery *battery; - - if (!device) - return -EINVAL; + int result; if (device->dep_unmet) return -EPROBE_DEFER; - battery = devm_kzalloc(&device->dev, sizeof(*battery), GFP_KERNEL); + battery = devm_kzalloc(&pdev->dev, sizeof(*battery), GFP_KERNEL); if (!battery) return -ENOMEM; + + platform_set_drvdata(pdev, battery); + battery->device = device; strscpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_BATTERY_CLASS); - device->driver_data = battery; - result = devm_mutex_init(&device->dev, &battery->update_lock); + result = devm_mutex_init(&pdev->dev, &battery->update_lock); if (result) return result; @@ -1246,17 +1254,17 @@ static int acpi_battery_add(struct acpi_device *device) if (result) goto fail; - device_init_wakeup(&device->dev, 1); + device_init_wakeup(&pdev->dev, true); result = acpi_dev_install_notify_handler(device, ACPI_ALL_NOTIFY, - acpi_battery_notify, device); + acpi_battery_notify, battery); if (result) goto fail_pm; return 0; fail_pm: - device_init_wakeup(&device->dev, 0); + device_init_wakeup(&pdev->dev, false); unregister_pm_notifier(&battery->pm_nb); fail: sysfs_battery_cleanup(battery); @@ -1264,35 +1272,28 @@ fail: return result; } -static void acpi_battery_remove(struct acpi_device *device) +static void acpi_battery_remove(struct platform_device *pdev) { - struct acpi_battery *battery; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_battery *battery = platform_get_drvdata(pdev); - if (!device || !acpi_driver_data(device)) + if (!device || !battery) return; - battery = acpi_driver_data(device); - acpi_dev_remove_notify_handler(device, ACPI_ALL_NOTIFY, acpi_battery_notify); - device_init_wakeup(&device->dev, 0); + device_init_wakeup(&pdev->dev, false); unregister_pm_notifier(&battery->pm_nb); - guard(mutex)(&battery->update_lock); - - sysfs_remove_battery(battery); + sysfs_battery_cleanup(battery); } /* this is needed to learn about changes made in suspended state */ static int acpi_battery_resume(struct device *dev) { - struct acpi_battery *battery; + struct acpi_battery *battery = dev_get_drvdata(dev); - if (!dev) - return -EINVAL; - - battery = acpi_driver_data(to_acpi_device(dev)); if (!battery) return -EINVAL; @@ -1306,16 +1307,15 @@ static int acpi_battery_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume); -static struct acpi_driver acpi_battery_driver = { - .name = "battery", - .class = ACPI_BATTERY_CLASS, - .ids = battery_device_ids, - .ops = { - .add = acpi_battery_add, - .remove = acpi_battery_remove, - }, - .drv.pm = pm_sleep_ptr(&acpi_battery_pm), - .drv.probe_type = PROBE_PREFER_ASYNCHRONOUS, +static struct platform_driver acpi_battery_driver = { + .probe = acpi_battery_probe, + .remove = acpi_battery_remove, + .driver = { + .name = "acpi-battery", + .acpi_match_table = battery_device_ids, + .pm = pm_sleep_ptr(&acpi_battery_pm), + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, }; static int __init acpi_battery_init(void) @@ -1325,12 +1325,12 @@ static int __init acpi_battery_init(void) dmi_check_system(bat_dmi_table); - return acpi_bus_register_driver(&acpi_battery_driver); + return platform_driver_register(&acpi_battery_driver); } static void __exit acpi_battery_exit(void) { - acpi_bus_unregister_driver(&acpi_battery_driver); + platform_driver_unregister(&acpi_battery_driver); battery_hook_exit(); } diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a984ccd4a2a0..f6707325f582 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -180,104 +180,239 @@ 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) +static void acpi_dump_osc_data(acpi_handle handle, const guid_t *guid, int rev, + struct acpi_buffer *cap) { + u32 *capbuf = cap->pointer; int i; - acpi_handle_debug(handle, "(%s): %s\n", context->uuid_str, error); + acpi_handle_debug(handle, "_OSC: UUID: %pUL, rev: %d\n", guid, rev); + for (i = 0; i < cap->length / sizeof(u32); i++) + acpi_handle_debug(handle, "_OSC: capabilities DWORD %i: [%08x]\n", + i, capbuf[i]); +} + +#define OSC_ERROR_MASK (OSC_REQUEST_ERROR | OSC_INVALID_UUID_ERROR | \ + OSC_INVALID_REVISION_ERROR | \ + OSC_CAPABILITIES_MASK_ERROR) + +static int acpi_eval_osc(acpi_handle handle, guid_t *guid, int rev, + struct acpi_buffer *cap, + union acpi_object in_params[at_least 4], + struct acpi_buffer *output) +{ + struct acpi_object_list input; + union acpi_object *out_obj; + acpi_status status; + + in_params[0].type = ACPI_TYPE_BUFFER; + in_params[0].buffer.length = sizeof(*guid); + in_params[0].buffer.pointer = (u8 *)guid; + in_params[1].type = ACPI_TYPE_INTEGER; + in_params[1].integer.value = rev; + in_params[2].type = ACPI_TYPE_INTEGER; + in_params[2].integer.value = cap->length / sizeof(u32); + in_params[3].type = ACPI_TYPE_BUFFER; + in_params[3].buffer.length = cap->length; + in_params[3].buffer.pointer = cap->pointer; + input.pointer = in_params; + input.count = 4; - pr_debug("_OSC request data:"); - for (i = 0; i < context->cap.length; i += sizeof(u32)) - pr_debug(" %x", *((u32 *)(context->cap.pointer + i))); + output->length = ACPI_ALLOCATE_BUFFER; + output->pointer = NULL; - pr_debug("\n"); + status = acpi_evaluate_object(handle, "_OSC", &input, output); + if (ACPI_FAILURE(status) || !output->length) + return -ENODATA; + + out_obj = output->pointer; + if (out_obj->type != ACPI_TYPE_BUFFER || + out_obj->buffer.length != cap->length) { + acpi_handle_debug(handle, "Invalid _OSC return buffer\n"); + acpi_dump_osc_data(handle, guid, rev, cap); + ACPI_FREE(out_obj); + return -ENODATA; + } + + return 0; +} + +static bool acpi_osc_error_check(acpi_handle handle, guid_t *guid, int rev, + struct acpi_buffer *cap, u32 *retbuf) +{ + /* Only take defined error bits into account. */ + u32 errors = retbuf[OSC_QUERY_DWORD] & OSC_ERROR_MASK; + u32 *capbuf = cap->pointer; + bool fail; + + /* + * If OSC_QUERY_ENABLE is set, ignore the "capabilities masked" + * bit because it merely means that some features have not been + * acknowledged which is not unexpected. + */ + if (capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE) + errors &= ~OSC_CAPABILITIES_MASK_ERROR; + + if (!errors) + return false; + + acpi_dump_osc_data(handle, guid, rev, cap); + /* + * As a rule, fail only if OSC_QUERY_ENABLE is set because otherwise the + * acknowledged features need to be controlled. + */ + fail = !!(capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE); + + if (errors & OSC_REQUEST_ERROR) + acpi_handle_debug(handle, "_OSC: request failed\n"); + + if (errors & OSC_INVALID_UUID_ERROR) { + acpi_handle_debug(handle, "_OSC: invalid UUID\n"); + /* + * Always fail if this bit is set because it means that the + * request could not be processed. + */ + fail = true; + } + + if (errors & OSC_INVALID_REVISION_ERROR) + acpi_handle_debug(handle, "_OSC: invalid revision\n"); + + if (errors & OSC_CAPABILITIES_MASK_ERROR) + acpi_handle_debug(handle, "_OSC: capability bits masked\n"); + + return fail; } acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context) { - acpi_status status; - struct acpi_object_list input; - union acpi_object in_params[4]; - union acpi_object *out_obj; + union acpi_object in_params[4], *out_obj; + struct acpi_buffer output; + acpi_status status = AE_OK; guid_t guid; - u32 errors; - struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; - - if (!context) - return AE_ERROR; - if (guid_parse(context->uuid_str, &guid)) - return AE_ERROR; - context->ret.length = ACPI_ALLOCATE_BUFFER; - context->ret.pointer = NULL; + u32 *retbuf; + int ret; - /* Setting up input parameters */ - input.count = 4; - input.pointer = in_params; - in_params[0].type = ACPI_TYPE_BUFFER; - in_params[0].buffer.length = 16; - in_params[0].buffer.pointer = (u8 *)&guid; - in_params[1].type = ACPI_TYPE_INTEGER; - in_params[1].integer.value = context->rev; - in_params[2].type = ACPI_TYPE_INTEGER; - in_params[2].integer.value = context->cap.length/sizeof(u32); - in_params[3].type = ACPI_TYPE_BUFFER; - in_params[3].buffer.length = context->cap.length; - in_params[3].buffer.pointer = context->cap.pointer; - - status = acpi_evaluate_object(handle, "_OSC", &input, &output); - if (ACPI_FAILURE(status)) - return status; + if (!context || !context->cap.pointer || + context->cap.length < 2 * sizeof(u32) || + guid_parse(context->uuid_str, &guid)) + return AE_BAD_PARAMETER; - if (!output.length) - return AE_NULL_OBJECT; + ret = acpi_eval_osc(handle, &guid, context->rev, &context->cap, + in_params, &output); + if (ret) + return AE_ERROR; out_obj = output.pointer; - if (out_obj->type != ACPI_TYPE_BUFFER - || out_obj->buffer.length != context->cap.length) { - acpi_print_osc_error(handle, context, - "_OSC evaluation returned wrong type"); - status = AE_TYPE; - goto out_kfree; - } - /* Need to ignore the bit0 in result code */ - errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0); - if (errors) { - if (errors & OSC_REQUEST_ERROR) - acpi_print_osc_error(handle, context, - "_OSC request failed"); - if (errors & OSC_INVALID_UUID_ERROR) - acpi_print_osc_error(handle, context, - "_OSC invalid UUID"); - if (errors & OSC_INVALID_REVISION_ERROR) - acpi_print_osc_error(handle, context, - "_OSC invalid revision"); - if (errors & OSC_CAPABILITIES_MASK_ERROR) { - if (((u32 *)context->cap.pointer)[OSC_QUERY_DWORD] - & OSC_QUERY_ENABLE) - goto out_success; - status = AE_SUPPORT; - goto out_kfree; - } + retbuf = (u32 *)out_obj->buffer.pointer; + + if (acpi_osc_error_check(handle, &guid, context->rev, &context->cap, retbuf)) { status = AE_ERROR; - goto out_kfree; + goto out; } -out_success: + context->ret.length = out_obj->buffer.length; - context->ret.pointer = kmemdup(out_obj->buffer.pointer, - context->ret.length, GFP_KERNEL); + context->ret.pointer = kmemdup(retbuf, context->ret.length, GFP_KERNEL); if (!context->ret.pointer) { status = AE_NO_MEMORY; - goto out_kfree; + goto out; } status = AE_OK; -out_kfree: - kfree(output.pointer); +out: + ACPI_FREE(out_obj); return status; } EXPORT_SYMBOL(acpi_run_osc); +static int acpi_osc_handshake(acpi_handle handle, const char *uuid_str, + int rev, u32 *capbuf, size_t bufsize) +{ + union acpi_object in_params[4], *out_obj; + struct acpi_object_list input; + struct acpi_buffer cap = { + .pointer = capbuf, + .length = bufsize * sizeof(u32), + }; + struct acpi_buffer output; + u32 *retbuf, test; + guid_t guid; + int ret, i; + + if (!capbuf || bufsize < 2 || guid_parse(uuid_str, &guid)) + return -EINVAL; + + /* First evaluate _OSC with OSC_QUERY_ENABLE set. */ + capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; + + ret = acpi_eval_osc(handle, &guid, rev, &cap, in_params, &output); + if (ret) + return ret; + + out_obj = output.pointer; + retbuf = (u32 *)out_obj->buffer.pointer; + + if (acpi_osc_error_check(handle, &guid, rev, &cap, retbuf)) { + ret = -ENODATA; + goto out; + } + + /* + * Clear the feature bits in the capabilities buffer that have not been + * acknowledged and clear the return buffer. + */ + for (i = OSC_QUERY_DWORD + 1, test = 0; i < bufsize; i++) { + capbuf[i] &= retbuf[i]; + test |= capbuf[i]; + retbuf[i] = 0; + } + /* + * If none of the feature bits have been acknowledged, there's nothing + * more to do. capbuf[] contains a feature mask of all zeros. + */ + if (!test) + goto out; + + retbuf[OSC_QUERY_DWORD] = 0; + /* + * Now evaluate _OSC again (directly) with OSC_QUERY_ENABLE clear and + * the updated input and output buffers used before. Since the feature + * bits that were clear in the return buffer from the previous _OSC + * evaluation are also clear in the capabilities buffer now, this _OSC + * evaluation is not expected to fail. + */ + capbuf[OSC_QUERY_DWORD] = 0; + /* Reuse in_params[] populated by acpi_eval_osc(). */ + input.pointer = in_params; + input.count = 4; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_OSC", &input, &output))) { + ret = -ENODATA; + goto out; + } + + /* + * Clear the feature bits in capbuf[] that have not been acknowledged. + * After that, capbuf[] contains the resultant feature mask. + */ + for (i = OSC_QUERY_DWORD + 1; i < bufsize; i++) + capbuf[i] &= retbuf[i]; + + if (retbuf[OSC_QUERY_DWORD] & OSC_ERROR_MASK) { + /* + * Complain about the unexpected errors and print diagnostic + * information related to them. + */ + acpi_handle_err(handle, "_OSC: errors while processing control request\n"); + acpi_handle_err(handle, "_OSC: some features may be missing\n"); + acpi_osc_error_check(handle, &guid, rev, &cap, retbuf); + } + +out: + ACPI_FREE(out_obj); + return ret; +} + bool osc_sb_apei_support_acked; /* @@ -309,101 +444,69 @@ EXPORT_SYMBOL_GPL(osc_sb_native_usb4_support_confirmed); bool osc_sb_cppc2_support_acked; -static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; static void acpi_bus_osc_negotiate_platform_control(void) { - u32 capbuf[2], *capbuf_ret; - struct acpi_osc_context context = { - .uuid_str = sb_uuid_str, - .rev = 1, - .cap.length = 8, - .cap.pointer = capbuf, - }; + static const u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; + u32 capbuf[2], feature_mask; acpi_handle handle; - capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; - capbuf[OSC_SUPPORT_DWORD] = OSC_SB_PR3_SUPPORT; /* _PR3 is in use */ + feature_mask = OSC_SB_PR3_SUPPORT | OSC_SB_HOTPLUG_OST_SUPPORT | + OSC_SB_PCLPI_SUPPORT | OSC_SB_OVER_16_PSTATES_SUPPORT | + OSC_SB_GED_SUPPORT | OSC_SB_IRQ_RESOURCE_SOURCE_SUPPORT; + + if (IS_ENABLED(CONFIG_ARM64) || IS_ENABLED(CONFIG_X86)) + feature_mask |= OSC_SB_GENERIC_INITIATOR_SUPPORT; + + if (IS_ENABLED(CONFIG_ACPI_CPPC_LIB)) { + feature_mask |= OSC_SB_CPC_SUPPORT | OSC_SB_CPCV2_SUPPORT | + OSC_SB_CPC_FLEXIBLE_ADR_SPACE; + if (IS_ENABLED(CONFIG_SCHED_MC_PRIO)) + feature_mask |= OSC_SB_CPC_DIVERSE_HIGH_SUPPORT; + } + if (IS_ENABLED(CONFIG_ACPI_PROCESSOR_AGGREGATOR)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PAD_SUPPORT; + feature_mask |= OSC_SB_PAD_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_PROCESSOR)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PPC_OST_SUPPORT; + feature_mask |= OSC_SB_PPC_OST_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_THERMAL)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT; + feature_mask |= OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_BATTERY)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_BATTERY_CHARGE_LIMITING_SUPPORT; + feature_mask |= OSC_SB_BATTERY_CHARGE_LIMITING_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_OST_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PCLPI_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_OVER_16_PSTATES_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GED_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_IRQ_RESOURCE_SOURCE_SUPPORT; if (IS_ENABLED(CONFIG_ACPI_PRMT)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_PRM_SUPPORT; - if (IS_ENABLED(CONFIG_ACPI_FFH)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_FFH_OPR_SUPPORT; - -#ifdef CONFIG_ARM64 - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GENERIC_INITIATOR_SUPPORT; -#endif -#ifdef CONFIG_X86 - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_GENERIC_INITIATOR_SUPPORT; -#endif - -#ifdef CONFIG_ACPI_CPPC_LIB - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPCV2_SUPPORT; -#endif + feature_mask |= OSC_SB_PRM_SUPPORT; - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_FLEXIBLE_ADR_SPACE; - - if (IS_ENABLED(CONFIG_SCHED_MC_PRIO)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_CPC_DIVERSE_HIGH_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_FFH)) + feature_mask |= OSC_SB_FFH_OPR_SUPPORT; if (IS_ENABLED(CONFIG_USB4)) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_NATIVE_USB4_SUPPORT; + feature_mask |= OSC_SB_NATIVE_USB4_SUPPORT; if (!ghes_disable) - capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT; + feature_mask |= OSC_SB_APEI_SUPPORT; + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) return; - if (ACPI_FAILURE(acpi_run_osc(handle, &context))) - return; + capbuf[OSC_SUPPORT_DWORD] = feature_mask; - capbuf_ret = context.ret.pointer; - if (context.ret.length <= OSC_SUPPORT_DWORD) { - kfree(context.ret.pointer); - return; - } + acpi_handle_info(handle, "platform _OSC: OS support mask [%08x]\n", feature_mask); - /* - * Now run _OSC again with query flag clear and with the caps - * supported by both the OS and the platform. - */ - capbuf[OSC_QUERY_DWORD] = 0; - capbuf[OSC_SUPPORT_DWORD] = capbuf_ret[OSC_SUPPORT_DWORD]; - kfree(context.ret.pointer); - - if (ACPI_FAILURE(acpi_run_osc(handle, &context))) + if (acpi_osc_handshake(handle, sb_uuid_str, 1, capbuf, ARRAY_SIZE(capbuf))) return; - capbuf_ret = context.ret.pointer; - if (context.ret.length > OSC_SUPPORT_DWORD) { -#ifdef CONFIG_ACPI_CPPC_LIB - osc_sb_cppc2_support_acked = capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_CPCV2_SUPPORT; -#endif + feature_mask = capbuf[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; - osc_sb_native_usb4_support_confirmed = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT; - osc_cpc_flexible_adr_space_confirmed = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_CPC_FLEXIBLE_ADR_SPACE; - } + acpi_handle_info(handle, "platform _OSC: OS control mask [%08x]\n", feature_mask); - kfree(context.ret.pointer); + osc_sb_cppc2_support_acked = feature_mask & OSC_SB_CPCV2_SUPPORT; + osc_sb_apei_support_acked = feature_mask & OSC_SB_APEI_SUPPORT; + osc_pc_lpi_support_confirmed = feature_mask & OSC_SB_PCLPI_SUPPORT; + osc_sb_native_usb4_support_confirmed = feature_mask & OSC_SB_NATIVE_USB4_SUPPORT; + osc_cpc_flexible_adr_space_confirmed = feature_mask & OSC_SB_CPC_FLEXIBLE_ADR_SPACE; } /* @@ -423,19 +526,11 @@ static void acpi_bus_decode_usb_osc(const char *msg, u32 bits) (bits & OSC_USB_XDOMAIN) ? '+' : '-'); } -static u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A"; static void acpi_bus_osc_negotiate_usb_control(void) { - u32 capbuf[3], *capbuf_ret; - struct acpi_osc_context context = { - .uuid_str = sb_usb_uuid_str, - .rev = 1, - .cap.length = sizeof(capbuf), - .cap.pointer = capbuf, - }; + static const u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A"; + u32 capbuf[3], control; acpi_handle handle; - acpi_status status; - u32 control; if (!osc_sb_native_usb4_support_confirmed) return; @@ -446,54 +541,16 @@ static void acpi_bus_osc_negotiate_usb_control(void) control = OSC_USB_USB3_TUNNELING | OSC_USB_DP_TUNNELING | OSC_USB_PCIE_TUNNELING | OSC_USB_XDOMAIN; - /* - * Run _OSC first with query bit set, trying to get control over - * all tunneling. The platform can then clear out bits in the - * control dword that it does not want to grant to the OS. - */ - capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; capbuf[OSC_SUPPORT_DWORD] = 0; capbuf[OSC_CONTROL_DWORD] = control; - status = acpi_run_osc(handle, &context); - if (ACPI_FAILURE(status)) - return; - - if (context.ret.length != sizeof(capbuf)) { - pr_info("USB4 _OSC: returned invalid length buffer\n"); - goto out_free; - } - - /* - * Run _OSC again now with query bit clear and the control dword - * matching what the platform granted (which may not have all - * the control bits set). - */ - capbuf_ret = context.ret.pointer; - - capbuf[OSC_QUERY_DWORD] = 0; - capbuf[OSC_CONTROL_DWORD] = capbuf_ret[OSC_CONTROL_DWORD]; - - kfree(context.ret.pointer); - - status = acpi_run_osc(handle, &context); - if (ACPI_FAILURE(status)) + if (acpi_osc_handshake(handle, sb_usb_uuid_str, 1, capbuf, ARRAY_SIZE(capbuf))) return; - if (context.ret.length != sizeof(capbuf)) { - pr_info("USB4 _OSC: returned invalid length buffer\n"); - goto out_free; - } - - osc_sb_native_usb4_control = - control & acpi_osc_ctx_get_pci_control(&context); + osc_sb_native_usb4_control = capbuf[OSC_CONTROL_DWORD]; acpi_bus_decode_usb_osc("USB4 _OSC: OS supports", control); - acpi_bus_decode_usb_osc("USB4 _OSC: OS controls", - osc_sb_native_usb4_control); - -out_free: - kfree(context.ret.pointer); + acpi_bus_decode_usb_osc("USB4 _OSC: OS controls", osc_sb_native_usb4_control); } /* -------------------------------------------------------------------------- @@ -761,6 +818,9 @@ const struct acpi_device *acpi_companion_match(const struct device *dev) if (list_empty(&adev->pnp.ids)) return NULL; + if (adev->pnp.type.backlight) + return adev; + return acpi_primary_dev_companion(adev, dev); } @@ -956,30 +1016,24 @@ const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, } EXPORT_SYMBOL_GPL(acpi_match_device); -static const void *acpi_of_device_get_match_data(const struct device *dev) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - const struct of_device_id *match = NULL; - - if (!acpi_of_match_device(adev, dev->driver->of_match_table, &match)) - return NULL; - - return match->data; -} - const void *acpi_device_get_match_data(const struct device *dev) { const struct acpi_device_id *acpi_ids = dev->driver->acpi_match_table; - const struct acpi_device_id *match; - - if (!acpi_ids) - return acpi_of_device_get_match_data(dev); + const struct of_device_id *of_ids = dev->driver->of_match_table; + const struct acpi_device *adev = acpi_companion_match(dev); + const struct acpi_device_id *acpi_id = NULL; + const struct of_device_id *of_id = NULL; - match = acpi_match_device(acpi_ids, dev); - if (!match) + if (!__acpi_match_device(adev, acpi_ids, of_ids, &acpi_id, &of_id)) return NULL; - return (const void *)match->driver_data; + if (acpi_id) + return (const void *)acpi_id->driver_data; + + if (of_id) + return of_id->data; + + return NULL; } EXPORT_SYMBOL_GPL(acpi_device_get_match_data); @@ -1197,6 +1251,9 @@ static int __init acpi_bus_init_irq(void) case ACPI_IRQ_MODEL_GIC: message = "GIC"; break; + case ACPI_IRQ_MODEL_GIC_V5: + message = "GICv5"; + break; case ACPI_IRQ_MODEL_PLATFORM: message = "platform specific model"; break; diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 3c6dd9b4ba0a..b899b8745fed 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -19,6 +19,7 @@ #include <linux/slab.h> #include <linux/acpi.h> #include <linux/dmi.h> +#include <linux/platform_device.h> #include <acpi/button.h> #define ACPI_BUTTON_CLASS "button" @@ -145,8 +146,8 @@ static const struct dmi_system_id dmi_lid_quirks[] = { {} }; -static int acpi_button_add(struct acpi_device *device); -static void acpi_button_remove(struct acpi_device *device); +static int acpi_button_probe(struct platform_device *pdev); +static void acpi_button_remove(struct platform_device *pdev); #ifdef CONFIG_PM_SLEEP static int acpi_button_suspend(struct device *dev); @@ -157,18 +158,19 @@ static int acpi_button_resume(struct device *dev); #endif static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume); -static struct acpi_driver acpi_button_driver = { - .name = "button", - .class = ACPI_BUTTON_CLASS, - .ids = button_device_ids, - .ops = { - .add = acpi_button_add, - .remove = acpi_button_remove, +static struct platform_driver acpi_button_driver = { + .probe = acpi_button_probe, + .remove = acpi_button_remove, + .driver = { + .name = "acpi-button", + .acpi_match_table = button_device_ids, + .pm = &acpi_button_pm, }, - .drv.pm = &acpi_button_pm, }; struct acpi_button { + struct acpi_device *adev; + struct platform_device *pdev; unsigned int type; struct input_dev *input; char phys[32]; /* for input device */ @@ -202,9 +204,9 @@ static int acpi_lid_evaluate_state(struct acpi_device *device) return lid_state ? 1 : 0; } -static int acpi_lid_notify_state(struct acpi_device *device, int state) +static int acpi_lid_notify_state(struct acpi_button *button, int state) { - struct acpi_button *button = acpi_driver_data(device); + struct acpi_device *device = button->adev; ktime_t next_report; bool do_update; @@ -287,18 +289,18 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state) static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq, void *offset) { - struct acpi_device *device = seq->private; + struct acpi_button *button = seq->private; int state; - state = acpi_lid_evaluate_state(device); + state = acpi_lid_evaluate_state(button->adev); seq_printf(seq, "state: %s\n", state < 0 ? "unsupported" : (state ? "open" : "closed")); return 0; } -static int acpi_button_add_fs(struct acpi_device *device) +static int acpi_button_add_fs(struct acpi_button *button) { - struct acpi_button *button = acpi_driver_data(device); + struct acpi_device *device = button->adev; struct proc_dir_entry *entry = NULL; int ret = 0; @@ -333,7 +335,7 @@ static int acpi_button_add_fs(struct acpi_device *device) /* create /proc/acpi/button/lid/LID/state */ entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO, acpi_device_dir(device), acpi_button_state_seq_show, - device); + button); if (!entry) { ret = -ENODEV; goto remove_dev_dir; @@ -355,9 +357,9 @@ remove_button_dir: goto done; } -static int acpi_button_remove_fs(struct acpi_device *device) +static int acpi_button_remove_fs(struct acpi_button *button) { - struct acpi_button *button = acpi_driver_data(device); + struct acpi_device *device = button->adev; if (button->type != ACPI_BUTTON_TYPE_LID) return 0; @@ -385,9 +387,10 @@ int acpi_lid_open(void) } EXPORT_SYMBOL(acpi_lid_open); -static int acpi_lid_update_state(struct acpi_device *device, +static int acpi_lid_update_state(struct acpi_button *button, bool signal_wakeup) { + struct acpi_device *device = button->adev; int state; state = acpi_lid_evaluate_state(device); @@ -395,21 +398,19 @@ static int acpi_lid_update_state(struct acpi_device *device, return state; if (state && signal_wakeup) - acpi_pm_wakeup_event(&device->dev); + acpi_pm_wakeup_event(&button->pdev->dev); - return acpi_lid_notify_state(device, state); + return acpi_lid_notify_state(button, state); } -static void acpi_lid_initialize_state(struct acpi_device *device) +static void acpi_lid_initialize_state(struct acpi_button *button) { - struct acpi_button *button = acpi_driver_data(device); - switch (lid_init_state) { case ACPI_BUTTON_LID_INIT_OPEN: - (void)acpi_lid_notify_state(device, 1); + (void)acpi_lid_notify_state(button, 1); break; case ACPI_BUTTON_LID_INIT_METHOD: - (void)acpi_lid_update_state(device, false); + (void)acpi_lid_update_state(button, false); break; case ACPI_BUTTON_LID_INIT_IGNORE: default: @@ -421,8 +422,8 @@ static void acpi_lid_initialize_state(struct acpi_device *device) static void acpi_lid_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct acpi_button *button; + struct acpi_button *button = data; + struct acpi_device *device = button->adev; if (event != ACPI_BUTTON_NOTIFY_STATUS) { acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n", @@ -430,17 +431,16 @@ static void acpi_lid_notify(acpi_handle handle, u32 event, void *data) return; } - button = acpi_driver_data(device); if (!button->lid_state_initialized) return; - acpi_lid_update_state(device, true); + acpi_lid_update_state(button, true); } static void acpi_button_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct acpi_button *button; + struct acpi_button *button = data; + struct acpi_device *device = button->adev; struct input_dev *input; int keycode; @@ -455,9 +455,8 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data) return; } - acpi_pm_wakeup_event(&device->dev); + acpi_pm_wakeup_event(&button->pdev->dev); - button = acpi_driver_data(device); if (button->suspended || event == ACPI_BUTTON_NOTIFY_WAKE) return; @@ -488,8 +487,7 @@ static u32 acpi_button_event(void *data) #ifdef CONFIG_PM_SLEEP static int acpi_button_suspend(struct device *dev) { - struct acpi_device *device = to_acpi_device(dev); - struct acpi_button *button = acpi_driver_data(device); + struct acpi_button *button = dev_get_drvdata(dev); button->suspended = true; return 0; @@ -497,15 +495,15 @@ static int acpi_button_suspend(struct device *dev) static int acpi_button_resume(struct device *dev) { + struct acpi_button *button = dev_get_drvdata(dev); + struct acpi_device *device = ACPI_COMPANION(dev); struct input_dev *input; - struct acpi_device *device = to_acpi_device(dev); - struct acpi_button *button = acpi_driver_data(device); button->suspended = false; if (button->type == ACPI_BUTTON_TYPE_LID) { button->last_state = !!acpi_lid_evaluate_state(device); button->last_time = ktime_get(); - acpi_lid_initialize_state(device); + acpi_lid_initialize_state(button); } if (button->type == ACPI_BUTTON_TYPE_POWER) { @@ -521,18 +519,19 @@ static int acpi_button_resume(struct device *dev) static int acpi_lid_input_open(struct input_dev *input) { - struct acpi_device *device = input_get_drvdata(input); - struct acpi_button *button = acpi_driver_data(device); + struct acpi_button *button = input_get_drvdata(input); + struct acpi_device *device = button->adev; button->last_state = !!acpi_lid_evaluate_state(device); button->last_time = ktime_get(); - acpi_lid_initialize_state(device); + acpi_lid_initialize_state(button); return 0; } -static int acpi_button_add(struct acpi_device *device) +static int acpi_button_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_notify_handler handler; struct acpi_button *button; struct input_dev *input; @@ -549,8 +548,10 @@ static int acpi_button_add(struct acpi_device *device) if (!button) return -ENOMEM; - device->driver_data = button; + platform_set_drvdata(pdev, button); + button->pdev = pdev; + button->adev = device; button->input = input = input_allocate_device(); if (!input) { error = -ENOMEM; @@ -587,7 +588,7 @@ static int acpi_button_add(struct acpi_device *device) } if (!error) - error = acpi_button_add_fs(device); + error = acpi_button_add_fs(button); if (error) { input_free_device(input); @@ -600,7 +601,7 @@ static int acpi_button_add(struct acpi_device *device) input->phys = button->phys; input->id.bustype = BUS_HOST; input->id.product = button->type; - input->dev.parent = &device->dev; + input->dev.parent = &pdev->dev; switch (button->type) { case ACPI_BUTTON_TYPE_POWER: @@ -617,7 +618,7 @@ static int acpi_button_add(struct acpi_device *device) break; } - input_set_drvdata(input, device); + input_set_drvdata(input, button); error = input_register_device(input); if (error) { input_free_device(input); @@ -628,17 +629,17 @@ static int acpi_button_add(struct acpi_device *device) case ACPI_BUS_TYPE_POWER_BUTTON: status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_button_event, - device); + button); break; case ACPI_BUS_TYPE_SLEEP_BUTTON: status = acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, acpi_button_event, - device); + button); break; default: status = acpi_install_notify_handler(device->handle, ACPI_ALL_NOTIFY, handler, - device); + button); break; } if (ACPI_FAILURE(status)) { @@ -654,22 +655,23 @@ static int acpi_button_add(struct acpi_device *device) lid_device = device; } - device_init_wakeup(&device->dev, true); + device_init_wakeup(&pdev->dev, true); pr_info("%s [%s]\n", name, acpi_device_bid(device)); return 0; err_input_unregister: input_unregister_device(input); err_remove_fs: - acpi_button_remove_fs(device); + acpi_button_remove_fs(button); err_free_button: kfree(button); return error; } -static void acpi_button_remove(struct acpi_device *device) +static void acpi_button_remove(struct platform_device *pdev) { - struct acpi_button *button = acpi_driver_data(device); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_button *button = platform_get_drvdata(pdev); switch (device->device_type) { case ACPI_BUS_TYPE_POWER_BUTTON: @@ -689,7 +691,7 @@ static void acpi_button_remove(struct acpi_device *device) } acpi_os_wait_events_complete(); - acpi_button_remove_fs(device); + acpi_button_remove_fs(button); input_unregister_device(button->input); kfree(button); } @@ -728,7 +730,7 @@ module_param_call(lid_init_state, NULL, 0644); MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state"); -static int acpi_button_register_driver(struct acpi_driver *driver) +static int __init acpi_button_init(void) { const struct dmi_system_id *dmi_id; @@ -744,20 +746,20 @@ static int acpi_button_register_driver(struct acpi_driver *driver) * Modules such as nouveau.ko and i915.ko have a link time dependency * on acpi_lid_open(), and would therefore not be loadable on ACPI * capable kernels booted in non-ACPI mode if the return value of - * acpi_bus_register_driver() is returned from here with ACPI disabled + * platform_driver_register() is returned from here with ACPI disabled * when this driver is built as a module. */ if (acpi_disabled) return 0; - return acpi_bus_register_driver(driver); + return platform_driver_register(&acpi_button_driver); } -static void acpi_button_unregister_driver(struct acpi_driver *driver) +static void __exit acpi_button_exit(void) { if (!acpi_disabled) - acpi_bus_unregister_driver(driver); + platform_driver_unregister(&acpi_button_driver); } -module_driver(acpi_button_driver, acpi_button_register_driver, - acpi_button_unregister_driver); +module_init(acpi_button_init); +module_exit(acpi_button_exit); diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index e66e20d1f31b..c67494939f7b 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1609,7 +1609,7 @@ EXPORT_SYMBOL_GPL(cppc_set_epp_perf); */ int cppc_set_epp(int cpu, u64 epp_val) { - if (epp_val > CPPC_ENERGY_PERF_MAX) + if (epp_val > CPPC_EPP_ENERGY_EFFICIENCY_PREF) return -EINVAL; return cppc_set_reg_val(cpu, ENERGY_PERF, epp_val); diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 4e0583274b8f..f2579611e0a5 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -586,8 +586,7 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, goto out; mutex_lock(&acpi_pm_notifier_lock); - adev->wakeup.ws = wakeup_source_register(&adev->dev, - dev_name(&adev->dev)); + adev->wakeup.ws = wakeup_source_register(dev, dev_name(&adev->dev)); adev->wakeup.context.dev = dev; adev->wakeup.context.func = func; adev->wakeup.flags.notifier_present = true; @@ -1252,7 +1251,7 @@ static int acpi_subsys_resume_early(struct device *dev) return 0; if (pm && !pm->resume_early) { - dev_dbg(dev, "postponing D0 transition to normal resume stage\n"); + dev_dbg(dev, "Postponing ACPI PM to normal resume stage\n"); return 0; } @@ -1274,7 +1273,7 @@ static int acpi_subsys_resume(struct device *dev) int ret = 0; if (!dev_pm_skip_resume(dev) && pm && !pm->resume_early) { - dev_dbg(dev, "executing postponed D0 transition\n"); + dev_dbg(dev, "Applying postponed ACPI PM\n"); ret = acpi_dev_resume(dev); } @@ -1458,6 +1457,15 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on) return 0; /* + * Skip devices whose ACPI companions don't support power management and + * don't have a wakeup GPE. + */ + if (!acpi_device_power_manageable(adev) && !acpi_device_can_wakeup(adev)) { + dev_dbg(dev, "No ACPI power management or wakeup GPE\n"); + return 0; + } + + /* * Only attach the power domain to the first device if the * companion is shared by multiple. This is to prevent doing power * management twice. diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index cd199fbe4dc9..c5434b998604 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c @@ -27,7 +27,7 @@ static ssize_t acpi_object_path(acpi_handle handle, char *buf) if (result) return result; - result = sprintf(buf, "%s\n", (char *)path.pointer); + result = sysfs_emit(buf, "%s\n", (char *)path.pointer); kfree(path.pointer); return result; } @@ -347,7 +347,7 @@ static ssize_t real_power_state_show(struct device *dev, if (ret) return ret; - return sprintf(buf, "%s\n", acpi_power_state_string(state)); + return sysfs_emit(buf, "%s\n", acpi_power_state_string(state)); } static DEVICE_ATTR_RO(real_power_state); @@ -357,7 +357,7 @@ static ssize_t power_state_show(struct device *dev, { struct acpi_device *adev = to_acpi_device(dev); - return sprintf(buf, "%s\n", acpi_power_state_string(adev->power.state)); + return sysfs_emit(buf, "%s\n", acpi_power_state_string(adev->power.state)); } static DEVICE_ATTR_RO(power_state); @@ -399,16 +399,43 @@ hid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi_dev = to_acpi_device(dev); - return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev)); + return sysfs_emit(buf, "%s\n", acpi_device_hid(acpi_dev)); } static DEVICE_ATTR_RO(hid); +static ssize_t cid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_device_info *info = NULL; + ssize_t len = 0; + + acpi_get_object_info(acpi_dev->handle, &info); + if (!info) + return 0; + + if (info->valid & ACPI_VALID_CID) { + struct acpi_pnp_device_id_list *cid_list = &info->compatible_id_list; + int i; + + for (i = 0; i < cid_list->count - 1; i++) + len += sysfs_emit_at(buf, len, "%s,", cid_list->ids[i].string); + + len += sysfs_emit_at(buf, len, "%s\n", cid_list->ids[i].string); + } + + kfree(info); + + return len; +} +static DEVICE_ATTR_RO(cid); + static ssize_t uid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi_dev = to_acpi_device(dev); - return sprintf(buf, "%s\n", acpi_device_uid(acpi_dev)); + return sysfs_emit(buf, "%s\n", acpi_device_uid(acpi_dev)); } static DEVICE_ATTR_RO(uid); @@ -418,9 +445,9 @@ static ssize_t adr_show(struct device *dev, struct acpi_device *acpi_dev = to_acpi_device(dev); if (acpi_dev->pnp.bus_address > U32_MAX) - return sprintf(buf, "0x%016llx\n", acpi_dev->pnp.bus_address); + return sysfs_emit(buf, "0x%016llx\n", acpi_dev->pnp.bus_address); else - return sprintf(buf, "0x%08llx\n", acpi_dev->pnp.bus_address); + return sysfs_emit(buf, "0x%08llx\n", acpi_dev->pnp.bus_address); } static DEVICE_ATTR_RO(adr); @@ -482,7 +509,7 @@ sun_show(struct device *dev, struct device_attribute *attr, if (ACPI_FAILURE(status)) return -EIO; - return sprintf(buf, "%llu\n", sun); + return sysfs_emit(buf, "%llu\n", sun); } static DEVICE_ATTR_RO(sun); @@ -498,7 +525,7 @@ hrv_show(struct device *dev, struct device_attribute *attr, if (ACPI_FAILURE(status)) return -EIO; - return sprintf(buf, "%llu\n", hrv); + return sysfs_emit(buf, "%llu\n", hrv); } static DEVICE_ATTR_RO(hrv); @@ -513,13 +540,14 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr, if (ACPI_FAILURE(status)) return -EIO; - return sprintf(buf, "%llu\n", sta); + return sysfs_emit(buf, "%llu\n", sta); } static DEVICE_ATTR_RO(status); static struct attribute *acpi_attrs[] = { &dev_attr_path.attr, &dev_attr_hid.attr, + &dev_attr_cid.attr, &dev_attr_modalias.attr, &dev_attr_description.attr, &dev_attr_adr.attr, @@ -562,6 +590,9 @@ static bool acpi_show_attr(struct acpi_device *dev, const struct device_attribut if (attr == &dev_attr_status) return acpi_has_method(dev->handle, "_STA"); + if (attr == &dev_attr_cid) + return acpi_has_method(dev->handle, "_CID"); + /* * If device has _EJ0, 'eject' file is created that is used to trigger * hot-removal function from userland. diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 59b3d50ff01e..197970339edc 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -23,6 +23,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/list.h> +#include <linux/platform_device.h> #include <linux/printk.h> #include <linux/spinlock.h> #include <linux/slab.h> @@ -1674,8 +1675,9 @@ static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, bool ca return ret; } -static int acpi_ec_add(struct acpi_device *device) +static int acpi_ec_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_ec *ec; int ret; @@ -1730,7 +1732,7 @@ static int acpi_ec_add(struct acpi_device *device) acpi_handle_info(ec->handle, "EC: Used to handle transactions and events\n"); - device->driver_data = ec; + platform_set_drvdata(pdev, ec); ret = !!request_region(ec->data_addr, 1, "EC data"); WARN(!ret, "Could not request EC data io port 0x%lx", ec->data_addr); @@ -1750,14 +1752,11 @@ err: return ret; } -static void acpi_ec_remove(struct acpi_device *device) +static void acpi_ec_remove(struct platform_device *pdev) { - struct acpi_ec *ec; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_ec *ec = platform_get_drvdata(pdev); - if (!device) - return; - - ec = acpi_driver_data(device); release_region(ec->data_addr, 1); release_region(ec->command_addr, 1); device->driver_data = NULL; @@ -2095,8 +2094,7 @@ out: #ifdef CONFIG_PM_SLEEP static int acpi_ec_suspend(struct device *dev) { - struct acpi_ec *ec = - acpi_driver_data(to_acpi_device(dev)); + struct acpi_ec *ec = dev_get_drvdata(dev); if (!pm_suspend_no_platform() && ec_freeze_events) acpi_ec_disable_event(ec); @@ -2105,7 +2103,7 @@ static int acpi_ec_suspend(struct device *dev) static int acpi_ec_suspend_noirq(struct device *dev) { - struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); + struct acpi_ec *ec = dev_get_drvdata(dev); /* * The SCI handler doesn't run at this point, so the GPE can be @@ -2122,7 +2120,7 @@ static int acpi_ec_suspend_noirq(struct device *dev) static int acpi_ec_resume_noirq(struct device *dev) { - struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); + struct acpi_ec *ec = dev_get_drvdata(dev); acpi_ec_leave_noirq(ec); @@ -2135,8 +2133,7 @@ static int acpi_ec_resume_noirq(struct device *dev) static int acpi_ec_resume(struct device *dev) { - struct acpi_ec *ec = - acpi_driver_data(to_acpi_device(dev)); + struct acpi_ec *ec = dev_get_drvdata(dev); acpi_ec_enable_event(ec); return 0; @@ -2265,15 +2262,14 @@ module_param_call(ec_event_clearing, param_set_event_clearing, param_get_event_c NULL, 0644); MODULE_PARM_DESC(ec_event_clearing, "Assumed SCI_EVT clearing timing"); -static struct acpi_driver acpi_ec_driver = { - .name = "ec", - .class = ACPI_EC_CLASS, - .ids = ec_device_ids, - .ops = { - .add = acpi_ec_add, - .remove = acpi_ec_remove, - }, - .drv.pm = &acpi_ec_pm, +static struct platform_driver acpi_ec_driver = { + .probe = acpi_ec_probe, + .remove = acpi_ec_remove, + .driver = { + .name = "acpi-ec", + .acpi_match_table = ec_device_ids, + .pm = &acpi_ec_pm, + }, }; static void acpi_ec_destroy_workqueues(void) @@ -2378,17 +2374,7 @@ void __init acpi_ec_init(void) } /* Driver must be registered after acpi_ec_init_workqueues(). */ - acpi_bus_register_driver(&acpi_ec_driver); + platform_driver_register(&acpi_ec_driver); acpi_ec_ecdt_start(); } - -/* EC driver currently not unloadable */ -#if 0 -static void __exit acpi_ec_exit(void) -{ - - acpi_bus_unregister_driver(&acpi_ec_driver); - acpi_ec_destroy_workqueues(); -} -#endif /* 0 */ diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c index 3499f86c411e..4d5e12ed6f3c 100644 --- a/drivers/acpi/hed.c +++ b/drivers/acpi/hed.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/acpi.h> +#include <linux/platform_device.h> #include <acpi/hed.h> static const struct acpi_device_id acpi_hed_ids[] = { @@ -47,8 +48,9 @@ static void acpi_hed_notify(acpi_handle handle, u32 event, void *data) blocking_notifier_call_chain(&acpi_hed_notify_list, 0, NULL); } -static int acpi_hed_add(struct acpi_device *device) +static int acpi_hed_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); int err; /* Only one hardware error device */ @@ -64,26 +66,27 @@ static int acpi_hed_add(struct acpi_device *device) return err; } -static void acpi_hed_remove(struct acpi_device *device) +static void acpi_hed_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, acpi_hed_notify); hed_handle = NULL; } -static struct acpi_driver acpi_hed_driver = { - .name = "hardware_error_device", - .class = "hardware_error", - .ids = acpi_hed_ids, - .ops = { - .add = acpi_hed_add, - .remove = acpi_hed_remove, +static struct platform_driver acpi_hed_driver = { + .probe = acpi_hed_probe, + .remove = acpi_hed_remove, + .driver = { + .name = "acpi-hardware-error-device", + .acpi_match_table = acpi_hed_ids, }, }; static int __init acpi_hed_driver_init(void) { - return acpi_bus_register_driver(&acpi_hed_driver); + return platform_driver_register(&acpi_hed_driver); } subsys_initcall(acpi_hed_driver_init); diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 3eb56b77cb6d..e19aba02b800 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2,6 +2,7 @@ /* * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. */ +#include <linux/platform_device.h> #include <linux/list_sort.h> #include <linux/libnvdimm.h> #include <linux/module.h> @@ -89,15 +90,22 @@ static const guid_t *to_nfit_bus_uuid(int family) static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc) { struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc; + struct acpi_device *adev; - /* - * If provider == 'ACPI.NFIT' we can assume 'dev' is a struct - * acpi_device. - */ + /* If provider == 'ACPI.NFIT', a struct acpi_device is there. */ if (!nd_desc->provider_name || strcmp(nd_desc->provider_name, "ACPI.NFIT") != 0) return NULL; + /* + * But it can be the ACPI companion of acpi_desc->dev when it cones from + * acpi_nfit_probe(). + */ + adev = ACPI_COMPANION(acpi_desc->dev); + if (adev) + return adev; + + /* Or it is acpi_desc->dev itself when it comes from nfit_ctl_test(). */ return to_acpi_device(acpi_desc->dev); } @@ -3283,11 +3291,11 @@ static void acpi_nfit_put_table(void *table) static void acpi_nfit_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *adev = data; + struct device *dev = data; - device_lock(&adev->dev); - __acpi_nfit_notify(&adev->dev, handle, event); - device_unlock(&adev->dev); + device_lock(dev); + __acpi_nfit_notify(dev, handle, event); + device_unlock(dev); } static void acpi_nfit_remove_notify_handler(void *data) @@ -3328,18 +3336,19 @@ void acpi_nfit_shutdown(void *data) } EXPORT_SYMBOL_GPL(acpi_nfit_shutdown); -static int acpi_nfit_add(struct acpi_device *adev) +static int acpi_nfit_probe(struct platform_device *pdev) { struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_nfit_desc *acpi_desc; - struct device *dev = &adev->dev; + struct device *dev = &pdev->dev; + struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_table_header *tbl; acpi_status status = AE_OK; acpi_size sz; int rc = 0; rc = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, - acpi_nfit_notify, adev); + acpi_nfit_notify, dev); if (rc) return rc; @@ -3369,7 +3378,7 @@ static int acpi_nfit_add(struct acpi_device *adev) acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); if (!acpi_desc) return -ENOMEM; - acpi_nfit_desc_init(acpi_desc, &adev->dev); + acpi_nfit_desc_init(acpi_desc, dev); /* Save the acpi header for exporting the revision via sysfs */ acpi_desc->acpi_header = *tbl; @@ -3474,11 +3483,11 @@ static const struct acpi_device_id acpi_nfit_ids[] = { }; MODULE_DEVICE_TABLE(acpi, acpi_nfit_ids); -static struct acpi_driver acpi_nfit_driver = { - .name = KBUILD_MODNAME, - .ids = acpi_nfit_ids, - .ops = { - .add = acpi_nfit_add, +static struct platform_driver acpi_nfit_driver = { + .probe = acpi_nfit_probe, + .driver = { + .name = "acpi-nfit", + .acpi_match_table = acpi_nfit_ids, }, }; @@ -3516,7 +3525,7 @@ static __init int nfit_init(void) return -ENOMEM; nfit_mce_register(); - ret = acpi_bus_register_driver(&acpi_nfit_driver); + ret = platform_driver_register(&acpi_nfit_driver); if (ret) { nfit_mce_unregister(); destroy_workqueue(nfit_wq); @@ -3529,7 +3538,7 @@ static __init int nfit_init(void) static __exit void nfit_exit(void) { nfit_mce_unregister(); - acpi_bus_unregister_driver(&acpi_nfit_driver); + platform_driver_unregister(&acpi_nfit_driver); destroy_workqueue(nfit_wq); WARN_ON(!list_empty(&acpi_descs)); } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 74ade4160314..9d7f85dadc48 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -738,7 +738,7 @@ static int acpi_pci_root_add(struct acpi_device *device, if (no_aspm) pcie_no_aspm(); - pci_acpi_add_bus_pm_notifier(device); + pci_acpi_add_root_pm_notifier(device, root); device_set_wakeup_capable(root->bus->bridge, device->wakeup.flags.valid); if (hotadd) { diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index 741bcc9d6d6a..15234b65ea22 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -42,8 +42,9 @@ static int check_slot(acpi_handle handle, unsigned long long *sun) { int device = -1; - unsigned long long adr, sta; + unsigned long long sta; acpi_status status; + u64 adr; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); @@ -56,10 +57,9 @@ check_slot(acpi_handle handle, unsigned long long *sun) goto out; } - status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); - if (ACPI_FAILURE(status)) { - pr_debug("_ADR returned %d on %s\n", - status, (char *)buffer.pointer); + if (acpi_get_local_u64_address(handle, &adr)) { + pr_debug("_ADR returned with failure on %s\n", + (char *)buffer.pointer); goto out; } diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 65e779be64ff..882709796b4f 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -166,8 +166,7 @@ static int __acpi_processor_start(struct acpi_device *device) if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS)) dev_dbg(&device->dev, "CPPC data invalid or not present\n"); - if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) - acpi_processor_power_init(pr); + acpi_processor_power_init(pr); acpi_pss_perf_init(pr); @@ -259,9 +258,11 @@ static int __init acpi_processor_driver_init(void) acpi_processor_ignore_ppc_init(); } + acpi_processor_register_idle_driver(); + result = driver_register(&acpi_processor_driver); if (result < 0) - return result; + goto unregister_idle_drv; result = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "acpi/cpu-drv:online", @@ -283,8 +284,13 @@ static int __init acpi_processor_driver_init(void) acpi_idle_rescan_dead_smt_siblings(); return 0; + err: driver_unregister(&acpi_processor_driver); + +unregister_idle_drv: + acpi_processor_unregister_idle_driver(); + return result; } @@ -302,6 +308,7 @@ static void __exit acpi_processor_driver_exit(void) cpuhp_remove_state_nocalls(hp_online); cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD); driver_unregister(&acpi_processor_driver); + acpi_processor_unregister_idle_driver(); } module_init(acpi_processor_driver_init); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 89f2f08b2554..81f372c64074 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -51,7 +51,7 @@ module_param(latency_factor, uint, 0644); static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device); -struct cpuidle_driver acpi_idle_driver = { +static struct cpuidle_driver acpi_idle_driver = { .name = "acpi_idle", .owner = THIS_MODULE, }; @@ -946,6 +946,8 @@ static int acpi_processor_evaluate_lpi(acpi_handle handle, lpi_state->entry_method = ACPI_CSTATE_INTEGER; lpi_state->address = obj->integer.value; } else { + pr_debug("Entry method of state-%d is invalid, disable it.\n", + state_idx); continue; } @@ -1178,7 +1180,7 @@ static int acpi_idle_lpi_enter(struct cpuidle_device *dev, return -EINVAL; } -static int acpi_processor_setup_lpi_states(struct acpi_processor *pr) +static void acpi_processor_setup_lpi_states(struct acpi_processor *pr) { int i; struct acpi_lpi_state *lpi; @@ -1186,7 +1188,7 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr) struct cpuidle_driver *drv = &acpi_idle_driver; if (!pr->flags.has_lpi) - return -EOPNOTSUPP; + return; for (i = 0; i < pr->power.count && i < CPUIDLE_STATE_MAX; i++) { lpi = &pr->power.lpi_states[i]; @@ -1204,8 +1206,6 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr) } drv->state_count = i; - - return 0; } /** @@ -1214,13 +1214,13 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr) * * @pr: the ACPI processor */ -static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) +static void acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) { int i; struct cpuidle_driver *drv = &acpi_idle_driver; if (!pr->flags.power_setup_done || !pr->flags.power) - return -EINVAL; + return; drv->safe_state_index = -1; for (i = ACPI_IDLE_STATE_START; i < CPUIDLE_STATE_MAX; i++) { @@ -1228,32 +1228,30 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) drv->states[i].desc[0] = '\0'; } - if (pr->flags.has_lpi) - return acpi_processor_setup_lpi_states(pr); + if (pr->flags.has_lpi) { + acpi_processor_setup_lpi_states(pr); + return; + } acpi_processor_setup_cstates(pr); - return 0; } /** - * acpi_processor_setup_cpuidle_dev - prepares and configures CPUIDLE + * acpi_processor_setup_cpuidle_dev - configures CPUIDLE * device i.e. per-cpu data * * @pr: the ACPI processor * @dev : the cpuidle device */ -static int acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr, - struct cpuidle_device *dev) +static void acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr, + struct cpuidle_device *dev) { if (!pr->flags.power_setup_done || !pr->flags.power || !dev) - return -EINVAL; + return; dev->cpu = pr->id; - if (pr->flags.has_lpi) - return acpi_processor_ffh_lpi_probe(pr->id); - - acpi_processor_setup_cpuidle_cx(pr, dev); - return 0; + if (!pr->flags.has_lpi) + acpi_processor_setup_cpuidle_cx(pr, dev); } static int acpi_processor_get_power_info(struct acpi_processor *pr) @@ -1262,7 +1260,13 @@ static int acpi_processor_get_power_info(struct acpi_processor *pr) ret = acpi_processor_get_lpi_info(pr); if (ret) - ret = acpi_processor_get_cstate_info(pr); + return acpi_processor_get_cstate_info(pr); + + if (pr->flags.has_lpi) { + ret = acpi_processor_ffh_lpi_probe(pr->id); + if (ret) + pr_err("CPU%u: Invalid FFH LPI data\n", pr->id); + } return ret; } @@ -1347,79 +1351,103 @@ int acpi_processor_power_state_has_changed(struct acpi_processor *pr) return 0; } -static int acpi_processor_registered; +void acpi_processor_register_idle_driver(void) +{ + struct acpi_processor *pr; + int ret = -ENODEV; + int cpu; + + /* + * ACPI idle driver is used by all possible CPUs. + * Use the processor power info of one in them to set up idle states. + * Note that the existing idle handler will be used on platforms that + * only support C1. + */ + for_each_possible_cpu(cpu) { + pr = per_cpu(processors, cpu); + if (!pr) + continue; + + acpi_processor_cstate_first_run_checks(); + ret = acpi_processor_get_power_info(pr); + if (!ret) { + pr->flags.power_setup_done = 1; + acpi_processor_setup_cpuidle_states(pr); + break; + } + } + + if (ret) { + pr_debug("No ACPI power information from any CPUs.\n"); + return; + } -int acpi_processor_power_init(struct acpi_processor *pr) + ret = cpuidle_register_driver(&acpi_idle_driver); + if (ret) { + pr_debug("register %s failed.\n", acpi_idle_driver.name); + return; + } + pr_debug("%s registered with cpuidle.\n", acpi_idle_driver.name); +} + +void acpi_processor_unregister_idle_driver(void) +{ + cpuidle_unregister_driver(&acpi_idle_driver); +} + +void acpi_processor_power_init(struct acpi_processor *pr) { - int retval; struct cpuidle_device *dev; + /* + * The code below only works if the current cpuidle driver is the ACPI + * idle driver. + */ + if (cpuidle_get_driver() != &acpi_idle_driver) + return; + if (disabled_by_idle_boot_param()) - return 0; + return; acpi_processor_cstate_first_run_checks(); if (!acpi_processor_get_power_info(pr)) pr->flags.power_setup_done = 1; - /* - * Install the idle handler if processor power management is supported. - * Note that we use previously set idle handler will be used on - * platforms that only support C1. - */ - if (pr->flags.power) { - /* Register acpi_idle_driver if not already registered */ - if (!acpi_processor_registered) { - acpi_processor_setup_cpuidle_states(pr); - retval = cpuidle_register_driver(&acpi_idle_driver); - if (retval) - return retval; - pr_debug("%s registered with cpuidle\n", - acpi_idle_driver.name); - } + if (!pr->flags.power) + return; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - per_cpu(acpi_cpuidle_device, pr->id) = dev; + per_cpu(acpi_cpuidle_device, pr->id) = dev; - acpi_processor_setup_cpuidle_dev(pr, dev); + acpi_processor_setup_cpuidle_dev(pr, dev); - /* Register per-cpu cpuidle_device. Cpuidle driver - * must already be registered before registering device - */ - retval = cpuidle_register_device(dev); - if (retval) { - if (acpi_processor_registered == 0) - cpuidle_unregister_driver(&acpi_idle_driver); - - per_cpu(acpi_cpuidle_device, pr->id) = NULL; - kfree(dev); - return retval; - } - acpi_processor_registered++; + /* + * Register a cpuidle device for this CPU. The cpuidle driver using + * this device is expected to be registered. + */ + if (cpuidle_register_device(dev)) { + per_cpu(acpi_cpuidle_device, pr->id) = NULL; + kfree(dev); } - return 0; } -int acpi_processor_power_exit(struct acpi_processor *pr) +void acpi_processor_power_exit(struct acpi_processor *pr) { struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id); if (disabled_by_idle_boot_param()) - return 0; + return; if (pr->flags.power) { cpuidle_unregister_device(dev); - acpi_processor_registered--; - if (acpi_processor_registered == 0) - cpuidle_unregister_driver(&acpi_idle_driver); - kfree(dev); } pr->flags.power_setup_done = 0; - return 0; } MODULE_IMPORT_NS("ACPI_PROCESSOR_IDLE"); diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index d16906f46484..bc8050d8a6f5 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -532,6 +532,12 @@ static const struct dmi_system_id irq1_level_low_skip_override[] = { DMI_MATCH(DMI_BOARD_NAME, "16T90SP"), }, }, + { + /* JWIPC JVC9100 */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "JVC9100"), + }, + }, { } }; @@ -706,6 +712,8 @@ struct irq_override_cmp { static const struct irq_override_cmp override_table[] = { { irq1_level_low_skip_override, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false }, + { irq1_level_low_skip_override, 10, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 1, false }, + { irq1_level_low_skip_override, 11, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 1, false }, { irq1_edge_low_force_override, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true }, }; diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index d3edc3bcbf01..85160e475c97 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -19,6 +19,7 @@ #include <linux/timer.h> #include <linux/jiffies.h> #include <linux/delay.h> +#include <linux/platform_device.h> #include <linux/power_supply.h> #include <linux/platform_data/x86/apple.h> #include <acpi/battery.h> @@ -95,7 +96,7 @@ struct acpi_sbs { #define to_acpi_sbs(x) power_supply_get_drvdata(x) -static void acpi_sbs_remove(struct acpi_device *device); +static void acpi_sbs_remove(struct platform_device *pdev); static int acpi_battery_get_state(struct acpi_battery *battery); static inline int battery_scale(int log) @@ -628,8 +629,9 @@ static void acpi_sbs_callback(void *context) } } -static int acpi_sbs_add(struct acpi_device *device) +static int acpi_sbs_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_sbs *sbs; int result = 0; int id; @@ -642,11 +644,12 @@ static int acpi_sbs_add(struct acpi_device *device) mutex_init(&sbs->lock); - sbs->hc = acpi_driver_data(acpi_dev_parent(device)); + platform_set_drvdata(pdev, sbs); + + sbs->hc = dev_get_drvdata(pdev->dev.parent); sbs->device = device; strscpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_SBS_CLASS); - device->driver_data = sbs; result = acpi_charger_add(sbs); if (result && result != -ENODEV) @@ -670,20 +673,15 @@ static int acpi_sbs_add(struct acpi_device *device) acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs); end: if (result) - acpi_sbs_remove(device); + acpi_sbs_remove(pdev); return result; } -static void acpi_sbs_remove(struct acpi_device *device) +static void acpi_sbs_remove(struct platform_device *pdev) { - struct acpi_sbs *sbs; + struct acpi_sbs *sbs = platform_get_drvdata(pdev); int id; - if (!device) - return; - sbs = acpi_driver_data(device); - if (!sbs) - return; mutex_lock(&sbs->lock); acpi_smbus_unregister_callback(sbs->hc); for (id = 0; id < MAX_SBS_BAT; ++id) @@ -697,11 +695,7 @@ static void acpi_sbs_remove(struct acpi_device *device) #ifdef CONFIG_PM_SLEEP static int acpi_sbs_resume(struct device *dev) { - struct acpi_sbs *sbs; - if (!dev) - return -EINVAL; - sbs = to_acpi_device(dev)->driver_data; - acpi_sbs_callback(sbs); + acpi_sbs_callback(dev_get_drvdata(dev)); return 0; } #else @@ -710,14 +704,14 @@ static int acpi_sbs_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(acpi_sbs_pm, NULL, acpi_sbs_resume); -static struct acpi_driver acpi_sbs_driver = { - .name = "sbs", - .class = ACPI_SBS_CLASS, - .ids = sbs_device_ids, - .ops = { - .add = acpi_sbs_add, - .remove = acpi_sbs_remove, - }, - .drv.pm = &acpi_sbs_pm, +static struct platform_driver acpi_sbs_driver = { + .probe = acpi_sbs_probe, + .remove = acpi_sbs_remove, + .driver = { + .name = "acpi-sbs", + .acpi_match_table = sbs_device_ids, + .pm = &acpi_sbs_pm, + }, }; -module_acpi_driver(acpi_sbs_driver); + +module_platform_driver(acpi_sbs_driver); diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c index 1a2bf520be23..7fc8ae7396d3 100644 --- a/drivers/acpi/sbshc.c +++ b/drivers/acpi/sbshc.c @@ -13,6 +13,8 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/platform_device.h> + #include "sbshc.h" #include "internal.h" @@ -30,8 +32,8 @@ struct acpi_smb_hc { bool done; }; -static int acpi_smbus_hc_add(struct acpi_device *device); -static void acpi_smbus_hc_remove(struct acpi_device *device); +static int acpi_smbus_hc_probe(struct platform_device *pdev); +static void acpi_smbus_hc_remove(struct platform_device *pdev); static const struct acpi_device_id sbs_device_ids[] = { {"ACPI0001", 0}, @@ -41,14 +43,13 @@ static const struct acpi_device_id sbs_device_ids[] = { MODULE_DEVICE_TABLE(acpi, sbs_device_ids); -static struct acpi_driver acpi_smb_hc_driver = { - .name = "smbus_hc", - .class = ACPI_SMB_HC_CLASS, - .ids = sbs_device_ids, - .ops = { - .add = acpi_smbus_hc_add, - .remove = acpi_smbus_hc_remove, - }, +static struct platform_driver acpi_smb_hc_driver = { + .probe = acpi_smbus_hc_probe, + .remove = acpi_smbus_hc_remove, + .driver = { + .name = "acpi-smbus-hc", + .acpi_match_table = sbs_device_ids, + }, }; union acpi_smb_status { @@ -237,15 +238,13 @@ static int smbus_alarm(void *context) return 0; } -static int acpi_smbus_hc_add(struct acpi_device *device) +static int acpi_smbus_hc_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); int status; unsigned long long val; struct acpi_smb_hc *hc; - if (!device) - return -EINVAL; - status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val); if (ACPI_FAILURE(status)) { pr_err("error obtaining _EC.\n"); @@ -261,10 +260,11 @@ static int acpi_smbus_hc_add(struct acpi_device *device) mutex_init(&hc->lock); init_waitqueue_head(&hc->wait); - hc->ec = acpi_driver_data(acpi_dev_parent(device)); + platform_set_drvdata(pdev, hc); + + hc->ec = dev_get_drvdata(pdev->dev.parent); hc->offset = (val >> 8) & 0xff; hc->query_bit = val & 0xff; - device->driver_data = hc; acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc); dev_info(&device->dev, "SBS HC: offset = 0x%0x, query_bit = 0x%0x\n", @@ -273,21 +273,18 @@ static int acpi_smbus_hc_add(struct acpi_device *device) return 0; } -static void acpi_smbus_hc_remove(struct acpi_device *device) +static void acpi_smbus_hc_remove(struct platform_device *pdev) { - struct acpi_smb_hc *hc; - - if (!device) - return; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_smb_hc *hc = platform_get_drvdata(pdev); - hc = acpi_driver_data(device); acpi_ec_remove_query_handler(hc->ec, hc->query_bit); acpi_os_wait_events_complete(); kfree(hc); device->driver_data = NULL; } -module_acpi_driver(acpi_smb_hc_driver); +module_platform_driver(acpi_smb_hc_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alexey Starikovskiy"); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 416d87f9bd10..565a84a7d7bc 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) "ACPI: " fmt +#include <linux/async.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -42,6 +43,7 @@ static LIST_HEAD(acpi_scan_handlers_list); DEFINE_MUTEX(acpi_device_lock); LIST_HEAD(acpi_wakeup_device_list); static DEFINE_MUTEX(acpi_hp_context_lock); +static LIST_HEAD(acpi_scan_system_dev_list); /* * The UART device described by the SPCR table is the only object which needs @@ -998,15 +1000,11 @@ static int acpi_bus_extract_wakeup_device_power_package(struct acpi_device *dev) return err; } -/* Do not use a button for S5 wakeup */ -#define ACPI_AVOID_WAKE_FROM_S5 BIT(0) - static bool acpi_wakeup_gpe_init(struct acpi_device *device) { static const struct acpi_device_id button_device_ids[] = { - {"PNP0C0C", 0}, /* Power button */ - {"PNP0C0D", ACPI_AVOID_WAKE_FROM_S5}, /* Lid */ - {"PNP0C0E", ACPI_AVOID_WAKE_FROM_S5}, /* Sleep button */ + {"PNP0C0D", 0}, /* Lid */ + {"PNP0C0E", 0}, /* Sleep button */ {"", 0}, }; struct acpi_device_wakeup *wakeup = &device->wakeup; @@ -1015,16 +1013,9 @@ static bool acpi_wakeup_gpe_init(struct acpi_device *device) wakeup->flags.notifier_present = 0; - /* Power button, Lid switch always enable wakeup */ match = acpi_match_acpi_device(button_device_ids, device); - if (match) { - if ((match->driver_data & ACPI_AVOID_WAKE_FROM_S5) && - wakeup->sleep_state == ACPI_STATE_S5) - wakeup->sleep_state = ACPI_STATE_S4; - acpi_mark_gpe_for_wake(wakeup->gpe_device, wakeup->gpe_number); - device_set_wakeup_capable(&device->dev, true); - return true; - } + if (match && wakeup->sleep_state == ACPI_STATE_S5) + wakeup->sleep_state = ACPI_STATE_S4; status = acpi_setup_gpe_for_wake(device->handle, wakeup->gpe_device, wakeup->gpe_number); @@ -1294,8 +1285,6 @@ acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context, * The device will get a Linux specific CID added in scan.c to * identify the device as an ACPI graphics device * Be aware that the graphics device may not be physically present - * Use acpi_video_get_capabilities() to detect general ACPI video - * capabilities of present cards */ long acpi_is_video_device(acpi_handle handle) { @@ -1469,6 +1458,7 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, break; case ACPI_BUS_TYPE_THERMAL: acpi_add_id(pnp, ACPI_THERMAL_HID); + pnp->type.platform_id = 1; break; case ACPI_BUS_TYPE_POWER_BUTTON: acpi_add_id(pnp, ACPI_BUTTON_HID_POWERF); @@ -2203,19 +2193,48 @@ static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used, return acpi_bus_check_add(handle, false, (struct acpi_device **)ret_p); } +struct acpi_scan_system_dev { + struct list_head node; + struct acpi_device *adev; +}; + +static const char * const acpi_system_dev_ids[] = { + "PNP0C01", /* Memory controller */ + "PNP0C02", /* Motherboard resource */ + NULL +}; + static void acpi_default_enumeration(struct acpi_device *device) { /* * Do not enumerate devices with enumeration_by_parent flag set as * they will be enumerated by their respective parents. */ - if (!device->flags.enumeration_by_parent) { - acpi_create_platform_device(device, NULL); - acpi_device_set_enumerated(device); - } else { + if (device->flags.enumeration_by_parent) { blocking_notifier_call_chain(&acpi_reconfig_chain, ACPI_RECONFIG_DEVICE_ADD, device); + return; + } + if (match_string(acpi_system_dev_ids, -1, acpi_device_hid(device)) >= 0) { + struct acpi_scan_system_dev *sd; + + /* + * This is a generic system device, so there is no need to + * create a platform device for it, but its resources need to be + * reserved. However, that needs to be done after all of the + * other device objects have been processed and PCI has claimed + * BARs in case there are resource conflicts. + */ + sd = kmalloc(sizeof(*sd), GFP_KERNEL); + if (sd) { + sd->adev = device; + list_add_tail(&sd->node, &acpi_scan_system_dev_list); + } + } else { + /* For a regular device object, create a platform device. */ + acpi_create_platform_device(device, NULL); } + acpi_device_set_enumerated(device); } static const struct acpi_device_id generic_device_ids[] = { @@ -2320,7 +2339,8 @@ static int acpi_bus_attach(struct acpi_device *device, void *first_pass) if (ret < 0) return 0; - if (device->pnp.type.platform_id || device->flags.enumeration_by_parent) + if (device->pnp.type.platform_id || device->pnp.type.backlight || + device->flags.enumeration_by_parent) acpi_default_enumeration(device); else acpi_device_set_enumerated(device); @@ -2360,46 +2380,34 @@ static int acpi_dev_get_next_consumer_dev_cb(struct acpi_dep_data *dep, void *da return 0; } -struct acpi_scan_clear_dep_work { - struct work_struct work; - struct acpi_device *adev; -}; - -static void acpi_scan_clear_dep_fn(struct work_struct *work) +static void acpi_scan_clear_dep_fn(void *dev, async_cookie_t cookie) { - struct acpi_scan_clear_dep_work *cdw; - - cdw = container_of(work, struct acpi_scan_clear_dep_work, work); + struct acpi_device *adev = to_acpi_device(dev); acpi_scan_lock_acquire(); - acpi_bus_attach(cdw->adev, (void *)true); + acpi_bus_attach(adev, (void *)true); acpi_scan_lock_release(); - acpi_dev_put(cdw->adev); - kfree(cdw); + acpi_dev_put(adev); } static bool acpi_scan_clear_dep_queue(struct acpi_device *adev) { - struct acpi_scan_clear_dep_work *cdw; - if (adev->dep_unmet) return false; - cdw = kmalloc(sizeof(*cdw), GFP_KERNEL); - if (!cdw) - return false; - - cdw->adev = adev; - INIT_WORK(&cdw->work, acpi_scan_clear_dep_fn); /* - * Since the work function may block on the lock until the entire - * initial enumeration of devices is complete, put it into the unbound - * workqueue. + * Async schedule the deferred acpi_scan_clear_dep_fn() since: + * - acpi_bus_attach() needs to hold acpi_scan_lock which cannot + * be acquired under acpi_dep_list_lock (held here) + * - the deferred work at boot stage is ensured to be finished + * before userspace init task by the async_synchronize_full() + * barrier + * + * Use _nocall variant since it'll return on failure instead of + * run the function synchronously. */ - queue_work(system_dfl_wq, &cdw->work); - - return true; + return async_schedule_dev_nocall(acpi_scan_clear_dep_fn, &adev->dev); } static void acpi_scan_delete_dep_data(struct acpi_dep_data *dep) @@ -2571,6 +2579,87 @@ static void acpi_scan_postponed(void) mutex_unlock(&acpi_dep_list_lock); } +static void acpi_scan_claim_resources(struct acpi_device *adev) +{ + struct resource_entry *rentry; + LIST_HEAD(resource_list); + unsigned int count = 0; + const char *regionid; + + if (acpi_dev_get_resources(adev, &resource_list, NULL, NULL) <= 0) + return; + + regionid = kstrdup(dev_name(&adev->dev), GFP_KERNEL); + if (!regionid) + goto exit; + + list_for_each_entry(rentry, &resource_list, node) { + struct resource *res = rentry->res; + struct resource *r; + + /* Skip disabled and invalid resources. */ + if ((res->flags & IORESOURCE_DISABLED) || res->end < res->start) + continue; + + if (resource_type(res) == IORESOURCE_IO) { + /* + * Follow the PNP system driver and on x86 skip I/O + * resources that start below 0x100 (the "standard PC + * hardware" boundary). + */ + if (IS_ENABLED(CONFIG_X86) && res->start < 0x100) { + dev_info(&adev->dev, "Skipped %pR\n", res); + continue; + } + r = request_region(res->start, resource_size(res), regionid); + } else if (resource_type(res) == IORESOURCE_MEM) { + r = request_mem_region(res->start, resource_size(res), regionid); + } else { + continue; + } + + if (r) { + r->flags &= ~IORESOURCE_BUSY; + dev_info(&adev->dev, "Reserved %pR\n", r); + count++; + } else { + /* + * Failures at this point are usually harmless. PCI + * quirks, for example, reserve resources they know + * about too, so there may well be double reservations. + */ + dev_info(&adev->dev, "Could not reserve %pR\n", res); + } + } + + if (!count) + kfree(regionid); + +exit: + acpi_dev_free_resource_list(&resource_list); +} + +static int __init acpi_reserve_motherboard_resources(void) +{ + struct acpi_scan_system_dev *sd, *tmp; + + guard(mutex)(&acpi_scan_lock); + + list_for_each_entry_safe(sd, tmp, &acpi_scan_system_dev_list, node) { + acpi_scan_claim_resources(sd->adev); + list_del(&sd->node); + kfree(sd); + } + + return 0; +} + +/* + * Reserve motherboard resources after PCI claims BARs, but before PCI assigns + * resources for uninitialized PCI devices. + */ +fs_initcall(acpi_reserve_motherboard_resources); + /** * acpi_bus_scan - Add ACPI device node objects in a given namespace scope. * @handle: Root of the namespace scope to scan. @@ -2642,38 +2731,27 @@ int acpi_bus_register_early_device(int type) if (result) return result; - device->flags.match_driver = true; - return device_attach(&device->dev); + acpi_default_enumeration(device); + return 0; } EXPORT_SYMBOL_GPL(acpi_bus_register_early_device); -static void acpi_bus_scan_fixed(void) +static void acpi_bus_add_fixed_device_object(enum acpi_bus_device_type type) { - if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) { - struct acpi_device *adev = NULL; + struct acpi_device *adev = NULL; - acpi_add_single_object(&adev, NULL, ACPI_BUS_TYPE_POWER_BUTTON, - false); - if (adev) { - adev->flags.match_driver = true; - if (device_attach(&adev->dev) >= 0) - device_init_wakeup(&adev->dev, true); - else - dev_dbg(&adev->dev, "No driver\n"); - } - } + acpi_add_single_object(&adev, NULL, type, false); + if (adev) + acpi_default_enumeration(adev); +} - if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) { - struct acpi_device *adev = NULL; +static void acpi_bus_scan_fixed(void) +{ + if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) + acpi_bus_add_fixed_device_object(ACPI_BUS_TYPE_POWER_BUTTON); - acpi_add_single_object(&adev, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON, - false); - if (adev) { - adev->flags.match_driver = true; - if (device_attach(&adev->dev) < 0) - dev_dbg(&adev->dev, "No driver\n"); - } - } + if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) + acpi_bus_add_fixed_device_object(ACPI_BUS_TYPE_SLEEP_BUTTON); } static void __init acpi_get_spcr_uart_addr(void) diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index e596224302f4..1bd2f555e673 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -687,7 +687,7 @@ static ssize_t counter_show(struct kobject *kobj, acpi_irq_not_handled; all_counters[num_gpes + ACPI_NUM_FIXED_EVENTS + COUNT_GPE].count = acpi_gpe_count; - size = sprintf(buf, "%8u", all_counters[index].count); + size = sysfs_emit(buf, "%8u", all_counters[index].count); /* "gpe_all" or "sci" */ if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) @@ -698,29 +698,29 @@ static ssize_t counter_show(struct kobject *kobj, goto end; if (status & ACPI_EVENT_FLAG_ENABLE_SET) - size += sprintf(buf + size, " EN"); + size += sysfs_emit_at(buf, size, " EN"); else - size += sprintf(buf + size, " "); + size += sysfs_emit_at(buf, size, " "); if (status & ACPI_EVENT_FLAG_STATUS_SET) - size += sprintf(buf + size, " STS"); + size += sysfs_emit_at(buf, size, " STS"); else - size += sprintf(buf + size, " "); + size += sysfs_emit_at(buf, size, " "); if (!(status & ACPI_EVENT_FLAG_HAS_HANDLER)) - size += sprintf(buf + size, " invalid "); + size += sysfs_emit_at(buf, size, " invalid "); else if (status & ACPI_EVENT_FLAG_ENABLED) - size += sprintf(buf + size, " enabled "); + size += sysfs_emit_at(buf, size, " enabled "); else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED) - size += sprintf(buf + size, " wake_enabled"); + size += sysfs_emit_at(buf, size, " wake_enabled"); else - size += sprintf(buf + size, " disabled "); + size += sysfs_emit_at(buf, size, " disabled "); if (status & ACPI_EVENT_FLAG_MASKED) - size += sprintf(buf + size, " masked "); + size += sysfs_emit_at(buf, size, " masked "); else - size += sprintf(buf + size, " unmasked"); + size += sysfs_emit_at(buf, size, " unmasked"); end: - size += sprintf(buf + size, "\n"); + size += sysfs_emit_at(buf, size, "\n"); return result ? result : size; } @@ -937,7 +937,7 @@ static void __exit interrupt_stats_exit(void) static ssize_t pm_profile_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", acpi_gbl_FADT.preferred_profile); + return sysfs_emit(buf, "%d\n", acpi_gbl_FADT.preferred_profile); } static const struct kobj_attribute pm_profile_attr = __ATTR_RO(pm_profile); @@ -946,7 +946,7 @@ static ssize_t enabled_show(struct kobject *kobj, struct kobj_attribute *attr, c { struct acpi_hotplug_profile *hotplug = to_acpi_hotplug_profile(kobj); - return sprintf(buf, "%d\n", hotplug->enabled); + return sysfs_emit(buf, "%d\n", hotplug->enabled); } static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr, @@ -1000,7 +1000,7 @@ void acpi_sysfs_add_hotplug_profile(struct acpi_hotplug_profile *hotplug, static ssize_t force_remove_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", 0); + return sysfs_emit(buf, "%d\n", 0); } static ssize_t force_remove_store(struct kobject *kobj, diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index a511f9ea0267..e9d3ab18b404 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -25,6 +25,7 @@ #include <linux/kmod.h> #include <linux/reboot.h> #include <linux/device.h> +#include <linux/platform_device.h> #include <linux/thermal.h> #include <linux/acpi.h> #include <linux/workqueue.h> @@ -670,8 +671,7 @@ static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct acpi_thermal *tz = acpi_driver_data(device); + struct acpi_thermal *tz = data; if (!tz) return; @@ -685,8 +685,8 @@ static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) acpi_thermal_trips_update(tz, event); break; default: - acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n", - event); + acpi_handle_debug(tz->device->handle, + "Unsupported event [0x%x]\n", event); break; } } @@ -777,9 +777,10 @@ static void acpi_thermal_free_thermal_zone(struct acpi_thermal *tz) kfree(tz); } -static int acpi_thermal_add(struct acpi_device *device) +static int acpi_thermal_probe(struct platform_device *pdev) { struct thermal_trip trip_table[ACPI_THERMAL_MAX_NR_TRIPS] = { 0 }; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_thermal_trip *acpi_trip; struct thermal_trip *trip; struct acpi_thermal *tz; @@ -795,11 +796,12 @@ static int acpi_thermal_add(struct acpi_device *device) if (!tz) return -ENOMEM; + platform_set_drvdata(pdev, tz); + tz->device = device; strscpy(tz->name, device->pnp.bus_id); strscpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_THERMAL_CLASS); - device->driver_data = tz; acpi_thermal_aml_dependency_fix(tz); @@ -881,7 +883,7 @@ static int acpi_thermal_add(struct acpi_device *device) acpi_device_bid(device), deci_kelvin_to_celsius(tz->temp_dk)); result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_thermal_notify, device); + acpi_thermal_notify, tz); if (result) goto flush_wq; @@ -896,16 +898,11 @@ free_memory: return result; } -static void acpi_thermal_remove(struct acpi_device *device) +static void acpi_thermal_remove(struct platform_device *pdev) { - struct acpi_thermal *tz; - - if (!device || !acpi_driver_data(device)) - return; + struct acpi_thermal *tz = platform_get_drvdata(pdev); - tz = acpi_driver_data(device); - - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_dev_remove_notify_handler(tz->device, ACPI_DEVICE_NOTIFY, acpi_thermal_notify); flush_workqueue(acpi_thermal_pm_queue); @@ -914,44 +911,26 @@ static void acpi_thermal_remove(struct acpi_device *device) } #ifdef CONFIG_PM_SLEEP -static int acpi_thermal_suspend(struct device *dev) +static int acpi_thermal_prepare(struct device *dev) { /* Make sure the previously queued thermal check work has been done */ flush_workqueue(acpi_thermal_pm_queue); return 0; } -static int acpi_thermal_resume(struct device *dev) +static void acpi_thermal_complete(struct device *dev) { - struct acpi_thermal *tz; - int i, j; - - if (!dev) - return -EINVAL; - - tz = acpi_driver_data(to_acpi_device(dev)); - if (!tz) - return -EINVAL; - - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - struct acpi_thermal_trip *acpi_trip = &tz->trips.active[i].trip; - - if (!acpi_thermal_trip_valid(acpi_trip)) - break; - - for (j = 0; j < acpi_trip->devices.count; j++) - acpi_bus_update_power(acpi_trip->devices.handles[j], NULL); - } - - acpi_queue_thermal_check(tz); - - return AE_OK; + acpi_queue_thermal_check(dev_get_drvdata(dev)); } -#else -#define acpi_thermal_suspend NULL -#define acpi_thermal_resume NULL -#endif -static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume); + +static const struct dev_pm_ops acpi_thermal_pm_ops = { + .prepare = acpi_thermal_prepare, + .complete = acpi_thermal_complete, +}; +#define ACPI_THERMAL_PM &acpi_thermal_pm_ops +#else /* !CONFIG_PM_SLEEP */ +#define ACPI_THERMAL_PM NULL +#endif /* CONFIG_PM_SLEEP */ static const struct acpi_device_id thermal_device_ids[] = { {ACPI_THERMAL_HID, 0}, @@ -959,15 +938,14 @@ static const struct acpi_device_id thermal_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, thermal_device_ids); -static struct acpi_driver acpi_thermal_driver = { - .name = "thermal", - .class = ACPI_THERMAL_CLASS, - .ids = thermal_device_ids, - .ops = { - .add = acpi_thermal_add, - .remove = acpi_thermal_remove, - }, - .drv.pm = &acpi_thermal_pm, +static struct platform_driver acpi_thermal_driver = { + .probe = acpi_thermal_probe, + .remove = acpi_thermal_remove, + .driver = { + .name = "acpi-thermal", + .acpi_match_table = thermal_device_ids, + .pm = ACPI_THERMAL_PM, + }, }; static int thermal_act(const struct dmi_system_id *d) @@ -1065,7 +1043,7 @@ static int __init acpi_thermal_init(void) if (!acpi_thermal_pm_queue) return -ENODEV; - result = acpi_bus_register_driver(&acpi_thermal_driver); + result = platform_driver_register(&acpi_thermal_driver); if (result < 0) { destroy_workqueue(acpi_thermal_pm_queue); return -ENODEV; @@ -1076,7 +1054,7 @@ static int __init acpi_thermal_init(void) static void __exit acpi_thermal_exit(void) { - acpi_bus_unregister_driver(&acpi_thermal_driver); + platform_driver_unregister(&acpi_thermal_driver); destroy_workqueue(acpi_thermal_pm_queue); } diff --git a/drivers/acpi/tiny-power-button.c b/drivers/acpi/tiny-power-button.c index 6353be6fec69..531e65b01bcb 100644 --- a/drivers/acpi/tiny-power-button.c +++ b/drivers/acpi/tiny-power-button.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include <linux/acpi.h> #include <linux/module.h> +#include <linux/platform_device.h> #include <linux/sched/signal.h> -#include <linux/acpi.h> #include <acpi/button.h> MODULE_AUTHOR("Josh Triplett"); @@ -35,8 +36,9 @@ static u32 acpi_tiny_power_button_event(void *not_used) return ACPI_INTERRUPT_HANDLED; } -static int acpi_tiny_power_button_add(struct acpi_device *device) +static int acpi_tiny_power_button_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_status status; if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { @@ -55,8 +57,10 @@ static int acpi_tiny_power_button_add(struct acpi_device *device) return 0; } -static void acpi_tiny_power_button_remove(struct acpi_device *device) +static void acpi_tiny_power_button_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_tiny_power_button_event); @@ -67,14 +71,13 @@ static void acpi_tiny_power_button_remove(struct acpi_device *device) acpi_os_wait_events_complete(); } -static struct acpi_driver acpi_tiny_power_button_driver = { - .name = "tiny-power-button", - .class = "tiny-power-button", - .ids = tiny_power_button_device_ids, - .ops = { - .add = acpi_tiny_power_button_add, - .remove = acpi_tiny_power_button_remove, +static struct platform_driver acpi_tiny_power_button_driver = { + .probe = acpi_tiny_power_button_probe, + .remove = acpi_tiny_power_button_remove, + .driver = { + .name = "acpi-tiny-power-button", + .acpi_match_table = tiny_power_button_device_ids, }, }; -module_acpi_driver(acpi_tiny_power_button_driver); +module_platform_driver(acpi_tiny_power_button_driver); diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index cc3c83e4cc23..b43d10f986a2 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -49,6 +49,7 @@ static const struct acpi_device_id lps0_device_ids[] = { #define ACPI_LPS0_EXIT 6 #define ACPI_LPS0_MS_ENTRY 7 #define ACPI_LPS0_MS_EXIT 8 +#define ACPI_MS_TURN_ON_DISPLAY 9 /* AMD */ #define ACPI_LPS0_DSM_UUID_AMD "e3f32452-febc-43ce-9039-932122d37721" @@ -356,6 +357,8 @@ static const char *acpi_sleep_dsm_state_to_str(unsigned int state) return "lps0 ms entry"; case ACPI_LPS0_MS_EXIT: return "lps0 ms exit"; + case ACPI_MS_TURN_ON_DISPLAY: + return "lps0 ms turn on display"; } } else { switch (state) { @@ -463,9 +466,6 @@ static int lps0_device_attach(struct acpi_device *adev, lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1; acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n", ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask); - } else if (lps0_dsm_func_mask_microsoft > 0 && rev_id) { - lps0_dsm_func_mask_microsoft = -EINVAL; - acpi_handle_debug(adev->handle, "_DSM Using AMD method\n"); } } else { rev_id = 1; @@ -617,6 +617,9 @@ static void acpi_s2idle_restore_early_lps0(void) if (lps0_dsm_func_mask_microsoft > 0) { acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT, lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); + /* Intent to turn on display */ + acpi_sleep_run_lps0_dsm(ACPI_MS_TURN_ON_DISPLAY, + lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); /* Modern Standby exit */ acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT, lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft); diff --git a/drivers/cxl/core/ras.c b/drivers/cxl/core/ras.c index 2731ba3a0799..a90480d07c87 100644 --- a/drivers/cxl/core/ras.c +++ b/drivers/cxl/core/ras.c @@ -63,7 +63,7 @@ static int match_memdev_by_parent(struct device *dev, const void *uport) return 0; } -static void cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data *data) +void cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data *data) { unsigned int devfn = PCI_DEVFN(data->prot_err.agent_addr.device, data->prot_err.agent_addr.function); @@ -104,6 +104,7 @@ static void cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data *data) else cxl_cper_trace_uncorr_prot_err(cxlmd, data->ras_cap); } +EXPORT_SYMBOL_GPL(cxl_cper_handle_prot_err); static void cxl_cper_prot_err_work_fn(struct work_struct *work) { diff --git a/drivers/firmware/efi/cper-arm.c b/drivers/firmware/efi/cper-arm.c index 76542a53e202..b21cb1232d82 100644 --- a/drivers/firmware/efi/cper-arm.c +++ b/drivers/firmware/efi/cper-arm.c @@ -226,7 +226,8 @@ static void cper_print_arm_err_info(const char *pfx, u32 type, } void cper_print_proc_arm(const char *pfx, - const struct cper_sec_proc_arm *proc) + const struct cper_sec_proc_arm *proc, + u32 length) { int i, len, max_ctx_type; struct cper_arm_err_info *err_info; @@ -238,9 +239,12 @@ void cper_print_proc_arm(const char *pfx, len = proc->section_length - (sizeof(*proc) + proc->err_info_num * (sizeof(*err_info))); - if (len < 0) { - printk("%ssection length: %d\n", pfx, proc->section_length); - printk("%ssection length is too small\n", pfx); + + if (len < 0 || proc->section_length > length) { + printk("%ssection length: %d, CPER size: %d\n", + pfx, proc->section_length, length); + printk("%ssection length is too %s\n", pfx, + (len < 0) ? "small" : "big"); printk("%sfirmware-generated error record is incorrect\n", pfx); printk("%sERR_INFO_NUM is %d\n", pfx, proc->err_info_num); return; diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index bd99802cb0ca..06b4fdb59917 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -560,6 +560,11 @@ static void cper_print_fw_err(const char *pfx, } else { offset = sizeof(*fw_err); } + if (offset > length) { + printk("%s""error section length is too small: offset=%d, length=%d\n", + pfx, offset, length); + return; + } buf += offset; length -= offset; @@ -659,7 +664,8 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata printk("%ssection_type: ARM processor error\n", newpfx); if (gdata->error_data_length >= sizeof(*arm_err)) - cper_print_proc_arm(newpfx, arm_err); + cper_print_proc_arm(newpfx, arm_err, + gdata->error_data_length); else goto err_section_too_small; #endif diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c index 12f45228c867..a832cdb2e697 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -19,18 +19,24 @@ MSI_FLAG_PCI_MSIX | \ MSI_FLAG_MULTI_PCI_MSI) -static int its_translate_frame_address(struct device_node *msi_node, phys_addr_t *pa) +static int its_translate_frame_address(struct fwnode_handle *msi_node, phys_addr_t *pa) { struct resource res; int ret; - ret = of_property_match_string(msi_node, "reg-names", "ns-translate"); - if (ret < 0) - return ret; + if (is_of_node(msi_node)) { + struct device_node *msi_np = to_of_node(msi_node); - ret = of_address_to_resource(msi_node, ret, &res); - if (ret) - return ret; + ret = of_property_match_string(msi_np, "reg-names", "ns-translate"); + if (ret < 0) + return ret; + + ret = of_address_to_resource(msi_np, ret, &res); + if (ret) + return ret; + } else { + ret = iort_its_translate_pa(msi_node, &res.start); + } *pa = res.start; return 0; @@ -104,7 +110,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev, static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *info) { - struct device_node *msi_node = NULL; + struct fwnode_handle *msi_node = NULL; struct msi_domain_info *msi_info; struct pci_dev *pdev; phys_addr_t pa; @@ -116,7 +122,7 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev, pdev = to_pci_dev(dev); - rid = pci_msi_map_rid_ctlr_node(pdev, &msi_node); + rid = pci_msi_map_rid_ctlr_node(domain->parent, pdev, &msi_node); if (!msi_node) return -ENODEV; @@ -124,7 +130,7 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev, if (ret) return -ENODEV; - of_node_put(msi_node); + fwnode_handle_put(msi_node); /* ITS specific DeviceID */ info->scratchpad[0].ul = rid; @@ -161,7 +167,7 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u ret = -EINVAL; if (!ret && pa) - ret = its_translate_frame_address(it.node, pa); + ret = its_translate_frame_address(of_fwnode_handle(it.node), pa); if (!ret) *dev_id = args; @@ -176,11 +182,6 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_ctrl, dev_id); } -int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id) -{ - return -1; -} - static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, int nvec, msi_alloc_info_t *info) { @@ -191,7 +192,7 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, if (dev->of_node) ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, NULL); else - ret = iort_pmsi_get_dev_id(dev, &dev_id); + ret = iort_pmsi_get_msi_info(dev, &dev_id, NULL); if (ret) return ret; @@ -214,10 +215,10 @@ static int its_v5_pmsi_prepare(struct irq_domain *domain, struct device *dev, u32 dev_id; int ret; - if (!dev->of_node) - return -ENODEV; - - ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa); + if (dev->of_node) + ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa); + else + ret = iort_pmsi_get_msi_info(dev, &dev_id, &pa); if (ret) return ret; diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-irs.c index ce2732d649a3..fcd032b5e2fa 100644 --- a/drivers/irqchip/irq-gic-v5-irs.c +++ b/drivers/irqchip/irq-gic-v5-irs.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) "GICv5 IRS: " fmt +#include <linux/acpi.h> #include <linux/kmemleak.h> #include <linux/log2.h> #include <linux/of.h> @@ -545,15 +546,13 @@ int gicv5_irs_register_cpu(int cpuid) static void __init gicv5_irs_init_bases(struct gicv5_irs_chip_data *irs_data, void __iomem *irs_base, - struct fwnode_handle *handle) + bool noncoherent) { - struct device_node *np = to_of_node(handle); u32 cr0, cr1; - irs_data->fwnode = handle; irs_data->irs_base = irs_base; - if (of_property_read_bool(np, "dma-noncoherent")) { + if (noncoherent) { /* * A non-coherent IRS implies that some cache levels cannot be * used coherently by the cores and GIC. Our only option is to mark @@ -678,12 +677,52 @@ static void irs_setup_pri_bits(u32 idr1) } } -static int __init gicv5_irs_init(struct device_node *node) +static int __init gicv5_irs_init(struct gicv5_irs_chip_data *irs_data) +{ + u32 spi_count, idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2); + + if (WARN(!FIELD_GET(GICV5_IRS_IDR2_LPI, idr), + "LPI support not available - no IPIs, can't proceed\n")) { + return -ENODEV; + } + + idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR7); + irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr); + + idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR6); + irs_data->spi_range = FIELD_GET(GICV5_IRS_IDR6_SPI_IRS_RANGE, idr); + + /* + * Do the global setting only on the first IRS. + * Global properties (iaffid_bits, global spi count) are guaranteed to + * be consistent across IRSes by the architecture. + */ + if (list_empty(&irs_nodes)) { + + idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1); + irs_setup_pri_bits(idr); + + idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR5); + + spi_count = FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr); + gicv5_global_data.global_spi_count = spi_count; + + gicv5_init_lpi_domain(); + + pr_debug("Detected %u SPIs globally\n", spi_count); + } + + list_add_tail(&irs_data->entry, &irs_nodes); + + return 0; +} + +static int __init gicv5_irs_of_init(struct device_node *node) { struct gicv5_irs_chip_data *irs_data; void __iomem *irs_base; - u32 idr, spi_count; u8 iaffid_bits; + u32 idr; int ret; irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL); @@ -705,7 +744,8 @@ static int __init gicv5_irs_init(struct device_node *node) goto out_err; } - gicv5_irs_init_bases(irs_data, irs_base, &node->fwnode); + irs_data->fwnode = of_fwnode_handle(node); + gicv5_irs_init_bases(irs_data, irs_base, of_property_read_bool(node, "dma-noncoherent")); idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1); iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1; @@ -716,18 +756,9 @@ static int __init gicv5_irs_init(struct device_node *node) goto out_iomem; } - idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2); - if (WARN(!FIELD_GET(GICV5_IRS_IDR2_LPI, idr), - "LPI support not available - no IPIs, can't proceed\n")) { - ret = -ENODEV; + ret = gicv5_irs_init(irs_data); + if (ret) goto out_iomem; - } - - idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR7); - irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr); - - idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR6); - irs_data->spi_range = FIELD_GET(GICV5_IRS_IDR6_SPI_IRS_RANGE, idr); if (irs_data->spi_range) { pr_info("%s detected SPI range [%u-%u]\n", @@ -737,29 +768,7 @@ static int __init gicv5_irs_init(struct device_node *node) irs_data->spi_range - 1); } - /* - * Do the global setting only on the first IRS. - * Global properties (iaffid_bits, global spi count) are guaranteed to - * be consistent across IRSes by the architecture. - */ - if (list_empty(&irs_nodes)) { - - idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1); - irs_setup_pri_bits(idr); - - idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR5); - - spi_count = FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr); - gicv5_global_data.global_spi_count = spi_count; - - gicv5_init_lpi_domain(); - - pr_debug("Detected %u SPIs globally\n", spi_count); - } - - list_add_tail(&irs_data->entry, &irs_nodes); - - return 0; + return ret; out_iomem: iounmap(irs_base); @@ -805,8 +814,11 @@ void __init gicv5_irs_its_probe(void) { struct gicv5_irs_chip_data *irs_data; - list_for_each_entry(irs_data, &irs_nodes, entry) - gicv5_its_of_probe(to_of_node(irs_data->fwnode)); + if (acpi_disabled) + list_for_each_entry(irs_data, &irs_nodes, entry) + gicv5_its_of_probe(to_of_node(irs_data->fwnode)); + else + gicv5_its_acpi_probe(); } int __init gicv5_irs_of_probe(struct device_node *parent) @@ -818,10 +830,137 @@ int __init gicv5_irs_of_probe(struct device_node *parent) if (!of_device_is_compatible(np, "arm,gic-v5-irs")) continue; - ret = gicv5_irs_init(np); + ret = gicv5_irs_of_init(np); if (ret) pr_err("Failed to init IRS %s\n", np->full_name); } return list_empty(&irs_nodes) ? -ENODEV : 0; } + +#ifdef CONFIG_ACPI + +#define ACPI_GICV5_IRS_MEM_SIZE (SZ_64K) +static struct gicv5_irs_chip_data *current_irs_data __initdata; +static int current_irsid __initdata = -1; +static u8 current_iaffid_bits __initdata; + +static int __init gic_acpi_parse_iaffid(union acpi_subtable_headers *header, + const unsigned long end) +{ + struct acpi_madt_generic_interrupt *gicc = (struct acpi_madt_generic_interrupt *)header; + int cpu; + + if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE))) + return 0; + + if (gicc->irs_id != current_irsid) + return 0; + + cpu = get_logical_index(gicc->arm_mpidr); + + if (gicc->iaffid & ~GENMASK(current_iaffid_bits - 1, 0)) { + pr_warn("CPU %d iaffid 0x%x exceeds IRS iaffid bits\n", cpu, gicc->iaffid); + return 0; + } + + /* Bind the IAFFID and the CPU */ + per_cpu(cpu_iaffid, cpu).iaffid = gicc->iaffid; + per_cpu(cpu_iaffid, cpu).valid = true; + pr_debug("Processed IAFFID %u for CPU%d", per_cpu(cpu_iaffid, cpu).iaffid, cpu); + + /* We also know that the CPU is connected to this IRS */ + per_cpu(per_cpu_irs_data, cpu) = current_irs_data; + + return 0; +} + +static int __init gicv5_irs_acpi_init_affinity(u32 irsid, struct gicv5_irs_chip_data *irs_data) +{ + u32 idr; + + current_irsid = irsid; + current_irs_data = irs_data; + + idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1); + current_iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1; + + acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, gic_acpi_parse_iaffid, 0); + + return 0; +} + +static struct resource * __init gic_request_region(resource_size_t base, resource_size_t size, + const char *name) +{ + struct resource *r = request_mem_region(base, size, name); + + if (!r) + pr_warn_once(FW_BUG "%s region %pa has overlapping address\n", name, &base); + + return r; +} + +static int __init gic_acpi_parse_madt_irs(union acpi_subtable_headers *header, + const unsigned long end) +{ + struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header; + struct gicv5_irs_chip_data *irs_data; + void __iomem *irs_base; + struct resource *r; + int ret; + + /* Per-IRS data structure */ + irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL); + if (!irs_data) + return -ENOMEM; + + /* This spinlock is used for SPI config changes */ + raw_spin_lock_init(&irs_data->spi_config_lock); + + r = gic_request_region(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE, "GICv5 IRS"); + if (!r) { + ret = -EBUSY; + goto out_free; + } + + irs_base = ioremap(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE); + if (!irs_base) { + pr_err("Unable to map GIC IRS registers\n"); + ret = -ENOMEM; + goto out_release; + } + + gicv5_irs_init_bases(irs_data, irs_base, irs->flags & ACPI_MADT_IRS_NON_COHERENT); + + gicv5_irs_acpi_init_affinity(irs->irs_id, irs_data); + + ret = gicv5_irs_init(irs_data); + if (ret) + goto out_map; + + if (irs_data->spi_range) { + pr_info("%s @%llx detected SPI range [%u-%u]\n", "IRS", irs->config_base_address, + irs_data->spi_min, + irs_data->spi_min + + irs_data->spi_range - 1); + } + + return 0; + +out_map: + iounmap(irs_base); +out_release: + release_mem_region(r->start, resource_size(r)); +out_free: + kfree(irs_data); + return ret; +} + +int __init gicv5_irs_acpi_probe(void) +{ + acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_IRS, gic_acpi_parse_madt_irs, 0); + + return list_empty(&irs_nodes) ? -ENODEV : 0; +} +#endif diff --git a/drivers/irqchip/irq-gic-v5-its.c b/drivers/irqchip/irq-gic-v5-its.c index 8e22134b9f48..e24ce3d9fb62 100644 --- a/drivers/irqchip/irq-gic-v5-its.c +++ b/drivers/irqchip/irq-gic-v5-its.c @@ -5,6 +5,8 @@ #define pr_fmt(fmt) "GICv5 ITS: " fmt +#include <linux/acpi.h> +#include <linux/acpi_iort.h> #include <linux/bitmap.h> #include <linux/iommu.h> #include <linux/init.h> @@ -1115,7 +1117,7 @@ static int gicv5_its_init_domain(struct gicv5_its_chip_data *its, struct irq_dom } static int __init gicv5_its_init_bases(void __iomem *its_base, struct fwnode_handle *handle, - struct irq_domain *parent_domain) + struct irq_domain *parent_domain, bool noncoherent) { struct device_node *np = to_of_node(handle); struct gicv5_its_chip_data *its_node; @@ -1208,7 +1210,8 @@ static int __init gicv5_its_init(struct device_node *node) } ret = gicv5_its_init_bases(its_base, of_fwnode_handle(node), - gicv5_global_data.lpi_domain); + gicv5_global_data.lpi_domain, + of_property_read_bool(node, "dma-noncoherent")); if (ret) goto out_unmap; @@ -1231,3 +1234,128 @@ void __init gicv5_its_of_probe(struct device_node *parent) pr_err("Failed to init ITS %s\n", np->full_name); } } + +#ifdef CONFIG_ACPI + +#define ACPI_GICV5_ITS_MEM_SIZE (SZ_64K) + +static struct acpi_madt_gicv5_translator *current_its_entry __initdata; +static struct fwnode_handle *current_its_fwnode __initdata; + +static int __init gic_acpi_parse_madt_its_translate(union acpi_subtable_headers *header, + const unsigned long end) +{ + struct acpi_madt_gicv5_translate_frame *its_frame; + struct fwnode_handle *msi_dom_handle; + struct resource res = {}; + int err; + + its_frame = (struct acpi_madt_gicv5_translate_frame *)header; + if (its_frame->linked_translator_id != current_its_entry->translator_id) + return 0; + + res.start = its_frame->base_address; + res.end = its_frame->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1; + res.flags = IORESOURCE_MEM; + + msi_dom_handle = irq_domain_alloc_parented_fwnode(&res.start, current_its_fwnode); + if (!msi_dom_handle) { + pr_err("ITS@%pa: Unable to allocate GICv5 ITS translate domain token\n", + &res.start); + return -ENOMEM; + } + + err = iort_register_domain_token(its_frame->translate_frame_id, res.start, + msi_dom_handle); + if (err) { + pr_err("ITS@%pa: Unable to register GICv5 ITS domain token (ITS TRANSLATE FRAME ID %d) to IORT\n", + &res.start, its_frame->translate_frame_id); + irq_domain_free_fwnode(msi_dom_handle); + return err; + } + + return 0; +} + +static int __init gic_acpi_free_madt_its_translate(union acpi_subtable_headers *header, + const unsigned long end) +{ + struct acpi_madt_gicv5_translate_frame *its_frame; + struct fwnode_handle *msi_dom_handle; + + its_frame = (struct acpi_madt_gicv5_translate_frame *)header; + if (its_frame->linked_translator_id != current_its_entry->translator_id) + return 0; + + msi_dom_handle = iort_find_domain_token(its_frame->translate_frame_id); + if (!msi_dom_handle) + return 0; + + iort_deregister_domain_token(its_frame->translate_frame_id); + irq_domain_free_fwnode(msi_dom_handle); + + return 0; +} + +static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header, + const unsigned long end) +{ + struct acpi_madt_gicv5_translator *its_entry; + struct fwnode_handle *dom_handle; + struct resource res = {}; + void __iomem *its_base; + int err; + + its_entry = (struct acpi_madt_gicv5_translator *)header; + res.start = its_entry->base_address; + res.end = its_entry->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1; + res.flags = IORESOURCE_MEM; + + if (!request_mem_region(res.start, resource_size(&res), "GICv5 ITS")) + return -EBUSY; + + dom_handle = irq_domain_alloc_fwnode(&res.start); + if (!dom_handle) { + pr_err("ITS@%pa: Unable to allocate GICv5 ITS domain token\n", + &res.start); + err = -ENOMEM; + goto out_rel_res; + } + + current_its_entry = its_entry; + current_its_fwnode = dom_handle; + + acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE, + gic_acpi_parse_madt_its_translate, 0); + + its_base = ioremap(res.start, ACPI_GICV5_ITS_MEM_SIZE); + if (!its_base) { + err = -ENOMEM; + goto out_unregister; + } + + err = gicv5_its_init_bases(its_base, dom_handle, gicv5_global_data.lpi_domain, + its_entry->flags & ACPI_MADT_GICV5_ITS_NON_COHERENT); + if (err) + goto out_unmap; + + return 0; + +out_unmap: + iounmap(its_base); +out_unregister: + acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE, + gic_acpi_free_madt_its_translate, 0); + irq_domain_free_fwnode(dom_handle); +out_rel_res: + release_mem_region(res.start, resource_size(&res)); + return err; +} + +void __init gicv5_its_acpi_probe(void) +{ + acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS, gic_acpi_parse_madt_its, 0); +} +#else +void __init gicv5_its_acpi_probe(void) { } +#endif diff --git a/drivers/irqchip/irq-gic-v5-iwb.c b/drivers/irqchip/irq-gic-v5-iwb.c index ad9fdc14d1c6..c7d5fd34d053 100644 --- a/drivers/irqchip/irq-gic-v5-iwb.c +++ b/drivers/irqchip/irq-gic-v5-iwb.c @@ -4,6 +4,7 @@ */ #define pr_fmt(fmt) "GICv5 IWB: " fmt +#include <linux/acpi.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/msi.h> @@ -136,18 +137,31 @@ static int gicv5_iwb_irq_domain_translate(struct irq_domain *d, struct irq_fwspe irq_hw_number_t *hwirq, unsigned int *type) { - if (!is_of_node(fwspec->fwnode)) - return -EINVAL; + if (is_of_node(fwspec->fwnode)) { - if (fwspec->param_count < 2) - return -EINVAL; + if (fwspec->param_count < 2) + return -EINVAL; - /* - * param[0] is be the wire - * param[1] is the interrupt type - */ - *hwirq = fwspec->param[0]; - *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + /* + * param[0] is be the wire + * param[1] is the interrupt type + */ + *hwirq = fwspec->param[0]; + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + } + + if (is_acpi_device_node(fwspec->fwnode)) { + + if (fwspec->param_count < 2) + return -EINVAL; + + /* + * Extract the wire from param[0] + * param[1] is the interrupt type + */ + *hwirq = FIELD_GET(GICV5_GSI_IWB_WIRE, fwspec->param[0]); + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + } return 0; } @@ -265,10 +279,18 @@ static const struct of_device_id gicv5_iwb_of_match[] = { }; MODULE_DEVICE_TABLE(of, gicv5_iwb_of_match); +#ifdef CONFIG_ACPI +static const struct acpi_device_id iwb_acpi_match[] = { + { "ARMH0003", 0 }, + {} +}; +#endif + static struct platform_driver gicv5_iwb_platform_driver = { .driver = { .name = "GICv5 IWB", .of_match_table = gicv5_iwb_of_match, + .acpi_match_table = ACPI_PTR(iwb_acpi_match), .suppress_bind_attrs = true, }, .probe = gicv5_iwb_device_probe, diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c index 41ef286c4d78..da867dd2e77d 100644 --- a/drivers/irqchip/irq-gic-v5.c +++ b/drivers/irqchip/irq-gic-v5.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) "GICv5: " fmt +#include <linux/acpi_iort.h> #include <linux/cpuhotplug.h> #include <linux/idr.h> #include <linux/irqdomain.h> @@ -579,16 +580,36 @@ static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d, unsigned int *type, const u8 hwirq_type) { - if (!is_of_node(fwspec->fwnode)) - return -EINVAL; + unsigned int hwirq_trigger; + u8 fwspec_irq_type; - if (fwspec->param_count < 3) - return -EINVAL; + if (is_of_node(fwspec->fwnode)) { - if (fwspec->param[0] != hwirq_type) - return -EINVAL; + if (fwspec->param_count < 3) + return -EINVAL; + + fwspec_irq_type = fwspec->param[0]; + + if (fwspec->param[0] != hwirq_type) + return -EINVAL; + + *hwirq = fwspec->param[1]; + hwirq_trigger = fwspec->param[2]; + } + + if (is_fwnode_irqchip(fwspec->fwnode)) { + + if (fwspec->param_count != 2) + return -EINVAL; - *hwirq = fwspec->param[1]; + fwspec_irq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]); + + if (fwspec_irq_type != hwirq_type) + return -EINVAL; + + *hwirq = FIELD_GET(GICV5_HWIRQ_ID, fwspec->param[0]); + hwirq_trigger = fwspec->param[1]; + } switch (hwirq_type) { case GICV5_HWIRQ_TYPE_PPI: @@ -600,7 +621,7 @@ static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d, IRQ_TYPE_EDGE_RISING; break; case GICV5_HWIRQ_TYPE_SPI: - *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; + *type = hwirq_trigger & IRQ_TYPE_SENSE_MASK; break; default: BUILD_BUG_ON(1); @@ -660,10 +681,18 @@ static void gicv5_irq_domain_free(struct irq_domain *domain, unsigned int virq, static int gicv5_irq_ppi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, enum irq_domain_bus_token bus_token) { + u32 hwirq_type; + if (fwspec->fwnode != d->fwnode) return 0; - if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI) + if (is_of_node(fwspec->fwnode)) + hwirq_type = fwspec->param[0]; + + if (is_fwnode_irqchip(fwspec->fwnode)) + hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]); + + if (hwirq_type != GICV5_HWIRQ_TYPE_PPI) return 0; return (d == gicv5_global_data.ppi_domain); @@ -718,10 +747,18 @@ static int gicv5_irq_spi_domain_alloc(struct irq_domain *domain, unsigned int vi static int gicv5_irq_spi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec, enum irq_domain_bus_token bus_token) { + u32 hwirq_type; + if (fwspec->fwnode != d->fwnode) return 0; - if (fwspec->param[0] != GICV5_HWIRQ_TYPE_SPI) + if (is_of_node(fwspec->fwnode)) + hwirq_type = fwspec->param[0]; + + if (is_fwnode_irqchip(fwspec->fwnode)) + hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]); + + if (hwirq_type != GICV5_HWIRQ_TYPE_SPI) return 0; return (d == gicv5_global_data.spi_domain); @@ -1082,16 +1119,12 @@ static inline void __init gic_of_setup_kvm_info(struct device_node *node) } #endif // CONFIG_KVM -static int __init gicv5_of_init(struct device_node *node, struct device_node *parent) +static int __init gicv5_init_common(struct fwnode_handle *parent_domain) { - int ret = gicv5_irs_of_probe(node); + int ret = gicv5_init_domains(parent_domain); if (ret) return ret; - ret = gicv5_init_domains(of_fwnode_handle(node)); - if (ret) - goto out_irs; - gicv5_set_cpuif_pribits(); gicv5_set_cpuif_idbits(); @@ -1113,18 +1146,85 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa gicv5_smp_init(); gicv5_irs_its_probe(); - - gic_of_setup_kvm_info(node); - return 0; out_int: gicv5_cpu_disable_interrupts(); out_dom: gicv5_free_domains(); + return ret; +} + +static int __init gicv5_of_init(struct device_node *node, struct device_node *parent) +{ + int ret = gicv5_irs_of_probe(node); + if (ret) + return ret; + + ret = gicv5_init_common(of_fwnode_handle(node)); + if (ret) + goto out_irs; + + gic_of_setup_kvm_info(node); + + return 0; out_irs: gicv5_irs_remove(); return ret; } IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init); + +#ifdef CONFIG_ACPI +static bool __init acpi_validate_gic_table(struct acpi_subtable_header *header, + struct acpi_probe_entry *ape) +{ + struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header; + + return (irs->version == ape->driver_data); +} + +static struct fwnode_handle *gsi_domain_handle; + +static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi) +{ + if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) == GICV5_GSI_IWB_TYPE) + return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi)); + + return gsi_domain_handle; +} + +static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end) +{ + struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header; + int ret; + + if (gsi_domain_handle) + return 0; + + gsi_domain_handle = irq_domain_alloc_fwnode(&irs->config_base_address); + if (!gsi_domain_handle) + return -ENOMEM; + + ret = gicv5_irs_acpi_probe(); + if (ret) + goto out_fwnode; + + ret = gicv5_init_common(gsi_domain_handle); + if (ret) + goto out_irs; + + acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id); + + return 0; + +out_irs: + gicv5_irs_remove(); +out_fwnode: + irq_domain_free_fwnode(gsi_domain_handle); + return ret; +} +IRQCHIP_ACPI_DECLARE(gic_v5, ACPI_MADT_TYPE_GICV5_IRS, + acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V5, + gic_acpi_init); +#endif diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c index a329060287b5..6e65f0f44112 100644 --- a/drivers/pci/msi/irqdomain.c +++ b/drivers/pci/msi/irqdomain.c @@ -376,23 +376,36 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev) } /** - * pci_msi_map_rid_ctlr_node - Get the MSI controller node and MSI requester id (RID) + * pci_msi_map_rid_ctlr_node - Get the MSI controller fwnode_handle and MSI requester id (RID) + * @domain: The interrupt domain * @pdev: The PCI device - * @node: Pointer to store the MSI controller device node + * @node: Pointer to store the MSI controller fwnode_handle * - * Use the firmware data to find the MSI controller node for @pdev. + * Use the firmware data to find the MSI controller fwnode_handle for @pdev. * If found map the RID and initialize @node with it. @node value must * be set to NULL on entry. * * Returns: The RID. */ -u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node) +u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev, + struct fwnode_handle **node) { u32 rid = pci_dev_id(pdev); pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid); - return of_msi_xlate(&pdev->dev, node, rid); + /* Check whether the domain fwnode is an OF node */ + if (irq_domain_get_of_node(domain)) { + struct device_node *msi_ctlr_node = NULL; + + rid = of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid); + if (msi_ctlr_node) + *node = of_fwnode_handle(msi_ctlr_node); + } else { + rid = iort_msi_xlate(&pdev->dev, rid, node); + } + + return rid; } /** diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 9369377725fa..79980203a1fb 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -847,12 +847,7 @@ bool shpchp_is_native(struct pci_dev *bridge) */ static void pci_acpi_wake_bus(struct acpi_device_wakeup_context *context) { - struct acpi_device *adev; - struct acpi_pci_root *root; - - adev = container_of(context, struct acpi_device, wakeup.context); - root = acpi_driver_data(adev); - pci_pme_wakeup_bus(root->bus); + pci_pme_wakeup_bus(to_pci_host_bridge(context->dev)->bus); } /** @@ -885,12 +880,14 @@ static void pci_acpi_wake_dev(struct acpi_device_wakeup_context *context) } /** - * pci_acpi_add_bus_pm_notifier - Register PM notifier for root PCI bus. + * pci_acpi_add_root_pm_notifier - Register PM notifier for root PCI bus. * @dev: PCI root bridge ACPI device. + * @root: PCI root corresponding to @dev. */ -acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev) +acpi_status pci_acpi_add_root_pm_notifier(struct acpi_device *dev, + struct acpi_pci_root *root) { - return acpi_add_pm_notifier(dev, NULL, pci_acpi_wake_bus); + return acpi_add_pm_notifier(dev, root->bus->bridge, pci_acpi_wake_bus); } /** diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index e0bcaa896803..71ee4f5064de 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -973,7 +973,7 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, pcie_print_tlp_log(dev, &aer->header_log, info.level, dev_fmt(" ")); } -EXPORT_SYMBOL_NS_GPL(pci_print_aer, "CXL"); +EXPORT_SYMBOL_GPL(pci_print_aer); /** * add_error_device - list device to be handled diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index 560cc063198e..0f8684f4464b 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -779,43 +779,4 @@ static struct platform_driver intel_hid_pl_driver = { .remove = intel_hid_remove, }; -/* - * Unfortunately, some laptops provide a _HID="INT33D5" device with - * _CID="PNP0C02". This causes the pnpacpi scan driver to claim the - * ACPI node, so no platform device will be created. The pnpacpi - * driver rejects this device in subsequent processing, so no physical - * node is created at all. - * - * As a workaround until the ACPI core figures out how to handle - * this corner case, manually ask the ACPI platform device code to - * claim the ACPI node. - */ -static acpi_status __init -check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - const struct acpi_device_id *ids = context; - struct acpi_device *dev = acpi_fetch_acpi_dev(handle); - - if (dev && acpi_match_device_ids(dev, ids) == 0) - if (!IS_ERR_OR_NULL(acpi_create_platform_device(dev, NULL))) - dev_info(&dev->dev, - "intel-hid: created platform device\n"); - - return AE_OK; -} - -static int __init intel_hid_init(void) -{ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, check_acpi_dev, NULL, - (void *)intel_hid_ids, NULL); - - return platform_driver_register(&intel_hid_pl_driver); -} -module_init(intel_hid_init); - -static void __exit intel_hid_exit(void) -{ - platform_driver_unregister(&intel_hid_pl_driver); -} -module_exit(intel_hid_exit); +module_platform_driver(intel_hid_pl_driver); diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c index 232cd12e3c9f..9ca87e707582 100644 --- a/drivers/platform/x86/intel/vbtn.c +++ b/drivers/platform/x86/intel/vbtn.c @@ -390,32 +390,4 @@ static struct platform_driver intel_vbtn_pl_driver = { .remove = intel_vbtn_remove, }; -static acpi_status __init -check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - const struct acpi_device_id *ids = context; - struct acpi_device *dev = acpi_fetch_acpi_dev(handle); - - if (dev && acpi_match_device_ids(dev, ids) == 0) - if (!IS_ERR_OR_NULL(acpi_create_platform_device(dev, NULL))) - dev_info(&dev->dev, - "intel-vbtn: created platform device\n"); - - return AE_OK; -} - -static int __init intel_vbtn_init(void) -{ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, check_acpi_dev, NULL, - (void *)intel_vbtn_ids, NULL); - - return platform_driver_register(&intel_vbtn_pl_driver); -} -module_init(intel_vbtn_init); - -static void __exit intel_vbtn_exit(void) -{ - platform_driver_unregister(&intel_vbtn_pl_driver); -} -module_exit(intel_vbtn_exit); +module_platform_driver(intel_vbtn_pl_driver); diff --git a/drivers/ras/ras.c b/drivers/ras/ras.c index 2a5b5a9fdcb3..03df3db62334 100644 --- a/drivers/ras/ras.c +++ b/drivers/ras/ras.c @@ -72,7 +72,11 @@ void log_arm_hw_error(struct cper_sec_proc_arm *err, const u8 sev) ctx_err = (u8 *)ctx_info; for (n = 0; n < err->context_info_num; n++) { - sz = sizeof(struct cper_arm_ctx_info) + ctx_info->size; + sz = sizeof(struct cper_arm_ctx_info); + + if (sz + (long)ctx_info - (long)err >= err->section_length) + sz += ctx_info->size; + ctx_info = (struct cper_arm_ctx_info *)((long)ctx_info + sz); ctx_len += sz; } diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index e65a2afe9250..49d1749f30bb 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -12,7 +12,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20250807 +#define ACPI_CA_VERSION 0x20251212 #include <acpi/acconfig.h> #include <acpi/actypes.h> diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 7f35eb0e8458..4e15583e0d25 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -37,6 +37,7 @@ #define ACPI_SIG_DBGP "DBGP" /* Debug Port table */ #define ACPI_SIG_DMAR "DMAR" /* DMA Remapping table */ #define ACPI_SIG_DRTM "DRTM" /* Dynamic Root of Trust for Measurement table */ +#define ACPI_SIG_DTPR "DTPR" /* DMA TXT Protection Ranges table */ #define ACPI_SIG_ECDT "ECDT" /* Embedded Controller Boot Resources Table */ #define ACPI_SIG_EINJ "EINJ" /* Error Injection table */ #define ACPI_SIG_ERST "ERST" /* Error Record Serialization Table */ @@ -1001,6 +1002,262 @@ struct acpi_drtm_dps_id { /******************************************************************************* * + * DTPR - DMA TXT Protection Ranges Table + * Version 1 + * + * Conforms to "Intel® Trusted Execution Technology (Intel® TXT) DMA Protection + * Ranges", + * Revision 0.73, August 2021 + * + ******************************************************************************/ + +struct acpi_table_dtpr { + struct acpi_table_header header; + u32 flags; /* 36 */ + u32 ins_cnt; +}; + +struct acpi_tpr_array { + u64 base; +}; + +struct acpi_tpr_instance { + u32 flags; + u32 tpr_cnt; +}; + +struct acpi_tpr_aux_sr { + u32 srl_cnt; +}; + +/* + * TPRn_BASE (ACPI_TPRN_BASE_REG) + * + * Specifies the start address of TPRn region. TPR region address and size must + * be with 1MB resolution. These bits are compared with the result of the + * TPRn_LIMIT[63:20], which is applied to the incoming address, to + * determine if an access fall within the TPRn defined region. + * + * Minimal TPRn_Base resolution is 1MB. Applied to the incoming address, to + * determine if an access fall within the TPRn defined region. Width is + * determined by a bus width which can be obtained via CPUID + * function 0x80000008. + */ + +typedef u64 ACPI_TPRN_BASE_REG; + +/* TPRn_BASE Register Bit Masks */ + +/* Bit 3 - RW: access: 1 == RO, 0 == RW register (for TPR must be RW) */ +#define ACPI_TPRN_BASE_RW_SHIFT 3 + +#define ACPI_TPRN_BASE_RW_MASK ((u64) 1 << ACPI_TPRN_BASE_RW_SHIFT) + +/* + * Bit 4 - Enable: 0 – TPRn address range enabled; + * 1 – TPRn address range disabled. + */ +#define ACPI_TPRN_BASE_ENABLE_SHIFT 4 + +#define ACPI_TPRN_BASE_ENABLE_MASK ((u64) 1 << ACPI_TPRN_BASE_ENABLE_SHIFT) + +/* Bits 63:20 - tpr_base_rw */ +#define ACPI_TPRN_BASE_ADDR_SHIFT 20 + +#define ACPI_TPRN_BASE_ADDR_MASK ((u64) 0xFFFFFFFFFFF << \ + ACPI_TPRN_BASE_ADDR_SHIFT) + +/* TPRn_BASE Register Bit Handlers*/ + +/* + * GET_TPRN_BASE_RW: + * + * Read RW bit from TPRn Base register - bit 3. + * + * Input: + * - reg (represents TPRn Base Register (ACPI_TPRN_BASE_REG)) + * + * Output: + * + * Returns RW bit value (u64). + */ +#define GET_TPRN_BASE_RW(reg) (((u64) reg & ACPI_TPRN_BASE_RW_MASK) >> \ + ACPI_TPRN_BASE_RW_SHIFT) + +/* + * GET_TPRN_BASE_ENABLE: + * + * Read Enable bit from TPRn Base register - bit 4. + * + * Input: + * - reg (represents TPRn Base Register (ACPI_TPRN_BASE_REG)) + * + * Output: + * + * Returns Enable bit value (u64). + */ +#define GET_TPRN_BASE_ENABLE(reg) (((u64) reg & ACPI_TPRN_BASE_ENABLE_MASK) \ + >> ACPI_TPRN_BASE_ENABLE_SHIFT) + +/* + * GET_TPRN_BASE_ADDR: + * + * Read TPRn Base Register address from bits 63:20. + * + * Input: + * - reg (represents TPRn Base Register (ACPI_TPRN_BASE_REG)) + * + * Output: + * + * Returns TPRn Base Register address (u64). + */ +#define GET_TPRN_BASE_ADDR(reg) (((u64) reg & ACPI_TPRN_BASE_ADDR_MASK) \ + >> ACPI_TPRN_BASE_ADDR_SHIFT) + +/* + * SET_TPRN_BASE_RW: + * + * Set RW bit in TPRn Base register - bit 3. + * + * Input: + * - reg (represents TPRn Base Register (ACPI_TPRN_BASE_REG)) + * - val (represents RW value to be set (u64)) + */ +#define SET_TPRN_BASE_RW(reg, val) ACPI_REGISTER_INSERT_VALUE(reg, \ + ACPI_TPRN_BASE_RW_SHIFT, \ + ACPI_TPRN_BASE_RW_MASK, val); + +/* + * SET_TPRN_BASE_ENABLE: + * + * Set Enable bit in TPRn Base register - bit 4. + * + * Input: + * - reg (represents TPRn Base Register (ACPI_TPRN_BASE_REG)) + * - val (represents Enable value to be set (u64)) + */ +#define SET_TPRN_BASE_ENABLE(reg, val) ACPI_REGISTER_INSERT_VALUE(reg, \ + ACPI_TPRN_BASE_ENABLE_SHIFT, \ + ACPI_TPRN_BASE_ENABLE_MASK, val); + +/* + * SET_TPRN_BASE_ADDR: + * + * Set TPRn Base Register address - bits 63:20 + * + * Input + * - reg (represents TPRn Base Register (ACPI_TPRN_BASE_REG)) + * - val (represents address value to be set (u64)) + */ +#define SET_TPRN_BASE_ADDR(reg, val) ACPI_REGISTER_INSERT_VALUE(reg, \ + ACPI_TPRN_BASE_ADDR_SHIFT, \ + ACPI_TPRN_BASE_ADDR_MASK, val); + +/* + * TPRn_LIMIT + * + * This register defines an isolated region of memory that can be enabled + * to prohibit certain system agents from accessing memory. When an agent + * sends a request upstream, whether snooped or not, a TPR prevents that + * transaction from changing the state of memory. + * + * Minimal TPRn_Limit resolution is 1MB. Width is determined by a bus width. + */ + +typedef u64 ACPI_TPRN_LIMIT_REG; + +/* TPRn_LIMIT Register Bit Masks */ + +/* Bit 3 - RW: access: 1 == RO, 0 == RW register (for TPR must be RW) */ +#define ACPI_TPRN_LIMIT_RW_SHIFT 3 + +#define ACPI_TPRN_LIMIT_RW_MASK ((u64) 1 << ACPI_TPRN_LIMIT_RW_SHIFT) + +/* Bits 63:20 - tpr_limit_rw */ +#define ACPI_TPRN_LIMIT_ADDR_SHIFT 20 + +#define ACPI_TPRN_LIMIT_ADDR_MASK ((u64) 0xFFFFFFFFFFF << \ + ACPI_TPRN_LIMIT_ADDR_SHIFT) + +/* TPRn_LIMIT Register Bit Handlers*/ + +/* + * GET_TPRN_LIMIT_RW: + * + * Read RW bit from TPRn Limit register - bit 3. + * + * Input: + * - reg (represents TPRn Limit Register (ACPI_TPRN_LIMIT_REG)) + * + * Output: + * + * Returns RW bit value (u64). + */ +#define GET_TPRN_LIMIT_RW(reg) (((u64) reg & ACPI_TPRN_LIMIT_RW_MASK) \ + >> ACPI_TPRN_LIMIT_RW_SHIFT) + +/* + * GET_TPRN_LIMIT_ADDR: + * + * Read TPRn Limit Register address from bits 63:20. + * + * Input: + * - reg (represents TPRn Limit Register (ACPI_TPRN_LIMIT_REG)) + * + * Output: + * + * Returns TPRn Limit Register address (u64). + */ +#define GET_TPRN_LIMIT_ADDR(reg) (((u64) reg & ACPI_TPRN_LIMIT_ADDR_MASK) \ + >> ACPI_TPRN_LIMIT_ADDR_SHIFT) + +/* + * SET_TPRN_LIMIT_RW: + * + * Set RW bit in TPRn Limit register - bit 3. + * + * Input: + * - reg (represents TPRn Limit Register (ACPI_TPRN_LIMIT_REG)) + * - val (represents RW value to be set (u64)) + */ +#define SET_TPRN_LIMIT_RW(reg, val) ACPI_REGISTER_INSERT_VALUE(reg, \ + ACPI_TPRN_LIMIT_RW_SHIFT, \ + ACPI_TPRN_LIMIT_RW_MASK, val); + +/* + * SET_TPRN_LIMIT_ADDR: + * + * Set TPRn Limit Register address - bits 63:20. + * + * Input: + * - reg (represents TPRn Limit Register (ACPI_TPRN_LIMIT_REG)) + * - val (represents address value to be set (u64)) + */ +#define SET_TPRN_LIMIT_ADDR(reg, val) ACPI_REGISTER_INSERT_VALUE(reg, \ + ACPI_TPRN_LIMIT_ADDR_SHIFT, \ + ACPI_TPRN_LIMIT_ADDR_MASK, val); + +/* + * SERIALIZE_REQUEST + * + * This register is used to request serialization of non-coherent DMA + * transactions. OS shall issue it before changing of TPR settings + * (base / size). + */ + +struct acpi_tpr_serialize_request { + u64 sr_register; + /* + * BIT 1 - Status of serialization request (RO) + * 0 == register idle, 1 == serialization in progress + * BIT 2 - Control field to initiate serialization (RW) + * 0 == normal, 1 == initialize serialization + * (self-clear to allow multiple serialization requests) + */ +}; + +/******************************************************************************* + * * ECDT - Embedded Controller Boot Resources Table * Version 1 * diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index f726bce3eb84..5c0b55e7b3e4 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -31,7 +31,9 @@ #define ACPI_SIG_CDAT "CDAT" /* Coherent Device Attribute Table */ #define ACPI_SIG_ERDT "ERDT" /* Enhanced Resource Director Technology */ #define ACPI_SIG_IORT "IORT" /* IO Remapping Table */ +#define ACPI_SIG_IOVT "IOVT" /* I/O Virtualization Table */ #define ACPI_SIG_IVRS "IVRS" /* I/O Virtualization Reporting Structure */ +#define ACPI_SIG_KEYP "KEYP" /* Key Programming Interface for IDE */ #define ACPI_SIG_LPIT "LPIT" /* Low Power Idle Table */ #define ACPI_SIG_MADT "APIC" /* Multiple APIC Description Table */ #define ACPI_SIG_MCFG "MCFG" /* PCI Memory Mapped Configuration table */ @@ -680,6 +682,7 @@ enum acpi_iort_node_type { ACPI_IORT_NODE_SMMU_V3 = 0x04, ACPI_IORT_NODE_PMCG = 0x05, ACPI_IORT_NODE_RMR = 0x06, + ACPI_IORT_NODE_IWB = 0x07, }; struct acpi_iort_id_mapping { @@ -858,6 +861,79 @@ struct acpi_iort_rmr_desc { u32 reserved; }; +struct acpi_iort_iwb { + u64 base_address; + u16 iwb_index; /* Unique IWB identifier matching with the IWB GSI namespace. */ + char device_name[]; /* Path of the IWB namespace object */ +}; + +/******************************************************************************* + * + * IOVT - I/O Virtualization Table + * + * Conforms to "LoongArch I/O Virtualization Table", + * Version 0.1, October 2024 + * + ******************************************************************************/ + +struct acpi_table_iovt { + struct acpi_table_header header; /* Common ACPI table header */ + u16 iommu_count; + u16 iommu_offset; + u8 reserved[8]; +}; + +/* IOVT subtable header */ + +struct acpi_iovt_header { + u16 type; + u16 length; +}; + +/* Values for Type field above */ + +enum acpi_iovt_iommu_type { + ACPI_IOVT_IOMMU_V1 = 0x00, + ACPI_IOVT_IOMMU_RESERVED = 0x01 /* 1 and greater are reserved */ +}; + +/* IOVT subtables */ + +struct acpi_iovt_iommu { + struct acpi_iovt_header header; + u32 flags; + u16 segment; + u16 phy_width; /* Physical Address Width */ + u16 virt_width; /* Virtual Address Width */ + u16 max_page_level; + u64 page_size; + u32 device_id; + u64 base_address; + u32 address_space_size; + u8 interrupt_type; + u8 reserved[3]; + u32 gsi_number; + u32 proximity_domain; + u32 max_device_num; + u32 device_entry_num; + u32 device_entry_offset; +}; + +struct acpi_iovt_device_entry { + u8 type; + u8 length; + u8 flags; + u8 reserved[3]; + u16 device_id; +}; + +enum acpi_iovt_device_entry_type { + ACPI_IOVT_DEVICE_ENTRY_SINGLE = 0x00, + ACPI_IOVT_DEVICE_ENTRY_START = 0x01, + ACPI_IOVT_DEVICE_ENTRY_END = 0x02, + ACPI_IOVT_DEVICE_ENTRY_RESERVED = 0x03 /* 3 and greater are reserved */ +}; + /******************************************************************************* * * IVRS - I/O Virtualization Reporting Structure @@ -1067,6 +1143,64 @@ struct acpi_ivrs_memory { /******************************************************************************* * + * KEYP - Key Programming Interface for Root Complex Integrity and Data + * Encryption (IDE) + * Version 1 + * + * Conforms to "Key Programming Interface for Root Complex Integrity and Data + * Encryption (IDE)" document. See under ACPI-Related Documents. + * + ******************************************************************************/ +struct acpi_table_keyp { + struct acpi_table_header header; /* Common ACPI table header */ + u32 reserved; +}; + +/* KEYP common subtable header */ + +struct acpi_keyp_common_header { + u8 type; + u8 reserved; + u16 length; +}; + +/* Values for Type field above */ + +enum acpi_keyp_type { + ACPI_KEYP_TYPE_CONFIG_UNIT = 0, +}; + +/* Root Port Information Structure */ + +struct acpi_keyp_rp_info { + u16 segment; + u8 bus; + u8 devfn; +}; + +/* Key Configuration Unit Structure */ + +struct acpi_keyp_config_unit { + struct acpi_keyp_common_header header; + u8 protocol_type; + u8 version; + u8 root_port_count; + u8 flags; + u64 register_base_address; + struct acpi_keyp_rp_info rp_info[]; +}; + +enum acpi_keyp_protocol_type { + ACPI_KEYP_PROTO_TYPE_INVALID = 0, + ACPI_KEYP_PROTO_TYPE_PCIE, + ACPI_KEYP_PROTO_TYPE_CXL, + ACPI_KEYP_PROTO_TYPE_RESERVED +}; + +#define ACPI_KEYP_F_TVM_USABLE (1) + +/******************************************************************************* + * * LPIT - Low Power Idle Table * * Conforms to "ACPI Low Power Idle Table (LPIT)" July 2014. @@ -1167,7 +1301,10 @@ enum acpi_madt_type { ACPI_MADT_TYPE_IMSIC = 25, ACPI_MADT_TYPE_APLIC = 26, ACPI_MADT_TYPE_PLIC = 27, - ACPI_MADT_TYPE_RESERVED = 28, /* 28 to 0x7F are reserved */ + ACPI_MADT_TYPE_GICV5_IRS = 28, + ACPI_MADT_TYPE_GICV5_ITS = 29, + ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE = 30, + ACPI_MADT_TYPE_RESERVED = 31, /* 31 to 0x7F are reserved */ ACPI_MADT_TYPE_OEM_RESERVED = 0x80 /* 0x80 to 0xFF are reserved for OEM use */ }; @@ -1289,7 +1426,7 @@ struct acpi_madt_local_x2apic_nmi { u8 reserved[3]; /* reserved - must be zero */ }; -/* 11: Generic interrupt - GICC (ACPI 5.0 + ACPI 6.0 + ACPI 6.3 + ACPI 6.5 changes) */ +/* 11: Generic interrupt - GICC (ACPI 5.0 + ACPI 6.0 + ACPI 6.3 + ACPI 6.5 + ACPI 6.7 changes) */ struct acpi_madt_generic_interrupt { struct acpi_subtable_header header; @@ -1310,6 +1447,8 @@ struct acpi_madt_generic_interrupt { u8 reserved2[1]; u16 spe_interrupt; /* ACPI 6.3 */ u16 trbe_interrupt; /* ACPI 6.5 */ + u16 iaffid; /* ACPI 6.7 */ + u32 irs_id; }; /* Masks for Flags field above */ @@ -1332,7 +1471,7 @@ struct acpi_madt_generic_distributor { u8 reserved2[3]; /* reserved - must be zero */ }; -/* Values for Version field above */ +/* Values for Version field above and Version field in acpi_madt_gicv5_irs */ enum acpi_madt_gic_version { ACPI_MADT_GIC_VERSION_NONE = 0, @@ -1340,7 +1479,8 @@ enum acpi_madt_gic_version { ACPI_MADT_GIC_VERSION_V2 = 2, ACPI_MADT_GIC_VERSION_V3 = 3, ACPI_MADT_GIC_VERSION_V4 = 4, - ACPI_MADT_GIC_VERSION_RESERVED = 5 /* 5 and greater are reserved */ + ACPI_MADT_GIC_VERSION_V5 = 5, + ACPI_MADT_GIC_VERSION_RESERVED = 6 /* 6 and greater are reserved */ }; /* 13: Generic MSI Frame (ACPI 5.1) */ @@ -1611,6 +1751,41 @@ struct acpi_madt_plic { u32 gsi_base; }; +/* 28: Arm GICv5 IRS (ACPI 6.7) */ +struct acpi_madt_gicv5_irs { + struct acpi_subtable_header header; + u8 version; + u8 reserved; + u32 irs_id; + u32 flags; + u32 reserved2; + u64 config_base_address; + u64 setlpi_base_address; +}; + +#define ACPI_MADT_IRS_NON_COHERENT (1) + +/* 29: Arm GICv5 ITS Config Frame (ACPI 6.7) */ +struct acpi_madt_gicv5_translator { + struct acpi_subtable_header header; + u8 flags; + u8 reserved; /* reserved - must be zero */ + u32 translator_id; + u64 base_address; +}; + +#define ACPI_MADT_GICV5_ITS_NON_COHERENT (1) + +/* 30: Arm GICv5 ITS Translate Frame (ACPI 6.7) */ +struct acpi_madt_gicv5_translate_frame { + struct acpi_subtable_header header; + u16 reserved; /* reserved - must be zero */ + u32 linked_translator_id; + u32 translate_frame_id; + u32 reserved2; + u64 base_address; +}; + /* 80: OEM data */ struct acpi_madt_oem_data { @@ -2826,6 +3001,15 @@ struct acpi_pptt_cache { /* 1: Cache Type Structure for PPTT version 3 */ struct acpi_pptt_cache_v1 { + struct acpi_subtable_header header; + u16 reserved; + u32 flags; + u32 next_level_of_cache; + u32 size; + u32 number_of_sets; + u8 associativity; + u8 attributes; + u16 line_size; u32 cache_id; }; @@ -3065,6 +3249,8 @@ struct acpi_ras2_patrol_scrub_param { u32 flags; u32 scrub_params_out; u32 scrub_params_in; + u32 ext_scrub_params; + u8 scrub_rate_desc[256]; }; /* Masks for Flags field above */ diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index 79d3aa5a4bad..7ca456e88377 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -238,6 +238,7 @@ struct acpi_srat_mem_affinity { #define ACPI_SRAT_MEM_ENABLED (1) /* 00: Use affinity structure */ #define ACPI_SRAT_MEM_HOT_PLUGGABLE (1<<1) /* 01: Memory region is hot pluggable */ #define ACPI_SRAT_MEM_NON_VOLATILE (1<<2) /* 02: Memory region is non-volatile */ +#define ACPI_SRAT_MEM_SPEC_PURPOSE (1<<3) /* 03: Memory is intended for specific-purpose usage */ /* 2: Processor Local X2_APIC Affinity (ACPI 4.0) */ diff --git a/include/acpi/acuuid.h b/include/acpi/acuuid.h index 25dd3e998727..b2e29da6ba0a 100644 --- a/include/acpi/acuuid.h +++ b/include/acpi/acuuid.h @@ -37,6 +37,11 @@ #define UUID_DEVICE_LABELING "e5c937d0-3553-4d7a-9117-ea4d19c3434d" #define UUID_PHYSICAL_PRESENCE "3dddfaa6-361b-4eb4-a424-8d10089d1653" +/* TPM */ +#define UUID_HARDWARE_INFORMATION "cf8e16a5-c1e8-4e25-b712-4f54a96702c8" +#define UUID_START_METHOD "6bbf6cab-5463-4714-b7cd-f0203c0368d4" +#define UUID_MEMORY_CLEAR "376054ed-cc13-4675-901c-4756d7f2d45d" + /* NVDIMM - NFIT table */ #define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66" @@ -71,4 +76,5 @@ #define UUID_USB4_CAPABILITIES "23a0d13a-26ab-486c-9c5f-0ffa525a575a" #define UUID_1ST_FUNCTION_ID "893f00a6-660c-494e-bcfd-3043f4fb67c0" #define UUID_2ND_FUNCTION_ID "107ededd-d381-4fd7-8da9-08e9a6c79644" +#define UUID_FAN_TRIP_POINTS "a7611840-99fe-41ae-a488-35c75926c8eb" #endif /* __ACUUID_H__ */ diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 13fa81504844..44d41ecb1b1d 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -39,7 +39,8 @@ /* CPPC_AUTO_ACT_WINDOW_MAX_SIG is 127, so 128 and 129 will decay to 127 when writing */ #define CPPC_AUTO_ACT_WINDOW_SIG_CARRY_THRESH 129 -#define CPPC_ENERGY_PERF_MAX (0xFF) +#define CPPC_EPP_PERFORMANCE_PREF 0x00 +#define CPPC_EPP_ENERGY_EFFICIENCY_PREF 0xFF /* Each register has the folowing format. */ struct cpc_reg { @@ -119,8 +120,6 @@ struct cppc_perf_caps { u32 lowest_nonlinear_perf; u32 lowest_freq; u32 nominal_freq; - u32 energy_perf; - bool auto_sel; }; struct cppc_perf_ctrls { @@ -128,6 +127,7 @@ struct cppc_perf_ctrls { u32 min_perf; u32 desired_perf; u32 energy_perf; + bool auto_sel; }; struct cppc_perf_fb_ctrs { diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h index ebd21b05fe6e..7bea522c0657 100644 --- a/include/acpi/ghes.h +++ b/include/acpi/ghes.h @@ -21,6 +21,7 @@ struct ghes { struct acpi_hest_generic_v2 *generic_v2; }; struct acpi_hest_generic_status *estatus; + unsigned int estatus_length; unsigned long flags; union { struct list_head list; @@ -29,6 +30,7 @@ struct ghes { }; struct device *dev; struct list_head elist; + void __iomem *error_status_vaddr; }; struct ghes_estatus_node { diff --git a/include/acpi/processor.h b/include/acpi/processor.h index d0eccbd920e5..7146a8e9e9c2 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -417,32 +417,15 @@ static inline void acpi_processor_throttling_init(void) {} #endif /* CONFIG_ACPI_CPU_FREQ_PSS */ /* in processor_idle.c */ -extern struct cpuidle_driver acpi_idle_driver; #ifdef CONFIG_ACPI_PROCESSOR_IDLE -int acpi_processor_power_init(struct acpi_processor *pr); -int acpi_processor_power_exit(struct acpi_processor *pr); +void acpi_processor_power_init(struct acpi_processor *pr); +void acpi_processor_power_exit(struct acpi_processor *pr); int acpi_processor_power_state_has_changed(struct acpi_processor *pr); int acpi_processor_hotplug(struct acpi_processor *pr); -#else -static inline int acpi_processor_power_init(struct acpi_processor *pr) -{ - return -ENODEV; -} - -static inline int acpi_processor_power_exit(struct acpi_processor *pr) -{ - return -ENODEV; -} - -static inline int acpi_processor_power_state_has_changed(struct acpi_processor *pr) -{ - return -ENODEV; -} - -static inline int acpi_processor_hotplug(struct acpi_processor *pr) -{ - return -ENODEV; -} +void acpi_processor_register_idle_driver(void); +void acpi_processor_unregister_idle_driver(void); +int acpi_processor_ffh_lpi_probe(unsigned int cpu); +int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi); #endif /* CONFIG_ACPI_PROCESSOR_IDLE */ /* in processor_thermal.c */ @@ -465,11 +448,6 @@ static inline void acpi_thermal_cpufreq_exit(struct cpufreq_policy *policy) } #endif /* CONFIG_CPU_FREQ */ -#ifdef CONFIG_ACPI_PROCESSOR_IDLE -extern int acpi_processor_ffh_lpi_probe(unsigned int cpu); -extern int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi); -#endif - void acpi_processor_init_invariance_cppc(void); #endif diff --git a/include/cxl/event.h b/include/cxl/event.h index 6fd90f9cc203..ff97fea718d2 100644 --- a/include/cxl/event.h +++ b/include/cxl/event.h @@ -320,4 +320,26 @@ static inline int cxl_cper_prot_err_kfifo_get(struct cxl_cper_prot_err_work_data } #endif +#ifdef CONFIG_ACPI_APEI_PCIEAER +int cxl_cper_sec_prot_err_valid(struct cxl_cper_sec_prot_err *prot_err); +int cxl_cper_setup_prot_err_work_data(struct cxl_cper_prot_err_work_data *wd, + struct cxl_cper_sec_prot_err *prot_err, + int severity); +#else +static inline int +cxl_cper_sec_prot_err_valid(struct cxl_cper_sec_prot_err *prot_err) +{ + return -EOPNOTSUPP; +} +static inline int +cxl_cper_setup_prot_err_work_data(struct cxl_cper_prot_err_work_data *wd, + struct cxl_cper_sec_prot_err *prot_err, + int severity) +{ + return -EOPNOTSUPP; +} +#endif + +void cxl_cper_handle_prot_err(struct cxl_cper_prot_err_work_data *wd); + #endif /* _LINUX_CXL_EVENT_H */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index fbf0c3a65f59..3a412dcebc29 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -107,6 +107,7 @@ enum acpi_irq_model_id { ACPI_IRQ_MODEL_IOSAPIC, ACPI_IRQ_MODEL_PLATFORM, ACPI_IRQ_MODEL_GIC, + ACPI_IRQ_MODEL_GIC_V5, ACPI_IRQ_MODEL_LPIC, ACPI_IRQ_MODEL_RINTC, ACPI_IRQ_MODEL_COUNT diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h index d4ed5622cf2b..17bb3374f4ca 100644 --- a/include/linux/acpi_iort.h +++ b/include/linux/acpi_iort.h @@ -27,12 +27,15 @@ int iort_register_domain_token(int trans_id, phys_addr_t base, struct fwnode_handle *fw_node); void iort_deregister_domain_token(int trans_id); struct fwnode_handle *iort_find_domain_token(int trans_id); -int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id); +struct fwnode_handle *iort_iwb_handle(u32 iwb_id); #ifdef CONFIG_ACPI_IORT u32 iort_msi_map_id(struct device *dev, u32 id); +u32 iort_msi_xlate(struct device *dev, u32 id, struct fwnode_handle **node); +int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base); struct irq_domain *iort_get_device_domain(struct device *dev, u32 id, enum irq_domain_bus_token bus_token); +int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa); void acpi_configure_pmsi_domain(struct device *dev); void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode, struct list_head *head); @@ -46,9 +49,15 @@ phys_addr_t acpi_iort_dma_get_max_cpu_address(void); #else static inline u32 iort_msi_map_id(struct device *dev, u32 id) { return id; } +static inline u32 iort_msi_xlate(struct device *dev, u32 id, struct fwnode_handle **node) +{ return id; } +static inline int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base) +{ return -ENODEV; } static inline struct irq_domain *iort_get_device_domain( struct device *dev, u32 id, enum irq_domain_bus_token bus_token) { return NULL; } +static inline int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa) +{ return -ENODEV; } static inline void acpi_configure_pmsi_domain(struct device *dev) { } static inline void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode, struct list_head *head) { } diff --git a/include/linux/cper.h b/include/linux/cper.h index 5b1236d8c65b..440b35e459e5 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -595,7 +595,8 @@ void cper_mem_err_pack(const struct cper_sec_mem_err *, const char *cper_mem_err_unpack(struct trace_seq *, struct cper_mem_err_compact *); void cper_print_proc_arm(const char *pfx, - const struct cper_sec_proc_arm *proc); + const struct cper_sec_proc_arm *proc, + u32 length); void cper_print_proc_ia(const char *pfx, const struct cper_sec_proc_ia *proc); int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg); diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h index 68ddcdb1cec5..3da1ad80fc9d 100644 --- a/include/linux/irqchip/arm-gic-v5.h +++ b/include/linux/irqchip/arm-gic-v5.h @@ -265,6 +265,12 @@ #define GICV5_IWB_WENABLE_STATUSR_IDLE BIT(0) +#define GICV5_GSI_IC_TYPE GENMASK(31, 29) +#define GICV5_GSI_IWB_TYPE 0x7 + +#define GICV5_GSI_IWB_FRAME_ID GENMASK(28, 16) +#define GICV5_GSI_IWB_WIRE GENMASK(15, 0) + /* * Global Data structures and functions */ @@ -344,6 +350,7 @@ void __init gicv5_init_lpi_domain(void); void __init gicv5_free_lpi_domain(void); int gicv5_irs_of_probe(struct device_node *parent); +int gicv5_irs_acpi_probe(void); void gicv5_irs_remove(void); int gicv5_irs_enable(void); void gicv5_irs_its_probe(void); @@ -391,4 +398,5 @@ int gicv5_alloc_lpi(void); void gicv5_free_lpi(u32 lpi); void __init gicv5_its_of_probe(struct device_node *parent); +void __init gicv5_its_acpi_probe(void); #endif diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 62f81bbeb490..73c25d40846c 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -257,7 +257,8 @@ static inline void irq_domain_set_pm_device(struct irq_domain *d, struct device #ifdef CONFIG_IRQ_DOMAIN struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id, - const char *name, phys_addr_t *pa); + const char *name, phys_addr_t *pa, + struct fwnode_handle *parent); enum { IRQCHIP_FWNODE_REAL, @@ -267,18 +268,39 @@ enum { static inline struct fwnode_handle *irq_domain_alloc_named_fwnode(const char *name) { - return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL); + return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, NULL); +} + +static inline +struct fwnode_handle *irq_domain_alloc_named_parented_fwnode(const char *name, + struct fwnode_handle *parent) +{ + return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, parent); } static inline struct fwnode_handle *irq_domain_alloc_named_id_fwnode(const char *name, int id) { return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED_ID, id, name, - NULL); + NULL, NULL); +} + +static inline +struct fwnode_handle *irq_domain_alloc_named_id_parented_fwnode(const char *name, int id, + struct fwnode_handle *parent) +{ + return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED_ID, id, name, + NULL, parent); } static inline struct fwnode_handle *irq_domain_alloc_fwnode(phys_addr_t *pa) { - return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa); + return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa, NULL); +} + +static inline struct fwnode_handle *irq_domain_alloc_parented_fwnode(phys_addr_t *pa, + struct fwnode_handle *parent) +{ + return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa, parent); } void irq_domain_free_fwnode(struct fwnode_handle *fwnode); diff --git a/include/linux/msi.h b/include/linux/msi.h index 8003e3218c46..8ddb05d5c96a 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -702,7 +702,8 @@ void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg); void pci_msi_mask_irq(struct irq_data *data); void pci_msi_unmask_irq(struct irq_data *data); u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev); -u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node); +u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev, + struct fwnode_handle **node); struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev); void pci_msix_prepare_desc(struct irq_domain *domain, msi_alloc_info_t *arg, struct msi_desc *desc); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 078225b514d4..c0c54baadf04 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -12,7 +12,8 @@ #include <linux/acpi.h> #ifdef CONFIG_ACPI -extern acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev); +extern acpi_status pci_acpi_add_root_pm_notifier(struct acpi_device *dev, + struct acpi_pci_root *pci_root); static inline acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev) { return acpi_remove_pm_notifier(dev); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 2652c4cfd877..baf77cd167c4 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -33,6 +33,7 @@ static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq struct irqchip_fwid { struct fwnode_handle fwnode; + struct fwnode_handle *parent; unsigned int type; char *name; phys_addr_t *pa; @@ -53,8 +54,16 @@ static const char *irqchip_fwnode_get_name(const struct fwnode_handle *fwnode) return fwid->name; } +static struct fwnode_handle *irqchip_fwnode_get_parent(const struct fwnode_handle *fwnode) +{ + struct irqchip_fwid *fwid = container_of(fwnode, struct irqchip_fwid, fwnode); + + return fwid->parent; +} + const struct fwnode_operations irqchip_fwnode_ops = { .get_name = irqchip_fwnode_get_name, + .get_parent = irqchip_fwnode_get_parent, }; EXPORT_SYMBOL_GPL(irqchip_fwnode_ops); @@ -65,6 +74,7 @@ EXPORT_SYMBOL_GPL(irqchip_fwnode_ops); * @id: Optional user provided id if name != NULL * @name: Optional user provided domain name * @pa: Optional user-provided physical address + * @parent: Optional parent fwnode_handle * * Allocate a struct irqchip_fwid, and return a pointer to the embedded * fwnode_handle (or NULL on failure). @@ -76,7 +86,8 @@ EXPORT_SYMBOL_GPL(irqchip_fwnode_ops); */ struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id, const char *name, - phys_addr_t *pa) + phys_addr_t *pa, + struct fwnode_handle *parent) { struct irqchip_fwid *fwid; char *n; @@ -104,6 +115,7 @@ struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id, fwid->type = type; fwid->name = n; fwid->pa = pa; + fwid->parent = parent; fwnode_init(&fwid->fwnode, &irqchip_fwnode_ops); return &fwid->fwnode; } |
