ACPI updates for 6.8-rc1

- Add CSI-2 and DisCo for Imaging support to the ACPI device
    enumeration code (Sakari Ailus, Rafael J. Wysocki).
 
  - Adjust the cpufreq thermal reduction algorithm in the ACPI processor
    driver for Tegra241 (Srikar Srimath Tirumala, Arnd Bergmann).
 
  - Make acpi_proc_quirk_mwait_check() x86-specific (Rafael J. Wysocki).
 
  - Switch over ACPI to using a threaded interrupt handler for the
    SCI (Rafael J. Wysocki).
 
  - Allow ACPI Notify () handlers to run on all CPUs and clean up the
    ACPI interface for deferred events processing (Rafael J. Wysocki).
 
  - Switch over the ACPI EC driver to using a threaded handler for the
    dedicated IRQ on systems without the EC GPE (Rafael J. Wysocki).
 
  - Adjust code using ACPICA spinlocks and the ACPI EC driver spinlock to
    keep local interrupts on (Rafael J. Wysocki).
 
  - Adjust the USB4 _OSC handshake to correctly handle cases in which
    certain types of OS control are denied by the platform (Mika
    Westerberg).
 
  - Correct and clean up the generic function for parsing ACPI data-only
    tables with array structure (Yuntao Wang).
 
  - Modify acpi_dev_uid_match() to support different types of its second
    argument and adjust its users accordingly (Raag Jadav).
 
  - Clean up code related to acpi_evaluate_reference() and ACPI device
    lists (Rafael J. Wysocki).
 
  - Use generic ACPI helpers for evaluating trip point temperature
    objects in the ACPI thermal zone driver (Rafael J. Wysockii, Arnd
    Bergmann).
 
  - Add Thermal fast Sampling Period (_TFP) support to the ACPI thermal
    zone driver (Jeff Brasen).
 
  - Modify the ACPI LPIT table handling code to avoid u32 multiplication
    overflows in state residency computations (Nikita Kiryushin).
 
  - Drop an unused helper function from the ACPI backlight (video) driver
    and add a clarifying comment to it (Hans de Goede).
 
  - Update the ACPI backlight driver to avoid using uninitialized memory
    in some cases (Nikita Kiryushin).
 
  - Add ACPI backlight quirk for the Colorful X15 AT 23 laptop (Yuluo
    Qiu).
 
  - Add support for vendor-defined error types to the ACPI APEI error
    injection code (Avadhut Naik).
 
  - Adjust APEI to properly set MF_ACTION_REQUIRED on synchronous memory
    failure events, so they are handled differently from the asynchronous
    ones (Shuai Xue).
 
  - Fix NULL pointer dereference check in the ACPI extlog driver (Prarit
    Bhargava).
 
  - Adjust the ACPI extlog driver to clear the Extended Error Log status
    when RAS_CEC handled the error (Tony Luck).
 
  - Add IRQ override quirks for some Infinity laptops and for TongFang
    GMxXGxx (David McFarland, Hans de Goede).
 
  - Clean up the ACPI NUMA code and fix it to ensure that fake_pxm is not
    the same as one of the real pxm values (Yuntao Wang).
 
  - Fix the fractional clock divider flags in the ACPI LPSS (Intel SoC)
    driver so as to prevent miscalculation of the values in the clock
    divider (Andy Shevchenko).
 
  - Adjust comments in the ACPI watchdog driver to prevent kernel-doc
    from complaining during documentation builds (Randy Dunlap).
 
  - Make the ACPI button driver send wakeup key events to user space in
    addition to power button events on systems that can be woken up by
    the power button (Ken Xue).
 
  - Adjust pnpacpi_parse_allocated_vendor() to use memcpy() on a full
    structure field (Dmitry Antipov).
 -----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmWb8asSHHJqd0Byand5
 c29ja2kubmV0AAoJEILEb/54YlRxhsQP/jfRiEP7L9WUl66PdzxSWi1u7bVUZIbs
 z07ujAFdAbvpdM1WgWVq6mSzYewAqIm0A9Koabj7zKuG4VPh0Gjvq26jrK/et65m
 RJhC/qcnZ4h/2bELf9/JE7FIQMDWBGK8gNHBBXVQOZrQYIiBzJ2xyHJ4F0AvLVW6
 GGuX/4mb00jlWGr6uot6qjBgLLxY0EowneLUuH4onEWrThoNWy7zbD34LSsKuljA
 a69UkQPetXbkX4XQYnt4K4BAnwjRQNU2DlUE9lpMtheTS70wilxrC+P0XaETeO7c
 NCm38X2aUv/hSwJ0BekBRdNEvG/WQsfRdOt9jWAkoCL3oDCZdOgfM6Eas7ZDLF2n
 RoxLk2O9UXFwaSSGBVgkRLPCVyWBNI6C8GXnVDN8f9hqIk+jmlsXaXghpzVlGS54
 +ox6fjO81zJjEBxSP5ACCTNZq3BwwHhPhygtIkTO5JQ9SPn+WYCPM0C5Lcvzoj7A
 x7cdOguddhAi4ZWcoRo2cg7qN6vVaDgDgV+ylzh7q5N4cBY4edCJLzcFFuasriN4
 j9/Uj/EgCafrnOhlTJz0iZkAbPZ6T/qa3qBfF948dtFRkztTsddmGA4xof90jfG9
 /FLXL4wSiXK7jbFeUb1OCLOVANWpjHP3pM3gmnggiI3ApcweEGilhhbgVr7FuCG8
 7qj78EUqNVbW
 =Ntzm
 -----END PGP SIGNATURE-----

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

Pull ACPI updates from Rafael Wysocki:
 "From the new features standpoint, the most significant change here is
  the addition of CSI-2 and MIPI DisCo for Imaging support to the ACPI
  device enumeration code that will allow MIPI cameras to be enumerated
  through the platform firmware on systems using ACPI.

  Also significant is the switch-over to threaded interrupt handlers for
  the ACPI SCI and the dedicated EC interrupt (on systems where the
  former is not used) which essentially allows all ACPI code to run with
  local interrupts enabled. That should improve responsiveness
  significantly on systems where multiple GPEs are enabled and the
  handling of one SCI involves many I/O address space accesses which
  previously had to be carried out in one go with disabled interrupts on
  the local CPU.

  Apart from the above, the ACPI thermal zone driver will use the
  Thermal fast Sampling Period (_TFP) object if available, which should
  allow temperature changes to be followed more accurately on some
  systems, the ACPI Notify () handlers can run on all CPUs (not just on
  CPU0), which should generally speed up the processing of events
  signaled through the ACPI SCI, and the ACPI power button driver will
  trigger wakeup key events via the input subsystem (on systems where it
  is a system wakeup device)

  In addition to that, there are the usual bunch of fixes and cleanups.

  Specifics:

   - Add CSI-2 and DisCo for Imaging support to the ACPI device
     enumeration code (Sakari Ailus, Rafael J. Wysocki)

   - Adjust the cpufreq thermal reduction algorithm in the ACPI
     processor driver for Tegra241 (Srikar Srimath Tirumala, Arnd
     Bergmann)

   - Make acpi_proc_quirk_mwait_check() x86-specific (Rafael J. Wysocki)

   - Switch over ACPI to using a threaded interrupt handler for the SCI
     (Rafael J. Wysocki)

   - Allow ACPI Notify () handlers to run on all CPUs and clean up the
     ACPI interface for deferred events processing (Rafael J. Wysocki)

   - Switch over the ACPI EC driver to using a threaded handler for the
     dedicated IRQ on systems without the EC GPE (Rafael J. Wysocki)

   - Adjust code using ACPICA spinlocks and the ACPI EC driver spinlock
     to keep local interrupts on (Rafael J. Wysocki)

   - Adjust the USB4 _OSC handshake to correctly handle cases in which
     certain types of OS control are denied by the platform (Mika
     Westerberg)

   - Correct and clean up the generic function for parsing ACPI
     data-only tables with array structure (Yuntao Wang)

   - Modify acpi_dev_uid_match() to support different types of its
     second argument and adjust its users accordingly (Raag Jadav)

   - Clean up code related to acpi_evaluate_reference() and ACPI device
     lists (Rafael J. Wysocki)

   - Use generic ACPI helpers for evaluating trip point temperature
     objects in the ACPI thermal zone driver (Rafael J. Wysockii, Arnd
     Bergmann)

   - Add Thermal fast Sampling Period (_TFP) support to the ACPI thermal
     zone driver (Jeff Brasen)

   - Modify the ACPI LPIT table handling code to avoid u32
     multiplication overflows in state residency computations (Nikita
     Kiryushin)

   - Drop an unused helper function from the ACPI backlight (video)
     driver and add a clarifying comment to it (Hans de Goede)

   - Update the ACPI backlight driver to avoid using uninitialized
     memory in some cases (Nikita Kiryushin)

   - Add ACPI backlight quirk for the Colorful X15 AT 23 laptop (Yuluo
     Qiu)

   - Add support for vendor-defined error types to the ACPI APEI error
     injection code (Avadhut Naik)

   - Adjust APEI to properly set MF_ACTION_REQUIRED on synchronous
     memory failure events, so they are handled differently from the
     asynchronous ones (Shuai Xue)

   - Fix NULL pointer dereference check in the ACPI extlog driver
     (Prarit Bhargava)

   - Adjust the ACPI extlog driver to clear the Extended Error Log
     status when RAS_CEC handled the error (Tony Luck)

   - Add IRQ override quirks for some Infinity laptops and for TongFang
     GMxXGxx (David McFarland, Hans de Goede)

   - Clean up the ACPI NUMA code and fix it to ensure that fake_pxm is
     not the same as one of the real pxm values (Yuntao Wang)

   - Fix the fractional clock divider flags in the ACPI LPSS (Intel SoC)
     driver so as to prevent miscalculation of the values in the clock
     divider (Andy Shevchenko)

   - Adjust comments in the ACPI watchdog driver to prevent kernel-doc
     from complaining during documentation builds (Randy Dunlap)

   - Make the ACPI button driver send wakeup key events to user space in
     addition to power button events on systems that can be woken up by
     the power button (Ken Xue)

   - Adjust pnpacpi_parse_allocated_vendor() to use memcpy() on a full
     structure field (Dmitry Antipov)"

* tag 'acpi-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (56 commits)
  ACPI: resource: Add Infinity laptops to irq1_edge_low_force_override
  ACPI: button: trigger wakeup key events
  ACPI: resource: Add another DMI match for the TongFang GMxXGxx
  ACPI: EC: Use a spin lock without disabing interrupts
  ACPI: EC: Use a threaded handler for dedicated IRQ
  ACPI: OSL: Use spin locks without disabling interrupts
  ACPI: APEI: set memory failure flags as MF_ACTION_REQUIRED on synchronous events
  ACPI: utils: Introduce helper for _DEP list lookup
  ACPI: utils: Fix white space in struct acpi_handle_list definition
  ACPI: utils: Refine acpi_handle_list_equal() slightly
  ACPI: utils: Return bool from acpi_evaluate_reference()
  ACPI: utils: Rearrange in acpi_evaluate_reference()
  ACPI: arm64: export acpi_arch_thermal_cpufreq_pctg()
  ACPI: extlog: Clear Extended Error Log status when RAS_CEC handled the error
  ACPI: LPSS: Fix the fractional clock divider flags
  ACPI: NUMA: Fix the logic of getting the fake_pxm value
  ACPI: NUMA: Optimize the check for the availability of node values
  ACPI: NUMA: Remove unnecessary check in acpi_parse_gi_affinity()
  ACPI: watchdog: fix kernel-doc warnings
  ACPI: extlog: fix NULL pointer dereference check
  ...
This commit is contained in:
Linus Torvalds 2024-01-09 16:12:44 -08:00
commit bd012f3a5b
41 changed files with 1603 additions and 554 deletions

View File

@ -61,6 +61,10 @@ config ACPI_CCA_REQUIRED
config ACPI_TABLE_LIB
bool
config ACPI_THERMAL_LIB
depends on THERMAL
bool
config ACPI_DEBUGGER
bool "AML debugger interface"
select ACPI_DEBUG
@ -327,6 +331,7 @@ config ACPI_THERMAL
tristate "Thermal Zone"
depends on ACPI_PROCESSOR
select THERMAL
select ACPI_THERMAL_LIB
default y
help
This driver supports ACPI thermal zones. Most mobile and

View File

@ -37,7 +37,7 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o
# ACPI Bus and Device Drivers
#
acpi-y += bus.o glue.o
acpi-y += scan.o
acpi-y += scan.o mipi-disco-img.o
acpi-y += resource.o
acpi-y += acpi_processor.o
acpi-y += processor_core.o
@ -89,6 +89,7 @@ obj-$(CONFIG_ACPI_TAD) += acpi_tad.o
obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
obj-$(CONFIG_ACPI) += container.o
obj-$(CONFIG_ACPI_THERMAL_LIB) += thermal_lib.o
obj-$(CONFIG_ACPI_THERMAL) += thermal.o
obj-$(CONFIG_ACPI_PLATFORM_PROFILE) += platform_profile.o
obj-$(CONFIG_ACPI_NFIT) += nfit/

View File

@ -145,9 +145,14 @@ static int extlog_print(struct notifier_block *nb, unsigned long val,
static u32 err_seq;
estatus = extlog_elog_entry_check(cpu, bank);
if (estatus == NULL || (mce->kflags & MCE_HANDLED_CEC))
if (!estatus)
return NOTIFY_DONE;
if (mce->kflags & MCE_HANDLED_CEC) {
estatus->block_status = 0;
return NOTIFY_DONE;
}
memcpy(elog_buf, (void *)estatus, ELOG_ENTRY_LEN);
/* clear record status to enable BIOS to update it again */
estatus->block_status = 0;
@ -303,9 +308,10 @@ err:
static void __exit extlog_exit(void)
{
mce_unregister_decode_chain(&extlog_mce_dec);
((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN;
if (extlog_l1_addr)
if (extlog_l1_addr) {
((struct extlog_l1_head *)extlog_l1_addr)->flags &= ~FLAG_OS_OPTIN;
acpi_os_unmap_iomem(extlog_l1_addr, l1_size);
}
if (elog_addr)
acpi_os_unmap_iomem(elog_addr, elog_size);
release_mem_region(elog_base, elog_size);

View File

@ -105,7 +105,7 @@ static void lpit_update_residency(struct lpit_residency_info *info,
return;
info->frequency = lpit_native->counter_frequency ?
lpit_native->counter_frequency : tsc_khz * 1000;
lpit_native->counter_frequency : mul_u32_u32(tsc_khz, 1000U);
if (!info->frequency)
info->frequency = 1;

View File

@ -167,13 +167,9 @@ static struct pwm_lookup byt_pwm_lookup[] = {
static void byt_pwm_setup(struct lpss_private_data *pdata)
{
u64 uid;
/* Only call pwm_add_table for the first PWM controller */
if (acpi_dev_uid_to_integer(pdata->adev, &uid) || uid != 1)
return;
pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup));
if (acpi_dev_uid_match(pdata->adev, 1))
pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup));
}
#define LPSS_I2C_ENABLE 0x6c
@ -218,13 +214,9 @@ static struct pwm_lookup bsw_pwm_lookup[] = {
static void bsw_pwm_setup(struct lpss_private_data *pdata)
{
u64 uid;
/* Only call pwm_add_table for the first PWM controller */
if (acpi_dev_uid_to_integer(pdata->adev, &uid) || uid != 1)
return;
pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup));
if (acpi_dev_uid_match(pdata->adev, 1))
pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup));
}
static const struct property_entry lpt_spi_properties[] = {
@ -461,8 +453,9 @@ static int register_device_clock(struct acpi_device *adev,
if (!clk_name)
return -ENOMEM;
clk = clk_register_fractional_divider(NULL, clk_name, parent,
0, prv_base, 1, 15, 16, 15,
CLK_FRAC_DIVIDER_POWER_OF_TWO_PS,
prv_base, 1, 15, 16, 15, 0, NULL);
NULL);
parent = clk_name;
clk_name = kasprintf(GFP_KERNEL, "%s-update", devname);
@ -570,34 +563,6 @@ static struct device *acpi_lpss_find_device(const char *hid, const char *uid)
return bus_find_device(&pci_bus_type, NULL, &data, match_hid_uid);
}
static bool acpi_lpss_dep(struct acpi_device *adev, acpi_handle handle)
{
struct acpi_handle_list dep_devices;
acpi_status status;
bool ret = false;
int i;
if (!acpi_has_method(adev->handle, "_DEP"))
return false;
status = acpi_evaluate_reference(adev->handle, "_DEP", NULL,
&dep_devices);
if (ACPI_FAILURE(status)) {
dev_dbg(&adev->dev, "Failed to evaluate _DEP.\n");
return false;
}
for (i = 0; i < dep_devices.count; i++) {
if (dep_devices.handles[i] == handle) {
ret = true;
break;
}
}
acpi_handle_list_free(&dep_devices);
return ret;
}
static void acpi_lpss_link_consumer(struct device *dev1,
const struct lpss_device_links *link)
{
@ -608,7 +573,7 @@ static void acpi_lpss_link_consumer(struct device *dev1,
return;
if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids))
|| acpi_lpss_dep(ACPI_COMPANION(dev2), ACPI_HANDLE(dev1)))
|| acpi_device_dep(ACPI_HANDLE(dev2), ACPI_HANDLE(dev1)))
device_link_add(dev2, dev1, link->flags);
put_device(dev2);
@ -624,7 +589,7 @@ static void acpi_lpss_link_supplier(struct device *dev1,
return;
if ((link->dep_missing_ids && dmi_check_system(link->dep_missing_ids))
|| acpi_lpss_dep(ACPI_COMPANION(dev1), ACPI_HANDLE(dev2)))
|| acpi_device_dep(ACPI_HANDLE(dev1), ACPI_HANDLE(dev2)))
device_link_add(dev1, dev2, link->flags);
put_device(dev2);

