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
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
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
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
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
multiple property subsets that each may contain property subsets of its
@ -96,5 +100,4 @@ contents.
References
==========
.. [1] https://www.uefi.org/sites/default/files/resources/_DSD-device-properties-UUID.pdf
.. [2] https://www.uefi.org/sites/default/files/resources/_DSD-hierarchical-data-extension-UUID-v1.1.pdf
.. [1] https://github.com/UEFI/DSD-Guide

View File

@ -168,7 +168,7 @@ An error injection example::
0x00000008 Memory Correctable
0x00000010 Memory Uncorrectable non-fatal
# 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 1 > error_inject # Inject now

View File

@ -210,7 +210,7 @@ config ACPI_TINY_POWER_BUTTON_SIGNAL
config ACPI_VIDEO
tristate "Video"
depends on X86 && BACKLIGHT_CLASS_DEVICE
depends on BACKLIGHT_CLASS_DEVICE
depends on INPUT
select THERMAL
help
@ -255,7 +255,6 @@ config ACPI_DOCK
config ACPI_CPU_FREQ_PSS
bool
select THERMAL
config ACPI_PROCESSOR_CSTATE
def_bool y
@ -287,6 +286,7 @@ config ACPI_PROCESSOR
depends on X86 || IA64 || ARM64 || LOONGARCH
select ACPI_PROCESSOR_IDLE
select ACPI_CPU_FREQ_PSS if X86 || IA64 || LOONGARCH
select THERMAL
default y
help
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
# 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_CPU_FREQ_PSS) += processor_throttling.o \
processor_thermal.o
processor-$(CONFIG_ACPI_CPU_FREQ_PSS) += processor_throttling.o
processor-$(CONFIG_CPU_FREQ) += processor_perflib.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)
return;
if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
return;
/* Silently fail, if cpuidle attribute group is not present */
sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
&dev_attr_low_power_idle_system_residency_us.attr,
"cpuidle");
} 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 */
sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
&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)
lpt_register_clock_device();
if (IS_ERR(lpss_clk_dev))
return PTR_ERR(lpss_clk_dev);
clk_data = platform_get_drvdata(lpss_clk_dev);
if (!clk_data)
return -ENODEV;

View File

