1198733Smarcel/*-
2198733Smarcel * Copyright (c) 2009 Marcel Moolenaar
3198733Smarcel * All rights reserved.
4198733Smarcel *
5198733Smarcel * Redistribution and use in source and binary forms, with or without
6198733Smarcel * modification, are permitted provided that the following conditions
7198733Smarcel * are met:
8198733Smarcel * 1. Redistributions of source code must retain the above copyright
9198733Smarcel *    notice, this list of conditions and the following disclaimer.
10198733Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11198733Smarcel *    notice, this list of conditions and the following disclaimer in the
12198733Smarcel *    documentation and/or other materials provided with the distribution.
13198733Smarcel *
14198733Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15198733Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16198733Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17198733Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18198733Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19198733Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20198733Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21198733Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22198733Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23198733Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24198733Smarcel * SUCH DAMAGE.
25198733Smarcel */
26198733Smarcel
27198733Smarcel#include <sys/cdefs.h>
28198733Smarcel__FBSDID("$FreeBSD$");
29198733Smarcel
30198733Smarcel#include <sys/param.h>
31198733Smarcel#include <sys/systm.h>
32198733Smarcel#include <sys/kernel.h>
33198733Smarcel#include <sys/lock.h>
34198733Smarcel#include <sys/mutex.h>
35198733Smarcel#include <sys/proc.h>
36198733Smarcel
37198733Smarcel#include <machine/frame.h>
38198733Smarcel#include <machine/md_var.h>
39198733Smarcel#include <machine/smp.h>
40198733Smarcel
41198733Smarcelstatic struct mtx ia64_highfp_mtx;
42198733Smarcel
43198733Smarcelstatic void
44198733Smarcelia64_highfp_init(void *_)
45198733Smarcel{
46198733Smarcel	mtx_init(&ia64_highfp_mtx, "High FP lock", NULL, MTX_SPIN);
47198733Smarcel}
48198733SmarcelSYSINIT(ia64_highfp_init, SI_SUB_LOCK, SI_ORDER_ANY, ia64_highfp_init, NULL);
49198733Smarcel
50198733Smarcel#ifdef SMP
51198733Smarcelstatic int
52198733Smarcelia64_highfp_ipi(struct pcpu *cpu)
53198733Smarcel{
54198733Smarcel	int error;
55198733Smarcel
56205234Smarcel	ipi_send(cpu, ia64_ipi_highfp);
57198733Smarcel	error = msleep_spin(&cpu->pc_fpcurthread, &ia64_highfp_mtx,
58198733Smarcel	    "High FP", 0);
59198733Smarcel	return (error);
60198733Smarcel}
61198733Smarcel#endif
62198733Smarcel
63198733Smarcelint
64198733Smarcelia64_highfp_drop(struct thread *td)
65198733Smarcel{
66198733Smarcel	struct pcb *pcb;
67198733Smarcel	struct pcpu *cpu;
68198733Smarcel
69198733Smarcel	pcb = td->td_pcb;
70198733Smarcel
71198733Smarcel	mtx_lock_spin(&ia64_highfp_mtx);
72198733Smarcel	cpu = pcb->pcb_fpcpu;
73198733Smarcel	if (cpu != NULL) {
74198733Smarcel		KASSERT(cpu->pc_fpcurthread == td,
75198733Smarcel		    ("cpu->pc_fpcurthread != td"));
76198733Smarcel		td->td_frame->tf_special.psr |= IA64_PSR_DFH;
77198733Smarcel		pcb->pcb_fpcpu = NULL;
78198733Smarcel		cpu->pc_fpcurthread = NULL;
79198733Smarcel	}
80198733Smarcel	mtx_unlock_spin(&ia64_highfp_mtx);
81198733Smarcel
82198733Smarcel	return ((cpu != NULL) ? 1 : 0);
83198733Smarcel}
84198733Smarcel
85198733Smarcelint
86198733Smarcelia64_highfp_enable(struct thread *td, struct trapframe *tf)
87198733Smarcel{
88198733Smarcel	struct pcb *pcb;
89198733Smarcel	struct pcpu *cpu;
90198733Smarcel	struct thread *td1;
91198733Smarcel
92198733Smarcel	pcb = td->td_pcb;
93198733Smarcel
94198733Smarcel	mtx_lock_spin(&ia64_highfp_mtx);
95198733Smarcel	cpu = pcb->pcb_fpcpu;
96198733Smarcel#ifdef SMP
97198733Smarcel	if (cpu != NULL && cpu != pcpup) {
98198733Smarcel		KASSERT(cpu->pc_fpcurthread == td,
99198733Smarcel		    ("cpu->pc_fpcurthread != td"));
100198733Smarcel		ia64_highfp_ipi(cpu);
101198733Smarcel	}
102198733Smarcel#endif
103198733Smarcel	td1 = PCPU_GET(fpcurthread);
104198733Smarcel	if (td1 != NULL && td1 != td) {
105198733Smarcel		KASSERT(td1->td_pcb->pcb_fpcpu == pcpup,
106198733Smarcel		    ("td1->td_pcb->pcb_fpcpu != pcpup"));
107198733Smarcel		save_high_fp(&td1->td_pcb->pcb_high_fp);
108198733Smarcel		td1->td_frame->tf_special.psr |= IA64_PSR_DFH;
109198733Smarcel		td1->td_pcb->pcb_fpcpu = NULL;
110198733Smarcel		PCPU_SET(fpcurthread, NULL);
111198733Smarcel		td1 = NULL;
112198733Smarcel	}
113198733Smarcel	if (td1 == NULL) {
114198733Smarcel		KASSERT(pcb->pcb_fpcpu == NULL, ("pcb->pcb_fpcpu != NULL"));
115198733Smarcel		KASSERT(PCPU_GET(fpcurthread) == NULL,
116198733Smarcel		    ("PCPU_GET(fpcurthread) != NULL"));
117198733Smarcel		restore_high_fp(&pcb->pcb_high_fp);
118198733Smarcel		PCPU_SET(fpcurthread, td);
119198733Smarcel		pcb->pcb_fpcpu = pcpup;
120198733Smarcel		tf->tf_special.psr &= ~IA64_PSR_MFH;
121198733Smarcel	}
122198733Smarcel	tf->tf_special.psr &= ~IA64_PSR_DFH;
123198733Smarcel	mtx_unlock_spin(&ia64_highfp_mtx);
124198733Smarcel
125198733Smarcel	return ((td1 != NULL) ? 1 : 0);
126198733Smarcel}
127198733Smarcel
128198733Smarcelint
129198733Smarcelia64_highfp_save(struct thread *td)
130198733Smarcel{
131198733Smarcel	struct pcb *pcb;
132198733Smarcel	struct pcpu *cpu;
133198733Smarcel
134198733Smarcel	pcb = td->td_pcb;
135198733Smarcel
136198733Smarcel	mtx_lock_spin(&ia64_highfp_mtx);
137198733Smarcel	cpu = pcb->pcb_fpcpu;
138198733Smarcel#ifdef SMP
139198733Smarcel	if (cpu != NULL && cpu != pcpup) {
140198733Smarcel		KASSERT(cpu->pc_fpcurthread == td,
141198733Smarcel		    ("cpu->pc_fpcurthread != td"));
142198733Smarcel		ia64_highfp_ipi(cpu);
143198733Smarcel	} else
144198733Smarcel#endif
145198733Smarcel	if (cpu != NULL) {
146198733Smarcel		KASSERT(cpu->pc_fpcurthread == td,
147198733Smarcel		    ("cpu->pc_fpcurthread != td"));
148198733Smarcel		save_high_fp(&pcb->pcb_high_fp);
149198733Smarcel		td->td_frame->tf_special.psr |= IA64_PSR_DFH;
150198733Smarcel		pcb->pcb_fpcpu = NULL;
151198733Smarcel		cpu->pc_fpcurthread = NULL;
152198733Smarcel	}
153198733Smarcel	mtx_unlock_spin(&ia64_highfp_mtx);
154198733Smarcel
155198733Smarcel	return ((cpu != NULL) ? 1 : 0);
156198733Smarcel}
157198733Smarcel
158198733Smarcel#ifdef SMP
159198733Smarcelint
160198733Smarcelia64_highfp_save_ipi(void)
161198733Smarcel{
162198733Smarcel	struct thread *td;
163198733Smarcel
164198733Smarcel	mtx_lock_spin(&ia64_highfp_mtx);
165198733Smarcel	td = PCPU_GET(fpcurthread);
166198733Smarcel	if (td != NULL) {
167198733Smarcel		KASSERT(td->td_pcb->pcb_fpcpu == pcpup,
168198733Smarcel		    ("td->td_pcb->pcb_fpcpu != pcpup"));
169198733Smarcel		save_high_fp(&td->td_pcb->pcb_high_fp);
170198733Smarcel		td->td_frame->tf_special.psr |= IA64_PSR_DFH;
171198733Smarcel		td->td_pcb->pcb_fpcpu = NULL;
172198733Smarcel		PCPU_SET(fpcurthread, NULL);
173198733Smarcel	}
174198733Smarcel	mtx_unlock_spin(&ia64_highfp_mtx);
175198733Smarcel	wakeup(&PCPU_GET(fpcurthread));
176198733Smarcel
177198733Smarcel	return ((td != NULL) ? 1 : 0);
178198733Smarcel}
179198733Smarcel#endif
180