Subject: tipb 667/800Mhz
To: None <port-macppc@netbsd.org>
From: Benjamin Herrenschmidt <benh@mipsys.com>
List: port-macppc
Date: 08/05/2002 17:17:52
Since this was discussed here earlier, I though you would be interested 
in the conclusion of my experiments with having this machine actually 
do 800Mhz with a non-Apple OS.

So first, forget about the OF hack that hang around on the net, all I 
could do with it is let the kernel beleive it runs at 800Mhz, but run 
any real benchmark of the CPU and you'll quickly discover that it's 
really still at 667Mhz. This matches the result of the Open Firmware 
dumps I did on this box, that is OF there doesn't seem to have code to 
do the actual switch.

So how does it work ? Well, basically, it's the PMU that controls the 
pin strapping of the PLL_CFG[0..3] pins. You need to send it a 0x7d 
command, with 5 bytes as parameters ('W', 'O', 'O', 'F', x) where "x" 
is 0x00 for high speed or 0x01 for low speed.
When you do that, the PMU enters a state similar to what happens with 
the sleep command. That is it basically waits to be informed (by 
uninorth I suppose) that the CPU has went to sleep, then shuts it down 
(or hard resets it) with the proper PLL config. So the actual speed 
switch procedure is very similar to machine sleep, except that device 
drivers don't need to deal with sleep and a slight change to what you 
write to UniNorth power management register.

The actual scenario I implemented successfully in linux/ppc is:

   1) disable all interrupt sources on OpenPIC (basically raise the CPU 
priority mask)
   2) Wait for the PMU to be idle (complete outstanding  battery or 
environement requests)
   3) Make sure the DEC won't bother us
   4) disable interrupts (MSR:EE)
   5) save values of L2CR and L3CR, then flush & disable L2 and L3 
caches (MacOS X does this
        later as part of the last step, do as you will, it was simpler 
for me that way)
   6) send the PMU request
   7) Write 0x00000001 (BE) to UniNorth HWInitState register 
(uninorth_base + 0x70). This one
        tells the ROM to branch to the wakeup vector (0x80 physical in 
RAM) when the CPU is restarted.
        Write 0x00000001 (BE) to UniNorth PowerManagement register 
(uninorth_base + 0x30)
   8) write a physical pointer to the resume code at physical address 
0x80 of RAM. The ROM will read
        that pointer and jump to that address on wakeup. MacOS X just 
uses the reset vector for that by
        writing 0x100 in this location, Linux just jumps directly to the 
resume code
   9) flush remaining caches & disable them, backup CPU state 
(registers, important SPRs, etc...)
   10) put the CPU in and endless sleep loop (HID0:SLEEP, MSR:POW, 
MSR:EE probably optional)

At this point, the PMU will kill the CPU, change the PLL_CFG bits, and 
restart to ROM, which will
call your resume vector where you can restore the CPU state, the 
caches, etc...

You can see the actual value of the PLL_CFG bits by reading the HID1 
register. Refer to the 7455
docs for actual table of values for a 133Mhz bus, though I don't know 
if we can instruct the PMU to
present other than 667Mhz and 800Mhz values.

Have fun !

Ben.