@ -1150,24 +1150,25 @@ acpi_video_get_device_type(struct acpi_video_bus *video,
return 0;
}
static int
acpi_video_bus_get_one_device(struct acpi_device *device,
struct acpi_video_bus *video)
static int acpi_video_bus_get_one_device(struct acpi_device *device, void *arg)
{
unsigned long long device_id;
int status, device_type;
struct acpi_video_device *data;
struct acpi_video_bus *video = arg;
struct acpi_video_device_attrib *attribute;
struct acpi_video_device *data;
unsigned long long device_id;
acpi_status status;
int device_type;
status =
acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
/* Some device omits _ADR, we skip them instead of fail */
status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
/* Skip devices without _ADR instead of failing. */
if (ACPI_FAILURE(status))
return 0;
goto exit;
data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
if (!data)
if (!data) {
dev_dbg(&device->dev, "Cannot attach\n");
return -ENOMEM;
}
strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
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);
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,
struct acpi_device *device)
{
int status = 0;
struct acpi_device *dev;
/*
* There are systems where video module known to work fine regardless
* 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);
list_for_each_entry(dev, &device->children, node) {
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;
return acpi_dev_for_each_child(device, acpi_video_bus_get_one_device, video);
}
/* acpi_video interface */

View File

@ -3,7 +3,7 @@
* apei-base.c - ACPI Platform Error Interface (APEI) supporting
* 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
* addition it supports error serialization and error injection.
*

View File

@ -29,16 +29,26 @@
#undef pr_fmt
#define pr_fmt(fmt) "BERT: " fmt
#define ACPI_BERT_PRINT_MAX_RECORDS 5
#define ACPI_BERT_PRINT_MAX_LEN 1024
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,
unsigned int region_len)
{
struct acpi_hest_generic_status *estatus =
(struct acpi_hest_generic_status *)region;
int remain = region_len;
int printed = 0, skipped = 0;
u32 estatus_len;
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) {
pr_err(FW_BUG "Truncated status block (length: %u).\n",
estatus_len);
return;
break;
}
/* No more error records. */
if (!estatus->block_status)
return;
break;
if (cper_estatus_check(estatus)) {
pr_err(FW_BUG "Invalid error record.\n");
return;
break;
}
pr_info_once("Error records from previous boot:\n");
if (region_len < ACPI_BERT_PRINT_MAX_LEN)
if (estatus_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);
else
pr_info_once("Max print length exceeded, table data is available at:\n"
"/sys/firmware/acpi/tables/data/BERT");
printed++;
} else {
skipped++;
}
/*
* 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;
remain -= estatus_len;
}
if (skipped)
pr_info(HW_ERR "Skipped %d error records\n", skipped);
}
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(base_addr, size, IORESOURCE_MEM, IORES_DESC_PERSISTENT_MEMORY)
!= REGION_INTERSECTS) &&
(region_intersects(base_addr, size, IORESOURCE_MEM, IORES_DESC_SOFT_RESERVED)
!= REGION_INTERSECTS) &&
!arch_is_platform_page(base_addr)))
return -EINVAL;

View File

@ -464,7 +464,6 @@ out_free:
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
{
struct acpi_device *adev;
struct acpi_driver *driver;
u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE;
bool hotplug_event = false;
@ -516,10 +515,13 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
if (!adev)
goto err;
driver = adev->driver;
if (driver && driver->ops.notify &&
(driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
driver->ops.notify(adev, type);
if (adev->dev.driver) {
struct acpi_driver *driver = to_acpi_driver(adev->dev.driver);
if (driver && driver->ops.notify &&
(driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
driver->ops.notify(adev, type);
}
if (!hotplug_event) {
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)
{
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)
@ -1032,8 +1035,6 @@ static int acpi_device_probe(struct device *dev)
if (ret)
return ret;
acpi_dev->driver = acpi_drv;
pr_debug("Driver [%s] successfully bound to device [%s]\n",
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)
acpi_drv->ops.remove(acpi_dev);
acpi_dev->driver = NULL;
acpi_dev->driver_data = NULL;
return ret;
}
@ -1059,15 +1059,14 @@ static int acpi_device_probe(struct device *dev)
static void acpi_device_remove(struct 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;
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);
}
EXPORT_SYMBOL_GPL(acpi_dev_for_each_child);
int acpi_dev_for_each_child(struct acpi_device *adev,
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);
}
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
-------------------------------------------------------------------------- */
@ -1402,6 +1414,7 @@ static int __init acpi_init(void)
pci_mmcfg_late_init();
acpi_iort_init();
acpi_viot_early_init();
acpi_hest_init();
acpi_ghes_init();
acpi_scan_init();

View File

@ -23,17 +23,18 @@ static const struct acpi_device_id container_device_ids[] = {
#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)
{
struct acpi_device *adev = ACPI_COMPANION(&cdev->dev);
struct acpi_device *child;
/* Check all of the dependent devices' physical companions. */
list_for_each_entry(child, &adev->children, node)
if (!acpi_scan_is_offline(child, false))
return -EBUSY;
return 0;
return acpi_dev_for_each_child(ACPI_COMPANION(&cdev->dev), check_offline, NULL);
}
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;
}
/* 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.
*
@ -733,7 +706,6 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
cpc_obj->type, pr->id);
goto out_free;
}
cpc_ptr->num_entries = num_ent;
/* Second entry should be revision. */
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);
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;
}
/*
* 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 */
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);
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 state;

View File

