Commit bdfd71e6 authored by Russell King's avatar Russell King

[CPUFREQ] Update ARM CPUFREQ drivers

A 4 patch set from Dominik Brodowski, tested and fixed up by rmk
for ARM.

(1)

- the system3.c cpufreq notifier couldn't possibly compile before because
      cpufreq_updateminmax has been undefined for ages.
- clean up sa1100fb.c notifier to specify exactly what's left TBD.
- double #include of cpufreq.c in drivers/pcmcia/sa1100_generic.c

(2)

Split up config symbols, add help text for CPU_FREQ_PROC_INTF

(3)

- update the Integrator CPUfreq driver so that it can get along with
  cpufreq policies.
- modularize Integrator CPUfreq driver (all it did with
  !CONFIG_CPU_FREQ was to print out some debug information)

(4)

- update the SA11x0 CPUfreq drivers so that they can get along with
  cpufreq policies and governors.
- update the cpufreq core so that cpufreq_get() returns something
  sensible during the first ->setpolicy or ->target call.
parent e3c33e3b
......@@ -534,15 +534,43 @@ config CPU_FREQ
written) to implement the policy. If you don't understand what this
is all about, it's safe to say 'N'.
config CPU_FREQ_SA1100
bool
depends on CPU_FREQ && SA1100_LART
default y
config CPU_FREQ_SA1110
bool
depends on CPU_FREQ && (SA1100_ASSABET || SA1100_CERF || SA1100_PT_SYSTEM3)
default y
config CPU_FREQ_INTEGRATOR
tristate "CPUfreq driver for ARM Integrator CPUs"
depends on ARCH_INTEGRATOR && CPU_FREQ
default y
help
This enables the CPUfreq driver for ARM Integrator CPUs.
For details, take a look at linux/Documentation/cpufreq.
If in doubt, say Y.
config CPU_FREQ_24_API
bool
depends on CPU_FREQ
default y
config CPU_FREQ_PROC_INTF
tristate
depends on CPU_FREQ
default y
tristate "/proc/cpufreq interface (deprecated)"
depends on CPU_FREQ && PROC_FS
help
This enables the /proc/cpufreq interface for controlling
CPUFreq. Please note that it is recommended to use the sysfs
interface instead (which is built automatically).
For details, take a look at linux/Documentation/cpufreq.
If in doubt, say N.
source "drivers/pci/Kconfig"
......
......@@ -4,10 +4,11 @@
# Object file lists.
obj-y := arch.o cpu.o irq.o mm.o time.o
obj-y := arch.o irq.o mm.o time.o
obj-m :=
obj-n :=
obj- :=
obj-$(CONFIG_LEDS) += leds.o
obj-$(CONFIG_PCI) += pci_v3.o pci.o
obj-$(CONFIG_CPU_FREQ_INTEGRATOR) += cpu.o
......@@ -23,6 +23,8 @@
#include <asm/hardware.h>
#include <asm/io.h>
static struct cpufreq_driver integrator_driver;
#define CM_ID (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_ID_OFFSET)
#define CM_OSC (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_OSC_OFFSET)
#define CM_STAT (IO_ADDRESS(INTEGRATOR_HDR_BASE)+INTEGRATOR_HDR_STAT_OFFSET)
......@@ -43,7 +45,6 @@ static unsigned int vco_to_freq(struct vco vco, int factor)
return 2000 * (vco.vdw + 8) / cc_divisor[vco.od] / factor;
}
#ifdef CONFIG_CPU_FREQ
/*
* Divisor indexes in ascending divisor order
*/
......@@ -69,21 +70,17 @@ static struct vco freq_to_vco(unsigned int freq_khz, int factor)
return vco;
}
/*
* Validate the speed in khz. If it is outside our
* range, then return the lowest.
* Validate the speed policy.
*/
static int integrator_verify_speed(struct cpufreq_policy *policy)
static int integrator_verify_policy(struct cpufreq_policy *policy)
{
struct vco vco;
if (policy->max > policy->cpuinfo.max_freq)
policy->max = policy->cpuinfo.max_freq;
if (policy->max < 12000)
policy->max = 12000;
if (policy->max > 160000)
policy->max = 160000;
cpufreq_verify_within_limits(policy,
policy->cpuinfo.min_freq,
policy->cpuinfo.max_freq);
vco = freq_to_vco(policy->max, 1);
......@@ -92,12 +89,28 @@ static int integrator_verify_speed(struct cpufreq_policy *policy)
if (vco.vdw > 152)
vco.vdw = 152;
policy->min = policy->max = vco_to_freq(vco, 1);
policy->max = vco_to_freq(vco, 1);
vco = freq_to_vco(policy->min, 1);
if (vco.vdw < 4)
vco.vdw = 4;
if (vco.vdw > 152)
vco.vdw = 152;
policy->min = vco_to_freq(vco, 1);
cpufreq_verify_within_limits(policy,
policy->cpuinfo.min_freq,
policy->cpuinfo.max_freq);
return 0;
}
static int integrator_set_policy(struct cpufreq_policy *policy)
static int integrator_set_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
unsigned long cpus_allowed;
int cpu = policy->cpu;
......@@ -121,9 +134,18 @@ static int integrator_set_policy(struct cpufreq_policy *policy)
cm_osc = __raw_readl(CM_OSC);
vco.od = (cm_osc >> 8) & 7;
vco.vdw = cm_osc & 255;
freqs.old = vco_to_freq(vco, 1);
freqs.new = target_freq;
/* freq_to_vco rounds down -- so we need the next larger freq in
* case of CPUFREQ_RELATION_L.
*/
if (relation == CPUFREQ_RELATION_L)
target_freq += 1999;
if (target_freq > policy->max)
target_freq = policy->max;
vco = freq_to_vco(target_freq, 1);
freqs.new = vco_to_freq(vco, 1);
freqs.cpu = policy->cpu;
if (freqs.old == freqs.new) {
......@@ -132,7 +154,6 @@ static int integrator_set_policy(struct cpufreq_policy *policy)
}
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
vco = freq_to_vco(policy->max, 1);
cm_osc = __raw_readl(CM_OSC);
cm_osc &= 0xfffff800;
......@@ -152,48 +173,19 @@ static int integrator_set_policy(struct cpufreq_policy *policy)
return 0;
}
static struct cpufreq_policy integrator_policy = {
.cpu = 0,
.policy = CPUFREQ_POLICY_POWERSAVE,
.cpuinfo = {
.max_freq = 160000,
.min_freq = 12000,
.transition_latency = CPUFREQ_ETERNAL,
},
};
static struct cpufreq_driver integrator_driver = {
.verify = integrator_verify_speed,
.setpolicy = integrator_set_policy,
.policy = &integrator_policy,
.name = "integrator",
};
#endif
static int __init integrator_cpu_init(void)
static int integrator_cpufreq_init(struct cpufreq *policy)
{
struct cpufreq_policy *policies;
unsigned long cpus_allowed;
int cpu;
policies = kmalloc(sizeof(struct cpufreq_policy) * NR_CPUS,
GFP_KERNEL);
if (!policies) {
printk(KERN_ERR "CPU: unable to allocate policies structure\n");
return -ENOMEM;
}
cpus_allowed = current->cpus_allowed;
for (cpu = 0; cpu < NR_CPUS; cpu++) {
unsigned long cus_allowed;
unsigned int cpu = policy->cpu;
u_int cm_osc, cm_stat, mem_freq_khz;
struct vco vco;
if (!cpu_online(cpu))
continue;
cpus_allowed = current->cpus_allowed;
set_cpus_allowed(current, 1 << cpu);
BUG_ON(cpu != smp_processor_id());
/* detect memory etc. */
cm_stat = __raw_readl(CM_STAT);
cm_osc = __raw_readl(CM_OSC);
vco.od = (cm_osc >> 20) & 7;
......@@ -207,25 +199,38 @@ static int __init integrator_cpu_init(void)
vco.od = (cm_osc >> 8) & 7;
vco.vdw = cm_osc & 255;
policies[cpu].cpu = cpu;
policies[cpu].policy = CPUFREQ_POLICY_POWERSAVE,
policies[cpu].cpuinfo.max_freq = 160000;
policies[cpu].cpuinfo.min_freq = 12000;
policies[cpu].cpuinfo.transition_latency = CPUFREQ_ETERNAL;
policies[cpu].min =
policies[cpu].max = vco_to_freq(vco, 1);
}
/* set default policy and cpuinfo */
policy->policy = CPUFREQ_POLICY_PERFORMANCE;
policy->cpuinfo.max_freq = 160000;
policy->cpuinfo.min_freq = 12000;
policy->cpuinfo.transition_latency = 1000; /* 1 ms, assumed */
integrator_driver.cpu_cur_freq[policy->cpu] = policy->min = policy->max = vco_to_freq(vco, 1); /* current freq */
set_cpus_allowed(current, cpus_allowed);
#ifdef CONFIG_CPU_FREQ
integrator_driver.policy = policies;
cpufreq_register(&integrator_driver);
#else
kfree(policies);
#endif
return 0;
}
arch_initcall(integrator_cpu_init);
static struct cpufreq_driver integrator_driver = {
.verify = integrator_verify_policy,
.target = integrator_set_target,
.init = integrator_cpufreq_init,
.name = "integrator",
};
static int __init integrator_cpu_init(void)
{
return cpufreq_register_driver(&integrator_driver);
}
static void __exit integrator_cpu_exit(void)
{
cpufreq_unregister_driver(&integrator_driver);
}
MODULE_AUTHOR ("Russell M. King");
MODULE_DESCRIPTION ("cpufreq driver for ARM Integrator CPUs");
MODULE_LICENSE ("GPL");
module_init(integrator_cpu_init);
module_exit(integrator_cpu_exit);
......@@ -9,15 +9,8 @@ obj-n :=
obj- :=
led-y := leds.o
# This needs to be cleaned up. We probably need to have SA1100
# and SA1110 config symbols.
ifeq ($(CONFIG_CPU_FREQ),y)
obj-$(CONFIG_SA1100_ASSABET) += cpu-sa1110.o
obj-$(CONFIG_SA1100_CERF) += cpu-sa1110.o
obj-$(CONFIG_SA1100_HACKKIT) += cpu-sa1110.o
obj-$(CONFIG_SA1100_LART) += cpu-sa1100.o
obj-$(CONFIG_SA1100_PT_SYSTEM3) += cpu-sa1110.o
endif
obj-$(CONFIG_CPU_FREQ_SA1100) += cpu-sa1100.o
obj-$(CONFIG_CPU_FREQ_SA1110) += cpu-sa1110.o
# Specific board support
obj-$(CONFIG_SA1100_ADSBITSY) += adsbitsy.o
......
......@@ -101,7 +101,7 @@ typedef struct {
} sa1100_dram_regs_t;
static struct cpufreq_driver sa1100_driver;
static sa1100_dram_regs_t sa1100_dram_settings[] =
{
......@@ -176,60 +176,72 @@ static void sa1100_update_dram_timings(int current_speed, int new_speed)
}
}
static int sa1100_setspeed(struct cpufreq_policy *policy)
static int sa1100_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
unsigned int cur = sa11x0_getspeed();
unsigned int new_ppcr;
struct cpufreq_freqs freqs;
switch(relation){
case CPUFREQ_RELATION_L:
new_ppcr = sa11x0_freq_to_ppcr(target_freq);
if (sa11x0_ppcr_to_freq(new_ppcr) > policy->max)
new_ppcr--;
break;
case CPUFREQ_RELATION_H:
new_ppcr = sa11x0_freq_to_ppcr(target_freq);
if ((sa11x0_ppcr_to_freq(new_ppcr) > target_freq) &&
(sa11x0_ppcr_to_freq(new_ppcr - 1) >= policy->min))
mew_ppcr--;
break;
}
freqs.old = cur;
freqs.new = policy->max;
freqs.new = sa11x0_ppcr_to_freq(new_ppcr);
freqs.cpu = 0;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
if (policy->max > cur)
sa1100_update_dram_timings(cur, policy->max);
if (freqs.new > cur)
sa1100_update_dram_timings(cur, freqs.new);
PPCR = sa11x0_freq_to_ppcr(policy->max);
PPCR = new_ppcr;
if (policy->max < cur)
sa1100_update_dram_timings(cur, policy->max);
if (freqs.new < cur)
sa1100_update_dram_timings(cur, freqs.new);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return 0;
}
static struct cpufreq_policy sa1100_policy = {
.cpu = 0,
.policy = CPUFREQ_POLICY_POWERSAVE,
.cpuinfo = {
.max_freq = 287000,
.min_freq = 59000,
.transition_latency = CPUFREQ_ETERNAL,
},
};
static int __init sa1100_cpu_init(struct cpufreq_policy *policy)
{
if (policy->cpu != 0)
return -EINVAL;
sa1100_driver.cpu_cur_freq[policy->cpu] = policy->min = policy->max = sa11x0_getspeed();
policy->policy = CPUFREQ_POLICY_POWERSAVE;
policy->cpuinfo.min_freq = 59000;
policy->cpuinfo.max_freq = 287000;
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
return 0;
}
static struct cpufreq_driver sa1100_driver = {
.verify = sa11x0_verify_speed,
.setpolicy = sa1100_setspeed,
.policy = &sa1100_policy,
.target = sa1100_target,
.init = sa1100_cpu_init,
.name = "sa1100",
};
static int __init sa1100_dram_init(void)
{
int ret = -ENODEV;
if ((processor_id & CPU_SA1100_MASK) == CPU_SA1100_ID) {
sa1100_driver.cpu_cur_freq[0] =
sa1100_policy.min =
sa1100_policy.max = sa11x0_getspeed();
ret = cpufreq_register(&sa1100_driver);
}
return ret;
if ((processor_id & CPU_SA1100_MASK) == CPU_SA1100_ID)
return cpufreq_register_driver(&sa1100_driver);
else
return -ENODEV;
}
arch_initcall(sa1100_dram_init);
......@@ -32,6 +32,8 @@
#undef DEBUG
static struct cpufreq_driver sa1110_driver;
struct sdram_params {
u_char rows; /* bits */
u_char cas_latency; /* cycles */
......@@ -208,11 +210,11 @@ sdram_update_refresh(u_int cpu_khz, struct sdram_params *sdram)
}
/*
* Ok, set the CPU frequency. Since we've done the validation
* above, we can match for an exact frequency. If we don't find
* an exact match, we will to set the lowest frequency to be safe.
* Ok, set the CPU frequency.
*/
static int sa1110_setspeed(struct cpufreq_policy *policy)
static int sa1110_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
struct sdram_params *sdram = &sdram_params;
struct cpufreq_freqs freqs;
......@@ -220,8 +222,25 @@ static int sa1110_setspeed(struct cpufreq_policy *policy)
unsigned long flags;
unsigned int ppcr, unused;
ppcr = sa11x0_freq_to_ppcr(policy->max);
sdram_calculate_timing(&sd, policy->max, sdram);
switch(relation){
case CPUFREQ_RELATION_L:
ppcr = sa11x0_freq_to_ppcr(target_freq);
if (sa11x0_ppcr_to_freq(ppcr) > policy->max)
ppcr--;
break;
case CPUFREQ_RELATION_H:
ppcr = sa11x0_freq_to_ppcr(target_freq);
if (ppcr && (sa11x0_ppcr_to_freq(ppcr) > target_freq) &&
(sa11x0_ppcr_to_freq(ppcr-1) >= policy->min))
ppcr--;
break;
}
freqs.old = sa11x0_getspeed();
freqs.new = sa11x0_ppcr_to_freq(ppcr);
freqs.cpu = 0;
sdram_calculate_timing(&sd, freqs.new, sdram);
#if 0
/*
......@@ -240,10 +259,6 @@ static int sa1110_setspeed(struct cpufreq_policy *policy)
sd.mdcas[2] = 0xaaaaaaaa;
#endif
freqs.old = sa11x0_getspeed();
freqs.new = policy->max;
freqs.cpu = 0;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
/*
......@@ -288,27 +303,29 @@ static int sa1110_setspeed(struct cpufreq_policy *policy)
/*
* Now, return the SDRAM refresh back to normal.
*/
sdram_update_refresh(policy->max, sdram);
sdram_update_refresh(freqs.new, sdram);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return 0;
}
static struct cpufreq_policy sa1110_policy = {
.cpu = 0,
.policy = CPUFREQ_POLICY_POWERSAVE,
.cpuinfo = {
.max_freq = 287000,
.min_freq = 59000,
.transition_latency = CPUFREQ_ETERNAL,
},
};
static int __init sa1110_cpu_init(struct cpufreq_policy *policy)
{
if (policy->cpu != 0)
return -EINVAL;
sa1110_driver.cpu_cur_freq[policy->cpu] = policy->min = policy->max = sa11x0_getspeed();
policy->policy = CPUFREQ_POLICY_POWERSAVE;
policy->cpuinfo.min_freq = 59000;
policy->cpuinfo.max_freq = 287000;
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
return 0;
}
static struct cpufreq_driver sa1110_driver = {
.verify = sa11x0_verify_speed,
.setpolicy = sa1110_setspeed,
.policy = &sa1110_policy,
.target = sa1110_target,
.init = sa1110_cpu_init,
.name = "sa1110",
};
......@@ -333,13 +350,7 @@ static int __init sa1110_clk_init(void)
memcpy(&sdram_params, sdram, sizeof(sdram_params));
sa1110_driver.cpu_cur_freq[0] =
sa1110_policy.min =
sa1110_policy.max = sa11x0_getspeed();
sa1110_setspeed(&sa1110_policy);
return cpufreq_register(&sa1110_driver);
return cpufreq_register_driver(&sa1110_driver);
}
return 0;
......
......@@ -48,33 +48,48 @@ static const unsigned short cclk_frequency_100khz[NR_FREQS] = {
2802 /* 280.2 MHz */
};
#ifdef CONFIG_CPU_FREQ
#if defined(CONFIG_CPU_FREQ_SA1100) || defined(CONFIG_CPU_FREQ_SA1110)
/* rounds up(!) */
unsigned int sa11x0_freq_to_ppcr(unsigned int khz)
{
int i;
khz /= 100;
for (i = NR_FREQS - 1; i > 0; i--)
if (cclk_frequency_100khz[i] <= khz)
for (i = 0; i < NR_FREQS; i++)
if (cclk_frequency_100khz[i] >= khz)
break;
return i;
}
/*
* Validate the policy. We aren't able to do any fancy in-kernel
* scaling, so we force min=max, and set the policy to "performance".
* If we can't generate the precise frequency requested, round it up.
unsigned int sa11x0_ppcr_to_freq(unsigned int idx)
{
if (idx >= NR_FREQS)
return 0;
else
return cclk_frequency_100khz[idx];
}
/* make sure that only the "userspace" governor is run -- anything else wouldn't make sense on
* this platform, anyway.
*/
int sa11x0_verify_speed(struct cpufreq_policy *policy)
{
if (policy->max > policy->cpuinfo.max_freq)
policy->max = policy->cpuinfo.max_freq;
unsigned int tmp;
if (policy->cpu)
return -EINVAL;
cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
/* make sure that at least one frequency is within the policy */
tmp = cclk_frequency_100khz[sa11x0_freq_to_ppcr(policy->min)] * 100;
if (tmp > policy->max)
policy->max = tmp;
cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
policy->max = cclk_frequency_100khz[sa11x0_freq_to_ppcr(policy->max)] * 100;
policy->min = policy->max;
policy->policy = CPUFREQ_POLICY_POWERSAVE;
return 0;
}
......
......@@ -23,3 +23,4 @@ struct cpufreq_policy;
extern unsigned int sa11x0_freq_to_ppcr(unsigned int khz);
extern int sa11x0_verify_speed(struct cpufreq_policy *policy);
extern unsigned int sa11x0_getspeed(void);
extern unsigned int sa11x0_ppcr_to_freq(unsigned int idx);
......@@ -213,11 +213,17 @@ static void __init system3_init_irq(void)
static int sdram_notifier(struct notifier_block *nb, unsigned long event,
void *data)
{
struct cpufreq_policy *policy = data;
switch (event) {
case CPUFREQ_MINMAX:
cpufreq_updateminmax(data, 147500, 206000);
case CPUFREQ_ADJUST:
case CPUFREQ_INCOMPATIBLE:
cpufreq_verify_within_limits(policy, 147500, 206000);
break;
case CPUFREQ_NOTIFY:
if ((policy->min < 147500) ||
(policy->max > 206000))
panic("cpufreq failed to limit the speed\n");
break;
}
return 0;
}
......@@ -405,7 +411,7 @@ static int __init system3_init(void)
goto DONE;
}
#if defined( CONFIG_CPU_FREQ )
#ifdef CONFIG_CPU_FREQ
ret = cpufreq_register_notifier(&system3_clkchg_block);
if ( ret != 0 ) {
printk( KERN_WARNING"PT Digital Board: could not register clock scale callback\n" );
......
......@@ -47,7 +47,6 @@
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/version.h>
#include <linux/cpufreq.h>
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
......
......@@ -1590,11 +1590,21 @@ sa1100fb_freq_policy(struct notifier_block *nb, unsigned long val,
struct sa1100fb_info *fbi = TO_INF(nb, freq_policy);
struct cpufreq_policy *policy = data;
if (val == CPUFREQ_INCOMPATIBLE) {
switch (val) {
case CPUFREQ_ADJUST:
case CPUFREQ_INCOMPATIBLE:
printk(KERN_DEBUG "min dma period: %d ps, "
"new clock %d kHz\n", sa1100fb_min_dma_period(fbi),
policy->max);
/* todo: fill in min/max values */
break;
case CPUFREQ_NOTIFY:
do {} while(0);
/* todo: panic if min/max values aren't fulfilled
* [can't really happen unless there's a bug in the
* CPU policy verififcation process *
*/
break;
}
return 0;
}
......
......@@ -336,6 +336,13 @@ static int cpufreq_add_dev (struct device * dev)
&cpufreq_driver->policy[cpu],
sizeof(struct cpufreq_policy));
/* 2.4-API init for this CPU */
#ifdef CONFIG_CPU_FREQ_24_API
cpu_min_freq[cpu] = cpufreq_driver->policy[cpu].cpuinfo.min_freq;
cpu_max_freq[cpu] = cpufreq_driver->policy[cpu].cpuinfo.max_freq;
cpu_cur_freq[cpu] = cpufreq_driver->cpu_cur_freq[cpu];
#endif
if (cpufreq_driver->target)
cpufreq_governor(cpu, CPUFREQ_GOV_START);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment