* 12.5 MHz woo hoo!
@ 2008-08-15 21:36 Kevin Diggs
0 siblings, 0 replies; only message in thread
From: Kevin Diggs @ 2008-08-15 21:36 UTC (permalink / raw)
To: linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 1206 bytes --]
[root@PowerMac8600B root]# cat /proc/cpuinfo
processor : 0
cpu : 750GX
temperature : 1-76 C (uncalibrated)
clock : 200.000000MHz
revision : 2.3 (pvr 0008 0203)
bogomips : 24.96
timebase : 12500000 <-- 12.5 MHz exactly!!!
platform : PowerMac
model : Power Macintosh
machine : Power Macintosh
motherboard : AAPL,8500 MacRISC
detected as : 16 (PowerMac 8500/8600)
pmac flags : 00000000
pmac-generation : OldWorld
Hey, anybody know if that temperature, thermal thingy works well enough
to bother fooling with the code to produce a more valid value?
also:
[root@PowerMac8600B root]# alias tis
alias tis='cat /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state'
[root@PowerMac8600B root]# tis
250000 178419
300000 0
350000 0
400000 0
450000 0
500000 0
550000 0
600000 0
650000 0
700000 0
750000 0
800000 0
850000 0
900000 0
950000 0
1000000 0
[root@PowerMac8600B root]# uname -vr
2.6.26-pll #4 Thu Aug 14 04:02:58 PDT 2008
Finally, can someone tell me if the attached file shows up ok if it were
a patch I wanted to submit? I can't seem to figure out how to 'import
inline' using this ancient mailer.
kevin
[-- Attachment #2: pll_if.c --]
[-- Type: text/plain, Size: 20033 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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define DEBUG
#include "linux/init.h"
#include "linux/module.h"
#include <linux/autoconf.h>
#include "linux/kernel.h"
#include <linux/errno.h>
#include <linux/cpu.h>
#include "linux/of.h"
#include "linux/notifier.h"
#include "linux/delay.h"
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_SYSFS
#include "linux/sysdev.h"
#endif
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
#include "linux/hrtimer.h"
#endif
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include "asm/time.h"
#include <asm/mmu.h>
#include <asm/processor.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/cputable.h>
#include <asm/system.h>
#include <asm/pll_if.h>
#include <asm/pll.h>
#include <asm/smp.h>
MODULE_LICENSE("GPL");
static unsigned int boot_ratio;
static unsigned int pllifvBusClock;
static unsigned int override_bus_clock = 0;
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
static enum hrtimer_restart pllTimerF(struct hrtimer *hrt);
static struct hrtimer pll_timer;
static unsigned long hrtimers_got_no_freakin_callback_data;
#ifdef DEBUG
cycles_t pll_time_stamp;
#endif
#else
static void pllTimerF(unsigned long newPLL);
static struct timer_list pll_timer;
cycles_t pll_time_stamp;
#endif
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_SYSFS
extern unsigned long loops_per_jiffy;
unsigned long boot_loops;
static struct sys_device *sysdev_cpu;
static ssize_t show_ppc750gxpll(struct sys_device *dev, char *buf)
{
return sprintf(buf, "%x\n", get_PLL());
}
int modifyPLL(unsigned int pll, int scaleLPJ);
//static ssize_t __attribute_used__ store_ppc750gxpll(struct sys_device *dev,
// const char *buf, size_t count)
static ssize_t __used store_ppc750gxpll(struct sys_device *dev,
const char *buf, size_t count)
{
unsigned int pll;
char *ptr;
pr_debug(__FILE__">%s()-%d: buf=%s\n", __func__, __LINE__, buf);
pll = simple_strtoul(buf, &ptr, 16);
pr_debug(__FILE__">%s()-%d: %x (%d)\n", __func__, __LINE__, pll, pll);
/* modifyPLL(pll,!0); */
modifyPLL(pll, 0);
return ptr-buf;
}
static SYSDEV_ATTR(ppc750gxpll, 0600, show_ppc750gxpll, store_ppc750gxpll);
#endif
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_CPU_FREQ
struct plliftCallData {
void *data;
int scalar;
};
static struct plliftCallData pllifvSwitchCallData;
static struct plliftCallData pllifvLockCallData;
static RAW_NOTIFIER_HEAD(pllifvPllSwitchChain);
static RAW_NOTIFIER_HEAD(pllifvPllLockChain);
#endif
/*
* This initializes the code for the PLL control:
* boot_ratio is used to scale the loops_per_jiffy value from its boot value
* boot_loops is the boot value of loops_per_jiffy and is used to compute new
* values
*/
static int __init init_PLL(void)
{
unsigned int temp;
#ifdef CONFIG_PPC_OF
const u32 *clk;
struct device_node *tree_root;
#endif
if (!cpu_has_feature(CPU_FTR_DUAL_PLL_750FX))
return -ENODEV;
boot_ratio = 0;
/*
* See if bus clock override was specified
*/
if (override_bus_clock)
pllifvBusClock = override_bus_clock*1000;
#ifdef CONFIG_PPC_OF
/*
* If bus clock is not specified, try to get it via OF
*/
if (!pllifvBusClock) {
/*
* 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)
pllifvBusClock = (unsigned int) *clk;
of_node_put(tree_root);
pr_debug(__FILE__">%s()-%d: Bus clock from OF is %u\n",
__func__, __LINE__, pllifvBusClock);
}
}
#endif /* CONFIG_PPC_OF */
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_SYSFS
temp = get_PLL();
temp = get_PLL_ratio(get_active_PLL(temp), temp);
/*
* Units for boot ratio is halves, i.e. 20 is a ratio of 10.
* From 21 on the returned value needs to be converted to halves.
*/
if (temp > 20)
temp = (temp-10)<<1;
boot_ratio = temp;
boot_loops = loops_per_jiffy;
/*
* Try to get the cpu sysdev
*/
sysdev_cpu = get_cpu_sysdev(boot_cpuid);
if (sysdev_cpu != NULL)
sysdev_create_file(sysdev_cpu, &attr_ppc750gxpll);
#endif
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
hrtimer_init(&pll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
#else
init_timer(&pll_timer);
#endif
pll_timer.function = pllTimerF;
return 0;
}
/*__initcall(init_PLL); */
static void exit_PLL(void)
{
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_SYSFS
if (sysdev_cpu != NULL)
sysdev_remove_file(sysdev_cpu, &attr_ppc750gxpll);
#endif
/*
* Make sure there are no timers pending by making sure we are not
* doing anything
*/
while (test_bit(PLL_LOCK_BIT, (unsigned long *)&boot_ratio))
msleep(1);
}
module_init(init_PLL);
module_exit(exit_PLL);
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_SYSFS
static unsigned long pllNewLPJ(unsigned int oldRatio, unsigned int newRatio,
unsigned long LPJ)
{
if (LPJ > 200000000)
return LPJ/oldRatio*newRatio;
else
return LPJ*newRatio/oldRatio;
}
static inline void pllUpdateLPJ(unsigned int oldRatio, unsigned int newRatio,
unsigned long LPJ)
{
loops_per_jiffy = pllNewLPJ(oldRatio, newRatio, LPJ);
}
#else
#define pllUpdateLPJ(a, b, c)
#endif
static void pllifiSwitchPLLs(unsigned int newPLL)
{
#if 0
unsigned long flags;
#endif
unsigned int new_ratio, new_ratio_cp, old_ratio, current_pll, masked_boot_ratio;
pr_debug(__FILE__">%s()-%d: newPLL=0x%08x\n", __func__, __LINE__, newPLL);
/*
* Compute new loops_per_jiffy
*/
current_pll = get_PLL();
new_ratio = get_PLL_ratio(get_next_PLL(newPLL), current_pll);
old_ratio = get_PLL_ratio(get_active_PLL(current_pll), current_pll);
masked_boot_ratio = boot_ratio&0xff;
new_ratio_cp = new_ratio;
pr_debug(__FILE__">%s()-%d: current_pll=0x%08x, new=%d, old=%d\n",
__func__, __LINE__, current_pll, new_ratio, old_ratio);
current_pll = (current_pll&~PLL_SEL_MASK)|(newPLL&PLL_SEL_MASK);
pr_debug(__FILE__">%s()-%d: current_pll=0x%08x, new=%d, old=%d\n",
__func__, __LINE__, current_pll, new_ratio, old_ratio);
/*
* Convert to halves
*/
if (new_ratio > 20)
new_ratio = (new_ratio-10)<<1;
if (old_ratio > 20)
old_ratio = (old_ratio-10)<<1;
/*
* Make sure that we never shorten the sleep values
*/
if (new_ratio > old_ratio) {
if (newPLL&PLL_DO_LPJ)
pllUpdateLPJ(masked_boot_ratio, new_ratio, boot_loops);
pr_debug(__FILE__">%s()-%d: masked_boot_ratio=%d, new_ratio="
"%d, boot_loops=%ld, loops_per_jiffy=%ld\n", __func__, __LINE__,
masked_boot_ratio, new_ratio, boot_loops, loops_per_jiffy);
set_PLL(current_pll);
} else {
pr_debug(__FILE__">%s()-%d: masked_boot_ratio=%d, new_"
"ratio=%d, boot_loops=%ld, loops_per_jiffy=%ld\n",
__func__, __LINE__, masked_boot_ratio, new_ratio,
boot_loops, loops_per_jiffy);
set_PLL(current_pll);
if (newPLL&PLL_DO_LPJ)
pllUpdateLPJ(masked_boot_ratio, new_ratio, boot_loops);
pr_debug(__FILE__">%s()-%d: masked_boot_ratio=%d, new_"
"ratio=%d, boot_loops=%ld, loops_per_jiffy=%ld\n",
__func__, __LINE__, masked_boot_ratio, new_ratio,
boot_loops, loops_per_jiffy);
}
raw_notifier_call_chain(&pllifvPllSwitchChain, pllifmPllSwitch,
pllifvSwitchCallData.data);
/*
* This is used to print the clock frequency in /proc/cpuinfo
*/
ppc_proc_freq = pllifCfgToFreq(new_ratio_cp);
pr_debug(__FILE__">%s()-%d: pllifCfgToFreq(%u)=%lu\n", __func__,
__LINE__, new_ratio_cp, ppc_proc_freq);
#if 0
save_flags(flags);
cli();
loops_per_jiffy = pllNewLPJ(masked_boot_ratio, new_ratio, boot_loops);
set_PLL(current_pll);
restore_flags(flags);
#endif
}
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
static enum hrtimer_restart pllTimerF(struct hrtimer *hrt)
{
#ifdef DEBUG
cycles_t now;
cycles_t usec, tmp, cntlz, cnttz;
now = get_cycles();
now = now-pll_time_stamp;
/*
* Aw cmon, I'm just havin' a little fun with PPC assembly.
* Just wish I could find a way to use an rlwinm ...
*/
cnttz = tb_ticks_per_sec; /* needed to get the assembly to ...
* look right ... ??? */
tmp = now*15625;
__asm__ __volatile__ (
"addi %0,%3,-1\n"
"andc %1,%3,%0\n"
"cntlzw %1,%1\n"
"subfic %1,%1,31\n"
"cntlzw %0,%2\n":
"=r"(cntlz), "=r"(cnttz):
"r"(tmp), "b"(cnttz)
);
/*
* 1,000,000 usec per sec and 1,000,000 is 15625<<6
*/
usec = ((tmp<<cntlz)/(tb_ticks_per_sec>>cnttz))<<6;
usec = (usec+(1UL<<(cntlz+cnttz-1)))>>(cntlz+cnttz);
pr_debug(__FILE__">%s()-%d: Time delta is %lu cycles, "
"%lu uS (cntlz=%lu, cnttz=%lu)\n", __func__, __LINE__, now,
usec, cntlz, cnttz);
#endif
raw_notifier_call_chain(&pllifvPllLockChain, pllifmPllLock,
pllifvLockCallData.data);
/*
* Clear all lock bits
*/
boot_ratio &= ~(PLL_TIMER|PLL0_LOCK|PLL1_LOCK);
if ((unsigned int) hrtimers_got_no_freakin_callback_data)
pllifiSwitchPLLs((unsigned int)
hrtimers_got_no_freakin_callback_data);
return HRTIMER_NORESTART;
}
#else
static void pllTimerF(unsigned long newPLL)
{
cycles_t now;
now = get_cycles();
now = now-pll_time_stamp;
#ifdef DEBUG
{
cycles_t usec, tmp, cntlz, cnttz;
/*
* Aw cmon, I'm just havin' a little fun with PPC assembly.
* Just wish I could find a way to use an rlwinm ...
*/
cnttz = tb_ticks_per_sec; /* needed to get the assembly to ...
* look right ... ??? */
#define MULFIRST
#ifdef MULFIRST
tmp = now*15625;
#else
tmp = now;
#endif
__asm__ __volatile__ (
"addi %0,%3,-1\n"
"andc %1,%3,%0\n"
"cntlzw %1,%1\n"
"subfic %1,%1,31\n"
"cntlzw %0,%2\n":
"=r"(cntlz), "=r"(cnttz):
"r"(tmp), "b"(cnttz)
);
/*
* 1,000,000 usec per sec and 1,000,000 is 15625<<6
*/
// usec = (((now<<cntlz)/(tb_ticks_per_sec>>cnttz)*15625)+(1UL<<
// (cntlz+cnttz-1-6)))>>(cntlz+cnttz-6);
#ifdef MULFIRST
usec = ((tmp<<cntlz)/(tb_ticks_per_sec>>cnttz))<<6;
usec = (usec+(1UL<<(cntlz+cnttz-1)))>>(cntlz+cnttz);
#else
usec = ((tmp<<cntlz)/(tb_ticks_per_sec>>cnttz)*15625)<<6;
usec = (usec+(1UL<<(cntlz+cnttz-1)))>>(cntlz+cnttz);
#endif
pr_debug(__FILE__">%s()-%d: Time delta is %lu cycles, "
"%lu uS (cntlz=%lu, cnttz=%lu)\n", __func__, __LINE__,
now, usec, cntlz, cnttz);
}
#endif
/*
* Make sure it has been at least 100 usec. 100 usec is 100 *
* tb_ticks_per_sec / 1,000,000 cycles, so:
* if(now<100*tb_ticks_per_sec/1000000
* 1,000,000 is 15625<<6, so:
* if((now<<6)<100*tb_ticks_per_sec/15625)
* 100 is 25<<2, so:
* if((now<<4)<25*tb_ticks_per_sec/15625)
* 15625 is 3125*5, so:
* if((now<<4)*5<25*tb_ticks_per_sec/3125)
* obviously 25/3125 -> 1/125:
* if((now<<4)*5<tb_ticks_per_sec/125)
*/
if ((now<<4)*5 < tb_ticks_per_sec/125)
udelay(100-now*1000000/tb_ticks_per_sec);
raw_notifier_call_chain(&pllifvPllLockChain, pllifmPllLock,
pllifvLockCallData.data);
/*
* Clear all lock bits
*/
boot_ratio &= ~(PLL_TIMER|PLL0_LOCK|PLL1_LOCK);
if ((unsigned int)newPLL)
pllifiSwitchPLLs((unsigned int)newPLL);
}
#endif
/*
* Handle accesses to the pll register. Examples for write:
* value CFGx/RNGx/res effect
* 0x08010000 switch to PLL1
* 0x08000000 switch to PLL0
* 0xc000fa00 1111 1/01/0 PLL0 off (CFG/RNG 31/1)
* 0xc000f200 1111 0/01/0 PLL0 to 20x (CFG/RNG 30/1)
* 0x30000004 0000 0/10/0 PLL1 off (CFG/RNG 0/2)
* 0x30000054 0101 0/10/0 PLL1 to 5x (CFG/RNG a/2)
*/
/**
* modifyPLL: - Takes steps to modify PLL as requested
* @pll: Specifies the new value and desired operation to be performed
* @scaleLPJ: flag to indicate whether to scale the loops_per_jiffy value
*
* Based on the value passed in the pll argument, this takes the steps necessary
* to change the PLL as requested. The upper 7 or 8 bits of the PLL are read
* only. These bit positions in the pll argument are used to specify flags that
* indicate the validity of the other fields in the pll argument. See the
* pll_if.h header for detail and actual values.
*/
int modifyPLL(unsigned int pll, int scaleLPJ)
{
unsigned int current_pll, work_mask, pll_x;
int rval = 0;
pr_debug(__FILE__">%s()-%d:\n", __func__, __LINE__);
pr_debug(__FILE__">%s()-%d: pll=0x%08x\n", __func__, __LINE__, pll);
/*
* This is not reentrant
*/
if (test_and_set_bit(PLL_LOCK_BIT, (unsigned long *)&boot_ratio)) {
pr_debug(__FILE__">%s()-%d: Busy!\n", __func__, __LINE__);
return -EAGAIN;
}
/*
* Don't allow any changes if a timer is pending
*/
if (test_bit(PLL_TIMER_BIT, (unsigned long *)&boot_ratio))
goto checkPLLBusy;
current_pll = get_PLL();
work_mask = pll>>24;
/*
* Check to see if the currently selected PLL is being modified
*/
pll_x = get_active_PLL(current_pll);
if ((pll_x == 0 && work_mask&(PLL0_DO_CFG|PLL0_DO_RNG|PLL0_DO_CONTROL))
|| (pll_x == 1 && work_mask&(PLL1_DO_CFG|PLL1_DO_RNG)))
goto checkPLLInVal;
/*
* Can't change to a PLL that is off. Also can't immediately change to
* one that is not locked. Catch that supposedly impossible condition.
*/
if (work_mask&PLL_DO_SEL) {
int next_ratio;
unsigned int which_config;
pll_x = get_next_PLL(pll);
/*
* Figure out where the next ratio comes from. It will be from
* pll if we are changing the next pll and current_pll if not.
*/
which_config = pll_x?((work_mask&PLL1_DO_CFG)?pll:current_pll):
((work_mask&PLL0_DO_CFG)?pll:current_pll);
next_ratio = get_PLL_ratio(pll_x, which_config);
if (next_ratio < 4 || next_ratio > 30)
goto checkPLLInVal;
pll_x = ((pll_x == 0 && boot_ratio&PLL0_LOCK) || (pll_x == 1 &&
boot_ratio&PLL1_LOCK))?1:0;
}
/*
* To avoid complications, don't allow both plls to be half ratios
*/
if (work_mask&PLL0_DO_CFG) {
int old_ratio1, new_ratio0;
old_ratio1 = get_PLL_ratio(1, current_pll);
new_ratio0 = get_PLL_ratio(0, pll);
if (old_ratio1 > 4 && old_ratio1 < 20 && new_ratio0 > 4 &&
new_ratio0 < 20 && (old_ratio1&0x1) & (new_ratio0&0x1))
goto checkPLLInVal;
} else if (work_mask&PLL1_DO_CFG) {
int old_ratio0, new_ratio1;
old_ratio0 = get_PLL_ratio(0, current_pll);
new_ratio1 = get_PLL_ratio(1, pll);
if (old_ratio0 > 4 && old_ratio0 < 20 && new_ratio1 > 4 &&
new_ratio1 < 20 && (old_ratio0&0x1) & (new_ratio1&0x1))
goto checkPLLInVal;
}
/*
* Determine if we will need to schedule a timer for a PLL relock. If
* any PLL config is being changed then a timer will be needed. Also
* need one if changing to a PLL that is not locked, though that should
* not happen.
*/
if ((work_mask&(PLL0_DO_CFG|PLL0_DO_RNG|PLL1_DO_CFG|PLL1_DO_RNG|
PLL0_DO_CONTROL)) || (work_mask&PLL_DO_SEL && pll_x)) {
unsigned int pll_mask, temp;
pll_mask = 0;
if (work_mask&PLL0_DO_CFG) {
pll_mask |= PLL0_CFG_MASK;
/*
* Flag that PLL0 needs to relock
*/
boot_ratio |= PLL0_LOCK;
}
if (work_mask&PLL0_DO_RNG)
pll_mask |= PLL0_RNG_MASK;
if (work_mask&PLL1_DO_CFG) {
pll_mask |= PLL1_CFG_MASK;
/*
* Flag that PLL1 needs to relock
*/
boot_ratio |= PLL1_LOCK;
}
if (work_mask&PLL1_DO_RNG)
pll_mask |= PLL1_RNG_MASK;
temp = (current_pll&~pll_mask)|(pll&pll_mask);
if (pll_mask)
set_PLL(temp);
/*
* Flag that a timer is pending
*/
boot_ratio |= PLL_TIMER;
/*
* Schedule a timer to clear the PLL lock bits (and signal that
* it is ok to select the PLL)
*/
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_HRTIMER
/*
* Oh please, someone tell me I'm just to stupid to know how
* to pass this to the timer function!
*/
hrtimers_got_no_freakin_callback_data = (work_mask&PLL_DO_SEL)?
(PLL_DO_SEL<<24)|(scaleLPJ?PLL_DO_LPJ:0)|pll&
PLL_SEL_MASK:0;
pll_timer.expires = ktime_set(0, 100000);
hrtimer_start(&pll_timer, pll_timer.expires, HRTIMER_MODE_REL);
#ifdef DEBUG
pll_time_stamp = get_cycles();
#endif
#else
/*
* We might want to pass three pieces of data to the timer
* i) that we want to switch PLLs (PLL_DO_SEL)
* ii) which PLL to switch to (PLL_SEL_MASK)
* iii) flag to control whether loops_per_jiffy is updated
* (PLL_DO_LPJ)
*/
pll_timer.data = (work_mask&PLL_DO_SEL)?(PLL_DO_SEL<<24)|(
scaleLPJ?PLL_DO_LPJ:0)|(pll&PLL_SEL_MASK):0;
/*
* Relock takes 100 us. See how many jiffies will take care of
* it.
*/
pll_timer.expires = (100*HZ/1000000);
if (pll_timer.expires == 0)
pll_timer.expires = 1;
pll_timer.expires = jiffies+pll_timer.expires;
add_timer(&pll_timer);
pll_time_stamp = get_cycles();
#endif
} else if (work_mask&PLL_DO_SEL)
pllifiSwitchPLLs(pll|(scaleLPJ?PLL_DO_LPJ:0));
checkPLLOut:
clear_bit(PLL_LOCK_BIT, (unsigned long *)&boot_ratio);
return rval;
checkPLLBusy:
rval = -EBUSY;
goto checkPLLOut;
checkPLLInVal:
rval = -EINVAL;
goto checkPLLOut;
}
EXPORT_SYMBOL(modifyPLL);
/**
* pllifCFgToFreq: - Takes a ratio and returns the frequency
* @cfg: The PLL ratio field value
*
* This takes a PLL ratio field value and uses it along with the bus frequency
* to compute the processor frequency.
*/
unsigned int pllifCfgToFreq(unsigned int cfg)
{
return (cfg < 21?cfg>>1:cfg-10)*pllifvBusClock;
}
EXPORT_SYMBOL(pllifCfgToFreq);
#ifdef CONFIG_PPC_750GX_DUAL_PLL_IF_CPU_FREQ
/**
* pllifGetBusClock: - Returns the bus frequency
*
* This returns the determined bus frequency in Hz.
*/
unsigned int pllifGetBusClock()
{
return pllifvBusClock;
}
EXPORT_SYMBOL(pllifGetBusClock);
/**
* pllifRegisterPllSwitchCB: - Registers a pll switch call back
* @nb: structure describing the call back to register
*
* This registers a call back function that will be called when the clock is
* switched from one PLL to the other.
*/
int pllifRegisterPllSwitchCB(struct notifier_block *nb)
{
int ret;
pllifvSwitchCallData.data = (void *)nb->next;
nb->next = NULL;
pllifvSwitchCallData.scalar = nb->priority;
nb->priority = 0;
ret = raw_notifier_chain_register(&pllifvPllSwitchChain, nb);
return ret;
}
EXPORT_SYMBOL(pllifRegisterPllSwitchCB);
/**
* pllifUnregisterPllSwitchCB: - Cancels a previously registered call back
* @nb: structure describing the call back to cancel
*
* This cancels a previously registered switch call back
*/
void pllifUnregisterPllSwitchCB(struct notifier_block *nb)
{
raw_notifier_chain_unregister(&pllifvPllSwitchChain, nb);
}
EXPORT_SYMBOL(pllifUnregisterPllSwitchCB);
/**
* pllifRegisterPllLockCB: - Registers a pll lock call back
* @nb: structure describing the call back to register
*
* This registers a call back function that will be called when a PLL has
* locked to a new frequency.
*/
int pllifRegisterPllLockCB(struct notifier_block *nb)
{
int ret;
pllifvLockCallData.data = (void *)nb->next;
nb->next = NULL;
pllifvLockCallData.scalar = nb->priority;
nb->priority = 0;
ret = raw_notifier_chain_register(&pllifvPllLockChain, nb);
return ret;
}
EXPORT_SYMBOL(pllifRegisterPllLockCB);
/**
* pllifUnregisterPllLockCB: - Cancels a previously registered call back
* @nb: structure describing the call back to cancel
*
* This cancels a previously registered PLL lock call back
*/
void pllifUnregisterPllLockCB(struct notifier_block *nb)
{
raw_notifier_chain_unregister(&pllifvPllLockChain, nb);
}
EXPORT_SYMBOL(pllifUnregisterPllLockCB);
#endif
module_param(override_bus_clock, uint, 0644);
MODULE_PARM_DESC(override_bus_clock,
"Bus clock frequency in KHz used to compute core clock frequency from"
" bus ratios.");
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2008-08-15 21:41 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-08-15 21:36 12.5 MHz woo hoo! 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.