ACPI updates for 5.20-rc1

- Use facilities provided by the driver core and some additional
    helpers to handle the children of a given ACPI device object in
    multiple places instead of using the children and node list heads in
    struct acpi_device which is error prone (Rafael Wysocki).
 
  - Fix ACPI-related device reference counting issue in the hisi_lpc bus
    driver (Yang Yingliang).
 
  - Drop the children and node list heads that are not needed any more
    from struct acpi_device (Rafael Wysocki).
 
  - Drop driver member from struct acpi_device (Uwe Kleine-König).
 
  - Drop redundant check from acpi_device_remove() (Uwe Kleine-König).
 
  - Prepare the CPPC library for handling backwards-compatible future
    _CPC return package formats gracefully (Rafael Wysocki).
 
  - Clean up the ACPI EC driver after previous changes in it (Hans
    de Goede).
 
  - Drop leftover acpi_processor_get_limit_info() declaration (Riwen
    Lu).
 
  - Split out thermal initialization from ACPI PSS (Riwen Lu).
 
  - Annotate more functions in the ACPI CPU idle driver to live in the
    cpuidle section (Guilherme G. Piccoli).
 
  - Fix _EINJ vs "special purpose" EFI memory regions (Dan Williams).
 
  - Implement a better fix to avoid spamming the console with old error
    logs (Tony Luck).
 
  - Fix typo in a comment in the APEI code (Xiang wangx).
 
  - Save NVS memory during transitions into S3 on Lenovo G40-45 (Manyi
    Li).
 
  - Add support for upcoming AMD uPEP device ID AMDI008 to the ACPI
    suspend-to-idle driver for x86 platforms (Shyam Sundar S K).
 
  - Clean up checks related to the ACPI_FADT_LOW_POWER_S0 platform flag
    in the LPIT table driver and the suspend-to-idle driver for x86
    platforms (Rafael Wysocki).
 
  - Print information messages regarding declared LPS0 idle support in
    the platform firmware (Rafael Wysocki).
 
  - Fix missing check in register_device_clock() in the ACPI driver for
    Intel SoCs (huhai).
 
  - Fix ACS setup in the VIOT table parser (Eric Auger).
 
  - Skip IRQ override on AMD Zen platforms where it's harmful (Chuanhong
    Guo).
 
  - Use native backlight on Dell Inspiron N4010 (Hans de Goede).
 
  - Use native backlight on some TongFang devices (Werner Sembach).
 
  - Drop X86 dependency from the ACPI backlight driver Kconfig (Riwen
    Lu).
 
  - Shorten the quirk list in the ACPI backlight driver by identifying
    Clevo by board_name only (Werner Sembach).
 
  - Remove useless NULL pointer checks from 2 ACPI PCI link management
    functions (Andrey Strachuk).
 
  - Fix obsolete example in the ACPI EINJ documentation (Qifu Zhang).
 
  - Update links and references to _DSD-related documents (Sudeep
    Holla).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmLoKr8SHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRxOIoP/3mM3FKIXzHE0P3L9sY6vV470yeW8pA7
 /9SeZsswN872MuCO9J/EAINmgAk+HKOzJK4QUgYREwObagS5vPMAdYBRpGaNgBYg
 RPqoZIAaop6KZzhnzS3kE4luAEuIvTECSELLB5sRgcnLK16gf00RhOd/4jLW3Vdc
 LFUeh0Dubz+ZX9sD8jUh6+Z70nl0mJWQitT5JddqfiUxnMYAYmEqXA77NfVFSCLa
 UTQVBBAspdfT5tp0h4yUGYE3+cNXWRcZ/yx2UDIAcy4qjzzOZwYIOjzPJhkT8dE+
 7XTG2NpAqmsnEW9UCC9eJAG1X3c7X0yGJGj8XE2iqLBRqVvJcL7d/VNybFyj9odz
 zI3NLEBYsZP2d4wDGi1p2OwcmpftxQqfY62fe8yUEjsFA5HL0qu9iM+Xh9VgmiFA
 vPkT/7Fjx0chpMLwMXiSn/Olm79vVBu/tnjt64/BSsWDUlxQ5Hwx871Uq0nSJVoY
 gZazgbG/SFoPvDqe6n4UfOKtodgpvkVU1J6VFHGuLUEXquH7WdsypJMEvq3VQ8C0
 hgc3nDENx7on8iK+jyJOTbc5rNd8MZDDuAapst5BxPWFXZ/G9Td0jDzh0oea489I
 UTq0epp7oUhHpZMH0h+szIPMLxo+AnMKv8j7qWoy0qPpjer4B98h4szceFzHBbVZ
 uc8vA3EcO9UM
 =2/wJ
 -----END PGP SIGNATURE-----

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

Pull ACPI updates from Rafael Wysocki:
 "These rework the handling of ACPI device objects to use the driver
  core facilities for managing child ones instead of some questionable
  home-grown ways without the requisite locking and reference counting,
  clean up the EC driver, improve suspend-to-idle handling on x86, add
  some systems to the ACPI backlight quirk list, fix some assorted
  issues, clean up code and improve documentation.

  Specifics:

   - Use facilities provided by the driver core and some additional
     helpers to handle the children of a given ACPI device object in
     multiple places instead of using the children and node list heads
     in struct acpi_device which is error prone (Rafael Wysocki).

   - Fix ACPI-related device reference counting issue in the hisi_lpc
     bus driver (Yang Yingliang).

   - Drop the children and node list heads that are not needed any more
     from struct acpi_device (Rafael Wysocki).

   - Drop driver member from struct acpi_device (Uwe Kleine-König).

   - Drop redundant check from acpi_device_remove() (Uwe Kleine-König).

   - Prepare the CPPC library for handling backwards-compatible future
     _CPC return package formats gracefully (Rafael Wysocki).

   - Clean up the ACPI EC driver after previous changes in it (Hans de
     Goede).

   - Drop leftover acpi_processor_get_limit_info() declaration (Riwen
     Lu).

   - Split out thermal initialization from ACPI PSS (Riwen Lu).

   - Annotate more functions in the ACPI CPU idle driver to live in the
     cpuidle section (Guilherme G. Piccoli).

   - Fix _EINJ vs "special purpose" EFI memory regions (Dan Williams).

   - Implement a better fix to avoid spamming the console with old error
     logs (Tony Luck).

   - Fix typo in a comment in the APEI code (Xiang wangx).

   - Save NVS memory during transitions into S3 on Lenovo G40-45 (Manyi
     Li).

   - Add support for upcoming AMD uPEP device ID AMDI008 to the ACPI
     suspend-to-idle driver for x86 platforms (Shyam Sundar S K).

   - Clean up checks related to the ACPI_FADT_LOW_POWER_S0 platform flag
     in the LPIT table driver and the suspend-to-idle driver for x86
     platforms (Rafael Wysocki).

   - Print information messages regarding declared LPS0 idle support in
     the platform firmware (Rafael Wysocki).

   - Fix missing check in register_device_clock() in the ACPI driver for
     Intel SoCs (huhai).

   - Fix ACS setup in the VIOT table parser (Eric Auger).

   - Skip IRQ override on AMD Zen platforms where it's harmful
     (Chuanhong Guo).

   - Use native backlight on Dell Inspiron N4010 (Hans de Goede).

   - Use native backlight on some TongFang devices (Werner Sembach).

   - Drop X86 dependency from the ACPI backlight driver Kconfig (Riwen
     Lu).

   - Shorten the quirk list in the ACPI backlight driver by identifying
     Clevo by board_name only (Werner Sembach).

   - Remove useless NULL pointer checks from 2 ACPI PCI link management
     functions (Andrey Strachuk).

   - Fix obsolete example in the ACPI EINJ documentation (Qifu Zhang).

   - Update links and references to _DSD-related documents (Sudeep
     Holla)"

* tag 'acpi-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (46 commits)
  ACPI/PCI: Remove useless NULL pointer checks
  ACPI: CPPC: Do not prevent CPPC from working in the future
  ACPI: PM: x86: Print messages regarding LPS0 idle support
  ACPI: resource: skip IRQ override on AMD Zen platforms
  Documentation: ACPI: EINJ: Fix obsolete example
  ACPI: video: Use native backlight on Dell Inspiron N4010
  ACPI: PM: s2idle: Use LPS0 idle if ACPI_FADT_LOW_POWER_S0 is unset
  Revert "ACPI / PM: LPIT: Register sysfs attributes based on FADT"
  ACPI: video: Shortening quirk list by identifying Clevo by board_name only
  ACPI: video: Force backlight native for some TongFang devices
  ACPI: PM: s2idle: Add support for upcoming AMD uPEP HID AMDI008
  ACPI: VIOT: Fix ACS setup
  ACPI: bus: Drop unused list heads from struct acpi_device
  hisi_lpc: Use acpi_dev_for_each_child()
  bus: hisi_lpc: fix missing platform_device_put() in hisi_lpc_acpi_probe()
  ACPI: bus: Drop driver member of struct acpi_device
  ACPI: bus: Drop redundant check in acpi_device_remove()
  ACPI: APEI: Fix _EINJ vs EFI_MEMORY_SP
  ACPI: LPSS: Fix missing check in register_device_clock()
  ACPI: APEI: Better fix to avoid spamming the console with old error logs
  ...
This commit is contained in:
Linus Torvalds 2022-08-02 11:12:25 -07:00
commit 8fa0db3a9b
40 changed files with 757 additions and 657 deletions

View file

@ -21,7 +21,9 @@ specific type) associated with it.
In the ACPI _DSD context it is an element of the sub-package following the In the ACPI _DSD context it is an element of the sub-package following the
generic Device Properties UUID in the _DSD return package as specified in the generic Device Properties UUID in the _DSD return package as specified in the
Device Properties UUID definition document [1]_. section titled "Well-Known _DSD UUIDs and Data Structure Formats" sub-section
"Device Properties UUID" in _DSD (Device Specific Data) Implementation Guide
document [1]_.
It also may be regarded as the definition of a key and the associated data type It also may be regarded as the definition of a key and the associated data type
that can be returned by _DSD in the Device Properties UUID sub-package for a that can be returned by _DSD in the Device Properties UUID sub-package for a
@ -36,7 +38,9 @@ Property subsets are nested collections of properties. Each of them is
associated with an additional key (name) allowing the subset to be referred associated with an additional key (name) allowing the subset to be referred
to as a whole (and to be treated as a separate entity). The canonical to as a whole (and to be treated as a separate entity). The canonical
representation of property subsets is via the mechanism specified in the representation of property subsets is via the mechanism specified in the
Hierarchical Properties Extension UUID definition document [2]_. section titled "Well-Known _DSD UUIDs and Data Structure Formats" sub-section
"Hierarchical Data Extension UUID" in _DSD (Device Specific Data)
Implementation Guide document [1]_.
Property sets may be hierarchical. That is, a property set may contain Property sets may be hierarchical. That is, a property set may contain
multiple property subsets that each may contain property subsets of its multiple property subsets that each may contain property subsets of its
@ -96,5 +100,4 @@ contents.
References References
========== ==========
.. [1] https://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf .. [1] https://github.com/UEFI/DSD-Guide
.. [2] https://www.uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.1.pdf

View file

@ -168,7 +168,7 @@ An error injection example::
0x00000008 Memory Correctable 0x00000008 Memory Correctable
0x00000010 Memory Uncorrectable non-fatal 0x00000010 Memory Uncorrectable non-fatal
# echo 0x12345000 > param1 # Set memory address for injection # echo 0x12345000 > param1 # Set memory address for injection
# echo $((-1 << 12)) > param2 # Mask 0xfffffffffffff000 - anywhere in this page # echo 0xfffffffffffff000 > param2 # Mask - anywhere in this page
# echo 0x8 > error_type # Choose correctable memory error # echo 0x8 > error_type # Choose correctable memory error
# echo 1 > error_inject # Inject now # echo 1 > error_inject # Inject now

View file

