14cd92098Szrj /*
24cd92098Szrj * Copyright 2011 Advanced Micro Devices, Inc.
34cd92098Szrj *
44cd92098Szrj * Permission is hereby granted, free of charge, to any person obtaining a
54cd92098Szrj * copy of this software and associated documentation files (the "Software"),
64cd92098Szrj * to deal in the Software without restriction, including without limitation
74cd92098Szrj * the rights to use, copy, modify, merge, publish, distribute, sublicense,
84cd92098Szrj * and/or sell copies of the Software, and to permit persons to whom the
94cd92098Szrj * Software is furnished to do so, subject to the following conditions:
104cd92098Szrj *
114cd92098Szrj * The above copyright notice and this permission notice shall be included in
124cd92098Szrj * all copies or substantial portions of the Software.
134cd92098Szrj *
144cd92098Szrj * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
154cd92098Szrj * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
164cd92098Szrj * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
174cd92098Szrj * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
184cd92098Szrj * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
194cd92098Szrj * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
204cd92098Szrj * OTHER DEALINGS IN THE SOFTWARE.
214cd92098Szrj *
224cd92098Szrj * Authors: Alex Deucher
234cd92098Szrj */
244cd92098Szrj
254cd92098Szrj #include <linux/firmware.h>
26*3f2dd94aSFrançois Tigeot #include <drm/drmP.h>
274cd92098Szrj #include "radeon.h"
284cd92098Szrj #include "cikd.h"
294cd92098Szrj #include "ppsmc.h"
304cd92098Szrj #include "radeon_ucode.h"
31c6f73aabSFrançois Tigeot #include "ci_dpm.h"
324cd92098Szrj
ci_set_smc_sram_address(struct radeon_device * rdev,u32 smc_address,u32 limit)334cd92098Szrj static int ci_set_smc_sram_address(struct radeon_device *rdev,
344cd92098Szrj u32 smc_address, u32 limit)
354cd92098Szrj {
364cd92098Szrj if (smc_address & 3)
374cd92098Szrj return -EINVAL;
384cd92098Szrj if ((smc_address + 3) > limit)
394cd92098Szrj return -EINVAL;
404cd92098Szrj
414cd92098Szrj WREG32(SMC_IND_INDEX_0, smc_address);
424cd92098Szrj WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
434cd92098Szrj
444cd92098Szrj return 0;
454cd92098Szrj }
464cd92098Szrj
ci_copy_bytes_to_smc(struct radeon_device * rdev,u32 smc_start_address,const u8 * src,u32 byte_count,u32 limit)474cd92098Szrj int ci_copy_bytes_to_smc(struct radeon_device *rdev,
484cd92098Szrj u32 smc_start_address,
494cd92098Szrj const u8 *src, u32 byte_count, u32 limit)
504cd92098Szrj {
51a85cb24fSFrançois Tigeot unsigned long flags;
524cd92098Szrj u32 data, original_data;
534cd92098Szrj u32 addr;
544cd92098Szrj u32 extra_shift;
55c6f73aabSFrançois Tigeot int ret = 0;
564cd92098Szrj
574cd92098Szrj if (smc_start_address & 3)
584cd92098Szrj return -EINVAL;
594cd92098Szrj if ((smc_start_address + byte_count) > limit)
604cd92098Szrj return -EINVAL;
614cd92098Szrj
624cd92098Szrj addr = smc_start_address;
634cd92098Szrj
64a85cb24fSFrançois Tigeot spin_lock_irqsave(&rdev->smc_idx_lock, flags);
654cd92098Szrj while (byte_count >= 4) {
664cd92098Szrj /* SMC address space is BE */
674cd92098Szrj data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
684cd92098Szrj
694cd92098Szrj ret = ci_set_smc_sram_address(rdev, addr, limit);
704cd92098Szrj if (ret)
71c6f73aabSFrançois Tigeot goto done;
724cd92098Szrj
734cd92098Szrj WREG32(SMC_IND_DATA_0, data);
744cd92098Szrj
754cd92098Szrj src += 4;
764cd92098Szrj byte_count -= 4;
774cd92098Szrj addr += 4;
784cd92098Szrj }
794cd92098Szrj
804cd92098Szrj /* RMW for the final bytes */
814cd92098Szrj if (byte_count > 0) {
824cd92098Szrj data = 0;
834cd92098Szrj
844cd92098Szrj ret = ci_set_smc_sram_address(rdev, addr, limit);
854cd92098Szrj if (ret)
86c6f73aabSFrançois Tigeot goto done;
874cd92098Szrj
884cd92098Szrj original_data = RREG32(SMC_IND_DATA_0);
894cd92098Szrj
904cd92098Szrj extra_shift = 8 * (4 - byte_count);
914cd92098Szrj
924cd92098Szrj while (byte_count > 0) {
934cd92098Szrj data = (data << 8) + *src++;
944cd92098Szrj byte_count--;
954cd92098Szrj }
964cd92098Szrj
974cd92098Szrj data <<= extra_shift;
984cd92098Szrj
994cd92098Szrj data |= (original_data & ~((~0UL) << extra_shift));
1004cd92098Szrj
1014cd92098Szrj ret = ci_set_smc_sram_address(rdev, addr, limit);
1024cd92098Szrj if (ret)
103c6f73aabSFrançois Tigeot goto done;
1044cd92098Szrj
1054cd92098Szrj WREG32(SMC_IND_DATA_0, data);
1064cd92098Szrj }
107c6f73aabSFrançois Tigeot
108c6f73aabSFrançois Tigeot done:
109a85cb24fSFrançois Tigeot spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
110c6f73aabSFrançois Tigeot
111c6f73aabSFrançois Tigeot return ret;
1124cd92098Szrj }
1134cd92098Szrj
ci_start_smc(struct radeon_device * rdev)1144cd92098Szrj void ci_start_smc(struct radeon_device *rdev)
1154cd92098Szrj {
1164cd92098Szrj u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
1174cd92098Szrj
1184cd92098Szrj tmp &= ~RST_REG;
1194cd92098Szrj WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
1204cd92098Szrj }
1214cd92098Szrj
ci_reset_smc(struct radeon_device * rdev)1224cd92098Szrj void ci_reset_smc(struct radeon_device *rdev)
1234cd92098Szrj {
1244cd92098Szrj u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
1254cd92098Szrj
1264cd92098Szrj tmp |= RST_REG;
1274cd92098Szrj WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
1284cd92098Szrj }
1294cd92098Szrj
ci_program_jump_on_start(struct radeon_device * rdev)1304cd92098Szrj int ci_program_jump_on_start(struct radeon_device *rdev)
1314cd92098Szrj {
1327dcf36dcSFrançois Tigeot static const u8 data[] = { 0xE0, 0x00, 0x80, 0x40 };
1334cd92098Szrj
1344cd92098Szrj return ci_copy_bytes_to_smc(rdev, 0x0, data, 4, sizeof(data)+1);
1354cd92098Szrj }
1364cd92098Szrj
ci_stop_smc_clock(struct radeon_device * rdev)1374cd92098Szrj void ci_stop_smc_clock(struct radeon_device *rdev)
1384cd92098Szrj {
1394cd92098Szrj u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
1404cd92098Szrj
1414cd92098Szrj tmp |= CK_DISABLE;
1424cd92098Szrj
1434cd92098Szrj WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
1444cd92098Szrj }
1454cd92098Szrj
ci_start_smc_clock(struct radeon_device * rdev)1464cd92098Szrj void ci_start_smc_clock(struct radeon_device *rdev)
1474cd92098Szrj {
1484cd92098Szrj u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
1494cd92098Szrj
1504cd92098Szrj tmp &= ~CK_DISABLE;
1514cd92098Szrj
1524cd92098Szrj WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
1534cd92098Szrj }
1544cd92098Szrj
ci_is_smc_running(struct radeon_device * rdev)1554cd92098Szrj bool ci_is_smc_running(struct radeon_device *rdev)
1564cd92098Szrj {
1574cd92098Szrj u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
1584cd92098Szrj u32 pc_c = RREG32_SMC(SMC_PC_C);
1594cd92098Szrj
1604cd92098Szrj if (!(clk & CK_DISABLE) && (0x20100 <= pc_c))
1614cd92098Szrj return true;
1624cd92098Szrj
1634cd92098Szrj return false;
1644cd92098Szrj }
1654cd92098Szrj
166b6771645Szrj #if 0
1674cd92098Szrj PPSMC_Result ci_wait_for_smc_inactive(struct radeon_device *rdev)
1684cd92098Szrj {
1694cd92098Szrj u32 tmp;
1704cd92098Szrj int i;
1714cd92098Szrj
1724cd92098Szrj if (!ci_is_smc_running(rdev))
1734cd92098Szrj return PPSMC_Result_OK;
1744cd92098Szrj
1754cd92098Szrj for (i = 0; i < rdev->usec_timeout; i++) {
1764cd92098Szrj tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
1774cd92098Szrj if ((tmp & CKEN) == 0)
1784cd92098Szrj break;
1794cd92098Szrj udelay(1);
1804cd92098Szrj }
1814cd92098Szrj
1824cd92098Szrj return PPSMC_Result_OK;
1834cd92098Szrj }
184b6771645Szrj #endif
1854cd92098Szrj
ci_load_smc_ucode(struct radeon_device * rdev,u32 limit)1864cd92098Szrj int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
1874cd92098Szrj {
188a85cb24fSFrançois Tigeot unsigned long flags;
1894cd92098Szrj u32 ucode_start_address;
1904cd92098Szrj u32 ucode_size;
1914cd92098Szrj const u8 *src;
1924cd92098Szrj u32 data;
1934cd92098Szrj
1944cd92098Szrj if (!rdev->smc_fw)
1954cd92098Szrj return -EINVAL;
1964cd92098Szrj
197cb754608SImre Vadász if (rdev->new_fw) {
198cb754608SImre Vadász const struct smc_firmware_header_v1_0 *hdr =
199cb754608SImre Vadász (const struct smc_firmware_header_v1_0 *)rdev->smc_fw->data;
200cb754608SImre Vadász
201cb754608SImre Vadász radeon_ucode_print_smc_hdr(&hdr->header);
202cb754608SImre Vadász
203cb754608SImre Vadász ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
204cb754608SImre Vadász ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
205cb754608SImre Vadász src = (const u8 *)
206a85cb24fSFrançois Tigeot (rdev->smc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
207cb754608SImre Vadász } else {
2084cd92098Szrj switch (rdev->family) {
2094cd92098Szrj case CHIP_BONAIRE:
2104cd92098Szrj ucode_start_address = BONAIRE_SMC_UCODE_START;
2114cd92098Szrj ucode_size = BONAIRE_SMC_UCODE_SIZE;
2124cd92098Szrj break;
213c6f73aabSFrançois Tigeot case CHIP_HAWAII:
214c6f73aabSFrançois Tigeot ucode_start_address = HAWAII_SMC_UCODE_START;
215c6f73aabSFrançois Tigeot ucode_size = HAWAII_SMC_UCODE_SIZE;
216c6f73aabSFrançois Tigeot break;
2174cd92098Szrj default:
2184cd92098Szrj DRM_ERROR("unknown asic in smc ucode loader\n");
2194cd92098Szrj BUG();
2204cd92098Szrj }
2214cd92098Szrj
222cb754608SImre Vadász src = (const u8 *)rdev->smc_fw->data;
223cb754608SImre Vadász }
224cb754608SImre Vadász
2254cd92098Szrj if (ucode_size & 3)
2264cd92098Szrj return -EINVAL;
2274cd92098Szrj
228a85cb24fSFrançois Tigeot spin_lock_irqsave(&rdev->smc_idx_lock, flags);
2294cd92098Szrj WREG32(SMC_IND_INDEX_0, ucode_start_address);
2304cd92098Szrj WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
2314cd92098Szrj while (ucode_size >= 4) {
2324cd92098Szrj /* SMC address space is BE */
2334cd92098Szrj data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
2344cd92098Szrj
2354cd92098Szrj WREG32(SMC_IND_DATA_0, data);
2364cd92098Szrj
2374cd92098Szrj src += 4;
2384cd92098Szrj ucode_size -= 4;
2394cd92098Szrj }
2404cd92098Szrj WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
241a85cb24fSFrançois Tigeot spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
2424cd92098Szrj
2434cd92098Szrj return 0;
2444cd92098Szrj }
2454cd92098Szrj
ci_read_smc_sram_dword(struct radeon_device * rdev,u32 smc_address,u32 * value,u32 limit)2464cd92098Szrj int ci_read_smc_sram_dword(struct radeon_device *rdev,
2474cd92098Szrj u32 smc_address, u32 *value, u32 limit)
2484cd92098Szrj {
249a85cb24fSFrançois Tigeot unsigned long flags;
2504cd92098Szrj int ret;
2514cd92098Szrj
252a85cb24fSFrançois Tigeot spin_lock_irqsave(&rdev->smc_idx_lock, flags);
2534cd92098Szrj ret = ci_set_smc_sram_address(rdev, smc_address, limit);
254c6f73aabSFrançois Tigeot if (ret == 0)
2554cd92098Szrj *value = RREG32(SMC_IND_DATA_0);
256a85cb24fSFrançois Tigeot spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
257c6f73aabSFrançois Tigeot
258c6f73aabSFrançois Tigeot return ret;
2594cd92098Szrj }
2604cd92098Szrj
ci_write_smc_sram_dword(struct radeon_device * rdev,u32 smc_address,u32 value,u32 limit)2614cd92098Szrj int ci_write_smc_sram_dword(struct radeon_device *rdev,
2624cd92098Szrj u32 smc_address, u32 value, u32 limit)
2634cd92098Szrj {
264a85cb24fSFrançois Tigeot unsigned long flags;
2654cd92098Szrj int ret;
2664cd92098Szrj
267a85cb24fSFrançois Tigeot spin_lock_irqsave(&rdev->smc_idx_lock, flags);
2684cd92098Szrj ret = ci_set_smc_sram_address(rdev, smc_address, limit);
269c6f73aabSFrançois Tigeot if (ret == 0)
2704cd92098Szrj WREG32(SMC_IND_DATA_0, value);
271a85cb24fSFrançois Tigeot spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
272c6f73aabSFrançois Tigeot
273c6f73aabSFrançois Tigeot return ret;
2744cd92098Szrj }
275