root/sys/dev/drm/amd/amdgpu/amdgpu_xgmi.c
/*
 * Copyright 2018 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.
 *
 *
 */
#include <linux/list.h>
#include "amdgpu.h"
#include "amdgpu_psp.h"


static DEFINE_MUTEX(xgmi_mutex);

#define AMDGPU_MAX_XGMI_HIVE                    8
#define AMDGPU_MAX_XGMI_DEVICE_PER_HIVE         4

struct amdgpu_hive_info {
        uint64_t                hive_id;
        struct list_head        device_list;
};

static struct amdgpu_hive_info xgmi_hives[AMDGPU_MAX_XGMI_HIVE];
static unsigned hive_count = 0;

static struct amdgpu_hive_info *amdgpu_get_xgmi_hive(struct amdgpu_device *adev)
{
        int i;
        struct amdgpu_hive_info *tmp;

        if (!adev->gmc.xgmi.hive_id)
                return NULL;
        for (i = 0 ; i < hive_count; ++i) {
                tmp = &xgmi_hives[i];
                if (tmp->hive_id == adev->gmc.xgmi.hive_id)
                        return tmp;
        }
        if (i >= AMDGPU_MAX_XGMI_HIVE)
                return NULL;

        /* initialize new hive if not exist */
        tmp = &xgmi_hives[hive_count++];
        tmp->hive_id = adev->gmc.xgmi.hive_id;
        INIT_LIST_HEAD(&tmp->device_list);
        return tmp;
}

int amdgpu_xgmi_add_device(struct amdgpu_device *adev)
{
        struct psp_xgmi_topology_info tmp_topology[AMDGPU_MAX_XGMI_DEVICE_PER_HIVE];
        struct amdgpu_hive_info *hive;
        struct amdgpu_xgmi      *entry;
        struct amdgpu_device    *tmp_adev;

        int count = 0, ret = -EINVAL;

        if ((adev->asic_type < CHIP_VEGA20) ||
                (adev->flags & AMD_IS_APU) )
                return 0;
        adev->gmc.xgmi.device_id = psp_xgmi_get_device_id(&adev->psp);
        adev->gmc.xgmi.hive_id = psp_xgmi_get_hive_id(&adev->psp);

        memset(&tmp_topology[0], 0, sizeof(tmp_topology));
        mutex_lock(&xgmi_mutex);
        hive = amdgpu_get_xgmi_hive(adev);
        if (!hive)
                goto exit;

        list_add_tail(&adev->gmc.xgmi.head, &hive->device_list);
        list_for_each_entry(entry, &hive->device_list, head)
                tmp_topology[count++].device_id = entry->device_id;

        ret = psp_xgmi_get_topology_info(&adev->psp, count, tmp_topology);
        if (ret) {
                dev_err(adev->dev,
                        "XGMI: Get topology failure on device %llx, hive %llx, ret %d",
                        adev->gmc.xgmi.device_id,
                        adev->gmc.xgmi.hive_id, ret);
                goto exit;
        }
        /* Each psp need to set the latest topology */
        list_for_each_entry(tmp_adev, &hive->device_list, gmc.xgmi.head) {
                ret = psp_xgmi_set_topology_info(&tmp_adev->psp, count, tmp_topology);
                if (ret) {
                        dev_err(tmp_adev->dev,
                                "XGMI: Set topology failure on device %llx, hive %llx, ret %d",
                                tmp_adev->gmc.xgmi.device_id,
                                tmp_adev->gmc.xgmi.hive_id, ret);
                        /* To do : continue with some  node failed or disable the  whole  hive */
                        break;
                }
        }
        if (!ret)
                dev_info(adev->dev, "XGMI: Add node %d to hive 0x%llx.\n",
                        adev->gmc.xgmi.physical_node_id,
                        adev->gmc.xgmi.hive_id);

exit:
        mutex_unlock(&xgmi_mutex);
        return ret;
}