@ -210,7 +210,7 @@ config ACPI_TINY_POWER_BUTTON_SIGNAL
config ACPI_VIDEO config ACPI_VIDEO
tristate "Video" tristate "Video"
depends on X86 && BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on INPUT depends on INPUT
select THERMAL select THERMAL
help help
@ -255,7 +255,6 @@ config ACPI_DOCK
config ACPI_CPU_FREQ_PSS config ACPI_CPU_FREQ_PSS
bool bool
select THERMAL
config ACPI_PROCESSOR_CSTATE config ACPI_PROCESSOR_CSTATE
def_bool y def_bool y
@ -287,6 +286,7 @@ config ACPI_PROCESSOR
depends on X86 || IA64 || ARM64 || LOONGARCH depends on X86 || IA64 || ARM64 || LOONGARCH
select ACPI_PROCESSOR_IDLE select ACPI_PROCESSOR_IDLE
select ACPI_CPU_FREQ_PSS if X86 || IA64 || LOONGARCH select ACPI_CPU_FREQ_PSS if X86 || IA64 || LOONGARCH
select THERMAL
default y default y
help help
This driver adds support for the ACPI Processor package. It is required This driver adds support for the ACPI Processor package. It is required

View file

@ -109,10 +109,9 @@ obj-$(CONFIG_ACPI_PPTT) += pptt.o
obj-$(CONFIG_ACPI_PFRUT) += pfr_update.o pfr_telemetry.o obj-$(CONFIG_ACPI_PFRUT) += pfr_update.o pfr_telemetry.o
# processor has its own "processor." module_param namespace # processor has its own "processor." module_param namespace
processor-y := processor_driver.o processor-y := processor_driver.o processor_thermal.o
processor-$(CONFIG_ACPI_PROCESSOR_IDLE) += processor_idle.o processor-$(CONFIG_ACPI_PROCESSOR_IDLE) += processor_idle.o
processor-$(CONFIG_ACPI_CPU_FREQ_PSS) += processor_throttling.o \ processor-$(CONFIG_ACPI_CPU_FREQ_PSS) += processor_throttling.o
processor_thermal.o
processor-$(CONFIG_CPU_FREQ) += processor_perflib.o processor-$(CONFIG_CPU_FREQ) += processor_perflib.o
obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o

View file

@ -109,17 +109,11 @@ static void lpit_update_residency(struct lpit_residency_info *info,
if (!info->iomem_addr) if (!info->iomem_addr)
return; return;
if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
return;
/* Silently fail, if cpuidle attribute group is not present */ /* Silently fail, if cpuidle attribute group is not present */
sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj, sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
&dev_attr_low_power_idle_system_residency_us.attr, &dev_attr_low_power_idle_system_residency_us.attr,
"cpuidle"); "cpuidle");
} else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { } else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
return;
/* Silently fail, if cpuidle attribute group is not present */ /* Silently fail, if cpuidle attribute group is not present */
sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj, sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
&dev_attr_low_power_idle_cpu_residency_us.attr, &dev_attr_low_power_idle_cpu_residency_us.attr,

View file

@ -422,6 +422,9 @@ static int register_device_clock(struct acpi_device *adev,
if (!lpss_clk_dev) if (!lpss_clk_dev)
lpt_register_clock_device(); lpt_register_clock_device();
if (IS_ERR(lpss_clk_dev))
return PTR_ERR(lpss_clk_dev);
clk_data = platform_get_drvdata(lpss_clk_dev); clk_data = platform_get_drvdata(lpss_clk_dev);
if (!clk_data) if (!clk_data)
return -ENODEV; return -ENODEV;

View file

@ -1150,24 +1150,25 @@ acpi_video_get_device_type(struct acpi_video_bus *video,
return 0; return 0;
} }
static int static int acpi_video_bus_get_one_device(struct acpi_device *device, void *arg)
acpi_video_bus_get_one_device(struct acpi_device *device,
struct acpi_video_bus *video)
{ {
unsigned long long device_id; struct acpi_video_bus *video = arg;
int status, device_type;
struct acpi_video_device *data;
struct acpi_video_device_attrib *attribute; struct acpi_video_device_attrib *attribute;
struct acpi_video_device *data;
unsigned long long device_id;
acpi_status status;
int device_type;
status = status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); /* Skip devices without _ADR instead of failing. */
/* Some device omits _ADR, we skip them instead of fail */
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return 0; goto exit;
data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
if (!data) if (!data) {
dev_dbg(&device->dev, "Cannot attach\n");
return -ENOMEM; return -ENOMEM;
}
strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
@ -1230,7 +1231,9 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
list_add_tail(&data->entry, &video->video_device_list); list_add_tail(&data->entry, &video->video_device_list);
mutex_unlock(&video->device_list_lock); mutex_unlock(&video->device_list_lock);
return status; exit:
video->child_count++;
return 0;
} }
/* /*
@ -1542,9 +1545,6 @@ static int
acpi_video_bus_get_devices(struct acpi_video_bus *video, acpi_video_bus_get_devices(struct acpi_video_bus *video,
struct acpi_device *device) struct acpi_device *device)
{ {
int status = 0;
struct acpi_device *dev;
/* /*
* There are systems where video module known to work fine regardless * There are systems where video module known to work fine regardless
* of broken _DOD and ignoring returned value here doesn't cause * of broken _DOD and ignoring returned value here doesn't cause
@ -1552,16 +1552,7 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video,
*/ */
acpi_video_device_enumerate(video); acpi_video_device_enumerate(video);
list_for_each_entry(dev, &device->children, node) { return acpi_dev_for_each_child(device, acpi_video_bus_get_one_device, video);
status = acpi_video_bus_get_one_device(dev, video);
if (status) {
dev_err(&dev->dev, "Can't attach device\n");
break;
}
video->child_count++;
}
return status;
} }
/* acpi_video interface */ /* acpi_video interface */

View file

@ -3,7 +3,7 @@
* apei-base.c - ACPI Platform Error Interface (APEI) supporting * apei-base.c - ACPI Platform Error Interface (APEI) supporting
* infrastructure * infrastructure
* *
* APEI allows to report errors (for example from the chipset) to the * APEI allows to report errors (for example from the chipset) to
* the operating system. This improves NMI handling especially. In * the operating system. This improves NMI handling especially. In
* addition it supports error serialization and error injection. * addition it supports error serialization and error injection.
* *

View file

