166229b20SAlex Deucher /* 266229b20SAlex Deucher * Copyright 2011 Advanced Micro Devices, Inc. 366229b20SAlex Deucher * 466229b20SAlex Deucher * Permission is hereby granted, free of charge, to any person obtaining a 566229b20SAlex Deucher * copy of this software and associated documentation files (the "Software"), 666229b20SAlex Deucher * to deal in the Software without restriction, including without limitation 766229b20SAlex Deucher * the rights to use, copy, modify, merge, publish, distribute, sublicense, 866229b20SAlex Deucher * and/or sell copies of the Software, and to permit persons to whom the 966229b20SAlex Deucher * Software is furnished to do so, subject to the following conditions: 1066229b20SAlex Deucher * 1166229b20SAlex Deucher * The above copyright notice and this permission notice shall be included in 1266229b20SAlex Deucher * all copies or substantial portions of the Software. 1366229b20SAlex Deucher * 1466229b20SAlex Deucher * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1566229b20SAlex Deucher * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1666229b20SAlex Deucher * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1766229b20SAlex Deucher * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1866229b20SAlex Deucher * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1966229b20SAlex Deucher * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2066229b20SAlex Deucher * OTHER DEALINGS IN THE SOFTWARE. 2166229b20SAlex Deucher * 2266229b20SAlex Deucher * Authors: Alex Deucher 2366229b20SAlex Deucher */ 2466229b20SAlex Deucher 2566229b20SAlex Deucher #include <linux/firmware.h> 26c182615fSSam Ravnborg 2766229b20SAlex Deucher #include "radeon.h" 2866229b20SAlex Deucher #include "rv770d.h" 2966229b20SAlex Deucher #include "rv770_dpm.h" 3066229b20SAlex Deucher #include "rv770_smc.h" 3166229b20SAlex Deucher #include "atom.h" 3266229b20SAlex Deucher #include "radeon_ucode.h" 3366229b20SAlex Deucher 3466229b20SAlex Deucher #define FIRST_SMC_INT_VECT_REG 0xFFD8 3566229b20SAlex Deucher #define FIRST_INT_VECT_S19 0xFFC0 3666229b20SAlex Deucher 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 25769e0b57aSAlex Deucher static const u8 cayman_smc_int_vectors[] = 25869e0b57aSAlex Deucher { 25969e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 26069e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 26169e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 26269e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 26369e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 26469e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 26569e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 26669e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 26769e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 26869e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 26969e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 27069e0b57aSAlex Deucher 0x12, 0x05, 0x12, 0x05, 27169e0b57aSAlex Deucher 0x12, 0x05, 0x18, 0xEA, 27269e0b57aSAlex Deucher 0x12, 0x20, 0x1C, 0x34, 27369e0b57aSAlex Deucher 0x1C, 0x34, 0x08, 0x72, 27469e0b57aSAlex Deucher 0x08, 0x72, 0x08, 0x72 27569e0b57aSAlex Deucher }; 27669e0b57aSAlex Deucher 277fe78118cSAlex Deucher static int rv770_set_smc_sram_address(struct radeon_device *rdev, 27866229b20SAlex Deucher u16 smc_address, u16 limit) 27966229b20SAlex Deucher { 28066229b20SAlex Deucher u32 addr; 28166229b20SAlex Deucher 28266229b20SAlex Deucher if (smc_address & 3) 28366229b20SAlex Deucher return -EINVAL; 28466229b20SAlex Deucher if ((smc_address + 3) > limit) 28566229b20SAlex Deucher return -EINVAL; 28666229b20SAlex Deucher 28766229b20SAlex Deucher addr = smc_address; 28866229b20SAlex Deucher addr |= SMC_SRAM_AUTO_INC_DIS; 28966229b20SAlex Deucher 29066229b20SAlex Deucher WREG32(SMC_SRAM_ADDR, addr); 29166229b20SAlex Deucher 29266229b20SAlex Deucher return 0; 29366229b20SAlex Deucher } 29466229b20SAlex Deucher 29566229b20SAlex Deucher int rv770_copy_bytes_to_smc(struct radeon_device *rdev, 29666229b20SAlex Deucher u16 smc_start_address, const u8 *src, 29766229b20SAlex Deucher u16 byte_count, u16 limit) 29866229b20SAlex Deucher { 299fe78118cSAlex Deucher unsigned long flags; 30066229b20SAlex Deucher u32 data, original_data, extra_shift; 30166229b20SAlex Deucher u16 addr; 302fe78118cSAlex Deucher int ret = 0; 30366229b20SAlex Deucher 30466229b20SAlex Deucher if (smc_start_address & 3) 30566229b20SAlex Deucher return -EINVAL; 30666229b20SAlex Deucher if ((smc_start_address + byte_count) > limit) 30766229b20SAlex Deucher return -EINVAL; 30866229b20SAlex Deucher 30966229b20SAlex Deucher addr = smc_start_address; 31066229b20SAlex Deucher 311fe78118cSAlex Deucher spin_lock_irqsave(&rdev->smc_idx_lock, flags); 31266229b20SAlex Deucher while (byte_count >= 4) { 31366229b20SAlex Deucher /* SMC address space is BE */ 31466229b20SAlex Deucher data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]; 31566229b20SAlex Deucher 31666229b20SAlex Deucher ret = rv770_set_smc_sram_address(rdev, addr, limit); 31766229b20SAlex Deucher if (ret) 318fe78118cSAlex Deucher goto done; 31966229b20SAlex Deucher 32066229b20SAlex Deucher WREG32(SMC_SRAM_DATA, data); 32166229b20SAlex Deucher 32266229b20SAlex Deucher src += 4; 32366229b20SAlex Deucher byte_count -= 4; 32466229b20SAlex Deucher addr += 4; 32566229b20SAlex Deucher } 32666229b20SAlex Deucher 32766229b20SAlex Deucher /* RMW for final bytes */ 32866229b20SAlex Deucher if (byte_count > 0) { 32966229b20SAlex Deucher data = 0; 33066229b20SAlex Deucher 33166229b20SAlex Deucher ret = rv770_set_smc_sram_address(rdev, addr, limit); 33266229b20SAlex Deucher if (ret) 333fe78118cSAlex Deucher goto done; 33466229b20SAlex Deucher 33566229b20SAlex Deucher original_data = RREG32(SMC_SRAM_DATA); 33666229b20SAlex Deucher 33766229b20SAlex Deucher extra_shift = 8 * (4 - byte_count); 33866229b20SAlex Deucher 33966229b20SAlex Deucher while (byte_count > 0) { 34066229b20SAlex Deucher /* SMC address space is BE */ 34166229b20SAlex Deucher data = (data << 8) + *src++; 34266229b20SAlex Deucher byte_count--; 34366229b20SAlex Deucher } 34466229b20SAlex Deucher 34566229b20SAlex Deucher data <<= extra_shift; 34666229b20SAlex Deucher 34766229b20SAlex Deucher data |= (original_data & ~((~0UL) << extra_shift)); 34866229b20SAlex Deucher 34966229b20SAlex Deucher ret = rv770_set_smc_sram_address(rdev, addr, limit); 35066229b20SAlex Deucher if (ret) 351fe78118cSAlex Deucher goto done; 35266229b20SAlex Deucher 35366229b20SAlex Deucher WREG32(SMC_SRAM_DATA, data); 35466229b20SAlex Deucher } 35566229b20SAlex Deucher 356fe78118cSAlex Deucher done: 357fe78118cSAlex Deucher spin_unlock_irqrestore(&rdev->smc_idx_lock, flags); 358fe78118cSAlex Deucher 359fe78118cSAlex Deucher return ret; 36066229b20SAlex Deucher } 36166229b20SAlex Deucher 36266229b20SAlex Deucher static int rv770_program_interrupt_vectors(struct radeon_device *rdev, 36366229b20SAlex Deucher u32 smc_first_vector, const u8 *src, 36466229b20SAlex Deucher u32 byte_count) 36566229b20SAlex Deucher { 36666229b20SAlex Deucher u32 tmp, i; 36766229b20SAlex Deucher 36866229b20SAlex Deucher if (byte_count % 4) 36966229b20SAlex Deucher return -EINVAL; 37066229b20SAlex Deucher 37166229b20SAlex Deucher if (smc_first_vector < FIRST_SMC_INT_VECT_REG) { 37266229b20SAlex Deucher tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector; 37366229b20SAlex Deucher 37466229b20SAlex Deucher if (tmp > byte_count) 37566229b20SAlex Deucher return 0; 37666229b20SAlex Deucher 37766229b20SAlex Deucher byte_count -= tmp; 37866229b20SAlex Deucher src += tmp; 37966229b20SAlex Deucher smc_first_vector = FIRST_SMC_INT_VECT_REG; 38066229b20SAlex Deucher } 38166229b20SAlex Deucher 38266229b20SAlex Deucher for (i = 0; i < byte_count; i += 4) { 38366229b20SAlex Deucher /* SMC address space is BE */ 38466229b20SAlex Deucher tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3]; 38566229b20SAlex Deucher 38666229b20SAlex Deucher WREG32(SMC_ISR_FFD8_FFDB + i, tmp); 38766229b20SAlex Deucher } 38866229b20SAlex Deucher 38966229b20SAlex Deucher return 0; 39066229b20SAlex Deucher } 39166229b20SAlex Deucher 39266229b20SAlex Deucher void rv770_start_smc(struct radeon_device *rdev) 39366229b20SAlex Deucher { 39466229b20SAlex Deucher WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N); 39566229b20SAlex Deucher } 39666229b20SAlex Deucher 39766229b20SAlex Deucher void rv770_reset_smc(struct radeon_device *rdev) 39866229b20SAlex Deucher { 39966229b20SAlex Deucher WREG32_P(SMC_IO, 0, ~SMC_RST_N); 40066229b20SAlex Deucher } 40166229b20SAlex Deucher 40266229b20SAlex Deucher void rv770_stop_smc_clock(struct radeon_device *rdev) 40366229b20SAlex Deucher { 40466229b20SAlex Deucher WREG32_P(SMC_IO, 0, ~SMC_CLK_EN); 40566229b20SAlex Deucher } 40666229b20SAlex Deucher 40766229b20SAlex Deucher void rv770_start_smc_clock(struct radeon_device *rdev) 40866229b20SAlex Deucher { 40966229b20SAlex Deucher WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN); 41066229b20SAlex Deucher } 41166229b20SAlex Deucher 41266229b20SAlex Deucher bool rv770_is_smc_running(struct radeon_device *rdev) 41366229b20SAlex Deucher { 41466229b20SAlex Deucher u32 tmp; 41566229b20SAlex Deucher 41666229b20SAlex Deucher tmp = RREG32(SMC_IO); 41766229b20SAlex Deucher 41866229b20SAlex Deucher if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN)) 41966229b20SAlex Deucher return true; 42066229b20SAlex Deucher else 42166229b20SAlex Deucher return false; 42266229b20SAlex Deucher } 42366229b20SAlex Deucher 42466229b20SAlex Deucher PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg) 42566229b20SAlex Deucher { 42666229b20SAlex Deucher u32 tmp; 42766229b20SAlex Deucher int i; 42866229b20SAlex Deucher PPSMC_Result result; 42966229b20SAlex Deucher 43066229b20SAlex Deucher if (!rv770_is_smc_running(rdev)) 43166229b20SAlex Deucher return PPSMC_Result_Failed; 43266229b20SAlex Deucher 43366229b20SAlex Deucher WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK); 43466229b20SAlex Deucher 43566229b20SAlex Deucher for (i = 0; i < rdev->usec_timeout; i++) { 43666229b20SAlex Deucher tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK; 43766229b20SAlex Deucher tmp >>= HOST_SMC_RESP_SHIFT; 43866229b20SAlex Deucher if (tmp != 0) 43966229b20SAlex Deucher break; 44066229b20SAlex Deucher udelay(1); 44166229b20SAlex Deucher } 44266229b20SAlex Deucher 44366229b20SAlex Deucher tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK; 44466229b20SAlex Deucher tmp >>= HOST_SMC_RESP_SHIFT; 44566229b20SAlex Deucher 44666229b20SAlex Deucher result = (PPSMC_Result)tmp; 44766229b20SAlex Deucher return result; 44866229b20SAlex Deucher } 44966229b20SAlex Deucher 45066229b20SAlex Deucher PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev) 45166229b20SAlex Deucher { 45266229b20SAlex Deucher int i; 45366229b20SAlex Deucher PPSMC_Result result = PPSMC_Result_OK; 45466229b20SAlex Deucher 45566229b20SAlex Deucher if (!rv770_is_smc_running(rdev)) 45666229b20SAlex Deucher return result; 45766229b20SAlex Deucher 45866229b20SAlex Deucher for (i = 0; i < rdev->usec_timeout; i++) { 45966229b20SAlex Deucher if (RREG32(SMC_IO) & SMC_STOP_MODE) 46066229b20SAlex Deucher break; 46166229b20SAlex Deucher udelay(1); 46266229b20SAlex Deucher } 46366229b20SAlex Deucher 46466229b20SAlex Deucher return result; 46566229b20SAlex Deucher } 46666229b20SAlex Deucher 46766229b20SAlex Deucher static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit) 46866229b20SAlex Deucher { 469fe78118cSAlex Deucher unsigned long flags; 47066229b20SAlex Deucher u16 i; 47166229b20SAlex Deucher 472fe78118cSAlex Deucher spin_lock_irqsave(&rdev->smc_idx_lock, flags); 47366229b20SAlex Deucher for (i = 0; i < limit; i += 4) { 47466229b20SAlex Deucher rv770_set_smc_sram_address(rdev, i, limit); 47566229b20SAlex Deucher WREG32(SMC_SRAM_DATA, 0); 47666229b20SAlex Deucher } 477fe78118cSAlex Deucher spin_unlock_irqrestore(&rdev->smc_idx_lock, flags); 47866229b20SAlex Deucher } 47966229b20SAlex Deucher 48066229b20SAlex Deucher int rv770_load_smc_ucode(struct radeon_device *rdev, 48166229b20SAlex Deucher u16 limit) 48266229b20SAlex Deucher { 48366229b20SAlex Deucher int ret; 48466229b20SAlex Deucher const u8 *int_vect; 48566229b20SAlex Deucher u16 int_vect_start_address; 48666229b20SAlex Deucher u16 int_vect_size; 48766229b20SAlex Deucher const u8 *ucode_data; 48866229b20SAlex Deucher u16 ucode_start_address; 48966229b20SAlex Deucher u16 ucode_size; 49066229b20SAlex Deucher 49166229b20SAlex Deucher if (!rdev->smc_fw) 49266229b20SAlex Deucher return -EINVAL; 49366229b20SAlex Deucher 49466229b20SAlex Deucher rv770_clear_smc_sram(rdev, limit); 49566229b20SAlex Deucher 49666229b20SAlex Deucher switch (rdev->family) { 49766229b20SAlex Deucher case CHIP_RV770: 49866229b20SAlex Deucher ucode_start_address = RV770_SMC_UCODE_START; 49966229b20SAlex Deucher ucode_size = RV770_SMC_UCODE_SIZE; 50066229b20SAlex Deucher int_vect = (const u8 *)&rv770_smc_int_vectors; 50166229b20SAlex Deucher int_vect_start_address = RV770_SMC_INT_VECTOR_START; 50266229b20SAlex Deucher int_vect_size = RV770_SMC_INT_VECTOR_SIZE; 50366229b20SAlex Deucher break; 50466229b20SAlex Deucher case CHIP_RV730: 50566229b20SAlex Deucher ucode_start_address = RV730_SMC_UCODE_START; 50666229b20SAlex Deucher ucode_size = RV730_SMC_UCODE_SIZE; 50766229b20SAlex Deucher int_vect = (const u8 *)&rv730_smc_int_vectors; 50866229b20SAlex Deucher int_vect_start_address = RV730_SMC_INT_VECTOR_START; 50966229b20SAlex Deucher int_vect_size = RV730_SMC_INT_VECTOR_SIZE; 51066229b20SAlex Deucher break; 51166229b20SAlex Deucher case CHIP_RV710: 51266229b20SAlex Deucher ucode_start_address = RV710_SMC_UCODE_START; 51366229b20SAlex Deucher ucode_size = RV710_SMC_UCODE_SIZE; 51466229b20SAlex Deucher int_vect = (const u8 *)&rv710_smc_int_vectors; 51566229b20SAlex Deucher int_vect_start_address = RV710_SMC_INT_VECTOR_START; 51666229b20SAlex Deucher int_vect_size = RV710_SMC_INT_VECTOR_SIZE; 51766229b20SAlex Deucher break; 51866229b20SAlex Deucher case CHIP_RV740: 51966229b20SAlex Deucher ucode_start_address = RV740_SMC_UCODE_START; 52066229b20SAlex Deucher ucode_size = RV740_SMC_UCODE_SIZE; 52166229b20SAlex Deucher int_vect = (const u8 *)&rv740_smc_int_vectors; 52266229b20SAlex Deucher int_vect_start_address = RV740_SMC_INT_VECTOR_START; 52366229b20SAlex Deucher int_vect_size = RV740_SMC_INT_VECTOR_SIZE; 52466229b20SAlex Deucher break; 525dc50ba7fSAlex Deucher case CHIP_CEDAR: 526dc50ba7fSAlex Deucher ucode_start_address = CEDAR_SMC_UCODE_START; 527dc50ba7fSAlex Deucher ucode_size = CEDAR_SMC_UCODE_SIZE; 528dc50ba7fSAlex Deucher int_vect = (const u8 *)&cedar_smc_int_vectors; 529dc50ba7fSAlex Deucher int_vect_start_address = CEDAR_SMC_INT_VECTOR_START; 530dc50ba7fSAlex Deucher int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE; 531dc50ba7fSAlex Deucher break; 532dc50ba7fSAlex Deucher case CHIP_REDWOOD: 533dc50ba7fSAlex Deucher ucode_start_address = REDWOOD_SMC_UCODE_START; 534dc50ba7fSAlex Deucher ucode_size = REDWOOD_SMC_UCODE_SIZE; 535dc50ba7fSAlex Deucher int_vect = (const u8 *)&redwood_smc_int_vectors; 536dc50ba7fSAlex Deucher int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START; 537dc50ba7fSAlex Deucher int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE; 538dc50ba7fSAlex Deucher break; 539dc50ba7fSAlex Deucher case CHIP_JUNIPER: 540dc50ba7fSAlex Deucher ucode_start_address = JUNIPER_SMC_UCODE_START; 541dc50ba7fSAlex Deucher ucode_size = JUNIPER_SMC_UCODE_SIZE; 542dc50ba7fSAlex Deucher int_vect = (const u8 *)&juniper_smc_int_vectors; 543dc50ba7fSAlex Deucher int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START; 544dc50ba7fSAlex Deucher int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE; 545dc50ba7fSAlex Deucher break; 546dc50ba7fSAlex Deucher case CHIP_CYPRESS: 547dc50ba7fSAlex Deucher case CHIP_HEMLOCK: 548dc50ba7fSAlex Deucher ucode_start_address = CYPRESS_SMC_UCODE_START; 549dc50ba7fSAlex Deucher ucode_size = CYPRESS_SMC_UCODE_SIZE; 550dc50ba7fSAlex Deucher int_vect = (const u8 *)&cypress_smc_int_vectors; 551dc50ba7fSAlex Deucher int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START; 552dc50ba7fSAlex Deucher int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE; 553dc50ba7fSAlex Deucher break; 5546596afd4SAlex Deucher case CHIP_BARTS: 5556596afd4SAlex Deucher ucode_start_address = BARTS_SMC_UCODE_START; 5566596afd4SAlex Deucher ucode_size = BARTS_SMC_UCODE_SIZE; 5576596afd4SAlex Deucher int_vect = (const u8 *)&barts_smc_int_vectors; 5586596afd4SAlex Deucher int_vect_start_address = BARTS_SMC_INT_VECTOR_START; 5596596afd4SAlex Deucher int_vect_size = BARTS_SMC_INT_VECTOR_SIZE; 5606596afd4SAlex Deucher break; 5616596afd4SAlex Deucher case CHIP_TURKS: 5626596afd4SAlex Deucher ucode_start_address = TURKS_SMC_UCODE_START; 5636596afd4SAlex Deucher ucode_size = TURKS_SMC_UCODE_SIZE; 5646596afd4SAlex Deucher int_vect = (const u8 *)&turks_smc_int_vectors; 5656596afd4SAlex Deucher int_vect_start_address = TURKS_SMC_INT_VECTOR_START; 5666596afd4SAlex Deucher int_vect_size = TURKS_SMC_INT_VECTOR_SIZE; 5676596afd4SAlex Deucher break; 5686596afd4SAlex Deucher case CHIP_CAICOS: 5696596afd4SAlex Deucher ucode_start_address = CAICOS_SMC_UCODE_START; 5706596afd4SAlex Deucher ucode_size = CAICOS_SMC_UCODE_SIZE; 5716596afd4SAlex Deucher int_vect = (const u8 *)&caicos_smc_int_vectors; 5726596afd4SAlex Deucher int_vect_start_address = CAICOS_SMC_INT_VECTOR_START; 5736596afd4SAlex Deucher int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE; 5746596afd4SAlex Deucher break; 57569e0b57aSAlex Deucher case CHIP_CAYMAN: 57669e0b57aSAlex Deucher ucode_start_address = CAYMAN_SMC_UCODE_START; 57769e0b57aSAlex Deucher ucode_size = CAYMAN_SMC_UCODE_SIZE; 57869e0b57aSAlex Deucher int_vect = (const u8 *)&cayman_smc_int_vectors; 57969e0b57aSAlex Deucher int_vect_start_address = CAYMAN_SMC_INT_VECTOR_START; 58069e0b57aSAlex Deucher int_vect_size = CAYMAN_SMC_INT_VECTOR_SIZE; 58169e0b57aSAlex Deucher break; 58266229b20SAlex Deucher default: 58366229b20SAlex Deucher DRM_ERROR("unknown asic in smc ucode loader\n"); 58466229b20SAlex Deucher BUG(); 58566229b20SAlex Deucher } 58666229b20SAlex Deucher 58766229b20SAlex Deucher /* load the ucode */ 58866229b20SAlex Deucher ucode_data = (const u8 *)rdev->smc_fw->data; 58966229b20SAlex Deucher ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address, 59066229b20SAlex Deucher ucode_data, ucode_size, limit); 59166229b20SAlex Deucher if (ret) 59266229b20SAlex Deucher return ret; 59366229b20SAlex Deucher 59466229b20SAlex Deucher /* set up the int vectors */ 59566229b20SAlex Deucher ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address, 59666229b20SAlex Deucher int_vect, int_vect_size); 59766229b20SAlex Deucher if (ret) 59866229b20SAlex Deucher return ret; 59966229b20SAlex Deucher 60066229b20SAlex Deucher return 0; 60166229b20SAlex Deucher } 60266229b20SAlex Deucher 60366229b20SAlex Deucher int rv770_read_smc_sram_dword(struct radeon_device *rdev, 60466229b20SAlex Deucher u16 smc_address, u32 *value, u16 limit) 60566229b20SAlex Deucher { 606fe78118cSAlex Deucher unsigned long flags; 60766229b20SAlex Deucher int ret; 60866229b20SAlex Deucher 609fe78118cSAlex Deucher spin_lock_irqsave(&rdev->smc_idx_lock, flags); 61066229b20SAlex Deucher ret = rv770_set_smc_sram_address(rdev, smc_address, limit); 611fe78118cSAlex Deucher if (ret == 0) 61266229b20SAlex Deucher *value = RREG32(SMC_SRAM_DATA); 613fe78118cSAlex Deucher spin_unlock_irqrestore(&rdev->smc_idx_lock, flags); 61466229b20SAlex Deucher 615fe78118cSAlex Deucher return ret; 61666229b20SAlex Deucher } 61766229b20SAlex Deucher 61866229b20SAlex Deucher int rv770_write_smc_sram_dword(struct radeon_device *rdev, 61966229b20SAlex Deucher u16 smc_address, u32 value, u16 limit) 62066229b20SAlex Deucher { 621fe78118cSAlex Deucher unsigned long flags; 62266229b20SAlex Deucher int ret; 62366229b20SAlex Deucher 624fe78118cSAlex Deucher spin_lock_irqsave(&rdev->smc_idx_lock, flags); 62566229b20SAlex Deucher ret = rv770_set_smc_sram_address(rdev, smc_address, limit); 626fe78118cSAlex Deucher if (ret == 0) 62766229b20SAlex Deucher WREG32(SMC_SRAM_DATA, value); 628fe78118cSAlex Deucher spin_unlock_irqrestore(&rdev->smc_idx_lock, flags); 62966229b20SAlex Deucher 630fe78118cSAlex Deucher return ret; 63166229b20SAlex Deucher } 632