diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 36 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 4 | ||||
-rw-r--r-- | drivers/acpi/acpi_lpss.c | 98 | ||||
-rw-r--r-- | drivers/acpi/acpica/acglobal.h | 1 | ||||
-rw-r--r-- | drivers/acpi/acpica/aclocal.h | 4 | ||||
-rw-r--r-- | drivers/acpi/acpica/evgpe.c | 9 | ||||
-rw-r--r-- | drivers/acpi/acpica/hwgpe.c | 53 | ||||
-rw-r--r-- | drivers/acpi/acpica/utresrc.c | 2 | ||||
-rw-r--r-- | drivers/acpi/acpica/utxface.c | 4 | ||||
-rw-r--r-- | drivers/acpi/acpica/utxfinit.c | 11 | ||||
-rw-r--r-- | drivers/acpi/battery.c | 4 | ||||
-rw-r--r-- | drivers/acpi/blacklist.c | 8 | ||||
-rw-r--r-- | drivers/acpi/device_pm.c | 12 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 8 | ||||
-rw-r--r-- | drivers/acpi/pci_irq.c | 2 | ||||
-rw-r--r-- | drivers/acpi/pmic/intel_pmic.c | 354 | ||||
-rw-r--r-- | drivers/acpi/pmic/intel_pmic.h | 25 | ||||
-rw-r--r-- | drivers/acpi/pmic/intel_pmic_crc.c | 211 | ||||
-rw-r--r-- | drivers/acpi/pmic/intel_pmic_xpower.c | 268 | ||||
-rw-r--r-- | drivers/acpi/processor_idle.c | 20 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 85 | ||||
-rw-r--r-- | drivers/acpi/sleep.c | 2 | ||||
-rw-r--r-- | drivers/acpi/tables.c | 68 | ||||
-rw-r--r-- | drivers/acpi/utils.c | 5 | ||||
-rw-r--r-- | drivers/acpi/video.c | 18 |
25 files changed, 1213 insertions, 99 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b23fe37f67c0..8951cefb0a96 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -360,15 +360,14 @@ config ACPI_BGRT config ACPI_REDUCED_HARDWARE_ONLY bool "Hardware-reduced ACPI support only" if EXPERT def_bool n - depends on ACPI help - This config item changes the way the ACPI code is built. When this - option is selected, the kernel will use a specialized version of - ACPICA that ONLY supports the ACPI "reduced hardware" mode. The - resulting kernel will be smaller but it will also be restricted to - running in ACPI reduced hardware mode ONLY. + This config item changes the way the ACPI code is built. When this + option is selected, the kernel will use a specialized version of + ACPICA that ONLY supports the ACPI "reduced hardware" mode. The + resulting kernel will be smaller but it will also be restricted to + running in ACPI reduced hardware mode ONLY. - If you are unsure what to do, do not enable this option. + If you are unsure what to do, do not enable this option. source "drivers/acpi/apei/Kconfig" @@ -394,4 +393,27 @@ config ACPI_EXTLOG driver adds support for that functionality with corresponding tracepoint which carries that information to userspace. +menuconfig PMIC_OPREGION + bool "PMIC (Power Management Integrated Circuit) operation region support" + help + Select this option to enable support for ACPI operation + region of the PMIC chip. The operation region can be used + to control power rails and sensor reading/writing on the + PMIC chip. + +if PMIC_OPREGION +config CRC_PMIC_OPREGION + bool "ACPI operation region support for CrystalCove PMIC" + depends on INTEL_SOC_PMIC + help + This config adds ACPI operation region support for CrystalCove PMIC. + +config XPOWER_PMIC_OPREGION + bool "ACPI operation region support for XPower AXP288 PMIC" + depends on AXP288_ADC = y + help + This config adds ACPI operation region support for XPower AXP288 PMIC. + +endif + endif # ACPI diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 6d11522f0e48..f74317cc1ca9 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -88,3 +88,7 @@ obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o obj-$(CONFIG_ACPI_APEI) += apei/ obj-$(CONFIG_ACPI_EXTLOG) += acpi_extlog.o + +obj-$(CONFIG_PMIC_OPREGION) += pmic/intel_pmic.o +obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o +obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 93d160661f4c..4f3febf8a589 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -1,7 +1,7 @@ /* * ACPI support for Intel Lynxpoint LPSS. * - * Copyright (C) 2013, Intel Corporation + * Copyright (C) 2013, 2014, Intel Corporation * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> * Rafael J. Wysocki <rafael.j.wysocki@intel.com> * @@ -60,6 +60,8 @@ ACPI_MODULE_NAME("acpi_lpss"); #define LPSS_CLK_DIVIDER BIT(2) #define LPSS_LTR BIT(3) #define LPSS_SAVE_CTX BIT(4) +#define LPSS_DEV_PROXY BIT(5) +#define LPSS_PROXY_REQ BIT(6) struct lpss_private_data; @@ -70,8 +72,10 @@ struct lpss_device_desc { void (*setup)(struct lpss_private_data *pdata); }; +static struct device *proxy_device; + static struct lpss_device_desc lpss_dma_desc = { - .flags = LPSS_CLK, + .flags = LPSS_CLK | LPSS_PROXY_REQ, }; struct lpss_private_data { @@ -146,22 +150,24 @@ static struct lpss_device_desc byt_pwm_dev_desc = { }; static struct lpss_device_desc byt_uart_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX | + LPSS_DEV_PROXY, .prv_offset = 0x800, .setup = lpss_uart_setup, }; static struct lpss_device_desc byt_spi_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX | + LPSS_DEV_PROXY, .prv_offset = 0x400, }; static struct lpss_device_desc byt_sdio_dev_desc = { - .flags = LPSS_CLK, + .flags = LPSS_CLK | LPSS_DEV_PROXY, }; static struct lpss_device_desc byt_i2c_dev_desc = { - .flags = LPSS_CLK | LPSS_SAVE_CTX, + .flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_DEV_PROXY, .prv_offset = 0x800, .setup = byt_i2c_setup, }; @@ -368,6 +374,8 @@ static int acpi_lpss_create_device(struct acpi_device *adev, adev->driver_data = pdata; pdev = acpi_create_platform_device(adev); if (!IS_ERR_OR_NULL(pdev)) { + if (!proxy_device && dev_desc->flags & LPSS_DEV_PROXY) + proxy_device = &pdev->dev; return 1; } @@ -499,14 +507,15 @@ static void acpi_lpss_set_ltr(struct device *dev, s32 val) /** * acpi_lpss_save_ctx() - Save the private registers of LPSS device * @dev: LPSS device + * @pdata: pointer to the private data of the LPSS device * * Most LPSS devices have private registers which may loose their context when * the device is powered down. acpi_lpss_save_ctx() saves those registers into * prv_reg_ctx array. */ -static void acpi_lpss_save_ctx(struct device *dev) +static void acpi_lpss_save_ctx(struct device *dev, + struct lpss_private_data *pdata) { - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); unsigned int i; for (i = 0; i < LPSS_PRV_REG_COUNT; i++) { @@ -521,12 +530,13 @@ static void acpi_lpss_save_ctx(struct device *dev) /** * acpi_lpss_restore_ctx() - Restore the private registers of LPSS device * @dev: LPSS device + * @pdata: pointer to the private data of the LPSS device * * Restores the registers that were previously stored with acpi_lpss_save_ctx(). */ -static void acpi_lpss_restore_ctx(struct device *dev) +static void acpi_lpss_restore_ctx(struct device *dev, + struct lpss_private_data *pdata) { - struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); unsigned int i; /* @@ -549,54 +559,82 @@ static void acpi_lpss_restore_ctx(struct device *dev) #ifdef CONFIG_PM_SLEEP static int acpi_lpss_suspend_late(struct device *dev) { - int ret = pm_generic_suspend_late(dev); + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + ret = pm_generic_suspend_late(dev); if (ret) return ret; - acpi_lpss_save_ctx(dev); + if (pdata->dev_desc->flags & LPSS_SAVE_CTX) + acpi_lpss_save_ctx(dev, pdata); + return acpi_dev_suspend_late(dev); } static int acpi_lpss_resume_early(struct device *dev) { - int ret = acpi_dev_resume_early(dev); + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + ret = acpi_dev_resume_early(dev); if (ret) return ret; - acpi_lpss_restore_ctx(dev); + if (pdata->dev_desc->flags & LPSS_SAVE_CTX) + acpi_lpss_restore_ctx(dev, pdata); + return pm_generic_resume_early(dev); } #endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM_RUNTIME static int acpi_lpss_runtime_suspend(struct device *dev) { - int ret = pm_generic_runtime_suspend(dev); + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + ret = pm_generic_runtime_suspend(dev); + if (ret) + return ret; + if (pdata->dev_desc->flags & LPSS_SAVE_CTX) + acpi_lpss_save_ctx(dev, pdata); + + ret = acpi_dev_runtime_suspend(dev); if (ret) return ret; - acpi_lpss_save_ctx(dev); - return acpi_dev_runtime_suspend(dev); + if (pdata->dev_desc->flags & LPSS_PROXY_REQ && proxy_device) + return pm_runtime_put_sync_suspend(proxy_device); + + return 0; } static int acpi_lpss_runtime_resume(struct device *dev) { - int ret = acpi_dev_runtime_resume(dev); + struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); + int ret; + + if (pdata->dev_desc->flags & LPSS_PROXY_REQ && proxy_device) { + ret = pm_runtime_get_sync(proxy_device); + if (ret) + return ret; + } + ret = acpi_dev_runtime_resume(dev); if (ret) return ret; - acpi_lpss_restore_ctx(dev); + if (pdata->dev_desc->flags & LPSS_SAVE_CTX) + acpi_lpss_restore_ctx(dev, pdata); + return pm_generic_runtime_resume(dev); } -#endif /* CONFIG_PM_RUNTIME */ #endif /* CONFIG_PM */ static struct dev_pm_domain acpi_lpss_pm_domain = { .ops = { +#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, .complete = acpi_subsys_complete, @@ -608,7 +646,6 @@ static struct dev_pm_domain acpi_lpss_pm_domain = { .poweroff_late = acpi_lpss_suspend_late, .restore_early = acpi_lpss_resume_early, #endif -#ifdef CONFIG_PM_RUNTIME .runtime_suspend = acpi_lpss_runtime_suspend, .runtime_resume = acpi_lpss_runtime_resume, #endif @@ -631,30 +668,27 @@ static int acpi_lpss_platform_notify(struct notifier_block *nb, return 0; pdata = acpi_driver_data(adev); - if (!pdata || !pdata->mmio_base) + if (!pdata) return 0; - if (pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) { + if (pdata->mmio_base && + pdata->mmio_size < pdata->dev_desc->prv_offset + LPSS_LTR_SIZE) { dev_err(&pdev->dev, "MMIO size insufficient to access LTR\n"); return 0; } switch (action) { - case BUS_NOTIFY_BOUND_DRIVER: - if (pdata->dev_desc->flags & LPSS_SAVE_CTX) - pdev->dev.pm_domain = &acpi_lpss_pm_domain; - break; - case BUS_NOTIFY_UNBOUND_DRIVER: - if (pdata->dev_desc->flags & LPSS_SAVE_CTX) - pdev->dev.pm_domain = NULL; - break; case BUS_NOTIFY_ADD_DEVICE: + pdev->dev.pm_domain = &acpi_lpss_pm_domain; if (pdata->dev_desc->flags & LPSS_LTR) return sysfs_create_group(&pdev->dev.kobj, &lpss_attr_group); + break; case BUS_NOTIFY_DEL_DEVICE: if (pdata->dev_desc->flags & LPSS_LTR) sysfs_remove_group(&pdev->dev.kobj, &lpss_attr_group); + pdev->dev.pm_domain = NULL; + break; default: break; } diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index ebf02cc10a43..7f60582d0c8c 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -305,6 +305,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_db_output_flags, ACPI_DB_CONSOLE_OUTPUT); ACPI_INIT_GLOBAL(u8, acpi_gbl_no_resource_disassembly, FALSE); ACPI_INIT_GLOBAL(u8, acpi_gbl_ignore_noop_operator, FALSE); +ACPI_INIT_GLOBAL(u8, acpi_gbl_cstyle_disassembly, TRUE); ACPI_GLOBAL(u8, acpi_gbl_db_opt_disasm); ACPI_GLOBAL(u8, acpi_gbl_db_opt_verbose); diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index c00e7e41ad75..680d23bbae7c 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -454,6 +454,7 @@ struct acpi_gpe_register_info { u16 base_gpe_number; /* Base GPE number for this register */ u8 enable_for_wake; /* GPEs to keep enabled when sleeping */ u8 enable_for_run; /* GPEs to keep enabled when running */ + u8 enable_mask; /* Current mask of enabled GPEs */ }; /* @@ -722,6 +723,7 @@ union acpi_parse_value { ACPI_DISASM_ONLY_MEMBERS (\ u8 disasm_flags; /* Used during AML disassembly */\ u8 disasm_opcode; /* Subtype used for disassembly */\ + char *operator_symbol;/* Used for C-style operator name strings */\ char aml_op_name[16]) /* Op name (debug only) */ /* Flags for disasm_flags field above */ @@ -827,6 +829,8 @@ struct acpi_parse_state { #define ACPI_PARSEOP_EMPTY_TERMLIST 0x04 #define ACPI_PARSEOP_PREDEF_CHECKED 0x08 #define ACPI_PARSEOP_SPECIAL 0x10 +#define ACPI_PARSEOP_COMPOUND 0x20 +#define ACPI_PARSEOP_ASSIGNMENT 0x40 /***************************************************************************** * diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 2095dfb72bcb..aa70154cf4fa 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -134,7 +134,7 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) /* Enable the requested GPE */ - status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE_SAVE); return_ACPI_STATUS(status); } @@ -213,7 +213,7 @@ acpi_ev_remove_gpe_reference(struct acpi_gpe_event_info *gpe_event_info) if (ACPI_SUCCESS(status)) { status = acpi_hw_low_set_gpe(gpe_event_info, - ACPI_GPE_DISABLE); + ACPI_GPE_DISABLE_SAVE); } if (ACPI_FAILURE(status)) { @@ -616,8 +616,11 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context) { struct acpi_gpe_event_info *gpe_event_info = context; + acpi_cpu_flags flags; + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); (void)acpi_ev_finish_gpe(gpe_event_info); + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); ACPI_FREE(gpe_event_info); return; @@ -655,7 +658,7 @@ acpi_status acpi_ev_finish_gpe(struct acpi_gpe_event_info * gpe_event_info) /* * Enable this GPE, conditionally. This means that the GPE will - * only be physically enabled if the enable_for_run bit is set + * only be physically enabled if the enable_mask bit is set * in the event_info. */ (void)acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_CONDITIONAL_ENABLE); diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index 48ac7b7b59cd..494027f5c067 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -115,12 +115,12 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) /* Set or clear just the bit that corresponds to this GPE */ register_bit = acpi_hw_get_gpe_register_bit(gpe_event_info); - switch (action) { + switch (action & ~ACPI_GPE_SAVE_MASK) { case ACPI_GPE_CONDITIONAL_ENABLE: - /* Only enable if the enable_for_run bit is set */ + /* Only enable if the corresponding enable_mask bit is set */ - if (!(register_bit & gpe_register_info->enable_for_run)) { + if (!(register_bit & gpe_register_info->enable_mask)) { return (AE_BAD_PARAMETER); } @@ -145,6 +145,9 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) /* Write the updated enable mask */ status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); + if (ACPI_SUCCESS(status) && (action & ACPI_GPE_SAVE_MASK)) { + gpe_register_info->enable_mask = enable_mask; + } return (status); } @@ -262,6 +265,32 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, /****************************************************************************** * + * FUNCTION: acpi_hw_gpe_enable_write + * + * PARAMETERS: enable_mask - Bit mask to write to the GPE register + * gpe_register_info - Gpe Register info + * + * RETURN: Status + * + * DESCRIPTION: Write the enable mask byte to the given GPE register. + * + ******************************************************************************/ + +static acpi_status +acpi_hw_gpe_enable_write(u8 enable_mask, + struct acpi_gpe_register_info *gpe_register_info) +{ + acpi_status status; + + status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); + if (ACPI_SUCCESS(status)) { + gpe_register_info->enable_mask = enable_mask; + } + return (status); +} + +/****************************************************************************** + * * FUNCTION: acpi_hw_disable_gpe_block * * PARAMETERS: gpe_xrupt_info - GPE Interrupt info @@ -287,8 +316,8 @@ acpi_hw_disable_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, /* Disable all GPEs in this register */ status = - acpi_hw_write(0x00, - &gpe_block->register_info[i].enable_address); + acpi_hw_gpe_enable_write(0x00, + &gpe_block->register_info[i]); if (ACPI_FAILURE(status)) { return (status); } @@ -355,21 +384,23 @@ acpi_hw_enable_runtime_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, { u32 i; acpi_status status; + struct acpi_gpe_register_info *gpe_register_info; /* NOTE: assumes that all GPEs are currently disabled */ /* Examine each GPE Register within the block */ for (i = 0; i < gpe_block->register_count; i++) { - if (!gpe_block->register_info[i].enable_for_run) { + gpe_register_info = &gpe_block->register_info[i]; + if (!gpe_register_info->enable_for_run) { continue; } /* Enable all "runtime" GPEs in this register */ status = - acpi_hw_write(gpe_block->register_info[i].enable_for_run, - &gpe_block->register_info[i].enable_address); + acpi_hw_gpe_enable_write(gpe_register_info->enable_for_run, + gpe_register_info); if (ACPI_FAILURE(status)) { return (status); } @@ -399,10 +430,12 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, { u32 i; acpi_status status; + struct acpi_gpe_register_info *gpe_register_info; /* Examine each GPE Register within the block */ for (i = 0; i < gpe_block->register_count; i++) { + gpe_register_info = &gpe_block->register_info[i]; /* * Enable all "wake" GPEs in this register and disable the @@ -410,8 +443,8 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, */ status = - acpi_hw_write(gpe_block->register_info[i].enable_for_wake, - &gpe_block->register_info[i].enable_address); + acpi_hw_gpe_enable_write(gpe_register_info->enable_for_wake, + gpe_register_info); if (ACPI_FAILURE(status)) { return (status); } diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c index 5cd017c7ac0e..bc1ff820c7dd 100644 --- a/drivers/acpi/acpica/utresrc.c +++ b/drivers/acpi/acpica/utresrc.c @@ -263,7 +263,7 @@ const char *acpi_gbl_bpb_decode[] = { /* UART serial bus stop bits */ const char *acpi_gbl_sb_decode[] = { - "StopBitsNone", + "StopBitsZero", "StopBitsOne", "StopBitsOnePlusHalf", "StopBitsTwo" diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 502a8492dc83..49c873c68756 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -531,7 +531,9 @@ acpi_decode_pld_buffer(u8 *in_buffer, ACPI_MOVE_32_TO_32(&dword, &buffer[0]); pld_info->revision = ACPI_PLD_GET_REVISION(&dword); pld_info->ignore_color = ACPI_PLD_GET_IGNORE_COLOR(&dword); - pld_info->color = ACPI_PLD_GET_COLOR(&dword); + pld_info->red = ACPI_PLD_GET_RED(&dword); + pld_info->green = ACPI_PLD_GET_GREEN(&dword); + pld_info->blue = ACPI_PLD_GET_BLUE(&dword); /* Second 32-bit DWord */ diff --git a/drivers/acpi/acpica/utxfinit.c b/drivers/acpi/acpica/utxfinit.c index 13380d818462..b1fd6886e439 100644 --- a/drivers/acpi/acpica/utxfinit.c +++ b/drivers/acpi/acpica/utxfinit.c @@ -53,6 +53,9 @@ #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utxfinit") +/* For acpi_exec only */ +void ae_do_object_overrides(void); + /******************************************************************************* * * FUNCTION: acpi_initialize_subsystem @@ -65,6 +68,7 @@ ACPI_MODULE_NAME("utxfinit") * called, so any early initialization belongs here. * ******************************************************************************/ + acpi_status __init acpi_initialize_subsystem(void) { acpi_status status; @@ -275,6 +279,13 @@ acpi_status __init acpi_initialize_objects(u32 flags) return_ACPI_STATUS(status); } } +#ifdef ACPI_EXEC_APP + /* + * This call implements the "initialization file" option for acpi_exec. + * This is the precise point that we want to perform the overrides. + */ + ae_do_object_overrides(); +#endif /* * Execute any module-level code that was detected during the table load diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 8ec8a89a20ab..d98ba4355819 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1180,6 +1180,10 @@ static int acpi_battery_add(struct acpi_device *device) if (!device) return -EINVAL; + + if (device->dep_unmet) + return -EPROBE_DEFER; + battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL); if (!battery) return -ENOMEM; diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index ed122e17636e..7556e7c4a055 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -290,6 +290,14 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { 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. diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 143ec6ea1468..897640188acd 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -201,7 +201,7 @@ int acpi_device_set_power(struct acpi_device *device, int state) * Transition Power * ---------------- * In accordance with the ACPI specification first apply power (via - * power resources) and then evalute _PSx. + * power resources) and then evaluate _PSx. */ if (device->power.flags.power_resources) { result = acpi_power_transition(device, state); @@ -692,7 +692,6 @@ static int acpi_device_wakeup(struct acpi_device *adev, u32 target_state, return 0; } -#ifdef CONFIG_PM_RUNTIME /** * acpi_pm_device_run_wake - Enable/disable remote wakeup for given device. * @dev: Device to enable/disable the platform to wake up. @@ -714,7 +713,6 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) return acpi_device_wakeup(adev, ACPI_STATE_S0, enable); } EXPORT_SYMBOL(acpi_pm_device_run_wake); -#endif /* CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP /** @@ -773,7 +771,6 @@ static int acpi_dev_pm_full_power(struct acpi_device *adev) acpi_device_set_power(adev, ACPI_STATE_D0) : 0; } -#ifdef CONFIG_PM_RUNTIME /** * acpi_dev_runtime_suspend - Put device into a low-power state using ACPI. * @dev: Device to put into a low-power state. @@ -855,7 +852,6 @@ int acpi_subsys_runtime_resume(struct device *dev) return ret ? ret : pm_generic_runtime_resume(dev); } EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume); -#endif /* CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP /** @@ -878,7 +874,7 @@ int acpi_dev_suspend_late(struct device *dev) return 0; target_state = acpi_target_system_state(); - wakeup = device_may_wakeup(dev); + wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev); error = acpi_device_wakeup(adev, target_state, wakeup); if (wakeup && error) return error; @@ -1023,10 +1019,9 @@ EXPORT_SYMBOL_GPL(acpi_subsys_freeze); static struct dev_pm_domain acpi_general_pm_domain = { .ops = { -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM .runtime_suspend = acpi_subsys_runtime_suspend, .runtime_resume = acpi_subsys_runtime_resume, -#endif #ifdef CONFIG_PM_SLEEP .prepare = acpi_subsys_prepare, .complete = acpi_subsys_complete, @@ -1038,6 +1033,7 @@ static struct dev_pm_domain acpi_general_pm_domain = { .poweroff_late = acpi_subsys_suspend_late, .restore_early = acpi_subsys_resume_early, #endif +#endif }, }; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 9964f70be98d..f9eeae871593 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -436,7 +436,7 @@ static void acpi_os_drop_map_ref(struct acpi_ioremap *map) static void acpi_os_map_cleanup(struct acpi_ioremap *map) { if (!map->refcount) { - synchronize_rcu(); + synchronize_rcu_expedited(); acpi_unmap(map->phys, map->virt); kfree(map); } @@ -1188,6 +1188,12 @@ EXPORT_SYMBOL(acpi_os_execute); void acpi_os_wait_events_complete(void) { + /* + * Make sure the GPE handler or the fixed event handler is not used + * on another CPU after removal. + */ + if (acpi_irq_handler) + synchronize_hardirq(acpi_gbl_FADT.sci_interrupt); flush_workqueue(kacpid_wq); flush_workqueue(kacpi_notify_wq); } diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 6e6b80eb0bba..7cc4e33179f9 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -484,7 +484,7 @@ void acpi_pci_irq_disable(struct pci_dev *dev) /* Keep IOAPIC pin configuration when suspending */ if (dev->dev.power.is_prepared) return; -#ifdef CONFIG_PM_RUNTIME +#ifdef CONFIG_PM if (dev->dev.power.runtime_status == RPM_SUSPENDING) return; #endif diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c new file mode 100644 index 000000000000..a732e5d7e322 --- /dev/null +++ b/drivers/acpi/pmic/intel_pmic.c @@ -0,0 +1,354 @@ +/* + * intel_pmic.c - Intel PMIC operation region driver + * + * Copyright (C) 2014 Intel Corporation. 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 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. + */ + +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/regmap.h> +#include "intel_pmic.h" + +#define PMIC_POWER_OPREGION_ID 0x8d +#define PMIC_THERMAL_OPREGION_ID 0x8c + +struct acpi_lpat { + int temp; + int raw; +}; + +struct intel_pmic_opregion { + struct mutex lock; + struct acpi_lpat *lpat; + int lpat_count; + struct regmap *regmap; + struct intel_pmic_opregion_data *data; +}; + +static int pmic_get_reg_bit(int address, struct pmic_table *table, + int count, int *reg, int *bit) +{ + int i; + + for (i = 0; i < count; i++) { + if (table[i].address == address) { + *reg = table[i].reg; + if (bit) + *bit = table[i].bit; + return 0; + } + } + return -ENOENT; +} + +/** + * raw_to_temp(): Return temperature from raw value through LPAT table + * + * @lpat: the temperature_raw mapping table + * @count: the count of the above mapping table + * @raw: the raw value, used as a key to get the temerature from the + * above mapping table + * + * A positive value will be returned on success, a negative errno will + * be returned in error cases. + */ +static int raw_to_temp(struct acpi_lpat *lpat, int count, int raw) +{ + int i, delta_temp, delta_raw, temp; + + for (i = 0; i < count - 1; i++) { + if ((raw >= lpat[i].raw && raw <= lpat[i+1].raw) || + (raw <= lpat[i].raw && raw >= lpat[i+1].raw)) + break; + } + + if (i == count - 1) + return -ENOENT; + + delta_temp = lpat[i+1].temp - lpat[i].temp; + delta_raw = lpat[i+1].raw - lpat[i].raw; + temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw; + + return temp; +} + +/** + * temp_to_raw(): Return raw value from temperature through LPAT table + * + * @lpat: the temperature_raw mapping table + * @count: the count of the above mapping table + * @temp: the temperature, used as a key to get the raw value from the + * above mapping table + * + * A positive value will be returned on success, a negative errno will + * be returned in error cases. + */ +static int temp_to_raw(struct acpi_lpat *lpat, int count, int temp) +{ + int i, delta_temp, delta_raw, raw; + + for (i = 0; i < count - 1; i++) { + if (temp >= lpat[i].temp && temp <= lpat[i+1].temp) + break; + } + + if (i == count - 1) + return -ENOENT; + + delta_temp = lpat[i+1].temp - lpat[i].temp; + delta_raw = lpat[i+1].raw - lpat[i].raw; + raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp; + + return raw; +} + +static void pmic_thermal_lpat(struct intel_pmic_opregion *opregion, + acpi_handle handle, struct device *dev) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj_p, *obj_e; + int *lpat, i; + acpi_status status; + + status = acpi_evaluate_object(handle, "LPAT", NULL, &buffer); + if (ACPI_FAILURE(status)) + return; + + obj_p = (union acpi_object *)buffer.pointer; + if (!obj_p || (obj_p->type != ACPI_TYPE_PACKAGE) || + (obj_p->package.count % 2) || (obj_p->package.count < 4)) + goto out; + + lpat = devm_kmalloc(dev, sizeof(int) * obj_p->package.count, + GFP_KERNEL); + if (!lpat) + goto out; + + for (i = 0; i < obj_p->package.count; i++) { + obj_e = &obj_p->package.elements[i]; + if (obj_e->type != ACPI_TYPE_INTEGER) { + devm_kfree(dev, lpat); + goto out; + } + lpat[i] = (s64)obj_e->integer.value; + } + + opregion->lpat = (struct acpi_lpat *)lpat; + opregion->lpat_count = obj_p->package.count / 2; + +out: + kfree(buffer.pointer); +} + +static acpi_status intel_pmic_power_handler(u32 function, + acpi_physical_address address, u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct intel_pmic_opregion *opregion = region_context; + struct regmap *regmap = opregion->regmap; + struct intel_pmic_opregion_data *d = opregion->data; + int reg, bit, result; + + if (bits != 32 || !value64) + return AE_BAD_PARAMETER; + + if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1)) + return AE_BAD_PARAMETER; + + result = pmic_get_reg_bit(address, d->power_table, + d->power_table_count, ®, &bit); + if (result == -ENOENT) + return AE_BAD_PARAMETER; + + mutex_lock(&opregion->lock); + + result = function == ACPI_READ ? + d->get_power(regmap, reg, bit, value64) : + d->update_power(regmap, reg, bit, *value64 == 1); + + mutex_unlock(&opregion->lock); + + return result ? AE_ERROR : AE_OK; +} + +static int pmic_read_temp(struct intel_pmic_opregion *opregion, + int reg, u64 *value) +{ + int raw_temp, temp; + + if (!opregion->data->get_raw_temp) + return -ENXIO; + + raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg); + if (raw_temp < 0) + return raw_temp; + + if (!opregion->lpat) { + *value = raw_temp; + return 0; + } + + temp = raw_to_temp(opregion->lpat, opregion->lpat_count, raw_temp); + if (temp < 0) + return temp; + + *value = temp; + return 0; +} + +static int pmic_thermal_temp(struct intel_pmic_opregion *opregion, int reg, + u32 function, u64 *value) +{ + return function == ACPI_READ ? + pmic_read_temp(opregion, reg, value) : -EINVAL; +} + +static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg, + u32 function, u64 *value) +{ + int raw_temp; + + if (function == ACPI_READ) + return pmic_read_temp(opregion, reg, value); + + if (!opregion->data->update_aux) + return -ENXIO; + + if (opregion->lpat) { + raw_temp = temp_to_raw(opregion->lpat, opregion->lpat_count, + *value); + if (raw_temp < 0) + return raw_temp; + } else { + raw_temp = *value; + } + + return opregion->data->update_aux(opregion->regmap, reg, raw_temp); +} + +static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, + u32 function, u64 *value) +{ + struct intel_pmic_opregion_data *d = opregion->data; + struct regmap *regmap = opregion->regmap; + + if (!d->get_policy || !d->update_policy) + return -ENXIO; + + if (function == ACPI_READ) + return d->get_policy(regmap, reg, value); + + if (*value != 0 && *value != 1) + return -EINVAL; + + return d->update_policy(regmap, reg, *value); +} + +static bool pmic_thermal_is_temp(int address) +{ + return (address <= 0x3c) && !(address % 12); +} + +static bool pmic_thermal_is_aux(int address) +{ + return (address >= 4 && address <= 0x40 && !((address - 4) % 12)) || + (address >= 8 && address <= 0x44 && !((address - 8) % 12)); +} + +static bool pmic_thermal_is_pen(int address) +{ + return address >= 0x48 && address <= 0x5c; +} + +static acpi_status intel_pmic_thermal_handler(u32 function, + acpi_physical_address address, u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct intel_pmic_opregion *opregion = region_context; + struct intel_pmic_opregion_data *d = opregion->data; + int reg, result; + + if (bits != 32 || !value64) + return AE_BAD_PARAMETER; + + result = pmic_get_reg_bit(address, d->thermal_table, + d->thermal_table_count, ®, NULL); + if (result == -ENOENT) + return AE_BAD_PARAMETER; + + mutex_lock(&opregion->lock); + + if (pmic_thermal_is_temp(address)) + result = pmic_thermal_temp(opregion, reg, function, value64); + else if (pmic_thermal_is_aux(address)) + result = pmic_thermal_aux(opregion, reg, function, value64); + else if (pmic_thermal_is_pen(address)) + result = pmic_thermal_pen(opregion, reg, function, value64); + else + result = -EINVAL; + + mutex_unlock(&opregion->lock); + + if (result < 0) { + if (result == -EINVAL) + return AE_BAD_PARAMETER; + else + return AE_ERROR; + } + + return AE_OK; +} + +int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, + struct regmap *regmap, + struct intel_pmic_opregion_data *d) +{ + acpi_status status; + struct intel_pmic_opregion *opregion; + + if (!dev || !regmap || !d) + return -EINVAL; + + if (!handle) + return -ENODEV; + + opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL); + if (!opregion) + return -ENOMEM; + + mutex_init(&opregion->lock); + opregion->regmap = regmap; + pmic_thermal_lpat(opregion, handle, dev); + + status = acpi_install_address_space_handler(handle, + PMIC_POWER_OPREGION_ID, + intel_pmic_power_handler, + NULL, opregion); + if (ACPI_FAILURE(status)) + return -ENODEV; + + status = acpi_install_address_space_handler(handle, + PMIC_THERMAL_OPREGION_ID, + intel_pmic_thermal_handler, + NULL, opregion); + if (ACPI_FAILURE(status)) { + acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, + intel_pmic_power_handler); + return -ENODEV; + } + + opregion->data = d; + return 0; +} +EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); + +MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/pmic/intel_pmic.h b/drivers/acpi/pmic/intel_pmic.h new file mode 100644 index 000000000000..d4e90af8f0dd --- /dev/null +++ b/drivers/acpi/pmic/intel_pmic.h @@ -0,0 +1,25 @@ +#ifndef __INTEL_PMIC_H +#define __INTEL_PMIC_H + +struct pmic_table { + int address; /* operation region address */ + int reg; /* corresponding thermal register */ + int bit; /* control bit for power */ +}; + +struct intel_pmic_opregion_data { + int (*get_power)(struct regmap *r, int reg, int bit, u64 *value); + int (*update_power)(struct regmap *r, int reg, int bit, bool on); + int (*get_raw_temp)(struct regmap *r, int reg); + int (*update_aux)(struct regmap *r, int reg, int raw_temp); + int (*get_policy)(struct regmap *r, int reg, u64 *value); + int (*update_policy)(struct regmap *r, int reg, int enable); + struct pmic_table *power_table; + int power_table_count; + struct pmic_table *thermal_table; + int thermal_table_count; +}; + +int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, struct regmap *regmap, struct intel_pmic_opregion_data *d); + +#endif diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c new file mode 100644 index 000000000000..ef7d8ff95abe --- /dev/null +++ b/drivers/acpi/pmic/intel_pmic_crc.c @@ -0,0 +1,211 @@ +/* + * intel_pmic_crc.c - Intel CrystalCove PMIC operation region driver + * + * Copyright (C) 2014 Intel Corporation. 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 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. + */ + +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/mfd/intel_soc_pmic.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include "intel_pmic.h" + +#define PWR_SOURCE_SELECT BIT(1) + +#define PMIC_A0LOCK_REG 0xc5 + +static struct pmic_table power_table[] = { + { + .address = 0x24, + .reg = 0x66, + .bit = 0x00, + }, + { + .address = 0x48, + .reg = 0x5d, + .bit = 0x00, + }, +}; + +static struct pmic_table thermal_table[] = { + { + .address = 0x00, + .reg = 0x75 + }, + { + .address = 0x04, + .reg = 0x95 + }, + { + .address = 0x08, + .reg = 0x97 + }, + { + .address = 0x0c, + .reg = 0x77 + }, + { + .address = 0x10, + .reg = 0x9a + }, + { + .address = 0x14, + .reg = 0x9c + }, + { + .address = 0x18, + .reg = 0x79 + }, + { + .address = 0x1c, + .reg = 0x9f + }, + { + .address = 0x20, + .reg = 0xa1 + }, + { + .address = 0x48, + .reg = 0x94 + }, + { + .address = 0x4c, + .reg = 0x99 + }, + { + .address = 0x50, + .reg = 0x9e + }, +}; + +static int intel_crc_pmic_get_power(struct regmap *regmap, int reg, + int bit, u64 *value) +{ + int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + *value = (data & PWR_SOURCE_SELECT) && (data & BIT(bit)) ? 1 : 0; + return 0; +} + +static int intel_crc_pmic_update_power(struct regmap *regmap, int reg, + int bit, bool on) +{ + int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + if (on) { + data |= PWR_SOURCE_SELECT | BIT(bit); + } else { + data &= ~BIT(bit); + data |= PWR_SOURCE_SELECT; + } + + if (regmap_write(regmap, reg, data)) + return -EIO; + return 0; +} + +static int intel_crc_pmic_get_raw_temp(struct regmap *regmap, int reg) +{ + int temp_l, temp_h; + + /* + * Raw temperature value is 10bits: 8bits in reg + * and 2bits in reg-1: bit0,1 + */ + if (regmap_read(regmap, reg, &temp_l) || + regmap_read(regmap, reg - 1, &temp_h)) + return -EIO; + + return temp_l | (temp_h & 0x3) << 8; +} + +static int intel_crc_pmic_update_aux(struct regmap *regmap, int reg, int raw) +{ + return regmap_write(regmap, reg, raw) || + regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8) ? -EIO : 0; +} + +static int intel_crc_pmic_get_policy(struct regmap *regmap, int reg, u64 *value) +{ + int pen; + + if (regmap_read(regmap, reg, &pen)) + return -EIO; + *value = pen >> 7; + return 0; +} + +static int intel_crc_pmic_update_policy(struct regmap *regmap, + int reg, int enable) +{ + int alert0; + + /* Update to policy enable bit requires unlocking a0lock */ + if (regmap_read(regmap, PMIC_A0LOCK_REG, &alert0)) + return -EIO; + + if (regmap_update_bits(regmap, PMIC_A0LOCK_REG, 0x01, 0)) + return -EIO; + + if (regmap_update_bits(regmap, reg, 0x80, enable << 7)) + return -EIO; + + /* restore alert0 */ + if (regmap_write(regmap, PMIC_A0LOCK_REG, alert0)) + return -EIO; + + return 0; +} + +static struct intel_pmic_opregion_data intel_crc_pmic_opregion_data = { + .get_power = intel_crc_pmic_get_power, + .update_power = intel_crc_pmic_update_power, + .get_raw_temp = intel_crc_pmic_get_raw_temp, + .update_aux = intel_crc_pmic_update_aux, + .get_policy = intel_crc_pmic_get_policy, + .update_policy = intel_crc_pmic_update_policy, + .power_table = power_table, + .power_table_count= ARRAY_SIZE(power_table), + .thermal_table = thermal_table, + .thermal_table_count = ARRAY_SIZE(thermal_table), +}; + +static int intel_crc_pmic_opregion_probe(struct platform_device *pdev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + return intel_pmic_install_opregion_handler(&pdev->dev, + ACPI_HANDLE(pdev->dev.parent), pmic->regmap, + &intel_crc_pmic_opregion_data); +} + +static struct platform_driver intel_crc_pmic_opregion_driver = { + .probe = intel_crc_pmic_opregion_probe, + .driver = { + .name = "crystal_cove_pmic", + }, +}; + +static int __init intel_crc_pmic_opregion_driver_init(void) +{ + return platform_driver_register(&intel_crc_pmic_opregion_driver); +} +module_init(intel_crc_pmic_opregion_driver_init); + +MODULE_DESCRIPTION("CrystalCove ACPI opration region driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c new file mode 100644 index 000000000000..6a082d4de12c --- /dev/null +++ b/drivers/acpi/pmic/intel_pmic_xpower.c @@ -0,0 +1,268 @@ +/* + * intel_pmic_xpower.c - XPower AXP288 PMIC operation region driver + * + * Copyright (C) 2014 Intel Corporation. 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 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. + */ + +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/mfd/axp20x.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/iio/consumer.h> +#include "intel_pmic.h" + +#define XPOWER_GPADC_LOW 0x5b + +static struct pmic_table power_table[] = { + { + .address = 0x00, + .reg = 0x13, + .bit = 0x05, + }, + { + .address = 0x04, + .reg = 0x13, + .bit = 0x06, + }, + { + .address = 0x08, + .reg = 0x13, + .bit = 0x07, + }, + { + .address = 0x0c, + .reg = 0x12, + .bit = 0x03, + }, + { + .address = 0x10, + .reg = 0x12, + .bit = 0x04, + }, + { + .address = 0x14, + .reg = 0x12, + .bit = 0x05, + }, + { + .address = 0x18, + .reg = 0x12, + .bit = 0x06, + }, + { + .address = 0x1c, + .reg = 0x12, + .bit = 0x00, + }, + { + .address = 0x20, + .reg = 0x12, + .bit = 0x01, + }, + { + .address = 0x24, + .reg = 0x12, + .bit = 0x02, + }, + { + .address = 0x28, + .reg = 0x13, + .bit = 0x02, + }, + { + .address = 0x2c, + .reg = 0x13, + .bit = 0x03, + }, + { + .address = 0x30, + .reg = 0x13, + .bit = 0x04, + }, + { + .address = 0x38, + .reg = 0x10, + .bit = 0x03, + }, + { + .address = 0x3c, + .reg = 0x10, + .bit = 0x06, + }, + { + .address = 0x40, + .reg = 0x10, + .bit = 0x05, + }, + { + .address = 0x44, + .reg = 0x10, + .bit = 0x04, + }, + { + .address = 0x48, + .reg = 0x10, + .bit = 0x01, + }, + { + .address = 0x4c, + .reg = 0x10, + .bit = 0x00 + }, +}; + +/* TMP0 - TMP5 are the same, all from GPADC */ +static struct pmic_table thermal_table[] = { + { + .address = 0x00, + .reg = XPOWER_GPADC_LOW + }, + { + .address = 0x0c, + .reg = XPOWER_GPADC_LOW + }, + { + .address = 0x18, + .reg = XPOWER_GPADC_LOW + }, + { + .address = 0x24, + .reg = XPOWER_GPADC_LOW + }, + { + .address = 0x30, + .reg = XPOWER_GPADC_LOW + }, + { + .address = 0x3c, + .reg = XPOWER_GPADC_LOW + }, +}; + +static int intel_xpower_pmic_get_power(struct regmap *regmap, int reg, + int bit, u64 *value) +{ + int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + *value = (data & BIT(bit)) ? 1 : 0; + return 0; +} + +static int intel_xpower_pmic_update_power(struct regmap *regmap, int reg, + int bit, bool on) +{ + int data; + + if (regmap_read(regmap, reg, &data)) + return -EIO; + + if (on) + data |= BIT(bit); + else + data &= ~BIT(bit); + + if (regmap_write(regmap, reg, data)) + return -EIO; + + return 0; +} + +/** + * intel_xpower_pmic_get_raw_temp(): Get raw temperature reading from the PMIC + * + * @regmap: regmap of the PMIC device + * @reg: register to get the reading + * + * We could get the sensor value by manipulating the HW regs here, but since + * the axp288 IIO driver may also access the same regs at the same time, the + * APIs provided by IIO subsystem are used here instead to avoid problems. As + * a result, the two passed in params are of no actual use. + * + * Return a positive value on success, errno on failure. + */ +static int intel_xpower_pmic_get_raw_temp(struct regmap *regmap, int reg) +{ + struct iio_channel *gpadc_chan; + int ret, val; + + gpadc_chan = iio_channel_get(NULL, "axp288-system-temp"); + if (IS_ERR_OR_NULL(gpadc_chan)) + return -EACCES; + + ret = iio_read_channel_raw(gpadc_chan, &val); + if (ret < 0) + val = ret; + + iio_channel_release(gpadc_chan); + return val; +} + +static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = { + .get_power = intel_xpower_pmic_get_power, + .update_power = intel_xpower_pmic_update_power, + .get_raw_temp = intel_xpower_pmic_get_raw_temp, + .power_table = power_table, + .power_table_count = ARRAY_SIZE(power_table), + .thermal_table = thermal_table, + .thermal_table_count = ARRAY_SIZE(thermal_table), +}; + +static acpi_status intel_xpower_pmic_gpio_handler(u32 function, + acpi_physical_address address, u32 bit_width, u64 *value, + void *handler_context, void *region_context) +{ + return AE_OK; +} + +static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev) +{ + struct device *parent = pdev->dev.parent; + struct axp20x_dev *axp20x = dev_get_drvdata(parent); + acpi_status status; + int result; + + status = acpi_install_address_space_handler(ACPI_HANDLE(parent), + ACPI_ADR_SPACE_GPIO, intel_xpower_pmic_gpio_handler, + NULL, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + result = intel_pmic_install_opregion_handler(&pdev->dev, + ACPI_HANDLE(parent), axp20x->regmap, + &intel_xpower_pmic_opregion_data); + if (result) + acpi_remove_address_space_handler(ACPI_HANDLE(parent), + ACPI_ADR_SPACE_GPIO, + intel_xpower_pmic_gpio_handler); + + return result; +} + +static struct platform_driver intel_xpower_pmic_opregion_driver = { + .probe = intel_xpower_pmic_opregion_probe, + .driver = { + .name = "axp288_pmic_acpi", + }, +}; + +static int __init intel_xpower_pmic_opregion_driver_init(void) +{ + return platform_driver_register(&intel_xpower_pmic_opregion_driver); +} +module_init(intel_xpower_pmic_opregion_driver_init); + +MODULE_DESCRIPTION("XPower AXP288 ACPI operation region driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 17f9ec501972..499536504698 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -334,10 +334,10 @@ static int acpi_processor_get_power_info_default(struct acpi_processor *pr) static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) { - acpi_status status = 0; + acpi_status status; u64 count; int current_count; - int i; + int i, ret = 0; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *cst; @@ -358,7 +358,7 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) /* There must be at least 2 elements */ if (!cst || (cst->type != ACPI_TYPE_PACKAGE) || cst->package.count < 2) { printk(KERN_ERR PREFIX "not enough elements in _CST\n"); - status = -EFAULT; + ret = -EFAULT; goto end; } @@ -367,7 +367,7 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) /* Validate number of power states. */ if (count < 1 || count != cst->package.count - 1) { printk(KERN_ERR PREFIX "count given by _CST is not valid\n"); - status = -EFAULT; + ret = -EFAULT; goto end; } @@ -489,12 +489,12 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr) /* Validate number of power states discovered */ if (current_count < 2) - status = -EFAULT; + ret = -EFAULT; end: kfree(buffer.pointer); - return status; + return ret; } static void acpi_processor_power_verify_c3(struct acpi_processor *pr, @@ -985,8 +985,8 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) state->flags = 0; switch (cx->type) { case ACPI_STATE_C1: - if (cx->entry_method == ACPI_CSTATE_FFH) - state->flags |= CPUIDLE_FLAG_TIME_VALID; + if (cx->entry_method != ACPI_CSTATE_FFH) + state->flags |= CPUIDLE_FLAG_TIME_INVALID; state->enter = acpi_idle_enter_c1; state->enter_dead = acpi_idle_play_dead; @@ -994,14 +994,12 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) break; case ACPI_STATE_C2: - state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = acpi_idle_enter_simple; state->enter_dead = acpi_idle_play_dead; drv->safe_state_index = count; break; case ACPI_STATE_C3: - state->flags |= CPUIDLE_FLAG_TIME_VALID; state->enter = pr->flags.bm_check ? acpi_idle_enter_bm : acpi_idle_enter_simple; @@ -1111,7 +1109,7 @@ static int acpi_processor_registered; int acpi_processor_power_init(struct acpi_processor *pr) { - acpi_status status = 0; + acpi_status status; int retval; struct cpuidle_device *dev; static int first_run; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9cb5cca3cfe3..1b1cf558d3d3 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -36,6 +36,8 @@ bool acpi_force_hot_remove; static const char *dummy_hid = "device"; +static LIST_HEAD(acpi_dep_list); +static DEFINE_MUTEX(acpi_dep_list_lock); static LIST_HEAD(acpi_bus_id_list); static DEFINE_MUTEX(acpi_scan_lock); static LIST_HEAD(acpi_scan_handlers_list); @@ -43,6 +45,12 @@ DEFINE_MUTEX(acpi_device_lock); LIST_HEAD(acpi_wakeup_device_list); static DEFINE_MUTEX(acpi_hp_context_lock); +struct acpi_dep_data { + struct list_head node; + acpi_handle master; + acpi_handle slave; +}; + struct acpi_device_bus_id{ char bus_id[15]; unsigned int instance_no; @@ -2193,6 +2201,59 @@ static void acpi_scan_init_hotplug(struct acpi_device *adev) } } +static void acpi_device_dep_initialize(struct acpi_device *adev) +{ + struct acpi_dep_data *dep; + struct acpi_handle_list dep_devices; + acpi_status status; + int i; + + if (!acpi_has_method(adev->handle, "_DEP")) + return; + + status = acpi_evaluate_reference(adev->handle, "_DEP", NULL, + &dep_devices); + if (ACPI_FAILURE(status)) { + dev_err(&adev->dev, "Failed to evaluate _DEP.\n"); + return; + } + + for (i = 0; i < dep_devices.count; i++) { + struct acpi_device_info *info; + int skip; + + status = acpi_get_object_info(dep_devices.handles[i], &info); + if (ACPI_FAILURE(status)) { + dev_err(&adev->dev, "Error reading device info\n"); + continue; + } + + /* + * Skip the dependency of Windows System Power + * Management Controller + */ + skip = info->valid & ACPI_VALID_HID && + !strcmp(info->hardware_id.string, "INT3396"); + + kfree(info); + + if (skip) + continue; + + dep = kzalloc(sizeof(struct acpi_dep_data), GFP_KERNEL); + if (!dep) + return; + + dep->master = dep_devices.handles[i]; + dep->slave = adev->handle; + adev->dep_unmet++; + + mutex_lock(&acpi_dep_list_lock); + list_add_tail(&dep->node , &acpi_dep_list); + mutex_unlock(&acpi_dep_list_lock); + } +} + static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, void *not_used, void **return_value) { @@ -2219,6 +2280,7 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, return AE_CTRL_DEPTH; acpi_scan_init_hotplug(device); + acpi_device_dep_initialize(device); out: if (!*return_value) @@ -2339,6 +2401,29 @@ static void acpi_bus_attach(struct acpi_device *device) device->handler->hotplug.notify_online(device); } +void acpi_walk_dep_device_list(acpi_handle handle) +{ + struct acpi_dep_data *dep, *tmp; + struct acpi_device *adev; + + mutex_lock(&acpi_dep_list_lock); + list_for_each_entry_safe(dep, tmp, &acpi_dep_list, node) { + if (dep->master == handle) { + acpi_bus_get_device(dep->slave, &adev); + if (!adev) + continue; + + adev->dep_unmet--; + if (!adev->dep_unmet) + acpi_bus_attach(adev); + list_del(&dep->node); + kfree(dep); + } + } + mutex_unlock(&acpi_dep_list_lock); +} +EXPORT_SYMBOL_GPL(acpi_walk_dep_device_list); + /** * acpi_bus_scan - Add ACPI device node objects in a given namespace scope. * @handle: Root of the namespace scope to scan. diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 05a31b573fc3..8aa9254a387f 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -630,6 +630,7 @@ static int acpi_freeze_begin(void) static int acpi_freeze_prepare(void) { acpi_enable_all_wakeup_gpes(); + acpi_os_wait_events_complete(); enable_irq_wake(acpi_gbl_FADT.sci_interrupt); return 0; } @@ -825,6 +826,7 @@ static void acpi_power_off_prepare(void) /* Prepare to power off the system */ acpi_sleep_prepare(ACPI_STATE_S5); acpi_disable_all_gpes(); + acpi_os_wait_events_complete(); } static void acpi_power_off(void) diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 6d5a6cda0734..93b81523a2fe 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -190,30 +190,24 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header) } } - int __init -acpi_table_parse_entries(char *id, - unsigned long table_size, - int entry_id, - acpi_tbl_entry_handler handler, - unsigned int max_entries) +acpi_parse_entries(char *id, unsigned long table_size, + acpi_tbl_entry_handler handler, + struct acpi_table_header *table_header, + int entry_id, unsigned int max_entries) { - struct acpi_table_header *table_header = NULL; struct acpi_subtable_header *entry; - unsigned int count = 0; + int count = 0; unsigned long table_end; - acpi_size tbl_size; if (acpi_disabled) return -ENODEV; - if (!handler) + if (!id || !handler) return -EINVAL; - if (strncmp(id, ACPI_SIG_MADT, 4) == 0) - acpi_get_table_with_size(id, acpi_apic_instance, &table_header, &tbl_size); - else - acpi_get_table_with_size(id, 0, &table_header, &tbl_size); + if (!table_size) + return -EINVAL; if (!table_header) { pr_warn("%4.4s not present\n", id); @@ -230,9 +224,12 @@ acpi_table_parse_entries(char *id, while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) < table_end) { if (entry->type == entry_id - && (!max_entries || count++ < max_entries)) + && (!max_entries || count < max_entries)) { if (handler(entry, table_end)) - goto err; + return -EINVAL; + + count++; + } /* * If entry->length is 0, break from this loop to avoid @@ -240,22 +237,53 @@ acpi_table_parse_entries(char *id, */ if (entry->length == 0) { pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, entry_id); - goto err; + return -EINVAL; } entry = (struct acpi_subtable_header *) ((unsigned long)entry + entry->length); } + if (max_entries && count > max_entries) { pr_warn("[%4.4s:0x%02x] ignored %i entries of %i found\n", id, entry_id, count - max_entries, count); } - early_acpi_os_unmap_memory((char *)table_header, tbl_size); return count; -err: +} + +int __init +acpi_table_parse_entries(char *id, + unsigned long table_size, + int entry_id, + acpi_tbl_entry_handler handler, + unsigned int max_entries) +{ + struct acpi_table_header *table_header = NULL; + acpi_size tbl_size; + int count; + u32 instance = 0; + + if (acpi_disabled) + return -ENODEV; + + if (!id || !handler) + return -EINVAL; + + if (!strncmp(id, ACPI_SIG_MADT, 4)) + instance = acpi_apic_instance; + + acpi_get_table_with_size(id, instance, &table_header, &tbl_size); + if (!table_header) { + pr_warn("%4.4s not present\n", id); + return -ENODEV; + } + + count = acpi_parse_entries(id, table_size, handler, table_header, + entry_id, max_entries); + early_acpi_os_unmap_memory((char *)table_header, tbl_size); - return -EINVAL; + return count; } int __init diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 371ac12d25b1..dd8ff63ee2b4 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -136,8 +136,7 @@ acpi_extract_package(union acpi_object *package, break; case 'B': size_required += - sizeof(u8 *) + - (element->buffer.length * sizeof(u8)); + sizeof(u8 *) + element->buffer.length; tail_offset += sizeof(u8 *); break; default: @@ -255,7 +254,7 @@ acpi_extract_package(union acpi_object *package, memcpy(tail, element->buffer.pointer, element->buffer.length); head += sizeof(u8 *); - tail += element->buffer.length * sizeof(u8); + tail += element->buffer.length; break; default: /* Should never get here */ diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 807a88a0f394..185a57d13723 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1164,7 +1164,8 @@ static bool acpi_video_device_in_dod(struct acpi_video_device *device) return true; for (i = 0; i < video->attached_count; i++) { - if (video->attached_array[i].bind_info == device) + if ((video->attached_array[i].value.int_val & 0xfff) == + (device->device_id & 0xfff)) return true; } @@ -1680,6 +1681,19 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) printk(KERN_ERR PREFIX "Create sysfs link\n"); } +static void acpi_video_run_bcl_for_osi(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev; + union acpi_object *levels; + + 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)) + kfree(levels); + } + mutex_unlock(&video->device_list_lock); +} + static int acpi_video_bus_register_backlight(struct acpi_video_bus *video) { struct acpi_video_device *dev; @@ -1687,6 +1701,8 @@ static int acpi_video_bus_register_backlight(struct acpi_video_bus *video) if (video->backlight_registered) return 0; + acpi_video_run_bcl_for_osi(video); + if (!acpi_video_verify_backlight_support()) return 0; |