View File

@ -67,7 +67,7 @@ MODULE_PARM_DESC(hw_changes_brightness,
static bool device_id_scheme = false;
module_param(device_id_scheme, bool, 0444);
static int only_lcd = -1;
static int only_lcd;
module_param(only_lcd, int, 0444);
static bool may_report_brightness_keys;
@ -500,6 +500,15 @@ static const struct dmi_system_id video_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"),
},
},
{
.callback = video_set_report_key_events,
.driver_data = (void *)((uintptr_t)REPORT_BRIGHTNESS_KEY_EVENTS),
.ident = "COLORFUL X15 AT 23",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "COLORFUL"),
DMI_MATCH(DMI_PRODUCT_NAME, "X15 AT 23"),
},
},
/*
* Some machines change the brightness themselves when a brightness
* hotkey gets pressed, despite us telling them not to. In this case
@ -1713,12 +1722,12 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
return;
count++;
acpi_get_parent(device->dev->handle, &acpi_parent);
pdev = acpi_get_pci_dev(acpi_parent);
if (pdev) {
parent = &pdev->dev;
pci_dev_put(pdev);
if (ACPI_SUCCESS(acpi_get_parent(device->dev->handle, &acpi_parent))) {
pdev = acpi_get_pci_dev(acpi_parent);
if (pdev) {
parent = &pdev->dev;
pci_dev_put(pdev);
}
}
memset(&props, 0, sizeof(struct backlight_properties));
@ -2137,57 +2146,6 @@ static int __init intel_opregion_present(void)
return opregion;
}
/* Check if the chassis-type indicates there is no builtin LCD panel */
static bool dmi_is_desktop(void)
{
const char *chassis_type;
unsigned long type;
chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
if (!chassis_type)
return false;
if (kstrtoul(chassis_type, 10, &type) != 0)
return false;
switch (type) {
case 0x03: /* Desktop */
case 0x04: /* Low Profile Desktop */
case 0x05: /* Pizza Box */
case 0x06: /* Mini Tower */
case 0x07: /* Tower */
case 0x10: /* Lunch Box */
case 0x11: /* Main Server Chassis */
return true;
}
return false;
}
/*
* We're seeing a lot of bogus backlight interfaces on newer machines
* without a LCD such as desktops, servers and HDMI sticks. Checking the
* lcd flag fixes this, enable this by default on any machines which are:
* 1. Win8 ready (where we also prefer the native backlight driver, so
* normally the acpi_video code should not register there anyways); *and*
* 2.1 Report a desktop/server DMI chassis-type, or
* 2.2 Are an ACPI-reduced-hardware platform (and thus won't use the EC for
backlight control)
*/
static bool should_check_lcd_flag(void)
{
if (!acpi_osi_is_win8())
return false;
if (dmi_is_desktop())
return true;
if (acpi_reduced_hardware())
return true;
return false;
}
int acpi_video_register(void)
{
int ret = 0;
@ -2201,9 +2159,6 @@ int acpi_video_register(void)
goto leave;
}
if (only_lcd == -1)
only_lcd = should_check_lcd_flag();
dmi_check_system(video_dmi_table);
ret = acpi_bus_register_driver(&acpi_video_bus);

View File

@ -81,7 +81,7 @@ static const struct acpi_table_wdat *acpi_watchdog_get_wdat(void)
return wdat;
}
/**
/*
* Returns true if this system should prefer ACPI based watchdog instead of
* the native one (which are typically the same hardware).
*/

View File

