diff options
Diffstat (limited to 'drivers/cpufreq/cpufreq.c')
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 67 |
1 files changed, 60 insertions, 7 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index ae11dd51f81d..aed2b0cb83dc 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c | |||
@@ -1816,20 +1816,55 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier); | |||
1816 | * GOVERNORS * | 1816 | * GOVERNORS * |
1817 | *********************************************************************/ | 1817 | *********************************************************************/ |
1818 | 1818 | ||
1819 | /* Must set freqs->new to intermediate frequency */ | ||
1820 | static int __target_intermediate(struct cpufreq_policy *policy, | ||
1821 | struct cpufreq_freqs *freqs, int index) | ||
1822 | { | ||
1823 | int ret; | ||
1824 | |||
1825 | freqs->new = cpufreq_driver->get_intermediate(policy, index); | ||
1826 | |||
1827 | /* We don't need to switch to intermediate freq */ | ||
1828 | if (!freqs->new) | ||
1829 | return 0; | ||
1830 | |||
1831 | pr_debug("%s: cpu: %d, switching to intermediate freq: oldfreq: %u, intermediate freq: %u\n", | ||
1832 | __func__, policy->cpu, freqs->old, freqs->new); | ||
1833 | |||
1834 | cpufreq_freq_transition_begin(policy, freqs); | ||
1835 | ret = cpufreq_driver->target_intermediate(policy, index); | ||
1836 | cpufreq_freq_transition_end(policy, freqs, ret); | ||
1837 | |||
1838 | if (ret) | ||
1839 | pr_err("%s: Failed to change to intermediate frequency: %d\n", | ||
1840 | __func__, ret); | ||
1841 | |||
1842 | return ret; | ||
1843 | } | ||
1844 | |||
1819 | static int __target_index(struct cpufreq_policy *policy, | 1845 | static int __target_index(struct cpufreq_policy *policy, |
1820 | struct cpufreq_frequency_table *freq_table, int index) | 1846 | struct cpufreq_frequency_table *freq_table, int index) |
1821 | { | 1847 | { |
1822 | struct cpufreq_freqs freqs; | 1848 | struct cpufreq_freqs freqs = {.old = policy->cur, .flags = 0}; |
1849 | unsigned int intermediate_freq = 0; | ||
1823 | int retval = -EINVAL; | 1850 | int retval = -EINVAL; |
1824 | bool notify; | 1851 | bool notify; |
1825 | 1852 | ||
1826 | notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION); | 1853 | notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION); |
1827 | |||
1828 | if (notify) { | 1854 | if (notify) { |
1829 | freqs.old = policy->cur; | 1855 | /* Handle switching to intermediate frequency */ |
1830 | freqs.new = freq_table[index].frequency; | 1856 | if (cpufreq_driver->get_intermediate) { |
1831 | freqs.flags = 0; | 1857 | retval = __target_intermediate(policy, &freqs, index); |
1858 | if (retval) | ||
1859 | return retval; | ||
1860 | |||
1861 | intermediate_freq = freqs.new; | ||
1862 | /* Set old freq to intermediate */ | ||
1863 | if (intermediate_freq) | ||
1864 | freqs.old = freqs.new; | ||
1865 | } | ||
1832 | 1866 | ||
1867 | freqs.new = freq_table[index].frequency; | ||
1833 | pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n", | 1868 | pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n", |
1834 | __func__, policy->cpu, freqs.old, freqs.new); | 1869 | __func__, policy->cpu, freqs.old, freqs.new); |
1835 | 1870 | ||
@@ -1841,9 +1876,23 @@ static int __target_index(struct cpufreq_policy *policy, | |||
1841 | pr_err("%s: Failed to change cpu frequency: %d\n", __func__, | 1876 | pr_err("%s: Failed to change cpu frequency: %d\n", __func__, |
1842 | retval); | 1877 | retval); |
1843 | 1878 | ||
1844 | if (notify) | 1879 | if (notify) { |
1845 | cpufreq_freq_transition_end(policy, &freqs, retval); | 1880 | cpufreq_freq_transition_end(policy, &freqs, retval); |
1846 | 1881 | ||
1882 | /* | ||
1883 | * Failed after setting to intermediate freq? Driver should have | ||
1884 | * reverted back to initial frequency and so should we. Check | ||
1885 | * here for intermediate_freq instead of get_intermediate, in | ||
1886 | * case we have't switched to intermediate freq at all. | ||
1887 | */ | ||
1888 | if (unlikely(retval && intermediate_freq)) { | ||
1889 | freqs.old = intermediate_freq; | ||
1890 | freqs.new = policy->restore_freq; | ||
1891 | cpufreq_freq_transition_begin(policy, &freqs); | ||
1892 | cpufreq_freq_transition_end(policy, &freqs, 0); | ||
1893 | } | ||
1894 | } | ||
1895 | |||
1847 | return retval; | 1896 | return retval; |
1848 | } | 1897 | } |
1849 | 1898 | ||
@@ -1875,6 +1924,9 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, | |||
1875 | if (target_freq == policy->cur) | 1924 | if (target_freq == policy->cur) |
1876 | return 0; | 1925 | return 0; |
1877 | 1926 | ||
1927 | /* Save last value to restore later on errors */ | ||
1928 | policy->restore_freq = policy->cur; | ||
1929 | |||
1878 | if (cpufreq_driver->target) | 1930 | if (cpufreq_driver->target) |
1879 | retval = cpufreq_driver->target(policy, target_freq, relation); | 1931 | retval = cpufreq_driver->target(policy, target_freq, relation); |
1880 | else if (cpufreq_driver->target_index) { | 1932 | else if (cpufreq_driver->target_index) { |
@@ -2361,7 +2413,8 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) | |||
2361 | !(driver_data->setpolicy || driver_data->target_index || | 2413 | !(driver_data->setpolicy || driver_data->target_index || |
2362 | driver_data->target) || | 2414 | driver_data->target) || |
2363 | (driver_data->setpolicy && (driver_data->target_index || | 2415 | (driver_data->setpolicy && (driver_data->target_index || |
2364 | driver_data->target))) | 2416 | driver_data->target)) || |
2417 | (!!driver_data->get_intermediate != !!driver_data->target_intermediate)) | ||
2365 | return -EINVAL; | 2418 | return -EINVAL; |
2366 | 2419 | ||
2367 | pr_debug("trying to register driver %s\n", driver_data->name); | 2420 | pr_debug("trying to register driver %s\n", driver_data->name); |