xref: /openbmc/linux/drivers/gpu/drm/radeon/rv770_smc.c (revision c182615f)
166229b20SAlex Deucher /*
266229b20SAlex Deucher  * Copyright 2011 Advanced Micro Devices, Inc.
366229b20SAlex Deucher  *
466229b20SAlex Deucher  * Permission is hereby granted, free of charge, to any person obtaining a
566229b20SAlex Deucher  * copy of this software and associated documentation files (the "Software"),
666229b20SAlex Deucher  * to deal in the Software without restriction, including without limitation
766229b20SAlex Deucher  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
866229b20SAlex Deucher  * and/or sell copies of the Software, and to permit persons to whom the
966229b20SAlex Deucher  * Software is furnished to do so, subject to the following conditions:
1066229b20SAlex Deucher  *
1166229b20SAlex Deucher  * The above copyright notice and this permission notice shall be included in
1266229b20SAlex Deucher  * all copies or substantial portions of the Software.
1366229b20SAlex Deucher  *
1466229b20SAlex Deucher  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1566229b20SAlex Deucher  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1666229b20SAlex Deucher  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1766229b20SAlex Deucher  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1866229b20SAlex Deucher  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1966229b20SAlex Deucher  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2066229b20SAlex Deucher  * OTHER DEALINGS IN THE SOFTWARE.
2166229b20SAlex Deucher  *
2266229b20SAlex Deucher  * Authors: Alex Deucher
2366229b20SAlex Deucher  */
2466229b20SAlex Deucher 
2566229b20SAlex Deucher #include <linux/firmware.h>
26c182615fSSam Ravnborg 
2766229b20SAlex Deucher #include "radeon.h"
2866229b20SAlex Deucher #include "rv770d.h"
2966229b20SAlex Deucher #include "rv770_dpm.h"
3066229b20SAlex Deucher #include "rv770_smc.h"
3166229b20SAlex Deucher #include "atom.h"
3266229b20SAlex Deucher #include "radeon_ucode.h"
3366229b20SAlex Deucher 
3466229b20SAlex Deucher #define FIRST_SMC_INT_VECT_REG 0xFFD8
3566229b20SAlex Deucher #define FIRST_INT_VECT_S19     0xFFC0
3666229b20SAlex Deucher 
3766229b20SAlex Deucher static const u8 rv770_smc_int_vectors[] =
3866229b20SAlex Deucher {
3966229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
4066229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
4166229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
4266229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
4366229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
4466229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
4566229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
4666229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
4766229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
4866229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
4966229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
5066229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
5166229b20SAlex Deucher 	0x08, 0x10, 0x0C, 0xD7,
5266229b20SAlex Deucher 	0x08, 0x2B, 0x08, 0x10,
5366229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51,
5466229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51
5566229b20SAlex Deucher };
5666229b20SAlex Deucher 
5766229b20SAlex Deucher static const u8 rv730_smc_int_vectors[] =
5866229b20SAlex Deucher {
5966229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
6066229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
6166229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
6266229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
6366229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
6466229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
6566229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
6666229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
6766229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
6866229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
6966229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
7066229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
7166229b20SAlex Deucher 	0x08, 0x15, 0x0C, 0xBB,
7266229b20SAlex Deucher 	0x08, 0x30, 0x08, 0x15,
7366229b20SAlex Deucher 	0x03, 0x56, 0x03, 0x56,
7466229b20SAlex Deucher 	0x03, 0x56, 0x03, 0x56
7566229b20SAlex Deucher };
7666229b20SAlex Deucher 
7766229b20SAlex Deucher static const u8 rv710_smc_int_vectors[] =
7866229b20SAlex Deucher {
7966229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
8066229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
8166229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
8266229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
8366229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
8466229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
8566229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
8666229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
8766229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
8866229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
8966229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
9066229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
9166229b20SAlex Deucher 	0x08, 0x04, 0x0C, 0xCB,
9266229b20SAlex Deucher 	0x08, 0x1F, 0x08, 0x04,
9366229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51,
9466229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51
9566229b20SAlex Deucher };
9666229b20SAlex Deucher 
9766229b20SAlex Deucher static const u8 rv740_smc_int_vectors[] =
9866229b20SAlex Deucher {
9966229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
10066229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
10166229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
10266229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
10366229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
10466229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
10566229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
10666229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
10766229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
10866229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
10966229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
11066229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
11166229b20SAlex Deucher 	0x08, 0x10, 0x0C, 0xD7,
11266229b20SAlex Deucher 	0x08, 0x2B, 0x08, 0x10,
11366229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51,
11466229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51
11566229b20SAlex Deucher };
11666229b20SAlex Deucher 
117dc50ba7fSAlex Deucher static const u8 cedar_smc_int_vectors[] =
118dc50ba7fSAlex Deucher {
119dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
120dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
121dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
122dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
123dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
124dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
125dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
126dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
127dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
128dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
129dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
130dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
131dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x11, 0x8B,
132dc50ba7fSAlex Deucher 	0x0B, 0x20, 0x0B, 0x05,
133dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6,
134dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6
135dc50ba7fSAlex Deucher };
136dc50ba7fSAlex Deucher 
137dc50ba7fSAlex Deucher static const u8 redwood_smc_int_vectors[] =
138dc50ba7fSAlex Deucher {
139dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
140dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
141dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
142dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
143dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
144dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
145dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
146dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
147dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
148dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
149dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
150dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
151dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x11, 0x8B,
152dc50ba7fSAlex Deucher 	0x0B, 0x20, 0x0B, 0x05,
153dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6,
154dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6
155dc50ba7fSAlex Deucher };
156dc50ba7fSAlex Deucher 
157dc50ba7fSAlex Deucher static const u8 juniper_smc_int_vectors[] =
158dc50ba7fSAlex Deucher {
159dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
160dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
161dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
162dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
163dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
164dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
165dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
166dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
167dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
168dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
169dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
170dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
171dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x11, 0x8B,
172dc50ba7fSAlex Deucher 	0x0B, 0x20, 0x0B, 0x05,
173dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6,
174dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6
175dc50ba7fSAlex Deucher };
176dc50ba7fSAlex Deucher 
177dc50ba7fSAlex Deucher static const u8 cypress_smc_int_vectors[] =
178dc50ba7fSAlex Deucher {
179dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
180dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
181dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
182dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
183dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
184dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
185dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
186dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
187dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
188dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
189dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
190dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
191dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x11, 0x8B,
192dc50ba7fSAlex Deucher 	0x0B, 0x20, 0x0B, 0x05,
193dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6,
194dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6
195dc50ba7fSAlex Deucher };
196dc50ba7fSAlex Deucher 
1976596afd4SAlex Deucher static const u8 barts_smc_int_vectors[] =
1986596afd4SAlex Deucher {
1996596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2006596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2016596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2026596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2036596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2046596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2056596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2066596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2076596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2086596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2096596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2106596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2116596afd4SAlex Deucher 	0x0C, 0x14, 0x12, 0xAA,
2126596afd4SAlex Deucher 	0x0C, 0x2F, 0x15, 0xF6,
2136596afd4SAlex Deucher 	0x15, 0xF6, 0x05, 0x0A,
2146596afd4SAlex Deucher 	0x05, 0x0A, 0x05, 0x0A
2156596afd4SAlex Deucher };
2166596afd4SAlex Deucher 
2176596afd4SAlex Deucher static const u8 turks_smc_int_vectors[] =
2186596afd4SAlex Deucher {
2196596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2206596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2216596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2226596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2236596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2246596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2256596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2266596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2276596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2286596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2296596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2306596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2316596afd4SAlex Deucher 	0x0C, 0x14, 0x12, 0xAA,
2326596afd4SAlex Deucher 	0x0C, 0x2F, 0x15, 0xF6,
2336596afd4SAlex Deucher 	0x15, 0xF6, 0x05, 0x0A,
2346596afd4SAlex Deucher 	0x05, 0x0A, 0x05, 0x0A
2356596afd4SAlex Deucher };
2366596afd4SAlex Deucher 
2376596afd4SAlex Deucher static const u8 caicos_smc_int_vectors[] =
2386596afd4SAlex Deucher {
2396596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2406596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2416596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2426596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2436596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2446596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2456596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2466596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2476596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2486596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2496596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2506596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2516596afd4SAlex Deucher 	0x0C, 0x14, 0x12, 0xAA,
2526596afd4SAlex Deucher 	0x0C, 0x2F, 0x15, 0xF6,
2536596afd4SAlex Deucher 	0x15, 0xF6, 0x05, 0x0A,
2546596afd4SAlex Deucher 	0x05, 0x0A, 0x05, 0x0A
2556596afd4SAlex Deucher };
2566596afd4SAlex Deucher 
25769e0b57aSAlex Deucher static const u8 cayman_smc_int_vectors[] =
25869e0b57aSAlex Deucher {
25969e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
26069e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
26169e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
26269e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
26369e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
26469e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
26569e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
26669e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
26769e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
26869e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
26969e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
27069e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
27169e0b57aSAlex Deucher 	0x12, 0x05, 0x18, 0xEA,
27269e0b57aSAlex Deucher 	0x12, 0x20, 0x1C, 0x34,
27369e0b57aSAlex Deucher 	0x1C, 0x34, 0x08, 0x72,
27469e0b57aSAlex Deucher 	0x08, 0x72, 0x08, 0x72
27569e0b57aSAlex Deucher };
27669e0b57aSAlex Deucher 
277fe78118cSAlex Deucher static int rv770_set_smc_sram_address(struct radeon_device *rdev,
27866229b20SAlex Deucher 				      u16 smc_address, u16 limit)
27966229b20SAlex Deucher {
28066229b20SAlex Deucher 	u32 addr;
28166229b20SAlex Deucher 
28266229b20SAlex Deucher 	if (smc_address & 3)
28366229b20SAlex Deucher 		return -EINVAL;
28466229b20SAlex Deucher 	if ((smc_address + 3) > limit)
28566229b20SAlex Deucher 		return -EINVAL;
28666229b20SAlex Deucher 
28766229b20SAlex Deucher 	addr = smc_address;
28866229b20SAlex Deucher 	addr |= SMC_SRAM_AUTO_INC_DIS;
28966229b20SAlex Deucher 
29066229b20SAlex Deucher 	WREG32(SMC_SRAM_ADDR, addr);
29166229b20SAlex Deucher 
29266229b20SAlex Deucher 	return 0;
29366229b20SAlex Deucher }
29466229b20SAlex Deucher 
29566229b20SAlex Deucher int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
29666229b20SAlex Deucher 			    u16 smc_start_address, const u8 *src,
29766229b20SAlex Deucher 			    u16 byte_count, u16 limit)
29866229b20SAlex Deucher {
299fe78118cSAlex Deucher 	unsigned long flags;
30066229b20SAlex Deucher 	u32 data, original_data, extra_shift;
30166229b20SAlex Deucher 	u16 addr;
302fe78118cSAlex Deucher 	int ret = 0;
30366229b20SAlex Deucher 
30466229b20SAlex Deucher 	if (smc_start_address & 3)
30566229b20SAlex Deucher 		return -EINVAL;
30666229b20SAlex Deucher 	if ((smc_start_address + byte_count) > limit)
30766229b20SAlex Deucher 		return -EINVAL;
30866229b20SAlex Deucher 
30966229b20SAlex Deucher 	addr = smc_start_address;
31066229b20SAlex Deucher 
311fe78118cSAlex Deucher 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
31266229b20SAlex Deucher 	while (byte_count >= 4) {
31366229b20SAlex Deucher 		/* SMC address space is BE */
31466229b20SAlex Deucher 		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
31566229b20SAlex Deucher 
31666229b20SAlex Deucher 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
31766229b20SAlex Deucher 		if (ret)
318fe78118cSAlex Deucher 			goto done;
31966229b20SAlex Deucher 
32066229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, data);
32166229b20SAlex Deucher 
32266229b20SAlex Deucher 		src += 4;
32366229b20SAlex Deucher 		byte_count -= 4;
32466229b20SAlex Deucher 		addr += 4;
32566229b20SAlex Deucher 	}
32666229b20SAlex Deucher 
32766229b20SAlex Deucher 	/* RMW for final bytes */
32866229b20SAlex Deucher 	if (byte_count > 0) {
32966229b20SAlex Deucher 		data = 0;
33066229b20SAlex Deucher 
33166229b20SAlex Deucher 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
33266229b20SAlex Deucher 		if (ret)
333fe78118cSAlex Deucher 			goto done;
33466229b20SAlex Deucher 
33566229b20SAlex Deucher 		original_data = RREG32(SMC_SRAM_DATA);
33666229b20SAlex Deucher 
33766229b20SAlex Deucher 		extra_shift = 8 * (4 - byte_count);
33866229b20SAlex Deucher 
33966229b20SAlex Deucher 		while (byte_count > 0) {
34066229b20SAlex Deucher 			/* SMC address space is BE */
34166229b20SAlex Deucher 			data = (data << 8) + *src++;
34266229b20SAlex Deucher 			byte_count--;
34366229b20SAlex Deucher 		}
34466229b20SAlex Deucher 
34566229b20SAlex Deucher 		data <<= extra_shift;
34666229b20SAlex Deucher 
34766229b20SAlex Deucher 		data |= (original_data & ~((~0UL) << extra_shift));
34866229b20SAlex Deucher 
34966229b20SAlex Deucher 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
35066229b20SAlex Deucher 		if (ret)
351fe78118cSAlex Deucher 			goto done;
35266229b20SAlex Deucher 
35366229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, data);
35466229b20SAlex Deucher 	}
35566229b20SAlex Deucher 
356fe78118cSAlex Deucher done:
357fe78118cSAlex Deucher 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
358fe78118cSAlex Deucher 
359fe78118cSAlex Deucher 	return ret;
36066229b20SAlex Deucher }
36166229b20SAlex Deucher 
36266229b20SAlex Deucher static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
36366229b20SAlex Deucher 					   u32 smc_first_vector, const u8 *src,
36466229b20SAlex Deucher 					   u32 byte_count)
36566229b20SAlex Deucher {
36666229b20SAlex Deucher 	u32 tmp, i;
36766229b20SAlex Deucher 
36866229b20SAlex Deucher 	if (byte_count % 4)
36966229b20SAlex Deucher 		return -EINVAL;
37066229b20SAlex Deucher 
37166229b20SAlex Deucher 	if (smc_first_vector < FIRST_SMC_INT_VECT_REG) {
37266229b20SAlex Deucher 		tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector;
37366229b20SAlex Deucher 
37466229b20SAlex Deucher 		if (tmp > byte_count)
37566229b20SAlex Deucher 			return 0;
37666229b20SAlex Deucher 
37766229b20SAlex Deucher 		byte_count -= tmp;
37866229b20SAlex Deucher 		src += tmp;
37966229b20SAlex Deucher 		smc_first_vector = FIRST_SMC_INT_VECT_REG;
38066229b20SAlex Deucher 	}
38166229b20SAlex Deucher 
38266229b20SAlex Deucher 	for (i = 0; i < byte_count; i += 4) {
38366229b20SAlex Deucher 		/* SMC address space is BE */
38466229b20SAlex Deucher 		tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
38566229b20SAlex Deucher 
38666229b20SAlex Deucher 		WREG32(SMC_ISR_FFD8_FFDB + i, tmp);
38766229b20SAlex Deucher 	}
38866229b20SAlex Deucher 
38966229b20SAlex Deucher 	return 0;
39066229b20SAlex Deucher }
39166229b20SAlex Deucher 
39266229b20SAlex Deucher void rv770_start_smc(struct radeon_device *rdev)
39366229b20SAlex Deucher {
39466229b20SAlex Deucher 	WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N);
39566229b20SAlex Deucher }
39666229b20SAlex Deucher 
39766229b20SAlex Deucher void rv770_reset_smc(struct radeon_device *rdev)
39866229b20SAlex Deucher {
39966229b20SAlex Deucher 	WREG32_P(SMC_IO, 0, ~SMC_RST_N);
40066229b20SAlex Deucher }
40166229b20SAlex Deucher 
40266229b20SAlex Deucher void rv770_stop_smc_clock(struct radeon_device *rdev)
40366229b20SAlex Deucher {
40466229b20SAlex Deucher 	WREG32_P(SMC_IO, 0, ~SMC_CLK_EN);
40566229b20SAlex Deucher }
40666229b20SAlex Deucher 
40766229b20SAlex Deucher void rv770_start_smc_clock(struct radeon_device *rdev)
40866229b20SAlex Deucher {
40966229b20SAlex Deucher 	WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN);
41066229b20SAlex Deucher }
41166229b20SAlex Deucher 
41266229b20SAlex Deucher bool rv770_is_smc_running(struct radeon_device *rdev)
41366229b20SAlex Deucher {
41466229b20SAlex Deucher 	u32 tmp;
41566229b20SAlex Deucher 
41666229b20SAlex Deucher 	tmp = RREG32(SMC_IO);
41766229b20SAlex Deucher 
41866229b20SAlex Deucher 	if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN))
41966229b20SAlex Deucher 		return true;
42066229b20SAlex Deucher 	else
42166229b20SAlex Deucher 		return false;
42266229b20SAlex Deucher }
42366229b20SAlex Deucher 
42466229b20SAlex Deucher PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
42566229b20SAlex Deucher {
42666229b20SAlex Deucher 	u32 tmp;
42766229b20SAlex Deucher 	int i;
42866229b20SAlex Deucher 	PPSMC_Result result;
42966229b20SAlex Deucher 
43066229b20SAlex Deucher 	if (!rv770_is_smc_running(rdev))
43166229b20SAlex Deucher 		return PPSMC_Result_Failed;
43266229b20SAlex Deucher 
43366229b20SAlex Deucher 	WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK);
43466229b20SAlex Deucher 
43566229b20SAlex Deucher 	for (i = 0; i < rdev->usec_timeout; i++) {
43666229b20SAlex Deucher 		tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
43766229b20SAlex Deucher 		tmp >>= HOST_SMC_RESP_SHIFT;
43866229b20SAlex Deucher 		if (tmp != 0)
43966229b20SAlex Deucher 			break;
44066229b20SAlex Deucher 		udelay(1);
44166229b20SAlex Deucher 	}
44266229b20SAlex Deucher 
44366229b20SAlex Deucher 	tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
44466229b20SAlex Deucher 	tmp >>= HOST_SMC_RESP_SHIFT;
44566229b20SAlex Deucher 
44666229b20SAlex Deucher 	result = (PPSMC_Result)tmp;
44766229b20SAlex Deucher 	return result;
44866229b20SAlex Deucher }
44966229b20SAlex Deucher 
45066229b20SAlex Deucher PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)
45166229b20SAlex Deucher {
45266229b20SAlex Deucher 	int i;
45366229b20SAlex Deucher 	PPSMC_Result result = PPSMC_Result_OK;
45466229b20SAlex Deucher 
45566229b20SAlex Deucher 	if (!rv770_is_smc_running(rdev))
45666229b20SAlex Deucher 		return result;
45766229b20SAlex Deucher 
45866229b20SAlex Deucher 	for (i = 0; i < rdev->usec_timeout; i++) {
45966229b20SAlex Deucher 		if (RREG32(SMC_IO) & SMC_STOP_MODE)
46066229b20SAlex Deucher 			break;
46166229b20SAlex Deucher 		udelay(1);
46266229b20SAlex Deucher 	}
46366229b20SAlex Deucher 
46466229b20SAlex Deucher 	return result;
46566229b20SAlex Deucher }
46666229b20SAlex Deucher 
46766229b20SAlex Deucher static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
46866229b20SAlex Deucher {
469fe78118cSAlex Deucher 	unsigned long flags;
47066229b20SAlex Deucher 	u16 i;
47166229b20SAlex Deucher 
472fe78118cSAlex Deucher 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
47366229b20SAlex Deucher 	for (i = 0;  i < limit; i += 4) {
47466229b20SAlex Deucher 		rv770_set_smc_sram_address(rdev, i, limit);
47566229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, 0);
47666229b20SAlex Deucher 	}
477fe78118cSAlex Deucher 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
47866229b20SAlex Deucher }
47966229b20SAlex Deucher 
48066229b20SAlex Deucher int rv770_load_smc_ucode(struct radeon_device *rdev,
48166229b20SAlex Deucher 			 u16 limit)
48266229b20SAlex Deucher {
48366229b20SAlex Deucher 	int ret;
48466229b20SAlex Deucher 	const u8 *int_vect;
48566229b20SAlex Deucher 	u16 int_vect_start_address;
48666229b20SAlex Deucher 	u16 int_vect_size;
48766229b20SAlex Deucher 	const u8 *ucode_data;
48866229b20SAlex Deucher 	u16 ucode_start_address;
48966229b20SAlex Deucher 	u16 ucode_size;
49066229b20SAlex Deucher 
49166229b20SAlex Deucher 	if (!rdev->smc_fw)
49266229b20SAlex Deucher 		return -EINVAL;
49366229b20SAlex Deucher 
49466229b20SAlex Deucher 	rv770_clear_smc_sram(rdev, limit);
49566229b20SAlex Deucher 
49666229b20SAlex Deucher 	switch (rdev->family) {
49766229b20SAlex Deucher 	case CHIP_RV770:
49866229b20SAlex Deucher 		ucode_start_address = RV770_SMC_UCODE_START;
49966229b20SAlex Deucher 		ucode_size = RV770_SMC_UCODE_SIZE;
50066229b20SAlex Deucher 		int_vect = (const u8 *)&rv770_smc_int_vectors;
50166229b20SAlex Deucher 		int_vect_start_address = RV770_SMC_INT_VECTOR_START;
50266229b20SAlex Deucher 		int_vect_size = RV770_SMC_INT_VECTOR_SIZE;
50366229b20SAlex Deucher 		break;
50466229b20SAlex Deucher 	case CHIP_RV730:
50566229b20SAlex Deucher 		ucode_start_address = RV730_SMC_UCODE_START;
50666229b20SAlex Deucher 		ucode_size = RV730_SMC_UCODE_SIZE;
50766229b20SAlex Deucher 		int_vect = (const u8 *)&rv730_smc_int_vectors;
50866229b20SAlex Deucher 		int_vect_start_address = RV730_SMC_INT_VECTOR_START;
50966229b20SAlex Deucher 		int_vect_size = RV730_SMC_INT_VECTOR_SIZE;
51066229b20SAlex Deucher 		break;
51166229b20SAlex Deucher 	case CHIP_RV710:
51266229b20SAlex Deucher 		ucode_start_address = RV710_SMC_UCODE_START;
51366229b20SAlex Deucher 		ucode_size = RV710_SMC_UCODE_SIZE;
51466229b20SAlex Deucher 		int_vect = (const u8 *)&rv710_smc_int_vectors;
51566229b20SAlex Deucher 		int_vect_start_address = RV710_SMC_INT_VECTOR_START;
51666229b20SAlex Deucher 		int_vect_size = RV710_SMC_INT_VECTOR_SIZE;
51766229b20SAlex Deucher 		break;
51866229b20SAlex Deucher 	case CHIP_RV740:
51966229b20SAlex Deucher 		ucode_start_address = RV740_SMC_UCODE_START;
52066229b20SAlex Deucher 		ucode_size = RV740_SMC_UCODE_SIZE;
52166229b20SAlex Deucher 		int_vect = (const u8 *)&rv740_smc_int_vectors;
52266229b20SAlex Deucher 		int_vect_start_address = RV740_SMC_INT_VECTOR_START;
52366229b20SAlex Deucher 		int_vect_size = RV740_SMC_INT_VECTOR_SIZE;
52466229b20SAlex Deucher 		break;
525dc50ba7fSAlex Deucher 	case CHIP_CEDAR:
526dc50ba7fSAlex Deucher 		ucode_start_address = CEDAR_SMC_UCODE_START;
527dc50ba7fSAlex Deucher 		ucode_size = CEDAR_SMC_UCODE_SIZE;
528dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&cedar_smc_int_vectors;
529dc50ba7fSAlex Deucher 		int_vect_start_address = CEDAR_SMC_INT_VECTOR_START;
530dc50ba7fSAlex Deucher 		int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE;
531dc50ba7fSAlex Deucher 		break;
532dc50ba7fSAlex Deucher 	case CHIP_REDWOOD:
533dc50ba7fSAlex Deucher 		ucode_start_address = REDWOOD_SMC_UCODE_START;
534dc50ba7fSAlex Deucher 		ucode_size = REDWOOD_SMC_UCODE_SIZE;
535dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&redwood_smc_int_vectors;
536dc50ba7fSAlex Deucher 		int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START;
537dc50ba7fSAlex Deucher 		int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE;
538dc50ba7fSAlex Deucher 		break;
539dc50ba7fSAlex Deucher 	case CHIP_JUNIPER:
540dc50ba7fSAlex Deucher 		ucode_start_address = JUNIPER_SMC_UCODE_START;
541dc50ba7fSAlex Deucher 		ucode_size = JUNIPER_SMC_UCODE_SIZE;
542dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&juniper_smc_int_vectors;
543dc50ba7fSAlex Deucher 		int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START;
544dc50ba7fSAlex Deucher 		int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE;
545dc50ba7fSAlex Deucher 		break;
546dc50ba7fSAlex Deucher 	case CHIP_CYPRESS:
547dc50ba7fSAlex Deucher 	case CHIP_HEMLOCK:
548dc50ba7fSAlex Deucher 		ucode_start_address = CYPRESS_SMC_UCODE_START;
549dc50ba7fSAlex Deucher 		ucode_size = CYPRESS_SMC_UCODE_SIZE;
550dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&cypress_smc_int_vectors;
551dc50ba7fSAlex Deucher 		int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START;
552dc50ba7fSAlex Deucher 		int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE;
553dc50ba7fSAlex Deucher 		break;
5546596afd4SAlex Deucher 	case CHIP_BARTS:
5556596afd4SAlex Deucher 		ucode_start_address = BARTS_SMC_UCODE_START;
5566596afd4SAlex Deucher 		ucode_size = BARTS_SMC_UCODE_SIZE;
5576596afd4SAlex Deucher 		int_vect = (const u8 *)&barts_smc_int_vectors;
5586596afd4SAlex Deucher 		int_vect_start_address = BARTS_SMC_INT_VECTOR_START;
5596596afd4SAlex Deucher 		int_vect_size = BARTS_SMC_INT_VECTOR_SIZE;
5606596afd4SAlex Deucher 		break;
5616596afd4SAlex Deucher 	case CHIP_TURKS:
5626596afd4SAlex Deucher 		ucode_start_address = TURKS_SMC_UCODE_START;
5636596afd4SAlex Deucher 		ucode_size = TURKS_SMC_UCODE_SIZE;
5646596afd4SAlex Deucher 		int_vect = (const u8 *)&turks_smc_int_vectors;
5656596afd4SAlex Deucher 		int_vect_start_address = TURKS_SMC_INT_VECTOR_START;
5666596afd4SAlex Deucher 		int_vect_size = TURKS_SMC_INT_VECTOR_SIZE;
5676596afd4SAlex Deucher 		break;
5686596afd4SAlex Deucher 	case CHIP_CAICOS:
5696596afd4SAlex Deucher 		ucode_start_address = CAICOS_SMC_UCODE_START;
5706596afd4SAlex Deucher 		ucode_size = CAICOS_SMC_UCODE_SIZE;
5716596afd4SAlex Deucher 		int_vect = (const u8 *)&caicos_smc_int_vectors;
5726596afd4SAlex Deucher 		int_vect_start_address = CAICOS_SMC_INT_VECTOR_START;
5736596afd4SAlex Deucher 		int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE;
5746596afd4SAlex Deucher 		break;
57569e0b57aSAlex Deucher 	case CHIP_CAYMAN:
57669e0b57aSAlex Deucher 		ucode_start_address = CAYMAN_SMC_UCODE_START;
57769e0b57aSAlex Deucher 		ucode_size = CAYMAN_SMC_UCODE_SIZE;
57869e0b57aSAlex Deucher 		int_vect = (const u8 *)&cayman_smc_int_vectors;
57969e0b57aSAlex Deucher 		int_vect_start_address = CAYMAN_SMC_INT_VECTOR_START;
58069e0b57aSAlex Deucher 		int_vect_size = CAYMAN_SMC_INT_VECTOR_SIZE;
58169e0b57aSAlex Deucher 		break;
58266229b20SAlex Deucher 	default:
58366229b20SAlex Deucher 		DRM_ERROR("unknown asic in smc ucode loader\n");
58466229b20SAlex Deucher 		BUG();
58566229b20SAlex Deucher 	}
58666229b20SAlex Deucher 
58766229b20SAlex Deucher 	/* load the ucode */
58866229b20SAlex Deucher 	ucode_data = (const u8 *)rdev->smc_fw->data;
58966229b20SAlex Deucher 	ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address,
59066229b20SAlex Deucher 				      ucode_data, ucode_size, limit);
59166229b20SAlex Deucher 	if (ret)
59266229b20SAlex Deucher 		return ret;
59366229b20SAlex Deucher 
59466229b20SAlex Deucher 	/* set up the int vectors */
59566229b20SAlex Deucher 	ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address,
59666229b20SAlex Deucher 					      int_vect, int_vect_size);
59766229b20SAlex Deucher 	if (ret)
59866229b20SAlex Deucher 		return ret;
59966229b20SAlex Deucher 
60066229b20SAlex Deucher 	return 0;
60166229b20SAlex Deucher }
60266229b20SAlex Deucher 
60366229b20SAlex Deucher int rv770_read_smc_sram_dword(struct radeon_device *rdev,
60466229b20SAlex Deucher 			      u16 smc_address, u32 *value, u16 limit)
60566229b20SAlex Deucher {
606fe78118cSAlex Deucher 	unsigned long flags;
60766229b20SAlex Deucher 	int ret;
60866229b20SAlex Deucher 
609fe78118cSAlex Deucher 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
61066229b20SAlex Deucher 	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
611fe78118cSAlex Deucher 	if (ret == 0)
61266229b20SAlex Deucher 		*value = RREG32(SMC_SRAM_DATA);
613fe78118cSAlex Deucher 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
61466229b20SAlex Deucher 
615fe78118cSAlex Deucher 	return ret;
61666229b20SAlex Deucher }
61766229b20SAlex Deucher 
61866229b20SAlex Deucher int rv770_write_smc_sram_dword(struct radeon_device *rdev,
61966229b20SAlex Deucher 			       u16 smc_address, u32 value, u16 limit)
62066229b20SAlex Deucher {
621fe78118cSAlex Deucher 	unsigned long flags;
62266229b20SAlex Deucher 	int ret;
62366229b20SAlex Deucher 
624fe78118cSAlex Deucher 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
62566229b20SAlex Deucher 	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
626fe78118cSAlex Deucher 	if (ret == 0)
62766229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, value);
628fe78118cSAlex Deucher 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
62966229b20SAlex Deucher 
630fe78118cSAlex Deucher 	return ret;
63166229b20SAlex Deucher }
632