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); |