@ -29,16 +29,26 @@
#undef pr_fmt #undef pr_fmt
#define pr_fmt(fmt) "BERT: " fmt #define pr_fmt(fmt) "BERT: " fmt
#define ACPI_BERT_PRINT_MAX_RECORDS 5
#define ACPI_BERT_PRINT_MAX_LEN 1024 #define ACPI_BERT_PRINT_MAX_LEN 1024
static int bert_disable; static int bert_disable;
/*
* Print "all" the error records in the BERT table, but avoid huge spam to
* the console if the BIOS included oversize records, or too many records.
* Skipping some records here does not lose anything because the full
* data is available to user tools in:
* /sys/firmware/acpi/tables/data/BERT
*/
static void __init bert_print_all(struct acpi_bert_region *region, static void __init bert_print_all(struct acpi_bert_region *region,
unsigned int region_len) unsigned int region_len)
{ {
struct acpi_hest_generic_status *estatus = struct acpi_hest_generic_status *estatus =
(struct acpi_hest_generic_status *)region; (struct acpi_hest_generic_status *)region;
int remain = region_len; int remain = region_len;
int printed = 0, skipped = 0;
u32 estatus_len; u32 estatus_len;
while (remain >= sizeof(struct acpi_bert_region)) { while (remain >= sizeof(struct acpi_bert_region)) {
@ -46,24 +56,26 @@ static void __init bert_print_all(struct acpi_bert_region *region,
if (remain < estatus_len) { if (remain < estatus_len) {
pr_err(FW_BUG "Truncated status block (length: %u).\n", pr_err(FW_BUG "Truncated status block (length: %u).\n",
estatus_len); estatus_len);
return; break;
} }
/* No more error records. */ /* No more error records. */
if (!estatus->block_status) if (!estatus->block_status)
return; break;
if (cper_estatus_check(estatus)) { if (cper_estatus_check(estatus)) {
pr_err(FW_BUG "Invalid error record.\n"); pr_err(FW_BUG "Invalid error record.\n");
return; break;
} }
pr_info_once("Error records from previous boot:\n"); if (estatus_len < ACPI_BERT_PRINT_MAX_LEN &&
if (region_len < ACPI_BERT_PRINT_MAX_LEN) printed < ACPI_BERT_PRINT_MAX_RECORDS) {
pr_info_once("Error records from previous boot:\n");
cper_estatus_print(KERN_INFO HW_ERR, estatus); cper_estatus_print(KERN_INFO HW_ERR, estatus);
else printed++;
pr_info_once("Max print length exceeded, table data is available at:\n" } else {
"/sys/firmware/acpi/tables/data/BERT"); skipped++;
}
/* /*
* Because the boot error source is "one-time polled" type, * Because the boot error source is "one-time polled" type,
@ -75,6 +87,9 @@ static void __init bert_print_all(struct acpi_bert_region *region,
estatus = (void *)estatus + estatus_len; estatus = (void *)estatus + estatus_len;
remain -= estatus_len; remain -= estatus_len;
} }
if (skipped)
pr_info(HW_ERR "Skipped %d error records\n", skipped);
} }
static int __init setup_bert_disable(char *str) static int __init setup_bert_disable(char *str)

View file

@ -546,6 +546,8 @@ static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
!= REGION_INTERSECTS) && != REGION_INTERSECTS) &&
(region_intersects(base_addr, size, IORESOURCE_MEM, IORES_DESC_PERSISTENT_MEMORY) (region_intersects(base_addr, size, IORESOURCE_MEM, IORES_DESC_PERSISTENT_MEMORY)
!= REGION_INTERSECTS) && != REGION_INTERSECTS) &&
(region_intersects(base_addr, size, IORESOURCE_MEM, IORES_DESC_SOFT_RESERVED)
!= REGION_INTERSECTS) &&
!arch_is_platform_page(base_addr))) !arch_is_platform_page(base_addr)))
return -EINVAL; return -EINVAL;

View file

@ -464,7 +464,6 @@ static void acpi_bus_osc_negotiate_usb_control(void)
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
{ {
struct acpi_device *adev; struct acpi_device *adev;
struct acpi_driver *driver;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
bool hotplug_event = false; bool hotplug_event = false;
@ -516,10 +515,13 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
if (!adev) if (!adev)
goto err; goto err;
driver = adev->driver; if (adev->dev.driver) {
if (driver && driver->ops.notify && struct acpi_driver *driver = to_acpi_driver(adev->dev.driver);
(driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
driver->ops.notify(adev, type); if (driver && driver->ops.notify &&
(driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
driver->ops.notify(adev, type);
}
if (!hotplug_event) { if (!hotplug_event) {
acpi_bus_put_acpi_device(adev); acpi_bus_put_acpi_device(adev);
@ -538,8 +540,9 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
static void acpi_notify_device(acpi_handle handle, u32 event, void *data) static void acpi_notify_device(acpi_handle handle, u32 event, void *data)
{ {
struct acpi_device *device = data; struct acpi_device *device = data;
struct acpi_driver *acpi_drv = to_acpi_driver(device->dev.driver);
device->driver->ops.notify(device, event); acpi_drv->ops.notify(device, event);
} }
static void acpi_notify_device_fixed(void *data) static void acpi_notify_device_fixed(void *data)
@ -1032,8 +1035,6 @@ static int acpi_device_probe(struct device *dev)
if (ret) if (ret)
return ret; return ret;
acpi_dev->driver = acpi_drv;
pr_debug("Driver [%s] successfully bound to device [%s]\n", pr_debug("Driver [%s] successfully bound to device [%s]\n",
acpi_drv->name, acpi_dev->pnp.bus_id); acpi_drv->name, acpi_dev->pnp.bus_id);
@ -1043,7 +1044,6 @@ static int acpi_device_probe(struct device *dev)
if (acpi_drv->ops.remove) if (acpi_drv->ops.remove)
acpi_drv->ops.remove(acpi_dev); acpi_drv->ops.remove(acpi_dev);
acpi_dev->driver = NULL;
acpi_dev->driver_data = NULL; acpi_dev->driver_data = NULL;
return ret; return ret;
} }
@ -1059,15 +1059,14 @@ static int acpi_device_probe(struct device *dev)
static void acpi_device_remove(struct device *dev) static void acpi_device_remove(struct device *dev)
{ {
struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_driver *acpi_drv = acpi_dev->driver; struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver);
if (acpi_drv->ops.notify)
acpi_device_remove_notify_handler(acpi_dev);
if (acpi_drv->ops.remove)
acpi_drv->ops.remove(acpi_dev);
if (acpi_drv) {
if (acpi_drv->ops.notify)
acpi_device_remove_notify_handler(acpi_dev);
if (acpi_drv->ops.remove)
acpi_drv->ops.remove(acpi_dev);
}
acpi_dev->driver = NULL;
acpi_dev->driver_data = NULL; acpi_dev->driver_data = NULL;
put_device(dev); put_device(dev);
@ -1101,6 +1100,7 @@ static int acpi_dev_for_one_check(struct device *dev, void *context)
return adwc->fn(to_acpi_device(dev), adwc->data); return adwc->fn(to_acpi_device(dev), adwc->data);
} }
EXPORT_SYMBOL_GPL(acpi_dev_for_each_child);
int acpi_dev_for_each_child(struct acpi_device *adev, int acpi_dev_for_each_child(struct acpi_device *adev,
int (*fn)(struct acpi_device *, void *), void *data) int (*fn)(struct acpi_device *, void *), void *data)
@ -1113,6 +1113,18 @@ int acpi_dev_for_each_child(struct acpi_device *adev,
return device_for_each_child(&adev->dev, &adwc, acpi_dev_for_one_check); return device_for_each_child(&adev->dev, &adwc, acpi_dev_for_one_check);
} }
int acpi_dev_for_each_child_reverse(struct acpi_device *adev,
int (*fn)(struct acpi_device *, void *),
void *data)
{
struct acpi_dev_walk_context adwc = {
.fn = fn,
.data = data,
};
return device_for_each_child_reverse(&adev->dev, &adwc, acpi_dev_for_one_check);
}
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
Initialization/Cleanup Initialization/Cleanup
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
@ -1402,6 +1414,7 @@ static int __init acpi_init(void)
pci_mmcfg_late_init(); pci_mmcfg_late_init();
acpi_iort_init(); acpi_iort_init();
acpi_viot_early_init();
acpi_hest_init(); acpi_hest_init();
acpi_ghes_init(); acpi_ghes_init();
acpi_scan_init(); acpi_scan_init();

View file

@ -23,17 +23,18 @@ static const struct acpi_device_id container_device_ids[] = {
#ifdef CONFIG_ACPI_CONTAINER #ifdef CONFIG_ACPI_CONTAINER
static int check_offline(struct acpi_device *adev, void *not_used)
{
if (acpi_scan_is_offline(adev, false))
return 0;
return -EBUSY;
}
static int acpi_container_offline(struct container_dev *cdev) static int acpi_container_offline(struct container_dev *cdev)
{ {
struct acpi_device *adev = ACPI_COMPANION(&cdev->dev);
struct acpi_device *child;
/* Check all of the dependent devices' physical companions. */ /* Check all of the dependent devices' physical companions. */
list_for_each_entry(child, &adev->children, node) return acpi_dev_for_each_child(ACPI_COMPANION(&cdev->dev), check_offline, NULL);
if (!acpi_scan_is_offline(child, false))
return -EBUSY;
return 0;
} }
static void acpi_container_release(struct device *dev) static void acpi_container_release(struct device *dev)

View file

@ -618,33 +618,6 @@ static int pcc_data_alloc(int pcc_ss_id)
return 0; return 0;
} }
/* Check if CPPC revision + num_ent combination is supported */
static bool is_cppc_supported(int revision, int num_ent)
{
int expected_num_ent;
switch (revision) {
case CPPC_V2_REV:
expected_num_ent = CPPC_V2_NUM_ENT;
break;
case CPPC_V3_REV:
expected_num_ent = CPPC_V3_NUM_ENT;
break;
default:
pr_debug("Firmware exports unsupported CPPC revision: %d\n",
revision);
return false;
}
if (expected_num_ent != num_ent) {
pr_debug("Firmware exports %d entries. Expected: %d for CPPC rev:%d\n",
num_ent, expected_num_ent, revision);
return false;
}
return true;
}
/* /*
* An example CPC table looks like the following. * An example CPC table looks like the following.
* *
@ -733,7 +706,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
cpc_obj->type, pr->id); cpc_obj->type, pr->id);
goto out_free; goto out_free;
} }
cpc_ptr->num_entries = num_ent;
/* Second entry should be revision. */ /* Second entry should be revision. */
cpc_obj = &out_obj->package.elements[1]; cpc_obj = &out_obj->package.elements[1];
@ -744,10 +716,32 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
cpc_obj->type, pr->id); cpc_obj->type, pr->id);
goto out_free; goto out_free;
} }
cpc_ptr->version = cpc_rev;
if (!is_cppc_supported(cpc_rev, num_ent)) if (cpc_rev < CPPC_V2_REV) {
pr_debug("Unsupported _CPC Revision (%d) for CPU:%d\n", cpc_rev,
pr->id);
goto out_free; goto out_free;
}
/*
* Disregard _CPC if the number of entries in the return pachage is not
* as expected, but support future revisions being proper supersets of
* the v3 and only causing more entries to be returned by _CPC.
*/
if ((cpc_rev == CPPC_V2_REV && num_ent != CPPC_V2_NUM_ENT) ||
(cpc_rev == CPPC_V3_REV && num_ent != CPPC_V3_NUM_ENT) ||
(cpc_rev > CPPC_V3_REV && num_ent <= CPPC_V3_NUM_ENT)) {
pr_debug("Unexpected number of _CPC return package entries (%d) for CPU:%d\n",
num_ent, pr->id);
goto out_free;
}
if (cpc_rev > CPPC_V3_REV) {
num_ent = CPPC_V3_NUM_ENT;
cpc_rev = CPPC_V3_REV;
}
cpc_ptr->num_entries = num_ent;
cpc_ptr->version = cpc_rev;
/* Iterate through remaining entries in _CPC */ /* Iterate through remaining entries in _CPC */
for (i = 2; i < num_ent; i++) { for (i = 2; i < num_ent; i++) {

View file

@ -369,6 +369,28 @@ int acpi_device_fix_up_power(struct acpi_device *device)
} }
EXPORT_SYMBOL_GPL(acpi_device_fix_up_power); EXPORT_SYMBOL_GPL(acpi_device_fix_up_power);
static int fix_up_power_if_applicable(struct acpi_device *adev, void *not_used)
{
if (adev->status.present && adev->status.enabled)
acpi_device_fix_up_power(adev);
return 0;
}
/**
* acpi_device_fix_up_power_extended - Force device and its children into D0.
* @adev: Parent device object whose power state is to be fixed up.
*
* Call acpi_device_fix_up_power() for @adev and its children so long as they
* are reported as present and enabled.
*/
void acpi_device_fix_up_power_extended(struct acpi_device *adev)
{
acpi_device_fix_up_power(adev);
acpi_dev_for_each_child(adev, fix_up_power_if_applicable, NULL);
}
EXPORT_SYMBOL_GPL(acpi_device_fix_up_power_extended);
int acpi_device_update_power(struct acpi_device *device, int *state_p) int acpi_device_update_power(struct acpi_device *device, int *state_p)
{ {
int state; int state;

View file

@ -376,7 +376,7 @@ eject_store(struct device *d, struct device_attribute *attr,
return -EINVAL; return -EINVAL;
if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled) if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled)
&& !acpi_device->driver) && !d->driver)
return -ENODEV; return -ENODEV;
status = acpi_get_type(acpi_device->handle, &not_used); status = acpi_get_type(acpi_device->handle, &not_used);

View file

