xref: /openbmc/linux/drivers/gpu/drm/radeon/rv770_smc.c (revision a645529d)
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 
37*a645529dSRan Sun static const u8 rv770_smc_int_vectors[] = {
3866229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
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, 0x0C, 0xD7,
5166229b20SAlex Deucher 	0x08, 0x2B, 0x08, 0x10,
5266229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51,
5366229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51
5466229b20SAlex Deucher };
5566229b20SAlex Deucher 
56*a645529dSRan Sun static const u8 rv730_smc_int_vectors[] = {
5766229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
5866229b20SAlex Deucher 	0x08, 0x15, 0x08, 0x15,
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, 0x0C, 0xBB,
7066229b20SAlex Deucher 	0x08, 0x30, 0x08, 0x15,
7166229b20SAlex Deucher 	0x03, 0x56, 0x03, 0x56,
7266229b20SAlex Deucher 	0x03, 0x56, 0x03, 0x56
7366229b20SAlex Deucher };
7466229b20SAlex Deucher 
75*a645529dSRan Sun static const u8 rv710_smc_int_vectors[] = {
7666229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
7766229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
7866229b20SAlex Deucher 	0x08, 0x04, 0x08, 0x04,
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, 0x0C, 0xCB,
8966229b20SAlex Deucher 	0x08, 0x1F, 0x08, 0x04,
9066229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51,
9166229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51
9266229b20SAlex Deucher };
9366229b20SAlex Deucher 
94*a645529dSRan Sun static const u8 rv740_smc_int_vectors[] = {
9566229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
9666229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
9766229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
9866229b20SAlex Deucher 	0x08, 0x10, 0x08, 0x10,
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, 0x0C, 0xD7,
10866229b20SAlex Deucher 	0x08, 0x2B, 0x08, 0x10,
10966229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51,
11066229b20SAlex Deucher 	0x03, 0x51, 0x03, 0x51
11166229b20SAlex Deucher };
11266229b20SAlex Deucher 
113*a645529dSRan Sun static const u8 cedar_smc_int_vectors[] = {
114dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
115dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
116dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
117dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
118dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
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, 0x11, 0x8B,
127dc50ba7fSAlex Deucher 	0x0B, 0x20, 0x0B, 0x05,
128dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6,
129dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6
130dc50ba7fSAlex Deucher };
131dc50ba7fSAlex Deucher 
132*a645529dSRan Sun static const u8 redwood_smc_int_vectors[] = {
133dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
134dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
135dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
136dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
137dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
138dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
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, 0x11, 0x8B,
146dc50ba7fSAlex Deucher 	0x0B, 0x20, 0x0B, 0x05,
147dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6,
148dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6
149dc50ba7fSAlex Deucher };
150dc50ba7fSAlex Deucher 
151*a645529dSRan Sun static const u8 juniper_smc_int_vectors[] = {
152dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
153dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
154dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
155dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
156dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
157dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
158dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
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, 0x11, 0x8B,
165dc50ba7fSAlex Deucher 	0x0B, 0x20, 0x0B, 0x05,
166dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6,
167dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6
168dc50ba7fSAlex Deucher };
169dc50ba7fSAlex Deucher 
170*a645529dSRan Sun static const u8 cypress_smc_int_vectors[] = {
171dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
172dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
173dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
174dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
175dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
176dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
177dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
178dc50ba7fSAlex Deucher 	0x0B, 0x05, 0x0B, 0x05,
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, 0x11, 0x8B,
184dc50ba7fSAlex Deucher 	0x0B, 0x20, 0x0B, 0x05,
185dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6,
186dc50ba7fSAlex Deucher 	0x04, 0xF6, 0x04, 0xF6
187dc50ba7fSAlex Deucher };
188dc50ba7fSAlex Deucher 
189*a645529dSRan Sun static const u8 barts_smc_int_vectors[] = {
1906596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
1916596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
1926596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
1936596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
1946596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
1956596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
1966596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
1976596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
1986596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
1996596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2006596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2016596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2026596afd4SAlex Deucher 	0x0C, 0x14, 0x12, 0xAA,
2036596afd4SAlex Deucher 	0x0C, 0x2F, 0x15, 0xF6,
2046596afd4SAlex Deucher 	0x15, 0xF6, 0x05, 0x0A,
2056596afd4SAlex Deucher 	0x05, 0x0A, 0x05, 0x0A
2066596afd4SAlex Deucher };
2076596afd4SAlex Deucher 
208*a645529dSRan Sun static const u8 turks_smc_int_vectors[] = {
2096596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2106596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2116596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2126596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2136596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2146596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2156596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2166596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2176596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2186596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2196596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2206596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2216596afd4SAlex Deucher 	0x0C, 0x14, 0x12, 0xAA,
2226596afd4SAlex Deucher 	0x0C, 0x2F, 0x15, 0xF6,
2236596afd4SAlex Deucher 	0x15, 0xF6, 0x05, 0x0A,
2246596afd4SAlex Deucher 	0x05, 0x0A, 0x05, 0x0A
2256596afd4SAlex Deucher };
2266596afd4SAlex Deucher 
227*a645529dSRan Sun static const u8 caicos_smc_int_vectors[] = {
2286596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2296596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2306596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2316596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2326596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2336596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2346596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2356596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2366596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2376596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2386596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2396596afd4SAlex Deucher 	0x0C, 0x14, 0x0C, 0x14,
2406596afd4SAlex Deucher 	0x0C, 0x14, 0x12, 0xAA,
2416596afd4SAlex Deucher 	0x0C, 0x2F, 0x15, 0xF6,
2426596afd4SAlex Deucher 	0x15, 0xF6, 0x05, 0x0A,
2436596afd4SAlex Deucher 	0x05, 0x0A, 0x05, 0x0A
2446596afd4SAlex Deucher };
2456596afd4SAlex Deucher 
246*a645529dSRan Sun static const u8 cayman_smc_int_vectors[] = {
24769e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
24869e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
24969e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
25069e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
25169e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
25269e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
25369e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
25469e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
25569e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
25669e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
25769e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
25869e0b57aSAlex Deucher 	0x12, 0x05, 0x12, 0x05,
25969e0b57aSAlex Deucher 	0x12, 0x05, 0x18, 0xEA,
26069e0b57aSAlex Deucher 	0x12, 0x20, 0x1C, 0x34,
26169e0b57aSAlex Deucher 	0x1C, 0x34, 0x08, 0x72,
26269e0b57aSAlex Deucher 	0x08, 0x72, 0x08, 0x72
26369e0b57aSAlex Deucher };
26469e0b57aSAlex Deucher 
rv770_set_smc_sram_address(struct radeon_device * rdev,u16 smc_address,u16 limit)265fe78118cSAlex Deucher static int rv770_set_smc_sram_address(struct radeon_device *rdev,
26666229b20SAlex Deucher 				      u16 smc_address, u16 limit)
26766229b20SAlex Deucher {
26866229b20SAlex Deucher 	u32 addr;
26966229b20SAlex Deucher 
27066229b20SAlex Deucher 	if (smc_address & 3)
27166229b20SAlex Deucher 		return -EINVAL;
27266229b20SAlex Deucher 	if ((smc_address + 3) > limit)
27366229b20SAlex Deucher 		return -EINVAL;
27466229b20SAlex Deucher 
27566229b20SAlex Deucher 	addr = smc_address;
27666229b20SAlex Deucher 	addr |= SMC_SRAM_AUTO_INC_DIS;
27766229b20SAlex Deucher 
27866229b20SAlex Deucher 	WREG32(SMC_SRAM_ADDR, addr);
27966229b20SAlex Deucher 
28066229b20SAlex Deucher 	return 0;
28166229b20SAlex Deucher }
28266229b20SAlex Deucher 
rv770_copy_bytes_to_smc(struct radeon_device * rdev,u16 smc_start_address,const u8 * src,u16 byte_count,u16 limit)28366229b20SAlex Deucher int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
28466229b20SAlex Deucher 			    u16 smc_start_address, const u8 *src,
28566229b20SAlex Deucher 			    u16 byte_count, u16 limit)
28666229b20SAlex Deucher {
287fe78118cSAlex Deucher 	unsigned long flags;
28866229b20SAlex Deucher 	u32 data, original_data, extra_shift;
28966229b20SAlex Deucher 	u16 addr;
290fe78118cSAlex Deucher 	int ret = 0;
29166229b20SAlex Deucher 
29266229b20SAlex Deucher 	if (smc_start_address & 3)
29366229b20SAlex Deucher 		return -EINVAL;
29466229b20SAlex Deucher 	if ((smc_start_address + byte_count) > limit)
29566229b20SAlex Deucher 		return -EINVAL;
29666229b20SAlex Deucher 
29766229b20SAlex Deucher 	addr = smc_start_address;
29866229b20SAlex Deucher 
299fe78118cSAlex Deucher 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
30066229b20SAlex Deucher 	while (byte_count >= 4) {
30166229b20SAlex Deucher 		/* SMC address space is BE */
30266229b20SAlex Deucher 		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
30366229b20SAlex Deucher 
30466229b20SAlex Deucher 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
30566229b20SAlex Deucher 		if (ret)
306fe78118cSAlex Deucher 			goto done;
30766229b20SAlex Deucher 
30866229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, data);
30966229b20SAlex Deucher 
31066229b20SAlex Deucher 		src += 4;
31166229b20SAlex Deucher 		byte_count -= 4;
31266229b20SAlex Deucher 		addr += 4;
31366229b20SAlex Deucher 	}
31466229b20SAlex Deucher 
31566229b20SAlex Deucher 	/* RMW for final bytes */
31666229b20SAlex Deucher 	if (byte_count > 0) {
31766229b20SAlex Deucher 		data = 0;
31866229b20SAlex Deucher 
31966229b20SAlex Deucher 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
32066229b20SAlex Deucher 		if (ret)
321fe78118cSAlex Deucher 			goto done;
32266229b20SAlex Deucher 
32366229b20SAlex Deucher 		original_data = RREG32(SMC_SRAM_DATA);
32466229b20SAlex Deucher 
32566229b20SAlex Deucher 		extra_shift = 8 * (4 - byte_count);
32666229b20SAlex Deucher 
32766229b20SAlex Deucher 		while (byte_count > 0) {
32866229b20SAlex Deucher 			/* SMC address space is BE */
32966229b20SAlex Deucher 			data = (data << 8) + *src++;
33066229b20SAlex Deucher 			byte_count--;
33166229b20SAlex Deucher 		}
33266229b20SAlex Deucher 
33366229b20SAlex Deucher 		data <<= extra_shift;
33466229b20SAlex Deucher 
33566229b20SAlex Deucher 		data |= (original_data & ~((~0UL) << extra_shift));
33666229b20SAlex Deucher 
33766229b20SAlex Deucher 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
33866229b20SAlex Deucher 		if (ret)
339fe78118cSAlex Deucher 			goto done;
34066229b20SAlex Deucher 
34166229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, data);
34266229b20SAlex Deucher 	}
34366229b20SAlex Deucher 
344fe78118cSAlex Deucher done:
345fe78118cSAlex Deucher 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
346fe78118cSAlex Deucher 
347fe78118cSAlex Deucher 	return ret;
34866229b20SAlex Deucher }
34966229b20SAlex Deucher 
rv770_program_interrupt_vectors(struct radeon_device * rdev,u32 smc_first_vector,const u8 * src,u32 byte_count)35066229b20SAlex Deucher static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
35166229b20SAlex Deucher 					   u32 smc_first_vector, const u8 *src,
35266229b20SAlex Deucher 					   u32 byte_count)
35366229b20SAlex Deucher {
35466229b20SAlex Deucher 	u32 tmp, i;
35566229b20SAlex Deucher 
35666229b20SAlex Deucher 	if (byte_count % 4)
35766229b20SAlex Deucher 		return -EINVAL;
35866229b20SAlex Deucher 
35966229b20SAlex Deucher 	if (smc_first_vector < FIRST_SMC_INT_VECT_REG) {
36066229b20SAlex Deucher 		tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector;
36166229b20SAlex Deucher 
36266229b20SAlex Deucher 		if (tmp > byte_count)
36366229b20SAlex Deucher 			return 0;
36466229b20SAlex Deucher 
36566229b20SAlex Deucher 		byte_count -= tmp;
36666229b20SAlex Deucher 		src += tmp;
36766229b20SAlex Deucher 		smc_first_vector = FIRST_SMC_INT_VECT_REG;
36866229b20SAlex Deucher 	}
36966229b20SAlex Deucher 
37066229b20SAlex Deucher 	for (i = 0; i < byte_count; i += 4) {
37166229b20SAlex Deucher 		/* SMC address space is BE */
37266229b20SAlex Deucher 		tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
37366229b20SAlex Deucher 
37466229b20SAlex Deucher 		WREG32(SMC_ISR_FFD8_FFDB + i, tmp);
37566229b20SAlex Deucher 	}
37666229b20SAlex Deucher 
37766229b20SAlex Deucher 	return 0;
37866229b20SAlex Deucher }
37966229b20SAlex Deucher 
rv770_start_smc(struct radeon_device * rdev)38066229b20SAlex Deucher void rv770_start_smc(struct radeon_device *rdev)
38166229b20SAlex Deucher {
38266229b20SAlex Deucher 	WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N);
38366229b20SAlex Deucher }
38466229b20SAlex Deucher 
rv770_reset_smc(struct radeon_device * rdev)38566229b20SAlex Deucher void rv770_reset_smc(struct radeon_device *rdev)
38666229b20SAlex Deucher {
38766229b20SAlex Deucher 	WREG32_P(SMC_IO, 0, ~SMC_RST_N);
38866229b20SAlex Deucher }
38966229b20SAlex Deucher 
rv770_stop_smc_clock(struct radeon_device * rdev)39066229b20SAlex Deucher void rv770_stop_smc_clock(struct radeon_device *rdev)
39166229b20SAlex Deucher {
39266229b20SAlex Deucher 	WREG32_P(SMC_IO, 0, ~SMC_CLK_EN);
39366229b20SAlex Deucher }
39466229b20SAlex Deucher 
rv770_start_smc_clock(struct radeon_device * rdev)39566229b20SAlex Deucher void rv770_start_smc_clock(struct radeon_device *rdev)
39666229b20SAlex Deucher {
39766229b20SAlex Deucher 	WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN);
39866229b20SAlex Deucher }
39966229b20SAlex Deucher 
rv770_is_smc_running(struct radeon_device * rdev)40066229b20SAlex Deucher bool rv770_is_smc_running(struct radeon_device *rdev)
40166229b20SAlex Deucher {
40266229b20SAlex Deucher 	u32 tmp;
40366229b20SAlex Deucher 
40466229b20SAlex Deucher 	tmp = RREG32(SMC_IO);
40566229b20SAlex Deucher 
40666229b20SAlex Deucher 	if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN))
40766229b20SAlex Deucher 		return true;
40866229b20SAlex Deucher 	else
40966229b20SAlex Deucher 		return false;
41066229b20SAlex Deucher }
41166229b20SAlex Deucher 
rv770_send_msg_to_smc(struct radeon_device * rdev,PPSMC_Msg msg)41266229b20SAlex Deucher PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
41366229b20SAlex Deucher {
41466229b20SAlex Deucher 	u32 tmp;
41566229b20SAlex Deucher 	int i;
41666229b20SAlex Deucher 	PPSMC_Result result;
41766229b20SAlex Deucher 
41866229b20SAlex Deucher 	if (!rv770_is_smc_running(rdev))
41966229b20SAlex Deucher 		return PPSMC_Result_Failed;
42066229b20SAlex Deucher 
42166229b20SAlex Deucher 	WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK);
42266229b20SAlex Deucher 
42366229b20SAlex Deucher 	for (i = 0; i < rdev->usec_timeout; i++) {
42466229b20SAlex Deucher 		tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
42566229b20SAlex Deucher 		tmp >>= HOST_SMC_RESP_SHIFT;
42666229b20SAlex Deucher 		if (tmp != 0)
42766229b20SAlex Deucher 			break;
42866229b20SAlex Deucher 		udelay(1);
42966229b20SAlex Deucher 	}
43066229b20SAlex Deucher 
43166229b20SAlex Deucher 	tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
43266229b20SAlex Deucher 	tmp >>= HOST_SMC_RESP_SHIFT;
43366229b20SAlex Deucher 
43466229b20SAlex Deucher 	result = (PPSMC_Result)tmp;
43566229b20SAlex Deucher 	return result;
43666229b20SAlex Deucher }
43766229b20SAlex Deucher 
rv770_wait_for_smc_inactive(struct radeon_device * rdev)43866229b20SAlex Deucher PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)
43966229b20SAlex Deucher {
44066229b20SAlex Deucher 	int i;
44166229b20SAlex Deucher 	PPSMC_Result result = PPSMC_Result_OK;
44266229b20SAlex Deucher 
44366229b20SAlex Deucher 	if (!rv770_is_smc_running(rdev))
44466229b20SAlex Deucher 		return result;
44566229b20SAlex Deucher 
44666229b20SAlex Deucher 	for (i = 0; i < rdev->usec_timeout; i++) {
44766229b20SAlex Deucher 		if (RREG32(SMC_IO) & SMC_STOP_MODE)
44866229b20SAlex Deucher 			break;
44966229b20SAlex Deucher 		udelay(1);
45066229b20SAlex Deucher 	}
45166229b20SAlex Deucher 
45266229b20SAlex Deucher 	return result;
45366229b20SAlex Deucher }
45466229b20SAlex Deucher 
rv770_clear_smc_sram(struct radeon_device * rdev,u16 limit)45566229b20SAlex Deucher static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
45666229b20SAlex Deucher {
457fe78118cSAlex Deucher 	unsigned long flags;
45866229b20SAlex Deucher 	u16 i;
45966229b20SAlex Deucher 
460fe78118cSAlex Deucher 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
46166229b20SAlex Deucher 	for (i = 0;  i < limit; i += 4) {
46266229b20SAlex Deucher 		rv770_set_smc_sram_address(rdev, i, limit);
46366229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, 0);
46466229b20SAlex Deucher 	}
465fe78118cSAlex Deucher 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
46666229b20SAlex Deucher }
46766229b20SAlex Deucher 
rv770_load_smc_ucode(struct radeon_device * rdev,u16 limit)46866229b20SAlex Deucher int rv770_load_smc_ucode(struct radeon_device *rdev,
46966229b20SAlex Deucher 			 u16 limit)
47066229b20SAlex Deucher {
47166229b20SAlex Deucher 	int ret;
47266229b20SAlex Deucher 	const u8 *int_vect;
47366229b20SAlex Deucher 	u16 int_vect_start_address;
47466229b20SAlex Deucher 	u16 int_vect_size;
47566229b20SAlex Deucher 	const u8 *ucode_data;
47666229b20SAlex Deucher 	u16 ucode_start_address;
47766229b20SAlex Deucher 	u16 ucode_size;
47866229b20SAlex Deucher 
47966229b20SAlex Deucher 	if (!rdev->smc_fw)
48066229b20SAlex Deucher 		return -EINVAL;
48166229b20SAlex Deucher 
48266229b20SAlex Deucher 	rv770_clear_smc_sram(rdev, limit);
48366229b20SAlex Deucher 
48466229b20SAlex Deucher 	switch (rdev->family) {
48566229b20SAlex Deucher 	case CHIP_RV770:
48666229b20SAlex Deucher 		ucode_start_address = RV770_SMC_UCODE_START;
48766229b20SAlex Deucher 		ucode_size = RV770_SMC_UCODE_SIZE;
48866229b20SAlex Deucher 		int_vect = (const u8 *)&rv770_smc_int_vectors;
48966229b20SAlex Deucher 		int_vect_start_address = RV770_SMC_INT_VECTOR_START;
49066229b20SAlex Deucher 		int_vect_size = RV770_SMC_INT_VECTOR_SIZE;
49166229b20SAlex Deucher 		break;
49266229b20SAlex Deucher 	case CHIP_RV730:
49366229b20SAlex Deucher 		ucode_start_address = RV730_SMC_UCODE_START;
49466229b20SAlex Deucher 		ucode_size = RV730_SMC_UCODE_SIZE;
49566229b20SAlex Deucher 		int_vect = (const u8 *)&rv730_smc_int_vectors;
49666229b20SAlex Deucher 		int_vect_start_address = RV730_SMC_INT_VECTOR_START;
49766229b20SAlex Deucher 		int_vect_size = RV730_SMC_INT_VECTOR_SIZE;
49866229b20SAlex Deucher 		break;
49966229b20SAlex Deucher 	case CHIP_RV710:
50066229b20SAlex Deucher 		ucode_start_address = RV710_SMC_UCODE_START;
50166229b20SAlex Deucher 		ucode_size = RV710_SMC_UCODE_SIZE;
50266229b20SAlex Deucher 		int_vect = (const u8 *)&rv710_smc_int_vectors;
50366229b20SAlex Deucher 		int_vect_start_address = RV710_SMC_INT_VECTOR_START;
50466229b20SAlex Deucher 		int_vect_size = RV710_SMC_INT_VECTOR_SIZE;
50566229b20SAlex Deucher 		break;
50666229b20SAlex Deucher 	case CHIP_RV740:
50766229b20SAlex Deucher 		ucode_start_address = RV740_SMC_UCODE_START;
50866229b20SAlex Deucher 		ucode_size = RV740_SMC_UCODE_SIZE;
50966229b20SAlex Deucher 		int_vect = (const u8 *)&rv740_smc_int_vectors;
51066229b20SAlex Deucher 		int_vect_start_address = RV740_SMC_INT_VECTOR_START;
51166229b20SAlex Deucher 		int_vect_size = RV740_SMC_INT_VECTOR_SIZE;
51266229b20SAlex Deucher 		break;
513dc50ba7fSAlex Deucher 	case CHIP_CEDAR:
514dc50ba7fSAlex Deucher 		ucode_start_address = CEDAR_SMC_UCODE_START;
515dc50ba7fSAlex Deucher 		ucode_size = CEDAR_SMC_UCODE_SIZE;
516dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&cedar_smc_int_vectors;
517dc50ba7fSAlex Deucher 		int_vect_start_address = CEDAR_SMC_INT_VECTOR_START;
518dc50ba7fSAlex Deucher 		int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE;
519dc50ba7fSAlex Deucher 		break;
520dc50ba7fSAlex Deucher 	case CHIP_REDWOOD:
521dc50ba7fSAlex Deucher 		ucode_start_address = REDWOOD_SMC_UCODE_START;
522dc50ba7fSAlex Deucher 		ucode_size = REDWOOD_SMC_UCODE_SIZE;
523dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&redwood_smc_int_vectors;
524dc50ba7fSAlex Deucher 		int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START;
525dc50ba7fSAlex Deucher 		int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE;
526dc50ba7fSAlex Deucher 		break;
527dc50ba7fSAlex Deucher 	case CHIP_JUNIPER:
528dc50ba7fSAlex Deucher 		ucode_start_address = JUNIPER_SMC_UCODE_START;
529dc50ba7fSAlex Deucher 		ucode_size = JUNIPER_SMC_UCODE_SIZE;
530dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&juniper_smc_int_vectors;
531dc50ba7fSAlex Deucher 		int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START;
532dc50ba7fSAlex Deucher 		int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE;
533dc50ba7fSAlex Deucher 		break;
534dc50ba7fSAlex Deucher 	case CHIP_CYPRESS:
535dc50ba7fSAlex Deucher 	case CHIP_HEMLOCK:
536dc50ba7fSAlex Deucher 		ucode_start_address = CYPRESS_SMC_UCODE_START;
537dc50ba7fSAlex Deucher 		ucode_size = CYPRESS_SMC_UCODE_SIZE;
538dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&cypress_smc_int_vectors;
539dc50ba7fSAlex Deucher 		int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START;
540dc50ba7fSAlex Deucher 		int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE;
541dc50ba7fSAlex Deucher 		break;
5426596afd4SAlex Deucher 	case CHIP_BARTS:
5436596afd4SAlex Deucher 		ucode_start_address = BARTS_SMC_UCODE_START;
5446596afd4SAlex Deucher 		ucode_size = BARTS_SMC_UCODE_SIZE;
5456596afd4SAlex Deucher 		int_vect = (const u8 *)&barts_smc_int_vectors;
5466596afd4SAlex Deucher 		int_vect_start_address = BARTS_SMC_INT_VECTOR_START;
5476596afd4SAlex Deucher 		int_vect_size = BARTS_SMC_INT_VECTOR_SIZE;
5486596afd4SAlex Deucher 		break;
5496596afd4SAlex Deucher 	case CHIP_TURKS:
5506596afd4SAlex Deucher 		ucode_start_address = TURKS_SMC_UCODE_START;
5516596afd4SAlex Deucher 		ucode_size = TURKS_SMC_UCODE_SIZE;
5526596afd4SAlex Deucher 		int_vect = (const u8 *)&turks_smc_int_vectors;
5536596afd4SAlex Deucher 		int_vect_start_address = TURKS_SMC_INT_VECTOR_START;
5546596afd4SAlex Deucher 		int_vect_size = TURKS_SMC_INT_VECTOR_SIZE;
5556596afd4SAlex Deucher 		break;
5566596afd4SAlex Deucher 	case CHIP_CAICOS:
5576596afd4SAlex Deucher 		ucode_start_address = CAICOS_SMC_UCODE_START;
5586596afd4SAlex Deucher 		ucode_size = CAICOS_SMC_UCODE_SIZE;
5596596afd4SAlex Deucher 		int_vect = (const u8 *)&caicos_smc_int_vectors;
5606596afd4SAlex Deucher 		int_vect_start_address = CAICOS_SMC_INT_VECTOR_START;
5616596afd4SAlex Deucher 		int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE;
5626596afd4SAlex Deucher 		break;
56369e0b57aSAlex Deucher 	case CHIP_CAYMAN:
56469e0b57aSAlex Deucher 		ucode_start_address = CAYMAN_SMC_UCODE_START;
56569e0b57aSAlex Deucher 		ucode_size = CAYMAN_SMC_UCODE_SIZE;
56669e0b57aSAlex Deucher 		int_vect = (const u8 *)&cayman_smc_int_vectors;
56769e0b57aSAlex Deucher 		int_vect_start_address = CAYMAN_SMC_INT_VECTOR_START;
56869e0b57aSAlex Deucher 		int_vect_size = CAYMAN_SMC_INT_VECTOR_SIZE;
56969e0b57aSAlex Deucher 		break;
57066229b20SAlex Deucher 	default:
57166229b20SAlex Deucher 		DRM_ERROR("unknown asic in smc ucode loader\n");
57266229b20SAlex Deucher 		BUG();
57366229b20SAlex Deucher 	}
57466229b20SAlex Deucher 
57566229b20SAlex Deucher 	/* load the ucode */
57666229b20SAlex Deucher 	ucode_data = (const u8 *)rdev->smc_fw->data;
57766229b20SAlex Deucher 	ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address,
57866229b20SAlex Deucher 				      ucode_data, ucode_size, limit);
57966229b20SAlex Deucher 	if (ret)
58066229b20SAlex Deucher 		return ret;
58166229b20SAlex Deucher 
58266229b20SAlex Deucher 	/* set up the int vectors */
58366229b20SAlex Deucher 	ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address,
58466229b20SAlex Deucher 					      int_vect, int_vect_size);
58566229b20SAlex Deucher 	if (ret)
58666229b20SAlex Deucher 		return ret;
58766229b20SAlex Deucher 
58866229b20SAlex Deucher 	return 0;
58966229b20SAlex Deucher }
59066229b20SAlex Deucher 
rv770_read_smc_sram_dword(struct radeon_device * rdev,u16 smc_address,u32 * value,u16 limit)59166229b20SAlex Deucher int rv770_read_smc_sram_dword(struct radeon_device *rdev,
59266229b20SAlex Deucher 			      u16 smc_address, u32 *value, u16 limit)
59366229b20SAlex Deucher {
594fe78118cSAlex Deucher 	unsigned long flags;
59566229b20SAlex Deucher 	int ret;
59666229b20SAlex Deucher 
597fe78118cSAlex Deucher 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
59866229b20SAlex Deucher 	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
599fe78118cSAlex Deucher 	if (ret == 0)
60066229b20SAlex Deucher 		*value = RREG32(SMC_SRAM_DATA);
601fe78118cSAlex Deucher 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
60266229b20SAlex Deucher 
603fe78118cSAlex Deucher 	return ret;
60466229b20SAlex Deucher }
60566229b20SAlex Deucher 
rv770_write_smc_sram_dword(struct radeon_device * rdev,u16 smc_address,u32 value,u16 limit)60666229b20SAlex Deucher int rv770_write_smc_sram_dword(struct radeon_device *rdev,
60766229b20SAlex Deucher 			       u16 smc_address, u32 value, u16 limit)
60866229b20SAlex Deucher {
609fe78118cSAlex Deucher 	unsigned long flags;
61066229b20SAlex Deucher 	int ret;
61166229b20SAlex Deucher 
612fe78118cSAlex Deucher 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
61366229b20SAlex Deucher 	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
614fe78118cSAlex Deucher 	if (ret == 0)
61566229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, value);
616fe78118cSAlex Deucher 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
61766229b20SAlex Deucher 
618fe78118cSAlex Deucher 	return ret;
61966229b20SAlex Deucher }
620