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