root/drivers/gpu/drm/xe/xe_pxp_debugfs.c
// SPDX-License-Identifier: MIT
/*
 * Copyright © 2024 Intel Corporation
 */

#include "xe_pxp_debugfs.h"

#include <linux/debugfs.h>

#include <drm/drm_debugfs.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>

#include "xe_device_types.h"
#include "xe_pxp.h"
#include "xe_pxp_types.h"
#include "regs/xe_irq_regs.h"

static struct xe_pxp *node_to_pxp(struct drm_info_node *node)
{
        return node->info_ent->data;
}

static const char *pxp_status_to_str(struct xe_pxp *pxp)
{
        lockdep_assert_held(&pxp->mutex);

        switch (pxp->status) {
        case XE_PXP_ERROR:
                return "error";
        case XE_PXP_NEEDS_TERMINATION:
                return "needs termination";
        case XE_PXP_TERMINATION_IN_PROGRESS:
                return "termination in progress";
        case XE_PXP_READY_TO_START:
                return "ready to start";
        case XE_PXP_ACTIVE:
                return "active";
        case XE_PXP_SUSPENDED:
                return "suspended";
        default:
                return "unknown";
        }
};

static int pxp_info(struct seq_file *m, void *data)
{
        struct xe_pxp *pxp = node_to_pxp(m->private);
        struct drm_printer p = drm_seq_file_printer(m);
        const char *status;

        if (!xe_pxp_is_enabled(pxp))
                return -ENODEV;

        mutex_lock(&pxp->mutex);
        status = pxp_status_to_str(pxp);

        drm_printf(&p, "status: %s\n", status);
        drm_printf(&p, "instance counter: %u\n", pxp->key_instance);
        mutex_unlock(&pxp->mutex);

        return 0;
}

static int pxp_terminate(struct seq_file *m, void *data)
{
        struct xe_pxp *pxp = node_to_pxp(m->private);
        struct drm_printer p = drm_seq_file_printer(m);
        int ready = xe_pxp_get_readiness_status(pxp);

        if (ready < 0)
                return ready; /* disabled or error occurred */
        else if (!ready)
                return -EBUSY; /* init still in progress */

        /* no need for a termination if PXP is not active */
        if (pxp->status != XE_PXP_ACTIVE) {
                drm_printf(&p, "PXP not active\n");
                return 0;
        }

        /* simulate a termination interrupt */
        spin_lock_irq(&pxp->xe->irq.lock);
        xe_pxp_irq_handler(pxp->xe, KCR_PXP_STATE_TERMINATED_INTERRUPT);
        spin_unlock_irq(&pxp->xe->irq.lock);

        drm_printf(&p, "PXP termination queued\n");

        return 0;
}

static const struct drm_info_list debugfs_list[] = {
        {"info", pxp_info, 0},
        {"terminate", pxp_terminate, 0},
};

void xe_pxp_debugfs_register(struct xe_pxp *pxp)
{
        struct drm_minor *minor;
        struct drm_info_list *local;
        struct dentry *root;
        int i;

        if (!xe_pxp_is_enabled(pxp))
                return;

        minor = pxp->xe->drm.primary;
        if (!minor->debugfs_root)
                return;

#define DEBUGFS_SIZE    (ARRAY_SIZE(debugfs_list) * sizeof(struct drm_info_list))
        local = drmm_kmalloc(&pxp->xe->drm, DEBUGFS_SIZE, GFP_KERNEL);
        if (!local)
                return;

        memcpy(local, debugfs_list, DEBUGFS_SIZE);
#undef DEBUGFS_SIZE

        for (i = 0; i < ARRAY_SIZE(debugfs_list); ++i)
                local[i].data = pxp;

        root = debugfs_create_dir("pxp", minor->debugfs_root);
        if (IS_ERR(root))
                return;

        drm_debugfs_create_files(local,
                                 ARRAY_SIZE(debugfs_list),
                                 root, minor);
}