aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/amd/amdgpu/cz_ih.c
diff options
context:
space:
mode:
authorAlex Deucher <alexander.deucher@amd.com>2015-04-20 17:31:14 -0400
committerAlex Deucher <alexander.deucher@amd.com>2015-06-03 21:03:17 -0400
commitaaa36a976bbb9b02a54c087ff390c0bad1d18e3e (patch)
tree105be3c06ef33c39e6934801d386847950d4ebf9 /drivers/gpu/drm/amd/amdgpu/cz_ih.c
parenta2e73f56fa6282481927ec43aa9362c03c2e2104 (diff)
drm/amdgpu: Add initial VI support
This adds initial support for VI asics. This includes Iceland, Tonga, and Carrizo. Our inital focus as been Carrizo, so there are still gaps in support for Tonga and Iceland, notably power management. Acked-by: Christian König <christian.koenig@amd.com> Acked-by: Jammy Zhou <Jammy.Zhou@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/cz_ih.c')
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cz_ih.c435
1 files changed, 435 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/cz_ih.c b/drivers/gpu/drm/amd/amdgpu/cz_ih.c
new file mode 100644
index 000000000000..80d508e64a86
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/cz_ih.c
@@ -0,0 +1,435 @@
1/*
2 * Copyright 2014 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 */
23#include "drmP.h"
24#include "amdgpu.h"
25#include "amdgpu_ih.h"
26#include "vid.h"
27
28#include "oss/oss_3_0_1_d.h"
29#include "oss/oss_3_0_1_sh_mask.h"
30
31#include "bif/bif_5_1_d.h"
32#include "bif/bif_5_1_sh_mask.h"
33
34/*
35 * Interrupts
36 * Starting with r6xx, interrupts are handled via a ring buffer.
37 * Ring buffers are areas of GPU accessible memory that the GPU
38 * writes interrupt vectors into and the host reads vectors out of.
39 * There is a rptr (read pointer) that determines where the
40 * host is currently reading, and a wptr (write pointer)
41 * which determines where the GPU has written. When the
42 * pointers are equal, the ring is idle. When the GPU
43 * writes vectors to the ring buffer, it increments the
44 * wptr. When there is an interrupt, the host then starts
45 * fetching commands and processing them until the pointers are
46 * equal again at which point it updates the rptr.
47 */
48
49static void cz_ih_set_interrupt_funcs(struct amdgpu_device *adev);
50
51/**
52 * cz_ih_enable_interrupts - Enable the interrupt ring buffer
53 *
54 * @adev: amdgpu_device pointer
55 *
56 * Enable the interrupt ring buffer (VI).
57 */
58static void cz_ih_enable_interrupts(struct amdgpu_device *adev)
59{
60 u32 ih_cntl = RREG32(mmIH_CNTL);
61 u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
62
63 ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, ENABLE_INTR, 1);
64 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 1);
65 WREG32(mmIH_CNTL, ih_cntl);
66 WREG32(mmIH_RB_CNTL, ih_rb_cntl);
67 adev->irq.ih.enabled = true;
68}
69
70/**
71 * cz_ih_disable_interrupts - Disable the interrupt ring buffer
72 *
73 * @adev: amdgpu_device pointer
74 *
75 * Disable the interrupt ring buffer (VI).
76 */
77static void cz_ih_disable_interrupts(struct amdgpu_device *adev)
78{
79 u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
80 u32 ih_cntl = RREG32(mmIH_CNTL);
81
82 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 0);
83 ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, ENABLE_INTR, 0);
84 WREG32(mmIH_RB_CNTL, ih_rb_cntl);
85 WREG32(mmIH_CNTL, ih_cntl);
86 /* set rptr, wptr to 0 */
87 WREG32(mmIH_RB_RPTR, 0);
88 WREG32(mmIH_RB_WPTR, 0);
89 adev->irq.ih.enabled = false;
90 adev->irq.ih.rptr = 0;
91}
92
93/**
94 * cz_ih_irq_init - init and enable the interrupt ring
95 *
96 * @adev: amdgpu_device pointer
97 *
98 * Allocate a ring buffer for the interrupt controller,
99 * enable the RLC, disable interrupts, enable the IH
100 * ring buffer and enable it (VI).
101 * Called at device load and reume.
102 * Returns 0 for success, errors for failure.
103 */
104static int cz_ih_irq_init(struct amdgpu_device *adev)
105{
106 int ret = 0;
107 int rb_bufsz;
108 u32 interrupt_cntl, ih_cntl, ih_rb_cntl;
109 u64 wptr_off;
110
111 /* disable irqs */
112 cz_ih_disable_interrupts(adev);
113
114 /* setup interrupt control */
115 WREG32(mmINTERRUPT_CNTL2, adev->dummy_page.addr >> 8);
116 interrupt_cntl = RREG32(mmINTERRUPT_CNTL);
117 /* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi
118 * INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN
119 */
120 interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_DUMMY_RD_OVERRIDE, 0);
121 /* INTERRUPT_CNTL__IH_REQ_NONSNOOP_EN_MASK=1 if ring is in non-cacheable memory, e.g., vram */
122 interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_REQ_NONSNOOP_EN, 0);
123 WREG32(mmINTERRUPT_CNTL, interrupt_cntl);
124
125 /* Ring Buffer base. [39:8] of 40-bit address of the beginning of the ring buffer*/
126 WREG32(mmIH_RB_BASE, adev->irq.ih.gpu_addr >> 8);
127
128 rb_bufsz = order_base_2(adev->irq.ih.ring_size / 4);
129 ih_rb_cntl = REG_SET_FIELD(0, IH_RB_CNTL, WPTR_OVERFLOW_ENABLE, 1);
130 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
131 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_SIZE, rb_bufsz);
132
133 /* Ring Buffer write pointer writeback. If enabled, IH_RB_WPTR register value is written to memory */
134 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_WRITEBACK_ENABLE, 1);
135
136 /* set the writeback address whether it's enabled or not */
137 wptr_off = adev->wb.gpu_addr + (adev->irq.ih.wptr_offs * 4);
138 WREG32(mmIH_RB_WPTR_ADDR_LO, lower_32_bits(wptr_off));
139 WREG32(mmIH_RB_WPTR_ADDR_HI, upper_32_bits(wptr_off) & 0xFF);
140
141 WREG32(mmIH_RB_CNTL, ih_rb_cntl);
142
143 /* set rptr, wptr to 0 */
144 WREG32(mmIH_RB_RPTR, 0);
145 WREG32(mmIH_RB_WPTR, 0);
146
147 /* Default settings for IH_CNTL (disabled at first) */
148 ih_cntl = RREG32(mmIH_CNTL);
149 ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, MC_VMID, 0);
150
151 if (adev->irq.msi_enabled)
152 ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, RPTR_REARM, 1);
153 WREG32(mmIH_CNTL, ih_cntl);
154
155 pci_set_master(adev->pdev);
156
157 /* enable interrupts */
158 cz_ih_enable_interrupts(adev);
159
160 return ret;
161}
162
163/**
164 * cz_ih_irq_disable - disable interrupts
165 *
166 * @adev: amdgpu_device pointer
167 *
168 * Disable interrupts on the hw (VI).
169 */
170static void cz_ih_irq_disable(struct amdgpu_device *adev)
171{
172 cz_ih_disable_interrupts(adev);
173
174 /* Wait and acknowledge irq */
175 mdelay(1);
176}
177
178/**
179 * cz_ih_get_wptr - get the IH ring buffer wptr
180 *
181 * @adev: amdgpu_device pointer
182 *
183 * Get the IH ring buffer wptr from either the register
184 * or the writeback memory buffer (VI). Also check for
185 * ring buffer overflow and deal with it.
186 * Used by cz_irq_process(VI).
187 * Returns the value of the wptr.
188 */
189static u32 cz_ih_get_wptr(struct amdgpu_device *adev)
190{
191 u32 wptr, tmp;
192
193 wptr = le32_to_cpu(adev->wb.wb[adev->irq.ih.wptr_offs]);
194
195 if (REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) {
196 wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0);
197 /* When a ring buffer overflow happen start parsing interrupt
198 * from the last not overwritten vector (wptr + 16). Hopefully
199 * this should allow us to catchup.
200 */
201 dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
202 wptr, adev->irq.ih.rptr, (wptr + 16) & adev->irq.ih.ptr_mask);
203 adev->irq.ih.rptr = (wptr + 16) & adev->irq.ih.ptr_mask;
204 tmp = RREG32(mmIH_RB_CNTL);
205 tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
206 WREG32(mmIH_RB_CNTL, tmp);
207 }
208 return (wptr & adev->irq.ih.ptr_mask);
209}
210
211/**
212 * cz_ih_decode_iv - decode an interrupt vector
213 *
214 * @adev: amdgpu_device pointer
215 *
216 * Decodes the interrupt vector at the current rptr
217 * position and also advance the position.
218 */
219static void cz_ih_decode_iv(struct amdgpu_device *adev,
220 struct amdgpu_iv_entry *entry)
221{
222 /* wptr/rptr are in bytes! */
223 u32 ring_index = adev->irq.ih.rptr >> 2;
224 uint32_t dw[4];
225
226 dw[0] = le32_to_cpu(adev->irq.ih.ring[ring_index + 0]);
227 dw[1] = le32_to_cpu(adev->irq.ih.ring[ring_index + 1]);
228 dw[2] = le32_to_cpu(adev->irq.ih.ring[ring_index + 2]);
229 dw[3] = le32_to_cpu(adev->irq.ih.ring[ring_index + 3]);
230
231 entry->src_id = dw[0] & 0xff;
232 entry->src_data = dw[1] & 0xfffffff;
233 entry->ring_id = dw[2] & 0xff;
234 entry->vm_id = (dw[2] >> 8) & 0xff;
235 entry->pas_id = (dw[2] >> 16) & 0xffff;
236
237 /* wptr/rptr are in bytes! */
238 adev->irq.ih.rptr += 16;
239}
240
241/**
242 * cz_ih_set_rptr - set the IH ring buffer rptr
243 *
244 * @adev: amdgpu_device pointer
245 *
246 * Set the IH ring buffer rptr.
247 */
248static void cz_ih_set_rptr(struct amdgpu_device *adev)
249{
250 WREG32(mmIH_RB_RPTR, adev->irq.ih.rptr);
251}
252
253static int cz_ih_early_init(struct amdgpu_device *adev)
254{
255 cz_ih_set_interrupt_funcs(adev);
256 return 0;
257}
258
259static int cz_ih_sw_init(struct amdgpu_device *adev)
260{
261 int r;
262
263 r = amdgpu_ih_ring_init(adev, 64 * 1024, false);
264 if (r)
265 return r;
266
267 r = amdgpu_irq_init(adev);
268
269 return r;
270}
271
272static int cz_ih_sw_fini(struct amdgpu_device *adev)
273{
274 amdgpu_irq_fini(adev);
275 amdgpu_ih_ring_fini(adev);
276
277 return 0;
278}
279
280static int cz_ih_hw_init(struct amdgpu_device *adev)
281{
282 int r;
283
284 r = cz_ih_irq_init(adev);
285 if (r)
286 return r;
287
288 return 0;
289}
290
291static int cz_ih_hw_fini(struct amdgpu_device *adev)
292{
293 cz_ih_irq_disable(adev);
294
295 return 0;
296}
297
298static int cz_ih_suspend(struct amdgpu_device *adev)
299{
300 return cz_ih_hw_fini(adev);
301}
302
303static int cz_ih_resume(struct amdgpu_device *adev)
304{
305 return cz_ih_hw_init(adev);
306}
307
308static bool cz_ih_is_idle(struct amdgpu_device *adev)
309{
310 u32 tmp = RREG32(mmSRBM_STATUS);
311
312 if (REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
313 return false;
314
315 return true;
316}
317
318static int cz_ih_wait_for_idle(struct amdgpu_device *adev)
319{
320 unsigned i;
321 u32 tmp;
322
323 for (i = 0; i < adev->usec_timeout; i++) {
324 /* read MC_STATUS */
325 tmp = RREG32(mmSRBM_STATUS);
326 if (!REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
327 return 0;
328 udelay(1);
329 }
330 return -ETIMEDOUT;
331}
332
333static void cz_ih_print_status(struct amdgpu_device *adev)
334{
335 dev_info(adev->dev, "CZ IH registers\n");
336 dev_info(adev->dev, " SRBM_STATUS=0x%08X\n",
337 RREG32(mmSRBM_STATUS));
338 dev_info(adev->dev, " SRBM_STATUS2=0x%08X\n",
339 RREG32(mmSRBM_STATUS2));
340 dev_info(adev->dev, " INTERRUPT_CNTL=0x%08X\n",
341 RREG32(mmINTERRUPT_CNTL));
342 dev_info(adev->dev, " INTERRUPT_CNTL2=0x%08X\n",
343 RREG32(mmINTERRUPT_CNTL2));
344 dev_info(adev->dev, " IH_CNTL=0x%08X\n",
345 RREG32(mmIH_CNTL));
346 dev_info(adev->dev, " IH_RB_CNTL=0x%08X\n",
347 RREG32(mmIH_RB_CNTL));
348 dev_info(adev->dev, " IH_RB_BASE=0x%08X\n",
349 RREG32(mmIH_RB_BASE));
350 dev_info(adev->dev, " IH_RB_WPTR_ADDR_LO=0x%08X\n",
351 RREG32(mmIH_RB_WPTR_ADDR_LO));
352 dev_info(adev->dev, " IH_RB_WPTR_ADDR_HI=0x%08X\n",
353 RREG32(mmIH_RB_WPTR_ADDR_HI));
354 dev_info(adev->dev, " IH_RB_RPTR=0x%08X\n",
355 RREG32(mmIH_RB_RPTR));
356 dev_info(adev->dev, " IH_RB_WPTR=0x%08X\n",
357 RREG32(mmIH_RB_WPTR));
358}
359
360static int cz_ih_soft_reset(struct amdgpu_device *adev)
361{
362 u32 srbm_soft_reset = 0;
363 u32 tmp = RREG32(mmSRBM_STATUS);
364
365 if (tmp & SRBM_STATUS__IH_BUSY_MASK)
366 srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
367 SOFT_RESET_IH, 1);
368
369 if (srbm_soft_reset) {
370 cz_ih_print_status(adev);
371
372 tmp = RREG32(mmSRBM_SOFT_RESET);
373 tmp |= srbm_soft_reset;
374 dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
375 WREG32(mmSRBM_SOFT_RESET, tmp);
376 tmp = RREG32(mmSRBM_SOFT_RESET);
377
378 udelay(50);
379
380 tmp &= ~srbm_soft_reset;
381 WREG32(mmSRBM_SOFT_RESET, tmp);
382 tmp = RREG32(mmSRBM_SOFT_RESET);
383
384 /* Wait a little for things to settle down */
385 udelay(50);
386
387 cz_ih_print_status(adev);
388 }
389
390 return 0;
391}
392
393static int cz_ih_set_clockgating_state(struct amdgpu_device *adev,
394 enum amdgpu_clockgating_state state)
395{
396 // TODO
397 return 0;
398}
399
400static int cz_ih_set_powergating_state(struct amdgpu_device *adev,
401 enum amdgpu_powergating_state state)
402{
403 // TODO
404 return 0;
405}
406
407const struct amdgpu_ip_funcs cz_ih_ip_funcs = {
408 .early_init = cz_ih_early_init,
409 .late_init = NULL,
410 .sw_init = cz_ih_sw_init,
411 .sw_fini = cz_ih_sw_fini,
412 .hw_init = cz_ih_hw_init,
413 .hw_fini = cz_ih_hw_fini,
414 .suspend = cz_ih_suspend,
415 .resume = cz_ih_resume,
416 .is_idle = cz_ih_is_idle,
417 .wait_for_idle = cz_ih_wait_for_idle,
418 .soft_reset = cz_ih_soft_reset,
419 .print_status = cz_ih_print_status,
420 .set_clockgating_state = cz_ih_set_clockgating_state,
421 .set_powergating_state = cz_ih_set_powergating_state,
422};
423
424static const struct amdgpu_ih_funcs cz_ih_funcs = {
425 .get_wptr = cz_ih_get_wptr,
426 .decode_iv = cz_ih_decode_iv,
427 .set_rptr = cz_ih_set_rptr
428};
429
430static void cz_ih_set_interrupt_funcs(struct amdgpu_device *adev)
431{
432 if (adev->irq.ih_funcs == NULL)
433 adev->irq.ih_funcs = &cz_ih_funcs;
434}
435