xref: /openbsd/sys/dev/pci/drm/radeon/ci_smc.c (revision c349dbc7)
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>
26*c349dbc7Sjsg 
277ccd5a2cSjsg #include "radeon.h"
287ccd5a2cSjsg #include "cikd.h"
297ccd5a2cSjsg #include "ppsmc.h"
307ccd5a2cSjsg #include "radeon_ucode.h"
317ccd5a2cSjsg #include "ci_dpm.h"
327ccd5a2cSjsg 
ci_set_smc_sram_address(struct radeon_device * rdev,u32 smc_address,u32 limit)337ccd5a2cSjsg static int ci_set_smc_sram_address(struct radeon_device *rdev,
347ccd5a2cSjsg 				   u32 smc_address, u32 limit)
357ccd5a2cSjsg {
367ccd5a2cSjsg 	if (smc_address & 3)
377ccd5a2cSjsg 		return -EINVAL;
387ccd5a2cSjsg 	if ((smc_address + 3) > limit)
397ccd5a2cSjsg 		return -EINVAL;
407ccd5a2cSjsg 
417ccd5a2cSjsg 	WREG32(SMC_IND_INDEX_0, smc_address);
427ccd5a2cSjsg 	WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
437ccd5a2cSjsg 
447ccd5a2cSjsg 	return 0;
457ccd5a2cSjsg }
467ccd5a2cSjsg 
ci_copy_bytes_to_smc(struct radeon_device * rdev,u32 smc_start_address,const u8 * src,u32 byte_count,u32 limit)477ccd5a2cSjsg int ci_copy_bytes_to_smc(struct radeon_device *rdev,
487ccd5a2cSjsg 			 u32 smc_start_address,
497ccd5a2cSjsg 			 const u8 *src, u32 byte_count, u32 limit)
507ccd5a2cSjsg {
517ccd5a2cSjsg 	unsigned long flags;
527ccd5a2cSjsg 	u32 data, original_data;
537ccd5a2cSjsg 	u32 addr;
547ccd5a2cSjsg 	u32 extra_shift;
557ccd5a2cSjsg 	int ret = 0;
567ccd5a2cSjsg 
577ccd5a2cSjsg 	if (smc_start_address & 3)
587ccd5a2cSjsg 		return -EINVAL;
597ccd5a2cSjsg 	if ((smc_start_address + byte_count) > limit)
607ccd5a2cSjsg 		return -EINVAL;
617ccd5a2cSjsg 
627ccd5a2cSjsg 	addr = smc_start_address;
637ccd5a2cSjsg 
647ccd5a2cSjsg 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
657ccd5a2cSjsg 	while (byte_count >= 4) {
667ccd5a2cSjsg 		/* SMC address space is BE */
677ccd5a2cSjsg 		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
687ccd5a2cSjsg 
697ccd5a2cSjsg 		ret = ci_set_smc_sram_address(rdev, addr, limit);
707ccd5a2cSjsg 		if (ret)
717ccd5a2cSjsg 			goto done;
727ccd5a2cSjsg 
737ccd5a2cSjsg 		WREG32(SMC_IND_DATA_0, data);
747ccd5a2cSjsg 
757ccd5a2cSjsg 		src += 4;
767ccd5a2cSjsg 		byte_count -= 4;
777ccd5a2cSjsg 		addr += 4;
787ccd5a2cSjsg 	}
797ccd5a2cSjsg 
807ccd5a2cSjsg 	/* RMW for the final bytes */
817ccd5a2cSjsg 	if (byte_count > 0) {
827ccd5a2cSjsg 		data = 0;
837ccd5a2cSjsg 
847ccd5a2cSjsg 		ret = ci_set_smc_sram_address(rdev, addr, limit);
857ccd5a2cSjsg 		if (ret)
867ccd5a2cSjsg 			goto done;
877ccd5a2cSjsg 
887ccd5a2cSjsg 		original_data = RREG32(SMC_IND_DATA_0);
897ccd5a2cSjsg 
907ccd5a2cSjsg 		extra_shift = 8 * (4 - byte_count);
917ccd5a2cSjsg 
927ccd5a2cSjsg 		while (byte_count > 0) {
937ccd5a2cSjsg 			data = (data << 8) + *src++;
947ccd5a2cSjsg 			byte_count--;
957ccd5a2cSjsg 		}
967ccd5a2cSjsg 
977ccd5a2cSjsg 		data <<= extra_shift;
987ccd5a2cSjsg 
997ccd5a2cSjsg 		data |= (original_data & ~((~0UL) << extra_shift));
1007ccd5a2cSjsg 
1017ccd5a2cSjsg 		ret = ci_set_smc_sram_address(rdev, addr, limit);
1027ccd5a2cSjsg 		if (ret)
1037ccd5a2cSjsg 			goto done;
1047ccd5a2cSjsg 
1057ccd5a2cSjsg 		WREG32(SMC_IND_DATA_0, data);
1067ccd5a2cSjsg 	}
1077ccd5a2cSjsg 
1087ccd5a2cSjsg done:
1097ccd5a2cSjsg 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
1107ccd5a2cSjsg 
1117ccd5a2cSjsg 	return ret;
1127ccd5a2cSjsg }
1137ccd5a2cSjsg 
ci_start_smc(struct radeon_device * rdev)1147ccd5a2cSjsg void ci_start_smc(struct radeon_device *rdev)
1157ccd5a2cSjsg {
1167ccd5a2cSjsg 	u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
1177ccd5a2cSjsg 
1187ccd5a2cSjsg 	tmp &= ~RST_REG;
1197ccd5a2cSjsg 	WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
1207ccd5a2cSjsg }
1217ccd5a2cSjsg 
ci_reset_smc(struct radeon_device * rdev)1227ccd5a2cSjsg void ci_reset_smc(struct radeon_device *rdev)
1237ccd5a2cSjsg {
1247ccd5a2cSjsg 	u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
1257ccd5a2cSjsg 
1267ccd5a2cSjsg 	tmp |= RST_REG;
1277ccd5a2cSjsg 	WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
1287ccd5a2cSjsg }
1297ccd5a2cSjsg 
ci_program_jump_on_start(struct radeon_device * rdev)1307ccd5a2cSjsg int ci_program_jump_on_start(struct radeon_device *rdev)
1317ccd5a2cSjsg {
1327ccd5a2cSjsg 	static const u8 data[] = { 0xE0, 0x00, 0x80, 0x40 };
1337ccd5a2cSjsg 
1347ccd5a2cSjsg 	return ci_copy_bytes_to_smc(rdev, 0x0, data, 4, sizeof(data)+1);
1357ccd5a2cSjsg }
1367ccd5a2cSjsg 
ci_stop_smc_clock(struct radeon_device * rdev)1377ccd5a2cSjsg void ci_stop_smc_clock(struct radeon_device *rdev)
1387ccd5a2cSjsg {
1397ccd5a2cSjsg 	u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
1407ccd5a2cSjsg 
1417ccd5a2cSjsg 	tmp |= CK_DISABLE;
1427ccd5a2cSjsg 
1437ccd5a2cSjsg 	WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
1447ccd5a2cSjsg }
1457ccd5a2cSjsg 
ci_start_smc_clock(struct radeon_device * rdev)1467ccd5a2cSjsg void ci_start_smc_clock(struct radeon_device *rdev)
1477ccd5a2cSjsg {
1487ccd5a2cSjsg 	u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
1497ccd5a2cSjsg 
1507ccd5a2cSjsg 	tmp &= ~CK_DISABLE;
1517ccd5a2cSjsg 
1527ccd5a2cSjsg 	WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
1537ccd5a2cSjsg }
1547ccd5a2cSjsg 
ci_is_smc_running(struct radeon_device * rdev)1557ccd5a2cSjsg bool ci_is_smc_running(struct radeon_device *rdev)
1567ccd5a2cSjsg {
1577ccd5a2cSjsg 	u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
1587ccd5a2cSjsg 	u32 pc_c = RREG32_SMC(SMC_PC_C);
1597ccd5a2cSjsg 
1607ccd5a2cSjsg 	if (!(clk & CK_DISABLE) && (0x20100 <= pc_c))
1617ccd5a2cSjsg 		return true;
1627ccd5a2cSjsg 
1637ccd5a2cSjsg 	return false;
1647ccd5a2cSjsg }
1657ccd5a2cSjsg 
1667ccd5a2cSjsg #if 0
1677ccd5a2cSjsg PPSMC_Result ci_wait_for_smc_inactive(struct radeon_device *rdev)
1687ccd5a2cSjsg {
1697ccd5a2cSjsg 	u32 tmp;
1707ccd5a2cSjsg 	int i;
1717ccd5a2cSjsg 
1727ccd5a2cSjsg 	if (!ci_is_smc_running(rdev))
1737ccd5a2cSjsg 		return PPSMC_Result_OK;
1747ccd5a2cSjsg 
1757ccd5a2cSjsg 	for (i = 0; i < rdev->usec_timeout; i++) {
1767ccd5a2cSjsg 		tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
1777ccd5a2cSjsg 		if ((tmp & CKEN) == 0)
1787ccd5a2cSjsg 			break;
1797ccd5a2cSjsg 		udelay(1);
1807ccd5a2cSjsg 	}
1817ccd5a2cSjsg 
1827ccd5a2cSjsg 	return PPSMC_Result_OK;
1837ccd5a2cSjsg }
1847ccd5a2cSjsg #endif
1857ccd5a2cSjsg 
ci_load_smc_ucode(struct radeon_device * rdev,u32 limit)1867ccd5a2cSjsg int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
1877ccd5a2cSjsg {
1887ccd5a2cSjsg 	unsigned long flags;
1897ccd5a2cSjsg 	u32 ucode_start_address;
1907ccd5a2cSjsg 	u32 ucode_size;
1917ccd5a2cSjsg 	const u8 *src;
1927ccd5a2cSjsg 	u32 data;
1937ccd5a2cSjsg 
1947ccd5a2cSjsg 	if (!rdev->smc_fw)
1957ccd5a2cSjsg 		return -EINVAL;
1967ccd5a2cSjsg 
1977ccd5a2cSjsg 	if (rdev->new_fw) {
1987ccd5a2cSjsg 		const struct smc_firmware_header_v1_0 *hdr =
1997ccd5a2cSjsg 			(const struct smc_firmware_header_v1_0 *)rdev->smc_fw->data;
2007ccd5a2cSjsg 
2017ccd5a2cSjsg 		radeon_ucode_print_smc_hdr(&hdr->header);
2027ccd5a2cSjsg 
2037ccd5a2cSjsg 		ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
2047ccd5a2cSjsg 		ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
2057ccd5a2cSjsg 		src = (const u8 *)
2067ccd5a2cSjsg 			(rdev->smc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
2077ccd5a2cSjsg 	} else {
2087ccd5a2cSjsg 		switch (rdev->family) {
2097ccd5a2cSjsg 		case CHIP_BONAIRE:
2107ccd5a2cSjsg 			ucode_start_address = BONAIRE_SMC_UCODE_START;
2117ccd5a2cSjsg 			ucode_size = BONAIRE_SMC_UCODE_SIZE;
2127ccd5a2cSjsg 			break;
2137ccd5a2cSjsg 		case CHIP_HAWAII:
2147ccd5a2cSjsg 			ucode_start_address = HAWAII_SMC_UCODE_START;
2157ccd5a2cSjsg 			ucode_size = HAWAII_SMC_UCODE_SIZE;
2167ccd5a2cSjsg 			break;
2177ccd5a2cSjsg 		default:
2187ccd5a2cSjsg 			DRM_ERROR("unknown asic in smc ucode loader\n");
2197ccd5a2cSjsg 			BUG();
2207ccd5a2cSjsg 		}
2217ccd5a2cSjsg 
2227ccd5a2cSjsg 		src = (const u8 *)rdev->smc_fw->data;
2237ccd5a2cSjsg 	}
2247ccd5a2cSjsg 
2257ccd5a2cSjsg 	if (ucode_size & 3)
2267ccd5a2cSjsg 		return -EINVAL;
2277ccd5a2cSjsg 
2287ccd5a2cSjsg 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
2297ccd5a2cSjsg 	WREG32(SMC_IND_INDEX_0, ucode_start_address);
2307ccd5a2cSjsg 	WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
2317ccd5a2cSjsg 	while (ucode_size >= 4) {
2327ccd5a2cSjsg 		/* SMC address space is BE */
2337ccd5a2cSjsg 		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
2347ccd5a2cSjsg 
2357ccd5a2cSjsg 		WREG32(SMC_IND_DATA_0, data);
2367ccd5a2cSjsg 
2377ccd5a2cSjsg 		src += 4;
2387ccd5a2cSjsg 		ucode_size -= 4;
2397ccd5a2cSjsg 	}
2407ccd5a2cSjsg 	WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
2417ccd5a2cSjsg 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
2427ccd5a2cSjsg 
2437ccd5a2cSjsg 	return 0;
2447ccd5a2cSjsg }
2457ccd5a2cSjsg 
ci_read_smc_sram_dword(struct radeon_device * rdev,u32 smc_address,u32 * value,u32 limit)2467ccd5a2cSjsg int ci_read_smc_sram_dword(struct radeon_device *rdev,
2477ccd5a2cSjsg 			   u32 smc_address, u32 *value, u32 limit)
2487ccd5a2cSjsg {
2497ccd5a2cSjsg 	unsigned long flags;
2507ccd5a2cSjsg 	int ret;
2517ccd5a2cSjsg 
2527ccd5a2cSjsg 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
2537ccd5a2cSjsg 	ret = ci_set_smc_sram_address(rdev, smc_address, limit);
2547ccd5a2cSjsg 	if (ret == 0)
2557ccd5a2cSjsg 		*value = RREG32(SMC_IND_DATA_0);
2567ccd5a2cSjsg 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
2577ccd5a2cSjsg 
2587ccd5a2cSjsg 	return ret;
2597ccd5a2cSjsg }
2607ccd5a2cSjsg 
ci_write_smc_sram_dword(struct radeon_device * rdev,u32 smc_address,u32 value,u32 limit)2617ccd5a2cSjsg int ci_write_smc_sram_dword(struct radeon_device *rdev,
2627ccd5a2cSjsg 			    u32 smc_address, u32 value, u32 limit)
2637ccd5a2cSjsg {
2647ccd5a2cSjsg 	unsigned long flags;
2657ccd5a2cSjsg 	int ret;
2667ccd5a2cSjsg 
2677ccd5a2cSjsg 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
2687ccd5a2cSjsg 	ret = ci_set_smc_sram_address(rdev, smc_address, limit);
2697ccd5a2cSjsg 	if (ret == 0)
2707ccd5a2cSjsg 		WREG32(SMC_IND_DATA_0, value);
2717ccd5a2cSjsg 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
2727ccd5a2cSjsg 
2737ccd5a2cSjsg 	return ret;
2747ccd5a2cSjsg }
275