BIOS starting on M1, SDRAM not working
[mw/milkymist.git] / software / libhpdmc / libhpdmc.S
1 /*
2  * libHPDMC - SDRAM initialization runtime for Milkymist bootloaders
3  * Copyright (C) 2010 Sebastien Bourdeauducq
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation;
8  * version 3 of the License.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, see <http://www.gnu.org/licenses>.
17  */
18
19 #include <hw/uart.h>
20 #include <hw/hpdmc.h>
21 #include <hw/vga.h>
22 #include <hw/interrupts.h>
23 #include <hw/sysctl.h>
24 #include <hw/gpio.h>
25 #include <version.h>
26
27 #include "libhpdmc.h"
28
29 /* evaluate feature dependencies */
30 #if defined(FEAT_MANUAL_CALIBRATION) && defined(FEAT_NO_OUTPUT)
31 #  error "Can't use FEAT_MANUAL_CALIBRATION with FEAT_NO_OUTPUT"
32 #endif
33
34 #if defined(FEAT_PBC_MANUAL_CALIBRATION) && ! defined(FEAT_MANUAL_CALIBRATION)
35 #  error "FEAT_PBC_MANUAL_CALIBRATION needs FEAT_MANUAL_CALIBRATION"
36 #endif
37
38 /*
39  * GLOBAL VARIABLES
40  * r25: Input delay
41  * r24: DQS phase
42  * r23: return address
43  */
44
45 #ifndef FEAT_NO_OUTPUT
46 .macro PRINT str
47         mvhi    r1, hi(\str)
48         ori     r1, r1, lo(\str)
49         calli   print
50 .endm
51 #else
52 .macro PRINT str
53 .endm
54 #endif
55
56 .macro RETURN rc
57         mvi     r1, \rc
58         b       r23
59 .endm
60
61 .section .text, "ax", @progbits
62 .global _sdram_init
63 _sdram_init:
64         mv      r23, ra
65         PRINT(banner)
66         PRINT(version)
67         PRINT(nl)
68         PRINT(nl)
69
70 // HACK for Milkymist One
71 #if 0
72         /* wait for the PLLs to lock */
73         mvhi    r1, hi(CSR_HPDMC_IODELAY)
74         ori     r1, r1, lo(CSR_HPDMC_IODELAY)
75         mvu     r3, (HPDMC_PLL1_LOCKED|HPDMC_PLL2_LOCKED)
76 wait_pll:
77         lw      r2, (r1+0)
78         and     r2, r2, r3
79         bne     r2, r3, wait_pll
80         
81         /* set IDELAY=0, wait for DQS generator ready */
82         mvu     r2, (HPDMC_IDELAY_RST)
83         sw      (r1+0), r2
84 wait_dqs_start:
85         lw      r2, (r1+0)
86         andi    r2, r2, (HPDMC_DQSDELAY_RDY)
87         be      r2, r0, wait_dqs_start
88         xor     r25, r25, r25
89         xor     r24, r24, r24
90 #endif
91
92         /* send init sequence */
93         bi send_init
94 send_init_return:
95         PRINT(seqcomplete)
96
97 #ifdef FEAT_PBC_MANUAL_CALIBRATION
98         /* if PBC is pushed, go into debug/manual mode */
99         mvhi    r1, hi(CSR_GPIO_IN)
100         ori     r1, r1, lo(CSR_GPIO_IN)
101         lw      r2, (r1+0)
102         andi    r2, r2, (GPIO_PBC)
103         bne     r2, r0, mancal
104 #endif
105
106 // HACK for Milkymist One
107         //bi    autocalibrate
108 autocalibrate_fail_return:
109         PRINT(autocalfail)
110 #ifdef FEAT_MANUAL_CALIBRATION
111         bi      mancal /* does not return */
112 #endif
113 #ifdef FEAT_RETURN_STATUS
114         RETURN(LIBHPDMC_CALIBRATION_FAILED)
115 #endif
116         bi      autocalibrate_fail_return
117
118 autocalibrate_success_return:
119         PRINT(autocalok)
120         
121         /* small memory test */
122         mvi     r1, 0
123         xor     r2, r2, r2
124         calli   memtest
125         be      r1, r0, memtest_fail
126         PRINT(continueboot)
127         
128         RETURN(LIBHPDMC_SUCCESS)
129
130 memtest_fail:
131         PRINT(testfailmsg)
132 #ifdef FEAT_MANUAL_CALIBRATION
133         bi      mancal /* does not return */
134 #endif
135 #ifdef FEAT_RETURN_STATUS
136         RETURN(LIBHPDMC_MEMTEST_FAILED)
137 #endif
138 memtest_fail_loop:
139         bi      memtest_fail_loop
140
141 /* clobbers: r1, r2, r3 */
142 send_init:
143         /* Bring CKE high */
144         mvhi    r2, hi(CSR_HPDMC_SYSTEM)
145         ori     r2, r2, lo(CSR_HPDMC_SYSTEM)
146         mvu     r3, (HPDMC_SYSTEM_BYPASS|HPDMC_SYSTEM_RESET|HPDMC_SYSTEM_CKE)
147         sw      (r2+0), r3
148         
149         ori     r1, r0, 2
150         calli   delay
151         
152         mvhi    r2, hi(CSR_HPDMC_BYPASS)
153         ori     r2, r2, lo(CSR_HPDMC_BYPASS)
154         
155         /* Precharge All */
156         mvu     r3, 0x400B
157         sw      (r2+0), r3
158         mvu     r1, 2
159         calli   delay
160         
161         /* Load Extended Mode Register */
162         mvhi    r3, 0x2
163         ori     r3, r3, 0x000F
164         sw      (r2+0), r3
165         mvu     r1, 2
166         calli   delay
167         
168         /* Load Mode Register */
169         mvu     r3, 0x123F
170         sw      (r2+0), r3
171         mvu     r1, 200
172         calli   delay
173         
174         /* Precharge All */
175         mvu     r3, 0x400B
176         sw      (r2+0), r3
177         mvu     r1, 2
178         calli   delay
179         
180         /* Auto Refresh */
181         mvu     r3, 0xD
182         sw      (r2+0), r3
183         mvu     r1, 8
184         calli   delay
185         
186         /* Auto Refresh */
187         mvu     r3, 0xD
188         sw      (r2+0), r3
189         mvu     r1, 8
190         calli   delay
191         
192         /* Load Mode Register, Enable DLL */
193         mvu     r3, 0x23F
194         sw      (r2+0), r3
195         mvu     r1, 200
196         calli   delay
197
198         /* All done, get the controller into gear */
199         mvhi    r2, hi(CSR_HPDMC_SYSTEM)
200         ori     r2, r2, lo(CSR_HPDMC_SYSTEM)
201         mvu     r3, (HPDMC_SYSTEM_CKE)
202         sw      (r2+0), r3
203
204         bi      send_init_return
205
206 /*
207  * TODO: proper dynamic autocalibration:
208  * scan IDELAY/DQS and memtest,
209  * then place in the middle of the working zone.
210  */
211 /* clobbers: r1, r2, r3, r4 */
212 autocalibrate:
213         mvu     r24, 244 /* DQS phase, change this number for your board */
214         mvhi    r1, hi(CSR_HPDMC_IODELAY)
215         ori     r1, r1, lo(CSR_HPDMC_IODELAY)
216         xor     r2, r2, r2
217         mvu     r3, (HPDMC_DQSDELAY_CE|HPDMC_DQSDELAY_INC)
218         be      r2, r24, dqs_loop_end
219 dqs_loop:
220         sw      (r1+0), r3
221 dqs_loop_wait:
222         lw      r4, (r1+0)
223         andi    r4, r4, (HPDMC_DQSDELAY_RDY)
224         be      r4, r0, dqs_loop_wait
225         addi    r2, r2, 1
226         bne     r2, r24, dqs_loop
227 dqs_loop_end:
228         bi      autocalibrate_success_return
229
230 #ifdef FEAT_MANUAL_CALIBRATION
231 /* does not return */
232 mancal:
233         /* enable VGA out */
234         mvhi    r1, hi(CSR_VGA_BASEADDRESS)
235         ori     r1, r1, lo(CSR_VGA_BASEADDRESS)
236         mvhi    r2, 0x4000
237         sw      (r1+0), r2
238         mvhi    r1, hi(CSR_VGA_RESET)
239         ori     r1, r1, lo(CSR_VGA_RESET)
240         sw      (r1+0), r0
241 mancal_loop:
242         PRINT(manualkeys)
243         mv      r1, r25
244         calli   printint
245         PRINT(spacer)
246         mv      r1, r24
247         calli   printint
248         PRINT(nl)
249         calli   getkey
250         mvu     r2, 'u'
251         be      r1, r2, inc_input_delay
252         mvu     r2, 'd'
253         be      r1, r2, dec_input_delay
254         mvu     r2, 'U'
255         be      r1, r2, inc_dqs_phase
256         mvu     r2, 'D'
257         be      r1, r2, dec_dqs_phase
258         mvu     r2, 't'
259         be      r1, r2, small_test
260         mvu     r2, 'T'
261         be      r1, r2, big_test
262         mvu     r2, 'p'
263         be      r1, r2, disp_pattern
264         mvu     r2, 'b'
265         be      r1, r2, boot
266         bi mancal_loop
267 inc_input_delay:
268         mvu     r1, 63
269         be      r25, r1, mancal_loop
270         addi    r25, r25, 1
271         mvhi    r1, hi(CSR_HPDMC_IODELAY)
272         ori     r1, r1, lo(CSR_HPDMC_IODELAY)
273         mvu     r2, (HPDMC_IDELAY_CE|HPDMC_IDELAY_INC)
274         sw      (r1+0), r2
275         bi      mancal_loop
276 dec_input_delay:
277         be      r25, r0, mancal_loop
278         addi    r25, r25, -1
279         mvhi    r1, hi(CSR_HPDMC_IODELAY)
280         ori     r1, r1, lo(CSR_HPDMC_IODELAY)
281         mvu     r2, (HPDMC_IDELAY_CE)
282         sw      (r1+0), r2
283         bi      mancal_loop
284 inc_dqs_phase:
285         mvu     r1, 255
286         be      r24, r1, mancal_loop
287         addi    r24, r24, 1
288         mvhi    r1, hi(CSR_HPDMC_IODELAY)
289         ori     r1, r1, lo(CSR_HPDMC_IODELAY)
290         mvu     r2, (HPDMC_DQSDELAY_CE|HPDMC_DQSDELAY_INC)
291         sw      (r1+0), r2
292         bi      wait_dqs
293 dec_dqs_phase:
294         be      r24, r0, mancal_loop
295         addi    r24, r24, -1
296         mvhi    r1, hi(CSR_HPDMC_IODELAY)
297         ori     r1, r1, lo(CSR_HPDMC_IODELAY)
298         mvu     r2, (HPDMC_DQSDELAY_CE)
299         sw      (r1+0), r2
300 wait_dqs:
301         lw      r2, (r1+0)
302         andi    r2, r2, HPDMC_DQSDELAY_RDY
303         be      r2, r0, wait_dqs
304         bi      mancal_loop
305 small_test:
306         xor     r1, r1, r1
307         xor     r2, r2, r2
308         calli   memtest
309         bi      after_test
310 big_test:
311         mvu     r1, 1
312         xor     r2, r2, r2
313         calli memtest
314 after_test:
315         be      r1, r0, print_test_fail
316         PRINT(memtestpassed)
317         bi      mancal_loop
318 print_test_fail:
319         PRINT(memtestfailed)
320         bi      mancal_loop
321 disp_pattern:
322         mvu     r1, 640
323         mvu     r2, 480
324         mvu     r7, 12
325         mvu     r8, 6
326         xor     r4, r4, r4 /* current Y pos */
327         mvhi    r5, 0x4000 /* current address */
328 yloop:
329         xor     r3, r3, r3 /* current X pos */
330 xloop:
331         add     r6, r3, r4
332         modu    r9, r6, r7
333         mvu     r6, 0xffff
334         bge     r9, r8, staywhite
335         xor     r6, r6, r6
336 staywhite:
337         sh      (r5+0), r6
338         addi    r5, r5, 2
339         addi    r3, r3, 1
340         bne     r3, r1, xloop
341         addi    r4, r4, 1
342         bne     r4, r2, yloop
343         bi      mancal_loop
344 boot:
345         mvhi    r1, hi(CSR_VGA_RESET)
346         ori     r1, r1, lo(CSR_VGA_RESET)
347         mvi     r2, VGA_RESET
348         sw      (r1+0), r2
349         RETURN(LIBHPDMC_SUCCESS)
350
351 /* getkey - gets a character from the console
352  *
353  * inputs:      none
354  * outputs:     r1 - character
355  * clobbers:    r1
356  */
357 getkey:
358         rcsr    r1, IP
359         andi    r1, r1, IRQ_UARTRX
360         be      r1, r0, getkey
361         wcsr    IP, r1
362         mvhi    r1, hi(CSR_UART_RXTX)
363         ori     r1, r1, lo(CSR_UART_RXTX)
364         lw      r1, (r1+0)
365         ret
366 #endif /* FEAT_MANUAL_CALIBRATION */
367
368 /* memtest - tests memory
369  *
370  * inputs:      r1 - big/small
371  *              r2 - PRNG seed
372  * outputs:     r1 - pass/fail
373  * clobbers:    r1, r3, r4, r5, r6, r7
374  */
375 memtest:
376         /* 1. fill with pattern */
377         mvhi    r5, 0x4000      /* r5 - current address */
378         mvhi    r3, 0x100       /* r3 - remaining words */
379         mv      r4, r3          /* r4 - number of words to test */
380         mv      r6, r2          /* r6 - current value of the PRNG */
381         mvhi    r7, hi(22695477)
382         ori     r7, r7, lo(22695477)
383         bne     r1, r0, patloop
384         mvhi    r3, 0x10
385         mv      r4, r3
386 patloop:
387         sw      (r5+0), r6
388         mul     r6, r6, r7
389         addi    r6, r6, 1
390         addi    r5, r5, 4
391         addi    r3, r3, -1
392         bne     r3, r0, patloop
393         /* 2. check pattern */
394         mvhi    r5, 0x4000      /* r5 - current address */
395         mv      r3, r4          /* r3 - remaining words */
396         mv      r6, r2          /* r6 - current value of the PRNG */
397 chkloop:
398         lw      r4, (r5+0)
399         bne     r4, r6, testfail
400         mul     r6, r6, r7
401         addi    r6, r6, 1
402         addi    r5, r5, 4
403         addi    r3, r3, -1
404         bne     r3, r0, chkloop
405         mvu     r1, 1
406         ret
407 testfail:
408         xor     r1, r1, r1
409         ret
410
411 #ifndef FEAT_NO_OUTPUT
412 /* printint - prints an integer 0-999
413  *
414  * inputs:      r1 - input
415  * outputs:     none
416  * clobbers:    r1, r2, r3, r4
417  */
418 printint:
419         mvhi    r2, hi(CSR_UART_RXTX)
420         ori     r2, r2, lo(CSR_UART_RXTX)
421         
422         mvu     r4, 100
423         divu    r3, r1, r4
424         modu    r1, r1, r4
425         addi    r3, r3, '0'
426         sw      (r2+0), r3
427 writeintwait1:
428         rcsr    r3, IP
429         andi    r3, r3, IRQ_UARTTX
430         be      r3, r0, writeintwait1
431         wcsr    IP, r3
432         
433         mvu     r4, 10
434         divu    r3, r1, r4
435         modu    r1, r1, r4
436         addi    r3, r3, '0'
437         sw      (r2+0), r3
438 writeintwait2:
439         rcsr    r3, IP
440         andi    r3, r3, IRQ_UARTTX
441         be      r3, r0, writeintwait2
442         wcsr    IP, r3
443         
444         addi    r3, r1, '0'
445         sw      (r2+0), r3
446 writeintwait3:
447         rcsr    r3, IP
448         andi    r3, r3, IRQ_UARTTX
449         be      r3, r0, writeintwait3
450         wcsr    IP, r3
451
452         ret
453
454 /* print - prints a zero terminated string on the console
455  *
456  * inputs:      r1 - address of the string
457  * outputs:     none
458  * clobbers:    r1, r2, r3
459  */
460 print:
461         mvhi    r2, hi(CSR_UART_RXTX)
462         ori     r2, r2, lo(CSR_UART_RXTX)
463 writeloop:
464         lb      r3, (r1+0)
465         be      r3, r0, print_endloop
466         sw      (r2+0), r3
467 writewait:
468         rcsr    r3, IP
469         andi    r3, r3, IRQ_UARTTX
470         be      r3, r0, writewait
471         wcsr    IP, r3
472         addi    r1, r1, 1
473         bi      writeloop
474 print_endloop:
475         ret
476 #endif /* FEAT_NO_OUTPUT */
477
478 /* delay - delay loop
479  *
480  * inputs:      r1 - number of iterations
481  * outputs:     none
482  * clobbers:    r1
483  */
484 delay:
485         addi    r1, r1, -1
486         bne     r1, r0, delay
487         ret
488
489 #ifndef FEAT_NO_OUTPUT
490 .section .rodata, "a"
491 banner: .string "\nlibHPDMC SDRAM initialization runtime\n(c) Copyright 2010 Sebastien Bourdeauducq, released under GNU LGPL version 3.\nVersion "
492 version: .string VERSION
493 seqcomplete: .string "Initialization sequence completed.\n"
494 autocalfail: .string "Autocalibration failed, entering manual mode.\n"
495 autocalok: .string "Autocalibration OK, testing memory...\n"
496 continueboot: .string "All SDRAM initialization completed, boot continuing.\n\n"
497 testfailmsg: .string "Memory test failed, entering manual mode.\n"
498 manualkeys: .string "\nu: inc. input delay // d: dec. input delay\nU: inc. DQS phase   // D: dec. DQS phase\nt: test (small)     // T: test (large)\np: display pattern\nb: boot\n"
499 memtestfailed: .string "Memory test failed.\n"
500 memtestpassed: .string "Memory test passed.\n"
501 spacer: .string " -- "
502 nl: .string "\n"
503 #endif /* FEAT_NO_OUTPUT */
504