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 <[email protected]>
---
 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

Reply via email to