Add debugfs directory "amdgpu_ualink_test" to trigger UALink selftests
without userspace application, with three read/write entries:

  command          write the test name to run it, read to list the
                   valid test names
  remote_accel_id  accel_id of the remote GPU used by the tests
  test_size        test buffer size in bytes, from 4 to 8192

Add helper functions: ualink_simple_rw() for remote VRAM R/W,
ualink_remote_shootdown() for TLB shootdown, ualink_send_hello()
for handshake, ualink_update_interrupt_wb()
and ualink_update_shootdown_wb() to read writeback results.
Use lsdma by default, can select sdma with command.

v4: Use a single "command" entry taking the test name, instead of one
    entry per test (Felix).
    Resync the remote wptr before sending a command, the peer may have
    rebooted.
v3: Replace the single entry taking a test number with a debugfs
    directory and a named entry per command.
    Take peer->lock to update the writeback address and selftest_mutex
    to read the test parameters.
v2: Fix typos and issues from copilot code review.

Signed-off-by: Philip Yang <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 241 +++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_lsdma.c   |  49 ++++
 drivers/gpu/drm/amd/amdgpu/amdgpu_lsdma.h   |   1 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c  | 249 +++++++++++++++++++-
 drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h  |  15 ++
 5 files changed, 554 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
index 3dc95d9accf2..a898a24fda0d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
@@ -2133,9 +2133,237 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_ib_preempt, NULL,
 DEFINE_DEBUGFS_ATTRIBUTE(fops_sclk_set, NULL,
                        amdgpu_debugfs_sclk_set, "%llu\n");
 
