Hi,
I was hoping that someone might know something about the cpufreq stuff
and would be willing to take a look at what I have so far.
It all loads up and tries to run. But if I switch to the conservative
governor it starts trying to switch to some 4.2 billion KHz (-49000). It
(the system) seems to eventually freeze up. The ondemand governor sort
of has the opposite effect. It wants to go to 0 KHz?
This is built on a module called pll_if that handles the low level
interface to the PLL register (HID1). It (pll_if) includes an optional
sysfs attribute that can be used to monkey with the PLL. I have a perl
script to allow one to modify the PLL in a more human friendly way. Via
the sysfs attribute, pll_if is useable. So I don't think the problem is
in it (though it is supposed to prevent you from doing anything stupid,
like switching to a PLL that is off or modifying the active PLL - but it
may not be as bullet proof as I would like).
In particular, the frequency switch in my driver is not synchronous (it
may not be completed when Target() returns). Don't know if
cpufreq_core/governors care about this? Is this notifier stuff some kind
of generic "callback" framework?
Any investigating tips would be appreciated. I don't know whether
pll_if is being asked to do something dumb and is doing it or if it is
freezing up else where.
"System" is a powermac 8600 with a powerlogix 750GX card in it.
kevin
P.S.: This will show how much of a novice I am: What's a "git"?
/*
* cf750gx.c - cpufreq driver for the dual PLLs in the 750gx
* ($Revision: 1.0 $)
*
* Copyright (C) 2008 kevin Diggs
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/cpufreq.h>
#include <linux/compiler.h>
#include <linux/dmi.h>
#include "linux/of.h"
//#include <asm/io.h>
//#include <asm/msr.h>
#include <asm/processor.h>
//#include <asm/cpufeature.h>
#include <asm/delay.h>
//#include <asm/uaccess.h>
#include "asm/pll.h"
#include "asm/pll_if.h"
#include "cf750gx.h"
#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
"ppc-750gx-cpufreq", msg)
MODULE_AUTHOR("Kevin Diggs");
MODULE_DESCRIPTION("750GX Dual PLL cpufreq driver");
MODULE_LICENSE("GPL");
static const struct pll_750fgx __initdata pll_750fx ={
.min_ratio = 2,
.max_ratio = 20,
.min_core = 400000,
.max_core = 800000,
};
static const struct pll_750fgx __initdata pll_750gx = {
.min_ratio = 2,
.max_ratio = 20,
.min_core = 500000,
.max_core = 1000000,
};
static unsigned int override_min_core=0;
static unsigned int override_max_core=0;
static unsigned int override_bus_freq=0;
static unsigned int freq_steps=0;
static unsigned int cf750gxvBusSpeed=0;
static unsigned int cf750gxvMinCore=0;
static unsigned int cf750gxvMaxCore=0;
struct cpufreq_frequency_table *cf750gxvFreqTable;
static int cf750gxTarget(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int relation)
{
unsigned int next_state = 0; /* Index into freq_table */
unsigned int next_perf_state = 0; /* Index into perf table */
//unsigned int i;
int result = 0;
unsigned int pll,new_pll;
unsigned int active_pll;
struct cpufreq_freqs freqs;
dprintk("cf750gxTarget() %d (%d)\n", target_freq, policy->cpu);
result = cpufreq_frequency_table_target(policy,
cf750gxvFreqTable,
target_freq,
relation, &next_state);
if (unlikely(result))
return -ENODEV;
pll=get_PLL();
active_pll=get_active_PLL(pll);
#ifdef CONFIG_HOTPLUG_CPU
/* cpufreq holds the hotplug lock, so we are safe from here on */
// cpus_and(online_policy_cpus, cpu_online_map, policy->cpus);
#else
// online_policy_cpus = policy->cpus;
#endif
next_perf_state = cf750gxvFreqTable[next_state].index;
if (cf750gxiPackState(get_PLL_ratio(active_pll,pll), get_PLL_range(
active_pll,pll)) == next_perf_state) {
dprintk("Already at target state (P%d)\n",
next_perf_state);
return 0;
}
// cpus_clear(cmd.mask);
if(active_pll)
{
/*
* Since active PLL is 1, modify PLL 0
*/
next_perf_state=next_perf_state<<(PLL0_CFG_SHIFT-
PLL1_CFG_SHIFT);
new_pll=(PLL0_DO_CFG|PLL0_DO_RNG|PLL_DO_SEL)<<24;
}
else
{
/*
* Use PLL 1
*/
new_pll=((PLL1_DO_CFG|PLL1_DO_RNG|PLL_DO_SEL)<<24)|PLL_SEL_MASK;
}
new_pll=new_pll|next_perf_state;
dprintk(__FILE__"-%d: Modifying PLL: 0x%x\n",__LINE__,new_pll);
freqs.old=cfgToFreq(get_PLL_ratio(active_pll,pll),cf750gxvBusSpeed);
freqs.new=cf750gxvFreqTable[next_state].frequency;
freqs.cpu=0;
dprintk(__FILE__"-%d: freqs.old=%d, freqs.new=%d\n",__LINE__,freqs.
old,freqs.new);
cpufreq_notify_transition(&freqs,CPUFREQ_PRECHANGE);
result=modifyPLL(new_pll,0);
cpufreq_notify_transition(&freqs,CPUFREQ_POSTCHANGE);
return result;
}
static int cf750gxVerify(struct cpufreq_policy *policy)
{
dprintk("cf750gxVerify\n");
return cpufreq_frequency_table_verify(policy, cf750gxvFreqTable);
}
static int cf750gxCpuInit(struct cpufreq_policy *policy)
{
unsigned int i,pll,ratio;
unsigned int result = 0;
dprintk("cf750gxCpuInit\n");
pll=get_PLL();
ratio=get_PLL_ratio(get_active_PLL(pll),pll);
if(ratio>20) ratio=(ratio-10)<<1;
policy->cur=ratio*cf750gxvBusSpeed/2000;
// policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
policy->cpuinfo.transition_latency = getLatency();
result = cpufreq_frequency_table_cpuinfo(policy, cf750gxvFreqTable);
if (result)
goto err_freqfree;
cpufreq_frequency_table_get_attr(cf750gxvFreqTable, policy->cpu);
return result;
err_freqfree:
kfree(cf750gxvFreqTable);
return result;
}
static int cf750gxCpuExit(struct cpufreq_policy *policy)
{
dprintk("cf750gxCpuExit\n");
cpufreq_frequency_table_put_attr(policy->cpu);
return 0;
}
static int cf750gxResume(struct cpufreq_policy *policy)
{
return 0;
}
static struct freq_attr *cf750gxvAttr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};
static struct cpufreq_driver cf750gxvDrv = {
.verify = cf750gxVerify,
.target = cf750gxTarget,
.init = cf750gxCpuInit,
.exit = cf750gxCpuExit,
.resume = cf750gxResume,
.name = "ppc750gx-cpufreq",
.owner = THIS_MODULE,
.attr = cf750gxvAttr,
};
static int __init cf750gxInit(void)
{
int ret;
unsigned int freq,i,j,rng;
unsigned short min_ratio,max_ratio;
struct cpufreq_frequency_table *tbp;
const struct pll_750fgx *pll_defaults;
#ifdef CONFIG_PPC_OF
struct device_node *tree_root;
const u32 *clk;
#endif /* CONFIG_PPC_OF */
dprintk("cf750gxInit\n");
if ( !cpu_has_feature(CPU_FTR_DUAL_PLL_750FX))
return 0;
/*
* See if bus speed override was specified
*/
if(override_bus_freq) cf750gxvBusSpeed=override_bus_freq;
#ifdef CONFIG_PPC_OF
/*
* If bus speed not specified, try to get it via OF
*/
if(!cf750gxvBusSpeed)
{
/*
* Get root node (aka MacRISC bus)
*/
tree_root=of_find_node_by_name(NULL,"");
if(tree_root)
{
clk=of_get_property(tree_root,"clock-frequency",NULL);
if(clk && *clk)
cf750gxvBusSpeed=(unsigned int)*clk/1000;
of_node_put(tree_root);
dprintk(__FILE__"-%d: Bus speed from OF: %d KHz\n",
__LINE__,cf750gxvBusSpeed);
}
}
#endif /* CONFIG_PPC_OF */
/*
* Get processor min and max core frequencies. If they have not been
* overriden, then get them from version defaults.
*/
if((cur_cpu_spec->pvr_value>>16)==0x7000)
pll_defaults=&pll_750fx;
else
pll_defaults=&pll_750gx;
cf750gxvMinCore=override_min_core?override_min_core:pll_defaults->
min_core;
cf750gxvMaxCore=override_max_core?override_max_core:pll_defaults->
max_core;
dprintk(__FILE__"-%d: cf750gxvMinCore is %u, cf750gxvMaxCore is %u\n",
__LINE__,cf750gxvMinCore,cf750gxvMaxCore);
dprintk(__FILE__"-%d: pll_defaults: min_ratio %d, max_ratio %d\n",
__LINE__,pll_defaults->min_ratio,pll_defaults->max_ratio);
if(!cf750gxvMinCore)
{
dprintk("Can't determine minimum core frequency\n");
ret=-EINVAL;
goto ErrSimple;
}
if(!cf750gxvMaxCore)
{
dprintk("Can't determine maximum core frequency\n");
ret=-EINVAL;
goto ErrSimple;
}
if(!cf750gxvBusSpeed)
{
dprintk("Can't determine system bus speed\n");
ret=-EINVAL;
goto ErrSimple;
}
/*
* Build maximum freq table. This will depend on the bus freq, the core
* frequency limits, and the ratios.
*/
min_ratio=pll_defaults->min_ratio;
freq=min_ratio*cf750gxvBusSpeed;
if(freq<cf750gxvMinCore)
{
/*
* Core min is above min ratio and bus speed clock. Find min
* ratio such that min ratio * bus speed >= core min.
*/
min_ratio=cf750gxvMinCore/cf750gxvBusSpeed;
j=cf750gxvMinCore%cf750gxvBusSpeed;
if(j) min_ratio++;
}
else
{
/*
* Core min is below min ratio and speed clock. Reset core min.
*/
cf750gxvMinCore=freq;
}
max_ratio=pll_defaults->max_ratio;
freq=max_ratio*cf750gxvBusSpeed;
if(freq>cf750gxvMaxCore)
{
/*
* Core max is below max ratio and bus speed. Find max ratio
* such that max ratio * bus speed <= core max.
*/
max_ratio=cf750gxvMaxCore/cf750gxvBusSpeed;
}
else
{
/*
* Core max is above max ratio and bus speed clock. Reset core
* max.
*/
cf750gxvMaxCore=freq;
}
dprintk(__FILE__"-%d: min_ratio is %d, max_ratio is %d\n",__LINE__,
min_ratio,max_ratio);
/*
* Bus ratios for the GX range from 2-20 for 19 INTEGER frequencies.
* The above checks may have changed this. There are max_ratio -
* min_ratio + 1 frequencies.
*/
j=max_ratio-min_ratio+1;
cf750gxvFreqTable=kmalloc(sizeof(struct cpufreq_frequency_table)*(j+2),
GFP_KERNEL);
if(cf750gxvFreqTable==NULL)
{
ret = -ENOMEM;
goto ErrSimple;
}
dprintk(__FILE__"-%d: cf750gxvFreqTable=%p\n",__LINE__,
cf750gxvFreqTable);
/*
* Use index of first entry to keep track of the count (one extra
* entry)
*/
cf750gxvFreqTable[0].frequency=CPUFREQ_ENTRY_INVALID;
cf750gxvFreqTable[0].index=j;
/*
* Populate the table
*/
// for(tbp=cf750gxvFreqTable+1,i=min_ratio; i<=max_ratio; tbp++,i++)
for(tbp=cf750gxvFreqTable,i=min_ratio; i<=max_ratio; tbp++,i++)
{
tbp->frequency=i*cf750gxvBusSpeed;
if(tbp->frequency<600) rng=2;
else if(tbp->frequency<900) rng=0;
else rng=1;
/*
* The computation in the first argument converts the bus
* ratio value to the PLL configuration value needed for the
* given ratio
*/
tbp->index=cf750gxiPackState(i>10?i+10:(i<<1),rng);
}
/*
* The other extra array member
*/
tbp->frequency=CPUFREQ_TABLE_END;
ret=cpufreq_register_driver(&cf750gxvDrv);
dprintk(__FILE__"-%d: return from cpufreq_register_driver() is %d\n",
__LINE__,ret);
if(ret) goto ErrFreqTable;
ErrSimple:
return ret;
ErrFreqTable:
kfree(cf750gxvFreqTable);
return ret;
}
static void __exit cf750gxExit(void)
{
dprintk("cf750gxExit\n");
cpufreq_unregister_driver(&cf750gxvDrv);
if(cf750gxvFreqTable)
kfree(cf750gxvFreqTable);
cf750gxvFreqTable=NULL;
return;
}
module_param(override_max_core, uint, 0644);
MODULE_PARM_DESC(override_max_core,
"clock frequency in KHz.");
module_param(override_min_core, uint, 0644);
MODULE_PARM_DESC(override_min_core,
"clock frequency in KHz.");
module_param(override_bus_freq, uint, 0644);
MODULE_PARM_DESC(override_bus_freq,
"bus frequency in KHz used to compute clock frequency using
multipliers.");
module_param(freq_steps, uint, 0644);
MODULE_PARM_DESC(freq_steps,
"specify number of frequency steps to use between min and max.");
late_initcall(cf750gxInit);
module_exit(cf750gxExit);
MODULE_ALIAS("dual-pll");
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev