1/* 2 * Copyright 2013 Red Hat Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Ben Skeggs 23 */ 24 25/****************************************************************************** 26 * kernel data segment 27 *****************************************************************************/ 28#ifdef INCLUDE_PROC 29proc_kern: 30process(PROC_KERN, 0, 0) 31proc_list_head: 32#endif 33 34#ifdef INCLUDE_DATA 35proc_list_tail: 36time_prev: .b32 0 37time_next: .b32 0 38#endif 39 40/****************************************************************************** 41 * kernel code segment 42 *****************************************************************************/ 43#ifdef INCLUDE_CODE 44 bra #init 45 46// read nv register 47// 48// $r15 - current 49// $r14 - addr 50// $r13 - data (return) 51// $r0 - zero 52rd32: 53 nv_iowr(NV_PPWR_MMIO_ADDR, $r14) 54 mov $r13 NV_PPWR_MMIO_CTRL_OP_RD 55 sethi $r13 NV_PPWR_MMIO_CTRL_TRIGGER 56 nv_iowr(NV_PPWR_MMIO_CTRL, $r13) 57 rd32_wait: 58 nv_iord($r13, NV_PPWR_MMIO_CTRL) 59 and $r13 NV_PPWR_MMIO_CTRL_STATUS 60 bra nz #rd32_wait 61 nv_iord($r13, NV_PPWR_MMIO_DATA) 62 ret 63 64// write nv register 65// 66// $r15 - current 67// $r14 - addr 68// $r13 - data 69// $r0 - zero 70wr32: 71 nv_iowr(NV_PPWR_MMIO_ADDR, $r14) 72 nv_iowr(NV_PPWR_MMIO_DATA, $r13) 73 mov $r13 NV_PPWR_MMIO_CTRL_OP_WR 74 or $r13 NV_PPWR_MMIO_CTRL_MASK_B32_0 75 sethi $r13 NV_PPWR_MMIO_CTRL_TRIGGER 76 77#ifdef NVKM_FALCON_MMIO_TRAP 78 push $r13 79 mov $r13 NV_PPWR_INTR_TRIGGER_USER1 80 nv_iowr(NV_PPWR_INTR_TRIGGER, $r13) 81 wr32_host: 82 nv_iord($r13, NV_PPWR_INTR) 83 and $r13 NV_PPWR_INTR_USER1 84 bra nz #wr32_host 85 pop $r13 86#endif 87 88 nv_iowr(NV_PPWR_MMIO_CTRL, $r13) 89 wr32_wait: 90 nv_iord($r13, NV_PPWR_MMIO_CTRL) 91 and $r13 NV_PPWR_MMIO_CTRL_STATUS 92 bra nz #wr32_wait 93 ret 94 95// busy-wait for a period of time 96// 97// $r15 - current 98// $r14 - ns 99// $r0 - zero 100nsec: 101 push $r9 102 push $r8 103 nv_iord($r8, NV_PPWR_TIMER_LOW) 104 nsec_loop: 105 nv_iord($r9, NV_PPWR_TIMER_LOW) 106 sub b32 $r9 $r8 107 cmp b32 $r9 $r14 108 bra l #nsec_loop 109 pop $r8 110 pop $r9 111 ret 112 113// busy-wait for a period of time 114// 115// $r15 - current 116// $r14 - addr 117// $r13 - mask 118// $r12 - data 119// $r11 - timeout (ns) 120// $r0 - zero 121wait: 122 push $r9 123 push $r8 124 nv_iord($r8, NV_PPWR_TIMER_LOW) 125 wait_loop: 126 nv_rd32($r10, $r14) 127 and $r10 $r13 128 cmp b32 $r10 $r12 129 bra e #wait_done 130 nv_iord($r9, NV_PPWR_TIMER_LOW) 131 sub b32 $r9 $r8 132 cmp b32 $r9 $r11 133 bra l #wait_loop 134 wait_done: 135 pop $r8 136 pop $r9 137 ret 138 139// $r15 - current (kern) 140// $r14 - process 141// $r8 - NV_PPWR_INTR 142intr_watchdog: 143 // read process' timer status, skip if not enabled 144 ld b32 $r9 D[$r14 + #proc_time] 145 cmp b32 $r9 0 146 bra z #intr_watchdog_next_proc 147 148 // subtract last timer's value from process' timer, 149 // if it's <= 0 then the timer has expired 150 ld b32 $r10 D[$r0 + #time_prev] 151 sub b32 $r9 $r10 152 bra g #intr_watchdog_next_time 153 mov $r13 KMSG_ALARM 154 call(send_proc) 155 clear b32 $r9 156 bra #intr_watchdog_next_proc 157 158 // otherwise, update the next timer's value if this 159 // process' timer is the soonest 160 intr_watchdog_next_time: 161 // ... or if there's no next timer yet 162 ld b32 $r10 D[$r0 + #time_next] 163 cmp b32 $r10 0 164 bra z #intr_watchdog_next_time_set 165 166 cmp b32 $r9 $r10 167 bra g #intr_watchdog_next_proc 168 intr_watchdog_next_time_set: 169 st b32 D[$r0 + #time_next] $r9 170 171 // update process' timer status, and advance 172 intr_watchdog_next_proc: 173 st b32 D[$r14 + #proc_time] $r9 174 add b32 $r14 #proc_size 175 cmp b32 $r14 #proc_list_tail 176 bra ne #intr_watchdog 177 ret 178 179intr: 180 push $r0 181 clear b32 $r0 182 push $r8 183 push $r9 184 push $r10 185 push $r11 186 push $r12 187 push $r13 188 push $r14 189 push $r15 190 mov $r15 #proc_kern 191 mov $r8 $flags 192 push $r8 193 194 nv_iord($r8, NV_PPWR_DSCRATCH(0)) 195 add b32 $r8 1 196 nv_iowr(NV_PPWR_DSCRATCH(0), $r8) 197 198 nv_iord($r8, NV_PPWR_INTR) 199 and $r9 $r8 NV_PPWR_INTR_WATCHDOG 200 bra z #intr_skip_watchdog 201 st b32 D[$r0 + #time_next] $r0 202 mov $r14 #proc_list_head 203 call(intr_watchdog) 204 ld b32 $r9 D[$r0 + #time_next] 205 cmp b32 $r9 0 206 bra z #intr_skip_watchdog 207 nv_iowr(NV_PPWR_WATCHDOG_TIME, $r9) 208 st b32 D[$r0 + #time_prev] $r9 209 210 intr_skip_watchdog: 211 and $r9 $r8 NV_PPWR_INTR_SUBINTR 212 bra z #intr_skip_subintr 213 nv_iord($r9, NV_PPWR_SUBINTR) 214 and $r10 $r9 NV_PPWR_SUBINTR_FIFO 215 bra z #intr_subintr_skip_fifo 216 nv_iord($r12, NV_PPWR_FIFO_INTR) 217 push $r12 218 mov $r14 (PROC_HOST & 0x0000ffff) 219 sethi $r14 (PROC_HOST & 0xffff0000) 220 mov $r13 KMSG_FIFO 221 call(send) 222 pop $r12 223 nv_iowr(NV_PPWR_FIFO_INTR, $r12) 224 intr_subintr_skip_fifo: 225 nv_iowr(NV_PPWR_SUBINTR, $r9) 226 227 intr_skip_subintr: 228 and $r9 $r8 NV_PPWR_INTR_PAUSE 229 bra z #intr_skip_pause 230 and $r10 0xffbf 231 232 intr_skip_pause: 233 and $r9 $r8 NV_PPWR_INTR_USER0 234 bra z #intr_skip_user0 235 and $r10 0xffbf 236 237 intr_skip_user0: 238 nv_iowr(NV_PPWR_INTR_ACK, $r8) 239 pop $r8 240 mov $flags $r8 241 pop $r15 242 pop $r14 243 pop $r13 244 pop $r12 245 pop $r11 246 pop $r10 247 pop $r9 248 pop $r8 249 pop $r0 250 bclr $flags $p0 251 iret 252 253// calculate the number of ticks in the specified nanoseconds delay 254// 255// $r15 - current 256// $r14 - ns 257// $r14 - ticks (return) 258// $r0 - zero 259ticks_from_ns: 260 push $r12 261 push $r11 262 263 /* try not losing precision (multiply then divide) */ 264 imm32($r13, HW_TICKS_PER_US) 265 call #mulu32_32_64 266 267 /* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */ 268 div $r12 $r12 1000 269 270 /* check if there wasn't any overflow */ 271 cmpu b32 $r11 0 272 bra e #ticks_from_ns_quit 273 274 /* let's divide then multiply, too bad for the precision! */ 275 div $r14 $r14 1000 276 imm32($r13, HW_TICKS_PER_US) 277 call #mulu32_32_64 278 279 /* this cannot overflow as long as HW_TICKS_PER_US < 1000 */ 280 281ticks_from_ns_quit: 282 mov b32 $r14 $r12 283 pop $r11 284 pop $r12 285 ret 286 287// calculate the number of ticks in the specified microsecond delay 288// 289// $r15 - current 290// $r14 - us 291// $r14 - ticks (return) 292// $r0 - zero 293ticks_from_us: 294 push $r12 295 push $r11 296 297 /* simply multiply $us by HW_TICKS_PER_US */ 298 imm32($r13, HW_TICKS_PER_US) 299 call #mulu32_32_64 300 mov b32 $r14 $r12 301 302 /* check if there wasn't any overflow */ 303 cmpu b32 $r11 0 304 bra e #ticks_from_us_quit 305 306 /* Overflow! */ 307 clear b32 $r14 308 309ticks_from_us_quit: 310 pop $r11 311 pop $r12 312 ret 313 314// calculate the number of ticks in the specified microsecond delay 315// 316// $r15 - current 317// $r14 - ticks 318// $r14 - us (return) 319// $r0 - zero 320ticks_to_us: 321 /* simply divide $ticks by HW_TICKS_PER_US */ 322 imm32($r13, HW_TICKS_PER_US) 323 div $r14 $r14 $r13 324 325 ret 326 327// request the current process be sent a message after a timeout expires 328// 329// $r15 - current 330// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow) 331// $r0 - zero 332timer: 333 push $r9 334 push $r8 335 336 // interrupts off to prevent racing with timer isr 337 bclr $flags ie0 338 339 // if current process already has a timer set, bail 340 ld b32 $r8 D[$r15 + #proc_time] 341 cmp b32 $r8 0 342 bra g #timer_done 343 344 // halt watchdog timer temporarily 345 clear b32 $r8 346 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8) 347 348 // find out how much time elapsed since the last update 349 // of the watchdog and add this time to the wanted ticks 350 nv_iord($r8, NV_PPWR_WATCHDOG_TIME) 351 ld b32 $r9 D[$r0 + #time_prev] 352 sub b32 $r9 $r8 353 add b32 $r14 $r9 354 st b32 D[$r15 + #proc_time] $r14 355 356 // check for a pending interrupt. if there's one already 357 // pending, we can just bail since the timer isr will 358 // queue the next soonest right after it's done 359 nv_iord($r8, NV_PPWR_INTR) 360 and $r8 NV_PPWR_INTR_WATCHDOG 361 bra nz #timer_enable 362 363 // update the watchdog if this timer should expire first, 364 // or if there's no timeout already set 365 nv_iord($r8, NV_PPWR_WATCHDOG_TIME) 366 cmp b32 $r14 $r0 367 bra e #timer_reset 368 cmp b32 $r14 $r8 369 bra g #timer_enable 370 timer_reset: 371 nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14) 372 st b32 D[$r0 + #time_prev] $r14 373 374 // re-enable the watchdog timer 375 timer_enable: 376 mov $r8 1 377 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8) 378 379 // interrupts back on 380 timer_done: 381 bset $flags ie0 382 383 pop $r8 384 pop $r9 385 ret 386 387// send message to another process 388// 389// $r15 - current 390// $r14 - process 391// $r13 - message 392// $r12 - message data 0 393// $r11 - message data 1 394// $r0 - zero 395send_proc: 396 push $r8 397 push $r9 398 // check for space in queue 399 ld b32 $r8 D[$r14 + #proc_qget] 400 ld b32 $r9 D[$r14 + #proc_qput] 401 xor $r8 #proc_qmaskb 402 cmp b32 $r8 $r9 403 bra e #send_done 404 405 // enqueue message 406 and $r8 $r9 #proc_qmaskp 407 shl b32 $r8 $r8 #proc_qlen 408 add b32 $r8 #proc_queue 409 add b32 $r8 $r14 410 411 ld b32 $r10 D[$r15 + #proc_id] 412 st b32 D[$r8 + #msg_process] $r10 413 st b32 D[$r8 + #msg_message] $r13 414 st b32 D[$r8 + #msg_data0] $r12 415 st b32 D[$r8 + #msg_data1] $r11 416 417 // increment PUT 418 add b32 $r9 1 419 and $r9 #proc_qmaskf 420 st b32 D[$r14 + #proc_qput] $r9 421 bset $flags $p2 422 send_done: 423 pop $r9 424 pop $r8 425 ret 426 427// lookup process structure by its name 428// 429// $r15 - current 430// $r14 - process name 431// $r0 - zero 432// 433// $r14 - process 434// $p1 - success 435find: 436 push $r8 437 mov $r8 #proc_list_head 438 bset $flags $p1 439 find_loop: 440 ld b32 $r10 D[$r8 + #proc_id] 441 cmp b32 $r10 $r14 442 bra e #find_done 443 add b32 $r8 #proc_size 444 cmp b32 $r8 #proc_list_tail 445 bra ne #find_loop 446 bclr $flags $p1 447 find_done: 448 mov b32 $r14 $r8 449 pop $r8 450 ret 451 452// send message to another process 453// 454// $r15 - current 455// $r14 - process id 456// $r13 - message 457// $r12 - message data 0 458// $r11 - message data 1 459// $r0 - zero 460send: 461 call(find) 462 bra $p1 #send_proc 463 ret 464 465// process single message for a given process 466// 467// $r15 - current 468// $r14 - process 469// $r0 - zero 470recv: 471 push $r9 472 push $r8 473 474 ld b32 $r8 D[$r14 + #proc_qget] 475 ld b32 $r9 D[$r14 + #proc_qput] 476 bclr $flags $p1 477 cmp b32 $r8 $r9 478 bra e #recv_done 479 // dequeue message 480 and $r9 $r8 #proc_qmaskp 481 add b32 $r8 1 482 and $r8 #proc_qmaskf 483 st b32 D[$r14 + #proc_qget] $r8 484 ld b32 $r10 D[$r14 + #proc_recv] 485 486 push $r15 487 mov $r15 $flags 488 push $r15 489 mov b32 $r15 $r14 490 491 shl b32 $r9 $r9 #proc_qlen 492 add b32 $r14 $r9 493 add b32 $r14 #proc_queue 494 ld b32 $r11 D[$r14 + #msg_data1] 495 ld b32 $r12 D[$r14 + #msg_data0] 496 ld b32 $r13 D[$r14 + #msg_message] 497 ld b32 $r14 D[$r14 + #msg_process] 498 499 // process it 500 call $r10 501 pop $r15 502 mov $flags $r15 503 bset $flags $p1 504 pop $r15 505 recv_done: 506 pop $r8 507 pop $r9 508 ret 509 510init: 511 // setup stack 512 nv_iord($r1, NV_PPWR_CAPS) 513 extr $r1 $r1 9:17 514 shl b32 $r1 8 515 mov $sp $r1 516 517#ifdef NVKM_FALCON_MMIO_UAS 518 // somehow allows the magic "access mmio via D[]" stuff that's 519 // used by the nv_rd32/nv_wr32 macros to work 520 mov $r1 0x0010 521 sethi $r1 NV_PPWR_UAS_CONFIG_ENABLE 522 nv_iowrs(NV_PPWR_UAS_CONFIG, $r1) 523#endif 524 525 // route all interrupts except user0/1 and pause to fuc 526 mov $r1 0x00e0 527 sethi $r1 0x00000000 528 nv_iowr(NV_PPWR_INTR_ROUTE, $r1) 529 530 // enable watchdog and subintr intrs 531 mov $r1 NV_PPWR_INTR_EN_CLR_MASK 532 nv_iowr(NV_PPWR_INTR_EN_CLR, $r1) 533 mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG 534 or $r1 NV_PPWR_INTR_EN_SET_SUBINTR 535 nv_iowr(NV_PPWR_INTR_EN_SET, $r1) 536 537 // enable interrupts globally 538 mov $r1 #intr 539 sethi $r1 0x00000000 540 mov $iv0 $r1 541 bset $flags ie0 542 543 // enable watchdog timer 544 mov $r1 1 545 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1) 546 547 // bootstrap processes, idle process will be last, and not return 548 mov $r15 #proc_list_head 549 init_proc: 550 ld b32 $r1 D[$r15 + #proc_init] 551 cmp b32 $r1 0 552 bra z #init_proc 553 call $r1 554 add b32 $r15 #proc_size 555 bra #init_proc 556#endif 557