diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 15:21:02 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 15:21:02 -0700 |
commit | 3ed1c478eff8db80e234d5446cb378b503135888 (patch) | |
tree | e1c8e0f488ca49c49b5a31fe59add4254381dd4b /drivers/cpufreq/sparc-us3-cpufreq.c | |
parent | 151173e8ce9b95bbbbd7eedb9035cfaffbdb7cb2 (diff) | |
parent | 371deb9500831ad1afbf9ea00e373f650deaed2f (diff) |
Merge tag 'pm+acpi-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management and ACPI updates from Rafael J Wysocki:
- ARM big.LITTLE cpufreq driver from Viresh Kumar.
- exynos5440 cpufreq driver from Amit Daniel Kachhap.
- cpufreq core cleanup and code consolidation from Viresh Kumar and
Stratos Karafotis.
- cpufreq scalability improvement from Nathan Zimmer.
- AMD "frequency sensitivity feedback" powersave bias for the ondemand
cpufreq governor from Jacob Shin.
- cpuidle code consolidation and cleanups from Daniel Lezcano.
- ARM OMAP cpuidle fixes from Santosh Shilimkar and Daniel Lezcano.
- ACPICA fixes and other improvements from Bob Moore, Jung-uk Kim, Lv
Zheng, Yinghai Lu, Tang Chen, Colin Ian King, and Linn Crosetto.
- ACPI core updates related to hotplug from Toshi Kani, Paul Bolle,
Yasuaki Ishimatsu, and Rafael J Wysocki.
- Intel Lynxpoint LPSS (Low-Power Subsystem) support improvements from
Rafael J Wysocki and Andy Shevchenko.
* tag 'pm+acpi-3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (192 commits)
cpufreq: Revert incorrect commit 5800043
cpufreq: MAINTAINERS: Add co-maintainer
cpuidle: add maintainer entry
ACPI / thermal: do not always return THERMAL_TREND_RAISING for active trip points
ARM: s3c64xx: cpuidle: use init/exit common routine
cpufreq: pxa2xx: initialize variables
ACPI: video: correct acpi_video_bus_add error processing
SH: cpuidle: use init/exit common routine
ARM: S5pv210: compiling issue, ARM_S5PV210_CPUFREQ needs CONFIG_CPU_FREQ_TABLE=y
ACPI: Fix wrong parameter passed to memblock_reserve
cpuidle: fix comment format
pnp: use %*phC to dump small buffers
isapnp: remove debug leftovers
ARM: imx: cpuidle: use init/exit common routine
ARM: davinci: cpuidle: use init/exit common routine
ARM: kirkwood: cpuidle: use init/exit common routine
ARM: calxeda: cpuidle: use init/exit common routine
ARM: tegra: cpuidle: use init/exit common routine for tegra3
ARM: tegra: cpuidle: use init/exit common routine for tegra2
ARM: OMAP4: cpuidle: use init/exit common routine
...
Diffstat (limited to 'drivers/cpufreq/sparc-us3-cpufreq.c')
-rw-r--r-- | drivers/cpufreq/sparc-us3-cpufreq.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c new file mode 100644 index 00000000000..c71ee142347 --- /dev/null +++ b/drivers/cpufreq/sparc-us3-cpufreq.c @@ -0,0 +1,269 @@ +/* us3_cpufreq.c: UltraSPARC-III cpu frequency support + * + * Copyright (C) 2003 David S. Miller (davem@redhat.com) + * + * Many thanks to Dominik Brodowski for fixing up the cpufreq + * infrastructure in order to make this driver easier to implement. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/cpufreq.h> +#include <linux/threads.h> +#include <linux/slab.h> +#include <linux/init.h> + +#include <asm/head.h> +#include <asm/timer.h> + +static struct cpufreq_driver *cpufreq_us3_driver; + +struct us3_freq_percpu_info { + struct cpufreq_frequency_table table[4]; +}; + +/* Indexed by cpu number. */ +static struct us3_freq_percpu_info *us3_freq_table; + +/* UltraSPARC-III has three dividers: 1, 2, and 32. These are controlled + * in the Safari config register. + */ +#define SAFARI_CFG_DIV_1 0x0000000000000000UL +#define SAFARI_CFG_DIV_2 0x0000000040000000UL +#define SAFARI_CFG_DIV_32 0x0000000080000000UL +#define SAFARI_CFG_DIV_MASK 0x00000000C0000000UL + +static unsigned long read_safari_cfg(void) +{ + unsigned long ret; + + __asm__ __volatile__("ldxa [%%g0] %1, %0" + : "=&r" (ret) + : "i" (ASI_SAFARI_CONFIG)); + return ret; +} + +static void write_safari_cfg(unsigned long val) +{ + __asm__ __volatile__("stxa %0, [%%g0] %1\n\t" + "membar #Sync" + : /* no outputs */ + : "r" (val), "i" (ASI_SAFARI_CONFIG) + : "memory"); +} + +static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg) +{ + unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000; + unsigned long ret; + + switch (safari_cfg & SAFARI_CFG_DIV_MASK) { + case SAFARI_CFG_DIV_1: + ret = clock_tick / 1; + break; + case SAFARI_CFG_DIV_2: + ret = clock_tick / 2; + break; + case SAFARI_CFG_DIV_32: + ret = clock_tick / 32; + break; + default: + BUG(); + } + + return ret; +} + +static unsigned int us3_freq_get(unsigned int cpu) +{ + cpumask_t cpus_allowed; + unsigned long reg; + unsigned int ret; + + cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current)); + set_cpus_allowed_ptr(current, cpumask_of(cpu)); + + reg = read_safari_cfg(); + ret = get_current_freq(cpu, reg); + + set_cpus_allowed_ptr(current, &cpus_allowed); + + return ret; +} + +static void us3_set_cpu_divider_index(struct cpufreq_policy *policy, + unsigned int index) +{ + unsigned int cpu = policy->cpu; + unsigned long new_bits, new_freq, reg; + cpumask_t cpus_allowed; + struct cpufreq_freqs freqs; + + cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current)); + set_cpus_allowed_ptr(current, cpumask_of(cpu)); + + new_freq = sparc64_get_clock_tick(cpu) / 1000; + switch (index) { + case 0: + new_bits = SAFARI_CFG_DIV_1; + new_freq /= 1; + break; + case 1: + new_bits = SAFARI_CFG_DIV_2; + new_freq /= 2; + break; + case 2: + new_bits = SAFARI_CFG_DIV_32; + new_freq /= 32; + break; + + default: + BUG(); + } + + reg = read_safari_cfg(); + + freqs.old = get_current_freq(cpu, reg); + freqs.new = new_freq; + cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); + + reg &= ~SAFARI_CFG_DIV_MASK; + reg |= new_bits; + write_safari_cfg(reg); + + cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); + + set_cpus_allowed_ptr(current, &cpus_allowed); +} + +static int us3_freq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int new_index = 0; + + if (cpufreq_frequency_table_target(policy, + &us3_freq_table[policy->cpu].table[0], + target_freq, + relation, + &new_index)) + return -EINVAL; + + us3_set_cpu_divider_index(policy, new_index); + + return 0; +} + +static int us3_freq_verify(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, + &us3_freq_table[policy->cpu].table[0]); +} + +static int __init us3_freq_cpu_init(struct cpufreq_policy *policy) +{ + unsigned int cpu = policy->cpu; + unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000; + struct cpufreq_frequency_table *table = + &us3_freq_table[cpu].table[0]; + + table[0].index = 0; + table[0].frequency = clock_tick / 1; + table[1].index = 1; + table[1].frequency = clock_tick / 2; + table[2].index = 2; + table[2].frequency = clock_tick / 32; + table[3].index = 0; + table[3].frequency = CPUFREQ_TABLE_END; + + policy->cpuinfo.transition_latency = 0; + policy->cur = clock_tick; + + return cpufreq_frequency_table_cpuinfo(policy, table); +} + +static int us3_freq_cpu_exit(struct cpufreq_policy *policy) +{ + if (cpufreq_us3_driver) + us3_set_cpu_divider_index(policy, 0); + + return 0; +} + +static int __init us3_freq_init(void) +{ + unsigned long manuf, impl, ver; + int ret; + + if (tlb_type != cheetah && tlb_type != cheetah_plus) + return -ENODEV; + + __asm__("rdpr %%ver, %0" : "=r" (ver)); + manuf = ((ver >> 48) & 0xffff); + impl = ((ver >> 32) & 0xffff); + + if (manuf == CHEETAH_MANUF && + (impl == CHEETAH_IMPL || + impl == CHEETAH_PLUS_IMPL || + impl == JAGUAR_IMPL || + impl == PANTHER_IMPL)) { + struct cpufreq_driver *driver; + + ret = -ENOMEM; + driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL); + if (!driver) + goto err_out; + + us3_freq_table = kzalloc( + (NR_CPUS * sizeof(struct us3_freq_percpu_info)), + GFP_KERNEL); + if (!us3_freq_table) + goto err_out; + + driver->init = us3_freq_cpu_init; + driver->verify = us3_freq_verify; + driver->target = us3_freq_target; + driver->get = us3_freq_get; + driver->exit = us3_freq_cpu_exit; + driver->owner = THIS_MODULE, + strcpy(driver->name, "UltraSPARC-III"); + + cpufreq_us3_driver = driver; + ret = cpufreq_register_driver(driver); + if (ret) + goto err_out; + + return 0; + +err_out: + if (driver) { + kfree(driver); + cpufreq_us3_driver = NULL; + } + kfree(us3_freq_table); + us3_freq_table = NULL; + return ret; + } + + return -ENODEV; +} + +static void __exit us3_freq_exit(void) +{ + if (cpufreq_us3_driver) { + cpufreq_unregister_driver(cpufreq_us3_driver); + kfree(cpufreq_us3_driver); + cpufreq_us3_driver = NULL; + kfree(us3_freq_table); + us3_freq_table = NULL; + } +} + +MODULE_AUTHOR("David S. Miller <davem@redhat.com>"); +MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III"); +MODULE_LICENSE("GPL"); + +module_init(us3_freq_init); +module_exit(us3_freq_exit); |