/* * Copyright 2016 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. *
*/
/** * vega10_ih_toggle_ring_interrupts - toggle the interrupt ring buffer * * @adev: amdgpu_device pointer * @ih: amdgpu_ih_ring pointet * @enable: true - enable the interrupts, false - disable the interrupts * * Toggle the interrupt ring buffer (VEGA10)
*/ staticint vega10_ih_toggle_ring_interrupts(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih, bool enable)
{ struct amdgpu_ih_regs *ih_regs;
uint32_t tmp;
ih_regs = &ih->ih_regs;
tmp = RREG32(ih_regs->ih_rb_cntl);
tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, RB_ENABLE, (enable ? 1 : 0));
tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, RB_GPU_TS_ENABLE, 1); /* enable_intr field is only valid in ring0 */ if (ih == &adev->irq.ih)
tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, ENABLE_INTR, (enable ? 1 : 0)); if (amdgpu_sriov_vf(adev)) { if (psp_reg_program(&adev->psp, ih_regs->psp_reg_id, tmp)) {
dev_err(adev->dev, "PSP program IH_RB_CNTL failed!\n"); return -ETIMEDOUT;
}
} else {
WREG32(ih_regs->ih_rb_cntl, tmp);
}
if (enable) {
ih->enabled = true;
} else { /* set rptr, wptr to 0 */
WREG32(ih_regs->ih_rb_rptr, 0);
WREG32(ih_regs->ih_rb_wptr, 0);
ih->enabled = false;
ih->rptr = 0;
}
return 0;
}
/** * vega10_ih_toggle_interrupts - Toggle all the available interrupt ring buffers * * @adev: amdgpu_device pointer * @enable: enable or disable interrupt ring buffers * * Toggle all the available interrupt ring buffers (VEGA10).
*/ staticint vega10_ih_toggle_interrupts(struct amdgpu_device *adev, bool enable)
{ struct amdgpu_ih_ring *ih[] = {&adev->irq.ih, &adev->irq.ih1, &adev->irq.ih2}; int i; int r;
for (i = 0; i < ARRAY_SIZE(ih); i++) { if (ih[i]->ring_size) {
r = vega10_ih_toggle_ring_interrupts(adev, ih[i], enable); if (r) return r;
}
}
/** * vega10_ih_enable_ring - enable an ih ring buffer * * @adev: amdgpu_device pointer * @ih: amdgpu_ih_ring pointer * * Enable an ih ring buffer (VEGA10)
*/ staticint vega10_ih_enable_ring(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih)
{ struct amdgpu_ih_regs *ih_regs;
uint32_t tmp;
ih_regs = &ih->ih_regs;
/* Ring Buffer base. [39:8] of 40-bit address of the beginning of the ring buffer*/
WREG32(ih_regs->ih_rb_base, ih->gpu_addr >> 8);
WREG32(ih_regs->ih_rb_base_hi, (ih->gpu_addr >> 40) & 0xff);
tmp = RREG32(ih_regs->ih_rb_cntl);
tmp = vega10_ih_rb_cntl(ih, tmp); if (ih == &adev->irq.ih)
tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, RPTR_REARM, !!adev->irq.msi_enabled); if (ih == &adev->irq.ih1)
tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, RB_FULL_DRAIN_ENABLE, 1); if (amdgpu_sriov_vf(adev)) { if (psp_reg_program(&adev->psp, ih_regs->psp_reg_id, tmp)) {
dev_err(adev->dev, "PSP program IH_RB_CNTL failed!\n"); return -ETIMEDOUT;
}
} else {
WREG32(ih_regs->ih_rb_cntl, tmp);
}
if (ih == &adev->irq.ih) { /* set the ih ring 0 writeback address whether it's enabled or not */
WREG32(ih_regs->ih_rb_wptr_addr_lo, lower_32_bits(ih->wptr_addr));
WREG32(ih_regs->ih_rb_wptr_addr_hi, upper_32_bits(ih->wptr_addr) & 0xFFFF);
}
/* set rptr, wptr to 0 */
WREG32(ih_regs->ih_rb_wptr, 0);
WREG32(ih_regs->ih_rb_rptr, 0);
/** * vega10_ih_irq_init - init and enable the interrupt ring * * @adev: amdgpu_device pointer * * Allocate a ring buffer for the interrupt controller, * enable the RLC, disable interrupts, enable the IH * ring buffer and enable it (VI). * Called at device load and reume. * Returns 0 for success, errors for failure.
*/ staticint vega10_ih_irq_init(struct amdgpu_device *adev)
{ struct amdgpu_ih_ring *ih[] = {&adev->irq.ih, &adev->irq.ih1, &adev->irq.ih2};
u32 ih_chicken; int ret; int i;
/* disable irqs */
ret = vega10_ih_toggle_interrupts(adev, false); if (ret) return ret;
/** * vega10_ih_get_wptr - get the IH ring buffer wptr * * @adev: amdgpu_device pointer * @ih: IH ring buffer to fetch wptr * * Get the IH ring buffer wptr from either the register * or the writeback memory buffer (VEGA10). Also check for * ring buffer overflow and deal with it. * Returns the value of the wptr.
*/ static u32 vega10_ih_get_wptr(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih)
{
u32 wptr, tmp; struct amdgpu_ih_regs *ih_regs;
if (ih == &adev->irq.ih || ih == &adev->irq.ih_soft) { /* Only ring0 supports writeback. On other rings fall back * to register-based code with overflow checking below. * ih_soft ring doesn't have any backing hardware registers, * update wptr and return.
*/
wptr = le32_to_cpu(*ih->wptr_cpu);
if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) goto out;
}
ih_regs = &ih->ih_regs;
/* Double check that the overflow wasn't already cleared. */
wptr = RREG32_NO_KIQ(ih_regs->ih_rb_wptr); if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) goto out;
/* When a ring buffer overflow happen start parsing interrupt * from the last not overwritten vector (wptr + 32). Hopefully * this should allow us to catchup.
*/
tmp = (wptr + 32) & ih->ptr_mask;
dev_warn_ratelimited(adev->dev, "%s ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
amdgpu_ih_ring_name(adev, ih), wptr, ih->rptr, tmp);
ih->rptr = tmp;
/* Unset the CLEAR_OVERFLOW bit immediately so new overflows * can be detected.
*/
tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 0);
WREG32_NO_KIQ(ih_regs->ih_rb_cntl, tmp);
out: return (wptr & ih->ptr_mask);
}
/** * vega10_ih_irq_rearm - rearm IRQ if lost * * @adev: amdgpu_device pointer * @ih: IH ring to match *
*/ staticvoid vega10_ih_irq_rearm(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih)
{
uint32_t v = 0;
uint32_t i = 0; struct amdgpu_ih_regs *ih_regs;
ih_regs = &ih->ih_regs; /* Rearm IRQ / re-wwrite doorbell if doorbell write is lost */ for (i = 0; i < MAX_REARM_RETRY; i++) {
v = RREG32_NO_KIQ(ih_regs->ih_rb_rptr); if ((v < ih->ring_size) && (v != ih->rptr))
WDOORBELL32(ih->doorbell_index, ih->rptr); else break;
}
}
/** * vega10_ih_set_rptr - set the IH ring buffer rptr * * @adev: amdgpu_device pointer * @ih: IH ring buffer to set rptr * * Set the IH ring buffer rptr.
*/ staticvoid vega10_ih_set_rptr(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih)
{ struct amdgpu_ih_regs *ih_regs;
if (ih == &adev->irq.ih_soft) return;
if (ih->use_doorbell) { /* XXX check if swapping is necessary on BE */
*ih->rptr_cpu = ih->rptr;
WDOORBELL32(ih->doorbell_index, ih->rptr);
/** * vega10_ih_self_irq - dispatch work for ring 1 and 2 * * @adev: amdgpu_device pointer * @source: irq source * @entry: IV with WPTR update * * Update the WPTR from the IV and schedule work to handle the entries.
*/ staticint vega10_ih_self_irq(struct amdgpu_device *adev, struct amdgpu_irq_src *source, struct amdgpu_iv_entry *entry)
{ switch (entry->ring_id) { case 1:
schedule_work(&adev->irq.ih1_work); break; case 2:
schedule_work(&adev->irq.ih2_work); break; default: break;
} return 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.