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 mov $r9 (NV_PPWR_INTR_USER0 | NV_PPWR_INTR_USER1 | NV_PPWR_INTR_PAUSE) 229 not b32 $r9 230 and $r8 $r9 231 nv_iowr(NV_PPWR_INTR_ACK, $r8) 232 233 pop $r8 234 mov $flags $r8 235 pop $r15 236 pop $r14 237 pop $r13 238 pop $r12 239 pop $r11 240 pop $r10 241 pop $r9 242 pop $r8 243 pop $r0 244 bclr $flags $p0 245 iret 246 247// calculate the number of ticks in the specified nanoseconds delay 248// 249// $r15 - current 250// $r14 - ns 251// $r14 - ticks (return) 252// $r0 - zero 253ticks_from_ns: 254 push $r12 255 push $r11 256 257 /* try not losing precision (multiply then divide) */ 258 imm32($r13, HW_TICKS_PER_US) 259 call #mulu32_32_64 260 261 /* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */ 262 div $r12 $r12 1000 263 264 /* check if there wasn't any overflow */ 265 cmpu b32 $r11 0 266 bra e #ticks_from_ns_quit 267 268 /* let's divide then multiply, too bad for the precision! */ 269 div $r14 $r14 1000 270 imm32($r13, HW_TICKS_PER_US) 271 call #mulu32_32_64 272 273 /* this cannot overflow as long as HW_TICKS_PER_US < 1000 */ 274 275ticks_from_ns_quit: 276 mov b32 $r14 $r12 277 pop $r11 278 pop $r12 279 ret 280 281// calculate the number of ticks in the specified microsecond delay 282// 283// $r15 - current 284// $r14 - us 285// $r14 - ticks (return) 286// $r0 - zero 287ticks_from_us: 288 push $r12 289 push $r11 290 291 /* simply multiply $us by HW_TICKS_PER_US */ 292 imm32($r13, HW_TICKS_PER_US) 293 call #mulu32_32_64 294 mov b32 $r14 $r12 295 296 /* check if there wasn't any overflow */ 297 cmpu b32 $r11 0 298 bra e #ticks_from_us_quit 299 300 /* Overflow! */ 301 clear b32 $r14 302 303ticks_from_us_quit: 304 pop $r11 305 pop $r12 306 ret 307 308// calculate the number of ticks in the specified microsecond delay 309// 310// $r15 - current 311// $r14 - ticks 312// $r14 - us (return) 313// $r0 - zero 314ticks_to_us: 315 /* simply divide $ticks by HW_TICKS_PER_US */ 316 imm32($r13, HW_TICKS_PER_US) 317 div $r14 $r14 $r13 318 319 ret 320 321// request the current process be sent a message after a timeout expires 322// 323// $r15 - current 324// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow) 325// $r0 - zero 326timer: 327 push $r9 328 push $r8 329 330 // interrupts off to prevent racing with timer isr 331 bclr $flags ie0 332 333 // if current process already has a timer set, bail 334 ld b32 $r8 D[$r15 + #proc_time] 335 cmp b32 $r8 0 336 bra g #timer_done 337 338 // halt watchdog timer temporarily 339 clear b32 $r8 340 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8) 341 342 // find out how much time elapsed since the last update 343 // of the watchdog and add this time to the wanted ticks 344 nv_iord($r8, NV_PPWR_WATCHDOG_TIME) 345 ld b32 $r9 D[$r0 + #time_prev] 346 sub b32 $r9 $r8 347 add b32 $r14 $r9 348 st b32 D[$r15 + #proc_time] $r14 349 350 // check for a pending interrupt. if there's one already 351 // pending, we can just bail since the timer isr will 352 // queue the next soonest right after it's done 353 nv_iord($r8, NV_PPWR_INTR) 354 and $r8 NV_PPWR_INTR_WATCHDOG 355 bra nz #timer_enable 356 357 // update the watchdog if this timer should expire first, 358 // or if there's no timeout already set 359 nv_iord($r8, NV_PPWR_WATCHDOG_TIME) 360 cmp b32 $r14 $r0 361 bra e #timer_reset 362 cmp b32 $r14 $r8 363 bra g #timer_enable 364 timer_reset: 365 nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14) 366 st b32 D[$r0 + #time_prev] $r14 367 368 // re-enable the watchdog timer 369 timer_enable: 370 mov $r8 1 371 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8) 372 373 // interrupts back on 374 timer_done: 375 bset $flags ie0 376 377 pop $r8 378 pop $r9 379 ret 380 381// send message to another process 382// 383// $r15 - current 384// $r14 - process 385// $r13 - message 386// $r12 - message data 0 387// $r11 - message data 1 388// $r0 - zero 389send_proc: 390 push $r8 391 push $r9 392 // check for space in queue 393 ld b32 $r8 D[$r14 + #proc_qget] 394 ld b32 $r9 D[$r14 + #proc_qput] 395 xor $r8 #proc_qmaskb 396 cmp b32 $r8 $r9 397 bra e #send_done 398 399 // enqueue message 400 and $r8 $r9 #proc_qmaskp 401 shl b32 $r8 $r8 #proc_qlen 402 add b32 $r8 #proc_queue 403 add b32 $r8 $r14 404 405 ld b32 $r10 D[$r15 + #proc_id] 406 st b32 D[$r8 + #msg_process] $r10 407 st b32 D[$r8 + #msg_message] $r13 408 st b32 D[$r8 + #msg_data0] $r12 409 st b32 D[$r8 + #msg_data1] $r11 410 411 // increment PUT 412 add b32 $r9 1 413 and $r9 #proc_qmaskf 414 st b32 D[$r14 + #proc_qput] $r9 415 bset $flags $p2 416 send_done: 417 pop $r9 418 pop $r8 419 ret 420 421// lookup process structure by its name 422// 423// $r15 - current 424// $r14 - process name 425// $r0 - zero 426// 427// $r14 - process 428// $p1 - success 429find: 430 push $r8 431 mov $r8 #proc_list_head 432 bset $flags $p1 433 find_loop: 434 ld b32 $r10 D[$r8 + #proc_id] 435 cmp b32 $r10 $r14 436 bra e #find_done 437 add b32 $r8 #proc_size 438 cmp b32 $r8 #proc_list_tail 439 bra ne #find_loop 440 bclr $flags $p1 441 find_done: 442 mov b32 $r14 $r8 443 pop $r8 444 ret 445 446// send message to another process 447// 448// $r15 - current 449// $r14 - process id 450// $r13 - message 451// $r12 - message data 0 452// $r11 - message data 1 453// $r0 - zero 454send: 455 call(find) 456 bra $p1 #send_proc 457 ret 458 459// process single message for a given process 460// 461// $r15 - current 462// $r14 - process 463// $r0 - zero 464recv: 465 push $r9 466 push $r8 467 468 ld b32 $r8 D[$r14 + #proc_qget] 469 ld b32 $r9 D[$r14 + #proc_qput] 470 bclr $flags $p1 471 cmp b32 $r8 $r9 472 bra e #recv_done 473 // dequeue message 474 and $r9 $r8 #proc_qmaskp 475 add b32 $r8 1 476 and $r8 #proc_qmaskf 477 st b32 D[$r14 + #proc_qget] $r8 478 ld b32 $r10 D[$r14 + #proc_recv] 479 480 push $r15 481 mov $r15 $flags 482 push $r15 483 mov b32 $r15 $r14 484 485 shl b32 $r9 $r9 #proc_qlen 486 add b32 $r14 $r9 487 add b32 $r14 #proc_queue 488 ld b32 $r11 D[$r14 + #msg_data1] 489 ld b32 $r12 D[$r14 + #msg_data0] 490 ld b32 $r13 D[$r14 + #msg_message] 491 ld b32 $r14 D[$r14 + #msg_process] 492 493 // process it 494 call $r10 495 pop $r15 496 mov $flags $r15 497 bset $flags $p1 498 pop $r15 499 recv_done: 500 pop $r8 501 pop $r9 502 ret 503 504init: 505 // setup stack 506 nv_iord($r1, NV_PPWR_CAPS) 507 extr $r1 $r1 9:17 508 shl b32 $r1 8 509 mov $sp $r1 510 511#ifdef NVKM_FALCON_MMIO_UAS 512 // somehow allows the magic "access mmio via D[]" stuff that's 513 // used by the nv_rd32/nv_wr32 macros to work 514 mov $r1 0x0010 515 sethi $r1 NV_PPWR_UAS_CONFIG_ENABLE 516 nv_iowrs(NV_PPWR_UAS_CONFIG, $r1) 517#endif 518 519 // route all interrupts except user0/1 and pause to fuc 520 mov $r1 0x00e0 521 sethi $r1 0x00000000 522 nv_iowr(NV_PPWR_INTR_ROUTE, $r1) 523 524 // enable watchdog and subintr intrs 525 mov $r1 NV_PPWR_INTR_EN_CLR_MASK 526 nv_iowr(NV_PPWR_INTR_EN_CLR, $r1) 527 mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG 528 or $r1 NV_PPWR_INTR_EN_SET_SUBINTR 529 nv_iowr(NV_PPWR_INTR_EN_SET, $r1) 530 531 // enable interrupts globally 532 mov $r1 #intr 533 sethi $r1 0x00000000 534 mov $iv0 $r1 535 bset $flags ie0 536 537 // enable watchdog timer 538 mov $r1 1 539 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1) 540 541 // bootstrap processes, idle process will be last, and not return 542 mov $r15 #proc_list_head 543 init_proc: 544 ld b32 $r1 D[$r15 + #proc_init] 545 cmp b32 $r1 0 546 bra z #init_proc 547 call $r1 548 add b32 $r15 #proc_size 549 bra #init_proc 550#endif 551