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