Merge branches 'acpi-drivers', 'acpi-pm', 'acpi-ec' and 'acpi-video'

* acpi-drivers:
  ACPI / GED: make evged.c explicitly non-modular
  ACPI / amba: Remove CLK_IS_ROOT
  ACPI / APD: Remove CLK_IS_ROOT
  ACPI: implement Generic Event Device

* acpi-pm:
  ACPI / PM: Introduce efi poweroff for HW-full platforms without _S5

* acpi-ec:
  ACPI 2.0 / AML: Improve module level execution by moving the If/Else/While execution to per-table basis
  ACPI 2.0 / ECDT: Enable correct ECDT initialization order
  ACPI 2.0 / ECDT: Remove early namespace reference from EC
  ACPI 2.0 / ECDT: Split EC_FLAGS_HANDLERS_INSTALLED

* acpi-video:
  ACPI / video: mark acpi_video_get_levels() inline
  Thermal / ACPI / video: add INT3406 thermal driver
  ACPI/video: export acpi_video_get_levels
  video / backlight: remove the backlight_device_registered API
  video / backlight: add two APIs for drivers to use
This commit is contained in:
Rafael J. Wysocki 2016-05-16 16:44:41 +02:00
19 changed files with 683 additions and 240 deletions

View File

@ -373,5 +373,5 @@ bool efi_reboot_required(void)
bool efi_poweroff_required(void)
{
return !!acpi_gbl_reduced_hardware;
return acpi_gbl_reduced_hardware || acpi_no_s5;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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");

View File

@ -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
@ -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
@ -367,7 +366,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 +376,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 +1288,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)
@ -1344,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)
@ -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;
@ -1448,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
@ -1503,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_validate_ecdt, "MSI MS-171F", {
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_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},
{},
@ -1534,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();
@ -1547,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");
if (ACPI_FAILURE(status)) {
ret = -ENODEV;
goto error;
}
if (!ecdt_ptr->control.address || !ecdt_ptr->data.address) {
/*
* Asus X50GL:
* https://bugzilla.kernel.org/show_bug.cgi?id=11880
*/
ret = -ENODEV;
goto error;
}
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 {
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 (EC_FLAGS_SKIP_DSDT_SCAN) {
kfree(saved_ec);
return -ENODEV;
}
/* 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;
} 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;
}
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)

154
drivers/acpi/evged.c Normal file
View File

@ -0,0 +1,154 @@
/*
* 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 <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#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),
},
};
builtin_platform_driver(ged_driver);

View File

@ -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;

View File

@ -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;

View File

@ -338,31 +338,9 @@ config INTEL_QUARK_DTS_THERMAL
hot & critical. The critical trip point default value is set by
underlying BIOS/Firmware.
config INT340X_THERMAL
tristate "ACPI INT340X thermal drivers"
depends on X86 && ACPI
select THERMAL_GOV_USER_SPACE
select ACPI_THERMAL_REL
select ACPI_FAN
select INTEL_SOC_DTS_IOSF_CORE
select THERMAL_WRITABLE_TRIPS
help
Newer laptops and tablets that use ACPI may have thermal sensors and
other devices with thermal control capabilities outside the core
CPU/SOC, for thermal safety reasons.
They are exposed for the OS to use via the INT3400 ACPI device object
as the master, and INT3401~INT340B ACPI device objects as the slaves.
Enable this to expose the temperature information and cooling ability
from these objects to userspace via the normal thermal framework.
This means that a wide range of applications and GUI widgets can show
the information to the user or use this information for making
decisions. For example, the Intel Thermal Daemon can use this
information to allow the user to select his laptop to run without
turning on the fans.
config ACPI_THERMAL_REL
tristate
depends on ACPI
menu "ACPI INT340X thermal drivers"
source drivers/thermal/int340x_thermal/Kconfig
endmenu
config INTEL_PCH_THERMAL
tristate "Intel PCH Thermal Reporting Driver"

View File

@ -0,0 +1,42 @@
#
# ACPI INT340x thermal drivers configuration
#
config INT340X_THERMAL
tristate "ACPI INT340X thermal drivers"
depends on X86 && ACPI
select THERMAL_GOV_USER_SPACE
select ACPI_THERMAL_REL
select ACPI_FAN
select INTEL_SOC_DTS_IOSF_CORE
help
Newer laptops and tablets that use ACPI may have thermal sensors and
other devices with thermal control capabilities outside the core
CPU/SOC, for thermal safety reasons.
They are exposed for the OS to use via the INT3400 ACPI device object
as the master, and INT3401~INT340B ACPI device objects as the slaves.
Enable this to expose the temperature information and cooling ability
from these objects to userspace via the normal thermal framework.
This means that a wide range of applications and GUI widgets can show
the information to the user or use this information for making
decisions. For example, the Intel Thermal Daemon can use this
information to allow the user to select his laptop to run without
turning on the fans.
config ACPI_THERMAL_REL
tristate
depends on ACPI
if INT340X_THERMAL
config INT3406_THERMAL
tristate "ACPI INT3406 display thermal driver"
depends on ACPI_VIDEO
help
The display thermal device represents the LED/LCD display panel
that may or may not include touch support. The main function of
the display thermal device is to allow control of the display
brightness in order to address a thermal condition or to reduce
power consumed by display device.
endif

View File

@ -3,4 +3,5 @@ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal_zone.o
obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o
obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o

View File

@ -0,0 +1,236 @@
/*
* INT3406 thermal driver for display participant device
*
* Copyright (C) 2016, Intel Corporation
* Authors: Aaron Lu <aaron.lu@intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/backlight.h>
#include <linux/thermal.h>
#include <acpi/video.h>
#define INT3406_BRIGHTNESS_LIMITS_CHANGED 0x80
struct int3406_thermal_data {
int upper_limit;
int upper_limit_index;
int lower_limit;
int lower_limit_index;
acpi_handle handle;
struct acpi_video_device_brightness *br;
struct backlight_device *raw_bd;
struct thermal_cooling_device *cooling_dev;
};
static int int3406_thermal_to_raw(int level, struct int3406_thermal_data *d)
{
int max_level = d->br->levels[d->br->count - 1];
int raw_max = d->raw_bd->props.max_brightness;
return level * raw_max / max_level;
}
static int int3406_thermal_to_acpi(int level, struct int3406_thermal_data *d)
{
int raw_max = d->raw_bd->props.max_brightness;
int max_level = d->br->levels[d->br->count - 1];
return level * max_level / raw_max;
}
static int
int3406_thermal_get_max_state(struct thermal_cooling_device *cooling_dev,
unsigned long *state)
{
struct int3406_thermal_data *d = cooling_dev->devdata;
int index = d->lower_limit_index ? d->lower_limit_index : 2;
*state = d->br->count - 1 - index;
return 0;
}
static int
int3406_thermal_set_cur_state(struct thermal_cooling_device *cooling_dev,
unsigned long state)
{
struct int3406_thermal_data *d = cooling_dev->devdata;
int level, raw_level;
if (state > d->br->count - 3)
return -EINVAL;
state = d->br->count - 1 - state;
level = d->br->levels[state];
if ((d->upper_limit && level > d->upper_limit) ||
(d->lower_limit && level < d->lower_limit))
return -EINVAL;
raw_level = int3406_thermal_to_raw(level, d);
return backlight_device_set_brightness(d->raw_bd, raw_level);
}
static int
int3406_thermal_get_cur_state(struct thermal_cooling_device *cooling_dev,
unsigned long *state)
{
struct int3406_thermal_data *d = cooling_dev->devdata;
int raw_level, level, i;
int *levels = d->br->levels;
raw_level = d->raw_bd->props.brightness;
level = int3406_thermal_to_acpi(raw_level, d);
/*
* There is no 1:1 mapping between the firmware interface level with the
* raw interface level, we will have to find one that is close enough.
*/
for (i = 2; i < d->br->count; i++) {
if (level < levels[i]) {
if (i == 2)
break;
if ((level - levels[i - 1]) < (levels[i] - level))
i--;
break;
}
}
*state = d->br->count - 1 - i;
return 0;
}
static const struct thermal_cooling_device_ops video_cooling_ops = {
.get_max_state = int3406_thermal_get_max_state,
.get_cur_state = int3406_thermal_get_cur_state,
.set_cur_state = int3406_thermal_set_cur_state,
};
static int int3406_thermal_get_index(int *array, int nr, int value)
{
int i;
for (i = 0; i < nr; i++) {
if (array[i] == value)
break;
}
return i == nr ? -ENOENT : i;
}
static void int3406_thermal_get_limit(struct int3406_thermal_data *d)
{
acpi_status status;
unsigned long long lower_limit, upper_limit;
int index;
status = acpi_evaluate_integer(d->handle, "DDDL", NULL, &lower_limit);
if (ACPI_SUCCESS(status)) {
index = int3406_thermal_get_index(d->br->levels, d->br->count,
lower_limit);
if (index > 0) {
d->lower_limit = (int)lower_limit;
d->lower_limit_index = index;
}
}
status = acpi_evaluate_integer(d->handle, "DDPC", NULL, &upper_limit);
if (ACPI_SUCCESS(status)) {
index = int3406_thermal_get_index(d->br->levels, d->br->count,
upper_limit);
if (index > 0) {
d->upper_limit = (int)upper_limit;
d->upper_limit_index = index;
}
}
}
static void int3406_notify(acpi_handle handle, u32 event, void *data)
{
if (event == INT3406_BRIGHTNESS_LIMITS_CHANGED)
int3406_thermal_get_limit(data);
}
static int int3406_thermal_probe(struct platform_device *pdev)
{
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
struct int3406_thermal_data *d;
struct backlight_device *bd;
int ret;
if (!ACPI_HANDLE(&pdev->dev))
return -ENODEV;
d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
if (!d)
return -ENOMEM;
d->handle = ACPI_HANDLE(&pdev->dev);
bd = backlight_device_get_by_type(BACKLIGHT_RAW);
if (!bd)
return -ENODEV;
d->raw_bd = bd;
ret = acpi_video_get_levels(ACPI_COMPANION(&pdev->dev), &d->br);
if (ret)
return ret;
int3406_thermal_get_limit(d);
d->cooling_dev = thermal_cooling_device_register(acpi_device_bid(adev),
d, &video_cooling_ops);
if (IS_ERR(d->cooling_dev))
goto err;
ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
int3406_notify, d);
if (ret)
goto err_cdev;
platform_set_drvdata(pdev, d);
return 0;
err_cdev:
thermal_cooling_device_unregister(d->cooling_dev);
err:
kfree(d->br);
return -ENODEV;
}
static int int3406_thermal_remove(struct platform_device *pdev)
{
struct int3406_thermal_data *d = platform_get_drvdata(pdev);
thermal_cooling_device_unregister(d->cooling_dev);
kfree(d->br);
return 0;
}
static const struct acpi_device_id int3406_thermal_match[] = {
{"INT3406", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, int3406_thermal_match);
static struct platform_driver int3406_thermal_driver = {
.probe = int3406_thermal_probe,
.remove = int3406_thermal_remove,
.driver = {
.name = "int3406 thermal",
.owner = THIS_MODULE,
.acpi_match_table = int3406_thermal_match,
},
};
module_platform_driver(int3406_thermal_driver);
MODULE_DESCRIPTION("INT3406 Thermal driver");
MODULE_LICENSE("GPL v2");

View File

@ -164,6 +164,30 @@ static ssize_t brightness_show(struct device *dev,
return sprintf(buf, "%d\n", bd->props.brightness);
}
int backlight_device_set_brightness(struct backlight_device *bd,
unsigned long brightness)
{
int rc = -ENXIO;
mutex_lock(&bd->ops_lock);
if (bd->ops) {
if (brightness > bd->props.max_brightness)
rc = -EINVAL;
else {
pr_debug("set brightness to %lu\n", brightness);
bd->props.brightness = brightness;
backlight_update_status(bd);
rc = 0;
}
}
mutex_unlock(&bd->ops_lock);
backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);
return rc;
}
EXPORT_SYMBOL(backlight_device_set_brightness);
static ssize_t brightness_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
@ -175,24 +199,9 @@ static ssize_t brightness_store(struct device *dev,
if (rc)
return rc;
rc = -ENXIO;
rc = backlight_device_set_brightness(bd, brightness);
mutex_lock(&bd->ops_lock);
if (bd->ops) {
if (brightness > bd->props.max_brightness)
rc = -EINVAL;
else {
pr_debug("set brightness to %lu\n", brightness);
bd->props.brightness = brightness;
backlight_update_status(bd);
rc = count;
}
}
mutex_unlock(&bd->ops_lock);
backlight_generate_event(bd, BACKLIGHT_UPDATE_SYSFS);
return rc;
return rc ? rc : count;
}
static DEVICE_ATTR_RW(brightness);
@ -380,7 +389,7 @@ struct backlight_device *backlight_device_register(const char *name,
}
EXPORT_SYMBOL(backlight_device_register);
bool backlight_device_registered(enum backlight_type type)
struct backlight_device *backlight_device_get_by_type(enum backlight_type type)
{
bool found = false;
struct backlight_device *bd;
@ -394,9 +403,9 @@ bool backlight_device_registered(enum backlight_type type)
}
mutex_unlock(&backlight_dev_list_mutex);
return found;
return found ? bd : NULL;
}
EXPORT_SYMBOL(backlight_device_registered);
EXPORT_SYMBOL(backlight_device_get_by_type);
/**
* backlight_device_unregister - unregisters a backlight device object.

View File

@ -192,7 +192,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE);
/*
* Optionally support group module level code.
*/
ACPI_INIT_GLOBAL(u8, acpi_gbl_group_module_level_code, TRUE);
ACPI_INIT_GLOBAL(u8, acpi_gbl_group_module_level_code, FALSE);
/*
* Optionally use 32-bit FADT addresses if and when there is a conflict

View File

@ -4,6 +4,19 @@
#include <linux/errno.h> /* for ENODEV */
#include <linux/types.h> /* for bool */
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_device;
#define ACPI_VIDEO_CLASS "video"
@ -37,6 +50,8 @@ extern void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type);
* may change over time and should not be cached.
*/
extern bool acpi_video_handles_brightness_key_presses(void);
extern int acpi_video_get_levels(struct acpi_device *device,
struct acpi_video_device_brightness **dev_br);
#else
static inline int acpi_video_register(void) { return 0; }
static inline void acpi_video_unregister(void) { return; }
@ -56,6 +71,11 @@ static inline bool acpi_video_handles_brightness_key_presses(void)
{
return false;
}
static inline int acpi_video_get_levels(struct acpi_device *device,
struct acpi_video_device_brightness **dev_br)
{
return -ENODEV;
}
#endif
#endif

View File

@ -278,6 +278,7 @@ void acpi_irq_stats_init(void);
extern u32 acpi_irq_handled;
extern u32 acpi_irq_not_handled;
extern unsigned int acpi_sci_irq;
extern bool acpi_no_s5;
#define INVALID_ACPI_IRQ ((unsigned)-1)
static inline bool acpi_sci_irq_valid(void)
{

View File

@ -141,9 +141,10 @@ extern void devm_backlight_device_unregister(struct device *dev,
struct backlight_device *bd);
extern void backlight_force_update(struct backlight_device *bd,
enum backlight_update_reason reason);
extern bool backlight_device_registered(enum backlight_type type);
extern int backlight_register_notifier(struct notifier_block *nb);
extern int backlight_unregister_notifier(struct notifier_block *nb);
extern struct backlight_device *backlight_device_get_by_type(enum backlight_type type);
extern int backlight_device_set_brightness(struct backlight_device *bd, unsigned long brightness);
#define to_backlight_device(obj) container_of(obj, struct backlight_device, dev)