@ -73,6 +73,7 @@ static u32 notrigger;
static u32 vendor_flags;
static struct debugfs_blob_wrapper vendor_blob;
static struct debugfs_blob_wrapper vendor_errors;
static char vendor_dev[64];
/*
@ -182,6 +183,21 @@ static int einj_timedout(u64 *t)
return 0;
}
static void get_oem_vendor_struct(u64 paddr, int offset,
struct vendor_error_type_extension *v)
{
unsigned long vendor_size;
u64 target_pa = paddr + offset + sizeof(struct vendor_error_type_extension);
vendor_size = v->length - sizeof(struct vendor_error_type_extension);
if (vendor_size)
vendor_errors.data = acpi_os_map_memory(target_pa, vendor_size);
if (vendor_errors.data)
vendor_errors.size = vendor_size;
}
static void check_vendor_extension(u64 paddr,
struct set_error_type_with_address *v5param)
{
@ -194,6 +210,7 @@ static void check_vendor_extension(u64 paddr,
v = acpi_os_map_iomem(paddr + offset, sizeof(*v));
if (!v)
return;
get_oem_vendor_struct(paddr, offset, v);
sbdf = v->pcie_sbdf;
sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n",
sbdf >> 24, (sbdf >> 16) & 0xff,
@ -577,38 +594,40 @@ static u64 error_param2;
static u64 error_param3;
static u64 error_param4;
static struct dentry *einj_debug_dir;
static const char * const einj_error_type_string[] = {
"0x00000001\tProcessor Correctable\n",
"0x00000002\tProcessor Uncorrectable non-fatal\n",
"0x00000004\tProcessor Uncorrectable fatal\n",
"0x00000008\tMemory Correctable\n",
"0x00000010\tMemory Uncorrectable non-fatal\n",
"0x00000020\tMemory Uncorrectable fatal\n",
"0x00000040\tPCI Express Correctable\n",
"0x00000080\tPCI Express Uncorrectable non-fatal\n",
"0x00000100\tPCI Express Uncorrectable fatal\n",
"0x00000200\tPlatform Correctable\n",
"0x00000400\tPlatform Uncorrectable non-fatal\n",
"0x00000800\tPlatform Uncorrectable fatal\n",
"0x00001000\tCXL.cache Protocol Correctable\n",
"0x00002000\tCXL.cache Protocol Uncorrectable non-fatal\n",
"0x00004000\tCXL.cache Protocol Uncorrectable fatal\n",
"0x00008000\tCXL.mem Protocol Correctable\n",
"0x00010000\tCXL.mem Protocol Uncorrectable non-fatal\n",
"0x00020000\tCXL.mem Protocol Uncorrectable fatal\n",
static struct { u32 mask; const char *str; } const einj_error_type_string[] = {
{ BIT(0), "Processor Correctable" },
{ BIT(1), "Processor Uncorrectable non-fatal" },
{ BIT(2), "Processor Uncorrectable fatal" },
{ BIT(3), "Memory Correctable" },
{ BIT(4), "Memory Uncorrectable non-fatal" },
{ BIT(5), "Memory Uncorrectable fatal" },
{ BIT(6), "PCI Express Correctable" },
{ BIT(7), "PCI Express Uncorrectable non-fatal" },
{ BIT(8), "PCI Express Uncorrectable fatal" },
{ BIT(9), "Platform Correctable" },
{ BIT(10), "Platform Uncorrectable non-fatal" },
{ BIT(11), "Platform Uncorrectable fatal"},
{ BIT(12), "CXL.cache Protocol Correctable" },
{ BIT(13), "CXL.cache Protocol Uncorrectable non-fatal" },
{ BIT(14), "CXL.cache Protocol Uncorrectable fatal" },
{ BIT(15), "CXL.mem Protocol Correctable" },
{ BIT(16), "CXL.mem Protocol Uncorrectable non-fatal" },
{ BIT(17), "CXL.mem Protocol Uncorrectable fatal" },
{ BIT(31), "Vendor Defined Error Types" },
};
static int available_error_type_show(struct seq_file *m, void *v)
{
int rc;
u32 available_error_type = 0;
u32 error_type = 0;
rc = einj_get_available_error_type(&available_error_type);
rc = einj_get_available_error_type(&error_type);
if (rc)
return rc;
for (int pos = 0; pos < ARRAY_SIZE(einj_error_type_string); pos++)
if (available_error_type & BIT(pos))
seq_puts(m, einj_error_type_string[pos]);
if (error_type & einj_error_type_string[pos].mask)
seq_printf(m, "0x%08x\t%s\n", einj_error_type_string[pos].mask,
einj_error_type_string[pos].str);
return 0;
}
@ -767,6 +786,10 @@ static int __init einj_init(void)
einj_debug_dir, &vendor_flags);
}
if (vendor_errors.size)
debugfs_create_blob("oem_error", 0600, einj_debug_dir,
&vendor_errors);
pr_info("Error INJection is initialized.\n");
return 0;
@ -792,6 +815,8 @@ static void __exit einj_exit(void)
sizeof(struct einj_parameter);
acpi_os_unmap_iomem(einj_param, size);
if (vendor_errors.size)
acpi_os_unmap_memory(vendor_errors.data, vendor_errors.size);
}
einj_exec_ctx_init(&ctx);
apei_exec_post_unmap_gars(&ctx);

View File

@ -101,6 +101,20 @@ static inline bool is_hest_type_generic_v2(struct ghes *ghes)
return ghes->generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR_V2;
}
/*
* A platform may describe one error source for the handling of synchronous
* errors (e.g. MCE or SEA), or for handling asynchronous errors (e.g. SCI
* or External Interrupt). On x86, the HEST notifications are always
* asynchronous, so only SEA on ARM is delivered as a synchronous
* notification.
*/
static inline bool is_hest_sync_notify(struct ghes *ghes)
{
u8 notify_type = ghes->generic->notify.type;
return notify_type == ACPI_HEST_NOTIFY_SEA;
}
/*
* This driver isn't really modular, however for the time being,
* continuing to use module_param is the easiest way to remain
@ -489,7 +503,7 @@ static bool ghes_do_memory_failure(u64 physical_addr, int flags)
}
static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
int sev)
int sev, bool sync)
{
int flags = -1;
int sec_sev = ghes_severity(gdata->error_severity);
@ -503,7 +517,7 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
(gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED))
flags = MF_SOFT_OFFLINE;
if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE)
flags = 0;
flags = sync ? MF_ACTION_REQUIRED : 0;
if (flags != -1)
return ghes_do_memory_failure(mem_err->physical_addr, flags);
@ -511,9 +525,11 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
return false;
}
static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, int sev)
static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata,
int sev, bool sync)
{
struct cper_sec_proc_arm *err = acpi_hest_get_payload(gdata);
int flags = sync ? MF_ACTION_REQUIRED : 0;
bool queued = false;
int sec_sev, i;
char *p;
@ -538,7 +554,7 @@ static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata, int s
* and don't filter out 'corrected' error here.
*/
if (is_cache && has_pa) {
queued = ghes_do_memory_failure(err_info->physical_fault_addr, 0);
queued = ghes_do_memory_failure(err_info->physical_fault_addr, flags);
p += err_info->length;
continue;
}
@ -666,6 +682,7 @@ static bool ghes_do_proc(struct ghes *ghes,
const guid_t *fru_id = &guid_null;
char *fru_text = "";
bool queued = false;
bool sync = is_hest_sync_notify(ghes);
sev = ghes_severity(estatus->error_severity);
apei_estatus_for_each_section(estatus, gdata) {
@ -683,13 +700,13 @@ static bool ghes_do_proc(struct ghes *ghes,
atomic_notifier_call_chain(&ghes_report_chain, sev, mem_err);
arch_apei_report_mem_error(sev, mem_err);
queued = ghes_handle_memory_failure(gdata, sev);
queued = ghes_handle_memory_failure(gdata, sev, sync);
}
else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
ghes_handle_aer(gdata);
}
else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
queued = ghes_handle_arm_hw_error(gdata, sev);
queued = ghes_handle_arm_hw_error(gdata, sev, sync);
} else {
void *err = acpi_hest_get_payload(gdata);

View File

@ -5,3 +5,4 @@ obj-$(CONFIG_ACPI_GTDT) += gtdt.o
obj-$(CONFIG_ACPI_APMT) += apmt.o
obj-$(CONFIG_ARM_AMBA) += amba.o
obj-y += dma.o init.o
obj-y += thermal_cpufreq.o

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/acpi.h>
#include <linux/export.h>
#include "../internal.h"
#define SMCCC_SOC_ID_T241 0x036b0241
int acpi_arch_thermal_cpufreq_pctg(void)
{
s32 soc_id = arm_smccc_get_soc_id_version();
/*
* Check JEP106 code for NVIDIA Tegra241 chip (036b:0241) and
* reduce the CPUFREQ Thermal reduction percentage to 5%.
*/
if (soc_id == SMCCC_SOC_ID_T241)
return 5;
return 0;
}
EXPORT_SYMBOL_GPL(acpi_arch_thermal_cpufreq_pctg);

View File

@ -408,7 +408,7 @@ static void acpi_bus_decode_usb_osc(const char *msg, u32 bits)
static u8 sb_usb_uuid_str[] = "23A0D13A-26AB-486C-9C5F-0FFA525A575A";
static void acpi_bus_osc_negotiate_usb_control(void)
{
u32 capbuf[3];
u32 capbuf[3], *capbuf_ret;
struct acpi_osc_context context = {
.uuid_str = sb_usb_uuid_str,
.rev = 1,
@ -428,7 +428,12 @@ static void acpi_bus_osc_negotiate_usb_control(void)
control = OSC_USB_USB3_TUNNELING | OSC_USB_DP_TUNNELING |
OSC_USB_PCIE_TUNNELING | OSC_USB_XDOMAIN;
capbuf[OSC_QUERY_DWORD] = 0;
/*
* Run _OSC first with query bit set, trying to get control over
* all tunneling. The platform can then clear out bits in the
* control dword that it does not want to grant to the OS.
*/
capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE;
capbuf[OSC_SUPPORT_DWORD] = 0;
capbuf[OSC_CONTROL_DWORD] = control;
@ -441,8 +446,29 @@ static void acpi_bus_osc_negotiate_usb_control(void)
goto out_free;
}
/*
* Run _OSC again now with query bit clear and the control dword
* matching what the platform granted (which may not have all
* the control bits set).
*/
capbuf_ret = context.ret.pointer;
capbuf[OSC_QUERY_DWORD] = 0;
capbuf[OSC_CONTROL_DWORD] = capbuf_ret[OSC_CONTROL_DWORD];
kfree(context.ret.pointer);
status = acpi_run_osc(handle, &context);
if (ACPI_FAILURE(status))
return;
if (context.ret.length != sizeof(capbuf)) {
pr_info("USB4 _OSC: returned invalid length buffer\n");
goto out_free;
}
osc_sb_native_usb4_control =
control & acpi_osc_ctx_get_pci_control(&context);
control & acpi_osc_ctx_get_pci_control(&context);
acpi_bus_decode_usb_osc("USB4 _OSC: OS supports", control);
acpi_bus_decode_usb_osc("USB4 _OSC: OS controls",

View File

@ -480,6 +480,7 @@ static int acpi_button_suspend(struct device *dev)
static int acpi_button_resume(struct device *dev)
{
struct input_dev *input;
struct acpi_device *device = to_acpi_device(dev);
struct acpi_button *button = acpi_driver_data(device);
@ -489,6 +490,14 @@ static int acpi_button_resume(struct device *dev)
button->last_time = ktime_get();
acpi_lid_initialize_state(device);
}
if (button->type == ACPI_BUTTON_TYPE_POWER) {
input = button->input;
input_report_key(input, KEY_WAKEUP, 1);
input_sync(input);
input_report_key(input, KEY_WAKEUP, 0);
input_sync(input);
}
return 0;
}
#endif
@ -579,6 +588,7 @@ static int acpi_button_add(struct acpi_device *device)
switch (button->type) {
case ACPI_BUTTON_TYPE_POWER:
input_set_capability(input, EV_KEY, KEY_POWER);
input_set_capability(input, EV_KEY, KEY_WAKEUP);
break;
case ACPI_BUTTON_TYPE_SLEEP:

View File

@ -525,12 +525,10 @@ static void acpi_ec_clear(struct acpi_ec *ec)
static void acpi_ec_enable_event(struct acpi_ec *ec)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
if (acpi_ec_started(ec))
__acpi_ec_enable_event(ec);
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
/* Drain additional events if hardware requires that */
if (EC_FLAGS_CLEAR_ON_RESUME)
@ -546,11 +544,9 @@ static void __acpi_ec_flush_work(void)
static void acpi_ec_disable_event(struct acpi_ec *ec)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
__acpi_ec_disable_event(ec);
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
/*
* When ec_freeze_events is true, we need to flush events in
@ -571,10 +567,9 @@ void acpi_ec_flush_work(void)
static bool acpi_ec_guard_event(struct acpi_ec *ec)
{
unsigned long flags;
bool guarded;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
/*
* If firmware SCI_EVT clearing timing is "event", we actually
* don't know when the SCI_EVT will be cleared by firmware after
@ -590,31 +585,29 @@ static bool acpi_ec_guard_event(struct acpi_ec *ec)
guarded = ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT &&
ec->event_state != EC_EVENT_READY &&
(!ec->curr || ec->curr->command != ACPI_EC_COMMAND_QUERY);
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
return guarded;
}
static int ec_transaction_polled(struct acpi_ec *ec)
{
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_POLL))
ret = 1;
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
return ret;
}
static int ec_transaction_completed(struct acpi_ec *ec)
{
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE))
ret = 1;
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
return ret;
}
@ -756,7 +749,6 @@ static int ec_guard(struct acpi_ec *ec)
static int ec_poll(struct acpi_ec *ec)
{
unsigned long flags;
int repeat = 5; /* number of command restarts */
while (repeat--) {
@ -765,14 +757,14 @@ static int ec_poll(struct acpi_ec *ec)
do {
if (!ec_guard(ec))
return 0;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
advance_transaction(ec, false);
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
} while (time_before(jiffies, delay));
pr_debug("controller reset, restart transaction\n");
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
start_transaction(ec);
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
}
return -ETIME;
}
@ -780,11 +772,10 @@ static int ec_poll(struct acpi_ec *ec)
static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
struct transaction *t)
{
unsigned long tmp;
int ret = 0;
/* start transaction */
spin_lock_irqsave(&ec->lock, tmp);
spin_lock(&ec->lock);
/* Enable GPE for command processing (IBF=0/OBF=1) */
if (!acpi_ec_submit_flushable_request(ec)) {
ret = -EINVAL;
@ -795,11 +786,11 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
ec->curr = t;
ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command));
start_transaction(ec);
spin_unlock_irqrestore(&ec->lock, tmp);
spin_unlock(&ec->lock);
ret = ec_poll(ec);
spin_lock_irqsave(&ec->lock, tmp);
spin_lock(&ec->lock);
if (t->irq_count == ec_storm_threshold)
acpi_ec_unmask_events(ec);
ec_dbg_req("Command(%s) stopped", acpi_ec_cmd_string(t->command));
@ -808,7 +799,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec,
acpi_ec_complete_request(ec);
ec_dbg_ref(ec, "Decrease command");
unlock:
spin_unlock_irqrestore(&ec->lock, tmp);
spin_unlock(&ec->lock);
return ret;
}
@ -936,9 +927,7 @@ EXPORT_SYMBOL(ec_get_handle);
static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) {
ec_dbg_drv("Starting EC");
/* Enable GPE for event processing (SCI_EVT=1) */
@ -948,31 +937,28 @@ static void acpi_ec_start(struct acpi_ec *ec, bool resuming)
}
ec_log_drv("EC started");
}
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
}
static bool acpi_ec_stopped(struct acpi_ec *ec)
{
unsigned long flags;
bool flushed;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
flushed = acpi_ec_flushed(ec);
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
return flushed;
}
static void acpi_ec_stop(struct acpi_ec *ec, bool suspending)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
if (acpi_ec_started(ec)) {
ec_dbg_drv("Stopping EC");
set_bit(EC_FLAGS_STOPPED, &ec->flags);
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
wait_event(ec->wait, acpi_ec_stopped(ec));
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
/* Disable GPE for event processing (SCI_EVT=1) */
if (!suspending) {
acpi_ec_complete_request(ec);
@ -983,29 +969,25 @@ static void acpi_ec_stop(struct acpi_ec *ec, bool suspending)
clear_bit(EC_FLAGS_STOPPED, &ec->flags);
ec_log_drv("EC stopped");
}
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
}
static void acpi_ec_enter_noirq(struct acpi_ec *ec)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
ec->busy_polling = true;
ec->polling_guard = 0;
ec_log_drv("interrupt blocked");
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
}
static void acpi_ec_leave_noirq(struct acpi_ec *ec)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
ec->busy_polling = ec_busy_polling;
ec->polling_guard = ec_polling_guard;
ec_log_drv("interrupt unblocked");
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
}
void acpi_ec_block_transactions(void)
@ -1137,9 +1119,9 @@ static void acpi_ec_event_processor(struct work_struct *work)
ec_dbg_evt("Query(0x%02x) stopped", handler->query_bit);
spin_lock_irq(&ec->lock);
spin_lock(&ec->lock);
ec->queries_in_progress--;
spin_unlock_irq(&ec->lock);
spin_unlock(&ec->lock);
acpi_ec_put_query_handler(handler);
kfree(q);
@ -1202,12 +1184,12 @@ static int acpi_ec_submit_query(struct acpi_ec *ec)
*/
ec_dbg_evt("Query(0x%02x) scheduled", value);
spin_lock_irq(&ec->lock);
spin_lock(&ec->lock);
ec->queries_in_progress++;
queue_work(ec_query_wq, &q->work);
spin_unlock_irq(&ec->lock);
spin_unlock(&ec->lock);
return 0;
@ -1223,14 +1205,14 @@ static void acpi_ec_event_handler(struct work_struct *work)
ec_dbg_evt("Event started");
spin_lock_irq(&ec->lock);
spin_lock(&ec->lock);
while (ec->events_to_process) {
spin_unlock_irq(&ec->lock);
spin_unlock(&ec->lock);
acpi_ec_submit_query(ec);
spin_lock_irq(&ec->lock);
spin_lock(&ec->lock);
ec->events_to_process--;
}
@ -1247,11 +1229,11 @@ static void acpi_ec_event_handler(struct work_struct *work)
ec_dbg_evt("Event stopped");
spin_unlock_irq(&ec->lock);
spin_unlock(&ec->lock);
guard_timeout = !!ec_guard(ec);
spin_lock_irq(&ec->lock);
spin_lock(&ec->lock);
/* Take care of SCI_EVT unless someone else is doing that. */
if (guard_timeout && !ec->curr)
@ -1264,7 +1246,7 @@ static void acpi_ec_event_handler(struct work_struct *work)
ec->events_in_progress--;
spin_unlock_irq(&ec->lock);
spin_unlock(&ec->lock);
}
static void clear_gpe_and_advance_transaction(struct acpi_ec *ec, bool interrupt)
@ -1289,13 +1271,11 @@ static void clear_gpe_and_advance_transaction(struct acpi_ec *ec, bool interrupt
static void acpi_ec_handle_interrupt(struct acpi_ec *ec)
{
unsigned long flags;
spin_lock_irqsave(&ec->lock, flags);
spin_lock(&ec->lock);
clear_gpe_and_advance_transaction(ec, true);
spin_unlock_irqrestore(&ec->lock, flags);
spin_unlock(&ec->lock);
}
static u32 acpi_ec_gpe_handler(acpi_handle gpe_device,
@ -1458,8 +1438,8 @@ static bool install_gpe_event_handler(struct acpi_ec *ec)
static bool install_gpio_irq_event_handler(struct acpi_ec *ec)
{
return request_irq(ec->irq, acpi_ec_irq_handler, IRQF_SHARED,
"ACPI EC", ec) >= 0;
return request_threaded_irq(ec->irq, NULL, acpi_ec_irq_handler,
IRQF_SHARED | IRQF_ONESHOT, "ACPI EC", ec) >= 0;
}
/**
@ -2105,7 +2085,7 @@ bool acpi_ec_dispatch_gpe(void)
* Dispatch the EC GPE in-band, but do not report wakeup in any case
* to allow the caller to process events properly after that.
*/
spin_lock_irq(&first_ec->lock);
spin_lock(&first_ec->lock);
if (acpi_ec_gpe_status_set(first_ec)) {
pm_pr_dbg("ACPI EC GPE status set\n");
@ -2114,7 +2094,7 @@ bool acpi_ec_dispatch_gpe(void)
work_in_progress = acpi_ec_work_in_progress(first_ec);
}
spin_unlock_irq(&first_ec->lock);
spin_unlock(&first_ec->lock);
if (!work_in_progress)
return false;
@ -2127,11 +2107,11 @@ bool acpi_ec_dispatch_gpe(void)
pm_pr_dbg("ACPI EC work flushed\n");
spin_lock_irq(&first_ec->lock);
spin_lock(&first_ec->lock);
work_in_progress = acpi_ec_work_in_progress(first_ec);
spin_unlock_irq(&first_ec->lock);
spin_unlock(&first_ec->lock);
} while (work_in_progress && !pm_wakeup_pending());
return false;

View File

@ -85,6 +85,20 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent);
acpi_status acpi_sysfs_table_handler(u32 event, void *table, void *context);
void acpi_scan_table_notify(void);
int acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp);
int acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp);
int acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp);
int acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp);
#ifdef CONFIG_ARM64
int acpi_arch_thermal_cpufreq_pctg(void);
#else
static inline int acpi_arch_thermal_cpufreq_pctg(void)
{
return 0;
}
#endif
/* --------------------------------------------------------------------------
Device Node Initialization / Removal
-------------------------------------------------------------------------- */
@ -148,8 +162,11 @@ int acpi_wakeup_device_init(void);
#ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC
void acpi_early_processor_control_setup(void);
void acpi_early_processor_set_pdc(void);
#ifdef CONFIG_X86
void acpi_proc_quirk_mwait_check(void);
#else
static inline void acpi_proc_quirk_mwait_check(void) {}
#endif
bool processor_physically_present(acpi_handle handle);
#else
static inline void acpi_early_processor_control_setup(void) {}
@ -276,4 +293,13 @@ void acpi_init_lpit(void);
static inline void acpi_init_lpit(void) { }
#endif
/*--------------------------------------------------------------------------
ACPI _CRS CSI-2 and MIPI DisCo for Imaging
-------------------------------------------------------------------------- */
void acpi_mipi_check_crs_csi2(acpi_handle handle);
void acpi_mipi_scan_crs_csi2(void);
void acpi_mipi_init_crs_csi2_swnodes(void);
void acpi_mipi_crs_csi2_cleanup(void);
#endif /* _ACPI_INTERNAL_H_ */

View File

