Pull additional power management updates from Rafael Wysocki: "These fix an ACPI EC driver bug exposed by the recent rework of the suspend-to-idle code flow, reintroduce frequency constraints into device PM QoS (in preparation for adding QoS support to devfreq), drop a redundant field from struct cpuidle_state and clean up Kconfig in some places. Specifics: - Avoid a race condition in the ACPI EC driver that may cause systems to be unable to leave suspend-to-idle (Rafael Wysocki) - Drop the "disabled" field, which is redundant, from struct cpuidle_state (Rafael Wysocki) - Reintroduce device PM QoS frequency constraints (temporarily introduced and than dropped during the 5.4 cycle) in preparation for adding QoS support to devfreq (Leonard Crestez) - Clean up indentation (in multiple places) and the cpuidle drivers help text in Kconfig (Krzysztof Kozlowski, Randy Dunlap)" * tag 'pm-5.5-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: ACPI: PM: s2idle: Rework ACPI events synchronization ACPI: EC: Rework flushing of pending work PM / devfreq: Add missing locking while setting suspend_freq PM / QoS: Restore DEV_PM_QOS_MIN/MAX_FREQUENCY PM / QoS: Reorder pm_qos/freq_qos/dev_pm_qos structs PM / QoS: Initial kunit test PM / QoS: Redefine FREQ_QOS_MAX_DEFAULT_VALUE to S32_MAX power: avs: Fix Kconfig indentation cpufreq: Fix Kconfig indentation cpuidle: minor Kconfig help text fixes cpuidle: Drop disabled field from struct cpuidle_state cpuidle: Fix Kconfig indentation
Unified · Split
| @@ -67,7 +67,7 @@ static struct cpuidle_driver cpuidle_driver = { · 66 unmodified lines | ||
| 67 | 67 | .enter = cpuidle_sleep_enter, |
| 68 | 68 | .name = "C2", |
| 69 | 69 | .desc = "SuperH Sleep Mode [SF]", |
| 70 | − .disabled = true, | |
| 70 | + .flags = CPUIDLE_FLAG_UNUSABLE, | |
| 71 | 71 | }, |
| 72 | 72 | { |
| 73 | 73 | .exit_latency = 2300, |
| @@ -76,7 +76,7 @@ static struct cpuidle_driver cpuidle_driver = { · 2 unmodified lines | ||
| 76 | 76 | .enter = cpuidle_sleep_enter, |
| 77 | 77 | .name = "C3", |
| 78 | 78 | .desc = "SuperH Mobile Standby Mode [SF]", |
| 79 | − .disabled = true, | |
| 79 | + .flags = CPUIDLE_FLAG_UNUSABLE, | |
| 80 | 80 | }, |
| 81 | 81 | }, |
| 82 | 82 | .safe_state_index = 0, |
| @@ -86,10 +86,10 @@ static struct cpuidle_driver cpuidle_driver = { · 3 unmodified lines | ||
| 86 | 86 | int __init sh_mobile_setup_cpuidle(void) |
| 87 | 87 | { |
| 88 | 88 | if (sh_mobile_sleep_supported & SUSP_SH_SF) |
| 89 | − cpuidle_driver.states[1].disabled = false; | |
| 89 | + cpuidle_driver.states[1].flags = CPUIDLE_FLAG_NONE; | |
| 90 | 90 | |
| 91 | 91 | if (sh_mobile_sleep_supported & SUSP_SH_STANDBY) |
| 92 | − cpuidle_driver.states[2].disabled = false; | |
| 92 | + cpuidle_driver.states[2].flags = CPUIDLE_FLAG_NONE; | |
| 93 | 93 | |
| 94 | 94 | return cpuidle_register(&cpuidle_driver, NULL); |
| 95 | 95 | } |
| @@ -533,26 +533,10 @@ static void acpi_ec_enable_event(struct acpi_ec *ec) · 532 unmodified lines | ||
| 533 | 533 | } |
| 534 | 534 | |
| 535 | 535 | #ifdef CONFIG_PM_SLEEP |
| 536 | −static bool acpi_ec_query_flushed(struct acpi_ec *ec) | |
| 536 | +static void __acpi_ec_flush_work(void) | |
| 537 | 537 | { |
| 538 | − bool flushed; | |
| 539 | − unsigned long flags; | |
| 540 | − | |
| 541 | − spin_lock_irqsave(&ec->lock, flags); | |
| 542 | − flushed = !ec->nr_pending_queries; | |
| 543 | − spin_unlock_irqrestore(&ec->lock, flags); | |
| 544 | − return flushed; | |
| 545 | −} | |
| 546 | − | |
| 547 | −static void __acpi_ec_flush_event(struct acpi_ec *ec) | |
| 548 | −{ | |
| 549 | − /* | |
| 550 | − * When ec_freeze_events is true, we need to flush events in | |
| 551 | − * the proper position before entering the noirq stage. | |
| 552 | − */ | |
| 553 | − wait_event(ec->wait, acpi_ec_query_flushed(ec)); | |
| 554 | − if (ec_query_wq) | |
| 555 | − flush_workqueue(ec_query_wq); | |
| 538 | + flush_scheduled_work(); /* flush ec->work */ | |
| 539 | + flush_workqueue(ec_query_wq); /* flush queries */ | |
| 556 | 540 | } |
| 557 | 541 | |
| 558 | 542 | static void acpi_ec_disable_event(struct acpi_ec *ec) |
| @@ -562,15 +546,21 @@ static void acpi_ec_disable_event(struct acpi_ec *ec) · 3 unmodified lines | ||
| 562 | 546 | spin_lock_irqsave(&ec->lock, flags); |
| 563 | 547 | __acpi_ec_disable_event(ec); |
| 564 | 548 | spin_unlock_irqrestore(&ec->lock, flags); |
| 565 | − __acpi_ec_flush_event(ec); | |
| 549 | + | |
| 550 | + /* | |
| 551 | + * When ec_freeze_events is true, we need to flush events in | |
| 552 | + * the proper position before entering the noirq stage. | |
| 553 | + */ | |
| 554 | + __acpi_ec_flush_work(); | |
| 566 | 555 | } |
| 567 | 556 | |
| 568 | 557 | void acpi_ec_flush_work(void) |
| 569 | 558 | { |
| 570 | − if (first_ec) | |
| 571 | − __acpi_ec_flush_event(first_ec); | |
| 559 | + /* Without ec_query_wq there is nothing to flush. */ | |
| 560 | + if (!ec_query_wq) | |
| 561 | + return; | |
| 572 | 562 | |
| 573 | − flush_scheduled_work(); | |
| 563 | + __acpi_ec_flush_work(); | |
| 574 | 564 | } |
| 575 | 565 | #endif /* CONFIG_PM_SLEEP */ |
| 576 | 566 | |
| @@ -977,6 +977,16 @@ static int acpi_s2idle_prepare_late(void) · 976 unmodified lines | ||
| 977 | 977 | return 0; |
| 978 | 978 | } |
| 979 | 979 | |
| 980 | +static void acpi_s2idle_sync(void) | |
| 981 | +{ | |
| 982 | + /* | |
| 983 | + * The EC driver uses the system workqueue and an additional special | |
| 984 | + * one, so those need to be flushed too. | |
| 985 | + */ | |
| 986 | + acpi_ec_flush_work(); | |
| 987 | + acpi_os_wait_events_complete(); /* synchronize Notify handling */ | |
| 988 | +} | |
| 989 | + | |
| 980 | 990 | static void acpi_s2idle_wake(void) |
| 981 | 991 | { |
| 982 | 992 | /* |
| @@ -1001,13 +1011,8 @@ static void acpi_s2idle_wake(void) · 18 unmodified lines | ||
| 1001 | 1011 | * should be missed by canceling the wakeup here. |
| 1002 | 1012 | */ |
| 1003 | 1013 | pm_system_cancel_wakeup(); |
| 1004 | − /* | |
| 1005 | − * The EC driver uses the system workqueue and an additional | |
| 1006 | − * special one, so those need to be flushed too. | |
| 1007 | − */ | |
| 1008 | − acpi_os_wait_events_complete(); /* synchronize EC GPE processing */ | |
| 1009 | − acpi_ec_flush_work(); | |
| 1010 | − acpi_os_wait_events_complete(); /* synchronize Notify handling */ | |
| 1014 | + | |
| 1015 | + acpi_s2idle_sync(); | |
| 1011 | 1016 | |
| 1012 | 1017 | rearm_wake_irq(acpi_sci_irq); |
| 1013 | 1018 | } |
| @@ -1024,6 +1029,13 @@ static void acpi_s2idle_restore_early(void) · 10 unmodified lines | ||
| 1024 | 1029 | |
| 1025 | 1030 | static void acpi_s2idle_restore(void) |
| 1026 | 1031 | { |
| 1032 | + /* | |
| 1033 | + * Drain pending events before restoring the working-state configuration | |
| 1034 | + * of GPEs. | |
| 1035 | + */ | |
| 1036 | + acpi_os_wait_events_complete(); /* synchronize GPE processing */ | |
| 1037 | + acpi_s2idle_sync(); | |
| 1038 | + | |
| 1027 | 1039 | s2idle_wakeup = false; |
| 1028 | 1040 | |
| 1029 | 1041 | acpi_enable_all_runtime_gpes(); |
| @@ -148,6 +148,10 @@ config DEBUG_TEST_DRIVER_REMOVE · 147 unmodified lines | ||
| 148 | 148 | unusable. You should say N here unless you are explicitly looking to |
| 149 | 149 | test this functionality. |
| 150 | 150 | |
| 151 | +config PM_QOS_KUNIT_TEST | |
| 152 | + bool "KUnit Test for PM QoS features" | |
| 153 | + depends on KUNIT | |
| 154 | + | |
| 151 | 155 | config HMEM_REPORTING |
| 152 | 156 | bool |
| 153 | 157 | default n |
| @@ -4,5 +4,6 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o wakeup_stats.o · 3 unmodified lines | ||
| 4 | 4 | obj-$(CONFIG_PM_TRACE_RTC) += trace.o |
| 5 | 5 | obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o |
| 6 | 6 | obj-$(CONFIG_HAVE_CLK) += clock_ops.o |
| 7 | +obj-$(CONFIG_PM_QOS_KUNIT_TEST) += qos-test.o | |
| 7 | 8 | |
| 8 | 9 | ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG |
| @@ -0,0 +1,117 @@ | ||
| 1 | +// SPDX-License-Identifier: GPL-2.0 | |
| 2 | +/* | |
| 3 | + * Copyright 2019 NXP | |
| 4 | + */ | |
| 5 | +#include <kunit/test.h> | |
| 6 | +#include <linux/pm_qos.h> | |
| 7 | + | |
| 8 | +/* Basic test for aggregating two "min" requests */ | |
| 9 | +static void freq_qos_test_min(struct kunit *test) | |
| 10 | +{ | |
| 11 | + struct freq_constraints qos; | |
| 12 | + struct freq_qos_request req1, req2; | |
| 13 | + int ret; | |
| 14 | + | |
| 15 | + freq_constraints_init(&qos); | |
| 16 | + memset(&req1, 0, sizeof(req1)); | |
| 17 | + memset(&req2, 0, sizeof(req2)); | |
| 18 | + | |
| 19 | + ret = freq_qos_add_request(&qos, &req1, FREQ_QOS_MIN, 1000); | |
| 20 | + KUNIT_EXPECT_EQ(test, ret, 1); | |
| 21 | + ret = freq_qos_add_request(&qos, &req2, FREQ_QOS_MIN, 2000); | |
| 22 | + KUNIT_EXPECT_EQ(test, ret, 1); | |
| 23 | + | |
| 24 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), 2000); | |
| 25 | + | |
| 26 | + ret = freq_qos_remove_request(&req2); | |
| 27 | + KUNIT_EXPECT_EQ(test, ret, 1); | |
| 28 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), 1000); | |
| 29 | + | |
| 30 | + ret = freq_qos_remove_request(&req1); | |
| 31 | + KUNIT_EXPECT_EQ(test, ret, 1); | |
| 32 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), | |
| 33 | + FREQ_QOS_MIN_DEFAULT_VALUE); | |
| 34 | +} | |
| 35 | + | |
| 36 | +/* Test that requests for MAX_DEFAULT_VALUE have no effect */ | |
| 37 | +static void freq_qos_test_maxdef(struct kunit *test) | |
| 38 | +{ | |
| 39 | + struct freq_constraints qos; | |
| 40 | + struct freq_qos_request req1, req2; | |
| 41 | + int ret; | |
| 42 | + | |
| 43 | + freq_constraints_init(&qos); | |
| 44 | + memset(&req1, 0, sizeof(req1)); | |
| 45 | + memset(&req2, 0, sizeof(req2)); | |
| 46 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MAX), | |
| 47 | + FREQ_QOS_MAX_DEFAULT_VALUE); | |
| 48 | + | |
| 49 | + ret = freq_qos_add_request(&qos, &req1, FREQ_QOS_MAX, | |
| 50 | + FREQ_QOS_MAX_DEFAULT_VALUE); | |
| 51 | + KUNIT_EXPECT_EQ(test, ret, 0); | |
| 52 | + ret = freq_qos_add_request(&qos, &req2, FREQ_QOS_MAX, | |
| 53 | + FREQ_QOS_MAX_DEFAULT_VALUE); | |
| 54 | + KUNIT_EXPECT_EQ(test, ret, 0); | |
| 55 | + | |
| 56 | + /* Add max 1000 */ | |
| 57 | + ret = freq_qos_update_request(&req1, 1000); | |
| 58 | + KUNIT_EXPECT_EQ(test, ret, 1); | |
| 59 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MAX), 1000); | |
| 60 | + | |
| 61 | + /* Add max 2000, no impact */ | |
| 62 | + ret = freq_qos_update_request(&req2, 2000); | |
| 63 | + KUNIT_EXPECT_EQ(test, ret, 0); | |
| 64 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MAX), 1000); | |
| 65 | + | |
| 66 | + /* Remove max 1000, new max 2000 */ | |
| 67 | + ret = freq_qos_remove_request(&req1); | |
| 68 | + KUNIT_EXPECT_EQ(test, ret, 1); | |
| 69 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MAX), 2000); | |
| 70 | +} | |
| 71 | + | |
| 72 | +/* | |
| 73 | + * Test that a freq_qos_request can be added again after removal | |
| 74 | + * | |
| 75 | + * This issue was solved by commit 05ff1ba412fd ("PM: QoS: Invalidate frequency | |
| 76 | + * QoS requests after removal") | |
| 77 | + */ | |
| 78 | +static void freq_qos_test_readd(struct kunit *test) | |
| 79 | +{ | |
| 80 | + struct freq_constraints qos; | |
| 81 | + struct freq_qos_request req; | |
| 82 | + int ret; | |
| 83 | + | |
| 84 | + freq_constraints_init(&qos); | |
| 85 | + memset(&req, 0, sizeof(req)); | |
| 86 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), | |
| 87 | + FREQ_QOS_MIN_DEFAULT_VALUE); | |
| 88 | + | |
| 89 | + /* Add */ | |
| 90 | + ret = freq_qos_add_request(&qos, &req, FREQ_QOS_MIN, 1000); | |
| 91 | + KUNIT_EXPECT_EQ(test, ret, 1); | |
| 92 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), 1000); | |
| 93 | + | |
| 94 | + /* Remove */ | |
| 95 | + ret = freq_qos_remove_request(&req); | |
| 96 | + KUNIT_EXPECT_EQ(test, ret, 1); | |
| 97 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), | |
| 98 | + FREQ_QOS_MIN_DEFAULT_VALUE); | |
| 99 | + | |
| 100 | + /* Add again */ | |
| 101 | + ret = freq_qos_add_request(&qos, &req, FREQ_QOS_MIN, 2000); | |
| 102 | + KUNIT_EXPECT_EQ(test, ret, 1); | |
| 103 | + KUNIT_EXPECT_EQ(test, freq_qos_read_value(&qos, FREQ_QOS_MIN), 2000); | |
| 104 | +} | |
| 105 | + | |
| 106 | +static struct kunit_case pm_qos_test_cases[] = { | |
| 107 | + KUNIT_CASE(freq_qos_test_min), | |
| 108 | + KUNIT_CASE(freq_qos_test_maxdef), | |
| 109 | + KUNIT_CASE(freq_qos_test_readd), | |
| 110 | + {}, | |
| 111 | +}; | |
| 112 | + | |
| 113 | +static struct kunit_suite pm_qos_test_module = { | |
| 114 | + .name = "qos-kunit-test", | |
| 115 | + .test_cases = pm_qos_test_cases, | |
| 116 | +}; | |
| 117 | +kunit_test_suite(pm_qos_test_module); | |
| @@ -115,10 +115,20 @@ s32 dev_pm_qos_read_value(struct device *dev, enum dev_pm_qos_req_type type) · 114 unmodified lines | ||
| 115 | 115 | |
| 116 | 116 | spin_lock_irqsave(&dev->power.lock, flags); |
| 117 | 117 | |
| 118 | − if (type == DEV_PM_QOS_RESUME_LATENCY) { | |
| 118 | + switch (type) { | |
| 119 | + case DEV_PM_QOS_RESUME_LATENCY: | |
| 119 | 120 | ret = IS_ERR_OR_NULL(qos) ? PM_QOS_RESUME_LATENCY_NO_CONSTRAINT |
| 120 | 121 | : pm_qos_read_value(&qos->resume_latency); |
| 121 | − } else { | |
| 122 | + break; | |
| 123 | + case DEV_PM_QOS_MIN_FREQUENCY: | |
| 124 | + ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE | |
| 125 | + : freq_qos_read_value(&qos->freq, FREQ_QOS_MIN); | |
| 126 | + break; | |
| 127 | + case DEV_PM_QOS_MAX_FREQUENCY: | |
| 128 | + ret = IS_ERR_OR_NULL(qos) ? PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE | |
| 129 | + : freq_qos_read_value(&qos->freq, FREQ_QOS_MAX); | |
| 130 | + break; | |
| 131 | + default: | |
| 122 | 132 | WARN_ON(1); |
| 123 | 133 | ret = 0; |
| 124 | 134 | } |
| @@ -159,6 +169,10 @@ static int apply_constraint(struct dev_pm_qos_request *req, · 34 unmodified lines | ||
| 159 | 169 | req->dev->power.set_latency_tolerance(req->dev, value); |
| 160 | 170 | } |
| 161 | 171 | break; |
| 172 | + case DEV_PM_QOS_MIN_FREQUENCY: | |
| 173 | + case DEV_PM_QOS_MAX_FREQUENCY: | |
| 174 | + ret = freq_qos_apply(&req->data.freq, action, value); | |
| 175 | + break; | |
| 162 | 176 | case DEV_PM_QOS_FLAGS: |
| 163 | 177 | ret = pm_qos_update_flags(&qos->flags, &req->data.flr, |
| 164 | 178 | action, value); |
| @@ -209,6 +223,8 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) · 44 unmodified lines | ||
| 209 | 223 | c->no_constraint_value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT; |
| 210 | 224 | c->type = PM_QOS_MIN; |
| 211 | 225 | |
| 226 | + freq_constraints_init(&qos->freq); | |
| 227 | + | |
| 212 | 228 | INIT_LIST_HEAD(&qos->flags.list); |
| 213 | 229 | |
| 214 | 230 | spin_lock_irq(&dev->power.lock); |
| @@ -269,6 +285,20 @@ void dev_pm_qos_constraints_destroy(struct device *dev) · 54 unmodified lines | ||
| 269 | 285 | memset(req, 0, sizeof(*req)); |
| 270 | 286 | } |
| 271 | 287 | |
| 288 | + c = &qos->freq.min_freq; | |
| 289 | + plist_for_each_entry_safe(req, tmp, &c->list, data.freq.pnode) { | |
| 290 | + apply_constraint(req, PM_QOS_REMOVE_REQ, | |
| 291 | + PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE); | |
| 292 | + memset(req, 0, sizeof(*req)); | |
| 293 | + } | |
| 294 | + | |
| 295 | + c = &qos->freq.max_freq; | |
| 296 | + plist_for_each_entry_safe(req, tmp, &c->list, data.freq.pnode) { | |
| 297 | + apply_constraint(req, PM_QOS_REMOVE_REQ, | |
| 298 | + PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE); | |
| 299 | + memset(req, 0, sizeof(*req)); | |
| 300 | + } | |
| 301 | + | |
| 272 | 302 | f = &qos->flags; |
| 273 | 303 | list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) { |
| 274 | 304 | apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); |
| @@ -314,11 +344,22 @@ static int __dev_pm_qos_add_request(struct device *dev, · 39 unmodified lines | ||
| 314 | 344 | ret = dev_pm_qos_constraints_allocate(dev); |
| 315 | 345 | |
| 316 | 346 | trace_dev_pm_qos_add_request(dev_name(dev), type, value); |
| 317 | − if (!ret) { | |
| 318 | − req->dev = dev; | |
| 319 | − req->type = type; | |
| 347 | + if (ret) | |
| 348 | + return ret; | |
| 349 | + | |
| 350 | + req->dev = dev; | |
| 351 | + req->type = type; | |
| 352 | + if (req->type == DEV_PM_QOS_MIN_FREQUENCY) | |
| 353 | + ret = freq_qos_add_request(&dev->power.qos->freq, | |
| 354 | + &req->data.freq, | |
| 355 | + FREQ_QOS_MIN, value); | |
| 356 | + else if (req->type == DEV_PM_QOS_MAX_FREQUENCY) | |
| 357 | + ret = freq_qos_add_request(&dev->power.qos->freq, | |
| 358 | + &req->data.freq, | |
| 359 | + FREQ_QOS_MAX, value); | |
| 360 | + else | |
| 320 | 361 | ret = apply_constraint(req, PM_QOS_ADD_REQ, value); |
| 321 | − } | |
| 362 | + | |
| 322 | 363 | return ret; |
| 323 | 364 | } |
| 324 | 365 | |
| @@ -382,6 +423,10 @@ static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, · 57 unmodified lines | ||
| 382 | 423 | case DEV_PM_QOS_LATENCY_TOLERANCE: |
| 383 | 424 | curr_value = req->data.pnode.prio; |
| 384 | 425 | break; |
| 426 | + case DEV_PM_QOS_MIN_FREQUENCY: | |
| 427 | + case DEV_PM_QOS_MAX_FREQUENCY: | |
| 428 | + curr_value = req->data.freq.pnode.prio; | |
| 429 | + break; | |
| 385 | 430 | case DEV_PM_QOS_FLAGS: |
| 386 | 431 | curr_value = req->data.flr.flags; |
| 387 | 432 | break; |
| @@ -507,6 +552,14 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, · 119 unmodified lines | ||
| 507 | 552 | ret = blocking_notifier_chain_register(dev->power.qos->resume_latency.notifiers, |
| 508 | 553 | notifier); |
| 509 | 554 | break; |
| 555 | + case DEV_PM_QOS_MIN_FREQUENCY: | |
| 556 | + ret = freq_qos_add_notifier(&dev->power.qos->freq, | |
| 557 | + FREQ_QOS_MIN, notifier); | |
| 558 | + break; | |
| 559 | + case DEV_PM_QOS_MAX_FREQUENCY: | |
| 560 | + ret = freq_qos_add_notifier(&dev->power.qos->freq, | |
| 561 | + FREQ_QOS_MAX, notifier); | |
| 562 | + break; | |
| 510 | 563 | default: |
| 511 | 564 | WARN_ON(1); |
| 512 | 565 | ret = -EINVAL; |
| @@ -546,6 +599,14 @@ int dev_pm_qos_remove_notifier(struct device *dev, · 33 unmodified lines | ||
| 546 | 599 | ret = blocking_notifier_chain_unregister(dev->power.qos->resume_latency.notifiers, |
| 547 | 600 | notifier); |
| 548 | 601 | break; |
| 602 | + case DEV_PM_QOS_MIN_FREQUENCY: | |
| 603 | + ret = freq_qos_remove_notifier(&dev->power.qos->freq, | |
| 604 | + FREQ_QOS_MIN, notifier); | |
| 605 | + break; | |
| 606 | + case DEV_PM_QOS_MAX_FREQUENCY: | |
| 607 | + ret = freq_qos_remove_notifier(&dev->power.qos->freq, | |
| 608 | + FREQ_QOS_MAX, notifier); | |
| 609 | + break; | |
| 549 | 610 | default: |
| 550 | 611 | WARN_ON(1); |
| 551 | 612 | ret = -EINVAL; |
| @@ -48,9 +48,9 @@ config PPC_PASEMI_CPUFREQ · 47 unmodified lines | ||
| 48 | 48 | PWRficient processors. |
| 49 | 49 | |
| 50 | 50 | config POWERNV_CPUFREQ |
| 51 | − tristate "CPU frequency scaling for IBM POWERNV platform" | |
| 52 | − depends on PPC_POWERNV | |
| 53 | − default y | |
| 54 | − help | |
| 51 | + tristate "CPU frequency scaling for IBM POWERNV platform" | |
| 52 | + depends on PPC_POWERNV | |
| 53 | + default y | |
| 54 | + help | |
| 55 | 55 | This adds support for CPU frequency switching on IBM POWERNV |
| 56 | 56 | platform |
| @@ -4,17 +4,17 @@ · 3 unmodified lines | ||
| 4 | 4 | # |
| 5 | 5 | |
| 6 | 6 | config X86_INTEL_PSTATE |
| 7 | − bool "Intel P state control" | |
| 8 | − depends on X86 | |
| 9 | − select ACPI_PROCESSOR if ACPI | |
| 10 | − select ACPI_CPPC_LIB if X86_64 && ACPI && SCHED_MC_PRIO | |
| 11 | − help | |
| 12 | − This driver provides a P state for Intel core processors. | |
| 7 | + bool "Intel P state control" | |
| 8 | + depends on X86 | |
| 9 | + select ACPI_PROCESSOR if ACPI | |
| 10 | + select ACPI_CPPC_LIB if X86_64 && ACPI && SCHED_MC_PRIO | |
| 11 | + help | |
| 12 | + This driver provides a P state for Intel core processors. | |
| 13 | 13 | The driver implements an internal governor and will become |
| 14 | − the scaling driver and governor for Sandy bridge processors. | |
| 14 | + the scaling driver and governor for Sandy bridge processors. | |
| 15 | 15 | |
| 16 | 16 | When this driver is enabled it will become the preferred |
| 17 | − scaling driver for Sandy bridge processors. | |
| 17 | + scaling driver for Sandy bridge processors. | |
| 18 | 18 | |
| 19 | 19 | If in doubt, say N. |
| 20 | 20 | |
| @@ -16,7 +16,7 @@ config CPU_IDLE · 15 unmodified lines | ||
| 16 | 16 | if CPU_IDLE |
| 17 | 17 | |
| 18 | 18 | config CPU_IDLE_MULTIPLE_DRIVERS |
| 19 | − bool | |
| 19 | + bool | |
| 20 | 20 | |
| 21 | 21 | config CPU_IDLE_GOV_LADDER |
| 22 | 22 | bool "Ladder governor (for periodic timer tick)" |
| @@ -63,13 +63,13 @@ source "drivers/cpuidle/Kconfig.powerpc" · 40 unmodified lines | ||
| 63 | 63 | endmenu |
| 64 | 64 | |
| 65 | 65 | config HALTPOLL_CPUIDLE |
| 66 | − tristate "Halt poll cpuidle driver" | |
| 67 | − depends on X86 && KVM_GUEST | |
| 68 | − default y | |
| 69 | − help | |
| 70 | − This option enables halt poll cpuidle driver, which allows to poll | |
| 71 | − before halting in the guest (more efficient than polling in the | |
| 72 | − host via halt_poll_ns for some scenarios). | |
| 66 | + tristate "Halt poll cpuidle driver" | |
| 67 | + depends on X86 && KVM_GUEST | |
| 68 | + default y | |
| 69 | + help | |
| 70 | + This option enables halt poll cpuidle driver, which allows to poll | |
| 71 | + before halting in the guest (more efficient than polling in the | |
| 72 | + host via halt_poll_ns for some scenarios). | |
| 73 | 73 | |
| 74 | 74 | endif |
| 75 | 75 | |
| @@ -3,15 +3,15 @@ · 2 unmodified lines | ||
| 3 | 3 | # ARM CPU Idle drivers |
| 4 | 4 | # |
| 5 | 5 | config ARM_CPUIDLE |
| 6 | − bool "Generic ARM/ARM64 CPU idle Driver" | |
| 7 | − select DT_IDLE_STATES | |
| 6 | + bool "Generic ARM/ARM64 CPU idle Driver" | |
| 7 | + select DT_IDLE_STATES | |
| 8 | 8 | select CPU_IDLE_MULTIPLE_DRIVERS |
| 9 | − help | |
| 10 | − Select this to enable generic cpuidle driver for ARM. | |
| 11 | − It provides a generic idle driver whose idle states are configured | |
| 12 | − at run-time through DT nodes. The CPUidle suspend backend is | |
| 13 | − initialized by calling the CPU operations init idle hook | |
| 14 | − provided by architecture code. | |
| 9 | + help | |
| 10 | + Select this to enable generic cpuidle driver for ARM. | |
| 11 | + It provides a generic idle driver whose idle states are configured | |
| 12 | + at run-time through DT nodes. The CPUidle suspend backend is | |
| 13 | + initialized by calling the CPU operations init idle hook | |
| 14 | + provided by architecture code. | |
| 15 | 15 | |
| 16 | 16 | config ARM_PSCI_CPUIDLE |
| 17 | 17 | bool "PSCI CPU idle Driver" |
| @@ -65,21 +65,21 @@ config ARM_U8500_CPUIDLE · 47 unmodified lines | ||
| 65 | 65 | bool "Cpu Idle Driver for the ST-E u8500 processors" |
| 66 | 66 | depends on ARCH_U8500 && !ARM64 |
| 67 | 67 | help |
| 68 | − Select this to enable cpuidle for ST-E u8500 processors | |
| 68 | + Select this to enable cpuidle for ST-E u8500 processors. | |
| 69 | 69 | |
| 70 | 70 | config ARM_AT91_CPUIDLE |
| 71 | 71 | bool "Cpu Idle Driver for the AT91 processors" |
| 72 | 72 | default y |
| 73 | 73 | depends on ARCH_AT91 && !ARM64 |
| 74 | 74 | help |
| 75 | − Select this to enable cpuidle for AT91 processors | |
| 75 | + Select this to enable cpuidle for AT91 processors. | |
| 76 | 76 | |
| 77 | 77 | config ARM_EXYNOS_CPUIDLE |
| 78 | 78 | bool "Cpu Idle Driver for the Exynos processors" |
| 79 | 79 | depends on ARCH_EXYNOS && !ARM64 |
| 80 | 80 | select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP |
| 81 | 81 | help |
| 82 | − Select this to enable cpuidle for Exynos processors | |
| 82 | + Select this to enable cpuidle for Exynos processors. | |
| 83 | 83 | |
| 84 | 84 | config ARM_MVEBU_V7_CPUIDLE |
| 85 | 85 | bool "CPU Idle Driver for mvebu v7 family processors" |
| @@ -572,7 +572,7 @@ static int __cpuidle_register_device(struct cpuidle_device *dev) · 571 unmodified lines | ||
| 572 | 572 | return -EINVAL; |
| 573 | 573 | |
| 574 | 574 | for (i = 0; i < drv->state_count; i++) |
| 575 | − if (drv->states[i].disabled) | |
| 575 | + if (drv->states[i].flags & CPUIDLE_FLAG_UNUSABLE) | |
| 576 | 576 | dev->states_usage[i].disable |= CPUIDLE_STATE_DISABLED_BY_DRIVER; |
| 577 | 577 | |
| 578 | 578 | per_cpu(cpuidle_devices, dev->cpu) = dev; |
| @@ -53,7 +53,6 @@ void cpuidle_poll_state_init(struct cpuidle_driver *drv) · 52 unmodified lines | ||
| 53 | 53 | state->target_residency_ns = 0; |
| 54 | 54 | state->power_usage = -1; |
| 55 | 55 | state->enter = poll_idle; |
| 56 | − state->disabled = false; | |
| 57 | 56 | state->flags = CPUIDLE_FLAG_POLLING; |
| 58 | 57 | } |
| 59 | 58 | EXPORT_SYMBOL_GPL(cpuidle_poll_state_init); |
| @@ -921,7 +921,9 @@ int devfreq_suspend_device(struct devfreq *devfreq) · 920 unmodified lines | ||
| 921 | 921 | } |
| 922 | 922 | |
| 923 | 923 | if (devfreq->suspend_freq) { |
| 924 | + mutex_lock(&devfreq->lock); | |
| 924 | 925 | ret = devfreq_set_target(devfreq, devfreq->suspend_freq, 0); |
| 926 | + mutex_unlock(&devfreq->lock); | |
| 925 | 927 | if (ret) |
| 926 | 928 | return ret; |
| 927 | 929 | } |
| @@ -949,7 +951,9 @@ int devfreq_resume_device(struct devfreq *devfreq) · 21 unmodified lines | ||
| 949 | 951 | return 0; |
| 950 | 952 | |
| 951 | 953 | if (devfreq->resume_freq) { |
| 954 | + mutex_lock(&devfreq->lock); | |
| 952 | 955 | ret = devfreq_set_target(devfreq, devfreq->resume_freq, 0); |
| 956 | + mutex_unlock(&devfreq->lock); | |
| 953 | 957 | if (ret) |
| 954 | 958 | return ret; |
| 955 | 959 | } |
| @@ -1291,8 +1291,8 @@ static void sklh_idle_state_table_update(void) · 1290 unmodified lines | ||
| 1291 | 1291 | return; |
| 1292 | 1292 | } |
| 1293 | 1293 | |
| 1294 | − skl_cstates[5].disabled = 1; /* C8-SKL */ | |
| 1295 | − skl_cstates[6].disabled = 1; /* C9-SKL */ | |
| 1294 | + skl_cstates[5].flags |= CPUIDLE_FLAG_UNUSABLE; /* C8-SKL */ | |
| 1295 | + skl_cstates[6].flags |= CPUIDLE_FLAG_UNUSABLE; /* C9-SKL */ | |
| 1296 | 1296 | } |
| 1297 | 1297 | /* |
| 1298 | 1298 | * intel_idle_state_table_update() |
| @@ -1355,7 +1355,7 @@ static void __init intel_idle_cpuidle_driver_init(void) · 56 unmodified lines | ||
| 1355 | 1355 | continue; |
| 1356 | 1356 | |
| 1357 | 1357 | /* if state marked as disabled, skip it */ |
| 1358 | − if (cpuidle_state_table[cstate].disabled != 0) { | |
| 1358 | + if (cpuidle_state_table[cstate].flags & CPUIDLE_FLAG_UNUSABLE) { | |
| 1359 | 1359 | pr_debug("state %s is disabled\n", |
| 1360 | 1360 | cpuidle_state_table[cstate].name); |
| 1361 | 1361 | continue; |
| @@ -13,9 +13,9 @@ menuconfig POWER_AVS · 12 unmodified lines | ||
| 13 | 13 | Say Y here to enable Adaptive Voltage Scaling class support. |
| 14 | 14 | |
| 15 | 15 | config ROCKCHIP_IODOMAIN |
| 16 | − tristate "Rockchip IO domain support" | |
| 17 | − depends on POWER_AVS && ARCH_ROCKCHIP && OF | |
| 18 | − help | |
| 19 | − Say y here to enable support io domains on Rockchip SoCs. It is | |
| 20 | − necessary for the io domain setting of the SoC to match the | |
| 21 | − voltage supplied by the regulators. | |
| 16 | + tristate "Rockchip IO domain support" | |
| 17 | + depends on POWER_AVS && ARCH_ROCKCHIP && OF | |
| 18 | + help | |
| 19 | + Say y here to enable support io domains on Rockchip SoCs. It is | |
| 20 | + necessary for the io domain setting of the SoC to match the | |
| 21 | + voltage supplied by the regulators. | |
| @@ -54,7 +54,6 @@ struct cpuidle_state { · 53 unmodified lines | ||
| 54 | 54 | unsigned int exit_latency; /* in US */ |
| 55 | 55 | int power_usage; /* in mW */ |
| 56 | 56 | unsigned int target_residency; /* in US */ |
| 57 | − bool disabled; /* disabled on all CPUs */ | |
| 58 | 57 | |
| 59 | 58 | int (*enter) (struct cpuidle_device *dev, |
| 60 | 59 | struct cpuidle_driver *drv, |
| @@ -77,6 +76,7 @@ struct cpuidle_state { · 16 unmodified lines | ||
| 77 | 76 | #define CPUIDLE_FLAG_POLLING BIT(0) /* polling state */ |
| 78 | 77 | #define CPUIDLE_FLAG_COUPLED BIT(1) /* state applies to multiple cpus */ |
| 79 | 78 | #define CPUIDLE_FLAG_TIMER_STOP BIT(2) /* timer is stopped on this state */ |
| 79 | +#define CPUIDLE_FLAG_UNUSABLE BIT(3) /* avoid using this state */ | |
| 80 | 80 | |
| 81 | 81 | struct cpuidle_device_kobj; |
| 82 | 82 | struct cpuidle_state_kobj; |
| @@ -34,6 +34,8 @@ enum pm_qos_flags_status { · 33 unmodified lines | ||
| 34 | 34 | #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY |
| 35 | 35 | #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS |
| 36 | 36 | #define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0 |
| 37 | +#define PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE 0 | |
| 38 | +#define PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE FREQ_QOS_MAX_DEFAULT_VALUE | |
| 37 | 39 | #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1) |
| 38 | 40 | |
| 39 | 41 | #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) |
| @@ -49,21 +51,6 @@ struct pm_qos_flags_request { · 9 unmodified lines | ||
| 49 | 51 | s32 flags; /* Do not change to 64 bit */ |
| 50 | 52 | }; |
| 51 | 53 | |
| 52 | −enum dev_pm_qos_req_type { | |
| 53 | − DEV_PM_QOS_RESUME_LATENCY = 1, | |
| 54 | − DEV_PM_QOS_LATENCY_TOLERANCE, | |
| 55 | − DEV_PM_QOS_FLAGS, | |
| 56 | −}; | |
| 57 | − | |
| 58 | −struct dev_pm_qos_request { | |
| 59 | − enum dev_pm_qos_req_type type; | |
| 60 | − union { | |
| 61 | − struct plist_node pnode; | |
| 62 | − struct pm_qos_flags_request flr; | |
| 63 | − } data; | |
| 64 | − struct device *dev; | |
| 65 | −}; | |
| 66 | − | |
| 67 | 54 | enum pm_qos_type { |
| 68 | 55 | PM_QOS_UNITIALIZED, |
| 69 | 56 | PM_QOS_MAX, /* return the largest value */ |
| @@ -90,9 +77,51 @@ struct pm_qos_flags { · 20 unmodified lines | ||
| 90 | 77 | s32 effective_flags; /* Do not change to 64 bit */ |
| 91 | 78 | }; |
| 92 | 79 | |
| 80 | + | |
| 81 | +#define FREQ_QOS_MIN_DEFAULT_VALUE 0 | |
| 82 | +#define FREQ_QOS_MAX_DEFAULT_VALUE S32_MAX | |
| 83 | + | |
| 84 | +enum freq_qos_req_type { | |
| 85 | + FREQ_QOS_MIN = 1, | |
| 86 | + FREQ_QOS_MAX, | |
| 87 | +}; | |
| 88 | + | |
| 89 | +struct freq_constraints { | |
| 90 | + struct pm_qos_constraints min_freq; | |
| 91 | + struct blocking_notifier_head min_freq_notifiers; | |
| 92 | + struct pm_qos_constraints max_freq; | |
| 93 | + struct blocking_notifier_head max_freq_notifiers; | |
| 94 | +}; | |
| 95 | + | |
| 96 | +struct freq_qos_request { | |
| 97 | + enum freq_qos_req_type type; | |
| 98 | + struct plist_node pnode; | |
| 99 | + struct freq_constraints *qos; | |
| 100 | +}; | |
| 101 | + | |
| 102 | + | |
| 103 | +enum dev_pm_qos_req_type { | |
| 104 | + DEV_PM_QOS_RESUME_LATENCY = 1, | |
| 105 | + DEV_PM_QOS_LATENCY_TOLERANCE, | |
| 106 | + DEV_PM_QOS_MIN_FREQUENCY, | |
| 107 | + DEV_PM_QOS_MAX_FREQUENCY, | |
| 108 | + DEV_PM_QOS_FLAGS, | |
| 109 | +}; | |
| 110 | + | |
| 111 | +struct dev_pm_qos_request { | |
| 112 | + enum dev_pm_qos_req_type type; | |
| 113 | + union { | |
| 114 | + struct plist_node pnode; | |
| 115 | + struct pm_qos_flags_request flr; | |
| 116 | + struct freq_qos_request freq; | |
| 117 | + } data; | |
| 118 | + struct device *dev; | |
| 119 | +}; | |
| 120 | + | |
| 93 | 121 | struct dev_pm_qos { |
| 94 | 122 | struct pm_qos_constraints resume_latency; |
| 95 | 123 | struct pm_qos_constraints latency_tolerance; |
| 124 | + struct freq_constraints freq; | |
| 96 | 125 | struct pm_qos_flags flags; |
| 97 | 126 | struct dev_pm_qos_request *resume_latency_req; |
| 98 | 127 | struct dev_pm_qos_request *latency_tolerance_req; |
| @@ -191,6 +220,10 @@ static inline s32 dev_pm_qos_read_value(struct device *dev, · 92 unmodified lines | ||
| 191 | 220 | switch (type) { |
| 192 | 221 | case DEV_PM_QOS_RESUME_LATENCY: |
| 193 | 222 | return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; |
| 223 | + case DEV_PM_QOS_MIN_FREQUENCY: | |
| 224 | + return PM_QOS_MIN_FREQUENCY_DEFAULT_VALUE; | |
| 225 | + case DEV_PM_QOS_MAX_FREQUENCY: | |
| 226 | + return PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE; | |
| 194 | 227 | default: |
| 195 | 228 | WARN_ON(1); |
| 196 | 229 | return 0; |
| @@ -255,27 +288,6 @@ static inline s32 dev_pm_qos_raw_resume_latency(struct device *dev) · 58 unmodified lines | ||
| 255 | 288 | } |
| 256 | 289 | #endif |
| 257 | 290 | |
| 258 | −#define FREQ_QOS_MIN_DEFAULT_VALUE 0 | |
| 259 | −#define FREQ_QOS_MAX_DEFAULT_VALUE (-1) | |
| 260 | − | |
| 261 | −enum freq_qos_req_type { | |
| 262 | − FREQ_QOS_MIN = 1, | |
| 263 | − FREQ_QOS_MAX, | |
| 264 | −}; | |
| 265 | − | |
| 266 | −struct freq_constraints { | |
| 267 | − struct pm_qos_constraints min_freq; | |
| 268 | − struct blocking_notifier_head min_freq_notifiers; | |
| 269 | − struct pm_qos_constraints max_freq; | |
| 270 | − struct blocking_notifier_head max_freq_notifiers; | |
| 271 | −}; | |
| 272 | − | |
| 273 | −struct freq_qos_request { | |
| 274 | − enum freq_qos_req_type type; | |
| 275 | − struct plist_node pnode; | |
| 276 | − struct freq_constraints *qos; | |
| 277 | −}; | |
| 278 | − | |
| 279 | 291 | static inline int freq_qos_request_active(struct freq_qos_request *req) |
| 280 | 292 | { |
| 281 | 293 | return !IS_ERR_OR_NULL(req->qos); |
| @@ -291,6 +303,8 @@ int freq_qos_add_request(struct freq_constraints *qos, · 9 unmodified lines | ||
| 291 | 303 | enum freq_qos_req_type type, s32 value); |
| 292 | 304 | int freq_qos_update_request(struct freq_qos_request *req, s32 new_value); |
| 293 | 305 | int freq_qos_remove_request(struct freq_qos_request *req); |
| 306 | +int freq_qos_apply(struct freq_qos_request *req, | |
| 307 | + enum pm_qos_req_action action, s32 value); | |
| 294 | 308 | |
| 295 | 309 | int freq_qos_add_notifier(struct freq_constraints *qos, |
| 296 | 310 | enum freq_qos_req_type type, |
| @@ -714,8 +714,10 @@ s32 freq_qos_read_value(struct freq_constraints *qos, · 713 unmodified lines | ||
| 714 | 714 | * @req: Constraint request to apply. |
| 715 | 715 | * @action: Action to perform (add/update/remove). |
| 716 | 716 | * @value: Value to assign to the QoS request. |
| 717 | + * | |
| 718 | + * This is only meant to be called from inside pm_qos, not drivers. | |
| 717 | 719 | */ |
| 718 | −static int freq_qos_apply(struct freq_qos_request *req, | |
| 720 | +int freq_qos_apply(struct freq_qos_request *req, | |
| 719 | 721 | enum pm_qos_req_action action, s32 value) |
| 720 | 722 | { |
| 721 | 723 | int ret; |