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	mov $r13 NV_PPWR_MMIO_CTRL_OP_RD
55	sethi $r13 NV_PPWR_MMIO_CTRL_TRIGGER
56	nv_iowr(NV_PPWR_MMIO_CTRL, $r13)
57	rd32_wait:
58		nv_iord($r13, NV_PPWR_MMIO_CTRL)
59		and $r13 NV_PPWR_MMIO_CTRL_STATUS
60		bra nz #rd32_wait
61	nv_iord($r13, NV_PPWR_MMIO_DATA)
62	ret
63
64// write nv register
65//
66// $r15 - current
67// $r14 - addr
68// $r13 - data
69// $r0  - zero
70wr32:
71	nv_iowr(NV_PPWR_MMIO_ADDR, $r14)
72	nv_iowr(NV_PPWR_MMIO_DATA, $r13)
73	mov $r13 NV_PPWR_MMIO_CTRL_OP_WR
74	or $r13 NV_PPWR_MMIO_CTRL_MASK_B32_0
75	sethi $r13 NV_PPWR_MMIO_CTRL_TRIGGER
76
77#ifdef NVKM_FALCON_MMIO_TRAP
78	push $r13
79	mov $r13 NV_PPWR_INTR_TRIGGER_USER1
80	nv_iowr(NV_PPWR_INTR_TRIGGER, $r13)
81	wr32_host:
82		nv_iord($r13, NV_PPWR_INTR)
83		and $r13 NV_PPWR_INTR_USER1
84		bra nz #wr32_host
85	pop $r13
86#endif
87
88	nv_iowr(NV_PPWR_MMIO_CTRL, $r13)
89	wr32_wait:
90		nv_iord($r13, NV_PPWR_MMIO_CTRL)
91		and $r13 NV_PPWR_MMIO_CTRL_STATUS
92		bra nz #wr32_wait
93	ret
94
95// busy-wait for a period of time
96//
97// $r15 - current
98// $r14 - ns
99// $r0  - zero
100nsec:
101	push $r9
102	push $r8
103	nv_iord($r8, NV_PPWR_TIMER_LOW)
104	nsec_loop:
105		nv_iord($r9, NV_PPWR_TIMER_LOW)
106		sub b32 $r9 $r8
107		cmp b32 $r9 $r14
108		bra l #nsec_loop
109	pop $r8
110	pop $r9
111	ret
112
113// busy-wait for a period of time
114//
115// $r15 - current
116// $r14 - addr
117// $r13 - mask
118// $r12 - data
119// $r11 - timeout (ns)
120// $r0  - zero
121wait:
122	push $r9
123	push $r8
124	nv_iord($r8, NV_PPWR_TIMER_LOW)
125	wait_loop:
126		nv_rd32($r10, $r14)
127		and $r10 $r13
128		cmp b32 $r10 $r12
129		bra e #wait_done
130		nv_iord($r9, NV_PPWR_TIMER_LOW)
131		sub b32 $r9 $r8
132		cmp b32 $r9 $r11
133		bra l #wait_loop
134	wait_done:
135	pop $r8
136	pop $r9
137	ret
138
139// $r15 - current (kern)
140// $r14 - process
141// $r8  - NV_PPWR_INTR
142intr_watchdog:
143	// read process' timer status, skip if not enabled
144	ld b32 $r9 D[$r14 + #proc_time]
145	cmp b32 $r9 0
146	bra z #intr_watchdog_next_proc
147
148	// subtract last timer's value from process' timer,
149	// if it's <= 0 then the timer has expired
150	ld b32 $r10 D[$r0 + #time_prev]
151	sub b32 $r9 $r10
152	bra g #intr_watchdog_next_time
153		mov $r13 KMSG_ALARM
154		call(send_proc)
155		clear b32 $r9
156		bra #intr_watchdog_next_proc
157
158	// otherwise, update the next timer's value if this
159	// process' timer is the soonest
160	intr_watchdog_next_time:
161		// ... or if there's no next timer yet
162		ld b32 $r10 D[$r0 + #time_next]
163		cmp b32 $r10 0
164		bra z #intr_watchdog_next_time_set
165
166		cmp b32 $r9 $r10
167		bra g #intr_watchdog_next_proc
168		intr_watchdog_next_time_set:
169		st b32 D[$r0 + #time_next] $r9
170
171	// update process' timer status, and advance
172	intr_watchdog_next_proc:
173	st b32 D[$r14 + #proc_time] $r9
174	add b32 $r14 #proc_size
175	cmp b32 $r14 #proc_list_tail
176	bra ne #intr_watchdog
177	ret
178
179intr:
180	push $r0
181	clear b32 $r0
182	push $r8
183	push $r9
184	push $r10
185	push $r11
186	push $r12
187	push $r13
188	push $r14
189	push $r15
190	mov $r15 #proc_kern
191	mov $r8 $flags
192	push $r8
193
194	nv_iord($r8, NV_PPWR_DSCRATCH(0))
195	add b32 $r8 1
196	nv_iowr(NV_PPWR_DSCRATCH(0), $r8)
197
198	nv_iord($r8, NV_PPWR_INTR)
199	and $r9 $r8 NV_PPWR_INTR_WATCHDOG
200	bra z #intr_skip_watchdog
201		st b32 D[$r0 + #time_next] $r0
202		mov $r14 #proc_list_head
203		call(intr_watchdog)
204		ld b32 $r9 D[$r0 + #time_next]
205		cmp b32 $r9 0
206		bra z #intr_skip_watchdog
207			nv_iowr(NV_PPWR_WATCHDOG_TIME, $r9)
208			st b32 D[$r0 + #time_prev] $r9
209
210	intr_skip_watchdog:
211	and $r9 $r8 NV_PPWR_INTR_SUBINTR
212	bra z #intr_skip_subintr
213		nv_iord($r9, NV_PPWR_SUBINTR)
214		and $r10 $r9 NV_PPWR_SUBINTR_FIFO
215		bra z #intr_subintr_skip_fifo
216			nv_iord($r12, NV_PPWR_FIFO_INTR)
217			push $r12
218			mov $r14 (PROC_HOST & 0x0000ffff)
219			sethi $r14 (PROC_HOST & 0xffff0000)
220			mov $r13 KMSG_FIFO
221			call(send)
222			pop $r12
223			nv_iowr(NV_PPWR_FIFO_INTR, $r12)
224		intr_subintr_skip_fifo:
225		nv_iowr(NV_PPWR_SUBINTR, $r9)
226
227	intr_skip_subintr:
228	and $r9 $r8 NV_PPWR_INTR_PAUSE
229	bra z #intr_skip_pause
230		and $r10 0xffbf
231
232	intr_skip_pause:
233	and $r9 $r8 NV_PPWR_INTR_USER0
234	bra z #intr_skip_user0
235		and $r10 0xffbf
236
237	intr_skip_user0:
238	nv_iowr(NV_PPWR_INTR_ACK, $r8)
239	pop $r8
240	mov $flags $r8
241	pop $r15
242	pop $r14
243	pop $r13
244	pop $r12
245	pop $r11
246	pop $r10
247	pop $r9
248	pop $r8
249	pop $r0
250	bclr $flags $p0
251	iret
252
253// calculate the number of ticks in the specified nanoseconds delay
254//
255// $r15 - current
256// $r14 - ns
257// $r14 - ticks (return)
258// $r0  - zero
259ticks_from_ns:
260	push $r12
261	push $r11
262
263	/* try not losing precision (multiply then divide) */
264	imm32($r13, HW_TICKS_PER_US)
265	call #mulu32_32_64
266
267	/* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */
268	div $r12 $r12 1000
269
270	/* check if there wasn't any overflow */
271	cmpu b32 $r11 0
272	bra e #ticks_from_ns_quit
273
274	/* let's divide then multiply, too bad for the precision! */
275	div $r14 $r14 1000
276	imm32($r13, HW_TICKS_PER_US)
277	call #mulu32_32_64
278
279	/* this cannot overflow as long as HW_TICKS_PER_US < 1000 */
280
281ticks_from_ns_quit:
282	mov b32 $r14 $r12
283	pop $r11
284	pop $r12
285	ret
286
287// calculate the number of ticks in the specified microsecond delay
288//
289// $r15 - current
290// $r14 - us
291// $r14 - ticks (return)
292// $r0  - zero
293ticks_from_us:
294	push $r12
295	push $r11
296
297	/* simply multiply $us by HW_TICKS_PER_US */
298	imm32($r13, HW_TICKS_PER_US)
299	call #mulu32_32_64
300	mov b32 $r14 $r12
301
302	/* check if there wasn't any overflow */
303	cmpu b32 $r11 0
304	bra e #ticks_from_us_quit
305
306	/* Overflow! */
307	clear b32 $r14
308
309ticks_from_us_quit:
310	pop $r11
311	pop $r12
312	ret
313
314// calculate the number of ticks in the specified microsecond delay
315//
316// $r15 - current
317// $r14 - ticks
318// $r14 - us (return)
319// $r0  - zero
320ticks_to_us:
321	/* simply divide $ticks by HW_TICKS_PER_US */
322	imm32($r13, HW_TICKS_PER_US)
323	div $r14 $r14 $r13
324
325	ret
326
327// request the current process be sent a message after a timeout expires
328//
329// $r15 - current
330// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow)
331// $r0  - zero
332timer:
333	push $r9
334	push $r8
335
336	// interrupts off to prevent racing with timer isr
337	bclr $flags ie0
338
339	// if current process already has a timer set, bail
340	ld b32 $r8 D[$r15 + #proc_time]
341	cmp b32 $r8 0
342	bra g #timer_done
343
344	// halt watchdog timer temporarily
345	clear b32 $r8
346	nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
347
348	// find out how much time elapsed since the last update
349	// of the watchdog and add this time to the wanted ticks
350	nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
351	ld b32 $r9 D[$r0 + #time_prev]
352	sub b32 $r9 $r8
353	add b32 $r14 $r9
354	st b32 D[$r15 + #proc_time] $r14
355
356	// check for a pending interrupt.  if there's one already
357	// pending, we can just bail since the timer isr will
358	// queue the next soonest right after it's done
359	nv_iord($r8, NV_PPWR_INTR)
360	and $r8 NV_PPWR_INTR_WATCHDOG
361	bra nz #timer_enable
362
363	// update the watchdog if this timer should expire first,
364	// or if there's no timeout already set
365	nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
366	cmp b32 $r14 $r0
367	bra e #timer_reset
368	cmp b32 $r14 $r8
369	bra g #timer_enable
370		timer_reset:
371		nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14)
372		st b32 D[$r0 + #time_prev] $r14
373
374	// re-enable the watchdog timer
375	timer_enable:
376	mov $r8 1
377	nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
378
379	// interrupts back on
380	timer_done:
381	bset $flags ie0
382
383	pop $r8
384	pop $r9
385	ret
386
387// send message to another process
388//
389// $r15 - current
390// $r14 - process
391// $r13 - message
392// $r12 - message data 0
393// $r11 - message data 1
394// $r0  - zero
395send_proc:
396	push $r8
397	push $r9
398	// check for space in queue
399	ld b32 $r8 D[$r14 + #proc_qget]
400	ld b32 $r9 D[$r14 + #proc_qput]
401	xor $r8 #proc_qmaskb
402	cmp b32 $r8 $r9
403	bra e #send_done
404
405	// enqueue message
406	and $r8 $r9 #proc_qmaskp
407	shl b32 $r8 $r8 #proc_qlen
408	add b32 $r8 #proc_queue
409	add b32 $r8 $r14
410
411	ld b32 $r10 D[$r15 + #proc_id]
412	st b32 D[$r8 + #msg_process] $r10
413	st b32 D[$r8 + #msg_message] $r13
414	st b32 D[$r8 + #msg_data0] $r12
415	st b32 D[$r8 + #msg_data1] $r11
416
417	// increment PUT
418	add b32 $r9 1
419	and $r9 #proc_qmaskf
420	st b32 D[$r14 + #proc_qput] $r9
421	bset $flags $p2
422	send_done:
423	pop $r9
424	pop $r8
425	ret
426
427// lookup process structure by its name
428//
429// $r15 - current
430// $r14 - process name
431// $r0  - zero
432//
433// $r14 - process
434// $p1  - success
435find:
436	push $r8
437	mov $r8 #proc_list_head
438	bset $flags $p1
439	find_loop:
440		ld b32 $r10 D[$r8 + #proc_id]
441		cmp b32 $r10 $r14
442		bra e #find_done
443		add b32 $r8 #proc_size
444		cmp b32 $r8 #proc_list_tail
445		bra ne #find_loop
446		bclr $flags $p1
447	find_done:
448	mov b32 $r14 $r8
449	pop $r8
450	ret
451
452// send message to another process
453//
454// $r15 - current
455// $r14 - process id
456// $r13 - message
457// $r12 - message data 0
458// $r11 - message data 1
459// $r0  - zero
460send:
461	call(find)
462	bra $p1 #send_proc
463	ret
464
465// process single message for a given process
466//
467// $r15 - current
468// $r14 - process
469// $r0  - zero
470recv:
471	push $r9
472	push $r8
473
474	ld b32 $r8 D[$r14 + #proc_qget]
475	ld b32 $r9 D[$r14 + #proc_qput]
476	bclr $flags $p1
477	cmp b32 $r8 $r9
478	bra e #recv_done
479		// dequeue message
480		and $r9 $r8 #proc_qmaskp
481		add b32 $r8 1
482		and $r8 #proc_qmaskf
483		st b32 D[$r14 + #proc_qget] $r8
484		ld b32 $r10 D[$r14 + #proc_recv]
485
486		push $r15
487		mov $r15 $flags
488		push $r15
489		mov b32 $r15 $r14
490
491		shl b32 $r9 $r9 #proc_qlen
492		add b32 $r14 $r9
493		add b32 $r14 #proc_queue
494		ld b32 $r11 D[$r14 + #msg_data1]
495		ld b32 $r12 D[$r14 + #msg_data0]
496		ld b32 $r13 D[$r14 + #msg_message]
497		ld b32 $r14 D[$r14 + #msg_process]
498
499		// process it
500		call $r10
501		pop $r15
502		mov $flags $r15
503		bset $flags $p1
504		pop $r15
505	recv_done:
506	pop $r8
507	pop $r9
508	ret
509
510init:
511	// setup stack
512	nv_iord($r1, NV_PPWR_CAPS)
513	extr $r1 $r1 9:17
514	shl b32 $r1 8
515	mov $sp $r1
516
517#ifdef NVKM_FALCON_MMIO_UAS
518	// somehow allows the magic "access mmio via D[]" stuff that's
519	// used by the nv_rd32/nv_wr32 macros to work
520	mov $r1 0x0010
521	sethi $r1 NV_PPWR_UAS_CONFIG_ENABLE
522	nv_iowrs(NV_PPWR_UAS_CONFIG, $r1)
523#endif
524
525	// route all interrupts except user0/1 and pause to fuc
526	mov $r1 0x00e0
527	sethi $r1 0x00000000
528	nv_iowr(NV_PPWR_INTR_ROUTE, $r1)
529
530	// enable watchdog and subintr intrs
531	mov $r1 NV_PPWR_INTR_EN_CLR_MASK
532	nv_iowr(NV_PPWR_INTR_EN_CLR, $r1)
533	mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG
534	or $r1 NV_PPWR_INTR_EN_SET_SUBINTR
535	nv_iowr(NV_PPWR_INTR_EN_SET, $r1)
536
537	// enable interrupts globally
538	mov $r1 #intr
539	sethi $r1 0x00000000
540	mov $iv0 $r1
541	bset $flags ie0
542
543	// enable watchdog timer
544	mov $r1 1
545	nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1)
546
547	// bootstrap processes, idle process will be last, and not return
548	mov $r15 #proc_list_head
549	init_proc:
550		ld b32 $r1 D[$r15 + #proc_init]
551		cmp b32 $r1 0
552		bra z #init_proc
553		call $r1
554		add b32 $r15 #proc_size
555		bra #init_proc
556#endif
557