@ -0,0 +1,725 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* MIPI DisCo for Imaging support.
*
* Copyright (C) 2023 Intel Corporation
*
* Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI-2 records defined in
* Section 6.4.3.8.2.4 "Camera Serial Interface (CSI-2) Connection Resource
* Descriptor" of ACPI 6.5 and using device properties defined by the MIPI DisCo
* for Imaging specification.
*
* The implementation looks for the information in the ACPI namespace (CSI-2
* resource descriptors in _CRS) and constructs software nodes compatible with
* Documentation/firmware-guide/acpi/dsd/graph.rst to represent the CSI-2
* connection graph. The software nodes are then populated with the data
* extracted from the _CRS CSI-2 resource descriptors and the MIPI DisCo
* for Imaging device properties present in _DSD for the ACPI device objects
* with CSI-2 connections.
*/
#include <linux/acpi.h>
#include <linux/limits.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <media/v4l2-fwnode.h>
#include "internal.h"
static LIST_HEAD(acpi_mipi_crs_csi2_list);
static void acpi_mipi_data_tag(acpi_handle handle, void *context)
{
}
/* Connection data extracted from one _CRS CSI-2 resource descriptor. */
struct crs_csi2_connection {
struct list_head entry;
struct acpi_resource_csi2_serialbus csi2_data;
acpi_handle remote_handle;
char remote_name[];
};
/* Data extracted from _CRS CSI-2 resource descriptors for one device. */
struct crs_csi2 {
struct list_head entry;
acpi_handle handle;
struct acpi_device_software_nodes *swnodes;
struct list_head connections;
u32 port_count;
};
struct csi2_resources_walk_data {
acpi_handle handle;
struct list_head connections;
};
static acpi_status parse_csi2_resource(struct acpi_resource *res, void *context)
{
struct csi2_resources_walk_data *crwd = context;
struct acpi_resource_csi2_serialbus *csi2_res;
struct acpi_resource_source *csi2_res_src;
u16 csi2_res_src_length;
struct crs_csi2_connection *conn;
acpi_handle remote_handle;
if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
return AE_OK;
csi2_res = &res->data.csi2_serial_bus;
if (csi2_res->type != ACPI_RESOURCE_SERIAL_TYPE_CSI2)
return AE_OK;
csi2_res_src = &csi2_res->resource_source;
if (ACPI_FAILURE(acpi_get_handle(NULL, csi2_res_src->string_ptr,
&remote_handle))) {
acpi_handle_debug(crwd->handle,
"unable to find resource source\n");
return AE_OK;
}
csi2_res_src_length = csi2_res_src->string_length;
if (!csi2_res_src_length) {
acpi_handle_debug(crwd->handle,
"invalid resource source string length\n");
return AE_OK;
}
conn = kmalloc(struct_size(conn, remote_name, csi2_res_src_length + 1),
GFP_KERNEL);
if (!conn)
return AE_OK;
conn->csi2_data = *csi2_res;
strscpy(conn->remote_name, csi2_res_src->string_ptr, csi2_res_src_length);
conn->csi2_data.resource_source.string_ptr = conn->remote_name;
conn->remote_handle = remote_handle;
list_add(&conn->entry, &crwd->connections);
return AE_OK;
}
static struct crs_csi2 *acpi_mipi_add_crs_csi2(acpi_handle handle,
struct list_head *list)
{
struct crs_csi2 *csi2;
csi2 = kzalloc(sizeof(*csi2), GFP_KERNEL);
if (!csi2)
return NULL;
csi2->handle = handle;
INIT_LIST_HEAD(&csi2->connections);
csi2->port_count = 1;
if (ACPI_FAILURE(acpi_attach_data(handle, acpi_mipi_data_tag, csi2))) {
kfree(csi2);
return NULL;
}
list_add(&csi2->entry, list);
return csi2;
}
static struct crs_csi2 *acpi_mipi_get_crs_csi2(acpi_handle handle)
{
struct crs_csi2 *csi2;
if (ACPI_FAILURE(acpi_get_data_full(handle, acpi_mipi_data_tag,
(void **)&csi2, NULL)))
return NULL;
return csi2;
}
static void csi_csr2_release_connections(struct list_head *list)
{
struct crs_csi2_connection *conn, *conn_tmp;
list_for_each_entry_safe(conn, conn_tmp, list, entry) {
list_del(&conn->entry);
kfree(conn);
}
}
static void acpi_mipi_del_crs_csi2(struct crs_csi2 *csi2)
{
list_del(&csi2->entry);
acpi_detach_data(csi2->handle, acpi_mipi_data_tag);
kfree(csi2->swnodes);
csi_csr2_release_connections(&csi2->connections);
kfree(csi2);
}
/**
* acpi_mipi_check_crs_csi2 - Look for CSI-2 resources in _CRS
* @handle: Device object handle to evaluate _CRS for.
*
* Find all CSI-2 resource descriptors in the given device's _CRS
* and collect them into a list.
*/
void acpi_mipi_check_crs_csi2(acpi_handle handle)
{
struct csi2_resources_walk_data crwd = {
.handle = handle,
.connections = LIST_HEAD_INIT(crwd.connections),
};
struct crs_csi2 *csi2;
/*
* Avoid allocating _CRS CSI-2 objects for devices without any CSI-2
* resource descriptions in _CRS to reduce overhead.
*/
acpi_walk_resources(handle, METHOD_NAME__CRS, parse_csi2_resource, &crwd);
if (list_empty(&crwd.connections))
return;
/*
* Create a _CRS CSI-2 entry to store the extracted connection
* information and add it to the global list.
*/
csi2 = acpi_mipi_add_crs_csi2(handle, &acpi_mipi_crs_csi2_list);
if (!csi2) {
csi_csr2_release_connections(&crwd.connections);
return; /* Nothing really can be done about this. */
}
list_replace(&crwd.connections, &csi2->connections);
}
#define NO_CSI2_PORT (UINT_MAX - 1)
static void alloc_crs_csi2_swnodes(struct crs_csi2 *csi2)
{
size_t port_count = csi2->port_count;
struct acpi_device_software_nodes *swnodes;
size_t alloc_size;
unsigned int i;
/*
* Allocate memory for ports, node pointers (number of nodes +
* 1 (guardian), nodes (root + number of ports * 2 (because for
* every port there is an endpoint)).
*/
if (check_mul_overflow(sizeof(*swnodes->ports) +
sizeof(*swnodes->nodes) * 2 +
sizeof(*swnodes->nodeptrs) * 2,
port_count, &alloc_size) ||
check_add_overflow(sizeof(*swnodes) +
sizeof(*swnodes->nodes) +
sizeof(*swnodes->nodeptrs) * 2,
alloc_size, &alloc_size)) {
acpi_handle_info(csi2->handle,
"too many _CRS CSI-2 resource handles (%zu)",
port_count);
return;
}
swnodes = kmalloc(alloc_size, GFP_KERNEL);
if (!swnodes)
return;
swnodes->ports = (struct acpi_device_software_node_port *)(swnodes + 1);
swnodes->nodes = (struct software_node *)(swnodes->ports + port_count);
swnodes->nodeptrs = (const struct software_node **)(swnodes->nodes + 1 +
2 * port_count);
swnodes->num_ports = port_count;
for (i = 0; i < 2 * port_count + 1; i++)
swnodes->nodeptrs[i] = &swnodes->nodes[i];
swnodes->nodeptrs[i] = NULL;
for (i = 0; i < port_count; i++)
swnodes->ports[i].port_nr = NO_CSI2_PORT;
csi2->swnodes = swnodes;
}
#define ACPI_CRS_CSI2_PHY_TYPE_C 0
#define ACPI_CRS_CSI2_PHY_TYPE_D 1
static unsigned int next_csi2_port_index(struct acpi_device_software_nodes *swnodes,
unsigned int port_nr)
{
unsigned int i;
for (i = 0; i < swnodes->num_ports; i++) {
struct acpi_device_software_node_port *port = &swnodes->ports[i];
if (port->port_nr == port_nr)
return i;
if (port->port_nr == NO_CSI2_PORT) {
port->port_nr = port_nr;
return i;
}
}
return NO_CSI2_PORT;
}
/* Print graph port name into a buffer, return non-zero on failure. */
#define GRAPH_PORT_NAME(var, num) \
(snprintf((var), sizeof(var), SWNODE_GRAPH_PORT_NAME_FMT, (num)) >= \
sizeof(var))
static void extract_crs_csi2_conn_info(acpi_handle local_handle,
struct acpi_device_software_nodes *local_swnodes,
struct crs_csi2_connection *conn)
{
struct crs_csi2 *remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle);
struct acpi_device_software_nodes *remote_swnodes;
struct acpi_device_software_node_port *local_port, *remote_port;
struct software_node *local_node, *remote_node;
unsigned int local_index, remote_index;
unsigned int bus_type;
/*
* If the previous steps have failed to make room for a _CRS CSI-2
* representation for the remote end of the given connection, skip it.
*/
if (!remote_csi2)
return;
remote_swnodes = remote_csi2->swnodes;
if (!remote_swnodes)
return;
switch (conn->csi2_data.phy_type) {
case ACPI_CRS_CSI2_PHY_TYPE_C:
bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_CPHY;
break;
case ACPI_CRS_CSI2_PHY_TYPE_D:
bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_DPHY;
break;
default:
acpi_handle_info(local_handle, "unknown CSI-2 PHY type %u\n",
conn->csi2_data.phy_type);
return;
}
local_index = next_csi2_port_index(local_swnodes,
conn->csi2_data.local_port_instance);
if (WARN_ON_ONCE(local_index >= local_swnodes->num_ports))
return;
remote_index = next_csi2_port_index(remote_swnodes,
conn->csi2_data.resource_source.index);
if (WARN_ON_ONCE(remote_index >= remote_swnodes->num_ports))
return;
local_port = &local_swnodes->ports[local_index];
local_node = &local_swnodes->nodes[ACPI_DEVICE_SWNODE_EP(local_index)];
local_port->crs_csi2_local = true;
remote_port = &remote_swnodes->ports[remote_index];
remote_node = &remote_swnodes->nodes[ACPI_DEVICE_SWNODE_EP(remote_index)];
local_port->remote_ep[0] = SOFTWARE_NODE_REFERENCE(remote_node);
remote_port->remote_ep[0] = SOFTWARE_NODE_REFERENCE(local_node);
local_port->ep_props[ACPI_DEVICE_SWNODE_EP_REMOTE_EP] =
PROPERTY_ENTRY_REF_ARRAY("remote-endpoint",
local_port->remote_ep);
local_port->ep_props[ACPI_DEVICE_SWNODE_EP_BUS_TYPE] =
PROPERTY_ENTRY_U32("bus-type", bus_type);
local_port->ep_props[ACPI_DEVICE_SWNODE_EP_REG] =
PROPERTY_ENTRY_U32("reg", 0);
local_port->port_props[ACPI_DEVICE_SWNODE_PORT_REG] =
PROPERTY_ENTRY_U32("reg", conn->csi2_data.local_port_instance);
if (GRAPH_PORT_NAME(local_port->port_name,
conn->csi2_data.local_port_instance))
acpi_handle_info(local_handle, "local port %u name too long",
conn->csi2_data.local_port_instance);
remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_REMOTE_EP] =
PROPERTY_ENTRY_REF_ARRAY("remote-endpoint",
remote_port->remote_ep);
remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_BUS_TYPE] =
PROPERTY_ENTRY_U32("bus-type", bus_type);
remote_port->ep_props[ACPI_DEVICE_SWNODE_EP_REG] =
PROPERTY_ENTRY_U32("reg", 0);
remote_port->port_props[ACPI_DEVICE_SWNODE_PORT_REG] =
PROPERTY_ENTRY_U32("reg", conn->csi2_data.resource_source.index);
if (GRAPH_PORT_NAME(remote_port->port_name,
conn->csi2_data.resource_source.index))
acpi_handle_info(local_handle, "remote port %u name too long",
conn->csi2_data.resource_source.index);
}
static void prepare_crs_csi2_swnodes(struct crs_csi2 *csi2)
{
struct acpi_device_software_nodes *local_swnodes = csi2->swnodes;
acpi_handle local_handle = csi2->handle;
struct crs_csi2_connection *conn;
/* Bail out if the allocation of swnodes has failed. */
if (!local_swnodes)
return;
list_for_each_entry(conn, &csi2->connections, entry)
extract_crs_csi2_conn_info(local_handle, local_swnodes, conn);
}
/**
* acpi_mipi_scan_crs_csi2 - Create ACPI _CRS CSI-2 software nodes
*
* Note that this function must be called before any struct acpi_device objects
* are bound to any ACPI drivers or scan handlers, so it cannot assume the
* existence of struct acpi_device objects for every device present in the ACPI
* namespace.
*
* acpi_scan_lock in scan.c must be held when calling this function.
*/
void acpi_mipi_scan_crs_csi2(void)
{
struct crs_csi2 *csi2;
LIST_HEAD(aux_list);
/* Count references to each ACPI handle in the CSI-2 connection graph. */
list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) {
struct crs_csi2_connection *conn;
list_for_each_entry(conn, &csi2->connections, entry) {
struct crs_csi2 *remote_csi2;
csi2->port_count++;
remote_csi2 = acpi_mipi_get_crs_csi2(conn->remote_handle);
if (remote_csi2) {
remote_csi2->port_count++;
continue;
}
/*
* The remote endpoint has no _CRS CSI-2 list entry yet,
* so create one for it and add it to the list.
*/
acpi_mipi_add_crs_csi2(conn->remote_handle, &aux_list);
}
}
list_splice(&aux_list, &acpi_mipi_crs_csi2_list);
/*
* Allocate software nodes for representing the CSI-2 information.
*
* This needs to be done for all of the list entries in one go, because
* they may point to each other without restrictions and the next step
* relies on the availability of swnodes memory for each list entry.
*/
list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry)
alloc_crs_csi2_swnodes(csi2);
/*
* Set up software node properties using data from _CRS CSI-2 resource
* descriptors.
*/
list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry)
prepare_crs_csi2_swnodes(csi2);
}
/*
* Get the index of the next property in the property array, with a given
* maximum value.
*/
#define NEXT_PROPERTY(index, max) \
(WARN_ON((index) > ACPI_DEVICE_SWNODE_##max) ? \
ACPI_DEVICE_SWNODE_##max : (index)++)
static void init_csi2_port_local(struct acpi_device *adev,
struct acpi_device_software_node_port *port,
struct fwnode_handle *port_fwnode,
unsigned int index)
{
acpi_handle handle = acpi_device_handle(adev);
unsigned int num_link_freqs;
int ret;
ret = fwnode_property_count_u64(port_fwnode, "mipi-img-link-frequencies");
if (ret <= 0)
return;
num_link_freqs = ret;
if (num_link_freqs > ACPI_DEVICE_CSI2_DATA_LANES) {
acpi_handle_info(handle, "Too many link frequencies: %u\n",
num_link_freqs);
num_link_freqs = ACPI_DEVICE_CSI2_DATA_LANES;
}
ret = fwnode_property_read_u64_array(port_fwnode,
"mipi-img-link-frequencies",
port->link_frequencies,
num_link_freqs);
if (ret) {
acpi_handle_info(handle, "Unable to get link frequencies (%d)\n",
ret);
return;
}
port->ep_props[NEXT_PROPERTY(index, EP_LINK_FREQUENCIES)] =
PROPERTY_ENTRY_U64_ARRAY_LEN("link-frequencies",
port->link_frequencies,
num_link_freqs);
}
static void init_csi2_port(struct acpi_device *adev,
struct acpi_device_software_nodes *swnodes,
struct acpi_device_software_node_port *port,
struct fwnode_handle *port_fwnode,
unsigned int port_index)
{
unsigned int ep_prop_index = ACPI_DEVICE_SWNODE_EP_CLOCK_LANES;
acpi_handle handle = acpi_device_handle(adev);
u8 val[ACPI_DEVICE_CSI2_DATA_LANES];
int num_lanes = 0;
int ret;
if (GRAPH_PORT_NAME(port->port_name, port->port_nr))
return;
swnodes->nodes[ACPI_DEVICE_SWNODE_PORT(port_index)] =
SOFTWARE_NODE(port->port_name, port->port_props,
&swnodes->nodes[ACPI_DEVICE_SWNODE_ROOT]);
ret = fwnode_property_read_u8(port_fwnode, "mipi-img-clock-lane", val);
if (!ret)
port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_CLOCK_LANES)] =
PROPERTY_ENTRY_U32("clock-lanes", val[0]);
ret = fwnode_property_count_u8(port_fwnode, "mipi-img-data-lanes");
if (ret > 0) {
num_lanes = ret;
if (num_lanes > ACPI_DEVICE_CSI2_DATA_LANES) {
acpi_handle_info(handle, "Too many data lanes: %u\n",
num_lanes);
num_lanes = ACPI_DEVICE_CSI2_DATA_LANES;
}
ret = fwnode_property_read_u8_array(port_fwnode,
"mipi-img-data-lanes",
val, num_lanes);
if (!ret) {
unsigned int i;
for (i = 0; i < num_lanes; i++)
port->data_lanes[i] = val[i];
port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_DATA_LANES)] =
PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes",
port->data_lanes,
num_lanes);
}
}
ret = fwnode_property_count_u8(port_fwnode, "mipi-img-lane-polarities");
if (ret < 0) {
acpi_handle_debug(handle, "Lane polarity bytes missing\n");
} else if (ret * BITS_PER_TYPE(u8) < num_lanes + 1) {
acpi_handle_info(handle, "Too few lane polarity bits (%zu vs. %d)\n",
ret * BITS_PER_TYPE(u8), num_lanes + 1);
} else {
unsigned long mask = 0;
int byte_count = ret;
unsigned int i;
/*
* The total number of lanes is ACPI_DEVICE_CSI2_DATA_LANES + 1
* (data lanes + clock lane). It is not expected to ever be
* greater than the number of bits in an unsigned long
* variable, but ensure that this is the case.
*/
BUILD_BUG_ON(BITS_PER_TYPE(unsigned long) <= ACPI_DEVICE_CSI2_DATA_LANES);
if (byte_count > sizeof(mask)) {
acpi_handle_info(handle, "Too many lane polarities: %d\n",
byte_count);
byte_count = sizeof(mask);
}
fwnode_property_read_u8_array(port_fwnode, "mipi-img-lane-polarities",
val, byte_count);
for (i = 0; i < byte_count; i++)
mask |= (unsigned long)val[i] << BITS_PER_TYPE(u8) * i;
for (i = 0; i <= num_lanes; i++)
port->lane_polarities[i] = test_bit(i, &mask);
port->ep_props[NEXT_PROPERTY(ep_prop_index, EP_LANE_POLARITIES)] =
PROPERTY_ENTRY_U32_ARRAY_LEN("lane-polarities",
port->lane_polarities,
num_lanes + 1);
}
swnodes->nodes[ACPI_DEVICE_SWNODE_EP(port_index)] =
SOFTWARE_NODE("endpoint@0", swnodes->ports[port_index].ep_props,
&swnodes->nodes[ACPI_DEVICE_SWNODE_PORT(port_index)]);
if (port->crs_csi2_local)
init_csi2_port_local(adev, port, port_fwnode, ep_prop_index);
}
#define MIPI_IMG_PORT_PREFIX "mipi-img-port-"
static struct fwnode_handle *get_mipi_port_handle(struct fwnode_handle *adev_fwnode,
unsigned int port_nr)
{
char port_name[sizeof(MIPI_IMG_PORT_PREFIX) + 2];
if (snprintf(port_name, sizeof(port_name), "%s%u",
MIPI_IMG_PORT_PREFIX, port_nr) >= sizeof(port_name))
return NULL;
return fwnode_get_named_child_node(adev_fwnode, port_name);
}
static void init_crs_csi2_swnodes(struct crs_csi2 *csi2)
{
struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER };
struct acpi_device_software_nodes *swnodes = csi2->swnodes;
acpi_handle handle = csi2->handle;
unsigned int prop_index = 0;
struct fwnode_handle *adev_fwnode;
struct acpi_device *adev;
acpi_status status;
unsigned int i;
u32 val;
int ret;
/*
* Bail out if the swnodes are not available (either they have not been
* allocated or they have been assigned to the device already).
*/
if (!swnodes)
return;
adev = acpi_fetch_acpi_dev(handle);
if (!adev)
return;
adev_fwnode = acpi_fwnode_handle(adev);
/*
* If the "rotation" property is not present, but _PLD is there,
* evaluate it to get the "rotation" value.
*/
if (!fwnode_property_present(adev_fwnode, "rotation")) {
struct acpi_pld_info *pld;
status = acpi_get_physical_device_location(handle, &pld);
if (ACPI_SUCCESS(status)) {
swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_ROTATION)] =
PROPERTY_ENTRY_U32("rotation",
pld->rotation * 45U);
kfree(pld);
}
}
if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-clock-frequency", &val))
swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_CLOCK_FREQUENCY)] =
PROPERTY_ENTRY_U32("clock-frequency", val);
if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-led-max-current", &val))
swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_LED_MAX_MICROAMP)] =
PROPERTY_ENTRY_U32("led-max-microamp", val);
if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-flash-max-current", &val))
swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_FLASH_MAX_MICROAMP)] =
PROPERTY_ENTRY_U32("flash-max-microamp", val);
if (!fwnode_property_read_u32(adev_fwnode, "mipi-img-flash-max-timeout-us", &val))
swnodes->dev_props[NEXT_PROPERTY(prop_index, DEV_FLASH_MAX_TIMEOUT_US)] =
PROPERTY_ENTRY_U32("flash-max-timeout-us", val);
status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
if (ACPI_FAILURE(status)) {
acpi_handle_info(handle, "Unable to get the path name\n");
return;
}
swnodes->nodes[ACPI_DEVICE_SWNODE_ROOT] =
SOFTWARE_NODE(buffer.pointer, swnodes->dev_props, NULL);
for (i = 0; i < swnodes->num_ports; i++) {
struct acpi_device_software_node_port *port = &swnodes->ports[i];
struct fwnode_handle *port_fwnode;
/*
* The MIPI DisCo for Imaging specification defines _DSD device
* properties for providing CSI-2 port parameters that can be
* accessed through the generic device properties framework. To
* access them, it is first necessary to find the data node
* representing the port under the given ACPI device object.
*/
port_fwnode = get_mipi_port_handle(adev_fwnode, port->port_nr);
if (!port_fwnode) {
acpi_handle_info(handle,
"MIPI port name too long for port %u\n",
port->port_nr);
continue;
}
init_csi2_port(adev, swnodes, port, port_fwnode, i);
fwnode_handle_put(port_fwnode);
}
ret = software_node_register_node_group(swnodes->nodeptrs);
if (ret < 0) {
acpi_handle_info(handle,
"Unable to register software nodes (%d)\n", ret);
return;
}
adev->swnodes = swnodes;
adev_fwnode->secondary = software_node_fwnode(swnodes->nodes);
/*
* Prevents the swnodes from this csi2 entry from being assigned again
* or freed prematurely.
*/
csi2->swnodes = NULL;
}
/**
* acpi_mipi_init_crs_csi2_swnodes - Initialize _CRS CSI-2 software nodes
*
* Use MIPI DisCo for Imaging device properties to finalize the initialization
* of CSI-2 software nodes for all ACPI device objects that have been already
* enumerated.
*/
void acpi_mipi_init_crs_csi2_swnodes(void)
{
struct crs_csi2 *csi2, *csi2_tmp;
list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry)
init_crs_csi2_swnodes(csi2);
}
/**
* acpi_mipi_crs_csi2_cleanup - Free _CRS CSI-2 temporary data
*/
void acpi_mipi_crs_csi2_cleanup(void)
{
struct crs_csi2 *csi2, *csi2_tmp;
list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry)
acpi_mipi_del_crs_csi2(csi2);
}

