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