18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2018 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 "amdgpu.h"
258c2ecf20Sopenharmony_ci#include "amdgpu_sdma.h"
268c2ecf20Sopenharmony_ci#include "amdgpu_ras.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define AMDGPU_CSA_SDMA_SIZE 64
298c2ecf20Sopenharmony_ci/* SDMA CSA reside in the 3rd page of CSA */
308c2ecf20Sopenharmony_ci#define AMDGPU_CSA_SDMA_OFFSET (4096 * 2)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * GPU SDMA IP block helpers function.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistruct amdgpu_sdma_instance *amdgpu_sdma_get_instance_from_ring(struct amdgpu_ring *ring)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = ring->adev;
398c2ecf20Sopenharmony_ci	int i;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	for (i = 0; i < adev->sdma.num_instances; i++)
428c2ecf20Sopenharmony_ci		if (ring == &adev->sdma.instance[i].ring ||
438c2ecf20Sopenharmony_ci		    ring == &adev->sdma.instance[i].page)
448c2ecf20Sopenharmony_ci			return &adev->sdma.instance[i];
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	return NULL;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ciint amdgpu_sdma_get_index_from_ring(struct amdgpu_ring *ring, uint32_t *index)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = ring->adev;
528c2ecf20Sopenharmony_ci	int i;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	for (i = 0; i < adev->sdma.num_instances; i++) {
558c2ecf20Sopenharmony_ci		if (ring == &adev->sdma.instance[i].ring ||
568c2ecf20Sopenharmony_ci			ring == &adev->sdma.instance[i].page) {
578c2ecf20Sopenharmony_ci			*index = i;
588c2ecf20Sopenharmony_ci			return 0;
598c2ecf20Sopenharmony_ci		}
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return -EINVAL;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ciuint64_t amdgpu_sdma_get_csa_mc_addr(struct amdgpu_ring *ring,
668c2ecf20Sopenharmony_ci				     unsigned vmid)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct amdgpu_device *adev = ring->adev;
698c2ecf20Sopenharmony_ci	uint64_t csa_mc_addr;
708c2ecf20Sopenharmony_ci	uint32_t index = 0;
718c2ecf20Sopenharmony_ci	int r;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* don't enable OS preemption on SDMA under SRIOV */
748c2ecf20Sopenharmony_ci	if (amdgpu_sriov_vf(adev) || vmid == 0 || !amdgpu_mcbp)
758c2ecf20Sopenharmony_ci		return 0;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	r = amdgpu_sdma_get_index_from_ring(ring, &index);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (r || index > 31)
808c2ecf20Sopenharmony_ci		csa_mc_addr = 0;
818c2ecf20Sopenharmony_ci	else
828c2ecf20Sopenharmony_ci		csa_mc_addr = amdgpu_csa_vaddr(adev) +
838c2ecf20Sopenharmony_ci			AMDGPU_CSA_SDMA_OFFSET +
848c2ecf20Sopenharmony_ci			index * AMDGPU_CSA_SDMA_SIZE;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return csa_mc_addr;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciint amdgpu_sdma_ras_late_init(struct amdgpu_device *adev,
908c2ecf20Sopenharmony_ci			      void *ras_ih_info)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	int r, i;
938c2ecf20Sopenharmony_ci	struct ras_ih_if *ih_info = (struct ras_ih_if *)ras_ih_info;
948c2ecf20Sopenharmony_ci	struct ras_fs_if fs_info = {
958c2ecf20Sopenharmony_ci		.sysfs_name = "sdma_err_count",
968c2ecf20Sopenharmony_ci	};
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (!ih_info)
998c2ecf20Sopenharmony_ci		return -EINVAL;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (!adev->sdma.ras_if) {
1028c2ecf20Sopenharmony_ci		adev->sdma.ras_if = kmalloc(sizeof(struct ras_common_if), GFP_KERNEL);
1038c2ecf20Sopenharmony_ci		if (!adev->sdma.ras_if)
1048c2ecf20Sopenharmony_ci			return -ENOMEM;
1058c2ecf20Sopenharmony_ci		adev->sdma.ras_if->block = AMDGPU_RAS_BLOCK__SDMA;
1068c2ecf20Sopenharmony_ci		adev->sdma.ras_if->type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE;
1078c2ecf20Sopenharmony_ci		adev->sdma.ras_if->sub_block_index = 0;
1088c2ecf20Sopenharmony_ci		strcpy(adev->sdma.ras_if->name, "sdma");
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci	fs_info.head = ih_info->head = *adev->sdma.ras_if;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	r = amdgpu_ras_late_init(adev, adev->sdma.ras_if,
1138c2ecf20Sopenharmony_ci				 &fs_info, ih_info);
1148c2ecf20Sopenharmony_ci	if (r)
1158c2ecf20Sopenharmony_ci		goto free;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (amdgpu_ras_is_supported(adev, adev->sdma.ras_if->block)) {
1188c2ecf20Sopenharmony_ci		for (i = 0; i < adev->sdma.num_instances; i++) {
1198c2ecf20Sopenharmony_ci			r = amdgpu_irq_get(adev, &adev->sdma.ecc_irq,
1208c2ecf20Sopenharmony_ci				AMDGPU_SDMA_IRQ_INSTANCE0 + i);
1218c2ecf20Sopenharmony_ci			if (r)
1228c2ecf20Sopenharmony_ci				goto late_fini;
1238c2ecf20Sopenharmony_ci		}
1248c2ecf20Sopenharmony_ci	} else {
1258c2ecf20Sopenharmony_ci		r = 0;
1268c2ecf20Sopenharmony_ci		goto free;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci        return 0;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cilate_fini:
1328c2ecf20Sopenharmony_ci	amdgpu_ras_late_fini(adev, adev->sdma.ras_if, ih_info);
1338c2ecf20Sopenharmony_cifree:
1348c2ecf20Sopenharmony_ci	kfree(adev->sdma.ras_if);
1358c2ecf20Sopenharmony_ci	adev->sdma.ras_if = NULL;
1368c2ecf20Sopenharmony_ci	return r;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_civoid amdgpu_sdma_ras_fini(struct amdgpu_device *adev)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__SDMA) &&
1428c2ecf20Sopenharmony_ci			adev->sdma.ras_if) {
1438c2ecf20Sopenharmony_ci		struct ras_common_if *ras_if = adev->sdma.ras_if;
1448c2ecf20Sopenharmony_ci		struct ras_ih_if ih_info = {
1458c2ecf20Sopenharmony_ci			.head = *ras_if,
1468c2ecf20Sopenharmony_ci			/* the cb member will not be used by
1478c2ecf20Sopenharmony_ci			 * amdgpu_ras_interrupt_remove_handler, init it only
1488c2ecf20Sopenharmony_ci			 * to cheat the check in ras_late_fini
1498c2ecf20Sopenharmony_ci			 */
1508c2ecf20Sopenharmony_ci			.cb = amdgpu_sdma_process_ras_data_cb,
1518c2ecf20Sopenharmony_ci		};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		amdgpu_ras_late_fini(adev, ras_if, &ih_info);
1548c2ecf20Sopenharmony_ci		kfree(ras_if);
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciint amdgpu_sdma_process_ras_data_cb(struct amdgpu_device *adev,
1598c2ecf20Sopenharmony_ci		void *err_data,
1608c2ecf20Sopenharmony_ci		struct amdgpu_iv_entry *entry)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	kgd2kfd_set_sram_ecc_flag(adev->kfd.dev);
1638c2ecf20Sopenharmony_ci	amdgpu_ras_reset_gpu(adev);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return AMDGPU_RAS_SUCCESS;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciint amdgpu_sdma_process_ecc_irq(struct amdgpu_device *adev,
1698c2ecf20Sopenharmony_ci				      struct amdgpu_irq_src *source,
1708c2ecf20Sopenharmony_ci				      struct amdgpu_iv_entry *entry)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct ras_common_if *ras_if = adev->sdma.ras_if;
1738c2ecf20Sopenharmony_ci	struct ras_dispatch_if ih_data = {
1748c2ecf20Sopenharmony_ci		.entry = entry,
1758c2ecf20Sopenharmony_ci	};
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (!ras_if)
1788c2ecf20Sopenharmony_ci		return 0;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	ih_data.head = *ras_if;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	amdgpu_ras_interrupt_dispatch(adev, &ih_data);
1838c2ecf20Sopenharmony_ci	return 0;
1848c2ecf20Sopenharmony_ci}
185