Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / board-js2x / llfw / startup.S
diff --git a/qemu/roms/SLOF/board-js2x/llfw/startup.S b/qemu/roms/SLOF/board-js2x/llfw/startup.S
new file mode 100644 (file)
index 0000000..1357d3f
--- /dev/null
@@ -0,0 +1,708 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2008 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ *     IBM Corporation - initial implementation
+ *****************************************************************************/
+
+# SLOF for JS20/JS21 -- ROM boot code.
+# Initial entry point, copy code from flash to cache, memory setup.
+# Also sets up serial console and optimizes some settings.
+
+#include "termctrl.h"
+#include <product.h>
+#include <xvect.h>
+#include <cpu.h>
+#include <macros.h>
+#include <southbridge.h>
+
+       .text
+       .globl __start
+__start:
+       /* put rombase in sprg1 ***********************/
+
+       bl      postHeader
+       .long 0xDEADBEE0
+       .long 0x0       /* size */ 
+       .long 0x0       /* crc  */
+       .long relTag - __start
+postHeader:
+       mflr    r3
+       li      r4, 0x7fff
+       not     r4, r4
+       and     r3, r3, r4
+       mtsprg  1, r3      /* romfs base */
+       bl      _start
+
+       .org 0x150 - 0x100
+__startSlave:
+       bl setup_cpu
+       bl set_ci_bit
+#      b slaveWithNumber
+       b slave
+
+       .org 0x180 - 0x100
+__startMaster:
+       li 3,0
+       mtsprg  1, r3      /* romfs base */
+       bl setup_cpu
+       bl set_ci_bit
+       b master
+
+
+       /* FIXME: Also need 0280, 0380, 0f20, etc. */
+
+       .irp i, 0x0100,0x0180,0x0200,0x0280,0x0300,0x0380,0x0400,0x0500,0x0600,0x0700, \
+               0x0800,0x0900,0x0a00,0x0b00,0x0c00,0x0d00,0x0e00,0x0f00, \
+               0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, \
+               0x1800,0x1900,0x1a00,0x1b00,0x1c00,0x1d00,0x1e00,0x1f00, \
+               0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600,0x2700, \
+               0x2800,0x2900,0x2a00,0x2b00,0x2c00,0x2d00,0x2e00
+       . = \i
+
+       /* enable this if you get exceptions before the console works    */
+       /* this will allow using the hardware debugger to see where      */
+       /* it traps, and with what register values etc.                  */
+       // b    $
+
+       mtsprg  0, r0
+       mfctr   r0
+       mtsprg  2,r0
+       mflr    r0
+// 10
+       mtsprg  3,r0
+       ld      r0, (\i + 0x160)(0)
+       mtctr   r0
+       li      r0, \i + 0x100
+// 20
+       bctr
+
+       . = \i + 0x60
+
+       .quad intHandler2C
+
+       .endr
+
+
+       . = XVECT_M_HANDLER - 0x100
+       .quad 0x00
+       . = XVECT_S_HANDLER - 0x100
+
+       .quad 0
+
+
+
+       .org 0x4000 - 0x100
+_start:
+       # optimize HID register settings
+       bl setup_cpu
+       bl set_ci_bit
+
+       # read semaphore, run as slave if not the first to do so
+       li 3,0 ; oris 3,3,0xf800 ; lwz 3,0x60(3) ; andi. 3,3,1 ; beq slave
+master:
+       # setup flash, serial
+       bl setup_sio
+
+       # early greet
+       li      r3, 10
+       bl      putc
+       li 3,13 ; bl putc ; li 3,10 ; bl putc ; li 3,'S' ; bl putc
+
+
+       #do we run from ram ?
+       mfsprg  r3, 1   /* rombase */
+       cmpdi   r3,0    /* rombase is 0 when running from RAM */
+
+       bne copy_to_cache
+
+       # wait a bit, start scripts are slow...  need to get all cores running!
+       lis 3,0x4000 ; mtctr 3 ; bdnz $
+
+       # copy 4MB from 0 to temp memory
+   lis 4,0x8 ; mtctr 4 ; lis 4,0x200  ; li 3,0 ; addi 4,4,-8 ; addi 3,3,-8
+0:     ldu 5,8(3) ; stdu 5,8(4) ; bdnz 0b
+
+       lis 4,0x200
+       mtsprg  1, r4
+
+       lis 4,0x1
+       lis 3,0x20 ; addi 3,3,0x200-8 ;
+       FLUSH_CACHE(r3, r4)
+
+       lis 4,0x200
+       addi 4,4,copy_to_cache@l
+       mtctr 4
+       bctr
+
+# make all data accesses cache-inhibited
+set_ci_bit:
+       SETCI(r0)
+       blr
+
+# make all data accesses cacheable
+clr_ci_bit:
+       CLRCI(r0)
+       blr
+
+# write a character to the serial port
+putc:
+# always write to serial1
+0:     lbz 0,5(13) ; andi. 0,0,0x20 ; beq 0b ; stb 3,0(13) ; eieio
+
+# read ID register: only if it is a PC87427 (JS21) also use serial2
+       li 4,0 ; oris 4,4,0xf400
+       li 5,0x20 ; stb 5,0x2e(4) ; lbz 5,0x2f(4); cmpdi 5,0xf2 ; bne 1f
+
+       addi 4,4,0x2f8
+0:     lbz 0,5(4) ; andi. 0,0,0x20 ; beq 0b ; stb 3,0(4) ; eieio
+
+1:     blr
+
+# transfer from running from flash to running from cache
+return_cacheable:
+       # find and set address to start running from cache, set msr value 
+       mflr 3 ; rldicl 3,3,0,44 
+jump_cacheable:
+       mtsrr0 3 ; 
+       mfmsr 3 ; ori 3,3,0x1000 ; mtsrr1 3 # enable MCE, as well
+
+       # set cacheable insn fetches, jump to cache
+       mfspr 3,HID1 ; rldicl 3,3,32,0 ; oris 3,3,0x0020 ; rldicl 3,3,32,0
+       sync ; mtspr HID1,3 ; mtspr HID1,3 ; rfid ; b .
+
+
+
+
+copy_to_cache:
+       # zero the whole cache
+       # also, invalidate the insn cache, to clear parity errors
+       # 128kB @ 0MB (boot code and vectors from 0x0 up to 0x20000)
+       li 4,0x400 ; mtctr 4 ; li 5,0x0 ; bl clr_ci_bit
+0:     dcbz 0,5 ; sync ; icbi 0,5 ; sync ; isync ; addi 5,5,0x80 ; bdnz 0b
+
+       # 0x2000 to 0x100000/0x80000 (smaller on 970/970FX)
+       li 4,0x1C00 ; mfpvr 0 ; srdi 0,0,16 ; cmpdi 0,0x0044 ; bge 0f ; li 4,0xC00
+0:
+       mtctr 4 ; li 5,0x2000
+0:     dcbz 0,5 ; sync ; isync ; addi 5,5,0x80 ; bdnz 0b ; bl set_ci_bit
+
+       # find base address
+       bcl 20,31,$+4 ; mflr 31 ; rldicr 31,31,0,43
+
+       # copy 1kB from 0x4000
+       li 4,0x80 ; mtctr 4 ; 
+       li      5,0x3ff8
+       addi 3,31,0x3ff8
+0:     ldu 4,8(3) ; bl clr_ci_bit ; stdu 4,8(5) ; bl set_ci_bit ; bdnz 0b
+       # now start executing from cache -- insn cache is huge speed boost
+
+       bl return_cacheable
+
+       li 3,'L' ; bl putc
+
+       # copy 128kB of flash to cache
+       li 4,0x800 ; mtctr 4 ; li 5,0x200-64 ; addi 3,31,0x200-64 ; 
+0:     ldu 16,64(3) ; ld 17,8(3) ; ld 18,16(3) ; ld 19,24(3)
+       ld 20,32(3) ; ld 21,40(3) ; ld 22,48(3) ; ld 23,56(3)
+       bl clr_ci_bit
+       stdu 16,64(5) ; std 17,8(5) ; std 18,16(5) ; std 19,24(5)
+       std 20,32(5) ; std 21,40(5) ; std 22,48(5) ; std 23,56(5)
+       icbi 0,5 ; bl set_ci_bit ; bdnz 0b ; isync
+
+
+       li 3,'O' ; bl putc
+
+       lis 4,0x20
+       mfsprg  r3,1
+       cmpd r3,r4
+       beq 1f
+
+       // at 0xf8000000 we decide if it is u3 or u4
+       li 4,0 ; oris 4,4,0xf800 ; lwz 3,0(4) ; srdi 3,3,4 ; cmpdi 3,3 ; bne 0f
+       bl setup_mem_u3
+       bl setup_mem_size
+       b 1f
+0:
+
+1:
+       li 3,'F' ; bl putc
+
+       # setup nvram logging only when not running from RAM
+       mfsprg  r3, 1   /* rombase */
+       cmpdi   r3, 0   /* rombase is 0 when running from RAM */
+       beq     0f
+
+       // at 0xf8000000 we decide if it is u3 or u4
+       li      r4, 0
+       oris    r4, r4, 0xf800
+       lwz     r3, 0(r4)
+       srdi    r3, r3, 4
+       cmpdi   r3, 3   /* 3 means js20; no nvram logging on js20 */
+       beq     0f
+
+       bl      io_log_init
+0:
+
+       #bl print_mem
+
+       # data is cacheable by default from now on
+       bl clr_ci_bit
+
+
+       /* give live sign *****************************/
+       bl      0f
+       .ascii  TERM_CTRL_RESET
+       .ascii  TERM_CTRL_CRSOFF
+       .ascii  " **********************************************************************"
+       .ascii  "\r\n"
+       .ascii  TERM_CTRL_BRIGHT
+       .ascii  PRODUCT_NAME
+       .ascii  " Starting\r\n"
+       .ascii  TERM_CTRL_RESET
+       .ascii  " Build Date = ", __DATE__, " ", __TIME__
+       .ascii  "\r\n"
+       .ascii  " FW Version = " , RELEASE
+       .ascii  "\r\n\0"
+       .align  2
+0:     mflr    r3
+       bl      io_print
+
+       # go!
+       li      r3,__startC@l
+       mtctr   r3
+       mfsprg  r10, 1
+       bctrl
+
+relTag:
+       .ascii  RELEASE
+       .ascii  "\0"
+       .align  2
+
+slave:
+
+       # get cpu number
+       li 3,0 ; oris 3,3,0xf800 ; lwz 28,0x50(3)
+
+slaveWithNumber:
+       # create our slave loop address
+       sldi 3,28,24 ; oris 3,3,0x3000
+
+       # invalidate the insn cache, to clear parity errors
+       # clear the L2 cache as well, to get ECC right
+       li 4,0x2000 ; mfpvr 0 ; srdi 0,0,16 ; cmpdi 0,0x0044 ; bge 0f ; li 4,0x1000
+0:     mtctr 4 ; mr 5,3 ; bl clr_ci_bit
+
+0:     dcbz 0,5 ; sync ; icbi 0,5 ; sync ; isync ; addi 5,5,0x80 ; bdnz 0b
+
+
+       # write a "b $" insn in there
+       lis 4,0x4800 ; stw 4,0(3)
+/*
+       mr 5,3
+
+       # jump there
+       bl set_ci_bit
+       li 13,0 ; oris 13,13,0xf400
+       # device address
+       addi 13,13,0x2f8
+       li 3,'O' ; add 3,3,28 ; bl putc
+       bl clr_ci_bit
+       mr 3,5
+*/
+       b jump_cacheable
+
+
+
+
+# allow the flash chip to be accessed faster
+# initialize the 16550-compatible uart on serial port 1 of the sio
+setup_sio:
+
+       # i/o base address
+       li 3,0 ; oris 3,3,0xf400
+
+       # i/o base address
+       li 3,0 ; oris 3,3,0xf400
+
+       # put x-bus in turbo mode
+       li 4,0xf1 ; stb 4,0x400(3) ; eieio
+
+
+       # select sio serial1
+       li 4,7 ; stb 4,0x2e(3) ; eieio ; li 4,3 ; stb 4,0x2f(3) ; eieio
+
+       # set base address to 3f8
+       li 4,0x60 ; stb 4,0x2e(3) ; eieio ; li 4,3 ; stb 4,0x2f(3) ; eieio
+
+       # enable device
+       li 4,0x30 ; stb 4,0x2e(3) ; eieio ; li 4,1 ; stb 4,0x2f(3) ; eieio
+
+       # read ID register: only if it is a PC87427, enable serial2
+       li 4,0x20 ; stb 4,0x2e(3) ; eieio ; lbz 4,0x2f(3) ; cmpdi 4,0xf2 ; bne 0f
+
+       # select sio serial2
+       li 4,7 ; stb 4,0x2e(3) ; eieio ; li 4,2 ; stb 4,0x2f(3) ; eieio
+
+       # set base address to 2f8
+       li 4,0x60 ; stb 4,0x2e(3) ; eieio ; li 4,2 ; stb 4,0x2f(3) ; eieio
+
+       # enable device
+       li 4,0x30 ; stb 4,0x2e(3) ; eieio ; li 4,1 ; stb 4,0x2f(3) ; eieio
+
+       # uart @0x2f8
+       addi 3,3,0x2f8
+
+       # disable interrupts, fifo off
+       li 4,0 ; stb 4,1(3) ; eieio ; stb 4,2(3) ; eieio
+
+       # set serial speed
+       li 4,0x80 ; stb 4,3(3) ; eieio
+       li 4,115200/19200 ; stb 4,0(3) ; eieio ; li 4,0 ; stb 4,1(3) ; eieio
+
+       # set 8-N-1, set RTS and DTR
+       li 4,3 ; stb 4,3(3) ; eieio ; stb 4,4(3) ; eieio
+
+       eieio
+
+       addi 3,3,-0x2f8
+
+       # uart @0x3f8
+0:     addi 3,3,0x3f8
+
+       # disable interrupts, fifo off
+       li 4,0 ; stb 4,1(3) ; eieio ; stb 4,2(3) ; eieio
+
+       # set serial speed
+       li 4,0x80 ; stb 4,3(3) ; eieio
+       li 4,115200/19200 ; stb 4,0(3) ; eieio ; li 4,0 ; stb 4,1(3) ; eieio
+
+       # set 8-N-1, set RTS and DTR
+       li 4,3 ; stb 4,3(3) ; eieio ; stb 4,4(3) ; eieio
+
+       eieio
+
+       # save UART base for putc routine
+0:     mr 13,3
+
+       blr
+
+
+
+
+# set the HID registers of the 970 for optimally executing from flash
+setup_cpu:
+
+       /* clear all the HV cruft */
+       li      r0, 0
+       sync
+       mtspr   HID4, r0
+       isync
+
+       /* enable dpm, disable attn insn, enable external mce
+        * first, try external time base; if clock doesn't run, switch to
+        * internal */
+       li      r0, 1                   /* do the setup for external timebase */
+       rldicl  r0, r0, 44, 0           /* bit 19 has to be set */
+       oris    r0, r0, 0x8000          /* Enable external machine check */
+                                       /* interrupts (preferred state */
+                                       /* equals `1'). */
+       sync
+       mtspr   HID0, r0
+       isync
+
+       mftb    r3                      /* read the timebase */
+       li      r1, 0x4000              /* wait long enough for the external */
+       mtctr   r1                      /* timebase (14MHz) to tick a bit */
+       bdnz    $                       /* 0x4000 seems to be enough (for now) */
+       mftb    r4                      /* read the timebase a second time */
+       cmpld   r3, r4                  /* see if it changed */
+       bne     0f
+       /* timebase did not change, do the setup for internal */
+       rldicl  r0, r0, 19, 1
+       rldicl  r0, r0, 45, 0
+       sync
+       mtspr   HID0, r0
+       isync
+
+0:
+       /* enable insn prefetch, speculative table walks */
+       mfspr   r0, HID1
+       rldicl  r0, r0, 20, 0
+       ori     r0, r0, 0x1002
+       mfsprg  r3, 1                   /* read rombase */
+       cmpdi   r3, 0                   /* check if running from ram */
+       bne     0f
+       /* running from ram */
+       /* Enable instruction fetch cacheability control */
+       ori     r0, r0, 0x200
+0:
+       rldicl  r0, r0, 44, 0
+       sync
+       mtspr   HID1, r0
+       isync
+
+       /* enable cache parity */
+       mfspr   r0, HID4
+       oris    r0, r0, 0xfff0
+       xoris   r0, r0, 0xfff0
+       sync
+       mtspr   HID4, r0
+       isync
+
+       /* exception offset at 0 */
+       li      r3, 0
+       mtspr   HIOR, r3
+
+       blr
+
+C_ENTRY(proceedInterrupt)
+
+       ld      r3,exception_stack_frame@got(r2)
+       ld      r1,0(r3)
+
+       .irp i, 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, \
+               17, 18, 19, 20, 21, 22, 23, 24, 25, 26, \
+               27, 28, 29, 30, 31
+       ld      r\i, 0x30+\i*8 (r1)
+       .endr
+
+       ld      r14,0x138(r1);
+       mtsrr0  r14
+
+       ld      r14,0x140(r1);
+       mtsrr1  r14
+
+       ld      r14,0x148(r1);
+       mtcr    r14
+
+
+       ld 0,XVECT_M_HANDLER(0)
+       mtctr 0
+
+       ld      r0,0x30(r1); # restore vector number
+       ld      r1,0x38(r1);
+
+       bctr
+
+intHandler2C:
+       mtctr   r1 # save old stack pointer
+       lis     r1,0x4
+       stdu    r1, -0x160(r1)
+       .irp i, 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, \
+               17, 18, 19, 20, 21, 22, 23, 24, 25, 26, \
+               27, 28, 29, 30, 31
+       std     r\i, 0x30+\i*8 (r1)
+       .endr
+
+       std     r0,0x30(r1);  # save vector number
+
+       mfctr   r14
+       std     r14,0x38(r1); # save old r1
+
+       mfsrr0  r14
+       std     r14,0x138(r1);
+
+       mfsrr1  r14
+       std     r14,0x140(r1);
+
+       mfcr    r14
+       std     r14,0x148(r1);
+
+       mfxer   r14
+       std     r14,0x150(r1);
+
+       bl toc_init
+
+       ld      r3,exception_stack_frame@got(r2)
+       std     r1,0(r3)
+
+
+       mr      r3,r0
+       bl .c_interrupt
+
+       ld      r14,0x138(r1);
+       mtsrr0  r14
+
+       ld      r14,0x140(r1);
+       mtsrr1  r14
+
+       ld      r14,0x148(r1);
+       mtcr    r14
+
+       ld      r14,0x150(r1);
+       mtxer   r14
+
+
+       .irp i, 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, \
+               17, 18, 19, 20, 21, 22, 23, 24, 25, 26, \
+               27, 28, 29, 30, 31
+       ld      r\i, 0x30+\i*8 (r1)
+       .endr
+
+       ld      r1,0x38(r1);
+
+       mfsprg  r0,2
+       mtctr   r0
+       mfsprg  r0,3
+       mtlr    r0
+       mfsprg  r0,0
+       rfid
+
+/* Set exception handler for given exception vector.  
+       r3:     exception vector offset
+       r4:     exception handler
+*/
+       .globl .set_exception
+.set_exception:
+       .globl set_exception
+set_exception:
+    ld r4,0x0(r4)
+       .globl .set_exception_asm
+.set_exception_asm:
+       .globl set_exception_asm
+set_exception_asm:
+       std     r4, 0x60(r3)    # fixme diff 1f - 0b
+       blr
+
+
+setup_mem_u3:
+       li 4,0x2000 ; oris 4,4,0xf800
+
+       # MemTimingParam -- CAS lat 2.5 / 4 (read-to-read / read-to-write)
+       lis 3,0x49e1 ; ori 3,3,0xa000 ; stw 3,0x50(4)
+
+       # MRSRegCntl -- CAS lat 2.5
+       li 3,0x6a ; stw 3,0xf0(4)
+
+       # MemBusConfig -- 128 bit bus
+       lis 3,0x8500 ; stw 3,0x190(4)
+
+       # CKDelAdj -- clock delay 75
+       lis 3,0x12c3 ; ori 3,3,0x30cc ; stw 3,0x520(4)
+
+       # IOModeCntl -- no termination on differential and 3-state drivers
+       lis 3,0x0350 ; stw 3,0x530(4)
+
+       li 3,18 ; mtctr 3 ; addi 5,4,0x5f0
+0:     # DQSDelAdj -- read delay offset -10
+       lis 3,0x3d8f ; ori 3,3,0x6000 ; stwu 3,0x10(5)
+
+       # DQSDataDelAdj -- write delay offset -32, write data delay offset +15
+       lis 3,0x380e ; ori 3,3,0x003c ; stwu 3,0x10(5)
+       bdnz 0b
+
+       # MemProgCntl -- set all
+       lis 3,0xc000 ; stw 3,0xe0(4)
+
+       eieio
+
+       blr
+
+
+# read dimm SPDs, program memory size and type
+setup_mem_size:
+       mflr 14
+
+       li 15,0 ; oris 15,15,0xf800 ; li 17,0
+       li 3,0xa0 ; li 4,3 ; li 5,3 ; bl i2c_read
+       mr 16,4 ; cmpdi 3,0 ; beq 0f ; li 16,0
+0:     li 3,0xa2 ; li 4,3 ; li 5,3 ; bl i2c_read
+       cmpd 16,4 ; bne 0f ; cmpdi 3,0 ; beq 1f
+0:     li 16,0x1e00
+1:     #li 3,0xd ; bl print_byte ; li 3,0xa ; bl print_byte
+       #mr 3,16 ; bl print_hex
+
+       #li 3,0x20 ; bl print_byte
+       sldi 3,16,7 ; add 3,3,16 ; rlwinm 3,3,10,0,6 ; subis 3,3,0x3c00
+       stw 3,0x21c0(15) ; andi. 0,16,2 ; beq 0f ; stw 3,0x21e0(15)
+0:     #bl print_hex
+       sldi 3,16,8 ; add 3,3,16 ; rldicl 3,3,48,56 ; li 0,8 ; slw 3,0,3
+               # slw, not sld, so that empty/bad banks translate into size 0
+       stw 17,0x21d0(15) ; bl add17173 ; stw 17,0x21f0(15)
+       andi. 0,16,2 ; beq 0f ; bl add17173
+0:     #bl print_hex
+
+       li 3,0xa4 ; li 4,3 ; li 5,3 ; bl i2c_read
+       mr 16,4 ; cmpdi 3,0 ; beq 0f ; li 16,0
+0:     li 3,0xa6 ; li 4,3 ; li 5,3 ; bl i2c_read
+       cmpd 16,4 ; bne 0f ; cmpdi 3,0 ; beq 1f
+0:     li 16,0x1e00
+1:     #li 3,0xd ; bl print_byte ; li 3,0xa ; bl print_byte
+       #mr 3,16 ; bl print_hex
+
+       #li 3,0x20 ; bl print_byte
+       sldi 3,16,7 ; add 3,3,16 ; rlwinm 3,3,10,0,6 ; subis 3,3,0x3c00
+       stw 3,0x2200(15) ; andi. 0,16,2 ; beq 0f ; stw 3,0x2220(15)
+0:     #bl print_hex
+       sldi 3,16,8 ; add 3,3,16 ; rldicl 3,3,48,56 ; li 0,8 ; slw 3,0,3
+       stw 17,0x2210(15) ; bl add17173 ; stw 17,0x2230(15)
+       andi. 0,16,2 ; beq 0f ; bl add17173
+0:     #bl print_hex
+       #mr 3,17 ; bl print_hex
+       stw 17,0x2250(15) ; stw 17,0x2270(15)
+       stw 17,0x2290(15) ; stw 17,0x22b0(15)
+
+       mtlr 14
+       blr
+
+
+
+
+# print GPR3 as 8-digit hex.  uses GPR18,19
+print_hex:
+       mflr 18 ; mr 19,3 ; li 3,8 ; mtctr 3
+1:     rlwinm 3,19,4,28,31 ; sldi 19,19,4
+       cmpdi 3,0xa ; blt 0f ; addi 3,3,0x27
+0:     addi 3,3,0x30 ; bl putc
+       bdnz 1b ; mtlr 18 ; blr
+
+
+# i2c stuff uses GPR20..GPR24
+
+# terminate any i2c transaction, at any point during that transaction
+i2c_stop:
+0:     lwz 3,0x30(20) ; stw 3,0x30(20) ; andi. 3,3,4 ; beq 0b
+       mr 3,21 ; mr 4,22 ; mtlr 24 ; eieio ; blr
+
+# do a combined-mode read
+# in: GPR3 = addr, GPR4 = subaddr, GPR5 = len
+# out: GPR3 = error, GPR4 = result (right-aligned, msb)
+i2c_read:
+       mflr 24
+       li 20,0x1000 ; oris 20,20,0xf800        # uni-n i2c base
+       mr 21,3 ; mr 22,4 ; mr 23,5             # save params
+       li 4,0xc ; stw 4,0(20)                  # set mode (combined)
+       ori 4,21,1 ; stw 4,0x50(20)             # set addr, read
+       stw 22,0x60(20)                         # set subaddr
+       li 4,2 ; stw 4,0x10(20) ; eieio         # start address phase
+       li 21,1                                 # error
+       li 22,0                                 # result accumulator
+0:     lwz 3,0x30(20) ; andi. 3,3,2 ; beq 0b   # wait until sent
+       lwz 3,0x20(20) ; andi. 3,3,2 ; beq i2c_stop # check result
+       li 4,1 ; cmpdi 23,1 ; bne 0f ; li 4,0
+0:     stw 4,0x10(20)                          # AAK for next byte (or not)
+       li 4,2 ; stw 4,0x30(20) ; eieio         # ack address phase
+i2c_read_loop:
+       lwz 3,0x30(20) ; andi. 3,3,1 ; beq 1f   # if byte recv'd:
+       subi 23,23,1 ; sldi 22,22,8             # shift byte accum
+       lwz 3,0x70(20) ; rlwimi 22,3,0,24,31    # get byte
+       cmpdi 23,0 ; bne 0f ; li 21,0 ; b i2c_stop # all done
+0:     li 4,1 ; cmpdi 23,1 ; bne 0f ; li 4,0
+0:      stw 4,0x10(20)                         # AAK for next byte (or not)
+       li 4,1 ; stw 4,0x30(20) ; eieio         # ack data phase
+1:     lwz 3,0x30(20) ; andi. 3,3,4 ; beq i2c_read_loop
+       li 4,0 ; stw 4,0x10(20) ; eieio ; b i2c_stop # stop bit received
+
+add17173: # add GPR3 into GPR17; if passing 2GB (0x10000000), add another 2GB.
+       lis 0,0x1000 ; cmpld 17,0 ; add 17,17,3 ; bgtlr
+       cmpld 17,0 ; blelr ; add 17,17,0 ; blr
+
+io_log_init:
+       LOAD64(r3, SB_NVRAM_adr)
+       b checkinitLog