head.S 15 KB

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