+enum amdgpu_ualink_test_cmd {
+       UALINK_TEST_SW_INIT,
+       UALINK_TEST_SEND_HELLO,
+       UALINK_TEST_REMOTE_SHOOTDOWN,
+       UALINK_TEST_FLUSH_TLB,
+       UALINK_TEST_READ_LSDMA,
+       UALINK_TEST_READ_SDMA,
+       UALINK_TEST_WRITE_LSDMA,
+       UALINK_TEST_WRITE_SDMA,
+       UALINK_TEST_UPDATE_INTERRUPT_WB,
+       UALINK_TEST_UPDATE_SHOOTDOWN_WB,
+       UALINK_TEST_LSDMA_SELFTEST,
+};
+
+/* commands talking to a remote accelerator, remote_accel_id must be set */
+#define UALINK_TEST_NEEDS_ACCEL_ID                                     \
+       (BIT(UALINK_TEST_SEND_HELLO) | BIT(UALINK_TEST_REMOTE_SHOOTDOWN) | \
+        BIT(UALINK_TEST_READ_LSDMA) | BIT(UALINK_TEST_READ_SDMA) |     \
+        BIT(UALINK_TEST_WRITE_LSDMA) | BIT(UALINK_TEST_WRITE_SDMA) |   \
+        BIT(UALINK_TEST_UPDATE_INTERRUPT_WB) |                         \
+        BIT(UALINK_TEST_UPDATE_SHOOTDOWN_WB))
+
+static int amdgpu_debugfs_ualink_test_run(struct amdgpu_device *adev,
+                                         enum amdgpu_ualink_test_cmd cmd)
+{
+       u32 remote_accel_id = adev->ualink.test_remote_accel_id;
+       int r = 0;
+
+       guard(mutex)(&adev->ualink.selftest_mutex);
+
+       if ((BIT(cmd) & UALINK_TEST_NEEDS_ACCEL_ID) &&
+           remote_accel_id >= AMDGPU_UALINK_ACCEL_MAX) {
+               dev_err(adev->dev, "set remote_accel_id first\n");
+               return -EINVAL;
+       }
+
+       adev->ualink.selftesting = true;
+
+       switch (cmd) {
+       case UALINK_TEST_SW_INIT:
+               if (adev->ualink.remote)
+                       amdgpu_ualink_sw_fini(adev);
+
+               dev_dbg(adev->dev, "load metadata init MPNHT firmware\n");
+               r = amdgpu_ualink_sw_init(adev);
+               break;
+       case UALINK_TEST_SEND_HELLO:
+               r = ualink_send_hello(adev, remote_accel_id);
+               break;
+       case UALINK_TEST_REMOTE_SHOOTDOWN:
+               r = ualink_remote_shootdown(adev, remote_accel_id, 1UL << 30, 1,
+                                           TLB_FLUSH_HEAVYWEIGHT);
+               break;
+       case UALINK_TEST_FLUSH_TLB:
+               ualink_flush_tlb(adev, TLB_FLUSH_HEAVYWEIGHT);
+               break;
+       case UALINK_TEST_READ_LSDMA:
+               r = ualink_simple_rw(adev, remote_accel_id, true, true);
+               break;
+       case UALINK_TEST_READ_SDMA:
+               r = ualink_simple_rw(adev, remote_accel_id, true, false);
+               break;
+       case UALINK_TEST_WRITE_LSDMA:
+               r = ualink_simple_rw(adev, remote_accel_id, false, true);
+               break;
+       case UALINK_TEST_WRITE_SDMA:
+               r = ualink_simple_rw(adev, remote_accel_id, false, false);
+               break;
+       case UALINK_TEST_UPDATE_INTERRUPT_WB:
+               r = ualink_update_interrupt_wb(adev, remote_accel_id);
+               break;
+       case UALINK_TEST_UPDATE_SHOOTDOWN_WB:
+               r = ualink_update_shootdown_wb(adev, remote_accel_id);
+               break;
+       case UALINK_TEST_LSDMA_SELFTEST:
+               r = adev->lsdma.funcs ? amdgpu_lsdma_selftest(adev) : 
-EOPNOTSUPP;
+               break;
+       }
+
+       adev->ualink.selftesting = false;
+
+       dev_dbg(adev->dev, "ualink test cmd %d remote_accel_id %u ret %d\n",
+               cmd, remote_accel_id, r);
+
+       return r;
+}
+
+#define UALINK_TEST_CMD_NAME_MAX       32
+
+static const char * const amdgpu_ualink_test_cmd_name[] = {
+       [UALINK_TEST_SW_INIT]                   = "sw_init",
+       [UALINK_TEST_SEND_HELLO]                = "send_hello",
+       [UALINK_TEST_REMOTE_SHOOTDOWN]          = "remote_shootdown",
+       [UALINK_TEST_FLUSH_TLB]                 = "flush_tlb",
+       [UALINK_TEST_READ_LSDMA]                = "read_lsdma",
+       [UALINK_TEST_READ_SDMA]                 = "read_sdma",
+       [UALINK_TEST_WRITE_LSDMA]               = "write_lsdma",
+       [UALINK_TEST_WRITE_SDMA]                = "write_sdma",
+       [UALINK_TEST_UPDATE_INTERRUPT_WB]       = "update_interrupt_wb",
+       [UALINK_TEST_UPDATE_SHOOTDOWN_WB]       = "update_shootdown_wb",
+       [UALINK_TEST_LSDMA_SELFTEST]            = "lsdma_selftest",
+};
+
+static ssize_t amdgpu_debugfs_ualink_command_read(struct file *file,
+                                                 char __user *ubuf,
+                                                 size_t len, loff_t *offp)
+{
+       char buf[UALINK_TEST_CMD_NAME_MAX * 
ARRAY_SIZE(amdgpu_ualink_test_cmd_name)];
+       unsigned int i;
+       int n = 0;
+
+       for (i = 0; i < ARRAY_SIZE(amdgpu_ualink_test_cmd_name); i++)
+               n += scnprintf(buf + n, sizeof(buf) - n, "%s\n",
+                              amdgpu_ualink_test_cmd_name[i]);
+
+       return simple_read_from_buffer(ubuf, len, offp, buf, n);
+}
+
+static ssize_t amdgpu_debugfs_ualink_command_write(struct file *file,
+                                                  const char __user *ubuf,
+                                                  size_t len, loff_t *offp)
+{
+       struct amdgpu_device *adev = file->private_data;
+       char buf[UALINK_TEST_CMD_NAME_MAX];
+       int cmd, r;
+
+       if (len >= sizeof(buf))
+               return -EINVAL;
+
+       if (copy_from_user(buf, ubuf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       cmd = sysfs_match_string(amdgpu_ualink_test_cmd_name, strim(buf));
+       if (cmd < 0) {
+               dev_err(adev->dev, "unknown ualink test command\n");
+               return cmd;
+       }
+
+       r = amdgpu_debugfs_ualink_test_run(adev, cmd);
+       if (r)
+               return r;
+
+       return len;
+}
+
+static const struct file_operations amdgpu_ualink_command_fops = {
+       .owner   = THIS_MODULE,
+       .open    = simple_open,
+       .read    = amdgpu_debugfs_ualink_command_read,
+       .write   = amdgpu_debugfs_ualink_command_write,
+       .llseek  = default_llseek,
+};
+
+static int amdgpu_debugfs_ualink_accel_id_get(void *data, u64 *val)
+{
+       struct amdgpu_device *adev = data;
+
+       guard(mutex)(&adev->ualink.selftest_mutex);
+
+       *val = adev->ualink.test_remote_accel_id;
+
+       return 0;
+}
+
+static int amdgpu_debugfs_ualink_accel_id_set(void *data, u64 val)
+{
+       struct amdgpu_device *adev = data;
+
+       if (!adev->ualink.info)
+               return -EOPNOTSUPP;
+
+       guard(mutex)(&adev->ualink.selftest_mutex);
+
+       if (val >= AMDGPU_UALINK_ACCEL_MAX ||
+           !test_bit(val, adev->ualink.info->vpod.active_accel_bits)) {
+               dev_err(adev->dev, "invalid remote accel_id %llu\n", val);
+               return -EINVAL;
+       }
+
+       if (val == adev->ualink.info->ppod.accel_id) {
+               dev_err(adev->dev, "remote accel_id equals to local 
accel_id\n");
+               return -EINVAL;
+       }
+
+       adev->ualink.test_remote_accel_id = val;
+       dev_info(adev->dev, "set remote accel_id to %llu, local accel_id %u\n",
+                val, adev->ualink.info->ppod.accel_id);
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(amdgpu_ualink_accel_id_fops,
+                        amdgpu_debugfs_ualink_accel_id_get,
+                        amdgpu_debugfs_ualink_accel_id_set, "%llu\n");
+
+static int amdgpu_debugfs_ualink_test_size_get(void *data, u64 *val)
+{
+       struct amdgpu_device *adev = data;
+
+       guard(mutex)(&adev->ualink.selftest_mutex);
+
+       *val = adev->ualink.test_size_dwords * 4;
+
+       return 0;
+}
+
+static int amdgpu_debugfs_ualink_test_size_set(void *data, u64 val)
+{
+       struct amdgpu_device *adev = data;
+
+       if (val < 4 || val > 8192)
+               return -EINVAL;
+
+       guard(mutex)(&adev->ualink.selftest_mutex);
+
+       adev->ualink.test_size_dwords = val / 4;
+       dev_dbg(adev->dev, "test buffer size %u bytes\n",
+               adev->ualink.test_size_dwords * 4);
+
+       return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(amdgpu_ualink_test_size_fops,
+                        amdgpu_debugfs_ualink_test_size_get,
+                        amdgpu_debugfs_ualink_test_size_set, "%llu\n");
+
 int amdgpu_debugfs_init(struct amdgpu_device *adev)
 {
        struct dentry *root = adev_to_drm(adev)->primary->debugfs_root;
+       struct dentry *ualink_test;
        struct dentry *ent;
        int r, i;
 
@@ -2246,6 +2474,19 @@ int amdgpu_debugfs_init(struct amdgpu_device *adev)
        debugfs_create_ulong("amdgpu_ualink_drop_msg_bitmap", 0600, root,
                             &adev->ualink.drop_msg_bitmap);
 
+       mutex_init(&adev->ualink.selftest_mutex);
+       /* 64 bytes test size by default */
+       adev->ualink.test_size_dwords = 16;
+       adev->ualink.test_remote_accel_id = AMDGPU_UALINK_ACCEL_MAX;
+
+       ualink_test = debugfs_create_dir("amdgpu_ualink_test", root);
+       debugfs_create_file("command", 0600, ualink_test, adev,
+                           &amdgpu_ualink_command_fops);
+       debugfs_create_file("remote_accel_id", 0600, ualink_test, adev,
+                           &amdgpu_ualink_accel_id_fops);
+       debugfs_create_file("test_size", 0600, ualink_test, adev,
+                           &amdgpu_ualink_test_size_fops);
+
        adev->debugfs_vbios_blob.data = adev->bios;
        adev->debugfs_vbios_blob.size = adev->bios_size;
        debugfs_create_blob("amdgpu_vbios", 0444, root,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lsdma.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_lsdma.c
index 05bc281216bf..1f83d9b69135 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lsdma.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lsdma.c
@@ -97,3 +97,52 @@ int amdgpu_lsdma_fill_mem(struct amdgpu_device *adev,
        mutex_unlock(&adev->lsdma.lock);
        return ret;
 }
+
+int amdgpu_lsdma_selftest(struct amdgpu_device *adev)
+{
+       struct amdgpu_ib ib;
+       int i, r;
+       u64 gpu_addr;
+       char *p;
+       u32 size = 128;
+
+       memset(&ib, 0, sizeof(ib));
+       r = amdgpu_ib_get(adev, NULL, 2 * size, AMDGPU_IB_POOL_DIRECT, &ib);
+       if (r)
+               return r;
+
+       gpu_addr = ib.gpu_addr;
+       p = (char *)ib.ptr;
+       memset(p, 0, 2 * size);
+
+       for (i = 0; i < size; i++)
+               *(p + i) = i;
+
+       dev_dbg(adev->dev, "copy src 0x%llx dst 0x%llx size %d bytes\n",
+               gpu_addr, gpu_addr + size, size);
+
+       r = amdgpu_lsdma_copy_mem(adev, gpu_addr, gpu_addr + size, size);
+       if (r)
+               goto out;
+
+       p += size;
+       for (i = 0; i < size; i++) {
+               if (*(p + i) != i) {
+                       dev_dbg(adev->dev, "data mismatch expecting 0x%x but 
read 0x%x\n",
+                               i, *(p + i));
+                       break;
+               }
+       }
+       if (i != size)
+               r = -EIO;
+
+out:
+       amdgpu_ib_free(&ib, NULL);
+
+       if (r)
+               dev_dbg(adev->dev, "lsdma selftest failed r=%d\n", r);
+       else
+               dev_dbg(adev->dev, "lsdma selftest passed\n");
+
+       return r;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lsdma.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_lsdma.h
index 34e92aaa4617..f0a5eeb3529f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lsdma.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lsdma.h
@@ -46,5 +46,6 @@ int amdgpu_lsdma_fill_mem(struct amdgpu_device *adev, 
uint64_t dst_addr,
                          uint32_t data, uint64_t mem_size);
 int amdgpu_lsdma_wait_for(struct amdgpu_device *adev, uint32_t reg_index,
                          uint32_t reg_val, uint32_t mask);
+int amdgpu_lsdma_selftest(struct amdgpu_device *adev);
 
 #endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
index 6cd3927a7e82..d6c4d98f5143 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
@@ -5284,7 +5284,8 @@ static int amdgpu_ualink_metadata_init(struct 
amdgpu_device *adev)
        if (status != AMDGPU_NHT_FW_ST_PREINIT &&
            status != AMDGPU_NHT_FW_ST_HALT) {
                dev_dbg(adev->dev, "fw status 0x%x not preinit or halt\n", 
status);
-               return -ENODEV;
+               if (!adev->ualink.selftesting)
+                       return -ENODEV;
        }
 
        dev_dbg(adev->dev, "accel_id %u addr_mode %d fw status 0x%x\n",
@@ -5333,6 +5334,14 @@ static int amdgpu_ualink_metadata_init(struct 
amdgpu_device *adev)
                remote->rb_gpu_addr, remote->rb_cpu_addr, adev->gmc.vram_start,
                adev->vm_manager.vram_base_offset);
 
+       if (adev->ualink.selftesting) {
+               u32 *p = remote->rb_cpu_addr;
+               int i;
+
+               for (i = 0; i < rb_size / 4; i++)
+                       *(p + i) = 0xdeadbeef;
+       }
+
        /*
         * address aliasing mode, shared wptr pagee left is not enough for wb 
data,
         * wb status and rptr, alloc another BO, and then alloc npa address and 
map
@@ -5402,6 +5411,8 @@ static int amdgpu_ualink_metadata_init(struct 
amdgpu_device *adev)
        r = adev->ualink.msg_ctl->send_metadata(adev,
                                                    remote->metadata_gpu_addr,
                                                    AMDGPU_UALINK_ACCEL_MAX << 
8);
+       if (r && adev->ualink.selftesting)
+               r = 0;
 
 out:
        if (r)
@@ -6387,11 +6398,247 @@ void amdgpu_ualink_sw_fini(struct amdgpu_device *adev)
        adev->ualink.remote = NULL;
 }
 
+static void dump_buffer(struct amdgpu_device *adev, u32 *src, u32 size_dwords)
+{
+       u32 i = 0;
+       u64 p;
+
+       dev_dbg(adev->dev, "buffer 0x%p 0x%x dwords\n", src, size_dwords);
+
+       /* at least 64 bytes dump */
+       size_dwords = ALIGN_DOWN(size_dwords, 16);
+
+       while (i < size_dwords) {
+               p = (u64)(src + i);
+               dev_dbg(adev->dev, "0x%016llx: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                       p,
+                       *(src + i),
+                       *(src + i + 1),
+                       *(src + i + 2),
+                       *(src + i + 3));
+               i += 4;
+       }
+}
+
+/*
+ * With or without MPNHT FW running, write to remote ring buffer
+ *
+ * Write 16 dwords (64 bytes) to ring buffer, don't update wptr and ring the 
doorbell
+ *
+ * Dump VRAM content check if remote writing is done correctly
+ *
+ * assuming ring buffer is located at VRAM offset 0x60000000
+ *
+ * root@atitest-MZ01-CE0-00:/home/atitest# cd 
/sys/kernel/debug/dri/0/amdgpu_ualink_test
+ *
+ * root@atitest-MZ01-CE0-00:/sys/kernel/debug/dri/0/amdgpu_ualink_test# echo 4 
> remote_accel_id
+ *
+ * root@atitest-MZ01-CE0-00:/sys/kernel/debug/dri/0/amdgpu_ualink_test# echo 
write_lsdma > command
+ *
+ * root@atitest-MZ01-CE0-00:/sys/kernel/debug/dri/0# xxd -e -g 4 -s 0x60000000 
-l 64 amdgpu_vram
+ * 60000000: 00000001 00000002 00000003 00000004       ................
+ * 60000010: 00000005 00000006 00000007 00000008       ................
+ * 60000020: 00000009 0000000a 0000000b 0000000c       ................
+ * 60000030: 0000000d 0000000e 0000000f 00000010       ................
+ *
+ * root@atitest-MZ01-CE0-00:/sys/kernel/debug/dri/0/amdgpu_ualink_test# echo 
write_lsdma > command
+ *
+ * root@atitest-MZ01-CE0-00:/sys/kernel/debug/dri/0# xxd -e -g 4 -s 0x60000000 
-l 64 amdgpu_vram
+ * 60000000: 00000002 00000003 00000004 00000005       ................
+ * 60000010: 00000006 00000007 00000008 00000009       ................
+ * 60000020: 0000000a 0000000b 0000000c 0000000d       ................
+ * 60000030: 0000000e 0000000f 00000010 00000011       ................
+ */
+int ualink_simple_rw(struct amdgpu_device *adev, u32 remote_accel_id, bool 
read, bool use_lsdma)
+{
+       struct amdgpu_ualink_remote *remote = to_remote(adev);
+       u32 test_size_dwords = adev->ualink.test_size_dwords;
+       struct amdgpu_ualink_peer *peer;
+       struct amdgpu_ualink_ring *ring;
+       struct dma_fence *fence;
+       struct amdgpu_job *job;
+       struct amdgpu_ib *ib;
+       u32 seq, ndw, ndw_copy_cmd;
+       u32 *src_cpu;
+       u64 src;
+       int r, i;
+
+       if (!remote)
+               return -EINVAL;
+
+       peer = &remote->peer[remote_accel_id];
+       ring = &peer->interrupt;
+
+       /*
+        * 1 sdma copy command to write data to ring buffer
+        *
+        * sdma linear copy command is 7 dwords, align to 8 dwords boundary
+        */
+       ndw_copy_cmd = ALIGN(adev->mman.buffer_funcs->copy_num_dw, 8);
+
+       /*
+        * 16 dwords data
+        */
+       ndw = ndw_copy_cmd + test_size_dwords;
+       r = amdgpu_job_alloc_with_ib(adev, &peer->entity, AMDGPU_FENCE_OWNER_VM,
+                                    ndw * 4, AMDGPU_IB_POOL_IMMEDIATE,
+                                    AMDGPU_KERNEL_JOB_ID_TTM_COPY_BUFFER, 
&job);
+       if (r)
+               return r;
+
+       mutex_lock(&peer->lock);
+
+       ib = job->ibs;
+       ib->length_dw = 0;
+       src = ib->gpu_addr + ndw_copy_cmd * 4;
+       src_cpu = ib->ptr + ndw_copy_cmd;
+
+       seq = ++ring->seq;
+
+       dev_dbg(adev->dev, "src 0x%llx to npa on gart rb 0x%llx seq 0x%x %s 
%s\n",
+               src, ring->rb_npa_gart, seq, read ? "read" : "write",
+               use_lsdma ? "use lsdma" : "use sdma");
+
+       for (i = 0; i < test_size_dwords; i++)
+               *src_cpu++ = seq + i;
+
+       dev_dbg(adev->dev, "dump test buffer data\n");
+       dump_buffer(adev, ib->ptr + ndw_copy_cmd, test_size_dwords);
+
+       remote->use_lsdma = use_lsdma;
+
+       dev_dbg(adev->dev, "use %s\n", remote->use_lsdma ? "lsdma" : "sdma");
+
+       if (remote->use_lsdma) {
+               if (read)
+                       r = amdgpu_lsdma_copy_mem(adev, ring->rb_npa_gart, src,
+                                                 test_size_dwords * 4);
+               else
+                       r = amdgpu_lsdma_copy_mem(adev, src, ring->rb_npa_gart,
+                                                 test_size_dwords * 4);
+
+               amdgpu_job_free(job);
+               goto out_unlock;
+       }
+
+       /* SDMA commands copy from ib to NPA */
+       if (read)
+               amdgpu_emit_copy_buffer(adev, ib, ring->rb_npa_gart, src,
+                                       test_size_dwords * 4, 0);
+       else
+               amdgpu_emit_copy_buffer(adev, ib, src, ring->rb_npa_gart,
+                                       test_size_dwords * 4, 0);
+
+       amdgpu_ring_pad_ib(&adev->sdma.instance[0].ring, ib);
+       WARN_ON(ib->length_dw > ndw_copy_cmd);
+
+       fence = amdgpu_job_submit(job);
+
+       /*
+        * wait sdma done
+        */
+       r = dma_fence_wait_timeout(fence, false, HZ * 10);
+       dma_fence_put(fence);
+       if (r <= 0) {
+               dev_dbg(adev->dev, "remote sdma fence wait timeout %d\n", r);
+               dev_dbg(adev->dev, "use lsdma for testing afterwards\n");
+               remote->use_lsdma = true;
+               r = -ETIME;
+       } else {
+               r = 0;
+       }
+
+out_unlock:
+       mutex_unlock(&peer->lock);
+       if (read) {
+               dev_dbg(adev->dev, "dump remote read data\n");
+               dump_buffer(adev, ib->ptr + ndw_copy_cmd, test_size_dwords);
+       }
+
+       dev_dbg(adev->dev, "ret r=%d\n", r);
+       return r;
+}
+
+/* The peer may have rebooted or the vpod was reconfigured, adopt the remote
+ * wptr before sending a command, same as the handshake does.
+ */
+static int ualink_resync_peer(struct amdgpu_device *adev, u32 remote_accel_id)
+{
+       int r;
+
+       if (!adev->ualink.remote)
+               return -EINVAL;
+
+       r = amdgpu_ualink_ring_resync_ptrs(adev, remote_accel_id);
+       if (r < 0)
+               return r;
+
+       amdgpu_ualink_reset_peer_rings(adev, remote_accel_id);
+
+       return 0;
+}
+
 int ualink_send_hello(struct amdgpu_device *adev, u32 remote_accel_id)
 {
+       int r;
+
+       r = ualink_resync_peer(adev, remote_accel_id);
+       if (r)
+               return r;
+
        return amdgpu_ualink_send_hello_msg(adev, remote_accel_id);
 }
 
+void ualink_flush_tlb(struct amdgpu_device *amdgpu, u32 flush_type)
+{
+       amdgpu_ualink_flush_tlb(amdgpu, flush_type);
+}
+
+int ualink_remote_shootdown(struct amdgpu_device *adev, u32 remote_accel_id, 
u64 addr,
+                           u32 size_in_pages, u32 flush_type)
+{
+       int r;
+
+       r = ualink_resync_peer(adev, remote_accel_id);
+       if (r)
+               return r;
+
+       return amdgpu_ualink_remote_shootdown(adev, remote_accel_id, addr,
+                                             size_in_pages, flush_type);
+}
+
+int ualink_update_interrupt_wb(struct amdgpu_device *adev, u32 remote_accel_id)
+{
+       int r;
+
+       r = ualink_resync_peer(adev, remote_accel_id);
+       if (r)
+               return r;
+
+       guard(mutex)(&adev->ualink.remote->peer[remote_accel_id].lock);
+
+       /* Update interrupt completion write back address */
+       r = amdgpu_ualink_update_wb_address(adev, remote_accel_id,
+                                           RB_TYPE_REMOTE_INTERRUPT);
+       return r;
+}
+
+int ualink_update_shootdown_wb(struct amdgpu_device *adev, u32 remote_accel_id)
+{
+       int r;
+
+       r = ualink_resync_peer(adev, remote_accel_id);
+       if (r)
+               return r;
+
+       guard(mutex)(&adev->ualink.remote->peer[remote_accel_id].lock);
+
+       /* Update shootdown completion write back address */
+       r = amdgpu_ualink_update_wb_address(adev, remote_accel_id,
+                                           RB_TYPE_TLB_INV);
+       return r;
+}
+
 /*
  * The low bits of handle_lo carry the message-header field on the wire.
  * Strip those bits and splice in @acc_id to recover the
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
index 85c9a9634a4b..b381e378dae2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.h
@@ -333,6 +333,13 @@ struct amdgpu_ualink_mgr {
        /* For remote interrupt and shootdown */
        struct amdgpu_ualink_remote *remote;
 
+       /* true for selftest driver path, ignore FW error */
+       bool selftesting;
+       /* serialize the selftest debugfs entries */
+       struct mutex selftest_mutex;
+       u32 test_size_dwords;
+       u32 test_remote_accel_id;
+
        /* handle irq from ualink client of remote GPUs */
        struct amdgpu_irq_src irq;
 
@@ -393,6 +400,14 @@ struct amdgpu_ualink_mgr {
 int amdgpu_ualink_init_interrupt(struct amdgpu_device *adev,
                                 unsigned int client_id, unsigned int src_id);
 
+int ualink_simple_rw(struct amdgpu_device *adev, u32 remote_accel_id, bool 
read, bool use_lsdma);
+int ualink_send_hello(struct amdgpu_device *adev, u32 remote_accel_id);
+void ualink_flush_tlb(struct amdgpu_device *amdgpu, u32 flush_type);
+int ualink_remote_shootdown(struct amdgpu_device *adev, u32 remote_accel_id, 
u64 addr,
+                           u32 size_in_pages, u32 flush_type);
+int ualink_update_interrupt_wb(struct amdgpu_device *adev, u32 
remote_accel_id);
+int ualink_update_shootdown_wb(struct amdgpu_device *adev, u32 
remote_accel_id);
+
 int amdgpu_ualink_sw_init(struct amdgpu_device *adev);
 void amdgpu_ualink_sw_fini(struct amdgpu_device *adev);
 
-- 
2.50.1

Reply via email to