[PATCH 14/15] yunsilicon: add RDMA counters and sysfs

From: Xin Tian

Date: Mon Oct 12 2026 - 07:55:00 EST


Add the hardware counter reporting through the RDMA counter interface,
the global counter sysfs attributes and the hca_type/hw_rev sysfs
attributes.

Signed-off-by: Xin Tian <tianx@xxxxxxxxxxxxxx>
---
drivers/infiniband/hw/xsc/Makefile | 3 +-
drivers/infiniband/hw/xsc/counters.c | 422 +++++++++++++++++++++++
drivers/infiniband/hw/xsc/counters.h | 31 ++
drivers/infiniband/hw/xsc/main.c | 4 +
drivers/infiniband/hw/xsc/xsc_ib_sysfs.c | 64 ++++
5 files changed, 523 insertions(+), 1 deletion(-)
create mode 100644 drivers/infiniband/hw/xsc/counters.c
create mode 100644 drivers/infiniband/hw/xsc/counters.h
create mode 100644 drivers/infiniband/hw/xsc/xsc_ib_sysfs.c

diff --git a/drivers/infiniband/hw/xsc/Makefile b/drivers/infiniband/hw/xsc/Makefile
index d5ad0e573..48a6d2b0a 100644
--- a/drivers/infiniband/hw/xsc/Makefile
+++ b/drivers/infiniband/hw/xsc/Makefile
@@ -6,4 +6,5 @@ ccflags-y := -I$(srctree)/drivers/net/ethernet/yunsilicon/xsc

obj-$(CONFIG_INFINIBAND_XSC) += xsc_ib.o

