All of lore.kernel.org
 help / color / mirror / Atom feed
* 750GX cpu freq support
@ 2008-06-03 22:41 Kevin Diggs
  0 siblings, 0 replies; only message in thread
From: Kevin Diggs @ 2008-06-03 22:41 UTC (permalink / raw)
  To: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 1509 bytes --]

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"?

[-- Attachment #2: cf750gx-new_c --]
[-- Type: text/plain, Size: 10819 bytes --]

/*
 * 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");

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2008-06-03 22:42 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-03 22:41 750GX cpu freq support Kevin Diggs

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.