Commit | Line | Data |
---|---|---|
282aae55 KW |
1 | /* |
2 | * Copyright 2016 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 | */ | |
47b757fb SR |
23 | |
24 | #include <linux/pci.h> | |
25 | ||
282aae55 KW |
26 | #include "amdgpu.h" |
27 | #include "amdgpu_ih.h" | |
28 | #include "soc15.h" | |
29 | ||
8af7454e FX |
30 | #include "oss/osssys_4_0_offset.h" |
31 | #include "oss/osssys_4_0_sh_mask.h" | |
282aae55 KW |
32 | |
33 | #include "soc15_common.h" | |
34 | #include "vega10_ih.h" | |
35 | ||
74dcfe74 | 36 | #define MAX_REARM_RETRY 10 |
282aae55 KW |
37 | |
38 | static void vega10_ih_set_interrupt_funcs(struct amdgpu_device *adev); | |
39 | ||
1ebb4841 HZ |
40 | /** |
41 | * vega10_ih_init_register_offset - Initialize register offset for ih rings | |
42 | * | |
43 | * @adev: amdgpu_device pointer | |
44 | * | |
45 | * Initialize register offset ih rings (VEGA10). | |
46 | */ | |
47 | static void vega10_ih_init_register_offset(struct amdgpu_device *adev) | |
48 | { | |
49 | struct amdgpu_ih_regs *ih_regs; | |
50 | ||
51 | if (adev->irq.ih.ring_size) { | |
52 | ih_regs = &adev->irq.ih.ih_regs; | |
53 | ih_regs->ih_rb_base = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_BASE); | |
54 | ih_regs->ih_rb_base_hi = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_BASE_HI); | |
55 | ih_regs->ih_rb_cntl = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_CNTL); | |
56 | ih_regs->ih_rb_wptr = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_WPTR); | |
57 | ih_regs->ih_rb_rptr = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_RPTR); | |
58 | ih_regs->ih_doorbell_rptr = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_DOORBELL_RPTR); | |
59 | ih_regs->ih_rb_wptr_addr_lo = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_WPTR_ADDR_LO); | |
60 | ih_regs->ih_rb_wptr_addr_hi = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_WPTR_ADDR_HI); | |
61 | ih_regs->psp_reg_id = PSP_REG_IH_RB_CNTL; | |
62 | } | |
63 | ||
64 | if (adev->irq.ih1.ring_size) { | |
65 | ih_regs = &adev->irq.ih1.ih_regs; | |
66 | ih_regs->ih_rb_base = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_BASE_RING1); | |
67 | ih_regs->ih_rb_base_hi = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_BASE_HI_RING1); | |
68 | ih_regs->ih_rb_cntl = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_CNTL_RING1); | |
69 | ih_regs->ih_rb_wptr = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_WPTR_RING1); | |
70 | ih_regs->ih_rb_rptr = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_RPTR_RING1); | |
71 | ih_regs->ih_doorbell_rptr = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_DOORBELL_RPTR_RING1); | |
72 | ih_regs->psp_reg_id = PSP_REG_IH_RB_CNTL_RING1; | |
73 | } | |
74 | ||
75 | if (adev->irq.ih2.ring_size) { | |
76 | ih_regs = &adev->irq.ih2.ih_regs; | |
77 | ih_regs->ih_rb_base = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_BASE_RING2); | |
78 | ih_regs->ih_rb_base_hi = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_BASE_HI_RING2); | |
79 | ih_regs->ih_rb_cntl = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_CNTL_RING2); | |
80 | ih_regs->ih_rb_wptr = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_WPTR_RING2); | |
81 | ih_regs->ih_rb_rptr = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_RB_RPTR_RING2); | |
82 | ih_regs->ih_doorbell_rptr = SOC15_REG_OFFSET(OSSSYS, 0, mmIH_DOORBELL_RPTR_RING2); | |
83 | ih_regs->psp_reg_id = PSP_REG_IH_RB_CNTL_RING2; | |
84 | } | |
85 | } | |
86 | ||
c7375032 HZ |
87 | /** |
88 | * vega10_ih_toggle_ring_interrupts - toggle the interrupt ring buffer | |
89 | * | |
90 | * @adev: amdgpu_device pointer | |
91 | * @ih: amdgpu_ih_ring pointet | |
92 | * @enable: true - enable the interrupts, false - disable the interrupts | |
93 | * | |
94 | * Toggle the interrupt ring buffer (VEGA10) | |
95 | */ | |
96 | static int vega10_ih_toggle_ring_interrupts(struct amdgpu_device *adev, | |
97 | struct amdgpu_ih_ring *ih, | |
98 | bool enable) | |
99 | { | |
100 | struct amdgpu_ih_regs *ih_regs; | |
101 | uint32_t tmp; | |
102 | ||
103 | ih_regs = &ih->ih_regs; | |
104 | ||
105 | tmp = RREG32(ih_regs->ih_rb_cntl); | |
106 | tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, RB_ENABLE, (enable ? 1 : 0)); | |
9dd9cc2f | 107 | tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, RB_GPU_TS_ENABLE, 1); |
c7375032 HZ |
108 | /* enable_intr field is only valid in ring0 */ |
109 | if (ih == &adev->irq.ih) | |
110 | tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, ENABLE_INTR, (enable ? 1 : 0)); | |
111 | if (amdgpu_sriov_vf(adev)) { | |
112 | if (psp_reg_program(&adev->psp, ih_regs->psp_reg_id, tmp)) { | |
113 | dev_err(adev->dev, "PSP program IH_RB_CNTL failed!\n"); | |
114 | return -ETIMEDOUT; | |
115 | } | |
116 | } else { | |
117 | WREG32(ih_regs->ih_rb_cntl, tmp); | |
118 | } | |
119 | ||
120 | if (enable) { | |
121 | ih->enabled = true; | |
122 | } else { | |
123 | /* set rptr, wptr to 0 */ | |
124 | WREG32(ih_regs->ih_rb_rptr, 0); | |
125 | WREG32(ih_regs->ih_rb_wptr, 0); | |
126 | ih->enabled = false; | |
127 | ih->rptr = 0; | |
128 | } | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
fd95e1b1 HZ |
133 | /** |
134 | * vega10_ih_toggle_interrupts - Toggle all the available interrupt ring buffers | |
135 | * | |
136 | * @adev: amdgpu_device pointer | |
137 | * @enable: enable or disable interrupt ring buffers | |
138 | * | |
139 | * Toggle all the available interrupt ring buffers (VEGA10). | |
140 | */ | |
141 | static int vega10_ih_toggle_interrupts(struct amdgpu_device *adev, bool enable) | |
142 | { | |
143 | struct amdgpu_ih_ring *ih[] = {&adev->irq.ih, &adev->irq.ih1, &adev->irq.ih2}; | |
144 | int i; | |
145 | int r; | |
146 | ||
147 | for (i = 0; i < ARRAY_SIZE(ih); i++) { | |
148 | if (ih[i]->ring_size) { | |
149 | r = vega10_ih_toggle_ring_interrupts(adev, ih[i], enable); | |
150 | if (r) | |
151 | return r; | |
152 | } | |
153 | } | |
154 | ||
155 | return 0; | |
156 | } | |
157 | ||
ad710812 CK |
158 | static uint32_t vega10_ih_rb_cntl(struct amdgpu_ih_ring *ih, uint32_t ih_rb_cntl) |
159 | { | |
160 | int rb_bufsz = order_base_2(ih->ring_size / 4); | |
161 | ||
162 | ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, | |
163 | MC_SPACE, ih->use_bus_addr ? 1 : 4); | |
164 | ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, | |
165 | WPTR_OVERFLOW_CLEAR, 1); | |
166 | ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, | |
167 | WPTR_OVERFLOW_ENABLE, 1); | |
168 | ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_SIZE, rb_bufsz); | |
169 | /* Ring Buffer write pointer writeback. If enabled, IH_RB_WPTR register | |
170 | * value is written to memory | |
171 | */ | |
172 | ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, | |
173 | WPTR_WRITEBACK_ENABLE, 1); | |
174 | ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, MC_SNOOP, 1); | |
175 | ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, MC_RO, 0); | |
176 | ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, MC_VMID, 0); | |
177 | ||
178 | return ih_rb_cntl; | |
282aae55 KW |
179 | } |
180 | ||
1ae64cec CK |
181 | static uint32_t vega10_ih_doorbell_rptr(struct amdgpu_ih_ring *ih) |
182 | { | |
183 | u32 ih_doorbell_rtpr = 0; | |
184 | ||
185 | if (ih->use_doorbell) { | |
186 | ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, | |
187 | IH_DOORBELL_RPTR, OFFSET, | |
188 | ih->doorbell_index); | |
189 | ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, | |
190 | IH_DOORBELL_RPTR, | |
191 | ENABLE, 1); | |
192 | } else { | |
193 | ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, | |
194 | IH_DOORBELL_RPTR, | |
195 | ENABLE, 0); | |
196 | } | |
197 | return ih_doorbell_rtpr; | |
198 | } | |
199 | ||
ffa02126 HZ |
200 | /** |
201 | * vega10_ih_enable_ring - enable an ih ring buffer | |
202 | * | |
203 | * @adev: amdgpu_device pointer | |
204 | * @ih: amdgpu_ih_ring pointer | |
205 | * | |
206 | * Enable an ih ring buffer (VEGA10) | |
207 | */ | |
208 | static int vega10_ih_enable_ring(struct amdgpu_device *adev, | |
209 | struct amdgpu_ih_ring *ih) | |
210 | { | |
211 | struct amdgpu_ih_regs *ih_regs; | |
212 | uint32_t tmp; | |
213 | ||
214 | ih_regs = &ih->ih_regs; | |
215 | ||
216 | /* Ring Buffer base. [39:8] of 40-bit address of the beginning of the ring buffer*/ | |
217 | WREG32(ih_regs->ih_rb_base, ih->gpu_addr >> 8); | |
218 | WREG32(ih_regs->ih_rb_base_hi, (ih->gpu_addr >> 40) & 0xff); | |
219 | ||
220 | tmp = RREG32(ih_regs->ih_rb_cntl); | |
221 | tmp = vega10_ih_rb_cntl(ih, tmp); | |
222 | if (ih == &adev->irq.ih) | |
223 | tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, RPTR_REARM, !!adev->irq.msi_enabled); | |
b672cb1e | 224 | if (ih == &adev->irq.ih1) |
ffa02126 | 225 | tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, RB_FULL_DRAIN_ENABLE, 1); |
ffa02126 HZ |
226 | if (amdgpu_sriov_vf(adev)) { |
227 | if (psp_reg_program(&adev->psp, ih_regs->psp_reg_id, tmp)) { | |
228 | dev_err(adev->dev, "PSP program IH_RB_CNTL failed!\n"); | |
229 | return -ETIMEDOUT; | |
230 | } | |
231 | } else { | |
232 | WREG32(ih_regs->ih_rb_cntl, tmp); | |
233 | } | |
234 | ||
235 | if (ih == &adev->irq.ih) { | |
236 | /* set the ih ring 0 writeback address whether it's enabled or not */ | |
237 | WREG32(ih_regs->ih_rb_wptr_addr_lo, lower_32_bits(ih->wptr_addr)); | |
238 | WREG32(ih_regs->ih_rb_wptr_addr_hi, upper_32_bits(ih->wptr_addr) & 0xFFFF); | |
239 | } | |
240 | ||
241 | /* set rptr, wptr to 0 */ | |
242 | WREG32(ih_regs->ih_rb_wptr, 0); | |
243 | WREG32(ih_regs->ih_rb_rptr, 0); | |
244 | ||
245 | WREG32(ih_regs->ih_doorbell_rptr, vega10_ih_doorbell_rptr(ih)); | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
282aae55 KW |
250 | /** |
251 | * vega10_ih_irq_init - init and enable the interrupt ring | |
252 | * | |
253 | * @adev: amdgpu_device pointer | |
254 | * | |
255 | * Allocate a ring buffer for the interrupt controller, | |
256 | * enable the RLC, disable interrupts, enable the IH | |
257 | * ring buffer and enable it (VI). | |
258 | * Called at device load and reume. | |
259 | * Returns 0 for success, errors for failure. | |
260 | */ | |
261 | static int vega10_ih_irq_init(struct amdgpu_device *adev) | |
262 | { | |
21822b6a HZ |
263 | struct amdgpu_ih_ring *ih[] = {&adev->irq.ih, &adev->irq.ih1, &adev->irq.ih2}; |
264 | u32 ih_chicken; | |
fd95e1b1 | 265 | int ret; |
21822b6a | 266 | int i; |
282aae55 KW |
267 | |
268 | /* disable irqs */ | |
fd95e1b1 HZ |
269 | ret = vega10_ih_toggle_interrupts(adev, false); |
270 | if (ret) | |
271 | return ret; | |
282aae55 | 272 | |
bebc0762 | 273 | adev->nbio.funcs->ih_control(adev); |
282aae55 | 274 | |
95c0c257 | 275 | if (adev->asic_type == CHIP_RENOIR) { |
25344d7e ZL |
276 | ih_chicken = RREG32_SOC15(OSSSYS, 0, mmIH_CHICKEN); |
277 | if (adev->irq.ih.use_bus_addr) { | |
278 | ih_chicken = REG_SET_FIELD(ih_chicken, IH_CHICKEN, | |
279 | MC_SPACE_GPA_ENABLE, 1); | |
25344d7e | 280 | } |
537e3bbf | 281 | WREG32_SOC15(OSSSYS, 0, mmIH_CHICKEN, ih_chicken); |
25344d7e | 282 | } |
f9c84ae5 | 283 | |
21822b6a HZ |
284 | for (i = 0; i < ARRAY_SIZE(ih); i++) { |
285 | if (ih[i]->ring_size) { | |
286 | ret = vega10_ih_enable_ring(adev, ih[i]); | |
287 | if (ret) | |
288 | return ret; | |
470b4250 | 289 | } |
ad710812 CK |
290 | } |
291 | ||
282aae55 KW |
292 | pci_set_master(adev->pdev); |
293 | ||
294 | /* enable interrupts */ | |
fd95e1b1 HZ |
295 | ret = vega10_ih_toggle_interrupts(adev, true); |
296 | if (ret) | |
297 | return ret; | |
282aae55 | 298 | |
7f03b148 HZ |
299 | if (adev->irq.ih_soft.ring_size) |
300 | adev->irq.ih_soft.enabled = true; | |
301 | ||
fd95e1b1 | 302 | return 0; |
282aae55 KW |
303 | } |
304 | ||
305 | /** | |
306 | * vega10_ih_irq_disable - disable interrupts | |
307 | * | |
308 | * @adev: amdgpu_device pointer | |
309 | * | |
310 | * Disable interrupts on the hw (VEGA10). | |
311 | */ | |
312 | static void vega10_ih_irq_disable(struct amdgpu_device *adev) | |
313 | { | |
fd95e1b1 | 314 | vega10_ih_toggle_interrupts(adev, false); |
282aae55 KW |
315 | |
316 | /* Wait and acknowledge irq */ | |
317 | mdelay(1); | |
318 | } | |
319 | ||
320 | /** | |
321 | * vega10_ih_get_wptr - get the IH ring buffer wptr | |
322 | * | |
323 | * @adev: amdgpu_device pointer | |
5162e40e | 324 | * @ih: IH ring buffer to fetch wptr |
282aae55 KW |
325 | * |
326 | * Get the IH ring buffer wptr from either the register | |
327 | * or the writeback memory buffer (VEGA10). Also check for | |
328 | * ring buffer overflow and deal with it. | |
329 | * Returns the value of the wptr. | |
330 | */ | |
8bb9eb48 CK |
331 | static u32 vega10_ih_get_wptr(struct amdgpu_device *adev, |
332 | struct amdgpu_ih_ring *ih) | |
282aae55 | 333 | { |
554bdbf6 HZ |
334 | u32 wptr, tmp; |
335 | struct amdgpu_ih_regs *ih_regs; | |
282aae55 | 336 | |
de8341ee | 337 | if (ih == &adev->irq.ih || ih == &adev->irq.ih_soft) { |
b672cb1e PY |
338 | /* Only ring0 supports writeback. On other rings fall back |
339 | * to register-based code with overflow checking below. | |
de8341ee MJ |
340 | * ih_soft ring doesn't have any backing hardware registers, |
341 | * update wptr and return. | |
b672cb1e PY |
342 | */ |
343 | wptr = le32_to_cpu(*ih->wptr_cpu); | |
282aae55 | 344 | |
b672cb1e PY |
345 | if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) |
346 | goto out; | |
347 | } | |
348 | ||
349 | ih_regs = &ih->ih_regs; | |
b8217575 CK |
350 | |
351 | /* Double check that the overflow wasn't already cleared. */ | |
554bdbf6 | 352 | wptr = RREG32_NO_KIQ(ih_regs->ih_rb_wptr); |
b8217575 CK |
353 | if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) |
354 | goto out; | |
355 | ||
356 | wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0); | |
357 | ||
358 | /* When a ring buffer overflow happen start parsing interrupt | |
359 | * from the last not overwritten vector (wptr + 32). Hopefully | |
360 | * this should allow us to catchup. | |
361 | */ | |
362 | tmp = (wptr + 32) & ih->ptr_mask; | |
363 | dev_warn(adev->dev, "IH ring buffer overflow " | |
364 | "(0x%08X, 0x%08X, 0x%08X)\n", | |
365 | wptr, ih->rptr, tmp); | |
366 | ih->rptr = tmp; | |
367 | ||
554bdbf6 | 368 | tmp = RREG32_NO_KIQ(ih_regs->ih_rb_cntl); |
b8217575 | 369 | tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1); |
554bdbf6 | 370 | WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp); |
b8217575 CK |
371 | |
372 | out: | |
8bb9eb48 | 373 | return (wptr & ih->ptr_mask); |
282aae55 KW |
374 | } |
375 | ||
74dcfe74 TH |
376 | /** |
377 | * vega10_ih_irq_rearm - rearm IRQ if lost | |
378 | * | |
379 | * @adev: amdgpu_device pointer | |
5162e40e | 380 | * @ih: IH ring to match |
74dcfe74 TH |
381 | * |
382 | */ | |
383 | static void vega10_ih_irq_rearm(struct amdgpu_device *adev, | |
384 | struct amdgpu_ih_ring *ih) | |
385 | { | |
74dcfe74 TH |
386 | uint32_t v = 0; |
387 | uint32_t i = 0; | |
554bdbf6 | 388 | struct amdgpu_ih_regs *ih_regs; |
74dcfe74 | 389 | |
554bdbf6 | 390 | ih_regs = &ih->ih_regs; |
74dcfe74 TH |
391 | /* Rearm IRQ / re-wwrite doorbell if doorbell write is lost */ |
392 | for (i = 0; i < MAX_REARM_RETRY; i++) { | |
554bdbf6 | 393 | v = RREG32_NO_KIQ(ih_regs->ih_rb_rptr); |
74dcfe74 TH |
394 | if ((v < ih->ring_size) && (v != ih->rptr)) |
395 | WDOORBELL32(ih->doorbell_index, ih->rptr); | |
396 | else | |
397 | break; | |
398 | } | |
399 | } | |
400 | ||
282aae55 KW |
401 | /** |
402 | * vega10_ih_set_rptr - set the IH ring buffer rptr | |
403 | * | |
404 | * @adev: amdgpu_device pointer | |
5162e40e | 405 | * @ih: IH ring buffer to set rptr |
282aae55 KW |
406 | * |
407 | * Set the IH ring buffer rptr. | |
408 | */ | |
8bb9eb48 CK |
409 | static void vega10_ih_set_rptr(struct amdgpu_device *adev, |
410 | struct amdgpu_ih_ring *ih) | |
282aae55 | 411 | { |
554bdbf6 HZ |
412 | struct amdgpu_ih_regs *ih_regs; |
413 | ||
de8341ee MJ |
414 | if (ih == &adev->irq.ih_soft) |
415 | return; | |
416 | ||
8bb9eb48 | 417 | if (ih->use_doorbell) { |
282aae55 | 418 | /* XXX check if swapping is necessary on BE */ |
d81f78b4 | 419 | *ih->rptr_cpu = ih->rptr; |
8bb9eb48 | 420 | WDOORBELL32(ih->doorbell_index, ih->rptr); |
74dcfe74 TH |
421 | |
422 | if (amdgpu_sriov_vf(adev)) | |
423 | vega10_ih_irq_rearm(adev, ih); | |
554bdbf6 HZ |
424 | } else { |
425 | ih_regs = &ih->ih_regs; | |
426 | WREG32(ih_regs->ih_rb_rptr, ih->rptr); | |
282aae55 KW |
427 | } |
428 | } | |
429 | ||
cf67950e CK |
430 | /** |
431 | * vega10_ih_self_irq - dispatch work for ring 1 and 2 | |
432 | * | |
433 | * @adev: amdgpu_device pointer | |
434 | * @source: irq source | |
435 | * @entry: IV with WPTR update | |
436 | * | |
437 | * Update the WPTR from the IV and schedule work to handle the entries. | |
438 | */ | |
439 | static int vega10_ih_self_irq(struct amdgpu_device *adev, | |
440 | struct amdgpu_irq_src *source, | |
441 | struct amdgpu_iv_entry *entry) | |
442 | { | |
cf67950e CK |
443 | switch (entry->ring_id) { |
444 | case 1: | |
cf67950e CK |
445 | schedule_work(&adev->irq.ih1_work); |
446 | break; | |
447 | case 2: | |
cf67950e CK |
448 | schedule_work(&adev->irq.ih2_work); |
449 | break; | |
450 | default: break; | |
451 | } | |
452 | return 0; | |
453 | } | |
454 | ||
455 | static const struct amdgpu_irq_src_funcs vega10_ih_self_irq_funcs = { | |
456 | .process = vega10_ih_self_irq, | |
457 | }; | |
458 | ||
459 | static void vega10_ih_set_self_irq_funcs(struct amdgpu_device *adev) | |
460 | { | |
461 | adev->irq.self_irq.num_types = 0; | |
462 | adev->irq.self_irq.funcs = &vega10_ih_self_irq_funcs; | |
463 | } | |
464 | ||
282aae55 KW |
465 | static int vega10_ih_early_init(void *handle) |
466 | { | |
467 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; | |
468 | ||
469 | vega10_ih_set_interrupt_funcs(adev); | |
cf67950e | 470 | vega10_ih_set_self_irq_funcs(adev); |
282aae55 KW |
471 | return 0; |
472 | } | |
473 | ||
474 | static int vega10_ih_sw_init(void *handle) | |
475 | { | |
282aae55 | 476 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
cf67950e CK |
477 | int r; |
478 | ||
479 | r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_IH, 0, | |
480 | &adev->irq.self_irq); | |
481 | if (r) | |
482 | return r; | |
282aae55 | 483 | |
425c3143 | 484 | r = amdgpu_ih_ring_init(adev, &adev->irq.ih, 256 * 1024, true); |
282aae55 KW |
485 | if (r) |
486 | return r; | |
487 | ||
1ae64cec CK |
488 | adev->irq.ih.use_doorbell = true; |
489 | adev->irq.ih.doorbell_index = adev->doorbell_index.ih << 1; | |
490 | ||
9f18985d HZ |
491 | if (!(adev->flags & AMD_IS_APU)) { |
492 | r = amdgpu_ih_ring_init(adev, &adev->irq.ih1, PAGE_SIZE, true); | |
493 | if (r) | |
494 | return r; | |
b51cd19e | 495 | |
9f18985d HZ |
496 | adev->irq.ih1.use_doorbell = true; |
497 | adev->irq.ih1.doorbell_index = (adev->doorbell_index.ih + 1) << 1; | |
b51cd19e | 498 | |
9f18985d HZ |
499 | r = amdgpu_ih_ring_init(adev, &adev->irq.ih2, PAGE_SIZE, true); |
500 | if (r) | |
501 | return r; | |
282aae55 | 502 | |
9f18985d HZ |
503 | adev->irq.ih2.use_doorbell = true; |
504 | adev->irq.ih2.doorbell_index = (adev->doorbell_index.ih + 2) << 1; | |
505 | } | |
f0594717 HZ |
506 | /* initialize ih control registers offset */ |
507 | vega10_ih_init_register_offset(adev); | |
508 | ||
47509189 CK |
509 | r = amdgpu_ih_ring_init(adev, &adev->irq.ih_soft, PAGE_SIZE, true); |
510 | if (r) | |
511 | return r; | |
512 | ||
282aae55 KW |
513 | r = amdgpu_irq_init(adev); |
514 | ||
515 | return r; | |
516 | } | |
517 | ||
518 | static int vega10_ih_sw_fini(void *handle) | |
519 | { | |
520 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; | |
521 | ||
72c8c97b | 522 | amdgpu_irq_fini_sw(adev); |
282aae55 KW |
523 | |
524 | return 0; | |
525 | } | |
526 | ||
527 | static int vega10_ih_hw_init(void *handle) | |
528 | { | |
282aae55 KW |
529 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
530 | ||
38c1c736 | 531 | return vega10_ih_irq_init(adev); |
282aae55 KW |
532 | } |
533 | ||
534 | static int vega10_ih_hw_fini(void *handle) | |
535 | { | |
536 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; | |
537 | ||
538 | vega10_ih_irq_disable(adev); | |
539 | ||
540 | return 0; | |
541 | } | |
542 | ||
543 | static int vega10_ih_suspend(void *handle) | |
544 | { | |
545 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; | |
546 | ||
547 | return vega10_ih_hw_fini(adev); | |
548 | } | |
549 | ||
550 | static int vega10_ih_resume(void *handle) | |
551 | { | |
552 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; | |
553 | ||
554 | return vega10_ih_hw_init(adev); | |
555 | } | |
556 | ||
557 | static bool vega10_ih_is_idle(void *handle) | |
558 | { | |
559 | /* todo */ | |
560 | return true; | |
561 | } | |
562 | ||
563 | static int vega10_ih_wait_for_idle(void *handle) | |
564 | { | |
565 | /* todo */ | |
566 | return -ETIMEDOUT; | |
567 | } | |
568 | ||
569 | static int vega10_ih_soft_reset(void *handle) | |
570 | { | |
571 | /* todo */ | |
572 | ||
573 | return 0; | |
574 | } | |
575 | ||
227f7d58 KF |
576 | static void vega10_ih_update_clockgating_state(struct amdgpu_device *adev, |
577 | bool enable) | |
578 | { | |
579 | uint32_t data, def, field_val; | |
580 | ||
581 | if (adev->cg_flags & AMD_CG_SUPPORT_IH_CG) { | |
582 | def = data = RREG32_SOC15(OSSSYS, 0, mmIH_CLK_CTRL); | |
583 | field_val = enable ? 0 : 1; | |
584 | /** | |
2601fa64 | 585 | * Vega10/12 and RAVEN don't have IH_BUFFER_MEM_CLK_SOFT_OVERRIDE field. |
227f7d58 | 586 | */ |
2601fa64 | 587 | if (adev->asic_type == CHIP_RENOIR) |
227f7d58 KF |
588 | data = REG_SET_FIELD(data, IH_CLK_CTRL, |
589 | IH_BUFFER_MEM_CLK_SOFT_OVERRIDE, field_val); | |
227f7d58 KF |
590 | |
591 | data = REG_SET_FIELD(data, IH_CLK_CTRL, | |
592 | DBUS_MUX_CLK_SOFT_OVERRIDE, field_val); | |
593 | data = REG_SET_FIELD(data, IH_CLK_CTRL, | |
594 | OSSSYS_SHARE_CLK_SOFT_OVERRIDE, field_val); | |
595 | data = REG_SET_FIELD(data, IH_CLK_CTRL, | |
596 | LIMIT_SMN_CLK_SOFT_OVERRIDE, field_val); | |
597 | data = REG_SET_FIELD(data, IH_CLK_CTRL, | |
598 | DYN_CLK_SOFT_OVERRIDE, field_val); | |
599 | data = REG_SET_FIELD(data, IH_CLK_CTRL, | |
600 | REG_CLK_SOFT_OVERRIDE, field_val); | |
601 | if (def != data) | |
602 | WREG32_SOC15(OSSSYS, 0, mmIH_CLK_CTRL, data); | |
603 | } | |
604 | } | |
605 | ||
282aae55 KW |
606 | static int vega10_ih_set_clockgating_state(void *handle, |
607 | enum amd_clockgating_state state) | |
608 | { | |
227f7d58 KF |
609 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; |
610 | ||
611 | vega10_ih_update_clockgating_state(adev, | |
a9d4fe2f | 612 | state == AMD_CG_STATE_GATE); |
282aae55 | 613 | return 0; |
227f7d58 | 614 | |
282aae55 KW |
615 | } |
616 | ||
617 | static int vega10_ih_set_powergating_state(void *handle, | |
618 | enum amd_powergating_state state) | |
619 | { | |
620 | return 0; | |
621 | } | |
622 | ||
623 | const struct amd_ip_funcs vega10_ih_ip_funcs = { | |
624 | .name = "vega10_ih", | |
625 | .early_init = vega10_ih_early_init, | |
626 | .late_init = NULL, | |
627 | .sw_init = vega10_ih_sw_init, | |
628 | .sw_fini = vega10_ih_sw_fini, | |
629 | .hw_init = vega10_ih_hw_init, | |
630 | .hw_fini = vega10_ih_hw_fini, | |
631 | .suspend = vega10_ih_suspend, | |
632 | .resume = vega10_ih_resume, | |
633 | .is_idle = vega10_ih_is_idle, | |
634 | .wait_for_idle = vega10_ih_wait_for_idle, | |
635 | .soft_reset = vega10_ih_soft_reset, | |
636 | .set_clockgating_state = vega10_ih_set_clockgating_state, | |
637 | .set_powergating_state = vega10_ih_set_powergating_state, | |
638 | }; | |
639 | ||
640 | static const struct amdgpu_ih_funcs vega10_ih_funcs = { | |
641 | .get_wptr = vega10_ih_get_wptr, | |
40838281 | 642 | .decode_iv = amdgpu_ih_decode_iv_helper, |
3c2d6ea2 | 643 | .decode_iv_ts = amdgpu_ih_decode_iv_ts_helper, |
282aae55 KW |
644 | .set_rptr = vega10_ih_set_rptr |
645 | }; | |
646 | ||
647 | static void vega10_ih_set_interrupt_funcs(struct amdgpu_device *adev) | |
648 | { | |
f54b30d7 | 649 | adev->irq.ih_funcs = &vega10_ih_funcs; |
282aae55 KW |
650 | } |
651 | ||
652 | const struct amdgpu_ip_block_version vega10_ih_ip_block = | |
653 | { | |
654 | .type = AMD_IP_BLOCK_TYPE_IH, | |
655 | .major = 4, | |
656 | .minor = 0, | |
657 | .rev = 0, | |
658 | .funcs = &vega10_ih_ip_funcs, | |
659 | }; |