head.S 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /* head.S: kernel entry point for FR-V kernel
  2. *
  3. * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
  4. * Written by David Howells (dhowells@redhat.com)
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version
  9. * 2 of the License, or (at your option) any later version.
  10. */
  11. #include <linux/init.h>
  12. #include <linux/threads.h>
  13. #include <linux/linkage.h>
  14. #include <asm/thread_info.h>
  15. #include <asm/ptrace.h>
  16. #include <asm/page.h>
  17. #include <asm/spr-regs.h>
  18. #include <asm/mb86943a.h>
  19. #include <asm/cache.h>
  20. #include "head.inc"
  21. ###############################################################################
  22. #
  23. # void _boot(unsigned long magic, char *command_line) __attribute__((noreturn))
  24. #
  25. # - if magic is 0xdead1eaf, then command_line is assumed to point to the kernel
  26. # command line string
  27. #
  28. ###############################################################################
  29. __HEAD
  30. .balign 4
  31. .globl _boot, __head_reference
  32. .type _boot,@function
  33. _boot:
  34. __head_reference:
  35. sethi.p %hi(LED_ADDR),gr30
  36. setlo %lo(LED_ADDR),gr30
  37. LEDS 0x0000
  38. # calculate reference address for PC-relative stuff
  39. call 0f
  40. 0: movsg lr,gr26
  41. addi gr26,#__head_reference-0b,gr26
  42. # invalidate and disable both of the caches and turn off the memory access checking
  43. dcef @(gr0,gr0),1
  44. bar
  45. sethi.p %hi(~(HSR0_ICE|HSR0_DCE|HSR0_CBM|HSR0_EIMMU|HSR0_EDMMU)),gr4
  46. setlo %lo(~(HSR0_ICE|HSR0_DCE|HSR0_CBM|HSR0_EIMMU|HSR0_EDMMU)),gr4
  47. movsg hsr0,gr5
  48. and gr4,gr5,gr5
  49. movgs gr5,hsr0
  50. movsg hsr0,gr5
  51. LEDS 0x0001
  52. icei @(gr0,gr0),1
  53. dcei @(gr0,gr0),1
  54. bar
  55. # turn the instruction cache back on
  56. sethi.p %hi(HSR0_ICE),gr4
  57. setlo %lo(HSR0_ICE),gr4
  58. movsg hsr0,gr5
  59. or gr4,gr5,gr5
  60. movgs gr5,hsr0
  61. movsg hsr0,gr5
  62. bar
  63. LEDS 0x0002
  64. # retrieve the parameters (including command line) before we overwrite them
  65. sethi.p %hi(0xdead1eaf),gr7
  66. setlo %lo(0xdead1eaf),gr7
  67. subcc gr7,gr8,gr0,icc0
  68. bne icc0,#0,__head_no_parameters
  69. sethi.p %hi(redboot_command_line-1),gr6
  70. setlo %lo(redboot_command_line-1),gr6
  71. sethi.p %hi(__head_reference),gr4
  72. setlo %lo(__head_reference),gr4
  73. sub gr6,gr4,gr6
  74. add.p gr6,gr26,gr6
  75. subi gr9,#1,gr9
  76. setlos.p #511,gr4
  77. setlos #1,gr5
  78. __head_copy_cmdline:
  79. ldubu.p @(gr9,gr5),gr16
  80. subicc gr4,#1,gr4,icc0
  81. stbu.p gr16,@(gr6,gr5)
  82. subicc gr16,#0,gr0,icc1
  83. bls icc0,#0,__head_end_cmdline
  84. bne icc1,#1,__head_copy_cmdline
  85. __head_end_cmdline:
  86. stbu gr0,@(gr6,gr5)
  87. __head_no_parameters:
  88. ###############################################################################
  89. #
  90. # we need to relocate the SDRAM to 0x00000000 (linux) or 0xC0000000 (uClinux)
  91. # - note that we're going to have to run entirely out of the icache whilst
  92. # fiddling with the SDRAM controller registers
  93. #
  94. ###############################################################################
  95. #ifdef CONFIG_MMU
  96. call __head_fr451_describe_sdram
  97. #else
  98. movsg psr,gr5
  99. srli gr5,#28,gr5
  100. subicc gr5,#3,gr0,icc0
  101. beq icc0,#0,__head_fr551_sdram
  102. call __head_fr401_describe_sdram
  103. bra __head_do_sdram
  104. __head_fr551_sdram:
  105. call __head_fr555_describe_sdram
  106. LEDS 0x000d
  107. __head_do_sdram:
  108. #endif
  109. # preload the registers with invalid values in case any DBR/DARS are marked not present
  110. sethi.p %hi(0xfe000000),gr17 ; unused SDRAM DBR value
  111. setlo %lo(0xfe000000),gr17
  112. or.p gr17,gr0,gr20
  113. or gr17,gr0,gr21
  114. or.p gr17,gr0,gr22
  115. or gr17,gr0,gr23
  116. # consult the SDRAM controller CS address registers
  117. cld @(gr14,gr0 ),gr20, cc0,#1 ; DBR0 / DARS0
  118. cld @(gr14,gr11),gr21, cc1,#1 ; DBR1 / DARS1
  119. cld @(gr14,gr12),gr22, cc2,#1 ; DBR2 / DARS2
  120. cld.p @(gr14,gr13),gr23, cc3,#1 ; DBR3 / DARS3
  121. sll gr20,gr15,gr20 ; shift values up for FR551
  122. sll gr21,gr15,gr21
  123. sll gr22,gr15,gr22
  124. sll gr23,gr15,gr23
  125. LEDS 0x0003
  126. # assume the lowest valid CS line to be the SDRAM base and get its address
  127. subcc gr20,gr17,gr0,icc0
  128. subcc.p gr21,gr17,gr0,icc1
  129. subcc gr22,gr17,gr0,icc2
  130. subcc.p gr23,gr17,gr0,icc3
  131. ckne icc0,cc4 ; T if DBR0 != 0xfe000000
  132. ckne icc1,cc5
  133. ckne icc2,cc6
  134. ckne icc3,cc7
  135. cor gr23,gr0,gr24, cc7,#1 ; GR24 = SDRAM base
  136. cor gr22,gr0,gr24, cc6,#1
  137. cor gr21,gr0,gr24, cc5,#1
  138. cor gr20,gr0,gr24, cc4,#1
  139. # calculate the displacement required to get the SDRAM into the right place in memory
  140. sethi.p %hi(__sdram_base),gr16
  141. setlo %lo(__sdram_base),gr16
  142. sub gr16,gr24,gr16 ; delta = __sdram_base - DBRx
  143. # calculate the new values to go in the controller regs
  144. cadd.p gr20,gr16,gr20, cc4,#1 ; DCS#0 (new) = DCS#0 (old) + delta
  145. cadd gr21,gr16,gr21, cc5,#1
  146. cadd.p gr22,gr16,gr22, cc6,#1
  147. cadd gr23,gr16,gr23, cc7,#1
  148. srl gr20,gr15,gr20 ; shift values down for FR551
  149. srl gr21,gr15,gr21
  150. srl gr22,gr15,gr22
  151. srl gr23,gr15,gr23
  152. # work out the address at which the reg updater resides and lock it into icache
  153. # also work out the address the updater will jump to when finished
  154. sethi.p %hi(__head_move_sdram-__head_reference),gr18
  155. setlo %lo(__head_move_sdram-__head_reference),gr18
  156. sethi.p %hi(__head_sdram_moved-__head_reference),gr19
  157. setlo %lo(__head_sdram_moved-__head_reference),gr19
  158. add.p gr18,gr26,gr18
  159. add gr19,gr26,gr19
  160. add.p gr19,gr16,gr19 ; moved = addr + (__sdram_base - DBRx)
  161. add gr18,gr5,gr4 ; two cachelines probably required
  162. icpl gr18,gr0,#1 ; load and lock the cachelines
  163. icpl gr4,gr0,#1
  164. LEDS 0x0004
  165. membar
  166. bar
  167. jmpl @(gr18,gr0)
  168. .balign L1_CACHE_BYTES
  169. __head_move_sdram:
  170. cst gr20,@(gr14,gr0 ), cc4,#1
  171. cst gr21,@(gr14,gr11), cc5,#1
  172. cst gr22,@(gr14,gr12), cc6,#1
  173. cst gr23,@(gr14,gr13), cc7,#1
  174. cld @(gr14,gr0 ),gr20, cc4,#1
  175. cld @(gr14,gr11),gr21, cc5,#1
  176. cld @(gr14,gr12),gr22, cc4,#1
  177. cld @(gr14,gr13),gr23, cc7,#1
  178. bar
  179. membar
  180. jmpl @(gr19,gr0)
  181. .balign L1_CACHE_BYTES
  182. __head_sdram_moved:
  183. icul gr18
  184. add gr18,gr5,gr4
  185. icul gr4
  186. icei @(gr0,gr0),1
  187. dcei @(gr0,gr0),1
  188. LEDS 0x0005
  189. # recalculate reference address
  190. call 0f
  191. 0: movsg lr,gr26
  192. addi gr26,#__head_reference-0b,gr26
  193. ###############################################################################
  194. #
  195. # move the kernel image down to the bottom of the SDRAM
  196. #
  197. ###############################################################################
  198. sethi.p %hi(__kernel_image_size_no_bss+15),gr4
  199. setlo %lo(__kernel_image_size_no_bss+15),gr4
  200. srli.p gr4,#4,gr4 ; count
  201. or gr26,gr26,gr16 ; source
  202. sethi.p %hi(__sdram_base),gr17 ; destination
  203. setlo %lo(__sdram_base),gr17
  204. setlos #8,gr5
  205. sub.p gr16,gr5,gr16 ; adjust src for LDDU
  206. sub gr17,gr5,gr17 ; adjust dst for LDDU
  207. sethi.p %hi(__head_move_kernel-__head_reference),gr18
  208. setlo %lo(__head_move_kernel-__head_reference),gr18
  209. sethi.p %hi(__head_kernel_moved-__head_reference+__sdram_base),gr19
  210. setlo %lo(__head_kernel_moved-__head_reference+__sdram_base),gr19
  211. add gr18,gr26,gr18
  212. icpl gr18,gr0,#1
  213. jmpl @(gr18,gr0)
  214. .balign 32
  215. __head_move_kernel:
  216. lddu @(gr16,gr5),gr10
  217. lddu @(gr16,gr5),gr12
  218. stdu.p gr10,@(gr17,gr5)
  219. subicc gr4,#1,gr4,icc0
  220. stdu.p gr12,@(gr17,gr5)
  221. bhi icc0,#0,__head_move_kernel
  222. jmpl @(gr19,gr0)
  223. .balign 32
  224. __head_kernel_moved:
  225. icul gr18
  226. icei @(gr0,gr0),1
  227. dcei @(gr0,gr0),1
  228. LEDS 0x0006
  229. # recalculate reference address
  230. call 0f
  231. 0: movsg lr,gr26
  232. addi gr26,#__head_reference-0b,gr26
  233. ###############################################################################
  234. #
  235. # rearrange the iomem map and set the protection registers
  236. #
  237. ###############################################################################
  238. #ifdef CONFIG_MMU
  239. LEDS 0x3301
  240. call __head_fr451_set_busctl
  241. LEDS 0x3303
  242. call __head_fr451_survey_sdram
  243. LEDS 0x3305
  244. call __head_fr451_set_protection
  245. #else
  246. movsg psr,gr5
  247. srli gr5,#PSR_IMPLE_SHIFT,gr5
  248. subicc gr5,#PSR_IMPLE_FR551,gr0,icc0
  249. beq icc0,#0,__head_fr555_memmap
  250. subicc gr5,#PSR_IMPLE_FR451,gr0,icc0
  251. beq icc0,#0,__head_fr451_memmap
  252. LEDS 0x3101
  253. call __head_fr401_set_busctl
  254. LEDS 0x3103
  255. call __head_fr401_survey_sdram
  256. LEDS 0x3105
  257. call __head_fr401_set_protection
  258. bra __head_done_memmap
  259. __head_fr451_memmap:
  260. LEDS 0x3301
  261. call __head_fr401_set_busctl
  262. LEDS 0x3303
  263. call __head_fr401_survey_sdram
  264. LEDS 0x3305
  265. call __head_fr451_set_protection
  266. bra __head_done_memmap
  267. __head_fr555_memmap:
  268. LEDS 0x3501
  269. call __head_fr555_set_busctl
  270. LEDS 0x3503
  271. call __head_fr555_survey_sdram
  272. LEDS 0x3505
  273. call __head_fr555_set_protection
  274. __head_done_memmap:
  275. #endif
  276. LEDS 0x0007
  277. ###############################################################################
  278. #
  279. # turn the data cache and MMU on
  280. # - for the FR451 this'll mean that the window through which the kernel is
  281. # viewed will change
  282. #
  283. ###############################################################################
  284. #ifdef CONFIG_MMU
  285. #define MMUMODE HSR0_EIMMU|HSR0_EDMMU|HSR0_EXMMU|HSR0_EDAT|HSR0_XEDAT
  286. #else
  287. #define MMUMODE HSR0_EIMMU|HSR0_EDMMU
  288. #endif
  289. movsg hsr0,gr5
  290. sethi.p %hi(MMUMODE),gr4
  291. setlo %lo(MMUMODE),gr4
  292. or gr4,gr5,gr5
  293. #if defined(CONFIG_FRV_DEFL_CACHE_WTHRU)
  294. sethi.p %hi(HSR0_DCE|HSR0_CBM_WRITE_THRU),gr4
  295. setlo %lo(HSR0_DCE|HSR0_CBM_WRITE_THRU),gr4
  296. #elif defined(CONFIG_FRV_DEFL_CACHE_WBACK)
  297. sethi.p %hi(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
  298. setlo %lo(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
  299. #elif defined(CONFIG_FRV_DEFL_CACHE_WBEHIND)
  300. sethi.p %hi(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
  301. setlo %lo(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4
  302. movsg psr,gr6
  303. srli gr6,#24,gr6
  304. cmpi gr6,#0x50,icc0 // FR451
  305. beq icc0,#0,0f
  306. cmpi gr6,#0x40,icc0 // FR405
  307. bne icc0,#0,1f
  308. 0:
  309. # turn off write-allocate
  310. sethi.p %hi(HSR0_NWA),gr6
  311. setlo %lo(HSR0_NWA),gr6
  312. or gr4,gr6,gr4
  313. 1:
  314. #else
  315. #error No default cache configuration set
  316. #endif
  317. or gr4,gr5,gr5
  318. movgs gr5,hsr0
  319. bar
  320. LEDS 0x0008
  321. sethi.p %hi(__head_mmu_enabled),gr19
  322. setlo %lo(__head_mmu_enabled),gr19
  323. jmpl @(gr19,gr0)
  324. __head_mmu_enabled:
  325. icei @(gr0,gr0),#1
  326. dcei @(gr0,gr0),#1
  327. LEDS 0x0009
  328. #ifdef CONFIG_MMU
  329. call __head_fr451_finalise_protection
  330. #endif
  331. LEDS 0x000a
  332. ###############################################################################
  333. #
  334. # set up the runtime environment
  335. #
  336. ###############################################################################
  337. # clear the BSS area
  338. sethi.p %hi(__bss_start),gr4
  339. setlo %lo(__bss_start),gr4
  340. sethi.p %hi(_end),gr5
  341. setlo %lo(_end),gr5
  342. or.p gr0,gr0,gr18
  343. or gr0,gr0,gr19
  344. 0:
  345. stdi gr18,@(gr4,#0)
  346. stdi gr18,@(gr4,#8)
  347. stdi gr18,@(gr4,#16)
  348. stdi.p gr18,@(gr4,#24)
  349. addi gr4,#24,gr4
  350. subcc gr5,gr4,gr0,icc0
  351. bhi icc0,#2,0b
  352. LEDS 0x000b
  353. # save the SDRAM details
  354. sethi.p %hi(__sdram_old_base),gr4
  355. setlo %lo(__sdram_old_base),gr4
  356. st gr24,@(gr4,gr0)
  357. sethi.p %hi(__sdram_base),gr5
  358. setlo %lo(__sdram_base),gr5
  359. sethi.p %hi(memory_start),gr4
  360. setlo %lo(memory_start),gr4
  361. st gr5,@(gr4,gr0)
  362. add gr25,gr5,gr25
  363. sethi.p %hi(memory_end),gr4
  364. setlo %lo(memory_end),gr4
  365. st gr25,@(gr4,gr0)
  366. # point the TBR at the kernel trap table
  367. sethi.p %hi(__entry_kerneltrap_table),gr4
  368. setlo %lo(__entry_kerneltrap_table),gr4
  369. movgs gr4,tbr
  370. # set up the exception frame for init
  371. sethi.p %hi(__kernel_frame0_ptr),gr28
  372. setlo %lo(__kernel_frame0_ptr),gr28
  373. sethi.p %hi(_gp),gr16
  374. setlo %lo(_gp),gr16
  375. sethi.p %hi(__entry_usertrap_table),gr4
  376. setlo %lo(__entry_usertrap_table),gr4
  377. lddi @(gr28,#0),gr28 ; load __frame & current
  378. ldi.p @(gr29,#4),gr15 ; set current_thread
  379. or gr0,gr0,fp
  380. or gr28,gr0,sp
  381. sti.p gr4,@(gr28,REG_TBR)
  382. setlos #ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5
  383. movgs gr5,isr
  384. # turn on and off various CPU services
  385. movsg psr,gr22
  386. sethi.p %hi(#PSR_EM|PSR_EF|PSR_CM|PSR_NEM),gr4
  387. setlo %lo(#PSR_EM|PSR_EF|PSR_CM|PSR_NEM),gr4
  388. or gr22,gr4,gr22
  389. movgs gr22,psr
  390. andi gr22,#~(PSR_PIL|PSR_PS|PSR_S),gr22
  391. ori gr22,#PSR_ET,gr22
  392. sti gr22,@(gr28,REG_PSR)
  393. ###############################################################################
  394. #
  395. # set up the registers and jump into the kernel
  396. #
  397. ###############################################################################
  398. LEDS 0x000c
  399. sethi.p #0xe5e5,gr3
  400. setlo #0xe5e5,gr3
  401. or.p gr3,gr0,gr4
  402. or gr3,gr0,gr5
  403. or.p gr3,gr0,gr6
  404. or gr3,gr0,gr7
  405. or.p gr3,gr0,gr8
  406. or gr3,gr0,gr9
  407. or.p gr3,gr0,gr10
  408. or gr3,gr0,gr11
  409. or.p gr3,gr0,gr12
  410. or gr3,gr0,gr13
  411. or.p gr3,gr0,gr14
  412. or gr3,gr0,gr17
  413. or.p gr3,gr0,gr18
  414. or gr3,gr0,gr19
  415. or.p gr3,gr0,gr20
  416. or gr3,gr0,gr21
  417. or.p gr3,gr0,gr23
  418. or gr3,gr0,gr24
  419. or.p gr3,gr0,gr25
  420. or gr3,gr0,gr26
  421. or.p gr3,gr0,gr27
  422. # or gr3,gr0,gr30
  423. or gr3,gr0,gr31
  424. movgs gr0,lr
  425. movgs gr0,lcr
  426. movgs gr0,ccr
  427. movgs gr0,cccr
  428. # initialise the virtual interrupt handling
  429. subcc gr0,gr0,gr0,icc2 /* set Z, clear C */
  430. #ifdef CONFIG_MMU
  431. movgs gr3,scr2
  432. movgs gr3,scr3
  433. #endif
  434. LEDS 0x0fff
  435. # invoke the debugging stub if present
  436. # - arch/frv/kernel/debug-stub.c will shift control directly to init/main.c
  437. # (it will not return here)
  438. break
  439. .globl __debug_stub_init_break
  440. __debug_stub_init_break:
  441. # however, if you need to use an ICE, and don't care about using any userspace
  442. # debugging tools (such as the ptrace syscall), you can just step over the break
  443. # above and get to the kernel this way
  444. # look at arch/frv/kernel/debug-stub.c: debug_stub_init() to see what you've missed
  445. call start_kernel
  446. .globl __head_end
  447. __head_end:
  448. .size _boot, .-_boot
  449. # provide a point for GDB to place a break
  450. .section .text..start,"ax"
  451. .globl _start
  452. .balign 4
  453. _start:
  454. call _boot
  455. .previous
  456. ###############################################################################
  457. #
  458. # split a tile off of the region defined by GR8-GR9
  459. #
  460. # ENTRY: EXIT:
  461. # GR4 - IAMPR value representing tile
  462. # GR5 - DAMPR value representing tile
  463. # GR6 - IAMLR value representing tile
  464. # GR7 - DAMLR value representing tile
  465. # GR8 region base pointer [saved]
  466. # GR9 region top pointer updated to exclude new tile
  467. # GR11 xAMLR mask [saved]
  468. # GR25 SDRAM size [saved]
  469. # GR30 LED address [saved]
  470. #
  471. # - GR8 and GR9 should be rounded up/down to the nearest megabyte before calling
  472. #
  473. ###############################################################################
  474. .globl __head_split_region
  475. .type __head_split_region,@function
  476. __head_split_region:
  477. subcc.p gr9,gr8,gr4,icc0
  478. setlos #31,gr5
  479. scan.p gr4,gr0,gr6
  480. beq icc0,#0,__head_region_empty
  481. sub.p gr5,gr6,gr6 ; bit number of highest set bit (1MB=>20)
  482. setlos #1,gr4
  483. sll.p gr4,gr6,gr4 ; size of region (1 << bitno)
  484. subi gr6,#17,gr6 ; 1MB => 0x03
  485. slli.p gr6,#4,gr6 ; 1MB => 0x30
  486. sub gr9,gr4,gr9 ; move uncovered top down
  487. or gr9,gr6,gr4
  488. ori gr4,#xAMPRx_S_USER|xAMPRx_C_CACHED|xAMPRx_V,gr4
  489. or.p gr4,gr0,gr5
  490. and gr4,gr11,gr6
  491. and.p gr5,gr11,gr7
  492. bralr
  493. __head_region_empty:
  494. or.p gr0,gr0,gr4
  495. or gr0,gr0,gr5
  496. or.p gr0,gr0,gr6
  497. or gr0,gr0,gr7
  498. bralr
  499. .size __head_split_region, .-__head_split_region
  500. ###############################################################################
  501. #
  502. # write the 32-bit hex number in GR8 to ttyS0
  503. #
  504. ###############################################################################
  505. #if 0
  506. .globl __head_write_to_ttyS0
  507. .type __head_write_to_ttyS0,@function
  508. __head_write_to_ttyS0:
  509. sethi.p %hi(0xfeff9c00),gr31
  510. setlo %lo(0xfeff9c00),gr31
  511. setlos #8,gr20
  512. 0: ldubi @(gr31,#5*8),gr21
  513. andi gr21,#0x60,gr21
  514. subicc gr21,#0x60,gr21,icc0
  515. bne icc0,#0,0b
  516. 1: srli gr8,#28,gr21
  517. slli gr8,#4,gr8
  518. addi gr21,#'0',gr21
  519. subicc gr21,#'9',gr0,icc0
  520. bls icc0,#2,2f
  521. addi gr21,#'A'-'0'-10,gr21
  522. 2:
  523. stbi gr21,@(gr31,#0*8)
  524. subicc gr20,#1,gr20,icc0
  525. bhi icc0,#2,1b
  526. setlos #'\r',gr21
  527. stbi gr21,@(gr31,#0*8)
  528. setlos #'\n',gr21
  529. stbi gr21,@(gr31,#0*8)
  530. 3: ldubi @(gr31,#5*8),gr21
  531. andi gr21,#0x60,gr21
  532. subicc gr21,#0x60,gr21,icc0
  533. bne icc0,#0,3b
  534. bralr
  535. .size __head_write_to_ttyS0, .-__head_write_to_ttyS0
  536. #endif