View File

@ -67,9 +67,9 @@ int acpi_map_pxm_to_node(int pxm)
node = pxm_to_node_map[pxm];
if (node == NUMA_NO_NODE) {
if (nodes_weight(nodes_found_map) >= MAX_NUMNODES)
return NUMA_NO_NODE;
node = first_unset_node(nodes_found_map);
if (node >= MAX_NUMNODES)
return NUMA_NO_NODE;
__acpi_map_pxm_to_node(pxm, node);
node_set(node, nodes_found_map);
}
@ -183,7 +183,7 @@ static int __init slit_valid(struct acpi_table_slit *slit)
int i, j;
int d = slit->locality_count;
for (i = 0; i < d; i++) {
for (j = 0; j < d; j++) {
for (j = 0; j < d; j++) {
u8 val = slit->entry[d*i + j];
if (i == j) {
if (val != LOCAL_DISTANCE)
@ -430,7 +430,7 @@ acpi_parse_gi_affinity(union acpi_subtable_headers *header,
return -EINVAL;
node = acpi_map_pxm_to_node(gi_affinity->proximity_domain);
if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) {
if (node == NUMA_NO_NODE) {
pr_err("SRAT: Too many proximity domains.\n");
return -EINVAL;
}
@ -532,7 +532,7 @@ int __init acpi_numa_init(void)
*/
/* fake_pxm is the next unused PXM value after SRAT parsing */
for (i = 0, fake_pxm = -1; i < MAX_NUMNODES - 1; i++) {
for (i = 0, fake_pxm = -1; i < MAX_NUMNODES; i++) {
if (node_to_pxm_map[i] > fake_pxm)
fake_pxm = node_to_pxm_map[i];
}

View File

@ -544,11 +544,7 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,
static irqreturn_t acpi_irq(int irq, void *dev_id)
{
u32 handled;
handled = (*acpi_irq_handler) (acpi_irq_context);
if (handled) {
if ((*acpi_irq_handler)(acpi_irq_context)) {
acpi_irq_handled++;
return IRQ_HANDLED;
} else {
@ -582,7 +578,8 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler,
acpi_irq_handler = handler;
acpi_irq_context = context;
if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) {
if (request_threaded_irq(irq, NULL, acpi_irq, IRQF_SHARED | IRQF_ONESHOT,
"acpi", acpi_irq)) {
pr_err("SCI (IRQ%d) allocation failed\n", irq);
acpi_irq_handler = NULL;
return AE_NOT_ACQUIRED;
@ -1063,9 +1060,7 @@ int __init acpi_debugger_init(void)
acpi_status acpi_os_execute(acpi_execute_type type,
acpi_osd_exec_callback function, void *context)
{
acpi_status status = AE_OK;
struct acpi_os_dpc *dpc;
struct workqueue_struct *queue;
int ret;
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
@ -1076,9 +1071,9 @@ acpi_status acpi_os_execute(acpi_execute_type type,
ret = acpi_debugger_create_thread(function, context);
if (ret) {
pr_err("Kernel thread creation failed\n");
status = AE_ERROR;
return AE_ERROR;
}
goto out_thread;
return AE_OK;
}
/*
@ -1096,43 +1091,41 @@ acpi_status acpi_os_execute(acpi_execute_type type,
dpc->function = function;
dpc->context = context;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
/*
* To prevent lockdep from complaining unnecessarily, make sure that
* there is a different static lockdep key for each workqueue by using
* INIT_WORK() for each of them separately.
*/
if (type == OSL_NOTIFY_HANDLER) {
queue = kacpi_notify_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
} else if (type == OSL_GPE_HANDLER) {
queue = kacpid_wq;
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
} else {
switch (type) {
case OSL_NOTIFY_HANDLER:
ret = queue_work(kacpi_notify_wq, &dpc->work);
break;
case OSL_GPE_HANDLER:
/*
* On some machines, a software-initiated SMI causes corruption
* unless the SMI runs on CPU 0. An SMI can be initiated by
* any AML, but typically it's done in GPE-related methods that
* are run via workqueues, so we can avoid the known corruption
* cases by always queueing on CPU 0.
*/
ret = queue_work_on(0, kacpid_wq, &dpc->work);
break;
default:
pr_err("Unsupported os_execute type %d.\n", type);
status = AE_ERROR;
goto err;
}
if (ACPI_FAILURE(status))
goto err_workqueue;
/*
* On some machines, a software-initiated SMI causes corruption unless
* the SMI runs on CPU 0. An SMI can be initiated by any AML, but
* typically it's done in GPE-related methods that are run via
* workqueues, so we can avoid the known corruption cases by always
* queueing on CPU 0.
*/
ret = queue_work_on(0, queue, &dpc->work);
if (!ret) {
pr_err("Unable to queue work\n");
status = AE_ERROR;
goto err;
}
err_workqueue:
if (ACPI_FAILURE(status))
kfree(dpc);
out_thread:
return status;
return AE_OK;
err:
kfree(dpc);
return AE_ERROR;
}
EXPORT_SYMBOL(acpi_os_execute);
@ -1522,20 +1515,18 @@ void acpi_os_delete_lock(acpi_spinlock handle)
acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock lockp)
__acquires(lockp)
{
acpi_cpu_flags flags;
spin_lock_irqsave(lockp, flags);
return flags;
spin_lock(lockp);
return 0;
}
/*
* Release a spinlock. See above.
*/
void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags flags)
void acpi_os_release_lock(acpi_spinlock lockp, acpi_cpu_flags not_used)
__releases(lockp)
{
spin_unlock_irqrestore(lockp, flags);
spin_unlock(lockp);
}
#ifndef ACPI_USE_LOCAL_CACHE
@ -1672,7 +1663,7 @@ acpi_status __init acpi_os_initialize(void)
acpi_status __init acpi_os_initialize1(void)
{
kacpid_wq = alloc_workqueue("kacpid", 0, 1);
kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 1);
kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 0);
kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
BUG_ON(!kacpid_wq);
BUG_ON(!kacpi_notify_wq);

View File

@ -17,6 +17,8 @@
#include <acpi/processor.h>
#include <linux/uaccess.h>
#include "internal.h"
#ifdef CONFIG_CPU_FREQ
/* If a passive cooling situation is detected, primarily CPUfreq is used, as it
@ -26,12 +28,21 @@
*/
#define CPUFREQ_THERMAL_MIN_STEP 0
#define CPUFREQ_THERMAL_MAX_STEP 3
static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_pctg);
static int cpufreq_thermal_max_step __read_mostly = 3;
#define reduction_pctg(cpu) \
per_cpu(cpufreq_thermal_reduction_pctg, phys_package_first_cpu(cpu))
/*
* Minimum throttle percentage for processor_thermal cooling device.
* The processor_thermal driver uses it to calculate the percentage amount by
* which cpu frequency must be reduced for each cooling state. This is also used
* to calculate the maximum number of throttling steps or cooling states.
*/
static int cpufreq_thermal_reduction_pctg __read_mostly = 20;
static DEFINE_PER_CPU(unsigned int, cpufreq_thermal_reduction_step);
#define reduction_step(cpu) \
per_cpu(cpufreq_thermal_reduction_step, phys_package_first_cpu(cpu))
/*
* Emulate "per package data" using per cpu data (which should really be
@ -71,7 +82,7 @@ static int cpufreq_get_max_state(unsigned int cpu)
if (!cpu_has_cpufreq(cpu))
return 0;
return CPUFREQ_THERMAL_MAX_STEP;
return cpufreq_thermal_max_step;
}
static int cpufreq_get_cur_state(unsigned int cpu)
@ -79,7 +90,7 @@ static int cpufreq_get_cur_state(unsigned int cpu)
if (!cpu_has_cpufreq(cpu))
return 0;
return reduction_pctg(cpu);
return reduction_step(cpu);
}
static int cpufreq_set_cur_state(unsigned int cpu, int state)
@ -92,7 +103,7 @@ static int cpufreq_set_cur_state(unsigned int cpu, int state)
if (!cpu_has_cpufreq(cpu))
return 0;
reduction_pctg(cpu) = state;
reduction_step(cpu) = state;
/*
* Update all the CPUs in the same package because they all
@ -113,7 +124,8 @@ static int cpufreq_set_cur_state(unsigned int cpu, int state)
if (!policy)
return -EINVAL;
max_freq = (policy->cpuinfo.max_freq * (100 - reduction_pctg(i) * 20)) / 100;
max_freq = (policy->cpuinfo.max_freq *
(100 - reduction_step(i) * cpufreq_thermal_reduction_pctg)) / 100;
cpufreq_cpu_put(policy);
@ -126,10 +138,29 @@ static int cpufreq_set_cur_state(unsigned int cpu, int state)
return 0;
}
static void acpi_thermal_cpufreq_config(void)
{
int cpufreq_pctg = acpi_arch_thermal_cpufreq_pctg();
if (!cpufreq_pctg)
return;
cpufreq_thermal_reduction_pctg = cpufreq_pctg;
/*
* Derive the MAX_STEP from minimum throttle percentage so that the reduction
* percentage doesn't end up becoming negative. Also, cap the MAX_STEP so that
* the CPU performance doesn't become 0.
*/
cpufreq_thermal_max_step = (100 / cpufreq_pctg) - 2;
}
void acpi_thermal_cpufreq_init(struct cpufreq_policy *policy)
{
unsigned int cpu;
acpi_thermal_cpufreq_config();
for_each_cpu(cpu, policy->related_cpus) {
struct acpi_processor *pr = per_cpu(processors, cpu);
int ret;
@ -190,7 +221,7 @@ static int acpi_processor_max_state(struct acpi_processor *pr)
/*
* There exists four states according to
* cpufreq_thermal_reduction_pctg. 0, 1, 2, 3
* cpufreq_thermal_reduction_step. 0, 1, 2, 3
*/
max_state += cpufreq_get_max_state(pr->id);
if (pr->flags.throttling)

View File

@ -2,14 +2,17 @@
/*
* ACPI device specific properties support.
*
* Copyright (C) 2014, Intel Corporation
* Copyright (C) 2014 - 2023, Intel Corporation
* All rights reserved.
*
* Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
* Darren Hart <dvhart@linux.intel.com>
* Rafael J. Wysocki <rafael.j.wysocki@intel.com>
* Sakari Ailus <sakari.ailus@linux.intel.com>
*/
#define pr_fmt(fmt) "ACPI: " fmt
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/export.h>
@ -800,28 +803,16 @@ static int acpi_get_ref_args(struct fwnode_reference_args *args,
{
u32 nargs = 0, i;
/*
* Find the referred data extension node under the
* referred device node.
*/
for (; *element < end && (*element)->type == ACPI_TYPE_STRING;
(*element)++) {
const char *child_name = (*element)->string.pointer;
ref_fwnode = acpi_fwnode_get_named_child_node(ref_fwnode, child_name);
if (!ref_fwnode)
return -EINVAL;
}
/*
* Assume the following integer elements are all args. Stop counting on
* the first reference or end of the package arguments. In case of
* neither reference, nor integer, return an error, we can't parse it.
* the first reference (possibly represented as a string) or end of the
* package arguments. In case of neither reference, nor integer, return
* an error, we can't parse it.
*/
for (i = 0; (*element) + i < end && i < num_args; i++) {
acpi_object_type type = (*element)[i].type;
if (type == ACPI_TYPE_LOCAL_REFERENCE)
if (type == ACPI_TYPE_LOCAL_REFERENCE || type == ACPI_TYPE_STRING)
break;
if (type == ACPI_TYPE_INTEGER)
@ -845,6 +836,44 @@ static int acpi_get_ref_args(struct fwnode_reference_args *args,
return 0;
}
static struct fwnode_handle *acpi_parse_string_ref(const struct fwnode_handle *fwnode,
const char *refstring)
{
acpi_handle scope, handle;
struct acpi_data_node *dn;
struct acpi_device *device;
acpi_status status;
if (is_acpi_device_node(fwnode)) {
scope = to_acpi_device_node(fwnode)->handle;
} else if (is_acpi_data_node(fwnode)) {
scope = to_acpi_data_node(fwnode)->handle;
} else {
pr_debug("Bad node type for node %pfw\n", fwnode);
return NULL;
}
status = acpi_get_handle(scope, refstring, &handle);
if (ACPI_FAILURE(status)) {
acpi_handle_debug(scope, "Unable to get an ACPI handle for %s\n",
refstring);
return NULL;
}
device = acpi_fetch_acpi_dev(handle);
if (device)
return acpi_fwnode_handle(device);
status = acpi_get_data_full(handle, acpi_nondev_subnode_tag,
(void **)&dn, NULL);
if (ACPI_FAILURE(status) || !dn) {
acpi_handle_debug(handle, "Subnode not found\n");
return NULL;
}
return &dn->fwnode;
}
/**
* __acpi_node_get_property_reference - returns handle to the referenced object
* @fwnode: Firmware node to get the property from
@ -887,6 +916,7 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
const union acpi_object *element, *end;
const union acpi_object *obj;
const struct acpi_device_data *data;
struct fwnode_handle *ref_fwnode;
struct acpi_device *device;
int ret, idx = 0;
@ -910,16 +940,30 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
args->fwnode = acpi_fwnode_handle(device);
args->nargs = 0;
return 0;
case ACPI_TYPE_STRING:
if (index)
return -ENOENT;
ref_fwnode = acpi_parse_string_ref(fwnode, obj->string.pointer);
if (!ref_fwnode)
return -EINVAL;
args->fwnode = ref_fwnode;
args->nargs = 0;
return 0;
case ACPI_TYPE_PACKAGE:
/*
* If it is not a single reference, then it is a package of
* references followed by number of ints as follows:
* references, followed by number of ints as follows:
*
* Package () { REF, INT, REF, INT, INT }
*
* The index argument is then used to determine which reference
* the caller wants (along with the arguments).
* Here, REF may be either a local reference or a string. The
* index argument is then used to determine which reference the
* caller wants (along with the arguments).
*/
break;
default:
@ -950,6 +994,24 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
if (idx == index)
return 0;
break;
case ACPI_TYPE_STRING:
ref_fwnode = acpi_parse_string_ref(fwnode,
element->string.pointer);
if (!ref_fwnode)
return -EINVAL;
element++;
ret = acpi_get_ref_args(idx == index ? args : NULL,
ref_fwnode, &element, end,
num_args);
if (ret < 0)
return ret;
if (idx == index)
return 0;
break;
case ACPI_TYPE_INTEGER:
if (idx == index)

View File

@ -510,6 +510,13 @@ static const struct dmi_system_id irq1_edge_low_force_override[] = {
DMI_MATCH(DMI_BOARD_NAME, "GMxXGxx"),
},
},
{
/* TongFang GMxXGxx sold as Eluktronics Inc. RP-15 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Eluktronics Inc."),
DMI_MATCH(DMI_BOARD_NAME, "RP-15"),
},
},
{
/* TongFang GM6XGxX/TUXEDO Stellaris 16 Gen5 AMD */
.matches = {
@ -548,6 +555,18 @@ static const struct dmi_system_id irq1_edge_low_force_override[] = {
DMI_MATCH(DMI_BOARD_NAME, "GM6BG0Q"),
},
},
{
/* Infinity E15-5A165-BM */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "GM5RG1E0009COM"),
},
},
{
/* Infinity E15-5A305-1M */
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "GM5RGEE0016COM"),
},
},
{ }
};

View File

@ -1981,10 +1981,9 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev)
}
}
static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep)
static u32 acpi_scan_check_dep(acpi_handle handle)
{
struct acpi_handle_list dep_devices;
acpi_status status;
u32 count;
int i;
@ -1994,12 +1993,10 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep)
* 2. ACPI nodes describing USB ports.
* Still, checking for _HID catches more then just these cases ...
*/
if (!check_dep || !acpi_has_method(handle, "_DEP") ||
!acpi_has_method(handle, "_HID"))
if (!acpi_has_method(handle, "_DEP") || !acpi_has_method(handle, "_HID"))
return 0;
status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices);
if (ACPI_FAILURE(status)) {
if (!acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices)) {
acpi_handle_debug(handle, "Failed to evaluate _DEP.\n");
return 0;
}
@ -2008,6 +2005,7 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep)
struct acpi_device_info *info;
struct acpi_dep_data *dep;
bool skip, honor_dep;
acpi_status status;
status = acpi_get_object_info(dep_devices.handles[i], &info);
if (ACPI_FAILURE(status)) {
@ -2041,7 +2039,13 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep)
return count;
}
static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
static acpi_status acpi_scan_check_crs_csi2_cb(acpi_handle handle, u32 a, void *b, void **c)
{
acpi_mipi_check_crs_csi2(handle);
return AE_OK;
}
static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass,
struct acpi_device **adev_p)
{
struct acpi_device *device = acpi_fetch_acpi_dev(handle);
@ -2059,9 +2063,25 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
if (acpi_device_should_be_hidden(handle))
return AE_OK;
/* Bail out if there are dependencies. */
if (acpi_scan_check_dep(handle, check_dep) > 0)
return AE_CTRL_DEPTH;
if (first_pass) {
acpi_mipi_check_crs_csi2(handle);
/* Bail out if there are dependencies. */
if (acpi_scan_check_dep(handle) > 0) {
/*
* The entire CSI-2 connection graph needs to be
* extracted before any drivers or scan handlers
* are bound to struct device objects, so scan
* _CRS CSI-2 resource descriptors for all
* devices below the current handle.
*/
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
ACPI_UINT32_MAX,
acpi_scan_check_crs_csi2_cb,
NULL, NULL, NULL);
return AE_CTRL_DEPTH;
}
}
fallthrough;
case ACPI_TYPE_ANY: /* for ACPI_ROOT_OBJECT */
@ -2084,10 +2104,10 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
}
/*
* If check_dep is true at this point, the device has no dependencies,
* If first_pass is true at this point, the device has no dependencies,
* or the creation of the device object would have been postponed above.
*/
acpi_add_single_object(&device, handle, type, !check_dep);
acpi_add_single_object(&device, handle, type, !first_pass);
if (!device)
return AE_CTRL_DEPTH;
@ -2431,6 +2451,13 @@ static void acpi_scan_postponed_branch(acpi_handle handle)
acpi_walk_namespace(ACPI_TYPE_ANY, handle, ACPI_UINT32_MAX,
acpi_bus_check_add_2, NULL, NULL, (void **)&adev);
/*
* Populate the ACPI _CRS CSI-2 software nodes for the ACPI devices that
* have been added above.
*/
acpi_mipi_init_crs_csi2_swnodes();
acpi_bus_attach(adev, NULL);
}
@ -2499,12 +2526,22 @@ int acpi_bus_scan(acpi_handle handle)
if (!device)
return -ENODEV;
/*
* Set up ACPI _CRS CSI-2 software nodes using information extracted
* from the _CRS CSI-2 resource descriptors during the ACPI namespace
* walk above and MIPI DisCo for Imaging device properties.
*/
acpi_mipi_scan_crs_csi2();
acpi_mipi_init_crs_csi2_swnodes();
acpi_bus_attach(device, (void *)true);
/* Pass 2: Enumerate all of the remaining devices. */
acpi_scan_postponed();
acpi_mipi_crs_csi2_cleanup();
return 0;
}
EXPORT_SYMBOL(acpi_bus_scan);

