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_1_d.h"
318c2ecf20Sopenharmony_ci#include "oss/oss_3_0_1_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 cz_ih_set_interrupt_funcs(struct amdgpu_device *adev);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/**
548c2ecf20Sopenharmony_ci * cz_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 cz_ih_enable_interrupts(struct amdgpu_device *adev)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	u32 ih_cntl = RREG32(mmIH_CNTL);
638c2ecf20Sopenharmony_ci	u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, ENABLE_INTR, 1);
668c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 1);
678c2ecf20Sopenharmony_ci	WREG32(mmIH_CNTL, ih_cntl);
688c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_CNTL, ih_rb_cntl);
698c2ecf20Sopenharmony_ci	adev->irq.ih.enabled = true;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/**
738c2ecf20Sopenharmony_ci * cz_ih_disable_interrupts - Disable the interrupt ring buffer
748c2ecf20Sopenharmony_ci *
758c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci * Disable the interrupt ring buffer (VI).
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_cistatic void cz_ih_disable_interrupts(struct amdgpu_device *adev)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
828c2ecf20Sopenharmony_ci	u32 ih_cntl = RREG32(mmIH_CNTL);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 0);
858c2ecf20Sopenharmony_ci	ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, ENABLE_INTR, 0);
868c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_CNTL, ih_rb_cntl);
878c2ecf20Sopenharmony_ci	WREG32(mmIH_CNTL, ih_cntl);
888c2ecf20Sopenharmony_ci	/* set rptr, wptr to 0 */
898c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_RPTR, 0);
908c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_WPTR, 0);
918c2ecf20Sopenharmony_ci	adev->irq.ih.enabled = false;
928c2ecf20Sopenharmony_ci	adev->irq.ih.rptr = 0;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/**
968c2ecf20Sopenharmony_ci * cz_ih_irq_init - init and enable the interrupt ring
978c2ecf20Sopenharmony_ci *
988c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
998c2ecf20Sopenharmony_ci *
1008c2ecf20Sopenharmony_ci * Allocate a ring buffer for the interrupt controller,
1018c2ecf20Sopenharmony_ci * enable the RLC, disable interrupts, enable the IH
1028c2ecf20Sopenharmony_ci * ring buffer and enable it (VI).
1038c2ecf20Sopenharmony_ci * Called at device load and reume.
1048c2ecf20Sopenharmony_ci * Returns 0 for success, errors for failure.
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_cistatic int cz_ih_irq_init(struct amdgpu_device *adev)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct amdgpu_ih_ring *ih = &adev->irq.ih;
1098c2ecf20Sopenharmony_ci	u32 interrupt_cntl, ih_cntl, ih_rb_cntl;
1108c2ecf20Sopenharmony_ci	int rb_bufsz;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* disable irqs */
1138c2ecf20Sopenharmony_ci	cz_ih_disable_interrupts(adev);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* setup interrupt control */
1168c2ecf20Sopenharmony_ci	WREG32(mmINTERRUPT_CNTL2, adev->dummy_page_addr >> 8);
1178c2ecf20Sopenharmony_ci	interrupt_cntl = RREG32(mmINTERRUPT_CNTL);
1188c2ecf20Sopenharmony_ci	/* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi
1198c2ecf20Sopenharmony_ci	 * INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_DUMMY_RD_OVERRIDE, 0);
1228c2ecf20Sopenharmony_ci	/* INTERRUPT_CNTL__IH_REQ_NONSNOOP_EN_MASK=1 if ring is in non-cacheable memory, e.g., vram */
1238c2ecf20Sopenharmony_ci	interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_REQ_NONSNOOP_EN, 0);
1248c2ecf20Sopenharmony_ci	WREG32(mmINTERRUPT_CNTL, interrupt_cntl);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* Ring Buffer base. [39:8] of 40-bit address of the beginning of the ring buffer*/
1278c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_BASE, adev->irq.ih.gpu_addr >> 8);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	rb_bufsz = order_base_2(adev->irq.ih.ring_size / 4);
1308c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(0, IH_RB_CNTL, WPTR_OVERFLOW_ENABLE, 1);
1318c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
1328c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_SIZE, rb_bufsz);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* Ring Buffer write pointer writeback. If enabled, IH_RB_WPTR register value is written to memory */
1358c2ecf20Sopenharmony_ci	ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_WRITEBACK_ENABLE, 1);
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	WREG32(mmIH_RB_CNTL, ih_rb_cntl);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* set rptr, wptr to 0 */
1448c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_RPTR, 0);
1458c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_WPTR, 0);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/* Default settings for IH_CNTL (disabled at first) */
1488c2ecf20Sopenharmony_ci	ih_cntl = RREG32(mmIH_CNTL);
1498c2ecf20Sopenharmony_ci	ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, MC_VMID, 0);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (adev->irq.msi_enabled)
1528c2ecf20Sopenharmony_ci		ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, RPTR_REARM, 1);
1538c2ecf20Sopenharmony_ci	WREG32(mmIH_CNTL, ih_cntl);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	pci_set_master(adev->pdev);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	/* enable interrupts */
1588c2ecf20Sopenharmony_ci	cz_ih_enable_interrupts(adev);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	return 0;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/**
1648c2ecf20Sopenharmony_ci * cz_ih_irq_disable - disable interrupts
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
1678c2ecf20Sopenharmony_ci *
1688c2ecf20Sopenharmony_ci * Disable interrupts on the hw (VI).
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_cistatic void cz_ih_irq_disable(struct amdgpu_device *adev)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	cz_ih_disable_interrupts(adev);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* Wait and acknowledge irq */
1758c2ecf20Sopenharmony_ci	mdelay(1);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/**
1798c2ecf20Sopenharmony_ci * cz_ih_get_wptr - get the IH ring buffer wptr
1808c2ecf20Sopenharmony_ci *
1818c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
1828c2ecf20Sopenharmony_ci *
1838c2ecf20Sopenharmony_ci * Get the IH ring buffer wptr from either the register
1848c2ecf20Sopenharmony_ci * or the writeback memory buffer (VI).  Also check for
1858c2ecf20Sopenharmony_ci * ring buffer overflow and deal with it.
1868c2ecf20Sopenharmony_ci * Used by cz_irq_process(VI).
1878c2ecf20Sopenharmony_ci * Returns the value of the wptr.
1888c2ecf20Sopenharmony_ci */
1898c2ecf20Sopenharmony_cistatic u32 cz_ih_get_wptr(struct amdgpu_device *adev,
1908c2ecf20Sopenharmony_ci			  struct amdgpu_ih_ring *ih)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	u32 wptr, tmp;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	wptr = le32_to_cpu(*ih->wptr_cpu);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW))
1978c2ecf20Sopenharmony_ci		goto out;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/* Double check that the overflow wasn't already cleared. */
2008c2ecf20Sopenharmony_ci	wptr = RREG32(mmIH_RB_WPTR);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW))
2038c2ecf20Sopenharmony_ci		goto out;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* When a ring buffer overflow happen start parsing interrupt
2088c2ecf20Sopenharmony_ci	 * from the last not overwritten vector (wptr + 16). Hopefully
2098c2ecf20Sopenharmony_ci	 * this should allow us to catchup.
2108c2ecf20Sopenharmony_ci	 */
2118c2ecf20Sopenharmony_ci	dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
2128c2ecf20Sopenharmony_ci		wptr, ih->rptr, (wptr + 16) & ih->ptr_mask);
2138c2ecf20Sopenharmony_ci	ih->rptr = (wptr + 16) & ih->ptr_mask;
2148c2ecf20Sopenharmony_ci	tmp = RREG32(mmIH_RB_CNTL);
2158c2ecf20Sopenharmony_ci	tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
2168c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_CNTL, tmp);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ciout:
2208c2ecf20Sopenharmony_ci	return (wptr & ih->ptr_mask);
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/**
2248c2ecf20Sopenharmony_ci * cz_ih_decode_iv - decode an interrupt vector
2258c2ecf20Sopenharmony_ci *
2268c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
2278c2ecf20Sopenharmony_ci *
2288c2ecf20Sopenharmony_ci * Decodes the interrupt vector at the current rptr
2298c2ecf20Sopenharmony_ci * position and also advance the position.
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_cistatic void cz_ih_decode_iv(struct amdgpu_device *adev,
2328c2ecf20Sopenharmony_ci			    struct amdgpu_ih_ring *ih,
2338c2ecf20Sopenharmony_ci			    struct amdgpu_iv_entry *entry)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	/* wptr/rptr are in bytes! */
2368c2ecf20Sopenharmony_ci	u32 ring_index = ih->rptr >> 2;
2378c2ecf20Sopenharmony_ci	uint32_t dw[4];
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	dw[0] = le32_to_cpu(ih->ring[ring_index + 0]);
2408c2ecf20Sopenharmony_ci	dw[1] = le32_to_cpu(ih->ring[ring_index + 1]);
2418c2ecf20Sopenharmony_ci	dw[2] = le32_to_cpu(ih->ring[ring_index + 2]);
2428c2ecf20Sopenharmony_ci	dw[3] = le32_to_cpu(ih->ring[ring_index + 3]);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	entry->client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
2458c2ecf20Sopenharmony_ci	entry->src_id = dw[0] & 0xff;
2468c2ecf20Sopenharmony_ci	entry->src_data[0] = dw[1] & 0xfffffff;
2478c2ecf20Sopenharmony_ci	entry->ring_id = dw[2] & 0xff;
2488c2ecf20Sopenharmony_ci	entry->vmid = (dw[2] >> 8) & 0xff;
2498c2ecf20Sopenharmony_ci	entry->pasid = (dw[2] >> 16) & 0xffff;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* wptr/rptr are in bytes! */
2528c2ecf20Sopenharmony_ci	ih->rptr += 16;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci/**
2568c2ecf20Sopenharmony_ci * cz_ih_set_rptr - set the IH ring buffer rptr
2578c2ecf20Sopenharmony_ci *
2588c2ecf20Sopenharmony_ci * @adev: amdgpu_device pointer
2598c2ecf20Sopenharmony_ci *
2608c2ecf20Sopenharmony_ci * Set the IH ring buffer rptr.
2618c2ecf20Sopenharmony_ci */
2628c2ecf20Sopenharmony_cistatic void cz_ih_set_rptr(struct amdgpu_device *adev,
2638c2ecf20Sopenharmony_ci			   struct amdgpu_ih_ring *ih)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	WREG32(mmIH_RB_RPTR, ih->rptr);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic int cz_ih_early_init(void *handle)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
2718c2ecf20Sopenharmony_ci	int ret;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	ret = amdgpu_irq_add_domain(adev);
2748c2ecf20Sopenharmony_ci	if (ret)
2758c2ecf20Sopenharmony_ci		return ret;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	cz_ih_set_interrupt_funcs(adev);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return 0;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic int cz_ih_sw_init(void *handle)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	int r;
2858c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	r = amdgpu_ih_ring_init(adev, &adev->irq.ih, 64 * 1024, false);
2888c2ecf20Sopenharmony_ci	if (r)
2898c2ecf20Sopenharmony_ci		return r;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	r = amdgpu_irq_init(adev);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	return r;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic int cz_ih_sw_fini(void *handle)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	amdgpu_irq_fini(adev);
3018c2ecf20Sopenharmony_ci	amdgpu_ih_ring_fini(adev, &adev->irq.ih);
3028c2ecf20Sopenharmony_ci	amdgpu_irq_remove_domain(adev);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return 0;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int cz_ih_hw_init(void *handle)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	int r;
3108c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	r = cz_ih_irq_init(adev);
3138c2ecf20Sopenharmony_ci	if (r)
3148c2ecf20Sopenharmony_ci		return r;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	return 0;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic int cz_ih_hw_fini(void *handle)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	cz_ih_irq_disable(adev);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	return 0;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic int cz_ih_suspend(void *handle)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return cz_ih_hw_fini(adev);
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int cz_ih_resume(void *handle)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return cz_ih_hw_init(adev);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic bool cz_ih_is_idle(void *handle)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3458c2ecf20Sopenharmony_ci	u32 tmp = RREG32(mmSRBM_STATUS);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
3488c2ecf20Sopenharmony_ci		return false;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return true;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic int cz_ih_wait_for_idle(void *handle)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	unsigned i;
3568c2ecf20Sopenharmony_ci	u32 tmp;
3578c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	for (i = 0; i < adev->usec_timeout; i++) {
3608c2ecf20Sopenharmony_ci		/* read MC_STATUS */
3618c2ecf20Sopenharmony_ci		tmp = RREG32(mmSRBM_STATUS);
3628c2ecf20Sopenharmony_ci		if (!REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
3638c2ecf20Sopenharmony_ci			return 0;
3648c2ecf20Sopenharmony_ci		udelay(1);
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int cz_ih_soft_reset(void *handle)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	u32 srbm_soft_reset = 0;
3728c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
3738c2ecf20Sopenharmony_ci	u32 tmp = RREG32(mmSRBM_STATUS);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (tmp & SRBM_STATUS__IH_BUSY_MASK)
3768c2ecf20Sopenharmony_ci		srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
3778c2ecf20Sopenharmony_ci						SOFT_RESET_IH, 1);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (srbm_soft_reset) {
3808c2ecf20Sopenharmony_ci		tmp = RREG32(mmSRBM_SOFT_RESET);
3818c2ecf20Sopenharmony_ci		tmp |= srbm_soft_reset;
3828c2ecf20Sopenharmony_ci		dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
3838c2ecf20Sopenharmony_ci		WREG32(mmSRBM_SOFT_RESET, tmp);
3848c2ecf20Sopenharmony_ci		tmp = RREG32(mmSRBM_SOFT_RESET);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		udelay(50);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		tmp &= ~srbm_soft_reset;
3898c2ecf20Sopenharmony_ci		WREG32(mmSRBM_SOFT_RESET, tmp);
3908c2ecf20Sopenharmony_ci		tmp = RREG32(mmSRBM_SOFT_RESET);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		/* Wait a little for things to settle down */
3938c2ecf20Sopenharmony_ci		udelay(50);
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic int cz_ih_set_clockgating_state(void *handle,
4008c2ecf20Sopenharmony_ci					  enum amd_clockgating_state state)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	// TODO
4038c2ecf20Sopenharmony_ci	return 0;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic int cz_ih_set_powergating_state(void *handle,
4078c2ecf20Sopenharmony_ci					  enum amd_powergating_state state)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	// TODO
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic const struct amd_ip_funcs cz_ih_ip_funcs = {
4148c2ecf20Sopenharmony_ci	.name = "cz_ih",
4158c2ecf20Sopenharmony_ci	.early_init = cz_ih_early_init,
4168c2ecf20Sopenharmony_ci	.late_init = NULL,
4178c2ecf20Sopenharmony_ci	.sw_init = cz_ih_sw_init,
4188c2ecf20Sopenharmony_ci	.sw_fini = cz_ih_sw_fini,
4198c2ecf20Sopenharmony_ci	.hw_init = cz_ih_hw_init,
4208c2ecf20Sopenharmony_ci	.hw_fini = cz_ih_hw_fini,
4218c2ecf20Sopenharmony_ci	.suspend = cz_ih_suspend,
4228c2ecf20Sopenharmony_ci	.resume = cz_ih_resume,
4238c2ecf20Sopenharmony_ci	.is_idle = cz_ih_is_idle,
4248c2ecf20Sopenharmony_ci	.wait_for_idle = cz_ih_wait_for_idle,
4258c2ecf20Sopenharmony_ci	.soft_reset = cz_ih_soft_reset,
4268c2ecf20Sopenharmony_ci	.set_clockgating_state = cz_ih_set_clockgating_state,
4278c2ecf20Sopenharmony_ci	.set_powergating_state = cz_ih_set_powergating_state,
4288c2ecf20Sopenharmony_ci};
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic const struct amdgpu_ih_funcs cz_ih_funcs = {
4318c2ecf20Sopenharmony_ci	.get_wptr = cz_ih_get_wptr,
4328c2ecf20Sopenharmony_ci	.decode_iv = cz_ih_decode_iv,
4338c2ecf20Sopenharmony_ci	.set_rptr = cz_ih_set_rptr
4348c2ecf20Sopenharmony_ci};
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic void cz_ih_set_interrupt_funcs(struct amdgpu_device *adev)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	adev->irq.ih_funcs = &cz_ih_funcs;
4398c2ecf20Sopenharmony_ci}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ciconst struct amdgpu_ip_block_version cz_ih_ip_block =
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	.type = AMD_IP_BLOCK_TYPE_IH,
4448c2ecf20Sopenharmony_ci	.major = 3,
4458c2ecf20Sopenharmony_ci	.minor = 0,
4468c2ecf20Sopenharmony_ci	.rev = 0,
4478c2ecf20Sopenharmony_ci	.funcs = &cz_ih_ip_funcs,
4488c2ecf20Sopenharmony_ci};
449