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