xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/radeon/ci_smc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright 2011 Advanced Micro Devices, Inc.
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Permission is hereby granted, free of charge, to any person obtaining a
5*4882a593Smuzhiyun  * copy of this software and associated documentation files (the "Software"),
6*4882a593Smuzhiyun  * to deal in the Software without restriction, including without limitation
7*4882a593Smuzhiyun  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*4882a593Smuzhiyun  * and/or sell copies of the Software, and to permit persons to whom the
9*4882a593Smuzhiyun  * Software is furnished to do so, subject to the following conditions:
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * The above copyright notice and this permission notice shall be included in
12*4882a593Smuzhiyun  * all copies or substantial portions of the Software.
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15*4882a593Smuzhiyun  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16*4882a593Smuzhiyun  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17*4882a593Smuzhiyun  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18*4882a593Smuzhiyun  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19*4882a593Smuzhiyun  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20*4882a593Smuzhiyun  * OTHER DEALINGS IN THE SOFTWARE.
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  * Authors: Alex Deucher
23*4882a593Smuzhiyun  */
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #include <linux/firmware.h>
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #include "radeon.h"
28*4882a593Smuzhiyun #include "cikd.h"
29*4882a593Smuzhiyun #include "ppsmc.h"
30*4882a593Smuzhiyun #include "radeon_ucode.h"
31*4882a593Smuzhiyun #include "ci_dpm.h"
32*4882a593Smuzhiyun 
ci_set_smc_sram_address(struct radeon_device * rdev,u32 smc_address,u32 limit)33*4882a593Smuzhiyun static int ci_set_smc_sram_address(struct radeon_device *rdev,
34*4882a593Smuzhiyun 				   u32 smc_address, u32 limit)
35*4882a593Smuzhiyun {
36*4882a593Smuzhiyun 	if (smc_address & 3)
37*4882a593Smuzhiyun 		return -EINVAL;
38*4882a593Smuzhiyun 	if ((smc_address + 3) > limit)
39*4882a593Smuzhiyun 		return -EINVAL;
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	WREG32(SMC_IND_INDEX_0, smc_address);
42*4882a593Smuzhiyun 	WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	return 0;
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun 
ci_copy_bytes_to_smc(struct radeon_device * rdev,u32 smc_start_address,const u8 * src,u32 byte_count,u32 limit)47*4882a593Smuzhiyun int ci_copy_bytes_to_smc(struct radeon_device *rdev,
48*4882a593Smuzhiyun 			 u32 smc_start_address,
49*4882a593Smuzhiyun 			 const u8 *src, u32 byte_count, u32 limit)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun 	unsigned long flags;
52*4882a593Smuzhiyun 	u32 data, original_data;
53*4882a593Smuzhiyun 	u32 addr;
54*4882a593Smuzhiyun 	u32 extra_shift;
55*4882a593Smuzhiyun 	int ret = 0;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	if (smc_start_address & 3)
58*4882a593Smuzhiyun 		return -EINVAL;
59*4882a593Smuzhiyun 	if ((smc_start_address + byte_count) > limit)
60*4882a593Smuzhiyun 		return -EINVAL;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	addr = smc_start_address;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
65*4882a593Smuzhiyun 	while (byte_count >= 4) {
66*4882a593Smuzhiyun 		/* SMC address space is BE */
67*4882a593Smuzhiyun 		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 		ret = ci_set_smc_sram_address(rdev, addr, limit);
70*4882a593Smuzhiyun 		if (ret)
71*4882a593Smuzhiyun 			goto done;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 		WREG32(SMC_IND_DATA_0, data);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 		src += 4;
76*4882a593Smuzhiyun 		byte_count -= 4;
77*4882a593Smuzhiyun 		addr += 4;
78*4882a593Smuzhiyun 	}
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	/* RMW for the final bytes */
81*4882a593Smuzhiyun 	if (byte_count > 0) {
82*4882a593Smuzhiyun 		data = 0;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 		ret = ci_set_smc_sram_address(rdev, addr, limit);
85*4882a593Smuzhiyun 		if (ret)
86*4882a593Smuzhiyun 			goto done;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 		original_data = RREG32(SMC_IND_DATA_0);
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 		extra_shift = 8 * (4 - byte_count);
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 		while (byte_count > 0) {
93*4882a593Smuzhiyun 			data = (data << 8) + *src++;
94*4882a593Smuzhiyun 			byte_count--;
95*4882a593Smuzhiyun 		}
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 		data <<= extra_shift;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 		data |= (original_data & ~((~0UL) << extra_shift));
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 		ret = ci_set_smc_sram_address(rdev, addr, limit);
102*4882a593Smuzhiyun 		if (ret)
103*4882a593Smuzhiyun 			goto done;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 		WREG32(SMC_IND_DATA_0, data);
106*4882a593Smuzhiyun 	}
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun done:
109*4882a593Smuzhiyun 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	return ret;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
ci_start_smc(struct radeon_device * rdev)114*4882a593Smuzhiyun void ci_start_smc(struct radeon_device *rdev)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun 	u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	tmp &= ~RST_REG;
119*4882a593Smuzhiyun 	WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun 
ci_reset_smc(struct radeon_device * rdev)122*4882a593Smuzhiyun void ci_reset_smc(struct radeon_device *rdev)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	tmp |= RST_REG;
127*4882a593Smuzhiyun 	WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
ci_program_jump_on_start(struct radeon_device * rdev)130*4882a593Smuzhiyun int ci_program_jump_on_start(struct radeon_device *rdev)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun 	static const u8 data[] = { 0xE0, 0x00, 0x80, 0x40 };
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	return ci_copy_bytes_to_smc(rdev, 0x0, data, 4, sizeof(data)+1);
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
ci_stop_smc_clock(struct radeon_device * rdev)137*4882a593Smuzhiyun void ci_stop_smc_clock(struct radeon_device *rdev)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	tmp |= CK_DISABLE;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
ci_start_smc_clock(struct radeon_device * rdev)146*4882a593Smuzhiyun void ci_start_smc_clock(struct radeon_device *rdev)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun 	u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	tmp &= ~CK_DISABLE;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
ci_is_smc_running(struct radeon_device * rdev)155*4882a593Smuzhiyun bool ci_is_smc_running(struct radeon_device *rdev)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun 	u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
158*4882a593Smuzhiyun 	u32 pc_c = RREG32_SMC(SMC_PC_C);
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	if (!(clk & CK_DISABLE) && (0x20100 <= pc_c))
161*4882a593Smuzhiyun 		return true;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	return false;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun #if 0
167*4882a593Smuzhiyun PPSMC_Result ci_wait_for_smc_inactive(struct radeon_device *rdev)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun 	u32 tmp;
170*4882a593Smuzhiyun 	int i;
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun 	if (!ci_is_smc_running(rdev))
173*4882a593Smuzhiyun 		return PPSMC_Result_OK;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	for (i = 0; i < rdev->usec_timeout; i++) {
176*4882a593Smuzhiyun 		tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
177*4882a593Smuzhiyun 		if ((tmp & CKEN) == 0)
178*4882a593Smuzhiyun 			break;
179*4882a593Smuzhiyun 		udelay(1);
180*4882a593Smuzhiyun 	}
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	return PPSMC_Result_OK;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun #endif
185*4882a593Smuzhiyun 
ci_load_smc_ucode(struct radeon_device * rdev,u32 limit)186*4882a593Smuzhiyun int ci_load_smc_ucode(struct radeon_device *rdev, u32 limit)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun 	unsigned long flags;
189*4882a593Smuzhiyun 	u32 ucode_start_address;
190*4882a593Smuzhiyun 	u32 ucode_size;
191*4882a593Smuzhiyun 	const u8 *src;
192*4882a593Smuzhiyun 	u32 data;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	if (!rdev->smc_fw)
195*4882a593Smuzhiyun 		return -EINVAL;
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	if (rdev->new_fw) {
198*4882a593Smuzhiyun 		const struct smc_firmware_header_v1_0 *hdr =
199*4882a593Smuzhiyun 			(const struct smc_firmware_header_v1_0 *)rdev->smc_fw->data;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 		radeon_ucode_print_smc_hdr(&hdr->header);
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 		ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
204*4882a593Smuzhiyun 		ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
205*4882a593Smuzhiyun 		src = (const u8 *)
206*4882a593Smuzhiyun 			(rdev->smc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
207*4882a593Smuzhiyun 	} else {
208*4882a593Smuzhiyun 		switch (rdev->family) {
209*4882a593Smuzhiyun 		case CHIP_BONAIRE:
210*4882a593Smuzhiyun 			ucode_start_address = BONAIRE_SMC_UCODE_START;
211*4882a593Smuzhiyun 			ucode_size = BONAIRE_SMC_UCODE_SIZE;
212*4882a593Smuzhiyun 			break;
213*4882a593Smuzhiyun 		case CHIP_HAWAII:
214*4882a593Smuzhiyun 			ucode_start_address = HAWAII_SMC_UCODE_START;
215*4882a593Smuzhiyun 			ucode_size = HAWAII_SMC_UCODE_SIZE;
216*4882a593Smuzhiyun 			break;
217*4882a593Smuzhiyun 		default:
218*4882a593Smuzhiyun 			DRM_ERROR("unknown asic in smc ucode loader\n");
219*4882a593Smuzhiyun 			BUG();
220*4882a593Smuzhiyun 		}
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 		src = (const u8 *)rdev->smc_fw->data;
223*4882a593Smuzhiyun 	}
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	if (ucode_size & 3)
226*4882a593Smuzhiyun 		return -EINVAL;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
229*4882a593Smuzhiyun 	WREG32(SMC_IND_INDEX_0, ucode_start_address);
230*4882a593Smuzhiyun 	WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
231*4882a593Smuzhiyun 	while (ucode_size >= 4) {
232*4882a593Smuzhiyun 		/* SMC address space is BE */
233*4882a593Smuzhiyun 		data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 		WREG32(SMC_IND_DATA_0, data);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 		src += 4;
238*4882a593Smuzhiyun 		ucode_size -= 4;
239*4882a593Smuzhiyun 	}
240*4882a593Smuzhiyun 	WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
241*4882a593Smuzhiyun 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	return 0;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun 
ci_read_smc_sram_dword(struct radeon_device * rdev,u32 smc_address,u32 * value,u32 limit)246*4882a593Smuzhiyun int ci_read_smc_sram_dword(struct radeon_device *rdev,
247*4882a593Smuzhiyun 			   u32 smc_address, u32 *value, u32 limit)
248*4882a593Smuzhiyun {
249*4882a593Smuzhiyun 	unsigned long flags;
250*4882a593Smuzhiyun 	int ret;
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
253*4882a593Smuzhiyun 	ret = ci_set_smc_sram_address(rdev, smc_address, limit);
254*4882a593Smuzhiyun 	if (ret == 0)
255*4882a593Smuzhiyun 		*value = RREG32(SMC_IND_DATA_0);
256*4882a593Smuzhiyun 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	return ret;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun 
ci_write_smc_sram_dword(struct radeon_device * rdev,u32 smc_address,u32 value,u32 limit)261*4882a593Smuzhiyun int ci_write_smc_sram_dword(struct radeon_device *rdev,
262*4882a593Smuzhiyun 			    u32 smc_address, u32 value, u32 limit)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun 	unsigned long flags;
265*4882a593Smuzhiyun 	int ret;
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	spin_lock_irqsave(&rdev->smc_idx_lock, flags);
268*4882a593Smuzhiyun 	ret = ci_set_smc_sram_address(rdev, smc_address, limit);
269*4882a593Smuzhiyun 	if (ret == 0)
270*4882a593Smuzhiyun 		WREG32(SMC_IND_DATA_0, value);
271*4882a593Smuzhiyun 	spin_unlock_irqrestore(&rdev->smc_idx_lock, flags);
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	return ret;
274*4882a593Smuzhiyun }
275