/* * Copyright 2023 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. *
*/
if (amdgpu_sriov_vf(adev) && amdgpu_sriov_reg_indirect_ih(adev)) { if (psp_reg_program(&adev->psp, PSP_REG_IH_RB_CNTL_RING1, ih_rb_cntl)) return;
} else {
WREG32_SOC15(OSSSYS, 0, regIH_RB_CNTL_RING1, ih_rb_cntl);
}
WREG32_SOC15(OSSSYS, 0, regIH_CNTL2, ih_cntl);
}
/** * ih_v7_0_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 (IH_V7_0)
*/ staticint ih_v7_0_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)); /* 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) && amdgpu_sriov_reg_indirect_ih(adev)) { if (psp_reg_program(&adev->psp, ih_regs->psp_reg_id, tmp)) 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;
}
/** * ih_v7_0_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 (IH_V7_0).
*/ staticint ih_v7_0_toggle_interrupts(struct amdgpu_device *adev, bool enable)
{ struct amdgpu_ih_ring *ih[] = {&adev->irq.ih, &adev->irq.ih1}; int i; int r;
for (i = 0; i < ARRAY_SIZE(ih); i++) { if (ih[i]->ring_size) {
r = ih_v7_0_toggle_ring_interrupts(adev, ih[i], enable); if (r) return r;
}
}
/** * ih_v7_0_enable_ring - enable an ih ring buffer * * @adev: amdgpu_device pointer * @ih: amdgpu_ih_ring pointer * * Enable an ih ring buffer (IH_V7_0)
*/ staticint ih_v7_0_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);
if (amdgpu_sriov_vf(adev) && amdgpu_sriov_reg_indirect_ih(adev)) { if (psp_reg_program(&adev->psp, ih_regs->psp_reg_id, tmp)) {
DRM_ERROR("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);
/** * ih_v7_0_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. * Called at device load and reume. * Returns 0 for success, errors for failure.
*/ staticint ih_v7_0_irq_init(struct amdgpu_device *adev)
{ struct amdgpu_ih_ring *ih[] = {&adev->irq.ih, &adev->irq.ih1};
u32 ih_chicken;
u32 tmp; int ret; int i;
/* disable irqs */
ret = ih_v7_0_toggle_interrupts(adev, false); if (ret) return ret;
/* GC/MMHUB UTCL2 page fault interrupts are configured as * MSI storm capable interrupts by deafult. The delay is * used to avoid ISR being called too frequently * when page fault happens on several continuous page
* and thus avoid MSI storm */
tmp = RREG32_SOC15(OSSSYS, 0, regIH_MSI_STORM_CTRL);
tmp = REG_SET_FIELD(tmp, IH_MSI_STORM_CTRL,
DELAY, 3);
WREG32_SOC15(OSSSYS, 0, regIH_MSI_STORM_CTRL, tmp);
/* Redirect the interrupts to IH RB1 for dGPU */ if (adev->irq.ih1.ring_size) {
tmp = RREG32_SOC15(OSSSYS, 0, regIH_RING1_CLIENT_CFG_INDEX);
tmp = REG_SET_FIELD(tmp, IH_RING1_CLIENT_CFG_INDEX, INDEX, 0);
WREG32_SOC15(OSSSYS, 0, regIH_RING1_CLIENT_CFG_INDEX, tmp);
/* enable interrupts */
ret = ih_v7_0_toggle_interrupts(adev, true); if (ret) return ret; /* enable wptr force update for self int */
force_update_wptr_for_self_int(adev, 0, 8, true);
if (adev->irq.ih_soft.ring_size)
adev->irq.ih_soft.enabled = true;
/** * ih_v7_0_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. Also check for * ring buffer overflow and deal with it. * Returns the value of the wptr.
*/ static u32 ih_v7_0_get_wptr(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih)
{
u32 wptr, tmp; struct amdgpu_ih_regs *ih_regs;
/* When a ring buffer overflow happen start parsing interrupt * from the last not overwritten vector (wptr + 32). Hopefully * this should allow us to catch up.
*/
tmp = (wptr + 32) & ih->ptr_mask;
dev_warn(adev->dev, "IH ring buffer overflow " "(0x%08X, 0x%08X, 0x%08X)\n",
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);
}
/** * ih_v7_0_irq_rearm - rearm IRQ if lost * * @adev: amdgpu_device pointer * @ih: IH ring to match *
*/ staticvoid ih_v7_0_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-write 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;
}
}
/** * ih_v7_0_set_rptr - set the IH ring buffer rptr * * @adev: amdgpu_device pointer * @ih: IH ring buffer to set rptr
*/ staticvoid ih_v7_0_set_rptr(struct amdgpu_device *adev, struct amdgpu_ih_ring *ih)
{ struct amdgpu_ih_regs *ih_regs;
if (ih->use_doorbell) { /* XXX check if swapping is necessary on BE */
*ih->rptr_cpu = ih->rptr;
WDOORBELL32(ih->doorbell_index, ih->rptr);
/** * ih_v7_0_self_irq - dispatch work for ring 1 * * @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 ih_v7_0_self_irq(struct amdgpu_device *adev, struct amdgpu_irq_src *source, struct amdgpu_iv_entry *entry)
{
uint32_t wptr = cpu_to_le32(entry->src_data[0]);
r = amdgpu_irq_add_id(adev, SOC21_IH_CLIENTID_IH, 0,
&adev->irq.self_irq);
if (r) return r;
/* use gpu virtual address for ih ring * until ih_checken is programmed to allow
* use bus address for ih ring by psp bl */
use_bus_addr =
(adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) ? false : true;
r = amdgpu_ih_ring_init(adev, &adev->irq.ih, 256 * 1024, use_bus_addr); if (r) return r;
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.