All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kevin Diggs <kevdig@hypersurf.com>
To: linuxppc-dev@ozlabs.org
Subject: preliminary 750GX PLL control patch
Date: Tue, 26 Jul 2005 10:28:17 -0700	[thread overview]
Message-ID: <42E672B1.7050709@hypersurf.com> (raw)

[-- 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},

             reply	other threads:[~2005-07-26 17:32 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-07-26 17:28 Kevin Diggs [this message]
2005-07-30 16:18 ` preliminary 750GX PLL control patch Benjamin Herrenschmidt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=42E672B1.7050709@hypersurf.com \
    --to=kevdig@hypersurf.com \
    --cc=linuxppc-dev@ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.