-xsc_ib-y := main.o cq.o qp.o mem.o mr.o srq.o ah.o
+xsc_ib-y := main.o cq.o qp.o mem.o mr.o ah.o counters.o \
+ xsc_ib_sysfs.o srq.o
diff --git a/drivers/infiniband/hw/xsc/counters.c b/drivers/infiniband/hw/xsc/counters.c
new file mode 100644
index 000000000..135df9595
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/counters.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include "common/xsc_core.h"
+#include "common/xsc_hsi.h"
+#include "common/driver.h"
+#include "common/xsc_cmd.h"
+#include "counters.h"
+#include "xsc_ib.h"
+
+#define COUNTERS_FILE_NAME "counters"
+#define COUNTERS_NAMES_FILE_NAME "counters_names"
+#define COUNTERS_VALUE_FILE_NAME "counters_value"
+#define COUNTERS_ATTER_GROUP_NAME "counters"
+#define GLOBAL_COUNTERS_GROUP_NAME "global_counters"
+#define GLOBAL_COUNTERS_FILE_NAME "counters"
+
+static const struct counter_desc hw_rdma_stats_pf_desc[] = {
+ /*by mac port*/
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_tx_pkts) },//0
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_tx_bytes) },//1
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_rx_pkts) },//2
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_rx_bytes) },//3
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, np_cnp_sent) },//4
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rp_cnp_handled) },//5
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, np_ecn_marked_roce_packets) },//6
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rp_cnp_ignored) },//7
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, read_rsp_out_of_seq) },//8
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, implied_nak_seq_err) },//9
+ /*by function*/
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, out_of_sequence) },//10
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, packet_seq_err) },//11
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, out_of_buffer) },//12
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rnr_nak_retry_err) },//13
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, local_ack_timeout_err) },//14
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rx_read_requests) },//15
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rx_write_requests) },//16
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, duplicate_requests) },//17
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_tx_pkts_func) },//18
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_tx_payload_bytes) },//19
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_rx_pkts_func) },//20
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_rx_payload_bytes) },//21
+ /*global*/
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_loopback_pkts) },//22
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_loopback_bytes) },//23
+ /*for diamond*/
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, out_of_sequence_sr) },//24
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, packet_seq_err_sr) },//25
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_ndp_rx_pkts) },//26
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_ndp_rx_trimmed_pkts) },//27
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rdma_ndp_trimmed_pkts_sr) },//28
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_pf, rx_icrc_encapsulated) },//29
+ /*XSC_CMDQ_GET_HW_STATS_RDMA_V1*/
+};
+
+static const struct counter_desc hw_rdma_stats_vf_desc[] = {
+ /*by function*/
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, rdma_tx_pkts_func) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, rdma_tx_payload_bytes) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, rdma_rx_pkts_func) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, rdma_rx_payload_bytes) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, out_of_sequence) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, packet_seq_err) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, out_of_buffer) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, rnr_nak_retry_err) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, local_ack_timeout_err) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, rx_read_requests) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, rx_write_requests) },
+ { XSC_DECLARE_STAT(struct xsc_hw_stats_rdma_vf, duplicate_requests) },
+};
+
+static const struct counter_desc hw_global_rdma_stats_desc[] = {
+ { XSC_DECLARE_STAT(struct xsc_hw_global_stats_rdma, rdma_loopback_pkts) },
+ { XSC_DECLARE_STAT(struct xsc_hw_global_stats_rdma, rdma_loopback_bytes) },
+ { XSC_DECLARE_STAT(struct xsc_hw_global_stats_rdma, rx_icrc_encapsulated) },
+ { XSC_DECLARE_STAT(struct xsc_hw_global_stats_rdma, req_cqe_error) },
+ { XSC_DECLARE_STAT(struct xsc_hw_global_stats_rdma, resp_cqe_error) },
+ { XSC_DECLARE_STAT(struct xsc_hw_global_stats_rdma, cqe_msg_code_error) },
+};
+
+static int get_hw_stats_rdma(struct xsc_core_device *dev, struct xsc_hw_stats_rdma *stats_rdma)
+{
+ int ret;
+ struct xsc_hw_stats_mbox_in in;
+ struct xsc_hw_stats_rdma_mbox_out out;
+
+ memset(stats_rdma, 0, sizeof(*stats_rdma));
+
+ if (!dev)
+ return -1;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.mac_port = dev->mac_port;
+ in.hdr.ver = cpu_to_be16(XSC_CMDQ_GET_HW_STATS_RDMA_V1);
+ in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_QUERY_HW_STATS_RDMA);
+
+ ret = xsc_cmd_exec(dev, (void *)&in, sizeof(in), (void *)&out, sizeof(out));
+ if (ret || out.hdr.status)
+ return -1;
+
+ memcpy(stats_rdma, &out.hw_stats, sizeof(*stats_rdma));
+
+ return 0;
+}
+
+static ssize_t counters_names_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct xsc_ib_dev *ibdev = container_of(device, struct xsc_ib_dev, ib_dev.dev);
+ struct xsc_core_device *xdev = ibdev->xdev;
+ struct xsc_hw_stats_rdma stats_rdma;
+ const struct counter_desc *desc;
+ int desc_size;
+ ssize_t count = 0;
+ u64 mask;
+ int i;
+
+ if (get_hw_stats_rdma(xdev, &stats_rdma))
+ return 0;
+
+ if (is_support_hw_pf_stats(xdev)) {
+ desc = hw_rdma_stats_pf_desc;
+ desc_size = ARRAY_SIZE(hw_rdma_stats_pf_desc);
+ } else {
+ desc = hw_rdma_stats_vf_desc;
+ desc_size = ARRAY_SIZE(hw_rdma_stats_vf_desc);
+ }
+
+ mask = xsc_get_rdma_stat_mask(xdev, be16_to_cpu(stats_rdma.ver));
+ for (i = 0; i < desc_size; i++) {
+ if (!((1ULL << i) & mask))
+ continue;
+ count += sysfs_emit_at(buf, count, "%s\n", desc[i].format);
+ }
+
+ return count;
+}
+static DEVICE_ATTR_RO(counters_names);
+
+static ssize_t counters_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct xsc_ib_dev *ibdev = container_of(device, struct xsc_ib_dev, ib_dev.dev);
+ struct xsc_core_device *xdev = ibdev->xdev;
+ struct xsc_hw_stats_rdma stats_rdma;
+ const struct counter_desc *desc;
+ int desc_size;
+ ssize_t count = 0;
+ u8 *stats;
+ u64 mask;
+ int i;
+
+ if (get_hw_stats_rdma(xdev, &stats_rdma) ||
+ is_support_hw_pf_stats(xdev) != stats_rdma.is_pf)
+ return 0;
+
+ if (is_support_hw_pf_stats(xdev)) {
+ desc = hw_rdma_stats_pf_desc;
+ desc_size = ARRAY_SIZE(hw_rdma_stats_pf_desc);
+ stats = (u8 *)&stats_rdma.stats.pf_stats;
+ } else {
+ desc = hw_rdma_stats_vf_desc;
+ desc_size = ARRAY_SIZE(hw_rdma_stats_vf_desc);
+ stats = (u8 *)&stats_rdma.stats.vf_stats;
+ }
+
+ mask = xsc_get_rdma_stat_mask(xdev, be16_to_cpu(stats_rdma.ver));
+ for (i = 0; i < desc_size; i++) {
+ u64 value;
+
+ if (!((1ULL << i) & mask))
+ continue;
+ value = be64_to_cpu(*(u64 *)(stats + desc[i].offset));
+ count += sysfs_emit_at(buf, count, "%-26s %-20llu\n",
+ desc[i].format, value);
+ }
+
+ return count;
+}
+static DEVICE_ATTR_RO(counters);
+
+static ssize_t counters_value_read(struct file *file, struct kobject *kob,
+ const struct bin_attribute *bin_attr,
+ char *buf, loff_t loff, size_t size)
+{
+ struct xsc_ib_dev *ibdev = container_of(kob, struct xsc_ib_dev, ib_dev.dev.kobj);
+ struct xsc_core_device *xdev = ibdev->xdev;
+ struct xsc_hw_stats_rdma stats_rdma;
+ const struct counter_desc *desc;
+ int desc_size;
+ int bin_size;
+ u64 *tmp_value;
+ u8 *stats;
+ u64 mask;
+ int i, j;
+
+ if (bin_attr->size > size || bin_attr->size == 0)
+ return 0;
+
+ if (get_hw_stats_rdma(xdev, &stats_rdma) ||
+ is_support_hw_pf_stats(xdev) != stats_rdma.is_pf)
+ return 0;
+
+ if (is_support_hw_pf_stats(xdev)) {
+ desc = hw_rdma_stats_pf_desc;
+ desc_size = ARRAY_SIZE(hw_rdma_stats_pf_desc);
+ stats = (u8 *)&stats_rdma.stats.pf_stats;
+ } else {
+ desc = hw_rdma_stats_vf_desc;
+ desc_size = ARRAY_SIZE(hw_rdma_stats_vf_desc);
+ stats = (u8 *)&stats_rdma.stats.vf_stats;
+ }
+
+ bin_size = desc_size * sizeof(u64);
+ if (bin_attr->size < bin_size)
+ return 0;
+
+ tmp_value = kzalloc(bin_attr->size, GFP_KERNEL);
+ if (!tmp_value)
+ return 0;
+
+ mask = xsc_get_rdma_stat_mask(xdev, be16_to_cpu(stats_rdma.ver));
+ j = 0;
+ for (i = 0; i < desc_size; i++) {
+ if (!((1ULL << i) & mask))
+ continue;
+ tmp_value[j] = be64_to_cpu(*(u64 *)(stats + desc[i].offset));
+ j++;
+ }
+
+ memcpy(buf, tmp_value, bin_attr->size);
+
+ kfree(tmp_value);
+ return bin_attr->size;
+}
+static BIN_ATTR_RO(counters_value, sizeof(struct xsc_hw_stats_rdma));
+
+static struct attribute *counters_attrs[] = {
+ &dev_attr_counters_names.attr,
+ &dev_attr_counters.attr,
+ NULL,
+};
+
+static const struct bin_attribute *const counters_bin_attrs[] = {
+ &bin_attr_counters_value,
+ NULL,
+};
+
+static const struct attribute_group counters_group = {
+ .name = "counters",
+ .attrs = counters_attrs,
+ .bin_attrs = counters_bin_attrs,
+};
+
+static int counters_sysfs_init(struct ib_device *ib_dev)
+{
+ return sysfs_create_group(&ib_dev->dev.kobj, &counters_group);
+}
+
+static void counters_sysfs_fini(struct ib_device *ib_dev)
+{
+ sysfs_remove_group(&ib_dev->dev.kobj, &counters_group);
+}
+
+static ssize_t global_cnt_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct xsc_global_cnt_attributes *a =
+ container_of(attr, struct xsc_global_cnt_attributes, attr);
+ struct xsc_global_cnt_interface *g =
+ container_of(kobj, struct xsc_global_cnt_interface, kobj);
+
+ if (!a->show)
+ return -EIO;
+
+ return a->show(g, a, buf);
+}
+
+static ssize_t global_cnt_attr_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *buf, size_t size)
+{
+ struct xsc_global_cnt_attributes *a =
+ container_of(attr, struct xsc_global_cnt_attributes, attr);
+ struct xsc_global_cnt_interface *g =
+ container_of(kobj, struct xsc_global_cnt_interface, kobj);
+
+ if (!a->store)
+ return -EIO;
+
+ return a->store(g, a, buf, size);
+}
+
+static ssize_t global_counters_show(struct xsc_global_cnt_interface *g,
+ struct xsc_global_cnt_attributes *a, char *buf)
+{
+ int i;
+ int ret;
+ u8 *stats;
+ u64 value;
+ int desc_size;
+ ssize_t count = 0;
+ const struct counter_desc *desc;
+ struct xsc_hw_global_stats_mbox_in in;
+ struct xsc_hw_global_stats_mbox_out out;
+
+ memset(&in, 0, sizeof(in));
+ memset(&out, 0, sizeof(out));
+ in.hdr.opcode = cpu_to_be16(XSC_CMD_OP_QUERY_HW_GLOBAL_STATS);
+ ret = xsc_cmd_exec(g->xdev, (void *)&in, sizeof(in),
+ (void *)&out, sizeof(out));
+ if (ret || out.hdr.status)
+ return 0;
+
+ desc = &hw_global_rdma_stats_desc[0];
+ desc_size = ARRAY_SIZE(hw_global_rdma_stats_desc);
+ stats = (u8 *)&out.hw_stats;
+
+ for (i = 0 ; i < desc_size; i++) {
+ value = *(u64 *)(stats + desc[i].offset);
+ value = be64_to_cpu(value);
+ count += sprintf(&buf[count], "%-26s %-20llu\n",
+ desc[i].format, value);
+ }
+
+ return count;
+}
+
+static ssize_t global_counters_store(struct xsc_global_cnt_interface *g,
+ struct xsc_global_cnt_attributes *a,
+ const char *buf, size_t count)
+{
+ return -EOPNOTSUPP;
+}
+
+#define GLOBAL_CNT_ATTR(_name) struct xsc_global_cnt_attributes xsc_global_cnt_attr_##_name = \
+ __ATTR(_name, 0444, global_##_name##_show, global_##_name##_store)
+
+GLOBAL_CNT_ATTR(counters);
+
+static const struct sysfs_ops global_cnt_sysfs_ops = {
+ .show = global_cnt_attr_show,
+ .store = global_cnt_attr_store,
+};
+
+static struct attribute *global_cnt_attrs[] = {
+ &xsc_global_cnt_attr_counters.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(global_cnt);
+
+static const struct kobj_type global_cnt_ktype = {
+ .sysfs_ops = &global_cnt_sysfs_ops,
+ .default_groups = global_cnt_groups,
+};
+
+static struct xsc_global_cnt_interface *g_global_cnt_interface;
+
+static int global_cnt_sysfs_init(struct ib_device *ib_dev, struct xsc_core_device *xdev)
+{
+ struct xsc_global_cnt_interface *tmp;
+ int err;
+
+ if (!xdev || !xsc_core_is_pf(xdev) || xdev->pf_id != 0)
+ return 0;
+
+ tmp = kzalloc_obj(*tmp, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ err = kobject_init_and_add(&tmp->kobj, &global_cnt_ktype,
+ &ib_dev->dev.kobj, GLOBAL_COUNTERS_GROUP_NAME);
+ if (err)
+ goto error_return;
+
+ g_global_cnt_interface = tmp;
+ tmp->xdev = xdev;
+ return 0;
+
+error_return:
+ kobject_put(&tmp->kobj);
+ kfree(tmp);
+ return err;
+}
+
+static void global_cnt_sysfs_fini(struct xsc_core_device *xdev)
+{
+ if (!g_global_cnt_interface || !xdev || !xsc_core_is_pf(xdev) || xdev->pf_id != 0)
+ return;
+
+ kobject_put(&g_global_cnt_interface->kobj);
+ kfree(g_global_cnt_interface);
+ g_global_cnt_interface = NULL;
+}
+
+int xsc_counters_init(struct ib_device *ib_dev, struct xsc_core_device *xdev)
+{
+ int ret;
+
+ ret = counters_sysfs_init(ib_dev);
+ if (ret)
+ return ret;
+
+ ret = global_cnt_sysfs_init(ib_dev, xdev);
+ if (ret)
+ counters_sysfs_fini(ib_dev);
+
+ return ret;
+}
+
+void xsc_counters_fini(struct ib_device *ib_dev, struct xsc_core_device *xdev)
+{
+ counters_sysfs_fini(ib_dev);
+ global_cnt_sysfs_fini(xdev);
+}
diff --git a/drivers/infiniband/hw/xsc/counters.h b/drivers/infiniband/hw/xsc/counters.h
new file mode 100644
index 000000000..944dbafb6
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/counters.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#ifndef __COUNTERS_H__
+#define __COUNTERS_H__
+
+#define STRING_LEN 32
+#define XSC_DECLARE_STAT(type, fld) ""#fld, offsetof(type, fld)
+
+struct counter_desc {
+ char format[STRING_LEN];
+ size_t offset; /* Byte offset */
+};
+
+struct xsc_global_cnt_interface {
+ struct xsc_core_device *xdev;
+ struct kobject kobj;
+};
+
+struct xsc_global_cnt_attributes {
+ struct attribute attr;
+ ssize_t (*show)(struct xsc_global_cnt_interface *g, struct xsc_global_cnt_attributes *a,
+ char *buf);
+ ssize_t (*store)(struct xsc_global_cnt_interface *g, struct xsc_global_cnt_attributes *a,
+ const char *buf, size_t count);
+};
+
+#endif
diff --git a/drivers/infiniband/hw/xsc/main.c b/drivers/infiniband/hw/xsc/main.c
index 30927006b..38c8b4f06 100644
--- a/drivers/infiniband/hw/xsc/main.c
+++ b/drivers/infiniband/hw/xsc/main.c
@@ -745,7 +745,9 @@ static int init_one(struct xsc_core_device *xdev,

xsc_register_netdev_notifier(dev);

+ xsc_counters_init(&dev->ib_dev, xdev);

+ xsc_ib_sysfs_init(&dev->ib_dev, xdev);

return 0;

@@ -759,6 +761,8 @@ static void remove_one(struct xsc_core_device *xdev, void *intf_ctx)
{
struct xsc_ib_dev *dev = (struct xsc_ib_dev *)intf_ctx;

+ xsc_ib_sysfs_fini(&dev->ib_dev, xdev);
+ xsc_counters_fini(&dev->ib_dev, xdev);
xsc_unregister_netdev_notifier(dev);
ib_unregister_device(&dev->ib_dev);
ib_dealloc_device(&dev->ib_dev);
diff --git a/drivers/infiniband/hw/xsc/xsc_ib_sysfs.c b/drivers/infiniband/hw/xsc/xsc_ib_sysfs.c
new file mode 100644
index 000000000..a0b577bde
--- /dev/null
+++ b/drivers/infiniband/hw/xsc/xsc_ib_sysfs.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd.
+ * All rights reserved.
+ */
+
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#include "common/xsc_core.h"
+#include "common/xsc_hsi.h"
+#include "common/driver.h"
+#include "common/xsc_cmd.h"
+#include "xsc_ib.h"
+
+static ssize_t hca_type_show(struct device *device, struct device_attribute *attr, char *buf)
+{
+ struct ib_device *ib_dev = container_of(device, struct ib_device, dev);
+ struct xsc_core_device *dev = to_xdev(ib_dev)->xdev;
+ struct pci_dev *pdev = dev->pdev;
+
+ return sprintf(buf, "%x\n", pdev->subsystem_device);
+}
+
+static DEVICE_ATTR_RO(hca_type);
+
+static ssize_t hw_rev_show(struct device *device, struct device_attribute *attr, char *buf)
+{
+ struct ib_device *ib_dev = container_of(device, struct ib_device, dev);
+ struct xsc_core_device *dev = to_xdev(ib_dev)->xdev;
+ u32 hw_ver = 0;
+
+ hw_ver = ((dev->chip_ver_l & 0xffff) << 16) |
+ (dev->hotfix_num & 0xffff);
+ return sprintf(buf, "0x%x\n", hw_ver);
+}
+
+static DEVICE_ATTR_RO(hw_rev);
+
+static struct device_attribute *xsc_ib_attributes[] = {
+ &dev_attr_hca_type,
+ &dev_attr_hw_rev,
+};
+
+void xsc_ib_sysfs_init(struct ib_device *ib_dev, struct xsc_core_device *xdev)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(xsc_ib_attributes); i++) {
+ err = device_create_file(&ib_dev->dev, xsc_ib_attributes[i]);
+ if (err)
+ dev_err(&xdev->pdev->dev, "Create sysfs file for %s failed.\n",
+ xsc_ib_attributes[i]->attr.name);
+ }
+}
+
+void xsc_ib_sysfs_fini(struct ib_device *ib_dev, struct xsc_core_device *xdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(xsc_ib_attributes); i++)
+ device_remove_file(&ib_dev->dev, xsc_ib_attributes[i]);
+}