summaryrefslogtreecommitdiffstats
path: root/utils/hwstub/stub/jz4760b/crt0.S
blob: 94d95b3e737b5c5fa281e815dc936ae83807159d (plain)
1
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include "mips.h"

.extern main
.global start

.set mips32
.set noreorder
.set noat

.section .init.text,"ax",%progbits
/* WARNING
 * We have no idea where the stubs starts running, there basically are three cases:
 * - tcsm0: the stub is already at the right place, nothing do to
 * - ram: sdram/ddram is active and we just need to move the stub
 * - cache: the bootrom put us in cache-as-ram, we need to be careful
 * Note that that those are initially quite different because:
 * - tcsm0 is uncached
 * - ram is almost always cached when we are running from it
 * - cache-as-ram is cached but the cache is the only copy of our code and the
 *   icache was prefilled from dcache by the bootrom using some mips magic
 *
 * This means we have to be very careful with the cache because if we flush the
 * icache in the cache-as-cache case, we cannot refill it, and worse, we cannot
 * commit the dcache either because the ram might not even be initialised. Thus
 * the only safe option in all cases is to copy the stub to an *uncached* location
 * so that we don't have to commit the dcache and the icache can safely read from
 * it.
 */
start:
    bltzal zero, load_addr      /* ra = PC + 8, branch not taken */
    nop
load_addr:
    addiu   v0, ra, -8          /* calc real load address
                                   account for branch delay slot */
    move    k0, v0              /* store starting location to give it to main */

    la      t0, relocstart      /* relocate code if needed */
    la      t1, relocend
    beq     t0, v0, clear_bss   /* no relocation needed */
    nop
reloc_loop:
    lw      s0, 0(v0)           /* v0 = src */
    lw      s1, 4(v0)
    lw      s2, 8(v0)
    lw      s3, 12(v0)

    sw      s0, 0(t0)           /* t0 = dst */
    sw      s1, 4(t0)
    sw      s2, 8(t0)
    sw      s3, 12(t0)

    /* Tricky part: as explained earlier, tcsm0 is uncached so no need to commit
     * the dcache but we want to invalidate the icache ONLY AT THIS LOCATION.
     * Indeed, if we invalidate the entire icache in the cache-as-ram case, we
     * will miserably crash */
    cache   ICHitInv, 0(t0)   /* invalidate virtual address in icache */

    addiu   t0, t0, 16          /* inc dst addr */
    slt     t2, t0, t1
    bnez    t2, reloc_loop
    addiu   v0, v0, 16          /* inc src addr */

    /* jump to tcsm0 */
    la      t0, tcsm0_entry
    jr      t0
    sync
tcsm0_entry:
    /* now that we are running from tcsm0, which is uncached, we can finally
     * properly invalidate all caches just to be sure */
    mtc0    zero, C0_TagLo
    mtc0    zero, C0_DataLo
    la      t0, 0x80000000          /* an idx op should use an unmappable address */
    ori     t1, t0, 0x4000          /* 16kB cache */

cache_inv_loop:
    cache   ICIndexStTag, 0(t0)     /* index store icache tag */
    cache   DCIndexStTag, 0(t0)     /* index store dcache tag */
    bne     t0, t1, cache_inv_loop
    addiu   t0, 0x20                /* 32 bytes per line */

clear_bss:
    la      t0, bssbegin
    la      t1, bssend
    beq     t0, t1, stack_setup
    nop

clear_bss_loop:
    sw      zero, 0(t0)
    bne     t0, t1, clear_bss_loop
    addiu   t0, 4

stack_setup:
    la      sp, oc_stackend

    /* the tcsm0 is usually accessed by its weird 0xf4000000 address but this
     * address is not in the range available for EBASE (because EBASE[31:30]
     * is hardwired to 0b10). Fortunately, the TCSM0 can be accessed by its
     * physical address (0x132b0000) if we ungate the AHB1 */
    la      t0, 0xb0000028 /* CPM_CLKGATE1 */
    lw      t1, 0(t0)
    li      t2, 0xffffff7e /* AHB1 */
    and     t1, t2 /* clear AHB1 */
    sw      t1, 0(t0)
    /* keep interrupts disabled, use normal exception vectors (to use EBASE) */
    li      t0, 0
    mtc0    t0, C0_STATUS
    /* set EBASE */
    la      t0, irqbase
    mtc0    t0, C0_EBASE
    /* jump to C code */
    la      t0, main
    jr      t0
    move    a0, k0

die_blink:
    /* die blinking */
    la      a0, 0xb0010400
    li      a1, 2
    sw      a1, 0x48(a0) /* clear function (gpio or interrupt) */
    sw      a1, 0x58(a0) /* clear select (gpio) */
    sw      a1, 0x64(a0) /* set direction (out) */
    sw      a1, 0x34(a0) /* set pull (disable) */
    /* turn backlight on and off */
    la      a0, 0xb0010414
    li      a1, 2
.blink_loop:
    sw      a1, (a0)
    la      v0, 10000000
.wait:
    bnez    v0, .wait
    subu    v0, 1
    sw      a1, 4(a0)
    la      v0, 10000000
.wait2:
    bnez    v0, .wait2
    subu    v0, 1
    j       .blink_loop
    nop

/* restore_data_abort_jmp restores the context and returns from exception */
    .extern restore_data_abort_jmp

    .global tlb_refill_handler
    .section .exception.tlb_refill,"ax",%progbits
tlb_refill_handler:
    la      k0, restore_data_abort_jmp
    jr      k0
    nop

    .global cache_error_handler
    .section .exception.cache_error,"ax",%progbits
cache_error_handler:
    la      k0, restore_data_abort_jmp
    jr      k0
    nop

    .global general_exception_handler
    .section .exception.general_exception,"ax",%progbits
general_exception_handler:
    la      k0, restore_data_abort_jmp
    jr      k0
    nop