@ -376,7 +376,7 @@ eject_store(struct device *d, struct device_attribute *attr,
return -EINVAL;
if ((!acpi_device->handler || !acpi_device->handler->hotplug.enabled)
&& !acpi_device->driver)
&& !d->driver)
return -ENODEV;
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 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_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)
return AE_OK;
if (boot_ec && boot_ec_is_ecdt && EC_FLAGS_IGNORE_DSDT_GPE) {
/*
* Always inherit the GPE number setting from the ECDT
* EC.
*/
ec->gpe = boot_ec->gpe;
} else {
/* Get GPE bit assignment (EC events). */
/* TODO: Add support for _GPE returning a package */
status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
if (ACPI_SUCCESS(status))
ec->gpe = tmp;
/* Get GPE bit assignment (EC events). */
/* 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.
*/
/*
* Errors are non-fatal, allowing for ACPI Reduced Hardware
* platforms which use GpioInt instead of GPE.
*/
}
/* Use the global lock for all EC transactions? */
tmp = 0;
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 &&
ec->data_addr == boot_ec->data_addr &&
!EC_FLAGS_TRUST_DSDT_GPE) {
ec->data_addr == boot_ec->data_addr) {
/*
* Trust PNP0C09 namespace location rather than
* ECDT ID. But trust ECDT GPE rather than _GPE
* because of ASUS quirks, so do not change
* boot_ec->gpe to ec->gpe.
* Trust PNP0C09 namespace location rather than ECDT ID.
* But trust ECDT GPE rather than _GPE because of ASUS
* quirks. So do not change boot_ec->gpe to ec->gpe,
* except when the TRUST_DSDT_GPE quirk is set.
*/
boot_ec->handle = ec->handle;
if (EC_FLAGS_TRUST_DSDT_GPE)
boot_ec->gpe = ec->gpe;
acpi_handle_debug(ec->handle, "duplicated.\n");
acpi_ec_free(ec);
ec = boot_ec;
@ -1862,68 +1856,40 @@ static int ec_honor_dsdt_gpe(const struct dmi_system_id *id)
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 = {
{
ec_correct_ecdt, "MSI MS-171F", {
DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),}, NULL},
/*
* MSI MS-171F
* 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."),
DMI_MATCH(DMI_PRODUCT_NAME, "FX502VD"),}, NULL},
/*
* HP Pavilion Gaming Laptop 15-cx0xxx
* 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."),
DMI_MATCH(DMI_PRODUCT_NAME, "FX502VE"),}, NULL},
{
ec_honor_ecdt_gpe, "ASUS GL702VMK", {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "GL702VMK"),}, NULL},
{
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},
{},
/*
* Samsung hardware
* https://bugzilla.kernel.org/show_bug.cgi?id=44161
*/
.callback = ec_clear_on_resume,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
},
},
{}
};
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[] = {
{
.ident = "Thinkpad X1 Carbon 6th",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
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 = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X1 Yoga 3rd"),
},
},
{
.ident = "HP ZHAN 66 Pro",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
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_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)
{
unsigned long long sta;
acpi_status status;
if (check_children && list_empty(&adev->children))
if (check_children && !acpi_dev_has_children(adev))
return -ENODEV;
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;
}
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,
u64 address, bool check_children)
{
struct acpi_device *adev, *ret = NULL;
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;
return acpi_find_child(parent, address, check_children, true);
}
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)
{
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:
{
struct acpi_resource_irq *p = &resource->data.irq;
if (!p || !p->interrupt_count) {
if (!p->interrupt_count) {
acpi_handle_debug(handle,
"Blank _PRS IRQ resource\n");
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 =
&resource->data.extended_irq;
if (!p || !p->interrupt_count) {
if (!p->interrupt_count) {
acpi_handle_debug(handle,
"Blank _PRS EXT IRQ resource\n");
return AE_OK;
@ -182,7 +182,7 @@ static acpi_status acpi_pci_link_check_current(struct acpi_resource *resource,
case ACPI_RESOURCE_TYPE_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,
* 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 =
&resource->data.extended_irq;
if (!p || !p->interrupt_count) {
if (!p->interrupt_count) {
/*
* extended IRQ descriptors must
* 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
static int acpi_pss_perf_init(struct acpi_processor *pr,
struct acpi_device *device)
static void acpi_pss_perf_init(struct acpi_processor *pr)
{
int result = 0;
acpi_processor_ppc_has_changed(pr, 0);
acpi_processor_get_throttling_info(pr);
if (pr->flags.throttling)
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
static inline int 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) {}
static inline void acpi_pss_perf_init(struct acpi_processor *pr) {}
#endif /* CONFIG_ACPI_CPU_FREQ_PSS */
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)
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)
goto err_power_exit;
@ -239,7 +183,7 @@ static int __acpi_processor_start(struct acpi_device *device)
return 0;
result = -ENODEV;
acpi_pss_perf_exit(pr, device);
acpi_processor_thermal_exit(pr, device);
err_power_exit:
acpi_processor_power_exit(pr);
@ -277,10 +221,10 @@ static int acpi_processor_stop(struct device *dev)
return 0;
acpi_processor_power_exit(pr);
acpi_pss_perf_exit(pr, device);
acpi_cppc_processor_exit(pr);
acpi_processor_thermal_exit(pr, device);
return 0;
}

View File

@ -607,7 +607,7 @@ static DEFINE_RAW_SPINLOCK(c3_lock);
* @cx: Target state context
* @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_cx *cx,
int index)
@ -664,7 +664,7 @@ static int acpi_idle_enter_bm(struct cpuidle_driver *drv,
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 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;
}
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 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,
.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);
}
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
* @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 *child)
{
const struct acpi_device *adev = to_acpi_device_node(fwnode);
const struct list_head *head;
struct list_head *next;
struct acpi_device *adev = to_acpi_device_node(fwnode);
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;
if (list_empty(head))
goto nondev;
acpi_dev_for_each_child(adev, stop_on_next, &child_adev);
if (child_adev)
return acpi_fwnode_handle(child_adev);
if (child) {
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);
child = NULL;
}
nondev:
if (!child || is_acpi_data_node(child)) {
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;
/*

View File

@ -416,6 +416,16 @@ static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity,
{
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++) {
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;
}
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_device *child;
int error;
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");
return error;
}
list_for_each_entry(child, &adev->children, node) {
error = acpi_scan_bus_check(child);
if (error)
return error;
}
return 0;
return acpi_dev_for_each_child(adev, acpi_scan_bus_check, NULL);
}
static int acpi_generic_hotplug_event(struct acpi_device *adev, u32 type)
{
switch (type) {
case ACPI_NOTIFY_BUS_CHECK:
return acpi_scan_bus_check(adev);
return acpi_scan_bus_check(adev, NULL);
case ACPI_NOTIFY_DEVICE_CHECK:
return acpi_scan_device_check(adev);
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;
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)
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);
mutex_unlock(&acpi_device_lock);
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.
*/
INIT_LIST_HEAD(&device->children);
INIT_LIST_HEAD(&device->node);
INIT_LIST_HEAD(&device->wakeup_list);
INIT_LIST_HEAD(&device->physical_node_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);
}
if (device->parent)
list_add_tail(&device->node, &device->parent->children);
if (device->wakeup.flags.valid)
list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list);
@ -752,9 +740,6 @@ static int __acpi_device_add(struct acpi_device *device,
err:
mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
list_del(&device->wakeup_list);
err_unlock:
@ -2187,9 +2172,8 @@ static int acpi_scan_attach_handler(struct acpi_device *device)
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;
acpi_handle ejd;
int ret;
@ -2206,7 +2190,7 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
device->flags.initialized = false;
acpi_device_clear_enumerated(device);
device->flags.power_manageable = 0;
return;
return 0;
}
if (device->handler)
goto ok;
@ -2224,7 +2208,7 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass)
ret = acpi_scan_attach_handler(device);
if (ret < 0)
return;
return 0;
device->flags.match_driver = true;
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);
if (ret < 0)
return;
return 0;
if (device->pnp.type.platform_id || device->flags.enumeration_by_parent)
acpi_default_enumeration(device);
else
acpi_device_set_enumerated(device);
ok:
list_for_each_entry(child, &device->children, node)
acpi_bus_attach(child, first_pass);
ok:
acpi_dev_for_each_child(device, acpi_bus_attach, first_pass);
if (!skip && device->handler && device->handler->hotplug.notify_online)
device->handler->hotplug.notify_online(device);
return 0;
}
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);
acpi_scan_lock_acquire();
acpi_bus_attach(cdw->adev, true);
acpi_bus_attach(cdw->adev, (void *)true);
acpi_scan_lock_release();
acpi_dev_put(cdw->adev);
@ -2432,7 +2417,7 @@ int acpi_bus_scan(acpi_handle handle)
if (!device)
return -ENODEV;
acpi_bus_attach(device, true);
acpi_bus_attach(device, (void *)true);
if (!acpi_bus_scan_second_pass)
return 0;
@ -2446,25 +2431,17 @@ int acpi_bus_scan(acpi_handle handle)
acpi_bus_check_add_2, NULL, NULL,
(void **)&device);
acpi_bus_attach(device, false);
acpi_bus_attach(device, NULL);
return 0;
}
EXPORT_SYMBOL(acpi_bus_scan);
/**
* 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)
static int acpi_bus_trim_one(struct acpi_device *adev, void *not_used)
{
struct acpi_scan_handler *handler = adev->handler;
struct acpi_device *child;
list_for_each_entry_reverse(child, &adev->children, node)
acpi_bus_trim(child);
acpi_dev_for_each_child_reverse(adev, acpi_bus_trim_one, NULL);
adev->flags.match_driver = false;
if (handler) {
@ -2482,6 +2459,19 @@ void acpi_bus_trim(struct acpi_device *adev)
acpi_device_set_power(adev, ACPI_STATE_D3_COLD);
adev->flags.initialized = false;
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);

View File

@ -360,6 +360,14 @@ static const struct dmi_system_id acpisleep_dmi_table[] __initconst = {
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
* 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)
{
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);
}

View File

@ -347,6 +347,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
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,
/* Dell Vostro V131 */
@ -430,23 +438,6 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
.callback = video_detect_force_native,
.ident = "Clevo NL5xRU",
.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"),
},
},
@ -469,28 +460,60 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
{
.callback = video_detect_force_native,
.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 = {
DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"),
DMI_MATCH(DMI_BOARD_NAME, "PULSE1401"),
},
},
{
.callback = video_detect_force_native,
.ident = "Clevo NL5xNU",
.ident = "TongFang PF5NU1G",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"),
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"),
DMI_MATCH(DMI_BOARD_NAME, "PF5NU1G"),
},
},
{
.callback = video_detect_force_native,
.ident = "Clevo NL5xNU",
.ident = "TongFang PF5NU1G",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"),
DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
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
* for this do not catch.

View File

@ -248,6 +248,26 @@ err_free:
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
*
@ -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) +
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,
epid);
}

