GNU Linux-libre 4.19.207-gnu1
[releases.git] / drivers / gpu / drm / nouveau / nvkm / subdev / pmu / fuc / kernel.fuc
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
29 proc_kern:
30 process(PROC_KERN, 0, 0)
31 proc_list_head:
32 #endif
33
34 #ifdef INCLUDE_DATA
35 proc_list_tail:
36 time_prev: .b32 0
37 time_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
52 rd32:
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
69 wr32:
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
97 nsec:
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
118 wait:
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
139 intr_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
176 intr:
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
249 ticks_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
271 ticks_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
283 ticks_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
299 ticks_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
310 ticks_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
322 timer:
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
385 send_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
425 find:
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
450 send:
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
460 recv:
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
500 init:
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