0001 /*
0002 * Copyright 2013 Red Hat Inc.
0003 *
0004 * Permission is hereby granted, free of charge, to any person obtaining a
0005 * copy of this software and associated documentation files (the "Software"),
0006 * to deal in the Software without restriction, including without limitation
0007 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008 * and/or sell copies of the Software, and to permit persons to whom the
0009 * Software is furnished to do so, subject to the following conditions:
0010 *
0011 * The above copyright notice and this permission notice shall be included in
0012 * all copies or substantial portions of the Software.
0013 *
0014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
0017 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020 * OTHER DEALINGS IN THE SOFTWARE.
0021 *
0022 * Authors: Ben Skeggs
0023 */
0024
0025 /******************************************************************************
0026 * kernel data segment
0027 *****************************************************************************/
0028 #ifdef INCLUDE_PROC
0029 proc_kern:
0030 process(PROC_KERN, 0, 0)
0031 proc_list_head:
0032 #endif
0033
0034 #ifdef INCLUDE_DATA
0035 proc_list_tail:
0036 time_prev: .b32 0
0037 time_next: .b32 0
0038 #endif
0039
0040 /******************************************************************************
0041 * kernel code segment
0042 *****************************************************************************/
0043 #ifdef INCLUDE_CODE
0044 bra #init
0045
0046 // read nv register
0047 //
0048 // $r15 - current
0049 // $r14 - addr
0050 // $r13 - data (return)
0051 // $r0 - zero
0052 rd32:
0053 nv_iowr(NV_PPWR_MMIO_ADDR, $r14)
0054 imm32($r13, NV_PPWR_MMIO_CTRL_OP_RD | NV_PPWR_MMIO_CTRL_TRIGGER)
0055 nv_iowr(NV_PPWR_MMIO_CTRL, $r13)
0056 rd32_wait:
0057 nv_iord($r13, NV_PPWR_MMIO_CTRL)
0058 and $r13 NV_PPWR_MMIO_CTRL_STATUS
0059 bra nz #rd32_wait
0060 nv_iord($r13, NV_PPWR_MMIO_DATA)
0061 ret
0062
0063 // write nv register
0064 //
0065 // $r15 - current
0066 // $r14 - addr
0067 // $r13 - data
0068 // $r0 - zero
0069 wr32:
0070 nv_iowr(NV_PPWR_MMIO_ADDR, $r14)
0071 nv_iowr(NV_PPWR_MMIO_DATA, $r13)
0072 imm32($r13, NV_PPWR_MMIO_CTRL_OP_WR | NV_PPWR_MMIO_CTRL_MASK_B32_0 | NV_PPWR_MMIO_CTRL_TRIGGER)
0073
0074 #ifdef NVKM_FALCON_MMIO_TRAP
0075 push $r13
0076 mov $r13 NV_PPWR_INTR_TRIGGER_USER1
0077 nv_iowr(NV_PPWR_INTR_TRIGGER, $r13)
0078 wr32_host:
0079 nv_iord($r13, NV_PPWR_INTR)
0080 and $r13 NV_PPWR_INTR_USER1
0081 bra nz #wr32_host
0082 pop $r13
0083 #endif
0084
0085 nv_iowr(NV_PPWR_MMIO_CTRL, $r13)
0086 wr32_wait:
0087 nv_iord($r13, NV_PPWR_MMIO_CTRL)
0088 and $r13 NV_PPWR_MMIO_CTRL_STATUS
0089 bra nz #wr32_wait
0090 ret
0091
0092 // busy-wait for a period of time
0093 //
0094 // $r15 - current
0095 // $r14 - ns
0096 // $r0 - zero
0097 nsec:
0098 push $r9
0099 push $r8
0100 nv_iord($r8, NV_PPWR_TIMER_LOW)
0101 nsec_loop:
0102 nv_iord($r9, NV_PPWR_TIMER_LOW)
0103 sub b32 $r9 $r8
0104 cmp b32 $r9 $r14
0105 bra l #nsec_loop
0106 pop $r8
0107 pop $r9
0108 ret
0109
0110 // busy-wait for a period of time
0111 //
0112 // $r15 - current
0113 // $r14 - addr
0114 // $r13 - mask
0115 // $r12 - data
0116 // $r11 - timeout (ns)
0117 // $r0 - zero
0118 wait:
0119 push $r9
0120 push $r8
0121 nv_iord($r8, NV_PPWR_TIMER_LOW)
0122 wait_loop:
0123 nv_rd32($r10, $r14)
0124 and $r10 $r13
0125 cmp b32 $r10 $r12
0126 bra e #wait_done
0127 nv_iord($r9, NV_PPWR_TIMER_LOW)
0128 sub b32 $r9 $r8
0129 cmp b32 $r9 $r11
0130 bra l #wait_loop
0131 wait_done:
0132 pop $r8
0133 pop $r9
0134 ret
0135
0136 // $r15 - current (kern)
0137 // $r14 - process
0138 // $r8 - NV_PPWR_INTR
0139 intr_watchdog:
0140 // read process' timer status, skip if not enabled
0141 ld b32 $r9 D[$r14 + #proc_time]
0142 cmp b32 $r9 0
0143 bra z #intr_watchdog_next_proc
0144
0145 // subtract last timer's value from process' timer,
0146 // if it's <= 0 then the timer has expired
0147 ld b32 $r10 D[$r0 + #time_prev]
0148 sub b32 $r9 $r10
0149 bra g #intr_watchdog_next_time
0150 mov $r13 KMSG_ALARM
0151 call(send_proc)
0152 clear b32 $r9
0153 bra #intr_watchdog_next_proc
0154
0155 // otherwise, update the next timer's value if this
0156 // process' timer is the soonest
0157 intr_watchdog_next_time:
0158 // ... or if there's no next timer yet
0159 ld b32 $r10 D[$r0 + #time_next]
0160 cmp b32 $r10 0
0161 bra z #intr_watchdog_next_time_set
0162
0163 cmp b32 $r9 $r10
0164 bra g #intr_watchdog_next_proc
0165 intr_watchdog_next_time_set:
0166 st b32 D[$r0 + #time_next] $r9
0167
0168 // update process' timer status, and advance
0169 intr_watchdog_next_proc:
0170 st b32 D[$r14 + #proc_time] $r9
0171 add b32 $r14 #proc_size
0172 cmp b32 $r14 #proc_list_tail
0173 bra ne #intr_watchdog
0174 ret
0175
0176 intr:
0177 push $r0
0178 clear b32 $r0
0179 push $r8
0180 push $r9
0181 push $r10
0182 push $r11
0183 push $r12
0184 push $r13
0185 push $r14
0186 push $r15
0187 mov $r15 #proc_kern
0188 mov $r8 $flags
0189 push $r8
0190
0191 nv_iord($r8, NV_PPWR_DSCRATCH(0))
0192 add b32 $r8 1
0193 nv_iowr(NV_PPWR_DSCRATCH(0), $r8)
0194
0195 nv_iord($r8, NV_PPWR_INTR)
0196 and $r9 $r8 NV_PPWR_INTR_WATCHDOG
0197 bra z #intr_skip_watchdog
0198 st b32 D[$r0 + #time_next] $r0
0199 mov $r14 #proc_list_head
0200 call(intr_watchdog)
0201 ld b32 $r9 D[$r0 + #time_next]
0202 cmp b32 $r9 0
0203 bra z #intr_skip_watchdog
0204 nv_iowr(NV_PPWR_WATCHDOG_TIME, $r9)
0205 st b32 D[$r0 + #time_prev] $r9
0206
0207 intr_skip_watchdog:
0208 and $r9 $r8 NV_PPWR_INTR_SUBINTR
0209 bra z #intr_skip_subintr
0210 nv_iord($r9, NV_PPWR_SUBINTR)
0211 and $r10 $r9 NV_PPWR_SUBINTR_FIFO
0212 bra z #intr_subintr_skip_fifo
0213 nv_iord($r12, NV_PPWR_FIFO_INTR)
0214 push $r12
0215 imm32($r14, PROC_HOST)
0216 mov $r13 KMSG_FIFO
0217 call(send)
0218 pop $r12
0219 nv_iowr(NV_PPWR_FIFO_INTR, $r12)
0220 intr_subintr_skip_fifo:
0221 nv_iowr(NV_PPWR_SUBINTR, $r9)
0222
0223 intr_skip_subintr:
0224 mov $r9 (NV_PPWR_INTR_USER0 | NV_PPWR_INTR_USER1 | NV_PPWR_INTR_PAUSE)
0225 not b32 $r9
0226 and $r8 $r9
0227 nv_iowr(NV_PPWR_INTR_ACK, $r8)
0228
0229 pop $r8
0230 mov $flags $r8
0231 pop $r15
0232 pop $r14
0233 pop $r13
0234 pop $r12
0235 pop $r11
0236 pop $r10
0237 pop $r9
0238 pop $r8
0239 pop $r0
0240 bclr $flags $p0
0241 iret
0242
0243 // calculate the number of ticks in the specified nanoseconds delay
0244 //
0245 // $r15 - current
0246 // $r14 - ns
0247 // $r14 - ticks (return)
0248 // $r0 - zero
0249 ticks_from_ns:
0250 push $r12
0251 push $r11
0252
0253 /* try not losing precision (multiply then divide) */
0254 imm32($r13, HW_TICKS_PER_US)
0255 call(mulu32_32_64)
0256
0257 /* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */
0258 div $r12 $r12 1000
0259
0260 /* check if there wasn't any overflow */
0261 cmpu b32 $r11 0
0262 bra e #ticks_from_ns_quit
0263
0264 /* let's divide then multiply, too bad for the precision! */
0265 div $r14 $r14 1000
0266 imm32($r13, HW_TICKS_PER_US)
0267 call(mulu32_32_64)
0268
0269 /* this cannot overflow as long as HW_TICKS_PER_US < 1000 */
0270
0271 ticks_from_ns_quit:
0272 mov b32 $r14 $r12
0273 pop $r11
0274 pop $r12
0275 ret
0276
0277 // calculate the number of ticks in the specified microsecond delay
0278 //
0279 // $r15 - current
0280 // $r14 - us
0281 // $r14 - ticks (return)
0282 // $r0 - zero
0283 ticks_from_us:
0284 push $r12
0285 push $r11
0286
0287 /* simply multiply $us by HW_TICKS_PER_US */
0288 imm32($r13, HW_TICKS_PER_US)
0289 call(mulu32_32_64)
0290 mov b32 $r14 $r12
0291
0292 /* check if there wasn't any overflow */
0293 cmpu b32 $r11 0
0294 bra e #ticks_from_us_quit
0295
0296 /* Overflow! */
0297 clear b32 $r14
0298
0299 ticks_from_us_quit:
0300 pop $r11
0301 pop $r12
0302 ret
0303
0304 // calculate the number of ticks in the specified microsecond delay
0305 //
0306 // $r15 - current
0307 // $r14 - ticks
0308 // $r14 - us (return)
0309 // $r0 - zero
0310 ticks_to_us:
0311 /* simply divide $ticks by HW_TICKS_PER_US */
0312 imm32($r13, HW_TICKS_PER_US)
0313 div $r14 $r14 $r13
0314
0315 ret
0316
0317 // request the current process be sent a message after a timeout expires
0318 //
0319 // $r15 - current
0320 // $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow)
0321 // $r0 - zero
0322 timer:
0323 push $r9
0324 push $r8
0325
0326 // interrupts off to prevent racing with timer isr
0327 bclr $flags ie0
0328
0329 // if current process already has a timer set, bail
0330 ld b32 $r8 D[$r15 + #proc_time]
0331 cmp b32 $r8 0
0332 bra g #timer_done
0333
0334 // halt watchdog timer temporarily
0335 clear b32 $r8
0336 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
0337
0338 // find out how much time elapsed since the last update
0339 // of the watchdog and add this time to the wanted ticks
0340 nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
0341 ld b32 $r9 D[$r0 + #time_prev]
0342 sub b32 $r9 $r8
0343 add b32 $r14 $r9
0344 st b32 D[$r15 + #proc_time] $r14
0345
0346 // check for a pending interrupt. if there's one already
0347 // pending, we can just bail since the timer isr will
0348 // queue the next soonest right after it's done
0349 nv_iord($r8, NV_PPWR_INTR)
0350 and $r8 NV_PPWR_INTR_WATCHDOG
0351 bra nz #timer_enable
0352
0353 // update the watchdog if this timer should expire first,
0354 // or if there's no timeout already set
0355 nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
0356 cmp b32 $r14 $r0
0357 bra e #timer_reset
0358 cmp b32 $r14 $r8
0359 bra g #timer_enable
0360 timer_reset:
0361 nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14)
0362 st b32 D[$r0 + #time_prev] $r14
0363
0364 // re-enable the watchdog timer
0365 timer_enable:
0366 mov $r8 1
0367 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
0368
0369 // interrupts back on
0370 timer_done:
0371 bset $flags ie0
0372
0373 pop $r8
0374 pop $r9
0375 ret
0376
0377 // send message to another process
0378 //
0379 // $r15 - current
0380 // $r14 - process
0381 // $r13 - message
0382 // $r12 - message data 0
0383 // $r11 - message data 1
0384 // $r0 - zero
0385 send_proc:
0386 push $r8
0387 push $r9
0388 // check for space in queue
0389 ld b32 $r8 D[$r14 + #proc_qget]
0390 ld b32 $r9 D[$r14 + #proc_qput]
0391 xor $r8 #proc_qmaskb
0392 cmp b32 $r8 $r9
0393 bra e #send_done
0394
0395 // enqueue message
0396 and $r8 $r9 #proc_qmaskp
0397 shl b32 $r8 $r8 #proc_qlen
0398 add b32 $r8 #proc_queue
0399 add b32 $r8 $r14
0400
0401 ld b32 $r10 D[$r15 + #proc_id]
0402 st b32 D[$r8 + #msg_process] $r10
0403 st b32 D[$r8 + #msg_message] $r13
0404 st b32 D[$r8 + #msg_data0] $r12
0405 st b32 D[$r8 + #msg_data1] $r11
0406
0407 // increment PUT
0408 add b32 $r9 1
0409 and $r9 #proc_qmaskf
0410 st b32 D[$r14 + #proc_qput] $r9
0411 bset $flags $p2
0412 send_done:
0413 pop $r9
0414 pop $r8
0415 ret
0416
0417 // lookup process structure by its name
0418 //
0419 // $r15 - current
0420 // $r14 - process name
0421 // $r0 - zero
0422 //
0423 // $r14 - process
0424 // $p1 - success
0425 find:
0426 push $r8
0427 mov $r8 #proc_list_head
0428 bset $flags $p1
0429 find_loop:
0430 ld b32 $r10 D[$r8 + #proc_id]
0431 cmp b32 $r10 $r14
0432 bra e #find_done
0433 add b32 $r8 #proc_size
0434 cmp b32 $r8 #proc_list_tail
0435 bra ne #find_loop
0436 bclr $flags $p1
0437 find_done:
0438 mov b32 $r14 $r8
0439 pop $r8
0440 ret
0441
0442 // send message to another process
0443 //
0444 // $r15 - current
0445 // $r14 - process id
0446 // $r13 - message
0447 // $r12 - message data 0
0448 // $r11 - message data 1
0449 // $r0 - zero
0450 send:
0451 call(find)
0452 bra $p1 #send_proc
0453 ret
0454
0455 // process single message for a given process
0456 //
0457 // $r15 - current
0458 // $r14 - process
0459 // $r0 - zero
0460 recv:
0461 push $r9
0462 push $r8
0463
0464 ld b32 $r8 D[$r14 + #proc_qget]
0465 ld b32 $r9 D[$r14 + #proc_qput]
0466 bclr $flags $p1
0467 cmp b32 $r8 $r9
0468 bra e #recv_done
0469 // dequeue message
0470 and $r9 $r8 #proc_qmaskp
0471 add b32 $r8 1
0472 and $r8 #proc_qmaskf
0473 st b32 D[$r14 + #proc_qget] $r8
0474 ld b32 $r10 D[$r14 + #proc_recv]
0475
0476 push $r15
0477 mov $r15 $flags
0478 push $r15
0479 mov b32 $r15 $r14
0480
0481 shl b32 $r9 $r9 #proc_qlen
0482 add b32 $r14 $r9
0483 add b32 $r14 #proc_queue
0484 ld b32 $r11 D[$r14 + #msg_data1]
0485 ld b32 $r12 D[$r14 + #msg_data0]
0486 ld b32 $r13 D[$r14 + #msg_message]
0487 ld b32 $r14 D[$r14 + #msg_process]
0488
0489 // process it
0490 call $r10
0491 pop $r15
0492 mov $flags $r15
0493 bset $flags $p1
0494 pop $r15
0495 recv_done:
0496 pop $r8
0497 pop $r9
0498 ret
0499
0500 init:
0501 // setup stack
0502 nv_iord($r1, NV_PPWR_CAPS)
0503 extr $r1 $r1 9:17
0504 shl b32 $r1 8
0505 mov $sp $r1
0506
0507 #ifdef NVKM_FALCON_MMIO_UAS
0508 // somehow allows the magic "access mmio via D[]" stuff that's
0509 // used by the nv_rd32/nv_wr32 macros to work
0510 imm32($r1, 0x10 | NV_PPWR_UAS_CONFIG_ENABLE)
0511 nv_iowrs(NV_PPWR_UAS_CONFIG, $r1)
0512 #endif
0513
0514 // route all interrupts except user0/1 and pause to fuc
0515 imm32($r1, 0xe0)
0516 nv_iowr(NV_PPWR_INTR_ROUTE, $r1)
0517
0518 // enable watchdog and subintr intrs
0519 mov $r1 NV_PPWR_INTR_EN_CLR_MASK
0520 nv_iowr(NV_PPWR_INTR_EN_CLR, $r1)
0521 mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG
0522 or $r1 NV_PPWR_INTR_EN_SET_SUBINTR
0523 nv_iowr(NV_PPWR_INTR_EN_SET, $r1)
0524
0525 // enable interrupts globally
0526 imm32($r1, #intr)
0527 and $r1 0xffff
0528 mov $iv0 $r1
0529 bset $flags ie0
0530
0531 // enable watchdog timer
0532 mov $r1 1
0533 nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1)
0534
0535 // bootstrap processes, idle process will be last, and not return
0536 mov $r15 #proc_list_head
0537 init_proc:
0538 ld b32 $r1 D[$r15 + #proc_init]
0539 cmp b32 $r1 0
0540 bra z #init_proc
0541 call $r1
0542 add b32 $r15 #proc_size
0543 bra #init_proc
0544 #endif