View File

@ -369,9 +369,6 @@ static int lps0_device_attach(struct acpi_device *adev,
if (lps0_device_handle)
return 0;
if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
return 0;
if (acpi_s2idle_vendor_amd()) {
/* AMD0004, AMD0005, AMDI0005:
* - 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;
acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n",
ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask);
} else if (lps0_dsm_func_mask_microsoft > 0 && !strcmp(hid, "AMDI0007")) {
} else if (lps0_dsm_func_mask_microsoft > 0 &&
(!strcmp(hid, "AMDI0007") ||
!strcmp(hid, "AMDI0008"))) {
lps0_dsm_func_mask_microsoft = -EINVAL;
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();
/*
* Use suspend-to-idle by default if the default suspend mode was not
* set from the command line.
* Use suspend-to-idle by default if ACPI_FADT_LOW_POWER_S0 is set in
* 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;
pr_info("Low-power S0 idle used by default for system suspend\n");
}
/*
* 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
* @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
* @res: double pointer to be set to the address 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
* 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,
const struct resource **res, int *num_res)
{
struct acpi_device *adev;
struct acpi_device *host;
struct acpi_device *host = to_acpi_device(adev->dev.parent);
struct resource_entry *rentry;
LIST_HEAD(resource_list);
struct resource *resources;
int count;
int i;
if (!child || !hostdev)
return -EINVAL;
host = to_acpi_device(hostdev);
adev = to_acpi_device(child);
if (!adev->status.present) {
dev_dbg(child, "device is not present\n");
dev_dbg(&adev->dev, "device is not present\n");
return -EIO;
}
if (acpi_device_enumerated(adev)) {
dev_dbg(child, "has been enumerated\n");
dev_dbg(&adev->dev, "has been enumerated\n");
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);
if (count <= 0) {
dev_dbg(child, "failed to get resources\n");
dev_dbg(&adev->dev, "failed to get resources\n");
return count ? count : -EIO;
}
@ -454,7 +447,7 @@ static int hisi_lpc_acpi_set_io_res(struct device *child,
continue;
ret = hisi_lpc_acpi_xlat_io_res(adev, host, &resources[i]);
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);
return ret;
}
@ -471,6 +464,12 @@ static int hisi_lpc_acpi_remove_subdev(struct device *dev, void *unused)
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 {
const char *hid;
const char *name;
@ -480,13 +479,92 @@ struct hisi_lpc_acpi_cell {
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);
acpi_dev_for_each_child(ACPI_COMPANION(hostdev),
hisi_lpc_acpi_clear_enumerated, NULL);
}
list_for_each_entry(child, &adev->children, node)
acpi_device_clear_enumerated(child);
static int hisi_lpc_acpi_add_child(struct acpi_device *child, void *data)
{
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)
{
struct acpi_device *adev = ACPI_COMPANION(hostdev);
struct acpi_device *child;
int ret;
/* Only consider the children of the host */
list_for_each_entry(child, &adev->children, node) {
const char *hid = acpi_device_hid(child);
const struct hisi_lpc_acpi_cell *cell;
struct platform_device *pdev;
const struct resource *res;
bool found = false;
int num_res;
ret = acpi_dev_for_each_child(ACPI_COMPANION(hostdev),
hisi_lpc_acpi_add_child, hostdev);
if (ret)
hisi_lpc_acpi_remove(hostdev);
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;
}

View File

@ -60,12 +60,29 @@ int mfd_cell_disable(struct platform_device *pdev)
EXPORT_SYMBOL(mfd_cell_disable);
#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,
struct platform_device *pdev)
{
const struct mfd_cell_acpi_match *match = cell->acpi_match;
struct acpi_device *parent, *child;
struct acpi_device *adev = NULL;
struct acpi_device *parent;
parent = ACPI_COMPANION(pdev->dev.parent);
if (!parent)
@ -83,14 +100,14 @@ static void mfd_acpi_add_device(const struct mfd_cell *cell,
if (match) {
if (match->pnpid) {
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));
list_for_each_entry(child, &parent->children, node) {
if (!acpi_match_device_ids(child, ids)) {
adev = child;
break;
}
}
acpi_dev_for_each_child(parent, match_device_ids, &wd);
adev = wd.adev;
} else {
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;
const struct sdhci_acpi_slot *slot;
struct acpi_device *device, *child;
const struct dmi_system_id *id;
struct acpi_device *device;
struct sdhci_acpi_host *c;
struct sdhci_host *host;
struct resource *iomem;
@ -796,10 +796,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
slot = sdhci_acpi_get_slot(device);
/* Power on the SDHCI controller and its children */
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);
acpi_device_fix_up_power_extended(device);
if (sdhci_acpi_byt_defer(dev))
return -EPROBE_DEFER;

View File

@ -1240,16 +1240,11 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
#ifdef CONFIG_ACPI
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);
if (!device)
return;
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);
if (device)
acpi_device_fix_up_power_extended(device);
}
#else
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
* 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)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
struct acpi_device *device, *child;
int rc;
struct acpi_device *device;
device = acpi_fetch_acpi_dev(handle);
if (!device)
return 0;
rc = 0;
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;
return acpi_dev_for_each_child(device, tpacpi_evaluate_bcl, NULL);
}

