1 /* $NetBSD: mips_dsp.c,v 1.6 2017/05/07 05:45:07 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2011 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Thomas of 3am Software Foundry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: mips_dsp.c,v 1.6 2017/05/07 05:45:07 skrll Exp $");
34
35 #include <sys/param.h>
36 #include <sys/mutex.h>
37 #include <sys/condvar.h>
38 #include <sys/cpu.h>
39 #include <sys/proc.h>
40 #include <sys/lwp.h>
41 #include <sys/pcu.h>
42
43 #include <mips/locore.h>
44 #include <mips/regnum.h>
45 #include <mips/pcb.h>
46
47 static void mips_dsp_state_save(lwp_t *);
48 static void mips_dsp_state_load(lwp_t *, u_int);
49 static void mips_dsp_state_release(lwp_t *);
50
51 const pcu_ops_t mips_dsp_ops = {
52 .pcu_id = PCU_DSP,
53 .pcu_state_save = mips_dsp_state_save,
54 .pcu_state_load = mips_dsp_state_load,
55 .pcu_state_release = mips_dsp_state_release
56 };
57
58 void
dsp_discard(lwp_t * l)59 dsp_discard(lwp_t *l)
60 {
61 pcu_discard(&mips_dsp_ops, l, false);
62 }
63
64 void
dsp_load(void)65 dsp_load(void)
66 {
67 pcu_load(&mips_dsp_ops);
68 }
69
70 void
dsp_save(lwp_t * l)71 dsp_save(lwp_t *l)
72 {
73 pcu_save(&mips_dsp_ops, l);
74 }
75
76 bool
dsp_used_p(const lwp_t * l)77 dsp_used_p(const lwp_t *l)
78 {
79 return pcu_valid_p(&mips_dsp_ops, l);
80 }
81
82 void
mips_dsp_state_save(lwp_t * l)83 mips_dsp_state_save(lwp_t *l)
84 {
85 struct trapframe * const tf = l->l_md.md_utf;
86 struct pcb * const pcb = lwp_getpcb(l);
87 mips_reg_t * const dsp = pcb->pcb_dspregs.r_regs;
88 uint32_t status;
89
90 /*
91 * Don't do anything if the DSP is already off.
92 */
93 if ((tf->tf_regs[_R_SR] & MIPS_SR_MX) == 0)
94 return;
95
96 l->l_cpu->ci_ev_dsp_saves.ev_count++;
97
98 /*
99 * load DSP registers and establish lwp's DSP context.
100 */
101 __asm volatile (
102 ".set push" "\n\t"
103 ".set mips32r2" "\n\t"
104 ".set dspr2" "\n\t"
105 ".set noat" "\n\t"
106 ".set noreorder" "\n\t"
107 "mfc0 %[status], $%[cp0_status]" "\n\t"
108 "or %[status], %[mips_sr_mx]" "\n\t"
109 "mtc0 %[status], $%[cp0_status]" "\n\t"
110 "ehb" "\n\t"
111 "mflo %[mullo1], $ac1" "\n\t"
112 "mfhi %[mulhi1], $ac1" "\n\t"
113 "mflo %[mullo2], $ac2" "\n\t"
114 "mfhi %[mulhi2], $ac2" "\n\t"
115 "mflo %[mullo3], $ac3" "\n\t"
116 "mfhi %[mulhi3], $ac3" "\n\t"
117 "rddsp %[dspctl]" "\n\t"
118 "xor %[status], %[mips_sr_mx]" "\n\t"
119 "mtc0 %[status], $%[cp0_status]" "\n\t"
120 "ehb" "\n\t"
121 ".set pop"
122 : [status] "=&r" (status),
123 [mullo1] "=r"(dsp[_R_MULLO1 - _R_DSPBASE]),
124 [mulhi1] "=r"(dsp[_R_MULHI1 - _R_DSPBASE]),
125 [mullo2] "=r"(dsp[_R_MULLO2 - _R_DSPBASE]),
126 [mulhi2] "=r"(dsp[_R_MULHI2 - _R_DSPBASE]),
127 [mullo3] "=r"(dsp[_R_MULLO3 - _R_DSPBASE]),
128 [mulhi3] "=r"(dsp[_R_MULHI3 - _R_DSPBASE]),
129 [dspctl] "=r"(dsp[_R_DSPCTL - _R_DSPBASE])
130 : [mips_sr_mx] "r"(MIPS_SR_MX),
131 [cp0_status] "n"(MIPS_COP_0_STATUS));
132 }
133
134 void
mips_dsp_state_load(lwp_t * l,u_int flags)135 mips_dsp_state_load(lwp_t *l, u_int flags)
136 {
137 struct trapframe * const tf = l->l_md.md_utf;
138 struct pcb * const pcb = lwp_getpcb(l);
139 mips_reg_t * const dsp = pcb->pcb_dspregs.r_regs;
140 uint32_t status;
141
142 l->l_cpu->ci_ev_dsp_loads.ev_count++;
143
144 /*
145 * If this is the first time the state is being loaded, zero it first.
146 */
147 if (__predict_false((flags & PCU_VALID) == 0)) {
148 memset(&pcb->pcb_dspregs, 0, sizeof(pcb->pcb_dspregs));
149 }
150
151 /*
152 * Enable the DSP when this lwp return to userspace.
153 */
154 tf->tf_regs[_R_SR] |= MIPS_SR_MX;
155
156 /*
157 * load DSP registers and establish lwp's DSP context.
158 */
159 __asm volatile (
160 ".set push" "\n\t"
161 ".set mips32r2" "\n\t"
162 ".set dspr2" "\n\t"
163 ".set noat" "\n\t"
164 ".set noreorder" "\n\t"
165 "mfc0 %[status], $%[cp0_status]" "\n\t"
166 "or %[status], %[mips_sr_mx]" "\n\t"
167 "mtc0 %[status], $%[cp0_status]" "\n\t"
168 "ehb" "\n\t"
169 "mtlo %[mullo1], $ac1" "\n\t"
170 "mthi %[mulhi1], $ac1" "\n\t"
171 "mtlo %[mullo2], $ac2" "\n\t"
172 "mthi %[mulhi2], $ac2" "\n\t"
173 "mtlo %[mullo3], $ac3" "\n\t"
174 "mthi %[mulhi3], $ac3" "\n\t"
175 "wrdsp %[dspctl]" "\n\t"
176 "xor %[status], %[mips_sr_mx]" "\n\t"
177 "mtc0 %[status], $%[cp0_status]" "\n\t"
178 "ehb" "\n\t"
179 ".set pop"
180 : [status] "=&r" (status)
181 : [mullo1] "r"(dsp[_R_MULLO1 - _R_DSPBASE]),
182 [mulhi1] "r"(dsp[_R_MULHI1 - _R_DSPBASE]),
183 [mullo2] "r"(dsp[_R_MULLO2 - _R_DSPBASE]),
184 [mulhi2] "r"(dsp[_R_MULHI2 - _R_DSPBASE]),
185 [mullo3] "r"(dsp[_R_MULLO3 - _R_DSPBASE]),
186 [mulhi3] "r"(dsp[_R_MULHI3 - _R_DSPBASE]),
187 [dspctl] "r"(dsp[_R_DSPCTL - _R_DSPBASE]),
188 [mips_sr_mx] "r"(MIPS_SR_MX),
189 [cp0_status] "n"(MIPS_COP_0_STATUS));
190 }
191
192 void
mips_dsp_state_release(lwp_t * l)193 mips_dsp_state_release(lwp_t *l)
194 {
195 KASSERT(l == curlwp);
196 l->l_md.md_utf->tf_regs[_R_SR] &= ~MIPS_SR_MX;
197 }
198