1243791Sdim/*-
2243791Sdim * Copyright (C) 1996 Wolfgang Solfrank.
3243791Sdim * Copyright (C) 1996 TooLs GmbH.
4243791Sdim * All rights reserved.
5243791Sdim *
6243791Sdim * Redistribution and use in source and binary forms, with or without
7243791Sdim * modification, are permitted provided that the following conditions
8243791Sdim * are met:
9243791Sdim * 1. Redistributions of source code must retain the above copyright
10243791Sdim *    notice, this list of conditions and the following disclaimer.
11243791Sdim * 2. Redistributions in binary form must reproduce the above copyright
12243791Sdim *    notice, this list of conditions and the following disclaimer in the
13243791Sdim *    documentation and/or other materials provided with the distribution.
14243791Sdim * 3. All advertising materials mentioning features or use of this software
15243791Sdim *    must display the following acknowledgement:
16243791Sdim *	This product includes software developed by TooLs GmbH.
17243791Sdim * 4. The name of TooLs GmbH may not be used to endorse or promote products
18243791Sdim *    derived from this software without specific prior written permission.
19243791Sdim *
20243791Sdim * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21243791Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22243791Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23243791Sdim * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24243791Sdim * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25243791Sdim * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26243791Sdim * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27243791Sdim * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28243791Sdim * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29243791Sdim * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30243791Sdim *
31243791Sdim *	$NetBSD: fpu.c,v 1.5 2001/07/22 11:29:46 wiz Exp $
32243791Sdim */
33243791Sdim
34243791Sdim#include <sys/cdefs.h>
35243791Sdim__FBSDID("$FreeBSD: releng/10.3/sys/powerpc/powerpc/altivec.c 188860 2009-02-20 17:48:40Z nwhitehorn $");
36243791Sdim
37243791Sdim#include <sys/param.h>
38243791Sdim#include <sys/proc.h>
39243791Sdim#include <sys/systm.h>
40243791Sdim#include <sys/limits.h>
41243791Sdim
42243791Sdim#include <machine/altivec.h>
43243791Sdim#include <machine/pcb.h>
44243791Sdim#include <machine/psl.h>
45243791Sdim
46243791Sdimvoid
47243791Sdimenable_vec(struct thread *td)
48243791Sdim{
49243791Sdim	int	msr;
50243791Sdim	struct	pcb *pcb;
51243791Sdim	struct	trapframe *tf;
52243791Sdim
53243791Sdim	pcb = td->td_pcb;
54243791Sdim	tf = trapframe(td);
55243791Sdim
56243791Sdim	/*
57243791Sdim	 * Save the thread's Altivec CPU number, and set the CPU's current
58243791Sdim	 * vector thread
59243791Sdim	 */
60243791Sdim	td->td_pcb->pcb_veccpu = PCPU_GET(cpuid);
61243791Sdim	PCPU_SET(vecthread, td);
62243791Sdim
63243791Sdim	/*
64243791Sdim	 * Enable the vector unit for when the thread returns from the
65243791Sdim	 * exception. If this is the first time the unit has been used by
66243791Sdim	 * the thread, initialise the vector registers and VSCR to 0, and
67243791Sdim	 * set the flag to indicate that the vector unit is in use.
68243791Sdim	 */
69243791Sdim	tf->srr1 |= PSL_VEC;
70243791Sdim	if (!(pcb->pcb_flags & PCB_VEC)) {
71243791Sdim		memset(&pcb->pcb_vec, 0, sizeof pcb->pcb_vec);
72243791Sdim		pcb->pcb_flags |= PCB_VEC;
73243791Sdim	}
74243791Sdim
75243791Sdim	/*
76243791Sdim	 * Temporarily enable the vector unit so the registers
77243791Sdim	 * can be restored.
78243791Sdim	 */
79243791Sdim	msr = mfmsr();
80243791Sdim	mtmsr(msr | PSL_VEC);
81243791Sdim	isync();
82243791Sdim
83243791Sdim	/*
84243791Sdim	 * Restore VSCR by first loading it into a vector and then into VSCR.
85243791Sdim	 * (this needs to done before loading the user's vector registers
86243791Sdim	 * since we need to use a scratch vector register)
87243791Sdim	 */
88243791Sdim	__asm __volatile("vxor 0,0,0; lvewx 0,0,%0; mtvscr 0" \
89243791Sdim			  :: "b"(&pcb->pcb_vec.vscr));
90243791Sdim
91243791Sdim#define LVX(n)   __asm ("lvx " #n ",0,%0" \
92243791Sdim		:: "b"(&pcb->pcb_vec.vr[n]));
93243791Sdim	LVX(0);		LVX(1);		LVX(2);		LVX(3);
94243791Sdim	LVX(4);		LVX(5);		LVX(6);		LVX(7);
95243791Sdim	LVX(8);		LVX(9);		LVX(10);	LVX(11);
96243791Sdim	LVX(12);	LVX(13);	LVX(14);	LVX(15);
97243791Sdim	LVX(16);	LVX(17);	LVX(18);	LVX(19);
98243791Sdim	LVX(20);	LVX(21);	LVX(22);	LVX(23);
99243791Sdim	LVX(24);	LVX(25);	LVX(26);	LVX(27);
100243791Sdim	LVX(28);	LVX(29);	LVX(30);	LVX(31);
101243791Sdim#undef LVX
102243791Sdim
103243791Sdim	isync();
104243791Sdim	mtmsr(msr);
105243791Sdim}
106243791Sdim
107243791Sdimvoid
108243791Sdimsave_vec(struct thread *td)
109243791Sdim{
110243791Sdim	int	msr;
111243791Sdim	struct	pcb *pcb;
112243791Sdim
113243791Sdim	pcb = td->td_pcb;
114243791Sdim
115243791Sdim	/*
116243791Sdim	 * Temporarily re-enable the vector unit during the save
117243791Sdim	 */
118243791Sdim	msr = mfmsr();
119243791Sdim	mtmsr(msr | PSL_VEC);
120243791Sdim	isync();
121243791Sdim
122243791Sdim	/*
123243791Sdim	 * Save the vector registers and VSCR to the PCB
124243791Sdim	 */
125243791Sdim#define STVX(n)   __asm ("stvx %1,0,%0" \
126243791Sdim		:: "b"(pcb->pcb_vec.vr[n]), "n"(n));
127243791Sdim	STVX(0);	STVX(1);	STVX(2);	STVX(3);
128243791Sdim	STVX(4);	STVX(5);	STVX(6);	STVX(7);
129243791Sdim	STVX(8);	STVX(9);	STVX(10);	STVX(11);
130243791Sdim	STVX(12);	STVX(13);	STVX(14);	STVX(15);
131243791Sdim	STVX(16);	STVX(17);	STVX(18);	STVX(19);
132243791Sdim	STVX(20);	STVX(21);	STVX(22);	STVX(23);
133243791Sdim	STVX(24);	STVX(25);	STVX(26);	STVX(27);
134243791Sdim	STVX(28);	STVX(29);	STVX(30);	STVX(31);
135243791Sdim#undef STVX
136243791Sdim
137243791Sdim	__asm __volatile("mfvscr 0; stvewx 0,0,%0" :: "b"(&pcb->pcb_vec.vscr));
138243791Sdim
139243791Sdim	/*
140243791Sdim	 * Disable vector unit again
141243791Sdim	 */
142243791Sdim	isync();
143243791Sdim	mtmsr(msr);
144243791Sdim
145243791Sdim	/*
146243791Sdim	 * Clear the current vec thread and pcb's CPU id
147243791Sdim	 * XXX should this be left clear to allow lazy save/restore ?
148243791Sdim	 */
149243791Sdim	pcb->pcb_veccpu = INT_MAX;
150243791Sdim	PCPU_SET(vecthread, NULL);
151243791Sdim}
152243791Sdim
153243791Sdim