123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544 |
- /*
- * 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
|