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	mov $r9 (NV_PPWR_INTR_USER0 | NV_PPWR_INTR_USER1 | NV_PPWR_INTR_PAUSE)
229	not b32 $r9
230	and $r8 $r9
231	nv_iowr(NV_PPWR_INTR_ACK, $r8)
232
233	pop $r8
234	mov $flags $r8
235	pop $r15
236	pop $r14
237	pop $r13
238	pop $r12
239	pop $r11
240	pop $r10
241	pop $r9
242	pop $r8
243	pop $r0
244	bclr $flags $p0
245	iret
246
247// calculate the number of ticks in the specified nanoseconds delay
248//
249// $r15 - current
250// $r14 - ns
251// $r14 - ticks (return)
252// $r0  - zero
253ticks_from_ns:
254	push $r12
255	push $r11
256
257	/* try not losing precision (multiply then divide) */
258	imm32($r13, HW_TICKS_PER_US)
259	call #mulu32_32_64
260
261	/* use an immeditate, it's ok because HW_TICKS_PER_US < 16 bits */
262	div $r12 $r12 1000
263
264	/* check if there wasn't any overflow */
265	cmpu b32 $r11 0
266	bra e #ticks_from_ns_quit
267
268	/* let's divide then multiply, too bad for the precision! */
269	div $r14 $r14 1000
270	imm32($r13, HW_TICKS_PER_US)
271	call #mulu32_32_64
272
273	/* this cannot overflow as long as HW_TICKS_PER_US < 1000 */
274
275ticks_from_ns_quit:
276	mov b32 $r14 $r12
277	pop $r11
278	pop $r12
279	ret
280
281// calculate the number of ticks in the specified microsecond delay
282//
283// $r15 - current
284// $r14 - us
285// $r14 - ticks (return)
286// $r0  - zero
287ticks_from_us:
288	push $r12
289	push $r11
290
291	/* simply multiply $us by HW_TICKS_PER_US */
292	imm32($r13, HW_TICKS_PER_US)
293	call #mulu32_32_64
294	mov b32 $r14 $r12
295
296	/* check if there wasn't any overflow */
297	cmpu b32 $r11 0
298	bra e #ticks_from_us_quit
299
300	/* Overflow! */
301	clear b32 $r14
302
303ticks_from_us_quit:
304	pop $r11
305	pop $r12
306	ret
307
308// calculate the number of ticks in the specified microsecond delay
309//
310// $r15 - current
311// $r14 - ticks
312// $r14 - us (return)
313// $r0  - zero
314ticks_to_us:
315	/* simply divide $ticks by HW_TICKS_PER_US */
316	imm32($r13, HW_TICKS_PER_US)
317	div $r14 $r14 $r13
318
319	ret
320
321// request the current process be sent a message after a timeout expires
322//
323// $r15 - current
324// $r14 - ticks (make sure it is < 2^31 to avoid any possible overflow)
325// $r0  - zero
326timer:
327	push $r9
328	push $r8
329
330	// interrupts off to prevent racing with timer isr
331	bclr $flags ie0
332
333	// if current process already has a timer set, bail
334	ld b32 $r8 D[$r15 + #proc_time]
335	cmp b32 $r8 0
336	bra g #timer_done
337
338	// halt watchdog timer temporarily
339	clear b32 $r8
340	nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
341
342	// find out how much time elapsed since the last update
343	// of the watchdog and add this time to the wanted ticks
344	nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
345	ld b32 $r9 D[$r0 + #time_prev]
346	sub b32 $r9 $r8
347	add b32 $r14 $r9
348	st b32 D[$r15 + #proc_time] $r14
349
350	// check for a pending interrupt.  if there's one already
351	// pending, we can just bail since the timer isr will
352	// queue the next soonest right after it's done
353	nv_iord($r8, NV_PPWR_INTR)
354	and $r8 NV_PPWR_INTR_WATCHDOG
355	bra nz #timer_enable
356
357	// update the watchdog if this timer should expire first,
358	// or if there's no timeout already set
359	nv_iord($r8, NV_PPWR_WATCHDOG_TIME)
360	cmp b32 $r14 $r0
361	bra e #timer_reset
362	cmp b32 $r14 $r8
363	bra g #timer_enable
364		timer_reset:
365		nv_iowr(NV_PPWR_WATCHDOG_TIME, $r14)
366		st b32 D[$r0 + #time_prev] $r14
367
368	// re-enable the watchdog timer
369	timer_enable:
370	mov $r8 1
371	nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r8)
372
373	// interrupts back on
374	timer_done:
375	bset $flags ie0
376
377	pop $r8
378	pop $r9
379	ret
380
381// send message to another process
382//
383// $r15 - current
384// $r14 - process
385// $r13 - message
386// $r12 - message data 0
387// $r11 - message data 1
388// $r0  - zero
389send_proc:
390	push $r8
391	push $r9
392	// check for space in queue
393	ld b32 $r8 D[$r14 + #proc_qget]
394	ld b32 $r9 D[$r14 + #proc_qput]
395	xor $r8 #proc_qmaskb
396	cmp b32 $r8 $r9
397	bra e #send_done
398
399	// enqueue message
400	and $r8 $r9 #proc_qmaskp
401	shl b32 $r8 $r8 #proc_qlen
402	add b32 $r8 #proc_queue
403	add b32 $r8 $r14
404
405	ld b32 $r10 D[$r15 + #proc_id]
406	st b32 D[$r8 + #msg_process] $r10
407	st b32 D[$r8 + #msg_message] $r13
408	st b32 D[$r8 + #msg_data0] $r12
409	st b32 D[$r8 + #msg_data1] $r11
410
411	// increment PUT
412	add b32 $r9 1
413	and $r9 #proc_qmaskf
414	st b32 D[$r14 + #proc_qput] $r9
415	bset $flags $p2
416	send_done:
417	pop $r9
418	pop $r8
419	ret
420
421// lookup process structure by its name
422//
423// $r15 - current
424// $r14 - process name
425// $r0  - zero
426//
427// $r14 - process
428// $p1  - success
429find:
430	push $r8
431	mov $r8 #proc_list_head
432	bset $flags $p1
433	find_loop:
434		ld b32 $r10 D[$r8 + #proc_id]
435		cmp b32 $r10 $r14
436		bra e #find_done
437		add b32 $r8 #proc_size
438		cmp b32 $r8 #proc_list_tail
439		bra ne #find_loop
440		bclr $flags $p1
441	find_done:
442	mov b32 $r14 $r8
443	pop $r8
444	ret
445
446// send message to another process
447//
448// $r15 - current
449// $r14 - process id
450// $r13 - message
451// $r12 - message data 0
452// $r11 - message data 1
453// $r0  - zero
454send:
455	call(find)
456	bra $p1 #send_proc
457	ret
458
459// process single message for a given process
460//
461// $r15 - current
462// $r14 - process
463// $r0  - zero
464recv:
465	push $r9
466	push $r8
467
468	ld b32 $r8 D[$r14 + #proc_qget]
469	ld b32 $r9 D[$r14 + #proc_qput]
470	bclr $flags $p1
471	cmp b32 $r8 $r9
472	bra e #recv_done
473		// dequeue message
474		and $r9 $r8 #proc_qmaskp
475		add b32 $r8 1
476		and $r8 #proc_qmaskf
477		st b32 D[$r14 + #proc_qget] $r8
478		ld b32 $r10 D[$r14 + #proc_recv]
479
480		push $r15
481		mov $r15 $flags
482		push $r15
483		mov b32 $r15 $r14
484
485		shl b32 $r9 $r9 #proc_qlen
486		add b32 $r14 $r9
487		add b32 $r14 #proc_queue
488		ld b32 $r11 D[$r14 + #msg_data1]
489		ld b32 $r12 D[$r14 + #msg_data0]
490		ld b32 $r13 D[$r14 + #msg_message]
491		ld b32 $r14 D[$r14 + #msg_process]
492
493		// process it
494		call $r10
495		pop $r15
496		mov $flags $r15
497		bset $flags $p1
498		pop $r15
499	recv_done:
500	pop $r8
501	pop $r9
502	ret
503
504init:
505	// setup stack
506	nv_iord($r1, NV_PPWR_CAPS)
507	extr $r1 $r1 9:17
508	shl b32 $r1 8
509	mov $sp $r1
510
511#ifdef NVKM_FALCON_MMIO_UAS
512	// somehow allows the magic "access mmio via D[]" stuff that's
513	// used by the nv_rd32/nv_wr32 macros to work
514	mov $r1 0x0010
515	sethi $r1 NV_PPWR_UAS_CONFIG_ENABLE
516	nv_iowrs(NV_PPWR_UAS_CONFIG, $r1)
517#endif
518
519	// route all interrupts except user0/1 and pause to fuc
520	mov $r1 0x00e0
521	sethi $r1 0x00000000
522	nv_iowr(NV_PPWR_INTR_ROUTE, $r1)
523
524	// enable watchdog and subintr intrs
525	mov $r1 NV_PPWR_INTR_EN_CLR_MASK
526	nv_iowr(NV_PPWR_INTR_EN_CLR, $r1)
527	mov $r1 NV_PPWR_INTR_EN_SET_WATCHDOG
528	or $r1 NV_PPWR_INTR_EN_SET_SUBINTR
529	nv_iowr(NV_PPWR_INTR_EN_SET, $r1)
530
531	// enable interrupts globally
532	mov $r1 #intr
533	sethi $r1 0x00000000
534	mov $iv0 $r1
535	bset $flags ie0
536
537	// enable watchdog timer
538	mov $r1 1
539	nv_iowr(NV_PPWR_WATCHDOG_ENABLE, $r1)
540
541	// bootstrap processes, idle process will be last, and not return
542	mov $r15 #proc_list_head
543	init_proc:
544		ld b32 $r1 D[$r15 + #proc_init]
545		cmp b32 $r1 0
546		bra z #init_proc
547		call $r1
548		add b32 $r15 #proc_size
549		bra #init_proc
550#endif
551