View File

@ -127,6 +127,71 @@ static bool find_slave(struct sdw_bus *bus,
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
* @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)
{
struct acpi_device *adev, *parent;
struct acpi_device *adev2, *parent2;
struct acpi_device *parent;
parent = ACPI_COMPANION(bus->dev);
if (!parent) {
@ -144,54 +208,7 @@ int sdw_acpi_find_slaves(struct sdw_bus *bus)
return -ENODEV;
}
list_for_each_entry(adev, &parent->children, node) {
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;
return acpi_dev_for_each_child(parent, sdw_acpi_find_one, bus);
}
#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);
}
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)
{
struct acpi_device *adev = NULL;
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);
if (parent_sw) {
struct tb_port *port = tb_port_at(tb_route(sw), parent_sw);
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)
adev = acpi_find_child_device(port_adev, 0, false);
} else {
@ -364,8 +349,8 @@ static struct acpi_device *tb_acpi_find_companion(struct device *dev)
if (tb_is_switch(dev))
return tb_acpi_switch_find_companion(tb_to_switch(dev));
else if (tb_is_usb4_port_device(dev))
return tb_acpi_find_port(ACPI_COMPANION(dev->parent),
tb_to_usb4_port_device(dev)->port);
return acpi_find_child_by_adr(ACPI_COMPANION(dev->parent),
tb_to_usb4_port_device(dev)->port->port);
return NULL;
}