View File

@ -31,6 +31,8 @@
#include <linux/uaccess.h>
#include <linux/units.h>
#include "internal.h"
#define ACPI_THERMAL_CLASS "thermal_zone"
#define ACPI_THERMAL_DEVICE_NAME "Thermal Zone"
#define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80
@ -90,7 +92,7 @@ struct acpi_thermal_passive {
struct acpi_thermal_trip trip;
unsigned long tc1;
unsigned long tc2;
unsigned long tsp;
unsigned long delay;
};
struct acpi_thermal_active {
@ -188,24 +190,19 @@ static int active_trip_index(struct acpi_thermal *tz,
static long get_passive_temp(struct acpi_thermal *tz)
{
unsigned long long tmp;
acpi_status status;
int temp;
status = acpi_evaluate_integer(tz->device->handle, "_PSV", NULL, &tmp);
if (ACPI_FAILURE(status))
if (acpi_passive_trip_temp(tz->device, &temp))
return THERMAL_TEMP_INVALID;
return tmp;
return temp;
}
static long get_active_temp(struct acpi_thermal *tz, int index)
{
char method[] = { '_', 'A', 'C', '0' + index, '\0' };
unsigned long long tmp;
acpi_status status;
int temp;
status = acpi_evaluate_integer(tz->device->handle, method, NULL, &tmp);
if (ACPI_FAILURE(status))
if (acpi_active_trip_temp(tz->device, index, &temp))
return THERMAL_TEMP_INVALID;
/*
@ -215,10 +212,10 @@ static long get_active_temp(struct acpi_thermal *tz, int index)
if (act > 0) {
unsigned long long override = celsius_to_deci_kelvin(act);
if (tmp > override)
tmp = override;
if (temp > override)
return override;
}
return tmp;
return temp;
}
static void acpi_thermal_update_trip(struct acpi_thermal *tz,
@ -247,7 +244,6 @@ static bool update_trip_devices(struct acpi_thermal *tz,
{
struct acpi_handle_list devices = { 0 };
char method[] = "_PSL";
acpi_status status;
if (index != ACPI_THERMAL_TRIP_PASSIVE) {
method[1] = 'A';
@ -255,8 +251,7 @@ static bool update_trip_devices(struct acpi_thermal *tz,
method[3] = '0' + index;
}
status = acpi_evaluate_reference(tz->device->handle, method, NULL, &devices);
if (ACPI_FAILURE(status)) {
if (!acpi_evaluate_reference(tz->device->handle, method, NULL, &devices)) {
acpi_handle_info(tz->device->handle, "%s evaluation failure\n", method);
return false;
}
@ -339,13 +334,12 @@ static void acpi_thermal_trips_update(struct acpi_thermal *tz, u32 event)
dev_name(&adev->dev), event, 0);
}
static long acpi_thermal_get_critical_trip(struct acpi_thermal *tz)
static int acpi_thermal_get_critical_trip(struct acpi_thermal *tz)
{
unsigned long long tmp;
acpi_status status;
int temp;
if (crt > 0) {
tmp = celsius_to_deci_kelvin(crt);
temp = celsius_to_deci_kelvin(crt);
goto set;
}
if (crt == -1) {
@ -353,38 +347,34 @@ static long acpi_thermal_get_critical_trip(struct acpi_thermal *tz)
return THERMAL_TEMP_INVALID;
}
status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL, &tmp);
if (ACPI_FAILURE(status)) {
acpi_handle_debug(tz->device->handle, "No critical threshold\n");
if (acpi_critical_trip_temp(tz->device, &temp))
return THERMAL_TEMP_INVALID;
}
if (tmp <= 2732) {
if (temp <= 2732) {
/*
* Below zero (Celsius) values clearly aren't right for sure,
* so discard them as invalid.
*/
pr_info(FW_BUG "Invalid critical threshold (%llu)\n", tmp);
pr_info(FW_BUG "Invalid critical threshold (%d)\n", temp);
return THERMAL_TEMP_INVALID;
}
set:
acpi_handle_debug(tz->device->handle, "Critical threshold [%llu]\n", tmp);
return tmp;
acpi_handle_debug(tz->device->handle, "Critical threshold [%d]\n", temp);
return temp;
}
static long acpi_thermal_get_hot_trip(struct acpi_thermal *tz)
static int acpi_thermal_get_hot_trip(struct acpi_thermal *tz)
{
unsigned long long tmp;
acpi_status status;
int temp;
status = acpi_evaluate_integer(tz->device->handle, "_HOT", NULL, &tmp);
if (ACPI_FAILURE(status)) {
if (acpi_hot_trip_temp(tz->device, &temp) || temp == THERMAL_TEMP_INVALID) {
acpi_handle_debug(tz->device->handle, "No hot threshold\n");
return THERMAL_TEMP_INVALID;
}
acpi_handle_debug(tz->device->handle, "Hot threshold [%llu]\n", tmp);
return tmp;
acpi_handle_debug(tz->device->handle, "Hot threshold [%d]\n", temp);
return temp;
}
static bool passive_trip_params_init(struct acpi_thermal *tz)
@ -404,11 +394,17 @@ static bool passive_trip_params_init(struct acpi_thermal *tz)
tz->trips.passive.tc2 = tmp;
status = acpi_evaluate_integer(tz->device->handle, "_TFP", NULL, &tmp);
if (ACPI_SUCCESS(status)) {
tz->trips.passive.delay = tmp;
return true;
}
status = acpi_evaluate_integer(tz->device->handle, "_TSP", NULL, &tmp);
if (ACPI_FAILURE(status))
return false;
tz->trips.passive.tsp = tmp;
tz->trips.passive.delay = tmp * 100;
return true;
}
@ -904,7 +900,7 @@ static int acpi_thermal_add(struct acpi_device *device)
acpi_trip = &tz->trips.passive.trip;
if (acpi_thermal_trip_valid(acpi_trip)) {
passive_delay = tz->trips.passive.tsp * 100;
passive_delay = tz->trips.passive.delay;
trip->type = THERMAL_TRIP_PASSIVE;
trip->temperature = acpi_thermal_temp(tz, acpi_trip->temp_dk);
@ -1142,6 +1138,7 @@ static void __exit acpi_thermal_exit(void)
module_init(acpi_thermal_init);
module_exit(acpi_thermal_exit);
MODULE_IMPORT_NS(ACPI_THERMAL);
MODULE_AUTHOR("Paul Diefenbaugh");
MODULE_DESCRIPTION("ACPI Thermal Zone Driver");
MODULE_LICENSE("GPL");

View File

@ -3,12 +3,13 @@
* Copyright 2023 Linaro Limited
* Copyright 2023 Intel Corporation
*
* Library routines for populating a generic thermal trip point structure
* with data obtained by evaluating a specific object in the ACPI Namespace.
* Library routines for retrieving trip point temperature values from the
* platform firmware via ACPI.
*/
#include <linux/acpi.h>
#include <linux/units.h>
#include <linux/thermal.h>
#include "internal.h"
/*
* Minimum temperature for full military grade is 218°K (-55°C) and
@ -17,11 +18,11 @@
* firmware. Any values out of these boundaries may be considered
* bogus and we can assume the firmware has no data to provide.
*/
#define TEMP_MIN_DECIK 2180
#define TEMP_MAX_DECIK 4480
#define TEMP_MIN_DECIK 2180ULL
#define TEMP_MAX_DECIK 4480ULL
static int thermal_acpi_trip_temp(struct acpi_device *adev, char *obj_name,
int *ret_temp)
static int acpi_trip_temp(struct acpi_device *adev, char *obj_name,
int *ret_temp)
{
unsigned long long temp;
acpi_status status;
@ -33,7 +34,7 @@ static int thermal_acpi_trip_temp(struct acpi_device *adev, char *obj_name,
}
if (temp >= TEMP_MIN_DECIK && temp <= TEMP_MAX_DECIK) {
*ret_temp = deci_kelvin_to_millicelsius(temp);
*ret_temp = temp;
} else {
acpi_handle_debug(adev->handle, "%s result %llu out of range\n",
obj_name, temp);
@ -43,6 +44,48 @@ static int thermal_acpi_trip_temp(struct acpi_device *adev, char *obj_name,
return 0;
}
int acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp)
{
char obj_name[] = {'_', 'A', 'C', '0' + id, '\0'};
if (id < 0 || id > 9)
return -EINVAL;
return acpi_trip_temp(adev, obj_name, ret_temp);
}
EXPORT_SYMBOL_NS_GPL(acpi_active_trip_temp, ACPI_THERMAL);
int acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp)
{
return acpi_trip_temp(adev, "_PSV", ret_temp);
}
EXPORT_SYMBOL_NS_GPL(acpi_passive_trip_temp, ACPI_THERMAL);
int acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp)
{
return acpi_trip_temp(adev, "_HOT", ret_temp);
}
EXPORT_SYMBOL_NS_GPL(acpi_hot_trip_temp, ACPI_THERMAL);
int acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp)
{
return acpi_trip_temp(adev, "_CRT", ret_temp);
}
EXPORT_SYMBOL_NS_GPL(acpi_critical_trip_temp, ACPI_THERMAL);
static int thermal_temp(int error, int temp_decik, int *ret_temp)
{
if (error)
return error;
if (temp_decik == THERMAL_TEMP_INVALID)
*ret_temp = THERMAL_TEMP_INVALID;
else
*ret_temp = deci_kelvin_to_millicelsius(temp_decik);
return 0;
}
/**
* thermal_acpi_active_trip_temp - Retrieve active trip point temperature
* @adev: Target thermal zone ACPI device object.
@ -57,12 +100,10 @@ static int thermal_acpi_trip_temp(struct acpi_device *adev, char *obj_name,
*/
int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp)
{
char obj_name[] = {'_', 'A', 'C', '0' + id, '\0'};
int temp_decik;
int ret = acpi_active_trip_temp(adev, id, &temp_decik);
if (id < 0 || id > 9)
return -EINVAL;
return thermal_acpi_trip_temp(adev, obj_name, ret_temp);
return thermal_temp(ret, temp_decik, ret_temp);
}
EXPORT_SYMBOL_GPL(thermal_acpi_active_trip_temp);
@ -78,7 +119,10 @@ EXPORT_SYMBOL_GPL(thermal_acpi_active_trip_temp);
*/
int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp)
{
return thermal_acpi_trip_temp(adev, "_PSV", ret_temp);
int temp_decik;
int ret = acpi_passive_trip_temp(adev, &temp_decik);
return thermal_temp(ret, temp_decik, ret_temp);
}
EXPORT_SYMBOL_GPL(thermal_acpi_passive_trip_temp);
@ -95,7 +139,10 @@ EXPORT_SYMBOL_GPL(thermal_acpi_passive_trip_temp);
*/
int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp)
{
return thermal_acpi_trip_temp(adev, "_HOT", ret_temp);
int temp_decik;
int ret = acpi_hot_trip_temp(adev, &temp_decik);
return thermal_temp(ret, temp_decik, ret_temp);
}
EXPORT_SYMBOL_GPL(thermal_acpi_hot_trip_temp);
@ -111,6 +158,9 @@ EXPORT_SYMBOL_GPL(thermal_acpi_hot_trip_temp);
*/
int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp)
{
return thermal_acpi_trip_temp(adev, "_CRT", ret_temp);
int temp_decik;
int ret = acpi_critical_trip_temp(adev, &temp_decik);
return thermal_temp(ret, temp_decik, ret_temp);
}
EXPORT_SYMBOL_GPL(thermal_acpi_critical_trip_temp);

