[RFC PATCH 10/14] drm/qaic: Add sysfs

From: Jeffrey Hugo
Date: Mon Aug 15 2022 - 15:34:30 EST


The QAIC driver can advertise the state of individual dma_bridge channels
to userspace. Userspace can use this information to manage userspace
state when a channel crashes.

Change-Id: Ifc7435c53cec6aa326bdcd9bfcb77ea7f2a63bab
Signed-off-by: Jeffrey Hugo <quic_jhugo@xxxxxxxxxxx>
---
drivers/gpu/drm/qaic/qaic_sysfs.c | 113 ++++++++++++++++++++++++++++++++++++++
1 file changed, 113 insertions(+)
create mode 100644 drivers/gpu/drm/qaic/qaic_sysfs.c

diff --git a/drivers/gpu/drm/qaic/qaic_sysfs.c b/drivers/gpu/drm/qaic/qaic_sysfs.c
new file mode 100644
index 0000000..5ee1696
--- /dev/null
+++ b/drivers/gpu/drm/qaic/qaic_sysfs.c
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/sysfs.h>
+
+#include "qaic.h"
+
+#define NAME_LEN 14
+
+struct dbc_attribute {
+ struct device_attribute dev_attr;
+ u32 dbc_id;
+ char name[NAME_LEN];
+};
+
+static ssize_t dbc_state_show(struct device *dev,
+ struct device_attribute *a, char *buf)
+{
+ struct dbc_attribute *attr = container_of(a, struct dbc_attribute, dev_attr);
+ struct qaic_device *qdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", qdev->dbc[attr->dbc_id].state);
+}
+
+void set_dbc_state(struct qaic_device *qdev, u32 dbc_id, unsigned int state)
+{
+ char id_str[12];
+ char state_str[16];
+ char *envp[] = { id_str, state_str, NULL };
+ struct qaic_drm_device *qddev;
+
+ if (state >= DBC_STATE_MAX) {
+ pci_dbg(qdev->pdev, "%s invalid state %d\n", __func__, state);
+ return;
+ }
+ if (dbc_id >= qdev->num_dbc) {
+ pci_dbg(qdev->pdev, "%s invalid dbc_id %d\n", __func__, dbc_id);
+ return;
+ }
+ if (state == qdev->dbc[dbc_id].state) {
+ pci_dbg(qdev->pdev, "%s already at state %d\n", __func__, state);
+ return;
+ }
+
+ snprintf(id_str, ARRAY_SIZE(id_str), "DBC_ID=%d", dbc_id);
+ snprintf(state_str, ARRAY_SIZE(state_str), "DBC_STATE=%d", state);
+
+ qdev->dbc[dbc_id].state = state;
+ mutex_lock(&qdev->qaic_drm_devices_mutex);
+ list_for_each_entry(qddev, &qdev->qaic_drm_devices, node)
+ kobject_uevent_env(&qddev->ddev->dev->kobj, KOBJ_CHANGE, envp);
+ mutex_unlock(&qdev->qaic_drm_devices_mutex);
+}
+
+int qaic_sysfs_init(struct qaic_drm_device *qddev)
+{
+ u32 num_dbc = qddev->qdev->num_dbc;
+ struct dbc_attribute *dbc_attrs;
+ int i, ret;
+
+ dbc_attrs = kcalloc(num_dbc, sizeof(*dbc_attrs), GFP_KERNEL);
+ if (!dbc_attrs)
+ return -ENOMEM;
+
+ qddev->sysfs_attrs = dbc_attrs;
+
+ for (i = 0; i < num_dbc; ++i) {
+ struct dbc_attribute *dbc = &dbc_attrs[i];
+
+ sysfs_attr_init(&dbc->dev_attr.attr);
+ dbc->dbc_id = i;
+ snprintf(dbc->name, NAME_LEN, "dbc%d_state", i);
+ dbc->dev_attr.attr.name = dbc->name;
+ dbc->dev_attr.attr.mode = 0444;
+ dbc->dev_attr.show = dbc_state_show;
+ ret = sysfs_create_file(&qddev->ddev->dev->kobj,
+ &dbc->dev_attr.attr);
+ if (ret) {
+ int j;
+
+ for (j = 0; j < i; ++j) {
+ dbc = &dbc_attrs[j];
+ sysfs_remove_file(&qddev->ddev->dev->kobj,
+ &dbc->dev_attr.attr);
+ }
+ break;
+ }
+ }
+
+ if (ret)
+ kfree(dbc_attrs);
+
+ return ret;
+}
+
+void qaic_sysfs_remove(struct qaic_drm_device *qddev)
+{
+ struct dbc_attribute *dbc_attrs = qddev->sysfs_attrs;
+ u32 num_dbc = qddev->qdev->num_dbc;
+ int i;
+
+ for (i = 0; i < num_dbc; ++i)
+ sysfs_remove_file(&qddev->ddev->dev->kobj,
+ &dbc_attrs[i].dev_attr.attr);
+
+ kfree(dbc_attrs);
+}
--
2.7.4