From 115684961a335a1c97074158e8f789118ac8b00d Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 18 Mar 2015 09:41:35 +0100 Subject: GHES: Carve out error queueing in a separate function Make the handler more readable. No functionality change. Signed-off-by: Borislav Petkov diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index e82d097..fe1e41b 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -797,6 +797,32 @@ static void ghes_print_queued_estatus(void) } } +/* Save estatus for further processing in IRQ context */ +static void __process_error(struct ghes *ghes) +{ +#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG + u32 len, node_len; + struct ghes_estatus_node *estatus_node; + struct acpi_hest_generic_status *estatus; + + if (ghes_estatus_cached(ghes->estatus)) + return; + + len = cper_estatus_len(ghes->estatus); + node_len = GHES_ESTATUS_NODE_LEN(len); + + estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, node_len); + if (!estatus_node) + return; + + estatus_node->ghes = ghes; + estatus_node->generic = ghes->generic; + estatus = GHES_ESTATUS_FROM_NODE(estatus_node); + memcpy(estatus, ghes->estatus, len); + llist_add(&estatus_node->llnode, &ghes_estatus_llist); +#endif +} + static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) { struct ghes *ghes, *ghes_global = NULL; @@ -832,32 +858,13 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) } list_for_each_entry_rcu(ghes, &ghes_nmi, list) { -#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG - u32 len, node_len; - struct ghes_estatus_node *estatus_node; - struct acpi_hest_generic_status *estatus; -#endif if (!(ghes->flags & GHES_TO_CLEAR)) continue; -#ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG - if (ghes_estatus_cached(ghes->estatus)) - goto next; - /* Save estatus for further processing in IRQ context */ - len = cper_estatus_len(ghes->estatus); - node_len = GHES_ESTATUS_NODE_LEN(len); - estatus_node = (void *)gen_pool_alloc(ghes_estatus_pool, - node_len); - if (estatus_node) { - estatus_node->ghes = ghes; - estatus_node->generic = ghes->generic; - estatus = GHES_ESTATUS_FROM_NODE(estatus_node); - memcpy(estatus, ghes->estatus, len); - llist_add(&estatus_node->llnode, &ghes_estatus_llist); - } -next: -#endif + + __process_error(ghes); ghes_clear_estatus(ghes); } + #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG irq_work_queue(&ghes_proc_irq_work); #endif -- cgit v0.10.2 From e10be03f603d521d5c8ac0bb0f48e5723ce19d58 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 18 Mar 2015 09:52:39 +0100 Subject: GHES: Carve out the panic functionality ... into another function for more clarity. No functionality change. Signed-off-by: Borislav Petkov diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index fe1e41b..712ed95 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -823,6 +823,18 @@ static void __process_error(struct ghes *ghes) #endif } +static void __ghes_panic(struct ghes *ghes) +{ + oops_begin(); + ghes_print_queued_estatus(); + __ghes_print_estatus(KERN_EMERG, ghes->generic, ghes->estatus); + + /* reboot to log the error! */ + if (panic_timeout == 0) + panic_timeout = ghes_panic_timeout; + panic("Fatal hardware error!"); +} + static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) { struct ghes *ghes, *ghes_global = NULL; @@ -846,16 +858,8 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) if (ret == NMI_DONE) goto out; - if (sev_global >= GHES_SEV_PANIC) { - oops_begin(); - ghes_print_queued_estatus(); - __ghes_print_estatus(KERN_EMERG, ghes_global->generic, - ghes_global->estatus); - /* reboot to log the error! */ - if (panic_timeout == 0) - panic_timeout = ghes_panic_timeout; - panic("Fatal hardware error!"); - } + if (sev_global >= GHES_SEV_PANIC) + __ghes_panic(ghes_global); list_for_each_entry_rcu(ghes, &ghes_nmi, list) { if (!(ghes->flags & GHES_TO_CLEAR)) -- cgit v0.10.2 From 6169ddf846c528509e66a0fe7804393aa330a970 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 18 Mar 2015 09:55:21 +0100 Subject: GHES: Panic right after detection The moment we log an error of panic severity, there's no need to noodle through the ghes_nmi list anymore. So panic instead right then and there. Signed-off-by: Borislav Petkov diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 712ed95..0de3adc 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -837,9 +837,8 @@ static void __ghes_panic(struct ghes *ghes) static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) { - struct ghes *ghes, *ghes_global = NULL; - int sev, sev_global = -1; - int ret = NMI_DONE; + struct ghes *ghes; + int sev, ret = NMI_DONE; raw_spin_lock(&ghes_nmi_lock); list_for_each_entry_rcu(ghes, &ghes_nmi, list) { @@ -847,20 +846,17 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) ghes_clear_estatus(ghes); continue; } + sev = ghes_severity(ghes->estatus->error_severity); - if (sev > sev_global) { - sev_global = sev; - ghes_global = ghes; - } + if (sev >= GHES_SEV_PANIC) + __ghes_panic(ghes); + ret = NMI_HANDLED; } if (ret == NMI_DONE) goto out; - if (sev_global >= GHES_SEV_PANIC) - __ghes_panic(ghes_global); - list_for_each_entry_rcu(ghes, &ghes_nmi, list) { if (!(ghes->flags & GHES_TO_CLEAR)) continue; -- cgit v0.10.2 From 2383844d4850888cfdf6d202563d2ddb4125a4e9 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Wed, 18 Mar 2015 10:12:35 +0100 Subject: GHES: Elliminate double-loop in the NMI handler There's no real need to iterate twice over the HW error sources in the NMI handler. With the previous cleanups, elliminating the second loop is almost trivial. Signed-off-by: Borislav Petkov diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 0de3adc..94a44ba 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -851,25 +851,18 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) if (sev >= GHES_SEV_PANIC) __ghes_panic(ghes); - ret = NMI_HANDLED; - } - - if (ret == NMI_DONE) - goto out; - - list_for_each_entry_rcu(ghes, &ghes_nmi, list) { if (!(ghes->flags & GHES_TO_CLEAR)) continue; __process_error(ghes); ghes_clear_estatus(ghes); + + ret = NMI_HANDLED; } #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG irq_work_queue(&ghes_proc_irq_work); #endif - -out: raw_spin_unlock(&ghes_nmi_lock); return ret; } -- cgit v0.10.2 From 6fe9e7c26a97105645fd24f264f1b94e21aade3e Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 27 Mar 2015 10:05:00 +0100 Subject: GHES: Make NMI handler have a single reader Since GHES sources are global, we theoretically need only a single CPU reading them per NMI instead of a thundering herd of CPUs waiting on a spinlock in NMI context for no reason at all. Do that. Signed-off-by: Jiri Kosina Signed-off-by: Borislav Petkov diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 94a44ba..2bfd53c 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -729,10 +729,10 @@ static struct llist_head ghes_estatus_llist; static struct irq_work ghes_proc_irq_work; /* - * NMI may be triggered on any CPU, so ghes_nmi_lock is used for - * mutual exclusion. + * NMI may be triggered on any CPU, so ghes_in_nmi is used for + * having only one concurrent reader. */ -static DEFINE_RAW_SPINLOCK(ghes_nmi_lock); +static atomic_t ghes_in_nmi = ATOMIC_INIT(0); static LIST_HEAD(ghes_nmi); @@ -840,7 +840,9 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) struct ghes *ghes; int sev, ret = NMI_DONE; - raw_spin_lock(&ghes_nmi_lock); + if (!atomic_add_unless(&ghes_in_nmi, 1, 1)) + return ret; + list_for_each_entry_rcu(ghes, &ghes_nmi, list) { if (ghes_read_estatus(ghes, 1)) { ghes_clear_estatus(ghes); @@ -863,7 +865,7 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) #ifdef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG irq_work_queue(&ghes_proc_irq_work); #endif - raw_spin_unlock(&ghes_nmi_lock); + atomic_dec(&ghes_in_nmi); return ret; } -- cgit v0.10.2 From 82f663277d0db854e8978e5f89fd88f6df75a4a4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 4 May 2015 22:53:22 +0200 Subject: sched / idle: Move the default idle call code to a separate function Move the code under the "use_default" label in cpuidle_idle_call() into a separate (new) function. This just allows the subsequent changes to be more stratightforward. Signed-off-by: Rafael J. Wysocki Reviewed-by: Daniel Lezcano Acked-by: Peter Zijlstra (Intel) diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index fefcb1f..ae7c0be 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -67,6 +67,18 @@ void __weak arch_cpu_idle(void) local_irq_enable(); } +static void default_idle_call(void) +{ + /* + * We can't use the cpuidle framework, let's use the default idle + * routine. + */ + if (current_clr_polling_and_test()) + local_irq_enable(); + else + arch_cpu_idle(); +} + /** * cpuidle_idle_call - the main idle function * @@ -105,8 +117,10 @@ static void cpuidle_idle_call(void) */ rcu_idle_enter(); - if (cpuidle_not_available(drv, dev)) - goto use_default; + if (cpuidle_not_available(drv, dev)) { + default_idle_call(); + goto exit_idle; + } /* * Suspend-to-idle ("freeze") is a system state in which all user space @@ -134,8 +148,10 @@ static void cpuidle_idle_call(void) next_state = cpuidle_select(drv, dev); } /* Fall back to the default arch idle method on errors. */ - if (next_state < 0) - goto use_default; + if (next_state < 0) { + default_idle_call(); + goto exit_idle; + } /* * The idle task must be scheduled, it is pointless to @@ -162,8 +178,10 @@ static void cpuidle_idle_call(void) /* The cpu is no longer idle or about to enter idle. */ idle_set_state(this_rq(), NULL); - if (entered_state == -EBUSY) - goto use_default; + if (entered_state == -EBUSY) { + default_idle_call(); + goto exit_idle; + } /* * Give the governor an opportunity to reflect on the outcome @@ -182,19 +200,6 @@ exit_idle: rcu_idle_exit(); start_critical_timings(); - return; - -use_default: - /* - * We can't use the cpuidle framework, let's use the default - * idle routine. - */ - if (current_clr_polling_and_test()) - local_irq_enable(); - else - arch_cpu_idle(); - - goto exit_idle; } DEFINE_PER_CPU(bool, cpu_dead_idle); -- cgit v0.10.2 From a802ea96454570f3c526dd9d7ad8c706e570444d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 4 May 2015 22:53:28 +0200 Subject: cpuidle: Check the sign of index in cpuidle_reflect() Avoid calling the governor's ->reflect method if the state index passed to cpuidle_reflect() is negative. This allows the analogous check to be dropped from menu_reflect(), so do that too, and ensures that arbitrary error codes can be passed to cpuidle_reflect() as the index with no adverse consequences. Signed-off-by: Rafael J. Wysocki Reviewed-by: Daniel Lezcano Acked-by: Peter Zijlstra (Intel) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 61c417b..3b80b77 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -249,7 +249,7 @@ int cpuidle_enter(struct cpuidle_driver *drv, struct cpuidle_device *dev, */ void cpuidle_reflect(struct cpuidle_device *dev, int index) { - if (cpuidle_curr_governor->reflect) + if (cpuidle_curr_governor->reflect && index >= 0) cpuidle_curr_governor->reflect(dev, index); } diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index b8a5fa1..22e4463 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -367,9 +367,9 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) static void menu_reflect(struct cpuidle_device *dev, int index) { struct menu_device *data = this_cpu_ptr(&menu_devices); + data->last_state_idx = index; - if (index >= 0) - data->needs_update = 1; + data->needs_update = 1; } /** -- cgit v0.10.2 From bcf6ad8a4a3d002e8bc8f6639cdc119168f4e87b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 4 May 2015 22:53:35 +0200 Subject: sched / idle: Eliminate the "reflect" check from cpuidle_idle_call() Since cpuidle_reflect() should only be called if the idle state to enter was selected by cpuidle_select(), there is the "reflect" variable in cpuidle_idle_call() whose value is used to determine whether or not that is the case. However, if the entire code run between the conditional setting "reflect" and the call to cpuidle_reflect() is moved to a separate function, it will be possible to call that new function in both branches of the conditional, in which case cpuidle_reflect() will only need to be called from one of them too and the "reflect" variable won't be necessary any more. This eliminates one check made by cpuidle_idle_call() on the majority of its invocations, so change the code as described. Signed-off-by: Rafael J. Wysocki Reviewed-by: Daniel Lezcano Acked-by: Peter Zijlstra (Intel) diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index ae7c0be..9c919b4 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -79,6 +79,46 @@ static void default_idle_call(void) arch_cpu_idle(); } +static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev, + int next_state) +{ + int entered_state; + + /* Fall back to the default arch idle method on errors. */ + if (next_state < 0) { + default_idle_call(); + return next_state; + } + + /* + * The idle task must be scheduled, it is pointless to go to idle, just + * update no idle residency and return. + */ + if (current_clr_polling_and_test()) { + dev->last_residency = 0; + local_irq_enable(); + return -EBUSY; + } + + /* Take note of the planned idle state. */ + idle_set_state(this_rq(), &drv->states[next_state]); + + /* + * Enter the idle state previously returned by the governor decision. + * This function will block until an interrupt occurs and will take + * care of re-enabling the local interrupts + */ + entered_state = cpuidle_enter(drv, dev, next_state); + + /* The cpu is no longer idle or about to enter idle. */ + idle_set_state(this_rq(), NULL); + + if (entered_state == -EBUSY) + default_idle_call(); + + return entered_state; +} + /** * cpuidle_idle_call - the main idle function * @@ -93,7 +133,6 @@ static void cpuidle_idle_call(void) struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices); struct cpuidle_driver *drv = cpuidle_get_cpu_driver(dev); int next_state, entered_state; - bool reflect; /* * Check if the idle task must be rescheduled. If it is the @@ -138,56 +177,19 @@ static void cpuidle_idle_call(void) goto exit_idle; } - reflect = false; next_state = cpuidle_find_deepest_state(drv, dev); + call_cpuidle(drv, dev, next_state); } else { - reflect = true; /* * Ask the cpuidle framework to choose a convenient idle state. */ next_state = cpuidle_select(drv, dev); - } - /* Fall back to the default arch idle method on errors. */ - if (next_state < 0) { - default_idle_call(); - goto exit_idle; - } - - /* - * The idle task must be scheduled, it is pointless to - * go to idle, just update no idle residency and get - * out of this function - */ - if (current_clr_polling_and_test()) { - dev->last_residency = 0; - entered_state = next_state; - local_irq_enable(); - goto exit_idle; - } - - /* Take note of the planned idle state. */ - idle_set_state(this_rq(), &drv->states[next_state]); - - /* - * Enter the idle state previously returned by the governor decision. - * This function will block until an interrupt occurs and will take - * care of re-enabling the local interrupts - */ - entered_state = cpuidle_enter(drv, dev, next_state); - - /* The cpu is no longer idle or about to enter idle. */ - idle_set_state(this_rq(), NULL); - - if (entered_state == -EBUSY) { - default_idle_call(); - goto exit_idle; - } - - /* - * Give the governor an opportunity to reflect on the outcome - */ - if (reflect) + entered_state = call_cpuidle(drv, dev, next_state); + /* + * Give the governor an opportunity to reflect on the outcome + */ cpuidle_reflect(dev, entered_state); + } exit_idle: __current_set_polling(); -- cgit v0.10.2 From 52352558d288c3dd5aa40f3bbf736fc005e17f7c Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Fri, 1 May 2015 10:34:00 +0200 Subject: cpufreq: pxa: replace typedef pxa_freqs_t by structure typedef is not really useful here. Replace it by structure to improve readability. typedef should only be used in some cases. (See Documentation/CodingStyle Chapter 5 for details). Signed-off-by: Fabian Frederick Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c index e24269a..fcf6e34 100644 --- a/drivers/cpufreq/pxa2xx-cpufreq.c +++ b/drivers/cpufreq/pxa2xx-cpufreq.c @@ -56,7 +56,7 @@ module_param(pxa27x_maxfreq, uint, 0); MODULE_PARM_DESC(pxa27x_maxfreq, "Set the pxa27x maxfreq in MHz" "(typically 624=>pxa270, 416=>pxa271, 520=>pxa272)"); -typedef struct { +struct pxa_freqs { unsigned int khz; unsigned int membus; unsigned int cccr; @@ -64,7 +64,7 @@ typedef struct { unsigned int cclkcfg; int vmin; int vmax; -} pxa_freqs_t; +}; /* Define the refresh period in mSec for the SDRAM and the number of rows */ #define SDRAM_TREF 64 /* standard 64ms SDRAM */ @@ -86,7 +86,7 @@ static unsigned int sdram_rows; /* Use the run mode frequencies for the CPUFREQ_POLICY_PERFORMANCE policy */ #define CCLKCFG CCLKCFG_TURBO | CCLKCFG_FCS -static pxa_freqs_t pxa255_run_freqs[] = +static struct pxa_freqs pxa255_run_freqs[] = { /* CPU MEMBUS CCCR DIV2 CCLKCFG run turbo PXbus SDRAM */ { 99500, 99500, 0x121, 1, CCLKCFG, -1, -1}, /* 99, 99, 50, 50 */ @@ -98,7 +98,7 @@ static pxa_freqs_t pxa255_run_freqs[] = }; /* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */ -static pxa_freqs_t pxa255_turbo_freqs[] = +static struct pxa_freqs pxa255_turbo_freqs[] = { /* CPU MEMBUS CCCR DIV2 CCLKCFG run turbo PXbus SDRAM */ { 99500, 99500, 0x121, 1, CCLKCFG, -1, -1}, /* 99, 99, 50, 50 */ @@ -153,7 +153,7 @@ MODULE_PARM_DESC(pxa255_turbo_table, "Selects the frequency table (0 = run table ((HT) ? CCLKCFG_HALFTURBO : 0) | \ ((T) ? CCLKCFG_TURBO : 0)) -static pxa_freqs_t pxa27x_freqs[] = { +static struct pxa_freqs pxa27x_freqs[] = { {104000, 104000, PXA27x_CCCR(1, 8, 2), 0, CCLKCFG2(1, 0, 1), 900000, 1705000 }, {156000, 104000, PXA27x_CCCR(1, 8, 3), 0, CCLKCFG2(1, 0, 1), 1000000, 1705000 }, {208000, 208000, PXA27x_CCCR(0, 16, 2), 1, CCLKCFG2(0, 0, 1), 1180000, 1705000 }, @@ -171,7 +171,7 @@ extern unsigned get_clk_frequency_khz(int info); #ifdef CONFIG_REGULATOR -static int pxa_cpufreq_change_voltage(pxa_freqs_t *pxa_freq) +static int pxa_cpufreq_change_voltage(struct pxa_freqs *pxa_freq) { int ret = 0; int vmin, vmax; @@ -202,7 +202,7 @@ static void __init pxa_cpufreq_init_voltages(void) } } #else -static int pxa_cpufreq_change_voltage(pxa_freqs_t *pxa_freq) +static int pxa_cpufreq_change_voltage(struct pxa_freqs *pxa_freq) { return 0; } @@ -211,7 +211,7 @@ static void __init pxa_cpufreq_init_voltages(void) { } #endif static void find_freq_tables(struct cpufreq_frequency_table **freq_table, - pxa_freqs_t **pxa_freqs) + struct pxa_freqs **pxa_freqs) { if (cpu_is_pxa25x()) { if (!pxa255_turbo_table) { @@ -270,7 +270,7 @@ static unsigned int pxa_cpufreq_get(unsigned int cpu) static int pxa_set_target(struct cpufreq_policy *policy, unsigned int idx) { struct cpufreq_frequency_table *pxa_freqs_table; - pxa_freqs_t *pxa_freq_settings; + struct pxa_freqs *pxa_freq_settings; unsigned long flags; unsigned int new_freq_cpu, new_freq_mem; unsigned int unused, preset_mdrefr, postset_mdrefr, cclkcfg; @@ -361,7 +361,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) int i; unsigned int freq; struct cpufreq_frequency_table *pxa255_freq_table; - pxa_freqs_t *pxa255_freqs; + struct pxa_freqs *pxa255_freqs; /* try to guess pxa27x cpu */ if (cpu_is_pxa27x()) -- cgit v0.10.2 From 03c229906311f3b7232ce134fdd6405288780ed3 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Fri, 1 May 2015 10:34:01 +0200 Subject: cpufreq: pxa: make pxa_freqs arrays const pxa255_run_freqs and pxa255_turbo_freqs are only read. This patch updates arrays declaration, find_freq_tables() and its callsites. Suggested-by: Joe Perches Signed-off-by: Fabian Frederick Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c index fcf6e34..1d99c97 100644 --- a/drivers/cpufreq/pxa2xx-cpufreq.c +++ b/drivers/cpufreq/pxa2xx-cpufreq.c @@ -86,7 +86,7 @@ static unsigned int sdram_rows; /* Use the run mode frequencies for the CPUFREQ_POLICY_PERFORMANCE policy */ #define CCLKCFG CCLKCFG_TURBO | CCLKCFG_FCS -static struct pxa_freqs pxa255_run_freqs[] = +static const struct pxa_freqs pxa255_run_freqs[] = { /* CPU MEMBUS CCCR DIV2 CCLKCFG run turbo PXbus SDRAM */ { 99500, 99500, 0x121, 1, CCLKCFG, -1, -1}, /* 99, 99, 50, 50 */ @@ -98,7 +98,7 @@ static struct pxa_freqs pxa255_run_freqs[] = }; /* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */ -static struct pxa_freqs pxa255_turbo_freqs[] = +static const struct pxa_freqs pxa255_turbo_freqs[] = { /* CPU MEMBUS CCCR DIV2 CCLKCFG run turbo PXbus SDRAM */ { 99500, 99500, 0x121, 1, CCLKCFG, -1, -1}, /* 99, 99, 50, 50 */ @@ -171,7 +171,7 @@ extern unsigned get_clk_frequency_khz(int info); #ifdef CONFIG_REGULATOR -static int pxa_cpufreq_change_voltage(struct pxa_freqs *pxa_freq) +static int pxa_cpufreq_change_voltage(const struct pxa_freqs *pxa_freq) { int ret = 0; int vmin, vmax; @@ -211,7 +211,7 @@ static void __init pxa_cpufreq_init_voltages(void) { } #endif static void find_freq_tables(struct cpufreq_frequency_table **freq_table, - struct pxa_freqs **pxa_freqs) + const struct pxa_freqs **pxa_freqs) { if (cpu_is_pxa25x()) { if (!pxa255_turbo_table) { @@ -270,7 +270,7 @@ static unsigned int pxa_cpufreq_get(unsigned int cpu) static int pxa_set_target(struct cpufreq_policy *policy, unsigned int idx) { struct cpufreq_frequency_table *pxa_freqs_table; - struct pxa_freqs *pxa_freq_settings; + const struct pxa_freqs *pxa_freq_settings; unsigned long flags; unsigned int new_freq_cpu, new_freq_mem; unsigned int unused, preset_mdrefr, postset_mdrefr, cclkcfg; @@ -361,7 +361,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) int i; unsigned int freq; struct cpufreq_frequency_table *pxa255_freq_table; - struct pxa_freqs *pxa255_freqs; + const struct pxa_freqs *pxa255_freqs; /* try to guess pxa27x cpu */ if (cpu_is_pxa27x()) -- cgit v0.10.2 From 51b63409b75980753f52edb45da7d1b3e2b9405d Mon Sep 17 00:00:00 2001 From: Ajay Thomas Date: Thu, 30 Apr 2015 01:43:23 +0530 Subject: powercap / RAPL: Floor frequency setting in Atom SoC CPU Floor frequency is set in BIOS for newer Atom SoCs. This patch handles configuration of floor frequency for different variants of Atom SoCs appropriately and ensures configuration of floor frequency is not done from driver for these newer Atom SoCs. Since address of the register for configuring floor frequency might change for different Atom SoCs, this patch also prevents potential overwriting of wrong registers. Reviewed-by: Jacob Pan Signed-off-by: Ajay Thomas Signed-off-by: Rafael J. Wysocki diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index fd24323..f332628 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -187,6 +187,7 @@ struct rapl_package { }; struct rapl_defaults { + u8 floor_freq_reg_addr; int (*check_unit)(struct rapl_package *rp, int cpu); void (*set_floor_freq)(struct rapl_domain *rd, bool mode); u64 (*compute_time_window)(struct rapl_package *rp, u64 val, @@ -196,7 +197,8 @@ struct rapl_defaults { static struct rapl_defaults *rapl_defaults; /* Sideband MBI registers */ -#define IOSF_CPU_POWER_BUDGET_CTL (0x2) +#define IOSF_CPU_POWER_BUDGET_CTL_BYT (0x2) +#define IOSF_CPU_POWER_BUDGET_CTL_TNG (0xdf) #define PACKAGE_PLN_INT_SAVED BIT(0) #define MAX_PRIM_NAME (32) @@ -358,7 +360,8 @@ static int set_domain_enable(struct powercap_zone *power_zone, bool mode) get_online_cpus(); rapl_write_data_raw(rd, PL1_ENABLE, mode); - rapl_defaults->set_floor_freq(rd, mode); + if (rapl_defaults->set_floor_freq) + rapl_defaults->set_floor_freq(rd, mode); put_online_cpus(); return 0; @@ -979,16 +982,22 @@ static void set_floor_freq_atom(struct rapl_domain *rd, bool enable) static u32 power_ctrl_orig_val; u32 mdata; + if (!rapl_defaults->floor_freq_reg_addr) { + pr_err("Invalid floor frequency config register\n"); + return; + } + if (!power_ctrl_orig_val) iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_PMC_READ, - IOSF_CPU_POWER_BUDGET_CTL, &power_ctrl_orig_val); + rapl_defaults->floor_freq_reg_addr, + &power_ctrl_orig_val); mdata = power_ctrl_orig_val; if (enable) { mdata &= ~(0x7f << 8); mdata |= 1 << 8; } iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_PMC_WRITE, - IOSF_CPU_POWER_BUDGET_CTL, mdata); + rapl_defaults->floor_freq_reg_addr, mdata); } static u64 rapl_compute_time_window_core(struct rapl_package *rp, u64 value, @@ -1029,6 +1038,7 @@ static u64 rapl_compute_time_window_atom(struct rapl_package *rp, u64 value, } static const struct rapl_defaults rapl_defaults_core = { + .floor_freq_reg_addr = 0, .check_unit = rapl_check_unit_core, .set_floor_freq = set_floor_freq_default, .compute_time_window = rapl_compute_time_window_core, @@ -1041,12 +1051,34 @@ static const struct rapl_defaults rapl_defaults_hsw_server = { .dram_domain_energy_unit = 15300, }; -static const struct rapl_defaults rapl_defaults_atom = { +static const struct rapl_defaults rapl_defaults_byt = { + .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_BYT, + .check_unit = rapl_check_unit_atom, + .set_floor_freq = set_floor_freq_atom, + .compute_time_window = rapl_compute_time_window_atom, +}; + +static const struct rapl_defaults rapl_defaults_tng = { + .floor_freq_reg_addr = IOSF_CPU_POWER_BUDGET_CTL_TNG, .check_unit = rapl_check_unit_atom, .set_floor_freq = set_floor_freq_atom, .compute_time_window = rapl_compute_time_window_atom, }; +static const struct rapl_defaults rapl_defaults_ann = { + .floor_freq_reg_addr = 0, + .check_unit = rapl_check_unit_atom, + .set_floor_freq = NULL, + .compute_time_window = rapl_compute_time_window_atom, +}; + +static const struct rapl_defaults rapl_defaults_cht = { + .floor_freq_reg_addr = 0, + .check_unit = rapl_check_unit_atom, + .set_floor_freq = NULL, + .compute_time_window = rapl_compute_time_window_atom, +}; + #define RAPL_CPU(_model, _ops) { \ .vendor = X86_VENDOR_INTEL, \ .family = 6, \ @@ -1057,7 +1089,7 @@ static const struct rapl_defaults rapl_defaults_atom = { static const struct x86_cpu_id rapl_ids[] __initconst = { RAPL_CPU(0x2a, rapl_defaults_core),/* Sandy Bridge */ RAPL_CPU(0x2d, rapl_defaults_core),/* Sandy Bridge EP */ - RAPL_CPU(0x37, rapl_defaults_atom),/* Valleyview */ + RAPL_CPU(0x37, rapl_defaults_byt),/* Valleyview */ RAPL_CPU(0x3a, rapl_defaults_core),/* Ivy Bridge */ RAPL_CPU(0x3c, rapl_defaults_core),/* Haswell */ RAPL_CPU(0x3d, rapl_defaults_core),/* Broadwell */ @@ -1065,10 +1097,10 @@ static const struct x86_cpu_id rapl_ids[] __initconst = { RAPL_CPU(0x4f, rapl_defaults_hsw_server),/* Broadwell servers */ RAPL_CPU(0x45, rapl_defaults_core),/* Haswell ULT */ RAPL_CPU(0x4E, rapl_defaults_core),/* Skylake */ - RAPL_CPU(0x4C, rapl_defaults_atom),/* Braswell */ - RAPL_CPU(0x4A, rapl_defaults_atom),/* Tangier */ + RAPL_CPU(0x4C, rapl_defaults_cht),/* Braswell/Cherryview */ + RAPL_CPU(0x4A, rapl_defaults_tng),/* Tangier */ RAPL_CPU(0x56, rapl_defaults_core),/* Future Xeon */ - RAPL_CPU(0x5A, rapl_defaults_atom),/* Annidale */ + RAPL_CPU(0x5A, rapl_defaults_ann),/* Annidale */ {} }; MODULE_DEVICE_TABLE(x86cpu, rapl_ids); -- cgit v0.10.2 From 0a95e630b49a30c176daeff39ac2e90f1231604b Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 27 Apr 2015 10:51:05 +0100 Subject: cpufreq: arm_big_little: check if the frequency is set correctly The actual frequency is set through "clk_change_rate" which is void function. If the underlying hardware fails and returns error, the error is lost in the clk layer. In order to track such failures, we need to read back the frequency(just the cached value as clk_recalc called after clk->ops->set_rate gets the frequency) This patch adds check to see if the frequency is set correctly or if they were any hardware failures and sends the appropriate errors to the cpufreq core. Reviewed-by: Michael Turquette Signed-off-by: Sudeep Holla Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index e1a6ba6..f65e19f 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -186,6 +186,15 @@ bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate) mutex_unlock(&cluster_lock[old_cluster]); } + /* + * FIXME: clk_set_rate has to handle the case where clk_change_rate + * can fail due to hardware or firmware issues. Until the clk core + * layer is fixed, we can check here. In most of the cases we will + * be reading only the cached value anyway. This needs to be removed + * once clk core is fixed. + */ + if (bL_cpufreq_get_rate(cpu) != new_rate) + return -EIO; return 0; } -- cgit v0.10.2 From b904f5cce1aeb9a9ee5ca7f1a31c32e1f3487c8b Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 27 Apr 2015 10:51:06 +0100 Subject: cpufreq: arm_big_little: remove unused cpu-cluster. clock name The "cpu-cluster." used to get the cluster clock is not used by any platform. Moreover __of_clk_get_by_name used in clk_get return error if the "clock-names" in the DT doesn't match this string. When using DT, it's not compulsory to specify the clock name unless there are multiple clock input entries in the consumer. This patch removes the unused clock string from the driver. Acked-by: Viresh Kumar Signed-off-by: Sudeep Holla Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index f65e19f..e4d75ca 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -331,7 +331,6 @@ static void put_cluster_clk_and_freq_table(struct device *cpu_dev) static int _get_cluster_clk_and_freq_table(struct device *cpu_dev) { u32 cluster = raw_cpu_to_cluster(cpu_dev->id); - char name[14] = "cpu-cluster."; int ret; if (freq_table[cluster]) @@ -351,8 +350,7 @@ static int _get_cluster_clk_and_freq_table(struct device *cpu_dev) goto free_opp_table; } - name[12] = cluster + '0'; - clk[cluster] = clk_get(cpu_dev, name); + clk[cluster] = clk_get(cpu_dev, NULL); if (!IS_ERR(clk[cluster])) { dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n", __func__, clk[cluster], freq_table[cluster], -- cgit v0.10.2 From 4055fad34086dcf5229c43846e0a3cf0fb3692e3 Mon Sep 17 00:00:00 2001 From: Doug Smythies Date: Sat, 11 Apr 2015 21:10:26 -0700 Subject: intel_pstate: Add tsc collection and keep previous target pstate The intel_pstate driver is difficult to debug and investigate without tsc. Also, it is likely use of tsc, and some version of C0 percentage, will be re-introdcued in futute. There have also been occasions where it is desirebale to know, and confirm, the previous target pstate. This patch brings back tsc, adds previous target pstate, and adds both to the trace data collection. Signed-off-by: Doug Smythies Acked-by: Kristen Carlson Accardi Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 6414661..e833be4 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -68,6 +68,7 @@ struct sample { int32_t core_pct_busy; u64 aperf; u64 mperf; + u64 tsc; int freq; ktime_t time; }; @@ -109,6 +110,7 @@ struct cpudata { ktime_t last_sample_time; u64 prev_aperf; u64 prev_mperf; + u64 prev_tsc; struct sample sample; }; @@ -756,23 +758,28 @@ static inline void intel_pstate_sample(struct cpudata *cpu) { u64 aperf, mperf; unsigned long flags; + u64 tsc; local_irq_save(flags); rdmsrl(MSR_IA32_APERF, aperf); rdmsrl(MSR_IA32_MPERF, mperf); + tsc = native_read_tsc(); local_irq_restore(flags); cpu->last_sample_time = cpu->sample.time; cpu->sample.time = ktime_get(); cpu->sample.aperf = aperf; cpu->sample.mperf = mperf; + cpu->sample.tsc = tsc; cpu->sample.aperf -= cpu->prev_aperf; cpu->sample.mperf -= cpu->prev_mperf; + cpu->sample.tsc -= cpu->prev_tsc; intel_pstate_calc_busy(cpu); cpu->prev_aperf = aperf; cpu->prev_mperf = mperf; + cpu->prev_tsc = tsc; } static inline void intel_hwp_set_sample_time(struct cpudata *cpu) @@ -837,6 +844,10 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu) int32_t busy_scaled; struct _pid *pid; signed int ctl; + int from; + struct sample *sample; + + from = cpu->pstate.current_pstate; pid = &cpu->pid; busy_scaled = intel_pstate_get_scaled_busy(cpu); @@ -845,6 +856,16 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu) /* Negative values of ctl increase the pstate and vice versa */ intel_pstate_set_pstate(cpu, cpu->pstate.current_pstate - ctl); + + sample = &cpu->sample; + trace_pstate_sample(fp_toint(sample->core_pct_busy), + fp_toint(busy_scaled), + from, + cpu->pstate.current_pstate, + sample->mperf, + sample->aperf, + sample->tsc, + sample->freq); } static void intel_hwp_timer_func(unsigned long __data) @@ -858,21 +879,11 @@ static void intel_hwp_timer_func(unsigned long __data) static void intel_pstate_timer_func(unsigned long __data) { struct cpudata *cpu = (struct cpudata *) __data; - struct sample *sample; intel_pstate_sample(cpu); - sample = &cpu->sample; - intel_pstate_adjust_busy_pstate(cpu); - trace_pstate_sample(fp_toint(sample->core_pct_busy), - fp_toint(intel_pstate_get_scaled_busy(cpu)), - cpu->pstate.current_pstate, - sample->mperf, - sample->aperf, - sample->freq); - intel_pstate_set_sample_time(cpu); } diff --git a/include/trace/events/power.h b/include/trace/events/power.h index d19840b..630d1e5 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -42,45 +42,54 @@ TRACE_EVENT(pstate_sample, TP_PROTO(u32 core_busy, u32 scaled_busy, - u32 state, + u32 from, + u32 to, u64 mperf, u64 aperf, + u64 tsc, u32 freq ), TP_ARGS(core_busy, scaled_busy, - state, + from, + to, mperf, aperf, + tsc, freq ), TP_STRUCT__entry( __field(u32, core_busy) __field(u32, scaled_busy) - __field(u32, state) + __field(u32, from) + __field(u32, to) __field(u64, mperf) __field(u64, aperf) + __field(u64, tsc) __field(u32, freq) - - ), + ), TP_fast_assign( __entry->core_busy = core_busy; __entry->scaled_busy = scaled_busy; - __entry->state = state; + __entry->from = from; + __entry->to = to; __entry->mperf = mperf; __entry->aperf = aperf; + __entry->tsc = tsc; __entry->freq = freq; ), - TP_printk("core_busy=%lu scaled=%lu state=%lu mperf=%llu aperf=%llu freq=%lu ", + TP_printk("core_busy=%lu scaled=%lu from=%lu to=%lu mperf=%llu aperf=%llu tsc=%llu freq=%lu ", (unsigned long)__entry->core_busy, (unsigned long)__entry->scaled_busy, - (unsigned long)__entry->state, + (unsigned long)__entry->from, + (unsigned long)__entry->to, (unsigned long long)__entry->mperf, (unsigned long long)__entry->aperf, + (unsigned long long)__entry->tsc, (unsigned long)__entry->freq ) -- cgit v0.10.2 From 5c53b262c861dc99aefb215eec579ae438d64fdd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 5 May 2015 15:43:07 +0200 Subject: ACPI / property: Refine consistency check for PRP0001 Refine the check for the presence of the "compatible" property if the PRP0001 device ID is present in the device's list of ACPI/PNP IDs to also print the message if _DSD is missing entirely or the format of it is incorrect. One special case to take into accout is that the "compatible" property need not be provided for devices having the PRP0001 device ID in their lists of ACPI/PNP IDs if they are ancestors of PRP0001 devices with the "compatible" property present. This is to cover heriarchies of device objects where the kernel is only supposed to use a struct device representation for the topmost one and the others represent, for example, functional blocks of a composite device. While at it, reduce the log level of the message to "info" and reduce the log level of the "broken _DSD" message to "debug" (noise reduction). Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 0d08373..76075ee 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -79,50 +79,51 @@ static bool acpi_properties_format_valid(const union acpi_object *properties) static void acpi_init_of_compatible(struct acpi_device *adev) { const union acpi_object *of_compatible; - struct acpi_hardware_id *hwid; - bool acpi_of = false; int ret; - /* - * Check if the special PRP0001 ACPI ID is present and in that - * case we fill in Device Tree compatible properties for this - * device. - */ - list_for_each_entry(hwid, &adev->pnp.ids, list) { - if (!strcmp(hwid->id, "PRP0001")) { - acpi_of = true; - break; - } - } - - if (!acpi_of) - return; - ret = acpi_dev_get_property_array(adev, "compatible", ACPI_TYPE_STRING, &of_compatible); if (ret) { ret = acpi_dev_get_property(adev, "compatible", ACPI_TYPE_STRING, &of_compatible); if (ret) { - acpi_handle_warn(adev->handle, - "PRP0001 requires compatible property\n"); + if (adev->parent + && adev->parent->flags.of_compatible_ok) + goto out; + return; } } adev->data.of_compatible = of_compatible; + + out: + adev->flags.of_compatible_ok = 1; } void acpi_init_properties(struct acpi_device *adev) { struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER }; + bool acpi_of = false; + struct acpi_hardware_id *hwid; const union acpi_object *desc; acpi_status status; int i; + /* + * Check if the special PRP0001 ACPI ID is present and in that case we + * fill in Device Tree compatible properties for this device. + */ + list_for_each_entry(hwid, &adev->pnp.ids, list) { + if (!strcmp(hwid->id, "PRP0001")) { + acpi_of = true; + break; + } + } + status = acpi_evaluate_object_typed(adev->handle, "_DSD", NULL, &buf, ACPI_TYPE_PACKAGE); if (ACPI_FAILURE(status)) - return; + goto out; desc = buf.pointer; if (desc->package.count % 2) @@ -156,13 +157,20 @@ void acpi_init_properties(struct acpi_device *adev) adev->data.pointer = buf.pointer; adev->data.properties = properties; - acpi_init_of_compatible(adev); - return; + if (acpi_of) + acpi_init_of_compatible(adev); + + goto out; } fail: - dev_warn(&adev->dev, "Returned _DSD data is not valid, skipping\n"); + dev_dbg(&adev->dev, "Returned _DSD data is not valid, skipping\n"); ACPI_FREE(buf.pointer); + + out: + if (acpi_of && !adev->flags.of_compatible_ok) + acpi_handle_info(adev->handle, + "PRP0001 requires 'compatible' property\n"); } void acpi_free_properties(struct acpi_device *adev) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 8de4fa9..da07997 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -208,7 +208,8 @@ struct acpi_device_flags { u32 visited:1; u32 hotplug_notify:1; u32 is_dock_station:1; - u32 reserved:23; + u32 of_compatible_ok:1; + u32 reserved:22; }; /* File System */ -- cgit v0.10.2 From bbee06d067ba37be3ca3554a401aea49df715c16 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 2 May 2015 17:14:49 +0200 Subject: PNP / ACPI: use u8 instead of int in acpi_resource_extended_irq context acpi_resource_extented_irq variables are all u8. Use that type for triggering, polarity and shareable. This fixes gcc warnings of type "conversion to u8 from int may alter its value" Signed-off-by: Fabian Frederick Signed-off-by: Rafael J. Wysocki diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index ff0356f..23a08d1 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -28,8 +28,8 @@ #include "../base.h" #include "pnpacpi.h" -static void decode_irq_flags(struct pnp_dev *dev, int flags, int *triggering, - int *polarity, int *shareable) +static void decode_irq_flags(struct pnp_dev *dev, int flags, u8 *triggering, + u8 *polarity, u8 *shareable) { switch (flags & (IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE)) { @@ -654,7 +654,7 @@ static void pnpacpi_encode_irq(struct pnp_dev *dev, struct resource *p) { struct acpi_resource_irq *irq = &resource->data.irq; - int triggering, polarity, shareable; + u8 triggering, polarity, shareable; if (!pnp_resource_enabled(p)) { irq->interrupt_count = 0; @@ -683,7 +683,7 @@ static void pnpacpi_encode_ext_irq(struct pnp_dev *dev, struct resource *p) { struct acpi_resource_extended_irq *extended_irq = &resource->data.extended_irq; - int triggering, polarity, shareable; + u8 triggering, polarity, shareable; if (!pnp_resource_enabled(p)) { extended_irq->interrupt_count = 0; -- cgit v0.10.2 From fac69a2bc8278e209cd6e2ef3585f751cc034b00 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 2 May 2015 17:14:50 +0200 Subject: PNP / ACPI: use unsigned int in pnpacpi_encode_resources() use unsigned int for port, irq, dma and mem used for pnp_get_resource() This fixes gcc warnings of type "conversion to unsigned int from int may change the sign of the result" Signed-off-by: Fabian Frederick Signed-off-by: Rafael J. Wysocki diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 23a08d1..0579649 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -873,7 +873,7 @@ int pnpacpi_encode_resources(struct pnp_dev *dev, struct acpi_buffer *buffer) /* pnpacpi_build_resource_template allocates extra mem */ int res_cnt = (buffer->length - 1) / sizeof(struct acpi_resource) - 1; struct acpi_resource *resource = buffer->pointer; - int port = 0, irq = 0, dma = 0, mem = 0; + unsigned int port = 0, irq = 0, dma = 0, mem = 0; pnp_dbg(&dev->dev, "encode %d resources\n", res_cnt); while (i < res_cnt) { -- cgit v0.10.2 From bbf55ae19f55cc0ec0d75cfcfa3553331177b518 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 1 May 2015 11:27:01 +0100 Subject: ACPI / proc: make ACPI_PROCFS_POWER X86 only The ACPI procfs power interface is initialized by compilation units that are only selectable on X86 platforms. Since its usage is deprecated and it cannot even be used on platforms other than X86 it should be compiled in only on X86 platforms. This patch makes CONFIG_ACPI_PROCFS_POWER dependent on X86, so that other architectures are prevented from compiling it in for no purpose. Signed-off-by: Lorenzo Pieralisi Acked-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ab2cbb5..16da185 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -62,7 +62,7 @@ config ACPI_SLEEP config ACPI_PROCFS_POWER bool "Deprecated power /proc/acpi directories" - depends on PROC_FS + depends on X86 && PROC_FS help For backwards compatibility, this option allows deprecated power /proc/acpi/ directories to exist, even when -- cgit v0.10.2 From f133d08a3986efeff24069616ac739fc83c8ec9f Mon Sep 17 00:00:00 2001 From: Wang Long Date: Tue, 5 May 2015 01:22:26 +0000 Subject: Documentation: cpufreq: delete duplicate description of sysfs interface 'scaling_driver' The file 'Documentation/cpu-freq/user-guide.txt' has duplicate description of sysfs interface 'scaling_driver'. [first] scaling_driver : this file shows what cpufreq driver is used to set the frequency on this CPU [second] scaling_driver : Hardware driver for cpufreq. Although this does not affect anything, I think we should only have one. so delete the second one because the first one is described in more detail. Signed-off-by: Wang Long Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/cpu-freq/user-guide.txt b/Documentation/cpu-freq/user-guide.txt index ff2f283..109e97b 100644 --- a/Documentation/cpu-freq/user-guide.txt +++ b/Documentation/cpu-freq/user-guide.txt @@ -196,8 +196,6 @@ affected_cpus : List of Online CPUs that require software related_cpus : List of Online + Offline CPUs that need software coordination of frequency. -scaling_driver : Hardware driver for cpufreq. - scaling_cur_freq : Current frequency of the CPU as determined by the governor and cpufreq core, in KHz. This is the frequency the kernel thinks the CPU runs -- cgit v0.10.2 From 50e9c852130e86b10e9098227d4153b2bf997611 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Feb 2015 17:02:03 +0530 Subject: cpufreq: Add doc style comment about cpufreq_cpu_{get|put}() This clearly states what the code inside these routines is doing and how these must be used. Signed-off-by: Viresh Kumar Acked-by: Saravana Kannan Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 8ae655c..5ff57b0 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -207,6 +207,23 @@ struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu) return per_cpu(cpufreq_cpu_data, cpu); } +/** + * cpufreq_cpu_get: returns policy for a cpu and marks it busy. + * + * @cpu: cpu to find policy for. + * + * This returns policy for 'cpu', returns NULL if it doesn't exist. + * It also increments the kobject reference count to mark it busy and so would + * require a corresponding call to cpufreq_cpu_put() to decrement it back. + * If corresponding call cpufreq_cpu_put() isn't made, the policy wouldn't be + * freed as that depends on the kobj count. + * + * It also takes a read-lock of 'cpufreq_rwsem' and doesn't put it back if a + * valid policy is found. This is done to make sure the driver doesn't get + * unregistered while the policy is being used. + * + * Return: A valid policy on success, otherwise NULL on failure. + */ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) { struct cpufreq_policy *policy = NULL; @@ -237,6 +254,16 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) } EXPORT_SYMBOL_GPL(cpufreq_cpu_get); +/** + * cpufreq_cpu_put: Decrements the usage count of a policy + * + * @policy: policy earlier returned by cpufreq_cpu_get(). + * + * This decrements the kobject reference count incremented earlier by calling + * cpufreq_cpu_get(). + * + * It also drops the read-lock of 'cpufreq_rwsem' taken at cpufreq_cpu_get(). + */ void cpufreq_cpu_put(struct cpufreq_policy *policy) { kobject_put(&policy->kobj); -- cgit v0.10.2 From 23faf0b7430f1f19cf033f4ee1588ab86a026b60 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Feb 2015 17:02:04 +0530 Subject: cpufreq: Merge __cpufreq_add_dev() and cpufreq_add_dev() cpufreq_add_dev() is an unnecessary wrapper over __cpufreq_add_dev(). Merge them. Signed-off-by: Viresh Kumar Acked-by: Saravana Kannan Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 5ff57b0..ee3d920 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1125,7 +1125,16 @@ static int update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu, return 0; } -static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) +/** + * cpufreq_add_dev - add a CPU device + * + * Adds the cpufreq interface for a CPU device. + * + * The Oracle says: try running cpufreq registration/unregistration concurrently + * with with cpu hotplugging and all hell will break loose. Tried to clean this + * mess up, but more thorough testing is needed. - Mathieu + */ +static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) { unsigned int j, cpu = dev->id; int ret = -ENOMEM; @@ -1336,20 +1345,6 @@ nomem_out: return ret; } -/** - * cpufreq_add_dev - add a CPU device - * - * Adds the cpufreq interface for a CPU device. - * - * The Oracle says: try running cpufreq registration/unregistration concurrently - * with with cpu hotplugging and all hell will break loose. Tried to clean this - * mess up, but more thorough testing is needed. - Mathieu - */ -static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) -{ - return __cpufreq_add_dev(dev, sif); -} - static int __cpufreq_remove_dev_prepare(struct device *dev, struct subsys_interface *sif) { @@ -2331,7 +2326,7 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb, if (dev) { switch (action & ~CPU_TASKS_FROZEN) { case CPU_ONLINE: - __cpufreq_add_dev(dev, NULL); + cpufreq_add_dev(dev, NULL); break; case CPU_DOWN_PREPARE: @@ -2343,7 +2338,7 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb, break; case CPU_DOWN_FAILED: - __cpufreq_add_dev(dev, NULL); + cpufreq_add_dev(dev, NULL); break; } } -- cgit v0.10.2 From 1b947c904c4831d787c1f17a6a6cd40c7144ba85 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Feb 2015 17:02:05 +0530 Subject: cpufreq: Throw warning when we try to get policy for an invalid CPU Simply returning here with an error is not enough. It shouldn't be allowed at all to try calling cpufreq_cpu_get() for an invalid CPU. Add a WARN here to make it clear that it wouldn't be acceptable at all. Signed-off-by: Viresh Kumar Acked-by: Saravana Kannan Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index ee3d920..48ca076 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -229,7 +229,7 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) struct cpufreq_policy *policy = NULL; unsigned long flags; - if (cpu >= nr_cpu_ids) + if (WARN_ON(cpu >= nr_cpu_ids)) return NULL; if (!down_read_trylock(&cpufreq_rwsem)) -- cgit v0.10.2 From bb29ae152e3b9d79ca35e41945a55b616e91b5b9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Feb 2015 17:02:06 +0530 Subject: cpufreq: Keep a single path for adding managed CPUs There are two cases when we may try to add CPUs we're already handling: - On boot, the first cpu has marked all policy->cpus managed and so we will find policy for all other policy->cpus later on. - When a managed cpu is hotplugged out and later brought back in. Currently, separate paths and checks take care of the two. While the first one is detected by testing cpu against 'policy->cpus', the other one is detected by testing cpu against 'policy->related_cpus'. We can handle them both via a single path and there is no need to do special checking for the first one. Signed-off-by: Viresh Kumar Acked-by: Saravana Kannan [ rjw: Changelog, comments ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 48ca076..497935a 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -992,6 +992,10 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, int ret = 0; unsigned long flags; + /* Has this CPU been taken care of already? */ + if (cpumask_test_cpu(cpu, policy->cpus)) + return 0; + if (has_target()) { ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP); if (ret) { @@ -1147,16 +1151,10 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) pr_debug("adding CPU %u\n", cpu); - /* check whether a different CPU already registered this - * CPU because it is in the same boat. */ - policy = cpufreq_cpu_get_raw(cpu); - if (unlikely(policy)) - return 0; - if (!down_read_trylock(&cpufreq_rwsem)) return 0; - /* Check if this cpu was hot-unplugged earlier and has siblings */ + /* Check if this CPU already has a policy to manage it */ read_lock_irqsave(&cpufreq_driver_lock, flags); for_each_policy(policy) { if (cpumask_test_cpu(cpu, policy->related_cpus)) { -- cgit v0.10.2 From 303ae7230751219f43115615a2b79669f8ba53cf Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 19 Feb 2015 17:02:07 +0530 Subject: cpufreq: Clear policy->cpus even for the last CPU We clear policy->cpus mask while CPUs are hotplugged out. We do it for all CPUs except the last CPU of the policy. I don't remember what the rationale behind that was, but I couldn't think of anything that will break if we remove this conditional clearing and always clear policy->cpus. The benefit we get out of it is, we can know if a policy is active or not by checking if this field is empty or not. That will be used by later commits. Signed-off-by: Viresh Kumar Acked-by: Saravana Kannan Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 497935a..8cf0c0e 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1430,9 +1430,7 @@ static int __cpufreq_remove_dev_finish(struct device *dev, down_write(&policy->rwsem); cpus = cpumask_weight(policy->cpus); - - if (cpus > 1) - cpumask_clear_cpu(cpu, policy->cpus); + cpumask_clear_cpu(cpu, policy->cpus); up_write(&policy->rwsem); /* If cpu is last user of policy, free policy */ -- cgit v0.10.2 From b6ec94520c5b944da9e045e173ddb453ecf42da3 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Wed, 6 May 2015 15:26:56 -0700 Subject: PM / wakeup: validate wakeup source before activating it. A rogue wakeup source not registered in wakeup_sources list is not visible from wakeup_sources_stats_show. Check if the wakeup source is registered properly by looking at the timer struct. Signed-off-by: Jin Qian Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 7726200..7b5ad9a 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -351,6 +351,20 @@ int device_set_wakeup_enable(struct device *dev, bool enable) } EXPORT_SYMBOL_GPL(device_set_wakeup_enable); +/** + * wakeup_source_not_registered - validate the given wakeup source. + * @ws: Wakeup source to be validated. + */ +static bool wakeup_source_not_registered(struct wakeup_source *ws) +{ + /* + * Use timer struct to check if the given source is initialized + * by wakeup_source_add. + */ + return ws->timer.function != pm_wakeup_timer_fn || + ws->timer.data != (unsigned long)ws; +} + /* * The functions below use the observation that each wakeup event starts a * period in which the system should not be suspended. The moment this period @@ -391,6 +405,10 @@ static void wakeup_source_activate(struct wakeup_source *ws) { unsigned int cec; + if (WARN_ONCE(wakeup_source_not_registered(ws), + "unregistered wakeup source\n")) + return; + /* * active wakeup source should bring the system * out of PM_SUSPEND_FREEZE state -- cgit v0.10.2 From 6656bde5ec868d89cc803539f9edf85a89497b6a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2015 22:41:05 +0200 Subject: ACPI / PM: Drop stale comment from acpi_power_transition() An old comment in acpi_power_transition() indicates that support for ordering power resources needs to be added, but the current code handles that already. Drop the comment to avoid confusion. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index e0bcfb6..59a6bf7 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -710,8 +710,6 @@ int acpi_power_transition(struct acpi_device *device, int state) || (device->power.state > ACPI_STATE_D3_COLD)) return -ENODEV; - /* TBD: Resources must be ordered. */ - /* * First we reference all power resources required in the target list * (e.g. so the device doesn't lose power while transitioning). Then, -- cgit v0.10.2 From 7312280bd2ad9df1bcca236c5614091a0bd1504c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 9 May 2015 21:50:32 +0200 Subject: cpuidle: Fix the kerneldoc comment for cpuidle_enter_state() The kerneldoc comment for cpuidle_enter_state() doesn't match the function's header any more, so fix it. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 3b80b77..597f884 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -150,7 +150,7 @@ int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev) * cpuidle_enter_state - enter the state and update stats * @dev: cpuidle device for this cpu * @drv: cpuidle driver for this cpu - * @next_state: index into drv->states of the state to enter + * @index: index into the states table in @drv of the state to enter */ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) -- cgit v0.10.2 From 0dd23f94251f49da99a6cbfb22418b2d757d77d6 Mon Sep 17 00:00:00 2001 From: Joe Konno Date: Tue, 12 May 2015 07:59:42 -0700 Subject: intel_pstate: set BYT MSR with wrmsrl_on_cpu() Commit 007bea098b86 (intel_pstate: Add setting voltage value for baytrail P states.) introduced byt_set_pstate() with the assumption that it would always be run by the CPU whose MSR is to be written by it. It turns out, however, that is not always the case in practice, so modify byt_set_pstate() to enforce the MSR write done by it to always happen on the right CPU. Fixes: 007bea098b86 (intel_pstate: Add setting voltage value for baytrail P states.) Signed-off-by: Joe Konno Acked-by: Kristen Carlson Accardi Cc: 3.14+ # 3.14+ Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index e833be4..2f329b4 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -537,7 +537,7 @@ static void byt_set_pstate(struct cpudata *cpudata, int pstate) val |= vid; - wrmsrl(MSR_IA32_PERF_CTL, val); + wrmsrl_on_cpu(cpudata->cpu, MSR_IA32_PERF_CTL, val); } #define BYT_BCLK_FREQS 5 -- cgit v0.10.2 From a921504588cfe0e11d9306536bdf131b5b957fd3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 5 May 2015 23:42:25 +0200 Subject: PM / sleep: Refine diagnostic messages in enter_state() Some of the system suspend diagnostic messages related to suspend-to-idle refer to it as "freeze sleep" or "freeze state" while the others say "suspend-to-idle". To reduce the possible confusion that may result from that, refine the former either to say "suspend to idle" too or to make it clearer that what is printed is a state string written to /sys/power/state ("mem", "standby", or "freeze"). Signed-off-by: Rafael J. Wysocki diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 8d7a1ef..274371a 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -468,7 +468,7 @@ static int enter_state(suspend_state_t state) if (state == PM_SUSPEND_FREEZE) { #ifdef CONFIG_PM_DEBUG if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) { - pr_warning("PM: Unsupported test mode for freeze state," + pr_warning("PM: Unsupported test mode for suspend to idle," "please choose none/freezer/devices/platform.\n"); return -EAGAIN; } @@ -488,7 +488,7 @@ static int enter_state(suspend_state_t state) printk("done.\n"); trace_suspend_resume(TPS("sync_filesystems"), 0, false); - pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); + pr_debug("PM: Preparing system for sleep (%s)\n", pm_states[state]); error = suspend_prepare(state); if (error) goto Unlock; @@ -497,7 +497,7 @@ static int enter_state(suspend_state_t state) goto Finish; trace_suspend_resume(TPS("suspend_enter"), state, false); - pr_debug("PM: Entering %s sleep\n", pm_states[state]); + pr_debug("PM: Suspending system (%s)\n", pm_states[state]); pm_restrict_gfp_mask(); error = suspend_devices_and_enter(state); pm_restore_gfp_mask(); -- cgit v0.10.2 From 020af89a41c41fd2c92d0da524968dfaba6269f0 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 27 Apr 2015 21:24:30 +0300 Subject: PM / sleep: Add macro to define common noirq system PM callbacks The same approach is used as for the existing SET_SYSTEM_SLEEP_PM_OPS, but for noirq callbacks. New SET_NOIRQ_SYSTEM_SLEEP_PM_OPS, defined for CONFIG_PM_SLEEP, will point ->suspend_noirq, ->freeze_noirq and ->poweroff_noirq to the same function. Vice versa happens for ->resume_noirq, ->thaw_noirq and ->restore_noirq. Signed-off-by: Grygorii Strashko Acked-by: Santosh Shilimkar Reviewed-by: Ulf Hansson Acked-by: Pavel Machek Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki diff --git a/include/linux/pm.h b/include/linux/pm.h index 2d29c64..4890743 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -342,6 +342,18 @@ struct dev_pm_ops { #define SET_LATE_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) #endif +#ifdef CONFIG_PM_SLEEP +#define SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \ + .suspend_noirq = suspend_fn, \ + .resume_noirq = resume_fn, \ + .freeze_noirq = suspend_fn, \ + .thaw_noirq = resume_fn, \ + .poweroff_noirq = suspend_fn, \ + .restore_noirq = resume_fn, +#else +#define SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) +#endif + #ifdef CONFIG_PM #define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \ .runtime_suspend = suspend_fn, \ -- cgit v0.10.2 From 258d2a1095eebd1d061f84925b05f0d13bd5fcad Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 27 Apr 2015 21:24:31 +0300 Subject: bus: omap_l3_noc: add missed callbacks for suspend-to-disk Add missed callbacks needed for proper supporting of suspend-to-disk by using recently introduced macro SET_NOIRQ_SYSTEM_SLEEP_PM_OPS. Signed-off-by: Grygorii Strashko Acked-by: Nishanth Menon Acked-by: Santosh Shilimkar Reviewed-by: Ulf Hansson Acked-by: Pavel Machek Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index 11f7982..6ae3884 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -300,7 +300,7 @@ static int omap_l3_probe(struct platform_device *pdev) return ret; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /** * l3_resume_noirq() - resume function for l3_noc @@ -346,7 +346,7 @@ static int l3_resume_noirq(struct device *dev) } static const struct dev_pm_ops l3_dev_pm_ops = { - .resume_noirq = l3_resume_noirq, + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, l3_resume_noirq) }; #define L3_DEV_PM_OPS (&l3_dev_pm_ops) -- cgit v0.10.2 From c381f22947ea9afa55311dfe672dd73b92f01e6e Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 27 Apr 2015 21:24:32 +0300 Subject: ARM: omap-device: use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS Use recently introduced macro SET_NOIRQ_SYSTEM_SLEEP_PM_OPS to set up PM callbacks. This also fixes missed assignment of .poweroff_noirq() callback. Signed-off-by: Grygorii Strashko Acked-by: Santosh Shilimkar Reviewed-by: Ulf Hansson Acked-by: Pavel Machek Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm/mach-omap2/omap_device.c b/arch/arm/mach-omap2/omap_device.c index 166b18f..83a0f5a 100644 --- a/arch/arm/mach-omap2/omap_device.c +++ b/arch/arm/mach-omap2/omap_device.c @@ -688,11 +688,8 @@ struct dev_pm_domain omap_device_pm_domain = { SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume, NULL) USE_PLATFORM_PM_SLEEP_OPS - .suspend_noirq = _od_suspend_noirq, - .resume_noirq = _od_resume_noirq, - .freeze_noirq = _od_suspend_noirq, - .thaw_noirq = _od_resume_noirq, - .restore_noirq = _od_resume_noirq, + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(_od_suspend_noirq, + _od_resume_noirq) } }; -- cgit v0.10.2 From 75f504004ab866c8f84749303b0f70953724e259 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 23 Apr 2015 14:03:09 +0530 Subject: PM / clock_ops: Provide default runtime ops to users Most users of PM clocks do the extact same things in the runtime suspend/resume callbacks. Provide them USE_PM_CLK_RUNTIME_OPS so as to avoid/remove boilerplate code. Signed-off-by: Rajendra Nayak Reviewed-by: Kevin Hilman Acked-by: Santosh Shilimkar Acked-by: Geert Uytterhoeven Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 7fdd017..8abea66 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef CONFIG_PM @@ -367,6 +368,43 @@ static int pm_clk_notify(struct notifier_block *nb, return 0; } +int pm_clk_runtime_suspend(struct device *dev) +{ + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = pm_generic_runtime_suspend(dev); + if (ret) { + dev_err(dev, "failed to suspend device\n"); + return ret; + } + + ret = pm_clk_suspend(dev); + if (ret) { + dev_err(dev, "failed to suspend clock\n"); + pm_generic_runtime_resume(dev); + return ret; + } + + return 0; +} + +int pm_clk_runtime_resume(struct device *dev) +{ + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = pm_clk_resume(dev); + if (ret) { + dev_err(dev, "failed to resume clock\n"); + return ret; + } + + return pm_generic_runtime_resume(dev); +} + #else /* !CONFIG_PM */ /** diff --git a/include/linux/pm_clock.h b/include/linux/pm_clock.h index 0b00396..25266c6 100644 --- a/include/linux/pm_clock.h +++ b/include/linux/pm_clock.h @@ -20,6 +20,16 @@ struct pm_clk_notifier_block { struct clk; +#ifdef CONFIG_PM +extern int pm_clk_runtime_suspend(struct device *dev); +extern int pm_clk_runtime_resume(struct device *dev); +#define USE_PM_CLK_RUNTIME_OPS \ + .runtime_suspend = pm_clk_runtime_suspend, \ + .runtime_resume = pm_clk_runtime_resume, +#else +#define USE_PM_CLK_RUNTIME_OPS +#endif + #ifdef CONFIG_PM_CLK static inline bool pm_clk_no_clocks(struct device *dev) { -- cgit v0.10.2 From 1f51b0c6461eb72595c2b69b79ca69a322ed1d0e Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 23 Apr 2015 14:03:10 +0530 Subject: arm: keystone: remove boilerplate code and use USE_PM_CLK_RUNTIME_OPS USE_PM_CLK_RUNTIME_OPS is introduced so we don't repeat the same code to do runtime_suspend and runtime_resume across users of PM clocks. Use it to remove the boilerplate code. Signed-off-by: Rajendra Nayak Reviewed-by: Kevin Hilman Acked-by: Santosh Shilimkar Acked-by: Geert Uytterhoeven Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm/mach-keystone/pm_domain.c b/arch/arm/mach-keystone/pm_domain.c index 41bebfd..edea697 100644 --- a/arch/arm/mach-keystone/pm_domain.c +++ b/arch/arm/mach-keystone/pm_domain.c @@ -19,40 +19,9 @@ #include #include -#ifdef CONFIG_PM -static int keystone_pm_runtime_suspend(struct device *dev) -{ - int ret; - - dev_dbg(dev, "%s\n", __func__); - - ret = pm_generic_runtime_suspend(dev); - if (ret) - return ret; - - ret = pm_clk_suspend(dev); - if (ret) { - pm_generic_runtime_resume(dev); - return ret; - } - - return 0; -} - -static int keystone_pm_runtime_resume(struct device *dev) -{ - dev_dbg(dev, "%s\n", __func__); - - pm_clk_resume(dev); - - return pm_generic_runtime_resume(dev); -} -#endif - static struct dev_pm_domain keystone_pm_domain = { .ops = { - SET_RUNTIME_PM_OPS(keystone_pm_runtime_suspend, - keystone_pm_runtime_resume, NULL) + USE_PM_CLK_RUNTIME_OPS USE_PLATFORM_PM_SLEEP_OPS }, }; -- cgit v0.10.2 From 2d3dc933e0575f1e5e07d222ffd95d8517562b38 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 23 Apr 2015 14:03:11 +0530 Subject: arm: omap1: remove boilerplate code and use USE_PM_CLK_RUNTIME_OPS USE_PM_CLK_RUNTIME_OPS is introduced so we don't repeat the same code to do runtime_suspend and runtime_resume across users of PM clocks. Use it to remove the boilerplate code. Signed-off-by: Rajendra Nayak Reviewed-by: Kevin Hilman Acked-by: Santosh Shilimkar Acked-by: Geert Uytterhoeven Acked-by: Tony Lindgren Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm/mach-omap1/pm_bus.c b/arch/arm/mach-omap1/pm_bus.c index c40e209..667c163 100644 --- a/arch/arm/mach-omap1/pm_bus.c +++ b/arch/arm/mach-omap1/pm_bus.c @@ -21,48 +21,15 @@ #include "soc.h" -#ifdef CONFIG_PM -static int omap1_pm_runtime_suspend(struct device *dev) -{ - int ret; - - dev_dbg(dev, "%s\n", __func__); - - ret = pm_generic_runtime_suspend(dev); - if (ret) - return ret; - - ret = pm_clk_suspend(dev); - if (ret) { - pm_generic_runtime_resume(dev); - return ret; - } - - return 0; -} - -static int omap1_pm_runtime_resume(struct device *dev) -{ - dev_dbg(dev, "%s\n", __func__); - - pm_clk_resume(dev); - return pm_generic_runtime_resume(dev); -} - static struct dev_pm_domain default_pm_domain = { .ops = { - .runtime_suspend = omap1_pm_runtime_suspend, - .runtime_resume = omap1_pm_runtime_resume, + USE_PM_CLK_RUNTIME_OPS USE_PLATFORM_PM_SLEEP_OPS }, }; -#define OMAP1_PM_DOMAIN (&default_pm_domain) -#else -#define OMAP1_PM_DOMAIN NULL -#endif /* CONFIG_PM */ static struct pm_clk_notifier_block platform_bus_notifier = { - .pm_domain = OMAP1_PM_DOMAIN, + .pm_domain = &default_pm_domain, .con_ids = { "ick", "fck", NULL, }, }; -- cgit v0.10.2 From ea6f83346ecbe65e061a8c66c5486d3f2c9964e9 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 23 Apr 2015 14:03:12 +0530 Subject: arm: davinci: remove boilerplate code and use USE_PM_CLK_RUNTIME_OPS USE_PM_CLK_RUNTIME_OPS is introduced so we don't repeat the same code to do runtime_suspend and runtime_resume across users of PM clocks. Use it to remove the boilerplate code. Signed-off-by: Rajendra Nayak Reviewed-by: Kevin Hilman Acked-by: Santosh Shilimkar Acked-by: Geert Uytterhoeven Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm/mach-davinci/pm_domain.c b/arch/arm/mach-davinci/pm_domain.c index 641edc3..78eac2c 100644 --- a/arch/arm/mach-davinci/pm_domain.c +++ b/arch/arm/mach-davinci/pm_domain.c @@ -14,39 +14,9 @@ #include #include -#ifdef CONFIG_PM -static int davinci_pm_runtime_suspend(struct device *dev) -{ - int ret; - - dev_dbg(dev, "%s\n", __func__); - - ret = pm_generic_runtime_suspend(dev); - if (ret) - return ret; - - ret = pm_clk_suspend(dev); - if (ret) { - pm_generic_runtime_resume(dev); - return ret; - } - - return 0; -} - -static int davinci_pm_runtime_resume(struct device *dev) -{ - dev_dbg(dev, "%s\n", __func__); - - pm_clk_resume(dev); - return pm_generic_runtime_resume(dev); -} -#endif - static struct dev_pm_domain davinci_pm_domain = { .ops = { - SET_RUNTIME_PM_OPS(davinci_pm_runtime_suspend, - davinci_pm_runtime_resume, NULL) + USE_PM_CLK_RUNTIME_OPS USE_PLATFORM_PM_SLEEP_OPS }, }; -- cgit v0.10.2 From d2c4b43d8848afaf968ed1f3cea0c99296ff5ff3 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Thu, 23 Apr 2015 14:03:13 +0530 Subject: drivers: sh: remove boilerplate code and use USE_PM_CLK_RUNTIME_OPS USE_PM_CLK_RUNTIME_OPS is introduced so we don't repeat the same code to do runtime_suspend and runtime_resume across users of PM clocks. Use it to remove the boilerplate code. Signed-off-by: Rajendra Nayak Reviewed-by: Kevin Hilman Acked-by: Santosh Shilimkar Acked-by: Geert Uytterhoeven Signed-off-by: Rafael J. Wysocki diff --git a/drivers/sh/pm_runtime.c b/drivers/sh/pm_runtime.c index fe8875f..d3d1891 100644 --- a/drivers/sh/pm_runtime.c +++ b/drivers/sh/pm_runtime.c @@ -20,58 +20,15 @@ #include #include -#ifdef CONFIG_PM -static int sh_pm_runtime_suspend(struct device *dev) -{ - int ret; - - ret = pm_generic_runtime_suspend(dev); - if (ret) { - dev_err(dev, "failed to suspend device\n"); - return ret; - } - - ret = pm_clk_suspend(dev); - if (ret) { - dev_err(dev, "failed to suspend clock\n"); - pm_generic_runtime_resume(dev); - return ret; - } - - return 0; -} - -static int sh_pm_runtime_resume(struct device *dev) -{ - int ret; - - ret = pm_clk_resume(dev); - if (ret) { - dev_err(dev, "failed to resume clock\n"); - return ret; - } - - return pm_generic_runtime_resume(dev); -} - static struct dev_pm_domain default_pm_domain = { .ops = { - .runtime_suspend = sh_pm_runtime_suspend, - .runtime_resume = sh_pm_runtime_resume, + USE_PM_CLK_RUNTIME_OPS USE_PLATFORM_PM_SLEEP_OPS }, }; -#define DEFAULT_PM_DOMAIN_PTR (&default_pm_domain) - -#else - -#define DEFAULT_PM_DOMAIN_PTR NULL - -#endif /* CONFIG_PM */ - static struct pm_clk_notifier_block platform_bus_notifier = { - .pm_domain = DEFAULT_PM_DOMAIN_PTR, + .pm_domain = &default_pm_domain, .con_ids = { NULL, }, }; -- cgit v0.10.2 From 084609bf727981c7a2e6e69aefe0052c9d793300 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 24 Apr 2015 14:57:10 +0300 Subject: leds / PM: fix hibernation on arm when gpio-led used with CPU led trigger Setting a dev_pm_ops suspend/resume pair of callbacks but not a set of hibernation callbacks means those pm functions will not be called upon hibernation - that leads to system crash on ARM during freezing if gpio-led is used in combination with CPU led trigger. It may happen after freeze_noirq stage (GPIO is suspended) and before syscore_suspend stage (CPU led trigger is suspended) - usually when disable_nonboot_cpus() is called. Log: PM: noirq freeze of devices complete after 1.425 msecs Disabling non-boot CPUs ... ^ system may crash or stuck here with message (TI AM572x) WARNING: CPU: 0 PID: 3100 at drivers/bus/omap_l3_noc.c:148 l3_interrupt_handler+0x22c/0x370() 44000000.ocp:L3 Custom Error: MASTER MPU TARGET L4_PER1_P3 (Idle): Data Access in Supervisor mode during Functional access CPU1: shutdown ^ or here Fix this by using SIMPLE_DEV_PM_OPS, which appropriately assigns the suspend and hibernation callbacks and move led_suspend/led_resume under CONFIG_PM_SLEEP to avoid build warnings. Fixes: 73e1ab41a80d (leds: Convert led class driver from legacy pm ops to dev_pm_ops) Signed-off-by: Grygorii Strashko Acked-by: Jacek Anaszewski Cc: 3.11+ # 3.11+ Signed-off-by: Rafael J. Wysocki diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 728681d..7fb2a19 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -187,6 +187,7 @@ void led_classdev_resume(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_classdev_resume); +#ifdef CONFIG_PM_SLEEP static int led_suspend(struct device *dev) { struct led_classdev *led_cdev = dev_get_drvdata(dev); @@ -206,11 +207,9 @@ static int led_resume(struct device *dev) return 0; } +#endif -static const struct dev_pm_ops leds_class_dev_pm_ops = { - .suspend = led_suspend, - .resume = led_resume, -}; +static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume); static int match_name(struct device *dev, const void *data) { -- cgit v0.10.2 From f6a2fbb903d51b1e328d6e358048426ca97932ad Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Fri, 17 Apr 2015 01:14:15 +0100 Subject: PM / runtime: add note about re-calling in during device probe() The sh_eth driver has come up with an issue where the runtime_pm code suspends it during the probe() method due to the network device registration re-calling into the driver. Add a note about this into the documentation. Signed-off-by: Ben Dooks Signed-off-by: Ben Hutchings Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt index 44fe1d2..e76dc0a 100644 --- a/Documentation/power/runtime_pm.txt +++ b/Documentation/power/runtime_pm.txt @@ -556,6 +556,12 @@ helper functions described in Section 4. In that case, pm_runtime_resume() should be used. Of course, for this purpose the device's runtime PM has to be enabled earlier by calling pm_runtime_enable(). +Note, if the device may execute pm_runtime calls during the probe (such as +if it is registers with a subsystem that may call back in) then the +pm_runtime_get_sync() call paired with a pm_runtime_put() call will be +appropriate to ensure that the device is not put back to sleep during the +probe. This can happen with systems such as the network device layer. + It may be desirable to suspend the device once ->probe() has finished. Therefore the driver core uses the asyncronous pm_request_idle() to submit a request to execute the subsystem-level idle callback for the device at that -- cgit v0.10.2 From 819b1bb30d2fb1b3a2b8016e83f02dfc85ada1e0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 13 May 2015 15:31:12 +0200 Subject: PM / sleep: Fix symbol name in a comment in kernel/power/main.c Signed-off-by: Rafael J. Wysocki diff --git a/kernel/power/main.c b/kernel/power/main.c index 86e8157..63d395b 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -272,7 +272,7 @@ static inline void pm_print_times_init(void) { pm_print_times_enabled = !!initcall_debug; } -#else /* !CONFIG_PP_SLEEP_DEBUG */ +#else /* !CONFIG_PM_SLEEP_DEBUG */ static inline void pm_print_times_init(void) {} #endif /* CONFIG_PM_SLEEP_DEBUG */ -- cgit v0.10.2 From 25956b6612601cf36022392ffa83f6bf97939bcd Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Mon, 11 May 2015 12:17:13 +0800 Subject: ACPI / processor: Introduce invalid_logical_cpuid() In ACPI processor drivers, we use direct comparisons of cpu logical id with -1 which are error prone in case logical cpuid is accidentally assinged an error code and prevents us from returning an error-encoding cpuid directly in some cases. So introduce invalid_logical_cpuid() to identify cpu with invalid logical cpu num, then it will be used to replace the direct comparisons with -1. Signed-off-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 58f335c..ac6bda0 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -275,7 +275,8 @@ static int acpi_processor_get_info(struct acpi_device *device) * Handle UP system running SMP kernel, with no CPU * entry in MADT */ - if ((cpu_index == -1) && (num_online_cpus() == 1)) + if (invalid_logical_cpuid(cpu_index) + && (num_online_cpus() == 1)) cpu_index = 0; } pr->id = cpu_index; @@ -285,7 +286,7 @@ static int acpi_processor_get_info(struct acpi_device *device) * less than the max # of CPUs. They should be ignored _iff * they are physically not present. */ - if (pr->id == -1) { + if (invalid_logical_cpuid(pr->id)) { int ret = acpi_processor_hotadd_init(pr); if (ret) return ret; diff --git a/drivers/acpi/processor_pdc.c b/drivers/acpi/processor_pdc.c index e5dd808..7cfbda4 100644 --- a/drivers/acpi/processor_pdc.c +++ b/drivers/acpi/processor_pdc.c @@ -52,10 +52,7 @@ static bool __init processor_physically_present(acpi_handle handle) type = (acpi_type == ACPI_TYPE_DEVICE) ? 1 : 0; cpuid = acpi_get_cpuid(handle, type, acpi_id); - if (cpuid == -1) - return false; - - return true; + return !invalid_logical_cpuid(cpuid); } static void acpi_set_pdc_bits(u32 *buf) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e4da5e3..913b49f 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -158,6 +158,11 @@ typedef u32 phys_cpuid_t; #define PHYS_CPUID_INVALID (phys_cpuid_t)(-1) #endif +static inline bool invalid_logical_cpuid(u32 cpuid) +{ + return (int)cpuid < 0; +} + #ifdef CONFIG_ACPI_HOTPLUG_CPU /* Arch dependent functions for cpu hotplug support */ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, int *pcpu); -- cgit v0.10.2 From 16eac059a6800f640423b7b6e2e9fc7091bad102 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Mon, 11 May 2015 12:17:14 +0800 Subject: Xen / ACPI / processor: use invalid_logical_cpuid() Use invalid_logical_cpuid(pr->id) instead of direct comparison. Signed-off-by: Hanjun Guo CC: Boris Ostrovsky CC: Stefano Stabellini Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Rafael J. Wysocki diff --git a/drivers/xen/xen-acpi-cpuhotplug.c b/drivers/xen/xen-acpi-cpuhotplug.c index 3e62ee4..5a62aa0 100644 --- a/drivers/xen/xen-acpi-cpuhotplug.c +++ b/drivers/xen/xen-acpi-cpuhotplug.c @@ -77,7 +77,7 @@ static int xen_acpi_processor_enable(struct acpi_device *device) pr->id = xen_pcpu_id(pr->acpi_id); - if ((int)pr->id < 0) + if (invalid_logical_cpuid(pr->id)) /* This cpu is not presented at hypervisor, try to hotadd it */ if (ACPI_FAILURE(xen_acpi_cpu_hotadd(pr))) { pr_err(PREFIX "Hotadd CPU (acpi_id = %d) failed.\n", @@ -226,7 +226,7 @@ static acpi_status xen_acpi_cpu_hotadd(struct acpi_processor *pr) return AE_ERROR; pr->id = xen_hotadd_cpu(pr); - if ((int)pr->id < 0) + if (invalid_logical_cpuid(pr->id)) return AE_ERROR; /* -- cgit v0.10.2 From c867d83855a6c50ac1a33960c9d50a77ff636f64 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Mon, 11 May 2015 12:17:15 +0800 Subject: Xen / ACPI / processor: Remove unneeded NULL check Before xen_acpi_processor_enable() is called, struct acpi_processor *pr is allocated in xen_acpi_processor_add() and checked if it's NULL, so no need to check again when passed to xen_acpi_processor_enable(), just remove it. Signed-off-by: Hanjun Guo CC: Boris Ostrovsky CC: Stefano Stabellini Reviewed-by: Konrad Rzeszutek Wilk Signed-off-by: Rafael J. Wysocki diff --git a/drivers/xen/xen-acpi-cpuhotplug.c b/drivers/xen/xen-acpi-cpuhotplug.c index 5a62aa0..f4a3694 100644 --- a/drivers/xen/xen-acpi-cpuhotplug.c +++ b/drivers/xen/xen-acpi-cpuhotplug.c @@ -46,13 +46,7 @@ static int xen_acpi_processor_enable(struct acpi_device *device) unsigned long long value; union acpi_object object = { 0 }; struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; - struct acpi_processor *pr; - - pr = acpi_driver_data(device); - if (!pr) { - pr_err(PREFIX "Cannot find driver data\n"); - return -EINVAL; - } + struct acpi_processor *pr = acpi_driver_data(device); if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) { /* Declared with "Processor" statement; match ProcessorID */ -- cgit v0.10.2 From 05eb3cd8b4f2dc5b694bd17b20df5b6aa4ffdae3 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Mon, 11 May 2015 12:17:16 +0800 Subject: ACPI / processor: remove cpu_index in acpi_processor_get_info() Just use pr->id instead of cpu_index to simplify the code. Signed-off-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index ac6bda0..0676b50 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -216,7 +216,7 @@ static int acpi_processor_get_info(struct acpi_device *device) struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; struct acpi_processor *pr = acpi_driver_data(device); phys_cpuid_t phys_id; - int cpu_index, device_declaration = 0; + int device_declaration = 0; acpi_status status = AE_OK; static int cpu0_initialized; unsigned long long value; @@ -268,18 +268,16 @@ static int acpi_processor_get_info(struct acpi_device *device) acpi_handle_debug(pr->handle, "failed to get CPU physical ID.\n"); pr->phys_id = phys_id; - cpu_index = acpi_map_cpuid(pr->phys_id, pr->acpi_id); + pr->id = acpi_map_cpuid(pr->phys_id, pr->acpi_id); if (!cpu0_initialized && !acpi_has_cpu_in_madt()) { cpu0_initialized = 1; /* * Handle UP system running SMP kernel, with no CPU * entry in MADT */ - if (invalid_logical_cpuid(cpu_index) - && (num_online_cpus() == 1)) - cpu_index = 0; + if (invalid_logical_cpuid(pr->id) && (num_online_cpus() == 1)) + pr->id = 0; } - pr->id = cpu_index; /* * Extra Processor objects may be enumerated on MP systems with -- cgit v0.10.2 From 8416c5bda17c1c4f5bd710ef6ebba8827a719e74 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Mon, 11 May 2015 12:17:17 +0800 Subject: ACPI / processor: remove phys_id in acpi_processor_get_info() Use pr->phys_id to replace phys_id to simplify the code. Signed-off-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 0676b50..62c846b 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -215,7 +215,6 @@ static int acpi_processor_get_info(struct acpi_device *device) union acpi_object object = { 0 }; struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; struct acpi_processor *pr = acpi_driver_data(device); - phys_cpuid_t phys_id; int device_declaration = 0; acpi_status status = AE_OK; static int cpu0_initialized; @@ -263,10 +262,10 @@ static int acpi_processor_get_info(struct acpi_device *device) pr->acpi_id = value; } - phys_id = acpi_get_phys_id(pr->handle, device_declaration, pr->acpi_id); - if (phys_id == PHYS_CPUID_INVALID) + pr->phys_id = acpi_get_phys_id(pr->handle, device_declaration, + pr->acpi_id); + if (pr->phys_id == PHYS_CPUID_INVALID) acpi_handle_debug(pr->handle, "failed to get CPU physical ID.\n"); - pr->phys_id = phys_id; pr->id = acpi_map_cpuid(pr->phys_id, pr->acpi_id); if (!cpu0_initialized && !acpi_has_cpu_in_madt()) { -- cgit v0.10.2 From d3da7cb9d243c484fcb05e6c16f55090fba99702 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Mon, 11 May 2015 12:17:18 +0800 Subject: ACPI / processor: return specific error instead of -1 Since invalid_logical_cpuid() can check error values, so return specific error instead of -1 for acpi_map_cpuid(). Signed-off-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index b1ec78b..fd4140d 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -215,12 +215,12 @@ int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id) * Ignores phys_id and always returns 0 for the processor * handle with acpi id 0 if nr_cpu_ids is 1. * This should be the case if SMP tables are not found. - * Return -1 for other CPU's handle. + * Return -EINVAL for other CPU's handle. */ if (nr_cpu_ids <= 1 && acpi_id == 0) return acpi_id; else - return -1; + return -EINVAL; } #ifdef CONFIG_SMP @@ -233,7 +233,7 @@ int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id) if (phys_id == 0) return phys_id; #endif - return -1; + return -ENODEV; } int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) -- cgit v0.10.2 From ddcc18f5bdd1aafd457032ec693fd9d0af764d61 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Wed, 13 May 2015 16:19:30 +0800 Subject: ACPI / processor: Introduce invalid_phys_cpuid() Introduce invalid_phys_cpuid() to identify cpu with invalid physical ID, then used it as replacement of the direct comparisons with PHYS_CPUID_INVALID. Signed-off-by: Hanjun Guo Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 62c846b..92a5f73 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -170,7 +170,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) acpi_status status; int ret; - if (pr->phys_id == PHYS_CPUID_INVALID) + if (invalid_phys_cpuid(pr->phys_id)) return -ENODEV; status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta); @@ -264,7 +264,7 @@ static int acpi_processor_get_info(struct acpi_device *device) pr->phys_id = acpi_get_phys_id(pr->handle, device_declaration, pr->acpi_id); - if (pr->phys_id == PHYS_CPUID_INVALID) + if (invalid_phys_cpuid(pr->phys_id)) acpi_handle_debug(pr->handle, "failed to get CPU physical ID.\n"); pr->id = acpi_map_cpuid(pr->phys_id, pr->acpi_id); diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index fd4140d..33a38d6 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -184,7 +184,7 @@ phys_cpuid_t acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id) phys_cpuid_t phys_id; phys_id = map_mat_entry(handle, type, acpi_id); - if (phys_id == PHYS_CPUID_INVALID) + if (invalid_phys_cpuid(phys_id)) phys_id = map_madt_entry(type, acpi_id); return phys_id; @@ -196,7 +196,7 @@ int acpi_map_cpuid(phys_cpuid_t phys_id, u32 acpi_id) int i; #endif - if (phys_id == PHYS_CPUID_INVALID) { + if (invalid_phys_cpuid(phys_id)) { /* * On UP processor, there is no _MAT or MADT table. * So above phys_id is always set to PHYS_CPUID_INVALID. diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 913b49f..90e4ed1 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -163,6 +163,11 @@ static inline bool invalid_logical_cpuid(u32 cpuid) return (int)cpuid < 0; } +static inline bool invalid_phys_cpuid(phys_cpuid_t phys_id) +{ + return phys_id == PHYS_CPUID_INVALID; +} + #ifdef CONFIG_ACPI_HOTPLUG_CPU /* Arch dependent functions for cpu hotplug support */ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, int *pcpu); -- cgit v0.10.2 From e234b074c8ebe736f74cc40a8953accb66b82d04 Mon Sep 17 00:00:00 2001 From: Luis Henriques Date: Mon, 11 May 2015 22:48:38 +0100 Subject: ACPI / battery: abort initialization earlier if acpi_disabled If ACPI is disabled there's no need to schedule an async function call, the driver initialization can be aborted earlier in acpi_battery_init(). Signed-off-by: Luis Henriques Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 63d4367..69b8e60 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1292,9 +1292,6 @@ static struct acpi_driver acpi_battery_driver = { static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie) { - if (acpi_disabled) - return; - dmi_check_system(bat_dmi_table); #ifdef CONFIG_ACPI_PROCFS_POWER @@ -1313,6 +1310,9 @@ static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie) static int __init acpi_battery_init(void) { + if (acpi_disabled) + return -ENODEV; + async_schedule(acpi_battery_init_async, NULL); return 0; } -- cgit v0.10.2 From 479faaf00fde35e80a5be1389938b38e422d799d Mon Sep 17 00:00:00 2001 From: Luis Henriques Date: Mon, 11 May 2015 22:48:46 +0100 Subject: ACPI / battery: drop useless return statements Signed-off-by: Luis Henriques Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 69b8e60..9c676a6 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1292,20 +1292,20 @@ static struct acpi_driver acpi_battery_driver = { static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie) { + int result; + dmi_check_system(bat_dmi_table); - + #ifdef CONFIG_ACPI_PROCFS_POWER acpi_battery_dir = acpi_lock_battery_dir(); if (!acpi_battery_dir) return; #endif - if (acpi_bus_register_driver(&acpi_battery_driver) < 0) { + result = acpi_bus_register_driver(&acpi_battery_driver); #ifdef CONFIG_ACPI_PROCFS_POWER + if (result < 0) acpi_unlock_battery_dir(acpi_battery_dir); #endif - return; - } - return; } static int __init acpi_battery_init(void) -- cgit v0.10.2 From eca21d9167213bc9217450613a671553ab45c225 Mon Sep 17 00:00:00 2001 From: Luis Henriques Date: Mon, 11 May 2015 22:49:05 +0100 Subject: ACPI / battery: ensure acpi_battery_init() has finish Make sure that async function scheduled with async_schedule() has already been executed. Signed-off-by: Luis Henriques Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 9c676a6..547e627 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -70,6 +70,7 @@ MODULE_AUTHOR("Alexey Starikovskiy "); MODULE_DESCRIPTION("ACPI Battery Driver"); MODULE_LICENSE("GPL"); +static async_cookie_t async_cookie; static int battery_bix_broken_package; static int battery_notification_delay_ms; static unsigned int cache_time = 1000; @@ -1313,12 +1314,13 @@ static int __init acpi_battery_init(void) if (acpi_disabled) return -ENODEV; - async_schedule(acpi_battery_init_async, NULL); + async_cookie = async_schedule(acpi_battery_init_async, NULL); return 0; } static void __exit acpi_battery_exit(void) { + async_synchronize_cookie(async_cookie); acpi_bus_unregister_driver(&acpi_battery_driver); #ifdef CONFIG_ACPI_PROCFS_POWER acpi_unlock_battery_dir(acpi_battery_dir); -- cgit v0.10.2 From faad38492814112e3e7ce94d90123bbe301fff33 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 10 May 2015 01:18:03 +0200 Subject: sched / idle: Call idle_set_state() from cpuidle_enter_state() Introduce a wrapper function around idle_set_state() called sched_idle_set_state() that will pass this_rq() to it as the first argument and make cpuidle_enter_state() call the new function before and after entering the target state. At the same time, remove direct invocations of idle_set_state() from call_cpuidle(). This will allow the invocation of default_idle_call() to be moved from call_cpuidle() to cpuidle_enter_state() safely and call_cpuidle() to be simplified a bit as a result. Signed-off-by: Rafael J. Wysocki Reviewed-by: Preeti U Murthy Tested-by: Preeti U Murthy Tested-by: Sudeep Holla Acked-by: Kevin Hilman diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 597f884..9306dd5 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -170,6 +170,9 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, if (broadcast && tick_broadcast_enter()) return -EBUSY; + /* Take note of the planned idle state. */ + sched_idle_set_state(target_state); + trace_cpu_idle_rcuidle(index, dev->cpu); time_start = ktime_get(); @@ -178,6 +181,9 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, time_end = ktime_get(); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); + /* The cpu is no longer idle or about to enter idle. */ + sched_idle_set_state(NULL); + if (broadcast) { if (WARN_ON_ONCE(!irqs_disabled())) local_irq_disable(); diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 9c5e892..301eaaa 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -200,6 +200,9 @@ static inline struct cpuidle_driver *cpuidle_get_cpu_driver( struct cpuidle_device *dev) {return NULL; } #endif +/* kernel/sched/idle.c */ +extern void sched_idle_set_state(struct cpuidle_state *idle_state); + #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a); #else diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 9c919b4..5d9f549 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -15,6 +15,15 @@ #include "sched.h" +/** + * sched_idle_set_state - Record idle state for the current CPU. + * @idle_state: State to record. + */ +void sched_idle_set_state(struct cpuidle_state *idle_state) +{ + idle_set_state(this_rq(), idle_state); +} + static int __read_mostly cpu_idle_force_poll; void cpu_idle_poll_ctrl(bool enable) @@ -100,9 +109,6 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev, return -EBUSY; } - /* Take note of the planned idle state. */ - idle_set_state(this_rq(), &drv->states[next_state]); - /* * Enter the idle state previously returned by the governor decision. * This function will block until an interrupt occurs and will take @@ -110,9 +116,6 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev, */ entered_state = cpuidle_enter(drv, dev, next_state); - /* The cpu is no longer idle or about to enter idle. */ - idle_set_state(this_rq(), NULL); - if (entered_state == -EBUSY) default_idle_call(); -- cgit v0.10.2 From 827a5aefc542b8fb17c00de06118e5cd0e3800f2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 10 May 2015 01:18:46 +0200 Subject: sched / idle: Call default_idle_call() from cpuidle_enter_state() The check of the cpuidle_enter() return value against -EBUSY made in call_cpuidle() will not be necessary any more if cpuidle_enter_state() calls default_idle_call() directly when it is about to return -EBUSY, so make that happen and eliminate the check. Signed-off-by: Rafael J. Wysocki Reviewed-by: Preeti U Murthy Tested-by: Preeti U Murthy Tested-by: Sudeep Holla Acked-by: Kevin Hilman diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 9306dd5..a7b9e67 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -167,8 +167,10 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, * local timer will be shut down. If a local timer is used from another * CPU as a broadcast timer, this call may fail if it is not available. */ - if (broadcast && tick_broadcast_enter()) + if (broadcast && tick_broadcast_enter()) { + default_idle_call(); return -EBUSY; + } /* Take note of the planned idle state. */ sched_idle_set_state(target_state); diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 301eaaa..c7a6364 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -202,6 +202,7 @@ static inline struct cpuidle_driver *cpuidle_get_cpu_driver( /* kernel/sched/idle.c */ extern void sched_idle_set_state(struct cpuidle_state *idle_state); +extern void default_idle_call(void); #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED void cpuidle_coupled_parallel_barrier(struct cpuidle_device *dev, atomic_t *a); diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c index 5d9f549..594275e 100644 --- a/kernel/sched/idle.c +++ b/kernel/sched/idle.c @@ -76,12 +76,13 @@ void __weak arch_cpu_idle(void) local_irq_enable(); } -static void default_idle_call(void) +/** + * default_idle_call - Default CPU idle routine. + * + * To use when the cpuidle framework cannot be used. + */ +void default_idle_call(void) { - /* - * We can't use the cpuidle framework, let's use the default idle - * routine. - */ if (current_clr_polling_and_test()) local_irq_enable(); else @@ -91,8 +92,6 @@ static void default_idle_call(void) static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev, int next_state) { - int entered_state; - /* Fall back to the default arch idle method on errors. */ if (next_state < 0) { default_idle_call(); @@ -114,12 +113,7 @@ static int call_cpuidle(struct cpuidle_driver *drv, struct cpuidle_device *dev, * This function will block until an interrupt occurs and will take * care of re-enabling the local interrupts */ - entered_state = cpuidle_enter(drv, dev, next_state); - - if (entered_state == -EBUSY) - default_idle_call(); - - return entered_state; + return cpuidle_enter(drv, dev, next_state); } /** -- cgit v0.10.2 From 0d94039fabccaa81d87eafdac509d0dda4df2f7b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 10 May 2015 01:19:52 +0200 Subject: cpuidle: Select a different state on tick_broadcast_enter() failures If tick_broadcast_enter() fails in cpuidle_enter_state(), try to find another idle state to enter instead of invoking default_idle_call() immediately and returning -EBUSY which should increase the chances of saving some energy in those cases. Signed-off-by: Rafael J. Wysocki Reviewed-by: Preeti U Murthy Tested-by: Preeti U Murthy Tested-by: Sudeep Holla Acked-by: Kevin Hilman diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index a7b9e67..af6dd59 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -73,7 +73,10 @@ int cpuidle_play_dead(void) } static int find_deepest_state(struct cpuidle_driver *drv, - struct cpuidle_device *dev, bool freeze) + struct cpuidle_device *dev, + unsigned int max_latency, + unsigned int forbidden_flags, + bool freeze) { unsigned int latency_req = 0; int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1; @@ -83,6 +86,8 @@ static int find_deepest_state(struct cpuidle_driver *drv, struct cpuidle_state_usage *su = &dev->states_usage[i]; if (s->disabled || su->disable || s->exit_latency <= latency_req + || s->exit_latency > max_latency + || (s->flags & forbidden_flags) || (freeze && !s->enter_freeze)) continue; @@ -100,7 +105,7 @@ static int find_deepest_state(struct cpuidle_driver *drv, int cpuidle_find_deepest_state(struct cpuidle_driver *drv, struct cpuidle_device *dev) { - return find_deepest_state(drv, dev, false); + return find_deepest_state(drv, dev, UINT_MAX, 0, false); } static void enter_freeze_proper(struct cpuidle_driver *drv, @@ -139,7 +144,7 @@ int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev) * that interrupts won't be enabled when it exits and allows the tick to * be frozen safely. */ - index = find_deepest_state(drv, dev, true); + index = find_deepest_state(drv, dev, UINT_MAX, 0, true); if (index >= 0) enter_freeze_proper(drv, dev, index); @@ -168,8 +173,13 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, * CPU as a broadcast timer, this call may fail if it is not available. */ if (broadcast && tick_broadcast_enter()) { - default_idle_call(); - return -EBUSY; + index = find_deepest_state(drv, dev, target_state->exit_latency, + CPUIDLE_FLAG_TIMER_STOP, false); + if (index < 0) { + default_idle_call(); + return -EBUSY; + } + target_state = &drv->states[index]; } /* Take note of the planned idle state. */ -- cgit v0.10.2 From 2bad7e27e02ce0984c17e4074c63e7691291244f Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Thu, 14 May 2015 15:31:25 +0200 Subject: ACPI / osl: use same type for acpi_predefined_names values as in definition In the definition of struct acpi_predefined_names, value is of type char *. Make the OSL override function also work with type char * (or, more precisely, with a pointer to it). Signed-off-by: Dominik Brodowski Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 39748bb..7a327b2 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -543,7 +543,7 @@ static char acpi_os_name[ACPI_MAX_OVERRIDE_LEN]; acpi_status acpi_os_predefined_override(const struct acpi_predefined_names *init_val, - acpi_string * new_val) + char **new_val) { if (!init_val || !new_val) return AE_BAD_PARAMETER; diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 0bc78df..d02df0a 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -95,7 +95,7 @@ acpi_physical_address acpi_os_get_root_pointer(void); #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_predefined_override acpi_status acpi_os_predefined_override(const struct acpi_predefined_names *init_val, - acpi_string * new_val); + char **new_val); #endif #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_table_override -- cgit v0.10.2 From ef69449b1c06668c3f08ae6d147833cf52c6381c Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Thu, 14 May 2015 15:31:28 +0200 Subject: ACPI: fix kernel-parameters ordering in Documentation Signed-off-by: Dominik Brodowski Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 61ab162..7e0fe6f 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -179,11 +179,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted. See also Documentation/power/runtime_pm.txt, pci=noacpi - acpi_rsdp= [ACPI,EFI,KEXEC] - Pass the RSDP address to the kernel, mostly used - on machines running EFI runtime service to boot the - second kernel for kdump. - acpi_apic_instance= [ACPI, IOAPIC] Format: 2: use 2nd APIC table, if available @@ -197,6 +192,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted. (e.g. thinkpad_acpi, sony_acpi, etc.) instead of the ACPI video.ko driver. + acpica_no_return_repair [HW, ACPI] + Disable AML predefined validation mechanism + This mechanism can repair the evaluation result to make + the return objects more ACPI specification compliant. + This option is useful for developers to identify the + root cause of an AML interpreter issue when the issue + has something to do with the repair mechanism. + acpi.debug_layer= [HW,ACPI,ACPI_DEBUG] acpi.debug_level= [HW,ACPI,ACPI_DEBUG] Format: @@ -225,6 +228,22 @@ bytes respectively. Such letter suffixes can also be entirely omitted. unusable. The "log_buf_len" parameter may be useful if you need to capture more output. + acpi_enforce_resources= [ACPI] + { strict | lax | no } + Check for resource conflicts between native drivers + and ACPI OperationRegions (SystemIO and SystemMemory + only). IO ports and memory declared in ACPI might be + used by the ACPI subsystem in arbitrary AML code and + can interfere with legacy drivers. + strict (default): access to resources claimed by ACPI + is denied; legacy drivers trying to access reserved + resources will fail to bind to device using them. + lax: access to resources claimed by ACPI is allowed; + legacy drivers trying to access reserved resources + will bind successfully but a warning message is logged. + no: ACPI OperationRegions are not marked as reserved, + no further checks are performed. + acpi_force_table_verification [HW,ACPI] Enable table checksum verification during early stage. By default, this is disabled due to x86 early mapping @@ -253,6 +272,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted. This feature is enabled by default. This option allows to turn off the feature. + acpi_no_memhotplug [ACPI] Disable memory hotplug. Useful for kdump + kernels. + acpi_no_static_ssdt [HW,ACPI] Disable installation of static SSDTs at early boot time By default, SSDTs contained in the RSDT/XSDT will be @@ -263,13 +285,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. dynamic table installation which will install SSDT tables to /sys/firmware/acpi/tables/dynamic. - acpica_no_return_repair [HW, ACPI] - Disable AML predefined validation mechanism - This mechanism can repair the evaluation result to make - the return objects more ACPI specification compliant. - This option is useful for developers to identify the - root cause of an AML interpreter issue when the issue - has something to do with the repair mechanism. + acpi_rsdp= [ACPI,EFI,KEXEC] + Pass the RSDP address to the kernel, mostly used + on machines running EFI runtime service to boot the + second kernel for kdump. acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS Format: To spoof as Windows 98: ="Microsoft Windows" @@ -365,25 +384,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Use timer override. For some broken Nvidia NF5 boards that require a timer override, but don't have HPET - acpi_enforce_resources= [ACPI] - { strict | lax | no } - Check for resource conflicts between native drivers - and ACPI OperationRegions (SystemIO and SystemMemory - only). IO ports and memory declared in ACPI might be - used by the ACPI subsystem in arbitrary AML code and - can interfere with legacy drivers. - strict (default): access to resources claimed by ACPI - is denied; legacy drivers trying to access reserved - resources will fail to bind to device using them. - lax: access to resources claimed by ACPI is allowed; - legacy drivers trying to access reserved resources - will bind successfully but a warning message is logged. - no: ACPI OperationRegions are not marked as reserved, - no further checks are performed. - - acpi_no_memhotplug [ACPI] Disable memory hotplug. Useful for kdump - kernels. - add_efi_memmap [EFI; X86] Include EFI memory map in kernel's map of available physical RAM. -- cgit v0.10.2 From 75e0678e7095c486a1b39ea560f8eb51a2714d6d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 10 May 2015 01:23:35 +0200 Subject: PM / tick: Add tracepoints for suspend-to-idle diagnostics Add suspend/resume tracepoints to tick_freeze() and tick_unfreeze() to catch when timekeeping is suspended and resumed during suspend-to-idle so as to be able to check whether or not we enter the "frozen" state and to measure the time spent in it. Signed-off-by: Rafael J. Wysocki Acked-by: Thomas Gleixner diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 3ae6afa..80c0430 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -457,10 +458,13 @@ void tick_freeze(void) raw_spin_lock(&tick_freeze_lock); tick_freeze_depth++; - if (tick_freeze_depth == num_online_cpus()) + if (tick_freeze_depth == num_online_cpus()) { + trace_suspend_resume(TPS("timekeeping_freeze"), + smp_processor_id(), true); timekeeping_suspend(); - else + } else { tick_suspend_local(); + } raw_spin_unlock(&tick_freeze_lock); } @@ -478,10 +482,13 @@ void tick_unfreeze(void) { raw_spin_lock(&tick_freeze_lock); - if (tick_freeze_depth == num_online_cpus()) + if (tick_freeze_depth == num_online_cpus()) { timekeeping_resume(); - else + trace_suspend_resume(TPS("timekeeping_freeze"), + smp_processor_id(), false); + } else { tick_resume_local(); + } tick_freeze_depth--; -- cgit v0.10.2 From 147301450248d51c5913aff8476cd326f1de557e Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 13 May 2015 13:35:52 +0100 Subject: cpufreq: arm_big_little: remove compile-time dependency on BIG_LITTLE With the addition of switcher code, there's compile-time dependency on BIG_LITTLE to get arm_big_little driver compiling on ARM64. Since ARM64 will never add support for bL switcher, it's better to remove the dependency so that the driver can be reused on ARM64 platforms. This patch adds stubs to remove BIG_LITTLE dependency in the driver. Signed-off-by: Sudeep Holla Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 4f3dbc8..611cb09 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -5,7 +5,7 @@ # big LITTLE core layer and glue drivers config ARM_BIG_LITTLE_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver" - depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK + depends on (ARM_CPU_TOPOLOGY || ARM64) && HAVE_CLK select PM_OPP help This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index e4d75ca..f1e42f8 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -31,7 +31,6 @@ #include #include #include -#include #include "arm_big_little.h" @@ -41,12 +40,16 @@ #define MAX_CLUSTERS 2 #ifdef CONFIG_BL_SWITCHER +#include static bool bL_switching_enabled; #define is_bL_switching_enabled() bL_switching_enabled #define set_switching_enabled(x) (bL_switching_enabled = (x)) #else #define is_bL_switching_enabled() false #define set_switching_enabled(x) do { } while (0) +#define bL_switch_request(...) do { } while (0) +#define bL_switcher_put_enabled() do { } while (0) +#define bL_switcher_get_enabled() do { } while (0) #endif #define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq) @@ -513,6 +516,7 @@ static struct cpufreq_driver bL_cpufreq_driver = { .attr = cpufreq_generic_attr, }; +#ifdef CONFIG_BL_SWITCHER static int bL_cpufreq_switcher_notifier(struct notifier_block *nfb, unsigned long action, void *_arg) { @@ -545,6 +549,20 @@ static struct notifier_block bL_switcher_notifier = { .notifier_call = bL_cpufreq_switcher_notifier, }; +static int __bLs_register_notifier(void) +{ + return bL_switcher_register_notifier(&bL_switcher_notifier); +} + +static int __bLs_unregister_notifier(void) +{ + return bL_switcher_unregister_notifier(&bL_switcher_notifier); +} +#else +static int __bLs_register_notifier(void) { return 0; } +static int __bLs_unregister_notifier(void) { return 0; } +#endif + int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) { int ret, i; @@ -562,8 +580,7 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) arm_bL_ops = ops; - ret = bL_switcher_get_enabled(); - set_switching_enabled(ret); + set_switching_enabled(bL_switcher_get_enabled()); for (i = 0; i < MAX_CLUSTERS; i++) mutex_init(&cluster_lock[i]); @@ -574,7 +591,7 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) __func__, ops->name, ret); arm_bL_ops = NULL; } else { - ret = bL_switcher_register_notifier(&bL_switcher_notifier); + ret = __bLs_register_notifier(); if (ret) { cpufreq_unregister_driver(&bL_cpufreq_driver); arm_bL_ops = NULL; @@ -598,7 +615,7 @@ void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops) } bL_switcher_get_enabled(); - bL_switcher_unregister_notifier(&bL_switcher_notifier); + __bLs_unregister_notifier(); cpufreq_unregister_driver(&bL_cpufreq_driver); bL_switcher_put_enabled(); pr_info("%s: Un-registered platform driver: %s\n", __func__, -- cgit v0.10.2 From f963735a3ca388da4893fc2d463eca6b58667add Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 12 May 2015 12:20:11 +0530 Subject: cpufreq: Create for_each_{in}active_policy() policy->cpus is cleared unconditionally now on hotplug-out of a CPU and it can be checked to know if a policy is active or not. Create helper routines to iterate over all active/inactive policies, based on policy->cpus field. Replace all instances of for_each_policy() with for_each_active_policy() to make them iterate only for active policies. (We haven't made changes yet to keep inactive policies in the same list, but that will be followed in a later patch). Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 8cf0c0e..74d9fcb 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -31,10 +31,62 @@ #include #include -/* Macros to iterate over lists */ -/* Iterate over online CPUs policies */ static LIST_HEAD(cpufreq_policy_list); -#define for_each_policy(__policy) \ + +static inline bool policy_is_inactive(struct cpufreq_policy *policy) +{ + return cpumask_empty(policy->cpus); +} + +static bool suitable_policy(struct cpufreq_policy *policy, bool active) +{ + return active == !policy_is_inactive(policy); +} + +/* Finds Next Acive/Inactive policy */ +static struct cpufreq_policy *next_policy(struct cpufreq_policy *policy, + bool active) +{ + do { + policy = list_next_entry(policy, policy_list); + + /* No more policies in the list */ + if (&policy->policy_list == &cpufreq_policy_list) + return NULL; + } while (!suitable_policy(policy, active)); + + return policy; +} + +static struct cpufreq_policy *first_policy(bool active) +{ + struct cpufreq_policy *policy; + + /* No policies in the list */ + if (list_empty(&cpufreq_policy_list)) + return NULL; + + policy = list_first_entry(&cpufreq_policy_list, typeof(*policy), + policy_list); + + if (!suitable_policy(policy, active)) + policy = next_policy(policy, active); + + return policy; +} + +/* Macros to iterate over CPU policies */ +#define for_each_suitable_policy(__policy, __active) \ + for (__policy = first_policy(__active); \ + __policy; \ + __policy = next_policy(__policy, __active)) + +#define for_each_active_policy(__policy) \ + for_each_suitable_policy(__policy, true) +#define for_each_inactive_policy(__policy) \ + for_each_suitable_policy(__policy, false) + +#define for_each_policy(__policy) \ list_for_each_entry(__policy, &cpufreq_policy_list, policy_list) /* Iterate over governors */ @@ -1156,7 +1208,7 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) /* Check if this CPU already has a policy to manage it */ read_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_policy(policy) { + for_each_active_policy(policy) { if (cpumask_test_cpu(cpu, policy->related_cpus)) { read_unlock_irqrestore(&cpufreq_driver_lock, flags); ret = cpufreq_add_policy_cpu(policy, cpu, dev); @@ -1674,7 +1726,7 @@ void cpufreq_suspend(void) pr_debug("%s: Suspending Governors\n", __func__); - for_each_policy(policy) { + for_each_active_policy(policy) { if (__cpufreq_governor(policy, CPUFREQ_GOV_STOP)) pr_err("%s: Failed to stop governor for policy: %p\n", __func__, policy); @@ -1708,7 +1760,7 @@ void cpufreq_resume(void) pr_debug("%s: Resuming Governors\n", __func__); - for_each_policy(policy) { + for_each_active_policy(policy) { if (cpufreq_driver->resume && cpufreq_driver->resume(policy)) pr_err("%s: Failed to resume driver: %p\n", __func__, policy); @@ -2354,7 +2406,7 @@ static int cpufreq_boost_set_sw(int state) struct cpufreq_policy *policy; int ret = -EINVAL; - for_each_policy(policy) { + for_each_active_policy(policy) { freq_table = cpufreq_frequency_get_table(policy->cpu); if (freq_table) { ret = cpufreq_frequency_table_cpuinfo(policy, -- cgit v0.10.2 From 988bed09d35a8533eb59868692b827dfae7b66fe Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 8 May 2015 11:53:45 +0530 Subject: cpufreq: Don't clear cpufreq_cpu_data and policy list for inactive policies Now that we can check policy->cpus to find if policy is active or not, we don't need to clean cpufreq_cpu_data and delete policy from the list on light weight tear down of policies (like in suspend). To make it consistent and clean, set cpufreq_cpu_data for all related CPUs when the policy is first created and clean it only while it is freed. Also update cpufreq_cpu_get_raw() to check if cpu is part of policy->cpus mask, so that we don't end up getting policies for offline CPUs. In order to make sure that no users of 'policy' are using an inactive policy, use cpufreq_cpu_get_raw() instead of directly accessing cpufreq_cpu_data. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 74d9fcb..e899a54 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -239,10 +239,18 @@ int cpufreq_generic_init(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_generic_init); -unsigned int cpufreq_generic_get(unsigned int cpu) +/* Only for cpufreq core internal use */ +struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu) { struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); + return policy && cpumask_test_cpu(cpu, policy->cpus) ? policy : NULL; +} + +unsigned int cpufreq_generic_get(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); + if (!policy || IS_ERR(policy->clk)) { pr_err("%s: No %s associated to cpu: %d\n", __func__, policy ? "clk" : "policy", cpu); @@ -253,12 +261,6 @@ unsigned int cpufreq_generic_get(unsigned int cpu) } EXPORT_SYMBOL_GPL(cpufreq_generic_get); -/* Only for cpufreq core internal use */ -struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu) -{ - return per_cpu(cpufreq_cpu_data, cpu); -} - /** * cpufreq_cpu_get: returns policy for a cpu and marks it busy. * @@ -292,7 +294,7 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) if (cpufreq_driver) { /* get the CPU */ - policy = per_cpu(cpufreq_cpu_data, cpu); + policy = cpufreq_cpu_get_raw(cpu); if (policy) kobject_get(&policy->kobj); } @@ -1042,7 +1044,6 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu, struct device *dev) { int ret = 0; - unsigned long flags; /* Has this CPU been taken care of already? */ if (cpumask_test_cpu(cpu, policy->cpus)) @@ -1057,13 +1058,7 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, } down_write(&policy->rwsem); - - write_lock_irqsave(&cpufreq_driver_lock, flags); - cpumask_set_cpu(cpu, policy->cpus); - per_cpu(cpufreq_cpu_data, cpu) = policy; - write_unlock_irqrestore(&cpufreq_driver_lock, flags); - up_write(&policy->rwsem); if (has_target()) { @@ -1154,6 +1149,17 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy) static void cpufreq_policy_free(struct cpufreq_policy *policy) { + unsigned long flags; + int cpu; + + /* Remove policy from list */ + write_lock_irqsave(&cpufreq_driver_lock, flags); + list_del(&policy->policy_list); + + for_each_cpu(cpu, policy->related_cpus) + per_cpu(cpufreq_cpu_data, cpu) = NULL; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + free_cpumask_var(policy->related_cpus); free_cpumask_var(policy->cpus); kfree(policy); @@ -1275,12 +1281,12 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) __func__, ret); goto err_init_policy_kobj; } - } - write_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_cpu(j, policy->cpus) - per_cpu(cpufreq_cpu_data, j) = policy; - write_unlock_irqrestore(&cpufreq_driver_lock, flags); + write_lock_irqsave(&cpufreq_driver_lock, flags); + for_each_cpu(j, policy->related_cpus) + per_cpu(cpufreq_cpu_data, j) = policy; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + } if (cpufreq_driver->get && !cpufreq_driver->setpolicy) { policy->cur = cpufreq_driver->get(policy->cpu); @@ -1339,11 +1345,11 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) goto err_out_unregister; blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_CREATE_POLICY, policy); - } - write_lock_irqsave(&cpufreq_driver_lock, flags); - list_add(&policy->policy_list, &cpufreq_policy_list); - write_unlock_irqrestore(&cpufreq_driver_lock, flags); + write_lock_irqsave(&cpufreq_driver_lock, flags); + list_add(&policy->policy_list, &cpufreq_policy_list); + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + } cpufreq_init_policy(policy); @@ -1367,11 +1373,6 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) err_out_unregister: err_get_freq: - write_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_cpu(j, policy->cpus) - per_cpu(cpufreq_cpu_data, j) = NULL; - write_unlock_irqrestore(&cpufreq_driver_lock, flags); - if (!recover_policy) { kobject_put(&policy->kobj); wait_for_completion(&policy->kobj_unregister); @@ -1407,7 +1408,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, write_lock_irqsave(&cpufreq_driver_lock, flags); - policy = per_cpu(cpufreq_cpu_data, cpu); + policy = cpufreq_cpu_get_raw(cpu); /* Save the policy somewhere when doing a light-weight tear-down */ if (cpufreq_suspended) @@ -1465,15 +1466,9 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, static int __cpufreq_remove_dev_finish(struct device *dev, struct subsys_interface *sif) { - unsigned int cpu = dev->id, cpus; + unsigned int cpu = dev->id; int ret; - unsigned long flags; - struct cpufreq_policy *policy; - - write_lock_irqsave(&cpufreq_driver_lock, flags); - policy = per_cpu(cpufreq_cpu_data, cpu); - per_cpu(cpufreq_cpu_data, cpu) = NULL; - write_unlock_irqrestore(&cpufreq_driver_lock, flags); + struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); if (!policy) { pr_debug("%s: No cpu_data found\n", __func__); @@ -1481,12 +1476,11 @@ static int __cpufreq_remove_dev_finish(struct device *dev, } down_write(&policy->rwsem); - cpus = cpumask_weight(policy->cpus); cpumask_clear_cpu(cpu, policy->cpus); up_write(&policy->rwsem); /* If cpu is last user of policy, free policy */ - if (cpus == 1) { + if (policy_is_inactive(policy)) { if (has_target()) { ret = __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); @@ -1508,11 +1502,6 @@ static int __cpufreq_remove_dev_finish(struct device *dev, if (cpufreq_driver->exit) cpufreq_driver->exit(policy); - /* Remove policy from list of active policies */ - write_lock_irqsave(&cpufreq_driver_lock, flags); - list_del(&policy->policy_list); - write_unlock_irqrestore(&cpufreq_driver_lock, flags); - if (!cpufreq_suspended) cpufreq_policy_free(policy); } else if (has_target()) { -- cgit v0.10.2 From 3914d37910af2cd0cc992ae546b8308e05759d2b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 8 May 2015 11:53:46 +0530 Subject: cpufreq: Get rid of cpufreq_cpu_data_fallback We can extract the same information from cpufreq_cpu_data as it is also available for inactive policies now. And so don't need cpufreq_cpu_data_fallback anymore. Also add a WARN_ON() for the case where we try to restore from an active policy. Acked-by: Saravana Kannan Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index e899a54..eb0c3a8 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -101,7 +101,6 @@ static LIST_HEAD(cpufreq_governor_list); */ static struct cpufreq_driver *cpufreq_driver; static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); -static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback); static DEFINE_RWLOCK(cpufreq_driver_lock); DEFINE_MUTEX(cpufreq_governor_lock); @@ -1081,13 +1080,14 @@ static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu) unsigned long flags; read_lock_irqsave(&cpufreq_driver_lock, flags); - - policy = per_cpu(cpufreq_cpu_data_fallback, cpu); - + policy = per_cpu(cpufreq_cpu_data, cpu); read_unlock_irqrestore(&cpufreq_driver_lock, flags); - if (policy) + if (likely(policy)) { + /* Policy should be inactive here */ + WARN_ON(!policy_is_inactive(policy)); policy->governor = NULL; + } return policy; } @@ -1383,11 +1383,8 @@ err_init_policy_kobj: if (cpufreq_driver->exit) cpufreq_driver->exit(policy); err_set_policy_cpu: - if (recover_policy) { - /* Do not leave stale fallback data behind. */ - per_cpu(cpufreq_cpu_data_fallback, cpu) = NULL; + if (recover_policy) cpufreq_policy_put_kobj(policy); - } cpufreq_policy_free(policy); nomem_out: @@ -1401,21 +1398,11 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, { unsigned int cpu = dev->id, cpus; int ret; - unsigned long flags; struct cpufreq_policy *policy; pr_debug("%s: unregistering CPU %u\n", __func__, cpu); - write_lock_irqsave(&cpufreq_driver_lock, flags); - policy = cpufreq_cpu_get_raw(cpu); - - /* Save the policy somewhere when doing a light-weight tear-down */ - if (cpufreq_suspended) - per_cpu(cpufreq_cpu_data_fallback, cpu) = policy; - - write_unlock_irqrestore(&cpufreq_driver_lock, flags); - if (!policy) { pr_debug("%s: No cpu_data found\n", __func__); return -EINVAL; -- cgit v0.10.2 From 9104bb26c740cd4b2c9ee927f3caabbde0414558 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 12 May 2015 12:22:12 +0530 Subject: cpufreq: Don't traverse all active policies to find policy for a cpu We reach here while adding policy for a CPU and enter into the 'if' block only if a policy already exists for the CPU. As cpufreq_cpu_data is set for all policy->related_cpus now, when the policy is first added, we can use that to find the CPU's policy instead of traversing the list of all active policies. Acked-by: Saravana Kannan Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index eb0c3a8..e6a63d6 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1213,16 +1213,13 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) return 0; /* Check if this CPU already has a policy to manage it */ - read_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_active_policy(policy) { - if (cpumask_test_cpu(cpu, policy->related_cpus)) { - read_unlock_irqrestore(&cpufreq_driver_lock, flags); - ret = cpufreq_add_policy_cpu(policy, cpu, dev); - up_read(&cpufreq_rwsem); - return ret; - } + policy = per_cpu(cpufreq_cpu_data, cpu); + if (policy && !policy_is_inactive(policy)) { + WARN_ON(!cpumask_test_cpu(cpu, policy->related_cpus)); + ret = cpufreq_add_policy_cpu(policy, cpu, dev); + up_read(&cpufreq_rwsem); + return ret; } - read_unlock_irqrestore(&cpufreq_driver_lock, flags); /* * Restore the saved policy when doing light-weight init and fall back -- cgit v0.10.2 From 4573237b01221881702fbe6655f3ae5135be1c18 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 12 May 2015 12:22:34 +0530 Subject: cpufreq: Manage governor usage history with 'policy->last_governor' History of which governor was used last is common to all CPUs within a policy and maintaining it per-cpu isn't the best approach for sure. Apart from wasting memory, this also increases the complexity of managing this data structure as it has to be updated for all CPUs. To make that somewhat simpler, lets store this information in a new field 'last_governor' in struct cpufreq_policy and update it on removal of last cpu of a policy. As a side-effect it also solves an old problem, consider a system with two clusters 0 & 1. And there is one policy per cluster. Cluster 0: CPU0 and 1. Cluster 1: CPU2 and 3. - CPU2 is first brought online, and governor is set to performance (default as cpufreq_cpu_governor wasn't set). - Governor is changed to ondemand. - CPU2 is taken offline and cpufreq_cpu_governor is updated for CPU2. - CPU3 is brought online. - Because cpufreq_cpu_governor wasn't set for CPU3, the default governor performance is picked for CPU3. This patch fixes the bug as we now have a single variable to update for policy. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index e6a63d6..16275ba 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -104,9 +104,6 @@ static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); static DEFINE_RWLOCK(cpufreq_driver_lock); DEFINE_MUTEX(cpufreq_governor_lock); -/* This one keeps track of the previously set governor of a removed CPU */ -static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor); - /* Flag to suspend/resume CPUFreq governors */ static bool cpufreq_suspended; @@ -1017,7 +1014,7 @@ static void cpufreq_init_policy(struct cpufreq_policy *policy) memcpy(&new_policy, policy, sizeof(*policy)); /* Update governor of new_policy to the governor used before hotplug */ - gov = find_governor(per_cpu(cpufreq_cpu_governor, policy->cpu)); + gov = find_governor(policy->last_governor); if (gov) pr_debug("Restoring governor %s for cpu %d\n", policy->governor->name, policy->cpu); @@ -1411,14 +1408,15 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, pr_err("%s: Failed to stop governor\n", __func__); return ret; } - - strncpy(per_cpu(cpufreq_cpu_governor, cpu), - policy->governor->name, CPUFREQ_NAME_LEN); } - down_read(&policy->rwsem); + down_write(&policy->rwsem); cpus = cpumask_weight(policy->cpus); - up_read(&policy->rwsem); + + if (has_target() && cpus == 1) + strncpy(policy->last_governor, policy->governor->name, + CPUFREQ_NAME_LEN); + up_write(&policy->rwsem); if (cpu != policy->cpu) { sysfs_remove_link(&dev->kobj, "cpufreq"); @@ -2135,7 +2133,8 @@ EXPORT_SYMBOL_GPL(cpufreq_register_governor); void cpufreq_unregister_governor(struct cpufreq_governor *governor) { - int cpu; + struct cpufreq_policy *policy; + unsigned long flags; if (!governor) return; @@ -2143,12 +2142,13 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) if (cpufreq_disabled()) return; - for_each_present_cpu(cpu) { - if (cpu_online(cpu)) - continue; - if (!strcmp(per_cpu(cpufreq_cpu_governor, cpu), governor->name)) - strcpy(per_cpu(cpufreq_cpu_governor, cpu), "\0"); + /* clear last_governor for all inactive policies */ + read_lock_irqsave(&cpufreq_driver_lock, flags); + for_each_inactive_policy(policy) { + if (!strcmp(policy->last_governor, governor->name)) + strcpy(policy->last_governor, "\0"); } + read_unlock_irqrestore(&cpufreq_driver_lock, flags); mutex_lock(&cpufreq_governor_mutex); list_del(&governor->governor_list); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 2ee4888..48e37c0 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -80,6 +80,7 @@ struct cpufreq_policy { struct cpufreq_governor *governor; /* see below */ void *governor_data; bool governor_enabled; /* governor start/stop flag */ + char last_governor[CPUFREQ_NAME_LEN]; /* last governor used */ struct work_struct update; /* if update_policy() needs to be * called, but you're in IRQ context */ -- cgit v0.10.2 From 18bf3a124ef87fe43045cbf13dff7ea7e3a94aa3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 12 May 2015 12:22:51 +0530 Subject: cpufreq: Mark policy->governor = NULL for inactive policies Later commits would change the way policies are managed today. Policies wouldn't be freed on cpu hotplug (currently they aren't freed on suspend), and while the CPU is offline, the sysfs cpufreq files would still be present. Because we don't mark policy->governor as NULL, it still contains pointer of the last used governor. And if the governor is removed, while all the CPUs of a policy are hotplugged out, this pointer wouldn't be valid anymore. And if we try to read the 'scaling_governor', etc. from sysfs, it will result in kernel OOPs. To prevent this, mark policy->governor as NULL for all inactive policies while the governor is removed from kernel. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 16275ba..c08de5e 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1083,7 +1083,6 @@ static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu) if (likely(policy)) { /* Policy should be inactive here */ WARN_ON(!policy_is_inactive(policy)); - policy->governor = NULL; } return policy; @@ -2145,8 +2144,10 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor) /* clear last_governor for all inactive policies */ read_lock_irqsave(&cpufreq_driver_lock, flags); for_each_inactive_policy(policy) { - if (!strcmp(policy->last_governor, governor->name)) + if (!strcmp(policy->last_governor, governor->name)) { + policy->governor = NULL; strcpy(policy->last_governor, "\0"); + } } read_unlock_irqrestore(&cpufreq_driver_lock, flags); -- cgit v0.10.2 From 7c0b2595dabb25f96813a12940fb156134be1102 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 15 May 2015 14:16:17 +0800 Subject: ACPI / EC: Update acpi_ec_is_gpe_raised() with new GPE status flag. This patch updates acpi_ec_is_gpe_raised() according to the following commit: Commit: 09af8e8290deaff821ced01ea83594ee4c21e8df Subject: ACPICA: Events: Add support to return both enable/status register values for GPE and fixed event. This is actually a no-op change as both the flags are defined to a same value. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 5e8fed4..99084e8 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -267,7 +267,7 @@ static inline bool acpi_ec_is_gpe_raised(struct acpi_ec *ec) acpi_event_status gpe_status = 0; (void)acpi_get_gpe_status(NULL, ec->gpe, &gpe_status); - return (gpe_status & ACPI_EVENT_FLAG_SET) ? true : false; + return (gpe_status & ACPI_EVENT_FLAG_STATUS_SET) ? true : false; } static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open) -- cgit v0.10.2 From 5ab82a11e58b6af704976f5d50ce098472f8abbd Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 15 May 2015 14:16:27 +0800 Subject: ACPI / EC: Remove storming threashold enlarging quirk. This patch removes the storming threashold enlarging quirk. After applying the following commit, we can notice that there is no no-op GPE handling invocation can be observed, thus it is unlikely that the no-op counts can exceed the storming threashold: Commit: ca37bfdfbc8d0a3ec73e4b97bb26dcfa51d515aa Subject: ACPI / EC: Fix several GPE handling issues by deploying ACPI_GPE_DISPATCH_RAW_HANDLER mode. Even when the storming happens, we have already limited its affection to the only transaction and no further transactions will be affected. This is done by this commit: Commit: e1d4d90fc0313d3d58cbd7912c90f8ef24df45ff Subject: ACPI / EC: Refine command storm prevention support So it's time to remove this quirk. Link: https://bugzilla.kernel.org/show_bug.cgi?id=45151 Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 99084e8..170d743 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1247,17 +1247,6 @@ static int ec_flag_msi(const struct dmi_system_id *id) } /* - * Clevo M720 notebook actually works ok with IRQ mode, if we lifted - * the GPE storm threshold back to 20 - */ -static int ec_enlarge_storm_threshold(const struct dmi_system_id *id) -{ - pr_debug("Setting the EC GPE storm threshold to 20\n"); - ec_storm_threshold = 20; - return 0; -} - -/* * Acer EC firmware refuses to respond QR_EC when SCI_EVT is not set, for * which case, we complete the QR_EC without issuing it to the firmware. * https://bugzilla.kernel.org/show_bug.cgi?id=86211 @@ -1329,10 +1318,6 @@ static struct dmi_system_id ec_dmi_table[] __initdata = { ec_validate_ecdt, "ASUS hardware", { DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc.") }, NULL}, { - ec_enlarge_storm_threshold, "CLEVO hardware", { - DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."), - DMI_MATCH(DMI_PRODUCT_NAME, "M720T/M730T"),}, NULL}, - { ec_skip_dsdt_scan, "HP Folio 13", { DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), DMI_MATCH(DMI_PRODUCT_NAME, "HP Folio 13"),}, NULL}, -- cgit v0.10.2 From 373783e6e9394b0dd5050eba152f436e08577d69 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 15 May 2015 14:16:34 +0800 Subject: ACPI / EC: Remove irqs_disabled() check. The following commit merges polling and interrupt modes for EC driver: Commit: 2a84cb9852f52c0cd1c48bca41a8792d44ad06cc Mon Sep 17 00:00:00 2001 Subject: ACPI: EC: Merge IRQ and POLL modes The irqs_disabled() check introduced in it tries to fall into busy polling mode when the context of ec_poll() cannot sleep. Actually ec_poll() is ensured to be invoked in the contexts that can sleep (from a sysfs /sys/kernel/debug/ec/ec0/io access, or from acpi_evaluate_object(), or from acpi_ec_gpe_poller()). Without the MSI quirk, we never saw the udelay() logic invoked. Thus this check is useless and can be removed. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 170d743..20bd43f 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -504,8 +504,7 @@ static int ec_poll(struct acpi_ec *ec) msecs_to_jiffies(ec_delay); unsigned long usecs = ACPI_EC_UDELAY_POLL; do { - /* don't sleep with disabled interrupts */ - if (EC_FLAGS_MSI || irqs_disabled()) { + if (EC_FLAGS_MSI) { usecs = ACPI_EC_MSI_UDELAY; udelay(usecs); if (ec_transaction_completed(ec)) -- cgit v0.10.2 From d8d031a605bff183b76611e0d18e2ca7021fb99f Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 15 May 2015 14:16:42 +0800 Subject: ACPI / EC: Fix and clean up register access guarding logics. In the polling mode, EC driver shouldn't access the EC registers too frequently. Though this statement is concluded from the non-root caused bugs (see links below), we've maintained the register access guarding logics in the current EC driver. The guarding logics can be found here and there, makes it hard to root cause real timing issues. This patch collects the guarding logics into one single function so that all hidden logics related to this can be seen clearly. The current guarding related code also has several issues: 1. Per-transaction timestamp prevents inter-transaction guarding from being implemented in the same place. We have an inter-transaction udelay() in acpi_ec_transaction_unblocked(), this logic can be merged into ec_poll() if we can use per-device timestamp. This patch completes such merge to form a new ec_guard() function and collects all guarding related hidden logics in it. One hidden logic is: there is no inter-transaction guarding performed for non MSI quirk (wait polling mode), this patch skips inter-transaction guarding before wait_event_timeout() for the wait polling mode to reveal the hidden logic. The other hidden logic is: there is msleep() inter-transaction guarding performed when the GPE storming is observed. As after merging this commit: Commit: e1d4d90fc0313d3d58cbd7912c90f8ef24df45ff Subject: ACPI / EC: Refine command storm prevention support EC_FLAGS_COMMAND_STORM is ensured to be cleared after invoking acpi_ec_transaction_unlocked(), the msleep() guard logic will never happen now. Since no one complains such change, this logic is likely added during the old times where the EC race issues are not fixed and the bugs are false root-caused to the timing issue. This patch simply removes the out-dated logic. We can restore it by stop skipping inter-transaction guarding for wait polling mode. Two different delay values are defined for msleep() and udelay() while they are merged in this patch to 550us. 2. time_after() causes additional delay in the polling mode (can only be observed in noirq suspend/resume processes where polling mode is always used) before advance_transaction() is invoked ("wait polling" log is added before wait_event_timeout()). We can see 2 wait_event_timeout() invocations. This is because time_after() ensures a ">" validation while we only need a ">=" validation here: [ 86.739909] ACPI: Waking up from system sleep state S3 [ 86.742857] ACPI : EC: 2: Increase command [ 86.742859] ACPI : EC: ***** Command(RD_EC) started ***** [ 86.742861] ACPI : EC: ===== TASK (0) ===== [ 86.742871] ACPI : EC: EC_SC(R) = 0x20 SCI_EVT=1 BURST=0 CMD=0 IBF=0 OBF=0 [ 86.742873] ACPI : EC: EC_SC(W) = 0x80 [ 86.742876] ACPI : EC: ***** Event started ***** [ 86.742880] ACPI : EC: ~~~~~ wait polling ~~~~~ [ 86.743972] ACPI : EC: ~~~~~ wait polling ~~~~~ [ 86.747966] ACPI : EC: ===== TASK (0) ===== [ 86.747977] ACPI : EC: EC_SC(R) = 0x20 SCI_EVT=1 BURST=0 CMD=0 IBF=0 OBF=0 [ 86.747978] ACPI : EC: EC_DATA(W) = 0x06 [ 86.747981] ACPI : EC: ~~~~~ wait polling ~~~~~ [ 86.751971] ACPI : EC: ~~~~~ wait polling ~~~~~ [ 86.755969] ACPI : EC: ===== TASK (0) ===== [ 86.755991] ACPI : EC: EC_SC(R) = 0x21 SCI_EVT=1 BURST=0 CMD=0 IBF=0 OBF=1 [ 86.755993] ACPI : EC: EC_DATA(R) = 0x03 [ 86.755994] ACPI : EC: ~~~~~ wait polling ~~~~~ [ 86.755995] ACPI : EC: ***** Command(RD_EC) stopped ***** [ 86.755996] ACPI : EC: 1: Decrease command This patch corrects this by using time_before() instead in ec_guard(): [ 54.283146] ACPI: Waking up from system sleep state S3 [ 54.285414] ACPI : EC: 2: Increase command [ 54.285415] ACPI : EC: ***** Command(RD_EC) started ***** [ 54.285416] ACPI : EC: ~~~~~ wait polling ~~~~~ [ 54.285417] ACPI : EC: ===== TASK (0) ===== [ 54.285424] ACPI : EC: EC_SC(R) = 0x20 SCI_EVT=1 BURST=0 CMD=0 IBF=0 OBF=0 [ 54.285425] ACPI : EC: EC_SC(W) = 0x80 [ 54.285427] ACPI : EC: ***** Event started ***** [ 54.285429] ACPI : EC: ~~~~~ wait polling ~~~~~ [ 54.287209] ACPI : EC: ===== TASK (0) ===== [ 54.287218] ACPI : EC: EC_SC(R) = 0x20 SCI_EVT=1 BURST=0 CMD=0 IBF=0 OBF=0 [ 54.287219] ACPI : EC: EC_DATA(W) = 0x06 [ 54.287222] ACPI : EC: ~~~~~ wait polling ~~~~~ [ 54.291190] ACPI : EC: ===== TASK (0) ===== [ 54.291210] ACPI : EC: EC_SC(R) = 0x21 SCI_EVT=1 BURST=0 CMD=0 IBF=0 OBF=1 [ 54.291213] ACPI : EC: EC_DATA(R) = 0x03 [ 54.291214] ACPI : EC: ~~~~~ wait polling ~~~~~ [ 54.291215] ACPI : EC: ***** Command(RD_EC) stopped ***** [ 54.291216] ACPI : EC: 1: Decrease command After cleaning up all guarding logics, we have one single function ec_guard() collecting all old, non-root-caused, hidden logics. Then we can easily tune the logics in one place to respond to the bug reports. Except the time_before() change, all other changes do not change the behavior of the EC driver. Link: https://bugzilla.kernel.org/show_bug.cgi?id=12011 Link: https://bugzilla.kernel.org/show_bug.cgi?id=20242 Link: https://bugzilla.kernel.org/show_bug.cgi?id=77431 Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 20bd43f..a521b6b 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -70,8 +70,7 @@ enum ec_command { #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ -#define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ -#define ACPI_EC_UDELAY_POLL 1000 /* Wait 1ms for EC transaction polling */ +#define ACPI_EC_UDELAY_POLL 550 /* Wait 1ms for EC transaction polling */ #define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query * when trying to clear the EC */ @@ -121,7 +120,6 @@ struct transaction { u8 wlen; u8 rlen; u8 flags; - unsigned long timestamp; }; static int acpi_ec_query(struct acpi_ec *ec, u8 *data); @@ -218,7 +216,7 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec) { u8 x = inb(ec->data_addr); - ec->curr->timestamp = jiffies; + ec->timestamp = jiffies; ec_dbg_raw("EC_DATA(R) = 0x%2.2x", x); return x; } @@ -227,14 +225,14 @@ static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) { ec_dbg_raw("EC_SC(W) = 0x%2.2x", command); outb(command, ec->command_addr); - ec->curr->timestamp = jiffies; + ec->timestamp = jiffies; } static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) { ec_dbg_raw("EC_DATA(W) = 0x%2.2x", data); outb(data, ec->data_addr); - ec->curr->timestamp = jiffies; + ec->timestamp = jiffies; } #ifdef DEBUG @@ -392,6 +390,18 @@ static void acpi_ec_complete_query(struct acpi_ec *ec) } } +static int ec_transaction_polled(struct acpi_ec *ec) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&ec->lock, flags); + if (ec->curr && (ec->curr->flags & ACPI_EC_COMMAND_POLL)) + ret = 1; + spin_unlock_irqrestore(&ec->lock, flags); + return ret; +} + static int ec_transaction_completed(struct acpi_ec *ec) { unsigned long flags; @@ -490,8 +500,37 @@ static void start_transaction(struct acpi_ec *ec) { ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; ec->curr->flags = 0; - ec->curr->timestamp = jiffies; - advance_transaction(ec); +} + +static int ec_guard(struct acpi_ec *ec) +{ + unsigned long guard = usecs_to_jiffies(ACPI_EC_UDELAY_POLL); + unsigned long timeout = ec->timestamp + guard; + + do { + if (EC_FLAGS_MSI) { + /* Perform busy polling */ + if (ec_transaction_completed(ec)) + return 0; + udelay(jiffies_to_usecs(guard)); + } else { + /* + * Perform wait polling + * + * The following check is there to keep the old + * logic - no inter-transaction guarding for the + * wait polling mode. + */ + if (!ec_transaction_polled(ec)) + break; + if (wait_event_timeout(ec->wait, + ec_transaction_completed(ec), + guard)) + return 0; + } + /* Guard the register accesses for the polling modes */ + } while (time_before(jiffies, timeout)); + return -ETIME; } static int ec_poll(struct acpi_ec *ec) @@ -502,24 +541,11 @@ static int ec_poll(struct acpi_ec *ec) while (repeat--) { unsigned long delay = jiffies + msecs_to_jiffies(ec_delay); - unsigned long usecs = ACPI_EC_UDELAY_POLL; do { - if (EC_FLAGS_MSI) { - usecs = ACPI_EC_MSI_UDELAY; - udelay(usecs); - if (ec_transaction_completed(ec)) - return 0; - } else { - if (wait_event_timeout(ec->wait, - ec_transaction_completed(ec), - usecs_to_jiffies(usecs))) - return 0; - } + if (!ec_guard(ec)) + return 0; spin_lock_irqsave(&ec->lock, flags); - if (time_after(jiffies, - ec->curr->timestamp + - usecs_to_jiffies(usecs))) - advance_transaction(ec); + advance_transaction(ec); spin_unlock_irqrestore(&ec->lock, flags); } while (time_before(jiffies, delay)); pr_debug("controller reset, restart transaction\n"); @@ -536,8 +562,6 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, unsigned long tmp; int ret = 0; - if (EC_FLAGS_MSI) - udelay(ACPI_EC_MSI_UDELAY); /* start transaction */ spin_lock_irqsave(&ec->lock, tmp); /* Enable GPE for command processing (IBF=0/OBF=1) */ @@ -551,7 +575,9 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, ec_dbg_req("Command(%s) started", acpi_ec_cmd_string(t->command)); start_transaction(ec); spin_unlock_irqrestore(&ec->lock, tmp); + ret = ec_poll(ec); + spin_lock_irqsave(&ec->lock, tmp); if (t->irq_count == ec_storm_threshold) acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM); @@ -574,6 +600,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) return -EINVAL; if (t->rdata) memset(t->rdata, 0, t->rlen); + mutex_lock(&ec->mutex); if (ec->global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); @@ -585,8 +612,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) status = acpi_ec_transaction_unlocked(ec, t); - if (test_bit(EC_FLAGS_COMMAND_STORM, &ec->flags)) - msleep(1); if (ec->global_lock) acpi_release_global_lock(glk); unlock: @@ -1002,6 +1027,7 @@ static struct acpi_ec *make_acpi_ec(void) INIT_LIST_HEAD(&ec->list); spin_lock_init(&ec->lock); INIT_WORK(&ec->work, acpi_ec_gpe_poller); + ec->timestamp = jiffies; return ec; } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index ba4a61e..61cb506 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -138,6 +138,7 @@ struct acpi_ec { struct transaction *curr; spinlock_t lock; struct work_struct work; + unsigned long timestamp; }; extern struct acpi_ec *first_ec; -- cgit v0.10.2 From 15de603b04b229b5582fd148fd851801a79472cc Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 15 May 2015 14:16:48 +0800 Subject: ACPI / EC: Add module params for polling modes. We have 2 polling modes in the EC driver: 1. busy polling: originally used for the MSI quirks. udelay() is used to perform register access guarding. 2. wait polling: normal code path uses wait_event_timeout() and it can be woken up as soon as the transaction is completed in the interrupt mode. It also contains the register acces guarding logic in case the interrupt doesn't arrive and the EC driver is about to advance the transaction in task context (the polling mode). The wait polling is useful for interrupt mode to allow other tasks to use the CPU during the wait. But for the polling mode, the busy polling takes less time than the wait polling, because if no interrupt arrives, the wait polling has to wait the minimal HZ interval. We have a new use case for using the busy polling mode. Some GPIO drivers initialize PIN configuration which cause a GPIO multiplexed EC GPE to be disabled out of the GPE register's control. Busy polling mode is useful here as it takes less time than the wait polling. But the guarding logic prevents it from responding even faster. We should spinning around the EC status rather than spinning around the nop execution lasted a determined period. This patch introduces 2 module params for the polling mode switch and the guard time, so that users can use the busy polling mode without the guarding in case the guarding is not necessary. This is an example to use the 2 module params for this purpose: acpi.ec_busy_polling acpi.ec_polling_guard=0 We've tested the patch on a test platform. The platform suffers from such kind of the GPIO PIN issue. The GPIO driver resets all PIN configuration and after that, EC interrupt cannot arrive because of the multiplexing. Then the platform suffers from a long delay carried out by the wait_event_timeout() as all further EC transactions will run in the polling mode. We switched the EC driver to use the busy polling mechanism instead of the wait timeout polling mechanism and the delay is still high: [ 44.283005] calling PNP0C0B:00+ @ 1305, parent: platform [ 44.417548] call PNP0C0B:00+ returned 0 after 131323 usecs And this patch can significantly reduce the delay: [ 44.502625] calling PNP0C0B:00+ @ 1308, parent: platform [ 44.503760] call PNP0C0B:00+ returned 0 after 1103 usecs Tested-by: Chen Yu Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index a521b6b..846e0617 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -92,6 +92,14 @@ static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; module_param(ec_delay, uint, 0644); MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes"); +static bool ec_busy_polling __read_mostly; +module_param(ec_busy_polling, bool, 0644); +MODULE_PARM_DESC(ec_busy_polling, "Use busy polling to advance EC transaction"); + +static unsigned int ec_polling_guard __read_mostly = ACPI_EC_UDELAY_POLL; +module_param(ec_polling_guard, uint, 0644); +MODULE_PARM_DESC(ec_polling_guard, "Guard time(us) between EC accesses in polling modes"); + /* * If the number of false interrupts per one transaction exceeds * this threshold, will think there is a GPE storm happened and @@ -128,7 +136,6 @@ static void advance_transaction(struct acpi_ec *ec); struct acpi_ec *boot_ec, *first_ec; EXPORT_SYMBOL(first_ec); -static int EC_FLAGS_MSI; /* Out-of-spec MSI controller */ 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 */ @@ -504,11 +511,11 @@ static void start_transaction(struct acpi_ec *ec) static int ec_guard(struct acpi_ec *ec) { - unsigned long guard = usecs_to_jiffies(ACPI_EC_UDELAY_POLL); + unsigned long guard = usecs_to_jiffies(ec_polling_guard); unsigned long timeout = ec->timestamp + guard; do { - if (EC_FLAGS_MSI) { + if (ec_busy_polling) { /* Perform busy polling */ if (ec_transaction_completed(ec)) return 0; @@ -985,7 +992,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, if (function != ACPI_READ && function != ACPI_WRITE) return AE_BAD_PARAMETER; - if (EC_FLAGS_MSI || bits > 8) + if (ec_busy_polling || bits > 8) acpi_ec_burst_enable(ec); for (i = 0; i < bytes; ++i, ++address, ++value) @@ -993,7 +1000,7 @@ acpi_ec_space_handler(u32 function, acpi_physical_address address, acpi_ec_read(ec, address, value) : acpi_ec_write(ec, address, *value); - if (EC_FLAGS_MSI || bits > 8) + if (ec_busy_polling || bits > 8) acpi_ec_burst_disable(ec); switch (result) { @@ -1262,11 +1269,11 @@ static int ec_validate_ecdt(const struct dmi_system_id *id) return 0; } -/* MSI EC needs special treatment, enable it */ -static int ec_flag_msi(const struct dmi_system_id *id) +/* EC firmware needs special polling mode, enable it */ +static int ec_use_busy_polling(const struct dmi_system_id *id) { - pr_debug("Detected MSI hardware, enabling workarounds.\n"); - EC_FLAGS_MSI = 1; + pr_debug("Detected the EC firmware requiring busy polling mode.\n"); + ec_busy_polling = 1; EC_FLAGS_VALIDATE_ECDT = 1; return 0; } @@ -1313,27 +1320,27 @@ static struct dmi_system_id ec_dmi_table[] __initdata = { DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), DMI_MATCH(DMI_BOARD_NAME, "JFL92") }, NULL}, { - ec_flag_msi, "MSI hardware", { + ec_use_busy_polling, "MSI hardware", { DMI_MATCH(DMI_BIOS_VENDOR, "Micro-Star")}, NULL}, { - ec_flag_msi, "MSI hardware", { + ec_use_busy_polling, "MSI hardware", { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star")}, NULL}, { - ec_flag_msi, "MSI hardware", { + ec_use_busy_polling, "MSI hardware", { DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-Star")}, NULL}, { - ec_flag_msi, "MSI hardware", { + ec_use_busy_polling, "MSI hardware", { DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR")}, NULL}, { - ec_flag_msi, "Quanta hardware", { + ec_use_busy_polling, "Quanta hardware", { DMI_MATCH(DMI_SYS_VENDOR, "Quanta"), DMI_MATCH(DMI_PRODUCT_NAME, "TW8/SW8/DW8"),}, NULL}, { - ec_flag_msi, "Quanta hardware", { + ec_use_busy_polling, "Quanta hardware", { DMI_MATCH(DMI_SYS_VENDOR, "Quanta"), DMI_MATCH(DMI_PRODUCT_NAME, "TW9/SW9"),}, NULL}, { - ec_flag_msi, "Clevo W350etq", { + ec_use_busy_polling, "Clevo W350etq", { DMI_MATCH(DMI_SYS_VENDOR, "CLEVO CO."), DMI_MATCH(DMI_PRODUCT_NAME, "W35_37ET"),}, NULL}, { -- cgit v0.10.2 From 3174abcfea6a05aa25038156d6722b6c8876fb36 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Fri, 15 May 2015 14:37:11 +0800 Subject: ACPI / EC: Remove non-root-caused busy polling quirks. { Update to correct 1 patch subject in the description } We have fixed a lot of race issues in the EC driver recently. The following commit introduces MSI udelay()/msleep() quirk to MSI laptops to make EC firmware working for bug 12011 without root causing any EC driver race issues: Commit: 5423a0cb3f74c16e90683f8ee1cec6c240a9556e Subject: ACPI: EC: Add delay for slow MSI controller Commit: 34ff4dbccccce54c83b1234d39b7ad9e548a75dd Subject: ACPI: EC: Separate delays for MSI hardware The following commit extends ECDT validation quirk to MSI laptops to make EC driver locating EC registers properly for bug 12461: Commit: a5032bfdd9c80e0231a6324661e123818eb46ecd Subject: ACPI: EC: Always parse EC device This is a different quirk than the MSI udelay()/msleep() quirk. This patch keeps validating ECDT for only "Micro-Star MS-171F" as reported. The following commit extends MSI udelay()/msleep() quirk to Quanta laptops to make EC firmware working for bug 20242, there is no requirement to validate ECDT for Quanta laptops: Commit: 534bc4e3d27096e2f3fc00c14a20efd597837a4f Mon Sep 17 00:00:00 2001 Subject: ACPI EC: enable MSI workaround for Quanta laptops The following commit extends MSI udelay()/msleep() quirk to Clevo laptops to make EC firmware working for bug 77431, there is no requirement to validate ECDT for Clevo laptops: Commit: 777cb382958851c88763253fe00a26529be4c0e9 Subject: ACPI / EC: Add msi quirk for Clevo W350etq All udelay()/msleep() quirks for MSI/Quanta/Clevo seem to be the wrong fixes generated without fixing the EC driver race issues. And even if it is not wrong, the guarding can be covered by the following commits in wait polling mode: Commit: 9e295ac14d6a59180beed0735e6a504c2ee87761 Subject: ACPI / EC: Reduce ec_poll() by referencing the last register access timestamp. Commit: commit in the same series Subject: ACPI / EC: Fix and clean up register access guarding logics. The only case that is not covered is the inter-transaction guarding. And there is no evidence that we need the inter-transaction guarding upon reading the noted bug entries. So it is time to remove the quirks and let the users to try again. If there is a regression, the only thing we need to do is to restore the inter-transaction guarding for the reported platforms. Link: https://bugzilla.kernel.org/show_bug.cgi?id=12011 Link: https://bugzilla.kernel.org/show_bug.cgi?id=12461 Link: https://bugzilla.kernel.org/show_bug.cgi?id=20242 Link: https://bugzilla.kernel.org/show_bug.cgi?id=77431 Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 846e0617..149b5e7 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1269,15 +1269,6 @@ static int ec_validate_ecdt(const struct dmi_system_id *id) return 0; } -/* EC firmware needs special polling mode, enable it */ -static int ec_use_busy_polling(const struct dmi_system_id *id) -{ - pr_debug("Detected the EC firmware requiring busy polling mode.\n"); - ec_busy_polling = 1; - EC_FLAGS_VALIDATE_ECDT = 1; - return 0; -} - /* * Acer EC firmware refuses to respond QR_EC when SCI_EVT is not set, for * which case, we complete the QR_EC without issuing it to the firmware. @@ -1320,29 +1311,9 @@ static struct dmi_system_id ec_dmi_table[] __initdata = { DMI_MATCH(DMI_BIOS_VENDOR, "COMPAL"), DMI_MATCH(DMI_BOARD_NAME, "JFL92") }, NULL}, { - ec_use_busy_polling, "MSI hardware", { - DMI_MATCH(DMI_BIOS_VENDOR, "Micro-Star")}, NULL}, - { - ec_use_busy_polling, "MSI hardware", { - DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star")}, NULL}, - { - ec_use_busy_polling, "MSI hardware", { - DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-Star")}, NULL}, - { - ec_use_busy_polling, "MSI hardware", { - DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR")}, NULL}, - { - ec_use_busy_polling, "Quanta hardware", { - DMI_MATCH(DMI_SYS_VENDOR, "Quanta"), - DMI_MATCH(DMI_PRODUCT_NAME, "TW8/SW8/DW8"),}, NULL}, - { - ec_use_busy_polling, "Quanta hardware", { - DMI_MATCH(DMI_SYS_VENDOR, "Quanta"), - DMI_MATCH(DMI_PRODUCT_NAME, "TW9/SW9"),}, NULL}, - { - ec_use_busy_polling, "Clevo W350etq", { - DMI_MATCH(DMI_SYS_VENDOR, "CLEVO CO."), - DMI_MATCH(DMI_PRODUCT_NAME, "W35_37ET"),}, NULL}, + ec_validate_ecdt, "MSI MS-171F", { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-171F"),}, NULL}, { ec_validate_ecdt, "ASUS hardware", { DMI_MATCH(DMI_BIOS_VENDOR, "ASUS") }, NULL}, -- cgit v0.10.2 From 20dacb71ad283b9506ee7e01286a424999fb8309 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 May 2015 01:55:35 +0200 Subject: ACPI / PM: Rework device power management to follow ACPI 6 The ACPI 6 specification has made some changes in the device power management area. In particular: * The D3hot power state is now supposed to be always available (instead of D3cold) and D3cold is only regarded as valid if the _PR3 object is present for the given device. * The required ordering of transitions into power states deeper than D0 is now such that for a transition into state Dx the _PSx method is supposed to be executed first, if present, and the states of the power resources the device depends on are supposed to be changed after that. * It is now explicitly forbidden to transition devices from lower-power (deeper) into higher-power (shallower) power states other than D0. Those changes have been made so the specification reflects the Windows' device power management code that the vast majority of systems using ACPI is validated against. To avoid artificial differences in ACPI device power management between Windows and Linux, modify the ACPI device power management code to follow the new specification. Add comments explaining the code flow in some unclear places. This only may affect some real corner cases in which the OS behavior expected by the firmware is different from the Windows one, but that's quite unlikely. The transition ordering change affects transitions to D1 and D2 which are rarely used (if at all) and into D3hot and D3cold for devices actually having _PR3, but those are likely to be validated against Windows anyway. The other changes may affect code calling acpi_device_get_power() or acpi_device_update_power() where ACPI_STATE_D3_HOT may be returned instead of ACPI_STATE_D3_COLD (that's why the ACPI fan driver needs to be updated too) and since transitions into ACPI_STATE_D3_HOT may remove power now, it is better to avoid this one in acpi_pm_device_sleep_state() if the "no power off" PM QoS flag is set. The only existing user of acpi_device_can_poweroff() really cares about the case when _PR3 is present, so the change in that function should not cause any problems to happen too. A plus is that PCI_D3hot can be mapped to ACPI_STATE_D3_HOT now and the compatibility with older systems should be covered automatically. In any case, if any real problems result from this, it still will be better to follow the Windows' behavior (which now is reflected by the specification too) in general and handle the cases when it doesn't work via quirks. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 735db11..87c16a5 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -98,17 +98,16 @@ int acpi_device_get_power(struct acpi_device *device, int *state) /* * The power resources settings may indicate a power state - * shallower than the actual power state of the device. + * shallower than the actual power state of the device, because + * the same power resources may be referenced by other devices. * - * Moreover, on systems predating ACPI 4.0, if the device - * doesn't depend on any power resources and _PSC returns 3, - * that means "power off". We need to maintain compatibility - * with those systems. + * For systems predating ACPI 4.0 we assume that D3hot is the + * deepest state that can be supported. */ if (psc > result && psc < ACPI_STATE_D3_COLD) result = psc; else if (result == ACPI_STATE_UNKNOWN) - result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc; + result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_HOT : psc; } /* @@ -153,8 +152,8 @@ static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state) */ int acpi_device_set_power(struct acpi_device *device, int state) { + int target_state = state; int result = 0; - bool cut_power = false; if (!device || !device->flags.power_manageable || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) @@ -169,11 +168,21 @@ int acpi_device_set_power(struct acpi_device *device, int state) return 0; } - if (!device->power.states[state].flags.valid) { + if (state == ACPI_STATE_D3_COLD) { + /* + * For transitions to D3cold we need to execute _PS3 and then + * possibly drop references to the power resources in use. + */ + state = ACPI_STATE_D3_HOT; + /* If _PR3 is not available, use D3hot as the target state. */ + if (!device->power.states[ACPI_STATE_D3_COLD].flags.valid) + target_state = state; + } else if (!device->power.states[state].flags.valid) { dev_warn(&device->dev, "Power state %s not supported\n", acpi_power_state_string(state)); return -ENODEV; } + if (!device->power.flags.ignore_parent && device->parent && (state < device->parent->power.state)) { dev_warn(&device->dev, @@ -183,39 +192,38 @@ int acpi_device_set_power(struct acpi_device *device, int state) return -ENODEV; } - /* For D3cold we should first transition into D3hot. */ - if (state == ACPI_STATE_D3_COLD - && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) { - state = ACPI_STATE_D3_HOT; - cut_power = true; - } - - if (state < device->power.state && state != ACPI_STATE_D0 - && device->power.state >= ACPI_STATE_D3_HOT) { - dev_warn(&device->dev, - "Cannot transition to non-D0 state from D3\n"); - return -ENODEV; - } - /* * Transition Power * ---------------- - * In accordance with the ACPI specification first apply power (via - * power resources) and then evaluate _PSx. + * In accordance with ACPI 6, _PSx is executed before manipulating power + * resources, unless the target state is D0, in which case _PS0 is + * supposed to be executed after turning the power resources on. */ - if (device->power.flags.power_resources) { - result = acpi_power_transition(device, state); + if (state > ACPI_STATE_D0) { + /* + * According to ACPI 6, devices cannot go from lower-power + * (deeper) states to higher-power (shallower) states. + */ + if (state < device->power.state) { + dev_warn(&device->dev, "Cannot transition from %s to %s\n", + acpi_power_state_string(device->power.state), + acpi_power_state_string(state)); + return -ENODEV; + } + + result = acpi_dev_pm_explicit_set(device, state); if (result) goto end; - } - result = acpi_dev_pm_explicit_set(device, state); - if (result) - goto end; - if (cut_power) { - device->power.state = state; - state = ACPI_STATE_D3_COLD; - result = acpi_power_transition(device, state); + if (device->power.flags.power_resources) + result = acpi_power_transition(device, target_state); + } else { + if (device->power.flags.power_resources) { + result = acpi_power_transition(device, ACPI_STATE_D0); + if (result) + goto end; + } + result = acpi_dev_pm_explicit_set(device, ACPI_STATE_D0); } end: @@ -264,13 +272,24 @@ int acpi_bus_init_power(struct acpi_device *device) return result; if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) { + /* Reference count the power resources. */ result = acpi_power_on_resources(device, state); if (result) return result; - result = acpi_dev_pm_explicit_set(device, state); - if (result) - return result; + if (state == ACPI_STATE_D0) { + /* + * If _PSC is not present and the state inferred from + * power resources appears to be D0, it still may be + * necessary to execute _PS0 at this point, because + * another device using the same power resources may + * have been put into D0 previously and that's why we + * see D0 here. + */ + result = acpi_dev_pm_explicit_set(device, state); + if (result) + return result; + } } else if (state == ACPI_STATE_UNKNOWN) { /* * No power resources and missing _PSC? Cross fingers and make @@ -603,12 +622,12 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD) return -EINVAL; - if (d_max_in > ACPI_STATE_D3_HOT) { + if (d_max_in > ACPI_STATE_D2) { enum pm_qos_flags_status stat; stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); if (stat == PM_QOS_FLAGS_ALL) - d_max_in = ACPI_STATE_D3_HOT; + d_max_in = ACPI_STATE_D2; } adev = ACPI_COMPANION(dev); diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 7a36f02..bea0bba 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -158,8 +158,9 @@ static int fan_get_state(struct acpi_device *device, unsigned long *state) if (result) return result; - *state = (acpi_state == ACPI_STATE_D3_COLD ? 0 : - (acpi_state == ACPI_STATE_D0 ? 1 : -1)); + *state = acpi_state == ACPI_STATE_D3_COLD + || acpi_state == ACPI_STATE_D3_HOT ? + 0 : (acpi_state == ACPI_STATE_D0 ? 1 : -1); return 0; } diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 59a6bf7..1f8138f 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -684,7 +684,8 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) } } - *state = ACPI_STATE_D3_COLD; + *state = device->power.states[ACPI_STATE_D3_COLD].flags.valid ? + ACPI_STATE_D3_COLD : ACPI_STATE_D3_HOT; return 0; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 03141aa..ccf15d7 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1766,15 +1766,9 @@ static void acpi_bus_init_power_state(struct acpi_device *device, int state) if (acpi_has_method(device->handle, pathname)) ps->flags.explicit_set = 1; - /* - * State is valid if there are means to put the device into it. - * D3hot is only valid if _PR3 present. - */ - if (!list_empty(&ps->resources) - || (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) { + /* State is valid if there are means to put the device into it. */ + if (!list_empty(&ps->resources) || ps->flags.explicit_set) ps->flags.valid = 1; - ps->flags.os_accessible = 1; - } ps->power = -1; /* Unknown - driver assigned */ ps->latency = -1; /* Unknown - driver assigned */ @@ -1810,21 +1804,13 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) acpi_bus_init_power_state(device, i); INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources); + if (!list_empty(&device->power.states[ACPI_STATE_D3_HOT].resources)) + device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; - /* Set defaults for D0 and D3 states (always valid) */ + /* Set defaults for D0 and D3hot states (always valid) */ device->power.states[ACPI_STATE_D0].flags.valid = 1; device->power.states[ACPI_STATE_D0].power = 100; - device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; - device->power.states[ACPI_STATE_D3_COLD].power = 0; - - /* Set D3cold's explicit_set flag if _PS3 exists. */ - if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set) - device->power.states[ACPI_STATE_D3_COLD].flags.explicit_set = 1; - - /* Presence of _PS3 or _PRx means we can put the device into D3 cold */ - if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set || - device->power.flags.power_resources) - device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1; + device->power.states[ACPI_STATE_D3_HOT].flags.valid = 1; if (acpi_bus_init_power(device)) device->flags.power_manageable = 0; diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 6f6f175..314a625 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -420,7 +420,7 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) [PCI_D0] = ACPI_STATE_D0, [PCI_D1] = ACPI_STATE_D1, [PCI_D2] = ACPI_STATE_D2, - [PCI_D3hot] = ACPI_STATE_D3_COLD, + [PCI_D3hot] = ACPI_STATE_D3_HOT, [PCI_D3cold] = ACPI_STATE_D3_COLD, }; int error = -EINVAL; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 8de4fa9..1ba841f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -271,7 +271,6 @@ struct acpi_device_power_flags { struct acpi_device_power_state { struct { u8 valid:1; - u8 os_accessible:1; u8 explicit_set:1; /* _PSx present? */ u8 reserved:6; } flags; @@ -601,7 +600,7 @@ static inline bool acpi_device_can_wakeup(struct acpi_device *adev) static inline bool acpi_device_can_poweroff(struct acpi_device *adev) { - return adev->power.states[ACPI_STATE_D3_COLD].flags.os_accessible; + return adev->power.states[ACPI_STATE_D3_COLD].flags.valid; } #else /* CONFIG_ACPI */ -- cgit v0.10.2 From 9c82e83e6b9d999f13c93083ab47b14209662c4b Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Wed, 6 May 2015 10:57:53 +0800 Subject: ACPICA: Divergence: Remove redundant definitions. There are two same "define"s in the actypes.h for ACPI_USE_NATIVE_DIVIDE, this patch removes one of them as it is useless and is not in the ACPICA upstream. It is likely that the useless block is there because of the issues in the old ACPICA release process. Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 1c3002e..a9d33e8 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -471,11 +471,6 @@ typedef u8 acpi_owner_id; #define ACPI_INTEGER_BIT_SIZE 64 #define ACPI_MAX_DECIMAL_DIGITS 20 /* 2^64 = 18,446,744,073,709,551,616 */ - -#if ACPI_MACHINE_WIDTH == 64 -#define ACPI_USE_NATIVE_DIVIDE /* Use compiler native 64-bit divide */ -#endif - #define ACPI_MAX64_DECIMAL_DIGITS 20 #define ACPI_MAX32_DECIMAL_DIGITS 10 #define ACPI_MAX16_DECIMAL_DIGITS 5 -- cgit v0.10.2 From 671767360db8fdd1f082d15fb4b0107c1bb94a0b Mon Sep 17 00:00:00 2001 From: Ruchi Kandoi Date: Thu, 7 May 2015 19:07:42 -0700 Subject: PM / sleep: Return -EBUSY from suspend_enter() on wakeup detection If a wakeup source is found to be pending in the last stage of suspend after syscore suspend, then the machine won't suspend, but suspend_enter() will return 0. That is confusing, as wakeup detection elsewhere causes -EBUSY to be returned from suspend_enter(). To avoid the confusion, make suspend_enter() return -EBUSY in that case too. Signed-off-by: Ruchi Kandoi [ rjw: Subject and changelog ] Signed-off-by: Rafael J. Wysocki diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 274371a..53266b7 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -366,6 +366,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup) trace_suspend_resume(TPS("machine_suspend"), state, false); events_check_enabled = false; + } else if (*wakeup) { + error = -EBUSY; } syscore_resume(); } -- cgit v0.10.2 From 87e9b9f1d86c2ee9a10c2a4186a72d0af4cc963e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 May 2015 01:38:15 +0200 Subject: PM / sleep: Make suspend-to-idle-specific code depend on CONFIG_SUSPEND Since idle_should_freeze() is defined to always return 'false' for CONFIG_SUSPEND unset, all of the code depending on it in cpuidle_idle_call() is not necessary in that case. Make that code depend on CONFIG_SUSPEND too to avoid building it when it is not going to be used. Signed-off-by: Rafael J. Wysocki Acked-by: Thomas Gleixner diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 61c417b..71459f5 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -92,6 +92,7 @@ static int find_deepest_state(struct cpuidle_driver *drv, return ret; } +#ifdef CONFIG_SUSPEND /** * cpuidle_find_deepest_state - Find the deepest available idle state. * @drv: cpuidle driver for the given CPU. @@ -145,6 +146,7 @@ int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev) return index; } +#endif /* CONFIG_SUSPEND */ /** * cpuidle_enter_state - enter the state and update stats diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 9c5e892..13ee266 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -151,10 +151,6 @@ extern void cpuidle_resume(void); extern int cpuidle_enable_device(struct cpuidle_device *dev); extern void cpuidle_disable_device(struct cpuidle_device *dev); extern int cpuidle_play_dead(void); -extern int cpuidle_find_deepest_state(struct cpuidle_driver *drv, - struct cpuidle_device *dev); -extern int cpuidle_enter_freeze(struct cpuidle_driver *drv, - struct cpuidle_device *dev); extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev); #else @@ -190,14 +186,22 @@ static inline int cpuidle_enable_device(struct cpuidle_device *dev) {return -ENODEV; } static inline void cpuidle_disable_device(struct cpuidle_device *dev) { } static inline int cpuidle_play_dead(void) {return -ENODEV; } +static inline struct cpuidle_driver *cpuidle_get_cpu_driver( + struct cpuidle_device *dev) {return NULL; } +#endif + +#if defined(CONFIG_CPU_IDLE) && defined(CONFIG_SUSPEND) +extern int cpuidle_find_deepest_state(struct cpuidle_driver *drv, + struct cpuidle_device *dev); +extern int cpuidle_enter_freeze(struct cpuidle_driver *drv, + struct cpuidle_device *dev); +#else static inline int cpuidle_find_deepest_state(struct cpuidle_driver *drv, struct cpuidle_device *dev) {return -ENODEV; } static inline int cpuidle_enter_freeze(struct cpuidle_driver *drv, struct cpuidle_device *dev) {return -ENODEV; } -static inline struct cpuidle_driver *cpuidle_get_cpu_driver( - struct cpuidle_device *dev) {return NULL; } #endif #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED diff --git a/include/linux/tick.h b/include/linux/tick.h index f8492da5..ec6e8bc 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h @@ -13,8 +13,6 @@ #ifdef CONFIG_GENERIC_CLOCKEVENTS extern void __init tick_init(void); -extern void tick_freeze(void); -extern void tick_unfreeze(void); /* Should be core only, but ARM BL switcher requires it */ extern void tick_suspend_local(void); /* Should be core only, but XEN resume magic and ARM BL switcher require it */ @@ -23,14 +21,20 @@ extern void tick_handover_do_timer(void); extern void tick_cleanup_dead_cpu(int cpu); #else /* CONFIG_GENERIC_CLOCKEVENTS */ static inline void tick_init(void) { } -static inline void tick_freeze(void) { } -static inline void tick_unfreeze(void) { } static inline void tick_suspend_local(void) { } static inline void tick_resume_local(void) { } static inline void tick_handover_do_timer(void) { } static inline void tick_cleanup_dead_cpu(int cpu) { } #endif /* !CONFIG_GENERIC_CLOCKEVENTS */ +#if defined(CONFIG_GENERIC_CLOCKEVENTS) && defined(CONFIG_SUSPEND) +extern void tick_freeze(void); +extern void tick_unfreeze(void); +#else +static inline void tick_freeze(void) { } +static inline void tick_unfreeze(void) { } +#endif + #ifdef CONFIG_TICK_ONESHOT extern void tick_irq_enter(void); # ifndef arch_needs_cpu diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 80c0430..5150846 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -441,6 +441,7 @@ void tick_resume(void) tick_resume_local(); } +#ifdef CONFIG_SUSPEND static DEFINE_RAW_SPINLOCK(tick_freeze_lock); static unsigned int tick_freeze_depth; @@ -494,6 +495,7 @@ void tick_unfreeze(void) raw_spin_unlock(&tick_freeze_lock); } +#endif /* CONFIG_SUSPEND */ /** * tick_init - initialize the tick control -- cgit v0.10.2 From 7f436055cb0c0e17c430fb81197b42e20c2e812c Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 15 May 2015 18:10:37 -0700 Subject: PM / wakeup: add a dummy wakeup_source to record statistics After a wakeup_source is destroyed, we lost all information such as how long this wakeup_source has been active. Add a dummy wakeup_source to record such info. Signed-off-by: Jin Qian Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 7b5ad9a..87c2603 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -56,6 +56,11 @@ static LIST_HEAD(wakeup_sources); static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue); +static struct wakeup_source deleted_ws = { + .name = "deleted", + .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock), +}; + /** * wakeup_source_prepare - Prepare a new wakeup source for initialization. * @ws: Wakeup source to prepare. @@ -107,6 +112,34 @@ void wakeup_source_drop(struct wakeup_source *ws) } EXPORT_SYMBOL_GPL(wakeup_source_drop); +/* + * Record wakeup_source statistics being deleted into a dummy wakeup_source. + */ +static void wakeup_source_record(struct wakeup_source *ws) +{ + unsigned long flags; + + spin_lock_irqsave(&deleted_ws.lock, flags); + + if (ws->event_count) { + deleted_ws.total_time = + ktime_add(deleted_ws.total_time, ws->total_time); + deleted_ws.prevent_sleep_time = + ktime_add(deleted_ws.prevent_sleep_time, + ws->prevent_sleep_time); + deleted_ws.max_time = + ktime_compare(deleted_ws.max_time, ws->max_time) > 0 ? + deleted_ws.max_time : ws->max_time; + deleted_ws.event_count += ws->event_count; + deleted_ws.active_count += ws->active_count; + deleted_ws.relax_count += ws->relax_count; + deleted_ws.expire_count += ws->expire_count; + deleted_ws.wakeup_count += ws->wakeup_count; + } + + spin_unlock_irqrestore(&deleted_ws.lock, flags); +} + /** * wakeup_source_destroy - Destroy a struct wakeup_source object. * @ws: Wakeup source to destroy. @@ -119,6 +152,7 @@ void wakeup_source_destroy(struct wakeup_source *ws) return; wakeup_source_drop(ws); + wakeup_source_record(ws); kfree(ws->name); kfree(ws); } @@ -912,6 +946,8 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) print_wakeup_source_stats(m, ws); rcu_read_unlock(); + print_wakeup_source_stats(m, &deleted_ws); + return 0; } -- cgit v0.10.2 From 3fc3a0be0dab352e065d1dad7d3f81953ed0d4bc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 May 2015 10:47:43 +0200 Subject: PM / clk: Fix clock error check in __pm_clk_add() In the final iteration of commit 245bd6f6af8a62a2 ("PM / clock_ops: Add pm_clk_add_clk()"), a refcount increment was added by Grygorii Strashko. However, the accompanying IS_ERR() check operates on the wrong clock pointer, which is always zero at this point, i.e. not an error. This may lead to a NULL pointer dereference later, when __clk_get() tries to dereference an error pointer. Check the passed clock pointer instead to fix this. Signed-off-by: Geert Uytterhoeven Fixes: 245bd6f6af8a62a2 ("PM / clock_ops: Add pm_clk_add_clk()") Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 8abea66..442ce01 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -94,7 +94,7 @@ static int __pm_clk_add(struct device *dev, const char *con_id, return -ENOMEM; } } else { - if (IS_ERR(ce->clk) || !__clk_get(clk)) { + if (IS_ERR(clk) || !__clk_get(clk)) { kfree(ce); return -ENOENT; } -- cgit v0.10.2 From 56f487c78015936097474fd89b2ccb229d500d0f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 13 May 2015 16:36:32 -0700 Subject: PM / Runtime: Update last_busy in rpm_resume If we don't update last_busy in rpm_resume, devices can go back to sleep immediately after resume. This happens at least in cases where the device has been powered off and does not have any interrupt pending until there's something in the FIFO. Signed-off-by: Tony Lindgren Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 5070c4f..4ffe4a2 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -741,6 +741,7 @@ static int rpm_resume(struct device *dev, int rpmflags) } else { no_callback: __update_runtime_status(dev, RPM_ACTIVE); + pm_runtime_mark_last_busy(dev); if (parent) atomic_inc(&parent->power.child_count); } -- cgit v0.10.2 From 4990d4fe327b9d9a7a3be7103a82699406fdde69 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 18 May 2015 15:40:29 -0700 Subject: PM / Wakeirq: Add automated device wake IRQ handling Turns out we can automate the handling for the device_may_wakeup() quite a bit by using the kernel wakeup source list as suggested by Rafael J. Wysocki . And as some hardware has separate dedicated wake-up interrupt in addition to the IO interrupt, we can automate the handling by adding a generic threaded interrupt handler that just calls the device PM runtime to wake up the device. This allows dropping code from device drivers as we currently are doing it in multiple ways, and often wrong. For most drivers, we should be able to drop the following boilerplate code from runtime_suspend and runtime_resume functions: ... device_init_wakeup(dev, true); ... if (device_may_wakeup(dev)) enable_irq_wake(irq); ... if (device_may_wakeup(dev)) disable_irq_wake(irq); ... device_init_wakeup(dev, false); ... We can replace it with just the following init and exit time code: ... device_init_wakeup(dev, true); dev_pm_set_wake_irq(dev, irq); ... dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); ... And for hardware with dedicated wake-up interrupts: ... device_init_wakeup(dev, true); dev_pm_set_dedicated_wake_irq(dev, irq); ... dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); ... Signed-off-by: Tony Lindgren Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 1cb8544..f94a6cc 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o +obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o qos.o runtime.o wakeirq.o obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp.o diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 3d874ec..6f2515c 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -587,6 +588,7 @@ void dpm_resume_noirq(pm_message_t state) async_synchronize_full(); dpm_show_time(starttime, state, "noirq"); resume_device_irqs(); + device_wakeup_disarm_wake_irqs(); cpuidle_resume(); trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false); } @@ -1104,6 +1106,7 @@ int dpm_suspend_noirq(pm_message_t state) trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true); cpuidle_pause(); + device_wakeup_arm_wake_irqs(); suspend_device_irqs(); mutex_lock(&dpm_list_mtx); pm_transition = state; diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index b6b8a27..f1a5d95 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -20,6 +20,46 @@ static inline void pm_runtime_early_init(struct device *dev) extern void pm_runtime_init(struct device *dev); extern void pm_runtime_remove(struct device *dev); +struct wake_irq { + struct device *dev; + int irq; + bool dedicated_irq:1; +}; + +extern void dev_pm_arm_wake_irq(struct wake_irq *wirq); +extern void dev_pm_disarm_wake_irq(struct wake_irq *wirq); + +#ifdef CONFIG_PM_SLEEP + +extern int device_wakeup_attach_irq(struct device *dev, + struct wake_irq *wakeirq); +extern void device_wakeup_detach_irq(struct device *dev); +extern void device_wakeup_arm_wake_irqs(void); +extern void device_wakeup_disarm_wake_irqs(void); + +#else + +static inline int +device_wakeup_attach_irq(struct device *dev, + struct wake_irq *wakeirq) +{ + return 0; +} + +static inline void device_wakeup_detach_irq(struct device *dev) +{ +} + +static inline void device_wakeup_arm_wake_irqs(void) +{ +} + +static inline void device_wakeup_disarm_wake_irqs(void) +{ +} + +#endif /* CONFIG_PM_SLEEP */ + /* * sysfs.c */ @@ -52,6 +92,14 @@ static inline void wakeup_sysfs_remove(struct device *dev) {} static inline int pm_qos_sysfs_add(struct device *dev) { return 0; } static inline void pm_qos_sysfs_remove(struct device *dev) {} +static inline void dev_pm_arm_wake_irq(struct wake_irq *wirq) +{ +} + +static inline void dev_pm_disarm_wake_irq(struct wake_irq *wirq) +{ +} + #endif #ifdef CONFIG_PM_SLEEP diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 4ffe4a2..e1a10a0 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "power.h" @@ -514,6 +515,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) callback = RPM_GET_CALLBACK(dev, runtime_suspend); + dev_pm_enable_wake_irq(dev); retval = rpm_callback(callback, dev); if (retval) goto fail; @@ -552,6 +554,7 @@ static int rpm_suspend(struct device *dev, int rpmflags) return retval; fail: + dev_pm_disable_wake_irq(dev); __update_runtime_status(dev, RPM_ACTIVE); dev->power.deferred_resume = false; wake_up_all(&dev->power.wait_queue); @@ -734,10 +737,12 @@ static int rpm_resume(struct device *dev, int rpmflags) callback = RPM_GET_CALLBACK(dev, runtime_resume); + dev_pm_disable_wake_irq(dev); retval = rpm_callback(callback, dev); if (retval) { __update_runtime_status(dev, RPM_SUSPENDED); pm_runtime_cancel_pending(dev); + dev_pm_enable_wake_irq(dev); } else { no_callback: __update_runtime_status(dev, RPM_ACTIVE); diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c new file mode 100644 index 0000000..7470004 --- /dev/null +++ b/drivers/base/power/wakeirq.c @@ -0,0 +1,273 @@ +/* + * wakeirq.c - Device wakeirq helper functions + * + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "power.h" + +/** + * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ + * @dev: Device entry + * @irq: Device wake-up capable interrupt + * @wirq: Wake irq specific data + * + * Internal function to attach either a device IO interrupt or a + * dedicated wake-up interrupt as a wake IRQ. + */ +static int dev_pm_attach_wake_irq(struct device *dev, int irq, + struct wake_irq *wirq) +{ + unsigned long flags; + int err; + + if (!dev || !wirq) + return -EINVAL; + + spin_lock_irqsave(&dev->power.lock, flags); + if (dev_WARN_ONCE(dev, dev->power.wakeirq, + "wake irq already initialized\n")) { + spin_unlock_irqrestore(&dev->power.lock, flags); + return -EEXIST; + } + + dev->power.wakeirq = wirq; + spin_unlock_irqrestore(&dev->power.lock, flags); + + err = device_wakeup_attach_irq(dev, wirq); + if (err) + return err; + + return 0; +} + +/** + * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ + * @dev: Device entry + * @irq: Device IO interrupt + * + * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets + * automatically configured for wake-up from suspend based + * on the device specific sysfs wakeup entry. Typically called + * during driver probe after calling device_init_wakeup(). + */ +int dev_pm_set_wake_irq(struct device *dev, int irq) +{ + struct wake_irq *wirq; + int err; + + wirq = kzalloc(sizeof(*wirq), GFP_KERNEL); + if (!wirq) + return -ENOMEM; + + wirq->dev = dev; + wirq->irq = irq; + + err = dev_pm_attach_wake_irq(dev, irq, wirq); + if (err) + kfree(wirq); + + return err; +} +EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq); + +/** + * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ + * @dev: Device entry + * + * Detach a device wake IRQ and free resources. + * + * Note that it's OK for drivers to call this without calling + * dev_pm_set_wake_irq() as all the driver instances may not have + * a wake IRQ configured. This avoid adding wake IRQ specific + * checks into the drivers. + */ +void dev_pm_clear_wake_irq(struct device *dev) +{ + struct wake_irq *wirq = dev->power.wakeirq; + unsigned long flags; + + if (!wirq) + return; + + spin_lock_irqsave(&dev->power.lock, flags); + dev->power.wakeirq = NULL; + spin_unlock_irqrestore(&dev->power.lock, flags); + + device_wakeup_detach_irq(dev); + if (wirq->dedicated_irq) + free_irq(wirq->irq, wirq); + kfree(wirq); +} +EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq); + +/** + * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts + * @irq: Device specific dedicated wake-up interrupt + * @_wirq: Wake IRQ data + * + * Some devices have a separate wake-up interrupt in addition to the + * device IO interrupt. The wake-up interrupt signals that a device + * should be woken up from it's idle state. This handler uses device + * specific pm_runtime functions to wake the device, and then it's + * up to the device to do whatever it needs to. Note that as the + * device may need to restore context and start up regulators, we + * use a threaded IRQ. + * + * Also note that we are not resending the lost device interrupts. + * We assume that the wake-up interrupt just needs to wake-up the + * device, and then device's pm_runtime_resume() can deal with the + * situation. + */ +static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq) +{ + struct wake_irq *wirq = _wirq; + int res; + + /* We don't want RPM_ASYNC or RPM_NOWAIT here */ + res = pm_runtime_resume(wirq->dev); + if (res < 0) + dev_warn(wirq->dev, + "wake IRQ with no resume: %i\n", res); + + return IRQ_HANDLED; +} + +/** + * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt + * @dev: Device entry + * @irq: Device wake-up interrupt + * + * Unless your hardware has separate wake-up interrupts in addition + * to the device IO interrupts, you don't need this. + * + * Sets up a threaded interrupt handler for a device that has + * a dedicated wake-up interrupt in addition to the device IO + * interrupt. + * + * The interrupt starts disabled, and needs to be managed for + * the device by the bus code or the device driver using + * dev_pm_enable_wake_irq() and dev_pm_disable_wake_irq() + * functions. + */ +int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) +{ + struct wake_irq *wirq; + int err; + + wirq = kzalloc(sizeof(*wirq), GFP_KERNEL); + if (!wirq) + return -ENOMEM; + + wirq->dev = dev; + wirq->irq = irq; + wirq->dedicated_irq = true; + irq_set_status_flags(irq, IRQ_NOAUTOEN); + + /* + * Consumer device may need to power up and restore state + * so we use a threaded irq. + */ + err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq, + IRQF_ONESHOT, dev_name(dev), wirq); + if (err) + goto err_free; + + err = dev_pm_attach_wake_irq(dev, irq, wirq); + if (err) + goto err_free_irq; + + return err; + +err_free_irq: + free_irq(irq, wirq); +err_free: + kfree(wirq); + + return err; +} +EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq); + +/** + * dev_pm_enable_wake_irq - Enable device wake-up interrupt + * @dev: Device + * + * Called from the bus code or the device driver for + * runtime_suspend() to enable the wake-up interrupt while + * the device is running. + * + * Note that for runtime_suspend()) the wake-up interrupts + * should be unconditionally enabled unlike for suspend() + * that is conditional. + */ +void dev_pm_enable_wake_irq(struct device *dev) +{ + struct wake_irq *wirq = dev->power.wakeirq; + + if (wirq && wirq->dedicated_irq) + enable_irq(wirq->irq); +} +EXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); + +/** + * dev_pm_disable_wake_irq - Disable device wake-up interrupt + * @dev: Device + * + * Called from the bus code or the device driver for + * runtime_resume() to disable the wake-up interrupt while + * the device is running. + */ +void dev_pm_disable_wake_irq(struct device *dev) +{ + struct wake_irq *wirq = dev->power.wakeirq; + + if (wirq && wirq->dedicated_irq) + disable_irq_nosync(wirq->irq); +} +EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq); + +/** + * dev_pm_arm_wake_irq - Arm device wake-up + * @wirq: Device wake-up interrupt + * + * Sets up the wake-up event conditionally based on the + * device_may_wake(). + */ +void dev_pm_arm_wake_irq(struct wake_irq *wirq) +{ + if (!wirq) + return; + + if (device_may_wakeup(wirq->dev)) + enable_irq_wake(wirq->irq); +} + +/** + * dev_pm_disarm_wake_irq - Disarm device wake-up + * @wirq: Device wake-up interrupt + * + * Clears up the wake-up event conditionally based on the + * device_may_wake(). + */ +void dev_pm_disarm_wake_irq(struct wake_irq *wirq) +{ + if (!wirq) + return; + + if (device_may_wakeup(wirq->dev)) + disable_irq_wake(wirq->irq); +} diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 7726200..7332ebc 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "power.h" @@ -239,6 +240,97 @@ int device_wakeup_enable(struct device *dev) EXPORT_SYMBOL_GPL(device_wakeup_enable); /** + * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source + * @dev: Device to handle + * @wakeirq: Device specific wakeirq entry + * + * Attach a device wakeirq to the wakeup source so the device + * wake IRQ can be configured automatically for suspend and + * resume. + */ +int device_wakeup_attach_irq(struct device *dev, + struct wake_irq *wakeirq) +{ + struct wakeup_source *ws; + int ret = 0; + + spin_lock_irq(&dev->power.lock); + ws = dev->power.wakeup; + if (!ws) { + dev_err(dev, "forgot to call call device_init_wakeup?\n"); + ret = -EINVAL; + goto unlock; + } + + if (ws->wakeirq) { + ret = -EEXIST; + goto unlock; + } + + ws->wakeirq = wakeirq; + +unlock: + spin_unlock_irq(&dev->power.lock); + + return ret; +} + +/** + * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source + * @dev: Device to handle + * + * Removes a device wakeirq from the wakeup source. + */ +void device_wakeup_detach_irq(struct device *dev) +{ + struct wakeup_source *ws; + + spin_lock_irq(&dev->power.lock); + ws = dev->power.wakeup; + if (!ws) + goto unlock; + + ws->wakeirq = NULL; + +unlock: + spin_unlock_irq(&dev->power.lock); +} + +/** + * device_wakeup_arm_wake_irqs(void) + * + * Itereates over the list of device wakeirqs to arm them. + */ +void device_wakeup_arm_wake_irqs(void) +{ + struct wakeup_source *ws; + + rcu_read_lock(); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) { + if (ws->wakeirq) + dev_pm_arm_wake_irq(ws->wakeirq); + } + rcu_read_unlock(); +} + +/** + * device_wakeup_disarm_wake_irqs(void) + * + * Itereates over the list of device wakeirqs to disarm them. + */ +void device_wakeup_disarm_wake_irqs(void) +{ + struct wakeup_source *ws; + + rcu_read_lock(); + list_for_each_entry_rcu(ws, &wakeup_sources, entry) { + if (ws->wakeirq) + dev_pm_disarm_wake_irq(ws->wakeirq); + } + rcu_read_unlock(); +} + +/** * device_wakeup_detach - Detach a device's wakeup source object from it. * @dev: Device to detach the wakeup source object from. * diff --git a/include/linux/pm.h b/include/linux/pm.h index 2d29c64..1c4ed0c 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -529,6 +529,7 @@ enum rpm_request { }; struct wakeup_source; +struct wake_irq; struct pm_domain_data; struct pm_subsys_data { @@ -568,6 +569,7 @@ struct dev_pm_info { unsigned long timer_expires; struct work_struct work; wait_queue_head_t wait_queue; + struct wake_irq *wakeirq; atomic_t usage_count; atomic_t child_count; unsigned int disable_depth:3; diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h new file mode 100644 index 0000000..4046fa1 --- /dev/null +++ b/include/linux/pm_wakeirq.h @@ -0,0 +1,52 @@ +/* + * pm_wakeirq.h - Device wakeirq helper functions + * + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_PM_WAKEIRQ_H +#define _LINUX_PM_WAKEIRQ_H + +#ifdef CONFIG_PM + +extern int dev_pm_set_wake_irq(struct device *dev, int irq); +extern int dev_pm_set_dedicated_wake_irq(struct device *dev, + int irq); +extern void dev_pm_clear_wake_irq(struct device *dev); +extern void dev_pm_enable_wake_irq(struct device *dev); +extern void dev_pm_disable_wake_irq(struct device *dev); + +#else /* !CONFIG_PM */ + +static inline int dev_pm_set_wake_irq(struct device *dev, int irq) +{ + return 0; +} + +static inline int dev_pm_set_dedicated__wake_irq(struct device *dev, + int irq) +{ + return 0; +} + +static inline void dev_pm_clear_wake_irq(struct device *dev) +{ +} + +static inline void dev_pm_enable_wake_irq(struct device *dev) +{ +} + +static inline void dev_pm_disable_wake_irq(struct device *dev) +{ +} + +#endif /* CONFIG_PM */ +#endif /* _LINUX_PM_WAKEIRQ_H */ diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index a0f7080..a344793 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -28,9 +28,17 @@ #include +struct wake_irq; + /** * struct wakeup_source - Representation of wakeup sources * + * @name: Name of the wakeup source + * @entry: Wakeup source list entry + * @lock: Wakeup source lock + * @wakeirq: Optional device specific wakeirq + * @timer: Wakeup timer list + * @timer_expires: Wakeup timer expiration * @total_time: Total time this wakeup source has been active. * @max_time: Maximum time this wakeup source has been continuously active. * @last_time: Monotonic clock when the wakeup source's was touched last time. @@ -47,6 +55,7 @@ struct wakeup_source { const char *name; struct list_head entry; spinlock_t lock; + struct wake_irq *wakeirq; struct timer_list timer; unsigned long timer_expires; ktime_t total_time; -- cgit v0.10.2 From 6f066d4d2621affdbc510b5b0bd624af7ae74e1b Mon Sep 17 00:00:00 2001 From: Dasaratharaman Chandramouli Date: Tue, 19 May 2015 15:37:49 -0700 Subject: powercap / RAPL: Support Knights Landing This patch enables intel_rapl driver to run on the next-generation Intel(R) Xeon Phi Microarchitecture code named "Knights Landing" Signed-off-by: Dasaratharaman Chandramouli Acked-by: Jacob Pan Signed-off-by: Rafael J. Wysocki diff --git a/drivers/powercap/intel_rapl.c b/drivers/powercap/intel_rapl.c index f332628..482b22d 100644 --- a/drivers/powercap/intel_rapl.c +++ b/drivers/powercap/intel_rapl.c @@ -1101,6 +1101,7 @@ static const struct x86_cpu_id rapl_ids[] __initconst = { RAPL_CPU(0x4A, rapl_defaults_tng),/* Tangier */ RAPL_CPU(0x56, rapl_defaults_core),/* Future Xeon */ RAPL_CPU(0x5A, rapl_defaults_ann),/* Annidale */ + RAPL_CPU(0x57, rapl_defaults_hsw_server),/* Knights Landing */ {} }; MODULE_DEVICE_TABLE(x86cpu, rapl_ids); -- cgit v0.10.2 From f38a437862c61530d014a55db082d6c906edd5df Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:30:03 +0800 Subject: ACPICA: Additional dragon_fly BSD support. ACPICA commit 3e93431674abe947202b0f9a0afa7b625b17caa6 Makefiles and environment defines. This commit doesn't affect Linux builds. Link: https://github.com/acpica/acpica/commit/3e934316 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index ecdf940..073997d 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -175,6 +175,9 @@ #elif defined(_APPLE) || defined(__APPLE__) #include "acmacosx.h" +#elif defined(__DragonFly__) +#include "acdragonfly.h" + #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include "acfreebsd.h" diff --git a/include/acpi/platform/acenvex.h b/include/acpi/platform/acenvex.h index 71e5ec5..14dc6f6 100644 --- a/include/acpi/platform/acenvex.h +++ b/include/acpi/platform/acenvex.h @@ -56,6 +56,9 @@ #if defined(_LINUX) || defined(__linux__) #include +#elif defined(__DragonFly__) +#include "acdragonflyex.h" + #endif /*! [End] no source code translation !*/ -- cgit v0.10.2 From 37e12657f8922ebc62f696494c56c81db509053e Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:30:11 +0800 Subject: ACPICA: ACPI 6.0: Add support for STAO table. ACPICA commit 532bf402a503061afd9d80a23e1d3c8fd99b052c _STA override table. Link: https://github.com/acpica/acpica/commit/532bf402 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index 440ca81..eb994e9 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -68,6 +68,7 @@ #define ACPI_SIG_PCCT "PCCT" /* Platform Communications Channel Table */ #define ACPI_SIG_PMTT "PMTT" /* Platform Memory Topology Table */ #define ACPI_SIG_RASF "RASF" /* RAS Feature table */ +#define ACPI_SIG_STAO "STAO" /* Status Override table */ #define ACPI_SIG_TPM2 "TPM2" /* Trusted Platform Module 2.0 H/W interface table */ #define ACPI_SIG_S3PT "S3PT" /* S3 Performance (sub)Table */ @@ -685,6 +686,21 @@ enum acpi_rasf_status { /******************************************************************************* * + * STAO - Status Override Table (_STA override) - ACPI 6.0 + * Version 1 + * + * Conforms to "ACPI Specification for Status Override Table" + * 6 January 2015 + * + ******************************************************************************/ + +struct acpi_table_stao { + struct acpi_table_header header; /* Common ACPI table header */ + u8 ignore_uart; +}; + +/******************************************************************************* + * * TPM2 - Trusted Platform Module (TPM) 2.0 Hardware Interface Table * Version 3 * -- cgit v0.10.2 From e34a7813cf7841ab351d1cb4152c2f4ceaa00070 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:30:18 +0800 Subject: ACPICA: ACPI 6.0: Add support for new predefined names. ACPICA commit 7ba68f2eafa12fe75ee7aa0df7543d5ea2443051 Compiler, Interpreter, acpi_help. _BTH, _CR3, _DSD, _LPI, _MTL, _PRR, _RDI, _RST, _TFP, _TSN. Link: https://github.com/acpica/acpica/commit/7ba68f2e Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 87b2752..3596958 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -352,11 +352,21 @@ struct acpi_package_info3 { u16 reserved; }; +struct acpi_package_info4 { + u8 type; + u8 object_type1; + u8 count1; + u8 sub_object_types; + u8 pkg_count; + u16 reserved; +}; + union acpi_predefined_info { struct acpi_name_info info; struct acpi_package_info ret_info; struct acpi_package_info2 ret_info2; struct acpi_package_info3 ret_info3; + struct acpi_package_info4 ret_info4; }; /* Reset to default packing */ diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index a972d11..b9474b5 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -105,6 +105,11 @@ * count = 0 (optional) * (Used for _DLM) * + * ACPI_PTYPE2_VAR_VAR: Variable number of subpackages, each of either a + * constant or variable length. The subpackages are preceded by a + * constant number of objects. + * (Used for _LPI, _RDI) + * * ACPI_PTYPE2_UUID_PAIR: Each subpackage is preceded by a UUID Buffer. The UUID * defines the format of the package. Zero-length parent package is * allowed. @@ -123,7 +128,8 @@ enum acpi_return_package_types { ACPI_PTYPE2_MIN = 8, ACPI_PTYPE2_REV_FIXED = 9, ACPI_PTYPE2_FIX_VAR = 10, - ACPI_PTYPE2_UUID_PAIR = 11 + ACPI_PTYPE2_VAR_VAR = 11, + ACPI_PTYPE2_UUID_PAIR = 12 }; /* Support macros for users of the predefined info table */ @@ -172,7 +178,7 @@ enum acpi_return_package_types { * These are the names that can actually be evaluated via acpi_evaluate_object. * Not present in this table are the following: * - * 1) Predefined/Reserved names that are never evaluated via + * 1) Predefined/Reserved names that are not usually evaluated via * acpi_evaluate_object: * _Lxx and _Exx GPE methods * _Qxx EC methods @@ -361,6 +367,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (4 Int) */ PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_INTEGER, 4, 0, 0, 0), + {{"_BTH", METHOD_1ARGS(ACPI_TYPE_INTEGER), /* ACPI 6.0 */ + METHOD_NO_RETURN_VALUE}}, + {{"_BTM", METHOD_1ARGS(ACPI_TYPE_INTEGER), METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, @@ -390,6 +399,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER, 0, 0, 0, 0), + {{"_CR3", METHOD_0ARGS, /* ACPI 6.0 */ + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + {{"_CRS", METHOD_0ARGS, METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, @@ -445,7 +457,7 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { {{"_DOS", METHOD_1ARGS(ACPI_TYPE_INTEGER), METHOD_NO_RETURN_VALUE}}, - {{"_DSD", METHOD_0ARGS, + {{"_DSD", METHOD_0ARGS, /* ACPI 6.0 */ METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each: 1 Buf, 1 Pkg */ PACKAGE_INFO(ACPI_PTYPE2_UUID_PAIR, ACPI_RTYPE_BUFFER, 1, ACPI_RTYPE_PACKAGE, 1, 0), @@ -604,6 +616,12 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int(rev), n Pkg (2 Int) */ PACKAGE_INFO(ACPI_PTYPE2_REV_FIXED, ACPI_RTYPE_INTEGER, 2, 0, 0, 0), + {{"_LPI", METHOD_0ARGS, /* ACPI 6.0 */ + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (3 Int, n Pkg (10 Int/Buf) */ + PACKAGE_INFO(ACPI_PTYPE2_VAR_VAR, ACPI_RTYPE_INTEGER, 3, + ACPI_RTYPE_INTEGER | ACPI_RTYPE_BUFFER | ACPI_RTYPE_STRING, + 10, 0), + {{"_MAT", METHOD_0ARGS, METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, @@ -624,6 +642,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { ACPI_TYPE_INTEGER), METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + {{"_MTL", METHOD_0ARGS, /* ACPI 6.0 */ + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + {{"_NTT", METHOD_0ARGS, METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, @@ -716,6 +737,10 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Refs) */ PACKAGE_INFO(ACPI_PTYPE1_VAR, ACPI_RTYPE_REFERENCE, 0, 0, 0, 0), + {{"_PRR", METHOD_0ARGS, /* ACPI 6.0 */ + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Fixed-length (1 Ref) */ + PACKAGE_INFO(ACPI_PTYPE1_FIXED, ACPI_RTYPE_REFERENCE, 1, 0, 0, 0), + {{"_PRS", METHOD_0ARGS, METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, @@ -796,6 +821,11 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { {{"_PXM", METHOD_0ARGS, METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + {{"_RDI", METHOD_0ARGS, /* ACPI 6.0 */ + METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (1 Int, n Pkg (m Ref)) */ + PACKAGE_INFO(ACPI_PTYPE2_VAR_VAR, ACPI_RTYPE_INTEGER, 1, + ACPI_RTYPE_REFERENCE, 0, 0), + {{"_REG", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER), METHOD_NO_RETURN_VALUE}}, @@ -808,6 +838,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { {{"_ROM", METHOD_2ARGS(ACPI_TYPE_INTEGER, ACPI_TYPE_INTEGER), METHOD_RETURNS(ACPI_RTYPE_BUFFER)}}, + {{"_RST", METHOD_0ARGS, /* ACPI 6.0 */ + METHOD_NO_RETURN_VALUE}}, + {{"_RTV", METHOD_0ARGS, METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, @@ -935,6 +968,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { {{"_TDL", METHOD_0ARGS, METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + {{"_TFP", METHOD_0ARGS, /* ACPI 6.0 */ + METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, + {{"_TIP", METHOD_1ARGS(ACPI_TYPE_INTEGER), METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, @@ -959,6 +995,9 @@ const union acpi_predefined_info acpi_gbl_predefined_methods[] = { METHOD_RETURNS(ACPI_RTYPE_PACKAGE)}}, /* Variable-length (Pkgs) each 5 Int with count */ PACKAGE_INFO(ACPI_PTYPE2_COUNT, ACPI_RTYPE_INTEGER, 5, 0, 0, 0), + {{"_TSN", METHOD_0ARGS, /* ACPI 6.0 */ + METHOD_RETURNS(ACPI_RTYPE_REFERENCE)}}, + {{"_TSP", METHOD_0ARGS, METHOD_RETURNS(ACPI_RTYPE_INTEGER)}}, diff --git a/drivers/acpi/acpica/nsprepkg.c b/drivers/acpi/acpica/nsprepkg.c index 8b79958..9bb2519 100644 --- a/drivers/acpi/acpica/nsprepkg.c +++ b/drivers/acpi/acpica/nsprepkg.c @@ -316,6 +316,13 @@ acpi_ns_check_package(struct acpi_evaluate_info *info, acpi_ns_check_package_list(info, package, elements, count); break; + case ACPI_PTYPE2_VAR_VAR: + /* + * Returns a variable list of packages, each with a variable list + * of objects. + */ + break; + case ACPI_PTYPE2_UUID_PAIR: /* The package must contain pairs of (UUID + type) */ @@ -487,6 +494,12 @@ acpi_ns_check_package_list(struct acpi_evaluate_info *info, } break; + case ACPI_PTYPE2_VAR_VAR: + /* + * Each subpackage has a fixed or variable number of elements + */ + break; + case ACPI_PTYPE2_FIXED: /* Each subpackage has a fixed length */ diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index 151fcd9..77d8103 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -497,10 +497,10 @@ acpi_ns_remove_null_elements(struct acpi_evaluate_info *info, case ACPI_PTYPE2_MIN: case ACPI_PTYPE2_REV_FIXED: case ACPI_PTYPE2_FIX_VAR: - break; default: + case ACPI_PTYPE2_VAR_VAR: case ACPI_PTYPE1_FIXED: case ACPI_PTYPE1_OPTION: return; -- cgit v0.10.2 From b6944efd638d746653f69bc6cbbef7986c0ff962 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:30:24 +0800 Subject: ACPICA: ACPI 6.0: Add support for XENV table. ACPICA commit 08c4197cf4ddd45f0c961078220b0fc19c10745c Xen Environment table. Link: https://github.com/acpica/acpica/commit/08c4197c Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index eb994e9..3eed4c8 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -70,6 +70,7 @@ #define ACPI_SIG_RASF "RASF" /* RAS Feature table */ #define ACPI_SIG_STAO "STAO" /* Status Override table */ #define ACPI_SIG_TPM2 "TPM2" /* Trusted Platform Module 2.0 H/W interface table */ +#define ACPI_SIG_XENV "XENV" /* Xen Environment table */ #define ACPI_SIG_S3PT "S3PT" /* S3 Performance (sub)Table */ #define ACPI_SIG_PCCS "PCC" /* PCC Shared Memory Region */ @@ -729,6 +730,23 @@ struct acpi_tpm2_control { u64 response_address; }; +/******************************************************************************* + * + * XENV - Xen Environment Table (ACPI 6.0) + * Version 1 + * + * Conforms to "ACPI Specification for Xen Environment Table" 4 January 2015 + * + ******************************************************************************/ + +struct acpi_table_xenv { + struct acpi_table_header header; /* Common ACPI table header */ + u64 grant_table_address; + u64 grant_table_size; + u32 event_interrupt; + u8 event_flags; +}; + /* Reset to default packing */ #pragma pack() -- cgit v0.10.2 From b0e01c7241560b4e62b43e397b2bb3484e30e670 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:30:31 +0800 Subject: ACPICA: Parser: Move a couple externals to the proper header. ACPICA commit 7325b59c8b5d1522ded51ae6a76b804f6e8da5d2 Moved from a C module. Link: https://github.com/acpica/acpica/commit/7325b59c Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h index 74a390c..0cdd2fc 100644 --- a/drivers/acpi/acpica/acparser.h +++ b/drivers/acpi/acpica/acparser.h @@ -70,6 +70,9 @@ * *****************************************************************************/ +extern const u8 acpi_gbl_short_op_index[]; +extern const u8 acpi_gbl_long_op_index[]; + /* * psxface - Parser external interfaces */ diff --git a/drivers/acpi/acpica/psopinfo.c b/drivers/acpi/acpica/psopinfo.c index 20e1a35..5831090 100644 --- a/drivers/acpi/acpica/psopinfo.c +++ b/drivers/acpi/acpica/psopinfo.c @@ -50,9 +50,6 @@ #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("psopinfo") -extern const u8 acpi_gbl_short_op_index[]; -extern const u8 acpi_gbl_long_op_index[]; - static const u8 acpi_gbl_argument_count[] = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 6 }; -- cgit v0.10.2 From a737222240848c771b2bc44880d3253fb7c03e13 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:30:38 +0800 Subject: ACPICA: iASL: Enhance detection of non-ascii or corrupted input files. ACPICA commit 08170904011f1e8f817d9e3a9f2bb2438aeacf60 For the compiler part (not disassembler). - Characters not within a comment must be be ASCII (0-0x7F), and now either printable or a "space" character. Provides better detection of files that cannot be compiled. This patch only affects iASL which is not in the Linux upstream. Link: https://github.com/acpica/acpica/commit/08170904 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 2b3c5bd..d49f5c7 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -251,7 +251,7 @@ extern const u8 _acpi_ctype[]; #define _ACPI_DI 0x04 /* '0'-'9' */ #define _ACPI_LO 0x02 /* 'a'-'z' */ #define _ACPI_PU 0x10 /* punctuation */ -#define _ACPI_SP 0x08 /* space */ +#define _ACPI_SP 0x08 /* space, tab, CR, LF, VT, FF */ #define _ACPI_UP 0x01 /* 'A'-'Z' */ #define _ACPI_XD 0x80 /* '0'-'9', 'A'-'F', 'a'-'f' */ diff --git a/drivers/acpi/acpica/utfileio.c b/drivers/acpi/acpica/utfileio.c index 7e1168b..f72c53c 100644 --- a/drivers/acpi/acpica/utfileio.c +++ b/drivers/acpi/acpica/utfileio.c @@ -198,11 +198,8 @@ acpi_ut_read_table(FILE * fp, table_header.length, file_size); #ifdef ACPI_ASL_COMPILER - status = fl_check_for_ascii(fp, NULL, FALSE); - if (ACPI_SUCCESS(status)) { - acpi_os_printf - ("File appears to be ASCII only, must be binary\n"); - } + acpi_os_printf("File is corrupt or is ASCII text -- " + "it must be a binary file\n"); #endif return (AE_BAD_HEADER); } -- cgit v0.10.2 From 68edb038231ada9ab39701b776224e4bb23f928e Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:30:44 +0800 Subject: ACPICA: ACPI 6.0: Add support for WPBT table. ACPICA commit a6ccb4033b49f7aa33a17ddc41dd69d57e799fbd Windows Platform Binary Table. Link: https://github.com/acpica/acpica/commit/a6ccb403 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index 3eed4c8..e1aac82 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -70,6 +70,7 @@ #define ACPI_SIG_RASF "RASF" /* RAS Feature table */ #define ACPI_SIG_STAO "STAO" /* Status Override table */ #define ACPI_SIG_TPM2 "TPM2" /* Trusted Platform Module 2.0 H/W interface table */ +#define ACPI_SIG_WPBT "WPBT" /* Windows Platform Binary Table */ #define ACPI_SIG_XENV "XENV" /* Xen Environment table */ #define ACPI_SIG_S3PT "S3PT" /* S3 Performance (sub)Table */ @@ -79,7 +80,6 @@ #define ACPI_SIG_MATR "MATR" /* Memory Address Translation Table */ #define ACPI_SIG_MSDM "MSDM" /* Microsoft Data Management Table */ -#define ACPI_SIG_WPBT "WPBT" /* Windows Platform Binary Table */ /* * All tables must be byte-packed to match the ACPI specification, since @@ -732,6 +732,24 @@ struct acpi_tpm2_control { /******************************************************************************* * + * WPBT - Windows Platform Environment Table (ACPI 6.0) + * Version 1 + * + * Conforms to "Windows Platform Binary Table (WPBT)" 29 November 2011 + * + ******************************************************************************/ + +struct acpi_table_wpbt { + struct acpi_table_header header; /* Common ACPI table header */ + u32 handoff_size; + u64 handoff_address; + u8 layout; + u8 type; + u16 arguments_length; +}; + +/******************************************************************************* + * * XENV - Xen Environment Table (ACPI 6.0) * Version 1 * -- cgit v0.10.2 From aeb823bbacc2a3aaee29eda5875b58a049fa1f78 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:30:52 +0800 Subject: ACPICA: ACPI 6.0: Add changes for FADT table. ACPICA commit 72b0b6741990f619f6aaa915302836b7cbb41ac4 One new 64-bit field at the end of the table. FADT version is now 6. Link: https://github.com/acpica/acpica/commit/72b0b674 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index d4081fe..cb8a6b9 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -284,6 +284,7 @@ struct acpi_table_fadt { struct acpi_generic_address xgpe1_block; /* 64-bit Extended General Purpose Event 1 Reg Blk address */ struct acpi_generic_address sleep_control; /* 64-bit Sleep Control register (ACPI 5.0) */ struct acpi_generic_address sleep_status; /* 64-bit Sleep Status register (ACPI 5.0) */ + u64 hypervisor_id; /* Hypervisor Vendor ID (ACPI 6.0) */ }; /* Masks for FADT IA-PC Boot Architecture Flags (boot_flags) [Vx]=Introduced in this FADT revision */ @@ -341,7 +342,7 @@ enum acpi_preferred_pm_profiles { PM_TABLET = 8 }; -/* Values for sleep_status and sleep_control registers (V5 FADT) */ +/* Values for sleep_status and sleep_control registers (V5+ FADT) */ #define ACPI_X_WAKE_STATUS 0x80 #define ACPI_X_SLEEP_TYPE_MASK 0x1C @@ -398,15 +399,17 @@ struct acpi_table_desc { * FADT is the bottom line as to what the version really is. * * For reference, the values below are as follows: - * FADT V1 size: 0x074 - * FADT V2 size: 0x084 - * FADT V3 size: 0x0F4 - * FADT V4 size: 0x0F4 - * FADT V5 size: 0x10C + * FADT V1 size: 0x074 + * FADT V2 size: 0x084 + * FADT V3 size: 0x0F4 + * FADT V4 size: 0x0F4 + * FADT V5 size: 0x10C + * FADT V6 size: 0x114 */ #define ACPI_FADT_V1_SIZE (u32) (ACPI_FADT_OFFSET (flags) + 4) #define ACPI_FADT_V2_SIZE (u32) (ACPI_FADT_OFFSET (minor_revision) + 1) #define ACPI_FADT_V3_SIZE (u32) (ACPI_FADT_OFFSET (sleep_control)) -#define ACPI_FADT_V5_SIZE (u32) (sizeof (struct acpi_table_fadt)) +#define ACPI_FADT_V5_SIZE (u32) (ACPI_FADT_OFFSET (hypervisor_id)) +#define ACPI_FADT_V6_SIZE (u32) (sizeof (struct acpi_table_fadt)) #endif /* __ACTBL_H__ */ -- cgit v0.10.2 From 9ab8cf1b699758d502411ad9d131ff521ca01cd6 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:30:59 +0800 Subject: ACPICA: ACPI 6.0: Add changes for LPIT table. ACPICA commit d527908bb33a3ed515cfb349cbec57121deafcc8 Second subtable type was removed from the July 2014 LPIT document. Link: https://github.com/acpica/acpica/commit/d527908b Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index cafdeb5..93f85e8 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -824,7 +824,7 @@ struct acpi_ivrs_memory { * * LPIT - Low Power Idle Table * - * Conforms to "ACPI Low Power Idle Table (LPIT) and _LPD Proposal (DRAFT)" + * Conforms to "ACPI Low Power Idle Table (LPIT)" July 2014. * ******************************************************************************/ @@ -846,8 +846,7 @@ struct acpi_lpit_header { enum acpi_lpit_type { ACPI_LPIT_TYPE_NATIVE_CSTATE = 0x00, - ACPI_LPIT_TYPE_SIMPLE_IO = 0x01, - ACPI_LPIT_TYPE_RESERVED = 0x02 /* 2 and above are reserved */ + ACPI_LPIT_TYPE_RESERVED = 0x01 /* 1 and above are reserved */ }; /* Masks for Flags field above */ @@ -870,21 +869,6 @@ struct acpi_lpit_native { u64 counter_frequency; }; -/* 0x01: Simple I/O based LPI structure */ - -struct acpi_lpit_io { - struct acpi_lpit_header header; - struct acpi_generic_address entry_trigger; - u32 trigger_action; - u64 trigger_value; - u64 trigger_mask; - struct acpi_generic_address minimum_idle_state; - u32 residency; - u32 latency; - struct acpi_generic_address residency_counter; - u64 counter_frequency; -}; - /******************************************************************************* * * MCFG - PCI Memory Mapped Configuration table and subtable -- cgit v0.10.2 From c8dec7459d9fcb880e5c42929d01c308bea9f823 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 21 May 2015 10:31:06 +0800 Subject: ACPICA: Dispatcher: Fix a resource leak issue in acpi_ds_auto_serialize_method(). ACPICA commit 29d03840cbab435e8ea82e9339ff9d84535c647d This patch fixes a resource leak issue in acpi_ds_auto_serialize_method(). It is reported by the "Coverity" tool as unsecure code. Lv Zheng. Link: https://github.com/acpica/acpica/commit/29d03840 Link: https://jira01.devtools.intel.com/browse/LCK-2142 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index d72565a..85bb951 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -116,6 +116,7 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node, walk_state = acpi_ds_create_walk_state(node->owner_id, NULL, NULL, NULL); if (!walk_state) { + acpi_ps_free_op(op); return_ACPI_STATUS(AE_NO_MEMORY); } @@ -125,6 +126,7 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node, obj_desc->method.aml_length, NULL, 0); if (ACPI_FAILURE(status)) { acpi_ds_delete_walk_state(walk_state); + acpi_ps_free_op(op); return_ACPI_STATUS(status); } @@ -133,9 +135,6 @@ acpi_ds_auto_serialize_method(struct acpi_namespace_node *node, /* Parse the method, scan for creation of named objects */ status = acpi_ps_parse_aml(walk_state); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } acpi_ps_delete_parse_tree(op); return_ACPI_STATUS(status); -- cgit v0.10.2 From 2900d56ffb3bd23646db0d2562b6483329632e8a Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 21 May 2015 10:31:12 +0800 Subject: ACPICA: Hardware: Fix a resource leak issue in acpi_hw_build_pci_list(). ACPICA commit e4f0b73c107680841d7dd01cc04ec108df6580bd There is code in acpi_hw_build_pci_list() destructing returned object (return_list_head) before touching it while the allocated new object (list_head) is not tracked correctly to be destructed on the error case, which is detected as unsecure code by the "Coverity" tool. This patch fixes this issue by always intializing the returned object in acpi_hw_build_pci_list() so that the caller of acpi_hw_build_pci_list() needn't initialize it and always using the returned object to track the new allocated objects. Lv Zheng. Link: https://github.com/acpica/acpica/commit/e4f0b73c Link: https://jira01.devtools.intel.com/browse/LCK-2143 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/hwpci.c b/drivers/acpi/acpica/hwpci.c index c5214de..f785ea7 100644 --- a/drivers/acpi/acpica/hwpci.c +++ b/drivers/acpi/acpica/hwpci.c @@ -123,7 +123,7 @@ acpi_hw_derive_pci_id(struct acpi_pci_id *pci_id, acpi_handle root_pci_device, acpi_handle pci_region) { acpi_status status; - struct acpi_pci_device *list_head = NULL; + struct acpi_pci_device *list_head; ACPI_FUNCTION_TRACE(hw_derive_pci_id); @@ -177,13 +177,13 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device, acpi_handle parent_device; acpi_status status; struct acpi_pci_device *list_element; - struct acpi_pci_device *list_head = NULL; /* * Ascend namespace branch until the root_pci_device is reached, building * a list of device nodes. Loop will exit when either the PCI device is * found, or the root of the namespace is reached. */ + *return_list_head = NULL; current_device = pci_region; while (1) { status = acpi_get_parent(current_device, &parent_device); @@ -198,7 +198,6 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device, /* Finished when we reach the PCI root device (PNP0A03 or PNP0A08) */ if (parent_device == root_pci_device) { - *return_list_head = list_head; return (AE_OK); } @@ -213,9 +212,9 @@ acpi_hw_build_pci_list(acpi_handle root_pci_device, /* Put new element at the head of the list */ - list_element->next = list_head; + list_element->next = *return_list_head; list_element->device = parent_device; - list_head = list_element; + *return_list_head = list_element; current_device = parent_device; } -- cgit v0.10.2 From 0cff8dc0099f6d4f7431181918b37a472bcd1bbb Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:31:18 +0800 Subject: ACPICA: ACPI 6.0: Add changes for MADT table. ACPICA commit 02cbb41232bccf7a91967140cab95d5f48291f21 New subtable type. Some additions to existing subtables. Link: https://github.com/acpica/acpica/commit/02cbb412 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index b80b0e6..cadf21c 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -673,7 +673,8 @@ enum acpi_madt_type { ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR = 12, ACPI_MADT_TYPE_GENERIC_MSI_FRAME = 13, ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR = 14, - ACPI_MADT_TYPE_RESERVED = 15 /* 15 and greater are reserved */ + ACPI_MADT_TYPE_GENERIC_TRANSLATOR = 15, + ACPI_MADT_TYPE_RESERVED = 16 /* 16 and greater are reserved */ }; /* @@ -794,7 +795,7 @@ struct acpi_madt_local_x2apic_nmi { u8 reserved[3]; /* reserved - must be zero */ }; -/* 11: Generic Interrupt (ACPI 5.0) */ +/* 11: Generic Interrupt (ACPI 5.0 + ACPI 6.0 changes) */ struct acpi_madt_generic_interrupt { struct acpi_subtable_header header; @@ -811,6 +812,8 @@ struct acpi_madt_generic_interrupt { u32 vgic_interrupt; u64 gicr_base_address; u64 arm_mpidr; + u8 efficiency_class; + u8 reserved2[3]; }; /* Masks for Flags field above */ @@ -819,7 +822,7 @@ struct acpi_madt_generic_interrupt { #define ACPI_MADT_PERFORMANCE_IRQ_MODE (1<<1) /* 01: Performance Interrupt Mode */ #define ACPI_MADT_VGIC_IRQ_MODE (1<<2) /* 02: VGIC Maintenance Interrupt mode */ -/* 12: Generic Distributor (ACPI 5.0) */ +/* 12: Generic Distributor (ACPI 5.0 + ACPI 6.0 changes) */ struct acpi_madt_generic_distributor { struct acpi_subtable_header header; @@ -827,7 +830,8 @@ struct acpi_madt_generic_distributor { u32 gic_id; u64 base_address; u32 global_irq_base; - u32 reserved2; /* reserved - must be zero */ + u8 version; + u8 reserved2[3]; /* reserved - must be zero */ }; /* 13: Generic MSI Frame (ACPI 5.1) */ @@ -855,6 +859,16 @@ struct acpi_madt_generic_redistributor { u32 length; }; +/* 15: Generic Translator (ACPI 6.0) */ + +struct acpi_madt_generic_translator { + struct acpi_subtable_header header; + u16 reserved; /* reserved - must be zero */ + u32 translation_id; + u64 base_address; + u32 reserved2; +}; + /* * Common flags fields for MADT subtables */ -- cgit v0.10.2 From 69ee810cbbca8d6834cdbbb31a8a00fa9ad91c47 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 21 May 2015 10:31:24 +0800 Subject: ACPICA: ACPI 6.0: Add ACPI_SUB_PTR(). ACPICA commit 5de82757aef5d6163e37064033aacbce193abbca Using a minus number with ACPI_ADD_PTR() will cause compiler warnings, such warnings cannot be eliminated by force casting an unsigned value to a signed value. This patch thus introduces ACPI_SUB_PTR() to be used with minus numbers. Lv Zheng. Link: https://github.com/acpica/acpica/commit/5de82757 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index a9d33e8..63fd7f5 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -525,6 +525,7 @@ typedef u64 acpi_integer; #define ACPI_CAST_PTR(t, p) ((t *) (acpi_uintptr_t) (p)) #define ACPI_CAST_INDIRECT_PTR(t, p) ((t **) (acpi_uintptr_t) (p)) #define ACPI_ADD_PTR(t, a, b) ACPI_CAST_PTR (t, (ACPI_CAST_PTR (u8, (a)) + (acpi_size)(b))) +#define ACPI_SUB_PTR(t, a, b) ACPI_CAST_PTR (t, (ACPI_CAST_PTR (u8, (a)) - (acpi_size)(b))) #define ACPI_PTR_DIFF(a, b) (acpi_size) (ACPI_CAST_PTR (u8, (a)) - ACPI_CAST_PTR (u8, (b))) /* Pointer/Integer type conversions */ -- cgit v0.10.2 From 874f6a723e56d0da9e481629b17482bcd3801ecf Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 21 May 2015 10:31:30 +0800 Subject: ACPICA: ACPI 6.0: Add support for IORT table. ACPICA commit 5de82757aef5d6163e37064033aacbce193abbca This patch adds support for IORT (IO Remapping Table) in iasl. Note that some field names are modified to shrink their length or the decompiled IORT ASL will contain fields with ugly ":" alignment. The IORT contains field definitions around "Memory Access Properties". This patch also adds support to encode/decode it using inline table. This patch doesn't add inline table support for the SMMU interrupt fields due to a limitation in current ACPICA data table support. Lv Zheng. Link: https://github.com/acpica/acpica/commit/5de82757 Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 93f85e8..370d69d 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -69,6 +69,7 @@ #define ACPI_SIG_DMAR "DMAR" /* DMA Remapping table */ #define ACPI_SIG_HPET "HPET" /* High Precision Event Timer table */ #define ACPI_SIG_IBFT "IBFT" /* iSCSI Boot Firmware Table */ +#define ACPI_SIG_IORT "IORT" /* IO Remapping Table */ #define ACPI_SIG_IVRS "IVRS" /* I/O Virtualization Reporting Structure */ #define ACPI_SIG_LPIT "LPIT" /* Low Power Idle Table */ #define ACPI_SIG_MCFG "MCFG" /* PCI Memory Mapped Configuration table */ @@ -650,6 +651,131 @@ struct acpi_ibft_target { /******************************************************************************* * + * IORT - IO Remapping Table + * + * Conforms to "IO Remapping Table System Software on ARM Platforms", + * Document number: ARM DEN 0049A, 2015 + * + ******************************************************************************/ + +struct acpi_table_iort { + struct acpi_table_header header; + u32 node_count; + u32 node_offset; + u32 reserved; +}; + +/* + * IORT subtables + */ +struct acpi_iort_node { + u8 type; + u16 length; + u8 revision; + u32 reserved; + u32 mapping_count; + u32 mapping_offset; + char node_data[1]; +}; + +/* Values for subtable Type above */ + +enum acpi_iort_node_type { + ACPI_IORT_NODE_ITS_GROUP = 0x00, + ACPI_IORT_NODE_NAMED_COMPONENT = 0x01, + ACPI_IORT_NODE_PCI_ROOT_COMPLEX = 0x02, + ACPI_IORT_NODE_SMMU = 0x03 +}; + +struct acpi_iort_id_mapping { + u32 input_base; /* Lowest value in input range */ + u32 id_count; /* Number of IDs */ + u32 output_base; /* Lowest value in output range */ + u32 output_reference; /* A reference to the output node */ + u32 flags; +}; + +/* Masks for Flags field above for IORT subtable */ + +#define ACPI_IORT_ID_SINGLE_MAPPING (1) + +struct acpi_iort_memory_access { + u32 cache_coherency; + u8 hints; + u16 reserved; + u8 memory_flags; +}; + +/* Values for cache_coherency field above */ + +#define ACPI_IORT_NODE_COHERENT 0x00000001 /* The device node is fully coherent */ +#define ACPI_IORT_NODE_NOT_COHERENT 0x00000000 /* The device node is not coherent */ + +/* Masks for Hints field above */ + +#define ACPI_IORT_HT_TRANSIENT (1) +#define ACPI_IORT_HT_WRITE (1<<1) +#define ACPI_IORT_HT_READ (1<<2) +#define ACPI_IORT_HT_OVERRIDE (1<<3) + +/* Masks for memory_flags field above */ + +#define ACPI_IORT_MF_COHERENCY (1) +#define ACPI_IORT_MF_ATTRIBUTES (1<<1) + +/* + * IORT node specific subtables + */ +struct acpi_iort_its_group { + u32 its_count; + u32 identifiers[1]; /* GIC ITS identifier arrary */ +}; + +struct acpi_iort_named_component { + u32 node_flags; + u64 memory_properties; /* Memory access properties */ + u8 memory_address_limit; /* Memory address size limit */ + char device_name[1]; /* Path of namespace object */ +}; + +struct acpi_iort_root_complex { + u64 memory_properties; /* Memory access properties */ + u32 ats_attribute; + u32 pci_segment_number; +}; + +/* Values for ats_attribute field above */ + +#define ACPI_IORT_ATS_SUPPORTED 0x00000001 /* The root complex supports ATS */ +#define ACPI_IORT_ATS_UNSUPPORTED 0x00000000 /* The root complex doesn't support ATS */ + +struct acpi_iort_smmu { + u64 base_address; /* SMMU base address */ + u64 span; /* Length of memory range */ + u32 model; + u32 flags; + u32 global_interrupt_offset; + u32 context_interrupt_count; + u32 context_interrupt_offset; + u32 pmu_interrupt_count; + u32 pmu_interrupt_offset; + u64 interrupts[1]; /* Interrupt array */ +}; + +/* Values for Model field above */ + +#define ACPI_IORT_SMMU_V1 0x00000000 /* Generic SMMUv1 */ +#define ACPI_IORT_SMMU_V2 0x00000001 /* Generic SMMUv2 */ +#define ACPI_IORT_SMMU_CORELINK_MMU400 0x00000002 /* ARM Corelink MMU-400 */ +#define ACPI_IORT_SMMU_CORELINK_MMU500 0x00000003 /* ARM Corelink MMU-500 */ + +/* Masks for Flags field above */ + +#define ACPI_IORT_SMMU_DVM_SUPPORTED (1) +#define ACPI_IORT_SMMU_COHERENT_WALK (1<<1) + +/******************************************************************************* + * * IVRS - I/O Virtualization Reporting Structure * Version 1 * -- cgit v0.10.2 From 80fa6cf95e71d56333c89c92dbf74465a9505421 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 21 May 2015 10:31:37 +0800 Subject: ACPICA: ACPI 6.0: Add changes for DRTM table. ACPICA commit b02b754a2b7afcd0384cb3b31f29eb1be028fe90 This patch adds support for DRTM (Dynamic Root of Trust for Measurement table) in iasl. Lv Zheng. Link: https://github.com/acpica/acpica/commit/b02b754a Signed-off-by: Lv Zheng Signed-off-by: Bob Moore Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/actbl3.h b/include/acpi/actbl3.h index e1aac82..4018986 100644 --- a/include/acpi/actbl3.h +++ b/include/acpi/actbl3.h @@ -119,6 +119,8 @@ struct acpi_table_bgrt { /******************************************************************************* * * DRTM - Dynamic Root of Trust for Measurement table + * Conforms to "TCG D-RTM Architecture" June 17 2013, Version 1.0.0 + * Table version 1 * ******************************************************************************/ @@ -135,22 +137,40 @@ struct acpi_table_drtm { u32 flags; }; -/* 1) Validated Tables List */ +/* Flag Definitions for above */ -struct acpi_drtm_vtl_list { - u32 validated_table_list_count; +#define ACPI_DRTM_ACCESS_ALLOWED (1) +#define ACPI_DRTM_ENABLE_GAP_CODE (1<<1) +#define ACPI_DRTM_INCOMPLETE_MEASUREMENTS (1<<2) +#define ACPI_DRTM_AUTHORITY_ORDER (1<<3) + +/* 1) Validated Tables List (64-bit addresses) */ + +struct acpi_drtm_vtable_list { + u32 validated_table_count; + u64 validated_tables[1]; }; -/* 2) Resources List */ +/* 2) Resources List (of Resource Descriptors) */ + +/* Resource Descriptor */ + +struct acpi_drtm_resource { + u8 size[7]; + u8 type; + u64 address; +}; struct acpi_drtm_resource_list { - u32 resource_list_count; + u32 resource_count; + struct acpi_drtm_resource resources[1]; }; /* 3) Platform-specific Identifiers List */ -struct acpi_drtm_id_list { - u32 id_list_count; +struct acpi_drtm_dps_id { + u32 dps_id_length; + u8 dps_id[16]; }; /******************************************************************************* -- cgit v0.10.2 From 5b0bbfbd56118d2f5dca2b56ea47103470fa7aaa Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:31:44 +0800 Subject: ACPICA: iASL/disassembler - fix possible fault for -e option. ACPICA commit 403b8b0023fd7549b2f9bf818fcc1ba481047b69 If non-AML files are used with the -e option, the disassembler can fault. The fix is to ensure that all -e files are either SSDTs or a DSDT. ACPICA BZ 1158. Link: https://github.com/acpica/acpica/commit/403b8b00 Reference: https://bugs.acpica.org/show_bug.cgi?id=1158 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h index 4169bb8..43685dd 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -231,7 +231,9 @@ void acpi_db_open_debug_file(char *name); acpi_status acpi_db_load_acpi_table(char *filename); acpi_status -acpi_db_get_table_from_file(char *filename, struct acpi_table_header **table); +acpi_db_get_table_from_file(char *filename, + struct acpi_table_header **table, + u8 must_be_aml_table); /* * dbhistry - debugger HISTORY command diff --git a/drivers/acpi/acpica/utfileio.c b/drivers/acpi/acpica/utfileio.c index f72c53c..857af82 100644 --- a/drivers/acpi/acpica/utfileio.c +++ b/drivers/acpi/acpica/utfileio.c @@ -312,7 +312,7 @@ acpi_ut_read_table_from_file(char *filename, struct acpi_table_header ** table) /* Get the entire file */ fprintf(stderr, - "Loading Acpi table from file %10s - Length %.8u (%06X)\n", + "Reading ACPI table from file %10s - Length %.8u (0x%06X)\n", filename, file_size, file_size); status = acpi_ut_read_table(file, table, &table_length); diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c index 306e785..98d5787 100644 --- a/drivers/acpi/acpica/utxferror.c +++ b/drivers/acpi/acpica/utxferror.c @@ -107,9 +107,16 @@ acpi_exception(const char *module_name, va_list arg_list; ACPI_MSG_REDIRECT_BEGIN; - acpi_os_printf(ACPI_MSG_EXCEPTION "%s, ", - acpi_format_exception(status)); + /* For AE_OK, just print the message */ + + if (ACPI_SUCCESS(status)) { + acpi_os_printf(ACPI_MSG_EXCEPTION); + + } else { + acpi_os_printf(ACPI_MSG_EXCEPTION "%s, ", + acpi_format_exception(status)); + } va_start(arg_list, format); acpi_os_vprintf(format, arg_list); ACPI_MSG_SUFFIX; -- cgit v0.10.2 From 0bb346cca1899542cdad470f2416070881d54437 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:31:52 +0800 Subject: ACPICA: acpi_help: Add option to display all known/supported ACPI tables. ACPICA commit d6d003556c6fc22e067d5d511577128a661266c3 -t option displays all ACPI tables. Link: https://github.com/acpica/acpica/commit/d6d00355 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 3596958..ffdb956 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -1175,4 +1175,9 @@ struct ah_uuid { char *string; }; +struct ah_table { + char *signature; + char *description; +}; + #endif /* __ACLOCAL_H__ */ -- cgit v0.10.2 From 04f8e38497b02cd5596ff9af278e62cd057fff68 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:31:58 +0800 Subject: ACPICA: ACPI 6.0: Add support for NFIT table. ACPICA commit e4e17ca361373e9b81494bb4ca697a12cef3cba6 NVDIMM Firmware Interface Table. Link: https://github.com/acpica/acpica/commit/e4e17ca3 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c index aa44827..fda8b3d 100644 --- a/drivers/acpi/acpica/uthex.c +++ b/drivers/acpi/acpica/uthex.c @@ -75,9 +75,9 @@ char acpi_ut_hex_to_ascii_char(u64 integer, u32 position) /******************************************************************************* * - * FUNCTION: acpi_ut_hex_char_to_value + * FUNCTION: acpi_ut_ascii_char_to_hex * - * PARAMETERS: ascii_char - Hex character in Ascii + * PARAMETERS: hex_char - Hex character in Ascii * * RETURN: The binary value of the ascii/hex character * diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index cadf21c..06b61f0 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -71,6 +71,7 @@ #define ACPI_SIG_SBST "SBST" /* Smart Battery Specification Table */ #define ACPI_SIG_SLIT "SLIT" /* System Locality Distance Information Table */ #define ACPI_SIG_SRAT "SRAT" /* System Resource Affinity Table */ +#define ACPI_SIG_NFIT "NFIT" /* NVDIMM Firmware Interface Table */ /* * All tables must be byte-packed to match the ACPI specification, since @@ -922,6 +923,159 @@ struct acpi_msct_proximity { /******************************************************************************* * + * NFIT - NVDIMM Interface Table (ACPI 6.0) + * Version 1 + * + ******************************************************************************/ + +struct acpi_table_nfit { + struct acpi_table_header header; /* Common ACPI table header */ + u32 reserved; /* Reserved, must be zero */ +}; + +/* Subtable header for NFIT */ + +struct acpi_nfit_header { + u16 type; + u16 length; +}; + +/* Values for subtable type in struct acpi_nfit_header */ + +enum acpi_nfit_type { + ACPI_NFIT_TYPE_SYSTEM_ADDRESS = 0, + ACPI_NFIT_TYPE_MEMORY_MAP = 1, + ACPI_NFIT_TYPE_INTERLEAVE = 2, + ACPI_NFIT_TYPE_SMBIOS = 3, + ACPI_NFIT_TYPE_CONTROL_REGION = 4, + ACPI_NFIT_TYPE_DATA_REGION = 5, + ACPI_NFIT_TYPE_FLUSH_ADDRESS = 6, + ACPI_NFIT_TYPE_RESERVED = 7 /* 7 and greater are reserved */ +}; + +/* + * NFIT Subtables + */ + +/* 0: System Physical Address Range Structure */ + +struct acpi_nfit_system_address { + struct acpi_nfit_header header; + u16 range_index; + u16 flags; + u32 reserved; /* Reseved, must be zero */ + u32 proximity_domain; + u8 range_guid[16]; + u64 address; + u64 length; + u64 memory_mapping; +}; + +/* Flags */ + +#define ACPI_NFIT_ADD_ONLINE_ONLY (1) /* 00: Add/Online Operation Only */ +#define ACPI_NFIT_PROXIMITY_VALID (1<<1) /* 01: Proximity Domain Valid */ + +/* Range Type GUIDs appear in the include/acuuid.h file */ + +/* 1: Memory Device to System Address Range Map Structure */ + +struct acpi_nfit_memory_map { + struct acpi_nfit_header header; + u32 device_handle; + u16 physical_id; + u16 region_id; + u16 range_index; + u16 region_index; + u64 region_size; + u64 region_offset; + u64 address; + u16 interleave_index; + u16 interleave_ways; + u16 flags; + u16 reserved; /* Reserved, must be zero */ +}; + +/* Flags */ + +#define ACPI_NFIT_MEM_SAVE_FAILED (1) /* 00: Last SAVE to Memory Device failed */ +#define ACPI_NFIT_MEM_RESTORE_FAILED (1<<1) /* 01: Last RESTORE from Memory Device failed */ +#define ACPI_NFIT_MEM_FLUSH_FAILED (1<<2) /* 02: Platform flush failed */ +#define ACPI_NFIT_MEM_ARMED (1<<3) /* 03: Memory Device observed to be not armed */ +#define ACPI_NFIT_MEM_HEALTH_OBSERVED (1<<4) /* 04: Memory Device observed SMART/health events */ +#define ACPI_NFIT_MEM_HEALTH_ENABLED (1<<5) /* 05: SMART/health events enabled */ + +/* 2: Interleave Structure */ + +struct acpi_nfit_interleave { + struct acpi_nfit_header header; + u16 interleave_index; + u16 reserved; /* Reserved, must be zero */ + u32 line_count; + u32 line_size; + u32 line_offset[1]; /* Variable length */ +}; + +/* 3: SMBIOS Management Information Structure */ + +struct acpi_nfit_smbios { + struct acpi_nfit_header header; + u32 reserved; /* Reserved, must be zero */ + u8 data[1]; /* Variable length */ +}; + +/* 4: NVDIMM Control Region Structure */ + +struct acpi_nfit_control_region { + struct acpi_nfit_header header; + u16 region_index; + u16 vendor_id; + u16 device_id; + u16 revision_id; + u16 subsystem_vendor_id; + u16 subsystem_device_id; + u16 subsystem_revision_id; + u8 reserved[6]; /* Reserved, must be zero */ + u32 serial_number; + u16 code; + u16 windows; + u64 window_size; + u64 command_offset; + u64 command_size; + u64 status_offset; + u64 status_size; + u16 flags; + u8 reserved1[6]; /* Reserved, must be zero */ +}; + +/* Flags */ + +#define ACPI_NFIT_CONTROL_BUFFERED (1) /* Block Data Windows implementation is buffered */ + +/* 5: NVDIMM Block Data Window Region Structure */ + +struct acpi_nfit_data_region { + struct acpi_nfit_header header; + u16 region_index; + u16 windows; + u64 offset; + u64 size; + u64 capacity; + u64 start_address; +}; + +/* 6: Flush Hint Address Structure */ + +struct acpi_nfit_flush_address { + struct acpi_nfit_header header; + u32 device_handle; + u16 hint_count; + u8 reserved[6]; /* Reserved, must be zero */ + u64 hint_address[1]; /* Variable length */ +}; + +/******************************************************************************* + * * SBST - Smart Battery Specification Table * Version 1 * diff --git a/include/acpi/acuuid.h b/include/acpi/acuuid.h new file mode 100644 index 0000000..4955d5e --- /dev/null +++ b/include/acpi/acuuid.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * + * Name: acuuid.h - ACPI-related UUID/GUID definitions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2015, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef __ACUUID_H__ +#define __ACUUID_H__ + +/* + * Note1: UUIDs and GUIDs are defined to be identical in ACPI. + * + * Note2: This file is standalone and should remain that way. + */ + +/* NFIT/NVDIMM */ + +#define UUID_VOLATILE_MEMORY "4F940573-DAFD-E344-B16C-3F22D252E5D0" +#define UUID_PERSISTENT_MEMORY "79D3F066-F3B4-7440-AC43-0D3318B78CDB" +#define UUID_CONTROL_REGION "F601F792-B413-5D40-910B-299367E8234C" +#define UUID_DATA_REGION "3005AF91-865D-0E47-A6B0-0A2DB9408249" +#define UUID_VOLATILE_VIRTUAL_DISK "5A53AB77-FC45-4B62-5560-F7B281D1F96E" +#define UUID_VOLATILE_VIRTUAL_CD "30BD5A3D-7541-CE87-6D64-D2ADE523C4BB" +#define UUID_PERSISTENT_VIRTUAL_DISK "C902EA5C-074D-69D3-269F-4496FBE096F9" +#define UUID_PERSISTENT_VIRTUAL_CD "88810108-CD42-48BB-100F-5387D53DED3D" + +#endif /* __AUUID_H__ */ -- cgit v0.10.2 From 1aebaa021f3eb0a9854183e7bf65d0d96d939a27 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Thu, 21 May 2015 10:32:05 +0800 Subject: ACPICA: Update version to 20150515. ACPICA commit ed4de2e8b0a5dd6fc17773a055590bff0e995588 Version 20150515. Link: https://github.com/acpica/acpica/commit/ed4de2e8 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 08ef57b..d68f1cd 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20150410 +#define ACPI_CA_VERSION 0x20150515 #include #include -- cgit v0.10.2 From cae756fbaa02a24f868330e68631b3f5ea345cb8 Mon Sep 17 00:00:00 2001 From: Hanjun Guo Date: Thu, 21 May 2015 23:29:26 +0800 Subject: ACPI / PCI: remove stale list_head in struct acpi_prt_entry list_head "list" in struct acpi_prt_entry was used to connect _PRT entries for PCI irq, but after commit 181380b702ee ("PCI/ACPI: Don't cache _PRT, and don't associate them with bus numbers"), the list head for _PRT entries was removed, but left "list" in struct acpi_prt_entry which is useless and stale, remove it now. Signed-off-by: Hanjun Guo Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index b1def41..03e4b6c 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -44,7 +44,6 @@ ACPI_MODULE_NAME("pci_irq"); struct acpi_prt_entry { - struct list_head list; struct acpi_pci_id id; u8 pin; acpi_handle link; -- cgit v0.10.2 From ee89209402e0b9a733169901063afdf0ae7909db Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 22 May 2015 04:24:34 +0200 Subject: ACPI / property: Define a symbol for PRP0001 Use a #defined symbol ACPI_DT_NAMESPACE_HID instead of the PRP0001 string. Signed-off-by: Rafael J. Wysocki Acked-by: Mika Westerberg Reviewed-by: Hanjun Guo diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index ba4a61e..d93628c 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -191,6 +191,8 @@ bool acpi_osi_is_win8(void); /*-------------------------------------------------------------------------- Device properties -------------------------------------------------------------------------- */ +#define ACPI_DT_NAMESPACE_HID "PRP0001" + void acpi_init_properties(struct acpi_device *adev); void acpi_free_properties(struct acpi_device *adev); diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 76075ee..7836e2e 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -110,11 +110,11 @@ void acpi_init_properties(struct acpi_device *adev) int i; /* - * Check if the special PRP0001 ACPI ID is present and in that case we - * fill in Device Tree compatible properties for this device. + * Check if ACPI_DT_NAMESPACE_HID is present and inthat case we fill in + * Device Tree compatible properties for this device. */ list_for_each_entry(hwid, &adev->pnp.ids, list) { - if (!strcmp(hwid->id, "PRP0001")) { + if (!strcmp(hwid->id, ACPI_DT_NAMESPACE_HID)) { acpi_of = true; break; } @@ -170,7 +170,7 @@ void acpi_init_properties(struct acpi_device *adev) out: if (acpi_of && !adev->flags.of_compatible_ok) acpi_handle_info(adev->handle, - "PRP0001 requires 'compatible' property\n"); + ACPI_DT_NAMESPACE_HID " requires 'compatible' property\n"); } void acpi_free_properties(struct acpi_device *adev) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 03141aa..b19283b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -135,12 +135,13 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias, struct acpi_hardware_id *id; /* - * Since we skip PRP0001 from the modalias below, 0 should be returned - * if PRP0001 is the only ACPI/PNP ID in the device's list. + * Since we skip ACPI_DT_NAMESPACE_HID from the modalias below, 0 should + * be returned if ACPI_DT_NAMESPACE_HID is the only ACPI/PNP ID in the + * device's list. */ count = 0; list_for_each_entry(id, &acpi_dev->pnp.ids, list) - if (strcmp(id->id, "PRP0001")) + if (strcmp(id->id, ACPI_DT_NAMESPACE_HID)) count++; if (!count) @@ -153,7 +154,7 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias, size -= len; list_for_each_entry(id, &acpi_dev->pnp.ids, list) { - if (!strcmp(id->id, "PRP0001")) + if (!strcmp(id->id, ACPI_DT_NAMESPACE_HID)) continue; count = snprintf(&modalias[len], size, "%s:", id->id); @@ -177,7 +178,8 @@ static int create_pnp_modalias(struct acpi_device *acpi_dev, char *modalias, * @size: Size of the buffer. * * Expose DT compatible modalias as of:NnameTCcompatible. This function should - * only be called for devices having PRP0001 in their list of ACPI/PNP IDs. + * only be called for devices having ACPI_DT_NAMESPACE_HID in their list of + * ACPI/PNP IDs. */ static int create_of_modalias(struct acpi_device *acpi_dev, char *modalias, int size) @@ -980,9 +982,9 @@ static void acpi_device_remove_files(struct acpi_device *dev) * @adev: ACPI device object to match. * @of_match_table: List of device IDs to match against. * - * If @dev has an ACPI companion which has the special PRP0001 device ID in its - * list of identifiers and a _DSD object with the "compatible" property, use - * that property to match against the given list of identifiers. + * If @dev has an ACPI companion which has ACPI_DT_NAMESPACE_HID in its list of + * identifiers and a _DSD object with the "compatible" property, use that + * property to match against the given list of identifiers. */ static bool acpi_of_match_device(struct acpi_device *adev, const struct of_device_id *of_match_table) @@ -1038,14 +1040,14 @@ static const struct acpi_device_id *__acpi_match_device( return id; /* - * Next, check the special "PRP0001" ID and try to match the + * Next, check ACPI_DT_NAMESPACE_HID and try to match the * "compatible" property if found. * * The id returned by the below is not valid, but the only * caller passing non-NULL of_ids here is only interested in * whether or not the return value is NULL. */ - if (!strcmp("PRP0001", hwid->id) + if (!strcmp(ACPI_DT_NAMESPACE_HID, hwid->id) && acpi_of_match_device(device, of_ids)) return id; } @@ -2405,7 +2407,7 @@ static void acpi_default_enumeration(struct acpi_device *device) } static const struct acpi_device_id generic_device_ids[] = { - {"PRP0001", }, + {ACPI_DT_NAMESPACE_HID, }, {"", }, }; @@ -2413,8 +2415,8 @@ static int acpi_generic_device_attach(struct acpi_device *adev, const struct acpi_device_id *not_used) { /* - * Since PRP0001 is the only ID handled here, the test below can be - * unconditional. + * Since ACPI_DT_NAMESPACE_HID is the only ID handled here, the test + * below can be unconditional. */ if (adev->data.of_compatible) acpi_default_enumeration(adev); -- cgit v0.10.2 From 58405af6321a6d69de838f7a2d3115b85c987416 Mon Sep 17 00:00:00 2001 From: Shailendra Verma Date: Fri, 22 May 2015 22:48:22 +0530 Subject: cpufreq: Fix for typos in two comments Signed-off-by: Shailendra Verma Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index c08de5e..870df94 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -226,7 +226,7 @@ int cpufreq_generic_init(struct cpufreq_policy *policy, policy->cpuinfo.transition_latency = transition_latency; /* - * The driver only supports the SMP configuartion where all processors + * The driver only supports the SMP configuration where all processors * share the clock and voltage and clock. */ cpumask_setall(policy->cpus); @@ -1931,7 +1931,7 @@ static int __target_index(struct cpufreq_policy *policy, * Failed after setting to intermediate freq? Driver should have * reverted back to initial frequency and so should we. Check * here for intermediate_freq instead of get_intermediate, in - * case we have't switched to intermediate freq at all. + * case we haven't switched to intermediate freq at all. */ if (unlikely(retval && intermediate_freq)) { freqs.old = intermediate_freq; -- cgit v0.10.2 From 9d16f207112f77711600fb0770182a06e056e5de Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 18 May 2015 10:43:31 +0530 Subject: cpufreq: Track cpu managing sysfs kobjects separately In order to prepare for the next few commits, that will stop migrating sysfs files on cpu hotplug, this patch starts managing sysfs-cpu separately. The behavior is still the same as we are still migrating sysfs files on hotplug, later commits would change that. Signed-off-by: Saravana Kannan Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 870df94..5d780ff 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -959,7 +959,7 @@ static int cpufreq_add_dev_symlink(struct cpufreq_policy *policy) for_each_cpu(j, policy->cpus) { struct device *cpu_dev; - if (j == policy->cpu) + if (j == policy->kobj_cpu) continue; pr_debug("Adding link for CPU: %u\n", j); @@ -1178,6 +1178,7 @@ static int update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu, down_write(&policy->rwsem); policy->cpu = cpu; + policy->kobj_cpu = cpu; up_write(&policy->rwsem); return 0; @@ -1235,10 +1236,12 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) * the creation of a brand new one. So we need to perform this update * by invoking update_policy_cpu(). */ - if (recover_policy && cpu != policy->cpu) + if (recover_policy && cpu != policy->cpu) { WARN_ON(update_policy_cpu(policy, cpu, dev)); - else + } else { policy->cpu = cpu; + policy->kobj_cpu = cpu; + } cpumask_copy(policy->cpus, cpumask_of(cpu)); @@ -1417,7 +1420,7 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, CPUFREQ_NAME_LEN); up_write(&policy->rwsem); - if (cpu != policy->cpu) { + if (cpu != policy->kobj_cpu) { sysfs_remove_link(&dev->kobj, "cpufreq"); } else if (cpus > 1) { /* Nominate new CPU */ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 48e37c0..29ad97c 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -65,7 +65,9 @@ struct cpufreq_policy { unsigned int shared_type; /* ACPI: ANY or ALL affected CPUs should set cpufreq */ - unsigned int cpu; /* cpu nr of CPU managing this policy */ + unsigned int cpu; /* cpu managing this policy, must be online */ + unsigned int kobj_cpu; /* cpu managing sysfs files, can be offline */ + struct clk *clk; struct cpufreq_cpuinfo cpuinfo;/* see above */ -- cgit v0.10.2 From 6c0d14680e247849cdb870c995a332781bdb93f2 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 25 May 2015 08:15:27 +0800 Subject: ACPICA: acpihelp: Update for new NFIT table GUIDs. ACPICA commit 83727bed8f715685a63a9f668e73c60496a06054 Add original UUIDs/GUIDs to the acuuid.h file. Cleanup acpihelp output for UUIDs/GUIDs. Link: https://github.com/acpica/acpica/commit/83727bed Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/acuuid.h b/include/acpi/acuuid.h index 4955d5e..7c6cbb0 100644 --- a/include/acpi/acuuid.h +++ b/include/acpi/acuuid.h @@ -50,15 +50,40 @@ * Note2: This file is standalone and should remain that way. */ -/* NFIT/NVDIMM */ +/* Controllers */ -#define UUID_VOLATILE_MEMORY "4F940573-DAFD-E344-B16C-3F22D252E5D0" -#define UUID_PERSISTENT_MEMORY "79D3F066-F3B4-7440-AC43-0D3318B78CDB" -#define UUID_CONTROL_REGION "F601F792-B413-5D40-910B-299367E8234C" -#define UUID_DATA_REGION "3005AF91-865D-0E47-A6B0-0A2DB9408249" -#define UUID_VOLATILE_VIRTUAL_DISK "5A53AB77-FC45-4B62-5560-F7B281D1F96E" -#define UUID_VOLATILE_VIRTUAL_CD "30BD5A3D-7541-CE87-6D64-D2ADE523C4BB" -#define UUID_PERSISTENT_VIRTUAL_DISK "C902EA5C-074D-69D3-269F-4496FBE096F9" -#define UUID_PERSISTENT_VIRTUAL_CD "88810108-CD42-48BB-100F-5387D53DED3D" +#define UUID_GPIO_CONTROLLER "4f248f40-d5e2-499f-834c-27758ea1cd3f" +#define UUID_USB_CONTROLLER "ce2ee385-00e6-48cb-9f05-2edb927c4899" +#define UUID_SATA_CONTROLLER "e4db149b-fcfe-425b-a6d8-92357d78fc7f" + +/* Devices */ + +#define UUID_PCI_HOST_BRIDGE "33db4d5b-1ff7-401c-9657-7441c03dd766" +#define UUID_I2C_DEVICE "3cdff6f7-4267-4555-ad05-b30a3d8938de" +#define UUID_POWER_BUTTON "dfbcf3c5-e7a5-44e6-9c1f-29c76f6e059c" + +/* Interfaces */ + +#define UUID_DEVICE_LABELING "e5c937d0-3553-4d7a-9117-ea4d19c3434d" +#define UUID_PHYSICAL_PRESENCE "3dddfaa6-361b-4eb4-a424-8d10089d1653" + +/* NVDIMM - NFIT table */ + +#define UUID_VOLATILE_MEMORY "4f940573-dafd-e344-b16c-3f22d252e5d0" +#define UUID_PERSISTENT_MEMORY "79d3f066-f3b4-7440-ac43-0d3318b78cdb" +#define UUID_CONTROL_REGION "f601f792-b413-5d40-910b-299367e8234c" +#define UUID_DATA_REGION "3005af91-865d-0e47-a6b0-0a2db9408249" +#define UUID_VOLATILE_VIRTUAL_DISK "5a53ab77-fc45-4b62-5560-f7b281d1f96e" +#define UUID_VOLATILE_VIRTUAL_CD "30bd5a3d-7541-ce87-6d64-d2ade523c4bb" +#define UUID_PERSISTENT_VIRTUAL_DISK "c902ea5c-074d-69d3-269f-4496fbe096f9" +#define UUID_PERSISTENT_VIRTUAL_CD "88810108-cd42-48bb-100f-5387d53ded3d" + +/* Miscellaneous */ + +#define UUID_PLATFORM_CAPABILITIES "0811b06e-4a27-44f9-8d60-3cbbc22e7b48" +#define UUID_DYNAMIC_ENUMERATION "d8c1a3a6-be9b-4c9b-91bf-c3cb81fc5daf" +#define UUID_BATTERY_THERMAL_LIMIT "4c2067e3-887d-475c-9720-4af1d3ed602e" +#define UUID_THERMAL_EXTENSIONS "14d399cd-7a27-4b18-8fb4-7cb7b9f4e500" +#define UUID_DEVICE_PROPERTIES "daffd814-6eba-4d8c-8a91-bc9bbf4aa301" #endif /* __AUUID_H__ */ -- cgit v0.10.2 From f3b6ced236259a87829b829e8e542ff53bfb9a4f Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 25 May 2015 08:15:35 +0800 Subject: ACPICA: Fix for ill-formed GUID strings for NFIT tables. ACPICA commit 60052949ba2aa7377106870da69b237193d10dc1 Error in transcription from the ACPI spec. Link: https://github.com/acpica/acpica/commit/60052949 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/include/acpi/acuuid.h b/include/acpi/acuuid.h index 7c6cbb0..80fe8cf 100644 --- a/include/acpi/acuuid.h +++ b/include/acpi/acuuid.h @@ -69,14 +69,14 @@ /* NVDIMM - NFIT table */ -#define UUID_VOLATILE_MEMORY "4f940573-dafd-e344-b16c-3f22d252e5d0" -#define UUID_PERSISTENT_MEMORY "79d3f066-f3b4-7440-ac43-0d3318b78cdb" -#define UUID_CONTROL_REGION "f601f792-b413-5d40-910b-299367e8234c" -#define UUID_DATA_REGION "3005af91-865d-0e47-a6b0-0a2db9408249" -#define UUID_VOLATILE_VIRTUAL_DISK "5a53ab77-fc45-4b62-5560-f7b281d1f96e" -#define UUID_VOLATILE_VIRTUAL_CD "30bd5a3d-7541-ce87-6d64-d2ade523c4bb" -#define UUID_PERSISTENT_VIRTUAL_DISK "c902ea5c-074d-69d3-269f-4496fbe096f9" -#define UUID_PERSISTENT_VIRTUAL_CD "88810108-cd42-48bb-100f-5387d53ded3d" +#define UUID_VOLATILE_MEMORY "7305944f-fdda-44e3-b16c-3f22d252e5d0" +#define UUID_PERSISTENT_MEMORY "66f0d379-b4f3-4074-ac43-0d3318b78cdb" +#define UUID_CONTROL_REGION "92f701f6-13b4-405d-910b-299367e8234c" +#define UUID_DATA_REGION "91af0530-5d86-470e-a6b0-0a2db9408249" +#define UUID_VOLATILE_VIRTUAL_DISK "77ab535a-45fc-624b-5560-f7b281d1f96e" +#define UUID_VOLATILE_VIRTUAL_CD "3d5abd30-4175-87ce-6d64-d2ade523c4bb" +#define UUID_PERSISTENT_VIRTUAL_DISK "5cea02c9-4d07-69d3-269f-4496fbe096f9" +#define UUID_PERSISTENT_VIRTUAL_CD "08018188-42cd-bb48-100f-5387d53ded3d" /* Miscellaneous */ -- cgit v0.10.2 From d5eefa8280a8bb1e8aef059154bc1d63e1ac3336 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 May 2015 04:19:49 +0200 Subject: ACPI / PM: Turn power resources on and off in the right order during resume According to Section 7.2 of ACPI 6.0, power resources should always be enabled and disabled in order given by the "resourceorder" field of the corresponding Power Resource objects: "Power Resource levels are enabled from low values to high values and are disabled from high values to low values." However, this is not what happens during system resume, because in that case the enabling/disabling is carried out in the power resource registration order which may not reflect the ordering required by the platform. For this reason, make the ordering of the global list of all power resources in the system (used by the system resume code) reflect the one given by the "resourceorder" attributes of the Power Resource objects in the ACPI namespace and modify acpi_resume_power_resources() to walk the list in the reverse order when turning off the power resources that had been off before the system was suspended. Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 1f8138f..93eac53 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -760,6 +760,25 @@ static void acpi_power_sysfs_remove(struct acpi_device *device) device_remove_file(&device->dev, &dev_attr_resource_in_use); } +static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource) +{ + mutex_lock(&power_resource_list_lock); + + if (!list_empty(&acpi_power_resource_list)) { + struct acpi_power_resource *r; + + list_for_each_entry(r, &acpi_power_resource_list, list_node) + if (r->order > resource->order) { + list_add_tail(&resource->list_node, &r->list_node); + goto out; + } + } + list_add_tail(&resource->list_node, &acpi_power_resource_list); + + out: + mutex_unlock(&power_resource_list_lock); +} + int acpi_add_power_resource(acpi_handle handle) { struct acpi_power_resource *resource; @@ -810,9 +829,7 @@ int acpi_add_power_resource(acpi_handle handle) if (!device_create_file(&device->dev, &dev_attr_resource_in_use)) device->remove = acpi_power_sysfs_remove; - mutex_lock(&power_resource_list_lock); - list_add(&resource->list_node, &acpi_power_resource_list); - mutex_unlock(&power_resource_list_lock); + acpi_power_add_resource_to_list(resource); acpi_device_add_finalize(device); return 0; @@ -843,7 +860,22 @@ void acpi_resume_power_resources(void) && resource->ref_count) { dev_info(&resource->device.dev, "Turning ON\n"); __acpi_power_on(resource); - } else if (state == ACPI_POWER_RESOURCE_STATE_ON + } + + mutex_unlock(&resource->resource_lock); + } + list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) { + int result, state; + + mutex_lock(&resource->resource_lock); + + result = acpi_power_get_state(resource->device.handle, &state); + if (result) { + mutex_unlock(&resource->resource_lock); + continue; + } + + if (state == ACPI_POWER_RESOURCE_STATE_ON && !resource->ref_count) { dev_info(&resource->device.dev, "Turning OFF\n"); __acpi_power_off(resource); -- cgit v0.10.2 From 7d51d97925e6cbfa2f7f14e3e3aa363b35ee5c24 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 28 May 2015 04:09:24 +0200 Subject: cpuidle: Do not use CPUIDLE_DRIVER_STATE_START in cpuidle.c The CPUIDLE_DRIVER_STATE_START symbol is defined as 1 only if CONFIG_ARCH_HAS_CPU_RELAX is set, otherwise it is defined as 0. However, if CONFIG_ARCH_HAS_CPU_RELAX is set, the first (index 0) entry in the cpuidle driver's table of states is overwritten with the default "poll" entry by the core. The "state" defined by the "poll" entry doesn't provide ->enter_dead and ->enter_freeze callbacks and its exit_latency is 0. For this reason, it is not necessary to use CPUIDLE_DRIVER_STATE_START in cpuidle_play_dead() (->enter_dead is NULL, so the "poll state" will be skipped by the loop). It also is arguably unuseful to return states with exit_latency equal to 0 from find_deepest_state(), so the function can be modified to start the loop from index 0 and the "poll state" will be skipped by it as a result of the check against latency_req. Signed-off-by: Rafael J. Wysocki Reviewed-by: Preeti U Murthy diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index af6dd59..7f1b8f5 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -65,7 +65,7 @@ int cpuidle_play_dead(void) return -ENODEV; /* Find lowest-power state that supports long-term idle */ - for (i = drv->state_count - 1; i >= CPUIDLE_DRIVER_STATE_START; i--) + for (i = drv->state_count - 1; i >= 0; i--) if (drv->states[i].enter_dead) return drv->states[i].enter_dead(dev, i); @@ -79,9 +79,9 @@ static int find_deepest_state(struct cpuidle_driver *drv, bool freeze) { unsigned int latency_req = 0; - int i, ret = freeze ? -1 : CPUIDLE_DRIVER_STATE_START - 1; + int i, ret = -ENXIO; - for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { + for (i = 0; i < drv->state_count; i++) { struct cpuidle_state *s = &drv->states[i]; struct cpuidle_state_usage *su = &dev->states_usage[i]; -- cgit v0.10.2 From db874c7e10557f8f1af9a6fb1ec6589ae06f349c Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 29 May 2015 09:54:02 -0700 Subject: PM / wakeirq: Fix typo in prototype for dev_pm_set_dedicated_wake_irq Looks like I only built test the dev_pm_set_wake_irq and not the dev_pm_set_dedicated_wake_irq case on x86. Turns out there's a typo for the dev_pm_set_dedicated_wake_irq prototype that causes a build error if CONFIG_COMPILE_TEST and CONFIG_MMC_OMAP_HS are selected. Reported-by: Jim Davis Signed-off-by: Tony Lindgren Reviewed-by: Felipe Balbi Signed-off-by: Rafael J. Wysocki diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h index 4046fa1..cd5b62d 100644 --- a/include/linux/pm_wakeirq.h +++ b/include/linux/pm_wakeirq.h @@ -30,8 +30,7 @@ static inline int dev_pm_set_wake_irq(struct device *dev, int irq) return 0; } -static inline int dev_pm_set_dedicated__wake_irq(struct device *dev, - int irq) +static inline int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) { return 0; } -- cgit v0.10.2 From 47b98c74fab2d05fd724a6d9fd0efc8987ae3911 Mon Sep 17 00:00:00 2001 From: "Herton R. Krzesinski" Date: Sat, 30 May 2015 02:21:31 +0200 Subject: cpupower: mperf monitor: fix output in MAX_FREQ_SYSFS mode There is clearly wrong output when mperf monitor runs in MAX_FREQ_SYSFS mode: average frequency shows in kHz unit (despite the intended output to be in MHz), and percentages for C state information are all wrong (including high/negative values shown). The problem is that the max_frequency read on initialization isn't used where it should have been used on mperf_get_count_percent (to estimate the number of ticks in the given time period), and the value we read from sysfs is in kHz, so we must divide it to get the MHz value to use in current calculations. While at it, also I fixed another small issues in the debug output of max_frequency value in mperf_get_count_freq. Signed-off-by: Herton R. Krzesinski Acked-by: Thomas Renninger Signed-off-by: Rafael J. Wysocki diff --git a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c index 90a8c4f..c83f160 100644 --- a/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/mperf_monitor.c @@ -135,7 +135,7 @@ static int mperf_get_count_percent(unsigned int id, double *percent, dprint("%s: TSC Ref - mperf_diff: %llu, tsc_diff: %llu\n", mperf_cstates[id].name, mperf_diff, tsc_diff); } else if (max_freq_mode == MAX_FREQ_SYSFS) { - timediff = timespec_diff_us(time_start, time_end); + timediff = max_frequency * timespec_diff_us(time_start, time_end); *percent = 100.0 * mperf_diff / timediff; dprint("%s: MAXFREQ - mperf_diff: %llu, time_diff: %llu\n", mperf_cstates[id].name, mperf_diff, timediff); @@ -176,7 +176,7 @@ static int mperf_get_count_freq(unsigned int id, unsigned long long *count, dprint("%s: Average freq based on %s maximum frequency:\n", mperf_cstates[id].name, (max_freq_mode == MAX_FREQ_TSC_REF) ? "TSC calculated" : "sysfs read"); - dprint("%max_frequency: %lu", max_frequency); + dprint("max_frequency: %lu\n", max_frequency); dprint("aperf_diff: %llu\n", aperf_diff); dprint("mperf_diff: %llu\n", mperf_diff); dprint("avg freq: %llu\n", *count); @@ -279,6 +279,7 @@ use_sysfs: return -1; } max_freq_mode = MAX_FREQ_SYSFS; + max_frequency /= 1000; /* Default automatically to MHz value */ return 0; } -- cgit v0.10.2 From 3d56402d3fa8d10749eeb36293dd1992bd5ad0c3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Jun 2015 01:32:38 +0200 Subject: ACPI / PM: Add missing pm_generic_complete() invocation Add missing invocation of pm_generic_complete() to acpi_subsys_complete() to allow ->complete callbacks provided by the drivers of devices using the ACPI PM domain to be executed during system resume. Fixes: f25c0ae2b4c4 (ACPI / PM: Avoid resuming devices in ACPI PM domain during system suspend) Cc: 3.16+ # 3.16+ Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 87c16a5..717afcd 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -972,6 +972,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_prepare); */ void acpi_subsys_complete(struct device *dev) { + pm_generic_complete(dev); /* * If the device had been runtime-suspended before the system went into * the sleep state it is going out of and it has never been resumed till -- cgit v0.10.2 From f16255eb930173f386db0ce78ed41401aa8a94a6 Mon Sep 17 00:00:00 2001 From: Doug Smythies Date: Sun, 31 May 2015 07:46:47 -0700 Subject: intel_pstate: change some inconsistent debug information Commit ce717613f3fb (intel_pstate: Turn per cpu printk into pr_debug) turned per cpu printk into pr_debug. However, only half of the change was done, introducing an inconsistency between entry and exit from driver pstate control. This patch changes the exit message to pr_debug also. The various messages are inconsistent with respect to any identifier text that can be used to help isolate the desired information from a huge log. This patch makes a consistent identifier portion of the string. Amends: ce717613f3fb (intel_pstate: Turn per cpu printk into pr_debug) Signed-off-by: Doug Smythies Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 2f329b4..2050796 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -398,7 +398,7 @@ static ssize_t store_no_turbo(struct kobject *a, struct attribute *b, update_turbo_state(); if (limits.turbo_disabled) { - pr_warn("Turbo disabled by BIOS or unavailable on processor\n"); + pr_warn("intel_pstate: Turbo disabled by BIOS or unavailable on processor\n"); return -EPERM; } @@ -486,7 +486,7 @@ static void __init intel_pstate_sysfs_expose_params(void) static void intel_pstate_hwp_enable(void) { hwp_active++; - pr_info("intel_pstate HWP enabled\n"); + pr_info("intel_pstate: HWP enabled\n"); wrmsrl( MSR_PM_ENABLE, 0x1); } @@ -946,7 +946,7 @@ static int intel_pstate_init_cpu(unsigned int cpunum) add_timer_on(&cpu->timer, cpunum); - pr_debug("Intel pstate controlling: cpu %d\n", cpunum); + pr_debug("intel_pstate: controlling: cpu %d\n", cpunum); return 0; } @@ -1012,7 +1012,7 @@ static void intel_pstate_stop_cpu(struct cpufreq_policy *policy) int cpu_num = policy->cpu; struct cpudata *cpu = all_cpu_data[cpu_num]; - pr_info("intel_pstate CPU %d exiting\n", cpu_num); + pr_debug("intel_pstate: CPU %d exiting\n", cpu_num); del_timer_sync(&all_cpu_data[cpu_num]->timer); if (hwp_active) -- cgit v0.10.2 From 6c1e45917dec5e7c99ba8125fd8cc50f6e482a21 Mon Sep 17 00:00:00 2001 From: Doug Smythies Date: Mon, 1 Jun 2015 21:12:34 -0700 Subject: intel_pstate: Force setting target pstate when required During initialization and exit it is possible that the target pstate might not actually be set. Furthermore, the result can be that the driver and the processor are out of synch and, under some conditions, the driver might never send the processor the proper target pstate. This patch adds a bypass or do_checks flag to the call to intel_pstate_set_pstate. If bypass, then specifically bypass clamp checks and the do not send if it is the same as last time check. If do_checks, then, and as before, do the current policy clamp checks, and do not do actual send if the new target is the same as the old. Signed-off-by: Doug Smythies Reported-by: Marien Zwart Reported-by: Alex Lochmann Reported-by: Piotr Ko?aczkowski Reported-by: Clemens Eisserer Tested-by: Marien Zwart Tested-by: Doug Smythies [ rjw: Dropped pointless symbol definitions, rebased ] Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 2050796..aef3572 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -706,19 +706,20 @@ static void intel_pstate_get_min_max(struct cpudata *cpu, int *min, int *max) *min = clamp_t(int, min_perf, cpu->pstate.min_pstate, max_perf); } -static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) +static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate, bool force) { int max_perf, min_perf; - update_turbo_state(); - - intel_pstate_get_min_max(cpu, &min_perf, &max_perf); + if (force) { + update_turbo_state(); - pstate = clamp_t(int, pstate, min_perf, max_perf); + intel_pstate_get_min_max(cpu, &min_perf, &max_perf); - if (pstate == cpu->pstate.current_pstate) - return; + pstate = clamp_t(int, pstate, min_perf, max_perf); + if (pstate == cpu->pstate.current_pstate) + return; + } trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); cpu->pstate.current_pstate = pstate; @@ -735,7 +736,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) if (pstate_funcs.get_vid) pstate_funcs.get_vid(cpu); - intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); + intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate, false); } static inline void intel_pstate_calc_busy(struct cpudata *cpu) @@ -855,7 +856,7 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu) ctl = pid_calc(pid, busy_scaled); /* Negative values of ctl increase the pstate and vice versa */ - intel_pstate_set_pstate(cpu, cpu->pstate.current_pstate - ctl); + intel_pstate_set_pstate(cpu, cpu->pstate.current_pstate - ctl, true); sample = &cpu->sample; trace_pstate_sample(fp_toint(sample->core_pct_busy), @@ -1018,7 +1019,7 @@ static void intel_pstate_stop_cpu(struct cpufreq_policy *policy) if (hwp_active) return; - intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); + intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate, false); } static int intel_pstate_cpu_init(struct cpufreq_policy *policy) -- cgit v0.10.2 From 11e584cfb8a9d2226151fd39bfa74d09e575f72d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 10 Jun 2015 02:11:45 +0200 Subject: cpufreq: Don't allow updating inactive policies from sysfs Later commits would change the way policies are managed today. Policies wouldn't be freed on cpu hotplug (currently they aren't freed only for suspend), and while the CPU is offline, the sysfs cpufreq files would still be present. User may accidentally try to update the sysfs files in following directory: '/sys/devices/system/cpu/cpuX/cpufreq/'. And that would result in undefined behavior as policy wouldn't be active then. Apart from updating the store() routine, we also update __cpufreq_get() which can call cpufreq_out_of_sync(). The later routine tries to update policy->cur and starts notifying kernel about it. Signed-off-by: Viresh Kumar Acked-by: Saravana Kannan Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 5d780ff..16214df 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -875,11 +875,18 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr, down_write(&policy->rwsem); + /* Updating inactive policies is invalid, so avoid doing that. */ + if (unlikely(policy_is_inactive(policy))) { + ret = -EBUSY; + goto unlock_policy_rwsem; + } + if (fattr->store) ret = fattr->store(policy, buf, count); else ret = -EIO; +unlock_policy_rwsem: up_write(&policy->rwsem); up_read(&cpufreq_rwsem); @@ -1610,6 +1617,10 @@ static unsigned int __cpufreq_get(struct cpufreq_policy *policy) ret_freq = cpufreq_driver->get(policy->cpu); + /* Updating inactive policies is invalid, so avoid doing that. */ + if (unlikely(policy_is_inactive(policy))) + return ret_freq; + if (ret_freq && policy->cur && !(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) { /* verify no discrepancy between actual and -- cgit v0.10.2 From 32e8d689dc12e29fcb6ba9c65a33473d0cbdfec8 Mon Sep 17 00:00:00 2001 From: Todd E Brandt Date: Thu, 28 May 2015 12:55:53 -0700 Subject: PM / sleep: trace_device_pm_callback coverage in dpm_prepare/complete Move the trace_device_pm_callback locations for dpm_prepare and dpm_complete to encompass the attempt to capture the device mutex prior to callback. This is needed by analyze_suspend to identify gaps in the trace output caused by the delay in locking the mutex for a device. Signed-off-by: Todd Brandt Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 3d874ec..5528e59 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -920,9 +920,7 @@ static void device_complete(struct device *dev, pm_message_t state) if (callback) { pm_dev_dbg(dev, state, info); - trace_device_pm_callback_start(dev, info, state.event); callback(dev); - trace_device_pm_callback_end(dev, 0); } device_unlock(dev); @@ -954,7 +952,9 @@ void dpm_complete(pm_message_t state) list_move(&dev->power.entry, &list); mutex_unlock(&dpm_list_mtx); + trace_device_pm_callback_start(dev, "", state.event); device_complete(dev, state); + trace_device_pm_callback_end(dev, 0); mutex_lock(&dpm_list_mtx); put_device(dev); @@ -1585,11 +1585,8 @@ static int device_prepare(struct device *dev, pm_message_t state) callback = dev->driver->pm->prepare; } - if (callback) { - trace_device_pm_callback_start(dev, info, state.event); + if (callback) ret = callback(dev); - trace_device_pm_callback_end(dev, ret); - } device_unlock(dev); @@ -1631,7 +1628,9 @@ int dpm_prepare(pm_message_t state) get_device(dev); mutex_unlock(&dpm_list_mtx); + trace_device_pm_callback_start(dev, "", state.event); error = device_prepare(dev, state); + trace_device_pm_callback_end(dev, error); mutex_lock(&dpm_list_mtx); if (error) { -- cgit v0.10.2 From b064a8fa77dfead647564c46ac8fc5b13bd1ab73 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Jun 2015 01:33:36 +0200 Subject: ACPI / init: Switch over platform to the ACPI mode later Commit 73f7d1ca3263 "ACPI / init: Run acpi_early_init() before timekeeping_init()" moved the ACPI subsystem initialization, including the ACPI mode enabling, to an earlier point in the initialization sequence, to allow the timekeeping subsystem use ACPI early. Unfortunately, that resulted in boot regressions on some systems and the early ACPI initialization was moved toward its original position in the kernel initialization code by commit c4e1acbb35e4 "ACPI / init: Invoke early ACPI initialization later". However, that turns out to be insufficient, as boot is still broken on the Tyan S8812 mainboard. To fix that issue, split the ACPI early initialization code into two pieces so the majority of it still located in acpi_early_init() and the part switching over the platform into the ACPI mode goes into a new function, acpi_subsystem_init(), executed at the original early ACPI initialization spot. That fixes the Tyan S8812 boot problem, but still allows ACPI tables to be loaded earlier which is useful to the EFI code in efi_enter_virtual_mode(). Link: https://bugzilla.kernel.org/show_bug.cgi?id=97141 Fixes: 73f7d1ca3263 "ACPI / init: Run acpi_early_init() before timekeeping_init()" Reported-and-tested-by: Marius Tolzmann Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Reviewed-by: Hanjun Guo Reviewed-by: Lee, Chun-Yi diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index c412fdb..513e7230e 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -470,6 +470,16 @@ static int __init acpi_bus_init_irq(void) return 0; } +/** + * acpi_early_init - Initialize ACPICA and populate the ACPI namespace. + * + * The ACPI tables are accessible after this, but the handling of events has not + * been initialized and the global lock is not available yet, so AML should not + * be executed at this point. + * + * Doing this before switching the EFI runtime services to virtual mode allows + * the EfiBootServices memory to be freed slightly earlier on boot. + */ void __init acpi_early_init(void) { acpi_status status; @@ -533,26 +543,42 @@ void __init acpi_early_init(void) acpi_gbl_FADT.sci_interrupt = acpi_sci_override_gsi; } #endif + return; + + error0: + disable_acpi(); +} + +/** + * acpi_subsystem_init - Finalize the early initialization of ACPI. + * + * Switch over the platform to the ACPI mode (if possible), initialize the + * handling of ACPI events, install the interrupt and global lock handlers. + * + * Doing this too early is generally unsafe, but at the same time it needs to be + * done before all things that really depend on ACPI. The right spot appears to + * be before finalizing the EFI initialization. + */ +void __init acpi_subsystem_init(void) +{ + acpi_status status; + + if (acpi_disabled) + return; status = acpi_enable_subsystem(~ACPI_NO_ACPI_ENABLE); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX "Unable to enable ACPI\n"); - goto error0; + disable_acpi(); + } else { + /* + * If the system is using ACPI then we can be reasonably + * confident that any regulators are managed by the firmware + * so tell the regulator core it has everything it needs to + * know. + */ + regulator_has_full_constraints(); } - - /* - * If the system is using ACPI then we can be reasonably - * confident that any regulators are managed by the firmware - * so tell the regulator core it has everything it needs to - * know. - */ - regulator_has_full_constraints(); - - return; - - error0: - disable_acpi(); - return; } static int __init acpi_bus_init(void) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e4da5e3..4550be3 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -440,6 +440,7 @@ extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, #define ACPI_OST_SC_INSERT_NOT_SUPPORTED 0x82 extern void acpi_early_init(void); +extern void acpi_subsystem_init(void); extern int acpi_nvs_register(__u64 start, __u64 size); @@ -494,6 +495,7 @@ static inline const char *acpi_dev_name(struct acpi_device *adev) } static inline void acpi_early_init(void) { } +static inline void acpi_subsystem_init(void) { } static inline int early_acpi_boot_init(void) { diff --git a/init/main.c b/init/main.c index 2115055..2a89545 100644 --- a/init/main.c +++ b/init/main.c @@ -664,6 +664,7 @@ asmlinkage __visible void __init start_kernel(void) check_bugs(); + acpi_subsystem_init(); sfi_init_late(); if (efi_enabled(EFI_RUNTIME_SERVICES)) { -- cgit v0.10.2 From 87549141d516aee71d511138e27117c41e8aef68 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 10 Jun 2015 02:13:21 +0200 Subject: cpufreq: Stop migrating sysfs files on hotplug When we hot-unplug a cpu, we remove its sysfs cpufreq directory and if the outgoing cpu was the owner of policy->kobj earlier then we migrate the sysfs directory to under another online cpu. There are few disadvantages this brings: - Code Complexity - Slower hotplug/suspend/resume - sysfs file permissions are reset after all policy->cpus are offlined - CPUFreq stats history lost after all policy->cpus are offlined - Special management of sysfs stuff during suspend/resume To overcome these, this patch modifies the way sysfs directories are managed: - Select sysfs kobjects owner while initializing policy and don't change it during hotplugs. Track it with kobj_cpu created earlier. - Create symlinks for all related CPUs (can be offline) instead of affected CPUs on policy initialization and remove them only when the policy is freed. - Free policy structure only on the removal of cpufreq-driver and not during hotplug/suspend/resume, detected by checking 'struct subsys_interface *' (Valid only when called from subsys_interface_unregister() while unregistering driver). Apart from this, special care is taken to handle physical hoplug of CPUs as we wouldn't remove sysfs links or remove policies on logical hotplugs. Physical hotplug happens in the following sequence. Hot removal: - CPU is offlined first, ~ 'echo 0 > /sys/devices/system/cpu/cpuX/online' - Then its device is removed along with all sysfs files, cpufreq core notified with cpufreq_remove_dev() callback from subsys-interface.. Hot addition: - First the device along with its sysfs files is added, cpufreq core notified with cpufreq_add_dev() callback from subsys-interface.. - CPU is onlined, ~ 'echo 1 > /sys/devices/system/cpu/cpuX/online' We call the same routines with both hotplug and subsys callbacks, and we sense physical hotplug with cpu_offline() check in subsys callback. We can handle most of the stuff with regular hotplug callback paths and add/remove cpufreq sysfs links or free policy from subsys callbacks. Original-by: Saravana Kannan Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 16214df..4bfe0a53 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -957,28 +957,67 @@ void cpufreq_sysfs_remove_file(const struct attribute *attr) } EXPORT_SYMBOL(cpufreq_sysfs_remove_file); -/* symlink affected CPUs */ +static int add_cpu_dev_symlink(struct cpufreq_policy *policy, int cpu) +{ + struct device *cpu_dev; + + pr_debug("%s: Adding symlink for CPU: %u\n", __func__, cpu); + + if (!policy) + return 0; + + cpu_dev = get_cpu_device(cpu); + if (WARN_ON(!cpu_dev)) + return 0; + + return sysfs_create_link(&cpu_dev->kobj, &policy->kobj, "cpufreq"); +} + +static void remove_cpu_dev_symlink(struct cpufreq_policy *policy, int cpu) +{ + struct device *cpu_dev; + + pr_debug("%s: Removing symlink for CPU: %u\n", __func__, cpu); + + cpu_dev = get_cpu_device(cpu); + if (WARN_ON(!cpu_dev)) + return; + + sysfs_remove_link(&cpu_dev->kobj, "cpufreq"); +} + +/* Add/remove symlinks for all related CPUs */ static int cpufreq_add_dev_symlink(struct cpufreq_policy *policy) { unsigned int j; int ret = 0; - for_each_cpu(j, policy->cpus) { - struct device *cpu_dev; - + /* Some related CPUs might not be present (physically hotplugged) */ + for_each_cpu_and(j, policy->related_cpus, cpu_present_mask) { if (j == policy->kobj_cpu) continue; - pr_debug("Adding link for CPU: %u\n", j); - cpu_dev = get_cpu_device(j); - ret = sysfs_create_link(&cpu_dev->kobj, &policy->kobj, - "cpufreq"); + ret = add_cpu_dev_symlink(policy, j); if (ret) break; } + return ret; } +static void cpufreq_remove_dev_symlink(struct cpufreq_policy *policy) +{ + unsigned int j; + + /* Some related CPUs might not be present (physically hotplugged) */ + for_each_cpu_and(j, policy->related_cpus, cpu_present_mask) { + if (j == policy->kobj_cpu) + continue; + + remove_cpu_dev_symlink(policy, j); + } +} + static int cpufreq_add_dev_interface(struct cpufreq_policy *policy, struct device *dev) { @@ -1075,7 +1114,7 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, } } - return sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq"); + return 0; } static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu) @@ -1095,7 +1134,7 @@ static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu) return policy; } -static struct cpufreq_policy *cpufreq_policy_alloc(void) +static struct cpufreq_policy *cpufreq_policy_alloc(int cpu) { struct cpufreq_policy *policy; @@ -1116,6 +1155,11 @@ static struct cpufreq_policy *cpufreq_policy_alloc(void) init_completion(&policy->kobj_unregister); INIT_WORK(&policy->update, handle_update); + policy->cpu = cpu; + + /* Set this once on allocation */ + policy->kobj_cpu = cpu; + return policy; err_free_cpumask: @@ -1134,10 +1178,11 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy) blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_REMOVE_POLICY, policy); - down_read(&policy->rwsem); + down_write(&policy->rwsem); + cpufreq_remove_dev_symlink(policy); kobj = &policy->kobj; cmp = &policy->kobj_unregister; - up_read(&policy->rwsem); + up_write(&policy->rwsem); kobject_put(kobj); /* @@ -1168,27 +1213,14 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy) kfree(policy); } -static int update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu, - struct device *cpu_dev) +static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu) { - int ret; - if (WARN_ON(cpu == policy->cpu)) - return 0; - - /* Move kobject to the new policy->cpu */ - ret = kobject_move(&policy->kobj, &cpu_dev->kobj); - if (ret) { - pr_err("%s: Failed to move kobj: %d\n", __func__, ret); - return ret; - } + return; down_write(&policy->rwsem); policy->cpu = cpu; - policy->kobj_cpu = cpu; up_write(&policy->rwsem); - - return 0; } /** @@ -1206,13 +1238,19 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) int ret = -ENOMEM; struct cpufreq_policy *policy; unsigned long flags; - bool recover_policy = cpufreq_suspended; - - if (cpu_is_offline(cpu)) - return 0; + bool recover_policy = !sif; pr_debug("adding CPU %u\n", cpu); + /* + * Only possible if 'cpu' wasn't physically present earlier and we are + * here from subsys_interface add callback. A hotplug notifier will + * follow and we will handle it like logical CPU hotplug then. For now, + * just create the sysfs link. + */ + if (cpu_is_offline(cpu)) + return add_cpu_dev_symlink(per_cpu(cpufreq_cpu_data, cpu), cpu); + if (!down_read_trylock(&cpufreq_rwsem)) return 0; @@ -1232,7 +1270,7 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) policy = recover_policy ? cpufreq_policy_restore(cpu) : NULL; if (!policy) { recover_policy = false; - policy = cpufreq_policy_alloc(); + policy = cpufreq_policy_alloc(cpu); if (!policy) goto nomem_out; } @@ -1243,12 +1281,8 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) * the creation of a brand new one. So we need to perform this update * by invoking update_policy_cpu(). */ - if (recover_policy && cpu != policy->cpu) { - WARN_ON(update_policy_cpu(policy, cpu, dev)); - } else { - policy->cpu = cpu; - policy->kobj_cpu = cpu; - } + if (recover_policy && cpu != policy->cpu) + update_policy_cpu(policy, cpu); cpumask_copy(policy->cpus, cpumask_of(cpu)); @@ -1427,29 +1461,14 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, CPUFREQ_NAME_LEN); up_write(&policy->rwsem); - if (cpu != policy->kobj_cpu) { - sysfs_remove_link(&dev->kobj, "cpufreq"); - } else if (cpus > 1) { - /* Nominate new CPU */ - int new_cpu = cpumask_any_but(policy->cpus, cpu); - struct device *cpu_dev = get_cpu_device(new_cpu); - - sysfs_remove_link(&cpu_dev->kobj, "cpufreq"); - ret = update_policy_cpu(policy, new_cpu, cpu_dev); - if (ret) { - if (sysfs_create_link(&cpu_dev->kobj, &policy->kobj, - "cpufreq")) - pr_err("%s: Failed to restore kobj link to cpu:%d\n", - __func__, cpu_dev->id); - return ret; - } + if (cpu != policy->cpu) + return 0; - if (!cpufreq_suspended) - pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n", - __func__, new_cpu, cpu); - } else if (cpufreq_driver->stop_cpu) { + if (cpus > 1) + /* Nominate new CPU */ + update_policy_cpu(policy, cpumask_any_but(policy->cpus, cpu)); + else if (cpufreq_driver->stop_cpu) cpufreq_driver->stop_cpu(policy); - } return 0; } @@ -1470,32 +1489,11 @@ static int __cpufreq_remove_dev_finish(struct device *dev, cpumask_clear_cpu(cpu, policy->cpus); up_write(&policy->rwsem); - /* If cpu is last user of policy, free policy */ - if (policy_is_inactive(policy)) { - if (has_target()) { - ret = __cpufreq_governor(policy, - CPUFREQ_GOV_POLICY_EXIT); - if (ret) { - pr_err("%s: Failed to exit governor\n", - __func__); - return ret; - } - } - - if (!cpufreq_suspended) - cpufreq_policy_put_kobj(policy); + /* Not the last cpu of policy, start governor again ? */ + if (!policy_is_inactive(policy)) { + if (!has_target()) + return 0; - /* - * Perform the ->exit() even during light-weight tear-down, - * since this is a core component, and is essential for the - * subsequent light-weight ->init() to succeed. - */ - if (cpufreq_driver->exit) - cpufreq_driver->exit(policy); - - if (!cpufreq_suspended) - cpufreq_policy_free(policy); - } else if (has_target()) { ret = __cpufreq_governor(policy, CPUFREQ_GOV_START); if (!ret) ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); @@ -1504,8 +1502,34 @@ static int __cpufreq_remove_dev_finish(struct device *dev, pr_err("%s: Failed to start governor\n", __func__); return ret; } + + return 0; } + /* If cpu is last user of policy, free policy */ + if (has_target()) { + ret = __cpufreq_governor(policy, CPUFREQ_GOV_POLICY_EXIT); + if (ret) { + pr_err("%s: Failed to exit governor\n", __func__); + return ret; + } + } + + /* Free the policy kobjects only if the driver is getting removed. */ + if (sif) + cpufreq_policy_put_kobj(policy); + + /* + * Perform the ->exit() even during light-weight tear-down, + * since this is a core component, and is essential for the + * subsequent light-weight ->init() to succeed. + */ + if (cpufreq_driver->exit) + cpufreq_driver->exit(policy); + + if (sif) + cpufreq_policy_free(policy); + return 0; } @@ -1519,8 +1543,34 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) unsigned int cpu = dev->id; int ret; - if (cpu_is_offline(cpu)) + /* + * Only possible if 'cpu' is getting physically removed now. A hotplug + * notifier should have already been called and we just need to remove + * link or free policy here. + */ + if (cpu_is_offline(cpu)) { + struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); + struct cpumask mask; + + if (!policy) + return 0; + + cpumask_copy(&mask, policy->related_cpus); + cpumask_clear_cpu(cpu, &mask); + + /* + * Free policy only if all policy->related_cpus are removed + * physically. + */ + if (cpumask_intersects(&mask, cpu_present_mask)) { + remove_cpu_dev_symlink(policy, cpu); + return 0; + } + + cpufreq_policy_put_kobj(policy); + cpufreq_policy_free(policy); return 0; + } ret = __cpufreq_remove_dev_prepare(dev, sif); -- cgit v0.10.2 From 2fc3384dc75bf7333384c7a16d12c796f61c3f56 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 8 Jun 2015 18:25:29 +0530 Subject: cpufreq: Initialize policy->kobj while allocating policy policy->kobj is required to be initialized once in the lifetime of a policy. Currently we are initializing it from __cpufreq_add_dev() and that doesn't look to be the best place for doing so as we have to do this on special cases (like: !recover_policy). We can initialize it from a more obvious place cpufreq_policy_alloc() and that will make code look cleaner, specially the error handling part. The error handling part of __cpufreq_add_dev() was doing almost the same thing while recover_policy is true or false. Fix that as well by always calling cpufreq_policy_put_kobj() with an additional parameter to skip notification part of it. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 4bfe0a53..dbb1bd6 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1134,9 +1134,10 @@ static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu) return policy; } -static struct cpufreq_policy *cpufreq_policy_alloc(int cpu) +static struct cpufreq_policy *cpufreq_policy_alloc(struct device *dev) { struct cpufreq_policy *policy; + int ret; policy = kzalloc(sizeof(*policy), GFP_KERNEL); if (!policy) @@ -1148,6 +1149,13 @@ static struct cpufreq_policy *cpufreq_policy_alloc(int cpu) if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL)) goto err_free_cpumask; + ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, &dev->kobj, + "cpufreq"); + if (ret) { + pr_err("%s: failed to init policy->kobj: %d\n", __func__, ret); + goto err_free_rcpumask; + } + INIT_LIST_HEAD(&policy->policy_list); init_rwsem(&policy->rwsem); spin_lock_init(&policy->transition_lock); @@ -1155,13 +1163,15 @@ static struct cpufreq_policy *cpufreq_policy_alloc(int cpu) init_completion(&policy->kobj_unregister); INIT_WORK(&policy->update, handle_update); - policy->cpu = cpu; + policy->cpu = dev->id; /* Set this once on allocation */ - policy->kobj_cpu = cpu; + policy->kobj_cpu = dev->id; return policy; +err_free_rcpumask: + free_cpumask_var(policy->related_cpus); err_free_cpumask: free_cpumask_var(policy->cpus); err_free_policy: @@ -1170,13 +1180,14 @@ err_free_policy: return NULL; } -static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy) +static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify) { struct kobject *kobj; struct completion *cmp; - blocking_notifier_call_chain(&cpufreq_policy_notifier_list, - CPUFREQ_REMOVE_POLICY, policy); + if (notify) + blocking_notifier_call_chain(&cpufreq_policy_notifier_list, + CPUFREQ_REMOVE_POLICY, policy); down_write(&policy->rwsem); cpufreq_remove_dev_symlink(policy); @@ -1270,7 +1281,7 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) policy = recover_policy ? cpufreq_policy_restore(cpu) : NULL; if (!policy) { recover_policy = false; - policy = cpufreq_policy_alloc(cpu); + policy = cpufreq_policy_alloc(dev); if (!policy) goto nomem_out; } @@ -1310,15 +1321,6 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) policy->user_policy.min = policy->min; policy->user_policy.max = policy->max; - /* prepare interface data */ - ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq, - &dev->kobj, "cpufreq"); - if (ret) { - pr_err("%s: failed to init policy->kobj: %d\n", - __func__, ret); - goto err_init_policy_kobj; - } - write_lock_irqsave(&cpufreq_driver_lock, flags); for_each_cpu(j, policy->related_cpus) per_cpu(cpufreq_cpu_data, j) = policy; @@ -1410,18 +1412,12 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) err_out_unregister: err_get_freq: - if (!recover_policy) { - kobject_put(&policy->kobj); - wait_for_completion(&policy->kobj_unregister); - } -err_init_policy_kobj: up_write(&policy->rwsem); if (cpufreq_driver->exit) cpufreq_driver->exit(policy); err_set_policy_cpu: - if (recover_policy) - cpufreq_policy_put_kobj(policy); + cpufreq_policy_put_kobj(policy, recover_policy); cpufreq_policy_free(policy); nomem_out: @@ -1517,7 +1513,7 @@ static int __cpufreq_remove_dev_finish(struct device *dev, /* Free the policy kobjects only if the driver is getting removed. */ if (sif) - cpufreq_policy_put_kobj(policy); + cpufreq_policy_put_kobj(policy, true); /* * Perform the ->exit() even during light-weight tear-down, @@ -1567,7 +1563,7 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) return 0; } - cpufreq_policy_put_kobj(policy); + cpufreq_policy_put_kobj(policy, true); cpufreq_policy_free(policy); return 0; } -- cgit v0.10.2 From 3654c5cc810e9b1f7eadf19780d1bc5f37e1ae6e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 8 Jun 2015 18:25:30 +0530 Subject: cpufreq: Call cpufreq_policy_put_kobj() from cpufreq_policy_free() cpufreq_policy_put_kobj() is actually part of freeing the policy and can be called from cpufreq_policy_free() directly instead of a separate call. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index dbb1bd6..8b81007 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1206,7 +1206,7 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy, bool notify) pr_debug("wait complete\n"); } -static void cpufreq_policy_free(struct cpufreq_policy *policy) +static void cpufreq_policy_free(struct cpufreq_policy *policy, bool notify) { unsigned long flags; int cpu; @@ -1219,6 +1219,7 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy) per_cpu(cpufreq_cpu_data, cpu) = NULL; write_unlock_irqrestore(&cpufreq_driver_lock, flags); + cpufreq_policy_put_kobj(policy, notify); free_cpumask_var(policy->related_cpus); free_cpumask_var(policy->cpus); kfree(policy); @@ -1417,9 +1418,7 @@ err_get_freq: if (cpufreq_driver->exit) cpufreq_driver->exit(policy); err_set_policy_cpu: - cpufreq_policy_put_kobj(policy, recover_policy); - cpufreq_policy_free(policy); - + cpufreq_policy_free(policy, recover_policy); nomem_out: up_read(&cpufreq_rwsem); @@ -1511,10 +1510,6 @@ static int __cpufreq_remove_dev_finish(struct device *dev, } } - /* Free the policy kobjects only if the driver is getting removed. */ - if (sif) - cpufreq_policy_put_kobj(policy, true); - /* * Perform the ->exit() even during light-weight tear-down, * since this is a core component, and is essential for the @@ -1523,8 +1518,9 @@ static int __cpufreq_remove_dev_finish(struct device *dev, if (cpufreq_driver->exit) cpufreq_driver->exit(policy); + /* Free the policy only if the driver is getting removed. */ if (sif) - cpufreq_policy_free(policy); + cpufreq_policy_free(policy, true); return 0; } @@ -1563,8 +1559,7 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) return 0; } - cpufreq_policy_put_kobj(policy, true); - cpufreq_policy_free(policy); + cpufreq_policy_free(policy, true); return 0; } -- cgit v0.10.2 From 9591becbf226e3aa0f6c73494736e2c5ab14cc8d Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 10 Jun 2015 02:20:23 +0200 Subject: cpufreq: Restart governor as soon as possible __cpufreq_remove_dev_finish() is doing two things today: - Restarts the governor if some CPUs from concerned policy are still online. - Frees the policy if all CPUs are offline. The first task of restarting the governor can be moved to __cpufreq_remove_dev_prepare() to restart the governor early. There is no race between _prepare() and _finish() as they would be handling completely different cases. _finish() will only be required if we are going to free the policy and that has nothing to do with restarting the governor. Original-by: Saravana Kannan Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 8b81007..c355ab6 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1428,8 +1428,8 @@ nomem_out: static int __cpufreq_remove_dev_prepare(struct device *dev, struct subsys_interface *sif) { - unsigned int cpu = dev->id, cpus; - int ret; + unsigned int cpu = dev->id; + int ret = 0; struct cpufreq_policy *policy; pr_debug("%s: unregistering CPU %u\n", __func__, cpu); @@ -1449,23 +1449,33 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, } down_write(&policy->rwsem); - cpus = cpumask_weight(policy->cpus); + cpumask_clear_cpu(cpu, policy->cpus); - if (has_target() && cpus == 1) - strncpy(policy->last_governor, policy->governor->name, - CPUFREQ_NAME_LEN); + if (policy_is_inactive(policy)) { + if (has_target()) + strncpy(policy->last_governor, policy->governor->name, + CPUFREQ_NAME_LEN); + } else if (cpu == policy->cpu) { + /* Nominate new CPU */ + policy->cpu = cpumask_any(policy->cpus); + } up_write(&policy->rwsem); - if (cpu != policy->cpu) - return 0; + /* Start governor again for active policy */ + if (!policy_is_inactive(policy)) { + if (has_target()) { + ret = __cpufreq_governor(policy, CPUFREQ_GOV_START); + if (!ret) + ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); - if (cpus > 1) - /* Nominate new CPU */ - update_policy_cpu(policy, cpumask_any_but(policy->cpus, cpu)); - else if (cpufreq_driver->stop_cpu) + if (ret) + pr_err("%s: Failed to start governor\n", __func__); + } + } else if (cpufreq_driver->stop_cpu) { cpufreq_driver->stop_cpu(policy); + } - return 0; + return ret; } static int __cpufreq_remove_dev_finish(struct device *dev, @@ -1473,33 +1483,16 @@ static int __cpufreq_remove_dev_finish(struct device *dev, { unsigned int cpu = dev->id; int ret; - struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); + struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); if (!policy) { pr_debug("%s: No cpu_data found\n", __func__); return -EINVAL; } - down_write(&policy->rwsem); - cpumask_clear_cpu(cpu, policy->cpus); - up_write(&policy->rwsem); - - /* Not the last cpu of policy, start governor again ? */ - if (!policy_is_inactive(policy)) { - if (!has_target()) - return 0; - - ret = __cpufreq_governor(policy, CPUFREQ_GOV_START); - if (!ret) - ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); - - if (ret) { - pr_err("%s: Failed to start governor\n", __func__); - return ret; - } - + /* Only proceed for inactive policies */ + if (!policy_is_inactive(policy)) return 0; - } /* If cpu is last user of policy, free policy */ if (has_target()) { -- cgit v0.10.2 From 37829029837b2f653fd407cbd6796dcf124c1ed8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 8 Jun 2015 18:25:32 +0530 Subject: cpufreq: Remove cpufreq_update_policy() cpufreq_update_policy() was kept as a separate routine earlier as it was handling migration of sysfs directories, which isn't the case anymore. It is only updating policy->cpu now and is called by a single caller. The WARN_ON() isn't really required anymore, as we are just updating the cpu now, not moving the sysfs directories. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index c355ab6..b612411 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1129,6 +1129,10 @@ static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu) if (likely(policy)) { /* Policy should be inactive here */ WARN_ON(!policy_is_inactive(policy)); + + down_write(&policy->rwsem); + policy->cpu = cpu; + up_write(&policy->rwsem); } return policy; @@ -1225,16 +1229,6 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy, bool notify) kfree(policy); } -static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu) -{ - if (WARN_ON(cpu == policy->cpu)) - return; - - down_write(&policy->rwsem); - policy->cpu = cpu; - up_write(&policy->rwsem); -} - /** * cpufreq_add_dev - add a CPU device * @@ -1287,15 +1281,6 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) goto nomem_out; } - /* - * In the resume path, since we restore a saved policy, the assignment - * to policy->cpu is like an update of the existing policy, rather than - * the creation of a brand new one. So we need to perform this update - * by invoking update_policy_cpu(). - */ - if (recover_policy && cpu != policy->cpu) - update_policy_cpu(policy, cpu); - cpumask_copy(policy->cpus, cpumask_of(cpu)); /* call driver. From then on the cpufreq must be able -- cgit v0.10.2 From 7f5c1882add891f7420a327e908f206fafed3670 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 28 May 2015 18:29:48 +0200 Subject: ACPI / video: Add enable_native_backlight quirk for MacbookPro12,1 It seems that the latest generation of MacbookPro needs to use the native backlight driver, just like most modern laptops do, but it does not automatically get enabled as the Apple BIOS does not advertise Windows 8 compatibility. So add a quirk for this. Reported-by: Christopher Beland Signed-off-by: Hans de Goede Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index cc79d3f..518f0e1 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -583,6 +583,15 @@ static struct dmi_system_id video_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "102434U"), }, }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */ + .callback = video_enable_native_backlight, + .ident = "Apple MacBook Pro 12,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"), + }, + }, {} }; -- cgit v0.10.2 From 654a182d8562d55a69fdfbd2709e11f6ee1a0aac Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 9 Jun 2015 10:32:25 +0200 Subject: ACPI / video: Add a parameter to not register the backlight sysfs interface On some systems acpi-video backlight is broken in the sense that it cannot control the brightness of the backlight, but it must still be called on resume to power-up the backlight after resume. This commit allows these systems to work by going through all the usual backlight control moves, while not registering a sysfs backlight interface. This commit also adds a quirk enabling this parameter on Toshiba Portege R830 systems which are known to be affected by this. I wish there was a better way to deal with this, but we've been unable to find one. Link: https://bugzilla.kernel.org/show_bug.cgi?id=21012 Link: https://bugs.freedesktop.org/show_bug.cgi?id=82634 Reported-and-tested-by: Sylvain Pasche Signed-off-by: Hans de Goede Acked-by: Aaron Lu Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 518f0e1..3bc4c68 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -92,6 +92,9 @@ static int use_native_backlight_param = NATIVE_BACKLIGHT_NOT_SET; module_param_named(use_native_backlight, use_native_backlight_param, int, 0444); static int use_native_backlight_dmi = NATIVE_BACKLIGHT_NOT_SET; +static int disable_backlight_sysfs_if = -1; +module_param(disable_backlight_sysfs_if, int, 0444); + static int register_count; static struct mutex video_list_lock; static struct list_head video_bus_head; @@ -431,6 +434,14 @@ static int __init video_enable_native_backlight(const struct dmi_system_id *d) return 0; } +static int __init video_disable_backlight_sysfs_if( + const struct dmi_system_id *d) +{ + if (disable_backlight_sysfs_if == -1) + disable_backlight_sysfs_if = 1; + return 0; +} + static struct dmi_system_id video_dmi_table[] __initdata = { /* * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 @@ -592,6 +603,23 @@ static struct dmi_system_id video_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"), }, }, + + /* + * Some machines have a broken acpi-video interface for brightness + * control, but still need an acpi_video_device_lcd_set_level() call + * on resume to turn the backlight power on. We Enable backlight + * control on these systems, but do not register a backlight sysfs + * as brightness control does not work. + */ + { + /* https://bugs.freedesktop.org/show_bug.cgi?id=82634 */ + .callback = video_disable_backlight_sysfs_if, + .ident = "Toshiba Portege R830", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R830"), + }, + }, {} }; @@ -1391,7 +1419,7 @@ acpi_video_switch_brightness(struct work_struct *work) int result = -EINVAL; /* no warning message if acpi_backlight=vendor or a quirk is used */ - if (!acpi_video_verify_backlight_support()) + if (!device->backlight) return; if (!device->brightness) @@ -1666,8 +1694,9 @@ static int acpi_video_resume(struct notifier_block *nb, for (i = 0; i < video->attached_count; i++) { video_device = video->attached_array[i].bind_info; - if (video_device && video_device->backlight) - acpi_video_set_brightness(video_device->backlight); + if (video_device && video_device->brightness) + acpi_video_device_lcd_set_level(video_device, + video_device->brightness->curr); } return NOTIFY_OK; @@ -1716,6 +1745,10 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) result = acpi_video_init_brightness(device); if (result) return; + + if (disable_backlight_sysfs_if > 0) + return; + name = kasprintf(GFP_KERNEL, "acpi_video%d", count); if (!name) return; @@ -1738,8 +1771,10 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) &acpi_backlight_ops, &props); kfree(name); - if (IS_ERR(device->backlight)) + if (IS_ERR(device->backlight)) { + device->backlight = NULL; return; + } /* * Save current brightness level in case we have to restore it -- cgit v0.10.2 From 4a4f01a6af433894ad49110ba7857a76c4c9ca8b Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:26:59 +0200 Subject: ACPI / video: constify ACPI and DMI id tables Make the video ACPI device ID array static and constify the DMI system IDs array. Saves us a little bit of code. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index c42feb2..be82580 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -109,7 +109,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv) struct pci_dev *dev; struct acpi_device *acpi_dev; - const struct acpi_device_id video_ids[] = { + static const struct acpi_device_id video_ids[] = { {ACPI_VIDEO_HID, 0}, {"", 0}, }; @@ -134,7 +134,7 @@ static int video_detect_force_vendor(const struct dmi_system_id *d) return 0; } -static struct dmi_system_id video_detect_dmi_table[] = { +static const struct dmi_system_id video_detect_dmi_table[] = { /* On Samsung X360, the BIOS will set a flag (VDRV) if generic * ACPI backlight device is used. This flag will definitively break * the backlight interface (even the vendor interface) untill next -- cgit v0.10.2 From 44f610cdfb8d1d134dc7756ad50287acfaee7f2e Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:26:49 +0200 Subject: ACPI / AC: constify DMI system id table There is no need to have ac_dmi_table[] writeable, constify it. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index bbcc2b5..9b5354a 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -308,7 +308,7 @@ static int thinkpad_e530_quirk(const struct dmi_system_id *d) return 0; } -static struct dmi_system_id ac_dmi_table[] = { +static const struct dmi_system_id ac_dmi_table[] = { { .callback = thinkpad_e530_quirk, .ident = "thinkpad e530", -- cgit v0.10.2 From a465878455a06a8d52599be25459ba23cec3fd09 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:26:53 +0200 Subject: ACPI / battery: constify the offset tables The offset tables are only read, not modified. Make them const. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 547e627..7a175e9 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -355,14 +355,14 @@ struct acpi_offsets { u8 mode; /* int or string? */ }; -static struct acpi_offsets state_offsets[] = { +static const struct acpi_offsets state_offsets[] = { {offsetof(struct acpi_battery, state), 0}, {offsetof(struct acpi_battery, rate_now), 0}, {offsetof(struct acpi_battery, capacity_now), 0}, {offsetof(struct acpi_battery, voltage_now), 0}, }; -static struct acpi_offsets info_offsets[] = { +static const struct acpi_offsets info_offsets[] = { {offsetof(struct acpi_battery, power_unit), 0}, {offsetof(struct acpi_battery, design_capacity), 0}, {offsetof(struct acpi_battery, full_charge_capacity), 0}, @@ -378,7 +378,7 @@ static struct acpi_offsets info_offsets[] = { {offsetof(struct acpi_battery, oem_info), 1}, }; -static struct acpi_offsets extended_info_offsets[] = { +static const struct acpi_offsets extended_info_offsets[] = { {offsetof(struct acpi_battery, revision), 0}, {offsetof(struct acpi_battery, power_unit), 0}, {offsetof(struct acpi_battery, design_capacity), 0}, @@ -403,7 +403,7 @@ static struct acpi_offsets extended_info_offsets[] = { static int extract_package(struct acpi_battery *battery, union acpi_object *package, - struct acpi_offsets *offsets, int num) + const struct acpi_offsets *offsets, int num) { int i; union acpi_object *element; -- cgit v0.10.2 From 27059b9132880f4211c41dfbf7665671cbf1ac91 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:26:54 +0200 Subject: ACPI / battery: minor tweaks to acpi_battery_units() Make the acpi_battery_units() function take a const argument and return a const char*, too. Also make it static. It probably doesn't matter, as gcc will be clever enough to optimize and inline the code even without these hints. However, we also get rid of a #ifdef block by moving the function closer to its usage location, so it's at least a small gain in code readability. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 7a175e9..c918c01 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -339,14 +339,6 @@ static enum power_supply_property energy_battery_props[] = { POWER_SUPPLY_PROP_SERIAL_NUMBER, }; -#ifdef CONFIG_ACPI_PROCFS_POWER -inline char *acpi_battery_units(struct acpi_battery *battery) -{ - return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ? - "mA" : "mW"; -} -#endif - /* -------------------------------------------------------------------------- Battery Management -------------------------------------------------------------------------- */ @@ -793,6 +785,12 @@ static void acpi_battery_refresh(struct acpi_battery *battery) #ifdef CONFIG_ACPI_PROCFS_POWER static struct proc_dir_entry *acpi_battery_dir; +static const char *acpi_battery_units(const struct acpi_battery *battery) +{ + return (battery->power_unit == ACPI_BATTERY_POWER_UNIT_MA) ? + "mA" : "mW"; +} + static int acpi_battery_print_info(struct seq_file *seq, int result) { struct acpi_battery *battery = seq->private; -- cgit v0.10.2 From 048d16da75cc3ae1a75455c0e7b2bf107c4f0f84 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:26:55 +0200 Subject: ACPI / battery: mark DMI table as __initconst The bat_dmi_table[] DMI table is referenced from the __init function acpi_battery_init_async() only. It and its referenced functions can therefore be marked __initconst to free up ~1kB of runtime memory after initialization is done. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index c918c01..b3628cc 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1124,19 +1124,21 @@ static int battery_notify(struct notifier_block *nb, return 0; } -static int battery_bix_broken_package_quirk(const struct dmi_system_id *d) +static int __init +battery_bix_broken_package_quirk(const struct dmi_system_id *d) { battery_bix_broken_package = 1; return 0; } -static int battery_notification_delay_quirk(const struct dmi_system_id *d) +static int __init +battery_notification_delay_quirk(const struct dmi_system_id *d) { battery_notification_delay_ms = 1000; return 0; } -static struct dmi_system_id bat_dmi_table[] = { +static const struct dmi_system_id bat_dmi_table[] __initconst = { { .callback = battery_bix_broken_package_quirk, .ident = "NEC LZ750/LS", -- cgit v0.10.2 From b2687cd7d5fd7394254ebc2b6a553ed9a3dcd5f1 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:26:50 +0200 Subject: ACPI / LPSS: constify device descriptors The device descriptors are never written to -- even pointed to as 'const' from struct lpss_private_data. Make them r/o for real. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 37fb190..569ee09 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -129,50 +129,50 @@ static void byt_i2c_setup(struct lpss_private_data *pdata) writel(0, pdata->mmio_base + LPSS_I2C_ENABLE); } -static struct lpss_device_desc lpt_dev_desc = { +static const struct lpss_device_desc lpt_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR, .prv_offset = 0x800, }; -static struct lpss_device_desc lpt_i2c_dev_desc = { +static const struct lpss_device_desc lpt_i2c_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR, .prv_offset = 0x800, }; -static struct lpss_device_desc lpt_uart_dev_desc = { +static const struct lpss_device_desc lpt_uart_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_LTR, .clk_con_id = "baudclk", .prv_offset = 0x800, .setup = lpss_uart_setup, }; -static struct lpss_device_desc lpt_sdio_dev_desc = { +static const struct lpss_device_desc lpt_sdio_dev_desc = { .flags = LPSS_LTR, .prv_offset = 0x1000, .prv_size_override = 0x1018, }; -static struct lpss_device_desc byt_pwm_dev_desc = { +static const struct lpss_device_desc byt_pwm_dev_desc = { .flags = LPSS_SAVE_CTX, }; -static struct lpss_device_desc byt_uart_dev_desc = { +static const struct lpss_device_desc byt_uart_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, .clk_con_id = "baudclk", .prv_offset = 0x800, .setup = lpss_uart_setup, }; -static struct lpss_device_desc byt_spi_dev_desc = { +static const struct lpss_device_desc byt_spi_dev_desc = { .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, .prv_offset = 0x400, }; -static struct lpss_device_desc byt_sdio_dev_desc = { +static const struct lpss_device_desc byt_sdio_dev_desc = { .flags = LPSS_CLK, }; -static struct lpss_device_desc byt_i2c_dev_desc = { +static const struct lpss_device_desc byt_i2c_dev_desc = { .flags = LPSS_CLK | LPSS_SAVE_CTX, .prv_offset = 0x800, .setup = byt_i2c_setup, @@ -323,14 +323,14 @@ out: static int acpi_lpss_create_device(struct acpi_device *adev, const struct acpi_device_id *id) { - struct lpss_device_desc *dev_desc; + const struct lpss_device_desc *dev_desc; struct lpss_private_data *pdata; struct resource_entry *rentry; struct list_head resource_list; struct platform_device *pdev; int ret; - dev_desc = (struct lpss_device_desc *)id->driver_data; + dev_desc = (const struct lpss_device_desc *)id->driver_data; if (!dev_desc) { pdev = acpi_create_platform_device(adev); return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; -- cgit v0.10.2 From 69cda6e0f035494188fd76749e35627e087b3ff8 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:26:56 +0200 Subject: ACPI / HED: constify ACPI device ids Constify the acpi_hed_ids[] ACPI device IDs array -- no need to have it writeable. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c index aafe3ca..a322710 100644 --- a/drivers/acpi/hed.c +++ b/drivers/acpi/hed.c @@ -27,7 +27,7 @@ #include #include -static struct acpi_device_id acpi_hed_ids[] = { +static const struct acpi_device_id acpi_hed_ids[] = { {"PNP0C33", 0}, {"", 0}, }; -- cgit v0.10.2 From b03466883edb192b3e3877ea3f71036a3899ae51 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:26:57 +0200 Subject: ACPI / processor: constify DMI system id table There is no need to have processor_power_dmi_table[] writeable, constify it. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 39e0c8e..d540f42 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -94,7 +94,7 @@ static int set_max_cstate(const struct dmi_system_id *id) return 0; } -static struct dmi_system_id processor_power_dmi_table[] = { +static const struct dmi_system_id processor_power_dmi_table[] = { { set_max_cstate, "Clevo 5600D", { DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"), DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")}, -- cgit v0.10.2 From 0519ade71852701ec76ee7f8a0b37fe5a5504f98 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:26:58 +0200 Subject: ACPI / scan: constify ACPI device ids Make the button ACPI device ID array static const. Safes us a little bit of code. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b19283b..0a09991 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1673,7 +1673,7 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle, static void acpi_wakeup_gpe_init(struct acpi_device *device) { - struct acpi_device_id button_device_ids[] = { + static const struct acpi_device_id button_device_ids[] = { {"PNP0C0C", 0}, {"PNP0C0D", 0}, {"PNP0C0E", 0}, -- cgit v0.10.2 From f8b8eb71533338654f39211a87efeca35055566b Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 11 Jun 2015 13:21:23 +0800 Subject: ACPI / EC: Cleanup transaction state transition. This patch collects transaction state transition code into one function. We then could have a single function to maintain transaction transition related behaviors. No functional changes. Signed-off-by: Lv Zheng Tested-by: Gabriele Mazzotta Tested-by: Tigran Gabrielyan Tested-by: Adrien D Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 149b5e7..0ce8b6e8 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -391,7 +391,7 @@ static void acpi_ec_submit_query(struct acpi_ec *ec) static void acpi_ec_complete_query(struct acpi_ec *ec) { - if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { + if (test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); ec_dbg_req("Event stopped"); } @@ -421,6 +421,15 @@ static int ec_transaction_completed(struct acpi_ec *ec) return ret; } +static inline void ec_transaction_transition(struct acpi_ec *ec, unsigned long flag) +{ + ec->curr->flags |= flag; + if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { + if (flag == ACPI_EC_COMMAND_POLL) + acpi_ec_complete_query(ec); + } +} + static void advance_transaction(struct acpi_ec *ec) { struct transaction *t; @@ -449,7 +458,7 @@ static void advance_transaction(struct acpi_ec *ec) if ((status & ACPI_EC_FLAG_OBF) == 1) { t->rdata[t->ri++] = acpi_ec_read_data(ec); if (t->rlen == t->ri) { - t->flags |= ACPI_EC_COMMAND_COMPLETE; + ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE); if (t->command == ACPI_EC_COMMAND_QUERY) ec_dbg_req("Command(%s) hardware completion", acpi_ec_cmd_string(t->command)); @@ -459,7 +468,7 @@ static void advance_transaction(struct acpi_ec *ec) goto err; } else if (t->wlen == t->wi && (status & ACPI_EC_FLAG_IBF) == 0) { - t->flags |= ACPI_EC_COMMAND_COMPLETE; + ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE); wakeup = true; } goto out; @@ -467,17 +476,15 @@ static void advance_transaction(struct acpi_ec *ec) if (EC_FLAGS_QUERY_HANDSHAKE && !(status & ACPI_EC_FLAG_SCI) && (t->command == ACPI_EC_COMMAND_QUERY)) { - t->flags |= ACPI_EC_COMMAND_POLL; - acpi_ec_complete_query(ec); + ec_transaction_transition(ec, ACPI_EC_COMMAND_POLL); t->rdata[t->ri++] = 0x00; - t->flags |= ACPI_EC_COMMAND_COMPLETE; + ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE); ec_dbg_req("Command(%s) software completion", acpi_ec_cmd_string(t->command)); wakeup = true; } else if ((status & ACPI_EC_FLAG_IBF) == 0) { acpi_ec_write_cmd(ec, t->command); - t->flags |= ACPI_EC_COMMAND_POLL; - acpi_ec_complete_query(ec); + ec_transaction_transition(ec, ACPI_EC_COMMAND_POLL); } else goto err; goto out; -- cgit v0.10.2 From 9d8993be2d9149bc8b3132dad030ff5960f5abcc Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 11 Jun 2015 13:21:32 +0800 Subject: ACPI / EC: Convert event handling work queue into loop style. During the period that a work queue is scheduled (queued up for run) but hasn't been run, second schedule_work() could fail. This may not lead to the loss of queries because QR_EC is always ensured to be submitted after the work queue has been in the running state. The event handling work queue can be changed into the loop style to allow us to control the code in a more flexible way: 1. Makes it possible to add event=0x00 termination condition in the loop. 2. Increases the thoughput of the QR_EC transactions as the 2nd+ QR_EC transactions may be handled in the same work item used for the 1st QR_EC transaction, thus the delay caused by the 2nd+ work item scheduling can be eliminated. Except the logging message changes and the throughput improvement, this patch is just a funcitonal no-op. Signed-off-by: Lv Zheng Tested-by: Gabriele Mazzotta Tested-by: Tigran Gabrielyan Tested-by: Adrien D Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 0ce8b6e8..824f3e8 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -384,7 +384,9 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec) static void acpi_ec_submit_query(struct acpi_ec *ec) { if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { - ec_dbg_req("Event started"); + ec_dbg_evt("Command(%s) submitted/blocked", + acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); + ec->nr_pending_queries++; schedule_work(&ec->work); } } @@ -393,7 +395,8 @@ static void acpi_ec_complete_query(struct acpi_ec *ec) { if (test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); - ec_dbg_req("Event stopped"); + ec_dbg_evt("Command(%s) unblocked", + acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); } } @@ -460,8 +463,8 @@ static void advance_transaction(struct acpi_ec *ec) if (t->rlen == t->ri) { ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE); if (t->command == ACPI_EC_COMMAND_QUERY) - ec_dbg_req("Command(%s) hardware completion", - acpi_ec_cmd_string(t->command)); + ec_dbg_evt("Command(%s) completed by hardware", + acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); wakeup = true; } } else @@ -479,8 +482,8 @@ static void advance_transaction(struct acpi_ec *ec) ec_transaction_transition(ec, ACPI_EC_COMMAND_POLL); t->rdata[t->ri++] = 0x00; ec_transaction_transition(ec, ACPI_EC_COMMAND_COMPLETE); - ec_dbg_req("Command(%s) software completion", - acpi_ec_cmd_string(t->command)); + ec_dbg_evt("Command(%s) completed by software", + acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); wakeup = true; } else if ((status & ACPI_EC_FLAG_IBF) == 0) { acpi_ec_write_cmd(ec, t->command); @@ -961,11 +964,23 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data) return result; } -static void acpi_ec_gpe_poller(struct work_struct *work) +static void acpi_ec_event_handler(struct work_struct *work) { + unsigned long flags; struct acpi_ec *ec = container_of(work, struct acpi_ec, work); - acpi_ec_query(ec, NULL); + ec_dbg_evt("Event started"); + + spin_lock_irqsave(&ec->lock, flags); + while (ec->nr_pending_queries) { + spin_unlock_irqrestore(&ec->lock, flags); + (void)acpi_ec_query(ec, NULL); + spin_lock_irqsave(&ec->lock, flags); + ec->nr_pending_queries--; + } + spin_unlock_irqrestore(&ec->lock, flags); + + ec_dbg_evt("Event stopped"); } static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, @@ -1040,7 +1055,7 @@ static struct acpi_ec *make_acpi_ec(void) init_waitqueue_head(&ec->wait); INIT_LIST_HEAD(&ec->list); spin_lock_init(&ec->lock); - INIT_WORK(&ec->work, acpi_ec_gpe_poller); + INIT_WORK(&ec->work, acpi_ec_event_handler); ec->timestamp = jiffies; return ec; } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 61cb506..b09756a 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -139,6 +139,7 @@ struct acpi_ec { spinlock_t lock; struct work_struct work; unsigned long timestamp; + unsigned long nr_pending_queries; }; extern struct acpi_ec *first_ec; -- cgit v0.10.2 From 1d68d2612c2e7309166fa43d8e27eb163435527f Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 11 Jun 2015 13:21:38 +0800 Subject: ACPI / EC: Add event clearing variation support. We've been suffering from the uncertainty of the SCI_EVT clearing timing. This patch implements 3 of 4 possible modes to handle SCI_EVT clearing variations. The old behavior is kept in this patch. Status: QR_EC is re-checked as early as possible after checking previous SCI_EVT. This always leads to 2 QR_EC transactions per SCI_EVT indication and the target may implement event queue which returns 0x00 indicating "no outstanding event". This is proven to be a conflict against Windows behavior, but is still kept in this patch to make the EC driver robust to the possible regressions that may occur on Samsung platforms. Query: QR_EC is re-checked after the target has handled the QR_EC query request command pushed by the host. Event: QR_EC is re-checked after the target has noticed the query event response data pulled by the host. This timing is not determined by any IRQs, so we may need to use a guard period in this mode, which may explain the existence of the ec_guard() code used by the old EC driver where the re-check timing is implemented in the similar way as this mode. Method: QR_EC is re-checked as late as possible after completing the _Qxx evaluation. The target may implement SCI_EVT like a level triggered interrupt. It is proven on kernel bugzilla 94411 that, Windows will have all _Qxx evaluations parallelized. Thus unless required by further evidences, we needn't implement this mode as it is a conflict of the _Qxx parallelism requirement. Note that, according to the reports, there are platforms that cannot be handled using the "Status" mode without enabling the EC_FLAGS_QUERY_HANDSHAKE quirk. But they can be handled with the other modes according to the tests (kernel bugzilla 97381). The following log entry can be used to confirm the differences of the 3 modes as it should appear at the different positions for the 3 modes: Command(QR_EC) unblocked Status: appearing after EC_SC(W) = 0x84 Query: appearing after EC_DATA(R) = 0xXX where XX is the event number used to determine _QXX Event: appearing after first EC_SC(R) = 0xX0 SCI_EVT=x BURST=0 CMD=0 IBF=0 OBF=0 that is next to the following log entry: Command(QR_EC) completed by hardware Link: https://bugzilla.kernel.org/show_bug.cgi?id=94411 Link: https://bugzilla.kernel.org/show_bug.cgi?id=97381 Link: https://bugzilla.kernel.org/show_bug.cgi?id=98111 Reported-and-tested-by: Gabriele Mazzotta Reported-and-tested-by: Tigran Gabrielyan Reported-and-tested-by: Adrien D Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 824f3e8..41eb523 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -59,6 +59,38 @@ #define ACPI_EC_FLAG_BURST 0x10 /* burst mode */ #define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ +/* + * The SCI_EVT clearing timing is not defined by the ACPI specification. + * This leads to lots of practical timing issues for the host EC driver. + * The following variations are defined (from the target EC firmware's + * perspective): + * STATUS: After indicating SCI_EVT edge triggered IRQ to the host, the + * target can clear SCI_EVT at any time so long as the host can see + * the indication by reading the status register (EC_SC). So the + * host should re-check SCI_EVT after the first time the SCI_EVT + * indication is seen, which is the same time the query request + * (QR_EC) is written to the command register (EC_CMD). SCI_EVT set + * at any later time could indicate another event. Normally such + * kind of EC firmware has implemented an event queue and will + * return 0x00 to indicate "no outstanding event". + * QUERY: After seeing the query request (QR_EC) written to the command + * register (EC_CMD) by the host and having prepared the responding + * event value in the data register (EC_DATA), the target can safely + * clear SCI_EVT because the target can confirm that the current + * event is being handled by the host. The host then should check + * SCI_EVT right after reading the event response from the data + * register (EC_DATA). + * EVENT: After seeing the event response read from the data register + * (EC_DATA) by the host, the target can clear SCI_EVT. As the + * target requires time to notice the change in the data register + * (EC_DATA), the host may be required to wait additional guarding + * time before checking the SCI_EVT again. Such guarding may not be + * necessary if the host is notified via another IRQ. + */ +#define ACPI_EC_EVT_TIMING_STATUS 0x00 +#define ACPI_EC_EVT_TIMING_QUERY 0x01 +#define ACPI_EC_EVT_TIMING_EVENT 0x02 + /* EC commands */ enum ec_command { ACPI_EC_COMMAND_READ = 0x80, @@ -76,6 +108,7 @@ enum ec_command { enum { EC_FLAGS_QUERY_PENDING, /* Query is pending */ + EC_FLAGS_QUERY_GUARDING, /* Guard for SCI_EVT check */ EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and * OpReg are installed */ EC_FLAGS_STARTED, /* Driver is started */ @@ -100,6 +133,8 @@ static unsigned int ec_polling_guard __read_mostly = ACPI_EC_UDELAY_POLL; module_param(ec_polling_guard, uint, 0644); MODULE_PARM_DESC(ec_polling_guard, "Guard time(us) between EC accesses in polling modes"); +static unsigned int ec_event_clearing __read_mostly = ACPI_EC_EVT_TIMING_STATUS; + /* * If the number of false interrupts per one transaction exceeds * this threshold, will think there is a GPE storm happened and @@ -400,6 +435,21 @@ static void acpi_ec_complete_query(struct acpi_ec *ec) } } +static bool acpi_ec_guard_event(struct acpi_ec *ec) +{ + if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS || + ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY || + !test_bit(EC_FLAGS_QUERY_PENDING, &ec->flags) || + (ec->curr && ec->curr->command == ACPI_EC_COMMAND_QUERY)) + return false; + + /* + * Postpone the query submission to allow the firmware to proceed, + * we shouldn't check SCI_EVT before the firmware reflagging it. + */ + return true; +} + static int ec_transaction_polled(struct acpi_ec *ec) { unsigned long flags; @@ -428,8 +478,15 @@ static inline void ec_transaction_transition(struct acpi_ec *ec, unsigned long f { ec->curr->flags |= flag; if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { - if (flag == ACPI_EC_COMMAND_POLL) + if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS && + flag == ACPI_EC_COMMAND_POLL) + acpi_ec_complete_query(ec); + if (ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY && + flag == ACPI_EC_COMMAND_COMPLETE) acpi_ec_complete_query(ec); + if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && + flag == ACPI_EC_COMMAND_COMPLETE) + set_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags); } } @@ -449,6 +506,17 @@ static void advance_transaction(struct acpi_ec *ec) acpi_ec_clear_gpe(ec); status = acpi_ec_read_status(ec); t = ec->curr; + /* + * Another IRQ or a guarded polling mode advancement is detected, + * the next QR_EC submission is then allowed. + */ + if (!t || !(t->flags & ACPI_EC_COMMAND_POLL)) { + if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && + test_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags)) { + clear_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags); + acpi_ec_complete_query(ec); + } + } if (!t) goto err; if (t->flags & ACPI_EC_COMMAND_POLL) { @@ -534,11 +602,13 @@ static int ec_guard(struct acpi_ec *ec) /* * Perform wait polling * - * The following check is there to keep the old - * logic - no inter-transaction guarding for the - * wait polling mode. + * For SCI_EVT clearing timing of "event", + * performing guarding before re-checking the + * SCI_EVT. Otherwise, such guarding is not needed + * due to the old practices. */ - if (!ec_transaction_polled(ec)) + if (!ec_transaction_polled(ec) && + !acpi_ec_guard_event(ec)) break; if (wait_event_timeout(ec->wait, ec_transaction_completed(ec), @@ -964,6 +1034,24 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 *data) return result; } +static void acpi_ec_check_event(struct acpi_ec *ec) +{ + unsigned long flags; + + if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) { + if (ec_guard(ec)) { + spin_lock_irqsave(&ec->lock, flags); + /* + * Take care of the SCI_EVT unless no one else is + * taking care of it. + */ + if (!ec->curr) + advance_transaction(ec); + spin_unlock_irqrestore(&ec->lock, flags); + } + } +} + static void acpi_ec_event_handler(struct work_struct *work) { unsigned long flags; @@ -981,6 +1069,8 @@ static void acpi_ec_event_handler(struct work_struct *work) spin_unlock_irqrestore(&ec->lock, flags); ec_dbg_evt("Event stopped"); + + acpi_ec_check_event(ec); } static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, @@ -1437,6 +1527,43 @@ error: return -ENODEV; } +static int param_set_event_clearing(const char *val, struct kernel_param *kp) +{ + int result = 0; + + if (!strncmp(val, "status", sizeof("status") - 1)) { + ec_event_clearing = ACPI_EC_EVT_TIMING_STATUS; + pr_info("Assuming SCI_EVT clearing on EC_SC accesses\n"); + } else if (!strncmp(val, "query", sizeof("query") - 1)) { + ec_event_clearing = ACPI_EC_EVT_TIMING_QUERY; + pr_info("Assuming SCI_EVT clearing on QR_EC writes\n"); + } else if (!strncmp(val, "event", sizeof("event") - 1)) { + ec_event_clearing = ACPI_EC_EVT_TIMING_EVENT; + pr_info("Assuming SCI_EVT clearing on event reads\n"); + } else + result = -EINVAL; + return result; +} + +static int param_get_event_clearing(char *buffer, struct kernel_param *kp) +{ + switch (ec_event_clearing) { + case ACPI_EC_EVT_TIMING_STATUS: + return sprintf(buffer, "status"); + case ACPI_EC_EVT_TIMING_QUERY: + return sprintf(buffer, "query"); + case ACPI_EC_EVT_TIMING_EVENT: + return sprintf(buffer, "event"); + default: + return sprintf(buffer, "invalid"); + } + return 0; +} + +module_param_call(ec_event_clearing, param_set_event_clearing, param_get_event_clearing, + NULL, 0644); +MODULE_PARM_DESC(ec_event_clearing, "Assumed SCI_EVT clearing timing"); + static struct acpi_driver acpi_ec_driver = { .name = "ec", .class = ACPI_EC_CLASS, -- cgit v0.10.2 From 3cb02aeb28dd3f1b8a132fa3ecf6db17afd518d6 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 11 Jun 2015 13:21:45 +0800 Subject: ACPI / EC: Fix EC_FLAGS_QUERY_HANDSHAKE platforms using new event clearing timing. It is reported that on several platforms, EC firmware will not respond non-expected QR_EC (see EC_FLAGS_QUERY_HANDSHAKE, only write QR_EC when SCI_EVT is set). Unfortunately, ACPI specification doesn't define when the SCI_EVT should be cleared by the firmware, thus the original implementation queued up second QR_EC right after writing QR_EC command and before reading the returned event value as at that time the SCI_EVT is ensured not cleared. This behavior is also based on the assumption that the firmware should be able to return 0x00 to indicate "no outstanding event". This behavior did fix issues on Samsung platforms where the spurious query value of 0x00 is supported and didn't break platforms in my test queue. But recently, specific Acer, Asus, Lenovo platforms keep on blaming this change. This patch changes the behavior to re-check the SCI_EVT a bit later and removes EC_FLAGS_QUERY_HANDSHAKE quirks, hoping this is the Windows compliant EC driver behavior. In order to be robust to the possible regressions, instead of removing the quirk directly, this patch keeps the quirk code, removes the quirk users and keeps old behavior for Samsung platforms. Cc: 3.16+ # 3.16+ Link: https://bugzilla.kernel.org/show_bug.cgi?id=94411 Link: https://bugzilla.kernel.org/show_bug.cgi?id=97381 Link: https://bugzilla.kernel.org/show_bug.cgi?id=98111 Reported-and-tested-by: Gabriele Mazzotta Reported-and-tested-by: Tigran Gabrielyan Reported-and-tested-by: Adrien D Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 41eb523..79817ce 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -133,7 +133,7 @@ static unsigned int ec_polling_guard __read_mostly = ACPI_EC_UDELAY_POLL; module_param(ec_polling_guard, uint, 0644); MODULE_PARM_DESC(ec_polling_guard, "Guard time(us) between EC accesses in polling modes"); -static unsigned int ec_event_clearing __read_mostly = ACPI_EC_EVT_TIMING_STATUS; +static unsigned int ec_event_clearing __read_mostly = ACPI_EC_EVT_TIMING_QUERY; /* * If the number of false interrupts per one transaction exceeds @@ -1381,10 +1381,13 @@ static int ec_validate_ecdt(const struct dmi_system_id *id) return 0; } +#if 0 /* - * Acer EC firmware refuses to respond QR_EC when SCI_EVT is not set, for - * which case, we complete the QR_EC without issuing it to the firmware. - * https://bugzilla.kernel.org/show_bug.cgi?id=86211 + * Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not + * set, for which case, we complete the QR_EC without issuing it to the + * firmware. + * https://bugzilla.kernel.org/show_bug.cgi?id=82611 + * https://bugzilla.kernel.org/show_bug.cgi?id=97381 */ static int ec_flag_query_handshake(const struct dmi_system_id *id) { @@ -1392,6 +1395,7 @@ static int ec_flag_query_handshake(const struct dmi_system_id *id) EC_FLAGS_QUERY_HANDSHAKE = 1; return 0; } +#endif /* * On some hardware it is necessary to clear events accumulated by the EC during @@ -1414,6 +1418,7 @@ static int ec_clear_on_resume(const struct dmi_system_id *id) { pr_debug("Detected system needing EC poll on resume.\n"); EC_FLAGS_CLEAR_ON_RESUME = 1; + ec_event_clearing = ACPI_EC_EVT_TIMING_STATUS; return 0; } @@ -1443,9 +1448,6 @@ static struct dmi_system_id ec_dmi_table[] __initdata = { { ec_clear_on_resume, "Samsung hardware", { DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD.")}, NULL}, - { - ec_flag_query_handshake, "Acer hardware", { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), }, NULL}, {}, }; -- cgit v0.10.2 From 66db383439b51b1aa920f3579da644fb5fdb2b7c Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Thu, 11 Jun 2015 13:21:51 +0800 Subject: ACPI / EC: Fix a code coverity issue when QR_EC transactions are failed. When the QR_EC transaction fails, the EC_FLAGS_QUERY_PENDING flag prevents the event handling work queue from being scheduled again. Though there shouldn't be failed QR_EC transactions, and this gap was efficiently used for catching and learning the SCI_EVT clearing timing compliance issues, we need to fix this as we are not fully compatible with all platforms/Windows to handle SCI_EVT clearing timing correctly. Fixing this gives the EC driver the chances to recover from a state machine failure. So this patch fixes this issue. When nr_pending_queries drops to 0, it clears EC_FLAGS_QUERY_PENDING at the proper position for different modes in order to ensure that the SCI_EVT handling can proceed. In order to be clearer for future ec_event_clearing modes, all checks in this patch are written in the inclusive style, not the exclusive style. Cc: 3.16+ # 3.16+ Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 79817ce..9d4761d 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -512,7 +512,8 @@ static void advance_transaction(struct acpi_ec *ec) */ if (!t || !(t->flags & ACPI_EC_COMMAND_POLL)) { if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && - test_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags)) { + (!ec->nr_pending_queries || + test_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags))) { clear_bit(EC_FLAGS_QUERY_GUARDING, &ec->flags); acpi_ec_complete_query(ec); } @@ -1065,6 +1066,17 @@ static void acpi_ec_event_handler(struct work_struct *work) (void)acpi_ec_query(ec, NULL); spin_lock_irqsave(&ec->lock, flags); ec->nr_pending_queries--; + /* + * Before exit, make sure that this work item can be + * scheduled again. There might be QR_EC failures, leaving + * EC_FLAGS_QUERY_PENDING uncleared and preventing this work + * item from being scheduled again. + */ + if (!ec->nr_pending_queries) { + if (ec_event_clearing == ACPI_EC_EVT_TIMING_STATUS || + ec_event_clearing == ACPI_EC_EVT_TIMING_QUERY) + acpi_ec_complete_query(ec); + } } spin_unlock_irqrestore(&ec->lock, flags); -- cgit v0.10.2 From d0562674838c08ff142c0e9a8e12634e133c4361 Mon Sep 17 00:00:00 2001 From: "Suthikulpanit, Suravee" Date: Wed, 10 Jun 2015 11:08:52 -0500 Subject: ACPI / scan: Parse _CCA and setup device coherency This patch implements support for ACPI _CCA object, which is introduced in ACPIv5.1, can be used for specifying device DMA coherency attribute. The parsing logic traverses device namespace to parse coherency information, and stores it in acpi_device_flags. Then uses it to call arch_setup_dma_ops() when creating each device enumerated in DSDT during ACPI scan. This patch also introduces acpi_dma_is_coherent(), which provides an interface for device drivers to check the coherency information similarly to the of_dma_is_coherent(). Signed-off-by: Mark Salter Signed-off-by: Suravee Suthikulpanit Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ab2cbb5..212735f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -54,6 +54,9 @@ config ACPI_GENERIC_GSI config ACPI_SYSTEM_POWER_STATES_SUPPORT bool +config ACPI_CCA_REQUIRED + bool + config ACPI_SLEEP bool depends on SUSPEND || HIBERNATION diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 4bf7559..06a67d5 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -103,7 +103,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) pdevinfo.res = resources; pdevinfo.num_res = count; pdevinfo.fwnode = acpi_fwnode_handle(adev); - pdevinfo.dma_mask = DMA_BIT_MASK(32); + pdevinfo.dma_mask = acpi_check_dma(adev, NULL) ? DMA_BIT_MASK(32) : 0; pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) dev_err(&adev->dev, "platform device creation failed: %ld\n", diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 39c485b..b9657af 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "internal.h" @@ -167,6 +168,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) struct list_head *physnode_list; unsigned int node_id; int retval = -EINVAL; + bool coherent; if (has_acpi_companion(dev)) { if (acpi_dev) { @@ -223,6 +225,9 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) if (!has_acpi_companion(dev)) ACPI_COMPANION_SET(dev, acpi_dev); + if (acpi_check_dma(acpi_dev, &coherent)) + arch_setup_dma_ops(dev, 0, 0, NULL, coherent); + acpi_physnode_link_name(physical_node_name, node_id); retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, physical_node_name); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 0a09991..67509b2 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -2111,6 +2112,39 @@ void acpi_free_pnp_ids(struct acpi_device_pnp *pnp) kfree(pnp->unique_id); } +static void acpi_init_coherency(struct acpi_device *adev) +{ + unsigned long long cca = 0; + acpi_status status; + struct acpi_device *parent = adev->parent; + + if (parent && parent->flags.cca_seen) { + /* + * From ACPI spec, OSPM will ignore _CCA if an ancestor + * already saw one. + */ + adev->flags.cca_seen = 1; + cca = parent->flags.coherent_dma; + } else { + status = acpi_evaluate_integer(adev->handle, "_CCA", + NULL, &cca); + if (ACPI_SUCCESS(status)) + adev->flags.cca_seen = 1; + else if (!IS_ENABLED(CONFIG_ACPI_CCA_REQUIRED)) + /* + * If architecture does not specify that _CCA is + * required for DMA-able devices (e.g. x86), + * we default to _CCA=1. + */ + cca = 1; + else + acpi_handle_debug(adev->handle, + "ACPI device is missing _CCA.\n"); + } + + adev->flags.coherent_dma = cca; +} + void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, int type, unsigned long long sta) { @@ -2129,6 +2163,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, device->flags.visited = false; device_initialize(&device->dev); dev_set_uevent_suppress(&device->dev, true); + acpi_init_coherency(device); } void acpi_device_add_finalize(struct acpi_device *device) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index da07997..54df54d 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -209,7 +209,9 @@ struct acpi_device_flags { u32 hotplug_notify:1; u32 is_dock_station:1; u32 of_compatible_ok:1; - u32 reserved:22; + u32 coherent_dma:1; + u32 cca_seen:1; + u32 reserved:20; }; /* File System */ @@ -381,6 +383,39 @@ struct acpi_device { void (*remove)(struct acpi_device *); }; +static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) +{ + bool ret = false; + + if (!adev) + return ret; + + /** + * Currently, we only support _CCA=1 (i.e. coherent_dma=1) + * This should be equivalent to specifyig dma-coherent for + * a device in OF. + * + * For the case when _CCA=0 (i.e. coherent_dma=0 && cca_seen=1), + * There are two cases: + * case 1. Do not support and disable DMA. + * case 2. Support but rely on arch-specific cache maintenance for + * non-coherence DMA operations. + * Currently, we implement case 1 above. + * + * For the case when _CCA is missing (i.e. cca_seen=0) and + * platform specifies ACPI_CCA_REQUIRED, we do not support DMA, + * and fallback to arch-specific default handling. + * + * See acpi_init_coherency() for more info. + */ + if (adev->flags.coherent_dma) { + ret = true; + if (coherent) + *coherent = adev->flags.coherent_dma; + } + return ret; +} + static inline bool is_acpi_node(struct fwnode_handle *fwnode) { return fwnode && fwnode->type == FWNODE_ACPI; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e4da5e3..d46a48c 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -569,6 +569,11 @@ static inline int acpi_device_modalias(struct device *dev, return -ENODEV; } +static inline bool acpi_check_dma(struct acpi_device *adev, bool *coherent) +{ + return false; +} + #define ACPI_PTR(_ptr) (NULL) #endif /* !CONFIG_ACPI */ -- cgit v0.10.2 From b6197b93fa4bcba9313cc415934e3fe27e2db9b0 Mon Sep 17 00:00:00 2001 From: "Suthikulpanit, Suravee" Date: Wed, 10 Jun 2015 11:08:53 -0500 Subject: arm64 : Introduce support for ACPI _CCA object section 6.2.17 _CCA states that ARM platforms require ACPI _CCA object to be specified for DMA-cabpable devices. Therefore, this patch specifies ACPI_CCA_REQUIRED in arm64 Kconfig. In addition, to handle the case when _CCA is missing, arm64 would assign dummy_dma_ops to disable DMA capability of the device. Acked-by: Catalin Marinas Signed-off-by: Mark Salter Signed-off-by: Suravee Suthikulpanit Signed-off-by: Rafael J. Wysocki diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7796af4..6be1a6e 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1,5 +1,6 @@ config ARM64 def_bool y + select ACPI_CCA_REQUIRED if ACPI select ACPI_GENERIC_GSI if ACPI select ACPI_REDUCED_HARDWARE_ONLY if ACPI select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE diff --git a/arch/arm64/include/asm/dma-mapping.h b/arch/arm64/include/asm/dma-mapping.h index 9437e3d..f0d6d0b 100644 --- a/arch/arm64/include/asm/dma-mapping.h +++ b/arch/arm64/include/asm/dma-mapping.h @@ -18,6 +18,7 @@ #ifdef __KERNEL__ +#include #include #include @@ -28,13 +29,23 @@ #define DMA_ERROR_CODE (~(dma_addr_t)0) extern struct dma_map_ops *dma_ops; +extern struct dma_map_ops dummy_dma_ops; static inline struct dma_map_ops *__generic_dma_ops(struct device *dev) { - if (unlikely(!dev) || !dev->archdata.dma_ops) + if (unlikely(!dev)) return dma_ops; - else + else if (dev->archdata.dma_ops) return dev->archdata.dma_ops; + else if (acpi_disabled) + return dma_ops; + + /* + * When ACPI is enabled, if arch_set_dma_ops is not called, + * we will disable device DMA capability by setting it + * to dummy_dma_ops. + */ + return &dummy_dma_ops; } static inline struct dma_map_ops *get_dma_ops(struct device *dev) @@ -48,6 +59,9 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev) static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, struct iommu_ops *iommu, bool coherent) { + if (!acpi_disabled && !dev->archdata.dma_ops) + dev->archdata.dma_ops = dma_ops; + dev->archdata.dma_coherent = coherent; } #define arch_setup_dma_ops arch_setup_dma_ops diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index b0bd4e5..d16a1ce 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -414,6 +414,98 @@ out: return -ENOMEM; } +/******************************************** + * The following APIs are for dummy DMA ops * + ********************************************/ + +static void *__dummy_alloc(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flags, + struct dma_attrs *attrs) +{ + return NULL; +} + +static void __dummy_free(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle, + struct dma_attrs *attrs) +{ +} + +static int __dummy_mmap(struct device *dev, + struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size, + struct dma_attrs *attrs) +{ + return -ENXIO; +} + +static dma_addr_t __dummy_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + return DMA_ERROR_CODE; +} + +static void __dummy_unmap_page(struct device *dev, dma_addr_t dev_addr, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ +} + +static int __dummy_map_sg(struct device *dev, struct scatterlist *sgl, + int nelems, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + return 0; +} + +static void __dummy_unmap_sg(struct device *dev, + struct scatterlist *sgl, int nelems, + enum dma_data_direction dir, + struct dma_attrs *attrs) +{ +} + +static void __dummy_sync_single(struct device *dev, + dma_addr_t dev_addr, size_t size, + enum dma_data_direction dir) +{ +} + +static void __dummy_sync_sg(struct device *dev, + struct scatterlist *sgl, int nelems, + enum dma_data_direction dir) +{ +} + +static int __dummy_mapping_error(struct device *hwdev, dma_addr_t dma_addr) +{ + return 1; +} + +static int __dummy_dma_supported(struct device *hwdev, u64 mask) +{ + return 0; +} + +struct dma_map_ops dummy_dma_ops = { + .alloc = __dummy_alloc, + .free = __dummy_free, + .mmap = __dummy_mmap, + .map_page = __dummy_map_page, + .unmap_page = __dummy_unmap_page, + .map_sg = __dummy_map_sg, + .unmap_sg = __dummy_unmap_sg, + .sync_single_for_cpu = __dummy_sync_single, + .sync_single_for_device = __dummy_sync_single, + .sync_sg_for_cpu = __dummy_sync_sg, + .sync_sg_for_device = __dummy_sync_sg, + .mapping_error = __dummy_mapping_error, + .dma_supported = __dummy_dma_supported, +}; +EXPORT_SYMBOL(dummy_dma_ops); + static int __init arm64_dma_init(void) { int ret; -- cgit v0.10.2 From 05ca556003b1d6b4df0b8831e4c07fad7f5bdd2c Mon Sep 17 00:00:00 2001 From: "Suthikulpanit, Suravee" Date: Wed, 10 Jun 2015 11:08:54 -0500 Subject: device property: Introduces device_dma_is_coherent() Currently, device drivers, which support both OF and ACPI, need to call two separate APIs, of_dma_is_coherent() and acpi_dma_is_coherent()) to determine device coherency attribute. This patch simplifies this process by introducing a new device property API, device_dma_is_coherent(), which calls the appropriate interface based on the booting architecture. Signed-off-by: Suravee Suthikulpanit Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/property.c b/drivers/base/property.c index 1d0b116..e645852 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -14,6 +14,7 @@ #include #include #include +#include #include /** @@ -519,3 +520,16 @@ unsigned int device_get_child_node_count(struct device *dev) return count; } EXPORT_SYMBOL_GPL(device_get_child_node_count); + +bool device_dma_is_coherent(struct device *dev) +{ + bool coherent = false; + + if (IS_ENABLED(CONFIG_OF) && dev->of_node) + coherent = of_dma_is_coherent(dev->of_node); + else + acpi_check_dma(ACPI_COMPANION(dev), &coherent); + + return coherent; +} +EXPORT_SYMBOL_GPL(device_dma_is_coherent); diff --git a/include/linux/property.h b/include/linux/property.h index de8bdf4..76ebde9 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -164,4 +164,6 @@ struct property_set { void device_add_property_set(struct device *dev, struct property_set *pset); +bool device_dma_is_coherent(struct device *dev); + #endif /* _LINUX_PROPERTY_H_ */ -- cgit v0.10.2 From 04825cfedf525b455384547adb90102d02b293b4 Mon Sep 17 00:00:00 2001 From: "Suthikulpanit, Suravee" Date: Wed, 10 Jun 2015 11:08:55 -0500 Subject: crypto: ccp - Unify coherency checking logic with device_dma_is_coherent() Currently, the driver has separate logic to determine device coherency for DT vs ACPI. This patch simplifies the code with a call to device_dma_is_coherent(). Signed-off-by: Tom Lendacky Signed-off-by: Suravee Suthikulpanit Signed-off-by: Rafael J. Wysocki diff --git a/drivers/crypto/ccp/ccp-platform.c b/drivers/crypto/ccp/ccp-platform.c index b1c20b2..e446781 100644 --- a/drivers/crypto/ccp/ccp-platform.c +++ b/drivers/crypto/ccp/ccp-platform.c @@ -90,58 +90,6 @@ static struct resource *ccp_find_mmio_area(struct ccp_device *ccp) return NULL; } -#ifdef CONFIG_ACPI -static int ccp_acpi_support(struct ccp_device *ccp) -{ - struct ccp_platform *ccp_platform = ccp->dev_specific; - struct acpi_device *adev = ACPI_COMPANION(ccp->dev); - acpi_handle handle; - acpi_status status; - unsigned long long data; - int cca; - - /* Retrieve the device cache coherency value */ - handle = adev->handle; - do { - status = acpi_evaluate_integer(handle, "_CCA", NULL, &data); - if (!ACPI_FAILURE(status)) { - cca = data; - break; - } - } while (!ACPI_FAILURE(status)); - - if (ACPI_FAILURE(status)) { - dev_err(ccp->dev, "error obtaining acpi coherency value\n"); - return -EINVAL; - } - - ccp_platform->coherent = !!cca; - - return 0; -} -#else /* CONFIG_ACPI */ -static int ccp_acpi_support(struct ccp_device *ccp) -{ - return -EINVAL; -} -#endif - -#ifdef CONFIG_OF -static int ccp_of_support(struct ccp_device *ccp) -{ - struct ccp_platform *ccp_platform = ccp->dev_specific; - - ccp_platform->coherent = of_dma_is_coherent(ccp->dev->of_node); - - return 0; -} -#else -static int ccp_of_support(struct ccp_device *ccp) -{ - return -EINVAL; -} -#endif - static int ccp_platform_probe(struct platform_device *pdev) { struct ccp_device *ccp; @@ -182,13 +130,7 @@ static int ccp_platform_probe(struct platform_device *pdev) goto e_err; } - if (ccp_platform->use_acpi) - ret = ccp_acpi_support(ccp); - else - ret = ccp_of_support(ccp); - if (ret) - goto e_err; - + ccp_platform->coherent = device_dma_is_coherent(ccp->dev); if (ccp_platform->coherent) ccp->axcache = CACHE_WB_NO_ALLOC; else -- cgit v0.10.2 From c9a49642aef95d5adb6b7a4bd6a6f5df852cdb98 Mon Sep 17 00:00:00 2001 From: "Suthikulpanit, Suravee" Date: Wed, 10 Jun 2015 11:08:56 -0500 Subject: amd-xgbe: Unify coherency checking logic with device_dma_is_coherent() Currently, amd-xgbe driver has separate logic to determine device coherency for DT vs. ACPI. This patch simplifies the code with a call to device_dma_is_coherent(). Signed-off-by: Tom Lendacky Signed-off-by: Suravee Suthikulpanit Signed-off-by: Rafael J. Wysocki diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 7149053..6d2c702 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -168,13 +168,8 @@ static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata) #ifdef CONFIG_ACPI static int xgbe_acpi_support(struct xgbe_prv_data *pdata) { - struct acpi_device *adev = pdata->adev; struct device *dev = pdata->dev; u32 property; - acpi_handle handle; - acpi_status status; - unsigned long long data; - int cca; int ret; /* Obtain the system clock setting */ @@ -195,24 +190,6 @@ static int xgbe_acpi_support(struct xgbe_prv_data *pdata) } pdata->ptpclk_rate = property; - /* Retrieve the device cache coherency value */ - handle = adev->handle; - do { - status = acpi_evaluate_integer(handle, "_CCA", NULL, &data); - if (!ACPI_FAILURE(status)) { - cca = data; - break; - } - - status = acpi_get_parent(handle, &handle); - } while (!ACPI_FAILURE(status)); - - if (ACPI_FAILURE(status)) { - dev_err(dev, "error obtaining acpi coherency value\n"); - return -EINVAL; - } - pdata->coherent = !!cca; - return 0; } #else /* CONFIG_ACPI */ @@ -243,9 +220,6 @@ static int xgbe_of_support(struct xgbe_prv_data *pdata) } pdata->ptpclk_rate = clk_get_rate(pdata->ptpclk); - /* Retrieve the device cache coherency value */ - pdata->coherent = of_dma_is_coherent(dev->of_node); - return 0; } #else /* CONFIG_OF */ @@ -364,6 +338,7 @@ static int xgbe_probe(struct platform_device *pdev) goto err_io; /* Set the DMA coherency values */ + pdata->coherent = device_dma_is_coherent(pdata->dev); if (pdata->coherent) { pdata->axdomain = XGBE_DMA_OS_AXDOMAIN; pdata->arcache = XGBE_DMA_OS_ARCACHE; -- cgit v0.10.2 From ad466c6c1edd3deac6deda3885f12d0b05efac11 Mon Sep 17 00:00:00 2001 From: "Suthikulpanit, Suravee" Date: Wed, 10 Jun 2015 11:08:57 -0500 Subject: megaraid_sas: fix TRUE and FALSE re-define build error Signed-off-by: Suravee Suthikulpanit Cc: Kashyap Desai Cc: Sumit Saxena Cc: Uday Lingala Acked-by: Sumit Saxena Signed-off-by: Rafael J. Wysocki diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index 4f72287..e8b7a69 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -66,7 +66,15 @@ MODULE_PARM_DESC(lb_pending_cmds, "Change raid-1 load balancing outstanding " #define ABS_DIFF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) #define MR_LD_STATE_OPTIMAL 3 + +#ifdef FALSE +#undef FALSE +#endif #define FALSE 0 + +#ifdef TRUE +#undef TRUE +#endif #define TRUE 1 #define SPAN_DEBUG 0 -- cgit v0.10.2 From e144cd045e123757b6b5f732c420b94dd5544407 Mon Sep 17 00:00:00 2001 From: "Suthikulpanit, Suravee" Date: Wed, 10 Jun 2015 11:08:58 -0500 Subject: ufs: fix TRUE and FALSE re-define build error Signed-off-by: Suravee Suthikulpanit Cc: Vinayak Holikatti Signed-off-by: Rafael J. Wysocki diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h index 3fc3e21..816a8a4 100644 --- a/drivers/scsi/ufs/unipro.h +++ b/drivers/scsi/ufs/unipro.h @@ -198,6 +198,14 @@ enum ufs_hs_gear_tag { #define T_TC0TXMAXSDUSIZE 0x4060 #define T_TC1TXMAXSDUSIZE 0x4061 +#ifdef FALSE +#undef FALSE +#endif + +#ifdef TRUE +#undef TRUE +#endif + /* Boolean attribute values */ enum { FALSE = 0, -- cgit v0.10.2 From 8e0484d2b38aeb2bcce0a7b32e6b33d72c11ad85 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 3 Jun 2015 15:57:11 +0530 Subject: cpufreq: governor: register notifier from cs_init() Notifiers are required only for conservative governor and the common governor code is unnecessarily polluted with that. Handle that from cs_init/exit() instead of cpufreq_governor_dbs(). Signed-off-by: Viresh Kumar Reviewed-by: Preeti U Murthy Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 25a70d0..75f875b 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -148,6 +148,10 @@ static int dbs_cpufreq_notifier(struct notifier_block *nb, unsigned long val, return 0; } +static struct notifier_block cs_cpufreq_notifier_block = { + .notifier_call = dbs_cpufreq_notifier, +}; + /************************** sysfs interface ************************/ static struct common_dbs_data cs_dbs_cdata; @@ -317,7 +321,7 @@ static struct attribute_group cs_attr_group_gov_pol = { /************************** sysfs end ************************/ -static int cs_init(struct dbs_data *dbs_data) +static int cs_init(struct dbs_data *dbs_data, bool notify) { struct cs_dbs_tuners *tuners; @@ -336,25 +340,26 @@ static int cs_init(struct dbs_data *dbs_data) dbs_data->tuners = tuners; dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO * jiffies_to_usecs(10); + + if (notify) + cpufreq_register_notifier(&cs_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + mutex_init(&dbs_data->mutex); return 0; } -static void cs_exit(struct dbs_data *dbs_data) +static void cs_exit(struct dbs_data *dbs_data, bool notify) { + if (notify) + cpufreq_unregister_notifier(&cs_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + kfree(dbs_data->tuners); } define_get_cpu_dbs_routines(cs_cpu_dbs_info); -static struct notifier_block cs_cpufreq_notifier_block = { - .notifier_call = dbs_cpufreq_notifier, -}; - -static struct cs_ops cs_ops = { - .notifier_block = &cs_cpufreq_notifier_block, -}; - static struct common_dbs_data cs_dbs_cdata = { .governor = GOV_CONSERVATIVE, .attr_group_gov_sys = &cs_attr_group_gov_sys, @@ -363,7 +368,6 @@ static struct common_dbs_data cs_dbs_cdata = { .get_cpu_dbs_info_s = get_cpu_dbs_info_s, .gov_dbs_timer = cs_dbs_timer, .gov_check_cpu = cs_check_cpu, - .gov_ops = &cs_ops, .init = cs_init, .exit = cs_exit, }; diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 1b44496..d64a82e 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -278,7 +278,7 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, dbs_data->cdata = cdata; dbs_data->usage_count = 1; - rc = cdata->init(dbs_data); + rc = cdata->init(dbs_data, !policy->governor->initialized); if (rc) { pr_err("%s: POLICY_INIT: init() failed\n", __func__); kfree(dbs_data); @@ -291,7 +291,7 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, rc = sysfs_create_group(get_governor_parent_kobj(policy), get_sysfs_attr(dbs_data)); if (rc) { - cdata->exit(dbs_data); + cdata->exit(dbs_data, !policy->governor->initialized); kfree(dbs_data); return rc; } @@ -309,14 +309,6 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate, latency * LATENCY_MULTIPLIER)); - if ((cdata->governor == GOV_CONSERVATIVE) && - (!policy->governor->initialized)) { - struct cs_ops *cs_ops = dbs_data->cdata->gov_ops; - - cpufreq_register_notifier(cs_ops->notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); - } - if (!have_governor_per_policy()) cdata->gdbs_data = dbs_data; @@ -329,15 +321,7 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, if (!have_governor_per_policy()) cpufreq_put_global_kobject(); - if ((dbs_data->cdata->governor == GOV_CONSERVATIVE) && - (policy->governor->initialized == 1)) { - struct cs_ops *cs_ops = dbs_data->cdata->gov_ops; - - cpufreq_unregister_notifier(cs_ops->notifier_block, - CPUFREQ_TRANSITION_NOTIFIER); - } - - cdata->exit(dbs_data); + cdata->exit(dbs_data, policy->governor->initialized == 1); kfree(dbs_data); cdata->gdbs_data = NULL; } diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index cc401d1..1690120 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -208,8 +208,8 @@ struct common_dbs_data { void *(*get_cpu_dbs_info_s)(int cpu); void (*gov_dbs_timer)(struct work_struct *work); void (*gov_check_cpu)(int cpu, unsigned int load); - int (*init)(struct dbs_data *dbs_data); - void (*exit)(struct dbs_data *dbs_data); + int (*init)(struct dbs_data *dbs_data, bool notify); + void (*exit)(struct dbs_data *dbs_data, bool notify); /* Governor specific ops, see below */ void *gov_ops; @@ -234,10 +234,6 @@ struct od_ops { void (*freq_increase)(struct cpufreq_policy *policy, unsigned int freq); }; -struct cs_ops { - struct notifier_block *notifier_block; -}; - static inline int delay_for_sampling_rate(unsigned int sampling_rate) { int delay = usecs_to_jiffies(sampling_rate); diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index ad3f38f..4fe78a9 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -475,7 +475,7 @@ static struct attribute_group od_attr_group_gov_pol = { /************************** sysfs end ************************/ -static int od_init(struct dbs_data *dbs_data) +static int od_init(struct dbs_data *dbs_data, bool notify) { struct od_dbs_tuners *tuners; u64 idle_time; @@ -517,7 +517,7 @@ static int od_init(struct dbs_data *dbs_data) return 0; } -static void od_exit(struct dbs_data *dbs_data) +static void od_exit(struct dbs_data *dbs_data, bool notify) { kfree(dbs_data->tuners); } -- cgit v0.10.2 From 714a2d9c8792919090f256c16286ac3cff4cb489 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Jun 2015 16:43:27 +0530 Subject: cpufreq: governor: split cpufreq_governor_dbs() cpufreq_governor_dbs() is hardly readable, it is just too big and complicated. Lets make it more readable by splitting out event specific routines. Order of statements is changed at few places, but that shouldn't bring any functional change. Signed-off-by: Viresh Kumar Reviewed-by: Preeti U Murthy Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index d64a82e..ccf6ce7 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -239,195 +239,244 @@ static void set_sampling_rate(struct dbs_data *dbs_data, } } -int cpufreq_governor_dbs(struct cpufreq_policy *policy, - struct common_dbs_data *cdata, unsigned int event) +static int cpufreq_governor_init(struct cpufreq_policy *policy, + struct dbs_data *dbs_data, + struct common_dbs_data *cdata) { - struct dbs_data *dbs_data; - struct od_cpu_dbs_info_s *od_dbs_info = NULL; - struct cs_cpu_dbs_info_s *cs_dbs_info = NULL; - struct od_ops *od_ops = NULL; - struct od_dbs_tuners *od_tuners = NULL; - struct cs_dbs_tuners *cs_tuners = NULL; - struct cpu_dbs_common_info *cpu_cdbs; - unsigned int sampling_rate, latency, ignore_nice, j, cpu = policy->cpu; - int io_busy = 0; - int rc; + unsigned int latency; + int ret; - if (have_governor_per_policy()) - dbs_data = policy->governor_data; - else - dbs_data = cdata->gdbs_data; + if (dbs_data) { + if (WARN_ON(have_governor_per_policy())) + return -EINVAL; + dbs_data->usage_count++; + policy->governor_data = dbs_data; + return 0; + } - WARN_ON(!dbs_data && (event != CPUFREQ_GOV_POLICY_INIT)); + dbs_data = kzalloc(sizeof(*dbs_data), GFP_KERNEL); + if (!dbs_data) + return -ENOMEM; - switch (event) { - case CPUFREQ_GOV_POLICY_INIT: - if (have_governor_per_policy()) { - WARN_ON(dbs_data); - } else if (dbs_data) { - dbs_data->usage_count++; - policy->governor_data = dbs_data; - return 0; - } + dbs_data->cdata = cdata; + dbs_data->usage_count = 1; - dbs_data = kzalloc(sizeof(*dbs_data), GFP_KERNEL); - if (!dbs_data) { - pr_err("%s: POLICY_INIT: kzalloc failed\n", __func__); - return -ENOMEM; - } + ret = cdata->init(dbs_data, !policy->governor->initialized); + if (ret) + goto free_dbs_data; - dbs_data->cdata = cdata; - dbs_data->usage_count = 1; - rc = cdata->init(dbs_data, !policy->governor->initialized); - if (rc) { - pr_err("%s: POLICY_INIT: init() failed\n", __func__); - kfree(dbs_data); - return rc; - } + /* policy latency is in ns. Convert it to us first */ + latency = policy->cpuinfo.transition_latency / 1000; + if (latency == 0) + latency = 1; - if (!have_governor_per_policy()) - WARN_ON(cpufreq_get_global_kobject()); + /* Bring kernel and HW constraints together */ + dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate, + MIN_LATENCY_MULTIPLIER * latency); + set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate, + latency * LATENCY_MULTIPLIER)); - rc = sysfs_create_group(get_governor_parent_kobj(policy), - get_sysfs_attr(dbs_data)); - if (rc) { - cdata->exit(dbs_data, !policy->governor->initialized); - kfree(dbs_data); - return rc; + if (!have_governor_per_policy()) { + if (WARN_ON(cpufreq_get_global_kobject())) { + ret = -EINVAL; + goto cdata_exit; } + cdata->gdbs_data = dbs_data; + } - policy->governor_data = dbs_data; + ret = sysfs_create_group(get_governor_parent_kobj(policy), + get_sysfs_attr(dbs_data)); + if (ret) + goto put_kobj; - /* policy latency is in ns. Convert it to us first */ - latency = policy->cpuinfo.transition_latency / 1000; - if (latency == 0) - latency = 1; + policy->governor_data = dbs_data; - /* Bring kernel and HW constraints together */ - dbs_data->min_sampling_rate = max(dbs_data->min_sampling_rate, - MIN_LATENCY_MULTIPLIER * latency); - set_sampling_rate(dbs_data, max(dbs_data->min_sampling_rate, - latency * LATENCY_MULTIPLIER)); + return 0; - if (!have_governor_per_policy()) - cdata->gdbs_data = dbs_data; +put_kobj: + if (!have_governor_per_policy()) { + cdata->gdbs_data = NULL; + cpufreq_put_global_kobject(); + } +cdata_exit: + cdata->exit(dbs_data, !policy->governor->initialized); +free_dbs_data: + kfree(dbs_data); + return ret; +} - return 0; - case CPUFREQ_GOV_POLICY_EXIT: - if (!--dbs_data->usage_count) { - sysfs_remove_group(get_governor_parent_kobj(policy), - get_sysfs_attr(dbs_data)); +static void cpufreq_governor_exit(struct cpufreq_policy *policy, + struct dbs_data *dbs_data) +{ + struct common_dbs_data *cdata = dbs_data->cdata; - if (!have_governor_per_policy()) - cpufreq_put_global_kobject(); + policy->governor_data = NULL; + if (!--dbs_data->usage_count) { + sysfs_remove_group(get_governor_parent_kobj(policy), + get_sysfs_attr(dbs_data)); - cdata->exit(dbs_data, policy->governor->initialized == 1); - kfree(dbs_data); + if (!have_governor_per_policy()) { cdata->gdbs_data = NULL; + cpufreq_put_global_kobject(); } - policy->governor_data = NULL; - return 0; + cdata->exit(dbs_data, policy->governor->initialized == 1); + kfree(dbs_data); } +} - cpu_cdbs = dbs_data->cdata->get_cpu_cdbs(cpu); +static int cpufreq_governor_start(struct cpufreq_policy *policy, + struct dbs_data *dbs_data) +{ + struct common_dbs_data *cdata = dbs_data->cdata; + unsigned int sampling_rate, ignore_nice, j, cpu = policy->cpu; + struct cpu_dbs_common_info *cpu_cdbs = cdata->get_cpu_cdbs(cpu); + int io_busy = 0; + + if (!policy->cur) + return -EINVAL; + + if (cdata->governor == GOV_CONSERVATIVE) { + struct cs_dbs_tuners *cs_tuners = dbs_data->tuners; - if (dbs_data->cdata->governor == GOV_CONSERVATIVE) { - cs_tuners = dbs_data->tuners; - cs_dbs_info = dbs_data->cdata->get_cpu_dbs_info_s(cpu); sampling_rate = cs_tuners->sampling_rate; ignore_nice = cs_tuners->ignore_nice_load; } else { - od_tuners = dbs_data->tuners; - od_dbs_info = dbs_data->cdata->get_cpu_dbs_info_s(cpu); + struct od_dbs_tuners *od_tuners = dbs_data->tuners; + sampling_rate = od_tuners->sampling_rate; ignore_nice = od_tuners->ignore_nice_load; - od_ops = dbs_data->cdata->gov_ops; io_busy = od_tuners->io_is_busy; } - switch (event) { - case CPUFREQ_GOV_START: - if (!policy->cur) - return -EINVAL; + mutex_lock(&dbs_data->mutex); - mutex_lock(&dbs_data->mutex); + for_each_cpu(j, policy->cpus) { + struct cpu_dbs_common_info *j_cdbs = cdata->get_cpu_cdbs(j); + unsigned int prev_load; - for_each_cpu(j, policy->cpus) { - struct cpu_dbs_common_info *j_cdbs = - dbs_data->cdata->get_cpu_cdbs(j); - unsigned int prev_load; + j_cdbs->cpu = j; + j_cdbs->cur_policy = policy; + j_cdbs->prev_cpu_idle = + get_cpu_idle_time(j, &j_cdbs->prev_cpu_wall, io_busy); - j_cdbs->cpu = j; - j_cdbs->cur_policy = policy; - j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, - &j_cdbs->prev_cpu_wall, io_busy); + prev_load = (unsigned int)(j_cdbs->prev_cpu_wall - + j_cdbs->prev_cpu_idle); + j_cdbs->prev_load = 100 * prev_load / + (unsigned int)j_cdbs->prev_cpu_wall; - prev_load = (unsigned int) - (j_cdbs->prev_cpu_wall - j_cdbs->prev_cpu_idle); - j_cdbs->prev_load = 100 * prev_load / - (unsigned int) j_cdbs->prev_cpu_wall; + if (ignore_nice) + j_cdbs->prev_cpu_nice = kcpustat_cpu(j).cpustat[CPUTIME_NICE]; - if (ignore_nice) - j_cdbs->prev_cpu_nice = - kcpustat_cpu(j).cpustat[CPUTIME_NICE]; + mutex_init(&j_cdbs->timer_mutex); + INIT_DEFERRABLE_WORK(&j_cdbs->work, cdata->gov_dbs_timer); + } - mutex_init(&j_cdbs->timer_mutex); - INIT_DEFERRABLE_WORK(&j_cdbs->work, - dbs_data->cdata->gov_dbs_timer); - } + if (cdata->governor == GOV_CONSERVATIVE) { + struct cs_cpu_dbs_info_s *cs_dbs_info = + cdata->get_cpu_dbs_info_s(cpu); - if (dbs_data->cdata->governor == GOV_CONSERVATIVE) { - cs_dbs_info->down_skip = 0; - cs_dbs_info->enable = 1; - cs_dbs_info->requested_freq = policy->cur; - } else { - od_dbs_info->rate_mult = 1; - od_dbs_info->sample_type = OD_NORMAL_SAMPLE; - od_ops->powersave_bias_init_cpu(cpu); - } + cs_dbs_info->down_skip = 0; + cs_dbs_info->enable = 1; + cs_dbs_info->requested_freq = policy->cur; + } else { + struct od_ops *od_ops = cdata->gov_ops; + struct od_cpu_dbs_info_s *od_dbs_info = cdata->get_cpu_dbs_info_s(cpu); - mutex_unlock(&dbs_data->mutex); + od_dbs_info->rate_mult = 1; + od_dbs_info->sample_type = OD_NORMAL_SAMPLE; + od_ops->powersave_bias_init_cpu(cpu); + } - /* Initiate timer time stamp */ - cpu_cdbs->time_stamp = ktime_get(); + mutex_unlock(&dbs_data->mutex); - gov_queue_work(dbs_data, policy, - delay_for_sampling_rate(sampling_rate), true); - break; + /* Initiate timer time stamp */ + cpu_cdbs->time_stamp = ktime_get(); - case CPUFREQ_GOV_STOP: - if (dbs_data->cdata->governor == GOV_CONSERVATIVE) - cs_dbs_info->enable = 0; + gov_queue_work(dbs_data, policy, delay_for_sampling_rate(sampling_rate), + true); + return 0; +} + +static void cpufreq_governor_stop(struct cpufreq_policy *policy, + struct dbs_data *dbs_data) +{ + struct common_dbs_data *cdata = dbs_data->cdata; + unsigned int cpu = policy->cpu; + struct cpu_dbs_common_info *cpu_cdbs = cdata->get_cpu_cdbs(cpu); + + if (cdata->governor == GOV_CONSERVATIVE) { + struct cs_cpu_dbs_info_s *cs_dbs_info = + cdata->get_cpu_dbs_info_s(cpu); - gov_cancel_work(dbs_data, policy); + cs_dbs_info->enable = 0; + } + + gov_cancel_work(dbs_data, policy); + + mutex_lock(&dbs_data->mutex); + mutex_destroy(&cpu_cdbs->timer_mutex); + cpu_cdbs->cur_policy = NULL; + mutex_unlock(&dbs_data->mutex); +} - mutex_lock(&dbs_data->mutex); - mutex_destroy(&cpu_cdbs->timer_mutex); - cpu_cdbs->cur_policy = NULL; +static void cpufreq_governor_limits(struct cpufreq_policy *policy, + struct dbs_data *dbs_data) +{ + struct common_dbs_data *cdata = dbs_data->cdata; + unsigned int cpu = policy->cpu; + struct cpu_dbs_common_info *cpu_cdbs = cdata->get_cpu_cdbs(cpu); + mutex_lock(&dbs_data->mutex); + if (!cpu_cdbs->cur_policy) { mutex_unlock(&dbs_data->mutex); + return; + } - break; + mutex_lock(&cpu_cdbs->timer_mutex); + if (policy->max < cpu_cdbs->cur_policy->cur) + __cpufreq_driver_target(cpu_cdbs->cur_policy, policy->max, + CPUFREQ_RELATION_H); + else if (policy->min > cpu_cdbs->cur_policy->cur) + __cpufreq_driver_target(cpu_cdbs->cur_policy, policy->min, + CPUFREQ_RELATION_L); + dbs_check_cpu(dbs_data, cpu); + mutex_unlock(&cpu_cdbs->timer_mutex); + + mutex_unlock(&dbs_data->mutex); +} +int cpufreq_governor_dbs(struct cpufreq_policy *policy, + struct common_dbs_data *cdata, unsigned int event) +{ + struct dbs_data *dbs_data; + int ret = 0; + + if (have_governor_per_policy()) + dbs_data = policy->governor_data; + else + dbs_data = cdata->gdbs_data; + + WARN_ON(!dbs_data && (event != CPUFREQ_GOV_POLICY_INIT)); + + switch (event) { + case CPUFREQ_GOV_POLICY_INIT: + ret = cpufreq_governor_init(policy, dbs_data, cdata); + break; + case CPUFREQ_GOV_POLICY_EXIT: + cpufreq_governor_exit(policy, dbs_data); + break; + case CPUFREQ_GOV_START: + ret = cpufreq_governor_start(policy, dbs_data); + break; + case CPUFREQ_GOV_STOP: + cpufreq_governor_stop(policy, dbs_data); + break; case CPUFREQ_GOV_LIMITS: - mutex_lock(&dbs_data->mutex); - if (!cpu_cdbs->cur_policy) { - mutex_unlock(&dbs_data->mutex); - break; - } - mutex_lock(&cpu_cdbs->timer_mutex); - if (policy->max < cpu_cdbs->cur_policy->cur) - __cpufreq_driver_target(cpu_cdbs->cur_policy, - policy->max, CPUFREQ_RELATION_H); - else if (policy->min > cpu_cdbs->cur_policy->cur) - __cpufreq_driver_target(cpu_cdbs->cur_policy, - policy->min, CPUFREQ_RELATION_L); - dbs_check_cpu(dbs_data, cpu); - mutex_unlock(&cpu_cdbs->timer_mutex); - mutex_unlock(&dbs_data->mutex); + cpufreq_governor_limits(policy, dbs_data); break; } - return 0; + + return ret; } EXPORT_SYMBOL_GPL(cpufreq_governor_dbs); -- cgit v0.10.2 From 732b6d617a4cfd8363d1f70a06bff38b8c1a19e9 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 3 Jun 2015 15:57:13 +0530 Subject: cpufreq: governor: Serialize governor callbacks There are several races reported in cpufreq core around governors (only ondemand and conservative) by different people. There are at least two race scenarios present in governor code: (a) Concurrent access/updates of governor internal structures. It is possible that fields such as 'dbs_data->usage_count', etc. are accessed simultaneously for different policies using same governor structure (i.e. CPUFREQ_HAVE_GOVERNOR_PER_POLICY flag unset). And because of this we can dereference bad pointers. For example consider a system with two CPUs with separate 'struct cpufreq_policy' instances. CPU0 governor: ondemand and CPU1: powersave. CPU0 switching to powersave and CPU1 to ondemand: CPU0 CPU1 store* store* cpufreq_governor_exit() cpufreq_governor_init() dbs_data = cdata->gdbs_data; if (!--dbs_data->usage_count) kfree(dbs_data); dbs_data->usage_count++; *Bad pointer dereference* There are other races possible between EXIT and START/STOP/LIMIT as well. Its really complicated. (b) Switching governor state in bad sequence: For example trying to switch a governor to START state, when the governor is in EXIT state. There are some checks present in __cpufreq_governor() but they aren't sufficient as they compare events against 'policy->governor_enabled', where as we need to take governor's state into account, which can be used by multiple policies. These two issues need to be solved separately and the responsibility should be properly divided between cpufreq and governor core. The first problem is more about the governor core, as it needs to protect its structures properly. And the second problem should be fixed in cpufreq core instead of governor, as its all about sequence of events. This patch is trying to solve only the first problem. There are two types of data we need to protect, - 'struct common_dbs_data': No matter what, there is going to be a single copy of this per governor. - 'struct dbs_data': With CPUFREQ_HAVE_GOVERNOR_PER_POLICY flag set, we will have per-policy copy of this data, otherwise a single copy. Because of such complexities, the mutex present in 'struct dbs_data' is insufficient to solve our problem. For example we need to protect fetching of 'dbs_data' from different structures at the beginning of cpufreq_governor_dbs(), to make sure it isn't currently being updated. This can be fixed if we can guarantee serialization of event parsing code for an individual governor. This is best solved with a mutex per governor, and the placeholder for that is 'struct common_dbs_data'. And so this patch moves the mutex from 'struct dbs_data' to 'struct common_dbs_data' and takes it at the beginning and drops it at the end of cpufreq_governor_dbs(). Tested with and without following configuration options: CONFIG_LOCKDEP_SUPPORT=y CONFIG_DEBUG_RT_MUTEXES=y CONFIG_DEBUG_PI_LIST=y CONFIG_DEBUG_SPINLOCK=y CONFIG_DEBUG_MUTEXES=y CONFIG_DEBUG_LOCK_ALLOC=y CONFIG_PROVE_LOCKING=y CONFIG_LOCKDEP=y CONFIG_DEBUG_ATOMIC_SLEEP=y Signed-off-by: Viresh Kumar Reviewed-by: Preeti U Murthy Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 75f875b..c86a10c 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -345,7 +345,6 @@ static int cs_init(struct dbs_data *dbs_data, bool notify) cpufreq_register_notifier(&cs_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); - mutex_init(&dbs_data->mutex); return 0; } @@ -370,6 +369,7 @@ static struct common_dbs_data cs_dbs_cdata = { .gov_check_cpu = cs_check_cpu, .init = cs_init, .exit = cs_exit, + .mutex = __MUTEX_INITIALIZER(cs_dbs_cdata.mutex), }; static int cs_cpufreq_governor_dbs(struct cpufreq_policy *policy, diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index ccf6ce7..57a39f8 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -349,8 +349,6 @@ static int cpufreq_governor_start(struct cpufreq_policy *policy, io_busy = od_tuners->io_is_busy; } - mutex_lock(&dbs_data->mutex); - for_each_cpu(j, policy->cpus) { struct cpu_dbs_common_info *j_cdbs = cdata->get_cpu_cdbs(j); unsigned int prev_load; @@ -388,8 +386,6 @@ static int cpufreq_governor_start(struct cpufreq_policy *policy, od_ops->powersave_bias_init_cpu(cpu); } - mutex_unlock(&dbs_data->mutex); - /* Initiate timer time stamp */ cpu_cdbs->time_stamp = ktime_get(); @@ -414,10 +410,8 @@ static void cpufreq_governor_stop(struct cpufreq_policy *policy, gov_cancel_work(dbs_data, policy); - mutex_lock(&dbs_data->mutex); mutex_destroy(&cpu_cdbs->timer_mutex); cpu_cdbs->cur_policy = NULL; - mutex_unlock(&dbs_data->mutex); } static void cpufreq_governor_limits(struct cpufreq_policy *policy, @@ -427,11 +421,8 @@ static void cpufreq_governor_limits(struct cpufreq_policy *policy, unsigned int cpu = policy->cpu; struct cpu_dbs_common_info *cpu_cdbs = cdata->get_cpu_cdbs(cpu); - mutex_lock(&dbs_data->mutex); - if (!cpu_cdbs->cur_policy) { - mutex_unlock(&dbs_data->mutex); + if (!cpu_cdbs->cur_policy) return; - } mutex_lock(&cpu_cdbs->timer_mutex); if (policy->max < cpu_cdbs->cur_policy->cur) @@ -442,8 +433,6 @@ static void cpufreq_governor_limits(struct cpufreq_policy *policy, CPUFREQ_RELATION_L); dbs_check_cpu(dbs_data, cpu); mutex_unlock(&cpu_cdbs->timer_mutex); - - mutex_unlock(&dbs_data->mutex); } int cpufreq_governor_dbs(struct cpufreq_policy *policy, @@ -452,12 +441,18 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, struct dbs_data *dbs_data; int ret = 0; + /* Lock governor to block concurrent initialization of governor */ + mutex_lock(&cdata->mutex); + if (have_governor_per_policy()) dbs_data = policy->governor_data; else dbs_data = cdata->gdbs_data; - WARN_ON(!dbs_data && (event != CPUFREQ_GOV_POLICY_INIT)); + if (WARN_ON(!dbs_data && (event != CPUFREQ_GOV_POLICY_INIT))) { + ret = -EINVAL; + goto unlock; + } switch (event) { case CPUFREQ_GOV_POLICY_INIT: @@ -477,6 +472,9 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy, break; } +unlock: + mutex_unlock(&cdata->mutex); + return ret; } EXPORT_SYMBOL_GPL(cpufreq_governor_dbs); diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index 1690120..34736f5 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -213,6 +213,11 @@ struct common_dbs_data { /* Governor specific ops, see below */ void *gov_ops; + + /* + * Protects governor's data (struct dbs_data and struct common_dbs_data) + */ + struct mutex mutex; }; /* Governor Per policy data */ @@ -221,9 +226,6 @@ struct dbs_data { unsigned int min_sampling_rate; int usage_count; void *tuners; - - /* dbs_mutex protects dbs_enable in governor start/stop */ - struct mutex mutex; }; /* Governor specific ops, will be passed to dbs_data->gov_ops */ diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 4fe78a9..3c1e10f 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -513,7 +513,6 @@ static int od_init(struct dbs_data *dbs_data, bool notify) tuners->io_is_busy = should_io_be_busy(); dbs_data->tuners = tuners; - mutex_init(&dbs_data->mutex); return 0; } @@ -541,6 +540,7 @@ static struct common_dbs_data od_dbs_cdata = { .gov_ops = &od_ops, .init = od_init, .exit = od_exit, + .mutex = __MUTEX_INITIALIZER(od_dbs_cdata.mutex), }; static void od_set_powersave_bias(unsigned int powersave_bias) -- cgit v0.10.2 From 97155e033662d0e9059ed2a007b0eb5080339349 Mon Sep 17 00:00:00 2001 From: Shailendra Verma Date: Sat, 23 May 2015 10:36:18 +0530 Subject: cpufreq: nforce2: Fix typo in comment to function nforce2_init() Signed-off-by: Shailendra Verma Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c index a225809..db69eeb 100644 --- a/drivers/cpufreq/cpufreq-nforce2.c +++ b/drivers/cpufreq/cpufreq-nforce2.c @@ -414,7 +414,7 @@ static int nforce2_detect_chipset(void) * nforce2_init - initializes the nForce2 CPUFreq driver * * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported - * devices, -EINVAL on problems during initiatization, and zero on + * devices, -EINVAL on problems during initialization, and zero on * success. */ static int __init nforce2_init(void) -- cgit v0.10.2 From 431920edfd675ba74949415aace0a4eae07073e3 Mon Sep 17 00:00:00 2001 From: Shailendra Verma Date: Sat, 23 May 2015 10:36:49 +0530 Subject: cpufreq: gx-suspmod: Fix two typos in two comments Signed-off-by: Shailendra Verma Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c index 1d723dc..3488c9c 100644 --- a/drivers/cpufreq/gx-suspmod.c +++ b/drivers/cpufreq/gx-suspmod.c @@ -144,7 +144,7 @@ module_param(max_duration, int, 0444); /** - * we can detect a core multipiler from dir0_lsb + * we can detect a core multiplier from dir0_lsb * from GX1 datasheet p.56, * MULT[3:0]: * 0000 = SYSCLK multiplied by 4 (test only) @@ -346,7 +346,7 @@ static int cpufreq_gx_verify(struct cpufreq_policy *policy) /* it needs to be assured that at least one supported frequency is * within policy->min and policy->max. If it is not, policy->max - * needs to be increased until one freuqency is supported. + * needs to be increased until one frequency is supported. * policy->min may not be decreased, though. This way we guarantee a * specific processing capacity. */ -- cgit v0.10.2 From 8a95c1441c799bb0f0d31cdb11523d91923d51a7 Mon Sep 17 00:00:00 2001 From: Tang Yuantian Date: Thu, 4 Jun 2015 14:25:42 +0800 Subject: cpufreq: qoriq: optimize the CPU frequency switching time Each time the CPU switches its frequency, the clock nodes in DTS are walked through to find proper clock source. This is very time-consuming, for example, it is up to 500+ us on T4240. Besides, switching time varies from clock to clock. To optimize this, each input clock of CPU is buffered, so that it can be picked up instantly when needed. Since for each CPU each input clock is stored in a pointer which takes 4 or 8 bytes memory and normally there are several input clocks per CPU, that will not take much memory as well. Signed-off-by: Tang Yuantian Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/qoriq-cpufreq.c b/drivers/cpufreq/qoriq-cpufreq.c index 88b21ae..358f075 100644 --- a/drivers/cpufreq/qoriq-cpufreq.c +++ b/drivers/cpufreq/qoriq-cpufreq.c @@ -27,11 +27,11 @@ /** * struct cpu_data - * @parent: the parent node of cpu clock + * @pclk: the parent clock of cpu * @table: frequency table */ struct cpu_data { - struct device_node *parent; + struct clk **pclk; struct cpufreq_frequency_table *table; }; @@ -196,7 +196,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table, static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) { - struct device_node *np; + struct device_node *np, *pnode; int i, count, ret; u32 freq, mask; struct clk *clk; @@ -219,17 +219,23 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) goto err_nomem2; } - data->parent = of_parse_phandle(np, "clocks", 0); - if (!data->parent) { + pnode = of_parse_phandle(np, "clocks", 0); + if (!pnode) { pr_err("%s: could not get clock information\n", __func__); goto err_nomem2; } - count = of_property_count_strings(data->parent, "clock-names"); + count = of_property_count_strings(pnode, "clock-names"); + data->pclk = kcalloc(count, sizeof(struct clk *), GFP_KERNEL); + if (!data->pclk) { + pr_err("%s: no memory\n", __func__); + goto err_node; + } + table = kcalloc(count + 1, sizeof(*table), GFP_KERNEL); if (!table) { pr_err("%s: no memory\n", __func__); - goto err_node; + goto err_pclk; } if (fmask) @@ -238,7 +244,8 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) mask = 0x0; for (i = 0; i < count; i++) { - clk = of_clk_get(data->parent, i); + clk = of_clk_get(pnode, i); + data->pclk[i] = clk; freq = clk_get_rate(clk); /* * the clock is valid if its frequency is not masked @@ -273,13 +280,16 @@ static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = u64temp + 1; of_node_put(np); + of_node_put(pnode); return 0; err_nomem1: kfree(table); +err_pclk: + kfree(data->pclk); err_node: - of_node_put(data->parent); + of_node_put(pnode); err_nomem2: policy->driver_data = NULL; kfree(data); @@ -293,7 +303,7 @@ static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy) { struct cpu_data *data = policy->driver_data; - of_node_put(data->parent); + kfree(data->pclk); kfree(data->table); kfree(data); policy->driver_data = NULL; @@ -307,7 +317,7 @@ static int qoriq_cpufreq_target(struct cpufreq_policy *policy, struct clk *parent; struct cpu_data *data = policy->driver_data; - parent = of_clk_get(data->parent, data->table[index].driver_data); + parent = data->pclk[data->table[index].driver_data]; return clk_set_parent(policy->clk, parent); } -- cgit v0.10.2 From a4630c61274322eda7c4a4d17051f7c72a9e03b1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 29 May 2015 17:24:23 +0200 Subject: PM / Domains: Skip timings during syscore suspend/resume The PM Domain code uses ktime_get() to perform various latency measurements. However, if ktime_get() is called while timekeeping is suspended, the following warning is printed: WARNING: CPU: 0 PID: 1340 at kernel/time/timekeeping.c:576 ktime_get+0x3 This happens when resuming the PM Domain that contains the clock events source, which calls pm_genpd_syscore_poweron(). Chain of operations is: timekeeping_resume() { clockevents_resume() sh_cmt_clock_event_resume() pm_genpd_syscore_poweron() pm_genpd_sync_poweron() genpd_syscore_switch() genpd_power_on() ktime_get(), but timekeeping_suspended == 1 ... timekeeping_suspended = 0; } Fix this by adding a "timed" parameter to genpd_power_{on,off}() and pm_genpd_sync_power{off,on}(), to indicate whether latency measurements are allowed. This parameter is passed as false in genpd_syscore_switch() (i.e. during syscore suspend/resume), and true in all other cases. Signed-off-by: Geert Uytterhoeven Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 2327613..cdd547b 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -181,7 +181,7 @@ static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) genpd->cpuidle_data->idle_state->exit_latency = usecs64; } -static int genpd_power_on(struct generic_pm_domain *genpd) +static int genpd_power_on(struct generic_pm_domain *genpd, bool timed) { ktime_t time_start; s64 elapsed_ns; @@ -190,6 +190,9 @@ static int genpd_power_on(struct generic_pm_domain *genpd) if (!genpd->power_on) return 0; + if (!timed) + return genpd->power_on(genpd); + time_start = ktime_get(); ret = genpd->power_on(genpd); if (ret) @@ -208,7 +211,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd) return ret; } -static int genpd_power_off(struct generic_pm_domain *genpd) +static int genpd_power_off(struct generic_pm_domain *genpd, bool timed) { ktime_t time_start; s64 elapsed_ns; @@ -217,6 +220,9 @@ static int genpd_power_off(struct generic_pm_domain *genpd) if (!genpd->power_off) return 0; + if (!timed) + return genpd->power_off(genpd); + time_start = ktime_get(); ret = genpd->power_off(genpd); if (ret == -EBUSY) @@ -305,7 +311,7 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd) } } - ret = genpd_power_on(genpd); + ret = genpd_power_on(genpd, true); if (ret) goto err; @@ -615,7 +621,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) * the pm_genpd_poweron() restore power for us (this shouldn't * happen very often). */ - ret = genpd_power_off(genpd); + ret = genpd_power_off(genpd, true); if (ret == -EBUSY) { genpd_set_active(genpd); goto out; @@ -827,6 +833,7 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, /** * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. * @genpd: PM domain to power off, if possible. + * @timed: True if latency measurements are allowed. * * Check if the given PM domain can be powered off (during system suspend or * hibernation) and do that if so. Also, in that case propagate to its masters. @@ -836,7 +843,8 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, * executed sequentially, so it is guaranteed that it will never run twice in * parallel). */ -static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) +static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd, + bool timed) { struct gpd_link *link; @@ -847,26 +855,28 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd) || atomic_read(&genpd->sd_count) > 0) return; - genpd_power_off(genpd); + genpd_power_off(genpd, timed); genpd->status = GPD_STATE_POWER_OFF; list_for_each_entry(link, &genpd->slave_links, slave_node) { genpd_sd_counter_dec(link->master); - pm_genpd_sync_poweroff(link->master); + pm_genpd_sync_poweroff(link->master, timed); } } /** * pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. * @genpd: PM domain to power on. + * @timed: True if latency measurements are allowed. * * This function is only called in "noirq" and "syscore" stages of system power * transitions, so it need not acquire locks (all of the "noirq" callbacks are * executed sequentially, so it is guaranteed that it will never run twice in * parallel). */ -static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd) +static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd, + bool timed) { struct gpd_link *link; @@ -874,11 +884,11 @@ static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd) return; list_for_each_entry(link, &genpd->slave_links, slave_node) { - pm_genpd_sync_poweron(link->master); + pm_genpd_sync_poweron(link->master, timed); genpd_sd_counter_inc(link->master); } - genpd_power_on(genpd); + genpd_power_on(genpd, timed); genpd->status = GPD_STATE_ACTIVE; } @@ -1056,7 +1066,7 @@ static int pm_genpd_suspend_noirq(struct device *dev) * the same PM domain, so it is not necessary to use locking here. */ genpd->suspended_count++; - pm_genpd_sync_poweroff(genpd); + pm_genpd_sync_poweroff(genpd, true); return 0; } @@ -1086,7 +1096,7 @@ static int pm_genpd_resume_noirq(struct device *dev) * guaranteed that this function will never run twice in parallel for * the same PM domain, so it is not necessary to use locking here. */ - pm_genpd_sync_poweron(genpd); + pm_genpd_sync_poweron(genpd, true); genpd->suspended_count--; return genpd_start_dev(genpd, dev); @@ -1300,7 +1310,7 @@ static int pm_genpd_restore_noirq(struct device *dev) * If the domain was off before the hibernation, make * sure it will be off going forward. */ - genpd_power_off(genpd); + genpd_power_off(genpd, true); return 0; } @@ -1309,7 +1319,7 @@ static int pm_genpd_restore_noirq(struct device *dev) if (genpd->suspend_power_off) return 0; - pm_genpd_sync_poweron(genpd); + pm_genpd_sync_poweron(genpd, true); return genpd_start_dev(genpd, dev); } @@ -1367,9 +1377,9 @@ static void genpd_syscore_switch(struct device *dev, bool suspend) if (suspend) { genpd->suspended_count++; - pm_genpd_sync_poweroff(genpd); + pm_genpd_sync_poweroff(genpd, false); } else { - pm_genpd_sync_poweron(genpd); + pm_genpd_sync_poweron(genpd, false); genpd->suspended_count--; } } -- cgit v0.10.2 From f17f4adfe48b64b23247b792c0e5ab998b7ab34e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 29 May 2015 18:35:31 +0200 Subject: PM / clk: Print acquired clock name in addition to con_id Currently the con_id of the acquired clock is printed for debugging purposes. But in several cases, the con_id is NULL, which doesn't provide much debugging information when printed. These cases are: - When explicitly passing a NULL con_id (which means the first clock tied to the device, if available), - When not using pm_clk_add(), but pm_clk_add_clk() (which takes a "struct clk *" directly). Hence print the actual clock name in addition to (and not instead of; thanks Grygorii Strashko!) the con_id. Note that the clock name is not available with legacy clock frameworks, and the hex pointer address will be printed instead. Signed-off-by: Geert Uytterhoeven Reviewed-by: Grygorii Strashko Reviewed-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 442ce01..acef9f9 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -68,7 +68,8 @@ static void pm_clk_acquire(struct device *dev, struct pm_clock_entry *ce) } else { clk_prepare(ce->clk); ce->status = PCE_STATUS_ACQUIRED; - dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id); + dev_dbg(dev, "Clock %pC con_id %s managed by runtime PM.\n", + ce->clk, ce->con_id); } } -- cgit v0.10.2 From 7180dddf7c32c49975c7e7babf2b60ed450cb760 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Mon, 15 Jun 2015 13:43:29 -0400 Subject: intel_pstate: Fix overflow in busy_scaled due to long delay The kernel may delay interrupts for a long time which can result in timers being delayed. If this occurs the intel_pstate driver will crash with a divide by zero error: divide error: 0000 [#1] SMP Modules linked in: btrfs zlib_deflate raid6_pq xor msdos ext4 mbcache jbd2 binfmt_misc arc4 md4 nls_utf8 cifs dns_resolver tcp_lp bnep bluetooth rfkill fuse dm_service_time iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi nf_conntrack_netbios_ns nf_conntrack_broadcast nf_conntrack_ftp ip6t_rpfilter ip6t_REJECT ipt_REJECT xt_conntrack ebtable_nat ebtable_broute bridge stp llc ebtable_filter ebtables ip6table_nat nf_conntrack_ipv6 nf_defrag_ipv6 nf_nat_ipv6 ip6table_mangle ip6table_security ip6table_raw ip6table_filter ip6_tables iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_security iptable_raw iptable_filter ip_tables intel_powerclamp coretemp vfat fat kvm_intel iTCO_wdt iTCO_vendor_support ipmi_devintf sr_mod kvm crct10dif_pclmul crc32_pclmul crc32c_intel ghash_clmulni_intel aesni_intel cdc_ether lrw usbnet cdrom mii gf128mul glue_helper ablk_helper cryptd lpc_ich mfd_core pcspkr sb_edac edac_core ipmi_si ipmi_msghandler ioatdma wmi shpchp acpi_pad nfsd auth_rpcgss nfs_acl lockd uinput dm_multipath sunrpc xfs libcrc32c usb_storage sd_mod crc_t10dif crct10dif_common ixgbe mgag200 syscopyarea sysfillrect sysimgblt mdio drm_kms_helper ttm igb drm ptp pps_core dca i2c_algo_bit megaraid_sas i2c_core dm_mirror dm_region_hash dm_log dm_mod CPU: 113 PID: 0 Comm: swapper/113 Tainted: G W -------------- 3.10.0-229.1.2.el7.x86_64 #1 Hardware name: IBM x3950 X6 -[3837AC2]-/00FN827, BIOS -[A8E112BUS-1.00]- 08/27/2014 task: ffff880fe8abe660 ti: ffff880fe8ae4000 task.ti: ffff880fe8ae4000 RIP: 0010:[] [] intel_pstate_timer_func+0x179/0x3d0 RSP: 0018:ffff883fff4e3db8 EFLAGS: 00010206 RAX: 0000000027100000 RBX: ffff883fe6965100 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000010 RDI: 000000002e53632d RBP: ffff883fff4e3e20 R08: 000e6f69a5a125c0 R09: ffff883fe84ec001 R10: 0000000000000002 R11: 0000000000000005 R12: 00000000000049f5 R13: 0000000000271000 R14: 00000000000049f5 R15: 0000000000000246 FS: 0000000000000000(0000) GS:ffff883fff4e0000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f7668601000 CR3: 000000000190a000 CR4: 00000000001407e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Stack: ffff883fff4e3e58 ffffffff81099dc1 0000000000000086 0000000000000071 ffff883fff4f3680 0000000000000071 fbdc8a965e33afee ffffffff810b69dd ffff883fe84ec000 ffff883fe6965108 0000000000000100 ffffffff814a9100 Call Trace: [] ? run_posix_cpu_timers+0x51/0x840 [] ? trigger_load_balance+0x5d/0x200 [] ? pid_param_set+0x130/0x130 [] call_timer_fn+0x36/0x110 [] ? pid_param_set+0x130/0x130 [] run_timer_softirq+0x21f/0x320 [] __do_softirq+0xef/0x280 [] call_softirq+0x1c/0x30 [] do_softirq+0x65/0xa0 [] irq_exit+0x115/0x120 [] smp_apic_timer_interrupt+0x45/0x60 [] apic_timer_interrupt+0x6d/0x80 [] ? cpuidle_enter_state+0x52/0xc0 [] ? cpuidle_enter_state+0x48/0xc0 [] cpuidle_idle_call+0xc5/0x200 [] arch_cpu_idle+0xe/0x30 [] cpu_startup_entry+0xf1/0x290 [] start_secondary+0x1ba/0x230 Code: 42 0f 00 45 89 e6 48 01 c2 43 8d 44 6d 00 39 d0 73 26 49 c1 e5 08 89 d2 4d 63 f4 49 63 c5 48 c1 e2 08 48 c1 e0 08 48 63 ca 48 99 <48> f7 f9 48 98 4c 0f af f0 49 c1 ee 08 8b 43 78 c1 e0 08 44 29 RIP [] intel_pstate_timer_func+0x179/0x3d0 RSP The kernel values for cpudata for CPU 113 were: struct cpudata { cpu = 113, timer = { entry = { next = 0x0, prev = 0xdead000000200200 }, expires = 8357799745, base = 0xffff883fe84ec001, function = 0xffffffff814a9100 , data = 18446612406765768960, i_gain = 0, d_gain = 0, deadband = 0, last_err = 22489 }, last_sample_time = { tv64 = 4063132438017305 }, prev_aperf = 287326796397463, prev_mperf = 251427432090198, sample = { core_pct_busy = 23081, aperf = 2937407, mperf = 3257884, freq = 2524484, time = { tv64 = 4063149215234118 } } } which results in the time between samples = last_sample_time - sample.time = 4063149215234118 - 4063132438017305 = 16777216813 which is 16.777 seconds. The duration between reads of the APERF and MPERF registers overflowed a s32 sized integer in intel_pstate_get_scaled_busy()'s call to div_fp(). The result is that int_tofp(duration_us) == 0, and the kernel attempts to divide by 0. While the kernel shouldn't be delaying for a long time, it can and does happen and the intel_pstate driver should not panic in this situation. This patch changes the div_fp() function to use div64_s64() to allow for "long" division. This will avoid the overflow condition on long delays. [v2]: use div64_s64() in div_fp() Signed-off-by: Prarit Bhargava Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index aef3572..1da3197 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -48,9 +48,9 @@ static inline int32_t mul_fp(int32_t x, int32_t y) return ((int64_t)x * (int64_t)y) >> FRAC_BITS; } -static inline int32_t div_fp(int32_t x, int32_t y) +static inline int32_t div_fp(s64 x, s64 y) { - return div_s64((int64_t)x << FRAC_BITS, y); + return div64_s64((int64_t)x << FRAC_BITS, y); } static inline int ceiling_fp(int32_t x) @@ -802,7 +802,7 @@ static inline void intel_pstate_set_sample_time(struct cpudata *cpu) static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu) { int32_t core_busy, max_pstate, current_pstate, sample_ratio; - u32 duration_us; + s64 duration_us; u32 sample_time; /* @@ -829,8 +829,8 @@ static inline int32_t intel_pstate_get_scaled_busy(struct cpudata *cpu) * to adjust our busyness. */ sample_time = pid_params.sample_rate_ms * USEC_PER_MSEC; - duration_us = (u32) ktime_us_delta(cpu->sample.time, - cpu->last_sample_time); + duration_us = ktime_us_delta(cpu->sample.time, + cpu->last_sample_time); if (duration_us > sample_time * 3) { sample_ratio = div_fp(int_tofp(sample_time), int_tofp(duration_us)); -- cgit v0.10.2 From 07949bf9c63c9a80027fe8452d5fe8b9ba9b3c23 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 8 May 2015 14:57:30 -0500 Subject: cpufreq: dt: allow driver to boot automatically by adding the missing MODULE_ALIAS(), cpufreq-dt can be autoloaded by udev/systemd. Signed-off-by: Felipe Balbi Acked-by: Nishanth Menon Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index bab67db..528a82bf 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -416,6 +416,7 @@ static struct platform_driver dt_cpufreq_platdrv = { }; module_platform_driver(dt_cpufreq_platdrv); +MODULE_ALIAS("platform:cpufreq-dt"); MODULE_AUTHOR("Viresh Kumar "); MODULE_AUTHOR("Shawn Guo "); MODULE_DESCRIPTION("Generic cpufreq driver"); -- cgit v0.10.2 From 0f1b414d190724617eb1cdd615592fa8cd9d0b50 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 18 Jun 2015 18:32:02 +0200 Subject: ACPI / PNP: Avoid conflicting resource reservations Commit b9a5e5e18fbf "ACPI / init: Fix the ordering of acpi_reserve_resources()" overlooked the fact that the memory and/or I/O regions reserved by acpi_reserve_resources() may conflict with those reserved by the PNP "system" driver. If that conflict actually takes place, it causes the reservations made by the "system" driver to fail while before commit b9a5e5e18fbf all reservations made by it and by acpi_reserve_resources() would be successful. In turn, that allows the resources that haven't been reserved by the "system" driver to be used by others (e.g. PCI) which sometimes leads to functional problems (up to and including boot failures). To fix that issue, introduce a common resource reservation routine, acpi_reserve_region(), to be used by both acpi_reserve_resources() and the "system" driver, that will track all resources reserved by it and avoid making conflicting requests. Link: https://bugzilla.kernel.org/show_bug.cgi?id=99831 Link: http://marc.info/?t=143389402600001&r=1&w=2 Fixes: b9a5e5e18fbf "ACPI / init: Fix the ordering of acpi_reserve_resources()" Reported-by: Roland Dreier Cc: All applicable Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 7ccba39..5226a8b 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -175,11 +175,7 @@ static void __init acpi_request_region (struct acpi_generic_address *gas, if (!addr || !length) return; - /* Resources are never freed */ - if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) - request_region(addr, length, desc); - else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) - request_mem_region(addr, length, desc); + acpi_reserve_region(addr, length, gas->space_id, 0, desc); } static void __init acpi_reserve_resources(void) diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 8244f01..fcb7807 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #ifdef CONFIG_X86 @@ -621,3 +622,162 @@ int acpi_dev_filter_resource_type(struct acpi_resource *ares, return (type & types) ? 0 : 1; } EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); + +struct reserved_region { + struct list_head node; + u64 start; + u64 end; +}; + +static LIST_HEAD(reserved_io_regions); +static LIST_HEAD(reserved_mem_regions); + +static int request_range(u64 start, u64 end, u8 space_id, unsigned long flags, + char *desc) +{ + unsigned int length = end - start + 1; + struct resource *res; + + res = space_id == ACPI_ADR_SPACE_SYSTEM_IO ? + request_region(start, length, desc) : + request_mem_region(start, length, desc); + if (!res) + return -EIO; + + res->flags &= ~flags; + return 0; +} + +static int add_region_before(u64 start, u64 end, u8 space_id, + unsigned long flags, char *desc, + struct list_head *head) +{ + struct reserved_region *reg; + int error; + + reg = kmalloc(sizeof(*reg), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + error = request_range(start, end, space_id, flags, desc); + if (error) + return error; + + reg->start = start; + reg->end = end; + list_add_tail(®->node, head); + return 0; +} + +/** + * acpi_reserve_region - Reserve an I/O or memory region as a system resource. + * @start: Starting address of the region. + * @length: Length of the region. + * @space_id: Identifier of address space to reserve the region from. + * @flags: Resource flags to clear for the region after requesting it. + * @desc: Region description (for messages). + * + * Reserve an I/O or memory region as a system resource to prevent others from + * using it. If the new region overlaps with one of the regions (in the given + * address space) already reserved by this routine, only the non-overlapping + * parts of it will be reserved. + * + * Returned is either 0 (success) or a negative error code indicating a resource + * reservation problem. It is the code of the first encountered error, but the + * routine doesn't abort until it has attempted to request all of the parts of + * the new region that don't overlap with other regions reserved previously. + * + * The resources requested by this routine are never released. + */ +int acpi_reserve_region(u64 start, unsigned int length, u8 space_id, + unsigned long flags, char *desc) +{ + struct list_head *regions; + struct reserved_region *reg; + u64 end = start + length - 1; + int ret = 0, error = 0; + + if (space_id == ACPI_ADR_SPACE_SYSTEM_IO) + regions = &reserved_io_regions; + else if (space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) + regions = &reserved_mem_regions; + else + return -EINVAL; + + if (list_empty(regions)) + return add_region_before(start, end, space_id, flags, desc, regions); + + list_for_each_entry(reg, regions, node) + if (reg->start == end + 1) { + /* The new region can be prepended to this one. */ + ret = request_range(start, end, space_id, flags, desc); + if (!ret) + reg->start = start; + + return ret; + } else if (reg->start > end) { + /* No overlap. Add the new region here and get out. */ + return add_region_before(start, end, space_id, flags, + desc, ®->node); + } else if (reg->end == start - 1) { + goto combine; + } else if (reg->end >= start) { + goto overlap; + } + + /* The new region goes after the last existing one. */ + return add_region_before(start, end, space_id, flags, desc, regions); + + overlap: + /* + * The new region overlaps an existing one. + * + * The head part of the new region immediately preceding the existing + * overlapping one can be combined with it right away. + */ + if (reg->start > start) { + error = request_range(start, reg->start - 1, space_id, flags, desc); + if (error) + ret = error; + else + reg->start = start; + } + + combine: + /* + * The new region is adjacent to an existing one. If it extends beyond + * that region all the way to the next one, it is possible to combine + * all three of them. + */ + while (reg->end < end) { + struct reserved_region *next = NULL; + u64 a = reg->end + 1, b = end; + + if (!list_is_last(®->node, regions)) { + next = list_next_entry(reg, node); + if (next->start <= end) + b = next->start - 1; + } + error = request_range(a, b, space_id, flags, desc); + if (!error) { + if (next && next->start == b + 1) { + reg->end = next->end; + list_del(&next->node); + kfree(next); + } else { + reg->end = end; + break; + } + } else if (next) { + if (!ret) + ret = error; + + reg = next; + } else { + break; + } + } + + return ret ? ret : error; +} +EXPORT_SYMBOL_GPL(acpi_reserve_region); diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c index 49c1720..515f338 100644 --- a/drivers/pnp/system.c +++ b/drivers/pnp/system.c @@ -7,6 +7,7 @@ * Bjorn Helgaas */ +#include #include #include #include @@ -22,25 +23,41 @@ static const struct pnp_device_id pnp_dev_table[] = { {"", 0} }; +#ifdef CONFIG_ACPI +static bool __reserve_range(u64 start, unsigned int length, bool io, char *desc) +{ + u8 space_id = io ? ACPI_ADR_SPACE_SYSTEM_IO : ACPI_ADR_SPACE_SYSTEM_MEMORY; + return !acpi_reserve_region(start, length, space_id, IORESOURCE_BUSY, desc); +} +#else +static bool __reserve_range(u64 start, unsigned int length, bool io, char *desc) +{ + struct resource *res; + + res = io ? request_region(start, length, desc) : + request_mem_region(start, length, desc); + if (res) { + res->flags &= ~IORESOURCE_BUSY; + return true; + } + return false; +} +#endif + static void reserve_range(struct pnp_dev *dev, struct resource *r, int port) { char *regionid; const char *pnpid = dev_name(&dev->dev); resource_size_t start = r->start, end = r->end; - struct resource *res; + bool reserved; regionid = kmalloc(16, GFP_KERNEL); if (!regionid) return; snprintf(regionid, 16, "pnp %s", pnpid); - if (port) - res = request_region(start, end - start + 1, regionid); - else - res = request_mem_region(start, end - start + 1, regionid); - if (res) - res->flags &= ~IORESOURCE_BUSY; - else + reserved = __reserve_range(start, end - start + 1, !!port, regionid); + if (!reserved) kfree(regionid); /* @@ -49,7 +66,7 @@ static void reserve_range(struct pnp_dev *dev, struct resource *r, int port) * have double reservations. */ dev_info(&dev->dev, "%pR %s reserved\n", r, - res ? "has been" : "could not be"); + reserved ? "has been" : "could not be"); } static void reserve_resources_of_dev(struct pnp_dev *dev) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e4da5e3..299763d 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -332,6 +332,9 @@ int acpi_check_region(resource_size_t start, resource_size_t n, int acpi_resources_are_enforced(void); +int acpi_reserve_region(u64 start, unsigned int length, u8 space_id, + unsigned long flags, char *desc); + #ifdef CONFIG_HIBERNATION void __init acpi_no_s4_hw_signature(void); #endif @@ -525,6 +528,13 @@ static inline int acpi_check_region(resource_size_t start, resource_size_t n, return 0; } +static inline int acpi_reserve_region(u64 start, unsigned int length, + u8 space_id, unsigned long flags, + char *desc) +{ + return -ENXIO; +} + struct acpi_table_header; static inline int acpi_table_parse(char *id, int (*handler)(struct acpi_table_header *)) -- cgit v0.10.2 From 10bffa65b99fb1747103e0337f47da55885929be Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:39 +0200 Subject: apple_gmux: Use acpi_video_unregister_backlight instead of acpi_video_unregister acpi_video_unregister() not only unregisters the acpi-video backlight interface but also unregisters the acpi video bus event listener, causing e.g. brightness hotkey presses to no longer generate keypress events. The unregistering of the acpi video bus event listener usually is undesirable, which by itself is a good reason to switch to acpi_video_unregister_backlight(). Another problem with using acpi_video_unregister() rather then using acpi_video_unregister_backlight() is that on systems with an intel video opregion (most systems) whether or not the acpi video bus event listener actually gets unregistered depends on module load ordering: Scenario a: 1) acpi/video.ko gets loaded (*), does not do acpi_video_register as there is an intel opregion. 2) intel.ko gets loaded, calls acpi_video_register() which registers both the listener and the acpi backlight interface 3) apple-gmux.ko gets loaded, calls acpi_video_unregister() causing both the listener and the acpi backlight interface to unregister Scenario b: 1) acpi/video.ko gets loaded (*), does not do acpi_video_register as there is an intel opregion. 2) apple-gmux.ko gets loaded, calls acpi_video_dmi_promote_vendor(), calls acpi_video_unregister(), which is a nop since acpi_video_register has not yet been called 2) intel.ko gets loaded, calls acpi_video_register() which registers the listener, but does not register the acpi backlight interface due to the call to the preciding call to acpi_video_dmi_promote_vendor() *) acpi/video.ko always loads first as both other modules depend on it. So we end up with or without an acpi video bus event listener depending on module load ordering, not good. Switching to using acpi_video_unregister_backlight() means that independ of ordering we will always have an acpi video bus event listener fixing this. Note that this commit means that systems without an intel video opregion, and systems which were hitting scenario a wrt module load ordering, are now getting an acpi video bus event listener while before they were not! On some systems this may cause the brightness hotkeys to start generating keypresses while before they were not (good), while on other systems this may cause the brightness hotkeys to generate multiple keypress events for a single press (not so good). Since on most systems the acpi video bus is the canonical source for brightness events I believe that the latter case will needs to be handled on a case by case basis by filtering out the duplicate keypresses at the other source for them. Cc: Seth Forshee Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 6808715..45032ce 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -551,7 +551,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) * Disable the other backlight choices. */ acpi_video_dmi_promote_vendor(); - acpi_video_unregister(); + acpi_video_unregister_backlight(); apple_bl_unregister(); gmux_data->power_state = VGA_SWITCHEROO_ON; -- cgit v0.10.2 From 4c27febf8e51857e8981be060a31e1c5c0f9a2e7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:40 +0200 Subject: asus-wmi: Use acpi_video_unregister_backlight instead of acpi_video_unregister acpi_video_unregister() not only unregisters the acpi-video backlight interface but also unregisters the acpi video bus event listener, causing e.g. brightness hotkey presses to no longer generate keypress events. The unregistering of the acpi video bus event listener usually is undesirable, which by itself is a good reason to switch to acpi_video_unregister_backlight(). Another problem with using acpi_video_unregister() rather then using acpi_video_unregister_backlight() is that on systems with an intel video opregion (most systems) and a wmi_backlight_power quirk, whether or not the acpi video bus event listener actually gets unregistered depends on module load ordering: Scenario a: 1) acpi/video.ko gets loaded (*), does not do acpi_video_register as there is an intel opregion. 2) intel.ko gets loaded, calls acpi_video_register() which registers both the listener and the acpi backlight interface 3) asus-wmi.ko gets loaded, calls acpi_video_unregister() causing both the listener and the acpi backlight interface to unregister Scenario b: 1) acpi/video.ko gets loaded (*), does not do acpi_video_register as there is an intel opregion. 2) asus-wmi.ko gets loaded, calls acpi_video_dmi_promote_vendor(), calls acpi_video_unregister(), which is a nop since acpi_video_register has not yet been called 2) intel.ko gets loaded, calls acpi_video_register() which registers the listener, but does not register the acpi backlight interface due to the call to the preciding call to acpi_video_dmi_promote_vendor() *) acpi/video.ko always loads first as both other modules depend on it. So we end up with or without an acpi video bus event listener depending on module load ordering, not good. Switching to using acpi_video_unregister_backlight() means that independ of ordering we will always have an acpi video bus event listener fixing this. Note that this commit means that systems without an intel video opregion, and systems which were hitting scenario a wrt module load ordering, are now getting an acpi video bus event listener while before they were not! On some systems this may cause the brightness hotkeys to start generating keypresses while before they were not (good), while on other systems this may cause the brightness hotkeys to generate multiple keypress events for a single press (not so good). Since on most systems the acpi video bus is the canonical source for brightness events I believe that the latter case will needs to be handled on a case by case basis by filtering out the duplicate keypresses at the other source for them. Cc: Corentin Chary Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 7543a56..945145d 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1777,7 +1777,7 @@ static int asus_wmi_add(struct platform_device *pdev) acpi_video_dmi_promote_vendor(); if (!acpi_video_backlight_support()) { pr_info("Disabling ACPI video driver\n"); - acpi_video_unregister(); + acpi_video_unregister_backlight(); err = asus_wmi_backlight_init(asus); if (err && err != -ENODEV) goto fail_backlight; -- cgit v0.10.2 From acf5493c3c36553ada7bc9b6f9569b0a12f9e552 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:41 +0200 Subject: samsung-laptop: Use acpi_video_unregister_backlight instead of acpi_video_unregister acpi_video_unregister() not only unregisters the acpi-video backlight interface but also unregisters the acpi video bus event listener, causing e.g. brightness hotkey presses to no longer generate keypress events. The unregistering of the acpi video bus event listener usually is undesirable, which by itself is a good reason to switch to acpi_video_unregister_backlight(). Another problem with using acpi_video_unregister() rather then using acpi_video_unregister_backlight() is that on systems with an intel video opregion (most systems) and a broken_acpi_video quirk, whether or not the acpi video bus event listener actually gets unregistered depends on module load ordering: Scenario a: 1) acpi/video.ko gets loaded (*), does not do acpi_video_register as there is an intel opregion. 2) intel.ko gets loaded, calls acpi_video_register() which registers both the listener and the acpi backlight interface 3) samsung-laptop.ko gets loaded, calls acpi_video_unregister() causing both the listener and the acpi backlight interface to unregister Scenario b: 1) acpi/video.ko gets loaded (*), does not do acpi_video_register as there is an intel opregion. 2) samsung-laptop.ko gets loaded, calls acpi_video_dmi_promote_vendor(), calls acpi_video_unregister(), which is a nop since acpi_video_register has not yet been called 2) intel.ko gets loaded, calls acpi_video_register() which registers the listener, but does not register the acpi backlight interface due to the call to the preciding call to acpi_video_dmi_promote_vendor() *) acpi/video.ko always loads first as both other modules depend on it. So we end up with or without an acpi video bus event listener depending on module load ordering, not good. Switching to using acpi_video_unregister_backlight() means that independ of ordering we will always have an acpi video bus event listener fixing this. Note that this commit means that systems without an intel video opregion, and systems which were hitting scenario a wrt module load ordering, are now getting an acpi video bus event listener while before they were not! On some systems this may cause the brightness hotkeys to start generating keypresses while before they were not (good), while on other systems this may cause the brightness hotkeys to generate multiple keypress events for a single press (not so good). Since on most systems the acpi video bus is the canonical source for brightness events I believe that the latter case will needs to be handled on a case by case basis by filtering out the duplicate keypresses at the other source for them. Cc: Corentin Chary Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 9e701b2..0df03e2 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -1730,14 +1730,14 @@ static int __init samsung_init(void) samsung->handle_backlight = false; } else if (samsung->quirks->broken_acpi_video) { pr_info("Disabling ACPI video driver\n"); - acpi_video_unregister(); + acpi_video_unregister_backlight(); } if (samsung->quirks->use_native_backlight) { pr_info("Using native backlight driver\n"); /* Tell acpi-video to not handle the backlight */ acpi_video_dmi_promote_vendor(); - acpi_video_unregister(); + acpi_video_unregister_backlight(); /* And also do not handle it ourselves */ samsung->handle_backlight = false; } -- cgit v0.10.2 From a341b8aba13c39fc76aea29fe13ea851df313bd6 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:42 +0200 Subject: apple-gmux: Stop using acpi_video_dmi_demote_vendor() acpi_video_dmi_demote_vendor() is going away as part of the cleanup of the code for determinging which backlight class driver(s) to register. The call to acpi_video_dmi_demote_vendor() was meant to undo the call to acpi_video_dmi_promote_vendor() when the gmux device is removed, this is questionable though since the promote call sets a flag, not a counter, so the demote call may undo a promoto done elsewhere. Moreover in practice this is a nop since the gmux device is never removed, and the flag is only checked when acpi/video.ko gets loaded, so even if the user manually removes apple-gmux the demote call is still a nop as video.ko will already have loaded by this time. Also note that none of the other users of acpi_video_dmi_promote_vendor() use acpi_video_dmi_demote_vendor(). If we ever encounter a system with a gmux where the acpi-video interface should be used, then the proper fix would be to dmi-blacklist the gmux driver on that system. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 45032ce..a7f6412 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -645,7 +645,6 @@ static void gmux_remove(struct pnp_dev *pnp) apple_gmux_data = NULL; kfree(gmux_data); - acpi_video_dmi_demote_vendor(); acpi_video_register(); apple_bl_register(); } -- cgit v0.10.2 From 7f3b62cf945dc3e57fbd693022a5651206ce85b0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:43 +0200 Subject: acpi-video-detect: Remove the unused acpi_video_dmi_demote_vendor() function Remove the now unused acpi_video_dmi_demote_vendor() function, this was never a proper counter part of acpi_video_dmi_promote_vendor() since the calls to acpi_video_dmi_promote_vendor() are not counted. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index be82580..bb78c9e 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -258,15 +258,6 @@ void acpi_video_dmi_promote_vendor(void) } EXPORT_SYMBOL(acpi_video_dmi_promote_vendor); -/* To be called when a driver who previously promoted the vendor - * interface */ -void acpi_video_dmi_demote_vendor(void) -{ - acpi_video_caps_check(); - acpi_video_support &= ~ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; -} -EXPORT_SYMBOL(acpi_video_dmi_demote_vendor); - /* Returns true if video.ko can do backlight switching */ int acpi_video_backlight_support(void) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e4da5e3..01bffd3 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -248,7 +248,6 @@ extern bool wmi_has_guid(const char *guid); extern long acpi_video_get_capabilities(acpi_handle graphics_dev_handle); extern long acpi_is_video_device(acpi_handle handle); extern void acpi_video_dmi_promote_vendor(void); -extern void acpi_video_dmi_demote_vendor(void); extern int acpi_video_backlight_support(void); extern int acpi_video_display_switch_support(void); @@ -268,10 +267,6 @@ static inline void acpi_video_dmi_promote_vendor(void) { } -static inline void acpi_video_dmi_demote_vendor(void) -{ -} - static inline int acpi_video_backlight_support(void) { return 0; -- cgit v0.10.2 From fb105d964226ce4834b45d7e3d9f339aa716ed70 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:44 +0200 Subject: acpi-video-detect: Make acpi_video_get_capabilities a private function acpi_video_get_capabilities() is only used inside video_detect.c so make it static. While at it also remove the prototype for the non existent acpi_video_display_switch_support function from acpi.h Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index bb78c9e..3af18be 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -184,7 +184,7 @@ static const struct dmi_system_id video_detect_dmi_table[] = { * all graphics capabilities of physically present devices are * summarized and returned. This is cached and done only once. */ -long acpi_video_get_capabilities(acpi_handle graphics_handle) +static long acpi_video_get_capabilities(acpi_handle graphics_handle) { long caps = 0; struct acpi_device *tmp_dev; @@ -227,7 +227,6 @@ long acpi_video_get_capabilities(acpi_handle graphics_handle) graphics_handle ? acpi_device_bid(tmp_dev) : "")); return caps; } -EXPORT_SYMBOL(acpi_video_get_capabilities); static void acpi_video_caps_check(void) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 01bffd3..88c92a03 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -245,19 +245,12 @@ extern bool wmi_has_guid(const char *guid); #if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) -extern long acpi_video_get_capabilities(acpi_handle graphics_dev_handle); extern long acpi_is_video_device(acpi_handle handle); extern void acpi_video_dmi_promote_vendor(void); extern int acpi_video_backlight_support(void); -extern int acpi_video_display_switch_support(void); #else -static inline long acpi_video_get_capabilities(acpi_handle graphics_dev_handle) -{ - return 0; -} - static inline long acpi_is_video_device(acpi_handle handle) { return 0; @@ -272,11 +265,6 @@ static inline int acpi_video_backlight_support(void) return 0; } -static inline int acpi_video_display_switch_support(void) -{ - return 0; -} - #endif /* defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) */ extern int acpi_blacklisted(void); -- cgit v0.10.2 From adc8bb8e0fe005ed29366e6c4621652481878214 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:45 +0200 Subject: acpi-video-detect: Move acpi_is_video_device() to acpi/scan.c This allows video_detect.c to be build as a module, this is a preparation patch for the backlight interface selection logic cleanup. Note this commit also causes acpi_is_video_device() to always be build indepedent of CONFIG_ACPI_VIDEO, as there is no reason to make its building depend on CONFIG_ACPI_VIDEO. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 0a09991..aa997c6 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1949,6 +1949,62 @@ bool acpi_dock_match(acpi_handle handle) return acpi_has_method(handle, "_DCK"); } +static acpi_status +acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context, + void **return_value) +{ + long *cap = context; + + if (acpi_has_method(handle, "_BCM") && + acpi_has_method(handle, "_BCL")) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight " + "support\n")); + *cap |= ACPI_VIDEO_BACKLIGHT; + if (!acpi_has_method(handle, "_BQC")) + printk(KERN_WARNING FW_BUG PREFIX "No _BQC method, " + "cannot determine initial brightness\n"); + /* We have backlight support, no need to scan further */ + return AE_CTRL_TERMINATE; + } + return 0; +} + +/* Returns true if the ACPI object is a video device which can be + * handled by video.ko. + * The device will get a Linux specific CID added in scan.c to + * identify the device as an ACPI graphics device + * Be aware that the graphics device may not be physically present + * Use acpi_video_get_capabilities() to detect general ACPI video + * capabilities of present cards + */ +long acpi_is_video_device(acpi_handle handle) +{ + long video_caps = 0; + + /* Is this device able to support video switching ? */ + if (acpi_has_method(handle, "_DOD") || acpi_has_method(handle, "_DOS")) + video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; + + /* Is this device able to retrieve a video ROM ? */ + if (acpi_has_method(handle, "_ROM")) + video_caps |= ACPI_VIDEO_ROM_AVAILABLE; + + /* Is this device able to configure which video head to be POSTed ? */ + if (acpi_has_method(handle, "_VPO") && + acpi_has_method(handle, "_GPD") && + acpi_has_method(handle, "_SPD")) + video_caps |= ACPI_VIDEO_DEVICE_POSTING; + + /* Only check for backlight functionality if one of the above hit. */ + if (video_caps) + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, + ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL, + &video_caps, NULL); + + return video_caps; +} +EXPORT_SYMBOL(acpi_is_video_device); + const char *acpi_device_hid(struct acpi_device *device) { struct acpi_hardware_id *hid; diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 3af18be..5076138 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -5,10 +5,6 @@ * May be copied or modified under the terms of the GNU General Public License * * video_detect.c: - * Provides acpi_is_video_device() for early scanning of ACPI devices in scan.c - * There a Linux specific (Spec does not provide a HID for video devices) is - * assigned - * * After PCI devices are glued with ACPI devices * acpi_get_pci_dev() can be called to identify ACPI graphics * devices for which a real graphics card is plugged in @@ -47,62 +43,6 @@ static long acpi_video_support; static bool acpi_video_caps_checked; static acpi_status -acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context, - void **return_value) -{ - long *cap = context; - - if (acpi_has_method(handle, "_BCM") && - acpi_has_method(handle, "_BCL")) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight " - "support\n")); - *cap |= ACPI_VIDEO_BACKLIGHT; - if (!acpi_has_method(handle, "_BQC")) - printk(KERN_WARNING FW_BUG PREFIX "No _BQC method, " - "cannot determine initial brightness\n"); - /* We have backlight support, no need to scan further */ - return AE_CTRL_TERMINATE; - } - return 0; -} - -/* Returns true if the ACPI object is a video device which can be - * handled by video.ko. - * The device will get a Linux specific CID added in scan.c to - * identify the device as an ACPI graphics device - * Be aware that the graphics device may not be physically present - * Use acpi_video_get_capabilities() to detect general ACPI video - * capabilities of present cards - */ -long acpi_is_video_device(acpi_handle handle) -{ - long video_caps = 0; - - /* Is this device able to support video switching ? */ - if (acpi_has_method(handle, "_DOD") || acpi_has_method(handle, "_DOS")) - video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; - - /* Is this device able to retrieve a video ROM ? */ - if (acpi_has_method(handle, "_ROM")) - video_caps |= ACPI_VIDEO_ROM_AVAILABLE; - - /* Is this device able to configure which video head to be POSTed ? */ - if (acpi_has_method(handle, "_VPO") && - acpi_has_method(handle, "_GPD") && - acpi_has_method(handle, "_SPD")) - video_caps |= ACPI_VIDEO_DEVICE_POSTING; - - /* Only check for backlight functionality if one of the above hit. */ - if (video_caps) - acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, - ACPI_UINT32_MAX, acpi_backlight_cap_match, NULL, - &video_caps, NULL); - - return video_caps; -} -EXPORT_SYMBOL(acpi_is_video_device); - -static acpi_status find_video(acpi_handle handle, u32 lvl, void *context, void **rv) { long *cap = context; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 88c92a03..7cb3b0b 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -243,19 +243,15 @@ extern bool wmi_has_guid(const char *guid); #define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR 0x0400 #define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO 0x0800 +extern long acpi_is_video_device(acpi_handle handle); + #if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) -extern long acpi_is_video_device(acpi_handle handle); extern void acpi_video_dmi_promote_vendor(void); extern int acpi_video_backlight_support(void); #else -static inline long acpi_is_video_device(acpi_handle handle) -{ - return 0; -} - static inline void acpi_video_dmi_promote_vendor(void) { } -- cgit v0.10.2 From a87878bafa1f82c20eddaf2d23780b194c35ccf5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:46 +0200 Subject: acpi-video-detect: Move acpi_osi_is_win8 to osl.c acpi_osi_is_win8 needs access to acpi_gbl_osi_data which is not exported, so move it to osl.c. Alternatively we could export acpi_gbl_osi_data but that seems undesirable. This allows video_detect.c to be build as a module, besides that acpi_osi_is_win8() is something which does not really belong in video_detect.c in the first place. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index d93628c..a637497 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -182,13 +182,6 @@ static inline void suspend_nvs_restore(void) {} #endif /*-------------------------------------------------------------------------- - Video - -------------------------------------------------------------------------- */ -#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) -bool acpi_osi_is_win8(void); -#endif - -/*-------------------------------------------------------------------------- Device properties -------------------------------------------------------------------------- */ #define ACPI_DT_NAMESPACE_HID "PRP0001" diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 7ccba39..b906deb 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1684,6 +1684,12 @@ int acpi_resources_are_enforced(void) } EXPORT_SYMBOL(acpi_resources_are_enforced); +bool acpi_osi_is_win8(void) +{ + return acpi_gbl_osi_data >= ACPI_OSI_WIN_8; +} +EXPORT_SYMBOL(acpi_osi_is_win8); + /* * Deallocate the memory for a spinlock. */ diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 5076138..b2270ca 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -178,12 +178,6 @@ static void acpi_video_caps_check(void) acpi_video_get_capabilities(NULL); } -bool acpi_osi_is_win8(void) -{ - return acpi_gbl_osi_data >= ACPI_OSI_WIN_8; -} -EXPORT_SYMBOL(acpi_osi_is_win8); - /* Promote the vendor interface instead of the generic video module. * This function allow DMI blacklists to be implemented by externals * platform drivers instead of putting a big blacklist in video_detect.c diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7cb3b0b..913a1c1 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -266,6 +266,7 @@ static inline int acpi_video_backlight_support(void) extern int acpi_blacklisted(void); extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d); extern void acpi_osi_setup(char *str); +extern bool acpi_osi_is_win8(void); #ifdef CONFIG_ACPI_NUMA int acpi_get_node(acpi_handle handle); -- cgit v0.10.2 From 14ca7a47d0ab2a7a35faab130e6d9682f8ff1a46 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:47 +0200 Subject: acpi-video-detect: video: Make video_detect code part of the video module This is a preparation patch for the backlight interface selection logic cleanup, there are 2 reasons to not always build the video_detect code into the kernel: 1) In order for the video_detect.c to also deal with / select native backlight interfaces on win8 systems, instead of doing this in video.c where it does not belong, video_detect.c needs to call into the backlight class code. Which cannot be done if it is builtin and the blacklight class is not. 2) Currently all the platform/x86 drivers which have quirks to prefer the vendor driver over acpi-video call acpi_video_unregister_backlight() to remove the acpi-video backlight interface, this logic really belongs in video_detect.c, which will cause video_detect.c to depend on symbols of video.c and video.c already depends on video_detect.c symbols, so they really need to be a single module. Note that this commits make 2 changes so as to maintain 100% kernel commandline compatibility: 1) The __setup call for the acpi_backlight= handling is moved to acpi/util.c as __setup may only be used by code which is alwasy builtin 2) video.c is renamed to acpi_video.c so that it can be combined with video_detect.c into video.ko This commit also makes changes to drivers/platform/x86/Kconfig to ensure that drivers which use acpi_video_backlight_support() from video_detect.c, will not be built-in when acpi_video is not built in. This also changes some "select" uses to "depends on" to avoid dependency loops. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 8a063e2..73d840b 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -52,9 +52,6 @@ acpi-$(CONFIG_X86) += acpi_cmos_rtc.o acpi-$(CONFIG_DEBUG_FS) += debugfs.o acpi-$(CONFIG_ACPI_NUMA) += numa.o acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o -ifdef CONFIG_ACPI_VIDEO -acpi-y += video_detect.o -endif acpi-y += acpi_lpat.o acpi-$(CONFIG_ACPI_GENERIC_GSI) += gsi.o @@ -95,3 +92,5 @@ 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 + +video-objs += acpi_video.o video_detect.o diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c new file mode 100644 index 0000000..db4f353 --- /dev/null +++ b/drivers/acpi/acpi_video.c @@ -0,0 +1,2275 @@ +/* + * video.c - ACPI Video Driver + * + * Copyright (C) 2004 Luming Yu + * Copyright (C) 2004 Bruno Ducrot + * Copyright (C) 2006 Thomas Tuttle + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PREFIX "ACPI: " + +#define ACPI_VIDEO_BUS_NAME "Video Bus" +#define ACPI_VIDEO_DEVICE_NAME "Video Device" +#define ACPI_VIDEO_NOTIFY_SWITCH 0x80 +#define ACPI_VIDEO_NOTIFY_PROBE 0x81 +#define ACPI_VIDEO_NOTIFY_CYCLE 0x82 +#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83 +#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84 + +#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x85 +#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 +#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 +#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x88 +#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x89 + +#define MAX_NAME_LEN 20 + +#define _COMPONENT ACPI_VIDEO_COMPONENT +ACPI_MODULE_NAME("video"); + +MODULE_AUTHOR("Bruno Ducrot"); +MODULE_DESCRIPTION("ACPI Video Driver"); +MODULE_LICENSE("GPL"); + +static bool brightness_switch_enabled = 1; +module_param(brightness_switch_enabled, bool, 0644); + +/* + * By default, we don't allow duplicate ACPI video bus devices + * under the same VGA controller + */ +static bool allow_duplicates; +module_param(allow_duplicates, bool, 0644); + +/* + * For Windows 8 systems: used to decide if video module + * should skip registering backlight interface of its own. + */ +enum { + NATIVE_BACKLIGHT_NOT_SET = -1, + NATIVE_BACKLIGHT_OFF, + NATIVE_BACKLIGHT_ON, +}; + +static int use_native_backlight_param = NATIVE_BACKLIGHT_NOT_SET; +module_param_named(use_native_backlight, use_native_backlight_param, int, 0444); +static int use_native_backlight_dmi = NATIVE_BACKLIGHT_NOT_SET; + +static int disable_backlight_sysfs_if = -1; +module_param(disable_backlight_sysfs_if, int, 0444); + +static int register_count; +static struct mutex video_list_lock; +static struct list_head video_bus_head; +static int acpi_video_bus_add(struct acpi_device *device); +static int acpi_video_bus_remove(struct acpi_device *device); +static void acpi_video_bus_notify(struct acpi_device *device, u32 event); + +static const struct acpi_device_id video_device_ids[] = { + {ACPI_VIDEO_HID, 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, video_device_ids); + +static struct acpi_driver acpi_video_bus = { + .name = "video", + .class = ACPI_VIDEO_CLASS, + .ids = video_device_ids, + .ops = { + .add = acpi_video_bus_add, + .remove = acpi_video_bus_remove, + .notify = acpi_video_bus_notify, + }, +}; + +struct acpi_video_bus_flags { + u8 multihead:1; /* can switch video heads */ + u8 rom:1; /* can retrieve a video rom */ + u8 post:1; /* can configure the head to */ + u8 reserved:5; +}; + +struct acpi_video_bus_cap { + u8 _DOS:1; /* Enable/Disable output switching */ + u8 _DOD:1; /* Enumerate all devices attached to display adapter */ + u8 _ROM:1; /* Get ROM Data */ + u8 _GPD:1; /* Get POST Device */ + u8 _SPD:1; /* Set POST Device */ + u8 _VPO:1; /* Video POST Options */ + u8 reserved:2; +}; + +struct acpi_video_device_attrib { + u32 display_index:4; /* A zero-based instance of the Display */ + u32 display_port_attachment:4; /* This field differentiates the display type */ + u32 display_type:4; /* Describe the specific type in use */ + u32 vendor_specific:4; /* Chipset Vendor Specific */ + u32 bios_can_detect:1; /* BIOS can detect the device */ + u32 depend_on_vga:1; /* Non-VGA output device whose power is related to + the VGA device. */ + u32 pipe_id:3; /* For VGA multiple-head devices. */ + u32 reserved:10; /* Must be 0 */ + u32 device_id_scheme:1; /* Device ID Scheme */ +}; + +struct acpi_video_enumerated_device { + union { + u32 int_val; + struct acpi_video_device_attrib attrib; + } value; + struct acpi_video_device *bind_info; +}; + +struct acpi_video_bus { + struct acpi_device *device; + bool backlight_registered; + bool backlight_notifier_registered; + u8 dos_setting; + struct acpi_video_enumerated_device *attached_array; + u8 attached_count; + u8 child_count; + struct acpi_video_bus_cap cap; + struct acpi_video_bus_flags flags; + struct list_head video_device_list; + struct mutex device_list_lock; /* protects video_device_list */ + struct list_head entry; + struct input_dev *input; + char phys[32]; /* for input device */ + struct notifier_block pm_nb; + struct notifier_block backlight_nb; +}; + +struct acpi_video_device_flags { + u8 crt:1; + u8 lcd:1; + u8 tvout:1; + u8 dvi:1; + u8 bios:1; + u8 unknown:1; + u8 notify:1; + u8 reserved:1; +}; + +struct acpi_video_device_cap { + u8 _ADR:1; /* Return the unique ID */ + u8 _BCL:1; /* Query list of brightness control levels supported */ + u8 _BCM:1; /* Set the brightness level */ + u8 _BQC:1; /* Get current brightness level */ + u8 _BCQ:1; /* Some buggy BIOS uses _BCQ instead of _BQC */ + u8 _DDC:1; /* Return the EDID for this device */ +}; + +struct acpi_video_brightness_flags { + u8 _BCL_no_ac_battery_levels:1; /* no AC/Battery levels in _BCL */ + u8 _BCL_reversed:1; /* _BCL package is in a reversed order */ + u8 _BQC_use_index:1; /* _BQC returns an index value */ +}; + +struct acpi_video_device_brightness { + int curr; + int count; + int *levels; + struct acpi_video_brightness_flags flags; +}; + +struct acpi_video_device { + unsigned long device_id; + struct acpi_video_device_flags flags; + struct acpi_video_device_cap cap; + struct list_head entry; + struct delayed_work switch_brightness_work; + int switch_brightness_event; + struct acpi_video_bus *video; + struct acpi_device *dev; + struct acpi_video_device_brightness *brightness; + struct backlight_device *backlight; + struct thermal_cooling_device *cooling_dev; +}; + +static const char device_decode[][30] = { + "motherboard VGA device", + "PCI VGA device", + "AGP VGA device", + "UNKNOWN", +}; + +static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data); +static void acpi_video_device_rebind(struct acpi_video_bus *video); +static void acpi_video_device_bind(struct acpi_video_bus *video, + struct acpi_video_device *device); +static int acpi_video_device_enumerate(struct acpi_video_bus *video); +static int acpi_video_device_lcd_set_level(struct acpi_video_device *device, + int level); +static int acpi_video_device_lcd_get_level_current( + struct acpi_video_device *device, + unsigned long long *level, bool raw); +static int acpi_video_get_next_level(struct acpi_video_device *device, + u32 level_current, u32 event); +static void acpi_video_switch_brightness(struct work_struct *work); + +static bool acpi_video_use_native_backlight(void) +{ + if (use_native_backlight_param != NATIVE_BACKLIGHT_NOT_SET) + return use_native_backlight_param; + else if (use_native_backlight_dmi != NATIVE_BACKLIGHT_NOT_SET) + return use_native_backlight_dmi; + return acpi_osi_is_win8(); +} + +bool acpi_video_verify_backlight_support(void) +{ + if (acpi_video_use_native_backlight() && + backlight_device_registered(BACKLIGHT_RAW)) + return false; + return acpi_video_backlight_support(); +} +EXPORT_SYMBOL_GPL(acpi_video_verify_backlight_support); + +/* backlight device sysfs support */ +static int acpi_video_get_brightness(struct backlight_device *bd) +{ + unsigned long long cur_level; + int i; + struct acpi_video_device *vd = bl_get_data(bd); + + if (acpi_video_device_lcd_get_level_current(vd, &cur_level, false)) + return -EINVAL; + for (i = 2; i < vd->brightness->count; i++) { + if (vd->brightness->levels[i] == cur_level) + /* + * The first two entries are special - see page 575 + * of the ACPI spec 3.0 + */ + return i - 2; + } + return 0; +} + +static int acpi_video_set_brightness(struct backlight_device *bd) +{ + int request_level = bd->props.brightness + 2; + struct acpi_video_device *vd = bl_get_data(bd); + + cancel_delayed_work(&vd->switch_brightness_work); + return acpi_video_device_lcd_set_level(vd, + vd->brightness->levels[request_level]); +} + +static const struct backlight_ops acpi_backlight_ops = { + .get_brightness = acpi_video_get_brightness, + .update_status = acpi_video_set_brightness, +}; + +/* thermal cooling device callbacks */ +static int video_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned + long *state) +{ + struct acpi_device *device = cooling_dev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + + *state = video->brightness->count - 3; + return 0; +} + +static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsigned + long *state) +{ + struct acpi_device *device = cooling_dev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + unsigned long long level; + int offset; + + if (acpi_video_device_lcd_get_level_current(video, &level, false)) + return -EINVAL; + for (offset = 2; offset < video->brightness->count; offset++) + if (level == video->brightness->levels[offset]) { + *state = video->brightness->count - offset - 1; + return 0; + } + + return -EINVAL; +} + +static int +video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state) +{ + struct acpi_device *device = cooling_dev->devdata; + struct acpi_video_device *video = acpi_driver_data(device); + int level; + + if (state >= video->brightness->count - 2) + return -EINVAL; + + state = video->brightness->count - state; + level = video->brightness->levels[state - 1]; + return acpi_video_device_lcd_set_level(video, level); +} + +static const struct thermal_cooling_device_ops video_cooling_ops = { + .get_max_state = video_get_max_state, + .get_cur_state = video_get_cur_state, + .set_cur_state = video_set_cur_state, +}; + +/* + * -------------------------------------------------------------------------- + * Video Management + * -------------------------------------------------------------------------- + */ + +static int +acpi_video_device_lcd_query_levels(struct acpi_video_device *device, + union acpi_object **levels) +{ + int status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + + *levels = NULL; + + status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer); + if (!ACPI_SUCCESS(status)) + return status; + obj = (union acpi_object *)buffer.pointer; + if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { + printk(KERN_ERR PREFIX "Invalid _BCL data\n"); + status = -EFAULT; + goto err; + } + + *levels = obj; + + return 0; + +err: + kfree(buffer.pointer); + + return status; +} + +static int +acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) +{ + int status; + int state; + + status = acpi_execute_simple_method(device->dev->handle, + "_BCM", level); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Evaluating _BCM failed")); + return -EIO; + } + + device->brightness->curr = level; + for (state = 2; state < device->brightness->count; state++) + if (level == device->brightness->levels[state]) { + if (device->backlight) + device->backlight->props.brightness = state - 2; + return 0; + } + + ACPI_ERROR((AE_INFO, "Current brightness invalid")); + return -EINVAL; +} + +/* + * For some buggy _BQC methods, we need to add a constant value to + * the _BQC return value to get the actual current brightness level + */ + +static int bqc_offset_aml_bug_workaround; +static int __init video_set_bqc_offset(const struct dmi_system_id *d) +{ + bqc_offset_aml_bug_workaround = 9; + return 0; +} + +static int __init video_disable_native_backlight(const struct dmi_system_id *d) +{ + use_native_backlight_dmi = NATIVE_BACKLIGHT_OFF; + return 0; +} + +static int __init video_enable_native_backlight(const struct dmi_system_id *d) +{ + use_native_backlight_dmi = NATIVE_BACKLIGHT_ON; + return 0; +} + +static int __init video_disable_backlight_sysfs_if( + const struct dmi_system_id *d) +{ + if (disable_backlight_sysfs_if == -1) + disable_backlight_sysfs_if = 1; + return 0; +} + +static struct dmi_system_id video_dmi_table[] __initdata = { + /* + * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 + */ + { + .callback = video_set_bqc_offset, + .ident = "Acer Aspire 5720", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), + }, + }, + { + .callback = video_set_bqc_offset, + .ident = "Acer Aspire 5710Z", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"), + }, + }, + { + .callback = video_set_bqc_offset, + .ident = "eMachines E510", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"), + DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"), + }, + }, + { + .callback = video_set_bqc_offset, + .ident = "Acer Aspire 5315", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"), + }, + }, + { + .callback = video_set_bqc_offset, + .ident = "Acer Aspire 7720", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"), + }, + }, + + /* + * These models have a working acpi_video backlight control, and using + * native backlight causes a regression where backlight does not work + * when userspace is not handling brightness key events. Disable + * native_backlight on these to fix this: + * https://bugzilla.kernel.org/show_bug.cgi?id=81691 + */ + { + .callback = video_disable_native_backlight, + .ident = "ThinkPad T420", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"), + }, + }, + { + .callback = video_disable_native_backlight, + .ident = "ThinkPad T520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"), + }, + }, + { + .callback = video_disable_native_backlight, + .ident = "ThinkPad X201s", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"), + }, + }, + + /* The native backlight controls do not work on some older machines */ + { + /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */ + .callback = video_disable_native_backlight, + .ident = "HP ENVY 15 Notebook", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"), + }, + }, + + { + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"), + }, + }, + { + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "370R4E/370R4V/370R5E/3570RE/370R5V"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */ + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "3570R/370R/470R/450R/510R/4450RV"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */ + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 730U3E/740U3E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), + }, + }, + { + /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */ + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "900X3C/900X3D/900X3E/900X4C/900X4D"), + }, + }, + + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */ + .callback = video_disable_native_backlight, + .ident = "Dell XPS15 L521X", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"), + }, + }, + + /* Non win8 machines which need native backlight nevertheless */ + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ + .callback = video_enable_native_backlight, + .ident = "Lenovo Ideapad Z570", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "102434U"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */ + .callback = video_enable_native_backlight, + .ident = "Apple MacBook Pro 12,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"), + }, + }, + + /* + * Some machines have a broken acpi-video interface for brightness + * control, but still need an acpi_video_device_lcd_set_level() call + * on resume to turn the backlight power on. We Enable backlight + * control on these systems, but do not register a backlight sysfs + * as brightness control does not work. + */ + { + /* https://bugs.freedesktop.org/show_bug.cgi?id=82634 */ + .callback = video_disable_backlight_sysfs_if, + .ident = "Toshiba Portege R830", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R830"), + }, + }, + {} +}; + +static unsigned long long +acpi_video_bqc_value_to_level(struct acpi_video_device *device, + unsigned long long bqc_value) +{ + unsigned long long level; + + if (device->brightness->flags._BQC_use_index) { + /* + * _BQC returns an index that doesn't account for + * the first 2 items with special meaning, so we need + * to compensate for that by offsetting ourselves + */ + if (device->brightness->flags._BCL_reversed) + bqc_value = device->brightness->count - 3 - bqc_value; + + level = device->brightness->levels[bqc_value + 2]; + } else { + level = bqc_value; + } + + level += bqc_offset_aml_bug_workaround; + + return level; +} + +static int +acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, + unsigned long long *level, bool raw) +{ + acpi_status status = AE_OK; + int i; + + if (device->cap._BQC || device->cap._BCQ) { + char *buf = device->cap._BQC ? "_BQC" : "_BCQ"; + + status = acpi_evaluate_integer(device->dev->handle, buf, + NULL, level); + if (ACPI_SUCCESS(status)) { + if (raw) { + /* + * Caller has indicated he wants the raw + * value returned by _BQC, so don't furtherly + * mess with the value. + */ + return 0; + } + + *level = acpi_video_bqc_value_to_level(device, *level); + + for (i = 2; i < device->brightness->count; i++) + if (device->brightness->levels[i] == *level) { + device->brightness->curr = *level; + return 0; + } + /* + * BQC returned an invalid level. + * Stop using it. + */ + ACPI_WARNING((AE_INFO, + "%s returned an invalid level", + buf)); + device->cap._BQC = device->cap._BCQ = 0; + } else { + /* + * Fixme: + * should we return an error or ignore this failure? + * dev->brightness->curr is a cached value which stores + * the correct current backlight level in most cases. + * ACPI video backlight still works w/ buggy _BQC. + * http://bugzilla.kernel.org/show_bug.cgi?id=12233 + */ + ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf)); + device->cap._BQC = device->cap._BCQ = 0; + } + } + + *level = device->brightness->curr; + return 0; +} + +static int +acpi_video_device_EDID(struct acpi_video_device *device, + union acpi_object **edid, ssize_t length) +{ + int status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + + + *edid = NULL; + + if (!device) + return -ENODEV; + if (length == 128) + arg0.integer.value = 1; + else if (length == 256) + arg0.integer.value = 2; + else + return -EINVAL; + + status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; + + obj = buffer.pointer; + + if (obj && obj->type == ACPI_TYPE_BUFFER) + *edid = obj; + else { + printk(KERN_ERR PREFIX "Invalid _DDC data\n"); + status = -EFAULT; + kfree(obj); + } + + return status; +} + +/* bus */ + +/* + * Arg: + * video : video bus device pointer + * bios_flag : + * 0. The system BIOS should NOT automatically switch(toggle) + * the active display output. + * 1. The system BIOS should automatically switch (toggle) the + * active display output. No switch event. + * 2. The _DGS value should be locked. + * 3. The system BIOS should not automatically switch (toggle) the + * active display output, but instead generate the display switch + * event notify code. + * lcd_flag : + * 0. The system BIOS should automatically control the brightness level + * of the LCD when the power changes from AC to DC + * 1. The system BIOS should NOT automatically control the brightness + * level of the LCD when the power changes from AC to DC. + * Return Value: + * -EINVAL wrong arg. + */ + +static int +acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) +{ + acpi_status status; + + if (!video->cap._DOS) + return 0; + + if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) + return -EINVAL; + video->dos_setting = (lcd_flag << 2) | bios_flag; + status = acpi_execute_simple_method(video->device->handle, "_DOS", + (lcd_flag << 2) | bios_flag); + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; +} + +/* + * Simple comparison function used to sort backlight levels. + */ + +static int +acpi_video_cmp_level(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + +/* + * Decides if _BQC/_BCQ for this system is usable + * + * We do this by changing the level first and then read out the current + * brightness level, if the value does not match, find out if it is using + * index. If not, clear the _BQC/_BCQ capability. + */ +static int acpi_video_bqc_quirk(struct acpi_video_device *device, + int max_level, int current_level) +{ + struct acpi_video_device_brightness *br = device->brightness; + int result; + unsigned long long level; + int test_level; + + /* don't mess with existing known broken systems */ + if (bqc_offset_aml_bug_workaround) + return 0; + + /* + * Some systems always report current brightness level as maximum + * through _BQC, we need to test another value for them. + */ + test_level = current_level == max_level ? br->levels[3] : max_level; + + result = acpi_video_device_lcd_set_level(device, test_level); + if (result) + return result; + + result = acpi_video_device_lcd_get_level_current(device, &level, true); + if (result) + return result; + + if (level != test_level) { + /* buggy _BQC found, need to find out if it uses index */ + if (level < br->count) { + if (br->flags._BCL_reversed) + level = br->count - 3 - level; + if (br->levels[level + 2] == test_level) + br->flags._BQC_use_index = 1; + } + + if (!br->flags._BQC_use_index) + device->cap._BQC = device->cap._BCQ = 0; + } + + return 0; +} + + +/* + * Arg: + * device : video output device (LCD, CRT, ..) + * + * Return Value: + * Maximum brightness level + * + * Allocate and initialize device->brightness. + */ + +static int +acpi_video_init_brightness(struct acpi_video_device *device) +{ + union acpi_object *obj = NULL; + int i, max_level = 0, count = 0, level_ac_battery = 0; + unsigned long long level, level_old; + union acpi_object *o; + struct acpi_video_device_brightness *br = NULL; + int result = -EINVAL; + u32 value; + + if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available " + "LCD brightness level\n")); + goto out; + } + + if (obj->package.count < 2) + goto out; + + br = kzalloc(sizeof(*br), GFP_KERNEL); + if (!br) { + printk(KERN_ERR "can't allocate memory\n"); + result = -ENOMEM; + goto out; + } + + br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels), + GFP_KERNEL); + if (!br->levels) { + result = -ENOMEM; + goto out_free; + } + + for (i = 0; i < obj->package.count; i++) { + o = (union acpi_object *)&obj->package.elements[i]; + if (o->type != ACPI_TYPE_INTEGER) { + printk(KERN_ERR PREFIX "Invalid data\n"); + continue; + } + value = (u32) o->integer.value; + /* Skip duplicate entries */ + if (count > 2 && br->levels[count - 1] == value) + continue; + + br->levels[count] = value; + + if (br->levels[count] > max_level) + max_level = br->levels[count]; + count++; + } + + /* + * some buggy BIOS don't export the levels + * when machine is on AC/Battery in _BCL package. + * In this case, the first two elements in _BCL packages + * are also supported brightness levels that OS should take care of. + */ + for (i = 2; i < count; i++) { + if (br->levels[i] == br->levels[0]) + level_ac_battery++; + if (br->levels[i] == br->levels[1]) + level_ac_battery++; + } + + if (level_ac_battery < 2) { + level_ac_battery = 2 - level_ac_battery; + br->flags._BCL_no_ac_battery_levels = 1; + for (i = (count - 1 + level_ac_battery); i >= 2; i--) + br->levels[i] = br->levels[i - level_ac_battery]; + count += level_ac_battery; + } else if (level_ac_battery > 2) + ACPI_ERROR((AE_INFO, "Too many duplicates in _BCL package")); + + /* Check if the _BCL package is in a reversed order */ + if (max_level == br->levels[2]) { + br->flags._BCL_reversed = 1; + sort(&br->levels[2], count - 2, sizeof(br->levels[2]), + acpi_video_cmp_level, NULL); + } else if (max_level != br->levels[count - 1]) + ACPI_ERROR((AE_INFO, + "Found unordered _BCL package")); + + br->count = count; + device->brightness = br; + + /* _BQC uses INDEX while _BCL uses VALUE in some laptops */ + br->curr = level = max_level; + + if (!device->cap._BQC) + goto set_level; + + result = acpi_video_device_lcd_get_level_current(device, + &level_old, true); + if (result) + goto out_free_levels; + + result = acpi_video_bqc_quirk(device, max_level, level_old); + if (result) + goto out_free_levels; + /* + * cap._BQC may get cleared due to _BQC is found to be broken + * in acpi_video_bqc_quirk, so check again here. + */ + if (!device->cap._BQC) + goto set_level; + + level = acpi_video_bqc_value_to_level(device, level_old); + /* + * On some buggy laptops, _BQC returns an uninitialized + * value when invoked for the first time, i.e. + * level_old is invalid (no matter whether it's a level + * or an index). Set the backlight to max_level in this case. + */ + for (i = 2; i < br->count; i++) + if (level == br->levels[i]) + break; + if (i == br->count || !level) + level = max_level; + +set_level: + result = acpi_video_device_lcd_set_level(device, level); + if (result) + goto out_free_levels; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "found %d brightness levels\n", count - 2)); + kfree(obj); + return result; + +out_free_levels: + kfree(br->levels); +out_free: + kfree(br); +out: + device->brightness = NULL; + kfree(obj); + return result; +} + +/* + * Arg: + * device : video output device (LCD, CRT, ..) + * + * Return Value: + * None + * + * Find out all required AML methods defined under the output + * device. + */ + +static void acpi_video_device_find_cap(struct acpi_video_device *device) +{ + if (acpi_has_method(device->dev->handle, "_ADR")) + device->cap._ADR = 1; + if (acpi_has_method(device->dev->handle, "_BCL")) + device->cap._BCL = 1; + if (acpi_has_method(device->dev->handle, "_BCM")) + device->cap._BCM = 1; + if (acpi_has_method(device->dev->handle, "_BQC")) { + device->cap._BQC = 1; + } else if (acpi_has_method(device->dev->handle, "_BCQ")) { + printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n"); + device->cap._BCQ = 1; + } + + if (acpi_has_method(device->dev->handle, "_DDC")) + device->cap._DDC = 1; +} + +/* + * Arg: + * device : video output device (VGA) + * + * Return Value: + * None + * + * Find out all required AML methods defined under the video bus device. + */ + +static void acpi_video_bus_find_cap(struct acpi_video_bus *video) +{ + if (acpi_has_method(video->device->handle, "_DOS")) + video->cap._DOS = 1; + if (acpi_has_method(video->device->handle, "_DOD")) + video->cap._DOD = 1; + if (acpi_has_method(video->device->handle, "_ROM")) + video->cap._ROM = 1; + if (acpi_has_method(video->device->handle, "_GPD")) + video->cap._GPD = 1; + if (acpi_has_method(video->device->handle, "_SPD")) + video->cap._SPD = 1; + if (acpi_has_method(video->device->handle, "_VPO")) + video->cap._VPO = 1; +} + +/* + * Check whether the video bus device has required AML method to + * support the desired features + */ + +static int acpi_video_bus_check(struct acpi_video_bus *video) +{ + acpi_status status = -ENOENT; + struct pci_dev *dev; + + if (!video) + return -EINVAL; + + dev = acpi_get_pci_dev(video->device->handle); + if (!dev) + return -ENODEV; + pci_dev_put(dev); + + /* + * Since there is no HID, CID and so on for VGA driver, we have + * to check well known required nodes. + */ + + /* Does this device support video switching? */ + if (video->cap._DOS || video->cap._DOD) { + if (!video->cap._DOS) { + printk(KERN_WARNING FW_BUG + "ACPI(%s) defines _DOD but not _DOS\n", + acpi_device_bid(video->device)); + } + video->flags.multihead = 1; + status = 0; + } + + /* Does this device support retrieving a video ROM? */ + if (video->cap._ROM) { + video->flags.rom = 1; + status = 0; + } + + /* Does this device support configuring which video device to POST? */ + if (video->cap._GPD && video->cap._SPD && video->cap._VPO) { + video->flags.post = 1; + status = 0; + } + + return status; +} + +/* + * -------------------------------------------------------------------------- + * Driver Interface + * -------------------------------------------------------------------------- + */ + +/* device interface */ +static struct acpi_video_device_attrib * +acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id) +{ + struct acpi_video_enumerated_device *ids; + int i; + + for (i = 0; i < video->attached_count; i++) { + ids = &video->attached_array[i]; + if ((ids->value.int_val & 0xffff) == device_id) + return &ids->value.attrib; + } + + return NULL; +} + +static int +acpi_video_get_device_type(struct acpi_video_bus *video, + unsigned long device_id) +{ + struct acpi_video_enumerated_device *ids; + int i; + + for (i = 0; i < video->attached_count; i++) { + ids = &video->attached_array[i]; + if ((ids->value.int_val & 0xffff) == device_id) + return ids->value.int_val; + } + + return 0; +} + +static int +acpi_video_bus_get_one_device(struct acpi_device *device, + struct acpi_video_bus *video) +{ + unsigned long long device_id; + int status, device_type; + struct acpi_video_device *data; + struct acpi_video_device_attrib *attribute; + + status = + acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); + /* Some device omits _ADR, we skip them instead of fail */ + if (ACPI_FAILURE(status)) + return 0; + + data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); + if (!data) + return -ENOMEM; + + strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); + device->driver_data = data; + + data->device_id = device_id; + data->video = video; + data->dev = device; + INIT_DELAYED_WORK(&data->switch_brightness_work, + acpi_video_switch_brightness); + + attribute = acpi_video_get_device_attr(video, device_id); + + if (attribute && attribute->device_id_scheme) { + switch (attribute->display_type) { + case ACPI_VIDEO_DISPLAY_CRT: + data->flags.crt = 1; + break; + case ACPI_VIDEO_DISPLAY_TV: + data->flags.tvout = 1; + break; + case ACPI_VIDEO_DISPLAY_DVI: + data->flags.dvi = 1; + break; + case ACPI_VIDEO_DISPLAY_LCD: + data->flags.lcd = 1; + break; + default: + data->flags.unknown = 1; + break; + } + if (attribute->bios_can_detect) + data->flags.bios = 1; + } else { + /* Check for legacy IDs */ + device_type = acpi_video_get_device_type(video, device_id); + /* Ignore bits 16 and 18-20 */ + switch (device_type & 0xffe2ffff) { + case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR: + data->flags.crt = 1; + break; + case ACPI_VIDEO_DISPLAY_LEGACY_PANEL: + data->flags.lcd = 1; + break; + case ACPI_VIDEO_DISPLAY_LEGACY_TV: + data->flags.tvout = 1; + break; + default: + data->flags.unknown = 1; + } + } + + acpi_video_device_bind(video, data); + acpi_video_device_find_cap(data); + + mutex_lock(&video->device_list_lock); + list_add_tail(&data->entry, &video->video_device_list); + mutex_unlock(&video->device_list_lock); + + return status; +} + +/* + * Arg: + * video : video bus device + * + * Return: + * none + * + * Enumerate the video device list of the video bus, + * bind the ids with the corresponding video devices + * under the video bus. + */ + +static void acpi_video_device_rebind(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev; + + mutex_lock(&video->device_list_lock); + + list_for_each_entry(dev, &video->video_device_list, entry) + acpi_video_device_bind(video, dev); + + mutex_unlock(&video->device_list_lock); +} + +/* + * Arg: + * video : video bus device + * device : video output device under the video + * bus + * + * Return: + * none + * + * Bind the ids with the corresponding video devices + * under the video bus. + */ + +static void +acpi_video_device_bind(struct acpi_video_bus *video, + struct acpi_video_device *device) +{ + struct acpi_video_enumerated_device *ids; + int i; + + for (i = 0; i < video->attached_count; i++) { + ids = &video->attached_array[i]; + if (device->device_id == (ids->value.int_val & 0xffff)) { + ids->bind_info = device; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i)); + } + } +} + +static bool acpi_video_device_in_dod(struct acpi_video_device *device) +{ + struct acpi_video_bus *video = device->video; + int i; + + /* + * If we have a broken _DOD or we have more than 8 output devices + * under the graphics controller node that we can't proper deal with + * in the operation region code currently, no need to test. + */ + if (!video->attached_count || video->child_count > 8) + return true; + + for (i = 0; i < video->attached_count; i++) { + if ((video->attached_array[i].value.int_val & 0xfff) == + (device->device_id & 0xfff)) + return true; + } + + return false; +} + +/* + * Arg: + * video : video bus device + * + * Return: + * < 0 : error + * + * Call _DOD to enumerate all devices attached to display adapter + * + */ + +static int acpi_video_device_enumerate(struct acpi_video_bus *video) +{ + int status; + int count; + int i; + struct acpi_video_enumerated_device *active_list; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *dod = NULL; + union acpi_object *obj; + + status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer); + if (!ACPI_SUCCESS(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD")); + return status; + } + + dod = buffer.pointer; + if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) { + ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data")); + status = -EFAULT; + goto out; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n", + dod->package.count)); + + active_list = kcalloc(1 + dod->package.count, + sizeof(struct acpi_video_enumerated_device), + GFP_KERNEL); + if (!active_list) { + status = -ENOMEM; + goto out; + } + + count = 0; + for (i = 0; i < dod->package.count; i++) { + obj = &dod->package.elements[i]; + + if (obj->type != ACPI_TYPE_INTEGER) { + printk(KERN_ERR PREFIX + "Invalid _DOD data in element %d\n", i); + continue; + } + + active_list[count].value.int_val = obj->integer.value; + active_list[count].bind_info = NULL; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i, + (int)obj->integer.value)); + count++; + } + + kfree(video->attached_array); + + video->attached_array = active_list; + video->attached_count = count; + +out: + kfree(buffer.pointer); + return status; +} + +static int +acpi_video_get_next_level(struct acpi_video_device *device, + u32 level_current, u32 event) +{ + int min, max, min_above, max_below, i, l, delta = 255; + max = max_below = 0; + min = min_above = 255; + /* Find closest level to level_current */ + for (i = 2; i < device->brightness->count; i++) { + l = device->brightness->levels[i]; + if (abs(l - level_current) < abs(delta)) { + delta = l - level_current; + if (!delta) + break; + } + } + /* Ajust level_current to closest available level */ + level_current += delta; + for (i = 2; i < device->brightness->count; i++) { + l = device->brightness->levels[i]; + if (l < min) + min = l; + if (l > max) + max = l; + if (l < min_above && l > level_current) + min_above = l; + if (l > max_below && l < level_current) + max_below = l; + } + + switch (event) { + case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: + return (level_current < max) ? min_above : min; + case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: + return (level_current < max) ? min_above : max; + case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: + return (level_current > min) ? max_below : min; + case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: + case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: + return 0; + default: + return level_current; + } +} + +static void +acpi_video_switch_brightness(struct work_struct *work) +{ + struct acpi_video_device *device = container_of(to_delayed_work(work), + struct acpi_video_device, switch_brightness_work); + unsigned long long level_current, level_next; + int event = device->switch_brightness_event; + int result = -EINVAL; + + /* no warning message if acpi_backlight=vendor or a quirk is used */ + if (!device->backlight) + return; + + if (!device->brightness) + goto out; + + result = acpi_video_device_lcd_get_level_current(device, + &level_current, + false); + if (result) + goto out; + + level_next = acpi_video_get_next_level(device, level_current, event); + + result = acpi_video_device_lcd_set_level(device, level_next); + + if (!result) + backlight_force_update(device->backlight, + BACKLIGHT_UPDATE_HOTKEY); + +out: + if (result) + printk(KERN_ERR PREFIX "Failed to switch the brightness\n"); +} + +int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, + void **edid) +{ + struct acpi_video_bus *video; + struct acpi_video_device *video_device; + union acpi_object *buffer = NULL; + acpi_status status; + int i, length; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + video = acpi_driver_data(device); + + for (i = 0; i < video->attached_count; i++) { + video_device = video->attached_array[i].bind_info; + length = 256; + + if (!video_device) + continue; + + if (!video_device->cap._DDC) + continue; + + if (type) { + switch (type) { + case ACPI_VIDEO_DISPLAY_CRT: + if (!video_device->flags.crt) + continue; + break; + case ACPI_VIDEO_DISPLAY_TV: + if (!video_device->flags.tvout) + continue; + break; + case ACPI_VIDEO_DISPLAY_DVI: + if (!video_device->flags.dvi) + continue; + break; + case ACPI_VIDEO_DISPLAY_LCD: + if (!video_device->flags.lcd) + continue; + break; + } + } else if (video_device->device_id != device_id) { + continue; + } + + status = acpi_video_device_EDID(video_device, &buffer, length); + + if (ACPI_FAILURE(status) || !buffer || + buffer->type != ACPI_TYPE_BUFFER) { + length = 128; + status = acpi_video_device_EDID(video_device, &buffer, + length); + if (ACPI_FAILURE(status) || !buffer || + buffer->type != ACPI_TYPE_BUFFER) { + continue; + } + } + + *edid = buffer->buffer.pointer; + return length; + } + + return -ENODEV; +} +EXPORT_SYMBOL(acpi_video_get_edid); + +static int +acpi_video_bus_get_devices(struct acpi_video_bus *video, + struct acpi_device *device) +{ + int status = 0; + struct acpi_device *dev; + + /* + * There are systems where video module known to work fine regardless + * of broken _DOD and ignoring returned value here doesn't cause + * any issues later. + */ + acpi_video_device_enumerate(video); + + list_for_each_entry(dev, &device->children, node) { + + status = acpi_video_bus_get_one_device(dev, video); + if (status) { + dev_err(&dev->dev, "Can't attach device\n"); + break; + } + video->child_count++; + } + return status; +} + +/* acpi_video interface */ + +/* + * Win8 requires setting bit2 of _DOS to let firmware know it shouldn't + * preform any automatic brightness change on receiving a notification. + */ +static int acpi_video_bus_start_devices(struct acpi_video_bus *video) +{ + return acpi_video_bus_DOS(video, 0, + acpi_osi_is_win8() ? 1 : 0); +} + +static int acpi_video_bus_stop_devices(struct acpi_video_bus *video) +{ + return acpi_video_bus_DOS(video, 0, + acpi_osi_is_win8() ? 0 : 1); +} + +static void acpi_video_bus_notify(struct acpi_device *device, u32 event) +{ + struct acpi_video_bus *video = acpi_driver_data(device); + struct input_dev *input; + int keycode = 0; + + if (!video || !video->input) + return; + + input = video->input; + + switch (event) { + case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch, + * most likely via hotkey. */ + keycode = KEY_SWITCHVIDEOMODE; + break; + + case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video + * connector. */ + acpi_video_device_enumerate(video); + acpi_video_device_rebind(video); + keycode = KEY_SWITCHVIDEOMODE; + break; + + case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */ + keycode = KEY_SWITCHVIDEOMODE; + break; + case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */ + keycode = KEY_VIDEO_NEXT; + break; + case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */ + keycode = KEY_VIDEO_PREV; + break; + + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + if (acpi_notifier_call_chain(device, event, 0)) + /* Something vetoed the keypress. */ + keycode = 0; + + if (keycode) { + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + input_sync(input); + } + + return; +} + +static void brightness_switch_event(struct acpi_video_device *video_device, + u32 event) +{ + if (!brightness_switch_enabled) + return; + + video_device->switch_brightness_event = event; + schedule_delayed_work(&video_device->switch_brightness_work, HZ / 10); +} + +static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) +{ + struct acpi_video_device *video_device = data; + struct acpi_device *device = NULL; + struct acpi_video_bus *bus; + struct input_dev *input; + int keycode = 0; + + if (!video_device) + return; + + device = video_device->dev; + bus = video_device->video; + input = bus->input; + + switch (event) { + case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ + brightness_switch_event(video_device, event); + keycode = KEY_BRIGHTNESS_CYCLE; + break; + case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */ + brightness_switch_event(video_device, event); + keycode = KEY_BRIGHTNESSUP; + break; + case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */ + brightness_switch_event(video_device, event); + keycode = KEY_BRIGHTNESSDOWN; + break; + case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightness */ + brightness_switch_event(video_device, event); + keycode = KEY_BRIGHTNESS_ZERO; + break; + case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */ + brightness_switch_event(video_device, event); + keycode = KEY_DISPLAY_OFF; + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + acpi_notifier_call_chain(device, event, 0); + + if (keycode) { + input_report_key(input, keycode, 1); + input_sync(input); + input_report_key(input, keycode, 0); + input_sync(input); + } + + return; +} + +static int acpi_video_resume(struct notifier_block *nb, + unsigned long val, void *ign) +{ + struct acpi_video_bus *video; + struct acpi_video_device *video_device; + int i; + + switch (val) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + case PM_RESTORE_PREPARE: + return NOTIFY_DONE; + } + + video = container_of(nb, struct acpi_video_bus, pm_nb); + + dev_info(&video->device->dev, "Restoring backlight state\n"); + + for (i = 0; i < video->attached_count; i++) { + video_device = video->attached_array[i].bind_info; + if (video_device && video_device->brightness) + acpi_video_device_lcd_set_level(video_device, + video_device->brightness->curr); + } + + return NOTIFY_OK; +} + +static acpi_status +acpi_video_bus_match(acpi_handle handle, u32 level, void *context, + void **return_value) +{ + struct acpi_device *device = context; + struct acpi_device *sibling; + int result; + + if (handle == device->handle) + return AE_CTRL_TERMINATE; + + result = acpi_bus_get_device(handle, &sibling); + if (result) + return AE_OK; + + if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) + return AE_ALREADY_EXISTS; + + return AE_OK; +} + +static void acpi_video_dev_register_backlight(struct acpi_video_device *device) +{ + struct backlight_properties props; + struct pci_dev *pdev; + acpi_handle acpi_parent; + struct device *parent = NULL; + int result; + static int count; + char *name; + + /* + * Do not create backlight device for video output + * device that is not in the enumerated list. + */ + if (!acpi_video_device_in_dod(device)) { + dev_dbg(&device->dev->dev, "not in _DOD list, ignore\n"); + return; + } + + result = acpi_video_init_brightness(device); + if (result) + return; + + if (disable_backlight_sysfs_if > 0) + return; + + name = kasprintf(GFP_KERNEL, "acpi_video%d", count); + if (!name) + return; + count++; + + acpi_get_parent(device->dev->handle, &acpi_parent); + + pdev = acpi_get_pci_dev(acpi_parent); + if (pdev) { + parent = &pdev->dev; + pci_dev_put(pdev); + } + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_FIRMWARE; + props.max_brightness = device->brightness->count - 3; + device->backlight = backlight_device_register(name, + parent, + device, + &acpi_backlight_ops, + &props); + kfree(name); + if (IS_ERR(device->backlight)) { + device->backlight = NULL; + return; + } + + /* + * Save current brightness level in case we have to restore it + * before acpi_video_device_lcd_set_level() is called next time. + */ + device->backlight->props.brightness = + acpi_video_get_brightness(device->backlight); + + device->cooling_dev = thermal_cooling_device_register("LCD", + device->dev, &video_cooling_ops); + if (IS_ERR(device->cooling_dev)) { + /* + * Set cooling_dev to NULL so we don't crash trying to free it. + * Also, why the hell we are returning early and not attempt to + * register video output if cooling device registration failed? + * -- dtor + */ + device->cooling_dev = NULL; + return; + } + + dev_info(&device->dev->dev, "registered as cooling_device%d\n", + device->cooling_dev->id); + result = sysfs_create_link(&device->dev->dev.kobj, + &device->cooling_dev->device.kobj, + "thermal_cooling"); + if (result) + printk(KERN_ERR PREFIX "Create sysfs link\n"); + result = sysfs_create_link(&device->cooling_dev->device.kobj, + &device->dev->dev.kobj, "device"); + if (result) + 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; + + if (video->backlight_registered) + return 0; + + acpi_video_run_bcl_for_osi(video); + + if (!acpi_video_verify_backlight_support()) + return 0; + + mutex_lock(&video->device_list_lock); + list_for_each_entry(dev, &video->video_device_list, entry) + acpi_video_dev_register_backlight(dev); + mutex_unlock(&video->device_list_lock); + + video->backlight_registered = true; + + video->pm_nb.notifier_call = acpi_video_resume; + video->pm_nb.priority = 0; + return register_pm_notifier(&video->pm_nb); +} + +static void acpi_video_dev_unregister_backlight(struct acpi_video_device *device) +{ + if (device->backlight) { + backlight_device_unregister(device->backlight); + device->backlight = NULL; + } + if (device->brightness) { + kfree(device->brightness->levels); + kfree(device->brightness); + device->brightness = NULL; + } + if (device->cooling_dev) { + sysfs_remove_link(&device->dev->dev.kobj, "thermal_cooling"); + sysfs_remove_link(&device->cooling_dev->device.kobj, "device"); + thermal_cooling_device_unregister(device->cooling_dev); + device->cooling_dev = NULL; + } +} + +static int acpi_video_bus_unregister_backlight(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev; + int error; + + if (!video->backlight_registered) + return 0; + + error = unregister_pm_notifier(&video->pm_nb); + + mutex_lock(&video->device_list_lock); + list_for_each_entry(dev, &video->video_device_list, entry) + acpi_video_dev_unregister_backlight(dev); + mutex_unlock(&video->device_list_lock); + + video->backlight_registered = false; + + return error; +} + +static void acpi_video_dev_add_notify_handler(struct acpi_video_device *device) +{ + acpi_status status; + struct acpi_device *adev = device->dev; + + status = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, + acpi_video_device_notify, device); + if (ACPI_FAILURE(status)) + dev_err(&adev->dev, "Error installing notify handler\n"); + else + device->flags.notify = 1; +} + +static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video) +{ + struct input_dev *input; + struct acpi_video_device *dev; + int error; + + video->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto out; + } + + error = acpi_video_bus_start_devices(video); + if (error) + goto err_free_input; + + snprintf(video->phys, sizeof(video->phys), + "%s/video/input0", acpi_device_hid(video->device)); + + input->name = acpi_device_name(video->device); + input->phys = video->phys; + input->id.bustype = BUS_HOST; + input->id.product = 0x06; + input->dev.parent = &video->device->dev; + input->evbit[0] = BIT(EV_KEY); + set_bit(KEY_SWITCHVIDEOMODE, input->keybit); + set_bit(KEY_VIDEO_NEXT, input->keybit); + set_bit(KEY_VIDEO_PREV, input->keybit); + set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit); + set_bit(KEY_BRIGHTNESSUP, input->keybit); + set_bit(KEY_BRIGHTNESSDOWN, input->keybit); + set_bit(KEY_BRIGHTNESS_ZERO, input->keybit); + set_bit(KEY_DISPLAY_OFF, input->keybit); + + error = input_register_device(input); + if (error) + goto err_stop_dev; + + mutex_lock(&video->device_list_lock); + list_for_each_entry(dev, &video->video_device_list, entry) + acpi_video_dev_add_notify_handler(dev); + mutex_unlock(&video->device_list_lock); + + return 0; + +err_stop_dev: + acpi_video_bus_stop_devices(video); +err_free_input: + input_free_device(input); + video->input = NULL; +out: + return error; +} + +static void acpi_video_dev_remove_notify_handler(struct acpi_video_device *dev) +{ + if (dev->flags.notify) { + acpi_remove_notify_handler(dev->dev->handle, ACPI_DEVICE_NOTIFY, + acpi_video_device_notify); + dev->flags.notify = 0; + } +} + +static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev; + + mutex_lock(&video->device_list_lock); + list_for_each_entry(dev, &video->video_device_list, entry) + acpi_video_dev_remove_notify_handler(dev); + mutex_unlock(&video->device_list_lock); + + acpi_video_bus_stop_devices(video); + input_unregister_device(video->input); + video->input = NULL; +} + +static int acpi_video_backlight_notify(struct notifier_block *nb, + unsigned long val, void *bd) +{ + struct backlight_device *backlight = bd; + struct acpi_video_bus *video; + + /* acpi_video_verify_backlight_support only cares about raw devices */ + if (backlight->props.type != BACKLIGHT_RAW) + return NOTIFY_DONE; + + video = container_of(nb, struct acpi_video_bus, backlight_nb); + + switch (val) { + case BACKLIGHT_REGISTERED: + if (!acpi_video_verify_backlight_support()) + acpi_video_bus_unregister_backlight(video); + break; + case BACKLIGHT_UNREGISTERED: + acpi_video_bus_register_backlight(video); + break; + } + + return NOTIFY_OK; +} + +static int acpi_video_bus_add_backlight_notify_handler( + struct acpi_video_bus *video) +{ + int error; + + video->backlight_nb.notifier_call = acpi_video_backlight_notify; + video->backlight_nb.priority = 0; + error = backlight_register_notifier(&video->backlight_nb); + if (error == 0) + video->backlight_notifier_registered = true; + + return error; +} + +static int acpi_video_bus_remove_backlight_notify_handler( + struct acpi_video_bus *video) +{ + if (!video->backlight_notifier_registered) + return 0; + + video->backlight_notifier_registered = false; + + return backlight_unregister_notifier(&video->backlight_nb); +} + +static int acpi_video_bus_put_devices(struct acpi_video_bus *video) +{ + struct acpi_video_device *dev, *next; + + mutex_lock(&video->device_list_lock); + list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { + list_del(&dev->entry); + kfree(dev); + } + mutex_unlock(&video->device_list_lock); + + return 0; +} + +static int instance; + +static int acpi_video_bus_add(struct acpi_device *device) +{ + struct acpi_video_bus *video; + int error; + acpi_status status; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, + device->parent->handle, 1, + acpi_video_bus_match, NULL, + device, NULL); + if (status == AE_ALREADY_EXISTS) { + printk(KERN_WARNING FW_BUG + "Duplicate ACPI video bus devices for the" + " same VGA controller, please try module " + "parameter \"video.allow_duplicates=1\"" + "if the current driver doesn't work.\n"); + if (!allow_duplicates) + return -ENODEV; + } + + video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL); + if (!video) + return -ENOMEM; + + /* a hack to fix the duplicate name "VID" problem on T61 */ + if (!strcmp(device->pnp.bus_id, "VID")) { + if (instance) + device->pnp.bus_id[3] = '0' + instance; + instance++; + } + /* a hack to fix the duplicate name "VGA" problem on Pa 3553 */ + if (!strcmp(device->pnp.bus_id, "VGA")) { + if (instance) + device->pnp.bus_id[3] = '0' + instance; + instance++; + } + + video->device = device; + strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); + strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); + device->driver_data = video; + + acpi_video_bus_find_cap(video); + error = acpi_video_bus_check(video); + if (error) + goto err_free_video; + + mutex_init(&video->device_list_lock); + INIT_LIST_HEAD(&video->video_device_list); + + error = acpi_video_bus_get_devices(video, device); + if (error) + goto err_put_video; + + printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", + ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), + video->flags.multihead ? "yes" : "no", + video->flags.rom ? "yes" : "no", + video->flags.post ? "yes" : "no"); + mutex_lock(&video_list_lock); + list_add_tail(&video->entry, &video_bus_head); + mutex_unlock(&video_list_lock); + + acpi_video_bus_register_backlight(video); + acpi_video_bus_add_notify_handler(video); + acpi_video_bus_add_backlight_notify_handler(video); + + return 0; + +err_put_video: + acpi_video_bus_put_devices(video); + kfree(video->attached_array); +err_free_video: + kfree(video); + device->driver_data = NULL; + + return error; +} + +static int acpi_video_bus_remove(struct acpi_device *device) +{ + struct acpi_video_bus *video = NULL; + + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + video = acpi_driver_data(device); + + acpi_video_bus_remove_backlight_notify_handler(video); + acpi_video_bus_remove_notify_handler(video); + acpi_video_bus_unregister_backlight(video); + acpi_video_bus_put_devices(video); + + mutex_lock(&video_list_lock); + list_del(&video->entry); + mutex_unlock(&video_list_lock); + + kfree(video->attached_array); + kfree(video); + + return 0; +} + +static int __init is_i740(struct pci_dev *dev) +{ + if (dev->device == 0x00D1) + return 1; + if (dev->device == 0x7000) + return 1; + return 0; +} + +static int __init intel_opregion_present(void) +{ + int opregion = 0; + struct pci_dev *dev = NULL; + u32 address; + + for_each_pci_dev(dev) { + if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) + continue; + if (dev->vendor != PCI_VENDOR_ID_INTEL) + continue; + /* We don't want to poke around undefined i740 registers */ + if (is_i740(dev)) + continue; + pci_read_config_dword(dev, 0xfc, &address); + if (!address) + continue; + opregion = 1; + } + return opregion; +} + +int acpi_video_register(void) +{ + int ret; + + if (register_count) { + /* + * if the function of acpi_video_register is already called, + * don't register the acpi_vide_bus again and return no error. + */ + return 0; + } + + mutex_init(&video_list_lock); + INIT_LIST_HEAD(&video_bus_head); + + ret = acpi_bus_register_driver(&acpi_video_bus); + if (ret) + return ret; + + /* + * When the acpi_video_bus is loaded successfully, increase + * the counter reference. + */ + register_count = 1; + + return 0; +} +EXPORT_SYMBOL(acpi_video_register); + +void acpi_video_unregister(void) +{ + if (!register_count) { + /* + * If the acpi video bus is already unloaded, don't + * unload it again and return directly. + */ + return; + } + acpi_bus_unregister_driver(&acpi_video_bus); + + register_count = 0; + + return; +} +EXPORT_SYMBOL(acpi_video_unregister); + +void acpi_video_unregister_backlight(void) +{ + struct acpi_video_bus *video; + + if (!register_count) + return; + + mutex_lock(&video_list_lock); + list_for_each_entry(video, &video_bus_head, entry) + acpi_video_bus_unregister_backlight(video); + mutex_unlock(&video_list_lock); +} +EXPORT_SYMBOL(acpi_video_unregister_backlight); + +/* + * This is kind of nasty. Hardware using Intel chipsets may require + * the video opregion code to be run first in order to initialise + * state before any ACPI video calls are made. To handle this we defer + * registration of the video class until the opregion code has run. + */ + +static int __init acpi_video_init(void) +{ + /* + * Let the module load even if ACPI is disabled (e.g. due to + * a broken BIOS) so that i915.ko can still be loaded on such + * old systems without an AcpiOpRegion. + * + * acpi_video_register() will report -ENODEV later as well due + * to acpi_disabled when i915.ko tries to register itself afterwards. + */ + if (acpi_disabled) + return 0; + + dmi_check_system(video_dmi_table); + + if (intel_opregion_present()) + return 0; + + return acpi_video_register(); +} + +static void __exit acpi_video_exit(void) +{ + acpi_video_unregister(); + + return; +} + +module_init(acpi_video_init); +module_exit(acpi_video_exit); diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index cd49a39..67c548a 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -712,3 +712,18 @@ bool acpi_check_dsm(acpi_handle handle, const u8 *uuid, int rev, u64 funcs) return false; } EXPORT_SYMBOL(acpi_check_dsm); + +/* + * acpi_backlight= handling, this is done here rather then in video_detect.c + * because __setup cannot be used in modules. + */ +char acpi_video_backlight_string[16]; +EXPORT_SYMBOL(acpi_video_backlight_string); + +static int __init acpi_backlight(char *str) +{ + strlcpy(acpi_video_backlight_string, str, + sizeof(acpi_video_backlight_string)); + return 1; +} +__setup("acpi_backlight=", acpi_backlight); diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c deleted file mode 100644 index 3bc4c68..0000000 --- a/drivers/acpi/video.c +++ /dev/null @@ -1,2275 +0,0 @@ -/* - * video.c - ACPI Video Driver - * - * Copyright (C) 2004 Luming Yu - * Copyright (C) 2004 Bruno Ducrot - * Copyright (C) 2006 Thomas Tuttle - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "internal.h" - -#define ACPI_VIDEO_BUS_NAME "Video Bus" -#define ACPI_VIDEO_DEVICE_NAME "Video Device" -#define ACPI_VIDEO_NOTIFY_SWITCH 0x80 -#define ACPI_VIDEO_NOTIFY_PROBE 0x81 -#define ACPI_VIDEO_NOTIFY_CYCLE 0x82 -#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83 -#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84 - -#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x85 -#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x86 -#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x87 -#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x88 -#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x89 - -#define MAX_NAME_LEN 20 - -#define _COMPONENT ACPI_VIDEO_COMPONENT -ACPI_MODULE_NAME("video"); - -MODULE_AUTHOR("Bruno Ducrot"); -MODULE_DESCRIPTION("ACPI Video Driver"); -MODULE_LICENSE("GPL"); - -static bool brightness_switch_enabled = 1; -module_param(brightness_switch_enabled, bool, 0644); - -/* - * By default, we don't allow duplicate ACPI video bus devices - * under the same VGA controller - */ -static bool allow_duplicates; -module_param(allow_duplicates, bool, 0644); - -/* - * For Windows 8 systems: used to decide if video module - * should skip registering backlight interface of its own. - */ -enum { - NATIVE_BACKLIGHT_NOT_SET = -1, - NATIVE_BACKLIGHT_OFF, - NATIVE_BACKLIGHT_ON, -}; - -static int use_native_backlight_param = NATIVE_BACKLIGHT_NOT_SET; -module_param_named(use_native_backlight, use_native_backlight_param, int, 0444); -static int use_native_backlight_dmi = NATIVE_BACKLIGHT_NOT_SET; - -static int disable_backlight_sysfs_if = -1; -module_param(disable_backlight_sysfs_if, int, 0444); - -static int register_count; -static struct mutex video_list_lock; -static struct list_head video_bus_head; -static int acpi_video_bus_add(struct acpi_device *device); -static int acpi_video_bus_remove(struct acpi_device *device); -static void acpi_video_bus_notify(struct acpi_device *device, u32 event); - -static const struct acpi_device_id video_device_ids[] = { - {ACPI_VIDEO_HID, 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, video_device_ids); - -static struct acpi_driver acpi_video_bus = { - .name = "video", - .class = ACPI_VIDEO_CLASS, - .ids = video_device_ids, - .ops = { - .add = acpi_video_bus_add, - .remove = acpi_video_bus_remove, - .notify = acpi_video_bus_notify, - }, -}; - -struct acpi_video_bus_flags { - u8 multihead:1; /* can switch video heads */ - u8 rom:1; /* can retrieve a video rom */ - u8 post:1; /* can configure the head to */ - u8 reserved:5; -}; - -struct acpi_video_bus_cap { - u8 _DOS:1; /* Enable/Disable output switching */ - u8 _DOD:1; /* Enumerate all devices attached to display adapter */ - u8 _ROM:1; /* Get ROM Data */ - u8 _GPD:1; /* Get POST Device */ - u8 _SPD:1; /* Set POST Device */ - u8 _VPO:1; /* Video POST Options */ - u8 reserved:2; -}; - -struct acpi_video_device_attrib { - u32 display_index:4; /* A zero-based instance of the Display */ - u32 display_port_attachment:4; /* This field differentiates the display type */ - u32 display_type:4; /* Describe the specific type in use */ - u32 vendor_specific:4; /* Chipset Vendor Specific */ - u32 bios_can_detect:1; /* BIOS can detect the device */ - u32 depend_on_vga:1; /* Non-VGA output device whose power is related to - the VGA device. */ - u32 pipe_id:3; /* For VGA multiple-head devices. */ - u32 reserved:10; /* Must be 0 */ - u32 device_id_scheme:1; /* Device ID Scheme */ -}; - -struct acpi_video_enumerated_device { - union { - u32 int_val; - struct acpi_video_device_attrib attrib; - } value; - struct acpi_video_device *bind_info; -}; - -struct acpi_video_bus { - struct acpi_device *device; - bool backlight_registered; - bool backlight_notifier_registered; - u8 dos_setting; - struct acpi_video_enumerated_device *attached_array; - u8 attached_count; - u8 child_count; - struct acpi_video_bus_cap cap; - struct acpi_video_bus_flags flags; - struct list_head video_device_list; - struct mutex device_list_lock; /* protects video_device_list */ - struct list_head entry; - struct input_dev *input; - char phys[32]; /* for input device */ - struct notifier_block pm_nb; - struct notifier_block backlight_nb; -}; - -struct acpi_video_device_flags { - u8 crt:1; - u8 lcd:1; - u8 tvout:1; - u8 dvi:1; - u8 bios:1; - u8 unknown:1; - u8 notify:1; - u8 reserved:1; -}; - -struct acpi_video_device_cap { - u8 _ADR:1; /* Return the unique ID */ - u8 _BCL:1; /* Query list of brightness control levels supported */ - u8 _BCM:1; /* Set the brightness level */ - u8 _BQC:1; /* Get current brightness level */ - u8 _BCQ:1; /* Some buggy BIOS uses _BCQ instead of _BQC */ - u8 _DDC:1; /* Return the EDID for this device */ -}; - -struct acpi_video_brightness_flags { - u8 _BCL_no_ac_battery_levels:1; /* no AC/Battery levels in _BCL */ - u8 _BCL_reversed:1; /* _BCL package is in a reversed order */ - u8 _BQC_use_index:1; /* _BQC returns an index value */ -}; - -struct acpi_video_device_brightness { - int curr; - int count; - int *levels; - struct acpi_video_brightness_flags flags; -}; - -struct acpi_video_device { - unsigned long device_id; - struct acpi_video_device_flags flags; - struct acpi_video_device_cap cap; - struct list_head entry; - struct delayed_work switch_brightness_work; - int switch_brightness_event; - struct acpi_video_bus *video; - struct acpi_device *dev; - struct acpi_video_device_brightness *brightness; - struct backlight_device *backlight; - struct thermal_cooling_device *cooling_dev; -}; - -static const char device_decode[][30] = { - "motherboard VGA device", - "PCI VGA device", - "AGP VGA device", - "UNKNOWN", -}; - -static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data); -static void acpi_video_device_rebind(struct acpi_video_bus *video); -static void acpi_video_device_bind(struct acpi_video_bus *video, - struct acpi_video_device *device); -static int acpi_video_device_enumerate(struct acpi_video_bus *video); -static int acpi_video_device_lcd_set_level(struct acpi_video_device *device, - int level); -static int acpi_video_device_lcd_get_level_current( - struct acpi_video_device *device, - unsigned long long *level, bool raw); -static int acpi_video_get_next_level(struct acpi_video_device *device, - u32 level_current, u32 event); -static void acpi_video_switch_brightness(struct work_struct *work); - -static bool acpi_video_use_native_backlight(void) -{ - if (use_native_backlight_param != NATIVE_BACKLIGHT_NOT_SET) - return use_native_backlight_param; - else if (use_native_backlight_dmi != NATIVE_BACKLIGHT_NOT_SET) - return use_native_backlight_dmi; - return acpi_osi_is_win8(); -} - -bool acpi_video_verify_backlight_support(void) -{ - if (acpi_video_use_native_backlight() && - backlight_device_registered(BACKLIGHT_RAW)) - return false; - return acpi_video_backlight_support(); -} -EXPORT_SYMBOL_GPL(acpi_video_verify_backlight_support); - -/* backlight device sysfs support */ -static int acpi_video_get_brightness(struct backlight_device *bd) -{ - unsigned long long cur_level; - int i; - struct acpi_video_device *vd = bl_get_data(bd); - - if (acpi_video_device_lcd_get_level_current(vd, &cur_level, false)) - return -EINVAL; - for (i = 2; i < vd->brightness->count; i++) { - if (vd->brightness->levels[i] == cur_level) - /* - * The first two entries are special - see page 575 - * of the ACPI spec 3.0 - */ - return i - 2; - } - return 0; -} - -static int acpi_video_set_brightness(struct backlight_device *bd) -{ - int request_level = bd->props.brightness + 2; - struct acpi_video_device *vd = bl_get_data(bd); - - cancel_delayed_work(&vd->switch_brightness_work); - return acpi_video_device_lcd_set_level(vd, - vd->brightness->levels[request_level]); -} - -static const struct backlight_ops acpi_backlight_ops = { - .get_brightness = acpi_video_get_brightness, - .update_status = acpi_video_set_brightness, -}; - -/* thermal cooling device callbacks */ -static int video_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned - long *state) -{ - struct acpi_device *device = cooling_dev->devdata; - struct acpi_video_device *video = acpi_driver_data(device); - - *state = video->brightness->count - 3; - return 0; -} - -static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsigned - long *state) -{ - struct acpi_device *device = cooling_dev->devdata; - struct acpi_video_device *video = acpi_driver_data(device); - unsigned long long level; - int offset; - - if (acpi_video_device_lcd_get_level_current(video, &level, false)) - return -EINVAL; - for (offset = 2; offset < video->brightness->count; offset++) - if (level == video->brightness->levels[offset]) { - *state = video->brightness->count - offset - 1; - return 0; - } - - return -EINVAL; -} - -static int -video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state) -{ - struct acpi_device *device = cooling_dev->devdata; - struct acpi_video_device *video = acpi_driver_data(device); - int level; - - if (state >= video->brightness->count - 2) - return -EINVAL; - - state = video->brightness->count - state; - level = video->brightness->levels[state - 1]; - return acpi_video_device_lcd_set_level(video, level); -} - -static const struct thermal_cooling_device_ops video_cooling_ops = { - .get_max_state = video_get_max_state, - .get_cur_state = video_get_cur_state, - .set_cur_state = video_set_cur_state, -}; - -/* - * -------------------------------------------------------------------------- - * Video Management - * -------------------------------------------------------------------------- - */ - -static int -acpi_video_device_lcd_query_levels(struct acpi_video_device *device, - union acpi_object **levels) -{ - int status; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - - - *levels = NULL; - - status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer); - if (!ACPI_SUCCESS(status)) - return status; - obj = (union acpi_object *)buffer.pointer; - if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { - printk(KERN_ERR PREFIX "Invalid _BCL data\n"); - status = -EFAULT; - goto err; - } - - *levels = obj; - - return 0; - -err: - kfree(buffer.pointer); - - return status; -} - -static int -acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) -{ - int status; - int state; - - status = acpi_execute_simple_method(device->dev->handle, - "_BCM", level); - if (ACPI_FAILURE(status)) { - ACPI_ERROR((AE_INFO, "Evaluating _BCM failed")); - return -EIO; - } - - device->brightness->curr = level; - for (state = 2; state < device->brightness->count; state++) - if (level == device->brightness->levels[state]) { - if (device->backlight) - device->backlight->props.brightness = state - 2; - return 0; - } - - ACPI_ERROR((AE_INFO, "Current brightness invalid")); - return -EINVAL; -} - -/* - * For some buggy _BQC methods, we need to add a constant value to - * the _BQC return value to get the actual current brightness level - */ - -static int bqc_offset_aml_bug_workaround; -static int __init video_set_bqc_offset(const struct dmi_system_id *d) -{ - bqc_offset_aml_bug_workaround = 9; - return 0; -} - -static int __init video_disable_native_backlight(const struct dmi_system_id *d) -{ - use_native_backlight_dmi = NATIVE_BACKLIGHT_OFF; - return 0; -} - -static int __init video_enable_native_backlight(const struct dmi_system_id *d) -{ - use_native_backlight_dmi = NATIVE_BACKLIGHT_ON; - return 0; -} - -static int __init video_disable_backlight_sysfs_if( - const struct dmi_system_id *d) -{ - if (disable_backlight_sysfs_if == -1) - disable_backlight_sysfs_if = 1; - return 0; -} - -static struct dmi_system_id video_dmi_table[] __initdata = { - /* - * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 - */ - { - .callback = video_set_bqc_offset, - .ident = "Acer Aspire 5720", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), - }, - }, - { - .callback = video_set_bqc_offset, - .ident = "Acer Aspire 5710Z", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"), - }, - }, - { - .callback = video_set_bqc_offset, - .ident = "eMachines E510", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"), - DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"), - }, - }, - { - .callback = video_set_bqc_offset, - .ident = "Acer Aspire 5315", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"), - }, - }, - { - .callback = video_set_bqc_offset, - .ident = "Acer Aspire 7720", - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"), - }, - }, - - /* - * These models have a working acpi_video backlight control, and using - * native backlight causes a regression where backlight does not work - * when userspace is not handling brightness key events. Disable - * native_backlight on these to fix this: - * https://bugzilla.kernel.org/show_bug.cgi?id=81691 - */ - { - .callback = video_disable_native_backlight, - .ident = "ThinkPad T420", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"), - }, - }, - { - .callback = video_disable_native_backlight, - .ident = "ThinkPad T520", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"), - }, - }, - { - .callback = video_disable_native_backlight, - .ident = "ThinkPad X201s", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"), - }, - }, - - /* The native backlight controls do not work on some older machines */ - { - /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */ - .callback = video_disable_native_backlight, - .ident = "HP ENVY 15 Notebook", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"), - }, - }, - - { - .callback = video_disable_native_backlight, - .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"), - }, - }, - { - .callback = video_disable_native_backlight, - .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "370R4E/370R4V/370R5E/3570RE/370R5V"), - }, - }, - { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */ - .callback = video_disable_native_backlight, - .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "3570R/370R/470R/450R/510R/4450RV"), - }, - }, - { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */ - .callback = video_disable_native_backlight, - .ident = "SAMSUNG 730U3E/740U3E", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), - }, - }, - { - /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */ - .callback = video_disable_native_backlight, - .ident = "SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "900X3C/900X3D/900X3E/900X4C/900X4D"), - }, - }, - - { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */ - .callback = video_disable_native_backlight, - .ident = "Dell XPS15 L521X", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"), - }, - }, - - /* Non win8 machines which need native backlight nevertheless */ - { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ - .callback = video_enable_native_backlight, - .ident = "Lenovo Ideapad Z570", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "102434U"), - }, - }, - { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */ - .callback = video_enable_native_backlight, - .ident = "Apple MacBook Pro 12,1", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"), - }, - }, - - /* - * Some machines have a broken acpi-video interface for brightness - * control, but still need an acpi_video_device_lcd_set_level() call - * on resume to turn the backlight power on. We Enable backlight - * control on these systems, but do not register a backlight sysfs - * as brightness control does not work. - */ - { - /* https://bugs.freedesktop.org/show_bug.cgi?id=82634 */ - .callback = video_disable_backlight_sysfs_if, - .ident = "Toshiba Portege R830", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R830"), - }, - }, - {} -}; - -static unsigned long long -acpi_video_bqc_value_to_level(struct acpi_video_device *device, - unsigned long long bqc_value) -{ - unsigned long long level; - - if (device->brightness->flags._BQC_use_index) { - /* - * _BQC returns an index that doesn't account for - * the first 2 items with special meaning, so we need - * to compensate for that by offsetting ourselves - */ - if (device->brightness->flags._BCL_reversed) - bqc_value = device->brightness->count - 3 - bqc_value; - - level = device->brightness->levels[bqc_value + 2]; - } else { - level = bqc_value; - } - - level += bqc_offset_aml_bug_workaround; - - return level; -} - -static int -acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, - unsigned long long *level, bool raw) -{ - acpi_status status = AE_OK; - int i; - - if (device->cap._BQC || device->cap._BCQ) { - char *buf = device->cap._BQC ? "_BQC" : "_BCQ"; - - status = acpi_evaluate_integer(device->dev->handle, buf, - NULL, level); - if (ACPI_SUCCESS(status)) { - if (raw) { - /* - * Caller has indicated he wants the raw - * value returned by _BQC, so don't furtherly - * mess with the value. - */ - return 0; - } - - *level = acpi_video_bqc_value_to_level(device, *level); - - for (i = 2; i < device->brightness->count; i++) - if (device->brightness->levels[i] == *level) { - device->brightness->curr = *level; - return 0; - } - /* - * BQC returned an invalid level. - * Stop using it. - */ - ACPI_WARNING((AE_INFO, - "%s returned an invalid level", - buf)); - device->cap._BQC = device->cap._BCQ = 0; - } else { - /* - * Fixme: - * should we return an error or ignore this failure? - * dev->brightness->curr is a cached value which stores - * the correct current backlight level in most cases. - * ACPI video backlight still works w/ buggy _BQC. - * http://bugzilla.kernel.org/show_bug.cgi?id=12233 - */ - ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf)); - device->cap._BQC = device->cap._BCQ = 0; - } - } - - *level = device->brightness->curr; - return 0; -} - -static int -acpi_video_device_EDID(struct acpi_video_device *device, - union acpi_object **edid, ssize_t length) -{ - int status; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - - - *edid = NULL; - - if (!device) - return -ENODEV; - if (length == 128) - arg0.integer.value = 1; - else if (length == 256) - arg0.integer.value = 2; - else - return -EINVAL; - - status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer); - if (ACPI_FAILURE(status)) - return -ENODEV; - - obj = buffer.pointer; - - if (obj && obj->type == ACPI_TYPE_BUFFER) - *edid = obj; - else { - printk(KERN_ERR PREFIX "Invalid _DDC data\n"); - status = -EFAULT; - kfree(obj); - } - - return status; -} - -/* bus */ - -/* - * Arg: - * video : video bus device pointer - * bios_flag : - * 0. The system BIOS should NOT automatically switch(toggle) - * the active display output. - * 1. The system BIOS should automatically switch (toggle) the - * active display output. No switch event. - * 2. The _DGS value should be locked. - * 3. The system BIOS should not automatically switch (toggle) the - * active display output, but instead generate the display switch - * event notify code. - * lcd_flag : - * 0. The system BIOS should automatically control the brightness level - * of the LCD when the power changes from AC to DC - * 1. The system BIOS should NOT automatically control the brightness - * level of the LCD when the power changes from AC to DC. - * Return Value: - * -EINVAL wrong arg. - */ - -static int -acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) -{ - acpi_status status; - - if (!video->cap._DOS) - return 0; - - if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) - return -EINVAL; - video->dos_setting = (lcd_flag << 2) | bios_flag; - status = acpi_execute_simple_method(video->device->handle, "_DOS", - (lcd_flag << 2) | bios_flag); - if (ACPI_FAILURE(status)) - return -EIO; - - return 0; -} - -/* - * Simple comparison function used to sort backlight levels. - */ - -static int -acpi_video_cmp_level(const void *a, const void *b) -{ - return *(int *)a - *(int *)b; -} - -/* - * Decides if _BQC/_BCQ for this system is usable - * - * We do this by changing the level first and then read out the current - * brightness level, if the value does not match, find out if it is using - * index. If not, clear the _BQC/_BCQ capability. - */ -static int acpi_video_bqc_quirk(struct acpi_video_device *device, - int max_level, int current_level) -{ - struct acpi_video_device_brightness *br = device->brightness; - int result; - unsigned long long level; - int test_level; - - /* don't mess with existing known broken systems */ - if (bqc_offset_aml_bug_workaround) - return 0; - - /* - * Some systems always report current brightness level as maximum - * through _BQC, we need to test another value for them. - */ - test_level = current_level == max_level ? br->levels[3] : max_level; - - result = acpi_video_device_lcd_set_level(device, test_level); - if (result) - return result; - - result = acpi_video_device_lcd_get_level_current(device, &level, true); - if (result) - return result; - - if (level != test_level) { - /* buggy _BQC found, need to find out if it uses index */ - if (level < br->count) { - if (br->flags._BCL_reversed) - level = br->count - 3 - level; - if (br->levels[level + 2] == test_level) - br->flags._BQC_use_index = 1; - } - - if (!br->flags._BQC_use_index) - device->cap._BQC = device->cap._BCQ = 0; - } - - return 0; -} - - -/* - * Arg: - * device : video output device (LCD, CRT, ..) - * - * Return Value: - * Maximum brightness level - * - * Allocate and initialize device->brightness. - */ - -static int -acpi_video_init_brightness(struct acpi_video_device *device) -{ - union acpi_object *obj = NULL; - int i, max_level = 0, count = 0, level_ac_battery = 0; - unsigned long long level, level_old; - union acpi_object *o; - struct acpi_video_device_brightness *br = NULL; - int result = -EINVAL; - u32 value; - - if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available " - "LCD brightness level\n")); - goto out; - } - - if (obj->package.count < 2) - goto out; - - br = kzalloc(sizeof(*br), GFP_KERNEL); - if (!br) { - printk(KERN_ERR "can't allocate memory\n"); - result = -ENOMEM; - goto out; - } - - br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels), - GFP_KERNEL); - if (!br->levels) { - result = -ENOMEM; - goto out_free; - } - - for (i = 0; i < obj->package.count; i++) { - o = (union acpi_object *)&obj->package.elements[i]; - if (o->type != ACPI_TYPE_INTEGER) { - printk(KERN_ERR PREFIX "Invalid data\n"); - continue; - } - value = (u32) o->integer.value; - /* Skip duplicate entries */ - if (count > 2 && br->levels[count - 1] == value) - continue; - - br->levels[count] = value; - - if (br->levels[count] > max_level) - max_level = br->levels[count]; - count++; - } - - /* - * some buggy BIOS don't export the levels - * when machine is on AC/Battery in _BCL package. - * In this case, the first two elements in _BCL packages - * are also supported brightness levels that OS should take care of. - */ - for (i = 2; i < count; i++) { - if (br->levels[i] == br->levels[0]) - level_ac_battery++; - if (br->levels[i] == br->levels[1]) - level_ac_battery++; - } - - if (level_ac_battery < 2) { - level_ac_battery = 2 - level_ac_battery; - br->flags._BCL_no_ac_battery_levels = 1; - for (i = (count - 1 + level_ac_battery); i >= 2; i--) - br->levels[i] = br->levels[i - level_ac_battery]; - count += level_ac_battery; - } else if (level_ac_battery > 2) - ACPI_ERROR((AE_INFO, "Too many duplicates in _BCL package")); - - /* Check if the _BCL package is in a reversed order */ - if (max_level == br->levels[2]) { - br->flags._BCL_reversed = 1; - sort(&br->levels[2], count - 2, sizeof(br->levels[2]), - acpi_video_cmp_level, NULL); - } else if (max_level != br->levels[count - 1]) - ACPI_ERROR((AE_INFO, - "Found unordered _BCL package")); - - br->count = count; - device->brightness = br; - - /* _BQC uses INDEX while _BCL uses VALUE in some laptops */ - br->curr = level = max_level; - - if (!device->cap._BQC) - goto set_level; - - result = acpi_video_device_lcd_get_level_current(device, - &level_old, true); - if (result) - goto out_free_levels; - - result = acpi_video_bqc_quirk(device, max_level, level_old); - if (result) - goto out_free_levels; - /* - * cap._BQC may get cleared due to _BQC is found to be broken - * in acpi_video_bqc_quirk, so check again here. - */ - if (!device->cap._BQC) - goto set_level; - - level = acpi_video_bqc_value_to_level(device, level_old); - /* - * On some buggy laptops, _BQC returns an uninitialized - * value when invoked for the first time, i.e. - * level_old is invalid (no matter whether it's a level - * or an index). Set the backlight to max_level in this case. - */ - for (i = 2; i < br->count; i++) - if (level == br->levels[i]) - break; - if (i == br->count || !level) - level = max_level; - -set_level: - result = acpi_video_device_lcd_set_level(device, level); - if (result) - goto out_free_levels; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "found %d brightness levels\n", count - 2)); - kfree(obj); - return result; - -out_free_levels: - kfree(br->levels); -out_free: - kfree(br); -out: - device->brightness = NULL; - kfree(obj); - return result; -} - -/* - * Arg: - * device : video output device (LCD, CRT, ..) - * - * Return Value: - * None - * - * Find out all required AML methods defined under the output - * device. - */ - -static void acpi_video_device_find_cap(struct acpi_video_device *device) -{ - if (acpi_has_method(device->dev->handle, "_ADR")) - device->cap._ADR = 1; - if (acpi_has_method(device->dev->handle, "_BCL")) - device->cap._BCL = 1; - if (acpi_has_method(device->dev->handle, "_BCM")) - device->cap._BCM = 1; - if (acpi_has_method(device->dev->handle, "_BQC")) { - device->cap._BQC = 1; - } else if (acpi_has_method(device->dev->handle, "_BCQ")) { - printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n"); - device->cap._BCQ = 1; - } - - if (acpi_has_method(device->dev->handle, "_DDC")) - device->cap._DDC = 1; -} - -/* - * Arg: - * device : video output device (VGA) - * - * Return Value: - * None - * - * Find out all required AML methods defined under the video bus device. - */ - -static void acpi_video_bus_find_cap(struct acpi_video_bus *video) -{ - if (acpi_has_method(video->device->handle, "_DOS")) - video->cap._DOS = 1; - if (acpi_has_method(video->device->handle, "_DOD")) - video->cap._DOD = 1; - if (acpi_has_method(video->device->handle, "_ROM")) - video->cap._ROM = 1; - if (acpi_has_method(video->device->handle, "_GPD")) - video->cap._GPD = 1; - if (acpi_has_method(video->device->handle, "_SPD")) - video->cap._SPD = 1; - if (acpi_has_method(video->device->handle, "_VPO")) - video->cap._VPO = 1; -} - -/* - * Check whether the video bus device has required AML method to - * support the desired features - */ - -static int acpi_video_bus_check(struct acpi_video_bus *video) -{ - acpi_status status = -ENOENT; - struct pci_dev *dev; - - if (!video) - return -EINVAL; - - dev = acpi_get_pci_dev(video->device->handle); - if (!dev) - return -ENODEV; - pci_dev_put(dev); - - /* - * Since there is no HID, CID and so on for VGA driver, we have - * to check well known required nodes. - */ - - /* Does this device support video switching? */ - if (video->cap._DOS || video->cap._DOD) { - if (!video->cap._DOS) { - printk(KERN_WARNING FW_BUG - "ACPI(%s) defines _DOD but not _DOS\n", - acpi_device_bid(video->device)); - } - video->flags.multihead = 1; - status = 0; - } - - /* Does this device support retrieving a video ROM? */ - if (video->cap._ROM) { - video->flags.rom = 1; - status = 0; - } - - /* Does this device support configuring which video device to POST? */ - if (video->cap._GPD && video->cap._SPD && video->cap._VPO) { - video->flags.post = 1; - status = 0; - } - - return status; -} - -/* - * -------------------------------------------------------------------------- - * Driver Interface - * -------------------------------------------------------------------------- - */ - -/* device interface */ -static struct acpi_video_device_attrib * -acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id) -{ - struct acpi_video_enumerated_device *ids; - int i; - - for (i = 0; i < video->attached_count; i++) { - ids = &video->attached_array[i]; - if ((ids->value.int_val & 0xffff) == device_id) - return &ids->value.attrib; - } - - return NULL; -} - -static int -acpi_video_get_device_type(struct acpi_video_bus *video, - unsigned long device_id) -{ - struct acpi_video_enumerated_device *ids; - int i; - - for (i = 0; i < video->attached_count; i++) { - ids = &video->attached_array[i]; - if ((ids->value.int_val & 0xffff) == device_id) - return ids->value.int_val; - } - - return 0; -} - -static int -acpi_video_bus_get_one_device(struct acpi_device *device, - struct acpi_video_bus *video) -{ - unsigned long long device_id; - int status, device_type; - struct acpi_video_device *data; - struct acpi_video_device_attrib *attribute; - - status = - acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); - /* Some device omits _ADR, we skip them instead of fail */ - if (ACPI_FAILURE(status)) - return 0; - - data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); - if (!data) - return -ENOMEM; - - strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); - device->driver_data = data; - - data->device_id = device_id; - data->video = video; - data->dev = device; - INIT_DELAYED_WORK(&data->switch_brightness_work, - acpi_video_switch_brightness); - - attribute = acpi_video_get_device_attr(video, device_id); - - if (attribute && attribute->device_id_scheme) { - switch (attribute->display_type) { - case ACPI_VIDEO_DISPLAY_CRT: - data->flags.crt = 1; - break; - case ACPI_VIDEO_DISPLAY_TV: - data->flags.tvout = 1; - break; - case ACPI_VIDEO_DISPLAY_DVI: - data->flags.dvi = 1; - break; - case ACPI_VIDEO_DISPLAY_LCD: - data->flags.lcd = 1; - break; - default: - data->flags.unknown = 1; - break; - } - if (attribute->bios_can_detect) - data->flags.bios = 1; - } else { - /* Check for legacy IDs */ - device_type = acpi_video_get_device_type(video, device_id); - /* Ignore bits 16 and 18-20 */ - switch (device_type & 0xffe2ffff) { - case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR: - data->flags.crt = 1; - break; - case ACPI_VIDEO_DISPLAY_LEGACY_PANEL: - data->flags.lcd = 1; - break; - case ACPI_VIDEO_DISPLAY_LEGACY_TV: - data->flags.tvout = 1; - break; - default: - data->flags.unknown = 1; - } - } - - acpi_video_device_bind(video, data); - acpi_video_device_find_cap(data); - - mutex_lock(&video->device_list_lock); - list_add_tail(&data->entry, &video->video_device_list); - mutex_unlock(&video->device_list_lock); - - return status; -} - -/* - * Arg: - * video : video bus device - * - * Return: - * none - * - * Enumerate the video device list of the video bus, - * bind the ids with the corresponding video devices - * under the video bus. - */ - -static void acpi_video_device_rebind(struct acpi_video_bus *video) -{ - struct acpi_video_device *dev; - - mutex_lock(&video->device_list_lock); - - list_for_each_entry(dev, &video->video_device_list, entry) - acpi_video_device_bind(video, dev); - - mutex_unlock(&video->device_list_lock); -} - -/* - * Arg: - * video : video bus device - * device : video output device under the video - * bus - * - * Return: - * none - * - * Bind the ids with the corresponding video devices - * under the video bus. - */ - -static void -acpi_video_device_bind(struct acpi_video_bus *video, - struct acpi_video_device *device) -{ - struct acpi_video_enumerated_device *ids; - int i; - - for (i = 0; i < video->attached_count; i++) { - ids = &video->attached_array[i]; - if (device->device_id == (ids->value.int_val & 0xffff)) { - ids->bind_info = device; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i)); - } - } -} - -static bool acpi_video_device_in_dod(struct acpi_video_device *device) -{ - struct acpi_video_bus *video = device->video; - int i; - - /* - * If we have a broken _DOD or we have more than 8 output devices - * under the graphics controller node that we can't proper deal with - * in the operation region code currently, no need to test. - */ - if (!video->attached_count || video->child_count > 8) - return true; - - for (i = 0; i < video->attached_count; i++) { - if ((video->attached_array[i].value.int_val & 0xfff) == - (device->device_id & 0xfff)) - return true; - } - - return false; -} - -/* - * Arg: - * video : video bus device - * - * Return: - * < 0 : error - * - * Call _DOD to enumerate all devices attached to display adapter - * - */ - -static int acpi_video_device_enumerate(struct acpi_video_bus *video) -{ - int status; - int count; - int i; - struct acpi_video_enumerated_device *active_list; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *dod = NULL; - union acpi_object *obj; - - status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer); - if (!ACPI_SUCCESS(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD")); - return status; - } - - dod = buffer.pointer; - if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) { - ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data")); - status = -EFAULT; - goto out; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n", - dod->package.count)); - - active_list = kcalloc(1 + dod->package.count, - sizeof(struct acpi_video_enumerated_device), - GFP_KERNEL); - if (!active_list) { - status = -ENOMEM; - goto out; - } - - count = 0; - for (i = 0; i < dod->package.count; i++) { - obj = &dod->package.elements[i]; - - if (obj->type != ACPI_TYPE_INTEGER) { - printk(KERN_ERR PREFIX - "Invalid _DOD data in element %d\n", i); - continue; - } - - active_list[count].value.int_val = obj->integer.value; - active_list[count].bind_info = NULL; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i, - (int)obj->integer.value)); - count++; - } - - kfree(video->attached_array); - - video->attached_array = active_list; - video->attached_count = count; - -out: - kfree(buffer.pointer); - return status; -} - -static int -acpi_video_get_next_level(struct acpi_video_device *device, - u32 level_current, u32 event) -{ - int min, max, min_above, max_below, i, l, delta = 255; - max = max_below = 0; - min = min_above = 255; - /* Find closest level to level_current */ - for (i = 2; i < device->brightness->count; i++) { - l = device->brightness->levels[i]; - if (abs(l - level_current) < abs(delta)) { - delta = l - level_current; - if (!delta) - break; - } - } - /* Ajust level_current to closest available level */ - level_current += delta; - for (i = 2; i < device->brightness->count; i++) { - l = device->brightness->levels[i]; - if (l < min) - min = l; - if (l > max) - max = l; - if (l < min_above && l > level_current) - min_above = l; - if (l > max_below && l < level_current) - max_below = l; - } - - switch (event) { - case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: - return (level_current < max) ? min_above : min; - case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: - return (level_current < max) ? min_above : max; - case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: - return (level_current > min) ? max_below : min; - case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: - case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: - return 0; - default: - return level_current; - } -} - -static void -acpi_video_switch_brightness(struct work_struct *work) -{ - struct acpi_video_device *device = container_of(to_delayed_work(work), - struct acpi_video_device, switch_brightness_work); - unsigned long long level_current, level_next; - int event = device->switch_brightness_event; - int result = -EINVAL; - - /* no warning message if acpi_backlight=vendor or a quirk is used */ - if (!device->backlight) - return; - - if (!device->brightness) - goto out; - - result = acpi_video_device_lcd_get_level_current(device, - &level_current, - false); - if (result) - goto out; - - level_next = acpi_video_get_next_level(device, level_current, event); - - result = acpi_video_device_lcd_set_level(device, level_next); - - if (!result) - backlight_force_update(device->backlight, - BACKLIGHT_UPDATE_HOTKEY); - -out: - if (result) - printk(KERN_ERR PREFIX "Failed to switch the brightness\n"); -} - -int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, - void **edid) -{ - struct acpi_video_bus *video; - struct acpi_video_device *video_device; - union acpi_object *buffer = NULL; - acpi_status status; - int i, length; - - if (!device || !acpi_driver_data(device)) - return -EINVAL; - - video = acpi_driver_data(device); - - for (i = 0; i < video->attached_count; i++) { - video_device = video->attached_array[i].bind_info; - length = 256; - - if (!video_device) - continue; - - if (!video_device->cap._DDC) - continue; - - if (type) { - switch (type) { - case ACPI_VIDEO_DISPLAY_CRT: - if (!video_device->flags.crt) - continue; - break; - case ACPI_VIDEO_DISPLAY_TV: - if (!video_device->flags.tvout) - continue; - break; - case ACPI_VIDEO_DISPLAY_DVI: - if (!video_device->flags.dvi) - continue; - break; - case ACPI_VIDEO_DISPLAY_LCD: - if (!video_device->flags.lcd) - continue; - break; - } - } else if (video_device->device_id != device_id) { - continue; - } - - status = acpi_video_device_EDID(video_device, &buffer, length); - - if (ACPI_FAILURE(status) || !buffer || - buffer->type != ACPI_TYPE_BUFFER) { - length = 128; - status = acpi_video_device_EDID(video_device, &buffer, - length); - if (ACPI_FAILURE(status) || !buffer || - buffer->type != ACPI_TYPE_BUFFER) { - continue; - } - } - - *edid = buffer->buffer.pointer; - return length; - } - - return -ENODEV; -} -EXPORT_SYMBOL(acpi_video_get_edid); - -static int -acpi_video_bus_get_devices(struct acpi_video_bus *video, - struct acpi_device *device) -{ - int status = 0; - struct acpi_device *dev; - - /* - * There are systems where video module known to work fine regardless - * of broken _DOD and ignoring returned value here doesn't cause - * any issues later. - */ - acpi_video_device_enumerate(video); - - list_for_each_entry(dev, &device->children, node) { - - status = acpi_video_bus_get_one_device(dev, video); - if (status) { - dev_err(&dev->dev, "Can't attach device\n"); - break; - } - video->child_count++; - } - return status; -} - -/* acpi_video interface */ - -/* - * Win8 requires setting bit2 of _DOS to let firmware know it shouldn't - * preform any automatic brightness change on receiving a notification. - */ -static int acpi_video_bus_start_devices(struct acpi_video_bus *video) -{ - return acpi_video_bus_DOS(video, 0, - acpi_osi_is_win8() ? 1 : 0); -} - -static int acpi_video_bus_stop_devices(struct acpi_video_bus *video) -{ - return acpi_video_bus_DOS(video, 0, - acpi_osi_is_win8() ? 0 : 1); -} - -static void acpi_video_bus_notify(struct acpi_device *device, u32 event) -{ - struct acpi_video_bus *video = acpi_driver_data(device); - struct input_dev *input; - int keycode = 0; - - if (!video || !video->input) - return; - - input = video->input; - - switch (event) { - case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch, - * most likely via hotkey. */ - keycode = KEY_SWITCHVIDEOMODE; - break; - - case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video - * connector. */ - acpi_video_device_enumerate(video); - acpi_video_device_rebind(video); - keycode = KEY_SWITCHVIDEOMODE; - break; - - case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */ - keycode = KEY_SWITCHVIDEOMODE; - break; - case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */ - keycode = KEY_VIDEO_NEXT; - break; - case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */ - keycode = KEY_VIDEO_PREV; - break; - - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - break; - } - - if (acpi_notifier_call_chain(device, event, 0)) - /* Something vetoed the keypress. */ - keycode = 0; - - if (keycode) { - input_report_key(input, keycode, 1); - input_sync(input); - input_report_key(input, keycode, 0); - input_sync(input); - } - - return; -} - -static void brightness_switch_event(struct acpi_video_device *video_device, - u32 event) -{ - if (!brightness_switch_enabled) - return; - - video_device->switch_brightness_event = event; - schedule_delayed_work(&video_device->switch_brightness_work, HZ / 10); -} - -static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) -{ - struct acpi_video_device *video_device = data; - struct acpi_device *device = NULL; - struct acpi_video_bus *bus; - struct input_dev *input; - int keycode = 0; - - if (!video_device) - return; - - device = video_device->dev; - bus = video_device->video; - input = bus->input; - - switch (event) { - case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ - brightness_switch_event(video_device, event); - keycode = KEY_BRIGHTNESS_CYCLE; - break; - case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */ - brightness_switch_event(video_device, event); - keycode = KEY_BRIGHTNESSUP; - break; - case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */ - brightness_switch_event(video_device, event); - keycode = KEY_BRIGHTNESSDOWN; - break; - case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightness */ - brightness_switch_event(video_device, event); - keycode = KEY_BRIGHTNESS_ZERO; - break; - case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */ - brightness_switch_event(video_device, event); - keycode = KEY_DISPLAY_OFF; - break; - default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); - break; - } - - acpi_notifier_call_chain(device, event, 0); - - if (keycode) { - input_report_key(input, keycode, 1); - input_sync(input); - input_report_key(input, keycode, 0); - input_sync(input); - } - - return; -} - -static int acpi_video_resume(struct notifier_block *nb, - unsigned long val, void *ign) -{ - struct acpi_video_bus *video; - struct acpi_video_device *video_device; - int i; - - switch (val) { - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - case PM_RESTORE_PREPARE: - return NOTIFY_DONE; - } - - video = container_of(nb, struct acpi_video_bus, pm_nb); - - dev_info(&video->device->dev, "Restoring backlight state\n"); - - for (i = 0; i < video->attached_count; i++) { - video_device = video->attached_array[i].bind_info; - if (video_device && video_device->brightness) - acpi_video_device_lcd_set_level(video_device, - video_device->brightness->curr); - } - - return NOTIFY_OK; -} - -static acpi_status -acpi_video_bus_match(acpi_handle handle, u32 level, void *context, - void **return_value) -{ - struct acpi_device *device = context; - struct acpi_device *sibling; - int result; - - if (handle == device->handle) - return AE_CTRL_TERMINATE; - - result = acpi_bus_get_device(handle, &sibling); - if (result) - return AE_OK; - - if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) - return AE_ALREADY_EXISTS; - - return AE_OK; -} - -static void acpi_video_dev_register_backlight(struct acpi_video_device *device) -{ - struct backlight_properties props; - struct pci_dev *pdev; - acpi_handle acpi_parent; - struct device *parent = NULL; - int result; - static int count; - char *name; - - /* - * Do not create backlight device for video output - * device that is not in the enumerated list. - */ - if (!acpi_video_device_in_dod(device)) { - dev_dbg(&device->dev->dev, "not in _DOD list, ignore\n"); - return; - } - - result = acpi_video_init_brightness(device); - if (result) - return; - - if (disable_backlight_sysfs_if > 0) - return; - - name = kasprintf(GFP_KERNEL, "acpi_video%d", count); - if (!name) - return; - count++; - - acpi_get_parent(device->dev->handle, &acpi_parent); - - pdev = acpi_get_pci_dev(acpi_parent); - if (pdev) { - parent = &pdev->dev; - pci_dev_put(pdev); - } - - memset(&props, 0, sizeof(struct backlight_properties)); - props.type = BACKLIGHT_FIRMWARE; - props.max_brightness = device->brightness->count - 3; - device->backlight = backlight_device_register(name, - parent, - device, - &acpi_backlight_ops, - &props); - kfree(name); - if (IS_ERR(device->backlight)) { - device->backlight = NULL; - return; - } - - /* - * Save current brightness level in case we have to restore it - * before acpi_video_device_lcd_set_level() is called next time. - */ - device->backlight->props.brightness = - acpi_video_get_brightness(device->backlight); - - device->cooling_dev = thermal_cooling_device_register("LCD", - device->dev, &video_cooling_ops); - if (IS_ERR(device->cooling_dev)) { - /* - * Set cooling_dev to NULL so we don't crash trying to free it. - * Also, why the hell we are returning early and not attempt to - * register video output if cooling device registration failed? - * -- dtor - */ - device->cooling_dev = NULL; - return; - } - - dev_info(&device->dev->dev, "registered as cooling_device%d\n", - device->cooling_dev->id); - result = sysfs_create_link(&device->dev->dev.kobj, - &device->cooling_dev->device.kobj, - "thermal_cooling"); - if (result) - printk(KERN_ERR PREFIX "Create sysfs link\n"); - result = sysfs_create_link(&device->cooling_dev->device.kobj, - &device->dev->dev.kobj, "device"); - if (result) - 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; - - if (video->backlight_registered) - return 0; - - acpi_video_run_bcl_for_osi(video); - - if (!acpi_video_verify_backlight_support()) - return 0; - - mutex_lock(&video->device_list_lock); - list_for_each_entry(dev, &video->video_device_list, entry) - acpi_video_dev_register_backlight(dev); - mutex_unlock(&video->device_list_lock); - - video->backlight_registered = true; - - video->pm_nb.notifier_call = acpi_video_resume; - video->pm_nb.priority = 0; - return register_pm_notifier(&video->pm_nb); -} - -static void acpi_video_dev_unregister_backlight(struct acpi_video_device *device) -{ - if (device->backlight) { - backlight_device_unregister(device->backlight); - device->backlight = NULL; - } - if (device->brightness) { - kfree(device->brightness->levels); - kfree(device->brightness); - device->brightness = NULL; - } - if (device->cooling_dev) { - sysfs_remove_link(&device->dev->dev.kobj, "thermal_cooling"); - sysfs_remove_link(&device->cooling_dev->device.kobj, "device"); - thermal_cooling_device_unregister(device->cooling_dev); - device->cooling_dev = NULL; - } -} - -static int acpi_video_bus_unregister_backlight(struct acpi_video_bus *video) -{ - struct acpi_video_device *dev; - int error; - - if (!video->backlight_registered) - return 0; - - error = unregister_pm_notifier(&video->pm_nb); - - mutex_lock(&video->device_list_lock); - list_for_each_entry(dev, &video->video_device_list, entry) - acpi_video_dev_unregister_backlight(dev); - mutex_unlock(&video->device_list_lock); - - video->backlight_registered = false; - - return error; -} - -static void acpi_video_dev_add_notify_handler(struct acpi_video_device *device) -{ - acpi_status status; - struct acpi_device *adev = device->dev; - - status = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, - acpi_video_device_notify, device); - if (ACPI_FAILURE(status)) - dev_err(&adev->dev, "Error installing notify handler\n"); - else - device->flags.notify = 1; -} - -static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video) -{ - struct input_dev *input; - struct acpi_video_device *dev; - int error; - - video->input = input = input_allocate_device(); - if (!input) { - error = -ENOMEM; - goto out; - } - - error = acpi_video_bus_start_devices(video); - if (error) - goto err_free_input; - - snprintf(video->phys, sizeof(video->phys), - "%s/video/input0", acpi_device_hid(video->device)); - - input->name = acpi_device_name(video->device); - input->phys = video->phys; - input->id.bustype = BUS_HOST; - input->id.product = 0x06; - input->dev.parent = &video->device->dev; - input->evbit[0] = BIT(EV_KEY); - set_bit(KEY_SWITCHVIDEOMODE, input->keybit); - set_bit(KEY_VIDEO_NEXT, input->keybit); - set_bit(KEY_VIDEO_PREV, input->keybit); - set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit); - set_bit(KEY_BRIGHTNESSUP, input->keybit); - set_bit(KEY_BRIGHTNESSDOWN, input->keybit); - set_bit(KEY_BRIGHTNESS_ZERO, input->keybit); - set_bit(KEY_DISPLAY_OFF, input->keybit); - - error = input_register_device(input); - if (error) - goto err_stop_dev; - - mutex_lock(&video->device_list_lock); - list_for_each_entry(dev, &video->video_device_list, entry) - acpi_video_dev_add_notify_handler(dev); - mutex_unlock(&video->device_list_lock); - - return 0; - -err_stop_dev: - acpi_video_bus_stop_devices(video); -err_free_input: - input_free_device(input); - video->input = NULL; -out: - return error; -} - -static void acpi_video_dev_remove_notify_handler(struct acpi_video_device *dev) -{ - if (dev->flags.notify) { - acpi_remove_notify_handler(dev->dev->handle, ACPI_DEVICE_NOTIFY, - acpi_video_device_notify); - dev->flags.notify = 0; - } -} - -static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video) -{ - struct acpi_video_device *dev; - - mutex_lock(&video->device_list_lock); - list_for_each_entry(dev, &video->video_device_list, entry) - acpi_video_dev_remove_notify_handler(dev); - mutex_unlock(&video->device_list_lock); - - acpi_video_bus_stop_devices(video); - input_unregister_device(video->input); - video->input = NULL; -} - -static int acpi_video_backlight_notify(struct notifier_block *nb, - unsigned long val, void *bd) -{ - struct backlight_device *backlight = bd; - struct acpi_video_bus *video; - - /* acpi_video_verify_backlight_support only cares about raw devices */ - if (backlight->props.type != BACKLIGHT_RAW) - return NOTIFY_DONE; - - video = container_of(nb, struct acpi_video_bus, backlight_nb); - - switch (val) { - case BACKLIGHT_REGISTERED: - if (!acpi_video_verify_backlight_support()) - acpi_video_bus_unregister_backlight(video); - break; - case BACKLIGHT_UNREGISTERED: - acpi_video_bus_register_backlight(video); - break; - } - - return NOTIFY_OK; -} - -static int acpi_video_bus_add_backlight_notify_handler( - struct acpi_video_bus *video) -{ - int error; - - video->backlight_nb.notifier_call = acpi_video_backlight_notify; - video->backlight_nb.priority = 0; - error = backlight_register_notifier(&video->backlight_nb); - if (error == 0) - video->backlight_notifier_registered = true; - - return error; -} - -static int acpi_video_bus_remove_backlight_notify_handler( - struct acpi_video_bus *video) -{ - if (!video->backlight_notifier_registered) - return 0; - - video->backlight_notifier_registered = false; - - return backlight_unregister_notifier(&video->backlight_nb); -} - -static int acpi_video_bus_put_devices(struct acpi_video_bus *video) -{ - struct acpi_video_device *dev, *next; - - mutex_lock(&video->device_list_lock); - list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { - list_del(&dev->entry); - kfree(dev); - } - mutex_unlock(&video->device_list_lock); - - return 0; -} - -static int instance; - -static int acpi_video_bus_add(struct acpi_device *device) -{ - struct acpi_video_bus *video; - int error; - acpi_status status; - - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, - device->parent->handle, 1, - acpi_video_bus_match, NULL, - device, NULL); - if (status == AE_ALREADY_EXISTS) { - printk(KERN_WARNING FW_BUG - "Duplicate ACPI video bus devices for the" - " same VGA controller, please try module " - "parameter \"video.allow_duplicates=1\"" - "if the current driver doesn't work.\n"); - if (!allow_duplicates) - return -ENODEV; - } - - video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL); - if (!video) - return -ENOMEM; - - /* a hack to fix the duplicate name "VID" problem on T61 */ - if (!strcmp(device->pnp.bus_id, "VID")) { - if (instance) - device->pnp.bus_id[3] = '0' + instance; - instance++; - } - /* a hack to fix the duplicate name "VGA" problem on Pa 3553 */ - if (!strcmp(device->pnp.bus_id, "VGA")) { - if (instance) - device->pnp.bus_id[3] = '0' + instance; - instance++; - } - - video->device = device; - strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); - strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); - device->driver_data = video; - - acpi_video_bus_find_cap(video); - error = acpi_video_bus_check(video); - if (error) - goto err_free_video; - - mutex_init(&video->device_list_lock); - INIT_LIST_HEAD(&video->video_device_list); - - error = acpi_video_bus_get_devices(video, device); - if (error) - goto err_put_video; - - printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", - ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), - video->flags.multihead ? "yes" : "no", - video->flags.rom ? "yes" : "no", - video->flags.post ? "yes" : "no"); - mutex_lock(&video_list_lock); - list_add_tail(&video->entry, &video_bus_head); - mutex_unlock(&video_list_lock); - - acpi_video_bus_register_backlight(video); - acpi_video_bus_add_notify_handler(video); - acpi_video_bus_add_backlight_notify_handler(video); - - return 0; - -err_put_video: - acpi_video_bus_put_devices(video); - kfree(video->attached_array); -err_free_video: - kfree(video); - device->driver_data = NULL; - - return error; -} - -static int acpi_video_bus_remove(struct acpi_device *device) -{ - struct acpi_video_bus *video = NULL; - - - if (!device || !acpi_driver_data(device)) - return -EINVAL; - - video = acpi_driver_data(device); - - acpi_video_bus_remove_backlight_notify_handler(video); - acpi_video_bus_remove_notify_handler(video); - acpi_video_bus_unregister_backlight(video); - acpi_video_bus_put_devices(video); - - mutex_lock(&video_list_lock); - list_del(&video->entry); - mutex_unlock(&video_list_lock); - - kfree(video->attached_array); - kfree(video); - - return 0; -} - -static int __init is_i740(struct pci_dev *dev) -{ - if (dev->device == 0x00D1) - return 1; - if (dev->device == 0x7000) - return 1; - return 0; -} - -static int __init intel_opregion_present(void) -{ - int opregion = 0; - struct pci_dev *dev = NULL; - u32 address; - - for_each_pci_dev(dev) { - if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) - continue; - if (dev->vendor != PCI_VENDOR_ID_INTEL) - continue; - /* We don't want to poke around undefined i740 registers */ - if (is_i740(dev)) - continue; - pci_read_config_dword(dev, 0xfc, &address); - if (!address) - continue; - opregion = 1; - } - return opregion; -} - -int acpi_video_register(void) -{ - int ret; - - if (register_count) { - /* - * if the function of acpi_video_register is already called, - * don't register the acpi_vide_bus again and return no error. - */ - return 0; - } - - mutex_init(&video_list_lock); - INIT_LIST_HEAD(&video_bus_head); - - ret = acpi_bus_register_driver(&acpi_video_bus); - if (ret) - return ret; - - /* - * When the acpi_video_bus is loaded successfully, increase - * the counter reference. - */ - register_count = 1; - - return 0; -} -EXPORT_SYMBOL(acpi_video_register); - -void acpi_video_unregister(void) -{ - if (!register_count) { - /* - * If the acpi video bus is already unloaded, don't - * unload it again and return directly. - */ - return; - } - acpi_bus_unregister_driver(&acpi_video_bus); - - register_count = 0; - - return; -} -EXPORT_SYMBOL(acpi_video_unregister); - -void acpi_video_unregister_backlight(void) -{ - struct acpi_video_bus *video; - - if (!register_count) - return; - - mutex_lock(&video_list_lock); - list_for_each_entry(video, &video_bus_head, entry) - acpi_video_bus_unregister_backlight(video); - mutex_unlock(&video_list_lock); -} -EXPORT_SYMBOL(acpi_video_unregister_backlight); - -/* - * This is kind of nasty. Hardware using Intel chipsets may require - * the video opregion code to be run first in order to initialise - * state before any ACPI video calls are made. To handle this we defer - * registration of the video class until the opregion code has run. - */ - -static int __init acpi_video_init(void) -{ - /* - * Let the module load even if ACPI is disabled (e.g. due to - * a broken BIOS) so that i915.ko can still be loaded on such - * old systems without an AcpiOpRegion. - * - * acpi_video_register() will report -ENODEV later as well due - * to acpi_disabled when i915.ko tries to register itself afterwards. - */ - if (acpi_disabled) - return 0; - - dmi_check_system(video_dmi_table); - - if (intel_opregion_present()) - return 0; - - return acpi_video_register(); -} - -static void __exit acpi_video_exit(void) -{ - acpi_video_unregister(); - - return; -} - -module_init(acpi_video_init); -module_exit(acpi_video_exit); diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index b2270ca..bb6133c 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -32,16 +32,23 @@ #include #include #include +#include #include -#include "internal.h" - ACPI_MODULE_NAME("video"); #define _COMPONENT ACPI_VIDEO_COMPONENT static long acpi_video_support; static bool acpi_video_caps_checked; +static void acpi_video_parse_cmdline(void) +{ + if (!strcmp("vendor", acpi_video_backlight_string)) + acpi_video_support |= ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR; + if (!strcmp("video", acpi_video_backlight_string)) + acpi_video_support |= ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO; +} + static acpi_status find_video(acpi_handle handle, u32 lvl, void *context, void **rv) { @@ -174,8 +181,10 @@ static void acpi_video_caps_check(void) * We must check whether the ACPI graphics device is physically plugged * in. Therefore this must be called after binding PCI and ACPI devices */ - if (!acpi_video_caps_checked) + if (!acpi_video_caps_checked) { + acpi_video_parse_cmdline(); acpi_video_get_capabilities(NULL); + } } /* Promote the vendor interface instead of the generic video module. @@ -212,23 +221,3 @@ int acpi_video_backlight_support(void) return acpi_video_support & ACPI_VIDEO_BACKLIGHT; } EXPORT_SYMBOL(acpi_video_backlight_support); - -/* - * Use acpi_backlight=vendor/video to force that backlight switching - * is processed by vendor specific acpi drivers or video.ko driver. - */ -static int __init acpi_backlight(char *str) -{ - if (str == NULL || *str == '\0') - return 1; - else { - if (!strcmp("vendor", str)) - acpi_video_support |= - ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR; - if (!strcmp("video", str)) - acpi_video_support |= - ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO; - } - return 1; -} -__setup("acpi_backlight=", acpi_backlight); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index f9f205c..909133c 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -71,9 +71,10 @@ config ASUS_LAPTOP depends on ACPI select LEDS_CLASS select NEW_LEDS - select BACKLIGHT_CLASS_DEVICE + depends on BACKLIGHT_CLASS_DEVICE depends on INPUT depends on RFKILL || RFKILL = n + depends on ACPI_VIDEO || ACPI_VIDEO = n select INPUT_SPARSEKMAP select INPUT_POLLDEV ---help--- @@ -95,6 +96,7 @@ config DELL_LAPTOP depends on X86 depends on DCDBAS depends on BACKLIGHT_CLASS_DEVICE + depends on ACPI_VIDEO || ACPI_VIDEO = n depends on RFKILL || RFKILL = n depends on SERIO_I8042 select POWER_SUPPLY @@ -109,6 +111,7 @@ config DELL_WMI tristate "Dell WMI extras" depends on ACPI_WMI depends on INPUT + depends on ACPI_VIDEO || ACPI_VIDEO = n select INPUT_SPARSEKMAP ---help--- Say Y here if you want to support WMI-based hotkeys on Dell laptops. @@ -144,6 +147,7 @@ config FUJITSU_LAPTOP depends on ACPI depends on INPUT depends on BACKLIGHT_CLASS_DEVICE + depends on ACPI_VIDEO || ACPI_VIDEO = n depends on LEDS_CLASS || LEDS_CLASS=n ---help--- This is a driver for laptops built by Fujitsu: @@ -247,6 +251,7 @@ config MSI_LAPTOP tristate "MSI Laptop Extras" depends on ACPI depends on BACKLIGHT_CLASS_DEVICE + depends on ACPI_VIDEO || ACPI_VIDEO = n depends on RFKILL depends on INPUT && SERIO_I8042 select INPUT_SPARSEKMAP @@ -280,6 +285,7 @@ config COMPAL_LAPTOP tristate "Compal (and others) Laptop Extras" depends on ACPI depends on BACKLIGHT_CLASS_DEVICE + depends on ACPI_VIDEO || ACPI_VIDEO = n depends on RFKILL depends on HWMON depends on POWER_SUPPLY @@ -296,7 +302,8 @@ config COMPAL_LAPTOP config SONY_LAPTOP tristate "Sony Laptop Extras" depends on ACPI - select BACKLIGHT_CLASS_DEVICE + depends on ACPI_VIDEO || ACPI_VIDEO = n + depends on BACKLIGHT_CLASS_DEVICE depends on INPUT depends on RFKILL ---help--- @@ -321,6 +328,7 @@ config IDEAPAD_LAPTOP depends on RFKILL && INPUT depends on SERIO_I8042 depends on BACKLIGHT_CLASS_DEVICE + depends on ACPI_VIDEO || ACPI_VIDEO = n select INPUT_SPARSEKMAP help This is a driver for Lenovo IdeaPad netbooks contains drivers for @@ -331,8 +339,8 @@ config THINKPAD_ACPI depends on ACPI depends on INPUT depends on RFKILL || RFKILL = n - select BACKLIGHT_LCD_SUPPORT - select BACKLIGHT_CLASS_DEVICE + depends on ACPI_VIDEO || ACPI_VIDEO = n + depends on BACKLIGHT_CLASS_DEVICE select HWMON select NVRAM select NEW_LEDS @@ -500,8 +508,9 @@ config EEEPC_LAPTOP depends on ACPI depends on INPUT depends on RFKILL || RFKILL = n + depends on ACPI_VIDEO || ACPI_VIDEO = n depends on HOTPLUG_PCI - select BACKLIGHT_CLASS_DEVICE + depends on BACKLIGHT_CLASS_DEVICE select HWMON select LEDS_CLASS select NEW_LEDS @@ -587,6 +596,7 @@ config MSI_WMI depends on ACPI_WMI depends on INPUT depends on BACKLIGHT_CLASS_DEVICE + depends on ACPI_VIDEO || ACPI_VIDEO = n select INPUT_SPARSEKMAP help Say Y here if you want to support WMI-based hotkeys on MSI laptops. @@ -824,6 +834,7 @@ config MXM_WMI config INTEL_OAKTRAIL tristate "Intel Oaktrail Platform Extras" depends on ACPI + depends on ACPI_VIDEO || ACPI_VIDEO = n depends on RFKILL && BACKLIGHT_CLASS_DEVICE && ACPI ---help--- Intel Oaktrail platform need this driver to provide interfaces to diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 913a1c1..f097c0a 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -243,6 +243,7 @@ extern bool wmi_has_guid(const char *guid); #define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR 0x0400 #define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO 0x0800 +extern char acpi_video_backlight_string[]; extern long acpi_is_video_device(acpi_handle handle); #if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) -- cgit v0.10.2 From 87521e16a7abbf3fa337f56cb4d1e18247f15e8a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:48 +0200 Subject: acpi-video-detect: Rewrite backlight interface selection logic Currently we have 2 kernel commandline options + dmi-quirks in 3 places all interacting (in interesting ways) to select which which backlight interface to use. On the commandline we've acpi_backlight=[video|vendor] and video.use_native_backlight=[0|1]. DMI quirks we have in acpi/video-detect.c, acpi/video.c and drivers/platform/x86/*.c . This commit is the first step to cleaning this up, replacing the 2 cmdline options with just acpi_backlight=[video|vendor|native|none], and adds a new API to video_detect.c to reflect this. Follow up commits will also move other related code, like unregistering the acpi_video backlight interface if it was registered before other drivers which take priority over it are loaded, to video_detect.c where this logic really belongs. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index bb6133c..0f9586b 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -1,4 +1,6 @@ /* + * Copyright (C) 2015 Red Hat Inc. + * Hans de Goede * Copyright (C) 2008 SuSE Linux Products GmbH * Thomas Renninger * @@ -9,44 +11,45 @@ * acpi_get_pci_dev() can be called to identify ACPI graphics * devices for which a real graphics card is plugged in * - * Now acpi_video_get_capabilities() can be called to check which - * capabilities the graphics cards plugged in support. The check for general - * video capabilities will be triggered by the first caller of - * acpi_video_get_capabilities(NULL); which will happen when the first - * backlight switching supporting driver calls: - * acpi_video_backlight_support(); - * * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B) * are available, video.ko should be used to handle the device. * * Otherwise vendor specific drivers like thinkpad_acpi, asus-laptop, * sony_acpi,... can take care about backlight brightness. * - * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m) - * this file will not be compiled, acpi_video_get_capabilities() and - * acpi_video_backlight_support() will always return 0 and vendor specific - * drivers always can handle backlight. + * Backlight drivers can use acpi_video_get_backlight_type() to determine + * which driver should handle the backlight. * + * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m) + * this file will not be compiled and acpi_video_get_backlight_type() will + * always return acpi_backlight_vendor. */ #include #include +#include #include #include #include +#include +#include ACPI_MODULE_NAME("video"); #define _COMPONENT ACPI_VIDEO_COMPONENT -static long acpi_video_support; -static bool acpi_video_caps_checked; +static enum acpi_backlight_type acpi_backlight_cmdline = acpi_backlight_undef; +static enum acpi_backlight_type acpi_backlight_dmi = acpi_backlight_undef; static void acpi_video_parse_cmdline(void) { if (!strcmp("vendor", acpi_video_backlight_string)) - acpi_video_support |= ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR; + acpi_backlight_cmdline = acpi_backlight_vendor; if (!strcmp("video", acpi_video_backlight_string)) - acpi_video_support |= ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO; + acpi_backlight_cmdline = acpi_backlight_video; + if (!strcmp("native", acpi_video_backlight_string)) + acpi_backlight_cmdline = acpi_backlight_native; + if (!strcmp("none", acpi_video_backlight_string)) + acpi_backlight_cmdline = acpi_backlight_none; } static acpi_status @@ -77,7 +80,7 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv) * buggy */ static int video_detect_force_vendor(const struct dmi_system_id *d) { - acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; + acpi_backlight_dmi = acpi_backlight_vendor; return 0; } @@ -125,99 +128,91 @@ static const struct dmi_system_id video_detect_dmi_table[] = { }; /* - * Returns the video capabilities of a specific ACPI graphics device + * Determine which type of backlight interface to use on this system, + * First check cmdline, then dmi quirks, then do autodetect. + * + * The autodetect order is: + * 1) Is the acpi-video backlight interface supported -> + * no, use a vendor interface + * 2) Is this a win8 "ready" BIOS and do we have a native interface -> + * yes, use a native interface + * 3) Else use the acpi-video interface * - * if NULL is passed as argument all ACPI devices are enumerated and - * all graphics capabilities of physically present devices are - * summarized and returned. This is cached and done only once. + * Arguably the native on win8 check should be done first, but that would + * be a behavior change, which may causes issues. */ -static long acpi_video_get_capabilities(acpi_handle graphics_handle) +enum acpi_backlight_type acpi_video_get_backlight_type(void) { - long caps = 0; - struct acpi_device *tmp_dev; - acpi_status status; - - if (acpi_video_caps_checked && graphics_handle == NULL) - return acpi_video_support; - - if (!graphics_handle) { - /* Only do the global walk through all graphics devices once */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_video, NULL, - &caps, NULL); - /* There might be boot param flags set already... */ - acpi_video_support |= caps; - acpi_video_caps_checked = 1; - /* Add blacklists here. Be careful to use the right *DMI* bits - * to still be able to override logic via boot params, e.g.: - * - * if (dmi_name_in_vendors("XY")) { - * acpi_video_support |= - * ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; - *} - */ + static DEFINE_MUTEX(init_mutex); + static bool init_done; + static long video_caps; + /* Parse cmdline, dmi and acpi only once */ + mutex_lock(&init_mutex); + if (!init_done) { + acpi_video_parse_cmdline(); dmi_check_system(video_detect_dmi_table); - } else { - status = acpi_bus_get_device(graphics_handle, &tmp_dev); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Invalid device")); - return 0; - } - acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle, + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_video, NULL, - &caps, NULL); + &video_caps, NULL); + init_done = true; } - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %s %s\n", - graphics_handle ? caps : acpi_video_support, - graphics_handle ? "on device " : "in general", - graphics_handle ? acpi_device_bid(tmp_dev) : "")); - return caps; + mutex_unlock(&init_mutex); + + if (acpi_backlight_cmdline != acpi_backlight_undef) + return acpi_backlight_cmdline; + + if (acpi_backlight_dmi != acpi_backlight_undef) + return acpi_backlight_dmi; + + if (!(video_caps & ACPI_VIDEO_BACKLIGHT)) + return acpi_backlight_vendor; + + if (acpi_osi_is_win8() && backlight_device_registered(BACKLIGHT_RAW)) + return acpi_backlight_native; + + return acpi_backlight_video; } +EXPORT_SYMBOL(acpi_video_get_backlight_type); -static void acpi_video_caps_check(void) +/* + * Set the preferred backlight interface type based on DMI info. + * This function allows DMI blacklists to be implemented by external + * platform drivers instead of putting a big blacklist in video_detect.c + */ +void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type) { - /* - * We must check whether the ACPI graphics device is physically plugged - * in. Therefore this must be called after binding PCI and ACPI devices - */ - if (!acpi_video_caps_checked) { - acpi_video_parse_cmdline(); - acpi_video_get_capabilities(NULL); - } + acpi_backlight_dmi = type; } +EXPORT_SYMBOL(acpi_video_set_dmi_backlight_type); -/* Promote the vendor interface instead of the generic video module. - * This function allow DMI blacklists to be implemented by externals - * platform drivers instead of putting a big blacklist in video_detect.c +/* + * Compatiblity function, this is going away as soon as all drivers are + * converted to acpi_video_set_dmi_backlight_type(). + * + * Promote the vendor interface instead of the generic video module. * After calling this function you will probably want to call * acpi_video_unregister() to make sure the video module is not loaded */ void acpi_video_dmi_promote_vendor(void) { - acpi_video_caps_check(); - acpi_video_support |= ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; + acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); } EXPORT_SYMBOL(acpi_video_dmi_promote_vendor); -/* Returns true if video.ko can do backlight switching */ +/* + * Compatiblity function, this is going away as soon as all drivers are + * converted to acpi_video_get_backlight_type(). + * + * Returns true if video.ko can do backlight switching. + */ int acpi_video_backlight_support(void) { - acpi_video_caps_check(); - - /* First check for boot param -> highest prio */ - if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR) - return 0; - else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO) - return 1; - - /* Then check for DMI blacklist -> second highest prio */ - if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VENDOR) - return 0; - else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VIDEO) - return 1; - - /* Then go the default way */ - return acpi_video_support & ACPI_VIDEO_BACKLIGHT; + /* + * This is done this way since vendor drivers call this to see + * if they should load, and we do not want them to load for both + * the acpi_backlight_video and acpi_backlight_native cases. + */ + return acpi_video_get_backlight_type() != acpi_backlight_vendor; } EXPORT_SYMBOL(acpi_video_backlight_support); diff --git a/include/acpi/video.h b/include/acpi/video.h index 843ef1a..01b5cc7 100644 --- a/include/acpi/video.h +++ b/include/acpi/video.h @@ -16,6 +16,14 @@ struct acpi_device; #define ACPI_VIDEO_DISPLAY_LEGACY_PANEL 0x0110 #define ACPI_VIDEO_DISPLAY_LEGACY_TV 0x0200 +enum acpi_backlight_type { + acpi_backlight_undef = -1, + acpi_backlight_none = 0, + acpi_backlight_video, + acpi_backlight_vendor, + acpi_backlight_native, +}; + #if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE) extern int acpi_video_register(void); extern void acpi_video_unregister(void); @@ -23,6 +31,8 @@ extern void acpi_video_unregister_backlight(void); extern int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, void **edid); extern bool acpi_video_verify_backlight_support(void); +extern enum acpi_backlight_type acpi_video_get_backlight_type(void); +extern void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type); #else static inline int acpi_video_register(void) { return 0; } static inline void acpi_video_unregister(void) { return; } @@ -33,6 +43,13 @@ static inline int acpi_video_get_edid(struct acpi_device *device, int type, return -ENODEV; } static inline bool acpi_video_verify_backlight_support(void) { return false; } +static inline enum acpi_backlight_type acpi_video_get_backlight_type(void) +{ + return acpi_backlight_vendor; +} +static void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type) +{ +} #endif #endif -- cgit v0.10.2 From 5fd677b75d2eefde6b8e3f510a5c2d4540eba1fe Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:49 +0200 Subject: acpi-video-detect: Unregister acpi_video backlight when dmi quirks are added Make acpi_video_set_dmi_backlight_type() call acpi_video_unregister_backlight() when the new dmi quirk results in the desired backlight interface being of a type other then acpi_backlight_video. This avoid the need for the second if in the following construction which is currently found in many platform/x86 drivers: if (prefer_vendor_quirk) acpi_video_dmi_promote_vendor(); if (!acpi_video_backlight_support()) acpi_video_unregister_backlight() This second if-block will be removed from the platform drivers as part of their conversion to the new backlight interface selection API. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 0f9586b..d00c0f4 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -183,6 +183,9 @@ EXPORT_SYMBOL(acpi_video_get_backlight_type); void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type) { acpi_backlight_dmi = type; + /* Remove acpi-video backlight interface if it is no longer desired */ + if (acpi_video_get_backlight_type() != acpi_backlight_video) + acpi_video_unregister_backlight(); } EXPORT_SYMBOL(acpi_video_set_dmi_backlight_type); -- cgit v0.10.2 From da882e5acbebd2aa8bb076521a5c5c9c70be324e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:50 +0200 Subject: drm: i915: Port to new backlight interface selection API This results in a nice cleanup, as we can replace the complicated logic from should_ignore_backlight_request() with a simple check for the type being native. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c index 71e87ab..4813374 100644 --- a/drivers/gpu/drm/i915/intel_opregion.c +++ b/drivers/gpu/drm/i915/intel_opregion.c @@ -396,16 +396,6 @@ int intel_opregion_notify_adapter(struct drm_device *dev, pci_power_t state) return -EINVAL; } -/* - * If the vendor backlight interface is not in use and ACPI backlight interface - * is broken, do not bother processing backlight change requests from firmware. - */ -static bool should_ignore_backlight_request(void) -{ - return acpi_video_backlight_support() && - !acpi_video_verify_backlight_support(); -} - static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -414,7 +404,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) DRM_DEBUG_DRIVER("bclp = 0x%08x\n", bclp); - if (should_ignore_backlight_request()) { + if (acpi_video_get_backlight_type() == acpi_backlight_native) { DRM_DEBUG_KMS("opregion backlight request ignored\n"); return 0; } -- cgit v0.10.2 From 3bd6bce36975f571cb47fa9d0aac9e06f94f4028 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:51 +0200 Subject: ACPI / video: Port to new backlight interface selection API Most of the patch is moving the dmi quirks for forcing use of the acpi-video / the native backlight interface to video_detect.c. What remains is a nice cleanup. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index db4f353..bba0d87 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -78,20 +78,6 @@ module_param(brightness_switch_enabled, bool, 0644); static bool allow_duplicates; module_param(allow_duplicates, bool, 0644); -/* - * For Windows 8 systems: used to decide if video module - * should skip registering backlight interface of its own. - */ -enum { - NATIVE_BACKLIGHT_NOT_SET = -1, - NATIVE_BACKLIGHT_OFF, - NATIVE_BACKLIGHT_ON, -}; - -static int use_native_backlight_param = NATIVE_BACKLIGHT_NOT_SET; -module_param_named(use_native_backlight, use_native_backlight_param, int, 0444); -static int use_native_backlight_dmi = NATIVE_BACKLIGHT_NOT_SET; - static int disable_backlight_sysfs_if = -1; module_param(disable_backlight_sysfs_if, int, 0444); @@ -244,24 +230,6 @@ static int acpi_video_get_next_level(struct acpi_video_device *device, u32 level_current, u32 event); static void acpi_video_switch_brightness(struct work_struct *work); -static bool acpi_video_use_native_backlight(void) -{ - if (use_native_backlight_param != NATIVE_BACKLIGHT_NOT_SET) - return use_native_backlight_param; - else if (use_native_backlight_dmi != NATIVE_BACKLIGHT_NOT_SET) - return use_native_backlight_dmi; - return acpi_osi_is_win8(); -} - -bool acpi_video_verify_backlight_support(void) -{ - if (acpi_video_use_native_backlight() && - backlight_device_registered(BACKLIGHT_RAW)) - return false; - return acpi_video_backlight_support(); -} -EXPORT_SYMBOL_GPL(acpi_video_verify_backlight_support); - /* backlight device sysfs support */ static int acpi_video_get_brightness(struct backlight_device *bd) { @@ -422,18 +390,6 @@ static int __init video_set_bqc_offset(const struct dmi_system_id *d) return 0; } -static int __init video_disable_native_backlight(const struct dmi_system_id *d) -{ - use_native_backlight_dmi = NATIVE_BACKLIGHT_OFF; - return 0; -} - -static int __init video_enable_native_backlight(const struct dmi_system_id *d) -{ - use_native_backlight_dmi = NATIVE_BACKLIGHT_ON; - return 0; -} - static int __init video_disable_backlight_sysfs_if( const struct dmi_system_id *d) { @@ -488,123 +444,6 @@ static struct dmi_system_id video_dmi_table[] __initdata = { }, /* - * These models have a working acpi_video backlight control, and using - * native backlight causes a regression where backlight does not work - * when userspace is not handling brightness key events. Disable - * native_backlight on these to fix this: - * https://bugzilla.kernel.org/show_bug.cgi?id=81691 - */ - { - .callback = video_disable_native_backlight, - .ident = "ThinkPad T420", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"), - }, - }, - { - .callback = video_disable_native_backlight, - .ident = "ThinkPad T520", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"), - }, - }, - { - .callback = video_disable_native_backlight, - .ident = "ThinkPad X201s", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"), - }, - }, - - /* The native backlight controls do not work on some older machines */ - { - /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */ - .callback = video_disable_native_backlight, - .ident = "HP ENVY 15 Notebook", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"), - }, - }, - - { - .callback = video_disable_native_backlight, - .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"), - }, - }, - { - .callback = video_disable_native_backlight, - .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "370R4E/370R4V/370R5E/3570RE/370R5V"), - }, - }, - { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */ - .callback = video_disable_native_backlight, - .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "3570R/370R/470R/450R/510R/4450RV"), - }, - }, - { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */ - .callback = video_disable_native_backlight, - .ident = "SAMSUNG 730U3E/740U3E", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), - }, - }, - { - /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */ - .callback = video_disable_native_backlight, - .ident = "SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "900X3C/900X3D/900X3E/900X4C/900X4D"), - }, - }, - - { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */ - .callback = video_disable_native_backlight, - .ident = "Dell XPS15 L521X", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"), - }, - }, - - /* Non win8 machines which need native backlight nevertheless */ - { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ - .callback = video_enable_native_backlight, - .ident = "Lenovo Ideapad Z570", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "102434U"), - }, - }, - { - /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */ - .callback = video_enable_native_backlight, - .ident = "Apple MacBook Pro 12,1", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"), - }, - }, - - /* * Some machines have a broken acpi-video interface for brightness * control, but still need an acpi_video_device_lcd_set_level() call * on resume to turn the backlight power on. We Enable backlight @@ -1831,7 +1670,7 @@ static int acpi_video_bus_register_backlight(struct acpi_video_bus *video) acpi_video_run_bcl_for_osi(video); - if (!acpi_video_verify_backlight_support()) + if (acpi_video_get_backlight_type() != acpi_backlight_video) return 0; mutex_lock(&video->device_list_lock); @@ -1980,20 +1819,23 @@ static int acpi_video_backlight_notify(struct notifier_block *nb, { struct backlight_device *backlight = bd; struct acpi_video_bus *video; + enum acpi_backlight_type type; - /* acpi_video_verify_backlight_support only cares about raw devices */ + /* A raw bl (un)registering may change native <-> video */ if (backlight->props.type != BACKLIGHT_RAW) return NOTIFY_DONE; video = container_of(nb, struct acpi_video_bus, backlight_nb); + type = acpi_video_get_backlight_type(); switch (val) { case BACKLIGHT_REGISTERED: - if (!acpi_video_verify_backlight_support()) + if (type != acpi_backlight_video) acpi_video_bus_unregister_backlight(video); break; case BACKLIGHT_UNREGISTERED: - acpi_video_bus_register_backlight(video); + if (type == acpi_backlight_video) + acpi_video_bus_register_backlight(video); break; } diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index d00c0f4..d024ea0 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -84,6 +84,18 @@ static int video_detect_force_vendor(const struct dmi_system_id *d) return 0; } +static int video_detect_force_video(const struct dmi_system_id *d) +{ + acpi_backlight_dmi = acpi_backlight_video; + return 0; +} + +static int video_detect_force_native(const struct dmi_system_id *d) +{ + acpi_backlight_dmi = acpi_backlight_native; + return 0; +} + static const struct dmi_system_id video_detect_dmi_table[] = { /* On Samsung X360, the BIOS will set a flag (VDRV) if generic * ACPI backlight device is used. This flag will definitively break @@ -124,6 +136,124 @@ static const struct dmi_system_id video_detect_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5737"), }, }, + + /* + * These models have a working acpi_video backlight control, and using + * native backlight causes a regression where backlight does not work + * when userspace is not handling brightness key events. Disable + * native_backlight on these to fix this: + * https://bugzilla.kernel.org/show_bug.cgi?id=81691 + */ + { + .callback = video_detect_force_video, + .ident = "ThinkPad T420", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T420"), + }, + }, + { + .callback = video_detect_force_video, + .ident = "ThinkPad T520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T520"), + }, + }, + { + .callback = video_detect_force_video, + .ident = "ThinkPad X201s", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X201s"), + }, + }, + + /* The native backlight controls do not work on some older machines */ + { + /* https://bugs.freedesktop.org/show_bug.cgi?id=81515 */ + .callback = video_detect_force_video, + .ident = "HP ENVY 15 Notebook", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP ENVY 15 Notebook PC"), + }, + }, + { + .callback = video_detect_force_video, + .ident = "SAMSUNG 870Z5E/880Z5E/680Z5E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "870Z5E/880Z5E/680Z5E"), + }, + }, + { + .callback = video_detect_force_video, + .ident = "SAMSUNG 370R4E/370R4V/370R5E/3570RE/370R5V", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, + "370R4E/370R4V/370R5E/3570RE/370R5V"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */ + .callback = video_detect_force_video, + .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, + "3570R/370R/470R/450R/510R/4450RV"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */ + .callback = video_detect_force_video, + .ident = "SAMSUNG 730U3E/740U3E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), + }, + }, + { + /* https://bugs.freedesktop.org/show_bug.cgi?id=87286 */ + .callback = video_detect_force_video, + .ident = "SAMSUNG 900X3C/900X3D/900X3E/900X4C/900X4D", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, + "900X3C/900X3D/900X3E/900X4C/900X4D"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */ + .callback = video_detect_force_video, + .ident = "Dell XPS15 L521X", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"), + }, + }, + + /* Non win8 machines which need native backlight nevertheless */ + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1187004 */ + .callback = video_detect_force_native, + .ident = "Lenovo Ideapad Z570", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "102434U"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1217249 */ + .callback = video_detect_force_native, + .ident = "Apple MacBook Pro 12,1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro12,1"), + }, + }, { }, }; diff --git a/include/acpi/video.h b/include/acpi/video.h index 01b5cc7..47c7ad6 100644 --- a/include/acpi/video.h +++ b/include/acpi/video.h @@ -30,7 +30,6 @@ extern void acpi_video_unregister(void); extern void acpi_video_unregister_backlight(void); extern int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, void **edid); -extern bool acpi_video_verify_backlight_support(void); extern enum acpi_backlight_type acpi_video_get_backlight_type(void); extern void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type); #else @@ -42,7 +41,6 @@ static inline int acpi_video_get_edid(struct acpi_device *device, int type, { return -ENODEV; } -static inline bool acpi_video_verify_backlight_support(void) { return false; } static inline enum acpi_backlight_type acpi_video_get_backlight_type(void) { return acpi_backlight_vendor; -- cgit v0.10.2 From 93a291dfaf9c328ca5a9cea1733af1a128efe890 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:52 +0200 Subject: ACPI / video: Move backlight notifier to video_detect.c Move the unregistering of the acpi backlight interface on registering of a native backlight from video.c to video_detect.c where it belongs. Note this removes support for re-registering the acpi backlight interface when the native interface goes away. In practice this never happens and it needlessly complicates the code. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index bba0d87..23490f0 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -87,6 +87,7 @@ static struct list_head video_bus_head; static int acpi_video_bus_add(struct acpi_device *device); static int acpi_video_bus_remove(struct acpi_device *device); static void acpi_video_bus_notify(struct acpi_device *device, u32 event); +void acpi_video_detect_exit(void); static const struct acpi_device_id video_device_ids[] = { {ACPI_VIDEO_HID, 0}, @@ -146,7 +147,6 @@ struct acpi_video_enumerated_device { struct acpi_video_bus { struct acpi_device *device; bool backlight_registered; - bool backlight_notifier_registered; u8 dos_setting; struct acpi_video_enumerated_device *attached_array; u8 attached_count; @@ -159,7 +159,6 @@ struct acpi_video_bus { struct input_dev *input; char phys[32]; /* for input device */ struct notifier_block pm_nb; - struct notifier_block backlight_nb; }; struct acpi_video_device_flags { @@ -1814,59 +1813,6 @@ static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video) video->input = NULL; } -static int acpi_video_backlight_notify(struct notifier_block *nb, - unsigned long val, void *bd) -{ - struct backlight_device *backlight = bd; - struct acpi_video_bus *video; - enum acpi_backlight_type type; - - /* A raw bl (un)registering may change native <-> video */ - if (backlight->props.type != BACKLIGHT_RAW) - return NOTIFY_DONE; - - video = container_of(nb, struct acpi_video_bus, backlight_nb); - type = acpi_video_get_backlight_type(); - - switch (val) { - case BACKLIGHT_REGISTERED: - if (type != acpi_backlight_video) - acpi_video_bus_unregister_backlight(video); - break; - case BACKLIGHT_UNREGISTERED: - if (type == acpi_backlight_video) - acpi_video_bus_register_backlight(video); - break; - } - - return NOTIFY_OK; -} - -static int acpi_video_bus_add_backlight_notify_handler( - struct acpi_video_bus *video) -{ - int error; - - video->backlight_nb.notifier_call = acpi_video_backlight_notify; - video->backlight_nb.priority = 0; - error = backlight_register_notifier(&video->backlight_nb); - if (error == 0) - video->backlight_notifier_registered = true; - - return error; -} - -static int acpi_video_bus_remove_backlight_notify_handler( - struct acpi_video_bus *video) -{ - if (!video->backlight_notifier_registered) - return 0; - - video->backlight_notifier_registered = false; - - return backlight_unregister_notifier(&video->backlight_nb); -} - static int acpi_video_bus_put_devices(struct acpi_video_bus *video) { struct acpi_video_device *dev, *next; @@ -1948,7 +1894,6 @@ static int acpi_video_bus_add(struct acpi_device *device) acpi_video_bus_register_backlight(video); acpi_video_bus_add_notify_handler(video); - acpi_video_bus_add_backlight_notify_handler(video); return 0; @@ -1972,7 +1917,6 @@ static int acpi_video_bus_remove(struct acpi_device *device) video = acpi_driver_data(device); - acpi_video_bus_remove_backlight_notify_handler(video); acpi_video_bus_remove_notify_handler(video); acpi_video_bus_unregister_backlight(video); acpi_video_bus_put_devices(video); @@ -2108,6 +2052,7 @@ static int __init acpi_video_init(void) static void __exit acpi_video_exit(void) { + acpi_video_detect_exit(); acpi_video_unregister(); return; diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index d024ea0..0df1567 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -37,6 +37,9 @@ ACPI_MODULE_NAME("video"); #define _COMPONENT ACPI_VIDEO_COMPONENT +static bool backlight_notifier_registered; +static struct notifier_block backlight_nb; + static enum acpi_backlight_type acpi_backlight_cmdline = acpi_backlight_undef; static enum acpi_backlight_type acpi_backlight_dmi = acpi_backlight_undef; @@ -257,6 +260,20 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { }, }; +static int acpi_video_backlight_notify(struct notifier_block *nb, + unsigned long val, void *bd) +{ + struct backlight_device *backlight = bd; + + /* A raw bl registering may change video -> native */ + if (backlight->props.type == BACKLIGHT_RAW && + val == BACKLIGHT_REGISTERED && + acpi_video_get_backlight_type() != acpi_backlight_video) + acpi_video_unregister_backlight(); + + return NOTIFY_OK; +} + /* * Determine which type of backlight interface to use on this system, * First check cmdline, then dmi quirks, then do autodetect. @@ -285,6 +302,10 @@ enum acpi_backlight_type acpi_video_get_backlight_type(void) acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_video, NULL, &video_caps, NULL); + backlight_nb.notifier_call = acpi_video_backlight_notify; + backlight_nb.priority = 0; + if (backlight_register_notifier(&backlight_nb) == 0) + backlight_notifier_registered = true; init_done = true; } mutex_unlock(&init_mutex); @@ -349,3 +370,9 @@ int acpi_video_backlight_support(void) return acpi_video_get_backlight_type() != acpi_backlight_vendor; } EXPORT_SYMBOL(acpi_video_backlight_support); + +void __exit acpi_video_detect_exit(void) +{ + if (backlight_notifier_registered) + backlight_unregister_notifier(&backlight_nb); +} -- cgit v0.10.2 From 7ee33baabcae765e1eda73b988a62fe102c0ce76 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:53 +0200 Subject: ACPI / video: Move dmi_check_system from module_init to acpi_video_register When builtin there is no guarantee in which order module_init functions are run, so acpi_video_register() may get called from the i915 driver (if it is also builtin) before acpi_video_init() gets called, resulting in the dmi quirks not yet being parsed. This commit moves the dmi_check_system() call to acpi_video_register(), so that we can be sure the dmi quirks have always been applied before probing. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 23490f0..efa0ab5 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -383,13 +383,13 @@ acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) */ static int bqc_offset_aml_bug_workaround; -static int __init video_set_bqc_offset(const struct dmi_system_id *d) +static int video_set_bqc_offset(const struct dmi_system_id *d) { bqc_offset_aml_bug_workaround = 9; return 0; } -static int __init video_disable_backlight_sysfs_if( +static int video_disable_backlight_sysfs_if( const struct dmi_system_id *d) { if (disable_backlight_sysfs_if == -1) @@ -397,7 +397,7 @@ static int __init video_disable_backlight_sysfs_if( return 0; } -static struct dmi_system_id video_dmi_table[] __initdata = { +static struct dmi_system_id video_dmi_table[] = { /* * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 */ @@ -1977,6 +1977,8 @@ int acpi_video_register(void) mutex_init(&video_list_lock); INIT_LIST_HEAD(&video_bus_head); + dmi_check_system(video_dmi_table); + ret = acpi_bus_register_driver(&acpi_video_bus); if (ret) return ret; @@ -2042,8 +2044,6 @@ static int __init acpi_video_init(void) if (acpi_disabled) return 0; - dmi_check_system(video_dmi_table); - if (intel_opregion_present()) return 0; -- cgit v0.10.2 From 2a8b18e9fb650ab5c637005a7506045c46ed2b0e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:54 +0200 Subject: ACPI / video: Fix acpi_video _register vs _unregister_backlight race It is possible for a native backlight driver to load while acpi_video_register is running, which may lead to acpi_video_unregister_backlight being called while acpi_video_register is running and the 2 racing against eachother. The register_count variable protects against this, but not in a thread safe manner, this commit adds locking to make this thread safe. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index efa0ab5..5b877a1 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -82,6 +82,7 @@ static int disable_backlight_sysfs_if = -1; module_param(disable_backlight_sysfs_if, int, 0444); static int register_count; +static DEFINE_MUTEX(register_count_mutex); static struct mutex video_list_lock; static struct list_head video_bus_head; static int acpi_video_bus_add(struct acpi_device *device); @@ -1964,14 +1965,15 @@ static int __init intel_opregion_present(void) int acpi_video_register(void) { - int ret; + int ret = 0; + mutex_lock(®ister_count_mutex); if (register_count) { /* * if the function of acpi_video_register is already called, * don't register the acpi_vide_bus again and return no error. */ - return 0; + goto leave; } mutex_init(&video_list_lock); @@ -1981,7 +1983,7 @@ int acpi_video_register(void) ret = acpi_bus_register_driver(&acpi_video_bus); if (ret) - return ret; + goto leave; /* * When the acpi_video_bus is loaded successfully, increase @@ -1989,24 +1991,20 @@ int acpi_video_register(void) */ register_count = 1; - return 0; +leave: + mutex_unlock(®ister_count_mutex); + return ret; } EXPORT_SYMBOL(acpi_video_register); void acpi_video_unregister(void) { - if (!register_count) { - /* - * If the acpi video bus is already unloaded, don't - * unload it again and return directly. - */ - return; + mutex_lock(®ister_count_mutex); + if (register_count) { + acpi_bus_unregister_driver(&acpi_video_bus); + register_count = 0; } - acpi_bus_unregister_driver(&acpi_video_bus); - - register_count = 0; - - return; + mutex_unlock(®ister_count_mutex); } EXPORT_SYMBOL(acpi_video_unregister); @@ -2014,13 +2012,14 @@ void acpi_video_unregister_backlight(void) { struct acpi_video_bus *video; - if (!register_count) - return; - - mutex_lock(&video_list_lock); - list_for_each_entry(video, &video_bus_head, entry) - acpi_video_bus_unregister_backlight(video); - mutex_unlock(&video_list_lock); + mutex_lock(®ister_count_mutex); + if (register_count) { + mutex_lock(&video_list_lock); + list_for_each_entry(video, &video_bus_head, entry) + acpi_video_bus_unregister_backlight(video); + mutex_unlock(&video_list_lock); + } + mutex_unlock(®ister_count_mutex); } EXPORT_SYMBOL(acpi_video_unregister_backlight); -- cgit v0.10.2 From 9a65f0df6392a02c08637e01860f3263f60736d3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:55 +0200 Subject: acer-wmi: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. This commit also removes various obsolete pr_xxx messages related to backlight interface selection. These are obsolete because they assume there is only a vendor or acpi backlight driver and no other choice. Also they are not necessary, if the user wants to know which backlight interfaces are registered a simple "ls /sys/class/backlight" suffices. Signed-off-by: Hans de Goede Reviewed-by: Lee, Chun-Yi Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 3ac29a1..f6b280d 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -2246,14 +2246,10 @@ static int __init acer_wmi_init(void) set_quirks(); if (dmi_check_system(video_vendor_dmi_table)) - acpi_video_dmi_promote_vendor(); - if (acpi_video_backlight_support()) { + acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); + + if (acpi_video_get_backlight_type() != acpi_backlight_vendor) interface->capability &= ~ACER_CAP_BRIGHTNESS; - pr_info("Brightness must be controlled by acpi video driver\n"); - } else { - pr_info("Disabling ACPI video driver\n"); - acpi_video_unregister_backlight(); - } if (wmi_has_guid(WMID_GUID3)) { if (ec_raw_mode) { -- cgit v0.10.2 From 86ac056a30ef6af933c33f096c8275dc4977244c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:56 +0200 Subject: apple-gmux: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index a7f6412..0dec3f5 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -550,8 +550,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) * backlight control and supports more levels than other options. * Disable the other backlight choices. */ - acpi_video_dmi_promote_vendor(); - acpi_video_unregister_backlight(); + acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); apple_bl_unregister(); gmux_data->power_state = VGA_SWITCHEROO_ON; -- cgit v0.10.2 From 8b9e6b70a14aa52f9de95e8e0fac543a8ae1a1c3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:57 +0200 Subject: asus-laptop: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. This commit also removes various obsolete pr_xxx messages related to backlight interface selection. These are obsolete because they assume there is only a vendor or acpi backlight driver and no other choice. Also they are not necessary, if the user wants to know which backlight interfaces are registered a simple "ls /sys/class/backlight" suffices. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 46b2746..58d29c4 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -54,6 +54,7 @@ #include #include #include +#include #define ASUS_LAPTOP_VERSION "0.42" @@ -1884,12 +1885,11 @@ static int asus_acpi_add(struct acpi_device *device) if (result) goto fail_platform; - if (!acpi_video_backlight_support()) { + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { result = asus_backlight_init(asus); if (result) goto fail_backlight; - } else - pr_info("Backlight controlled by ACPI video driver\n"); + } result = asus_input_init(asus); if (result) -- cgit v0.10.2 From 62c4aa1a8d9ec3e7e9391ca46f8abf040499544e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:58 +0200 Subject: asus-wmi: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. This commit also removes various obsolete pr_xxx messages related to backlight interface selection. These are obsolete because they assume there is only a vendor or acpi backlight driver and no other choice. Also they are not necessary, if the user wants to know which backlight interfaces are registered a simple "ls /sys/class/backlight" suffices. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 945145d..6f8558f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1364,7 +1364,7 @@ static void asus_wmi_notify(u32 value, void *context) code = ASUS_WMI_BRN_DOWN; if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) { - if (!acpi_video_backlight_support()) { + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { asus_wmi_backlight_notify(asus, orig_code); goto exit; } @@ -1772,17 +1772,16 @@ static int asus_wmi_add(struct platform_device *pdev) stop this from showing up */ chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); if (chassis_type && !strcmp(chassis_type, "3")) - acpi_video_dmi_promote_vendor(); + acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); + if (asus->driver->quirks->wmi_backlight_power) - acpi_video_dmi_promote_vendor(); - if (!acpi_video_backlight_support()) { - pr_info("Disabling ACPI video driver\n"); - acpi_video_unregister_backlight(); + acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); + + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { err = asus_wmi_backlight_init(asus); if (err && err != -ENODEV) goto fail_backlight; - } else - pr_info("Backlight controlled by ACPI video driver\n"); + } status = wmi_install_notify_handler(asus->driver->event_guid, asus_wmi_notify, asus); -- cgit v0.10.2 From 358fefe78e8b5602bc9a790647674f4645d9c8ef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:27:59 +0200 Subject: compal-laptop: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index b4e9447..f2706d2 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -82,7 +82,7 @@ #include #include #include - +#include /* ======= */ /* Defines */ @@ -959,7 +959,7 @@ static int __init compal_init(void) return -ENODEV; } - if (!acpi_video_backlight_support()) { + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { struct backlight_properties props; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_PLATFORM; -- cgit v0.10.2 From ee4cfe28ca2969e3ba5a178aa085ea2fe3617e4d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:00 +0200 Subject: dell-laptop: Port to new backlight interface selection API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port the backlight selection logic to the new backlight interface selection API. Signed-off-by: Hans de Goede Acked-by: Pali Rohár Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index d688d80..01d0810 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "../../firmware/dcdbas.h" #define BRIGHTNESS_TOKEN 0x7d @@ -1920,13 +1921,8 @@ static int __init dell_init(void) debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, &dell_debugfs_fops); -#ifdef CONFIG_ACPI - /* In the event of an ACPI backlight being available, don't - * register the platform controller. - */ - if (acpi_video_backlight_support()) + if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return 0; -#endif get_buffer(); buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); -- cgit v0.10.2 From 18bd769623df7d24dc623900a60a1396bc8a4301 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:01 +0200 Subject: dell-wmi: Port to new backlight interface selection API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Port the backlight selection logic to the new backlight interface selection API. Signed-off-by: Hans de Goede Acked-by: Pali Rohár Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 6512a06..f2d77fe 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -35,6 +35,7 @@ #include #include #include +#include MODULE_AUTHOR("Matthew Garrett "); MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver"); @@ -397,7 +398,7 @@ static int __init dell_wmi_init(void) } dmi_walk(find_hk_type, NULL); - acpi_video = acpi_video_backlight_support(); + acpi_video = acpi_video_get_backlight_type() != acpi_backlight_vendor; err = dell_wmi_input_setup(); if (err) -- cgit v0.10.2 From 21db4b1d24aa8c7a80ddd48bd4505b134dbf4aac Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:02 +0200 Subject: eeepc-laptop: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. This commit also removes various obsolete pr_xxx messages related to backlight interface selection. These are obsolete because they assume there is only a vendor or acpi backlight driver and no other choice. Also they are not necessary, if the user wants to know which backlight interfaces are registered a simple "ls /sys/class/backlight" suffices. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 844c209..8cdf315 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -37,6 +37,7 @@ #include #include #include +#include #define EEEPC_LAPTOP_VERSION "0.1" #define EEEPC_LAPTOP_NAME "Eee PC Hotkey Driver" @@ -1433,12 +1434,10 @@ static int eeepc_acpi_add(struct acpi_device *device) if (result) goto fail_platform; - if (!acpi_video_backlight_support()) { + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { result = eeepc_backlight_init(eeepc); if (result) goto fail_backlight; - } else { - pr_info("Backlight controlled by ACPI video driver\n"); } result = eeepc_input_init(eeepc); -- cgit v0.10.2 From 413226f7224bbab3e554a014c4f8c27077c2b5d4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:03 +0200 Subject: fujitsu-laptop: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. Signed-off-by: Hans de Goede Acked-by: Jonathan Woithe Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 2a9afa2..1c62caf 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -72,6 +72,7 @@ #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) #include #endif +#include #define FUJITSU_DRIVER_VERSION "0.6.0" @@ -1099,7 +1100,7 @@ static int __init fujitsu_init(void) /* Register backlight stuff */ - if (!acpi_video_backlight_support()) { + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { struct backlight_properties props; memset(&props, 0, sizeof(struct backlight_properties)); @@ -1137,8 +1138,7 @@ static int __init fujitsu_init(void) } /* Sync backlight power status (needs FUJ02E3 device, hence deferred) */ - - if (!acpi_video_backlight_support()) { + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { if (call_fext_func(FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) fujitsu->bl_device->props.power = FB_BLANK_POWERDOWN; else -- cgit v0.10.2 From 26bff5f099722fa7c38796a3ccd0e880cf1a524a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:04 +0200 Subject: ideapad-laptop: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index b496db8..bea0228 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -38,6 +38,7 @@ #include #include #include +#include #define IDEAPAD_RFKILL_DEV_NUM (3) @@ -903,7 +904,7 @@ static int ideapad_acpi_add(struct platform_device *pdev) ideapad_sync_rfk_state(priv); ideapad_sync_touchpad_state(priv); - if (!acpi_video_backlight_support()) { + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { ret = ideapad_backlight_init(priv); if (ret && ret != -ENODEV) goto backlight_failed; -- cgit v0.10.2 From 76d0a35100b932fc8b6e006ce5697ad88028c0c3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:05 +0200 Subject: intel-oaktrail: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. This commit also removes various obsolete pr_xxx messages related to backlight interface selection. These are obsolete because they assume there is only a vendor or acpi backlight driver and no other choice. Also they are not necessary, if the user wants to know which backlight interfaces are registered a simple "ls /sys/class/backlight" suffices. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c index 8037c8b..6aa33c4 100644 --- a/drivers/platform/x86/intel_oaktrail.c +++ b/drivers/platform/x86/intel_oaktrail.c @@ -50,6 +50,7 @@ #include #include #include +#include #define DRIVER_NAME "intel_oaktrail" #define DRIVER_VERSION "0.4ac1" @@ -343,13 +344,11 @@ static int __init oaktrail_init(void) goto err_device_add; } - if (!acpi_video_backlight_support()) { + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { ret = oaktrail_backlight_init(); if (ret) goto err_backlight; - - } else - pr_info("Backlight controlled by ACPI video driver\n"); + } ret = oaktrail_rfkill_init(); if (ret) { -- cgit v0.10.2 From 2cc6c717799f0ab3e8309fa9e43757f0c1906f14 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:06 +0200 Subject: msi-laptop: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. This commit also removes various obsolete pr_xxx messages related to backlight interface selection. These are obsolete because they assume there is only a vendor or acpi backlight driver and no other choice. Also they are not necessary, if the user wants to know which backlight interfaces are registered a simple "ls /sys/class/backlight" suffices. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 0859877..4231770 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -64,6 +64,7 @@ #include #include #include +#include #define MSI_DRIVER_VERSION "0.5" @@ -1069,9 +1070,8 @@ static int __init msi_init(void) /* Register backlight stuff */ - if (!quirks->old_ec_model || acpi_video_backlight_support()) { - pr_info("Brightness ignored, must be controlled by ACPI video driver\n"); - } else { + if (quirks->old_ec_model || + acpi_video_get_backlight_type() == acpi_backlight_vendor) { struct backlight_properties props; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_PLATFORM; -- cgit v0.10.2 From 33a4edfba6bcc0482bf9013352c371816c6437c2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:07 +0200 Subject: msi-wmi: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index 6d2bac0..978e6d6 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -29,6 +29,7 @@ #include #include #include +#include MODULE_AUTHOR("Thomas Renninger "); MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver"); @@ -320,7 +321,8 @@ static int __init msi_wmi_init(void) break; } - if (wmi_has_guid(MSIWMI_BIOS_GUID) && !acpi_video_backlight_support()) { + if (wmi_has_guid(MSIWMI_BIOS_GUID) && + acpi_video_get_backlight_type() == acpi_backlight_vendor) { err = msi_wmi_backlight_setup(); if (err) { pr_err("Unable to setup backlight device\n"); -- cgit v0.10.2 From 6c072299b6253d0737db87c7c18328cb376d96bb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:08 +0200 Subject: samsung-laptop: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. This commit also removes various obsolete pr_xxx messages related to backlight interface selection. These are obsolete because they assume there is only a vendor or acpi backlight driver and no other choice. Also they are not necessary, if the user wants to know which backlight interfaces are registered a simple "ls /sys/class/backlight" suffices. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 0df03e2..8c146e2 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -1720,27 +1720,14 @@ static int __init samsung_init(void) samsung->handle_backlight = true; samsung->quirks = quirks; - #ifdef CONFIG_ACPI if (samsung->quirks->broken_acpi_video) - acpi_video_dmi_promote_vendor(); - - /* Don't handle backlight here if the acpi video already handle it */ - if (acpi_video_backlight_support()) { - samsung->handle_backlight = false; - } else if (samsung->quirks->broken_acpi_video) { - pr_info("Disabling ACPI video driver\n"); - acpi_video_unregister_backlight(); - } + acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); + if (samsung->quirks->use_native_backlight) + acpi_video_set_dmi_backlight_type(acpi_backlight_native); - if (samsung->quirks->use_native_backlight) { - pr_info("Using native backlight driver\n"); - /* Tell acpi-video to not handle the backlight */ - acpi_video_dmi_promote_vendor(); - acpi_video_unregister_backlight(); - /* And also do not handle it ourselves */ + if (acpi_video_get_backlight_type() != acpi_backlight_vendor) samsung->handle_backlight = false; - } #endif ret = samsung_platform_init(samsung); @@ -1751,12 +1738,6 @@ static int __init samsung_init(void) if (ret) goto error_sabi; -#ifdef CONFIG_ACPI - /* Only log that if we are really on a sabi platform */ - if (acpi_video_backlight_support()) - pr_info("Backlight controlled by ACPI video driver\n"); -#endif - ret = samsung_sysfs_init(samsung); if (ret) goto error_sysfs; -- cgit v0.10.2 From 56d5c34a94dfb652592ba654ea236fb133e3686b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:09 +0200 Subject: sony-laptop: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. This commit also removes various obsolete pr_xxx messages related to backlight interface selection. These are obsolete because they assume there is only a vendor or acpi backlight driver and no other choice. Also they are not necessary, if the user wants to know which backlight interfaces are registered a simple "ls /sys/class/backlight" suffices. Signed-off-by: Hans de Goede Acked-by: Mattia Dongili Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index e51c1e7..aeb80d1 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -69,6 +69,7 @@ #include #endif #include +#include #define dprintk(fmt, ...) \ do { \ @@ -3198,12 +3199,8 @@ static int sony_nc_add(struct acpi_device *device) sony_nc_function_setup(device, sony_pf_device); } - /* setup input devices and helper fifo */ - if (acpi_video_backlight_support()) { - pr_info("brightness ignored, must be controlled by ACPI video driver\n"); - } else { + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) sony_nc_backlight_setup(); - } /* create sony_pf sysfs attributes related to the SNC device */ for (item = sony_nc_values; item->name; ++item) { -- cgit v0.10.2 From b33c6ce5f2987184dcc444d05d0f5a7227ce431e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:10 +0200 Subject: thinkpad-acpi: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. Signed-off-by: Hans de Goede Acked-by: Henrique de Moraes Holschuh Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 28f3281..33e488c 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -83,6 +83,7 @@ #include #include #include +#include /* ThinkPad CMOS commands */ #define TP_CMOS_VOLUME_DOWN 0 @@ -3487,7 +3488,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* Do not issue duplicate brightness change events to * userspace. tpacpi_detect_brightness_capabilities() must have * been called before this point */ - if (acpi_video_backlight_support()) { + if (acpi_video_get_backlight_type() != acpi_backlight_vendor) { pr_info("This ThinkPad has standard ACPI backlight " "brightness control, supported by the ACPI " "video driver\n"); @@ -6491,7 +6492,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) return 1; } - if (acpi_video_backlight_support()) { + if (acpi_video_get_backlight_type() != acpi_backlight_vendor) { if (brightness_enable > 1) { pr_info("Standard ACPI backlight interface " "available, not loading native one\n"); -- cgit v0.10.2 From 234b7cf88dd42eab08c99afa455669f035e8d861 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:11 +0200 Subject: toshiba-acpi: Port to new backlight interface selection API Port the backlight selection logic to the new backlight interface selection API. Signed-off-by: Hans de Goede Acked-by: Azael Avalos Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 9956b990..59bf27e 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -2640,14 +2640,11 @@ static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev) */ if (dev->tr_backlight_supported || dmi_check_system(toshiba_vendor_backlight_dmi)) - acpi_video_dmi_promote_vendor(); + acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); - if (acpi_video_backlight_support()) + if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return 0; - /* acpi-video may have loaded before we called dmi_promote_vendor() */ - acpi_video_unregister_backlight(); - memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_PLATFORM; props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; -- cgit v0.10.2 From d0a530ba424ec1be7630f7fce2db9860b9429b8f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:12 +0200 Subject: acpi-video-detect: Remove old API Remove the old backlight interface selection API now that all drivers have been ported to the new API. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 0df1567..c392579 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -340,37 +340,6 @@ void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type) } EXPORT_SYMBOL(acpi_video_set_dmi_backlight_type); -/* - * Compatiblity function, this is going away as soon as all drivers are - * converted to acpi_video_set_dmi_backlight_type(). - * - * Promote the vendor interface instead of the generic video module. - * After calling this function you will probably want to call - * acpi_video_unregister() to make sure the video module is not loaded - */ -void acpi_video_dmi_promote_vendor(void) -{ - acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); -} -EXPORT_SYMBOL(acpi_video_dmi_promote_vendor); - -/* - * Compatiblity function, this is going away as soon as all drivers are - * converted to acpi_video_get_backlight_type(). - * - * Returns true if video.ko can do backlight switching. - */ -int acpi_video_backlight_support(void) -{ - /* - * This is done this way since vendor drivers call this to see - * if they should load, and we do not want them to load for both - * the acpi_backlight_video and acpi_backlight_native cases. - */ - return acpi_video_get_backlight_type() != acpi_backlight_vendor; -} -EXPORT_SYMBOL(acpi_video_backlight_support); - void __exit acpi_video_detect_exit(void) { if (backlight_notifier_registered) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index f097c0a..5966d1d 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -245,25 +245,6 @@ extern bool wmi_has_guid(const char *guid); extern char acpi_video_backlight_string[]; extern long acpi_is_video_device(acpi_handle handle); - -#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) - -extern void acpi_video_dmi_promote_vendor(void); -extern int acpi_video_backlight_support(void); - -#else - -static inline void acpi_video_dmi_promote_vendor(void) -{ -} - -static inline int acpi_video_backlight_support(void) -{ - return 0; -} - -#endif /* defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) */ - extern int acpi_blacklisted(void); extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d); extern void acpi_osi_setup(char *str); -- cgit v0.10.2 From e7d024c00a4a7b617390db863bdd5b9dc65821f7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Jun 2015 16:28:13 +0200 Subject: ACPI / video: Make acpi_video_unregister_backlight() private acpi_video_unregister_backlight() is now only used by video_detect.c which is part of the same acpi_video module as video.c, make acpi_video_unregister_backlight() private to this module. Signed-off-by: Hans de Goede Acked-by: Darren Hart Signed-off-by: Rafael J. Wysocki diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 5b877a1..8c2fe2f 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -2021,7 +2021,6 @@ void acpi_video_unregister_backlight(void) } mutex_unlock(®ister_count_mutex); } -EXPORT_SYMBOL(acpi_video_unregister_backlight); /* * This is kind of nasty. Hardware using Intel chipsets may require diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index c392579..815f75e 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -37,6 +37,8 @@ ACPI_MODULE_NAME("video"); #define _COMPONENT ACPI_VIDEO_COMPONENT +void acpi_video_unregister_backlight(void); + static bool backlight_notifier_registered; static struct notifier_block backlight_nb; diff --git a/include/acpi/video.h b/include/acpi/video.h index 47c7ad6..a7d7f10 100644 --- a/include/acpi/video.h +++ b/include/acpi/video.h @@ -27,7 +27,6 @@ enum acpi_backlight_type { #if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE) extern int acpi_video_register(void); extern void acpi_video_unregister(void); -extern void acpi_video_unregister_backlight(void); extern int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, void **edid); extern enum acpi_backlight_type acpi_video_get_backlight_type(void); @@ -35,7 +34,6 @@ extern void acpi_video_set_dmi_backlight_type(enum acpi_backlight_type type); #else static inline int acpi_video_register(void) { return 0; } static inline void acpi_video_unregister(void) { return; } -static inline void acpi_video_unregister_backlight(void) { return; } static inline int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, void **edid) { -- cgit v0.10.2 From eb3486646187fe2010786e1d092a903343fbcc64 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Jun 2015 13:48:00 +0200 Subject: ACPI / enumeration: Document the rules regarding the PRP0001 device ID Document how the ACPI device enumeration code uses the special PRP0001 device ID. Signed-off-by: Rafael J. Wysocki Acked-by: Mika Westerberg Reviewed-by: Hanjun Guo Reviewed-by: Darren Hart diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt index 15dfce7..f44b009 100644 --- a/Documentation/acpi/enumeration.txt +++ b/Documentation/acpi/enumeration.txt @@ -359,3 +359,54 @@ the id should be set like: The ACPI id "XYZ0001" is then used to lookup an ACPI device directly under the MFD device and if found, that ACPI companion device is bound to the resulting child platform device. + +Device Tree namespace link device ID +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The Device Tree protocol uses device indentification based on the "compatible" +property whose value is a string or an array of strings recognized as device +identifiers by drivers and the driver core. The set of all those strings may be +regarded as a device indentification namespace analogous to the ACPI/PNP device +ID namespace. Consequently, in principle it should not be necessary to allocate +a new (and arguably redundant) ACPI/PNP device ID for a devices with an existing +identification string in the Device Tree (DT) namespace, especially if that ID +is only needed to indicate that a given device is compatible with another one, +presumably having a matching driver in the kernel already. + +In ACPI, the device identification object called _CID (Compatible ID) is used to +list the IDs of devices the given one is compatible with, but those IDs must +belong to one of the namespaces prescribed by the ACPI specification (see +Section 6.1.2 of ACPI 6.0 for details) and the DT namespace is not one of them. +Moreover, the specification mandates that either a _HID or an _ADR identificaion +object be present for all ACPI objects representing devices (Section 6.1 of ACPI +6.0). For non-enumerable bus types that object must be _HID and its value must +be a device ID from one of the namespaces prescribed by the specification too. + +The special DT namespace link device ID, PRP0001, provides a means to use the +existing DT-compatible device identification in ACPI and to satisfy the above +requirements following from the ACPI specification at the same time. Namely, +if PRP0001 is returned by _HID, the ACPI subsystem will look for the +"compatible" property in the device object's _DSD and will use the value of that +property to identify the corresponding device in analogy with the original DT +device identification algorithm. If the "compatible" property is not present +or its value is not valid, the device will not be enumerated by the ACPI +subsystem. Otherwise, it will be enumerated automatically as a platform device +(except when an I2C or SPI link from the device to its parent is present, in +which case the ACPI core will leave the device enumeration to the parent's +driver) and the identification strings from the "compatible" property value will +be used to find a driver for the device along with the device IDs listed by _CID +(if present). + +Analogously, if PRP0001 is present in the list of device IDs returned by _CID, +the identification strings listed by the "compatible" property value (if present +and valid) will be used to look for a driver matching the device, but in that +case their relative priority with respect to the other device IDs listed by +_HID and _CID depends on the position of PRP0001 in the _CID return package. +Specifically, the device IDs returned by _HID and preceding PRP0001 in the _CID +return package will be checked first. Also in that case the bus type the device +will be enumerated to depends on the device ID returned by _HID. + +It is valid to define device objects with a _HID returning PRP0001 and without +the "compatible" property in the _DSD or a _CID as long as one of their +ancestors provides a _DSD with a valid "compatible" property. Such device +objects are then simply regarded as additional "blocks" providing hierarchical +configuration information to the driver of the composite ancestor device. -- cgit v0.10.2 From 1a147ed75cc94cb2eca3bfa5ca00e069574090fd Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:27:00 +0200 Subject: ACPI: Constify ACPI device IDs in documentation ACPI device ID arrays normally don't need to be written to as they're only ever read. The common usage -- embedding pointers to acpi_device_id arrays in other data structures -- reference them as 'const', e.g. as in struct acpi_driver / acpi_scan_handler / device_driver. The matchers are taking const pointers, too. So it's only natural, to propose using const arrays. Change the documentation accordingly. Signed-off-by: Mathias Krause Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/acpi/enumeration.txt b/Documentation/acpi/enumeration.txt index f44b009..b731b29 100644 --- a/Documentation/acpi/enumeration.txt +++ b/Documentation/acpi/enumeration.txt @@ -42,7 +42,7 @@ Adding ACPI support for an existing driver should be pretty straightforward. Here is the simplest example: #ifdef CONFIG_ACPI - static struct acpi_device_id mydrv_acpi_match[] = { + static const struct acpi_device_id mydrv_acpi_match[] = { /* ACPI IDs here */ { } }; @@ -166,7 +166,7 @@ the platform device drivers. Below is an example where we add ACPI support to at25 SPI eeprom driver (this is meant for the above ACPI snippet): #ifdef CONFIG_ACPI - static struct acpi_device_id at25_acpi_match[] = { + static const struct acpi_device_id at25_acpi_match[] = { { "AT25", 0 }, { }, }; @@ -230,7 +230,7 @@ Below is an example of how to add ACPI support to the existing mpu3050 input driver: #ifdef CONFIG_ACPI - static struct acpi_device_id mpu3050_acpi_match[] = { + static const struct acpi_device_id mpu3050_acpi_match[] = { { "MPU3050", 0 }, { }, }; -- cgit v0.10.2 From b901b518077ba87bc84c84de02fce186cf9e5856 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 4 Jun 2015 21:50:31 +0530 Subject: PM / OPP: Add new bindings to address shortcomings of existing bindings Current OPP (Operating performance point) device tree bindings have been insufficient due to the inflexible nature of the original bindings. Over time, we have realized that Operating Performance Point definitions and usage is varied depending on the SoC and a "single size (just frequency, voltage) fits all" model which the original bindings attempted and failed. The proposed next generation of the bindings addresses by providing a expandable binding for OPPs and introduces the following common shortcomings seen with the original bindings: - Getting clock/voltage/current rails sharing information between CPUs. Shared by all cores vs independent clock per core vs shared clock per cluster. - Support for specifying current levels along with voltages. - Support for multiple regulators. - Support for turbo modes. - Other per OPP settings: transition latencies, disabled status, etc.? - Expandability of OPPs in future. This patch introduces new bindings "operating-points-v2" to get these problems solved. Refer to the bindings for more details. We now have multiple versions of OPP binding and only one of them should be used per device. Signed-off-by: Viresh Kumar Reviewed-by: Rob Herring Reviewed-by: Stephen Boyd Acked-by: Nishanth Menon Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/devicetree/bindings/power/opp.txt b/Documentation/devicetree/bindings/power/opp.txt index 74499e5..259bf00 100644 --- a/Documentation/devicetree/bindings/power/opp.txt +++ b/Documentation/devicetree/bindings/power/opp.txt @@ -1,8 +1,19 @@ -* Generic OPP Interface +Generic OPP (Operating Performance Points) Bindings +---------------------------------------------------- -SoCs have a standard set of tuples consisting of frequency and -voltage pairs that the device will support per voltage domain. These -are called Operating Performance Points or OPPs. +Devices work at voltage-current-frequency combinations and some implementations +have the liberty of choosing these. These combinations are called Operating +Performance Points aka OPPs. This document defines bindings for these OPPs +applicable across wide range of devices. For illustration purpose, this document +uses CPU as a device. + +This document contain multiple versions of OPP binding and only one of them +should be used per device. + +Binding 1: operating-points +============================ + +This binding only supports voltage-frequency pairs. Properties: - operating-points: An array of 2-tuples items, and each item consists @@ -23,3 +34,365 @@ cpu@0 { 198000 850000 >; }; + + +Binding 2: operating-points-v2 +============================ + +* Property: operating-points-v2 + +Devices supporting OPPs must set their "operating-points-v2" property with +phandle to a OPP table in their DT node. The OPP core will use this phandle to +find the operating points for the device. + +If required, this can be extended for SoC vendor specfic bindings. Such bindings +should be documented as Documentation/devicetree/bindings/power/-opp.txt +and should have a compatible description like: "operating-points-v2-". + +* OPP Table Node + +This describes the OPPs belonging to a device. This node can have following +properties: + +Required properties: +- compatible: Allow OPPs to express their compatibility. It should be: + "operating-points-v2". + +- OPP nodes: One or more OPP nodes describing voltage-current-frequency + combinations. Their name isn't significant but their phandle can be used to + reference an OPP. + +Optional properties: +- opp-shared: Indicates that device nodes using this OPP Table Node's phandle + switch their DVFS state together, i.e. they share clock/voltage/current lines. + Missing property means devices have independent clock/voltage/current lines, + but they share OPP tables. + + +* OPP Node + +This defines voltage-current-frequency combinations along with other related +properties. + +Required properties: +- opp-hz: Frequency in Hz + +Optional properties: +- opp-microvolt: voltage in micro Volts. + + A single regulator's voltage is specified with an array of size one or three. + Single entry is for target voltage and three entries are for + voltages. + + Entries for multiple regulators must be present in the same order as + regulators are specified in device's DT node. + +- opp-microamp: The maximum current drawn by the device in microamperes + considering system specific parameters (such as transients, process, aging, + maximum operating temperature range etc.) as necessary. This may be used to + set the most efficient regulator operating mode. + + Should only be set if opp-microvolt is set for the OPP. + + Entries for multiple regulators must be present in the same order as + regulators are specified in device's DT node. If this property isn't required + for few regulators, then this should be marked as zero for them. If it isn't + required for any regulator, then this property need not be present. + +- clock-latency-ns: Specifies the maximum possible transition latency (in + nanoseconds) for switching to this OPP from any other OPP. + +- turbo-mode: Marks the OPP to be used only for turbo modes. Turbo mode is + available on some platforms, where the device can run over its operating + frequency for a short duration of time limited by the device's power, current + and thermal limits. + +- status: Marks the node enabled/disabled. + +Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together. + +/ { + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a9"; + reg = <0>; + next-level-cache = <&L2>; + clocks = <&clk_controller 0>; + clock-names = "cpu"; + cpu-supply = <&cpu_supply0>; + operating-points-v2 = <&cpu0_opp_table>; + }; + + cpu@1 { + compatible = "arm,cortex-a9"; + reg = <1>; + next-level-cache = <&L2>; + clocks = <&clk_controller 0>; + clock-names = "cpu"; + cpu-supply = <&cpu_supply0>; + operating-points-v2 = <&cpu0_opp_table>; + }; + }; + + cpu0_opp_table: opp_table0 { + compatible = "operating-points-v2"; + opp-shared; + + opp00 { + opp-hz = <1000000000>; + opp-microvolt = <970000 975000 985000>; + opp-microamp = <70000>; + clock-latency-ns = <300000>; + }; + opp01 { + opp-hz = <1100000000>; + opp-microvolt = <980000 1000000 1010000>; + opp-microamp = <80000>; + clock-latency-ns = <310000>; + }; + opp02 { + opp-hz = <1200000000>; + opp-microvolt = <1025000>; + clock-latency-ns = <290000>; + turbo-mode; + }; + }; +}; + +Example 2: Single cluster, Quad-core Qualcom-krait, switches DVFS states +independently. + +/ { + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "qcom,krait"; + reg = <0>; + next-level-cache = <&L2>; + clocks = <&clk_controller 0>; + clock-names = "cpu"; + cpu-supply = <&cpu_supply0>; + operating-points-v2 = <&cpu_opp_table>; + }; + + cpu@1 { + compatible = "qcom,krait"; + reg = <1>; + next-level-cache = <&L2>; + clocks = <&clk_controller 1>; + clock-names = "cpu"; + cpu-supply = <&cpu_supply1>; + operating-points-v2 = <&cpu_opp_table>; + }; + + cpu@2 { + compatible = "qcom,krait"; + reg = <2>; + next-level-cache = <&L2>; + clocks = <&clk_controller 2>; + clock-names = "cpu"; + cpu-supply = <&cpu_supply2>; + operating-points-v2 = <&cpu_opp_table>; + }; + + cpu@3 { + compatible = "qcom,krait"; + reg = <3>; + next-level-cache = <&L2>; + clocks = <&clk_controller 3>; + clock-names = "cpu"; + cpu-supply = <&cpu_supply3>; + operating-points-v2 = <&cpu_opp_table>; + }; + }; + + cpu_opp_table: opp_table { + compatible = "operating-points-v2"; + + /* + * Missing opp-shared property means CPUs switch DVFS states + * independently. + */ + + opp00 { + opp-hz = <1000000000>; + opp-microvolt = <970000 975000 985000>; + opp-microamp = <70000>; + clock-latency-ns = <300000>; + }; + opp01 { + opp-hz = <1100000000>; + opp-microvolt = <980000 1000000 1010000>; + opp-microamp = <80000>; + clock-latency-ns = <310000>; + }; + opp02 { + opp-hz = <1200000000>; + opp-microvolt = <1025000>; + opp-microamp = <90000; + lock-latency-ns = <290000>; + turbo-mode; + }; + }; +}; + +Example 3: Dual-cluster, Dual-core per cluster. CPUs within a cluster switch +DVFS state together. + +/ { + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + compatible = "arm,cortex-a7"; + reg = <0>; + next-level-cache = <&L2>; + clocks = <&clk_controller 0>; + clock-names = "cpu"; + cpu-supply = <&cpu_supply0>; + operating-points-v2 = <&cluster0_opp>; + }; + + cpu@1 { + compatible = "arm,cortex-a7"; + reg = <1>; + next-level-cache = <&L2>; + clocks = <&clk_controller 0>; + clock-names = "cpu"; + cpu-supply = <&cpu_supply0>; + operating-points-v2 = <&cluster0_opp>; + }; + + cpu@100 { + compatible = "arm,cortex-a15"; + reg = <100>; + next-level-cache = <&L2>; + clocks = <&clk_controller 1>; + clock-names = "cpu"; + cpu-supply = <&cpu_supply1>; + operating-points-v2 = <&cluster1_opp>; + }; + + cpu@101 { + compatible = "arm,cortex-a15"; + reg = <101>; + next-level-cache = <&L2>; + clocks = <&clk_controller 1>; + clock-names = "cpu"; + cpu-supply = <&cpu_supply1>; + operating-points-v2 = <&cluster1_opp>; + }; + }; + + cluster0_opp: opp_table0 { + compatible = "operating-points-v2"; + opp-shared; + + opp00 { + opp-hz = <1000000000>; + opp-microvolt = <970000 975000 985000>; + opp-microamp = <70000>; + clock-latency-ns = <300000>; + }; + opp01 { + opp-hz = <1100000000>; + opp-microvolt = <980000 1000000 1010000>; + opp-microamp = <80000>; + clock-latency-ns = <310000>; + }; + opp02 { + opp-hz = <1200000000>; + opp-microvolt = <1025000>; + opp-microamp = <90000>; + clock-latency-ns = <290000>; + turbo-mode; + }; + }; + + cluster1_opp: opp_table1 { + compatible = "operating-points-v2"; + opp-shared; + + opp10 { + opp-hz = <1300000000>; + opp-microvolt = <1045000 1050000 1055000>; + opp-microamp = <95000>; + clock-latency-ns = <400000>; + }; + opp11 { + opp-hz = <1400000000>; + opp-microvolt = <1075000>; + opp-microamp = <100000>; + clock-latency-ns = <400000>; + }; + opp12 { + opp-hz = <1500000000>; + opp-microvolt = <1010000 1100000 1110000>; + opp-microamp = <95000>; + clock-latency-ns = <400000>; + turbo-mode; + }; + }; +}; + +Example 4: Handling multiple regulators + +/ { + cpus { + cpu@0 { + compatible = "arm,cortex-a7"; + ... + + cpu-supply = <&cpu_supply0>, <&cpu_supply1>, <&cpu_supply2>; + operating-points-v2 = <&cpu0_opp_table>; + }; + }; + + cpu0_opp_table: opp_table0 { + compatible = "operating-points-v2"; + opp-shared; + + opp00 { + opp-hz = <1000000000>; + opp-microvolt = <970000>, /* Supply 0 */ + <960000>, /* Supply 1 */ + <960000>; /* Supply 2 */ + opp-microamp = <70000>, /* Supply 0 */ + <70000>, /* Supply 1 */ + <70000>; /* Supply 2 */ + clock-latency-ns = <300000>; + }; + + /* OR */ + + opp00 { + opp-hz = <1000000000>; + opp-microvolt = <970000 975000 985000>, /* Supply 0 */ + <960000 965000 975000>, /* Supply 1 */ + <960000 965000 975000>; /* Supply 2 */ + opp-microamp = <70000>, /* Supply 0 */ + <70000>, /* Supply 1 */ + <70000>; /* Supply 2 */ + clock-latency-ns = <300000>; + }; + + /* OR */ + + opp00 { + opp-hz = <1000000000>; + opp-microvolt = <970000 975000 985000>, /* Supply 0 */ + <960000 965000 975000>, /* Supply 1 */ + <960000 965000 975000>; /* Supply 2 */ + opp-microamp = <70000>, /* Supply 0 */ + <0>, /* Supply 1 doesn't need this */ + <70000>; /* Supply 2 */ + clock-latency-ns = <300000>; + }; + }; +}; -- cgit v0.10.2 From a9a80e7e3177000344c3993411aada4d1e019f7c Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 22 Jun 2015 14:21:38 +0200 Subject: PM / OPP: Allow multiple OPP tables to be passed via DT On some platforms (Like Qualcomm's SoCs), it is not decided until runtime on what OPPs to use. The OPP tables can be fixed at compile time, but which table to use is found out only after reading some efuses (sort of an prom) and knowing characteristics of the SoC. To support such platform we need to pass multiple OPP tables per device and hardware should be able to choose one and only one table out of those. Update operating-points-v2 bindings to support that. Reviewed-by: Stephen Boyd Acked-by: Rob Herring Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/devicetree/bindings/power/opp.txt b/Documentation/devicetree/bindings/power/opp.txt index 259bf00..3d5d32c 100644 --- a/Documentation/devicetree/bindings/power/opp.txt +++ b/Documentation/devicetree/bindings/power/opp.txt @@ -45,10 +45,21 @@ Devices supporting OPPs must set their "operating-points-v2" property with phandle to a OPP table in their DT node. The OPP core will use this phandle to find the operating points for the device. +Devices may want to choose OPP tables at runtime and so can provide a list of +phandles here. But only *one* of them should be chosen at runtime. This must be +accompanied by a corresponding "operating-points-names" property, to uniquely +identify the OPP tables. + If required, this can be extended for SoC vendor specfic bindings. Such bindings should be documented as Documentation/devicetree/bindings/power/-opp.txt and should have a compatible description like: "operating-points-v2-". +Optional properties: +- operating-points-names: Names of OPP tables (required if multiple OPP + tables are present), to uniquely identify them. The same list must be present + for all the CPUs which are sharing clock/voltage rails and hence the OPP + tables. + * OPP Table Node This describes the OPPs belonging to a device. This node can have following @@ -68,6 +79,8 @@ Optional properties: Missing property means devices have independent clock/voltage/current lines, but they share OPP tables. +- status: Marks the OPP table enabled/disabled. + * OPP Node @@ -396,3 +409,50 @@ Example 4: Handling multiple regulators }; }; }; + +Example 5: Multiple OPP tables + +/ { + cpus { + cpu@0 { + compatible = "arm,cortex-a7"; + ... + + cpu-supply = <&cpu_supply> + operating-points-v2 = <&cpu0_opp_table_slow>, <&cpu0_opp_table_fast>; + operating-points-names = "slow", "fast"; + }; + }; + + cpu0_opp_table_slow: opp_table_slow { + compatible = "operating-points-v2"; + status = "okay"; + opp-shared; + + opp00 { + opp-hz = <600000000>; + ... + }; + + opp01 { + opp-hz = <800000000>; + ... + }; + }; + + cpu0_opp_table_fast: opp_table_fast { + compatible = "operating-points-v2"; + status = "okay"; + opp-shared; + + opp10 { + opp-hz = <1000000000>; + ... + }; + + opp11 { + opp-hz = <1100000000>; + ... + }; + }; +}; -- cgit v0.10.2 From 9f20d6815c6c537980b8a8fcb1a172c0b95bd99f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 22 Jun 2015 14:23:27 +0200 Subject: PM / OPP: Add binding for 'opp-suspend' On few platforms, for power efficiency, we want the device to be configured for a specific OPP while we put the device in suspend state. Add an optional property in operating-points-v2 bindings for that. Suggested-by: Nishanth Menon Signed-off-by: Viresh Kumar Acked-by: Nishanth Menon Acked-by: Rob Herring Signed-off-by: Rafael J. Wysocki diff --git a/Documentation/devicetree/bindings/power/opp.txt b/Documentation/devicetree/bindings/power/opp.txt index 3d5d32c..0d5e7c9 100644 --- a/Documentation/devicetree/bindings/power/opp.txt +++ b/Documentation/devicetree/bindings/power/opp.txt @@ -120,6 +120,9 @@ Optional properties: frequency for a short duration of time limited by the device's power, current and thermal limits. +- opp-suspend: Marks the OPP to be used during device suspend. Only one OPP in + the table should have this. + - status: Marks the node enabled/disabled. Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together. @@ -159,6 +162,7 @@ Example 1: Single cluster Dual-core ARM cortex A9, switch DVFS states together. opp-microvolt = <970000 975000 985000>; opp-microamp = <70000>; clock-latency-ns = <300000>; + opp-suspend; }; opp01 { opp-hz = <1100000000>; @@ -237,6 +241,7 @@ independently. opp-microvolt = <970000 975000 985000>; opp-microamp = <70000>; clock-latency-ns = <300000>; + opp-suspend; }; opp01 { opp-hz = <1100000000>; @@ -312,6 +317,7 @@ DVFS state together. opp-microvolt = <970000 975000 985000>; opp-microamp = <70000>; clock-latency-ns = <300000>; + opp-suspend; }; opp01 { opp-hz = <1100000000>; @@ -337,6 +343,7 @@ DVFS state together. opp-microvolt = <1045000 1050000 1055000>; opp-microamp = <95000>; clock-latency-ns = <400000>; + opp-suspend; }; opp11 { opp-hz = <1400000000>; -- cgit v0.10.2 From ffa64eff956a25548cad0391dbc14c672827be7b Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 22 Jun 2015 14:40:03 +0200 Subject: x86: Load __USER_DS into DS/ES after resume Srinivas Pandruvada reported a problem with system resume from suspend-to-RAM on 32-bit x86 systems where the DS register of the CPU is set to __KERNEL_DS instead of __USER_DS on return to user space which cases a General Protection Fault to occur. The issue is that DS is set to __KERNEL_DS by the ACPI resume code path while the SYSEXIT path never reloads DS/ES. It assumes they are still __USER_DS set at the SYSENTER time (Brian Gerst), so if the return to user space happens to be through SYSEXIT, it will lead to the reported GPF. Fix the problem by setting the DS and ES registers to __USER_DS as expected by the SYSEXIT path. Link: https://bugzilla.kernel.org/show_bug.cgi?id=61781 Link: http://marc.info/?l=linux-pm&m=143406648920385&w=2 Acked-by: Pavel Machek Tested-by: Pavel Machek Acked-by: Ingo Molnar Signed-off-by: Rafael J. Wysocki diff --git a/arch/x86/kernel/acpi/wakeup_32.S b/arch/x86/kernel/acpi/wakeup_32.S index 665c6b7..0c26b1b 100644 --- a/arch/x86/kernel/acpi/wakeup_32.S +++ b/arch/x86/kernel/acpi/wakeup_32.S @@ -12,11 +12,13 @@ ENTRY(wakeup_pmode_return) wakeup_pmode_return: movw $__KERNEL_DS, %ax movw %ax, %ss - movw %ax, %ds - movw %ax, %es movw %ax, %fs movw %ax, %gs + movw $__USER_DS, %ax + movw %ax, %ds + movw %ax, %es + # reload the gdt, as we need the full 32 bit address lidt saved_idt lldt saved_ldt -- cgit v0.10.2 From 78eaa10f027cf69f9bd409e64eaff902172b2327 Mon Sep 17 00:00:00 2001 From: Shilpasri G Bhat Date: Thu, 18 Jun 2015 16:53:11 +0530 Subject: cpuidle: powernv/pseries: Auto-promotion of snooze to deeper idle state The idle cpus which stay in snooze for a long period can degrade the perfomance of the sibling cpus. If the cpu stays in snooze for more than target residency of the next available idle state, then exit from snooze. This gives a chance to the cpuidle governor to re-evaluate the last idle state of the cpu to promote it to deeper idle states. Signed-off-by: Shilpasri G Bhat Reviewed-by: Preeti U Murthy Signed-off-by: Rafael J. Wysocki diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index 5937207..1e3ef5e 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -29,18 +29,25 @@ struct cpuidle_driver powernv_idle_driver = { static int max_idle_state; static struct cpuidle_state *cpuidle_state_table; +static u64 snooze_timeout; +static bool snooze_timeout_en; static int snooze_loop(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { + u64 snooze_exit_time; + local_irq_enable(); set_thread_flag(TIF_POLLING_NRFLAG); + snooze_exit_time = get_tb() + snooze_timeout; ppc64_runlatch_off(); while (!need_resched()) { HMT_low(); HMT_very_low(); + if (snooze_timeout_en && get_tb() > snooze_exit_time) + break; } HMT_medium(); @@ -252,6 +259,11 @@ static int powernv_idle_probe(void) cpuidle_state_table = powernv_states; /* Device tree can indicate more idle states */ max_idle_state = powernv_add_idle_states(); + if (max_idle_state > 1) { + snooze_timeout_en = true; + snooze_timeout = powernv_states[1].target_residency * + tb_ticks_per_usec; + } } else return -ENODEV; diff --git a/drivers/cpuidle/cpuidle-pseries.c b/drivers/cpuidle/cpuidle-pseries.c index bb9e2b6..07135e0 100644 --- a/drivers/cpuidle/cpuidle-pseries.c +++ b/drivers/cpuidle/cpuidle-pseries.c @@ -27,6 +27,8 @@ struct cpuidle_driver pseries_idle_driver = { static int max_idle_state; static struct cpuidle_state *cpuidle_state_table; +static u64 snooze_timeout; +static bool snooze_timeout_en; static inline void idle_loop_prolog(unsigned long *in_purr) { @@ -58,14 +60,18 @@ static int snooze_loop(struct cpuidle_device *dev, int index) { unsigned long in_purr; + u64 snooze_exit_time; idle_loop_prolog(&in_purr); local_irq_enable(); set_thread_flag(TIF_POLLING_NRFLAG); + snooze_exit_time = get_tb() + snooze_timeout; while (!need_resched()) { HMT_low(); HMT_very_low(); + if (snooze_timeout_en && get_tb() > snooze_exit_time) + break; } HMT_medium(); @@ -244,6 +250,11 @@ static int pseries_idle_probe(void) } else return -ENODEV; + if (max_idle_state > 1) { + snooze_timeout_en = true; + snooze_timeout = cpuidle_state_table[1].target_residency * + tb_ticks_per_usec; + } return 0; } -- cgit v0.10.2