View File

@ -329,21 +329,18 @@ const char *acpi_get_subsystem_id(acpi_handle handle)
}
EXPORT_SYMBOL_GPL(acpi_get_subsystem_id);
acpi_status
acpi_evaluate_reference(acpi_handle handle,
acpi_string pathname,
struct acpi_object_list *arguments,
struct acpi_handle_list *list)
bool acpi_evaluate_reference(acpi_handle handle, acpi_string pathname,
struct acpi_object_list *arguments,
struct acpi_handle_list *list)
{
acpi_status status = AE_OK;
union acpi_object *package = NULL;
union acpi_object *element = NULL;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
u32 i = 0;
union acpi_object *package;
acpi_status status;
bool ret = false;
u32 i;
if (!list)
return AE_BAD_PARAMETER;
return false;
/* Evaluate object. */
@ -353,62 +350,47 @@ acpi_evaluate_reference(acpi_handle handle,
package = buffer.pointer;
if ((buffer.length == 0) || !package) {
status = AE_BAD_DATA;
acpi_util_eval_error(handle, pathname, status);
goto end;
}
if (package->type != ACPI_TYPE_PACKAGE) {
status = AE_BAD_DATA;
acpi_util_eval_error(handle, pathname, status);
goto end;
}
if (!package->package.count) {
status = AE_BAD_DATA;
acpi_util_eval_error(handle, pathname, status);
goto end;
}
if (buffer.length == 0 || !package ||
package->type != ACPI_TYPE_PACKAGE || !package->package.count)
goto err;
list->handles = kcalloc(package->package.count, sizeof(*list->handles), GFP_KERNEL);
if (!list->handles) {
kfree(package);
return AE_NO_MEMORY;
}
list->count = package->package.count;
list->handles = kcalloc(list->count, sizeof(*list->handles), GFP_KERNEL);
if (!list->handles)
goto err_clear;
/* Extract package data. */
for (i = 0; i < list->count; i++) {
union acpi_object *element = &(package->package.elements[i]);
element = &(package->package.elements[i]);
if (element->type != ACPI_TYPE_LOCAL_REFERENCE ||
!element->reference.handle)
goto err_free;
if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
status = AE_BAD_DATA;
acpi_util_eval_error(handle, pathname, status);
break;
}
if (!element->reference.handle) {
status = AE_NULL_ENTRY;
acpi_util_eval_error(handle, pathname, status);
break;
}
/* Get the acpi_handle. */
list->handles[i] = element->reference.handle;
acpi_handle_debug(list->handles[i], "Found in reference list\n");
}
if (ACPI_FAILURE(status)) {
list->count = 0;
kfree(list->handles);
list->handles = NULL;
}
ret = true;
end:
kfree(buffer.pointer);
return status;
return ret;
err_free:
kfree(list->handles);
list->handles = NULL;
err_clear:
list->count = 0;
err:
acpi_util_eval_error(handle, pathname, status);
goto end;
}
EXPORT_SYMBOL(acpi_evaluate_reference);
@ -426,7 +408,7 @@ bool acpi_handle_list_equal(struct acpi_handle_list *list1,
{
return list1->count == list2->count &&
!memcmp(list1->handles, list2->handles,
list1->count * sizeof(acpi_handle));
list1->count * sizeof(*list1->handles));
}
EXPORT_SYMBOL_GPL(acpi_handle_list_equal);
@ -468,6 +450,40 @@ void acpi_handle_list_free(struct acpi_handle_list *list)
}
EXPORT_SYMBOL_GPL(acpi_handle_list_free);
/**
* acpi_device_dep - Check ACPI device dependency
* @target: ACPI handle of the target ACPI device.
* @match: ACPI handle to look up in the target's _DEP list.
*
* Return true if @match is present in the list returned by _DEP for
* @target or false otherwise.
*/
bool acpi_device_dep(acpi_handle target, acpi_handle match)
{
struct acpi_handle_list dep_devices;
bool ret = false;
int i;
if (!acpi_has_method(target, "_DEP"))
return false;
if (!acpi_evaluate_reference(target, "_DEP", NULL, &dep_devices)) {
acpi_handle_debug(target, "Failed to evaluate _DEP.\n");
return false;
}
for (i = 0; i < dep_devices.count; i++) {
if (dep_devices.handles[i] == match) {
ret = true;
break;
}
}
acpi_handle_list_free(&dep_devices);
return ret;
}
EXPORT_SYMBOL_GPL(acpi_device_dep);
acpi_status
acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld)
{
@ -824,54 +840,6 @@ bool acpi_check_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 funcs)
}
EXPORT_SYMBOL(acpi_check_dsm);
/**
* acpi_dev_uid_match - Match device by supplied UID
* @adev: ACPI device to match.
* @uid2: Unique ID of the device.
*
* Matches UID in @adev with given @uid2.
*
* Returns:
* - %true if matches.
* - %false otherwise.
*/
bool acpi_dev_uid_match(struct acpi_device *adev, const char *uid2)
{
const char *uid1 = acpi_device_uid(adev);
return uid1 && uid2 && !strcmp(uid1, uid2);
}
EXPORT_SYMBOL_GPL(acpi_dev_uid_match);
/**
* acpi_dev_hid_uid_match - Match device by supplied HID and UID
* @adev: ACPI device to match.
* @hid2: Hardware ID of the device.
* @uid2: Unique ID of the device, pass NULL to not check _UID.
*
* Matches HID and UID in @adev with given @hid2 and @uid2. Absence of @uid2
* will be treated as a match. If user wants to validate @uid2, it should be
* done before calling this function.
*
* Returns:
* - %true if matches or @uid2 is NULL.
* - %false otherwise.
*/
bool acpi_dev_hid_uid_match(struct acpi_device *adev,
const char *hid2, const char *uid2)
{
const char *hid1 = acpi_device_hid(adev);
if (strcmp(hid1, hid2))
return false;
if (!uid2)
return true;
return acpi_dev_uid_match(adev, uid2);
}
EXPORT_SYMBOL(acpi_dev_hid_uid_match);
/**
* acpi_dev_uid_to_integer - treat ACPI device _UID as integer
* @adev: ACPI device to get _UID from

View File

@ -18,8 +18,6 @@ static long __init parse_acpi_path(const struct efi_dev_path *node,
struct acpi_device *adev;
struct device *phys_dev;
char hid[ACPI_ID_LEN];
u64 uid;
int ret;
if (node->header.length != 12)
return -EINVAL;
@ -31,10 +29,9 @@ static long __init parse_acpi_path(const struct efi_dev_path *node,
node->acpi.hid >> 16);
for_each_acpi_dev_match(adev, hid, NULL, -1) {
ret = acpi_dev_uid_to_integer(adev, &uid);
if (ret == 0 && node->acpi.uid == uid)
if (acpi_dev_uid_match(adev, node->acpi.uid))
break;
if (ret == -ENODATA && node->acpi.uid == 0)
if (!acpi_device_uid(adev) && node->acpi.uid == 0)
break;
}
if (!adev)

View File

@ -1108,7 +1108,6 @@ static int arm_cspmu_request_irq(struct arm_cspmu *cspmu)
static inline int arm_cspmu_find_cpu_container(int cpu, u32 container_uid)
{
u64 acpi_uid;
struct device *cpu_dev;
struct acpi_device *acpi_dev;
@ -1118,8 +1117,7 @@ static inline int arm_cspmu_find_cpu_container(int cpu, u32 container_uid)
acpi_dev = ACPI_COMPANION(cpu_dev);
while (acpi_dev) {
if (acpi_dev_hid_uid_match(acpi_dev, ACPI_PROCESSOR_CONTAINER_HID, NULL) &&
!acpi_dev_uid_to_integer(acpi_dev, &acpi_uid) && acpi_uid == container_uid)
if (acpi_dev_hid_uid_match(acpi_dev, ACPI_PROCESSOR_CONTAINER_HID, container_uid))
return 0;
acpi_dev = acpi_dev_parent(acpi_dev);

View File

@ -454,7 +454,7 @@ static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info)
debug_info->panicinfo_blob.data = data;
debug_info->panicinfo_blob.size = ret;
debugfs_create_blob("panicinfo", S_IFREG | 0444, debug_info->dir,
debugfs_create_blob("panicinfo", 0444, debug_info->dir,
&debug_info->panicinfo_blob);
return 0;

View File

@ -736,34 +736,6 @@ do { \
#define san_consumer_warn(dev, handle, fmt, ...) \
san_consumer_printk(warn, dev, handle, fmt, ##__VA_ARGS__)
static bool is_san_consumer(struct platform_device *pdev, acpi_handle handle)
{
struct acpi_handle_list dep_devices;
acpi_handle supplier = ACPI_HANDLE(&pdev->dev);
acpi_status status;
bool ret = false;
int i;
if (!acpi_has_method(handle, "_DEP"))
return false;
status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices);
if (ACPI_FAILURE(status)) {
san_consumer_dbg(&pdev->dev, handle, "failed to evaluate _DEP\n");
return false;
}
for (i = 0; i < dep_devices.count; i++) {
if (dep_devices.handles[i] == supplier) {
ret = true;
break;
}
}
acpi_handle_list_free(&dep_devices);
return ret;
}
static acpi_status san_consumer_setup(acpi_handle handle, u32 lvl,
void *context, void **rv)
{
@ -772,7 +744,7 @@ static acpi_status san_consumer_setup(acpi_handle handle, u32 lvl,
struct acpi_device *adev;
struct device_link *link;
if (!is_san_consumer(pdev, handle))
if (!acpi_device_dep(handle, ACPI_HANDLE(&pdev->dev)))
return AE_OK;
/* Ignore ACPI devices that are not present. */

View File

