| /* |
| * Copyright 2013 Red Hat Inc. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| * |
| * Authors: Ben Skeggs |
| */ |
| |
| /****************************************************************************** |
| * kernel data segment |
| *****************************************************************************/ |
| #ifdef INCLUDE_PROC |
| proc_kern: |
| process(PROC_KERN, 0, 0) |
| proc_list_head: |
| #endif |
| |
| #ifdef INCLUDE_DATA |
| proc_list_tail: |
| time_prev: .b32 0 |
| time_next: .b32 0 |
| #endif |
| |
| /****************************************************************************** |
| * kernel code segment |
| *****************************************************************************/ |
| #ifdef INCLUDE_CODE |
| bra #init |
| |
| // read nv register |
| // |
| // $r15 - current |
| // $r14 - addr |
| // $r13 - data (return) |
| // $r0 - zero |
| rd32: |
| nv_iowr(NV_PPWR_MMIO_ADDR, $r14) |
| imm32($r13, NV_PPWR_MMIO_CTRL_OP_RD | NV_PPWR_MMIO_CTRL_TRIGGER) |
| nv_iowr(NV_PPWR_MMIO_CTRL, $r13) |
| rd32_wait: |
| nv_iord($r13, NV_PPWR_MMIO_CTRL) |
| and $r13 NV_PPWR_MMIO_CTRL_STATUS |
| bra nz #rd32_wait |
| nv_iord($r13, NV_PPWR_MMIO_DATA) |
| ret |
| |
| // write nv register |
| // |
| // $r15 - current |
| // $r14 - addr |
| // $r13 - data |
| // $r0 - zero |
| wr32: |
| nv_iowr(NV_PPWR_MMIO_ADDR, $r14) |
| nv_iowr(NV_PPWR_MMIO_DATA, $r13) |
| imm32($r13, NV_PPWR_MMIO_CTRL_OP_WR | NV_PPWR_MMIO_CTRL_MASK_B32_0 | NV_PPWR_MMIO_CTRL_TRIGGER) |
| |
| #ifdef NVKM_FALCON_MMIO_TRAP |
| push $r13 |
| mov $r13 NV_PPWR_INTR_TRIGGER_USER1 |
| nv_iowr(NV_PPWR_INTR_TRIGGER, $r13) |
| wr32_host: |
| nv_iord($r13, NV_PPWR_INTR) |
| and $r13 NV_PPWR_INTR_USER1 |
| bra nz #wr32_host |
| pop $r13 |
| #endif |
| |
| nv_iowr(NV_PPWR_MMIO_CTRL, $r13) |
| wr32_wait: |
| nv_iord($r13, NV_PPWR_MMIO_CTRL) |
| and $r13 NV_PPWR_MMIO_CTRL_STATUS |
| bra nz #wr32_wait |
| ret |
| |
| // busy-wait for a period of time |
| // |
| // $r15 - current |
| // $r14 - ns |
| // $r0 - zero |
| nsec: |
| push $r9 |
| push $r8 |
| nv_iord($r8, NV_PPWR_TIMER_LOW) |
| nsec_loop: |
| nv_iord($r9, NV_PPWR_TIMER_LOW) |
| sub b32 $r9 $r8 |
| cmp b32 $r9 $r14 |
| bra l #nsec_loop |
| pop $r8 |
| pop $r9 |
| ret |
| |
| // busy-wait for a period of time |
| // |
| // $r15 - current |
| // $r14 - addr |
| // $r13 - mask |
| // $r12 - data |
| // $r11 - timeout (ns) |
| // $r0 - zero |
| wait: |
| push $r9 |
| push $r8 |
| nv_iord($r8, NV_PPWR_TIMER_LOW) |
| wait_loop: |
| nv_rd32($r10, $r14) |
| and $r10 $r13 |
| cmp b32 $r10 $r12 |
| bra e #wait_done |
| nv_iord($r9, NV_PPWR_TIMER_LOW) |
| sub b32 $r9 $r8 |
| cmp b32 $r9 $r11 |
| bra l #wait_loop |
| wait_done: |
| pop $r8 |
| pop $r9 |
| ret |
| |
| // $r15 - current (kern) |
| // $r14 - process |
| // $r8 - NV_PPWR_INTR |
| intr_watchdog: |
| // read process' timer status, skip if not enabled |
| ld b32 $r9 D[$r14 + #proc_time] |
| cmp b32 $r9 0 |
| bra z #intr_watchdog_next_proc |
| |
| // subtract last timer's value from process' timer, |
| // if it's <= 0 then the timer has expired |
| ld b32 $r10 D[$r0 + #time_prev] |
| sub b32 $r9 $r10 |
| bra g #intr_watchdog_next_time |
| mov $r13 KMSG_ALARM |
| call(send_proc) |
| clear b32 $r9 |
| bra #intr_watchdog_next_proc |
| |
| // otherwise, update the next timer's value if this |
| // process' timer is the soonest |
| intr_watchdog_next_time: |
| // ... or if there's no next timer yet |
| ld b32 $r10 D[$r0 + #time_next] |
| cmp b32 $r10 0 |
| bra z #intr_watchdog_next_time_set |
| |
| cmp b32 $r9 $r10 |
| bra g #intr_watchdog_next_proc |
| intr_watchdog_next_time_set: |
| st b32 D[$r0 + #time_next] $r9 |
| |
| // update process' timer status, and advance |
| intr_watchdog_next_proc: |
| st b32 D[$r14 + #proc_time] $r9 |
| add b32 $r14 #proc_size |
| cmp b32 $r14 #proc_list_tail |
| bra ne #intr_watchdog |
| ret |
| |
| intr: |
| push $r0 |
| clear b32 $r0 |
| push $r8 |
| push $r9 |
| push $r10 |
| push $r11 |
| push $r12 |
| push $r13 |
| push $r14 |
| push $r15 |
| mov $r15 #proc_kern |
| mov $r8 $flags |
| push $r8 |
| |
| nv_iord($r8, NV_PPWR_DSCRATCH(0)) |
| add b32 $r8 1 |
| nv_iowr(NV_PPWR_DSCRATCH(0), $r8) |
| |
| nv_iord($r8, NV_PPWR_INTR) |
| and $r9 $r8 NV_PPWR_INTR_WATCHDOG |
| bra z #intr_skip_watchdog |
| st b32 D[$r0 + #time_next] $r0 |
| mov $r14 #proc_list_head |
| call(intr_watchdog) |
| ld b32 $r9 D[$r0 + #time_next] |
| cmp b32 $r9 0 |
| bra z #intr_skip_watchdog |
| nv_iowr(NV_PPWR_WATCHDOG_TIME, $r9) |
| st b32 D[$r0 + #time_prev] $r9 |
| |
| intr_skip_watchdog: |
| and $r9 $r8 NV_PPWR_INTR_SUBINTR |
| bra z #intr_skip_subintr |
| nv_iord($r9, NV_PPWR_SUBINTR) |
| and $r10 $r9 NV_PPWR_SUBINTR_FIFO |
| bra z #intr_subintr_skip_fifo |
| nv_iord($r12, NV_PPWR_FIFO_INTR) |
| push $r12 |
| imm32($r14, PROC_HOST) |
| mov $r13 KMSG_FIFO |
| call(send) |
| pop $r12 |
| nv_iowr(NV_PPWR_FIFO_INTR, $r12) |
| intr_subintr_skip_fifo: |
| nv_iowr(NV_PPWR_SUBINTR, $r9) |
| |
| intr_skip_subintr: |
| mov $r9 (NV_PPWR_INTR_USER0 | NV_PPWR_INTR_USER1 | NV_PPWR_INTR_PAUSE) |
| not b32 $r9 |
| and $r8 $r9 |
| nv_iowr(NV_PPWR_INTR_ACK, $r8) |
| |
| pop $r8 |
| mov $flags $r8 |
| pop $r15 |
| pop $r14 |
| pop $r13 |
| pop $r12 |
| pop $r11 |
| pop $r10 |
| pop $r9 |
| pop $r8 |
| pop $r0 |
| bclr $flags $p0 |
| iret |
| |
| // calculate the number of ticks in the specified nanoseconds delay |
| // |
| // $r15 - current |
| // $r14 - ns |
| // $r14 - ticks (return) |
| // $r0 - zero |
| ticks_from_ns: |
| push $r12 |
| push $r11 |
| |
| /* try not losing precision (multiply then divide) */ |
| imm32($r13, HW_TICKS_PER_US) |
| call(mulu32_32_64) |
| |
| /* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */ |
| div $r12 $r12 1000 |
| |
| /* check if there wasn't any overflow */ |
| cmpu b32 $r11 0 |
| bra e #ticks_from_ns_quit |
| |
| /* let's divide then multiply, too bad for the precision! */ |
| div $r14 $r14 1000 |
| imm32($r13, HW_TICKS_PER_US) |
| call(mulu32_32_64) |
| |
| /* this cannot overflow as long as HW_TICKS_PER_US < 1000 */ |
| |
| ticks_from_ns_quit: |
| mov b32 $r14 $r12 |
| pop $r11 |
| pop $r12 |
| ret |
| |
| // calculate the number of ticks in the specified microsecond delay |
| // |
| // $r15 - current |
| // $r14 - us |
| // $r14 - ticks (return) |
| // $r0 - zero |
| ticks_from_us: |
| push $r12 |
| push $r11 |
| |
| /* simply multiply $us by HW_TICKS_PER_US */ |
| imm32($r13, HW_TICKS_PER_US) |
| call(mulu32_32_64) |
| mov b32 $r14 $r12 |
| |
| /* check if there wasn't any overflow */ |
| cmpu b32 $r11 0 |
| bra e #ticks_from_us_quit |
| |
| /* Overflow! */ |
| clear b32 $r14 |
| |
| ticks_from_us_quit: |
| pop $r11 |
| pop $r12 |
| ret |
| |
| // calculate the number of ticks in the specified microsecond delay |
| // |
| // $r15 - current |
| // $r14 - ticks |
| // $r14 - us (return) |
| // $r0 - zero |
| ticks_to_us: |
| /* simply divide $ticks by HW_TICKS_PER_US */ |
| imm32($r13, HW_TICKS_PER_US) |
| div $r14 $r14 $r13 |
| |
| ret |
| |
| // request the current process be sent a message after a timeout expires |
| // |
| // $r15 - current |
| // $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow) |
| // $r0 - zero |
| timer: |
| push $r9 |
| push $r8 |
| |
| // interrupts off to prevent racing with timer isr |
| bclr $flags ie0 |
| |
| // if current process already has a timer set, bail |
| ld b32 $r8 D[$r15 + #proc_time] |
| cmp b32 $r8 0 |
| bra g #timer_done |
| |
| // halt watchdog timer temporarily |
| clear b32 $r8 |
| nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8) |
| |
| // find out how much time elapsed since the last update |
| // of the watchdog and add this time to the wanted ticks |
| nv_iord($r8, NV_PPWR_WATCHDOG_TIME) |
| ld b32 $r9 D[$r0 + #time_prev] |
| sub b32 $r9 $r8 |
| add b32 $r14 $r9 |
| st b32 D[$r15 + #proc_time] $r14 |
| |
| // check for a pending interrupt. if there's one already |
| // pending, we can just bail since the timer isr will |
| // queue the next soonest right after it's done |
| nv_iord($r8, NV_PPWR_INTR) |
| and $r8 NV_PPWR_INTR_WATCHDOG |
| bra nz #timer_enable |
| |
| // update the watchdog if this timer should expire first, |
| // or if there's no timeout already set |
| nv_iord($r8, NV_PPWR_WATCHDOG_TIME) |
| cmp b32 $r14 $r0 |
| bra e #timer_reset |
| cmp b32 $r14 $r8 |
| bra g #timer_enable |
| timer_reset: |
| nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14) |
| st b32 D[$r0 + #time_prev] $r14 |
| |
| // re-enable the watchdog timer |
| timer_enable: |
| mov $r8 1 |
| nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8) |
| |
| // interrupts back on |
| timer_done: |
| bset $flags ie0 |
| |
| pop $r8 |
| pop $r9 |
| ret |
| |
| // send message to another process |
| // |
| // $r15 - current |
| // $r14 - process |
| // $r13 - message |
| // $r12 - message data 0 |
| // $r11 - message data 1 |
| // $r0 - zero |
| send_proc: |
| push $r8 |
| push $r9 |
| // check for space in queue |
| ld b32 $r8 D[$r14 + #proc_qget] |
| ld b32 $r9 D[$r14 + #proc_qput] |
| xor $r8 #proc_qmaskb |
| cmp b32 $r8 $r9 |
| bra e #send_done |
| |
| // enqueue message |
| and $r8 $r9 #proc_qmaskp |
| shl b32 $r8 $r8 #proc_qlen |
| add b32 $r8 #proc_queue |
| add b32 $r8 $r14 |
| |
| ld b32 $r10 D[$r15 + #proc_id] |
| st b32 D[$r8 + #msg_process] $r10 |
| st b32 D[$r8 + #msg_message] $r13 |
| st b32 D[$r8 + #msg_data0] $r12 |
| st b32 D[$r8 + #msg_data1] $r11 |
| |
| // increment PUT |
| add b32 $r9 1 |
| and $r9 #proc_qmaskf |
| st b32 D[$r14 + #proc_qput] $r9 |
| bset $flags $p2 |
| send_done: |
| pop $r9 |
| pop $r8 |
| ret |
| |
| // lookup process structure by its name |
| // |
| // $r15 - current |
| // $r14 - process name |
| // $r0 - zero |
| // |
| // $r14 - process |
| // $p1 - success |
| find: |
| push $r8 |
| mov $r8 #proc_list_head |
| bset $flags $p1 |
| find_loop: |
| ld b32 $r10 D[$r8 + #proc_id] |
| cmp b32 $r10 $r14 |
| bra e #find_done |
| add b32 $r8 #proc_size |
| cmp b32 $r8 #proc_list_tail |
| bra ne #find_loop |
| bclr $flags $p1 |
| find_done: |
| mov b32 $r14 $r8 |
| pop $r8 |
| ret |
| |
| // send message to another process |
| // |
| // $r15 - current |
| // $r14 - process id |
| // $r13 - message |
| // $r12 - message data 0 |
| // $r11 - message data 1 |
| // $r0 - zero |
| send: |
| call(find) |
| bra $p1 #send_proc |
| ret |
| |
| // process single message for a given process |
| // |
| // $r15 - current |
| // $r14 - process |
| // $r0 - zero |
| recv: |
| push $r9 |
| push $r8 |
| |
| ld b32 $r8 D[$r14 + #proc_qget] |
| ld b32 $r9 D[$r14 + #proc_qput] |
| bclr $flags $p1 |
| cmp b32 $r8 $r9 |
| bra e #recv_done |
| // dequeue message |
| and $r9 $r8 #proc_qmaskp |
| add b32 $r8 1 |
| and $r8 #proc_qmaskf |
| st b32 D[$r14 + #proc_qget] $r8 |
| ld b32 $r10 D[$r14 + #proc_recv] |
| |
| push $r15 |
| mov $r15 $flags |
| push $r15 |
| mov b32 $r15 $r14 |
| |
| shl b32 $r9 $r9 #proc_qlen |
| add b32 $r14 $r9 |
| add b32 $r14 #proc_queue |
| ld b32 $r11 D[$r14 + #msg_data1] |
| ld b32 $r12 D[$r14 + #msg_data0] |
| ld b32 $r13 D[$r14 + #msg_message] |
| ld b32 $r14 D[$r14 + #msg_process] |
| |
| // process it |
| call $r10 |
| pop $r15 |
| mov $flags $r15 |
| bset $flags $p1 |
| pop $r15 |
| recv_done: |
| pop $r8 |
| pop $r9 |
| ret |
| |
| init: |
| // setup stack |
| nv_iord($r1, NV_PPWR_CAPS) |
| extr $r1 $r1 9:17 |
| shl b32 $r1 8 |
| mov $sp $r1 |
| |
| #ifdef NVKM_FALCON_MMIO_UAS |
| // somehow allows the magic "access mmio via D[]" stuff that's |
| // used by the nv_rd32/nv_wr32 macros to work |
| imm32($r1, 0x10 | NV_PPWR_UAS_CONFIG_ENABLE) |
| nv_iowrs(NV_PPWR_UAS_CONFIG, $r1) |
| #endif |
| |
| // route all interrupts except user0/1 and pause to fuc |
| imm32($r1, 0xe0) |
| nv_iowr(NV_PPWR_INTR_ROUTE, $r1) |
| |
| // enable watchdog and subintr intrs |
| mov $r1 NV_PPWR_INTR_EN_CLR_MASK |
| nv_iowr(NV_PPWR_INTR_EN_CLR, $r1) |
| mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG |
| or $r1 NV_PPWR_INTR_EN_SET_SUBINTR |
| nv_iowr(NV_PPWR_INTR_EN_SET, $r1) |
| |
| // enable interrupts globally |
| imm32($r1, #intr) |
| and $r1 0xffff |
| mov $iv0 $r1 |
| bset $flags ie0 |
| |
| // enable watchdog timer |
| mov $r1 1 |
| nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1) |
| |
| // bootstrap processes, idle process will be last, and not return |
| mov $r15 #proc_list_head |
| init_proc: |
| ld b32 $r1 D[$r15 + #proc_init] |
| cmp b32 $r1 0 |
| bra z #init_proc |
| call $r1 |
| add b32 $r15 #proc_size |
| bra #init_proc |
| #endif |