diff options
53 files changed, 1007 insertions, 459 deletions
diff --git a/Documentation/admin-guide/acpi/fan_performance_states.rst b/Documentation/admin-guide/acpi/fan_performance_states.rst index 98fe5c333121..b9e4b4d146c1 100644 --- a/Documentation/admin-guide/acpi/fan_performance_states.rst +++ b/Documentation/admin-guide/acpi/fan_performance_states.rst @@ -60,3 +60,31 @@ For example:: When a given field is not populated or its value provided by the platform firmware is invalid, the "not-defined" string is shown instead of the value. + +ACPI Fan Fine Grain Control +============================= + +When _FIF object specifies support for fine grain control, then fan speed +can be set from 0 to 100% with the recommended minimum "step size" via +_FSL object. User can adjust fan speed using thermal sysfs cooling device. + +Here use can look at fan performance states for a reference speed (speed_rpm) +and set it by changing cooling device cur_state. If the fine grain control +is supported then user can also adjust to some other speeds which are +not defined in the performance states. + +The support of fine grain control is presented via sysfs attribute +"fine_grain_control". If fine grain control is present, this attribute +will show "1" otherwise "0". + +This sysfs attribute is presented in the same directory as performance states. + +ACPI Fan Performance Feedback +============================= + +The optional _FST object provides status information for the fan device. +This includes field to provide current fan speed in revolutions per minute +at which the fan is rotating. + +This speed is presented in the sysfs using the attribute "fan_speed_rpm", +in the same directory as performance states. diff --git a/Documentation/driver-api/gpio/board.rst b/Documentation/driver-api/gpio/board.rst index 191fa867826a..4e3adf31c8d1 100644 --- a/Documentation/driver-api/gpio/board.rst +++ b/Documentation/driver-api/gpio/board.rst @@ -71,14 +71,14 @@ with the help of _DSD (Device Specific Data), introduced in ACPI 5.1:: Device (FOO) { Name (_CRS, ResourceTemplate () { - GpioIo (Exclusive, ..., IoRestrictionOutputOnly, - "\\_SB.GPI0") {15} // red - GpioIo (Exclusive, ..., IoRestrictionOutputOnly, - "\\_SB.GPI0") {16} // green - GpioIo (Exclusive, ..., IoRestrictionOutputOnly, - "\\_SB.GPI0") {17} // blue - GpioIo (Exclusive, ..., IoRestrictionOutputOnly, - "\\_SB.GPI0") {1} // power + GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionOutputOnly, + "\\_SB.GPI0", 0, ResourceConsumer) { 15 } // red + GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionOutputOnly, + "\\_SB.GPI0", 0, ResourceConsumer) { 16 } // green + GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionOutputOnly, + "\\_SB.GPI0", 0, ResourceConsumer) { 17 } // blue + GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionOutputOnly, + "\\_SB.GPI0", 0, ResourceConsumer) { 1 } // power }) Name (_DSD, Package () { @@ -92,10 +92,7 @@ with the help of _DSD (Device Specific Data), introduced in ACPI 5.1:: ^FOO, 2, 0, 1, } }, - Package () { - "power-gpios", - Package () {^FOO, 3, 0, 0}, - }, + Package () { "power-gpios", Package () { ^FOO, 3, 0, 0 } }, } }) } diff --git a/Documentation/firmware-guide/acpi/enumeration.rst b/Documentation/firmware-guide/acpi/enumeration.rst index 74b830b2fd59..c414646a1bb4 100644 --- a/Documentation/firmware-guide/acpi/enumeration.rst +++ b/Documentation/firmware-guide/acpi/enumeration.rst @@ -19,16 +19,17 @@ possible we decided to do following: platform devices. - Devices behind real busses where there is a connector resource - are represented as struct spi_device or struct i2c_device - (standard UARTs are not busses so there is no struct uart_device). + are represented as struct spi_device or struct i2c_device. Note + that standard UARTs are not busses so there is no struct uart_device, + although some of them may be represented by sturct serdev_device. As both ACPI and Device Tree represent a tree of devices (and their resources) this implementation follows the Device Tree way as much as possible. -The ACPI implementation enumerates devices behind busses (platform, SPI and -I2C), creates the physical devices and binds them to their ACPI handle in -the ACPI namespace. +The ACPI implementation enumerates devices behind busses (platform, SPI, +I2C, and in some cases UART), creates the physical devices and binds them +to their ACPI handle in the ACPI namespace. This means that when ACPI_HANDLE(dev) returns non-NULL the device was enumerated from ACPI namespace. This handle can be used to extract other @@ -46,18 +47,16 @@ some minor changes. Adding ACPI support for an existing driver should be pretty straightforward. Here is the simplest example:: - #ifdef CONFIG_ACPI static const struct acpi_device_id mydrv_acpi_match[] = { /* ACPI IDs here */ { } }; MODULE_DEVICE_TABLE(acpi, mydrv_acpi_match); - #endif static struct platform_driver my_driver = { ... .driver = { - .acpi_match_table = ACPI_PTR(mydrv_acpi_match), + .acpi_match_table = mydrv_acpi_match, }, }; @@ -155,7 +154,7 @@ Here is what the ACPI namespace for a SPI slave might look like:: Device (EEP0) { Name (_ADR, 1) - Name (_CID, Package() { + Name (_CID, Package () { "ATML0025", "AT25", }) @@ -172,59 +171,51 @@ The SPI device drivers only need to add ACPI IDs in a similar way than with the platform device drivers. Below is an example where we add ACPI support to at25 SPI eeprom driver (this is meant for the above ACPI snippet):: - #ifdef CONFIG_ACPI static const struct acpi_device_id at25_acpi_match[] = { { "AT25", 0 }, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, at25_acpi_match); - #endif static struct spi_driver at25_driver = { .driver = { ... - .acpi_match_table = ACPI_PTR(at25_acpi_match), + .acpi_match_table = at25_acpi_match, }, }; Note that this driver actually needs more information like page size of the -eeprom etc. but at the time writing this there is no standard way of -passing those. One idea is to return this in _DSM method like:: +eeprom, etc. This information can be passed via _DSD method like:: Device (EEP0) { ... - Method (_DSM, 4, NotSerialized) + Name (_DSD, Package () { - Store (Package (6) + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { - "byte-len", 1024, - "addr-mode", 2, - "page-size, 32 - }, Local0) - - // Check UUIDs etc. - - Return (Local0) - } - -Then the at25 SPI driver can get this configuration by calling _DSM on its -ACPI handle like:: - - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_object_list input; - acpi_status status; + Package () { "size", 1024 }, + Package () { "pagesize", 32 }, + Package () { "address-width", 16 }, + } + }) + } - /* Fill in the input buffer */ +Then the at25 SPI driver can get this configuration by calling device property +APIs during ->probe() phase like:: - status = acpi_evaluate_object(ACPI_HANDLE(&spi->dev), "_DSM", - &input, &output); - if (ACPI_FAILURE(status)) - /* Handle the error */ + err = device_property_read_u32(dev, "size", &size); + if (err) + ...error handling... - /* Extract the data here */ + err = device_property_read_u32(dev, "pagesize", &page_size); + if (err) + ...error handling... - kfree(output.pointer); + err = device_property_read_u32(dev, "address-width", &addr_width); + if (err) + ...error handling... I2C serial bus support ====================== @@ -237,26 +228,24 @@ registered. Below is an example of how to add ACPI support to the existing mpu3050 input driver:: - #ifdef CONFIG_ACPI static const struct acpi_device_id mpu3050_acpi_match[] = { { "MPU3050", 0 }, - { }, + { } }; MODULE_DEVICE_TABLE(acpi, mpu3050_acpi_match); - #endif static struct i2c_driver mpu3050_i2c_driver = { .driver = { .name = "mpu3050", - .owner = THIS_MODULE, .pm = &mpu3050_pm, .of_match_table = mpu3050_of_match, - .acpi_match_table = ACPI_PTR(mpu3050_acpi_match), + .acpi_match_table = mpu3050_acpi_match, }, .probe = mpu3050_probe, .remove = mpu3050_remove, .id_table = mpu3050_ids, }; + module_i2c_driver(mpu3050_i2c_driver); Reference to PWM device ======================= @@ -282,9 +271,9 @@ introduced, i.e.:: } } } - }) ... + } In the above example the PWM-based LED driver references to the PWM channel 0 of \_SB.PCI0.PWM device with initial period setting equal to 600 ms (note that @@ -306,26 +295,13 @@ For example:: { Name (SBUF, ResourceTemplate() { - ... // Used to power on/off the device - GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, - IoRestrictionOutputOnly, "\\_SB.PCI0.GPI0", - 0x00, ResourceConsumer,,) - { - // Pin List - 0x0055 - } + GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionOutputOnly, + "\\_SB.PCI0.GPI0", 0, ResourceConsumer) { 85 } // Interrupt for the device - GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone, - 0x0000, "\\_SB.PCI0.GPI0", 0x00, ResourceConsumer,,) - { - // Pin list - 0x0058 - } - - ... - + GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone, 0, + "\\_SB.PCI0.GPI0", 0, ResourceConsumer) { 88 } } Return (SBUF) @@ -337,11 +313,12 @@ For example:: ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { - Package () {"power-gpios", Package() {^DEV, 0, 0, 0 }}, - Package () {"irq-gpios", Package() {^DEV, 1, 0, 0 }}, + Package () { "power-gpios", Package () { ^DEV, 0, 0, 0 } }, + Package () { "irq-gpios", Package () { ^DEV, 1, 0, 0 } }, } }) ... + } These GPIO numbers are controller relative and path "\\_SB.PCI0.GPI0" specifies the path to the controller. In order to use these GPIOs in Linux @@ -460,10 +437,10 @@ namespace link:: Device (TMP0) { Name (_HID, "PRP0001") - Name (_DSD, Package() { + Name (_DSD, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { - Package (2) { "compatible", "ti,tmp75" }, + Package () { "compatible", "ti,tmp75" }, } }) Method (_CRS, 0, Serialized) diff --git a/Documentation/firmware-guide/acpi/gpio-properties.rst b/Documentation/firmware-guide/acpi/gpio-properties.rst index df4b711053ee..eaec732cc77c 100644 --- a/Documentation/firmware-guide/acpi/gpio-properties.rst +++ b/Documentation/firmware-guide/acpi/gpio-properties.rst @@ -21,18 +21,18 @@ index, like the ASL example below shows:: Name (_CRS, ResourceTemplate () { GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionOutputOnly, - "\\_SB.GPO0", 0, ResourceConsumer) {15} + "\\_SB.GPO0", 0, ResourceConsumer) { 15 } GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionOutputOnly, - "\\_SB.GPO0", 0, ResourceConsumer) {27, 31} + "\\_SB.GPO0", 0, ResourceConsumer) { 27, 31 } }) Name (_DSD, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () - { - Package () {"reset-gpios", Package() {^BTH, 1, 1, 0 }}, - Package () {"shutdown-gpios", Package() {^BTH, 0, 0, 0 }}, + { + Package () { "reset-gpios", Package () { ^BTH, 1, 1, 0 } }, + Package () { "shutdown-gpios", Package () { ^BTH, 0, 0, 0 } }, } }) } @@ -123,17 +123,17 @@ Example:: // _DSD Hierarchical Properties Extension UUID ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), Package () { - Package () {"hog-gpio8", "G8PU"} + Package () { "hog-gpio8", "G8PU" } } }) Name (G8PU, Package () { ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), Package () { - Package () {"gpio-hog", 1}, - Package () {"gpios", Package () {8, 0}}, - Package () {"output-high", 1}, - Package () {"line-name", "gpio8-pullup"}, + Package () { "gpio-hog", 1 }, + Package () { "gpios", Package () { 8, 0 } }, + Package () { "output-high", 1 }, + Package () { "line-name", "gpio8-pullup" }, } }) @@ -266,15 +266,17 @@ have a device like below:: Name (_CRS, ResourceTemplate () { GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionNone, - "\\_SB.GPO0", 0, ResourceConsumer) {15} + "\\_SB.GPO0", 0, ResourceConsumer) { 15 } GpioIo (Exclusive, PullNone, 0, 0, IoRestrictionNone, - "\\_SB.GPO0", 0, ResourceConsumer) {27} + "\\_SB.GPO0", 0, ResourceConsumer) { 27 } }) } The driver might expect to get the right GPIO when it does:: desc = gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(desc)) + ...error handling... but since there is no way to know the mapping between "reset" and the GpioIo() in _CRS desc will hold ERR_PTR(-ENOENT). diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h index f386b90a79c8..9fab663dd2de 100644 --- a/arch/arm64/include/asm/topology.h +++ b/arch/arm64/include/asm/topology.h @@ -24,6 +24,10 @@ void update_freq_counters_refs(void); #define arch_scale_freq_capacity topology_get_freq_scale #define arch_scale_freq_invariant topology_scale_freq_invariant +#ifdef CONFIG_ACPI_CPPC_LIB +#define arch_init_invariance_cppc topology_init_cpu_capacity_cppc +#endif + /* Replace task scheduler's default cpu-invariant accounting */ #define arch_scale_cpu_capacity topology_get_cpu_scale diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h index 43a89476a522..9619385bf749 100644 --- a/arch/x86/include/asm/topology.h +++ b/arch/x86/include/asm/topology.h @@ -216,15 +216,26 @@ extern void arch_scale_freq_tick(void); #define arch_scale_freq_tick arch_scale_freq_tick extern void arch_set_max_freq_ratio(bool turbo_disabled); +void init_freq_invariance(bool secondary, bool cppc_ready); #else static inline void arch_set_max_freq_ratio(bool turbo_disabled) { } +static inline void init_freq_invariance(bool secondary, bool cppc_ready) +{ +} #endif -#if defined(CONFIG_ACPI_CPPC_LIB) && defined(CONFIG_SMP) +#ifdef CONFIG_ACPI_CPPC_LIB void init_freq_invariance_cppc(void); -#define init_freq_invariance_cppc init_freq_invariance_cppc +#define arch_init_invariance_cppc init_freq_invariance_cppc + +bool amd_set_max_freq_ratio(u64 *ratio); +#else +static inline bool amd_set_max_freq_ratio(u64 *ratio) +{ + return false; +} #endif #endif /* _ASM_X86_TOPOLOGY_H */ diff --git a/arch/x86/kernel/acpi/Makefile b/arch/x86/kernel/acpi/Makefile index cf340d85946a..fc17b3f136fe 100644 --- a/arch/x86/kernel/acpi/Makefile +++ b/arch/x86/kernel/acpi/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_ACPI) += boot.o obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o obj-$(CONFIG_ACPI_APEI) += apei.o -obj-$(CONFIG_ACPI_CPPC_LIB) += cppc_msr.o +obj-$(CONFIG_ACPI_CPPC_LIB) += cppc.o ifneq ($(CONFIG_ACPI_PROCESSOR),) obj-y += cstate.o diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 5b6d1a95776f..0d01e7f5078c 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -1328,6 +1328,17 @@ static int __init disable_acpi_pci(const struct dmi_system_id *d) return 0; } +static int __init disable_acpi_xsdt(const struct dmi_system_id *d) +{ + if (!acpi_force) { + pr_notice("%s detected: force use of acpi=rsdt\n", d->ident); + acpi_gbl_do_not_use_xsdt = TRUE; + } else { + pr_notice("Warning: DMI blacklist says broken, but acpi XSDT forced\n"); + } + return 0; +} + static int __init dmi_disable_acpi(const struct dmi_system_id *d) { if (!acpi_force) { @@ -1451,6 +1462,19 @@ static const struct dmi_system_id acpi_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"), }, }, + /* + * Boxes that need ACPI XSDT use disabled due to corrupted tables + */ + { + .callback = disable_acpi_xsdt, + .ident = "Advantech DAC-BJ01", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "NEC"), + DMI_MATCH(DMI_PRODUCT_NAME, "Bearlake CRB Board"), + DMI_MATCH(DMI_BIOS_VERSION, "V1.12"), + DMI_MATCH(DMI_BIOS_DATE, "02/01/2011"), + }, + }, {} }; diff --git a/arch/x86/kernel/acpi/cppc.c b/arch/x86/kernel/acpi/cppc.c new file mode 100644 index 000000000000..df1644d9b3b6 --- /dev/null +++ b/arch/x86/kernel/acpi/cppc.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cppc.c: CPPC Interface for x86 + * Copyright (c) 2016, Intel Corporation. + */ + +#include <acpi/cppc_acpi.h> +#include <asm/msr.h> +#include <asm/processor.h> +#include <asm/topology.h> + +/* Refer to drivers/acpi/cppc_acpi.c for the description of functions */ + +bool cpc_ffh_supported(void) +{ + return true; +} + +int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val) +{ + int err; + + err = rdmsrl_safe_on_cpu(cpunum, reg->address, val); + if (!err) { + u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1, + reg->bit_offset); + + *val &= mask; + *val >>= reg->bit_offset; + } + return err; +} + +int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val) +{ + u64 rd_val; + int err; + + err = rdmsrl_safe_on_cpu(cpunum, reg->address, &rd_val); + if (!err) { + u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1, + reg->bit_offset); + + val <<= reg->bit_offset; + val &= mask; + rd_val &= ~mask; + rd_val |= val; + err = wrmsrl_safe_on_cpu(cpunum, reg->address, rd_val); + } + return err; +} + +bool amd_set_max_freq_ratio(u64 *ratio) +{ + struct cppc_perf_caps perf_caps; + u64 highest_perf, nominal_perf; + u64 perf_ratio; + int rc; + + if (!ratio) + return false; + + rc = cppc_get_perf_caps(0, &perf_caps); + if (rc) { + pr_debug("Could not retrieve perf counters (%d)\n", rc); + return false; + } + + highest_perf = amd_get_highest_perf(); + nominal_perf = perf_caps.nominal_perf; + + if (!highest_perf || !nominal_perf) { + pr_debug("Could not retrieve highest or nominal performance\n"); + return false; + } + + perf_ratio = div_u64(highest_perf * SCHED_CAPACITY_SCALE, nominal_perf); + /* midpoint between max_boost and max_P */ + perf_ratio = (perf_ratio + SCHED_CAPACITY_SCALE) >> 1; + if (!perf_ratio) { + pr_debug("Non-zero highest/nominal perf values led to a 0 ratio\n"); + return false; + } + + *ratio = perf_ratio; + arch_set_max_freq_ratio(false); + + return true; +} + +static DEFINE_MUTEX(freq_invariance_lock); + +void init_freq_invariance_cppc(void) +{ + static bool secondary; + + mutex_lock(&freq_invariance_lock); + + init_freq_invariance(secondary, true); + secondary = true; + + mutex_unlock(&freq_invariance_lock); +} diff --git a/arch/x86/kernel/acpi/cppc_msr.c b/arch/x86/kernel/acpi/cppc_msr.c deleted file mode 100644 index b961de569e7e..000000000000 --- a/arch/x86/kernel/acpi/cppc_msr.c +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * cppc_msr.c: MSR Interface for CPPC - * Copyright (c) 2016, Intel Corporation. - */ - -#include <acpi/cppc_acpi.h> -#include <asm/msr.h> - -/* Refer to drivers/acpi/cppc_acpi.c for the description of functions */ - -bool cpc_ffh_supported(void) -{ - return true; -} - -int cpc_read_ffh(int cpunum, struct cpc_reg *reg, u64 *val) -{ - int err; - - err = rdmsrl_safe_on_cpu(cpunum, reg->address, val); - if (!err) { - u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1, - reg->bit_offset); - - *val &= mask; - *val >>= reg->bit_offset; - } - return err; -} - -int cpc_write_ffh(int cpunum, struct cpc_reg *reg, u64 val) -{ - u64 rd_val; - int err; - - err = rdmsrl_safe_on_cpu(cpunum, reg->address, &rd_val); - if (!err) { - u64 mask = GENMASK_ULL(reg->bit_offset + reg->bit_width - 1, - reg->bit_offset); - - val <<= reg->bit_offset; - val &= mask; - rd_val &= ~mask; - rd_val |= val; - err = wrmsrl_safe_on_cpu(cpunum, reg->address, rd_val); - } - return err; -} diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 617012f4619f..2ef14772dc04 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -83,10 +83,6 @@ #include <asm/hw_irq.h> #include <asm/stackprotector.h> -#ifdef CONFIG_ACPI_CPPC_LIB -#include <acpi/cppc_acpi.h> -#endif - /* representing HT siblings of each logical CPU */ DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_sibling_map); EXPORT_PER_CPU_SYMBOL(cpu_sibling_map); @@ -155,8 +151,6 @@ static inline void smpboot_restore_warm_reset_vector(void) *((volatile u32 *)phys_to_virt(TRAMPOLINE_PHYS_LOW)) = 0; } -static void init_freq_invariance(bool secondary, bool cppc_ready); - /* * Report back to the Boot Processor during boot time or to the caller processor * during CPU online. @@ -2097,48 +2091,6 @@ out: return true; } -#ifdef CONFIG_ACPI_CPPC_LIB -static bool amd_set_max_freq_ratio(void) -{ - struct cppc_perf_caps perf_caps; - u64 highest_perf, nominal_perf; - u64 perf_ratio; - int rc; - - rc = cppc_get_perf_caps(0, &perf_caps); - if (rc) { - pr_debug("Could not retrieve perf counters (%d)\n", rc); - return false; - } - - highest_perf = amd_get_highest_perf(); - nominal_perf = perf_caps.nominal_perf; - - if (!highest_perf || !nominal_perf) { - pr_debug("Could not retrieve highest or nominal performance\n"); - return false; - } - - perf_ratio = div_u64(highest_perf * SCHED_CAPACITY_SCALE, nominal_perf); - /* midpoint between max_boost and max_P */ - perf_ratio = (perf_ratio + SCHED_CAPACITY_SCALE) >> 1; - if (!perf_ratio) { - pr_debug("Non-zero highest/nominal perf values led to a 0 ratio\n"); - return false; - } - - arch_turbo_freq_ratio = perf_ratio; - arch_set_max_freq_ratio(false); - - return true; -} -#else -static bool amd_set_max_freq_ratio(void) -{ - return false; -} -#endif - static void init_counter_refs(void) { u64 aperf, mperf; @@ -2167,7 +2119,7 @@ static void register_freq_invariance_syscore_ops(void) static inline void register_freq_invariance_syscore_ops(void) {} #endif -static void init_freq_invariance(bool secondary, bool cppc_ready) +void init_freq_invariance(bool secondary, bool cppc_ready) { bool ret = false; @@ -2187,7 +2139,7 @@ static void init_freq_invariance(bool secondary, bool cppc_ready) if (!cppc_ready) { return; } - ret = amd_set_max_freq_ratio(); + ret = amd_set_max_freq_ratio(&arch_turbo_freq_ratio); } if (ret) { @@ -2200,22 +2152,6 @@ static void init_freq_invariance(bool secondary, bool cppc_ready) } } -#ifdef CONFIG_ACPI_CPPC_LIB -static DEFINE_MUTEX(freq_invariance_lock); - -void init_freq_invariance_cppc(void) -{ - static bool secondary; - - mutex_lock(&freq_invariance_lock); - - init_freq_invariance(secondary, true); - secondary = true; - - mutex_unlock(&freq_invariance_lock); -} -#endif - static void disable_freq_invariance_workfn(struct work_struct *work) { static_branch_disable(&arch_scale_freq_key); @@ -2264,8 +2200,4 @@ error: pr_warn("Scheduler frequency invariance went wobbly, disabling!\n"); schedule_work(&disable_freq_invariance_work); } -#else -static inline void init_freq_invariance(bool secondary, bool cppc_ready) -{ -} #endif /* CONFIG_X86_64 */ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 273741dedfd2..1e34f846508f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -302,7 +302,7 @@ config ACPI_IPMI help This driver enables the ACPI to access the BMC controller. And it uses the IPMI request/response message to communicate with BMC - controller, which can be found on on the server. + controller, which can be found on the server. To compile this driver as a module, choose M here: the module will be called as acpi_ipmi. diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index bb757148e7ba..b5a8d3e00a52 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -81,6 +81,9 @@ obj-$(CONFIG_ACPI_AC) += ac.o obj-$(CONFIG_ACPI_BUTTON) += button.o obj-$(CONFIG_ACPI_TINY_POWER_BUTTON) += tiny-power-button.o obj-$(CONFIG_ACPI_FAN) += fan.o +fan-objs := fan_core.o +fan-objs += fan_attr.o + obj-$(CONFIG_ACPI_VIDEO) += video.o obj-$(CONFIG_ACPI_TAD) += acpi_tad.o obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index bcae0f03572b..fbe0756259c5 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -21,6 +21,7 @@ #include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/pwm.h> +#include <linux/pxa2xx_ssp.h> #include <linux/suspend.h> #include <linux/delay.h> @@ -82,7 +83,7 @@ struct lpss_device_desc { const char *clk_con_id; unsigned int prv_offset; size_t prv_size_override; - struct property_entry *properties; + const struct property_entry *properties; void (*setup)(struct lpss_private_data *pdata); bool resume_from_noirq; }; @@ -219,10 +220,16 @@ static void bsw_pwm_setup(struct lpss_private_data *pdata) pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup)); } -static const struct lpss_device_desc lpt_dev_desc = { +static const struct property_entry lpt_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_LPT_SSP), + { } +}; + +static const struct lpss_device_desc lpt_spi_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR | LPSS_SAVE_CTX, .prv_offset = 0x800, + .properties = lpt_spi_properties, }; static const struct lpss_device_desc lpt_i2c_dev_desc = { @@ -282,9 +289,15 @@ static const struct lpss_device_desc bsw_uart_dev_desc = { .properties = uart_properties, }; +static const struct property_entry byt_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BYT_SSP), + { } +}; + static const struct lpss_device_desc byt_spi_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, .prv_offset = 0x400, + .properties = byt_spi_properties, }; static const struct lpss_device_desc byt_sdio_dev_desc = { @@ -305,11 +318,17 @@ static const struct lpss_device_desc bsw_i2c_dev_desc = { .resume_from_noirq = true, }; +static const struct property_entry bsw_spi_properties[] = { + PROPERTY_ENTRY_U32("intel,spi-pxa2xx-type", LPSS_BSW_SSP), + { } +}; + static const struct lpss_device_desc bsw_spi_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX | LPSS_NO_D3_DELAY, .prv_offset = 0x400, .setup = lpss_deassert_reset, + .properties = bsw_spi_properties, }; static const struct x86_cpu_id lpss_cpu_ids[] = { @@ -329,8 +348,8 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = { { "INTL9C60", LPSS_ADDR(lpss_dma_desc) }, /* Lynxpoint LPSS devices */ - { "INT33C0", LPSS_ADDR(lpt_dev_desc) }, - { "INT33C1", LPSS_ADDR(lpt_dev_desc) }, + { "INT33C0", LPSS_ADDR(lpt_spi_dev_desc) }, + { "INT33C1", LPSS_ADDR(lpt_spi_dev_desc) }, { "INT33C2", LPSS_ADDR(lpt_i2c_dev_desc) }, { "INT33C3", LPSS_ADDR(lpt_i2c_dev_desc) }, { "INT33C4", LPSS_ADDR(lpt_uart_dev_desc) }, @@ -356,8 +375,8 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = { { "808622C1", LPSS_ADDR(bsw_i2c_dev_desc) }, /* Broadwell LPSS devices */ - { "INT3430", LPSS_ADDR(lpt_dev_desc) }, - { "INT3431", LPSS_ADDR(lpt_dev_desc) }, + { "INT3430", LPSS_ADDR(lpt_spi_dev_desc) }, + { "INT3431", LPSS_ADDR(lpt_spi_dev_desc) }, { "INT3432", LPSS_ADDR(lpt_i2c_dev_desc) }, { "INT3433", LPSS_ADDR(lpt_i2c_dev_desc) }, { "INT3434", LPSS_ADDR(lpt_uart_dev_desc) }, @@ -366,7 +385,7 @@ static const struct acpi_device_id acpi_lpss_device_ids[] = { { "INT3437", }, /* Wildcat Point LPSS devices */ - { "INT3438", LPSS_ADDR(lpt_dev_desc) }, + { "INT3438", LPSS_ADDR(lpt_spi_dev_desc) }, { } }; diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 78d621290a35..de3cbf152dee 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -95,7 +95,7 @@ static void acpi_platform_fill_resource(struct acpi_device *adev, * Name of the platform device will be the same as @adev's. */ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, - struct property_entry *properties) + const struct property_entry *properties) { struct platform_device *pdev = NULL; struct platform_device_info pdevinfo; diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c index 915c2433463d..e7c30ce06e18 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -169,6 +169,9 @@ acpi_ns_walk_namespace(acpi_object_type type, if (start_node == ACPI_ROOT_OBJECT) { start_node = acpi_gbl_root_node; + if (!start_node) { + return_ACPI_STATUS(AE_NO_NAMESPACE); + } } /* Null child means "get first node" */ diff --git a/drivers/acpi/apei/bert.c b/drivers/acpi/apei/bert.c index 19e50fcbf4d6..598fd19b65fa 100644 --- a/drivers/acpi/apei/bert.c +++ b/drivers/acpi/apei/bert.c @@ -29,6 +29,7 @@ #undef pr_fmt #define pr_fmt(fmt) "BERT: " fmt +#define ACPI_BERT_PRINT_MAX_LEN 1024 static int bert_disable; @@ -58,8 +59,11 @@ static void __init bert_print_all(struct acpi_bert_region *region, } pr_info_once("Error records from previous boot:\n"); - - cper_estatus_print(KERN_INFO HW_ERR, estatus); + if (region_len < ACPI_BERT_PRINT_MAX_LEN) + cper_estatus_print(KERN_INFO HW_ERR, estatus); + else + pr_info_once("Max print length exceeded, table data is available at:\n" + "/sys/firmware/acpi/tables/data/BERT"); /* * Because the boot error source is "one-time polled" type, @@ -77,7 +81,7 @@ static int __init setup_bert_disable(char *str) { bert_disable = 1; - return 0; + return 1; } __setup("bert_disable", setup_bert_disable); diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 242f3c2d5533..698d67cee052 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -891,7 +891,7 @@ EXPORT_SYMBOL_GPL(erst_clear); static int __init setup_erst_disable(char *str) { erst_disable = 1; - return 0; + return 1; } __setup("erst_disable", setup_erst_disable); diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 0c5c9acc6254..d91ad378c00d 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -1457,33 +1457,35 @@ static struct platform_driver ghes_platform_driver = { .remove = ghes_remove, }; -static int __init ghes_init(void) +void __init acpi_ghes_init(void) { int rc; + sdei_init(); + if (acpi_disabled) - return -ENODEV; + return; switch (hest_disable) { case HEST_NOT_FOUND: - return -ENODEV; + return; case HEST_DISABLED: pr_info(GHES_PFX "HEST is not enabled!\n"); - return -EINVAL; + return; default: break; } if (ghes_disable) { pr_info(GHES_PFX "GHES is not enabled!\n"); - return -EINVAL; + return; } ghes_nmi_init_cxt(); rc = platform_driver_register(&ghes_platform_driver); if (rc) - goto err; + return; rc = apei_osc_setup(); if (rc == 0 && osc_sb_apei_support_acked) @@ -1494,9 +1496,4 @@ static int __init ghes_init(void) pr_info(GHES_PFX "APEI firmware first mode is enabled by APEI bit.\n"); else pr_info(GHES_PFX "Failed to enable APEI firmware first mode.\n"); - - return 0; -err: - return rc; } -device_initcall(ghes_init); diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c index 0edc1ed47673..6aef1ee5e1bd 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c @@ -224,7 +224,7 @@ err: static int __init setup_hest_disable(char *str) { hest_disable = HEST_DISABLED; - return 0; + return 1; } __setup("hest_disable", setup_hest_disable); diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig index 6dba187f4f2e..d4a72835f328 100644 --- a/drivers/acpi/arm64/Kconfig +++ b/drivers/acpi/arm64/Kconfig @@ -8,3 +8,13 @@ config ACPI_IORT config ACPI_GTDT bool + +config ACPI_AGDI + bool "Arm Generic Diagnostic Dump and Reset Device Interface" + depends on ARM_SDE_INTERFACE + help + Arm Generic Diagnostic Dump and Reset Device Interface (AGDI) is + a standard that enables issuing a non-maskable diagnostic dump and + reset command. + + If set, the kernel parses AGDI table and listens for the command. diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile index 66acbe77f46e..7b9e4045659d 100644 --- a/drivers/acpi/arm64/Makefile +++ b/drivers/acpi/arm64/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_ACPI_AGDI) += agdi.o obj-$(CONFIG_ACPI_IORT) += iort.o obj-$(CONFIG_ACPI_GTDT) += gtdt.o obj-y += dma.o diff --git a/drivers/acpi/arm64/agdi.c b/drivers/acpi/arm64/agdi.c new file mode 100644 index 000000000000..4df337d545b7 --- /dev/null +++ b/drivers/acpi/arm64/agdi.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This file implements handling of + * Arm Generic Diagnostic Dump and Reset Interface table (AGDI) + * + * Copyright (c) 2022, Ampere Computing LLC + */ + +#define pr_fmt(fmt) "ACPI: AGDI: " fmt + +#include <linux/acpi.h> +#include <linux/arm_sdei.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> + +struct agdi_data { + int sdei_event; +}; + +static int agdi_sdei_handler(u32 sdei_event, struct pt_regs *regs, void *arg) +{ + nmi_panic(regs, "Arm Generic Diagnostic Dump and Reset SDEI event issued"); + return 0; +} + +static int agdi_sdei_probe(struct platform_device *pdev, + struct agdi_data *adata) +{ + int err; + + err = sdei_event_register(adata->sdei_event, agdi_sdei_handler, pdev); + if (err) { + dev_err(&pdev->dev, "Failed to register for SDEI event %d", + adata->sdei_event); + return err; + } + + err = sdei_event_enable(adata->sdei_event); + if (err) { + sdei_event_unregister(adata->sdei_event); + dev_err(&pdev->dev, "Failed to enable event %d\n", + adata->sdei_event); + return err; + } + + return 0; +} + +static int agdi_probe(struct platform_device *pdev) +{ + struct agdi_data *adata = dev_get_platdata(&pdev->dev); + + if (!adata) + return -EINVAL; + + return agdi_sdei_probe(pdev, adata); +} + +static int agdi_remove(struct platform_device *pdev) +{ + struct agdi_data *adata = dev_get_platdata(&pdev->dev); + int err, i; + + err = sdei_event_disable(adata->sdei_event); + if (err) + return err; + + for (i = 0; i < 3; i++) { + err = sdei_event_unregister(adata->sdei_event); + if (err != -EINPROGRESS) + break; + + schedule(); + } + + return err; +} + +static struct platform_driver agdi_driver = { + .driver = { + .name = "agdi", + }, + .probe = agdi_probe, + .remove = agdi_remove, +}; + +void __init acpi_agdi_init(void) +{ + struct acpi_table_agdi *agdi_table; + struct agdi_data pdata; + struct platform_device *pdev; + acpi_status status; + + status = acpi_get_table(ACPI_SIG_AGDI, 0, + (struct acpi_table_header **) &agdi_table); + if (ACPI_FAILURE(status)) + return; + + if (agdi_table->flags & ACPI_AGDI_SIGNALING_MODE) { + pr_warn("Interrupt signaling is not supported"); + goto err_put_table; + } + + pdata.sdei_event = agdi_table->sdei_event; + + pdev = platform_device_register_data(NULL, "agdi", 0, &pdata, sizeof(pdata)); + if (IS_ERR(pdev)) + goto err_put_table; + + if (platform_driver_register(&agdi_driver)) + platform_device_unregister(pdev); + +err_put_table: + acpi_put_table((struct acpi_table_header *)agdi_table); +} diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index ea31ae01458b..dc208f5f5a1f 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -59,6 +59,10 @@ MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); static const struct acpi_device_id battery_device_ids[] = { {"PNP0C0A", 0}, + + /* Microsoft Surface Go 3 */ + {"MSHW0146", 0}, + {"", 0}, }; @@ -1148,6 +1152,14 @@ static const struct dmi_system_id bat_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad"), }, }, + { + /* Microsoft Surface Go 3 */ + .callback = battery_notification_delay_quirk, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"), + }, + }, {}, }; diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 07f604832fd6..3e58b613a2c4 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -26,6 +26,7 @@ #include <asm/mpspec.h> #include <linux/dmi.h> #endif +#include <linux/acpi_agdi.h> #include <linux/acpi_iort.h> #include <linux/acpi_viot.h> #include <linux/pci.h> @@ -283,6 +284,8 @@ EXPORT_SYMBOL_GPL(osc_pc_lpi_support_confirmed); bool osc_sb_native_usb4_support_confirmed; EXPORT_SYMBOL_GPL(osc_sb_native_usb4_support_confirmed); +bool osc_sb_cppc_not_supported; + static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; static void acpi_bus_osc_negotiate_platform_control(void) { @@ -332,21 +335,38 @@ static void acpi_bus_osc_negotiate_platform_control(void) if (ACPI_FAILURE(acpi_run_osc(handle, &context))) return; - kfree(context.ret.pointer); + capbuf_ret = context.ret.pointer; + if (context.ret.length <= OSC_SUPPORT_DWORD) { + kfree(context.ret.pointer); + return; + } + +#ifdef CONFIG_X86 + if (boot_cpu_has(X86_FEATURE_HWP)) + osc_sb_cppc_not_supported = !(capbuf_ret[OSC_SUPPORT_DWORD] & + (OSC_SB_CPC_SUPPORT | OSC_SB_CPCV2_SUPPORT)); +#endif - /* Now run _OSC again with query flag clear */ + /* + * Now run _OSC again with query flag clear and with the caps + * supported by both the OS and the platform. + */ capbuf[OSC_QUERY_DWORD] = 0; + capbuf[OSC_SUPPORT_DWORD] = capbuf_ret[OSC_SUPPORT_DWORD]; + kfree(context.ret.pointer); if (ACPI_FAILURE(acpi_run_osc(handle, &context))) return; capbuf_ret = context.ret.pointer; - osc_sb_apei_support_acked = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT; - osc_pc_lpi_support_confirmed = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT; - osc_sb_native_usb4_support_confirmed = - capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT; + if (context.ret.length > OSC_SUPPORT_DWORD) { + osc_sb_apei_support_acked = + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_APEI_SUPPORT; + osc_pc_lpi_support_confirmed = + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_PCLPI_SUPPORT; + osc_sb_native_usb4_support_confirmed = + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT; + } kfree(context.ret.pointer); } @@ -1043,7 +1063,12 @@ struct bus_type acpi_bus_type = { .remove = acpi_device_remove, .uevent = acpi_device_uevent, }; -EXPORT_SYMBOL_GPL(acpi_bus_type); + +int acpi_bus_for_each_dev(int (*fn)(struct device *, void *), void *data) +{ + return bus_for_each_dev(&acpi_bus_type, NULL, data, fn); +} +EXPORT_SYMBOL_GPL(acpi_bus_for_each_dev); /* -------------------------------------------------------------------------- Initialization/Cleanup @@ -1331,6 +1356,8 @@ static int __init acpi_init(void) pci_mmcfg_late_init(); acpi_iort_init(); + acpi_hest_init(); + acpi_ghes_init(); acpi_scan_init(); acpi_ec_init(); acpi_debugfs_init(); @@ -1339,6 +1366,7 @@ static int __init acpi_init(void) acpi_debugger_init(); acpi_setup_sb_notify_handler(); acpi_viot_init(); + acpi_agdi_init(); return 0; } diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 866560cbb082..d418449194ee 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -633,8 +633,8 @@ static bool is_cppc_supported(int revision, int num_ent) * ) */ -#ifndef init_freq_invariance_cppc -static inline void init_freq_invariance_cppc(void) { } +#ifndef arch_init_invariance_cppc +static inline void arch_init_invariance_cppc(void) { } #endif /** @@ -656,6 +656,9 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) acpi_status status; int ret = -EFAULT; + if (osc_sb_cppc_not_supported) + return -ENODEV; + /* Parse the ACPI _CPC table for this CPU. */ status = acpi_evaluate_object_typed(handle, "_CPC", NULL, &output, ACPI_TYPE_PACKAGE); @@ -816,7 +819,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr) goto out_free; } - init_freq_invariance_cppc(); + arch_init_invariance_cppc(); kfree(output.pointer); return 0; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 46710380a402..a1b871a418f8 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -168,7 +168,7 @@ struct acpi_ec_query { }; static int acpi_ec_submit_query(struct acpi_ec *ec); -static bool advance_transaction(struct acpi_ec *ec, bool interrupt); +static void advance_transaction(struct acpi_ec *ec, bool interrupt); static void acpi_ec_event_handler(struct work_struct *work); struct acpi_ec *first_ec; @@ -441,36 +441,35 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec) return true; } -static bool acpi_ec_submit_event(struct acpi_ec *ec) +static void acpi_ec_submit_event(struct acpi_ec *ec) { + /* + * It is safe to mask the events here, because acpi_ec_close_event() + * will run at least once after this. + */ acpi_ec_mask_events(ec); if (!acpi_ec_event_enabled(ec)) - return false; - - if (ec->event_state == EC_EVENT_READY) { - ec_dbg_evt("Command(%s) submitted/blocked", - acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); + return; - ec->event_state = EC_EVENT_IN_PROGRESS; - /* - * If events_to_process is greqter than 0 at this point, the - * while () loop in acpi_ec_event_handler() is still running - * and incrementing events_to_process will cause it to invoke - * acpi_ec_submit_query() once more, so it is not necessary to - * queue up the event work to start the same loop again. - */ - if (ec->events_to_process++ > 0) - return true; + if (ec->event_state != EC_EVENT_READY) + return; - ec->events_in_progress++; - return queue_work(ec_wq, &ec->work); - } + ec_dbg_evt("Command(%s) submitted/blocked", + acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); + ec->event_state = EC_EVENT_IN_PROGRESS; /* - * The event handling work has not been completed yet, so it needs to be - * flushed. + * If events_to_process is greater than 0 at this point, the while () + * loop in acpi_ec_event_handler() is still running and incrementing + * events_to_process will cause it to invoke acpi_ec_submit_query() once + * more, so it is not necessary to queue up the event work to start the + * same loop again. */ - return true; + if (ec->events_to_process++ > 0) + return; + + ec->events_in_progress++; + queue_work(ec_wq, &ec->work); } static void acpi_ec_complete_event(struct acpi_ec *ec) @@ -655,11 +654,10 @@ static void acpi_ec_spurious_interrupt(struct acpi_ec *ec, struct transaction *t acpi_ec_mask_events(ec); } -static bool advance_transaction(struct acpi_ec *ec, bool interrupt) +static void advance_transaction(struct acpi_ec *ec, bool interrupt) { struct transaction *t = ec->curr; bool wakeup = false; - bool ret = false; u8 status; ec_dbg_stm("%s (%d)", interrupt ? "IRQ" : "TASK", smp_processor_id()); @@ -724,12 +722,10 @@ static bool advance_transaction(struct acpi_ec *ec, bool interrupt) out: if (status & ACPI_EC_FLAG_SCI) - ret = acpi_ec_submit_event(ec); + acpi_ec_submit_event(ec); if (wakeup && interrupt) wake_up(&ec->wait); - - return ret; } static void start_transaction(struct acpi_ec *ec) @@ -1242,6 +1238,7 @@ static void acpi_ec_event_handler(struct work_struct *work) acpi_ec_submit_query(ec); spin_lock_irq(&ec->lock); + ec->events_to_process--; } @@ -1250,27 +1247,30 @@ static void acpi_ec_event_handler(struct work_struct *work) * event handling work again regardless of whether or not the query * queued up above is processed successfully. */ - if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) + if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) { + bool guard_timeout; + acpi_ec_complete_event(ec); - else - acpi_ec_close_event(ec); - spin_unlock_irq(&ec->lock); + ec_dbg_evt("Event stopped"); - ec_dbg_evt("Event stopped"); + spin_unlock_irq(&ec->lock); + + guard_timeout = !!ec_guard(ec); - if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && ec_guard(ec)) { spin_lock_irq(&ec->lock); /* Take care of SCI_EVT unless someone else is doing that. */ - if (!ec->curr) + if (guard_timeout && !ec->curr) advance_transaction(ec, false); + } else { + acpi_ec_close_event(ec); - spin_unlock_irq(&ec->lock); + ec_dbg_evt("Event stopped"); } - spin_lock_irq(&ec->lock); ec->events_in_progress--; + spin_unlock_irq(&ec->lock); } @@ -2051,6 +2051,11 @@ void acpi_ec_set_gpe_wake_mask(u8 action) acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action); } +static bool acpi_ec_work_in_progress(struct acpi_ec *ec) +{ + return ec->events_in_progress + ec->queries_in_progress > 0; +} + bool acpi_ec_dispatch_gpe(void) { bool work_in_progress = false; @@ -2081,8 +2086,12 @@ bool acpi_ec_dispatch_gpe(void) */ spin_lock_irq(&first_ec->lock); - if (acpi_ec_gpe_status_set(first_ec)) - work_in_progress = advance_transaction(first_ec, false); + if (acpi_ec_gpe_status_set(first_ec)) { + pm_pr_dbg("ACPI EC GPE status set\n"); + + advance_transaction(first_ec, false); + work_in_progress = acpi_ec_work_in_progress(first_ec); + } spin_unlock_irq(&first_ec->lock); @@ -2099,8 +2108,7 @@ bool acpi_ec_dispatch_gpe(void) spin_lock_irq(&first_ec->lock); - work_in_progress = first_ec->events_in_progress + - first_ec->queries_in_progress > 0; + work_in_progress = acpi_ec_work_in_progress(first_ec); spin_unlock_irq(&first_ec->lock); } while (work_in_progress && !pm_wakeup_pending()); diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index dd9bb8ca2244..44728529a5b6 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -6,9 +6,53 @@ * * Add new device IDs before the generic ACPI fan one. */ + +#ifndef _ACPI_FAN_H_ +#define _ACPI_FAN_H_ + #define ACPI_FAN_DEVICE_IDS \ {"INT3404", }, /* Fan */ \ {"INTC1044", }, /* Fan for Tiger Lake generation */ \ {"INTC1048", }, /* Fan for Alder Lake generation */ \ {"INTC10A2", }, /* Fan for Raptor Lake generation */ \ {"PNP0C0B", } /* Generic ACPI fan */ + +#define ACPI_FPS_NAME_LEN 20 + +struct acpi_fan_fps { + u64 control; + u64 trip_point; + u64 speed; + u64 noise_level; + u64 power; + char name[ACPI_FPS_NAME_LEN]; + struct device_attribute dev_attr; +}; + +struct acpi_fan_fif { + u8 revision; + u8 fine_grain_ctrl; + u8 step_size; + u8 low_speed_notification; +}; + +struct acpi_fan_fst { + u64 revision; + u64 control; + u64 speed; +}; + +struct acpi_fan { + bool acpi4; + struct acpi_fan_fif fif; + struct acpi_fan_fps *fps; + int fps_count; + struct thermal_cooling_device *cdev; + struct device_attribute fst_speed; + struct device_attribute fine_grain_control; +}; + +int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst); +int acpi_fan_create_attributes(struct acpi_device *device); +void acpi_fan_delete_attributes(struct acpi_device *device); +#endif diff --git a/drivers/acpi/fan_attr.c b/drivers/acpi/fan_attr.c new file mode 100644 index 000000000000..f15157d40713 --- /dev/null +++ b/drivers/acpi/fan_attr.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * fan_attr.c - Create extra attributes for ACPI Fan driver + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * Copyright (C) 2022 Intel Corporation. All rights reserved. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/acpi.h> + +#include "fan.h" + +MODULE_LICENSE("GPL"); + +static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr); + int count; + + if (fps->control == 0xFFFFFFFF || fps->control > 100) + count = scnprintf(buf, PAGE_SIZE, "not-defined:"); + else + count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control); + + if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9) + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); + else + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point); + + if (fps->speed == 0xFFFFFFFF) + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); + else + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed); + + if (fps->noise_level == 0xFFFFFFFF) + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); + else + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100); + + if (fps->power == 0xFFFFFFFF) + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n"); + else + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld\n", fps->power); + + return count; +} + +static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev); + struct acpi_fan_fst fst; + int status; + + status = acpi_fan_get_fst(acpi_dev, &fst); + if (status) + return status; + + return sprintf(buf, "%lld\n", fst.speed); +} + +static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev); + struct acpi_fan *fan = acpi_driver_data(acpi_dev); + + return sprintf(buf, "%d\n", fan->fif.fine_grain_ctrl); +} + +int acpi_fan_create_attributes(struct acpi_device *device) +{ + struct acpi_fan *fan = acpi_driver_data(device); + int i, status; + + sysfs_attr_init(&fan->fine_grain_control.attr); + fan->fine_grain_control.show = show_fine_grain_control; + fan->fine_grain_control.store = NULL; + fan->fine_grain_control.attr.name = "fine_grain_control"; + fan->fine_grain_control.attr.mode = 0444; + status = sysfs_create_file(&device->dev.kobj, &fan->fine_grain_control.attr); + if (status) + return status; + + /* _FST is present if we are here */ + sysfs_attr_init(&fan->fst_speed.attr); + fan->fst_speed.show = show_fan_speed; + fan->fst_speed.store = NULL; + fan->fst_speed.attr.name = "fan_speed_rpm"; + fan->fst_speed.attr.mode = 0444; + status = sysfs_create_file(&device->dev.kobj, &fan->fst_speed.attr); + if (status) + goto rem_fine_grain_attr; + + for (i = 0; i < fan->fps_count; ++i) { + struct acpi_fan_fps *fps = &fan->fps[i]; + + snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i); + sysfs_attr_init(&fps->dev_attr.attr); + fps->dev_attr.show = show_state; + fps->dev_attr.store = NULL; + fps->dev_attr.attr.name = fps->name; + fps->dev_attr.attr.mode = 0444; + status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr); + if (status) { + int j; + + for (j = 0; j < i; ++j) + sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr); + goto rem_fst_attr; + } + } + + return 0; + +rem_fst_attr: + sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr); + +rem_fine_grain_attr: + sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr); + + return status; +} + +void acpi_fan_delete_attributes(struct acpi_device *device) +{ + struct acpi_fan *fan = acpi_driver_data(device); + int i; + + for (i = 0; i < fan->fps_count; ++i) + sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); + + sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr); + sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr); +} diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan_core.c index 5cd0ceb50bc8..b9a9a59ddcc1 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan_core.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * acpi_fan.c - ACPI Fan Driver ($Revision: 29 $) + * fan_core.c - ACPI Fan core Driver * * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * Copyright (C) 2022 Intel Corporation. All rights reserved. */ #include <linux/kernel.h> @@ -45,33 +46,6 @@ static const struct dev_pm_ops acpi_fan_pm = { #define FAN_PM_OPS_PTR NULL #endif -#define ACPI_FPS_NAME_LEN 20 - -struct acpi_fan_fps { - u64 control; - u64 trip_point; - u64 speed; - u64 noise_level; - u64 power; - char name[ACPI_FPS_NAME_LEN]; - struct device_attribute dev_attr; -}; - -struct acpi_fan_fif { - u64 revision; - u64 fine_grain_ctrl; - u64 step_size; - u64 low_speed_notification; -}; - -struct acpi_fan { - bool acpi4; - struct acpi_fan_fif fif; - struct acpi_fan_fps *fps; - int fps_count; - struct thermal_cooling_device *cdev; -}; - static struct platform_driver acpi_fan_driver = { .probe = acpi_fan_probe, .remove = acpi_fan_remove, @@ -89,25 +63,29 @@ static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long struct acpi_device *device = cdev->devdata; struct acpi_fan *fan = acpi_driver_data(device); - if (fan->acpi4) - *state = fan->fps_count - 1; - else + if (fan->acpi4) { + if (fan->fif.fine_grain_ctrl) + *state = 100 / fan->fif.step_size; + else + *state = fan->fps_count - 1; + } else { *state = 1; + } + return 0; } -static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) +int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_fan *fan = acpi_driver_data(device); union acpi_object *obj; acpi_status status; - int control, i; + int ret = 0; status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer); if (ACPI_FAILURE(status)) { dev_err(&device->dev, "Get fan state failed\n"); - return status; + return -ENODEV; } obj = buffer.pointer; @@ -115,35 +93,52 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) obj->package.count != 3 || obj->package.elements[1].type != ACPI_TYPE_INTEGER) { dev_err(&device->dev, "Invalid _FST data\n"); - status = -EINVAL; + ret = -EINVAL; goto err; } - control = obj->package.elements[1].integer.value; + fst->revision = obj->package.elements[0].integer.value; + fst->control = obj->package.elements[1].integer.value; + fst->speed = obj->package.elements[2].integer.value; + +err: + kfree(obj); + return ret; +} + +static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) +{ + struct acpi_fan *fan = acpi_driver_data(device); + struct acpi_fan_fst fst; + int status, i; + + status = acpi_fan_get_fst(device, &fst); + if (status) + return status; + + if (fan->fif.fine_grain_ctrl) { + /* This control should be same what we set using _FSL by spec */ + if (fst.control > 100) { + dev_dbg(&device->dev, "Invalid control value returned\n"); + goto match_fps; + } + + *state = (int) fst.control / fan->fif.step_size; + return 0; + } + +match_fps: for (i = 0; i < fan->fps_count; i++) { - /* - * When Fine Grain Control is set, return the state - * corresponding to maximum fan->fps[i].control - * value compared to the current speed. Here the - * fan->fps[] is sorted array with increasing speed. - */ - if (fan->fif.fine_grain_ctrl && control < fan->fps[i].control) { - i = (i > 0) ? i - 1 : 0; + if (fst.control == fan->fps[i].control) break; - } else if (control == fan->fps[i].control) { - break; - } } if (i == fan->fps_count) { dev_dbg(&device->dev, "Invalid control value returned\n"); - status = -EINVAL; - goto err; + return -EINVAL; } *state = i; -err: - kfree(obj); return status; } @@ -187,15 +182,30 @@ static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state) { struct acpi_fan *fan = acpi_driver_data(device); acpi_status status; + u64 value = state; + int max_state; - if (state >= fan->fps_count) + if (fan->fif.fine_grain_ctrl) + max_state = 100 / fan->fif.step_size; + else + max_state = fan->fps_count - 1; + + if (state > max_state) return -EINVAL; - status = acpi_execute_simple_method(device->handle, "_FSL", - fan->fps[state].control); + if (fan->fif.fine_grain_ctrl) { + value *= fan->fif.step_size; + /* Spec allows compensate the last step only */ + if (value + fan->fif.step_size > 100) + value = 100; + } else { + value = fan->fps[state].control; + } + + status = acpi_execute_simple_method(device->handle, "_FSL", value); if (ACPI_FAILURE(status)) { dev_dbg(&device->dev, "Failed to set state by _FSL\n"); - return status; + return -ENODEV; } return 0; @@ -237,7 +247,8 @@ static int acpi_fan_get_fif(struct acpi_device *device) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_fan *fan = acpi_driver_data(device); struct acpi_buffer format = { sizeof("NNNN"), "NNNN" }; - struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif }; + u64 fields[4]; + struct acpi_buffer fif = { sizeof(fields), fields }; union acpi_object *obj; acpi_status status; @@ -258,6 +269,17 @@ static int acpi_fan_get_fif(struct acpi_device *device) status = -EINVAL; } + fan->fif.revision = fields[0]; + fan->fif.fine_grain_ctrl = fields[1]; + fan->fif.step_size = fields[2]; + fan->fif.low_speed_notification = fields[3]; + + /* If there is a bug in step size and set as 0, change to 1 */ + if (!fan->fif.step_size) + fan->fif.step_size = 1; + /* If step size > 9, change to 9 (by spec valid values 1-9) */ + else if (fan->fif.step_size > 9) + fan->fif.step_size = 9; err: kfree(obj); return status; @@ -270,39 +292,6 @@ static int acpi_fan_speed_cmp(const void *a, const void *b) return fps1->speed - fps2->speed; } -static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr); - int count; - - if (fps->control == 0xFFFFFFFF || fps->control > 100) - count = scnprintf(buf, PAGE_SIZE, "not-defined:"); - else - count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control); - - if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9) - count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); - else - count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point); - - if (fps->speed == 0xFFFFFFFF) - count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); - else - count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed); - - if (fps->noise_level == 0xFFFFFFFF) - count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); - else - count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100); - - if (fps->power == 0xFFFFFFFF) - count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n"); - else - count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld\n", fps->power); - - return count; -} - static int acpi_fan_get_fps(struct acpi_device *device) { struct acpi_fan *fan = acpi_driver_data(device); @@ -347,25 +336,6 @@ static int acpi_fan_get_fps(struct acpi_device *device) sort(fan->fps, fan->fps_count, sizeof(*fan->fps), acpi_fan_speed_cmp, NULL); - for (i = 0; i < fan->fps_count; ++i) { - struct acpi_fan_fps *fps = &fan->fps[i]; - - snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i); - sysfs_attr_init(&fps->dev_attr.attr); - fps->dev_attr.show = show_state; - fps->dev_attr.store = NULL; - fps->dev_attr.attr.name = fps->name; - fps->dev_attr.attr.mode = 0444; - status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr); - if (status) { - int j; - - for (j = 0; j < i; ++j) - sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr); - break; - } - } - err: kfree(obj); return status; @@ -396,6 +366,10 @@ static int acpi_fan_probe(struct platform_device *pdev) if (result) return result; + result = acpi_fan_create_attributes(device); + if (result) + return result; + fan->acpi4 = true; } else { result = acpi_device_update_power(device, NULL); @@ -437,12 +411,8 @@ static int acpi_fan_probe(struct platform_device *pdev) return 0; err_end: - if (fan->acpi4) { - int i; - - for (i = 0; i < fan->fps_count; ++i) - sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); - } + if (fan->acpi4) + acpi_fan_delete_attributes(device); return result; } @@ -453,10 +423,8 @@ static int acpi_fan_remove(struct platform_device *pdev) if (fan->acpi4) { struct acpi_device *device = ACPI_COMPANION(&pdev->dev); - int i; - for (i = 0; i < fan->fps_count; ++i) - sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); + acpi_fan_delete_attributes(device); } sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling"); sysfs_remove_link(&fan->cdev->device.kobj, "device"); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 457e11d851b8..628bf8f18130 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -96,8 +96,6 @@ void acpi_scan_table_notify(void); extern struct list_head acpi_bus_id_list; -#define ACPI_MAX_DEVICE_INSTANCES 4096 - struct acpi_device_bus_id { const char *bus_id; struct ida instance_ida; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 45c5c0e45e33..7a70c4bfc23c 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -642,22 +642,24 @@ u64 acpi_os_get_timer(void) (ACPI_100NSEC_PER_SEC / HZ); } -acpi_status acpi_os_read_port(acpi_io_address port, u32 * value, u32 width) +acpi_status acpi_os_read_port(acpi_io_address port, u32 *value, u32 width) { u32 dummy; - if (!value) + if (value) + *value = 0; + else value = &dummy; - *value = 0; if (width <= 8) { - *(u8 *) value = inb(port); + *value = inb(port); } else if (width <= 16) { - *(u16 *) value = inw(port); + *value = inw(port); } else if (width <= 32) { - *(u32 *) value = inl(port); + *value = inl(port); } else { - BUG(); + pr_debug("%s: Access width %d not supported\n", __func__, width); + return AE_BAD_PARAMETER; } return AE_OK; @@ -674,7 +676,8 @@ acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width) } else if (width <= 32) { outl(value, port); } else { - BUG(); + pr_debug("%s: Access width %d not supported\n", __func__, width); + return AE_BAD_PARAMETER; } return AE_OK; diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index d54fb8e54671..58647051c948 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -185,7 +185,7 @@ static acpi_status acpi_pci_link_check_current(struct acpi_resource *resource, if (!p || !p->interrupt_count) { /* * IRQ descriptors may have no IRQ# bits set, - * particularly those those w/ _STA disabled + * particularly those w/ _STA disabled */ pr_debug("Blank _CRS IRQ resource\n"); return AE_OK; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index b76db99cced3..6f9e75d14808 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -22,8 +22,6 @@ #include <linux/slab.h> #include <linux/dmi.h> #include <linux/platform_data/x86/apple.h> -#include <acpi/apei.h> /* for acpi_hest_init() */ - #include "internal.h" #define ACPI_PCI_ROOT_CLASS "pci_bridge" @@ -943,7 +941,6 @@ out_release_info: void __init acpi_pci_root_init(void) { - acpi_hest_init(); if (acpi_pci_disabled) return; diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index d0986bda2964..12bbfe833609 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -541,7 +541,8 @@ acpi_device_data_of_node(const struct fwnode_handle *fwnode) if (is_acpi_device_node(fwnode)) { const struct acpi_device *adev = to_acpi_device_node(fwnode); return &adev->data; - } else if (is_acpi_data_node(fwnode)) { + } + if (is_acpi_data_node(fwnode)) { const struct acpi_data_node *dn = to_acpi_data_node(fwnode); return &dn->data; } @@ -685,7 +686,7 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, */ if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) { if (index) - return -EINVAL; + return -ENOENT; device = acpi_fetch_acpi_dev(obj->reference.handle); if (!device) @@ -739,14 +740,19 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, return -EINVAL; } - /* assume following integer elements are all args */ + /* + * 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. + */ for (i = 0; element + i < end && i < num_args; i++) { int type = element[i].type; + if (type == ACPI_TYPE_LOCAL_REFERENCE) + break; if (type == ACPI_TYPE_INTEGER) nargs++; - else if (type == ACPI_TYPE_LOCAL_REFERENCE) - break; else return -EINVAL; } @@ -950,7 +956,7 @@ static int acpi_data_prop_read(const struct acpi_device_data *data, if (proptype != DEV_PROP_STRING && nval > obj->package.count) return -EOVERFLOW; - else if (nval <= 0) + if (nval == 0) return -EINVAL; items = obj->package.elements; @@ -1012,14 +1018,10 @@ struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode, const struct list_head *head; struct list_head *next; - if (!child || is_acpi_device_node(child)) { + if ((!child || is_acpi_device_node(child)) && adev) { struct acpi_device *child_adev; - if (adev) - head = &adev->children; - else - goto nondev; - + head = &adev->children; if (list_empty(head)) goto nondev; @@ -1089,7 +1091,8 @@ acpi_node_get_parent(const struct fwnode_handle *fwnode) if (is_acpi_data_node(fwnode)) { /* All data nodes have parent pointer so just return that */ return to_acpi_data_node(fwnode)->parent; - } else if (is_acpi_device_node(fwnode)) { + } + if (is_acpi_device_node(fwnode)) { struct device *dev = to_acpi_device_node(fwnode)->dev.parent; if (dev) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8b2e5ef15559..5ffd87ac42b3 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -477,7 +477,8 @@ static void acpi_device_del(struct acpi_device *device) list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) if (!strcmp(acpi_device_bus_id->bus_id, acpi_device_hid(device))) { - ida_simple_remove(&acpi_device_bus_id->instance_ida, device->pnp.instance_no); + ida_free(&acpi_device_bus_id->instance_ida, + device->pnp.instance_no); if (ida_is_empty(&acpi_device_bus_id->instance_ida)) { list_del(&acpi_device_bus_id->node); kfree_const(acpi_device_bus_id->bus_id); @@ -642,7 +643,7 @@ static int acpi_device_set_name(struct acpi_device *device, struct ida *instance_ida = &acpi_device_bus_id->instance_ida; int result; - result = ida_simple_get(instance_ida, 0, ACPI_MAX_DEVICE_INSTANCES, GFP_KERNEL); + result = ida_alloc(instance_ida, GFP_KERNEL); if (result < 0) return result; diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index d4fbea91ab6b..d068ff42fce4 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -758,6 +758,8 @@ bool acpi_s2idle_wake(void) return true; } + pm_pr_dbg("Rearming ACPI SCI for wakeup\n"); + pm_wakeup_clear(acpi_sci_irq); rearm_wake_irq(acpi_sci_irq); } diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 34600b5b9d8e..ceee808f7f2a 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -545,7 +545,7 @@ static const char table_sigs[][ACPI_NAMESEG_SIZE] __initconst = { ACPI_SIG_WDDT, ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT, ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, ACPI_SIG_IORT, ACPI_SIG_NFIT, ACPI_SIG_HMAT, ACPI_SIG_PPTT, - ACPI_SIG_NHLT, ACPI_SIG_AEST }; + ACPI_SIG_NHLT, ACPI_SIG_AEST, ACPI_SIG_CEDT, ACPI_SIG_AGDI }; #define ACPI_HEADER_SIZE sizeof(struct acpi_table_header) diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 4f64713e9917..becc198e4c22 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -415,6 +415,81 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "GA503"), }, }, + /* + * Clevo NL5xRU and NL5xNU/TUXEDO Aura 15 Gen1 and Gen2 have both a + * working native and video interface. However the default detection + * mechanism first registers the video interface before unregistering + * it again and switching to the native interface during boot. This + * results in a dangling SBIOS request for backlight change for some + * reason, causing the backlight to switch to ~2% once per boot on the + * first power cord connect or disconnect event. Setting the native + * interface explicitly circumvents this buggy behaviour, by avoiding + * the unregistering process. + */ + { + .callback = video_detect_force_native, + .ident = "Clevo NL5xRU", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), + }, + }, + { + .callback = video_detect_force_native, + .ident = "Clevo NL5xRU", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), + DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), + }, + }, + { + .callback = video_detect_force_native, + .ident = "Clevo NL5xRU", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), + }, + }, + { + .callback = video_detect_force_native, + .ident = "Clevo NL5xRU", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_BOARD_NAME, "AURA1501"), + }, + }, + { + .callback = video_detect_force_native, + .ident = "Clevo NL5xRU", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_BOARD_NAME, "EDUBOOK1502"), + }, + }, + { + .callback = video_detect_force_native, + .ident = "Clevo NL5xNU", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), + DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), + }, + }, + { + .callback = video_detect_force_native, + .ident = "Clevo NL5xNU", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), + DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), + }, + }, + { + .callback = video_detect_force_native, + .ident = "Clevo NL5xNU", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Notebook"), + DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), + }, + }, /* * Desktops which falsely report a backlight and which our heuristics diff --git a/drivers/acpi/x86/utils.c b/drivers/acpi/x86/utils.c index ffdeed5334d6..664070fc8349 100644 --- a/drivers/acpi/x86/utils.c +++ b/drivers/acpi/x86/utils.c @@ -285,6 +285,27 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = { ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY), }, { + /* Lenovo Yoga Tablet 1050F/L */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), + DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"), + DMI_MATCH(DMI_BOARD_NAME, "BYT-T FFD8"), + /* Partial match on beginning of BIOS version */ + DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), + }, + .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS | + ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY), + }, + { + /* Nextbook Ares 8 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), + DMI_MATCH(DMI_PRODUCT_NAME, "M890BAP"), + }, + .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS | + ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY), + }, + { /* Whitelabel (sold as various brands) TM800A550L */ .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 976154140f0b..1d6636ebaac5 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -339,6 +339,46 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu) return !ret; } +#ifdef CONFIG_ACPI_CPPC_LIB +#include <acpi/cppc_acpi.h> + +void topology_init_cpu_capacity_cppc(void) +{ + struct cppc_perf_caps perf_caps; + int cpu; + + if (likely(acpi_disabled || !acpi_cpc_valid())) + return; + + raw_capacity = kcalloc(num_possible_cpus(), sizeof(*raw_capacity), + GFP_KERNEL); + if (!raw_capacity) + return; + + for_each_possible_cpu(cpu) { + if (!cppc_get_perf_caps(cpu, &perf_caps) && + (perf_caps.highest_perf >= perf_caps.nominal_perf) && + (perf_caps.highest_perf >= perf_caps.lowest_perf)) { + raw_capacity[cpu] = perf_caps.highest_perf; + pr_debug("cpu_capacity: CPU%d cpu_capacity=%u (raw).\n", + cpu, raw_capacity[cpu]); + continue; + } + + pr_err("cpu_capacity: CPU%d missing/invalid highest performance.\n", cpu); + pr_err("cpu_capacity: partial information: fallback to 1024 for all CPUs\n"); + goto exit; + } + + topology_normalize_cpu_scale(); + schedule_work(&update_topology_flags_work); + pr_debug("cpu_capacity: cpu_capacity initialization done\n"); + +exit: + free_raw_capacity(); +} +#endif + #ifdef CONFIG_CPU_FREQ static cpumask_var_t cpus_to_visit; static void parsing_done_workfn(struct work_struct *work); @@ -387,9 +427,8 @@ static int __init register_cpufreq_notifier(void) int ret; /* - * on ACPI-based systems we need to use the default cpu capacity - * until we have the necessary code to parse the cpu capacity, so - * skip registering cpufreq notifier. + * On ACPI-based systems skip registering cpufreq notifier as cpufreq + * information is not needed for cpu capacity initialization. */ if (!acpi_disabled || !raw_capacity) return -EINVAL; diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c index eb596ff9e7bb..279ddff81ab4 100644 --- a/drivers/clocksource/acpi_pm.c +++ b/drivers/clocksource/acpi_pm.c @@ -229,8 +229,10 @@ static int __init parse_pmtmr(char *arg) int ret; ret = kstrtouint(arg, 16, &base); - if (ret) - return ret; + if (ret) { + pr_warn("PMTMR: invalid 'pmtmr=' value: '%s'\n", arg); + return 1; + } pr_info("PMTMR IOPort override: 0x%04x -> 0x%04x\n", pmtmr_ioport, base); diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 75cb91055c17..e5cfb01353d8 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -40,6 +40,7 @@ config ARM_SCPI_POWER_DOMAIN config ARM_SDE_INTERFACE bool "ARM Software Delegated Exception Interface (SDEI)" depends on ARM64 + depends on ACPI_APEI_GHES help The Software Delegated Exception Interface (SDEI) is an ARM standard for registering callbacks from the platform firmware diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index a7e762c352f9..1e1a51510e83 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -1059,14 +1059,14 @@ static bool __init sdei_present_acpi(void) return true; } -static int __init sdei_init(void) +void __init sdei_init(void) { struct platform_device *pdev; int ret; ret = platform_driver_register(&sdei_driver); if (ret || !sdei_present_acpi()) - return ret; + return; pdev = platform_device_register_simple(sdei_driver.driver.name, 0, NULL, 0); @@ -1076,17 +1076,8 @@ static int __init sdei_init(void) pr_info("Failed to register ACPI:SDEI platform device %d\n", ret); } - - return ret; } -/* - * On an ACPI system SDEI needs to be ready before HEST:GHES tries to register - * its events. ACPI is initialised from a subsys_initcall(), GHES is initialised - * by device_initcall(). We want to be called in the middle. - */ -subsys_initcall_sync(sdei_init); - int sdei_event_handler(struct pt_regs *regs, struct sdei_registered_event *arg) { diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c index a7d507802509..a929e000d0e2 100644 --- a/drivers/usb/typec/port-mapper.c +++ b/drivers/usb/typec/port-mapper.c @@ -59,7 +59,7 @@ int typec_link_ports(struct typec_port *con) if (!has_acpi_companion(&con->dev)) return 0; - bus_for_each_dev(&acpi_bus_type, NULL, &arg, typec_port_match); + acpi_bus_for_each_dev(typec_port_match, &arg); if (!arg.match) return 0; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index ca88c4706f2b..3f7f01f03869 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -480,6 +480,8 @@ void acpi_initialize_hp_context(struct acpi_device *adev, /* acpi_device.dev.bus == &acpi_bus_type */ extern struct bus_type acpi_bus_type; +int acpi_bus_for_each_dev(int (*fn)(struct device *, void *), void *data); + /* * Events * ------ diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 69e89d572b9e..02c1fa16e638 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -507,8 +507,12 @@ typedef u64 acpi_integer; /* Pointer/Integer type conversions */ #define ACPI_TO_POINTER(i) ACPI_CAST_PTR (void, (acpi_size) (i)) +#ifndef ACPI_TO_INTEGER #define ACPI_TO_INTEGER(p) ACPI_PTR_DIFF (p, (void *) 0) +#endif +#ifndef ACPI_OFFSET #define ACPI_OFFSET(d, f) ACPI_PTR_DIFF (&(((d *) 0)->f), (void *) 0) +#endif #define ACPI_PTR_TO_PHYSADDR(i) ACPI_TO_INTEGER(i) /* Optimizations for 4-character (32-bit) acpi_name manipulation */ diff --git a/include/acpi/apei.h b/include/acpi/apei.h index ece0a8af2bae..afaca3a075e8 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -27,14 +27,16 @@ extern int hest_disable; extern int erst_disable; #ifdef CONFIG_ACPI_APEI_GHES extern bool ghes_disable; +void __init acpi_ghes_init(void); #else #define ghes_disable 1 +static inline void acpi_ghes_init(void) { } #endif #ifdef CONFIG_ACPI_APEI void __init acpi_hest_init(void); #else -static inline void acpi_hest_init(void) { return; } +static inline void acpi_hest_init(void) { } #endif int erst_write(const struct cper_record_header *record); diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index b3ffb9bbf664..cec41e004ecf 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -114,6 +114,11 @@ #define acpi_raw_spinlock raw_spinlock_t * #define acpi_cpu_flags unsigned long +#define acpi_uintptr_t uintptr_t + +#define ACPI_TO_INTEGER(p) ((uintptr_t)(p)) +#define ACPI_OFFSET(d, f) offsetof(d, f) + /* Use native linux version of acpi_os_allocate_zeroed */ #define USE_NATIVE_ALLOCATE_ZEROED diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6274758648e3..a497b53e8689 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -580,6 +580,7 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); extern bool osc_sb_apei_support_acked; extern bool osc_pc_lpi_support_confirmed; extern bool osc_sb_native_usb4_support_confirmed; +extern bool osc_sb_cppc_not_supported; /* USB4 Capabilities */ #define OSC_USB_USB3_TUNNELING 0x00000001 @@ -691,7 +692,7 @@ int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *); int acpi_device_modalias(struct device *, char *, int); struct platform_device *acpi_create_platform_device(struct acpi_device *, - struct property_entry *); + const struct property_entry *); #define ACPI_PTR(_ptr) (_ptr) static inline void acpi_device_set_enumerated(struct acpi_device *adev) @@ -930,7 +931,7 @@ static inline int acpi_device_modalias(struct device *dev, static inline struct platform_device * acpi_create_platform_device(struct acpi_device *adev, - struct property_entry *properties) + const struct property_entry *properties) { return NULL; } diff --git a/include/linux/acpi_agdi.h b/include/linux/acpi_agdi.h new file mode 100644 index 000000000000..f477f0b452fa --- /dev/null +++ b/include/linux/acpi_agdi.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __ACPI_AGDI_H__ +#define __ACPI_AGDI_H__ + +#include <linux/acpi.h> + +#ifdef CONFIG_ACPI_AGDI +void __init acpi_agdi_init(void); +#else +static inline void acpi_agdi_init(void) {} +#endif +#endif /* __ACPI_AGDI_H__ */ diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index cce6136b300a..58cbe18d825c 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -11,6 +11,10 @@ void topology_normalize_cpu_scale(void); int topology_update_cpu_topology(void); +#ifdef CONFIG_ACPI_CPPC_LIB +void topology_init_cpu_capacity_cppc(void); +#endif + struct device_node; bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu); diff --git a/include/linux/arm_sdei.h b/include/linux/arm_sdei.h index 0a241c5c911d..14dc461b0e82 100644 --- a/include/linux/arm_sdei.h +++ b/include/linux/arm_sdei.h @@ -46,9 +46,11 @@ int sdei_unregister_ghes(struct ghes *ghes); /* For use by arch code when CPU hotplug notifiers are not appropriate. */ int sdei_mask_local_cpu(void); int sdei_unmask_local_cpu(void); +void __init sdei_init(void); #else static inline int sdei_mask_local_cpu(void) { return 0; } static inline int sdei_unmask_local_cpu(void) { return 0; } +static inline void sdei_init(void) { } #endif /* CONFIG_ARM_SDE_INTERFACE */ |