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