@ -180,7 +180,6 @@ static struct workqueue_struct *ec_wq;
static struct workqueue_struct *ec_query_wq; static struct workqueue_struct *ec_query_wq;
static int EC_FLAGS_CORRECT_ECDT; /* Needs ECDT port address correction */ static int EC_FLAGS_CORRECT_ECDT; /* Needs ECDT port address correction */
static int EC_FLAGS_IGNORE_DSDT_GPE; /* Needs ECDT GPE as correction setting */
static int EC_FLAGS_TRUST_DSDT_GPE; /* Needs DSDT GPE as correction setting */ static int EC_FLAGS_TRUST_DSDT_GPE; /* Needs DSDT GPE as correction setting */
static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */
@ -1407,24 +1406,16 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
if (ec->data_addr == 0 || ec->command_addr == 0) if (ec->data_addr == 0 || ec->command_addr == 0)
return AE_OK; return AE_OK;
if (boot_ec && boot_ec_is_ecdt && EC_FLAGS_IGNORE_DSDT_GPE) { /* Get GPE bit assignment (EC events). */
/* /* TODO: Add support for _GPE returning a package */
* Always inherit the GPE number setting from the ECDT status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
* EC. if (ACPI_SUCCESS(status))
*/ ec->gpe = tmp;
ec->gpe = boot_ec->gpe; /*
} else { * Errors are non-fatal, allowing for ACPI Reduced Hardware
/* Get GPE bit assignment (EC events). */ * platforms which use GpioInt instead of GPE.
/* TODO: Add support for _GPE returning a package */ */
status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
if (ACPI_SUCCESS(status))
ec->gpe = tmp;
/*
* Errors are non-fatal, allowing for ACPI Reduced Hardware
* platforms which use GpioInt instead of GPE.
*/
}
/* Use the global lock for all EC transactions? */ /* Use the global lock for all EC transactions? */
tmp = 0; tmp = 0;
acpi_evaluate_integer(handle, "_GLK", NULL, &tmp); acpi_evaluate_integer(handle, "_GLK", NULL, &tmp);
@ -1626,15 +1617,18 @@ static int acpi_ec_add(struct acpi_device *device)
} }
if (boot_ec && ec->command_addr == boot_ec->command_addr && if (boot_ec && ec->command_addr == boot_ec->command_addr &&
ec->data_addr == boot_ec->data_addr && ec->data_addr == boot_ec->data_addr) {
!EC_FLAGS_TRUST_DSDT_GPE) {
/* /*
* Trust PNP0C09 namespace location rather than * Trust PNP0C09 namespace location rather than ECDT ID.
* ECDT ID. But trust ECDT GPE rather than _GPE * But trust ECDT GPE rather than _GPE because of ASUS
* because of ASUS quirks, so do not change * quirks. So do not change boot_ec->gpe to ec->gpe,
* boot_ec->gpe to ec->gpe. * except when the TRUST_DSDT_GPE quirk is set.
*/ */
boot_ec->handle = ec->handle; boot_ec->handle = ec->handle;
if (EC_FLAGS_TRUST_DSDT_GPE)
boot_ec->gpe = ec->gpe;
acpi_handle_debug(ec->handle, "duplicated.\n"); acpi_handle_debug(ec->handle, "duplicated.\n");
acpi_ec_free(ec); acpi_ec_free(ec);
ec = boot_ec; ec = boot_ec;
@ -1862,68 +1856,40 @@ static int ec_honor_dsdt_gpe(const struct dmi_system_id *id)
return 0; return 0;
} }
/*
* Some DSDTs contain wrong GPE setting.
* Asus FX502VD/VE, GL702VMK, X550VXK, X580VD
* https://bugzilla.kernel.org/show_bug.cgi?id=195651
*/
static int ec_honor_ecdt_gpe(const struct dmi_system_id *id)
{
pr_debug("Detected system needing ignore DSDT GPE setting.\n");
EC_FLAGS_IGNORE_DSDT_GPE = 1;
return 0;
}
static const struct dmi_system_id ec_dmi_table[] __initconst = { static const struct dmi_system_id ec_dmi_table[] __initconst = {
{ {
ec_correct_ecdt, "MSI MS-171F", { /*
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"), * MSI MS-171F
DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),}, NULL}, * https://bugzilla.kernel.org/show_bug.cgi?id=12461
*/
.callback = ec_correct_ecdt,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),
},
},
{ {
ec_honor_ecdt_gpe, "ASUS FX502VD", { /*
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), * HP Pavilion Gaming Laptop 15-cx0xxx
DMI_MATCH(DMI_PRODUCT_NAME, "FX502VD"),}, NULL}, * https://bugzilla.kernel.org/show_bug.cgi?id=209989
*/
.callback = ec_honor_dsdt_gpe,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Gaming Laptop 15-cx0xxx"),
},
},
{ {
ec_honor_ecdt_gpe, "ASUS FX502VE", { /*
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), * Samsung hardware
DMI_MATCH(DMI_PRODUCT_NAME, "FX502VE"),}, NULL}, * https://bugzilla.kernel.org/show_bug.cgi?id=44161
{ */
ec_honor_ecdt_gpe, "ASUS GL702VMK", { .callback = ec_clear_on_resume,
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), .matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "GL702VMK"),}, NULL}, DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
{ },
ec_honor_ecdt_gpe, "ASUSTeK COMPUTER INC. X505BA", { },
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), {}
DMI_MATCH(DMI_PRODUCT_NAME, "X505BA"),}, NULL},
{
ec_honor_ecdt_gpe, "ASUSTeK COMPUTER INC. X505BP", {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X505BP"),}, NULL},
{
ec_honor_ecdt_gpe, "ASUSTeK COMPUTER INC. X542BA", {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X542BA"),}, NULL},
{
ec_honor_ecdt_gpe, "ASUSTeK COMPUTER INC. X542BP", {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X542BP"),}, NULL},
{
ec_honor_ecdt_gpe, "ASUS X550VXK", {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X550VXK"),}, NULL},
{
ec_honor_ecdt_gpe, "ASUS X580VD", {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X580VD"),}, NULL},
{
/* https://bugzilla.kernel.org/show_bug.cgi?id=209989 */
ec_honor_dsdt_gpe, "HP Pavilion Gaming Laptop 15-cx0xxx", {
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Gaming Laptop 15-cx0xxx"),}, NULL},
{
ec_clear_on_resume, "Samsung hardware", {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL},
{},
}; };
void __init acpi_ec_ecdt_probe(void) void __init acpi_ec_ecdt_probe(void)
@ -2201,28 +2167,18 @@ static int acpi_ec_init_workqueues(void)
static const struct dmi_system_id acpi_ec_no_wakeup[] = { static const struct dmi_system_id acpi_ec_no_wakeup[] = {
{ {
.ident = "Thinkpad X1 Carbon 6th",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_FAMILY, "Thinkpad X1 Carbon 6th"), DMI_MATCH(DMI_PRODUCT_FAMILY, "Thinkpad X1 Carbon 6th"),
}, },
}, },
{ {
.ident = "ThinkPad X1 Carbon 6th",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X1 Carbon 6th"),
},
},
{
.ident = "ThinkPad X1 Yoga 3rd",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X1 Yoga 3rd"), DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X1 Yoga 3rd"),
}, },
}, },
{ {
.ident = "HP ZHAN 66 Pro",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HP"), DMI_MATCH(DMI_SYS_VENDOR, "HP"),
DMI_MATCH(DMI_PRODUCT_FAMILY, "103C_5336AN HP ZHAN 66 Pro"), DMI_MATCH(DMI_PRODUCT_FAMILY, "103C_5336AN HP ZHAN 66 Pro"),

View file

@ -77,12 +77,22 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
#define FIND_CHILD_MIN_SCORE 1 #define FIND_CHILD_MIN_SCORE 1
#define FIND_CHILD_MAX_SCORE 2 #define FIND_CHILD_MAX_SCORE 2
static int match_any(struct acpi_device *adev, void *not_used)
{
return 1;
}
static bool acpi_dev_has_children(struct acpi_device *adev)
{
return acpi_dev_for_each_child(adev, match_any, NULL) > 0;
}
static int find_child_checks(struct acpi_device *adev, bool check_children) static int find_child_checks(struct acpi_device *adev, bool check_children)
{ {
unsigned long long sta; unsigned long long sta;
acpi_status status; acpi_status status;
if (check_children && list_empty(&adev->children)) if (check_children && !acpi_dev_has_children(adev))
return -ENODEV; return -ENODEV;
status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta); status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
@ -105,54 +115,97 @@ static int find_child_checks(struct acpi_device *adev, bool check_children)
return FIND_CHILD_MAX_SCORE; return FIND_CHILD_MAX_SCORE;
} }
struct find_child_walk_data {
struct acpi_device *adev;
u64 address;
int score;
bool check_sta;
bool check_children;
};
static int check_one_child(struct acpi_device *adev, void *data)
{
struct find_child_walk_data *wd = data;
int score;
if (!adev->pnp.type.bus_address || acpi_device_adr(adev) != wd->address)
return 0;
if (!wd->adev) {
/*
* This is the first matching object, so save it. If it is not
* necessary to look for any other matching objects, stop the
* search.
*/
wd->adev = adev;
return !(wd->check_sta || wd->check_children);
}
/*
* There is more than one matching device object with the same _ADR
* value. That really is unexpected, so we are kind of beyond the scope
* of the spec here. We have to choose which one to return, though.
*
* First, get the score for the previously found object and terminate
* the walk if it is maximum.
*/
if (!wd->score) {
score = find_child_checks(wd->adev, wd->check_children);
if (score == FIND_CHILD_MAX_SCORE)
return 1;
wd->score = score;
}
/*
* Second, if the object that has just been found has a better score,
* replace the previously found one with it and terminate the walk if
* the new score is maximum.
*/
score = find_child_checks(adev, wd->check_children);
if (score > wd->score) {
wd->adev = adev;
if (score == FIND_CHILD_MAX_SCORE)
return 1;
wd->score = score;
}
/* Continue, because there may be better matches. */
return 0;
}
static struct acpi_device *acpi_find_child(struct acpi_device *parent,
u64 address, bool check_children,
bool check_sta)
{
struct find_child_walk_data wd = {
.address = address,
.check_children = check_children,
.check_sta = check_sta,
.adev = NULL,
.score = 0,
};
if (parent)
acpi_dev_for_each_child(parent, check_one_child, &wd);
return wd.adev;
}
struct acpi_device *acpi_find_child_device(struct acpi_device *parent, struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
u64 address, bool check_children) u64 address, bool check_children)
{ {
struct acpi_device *adev, *ret = NULL; return acpi_find_child(parent, address, check_children, true);
int ret_score = 0;
if (!parent)
return NULL;
list_for_each_entry(adev, &parent->children, node) {
acpi_bus_address addr = acpi_device_adr(adev);
int score;
if (!adev->pnp.type.bus_address || addr != address)
continue;
if (!ret) {
/* This is the first matching object. Save it. */
ret = adev;
continue;
}
/*
* There is more than one matching device object with the same
* _ADR value. That really is unexpected, so we are kind of
* beyond the scope of the spec here. We have to choose which
* one to return, though.
*
* First, check if the previously found object is good enough
* and return it if so. Second, do the same for the object that
* we've just found.
*/
if (!ret_score) {
ret_score = find_child_checks(ret, check_children);
if (ret_score == FIND_CHILD_MAX_SCORE)
return ret;
}
score = find_child_checks(adev, check_children);
if (score == FIND_CHILD_MAX_SCORE) {
return adev;
} else if (score > ret_score) {
ret = adev;
ret_score = score;
}
}
return ret;
} }
EXPORT_SYMBOL_GPL(acpi_find_child_device); EXPORT_SYMBOL_GPL(acpi_find_child_device);
struct acpi_device *acpi_find_child_by_adr(struct acpi_device *adev,
acpi_bus_address adr)
{
return acpi_find_child(adev, adr, false, false);
}
EXPORT_SYMBOL_GPL(acpi_find_child_by_adr);
static void acpi_physnode_link_name(char *buf, unsigned int node_id) static void acpi_physnode_link_name(char *buf, unsigned int node_id)
{ {
if (node_id > 0) if (node_id > 0)

View file

@ -95,7 +95,7 @@ static acpi_status acpi_pci_link_check_possible(struct acpi_resource *resource,
case ACPI_RESOURCE_TYPE_IRQ: case ACPI_RESOURCE_TYPE_IRQ:
{ {
struct acpi_resource_irq *p = &resource->data.irq; struct acpi_resource_irq *p = &resource->data.irq;
if (!p || !p->interrupt_count) { if (!p->interrupt_count) {
acpi_handle_debug(handle, acpi_handle_debug(handle,
"Blank _PRS IRQ resource\n"); "Blank _PRS IRQ resource\n");
return AE_OK; return AE_OK;
@ -121,7 +121,7 @@ static acpi_status acpi_pci_link_check_possible(struct acpi_resource *resource,
{ {
struct acpi_resource_extended_irq *p = struct acpi_resource_extended_irq *p =
&resource->data.extended_irq; &resource->data.extended_irq;
if (!p || !p->interrupt_count) { if (!p->interrupt_count) {
acpi_handle_debug(handle, acpi_handle_debug(handle,
"Blank _PRS EXT IRQ resource\n"); "Blank _PRS EXT IRQ resource\n");
return AE_OK; return AE_OK;
@ -182,7 +182,7 @@ static acpi_status acpi_pci_link_check_current(struct acpi_resource *resource,
case ACPI_RESOURCE_TYPE_IRQ: case ACPI_RESOURCE_TYPE_IRQ:
{ {
struct acpi_resource_irq *p = &resource->data.irq; struct acpi_resource_irq *p = &resource->data.irq;
if (!p || !p->interrupt_count) { if (!p->interrupt_count) {
/* /*
* IRQ descriptors may have no IRQ# bits set, * IRQ descriptors may have no IRQ# bits set,
* particularly those w/ _STA disabled * particularly those w/ _STA disabled
@ -197,7 +197,7 @@ static acpi_status acpi_pci_link_check_current(struct acpi_resource *resource,
{ {
struct acpi_resource_extended_irq *p = struct acpi_resource_extended_irq *p =
&resource->data.extended_irq; &resource->data.extended_irq;
if (!p || !p->interrupt_count) { if (!p->interrupt_count) {
/* /*
* extended IRQ descriptors must * extended IRQ descriptors must
* return at least 1 IRQ * return at least 1 IRQ

View file

@ -139,75 +139,17 @@ static int acpi_soft_cpu_dead(unsigned int cpu)
} }
#ifdef CONFIG_ACPI_CPU_FREQ_PSS #ifdef CONFIG_ACPI_CPU_FREQ_PSS
static int acpi_pss_perf_init(struct acpi_processor *pr, static void acpi_pss_perf_init(struct acpi_processor *pr)
struct acpi_device *device)
{ {
int result = 0;
acpi_processor_ppc_has_changed(pr, 0); acpi_processor_ppc_has_changed(pr, 0);
acpi_processor_get_throttling_info(pr); acpi_processor_get_throttling_info(pr);
if (pr->flags.throttling) if (pr->flags.throttling)
pr->flags.limit = 1; pr->flags.limit = 1;
pr->cdev = thermal_cooling_device_register("Processor", device,
&processor_cooling_ops);
if (IS_ERR(pr->cdev)) {
result = PTR_ERR(pr->cdev);
return result;
}
dev_dbg(&device->dev, "registered as cooling_device%d\n",
pr->cdev->id);
result = sysfs_create_link(&device->dev.kobj,
&pr->cdev->device.kobj,
"thermal_cooling");
if (result) {
dev_err(&device->dev,
"Failed to create sysfs link 'thermal_cooling'\n");
goto err_thermal_unregister;
}
result = sysfs_create_link(&pr->cdev->device.kobj,
&device->dev.kobj,
"device");
if (result) {
dev_err(&pr->cdev->device,
"Failed to create sysfs link 'device'\n");
goto err_remove_sysfs_thermal;
}
return 0;
err_remove_sysfs_thermal:
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
err_thermal_unregister:
thermal_cooling_device_unregister(pr->cdev);
return result;
}
static void acpi_pss_perf_exit(struct acpi_processor *pr,
struct acpi_device *device)
{
if (pr->cdev) {
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
sysfs_remove_link(&pr->cdev->device.kobj, "device");
thermal_cooling_device_unregister(pr->cdev);
pr->cdev = NULL;
}
} }
#else #else
static inline int acpi_pss_perf_init(struct acpi_processor *pr, static inline void acpi_pss_perf_init(struct acpi_processor *pr) {}
struct acpi_device *device)
{
return 0;
}
static inline void acpi_pss_perf_exit(struct acpi_processor *pr,
struct acpi_device *device) {}
#endif /* CONFIG_ACPI_CPU_FREQ_PSS */ #endif /* CONFIG_ACPI_CPU_FREQ_PSS */
static int __acpi_processor_start(struct acpi_device *device) static int __acpi_processor_start(struct acpi_device *device)
@ -229,7 +171,9 @@ static int __acpi_processor_start(struct acpi_device *device)
if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver) if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
acpi_processor_power_init(pr); acpi_processor_power_init(pr);
result = acpi_pss_perf_init(pr, device); acpi_pss_perf_init(pr);
result = acpi_processor_thermal_init(pr, device);
if (result) if (result)
goto err_power_exit; goto err_power_exit;
@ -239,7 +183,7 @@ static int __acpi_processor_start(struct acpi_device *device)
return 0; return 0;
result = -ENODEV; result = -ENODEV;
acpi_pss_perf_exit(pr, device); acpi_processor_thermal_exit(pr, device);
err_power_exit: err_power_exit:
acpi_processor_power_exit(pr); acpi_processor_power_exit(pr);
@ -277,10 +221,10 @@ static int acpi_processor_stop(struct device *dev)
return 0; return 0;
acpi_processor_power_exit(pr); acpi_processor_power_exit(pr);
acpi_pss_perf_exit(pr, device);
acpi_cppc_processor_exit(pr); acpi_cppc_processor_exit(pr);
acpi_processor_thermal_exit(pr, device);
return 0; return 0;
} }

View file

@ -607,7 +607,7 @@ static DEFINE_RAW_SPINLOCK(c3_lock);
* @cx: Target state context * @cx: Target state context
* @index: index of target state * @index: index of target state
*/ */
static int acpi_idle_enter_bm(struct cpuidle_driver *drv, static int __cpuidle acpi_idle_enter_bm(struct cpuidle_driver *drv,
struct acpi_processor *pr, struct acpi_processor *pr,
struct acpi_processor_cx *cx, struct acpi_processor_cx *cx,
int index) int index)
@ -664,7 +664,7 @@ static int acpi_idle_enter_bm(struct cpuidle_driver *drv,
return index; return index;
} }
static int acpi_idle_enter(struct cpuidle_device *dev, static int __cpuidle acpi_idle_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index) struct cpuidle_driver *drv, int index)
{ {
struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);
@ -693,7 +693,7 @@ static int acpi_idle_enter(struct cpuidle_device *dev,
return index; return index;
} }
static int acpi_idle_enter_s2idle(struct cpuidle_device *dev, static int __cpuidle acpi_idle_enter_s2idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index) struct cpuidle_driver *drv, int index)
{ {
struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu);

View file

@ -266,3 +266,57 @@ const struct thermal_cooling_device_ops processor_cooling_ops = {
.get_cur_state = processor_get_cur_state, .get_cur_state = processor_get_cur_state,
.set_cur_state = processor_set_cur_state, .set_cur_state = processor_set_cur_state,
}; };
int acpi_processor_thermal_init(struct acpi_processor *pr,
struct acpi_device *device)
{
int result = 0;
pr->cdev = thermal_cooling_device_register("Processor", device,
&processor_cooling_ops);
if (IS_ERR(pr->cdev)) {
result = PTR_ERR(pr->cdev);
return result;
}
dev_dbg(&device->dev, "registered as cooling_device%d\n",
pr->cdev->id);
result = sysfs_create_link(&device->dev.kobj,
&pr->cdev->device.kobj,
"thermal_cooling");
if (result) {
dev_err(&device->dev,
"Failed to create sysfs link 'thermal_cooling'\n");
goto err_thermal_unregister;
}
result = sysfs_create_link(&pr->cdev->device.kobj,
&device->dev.kobj,
"device");
if (result) {
dev_err(&pr->cdev->device,
"Failed to create sysfs link 'device'\n");
goto err_remove_sysfs_thermal;
}
return 0;
err_remove_sysfs_thermal:
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
err_thermal_unregister:
thermal_cooling_device_unregister(pr->cdev);
return result;
}
void acpi_processor_thermal_exit(struct acpi_processor *pr,
struct acpi_device *device)
{
if (pr->cdev) {
sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
sysfs_remove_link(&pr->cdev->device.kobj, "device");
thermal_cooling_device_unregister(pr->cdev);
pr->cdev = NULL;
}
}

View file

@ -1012,6 +1012,22 @@ static int acpi_node_prop_read(const struct fwnode_handle *fwnode,
propname, proptype, val, nval); propname, proptype, val, nval);
} }
static int stop_on_next(struct acpi_device *adev, void *data)
{
struct acpi_device **ret_p = data;
if (!*ret_p) {
*ret_p = adev;
return 1;
}
/* Skip until the "previous" object is found. */
if (*ret_p == adev)
*ret_p = NULL;
return 0;
}
/** /**
* acpi_get_next_subnode - Return the next child node handle for a fwnode * acpi_get_next_subnode - Return the next child node handle for a fwnode
* @fwnode: Firmware node to find the next child node for. * @fwnode: Firmware node to find the next child node for.
@ -1020,35 +1036,22 @@ static int acpi_node_prop_read(const struct fwnode_handle *fwnode,
struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode, struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode,
struct fwnode_handle *child) struct fwnode_handle *child)
{ {
const struct acpi_device *adev = to_acpi_device_node(fwnode); struct acpi_device *adev = to_acpi_device_node(fwnode);
const struct list_head *head;
struct list_head *next;
if ((!child || is_acpi_device_node(child)) && adev) { if ((!child || is_acpi_device_node(child)) && adev) {
struct acpi_device *child_adev; struct acpi_device *child_adev = to_acpi_device_node(child);
head = &adev->children; acpi_dev_for_each_child(adev, stop_on_next, &child_adev);
if (list_empty(head)) if (child_adev)
goto nondev; return acpi_fwnode_handle(child_adev);
if (child) { child = NULL;
adev = to_acpi_device_node(child);
next = adev->node.next;
if (next == head) {
child = NULL;
goto nondev;
}
child_adev = list_entry(next, struct acpi_device, node);
} else {
child_adev = list_first_entry(head, struct acpi_device,
node);
}
return acpi_fwnode_handle(child_adev);
} }
nondev:
if (!child || is_acpi_data_node(child)) { if (!child || is_acpi_data_node(child)) {
const struct acpi_data_node *data = to_acpi_data_node(fwnode); const struct acpi_data_node *data = to_acpi_data_node(fwnode);
const struct list_head *head;
struct list_head *next;
struct acpi_data_node *dn; struct acpi_data_node *dn;
/* /*

View file

@ -416,6 +416,16 @@ static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity,
{ {
int i; int i;
#ifdef CONFIG_X86
/*
* IRQ override isn't needed on modern AMD Zen systems and
* this override breaks active low IRQs on AMD Ryzen 6000 and
* newer systems. Skip it.
*/
if (boot_cpu_has(X86_FEATURE_ZEN))
return false;
#endif
for (i = 0; i < ARRAY_SIZE(skip_override_table); i++) { for (i = 0; i < ARRAY_SIZE(skip_override_table); i++) {
const struct irq_override_cmp *entry = &skip_override_table[i]; const struct irq_override_cmp *entry = &skip_override_table[i];

View file

@ -334,10 +334,9 @@ static int acpi_scan_device_check(struct acpi_device *adev)
return error; return error;
} }
static int acpi_scan_bus_check(struct acpi_device *adev) static int acpi_scan_bus_check(struct acpi_device *adev, void *not_used)
{ {
struct acpi_scan_handler *handler = adev->handler; struct acpi_scan_handler *handler = adev->handler;
struct acpi_device *child;
int error; int error;
acpi_bus_get_status(adev); acpi_bus_get_status(adev);
@ -353,19 +352,14 @@ static int acpi_scan_bus_check(struct acpi_device *adev)
dev_warn(&adev->dev, "Namespace scan failure\n"); dev_warn(&adev->dev, "Namespace scan failure\n");
return error; return error;
} }
list_for_each_entry(child, &adev->children, node) { return acpi_dev_for_each_child(adev, acpi_scan_bus_check, NULL);
error = acpi_scan_bus_check(child);
if (error)
return error;
}
return 0;
} }
static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type) static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
{ {
switch (type) { switch (type) {
case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_BUS_CHECK:
return acpi_scan_bus_check(adev); return acpi_scan_bus_check(adev, NULL);
case ACPI_NOTIFY_DEVICE_CHECK: case ACPI_NOTIFY_DEVICE_CHECK:
return acpi_scan_device_check(adev); return acpi_scan_device_check(adev);
case ACPI_NOTIFY_EJECT_REQUEST: case ACPI_NOTIFY_EJECT_REQUEST:
@ -471,8 +465,6 @@ static void acpi_device_del(struct acpi_device *device)
struct acpi_device_bus_id *acpi_device_bus_id; struct acpi_device_bus_id *acpi_device_bus_id;
mutex_lock(&acpi_device_lock); mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node)
if (!strcmp(acpi_device_bus_id->bus_id, if (!strcmp(acpi_device_bus_id->bus_id,
@ -488,6 +480,7 @@ static void acpi_device_del(struct acpi_device *device)
} }
list_del(&device->wakeup_list); list_del(&device->wakeup_list);
mutex_unlock(&acpi_device_lock); mutex_unlock(&acpi_device_lock);
acpi_power_add_remove_device(device, false); acpi_power_add_remove_device(device, false);
@ -680,8 +673,6 @@ static int __acpi_device_add(struct acpi_device *device,
* ------- * -------
* Link this device to its parent and siblings. * Link this device to its parent and siblings.
*/ */
INIT_LIST_HEAD(&device->children);
INIT_LIST_HEAD(&device->node);
INIT_LIST_HEAD(&device->wakeup_list); INIT_LIST_HEAD(&device->wakeup_list);
INIT_LIST_HEAD(&device->physical_node_list); INIT_LIST_HEAD(&device->physical_node_list);
INIT_LIST_HEAD(&device->del_list); INIT_LIST_HEAD(&device->del_list);
@ -721,9 +712,6 @@ static int __acpi_device_add(struct acpi_device *device,
list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list);
} }
if (device->parent)
list_add_tail(&device->node, &device->parent->children);
if (device->wakeup.flags.valid) if (device->wakeup.flags.valid)
list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list); list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list);
@ -752,9 +740,6 @@ static int __acpi_device_add(struct acpi_device *device,
err: err:
mutex_lock(&acpi_device_lock); mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
list_del(&device->wakeup_list); list_del(&device->wakeup_list);
err_unlock: err_unlock:
@ -2187,9 +2172,8 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
return ret; return ret;
} }
static void acpi_bus_attach(struct acpi_device *device, bool first_pass) static int acpi_bus_attach(struct acpi_device *device, void *first_pass)
{ {
struct acpi_device *child;
bool skip = !first_pass && device->flags.visited; bool skip = !first_pass && device->flags.visited;
acpi_handle ejd; acpi_handle ejd;
int ret; int ret;
@ -2206,7 +2190,7 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
device->flags.initialized = false; device->flags.initialized = false;
acpi_device_clear_enumerated(device); acpi_device_clear_enumerated(device);
device->flags.power_manageable = 0; device->flags.power_manageable = 0;
return; return 0;
} }
if (device->handler) if (device->handler)
goto ok; goto ok;
@ -2224,7 +2208,7 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
ret = acpi_scan_attach_handler(device); ret = acpi_scan_attach_handler(device);
if (ret < 0) if (ret < 0)
return; return 0;
device->flags.match_driver = true; device->flags.match_driver = true;
if (ret > 0 && !device->flags.enumeration_by_parent) { if (ret > 0 && !device->flags.enumeration_by_parent) {
@ -2234,19 +2218,20 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
ret = device_attach(&device->dev); ret = device_attach(&device->dev);
if (ret < 0) if (ret < 0)
return; return 0;
if (device->pnp.type.platform_id || device->flags.enumeration_by_parent) if (device->pnp.type.platform_id || device->flags.enumeration_by_parent)
acpi_default_enumeration(device); acpi_default_enumeration(device);
else else
acpi_device_set_enumerated(device); acpi_device_set_enumerated(device);
ok: ok:
list_for_each_entry(child, &device->children, node) acpi_dev_for_each_child(device, acpi_bus_attach, first_pass);
acpi_bus_attach(child, first_pass);
if (!skip && device->handler && device->handler->hotplug.notify_online) if (!skip && device->handler && device->handler->hotplug.notify_online)
device->handler->hotplug.notify_online(device); device->handler->hotplug.notify_online(device);
return 0;
} }
static int acpi_dev_get_first_consumer_dev_cb(struct acpi_dep_data *dep, void *data) static int acpi_dev_get_first_consumer_dev_cb(struct acpi_dep_data *dep, void *data)
@ -2274,7 +2259,7 @@ static void acpi_scan_clear_dep_fn(struct work_struct *work)
cdw = container_of(work, struct acpi_scan_clear_dep_work, work); cdw = container_of(work, struct acpi_scan_clear_dep_work, work);
acpi_scan_lock_acquire(); acpi_scan_lock_acquire();
acpi_bus_attach(cdw->adev, true); acpi_bus_attach(cdw->adev, (void *)true);
acpi_scan_lock_release(); acpi_scan_lock_release();
acpi_dev_put(cdw->adev); acpi_dev_put(cdw->adev);
@ -2432,7 +2417,7 @@ int acpi_bus_scan(acpi_handle handle)
if (!device) if (!device)
return -ENODEV; return -ENODEV;
acpi_bus_attach(device, true); acpi_bus_attach(device, (void *)true);
if (!acpi_bus_scan_second_pass) if (!acpi_bus_scan_second_pass)
return 0; return 0;
@ -2446,25 +2431,17 @@ int acpi_bus_scan(acpi_handle handle)
acpi_bus_check_add_2, NULL, NULL, acpi_bus_check_add_2, NULL, NULL,
(void **)&device); (void **)&device);
acpi_bus_attach(device, false); acpi_bus_attach(device, NULL);
return 0; return 0;
} }
EXPORT_SYMBOL(acpi_bus_scan); EXPORT_SYMBOL(acpi_bus_scan);
/** static int acpi_bus_trim_one(struct acpi_device *adev, void *not_used)
* acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects.
* @adev: Root of the ACPI namespace scope to walk.
*
* Must be called under acpi_scan_lock.
*/
void acpi_bus_trim(struct acpi_device *adev)
{ {
struct acpi_scan_handler *handler = adev->handler; struct acpi_scan_handler *handler = adev->handler;
struct acpi_device *child;
list_for_each_entry_reverse(child, &adev->children, node) acpi_dev_for_each_child_reverse(adev, acpi_bus_trim_one, NULL);
acpi_bus_trim(child);
adev->flags.match_driver = false; adev->flags.match_driver = false;
if (handler) { if (handler) {
@ -2482,6 +2459,19 @@ void acpi_bus_trim(struct acpi_device *adev)
acpi_device_set_power(adev, ACPI_STATE_D3_COLD); acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
adev->flags.initialized = false; adev->flags.initialized = false;
acpi_device_clear_enumerated(adev); acpi_device_clear_enumerated(adev);
return 0;
}
/**
* acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects.
* @adev: Root of the ACPI namespace scope to walk.
*
* Must be called under acpi_scan_lock.
*/
void acpi_bus_trim(struct acpi_device *adev)
{
acpi_bus_trim_one(adev, NULL);
} }
EXPORT_SYMBOL_GPL(acpi_bus_trim); EXPORT_SYMBOL_GPL(acpi_bus_trim);

View file

@ -360,6 +360,14 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_NAME, "80E3"), DMI_MATCH(DMI_PRODUCT_NAME, "80E3"),
}, },
}, },
{
.callback = init_nvs_save_s3,
.ident = "Lenovo G40-45",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "80E1"),
},
},
/* /*
* ThinkPad X1 Tablet(2016) cannot do suspend-to-idle using * ThinkPad X1 Tablet(2016) cannot do suspend-to-idle using
* the Low Power S0 Idle firmware interface (see * the Low Power S0 Idle firmware interface (see
@ -816,6 +824,9 @@ static const struct platform_s2idle_ops acpi_s2idle_ops = {
void __weak acpi_s2idle_setup(void) void __weak acpi_s2idle_setup(void)
{ {
if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)
pr_info("Efficient low-power S0 idle declared\n");
s2idle_set_ops(&acpi_s2idle_ops); s2idle_set_ops(&acpi_s2idle_ops);
} }

View file

@ -347,6 +347,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"), DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"),
}, },
}, },
{
.callback = video_detect_force_native,
/* Dell Inspiron N4010 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N4010"),
},
},
{ {
.callback = video_detect_force_native, .callback = video_detect_force_native,
/* Dell Vostro V131 */ /* Dell Vostro V131 */
@ -430,23 +438,6 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
.callback = video_detect_force_native, .callback = video_detect_force_native,
.ident = "Clevo NL5xRU", .ident = "Clevo NL5xRU",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"),
},
},
{
.callback = video_detect_force_native,
.ident = "Clevo NL5xRU",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"),
DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"),
},
},
{
.callback = video_detect_force_native,
.ident = "Clevo NL5xRU",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"),
}, },
}, },
@ -469,28 +460,60 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
{ {
.callback = video_detect_force_native, .callback = video_detect_force_native,
.ident = "Clevo NL5xNU", .ident = "Clevo NL5xNU",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"),
},
},
/*
* The TongFang PF5PU1G, PF4NU1F, PF5NU1G, and PF5LUXG/TUXEDO BA15 Gen10,
* Pulse 14/15 Gen1, and Pulse 15 Gen2 have the same problem as the Clevo
* NL5xRU and NL5xNU/TUXEDO Aura 15 Gen1 and Gen2. See the description
* above.
*/
{
.callback = video_detect_force_native,
.ident = "TongFang PF5PU1G",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "PF5PU1G"),
},
},
{
.callback = video_detect_force_native,
.ident = "TongFang PF4NU1F",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "PF4NU1F"),
},
},
{
.callback = video_detect_force_native,
.ident = "TongFang PF4NU1F",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), DMI_MATCH(DMI_BOARD_NAME, "PULSE1401"),
}, },
}, },
{ {
.callback = video_detect_force_native, .callback = video_detect_force_native,
.ident = "Clevo NL5xNU", .ident = "TongFang PF5NU1G",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), DMI_MATCH(DMI_BOARD_NAME, "PF5NU1G"),
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"),
}, },
}, },
{ {
.callback = video_detect_force_native, .callback = video_detect_force_native,
.ident = "Clevo NL5xNU", .ident = "TongFang PF5NU1G",
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), DMI_MATCH(DMI_BOARD_NAME, "PULSE1501"),
},
},
{
.callback = video_detect_force_native,
.ident = "TongFang PF5LUXG",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "PF5LUXG"),
}, },
}, },
/* /*
* Desktops which falsely report a backlight and which our heuristics * Desktops which falsely report a backlight and which our heuristics
* for this do not catch. * for this do not catch.

View file

@ -248,6 +248,26 @@ static int __init viot_parse_node(const struct acpi_viot_header *hdr)
return ret; return ret;
} }
/**
* acpi_viot_early_init - Test the presence of VIOT and enable ACS
*
* If the VIOT does exist, ACS must be enabled. This cannot be
* done in acpi_viot_init() which is called after the bus scan
*/
void __init acpi_viot_early_init(void)
{
#ifdef CONFIG_PCI
acpi_status status;
struct acpi_table_header *hdr;
status = acpi_get_table(ACPI_SIG_VIOT, 0, &hdr);
if (ACPI_FAILURE(status))
return;
pci_request_acs();
acpi_put_table(hdr);
#endif
}
/** /**
* acpi_viot_init - Parse the VIOT table * acpi_viot_init - Parse the VIOT table
* *
@ -319,12 +339,6 @@ static int viot_pci_dev_iommu_init(struct pci_dev *pdev, u16 dev_id, void *data)
epid = ((domain_nr - ep->segment_start) << 16) + epid = ((domain_nr - ep->segment_start) << 16) +
dev_id - ep->bdf_start + ep->endpoint_id; dev_id - ep->bdf_start + ep->endpoint_id;
/*
* If we found a PCI range managed by the viommu, we're
* the one that has to request ACS.
*/
pci_request_acs();
return viot_dev_iommu_init(&pdev->dev, ep->viommu, return viot_dev_iommu_init(&pdev->dev, ep->viommu,
epid); epid);
} }

