xref: /openbsd-src/sys/dev/pci/drm/radeon/rv770_smc.c (revision f005ef32267c16bdb134f0e9fa4477dbe07c263a)
17ccd5a2cSjsg /*
27ccd5a2cSjsg  * Copyright 2011 Advanced Micro Devices, Inc.
37ccd5a2cSjsg  *
47ccd5a2cSjsg  * Permission is hereby granted, free of charge, to any person obtaining a
57ccd5a2cSjsg  * copy of this software and associated documentation files (the "Software"),
67ccd5a2cSjsg  * to deal in the Software without restriction, including without limitation
77ccd5a2cSjsg  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ccd5a2cSjsg  * and/or sell copies of the Software, and to permit persons to whom the
97ccd5a2cSjsg  * Software is furnished to do so, subject to the following conditions:
107ccd5a2cSjsg  *
117ccd5a2cSjsg  * The above copyright notice and this permission notice shall be included in
127ccd5a2cSjsg  * all copies or substantial portions of the Software.
137ccd5a2cSjsg  *
147ccd5a2cSjsg  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
157ccd5a2cSjsg  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
167ccd5a2cSjsg  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
177ccd5a2cSjsg  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
187ccd5a2cSjsg  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
197ccd5a2cSjsg  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
207ccd5a2cSjsg  * OTHER DEALINGS IN THE SOFTWARE.
217ccd5a2cSjsg  *
227ccd5a2cSjsg  * Authors: Alex Deucher
237ccd5a2cSjsg  */
247ccd5a2cSjsg 
257f4dd379Sjsg #include <linux/firmware.h>
26c349dbc7Sjsg 
277ccd5a2cSjsg #include "radeon.h"
287ccd5a2cSjsg #include "rv770d.h"
297ccd5a2cSjsg #include "rv770_dpm.h"
307ccd5a2cSjsg #include "rv770_smc.h"
317ccd5a2cSjsg #include "atom.h"
327ccd5a2cSjsg #include "radeon_ucode.h"
337ccd5a2cSjsg 
347ccd5a2cSjsg #define FIRST_SMC_INT_VECT_REG 0xFFD8
357ccd5a2cSjsg #define FIRST_INT_VECT_S19     0xFFC0
367ccd5a2cSjsg 
37*f005ef32Sjsg static const u8 rv770_smc_int_vectors[] = {
387ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
397ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
407ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
417ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
427ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
437ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
447ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
457ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
467ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
477ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
487ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
497ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
507ccd5a2cSjsg 	0x08, 0x10, 0x0C, 0xD7,
517ccd5a2cSjsg 	0x08, 0x2B, 0x08, 0x10,
527ccd5a2cSjsg 	0x03, 0x51, 0x03, 0x51,
537ccd5a2cSjsg 	0x03, 0x51, 0x03, 0x51
547ccd5a2cSjsg };
557ccd5a2cSjsg 
56*f005ef32Sjsg static const u8 rv730_smc_int_vectors[] = {
577ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
587ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
597ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
607ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
617ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
627ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
637ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
647ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
657ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
667ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
677ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
687ccd5a2cSjsg 	0x08, 0x15, 0x08, 0x15,
697ccd5a2cSjsg 	0x08, 0x15, 0x0C, 0xBB,
707ccd5a2cSjsg 	0x08, 0x30, 0x08, 0x15,
717ccd5a2cSjsg 	0x03, 0x56, 0x03, 0x56,
727ccd5a2cSjsg 	0x03, 0x56, 0x03, 0x56
737ccd5a2cSjsg };
747ccd5a2cSjsg 
75*f005ef32Sjsg static const u8 rv710_smc_int_vectors[] = {
767ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
777ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
787ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
797ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
807ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
817ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
827ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
837ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
847ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
857ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
867ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
877ccd5a2cSjsg 	0x08, 0x04, 0x08, 0x04,
887ccd5a2cSjsg 	0x08, 0x04, 0x0C, 0xCB,
897ccd5a2cSjsg 	0x08, 0x1F, 0x08, 0x04,
907ccd5a2cSjsg 	0x03, 0x51, 0x03, 0x51,
917ccd5a2cSjsg 	0x03, 0x51, 0x03, 0x51
927ccd5a2cSjsg };
937ccd5a2cSjsg 
94*f005ef32Sjsg static const u8 rv740_smc_int_vectors[] = {
957ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
967ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
977ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
987ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
997ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
1007ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
1017ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
1027ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
1037ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
1047ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
1057ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
1067ccd5a2cSjsg 	0x08, 0x10, 0x08, 0x10,
1077ccd5a2cSjsg 	0x08, 0x10, 0x0C, 0xD7,
1087ccd5a2cSjsg 	0x08, 0x2B, 0x08, 0x10,
1097ccd5a2cSjsg 	0x03, 0x51, 0x03, 0x51,
1107ccd5a2cSjsg 	0x03, 0x51, 0x03, 0x51
1117ccd5a2cSjsg };
1127ccd5a2cSjsg 
113*f005ef32Sjsg static const u8 cedar_smc_int_vectors[] = {
1147ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1157ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1167ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1177ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1187ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1197ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1207ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1217ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1227ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1237ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1247ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1257ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1267ccd5a2cSjsg 	0x0B, 0x05, 0x11, 0x8B,
1277ccd5a2cSjsg 	0x0B, 0x20, 0x0B, 0x05,
1287ccd5a2cSjsg 	0x04, 0xF6, 0x04, 0xF6,
1297ccd5a2cSjsg 	0x04, 0xF6, 0x04, 0xF6
1307ccd5a2cSjsg };
1317ccd5a2cSjsg 
132*f005ef32Sjsg static const u8 redwood_smc_int_vectors[] = {
1337ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1347ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1357ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1367ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1377ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1387ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1397ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1407ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1417ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1427ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1437ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1447ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1457ccd5a2cSjsg 	0x0B, 0x05, 0x11, 0x8B,
1467ccd5a2cSjsg 	0x0B, 0x20, 0x0B, 0x05,
1477ccd5a2cSjsg 	0x04, 0xF6, 0x04, 0xF6,
1487ccd5a2cSjsg 	0x04, 0xF6, 0x04, 0xF6
1497ccd5a2cSjsg };
1507ccd5a2cSjsg 
151*f005ef32Sjsg static const u8 juniper_smc_int_vectors[] = {
1527ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1537ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1547ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1557ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1567ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1577ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1587ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1597ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1607ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1617ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1627ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1637ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1647ccd5a2cSjsg 	0x0B, 0x05, 0x11, 0x8B,
1657ccd5a2cSjsg 	0x0B, 0x20, 0x0B, 0x05,
1667ccd5a2cSjsg 	0x04, 0xF6, 0x04, 0xF6,
1677ccd5a2cSjsg 	0x04, 0xF6, 0x04, 0xF6
1687ccd5a2cSjsg };
1697ccd5a2cSjsg 
170*f005ef32Sjsg static const u8 cypress_smc_int_vectors[] = {
1717ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1727ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1737ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1747ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1757ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1767ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1777ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1787ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1797ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1807ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1817ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1827ccd5a2cSjsg 	0x0B, 0x05, 0x0B, 0x05,
1837ccd5a2cSjsg 	0x0B, 0x05, 0x11, 0x8B,
1847ccd5a2cSjsg 	0x0B, 0x20, 0x0B, 0x05,
1857ccd5a2cSjsg 	0x04, 0xF6, 0x04, 0xF6,
1867ccd5a2cSjsg 	0x04, 0xF6, 0x04, 0xF6
1877ccd5a2cSjsg };
1887ccd5a2cSjsg 
189*f005ef32Sjsg static const u8 barts_smc_int_vectors[] = {
1907ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
1917ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
1927ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
1937ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
1947ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
1957ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
1967ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
1977ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
1987ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
1997ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2007ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2017ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2027ccd5a2cSjsg 	0x0C, 0x14, 0x12, 0xAA,
2037ccd5a2cSjsg 	0x0C, 0x2F, 0x15, 0xF6,
2047ccd5a2cSjsg 	0x15, 0xF6, 0x05, 0x0A,
2057ccd5a2cSjsg 	0x05, 0x0A, 0x05, 0x0A
2067ccd5a2cSjsg };
2077ccd5a2cSjsg 
208*f005ef32Sjsg static const u8 turks_smc_int_vectors[] = {
2097ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2107ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2117ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2127ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2137ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2147ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2157ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2167ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2177ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2187ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2197ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2207ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2217ccd5a2cSjsg 	0x0C, 0x14, 0x12, 0xAA,
2227ccd5a2cSjsg 	0x0C, 0x2F, 0x15, 0xF6,
2237ccd5a2cSjsg 	0x15, 0xF6, 0x05, 0x0A,
2247ccd5a2cSjsg 	0x05, 0x0A, 0x05, 0x0A
2257ccd5a2cSjsg };
2267ccd5a2cSjsg 
227*f005ef32Sjsg static const u8 caicos_smc_int_vectors[] = {
2287ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2297ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2307ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2317ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2327ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2337ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2347ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2357ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2367ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2377ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2387ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2397ccd5a2cSjsg 	0x0C, 0x14, 0x0C, 0x14,
2407ccd5a2cSjsg 	0x0C, 0x14, 0x12, 0xAA,
2417ccd5a2cSjsg 	0x0C, 0x2F, 0x15, 0xF6,
2427ccd5a2cSjsg 	0x15, 0xF6, 0x05, 0x0A,
2437ccd5a2cSjsg 	0x05, 0x0A, 0x05, 0x0A
2447ccd5a2cSjsg };
2457ccd5a2cSjsg 
246*f005ef32Sjsg static const u8 cayman_smc_int_vectors[] = {
2477ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2487ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2497ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2507ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2517ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2527ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2537ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2547ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2557ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2567ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2577ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2587ccd5a2cSjsg 	0x12, 0x05, 0x12, 0x05,
2597ccd5a2cSjsg 	0x12, 0x05, 0x18, 0xEA,
2607ccd5a2cSjsg 	0x12, 0x20, 0x1C, 0x34,
2617ccd5a2cSjsg 	0x1C, 0x34, 0x08, 0x72,
2627ccd5a2cSjsg 	0x08, 0x72, 0x08, 0x72
2637ccd5a2cSjsg };
2647ccd5a2cSjsg 
rv770_set_smc_sram_address(struct radeon_device * rdev,u16 smc_address,u16 limit)2657ccd5a2cSjsg static int rv770_set_smc_sram_address(struct radeon_device *rdev,
2667ccd5a2cSjsg 				      u16 smc_address, u16 limit)
2677ccd5a2cSjsg {
2687ccd5a2cSjsg 	u32 addr;
2697ccd5a2cSjsg 
2707ccd5a2cSjsg 	if (smc_address & 3)
2717ccd5a2cSjsg 		return -EINVAL;
2727ccd5a2cSjsg 	if ((smc_address + 3) > limit)
2737ccd5a2cSjsg 		return -EINVAL;
2747ccd5a2cSjsg 
2757ccd5a2cSjsg 	addr = smc_address;
2767ccd5a2cSjsg 	addr |= SMC_SRAM_AUTO_INC_DIS;
2777ccd5a2cSjsg 
2787ccd5a2cSjsg 	WREG32(SMC_SRAM_ADDR, addr);
2797ccd5a2cSjsg 
2807ccd5a2cSjsg 	return 0;
2817ccd5a2cSjsg }
2827ccd5a2cSjsg 
rv770_copy_bytes_to_smc(struct radeon_device * rdev,u16 smc_start_address,const u8 * src,u16 byte_count,u16 limit)2837ccd5a2cSjsg int rv770_copy_bytes_to_smc(struct radeon_device *rdev,
2847ccd5a2cSjsg 			    u16 smc_start_address, const u8 *src,
2857ccd5a2cSjsg 			    u16 byte_count, u16 limit)
2867ccd5a2cSjsg {
2877ccd5a2cSjsg 	unsigned long flags;
2887ccd5a2cSjsg 	u32 data, original_data, extra_shift;
2897ccd5a2cSjsg 	u16 addr;
2907ccd5a2cSjsg 	int ret = 0;
2917ccd5a2cSjsg 
2927ccd5a2cSjsg 	if (smc_start_address & 3)
2937ccd5a2cSjsg 		return -EINVAL;
2947ccd5a2cSjsg 	if ((smc_start_address + byte_count) > limit)
2957ccd5a2cSjsg 		return -EINVAL;
2967ccd5a2cSjsg 
2977ccd5a2cSjsg 	addr = smc_start_address;
2987ccd5a2cSjsg 
2997ccd5a2cSjsg 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
3007ccd5a2cSjsg 	while (byte_count >= 4) {
3017ccd5a2cSjsg 		/* SMC address space is BE */
3027ccd5a2cSjsg 		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
3037ccd5a2cSjsg 
3047ccd5a2cSjsg 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
3057ccd5a2cSjsg 		if (ret)
3067ccd5a2cSjsg 			goto done;
3077ccd5a2cSjsg 
3087ccd5a2cSjsg 		WREG32(SMC_SRAM_DATA, data);
3097ccd5a2cSjsg 
3107ccd5a2cSjsg 		src += 4;
3117ccd5a2cSjsg 		byte_count -= 4;
3127ccd5a2cSjsg 		addr += 4;
3137ccd5a2cSjsg 	}
3147ccd5a2cSjsg 
3157ccd5a2cSjsg 	/* RMW for final bytes */
3167ccd5a2cSjsg 	if (byte_count > 0) {
3177ccd5a2cSjsg 		data = 0;
3187ccd5a2cSjsg 
3197ccd5a2cSjsg 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
3207ccd5a2cSjsg 		if (ret)
3217ccd5a2cSjsg 			goto done;
3227ccd5a2cSjsg 
3237ccd5a2cSjsg 		original_data = RREG32(SMC_SRAM_DATA);
3247ccd5a2cSjsg 
3257ccd5a2cSjsg 		extra_shift = 8 * (4 - byte_count);
3267ccd5a2cSjsg 
3277ccd5a2cSjsg 		while (byte_count > 0) {
3287ccd5a2cSjsg 			/* SMC address space is BE */
3297ccd5a2cSjsg 			data = (data << 8) + *src++;
3307ccd5a2cSjsg 			byte_count--;
3317ccd5a2cSjsg 		}
3327ccd5a2cSjsg 
3337ccd5a2cSjsg 		data <<= extra_shift;
3347ccd5a2cSjsg 
3357ccd5a2cSjsg 		data |= (original_data & ~((~0UL) << extra_shift));
3367ccd5a2cSjsg 
3377ccd5a2cSjsg 		ret = rv770_set_smc_sram_address(rdev, addr, limit);
3387ccd5a2cSjsg 		if (ret)
3397ccd5a2cSjsg 			goto done;
3407ccd5a2cSjsg 
3417ccd5a2cSjsg 		WREG32(SMC_SRAM_DATA, data);
3427ccd5a2cSjsg 	}
3437ccd5a2cSjsg 
3447ccd5a2cSjsg done:
3457ccd5a2cSjsg 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
3467ccd5a2cSjsg 
3477ccd5a2cSjsg 	return ret;
3487ccd5a2cSjsg }
3497ccd5a2cSjsg 
rv770_program_interrupt_vectors(struct radeon_device * rdev,u32 smc_first_vector,const u8 * src,u32 byte_count)3507ccd5a2cSjsg static int rv770_program_interrupt_vectors(struct radeon_device *rdev,
3517ccd5a2cSjsg 					   u32 smc_first_vector, const u8 *src,
3527ccd5a2cSjsg 					   u32 byte_count)
3537ccd5a2cSjsg {
3547ccd5a2cSjsg 	u32 tmp, i;
3557ccd5a2cSjsg 
3567ccd5a2cSjsg 	if (byte_count % 4)
3577ccd5a2cSjsg 		return -EINVAL;
3587ccd5a2cSjsg 
3597ccd5a2cSjsg 	if (smc_first_vector < FIRST_SMC_INT_VECT_REG) {
3607ccd5a2cSjsg 		tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector;
3617ccd5a2cSjsg 
3627ccd5a2cSjsg 		if (tmp > byte_count)
3637ccd5a2cSjsg 			return 0;
3647ccd5a2cSjsg 
3657ccd5a2cSjsg 		byte_count -= tmp;
3667ccd5a2cSjsg 		src += tmp;
3677ccd5a2cSjsg 		smc_first_vector = FIRST_SMC_INT_VECT_REG;
3687ccd5a2cSjsg 	}
3697ccd5a2cSjsg 
3707ccd5a2cSjsg 	for (i = 0; i < byte_count; i += 4) {
3717ccd5a2cSjsg 		/* SMC address space is BE */
3727ccd5a2cSjsg 		tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3];
3737ccd5a2cSjsg 
3747ccd5a2cSjsg 		WREG32(SMC_ISR_FFD8_FFDB + i, tmp);
3757ccd5a2cSjsg 	}
3767ccd5a2cSjsg 
3777ccd5a2cSjsg 	return 0;
3787ccd5a2cSjsg }
3797ccd5a2cSjsg 
rv770_start_smc(struct radeon_device * rdev)3807ccd5a2cSjsg void rv770_start_smc(struct radeon_device *rdev)
3817ccd5a2cSjsg {
3827ccd5a2cSjsg 	WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N);
3837ccd5a2cSjsg }
3847ccd5a2cSjsg 
rv770_reset_smc(struct radeon_device * rdev)3857ccd5a2cSjsg void rv770_reset_smc(struct radeon_device *rdev)
3867ccd5a2cSjsg {
3877ccd5a2cSjsg 	WREG32_P(SMC_IO, 0, ~SMC_RST_N);
3887ccd5a2cSjsg }
3897ccd5a2cSjsg 
rv770_stop_smc_clock(struct radeon_device * rdev)3907ccd5a2cSjsg void rv770_stop_smc_clock(struct radeon_device *rdev)
3917ccd5a2cSjsg {
3927ccd5a2cSjsg 	WREG32_P(SMC_IO, 0, ~SMC_CLK_EN);
3937ccd5a2cSjsg }
3947ccd5a2cSjsg 
rv770_start_smc_clock(struct radeon_device * rdev)3957ccd5a2cSjsg void rv770_start_smc_clock(struct radeon_device *rdev)
3967ccd5a2cSjsg {
3977ccd5a2cSjsg 	WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN);
3987ccd5a2cSjsg }
3997ccd5a2cSjsg 
rv770_is_smc_running(struct radeon_device * rdev)4007ccd5a2cSjsg bool rv770_is_smc_running(struct radeon_device *rdev)
4017ccd5a2cSjsg {
4027ccd5a2cSjsg 	u32 tmp;
4037ccd5a2cSjsg 
4047ccd5a2cSjsg 	tmp = RREG32(SMC_IO);
4057ccd5a2cSjsg 
4067ccd5a2cSjsg 	if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN))
4077ccd5a2cSjsg 		return true;
4087ccd5a2cSjsg 	else
4097ccd5a2cSjsg 		return false;
4107ccd5a2cSjsg }
4117ccd5a2cSjsg 
rv770_send_msg_to_smc(struct radeon_device * rdev,PPSMC_Msg msg)4127ccd5a2cSjsg PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg)
4137ccd5a2cSjsg {
4147ccd5a2cSjsg 	u32 tmp;
4157ccd5a2cSjsg 	int i;
4167ccd5a2cSjsg 	PPSMC_Result result;
4177ccd5a2cSjsg 
4187ccd5a2cSjsg 	if (!rv770_is_smc_running(rdev))
4197ccd5a2cSjsg 		return PPSMC_Result_Failed;
4207ccd5a2cSjsg 
4217ccd5a2cSjsg 	WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK);
4227ccd5a2cSjsg 
4237ccd5a2cSjsg 	for (i = 0; i < rdev->usec_timeout; i++) {
4247ccd5a2cSjsg 		tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
4257ccd5a2cSjsg 		tmp >>= HOST_SMC_RESP_SHIFT;
4267ccd5a2cSjsg 		if (tmp != 0)
4277ccd5a2cSjsg 			break;
4287ccd5a2cSjsg 		udelay(1);
4297ccd5a2cSjsg 	}
4307ccd5a2cSjsg 
4317ccd5a2cSjsg 	tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK;
4327ccd5a2cSjsg 	tmp >>= HOST_SMC_RESP_SHIFT;
4337ccd5a2cSjsg 
4347ccd5a2cSjsg 	result = (PPSMC_Result)tmp;
4357ccd5a2cSjsg 	return result;
4367ccd5a2cSjsg }
4377ccd5a2cSjsg 
rv770_wait_for_smc_inactive(struct radeon_device * rdev)4387ccd5a2cSjsg PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev)
4397ccd5a2cSjsg {
4407ccd5a2cSjsg 	int i;
4417ccd5a2cSjsg 	PPSMC_Result result = PPSMC_Result_OK;
4427ccd5a2cSjsg 
4437ccd5a2cSjsg 	if (!rv770_is_smc_running(rdev))
4447ccd5a2cSjsg 		return result;
4457ccd5a2cSjsg 
4467ccd5a2cSjsg 	for (i = 0; i < rdev->usec_timeout; i++) {
4477ccd5a2cSjsg 		if (RREG32(SMC_IO) & SMC_STOP_MODE)
4487ccd5a2cSjsg 			break;
4497ccd5a2cSjsg 		udelay(1);
4507ccd5a2cSjsg 	}
4517ccd5a2cSjsg 
4527ccd5a2cSjsg 	return result;
4537ccd5a2cSjsg }
4547ccd5a2cSjsg 
rv770_clear_smc_sram(struct radeon_device * rdev,u16 limit)4557ccd5a2cSjsg static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit)
4567ccd5a2cSjsg {
4577ccd5a2cSjsg 	unsigned long flags;
4587ccd5a2cSjsg 	u16 i;
4597ccd5a2cSjsg 
4607ccd5a2cSjsg 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
4617ccd5a2cSjsg 	for (i = 0;  i < limit; i += 4) {
4627ccd5a2cSjsg 		rv770_set_smc_sram_address(rdev, i, limit);
4637ccd5a2cSjsg 		WREG32(SMC_SRAM_DATA, 0);
4647ccd5a2cSjsg 	}
4657ccd5a2cSjsg 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
4667ccd5a2cSjsg }
4677ccd5a2cSjsg 
rv770_load_smc_ucode(struct radeon_device * rdev,u16 limit)4687ccd5a2cSjsg int rv770_load_smc_ucode(struct radeon_device *rdev,
4697ccd5a2cSjsg 			 u16 limit)
4707ccd5a2cSjsg {
4717ccd5a2cSjsg 	int ret;
4727ccd5a2cSjsg 	const u8 *int_vect;
4737ccd5a2cSjsg 	u16 int_vect_start_address;
4747ccd5a2cSjsg 	u16 int_vect_size;
4757ccd5a2cSjsg 	const u8 *ucode_data;
4767ccd5a2cSjsg 	u16 ucode_start_address;
4777ccd5a2cSjsg 	u16 ucode_size;
4787ccd5a2cSjsg 
4797ccd5a2cSjsg 	if (!rdev->smc_fw)
4807ccd5a2cSjsg 		return -EINVAL;
4817ccd5a2cSjsg 
4827ccd5a2cSjsg 	rv770_clear_smc_sram(rdev, limit);
4837ccd5a2cSjsg 
4847ccd5a2cSjsg 	switch (rdev->family) {
4857ccd5a2cSjsg 	case CHIP_RV770:
4867ccd5a2cSjsg 		ucode_start_address = RV770_SMC_UCODE_START;
4877ccd5a2cSjsg 		ucode_size = RV770_SMC_UCODE_SIZE;
4887ccd5a2cSjsg 		int_vect = (const u8 *)&rv770_smc_int_vectors;
4897ccd5a2cSjsg 		int_vect_start_address = RV770_SMC_INT_VECTOR_START;
4907ccd5a2cSjsg 		int_vect_size = RV770_SMC_INT_VECTOR_SIZE;
4917ccd5a2cSjsg 		break;
4927ccd5a2cSjsg 	case CHIP_RV730:
4937ccd5a2cSjsg 		ucode_start_address = RV730_SMC_UCODE_START;
4947ccd5a2cSjsg 		ucode_size = RV730_SMC_UCODE_SIZE;
4957ccd5a2cSjsg 		int_vect = (const u8 *)&rv730_smc_int_vectors;
4967ccd5a2cSjsg 		int_vect_start_address = RV730_SMC_INT_VECTOR_START;
4977ccd5a2cSjsg 		int_vect_size = RV730_SMC_INT_VECTOR_SIZE;
4987ccd5a2cSjsg 		break;
4997ccd5a2cSjsg 	case CHIP_RV710:
5007ccd5a2cSjsg 		ucode_start_address = RV710_SMC_UCODE_START;
5017ccd5a2cSjsg 		ucode_size = RV710_SMC_UCODE_SIZE;
5027ccd5a2cSjsg 		int_vect = (const u8 *)&rv710_smc_int_vectors;
5037ccd5a2cSjsg 		int_vect_start_address = RV710_SMC_INT_VECTOR_START;
5047ccd5a2cSjsg 		int_vect_size = RV710_SMC_INT_VECTOR_SIZE;
5057ccd5a2cSjsg 		break;
5067ccd5a2cSjsg 	case CHIP_RV740:
5077ccd5a2cSjsg 		ucode_start_address = RV740_SMC_UCODE_START;
5087ccd5a2cSjsg 		ucode_size = RV740_SMC_UCODE_SIZE;
5097ccd5a2cSjsg 		int_vect = (const u8 *)&rv740_smc_int_vectors;
5107ccd5a2cSjsg 		int_vect_start_address = RV740_SMC_INT_VECTOR_START;
5117ccd5a2cSjsg 		int_vect_size = RV740_SMC_INT_VECTOR_SIZE;
5127ccd5a2cSjsg 		break;
5137ccd5a2cSjsg 	case CHIP_CEDAR:
5147ccd5a2cSjsg 		ucode_start_address = CEDAR_SMC_UCODE_START;
5157ccd5a2cSjsg 		ucode_size = CEDAR_SMC_UCODE_SIZE;
5167ccd5a2cSjsg 		int_vect = (const u8 *)&cedar_smc_int_vectors;
5177ccd5a2cSjsg 		int_vect_start_address = CEDAR_SMC_INT_VECTOR_START;
5187ccd5a2cSjsg 		int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE;
5197ccd5a2cSjsg 		break;
5207ccd5a2cSjsg 	case CHIP_REDWOOD:
5217ccd5a2cSjsg 		ucode_start_address = REDWOOD_SMC_UCODE_START;
5227ccd5a2cSjsg 		ucode_size = REDWOOD_SMC_UCODE_SIZE;
5237ccd5a2cSjsg 		int_vect = (const u8 *)&redwood_smc_int_vectors;
5247ccd5a2cSjsg 		int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START;
5257ccd5a2cSjsg 		int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE;
5267ccd5a2cSjsg 		break;
5277ccd5a2cSjsg 	case CHIP_JUNIPER:
5287ccd5a2cSjsg 		ucode_start_address = JUNIPER_SMC_UCODE_START;
5297ccd5a2cSjsg 		ucode_size = JUNIPER_SMC_UCODE_SIZE;
5307ccd5a2cSjsg 		int_vect = (const u8 *)&juniper_smc_int_vectors;
5317ccd5a2cSjsg 		int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START;
5327ccd5a2cSjsg 		int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE;
5337ccd5a2cSjsg 		break;
5347ccd5a2cSjsg 	case CHIP_CYPRESS:
5357ccd5a2cSjsg 	case CHIP_HEMLOCK:
5367ccd5a2cSjsg 		ucode_start_address = CYPRESS_SMC_UCODE_START;
5377ccd5a2cSjsg 		ucode_size = CYPRESS_SMC_UCODE_SIZE;
5387ccd5a2cSjsg 		int_vect = (const u8 *)&cypress_smc_int_vectors;
5397ccd5a2cSjsg 		int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START;
5407ccd5a2cSjsg 		int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE;
5417ccd5a2cSjsg 		break;
5427ccd5a2cSjsg 	case CHIP_BARTS:
5437ccd5a2cSjsg 		ucode_start_address = BARTS_SMC_UCODE_START;
5447ccd5a2cSjsg 		ucode_size = BARTS_SMC_UCODE_SIZE;
5457ccd5a2cSjsg 		int_vect = (const u8 *)&barts_smc_int_vectors;
5467ccd5a2cSjsg 		int_vect_start_address = BARTS_SMC_INT_VECTOR_START;
5477ccd5a2cSjsg 		int_vect_size = BARTS_SMC_INT_VECTOR_SIZE;
5487ccd5a2cSjsg 		break;
5497ccd5a2cSjsg 	case CHIP_TURKS:
5507ccd5a2cSjsg 		ucode_start_address = TURKS_SMC_UCODE_START;
5517ccd5a2cSjsg 		ucode_size = TURKS_SMC_UCODE_SIZE;
5527ccd5a2cSjsg 		int_vect = (const u8 *)&turks_smc_int_vectors;
5537ccd5a2cSjsg 		int_vect_start_address = TURKS_SMC_INT_VECTOR_START;
5547ccd5a2cSjsg 		int_vect_size = TURKS_SMC_INT_VECTOR_SIZE;
5557ccd5a2cSjsg 		break;
5567ccd5a2cSjsg 	case CHIP_CAICOS:
5577ccd5a2cSjsg 		ucode_start_address = CAICOS_SMC_UCODE_START;
5587ccd5a2cSjsg 		ucode_size = CAICOS_SMC_UCODE_SIZE;
5597ccd5a2cSjsg 		int_vect = (const u8 *)&caicos_smc_int_vectors;
5607ccd5a2cSjsg 		int_vect_start_address = CAICOS_SMC_INT_VECTOR_START;
5617ccd5a2cSjsg 		int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE;
5627ccd5a2cSjsg 		break;
5637ccd5a2cSjsg 	case CHIP_CAYMAN:
5647ccd5a2cSjsg 		ucode_start_address = CAYMAN_SMC_UCODE_START;
5657ccd5a2cSjsg 		ucode_size = CAYMAN_SMC_UCODE_SIZE;
5667ccd5a2cSjsg 		int_vect = (const u8 *)&cayman_smc_int_vectors;
5677ccd5a2cSjsg 		int_vect_start_address = CAYMAN_SMC_INT_VECTOR_START;
5687ccd5a2cSjsg 		int_vect_size = CAYMAN_SMC_INT_VECTOR_SIZE;
5697ccd5a2cSjsg 		break;
5707ccd5a2cSjsg 	default:
5717ccd5a2cSjsg 		DRM_ERROR("unknown asic in smc ucode loader\n");
5727ccd5a2cSjsg 		BUG();
5737ccd5a2cSjsg 	}
5747ccd5a2cSjsg 
5757ccd5a2cSjsg 	/* load the ucode */
5767ccd5a2cSjsg 	ucode_data = (const u8 *)rdev->smc_fw->data;
5777ccd5a2cSjsg 	ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address,
5787ccd5a2cSjsg 				      ucode_data, ucode_size, limit);
5797ccd5a2cSjsg 	if (ret)
5807ccd5a2cSjsg 		return ret;
5817ccd5a2cSjsg 
5827ccd5a2cSjsg 	/* set up the int vectors */
5837ccd5a2cSjsg 	ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address,
5847ccd5a2cSjsg 					      int_vect, int_vect_size);
5857ccd5a2cSjsg 	if (ret)
5867ccd5a2cSjsg 		return ret;
5877ccd5a2cSjsg 
5887ccd5a2cSjsg 	return 0;
5897ccd5a2cSjsg }
5907ccd5a2cSjsg 
rv770_read_smc_sram_dword(struct radeon_device * rdev,u16 smc_address,u32 * value,u16 limit)5917ccd5a2cSjsg int rv770_read_smc_sram_dword(struct radeon_device *rdev,
5927ccd5a2cSjsg 			      u16 smc_address, u32 *value, u16 limit)
5937ccd5a2cSjsg {
5947ccd5a2cSjsg 	unsigned long flags;
5957ccd5a2cSjsg 	int ret;
5967ccd5a2cSjsg 
5977ccd5a2cSjsg 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
5987ccd5a2cSjsg 	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
5997ccd5a2cSjsg 	if (ret == 0)
6007ccd5a2cSjsg 		*value = RREG32(SMC_SRAM_DATA);
6017ccd5a2cSjsg 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
6027ccd5a2cSjsg 
6037ccd5a2cSjsg 	return ret;
6047ccd5a2cSjsg }
6057ccd5a2cSjsg 
rv770_write_smc_sram_dword(struct radeon_device * rdev,u16 smc_address,u32 value,u16 limit)6067ccd5a2cSjsg int rv770_write_smc_sram_dword(struct radeon_device *rdev,
6077ccd5a2cSjsg 			       u16 smc_address, u32 value, u16 limit)
6087ccd5a2cSjsg {
6097ccd5a2cSjsg 	unsigned long flags;
6107ccd5a2cSjsg 	int ret;
6117ccd5a2cSjsg 
6127ccd5a2cSjsg 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
6137ccd5a2cSjsg 	ret = rv770_set_smc_sram_address(rdev, smc_address, limit);
6147ccd5a2cSjsg 	if (ret == 0)
6157ccd5a2cSjsg 		WREG32(SMC_SRAM_DATA, value);
6167ccd5a2cSjsg 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
6177ccd5a2cSjsg 
6187ccd5a2cSjsg 	return ret;
6197ccd5a2cSjsg }
620