1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright 2011 Advanced Micro Devices, Inc.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining a
5*4882a593Smuzhiyun * copy of this software and associated documentation files (the "Software"),
6*4882a593Smuzhiyun * to deal in the Software without restriction, including without limitation
7*4882a593Smuzhiyun * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*4882a593Smuzhiyun * and/or sell copies of the Software, and to permit persons to whom the
9*4882a593Smuzhiyun * Software is furnished to do so, subject to the following conditions:
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * The above copyright notice and this permission notice shall be included in
12*4882a593Smuzhiyun * all copies or substantial portions of the Software.
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*4882a593Smuzhiyun * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17*4882a593Smuzhiyun * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18*4882a593Smuzhiyun * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19*4882a593Smuzhiyun * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20*4882a593Smuzhiyun * OTHER DEALINGS IN THE SOFTWARE.
21*4882a593Smuzhiyun *
22*4882a593Smuzhiyun * Authors: Alex Deucher
23*4882a593Smuzhiyun */
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include <linux/firmware.h>
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #include "radeon.h"
28*4882a593Smuzhiyun #include "rv770d.h"
29*4882a593Smuzhiyun #include "rv770_dpm.h"
30*4882a593Smuzhiyun #include "rv770_smc.h"
31*4882a593Smuzhiyun #include "atom.h"
32*4882a593Smuzhiyun #include "radeon_ucode.h"
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define FIRST_SMC_INT_VECT_REG 0xFFD8
35*4882a593Smuzhiyun #define FIRST_INT_VECT_S19 0xFFC0
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun static const u8 rv770_smc_int_vectors[] =
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
40*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
41*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
42*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
43*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
44*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
45*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
46*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
47*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
48*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
49*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
50*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
51*4882a593Smuzhiyun 0x08, 0x10, 0x0C, 0xD7,
52*4882a593Smuzhiyun 0x08, 0x2B, 0x08, 0x10,
53*4882a593Smuzhiyun 0x03, 0x51, 0x03, 0x51,
54*4882a593Smuzhiyun 0x03, 0x51, 0x03, 0x51
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun static const u8 rv730_smc_int_vectors[] =
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
60*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
61*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
62*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
63*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
64*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
65*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
66*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
67*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
68*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
69*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
70*4882a593Smuzhiyun 0x08, 0x15, 0x08, 0x15,
71*4882a593Smuzhiyun 0x08, 0x15, 0x0C, 0xBB,
72*4882a593Smuzhiyun 0x08, 0x30, 0x08, 0x15,
73*4882a593Smuzhiyun 0x03, 0x56, 0x03, 0x56,
74*4882a593Smuzhiyun 0x03, 0x56, 0x03, 0x56
75*4882a593Smuzhiyun };
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun static const u8 rv710_smc_int_vectors[] =
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
80*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
81*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
82*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
83*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
84*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
85*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
86*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
87*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
88*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
89*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
90*4882a593Smuzhiyun 0x08, 0x04, 0x08, 0x04,
91*4882a593Smuzhiyun 0x08, 0x04, 0x0C, 0xCB,
92*4882a593Smuzhiyun 0x08, 0x1F, 0x08, 0x04,
93*4882a593Smuzhiyun 0x03, 0x51, 0x03, 0x51,
94*4882a593Smuzhiyun 0x03, 0x51, 0x03, 0x51
95*4882a593Smuzhiyun };
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun static const u8 rv740_smc_int_vectors[] =
98*4882a593Smuzhiyun {
99*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
100*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
101*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
102*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
103*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
104*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
105*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
106*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
107*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
108*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
109*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
110*4882a593Smuzhiyun 0x08, 0x10, 0x08, 0x10,
111*4882a593Smuzhiyun 0x08, 0x10, 0x0C, 0xD7,
112*4882a593Smuzhiyun 0x08, 0x2B, 0x08, 0x10,
113*4882a593Smuzhiyun 0x03, 0x51, 0x03, 0x51,
114*4882a593Smuzhiyun 0x03, 0x51, 0x03, 0x51
115*4882a593Smuzhiyun };
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun static const u8 cedar_smc_int_vectors[] =
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
120*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
121*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
122*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
123*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
124*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
125*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
126*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
127*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
128*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
129*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
130*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
131*4882a593Smuzhiyun 0x0B, 0x05, 0x11, 0x8B,
132*4882a593Smuzhiyun 0x0B, 0x20, 0x0B, 0x05,
133*4882a593Smuzhiyun 0x04, 0xF6, 0x04, 0xF6,
134*4882a593Smuzhiyun 0x04, 0xF6, 0x04, 0xF6
135*4882a593Smuzhiyun };
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun static const u8 redwood_smc_int_vectors[] =
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
140*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
141*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
142*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
143*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
144*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
145*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
146*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
147*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
148*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
149*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
150*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
151*4882a593Smuzhiyun 0x0B, 0x05, 0x11, 0x8B,
152*4882a593Smuzhiyun 0x0B, 0x20, 0x0B, 0x05,
153*4882a593Smuzhiyun 0x04, 0xF6, 0x04, 0xF6,
154*4882a593Smuzhiyun 0x04, 0xF6, 0x04, 0xF6
155*4882a593Smuzhiyun };
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun static const u8 juniper_smc_int_vectors[] =
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
160*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
161*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
162*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
163*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
164*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
165*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
166*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
167*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
168*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
169*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
170*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
171*4882a593Smuzhiyun 0x0B, 0x05, 0x11, 0x8B,
172*4882a593Smuzhiyun 0x0B, 0x20, 0x0B, 0x05,
173*4882a593Smuzhiyun 0x04, 0xF6, 0x04, 0xF6,
174*4882a593Smuzhiyun 0x04, 0xF6, 0x04, 0xF6
175*4882a593Smuzhiyun };
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun static const u8 cypress_smc_int_vectors[] =
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
180*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
181*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
182*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
183*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
184*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
185*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
186*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
187*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
188*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
189*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
190*4882a593Smuzhiyun 0x0B, 0x05, 0x0B, 0x05,
191*4882a593Smuzhiyun 0x0B, 0x05, 0x11, 0x8B,
192*4882a593Smuzhiyun 0x0B, 0x20, 0x0B, 0x05,
193*4882a593Smuzhiyun 0x04, 0xF6, 0x04, 0xF6,
194*4882a593Smuzhiyun 0x04, 0xF6, 0x04, 0xF6
195*4882a593Smuzhiyun };
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun static const u8 barts_smc_int_vectors[] =
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
200*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
201*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
202*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
203*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
204*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
205*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
206*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
207*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
208*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
209*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
210*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
211*4882a593Smuzhiyun 0x0C, 0x14, 0x12, 0xAA,
212*4882a593Smuzhiyun 0x0C, 0x2F, 0x15, 0xF6,
213*4882a593Smuzhiyun 0x15, 0xF6, 0x05, 0x0A,
214*4882a593Smuzhiyun 0x05, 0x0A, 0x05, 0x0A
215*4882a593Smuzhiyun };
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun static const u8 turks_smc_int_vectors[] =
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
220*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
221*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
222*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
223*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
224*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
225*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
226*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
227*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
228*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
229*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
230*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
231*4882a593Smuzhiyun 0x0C, 0x14, 0x12, 0xAA,
232*4882a593Smuzhiyun 0x0C, 0x2F, 0x15, 0xF6,
233*4882a593Smuzhiyun 0x15, 0xF6, 0x05, 0x0A,
234*4882a593Smuzhiyun 0x05, 0x0A, 0x05, 0x0A
235*4882a593Smuzhiyun };
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun static const u8 caicos_smc_int_vectors[] =
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
240*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
241*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
242*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
243*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
244*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
245*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
246*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
247*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
248*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
249*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
250*4882a593Smuzhiyun 0x0C, 0x14, 0x0C, 0x14,
251*4882a593Smuzhiyun 0x0C, 0x14, 0x12, 0xAA,
252*4882a593Smuzhiyun 0x0C, 0x2F, 0x15, 0xF6,
253*4882a593Smuzhiyun 0x15, 0xF6, 0x05, 0x0A,
254*4882a593Smuzhiyun 0x05, 0x0A, 0x05, 0x0A
255*4882a593Smuzhiyun };
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun static const u8 cayman_smc_int_vectors[] =
258*4882a593Smuzhiyun {
259*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
260*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
261*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
262*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
263*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
264*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
265*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
266*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
267*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
268*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
269*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
270*4882a593Smuzhiyun 0x12, 0x05, 0x12, 0x05,
271*4882a593Smuzhiyun 0x12, 0x05, 0x18, 0xEA,
272*4882a593Smuzhiyun 0x12, 0x20, 0x1C, 0x34,
273*4882a593Smuzhiyun 0x1C, 0x34, 0x08, 0x72,
274*4882a593Smuzhiyun 0x08, 0x72, 0x08, 0x72
275*4882a593Smuzhiyun };
276*4882a593Smuzhiyun
rv770_set_smc_sram_address(struct radeon_device * rdev,u16 smc_address,u16 limit)277*4882a593Smuzhiyun static int rv770_set_smc_sram_address(struct radeon_device *rdev,
278*4882a593Smuzhiyun u16 smc_address, u16 limit)
279*4882a593Smuzhiyun {
280*4882a593Smuzhiyun u32 addr;
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun if (smc_address & 3)
283*4882a593Smuzhiyun return -EINVAL;
284*4882a593Smuzhiyun if ((smc_address + 3) > limit)
285*4882a593Smuzhiyun return -EINVAL;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun addr = smc_address;
288*4882a593Smuzhiyun addr |= SMC_SRAM_AUTO_INC_DIS;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun WREG32(SMC_SRAM_ADDR, addr);
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun return 0;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun
rv770_copy_bytes_to_smc(struct radeon_device * rdev,u16 smc_start_address,const u8 * src,u16 byte_count,u16 limit)295*4882a593Smuzhiyun int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
296*4882a593Smuzhiyun u16 smc_start_address, const u8 *src,
297*4882a593Smuzhiyun u16 byte_count, u16 limit)
298*4882a593Smuzhiyun {
299*4882a593Smuzhiyun unsigned long flags;
300*4882a593Smuzhiyun u32 data, original_data, extra_shift;
301*4882a593Smuzhiyun u16 addr;
302*4882a593Smuzhiyun int ret = 0;
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun if (smc_start_address & 3)
305*4882a593Smuzhiyun return -EINVAL;
306*4882a593Smuzhiyun if ((smc_start_address + byte_count) > limit)
307*4882a593Smuzhiyun return -EINVAL;
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun addr = smc_start_address;
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun spin_lock_irqsave(&rdev->smc_idx_lock, flags);
312*4882a593Smuzhiyun while (byte_count >= 4) {
313*4882a593Smuzhiyun /* SMC address space is BE */
314*4882a593Smuzhiyun data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun ret = rv770_set_smc_sram_address(rdev, addr, limit);
317*4882a593Smuzhiyun if (ret)
318*4882a593Smuzhiyun goto done;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun WREG32(SMC_SRAM_DATA, data);
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun src += 4;
323*4882a593Smuzhiyun byte_count -= 4;
324*4882a593Smuzhiyun addr += 4;
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun
327*4882a593Smuzhiyun /* RMW for final bytes */
328*4882a593Smuzhiyun if (byte_count > 0) {
329*4882a593Smuzhiyun data = 0;
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun ret = rv770_set_smc_sram_address(rdev, addr, limit);
332*4882a593Smuzhiyun if (ret)
333*4882a593Smuzhiyun goto done;
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun original_data = RREG32(SMC_SRAM_DATA);
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun extra_shift = 8 * (4 - byte_count);
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun while (byte_count > 0) {
340*4882a593Smuzhiyun /* SMC address space is BE */
341*4882a593Smuzhiyun data = (data << 8) + *src++;
342*4882a593Smuzhiyun byte_count--;
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun data <<= extra_shift;
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun data |= (original_data & ~((~0UL) << extra_shift));
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun ret = rv770_set_smc_sram_address(rdev, addr, limit);
350*4882a593Smuzhiyun if (ret)
351*4882a593Smuzhiyun goto done;
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun WREG32(SMC_SRAM_DATA, data);
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun done:
357*4882a593Smuzhiyun spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun return ret;
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun
rv770_program_interrupt_vectors(struct radeon_device * rdev,u32 smc_first_vector,const u8 * src,u32 byte_count)362*4882a593Smuzhiyun static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
363*4882a593Smuzhiyun u32 smc_first_vector, const u8 *src,
364*4882a593Smuzhiyun u32 byte_count)
365*4882a593Smuzhiyun {
366*4882a593Smuzhiyun u32 tmp, i;
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun if (byte_count % 4)
369*4882a593Smuzhiyun return -EINVAL;
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun if (smc_first_vector < FIRST_SMC_INT_VECT_REG) {
372*4882a593Smuzhiyun tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector;
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun if (tmp > byte_count)
375*4882a593Smuzhiyun return 0;
376*4882a593Smuzhiyun
377*4882a593Smuzhiyun byte_count -= tmp;
378*4882a593Smuzhiyun src += tmp;
379*4882a593Smuzhiyun smc_first_vector = FIRST_SMC_INT_VECT_REG;
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun for (i = 0; i < byte_count; i += 4) {
383*4882a593Smuzhiyun /* SMC address space is BE */
384*4882a593Smuzhiyun tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun WREG32(SMC_ISR_FFD8_FFDB + i, tmp);
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun return 0;
390*4882a593Smuzhiyun }
391*4882a593Smuzhiyun
rv770_start_smc(struct radeon_device * rdev)392*4882a593Smuzhiyun void rv770_start_smc(struct radeon_device *rdev)
393*4882a593Smuzhiyun {
394*4882a593Smuzhiyun WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N);
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun
rv770_reset_smc(struct radeon_device * rdev)397*4882a593Smuzhiyun void rv770_reset_smc(struct radeon_device *rdev)
398*4882a593Smuzhiyun {
399*4882a593Smuzhiyun WREG32_P(SMC_IO, 0, ~SMC_RST_N);
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun
rv770_stop_smc_clock(struct radeon_device * rdev)402*4882a593Smuzhiyun void rv770_stop_smc_clock(struct radeon_device *rdev)
403*4882a593Smuzhiyun {
404*4882a593Smuzhiyun WREG32_P(SMC_IO, 0, ~SMC_CLK_EN);
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun
rv770_start_smc_clock(struct radeon_device * rdev)407*4882a593Smuzhiyun void rv770_start_smc_clock(struct radeon_device *rdev)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN);
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun
rv770_is_smc_running(struct radeon_device * rdev)412*4882a593Smuzhiyun bool rv770_is_smc_running(struct radeon_device *rdev)
413*4882a593Smuzhiyun {
414*4882a593Smuzhiyun u32 tmp;
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun tmp = RREG32(SMC_IO);
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN))
419*4882a593Smuzhiyun return true;
420*4882a593Smuzhiyun else
421*4882a593Smuzhiyun return false;
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun
rv770_send_msg_to_smc(struct radeon_device * rdev,PPSMC_Msg msg)424*4882a593Smuzhiyun PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun u32 tmp;
427*4882a593Smuzhiyun int i;
428*4882a593Smuzhiyun PPSMC_Result result;
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun if (!rv770_is_smc_running(rdev))
431*4882a593Smuzhiyun return PPSMC_Result_Failed;
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK);
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun for (i = 0; i < rdev->usec_timeout; i++) {
436*4882a593Smuzhiyun tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
437*4882a593Smuzhiyun tmp >>= HOST_SMC_RESP_SHIFT;
438*4882a593Smuzhiyun if (tmp != 0)
439*4882a593Smuzhiyun break;
440*4882a593Smuzhiyun udelay(1);
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
444*4882a593Smuzhiyun tmp >>= HOST_SMC_RESP_SHIFT;
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun result = (PPSMC_Result)tmp;
447*4882a593Smuzhiyun return result;
448*4882a593Smuzhiyun }
449*4882a593Smuzhiyun
rv770_wait_for_smc_inactive(struct radeon_device * rdev)450*4882a593Smuzhiyun PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)
451*4882a593Smuzhiyun {
452*4882a593Smuzhiyun int i;
453*4882a593Smuzhiyun PPSMC_Result result = PPSMC_Result_OK;
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun if (!rv770_is_smc_running(rdev))
456*4882a593Smuzhiyun return result;
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun for (i = 0; i < rdev->usec_timeout; i++) {
459*4882a593Smuzhiyun if (RREG32(SMC_IO) & SMC_STOP_MODE)
460*4882a593Smuzhiyun break;
461*4882a593Smuzhiyun udelay(1);
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun return result;
465*4882a593Smuzhiyun }
466*4882a593Smuzhiyun
rv770_clear_smc_sram(struct radeon_device * rdev,u16 limit)467*4882a593Smuzhiyun static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
468*4882a593Smuzhiyun {
469*4882a593Smuzhiyun unsigned long flags;
470*4882a593Smuzhiyun u16 i;
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun spin_lock_irqsave(&rdev->smc_idx_lock, flags);
473*4882a593Smuzhiyun for (i = 0; i < limit; i += 4) {
474*4882a593Smuzhiyun rv770_set_smc_sram_address(rdev, i, limit);
475*4882a593Smuzhiyun WREG32(SMC_SRAM_DATA, 0);
476*4882a593Smuzhiyun }
477*4882a593Smuzhiyun spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
478*4882a593Smuzhiyun }
479*4882a593Smuzhiyun
rv770_load_smc_ucode(struct radeon_device * rdev,u16 limit)480*4882a593Smuzhiyun int rv770_load_smc_ucode(struct radeon_device *rdev,
481*4882a593Smuzhiyun u16 limit)
482*4882a593Smuzhiyun {
483*4882a593Smuzhiyun int ret;
484*4882a593Smuzhiyun const u8 *int_vect;
485*4882a593Smuzhiyun u16 int_vect_start_address;
486*4882a593Smuzhiyun u16 int_vect_size;
487*4882a593Smuzhiyun const u8 *ucode_data;
488*4882a593Smuzhiyun u16 ucode_start_address;
489*4882a593Smuzhiyun u16 ucode_size;
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun if (!rdev->smc_fw)
492*4882a593Smuzhiyun return -EINVAL;
493*4882a593Smuzhiyun
494*4882a593Smuzhiyun rv770_clear_smc_sram(rdev, limit);
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun switch (rdev->family) {
497*4882a593Smuzhiyun case CHIP_RV770:
498*4882a593Smuzhiyun ucode_start_address = RV770_SMC_UCODE_START;
499*4882a593Smuzhiyun ucode_size = RV770_SMC_UCODE_SIZE;
500*4882a593Smuzhiyun int_vect = (const u8 *)&rv770_smc_int_vectors;
501*4882a593Smuzhiyun int_vect_start_address = RV770_SMC_INT_VECTOR_START;
502*4882a593Smuzhiyun int_vect_size = RV770_SMC_INT_VECTOR_SIZE;
503*4882a593Smuzhiyun break;
504*4882a593Smuzhiyun case CHIP_RV730:
505*4882a593Smuzhiyun ucode_start_address = RV730_SMC_UCODE_START;
506*4882a593Smuzhiyun ucode_size = RV730_SMC_UCODE_SIZE;
507*4882a593Smuzhiyun int_vect = (const u8 *)&rv730_smc_int_vectors;
508*4882a593Smuzhiyun int_vect_start_address = RV730_SMC_INT_VECTOR_START;
509*4882a593Smuzhiyun int_vect_size = RV730_SMC_INT_VECTOR_SIZE;
510*4882a593Smuzhiyun break;
511*4882a593Smuzhiyun case CHIP_RV710:
512*4882a593Smuzhiyun ucode_start_address = RV710_SMC_UCODE_START;
513*4882a593Smuzhiyun ucode_size = RV710_SMC_UCODE_SIZE;
514*4882a593Smuzhiyun int_vect = (const u8 *)&rv710_smc_int_vectors;
515*4882a593Smuzhiyun int_vect_start_address = RV710_SMC_INT_VECTOR_START;
516*4882a593Smuzhiyun int_vect_size = RV710_SMC_INT_VECTOR_SIZE;
517*4882a593Smuzhiyun break;
518*4882a593Smuzhiyun case CHIP_RV740:
519*4882a593Smuzhiyun ucode_start_address = RV740_SMC_UCODE_START;
520*4882a593Smuzhiyun ucode_size = RV740_SMC_UCODE_SIZE;
521*4882a593Smuzhiyun int_vect = (const u8 *)&rv740_smc_int_vectors;
522*4882a593Smuzhiyun int_vect_start_address = RV740_SMC_INT_VECTOR_START;
523*4882a593Smuzhiyun int_vect_size = RV740_SMC_INT_VECTOR_SIZE;
524*4882a593Smuzhiyun break;
525*4882a593Smuzhiyun case CHIP_CEDAR:
526*4882a593Smuzhiyun ucode_start_address = CEDAR_SMC_UCODE_START;
527*4882a593Smuzhiyun ucode_size = CEDAR_SMC_UCODE_SIZE;
528*4882a593Smuzhiyun int_vect = (const u8 *)&cedar_smc_int_vectors;
529*4882a593Smuzhiyun int_vect_start_address = CEDAR_SMC_INT_VECTOR_START;
530*4882a593Smuzhiyun int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE;
531*4882a593Smuzhiyun break;
532*4882a593Smuzhiyun case CHIP_REDWOOD:
533*4882a593Smuzhiyun ucode_start_address = REDWOOD_SMC_UCODE_START;
534*4882a593Smuzhiyun ucode_size = REDWOOD_SMC_UCODE_SIZE;
535*4882a593Smuzhiyun int_vect = (const u8 *)&redwood_smc_int_vectors;
536*4882a593Smuzhiyun int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START;
537*4882a593Smuzhiyun int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE;
538*4882a593Smuzhiyun break;
539*4882a593Smuzhiyun case CHIP_JUNIPER:
540*4882a593Smuzhiyun ucode_start_address = JUNIPER_SMC_UCODE_START;
541*4882a593Smuzhiyun ucode_size = JUNIPER_SMC_UCODE_SIZE;
542*4882a593Smuzhiyun int_vect = (const u8 *)&juniper_smc_int_vectors;
543*4882a593Smuzhiyun int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START;
544*4882a593Smuzhiyun int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE;
545*4882a593Smuzhiyun break;
546*4882a593Smuzhiyun case CHIP_CYPRESS:
547*4882a593Smuzhiyun case CHIP_HEMLOCK:
548*4882a593Smuzhiyun ucode_start_address = CYPRESS_SMC_UCODE_START;
549*4882a593Smuzhiyun ucode_size = CYPRESS_SMC_UCODE_SIZE;
550*4882a593Smuzhiyun int_vect = (const u8 *)&cypress_smc_int_vectors;
551*4882a593Smuzhiyun int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START;
552*4882a593Smuzhiyun int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE;
553*4882a593Smuzhiyun break;
554*4882a593Smuzhiyun case CHIP_BARTS:
555*4882a593Smuzhiyun ucode_start_address = BARTS_SMC_UCODE_START;
556*4882a593Smuzhiyun ucode_size = BARTS_SMC_UCODE_SIZE;
557*4882a593Smuzhiyun int_vect = (const u8 *)&barts_smc_int_vectors;
558*4882a593Smuzhiyun int_vect_start_address = BARTS_SMC_INT_VECTOR_START;
559*4882a593Smuzhiyun int_vect_size = BARTS_SMC_INT_VECTOR_SIZE;
560*4882a593Smuzhiyun break;
561*4882a593Smuzhiyun case CHIP_TURKS:
562*4882a593Smuzhiyun ucode_start_address = TURKS_SMC_UCODE_START;
563*4882a593Smuzhiyun ucode_size = TURKS_SMC_UCODE_SIZE;
564*4882a593Smuzhiyun int_vect = (const u8 *)&turks_smc_int_vectors;
565*4882a593Smuzhiyun int_vect_start_address = TURKS_SMC_INT_VECTOR_START;
566*4882a593Smuzhiyun int_vect_size = TURKS_SMC_INT_VECTOR_SIZE;
567*4882a593Smuzhiyun break;
568*4882a593Smuzhiyun case CHIP_CAICOS:
569*4882a593Smuzhiyun ucode_start_address = CAICOS_SMC_UCODE_START;
570*4882a593Smuzhiyun ucode_size = CAICOS_SMC_UCODE_SIZE;
571*4882a593Smuzhiyun int_vect = (const u8 *)&caicos_smc_int_vectors;
572*4882a593Smuzhiyun int_vect_start_address = CAICOS_SMC_INT_VECTOR_START;
573*4882a593Smuzhiyun int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE;
574*4882a593Smuzhiyun break;
575*4882a593Smuzhiyun case CHIP_CAYMAN:
576*4882a593Smuzhiyun ucode_start_address = CAYMAN_SMC_UCODE_START;
577*4882a593Smuzhiyun ucode_size = CAYMAN_SMC_UCODE_SIZE;
578*4882a593Smuzhiyun int_vect = (const u8 *)&cayman_smc_int_vectors;
579*4882a593Smuzhiyun int_vect_start_address = CAYMAN_SMC_INT_VECTOR_START;
580*4882a593Smuzhiyun int_vect_size = CAYMAN_SMC_INT_VECTOR_SIZE;
581*4882a593Smuzhiyun break;
582*4882a593Smuzhiyun default:
583*4882a593Smuzhiyun DRM_ERROR("unknown asic in smc ucode loader\n");
584*4882a593Smuzhiyun BUG();
585*4882a593Smuzhiyun }
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun /* load the ucode */
588*4882a593Smuzhiyun ucode_data = (const u8 *)rdev->smc_fw->data;
589*4882a593Smuzhiyun ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address,
590*4882a593Smuzhiyun ucode_data, ucode_size, limit);
591*4882a593Smuzhiyun if (ret)
592*4882a593Smuzhiyun return ret;
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun /* set up the int vectors */
595*4882a593Smuzhiyun ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address,
596*4882a593Smuzhiyun int_vect, int_vect_size);
597*4882a593Smuzhiyun if (ret)
598*4882a593Smuzhiyun return ret;
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun return 0;
601*4882a593Smuzhiyun }
602*4882a593Smuzhiyun
rv770_read_smc_sram_dword(struct radeon_device * rdev,u16 smc_address,u32 * value,u16 limit)603*4882a593Smuzhiyun int rv770_read_smc_sram_dword(struct radeon_device *rdev,
604*4882a593Smuzhiyun u16 smc_address, u32 *value, u16 limit)
605*4882a593Smuzhiyun {
606*4882a593Smuzhiyun unsigned long flags;
607*4882a593Smuzhiyun int ret;
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun spin_lock_irqsave(&rdev->smc_idx_lock, flags);
610*4882a593Smuzhiyun ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
611*4882a593Smuzhiyun if (ret == 0)
612*4882a593Smuzhiyun *value = RREG32(SMC_SRAM_DATA);
613*4882a593Smuzhiyun spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun return ret;
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun
rv770_write_smc_sram_dword(struct radeon_device * rdev,u16 smc_address,u32 value,u16 limit)618*4882a593Smuzhiyun int rv770_write_smc_sram_dword(struct radeon_device *rdev,
619*4882a593Smuzhiyun u16 smc_address, u32 value, u16 limit)
620*4882a593Smuzhiyun {
621*4882a593Smuzhiyun unsigned long flags;
622*4882a593Smuzhiyun int ret;
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun spin_lock_irqsave(&rdev->smc_idx_lock, flags);
625*4882a593Smuzhiyun ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
626*4882a593Smuzhiyun if (ret == 0)
627*4882a593Smuzhiyun WREG32(SMC_SRAM_DATA, value);
628*4882a593Smuzhiyun spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun return ret;
631*4882a593Smuzhiyun }
632