1 | /* |
2 | * Copyright 2013 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | * Authors: Alex Deucher |
23 | */ |
24 | |
25 | #include "amdgpu.h" |
26 | #include "cikd.h" |
27 | #include "kv_dpm.h" |
28 | |
29 | #include "smu/smu_7_0_0_d.h" |
30 | #include "smu/smu_7_0_0_sh_mask.h" |
31 | |
32 | int amdgpu_kv_notify_message_to_smu(struct amdgpu_device *adev, u32 id) |
33 | { |
34 | u32 i; |
35 | u32 tmp = 0; |
36 | |
37 | WREG32(mmSMC_MESSAGE_0, id & SMC_MESSAGE_0__SMC_MSG_MASK); |
38 | |
39 | for (i = 0; i < adev->usec_timeout; i++) { |
40 | if ((RREG32(mmSMC_RESP_0) & SMC_RESP_0__SMC_RESP_MASK) != 0) |
41 | break; |
42 | udelay(1); |
43 | } |
44 | tmp = RREG32(mmSMC_RESP_0) & SMC_RESP_0__SMC_RESP_MASK; |
45 | |
46 | if (tmp != 1) { |
47 | if (tmp == 0xFF) |
48 | return -EINVAL; |
49 | else if (tmp == 0xFE) |
50 | return -EINVAL; |
51 | } |
52 | |
53 | return 0; |
54 | } |
55 | |
56 | int amdgpu_kv_dpm_get_enable_mask(struct amdgpu_device *adev, u32 *enable_mask) |
57 | { |
58 | int ret; |
59 | |
60 | ret = amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_SCLKDPM_GetEnabledMask); |
61 | |
62 | if (ret == 0) |
63 | *enable_mask = RREG32_SMC(ixSMC_SYSCON_MSG_ARG_0); |
64 | |
65 | return ret; |
66 | } |
67 | |
68 | int amdgpu_kv_send_msg_to_smc_with_parameter(struct amdgpu_device *adev, |
69 | PPSMC_Msg msg, u32 parameter) |
70 | { |
71 | |
72 | WREG32(mmSMC_MSG_ARG_0, parameter); |
73 | |
74 | return amdgpu_kv_notify_message_to_smu(adev, id: msg); |
75 | } |
76 | |
77 | static int kv_set_smc_sram_address(struct amdgpu_device *adev, |
78 | u32 smc_address, u32 limit) |
79 | { |
80 | if (smc_address & 3) |
81 | return -EINVAL; |
82 | if ((smc_address + 3) > limit) |
83 | return -EINVAL; |
84 | |
85 | WREG32(mmSMC_IND_INDEX_0, smc_address); |
86 | WREG32_P(mmSMC_IND_ACCESS_CNTL, 0, |
87 | ~SMC_IND_ACCESS_CNTL__AUTO_INCREMENT_IND_0_MASK); |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | int amdgpu_kv_read_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address, |
93 | u32 *value, u32 limit) |
94 | { |
95 | int ret; |
96 | |
97 | ret = kv_set_smc_sram_address(adev, smc_address, limit); |
98 | if (ret) |
99 | return ret; |
100 | |
101 | *value = RREG32(mmSMC_IND_DATA_0); |
102 | return 0; |
103 | } |
104 | |
105 | int amdgpu_kv_smc_dpm_enable(struct amdgpu_device *adev, bool enable) |
106 | { |
107 | if (enable) |
108 | return amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_DPM_Enable); |
109 | else |
110 | return amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_DPM_Disable); |
111 | } |
112 | |
113 | int amdgpu_kv_smc_bapm_enable(struct amdgpu_device *adev, bool enable) |
114 | { |
115 | if (enable) |
116 | return amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_EnableBAPM); |
117 | else |
118 | return amdgpu_kv_notify_message_to_smu(adev, PPSMC_MSG_DisableBAPM); |
119 | } |
120 | |
121 | int amdgpu_kv_copy_bytes_to_smc(struct amdgpu_device *adev, |
122 | u32 smc_start_address, |
123 | const u8 *src, u32 byte_count, u32 limit) |
124 | { |
125 | int ret; |
126 | u32 data, original_data, addr, , t_byte, count, mask; |
127 | |
128 | if ((smc_start_address + byte_count) > limit) |
129 | return -EINVAL; |
130 | |
131 | addr = smc_start_address; |
132 | t_byte = addr & 3; |
133 | |
134 | /* RMW for the initial bytes */ |
135 | if (t_byte != 0) { |
136 | addr -= t_byte; |
137 | |
138 | ret = kv_set_smc_sram_address(adev, smc_address: addr, limit); |
139 | if (ret) |
140 | return ret; |
141 | |
142 | original_data = RREG32(mmSMC_IND_DATA_0); |
143 | |
144 | data = 0; |
145 | mask = 0; |
146 | count = 4; |
147 | while (count > 0) { |
148 | if (t_byte > 0) { |
149 | mask = (mask << 8) | 0xff; |
150 | t_byte--; |
151 | } else if (byte_count > 0) { |
152 | data = (data << 8) + *src++; |
153 | byte_count--; |
154 | mask <<= 8; |
155 | } else { |
156 | data <<= 8; |
157 | mask = (mask << 8) | 0xff; |
158 | } |
159 | count--; |
160 | } |
161 | |
162 | data |= original_data & mask; |
163 | |
164 | ret = kv_set_smc_sram_address(adev, smc_address: addr, limit); |
165 | if (ret) |
166 | return ret; |
167 | |
168 | WREG32(mmSMC_IND_DATA_0, data); |
169 | |
170 | addr += 4; |
171 | } |
172 | |
173 | while (byte_count >= 4) { |
174 | /* SMC address space is BE */ |
175 | data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3]; |
176 | |
177 | ret = kv_set_smc_sram_address(adev, smc_address: addr, limit); |
178 | if (ret) |
179 | return ret; |
180 | |
181 | WREG32(mmSMC_IND_DATA_0, data); |
182 | |
183 | src += 4; |
184 | byte_count -= 4; |
185 | addr += 4; |
186 | } |
187 | |
188 | /* RMW for the final bytes */ |
189 | if (byte_count > 0) { |
190 | data = 0; |
191 | |
192 | ret = kv_set_smc_sram_address(adev, smc_address: addr, limit); |
193 | if (ret) |
194 | return ret; |
195 | |
196 | original_data = RREG32(mmSMC_IND_DATA_0); |
197 | |
198 | extra_shift = 8 * (4 - byte_count); |
199 | |
200 | while (byte_count > 0) { |
201 | /* SMC address space is BE */ |
202 | data = (data << 8) + *src++; |
203 | byte_count--; |
204 | } |
205 | |
206 | data <<= extra_shift; |
207 | |
208 | data |= (original_data & ~((~0UL) << extra_shift)); |
209 | |
210 | ret = kv_set_smc_sram_address(adev, smc_address: addr, limit); |
211 | if (ret) |
212 | return ret; |
213 | |
214 | WREG32(mmSMC_IND_DATA_0, data); |
215 | } |
216 | return 0; |
217 | } |
218 | |
219 | |