[EMAIL PROTECTED] 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:

[EMAIL PROTECTED] root]# alias tis
alias tis='cat /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state'
[EMAIL PROTECTED] 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

[EMAIL PROTECTED] 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
/*
 * 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.");
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-dev

Reply via email to