root/drivers/gpu/drm/amd/amdgpu/tonga_ih.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tonga_ih_enable_interrupts
  2. tonga_ih_disable_interrupts
  3. tonga_ih_irq_init
  4. tonga_ih_irq_disable
  5. tonga_ih_get_wptr
  6. tonga_ih_decode_iv
  7. tonga_ih_set_rptr
  8. tonga_ih_early_init
  9. tonga_ih_sw_init
  10. tonga_ih_sw_fini
  11. tonga_ih_hw_init
  12. tonga_ih_hw_fini
  13. tonga_ih_suspend
  14. tonga_ih_resume
  15. tonga_ih_is_idle
  16. tonga_ih_wait_for_idle
  17. tonga_ih_check_soft_reset
  18. tonga_ih_pre_soft_reset
  19. tonga_ih_post_soft_reset
  20. tonga_ih_soft_reset
  21. tonga_ih_set_clockgating_state
  22. tonga_ih_set_powergating_state
  23. tonga_ih_set_interrupt_funcs

   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 
  24 #include <linux/pci.h>
  25 
  26 #include "amdgpu.h"
  27 #include "amdgpu_ih.h"
  28 #include "vid.h"
  29 
  30 #include "oss/oss_3_0_d.h"
  31 #include "oss/oss_3_0_sh_mask.h"
  32 
  33 #include "bif/bif_5_1_d.h"
  34 #include "bif/bif_5_1_sh_mask.h"
  35 
  36 /*
  37  * Interrupts
  38  * Starting with r6xx, interrupts are handled via a ring buffer.
  39  * Ring buffers are areas of GPU accessible memory that the GPU
  40  * writes interrupt vectors into and the host reads vectors out of.
  41  * There is a rptr (read pointer) that determines where the
  42  * host is currently reading, and a wptr (write pointer)
  43  * which determines where the GPU has written.  When the
  44  * pointers are equal, the ring is idle.  When the GPU
  45  * writes vectors to the ring buffer, it increments the
  46  * wptr.  When there is an interrupt, the host then starts
  47  * fetching commands and processing them until the pointers are
  48  * equal again at which point it updates the rptr.
  49  */
  50 
  51 static void tonga_ih_set_interrupt_funcs(struct amdgpu_device *adev);
  52 
  53 /**
  54  * tonga_ih_enable_interrupts - Enable the interrupt ring buffer
  55  *
  56  * @adev: amdgpu_device pointer
  57  *
  58  * Enable the interrupt ring buffer (VI).
  59  */
  60 static void tonga_ih_enable_interrupts(struct amdgpu_device *adev)
  61 {
  62         u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
  63 
  64         ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 1);
  65         ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, ENABLE_INTR, 1);
  66         WREG32(mmIH_RB_CNTL, ih_rb_cntl);
  67         adev->irq.ih.enabled = true;
  68 }
  69 
  70 /**
  71  * tonga_ih_disable_interrupts - Disable the interrupt ring buffer
  72  *
  73  * @adev: amdgpu_device pointer
  74  *
  75  * Disable the interrupt ring buffer (VI).
  76  */
  77 static void tonga_ih_disable_interrupts(struct amdgpu_device *adev)
  78 {
  79         u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
  80 
  81         ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 0);
  82         ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, ENABLE_INTR, 0);
  83         WREG32(mmIH_RB_CNTL, ih_rb_cntl);
  84         /* set rptr, wptr to 0 */
  85         WREG32(mmIH_RB_RPTR, 0);
  86         WREG32(mmIH_RB_WPTR, 0);
  87         adev->irq.ih.enabled = false;
  88         adev->irq.ih.rptr = 0;
  89 }
  90 
  91 /**
  92  * tonga_ih_irq_init - init and enable the interrupt ring
  93  *
  94  * @adev: amdgpu_device pointer
  95  *
  96  * Allocate a ring buffer for the interrupt controller,
  97  * enable the RLC, disable interrupts, enable the IH
  98  * ring buffer and enable it (VI).
  99  * Called at device load and reume.
 100  * Returns 0 for success, errors for failure.
 101  */
 102 static int tonga_ih_irq_init(struct amdgpu_device *adev)
 103 {
 104         u32 interrupt_cntl, ih_rb_cntl, ih_doorbell_rtpr;
 105         struct amdgpu_ih_ring *ih = &adev->irq.ih;
 106         int rb_bufsz;
 107 
 108         /* disable irqs */
 109         tonga_ih_disable_interrupts(adev);
 110 
 111         /* setup interrupt control */
 112         WREG32(mmINTERRUPT_CNTL2, adev->dummy_page_addr >> 8);
 113         interrupt_cntl = RREG32(mmINTERRUPT_CNTL);
 114         /* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi
 115          * INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN
 116          */
 117         interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_DUMMY_RD_OVERRIDE, 0);
 118         /* INTERRUPT_CNTL__IH_REQ_NONSNOOP_EN_MASK=1 if ring is in non-cacheable memory, e.g., vram */
 119         interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_REQ_NONSNOOP_EN, 0);
 120         WREG32(mmINTERRUPT_CNTL, interrupt_cntl);
 121 
 122         /* Ring Buffer base. [39:8] of 40-bit address of the beginning of the ring buffer*/
 123         WREG32(mmIH_RB_BASE, ih->gpu_addr >> 8);
 124 
 125         rb_bufsz = order_base_2(adev->irq.ih.ring_size / 4);
 126         ih_rb_cntl = REG_SET_FIELD(0, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
 127         ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_SIZE, rb_bufsz);
 128         /* Ring Buffer write pointer writeback. If enabled, IH_RB_WPTR register value is written to memory */
 129         ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_WRITEBACK_ENABLE, 1);
 130         ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, MC_VMID, 0);
 131 
 132         if (adev->irq.msi_enabled)
 133                 ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RPTR_REARM, 1);
 134 
 135         WREG32(mmIH_RB_CNTL, ih_rb_cntl);
 136 
 137         /* set the writeback address whether it's enabled or not */
 138         WREG32(mmIH_RB_WPTR_ADDR_LO, lower_32_bits(ih->wptr_addr));
 139         WREG32(mmIH_RB_WPTR_ADDR_HI, upper_32_bits(ih->wptr_addr) & 0xFF);
 140 
 141         /* set rptr, wptr to 0 */
 142         WREG32(mmIH_RB_RPTR, 0);
 143         WREG32(mmIH_RB_WPTR, 0);
 144 
 145         ih_doorbell_rtpr = RREG32(mmIH_DOORBELL_RPTR);
 146         if (adev->irq.ih.use_doorbell) {
 147                 ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
 148                                                  OFFSET, adev->irq.ih.doorbell_index);
 149                 ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
 150                                                  ENABLE, 1);
 151         } else {
 152                 ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
 153                                                  ENABLE, 0);
 154         }
 155         WREG32(mmIH_DOORBELL_RPTR, ih_doorbell_rtpr);
 156 
 157         pci_set_master(adev->pdev);
 158 
 159         /* enable interrupts */
 160         tonga_ih_enable_interrupts(adev);
 161 
 162         return 0;
 163 }
 164 
 165 /**
 166  * tonga_ih_irq_disable - disable interrupts
 167  *
 168  * @adev: amdgpu_device pointer
 169  *
 170  * Disable interrupts on the hw (VI).
 171  */
 172 static void tonga_ih_irq_disable(struct amdgpu_device *adev)
 173 {
 174         tonga_ih_disable_interrupts(adev);
 175 
 176         /* Wait and acknowledge irq */
 177         mdelay(1);
 178 }
 179 
 180 /**
 181  * tonga_ih_get_wptr - get the IH ring buffer wptr
 182  *
 183  * @adev: amdgpu_device pointer
 184  *
 185  * Get the IH ring buffer wptr from either the register
 186  * or the writeback memory buffer (VI).  Also check for
 187  * ring buffer overflow and deal with it.
 188  * Used by cz_irq_process(VI).
 189  * Returns the value of the wptr.
 190  */
 191 static u32 tonga_ih_get_wptr(struct amdgpu_device *adev,
 192                              struct amdgpu_ih_ring *ih)
 193 {
 194         u32 wptr, tmp;
 195 
 196         wptr = le32_to_cpu(*ih->wptr_cpu);
 197 
 198         if (REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) {
 199                 wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0);
 200                 /* When a ring buffer overflow happen start parsing interrupt
 201                  * from the last not overwritten vector (wptr + 16). Hopefully
 202                  * this should allow us to catchup.
 203                  */
 204                 dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
 205                          wptr, ih->rptr, (wptr + 16) & ih->ptr_mask);
 206                 ih->rptr = (wptr + 16) & ih->ptr_mask;
 207                 tmp = RREG32(mmIH_RB_CNTL);
 208                 tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
 209                 WREG32(mmIH_RB_CNTL, tmp);
 210         }
 211         return (wptr & ih->ptr_mask);
 212 }
 213 
 214 /**
 215  * tonga_ih_decode_iv - decode an interrupt vector
 216  *
 217  * @adev: amdgpu_device pointer
 218  *
 219  * Decodes the interrupt vector at the current rptr
 220  * position and also advance the position.
 221  */
 222 static void tonga_ih_decode_iv(struct amdgpu_device *adev,
 223                                struct amdgpu_ih_ring *ih,
 224                                struct amdgpu_iv_entry *entry)
 225 {
 226         /* wptr/rptr are in bytes! */
 227         u32 ring_index = ih->rptr >> 2;
 228         uint32_t dw[4];
 229 
 230         dw[0] = le32_to_cpu(ih->ring[ring_index + 0]);
 231         dw[1] = le32_to_cpu(ih->ring[ring_index + 1]);
 232         dw[2] = le32_to_cpu(ih->ring[ring_index + 2]);
 233         dw[3] = le32_to_cpu(ih->ring[ring_index + 3]);
 234 
 235         entry->client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
 236         entry->src_id = dw[0] & 0xff;
 237         entry->src_data[0] = dw[1] & 0xfffffff;
 238         entry->ring_id = dw[2] & 0xff;
 239         entry->vmid = (dw[2] >> 8) & 0xff;
 240         entry->pasid = (dw[2] >> 16) & 0xffff;
 241 
 242         /* wptr/rptr are in bytes! */
 243         ih->rptr += 16;
 244 }
 245 
 246 /**
 247  * tonga_ih_set_rptr - set the IH ring buffer rptr
 248  *
 249  * @adev: amdgpu_device pointer
 250  *
 251  * Set the IH ring buffer rptr.
 252  */
 253 static void tonga_ih_set_rptr(struct amdgpu_device *adev,
 254                               struct amdgpu_ih_ring *ih)
 255 {
 256         if (ih->use_doorbell) {
 257                 /* XXX check if swapping is necessary on BE */
 258                 *ih->rptr_cpu = ih->rptr;
 259                 WDOORBELL32(ih->doorbell_index, ih->rptr);
 260         } else {
 261                 WREG32(mmIH_RB_RPTR, ih->rptr);
 262         }
 263 }
 264 
 265 static int tonga_ih_early_init(void *handle)
 266 {
 267         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 268         int ret;
 269 
 270         ret = amdgpu_irq_add_domain(adev);
 271         if (ret)
 272                 return ret;
 273 
 274         tonga_ih_set_interrupt_funcs(adev);
 275 
 276         return 0;
 277 }
 278 
 279 static int tonga_ih_sw_init(void *handle)
 280 {
 281         int r;
 282         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 283 
 284         r = amdgpu_ih_ring_init(adev, &adev->irq.ih, 64 * 1024, true);
 285         if (r)
 286                 return r;
 287 
 288         adev->irq.ih.use_doorbell = true;
 289         adev->irq.ih.doorbell_index = adev->doorbell_index.ih;
 290 
 291         r = amdgpu_irq_init(adev);
 292 
 293         return r;
 294 }
 295 
 296 static int tonga_ih_sw_fini(void *handle)
 297 {
 298         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 299 
 300         amdgpu_irq_fini(adev);
 301         amdgpu_ih_ring_fini(adev, &adev->irq.ih);
 302         amdgpu_irq_remove_domain(adev);
 303 
 304         return 0;
 305 }
 306 
 307 static int tonga_ih_hw_init(void *handle)
 308 {
 309         int r;
 310         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 311 
 312         r = tonga_ih_irq_init(adev);
 313         if (r)
 314                 return r;
 315 
 316         return 0;
 317 }
 318 
 319 static int tonga_ih_hw_fini(void *handle)
 320 {
 321         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 322 
 323         tonga_ih_irq_disable(adev);
 324 
 325         return 0;
 326 }
 327 
 328 static int tonga_ih_suspend(void *handle)
 329 {
 330         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 331 
 332         return tonga_ih_hw_fini(adev);
 333 }
 334 
 335 static int tonga_ih_resume(void *handle)
 336 {
 337         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 338 
 339         return tonga_ih_hw_init(adev);
 340 }
 341 
 342 static bool tonga_ih_is_idle(void *handle)
 343 {
 344         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 345         u32 tmp = RREG32(mmSRBM_STATUS);
 346 
 347         if (REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
 348                 return false;
 349 
 350         return true;
 351 }
 352 
 353 static int tonga_ih_wait_for_idle(void *handle)
 354 {
 355         unsigned i;
 356         u32 tmp;
 357         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 358 
 359         for (i = 0; i < adev->usec_timeout; i++) {
 360                 /* read MC_STATUS */
 361                 tmp = RREG32(mmSRBM_STATUS);
 362                 if (!REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
 363                         return 0;
 364                 udelay(1);
 365         }
 366         return -ETIMEDOUT;
 367 }
 368 
 369 static bool tonga_ih_check_soft_reset(void *handle)
 370 {
 371         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 372         u32 srbm_soft_reset = 0;
 373         u32 tmp = RREG32(mmSRBM_STATUS);
 374 
 375         if (tmp & SRBM_STATUS__IH_BUSY_MASK)
 376                 srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
 377                                                 SOFT_RESET_IH, 1);
 378 
 379         if (srbm_soft_reset) {
 380                 adev->irq.srbm_soft_reset = srbm_soft_reset;
 381                 return true;
 382         } else {
 383                 adev->irq.srbm_soft_reset = 0;
 384                 return false;
 385         }
 386 }
 387 
 388 static int tonga_ih_pre_soft_reset(void *handle)
 389 {
 390         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 391 
 392         if (!adev->irq.srbm_soft_reset)
 393                 return 0;
 394 
 395         return tonga_ih_hw_fini(adev);
 396 }
 397 
 398 static int tonga_ih_post_soft_reset(void *handle)
 399 {
 400         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 401 
 402         if (!adev->irq.srbm_soft_reset)
 403                 return 0;
 404 
 405         return tonga_ih_hw_init(adev);
 406 }
 407 
 408 static int tonga_ih_soft_reset(void *handle)
 409 {
 410         struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 411         u32 srbm_soft_reset;
 412 
 413         if (!adev->irq.srbm_soft_reset)
 414                 return 0;
 415         srbm_soft_reset = adev->irq.srbm_soft_reset;
 416 
 417         if (srbm_soft_reset) {
 418                 u32 tmp;
 419 
 420                 tmp = RREG32(mmSRBM_SOFT_RESET);
 421                 tmp |= srbm_soft_reset;
 422                 dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
 423                 WREG32(mmSRBM_SOFT_RESET, tmp);
 424                 tmp = RREG32(mmSRBM_SOFT_RESET);
 425 
 426                 udelay(50);
 427 
 428                 tmp &= ~srbm_soft_reset;
 429                 WREG32(mmSRBM_SOFT_RESET, tmp);
 430                 tmp = RREG32(mmSRBM_SOFT_RESET);
 431 
 432                 /* Wait a little for things to settle down */
 433                 udelay(50);
 434         }
 435 
 436         return 0;
 437 }
 438 
 439 static int tonga_ih_set_clockgating_state(void *handle,
 440                                           enum amd_clockgating_state state)
 441 {
 442         return 0;
 443 }
 444 
 445 static int tonga_ih_set_powergating_state(void *handle,
 446                                           enum amd_powergating_state state)
 447 {
 448         return 0;
 449 }
 450 
 451 static const struct amd_ip_funcs tonga_ih_ip_funcs = {
 452         .name = "tonga_ih",
 453         .early_init = tonga_ih_early_init,
 454         .late_init = NULL,
 455         .sw_init = tonga_ih_sw_init,
 456         .sw_fini = tonga_ih_sw_fini,
 457         .hw_init = tonga_ih_hw_init,
 458         .hw_fini = tonga_ih_hw_fini,
 459         .suspend = tonga_ih_suspend,
 460         .resume = tonga_ih_resume,
 461         .is_idle = tonga_ih_is_idle,
 462         .wait_for_idle = tonga_ih_wait_for_idle,
 463         .check_soft_reset = tonga_ih_check_soft_reset,
 464         .pre_soft_reset = tonga_ih_pre_soft_reset,
 465         .soft_reset = tonga_ih_soft_reset,
 466         .post_soft_reset = tonga_ih_post_soft_reset,
 467         .set_clockgating_state = tonga_ih_set_clockgating_state,
 468         .set_powergating_state = tonga_ih_set_powergating_state,
 469 };
 470 
 471 static const struct amdgpu_ih_funcs tonga_ih_funcs = {
 472         .get_wptr = tonga_ih_get_wptr,
 473         .decode_iv = tonga_ih_decode_iv,
 474         .set_rptr = tonga_ih_set_rptr
 475 };
 476 
 477 static void tonga_ih_set_interrupt_funcs(struct amdgpu_device *adev)
 478 {
 479         adev->irq.ih_funcs = &tonga_ih_funcs;
 480 }
 481 
 482 const struct amdgpu_ip_block_version tonga_ih_ip_block =
 483 {
 484         .type = AMD_IP_BLOCK_TYPE_IH,
 485         .major = 3,
 486         .minor = 0,
 487         .rev = 0,
 488         .funcs = &tonga_ih_ip_funcs,
 489 };

/* [<][>][^][v][top][bottom][index][help] */