xref: /openbmc/linux/drivers/gpu/drm/radeon/rv770_smc.c (revision 6596afd4)
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>
2666229b20SAlex Deucher #include "drmP.h"
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 
25766229b20SAlex Deucher int rv770_set_smc_sram_address(struct radeon_device *rdev,
25866229b20SAlex Deucher 			       u16 smc_address, u16 limit)
25966229b20SAlex Deucher {
26066229b20SAlex Deucher 	u32 addr;
26166229b20SAlex Deucher 
26266229b20SAlex Deucher 	if (smc_address & 3)
26366229b20SAlex Deucher 		return -EINVAL;
26466229b20SAlex Deucher 	if ((smc_address + 3) > limit)
26566229b20SAlex Deucher 		return -EINVAL;
26666229b20SAlex Deucher 
26766229b20SAlex Deucher 	addr = smc_address;
26866229b20SAlex Deucher 	addr |= SMC_SRAM_AUTO_INC_DIS;
26966229b20SAlex Deucher 
27066229b20SAlex Deucher 	WREG32(SMC_SRAM_ADDR, addr);
27166229b20SAlex Deucher 
27266229b20SAlex Deucher 	return 0;
27366229b20SAlex Deucher }
27466229b20SAlex Deucher 
27566229b20SAlex Deucher int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
27666229b20SAlex Deucher 			    u16 smc_start_address, const u8 *src,
27766229b20SAlex Deucher 			    u16 byte_count, u16 limit)
27866229b20SAlex Deucher {
27966229b20SAlex Deucher 	u32 data, original_data, extra_shift;
28066229b20SAlex Deucher 	u16 addr;
28166229b20SAlex Deucher 	int ret;
28266229b20SAlex Deucher 
28366229b20SAlex Deucher 	if (smc_start_address & 3)
28466229b20SAlex Deucher 		return -EINVAL;
28566229b20SAlex Deucher 	if ((smc_start_address + byte_count) > limit)
28666229b20SAlex Deucher 		return -EINVAL;
28766229b20SAlex Deucher 
28866229b20SAlex Deucher 	addr = smc_start_address;
28966229b20SAlex Deucher 
29066229b20SAlex Deucher 	while (byte_count >= 4) {
29166229b20SAlex Deucher 		/* SMC address space is BE */
29266229b20SAlex Deucher 		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
29366229b20SAlex Deucher 
29466229b20SAlex Deucher 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
29566229b20SAlex Deucher 		if (ret)
29666229b20SAlex Deucher 			return ret;
29766229b20SAlex Deucher 
29866229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, data);
29966229b20SAlex Deucher 
30066229b20SAlex Deucher 		src += 4;
30166229b20SAlex Deucher 		byte_count -= 4;
30266229b20SAlex Deucher 		addr += 4;
30366229b20SAlex Deucher 	}
30466229b20SAlex Deucher 
30566229b20SAlex Deucher 	/* RMW for final bytes */
30666229b20SAlex Deucher 	if (byte_count > 0) {
30766229b20SAlex Deucher 		data = 0;
30866229b20SAlex Deucher 
30966229b20SAlex Deucher 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
31066229b20SAlex Deucher 		if (ret)
31166229b20SAlex Deucher 			return ret;
31266229b20SAlex Deucher 
31366229b20SAlex Deucher 		original_data = RREG32(SMC_SRAM_DATA);
31466229b20SAlex Deucher 
31566229b20SAlex Deucher 		extra_shift = 8 * (4 - byte_count);
31666229b20SAlex Deucher 
31766229b20SAlex Deucher 		while (byte_count > 0) {
31866229b20SAlex Deucher 			/* SMC address space is BE */
31966229b20SAlex Deucher 			data = (data << 8) + *src++;
32066229b20SAlex Deucher 			byte_count--;
32166229b20SAlex Deucher 		}
32266229b20SAlex Deucher 
32366229b20SAlex Deucher 		data <<= extra_shift;
32466229b20SAlex Deucher 
32566229b20SAlex Deucher 		data |= (original_data & ~((~0UL) << extra_shift));
32666229b20SAlex Deucher 
32766229b20SAlex Deucher 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
32866229b20SAlex Deucher 		if (ret)
32966229b20SAlex Deucher 			return ret;
33066229b20SAlex Deucher 
33166229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, data);
33266229b20SAlex Deucher 	}
33366229b20SAlex Deucher 
33466229b20SAlex Deucher 	return 0;
33566229b20SAlex Deucher }
33666229b20SAlex Deucher 
33766229b20SAlex Deucher static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
33866229b20SAlex Deucher 					   u32 smc_first_vector, const u8 *src,
33966229b20SAlex Deucher 					   u32 byte_count)
34066229b20SAlex Deucher {
34166229b20SAlex Deucher 	u32 tmp, i;
34266229b20SAlex Deucher 
34366229b20SAlex Deucher 	if (byte_count % 4)
34466229b20SAlex Deucher 		return -EINVAL;
34566229b20SAlex Deucher 
34666229b20SAlex Deucher 	if (smc_first_vector < FIRST_SMC_INT_VECT_REG) {
34766229b20SAlex Deucher 		tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector;
34866229b20SAlex Deucher 
34966229b20SAlex Deucher 		if (tmp > byte_count)
35066229b20SAlex Deucher 			return 0;
35166229b20SAlex Deucher 
35266229b20SAlex Deucher 		byte_count -= tmp;
35366229b20SAlex Deucher 		src += tmp;
35466229b20SAlex Deucher 		smc_first_vector = FIRST_SMC_INT_VECT_REG;
35566229b20SAlex Deucher 	}
35666229b20SAlex Deucher 
35766229b20SAlex Deucher 	for (i = 0; i < byte_count; i += 4) {
35866229b20SAlex Deucher 		/* SMC address space is BE */
35966229b20SAlex Deucher 		tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
36066229b20SAlex Deucher 
36166229b20SAlex Deucher 		WREG32(SMC_ISR_FFD8_FFDB + i, tmp);
36266229b20SAlex Deucher 	}
36366229b20SAlex Deucher 
36466229b20SAlex Deucher 	return 0;
36566229b20SAlex Deucher }
36666229b20SAlex Deucher 
36766229b20SAlex Deucher void rv770_start_smc(struct radeon_device *rdev)
36866229b20SAlex Deucher {
36966229b20SAlex Deucher 	WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N);
37066229b20SAlex Deucher }
37166229b20SAlex Deucher 
37266229b20SAlex Deucher void rv770_reset_smc(struct radeon_device *rdev)
37366229b20SAlex Deucher {
37466229b20SAlex Deucher 	WREG32_P(SMC_IO, 0, ~SMC_RST_N);
37566229b20SAlex Deucher }
37666229b20SAlex Deucher 
37766229b20SAlex Deucher void rv770_stop_smc_clock(struct radeon_device *rdev)
37866229b20SAlex Deucher {
37966229b20SAlex Deucher 	WREG32_P(SMC_IO, 0, ~SMC_CLK_EN);
38066229b20SAlex Deucher }
38166229b20SAlex Deucher 
38266229b20SAlex Deucher void rv770_start_smc_clock(struct radeon_device *rdev)
38366229b20SAlex Deucher {
38466229b20SAlex Deucher 	WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN);
38566229b20SAlex Deucher }
38666229b20SAlex Deucher 
38766229b20SAlex Deucher bool rv770_is_smc_running(struct radeon_device *rdev)
38866229b20SAlex Deucher {
38966229b20SAlex Deucher 	u32 tmp;
39066229b20SAlex Deucher 
39166229b20SAlex Deucher 	tmp = RREG32(SMC_IO);
39266229b20SAlex Deucher 
39366229b20SAlex Deucher 	if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN))
39466229b20SAlex Deucher 		return true;
39566229b20SAlex Deucher 	else
39666229b20SAlex Deucher 		return false;
39766229b20SAlex Deucher }
39866229b20SAlex Deucher 
39966229b20SAlex Deucher PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
40066229b20SAlex Deucher {
40166229b20SAlex Deucher 	u32 tmp;
40266229b20SAlex Deucher 	int i;
40366229b20SAlex Deucher 	PPSMC_Result result;
40466229b20SAlex Deucher 
40566229b20SAlex Deucher 	if (!rv770_is_smc_running(rdev))
40666229b20SAlex Deucher 		return PPSMC_Result_Failed;
40766229b20SAlex Deucher 
40866229b20SAlex Deucher 	WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK);
40966229b20SAlex Deucher 
41066229b20SAlex Deucher 	for (i = 0; i < rdev->usec_timeout; i++) {
41166229b20SAlex Deucher 		tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
41266229b20SAlex Deucher 		tmp >>= HOST_SMC_RESP_SHIFT;
41366229b20SAlex Deucher 		if (tmp != 0)
41466229b20SAlex Deucher 			break;
41566229b20SAlex Deucher 		udelay(1);
41666229b20SAlex Deucher 	}
41766229b20SAlex Deucher 
41866229b20SAlex Deucher 	tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
41966229b20SAlex Deucher 	tmp >>= HOST_SMC_RESP_SHIFT;
42066229b20SAlex Deucher 
42166229b20SAlex Deucher 	result = (PPSMC_Result)tmp;
42266229b20SAlex Deucher 	return result;
42366229b20SAlex Deucher }
42466229b20SAlex Deucher 
42566229b20SAlex Deucher PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)
42666229b20SAlex Deucher {
42766229b20SAlex Deucher 	int i;
42866229b20SAlex Deucher 	PPSMC_Result result = PPSMC_Result_OK;
42966229b20SAlex Deucher 
43066229b20SAlex Deucher 	if (!rv770_is_smc_running(rdev))
43166229b20SAlex Deucher 		return result;
43266229b20SAlex Deucher 
43366229b20SAlex Deucher 	for (i = 0; i < rdev->usec_timeout; i++) {
43466229b20SAlex Deucher 		if (RREG32(SMC_IO) & SMC_STOP_MODE)
43566229b20SAlex Deucher 			break;
43666229b20SAlex Deucher 		udelay(1);
43766229b20SAlex Deucher 	}
43866229b20SAlex Deucher 
43966229b20SAlex Deucher 	return result;
44066229b20SAlex Deucher }
44166229b20SAlex Deucher 
44266229b20SAlex Deucher static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
44366229b20SAlex Deucher {
44466229b20SAlex Deucher 	u16 i;
44566229b20SAlex Deucher 
44666229b20SAlex Deucher 	for (i = 0;  i < limit; i += 4) {
44766229b20SAlex Deucher 		rv770_set_smc_sram_address(rdev, i, limit);
44866229b20SAlex Deucher 		WREG32(SMC_SRAM_DATA, 0);
44966229b20SAlex Deucher 	}
45066229b20SAlex Deucher }
45166229b20SAlex Deucher 
45266229b20SAlex Deucher int rv770_load_smc_ucode(struct radeon_device *rdev,
45366229b20SAlex Deucher 			 u16 limit)
45466229b20SAlex Deucher {
45566229b20SAlex Deucher 	int ret;
45666229b20SAlex Deucher 	const u8 *int_vect;
45766229b20SAlex Deucher 	u16 int_vect_start_address;
45866229b20SAlex Deucher 	u16 int_vect_size;
45966229b20SAlex Deucher 	const u8 *ucode_data;
46066229b20SAlex Deucher 	u16 ucode_start_address;
46166229b20SAlex Deucher 	u16 ucode_size;
46266229b20SAlex Deucher 
46366229b20SAlex Deucher 	if (!rdev->smc_fw)
46466229b20SAlex Deucher 		return -EINVAL;
46566229b20SAlex Deucher 
46666229b20SAlex Deucher 	rv770_clear_smc_sram(rdev, limit);
46766229b20SAlex Deucher 
46866229b20SAlex Deucher 	switch (rdev->family) {
46966229b20SAlex Deucher 	case CHIP_RV770:
47066229b20SAlex Deucher 		ucode_start_address = RV770_SMC_UCODE_START;
47166229b20SAlex Deucher 		ucode_size = RV770_SMC_UCODE_SIZE;
47266229b20SAlex Deucher 		int_vect = (const u8 *)&rv770_smc_int_vectors;
47366229b20SAlex Deucher 		int_vect_start_address = RV770_SMC_INT_VECTOR_START;
47466229b20SAlex Deucher 		int_vect_size = RV770_SMC_INT_VECTOR_SIZE;
47566229b20SAlex Deucher 		break;
47666229b20SAlex Deucher 	case CHIP_RV730:
47766229b20SAlex Deucher 		ucode_start_address = RV730_SMC_UCODE_START;
47866229b20SAlex Deucher 		ucode_size = RV730_SMC_UCODE_SIZE;
47966229b20SAlex Deucher 		int_vect = (const u8 *)&rv730_smc_int_vectors;
48066229b20SAlex Deucher 		int_vect_start_address = RV730_SMC_INT_VECTOR_START;
48166229b20SAlex Deucher 		int_vect_size = RV730_SMC_INT_VECTOR_SIZE;
48266229b20SAlex Deucher 		break;
48366229b20SAlex Deucher 	case CHIP_RV710:
48466229b20SAlex Deucher 		ucode_start_address = RV710_SMC_UCODE_START;
48566229b20SAlex Deucher 		ucode_size = RV710_SMC_UCODE_SIZE;
48666229b20SAlex Deucher 		int_vect = (const u8 *)&rv710_smc_int_vectors;
48766229b20SAlex Deucher 		int_vect_start_address = RV710_SMC_INT_VECTOR_START;
48866229b20SAlex Deucher 		int_vect_size = RV710_SMC_INT_VECTOR_SIZE;
48966229b20SAlex Deucher 		break;
49066229b20SAlex Deucher 	case CHIP_RV740:
49166229b20SAlex Deucher 		ucode_start_address = RV740_SMC_UCODE_START;
49266229b20SAlex Deucher 		ucode_size = RV740_SMC_UCODE_SIZE;
49366229b20SAlex Deucher 		int_vect = (const u8 *)&rv740_smc_int_vectors;
49466229b20SAlex Deucher 		int_vect_start_address = RV740_SMC_INT_VECTOR_START;
49566229b20SAlex Deucher 		int_vect_size = RV740_SMC_INT_VECTOR_SIZE;
49666229b20SAlex Deucher 		break;
497dc50ba7fSAlex Deucher 	case CHIP_CEDAR:
498dc50ba7fSAlex Deucher 		ucode_start_address = CEDAR_SMC_UCODE_START;
499dc50ba7fSAlex Deucher 		ucode_size = CEDAR_SMC_UCODE_SIZE;
500dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&cedar_smc_int_vectors;
501dc50ba7fSAlex Deucher 		int_vect_start_address = CEDAR_SMC_INT_VECTOR_START;
502dc50ba7fSAlex Deucher 		int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE;
503dc50ba7fSAlex Deucher 		break;
504dc50ba7fSAlex Deucher 	case CHIP_REDWOOD:
505dc50ba7fSAlex Deucher 		ucode_start_address = REDWOOD_SMC_UCODE_START;
506dc50ba7fSAlex Deucher 		ucode_size = REDWOOD_SMC_UCODE_SIZE;
507dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&redwood_smc_int_vectors;
508dc50ba7fSAlex Deucher 		int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START;
509dc50ba7fSAlex Deucher 		int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE;
510dc50ba7fSAlex Deucher 		break;
511dc50ba7fSAlex Deucher 	case CHIP_JUNIPER:
512dc50ba7fSAlex Deucher 		ucode_start_address = JUNIPER_SMC_UCODE_START;
513dc50ba7fSAlex Deucher 		ucode_size = JUNIPER_SMC_UCODE_SIZE;
514dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&juniper_smc_int_vectors;
515dc50ba7fSAlex Deucher 		int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START;
516dc50ba7fSAlex Deucher 		int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE;
517dc50ba7fSAlex Deucher 		break;
518dc50ba7fSAlex Deucher 	case CHIP_CYPRESS:
519dc50ba7fSAlex Deucher 	case CHIP_HEMLOCK:
520dc50ba7fSAlex Deucher 		ucode_start_address = CYPRESS_SMC_UCODE_START;
521dc50ba7fSAlex Deucher 		ucode_size = CYPRESS_SMC_UCODE_SIZE;
522dc50ba7fSAlex Deucher 		int_vect = (const u8 *)&cypress_smc_int_vectors;
523dc50ba7fSAlex Deucher 		int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START;
524dc50ba7fSAlex Deucher 		int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE;
525dc50ba7fSAlex Deucher 		break;
5266596afd4SAlex Deucher 	case CHIP_BARTS:
5276596afd4SAlex Deucher 		ucode_start_address = BARTS_SMC_UCODE_START;
5286596afd4SAlex Deucher 		ucode_size = BARTS_SMC_UCODE_SIZE;
5296596afd4SAlex Deucher 		int_vect = (const u8 *)&barts_smc_int_vectors;
5306596afd4SAlex Deucher 		int_vect_start_address = BARTS_SMC_INT_VECTOR_START;
5316596afd4SAlex Deucher 		int_vect_size = BARTS_SMC_INT_VECTOR_SIZE;
5326596afd4SAlex Deucher 		break;
5336596afd4SAlex Deucher 	case CHIP_TURKS:
5346596afd4SAlex Deucher 		ucode_start_address = TURKS_SMC_UCODE_START;
5356596afd4SAlex Deucher 		ucode_size = TURKS_SMC_UCODE_SIZE;
5366596afd4SAlex Deucher 		int_vect = (const u8 *)&turks_smc_int_vectors;
5376596afd4SAlex Deucher 		int_vect_start_address = TURKS_SMC_INT_VECTOR_START;
5386596afd4SAlex Deucher 		int_vect_size = TURKS_SMC_INT_VECTOR_SIZE;
5396596afd4SAlex Deucher 		break;
5406596afd4SAlex Deucher 	case CHIP_CAICOS:
5416596afd4SAlex Deucher 		ucode_start_address = CAICOS_SMC_UCODE_START;
5426596afd4SAlex Deucher 		ucode_size = CAICOS_SMC_UCODE_SIZE;
5436596afd4SAlex Deucher 		int_vect = (const u8 *)&caicos_smc_int_vectors;
5446596afd4SAlex Deucher 		int_vect_start_address = CAICOS_SMC_INT_VECTOR_START;
5456596afd4SAlex Deucher 		int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE;
5466596afd4SAlex Deucher 		break;
54766229b20SAlex Deucher 	default:
54866229b20SAlex Deucher 		DRM_ERROR("unknown asic in smc ucode loader\n");
54966229b20SAlex Deucher 		BUG();
55066229b20SAlex Deucher 	}
55166229b20SAlex Deucher 
55266229b20SAlex Deucher 	/* load the ucode */
55366229b20SAlex Deucher 	ucode_data = (const u8 *)rdev->smc_fw->data;
55466229b20SAlex Deucher 	ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address,
55566229b20SAlex Deucher 				      ucode_data, ucode_size, limit);
55666229b20SAlex Deucher 	if (ret)
55766229b20SAlex Deucher 		return ret;
55866229b20SAlex Deucher 
55966229b20SAlex Deucher 	/* set up the int vectors */
56066229b20SAlex Deucher 	ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address,
56166229b20SAlex Deucher 					      int_vect, int_vect_size);
56266229b20SAlex Deucher 	if (ret)
56366229b20SAlex Deucher 		return ret;
56466229b20SAlex Deucher 
56566229b20SAlex Deucher 	return 0;
56666229b20SAlex Deucher }
56766229b20SAlex Deucher 
56866229b20SAlex Deucher int rv770_read_smc_sram_dword(struct radeon_device *rdev,
56966229b20SAlex Deucher 			      u16 smc_address, u32 *value, u16 limit)
57066229b20SAlex Deucher {
57166229b20SAlex Deucher 	int ret;
57266229b20SAlex Deucher 
57366229b20SAlex Deucher 	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
57466229b20SAlex Deucher 	if (ret)
57566229b20SAlex Deucher 		return ret;
57666229b20SAlex Deucher 
57766229b20SAlex Deucher 	*value = RREG32(SMC_SRAM_DATA);
57866229b20SAlex Deucher 
57966229b20SAlex Deucher 	return 0;
58066229b20SAlex Deucher }
58166229b20SAlex Deucher 
58266229b20SAlex Deucher int rv770_write_smc_sram_dword(struct radeon_device *rdev,
58366229b20SAlex Deucher 			       u16 smc_address, u32 value, u16 limit)
58466229b20SAlex Deucher {
58566229b20SAlex Deucher 	int ret;
58666229b20SAlex Deucher 
58766229b20SAlex Deucher 	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
58866229b20SAlex Deucher 	if (ret)
58966229b20SAlex Deucher 		return ret;
59066229b20SAlex Deucher 
59166229b20SAlex Deucher 	WREG32(SMC_SRAM_DATA, value);
59266229b20SAlex Deucher 
59366229b20SAlex Deucher 	return 0;
59466229b20SAlex Deucher }
595