545 lines · plain
1/*2 * Copyright 2013 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * 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 OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 * Authors: Ben Skeggs23 */24 25/******************************************************************************26 * kernel data segment27 *****************************************************************************/28#ifdef INCLUDE_PROC29proc_kern:30process(PROC_KERN, 0, 0)31proc_list_head:32#endif33 34#ifdef INCLUDE_DATA35proc_list_tail:36time_prev: .b32 037time_next: .b32 038#endif39 40/******************************************************************************41 * kernel code segment42 *****************************************************************************/43#ifdef INCLUDE_CODE44 bra #init45 46// read nv register47//48// $r15 - current49// $r14 - addr50// $r13 - data (return)51// $r0 - zero52rd32: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_STATUS59 bra nz #rd32_wait60 nv_iord($r13, NV_PPWR_MMIO_DATA)61 ret62 63// write nv register64//65// $r15 - current66// $r14 - addr67// $r13 - data68// $r0 - zero69wr32: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_TRAP75 push $r1376 mov $r13 NV_PPWR_INTR_TRIGGER_USER177 nv_iowr(NV_PPWR_INTR_TRIGGER, $r13)78 wr32_host:79 nv_iord($r13, NV_PPWR_INTR)80 and $r13 NV_PPWR_INTR_USER181 bra nz #wr32_host82 pop $r1383#endif84 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_STATUS89 bra nz #wr32_wait90 ret91 92// busy-wait for a period of time93//94// $r15 - current95// $r14 - ns96// $r0 - zero97nsec:98 push $r999 push $r8100 nv_iord($r8, NV_PPWR_TIMER_LOW)101 nsec_loop:102 nv_iord($r9, NV_PPWR_TIMER_LOW)103 sub b32 $r9 $r8104 cmp b32 $r9 $r14105 bra l #nsec_loop106 pop $r8107 pop $r9108 ret109 110// busy-wait for a period of time111//112// $r15 - current113// $r14 - addr114// $r13 - mask115// $r12 - data116// $r11 - timeout (ns)117// $r0 - zero118wait:119 push $r9120 push $r8121 nv_iord($r8, NV_PPWR_TIMER_LOW)122 wait_loop:123 nv_rd32($r10, $r14)124 and $r10 $r13125 cmp b32 $r10 $r12126 bra e #wait_done127 nv_iord($r9, NV_PPWR_TIMER_LOW)128 sub b32 $r9 $r8129 cmp b32 $r9 $r11130 bra l #wait_loop131 wait_done:132 pop $r8133 pop $r9134 ret135 136// $r15 - current (kern)137// $r14 - process138// $r8 - NV_PPWR_INTR139intr_watchdog:140 // read process' timer status, skip if not enabled141 ld b32 $r9 D[$r14 + #proc_time]142 cmp b32 $r9 0143 bra z #intr_watchdog_next_proc144 145 // subtract last timer's value from process' timer,146 // if it's <= 0 then the timer has expired147 ld b32 $r10 D[$r0 + #time_prev]148 sub b32 $r9 $r10149 bra g #intr_watchdog_next_time150 mov $r13 KMSG_ALARM151 call(send_proc)152 clear b32 $r9153 bra #intr_watchdog_next_proc154 155 // otherwise, update the next timer's value if this156 // process' timer is the soonest157 intr_watchdog_next_time:158 // ... or if there's no next timer yet159 ld b32 $r10 D[$r0 + #time_next]160 cmp b32 $r10 0161 bra z #intr_watchdog_next_time_set162 163 cmp b32 $r9 $r10164 bra g #intr_watchdog_next_proc165 intr_watchdog_next_time_set:166 st b32 D[$r0 + #time_next] $r9167 168 // update process' timer status, and advance169 intr_watchdog_next_proc:170 st b32 D[$r14 + #proc_time] $r9171 add b32 $r14 #proc_size172 cmp b32 $r14 #proc_list_tail173 bra ne #intr_watchdog174 ret175 176intr:177 push $r0178 clear b32 $r0179 push $r8180 push $r9181 push $r10182 push $r11183 push $r12184 push $r13185 push $r14186 push $r15187 mov $r15 #proc_kern188 mov $r8 $flags189 push $r8190 191 nv_iord($r8, NV_PPWR_DSCRATCH(0))192 add b32 $r8 1193 nv_iowr(NV_PPWR_DSCRATCH(0), $r8)194 195 nv_iord($r8, NV_PPWR_INTR)196 and $r9 $r8 NV_PPWR_INTR_WATCHDOG197 bra z #intr_skip_watchdog198 st b32 D[$r0 + #time_next] $r0199 mov $r14 #proc_list_head200 call(intr_watchdog)201 ld b32 $r9 D[$r0 + #time_next]202 cmp b32 $r9 0203 bra z #intr_skip_watchdog204 nv_iowr(NV_PPWR_WATCHDOG_TIME, $r9)205 st b32 D[$r0 + #time_prev] $r9206 207 intr_skip_watchdog:208 and $r9 $r8 NV_PPWR_INTR_SUBINTR209 bra z #intr_skip_subintr210 nv_iord($r9, NV_PPWR_SUBINTR)211 and $r10 $r9 NV_PPWR_SUBINTR_FIFO212 bra z #intr_subintr_skip_fifo213 nv_iord($r12, NV_PPWR_FIFO_INTR)214 push $r12215 imm32($r14, PROC_HOST)216 mov $r13 KMSG_FIFO217 call(send)218 pop $r12219 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 $r9226 and $r8 $r9227 nv_iowr(NV_PPWR_INTR_ACK, $r8)228 229 pop $r8230 mov $flags $r8231 pop $r15232 pop $r14233 pop $r13234 pop $r12235 pop $r11236 pop $r10237 pop $r9238 pop $r8239 pop $r0240 bclr $flags $p0241 iret242 243// calculate the number of ticks in the specified nanoseconds delay244//245// $r15 - current246// $r14 - ns247// $r14 - ticks (return)248// $r0 - zero249ticks_from_ns:250 push $r12251 push $r11252 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 1000259 260 /* check if there wasn't any overflow */261 cmpu b32 $r11 0262 bra e #ticks_from_ns_quit263 264 /* let's divide then multiply, too bad for the precision! */265 div $r14 $r14 1000266 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 $r12273 pop $r11274 pop $r12275 ret276 277// calculate the number of ticks in the specified microsecond delay278//279// $r15 - current280// $r14 - us281// $r14 - ticks (return)282// $r0 - zero283ticks_from_us:284 push $r12285 push $r11286 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 $r12291 292 /* check if there wasn't any overflow */293 cmpu b32 $r11 0294 bra e #ticks_from_us_quit295 296 /* Overflow! */297 clear b32 $r14298 299ticks_from_us_quit:300 pop $r11301 pop $r12302 ret303 304// calculate the number of ticks in the specified microsecond delay305//306// $r15 - current307// $r14 - ticks308// $r14 - us (return)309// $r0 - zero310ticks_to_us:311 /* simply divide $ticks by HW_TICKS_PER_US */312 imm32($r13, HW_TICKS_PER_US)313 div $r14 $r14 $r13314 315 ret316 317// request the current process be sent a message after a timeout expires318//319// $r15 - current320// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow)321// $r0 - zero322timer:323 push $r9324 push $r8325 326 // interrupts off to prevent racing with timer isr327 bclr $flags ie0328 329 // if current process already has a timer set, bail330 ld b32 $r8 D[$r15 + #proc_time]331 cmp b32 $r8 0332 bra g #timer_done333 334 // halt watchdog timer temporarily335 clear b32 $r8336 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)337 338 // find out how much time elapsed since the last update339 // of the watchdog and add this time to the wanted ticks340 nv_iord($r8, NV_PPWR_WATCHDOG_TIME)341 ld b32 $r9 D[$r0 + #time_prev]342 sub b32 $r9 $r8343 add b32 $r14 $r9344 st b32 D[$r15 + #proc_time] $r14345 346 // check for a pending interrupt. if there's one already347 // pending, we can just bail since the timer isr will348 // queue the next soonest right after it's done349 nv_iord($r8, NV_PPWR_INTR)350 and $r8 NV_PPWR_INTR_WATCHDOG351 bra nz #timer_enable352 353 // update the watchdog if this timer should expire first,354 // or if there's no timeout already set355 nv_iord($r8, NV_PPWR_WATCHDOG_TIME)356 cmp b32 $r14 $r0357 bra e #timer_reset358 cmp b32 $r14 $r8359 bra g #timer_enable360 timer_reset:361 nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14)362 st b32 D[$r0 + #time_prev] $r14363 364 // re-enable the watchdog timer365 timer_enable:366 mov $r8 1367 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)368 369 // interrupts back on370 timer_done:371 bset $flags ie0372 373 pop $r8374 pop $r9375 ret376 377// send message to another process378//379// $r15 - current380// $r14 - process381// $r13 - message382// $r12 - message data 0383// $r11 - message data 1384// $r0 - zero385send_proc:386 push $r8387 push $r9388 // check for space in queue389 ld b32 $r8 D[$r14 + #proc_qget]390 ld b32 $r9 D[$r14 + #proc_qput]391 xor $r8 #proc_qmaskb392 cmp b32 $r8 $r9393 bra e #send_done394 395 // enqueue message396 and $r8 $r9 #proc_qmaskp397 shl b32 $r8 $r8 #proc_qlen398 add b32 $r8 #proc_queue399 add b32 $r8 $r14400 401 ld b32 $r10 D[$r15 + #proc_id]402 st b32 D[$r8 + #msg_process] $r10403 st b32 D[$r8 + #msg_message] $r13404 st b32 D[$r8 + #msg_data0] $r12405 st b32 D[$r8 + #msg_data1] $r11406 407 // increment PUT408 add b32 $r9 1409 and $r9 #proc_qmaskf410 st b32 D[$r14 + #proc_qput] $r9411 bset $flags $p2412 send_done:413 pop $r9414 pop $r8415 ret416 417// lookup process structure by its name418//419// $r15 - current420// $r14 - process name421// $r0 - zero422//423// $r14 - process424// $p1 - success425find:426 push $r8427 mov $r8 #proc_list_head428 bset $flags $p1429 find_loop:430 ld b32 $r10 D[$r8 + #proc_id]431 cmp b32 $r10 $r14432 bra e #find_done433 add b32 $r8 #proc_size434 cmp b32 $r8 #proc_list_tail435 bra ne #find_loop436 bclr $flags $p1437 find_done:438 mov b32 $r14 $r8439 pop $r8440 ret441 442// send message to another process443//444// $r15 - current445// $r14 - process id446// $r13 - message447// $r12 - message data 0448// $r11 - message data 1449// $r0 - zero450send:451 call(find)452 bra $p1 #send_proc453 ret454 455// process single message for a given process456//457// $r15 - current458// $r14 - process459// $r0 - zero460recv:461 push $r9462 push $r8463 464 ld b32 $r8 D[$r14 + #proc_qget]465 ld b32 $r9 D[$r14 + #proc_qput]466 bclr $flags $p1467 cmp b32 $r8 $r9468 bra e #recv_done469 // dequeue message470 and $r9 $r8 #proc_qmaskp471 add b32 $r8 1472 and $r8 #proc_qmaskf473 st b32 D[$r14 + #proc_qget] $r8474 ld b32 $r10 D[$r14 + #proc_recv]475 476 push $r15477 mov $r15 $flags478 push $r15479 mov b32 $r15 $r14480 481 shl b32 $r9 $r9 #proc_qlen482 add b32 $r14 $r9483 add b32 $r14 #proc_queue484 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 it490 call $r10491 pop $r15492 mov $flags $r15493 bset $flags $p1494 pop $r15495 recv_done:496 pop $r8497 pop $r9498 ret499 500init:501 // setup stack502 nv_iord($r1, NV_PPWR_CAPS)503 extr $r1 $r1 9:17504 shl b32 $r1 8505 mov $sp $r1506 507#ifdef NVKM_FALCON_MMIO_UAS508 // somehow allows the magic "access mmio via D[]" stuff that's509 // used by the nv_rd32/nv_wr32 macros to work510 imm32($r1, 0x10 | NV_PPWR_UAS_CONFIG_ENABLE)511 nv_iowrs(NV_PPWR_UAS_CONFIG, $r1)512#endif513 514 // route all interrupts except user0/1 and pause to fuc515 imm32($r1, 0xe0)516 nv_iowr(NV_PPWR_INTR_ROUTE, $r1)517 518 // enable watchdog and subintr intrs519 mov $r1 NV_PPWR_INTR_EN_CLR_MASK520 nv_iowr(NV_PPWR_INTR_EN_CLR, $r1)521 mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG522 or $r1 NV_PPWR_INTR_EN_SET_SUBINTR523 nv_iowr(NV_PPWR_INTR_EN_SET, $r1)524 525 // enable interrupts globally526 imm32($r1, #intr)527 and $r1 0xffff528 mov $iv0 $r1529 bset $flags ie0530 531 // enable watchdog timer532 mov $r1 1533 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1)534 535 // bootstrap processes, idle process will be last, and not return536 mov $r15 #proc_list_head537 init_proc:538 ld b32 $r1 D[$r15 + #proc_init]539 cmp b32 $r1 0540 bra z #init_proc541 call $r1542 add b32 $r15 #proc_size543 bra #init_proc544#endif545