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