18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2014 Advanced Micro Devices, Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/pci.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "amdgpu.h"
278c2ecf20Sopenharmony_ci#include "amdgpu_ih.h"
288c2ecf20Sopenharmony_ci#include "vid.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "oss/oss_3_0_d.h"
318c2ecf20Sopenharmony_ci#include "oss/oss_3_0_sh_mask.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "bif/bif_5_1_d.h"
348c2ecf20Sopenharmony_ci#include "bif/bif_5_1_sh_mask.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * Interrupts
388c2ecf20Sopenharmony_ci * Starting with r6xx, interrupts are handled via a ring buffer.
398c2ecf20Sopenharmony_ci * Ring buffers are areas of GPU accessible memory that the GPU
408c2ecf20Sopenharmony_ci * writes interrupt vectors into and the host reads vectors out of.
418c2ecf20Sopenharmony_ci * There is a rptr (read pointer) that determines where the
428c2ecf20Sopenharmony_ci * host is currently reading, and a wptr (write pointer)
438c2ecf20Sopenharmony_ci * which determines where the GPU has written.  When the
448c2ecf20Sopenharmony_ci * pointers are equal, the ring is idle.  When the GPU
458c2ecf20Sopenharmony_ci * writes vectors to the ring buffer, it increments the
468c2ecf20Sopenharmony_ci * wptr.  When there is an interrupt, the host then starts
478c2ecf20Sopenharmony_ci * fetching commands and processing them until the pointers are
488c2ecf20Sopenharmony_ci * equal again at which point it updates the rptr.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void tonga_ih_set_interrupt_funcs(struct amdgpu_device *adev);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/**
548c2ecf20Sopenharmony_ci * tonga_ih_enable_interrupts - Enable the interrupt ring buffer
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
578c2ecf20Sopenharmony_ci *
588c2ecf20Sopenharmony_ci * Enable the interrupt ring buffer (VI).
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_cistatic void tonga_ih_enable_interrupts(struct amdgpu_device *adev)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 1);
658c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, ENABLE_INTR, 1);
668c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_CNTL, ih_rb_cntl);
678c2ecf20Sopenharmony_ci	adev->irq.ih.enabled = true;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/**
718c2ecf20Sopenharmony_ci * tonga_ih_disable_interrupts - Disable the interrupt ring buffer
728c2ecf20Sopenharmony_ci *
738c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
748c2ecf20Sopenharmony_ci *
758c2ecf20Sopenharmony_ci * Disable the interrupt ring buffer (VI).
768c2ecf20Sopenharmony_ci */
778c2ecf20Sopenharmony_cistatic void tonga_ih_disable_interrupts(struct amdgpu_device *adev)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 0);
828c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, ENABLE_INTR, 0);
838c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_CNTL, ih_rb_cntl);
848c2ecf20Sopenharmony_ci	/* set rptr, wptr to 0 */
858c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_RPTR, 0);
868c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_WPTR, 0);
878c2ecf20Sopenharmony_ci	adev->irq.ih.enabled = false;
888c2ecf20Sopenharmony_ci	adev->irq.ih.rptr = 0;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/**
928c2ecf20Sopenharmony_ci * tonga_ih_irq_init - init and enable the interrupt ring
938c2ecf20Sopenharmony_ci *
948c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
958c2ecf20Sopenharmony_ci *
968c2ecf20Sopenharmony_ci * Allocate a ring buffer for the interrupt controller,
978c2ecf20Sopenharmony_ci * enable the RLC, disable interrupts, enable the IH
988c2ecf20Sopenharmony_ci * ring buffer and enable it (VI).
998c2ecf20Sopenharmony_ci * Called at device load and reume.
1008c2ecf20Sopenharmony_ci * Returns 0 for success, errors for failure.
1018c2ecf20Sopenharmony_ci */
1028c2ecf20Sopenharmony_cistatic int tonga_ih_irq_init(struct amdgpu_device *adev)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	u32 interrupt_cntl, ih_rb_cntl, ih_doorbell_rtpr;
1058c2ecf20Sopenharmony_ci	struct amdgpu_ih_ring *ih = &adev->irq.ih;
1068c2ecf20Sopenharmony_ci	int rb_bufsz;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* disable irqs */
1098c2ecf20Sopenharmony_ci	tonga_ih_disable_interrupts(adev);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* setup interrupt control */
1128c2ecf20Sopenharmony_ci	WREG32(mmINTERRUPT_CNTL2, adev->dummy_page_addr >> 8);
1138c2ecf20Sopenharmony_ci	interrupt_cntl = RREG32(mmINTERRUPT_CNTL);
1148c2ecf20Sopenharmony_ci	/* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi
1158c2ecf20Sopenharmony_ci	 * INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci	interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_DUMMY_RD_OVERRIDE, 0);
1188c2ecf20Sopenharmony_ci	/* INTERRUPT_CNTL__IH_REQ_NONSNOOP_EN_MASK=1 if ring is in non-cacheable memory, e.g., vram */
1198c2ecf20Sopenharmony_ci	interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_REQ_NONSNOOP_EN, 0);
1208c2ecf20Sopenharmony_ci	WREG32(mmINTERRUPT_CNTL, interrupt_cntl);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* Ring Buffer base. [39:8] of 40-bit address of the beginning of the ring buffer*/
1238c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_BASE, ih->gpu_addr >> 8);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	rb_bufsz = order_base_2(adev->irq.ih.ring_size / 4);
1268c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(0, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
1278c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_SIZE, rb_bufsz);
1288c2ecf20Sopenharmony_ci	/* Ring Buffer write pointer writeback. If enabled, IH_RB_WPTR register value is written to memory */
1298c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_WRITEBACK_ENABLE, 1);
1308c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, MC_VMID, 0);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	if (adev->irq.msi_enabled)
1338c2ecf20Sopenharmony_ci		ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RPTR_REARM, 1);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_CNTL, ih_rb_cntl);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* set the writeback address whether it's enabled or not */
1388c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_WPTR_ADDR_LO, lower_32_bits(ih->wptr_addr));
1398c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_WPTR_ADDR_HI, upper_32_bits(ih->wptr_addr) & 0xFF);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* set rptr, wptr to 0 */
1428c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_RPTR, 0);
1438c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_WPTR, 0);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	ih_doorbell_rtpr = RREG32(mmIH_DOORBELL_RPTR);
1468c2ecf20Sopenharmony_ci	if (adev->irq.ih.use_doorbell) {
1478c2ecf20Sopenharmony_ci		ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
1488c2ecf20Sopenharmony_ci						 OFFSET, adev->irq.ih.doorbell_index);
1498c2ecf20Sopenharmony_ci		ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
1508c2ecf20Sopenharmony_ci						 ENABLE, 1);
1518c2ecf20Sopenharmony_ci	} else {
1528c2ecf20Sopenharmony_ci		ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
1538c2ecf20Sopenharmony_ci						 ENABLE, 0);
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci	WREG32(mmIH_DOORBELL_RPTR, ih_doorbell_rtpr);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	pci_set_master(adev->pdev);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* enable interrupts */
1608c2ecf20Sopenharmony_ci	tonga_ih_enable_interrupts(adev);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/**
1668c2ecf20Sopenharmony_ci * tonga_ih_irq_disable - disable interrupts
1678c2ecf20Sopenharmony_ci *
1688c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
1698c2ecf20Sopenharmony_ci *
1708c2ecf20Sopenharmony_ci * Disable interrupts on the hw (VI).
1718c2ecf20Sopenharmony_ci */
1728c2ecf20Sopenharmony_cistatic void tonga_ih_irq_disable(struct amdgpu_device *adev)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	tonga_ih_disable_interrupts(adev);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* Wait and acknowledge irq */
1778c2ecf20Sopenharmony_ci	mdelay(1);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/**
1818c2ecf20Sopenharmony_ci * tonga_ih_get_wptr - get the IH ring buffer wptr
1828c2ecf20Sopenharmony_ci *
1838c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
1848c2ecf20Sopenharmony_ci *
1858c2ecf20Sopenharmony_ci * Get the IH ring buffer wptr from either the register
1868c2ecf20Sopenharmony_ci * or the writeback memory buffer (VI).  Also check for
1878c2ecf20Sopenharmony_ci * ring buffer overflow and deal with it.
1888c2ecf20Sopenharmony_ci * Used by cz_irq_process(VI).
1898c2ecf20Sopenharmony_ci * Returns the value of the wptr.
1908c2ecf20Sopenharmony_ci */
1918c2ecf20Sopenharmony_cistatic u32 tonga_ih_get_wptr(struct amdgpu_device *adev,
1928c2ecf20Sopenharmony_ci			     struct amdgpu_ih_ring *ih)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	u32 wptr, tmp;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	wptr = le32_to_cpu(*ih->wptr_cpu);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW))
1998c2ecf20Sopenharmony_ci		goto out;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* Double check that the overflow wasn't already cleared. */
2028c2ecf20Sopenharmony_ci	wptr = RREG32(mmIH_RB_WPTR);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW))
2058c2ecf20Sopenharmony_ci		goto out;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* When a ring buffer overflow happen start parsing interrupt
2108c2ecf20Sopenharmony_ci	 * from the last not overwritten vector (wptr + 16). Hopefully
2118c2ecf20Sopenharmony_ci	 * this should allow us to catchup.
2128c2ecf20Sopenharmony_ci	 */
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
2158c2ecf20Sopenharmony_ci		wptr, ih->rptr, (wptr + 16) & ih->ptr_mask);
2168c2ecf20Sopenharmony_ci	ih->rptr = (wptr + 16) & ih->ptr_mask;
2178c2ecf20Sopenharmony_ci	tmp = RREG32(mmIH_RB_CNTL);
2188c2ecf20Sopenharmony_ci	tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
2198c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_CNTL, tmp);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ciout:
2228c2ecf20Sopenharmony_ci	return (wptr & ih->ptr_mask);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/**
2268c2ecf20Sopenharmony_ci * tonga_ih_decode_iv - decode an interrupt vector
2278c2ecf20Sopenharmony_ci *
2288c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
2298c2ecf20Sopenharmony_ci *
2308c2ecf20Sopenharmony_ci * Decodes the interrupt vector at the current rptr
2318c2ecf20Sopenharmony_ci * position and also advance the position.
2328c2ecf20Sopenharmony_ci */
2338c2ecf20Sopenharmony_cistatic void tonga_ih_decode_iv(struct amdgpu_device *adev,
2348c2ecf20Sopenharmony_ci			       struct amdgpu_ih_ring *ih,
2358c2ecf20Sopenharmony_ci			       struct amdgpu_iv_entry *entry)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	/* wptr/rptr are in bytes! */
2388c2ecf20Sopenharmony_ci	u32 ring_index = ih->rptr >> 2;
2398c2ecf20Sopenharmony_ci	uint32_t dw[4];
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	dw[0] = le32_to_cpu(ih->ring[ring_index + 0]);
2428c2ecf20Sopenharmony_ci	dw[1] = le32_to_cpu(ih->ring[ring_index + 1]);
2438c2ecf20Sopenharmony_ci	dw[2] = le32_to_cpu(ih->ring[ring_index + 2]);
2448c2ecf20Sopenharmony_ci	dw[3] = le32_to_cpu(ih->ring[ring_index + 3]);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	entry->client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
2478c2ecf20Sopenharmony_ci	entry->src_id = dw[0] & 0xff;
2488c2ecf20Sopenharmony_ci	entry->src_data[0] = dw[1] & 0xfffffff;
2498c2ecf20Sopenharmony_ci	entry->ring_id = dw[2] & 0xff;
2508c2ecf20Sopenharmony_ci	entry->vmid = (dw[2] >> 8) & 0xff;
2518c2ecf20Sopenharmony_ci	entry->pasid = (dw[2] >> 16) & 0xffff;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/* wptr/rptr are in bytes! */
2548c2ecf20Sopenharmony_ci	ih->rptr += 16;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/**
2588c2ecf20Sopenharmony_ci * tonga_ih_set_rptr - set the IH ring buffer rptr
2598c2ecf20Sopenharmony_ci *
2608c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
2618c2ecf20Sopenharmony_ci *
2628c2ecf20Sopenharmony_ci * Set the IH ring buffer rptr.
2638c2ecf20Sopenharmony_ci */
2648c2ecf20Sopenharmony_cistatic void tonga_ih_set_rptr(struct amdgpu_device *adev,
2658c2ecf20Sopenharmony_ci			      struct amdgpu_ih_ring *ih)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	if (ih->use_doorbell) {
2688c2ecf20Sopenharmony_ci		/* XXX check if swapping is necessary on BE */
2698c2ecf20Sopenharmony_ci		*ih->rptr_cpu = ih->rptr;
2708c2ecf20Sopenharmony_ci		WDOORBELL32(ih->doorbell_index, ih->rptr);
2718c2ecf20Sopenharmony_ci	} else {
2728c2ecf20Sopenharmony_ci		WREG32(mmIH_RB_RPTR, ih->rptr);
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int tonga_ih_early_init(void *handle)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
2798c2ecf20Sopenharmony_ci	int ret;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	ret = amdgpu_irq_add_domain(adev);
2828c2ecf20Sopenharmony_ci	if (ret)
2838c2ecf20Sopenharmony_ci		return ret;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	tonga_ih_set_interrupt_funcs(adev);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return 0;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int tonga_ih_sw_init(void *handle)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	int r;
2938c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	r = amdgpu_ih_ring_init(adev, &adev->irq.ih, 64 * 1024, true);
2968c2ecf20Sopenharmony_ci	if (r)
2978c2ecf20Sopenharmony_ci		return r;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	adev->irq.ih.use_doorbell = true;
3008c2ecf20Sopenharmony_ci	adev->irq.ih.doorbell_index = adev->doorbell_index.ih;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	r = amdgpu_irq_init(adev);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return r;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int tonga_ih_sw_fini(void *handle)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	amdgpu_irq_fini(adev);
3128c2ecf20Sopenharmony_ci	amdgpu_ih_ring_fini(adev, &adev->irq.ih);
3138c2ecf20Sopenharmony_ci	amdgpu_irq_remove_domain(adev);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int tonga_ih_hw_init(void *handle)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	int r;
3218c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	r = tonga_ih_irq_init(adev);
3248c2ecf20Sopenharmony_ci	if (r)
3258c2ecf20Sopenharmony_ci		return r;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return 0;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int tonga_ih_hw_fini(void *handle)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	tonga_ih_irq_disable(adev);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return 0;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic int tonga_ih_suspend(void *handle)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return tonga_ih_hw_fini(adev);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int tonga_ih_resume(void *handle)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return tonga_ih_hw_init(adev);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic bool tonga_ih_is_idle(void *handle)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3568c2ecf20Sopenharmony_ci	u32 tmp = RREG32(mmSRBM_STATUS);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
3598c2ecf20Sopenharmony_ci		return false;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	return true;
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic int tonga_ih_wait_for_idle(void *handle)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	unsigned i;
3678c2ecf20Sopenharmony_ci	u32 tmp;
3688c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	for (i = 0; i < adev->usec_timeout; i++) {
3718c2ecf20Sopenharmony_ci		/* read MC_STATUS */
3728c2ecf20Sopenharmony_ci		tmp = RREG32(mmSRBM_STATUS);
3738c2ecf20Sopenharmony_ci		if (!REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
3748c2ecf20Sopenharmony_ci			return 0;
3758c2ecf20Sopenharmony_ci		udelay(1);
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic bool tonga_ih_check_soft_reset(void *handle)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3838c2ecf20Sopenharmony_ci	u32 srbm_soft_reset = 0;
3848c2ecf20Sopenharmony_ci	u32 tmp = RREG32(mmSRBM_STATUS);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	if (tmp & SRBM_STATUS__IH_BUSY_MASK)
3878c2ecf20Sopenharmony_ci		srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
3888c2ecf20Sopenharmony_ci						SOFT_RESET_IH, 1);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (srbm_soft_reset) {
3918c2ecf20Sopenharmony_ci		adev->irq.srbm_soft_reset = srbm_soft_reset;
3928c2ecf20Sopenharmony_ci		return true;
3938c2ecf20Sopenharmony_ci	} else {
3948c2ecf20Sopenharmony_ci		adev->irq.srbm_soft_reset = 0;
3958c2ecf20Sopenharmony_ci		return false;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic int tonga_ih_pre_soft_reset(void *handle)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if (!adev->irq.srbm_soft_reset)
4048c2ecf20Sopenharmony_ci		return 0;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	return tonga_ih_hw_fini(adev);
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic int tonga_ih_post_soft_reset(void *handle)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (!adev->irq.srbm_soft_reset)
4148c2ecf20Sopenharmony_ci		return 0;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	return tonga_ih_hw_init(adev);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic int tonga_ih_soft_reset(void *handle)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
4228c2ecf20Sopenharmony_ci	u32 srbm_soft_reset;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (!adev->irq.srbm_soft_reset)
4258c2ecf20Sopenharmony_ci		return 0;
4268c2ecf20Sopenharmony_ci	srbm_soft_reset = adev->irq.srbm_soft_reset;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (srbm_soft_reset) {
4298c2ecf20Sopenharmony_ci		u32 tmp;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		tmp = RREG32(mmSRBM_SOFT_RESET);
4328c2ecf20Sopenharmony_ci		tmp |= srbm_soft_reset;
4338c2ecf20Sopenharmony_ci		dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
4348c2ecf20Sopenharmony_ci		WREG32(mmSRBM_SOFT_RESET, tmp);
4358c2ecf20Sopenharmony_ci		tmp = RREG32(mmSRBM_SOFT_RESET);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		udelay(50);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		tmp &= ~srbm_soft_reset;
4408c2ecf20Sopenharmony_ci		WREG32(mmSRBM_SOFT_RESET, tmp);
4418c2ecf20Sopenharmony_ci		tmp = RREG32(mmSRBM_SOFT_RESET);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci		/* Wait a little for things to settle down */
4448c2ecf20Sopenharmony_ci		udelay(50);
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	return 0;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic int tonga_ih_set_clockgating_state(void *handle,
4518c2ecf20Sopenharmony_ci					  enum amd_clockgating_state state)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic int tonga_ih_set_powergating_state(void *handle,
4578c2ecf20Sopenharmony_ci					  enum amd_powergating_state state)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	return 0;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic const struct amd_ip_funcs tonga_ih_ip_funcs = {
4638c2ecf20Sopenharmony_ci	.name = "tonga_ih",
4648c2ecf20Sopenharmony_ci	.early_init = tonga_ih_early_init,
4658c2ecf20Sopenharmony_ci	.late_init = NULL,
4668c2ecf20Sopenharmony_ci	.sw_init = tonga_ih_sw_init,
4678c2ecf20Sopenharmony_ci	.sw_fini = tonga_ih_sw_fini,
4688c2ecf20Sopenharmony_ci	.hw_init = tonga_ih_hw_init,
4698c2ecf20Sopenharmony_ci	.hw_fini = tonga_ih_hw_fini,
4708c2ecf20Sopenharmony_ci	.suspend = tonga_ih_suspend,
4718c2ecf20Sopenharmony_ci	.resume = tonga_ih_resume,
4728c2ecf20Sopenharmony_ci	.is_idle = tonga_ih_is_idle,
4738c2ecf20Sopenharmony_ci	.wait_for_idle = tonga_ih_wait_for_idle,
4748c2ecf20Sopenharmony_ci	.check_soft_reset = tonga_ih_check_soft_reset,
4758c2ecf20Sopenharmony_ci	.pre_soft_reset = tonga_ih_pre_soft_reset,
4768c2ecf20Sopenharmony_ci	.soft_reset = tonga_ih_soft_reset,
4778c2ecf20Sopenharmony_ci	.post_soft_reset = tonga_ih_post_soft_reset,
4788c2ecf20Sopenharmony_ci	.set_clockgating_state = tonga_ih_set_clockgating_state,
4798c2ecf20Sopenharmony_ci	.set_powergating_state = tonga_ih_set_powergating_state,
4808c2ecf20Sopenharmony_ci};
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic const struct amdgpu_ih_funcs tonga_ih_funcs = {
4838c2ecf20Sopenharmony_ci	.get_wptr = tonga_ih_get_wptr,
4848c2ecf20Sopenharmony_ci	.decode_iv = tonga_ih_decode_iv,
4858c2ecf20Sopenharmony_ci	.set_rptr = tonga_ih_set_rptr
4868c2ecf20Sopenharmony_ci};
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic void tonga_ih_set_interrupt_funcs(struct amdgpu_device *adev)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	adev->irq.ih_funcs = &tonga_ih_funcs;
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ciconst struct amdgpu_ip_block_version tonga_ih_ip_block =
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	.type = AMD_IP_BLOCK_TYPE_IH,
4968c2ecf20Sopenharmony_ci	.major = 3,
4978c2ecf20Sopenharmony_ci	.minor = 0,
4988c2ecf20Sopenharmony_ci	.rev = 0,
4998c2ecf20Sopenharmony_ci	.funcs = &tonga_ih_ip_funcs,
5008c2ecf20Sopenharmony_ci};
501