forked from Minki/linux
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:
commit
a6becfbaba
@ -373,5 +373,5 @@ bool efi_reboot_required(void)
|
|||||||
|
|
||||||
bool efi_poweroff_required(void)
|
bool efi_poweroff_required(void)
|
||||||
{
|
{
|
||||||
return !!acpi_gbl_reduced_hardware;
|
return acpi_gbl_reduced_hardware || acpi_no_s5;
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o
|
|||||||
acpi-y += int340x_thermal.o
|
acpi-y += int340x_thermal.o
|
||||||
acpi-y += power.o
|
acpi-y += power.o
|
||||||
acpi-y += event.o
|
acpi-y += event.o
|
||||||
|
acpi-$(CONFIG_ACPI_REDUCED_HARDWARE_ONLY) += evged.o
|
||||||
acpi-y += sysfs.o
|
acpi-y += sysfs.o
|
||||||
acpi-y += property.o
|
acpi-y += property.o
|
||||||
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
|
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
|
||||||
|
@ -35,8 +35,7 @@ static void amba_register_dummy_clk(void)
|
|||||||
if (amba_dummy_clk)
|
if (amba_dummy_clk)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
amba_dummy_clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL,
|
amba_dummy_clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 0);
|
||||||
CLK_IS_ROOT, 0);
|
|
||||||
clk_register_clkdev(amba_dummy_clk, "apb_pclk", NULL);
|
clk_register_clkdev(amba_dummy_clk, "apb_pclk", NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,8 +62,7 @@ static int acpi_apd_setup(struct apd_private_data *pdata)
|
|||||||
if (dev_desc->fixed_clk_rate) {
|
if (dev_desc->fixed_clk_rate) {
|
||||||
clk = clk_register_fixed_rate(&pdata->adev->dev,
|
clk = clk_register_fixed_rate(&pdata->adev->dev,
|
||||||
dev_name(&pdata->adev->dev),
|
dev_name(&pdata->adev->dev),
|
||||||
NULL, CLK_IS_ROOT,
|
NULL, 0, dev_desc->fixed_clk_rate);
|
||||||
dev_desc->fixed_clk_rate);
|
|
||||||
clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev));
|
clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev));
|
||||||
pdata->clk = clk;
|
pdata->clk = clk;
|
||||||
}
|
}
|
||||||
|
@ -191,19 +191,6 @@ struct acpi_video_device_cap {
|
|||||||
u8 _DDC:1; /* Return the EDID for this device */
|
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 {
|
struct acpi_video_device {
|
||||||
unsigned long device_id;
|
unsigned long device_id;
|
||||||
struct acpi_video_device_flags flags;
|
struct acpi_video_device_flags flags;
|
||||||
@ -325,7 +312,7 @@ static const struct thermal_cooling_device_ops video_cooling_ops = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
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)
|
union acpi_object **levels)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
@ -335,7 +322,7 @@ acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
|
|||||||
|
|
||||||
*levels = NULL;
|
*levels = NULL;
|
||||||
|
|
||||||
status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer);
|
status = acpi_evaluate_object(handle, "_BCL", NULL, &buffer);
|
||||||
if (!ACPI_SUCCESS(status))
|
if (!ACPI_SUCCESS(status))
|
||||||
return status;
|
return status;
|
||||||
obj = (union acpi_object *)buffer.pointer;
|
obj = (union acpi_object *)buffer.pointer;
|
||||||
@ -766,36 +753,28 @@ static int acpi_video_bqc_quirk(struct acpi_video_device *device,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int acpi_video_get_levels(struct acpi_device *device,
|
||||||
/*
|
struct acpi_video_device_brightness **dev_br)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
union acpi_object *obj = NULL;
|
union acpi_object *obj = NULL;
|
||||||
int i, max_level = 0, count = 0, level_ac_battery = 0;
|
int i, max_level = 0, count = 0, level_ac_battery = 0;
|
||||||
unsigned long long level, level_old;
|
|
||||||
union acpi_object *o;
|
union acpi_object *o;
|
||||||
struct acpi_video_device_brightness *br = NULL;
|
struct acpi_video_device_brightness *br = NULL;
|
||||||
int result = -EINVAL;
|
int result = 0;
|
||||||
u32 value;
|
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 "
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available "
|
||||||
"LCD brightness level\n"));
|
"LCD brightness level\n"));
|
||||||
|
result = -ENODEV;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj->package.count < 2)
|
if (obj->package.count < 2) {
|
||||||
|
result = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
br = kzalloc(sizeof(*br), GFP_KERNEL);
|
br = kzalloc(sizeof(*br), GFP_KERNEL);
|
||||||
if (!br) {
|
if (!br) {
|
||||||
@ -861,6 +840,38 @@ acpi_video_init_brightness(struct acpi_video_device *device)
|
|||||||
"Found unordered _BCL package"));
|
"Found unordered _BCL package"));
|
||||||
|
|
||||||
br->count = count;
|
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;
|
device->brightness = br;
|
||||||
|
|
||||||
/* _BQC uses INDEX while _BCL uses VALUE in some laptops */
|
/* _BQC uses INDEX while _BCL uses VALUE in some laptops */
|
||||||
@ -903,17 +914,13 @@ set_level:
|
|||||||
goto out_free_levels;
|
goto out_free_levels;
|
||||||
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||||
"found %d brightness levels\n", count - 2));
|
"found %d brightness levels\n", br->count - 2));
|
||||||
kfree(obj);
|
return 0;
|
||||||
return result;
|
|
||||||
|
|
||||||
out_free_levels:
|
out_free_levels:
|
||||||
kfree(br->levels);
|
kfree(br->levels);
|
||||||
out_free:
|
|
||||||
kfree(br);
|
kfree(br);
|
||||||
out:
|
|
||||||
device->brightness = NULL;
|
device->brightness = NULL;
|
||||||
kfree(obj);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -925,11 +925,13 @@ void __init acpi_early_init(void)
|
|||||||
goto error0;
|
goto error0;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = acpi_load_tables();
|
if (acpi_gbl_group_module_level_code) {
|
||||||
if (ACPI_FAILURE(status)) {
|
status = acpi_load_tables();
|
||||||
printk(KERN_ERR PREFIX
|
if (ACPI_FAILURE(status)) {
|
||||||
"Unable to load the System Description Tables\n");
|
printk(KERN_ERR PREFIX
|
||||||
goto error0;
|
"Unable to load the System Description Tables\n");
|
||||||
|
goto error0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_X86
|
#ifdef CONFIG_X86
|
||||||
@ -995,17 +997,10 @@ static int __init acpi_bus_init(void)
|
|||||||
|
|
||||||
acpi_os_initialize1();
|
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
|
* 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()
|
* the EC device is found in the namespace (i.e. before
|
||||||
* is called).
|
* acpi_load_tables() is called).
|
||||||
*
|
*
|
||||||
* This is accomplished by looking for the ECDT table, and getting
|
* This is accomplished by looking for the ECDT table, and getting
|
||||||
* the EC parameters out of that.
|
* the EC parameters out of that.
|
||||||
@ -1013,6 +1008,22 @@ static int __init acpi_bus_init(void)
|
|||||||
status = acpi_ec_ecdt_probe();
|
status = acpi_ec_ecdt_probe();
|
||||||
/* Ignore result. Not having an ECDT is not fatal. */
|
/* 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);
|
status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION);
|
||||||
if (ACPI_FAILURE(status)) {
|
if (ACPI_FAILURE(status)) {
|
||||||
printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n");
|
printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n");
|
||||||
|
@ -105,8 +105,8 @@ enum ec_command {
|
|||||||
enum {
|
enum {
|
||||||
EC_FLAGS_QUERY_PENDING, /* Query is pending */
|
EC_FLAGS_QUERY_PENDING, /* Query is pending */
|
||||||
EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */
|
EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */
|
||||||
EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and
|
EC_FLAGS_GPE_HANDLER_INSTALLED, /* GPE handler installed */
|
||||||
* OpReg are installed */
|
EC_FLAGS_EC_HANDLER_INSTALLED, /* OpReg handler installed */
|
||||||
EC_FLAGS_STARTED, /* Driver is started */
|
EC_FLAGS_STARTED, /* Driver is started */
|
||||||
EC_FLAGS_STOPPED, /* Driver is stopped */
|
EC_FLAGS_STOPPED, /* Driver is stopped */
|
||||||
EC_FLAGS_COMMAND_STORM, /* GPE storms occurred to the
|
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;
|
struct acpi_ec *boot_ec, *first_ec;
|
||||||
EXPORT_SYMBOL(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_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_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */
|
||||||
|
static int EC_FLAGS_CORRECT_ECDT; /* Needs ECDT port address correction */
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
* Logging/Debugging
|
* 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)
|
static void acpi_ec_submit_request(struct acpi_ec *ec)
|
||||||
{
|
{
|
||||||
ec->reference_count++;
|
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);
|
acpi_ec_enable_gpe(ec, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,7 +376,8 @@ static void acpi_ec_complete_request(struct acpi_ec *ec)
|
|||||||
bool flushed = false;
|
bool flushed = false;
|
||||||
|
|
||||||
ec->reference_count--;
|
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);
|
acpi_ec_disable_gpe(ec, true);
|
||||||
flushed = acpi_ec_flushed(ec);
|
flushed = acpi_ec_flushed(ec);
|
||||||
if (flushed)
|
if (flushed)
|
||||||
@ -1287,52 +1288,64 @@ static int ec_install_handlers(struct acpi_ec *ec)
|
|||||||
{
|
{
|
||||||
acpi_status status;
|
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);
|
acpi_ec_start(ec, false);
|
||||||
status = acpi_install_address_space_handler(ec->handle,
|
|
||||||
ACPI_ADR_SPACE_EC,
|
if (!test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) {
|
||||||
&acpi_ec_space_handler,
|
status = acpi_install_address_space_handler(ec->handle,
|
||||||
NULL, ec);
|
ACPI_ADR_SPACE_EC,
|
||||||
if (ACPI_FAILURE(status)) {
|
&acpi_ec_space_handler,
|
||||||
if (status == AE_NOT_FOUND) {
|
NULL, ec);
|
||||||
/*
|
if (ACPI_FAILURE(status)) {
|
||||||
* Maybe OS fails in evaluating the _REG object.
|
if (status == AE_NOT_FOUND) {
|
||||||
* The AE_NOT_FOUND error will be ignored and OS
|
/*
|
||||||
* continue to initialize EC.
|
* Maybe OS fails in evaluating the _REG
|
||||||
*/
|
* object. The AE_NOT_FOUND error will be
|
||||||
pr_err("Fail in evaluating the _REG object"
|
* ignored and OS * continue to initialize
|
||||||
" of EC device. Broken bios is suspected.\n");
|
* EC.
|
||||||
} else {
|
*/
|
||||||
acpi_ec_stop(ec, false);
|
pr_err("Fail in evaluating the _REG object"
|
||||||
acpi_remove_gpe_handler(NULL, ec->gpe,
|
" of EC device. Broken bios is suspected.\n");
|
||||||
&acpi_ec_gpe_handler);
|
} else {
|
||||||
return -ENODEV;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ec_remove_handlers(struct acpi_ec *ec)
|
static void ec_remove_handlers(struct acpi_ec *ec)
|
||||||
{
|
{
|
||||||
if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags))
|
|
||||||
return;
|
|
||||||
acpi_ec_stop(ec, false);
|
acpi_ec_stop(ec, false);
|
||||||
if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
|
|
||||||
ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
|
if (test_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags)) {
|
||||||
pr_err("failed to remove space handler\n");
|
if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
|
||||||
if (ACPI_FAILURE(acpi_remove_gpe_handler(NULL, ec->gpe,
|
ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
|
||||||
&acpi_ec_gpe_handler)))
|
pr_err("failed to remove space handler\n");
|
||||||
pr_err("failed to remove gpe handler\n");
|
clear_bit(EC_FLAGS_EC_HANDLER_INSTALLED, &ec->flags);
|
||||||
clear_bit(EC_FLAGS_HANDLERS_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)
|
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);
|
strcpy(acpi_device_class(device), ACPI_EC_CLASS);
|
||||||
|
|
||||||
/* Check for boot EC */
|
/* Check for boot EC */
|
||||||
if (boot_ec &&
|
if (boot_ec) {
|
||||||
(boot_ec->handle == device->handle ||
|
|
||||||
boot_ec->handle == ACPI_ROOT_OBJECT)) {
|
|
||||||
ec = boot_ec;
|
ec = boot_ec;
|
||||||
boot_ec = NULL;
|
boot_ec = NULL;
|
||||||
|
ec_remove_handlers(ec);
|
||||||
|
if (first_ec == ec)
|
||||||
|
first_ec = NULL;
|
||||||
} else {
|
} else {
|
||||||
ec = make_acpi_ec();
|
ec = make_acpi_ec();
|
||||||
if (!ec)
|
if (!ec)
|
||||||
@ -1434,7 +1448,7 @@ ec_parse_io_ports(struct acpi_resource *resource, void *context)
|
|||||||
|
|
||||||
int __init acpi_boot_ec_enable(void)
|
int __init acpi_boot_ec_enable(void)
|
||||||
{
|
{
|
||||||
if (!boot_ec || test_bit(EC_FLAGS_HANDLERS_INSTALLED, &boot_ec->flags))
|
if (!boot_ec)
|
||||||
return 0;
|
return 0;
|
||||||
if (!ec_install_handlers(boot_ec)) {
|
if (!ec_install_handlers(boot_ec)) {
|
||||||
first_ec = boot_ec;
|
first_ec = boot_ec;
|
||||||
@ -1448,20 +1462,6 @@ static const struct acpi_device_id ec_device_ids[] = {
|
|||||||
{"", 0},
|
{"", 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
|
#if 0
|
||||||
/*
|
/*
|
||||||
* Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not
|
* 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;
|
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 = {
|
static struct dmi_system_id ec_dmi_table[] __initdata = {
|
||||||
{
|
{
|
||||||
ec_skip_dsdt_scan, "Compal JFL92", {
|
ec_correct_ecdt, "Asus L4R", {
|
||||||
DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"),
|
DMI_MATCH(DMI_BIOS_VERSION, "1008.006"),
|
||||||
DMI_MATCH(DMI_BOARD_NAME, "JFL92") }, NULL},
|
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_SYS_VENDOR, "Micro-Star"),
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),}, NULL},
|
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", {
|
ec_clear_on_resume, "Samsung hardware", {
|
||||||
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL},
|
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 __init acpi_ec_ecdt_probe(void)
|
||||||
{
|
{
|
||||||
|
int ret = 0;
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
struct acpi_ec *saved_ec = NULL;
|
|
||||||
struct acpi_table_ecdt *ecdt_ptr;
|
struct acpi_table_ecdt *ecdt_ptr;
|
||||||
|
|
||||||
boot_ec = make_acpi_ec();
|
boot_ec = make_acpi_ec();
|
||||||
@ -1547,67 +1546,45 @@ int __init acpi_ec_ecdt_probe(void)
|
|||||||
dmi_check_system(ec_dmi_table);
|
dmi_check_system(ec_dmi_table);
|
||||||
status = acpi_get_table(ACPI_SIG_ECDT, 1,
|
status = acpi_get_table(ACPI_SIG_ECDT, 1,
|
||||||
(struct acpi_table_header **)&ecdt_ptr);
|
(struct acpi_table_header **)&ecdt_ptr);
|
||||||
if (ACPI_SUCCESS(status)) {
|
if (ACPI_FAILURE(status)) {
|
||||||
pr_info("EC description table is found, configuring boot EC\n");
|
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->command_addr = ecdt_ptr->control.address;
|
||||||
boot_ec->data_addr = ecdt_ptr->data.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 */
|
|
||||||
}
|
}
|
||||||
|
boot_ec->gpe = ecdt_ptr->gpe;
|
||||||
if (EC_FLAGS_SKIP_DSDT_SCAN) {
|
boot_ec->handle = ACPI_ROOT_OBJECT;
|
||||||
kfree(saved_ec);
|
ret = ec_install_handlers(boot_ec);
|
||||||
return -ENODEV;
|
if (!ret)
|
||||||
}
|
|
||||||
|
|
||||||
/* 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)) {
|
|
||||||
first_ec = boot_ec;
|
first_ec = boot_ec;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
error:
|
error:
|
||||||
kfree(boot_ec);
|
if (ret) {
|
||||||
kfree(saved_ec);
|
kfree(boot_ec);
|
||||||
boot_ec = NULL;
|
boot_ec = NULL;
|
||||||
return -ENODEV;
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int param_set_event_clearing(const char *val, struct kernel_param *kp)
|
static int param_set_event_clearing(const char *val, struct kernel_param *kp)
|
||||||
|
154
drivers/acpi/evged.c
Normal file
154
drivers/acpi/evged.c
Normal 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);
|
@ -26,6 +26,11 @@
|
|||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "sleep.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 u8 sleep_states[ACPI_S_STATE_COUNT];
|
||||||
|
|
||||||
static void acpi_sleep_tts_switch(u32 acpi_state)
|
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;
|
sleep_states[ACPI_STATE_S5] = 1;
|
||||||
pm_power_off_prepare = acpi_power_off_prepare;
|
pm_power_off_prepare = acpi_power_off_prepare;
|
||||||
pm_power_off = acpi_power_off;
|
pm_power_off = acpi_power_off;
|
||||||
|
} else {
|
||||||
|
acpi_no_s5 = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
supported[0] = 0;
|
supported[0] = 0;
|
||||||
|
@ -358,7 +358,7 @@ enum acpi_backlight_type acpi_video_get_backlight_type(void)
|
|||||||
if (!(video_caps & ACPI_VIDEO_BACKLIGHT))
|
if (!(video_caps & ACPI_VIDEO_BACKLIGHT))
|
||||||
return acpi_backlight_vendor;
|
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_native;
|
||||||
|
|
||||||
return acpi_backlight_video;
|
return acpi_backlight_video;
|
||||||
|
@ -338,31 +338,9 @@ config INTEL_QUARK_DTS_THERMAL
|
|||||||
hot & critical. The critical trip point default value is set by
|
hot & critical. The critical trip point default value is set by
|
||||||
underlying BIOS/Firmware.
|
underlying BIOS/Firmware.
|
||||||
|
|
||||||
config INT340X_THERMAL
|
menu "ACPI INT340X thermal drivers"
|
||||||
tristate "ACPI INT340X thermal drivers"
|
source drivers/thermal/int340x_thermal/Kconfig
|
||||||
depends on X86 && ACPI
|
endmenu
|
||||||
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
|
|
||||||
|
|
||||||
config INTEL_PCH_THERMAL
|
config INTEL_PCH_THERMAL
|
||||||
tristate "Intel PCH Thermal Reporting Driver"
|
tristate "Intel PCH Thermal Reporting Driver"
|
||||||
|
42
drivers/thermal/int340x_thermal/Kconfig
Normal file
42
drivers/thermal/int340x_thermal/Kconfig
Normal 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
|
@ -3,4 +3,5 @@ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal_zone.o
|
|||||||
obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
|
obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
|
||||||
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
|
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
|
||||||
obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.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
|
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
|
||||||
|
236
drivers/thermal/int340x_thermal/int3406_thermal.c
Normal file
236
drivers/thermal/int340x_thermal/int3406_thermal.c
Normal 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");
|
@ -164,6 +164,30 @@ static ssize_t brightness_show(struct device *dev,
|
|||||||
return sprintf(buf, "%d\n", bd->props.brightness);
|
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,
|
static ssize_t brightness_store(struct device *dev,
|
||||||
struct device_attribute *attr, const char *buf, size_t count)
|
struct device_attribute *attr, const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
@ -175,24 +199,9 @@ static ssize_t brightness_store(struct device *dev,
|
|||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = -ENXIO;
|
rc = backlight_device_set_brightness(bd, brightness);
|
||||||
|
|
||||||
mutex_lock(&bd->ops_lock);
|
return rc ? rc : count;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR_RW(brightness);
|
static DEVICE_ATTR_RW(brightness);
|
||||||
|
|
||||||
@ -380,7 +389,7 @@ struct backlight_device *backlight_device_register(const char *name,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(backlight_device_register);
|
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;
|
bool found = false;
|
||||||
struct backlight_device *bd;
|
struct backlight_device *bd;
|
||||||
@ -394,9 +403,9 @@ bool backlight_device_registered(enum backlight_type type)
|
|||||||
}
|
}
|
||||||
mutex_unlock(&backlight_dev_list_mutex);
|
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.
|
* backlight_device_unregister - unregisters a backlight device object.
|
||||||
|
@ -192,7 +192,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE);
|
|||||||
/*
|
/*
|
||||||
* Optionally support group module level code.
|
* 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
|
* Optionally use 32-bit FADT addresses if and when there is a conflict
|
||||||
|
@ -4,6 +4,19 @@
|
|||||||
#include <linux/errno.h> /* for ENODEV */
|
#include <linux/errno.h> /* for ENODEV */
|
||||||
#include <linux/types.h> /* for bool */
|
#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;
|
struct acpi_device;
|
||||||
|
|
||||||
#define ACPI_VIDEO_CLASS "video"
|
#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.
|
* may change over time and should not be cached.
|
||||||
*/
|
*/
|
||||||
extern bool acpi_video_handles_brightness_key_presses(void);
|
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
|
#else
|
||||||
static inline int acpi_video_register(void) { return 0; }
|
static inline int acpi_video_register(void) { return 0; }
|
||||||
static inline void acpi_video_unregister(void) { return; }
|
static inline void acpi_video_unregister(void) { return; }
|
||||||
@ -56,6 +71,11 @@ static inline bool acpi_video_handles_brightness_key_presses(void)
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
static inline int acpi_video_get_levels(struct acpi_device *device,
|
||||||
|
struct acpi_video_device_brightness **dev_br)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -278,6 +278,7 @@ void acpi_irq_stats_init(void);
|
|||||||
extern u32 acpi_irq_handled;
|
extern u32 acpi_irq_handled;
|
||||||
extern u32 acpi_irq_not_handled;
|
extern u32 acpi_irq_not_handled;
|
||||||
extern unsigned int acpi_sci_irq;
|
extern unsigned int acpi_sci_irq;
|
||||||
|
extern bool acpi_no_s5;
|
||||||
#define INVALID_ACPI_IRQ ((unsigned)-1)
|
#define INVALID_ACPI_IRQ ((unsigned)-1)
|
||||||
static inline bool acpi_sci_irq_valid(void)
|
static inline bool acpi_sci_irq_valid(void)
|
||||||
{
|
{
|
||||||
|
@ -141,9 +141,10 @@ extern void devm_backlight_device_unregister(struct device *dev,
|
|||||||
struct backlight_device *bd);
|
struct backlight_device *bd);
|
||||||
extern void backlight_force_update(struct backlight_device *bd,
|
extern void backlight_force_update(struct backlight_device *bd,
|
||||||
enum backlight_update_reason reason);
|
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_register_notifier(struct notifier_block *nb);
|
||||||
extern int backlight_unregister_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)
|
#define to_backlight_device(obj) container_of(obj, struct backlight_device, dev)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user