aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/amd/amdgpu/tonga_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/tonga_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/tonga_ih.c')
-rw-r--r--drivers/gpu/drm/amd/amdgpu/tonga_ih.c458
1 files changed, 458 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
new file mode 100644
index 000000000000..cff1b8bce6a4
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
@@ -0,0 +1,458 @@
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_d.h"
29#include "oss/oss_3_0_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 tonga_ih_set_interrupt_funcs(struct amdgpu_device *adev);
50
51/**
52 * tonga_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 tonga_ih_enable_interrupts(struct amdgpu_device *adev)
59{
60 u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
61
62 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 1);
63 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, ENABLE_INTR, 1);
64 WREG32(mmIH_RB_CNTL, ih_rb_cntl);
65 adev->irq.ih.enabled = true;
66}
67
68/**
69 * tonga_ih_disable_interrupts - Disable the interrupt ring buffer
70 *
71 * @adev: amdgpu_device pointer
72 *
73 * Disable the interrupt ring buffer (VI).
74 */
75static void tonga_ih_disable_interrupts(struct amdgpu_device *adev)
76{
77 u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
78
79 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 0);
80 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, ENABLE_INTR, 0);
81 WREG32(mmIH_RB_CNTL, ih_rb_cntl);
82 /* set rptr, wptr to 0 */
83 WREG32(mmIH_RB_RPTR, 0);
84 WREG32(mmIH_RB_WPTR, 0);
85 adev->irq.ih.enabled = false;
86 adev->irq.ih.rptr = 0;
87}
88
89/**
90 * tonga_ih_irq_init - init and enable the interrupt ring
91 *
92 * @adev: amdgpu_device pointer
93 *
94 * Allocate a ring buffer for the interrupt controller,
95 * enable the RLC, disable interrupts, enable the IH
96 * ring buffer and enable it (VI).
97 * Called at device load and reume.
98 * Returns 0 for success, errors for failure.
99 */
100static int tonga_ih_irq_init(struct amdgpu_device *adev)
101{
102 int ret = 0;
103 int rb_bufsz;
104 u32 interrupt_cntl, ih_rb_cntl, ih_doorbell_rtpr;
105 u64 wptr_off;
106
107 /* disable irqs */
108 tonga_ih_disable_interrupts(adev);
109
110 /* setup interrupt control */
111 WREG32(mmINTERRUPT_CNTL2, adev->dummy_page.addr >> 8);
112 interrupt_cntl = RREG32(mmINTERRUPT_CNTL);
113 /* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi
114 * INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN
115 */
116 interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_DUMMY_RD_OVERRIDE, 0);
117 /* INTERRUPT_CNTL__IH_REQ_NONSNOOP_EN_MASK=1 if ring is in non-cacheable memory, e.g., vram */
118 interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_REQ_NONSNOOP_EN, 0);
119 WREG32(mmINTERRUPT_CNTL, interrupt_cntl);
120
121 /* Ring Buffer base. [39:8] of 40-bit address of the beginning of the ring buffer*/
122 if (adev->irq.ih.use_bus_addr)
123 WREG32(mmIH_RB_BASE, adev->irq.ih.rb_dma_addr >> 8);
124 else
125 WREG32(mmIH_RB_BASE, adev->irq.ih.gpu_addr >> 8);
126
127 rb_bufsz = order_base_2(adev->irq.ih.ring_size / 4);
128 ih_rb_cntl = REG_SET_FIELD(0, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
129 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_SIZE, rb_bufsz);
130 /* Ring Buffer write pointer writeback. If enabled, IH_RB_WPTR register value is written to memory */
131 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_WRITEBACK_ENABLE, 1);
132 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, MC_VMID, 0);
133
134 if (adev->irq.msi_enabled)
135 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RPTR_REARM, 1);
136
137 WREG32(mmIH_RB_CNTL, ih_rb_cntl);
138
139 /* set the writeback address whether it's enabled or not */
140 if (adev->irq.ih.use_bus_addr)
141 wptr_off = adev->irq.ih.rb_dma_addr + (adev->irq.ih.wptr_offs * 4);
142 else
143 wptr_off = adev->wb.gpu_addr + (adev->irq.ih.wptr_offs * 4);
144 WREG32(mmIH_RB_WPTR_ADDR_LO, lower_32_bits(wptr_off));
145 WREG32(mmIH_RB_WPTR_ADDR_HI, upper_32_bits(wptr_off) & 0xFF);
146
147 /* set rptr, wptr to 0 */
148 WREG32(mmIH_RB_RPTR, 0);
149 WREG32(mmIH_RB_WPTR, 0);
150
151 ih_doorbell_rtpr = RREG32(mmIH_DOORBELL_RPTR);
152 if (adev->irq.ih.use_doorbell) {
153 ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
154 OFFSET, adev->irq.ih.doorbell_index);
155 ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
156 ENABLE, 1);
157 } else {
158 ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
159 ENABLE, 0);
160 }
161 WREG32(mmIH_DOORBELL_RPTR, ih_doorbell_rtpr);
162
163 pci_set_master(adev->pdev);
164
165 /* enable interrupts */
166 tonga_ih_enable_interrupts(adev);
167
168 return ret;
169}
170
171/**
172 * tonga_ih_irq_disable - disable interrupts
173 *
174 * @adev: amdgpu_device pointer
175 *
176 * Disable interrupts on the hw (VI).
177 */
178static void tonga_ih_irq_disable(struct amdgpu_device *adev)
179{
180 tonga_ih_disable_interrupts(adev);
181
182 /* Wait and acknowledge irq */
183 mdelay(1);
184}
185
186/**
187 * tonga_ih_get_wptr - get the IH ring buffer wptr
188 *
189 * @adev: amdgpu_device pointer
190 *
191 * Get the IH ring buffer wptr from either the register
192 * or the writeback memory buffer (VI). Also check for
193 * ring buffer overflow and deal with it.
194 * Used by cz_irq_process(VI).
195 * Returns the value of the wptr.
196 */
197static u32 tonga_ih_get_wptr(struct amdgpu_device *adev)
198{
199 u32 wptr, tmp;
200
201 if (adev->irq.ih.use_bus_addr)
202 wptr = le32_to_cpu(adev->irq.ih.ring[adev->irq.ih.wptr_offs]);
203 else
204 wptr = le32_to_cpu(adev->wb.wb[adev->irq.ih.wptr_offs]);
205
206 if (REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) {
207 wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0);
208 /* When a ring buffer overflow happen start parsing interrupt
209 * from the last not overwritten vector (wptr + 16). Hopefully
210 * this should allow us to catchup.
211 */
212 dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
213 wptr, adev->irq.ih.rptr, (wptr + 16) & adev->irq.ih.ptr_mask);
214 adev->irq.ih.rptr = (wptr + 16) & adev->irq.ih.ptr_mask;
215 tmp = RREG32(mmIH_RB_CNTL);
216 tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
217 WREG32(mmIH_RB_CNTL, tmp);
218 }
219 return (wptr & adev->irq.ih.ptr_mask);
220}
221
222/**
223 * tonga_ih_decode_iv - decode an interrupt vector
224 *
225 * @adev: amdgpu_device pointer
226 *
227 * Decodes the interrupt vector at the current rptr
228 * position and also advance the position.
229 */
230static void tonga_ih_decode_iv(struct amdgpu_device *adev,
231 struct amdgpu_iv_entry *entry)
232{
233 /* wptr/rptr are in bytes! */
234 u32 ring_index = adev->irq.ih.rptr >> 2;
235 uint32_t dw[4];
236
237 dw[0] = le32_to_cpu(adev->irq.ih.ring[ring_index + 0]);
238 dw[1] = le32_to_cpu(adev->irq.ih.ring[ring_index + 1]);
239 dw[2] = le32_to_cpu(adev->irq.ih.ring[ring_index + 2]);
240 dw[3] = le32_to_cpu(adev->irq.ih.ring[ring_index + 3]);
241
242 entry->src_id = dw[0] & 0xff;
243 entry->src_data = dw[1] & 0xfffffff;
244 entry->ring_id = dw[2] & 0xff;
245 entry->vm_id = (dw[2] >> 8) & 0xff;
246 entry->pas_id = (dw[2] >> 16) & 0xffff;
247
248 /* wptr/rptr are in bytes! */
249 adev->irq.ih.rptr += 16;
250}
251
252/**
253 * tonga_ih_set_rptr - set the IH ring buffer rptr
254 *
255 * @adev: amdgpu_device pointer
256 *
257 * Set the IH ring buffer rptr.
258 */
259static void tonga_ih_set_rptr(struct amdgpu_device *adev)
260{
261 if (adev->irq.ih.use_doorbell) {
262 /* XXX check if swapping is necessary on BE */
263 if (adev->irq.ih.use_bus_addr)
264 adev->irq.ih.ring[adev->irq.ih.rptr_offs] = adev->irq.ih.rptr;
265 else
266 adev->wb.wb[adev->irq.ih.rptr_offs] = adev->irq.ih.rptr;
267 WDOORBELL32(adev->irq.ih.doorbell_index, adev->irq.ih.rptr);
268 } else {
269 WREG32(mmIH_RB_RPTR, adev->irq.ih.rptr);
270 }
271}
272
273static int tonga_ih_early_init(struct amdgpu_device *adev)
274{
275 tonga_ih_set_interrupt_funcs(adev);
276 return 0;
277}
278
279static int tonga_ih_sw_init(struct amdgpu_device *adev)
280{
281 int r;
282
283 r = amdgpu_ih_ring_init(adev, 4 * 1024, true);
284 if (r)
285 return r;
286
287 adev->irq.ih.use_doorbell = true;
288 adev->irq.ih.doorbell_index = AMDGPU_DOORBELL_IH;
289
290 r = amdgpu_irq_init(adev);
291
292 return r;
293}
294
295static int tonga_ih_sw_fini(struct amdgpu_device *adev)
296{
297 amdgpu_irq_fini(adev);
298 amdgpu_ih_ring_fini(adev);
299
300 return 0;
301}
302
303static int tonga_ih_hw_init(struct amdgpu_device *adev)
304{
305 int r;
306
307 r = tonga_ih_irq_init(adev);
308 if (r)
309 return r;
310
311 return 0;
312}
313
314static int tonga_ih_hw_fini(struct amdgpu_device *adev)
315{
316 tonga_ih_irq_disable(adev);
317
318 return 0;
319}
320
321static int tonga_ih_suspend(struct amdgpu_device *adev)
322{
323 return tonga_ih_hw_fini(adev);
324}
325
326static int tonga_ih_resume(struct amdgpu_device *adev)
327{
328 return tonga_ih_hw_init(adev);
329}
330
331static bool tonga_ih_is_idle(struct amdgpu_device *adev)
332{
333 u32 tmp = RREG32(mmSRBM_STATUS);
334
335 if (REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
336 return false;
337
338 return true;
339}
340
341static int tonga_ih_wait_for_idle(struct amdgpu_device *adev)
342{
343 unsigned i;
344 u32 tmp;
345
346 for (i = 0; i < adev->usec_timeout; i++) {
347 /* read MC_STATUS */
348 tmp = RREG32(mmSRBM_STATUS);
349 if (!REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
350 return 0;
351 udelay(1);
352 }
353 return -ETIMEDOUT;
354}
355
356static void tonga_ih_print_status(struct amdgpu_device *adev)
357{
358 dev_info(adev->dev, "TONGA IH registers\n");
359 dev_info(adev->dev, " SRBM_STATUS=0x%08X\n",
360 RREG32(mmSRBM_STATUS));
361 dev_info(adev->dev, " SRBM_STATUS2=0x%08X\n",
362 RREG32(mmSRBM_STATUS2));
363 dev_info(adev->dev, " INTERRUPT_CNTL=0x%08X\n",
364 RREG32(mmINTERRUPT_CNTL));
365 dev_info(adev->dev, " INTERRUPT_CNTL2=0x%08X\n",
366 RREG32(mmINTERRUPT_CNTL2));
367 dev_info(adev->dev, " IH_CNTL=0x%08X\n",
368 RREG32(mmIH_CNTL));
369 dev_info(adev->dev, " IH_RB_CNTL=0x%08X\n",
370 RREG32(mmIH_RB_CNTL));
371 dev_info(adev->dev, " IH_RB_BASE=0x%08X\n",
372 RREG32(mmIH_RB_BASE));
373 dev_info(adev->dev, " IH_RB_WPTR_ADDR_LO=0x%08X\n",
374 RREG32(mmIH_RB_WPTR_ADDR_LO));
375 dev_info(adev->dev, " IH_RB_WPTR_ADDR_HI=0x%08X\n",
376 RREG32(mmIH_RB_WPTR_ADDR_HI));
377 dev_info(adev->dev, " IH_RB_RPTR=0x%08X\n",
378 RREG32(mmIH_RB_RPTR));
379 dev_info(adev->dev, " IH_RB_WPTR=0x%08X\n",
380 RREG32(mmIH_RB_WPTR));
381}
382
383static int tonga_ih_soft_reset(struct amdgpu_device *adev)
384{
385 u32 srbm_soft_reset = 0;
386 u32 tmp = RREG32(mmSRBM_STATUS);
387
388 if (tmp & SRBM_STATUS__IH_BUSY_MASK)
389 srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
390 SOFT_RESET_IH, 1);
391
392 if (srbm_soft_reset) {
393 tonga_ih_print_status(adev);
394
395 tmp = RREG32(mmSRBM_SOFT_RESET);
396 tmp |= srbm_soft_reset;
397 dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
398 WREG32(mmSRBM_SOFT_RESET, tmp);
399 tmp = RREG32(mmSRBM_SOFT_RESET);
400
401 udelay(50);
402
403 tmp &= ~srbm_soft_reset;
404 WREG32(mmSRBM_SOFT_RESET, tmp);
405 tmp = RREG32(mmSRBM_SOFT_RESET);
406
407 /* Wait a little for things to settle down */
408 udelay(50);
409
410 tonga_ih_print_status(adev);
411 }
412
413 return 0;
414}
415
416static int tonga_ih_set_clockgating_state(struct amdgpu_device *adev,
417 enum amdgpu_clockgating_state state)
418{
419 // TODO
420 return 0;
421}
422
423static int tonga_ih_set_powergating_state(struct amdgpu_device *adev,
424 enum amdgpu_powergating_state state)
425{
426 // TODO
427 return 0;
428}
429
430const struct amdgpu_ip_funcs tonga_ih_ip_funcs = {
431 .early_init = tonga_ih_early_init,
432 .late_init = NULL,
433 .sw_init = tonga_ih_sw_init,
434 .sw_fini = tonga_ih_sw_fini,
435 .hw_init = tonga_ih_hw_init,
436 .hw_fini = tonga_ih_hw_fini,
437 .suspend = tonga_ih_suspend,
438 .resume = tonga_ih_resume,
439 .is_idle = tonga_ih_is_idle,
440 .wait_for_idle = tonga_ih_wait_for_idle,
441 .soft_reset = tonga_ih_soft_reset,
442 .print_status = tonga_ih_print_status,
443 .set_clockgating_state = tonga_ih_set_clockgating_state,
444 .set_powergating_state = tonga_ih_set_powergating_state,
445};
446
447static const struct amdgpu_ih_funcs tonga_ih_funcs = {
448 .get_wptr = tonga_ih_get_wptr,
449 .decode_iv = tonga_ih_decode_iv,
450 .set_rptr = tonga_ih_set_rptr
451};
452
453static void tonga_ih_set_interrupt_funcs(struct amdgpu_device *adev)
454{
455 if (adev->irq.ih_funcs == NULL)
456 adev->irq.ih_funcs = &tonga_ih_funcs;
457}
458