View file

@ -369,9 +369,6 @@ static int lps0_device_attach(struct acpi_device *adev,
if (lps0_device_handle) if (lps0_device_handle)
return 0; return 0;
if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
return 0;
if (acpi_s2idle_vendor_amd()) { if (acpi_s2idle_vendor_amd()) {
/* AMD0004, AMD0005, AMDI0005: /* AMD0004, AMD0005, AMDI0005:
* - Should use rev_id 0x0 * - Should use rev_id 0x0
@ -397,7 +394,9 @@ static int lps0_device_attach(struct acpi_device *adev,
lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1; 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_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n",
ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask); ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask);
} else if (lps0_dsm_func_mask_microsoft > 0 && !strcmp(hid, "AMDI0007")) { } else if (lps0_dsm_func_mask_microsoft > 0 &&
(!strcmp(hid, "AMDI0007") ||
!strcmp(hid, "AMDI0008"))) {
lps0_dsm_func_mask_microsoft = -EINVAL; lps0_dsm_func_mask_microsoft = -EINVAL;
acpi_handle_debug(adev->handle, "_DSM Using AMD method\n"); acpi_handle_debug(adev->handle, "_DSM Using AMD method\n");
} }
@ -419,11 +418,15 @@ static int lps0_device_attach(struct acpi_device *adev,
lpi_device_get_constraints(); lpi_device_get_constraints();
/* /*
* Use suspend-to-idle by default if the default suspend mode was not * Use suspend-to-idle by default if ACPI_FADT_LOW_POWER_S0 is set in
* set from the command line. * the FADT and the default suspend mode was not set from the command
* line.
*/ */
if (mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) if ((acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) &&
mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3) {
mem_sleep_current = PM_SUSPEND_TO_IDLE; mem_sleep_current = PM_SUSPEND_TO_IDLE;
pr_info("Low-power S0 idle used by default for system suspend\n");
}
/* /*
* Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the

View file

@ -379,7 +379,7 @@ static void hisi_lpc_acpi_fixup_child_resource(struct device *hostdev,
/* /*
* hisi_lpc_acpi_set_io_res - set the resources for a child * hisi_lpc_acpi_set_io_res - set the resources for a child
* @child: the device node to be updated the I/O resource * @adev: ACPI companion of the device node to be updated the I/O resource
* @hostdev: the device node associated with host controller * @hostdev: the device node associated with host controller
* @res: double pointer to be set to the address of translated resources * @res: double pointer to be set to the address of translated resources
* @num_res: pointer to variable to hold the number of translated resources * @num_res: pointer to variable to hold the number of translated resources
@ -390,31 +390,24 @@ static void hisi_lpc_acpi_fixup_child_resource(struct device *hostdev,
* host-relative address resource. This function will return the translated * host-relative address resource. This function will return the translated
* logical PIO addresses for each child devices resources. * logical PIO addresses for each child devices resources.
*/ */
static int hisi_lpc_acpi_set_io_res(struct device *child, static int hisi_lpc_acpi_set_io_res(struct acpi_device *adev,
struct device *hostdev, struct device *hostdev,
const struct resource **res, int *num_res) const struct resource **res, int *num_res)
{ {
struct acpi_device *adev; struct acpi_device *host = to_acpi_device(adev->dev.parent);
struct acpi_device *host;
struct resource_entry *rentry; struct resource_entry *rentry;
LIST_HEAD(resource_list); LIST_HEAD(resource_list);
struct resource *resources; struct resource *resources;
int count; int count;
int i; int i;
if (!child || !hostdev)
return -EINVAL;
host = to_acpi_device(hostdev);
adev = to_acpi_device(child);
if (!adev->status.present) { if (!adev->status.present) {
dev_dbg(child, "device is not present\n"); dev_dbg(&adev->dev, "device is not present\n");
return -EIO; return -EIO;
} }
if (acpi_device_enumerated(adev)) { if (acpi_device_enumerated(adev)) {
dev_dbg(child, "has been enumerated\n"); dev_dbg(&adev->dev, "has been enumerated\n");
return -EIO; return -EIO;
} }
@ -425,7 +418,7 @@ static int hisi_lpc_acpi_set_io_res(struct device *child,
*/ */
count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
if (count <= 0) { if (count <= 0) {
dev_dbg(child, "failed to get resources\n"); dev_dbg(&adev->dev, "failed to get resources\n");
return count ? count : -EIO; return count ? count : -EIO;
} }
@ -454,7 +447,7 @@ static int hisi_lpc_acpi_set_io_res(struct device *child,
continue; continue;
ret = hisi_lpc_acpi_xlat_io_res(adev, host, &resources[i]); ret = hisi_lpc_acpi_xlat_io_res(adev, host, &resources[i]);
if (ret) { if (ret) {
dev_err(child, "translate IO range %pR failed (%d)\n", dev_err(&adev->dev, "translate IO range %pR failed (%d)\n",
&resources[i], ret); &resources[i], ret);
return ret; return ret;
} }
@ -471,6 +464,12 @@ static int hisi_lpc_acpi_remove_subdev(struct device *dev, void *unused)
return 0; return 0;
} }
static int hisi_lpc_acpi_clear_enumerated(struct acpi_device *adev, void *not_used)
{
acpi_device_clear_enumerated(adev);
return 0;
}
struct hisi_lpc_acpi_cell { struct hisi_lpc_acpi_cell {
const char *hid; const char *hid;
const char *name; const char *name;
@ -480,13 +479,92 @@ struct hisi_lpc_acpi_cell {
static void hisi_lpc_acpi_remove(struct device *hostdev) static void hisi_lpc_acpi_remove(struct device *hostdev)
{ {
struct acpi_device *adev = ACPI_COMPANION(hostdev);
struct acpi_device *child;
device_for_each_child(hostdev, NULL, hisi_lpc_acpi_remove_subdev); device_for_each_child(hostdev, NULL, hisi_lpc_acpi_remove_subdev);
acpi_dev_for_each_child(ACPI_COMPANION(hostdev),
hisi_lpc_acpi_clear_enumerated, NULL);
}
list_for_each_entry(child, &adev->children, node) static int hisi_lpc_acpi_add_child(struct acpi_device *child, void *data)
acpi_device_clear_enumerated(child); {
const char *hid = acpi_device_hid(child);
struct device *hostdev = data;
const struct hisi_lpc_acpi_cell *cell;
struct platform_device *pdev;
const struct resource *res;
bool found = false;
int num_res;
int ret;
ret = hisi_lpc_acpi_set_io_res(child, hostdev, &res, &num_res);
if (ret) {
dev_warn(hostdev, "set resource fail (%d)\n", ret);
return ret;
}
cell = (struct hisi_lpc_acpi_cell []){
/* ipmi */
{
.hid = "IPI0001",
.name = "hisi-lpc-ipmi",
},
/* 8250-compatible uart */
{
.hid = "HISI1031",
.name = "serial8250",
.pdata = (struct plat_serial8250_port []) {
{
.iobase = res->start,
.uartclk = 1843200,
.iotype = UPIO_PORT,
.flags = UPF_BOOT_AUTOCONF,
},
{}
},
.pdata_size = 2 *
sizeof(struct plat_serial8250_port),
},
{}
};
for (; cell && cell->name; cell++) {
if (!strcmp(cell->hid, hid)) {
found = true;
break;
}
}
if (!found) {
dev_warn(hostdev,
"could not find cell for child device (%s), discarding\n",
hid);
return 0;
}
pdev = platform_device_alloc(cell->name, PLATFORM_DEVID_AUTO);
if (!pdev)
return -ENOMEM;
pdev->dev.parent = hostdev;
ACPI_COMPANION_SET(&pdev->dev, child);
ret = platform_device_add_resources(pdev, res, num_res);
if (ret)
goto fail;
ret = platform_device_add_data(pdev, cell->pdata, cell->pdata_size);
if (ret)
goto fail;
ret = platform_device_add(pdev);
if (ret)
goto fail;
acpi_device_set_enumerated(child);
return 0;
fail:
platform_device_put(pdev);
return ret;
} }
/* /*
@ -501,94 +579,14 @@ static void hisi_lpc_acpi_remove(struct device *hostdev)
*/ */
static int hisi_lpc_acpi_probe(struct device *hostdev) static int hisi_lpc_acpi_probe(struct device *hostdev)
{ {
struct acpi_device *adev = ACPI_COMPANION(hostdev);
struct acpi_device *child;
int ret; int ret;
/* Only consider the children of the host */ /* Only consider the children of the host */
list_for_each_entry(child, &adev->children, node) { ret = acpi_dev_for_each_child(ACPI_COMPANION(hostdev),
const char *hid = acpi_device_hid(child); hisi_lpc_acpi_add_child, hostdev);
const struct hisi_lpc_acpi_cell *cell; if (ret)
struct platform_device *pdev; hisi_lpc_acpi_remove(hostdev);
const struct resource *res;
bool found = false;
int num_res;
ret = hisi_lpc_acpi_set_io_res(&child->dev, &adev->dev, &res,
&num_res);
if (ret) {
dev_warn(hostdev, "set resource fail (%d)\n", ret);
goto fail;
}
cell = (struct hisi_lpc_acpi_cell []){
/* ipmi */
{
.hid = "IPI0001",
.name = "hisi-lpc-ipmi",
},
/* 8250-compatible uart */
{
.hid = "HISI1031",
.name = "serial8250",
.pdata = (struct plat_serial8250_port []) {
{
.iobase = res->start,
.uartclk = 1843200,
.iotype = UPIO_PORT,
.flags = UPF_BOOT_AUTOCONF,
},
{}
},
.pdata_size = 2 *
sizeof(struct plat_serial8250_port),
},
{}
};
for (; cell && cell->name; cell++) {
if (!strcmp(cell->hid, hid)) {
found = true;
break;
}
}
if (!found) {
dev_warn(hostdev,
"could not find cell for child device (%s), discarding\n",
hid);
continue;
}
pdev = platform_device_alloc(cell->name, PLATFORM_DEVID_AUTO);
if (!pdev) {
ret = -ENOMEM;
goto fail;
}
pdev->dev.parent = hostdev;
ACPI_COMPANION_SET(&pdev->dev, child);
ret = platform_device_add_resources(pdev, res, num_res);
if (ret)
goto fail;
ret = platform_device_add_data(pdev, cell->pdata,
cell->pdata_size);
if (ret)
goto fail;
ret = platform_device_add(pdev);
if (ret)
goto fail;
acpi_device_set_enumerated(child);
}
return 0;
fail:
hisi_lpc_acpi_remove(hostdev);
return ret; return ret;
} }

View file

@ -60,12 +60,29 @@ int mfd_cell_disable(struct platform_device *pdev)
EXPORT_SYMBOL(mfd_cell_disable); EXPORT_SYMBOL(mfd_cell_disable);
#if IS_ENABLED(CONFIG_ACPI) #if IS_ENABLED(CONFIG_ACPI)
struct match_ids_walk_data {
struct acpi_device_id *ids;
struct acpi_device *adev;
};
static int match_device_ids(struct acpi_device *adev, void *data)
{
struct match_ids_walk_data *wd = data;
if (!acpi_match_device_ids(adev, wd->ids)) {
wd->adev = adev;
return 1;
}
return 0;
}
static void mfd_acpi_add_device(const struct mfd_cell *cell, static void mfd_acpi_add_device(const struct mfd_cell *cell,
struct platform_device *pdev) struct platform_device *pdev)
{ {
const struct mfd_cell_acpi_match *match = cell->acpi_match; const struct mfd_cell_acpi_match *match = cell->acpi_match;
struct acpi_device *parent, *child;
struct acpi_device *adev = NULL; struct acpi_device *adev = NULL;
struct acpi_device *parent;
parent = ACPI_COMPANION(pdev->dev.parent); parent = ACPI_COMPANION(pdev->dev.parent);
if (!parent) if (!parent)
@ -83,14 +100,14 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell,
if (match) { if (match) {
if (match->pnpid) { if (match->pnpid) {
struct acpi_device_id ids[2] = {}; struct acpi_device_id ids[2] = {};
struct match_ids_walk_data wd = {
.adev = NULL,
.ids = ids,
};
strlcpy(ids[0].id, match->pnpid, sizeof(ids[0].id)); strlcpy(ids[0].id, match->pnpid, sizeof(ids[0].id));
list_for_each_entry(child, &parent->children, node) { acpi_dev_for_each_child(parent, match_device_ids, &wd);
if (!acpi_match_device_ids(child, ids)) { adev = wd.adev;
adev = child;
break;
}
}
} else { } else {
adev = acpi_find_child_device(parent, match->adr, false); adev = acpi_find_child_device(parent, match->adr, false);
} }

View file

@ -775,8 +775,8 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
const struct sdhci_acpi_slot *slot; const struct sdhci_acpi_slot *slot;
struct acpi_device *device, *child;
const struct dmi_system_id *id; const struct dmi_system_id *id;
struct acpi_device *device;
struct sdhci_acpi_host *c; struct sdhci_acpi_host *c;
struct sdhci_host *host; struct sdhci_host *host;
struct resource *iomem; struct resource *iomem;
@ -796,10 +796,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
slot = sdhci_acpi_get_slot(device); slot = sdhci_acpi_get_slot(device);
/* Power on the SDHCI controller and its children */ /* Power on the SDHCI controller and its children */
acpi_device_fix_up_power(device); acpi_device_fix_up_power_extended(device);
list_for_each_entry(child, &device->children, node)
if (child->status.present && child->status.enabled)
acpi_device_fix_up_power(child);
if (sdhci_acpi_byt_defer(dev)) if (sdhci_acpi_byt_defer(dev))
return -EPROBE_DEFER; return -EPROBE_DEFER;

View file

@ -1240,16 +1240,11 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static void intel_mrfld_mmc_fix_up_power_slot(struct sdhci_pci_slot *slot) static void intel_mrfld_mmc_fix_up_power_slot(struct sdhci_pci_slot *slot)
{ {
struct acpi_device *device, *child; struct acpi_device *device;
device = ACPI_COMPANION(&slot->chip->pdev->dev); device = ACPI_COMPANION(&slot->chip->pdev->dev);
if (!device) if (device)
return; acpi_device_fix_up_power_extended(device);
acpi_device_fix_up_power(device);
list_for_each_entry(child, &device->children, node)
if (child->status.present && child->status.enabled)
acpi_device_fix_up_power(child);
} }
#else #else
static inline void intel_mrfld_mmc_fix_up_power_slot(struct sdhci_pci_slot *slot) {} static inline void intel_mrfld_mmc_fix_up_power_slot(struct sdhci_pci_slot *slot) {}

View file

@ -6842,6 +6842,31 @@ static const struct backlight_ops ibm_backlight_data = {
/* --------------------------------------------------------------------- */ /* --------------------------------------------------------------------- */
static int __init tpacpi_evaluate_bcl(struct acpi_device *adev, void *not_used)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
int rc;
status = acpi_evaluate_object(adev->handle, "_BCL", NULL, &buffer);
if (ACPI_FAILURE(status))
return 0;
obj = buffer.pointer;
if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
acpi_handle_info(adev->handle,
"Unknown _BCL data, please report this to %s\n",
TPACPI_MAIL);
rc = 0;
} else {
rc = obj->package.count;
}
kfree(obj);
return rc;
}
/* /*
* Call _BCL method of video device. On some ThinkPads this will * Call _BCL method of video device. On some ThinkPads this will
* switch the firmware to the ACPI brightness control mode. * switch the firmware to the ACPI brightness control mode.
@ -6849,37 +6874,13 @@ static const struct backlight_ops ibm_backlight_data = {
static int __init tpacpi_query_bcl_levels(acpi_handle handle) static int __init tpacpi_query_bcl_levels(acpi_handle handle)
{ {
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_device *device;
union acpi_object *obj;
struct acpi_device *device, *child;
int rc;
device = acpi_fetch_acpi_dev(handle); device = acpi_fetch_acpi_dev(handle);
if (!device) if (!device)
return 0; return 0;
rc = 0; return acpi_dev_for_each_child(device, tpacpi_evaluate_bcl, NULL);
list_for_each_entry(child, &device->children, node) {
acpi_status status = acpi_evaluate_object(child->handle, "_BCL",
NULL, &buffer);
if (ACPI_FAILURE(status)) {
buffer.length = ACPI_ALLOCATE_BUFFER;
continue;
}
obj = (union acpi_object *)buffer.pointer;
if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
pr_err("Unknown _BCL data, please report this to %s\n",
TPACPI_MAIL);
rc = 0;
} else {
rc = obj->package.count;
}
break;
}
kfree(buffer.pointer);
return rc;
} }

View file

@ -127,6 +127,71 @@ static bool find_slave(struct sdw_bus *bus,
return true; return true;
} }
struct sdw_acpi_child_walk_data {
struct sdw_bus *bus;
struct acpi_device *adev;
struct sdw_slave_id id;
bool ignore_unique_id;
};
static int sdw_acpi_check_duplicate(struct acpi_device *adev, void *data)
{
struct sdw_acpi_child_walk_data *cwd = data;
struct sdw_bus *bus = cwd->bus;
struct sdw_slave_id id;
if (adev == cwd->adev)
return 0;
if (!find_slave(bus, adev, &id))
return 0;
if (cwd->id.sdw_version != id.sdw_version || cwd->id.mfg_id != id.mfg_id ||
cwd->id.part_id != id.part_id || cwd->id.class_id != id.class_id)
return 0;
if (cwd->id.unique_id != id.unique_id) {
dev_dbg(bus->dev,
"Valid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x\n",
cwd->id.unique_id, id.unique_id, cwd->id.mfg_id,
cwd->id.part_id);
cwd->ignore_unique_id = false;
return 0;
}
dev_err(bus->dev,
"Invalid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x\n",
cwd->id.unique_id, id.unique_id, cwd->id.mfg_id, cwd->id.part_id);
return -ENODEV;
}
static int sdw_acpi_find_one(struct acpi_device *adev, void *data)
{
struct sdw_bus *bus = data;
struct sdw_acpi_child_walk_data cwd = {
.bus = bus,
.adev = adev,
.ignore_unique_id = true,
};
int ret;
if (!find_slave(bus, adev, &cwd.id))
return 0;
/* Brute-force O(N^2) search for duplicates. */
ret = acpi_dev_for_each_child(ACPI_COMPANION(bus->dev),
sdw_acpi_check_duplicate, &cwd);
if (ret)
return ret;
if (cwd.ignore_unique_id)
cwd.id.unique_id = SDW_IGNORED_UNIQUE_ID;
/* Ignore errors and continue. */
sdw_slave_add(bus, &cwd.id, acpi_fwnode_handle(adev));
return 0;
}
/* /*
* sdw_acpi_find_slaves() - Find Slave devices in Master ACPI node * sdw_acpi_find_slaves() - Find Slave devices in Master ACPI node
* @bus: SDW bus instance * @bus: SDW bus instance
@ -135,8 +200,7 @@ static bool find_slave(struct sdw_bus *bus,
*/ */
int sdw_acpi_find_slaves(struct sdw_bus *bus) int sdw_acpi_find_slaves(struct sdw_bus *bus)
{ {
struct acpi_device *adev, *parent; struct acpi_device *parent;
struct acpi_device *adev2, *parent2;
parent = ACPI_COMPANION(bus->dev); parent = ACPI_COMPANION(bus->dev);
if (!parent) { if (!parent) {
@ -144,54 +208,7 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus)
return -ENODEV; return -ENODEV;
} }
list_for_each_entry(adev, &parent->children, node) { return acpi_dev_for_each_child(parent, sdw_acpi_find_one, bus);
struct sdw_slave_id id;
struct sdw_slave_id id2;
bool ignore_unique_id = true;
if (!find_slave(bus, adev, &id))
continue;
/* brute-force O(N^2) search for duplicates */
parent2 = parent;
list_for_each_entry(adev2, &parent2->children, node) {
if (adev == adev2)
continue;
if (!find_slave(bus, adev2, &id2))
continue;
if (id.sdw_version != id2.sdw_version ||
id.mfg_id != id2.mfg_id ||
id.part_id != id2.part_id ||
id.class_id != id2.class_id)
continue;
if (id.unique_id != id2.unique_id) {
dev_dbg(bus->dev,
"Valid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x\n",
id.unique_id, id2.unique_id, id.mfg_id, id.part_id);
ignore_unique_id = false;
} else {
dev_err(bus->dev,
"Invalid unique IDs 0x%x 0x%x for Slave mfg_id 0x%04x, part_id 0x%04x\n",
id.unique_id, id2.unique_id, id.mfg_id, id.part_id);
return -ENODEV;
}
}
if (ignore_unique_id)
id.unique_id = SDW_IGNORED_UNIQUE_ID;
/*
* don't error check for sdw_slave_add as we want to continue
* adding Slaves
*/
sdw_slave_add(bus, &id, acpi_fwnode_handle(adev));
}
return 0;
} }
#endif #endif

View file

@ -301,37 +301,22 @@ static bool tb_acpi_bus_match(struct device *dev)
return tb_is_switch(dev) || tb_is_usb4_port_device(dev); return tb_is_switch(dev) || tb_is_usb4_port_device(dev);
} }
static struct acpi_device *tb_acpi_find_port(struct acpi_device *adev,
const struct tb_port *port)
{
struct acpi_device *port_adev;
if (!adev)
return NULL;
/*
* Device routers exists under the downstream facing USB4 port
* of the parent router. Their _ADR is always 0.
*/
list_for_each_entry(port_adev, &adev->children, node) {
if (acpi_device_adr(port_adev) == port->port)
return port_adev;
}
return NULL;
}
static struct acpi_device *tb_acpi_switch_find_companion(struct tb_switch *sw) static struct acpi_device *tb_acpi_switch_find_companion(struct tb_switch *sw)
{ {
struct acpi_device *adev = NULL; struct acpi_device *adev = NULL;
struct tb_switch *parent_sw; struct tb_switch *parent_sw;
/*
* Device routers exists under the downstream facing USB4 port
* of the parent router. Their _ADR is always 0.
*/
parent_sw = tb_switch_parent(sw); parent_sw = tb_switch_parent(sw);
if (parent_sw) { if (parent_sw) {
struct tb_port *port = tb_port_at(tb_route(sw), parent_sw); struct tb_port *port = tb_port_at(tb_route(sw), parent_sw);
struct acpi_device *port_adev; struct acpi_device *port_adev;
port_adev = tb_acpi_find_port(ACPI_COMPANION(&parent_sw->dev), port); port_adev = acpi_find_child_by_adr(ACPI_COMPANION(&parent_sw->dev),
port->port);
if (port_adev) if (port_adev)
adev = acpi_find_child_device(port_adev, 0, false); adev = acpi_find_child_device(port_adev, 0, false);
} else { } else {
@ -364,8 +349,8 @@ static struct acpi_device *tb_acpi_find_companion(struct device *dev)
if (tb_is_switch(dev)) if (tb_is_switch(dev))
return tb_acpi_switch_find_companion(tb_to_switch(dev)); return tb_acpi_switch_find_companion(tb_to_switch(dev));
else if (tb_is_usb4_port_device(dev)) else if (tb_is_usb4_port_device(dev))
return tb_acpi_find_port(ACPI_COMPANION(dev->parent), return acpi_find_child_by_adr(ACPI_COMPANION(dev->parent),
tb_to_usb4_port_device(dev)->port); tb_to_usb4_port_device(dev)->port->port);
return NULL; return NULL;
} }

View file

@ -124,22 +124,6 @@ static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
*/ */
#define USB_ACPI_LOCATION_VALID (1 << 31) #define USB_ACPI_LOCATION_VALID (1 << 31)
static struct acpi_device *usb_acpi_find_port(struct acpi_device *parent,
int raw)
{
struct acpi_device *adev;
if (!parent)
return NULL;
list_for_each_entry(adev, &parent->children, node) {
if (acpi_device_adr(adev) == raw)
return adev;
}
return acpi_find_child_device(parent, raw, false);
}
static struct acpi_device * static struct acpi_device *
usb_acpi_get_companion_for_port(struct usb_port *port_dev) usb_acpi_get_companion_for_port(struct usb_port *port_dev)
{ {
@ -170,7 +154,7 @@ usb_acpi_get_companion_for_port(struct usb_port *port_dev)
port1 = port_dev->portnum; port1 = port_dev->portnum;
} }
return usb_acpi_find_port(adev, port1); return acpi_find_child_by_adr(adev, port1);
} }
static struct acpi_device * static struct acpi_device *

View file

@ -365,8 +365,6 @@ struct acpi_device {
acpi_handle handle; /* no handle for fixed hardware */ acpi_handle handle; /* no handle for fixed hardware */
struct fwnode_handle fwnode; struct fwnode_handle fwnode;
struct acpi_device *parent; struct acpi_device *parent;
struct list_head children;
struct list_head node;
struct list_head wakeup_list; struct list_head wakeup_list;
struct list_head del_list; struct list_head del_list;
struct acpi_device_status status; struct acpi_device_status status;
@ -379,7 +377,6 @@ struct acpi_device {
struct acpi_device_data data; struct acpi_device_data data;
struct acpi_scan_handler *handler; struct acpi_scan_handler *handler;
struct acpi_hotplug_context *hp; struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
const struct acpi_gpio_mapping *driver_gpios; const struct acpi_gpio_mapping *driver_gpios;
void *driver_data; void *driver_data;
struct device dev; struct device dev;
@ -483,6 +480,9 @@ extern struct bus_type acpi_bus_type;
int acpi_bus_for_each_dev(int (*fn)(struct device *, void *), void *data); int acpi_bus_for_each_dev(int (*fn)(struct device *, void *), void *data);
int acpi_dev_for_each_child(struct acpi_device *adev, int acpi_dev_for_each_child(struct acpi_device *adev,
int (*fn)(struct acpi_device *, void *), void *data); int (*fn)(struct acpi_device *, void *), void *data);
int acpi_dev_for_each_child_reverse(struct acpi_device *adev,
int (*fn)(struct acpi_device *, void *),
void *data);
/* /*
* Events * Events
@ -521,6 +521,7 @@ const char *acpi_power_state_string(int state);
int acpi_device_set_power(struct acpi_device *device, int state); int acpi_device_set_power(struct acpi_device *device, int state);
int acpi_bus_init_power(struct acpi_device *device); int acpi_bus_init_power(struct acpi_device *device);
int acpi_device_fix_up_power(struct acpi_device *device); int acpi_device_fix_up_power(struct acpi_device *device);
void acpi_device_fix_up_power_extended(struct acpi_device *adev);
int acpi_bus_update_power(acpi_handle handle, int *state_p); int acpi_bus_update_power(acpi_handle handle, int *state_p);
int acpi_device_update_power(struct acpi_device *device, int *state_p); int acpi_device_update_power(struct acpi_device *device, int *state_p);
bool acpi_bus_power_manageable(acpi_handle handle); bool acpi_bus_power_manageable(acpi_handle handle);
@ -622,6 +623,8 @@ static inline int acpi_dma_configure(struct device *dev,
} }
struct acpi_device *acpi_find_child_device(struct acpi_device *parent, struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
u64 address, bool check_children); u64 address, bool check_children);
struct acpi_device *acpi_find_child_by_adr(struct acpi_device *adev,
acpi_bus_address adr);
int acpi_is_root_bridge(acpi_handle); int acpi_is_root_bridge(acpi_handle);
struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle);

View file

@ -17,7 +17,7 @@
#include <acpi/pcc.h> #include <acpi/pcc.h>
#include <acpi/processor.h> #include <acpi/processor.h>
/* Support CPPCv2 and CPPCv3 */ /* CPPCv2 and CPPCv3 support */
#define CPPC_V2_REV 2 #define CPPC_V2_REV 2
#define CPPC_V3_REV 3 #define CPPC_V3_REV 3
#define CPPC_V2_NUM_ENT 21 #define CPPC_V2_NUM_ENT 21

View file

@ -441,9 +441,12 @@ static inline int acpi_processor_hotplug(struct acpi_processor *pr)
#endif /* CONFIG_ACPI_PROCESSOR_IDLE */ #endif /* CONFIG_ACPI_PROCESSOR_IDLE */
/* in processor_thermal.c */ /* in processor_thermal.c */
int acpi_processor_get_limit_info(struct acpi_processor *pr); int acpi_processor_thermal_init(struct acpi_processor *pr,
struct acpi_device *device);
void acpi_processor_thermal_exit(struct acpi_processor *pr,
struct acpi_device *device);
extern const struct thermal_cooling_device_ops processor_cooling_ops; extern const struct thermal_cooling_device_ops processor_cooling_ops;
#if defined(CONFIG_ACPI_CPU_FREQ_PSS) & defined(CONFIG_CPU_FREQ) #ifdef CONFIG_CPU_FREQ
void acpi_thermal_cpufreq_init(struct cpufreq_policy *policy); void acpi_thermal_cpufreq_init(struct cpufreq_policy *policy);
void acpi_thermal_cpufreq_exit(struct cpufreq_policy *policy); void acpi_thermal_cpufreq_exit(struct cpufreq_policy *policy);
#else #else
@ -455,6 +458,6 @@ static inline void acpi_thermal_cpufreq_exit(struct cpufreq_policy *policy)
{ {
return; return;
} }
#endif /* CONFIG_ACPI_CPU_FREQ_PSS */ #endif /* CONFIG_CPU_FREQ */
#endif #endif

View file

@ -6,9 +6,11 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#ifdef CONFIG_ACPI_VIOT #ifdef CONFIG_ACPI_VIOT
void __init acpi_viot_early_init(void);
void __init acpi_viot_init(void); void __init acpi_viot_init(void);
int viot_iommu_configure(struct device *dev); int viot_iommu_configure(struct device *dev);
#else #else
static inline void acpi_viot_early_init(void) {}
static inline void acpi_viot_init(void) {} static inline void acpi_viot_init(void) {}
static inline int viot_iommu_configure(struct device *dev) static inline int viot_iommu_configure(struct device *dev)
{ {