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
29proc_kern:
30process(PROC_KERN, 0, 0)
31proc_list_head:
32#endif
33
34#ifdef INCLUDE_DATA
35proc_list_tail:
36time_prev: .b32 0
37time_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
52rd32:
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
69wr32:
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
97nsec:
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
118wait:
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
139intr_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
176intr:
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
249ticks_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
271ticks_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
283ticks_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
299ticks_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
310ticks_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
322timer:
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
385send_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
425find:
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
450send:
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
460recv:
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
500init:
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
545