* preliminary 750GX PLL control patch
@ 2005-07-26 17:28 Kevin Diggs
2005-07-30 16:18 ` Benjamin Herrenschmidt
0 siblings, 1 reply; 2+ messages in thread
From: Kevin Diggs @ 2005-07-26 17:28 UTC (permalink / raw)
To: linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 987 bytes --]
Hi,
I've attached a patch that adds preliminary access to the HID1
register in the 750GX (aka the PLL config register). The patch is
against 2.4.30. It has been lightly tested on a PowerMac 8600 with a
PowerLogix PowerForce 750GX cpu card.
Depending on the values for the pllx_cfg field, this should
work on the 750FX as well ... except for the stuff in idle_6xx.S. Since
there are plenty of people on this list way more smarterer than me I was
hoping someone could answer a question about the powersave modes that
some of the PowerPC processors have:
Since, as I understand it, the clock is shut off to much of the
chip when a powersave mode is engaged does it really buy you that much
to switch to a lower clock speed? Wouldn't you be better off shutting
off the unused PLL?
Also, please note that I am not very experienced creating
patches so please use a backup of your source tree.
kevin
[-- Attachment #2: pllpatch.u --]
[-- Type: text/plain, Size: 31193 bytes --]
diff -Naur linux-2.4.30.orig/Documentation/Configure.help linux-2.4.30/Documentation/Configure.help
--- linux-2.4.30.orig/Documentation/Configure.help Fri Apr 8 15:47:19 2005
+++ linux-2.4.30/Documentation/Configure.help Wed Jun 22 18:00:35 2005
@@ -24304,6 +24304,11 @@
a lot, or the TAU hardware is broken (likely on some G4's). If the range
is small (around 4 degrees), the temperature is relatively stable.
+Dual PLL support
+CONFIG_DUAL_PLL
+ This option enables sysctl support for checking and modifying the PLLs in
+ the IBM 750FX and 750GX processors.
+
Power management support for PowerBooks
CONFIG_PMAC_PBOOK
This provides support for putting a PowerBook to sleep; it also
diff -Naur linux-2.4.30.orig/arch/ppc/config.in linux-2.4.30/arch/ppc/config.in
--- linux-2.4.30.orig/arch/ppc/config.in Thu Apr 7 22:25:11 2005
+++ linux-2.4.30/arch/ppc/config.in Tue Jun 21 18:02:56 2005
@@ -175,10 +175,12 @@
bool ' Average high and low temp' CONFIG_TAU_AVERAGE
fi
define_bool CONFIG_PPC_ISATIMER y
+
+ bool 'Dual PLL Support (750FX/GX)' CONFIG_DUAL_PLL
fi
-if [ "$CONFIG_POWER4" = "y" ]; then
- bool 'VMX (same as AltiVec) support' CONFIG_ALTIVEC
-fi
+#if [ "$CONFIG_POWER4" = "y" ]; then
+# bool 'VMX (same as AltiVec) support' CONFIG_ALTIVEC
+#fi
if [ "$CONFIG_4xx" = "y" -o "$CONFIG_8xx" = "y" ]; then
bool 'Math emulation' CONFIG_MATH_EMULATION
diff -Naur linux-2.4.30.orig/arch/ppc/kernel/Makefile linux-2.4.30/arch/ppc/kernel/Makefile
--- linux-2.4.30.orig/arch/ppc/kernel/Makefile Thu Apr 7 22:24:19 2005
+++ linux-2.4.30/arch/ppc/kernel/Makefile Tue Jun 21 18:10:03 2005
@@ -50,6 +50,7 @@
obj-$(CONFIG_PPCBUG_NVRAM) += prep_nvram.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_TAU) += temp.o
+obj-$(CONFIG_DUAL_PLL) += pll.o pll_init.o
ifeq ($(CONFIG_SERIAL)$(CONFIG_GEN550),yy)
obj-$(CONFIG_KGDB) += gen550_kgdb.o gen550_dbg.o
obj-$(CONFIG_SERIAL_TEXT_DEBUG) += gen550_dbg.o
@@ -97,6 +98,8 @@
entry.o: entry.S ppc_defs.h
misc.o: misc.S ppc_defs.h
l2cr.o: l2cr.S ppc_defs.h
+pll.o: pll.S ppc_defs.h
+pll_init.o: pll_init.c ppc_defs.h
head.o: head.S ppc_defs.h
head_4xx.o: head_4xx.S ppc_defs.h
head_44x.o: head_44x.S ppc_defs.h
diff -Naur linux-2.4.30.orig/arch/ppc/kernel/cpu_setup_6xx.S linux-2.4.30/arch/ppc/kernel/cpu_setup_6xx.S
--- linux-2.4.30.orig/arch/ppc/kernel/cpu_setup_6xx.S Thu Apr 7 22:22:52 2005
+++ linux-2.4.30/arch/ppc/kernel/cpu_setup_6xx.S Sun May 15 12:16:39 2005
@@ -48,6 +48,13 @@
bl setup_750fx
mtlr r4
blr
+_GLOBAL(__setup_cpu_750gx)
+ mflr r4
+ bl setup_common_caches
+ bl setup_750_7400_hid0
+ bl setup_750gx
+ mtlr r4
+ blr
_GLOBAL(__setup_cpu_7400)
mflr r4
bl setup_7400_workarounds
@@ -200,6 +207,11 @@
/* 750fx specific
*/
setup_750fx:
+ blr
+
+/* 750gx specific
+ */
+setup_750gx:
blr
/* MPC 745x
diff -Naur linux-2.4.30.orig/arch/ppc/kernel/cputable.c linux-2.4.30/arch/ppc/kernel/cputable.c
--- linux-2.4.30.orig/arch/ppc/kernel/cputable.c Fri Apr 8 15:47:21 2005
+++ linux-2.4.30/arch/ppc/kernel/cputable.c Thu Jun 23 12:22:14 2005
@@ -24,6 +24,7 @@
extern void __setup_cpu_750(unsigned long offset, int cpu_nr, struct cpu_spec* spec);
extern void __setup_cpu_750cx(unsigned long offset, int cpu_nr, struct cpu_spec* spec);
extern void __setup_cpu_750fx(unsigned long offset, int cpu_nr, struct cpu_spec* spec);
+extern void __setup_cpu_750gx(unsigned long offset, int cpu_nr, struct cpu_spec* spec);
extern void __setup_cpu_7400(unsigned long offset, int cpu_nr, struct cpu_spec* spec);
extern void __setup_cpu_7410(unsigned long offset, int cpu_nr, struct cpu_spec* spec);
extern void __setup_cpu_745x(unsigned long offset, int cpu_nr, struct cpu_spec* spec);
@@ -60,6 +61,19 @@
#define CPU_FTR_COMMON 0
#endif
+#if 0
+const char *cpu_type_strings[] = {
+ "601","603","603e","603ev","604","604e","604r","604ev","740/750",
+ "745/755","750CX","750FX","750GX","7400","7400 (1.1)","7410","7450",
+ "7455","7457","7447A","82xx","8280","(generic PPC)","Power3 (630)",
+ "Power3 (630+)","I-star","S-star","Power4","PPC970","8xx","403GC",
+ "403GCX","403G ??","405GP","STB03xxx","440GP Rev. B","440GP Rev. C",
+ "440GX Rev. A","440GX Rev. B","440GX Rev. C"
+};
+
+#define CPU_TYPE_601 cpu_type_strings[0]
+#endif
+
struct cpu_spec cpu_specs[] = {
#if CLASSIC_PPC
{ /* 601 */
@@ -165,7 +179,7 @@
CPU_FTR_COMMON |
CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_CAN_NAP |
- CPU_FTR_750FX | CPU_FTR_NO_DPM,
+ CPU_FTR_750FX | CPU_FTR_NO_DPM | CPU_FTR_DUALPLL,
COMMON_PPC,
32, 32,
__setup_cpu_750
@@ -175,7 +189,8 @@
CPU_FTR_COMMON |
CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_CAN_NAP |
- CPU_FTR_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_NO_DPM,
+ CPU_FTR_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_NO_DPM |
+ CPU_FTR_DUALPLL,
COMMON_PPC,
32, 32,
__setup_cpu_750
@@ -185,10 +200,40 @@
CPU_FTR_COMMON |
CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_CAN_NAP |
- CPU_FTR_750FX | CPU_FTR_HAS_HIGH_BATS,
+ CPU_FTR_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_DUALPLL,
COMMON_PPC,
32, 32,
__setup_cpu_750fx
+ },
+ { /* 750GX (rev 1.x) */
+ 0xffffff00, 0x70020100, "750GX",
+ CPU_FTR_COMMON |
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
+ CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_CAN_NAP |
+ CPU_FTR_750GX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_DUALPLL,
+ COMMON_PPC,
+ 32, 32,
+ __setup_cpu_750gx
+ },
+ { /* 750GX (rev 2.3, as used on PowerLogix 750GX) */
+ 0xffffffff, 0x00080203, "750GX",
+ CPU_FTR_COMMON |
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
+ CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_CAN_NAP |
+ CPU_FTR_750GX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_DUALPLL,
+ COMMON_PPC,
+ 32, 32,
+ __setup_cpu_750gx
+ },
+ { /* 750GX (All revs >= 2.0) */
+ 0xffffff00, 0x70020200, "750GX",
+ CPU_FTR_COMMON |
+ CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
+ CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_CAN_NAP |
+ CPU_FTR_750GX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_DUALPLL,
+ COMMON_PPC,
+ 32, 32,
+ __setup_cpu_750gx
},
{ /* 740/750 (L2CR bit need fixup for 740) */
0xffff0000, 0x00080000, "740/750",
diff -Naur linux-2.4.30.orig/arch/ppc/kernel/pll.S linux-2.4.30/arch/ppc/kernel/pll.S
--- linux-2.4.30.orig/arch/ppc/kernel/pll.S Wed Dec 31 16:00:00 1969
+++ linux-2.4.30/arch/ppc/kernel/pll.S Wed Jun 22 18:15:51 2005
@@ -0,0 +1,182 @@
+/*
+ Dual PLL functions, for 750FX & 750GX
+ Copyright © 2005 by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ Tue, June 14, 2005.
+ - First public release, contributed by Kevin Diggs.
+ ***********
+ ***********
+
+ Author: Kevin Diggs ()
+ Please e-mail updates to this file to me, thanks!
+*/
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/cputable.h>
+#include <asm/ppc_asm.h>
+#include <asm/cache.h>
+
+/* Usage:
+
+ Examples:
+
+ _setPLL(0) - disables the cache
+ _setPLL(0xB3A04000) - enables my G3 upgrade card:
+ - L2E set to turn on the cache
+ - L2SIZ set to 1MB
+ - L2CLK set to 1:1
+ - L2RAM set to pipelined synchronous late-write
+ - L2I set to perform a global invalidation
+ - L2OH set to 0.5 nS
+ - L2DF set because this upgrade card
+ requires it
+
+ The layout of the PLL register (HID1) is:
+
+ 0 4|5 6|7|8| 9 11|12 13|14| 15 |16 20|21 22|24 28|29 30| 31
+ PCE |PRE|v|v| Res | Res |v | PS | PC0 | PR0 | PC1 | PR1 |Res
+ | | |
+ PSTAT1 -| | |
+ ECLK -----| |
+ PI0 --------------------|
+
+ PCE PLL0 read-only external config
+ PRE PLL0 read-only external range
+ PSTAT1 PLL status (0 -> PLL0, 1 -> PLL1)
+ ECLK 1 -> enable clkout pin
+ PI0 PLL0 control: 0 -> external
+ PS PLL select: 0 -> PLL0, 1 -> PLL1
+ PC0 PLL0 configuration
+ PR0 PLL0 range
+ PC1 PLL1 configuration
+ PR1 PLL1 range
+
+ PLL_CFG bus ratio PLL_CFG bus ratio
+ 00000 off 10000 8
+ 00001 off 10001 8.5
+ 00010 bypass 10010 9
+ 00011 bypass 10011 9.5
+ 00100 2 10100 10
+ 00101 2.5 10101 11
+ 00110 3 10110 12
+ 00111 3.5 10111 13
+ 01000 4 11000 14
+ 01001 4.5 11001 15
+ 01010 5 11010 16
+ 01011 5.5 11011 17
+ 01100 6 11100 18
+ 01101 6.5 11101 19
+ 01110 7 11110 20
+ 01111 7.5 11111 off
+
+ PLL_RNG range
+ 00 600 - 900
+ 01 900 - 1000
+ 10 500 - 600
+*/
+
+/*
+ * Return current PLL configuration
+ */
+_GLOBAL(_get_PLL)
+ /* Make sure this is a 750FX or 750GX chip */
+BEGIN_FTR_SECTION
+ li r3,-1
+ blr
+END_FTR_SECTION_IFCLR(CPU_FTR_DUALPLL)
+ mfspr r3,HID1 /* Get current PLL config */
+ blr
+
+/*
+ * Return active (selected) PLL from configuration (0 or 1)
+ * r3 contains configuration to dig selection out of
+ */
+_GLOBAL(_get_active_PLL)
+ /* Make sure this is a 750FX or 750GX chip */
+BEGIN_FTR_SECTION
+ li r3,-1
+ blr
+END_FTR_SECTION_IFCLR(CPU_FTR_DUALPLL)
+ rlwinm r3,r3,8,31,31 /* PSTAT1 to LSBit and mask */
+ blr
+
+/*
+ * Return next PLL from configuration (0 or 1)
+ * r3 contains configuration to dig next PLL out of (PS bit)
+ */
+_GLOBAL(_get_next_PLL)
+ /* Make sure this is a 750FX or 750GX chip */
+BEGIN_FTR_SECTION
+ li r3,-1
+ blr
+END_FTR_SECTION_IFCLR(CPU_FTR_DUALPLL)
+ rlwinm r3,r3,16,31,31 /* PS to LSBit and mask */
+ blr
+
+/*
+ * Return selected ratio
+ * r3 contains ratio to return (0 or 1)
+ * r4 contains the configuration to pick it out of
+ */
+_GLOBAL(_get_PLL_ratio)
+BEGIN_FTR_SECTION
+ li r3,-1
+ blr
+END_FTR_SECTION_IFCLR(CPU_FTR_DUALPLL)
+ /*
+ * Turn r3 into a rotate count for the selected configuration.
+ * 0 needs to become 21, 1 needs to become 29.
+ */
+ slwi r3,r3,3
+ addi r3,r3,21
+
+ rlwnm r3,r4,r3,27,31
+ blr
+
+/*
+ * Return selected range
+ * r3 contains range to return (0 or 1)
+ * r4 contains the configuration to pick it out of
+ */
+_GLOBAL(_get_PLL_range)
+BEGIN_FTR_SECTION
+ li r3,-1
+ blr
+END_FTR_SECTION_IFCLR(CPU_FTR_DUALPLL)
+ /*
+ * Turn r3 into a rotate count for the selected range.
+ * 0 needs to become 23, 1 needs to become 31.
+ */
+ slwi r3,r3,3
+ addi r3,r3,23
+
+ rlwnm r3,r4,r3,30,31
+ blr
+
+/*
+ * Update the value of the PLL configuration register
+ */
+_GLOBAL(_set_PLL)
+ /* Make sure this is a 750FX or 750GX chip */
+BEGIN_FTR_SECTION
+ li r3,-1
+ blr
+END_FTR_SECTION_IFCLR(CPU_FTR_DUALPLL)
+
+ mtspr HID1,r3 /* Get current PLL config */
+ blr
diff -Naur linux-2.4.30.orig/arch/ppc/kernel/pll_init.c linux-2.4.30/arch/ppc/kernel/pll_init.c
--- linux-2.4.30.orig/arch/ppc/kernel/pll_init.c Wed Dec 31 16:00:00 1969
+++ linux-2.4.30/arch/ppc/kernel/pll_init.c Sat Jun 25 21:15:14 2005
@@ -0,0 +1,506 @@
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/sysctl.h>
+#include <linux/ctype.h>
+#include <linux/threads.h>
+#include <linux/init.h>
+
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <asm/residual.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/cputable.h>
+#include <asm/system.h>
+#include <asm/pll.h>
+
+extern unsigned long loops_per_jiffy;
+
+static struct timer_list pll_timer;
+
+unsigned long boot_loops;
+unsigned int boot_ratio;
+
+#define TMPBUFLEN 128
+
+/*
+ * 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
+ */
+void __init init_PLL()
+{
+int temp;
+
+ 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;
+}
+
+__initcall(init_PLL);
+
+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 void pllSwitchPLLs(unsigned int newPLL)
+{
+#if 0
+unsigned long flags;
+#endif
+unsigned int new_ratio,old_ratio,current_pll,masked_boot_ratio;
+
+#if DEBUG
+printk(__FILE__"-%d: newPLL=0x%08x\n",__LINE__,newPLL);
+#endif
+ /*
+ * 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;
+#if DEBUG
+ printk(__FILE__"-%d: current_pll=0x%08x, new=%d, old=%d\n",__LINE__,
+ current_pll,new_ratio,old_ratio);
+#endif
+ current_pll=(current_pll&~PLL_SEL_MASK)|newPLL;
+
+#if DEBUG
+ printk(__FILE__"-%d: current_pll=0x%08x, new=%d, old=%d\n",__LINE__,
+ current_pll,new_ratio,old_ratio);
+#endif
+ /*
+ * 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)
+ {
+ loops_per_jiffy=pllNewLPJ(masked_boot_ratio,new_ratio,
+ boot_loops);
+#if DEBUG
+ printk(__FILE__"-%d: masked_boot_ratio=%d, new_ratio=%d, boot_loops=%d"
+ ", loops_per_jiffy=%d\n",__LINE__,masked_boot_ratio,new_ratio,
+ boot_loops,loops_per_jiffy);
+#endif
+
+ _set_PLL(current_pll);
+ }
+ else
+ {
+#if DEBUG
+ printk(__FILE__"-%d: masked_boot_ratio=%d, new_ratio=%d, "
+ "boot_loops=%d, loops_per_jiffy=%d\n",__LINE__,
+ masked_boot_ratio,new_ratio,boot_loops,
+ loops_per_jiffy);
+#endif
+ _set_PLL(current_pll);
+
+ loops_per_jiffy=pllNewLPJ(masked_boot_ratio,new_ratio,
+ boot_loops);
+#if DEBUG
+ printk(__FILE__"-%d: masked_boot_ratio=%d, new_ratio=%d, "
+ "boot_loops=%d, loops_per_jiffy=%d\n",__LINE__,
+ masked_boot_ratio,new_ratio,boot_loops,
+ loops_per_jiffy);
+#endif
+ }
+
+#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
+}
+
+static void pllTimerF(unsigned long newPLL)
+{
+ if((unsigned int)newPLL) pllSwitchPLLs((unsigned int)newPLL);
+
+ /*
+ * Clear all lock bits
+ */
+ boot_ratio&=~(PLL_TIMER|PLL0_LOCK|PLL1_LOCK);
+}
+
+static int checkPLL(unsigned int pll)
+{
+unsigned int current_pll,work_mask,pll_x;
+int rval;
+
+ /*
+ * This is not reentrant
+ */
+ if(test_and_set_bit(PLL_LOCK_BIT,&boot_ratio)) return -EBUSY;
+
+ /*
+ * Don't allow any changes if a timer is pending
+ */
+ if(boot_ratio&PLL_TIMER) 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)
+ */
+ init_timer(&pll_timer);
+
+ pll_timer.function=pllTimerF;
+ pll_timer.data=(work_mask&PLL_DO_SEL)?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=2;
+
+ pll_timer.expires=jiffies+pll_timer.expires;
+ add_timer(&pll_timer);
+ }
+ else if(work_mask&PLL_DO_SEL)
+ pllSwitchPLLs(pll&PLL_SEL_MASK);
+
+ rval=0;
+
+checkPLLErr:
+ clear_bit(PLL_LOCK_BIT,&boot_ratio);
+
+ return rval;
+checkPLLBusy:
+ rval=-EBUSY;
+ goto checkPLLErr;
+checkPLLInVal:
+ rval=-EINVAL;
+ goto checkPLLErr;
+}
+
+/*
+ * Handle accesses to the pll file. 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)
+ */
+int proc_dopllvec(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
+{
+ int vleft, first=1, len, left, val;
+ char buf[TMPBUFLEN], *p;
+#if DEBUG
+ printk(__FILE__"-%d: int proc_dopllvec()\n",__LINE__);
+#endif
+ if (!(cur_cpu_spec[0]->cpu_features & CPU_FTR_DUALPLL))
+ return -EFAULT;
+
+#if DEBUG
+ printk(__FILE__"-%d: int proc_dopllvec()\n",__LINE__);
+#endif
+ if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+
+ vleft = table->maxlen / sizeof(int);
+ left = *lenp;
+
+ for (; left /*&& vleft--*/; first=0) {
+ if (write) {
+ while (left) {
+ char c;
+ if(get_user(c,(char *) buffer))
+ return -EFAULT;
+ if (!isspace(c))
+ break;
+ left--;
+ buffer++;
+ }
+ if (!left)
+ break;
+ len = left;
+ if (len > TMPBUFLEN-1)
+ len = TMPBUFLEN-1;
+ if(copy_from_user(buf, buffer, len))
+ return -EFAULT;
+ buf[len] = 0;
+ p = buf;
+ if (*p < '0' || *p > '9')
+ break;
+ val = simple_strtoul(p, &p, 0);
+ len = p-buf;
+ if ((len < left) && *p && !isspace(*p))
+ break;
+ buffer += len;
+ left -= len;
+ val=checkPLL(val);
+ } else {
+ static const char *range_string[]={"default","high","low",
+ "reserved"};
+ static const char *fmt_list[]={"off","bypass","%d","%d.5"};
+ char pll0_cfg_ext[8],pll0_cfg[8],pll1_cfg[8];
+ unsigned int pll,temp0,rng0_ext,rng0,rng1;
+
+#if DEBUG
+ printk(__FILE__"-%d: int proc_dopllvec()\n",__LINE__);
+#endif
+ val=0;
+
+ pll=_get_PLL();
+
+#if DEBUG
+ printk(__FILE__"-%d: pll=0x%08x\n",__LINE__,pll);
+#endif
+ temp0=pll>>27;
+
+ switch(temp0)
+ {
+ case 0: case 1: case 31:
+ rng0=0;
+ break;
+ case 2: case 3:
+ rng0=1;
+ break;
+ case 5: case 7: case 9: case 11: case 13:
+ case 15: case 17: case 19:
+ rng0=3;
+ break;
+ default:
+ rng0=2;
+ break;
+ }
+
+ if(temp0>20) temp0=(temp0-10)<<1;
+
+ sprintf(pll0_cfg_ext,fmt_list[rng0],temp0>>1);
+
+ rng0_ext=(pll>>25)&0x3;
+
+ temp0=_get_PLL_ratio(0,pll);
+
+ switch(temp0)
+ {
+ case 0: case 1: case 31:
+ rng0=0;
+ break;
+ case 2: case 3:
+ rng0=1;
+ break;
+ case 5: case 7: case 9: case 11: case 13:
+ case 15: case 17: case 19:
+ rng0=3;
+ break;
+ default:
+ rng0=2;
+ break;
+ }
+
+ if(temp0>20) temp0=(temp0-10)<<1;
+
+ sprintf(pll0_cfg,fmt_list[rng0],temp0>>1);
+
+ rng0=_get_PLL_range(0,pll);
+
+ temp0=_get_PLL_ratio(1,pll);
+
+ switch(temp0)
+ {
+ case 0: case 1: case 31:
+ rng1=0;
+ break;
+ case 2: case 3:
+ rng1=1;
+ break;
+ case 5: case 7: case 9: case 11: case 13:
+ case 15: case 17: case 19:
+ rng1=3;
+ break;
+ default:
+ rng1=2;
+ break;
+ }
+
+ if(temp0>20) temp0=(temp0-10)<<1;
+
+ sprintf(pll1_cfg,fmt_list[rng1],temp0>>1);
+
+ rng1=_get_PLL_range(1,pll);
+
+ sprintf(buf,"0x%08x: PLL0 external (%s, %s), PLL %d "
+ "selected, PLL0 %s, PLL0 (%s, %s), PLL1 (%s, "
+ "%s)\n",pll,pll0_cfg_ext,range_string[rng0_ext],
+ _get_active_PLL(pll),(pll>>17)&0x1?"internal":
+ "external",pll0_cfg,range_string[rng0],
+ pll1_cfg,range_string[rng1]);
+
+ len = strlen(buf);
+ if (len > left)
+ len = left;
+ if(copy_to_user(buffer, buf, len))
+ return -EFAULT;
+ left -= len;
+ buffer += len;
+ break;
+ }
+ }
+
+ if (!write && !first && left) {
+ if(put_user('\n', (char *) buffer))
+ return -EFAULT;
+ left--, buffer++;
+ }
+ if (write) {
+ p = (char *) buffer;
+ while (left) {
+ char c;
+ if(get_user(c, p++))
+ return -EFAULT;
+ if (!isspace(c))
+ break;
+ left--;
+ }
+ }
+ if (write && first)
+ return -EINVAL;
+ *lenp -= left;
+ filp->f_pos += *lenp;
+
+ return val;
+}
diff -Naur linux-2.4.30.orig/arch/ppc/kernel/ppc_htab.c linux-2.4.30/arch/ppc/kernel/ppc_htab.c
--- linux-2.4.30.orig/arch/ppc/kernel/ppc_htab.c Thu Apr 7 22:26:06 2005
+++ linux-2.4.30/arch/ppc/kernel/ppc_htab.c Wed Jun 22 10:41:26 2005
@@ -510,7 +510,15 @@
left -= len;
_set_L2CR(val);
} else {
- int is750fx = cur_cpu_spec[0]->cpu_features & CPU_FTR_750FX;
+ int is750fgx = cur_cpu_spec[0]->cpu_features &
+ (CPU_FTR_750FX | CPU_FTR_750GX);
+
+ /*
+ * For identifying memory size
+ */
+ if(is750fgx) is750fgx=(cur_cpu_spec[0]->cpu_features
+ & CPU_FTR_750GX)?3:2;
+
p = buf;
if (!first)
*p++ = '\t';
@@ -520,7 +528,7 @@
"disabled");
if (!(val>>30&1))
p += sprintf(p, "no ");
- if (is750fx)
+ if (is750fgx)
p += sprintf(p, "ECC checkstop");
else
p += sprintf(p, "parity");
@@ -528,7 +536,7 @@
/* 75x & 74x0 have different L2CR than 745x */
if (!(cur_cpu_spec[0]->cpu_features &
CPU_FTR_SPEC7450)) {
- if (!is750fx) {
+ if (!is750fgx) {
p += sprintf(p, ", %s",
sizestrings[(val >> 28) & 3]);
p += sprintf(p, ", %s",
@@ -536,9 +544,14 @@
p += sprintf(p, ", %s",
typestrings[(val >> 23) & 3]);
}
+ else
+ p += sprintf(p, ", %s, +1 clock, "
+ "on chip, ",sizestrings[
+ is750fgx]);
+
p += sprintf(p, "%s", (val>>22)&1 ?
", data only" : "");
- if (!is750fx) {
+ if (!is750fgx) {
p += sprintf(p, "%s", (val>>20)&1 ?
", ZZ enabled": "");
}
@@ -546,7 +559,7 @@
"write-through" : "copy-back");
p += sprintf(p, "%s", (val>>18)&1 ?
", testing" : "");
- if (!is750fx) {
+ if (!is750fgx) {
p += sprintf(p, ", %sns hold",
holdstrings[(val>>16)&3]);
p += sprintf(p, "%s", (val>>15)&1 ?
diff -Naur linux-2.4.30.orig/include/asm-ppc/cputable.h linux-2.4.30/include/asm-ppc/cputable.h
--- linux-2.4.30.orig/include/asm-ppc/cputable.h Thu Apr 7 22:26:22 2005
+++ linux-2.4.30/include/asm-ppc/cputable.h Tue Jun 14 01:52:54 2005
@@ -76,6 +76,8 @@
#define CPU_FTR_NO_DPM 0x00008000
#define CPU_FTR_HAS_HIGH_BATS 0x00010000
#define CPU_FTR_NEED_COHERENT 0x00020000
+#define CPU_FTR_750GX 0x00040000
+#define CPU_FTR_DUALPLL 0x00080000
#ifdef __ASSEMBLY__
diff -Naur linux-2.4.30.orig/include/asm-ppc/pll.h linux-2.4.30/include/asm-ppc/pll.h
--- linux-2.4.30.orig/include/asm-ppc/pll.h Wed Dec 31 16:00:00 1969
+++ linux-2.4.30/include/asm-ppc/pll.h Thu Jun 23 11:32:28 2005
@@ -0,0 +1,125 @@
+#ifndef __PPC_PLL_H
+#define __PPC_PLL_H
+/*
+ Dual PLL functions, for 750FX & 750GX
+ Copyright © 2005 by 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+/*
+ Tue, June 14, 2005.
+ - First public release, contributed by Kevin Diggs.
+ ***********
+ ***********
+
+ Author: Kevin Diggs ()
+ Please e-mail updates to this file to me, thanks!
+*/
+#include <linux/config.h>
+#include <asm/processor.h>
+#include <asm/cputable.h>
+#include <asm/ppc_asm.h>
+#include <asm/cache.h>
+
+/*
+ The layout of the PLL register (HID1) is:
+
+ 0 4|5 6|7|8| 9 11|12 13|14| 15 |16 20|21 22|23|24 28|29 30| 31
+ PCE |PRE|v|v| Res | Res |v | PS | PC0 | PR0 |v | PC1 | PR1 |Res
+ | | | |
+ PSTAT1 -| | | |
+ ECLK -----| | |
+ PI0 --------------------| |
+ Res ----------------------------------------|
+
+ PCE PLL0 read-only external config
+ PRE PLL0 read-only external range
+ PSTAT1 PLL status (0 -> PLL0, 1 -> PLL1)
+ ECLK 1 -> enable clkout pin
+ PI0 PLL0 control: 0 -> external
+ PS PLL select: 0 -> PLL0, 1 -> PLL1
+ PC0 PLL0 configuration
+ PR0 PLL0 range
+ PC1 PLL1 configuration
+ PR1 PLL1 range
+
+ PLL_CFG bus ratio PLL_CFG bus ratio
+ 00000 off 10000 8
+ 00001 off 10001 8.5
+ 00010 bypass 10010 9
+ 00011 bypass 10011 9.5
+ 00100 2 10100 10
+ 00101 2.5 10101 11
+ 00110 3 10110 12
+ 00111 3.5 10111 13
+ 01000 4 11000 14
+ 01001 4.5 11001 15
+ 01010 5 11010 16
+ 01011 5.5 11011 17
+ 01100 6 11100 18
+ 01101 6.5 11101 19
+ 01110 7 11110 20
+ 01111 7.5 11111 off
+
+ PLL_RNG range
+ 00 600 - 900
+ 01 900 - 1000
+ 10 500 - 600
+
+ * Update the value of the PLL configuration register based on the crap passed
+ * in. The upper 8 bits (0 - 7) are read only and will be used as flags to con-
+ * trol what we are doing:
+ * 0x80 PLL0 configuration is valid
+ * 0x40 PLL0 range is valid
+ * 0x20 PLL1 configuration is valid
+ * 0x10 PLL1 range is valid
+ * 0x08 PLL select is valid
+ * 0x04 PLL0 control is valid
+ *
+ * Make sure that sufficient time (100 us) is given for a PLL that is changed
+ * to relock before selecting it.
+ */
+
+#define PLL0_DO_CFG (0x80)
+#define PLL0_DO_RNG (0x40)
+#define PLL1_DO_CFG (0x20)
+#define PLL1_DO_RNG (0x10)
+#define PLL_DO_SEL (0x08)
+#define PLL0_DO_CONTROL (0x04)
+
+#define PLL0_CONTROL_MASK (0x20000)
+#define PLL_SEL_MASK (0x10000)
+#define PLL0_CFG_MASK (0x0f800)
+#define PLL0_RNG_MASK (0x00600)
+#define PLL1_CFG_MASK (0x000f8)
+#define PLL1_RNG_MASK (0x00006)
+
+#define PLL_LOCK (0x80000000) /* Code lock bit */
+#define PLL_LOCK_BIT (31)
+#define PLL_TIMER (0x40000000) /* Timer is scheduled */
+#define PLL_TIMER_BIT (30)
+#define PLL0_LOCK (0x20000000) /* PLL 0 unsynced */
+#define PLL0_LOCK_BIT (29)
+#define PLL1_LOCK (0x10000000) /* PLL 1 unsynced */
+#define PLL1_LOCK_BIT (28)
+
+extern int _get_PLL(void);
+extern int _get_active_PLL(unsigned int pll);
+extern int _get_next_PLL(unsigned int pll);
+extern int _get_PLL_ratio(int which, unsigned int pll);
+extern int _get_PLL_range(int which, unsigned int pll);
+extern int _set_PLL(unsigned int pll);
+
+#endif
diff -Naur linux-2.4.30.orig/include/linux/sysctl.h linux-2.4.30/include/linux/sysctl.h
--- linux-2.4.30.orig/include/linux/sysctl.h Sat May 28 22:47:35 2005
+++ linux-2.4.30/include/linux/sysctl.h Tue Jun 14 01:26:41 2005
@@ -128,6 +128,7 @@
KERN_PPC_L3CR=57, /* l3cr register on PPC */
KERN_EXCEPTION_TRACE=58, /* boolean: exception trace */
KERN_CORE_SETUID=59, /* int: set to allow core dumps of setuid apps */
+ KERN_PPC_PLL=60, /* PLL control register (HID1) on some PPC */
KERN_SPARC_SCONS_PWROFF=64, /* int: serial console power-off halt */
};
diff -Naur linux-2.4.30.orig/kernel/sysctl.c linux-2.4.30/kernel/sysctl.c
--- linux-2.4.30.orig/kernel/sysctl.c Thu Apr 7 22:27:17 2005
+++ linux-2.4.30/kernel/sysctl.c Tue Jun 21 20:22:23 2005
@@ -100,6 +100,8 @@
void *buffer, size_t *lenp);
int proc_dol3crvec(ctl_table *table, int write, struct file *filp,
void *buffer, size_t *lenp);
+int proc_dopllvec(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp);
#endif
#ifdef CONFIG_BSD_PROCESS_ACCT
@@ -210,6 +212,10 @@
0644, NULL, &proc_dol2crvec},
{KERN_PPC_L3CR, "l3cr", NULL, 0,
0644, NULL, &proc_dol3crvec},
+#ifdef CONFIG_DUAL_PLL
+ {KERN_PPC_PLL, "pll", NULL, 0,
+ 0644, NULL, &proc_dopllvec},
+#endif
#endif
{KERN_CTLALTDEL, "ctrl-alt-del", &C_A_D, sizeof(int),
0644, NULL, &proc_dointvec},
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: preliminary 750GX PLL control patch
2005-07-26 17:28 preliminary 750GX PLL control patch Kevin Diggs
@ 2005-07-30 16:18 ` Benjamin Herrenschmidt
0 siblings, 0 replies; 2+ messages in thread
From: Benjamin Herrenschmidt @ 2005-07-30 16:18 UTC (permalink / raw)
To: Kevin Diggs; +Cc: linuxppc-dev
On Tue, 2005-07-26 at 10:28 -0700, Kevin Diggs wrote:
> Hi,
>
> I've attached a patch that adds preliminary access to the HID1
> register in the 750GX (aka the PLL config register). The patch is
> against 2.4.30. It has been lightly tested on a PowerMac 8600 with a
> PowerLogix PowerForce 750GX cpu card.
>
> Depending on the values for the pllx_cfg field, this should
> work on the 750FX as well ... except for the stuff in idle_6xx.S. Since
> there are plenty of people on this list way more smarterer than me I was
> hoping someone could answer a question about the powersave modes that
> some of the PowerPC processors have:
>
> Since, as I understand it, the clock is shut off to much of the
> chip when a powersave mode is engaged does it really buy you that much
> to switch to a lower clock speed? Wouldn't you be better off shutting
> off the unused PLL?
>
> Also, please note that I am not very experienced creating
> patches so please use a backup of your source tree.
Well... I replicated what darwin does here. I don't remember completely
but it's possible that there is some kind of CPU errata related to nap
and clock speed... or maybe it still makes a difference :)
You want to wakeup fast from nap too, so switching off/on the unused PLL
might not be that a good idea. But heh, you are welcome to try &
measure.
Ben.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2005-07-30 16:21 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-07-26 17:28 preliminary 750GX PLL control patch Kevin Diggs
2005-07-30 16:18 ` Benjamin Herrenschmidt
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.