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