From 3db80c230da15ceb1a526438b458058abcd53800 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sun, 7 Feb 2016 10:00:31 -0500 Subject: ACPI: implement Generic Event Device Generic Event Device described in ACPI 6.1 allows platforms to handle platform interrupts in ACPI ASL statements. It borrows constructs like _EVT from GPIO events. All interrupts are listed in _CRS and the handler is written in _EVT method. Here is an example. Device (GED0) { Name (_HID, "ACPI0013") Name (_UID, 0) Name(_CRS, ResourceTemplate () { Interrupt(ResourceConsumer, Edge, ActiveHigh, Shared, , , ) {123} }) Method (_EVT, 1) { if (Lequal(123, Arg0)) { } } } Wake capability has not been implemented yet. Signed-off-by: Sinan Kaya Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Makefile | 1 + drivers/acpi/evged.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 drivers/acpi/evged.c (limited to 'drivers/acpi') diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index edeb2d1..5a65f85 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -47,6 +47,7 @@ acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o acpi-y += int340x_thermal.o acpi-y += power.o acpi-y += event.o +acpi-$(CONFIG_ACPI_REDUCED_HARDWARE_ONLY) += evged.o acpi-y += sysfs.o acpi-y += property.o acpi-$(CONFIG_X86) += acpi_cmos_rtc.o diff --git a/drivers/acpi/evged.c b/drivers/acpi/evged.c new file mode 100644 index 0000000..9c0a868 --- /dev/null +++ b/drivers/acpi/evged.c @@ -0,0 +1,156 @@ +/* + * Generic Event Device for ACPI. + * + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Generic Event Device allows platforms to handle interrupts in ACPI + * ASL statements. It follows very similar to _EVT method approach + * from GPIO events. All interrupts are listed in _CRS and the handler + * is written in _EVT method. Here is an example. + * + * Device (GED0) + * { + * + * Name (_HID, "ACPI0013") + * Name (_UID, 0) + * Method (_CRS, 0x0, Serialized) + * { + * Name (RBUF, ResourceTemplate () + * { + * Interrupt(ResourceConsumer, Edge, ActiveHigh, Shared, , , ) + * {123} + * } + * }) + * + * Method (_EVT, 1) { + * if (Lequal(123, Arg0)) + * { + * } + * } + * } + * + */ + +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "acpi-ged" + +struct acpi_ged_event { + struct list_head node; + struct device *dev; + unsigned int gsi; + unsigned int irq; + acpi_handle handle; +}; + +static irqreturn_t acpi_ged_irq_handler(int irq, void *data) +{ + struct acpi_ged_event *event = data; + acpi_status acpi_ret; + + acpi_ret = acpi_execute_simple_method(event->handle, NULL, event->gsi); + if (ACPI_FAILURE(acpi_ret)) + dev_err_once(event->dev, "IRQ method execution failed\n"); + + return IRQ_HANDLED; +} + +static acpi_status acpi_ged_request_interrupt(struct acpi_resource *ares, + void *context) +{ + struct acpi_ged_event *event; + unsigned int irq; + unsigned int gsi; + unsigned int irqflags = IRQF_ONESHOT; + struct device *dev = context; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_handle evt_handle; + struct resource r; + struct acpi_resource_irq *p = &ares->data.irq; + struct acpi_resource_extended_irq *pext = &ares->data.extended_irq; + + if (ares->type == ACPI_RESOURCE_TYPE_END_TAG) + return AE_OK; + + if (!acpi_dev_resource_interrupt(ares, 0, &r)) { + dev_err(dev, "unable to parse IRQ resource\n"); + return AE_ERROR; + } + if (ares->type == ACPI_RESOURCE_TYPE_IRQ) + gsi = p->interrupts[0]; + else + gsi = pext->interrupts[0]; + + irq = r.start; + + if (ACPI_FAILURE(acpi_get_handle(handle, "_EVT", &evt_handle))) { + dev_err(dev, "cannot locate _EVT method\n"); + return AE_ERROR; + } + + dev_info(dev, "GED listening GSI %u @ IRQ %u\n", gsi, irq); + + event = devm_kzalloc(dev, sizeof(*event), GFP_KERNEL); + if (!event) + return AE_ERROR; + + event->gsi = gsi; + event->dev = dev; + event->irq = irq; + event->handle = evt_handle; + + if (r.flags & IORESOURCE_IRQ_SHAREABLE) + irqflags |= IRQF_SHARED; + + if (devm_request_threaded_irq(dev, irq, NULL, acpi_ged_irq_handler, + irqflags, "ACPI:Ged", event)) { + dev_err(dev, "failed to setup event handler for irq %u\n", irq); + return AE_ERROR; + } + + return AE_OK; +} + +static int ged_probe(struct platform_device *pdev) +{ + acpi_status acpi_ret; + + acpi_ret = acpi_walk_resources(ACPI_HANDLE(&pdev->dev), "_CRS", + acpi_ged_request_interrupt, &pdev->dev); + if (ACPI_FAILURE(acpi_ret)) { + dev_err(&pdev->dev, "unable to parse the _CRS record\n"); + return -EINVAL; + } + + return 0; +} + +static const struct acpi_device_id ged_acpi_ids[] = { + {"ACPI0013"}, + {}, +}; + +static struct platform_driver ged_driver = { + .probe = ged_probe, + .driver = { + .name = MODULE_NAME, + .acpi_match_table = ACPI_PTR(ged_acpi_ids), + }, +}; + +module_platform_driver(ged_driver); +MODULE_LICENSE("GPL v2"); -- cgit v1.1 From 1373718194ebebc43c00d8f117e03885749495b0 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Tue, 22 Mar 2016 08:51:10 +0800 Subject: ACPI / PM: Introduce efi poweroff for HW-full platforms without _S5 The problem is Linux registers pm_power_off = efi_power_off only if we are in hardware reduced mode. Actually, what we also want is to do this when ACPI S5 is simply not supported on non-legacy platforms. Since some future Intel platforms are HW-full mode where the DSDT fails to supply an _S5 object(without SLP_TYP), we should let such kind of platform to leverage efi runtime service to poweroff. This patch uses efi power off as first choice when S5 is unavailable, even if there is a customized poweroff(driver provided, eg). Meanwhile, the legacy platforms will not be affected because there is no path for them to overwrite the pm_power_off to efi power off. Suggested-by: Len Brown Reviewed-by: Matt Fleming Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sleep.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 2a8b596..7a2e4d4 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -26,6 +26,11 @@ #include "internal.h" #include "sleep.h" +/* + * Some HW-full platforms do not have _S5, so they may need + * to leverage efi power off for a shutdown. + */ +bool acpi_no_s5; static u8 sleep_states[ACPI_S_STATE_COUNT]; static void acpi_sleep_tts_switch(u32 acpi_state) @@ -882,6 +887,8 @@ int __init acpi_sleep_init(void) sleep_states[ACPI_STATE_S5] = 1; pm_power_off_prepare = acpi_power_off_prepare; pm_power_off = acpi_power_off; + } else { + acpi_no_s5 = true; } supported[0] = 0; -- cgit v1.1 From 0e1affe41bdd7b1bef64c007d260e142bcaef220 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 24 Mar 2016 10:42:47 +0800 Subject: ACPI 2.0 / ECDT: Split EC_FLAGS_HANDLERS_INSTALLED This patch splits EC_FLAGS_HANDLERS_INSTALLED so that address space handler can be installed when it is not possible to install GPE handler during early stage. This patch also tunes address space handler installation, making it happening earlier than GPE handler installation for the same purpose. Since acpi_ec_start()/acpi_ec_stop() will be entered multiple times after applying this change, it is also required to protect acpi_enable_gpe()/ acpi_disable_gpe() invocations. Link: https://bugzilla.kernel.org/show_bug.cgi?id=112911 Signed-off-by: Lv Zheng Tested-by: Chris Bainbridge Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 96 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 41 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index b420fb4..b8f474b 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -105,8 +105,8 @@ enum ec_command { enum { EC_FLAGS_QUERY_PENDING, /* Query is pending */ EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */ - EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and - * OpReg are installed */ + EC_FLAGS_GPE_HANDLER_INSTALLED, /* GPE handler installed */ + EC_FLAGS_EC_HANDLER_INSTALLED, /* OpReg handler installed */ EC_FLAGS_STARTED, /* Driver is started */ EC_FLAGS_STOPPED, /* Driver is stopped */ EC_FLAGS_COMMAND_STORM, /* GPE storms occurred to the @@ -367,7 +367,8 @@ static inline void acpi_ec_clear_gpe(struct acpi_ec *ec) static void acpi_ec_submit_request(struct acpi_ec *ec) { ec->reference_count++; - if (ec->reference_count == 1) + if (test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags) && + ec->reference_count == 1) acpi_ec_enable_gpe(ec, true); } @@ -376,7 +377,8 @@ static void acpi_ec_complete_request(struct acpi_ec *ec) bool flushed = false; ec->reference_count--; - if (ec->reference_count == 0) + if (test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags) && + ec->reference_count == 0) acpi_ec_disable_gpe(ec, true); flushed = acpi_ec_flushed(ec); if (flushed) @@ -1287,52 +1289,64 @@ static int ec_install_handlers(struct acpi_ec *ec) { acpi_status status; - if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) - return 0; - status = acpi_install_gpe_raw_handler(NULL, ec->gpe, - ACPI_GPE_EDGE_TRIGGERED, - &acpi_ec_gpe_handler, ec); - if (ACPI_FAILURE(status)) - return -ENODEV; - acpi_ec_start(ec, false); - status = acpi_install_address_space_handler(ec->handle, - ACPI_ADR_SPACE_EC, - &acpi_ec_space_handler, - NULL, ec); - if (ACPI_FAILURE(status)) { - if (status == AE_NOT_FOUND) { - /* - * Maybe OS fails in evaluating the _REG object. - * The AE_NOT_FOUND error will be ignored and OS - * continue to initialize EC. - */ - pr_err("Fail in evaluating the _REG object" - " of EC device. Broken bios is suspected.\n"); - } else { - acpi_ec_stop(ec, false); - acpi_remove_gpe_handler(NULL, ec->gpe, - &acpi_ec_gpe_handler); - return -ENODEV; + + if (!test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) { + status = acpi_install_address_space_handler(ec->handle, + ACPI_ADR_SPACE_EC, + &acpi_ec_space_handler, + NULL, ec); + if (ACPI_FAILURE(status)) { + if (status == AE_NOT_FOUND) { + /* + * Maybe OS fails in evaluating the _REG + * object. The AE_NOT_FOUND error will be + * ignored and OS * continue to initialize + * EC. + */ + pr_err("Fail in evaluating the _REG object" + " of EC device. Broken bios is suspected.\n"); + } else { + acpi_ec_stop(ec, false); + return -ENODEV; + } + } + set_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags); + } + + if (!test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) { + status = acpi_install_gpe_raw_handler(NULL, ec->gpe, + ACPI_GPE_EDGE_TRIGGERED, + &acpi_ec_gpe_handler, ec); + /* This is not fatal as we can poll EC events */ + if (ACPI_SUCCESS(status)) { + set_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags); + if (test_bit(EC_FLAGS_STARTED, &ec->flags) && + ec->reference_count >= 1) + acpi_ec_enable_gpe(ec, true); } } - set_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags); return 0; } static void ec_remove_handlers(struct acpi_ec *ec) { - if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) - return; acpi_ec_stop(ec, false); - if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, - ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) - pr_err("failed to remove space handler\n"); - if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe, - &acpi_ec_gpe_handler))) - pr_err("failed to remove gpe handler\n"); - clear_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags); + + if (test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) { + if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) + pr_err("failed to remove space handler\n"); + clear_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags); + } + + if (test_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags)) { + if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe, + &acpi_ec_gpe_handler))) + pr_err("failed to remove gpe handler\n"); + clear_bit(EC_FLAGS_GPE_HANDLER_INSTALLED, &ec->flags); + } } static int acpi_ec_add(struct acpi_device *device) @@ -1434,7 +1448,7 @@ ec_parse_io_ports(struct acpi_resource *resource, void *context) int __init acpi_boot_ec_enable(void) { - if (!boot_ec || test_bit(EC_FLAGS_HANDLERS_INSTALLED, &boot_ec->flags)) + if (!boot_ec) return 0; if (!ec_install_handlers(boot_ec)) { first_ec = boot_ec; -- cgit v1.1 From 59f0aa9480cfef9173a648cec4537addc5f3ad94 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 24 Mar 2016 10:42:53 +0800 Subject: ACPI 2.0 / ECDT: Remove early namespace reference from EC All operation region accesses are allowed by AML interpreter when AML is executed, so actually BIOSen are responsible to avoid the operation region accesses in AML before OSPM has prepared an operation region driver. This is done via _REG control method. So AML code normally sets a global named object REGC to 1 when _REG(3, 1) is evaluated. Then what is ECDT? Quoting from ACPI spec 6.0, 5.2.15 Embedded Controller Boot Resources Table (ECDT): "The presence of this table allows OSPM to provide Embedded Controller operation region space access before the namespace has been evaluated." Spec also suggests a compatible mean to indicate the early EC access availability: Device (EC) { Name (REGC, Ones) Method (_REG, 2) { If (LEqual (Arg0, 3)) { Store (Arg1, REGC) } } Method (ECAV) { If (LEqual (REGC, Ones)) { If (LGreaterEqual (_REV, 2)) { Return (One) } Else { Return (Zero) } } Else { Return (REGC) } } } In this way, it allows EC accesses to happen before EC._REG(3, 1) is invoked. But ECAV is not the only way practical BIOSen using to indicate the early EC access availibility, the known variations include: 1. Setting REGC to One in \_SB._INI when _REV >= 2. Since \_SB._INI is the first control method evaluated by OSPM during the enumeration, this allows EC accesses to happen for the entire enumeration process before the namespace EC is enumerated. 2. Initialize REGC to One by default, this even allows EC accesses to happen during the table loading. Linux is now broken around ECDT support during the long term bug fixing work because it has merged many wrong ECDT bug fixes (see details below). Linux currently uses namespace EC's settings instead of ECDT settings when ECDT is detected. This apparently will result in namespace walk and _CRS/_GPE/_REG evaluations. Such stuffs could only happen after namespace is ready, while ECDT is purposely to be used before namespace is ready. The wrong bug fixing story is: 1. Link 1: At Linux ACPI early stages, "no _Lxx/_Exx/_Qxx evaluation can happen before the namespace is ready" are not ensured by ACPICA core and Linux. This is currently ensured by deferred enabling of GPE and defered registering of EC query methods (acpi_ec_register_query_methods). 2. Link 2: Reporters reported buggy ECDTs, expecting quirks for the platform. Originally, the quirk is simple, only doing things with ECDT. Bug 9399 and 12461 are platforms (Asus L4R, Asus M6R, MSI MS-171F) reported to have wrong ECDT IO port addresses, the port addresses are reversed. Bug 11880 is a platform (Asus X50GL) reported to have 0 valued port addresses, we can see that all EC accesses are protected by ECAV on this platform, so actually no early EC accesses is required by this platform. 3. Link 3: But when the bug fixing developer was requested to provide a handy and non-quirk bug fix, he tried to use correct EC settings from namespace and broke the spec purpose. We can even see that the developer was suffered from many regrssions. One interesting one is 14086, where the actual root cause obviously should be: _REG is evaluated too early. But unfortunately, the bug is fixed in a totally wrong way. So everything goes wrong from these commits: Commit: c6cb0e878446c79f42e7833d7bb69ed6bfbb381f Subject: ACPI: EC: Don't trust ECDT tables from ASUS Commit: a5032bfdd9c80e0231a6324661e123818eb46ecd Subject: ACPI: EC: Always parse EC device This patch reverts Linux behavior to simple ECDT quirk support in order to stop early _CRS/_GPE/_REG evaluations. For Bug 9399, 12461, since it is reported that the platforms require early EC accesses, this patch restores the simple ECDT quirks for them. For Bug 11880, since it is not reported that the platform requires early EC accesses and its ACPI tables contain correct ECAV, we choose an ECDT enumeration failure for this platform. Link 1: https://bugzilla.kernel.org/show_bug.cgi?id=9916 http://bugzilla.kernel.org/show_bug.cgi?id=10100 https://lkml.org/lkml/2008/2/25/282 Link 2: https://bugzilla.kernel.org/show_bug.cgi?id=9399 https://bugzilla.kernel.org/show_bug.cgi?id=12461 https://bugzilla.kernel.org/show_bug.cgi?id=11880 Link 3: https://bugzilla.kernel.org/show_bug.cgi?id=11884 https://bugzilla.kernel.org/show_bug.cgi?id=14081 https://bugzilla.kernel.org/show_bug.cgi?id=14086 https://bugzilla.kernel.org/show_bug.cgi?id=14446 Link 4: https://bugzilla.kernel.org/show_bug.cgi?id=112911 Signed-off-by: Lv Zheng Tested-by: Chris Bainbridge Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 145 ++++++++++++++++++++---------------------------------- 1 file changed, 54 insertions(+), 91 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index b8f474b..0e70181 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -175,10 +175,9 @@ static void acpi_ec_event_processor(struct work_struct *work); struct acpi_ec *boot_ec, *first_ec; EXPORT_SYMBOL(first_ec); -static int EC_FLAGS_VALIDATE_ECDT; /* ASUStec ECDTs need to be validated */ -static int EC_FLAGS_SKIP_DSDT_SCAN; /* Not all BIOS survive early DSDT scan */ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */ +static int EC_FLAGS_CORRECT_ECDT; /* Needs ECDT port address correction */ /* -------------------------------------------------------------------------- * Logging/Debugging @@ -1358,11 +1357,12 @@ static int acpi_ec_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_EC_CLASS); /* Check for boot EC */ - if (boot_ec && - (boot_ec->handle == device->handle || - boot_ec->handle == ACPI_ROOT_OBJECT)) { + if (boot_ec) { ec = boot_ec; boot_ec = NULL; + ec_remove_handlers(ec); + if (first_ec == ec) + first_ec = NULL; } else { ec = make_acpi_ec(); if (!ec) @@ -1462,20 +1462,6 @@ static const struct acpi_device_id ec_device_ids[] = { {"", 0}, }; -/* Some BIOS do not survive early DSDT scan, skip it */ -static int ec_skip_dsdt_scan(const struct dmi_system_id *id) -{ - EC_FLAGS_SKIP_DSDT_SCAN = 1; - return 0; -} - -/* ASUStek often supplies us with broken ECDT, validate it */ -static int ec_validate_ecdt(const struct dmi_system_id *id) -{ - EC_FLAGS_VALIDATE_ECDT = 1; - return 0; -} - #if 0 /* * Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not @@ -1517,30 +1503,29 @@ static int ec_clear_on_resume(const struct dmi_system_id *id) return 0; } +static int ec_correct_ecdt(const struct dmi_system_id *id) +{ + pr_debug("Detected system needing ECDT address correction.\n"); + EC_FLAGS_CORRECT_ECDT = 1; + return 0; +} + static struct dmi_system_id ec_dmi_table[] __initdata = { { - ec_skip_dsdt_scan, "Compal JFL92", { - DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), - DMI_MATCH(DMI_BOARD_NAME, "JFL92") }, NULL}, + ec_correct_ecdt, "Asus L4R", { + DMI_MATCH(DMI_BIOS_VERSION, "1008.006"), + DMI_MATCH(DMI_PRODUCT_NAME, "L4R"), + DMI_MATCH(DMI_BOARD_NAME, "L4R") }, NULL}, + { + ec_correct_ecdt, "Asus M6R", { + DMI_MATCH(DMI_BIOS_VERSION, "0207"), + DMI_MATCH(DMI_PRODUCT_NAME, "M6R"), + DMI_MATCH(DMI_BOARD_NAME, "M6R") }, NULL}, { - ec_validate_ecdt, "MSI MS-171F", { + ec_correct_ecdt, "MSI MS-171F", { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"), DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),}, NULL}, { - ec_validate_ecdt, "ASUS hardware", { - DMI_MATCH(DMI_BIOS_VENDOR, "ASUS") }, NULL}, - { - ec_validate_ecdt, "ASUS hardware", { - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc.") }, NULL}, - { - ec_skip_dsdt_scan, "HP Folio 13", { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Folio 13"),}, NULL}, - { - ec_validate_ecdt, "ASUS hardware", { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTek Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "L4R"),}, NULL}, - { ec_clear_on_resume, "Samsung hardware", { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL}, {}, @@ -1548,8 +1533,8 @@ static struct dmi_system_id ec_dmi_table[] __initdata = { int __init acpi_ec_ecdt_probe(void) { + int ret = 0; acpi_status status; - struct acpi_ec *saved_ec = NULL; struct acpi_table_ecdt *ecdt_ptr; boot_ec = make_acpi_ec(); @@ -1561,67 +1546,45 @@ int __init acpi_ec_ecdt_probe(void) dmi_check_system(ec_dmi_table); status = acpi_get_table(ACPI_SIG_ECDT, 1, (struct acpi_table_header **)&ecdt_ptr); - if (ACPI_SUCCESS(status)) { - pr_info("EC description table is found, configuring boot EC\n"); - boot_ec->command_addr = ecdt_ptr->control.address; - boot_ec->data_addr = ecdt_ptr->data.address; - boot_ec->gpe = ecdt_ptr->gpe; - boot_ec->handle = ACPI_ROOT_OBJECT; - acpi_get_handle(ACPI_ROOT_OBJECT, ecdt_ptr->id, - &boot_ec->handle); - /* Don't trust ECDT, which comes from ASUSTek */ - if (!EC_FLAGS_VALIDATE_ECDT) - goto install; - saved_ec = kmemdup(boot_ec, sizeof(struct acpi_ec), GFP_KERNEL); - if (!saved_ec) - return -ENOMEM; - /* fall through */ + if (ACPI_FAILURE(status)) { + ret = -ENODEV; + goto error; } - if (EC_FLAGS_SKIP_DSDT_SCAN) { - kfree(saved_ec); - return -ENODEV; + if (!ecdt_ptr->control.address || !ecdt_ptr->data.address) { + /* + * Asus X50GL: + * https://bugzilla.kernel.org/show_bug.cgi?id=11880 + */ + ret = -ENODEV; + goto error; } - /* This workaround is needed only on some broken machines, - * which require early EC, but fail to provide ECDT */ - pr_debug("Look up EC in DSDT\n"); - status = acpi_get_devices(ec_device_ids[0].id, ec_parse_device, - boot_ec, NULL); - /* Check that acpi_get_devices actually find something */ - if (ACPI_FAILURE(status) || !boot_ec->handle) - goto error; - if (saved_ec) { - /* try to find good ECDT from ASUSTek */ - if (saved_ec->command_addr != boot_ec->command_addr || - saved_ec->data_addr != boot_ec->data_addr || - saved_ec->gpe != boot_ec->gpe || - saved_ec->handle != boot_ec->handle) - pr_info("ASUSTek keeps feeding us with broken " - "ECDT tables, which are very hard to workaround. " - "Trying to use DSDT EC info instead. Please send " - "output of acpidump to linux-acpi@vger.kernel.org\n"); - kfree(saved_ec); - saved_ec = NULL; + pr_info("EC description table is found, configuring boot EC\n"); + if (EC_FLAGS_CORRECT_ECDT) { + /* + * Asus L4R, Asus M6R + * https://bugzilla.kernel.org/show_bug.cgi?id=9399 + * MSI MS-171F + * https://bugzilla.kernel.org/show_bug.cgi?id=12461 + */ + boot_ec->command_addr = ecdt_ptr->data.address; + boot_ec->data_addr = ecdt_ptr->control.address; } else { - /* We really need to limit this workaround, the only ASUS, - * which needs it, has fake EC._INI method, so use it as flag. - * Keep boot_ec struct as it will be needed soon. - */ - if (!dmi_name_in_vendors("ASUS") || - !acpi_has_method(boot_ec->handle, "_INI")) - return -ENODEV; + boot_ec->command_addr = ecdt_ptr->control.address; + boot_ec->data_addr = ecdt_ptr->data.address; } -install: - if (!ec_install_handlers(boot_ec)) { + boot_ec->gpe = ecdt_ptr->gpe; + boot_ec->handle = ACPI_ROOT_OBJECT; + ret = ec_install_handlers(boot_ec); + if (!ret) first_ec = boot_ec; - return 0; - } error: - kfree(boot_ec); - kfree(saved_ec); - boot_ec = NULL; - return -ENODEV; + if (ret) { + kfree(boot_ec); + boot_ec = NULL; + } + return ret; } static int param_set_event_clearing(const char *val, struct kernel_param *kp) -- cgit v1.1 From fe6cbea0f096bfdb7eafdc7b937570cea8fca00e Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 24 Mar 2016 10:43:00 +0800 Subject: ACPI 2.0 / ECDT: Enable correct ECDT initialization order With wrong ECDT fixes reverted, it is possible to put ECDT probing before acpi_enable_subsystem(). But the ultimate purpose of ECDT re-enabling is to put the ECDT probing before the namespace initialization (acpi_load_tables()). This patch achieves this with protections so that we can enable it later when all necessary corrections are upstreamed. Link 4: https://bugzilla.kernel.org/show_bug.cgi?id=112911 Signed-off-by: Lv Zheng Tested-by: Chris Bainbridge Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index c068c82..31e8da6 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -925,11 +925,13 @@ void __init acpi_early_init(void) goto error0; } - status = acpi_load_tables(); - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX - "Unable to load the System Description Tables\n"); - goto error0; + if (acpi_gbl_group_module_level_code) { + status = acpi_load_tables(); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX + "Unable to load the System Description Tables\n"); + goto error0; + } } #ifdef CONFIG_X86 @@ -995,17 +997,10 @@ static int __init acpi_bus_init(void) acpi_os_initialize1(); - status = acpi_enable_subsystem(ACPI_NO_ACPI_ENABLE); - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX - "Unable to start the ACPI Interpreter\n"); - goto error1; - } - /* * ACPI 2.0 requires the EC driver to be loaded and work before - * the EC device is found in the namespace (i.e. before acpi_initialize_objects() - * is called). + * the EC device is found in the namespace (i.e. before + * acpi_load_tables() is called). * * This is accomplished by looking for the ECDT table, and getting * the EC parameters out of that. @@ -1013,6 +1008,22 @@ static int __init acpi_bus_init(void) status = acpi_ec_ecdt_probe(); /* Ignore result. Not having an ECDT is not fatal. */ + if (!acpi_gbl_group_module_level_code) { + status = acpi_load_tables(); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX + "Unable to load the System Description Tables\n"); + goto error1; + } + } + + status = acpi_enable_subsystem(ACPI_NO_ACPI_ENABLE); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX + "Unable to start the ACPI Interpreter\n"); + goto error1; + } + status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n"); -- cgit v1.1 From c68ae33e7fb4a010f9a48af3e4b87089dca96551 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 24 Mar 2016 13:15:20 +0100 Subject: ACPI / utils: Rename acpi_dev_present() acpi_dev_present() was originally named after pci_dev_present() to signify the similarity of the two functions. However Rafael J. Wysocki pointed out that the exported function acpi_dev_present() is easily confused with the non-exported acpi_device_is_present(). Additionally in ACPI parlance the term "present" usually refers to the "device is present" bit returned by the _STA control method, yet acpi_dev_present() merely checks presence in the namespace. It does not invoke _STA at all, let alone check the "device is present" bit. As suggested by Rafael, rename the function to acpi_dev_found() and adjust all existing call sites. Signed-off-by: Lukas Wunner Signed-off-by: Rafael J. Wysocki --- drivers/acpi/utils.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 050673f..ac832bf 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -707,7 +707,7 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs) EXPORT_SYMBOL(acpi_check_dsm); /** - * acpi_dev_present - Detect presence of a given ACPI device in the system. + * acpi_dev_found - Detect presence of a given ACPI device in the namespace. * @hid: Hardware ID of the device. * * Return %true if the device was present at the moment of invocation. @@ -719,7 +719,7 @@ EXPORT_SYMBOL(acpi_check_dsm); * instead). Calling from module_init() is fine (which is synonymous * with device_initcall()). */ -bool acpi_dev_present(const char *hid) +bool acpi_dev_found(const char *hid) { struct acpi_device_bus_id *acpi_device_bus_id; bool found = false; @@ -734,7 +734,7 @@ bool acpi_dev_present(const char *hid) return found; } -EXPORT_SYMBOL(acpi_dev_present); +EXPORT_SYMBOL(acpi_dev_found); /* * acpi_backlight= handling, this is done here rather then in video_detect.c -- cgit v1.1 From 8cc6ddfcafbb7e32ff025f7d9551ecf9649c12cd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 5 Apr 2016 15:26:50 -0700 Subject: libnvdimm, nfit: report multiple interface codes per-dimm Starting with ACPI 6.1 an NFIT table will report multiple 'NVDIMM Control Region Structure' instances per-dimm, one for each supported format interface. Report that code in the following format in sysfs: nmemX/nfit/formats nmemX/nfit/format nmemX/nfit/format1 nmemX/nfit/format2 ... nmemX/nfit/formatN Where format2 - formatN are theoretical as there are no known DIMMs with support for more than two interface formats. This layout is compatible with existing libndctl binaries that only expect one code per-dimm as they will ignore nmemX/nfit/formats and nmemX/nfit/formatN. Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++--- drivers/acpi/nfit.h | 1 + 2 files changed, 70 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index d0f35e6..db0f806 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -655,6 +655,7 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, if (!nfit_mem) return -ENOMEM; INIT_LIST_HEAD(&nfit_mem->list); + nfit_mem->acpi_desc = acpi_desc; list_add(&nfit_mem->list, &acpi_desc->dimms); } @@ -838,6 +839,18 @@ static ssize_t device_show(struct device *dev, } static DEVICE_ATTR_RO(device); +static int num_nvdimm_formats(struct nvdimm *nvdimm) +{ + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + int formats = 0; + + if (nfit_mem->memdev_pmem) + formats++; + if (nfit_mem->memdev_bdw) + formats++; + return formats; +} + static ssize_t format_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -847,6 +860,55 @@ static ssize_t format_show(struct device *dev, } static DEVICE_ATTR_RO(format); +static ssize_t format1_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 handle; + ssize_t rc = -ENXIO; + struct nfit_mem *nfit_mem; + struct nfit_memdev *nfit_memdev; + struct acpi_nfit_desc *acpi_desc; + struct nvdimm *nvdimm = to_nvdimm(dev); + struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); + + nfit_mem = nvdimm_provider_data(nvdimm); + acpi_desc = nfit_mem->acpi_desc; + handle = to_nfit_memdev(dev)->device_handle; + + /* assumes DIMMs have at most 2 published interface codes */ + mutex_lock(&acpi_desc->init_mutex); + list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { + struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev; + struct nfit_dcr *nfit_dcr; + + if (memdev->device_handle != handle) + continue; + + list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) { + if (nfit_dcr->dcr->region_index != memdev->region_index) + continue; + if (nfit_dcr->dcr->code == dcr->code) + continue; + rc = sprintf(buf, "%#x\n", nfit_dcr->dcr->code); + break; + } + if (rc != ENXIO) + break; + } + mutex_unlock(&acpi_desc->init_mutex); + return rc; +} +static DEVICE_ATTR_RO(format1); + +static ssize_t formats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + + return sprintf(buf, "%d\n", num_nvdimm_formats(nvdimm)); +} +static DEVICE_ATTR_RO(formats); + static ssize_t serial_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -876,6 +938,8 @@ static struct attribute *acpi_nfit_dimm_attributes[] = { &dev_attr_vendor.attr, &dev_attr_device.attr, &dev_attr_format.attr, + &dev_attr_formats.attr, + &dev_attr_format1.attr, &dev_attr_serial.attr, &dev_attr_rev_id.attr, &dev_attr_flags.attr, @@ -886,11 +950,13 @@ static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj, struct attribute *a, int n) { struct device *dev = container_of(kobj, struct device, kobj); + struct nvdimm *nvdimm = to_nvdimm(dev); - if (to_nfit_dcr(dev)) - return a->mode; - else + if (!to_nfit_dcr(dev)) + return 0; + if (a == &dev_attr_format1.attr && num_nvdimm_formats(nvdimm) <= 1) return 0; + return a->mode; } static struct attribute_group acpi_nfit_dimm_attribute_group = { diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index c75576b..5201840 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -109,6 +109,7 @@ struct nfit_mem { struct nfit_flush *nfit_flush; struct list_head list; struct acpi_device *adev; + struct acpi_nfit_desc *acpi_desc; unsigned long dsm_mask; }; -- cgit v1.1 From 8259542348d93da6a04eed979047b1fd1ca72abe Mon Sep 17 00:00:00 2001 From: "Lee, Chun-Yi" Date: Thu, 21 Jan 2016 20:32:10 +0800 Subject: libnvdimm, nfit: Use ACPI_SIG_NFIT instead of hard coded string It's minor but that's still better to use ACPI_SIG_NFIT instead of hard coded string. Cc: Ross Zwisler Cc: "Rafael J. Wysocki" Cc: Len Brown Signed-off-by: Lee, Chun-Yi Acked-by: Ross Zwisler Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index db0f806..a434b58 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -2372,7 +2372,7 @@ static int acpi_nfit_add(struct acpi_device *adev) acpi_size sz; int rc; - status = acpi_get_table_with_size("NFIT", 0, &tbl, &sz); + status = acpi_get_table_with_size(ACPI_SIG_NFIT, 0, &tbl, &sz); if (ACPI_FAILURE(status)) { /* This is ok, we could have an nvdimm hotplugged later */ dev_dbg(dev, "failed to find NFIT at startup\n"); -- cgit v1.1 From c7e16e5257ec46530e3e874af38191746c137c83 Mon Sep 17 00:00:00 2001 From: Jerry Hoemann Date: Mon, 11 Apr 2016 15:02:26 -0700 Subject: acpi: widen acpi_evaluate_dsm() revision and function-index arguments The ACPI specification states that arguments "Revision ID" and "Function Index" to a _DSM are type "Integer." Type Integers are 64 bit quantities. The function evaluate_dsm specifies these types as simple "int" which are 32 bits. Widen type passed to acpi_evaluate_dsm and its callers and derived callers to pass correct type. acpi_check_dsm and acpi_evaluate_dsm_typed had similar issue and were corrected as well. This is in preparation for libnvdimm implementing a generic _DSM passthrough facility to have the capacity to pass 64-bit values as the ACPI specification allows. [djbw: clarify the changelog, add rationale] Signed-off-by: Jerry Hoemann Acked-by: Rafael J. Wysocki Signed-off-by: Dan Williams --- drivers/acpi/utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 050673f..e854dea 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -625,7 +625,7 @@ acpi_status acpi_evaluate_lck(acpi_handle handle, int lock) * some old BIOSes do expect a buffer or an integer etc. */ union acpi_object * -acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, int rev, int func, +acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 func, union acpi_object *argv4) { acpi_status ret; @@ -674,7 +674,7 @@ EXPORT_SYMBOL(acpi_evaluate_dsm); * functions. Currently only support 64 functions at maximum, should be * enough for now. */ -bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs) +bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 funcs) { int i; u64 mask = 0; -- cgit v1.1 From 5ae74f2cc2f150c1a5cee54cabbd71dd0661285a Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 11 Apr 2016 10:13:18 +0800 Subject: ACPI / tables: Move table override mechanisms to tables.c This patch moves acpi_os_table_override() and acpi_os_physical_table_override() to tables.c. Along with the mechanisms, acpi_initrd_initialize_tables() is also moved to tables.c to form a static function. The following functions are renamed according to this change: 1. acpi_initrd_override() -> renamed to early_acpi_table_init(), which invokes acpi_table_initrd_init() 2. acpi_os_physical_table_override() -> which invokes acpi_table_initrd_override() 3. acpi_initialize_initrd_tables() -> renamed to acpi_table_initrd_scan() Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/internal.h | 1 - drivers/acpi/osl.c | 274 --------------------------------------------- drivers/acpi/tables.c | 292 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 291 insertions(+), 276 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 7c18847..1b0e6fd 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -20,7 +20,6 @@ #define PREFIX "ACPI: " -void acpi_initrd_initialize_tables(void); acpi_status acpi_os_initialize1(void); void init_acpi_device_notify(void); int acpi_scan_init(void); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 814d5f8..0796ad9 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -602,280 +602,6 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val, return AE_OK; } -static void acpi_table_taint(struct acpi_table_header *table) -{ - pr_warn(PREFIX - "Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n", - table->signature, table->oem_table_id); - add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE); -} - -#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE -#include -#include - -static u64 acpi_tables_addr; -static int all_tables_size; - -/* Copied from acpica/tbutils.c:acpi_tb_checksum() */ -static u8 __init acpi_table_checksum(u8 *buffer, u32 length) -{ - u8 sum = 0; - u8 *end = buffer + length; - - while (buffer < end) - sum = (u8) (sum + *(buffer++)); - return sum; -} - -/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */ -static const char * const table_sigs[] = { - ACPI_SIG_BERT, ACPI_SIG_CPEP, ACPI_SIG_ECDT, ACPI_SIG_EINJ, - ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT, ACPI_SIG_MSCT, - ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT, ACPI_SIG_ASF, - ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR, ACPI_SIG_HPET, - ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG, ACPI_SIG_MCHI, - ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI, ACPI_SIG_TCPA, - ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, ACPI_SIG_WDDT, - ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT, - ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, NULL }; - -#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header) - -#define ACPI_OVERRIDE_TABLES 64 -static struct cpio_data __initdata acpi_initrd_files[ACPI_OVERRIDE_TABLES]; -static DECLARE_BITMAP(acpi_initrd_installed, ACPI_OVERRIDE_TABLES); - -#define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT) - -void __init acpi_initrd_override(void *data, size_t size) -{ - int sig, no, table_nr = 0, total_offset = 0; - long offset = 0; - struct acpi_table_header *table; - char cpio_path[32] = "kernel/firmware/acpi/"; - struct cpio_data file; - - if (data == NULL || size == 0) - return; - - for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) { - file = find_cpio_data(cpio_path, data, size, &offset); - if (!file.data) - break; - - data += offset; - size -= offset; - - if (file.size < sizeof(struct acpi_table_header)) { - pr_err("ACPI OVERRIDE: Table smaller than ACPI header [%s%s]\n", - cpio_path, file.name); - continue; - } - - table = file.data; - - for (sig = 0; table_sigs[sig]; sig++) - if (!memcmp(table->signature, table_sigs[sig], 4)) - break; - - if (!table_sigs[sig]) { - pr_err("ACPI OVERRIDE: Unknown signature [%s%s]\n", - cpio_path, file.name); - continue; - } - if (file.size != table->length) { - pr_err("ACPI OVERRIDE: File length does not match table length [%s%s]\n", - cpio_path, file.name); - continue; - } - if (acpi_table_checksum(file.data, table->length)) { - pr_err("ACPI OVERRIDE: Bad table checksum [%s%s]\n", - cpio_path, file.name); - continue; - } - - pr_info("%4.4s ACPI table found in initrd [%s%s][0x%x]\n", - table->signature, cpio_path, file.name, table->length); - - all_tables_size += table->length; - acpi_initrd_files[table_nr].data = file.data; - acpi_initrd_files[table_nr].size = file.size; - table_nr++; - } - if (table_nr == 0) - return; - - acpi_tables_addr = - memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT, - all_tables_size, PAGE_SIZE); - if (!acpi_tables_addr) { - WARN_ON(1); - return; - } - /* - * Only calling e820_add_reserve does not work and the - * tables are invalid (memory got used) later. - * memblock_reserve works as expected and the tables won't get modified. - * But it's not enough on X86 because ioremap will - * complain later (used by acpi_os_map_memory) that the pages - * that should get mapped are not marked "reserved". - * Both memblock_reserve and e820_add_region (via arch_reserve_mem_area) - * works fine. - */ - memblock_reserve(acpi_tables_addr, all_tables_size); - arch_reserve_mem_area(acpi_tables_addr, all_tables_size); - - /* - * early_ioremap only can remap 256k one time. If we map all - * tables one time, we will hit the limit. Need to map chunks - * one by one during copying the same as that in relocate_initrd(). - */ - for (no = 0; no < table_nr; no++) { - unsigned char *src_p = acpi_initrd_files[no].data; - phys_addr_t size = acpi_initrd_files[no].size; - phys_addr_t dest_addr = acpi_tables_addr + total_offset; - phys_addr_t slop, clen; - char *dest_p; - - total_offset += size; - - while (size) { - slop = dest_addr & ~PAGE_MASK; - clen = size; - if (clen > MAP_CHUNK_SIZE - slop) - clen = MAP_CHUNK_SIZE - slop; - dest_p = early_ioremap(dest_addr & PAGE_MASK, - clen + slop); - memcpy(dest_p + slop, src_p, clen); - early_iounmap(dest_p, clen + slop); - src_p += clen; - dest_addr += clen; - size -= clen; - } - } -} - -acpi_status -acpi_os_physical_table_override(struct acpi_table_header *existing_table, - acpi_physical_address *address, u32 *length) -{ - int table_offset = 0; - int table_index = 0; - struct acpi_table_header *table; - u32 table_length; - - *length = 0; - *address = 0; - if (!acpi_tables_addr) - return AE_OK; - - while (table_offset + ACPI_HEADER_SIZE <= all_tables_size) { - table = acpi_os_map_memory(acpi_tables_addr + table_offset, - ACPI_HEADER_SIZE); - if (table_offset + table->length > all_tables_size) { - acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); - WARN_ON(1); - return AE_OK; - } - - table_length = table->length; - - /* Only override tables matched */ - if (test_bit(table_index, acpi_initrd_installed) || - memcmp(existing_table->signature, table->signature, 4) || - memcmp(table->oem_table_id, existing_table->oem_table_id, - ACPI_OEM_TABLE_ID_SIZE)) { - acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); - goto next_table; - } - - *length = table_length; - *address = acpi_tables_addr + table_offset; - acpi_table_taint(existing_table); - acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); - set_bit(table_index, acpi_initrd_installed); - break; - -next_table: - table_offset += table_length; - table_index++; - } - return AE_OK; -} - -void __init acpi_initrd_initialize_tables(void) -{ - int table_offset = 0; - int table_index = 0; - u32 table_length; - struct acpi_table_header *table; - - if (!acpi_tables_addr) - return; - - while (table_offset + ACPI_HEADER_SIZE <= all_tables_size) { - table = acpi_os_map_memory(acpi_tables_addr + table_offset, - ACPI_HEADER_SIZE); - if (table_offset + table->length > all_tables_size) { - acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); - WARN_ON(1); - return; - } - - table_length = table->length; - - /* Skip RSDT/XSDT which should only be used for override */ - if (test_bit(table_index, acpi_initrd_installed) || - ACPI_COMPARE_NAME(table->signature, ACPI_SIG_RSDT) || - ACPI_COMPARE_NAME(table->signature, ACPI_SIG_XSDT)) { - acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); - goto next_table; - } - - acpi_table_taint(table); - acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); - acpi_install_table(acpi_tables_addr + table_offset, TRUE); - set_bit(table_index, acpi_initrd_installed); -next_table: - table_offset += table_length; - table_index++; - } -} -#else -acpi_status -acpi_os_physical_table_override(struct acpi_table_header *existing_table, - acpi_physical_address *address, - u32 *table_length) -{ - *table_length = 0; - *address = 0; - return AE_OK; -} - -void __init acpi_initrd_initialize_tables(void) -{ -} -#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */ - -acpi_status -acpi_os_table_override(struct acpi_table_header *existing_table, - struct acpi_table_header **new_table) -{ - if (!existing_table || !new_table) - return AE_BAD_PARAMETER; - - *new_table = NULL; - -#ifdef CONFIG_ACPI_CUSTOM_DSDT - if (strncmp(existing_table->signature, "DSDT", 4) == 0) - *new_table = (struct acpi_table_header *)AmlCode; -#endif - if (*new_table != NULL) - acpi_table_taint(existing_table); - return AE_OK; -} - static irqreturn_t acpi_irq(int irq, void *dev_id) { u32 handled; diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index f49c024..2e74dbf 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include "internal.h" #define ACPI_MAX_TABLES 128 @@ -433,6 +435,294 @@ static void __init check_multiple_madt(void) return; } +static void acpi_table_taint(struct acpi_table_header *table) +{ + pr_warn("Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n", + table->signature, table->oem_table_id); + add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE); +} + +#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE +static u64 acpi_tables_addr; +static int all_tables_size; + +/* Copied from acpica/tbutils.c:acpi_tb_checksum() */ +static u8 __init acpi_table_checksum(u8 *buffer, u32 length) +{ + u8 sum = 0; + u8 *end = buffer + length; + + while (buffer < end) + sum = (u8) (sum + *(buffer++)); + return sum; +} + +/* All but ACPI_SIG_RSDP and ACPI_SIG_FACS: */ +static const char * const table_sigs[] = { + ACPI_SIG_BERT, ACPI_SIG_CPEP, ACPI_SIG_ECDT, ACPI_SIG_EINJ, + ACPI_SIG_ERST, ACPI_SIG_HEST, ACPI_SIG_MADT, ACPI_SIG_MSCT, + ACPI_SIG_SBST, ACPI_SIG_SLIT, ACPI_SIG_SRAT, ACPI_SIG_ASF, + ACPI_SIG_BOOT, ACPI_SIG_DBGP, ACPI_SIG_DMAR, ACPI_SIG_HPET, + ACPI_SIG_IBFT, ACPI_SIG_IVRS, ACPI_SIG_MCFG, ACPI_SIG_MCHI, + ACPI_SIG_SLIC, ACPI_SIG_SPCR, ACPI_SIG_SPMI, ACPI_SIG_TCPA, + ACPI_SIG_UEFI, ACPI_SIG_WAET, ACPI_SIG_WDAT, ACPI_SIG_WDDT, + ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT, + ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, NULL }; + +#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header) + +#define ACPI_OVERRIDE_TABLES 64 +static struct cpio_data __initdata acpi_initrd_files[ACPI_OVERRIDE_TABLES]; +static DECLARE_BITMAP(acpi_initrd_installed, ACPI_OVERRIDE_TABLES); + +#define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT) + +static void __init acpi_table_initrd_init(void *data, size_t size) +{ + int sig, no, table_nr = 0, total_offset = 0; + long offset = 0; + struct acpi_table_header *table; + char cpio_path[32] = "kernel/firmware/acpi/"; + struct cpio_data file; + + if (data == NULL || size == 0) + return; + + for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) { + file = find_cpio_data(cpio_path, data, size, &offset); + if (!file.data) + break; + + data += offset; + size -= offset; + + if (file.size < sizeof(struct acpi_table_header)) { + pr_err("ACPI OVERRIDE: Table smaller than ACPI header [%s%s]\n", + cpio_path, file.name); + continue; + } + + table = file.data; + + for (sig = 0; table_sigs[sig]; sig++) + if (!memcmp(table->signature, table_sigs[sig], 4)) + break; + + if (!table_sigs[sig]) { + pr_err("ACPI OVERRIDE: Unknown signature [%s%s]\n", + cpio_path, file.name); + continue; + } + if (file.size != table->length) { + pr_err("ACPI OVERRIDE: File length does not match table length [%s%s]\n", + cpio_path, file.name); + continue; + } + if (acpi_table_checksum(file.data, table->length)) { + pr_err("ACPI OVERRIDE: Bad table checksum [%s%s]\n", + cpio_path, file.name); + continue; + } + + pr_info("%4.4s ACPI table found in initrd [%s%s][0x%x]\n", + table->signature, cpio_path, file.name, table->length); + + all_tables_size += table->length; + acpi_initrd_files[table_nr].data = file.data; + acpi_initrd_files[table_nr].size = file.size; + table_nr++; + } + if (table_nr == 0) + return; + + acpi_tables_addr = + memblock_find_in_range(0, max_low_pfn_mapped << PAGE_SHIFT, + all_tables_size, PAGE_SIZE); + if (!acpi_tables_addr) { + WARN_ON(1); + return; + } + /* + * Only calling e820_add_reserve does not work and the + * tables are invalid (memory got used) later. + * memblock_reserve works as expected and the tables won't get modified. + * But it's not enough on X86 because ioremap will + * complain later (used by acpi_os_map_memory) that the pages + * that should get mapped are not marked "reserved". + * Both memblock_reserve and e820_add_region (via arch_reserve_mem_area) + * works fine. + */ + memblock_reserve(acpi_tables_addr, all_tables_size); + arch_reserve_mem_area(acpi_tables_addr, all_tables_size); + + /* + * early_ioremap only can remap 256k one time. If we map all + * tables one time, we will hit the limit. Need to map chunks + * one by one during copying the same as that in relocate_initrd(). + */ + for (no = 0; no < table_nr; no++) { + unsigned char *src_p = acpi_initrd_files[no].data; + phys_addr_t size = acpi_initrd_files[no].size; + phys_addr_t dest_addr = acpi_tables_addr + total_offset; + phys_addr_t slop, clen; + char *dest_p; + + total_offset += size; + + while (size) { + slop = dest_addr & ~PAGE_MASK; + clen = size; + if (clen > MAP_CHUNK_SIZE - slop) + clen = MAP_CHUNK_SIZE - slop; + dest_p = early_ioremap(dest_addr & PAGE_MASK, + clen + slop); + memcpy(dest_p + slop, src_p, clen); + early_iounmap(dest_p, clen + slop); + src_p += clen; + dest_addr += clen; + size -= clen; + } + } +} + +static acpi_status +acpi_table_initrd_override(struct acpi_table_header *existing_table, + acpi_physical_address *address, u32 *length) +{ + int table_offset = 0; + int table_index = 0; + struct acpi_table_header *table; + u32 table_length; + + *length = 0; + *address = 0; + if (!acpi_tables_addr) + return AE_OK; + + while (table_offset + ACPI_HEADER_SIZE <= all_tables_size) { + table = acpi_os_map_memory(acpi_tables_addr + table_offset, + ACPI_HEADER_SIZE); + if (table_offset + table->length > all_tables_size) { + acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); + WARN_ON(1); + return AE_OK; + } + + table_length = table->length; + + /* Only override tables matched */ + if (test_bit(table_index, acpi_initrd_installed) || + memcmp(existing_table->signature, table->signature, 4) || + memcmp(table->oem_table_id, existing_table->oem_table_id, + ACPI_OEM_TABLE_ID_SIZE)) { + acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); + goto next_table; + } + + *length = table_length; + *address = acpi_tables_addr + table_offset; + acpi_table_taint(existing_table); + acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); + set_bit(table_index, acpi_initrd_installed); + break; + +next_table: + table_offset += table_length; + table_index++; + } + return AE_OK; +} + +static void __init acpi_table_initrd_scan(void) +{ + int table_offset = 0; + int table_index = 0; + u32 table_length; + struct acpi_table_header *table; + + if (!acpi_tables_addr) + return; + + while (table_offset + ACPI_HEADER_SIZE <= all_tables_size) { + table = acpi_os_map_memory(acpi_tables_addr + table_offset, + ACPI_HEADER_SIZE); + if (table_offset + table->length > all_tables_size) { + acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); + WARN_ON(1); + return; + } + + table_length = table->length; + + /* Skip RSDT/XSDT which should only be used for override */ + if (test_bit(table_index, acpi_initrd_installed) || + ACPI_COMPARE_NAME(table->signature, ACPI_SIG_RSDT) || + ACPI_COMPARE_NAME(table->signature, ACPI_SIG_XSDT)) { + acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); + goto next_table; + } + + acpi_table_taint(table); + acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); + acpi_install_table(acpi_tables_addr + table_offset, TRUE); + set_bit(table_index, acpi_initrd_installed); +next_table: + table_offset += table_length; + table_index++; + } +} +#else +static void __init acpi_table_initrd_init(void *data, size_t size) +{ +} + +static acpi_status +acpi_table_initrd_override(struct acpi_table_header *existing_table, + acpi_physical_address *address, + u32 *table_length) +{ + *table_length = 0; + *address = 0; + return AE_OK; +} + +static void __init acpi_table_initrd_scan(void) +{ +} +#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */ + +acpi_status +acpi_os_physical_table_override(struct acpi_table_header *existing_table, + acpi_physical_address *address, + u32 *table_length) +{ + return acpi_table_initrd_override(existing_table, address, + table_length); +} + +acpi_status +acpi_os_table_override(struct acpi_table_header *existing_table, + struct acpi_table_header **new_table) +{ + if (!existing_table || !new_table) + return AE_BAD_PARAMETER; + + *new_table = NULL; + +#ifdef CONFIG_ACPI_CUSTOM_DSDT + if (strncmp(existing_table->signature, "DSDT", 4) == 0) + *new_table = (struct acpi_table_header *)AmlCode; +#endif + if (*new_table != NULL) + acpi_table_taint(existing_table); + return AE_OK; +} + +void __init early_acpi_table_init(void *data, size_t size) +{ + acpi_table_initrd_init(data, size); +} + /* * acpi_table_init() * @@ -457,7 +747,7 @@ int __init acpi_table_init(void) status = acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0); if (ACPI_FAILURE(status)) return -EINVAL; - acpi_initrd_initialize_tables(); + acpi_table_initrd_scan(); check_multiple_madt(); return 0; -- cgit v1.1 From 5d8813271f8a7c86027afb2ef554f2a5a9ba7c15 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 11 Apr 2016 10:13:33 +0800 Subject: ACPI / tables: Convert initrd table override to table upgrade mechanism This patch converts the initrd table override mechanism to the table upgrade mechanism by restricting its usage to the tables released with compatibility and more recent revision. This use case has been encouraged by the ACPI specification: 1. OEMID: An OEM-supplied string that identifies the OEM. 2. OEM Table ID: An OEM-supplied string that the OEM uses to identify the particular data table. This field is particularly useful when defining a definition block to distinguish definition block functions. OEM assigns each dissimilar table a new OEM Table Id. 3. OEM Revision: An OEM-supplied revision number. Larger numbers are assumed to be newer revisions. For OEMs, good practices will ensure consistency when assigning OEMID and OEM Table ID fields in any table. The intent of these fields is to allow for a binary control system that support services can use. Because many support function can be automated, it is useful when a tool can programatically determine which table release is a compatible and more recent revision of a prior table on the same OEMID and OEM Table ID. The facility can now be used by the vendors to upgrade wrong tables for bug fixing purpose, thus lockdep disabling taint is not suitable for it and it should be a default 'y' option to implement the spec encouraged use case. Note that, by implementing table upgrade inside of ACPICA itself, it is possible to remove acpi_table_initrd_override() and tables can be upgraded by acpi_install_table() automatically. Though current ACPICA impelentation hasn't implemented this, this patched changes the table flag setting timing to allow this to be implemented in ACPICA without changing the code here. Documentation of initrd override mechanism is upgraded accordingly. Original-by: Octavian Purdila Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 8 ++++---- drivers/acpi/tables.c | 48 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 18 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 82b96ee..b225c4b 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -311,12 +311,12 @@ config ACPI_CUSTOM_DSDT bool default ACPI_CUSTOM_DSDT_FILE != "" -config ACPI_INITRD_TABLE_OVERRIDE - bool "ACPI tables override via initrd" +config ACPI_TABLE_UPGRADE + bool "Allow upgrading ACPI tables via initrd" depends on BLK_DEV_INITRD && X86 - default n + default y help - This option provides functionality to override arbitrary ACPI tables + This option provides functionality to upgrade arbitrary ACPI tables via initrd. No functional change if no ACPI tables are passed via initrd, therefore it's safe to say Y. See Documentation/acpi/initrd_table_override.txt for details diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 2e74dbf..08795fb 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -442,7 +442,7 @@ static void acpi_table_taint(struct acpi_table_header *table) add_taint(TAINT_OVERRIDDEN_ACPI_TABLE, LOCKDEP_NOW_UNRELIABLE); } -#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE +#ifdef CONFIG_ACPI_TABLE_UPGRADE static u64 acpi_tables_addr; static int all_tables_size; @@ -471,9 +471,9 @@ static const char * const table_sigs[] = { #define ACPI_HEADER_SIZE sizeof(struct acpi_table_header) -#define ACPI_OVERRIDE_TABLES 64 -static struct cpio_data __initdata acpi_initrd_files[ACPI_OVERRIDE_TABLES]; -static DECLARE_BITMAP(acpi_initrd_installed, ACPI_OVERRIDE_TABLES); +#define NR_ACPI_INITRD_TABLES 64 +static struct cpio_data __initdata acpi_initrd_files[NR_ACPI_INITRD_TABLES]; +static DECLARE_BITMAP(acpi_initrd_installed, NR_ACPI_INITRD_TABLES); #define MAP_CHUNK_SIZE (NR_FIX_BTMAPS << PAGE_SHIFT) @@ -488,7 +488,7 @@ static void __init acpi_table_initrd_init(void *data, size_t size) if (data == NULL || size == 0) return; - for (no = 0; no < ACPI_OVERRIDE_TABLES; no++) { + for (no = 0; no < NR_ACPI_INITRD_TABLES; no++) { file = find_cpio_data(cpio_path, data, size, &offset); if (!file.data) break; @@ -611,19 +611,30 @@ acpi_table_initrd_override(struct acpi_table_header *existing_table, table_length = table->length; /* Only override tables matched */ - if (test_bit(table_index, acpi_initrd_installed) || - memcmp(existing_table->signature, table->signature, 4) || + if (memcmp(existing_table->signature, table->signature, 4) || + memcmp(table->oem_id, existing_table->oem_id, + ACPI_OEM_ID_SIZE) || memcmp(table->oem_table_id, existing_table->oem_table_id, ACPI_OEM_TABLE_ID_SIZE)) { acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); goto next_table; } + /* + * Mark the table to avoid being used in + * acpi_table_initrd_scan() and check the revision. + */ + if (test_and_set_bit(table_index, acpi_initrd_installed) || + existing_table->oem_revision >= table->oem_revision) { + acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); + goto next_table; + } *length = table_length; *address = acpi_tables_addr + table_offset; - acpi_table_taint(existing_table); + pr_info("Table Upgrade: override [%4.4s-%6.6s-%8.8s]\n", + table->signature, table->oem_id, + table->oem_table_id); acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); - set_bit(table_index, acpi_initrd_installed); break; next_table: @@ -655,17 +666,26 @@ static void __init acpi_table_initrd_scan(void) table_length = table->length; /* Skip RSDT/XSDT which should only be used for override */ - if (test_bit(table_index, acpi_initrd_installed) || - ACPI_COMPARE_NAME(table->signature, ACPI_SIG_RSDT) || + if (ACPI_COMPARE_NAME(table->signature, ACPI_SIG_RSDT) || ACPI_COMPARE_NAME(table->signature, ACPI_SIG_XSDT)) { acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); goto next_table; } + /* + * Mark the table to avoid being used in + * acpi_table_initrd_override(). Though this is not possible + * because override is disabled in acpi_install_table(). + */ + if (test_and_set_bit(table_index, acpi_initrd_installed)) { + acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); + goto next_table; + } - acpi_table_taint(table); + pr_info("Table Upgrade: install [%4.4s-%6.6s-%8.8s]\n", + table->signature, table->oem_id, + table->oem_table_id); acpi_os_unmap_memory(table, ACPI_HEADER_SIZE); acpi_install_table(acpi_tables_addr + table_offset, TRUE); - set_bit(table_index, acpi_initrd_installed); next_table: table_offset += table_length; table_index++; @@ -689,7 +709,7 @@ acpi_table_initrd_override(struct acpi_table_header *existing_table, static void __init acpi_table_initrd_scan(void) { } -#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */ +#endif /* CONFIG_ACPI_TABLE_UPGRADE */ acpi_status acpi_os_physical_table_override(struct acpi_table_header *existing_table, -- cgit v1.1 From 702b07fcc9b264c9afd372676bbdd50a762dcde0 Mon Sep 17 00:00:00 2001 From: Lukasz Anaczkowski Date: Thu, 21 Apr 2016 11:29:00 +0200 Subject: ACPI / SRAT: fix SRAT parsing order with both LAPIC and X2APIC present SRAT maps APIC ID to proximity domains ids (PXM). Mapping from PXM to NUMA node ids is based on order of entries in SRAT table. SRAT table has just LAPIC entires or mix of LAPIC and X2APIC entries. As long as there are only LAPIC entires, mapping from proximity domain id to NUMA node id is as assumed by BIOS. However, once APIC entries are mixed, X2APIC entries would be first mapped which causes unexpected NUMA node mapping. To fix that, change parsing to check each entry against both LAPIC and X2APIC so mapping is in the SRAT/PXM order. This is supplemental change to the fix made by commit d81056b5278 (Handle apic/x2apic entries in MADT in correct order) and using the mechanism introduced by 9b3fedd (ACPI / tables: Add acpi_subtable_proc to ACPI table parsers). Fixes: d81056b5278 (Handle apic/x2apic entries in MADT in correct order) Signed-off-by: Lukasz Anaczkowski [ rjw : Subject & changelog ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/numa.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 72b6e9e..d176e0e 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -327,10 +327,18 @@ int __init acpi_numa_init(void) /* SRAT: Static Resource Affinity Table */ if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) { - acpi_table_parse_srat(ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY, - acpi_parse_x2apic_affinity, 0); - acpi_table_parse_srat(ACPI_SRAT_TYPE_CPU_AFFINITY, - acpi_parse_processor_affinity, 0); + struct acpi_subtable_proc srat_proc[2]; + + memset(srat_proc, 0, sizeof(srat_proc)); + srat_proc[0].id = ACPI_SRAT_TYPE_CPU_AFFINITY; + srat_proc[0].handler = acpi_parse_processor_affinity; + srat_proc[1].id = ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY; + srat_proc[1].handler = acpi_parse_x2apic_affinity; + + acpi_table_parse_entries_array(ACPI_SIG_SRAT, + sizeof(struct acpi_table_srat), + srat_proc, ARRAY_SIZE(srat_proc), 0); + cnt = acpi_table_parse_srat(ACPI_SRAT_TYPE_MEMORY_AFFINITY, acpi_parse_memory_affinity, NR_NODE_MEMBLKS); -- cgit v1.1 From 6df795ff139a85eba8767a1504414c41e8814a1a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 15 Apr 2016 08:53:41 +0100 Subject: ACPI / ARM64: Don't enable ACPI by default on ARM64 If ACPI is selectable it is enabled by default. This is a good choice for architectures where the overwhelming majority of systems use ACPI like x86 and IA-64 but is less clear for architectures where it's less common like ARM64. Change the default selection so that it's only done explicitly on those architectures where ACPI is universally used. Signed-off-by: Mark Brown Acked-by: Catalin Marinas Acked-by: Hanjun Guo Acked-by: Olof Johansson Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 82b96ee..2fcf87a 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -8,7 +8,7 @@ menuconfig ACPI depends on IA64 || X86 || (ARM64 && EXPERT) depends on PCI select PNP - default y + default y if (IA64 || X86) help Advanced Configuration and Power Interface (ACPI) support for Linux requires an ACPI-compliant platform (hardware/firmware), -- cgit v1.1 From 46bcc6b1f3f77ff0a5b88b87a6be5954ed0c3ce6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 15 Apr 2016 08:53:42 +0100 Subject: ACPI / ARM64: Remove EXPERT dependency for ACPI on ARM64 When ACPI was originally merged for arm64 it had only been tested on emulators and not on real physical platforms and no platforms were relying on it. This meant that there were concerns that there might be serious issues attempting to use it on practical systems so it had a dependency on EXPERT added to warn people that it was in an early stage of development with very little practical testing. Since then things have moved on a bit. We have seen people testing on real hardware and now have people starting to produce some platforms (the most prominent being the 96boards Cello) which only have ACPI support and which build and run to some useful extent with mainline. This is not to say that ACPI support or support for these systems is completely done, there are still areas being worked on such as PCI, but at this point it seems that we can be reasonably sure that ACPI will be viable for use on ARM64 and that the already merged support works for the cases it handles. For the AMD Seattle based platforms support outside of PCI has been fairly complete in mainline a few releases now. This is also not to say that we don't have vendors working with ACPI who are trying do things that we would not consider optimal but it does not appear that the EXPERT dependency is having a substantial impact on these vendors. Given all this it seems that at this point the EXPERT dependency mainly creates inconvenience for users with systems that are doing the right thing and gets in the way of including the ACPI code in the testing that people are doing on mainline. Removing it should help our ongoing testing cover those platforms with only ACPI support and help ensure that when ACPI code is merged any problems it causes for other users are more easily discovered. Signed-off-by: Mark Brown Acked-by: Graeme Gregory Acked-by: Ard Biesheuvel Reviewed-by: Al Stone Acked-by: Hanjun Guo Acked-by: Catalin Marinas Acked-by: Roy Franz Acked-by: Olof Johansson Acked-by: Timur Tabi Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 2fcf87a..fa0d319 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -5,7 +5,7 @@ menuconfig ACPI bool "ACPI (Advanced Configuration and Power Interface) Support" depends on !IA64_HP_SIM - depends on IA64 || X86 || (ARM64 && EXPERT) + depends on IA64 || X86 || ARM64 depends on PCI select PNP default y if (IA64 || X86) -- cgit v1.1 From 5ad9a7fde07a95b326da9e650b4f0a41b85e47b5 Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Mon, 25 Apr 2016 15:34:58 -0600 Subject: acpi/nfit: Update nfit driver to comply with ACPI 6.1 ACPI 6.1, Table 5-133, updates NVDIMM Control Region Structure as follows. - Valid Fields, Manufacturing Location, and Manufacturing Date are added from reserved range. No change in the structure size. - IDs (SPD values) are stored as arrays of bytes (i.e. big-endian format). The spec clarifies that they need to be represented as arrays of bytes as well. This patch makes the following changes to support this update. - Change the NFIT driver to show SPD ID values in big-endian format. - Change sprintf format to use "0x" instead of "#" since "%#02x" does not prepend '0'. link: http://www.uefi.org/sites/default/files/resources/ACPI_6_1.pdf Signed-off-by: Toshi Kani Cc: Rafael J. Wysocki Cc: Dan Williams Cc: Robert Moore Cc: Robert Elliott Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index d0f35e6..5dc243c 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -816,7 +816,7 @@ static ssize_t vendor_show(struct device *dev, { struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); - return sprintf(buf, "%#x\n", dcr->vendor_id); + return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->vendor_id)); } static DEVICE_ATTR_RO(vendor); @@ -825,7 +825,7 @@ static ssize_t rev_id_show(struct device *dev, { struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); - return sprintf(buf, "%#x\n", dcr->revision_id); + return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->revision_id)); } static DEVICE_ATTR_RO(rev_id); @@ -834,7 +834,7 @@ static ssize_t device_show(struct device *dev, { struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); - return sprintf(buf, "%#x\n", dcr->device_id); + return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->device_id)); } static DEVICE_ATTR_RO(device); @@ -843,7 +843,7 @@ static ssize_t format_show(struct device *dev, { struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); - return sprintf(buf, "%#x\n", dcr->code); + return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->code)); } static DEVICE_ATTR_RO(format); @@ -852,7 +852,7 @@ static ssize_t serial_show(struct device *dev, { struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); - return sprintf(buf, "%#x\n", dcr->serial_number); + return sprintf(buf, "0x%08x\n", be32_to_cpu(dcr->serial_number)); } static DEVICE_ATTR_RO(serial); -- cgit v1.1 From 38a879ba9c0a6849fe26c36e325f754a89848da7 Mon Sep 17 00:00:00 2001 From: Toshi Kani Date: Mon, 25 Apr 2016 15:34:59 -0600 Subject: acpi/nfit: Add sysfs "id" for NVDIMM ID ACPI 6.1, section 5.2.25.9, defines an identifier for an NVDIMM. Change the NFIT driver to add a new sysfs file "id" under nfit directory. Signed-off-by: Toshi Kani Cc: Rafael J. Wysocki Cc: Dan Williams Cc: Robert Moore Cc: Robert Elliott Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 5dc243c..5a7199db 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -870,6 +870,24 @@ static ssize_t flags_show(struct device *dev, } static DEVICE_ATTR_RO(flags); +static ssize_t id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); + + if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID) + return sprintf(buf, "%04x-%02x-%04x-%08x\n", + be16_to_cpu(dcr->vendor_id), + dcr->manufacturing_location, + be16_to_cpu(dcr->manufacturing_date), + be32_to_cpu(dcr->serial_number)); + else + return sprintf(buf, "%04x-%08x\n", + be16_to_cpu(dcr->vendor_id), + be32_to_cpu(dcr->serial_number)); +} +static DEVICE_ATTR_RO(id); + static struct attribute *acpi_nfit_dimm_attributes[] = { &dev_attr_handle.attr, &dev_attr_phys_id.attr, @@ -879,6 +897,7 @@ static struct attribute *acpi_nfit_dimm_attributes[] = { &dev_attr_serial.attr, &dev_attr_rev_id.attr, &dev_attr_flags.attr, + &dev_attr_id.attr, NULL, }; -- cgit v1.1 From cdd9a6b2a7461021a5ee24bc130592c1bdc54cd8 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 19 Apr 2016 18:27:46 -0700 Subject: ACPI / APD: Remove CLK_IS_ROOT This flag is a no-op now (see commit 47b0eeb3dc8a "clk: Deprecate CLK_IS_ROOT", 2016-02-02) so remove it. Signed-off-by: Stephen Boyd Acked-by: Mika Westerberg Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_apd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index f245bf3..1daf9c4 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -62,8 +62,7 @@ static int acpi_apd_setup(struct apd_private_data *pdata) if (dev_desc->fixed_clk_rate) { clk = clk_register_fixed_rate(&pdata->adev->dev, dev_name(&pdata->adev->dev), - NULL, CLK_IS_ROOT, - dev_desc->fixed_clk_rate); + NULL, 0, dev_desc->fixed_clk_rate); clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev)); pdata->clk = clk; } -- cgit v1.1 From 628e35baa8e1e19217eb11b6ccfa96c273757d18 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 19 Apr 2016 18:28:38 -0700 Subject: ACPI / amba: Remove CLK_IS_ROOT This flag is a no-op now (see commit 47b0eeb3dc8a "clk: Deprecate CLK_IS_ROOT", 2016-02-02) so remove it. Signed-off-by: Stephen Boyd Acked-by: Graeme Gregory Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_amba.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_amba.c b/drivers/acpi/acpi_amba.c index 2a61b54..7f77c07 100644 --- a/drivers/acpi/acpi_amba.c +++ b/drivers/acpi/acpi_amba.c @@ -35,8 +35,7 @@ static void amba_register_dummy_clk(void) if (amba_dummy_clk) return; - amba_dummy_clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, - CLK_IS_ROOT, 0); + amba_dummy_clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 0); clk_register_clkdev(amba_dummy_clk, "apb_pclk", NULL); } -- cgit v1.1 From e3654eca70d63704c94a60a2aafc0b3c7b46a00b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 28 Apr 2016 16:17:07 -0700 Subject: nfit, libnvdimm: clarify "commands" vs "_DSMs" Clarify the distinction between "commands", the ioctls userspace calls to request the kernel take some action on a given dimm device, and "_DSMs", the actual function numbers used in the firmware interface to the DIMM. _DSMs are ACPI specific whereas commands are Linux kernel generic. This is in preparation for breaking the 1:1 implicit relationship between the kernel ioctl number space and the firmware specific function numbers. Cc: Jerry Hoemann Cc: Christoph Hellwig Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 21 +++++++++++++-------- drivers/acpi/nfit.h | 4 ++-- 2 files changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index d0f35e6..1b98e9d 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -175,7 +175,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, union acpi_object in_obj, in_buf, *out_obj; struct device *dev = acpi_desc->dev; const char *cmd_name, *dimm_name; - unsigned long dsm_mask; + unsigned long cmd_mask; acpi_handle handle; const u8 *uuid; u32 offset; @@ -189,7 +189,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, return -ENOTTY; dimm_name = nvdimm_name(nvdimm); cmd_name = nvdimm_cmd_name(cmd); - dsm_mask = nfit_mem->dsm_mask; + cmd_mask = nvdimm_cmd_mask(nvdimm); desc = nd_cmd_dimm_desc(cmd); uuid = to_nfit_uuid(NFIT_DEV_DIMM); handle = adev->handle; @@ -197,7 +197,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct acpi_device *adev = to_acpi_dev(acpi_desc); cmd_name = nvdimm_bus_cmd_name(cmd); - dsm_mask = nd_desc->dsm_mask; + cmd_mask = nd_desc->cmd_mask; desc = nd_cmd_bus_desc(cmd); uuid = to_nfit_uuid(NFIT_DEV_BUS); handle = adev->handle; @@ -207,7 +207,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, if (!desc || (cmd && (desc->out_num + desc->in_num == 0))) return -ENOTTY; - if (!test_bit(cmd, &dsm_mask)) + if (!test_bit(cmd, &cmd_mask)) return -ENOTTY; in_obj.type = ACPI_TYPE_PACKAGE; @@ -926,7 +926,8 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, const u8 *uuid = to_nfit_uuid(NFIT_DEV_DIMM); int i; - nfit_mem->dsm_mask = acpi_desc->dimm_dsm_force_en; + /* nfit test assumes 1:1 relationship between commands and dsms */ + nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en; adev = to_acpi_dev(acpi_desc); if (!adev) return 0; @@ -976,9 +977,13 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) if (rc) continue; + /* + * For now there is 1:1 relationship between cmd_mask and + * dsm_mask. + */ nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, acpi_nfit_dimm_attribute_groups, - flags, &nfit_mem->dsm_mask); + flags, nfit_mem->dsm_mask); if (!nvdimm) return -ENOMEM; @@ -1007,14 +1012,14 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc) struct acpi_device *adev; int i; - nd_desc->dsm_mask = acpi_desc->bus_dsm_force_en; + nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en; adev = to_acpi_dev(acpi_desc); if (!adev) return; for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++) if (acpi_check_dsm(adev->handle, uuid, 1, 1ULL << i)) - set_bit(i, &nd_desc->dsm_mask); + set_bit(i, &nd_desc->cmd_mask); } static ssize_t range_index_show(struct device *dev, diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index c75576b..332ee6f 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -132,8 +132,8 @@ struct acpi_nfit_desc { size_t ars_status_size; struct work_struct work; unsigned int cancel:1; - unsigned long dimm_dsm_force_en; - unsigned long bus_dsm_force_en; + unsigned long dimm_cmd_force_en; + unsigned long bus_cmd_force_en; int (*blk_do_io)(struct nd_blk_region *ndbr, resource_size_t dpa, void *iobuf, u64 len, int rw); }; -- cgit v1.1 From 31eca76ba2fc988bf88f16fcf763a0ec4068cd30 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 28 Apr 2016 16:23:43 -0700 Subject: nfit, libnvdimm: limited/whitelisted dimm command marshaling mechanism There are currently 4 known similar but incompatible definitions of the command sets that can be sent to an NVDIMM through ACPI. It is also clear that future platform generations (ACPI or not) will continue to revise and extend the DIMM command set as new devices and use cases arrive. It is obviously untenable to continue to proliferate divergence of these command definitions, and to that end a standardization process has begun to provide for a unified specification. However, that leaves a problem about what to do with this first generation where vendors are already shipping divergence. The Linux kernel can support these initial diverged platforms without giving platform-firmware free reign to continue to diverge and compound kernel maintenance overhead. The kernel implementation can encourage standardization in two ways: 1/ Require that any function code that userspace wants to send be explicitly white-listed in the implementation. For ACPI this means function codes marked as supported by acpi_check_dsm() may only be invoked if they appear in the white-list. A function must be publicly documented before it is added to the white-list. 2/ The above restrictions can be trivially bypassed by using the "vendor-specific" payload command. However, since vendor-specific commands are by definition not publicly documented and have the potential to corrupt the kernel's view of the dimm state, we provide a toggle to disable vendor-specific operations. Enabling undefined behavior is a policy decision that can be made by the platform owner and encourages firmware implementations to choose public over private command implementations. Based on an initial patch from Jerry Hoemann Cc: Jerry Hoemann Cc: Christoph Hellwig Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 101 +++++++++++++++++++++++++++++++++++++++++++--------- drivers/acpi/nfit.h | 14 +++++++- 2 files changed, 98 insertions(+), 17 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 1b98e9d..b85a468 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -171,33 +171,46 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, unsigned int buf_len, int *cmd_rc) { struct acpi_nfit_desc *acpi_desc = to_acpi_nfit_desc(nd_desc); - const struct nd_cmd_desc *desc = NULL; union acpi_object in_obj, in_buf, *out_obj; + const struct nd_cmd_desc *desc = NULL; struct device *dev = acpi_desc->dev; + struct nd_cmd_pkg *call_pkg = NULL; const char *cmd_name, *dimm_name; - unsigned long cmd_mask; + unsigned long cmd_mask, dsm_mask; acpi_handle handle; + unsigned int func; const u8 *uuid; u32 offset; int rc, i; + func = cmd; + if (cmd == ND_CMD_CALL) { + call_pkg = buf; + func = call_pkg->nd_command; + } + if (nvdimm) { struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); struct acpi_device *adev = nfit_mem->adev; if (!adev) return -ENOTTY; + if (call_pkg && nfit_mem->family != call_pkg->nd_family) + return -ENOTTY; + dimm_name = nvdimm_name(nvdimm); cmd_name = nvdimm_cmd_name(cmd); cmd_mask = nvdimm_cmd_mask(nvdimm); + dsm_mask = nfit_mem->dsm_mask; desc = nd_cmd_dimm_desc(cmd); - uuid = to_nfit_uuid(NFIT_DEV_DIMM); + uuid = to_nfit_uuid(nfit_mem->family); handle = adev->handle; } else { struct acpi_device *adev = to_acpi_dev(acpi_desc); cmd_name = nvdimm_bus_cmd_name(cmd); cmd_mask = nd_desc->cmd_mask; + dsm_mask = cmd_mask; desc = nd_cmd_bus_desc(cmd); uuid = to_nfit_uuid(NFIT_DEV_BUS); handle = adev->handle; @@ -207,7 +220,7 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, if (!desc || (cmd && (desc->out_num + desc->in_num == 0))) return -ENOTTY; - if (!test_bit(cmd, &cmd_mask)) + if (!test_bit(cmd, &cmd_mask) || !test_bit(func, &dsm_mask)) return -ENOTTY; in_obj.type = ACPI_TYPE_PACKAGE; @@ -222,21 +235,44 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc, i, buf); + if (call_pkg) { + /* skip over package wrapper */ + in_buf.buffer.pointer = (void *) &call_pkg->nd_payload; + in_buf.buffer.length = call_pkg->nd_size_in; + } + if (IS_ENABLED(CONFIG_ACPI_NFIT_DEBUG)) { - dev_dbg(dev, "%s:%s cmd: %s input length: %d\n", __func__, - dimm_name, cmd_name, in_buf.buffer.length); - print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, - 4, in_buf.buffer.pointer, min_t(u32, 128, - in_buf.buffer.length), true); + dev_dbg(dev, "%s:%s cmd: %d: func: %d input length: %d\n", + __func__, dimm_name, cmd, func, + in_buf.buffer.length); + print_hex_dump_debug("nvdimm in ", DUMP_PREFIX_OFFSET, 4, 4, + in_buf.buffer.pointer, + min_t(u32, 256, in_buf.buffer.length), true); } - out_obj = acpi_evaluate_dsm(handle, uuid, 1, cmd, &in_obj); + out_obj = acpi_evaluate_dsm(handle, uuid, 1, func, &in_obj); if (!out_obj) { dev_dbg(dev, "%s:%s _DSM failed cmd: %s\n", __func__, dimm_name, cmd_name); return -EINVAL; } + if (call_pkg) { + call_pkg->nd_fw_size = out_obj->buffer.length; + memcpy(call_pkg->nd_payload + call_pkg->nd_size_in, + out_obj->buffer.pointer, + min(call_pkg->nd_fw_size, call_pkg->nd_size_out)); + + ACPI_FREE(out_obj); + /* + * Need to support FW function w/o known size in advance. + * Caller can determine required size based upon nd_fw_size. + * If we return an error (like elsewhere) then caller wouldn't + * be able to rely upon data returned to make calculation. + */ + return 0; + } + if (out_obj->package.type != ACPI_TYPE_BUFFER) { dev_dbg(dev, "%s:%s unexpected output object type cmd: %s type: %d\n", __func__, dimm_name, cmd_name, out_obj->type); @@ -923,11 +959,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, { struct acpi_device *adev, *adev_dimm; struct device *dev = acpi_desc->dev; - const u8 *uuid = to_nfit_uuid(NFIT_DEV_DIMM); + unsigned long dsm_mask; + const u8 *uuid; int i; /* nfit test assumes 1:1 relationship between commands and dsms */ nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en; + nfit_mem->family = NVDIMM_FAMILY_INTEL; adev = to_acpi_dev(acpi_desc); if (!adev) return 0; @@ -940,7 +978,31 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, return force_enable_dimms ? 0 : -ENODEV; } - for (i = ND_CMD_SMART; i <= ND_CMD_VENDOR; i++) + /* + * Until standardization materializes we need to consider up to 3 + * different command sets. Note, that checking for function0 (bit0) + * tells us if any commands are reachable through this uuid. + */ + for (i = NVDIMM_FAMILY_INTEL; i <= NVDIMM_FAMILY_HPE2; i++) + if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1)) + break; + + /* limit the supported commands to those that are publicly documented */ + nfit_mem->family = i; + if (nfit_mem->family == NVDIMM_FAMILY_INTEL) + dsm_mask = 0x3fe; + else if (nfit_mem->family == NVDIMM_FAMILY_HPE1) + dsm_mask = 0x1c3c76; + else if (nfit_mem->family == NVDIMM_FAMILY_HPE2) + dsm_mask = 0x1fe; + else { + dev_err(dev, "unknown dimm command family\n"); + nfit_mem->family = -1; + return force_enable_dimms ? 0 : -ENODEV; + } + + uuid = to_nfit_uuid(nfit_mem->family); + for_each_set_bit(i, &dsm_mask, BITS_PER_LONG) if (acpi_check_dsm(adev_dimm->handle, uuid, 1, 1ULL << i)) set_bit(i, &nfit_mem->dsm_mask); @@ -953,8 +1015,8 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) int dimm_count = 0; list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { + unsigned long flags = 0, cmd_mask; struct nvdimm *nvdimm; - unsigned long flags = 0; u32 device_handle; u16 mem_flags; int rc; @@ -978,12 +1040,17 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) continue; /* - * For now there is 1:1 relationship between cmd_mask and - * dsm_mask. + * TODO: provide translation for non-NVDIMM_FAMILY_INTEL + * devices (i.e. from nd_cmd to acpi_dsm) to standardize the + * userspace interface. */ + cmd_mask = 1UL << ND_CMD_CALL; + if (nfit_mem->family == NVDIMM_FAMILY_INTEL) + cmd_mask |= nfit_mem->dsm_mask; + nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem, acpi_nfit_dimm_attribute_groups, - flags, nfit_mem->dsm_mask); + flags, cmd_mask); if (!nvdimm) return -ENOMEM; @@ -2468,6 +2535,8 @@ static __init int nfit_init(void) acpi_str_to_uuid(UUID_PERSISTENT_VIRTUAL_CD, nfit_uuid[NFIT_SPA_PCD]); acpi_str_to_uuid(UUID_NFIT_BUS, nfit_uuid[NFIT_DEV_BUS]); acpi_str_to_uuid(UUID_NFIT_DIMM, nfit_uuid[NFIT_DEV_DIMM]); + acpi_str_to_uuid(UUID_NFIT_DIMM_N_HPE1, nfit_uuid[NFIT_DEV_DIMM_N_HPE1]); + acpi_str_to_uuid(UUID_NFIT_DIMM_N_HPE2, nfit_uuid[NFIT_DEV_DIMM_N_HPE2]); nfit_wq = create_singlethread_workqueue("nfit"); if (!nfit_wq) diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index 332ee6f..f82fda5 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -21,13 +21,25 @@ #include #include +/* ACPI 6.1 */ #define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba" + +/* http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf */ #define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66" + +/* https://github.com/HewlettPackard/hpe-nvm/blob/master/Documentation/ */ +#define UUID_NFIT_DIMM_N_HPE1 "9002c334-acf3-4c0e-9642-a235f0d53bc6" +#define UUID_NFIT_DIMM_N_HPE2 "5008664b-b758-41a0-a03c-27c2f2d04f7e" + #define ACPI_NFIT_MEM_FAILED_MASK (ACPI_NFIT_MEM_SAVE_FAILED \ | ACPI_NFIT_MEM_RESTORE_FAILED | ACPI_NFIT_MEM_FLUSH_FAILED \ | ACPI_NFIT_MEM_NOT_ARMED) enum nfit_uuids { + /* for simplicity alias the uuid index with the family id */ + NFIT_DEV_DIMM = NVDIMM_FAMILY_INTEL, + NFIT_DEV_DIMM_N_HPE1 = NVDIMM_FAMILY_HPE1, + NFIT_DEV_DIMM_N_HPE2 = NVDIMM_FAMILY_HPE2, NFIT_SPA_VOLATILE, NFIT_SPA_PM, NFIT_SPA_DCR, @@ -37,7 +49,6 @@ enum nfit_uuids { NFIT_SPA_PDISK, NFIT_SPA_PCD, NFIT_DEV_BUS, - NFIT_DEV_DIMM, NFIT_UUID_MAX, }; @@ -110,6 +121,7 @@ struct nfit_mem { struct list_head list; struct acpi_device *adev; unsigned long dsm_mask; + int family; }; struct acpi_nfit_desc { -- cgit v1.1 From 30ec5fd464d51876247302da276db082e5675c35 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 28 Apr 2016 18:35:23 -0700 Subject: nfit: fix format interface code byte order per ACPI6.1 ACPI6.1 clarifies that DCR fields are stored as an array of bytes, update the format interface code constants to match. Reviewed-by: Toshi Kani Signed-off-by: Dan Williams --- drivers/acpi/nfit.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index c75576b..c1b4eb2 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -41,11 +41,13 @@ enum nfit_uuids { NFIT_UUID_MAX, }; -enum nfit_fic { - NFIT_FIC_BYTE = 0x101, /* byte-addressable energy backed */ - NFIT_FIC_BLK = 0x201, /* block-addressable non-energy backed */ - NFIT_FIC_BYTEN = 0x301, /* byte-addressable non-energy backed */ -}; +/* + * Region format interface codes are stored as an array of bytes in the + * NFIT DIMM Control Region structure + */ +#define NFIT_FIC_BYTE cpu_to_be16(0x101) /* byte-addressable energy backed */ +#define NFIT_FIC_BLK cpu_to_be16(0x201) /* block-addressable non-energy backed */ +#define NFIT_FIC_BYTEN cpu_to_be16(0x301) /* byte-addressable non-energy backed */ enum { NFIT_BLK_READ_FLUSH = 1, -- cgit v1.1 From 6ca7208569550de43d64db6cf873706c371284a5 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 29 Apr 2016 10:33:23 -0700 Subject: nfit: export subsystem ids as attributes Similar to pci-sysfs export the subsystem information available in the NFIT. ACPI 6.1 clarifies that this data is copied as an array of bytes from the DIMM SPD data. Reported-by: Ryon Jensen Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 5a7199db..0a1ba3d 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -847,6 +847,34 @@ static ssize_t format_show(struct device *dev, } static DEVICE_ATTR_RO(format); +static ssize_t subsystem_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); + + return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_vendor_id)); +} +static DEVICE_ATTR_RO(subsystem_vendor); + +static ssize_t subsystem_rev_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); + + return sprintf(buf, "0x%04x\n", + be16_to_cpu(dcr->subsystem_revision_id)); +} +static DEVICE_ATTR_RO(subsystem_rev_id); + +static ssize_t subsystem_device_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); + + return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_device_id)); +} +static DEVICE_ATTR_RO(subsystem_device); + static ssize_t serial_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -893,9 +921,12 @@ static struct attribute *acpi_nfit_dimm_attributes[] = { &dev_attr_phys_id.attr, &dev_attr_vendor.attr, &dev_attr_device.attr, + &dev_attr_rev_id.attr, + &dev_attr_subsystem_vendor.attr, + &dev_attr_subsystem_device.attr, + &dev_attr_subsystem_rev_id.attr, &dev_attr_format.attr, &dev_attr_serial.attr, - &dev_attr_rev_id.attr, &dev_attr_flags.attr, &dev_attr_id.attr, NULL, -- cgit v1.1 From 2eea65829dc6c20dccbe79726fd0f3fe7f8aa43b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 2 May 2016 09:11:53 -0700 Subject: nfit: fix translation of command status results When transportation of the command completes successfully, it indicates that the 'status' result is valid. Fix the missed checking and translation of the status field at the end of acpi_nfit_ctl(). Otherwise, we fail to handle reported errors and assume commands complete successfully. Reported-by: Linda Knippers Reviewed-by: Johannes Thumshirn Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index d0f35e6..63cc9db 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -287,8 +287,11 @@ static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, offset); rc = -ENXIO; } - } else + } else { rc = 0; + if (cmd_rc) + *cmd_rc = xlat_status(buf, cmd); + } out: ACPI_FREE(out_obj); -- cgit v1.1 From 01c3664de62f89f6777e59173ad8e20b5a4c267f Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 27 Apr 2016 20:45:03 +0800 Subject: video / backlight: remove the backlight_device_registered API Since we will need the backlight_device_get_by_type API, we can use it instead of the backlight_device_registered API whenever necessary so remove the backlight_device_registered API. Signed-off-by: Aaron Lu Acked-by: Jingoo Han Signed-off-by: Rafael J. Wysocki --- drivers/acpi/video_detect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 1316ddd..3d13276 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -358,7 +358,7 @@ enum acpi_backlight_type acpi_video_get_backlight_type(void) if (!(video_caps & ACPI_VIDEO_BACKLIGHT)) return acpi_backlight_vendor; - if (acpi_osi_is_win8() && backlight_device_registered(BACKLIGHT_RAW)) + if (acpi_osi_is_win8() && backlight_device_get_by_type(BACKLIGHT_RAW)) return acpi_backlight_native; return acpi_backlight_video; -- cgit v1.1 From 059500940defe285222d3b189b366dfe7f299cae Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 27 Apr 2016 20:45:04 +0800 Subject: ACPI/video: export acpi_video_get_levels The acpi_video_get_levels is useful for other drivers, i.e. the to-be-added int3406 thermal driver, so export it. Signed-off-by: Aaron Lu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_video.c | 83 +++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 38 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 4361bc9..3d5b8a0 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -191,19 +191,6 @@ struct acpi_video_device_cap { u8 _DDC:1; /* Return the EDID for this device */ }; -struct acpi_video_brightness_flags { - u8 _BCL_no_ac_battery_levels:1; /* no AC/Battery levels in _BCL */ - u8 _BCL_reversed:1; /* _BCL package is in a reversed order */ - u8 _BQC_use_index:1; /* _BQC returns an index value */ -}; - -struct acpi_video_device_brightness { - int curr; - int count; - int *levels; - struct acpi_video_brightness_flags flags; -}; - struct acpi_video_device { unsigned long device_id; struct acpi_video_device_flags flags; @@ -325,7 +312,7 @@ static const struct thermal_cooling_device_ops video_cooling_ops = { */ static int -acpi_video_device_lcd_query_levels(struct acpi_video_device *device, +acpi_video_device_lcd_query_levels(acpi_handle handle, union acpi_object **levels) { int status; @@ -335,7 +322,7 @@ acpi_video_device_lcd_query_levels(struct acpi_video_device *device, *levels = NULL; - status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer); + status = acpi_evaluate_object(handle, "_BCL", NULL, &buffer); if (!ACPI_SUCCESS(status)) return status; obj = (union acpi_object *)buffer.pointer; @@ -766,36 +753,28 @@ static int acpi_video_bqc_quirk(struct acpi_video_device *device, return 0; } - -/* - * Arg: - * device : video output device (LCD, CRT, ..) - * - * Return Value: - * Maximum brightness level - * - * Allocate and initialize device->brightness. - */ - -static int -acpi_video_init_brightness(struct acpi_video_device *device) +int acpi_video_get_levels(struct acpi_device *device, + struct acpi_video_device_brightness **dev_br) { union acpi_object *obj = NULL; int i, max_level = 0, count = 0, level_ac_battery = 0; - unsigned long long level, level_old; union acpi_object *o; struct acpi_video_device_brightness *br = NULL; - int result = -EINVAL; + int result = 0; u32 value; - if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) { + if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device->handle, + &obj))) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available " "LCD brightness level\n")); + result = -ENODEV; goto out; } - if (obj->package.count < 2) + if (obj->package.count < 2) { + result = -EINVAL; goto out; + } br = kzalloc(sizeof(*br), GFP_KERNEL); if (!br) { @@ -861,6 +840,38 @@ acpi_video_init_brightness(struct acpi_video_device *device) "Found unordered _BCL package")); br->count = count; + *dev_br = br; + +out: + kfree(obj); + return result; +out_free: + kfree(br); + goto out; +} +EXPORT_SYMBOL(acpi_video_get_levels); + +/* + * Arg: + * device : video output device (LCD, CRT, ..) + * + * Return Value: + * Maximum brightness level + * + * Allocate and initialize device->brightness. + */ + +static int +acpi_video_init_brightness(struct acpi_video_device *device) +{ + int i, max_level = 0; + unsigned long long level, level_old; + struct acpi_video_device_brightness *br = NULL; + int result = -EINVAL; + + result = acpi_video_get_levels(device->dev, &br); + if (result) + return result; device->brightness = br; /* _BQC uses INDEX while _BCL uses VALUE in some laptops */ @@ -903,17 +914,13 @@ set_level: goto out_free_levels; ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "found %d brightness levels\n", count - 2)); - kfree(obj); - return result; + "found %d brightness levels\n", br->count - 2)); + return 0; out_free_levels: kfree(br->levels); -out_free: kfree(br); -out: device->brightness = NULL; - kfree(obj); return result; } -- cgit v1.1 From 6283f97d5fbfdd1e31694d7f6000eb728cdfba77 Mon Sep 17 00:00:00 2001 From: Betty Dall Date: Sat, 30 Apr 2016 10:03:37 -0600 Subject: ACPI / device_sysfs: Add sysfs support for _HRV hardware revision The ACPI _HRV object on the device is used to supply Linux with the device's hardware revision. This is an optional object. Add sysfs support for the _HRV object if it exists on the device. This change allows users to easily find the hardware version of non-PCI hardware by looking at the sysfs 'hrv' file. It is most useful for non-PCI devices because lspci can list the hardware version for PCI devices. Signed-off-by: Betty Dall Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_sysfs.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index b9afb47..49cc0cb 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c @@ -473,6 +473,21 @@ acpi_device_sun_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(sun, 0444, acpi_device_sun_show, NULL); +static ssize_t +acpi_device_hrv_show(struct device *dev, struct device_attribute *attr, + char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + acpi_status status; + unsigned long long hrv; + + status = acpi_evaluate_integer(acpi_dev->handle, "_HRV", NULL, &hrv); + if (ACPI_FAILURE(status)) + return -EIO; + + return sprintf(buf, "%llu\n", hrv); +} +static DEVICE_ATTR(hrv, 0444, acpi_device_hrv_show, NULL); + static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct acpi_device *acpi_dev = to_acpi_device(dev); @@ -541,6 +556,12 @@ int acpi_device_setup_files(struct acpi_device *dev) goto end; } + if (acpi_has_method(dev->handle, "_HRV")) { + result = device_create_file(&dev->dev, &dev_attr_hrv); + if (result) + goto end; + } + if (acpi_has_method(dev->handle, "_STA")) { result = device_create_file(&dev->dev, &dev_attr_status); if (result) @@ -604,6 +625,9 @@ void acpi_device_remove_files(struct acpi_device *dev) if (acpi_has_method(dev->handle, "_SUN")) device_remove_file(&dev->dev, &dev_attr_sun); + if (acpi_has_method(dev->handle, "_HRV")) + device_remove_file(&dev->dev, &dev_attr_hrv); + if (dev->pnp.unique_id) device_remove_file(&dev->dev, &dev_attr_uid); if (dev->pnp.type.bus_address) -- cgit v1.1 From 6c0244091e8aa98e7977bdddeab2bb864cff9d91 Mon Sep 17 00:00:00 2001 From: Betty Dall Date: Sat, 30 Apr 2016 10:03:38 -0600 Subject: ACPI / device_sysfs: Change _SUN and _STA show functions error return to EIO The error return from a sysfs show function is passed up through the call chain and visible as the return from the read system call. The show functions for the _STA and _SUN object currently return -ENODEV. This patch changes the return to -EIO. ENODEV makes less sense since the "device' exists or there wouldn't be a sysfs file. Signed-off-by: Betty Dall Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index 49cc0cb..e556a3e 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c @@ -467,7 +467,7 @@ acpi_device_sun_show(struct device *dev, struct device_attribute *attr, status = acpi_evaluate_integer(acpi_dev->handle, "_SUN", NULL, &sun); if (ACPI_FAILURE(status)) - return -ENODEV; + return -EIO; return sprintf(buf, "%llu\n", sun); } @@ -496,7 +496,7 @@ static ssize_t status_show(struct device *dev, struct device_attribute *attr, status = acpi_evaluate_integer(acpi_dev->handle, "_STA", NULL, &sta); if (ACPI_FAILURE(status)) - return -ENODEV; + return -EIO; return sprintf(buf, "%llu\n", sta); } -- cgit v1.1 From a508d954af748bc1f5f1d9174f167a8650fca923 Mon Sep 17 00:00:00 2001 From: Betty Dall Date: Sat, 30 Apr 2016 10:03:39 -0600 Subject: ACPI / device_sysfs: Clean up checkpatch errors Cleaning up two existing checkpatch errors (and 2 warnings) in device_sysfs.c since the file is being changed. The change in acpi_device_setup_files() is changing spaces to a tab. Signed-off-by: Betty Dall Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_sysfs.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index e556a3e..7b2c48f 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c @@ -35,7 +35,7 @@ static ssize_t acpi_object_path(acpi_handle handle, char *buf) if (result) return result; - result = sprintf(buf, "%s\n", (char*)path.pointer); + result = sprintf(buf, "%s\n", (char *)path.pointer); kfree(path.pointer); return result; } @@ -333,7 +333,8 @@ int acpi_device_modalias(struct device *dev, char *buf, int size) EXPORT_SYMBOL_GPL(acpi_device_modalias); static ssize_t -acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { +acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) +{ return __acpi_device_modalias(to_acpi_device(dev), buf, 1024); } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); @@ -397,7 +398,8 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); static ssize_t -acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) { +acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ struct acpi_device *acpi_dev = to_acpi_device(dev); return sprintf(buf, "%s\n", acpi_device_hid(acpi_dev)); @@ -568,10 +570,10 @@ int acpi_device_setup_files(struct acpi_device *dev) goto end; } - /* - * If device has _EJ0, 'eject' file is created that is used to trigger - * hot-removal function from userland. - */ + /* + * If device has _EJ0, 'eject' file is created that is used to trigger + * hot-removal function from userland. + */ if (acpi_has_method(dev->handle, "_EJ0")) { result = device_create_file(&dev->dev, &dev_attr_eject); if (result) -- cgit v1.1 From 30c9bb0d7603e7b3f4d6a0ea231e1cddae020c32 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 3 May 2016 16:48:20 +0800 Subject: ACPI / osi: Fix an issue that acpi_osi=!* cannot disable ACPICA internal strings The order of the _OSI related functionalities is as follows: acpi_blacklisted() acpi_dmi_osi_linux() acpi_osi_setup() acpi_osi_setup() acpi_update_interfaces() if "!*" <<<<<<<<<<<<<<<<<<<<<<<< parse_args() __setup("acpi_osi=") acpi_osi_setup_linux() acpi_update_interfaces() if "!*" <<<<<<<<<<<<<<<<<<<<<<<< acpi_early_init() acpi_initialize_subsystem() acpi_ut_initialize_interfaces() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ acpi_bus_init() acpi_os_initialize1() acpi_install_interface_handler(acpi_osi_handler) acpi_osi_setup_late() acpi_update_interfaces() for "!" >>>>>>>>>>>>>>>>>>>>>>>> acpi_osi_handler() Since acpi_osi_setup_linux() can override acpi_dmi_osi_linux(), the command line setting can override the DMI detection. That's why acpi_blacklisted() is put before __setup("acpi_osi="). Then we can notice the following wrong invocation order. There are acpi_update_interfaces() (marked by <<<<) calls invoked before acpi_ut_initialize_interfaces() (marked by ^^^^). This makes it impossible to use acpi_osi=!* correctly from OSI DMI table or from the command line. The use of acpi_osi=!* is meant to disable both ACPICA (acpi_gbl_supported_interfaces) and Linux specific strings (osi_setup_entries) while the ACPICA part should have stopped working because of the order issue. This patch fixes this issue by moving acpi_update_interfaces() to where it is invoked for acpi_osi=! (marked by >>>>) as this is ensured to be invoked after acpi_ut_initialize_interfaces() (marked by ^^^^). Linux specific strings are still handled in the original place in order to make the following command line working: acpi_osi=!* acpi_osi="Module Device". Note that since acpi_osi=!* is meant to further disable linux specific string comparing to the acpi_osi=!, there is no such use case in our bug fixing work and hence there is no one using acpi_osi=!* either from the command line or from the DMI quirks, this issue is just a theoretical issue. Fixes: 741d81280ad2 (ACPI: Add facility to remove all _OSI strings) Cc: 3.12+ # 3.12+ Tested-by: Lukas Wunner Tested-by: Chen Yu Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 814d5f8..f036775 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -135,7 +135,7 @@ static struct osi_linux { unsigned int enable:1; unsigned int dmi:1; unsigned int cmdline:1; - unsigned int default_disabling:1; + u8 default_disabling; } osi_linux = {0, 0, 0, 0}; static u32 acpi_osi_handler(acpi_string interface, u32 supported) @@ -1751,10 +1751,13 @@ void __init acpi_osi_setup(char *str) if (*str == '!') { str++; if (*str == '\0') { - osi_linux.default_disabling = 1; + /* Do not override acpi_osi=!* */ + if (!osi_linux.default_disabling) + osi_linux.default_disabling = + ACPI_DISABLE_ALL_VENDOR_STRINGS; return; } else if (*str == '*') { - acpi_update_interfaces(ACPI_DISABLE_ALL_STRINGS); + osi_linux.default_disabling = ACPI_DISABLE_ALL_STRINGS; for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { osi = &osi_setup_entries[i]; osi->enable = false; @@ -1827,10 +1830,13 @@ static void __init acpi_osi_setup_late(void) acpi_status status; if (osi_linux.default_disabling) { - status = acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS); + status = acpi_update_interfaces(osi_linux.default_disabling); if (ACPI_SUCCESS(status)) - printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors\n"); + printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors%s\n", + osi_linux.default_disabling == + ACPI_DISABLE_ALL_STRINGS ? + " and feature groups" : ""); } for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { -- cgit v1.1 From dbee890bf69ad62c021e299e24881fdcd1f2c481 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 3 May 2016 16:48:27 +0800 Subject: ACPI / osi: Cleanup _OSI("Linux") related code before introducing new support This patch cleans up OSI code in osl.c to make osi_linux work for OSI strings other than "Linux", so it can be re-used for other purposes. Tested-by: Lukas Wunner Tested-by: Chen Yu Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index f036775..9f59dd1 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -131,12 +131,12 @@ static void __init acpi_osi_setup_late(void); * or boot with "acpi_osi=Linux" */ -static struct osi_linux { - unsigned int enable:1; - unsigned int dmi:1; - unsigned int cmdline:1; +static struct acpi_osi_config { + unsigned int linux_enable:1; + unsigned int linux_dmi:1; + unsigned int linux_cmdline:1; u8 default_disabling; -} osi_linux = {0, 0, 0, 0}; +} osi_config = {0, 0, 0, 0}; static u32 acpi_osi_handler(acpi_string interface, u32 supported) { @@ -144,9 +144,9 @@ static u32 acpi_osi_handler(acpi_string interface, u32 supported) printk_once(KERN_NOTICE FW_BUG PREFIX "BIOS _OSI(Linux) query %s%s\n", - osi_linux.enable ? "honored" : "ignored", - osi_linux.cmdline ? " via cmdline" : - osi_linux.dmi ? " via DMI" : ""); + osi_config.linux_enable ? "honored" : "ignored", + osi_config.linux_cmdline ? " via cmdline" : + osi_config.linux_dmi ? " via DMI" : ""); } if (!strcmp("Darwin", interface)) { @@ -1752,12 +1752,12 @@ void __init acpi_osi_setup(char *str) str++; if (*str == '\0') { /* Do not override acpi_osi=!* */ - if (!osi_linux.default_disabling) - osi_linux.default_disabling = + if (!osi_config.default_disabling) + osi_config.default_disabling = ACPI_DISABLE_ALL_VENDOR_STRINGS; return; } else if (*str == '*') { - osi_linux.default_disabling = ACPI_DISABLE_ALL_STRINGS; + osi_config.default_disabling = ACPI_DISABLE_ALL_STRINGS; for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { osi = &osi_setup_entries[i]; osi->enable = false; @@ -1782,10 +1782,10 @@ void __init acpi_osi_setup(char *str) static void __init set_osi_linux(unsigned int enable) { - if (osi_linux.enable != enable) - osi_linux.enable = enable; + if (osi_config.linux_enable != enable) + osi_config.linux_enable = enable; - if (osi_linux.enable) + if (osi_config.linux_enable) acpi_osi_setup("Linux"); else acpi_osi_setup("!Linux"); @@ -1795,8 +1795,9 @@ static void __init set_osi_linux(unsigned int enable) static void __init acpi_cmdline_osi_linux(unsigned int enable) { - osi_linux.cmdline = 1; /* cmdline set the default and override DMI */ - osi_linux.dmi = 0; + /* cmdline set the default and override DMI */ + osi_config.linux_cmdline = 1; + osi_config.linux_dmi = 0; set_osi_linux(enable); return; @@ -1809,7 +1810,8 @@ void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d) if (enable == -1) return; - osi_linux.dmi = 1; /* DMI knows that this box asks OSI(Linux) */ + /* DMI knows that this box asks OSI(Linux) */ + osi_config.linux_dmi = 1; set_osi_linux(enable); return; @@ -1829,12 +1831,12 @@ static void __init acpi_osi_setup_late(void) int i; acpi_status status; - if (osi_linux.default_disabling) { - status = acpi_update_interfaces(osi_linux.default_disabling); + if (osi_config.default_disabling) { + status = acpi_update_interfaces(osi_config.default_disabling); if (ACPI_SUCCESS(status)) printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors%s\n", - osi_linux.default_disabling == + osi_config.default_disabling == ACPI_DISABLE_ALL_STRINGS ? " and feature groups" : ""); } -- cgit v1.1 From a707edebadf1230f202000f29b28b7586ada4aa3 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 3 May 2016 16:48:32 +0800 Subject: ACPI / osi: Add acpi_osi=!! to allow reverting acpi_osi=! This patch introduces acpi_osi=!! so that quirks may use it to revert acpi_osi=!. Tested-by: Lukas Wunner Tested-by: Chen Yu Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 9f59dd1..f801b59 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1763,6 +1763,9 @@ void __init acpi_osi_setup(char *str) osi->enable = false; } return; + } else if (*str == '!') { + osi_config.default_disabling = 0; + return; } enable = false; } -- cgit v1.1 From e10cfdc33a0f23dc8449be7267f0a642e96a2a24 Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Tue, 3 May 2016 16:48:39 +0800 Subject: ACPI / osi: Fix default _OSI(Darwin) support The following commit always reports positive value when Apple hardware queries _OSI("Darwin"): Commit: 7bc5a2bad0b8d9d1ac9f7b8b33150e4ddf197334 Subject: ACPI: Support _OSI("Darwin") correctly However since this implementation places the judgement in runtime, it breaks acpi_osi=!Darwin and cannot return unsupported for _OSI("WinXXX") invoked before invoking _OSI("Darwin"). This patch fixes the issues by reverting the wrong support and implementing the default behavior of _OSI("Darwin")/_OSI("WinXXX") on Apple hardware via DMI matching. Fixes: 7bc5a2bad0b8 (ACPI: Support _OSI("Darwin") correctly) Link: https://bugzilla.kernel.org/show_bug.cgi?id=92111 Reported-and-tested-by: Lukas Wunner Signed-off-by: Chen Yu Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/blacklist.c | 23 +++++++++++++++++++ drivers/acpi/osl.c | 58 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 74 insertions(+), 7 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 96809cd..45390a9 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -133,6 +133,11 @@ int __init acpi_blacklisted(void) return blacklisted; } #ifdef CONFIG_DMI +static int __init dmi_enable_osi_darwin(const struct dmi_system_id *d) +{ + acpi_dmi_osi_darwin(1, d); /* enable */ + return 0; +} static int __init dmi_enable_osi_linux(const struct dmi_system_id *d) { acpi_dmi_osi_linux(1, d); /* enable */ @@ -331,6 +336,24 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { }, }, + /* + * Enable _OSI("Darwin") for all apple platforms. + */ + { + .callback = dmi_enable_osi_darwin, + .ident = "Apple hardware", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + }, + }, + { + .callback = dmi_enable_osi_darwin, + .ident = "Apple hardware", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), + }, + }, + #ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE /* * DELL XPS 13 (2015) switches sound between HDA and I2S diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index f801b59..1b84e46 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -135,6 +135,9 @@ static struct acpi_osi_config { unsigned int linux_enable:1; unsigned int linux_dmi:1; unsigned int linux_cmdline:1; + unsigned int darwin_enable:1; + unsigned int darwin_dmi:1; + unsigned int darwin_cmdline:1; u8 default_disabling; } osi_config = {0, 0, 0, 0}; @@ -150,13 +153,12 @@ static u32 acpi_osi_handler(acpi_string interface, u32 supported) } if (!strcmp("Darwin", interface)) { - /* - * Apple firmware will behave poorly if it receives positive - * answers to "Darwin" and any other OS. Respond positively - * to Darwin and then disable all other vendor strings. - */ - acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS); - supported = ACPI_UINT32_MAX; + + printk_once(KERN_NOTICE PREFIX + "BIOS _OSI(Darwin) query %s%s\n", + osi_config.darwin_enable ? "honored" : "ignored", + osi_config.darwin_cmdline ? " via cmdline" : + osi_config.darwin_dmi ? " via DMI" : ""); } return supported; @@ -1783,6 +1785,44 @@ void __init acpi_osi_setup(char *str) } } +static void __init set_osi_darwin(unsigned int enable) +{ + if (osi_config.darwin_enable != enable) + osi_config.darwin_enable = enable; + + if (enable) { + acpi_osi_setup("!"); + acpi_osi_setup("Darwin"); + } else { + acpi_osi_setup("!!"); + acpi_osi_setup("!Darwin"); + } +} + +static void __init acpi_cmdline_osi_darwin(unsigned int enable) +{ + /* cmdline set the default and override DMI */ + osi_config.darwin_cmdline = 1; + osi_config.darwin_dmi = 0; + set_osi_darwin(enable); + + return; +} + +void __init acpi_dmi_osi_darwin(int enable, const struct dmi_system_id *d) +{ + printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); + + if (enable == -1) + return; + + /* DMI knows that this box asks OSI(Darwin) */ + osi_config.darwin_dmi = 1; + set_osi_darwin(enable); + + return; +} + static void __init set_osi_linux(unsigned int enable) { if (osi_config.linux_enable != enable) @@ -1870,6 +1910,10 @@ static int __init osi_setup(char *str) acpi_cmdline_osi_linux(1); else if (str && !strcmp("!Linux", str)) acpi_cmdline_osi_linux(0); + else if (str && !strcmp("Darwin", str)) + acpi_cmdline_osi_darwin(1); + else if (str && !strcmp("!Darwin", str)) + acpi_cmdline_osi_darwin(0); else acpi_osi_setup(str); -- cgit v1.1 From dc45eb20a83d11ed649169fbe9159ed6bf586c88 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 3 May 2016 16:48:46 +0800 Subject: ACPI / osi: Cleanup OSI handling code to use bool This patch changes "int/unsigned int" to "bool" to simplify the code. Tested-by: Lukas Wunner Tested-by: Chen Yu Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/blacklist.c | 4 ++-- drivers/acpi/osl.c | 36 +++++++++++++----------------------- 2 files changed, 15 insertions(+), 25 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 45390a9..d7d498a 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -135,12 +135,12 @@ int __init acpi_blacklisted(void) #ifdef CONFIG_DMI static int __init dmi_enable_osi_darwin(const struct dmi_system_id *d) { - acpi_dmi_osi_darwin(1, d); /* enable */ + acpi_dmi_osi_darwin(true, d); /* enable */ return 0; } static int __init dmi_enable_osi_linux(const struct dmi_system_id *d) { - acpi_dmi_osi_linux(1, d); /* enable */ + acpi_dmi_osi_linux(true, d); /* enable */ return 0; } static int __init dmi_disable_osi_vista(const struct dmi_system_id *d) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 1b84e46..e30f325 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1785,11 +1785,9 @@ void __init acpi_osi_setup(char *str) } } -static void __init set_osi_darwin(unsigned int enable) +static void __init set_osi_darwin(bool enable) { - if (osi_config.darwin_enable != enable) - osi_config.darwin_enable = enable; - + osi_config.darwin_enable = !!enable; if (enable) { acpi_osi_setup("!"); acpi_osi_setup("Darwin"); @@ -1799,7 +1797,7 @@ static void __init set_osi_darwin(unsigned int enable) } } -static void __init acpi_cmdline_osi_darwin(unsigned int enable) +static void __init acpi_cmdline_osi_darwin(bool enable) { /* cmdline set the default and override DMI */ osi_config.darwin_cmdline = 1; @@ -1809,13 +1807,10 @@ static void __init acpi_cmdline_osi_darwin(unsigned int enable) return; } -void __init acpi_dmi_osi_darwin(int enable, const struct dmi_system_id *d) +void __init acpi_dmi_osi_darwin(bool enable, const struct dmi_system_id *d) { printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); - if (enable == -1) - return; - /* DMI knows that this box asks OSI(Darwin) */ osi_config.darwin_dmi = 1; set_osi_darwin(enable); @@ -1823,12 +1818,10 @@ void __init acpi_dmi_osi_darwin(int enable, const struct dmi_system_id *d) return; } -static void __init set_osi_linux(unsigned int enable) +static void __init set_osi_linux(bool enable) { - if (osi_config.linux_enable != enable) - osi_config.linux_enable = enable; - - if (osi_config.linux_enable) + osi_config.linux_enable = !!enable; + if (enable) acpi_osi_setup("Linux"); else acpi_osi_setup("!Linux"); @@ -1836,7 +1829,7 @@ static void __init set_osi_linux(unsigned int enable) return; } -static void __init acpi_cmdline_osi_linux(unsigned int enable) +static void __init acpi_cmdline_osi_linux(bool enable) { /* cmdline set the default and override DMI */ osi_config.linux_cmdline = 1; @@ -1846,13 +1839,10 @@ static void __init acpi_cmdline_osi_linux(unsigned int enable) return; } -void __init acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d) +void __init acpi_dmi_osi_linux(bool enable, const struct dmi_system_id *d) { printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); - if (enable == -1) - return; - /* DMI knows that this box asks OSI(Linux) */ osi_config.linux_dmi = 1; set_osi_linux(enable); @@ -1907,13 +1897,13 @@ static void __init acpi_osi_setup_late(void) static int __init osi_setup(char *str) { if (str && !strcmp("Linux", str)) - acpi_cmdline_osi_linux(1); + acpi_cmdline_osi_linux(true); else if (str && !strcmp("!Linux", str)) - acpi_cmdline_osi_linux(0); + acpi_cmdline_osi_linux(false); else if (str && !strcmp("Darwin", str)) - acpi_cmdline_osi_darwin(1); + acpi_cmdline_osi_darwin(true); else if (str && !strcmp("!Darwin", str)) - acpi_cmdline_osi_darwin(0); + acpi_cmdline_osi_darwin(false); else acpi_osi_setup(str); -- cgit v1.1 From d5a91d74c6d7da2cebadbb9f2d03e56f84d7be62 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 3 May 2016 16:48:53 +0800 Subject: ACPI / osi: Cleanup coding style issues before creating a separate OSI source file This patch performs necessary cleanups before moving OSI support to another file. 1. Change printk into pr_xxx 2. Do not initialize values to 0 3. Do not append additional "return" at the end of the function 4. Remove useless comments which may easily break line breaking rule After fixing the coding style issues, rename functions to make them looking like acpi_osi_xxx. No functional changes. Tested-by: Lukas Wunner Tested-by: Chen Yu Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/blacklist.c | 10 +++---- drivers/acpi/osl.c | 76 ++++++++++++++++++++---------------------------- 2 files changed, 36 insertions(+), 50 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index d7d498a..ba1601d 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -135,17 +135,17 @@ int __init acpi_blacklisted(void) #ifdef CONFIG_DMI static int __init dmi_enable_osi_darwin(const struct dmi_system_id *d) { - acpi_dmi_osi_darwin(true, d); /* enable */ + acpi_osi_dmi_darwin(true, d); return 0; } static int __init dmi_enable_osi_linux(const struct dmi_system_id *d) { - acpi_dmi_osi_linux(true, d); /* enable */ + acpi_osi_dmi_linux(true, d); return 0; } static int __init dmi_disable_osi_vista(const struct dmi_system_id *d) { - printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); + pr_notice(PREFIX "DMI detected: %s\n", d->ident); acpi_osi_setup("!Windows 2006"); acpi_osi_setup("!Windows 2006 SP1"); acpi_osi_setup("!Windows 2006 SP2"); @@ -153,13 +153,13 @@ static int __init dmi_disable_osi_vista(const struct dmi_system_id *d) } static int __init dmi_disable_osi_win7(const struct dmi_system_id *d) { - printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); + pr_notice(PREFIX "DMI detected: %s\n", d->ident); acpi_osi_setup("!Windows 2009"); return 0; } static int __init dmi_disable_osi_win8(const struct dmi_system_id *d) { - printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); + pr_notice(PREFIX "DMI detected: %s\n", d->ident); acpi_osi_setup("!Windows 2012"); return 0; } diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index e30f325..1340516 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -139,13 +139,13 @@ static struct acpi_osi_config { unsigned int darwin_dmi:1; unsigned int darwin_cmdline:1; u8 default_disabling; -} osi_config = {0, 0, 0, 0}; +} osi_config; static u32 acpi_osi_handler(acpi_string interface, u32 supported) { if (!strcmp("Linux", interface)) { - printk_once(KERN_NOTICE FW_BUG PREFIX + pr_notice_once(FW_BUG PREFIX "BIOS _OSI(Linux) query %s%s\n", osi_config.linux_enable ? "honored" : "ignored", osi_config.linux_cmdline ? " via cmdline" : @@ -154,7 +154,7 @@ static u32 acpi_osi_handler(acpi_string interface, u32 supported) if (!strcmp("Darwin", interface)) { - printk_once(KERN_NOTICE PREFIX + pr_notice_once(PREFIX "BIOS _OSI(Darwin) query %s%s\n", osi_config.darwin_enable ? "honored" : "ignored", osi_config.darwin_cmdline ? " via cmdline" : @@ -1719,15 +1719,15 @@ static int __init acpi_os_name_setup(char *str) __setup("acpi_os_name=", acpi_os_name_setup); -#define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ -#define OSI_STRING_ENTRIES_MAX 16 /* arbitrary */ +#define OSI_STRING_LENGTH_MAX 64 +#define OSI_STRING_ENTRIES_MAX 16 -struct osi_setup_entry { +struct acpi_osi_entry { char string[OSI_STRING_LENGTH_MAX]; bool enable; }; -static struct osi_setup_entry +static struct acpi_osi_entry osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = { {"Module Device", true}, {"Processor Device", true}, @@ -1737,7 +1737,7 @@ static struct osi_setup_entry void __init acpi_osi_setup(char *str) { - struct osi_setup_entry *osi; + struct acpi_osi_entry *osi; bool enable = true; int i; @@ -1745,7 +1745,7 @@ void __init acpi_osi_setup(char *str) return; if (str == NULL || *str == '\0') { - printk(KERN_INFO PREFIX "_OSI method disabled\n"); + pr_info(PREFIX "_OSI method disabled\n"); acpi_gbl_create_osi_method = FALSE; return; } @@ -1785,7 +1785,7 @@ void __init acpi_osi_setup(char *str) } } -static void __init set_osi_darwin(bool enable) +static void __init __acpi_osi_setup_darwin(bool enable) { osi_config.darwin_enable = !!enable; if (enable) { @@ -1797,57 +1797,43 @@ static void __init set_osi_darwin(bool enable) } } -static void __init acpi_cmdline_osi_darwin(bool enable) +static void __init acpi_osi_setup_darwin(bool enable) { - /* cmdline set the default and override DMI */ osi_config.darwin_cmdline = 1; osi_config.darwin_dmi = 0; - set_osi_darwin(enable); - - return; + __acpi_osi_setup_darwin(enable); } -void __init acpi_dmi_osi_darwin(bool enable, const struct dmi_system_id *d) +void __init acpi_osi_dmi_darwin(bool enable, const struct dmi_system_id *d) { - printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); - - /* DMI knows that this box asks OSI(Darwin) */ + pr_notice(PREFIX "DMI detected to setup _OSI(\"Darwin\"): %s\n", + d->ident); osi_config.darwin_dmi = 1; - set_osi_darwin(enable); - - return; + __acpi_osi_setup_darwin(enable); } -static void __init set_osi_linux(bool enable) +static void __init __acpi_osi_setup_linux(bool enable) { osi_config.linux_enable = !!enable; if (enable) acpi_osi_setup("Linux"); else acpi_osi_setup("!Linux"); - - return; } -static void __init acpi_cmdline_osi_linux(bool enable) +static void __init acpi_osi_setup_linux(bool enable) { - /* cmdline set the default and override DMI */ osi_config.linux_cmdline = 1; osi_config.linux_dmi = 0; - set_osi_linux(enable); - - return; + __acpi_osi_setup_linux(enable); } -void __init acpi_dmi_osi_linux(bool enable, const struct dmi_system_id *d) +void __init acpi_osi_dmi_linux(bool enable, const struct dmi_system_id *d) { - printk(KERN_NOTICE PREFIX "DMI detected: %s\n", d->ident); - - /* DMI knows that this box asks OSI(Linux) */ + pr_notice(PREFIX "DMI detected to setup _OSI(\"Linux\"): %s\n", + d->ident); osi_config.linux_dmi = 1; - set_osi_linux(enable); - - return; + __acpi_osi_setup_linux(enable); } /* @@ -1859,7 +1845,7 @@ void __init acpi_dmi_osi_linux(bool enable, const struct dmi_system_id *d) */ static void __init acpi_osi_setup_late(void) { - struct osi_setup_entry *osi; + struct acpi_osi_entry *osi; char *str; int i; acpi_status status; @@ -1868,7 +1854,7 @@ static void __init acpi_osi_setup_late(void) status = acpi_update_interfaces(osi_config.default_disabling); if (ACPI_SUCCESS(status)) - printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors%s\n", + pr_info(PREFIX "Disabled all _OSI OS vendors%s\n", osi_config.default_disabling == ACPI_DISABLE_ALL_STRINGS ? " and feature groups" : ""); @@ -1884,12 +1870,12 @@ static void __init acpi_osi_setup_late(void) status = acpi_install_interface(str); if (ACPI_SUCCESS(status)) - printk(KERN_INFO PREFIX "Added _OSI(%s)\n", str); + pr_info(PREFIX "Added _OSI(%s)\n", str); } else { status = acpi_remove_interface(str); if (ACPI_SUCCESS(status)) - printk(KERN_INFO PREFIX "Deleted _OSI(%s)\n", str); + pr_info(PREFIX "Deleted _OSI(%s)\n", str); } } } @@ -1897,13 +1883,13 @@ static void __init acpi_osi_setup_late(void) static int __init osi_setup(char *str) { if (str && !strcmp("Linux", str)) - acpi_cmdline_osi_linux(true); + acpi_osi_setup_linux(true); else if (str && !strcmp("!Linux", str)) - acpi_cmdline_osi_linux(false); + acpi_osi_setup_linux(false); else if (str && !strcmp("Darwin", str)) - acpi_cmdline_osi_darwin(true); + acpi_osi_setup_darwin(true); else if (str && !strcmp("!Darwin", str)) - acpi_cmdline_osi_darwin(false); + acpi_osi_setup_darwin(false); else acpi_osi_setup(str); -- cgit v1.1 From e5f660ebef68e3ed1a988ad06ba23562153cee5c Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 3 May 2016 16:49:01 +0800 Subject: ACPI / osi: Collect _OSI handling into one single file _OSI handling code grows giant and it's time to move them into one file. This patch collects all _OSI handling code into one single file. So that we only have the following functions to be used externally: early_acpi_osi_init(): Used by DMI detections; acpi_osi_init(): Used to initialize OSI command line settings and install Linux specific _OSI handler; acpi_osi_setup(): The API that should be used by the external quirks. acpi_osi_is_win8(): The API is used by the external drivers to determine if BIOS supports Win8. CONFIG_DMI is not useful as stub dmi_check_system() can make everything stub because of strip. No functional changes. Tested-by: Lukas Wunner Tested-by: Chen Yu Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Makefile | 2 +- drivers/acpi/blacklist.c | 219 +------------------- drivers/acpi/internal.h | 2 + drivers/acpi/osi.c | 522 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/osl.c | 256 +---------------------- 5 files changed, 531 insertions(+), 470 deletions(-) create mode 100644 drivers/acpi/osi.c (limited to 'drivers/acpi') diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index edeb2d1..345df7f 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -18,7 +18,7 @@ obj-$(CONFIG_ACPI) += acpi.o \ acpica/ # All the builtin files are in the "acpi." module_param namespace. -acpi-y += osl.o utils.o reboot.o +acpi-y += osi.o osl.o utils.o reboot.o acpi-y += nvs.o # Power management related files diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index ba1601d..bdc67ba 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -3,7 +3,7 @@ * * Check to see if the given machine has a known bad ACPI BIOS * or if the BIOS is too old. - * Check given machine against acpi_osi_dmi_table[]. + * Check given machine against acpi_rev_dmi_table[]. * * Copyright (C) 2004 Len Brown * Copyright (C) 2002 Andy Grover @@ -47,7 +47,7 @@ struct acpi_blacklist_item { u32 is_critical_error; }; -static struct dmi_system_id acpi_osi_dmi_table[] __initdata; +static struct dmi_system_id acpi_rev_dmi_table[] __initdata; /* * POLICY: If *anything* doesn't work, put it on the blacklist. @@ -128,41 +128,12 @@ int __init acpi_blacklisted(void) } } - dmi_check_system(acpi_osi_dmi_table); + (void)early_acpi_osi_init(); + dmi_check_system(acpi_rev_dmi_table); return blacklisted; } #ifdef CONFIG_DMI -static int __init dmi_enable_osi_darwin(const struct dmi_system_id *d) -{ - acpi_osi_dmi_darwin(true, d); - return 0; -} -static int __init dmi_enable_osi_linux(const struct dmi_system_id *d) -{ - acpi_osi_dmi_linux(true, d); - return 0; -} -static int __init dmi_disable_osi_vista(const struct dmi_system_id *d) -{ - pr_notice(PREFIX "DMI detected: %s\n", d->ident); - acpi_osi_setup("!Windows 2006"); - acpi_osi_setup("!Windows 2006 SP1"); - acpi_osi_setup("!Windows 2006 SP2"); - return 0; -} -static int __init dmi_disable_osi_win7(const struct dmi_system_id *d) -{ - pr_notice(PREFIX "DMI detected: %s\n", d->ident); - acpi_osi_setup("!Windows 2009"); - return 0; -} -static int __init dmi_disable_osi_win8(const struct dmi_system_id *d) -{ - pr_notice(PREFIX "DMI detected: %s\n", d->ident); - acpi_osi_setup("!Windows 2012"); - return 0; -} #ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE static int __init dmi_enable_rev_override(const struct dmi_system_id *d) { @@ -173,187 +144,7 @@ static int __init dmi_enable_rev_override(const struct dmi_system_id *d) } #endif -static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { - { - .callback = dmi_disable_osi_vista, - .ident = "Fujitsu Siemens", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), - DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"), - }, - }, - { - /* - * There have a NVIF method in MSI GX723 DSDT need call by Nvidia - * driver (e.g. nouveau) when user press brightness hotkey. - * Currently, nouveau driver didn't do the job and it causes there - * have a infinite while loop in DSDT when user press hotkey. - * We add MSI GX723's dmi information to this table for workaround - * this issue. - * Will remove MSI GX723 from the table after nouveau grows support. - */ - .callback = dmi_disable_osi_vista, - .ident = "MSI GX723", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), - DMI_MATCH(DMI_PRODUCT_NAME, "GX723"), - }, - }, - { - .callback = dmi_disable_osi_vista, - .ident = "Sony VGN-NS10J_S", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"), - }, - }, - { - .callback = dmi_disable_osi_vista, - .ident = "Sony VGN-SR290J", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR290J"), - }, - }, - { - .callback = dmi_disable_osi_vista, - .ident = "VGN-NS50B_L", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS50B_L"), - }, - }, - { - .callback = dmi_disable_osi_vista, - .ident = "VGN-SR19XN", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"), - }, - }, - { - .callback = dmi_disable_osi_vista, - .ident = "Toshiba Satellite L355", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Satellite L355"), - }, - }, - { - .callback = dmi_disable_osi_win7, - .ident = "ASUS K50IJ", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "K50IJ"), - }, - }, - { - .callback = dmi_disable_osi_vista, - .ident = "Toshiba P305D", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P305D"), - }, - }, - { - .callback = dmi_disable_osi_vista, - .ident = "Toshiba NB100", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "NB100"), - }, - }, - - /* - * The wireless hotkey does not work on those machines when - * returning true for _OSI("Windows 2012") - */ - { - .callback = dmi_disable_osi_win8, - .ident = "Dell Inspiron 7737", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"), - }, - }, - { - .callback = dmi_disable_osi_win8, - .ident = "Dell Inspiron 7537", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"), - }, - }, - { - .callback = dmi_disable_osi_win8, - .ident = "Dell Inspiron 5437", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"), - }, - }, - { - .callback = dmi_disable_osi_win8, - .ident = "Dell Inspiron 3437", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"), - }, - }, - { - .callback = dmi_disable_osi_win8, - .ident = "Dell Vostro 3446", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"), - }, - }, - { - .callback = dmi_disable_osi_win8, - .ident = "Dell Vostro 3546", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"), - }, - }, - - /* - * BIOS invocation of _OSI(Linux) is almost always a BIOS bug. - * Linux ignores it, except for the machines enumerated below. - */ - - /* - * Without this this EEEpc exports a non working WMI interface, with - * this it exports a working "good old" eeepc_laptop interface, fixing - * both brightness control, and rfkill not working. - */ - { - .callback = dmi_enable_osi_linux, - .ident = "Asus EEE PC 1015PX", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"), - }, - }, - - /* - * Enable _OSI("Darwin") for all apple platforms. - */ - { - .callback = dmi_enable_osi_darwin, - .ident = "Apple hardware", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - }, - }, - { - .callback = dmi_enable_osi_darwin, - .ident = "Apple hardware", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), - }, - }, - +static struct dmi_system_id acpi_rev_dmi_table[] __initdata = { #ifdef CONFIG_ACPI_REV_OVERRIDE_POSSIBLE /* * DELL XPS 13 (2015) switches sound between HDA and I2S diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 7c18847..a8780a2 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -20,6 +20,8 @@ #define PREFIX "ACPI: " +int early_acpi_osi_init(void); +int acpi_osi_init(void); void acpi_initrd_initialize_tables(void); acpi_status acpi_os_initialize1(void); void init_acpi_device_notify(void); diff --git a/drivers/acpi/osi.c b/drivers/acpi/osi.c new file mode 100644 index 0000000..849f9d2 --- /dev/null +++ b/drivers/acpi/osi.c @@ -0,0 +1,522 @@ +/* + * osi.c - _OSI implementation + * + * Copyright (C) 2016 Intel Corporation + * Author: Lv Zheng + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/* Uncomment next line to get verbose printout */ +/* #define DEBUG */ +#define pr_fmt(fmt) "ACPI: " fmt + +#include +#include +#include +#include + +#include "internal.h" + + +#define OSI_STRING_LENGTH_MAX 64 +#define OSI_STRING_ENTRIES_MAX 16 + +struct acpi_osi_entry { + char string[OSI_STRING_LENGTH_MAX]; + bool enable; +}; + +static struct acpi_osi_config { + u8 default_disabling; + unsigned int linux_enable:1; + unsigned int linux_dmi:1; + unsigned int linux_cmdline:1; + unsigned int darwin_enable:1; + unsigned int darwin_dmi:1; + unsigned int darwin_cmdline:1; +} osi_config; + +static struct acpi_osi_config osi_config; +static struct acpi_osi_entry +osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = { + {"Module Device", true}, + {"Processor Device", true}, + {"3.0 _SCP Extensions", true}, + {"Processor Aggregator Device", true}, +}; + +static u32 acpi_osi_handler(acpi_string interface, u32 supported) +{ + if (!strcmp("Linux", interface)) { + pr_notice_once(FW_BUG + "BIOS _OSI(Linux) query %s%s\n", + osi_config.linux_enable ? "honored" : "ignored", + osi_config.linux_cmdline ? " via cmdline" : + osi_config.linux_dmi ? " via DMI" : ""); + } + if (!strcmp("Darwin", interface)) { + pr_notice_once( + "BIOS _OSI(Darwin) query %s%s\n", + osi_config.darwin_enable ? "honored" : "ignored", + osi_config.darwin_cmdline ? " via cmdline" : + osi_config.darwin_dmi ? " via DMI" : ""); + } + + return supported; +} + +void __init acpi_osi_setup(char *str) +{ + struct acpi_osi_entry *osi; + bool enable = true; + int i; + + if (!acpi_gbl_create_osi_method) + return; + + if (str == NULL || *str == '\0') { + pr_info("_OSI method disabled\n"); + acpi_gbl_create_osi_method = FALSE; + return; + } + + if (*str == '!') { + str++; + if (*str == '\0') { + /* Do not override acpi_osi=!* */ + if (!osi_config.default_disabling) + osi_config.default_disabling = + ACPI_DISABLE_ALL_VENDOR_STRINGS; + return; + } else if (*str == '*') { + osi_config.default_disabling = ACPI_DISABLE_ALL_STRINGS; + for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { + osi = &osi_setup_entries[i]; + osi->enable = false; + } + return; + } else if (*str == '!') { + osi_config.default_disabling = 0; + return; + } + enable = false; + } + + for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { + osi = &osi_setup_entries[i]; + if (!strcmp(osi->string, str)) { + osi->enable = enable; + break; + } else if (osi->string[0] == '\0') { + osi->enable = enable; + strncpy(osi->string, str, OSI_STRING_LENGTH_MAX); + break; + } + } +} + +static void __init __acpi_osi_setup_darwin(bool enable) +{ + osi_config.darwin_enable = !!enable; + if (enable) { + acpi_osi_setup("!"); + acpi_osi_setup("Darwin"); + } else { + acpi_osi_setup("!!"); + acpi_osi_setup("!Darwin"); + } +} + +static void __init acpi_osi_setup_darwin(bool enable) +{ + /* Override acpi_osi_dmi_blacklisted() */ + osi_config.darwin_dmi = 0; + osi_config.darwin_cmdline = 1; + __acpi_osi_setup_darwin(enable); +} + +/* + * The story of _OSI(Linux) + * + * From pre-history through Linux-2.6.22, Linux responded TRUE upon a BIOS + * OSI(Linux) query. + * + * Unfortunately, reference BIOS writers got wind of this and put + * OSI(Linux) in their example code, quickly exposing this string as + * ill-conceived and opening the door to an un-bounded number of BIOS + * incompatibilities. + * + * For example, OSI(Linux) was used on resume to re-POST a video card on + * one system, because Linux at that time could not do a speedy restore in + * its native driver. But then upon gaining quick native restore + * capability, Linux has no way to tell the BIOS to skip the time-consuming + * POST -- putting Linux at a permanent performance disadvantage. On + * another system, the BIOS writer used OSI(Linux) to infer native OS + * support for IPMI! On other systems, OSI(Linux) simply got in the way of + * Linux claiming to be compatible with other operating systems, exposing + * BIOS issues such as skipped device initialization. + * + * So "Linux" turned out to be a really poor chose of OSI string, and from + * Linux-2.6.23 onward we respond FALSE. + * + * BIOS writers should NOT query _OSI(Linux) on future systems. Linux will + * complain on the console when it sees it, and return FALSE. To get Linux + * to return TRUE for your system will require a kernel source update to + * add a DMI entry, or boot with "acpi_osi=Linux" + */ +static void __init __acpi_osi_setup_linux(bool enable) +{ + osi_config.linux_enable = !!enable; + if (enable) + acpi_osi_setup("Linux"); + else + acpi_osi_setup("!Linux"); +} + +static void __init acpi_osi_setup_linux(bool enable) +{ + /* Override acpi_osi_dmi_blacklisted() */ + osi_config.linux_dmi = 0; + osi_config.linux_cmdline = 1; + __acpi_osi_setup_linux(enable); +} + +/* + * Modify the list of "OS Interfaces" reported to BIOS via _OSI + * + * empty string disables _OSI + * string starting with '!' disables that string + * otherwise string is added to list, augmenting built-in strings + */ +static void __init acpi_osi_setup_late(void) +{ + struct acpi_osi_entry *osi; + char *str; + int i; + acpi_status status; + + if (osi_config.default_disabling) { + status = acpi_update_interfaces(osi_config.default_disabling); + if (ACPI_SUCCESS(status)) + pr_info("Disabled all _OSI OS vendors%s\n", + osi_config.default_disabling == + ACPI_DISABLE_ALL_STRINGS ? + " and feature groups" : ""); + } + + for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { + osi = &osi_setup_entries[i]; + str = osi->string; + if (*str == '\0') + break; + if (osi->enable) { + status = acpi_install_interface(str); + if (ACPI_SUCCESS(status)) + pr_info("Added _OSI(%s)\n", str); + } else { + status = acpi_remove_interface(str); + if (ACPI_SUCCESS(status)) + pr_info("Deleted _OSI(%s)\n", str); + } + } +} + +static int __init osi_setup(char *str) +{ + if (str && !strcmp("Linux", str)) + acpi_osi_setup_linux(true); + else if (str && !strcmp("!Linux", str)) + acpi_osi_setup_linux(false); + else if (str && !strcmp("Darwin", str)) + acpi_osi_setup_darwin(true); + else if (str && !strcmp("!Darwin", str)) + acpi_osi_setup_darwin(false); + else + acpi_osi_setup(str); + + return 1; +} +__setup("acpi_osi=", osi_setup); + +bool acpi_osi_is_win8(void) +{ + return acpi_gbl_osi_data >= ACPI_OSI_WIN_8; +} +EXPORT_SYMBOL(acpi_osi_is_win8); + +static void __init acpi_osi_dmi_darwin(bool enable, + const struct dmi_system_id *d) +{ + pr_notice("DMI detected to setup _OSI(\"Darwin\"): %s\n", d->ident); + osi_config.darwin_dmi = 1; + __acpi_osi_setup_darwin(enable); +} + +void __init acpi_osi_dmi_linux(bool enable, const struct dmi_system_id *d) +{ + pr_notice("DMI detected to setup _OSI(\"Linux\"): %s\n", d->ident); + osi_config.linux_dmi = 1; + __acpi_osi_setup_linux(enable); +} + +static int __init dmi_enable_osi_darwin(const struct dmi_system_id *d) +{ + acpi_osi_dmi_darwin(true, d); + + return 0; +} + +static int __init dmi_enable_osi_linux(const struct dmi_system_id *d) +{ + acpi_osi_dmi_linux(true, d); + + return 0; +} + +static int __init dmi_disable_osi_vista(const struct dmi_system_id *d) +{ + pr_notice("DMI detected: %s\n", d->ident); + acpi_osi_setup("!Windows 2006"); + acpi_osi_setup("!Windows 2006 SP1"); + acpi_osi_setup("!Windows 2006 SP2"); + + return 0; +} + +static int __init dmi_disable_osi_win7(const struct dmi_system_id *d) +{ + pr_notice("DMI detected: %s\n", d->ident); + acpi_osi_setup("!Windows 2009"); + + return 0; +} + +static int __init dmi_disable_osi_win8(const struct dmi_system_id *d) +{ + pr_notice("DMI detected: %s\n", d->ident); + acpi_osi_setup("!Windows 2012"); + + return 0; +} + +/* + * Linux default _OSI response behavior is determined by this DMI table. + * + * Note that _OSI("Linux")/_OSI("Darwin") determined here can be overridden + * by acpi_osi=!Linux/acpi_osi=!Darwin command line options. + */ +static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { + { + .callback = dmi_disable_osi_vista, + .ident = "Fujitsu Siemens", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"), + }, + }, + { + /* + * There have a NVIF method in MSI GX723 DSDT need call by Nvidia + * driver (e.g. nouveau) when user press brightness hotkey. + * Currently, nouveau driver didn't do the job and it causes there + * have a infinite while loop in DSDT when user press hotkey. + * We add MSI GX723's dmi information to this table for workaround + * this issue. + * Will remove MSI GX723 from the table after nouveau grows support. + */ + .callback = dmi_disable_osi_vista, + .ident = "MSI GX723", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX723"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Sony VGN-NS10J_S", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Sony VGN-SR290J", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR290J"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "VGN-NS50B_L", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS50B_L"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "VGN-SR19XN", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-SR19XN"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Toshiba Satellite L355", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Satellite L355"), + }, + }, + { + .callback = dmi_disable_osi_win7, + .ident = "ASUS K50IJ", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "K50IJ"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Toshiba P305D", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P305D"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Toshiba NB100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "NB100"), + }, + }, + + /* + * The wireless hotkey does not work on those machines when + * returning true for _OSI("Windows 2012") + */ + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Inspiron 7737", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7737"), + }, + }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Inspiron 7537", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7537"), + }, + }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Inspiron 5437", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5437"), + }, + }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Inspiron 3437", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 3437"), + }, + }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Vostro 3446", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3446"), + }, + }, + { + .callback = dmi_disable_osi_win8, + .ident = "Dell Vostro 3546", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3546"), + }, + }, + + /* + * BIOS invocation of _OSI(Linux) is almost always a BIOS bug. + * Linux ignores it, except for the machines enumerated below. + */ + + /* + * Without this this EEEpc exports a non working WMI interface, with + * this it exports a working "good old" eeepc_laptop interface, fixing + * both brightness control, and rfkill not working. + */ + { + .callback = dmi_enable_osi_linux, + .ident = "Asus EEE PC 1015PX", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "1015PX"), + }, + }, + + /* + * Enable _OSI("Darwin") for all apple platforms. + */ + { + .callback = dmi_enable_osi_darwin, + .ident = "Apple hardware", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + }, + }, + { + .callback = dmi_enable_osi_darwin, + .ident = "Apple hardware", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Computer, Inc."), + }, + }, + {} +}; + +static __init void acpi_osi_dmi_blacklisted(void) +{ + dmi_check_system(acpi_osi_dmi_table); +} + +int __init early_acpi_osi_init(void) +{ + acpi_osi_dmi_blacklisted(); + + return 0; +} + +int __init acpi_osi_init(void) +{ + acpi_install_interface_handler(acpi_osi_handler); + acpi_osi_setup_late(); + + return 0; +} diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 1340516..29af6b4 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -96,74 +96,6 @@ struct acpi_ioremap { static LIST_HEAD(acpi_ioremaps); static DEFINE_MUTEX(acpi_ioremap_lock); -static void __init acpi_osi_setup_late(void); - -/* - * The story of _OSI(Linux) - * - * From pre-history through Linux-2.6.22, - * Linux responded TRUE upon a BIOS OSI(Linux) query. - * - * Unfortunately, reference BIOS writers got wind of this - * and put OSI(Linux) in their example code, quickly exposing - * this string as ill-conceived and opening the door to - * an un-bounded number of BIOS incompatibilities. - * - * For example, OSI(Linux) was used on resume to re-POST a - * video card on one system, because Linux at that time - * could not do a speedy restore in its native driver. - * But then upon gaining quick native restore capability, - * Linux has no way to tell the BIOS to skip the time-consuming - * POST -- putting Linux at a permanent performance disadvantage. - * On another system, the BIOS writer used OSI(Linux) - * to infer native OS support for IPMI! On other systems, - * OSI(Linux) simply got in the way of Linux claiming to - * be compatible with other operating systems, exposing - * BIOS issues such as skipped device initialization. - * - * So "Linux" turned out to be a really poor chose of - * OSI string, and from Linux-2.6.23 onward we respond FALSE. - * - * BIOS writers should NOT query _OSI(Linux) on future systems. - * Linux will complain on the console when it sees it, and return FALSE. - * To get Linux to return TRUE for your system will require - * a kernel source update to add a DMI entry, - * or boot with "acpi_osi=Linux" - */ - -static struct acpi_osi_config { - unsigned int linux_enable:1; - unsigned int linux_dmi:1; - unsigned int linux_cmdline:1; - unsigned int darwin_enable:1; - unsigned int darwin_dmi:1; - unsigned int darwin_cmdline:1; - u8 default_disabling; -} osi_config; - -static u32 acpi_osi_handler(acpi_string interface, u32 supported) -{ - if (!strcmp("Linux", interface)) { - - pr_notice_once(FW_BUG PREFIX - "BIOS _OSI(Linux) query %s%s\n", - osi_config.linux_enable ? "honored" : "ignored", - osi_config.linux_cmdline ? " via cmdline" : - osi_config.linux_dmi ? " via DMI" : ""); - } - - if (!strcmp("Darwin", interface)) { - - pr_notice_once(PREFIX - "BIOS _OSI(Darwin) query %s%s\n", - osi_config.darwin_enable ? "honored" : "ignored", - osi_config.darwin_cmdline ? " via cmdline" : - osi_config.darwin_dmi ? " via DMI" : ""); - } - - return supported; -} - static void __init acpi_request_region (struct acpi_generic_address *gas, unsigned int length, char *desc) { @@ -1719,185 +1651,6 @@ static int __init acpi_os_name_setup(char *str) __setup("acpi_os_name=", acpi_os_name_setup); -#define OSI_STRING_LENGTH_MAX 64 -#define OSI_STRING_ENTRIES_MAX 16 - -struct acpi_osi_entry { - char string[OSI_STRING_LENGTH_MAX]; - bool enable; -}; - -static struct acpi_osi_entry - osi_setup_entries[OSI_STRING_ENTRIES_MAX] __initdata = { - {"Module Device", true}, - {"Processor Device", true}, - {"3.0 _SCP Extensions", true}, - {"Processor Aggregator Device", true}, -}; - -void __init acpi_osi_setup(char *str) -{ - struct acpi_osi_entry *osi; - bool enable = true; - int i; - - if (!acpi_gbl_create_osi_method) - return; - - if (str == NULL || *str == '\0') { - pr_info(PREFIX "_OSI method disabled\n"); - acpi_gbl_create_osi_method = FALSE; - return; - } - - if (*str == '!') { - str++; - if (*str == '\0') { - /* Do not override acpi_osi=!* */ - if (!osi_config.default_disabling) - osi_config.default_disabling = - ACPI_DISABLE_ALL_VENDOR_STRINGS; - return; - } else if (*str == '*') { - osi_config.default_disabling = ACPI_DISABLE_ALL_STRINGS; - for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { - osi = &osi_setup_entries[i]; - osi->enable = false; - } - return; - } else if (*str == '!') { - osi_config.default_disabling = 0; - return; - } - enable = false; - } - - for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { - osi = &osi_setup_entries[i]; - if (!strcmp(osi->string, str)) { - osi->enable = enable; - break; - } else if (osi->string[0] == '\0') { - osi->enable = enable; - strncpy(osi->string, str, OSI_STRING_LENGTH_MAX); - break; - } - } -} - -static void __init __acpi_osi_setup_darwin(bool enable) -{ - osi_config.darwin_enable = !!enable; - if (enable) { - acpi_osi_setup("!"); - acpi_osi_setup("Darwin"); - } else { - acpi_osi_setup("!!"); - acpi_osi_setup("!Darwin"); - } -} - -static void __init acpi_osi_setup_darwin(bool enable) -{ - osi_config.darwin_cmdline = 1; - osi_config.darwin_dmi = 0; - __acpi_osi_setup_darwin(enable); -} - -void __init acpi_osi_dmi_darwin(bool enable, const struct dmi_system_id *d) -{ - pr_notice(PREFIX "DMI detected to setup _OSI(\"Darwin\"): %s\n", - d->ident); - osi_config.darwin_dmi = 1; - __acpi_osi_setup_darwin(enable); -} - -static void __init __acpi_osi_setup_linux(bool enable) -{ - osi_config.linux_enable = !!enable; - if (enable) - acpi_osi_setup("Linux"); - else - acpi_osi_setup("!Linux"); -} - -static void __init acpi_osi_setup_linux(bool enable) -{ - osi_config.linux_cmdline = 1; - osi_config.linux_dmi = 0; - __acpi_osi_setup_linux(enable); -} - -void __init acpi_osi_dmi_linux(bool enable, const struct dmi_system_id *d) -{ - pr_notice(PREFIX "DMI detected to setup _OSI(\"Linux\"): %s\n", - d->ident); - osi_config.linux_dmi = 1; - __acpi_osi_setup_linux(enable); -} - -/* - * Modify the list of "OS Interfaces" reported to BIOS via _OSI - * - * empty string disables _OSI - * string starting with '!' disables that string - * otherwise string is added to list, augmenting built-in strings - */ -static void __init acpi_osi_setup_late(void) -{ - struct acpi_osi_entry *osi; - char *str; - int i; - acpi_status status; - - if (osi_config.default_disabling) { - status = acpi_update_interfaces(osi_config.default_disabling); - - if (ACPI_SUCCESS(status)) - pr_info(PREFIX "Disabled all _OSI OS vendors%s\n", - osi_config.default_disabling == - ACPI_DISABLE_ALL_STRINGS ? - " and feature groups" : ""); - } - - for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { - osi = &osi_setup_entries[i]; - str = osi->string; - - if (*str == '\0') - break; - if (osi->enable) { - status = acpi_install_interface(str); - - if (ACPI_SUCCESS(status)) - pr_info(PREFIX "Added _OSI(%s)\n", str); - } else { - status = acpi_remove_interface(str); - - if (ACPI_SUCCESS(status)) - pr_info(PREFIX "Deleted _OSI(%s)\n", str); - } - } -} - -static int __init osi_setup(char *str) -{ - if (str && !strcmp("Linux", str)) - acpi_osi_setup_linux(true); - else if (str && !strcmp("!Linux", str)) - acpi_osi_setup_linux(false); - else if (str && !strcmp("Darwin", str)) - acpi_osi_setup_darwin(true); - else if (str && !strcmp("!Darwin", str)) - acpi_osi_setup_darwin(false); - else - acpi_osi_setup(str); - - return 1; -} - -__setup("acpi_osi=", osi_setup); - /* * Disable the auto-serialization of named objects creation methods. * @@ -2017,12 +1770,6 @@ int acpi_resources_are_enforced(void) } EXPORT_SYMBOL(acpi_resources_are_enforced); -bool acpi_osi_is_win8(void) -{ - return acpi_gbl_osi_data >= ACPI_OSI_WIN_8; -} -EXPORT_SYMBOL(acpi_osi_is_win8); - /* * Deallocate the memory for a spinlock. */ @@ -2188,8 +1935,7 @@ acpi_status __init acpi_os_initialize1(void) BUG_ON(!kacpid_wq); BUG_ON(!kacpi_notify_wq); BUG_ON(!kacpi_hotplug_wq); - acpi_install_interface_handler(acpi_osi_handler); - acpi_osi_setup_late(); + acpi_osi_init(); return AE_OK; } -- cgit v1.1 From 103544d86976338057d6a91f721b49d3acc7df7f Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sun, 17 Apr 2016 13:36:53 -0400 Subject: ACPI,PCI,IRQ: reduce resource requirements Code has been redesigned to calculate penalty requirements on the fly. This significantly simplifies the implementation and removes some of the init calls from x86 architecture. Signed-off-by: Sinan Kaya Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_link.c | 97 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 29 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index ededa90..cc0ba16 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "internal.h" @@ -440,7 +441,6 @@ static int acpi_pci_link_set(struct acpi_pci_link *link, int irq) #define ACPI_MAX_IRQS 256 #define ACPI_MAX_ISA_IRQ 16 -#define PIRQ_PENALTY_PCI_AVAILABLE (0) #define PIRQ_PENALTY_PCI_POSSIBLE (16*16) #define PIRQ_PENALTY_PCI_USING (16*16*16) #define PIRQ_PENALTY_ISA_TYPICAL (16*16*16*16) @@ -457,9 +457,9 @@ static int acpi_irq_penalty[ACPI_MAX_IRQS] = { PIRQ_PENALTY_ISA_TYPICAL, /* IRQ6 */ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ7 parallel, spurious */ PIRQ_PENALTY_ISA_TYPICAL, /* IRQ8 rtc, sometimes */ - PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ9 PCI, often acpi */ - PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ10 PCI */ - PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ11 PCI */ + 0, /* IRQ9 PCI, often acpi */ + 0, /* IRQ10 PCI */ + 0, /* IRQ11 PCI */ PIRQ_PENALTY_ISA_USED, /* IRQ12 mouse */ PIRQ_PENALTY_ISA_USED, /* IRQ13 fpe, sometimes */ PIRQ_PENALTY_ISA_USED, /* IRQ14 ide0 */ @@ -467,6 +467,60 @@ static int acpi_irq_penalty[ACPI_MAX_IRQS] = { /* >IRQ15 */ }; +static int acpi_irq_pci_sharing_penalty(int irq) +{ + struct acpi_pci_link *link; + int penalty = 0; + + list_for_each_entry(link, &acpi_link_list, list) { + /* + * If a link is active, penalize its IRQ heavily + * so we try to choose a different IRQ. + */ + if (link->irq.active && link->irq.active == irq) + penalty += PIRQ_PENALTY_PCI_USING; + else { + int i; + + /* + * If a link is inactive, penalize the IRQs it + * might use, but not as severely. + */ + for (i = 0; i < link->irq.possible_count; i++) + if (link->irq.possible[i] == irq) + penalty += PIRQ_PENALTY_PCI_POSSIBLE / + link->irq.possible_count; + } + } + + return penalty; +} + +static int acpi_irq_get_penalty(int irq) +{ + int penalty = 0; + + if (irq < ACPI_MAX_ISA_IRQ) + penalty += acpi_irq_penalty[irq]; + + /* + * Penalize IRQ used by ACPI SCI. If ACPI SCI pin attributes conflict + * with PCI IRQ attributes, mark ACPI SCI as ISA_ALWAYS so it won't be + * use for PCI IRQs. + */ + if (irq == acpi_gbl_FADT.sci_interrupt) { + u32 type = irq_get_trigger_type(irq) & IRQ_TYPE_SENSE_MASK; + + if (type != IRQ_TYPE_LEVEL_LOW) + penalty += PIRQ_PENALTY_ISA_ALWAYS; + else + penalty += PIRQ_PENALTY_PCI_USING; + } + + penalty += acpi_irq_pci_sharing_penalty(irq); + return penalty; +} + int __init acpi_irq_penalty_init(void) { struct acpi_pci_link *link; @@ -547,12 +601,12 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link) * the use of IRQs 9, 10, 11, and >15. */ for (i = (link->irq.possible_count - 1); i >= 0; i--) { - if (acpi_irq_penalty[irq] > - acpi_irq_penalty[link->irq.possible[i]]) + if (acpi_irq_get_penalty(irq) > + acpi_irq_get_penalty(link->irq.possible[i])) irq = link->irq.possible[i]; } } - if (acpi_irq_penalty[irq] >= PIRQ_PENALTY_ISA_ALWAYS) { + if (acpi_irq_get_penalty(irq) >= PIRQ_PENALTY_ISA_ALWAYS) { printk(KERN_ERR PREFIX "No IRQ available for %s [%s]. " "Try pci=noacpi or acpi=off\n", acpi_device_name(link->device), @@ -568,7 +622,6 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link) acpi_device_bid(link->device)); return -ENODEV; } else { - acpi_irq_penalty[link->irq.active] += PIRQ_PENALTY_PCI_USING; printk(KERN_WARNING PREFIX "%s [%s] enabled at IRQ %d\n", acpi_device_name(link->device), acpi_device_bid(link->device), link->irq.active); @@ -800,9 +853,10 @@ static int __init acpi_irq_penalty_update(char *str, int used) continue; if (used) - acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_USED; + acpi_irq_penalty[irq] = acpi_irq_get_penalty(irq) + + PIRQ_PENALTY_ISA_USED; else - acpi_irq_penalty[irq] = PIRQ_PENALTY_PCI_AVAILABLE; + acpi_irq_penalty[irq] = 0; if (retval != 2) /* no next number */ break; @@ -819,34 +873,19 @@ static int __init acpi_irq_penalty_update(char *str, int used) */ void acpi_penalize_isa_irq(int irq, int active) { - if (irq >= 0 && irq < ARRAY_SIZE(acpi_irq_penalty)) { - if (active) - acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_USED; - else - acpi_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING; - } + if (irq >= 0 && irq < ARRAY_SIZE(acpi_irq_penalty)) + acpi_irq_penalty[irq] = acpi_irq_get_penalty(irq) + + active ? PIRQ_PENALTY_ISA_USED : PIRQ_PENALTY_PCI_USING; } bool acpi_isa_irq_available(int irq) { return irq >= 0 && (irq >= ARRAY_SIZE(acpi_irq_penalty) || - acpi_irq_penalty[irq] < PIRQ_PENALTY_ISA_ALWAYS); + acpi_irq_get_penalty(irq) < PIRQ_PENALTY_ISA_ALWAYS); } -/* - * Penalize IRQ used by ACPI SCI. If ACPI SCI pin attributes conflict with - * PCI IRQ attributes, mark ACPI SCI as ISA_ALWAYS so it won't be use for - * PCI IRQs. - */ void acpi_penalize_sci_irq(int irq, int trigger, int polarity) { - if (irq >= 0 && irq < ARRAY_SIZE(acpi_irq_penalty)) { - if (trigger != ACPI_MADT_TRIGGER_LEVEL || - polarity != ACPI_MADT_POLARITY_ACTIVE_LOW) - acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_ALWAYS; - else - acpi_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING; - } } /* -- cgit v1.1 From 5c5087a5539083305f1199f09ac2e7f14d855ff3 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sun, 17 Apr 2016 13:36:54 -0400 Subject: ACPI,PCI,IRQ: reduce static IRQ array size to 16 Now that the supported number of PCI IRQs are no longer capped with 256, renaming the static array to support ISA IRQs only and removing the MAX_IRQS constant. Signed-off-by: Sinan Kaya Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_link.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index cc0ba16..12ea784 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -438,8 +438,7 @@ static int acpi_pci_link_set(struct acpi_pci_link *link, int irq) * enabled system. */ -#define ACPI_MAX_IRQS 256 -#define ACPI_MAX_ISA_IRQ 16 +#define ACPI_MAX_ISA_IRQS 16 #define PIRQ_PENALTY_PCI_POSSIBLE (16*16) #define PIRQ_PENALTY_PCI_USING (16*16*16) @@ -447,7 +446,7 @@ static int acpi_pci_link_set(struct acpi_pci_link *link, int irq) #define PIRQ_PENALTY_ISA_USED (16*16*16*16*16) #define PIRQ_PENALTY_ISA_ALWAYS (16*16*16*16*16*16) -static int acpi_irq_penalty[ACPI_MAX_IRQS] = { +static int acpi_isa_irq_penalty[ACPI_MAX_ISA_IRQS] = { PIRQ_PENALTY_ISA_ALWAYS, /* IRQ0 timer */ PIRQ_PENALTY_ISA_ALWAYS, /* IRQ1 keyboard */ PIRQ_PENALTY_ISA_ALWAYS, /* IRQ2 cascade */ @@ -500,8 +499,8 @@ static int acpi_irq_get_penalty(int irq) { int penalty = 0; - if (irq < ACPI_MAX_ISA_IRQ) - penalty += acpi_irq_penalty[irq]; + if (irq < ACPI_MAX_ISA_IRQS) + penalty += acpi_isa_irq_penalty[irq]; /* * Penalize IRQ used by ACPI SCI. If ACPI SCI pin attributes conflict @@ -541,14 +540,15 @@ int __init acpi_irq_penalty_init(void) link->irq.possible_count; for (i = 0; i < link->irq.possible_count; i++) { - if (link->irq.possible[i] < ACPI_MAX_ISA_IRQ) - acpi_irq_penalty[link->irq. + if (link->irq.possible[i] < ACPI_MAX_ISA_IRQS) + acpi_isa_irq_penalty[link->irq. possible[i]] += penalty; } - } else if (link->irq.active) { - acpi_irq_penalty[link->irq.active] += + } else if (link->irq.active && + (link->irq.active < ACPI_MAX_ISA_IRQS)) { + acpi_isa_irq_penalty[link->irq.active] += PIRQ_PENALTY_PCI_POSSIBLE; } } @@ -831,7 +831,7 @@ static void acpi_pci_link_remove(struct acpi_device *device) } /* - * modify acpi_irq_penalty[] from cmdline + * modify acpi_isa_irq_penalty[] from cmdline */ static int __init acpi_irq_penalty_update(char *str, int used) { @@ -840,24 +840,24 @@ static int __init acpi_irq_penalty_update(char *str, int used) for (i = 0; i < 16; i++) { int retval; int irq; + int new_penalty; retval = get_option(&str, &irq); if (!retval) break; /* no number found */ - if (irq < 0) - continue; - - if (irq >= ARRAY_SIZE(acpi_irq_penalty)) + /* see if this is a ISA IRQ */ + if ((irq < 0) || (irq >= ACPI_MAX_ISA_IRQS)) continue; if (used) - acpi_irq_penalty[irq] = acpi_irq_get_penalty(irq) + - PIRQ_PENALTY_ISA_USED; + new_penalty = acpi_irq_get_penalty(irq) + + PIRQ_PENALTY_ISA_USED; else - acpi_irq_penalty[irq] = 0; + new_penalty = 0; + acpi_isa_irq_penalty[irq] = new_penalty; if (retval != 2) /* no next number */ break; } @@ -873,14 +873,14 @@ static int __init acpi_irq_penalty_update(char *str, int used) */ void acpi_penalize_isa_irq(int irq, int active) { - if (irq >= 0 && irq < ARRAY_SIZE(acpi_irq_penalty)) - acpi_irq_penalty[irq] = acpi_irq_get_penalty(irq) + + if ((irq >= 0) && (irq < ARRAY_SIZE(acpi_isa_irq_penalty))) + acpi_isa_irq_penalty[irq] = acpi_irq_get_penalty(irq) + active ? PIRQ_PENALTY_ISA_USED : PIRQ_PENALTY_PCI_USING; } bool acpi_isa_irq_available(int irq) { - return irq >= 0 && (irq >= ARRAY_SIZE(acpi_irq_penalty) || + return irq >= 0 && (irq >= ARRAY_SIZE(acpi_isa_irq_penalty) || acpi_irq_get_penalty(irq) < PIRQ_PENALTY_ISA_ALWAYS); } -- cgit v1.1 From 1fcb6a813c4f67a0ba189b0d057b70ba084f6e1a Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sun, 17 Apr 2016 13:36:55 -0400 Subject: ACPI,PCI,IRQ: remove redundant code in acpi_irq_penalty_init() acpi_irq_get_penalty is now calculating the penalty on the fly now. No need to maintain global list of penalties or calculate them at the init time. Removing duplicate code in acpi_irq_penalty_init. Signed-off-by: Sinan Kaya Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_link.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 12ea784..ab39208 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -520,42 +520,6 @@ static int acpi_irq_get_penalty(int irq) return penalty; } -int __init acpi_irq_penalty_init(void) -{ - struct acpi_pci_link *link; - int i; - - /* - * Update penalties to facilitate IRQ balancing. - */ - list_for_each_entry(link, &acpi_link_list, list) { - - /* - * reflect the possible and active irqs in the penalty table -- - * useful for breaking ties. - */ - if (link->irq.possible_count) { - int penalty = - PIRQ_PENALTY_PCI_POSSIBLE / - link->irq.possible_count; - - for (i = 0; i < link->irq.possible_count; i++) { - if (link->irq.possible[i] < ACPI_MAX_ISA_IRQS) - acpi_isa_irq_penalty[link->irq. - possible[i]] += - penalty; - } - - } else if (link->irq.active && - (link->irq.active < ACPI_MAX_ISA_IRQS)) { - acpi_isa_irq_penalty[link->irq.active] += - PIRQ_PENALTY_PCI_POSSIBLE; - } - } - - return 0; -} - static int acpi_irq_balance = -1; /* 0: static, 1: balance */ static int acpi_pci_link_allocate(struct acpi_pci_link *link) -- cgit v1.1 From 9e5ed6d1fb87dc3ff0c2a94ed75fe82027a9f597 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Sun, 17 Apr 2016 13:36:56 -0400 Subject: ACPI,PCI,IRQ: remove SCI penalize function Removing the SCI penalize function as the penalty is now calculated on the fly. Signed-off-by: Sinan Kaya Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_link.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index ab39208..8fc7323 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -848,10 +848,6 @@ bool acpi_isa_irq_available(int irq) acpi_irq_get_penalty(irq) < PIRQ_PENALTY_ISA_ALWAYS); } -void acpi_penalize_sci_irq(int irq, int trigger, int polarity) -{ -} - /* * Over-ride default table to reserve additional IRQs for use by ISA * e.g. acpi_irq_isa=5 -- cgit v1.1 From f18ebc211e259d4f591e39e74b2aa2de226c9a1d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 5 May 2016 16:23:04 +0300 Subject: ACPI / sysfs: fix error code in get_status() The problem with ornamental, do-nothing gotos is that they lead to "forgot to set the error code" bugs. We should be returning -EINVAL here but we don't. It leads to an uninitalized variable in counter_show(): drivers/acpi/sysfs.c:603 counter_show() error: uninitialized symbol 'status'. Fixes: 1c8fce27e275 (ACPI: introduce drivers/acpi/sysfs.c) Signed-off-by: Dan Carpenter Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sysfs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 0243d37..4b3a9e2 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -555,23 +555,22 @@ static void acpi_global_event_handler(u32 event_type, acpi_handle device, static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle) { - int result = 0; + int result; if (index >= num_gpes + ACPI_NUM_FIXED_EVENTS) - goto end; + return -EINVAL; if (index < num_gpes) { result = acpi_get_gpe_device(index, handle); if (result) { ACPI_EXCEPTION((AE_INFO, AE_NOT_FOUND, "Invalid GPE 0x%x", index)); - goto end; + return result; } result = acpi_get_gpe_status(*handle, index, status); } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS)) result = acpi_get_event_status(index - num_gpes, status); -end: return result; } -- cgit v1.1 From 87554098fec74a6c4a8cbea0d9adea2e8868e9e4 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 28 Apr 2016 18:01:20 -0700 Subject: nfit: disable vendor specific commands Module option to limit userspace to the publicly defined command set. For cases where private DIMM commands may be interfering with the kernel's handling of DIMM state this option can be set to block vendor specific commands. Cc: Jerry Hoemann Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index b85a468..ad4fc86 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -45,6 +45,11 @@ module_param(scrub_overflow_abort, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(scrub_overflow_abort, "Number of times we overflow ARS results before abort"); +static bool disable_vendor_specific; +module_param(disable_vendor_specific, bool, S_IRUGO); +MODULE_PARM_DESC(disable_vendor_specific, + "Limit commands to the publicly specified set\n"); + static struct workqueue_struct *nfit_wq; struct nfit_table_prev { @@ -989,13 +994,17 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, /* limit the supported commands to those that are publicly documented */ nfit_mem->family = i; - if (nfit_mem->family == NVDIMM_FAMILY_INTEL) + if (nfit_mem->family == NVDIMM_FAMILY_INTEL) { dsm_mask = 0x3fe; - else if (nfit_mem->family == NVDIMM_FAMILY_HPE1) + if (disable_vendor_specific) + dsm_mask &= ~(1 << ND_CMD_VENDOR); + } else if (nfit_mem->family == NVDIMM_FAMILY_HPE1) dsm_mask = 0x1c3c76; - else if (nfit_mem->family == NVDIMM_FAMILY_HPE2) + else if (nfit_mem->family == NVDIMM_FAMILY_HPE2) { dsm_mask = 0x1fe; - else { + if (disable_vendor_specific) + dsm_mask &= ~(1 << 8); + } else { dev_err(dev, "unknown dimm command family\n"); nfit_mem->family = -1; return force_enable_dimms ? 0 : -ENODEV; -- cgit v1.1 From a94e3fbe4d53d4e512c4ea88a475e605b8d8dccb Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 28 Apr 2016 18:18:05 -0700 Subject: nfit: add sysfs dimm 'family' and 'dsm_mask' attributes Communicate the command format and supported functions to userspace tooling. Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index ad4fc86..bf2d7a2 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -897,6 +897,30 @@ static ssize_t serial_show(struct device *dev, } static DEVICE_ATTR_RO(serial); +static ssize_t family_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + + if (nfit_mem->family < 0) + return -ENXIO; + return sprintf(buf, "%d\n", nfit_mem->family); +} +static DEVICE_ATTR_RO(family); + +static ssize_t dsm_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm); + + if (nfit_mem->family < 0) + return -ENXIO; + return sprintf(buf, "%#lx\n", nfit_mem->dsm_mask); +} +static DEVICE_ATTR_RO(dsm_mask); + static ssize_t flags_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -920,6 +944,8 @@ static struct attribute *acpi_nfit_dimm_attributes[] = { &dev_attr_serial.attr, &dev_attr_rev_id.attr, &dev_attr_flags.attr, + &dev_attr_family.attr, + &dev_attr_dsm_mask.attr, NULL, }; -- cgit v1.1 From 74216699ddcca13541e8494cb2b995e6a44a04d9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 6 May 2016 01:27:09 +0200 Subject: ACPI / tables: Fix DSDT override mechanism Commit 5ae74f2cc2f1 (ACPI / tables: Move table override mechanisms to tables.c) forgot to move the CONFIG_ACPI_CUSTOM_DSDT_FILE inclusion directive from osl.c to tables.c. Fix that. Fixes: 5ae74f2cc2f1 (ACPI / tables: Move table override mechanisms to tables.c) Signed-off-by: Rafael J. Wysocki Acked-by: Lv Zheng --- drivers/acpi/osl.c | 4 ---- drivers/acpi/tables.c | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 0796ad9..0155f8b 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -56,10 +56,6 @@ struct acpi_os_dpc { struct work_struct work; }; -#ifdef CONFIG_ACPI_CUSTOM_DSDT -#include CONFIG_ACPI_CUSTOM_DSDT_FILE -#endif - #ifdef ENABLE_DEBUGGER #include diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 08795fb..a372f9e 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -36,6 +36,10 @@ #include #include "internal.h" +#ifdef CONFIG_ACPI_CUSTOM_DSDT +#include CONFIG_ACPI_CUSTOM_DSDT_FILE +#endif + #define ACPI_MAX_TABLES 128 static char *mps_inti_flags_polarity[] = { "dfl", "high", "res", "low" }; -- cgit v1.1 From 437014bdac9680e3b32c006635ac270b808ce476 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 9 May 2016 14:40:27 -0400 Subject: ACPI / GED: make evged.c explicitly non-modular The Makefile/Kconfig currently controlling compilation of this code is: Makefile:acpi-$(CONFIG_ACPI_REDUCED_HARDWARE_ONLY) += evged.o drivers/acpi/Kconfig:config ACPI_REDUCED_HARDWARE_ONLY drivers/acpi/Kconfig: bool "Hardware-reduced ACPI support only" if EXPERT ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modularity so that when reading the code there is no doubt it is builtin-only. Since module_platform_driver() uses the same init level priority as builtin_platform_driver() the init ordering remains unchanged with this commit. The file wasn't explicitly including the module.h file but it did already have init.h so, unlike similar changes, this one has no header changes at all. We also delete the MODULE_LICENSE tag since all that information is already contained at the top of the file in the comments. Signed-off-by: Paul Gortmaker Signed-off-by: Rafael J. Wysocki --- drivers/acpi/evged.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/evged.c b/drivers/acpi/evged.c index 9c0a868..46f0603 100644 --- a/drivers/acpi/evged.c +++ b/drivers/acpi/evged.c @@ -151,6 +151,4 @@ static struct platform_driver ged_driver = { .acpi_match_table = ACPI_PTR(ged_acpi_ids), }, }; - -module_platform_driver(ged_driver); -MODULE_LICENSE("GPL v2"); +builtin_platform_driver(ged_driver); -- cgit v1.1 From 78a898d0e39513469858de990de83210fee28ee9 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 19 May 2016 15:25:41 +0200 Subject: ACPI / PM: Export acpi_device_fix_up_power() Drivers that needs acpi_device_fix_up_power(), allow them to be built as modules by exporting this function. Cc: # 4.5+ Tested-by: Laszlo Fiat Signed-off-by: Ulf Hansson Acked-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index cd2c3d6..993fd31 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -319,6 +319,7 @@ int acpi_device_fix_up_power(struct acpi_device *device) return ret; } +EXPORT_SYMBOL_GPL(acpi_device_fix_up_power); int acpi_device_update_power(struct acpi_device *device, int *state_p) { -- cgit v1.1 From 5dfa0c73953360e07a47731e412d33dfc896bf4e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 19 May 2016 09:11:52 +0100 Subject: ACPI / battery: Correctly serialise with the pending async probe async_synchronize_cookie() only serialises all tasks up to the specified cookie, and importantly does not wait for the task corresponding with the cookie. [This is so that it can be trivially used from inside the async_func_t in order to serialise with all preceding tasks.] In order to serialise with acpi_battery_init_async() we need to compensate and pass in the next cookie instead. The impact today is zero since performing an async_schedule() from inside a module init function will trigger an async_synchronize_full() prior to the module loader's completion. However, if the probe was moved to its own unregistered async_domain, then the async_synchronize_cookie would be replaced with an async_synchronize_full_domain. Signed-off-by: Chris Wilson Signed-off-by: Rafael J. Wysocki --- drivers/acpi/battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index b719ab3..ab23479 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1316,7 +1316,7 @@ static int __init acpi_battery_init(void) static void __exit acpi_battery_exit(void) { - async_synchronize_cookie(async_cookie); + async_synchronize_cookie(async_cookie + 1); acpi_bus_unregister_driver(&acpi_battery_driver); #ifdef CONFIG_ACPI_PROCFS_POWER acpi_unlock_battery_dir(acpi_battery_dir); -- cgit v1.1 From 287980e49ffc0f6d911601e7e352a812ed27768e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 27 May 2016 23:23:25 +0200 Subject: remove lots of IS_ERR_VALUE abuses Most users of IS_ERR_VALUE() in the kernel are wrong, as they pass an 'int' into a function that takes an 'unsigned long' argument. This happens to work because the type is sign-extended on 64-bit architectures before it gets converted into an unsigned type. However, anything that passes an 'unsigned short' or 'unsigned int' argument into IS_ERR_VALUE() is guaranteed to be broken, as are 8-bit integers and types that are wider than 'unsigned long'. Andrzej Hajda has already fixed a lot of the worst abusers that were causing actual bugs, but it would be nice to prevent any users that are not passing 'unsigned long' arguments. This patch changes all users of IS_ERR_VALUE() that I could find on 32-bit ARM randconfig builds and x86 allmodconfig. For the moment, this doesn't change the definition of IS_ERR_VALUE() because there are probably still architecture specific users elsewhere. Almost all the warnings I got are for files that are better off using 'if (err)' or 'if (err < 0)'. The only legitimate user I could find that we get a warning for is the (32-bit only) freescale fman driver, so I did not remove the IS_ERR_VALUE() there but changed the type to 'unsigned long'. For 9pfs, I just worked around one user whose calling conventions are so obscure that I did not dare change the behavior. I was using this definition for testing: #define IS_ERR_VALUE(x) ((unsigned long*)NULL == (typeof (x)*)NULL && \ unlikely((unsigned long long)(x) >= (unsigned long long)(typeof(x))-MAX_ERRNO)) which ends up making all 16-bit or wider types work correctly with the most plausible interpretation of what IS_ERR_VALUE() was supposed to return according to its users, but also causes a compile-time warning for any users that do not pass an 'unsigned long' argument. I suggested this approach earlier this year, but back then we ended up deciding to just fix the users that are obviously broken. After the initial warning that caused me to get involved in the discussion (fs/gfs2/dir.c) showed up again in the mainline kernel, Linus asked me to send the whole thing again. [ Updated the 9p parts as per Al Viro - Linus ] Signed-off-by: Arnd Bergmann Cc: Andrzej Hajda Cc: Andrew Morton Link: https://lkml.org/lkml/2016/1/7/363 Link: https://lkml.org/lkml/2016/5/27/486 Acked-by: Srinivas Kandagatla # For nvmem part Signed-off-by: Linus Torvalds --- drivers/acpi/acpi_dbg.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c index 15e4604..1f41284 100644 --- a/drivers/acpi/acpi_dbg.c +++ b/drivers/acpi/acpi_dbg.c @@ -265,7 +265,7 @@ static int acpi_aml_write_kern(const char *buf, int len) char *p; ret = acpi_aml_lock_write(crc, ACPI_AML_OUT_KERN); - if (IS_ERR_VALUE(ret)) + if (ret < 0) return ret; /* sync tail before inserting logs */ smp_mb(); @@ -286,7 +286,7 @@ static int acpi_aml_readb_kern(void) char *p; ret = acpi_aml_lock_read(crc, ACPI_AML_IN_KERN); - if (IS_ERR_VALUE(ret)) + if (ret < 0) return ret; /* sync head before removing cmds */ smp_rmb(); @@ -330,7 +330,7 @@ again: goto again; break; } - if (IS_ERR_VALUE(ret)) + if (ret < 0) break; size += ret; count -= ret; @@ -373,7 +373,7 @@ again: if (ret == 0) goto again; } - if (IS_ERR_VALUE(ret)) + if (ret < 0) break; *(msg + size) = (char)ret; size++; @@ -526,7 +526,7 @@ static int acpi_aml_open(struct inode *inode, struct file *file) } acpi_aml_io.users++; err_lock: - if (IS_ERR_VALUE(ret)) { + if (ret < 0) { if (acpi_aml_active_reader == file) acpi_aml_active_reader = NULL; } @@ -587,7 +587,7 @@ static int acpi_aml_read_user(char __user *buf, int len) char *p; ret = acpi_aml_lock_read(crc, ACPI_AML_OUT_USER); - if (IS_ERR_VALUE(ret)) + if (ret < 0) return ret; /* sync head before removing logs */ smp_rmb(); @@ -602,7 +602,7 @@ static int acpi_aml_read_user(char __user *buf, int len) crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1); ret = n; out: - acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, !IS_ERR_VALUE(ret)); + acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, !ret); return ret; } @@ -634,7 +634,7 @@ again: goto again; } } - if (IS_ERR_VALUE(ret)) { + if (ret < 0) { if (!acpi_aml_running()) ret = 0; break; @@ -657,7 +657,7 @@ static int acpi_aml_write_user(const char __user *buf, int len) char *p; ret = acpi_aml_lock_write(crc, ACPI_AML_IN_USER); - if (IS_ERR_VALUE(ret)) + if (ret < 0) return ret; /* sync tail before inserting cmds */ smp_mb(); @@ -672,7 +672,7 @@ static int acpi_aml_write_user(const char __user *buf, int len) crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1); ret = n; out: - acpi_aml_unlock_fifo(ACPI_AML_IN_USER, !IS_ERR_VALUE(ret)); + acpi_aml_unlock_fifo(ACPI_AML_IN_USER, !ret); return n; } @@ -704,7 +704,7 @@ again: goto again; } } - if (IS_ERR_VALUE(ret)) { + if (ret < 0) { if (!acpi_aml_running()) ret = 0; break; -- cgit v1.1 From 9f9cd7ee2c05881f151e5ee4889e667a031dd8d9 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Sat, 21 May 2016 15:30:46 +0800 Subject: ACPI / Thermal / video: fix max_level incorrect value commit 059500940def (ACPI/video: export acpi_video_get_levels) mistakenly dropped the correct value of max_level and that caused the set_level function following failed and the acpi_video backlight interface didn't get created. Fix this by passing back the correct max_level value. While at it, also fix the param used in acpi_video_device_lcd_query_levels where acpi_handle is expected but acpi_video_device is passed. Fixes: 059500940def (ACPI/video: export acpi_video_get_levels) Reported-and-tested-by: Valdis Kletnieks Signed-off-by: Aaron Lu Acked-by: Zhang Rui Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_video.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 3d5b8a0..c1d138e 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -754,7 +754,8 @@ static int acpi_video_bqc_quirk(struct acpi_video_device *device, } int acpi_video_get_levels(struct acpi_device *device, - struct acpi_video_device_brightness **dev_br) + struct acpi_video_device_brightness **dev_br, + int *pmax_level) { union acpi_object *obj = NULL; int i, max_level = 0, count = 0, level_ac_battery = 0; @@ -841,6 +842,8 @@ int acpi_video_get_levels(struct acpi_device *device, br->count = count; *dev_br = br; + if (pmax_level) + *pmax_level = max_level; out: kfree(obj); @@ -869,7 +872,7 @@ acpi_video_init_brightness(struct acpi_video_device *device) struct acpi_video_device_brightness *br = NULL; int result = -EINVAL; - result = acpi_video_get_levels(device->dev, &br); + result = acpi_video_get_levels(device->dev, &br, &max_level); if (result) return result; device->brightness = br; @@ -1737,7 +1740,7 @@ static void acpi_video_run_bcl_for_osi(struct acpi_video_bus *video) mutex_lock(&video->device_list_lock); list_for_each_entry(dev, &video->video_device_list, entry) { - if (!acpi_video_device_lcd_query_levels(dev, &levels)) + if (!acpi_video_device_lcd_query_levels(dev->dev->handle, &levels)) kfree(levels); } mutex_unlock(&video->device_list_lock); -- cgit v1.1 From 86314751c7945fa0c67f459beeda2e7c610ca429 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 2 Jun 2016 01:57:50 +0200 Subject: ACPI / processor: Avoid reserving IO regions too early Roland Dreier reports that one of his systems cannot boot because of the changes made by commit ac212b6980d8 (ACPI / processor: Use common hotplug infrastructure). The problematic part of it is the request_region() call in acpi_processor_get_info() that used to run at module init time before the above commit and now it runs much earlier. Unfortunately, the region(s) reserved by it fall into a range the PCI subsystem attempts to reserve for AHCI IO BARs. As a result, the PCI reservation fails and AHCI doesn't work, while previously the PCI reservation would be made before acpi_processor_get_info() and it would succeed. That request_region() call, however, was overlooked by commit ac212b6980d8, as it is not necessary for the enumeration of the processors. It only is needed when the ACPI processor driver actually attempts to handle them which doesn't happen before loading the ACPI processor driver module. Therefore that call should have been moved from acpi_processor_get_info() into that module. Address the problem by moving the request_region() call in question out of acpi_processor_get_info() and use the observation that the region reserved by it is only needed if the FADT-based CPU throttling method is going to be used, which means that it should be sufficient to invoke it from acpi_processor_get_throttling_fadt(). Fixes: ac212b6980d8 (ACPI / processor: Use common hotplug infrastructure) Reported-by: Roland Dreier Tested-by: Roland Dreier Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_processor.c | 9 --------- drivers/acpi/processor_throttling.c | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 0d92d0f..c7ba948 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -331,15 +331,6 @@ static int acpi_processor_get_info(struct acpi_device *device) pr->throttling.duty_width = acpi_gbl_FADT.duty_width; pr->pblk = object.processor.pblk_address; - - /* - * We don't care about error returns - we just try to mark - * these reserved so that nobody else is confused into thinking - * that this region might be unused.. - * - * (In particular, allocating the IO range for Cardbus) - */ - request_region(pr->throttling.address, 6, "ACPI CPU throttle"); } /* diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index f170d74..c72e648 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -676,6 +676,15 @@ static int acpi_processor_get_throttling_fadt(struct acpi_processor *pr) if (!pr->flags.throttling) return -ENODEV; + /* + * We don't care about error returns - we just try to mark + * these reserved so that nobody else is confused into thinking + * that this region might be unused.. + * + * (In particular, allocating the IO range for Cardbus) + */ + request_region(pr->throttling.address, 6, "ACPI CPU throttle"); + pr->throttling.state = 0; duty_mask = pr->throttling.state_count - 1; -- cgit v1.1 From dcf15cbded656a12335bc4151f3f75f10080a375 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 3 Jun 2016 10:26:12 +0800 Subject: ACPI / EC: Fix a boot EC regresion by restoring boot EC support for the DSDT EC According to the Windows probing result, during the table loading, the EC device described in the ECDT should be used. And the ECDT EC is also effective during the period the namespace objects are initialized (we can see a separate process executing _STA/_INI on Windows before executing other device specific control methods, for example, EC._REG). During the device enumration, the EC device described in the DSDT should be used. But there are differences between Linux and Windows around the device probing order. Thus in Linux, we should enable the DSDT EC as early as possible before enumerating devices in order not to trigger issues related to the device enumeration order differences. This patch thus converts acpi_boot_ec_enable() into acpi_ec_dsdt_probe() to fix the gap. This also fixes a user reported regression triggered after we switched the "table loading"/"ECDT support" to be ACPI spec 2.0 compliant. Fixes: 59f0aa9480cf (ACPI 2.0 / ECDT: Remove early namespace reference from EC) Link: https://bugzilla.kernel.org/show_bug.cgi?id=119261 Reported-and-tested-by: Gabriele Mazzotta Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 2 +- drivers/acpi/ec.c | 29 ++++++++++++++++++++++------- drivers/acpi/internal.h | 2 +- 3 files changed, 24 insertions(+), 9 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 31e8da6..262ca31 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1051,7 +1051,7 @@ static int __init acpi_bus_init(void) * Maybe EC region is required at bus_scan/acpi_get_devices. So it * is necessary to enable it as early as possible. */ - acpi_boot_ec_enable(); + acpi_ec_dsdt_probe(); printk(KERN_INFO PREFIX "Interpreter enabled\n"); diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 0e70181..73c76d6 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1446,10 +1446,30 @@ ec_parse_io_ports(struct acpi_resource *resource, void *context) return AE_OK; } -int __init acpi_boot_ec_enable(void) +static const struct acpi_device_id ec_device_ids[] = { + {"PNP0C09", 0}, + {"", 0}, +}; + +int __init acpi_ec_dsdt_probe(void) { - if (!boot_ec) + acpi_status status; + + if (boot_ec) return 0; + + /* + * Finding EC from DSDT if there is no ECDT EC available. When this + * function is invoked, ACPI tables have been fully loaded, we can + * walk namespace now. + */ + boot_ec = make_acpi_ec(); + if (!boot_ec) + return -ENOMEM; + status = acpi_get_devices(ec_device_ids[0].id, + ec_parse_device, boot_ec, NULL); + if (ACPI_FAILURE(status) || !boot_ec->handle) + return -ENODEV; if (!ec_install_handlers(boot_ec)) { first_ec = boot_ec; return 0; @@ -1457,11 +1477,6 @@ int __init acpi_boot_ec_enable(void) return -EFAULT; } -static const struct acpi_device_id ec_device_ids[] = { - {"PNP0C09", 0}, - {"", 0}, -}; - #if 0 /* * Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 7c18847..b4733b5 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -180,7 +180,7 @@ typedef int (*acpi_ec_query_func) (void *data); int acpi_ec_init(void); int acpi_ec_ecdt_probe(void); -int acpi_boot_ec_enable(void); +int acpi_ec_dsdt_probe(void); void acpi_ec_block_transactions(void); void acpi_ec_unblock_transactions(void); void acpi_ec_unblock_transactions_early(void); -- cgit v1.1 From 4995734e973a2c2e9c6f6413cbad9913fc4df0dc Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 24 Jun 2016 09:07:39 -0700 Subject: acpi, nfit: fix acpi_check_dsm() vs zero functions implemented QEMU 2.6 implements nascent support for nvdimm DSMs. Depending on configuration it may only implement the function0 dsm to indicate that no other DSMs are available. Commit 31eca76ba2fc "nfit, libnvdimm: limited/whitelisted dimm command marshaling mechanism" breaks QEMU, but QEMU is spec compliant. Per the spec the way to indicate that no functions are supported is: If Function Index is zero, the return is a buffer containing one bit for each function index, starting with zero. Bit 0 indicates whether there is support for any functions other than function 0 for the specified UUID and Revision ID. If set to zero, no functions are supported (other than function zero) for the specified UUID and Revision ID. Update the nfit driver to determine the family (interface UUID) without requiring the implementation to define any other functions, i.e. short-circuit acpi_check_dsm() to succeed per the spec. The nfit driver appears to be the only user passing funcs==0 to acpi_check_dsm(), so this behavior change of the common routine should be limited to the probing done by the nfit driver. Cc: Len Brown Cc: Jerry Hoemann Acked-by: "Rafael J. Wysocki" Fixes: 31eca76ba2fc ("nfit, libnvdimm: limited/whitelisted dimm command marshaling mechanism") Reported-by: Xiao Guangrong Tested-by: Xiao Guangrong Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 6 +++--- drivers/acpi/utils.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 2215fc8..32579a7 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -1131,11 +1131,11 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, /* * Until standardization materializes we need to consider up to 3 - * different command sets. Note, that checking for function0 (bit0) - * tells us if any commands are reachable through this uuid. + * different command sets. Note, that checking for zero functions + * tells us if any commands might be reachable through this uuid. */ for (i = NVDIMM_FAMILY_INTEL; i <= NVDIMM_FAMILY_HPE2; i++) - if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1)) + if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 0)) break; /* limit the supported commands to those that are publicly documented */ diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 22c0995..b4de130 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -680,9 +680,6 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 funcs) u64 mask = 0; union acpi_object *obj; - if (funcs == 0) - return false; - obj = acpi_evaluate_dsm(handle, uuid, rev, 0, NULL); if (!obj) return false; @@ -695,6 +692,9 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, u64 rev, u64 funcs) mask |= (((u64)obj->buffer.pointer[i]) << (i * 8)); ACPI_FREE(obj); + if (funcs == 0) + return true; + /* * Bit 0 indicates whether there's support for any functions other than * function 0 for the specified UUID and revision. -- cgit v1.1 From 1bcbf42d2732b3fdaa8559b0dfc91567769e23c8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 29 Jun 2016 11:19:32 -0700 Subject: nfit: fix format interface code byte order Per JEDEC Annex L Release 3 the SPD data is: Bits 9~5 00 000 = Function Undefined 00 001 = Byte addressable energy backed 00 010 = Block addressed 00 011 = Byte addressable, no energy backed All other codes reserved Bits 4~0 0 0000 = Proprietary interface 0 0001 = Standard interface 1 All other codes reserved; see Definitions of Functions ...and per the ACPI 6.1 spec: byte0: Bits 4~0 (0 or 1) byte1: Bits 9~5 (1, 2, or 3) ...so a format interface code displayed as 0x301 should be stored in the nfit as (0x1, 0x3), little-endian. Cc: Toshi Kani Cc: Rafael J. Wysocki Cc: Robert Moore Cc: Robert Elliott Link: https://bugzilla.kernel.org/show_bug.cgi?id=121161 Fixes: 30ec5fd464d5 ("nfit: fix format interface code byte order per ACPI6.1") Fixes: 5ad9a7fde07a ("acpi/nfit: Update nfit driver to comply with ACPI 6.1") Reported-by: Kristin Jacque Signed-off-by: Dan Williams --- drivers/acpi/nfit.c | 6 +++--- drivers/acpi/nfit.h | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 32579a7..ac6ddcc0 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -928,7 +928,7 @@ static ssize_t format_show(struct device *dev, { struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev); - return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->code)); + return sprintf(buf, "0x%04x\n", le16_to_cpu(dcr->code)); } static DEVICE_ATTR_RO(format); @@ -961,8 +961,8 @@ static ssize_t format1_show(struct device *dev, continue; if (nfit_dcr->dcr->code == dcr->code) continue; - rc = sprintf(buf, "%#x\n", - be16_to_cpu(nfit_dcr->dcr->code)); + rc = sprintf(buf, "0x%04x\n", + le16_to_cpu(nfit_dcr->dcr->code)); break; } if (rc != ENXIO) diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index 11cb383..02b9ea1 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h @@ -53,12 +53,12 @@ enum nfit_uuids { }; /* - * Region format interface codes are stored as an array of bytes in the - * NFIT DIMM Control Region structure + * Region format interface codes are stored with the interface as the + * LSB and the function as the MSB. */ -#define NFIT_FIC_BYTE cpu_to_be16(0x101) /* byte-addressable energy backed */ -#define NFIT_FIC_BLK cpu_to_be16(0x201) /* block-addressable non-energy backed */ -#define NFIT_FIC_BYTEN cpu_to_be16(0x301) /* byte-addressable non-energy backed */ +#define NFIT_FIC_BYTE cpu_to_le16(0x101) /* byte-addressable energy backed */ +#define NFIT_FIC_BLK cpu_to_le16(0x201) /* block-addressable non-energy backed */ +#define NFIT_FIC_BYTEN cpu_to_le16(0x301) /* byte-addressable non-energy backed */ enum { NFIT_BLK_READ_FLUSH = 1, -- cgit v1.1 From 54794580f5949253520265e46c903878ab222d84 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Wed, 29 Jun 2016 04:27:38 -0400 Subject: ACPI,PCI,IRQ: correct operator precedence The omitted parenthesis prevents the addition operation when acpi_penalize_isa_irq function is called. Fixes: 103544d86976 (ACPI,PCI,IRQ: reduce resource requirements) Signed-off-by: Sinan Kaya Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_link.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 8fc7323..4ed4061 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -839,7 +839,7 @@ void acpi_penalize_isa_irq(int irq, int active) { if ((irq >= 0) && (irq < ARRAY_SIZE(acpi_isa_irq_penalty))) acpi_isa_irq_penalty[irq] = acpi_irq_get_penalty(irq) + - active ? PIRQ_PENALTY_ISA_USED : PIRQ_PENALTY_PCI_USING; + (active ? PIRQ_PENALTY_ISA_USED : PIRQ_PENALTY_PCI_USING); } bool acpi_isa_irq_available(int irq) -- cgit v1.1 From 4a6e68bf96c1fa293717d2f00a68a68c92fa4150 Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Wed, 29 Jun 2016 04:27:35 -0400 Subject: ACPI,PCI,IRQ: factor in PCI possible The change introduced in commit 103544d86976 (ACPI,PCI,IRQ: reduce resource requirements) omitted the initially applied PCI_POSSIBLE penalty when the IRQ is active. Incorrect calculation of the penalty leads the ACPI code to assigning a wrong interrupt number to a PCI INTx interrupt. This would not be as bad as it sounds in theory. It would just cause the interrupts to be shared and result in performance penalty. However, some drivers (like the parallel port driver) don't like interrupt sharing and in the above case they will causes all of the PCI drivers wanting to share the interrupt to be unable to request it. The issue has not been caught in testing because the behavior is platform-specific and depends on the peripherals ending up sharing the IRQ and their drivers. Before the above commit the code would add the PCI_POSSIBLE value divided by the number of possible IRQ users to the IRQ penalty during initialization. Later in that code path, if the IRQ is chosen as the active IRQ or if it is used by ISA; additional penalties are added. Fixes: 103544d86976 (ACPI,PCI,IRQ: reduce resource requirements) Signed-off-by: Sinan Kaya Tested-by: Wim Osterholt [ rjw: Changelog ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_link.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 4ed4061..db7be62 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -470,6 +470,7 @@ static int acpi_irq_pci_sharing_penalty(int irq) { struct acpi_pci_link *link; int penalty = 0; + int i; list_for_each_entry(link, &acpi_link_list, list) { /* @@ -478,18 +479,14 @@ static int acpi_irq_pci_sharing_penalty(int irq) */ if (link->irq.active && link->irq.active == irq) penalty += PIRQ_PENALTY_PCI_USING; - else { - int i; - - /* - * If a link is inactive, penalize the IRQs it - * might use, but not as severely. - */ - for (i = 0; i < link->irq.possible_count; i++) - if (link->irq.possible[i] == irq) - penalty += PIRQ_PENALTY_PCI_POSSIBLE / - link->irq.possible_count; - } + + /* + * penalize the IRQs PCI might use, but not as severely. + */ + for (i = 0; i < link->irq.possible_count; i++) + if (link->irq.possible[i] == irq) + penalty += PIRQ_PENALTY_PCI_POSSIBLE / + link->irq.possible_count; } return penalty; -- cgit v1.1 From 487cf917ed0d12afaf403d9d77684bf44b8c13be Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Wed, 29 Jun 2016 04:27:36 -0400 Subject: Revert "ACPI, PCI, IRQ: remove redundant code in acpi_irq_penalty_init()" Trying to make the ISA and PCI init functionality common turned out to be a bad idea, because the ISA path depends on external functionality. Restore the previous behavior and limit the refactoring to PCI interrupts only. Fixes: 1fcb6a813c4f "ACPI,PCI,IRQ: remove redundant code in acpi_irq_penalty_init()" Signed-off-by: Sinan Kaya Tested-by: Wim Osterholt Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_link.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index db7be62..606083b 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -517,6 +517,42 @@ static int acpi_irq_get_penalty(int irq) return penalty; } +int __init acpi_irq_penalty_init(void) +{ + struct acpi_pci_link *link; + int i; + + /* + * Update penalties to facilitate IRQ balancing. + */ + list_for_each_entry(link, &acpi_link_list, list) { + + /* + * reflect the possible and active irqs in the penalty table -- + * useful for breaking ties. + */ + if (link->irq.possible_count) { + int penalty = + PIRQ_PENALTY_PCI_POSSIBLE / + link->irq.possible_count; + + for (i = 0; i < link->irq.possible_count; i++) { + if (link->irq.possible[i] < ACPI_MAX_ISA_IRQS) + acpi_isa_irq_penalty[link->irq. + possible[i]] += + penalty; + } + + } else if (link->irq.active && + (link->irq.active < ACPI_MAX_ISA_IRQS)) { + acpi_isa_irq_penalty[link->irq.active] += + PIRQ_PENALTY_PCI_POSSIBLE; + } + } + + return 0; +} + static int acpi_irq_balance = -1; /* 0: static, 1: balance */ static int acpi_pci_link_allocate(struct acpi_pci_link *link) -- cgit v1.1 From f7eca374f000bd8bd6aacc2619475fdba0b7ecca Mon Sep 17 00:00:00 2001 From: Sinan Kaya Date: Wed, 29 Jun 2016 04:27:37 -0400 Subject: ACPI,PCI,IRQ: separate ISA penalty calculation Since commit 103544d86976 (ACPI,PCI,IRQ: reduce resource requirements) the penalty values are calculated on the fly rather than at boot time. This works fine for PCI interrupts but not so well for ISA interrupts. The information on whether or not an ISA interrupt is in use is not available to the pci_link.c code directly. That information is obtained from the outside via acpi_penalize_isa_irq(). [If its "active" argument is true, then the IRQ is in use by ISA.] Since the current code relies on PCI Link objects for determination of penalties, we are factoring in the PCI penalty twice after acpi_penalize_isa_irq() function is called. To avoid that, limit the newly added functionality to just PCI interrupts so that old behavior is still maintained. Fixes: 103544d86976 (ACPI,PCI,IRQ: reduce resource requirements) Signed-off-by: Sinan Kaya Tested-by: Wim Osterholt Signed-off-by: Rafael J. Wysocki --- drivers/acpi/pci_link.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 606083b..c983bf7 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -496,9 +496,6 @@ static int acpi_irq_get_penalty(int irq) { int penalty = 0; - if (irq < ACPI_MAX_ISA_IRQS) - penalty += acpi_isa_irq_penalty[irq]; - /* * Penalize IRQ used by ACPI SCI. If ACPI SCI pin attributes conflict * with PCI IRQ attributes, mark ACPI SCI as ISA_ALWAYS so it won't be @@ -513,6 +510,9 @@ static int acpi_irq_get_penalty(int irq) penalty += PIRQ_PENALTY_PCI_USING; } + if (irq < ACPI_MAX_ISA_IRQS) + return penalty + acpi_isa_irq_penalty[irq]; + penalty += acpi_irq_pci_sharing_penalty(irq); return penalty; } -- cgit v1.1 From 7e3fd813717693597daaa95dee875f4cb2d911ef Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 5 Jul 2016 19:18:07 +0800 Subject: ACPI / debugger: Fix regression introduced by IS_ERR_VALUE() removal The FIFO unlocking mechanism in acpi_dbg has been broken by the following commit: Commit: 287980e49ffc0f6d911601e7e352a812ed27768e Subject: remove lots of IS_ERR_VALUE abuses It converted !IS_ERR_VALUE(ret) into !ret which was not entirely correct. Fix the regression by taking ret > 0 into account too as appropriate. Fixes: 287980e49ffc (remove lots of IS_ERR_VALUE abuses) Signed-off-by: Lv Zheng [ rjw: Simplifications, changelog & subject massage ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_dbg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/acpi') diff --git a/drivers/acpi/acpi_dbg.c b/drivers/acpi/acpi_dbg.c index 1f41284..dee8692 100644 --- a/drivers/acpi/acpi_dbg.c +++ b/drivers/acpi/acpi_dbg.c @@ -602,7 +602,7 @@ static int acpi_aml_read_user(char __user *buf, int len) crc->tail = (crc->tail + n) & (ACPI_AML_BUF_SIZE - 1); ret = n; out: - acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, !ret); + acpi_aml_unlock_fifo(ACPI_AML_OUT_USER, ret >= 0); return ret; } @@ -672,7 +672,7 @@ static int acpi_aml_write_user(const char __user *buf, int len) crc->head = (crc->head + n) & (ACPI_AML_BUF_SIZE - 1); ret = n; out: - acpi_aml_unlock_fifo(ACPI_AML_IN_USER, !ret); + acpi_aml_unlock_fifo(ACPI_AML_IN_USER, ret >= 0); return n; } -- cgit v1.1