View File

@ -124,22 +124,6 @@ out:
*/
#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 *
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;
}
return usb_acpi_find_port(adev, port1);
return acpi_find_child_by_adr(adev, port1);
}
static struct acpi_device *

View File

@ -365,8 +365,6 @@ struct acpi_device {
acpi_handle handle; /* no handle for fixed hardware */
struct fwnode_handle fwnode;
struct acpi_device *parent;
struct list_head children;
struct list_head node;
struct list_head wakeup_list;
struct list_head del_list;
struct acpi_device_status status;
@ -379,7 +377,6 @@ struct acpi_device {
struct acpi_device_data data;
struct acpi_scan_handler *handler;
struct acpi_hotplug_context *hp;
struct acpi_driver *driver;
const struct acpi_gpio_mapping *driver_gpios;
void *driver_data;
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_dev_for_each_child(struct acpi_device *adev,
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
@ -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_bus_init_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_device_update_power(struct acpi_device *device, int *state_p);
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,
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);
struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle);

View File

@ -17,7 +17,7 @@
#include <acpi/pcc.h>
#include <acpi/processor.h>
/* Support CPPCv2 and CPPCv3 */
/* CPPCv2 and CPPCv3 support */
#define CPPC_V2_REV 2
#define CPPC_V3_REV 3
#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 */
/* 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;
#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_exit(struct cpufreq_policy *policy);
#else
@ -455,6 +458,6 @@ static inline void acpi_thermal_cpufreq_exit(struct cpufreq_policy *policy)
{
return;
}
#endif /* CONFIG_ACPI_CPU_FREQ_PSS */
#endif /* CONFIG_CPU_FREQ */
#endif

View File

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