18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright 2015 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 "sid.h" 298c2ecf20Sopenharmony_ci#include "si_ih.h" 308c2ecf20Sopenharmony_ci#include "oss/oss_1_0_d.h" 318c2ecf20Sopenharmony_ci#include "oss/oss_1_0_sh_mask.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void si_ih_set_interrupt_funcs(struct amdgpu_device *adev); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void si_ih_enable_interrupts(struct amdgpu_device *adev) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci u32 ih_cntl = RREG32(IH_CNTL); 388c2ecf20Sopenharmony_ci u32 ih_rb_cntl = RREG32(IH_RB_CNTL); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci ih_cntl |= ENABLE_INTR; 418c2ecf20Sopenharmony_ci ih_rb_cntl |= IH_RB_ENABLE; 428c2ecf20Sopenharmony_ci WREG32(IH_CNTL, ih_cntl); 438c2ecf20Sopenharmony_ci WREG32(IH_RB_CNTL, ih_rb_cntl); 448c2ecf20Sopenharmony_ci adev->irq.ih.enabled = true; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic void si_ih_disable_interrupts(struct amdgpu_device *adev) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci u32 ih_rb_cntl = RREG32(IH_RB_CNTL); 508c2ecf20Sopenharmony_ci u32 ih_cntl = RREG32(IH_CNTL); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci ih_rb_cntl &= ~IH_RB_ENABLE; 538c2ecf20Sopenharmony_ci ih_cntl &= ~ENABLE_INTR; 548c2ecf20Sopenharmony_ci WREG32(IH_RB_CNTL, ih_rb_cntl); 558c2ecf20Sopenharmony_ci WREG32(IH_CNTL, ih_cntl); 568c2ecf20Sopenharmony_ci WREG32(IH_RB_RPTR, 0); 578c2ecf20Sopenharmony_ci WREG32(IH_RB_WPTR, 0); 588c2ecf20Sopenharmony_ci adev->irq.ih.enabled = false; 598c2ecf20Sopenharmony_ci adev->irq.ih.rptr = 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int si_ih_irq_init(struct amdgpu_device *adev) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct amdgpu_ih_ring *ih = &adev->irq.ih; 658c2ecf20Sopenharmony_ci int rb_bufsz; 668c2ecf20Sopenharmony_ci u32 interrupt_cntl, ih_cntl, ih_rb_cntl; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci si_ih_disable_interrupts(adev); 698c2ecf20Sopenharmony_ci /* set dummy read address to dummy page address */ 708c2ecf20Sopenharmony_ci WREG32(INTERRUPT_CNTL2, adev->dummy_page_addr >> 8); 718c2ecf20Sopenharmony_ci interrupt_cntl = RREG32(INTERRUPT_CNTL); 728c2ecf20Sopenharmony_ci interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE; 738c2ecf20Sopenharmony_ci interrupt_cntl &= ~IH_REQ_NONSNOOP_EN; 748c2ecf20Sopenharmony_ci WREG32(INTERRUPT_CNTL, interrupt_cntl); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci WREG32(IH_RB_BASE, adev->irq.ih.gpu_addr >> 8); 778c2ecf20Sopenharmony_ci rb_bufsz = order_base_2(adev->irq.ih.ring_size / 4); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ih_rb_cntl = IH_WPTR_OVERFLOW_ENABLE | 808c2ecf20Sopenharmony_ci IH_WPTR_OVERFLOW_CLEAR | 818c2ecf20Sopenharmony_ci (rb_bufsz << 1) | 828c2ecf20Sopenharmony_ci IH_WPTR_WRITEBACK_ENABLE; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci WREG32(IH_RB_WPTR_ADDR_LO, lower_32_bits(ih->wptr_addr)); 858c2ecf20Sopenharmony_ci WREG32(IH_RB_WPTR_ADDR_HI, upper_32_bits(ih->wptr_addr) & 0xFF); 868c2ecf20Sopenharmony_ci WREG32(IH_RB_CNTL, ih_rb_cntl); 878c2ecf20Sopenharmony_ci WREG32(IH_RB_RPTR, 0); 888c2ecf20Sopenharmony_ci WREG32(IH_RB_WPTR, 0); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ih_cntl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10) | MC_VMID(0); 918c2ecf20Sopenharmony_ci if (adev->irq.msi_enabled) 928c2ecf20Sopenharmony_ci ih_cntl |= RPTR_REARM; 938c2ecf20Sopenharmony_ci WREG32(IH_CNTL, ih_cntl); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci pci_set_master(adev->pdev); 968c2ecf20Sopenharmony_ci si_ih_enable_interrupts(adev); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic void si_ih_irq_disable(struct amdgpu_device *adev) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci si_ih_disable_interrupts(adev); 1048c2ecf20Sopenharmony_ci mdelay(1); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic u32 si_ih_get_wptr(struct amdgpu_device *adev, 1088c2ecf20Sopenharmony_ci struct amdgpu_ih_ring *ih) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci u32 wptr, tmp; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci wptr = le32_to_cpu(*ih->wptr_cpu); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (wptr & IH_RB_WPTR__RB_OVERFLOW_MASK) { 1158c2ecf20Sopenharmony_ci wptr &= ~IH_RB_WPTR__RB_OVERFLOW_MASK; 1168c2ecf20Sopenharmony_ci dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n", 1178c2ecf20Sopenharmony_ci wptr, ih->rptr, (wptr + 16) & ih->ptr_mask); 1188c2ecf20Sopenharmony_ci ih->rptr = (wptr + 16) & ih->ptr_mask; 1198c2ecf20Sopenharmony_ci tmp = RREG32(IH_RB_CNTL); 1208c2ecf20Sopenharmony_ci tmp |= IH_RB_CNTL__WPTR_OVERFLOW_CLEAR_MASK; 1218c2ecf20Sopenharmony_ci WREG32(IH_RB_CNTL, tmp); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci return (wptr & ih->ptr_mask); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void si_ih_decode_iv(struct amdgpu_device *adev, 1278c2ecf20Sopenharmony_ci struct amdgpu_ih_ring *ih, 1288c2ecf20Sopenharmony_ci struct amdgpu_iv_entry *entry) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci u32 ring_index = ih->rptr >> 2; 1318c2ecf20Sopenharmony_ci uint32_t dw[4]; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci dw[0] = le32_to_cpu(ih->ring[ring_index + 0]); 1348c2ecf20Sopenharmony_ci dw[1] = le32_to_cpu(ih->ring[ring_index + 1]); 1358c2ecf20Sopenharmony_ci dw[2] = le32_to_cpu(ih->ring[ring_index + 2]); 1368c2ecf20Sopenharmony_ci dw[3] = le32_to_cpu(ih->ring[ring_index + 3]); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci entry->client_id = AMDGPU_IRQ_CLIENTID_LEGACY; 1398c2ecf20Sopenharmony_ci entry->src_id = dw[0] & 0xff; 1408c2ecf20Sopenharmony_ci entry->src_data[0] = dw[1] & 0xfffffff; 1418c2ecf20Sopenharmony_ci entry->ring_id = dw[2] & 0xff; 1428c2ecf20Sopenharmony_ci entry->vmid = (dw[2] >> 8) & 0xff; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ih->rptr += 16; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void si_ih_set_rptr(struct amdgpu_device *adev, 1488c2ecf20Sopenharmony_ci struct amdgpu_ih_ring *ih) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci WREG32(IH_RB_RPTR, ih->rptr); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int si_ih_early_init(void *handle) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci si_ih_set_interrupt_funcs(adev); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int si_ih_sw_init(void *handle) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int r; 1658c2ecf20Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci r = amdgpu_ih_ring_init(adev, &adev->irq.ih, 64 * 1024, false); 1688c2ecf20Sopenharmony_ci if (r) 1698c2ecf20Sopenharmony_ci return r; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return amdgpu_irq_init(adev); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int si_ih_sw_fini(void *handle) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci amdgpu_irq_fini(adev); 1798c2ecf20Sopenharmony_ci amdgpu_ih_ring_fini(adev, &adev->irq.ih); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int si_ih_hw_init(void *handle) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return si_ih_irq_init(adev); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int si_ih_hw_fini(void *handle) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci si_ih_irq_disable(adev); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int si_ih_suspend(void *handle) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return si_ih_hw_fini(adev); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int si_ih_resume(void *handle) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci return si_ih_hw_init(adev); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic bool si_ih_is_idle(void *handle) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 2178c2ecf20Sopenharmony_ci u32 tmp = RREG32(SRBM_STATUS); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (tmp & SRBM_STATUS__IH_BUSY_MASK) 2208c2ecf20Sopenharmony_ci return false; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return true; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int si_ih_wait_for_idle(void *handle) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci unsigned i; 2288c2ecf20Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci for (i = 0; i < adev->usec_timeout; i++) { 2318c2ecf20Sopenharmony_ci if (si_ih_is_idle(handle)) 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci udelay(1); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci return -ETIMEDOUT; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int si_ih_soft_reset(void *handle) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct amdgpu_device *adev = (struct amdgpu_device *)handle; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci u32 srbm_soft_reset = 0; 2438c2ecf20Sopenharmony_ci u32 tmp = RREG32(SRBM_STATUS); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (tmp & SRBM_STATUS__IH_BUSY_MASK) 2468c2ecf20Sopenharmony_ci srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_IH_MASK; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (srbm_soft_reset) { 2498c2ecf20Sopenharmony_ci tmp = RREG32(SRBM_SOFT_RESET); 2508c2ecf20Sopenharmony_ci tmp |= srbm_soft_reset; 2518c2ecf20Sopenharmony_ci dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp); 2528c2ecf20Sopenharmony_ci WREG32(SRBM_SOFT_RESET, tmp); 2538c2ecf20Sopenharmony_ci tmp = RREG32(SRBM_SOFT_RESET); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci udelay(50); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci tmp &= ~srbm_soft_reset; 2588c2ecf20Sopenharmony_ci WREG32(SRBM_SOFT_RESET, tmp); 2598c2ecf20Sopenharmony_ci tmp = RREG32(SRBM_SOFT_RESET); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci udelay(50); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int si_ih_set_clockgating_state(void *handle, 2688c2ecf20Sopenharmony_ci enum amd_clockgating_state state) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int si_ih_set_powergating_state(void *handle, 2748c2ecf20Sopenharmony_ci enum amd_powergating_state state) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic const struct amd_ip_funcs si_ih_ip_funcs = { 2808c2ecf20Sopenharmony_ci .name = "si_ih", 2818c2ecf20Sopenharmony_ci .early_init = si_ih_early_init, 2828c2ecf20Sopenharmony_ci .late_init = NULL, 2838c2ecf20Sopenharmony_ci .sw_init = si_ih_sw_init, 2848c2ecf20Sopenharmony_ci .sw_fini = si_ih_sw_fini, 2858c2ecf20Sopenharmony_ci .hw_init = si_ih_hw_init, 2868c2ecf20Sopenharmony_ci .hw_fini = si_ih_hw_fini, 2878c2ecf20Sopenharmony_ci .suspend = si_ih_suspend, 2888c2ecf20Sopenharmony_ci .resume = si_ih_resume, 2898c2ecf20Sopenharmony_ci .is_idle = si_ih_is_idle, 2908c2ecf20Sopenharmony_ci .wait_for_idle = si_ih_wait_for_idle, 2918c2ecf20Sopenharmony_ci .soft_reset = si_ih_soft_reset, 2928c2ecf20Sopenharmony_ci .set_clockgating_state = si_ih_set_clockgating_state, 2938c2ecf20Sopenharmony_ci .set_powergating_state = si_ih_set_powergating_state, 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic const struct amdgpu_ih_funcs si_ih_funcs = { 2978c2ecf20Sopenharmony_ci .get_wptr = si_ih_get_wptr, 2988c2ecf20Sopenharmony_ci .decode_iv = si_ih_decode_iv, 2998c2ecf20Sopenharmony_ci .set_rptr = si_ih_set_rptr 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic void si_ih_set_interrupt_funcs(struct amdgpu_device *adev) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci adev->irq.ih_funcs = &si_ih_funcs; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ciconst struct amdgpu_ip_block_version si_ih_ip_block = 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci .type = AMD_IP_BLOCK_TYPE_IH, 3108c2ecf20Sopenharmony_ci .major = 1, 3118c2ecf20Sopenharmony_ci .minor = 0, 3128c2ecf20Sopenharmony_ci .rev = 0, 3138c2ecf20Sopenharmony_ci .funcs = &si_ih_ip_funcs, 3148c2ecf20Sopenharmony_ci}; 315