@ -151,13 +151,13 @@ static int vendor_resource_matches(struct pnp_dev *dev,
static void pnpacpi_parse_allocated_vendor(struct pnp_dev *dev,
struct acpi_resource_vendor_typed *vendor)
{
if (vendor_resource_matches(dev, vendor, &hp_ccsr_uuid, 16)) {
u64 start, length;
struct { u64 start, length; } range;
memcpy(&start, vendor->byte_data, sizeof(start));
memcpy(&length, vendor->byte_data + 8, sizeof(length));
pnp_add_mem_resource(dev, start, start + length - 1, 0);
if (vendor_resource_matches(dev, vendor, &hp_ccsr_uuid,
sizeof(range))) {
memcpy(&range, vendor->byte_data, sizeof(range));
pnp_add_mem_resource(dev, range.start, range.start +
range.length - 1, 0);
}
}

View File

@ -76,10 +76,6 @@ config THERMAL_OF
Say 'Y' here if you need to build thermal infrastructure
based on device tree.
config THERMAL_ACPI
depends on ACPI
bool
config THERMAL_WRITABLE_TRIPS
bool "Enable writable trip points"
help

View File

@ -13,7 +13,6 @@ thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
# interface to/from other layers providing sensors
thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
thermal_sys-$(CONFIG_THERMAL_OF) += thermal_of.o
thermal_sys-$(CONFIG_THERMAL_ACPI) += thermal_acpi.o
# governors
CFLAGS_gov_power_allocator.o := -I$(src)

View File

@ -85,7 +85,7 @@ config INTEL_BXT_PMIC_THERMAL
config INTEL_PCH_THERMAL
tristate "Intel PCH Thermal Reporting Driver"
depends on X86 && PCI
select THERMAL_ACPI if ACPI
select ACPI_THERMAL_LIB if ACPI
help
Enable this to support thermal reporting on certain intel PCHs.
Thermal reporting device will provide temperature reading,

View File

@ -9,7 +9,7 @@ config INT340X_THERMAL
select THERMAL_GOV_USER_SPACE
select ACPI_THERMAL_REL
select ACPI_FAN
select THERMAL_ACPI
select ACPI_THERMAL_LIB
select INTEL_SOC_DTS_IOSF_CORE
select INTEL_TCC
select PROC_THERMAL_MMIO_RAPL if POWERCAP

View File

@ -1100,17 +1100,35 @@ static ssize_t read_file_blob(struct file *file, char __user *user_buf,
return r;
}
static ssize_t write_file_blob(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct debugfs_blob_wrapper *blob = file->private_data;
struct dentry *dentry = F_DENTRY(file);
ssize_t r;
r = debugfs_file_get(dentry);
if (unlikely(r))
return r;
r = simple_write_to_buffer(blob->data, blob->size, ppos, user_buf,
count);
debugfs_file_put(dentry);
return r;
}
static const struct file_operations fops_blob = {
.read = read_file_blob,
.write = write_file_blob,
.open = simple_open,
.llseek = default_llseek,
};
/**
* debugfs_create_blob - create a debugfs file that is used to read a binary blob
* debugfs_create_blob - create a debugfs file that is used to read and write
* a binary blob
* @name: a pointer to a string containing the name of the file to create.
* @mode: the read permission that the file should have (other permissions are
* masked out)
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
* directory dentry if set. If this parameter is %NULL, then the
* file will be created in the root of the debugfs filesystem.
@ -1119,7 +1137,7 @@ static const struct file_operations fops_blob = {
*
* This function creates a file in debugfs with the given name that exports
* @blob->data as a binary blob. If the @mode variable is so set it can be
* read from. Writing is not supported.
* read from and written to.
*
* This function will return a pointer to a dentry if it succeeds. This
* pointer must be passed to the debugfs_remove() function when the file is
@ -1134,7 +1152,7 @@ struct dentry *debugfs_create_blob(const char *name, umode_t mode,
struct dentry *parent,
struct debugfs_blob_wrapper *blob)
{
return debugfs_create_file_unsafe(name, mode & 0444, parent, blob, &fops_blob);
return debugfs_create_file_unsafe(name, mode & 0644, parent, blob, &fops_blob);
}
EXPORT_SYMBOL_GPL(debugfs_create_blob);

View File

@ -14,7 +14,7 @@
struct acpi_handle_list {
u32 count;
acpi_handle* handles;
acpi_handle *handles;
};
/* acpi_utils.h */
@ -25,16 +25,15 @@ acpi_status
acpi_evaluate_integer(acpi_handle handle,
acpi_string pathname,
struct acpi_object_list *arguments, unsigned long long *data);
acpi_status
acpi_evaluate_reference(acpi_handle handle,
acpi_string pathname,
struct acpi_object_list *arguments,
struct acpi_handle_list *list);
bool acpi_evaluate_reference(acpi_handle handle, acpi_string pathname,
struct acpi_object_list *arguments,
struct acpi_handle_list *list);
bool acpi_handle_list_equal(struct acpi_handle_list *list1,
struct acpi_handle_list *list2);
void acpi_handle_list_replace(struct acpi_handle_list *dst,
struct acpi_handle_list *src);
void acpi_handle_list_free(struct acpi_handle_list *list);
bool acpi_device_dep(acpi_handle target, acpi_handle match);
acpi_status
acpi_evaluate_ost(acpi_handle handle, u32 source_event, u32 status_code,
struct acpi_buffer *status_buf);
@ -366,6 +365,98 @@ struct acpi_device_data {
struct acpi_gpio_mapping;
#define ACPI_DEVICE_SWNODE_ROOT 0
/*
* The maximum expected number of CSI-2 data lanes.
*
* This number is not expected to ever have to be equal to or greater than the
* number of bits in an unsigned long variable, but if it needs to be increased
* above that limit, code will need to be adjusted accordingly.
*/
#define ACPI_DEVICE_CSI2_DATA_LANES 8
#define ACPI_DEVICE_SWNODE_PORT_NAME_LENGTH 8
enum acpi_device_swnode_dev_props {
ACPI_DEVICE_SWNODE_DEV_ROTATION,
ACPI_DEVICE_SWNODE_DEV_CLOCK_FREQUENCY,
ACPI_DEVICE_SWNODE_DEV_LED_MAX_MICROAMP,
ACPI_DEVICE_SWNODE_DEV_FLASH_MAX_MICROAMP,
ACPI_DEVICE_SWNODE_DEV_FLASH_MAX_TIMEOUT_US,
ACPI_DEVICE_SWNODE_DEV_NUM_OF,
ACPI_DEVICE_SWNODE_DEV_NUM_ENTRIES
};
enum acpi_device_swnode_port_props {
ACPI_DEVICE_SWNODE_PORT_REG,
ACPI_DEVICE_SWNODE_PORT_NUM_OF,
ACPI_DEVICE_SWNODE_PORT_NUM_ENTRIES
};
enum acpi_device_swnode_ep_props {
ACPI_DEVICE_SWNODE_EP_REMOTE_EP,
ACPI_DEVICE_SWNODE_EP_BUS_TYPE,
ACPI_DEVICE_SWNODE_EP_REG,
ACPI_DEVICE_SWNODE_EP_CLOCK_LANES,
ACPI_DEVICE_SWNODE_EP_DATA_LANES,
ACPI_DEVICE_SWNODE_EP_LANE_POLARITIES,
/* TX only */
ACPI_DEVICE_SWNODE_EP_LINK_FREQUENCIES,
ACPI_DEVICE_SWNODE_EP_NUM_OF,
ACPI_DEVICE_SWNODE_EP_NUM_ENTRIES
};
/*
* Each device has a root software node plus two times as many nodes as the
* number of CSI-2 ports.
*/
#define ACPI_DEVICE_SWNODE_PORT(port) (2 * (port) + 1)
#define ACPI_DEVICE_SWNODE_EP(endpoint) \
(ACPI_DEVICE_SWNODE_PORT(endpoint) + 1)
/**
* struct acpi_device_software_node_port - MIPI DisCo for Imaging CSI-2 port
* @port_name: Port name.
* @data_lanes: "data-lanes" property values.
* @lane_polarities: "lane-polarities" property values.
* @link_frequencies: "link_frequencies" property values.
* @port_nr: Port number.
* @crs_crs2_local: _CRS CSI2 record present (i.e. this is a transmitter one).
* @port_props: Port properties.
* @ep_props: Endpoint properties.
* @remote_ep: Reference to the remote endpoint.
*/
struct acpi_device_software_node_port {
char port_name[ACPI_DEVICE_SWNODE_PORT_NAME_LENGTH + 1];
u32 data_lanes[ACPI_DEVICE_CSI2_DATA_LANES];
u32 lane_polarities[ACPI_DEVICE_CSI2_DATA_LANES + 1 /* clock lane */];
u64 link_frequencies[ACPI_DEVICE_CSI2_DATA_LANES];
unsigned int port_nr;
bool crs_csi2_local;
struct property_entry port_props[ACPI_DEVICE_SWNODE_PORT_NUM_ENTRIES];
struct property_entry ep_props[ACPI_DEVICE_SWNODE_EP_NUM_ENTRIES];
struct software_node_ref_args remote_ep[1];
};
/**
* struct acpi_device_software_nodes - Software nodes for an ACPI device
* @dev_props: Device properties.
* @nodes: Software nodes for root as well as ports and endpoints.
* @nodeprts: Array of software node pointers, for (un)registering them.
* @ports: Information related to each port and endpoint within a port.
* @num_ports: The number of ports.
*/
struct acpi_device_software_nodes {
struct property_entry dev_props[ACPI_DEVICE_SWNODE_DEV_NUM_ENTRIES];
struct software_node *nodes;
const struct software_node **nodeptrs;
struct acpi_device_software_node_port *ports;
unsigned int num_ports;
};
/* Device */
struct acpi_device {
u32 pld_crc;
@ -384,6 +475,7 @@ struct acpi_device {
struct acpi_device_data data;
struct acpi_scan_handler *handler;
struct acpi_hotplug_context *hp;
struct acpi_device_software_nodes *swnodes;
const struct acpi_gpio_mapping *driver_gpios;
void *driver_data;
struct device dev;
@ -764,10 +856,71 @@ static inline bool acpi_device_can_poweroff(struct acpi_device *adev)
adev->power.states[ACPI_STATE_D3_HOT].flags.explicit_set);
}
bool acpi_dev_uid_match(struct acpi_device *adev, const char *uid2);
bool acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2);
int acpi_dev_uid_to_integer(struct acpi_device *adev, u64 *integer);
static inline bool acpi_dev_hid_match(struct acpi_device *adev, const char *hid2)
{
const char *hid1 = acpi_device_hid(adev);
return hid1 && hid2 && !strcmp(hid1, hid2);
}
static inline bool acpi_str_uid_match(struct acpi_device *adev, const char *uid2)
{
const char *uid1 = acpi_device_uid(adev);
return uid1 && uid2 && !strcmp(uid1, uid2);
}
static inline bool acpi_int_uid_match(struct acpi_device *adev, u64 uid2)
{
u64 uid1;
return !acpi_dev_uid_to_integer(adev, &uid1) && uid1 == uid2;
}
#define TYPE_ENTRY(type, x) \
const type: x, \
type: x
#define ACPI_STR_TYPES(match) \
TYPE_ENTRY(unsigned char *, match), \
TYPE_ENTRY(signed char *, match), \
TYPE_ENTRY(char *, match), \
TYPE_ENTRY(void *, match)
/**
* acpi_dev_uid_match - Match device by supplied UID
* @adev: ACPI device to match.
* @uid2: Unique ID of the device.
*
* Matches UID in @adev with given @uid2.
*
* Returns: %true if matches, %false otherwise.
*/
#define acpi_dev_uid_match(adev, uid2) \
_Generic(uid2, \
/* Treat @uid2 as a string for acpi string types */ \
ACPI_STR_TYPES(acpi_str_uid_match), \
/* Treat as an integer otherwise */ \
default: acpi_int_uid_match)(adev, uid2)
/**
* acpi_dev_hid_uid_match - Match device by supplied HID and UID
* @adev: ACPI device to match.
* @hid2: Hardware ID of the device.
* @uid2: Unique ID of the device, pass 0 or NULL to not check _UID.
*
* Matches HID and UID in @adev with given @hid2 and @uid2. Absence of @uid2
* will be treated as a match. If user wants to validate @uid2, it should be
* done before calling this function.
*
* Returns: %true if matches or @uid2 is 0 or NULL, %false otherwise.
*/
#define acpi_dev_hid_uid_match(adev, hid2, uid2) \
(acpi_dev_hid_match(adev, hid2) && \
(!(uid2) || acpi_dev_uid_match(adev, uid2)))
void acpi_dev_clear_dependencies(struct acpi_device *supplier);
bool acpi_dev_ready_for_enumeration(const struct acpi_device *device);
struct acpi_device *acpi_dev_get_next_consumer_dev(struct acpi_device *supplier,

View File

@ -75,6 +75,15 @@ static inline enum acpi_backlight_type acpi_video_get_backlight_type(void)
return __acpi_video_get_backlight_type(false, NULL);
}
/*
* This function MUST only be called by GPU drivers to check if the driver
* should register a backlight class device. This function not only checks
* if a GPU native backlight device should be registered it *also* tells
* the ACPI video-detect code that native GPU backlight control is available.
* Therefor calling this from any place other then the GPU driver is wrong!
* To check if GPU native backlight control is used in other places instead use:
* if (acpi_video_get_backlight_type() == acpi_backlight_native) { ... }
*/
static inline bool acpi_video_backlight_use_native(void)
{
return __acpi_video_get_backlight_type(true, NULL) == acpi_backlight_native;

View File

@ -424,6 +424,13 @@ extern int acpi_blacklisted(void);
extern void acpi_osi_setup(char *str);
extern bool acpi_osi_is_win8(void);
#ifdef CONFIG_ACPI_THERMAL_LIB
int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp);
int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp);
int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp);
int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp);
#endif
#ifdef CONFIG_ACPI_NUMA
int acpi_map_pxm_to_node(int pxm);
int acpi_get_node(acpi_handle handle);
@ -756,6 +763,10 @@ const char *acpi_get_subsystem_id(acpi_handle handle);
#define ACPI_HANDLE(dev) (NULL)
#define ACPI_HANDLE_FWNODE(fwnode) (NULL)
/* Get rid of the -Wunused-variable for adev */
#define acpi_dev_uid_match(adev, uid2) (adev && false)
#define acpi_dev_hid_uid_match(adev, hid2, uid2) (adev && false)
#include <acpi/acpi_numa.h>
struct fwnode_handle;
@ -772,17 +783,6 @@ static inline bool acpi_dev_present(const char *hid, const char *uid, s64 hrv)
struct acpi_device;
static inline bool acpi_dev_uid_match(struct acpi_device *adev, const char *uid2)
{
return false;
}
static inline bool
acpi_dev_hid_uid_match(struct acpi_device *adev, const char *hid2, const char *uid2)
{
return false;
}
static inline int acpi_dev_uid_to_integer(struct acpi_device *adev, u64 *integer)
{
return -ENODEV;

View File

@ -489,6 +489,13 @@ struct software_node {
const struct property_entry *properties;
};
#define SOFTWARE_NODE(_name_, _properties_, _parent_) \
(struct software_node) { \
.name = _name_, \
.properties = _properties_, \
.parent = _parent_, \
}
bool is_software_node(const struct fwnode_handle *fwnode);
const struct software_node *
to_software_node(const struct fwnode_handle *fwnode);

View File

@ -294,13 +294,6 @@ int thermal_zone_get_num_trips(struct thermal_zone_device *tz);
int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp);
#ifdef CONFIG_THERMAL_ACPI
int thermal_acpi_active_trip_temp(struct acpi_device *adev, int id, int *ret_temp);
int thermal_acpi_passive_trip_temp(struct acpi_device *adev, int *ret_temp);
int thermal_acpi_hot_trip_temp(struct acpi_device *adev, int *ret_temp);
int thermal_acpi_critical_trip_temp(struct acpi_device *adev, int *ret_temp);
#endif
#ifdef CONFIG_THERMAL
struct thermal_zone_device *thermal_zone_device_register_with_trips(
const char *type,

View File

@ -85,11 +85,6 @@ acpi_get_subtable_type(char *id)
return ACPI_SUBTABLE_COMMON;
}
static __init_or_acpilib bool has_handler(struct acpi_subtable_proc *proc)
{
return proc->handler || proc->handler_arg;
}
static __init_or_acpilib int call_handler(struct acpi_subtable_proc *proc,
union acpi_subtable_headers *hdr,
unsigned long end)
@ -133,7 +128,6 @@ acpi_parse_entries_array(char *id, unsigned long table_size,
unsigned long table_end, subtable_len, entry_len;
struct acpi_subtable_entry entry;
int count = 0;
int errs = 0;
int i;
table_end = (unsigned long)table_header + table_header->length;
@ -145,25 +139,19 @@ acpi_parse_entries_array(char *id, unsigned long table_size,
((unsigned long)table_header + table_size);
subtable_len = acpi_get_subtable_header_length(&entry);
while (((unsigned long)entry.hdr) + subtable_len < table_end) {
if (max_entries && count >= max_entries)
break;
while (((unsigned long)entry.hdr) + subtable_len < table_end) {
for (i = 0; i < proc_num; i++) {
if (acpi_get_entry_type(&entry) != proc[i].id)
continue;
if (!has_handler(&proc[i]) ||
(!errs &&
call_handler(&proc[i], entry.hdr, table_end))) {
errs++;
continue;
}
if (!max_entries || count < max_entries)
if (call_handler(&proc[i], entry.hdr, table_end))
return -EINVAL;
proc[i].count++;
count++;
break;
}
if (i != proc_num)
count++;
/*
* If entry->length is 0, break from this loop to avoid
@ -180,9 +168,9 @@ acpi_parse_entries_array(char *id, unsigned long table_size,
}
if (max_entries && count > max_entries) {
pr_warn("[%4.4s:0x%02x] found the maximum %i entries\n",
id, proc->id, count);
pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n",
id, proc->id, count - max_entries, count);
}
return errs ? -EINVAL : count;
return count;
}